Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

IB/usnic: Fix deadlock

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 the core's lock-free ib_device_get_by_netdev() scheme
to lookup ib_dev while handling netdev & inet events.

Signed-off-by: Parvi Kaustubhi <pkaustub@cisco.com>
Reviewed-by: Govindarajulu Varadarajan <gvaradar@cisco.com>
Reviewed-by: Tanmay Inamdar <tinamdar@cisco.com>
Signed-off-by: Jason Gunthorpe <jgg@mellanox.com>

authored by

Parvi Kaustubhi and committed by
Jason Gunthorpe
5bb3c1e9 ca22354b

+19 -28
+19 -17
drivers/infiniband/hw/usnic/usnic_ib_main.c
··· 216 216 unsigned long event, void *ptr) 217 217 { 218 218 struct usnic_ib_dev *us_ibdev; 219 + struct ib_device *ibdev; 219 220 220 221 struct net_device *netdev = netdev_notifier_info_to_dev(ptr); 221 222 222 - mutex_lock(&usnic_ib_ibdev_list_lock); 223 - list_for_each_entry(us_ibdev, &usnic_ib_ibdev_list, ib_dev_link) { 224 - if (us_ibdev->netdev == netdev) { 225 - usnic_ib_handle_usdev_event(us_ibdev, event); 226 - break; 227 - } 228 - } 229 - mutex_unlock(&usnic_ib_ibdev_list_lock); 223 + ibdev = ib_device_get_by_netdev(netdev, RDMA_DRIVER_USNIC); 224 + if (!ibdev) 225 + return NOTIFY_DONE; 230 226 227 + us_ibdev = container_of(ibdev, struct usnic_ib_dev, ib_dev); 228 + usnic_ib_handle_usdev_event(us_ibdev, event); 229 + ib_device_put(ibdev); 231 230 return NOTIFY_DONE; 232 231 } 233 232 ··· 281 282 struct usnic_ib_dev *us_ibdev; 282 283 struct in_ifaddr *ifa = ptr; 283 284 struct net_device *netdev = ifa->ifa_dev->dev; 285 + struct ib_device *ibdev; 284 286 285 - mutex_lock(&usnic_ib_ibdev_list_lock); 286 - list_for_each_entry(us_ibdev, &usnic_ib_ibdev_list, ib_dev_link) { 287 - if (us_ibdev->netdev == netdev) { 288 - usnic_ib_handle_inet_event(us_ibdev, event, ptr); 289 - break; 290 - } 291 - } 292 - mutex_unlock(&usnic_ib_ibdev_list_lock); 287 + ibdev = ib_device_get_by_netdev(netdev, RDMA_DRIVER_USNIC); 288 + if (!ibdev) 289 + return NOTIFY_DONE; 293 290 291 + us_ibdev = container_of(ibdev, struct usnic_ib_dev, ib_dev); 292 + usnic_ib_handle_inet_event(us_ibdev, event, ptr); 293 + ib_device_put(ibdev); 294 294 return NOTIFY_DONE; 295 295 } 296 296 static struct notifier_block usnic_ib_inetaddr_notifier = { ··· 340 342 .destroy_qp = usnic_ib_destroy_qp, 341 343 .get_dev_fw_str = usnic_get_dev_fw_str, 342 344 .get_link_layer = usnic_ib_port_link_layer, 343 - .get_netdev = usnic_get_netdev, 344 345 .get_port_immutable = usnic_port_immutable, 345 346 .mmap = usnic_ib_mmap, 346 347 .modify_qp = usnic_ib_modify_qp, ··· 359 362 union ib_gid gid; 360 363 struct in_device *ind; 361 364 struct net_device *netdev; 365 + int ret; 362 366 363 367 usnic_dbg("\n"); 364 368 netdev = pci_get_drvdata(dev); ··· 413 415 414 416 us_ibdev->ib_dev.driver_id = RDMA_DRIVER_USNIC; 415 417 rdma_set_device_sysfs_group(&us_ibdev->ib_dev, &usnic_attr_group); 418 + 419 + ret = ib_device_set_netdev(&us_ibdev->ib_dev, us_ibdev->netdev, 1); 420 + if (ret) 421 + goto err_fwd_dealloc; 416 422 417 423 if (ib_register_device(&us_ibdev->ib_dev, "usnic_%d")) 418 424 goto err_fwd_dealloc;
-10
drivers/infiniband/hw/usnic/usnic_ib_verbs.c
··· 437 437 return 0; 438 438 } 439 439 440 - struct net_device *usnic_get_netdev(struct ib_device *device, u8 port_num) 441 - { 442 - struct usnic_ib_dev *us_ibdev = to_usdev(device); 443 - 444 - if (us_ibdev->netdev) 445 - dev_hold(us_ibdev->netdev); 446 - 447 - return us_ibdev->netdev; 448 - } 449 - 450 440 int usnic_ib_query_pkey(struct ib_device *ibdev, u8 port, u16 index, 451 441 u16 *pkey) 452 442 {
-1
drivers/infiniband/hw/usnic/usnic_ib_verbs.h
··· 48 48 struct ib_qp_init_attr *qp_init_attr); 49 49 int usnic_ib_query_gid(struct ib_device *ibdev, u8 port, int index, 50 50 union ib_gid *gid); 51 - struct net_device *usnic_get_netdev(struct ib_device *device, u8 port_num); 52 51 int usnic_ib_query_pkey(struct ib_device *ibdev, u8 port, u16 index, 53 52 u16 *pkey); 54 53 int usnic_ib_alloc_pd(struct ib_pd *ibpd, struct ib_ucontext *context,