bnxt_re: Remove RTNL lock dependency in bnxt_re_query_port
authorSomnath Kotur <somnath.kotur@broadcom.com>
Thu, 31 Aug 2017 03:57:33 +0000 (09:27 +0530)
committerDoug Ledford <dledford@redhat.com>
Fri, 22 Sep 2017 17:57:33 +0000 (13:57 -0400)
When there is a NETDEV_UNREGISTER event, bnxt_re driver calls
ib_unregister_device() (RTNL lock held).
ib_unregister_device attempts to flush a worker queue scheduled by
ib_core and that queue might have a pending ib_query_port().
ib_query_port in turn calls bnxt_re_query_port(), which while querying the
link speed using ib_get_eth_speed(), tries to acquire the rtnl_lock() which
was already held by NETDEV_UNREGISTER.
Fixing the issue by removing the link speed query from bnxt_re_query_port()
Now the speed is queried post a successful ib_register_device or whenever
there is a NETDEV_CHANGE event.

Signed-off-by: Somnath Kotur <somnath.kotur@broadcom.com>
Signed-off-by: Doug Ledford <dledford@redhat.com>
drivers/infiniband/hw/bnxt_re/bnxt_re.h
drivers/infiniband/hw/bnxt_re/ib_verbs.c
drivers/infiniband/hw/bnxt_re/main.c

index a25f9d2408802d9650cfacf7afd9bbc950eed507..ecbac91b2e1441acf4529d6d40fa4256892349c0 100644 (file)
@@ -110,6 +110,8 @@ struct bnxt_re_dev {
 
        struct delayed_work             worker;
        u8                              cur_prio_map;
+       u8                              active_speed;
+       u8                              active_width;
 
        /* FP Notification Queue (CQ & SRQ) */
        struct tasklet_struct           nq_task;
index 0dbdbe1616abb807c478778181278d1154e331e3..7430ef07a0e173df55cb6bb70d1864e67b6c191e 100644 (file)
@@ -259,14 +259,9 @@ int bnxt_re_query_port(struct ib_device *ibdev, u8 port_num,
        port_attr->sm_sl = 0;
        port_attr->subnet_timeout = 0;
        port_attr->init_type_reply = 0;
-       /* call the underlying netdev's ethtool hooks to query speed settings
-        * for which we acquire rtnl_lock _only_ if it's registered with
-        * IB stack to avoid race in the NETDEV_UNREG path
-        */
-       if (test_bit(BNXT_RE_FLAG_IBDEV_REGISTERED, &rdev->flags))
-               if (ib_get_eth_speed(ibdev, port_num, &port_attr->active_speed,
-                                    &port_attr->active_width))
-                       return -EINVAL;
+       port_attr->active_speed = rdev->active_speed;
+       port_attr->active_width = rdev->active_width;
+
        return 0;
 }
 
index 29c3d7e254af73d8ec082fbefdb2843a793bbe6e..e7450ea92aa9e11ba0d28792f54fcf373e470a66 100644 (file)
@@ -1161,6 +1161,8 @@ static int bnxt_re_ib_reg(struct bnxt_re_dev *rdev)
                }
        }
        set_bit(BNXT_RE_FLAG_IBDEV_REGISTERED, &rdev->flags);
+       ib_get_eth_speed(&rdev->ibdev, 1, &rdev->active_speed,
+                        &rdev->active_width);
        bnxt_re_dispatch_event(&rdev->ibdev, NULL, 1, IB_EVENT_PORT_ACTIVE);
        bnxt_re_dispatch_event(&rdev->ibdev, NULL, 1, IB_EVENT_GID_CHANGE);
 
@@ -1255,6 +1257,8 @@ static void bnxt_re_task(struct work_struct *work)
                else if (netif_carrier_ok(rdev->netdev))
                        bnxt_re_dispatch_event(&rdev->ibdev, NULL, 1,
                                               IB_EVENT_PORT_ACTIVE);
+               ib_get_eth_speed(&rdev->ibdev, 1, &rdev->active_speed,
+                                &rdev->active_width);
                break;
        default:
                break;