diff options
author | Neil Horman <nhorman@tuxdriver.com> | 2013-01-15 14:34:40 -0500 |
---|---|---|
committer | Robert Love <robert.w.love@intel.com> | 2013-01-28 14:13:01 -0500 |
commit | f9184df3b99375964340c1a78e33f304bbf15f06 (patch) | |
tree | 740f3bdd627c95ee5856127dcbc0ff97ca310146 /drivers/scsi | |
parent | cf02820041668b14cbfa0fbd2bab45ac79bd6174 (diff) |
fcoe: close race on link speed detection in fcoe code
When creating an fcoe interfce, we call fcoe_link_speed_update before we add the
lports fcoe interface to the fc_hostlist. Since network device events like
NETDEV_CHANGE are only processed if an fcoe interface is found with an
underlying netdev that matches the netdev of the event. Since this processing
in fcoe_device_notification is how link_speed changes get communicated to the
libfc code (via fcoe_link_speed_update), we have a race condition - if a
NETDEV_CHANGE event is sent after the call to fcoe_link_speed_update in
fcoe_netdev_config, but before we add the interface to the fc_hostlist, we will
loose the event and attributes like /sys/class/fc_host/hostX/speed will not get
updated properly.
Fix this by moving the add to the fc_hostlist above the serialized call to
fcoe_netdev_config, ensuring that we catch netdev envents before we make a
direct call to fcoe_link_speed_update.
Also use this opportunity to clean up access to the fc_hostlist a bit by
creating a fcoe_hostlist_del accessor and replacing the cleanup in fcoe_exit to
use it properly.
Tested by myself successfully
[ Comment over 80 chars broken into multi-line by Robert Love to
satisfy checkpatch.pl ]
Signed-off-by: Neil Horman <nhorman@tuxdriver.com>
Reviewed-by: Yi Zou <yi.zou@intel.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
Diffstat (limited to 'drivers/scsi')
-rw-r--r-- | drivers/scsi/fcoe/fcoe.c | 31 |
1 files changed, 27 insertions, 4 deletions
diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index 0b333681a645..d605700f68cb 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c | |||
@@ -86,6 +86,7 @@ static int fcoe_link_ok(struct fc_lport *); | |||
86 | 86 | ||
87 | static struct fc_lport *fcoe_hostlist_lookup(const struct net_device *); | 87 | static struct fc_lport *fcoe_hostlist_lookup(const struct net_device *); |
88 | static int fcoe_hostlist_add(const struct fc_lport *); | 88 | static int fcoe_hostlist_add(const struct fc_lport *); |
89 | static void fcoe_hostlist_del(const struct fc_lport *); | ||
89 | 90 | ||
90 | static int fcoe_device_notification(struct notifier_block *, ulong, void *); | 91 | static int fcoe_device_notification(struct notifier_block *, ulong, void *); |
91 | static void fcoe_dev_setup(void); | 92 | static void fcoe_dev_setup(void); |
@@ -1119,6 +1120,12 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe, | |||
1119 | port->min_queue_depth = FCOE_MIN_QUEUE_DEPTH; | 1120 | port->min_queue_depth = FCOE_MIN_QUEUE_DEPTH; |
1120 | INIT_WORK(&port->destroy_work, fcoe_destroy_work); | 1121 | INIT_WORK(&port->destroy_work, fcoe_destroy_work); |
1121 | 1122 | ||
1123 | /* | ||
1124 | * Need to add the lport to the hostlist | ||
1125 | * so we catch NETDEV_CHANGE events. | ||
1126 | */ | ||
1127 | fcoe_hostlist_add(lport); | ||
1128 | |||
1122 | /* configure a fc_lport including the exchange manager */ | 1129 | /* configure a fc_lport including the exchange manager */ |
1123 | rc = fcoe_lport_config(lport); | 1130 | rc = fcoe_lport_config(lport); |
1124 | if (rc) { | 1131 | if (rc) { |
@@ -1190,6 +1197,7 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe, | |||
1190 | out_lp_destroy: | 1197 | out_lp_destroy: |
1191 | fc_exch_mgr_free(lport); | 1198 | fc_exch_mgr_free(lport); |
1192 | out_host_put: | 1199 | out_host_put: |
1200 | fcoe_hostlist_del(lport); | ||
1193 | scsi_host_put(lport->host); | 1201 | scsi_host_put(lport->host); |
1194 | out: | 1202 | out: |
1195 | return ERR_PTR(rc); | 1203 | return ERR_PTR(rc); |
@@ -2313,9 +2321,6 @@ static int _fcoe_create(struct net_device *netdev, enum fip_state fip_mode, | |||
2313 | /* setup DCB priority attributes. */ | 2321 | /* setup DCB priority attributes. */ |
2314 | fcoe_dcb_create(fcoe); | 2322 | fcoe_dcb_create(fcoe); |
2315 | 2323 | ||
2316 | /* add to lports list */ | ||
2317 | fcoe_hostlist_add(lport); | ||
2318 | |||
2319 | /* start FIP Discovery and FLOGI */ | 2324 | /* start FIP Discovery and FLOGI */ |
2320 | lport->boot_time = jiffies; | 2325 | lport->boot_time = jiffies; |
2321 | fc_fabric_login(lport); | 2326 | fc_fabric_login(lport); |
@@ -2523,6 +2528,24 @@ static int fcoe_hostlist_add(const struct fc_lport *lport) | |||
2523 | return 0; | 2528 | return 0; |
2524 | } | 2529 | } |
2525 | 2530 | ||
2531 | /** | ||
2532 | * fcoe_hostlist_del() - Remove the FCoE interface identified by a local | ||
2533 | * port to the hostlist | ||
2534 | * @lport: The local port that identifies the FCoE interface to be added | ||
2535 | * | ||
2536 | * Locking: must be called with the RTNL mutex held | ||
2537 | * | ||
2538 | */ | ||
2539 | static void fcoe_hostlist_del(const struct fc_lport *lport) | ||
2540 | { | ||
2541 | struct fcoe_interface *fcoe; | ||
2542 | struct fcoe_port *port; | ||
2543 | |||
2544 | port = lport_priv(lport); | ||
2545 | fcoe = port->priv; | ||
2546 | list_del(&fcoe->list); | ||
2547 | return; | ||
2548 | } | ||
2526 | 2549 | ||
2527 | static struct fcoe_transport fcoe_sw_transport = { | 2550 | static struct fcoe_transport fcoe_sw_transport = { |
2528 | .name = {FCOE_TRANSPORT_DEFAULT}, | 2551 | .name = {FCOE_TRANSPORT_DEFAULT}, |
@@ -2613,9 +2636,9 @@ static void __exit fcoe_exit(void) | |||
2613 | /* releases the associated fcoe hosts */ | 2636 | /* releases the associated fcoe hosts */ |
2614 | rtnl_lock(); | 2637 | rtnl_lock(); |
2615 | list_for_each_entry_safe(fcoe, tmp, &fcoe_hostlist, list) { | 2638 | list_for_each_entry_safe(fcoe, tmp, &fcoe_hostlist, list) { |
2616 | list_del(&fcoe->list); | ||
2617 | ctlr = fcoe_to_ctlr(fcoe); | 2639 | ctlr = fcoe_to_ctlr(fcoe); |
2618 | port = lport_priv(ctlr->lp); | 2640 | port = lport_priv(ctlr->lp); |
2641 | fcoe_hostlist_del(port->lport); | ||
2619 | queue_work(fcoe_wq, &port->destroy_work); | 2642 | queue_work(fcoe_wq, &port->destroy_work); |
2620 | } | 2643 | } |
2621 | rtnl_unlock(); | 2644 | rtnl_unlock(); |