diff options
author | Michael Chan <mchan@broadcom.com> | 2009-08-14 11:49:46 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-08-15 21:50:44 -0400 |
commit | 681dbd710779e8b8d5bae926f6b11f30df70638b (patch) | |
tree | 50605514ea7316f8a6316a93e205c0c9a3fb4399 /drivers/net/cnic.c | |
parent | c5a889508203446c1abc1d670599b3a816841813 (diff) |
cnic: Fix locking in start/stop calls.
The slow path ulp_start and ulp_stop calls to the bnx2i driver
are sleepable calls and therefore should not be protected using
rcu_read_lock. Fix it by using mutex and setting a bit during
these calls. cnic_unregister_device() will now wait for the bit
to clear before completing the call.
Signed-off-by: Michael Chan <mchan@broadcom.com>
Reviewed-by: Benjamin Li <benli@broadcom.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/cnic.c')
-rw-r--r-- | drivers/net/cnic.c | 44 |
1 files changed, 28 insertions, 16 deletions
diff --git a/drivers/net/cnic.c b/drivers/net/cnic.c index 2db81f0e4f72..4ff618a7afd2 100644 --- a/drivers/net/cnic.c +++ b/drivers/net/cnic.c | |||
@@ -466,6 +466,7 @@ EXPORT_SYMBOL(cnic_register_driver); | |||
466 | static int cnic_unregister_device(struct cnic_dev *dev, int ulp_type) | 466 | static int cnic_unregister_device(struct cnic_dev *dev, int ulp_type) |
467 | { | 467 | { |
468 | struct cnic_local *cp = dev->cnic_priv; | 468 | struct cnic_local *cp = dev->cnic_priv; |
469 | int i = 0; | ||
469 | 470 | ||
470 | if (ulp_type >= MAX_CNIC_ULP_TYPE) { | 471 | if (ulp_type >= MAX_CNIC_ULP_TYPE) { |
471 | printk(KERN_ERR PFX "cnic_unregister_device: Bad type %d\n", | 472 | printk(KERN_ERR PFX "cnic_unregister_device: Bad type %d\n", |
@@ -486,6 +487,15 @@ static int cnic_unregister_device(struct cnic_dev *dev, int ulp_type) | |||
486 | 487 | ||
487 | synchronize_rcu(); | 488 | synchronize_rcu(); |
488 | 489 | ||
490 | while (test_bit(ULP_F_CALL_PENDING, &cp->ulp_flags[ulp_type]) && | ||
491 | i < 20) { | ||
492 | msleep(100); | ||
493 | i++; | ||
494 | } | ||
495 | if (test_bit(ULP_F_CALL_PENDING, &cp->ulp_flags[ulp_type])) | ||
496 | printk(KERN_WARNING PFX "%s: Failed waiting for ULP up call" | ||
497 | " to complete.\n", dev->netdev->name); | ||
498 | |||
489 | return 0; | 499 | return 0; |
490 | } | 500 | } |
491 | EXPORT_SYMBOL(cnic_unregister_driver); | 501 | EXPORT_SYMBOL(cnic_unregister_driver); |
@@ -1076,18 +1086,23 @@ static void cnic_ulp_stop(struct cnic_dev *dev) | |||
1076 | if (cp->cnic_uinfo) | 1086 | if (cp->cnic_uinfo) |
1077 | cnic_send_nlmsg(cp, ISCSI_KEVENT_IF_DOWN, NULL); | 1087 | cnic_send_nlmsg(cp, ISCSI_KEVENT_IF_DOWN, NULL); |
1078 | 1088 | ||
1079 | rcu_read_lock(); | ||
1080 | for (if_type = 0; if_type < MAX_CNIC_ULP_TYPE; if_type++) { | 1089 | for (if_type = 0; if_type < MAX_CNIC_ULP_TYPE; if_type++) { |
1081 | struct cnic_ulp_ops *ulp_ops; | 1090 | struct cnic_ulp_ops *ulp_ops; |
1082 | 1091 | ||
1083 | ulp_ops = rcu_dereference(cp->ulp_ops[if_type]); | 1092 | mutex_lock(&cnic_lock); |
1084 | if (!ulp_ops) | 1093 | ulp_ops = cp->ulp_ops[if_type]; |
1094 | if (!ulp_ops) { | ||
1095 | mutex_unlock(&cnic_lock); | ||
1085 | continue; | 1096 | continue; |
1097 | } | ||
1098 | set_bit(ULP_F_CALL_PENDING, &cp->ulp_flags[if_type]); | ||
1099 | mutex_unlock(&cnic_lock); | ||
1086 | 1100 | ||
1087 | if (test_and_clear_bit(ULP_F_START, &cp->ulp_flags[if_type])) | 1101 | if (test_and_clear_bit(ULP_F_START, &cp->ulp_flags[if_type])) |
1088 | ulp_ops->cnic_stop(cp->ulp_handle[if_type]); | 1102 | ulp_ops->cnic_stop(cp->ulp_handle[if_type]); |
1103 | |||
1104 | clear_bit(ULP_F_CALL_PENDING, &cp->ulp_flags[if_type]); | ||
1089 | } | 1105 | } |
1090 | rcu_read_unlock(); | ||
1091 | } | 1106 | } |
1092 | 1107 | ||
1093 | static void cnic_ulp_start(struct cnic_dev *dev) | 1108 | static void cnic_ulp_start(struct cnic_dev *dev) |
@@ -1095,18 +1110,23 @@ static void cnic_ulp_start(struct cnic_dev *dev) | |||
1095 | struct cnic_local *cp = dev->cnic_priv; | 1110 | struct cnic_local *cp = dev->cnic_priv; |
1096 | int if_type; | 1111 | int if_type; |
1097 | 1112 | ||
1098 | rcu_read_lock(); | ||
1099 | for (if_type = 0; if_type < MAX_CNIC_ULP_TYPE; if_type++) { | 1113 | for (if_type = 0; if_type < MAX_CNIC_ULP_TYPE; if_type++) { |
1100 | struct cnic_ulp_ops *ulp_ops; | 1114 | struct cnic_ulp_ops *ulp_ops; |
1101 | 1115 | ||
1102 | ulp_ops = rcu_dereference(cp->ulp_ops[if_type]); | 1116 | mutex_lock(&cnic_lock); |
1103 | if (!ulp_ops || !ulp_ops->cnic_start) | 1117 | ulp_ops = cp->ulp_ops[if_type]; |
1118 | if (!ulp_ops || !ulp_ops->cnic_start) { | ||
1119 | mutex_unlock(&cnic_lock); | ||
1104 | continue; | 1120 | continue; |
1121 | } | ||
1122 | set_bit(ULP_F_CALL_PENDING, &cp->ulp_flags[if_type]); | ||
1123 | mutex_unlock(&cnic_lock); | ||
1105 | 1124 | ||
1106 | if (!test_and_set_bit(ULP_F_START, &cp->ulp_flags[if_type])) | 1125 | if (!test_and_set_bit(ULP_F_START, &cp->ulp_flags[if_type])) |
1107 | ulp_ops->cnic_start(cp->ulp_handle[if_type]); | 1126 | ulp_ops->cnic_start(cp->ulp_handle[if_type]); |
1127 | |||
1128 | clear_bit(ULP_F_CALL_PENDING, &cp->ulp_flags[if_type]); | ||
1108 | } | 1129 | } |
1109 | rcu_read_unlock(); | ||
1110 | } | 1130 | } |
1111 | 1131 | ||
1112 | static int cnic_ctl(void *data, struct cnic_ctl_info *info) | 1132 | static int cnic_ctl(void *data, struct cnic_ctl_info *info) |
@@ -1116,22 +1136,18 @@ static int cnic_ctl(void *data, struct cnic_ctl_info *info) | |||
1116 | switch (info->cmd) { | 1136 | switch (info->cmd) { |
1117 | case CNIC_CTL_STOP_CMD: | 1137 | case CNIC_CTL_STOP_CMD: |
1118 | cnic_hold(dev); | 1138 | cnic_hold(dev); |
1119 | mutex_lock(&cnic_lock); | ||
1120 | 1139 | ||
1121 | cnic_ulp_stop(dev); | 1140 | cnic_ulp_stop(dev); |
1122 | cnic_stop_hw(dev); | 1141 | cnic_stop_hw(dev); |
1123 | 1142 | ||
1124 | mutex_unlock(&cnic_lock); | ||
1125 | cnic_put(dev); | 1143 | cnic_put(dev); |
1126 | break; | 1144 | break; |
1127 | case CNIC_CTL_START_CMD: | 1145 | case CNIC_CTL_START_CMD: |
1128 | cnic_hold(dev); | 1146 | cnic_hold(dev); |
1129 | mutex_lock(&cnic_lock); | ||
1130 | 1147 | ||
1131 | if (!cnic_start_hw(dev)) | 1148 | if (!cnic_start_hw(dev)) |
1132 | cnic_ulp_start(dev); | 1149 | cnic_ulp_start(dev); |
1133 | 1150 | ||
1134 | mutex_unlock(&cnic_lock); | ||
1135 | cnic_put(dev); | 1151 | cnic_put(dev); |
1136 | break; | 1152 | break; |
1137 | default: | 1153 | default: |
@@ -2667,10 +2683,8 @@ static int cnic_netdev_event(struct notifier_block *this, unsigned long event, | |||
2667 | cnic_put(dev); | 2683 | cnic_put(dev); |
2668 | goto done; | 2684 | goto done; |
2669 | } | 2685 | } |
2670 | mutex_lock(&cnic_lock); | ||
2671 | if (!cnic_start_hw(dev)) | 2686 | if (!cnic_start_hw(dev)) |
2672 | cnic_ulp_start(dev); | 2687 | cnic_ulp_start(dev); |
2673 | mutex_unlock(&cnic_lock); | ||
2674 | } | 2688 | } |
2675 | 2689 | ||
2676 | rcu_read_lock(); | 2690 | rcu_read_lock(); |
@@ -2689,10 +2703,8 @@ static int cnic_netdev_event(struct notifier_block *this, unsigned long event, | |||
2689 | rcu_read_unlock(); | 2703 | rcu_read_unlock(); |
2690 | 2704 | ||
2691 | if (event == NETDEV_GOING_DOWN) { | 2705 | if (event == NETDEV_GOING_DOWN) { |
2692 | mutex_lock(&cnic_lock); | ||
2693 | cnic_ulp_stop(dev); | 2706 | cnic_ulp_stop(dev); |
2694 | cnic_stop_hw(dev); | 2707 | cnic_stop_hw(dev); |
2695 | mutex_unlock(&cnic_lock); | ||
2696 | cnic_unregister_netdev(dev); | 2708 | cnic_unregister_netdev(dev); |
2697 | } else if (event == NETDEV_UNREGISTER) { | 2709 | } else if (event == NETDEV_UNREGISTER) { |
2698 | write_lock(&cnic_dev_lock); | 2710 | write_lock(&cnic_dev_lock); |