]> nv-tegra.nvidia Code Review - linux-2.6.git/blobdiff - drivers/scsi/fcoe/fcoe.c
Merge git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi-misc-2.6
[linux-2.6.git] / drivers / scsi / fcoe / fcoe.c
index 4834d3c130d69fb61f78562fd0397dc07dcd10aa..61384ee4049b39364ec9481fffaaf3155efb77d7 100644 (file)
@@ -18,7 +18,6 @@
  */
 
 #include <linux/module.h>
-#include <linux/version.h>
 #include <linux/spinlock.h>
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
@@ -31,6 +30,7 @@
 #include <linux/fs.h>
 #include <linux/sysfs.h>
 #include <linux/ctype.h>
+#include <linux/workqueue.h>
 #include <scsi/scsi_tcq.h>
 #include <scsi/scsicam.h>
 #include <scsi/scsi_transport.h>
@@ -51,13 +51,15 @@ MODULE_DESCRIPTION("FCoE");
 MODULE_LICENSE("GPL v2");
 
 /* Performance tuning parameters for fcoe */
-static unsigned int fcoe_ddp_min;
+static unsigned int fcoe_ddp_min = 4096;
 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);
 
+static struct workqueue_struct *fcoe_wq;
+
 /* fcoe_percpu_clean completion.  Waiter protected by fcoe_create_mutex */
 static DECLARE_COMPLETION(fcoe_flush_completion);
 
@@ -72,7 +74,6 @@ static int fcoe_xmit(struct fc_lport *, struct fc_frame *);
 static int fcoe_rcv(struct sk_buff *, struct net_device *,
                    struct packet_type *, struct net_device *);
 static int fcoe_percpu_receive_thread(void *);
-static void fcoe_clean_pending_queue(struct fc_lport *);
 static void fcoe_percpu_clean(struct fc_lport *);
 static int fcoe_link_speed_update(struct fc_lport *);
 static int fcoe_link_ok(struct fc_lport *);
@@ -80,7 +81,6 @@ static int fcoe_link_ok(struct fc_lport *);
 static struct fc_lport *fcoe_hostlist_lookup(const struct net_device *);
 static int fcoe_hostlist_add(const struct fc_lport *);
 
-static void fcoe_check_wait_queue(struct fc_lport *, struct sk_buff *);
 static int fcoe_device_notification(struct notifier_block *, ulong, void *);
 static void fcoe_dev_setup(void);
 static void fcoe_dev_cleanup(void);
@@ -98,13 +98,15 @@ static void fcoe_destroy_work(struct work_struct *);
 static int fcoe_ddp_setup(struct fc_lport *, u16, struct scatterlist *,
                          unsigned int);
 static int fcoe_ddp_done(struct fc_lport *, u16);
-
+static int fcoe_ddp_target(struct fc_lport *, u16, struct scatterlist *,
+                          unsigned int);
 static int fcoe_cpu_callback(struct notifier_block *, unsigned long, void *);
 
-static int fcoe_create(const char *, struct kernel_param *);
-static int fcoe_destroy(const char *, struct kernel_param *);
-static int fcoe_enable(const char *, struct kernel_param *);
-static int fcoe_disable(const char *, struct kernel_param *);
+static bool fcoe_match(struct net_device *netdev);
+static int fcoe_create(struct net_device *netdev, enum fip_state fip_mode);
+static int fcoe_destroy(struct net_device *netdev);
+static int fcoe_enable(struct net_device *netdev);
+static int fcoe_disable(struct net_device *netdev);
 
 static struct fc_seq *fcoe_elsct_send(struct fc_lport *,
                                      u32 did, struct fc_frame *,
@@ -117,19 +119,6 @@ static void fcoe_recv_frame(struct sk_buff *skb);
 
 static void fcoe_get_lesb(struct fc_lport *, struct fc_els_lesb *);
 
-module_param_call(create, fcoe_create, NULL, NULL, S_IWUSR);
-__MODULE_PARM_TYPE(create, "string");
-MODULE_PARM_DESC(create, " Creates fcoe instance on a ethernet interface");
-module_param_call(destroy, fcoe_destroy, NULL, NULL, S_IWUSR);
-__MODULE_PARM_TYPE(destroy, "string");
-MODULE_PARM_DESC(destroy, " Destroys fcoe instance on a ethernet interface");
-module_param_call(enable, fcoe_enable, NULL, NULL, S_IWUSR);
-__MODULE_PARM_TYPE(enable, "string");
-MODULE_PARM_DESC(enable, " Enables fcoe on a ethernet interface.");
-module_param_call(disable, fcoe_disable, NULL, NULL, S_IWUSR);
-__MODULE_PARM_TYPE(disable, "string");
-MODULE_PARM_DESC(disable, " Disables fcoe on a ethernet interface.");
-
 /* notification function for packets from net device */
 static struct notifier_block fcoe_notifier = {
        .notifier_call = fcoe_device_notification,
@@ -140,8 +129,8 @@ static struct notifier_block fcoe_cpu_notifier = {
        .notifier_call = fcoe_cpu_callback,
 };
 
-static struct scsi_transport_template *fcoe_transport_template;
-static struct scsi_transport_template *fcoe_vport_transport_template;
+static struct scsi_transport_template *fcoe_nport_scsi_transport;
+static struct scsi_transport_template *fcoe_vport_scsi_transport;
 
 static int fcoe_vport_destroy(struct fc_vport *);
 static int fcoe_vport_create(struct fc_vport *, bool disabled);
@@ -153,12 +142,13 @@ static struct libfc_function_template fcoe_libfc_fcn_templ = {
        .frame_send = fcoe_xmit,
        .ddp_setup = fcoe_ddp_setup,
        .ddp_done = fcoe_ddp_done,
+       .ddp_target = fcoe_ddp_target,
        .elsct_send = fcoe_elsct_send,
        .get_lesb = fcoe_get_lesb,
        .lport_set_port_id = fcoe_set_port_id,
 };
 
-struct fc_function_template fcoe_transport_function = {
+struct fc_function_template fcoe_nport_fc_functions = {
        .show_host_node_name = 1,
        .show_host_port_name = 1,
        .show_host_supported_classes = 1,
@@ -198,7 +188,7 @@ struct fc_function_template fcoe_transport_function = {
        .bsg_request = fc_lport_bsg_request,
 };
 
-struct fc_function_template fcoe_vport_transport_function = {
+struct fc_function_template fcoe_vport_fc_functions = {
        .show_host_node_name = 1,
        .show_host_port_name = 1,
        .show_host_supported_classes = 1,
@@ -280,9 +270,7 @@ static int fcoe_interface_setup(struct fcoe_interface *fcoe,
        }
 
        /* 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)) {
+       if (netdev->priv_flags & IFF_BONDING && netdev->flags & IFF_MASTER) {
                FCOE_NETDEV_DBG(netdev, "Bonded interfaces not supported\n");
                return -EOPNOTSUPP;
        }
@@ -291,6 +279,7 @@ static int fcoe_interface_setup(struct fcoe_interface *fcoe,
         * use the first one for SPMA */
        real_dev = (netdev->priv_flags & IFF_802_1Q_VLAN) ?
                vlan_dev_real_dev(netdev) : netdev;
+       fcoe->realdev = real_dev;
        rcu_read_lock();
        for_each_dev_addr(real_dev, ha) {
                if ((ha->type == NETDEV_HW_ADDR_T_SAN) &&
@@ -312,10 +301,14 @@ static int fcoe_interface_setup(struct fcoe_interface *fcoe,
         * for multiple unicast MACs.
         */
        memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN);
-       dev_unicast_add(netdev, flogi_maddr);
+       dev_uc_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);
+               dev_uc_add(netdev, fip->ctl_src_addr);
+       if (fip->mode == FIP_MODE_VN2VN) {
+               dev_mc_add(netdev, FIP_ALL_VN2VN_MACS);
+               dev_mc_add(netdev, FIP_ALL_P2P_MACS);
+       } else
+               dev_mc_add(netdev, FIP_ALL_ENODE_MACS);
 
        /*
         * setup the receive function from ethernet driver
@@ -337,18 +330,28 @@ static int fcoe_interface_setup(struct fcoe_interface *fcoe,
 /**
  * fcoe_interface_create() - Create a FCoE interface on a net device
  * @netdev: The net device to create the FCoE interface on
+ * @fip_mode: The mode to use for FIP
  *
  * Returns: pointer to a struct fcoe_interface or NULL on error
  */
-static struct fcoe_interface *fcoe_interface_create(struct net_device *netdev)
+static struct fcoe_interface *fcoe_interface_create(struct net_device *netdev,
+                                                   enum fip_state fip_mode)
 {
        struct fcoe_interface *fcoe;
        int err;
 
+       if (!try_module_get(THIS_MODULE)) {
+               FCOE_NETDEV_DBG(netdev,
+                               "Could not get a reference to the module\n");
+               fcoe = ERR_PTR(-EBUSY);
+               goto out;
+       }
+
        fcoe = kzalloc(sizeof(*fcoe), GFP_KERNEL);
        if (!fcoe) {
                FCOE_NETDEV_DBG(netdev, "Could not allocate fcoe structure\n");
-               return NULL;
+               fcoe = ERR_PTR(-ENOMEM);
+               goto out_nomod;
        }
 
        dev_hold(netdev);
@@ -357,7 +360,7 @@ static struct fcoe_interface *fcoe_interface_create(struct net_device *netdev)
        /*
         * Initialize FIP.
         */
-       fcoe_ctlr_init(&fcoe->ctlr);
+       fcoe_ctlr_init(&fcoe->ctlr, fip_mode);
        fcoe->ctlr.send = fcoe_fip_send;
        fcoe->ctlr.update_mac = fcoe_update_src_mac;
        fcoe->ctlr.get_src_addr = fcoe_get_src_mac;
@@ -367,12 +370,54 @@ static struct fcoe_interface *fcoe_interface_create(struct net_device *netdev)
                fcoe_ctlr_destroy(&fcoe->ctlr);
                kfree(fcoe);
                dev_put(netdev);
-               return NULL;
+               fcoe = ERR_PTR(err);
+               goto out_nomod;
        }
 
+       goto out;
+
+out_nomod:
+       module_put(THIS_MODULE);
+out:
        return fcoe;
 }
 
+/**
+ * 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;
+       struct net_device *netdev;
+
+       fcoe = container_of(kref, struct fcoe_interface, kref);
+       netdev = fcoe->netdev;
+       /* tear-down the FCoE controller */
+       fcoe_ctlr_destroy(&fcoe->ctlr);
+       kfree(fcoe);
+       dev_put(netdev);
+       module_put(THIS_MODULE);
+}
+
+/**
+ * fcoe_interface_get() - Get a reference to a FCoE interface
+ * @fcoe: The FCoE interface to be held
+ */
+static inline void fcoe_interface_get(struct fcoe_interface *fcoe)
+{
+       kref_get(&fcoe->kref);
+}
+
+/**
+ * fcoe_interface_put() - Put a reference to a FCoE interface
+ * @fcoe: The FCoE interface to be released
+ */
+static inline void fcoe_interface_put(struct fcoe_interface *fcoe)
+{
+       kref_put(&fcoe->kref, fcoe_interface_release);
+}
+
 /**
  * fcoe_interface_cleanup() - Clean up a FCoE interface
  * @fcoe: The FCoE interface to be cleaned up
@@ -386,6 +431,8 @@ void fcoe_interface_cleanup(struct fcoe_interface *fcoe)
        u8 flogi_maddr[ETH_ALEN];
        const struct net_device_ops *ops;
 
+       rtnl_lock();
+
        /*
         * Don't listen for Ethernet packets anymore.
         * synchronize_net() ensures that the packet handlers are not running
@@ -398,10 +445,14 @@ void fcoe_interface_cleanup(struct fcoe_interface *fcoe)
 
        /* Delete secondary MAC addresses */
        memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN);
-       dev_unicast_delete(netdev, flogi_maddr);
+       dev_uc_del(netdev, flogi_maddr);
        if (fip->spma)
-               dev_unicast_delete(netdev, fip->ctl_src_addr);
-       dev_mc_delete(netdev, FIP_ALL_ENODE_MACS, ETH_ALEN, 0);
+               dev_uc_del(netdev, fip->ctl_src_addr);
+       if (fip->mode == FIP_MODE_VN2VN) {
+               dev_mc_del(netdev, FIP_ALL_VN2VN_MACS);
+               dev_mc_del(netdev, FIP_ALL_P2P_MACS);
+       } else
+               dev_mc_del(netdev, FIP_ALL_ENODE_MACS);
 
        /* Tell the LLD we are done w/ FCoE */
        ops = netdev->netdev_ops;
@@ -410,41 +461,11 @@ void fcoe_interface_cleanup(struct fcoe_interface *fcoe)
                        FCOE_NETDEV_DBG(netdev, "Failed to disable FCoE"
                                        " specific feature for LLD.\n");
        }
-}
 
-/**
- * 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;
-       struct net_device *netdev;
-
-       fcoe = container_of(kref, struct fcoe_interface, kref);
-       netdev = fcoe->netdev;
-       /* tear-down the FCoE controller */
-       fcoe_ctlr_destroy(&fcoe->ctlr);
-       kfree(fcoe);
-       dev_put(netdev);
-}
-
-/**
- * fcoe_interface_get() - Get a reference to a FCoE interface
- * @fcoe: The FCoE interface to be held
- */
-static inline void fcoe_interface_get(struct fcoe_interface *fcoe)
-{
-       kref_get(&fcoe->kref);
-}
+       rtnl_unlock();
 
-/**
- * fcoe_interface_put() - Put a reference to a FCoE interface
- * @fcoe: The FCoE interface to be released
- */
-static inline void fcoe_interface_put(struct fcoe_interface *fcoe)
-{
-       kref_put(&fcoe->kref, fcoe_interface_release);
+       /* Release the self-reference taken during fcoe_interface_create() */
+       fcoe_interface_put(fcoe);
 }
 
 /**
@@ -468,6 +489,19 @@ static int fcoe_fip_recv(struct sk_buff *skb, struct net_device *netdev,
        return 0;
 }
 
+/**
+ * fcoe_port_send() - Send an Ethernet-encapsulated FIP/FCoE frame
+ * @port: The FCoE port
+ * @skb: The FIP/FCoE packet to be sent
+ */
+static void fcoe_port_send(struct fcoe_port *port, struct sk_buff *skb)
+{
+       if (port->fcoe_pending_queue.qlen)
+               fcoe_check_wait_queue(port->lport, skb);
+       else if (fcoe_start_io(skb))
+               fcoe_check_wait_queue(port->lport, skb);
+}
+
 /**
  * fcoe_fip_send() - Send an Ethernet-encapsulated FIP frame
  * @fip: The FCoE controller
@@ -476,7 +510,7 @@ static int fcoe_fip_recv(struct sk_buff *skb, struct net_device *netdev,
 static void fcoe_fip_send(struct fcoe_ctlr *fip, struct sk_buff *skb)
 {
        skb->dev = fcoe_from_ctlr(fip)->netdev;
-       dev_queue_xmit(skb);
+       fcoe_port_send(lport_priv(fip->lp), skb);
 }
 
 /**
@@ -490,13 +524,13 @@ static void fcoe_fip_send(struct fcoe_ctlr *fip, struct sk_buff *skb)
 static void fcoe_update_src_mac(struct fc_lport *lport, u8 *addr)
 {
        struct fcoe_port *port = lport_priv(lport);
-       struct fcoe_interface *fcoe = port->fcoe;
+       struct fcoe_interface *fcoe = port->priv;
 
        rtnl_lock();
        if (!is_zero_ether_addr(port->data_src_addr))
-               dev_unicast_delete(fcoe->netdev, port->data_src_addr);
+               dev_uc_del(fcoe->netdev, port->data_src_addr);
        if (!is_zero_ether_addr(addr))
-               dev_unicast_add(fcoe->netdev, addr);
+               dev_uc_add(fcoe->netdev, addr);
        memcpy(port->data_src_addr, addr, ETH_ALEN);
        rtnl_unlock();
 }
@@ -546,31 +580,47 @@ static int fcoe_lport_config(struct fc_lport *lport)
 }
 
 /**
- * fcoe_queue_timer() - The fcoe queue timer
- * @lport: The local port
- *
- * Calls fcoe_check_wait_queue on timeout
+ * fcoe_netdev_features_change - Updates the lport's offload flags based
+ * on the LLD netdev's FCoE feature flags
  */
-static void fcoe_queue_timer(ulong lport)
+static void fcoe_netdev_features_change(struct fc_lport *lport,
+                                       struct net_device *netdev)
 {
-       fcoe_check_wait_queue((struct fc_lport *)lport, NULL);
-}
+       mutex_lock(&lport->lp_mutex);
 
-/**
- * fcoe_get_wwn() - Get the world wide name from LLD if it supports it
- * @netdev: the associated net device
- * @wwn: the output WWN
- * @type: the type of WWN (WWPN or WWNN)
- *
- * Returns: 0 for success
- */
-static int fcoe_get_wwn(struct net_device *netdev, u64 *wwn, int type)
-{
-       const struct net_device_ops *ops = netdev->netdev_ops;
+       if (netdev->features & NETIF_F_SG)
+               lport->sg_supp = 1;
+       else
+               lport->sg_supp = 0;
 
-       if (ops->ndo_fcoe_get_wwn)
-               return ops->ndo_fcoe_get_wwn(netdev, wwn, type);
-       return -EINVAL;
+       if (netdev->features & NETIF_F_FCOE_CRC) {
+               lport->crc_offload = 1;
+               FCOE_NETDEV_DBG(netdev, "Supports FCCRC offload\n");
+       } else {
+               lport->crc_offload = 0;
+       }
+
+       if (netdev->features & NETIF_F_FSO) {
+               lport->seq_offload = 1;
+               lport->lso_max = netdev->gso_max_size;
+               FCOE_NETDEV_DBG(netdev, "Supports LSO for max len 0x%x\n",
+                               lport->lso_max);
+       } else {
+               lport->seq_offload = 0;
+               lport->lso_max = 0;
+       }
+
+       if (netdev->fcoe_ddp_xid) {
+               lport->lro_enabled = 1;
+               lport->lro_xid = netdev->fcoe_ddp_xid;
+               FCOE_NETDEV_DBG(netdev, "Supports LRO for max xid 0x%x\n",
+                               lport->lro_xid);
+       } else {
+               lport->lro_enabled = 0;
+               lport->lro_xid = 0;
+       }
+
+       mutex_unlock(&lport->lp_mutex);
 }
 
 /**
@@ -588,11 +638,10 @@ static int fcoe_netdev_config(struct fc_lport *lport, struct net_device *netdev)
        u64 wwnn, wwpn;
        struct fcoe_interface *fcoe;
        struct fcoe_port *port;
-       int vid = 0;
 
        /* Setup lport private data to point to fcoe softc */
        port = lport_priv(lport);
-       fcoe = port->fcoe;
+       fcoe = port->priv;
 
        /*
         * Determine max frame size based on underlying device and optional
@@ -609,25 +658,8 @@ static int fcoe_netdev_config(struct fc_lport *lport, struct net_device *netdev)
                return -EINVAL;
 
        /* offload features support */
-       if (netdev->features & NETIF_F_SG)
-               lport->sg_supp = 1;
+       fcoe_netdev_features_change(lport, netdev);
 
-       if (netdev->features & NETIF_F_FCOE_CRC) {
-               lport->crc_offload = 1;
-               FCOE_NETDEV_DBG(netdev, "Supports FCCRC offload\n");
-       }
-       if (netdev->features & NETIF_F_FSO) {
-               lport->seq_offload = 1;
-               lport->lso_max = netdev->gso_max_size;
-               FCOE_NETDEV_DBG(netdev, "Supports LSO for max len 0x%x\n",
-                               lport->lso_max);
-       }
-       if (netdev->fcoe_ddp_xid) {
-               lport->lro_enabled = 1;
-               lport->lro_xid = netdev->fcoe_ddp_xid;
-               FCOE_NETDEV_DBG(netdev, "Supports LRO for max xid 0x%x\n",
-                               lport->lro_xid);
-       }
        skb_queue_head_init(&port->fcoe_pending_queue);
        port->fcoe_pending_queue_active = 0;
        setup_timer(&port->timer, fcoe_queue_timer, (unsigned long)lport);
@@ -635,20 +667,12 @@ static int fcoe_netdev_config(struct fc_lport *lport, struct net_device *netdev)
        fcoe_link_speed_update(lport);
 
        if (!lport->vport) {
-               /*
-                * Use NAA 1&2 (FC-FS Rev. 2.0, Sec. 15) to generate WWNN/WWPN:
-                * For WWNN, we use NAA 1 w/ bit 27-16 of word 0 as 0.
-                * For WWPN, we use NAA 2 w/ bit 27-16 of word 0 from VLAN ID
-                */
-               if (netdev->priv_flags & IFF_802_1Q_VLAN)
-                       vid = vlan_dev_vlan_id(netdev);
-
                if (fcoe_get_wwn(netdev, &wwnn, NETDEV_FCOE_WWNN))
                        wwnn = fcoe_wwn_from_mac(fcoe->ctlr.ctl_src_addr, 1, 0);
                fc_set_wwnn(lport, wwnn);
                if (fcoe_get_wwn(netdev, &wwpn, NETDEV_FCOE_WWPN))
                        wwpn = fcoe_wwn_from_mac(fcoe->ctlr.ctl_src_addr,
-                                                2, vid);
+                                                2, 0);
                fc_set_wwpn(lport, wwpn);
        }
 
@@ -675,9 +699,9 @@ static int fcoe_shost_config(struct fc_lport *lport, struct device *dev)
        lport->host->max_cmd_len = FCOE_MAX_CMD_LEN;
 
        if (lport->vport)
-               lport->host->transportt = fcoe_vport_transport_template;
+               lport->host->transportt = fcoe_vport_scsi_transport;
        else
-               lport->host->transportt = fcoe_transport_template;
+               lport->host->transportt = fcoe_nport_scsi_transport;
 
        /* add the new host to the SCSI-ml */
        rc = scsi_add_host(lport->host, dev);
@@ -688,7 +712,7 @@ static int fcoe_shost_config(struct fc_lport *lport, struct device *dev)
        }
 
        if (!lport->vport)
-               fc_host_max_npiv_vports(lport->host) = USHORT_MAX;
+               fc_host_max_npiv_vports(lport->host) = USHRT_MAX;
 
        snprintf(fc_host_symbolic_name(lport->host), FC_SYMBOLIC_NAME_SIZE,
                 "%s v%s over %s", FCOE_NAME, FCOE_VERSION,
@@ -710,12 +734,27 @@ static int fcoe_shost_config(struct fc_lport *lport, struct device *dev)
  * The offload EM that this routine is associated with will handle any
  * packets that are for SCSI read requests.
  *
+ * This has been enhanced to work when FCoE stack is operating in target
+ * mode.
+ *
  * Returns: True for read types I/O, 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);
+       struct fc_frame_header *fh = fc_frame_header_get(fp);
+       struct fcp_cmnd *fcp;
+
+       if (fc_fcp_is_read(fr_fsp(fp)) &&
+           (fr_fsp(fp)->data_len > fcoe_ddp_min))
+               return true;
+       else if (!(ntoh24(fh->fh_f_ctl) & FC_FC_EX_CTX)) {
+               fcp = fc_frame_payload_get(fp, sizeof(*fcp));
+               if (ntohs(fh->fh_rx_id) == FC_XID_UNKNOWN &&
+                   fcp && (ntohl(fcp->fc_dl) > fcoe_ddp_min) &&
+                   (fcp->fc_flags & FCP_CFL_WRDATA))
+                       return true;
+       }
+       return false;
 }
 
 /**
@@ -727,7 +766,7 @@ bool fcoe_oem_match(struct fc_frame *fp)
 static inline int fcoe_em_config(struct fc_lport *lport)
 {
        struct fcoe_port *port = lport_priv(lport);
-       struct fcoe_interface *fcoe = port->fcoe;
+       struct fcoe_interface *fcoe = port->priv;
        struct fcoe_interface *oldfcoe = NULL;
        struct net_device *old_real_dev, *cur_real_dev;
        u16 min_xid = FCOE_MIN_XID;
@@ -801,11 +840,12 @@ skip_oem:
 /**
  * fcoe_if_destroy() - Tear down a SW FCoE instance
  * @lport: The local port to be destroyed
+ *
  */
 static void fcoe_if_destroy(struct fc_lport *lport)
 {
        struct fcoe_port *port = lport_priv(lport);
-       struct fcoe_interface *fcoe = port->fcoe;
+       struct fcoe_interface *fcoe = port->priv;
        struct net_device *netdev = fcoe->netdev;
 
        FCOE_NETDEV_DBG(netdev, "Destroying interface\n");
@@ -815,7 +855,6 @@ static void fcoe_if_destroy(struct fc_lport *lport)
 
        /* Cleanup the fc_lport */
        fc_lport_destroy(lport);
-       fc_fcp_destroy(lport);
 
        /* Stop the transmit retry timer */
        del_timer_sync(&port->timer);
@@ -825,10 +864,10 @@ static void fcoe_if_destroy(struct fc_lport *lport)
 
        rtnl_lock();
        if (!is_zero_ether_addr(port->data_src_addr))
-               dev_unicast_delete(netdev, port->data_src_addr);
+               dev_uc_del(netdev, port->data_src_addr);
        rtnl_unlock();
 
-       /* receives may not be stopped until after this */
+       /* Release reference held in fcoe_if_create() */
        fcoe_interface_put(fcoe);
 
        /* Free queued packets for the per-CPU receive threads */
@@ -838,6 +877,9 @@ static void fcoe_if_destroy(struct fc_lport *lport)
        fc_remove_host(lport->host);
        scsi_remove_host(lport->host);
 
+       /* Destroy lport scsi_priv */
+       fc_fcp_destroy(lport);
+
        /* There are no more rports or I/O, free the EM */
        fc_exch_mgr_free(lport);
 
@@ -870,6 +912,28 @@ static int fcoe_ddp_setup(struct fc_lport *lport, u16 xid,
        return 0;
 }
 
+/**
+ * fcoe_ddp_target() - Call a LLD's ddp_target through the net device
+ * @lport: The local port to setup DDP for
+ * @xid:   The exchange ID for this DDP transfer
+ * @sgl:   The scatterlist describing this transfer
+ * @sgc:   The number of sg items
+ *
+ * Returns: 0 if the DDP context was not configured
+ */
+static int fcoe_ddp_target(struct fc_lport *lport, u16 xid,
+                          struct scatterlist *sgl, unsigned int sgc)
+{
+       struct net_device *netdev = fcoe_netdev(lport);
+
+       if (netdev->netdev_ops->ndo_fcoe_ddp_target)
+               return netdev->netdev_ops->ndo_fcoe_ddp_target(netdev, xid,
+                                                              sgl, sgc);
+
+       return 0;
+}
+
+
 /**
  * fcoe_ddp_done() - Call a LLD's ddp_done through the net device
  * @lport: The local port to complete DDP on
@@ -900,8 +964,9 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe,
                                       struct device *parent, int npiv)
 {
        struct net_device *netdev = fcoe->netdev;
-       struct fc_lport *lport = NULL;
+       struct fc_lport *lport, *n_port;
        struct fcoe_port *port;
+       struct Scsi_Host *shost;
        int rc;
        /*
         * parent is only a vport if npiv is 1,
@@ -911,13 +976,11 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe,
 
        FCOE_NETDEV_DBG(netdev, "Create Interface\n");
 
-       if (!npiv) {
-               lport = libfc_host_alloc(&fcoe_shost_template,
-                                        sizeof(struct fcoe_port));
-       } else  {
-               lport = libfc_vport_create(vport,
-                                          sizeof(struct fcoe_port));
-       }
+       if (!npiv)
+               lport = libfc_host_alloc(&fcoe_shost_template, sizeof(*port));
+       else
+               lport = libfc_vport_create(vport, sizeof(*port));
+
        if (!lport) {
                FCOE_NETDEV_DBG(netdev, "Could not allocate host structure\n");
                rc = -ENOMEM;
@@ -925,7 +988,9 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe,
        }
        port = lport_priv(lport);
        port->lport = lport;
-       port->fcoe = fcoe;
+       port->priv = fcoe;
+       port->max_queue_depth = FCOE_MAX_QUEUE_DEPTH;
+       port->min_queue_depth = FCOE_MIN_QUEUE_DEPTH;
        INIT_WORK(&port->destroy_work, fcoe_destroy_work);
 
        /* configure a fc_lport including the exchange manager */
@@ -961,31 +1026,34 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe,
        }
 
        /* Initialize the library */
-       rc = fcoe_libfc_config(lport, &fcoe_libfc_fcn_templ);
+       rc = fcoe_libfc_config(lport, &fcoe->ctlr, &fcoe_libfc_fcn_templ, 1);
        if (rc) {
                FCOE_NETDEV_DBG(netdev, "Could not configure libfc for the "
                                "interface\n");
                goto out_lp_destroy;
        }
 
-       if (!npiv) {
-               /*
-                * fcoe_em_alloc() and fcoe_hostlist_add() both
-                * need to be atomic with respect to other changes to the
-                * hostlist since fcoe_em_alloc() looks for an existing EM
-                * instance on host list updated by fcoe_hostlist_add().
-                *
-                * This is currently handled through the fcoe_config_mutex
-                * begin held.
-                */
-
+       /*
+        * fcoe_em_alloc() and fcoe_hostlist_add() both
+        * need to be atomic with respect to other changes to the
+        * hostlist since fcoe_em_alloc() looks for an existing EM
+        * instance on host list updated by fcoe_hostlist_add().
+        *
+        * This is currently handled through the fcoe_config_mutex
+        * begin held.
+        */
+       if (!npiv)
                /* lport exch manager allocation */
                rc = fcoe_em_config(lport);
-               if (rc) {
-                       FCOE_NETDEV_DBG(netdev, "Could not configure the EM "
-                                       "for the interface\n");
-                       goto out_lp_destroy;
-               }
+       else {
+               shost = vport_to_shost(vport);
+               n_port = shost_priv(shost);
+               rc = fc_exch_mgr_list_clone(n_port, lport);
+       }
+
+       if (rc) {
+               FCOE_NETDEV_DBG(netdev, "Could not configure the EM\n");
+               goto out_lp_destroy;
        }
 
        fcoe_interface_get(fcoe);
@@ -1009,11 +1077,12 @@ out:
 static int __init fcoe_if_init(void)
 {
        /* attach to scsi transport */
-       fcoe_transport_template = fc_attach_transport(&fcoe_transport_function);
-       fcoe_vport_transport_template =
-               fc_attach_transport(&fcoe_vport_transport_function);
+       fcoe_nport_scsi_transport =
+               fc_attach_transport(&fcoe_nport_fc_functions);
+       fcoe_vport_scsi_transport =
+               fc_attach_transport(&fcoe_vport_fc_functions);
 
-       if (!fcoe_transport_template) {
+       if (!fcoe_nport_scsi_transport) {
                printk(KERN_ERR "fcoe: Failed to attach to the FC transport\n");
                return -ENODEV;
        }
@@ -1030,10 +1099,10 @@ static int __init fcoe_if_init(void)
  */
 int __exit fcoe_if_exit(void)
 {
-       fc_release_transport(fcoe_transport_template);
-       fc_release_transport(fcoe_vport_transport_template);
-       fcoe_transport_template = NULL;
-       fcoe_vport_transport_template = NULL;
+       fc_release_transport(fcoe_nport_scsi_transport);
+       fc_release_transport(fcoe_vport_scsi_transport);
+       fcoe_nport_scsi_transport = NULL;
+       fcoe_vport_scsi_transport = NULL;
        return 0;
 }
 
@@ -1048,8 +1117,9 @@ static void fcoe_percpu_thread_create(unsigned int cpu)
 
        p = &per_cpu(fcoe_percpu, cpu);
 
-       thread = kthread_create(fcoe_percpu_receive_thread,
-                               (void *)p, "fcoethread/%d", cpu);
+       thread = kthread_create_on_node(fcoe_percpu_receive_thread,
+                                       (void *)p, cpu_to_node(cpu),
+                                       "fcoethread/%d", cpu);
 
        if (likely(!IS_ERR(thread))) {
                kthread_bind(thread, cpu);
@@ -1184,6 +1254,26 @@ static int fcoe_cpu_callback(struct notifier_block *nfb,
        return NOTIFY_OK;
 }
 
+/**
+ * fcoe_select_cpu() - Selects CPU to handle post-processing of incoming
+ *                     command.
+ *
+ * This routine selects next CPU based on cpumask to distribute
+ * incoming requests in round robin.
+ *
+ * Returns: int CPU number
+ */
+static inline unsigned int fcoe_select_cpu(void)
+{
+       static unsigned int selected_cpu;
+
+       selected_cpu = cpumask_next(selected_cpu, cpu_online_mask);
+       if (selected_cpu >= nr_cpu_ids)
+               selected_cpu = cpumask_first(cpu_online_mask);
+
+       return selected_cpu;
+}
+
 /**
  * fcoe_rcv() - Receive packets from a net device
  * @skb:    The received packet
@@ -1204,6 +1294,7 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *netdev,
        struct fcoe_interface *fcoe;
        struct fc_frame_header *fh;
        struct fcoe_percpu_s *fps;
+       struct ethhdr *eh;
        unsigned int cpu;
 
        fcoe = container_of(ptype, struct fcoe_interface, fcoe_packet_type);
@@ -1221,9 +1312,12 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *netdev,
                        skb_tail_pointer(skb), skb_end_pointer(skb),
                        skb->csum, skb->dev ? skb->dev->name : "<NULL>");
 
-       /* check for FCOE packet type */
-       if (unlikely(eth_hdr(skb)->h_proto != htons(ETH_P_FCOE))) {
-               FCOE_NETDEV_DBG(netdev, "Wrong FC type frame");
+       eh = eth_hdr(skb);
+
+       if (is_fip_mode(&fcoe->ctlr) &&
+           compare_ether_addr(eh->h_source, fcoe->ctlr.dest_addr)) {
+               FCOE_NETDEV_DBG(netdev, "wrong source mac address:%pM\n",
+                               eh->h_source);
                goto err;
        }
 
@@ -1238,20 +1332,33 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *netdev,
        skb_set_transport_header(skb, sizeof(struct fcoe_hdr));
        fh = (struct fc_frame_header *) skb_transport_header(skb);
 
+       if (ntoh24(&eh->h_dest[3]) != ntoh24(fh->fh_d_id)) {
+               FCOE_NETDEV_DBG(netdev, "FC frame d_id mismatch with MAC:%pM\n",
+                               eh->h_dest);
+               goto err;
+       }
+
        fr = fcoe_dev_from_skb(skb);
        fr->fr_dev = lport;
-       fr->ptype = ptype;
 
        /*
         * 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.
+        * was originated, otherwise select cpu using rx exchange id
+        * or fcoe_select_cpu().
         */
        if (ntoh24(fh->fh_f_ctl) & FC_FC_EX_CTX)
                cpu = ntohs(fh->fh_ox_id) & fc_cpu_mask;
-       else
-               cpu = smp_processor_id();
+       else {
+               if (ntohs(fh->fh_rx_id) == FC_XID_UNKNOWN)
+                       cpu = fcoe_select_cpu();
+               else
+                       cpu = ntohs(fh->fh_rx_id) & fc_cpu_mask;
+       }
+
+       if (cpu >= nr_cpu_ids)
+               goto err;
 
        fps = &per_cpu(fcoe_percpu, cpu);
        spin_lock_bh(&fps->fcoe_rx_list.lock);
@@ -1310,108 +1417,22 @@ err2:
 }
 
 /**
- * fcoe_start_io() - Start FCoE I/O
- * @skb: The packet to be transmitted
- *
- * This routine is called from the net device to start transmitting
- * FCoE packets.
- *
- * Returns: 0 for success
- */
-static inline int fcoe_start_io(struct sk_buff *skb)
-{
-       struct sk_buff *nskb;
-       int rc;
-
-       nskb = skb_clone(skb, GFP_ATOMIC);
-       rc = dev_queue_xmit(nskb);
-       if (rc != 0)
-               return rc;
-       kfree_skb(skb);
-       return 0;
-}
-
-/**
- * fcoe_get_paged_crc_eof() - Allocate a page to be used for the trailer CRC
+ * fcoe_alloc_paged_crc_eof() - Allocate a page to be used for the trailer CRC
  * @skb:  The packet to be transmitted
  * @tlen: The total length of the trailer
  *
- * This routine allocates a page for frame trailers. The page is re-used if
- * there is enough room left on it for the current trailer. If there isn't
- * enough buffer left a new page is allocated for the trailer. Reference to
- * the page from this function as well as the skbs using the page fragments
- * ensure that the page is freed at the appropriate time.
- *
  * Returns: 0 for success
  */
-static int fcoe_get_paged_crc_eof(struct sk_buff *skb, int tlen)
+static int fcoe_alloc_paged_crc_eof(struct sk_buff *skb, int tlen)
 {
        struct fcoe_percpu_s *fps;
-       struct page *page;
+       int rc;
 
        fps = &get_cpu_var(fcoe_percpu);
-       page = fps->crc_eof_page;
-       if (!page) {
-               page = alloc_page(GFP_ATOMIC);
-               if (!page) {
-                       put_cpu_var(fcoe_percpu);
-                       return -ENOMEM;
-               }
-               fps->crc_eof_page = page;
-               fps->crc_eof_offset = 0;
-       }
-
-       get_page(page);
-       skb_fill_page_desc(skb, skb_shinfo(skb)->nr_frags, page,
-                          fps->crc_eof_offset, tlen);
-       skb->len += tlen;
-       skb->data_len += tlen;
-       skb->truesize += tlen;
-       fps->crc_eof_offset += sizeof(struct fcoe_crc_eof);
-
-       if (fps->crc_eof_offset >= PAGE_SIZE) {
-               fps->crc_eof_page = NULL;
-               fps->crc_eof_offset = 0;
-               put_page(page);
-       }
+       rc = fcoe_get_paged_crc_eof(skb, tlen, fps);
        put_cpu_var(fcoe_percpu);
-       return 0;
-}
 
-/**
- * fcoe_fc_crc() - Calculates the CRC for a given frame
- * @fp: The frame to be checksumed
- *
- * This uses crc32() routine to calculate the CRC for a frame
- *
- * Return: The 32 bit CRC value
- */
-u32 fcoe_fc_crc(struct fc_frame *fp)
-{
-       struct sk_buff *skb = fp_skb(fp);
-       struct skb_frag_struct *frag;
-       unsigned char *data;
-       unsigned long off, len, clen;
-       u32 crc;
-       unsigned i;
-
-       crc = crc32(~0, skb->data, skb_headlen(skb));
-
-       for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
-               frag = &skb_shinfo(skb)->frags[i];
-               off = frag->page_offset;
-               len = frag->size;
-               while (len > 0) {
-                       clen = min(len, PAGE_SIZE - (off & ~PAGE_MASK));
-                       data = kmap_atomic(frag->page + (off >> PAGE_SHIFT),
-                                          KM_SKB_DATA_SOFTIRQ);
-                       crc = crc32(crc, data + (off & ~PAGE_MASK), clen);
-                       kunmap_atomic(data, KM_SKB_DATA_SOFTIRQ);
-                       off += clen;
-                       len -= clen;
-               }
-       }
-       return crc;
+       return rc;
 }
 
 /**
@@ -1434,7 +1455,7 @@ int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp)
        unsigned int tlen;              /* trailer length */
        unsigned int elen;              /* eth header, may include vlan */
        struct fcoe_port *port = lport_priv(lport);
-       struct fcoe_interface *fcoe = port->fcoe;
+       struct fcoe_interface *fcoe = port->priv;
        u8 sof, eof;
        struct fcoe_hdr *hp;
 
@@ -1475,12 +1496,12 @@ int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp)
        /* 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)) {
+               if (fcoe_alloc_paged_crc_eof(skb, tlen)) {
                        kfree_skb(skb);
                        return -ENOMEM;
                }
                frag = &skb_shinfo(skb)->frags[skb_shinfo(skb)->nr_frags - 1];
-               cp = kmap_atomic(frag->page, KM_SKB_DATA_SOFTIRQ)
+               cp = kmap_atomic(skb_frag_page(frag), KM_SKB_DATA_SOFTIRQ)
                        + frag->page_offset;
        } else {
                cp = (struct fcoe_crc_eof *)skb_put(skb, tlen);
@@ -1501,16 +1522,20 @@ int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp)
        skb_reset_network_header(skb);
        skb->mac_len = elen;
        skb->protocol = htons(ETH_P_FCOE);
-       skb->dev = fcoe->netdev;
+       if (fcoe->netdev->priv_flags & IFF_802_1Q_VLAN &&
+           fcoe->realdev->features & NETIF_F_HW_VLAN_TX) {
+               skb->vlan_tci = VLAN_TAG_PRESENT |
+                               vlan_dev_vlan_id(fcoe->netdev);
+               skb->dev = fcoe->realdev;
+       } else
+               skb->dev = fcoe->netdev;
 
        /* fill up mac and fcoe headers */
        eh = eth_hdr(skb);
        eh->h_proto = htons(ETH_P_FCOE);
+       memcpy(eh->h_dest, fcoe->ctlr.dest_addr, ETH_ALEN);
        if (fcoe->ctlr.map_dest)
-               fc_fcoe_set_mac(eh->h_dest, fh->fh_d_id);
-       else
-               /* insert GW address */
-               memcpy(eh->h_dest, fcoe->ctlr.dest_addr, ETH_ALEN);
+               memcpy(eh->h_dest + 3, fh->fh_d_id, 3);
 
        if (unlikely(fcoe->ctlr.flogi_oxid != FC_XID_UNKNOWN))
                memcpy(eh->h_source, fcoe->ctlr.ctl_src_addr, ETH_ALEN);
@@ -1539,11 +1564,7 @@ int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp)
 
        /* send down to lld */
        fr_dev(fp) = lport;
-       if (port->fcoe_pending_queue.qlen)
-               fcoe_check_wait_queue(lport, skb);
-       else if (fcoe_start_io(skb))
-               fcoe_check_wait_queue(lport, skb);
-
+       fcoe_port_send(port, skb);
        return 0;
 }
 
@@ -1556,6 +1577,56 @@ static void fcoe_percpu_flush_done(struct sk_buff *skb)
        complete(&fcoe_flush_completion);
 }
 
+/**
+ * fcoe_filter_frames() - filter out bad fcoe frames, i.e. bad CRC
+ * @lport: The local port the frame was received on
+ * @fp:           The received frame
+ *
+ * Return: 0 on passing filtering checks
+ */
+static inline int fcoe_filter_frames(struct fc_lport *lport,
+                                    struct fc_frame *fp)
+{
+       struct fcoe_interface *fcoe;
+       struct fc_frame_header *fh;
+       struct sk_buff *skb = (struct sk_buff *)fp;
+       struct fcoe_dev_stats *stats;
+
+       /*
+        * We only check CRC if no offload is available and if it is
+        * it's solicited data, in which case, the FCP layer would
+        * check it during the copy.
+        */
+       if (lport->crc_offload && skb->ip_summed == CHECKSUM_UNNECESSARY)
+               fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED;
+       else
+               fr_flags(fp) |= FCPHF_CRC_UNCHECKED;
+
+       fh = (struct fc_frame_header *) skb_transport_header(skb);
+       fh = fc_frame_header_get(fp);
+       if (fh->fh_r_ctl == FC_RCTL_DD_SOL_DATA && fh->fh_type == FC_TYPE_FCP)
+               return 0;
+
+       fcoe = ((struct fcoe_port *)lport_priv(lport))->priv;
+       if (is_fip_mode(&fcoe->ctlr) && fc_frame_payload_op(fp) == ELS_LOGO &&
+           ntoh24(fh->fh_s_id) == FC_FID_FLOGI) {
+               FCOE_DBG("fcoe: dropping FCoE lport LOGO in fip mode\n");
+               return -EINVAL;
+       }
+
+       if (!(fr_flags(fp) & FCPHF_CRC_UNCHECKED) ||
+           le32_to_cpu(fr_crc(fp)) == ~crc32(~0, skb->data, skb->len)) {
+               fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED;
+               return 0;
+       }
+
+       stats = per_cpu_ptr(lport->dev_stats, get_cpu());
+       stats->InvalidCRCCount++;
+       if (stats->InvalidCRCCount < 5)
+               printk(KERN_WARNING "fcoe: dropping frame with CRC error\n");
+       return -EINVAL;
+}
+
 /**
  * fcoe_recv_frame() - process a single received frame
  * @skb: frame to process
@@ -1566,7 +1637,6 @@ static void fcoe_recv_frame(struct sk_buff *skb)
        struct fc_lport *lport;
        struct fcoe_rcv_info *fr;
        struct fcoe_dev_stats *stats;
-       struct fc_frame_header *fh;
        struct fcoe_crc_eof crc_eof;
        struct fc_frame *fp;
        struct fcoe_port *port;
@@ -1597,7 +1667,6 @@ static void fcoe_recv_frame(struct sk_buff *skb)
         * was done in fcoe_rcv already.
         */
        hp = (struct fcoe_hdr *) skb_network_header(skb);
-       fh = (struct fc_frame_header *) skb_transport_header(skb);
 
        stats = per_cpu_ptr(lport->dev_stats, get_cpu());
        if (unlikely(FC_FCOE_DECAPS_VER(hp) != FC_FCOE_VER)) {
@@ -1630,35 +1699,11 @@ static void fcoe_recv_frame(struct sk_buff *skb)
        if (pskb_trim(skb, fr_len))
                goto drop;
 
-       /*
-        * We only check CRC if no offload is available and if it is
-        * it's solicited data, in which case, the FCP layer would
-        * check it during the copy.
-        */
-       if (lport->crc_offload &&
-           skb->ip_summed == CHECKSUM_UNNECESSARY)
-               fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED;
-       else
-               fr_flags(fp) |= FCPHF_CRC_UNCHECKED;
-
-       fh = fc_frame_header_get(fp);
-       if ((fh->fh_r_ctl != FC_RCTL_DD_SOL_DATA ||
-           fh->fh_type != FC_TYPE_FCP) &&
-           (fr_flags(fp) & FCPHF_CRC_UNCHECKED)) {
-               if (le32_to_cpu(fr_crc(fp)) !=
-                   ~crc32(~0, skb->data, fr_len)) {
-                       if (stats->InvalidCRCCount < 5)
-                               printk(KERN_WARNING "fcoe: dropping "
-                                      "frame with CRC error\n");
-                       stats->InvalidCRCCount++;
-                       goto drop;
-               }
-               fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED;
+       if (!fcoe_filter_frames(lport, fp)) {
+               put_cpu();
+               fc_exch_recv(lport, fp);
+               return;
        }
-       put_cpu();
-       fc_exch_recv(lport, fp);
-       return;
-
 drop:
        stats->ErrorFrames++;
        put_cpu();
@@ -1696,64 +1741,6 @@ int fcoe_percpu_receive_thread(void *arg)
        return 0;
 }
 
-/**
- * fcoe_check_wait_queue() - Attempt to clear the transmit backlog
- * @lport: The local port whose backlog is to be cleared
- *
- * This empties the wait_queue, dequeues the head of the wait_queue queue
- * and calls fcoe_start_io() for each packet. If all skb have been
- * transmitted it returns the qlen. If an error occurs it restores
- * wait_queue (to try again later) and returns -1.
- *
- * The wait_queue is used when the skb transmit fails. The failed skb
- * will go in the wait_queue which will be emptied by the timer function or
- * by the next skb transmit.
- */
-static void fcoe_check_wait_queue(struct fc_lport *lport, struct sk_buff *skb)
-{
-       struct fcoe_port *port = lport_priv(lport);
-       int rc;
-
-       spin_lock_bh(&port->fcoe_pending_queue.lock);
-
-       if (skb)
-               __skb_queue_tail(&port->fcoe_pending_queue, skb);
-
-       if (port->fcoe_pending_queue_active)
-               goto out;
-       port->fcoe_pending_queue_active = 1;
-
-       while (port->fcoe_pending_queue.qlen) {
-               /* keep qlen > 0 until fcoe_start_io succeeds */
-               port->fcoe_pending_queue.qlen++;
-               skb = __skb_dequeue(&port->fcoe_pending_queue);
-
-               spin_unlock_bh(&port->fcoe_pending_queue.lock);
-               rc = fcoe_start_io(skb);
-               spin_lock_bh(&port->fcoe_pending_queue.lock);
-
-               if (rc) {
-                       __skb_queue_head(&port->fcoe_pending_queue, skb);
-                       /* undo temporary increment above */
-                       port->fcoe_pending_queue.qlen--;
-                       break;
-               }
-               /* undo temporary increment above */
-               port->fcoe_pending_queue.qlen--;
-       }
-
-       if (port->fcoe_pending_queue.qlen < FCOE_LOW_QUEUE_DEPTH)
-               lport->qfull = 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 (port->fcoe_pending_queue.qlen > FCOE_MAX_QUEUE_DEPTH)
-               lport->qfull = 1;
-       spin_unlock_bh(&port->fcoe_pending_queue.lock);
-       return;
-}
-
 /**
  * fcoe_dev_setup() - Setup the link change notification interface
  */
@@ -1824,10 +1811,12 @@ static int fcoe_device_notification(struct notifier_block *notifier,
        case NETDEV_UNREGISTER:
                list_del(&fcoe->list);
                port = lport_priv(fcoe->ctlr.lp);
-               fcoe_interface_cleanup(fcoe);
-               schedule_work(&port->destroy_work);
+               queue_work(fcoe_wq, &port->destroy_work);
                goto out;
                break;
+       case NETDEV_FEAT_CHANGE:
+               fcoe_netdev_features_change(lport, netdev);
+               break;
        default:
                FCOE_NETDEV_DBG(netdev, "Unknown event %ld "
                                "from netdev netlink\n", event);
@@ -1847,178 +1836,90 @@ out:
        return rc;
 }
 
-/**
- * fcoe_if_to_netdev() - Parse a name buffer to get a net device
- * @buffer: The name of the net device
- *
- * Returns: NULL or a ptr to net_device
- */
-static struct net_device *fcoe_if_to_netdev(const char *buffer)
-{
-       char *cp;
-       char ifname[IFNAMSIZ + 2];
-
-       if (buffer) {
-               strlcpy(ifname, buffer, IFNAMSIZ);
-               cp = ifname + strlen(ifname);
-               while (--cp >= ifname && *cp == '\n')
-                       *cp = '\0';
-               return dev_get_by_name(&init_net, ifname);
-       }
-       return NULL;
-}
-
 /**
  * fcoe_disable() - Disables a FCoE interface
- * @buffer: The name of the Ethernet interface to be disabled
- * @kp:            The associated kernel parameter
+ * @netdev  : The net_device object the Ethernet interface to create on
  *
- * Called from sysfs.
+ * Called from fcoe transport.
  *
  * Returns: 0 for success
  */
-static int fcoe_disable(const char *buffer, struct kernel_param *kp)
+static int fcoe_disable(struct net_device *netdev)
 {
        struct fcoe_interface *fcoe;
-       struct net_device *netdev;
        int rc = 0;
 
        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;
-               goto out_nodev;
-       }
 
        rtnl_lock();
        fcoe = fcoe_hostlist_lookup_port(netdev);
        rtnl_unlock();
 
        if (fcoe) {
-               fc_fabric_logoff(fcoe->ctlr.lp);
                fcoe_ctlr_link_down(&fcoe->ctlr);
+               fcoe_clean_pending_queue(fcoe->ctlr.lp);
        } else
                rc = -ENODEV;
 
-       dev_put(netdev);
-out_nodev:
        mutex_unlock(&fcoe_config_mutex);
        return rc;
 }
 
 /**
  * fcoe_enable() - Enables a FCoE interface
- * @buffer: The name of the Ethernet interface to be enabled
- * @kp:     The associated kernel parameter
+ * @netdev  : The net_device object the Ethernet interface to create on
  *
- * Called from sysfs.
+ * Called from fcoe transport.
  *
  * Returns: 0 for success
  */
-static int fcoe_enable(const char *buffer, struct kernel_param *kp)
+static int fcoe_enable(struct net_device *netdev)
 {
        struct fcoe_interface *fcoe;
-       struct net_device *netdev;
        int rc = 0;
 
        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;
-               goto out_nodev;
-       }
-
        rtnl_lock();
        fcoe = fcoe_hostlist_lookup_port(netdev);
        rtnl_unlock();
 
-       if (fcoe) {
-               if (!fcoe_link_ok(fcoe->ctlr.lp))
-                       fcoe_ctlr_link_up(&fcoe->ctlr);
-               rc = fc_fabric_login(fcoe->ctlr.lp);
-       } else
+       if (!fcoe)
                rc = -ENODEV;
+       else if (!fcoe_link_ok(fcoe->ctlr.lp))
+               fcoe_ctlr_link_up(&fcoe->ctlr);
 
-       dev_put(netdev);
-out_nodev:
        mutex_unlock(&fcoe_config_mutex);
        return rc;
 }
 
 /**
  * fcoe_destroy() - Destroy a FCoE interface
- * @buffer: The name of the Ethernet interface to be destroyed
- * @kp:            The associated kernel parameter
+ * @netdev  : The net_device object the Ethernet interface to create on
  *
- * Called from sysfs.
+ * Called from fcoe transport
  *
  * Returns: 0 for success
  */
-static int fcoe_destroy(const char *buffer, struct kernel_param *kp)
+static int fcoe_destroy(struct net_device *netdev)
 {
        struct fcoe_interface *fcoe;
-       struct net_device *netdev;
+       struct fc_lport *lport;
+       struct fcoe_port *port;
        int rc = 0;
 
        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;
-               goto out_nodev;
-       }
-
        rtnl_lock();
        fcoe = fcoe_hostlist_lookup_port(netdev);
        if (!fcoe) {
-               rtnl_unlock();
                rc = -ENODEV;
-               goto out_putdev;
+               goto out_nodev;
        }
+       lport = fcoe->ctlr.lp;
+       port = lport_priv(lport);
        list_del(&fcoe->list);
-       fcoe_interface_cleanup(fcoe);
-       rtnl_unlock();
-       fcoe_if_destroy(fcoe->ctlr.lp);
-       module_put(THIS_MODULE);
-
-out_putdev:
-       dev_put(netdev);
+       queue_work(fcoe_wq, &port->destroy_work);
 out_nodev:
+       rtnl_unlock();
        mutex_unlock(&fcoe_config_mutex);
        return rc;
 }
@@ -2030,64 +1931,67 @@ out_nodev:
 static void fcoe_destroy_work(struct work_struct *work)
 {
        struct fcoe_port *port;
+       struct fcoe_interface *fcoe;
+       int npiv = 0;
 
        port = container_of(work, struct fcoe_port, destroy_work);
        mutex_lock(&fcoe_config_mutex);
+
+       /* set if this is an NPIV port */
+       npiv = port->lport->vport ? 1 : 0;
+
+       fcoe = port->priv;
        fcoe_if_destroy(port->lport);
+
+       /* Do not tear down the fcoe interface for NPIV port */
+       if (!npiv)
+               fcoe_interface_cleanup(fcoe);
+
        mutex_unlock(&fcoe_config_mutex);
 }
 
+/**
+ * fcoe_match() - Check if the FCoE is supported on the given netdevice
+ * @netdev  : The net_device object the Ethernet interface to create on
+ *
+ * Called from fcoe transport.
+ *
+ * Returns: always returns true as this is the default FCoE transport,
+ * i.e., support all netdevs.
+ */
+static bool fcoe_match(struct net_device *netdev)
+{
+       return true;
+}
+
 /**
  * fcoe_create() - Create a fcoe interface
- * @buffer: The name of the Ethernet interface to create on
- * @kp:            The associated kernel param
+ * @netdev  : The net_device object the Ethernet interface to create on
+ * @fip_mode: The FIP mode for this creation
  *
- * Called from sysfs.
+ * Called from fcoe transport
  *
  * Returns: 0 for success
  */
-static int fcoe_create(const char *buffer, struct kernel_param *kp)
+static int fcoe_create(struct net_device *netdev, enum fip_state fip_mode)
 {
-       int rc;
+       int rc = 0;
        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_nomod;
-       }
-#endif
-
-       if (!try_module_get(THIS_MODULE)) {
-               rc = -EINVAL;
-               goto out_nomod;
-       }
-
        rtnl_lock();
-       netdev = fcoe_if_to_netdev(buffer);
-       if (!netdev) {
-               rc = -ENODEV;
-               goto out_nodev;
-       }
 
        /* look for existing lport */
        if (fcoe_hostlist_lookup(netdev)) {
                rc = -EEXIST;
-               goto out_putdev;
+               goto out_nodev;
        }
 
-       fcoe = fcoe_interface_create(netdev);
-       if (!fcoe) {
-               rc = -ENOMEM;
-               goto out_putdev;
+       fcoe = fcoe_interface_create(netdev, fip_mode);
+       if (IS_ERR(fcoe)) {
+               rc = PTR_ERR(fcoe);
+               goto out_nodev;
        }
 
        lport = fcoe_if_create(fcoe, &netdev->dev, 0);
@@ -2095,8 +1999,9 @@ static int fcoe_create(const char *buffer, struct kernel_param *kp)
                printk(KERN_ERR "fcoe: Failed to create interface (%s)\n",
                       netdev->name);
                rc = -EIO;
+               rtnl_unlock();
                fcoe_interface_cleanup(fcoe);
-               goto out_free;
+               goto out_nortnl;
        }
 
        /* Make this the "master" N_Port */
@@ -2111,24 +2016,9 @@ static int fcoe_create(const char *buffer, struct kernel_param *kp)
        if (!fcoe_link_ok(lport))
                fcoe_ctlr_link_up(&fcoe->ctlr);
 
-       /*
-        * Release from init in fcoe_interface_create(), on success lport
-        * should be holding a reference taken in fcoe_if_create().
-        */
-       fcoe_interface_put(fcoe);
-       dev_put(netdev);
-       rtnl_unlock();
-       mutex_unlock(&fcoe_config_mutex);
-
-       return 0;
-out_free:
-       fcoe_interface_put(fcoe);
-out_putdev:
-       dev_put(netdev);
 out_nodev:
        rtnl_unlock();
-       module_put(THIS_MODULE);
-out_nomod:
+out_nortnl:
        mutex_unlock(&fcoe_config_mutex);
        return rc;
 }
@@ -2142,11 +2032,10 @@ out_nomod:
  */
 int fcoe_link_speed_update(struct fc_lport *lport)
 {
-       struct fcoe_port *port = lport_priv(lport);
-       struct net_device *netdev = port->fcoe->netdev;
-       struct ethtool_cmd ecmd = { ETHTOOL_GSET };
+       struct net_device *netdev = fcoe_netdev(lport);
+       struct ethtool_cmd ecmd;
 
-       if (!dev_ethtool_get_settings(netdev, &ecmd)) {
+       if (!__ethtool_get_settings(netdev, &ecmd)) {
                lport->link_supported_speeds &=
                        ~(FC_PORTSPEED_1GBIT | FC_PORTSPEED_10GBIT);
                if (ecmd.supported & (SUPPORTED_1000baseT_Half |
@@ -2155,11 +2044,14 @@ int fcoe_link_speed_update(struct fc_lport *lport)
                if (ecmd.supported & SUPPORTED_10000baseT_Full)
                        lport->link_supported_speeds |=
                                FC_PORTSPEED_10GBIT;
-               if (ecmd.speed == SPEED_1000)
+               switch (ethtool_cmd_speed(&ecmd)) {
+               case SPEED_1000:
                        lport->link_speed = FC_PORTSPEED_1GBIT;
-               if (ecmd.speed == SPEED_10000)
+                       break;
+               case SPEED_10000:
                        lport->link_speed = FC_PORTSPEED_10GBIT;
-
+                       break;
+               }
                return 0;
        }
        return -1;
@@ -2174,8 +2066,7 @@ int fcoe_link_speed_update(struct fc_lport *lport)
  */
 int fcoe_link_ok(struct fc_lport *lport)
 {
-       struct fcoe_port *port = lport_priv(lport);
-       struct net_device *netdev = port->fcoe->netdev;
+       struct net_device *netdev = fcoe_netdev(lport);
 
        if (netif_oper_up(netdev))
                return 0;
@@ -2238,24 +2129,6 @@ void fcoe_percpu_clean(struct fc_lport *lport)
        }
 }
 
-/**
- * fcoe_clean_pending_queue() - Dequeue a skb and free it
- * @lport: The local port to dequeue a skb on
- */
-void fcoe_clean_pending_queue(struct fc_lport *lport)
-{
-       struct fcoe_port  *port = lport_priv(lport);
-       struct sk_buff *skb;
-
-       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(&port->fcoe_pending_queue.lock);
-       }
-       spin_unlock_bh(&port->fcoe_pending_queue.lock);
-}
-
 /**
  * fcoe_reset() - Reset a local port
  * @shost: The SCSI host associated with the local port to be reset
@@ -2265,7 +2138,13 @@ void fcoe_clean_pending_queue(struct fc_lport *lport)
 int fcoe_reset(struct Scsi_Host *shost)
 {
        struct fc_lport *lport = shost_priv(shost);
-       fc_lport_reset(lport);
+       struct fcoe_port *port = lport_priv(lport);
+       struct fcoe_interface *fcoe = port->priv;
+
+       fcoe_ctlr_link_down(&fcoe->ctlr);
+       fcoe_clean_pending_queue(fcoe->ctlr.lp);
+       if (!fcoe_link_ok(fcoe->ctlr.lp))
+               fcoe_ctlr_link_up(&fcoe->ctlr);
        return 0;
 }
 
@@ -2323,12 +2202,24 @@ static int fcoe_hostlist_add(const struct fc_lport *lport)
        fcoe = fcoe_hostlist_lookup_port(fcoe_netdev(lport));
        if (!fcoe) {
                port = lport_priv(lport);
-               fcoe = port->fcoe;
+               fcoe = port->priv;
                list_add_tail(&fcoe->list, &fcoe_hostlist);
        }
        return 0;
 }
 
+
+static struct fcoe_transport fcoe_sw_transport = {
+       .name = {FCOE_TRANSPORT_DEFAULT},
+       .attached = false,
+       .list = LIST_HEAD_INIT(fcoe_sw_transport.list),
+       .match = fcoe_match,
+       .create = fcoe_create,
+       .destroy = fcoe_destroy,
+       .enable = fcoe_enable,
+       .disable = fcoe_disable,
+};
+
 /**
  * fcoe_init() - Initialize fcoe.ko
  *
@@ -2340,6 +2231,18 @@ static int __init fcoe_init(void)
        unsigned int cpu;
        int rc = 0;
 
+       fcoe_wq = alloc_workqueue("fcoe", 0, 0);
+       if (!fcoe_wq)
+               return -ENOMEM;
+
+       /* register as a fcoe transport */
+       rc = fcoe_transport_attach(&fcoe_sw_transport);
+       if (rc) {
+               printk(KERN_ERR "failed to register an fcoe transport, check "
+                       "if libfcoe is loaded\n");
+               return rc;
+       }
+
        mutex_lock(&fcoe_config_mutex);
 
        for_each_possible_cpu(cpu) {
@@ -2370,6 +2273,7 @@ out_free:
                fcoe_percpu_thread_destroy(cpu);
        }
        mutex_unlock(&fcoe_config_mutex);
+       destroy_workqueue(fcoe_wq);
        return rc;
 }
 module_init(fcoe_init);
@@ -2394,8 +2298,7 @@ static void __exit fcoe_exit(void)
        list_for_each_entry_safe(fcoe, tmp, &fcoe_hostlist, list) {
                list_del(&fcoe->list);
                port = lport_priv(fcoe->ctlr.lp);
-               fcoe_interface_cleanup(fcoe);
-               schedule_work(&port->destroy_work);
+               queue_work(fcoe_wq, &port->destroy_work);
        }
        rtnl_unlock();
 
@@ -2406,16 +2309,21 @@ static void __exit fcoe_exit(void)
 
        mutex_unlock(&fcoe_config_mutex);
 
-       /* flush any asyncronous interface destroys,
-        * this should happen after the netdev notifier is unregistered */
-       flush_scheduled_work();
-       /* That will flush out all the N_Ports on the hostlist, but now we
-        * may have NPIV VN_Ports scheduled for destruction */
-       flush_scheduled_work();
+       /*
+        * destroy_work's may be chained but destroy_workqueue()
+        * can take care of them. Just kill the fcoe_wq.
+        */
+       destroy_workqueue(fcoe_wq);
 
-       /* detach from scsi transport
-        * must happen after all destroys are done, therefor after the flush */
+       /*
+        * Detaching from the scsi transport must happen after all
+        * destroys are done on the fcoe_wq. destroy_workqueue will
+        * enusre the fcoe_wq is flushed.
+        */
        fcoe_if_exit();
+
+       /* detach from fcoe transport */
+       fcoe_transport_detach(&fcoe_sw_transport);
 }
 module_exit(fcoe_exit);
 
@@ -2425,7 +2333,7 @@ module_exit(fcoe_exit);
  * @fp: response frame, or error encoded in a pointer (timeout)
  * @arg: pointer the the fcoe_ctlr structure
  *
- * This handles MAC address managment for FCoE, then passes control on to
+ * This handles MAC address management for FCoE, then passes control on to
  * the libfc FLOGI response handler.
  */
 static void fcoe_flogi_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg)
@@ -2457,7 +2365,7 @@ done:
  * @fp: response frame, or error encoded in a pointer (timeout)
  * @arg: pointer the the fcoe_ctlr structure
  *
- * This handles MAC address managment for FCoE, then passes control on to
+ * This handles MAC address management for FCoE, then passes control on to
  * the libfc LOGO response handler.
  */
 static void fcoe_logo_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg)
@@ -2487,13 +2395,15 @@ static struct fc_seq *fcoe_elsct_send(struct fc_lport *lport, u32 did,
                                      void *arg, u32 timeout)
 {
        struct fcoe_port *port = lport_priv(lport);
-       struct fcoe_interface *fcoe = port->fcoe;
+       struct fcoe_interface *fcoe = port->priv;
        struct fcoe_ctlr *fip = &fcoe->ctlr;
        struct fc_frame_header *fh = fc_frame_header_get(fp);
 
        switch (op) {
        case ELS_FLOGI:
        case ELS_FDISC:
+               if (lport->point_to_multipoint)
+                       break;
                return fc_elsct_send(lport, did, fp, op, fcoe_flogi_resp,
                                     fip, timeout);
        case ELS_LOGO:
@@ -2518,12 +2428,25 @@ static int fcoe_vport_create(struct fc_vport *vport, bool disabled)
        struct Scsi_Host *shost = vport_to_shost(vport);
        struct fc_lport *n_port = shost_priv(shost);
        struct fcoe_port *port = lport_priv(n_port);
-       struct fcoe_interface *fcoe = port->fcoe;
+       struct fcoe_interface *fcoe = port->priv;
        struct net_device *netdev = fcoe->netdev;
        struct fc_lport *vn_port;
+       int rc;
+       char buf[32];
+
+       rc = fcoe_validate_vport_create(vport);
+       if (rc) {
+               fcoe_wwn_to_str(vport->port_name, buf, sizeof(buf));
+               printk(KERN_ERR "fcoe: Failed to create vport, "
+                       "WWPN (0x%s) already exists\n",
+                       buf);
+               return rc;
+       }
 
        mutex_lock(&fcoe_config_mutex);
+       rtnl_lock();
        vn_port = fcoe_if_create(fcoe, &vport->dev, 1);
+       rtnl_unlock();
        mutex_unlock(&fcoe_config_mutex);
 
        if (IS_ERR(vn_port)) {
@@ -2558,7 +2481,7 @@ static int fcoe_vport_destroy(struct fc_vport *vport)
        mutex_lock(&n_port->lp_mutex);
        list_del(&vn_port->list);
        mutex_unlock(&n_port->lp_mutex);
-       schedule_work(&port->destroy_work);
+       queue_work(fcoe_wq, &port->destroy_work);
        return 0;
 }
 
@@ -2622,27 +2545,9 @@ static void fcoe_set_vport_symbolic_name(struct fc_vport *vport)
 static void fcoe_get_lesb(struct fc_lport *lport,
                         struct fc_els_lesb *fc_lesb)
 {
-       unsigned int cpu;
-       u32 lfc, vlfc, mdac;
-       struct fcoe_dev_stats *devst;
-       struct fcoe_fc_els_lesb *lesb;
        struct net_device *netdev = fcoe_netdev(lport);
 
-       lfc = 0;
-       vlfc = 0;
-       mdac = 0;
-       lesb = (struct fcoe_fc_els_lesb *)fc_lesb;
-       memset(lesb, 0, sizeof(*lesb));
-       for_each_possible_cpu(cpu) {
-               devst = per_cpu_ptr(lport->dev_stats, cpu);
-               lfc += devst->LinkFailureCount;
-               vlfc += devst->VLinkFailureCount;
-               mdac += devst->MissDiscAdvCount;
-       }
-       lesb->lesb_link_fail = htonl(lfc);
-       lesb->lesb_vlink_fail = htonl(vlfc);
-       lesb->lesb_miss_fka = htonl(mdac);
-       lesb->lesb_fcs_error = htonl(dev_get_stats(netdev)->rx_crc_errors);
+       __fcoe_get_lesb(lport, fc_lesb, netdev);
 }
 
 /**
@@ -2661,7 +2566,7 @@ static void fcoe_set_port_id(struct fc_lport *lport,
                             u32 port_id, struct fc_frame *fp)
 {
        struct fcoe_port *port = lport_priv(lport);
-       struct fcoe_interface *fcoe = port->fcoe;
+       struct fcoe_interface *fcoe = port->priv;
 
        if (fp && fc_frame_payload_op(fp) == ELS_FLOGI)
                fcoe_ctlr_recv_flogi(&fcoe->ctlr, lport, fp);