diff options
author | Chris Leech <christopher.leech@intel.com> | 2009-08-25 17:00:23 -0400 |
---|---|---|
committer | James Bottomley <James.Bottomley@suse.de> | 2009-09-10 13:07:38 -0400 |
commit | 2e70e2415193b84c1b79ec373af15c3f280ad7c4 (patch) | |
tree | 84799295b00350f3fa33ca3becd7511d07e97791 /drivers/scsi | |
parent | c863df33bb784eecfb24090d2258fa0d3f653750 (diff) |
[SCSI] fcoe: Fix module ref count bug by adding NETDEV UNREGISTER handling
Fixes reference counting on fcoe_instance and net_device, and adds
NETDEV_UNREGISTER notifier handling so that you can unload network drivers.
FCoE no longer increments the module use count for the network driver.
On an NETDEV_UNREGISTER event, destroying the FCoE instance is deferred to a
workqueue context to avoid RTNL deadlocks.
Based in part by an earlier patch from John Fastabend
John's patch description:
Currently, the netdev module ref count is not decremented with module_put()
when the module is unloaded while fcoe instances are present. To fix this
removed reference count on netdev module completely and added functionality to
netdev event handling for NETDEV_UNREGISTER events.
This allows fcoe to remove devices cleanly when the netdev module is unloaded
so we no longer need to hold a reference count for the netdev module.
Signed-off-by: Chris Leech <christopher.leech@intel.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
Signed-off-by: James Bottomley <James.Bottomley@suse.de>
Diffstat (limited to 'drivers/scsi')
-rw-r--r-- | drivers/scsi/fcoe/fcoe.c | 186 | ||||
-rw-r--r-- | drivers/scsi/fcoe/fcoe.h | 2 |
2 files changed, 67 insertions, 121 deletions
diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index c9a0346e493a..c0264a984394 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c | |||
@@ -74,12 +74,13 @@ static int fcoe_link_ok(struct fc_lport *lp); | |||
74 | 74 | ||
75 | static struct fc_lport *fcoe_hostlist_lookup(const struct net_device *); | 75 | static struct fc_lport *fcoe_hostlist_lookup(const struct net_device *); |
76 | static int fcoe_hostlist_add(const struct fc_lport *); | 76 | static int fcoe_hostlist_add(const struct fc_lport *); |
77 | static int fcoe_hostlist_remove(const struct fc_lport *); | ||
78 | 77 | ||
79 | static void fcoe_check_wait_queue(struct fc_lport *, struct sk_buff *); | 78 | static void fcoe_check_wait_queue(struct fc_lport *, struct sk_buff *); |
80 | static int fcoe_device_notification(struct notifier_block *, ulong, void *); | 79 | static int fcoe_device_notification(struct notifier_block *, ulong, void *); |
81 | static void fcoe_dev_setup(void); | 80 | static void fcoe_dev_setup(void); |
82 | static void fcoe_dev_cleanup(void); | 81 | static void fcoe_dev_cleanup(void); |
82 | static struct fcoe_interface * | ||
83 | fcoe_hostlist_lookup_port(const struct net_device *dev); | ||
83 | 84 | ||
84 | /* notification function from net device */ | 85 | /* notification function from net device */ |
85 | static struct notifier_block fcoe_notifier = { | 86 | static struct notifier_block fcoe_notifier = { |
@@ -149,6 +150,7 @@ static int fcoe_fip_recv(struct sk_buff *skb, struct net_device *dev, | |||
149 | * @netdev : ptr to the associated netdevice struct | 150 | * @netdev : ptr to the associated netdevice struct |
150 | * | 151 | * |
151 | * Returns : 0 for success | 152 | * Returns : 0 for success |
153 | * Locking: must be called with the RTNL mutex held | ||
152 | */ | 154 | */ |
153 | static int fcoe_interface_setup(struct fcoe_interface *fcoe, | 155 | static int fcoe_interface_setup(struct fcoe_interface *fcoe, |
154 | struct net_device *netdev) | 156 | struct net_device *netdev) |
@@ -188,13 +190,11 @@ static int fcoe_interface_setup(struct fcoe_interface *fcoe, | |||
188 | * or enter promiscuous mode if not capable of listening | 190 | * or enter promiscuous mode if not capable of listening |
189 | * for multiple unicast MACs. | 191 | * for multiple unicast MACs. |
190 | */ | 192 | */ |
191 | rtnl_lock(); | ||
192 | memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN); | 193 | memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN); |
193 | dev_unicast_add(netdev, flogi_maddr); | 194 | dev_unicast_add(netdev, flogi_maddr); |
194 | if (fip->spma) | 195 | if (fip->spma) |
195 | dev_unicast_add(netdev, fip->ctl_src_addr); | 196 | dev_unicast_add(netdev, fip->ctl_src_addr); |
196 | dev_mc_add(netdev, FIP_ALL_ENODE_MACS, ETH_ALEN, 0); | 197 | dev_mc_add(netdev, FIP_ALL_ENODE_MACS, ETH_ALEN, 0); |
197 | rtnl_unlock(); | ||
198 | 198 | ||
199 | /* | 199 | /* |
200 | * setup the receive function from ethernet driver | 200 | * setup the receive function from ethernet driver |
@@ -215,6 +215,7 @@ static int fcoe_interface_setup(struct fcoe_interface *fcoe, | |||
215 | 215 | ||
216 | static void fcoe_fip_send(struct fcoe_ctlr *fip, struct sk_buff *skb); | 216 | static void fcoe_fip_send(struct fcoe_ctlr *fip, struct sk_buff *skb); |
217 | static void fcoe_update_src_mac(struct fcoe_ctlr *fip, u8 *old, u8 *new); | 217 | static void fcoe_update_src_mac(struct fcoe_ctlr *fip, u8 *old, u8 *new); |
218 | static void fcoe_destroy_work(struct work_struct *work); | ||
218 | 219 | ||
219 | /** | 220 | /** |
220 | * fcoe_interface_create() | 221 | * fcoe_interface_create() |
@@ -232,6 +233,7 @@ static struct fcoe_interface *fcoe_interface_create(struct net_device *netdev) | |||
232 | return NULL; | 233 | return NULL; |
233 | } | 234 | } |
234 | 235 | ||
236 | dev_hold(netdev); | ||
235 | kref_init(&fcoe->kref); | 237 | kref_init(&fcoe->kref); |
236 | 238 | ||
237 | /* | 239 | /* |
@@ -249,6 +251,8 @@ static struct fcoe_interface *fcoe_interface_create(struct net_device *netdev) | |||
249 | /** | 251 | /** |
250 | * fcoe_interface_cleanup() - clean up netdev configurations | 252 | * fcoe_interface_cleanup() - clean up netdev configurations |
251 | * @fcoe: | 253 | * @fcoe: |
254 | * | ||
255 | * Caller must be holding the RTNL mutex | ||
252 | */ | 256 | */ |
253 | void fcoe_interface_cleanup(struct fcoe_interface *fcoe) | 257 | void fcoe_interface_cleanup(struct fcoe_interface *fcoe) |
254 | { | 258 | { |
@@ -266,11 +270,7 @@ void fcoe_interface_cleanup(struct fcoe_interface *fcoe) | |||
266 | __dev_remove_pack(&fcoe->fip_packet_type); | 270 | __dev_remove_pack(&fcoe->fip_packet_type); |
267 | synchronize_net(); | 271 | synchronize_net(); |
268 | 272 | ||
269 | /* tear-down the FCoE controller */ | ||
270 | fcoe_ctlr_destroy(&fcoe->ctlr); | ||
271 | |||
272 | /* Delete secondary MAC addresses */ | 273 | /* Delete secondary MAC addresses */ |
273 | rtnl_lock(); | ||
274 | memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN); | 274 | memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN); |
275 | dev_unicast_delete(netdev, flogi_maddr); | 275 | dev_unicast_delete(netdev, flogi_maddr); |
276 | if (!is_zero_ether_addr(fip->data_src_addr)) | 276 | if (!is_zero_ether_addr(fip->data_src_addr)) |
@@ -278,7 +278,6 @@ void fcoe_interface_cleanup(struct fcoe_interface *fcoe) | |||
278 | if (fip->spma) | 278 | if (fip->spma) |
279 | dev_unicast_delete(netdev, fip->ctl_src_addr); | 279 | dev_unicast_delete(netdev, fip->ctl_src_addr); |
280 | dev_mc_delete(netdev, FIP_ALL_ENODE_MACS, ETH_ALEN, 0); | 280 | dev_mc_delete(netdev, FIP_ALL_ENODE_MACS, ETH_ALEN, 0); |
281 | rtnl_unlock(); | ||
282 | } | 281 | } |
283 | 282 | ||
284 | /** | 283 | /** |
@@ -288,10 +287,14 @@ void fcoe_interface_cleanup(struct fcoe_interface *fcoe) | |||
288 | static void fcoe_interface_release(struct kref *kref) | 287 | static void fcoe_interface_release(struct kref *kref) |
289 | { | 288 | { |
290 | struct fcoe_interface *fcoe; | 289 | struct fcoe_interface *fcoe; |
290 | struct net_device *netdev; | ||
291 | 291 | ||
292 | fcoe = container_of(kref, struct fcoe_interface, kref); | 292 | fcoe = container_of(kref, struct fcoe_interface, kref); |
293 | fcoe_interface_cleanup(fcoe); | 293 | netdev = fcoe->netdev; |
294 | /* tear-down the FCoE controller */ | ||
295 | fcoe_ctlr_destroy(&fcoe->ctlr); | ||
294 | kfree(fcoe); | 296 | kfree(fcoe); |
297 | dev_put(netdev); | ||
295 | } | 298 | } |
296 | 299 | ||
297 | /** | 300 | /** |
@@ -642,8 +645,7 @@ static void fcoe_if_destroy(struct fc_lport *lport) | |||
642 | /* Free memory used by statistical counters */ | 645 | /* Free memory used by statistical counters */ |
643 | fc_lport_free_stats(lport); | 646 | fc_lport_free_stats(lport); |
644 | 647 | ||
645 | /* Release the net_device and Scsi_Host */ | 648 | /* Release the Scsi_Host */ |
646 | dev_put(netdev); | ||
647 | scsi_host_put(lport->host); | 649 | scsi_host_put(lport->host); |
648 | } | 650 | } |
649 | 651 | ||
@@ -718,7 +720,9 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe, | |||
718 | } | 720 | } |
719 | lport = shost_priv(shost); | 721 | lport = shost_priv(shost); |
720 | port = lport_priv(lport); | 722 | port = lport_priv(lport); |
723 | port->lport = lport; | ||
721 | port->fcoe = fcoe; | 724 | port->fcoe = fcoe; |
725 | INIT_WORK(&port->destroy_work, fcoe_destroy_work); | ||
722 | 726 | ||
723 | /* configure fc_lport, e.g., em */ | 727 | /* configure fc_lport, e.g., em */ |
724 | rc = fcoe_lport_config(lport); | 728 | rc = fcoe_lport_config(lport); |
@@ -769,7 +773,6 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe, | |||
769 | goto out_lp_destroy; | 773 | goto out_lp_destroy; |
770 | } | 774 | } |
771 | 775 | ||
772 | dev_hold(netdev); | ||
773 | fcoe_interface_get(fcoe); | 776 | fcoe_interface_get(fcoe); |
774 | return lport; | 777 | return lport; |
775 | 778 | ||
@@ -1530,19 +1533,19 @@ static int fcoe_device_notification(struct notifier_block *notifier, | |||
1530 | struct fc_lport *lp = NULL; | 1533 | struct fc_lport *lp = NULL; |
1531 | struct net_device *netdev = ptr; | 1534 | struct net_device *netdev = ptr; |
1532 | struct fcoe_interface *fcoe; | 1535 | struct fcoe_interface *fcoe; |
1536 | struct fcoe_port *port; | ||
1533 | struct fcoe_dev_stats *stats; | 1537 | struct fcoe_dev_stats *stats; |
1534 | u32 link_possible = 1; | 1538 | u32 link_possible = 1; |
1535 | u32 mfs; | 1539 | u32 mfs; |
1536 | int rc = NOTIFY_OK; | 1540 | int rc = NOTIFY_OK; |
1537 | 1541 | ||
1538 | read_lock(&fcoe_hostlist_lock); | 1542 | write_lock(&fcoe_hostlist_lock); |
1539 | list_for_each_entry(fcoe, &fcoe_hostlist, list) { | 1543 | list_for_each_entry(fcoe, &fcoe_hostlist, list) { |
1540 | if (fcoe->netdev == netdev) { | 1544 | if (fcoe->netdev == netdev) { |
1541 | lp = fcoe->ctlr.lp; | 1545 | lp = fcoe->ctlr.lp; |
1542 | break; | 1546 | break; |
1543 | } | 1547 | } |
1544 | } | 1548 | } |
1545 | read_unlock(&fcoe_hostlist_lock); | ||
1546 | if (lp == NULL) { | 1549 | if (lp == NULL) { |
1547 | rc = NOTIFY_DONE; | 1550 | rc = NOTIFY_DONE; |
1548 | goto out; | 1551 | goto out; |
@@ -1564,6 +1567,13 @@ static int fcoe_device_notification(struct notifier_block *notifier, | |||
1564 | break; | 1567 | break; |
1565 | case NETDEV_REGISTER: | 1568 | case NETDEV_REGISTER: |
1566 | break; | 1569 | break; |
1570 | case NETDEV_UNREGISTER: | ||
1571 | list_del(&fcoe->list); | ||
1572 | port = lport_priv(fcoe->ctlr.lp); | ||
1573 | fcoe_interface_cleanup(fcoe); | ||
1574 | schedule_work(&port->destroy_work); | ||
1575 | goto out; | ||
1576 | break; | ||
1567 | default: | 1577 | default: |
1568 | FCOE_NETDEV_DBG(netdev, "Unknown event %ld " | 1578 | FCOE_NETDEV_DBG(netdev, "Unknown event %ld " |
1569 | "from netdev netlink\n", event); | 1579 | "from netdev netlink\n", event); |
@@ -1576,6 +1586,7 @@ static int fcoe_device_notification(struct notifier_block *notifier, | |||
1576 | fcoe_clean_pending_queue(lp); | 1586 | fcoe_clean_pending_queue(lp); |
1577 | } | 1587 | } |
1578 | out: | 1588 | out: |
1589 | write_unlock(&fcoe_hostlist_lock); | ||
1579 | return rc; | 1590 | return rc; |
1580 | } | 1591 | } |
1581 | 1592 | ||
@@ -1601,75 +1612,6 @@ static struct net_device *fcoe_if_to_netdev(const char *buffer) | |||
1601 | } | 1612 | } |
1602 | 1613 | ||
1603 | /** | 1614 | /** |
1604 | * fcoe_netdev_to_module_owner() - finds out the driver module of the netdev | ||
1605 | * @netdev: the target netdev | ||
1606 | * | ||
1607 | * Returns: ptr to the struct module, NULL for failure | ||
1608 | */ | ||
1609 | static struct module * | ||
1610 | fcoe_netdev_to_module_owner(const struct net_device *netdev) | ||
1611 | { | ||
1612 | struct device *dev; | ||
1613 | |||
1614 | if (!netdev) | ||
1615 | return NULL; | ||
1616 | |||
1617 | dev = netdev->dev.parent; | ||
1618 | if (!dev) | ||
1619 | return NULL; | ||
1620 | |||
1621 | if (!dev->driver) | ||
1622 | return NULL; | ||
1623 | |||
1624 | return dev->driver->owner; | ||
1625 | } | ||
1626 | |||
1627 | /** | ||
1628 | * fcoe_ethdrv_get() - Hold the Ethernet driver | ||
1629 | * @netdev: the target netdev | ||
1630 | * | ||
1631 | * Holds the Ethernet driver module by try_module_get() for | ||
1632 | * the corresponding netdev. | ||
1633 | * | ||
1634 | * Returns: 0 for success | ||
1635 | */ | ||
1636 | static int fcoe_ethdrv_get(const struct net_device *netdev) | ||
1637 | { | ||
1638 | struct module *owner; | ||
1639 | |||
1640 | owner = fcoe_netdev_to_module_owner(netdev); | ||
1641 | if (owner) { | ||
1642 | FCOE_NETDEV_DBG(netdev, "Hold driver module %s\n", | ||
1643 | module_name(owner)); | ||
1644 | return try_module_get(owner); | ||
1645 | } | ||
1646 | return -ENODEV; | ||
1647 | } | ||
1648 | |||
1649 | /** | ||
1650 | * fcoe_ethdrv_put() - Release the Ethernet driver | ||
1651 | * @netdev: the target netdev | ||
1652 | * | ||
1653 | * Releases the Ethernet driver module by module_put for | ||
1654 | * the corresponding netdev. | ||
1655 | * | ||
1656 | * Returns: 0 for success | ||
1657 | */ | ||
1658 | static int fcoe_ethdrv_put(const struct net_device *netdev) | ||
1659 | { | ||
1660 | struct module *owner; | ||
1661 | |||
1662 | owner = fcoe_netdev_to_module_owner(netdev); | ||
1663 | if (owner) { | ||
1664 | FCOE_NETDEV_DBG(netdev, "Release driver module %s\n", | ||
1665 | module_name(owner)); | ||
1666 | module_put(owner); | ||
1667 | return 0; | ||
1668 | } | ||
1669 | return -ENODEV; | ||
1670 | } | ||
1671 | |||
1672 | /** | ||
1673 | * fcoe_destroy() - handles the destroy from sysfs | 1615 | * fcoe_destroy() - handles the destroy from sysfs |
1674 | * @buffer: expected to be an eth if name | 1616 | * @buffer: expected to be an eth if name |
1675 | * @kp: associated kernel param | 1617 | * @kp: associated kernel param |
@@ -1678,10 +1620,8 @@ static int fcoe_ethdrv_put(const struct net_device *netdev) | |||
1678 | */ | 1620 | */ |
1679 | static int fcoe_destroy(const char *buffer, struct kernel_param *kp) | 1621 | static int fcoe_destroy(const char *buffer, struct kernel_param *kp) |
1680 | { | 1622 | { |
1681 | struct net_device *netdev; | ||
1682 | struct fcoe_interface *fcoe; | 1623 | struct fcoe_interface *fcoe; |
1683 | struct fcoe_port *port; | 1624 | struct net_device *netdev; |
1684 | struct fc_lport *lport; | ||
1685 | int rc; | 1625 | int rc; |
1686 | 1626 | ||
1687 | mutex_lock(&fcoe_config_mutex); | 1627 | mutex_lock(&fcoe_config_mutex); |
@@ -1702,19 +1642,20 @@ static int fcoe_destroy(const char *buffer, struct kernel_param *kp) | |||
1702 | rc = -ENODEV; | 1642 | rc = -ENODEV; |
1703 | goto out_nodev; | 1643 | goto out_nodev; |
1704 | } | 1644 | } |
1705 | /* look for existing lport */ | 1645 | |
1706 | lport = fcoe_hostlist_lookup(netdev); | 1646 | write_lock(&fcoe_hostlist_lock); |
1707 | if (!lport) { | 1647 | fcoe = fcoe_hostlist_lookup_port(netdev); |
1648 | if (!fcoe) { | ||
1649 | write_unlock(&fcoe_hostlist_lock); | ||
1708 | rc = -ENODEV; | 1650 | rc = -ENODEV; |
1709 | goto out_putdev; | 1651 | goto out_putdev; |
1710 | } | 1652 | } |
1711 | /* Remove the instance from fcoe's list */ | 1653 | list_del(&fcoe->list); |
1712 | fcoe_hostlist_remove(lport); | 1654 | write_unlock(&fcoe_hostlist_lock); |
1713 | port = lport_priv(lport); | 1655 | rtnl_lock(); |
1714 | fcoe = port->fcoe; | 1656 | fcoe_interface_cleanup(fcoe); |
1715 | fcoe_if_destroy(lport); | 1657 | rtnl_unlock(); |
1716 | fcoe_ethdrv_put(netdev); | 1658 | fcoe_if_destroy(fcoe->ctlr.lp); |
1717 | rc = 0; | ||
1718 | out_putdev: | 1659 | out_putdev: |
1719 | dev_put(netdev); | 1660 | dev_put(netdev); |
1720 | out_nodev: | 1661 | out_nodev: |
@@ -1722,6 +1663,16 @@ out_nodev: | |||
1722 | return rc; | 1663 | return rc; |
1723 | } | 1664 | } |
1724 | 1665 | ||
1666 | static void fcoe_destroy_work(struct work_struct *work) | ||
1667 | { | ||
1668 | struct fcoe_port *port; | ||
1669 | |||
1670 | port = container_of(work, struct fcoe_port, destroy_work); | ||
1671 | mutex_lock(&fcoe_config_mutex); | ||
1672 | fcoe_if_destroy(port->lport); | ||
1673 | mutex_unlock(&fcoe_config_mutex); | ||
1674 | } | ||
1675 | |||
1725 | /** | 1676 | /** |
1726 | * fcoe_create() - Handles the create call from sysfs | 1677 | * fcoe_create() - Handles the create call from sysfs |
1727 | * @buffer: expected to be an eth if name | 1678 | * @buffer: expected to be an eth if name |
@@ -1749,17 +1700,18 @@ static int fcoe_create(const char *buffer, struct kernel_param *kp) | |||
1749 | } | 1700 | } |
1750 | #endif | 1701 | #endif |
1751 | 1702 | ||
1703 | rtnl_lock(); | ||
1752 | netdev = fcoe_if_to_netdev(buffer); | 1704 | netdev = fcoe_if_to_netdev(buffer); |
1753 | if (!netdev) { | 1705 | if (!netdev) { |
1754 | rc = -ENODEV; | 1706 | rc = -ENODEV; |
1755 | goto out_nodev; | 1707 | goto out_nodev; |
1756 | } | 1708 | } |
1709 | |||
1757 | /* look for existing lport */ | 1710 | /* look for existing lport */ |
1758 | if (fcoe_hostlist_lookup(netdev)) { | 1711 | if (fcoe_hostlist_lookup(netdev)) { |
1759 | rc = -EEXIST; | 1712 | rc = -EEXIST; |
1760 | goto out_putdev; | 1713 | goto out_putdev; |
1761 | } | 1714 | } |
1762 | fcoe_ethdrv_get(netdev); | ||
1763 | 1715 | ||
1764 | fcoe = fcoe_interface_create(netdev); | 1716 | fcoe = fcoe_interface_create(netdev); |
1765 | if (!fcoe) { | 1717 | if (!fcoe) { |
@@ -1771,8 +1723,8 @@ static int fcoe_create(const char *buffer, struct kernel_param *kp) | |||
1771 | if (IS_ERR(lport)) { | 1723 | if (IS_ERR(lport)) { |
1772 | printk(KERN_ERR "fcoe: Failed to create interface (%s)\n", | 1724 | printk(KERN_ERR "fcoe: Failed to create interface (%s)\n", |
1773 | netdev->name); | 1725 | netdev->name); |
1774 | fcoe_ethdrv_put(netdev); | ||
1775 | rc = -EIO; | 1726 | rc = -EIO; |
1727 | fcoe_interface_cleanup(fcoe); | ||
1776 | goto out_free; | 1728 | goto out_free; |
1777 | } | 1729 | } |
1778 | 1730 | ||
@@ -1798,6 +1750,7 @@ out_free: | |||
1798 | out_putdev: | 1750 | out_putdev: |
1799 | dev_put(netdev); | 1751 | dev_put(netdev); |
1800 | out_nodev: | 1752 | out_nodev: |
1753 | rtnl_unlock(); | ||
1801 | mutex_unlock(&fcoe_config_mutex); | 1754 | mutex_unlock(&fcoe_config_mutex); |
1802 | return rc; | 1755 | return rc; |
1803 | } | 1756 | } |
@@ -1973,25 +1926,6 @@ int fcoe_hostlist_add(const struct fc_lport *lport) | |||
1973 | } | 1926 | } |
1974 | 1927 | ||
1975 | /** | 1928 | /** |
1976 | * fcoe_hostlist_remove() - remove a lport from lports list | ||
1977 | * @lp: ptr to the fc_lport to be removed | ||
1978 | * | ||
1979 | * Returns: 0 for success | ||
1980 | */ | ||
1981 | int fcoe_hostlist_remove(const struct fc_lport *lport) | ||
1982 | { | ||
1983 | struct fcoe_interface *fcoe; | ||
1984 | |||
1985 | write_lock_bh(&fcoe_hostlist_lock); | ||
1986 | fcoe = fcoe_hostlist_lookup_port(fcoe_netdev(lport)); | ||
1987 | BUG_ON(!fcoe); | ||
1988 | list_del(&fcoe->list); | ||
1989 | write_unlock_bh(&fcoe_hostlist_lock); | ||
1990 | |||
1991 | return 0; | ||
1992 | } | ||
1993 | |||
1994 | /** | ||
1995 | * fcoe_init() - fcoe module loading initialization | 1929 | * fcoe_init() - fcoe module loading initialization |
1996 | * | 1930 | * |
1997 | * Returns 0 on success, negative on failure | 1931 | * Returns 0 on success, negative on failure |
@@ -2046,6 +1980,7 @@ static void __exit fcoe_exit(void) | |||
2046 | unsigned int cpu; | 1980 | unsigned int cpu; |
2047 | struct fcoe_interface *fcoe, *tmp; | 1981 | struct fcoe_interface *fcoe, *tmp; |
2048 | LIST_HEAD(local_list); | 1982 | LIST_HEAD(local_list); |
1983 | struct fcoe_port *port; | ||
2049 | 1984 | ||
2050 | mutex_lock(&fcoe_config_mutex); | 1985 | mutex_lock(&fcoe_config_mutex); |
2051 | 1986 | ||
@@ -2058,7 +1993,11 @@ static void __exit fcoe_exit(void) | |||
2058 | 1993 | ||
2059 | list_for_each_entry_safe(fcoe, tmp, &local_list, list) { | 1994 | list_for_each_entry_safe(fcoe, tmp, &local_list, list) { |
2060 | list_del(&fcoe->list); | 1995 | list_del(&fcoe->list); |
2061 | fcoe_if_destroy(fcoe->ctlr.lp); | 1996 | port = lport_priv(fcoe->ctlr.lp); |
1997 | rtnl_lock(); | ||
1998 | fcoe_interface_cleanup(fcoe); | ||
1999 | rtnl_unlock(); | ||
2000 | schedule_work(&port->destroy_work); | ||
2062 | } | 2001 | } |
2063 | 2002 | ||
2064 | unregister_hotcpu_notifier(&fcoe_cpu_notifier); | 2003 | unregister_hotcpu_notifier(&fcoe_cpu_notifier); |
@@ -2066,9 +2005,14 @@ static void __exit fcoe_exit(void) | |||
2066 | for_each_online_cpu(cpu) | 2005 | for_each_online_cpu(cpu) |
2067 | fcoe_percpu_thread_destroy(cpu); | 2006 | fcoe_percpu_thread_destroy(cpu); |
2068 | 2007 | ||
2069 | /* detach from scsi transport */ | ||
2070 | fcoe_if_exit(); | ||
2071 | |||
2072 | mutex_unlock(&fcoe_config_mutex); | 2008 | mutex_unlock(&fcoe_config_mutex); |
2009 | |||
2010 | /* flush any asyncronous interface destroys, | ||
2011 | * this should happen after the netdev notifier is unregistered */ | ||
2012 | flush_scheduled_work(); | ||
2013 | |||
2014 | /* detach from scsi transport | ||
2015 | * must happen after all destroys are done, therefor after the flush */ | ||
2016 | fcoe_if_exit(); | ||
2073 | } | 2017 | } |
2074 | module_exit(fcoe_exit); | 2018 | module_exit(fcoe_exit); |
diff --git a/drivers/scsi/fcoe/fcoe.h b/drivers/scsi/fcoe/fcoe.h index ff229288b7f0..ce7f60fb1bc0 100644 --- a/drivers/scsi/fcoe/fcoe.h +++ b/drivers/scsi/fcoe/fcoe.h | |||
@@ -93,9 +93,11 @@ struct fcoe_interface { | |||
93 | */ | 93 | */ |
94 | struct fcoe_port { | 94 | struct fcoe_port { |
95 | struct fcoe_interface *fcoe; | 95 | struct fcoe_interface *fcoe; |
96 | struct fc_lport *lport; | ||
96 | struct sk_buff_head fcoe_pending_queue; | 97 | struct sk_buff_head fcoe_pending_queue; |
97 | u8 fcoe_pending_queue_active; | 98 | u8 fcoe_pending_queue_active; |
98 | struct timer_list timer; /* queue timer */ | 99 | struct timer_list timer; /* queue timer */ |
100 | struct work_struct destroy_work; /* to prevent rtnl deadlocks */ | ||
99 | }; | 101 | }; |
100 | 102 | ||
101 | #define fcoe_from_ctlr(fip) container_of(fip, struct fcoe_interface, ctlr) | 103 | #define fcoe_from_ctlr(fip) container_of(fip, struct fcoe_interface, ctlr) |