Re: [PATCH for-next v3 2/2] IB/usnic: fix deadlock

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Fri, Feb 08, 2019 at 01:53:44PM -0800, Parvi Kaustubhi wrote:
> There is a dead lock in usnic ib_register and netdev_notify
> path.
> 
> 	usnic_ib_discover_pf()
> 	| mutex_lock(&usnic_ib_ibdev_list_lock);
> 	 | usnic_ib_device_add();
> 	  | ib_register_device()
> 	   | usnic_ib_query_port()
> 	    | mutex_lock(&us_ibdev->usdev_lock);
> 	     | ib_get_eth_speed()
> 	      | rtnl_lock()
> 
> order of lock: &usnic_ib_ibdev_list_lock -> usdev_lock -> rtnl_lock
> 
> 	rtnl_lock()
> 	 | usnic_ib_netdevice_event()
> 	  | mutex_lock(&usnic_ib_ibdev_list_lock);
> 
> order of lock: rtnl_lock -> &usnic_ib_ibdev_list_lock
> 
> Solution is to use ib_device_get_by_netdev() to lookup ib_dev while
> handling netdev/ inet events.
> 
> Signed-off-by: Parvi Kaustubhi <pkaustub@xxxxxxxxx>
> Reviewed-by: Govindarajulu Varadarajan <gvaradar@xxxxxxxxx>
> Reviewed-by: Tanmay Inamdar <tinamdar@xxxxxxxxx>
> Changelog:
> 
> v2->v3:
> * Jason: drivers should not hold any locks while calling ib_unregister_device()
> * Jason: use https://github.com/jgunthorpe/linux/commits/device_locking_cleanup
> 
> v1->v2:
> * Have notifier blocks in usnic_ib_dev instead of using workqueue to defer
> event handling.
>  drivers/infiniband/hw/usnic/usnic_ib_main.c | 34 ++++++++++++++---------------
>  1 file changed, 16 insertions(+), 18 deletions(-)
> 
> diff --git a/drivers/infiniband/hw/usnic/usnic_ib_main.c b/drivers/infiniband/hw/usnic/usnic_ib_main.c
> index 9529a08..53d6c12 100644
> +++ b/drivers/infiniband/hw/usnic/usnic_ib_main.c
> @@ -216,18 +216,17 @@ static int usnic_ib_netdevice_event(struct notifier_block *notifier,
>  					unsigned long event, void *ptr)
>  {
>  	struct usnic_ib_dev *us_ibdev;
> +	struct ib_device *ibdev;
>  
>  	struct net_device *netdev = netdev_notifier_info_to_dev(ptr);
>  
> -	mutex_lock(&usnic_ib_ibdev_list_lock);
> -	list_for_each_entry(us_ibdev, &usnic_ib_ibdev_list, ib_dev_link) {
> -		if (us_ibdev->netdev == netdev) {
> -			usnic_ib_handle_usdev_event(us_ibdev, event);
> -			break;
> -		}
> -	}
> -	mutex_unlock(&usnic_ib_ibdev_list_lock);
> -
> +	ibdev = ib_device_get_by_netdev(netdev, RDMA_DRIVER_USNIC);
> +	if (!ibdev)
> +		goto exit;
> +	else
> +		us_ibdev = container_of(ibdev, struct usnic_ib_dev, ib_dev);

Just do 

if (!ibdev)
    return NOTIFY_DONE

instead of all this wonky control flow.

> +	usnic_ib_handle_usdev_event(us_ibdev, event);

All the ib_device_get_* calls need to be paired with an
ib_device_put()

Jason



[Index of Archives]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Photo]     [Yosemite News]     [Yosemite Photos]     [Linux Kernel]     [Linux SCSI]     [XFree86]

  Powered by Linux