[SCSI] fcoe: add mutex to protect create and destroy
[pandora-kernel.git] / drivers / scsi / fcoe / fcoe.c
index 0a5609b..43added 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright(c) 2007 - 2008 Intel Corporation. All rights reserved.
+ * Copyright(c) 2007 - 2009 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms and conditions of the GNU General Public License,
@@ -49,6 +49,14 @@ MODULE_AUTHOR("Open-FCoE.org");
 MODULE_DESCRIPTION("FCoE");
 MODULE_LICENSE("GPL v2");
 
+/* Performance tuning parameters for fcoe */
+static unsigned int fcoe_ddp_min;
+module_param_named(ddp_min, fcoe_ddp_min, uint, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(ddp_min, "Minimum I/O size in bytes for "     \
+                "Direct Data Placement (DDP).");
+
+DEFINE_MUTEX(fcoe_config_mutex);
+
 /* fcoe host list */
 LIST_HEAD(fcoe_hostlist);
 DEFINE_RWLOCK(fcoe_hostlist_lock);
@@ -132,6 +140,178 @@ static struct scsi_host_template fcoe_shost_template = {
        .max_sectors = 0xffff,
 };
 
+static int fcoe_fip_recv(struct sk_buff *skb, struct net_device *dev,
+                        struct packet_type *ptype,
+                        struct net_device *orig_dev);
+/**
+ * fcoe_interface_setup()
+ * @fcoe: new fcoe_interface
+ * @netdev : ptr to the associated netdevice struct
+ *
+ * Returns : 0 for success
+ */
+static int fcoe_interface_setup(struct fcoe_interface *fcoe,
+                               struct net_device *netdev)
+{
+       struct fcoe_ctlr *fip = &fcoe->ctlr;
+       struct netdev_hw_addr *ha;
+       u8 flogi_maddr[ETH_ALEN];
+
+       fcoe->netdev = netdev;
+
+       /* Do not support for bonding device */
+       if ((netdev->priv_flags & IFF_MASTER_ALB) ||
+           (netdev->priv_flags & IFF_SLAVE_INACTIVE) ||
+           (netdev->priv_flags & IFF_MASTER_8023AD)) {
+               return -EOPNOTSUPP;
+       }
+
+       /* look for SAN MAC address, if multiple SAN MACs exist, only
+        * use the first one for SPMA */
+       rcu_read_lock();
+       for_each_dev_addr(netdev, ha) {
+               if ((ha->type == NETDEV_HW_ADDR_T_SAN) &&
+                   (is_valid_ether_addr(fip->ctl_src_addr))) {
+                       memcpy(fip->ctl_src_addr, ha->addr, ETH_ALEN);
+                       fip->spma = 1;
+                       break;
+               }
+       }
+       rcu_read_unlock();
+
+       /* setup Source Mac Address */
+       if (!fip->spma)
+               memcpy(fip->ctl_src_addr, netdev->dev_addr, netdev->addr_len);
+
+       /*
+        * Add FCoE MAC address as second unicast MAC address
+        * or enter promiscuous mode if not capable of listening
+        * for multiple unicast MACs.
+        */
+       rtnl_lock();
+       memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN);
+       dev_unicast_add(netdev, flogi_maddr);
+       if (fip->spma)
+               dev_unicast_add(netdev, fip->ctl_src_addr);
+       dev_mc_add(netdev, FIP_ALL_ENODE_MACS, ETH_ALEN, 0);
+       rtnl_unlock();
+
+       /*
+        * setup the receive function from ethernet driver
+        * on the ethertype for the given device
+        */
+       fcoe->fcoe_packet_type.func = fcoe_rcv;
+       fcoe->fcoe_packet_type.type = __constant_htons(ETH_P_FCOE);
+       fcoe->fcoe_packet_type.dev = netdev;
+       dev_add_pack(&fcoe->fcoe_packet_type);
+
+       fcoe->fip_packet_type.func = fcoe_fip_recv;
+       fcoe->fip_packet_type.type = htons(ETH_P_FIP);
+       fcoe->fip_packet_type.dev = netdev;
+       dev_add_pack(&fcoe->fip_packet_type);
+
+       return 0;
+}
+
+static void fcoe_fip_send(struct fcoe_ctlr *fip, struct sk_buff *skb);
+static void fcoe_update_src_mac(struct fcoe_ctlr *fip, u8 *old, u8 *new);
+
+/**
+ * fcoe_interface_create()
+ * @netdev: network interface
+ *
+ * Returns: pointer to a struct fcoe_interface or NULL on error
+ */
+static struct fcoe_interface *fcoe_interface_create(struct net_device *netdev)
+{
+       struct fcoe_interface *fcoe;
+
+       fcoe = kzalloc(sizeof(*fcoe), GFP_KERNEL);
+       if (!fcoe) {
+               FCOE_NETDEV_DBG(netdev, "Could not allocate fcoe structure\n");
+               return NULL;
+       }
+
+       kref_init(&fcoe->kref);
+
+       /*
+        * Initialize FIP.
+        */
+       fcoe_ctlr_init(&fcoe->ctlr);
+       fcoe->ctlr.send = fcoe_fip_send;
+       fcoe->ctlr.update_mac = fcoe_update_src_mac;
+
+       fcoe_interface_setup(fcoe, netdev);
+
+       return fcoe;
+}
+
+/**
+ * fcoe_interface_cleanup() - clean up netdev configurations
+ * @fcoe:
+ */
+void fcoe_interface_cleanup(struct fcoe_interface *fcoe)
+{
+       struct net_device *netdev = fcoe->netdev;
+       struct fcoe_ctlr *fip = &fcoe->ctlr;
+       u8 flogi_maddr[ETH_ALEN];
+
+       /*
+        * Don't listen for Ethernet packets anymore.
+        * synchronize_net() ensures that the packet handlers are not running
+        * on another CPU. dev_remove_pack() would do that, this calls the
+        * unsyncronized version __dev_remove_pack() to avoid multiple delays.
+        */
+       __dev_remove_pack(&fcoe->fcoe_packet_type);
+       __dev_remove_pack(&fcoe->fip_packet_type);
+       synchronize_net();
+
+       /* tear-down the FCoE controller */
+       fcoe_ctlr_destroy(&fcoe->ctlr);
+
+       /* Delete secondary MAC addresses */
+       rtnl_lock();
+       memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN);
+       dev_unicast_delete(netdev, flogi_maddr);
+       if (!is_zero_ether_addr(fip->data_src_addr))
+               dev_unicast_delete(netdev, fip->data_src_addr);
+       if (fip->spma)
+               dev_unicast_delete(netdev, fip->ctl_src_addr);
+       dev_mc_delete(netdev, FIP_ALL_ENODE_MACS, ETH_ALEN, 0);
+       rtnl_unlock();
+}
+
+/**
+ * fcoe_interface_release() - fcoe_port kref release function
+ * @kref: embedded reference count in an fcoe_interface struct
+ */
+static void fcoe_interface_release(struct kref *kref)
+{
+       struct fcoe_interface *fcoe;
+
+       fcoe = container_of(kref, struct fcoe_interface, kref);
+       fcoe_interface_cleanup(fcoe);
+       kfree(fcoe);
+}
+
+/**
+ * fcoe_interface_get()
+ * @fcoe:
+ */
+static inline void fcoe_interface_get(struct fcoe_interface *fcoe)
+{
+       kref_get(&fcoe->kref);
+}
+
+/**
+ * fcoe_interface_put()
+ * @fcoe:
+ */
+static inline void fcoe_interface_put(struct fcoe_interface *fcoe)
+{
+       kref_put(&fcoe->kref, fcoe_interface_release);
+}
+
 /**
  * fcoe_fip_recv - handle a received FIP frame.
  * @skb: the receive skb
@@ -145,10 +325,10 @@ static int fcoe_fip_recv(struct sk_buff *skb, struct net_device *dev,
                         struct packet_type *ptype,
                         struct net_device *orig_dev)
 {
-       struct fcoe_softc *fc;
+       struct fcoe_interface *fcoe;
 
-       fc = container_of(ptype, struct fcoe_softc, fip_packet_type);
-       fcoe_ctlr_recv(&fc->ctlr, skb);
+       fcoe = container_of(ptype, struct fcoe_interface, fip_packet_type);
+       fcoe_ctlr_recv(&fcoe->ctlr, skb);
        return 0;
 }
 
@@ -159,7 +339,7 @@ static int fcoe_fip_recv(struct sk_buff *skb, struct net_device *dev,
  */
 static void fcoe_fip_send(struct fcoe_ctlr *fip, struct sk_buff *skb)
 {
-       skb->dev = fcoe_from_ctlr(fip)->real_dev;
+       skb->dev = fcoe_from_ctlr(fip)->netdev;
        dev_queue_xmit(skb);
 }
 
@@ -174,13 +354,13 @@ static void fcoe_fip_send(struct fcoe_ctlr *fip, struct sk_buff *skb)
  */
 static void fcoe_update_src_mac(struct fcoe_ctlr *fip, u8 *old, u8 *new)
 {
-       struct fcoe_softc *fc;
+       struct fcoe_interface *fcoe;
 
-       fc = fcoe_from_ctlr(fip);
+       fcoe = fcoe_from_ctlr(fip);
        rtnl_lock();
        if (!is_zero_ether_addr(old))
-               dev_unicast_delete(fc->real_dev, old);
-       dev_unicast_add(fc->real_dev, new);
+               dev_unicast_delete(fcoe->netdev, old);
+       dev_unicast_add(fcoe->netdev, new);
        rtnl_unlock();
 }
 
@@ -216,30 +396,6 @@ static int fcoe_lport_config(struct fc_lport *lp)
        return 0;
 }
 
-/**
- * fcoe_netdev_cleanup() - clean up netdev configurations
- * @fc: ptr to the fcoe_softc
- */
-void fcoe_netdev_cleanup(struct fcoe_softc *fc)
-{
-       u8 flogi_maddr[ETH_ALEN];
-
-       /* Don't listen for Ethernet packets anymore */
-       dev_remove_pack(&fc->fcoe_packet_type);
-       dev_remove_pack(&fc->fip_packet_type);
-
-       /* Delete secondary MAC addresses */
-       rtnl_lock();
-       memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN);
-       dev_unicast_delete(fc->real_dev, flogi_maddr);
-       if (!is_zero_ether_addr(fc->ctlr.data_src_addr))
-               dev_unicast_delete(fc->real_dev, fc->ctlr.data_src_addr);
-       if (fc->ctlr.spma)
-               dev_unicast_delete(fc->real_dev, fc->ctlr.ctl_src_addr);
-       dev_mc_delete(fc->real_dev, FIP_ALL_ENODE_MACS, ETH_ALEN, 0);
-       rtnl_unlock();
-}
-
 /**
  * fcoe_queue_timer() - fcoe queue timer
  * @lp: the fc_lport pointer
@@ -265,116 +421,53 @@ static int fcoe_netdev_config(struct fc_lport *lp, struct net_device *netdev)
 {
        u32 mfs;
        u64 wwnn, wwpn;
-       struct fcoe_softc *fc;
-       u8 flogi_maddr[ETH_ALEN];
-       struct netdev_hw_addr *ha;
+       struct fcoe_interface *fcoe;
+       struct fcoe_port *port;
 
        /* Setup lport private data to point to fcoe softc */
-       fc = lport_priv(lp);
-       fc->ctlr.lp = lp;
-       fc->real_dev = netdev;
-       fc->phys_dev = netdev;
-
-       /* Require support for get_pauseparam ethtool op. */
-       if (netdev->priv_flags & IFF_802_1Q_VLAN)
-               fc->phys_dev = vlan_dev_real_dev(netdev);
-
-       /* Do not support for bonding device */
-       if ((fc->real_dev->priv_flags & IFF_MASTER_ALB) ||
-           (fc->real_dev->priv_flags & IFF_SLAVE_INACTIVE) ||
-           (fc->real_dev->priv_flags & IFF_MASTER_8023AD)) {
-               return -EOPNOTSUPP;
-       }
+       port = lport_priv(lp);
+       fcoe = port->fcoe;
 
        /*
         * Determine max frame size based on underlying device and optional
         * user-configured limit.  If the MFS is too low, fcoe_link_ok()
         * will return 0, so do this first.
         */
-       mfs = fc->real_dev->mtu - (sizeof(struct fcoe_hdr) +
-                                  sizeof(struct fcoe_crc_eof));
+       mfs = netdev->mtu - (sizeof(struct fcoe_hdr) +
+                            sizeof(struct fcoe_crc_eof));
        if (fc_set_mfs(lp, mfs))
                return -EINVAL;
 
        /* offload features support */
-       if (fc->real_dev->features & NETIF_F_SG)
+       if (netdev->features & NETIF_F_SG)
                lp->sg_supp = 1;
 
-#ifdef NETIF_F_FCOE_CRC
        if (netdev->features & NETIF_F_FCOE_CRC) {
                lp->crc_offload = 1;
                FCOE_NETDEV_DBG(netdev, "Supports FCCRC offload\n");
        }
-#endif
-#ifdef NETIF_F_FSO
        if (netdev->features & NETIF_F_FSO) {
                lp->seq_offload = 1;
                lp->lso_max = netdev->gso_max_size;
                FCOE_NETDEV_DBG(netdev, "Supports LSO for max len 0x%x\n",
                                lp->lso_max);
        }
-#endif
        if (netdev->fcoe_ddp_xid) {
                lp->lro_enabled = 1;
                lp->lro_xid = netdev->fcoe_ddp_xid;
                FCOE_NETDEV_DBG(netdev, "Supports LRO for max xid 0x%x\n",
                                lp->lro_xid);
        }
-       skb_queue_head_init(&fc->fcoe_pending_queue);
-       fc->fcoe_pending_queue_active = 0;
-       setup_timer(&fc->timer, fcoe_queue_timer, (unsigned long)lp);
+       skb_queue_head_init(&port->fcoe_pending_queue);
+       port->fcoe_pending_queue_active = 0;
+       setup_timer(&port->timer, fcoe_queue_timer, (unsigned long)lp);
 
-       /* look for SAN MAC address, if multiple SAN MACs exist, only
-        * use the first one for SPMA */
-       rcu_read_lock();
-       for_each_dev_addr(netdev, ha) {
-               if ((ha->type == NETDEV_HW_ADDR_T_SAN) &&
-                   (is_valid_ether_addr(fc->ctlr.ctl_src_addr))) {
-                       memcpy(fc->ctlr.ctl_src_addr, ha->addr, ETH_ALEN);
-                       fc->ctlr.spma = 1;
-                       break;
-               }
-       }
-       rcu_read_unlock();
-
-       /* setup Source Mac Address */
-       if (!fc->ctlr.spma)
-               memcpy(fc->ctlr.ctl_src_addr, fc->real_dev->dev_addr,
-                      fc->real_dev->addr_len);
-
-       wwnn = fcoe_wwn_from_mac(fc->real_dev->dev_addr, 1, 0);
+       wwnn = fcoe_wwn_from_mac(netdev->dev_addr, 1, 0);
        fc_set_wwnn(lp, wwnn);
        /* XXX - 3rd arg needs to be vlan id */
-       wwpn = fcoe_wwn_from_mac(fc->real_dev->dev_addr, 2, 0);
+       wwpn = fcoe_wwn_from_mac(netdev->dev_addr, 2, 0);
        fc_set_wwpn(lp, wwpn);
 
-       /*
-        * Add FCoE MAC address as second unicast MAC address
-        * or enter promiscuous mode if not capable of listening
-        * for multiple unicast MACs.
-        */
-       rtnl_lock();
-       memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN);
-       dev_unicast_add(fc->real_dev, flogi_maddr);
-       if (fc->ctlr.spma)
-               dev_unicast_add(fc->real_dev, fc->ctlr.ctl_src_addr);
-       dev_mc_add(fc->real_dev, FIP_ALL_ENODE_MACS, ETH_ALEN, 0);
-       rtnl_unlock();
-
-       /*
-        * setup the receive function from ethernet driver
-        * on the ethertype for the given device
-        */
-       fc->fcoe_packet_type.func = fcoe_rcv;
-       fc->fcoe_packet_type.type = __constant_htons(ETH_P_FCOE);
-       fc->fcoe_packet_type.dev = fc->real_dev;
-       dev_add_pack(&fc->fcoe_packet_type);
-
-       fc->fip_packet_type.func = fcoe_fip_recv;
-       fc->fip_packet_type.type = htons(ETH_P_FIP);
-       fc->fip_packet_type.dev = fc->real_dev;
-       dev_add_pack(&fc->fip_packet_type);
-
        return 0;
 }
 
@@ -415,86 +508,146 @@ static int fcoe_shost_config(struct fc_lport *lp, struct Scsi_Host *shost,
        return 0;
 }
 
+/*
+ * fcoe_oem_match() - match for read types IO
+ * @fp: the fc_frame for new IO.
+ *
+ * Returns : true for read types IO, otherwise returns false.
+ */
+bool fcoe_oem_match(struct fc_frame *fp)
+{
+       return fc_fcp_is_read(fr_fsp(fp)) &&
+               (fr_fsp(fp)->data_len > fcoe_ddp_min);
+}
+
 /**
  * fcoe_em_config() - allocates em for this lport
- * @lp: the port that em is to allocated for
+ * @lp: the fcoe that em is to allocated for
+ *
+ * Called with write fcoe_hostlist_lock held.
  *
  * Returns : 0 on success
  */
 static inline int fcoe_em_config(struct fc_lport *lp)
 {
-       BUG_ON(lp->emp);
+       struct fcoe_port *port = lport_priv(lp);
+       struct fcoe_interface *fcoe = port->fcoe;
+       struct fcoe_interface *oldfcoe = NULL;
+       struct net_device *old_real_dev, *cur_real_dev;
+       u16 min_xid = FCOE_MIN_XID;
+       u16 max_xid = FCOE_MAX_XID;
+
+       /*
+        * Check if need to allocate an em instance for
+        * offload exchange ids to be shared across all VN_PORTs/lport.
+        */
+       if (!lp->lro_enabled || !lp->lro_xid || (lp->lro_xid >= max_xid)) {
+               lp->lro_xid = 0;
+               goto skip_oem;
+       }
+
+       /*
+        * Reuse existing offload em instance in case
+        * it is already allocated on real eth device
+        */
+       if (fcoe->netdev->priv_flags & IFF_802_1Q_VLAN)
+               cur_real_dev = vlan_dev_real_dev(fcoe->netdev);
+       else
+               cur_real_dev = fcoe->netdev;
+
+       list_for_each_entry(oldfcoe, &fcoe_hostlist, list) {
+               if (oldfcoe->netdev->priv_flags & IFF_802_1Q_VLAN)
+                       old_real_dev = vlan_dev_real_dev(oldfcoe->netdev);
+               else
+                       old_real_dev = oldfcoe->netdev;
+
+               if (cur_real_dev == old_real_dev) {
+                       fcoe->oem = oldfcoe->oem;
+                       break;
+               }
+       }
+
+       if (fcoe->oem) {
+               if (!fc_exch_mgr_add(lp, fcoe->oem, fcoe_oem_match)) {
+                       printk(KERN_ERR "fcoe_em_config: failed to add "
+                              "offload em:%p on interface:%s\n",
+                              fcoe->oem, fcoe->netdev->name);
+                       return -ENOMEM;
+               }
+       } else {
+               fcoe->oem = fc_exch_mgr_alloc(lp, FC_CLASS_3,
+                                           FCOE_MIN_XID, lp->lro_xid,
+                                           fcoe_oem_match);
+               if (!fcoe->oem) {
+                       printk(KERN_ERR "fcoe_em_config: failed to allocate "
+                              "em for offload exches on interface:%s\n",
+                              fcoe->netdev->name);
+                       return -ENOMEM;
+               }
+       }
 
-       lp->emp = fc_exch_mgr_alloc(lp, FC_CLASS_3,
-                                   FCOE_MIN_XID, FCOE_MAX_XID);
-       if (!lp->emp)
+       /*
+        * Exclude offload EM xid range from next EM xid range.
+        */
+       min_xid += lp->lro_xid + 1;
+
+skip_oem:
+       if (!fc_exch_mgr_alloc(lp, FC_CLASS_3, min_xid, max_xid, NULL)) {
+               printk(KERN_ERR "fcoe_em_config: failed to "
+                      "allocate em on interface %s\n", fcoe->netdev->name);
                return -ENOMEM;
+       }
 
        return 0;
 }
 
 /**
  * fcoe_if_destroy() - FCoE software HBA tear-down function
- * @netdev: ptr to the associated net_device
- *
- * Returns: 0 if link is OK for use by FCoE.
+ * @lport: fc_lport to destroy
  */
-static int fcoe_if_destroy(struct net_device *netdev)
+static void fcoe_if_destroy(struct fc_lport *lport)
 {
-       struct fc_lport *lp = NULL;
-       struct fcoe_softc *fc;
-
-       BUG_ON(!netdev);
+       struct fcoe_port *port = lport_priv(lport);
+       struct fcoe_interface *fcoe = port->fcoe;
+       struct net_device *netdev = fcoe->netdev;
 
        FCOE_NETDEV_DBG(netdev, "Destroying interface\n");
 
-       lp = fcoe_hostlist_lookup(netdev);
-       if (!lp)
-               return -ENODEV;
-
-       fc = lport_priv(lp);
-
        /* Logout of the fabric */
-       fc_fabric_logoff(lp);
+       fc_fabric_logoff(lport);
 
        /* Remove the instance from fcoe's list */
-       fcoe_hostlist_remove(lp);
-
-       /* clean up netdev configurations */
-       fcoe_netdev_cleanup(fc);
-
-       /* tear-down the FCoE controller */
-       fcoe_ctlr_destroy(&fc->ctlr);
+       fcoe_hostlist_remove(lport);
 
        /* Cleanup the fc_lport */
-       fc_lport_destroy(lp);
-       fc_fcp_destroy(lp);
+       fc_lport_destroy(lport);
+       fc_fcp_destroy(lport);
 
-       /* Detach from the scsi-ml */
-       fc_remove_host(lp->host);
-       scsi_remove_host(lp->host);
+       /* Stop the transmit retry timer */
+       del_timer_sync(&port->timer);
 
-       /* There are no more rports or I/O, free the EM */
-       if (lp->emp)
-               fc_exch_mgr_free(lp->emp);
+       /* Free existing transmit skbs */
+       fcoe_clean_pending_queue(lport);
 
-       /* Free the per-CPU receive threads */
-       fcoe_percpu_clean(lp);
+       /* receives may not be stopped until after this */
+       fcoe_interface_put(fcoe);
 
-       /* Free existing skbs */
-       fcoe_clean_pending_queue(lp);
+       /* Free queued packets for the per-CPU receive threads */
+       fcoe_percpu_clean(lport);
 
-       /* Stop the timer */
-       del_timer_sync(&fc->timer);
+       /* Detach from the scsi-ml */
+       fc_remove_host(lport->host);
+       scsi_remove_host(lport->host);
+
+       /* There are no more rports or I/O, free the EM */
+       fc_exch_mgr_free(lport);
 
        /* Free memory used by statistical counters */
-       fc_lport_free_stats(lp);
+       fc_lport_free_stats(lport);
 
        /* Release the net_device and Scsi_Host */
-       dev_put(fc->real_dev);
-       scsi_host_put(lp->host);
-
-       return 0;
+       dev_put(netdev);
+       scsi_host_put(lport->host);
 }
 
 /*
@@ -540,106 +693,97 @@ static struct libfc_function_template fcoe_libfc_fcn_templ = {
 };
 
 /**
- * fcoe_if_create() - this function creates the fcoe interface
- * @netdev: pointer the associated netdevice
+ * fcoe_if_create() - this function creates the fcoe port
+ * @fcoe: fcoe_interface structure to create an fc_lport instance on
+ * @parent: device pointer to be the parent in sysfs for the SCSI host
  *
- * Creates fc_lport struct and scsi_host for lport, configures lport
- * and starts fabric login.
+ * Creates fc_lport struct and scsi_host for lport, configures lport.
  *
- * Returns : 0 on success
+ * Returns : The allocated fc_lport or an error pointer
  */
-static int fcoe_if_create(struct net_device *netdev)
+static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe,
+                                      struct device *parent)
 {
        int rc;
-       struct fc_lport *lp = NULL;
-       struct fcoe_softc *fc;
+       struct fc_lport *lport = NULL;
+       struct fcoe_port *port;
        struct Scsi_Host *shost;
-
-       BUG_ON(!netdev);
+       struct net_device *netdev = fcoe->netdev;
 
        FCOE_NETDEV_DBG(netdev, "Create Interface\n");
 
-       lp = fcoe_hostlist_lookup(netdev);
-       if (lp)
-               return -EEXIST;
-
        shost = libfc_host_alloc(&fcoe_shost_template,
-                                sizeof(struct fcoe_softc));
+                                sizeof(struct fcoe_port));
        if (!shost) {
                FCOE_NETDEV_DBG(netdev, "Could not allocate host structure\n");
-               return -ENOMEM;
+               rc = -ENOMEM;
+               goto out;
        }
-       lp = shost_priv(shost);
-       fc = lport_priv(lp);
+       lport = shost_priv(shost);
+       port = lport_priv(lport);
+       port->fcoe = fcoe;
 
        /* configure fc_lport, e.g., em */
-       rc = fcoe_lport_config(lp);
+       rc = fcoe_lport_config(lport);
        if (rc) {
                FCOE_NETDEV_DBG(netdev, "Could not configure lport for the "
                                "interface\n");
                goto out_host_put;
        }
 
-       /*
-        * Initialize FIP.
-        */
-       fcoe_ctlr_init(&fc->ctlr);
-       fc->ctlr.send = fcoe_fip_send;
-       fc->ctlr.update_mac = fcoe_update_src_mac;
-
        /* configure lport network properties */
-       rc = fcoe_netdev_config(lp, netdev);
+       rc = fcoe_netdev_config(lport, netdev);
        if (rc) {
                FCOE_NETDEV_DBG(netdev, "Could not configure netdev for the "
                                "interface\n");
-               goto out_netdev_cleanup;
+               goto out_lp_destroy;
        }
 
        /* configure lport scsi host properties */
-       rc = fcoe_shost_config(lp, shost, &netdev->dev);
+       rc = fcoe_shost_config(lport, shost, parent);
        if (rc) {
                FCOE_NETDEV_DBG(netdev, "Could not configure shost for the "
                                "interface\n");
-               goto out_netdev_cleanup;
+               goto out_lp_destroy;
        }
 
-       /* lport exch manager allocation */
-       rc = fcoe_em_config(lp);
+       /* Initialize the library */
+       rc = fcoe_libfc_config(lport, &fcoe_libfc_fcn_templ);
        if (rc) {
-               FCOE_NETDEV_DBG(netdev, "Could not configure the EM for the "
+               FCOE_NETDEV_DBG(netdev, "Could not configure libfc for the "
                                "interface\n");
-               goto out_netdev_cleanup;
+               goto out_lp_destroy;
        }
 
-       /* Initialize the library */
-       rc = fcoe_libfc_config(lp, &fcoe_libfc_fcn_templ);
+       /*
+        * fcoe_em_alloc() and fcoe_hostlist_add() both
+        * need to be atomic under fcoe_hostlist_lock
+        * since fcoe_em_alloc() looks for an existing EM
+        * instance on host list updated by fcoe_hostlist_add().
+        */
+       write_lock(&fcoe_hostlist_lock);
+       /* lport exch manager allocation */
+       rc = fcoe_em_config(lport);
        if (rc) {
-               FCOE_NETDEV_DBG(netdev, "Could not configure libfc for the "
+               FCOE_NETDEV_DBG(netdev, "Could not configure the EM for the "
                                "interface\n");
                goto out_lp_destroy;
        }
 
        /* add to lports list */
-       fcoe_hostlist_add(lp);
-
-       lp->boot_time = jiffies;
-
-       fc_fabric_login(lp);
-
-       if (!fcoe_link_ok(lp))
-               fcoe_ctlr_link_up(&fc->ctlr);
+       fcoe_hostlist_add(lport);
+       write_unlock(&fcoe_hostlist_lock);
 
        dev_hold(netdev);
-
-       return rc;
+       fcoe_interface_get(fcoe);
+       return lport;
 
 out_lp_destroy:
-       fc_exch_mgr_free(lp->emp); /* Free the EM */
-out_netdev_cleanup:
-       fcoe_netdev_cleanup(fc);
+       fc_exch_mgr_free(lport);
 out_host_put:
-       scsi_host_put(lp->host);
-       return rc;
+       scsi_host_put(lport->host);
+out:
+       return ERR_PTR(rc);
 }
 
 /**
@@ -669,6 +813,7 @@ static int __init fcoe_if_init(void)
 int __exit fcoe_if_exit(void)
 {
        fc_release_transport(scsi_transport_fcoe_sw);
+       scsi_transport_fcoe_sw = NULL;
        return 0;
 }
 
@@ -838,14 +983,13 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *dev,
 {
        struct fc_lport *lp;
        struct fcoe_rcv_info *fr;
-       struct fcoe_softc *fc;
+       struct fcoe_interface *fcoe;
        struct fc_frame_header *fh;
        struct fcoe_percpu_s *fps;
-       unsigned short oxid;
-       unsigned int cpu = 0;
+       unsigned int cpu;
 
-       fc = container_of(ptype, struct fcoe_softc, fcoe_packet_type);
-       lp = fc->ctlr.lp;
+       fcoe = container_of(ptype, struct fcoe_interface, fcoe_packet_type);
+       lp = fcoe->ctlr.lp;
        if (unlikely(lp == NULL)) {
                FCOE_NETDEV_DBG(dev, "Cannot find hba structure");
                goto err2;
@@ -876,20 +1020,20 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *dev,
        skb_set_transport_header(skb, sizeof(struct fcoe_hdr));
        fh = (struct fc_frame_header *) skb_transport_header(skb);
 
-       oxid = ntohs(fh->fh_ox_id);
-
        fr = fcoe_dev_from_skb(skb);
        fr->fr_dev = lp;
        fr->ptype = ptype;
 
-#ifdef CONFIG_SMP
        /*
-        * The incoming frame exchange id(oxid) is ANDed with num of online
-        * cpu bits to get cpu and then this cpu is used for selecting
-        * a per cpu kernel thread from fcoe_percpu.
+        * In case the incoming frame's exchange is originated from
+        * the initiator, then received frame's exchange id is ANDed
+        * with fc_cpu_mask bits to get the same cpu on which exchange
+        * was originated, otherwise just use the current cpu.
         */
-       cpu = oxid & (num_online_cpus() - 1);
-#endif
+       if (ntoh24(fh->fh_f_ctl) & FC_FC_EX_CTX)
+               cpu = ntohs(fh->fh_ox_id) & fc_cpu_mask;
+       else
+               cpu = smp_processor_id();
 
        fps = &per_cpu(fcoe_percpu, cpu);
        spin_lock_bh(&fps->fcoe_rx_list.lock);
@@ -996,7 +1140,7 @@ static int fcoe_get_paged_crc_eof(struct sk_buff *skb, int tlen)
  * fcoe_fc_crc() - calculates FC CRC in this fcoe skb
  * @fp: the fc_frame containing data to be checksummed
  *
- * This uses crc32() to calculate the crc for fc frame
+ * This uses crc32() to calculate the crc for port frame
  * Return   : 32 bit crc
  */
 u32 fcoe_fc_crc(struct fc_frame *fp)
@@ -1029,7 +1173,7 @@ u32 fcoe_fc_crc(struct fc_frame *fp)
 
 /**
  * fcoe_xmit() - FCoE frame transmit function
- * @lp:        the associated local port
+ * @lp:        the associated local fcoe
  * @fp: the fc_frame to be transmitted
  *
  * Return   : 0 for success
@@ -1046,13 +1190,13 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
        unsigned int hlen;              /* header length implies the version */
        unsigned int tlen;              /* trailer length */
        unsigned int elen;              /* eth header, may include vlan */
-       struct fcoe_softc *fc;
+       struct fcoe_port *port = lport_priv(lp);
+       struct fcoe_interface *fcoe = port->fcoe;
        u8 sof, eof;
        struct fcoe_hdr *hp;
 
        WARN_ON((fr_len(fp) % sizeof(u32)) != 0);
 
-       fc = lport_priv(lp);
        fh = fc_frame_header_get(fp);
        skb = fp_skb(fp);
        wlen = skb->len / FCOE_WORD_TO_BYTE;
@@ -1063,7 +1207,7 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
        }
 
        if (unlikely(fh->fh_r_ctl == FC_RCTL_ELS_REQ) &&
-           fcoe_ctlr_els_send(&fc->ctlr, skb))
+           fcoe_ctlr_els_send(&fcoe->ctlr, skb))
                return 0;
 
        sof = fr_sof(fp);
@@ -1085,7 +1229,7 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
                crc = fcoe_fc_crc(fp);
        }
 
-       /* copy fc crc and eof to the skb buff */
+       /* copy port crc and eof to the skb buff */
        if (skb_is_nonlinear(skb)) {
                skb_frag_t *frag;
                if (fcoe_get_paged_crc_eof(skb, tlen)) {
@@ -1108,27 +1252,27 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
                cp = NULL;
        }
 
-       /* adjust skb network/transport offsets to match mac/fcoe/fc */
+       /* adjust skb network/transport offsets to match mac/fcoe/port */
        skb_push(skb, elen + hlen);
        skb_reset_mac_header(skb);
        skb_reset_network_header(skb);
        skb->mac_len = elen;
        skb->protocol = htons(ETH_P_FCOE);
-       skb->dev = fc->real_dev;
+       skb->dev = fcoe->netdev;
 
        /* fill up mac and fcoe headers */
        eh = eth_hdr(skb);
        eh->h_proto = htons(ETH_P_FCOE);
-       if (fc->ctlr.map_dest)
+       if (fcoe->ctlr.map_dest)
                fc_fcoe_set_mac(eh->h_dest, fh->fh_d_id);
        else
                /* insert GW address */
-               memcpy(eh->h_dest, fc->ctlr.dest_addr, ETH_ALEN);
+               memcpy(eh->h_dest, fcoe->ctlr.dest_addr, ETH_ALEN);
 
-       if (unlikely(fc->ctlr.flogi_oxid != FC_XID_UNKNOWN))
-               memcpy(eh->h_source, fc->ctlr.ctl_src_addr, ETH_ALEN);
+       if (unlikely(fcoe->ctlr.flogi_oxid != FC_XID_UNKNOWN))
+               memcpy(eh->h_source, fcoe->ctlr.ctl_src_addr, ETH_ALEN);
        else
-               memcpy(eh->h_source, fc->ctlr.data_src_addr, ETH_ALEN);
+               memcpy(eh->h_source, fcoe->ctlr.data_src_addr, ETH_ALEN);
 
        hp = (struct fcoe_hdr *)(eh + 1);
        memset(hp, 0, sizeof(*hp));
@@ -1136,7 +1280,6 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
                FC_FCOE_ENCAPS_VER(hp, FC_FCOE_VER);
        hp->fcoe_sof = sof;
 
-#ifdef NETIF_F_FSO
        /* fcoe lso, mss is in max_payload which is non-zero for FCP data */
        if (lp->seq_offload && fr_max_payload(fp)) {
                skb_shinfo(skb)->gso_type = SKB_GSO_FCOE;
@@ -1145,7 +1288,6 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
                skb_shinfo(skb)->gso_type = 0;
                skb_shinfo(skb)->gso_size = 0;
        }
-#endif
        /* update tx stats: regardless if LLD fails */
        stats = fc_lport_get_stats(lp);
        stats->TxFrames++;
@@ -1153,7 +1295,7 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
 
        /* send down to lld */
        fr_dev(fp) = lp;
-       if (fc->fcoe_pending_queue.qlen)
+       if (port->fcoe_pending_queue.qlen)
                fcoe_check_wait_queue(lp, skb);
        else if (fcoe_start_io(skb))
                fcoe_check_wait_queue(lp, skb);
@@ -1179,7 +1321,7 @@ int fcoe_percpu_receive_thread(void *arg)
        struct fcoe_crc_eof crc_eof;
        struct fc_frame *fp;
        u8 *mac = NULL;
-       struct fcoe_softc *fc;
+       struct fcoe_port *port;
        struct fcoe_hdr *hp;
 
        set_user_nice(current, -20);
@@ -1215,7 +1357,7 @@ int fcoe_percpu_receive_thread(void *arg)
                /*
                 * Save source MAC address before discarding header.
                 */
-               fc = lport_priv(lp);
+               port = lport_priv(lp);
                if (skb_is_nonlinear(skb))
                        skb_linearize(skb);     /* not ideal */
                mac = eth_hdr(skb)->h_source;
@@ -1277,7 +1419,7 @@ int fcoe_percpu_receive_thread(void *arg)
                fh = fc_frame_header_get(fp);
                if (fh->fh_r_ctl == FC_RCTL_DD_SOL_DATA &&
                    fh->fh_type == FC_TYPE_FCP) {
-                       fc_exch_recv(lp, lp->emp, fp);
+                       fc_exch_recv(lp, fp);
                        continue;
                }
                if (fr_flags(fp) & FCPHF_CRC_UNCHECKED) {
@@ -1293,12 +1435,12 @@ int fcoe_percpu_receive_thread(void *arg)
                        }
                        fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED;
                }
-               if (unlikely(fc->ctlr.flogi_oxid != FC_XID_UNKNOWN) &&
-                   fcoe_ctlr_recv_flogi(&fc->ctlr, fp, mac)) {
+               if (unlikely(port->fcoe->ctlr.flogi_oxid != FC_XID_UNKNOWN) &&
+                   fcoe_ctlr_recv_flogi(&port->fcoe->ctlr, fp, mac)) {
                        fc_frame_free(fp);
                        continue;
                }
-               fc_exch_recv(lp, lp->emp, fp);
+               fc_exch_recv(lp, fp);
        }
        return 0;
 }
@@ -1318,46 +1460,46 @@ int fcoe_percpu_receive_thread(void *arg)
  */
 static void fcoe_check_wait_queue(struct fc_lport *lp, struct sk_buff *skb)
 {
-       struct fcoe_softc *fc = lport_priv(lp);
+       struct fcoe_port *port = lport_priv(lp);
        int rc;
 
-       spin_lock_bh(&fc->fcoe_pending_queue.lock);
+       spin_lock_bh(&port->fcoe_pending_queue.lock);
 
        if (skb)
-               __skb_queue_tail(&fc->fcoe_pending_queue, skb);
+               __skb_queue_tail(&port->fcoe_pending_queue, skb);
 
-       if (fc->fcoe_pending_queue_active)
+       if (port->fcoe_pending_queue_active)
                goto out;
-       fc->fcoe_pending_queue_active = 1;
+       port->fcoe_pending_queue_active = 1;
 
-       while (fc->fcoe_pending_queue.qlen) {
+       while (port->fcoe_pending_queue.qlen) {
                /* keep qlen > 0 until fcoe_start_io succeeds */
-               fc->fcoe_pending_queue.qlen++;
-               skb = __skb_dequeue(&fc->fcoe_pending_queue);
+               port->fcoe_pending_queue.qlen++;
+               skb = __skb_dequeue(&port->fcoe_pending_queue);
 
-               spin_unlock_bh(&fc->fcoe_pending_queue.lock);
+               spin_unlock_bh(&port->fcoe_pending_queue.lock);
                rc = fcoe_start_io(skb);
-               spin_lock_bh(&fc->fcoe_pending_queue.lock);
+               spin_lock_bh(&port->fcoe_pending_queue.lock);
 
                if (rc) {
-                       __skb_queue_head(&fc->fcoe_pending_queue, skb);
+                       __skb_queue_head(&port->fcoe_pending_queue, skb);
                        /* undo temporary increment above */
-                       fc->fcoe_pending_queue.qlen--;
+                       port->fcoe_pending_queue.qlen--;
                        break;
                }
                /* undo temporary increment above */
-               fc->fcoe_pending_queue.qlen--;
+               port->fcoe_pending_queue.qlen--;
        }
 
-       if (fc->fcoe_pending_queue.qlen < FCOE_LOW_QUEUE_DEPTH)
+       if (port->fcoe_pending_queue.qlen < FCOE_LOW_QUEUE_DEPTH)
                lp->qfull = 0;
-       if (fc->fcoe_pending_queue.qlen && !timer_pending(&fc->timer))
-               mod_timer(&fc->timer, jiffies + 2);
-       fc->fcoe_pending_queue_active = 0;
+       if (port->fcoe_pending_queue.qlen && !timer_pending(&port->timer))
+               mod_timer(&port->timer, jiffies + 2);
+       port->fcoe_pending_queue_active = 0;
 out:
-       if (fc->fcoe_pending_queue.qlen > FCOE_MAX_QUEUE_DEPTH)
+       if (port->fcoe_pending_queue.qlen > FCOE_MAX_QUEUE_DEPTH)
                lp->qfull = 1;
-       spin_unlock_bh(&fc->fcoe_pending_queue.lock);
+       spin_unlock_bh(&port->fcoe_pending_queue.lock);
        return;
 }
 
@@ -1391,17 +1533,17 @@ static int fcoe_device_notification(struct notifier_block *notifier,
                                    ulong event, void *ptr)
 {
        struct fc_lport *lp = NULL;
-       struct net_device *real_dev = ptr;
-       struct fcoe_softc *fc;
+       struct net_device *netdev = ptr;
+       struct fcoe_interface *fcoe;
        struct fcoe_dev_stats *stats;
        u32 link_possible = 1;
        u32 mfs;
        int rc = NOTIFY_OK;
 
        read_lock(&fcoe_hostlist_lock);
-       list_for_each_entry(fc, &fcoe_hostlist, list) {
-               if (fc->real_dev == real_dev) {
-                       lp = fc->ctlr.lp;
+       list_for_each_entry(fcoe, &fcoe_hostlist, list) {
+               if (fcoe->netdev == netdev) {
+                       lp = fcoe->ctlr.lp;
                        break;
                }
        }
@@ -1420,21 +1562,20 @@ static int fcoe_device_notification(struct notifier_block *notifier,
        case NETDEV_CHANGE:
                break;
        case NETDEV_CHANGEMTU:
-               mfs = fc->real_dev->mtu -
-                       (sizeof(struct fcoe_hdr) +
-                        sizeof(struct fcoe_crc_eof));
+               mfs = netdev->mtu - (sizeof(struct fcoe_hdr) +
+                                    sizeof(struct fcoe_crc_eof));
                if (mfs >= FC_MIN_MAX_FRAME)
                        fc_set_mfs(lp, mfs);
                break;
        case NETDEV_REGISTER:
                break;
        default:
-               FCOE_NETDEV_DBG(real_dev, "Unknown event %ld "
+               FCOE_NETDEV_DBG(netdev, "Unknown event %ld "
                                "from netdev netlink\n", event);
        }
        if (link_possible && !fcoe_link_ok(lp))
-               fcoe_ctlr_link_up(&fc->ctlr);
-       else if (fcoe_ctlr_link_down(&fc->ctlr)) {
+               fcoe_ctlr_link_up(&fcoe->ctlr);
+       else if (fcoe_ctlr_link_down(&fcoe->ctlr)) {
                stats = fc_lport_get_stats(lp);
                stats->LinkFailureCount++;
                fcoe_clean_pending_queue(lp);
@@ -1542,8 +1683,24 @@ static int fcoe_ethdrv_put(const struct net_device *netdev)
  */
 static int fcoe_destroy(const char *buffer, struct kernel_param *kp)
 {
-       int rc;
        struct net_device *netdev;
+       struct fcoe_interface *fcoe;
+       struct fcoe_port *port;
+       struct fc_lport *lport;
+       int rc;
+
+       mutex_lock(&fcoe_config_mutex);
+#ifdef CONFIG_FCOE_MODULE
+       /*
+        * Make sure the module has been initialized, and is not about to be
+        * removed.  Module paramter sysfs files are writable before the
+        * module_init function is called and after module_exit.
+        */
+       if (THIS_MODULE->state != MODULE_STATE_LIVE) {
+               rc = -ENODEV;
+               goto out_nodev;
+       }
+#endif
 
        netdev = fcoe_if_to_netdev(buffer);
        if (!netdev) {
@@ -1551,22 +1708,20 @@ static int fcoe_destroy(const char *buffer, struct kernel_param *kp)
                goto out_nodev;
        }
        /* look for existing lport */
-       if (!fcoe_hostlist_lookup(netdev)) {
+       lport = fcoe_hostlist_lookup(netdev);
+       if (!lport) {
                rc = -ENODEV;
                goto out_putdev;
        }
-       rc = fcoe_if_destroy(netdev);
-       if (rc) {
-               printk(KERN_ERR "fcoe: Failed to destroy interface (%s)\n",
-                      netdev->name);
-               rc = -EIO;
-               goto out_putdev;
-       }
+       port = lport_priv(lport);
+       fcoe = port->fcoe;
+       fcoe_if_destroy(lport);
        fcoe_ethdrv_put(netdev);
        rc = 0;
 out_putdev:
        dev_put(netdev);
 out_nodev:
+       mutex_unlock(&fcoe_config_mutex);
        return rc;
 }
 
@@ -1580,8 +1735,23 @@ out_nodev:
 static int fcoe_create(const char *buffer, struct kernel_param *kp)
 {
        int rc;
+       struct fcoe_interface *fcoe;
+       struct fc_lport *lport;
        struct net_device *netdev;
 
+       mutex_lock(&fcoe_config_mutex);
+#ifdef CONFIG_FCOE_MODULE
+       /*
+        * Make sure the module has been initialized, and is not about to be
+        * removed.  Module paramter sysfs files are writable before the
+        * module_init function is called and after module_exit.
+        */
+       if (THIS_MODULE->state != MODULE_STATE_LIVE) {
+               rc = -ENODEV;
+               goto out_nodev;
+       }
+#endif
+
        netdev = fcoe_if_to_netdev(buffer);
        if (!netdev) {
                rc = -ENODEV;
@@ -1594,27 +1764,50 @@ static int fcoe_create(const char *buffer, struct kernel_param *kp)
        }
        fcoe_ethdrv_get(netdev);
 
-       rc = fcoe_if_create(netdev);
-       if (rc) {
+       fcoe = fcoe_interface_create(netdev);
+       if (!fcoe) {
+               rc = -ENOMEM;
+               goto out_putdev;
+       }
+
+       lport = fcoe_if_create(fcoe, &netdev->dev);
+       if (IS_ERR(lport)) {
                printk(KERN_ERR "fcoe: Failed to create interface (%s)\n",
                       netdev->name);
                fcoe_ethdrv_put(netdev);
                rc = -EIO;
-               goto out_putdev;
+               goto out_free;
        }
+
+       /* Make this the "master" N_Port */
+       fcoe->ctlr.lp = lport;
+
+       /* start FIP Discovery and FLOGI */
+       lport->boot_time = jiffies;
+       fc_fabric_login(lport);
+       if (!fcoe_link_ok(lport))
+               fcoe_ctlr_link_up(&fcoe->ctlr);
+
        rc = 0;
+out_free:
+       /*
+        * Release from init in fcoe_interface_create(), on success lport
+        * should be holding a reference taken in fcoe_if_create().
+        */
+       fcoe_interface_put(fcoe);
 out_putdev:
        dev_put(netdev);
 out_nodev:
+       mutex_unlock(&fcoe_config_mutex);
        return rc;
 }
 
 module_param_call(create, fcoe_create, NULL, NULL, S_IWUSR);
 __MODULE_PARM_TYPE(create, "string");
-MODULE_PARM_DESC(create, "Create fcoe port using net device passed in.");
+MODULE_PARM_DESC(create, "Create fcoe fcoe using net device passed in.");
 module_param_call(destroy, fcoe_destroy, NULL, NULL, S_IWUSR);
 __MODULE_PARM_TYPE(destroy, "string");
-MODULE_PARM_DESC(destroy, "Destroy fcoe port");
+MODULE_PARM_DESC(destroy, "Destroy fcoe fcoe");
 
 /**
  * fcoe_link_ok() - Check if link is ok for the fc_lport
@@ -1632,32 +1825,28 @@ MODULE_PARM_DESC(destroy, "Destroy fcoe port");
  */
 int fcoe_link_ok(struct fc_lport *lp)
 {
-       struct fcoe_softc *fc = lport_priv(lp);
-       struct net_device *dev = fc->real_dev;
+       struct fcoe_port *port = lport_priv(lp);
+       struct net_device *dev = port->fcoe->netdev;
        struct ethtool_cmd ecmd = { ETHTOOL_GSET };
-       int rc = 0;
 
-       if ((dev->flags & IFF_UP) && netif_carrier_ok(dev)) {
-               dev = fc->phys_dev;
-               if (dev->ethtool_ops->get_settings) {
-                       dev->ethtool_ops->get_settings(dev, &ecmd);
-                       lp->link_supported_speeds &=
-                               ~(FC_PORTSPEED_1GBIT | FC_PORTSPEED_10GBIT);
-                       if (ecmd.supported & (SUPPORTED_1000baseT_Half |
-                                             SUPPORTED_1000baseT_Full))
-                               lp->link_supported_speeds |= FC_PORTSPEED_1GBIT;
-                       if (ecmd.supported & SUPPORTED_10000baseT_Full)
-                               lp->link_supported_speeds |=
-                                       FC_PORTSPEED_10GBIT;
-                       if (ecmd.speed == SPEED_1000)
-                               lp->link_speed = FC_PORTSPEED_1GBIT;
-                       if (ecmd.speed == SPEED_10000)
-                               lp->link_speed = FC_PORTSPEED_10GBIT;
-               }
-       } else
-               rc = -1;
+       if ((dev->flags & IFF_UP) && netif_carrier_ok(dev) &&
+           (!dev_ethtool_get_settings(dev, &ecmd))) {
+               lp->link_supported_speeds &=
+                       ~(FC_PORTSPEED_1GBIT | FC_PORTSPEED_10GBIT);
+               if (ecmd.supported & (SUPPORTED_1000baseT_Half |
+                                     SUPPORTED_1000baseT_Full))
+                       lp->link_supported_speeds |= FC_PORTSPEED_1GBIT;
+               if (ecmd.supported & SUPPORTED_10000baseT_Full)
+                       lp->link_supported_speeds |=
+                               FC_PORTSPEED_10GBIT;
+               if (ecmd.speed == SPEED_1000)
+                       lp->link_speed = FC_PORTSPEED_1GBIT;
+               if (ecmd.speed == SPEED_10000)
+                       lp->link_speed = FC_PORTSPEED_10GBIT;
 
-       return rc;
+               return 0;
+       }
+       return -1;
 }
 
 /**
@@ -1699,16 +1888,16 @@ void fcoe_percpu_clean(struct fc_lport *lp)
  */
 void fcoe_clean_pending_queue(struct fc_lport *lp)
 {
-       struct fcoe_softc  *fc = lport_priv(lp);
+       struct fcoe_port  *port = lport_priv(lp);
        struct sk_buff *skb;
 
-       spin_lock_bh(&fc->fcoe_pending_queue.lock);
-       while ((skb = __skb_dequeue(&fc->fcoe_pending_queue)) != NULL) {
-               spin_unlock_bh(&fc->fcoe_pending_queue.lock);
+       spin_lock_bh(&port->fcoe_pending_queue.lock);
+       while ((skb = __skb_dequeue(&port->fcoe_pending_queue)) != NULL) {
+               spin_unlock_bh(&port->fcoe_pending_queue.lock);
                kfree_skb(skb);
-               spin_lock_bh(&fc->fcoe_pending_queue.lock);
+               spin_lock_bh(&port->fcoe_pending_queue.lock);
        }
-       spin_unlock_bh(&fc->fcoe_pending_queue.lock);
+       spin_unlock_bh(&port->fcoe_pending_queue.lock);
 }
 
 /**
@@ -1725,24 +1914,22 @@ int fcoe_reset(struct Scsi_Host *shost)
 }
 
 /**
- * fcoe_hostlist_lookup_softc() - find the corresponding lport by a given device
+ * fcoe_hostlist_lookup_port() - find the corresponding lport by a given device
  * @dev: this is currently ptr to net_device
  *
- * Returns: NULL or the located fcoe_softc
+ * Called with fcoe_hostlist_lock held.
+ *
+ * Returns: NULL or the located fcoe_port
  */
-static struct fcoe_softc *
-fcoe_hostlist_lookup_softc(const struct net_device *dev)
+static struct fcoe_interface *
+fcoe_hostlist_lookup_port(const struct net_device *dev)
 {
-       struct fcoe_softc *fc;
+       struct fcoe_interface *fcoe;
 
-       read_lock(&fcoe_hostlist_lock);
-       list_for_each_entry(fc, &fcoe_hostlist, list) {
-               if (fc->real_dev == dev) {
-                       read_unlock(&fcoe_hostlist_lock);
-                       return fc;
-               }
+       list_for_each_entry(fcoe, &fcoe_hostlist, list) {
+               if (fcoe->netdev == dev)
+                       return fcoe;
        }
-       read_unlock(&fcoe_hostlist_lock);
        return NULL;
 }
 
@@ -1754,29 +1941,33 @@ fcoe_hostlist_lookup_softc(const struct net_device *dev)
  */
 struct fc_lport *fcoe_hostlist_lookup(const struct net_device *netdev)
 {
-       struct fcoe_softc *fc;
+       struct fcoe_interface *fcoe;
 
-       fc = fcoe_hostlist_lookup_softc(netdev);
+       read_lock(&fcoe_hostlist_lock);
+       fcoe = fcoe_hostlist_lookup_port(netdev);
+       read_unlock(&fcoe_hostlist_lock);
 
-       return (fc) ? fc->ctlr.lp : NULL;
+       return (fcoe) ? fcoe->ctlr.lp : NULL;
 }
 
 /**
  * fcoe_hostlist_add() - Add a lport to lports list
  * @lp: ptr to the fc_lport to be added
  *
+ * Called with write fcoe_hostlist_lock held.
+ *
  * Returns: 0 for success
  */
-int fcoe_hostlist_add(const struct fc_lport *lp)
+int fcoe_hostlist_add(const struct fc_lport *lport)
 {
-       struct fcoe_softc *fc;
-
-       fc = fcoe_hostlist_lookup_softc(fcoe_netdev(lp));
-       if (!fc) {
-               fc = lport_priv(lp);
-               write_lock_bh(&fcoe_hostlist_lock);
-               list_add_tail(&fc->list, &fcoe_hostlist);
-               write_unlock_bh(&fcoe_hostlist_lock);
+       struct fcoe_interface *fcoe;
+       struct fcoe_port *port;
+
+       fcoe = fcoe_hostlist_lookup_port(fcoe_netdev(lport));
+       if (!fcoe) {
+               port = lport_priv(lport);
+               fcoe = port->fcoe;
+               list_add_tail(&fcoe->list, &fcoe_hostlist);
        }
        return 0;
 }
@@ -1787,14 +1978,14 @@ int fcoe_hostlist_add(const struct fc_lport *lp)
  *
  * Returns: 0 for success
  */
-int fcoe_hostlist_remove(const struct fc_lport *lp)
+int fcoe_hostlist_remove(const struct fc_lport *lport)
 {
-       struct fcoe_softc *fc;
+       struct fcoe_interface *fcoe;
 
-       fc = fcoe_hostlist_lookup_softc(fcoe_netdev(lp));
-       BUG_ON(!fc);
        write_lock_bh(&fcoe_hostlist_lock);
-       list_del(&fc->list);
+       fcoe = fcoe_hostlist_lookup_port(fcoe_netdev(lport));
+       BUG_ON(!fcoe);
+       list_del(&fcoe->list);
        write_unlock_bh(&fcoe_hostlist_lock);
 
        return 0;
@@ -1811,8 +2002,7 @@ static int __init fcoe_init(void)
        int rc = 0;
        struct fcoe_percpu_s *p;
 
-       INIT_LIST_HEAD(&fcoe_hostlist);
-       rwlock_init(&fcoe_hostlist_lock);
+       mutex_lock(&fcoe_config_mutex);
 
        for_each_possible_cpu(cpu) {
                p = &per_cpu(fcoe_percpu, cpu);
@@ -1830,15 +2020,18 @@ static int __init fcoe_init(void)
        /* Setup link change notification */
        fcoe_dev_setup();
 
-       fcoe_if_init();
+       rc = fcoe_if_init();
+       if (rc)
+               goto out_free;
 
+       mutex_unlock(&fcoe_config_mutex);
        return 0;
 
 out_free:
        for_each_online_cpu(cpu) {
                fcoe_percpu_thread_destroy(cpu);
        }
-
+       mutex_unlock(&fcoe_config_mutex);
        return rc;
 }
 module_init(fcoe_init);
@@ -1851,21 +2044,24 @@ module_init(fcoe_init);
 static void __exit fcoe_exit(void)
 {
        unsigned int cpu;
-       struct fcoe_softc *fc, *tmp;
+       struct fcoe_interface *fcoe, *tmp;
+
+       mutex_lock(&fcoe_config_mutex);
 
        fcoe_dev_cleanup();
 
        /* releases the associated fcoe hosts */
-       list_for_each_entry_safe(fc, tmp, &fcoe_hostlist, list)
-               fcoe_if_destroy(fc->real_dev);
+       list_for_each_entry_safe(fcoe, tmp, &fcoe_hostlist, list)
+               fcoe_if_destroy(fcoe->ctlr.lp);
 
        unregister_hotcpu_notifier(&fcoe_cpu_notifier);
 
-       for_each_online_cpu(cpu) {
+       for_each_online_cpu(cpu)
                fcoe_percpu_thread_destroy(cpu);
-       }
 
        /* detach from scsi transport */
        fcoe_if_exit();
+
+       mutex_unlock(&fcoe_config_mutex);
 }
 module_exit(fcoe_exit);