cnic: Don't take rcu_read_lock in cnic_rcv_netevent()
authorMichael Chan <mchan@broadcom.com>
Tue, 3 Jun 2014 06:08:46 +0000 (23:08 -0700)
committerDavid S. Miller <davem@davemloft.net>
Tue, 3 Jun 2014 06:16:41 +0000 (23:16 -0700)
Because the called function, such as bnx2fc_indicate_netevent(), can sleep,
we cannot take rcu_lock().  To prevent the rcu protected ulp_ops from going
away, we use the cnic_lock mutex and set the ULP_F_CALL_PENDING flag.
The code already waits for ULP_F_CALL_PENDING flag to clear in
cnic_unregister_device().

Signed-off-by: Michael Chan <mchan@broadcom.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/broadcom/cnic.c

index 09f3fefcbf9ce405839e6f5893174a79dc91c5b8..a0aae722ffcfe2734549d4b97262aa1543a72d17 100644 (file)
@@ -5624,20 +5624,27 @@ static void cnic_rcv_netevent(struct cnic_local *cp, unsigned long event,
 {
        int if_type;
 
-       rcu_read_lock();
        for (if_type = 0; if_type < MAX_CNIC_ULP_TYPE; if_type++) {
                struct cnic_ulp_ops *ulp_ops;
                void *ctx;
 
-               ulp_ops = rcu_dereference(cp->ulp_ops[if_type]);
-               if (!ulp_ops || !ulp_ops->indicate_netevent)
+               mutex_lock(&cnic_lock);
+               ulp_ops = rcu_dereference_protected(cp->ulp_ops[if_type],
+                                               lockdep_is_held(&cnic_lock));
+               if (!ulp_ops || !ulp_ops->indicate_netevent) {
+                       mutex_unlock(&cnic_lock);
                        continue;
+               }
 
                ctx = cp->ulp_handle[if_type];
 
+               set_bit(ULP_F_CALL_PENDING, &cp->ulp_flags[if_type]);
+               mutex_unlock(&cnic_lock);
+
                ulp_ops->indicate_netevent(ctx, event, vlan_id);
+
+               clear_bit(ULP_F_CALL_PENDING, &cp->ulp_flags[if_type]);
        }
-       rcu_read_unlock();
 }
 
 /* netdev event handler */