aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorNeil Horman <nhorman@tuxdriver.com>2013-01-15 14:34:40 -0500
committerRobert Love <robert.w.love@intel.com>2013-01-28 14:13:01 -0500
commitf9184df3b99375964340c1a78e33f304bbf15f06 (patch)
tree740f3bdd627c95ee5856127dcbc0ff97ca310146 /drivers
parentcf02820041668b14cbfa0fbd2bab45ac79bd6174 (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')
-rw-r--r--drivers/scsi/fcoe/fcoe.c31
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
87static struct fc_lport *fcoe_hostlist_lookup(const struct net_device *); 87static struct fc_lport *fcoe_hostlist_lookup(const struct net_device *);
88static int fcoe_hostlist_add(const struct fc_lport *); 88static int fcoe_hostlist_add(const struct fc_lport *);
89static void fcoe_hostlist_del(const struct fc_lport *);
89 90
90static int fcoe_device_notification(struct notifier_block *, ulong, void *); 91static int fcoe_device_notification(struct notifier_block *, ulong, void *);
91static void fcoe_dev_setup(void); 92static 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,
1190out_lp_destroy: 1197out_lp_destroy:
1191 fc_exch_mgr_free(lport); 1198 fc_exch_mgr_free(lport);
1192out_host_put: 1199out_host_put:
1200 fcoe_hostlist_del(lport);
1193 scsi_host_put(lport->host); 1201 scsi_host_put(lport->host);
1194out: 1202out:
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 */
2539static 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
2527static struct fcoe_transport fcoe_sw_transport = { 2550static 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();