Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > 89877e42827f16fa5f86b1df0c2860b1 > files > 2122

kernel-2.6.18-128.1.10.el5.src.rpm

From: mchristi@redhat.com <mchristi@redhat.com>
Date: Fri, 5 Dec 2008 16:04:34 -0600
Subject: [scsi] fcoe: update drivers
Message-id: 1228514674-26092-1-git-send-email-mchristi@redhat.com
O-Subject: [PATCH] RHEL 5.3: update fcoe drivers.
Bugzilla: 474089
RH-Acked-by: Rik van Riel <riel@redhat.com>
RH-Acked-by: Tom Coughlan <coughlan@redhat.com>

From: Mike Christie <mchristi@redhat.com>

This is for BZ 474089.

This fixes several bugs we have found during testing. I guess the SCSI
maintainer is also getting ready to merge the libfc and fcoe driver,
and so this patch also syncs us up with what is being set to upstream
which brings in some more bug fixes we wanted. There is also a fix
to the fc class:
http://git.kernel.org/?p=linux/kernel/git/jejb/scsi-rc-fixes-2.6.git;a=commit;h=21098c68df7115554fe041170899bdff709efd08
where the fast fail was supposed to be a bit field.

I know it is late in the development cycle and the patch is large.
The libfc, fnic and fcoe code is new code for 5.3, and the only user and
tester during the beta and snapshots has been Cisco.

The fnic and libfc parts were tested by Cisco. I tested the fcoe and
libfc parts by doing cable pulls whild IO was running. I also ran
some tests to inject task managemnt functions or set the disk command
timeout to 1 second to test those code paths.

diff --git a/drivers/scsi/fcoe/fc_transport_fcoe.c b/drivers/scsi/fcoe/fc_transport_fcoe.c
index c4b94c4..7944c07 100644
--- a/drivers/scsi/fcoe/fc_transport_fcoe.c
+++ b/drivers/scsi/fcoe/fc_transport_fcoe.c
@@ -17,355 +17,313 @@
  * Maintained at www.Open-FCoE.org
  */
 
-#include <linux/module.h>
-#include <linux/version.h>
-#include <linux/kernel.h>
-#include <linux/kthread.h>
-#include <linux/spinlock.h>
-#include <linux/cpu.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/ethtool.h>
-#include <linux/if_ether.h>
-#include <linux/fs.h>
-#include <linux/sysfs.h>
-#include <linux/ctype.h>
-
-#include <scsi/libfc/libfc.h>
-
-#include "fcoe_def.h"
-
-MODULE_AUTHOR("Open-FCoE.org");
-MODULE_DESCRIPTION("FCoE");
-MODULE_LICENSE("GPL");
+#include <linux/pci.h>
+#include <scsi/libfcoe.h>
+#include <scsi/fc_transport_fcoe.h>
 
-/*
- * Static functions and variables definations
- */
-#ifdef CONFIG_HOTPLUG_CPU
-static int fcoe_cpu_callback(struct notifier_block *, ulong, void *);
-#endif /* CONFIG_HOTPLUG_CPU */
-static int fcoe_device_notification(struct notifier_block *, ulong, void *);
-static void fcoe_dev_setup(void);
-static void fcoe_dev_cleanup(void);
-
-#ifdef CONFIG_HOTPLUG_CPU
-static struct notifier_block fcoe_cpu_notifier = {
-	.notifier_call = fcoe_cpu_callback,
+/* internal fcoe transport */
+struct fcoe_transport_internal {
+	struct fcoe_transport *t;
+	struct net_device *netdev;
+	struct list_head list;
 };
-#endif /* CONFIG_HOTPLUG_CPU */
 
-/*
- * notification function from net device
- */
-static struct notifier_block fcoe_notifier = {
-	.notifier_call = fcoe_device_notification,
-};
+/* fcoe transports list and its lock */
+static LIST_HEAD(fcoe_transports);
+static DEFINE_MUTEX(fcoe_transports_lock);
 
-#ifdef CONFIG_HOTPLUG_CPU
-/*
- * create percpu stats block
- * called by cpu add/remove notifier
- */
-static void fcoe_create_percpu_data(int cpu)
+/**
+ * fcoe_transport_default - returns ptr to the default transport fcoe_sw
+ **/
+struct fcoe_transport *fcoe_transport_default(void)
 {
-	struct fc_lport *lp;
-	struct fcoe_softc *fc;
-
-	write_lock_bh(&fcoe_hostlist_lock);
-	list_for_each_entry(fc, &fcoe_hostlist, list) {
-		lp = fc->lp;
-		if (lp->dev_stats[cpu] == NULL)
-			lp->dev_stats[cpu] = kzalloc(sizeof(struct fcoe_dev_stats),
-						     GFP_KERNEL);
-	}
-	write_unlock_bh(&fcoe_hostlist_lock);
+	return &fcoe_sw_transport;
 }
 
-/*
- * destroy percpu stats block
- * called by cpu add/remove notifier
- */
-static void fcoe_destroy_percpu_data(int cpu)
+/**
+ * fcoe_transport_device_lookup - find out netdev is managed by the
+ * transport
+ * assign a transport to a device
+ * @netdev: the netdev the transport to be attached to
+ *
+ * This will look for existing offload driver, if not found, it falls back to
+ * the default sw hba (fcoe_sw) as its fcoe transport.
+ *
+ * Returns: 0 for success
+ **/
+static struct fcoe_transport_internal *fcoe_transport_device_lookup(
+	struct fcoe_transport *t, struct net_device *netdev)
 {
-	struct fc_lport *lp;
-	struct fcoe_softc *fc;
-
-	write_lock_bh(&fcoe_hostlist_lock);
-	list_for_each_entry(fc, &fcoe_hostlist, list) {
-		lp = fc->lp;
-		kfree(lp->dev_stats[cpu]);
-		lp->dev_stats[cpu] = NULL;
+	struct fcoe_transport_internal *ti;
+
+	/* assign the transpor to this device */
+	mutex_lock(&t->devlock);
+	list_for_each_entry(ti, &t->devlist, list) {
+		if (ti->netdev == netdev) {
+			mutex_unlock(&t->devlock);
+			return ti;
+		}
 	}
-	write_unlock_bh(&fcoe_hostlist_lock);
+	mutex_unlock(&t->devlock);
+	return NULL;
 }
-
-/*
- * Get notified when a cpu comes on/off. Be hotplug friendly.
- */
-static int fcoe_cpu_callback(struct notifier_block *nfb, unsigned long action,
-			     void *hcpu)
+/**
+ * fcoe_transport_device_add - assign a transport to a device
+ * @netdev: the netdev the transport to be attached to
+ *
+ * This will look for existing offload driver, if not found, it falls back to
+ * the default sw hba (fcoe_sw) as its fcoe transport.
+ *
+ * Returns: 0 for success
+ **/
+static int fcoe_transport_device_add(struct fcoe_transport *t,
+				     struct net_device *netdev)
 {
-	unsigned int cpu = (unsigned long)hcpu;
-
-	switch (action) {
-	case CPU_ONLINE:
-		fcoe_create_percpu_data(cpu);
-		break;
-	case CPU_DEAD:
-		fcoe_destroy_percpu_data(cpu);
-		break;
-	default:
-		break;
+	struct fcoe_transport_internal *ti;
+
+	ti = fcoe_transport_device_lookup(t, netdev);
+	if (ti) {
+		printk(KERN_DEBUG "fcoe_transport_device_add:"
+		       "device %s is already added to transport %s\n",
+		       netdev->name, t->name);
+		return -EEXIST;
 	}
-	return NOTIFY_OK;
+	/* allocate an internal struct to host the netdev and the list */
+	ti = kzalloc(sizeof(*ti), GFP_KERNEL);
+	if (!ti)
+		return -ENOMEM;
+
+	ti->t = t;
+	ti->netdev = netdev;
+	INIT_LIST_HEAD(&ti->list);
+	dev_hold(ti->netdev);
+
+	mutex_lock(&t->devlock);
+	list_add(&ti->list, &t->devlist);
+	mutex_unlock(&t->devlock);
+
+	printk(KERN_DEBUG "fcoe_transport_device_add:"
+		       "device %s added to transport %s\n",
+		       netdev->name, t->name);
+
+	return 0;
 }
-#endif /* CONFIG_HOTPLUG_CPU */
 
-/*
- * function to setup link change notification interface
- */
-static void fcoe_dev_setup(void)
+/**
+ * fcoe_transport_device_remove - remove a device from its transport
+ * @netdev: the netdev the transport to be attached to
+ *
+ * this removes the device from the transport so the given transport will
+ * not manage this device any more
+ *
+ * Returns: 0 for success
+ **/
+static int fcoe_transport_device_remove(struct fcoe_transport *t,
+					struct net_device *netdev)
 {
-	/*
-	 * here setup a interface specific wd time to
-	 * monitor the link state
-	 */
-	register_netdevice_notifier(&fcoe_notifier);
+	struct fcoe_transport_internal *ti;
+
+	ti = fcoe_transport_device_lookup(t, netdev);
+	if (!ti) {
+		printk(KERN_DEBUG "fcoe_transport_device_remove:"
+		       "device %s is not managed by transport %s\n",
+		       netdev->name, t->name);
+		return -ENODEV;
+	}
+	mutex_lock(&t->devlock);
+	list_del(&ti->list);
+	mutex_unlock(&t->devlock);
+	printk(KERN_DEBUG "fcoe_transport_device_remove:"
+	       "device %s removed from transport %s\n",
+	       netdev->name, t->name);
+	dev_put(ti->netdev);
+	kfree(ti);
+	return 0;
 }
 
-/*
- * function to cleanup link change notification interface
- */
-static void fcoe_dev_cleanup(void)
+/**
+ * fcoe_transport_device_remove_all - remove all from transport devlist
+ *
+ * this removes the device from the transport so the given transport will
+ * not manage this device any more
+ *
+ * Returns: 0 for success
+ **/
+static void fcoe_transport_device_remove_all(struct fcoe_transport *t)
 {
-	unregister_netdevice_notifier(&fcoe_notifier);
+	struct fcoe_transport_internal *ti, *tmp;
+
+	mutex_lock(&t->devlock);
+	list_for_each_entry_safe(ti, tmp, &t->devlist, list) {
+		list_del(&ti->list);
+		kfree(ti);
+	}
+	mutex_unlock(&t->devlock);
 }
 
-/*
- * This function is called by the ethernet driver
- * this is called in case of link change event
- */
-static int fcoe_device_notification(struct notifier_block *notifier,
-				    ulong event, void *ptr)
+/**
+ * fcoe_transport_register - adds a fcoe transport to the fcoe transports list
+ * @t: ptr to the fcoe transport to be added
+ *
+ * Returns: 0 for success
+ **/
+int fcoe_transport_register(struct fcoe_transport *t)
 {
-	struct fc_lport *lp = NULL;
-	struct net_device *real_dev = ptr;
-	struct fcoe_softc *fc;
-	struct fcoe_dev_stats *stats;
-	u16 new_status;
-	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->lp;
-			break;
+	struct fcoe_transport *tt;
+
+	/* TODO - add fcoe_transport specific initialization here */
+	mutex_lock(&fcoe_transports_lock);
+	list_for_each_entry(tt, &fcoe_transports, list) {
+		if (tt == t) {
+			mutex_unlock(&fcoe_transports_lock);
+			return -EEXIST;
 		}
 	}
-	read_unlock(&fcoe_hostlist_lock);
-	if (lp == NULL) {
-		rc = NOTIFY_DONE;
-		goto out;
-	}
+	list_add_tail(&t->list, &fcoe_transports);
+	mutex_unlock(&fcoe_transports_lock);
 
-	new_status = lp->link_status;
-	switch (event) {
-	case NETDEV_DOWN:
-	case NETDEV_GOING_DOWN:
-		new_status &= ~FC_LINK_UP;
-		break;
-	case NETDEV_UP:
-	case NETDEV_CHANGE:
-		new_status &= ~FC_LINK_UP;
-		if (!fcoe_link_ok(lp))
-			new_status |= FC_LINK_UP;
-		break;
-	case NETDEV_CHANGEMTU:
-		mfs = fc->real_dev->mtu -
-			(sizeof(struct fcoe_hdr) +
-			 sizeof(struct fcoe_crc_eof));
-		if (fc->user_mfs && fc->user_mfs < mfs)
-			mfs = fc->user_mfs;
-		if (mfs >= FC_MIN_MAX_FRAME)
-			fc_set_mfs(lp, mfs);
-		new_status &= ~FC_LINK_UP;
-		if (!fcoe_link_ok(lp))
-			new_status |= FC_LINK_UP;
-		break;
-	case NETDEV_REGISTER:
-		break;
-	default:
-		FC_DBG("unknown event %ld call", event);
-	}
-	if (lp->link_status != new_status) {
-		if ((new_status & FC_LINK_UP) == FC_LINK_UP)
-			fc_linkup(lp);
-		else {
-			stats = lp->dev_stats[smp_processor_id()];
-			if (stats)
-				stats->LinkFailureCount++;
-			fc_linkdown(lp);
-			fcoe_clean_pending_queue(lp);
-		}
-	}
-out:
-	return rc;
-}
+	mutex_init(&t->devlock);
+	INIT_LIST_HEAD(&t->devlist);
 
-static void trimstr(char *str, int len)
-{
-	char *cp = str + len;
-	while (--cp >= str && *cp == '\n')
-		*cp = '\0';
+	printk(KERN_DEBUG "fcoe_transport_register:%s\n", t->name);
+
+	return 0;
 }
+EXPORT_SYMBOL_GPL(fcoe_transport_register);
 
-static int fcoe_destroy(const char *buffer, struct kernel_param *kp)
+/**
+ * fcoe_transport_unregister - remove the tranport fro the fcoe transports list
+ * @t: ptr to the fcoe transport to be removed
+ *
+ * Returns: 0 for success
+ **/
+int fcoe_transport_unregister(struct fcoe_transport *t)
 {
-	struct net_device *netdev;
-	char ifname[IFNAMSIZ + 2];
-	int rc = -ENODEV;
-
-	strlcpy(ifname, buffer, IFNAMSIZ);
-	trimstr(ifname, strlen(ifname));
-	netdev = dev_get_by_name(ifname);
-	if (netdev) {
-		rc = fcoe_destroy_interface(netdev);
-		dev_put(netdev);
+	struct fcoe_transport *tt, *tmp;
+
+	mutex_lock(&fcoe_transports_lock);
+	list_for_each_entry_safe(tt, tmp, &fcoe_transports, list) {
+		if (tt == t) {
+			list_del(&t->list);
+			mutex_unlock(&fcoe_transports_lock);
+			fcoe_transport_device_remove_all(t);
+			printk(KERN_DEBUG "fcoe_transport_unregister:%s\n",
+			       t->name);
+			return 0;
+		}
 	}
-	return rc;
+	mutex_unlock(&fcoe_transports_lock);
+	return -ENODEV;
 }
+EXPORT_SYMBOL_GPL(fcoe_transport_unregister);
 
-static int fcoe_create(const char *buffer, struct kernel_param *kp)
+/**
+ * fcoe_transport_attach - load transport to fcoe
+ * @netdev: the netdev the transport to be attached to
+ *
+ * This will look for existing offload driver, if not found, it falls back to
+ * the default sw hba (fcoe_sw) as its fcoe transport.
+ *
+ * Returns: 0 for success
+ **/
+int fcoe_transport_attach(struct net_device *netdev)
 {
-	struct net_device *netdev;
-	char ifname[IFNAMSIZ + 2];
-	int rc = -ENODEV;
-
-	strlcpy(ifname, buffer, IFNAMSIZ);
-	trimstr(ifname, strlen(ifname));
-	netdev = dev_get_by_name(ifname);
-	if (netdev) {
-		rc = fcoe_create_interface(netdev);
-		dev_put(netdev);
+	struct fcoe_transport *t;
+
+	/* find the corresponding transport */
+	t = fcoe_transport_default();
+	if (!t) {
+		printk(KERN_DEBUG "fcoe_transport_attach"
+		       ":no transport for %s:use %s\n",
+		       netdev->name, t->name);
+		return -ENODEV;
 	}
-	return rc;
-}
+	/* add to the transport */
+	if (fcoe_transport_device_add(t, netdev)) {
+		printk(KERN_DEBUG "fcoe_transport_attach"
+		       ":failed to add %s to tramsport %s\n",
+		       netdev->name, t->name);
+		return -EIO;
+	}
+	/* transport create function */
+	if (t->create)
+		t->create(netdev);
 
-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_param_call(destroy, fcoe_destroy, NULL, NULL, S_IWUSR);
-__MODULE_PARM_TYPE(destroy, "string");
-MODULE_PARM_DESC(destroy, "Destroy fcoe port");
+	printk(KERN_DEBUG "fcoe_transport_attach:transport %s for %s\n",
+	       t->name, netdev->name);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(fcoe_transport_attach);
 
-/*
- * Initialization routine
- * 1. Will create fc transport software structure
- * 2. initialize the link list of port information structure
- */
-static int __init fcoe_init(void)
+/**
+ * fcoe_transport_release - unload transport from fcoe
+ * @netdev: the net device on which fcoe is to be released
+ *
+ * Returns: 0 for success
+ **/
+int fcoe_transport_release(struct net_device *netdev)
 {
-	int cpu;
-	struct fcoe_percpu_s *p;
-
-	rwlock_init(&fcoe_hostlist_lock);
-
-#ifdef CONFIG_HOTPLUG_CPU
-	register_cpu_notifier(&fcoe_cpu_notifier);
-#endif /* CONFIG_HOTPLUG_CPU */
-
-	/*
-	 * initialize per CPU interrupt thread
-	 */
-	for_each_online_cpu(cpu) {
-		p = kzalloc(sizeof(struct fcoe_percpu_s), GFP_KERNEL);
-		if (p) {
-			p->thread = kthread_create(fcoe_percpu_receive_thread,
-						   (void *)p,
-						   "fcoethread/%d", cpu);
-
-			/*
-			 * if there is no error then bind the thread to the cpu
-			 * initialize the semaphore and skb queue head
-			 */
-			if (likely(!IS_ERR(p->thread))) {
-				p->cpu = cpu;
-				fcoe_percpu[cpu] = p;
-				skb_queue_head_init(&p->fcoe_rx_list);
-				kthread_bind(p->thread, cpu);
-				wake_up_process(p->thread);
-			} else {
-				fcoe_percpu[cpu] = NULL;
-				kfree(p);
-
-			}
-		}
+	struct fcoe_transport *t;
+
+	/* find the corresponding transport */
+	t = fcoe_transport_default();
+	if (!t) {
+		printk(KERN_DEBUG "fcoe_transport_release:"
+		       "no transport for %s:use %s\n",
+		       netdev->name, t->name);
+		return -ENODEV;
 	}
+	/* remove the device from the transport */
+	if (fcoe_transport_device_remove(t, netdev)) {
+		printk(KERN_DEBUG "fcoe_transport_release:"
+		       "failed to add %s to tramsport %s\n",
+		       netdev->name, t->name);
+		return -EIO;
+	}
+	/* transport destroy function */
+	if (t->destroy)
+		t->destroy(netdev);
 
-	/*
-	 * setup link change notification
-	 */
-	fcoe_dev_setup();
-
-	init_timer(&fcoe_timer);
-	fcoe_timer.data = 0;
-	fcoe_timer.function = fcoe_watchdog;
-	fcoe_timer.expires = (jiffies + (10 * HZ));
-	add_timer(&fcoe_timer);
+	printk(KERN_DEBUG "fcoe_transport_release:"
+	       "device %s dettached from transport %s\n",
+	       netdev->name, t->name);
 
-	if (fcoe_sw_init() != 0) {
-		FC_DBG("fail to attach fc transport");
-		return -1;
-	}
+	return 0;
+}
+EXPORT_SYMBOL_GPL(fcoe_transport_release);
 
+/**
+ * fcoe_transport_init - initializes fcoe transport layer
+ *
+ * This prepares for the fcoe transport layer
+ *
+ * Returns: none
+ **/
+int __init fcoe_transport_init(void)
+{
+	INIT_LIST_HEAD(&fcoe_transports);
+	mutex_init(&fcoe_transports_lock);
 	return 0;
 }
-module_init(fcoe_init);
 
-static void __exit fcoe_exit(void)
+/**
+ * fcoe_transport_exit - cleans up the fcoe transport layer
+ * This cleans up the fcoe transport layer. removing any transport on the list,
+ * note that the transport destroy func is not called here.
+ *
+ * Returns: none
+ **/
+int __exit fcoe_transport_exit(void)
 {
-	u32 idx;
-	struct fcoe_softc *fc, *tmp;
-	struct fcoe_percpu_s *p;
-	struct sk_buff *skb;
-
-	/*
-	 * Stop all call back interfaces
-	 */
-#ifdef CONFIG_HOTPLUG_CPU
-	unregister_cpu_notifier(&fcoe_cpu_notifier);
-#endif /* CONFIG_HOTPLUG_CPU */
-	fcoe_dev_cleanup();
-
-	/*
-	 * stop timer
-	 */
-	del_timer_sync(&fcoe_timer);
-
-	/*
-	 * assuming that at this time there will be no
-	 * ioctl in prograss, therefore we do not need to lock the
-	 * list.
-	 */
-	list_for_each_entry_safe(fc, tmp, &fcoe_hostlist, list)
-		fcoe_destroy_interface(fc->real_dev);
-
-	for (idx = 0; idx < NR_CPUS; idx++) {
-		if (fcoe_percpu[idx]) {
-			kthread_stop(fcoe_percpu[idx]->thread);
-			p = fcoe_percpu[idx];
-			spin_lock_bh(&p->fcoe_rx_list.lock);
-			while ((skb = __skb_dequeue(&p->fcoe_rx_list)) != NULL)
-				kfree_skb(skb);
-			spin_unlock_bh(&p->fcoe_rx_list.lock);
-			if (fcoe_percpu[idx]->crc_eof_page)
-				put_page(fcoe_percpu[idx]->crc_eof_page);
-			kfree(fcoe_percpu[idx]);
-		}
+	struct fcoe_transport *t, *tmp;
+
+	mutex_lock(&fcoe_transports_lock);
+	list_for_each_entry_safe(t, tmp, &fcoe_transports, list) {
+		list_del(&t->list);
+		mutex_unlock(&fcoe_transports_lock);
+		fcoe_transport_device_remove_all(t);
+		mutex_lock(&fcoe_transports_lock);
 	}
-
-	fcoe_sw_exit();
+	mutex_unlock(&fcoe_transports_lock);
+	return 0;
 }
-module_exit(fcoe_exit);
diff --git a/drivers/scsi/fcoe/fcoe_def.h b/drivers/scsi/fcoe/fcoe_def.h
deleted file mode 100644
index b00e14b..0000000
--- a/drivers/scsi/fcoe/fcoe_def.h
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright(c) 2007 - 2008 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,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Maintained at www.Open-FCoE.org
- */
-
-#ifndef _FCOE_DEF_H_
-#define _FCOE_DEF_H_
-
-#include <linux/etherdevice.h>
-#include <linux/if_ether.h>
-
-#include <scsi/libfc/libfc.h>
-
-#include <scsi/fc/fc_fcoe.h>
-
-#define	FCOE_DRIVER_NAME    "fcoe"	/* driver name for ioctls */
-#define	FCOE_DRIVER_VENDOR  "Open-FC.org" /* vendor name for ioctls */
-
-#define FCOE_MIN_FRAME	36
-#define FCOE_WORD_TO_BYTE  4
-
-/*
- * this is the main  common structure across all instance of fcoe driver.
- * There is one to one mapping between hba struct and ethernet nic.
- * list of hbas contains pointer to the hba struct, these structures are
- * stored in this array using there corresponding if_index.
- */
-
-struct fcoe_percpu_s {
-	int		cpu;
-	struct task_struct *thread;
-	struct sk_buff_head fcoe_rx_list;
-	struct page *crc_eof_page;
-	int crc_eof_offset;
-};
-
-extern struct timer_list fcoe_timer;
-extern rwlock_t fcoe_hostlist_lock;
-extern struct list_head fcoe_hostlist;
-extern struct fcoe_percpu_s *fcoe_percpu[];
-
-struct fcoe_softc {
-	struct list_head list;
-	struct fc_lport *lp;
-	struct net_device *real_dev;
-	struct net_device *phys_dev;		/* device with ethtool_ops */
-	struct packet_type  fcoe_packet_type;
-	struct sk_buff_head fcoe_pending_queue;
-	u16 user_mfs;			/* configured max frame size */
-
-	u8 dest_addr[ETH_ALEN];
-	u8 ctl_src_addr[ETH_ALEN];
-	u8 data_src_addr[ETH_ALEN];
-	/*
-	 * fcoe protocol address learning related stuff
-	 */
-	u16 flogi_oxid;
-	u8 flogi_progress;
-	u8 address_mode;
-};
-
-int fcoe_percpu_receive_thread(void *arg);
-
-/*
- * HBA transport ops prototypes
- */
-void fcoe_clean_pending_queue(struct fc_lport *fd);
-void fcoe_watchdog(ulong vp);
-int fcoe_destroy_interface(struct net_device *);
-int fcoe_create_interface(struct net_device *);
-int fcoe_xmit(struct fc_lport *, struct fc_frame *);
-int fcoe_rcv(struct sk_buff *, struct net_device *,
-	     struct packet_type *, struct net_device *);
-int fcoe_link_ok(struct fc_lport *);
-
-int __init fcoe_sw_init(void);
-void __exit fcoe_sw_exit(void);
-#endif /* _FCOE_DEF_H_ */
diff --git a/drivers/scsi/fcoe/fcoe_sw.c b/drivers/scsi/fcoe/fcoe_sw.c
index b442c86..aaf2326 100644
--- a/drivers/scsi/fcoe/fcoe_sw.c
+++ b/drivers/scsi/fcoe/fcoe_sw.c
@@ -17,19 +17,14 @@
  * Maintained at www.Open-FCoE.org
  */
 
-/*
- * FCOE protocol file
- */
-
 #include <linux/module.h>
 #include <linux/version.h>
 #include <linux/kernel.h>
+#include <linux/pci.h>
 #include <linux/init.h>
 #include <linux/spinlock.h>
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
-#include <linux/ethtool.h>
-#include <linux/if_ether.h>
 #include <linux/if_vlan.h>
 
 #include <scsi/fc/fc_els.h>
@@ -40,36 +35,25 @@
 #include <scsi/scsi_transport.h>
 #include <scsi/scsi_transport_fc.h>
 
-#include <scsi/libfc/libfc.h>
-
-#include <scsi/fc/fc_fcoe.h>
-#include "fcoe_def.h"
+#include <scsi/libfc.h>
+#include <scsi/libfcoe.h>
+#include <scsi/fc_transport_fcoe.h>
 
-#define FCOE_VERSION "0.1"
+#define FCOE_SW_VERSION	"0.1"
+#define	FCOE_SW_NAME	"fcoesw"
+#define	FCOE_SW_VENDOR	"Open-FCoE.org"
 
 #define FCOE_MAX_LUN		255
 #define FCOE_MAX_FCP_TARGET	256
 
 #define FCOE_MAX_OUTSTANDING_COMMANDS	1024
 
-#define FCOE_MIN_XID		0x0004
-#define FCOE_MAX_XID		0x07ef
+#define FCOE_MIN_XID		0x0001	/* the min xid supported by fcoe_sw */
+#define FCOE_MAX_XID		0x07ef	/* the max xid supported by fcoe_sw */
 
-LIST_HEAD(fcoe_hostlist);
-DEFINE_RWLOCK(fcoe_hostlist_lock);
-DEFINE_TIMER(fcoe_timer, NULL, 0, 0);
-struct fcoe_percpu_s *fcoe_percpu[NR_CPUS];
+static struct scsi_transport_template *scsi_transport_fcoe_sw;
 
-static struct scsi_transport_template *fcoe_transport_template;
-
-static int fcoe_reset(struct Scsi_Host *shost)
-{
-	struct fc_lport *lport = shost_priv(shost);
-	fc_lport_reset(lport);
-	return 0;
-}
-
-struct fc_function_template fcoe_transport_function = {
+struct fc_function_template fcoe_sw_transport_function = {
 	.show_host_node_name = 1,
 	.show_host_port_name = 1,
 	.show_host_supported_classes = 1,
@@ -102,60 +86,10 @@ struct fc_function_template fcoe_transport_function = {
 	.terminate_rport_io = fc_rport_terminate_io,
 };
 
-static struct fcoe_softc *fcoe_find_fc_lport(const struct net_device *netdev)
-{
-	struct fcoe_softc *fc;
-
-	read_lock(&fcoe_hostlist_lock);
-	list_for_each_entry(fc, &fcoe_hostlist, list) {
-		if (fc->real_dev == netdev) {
-			read_unlock(&fcoe_hostlist_lock);
-			return fc;
-		}
-	}
-	read_unlock(&fcoe_hostlist_lock);
-	return NULL;
-}
-
-/*
- * Convert 48-bit IEEE MAC address to 64-bit FC WWN.
- */
-static u64 fcoe_wwn_from_mac(unsigned char mac[MAX_ADDR_LEN],
-			     unsigned int scheme, unsigned int port)
-{
-	u64 wwn;
-	u64 host_mac;
-
-	/* The MAC is in NO, so flip only the low 48 bits */
-	host_mac = ((u64) mac[0] << 40) |
-		((u64) mac[1] << 32) |
-		((u64) mac[2] << 24) |
-		((u64) mac[3] << 16) |
-		((u64) mac[4] << 8) |
-		(u64) mac[5];
-
-	WARN_ON(host_mac >= (1ULL << 48));
-	wwn = host_mac | ((u64) scheme << 60);
-	switch (scheme) {
-	case 1:
-		WARN_ON(port != 0);
-		break;
-	case 2:
-		WARN_ON(port >= 0xfff);
-		wwn |= (u64) port << 48;
-		break;
-	default:
-		WARN_ON(1);
-		break;
-	}
-
-	return wwn;
-}
-
-static struct scsi_host_template fcoe_driver_template = {
+static struct scsi_host_template fcoe_sw_shost_template = {
 	.module = THIS_MODULE,
 	.name = "FCoE Driver",
-	.proc_name = FCOE_DRIVER_NAME,
+	.proc_name = FCOE_SW_NAME,
 	.queuecommand = fc_queuecommand,
 	.eh_abort_handler = fc_eh_abort,
 	.eh_device_reset_handler = fc_eh_device_reset,
@@ -171,133 +105,18 @@ static struct scsi_host_template fcoe_driver_template = {
 	.max_sectors = 0xffff,
 };
 
-int fcoe_destroy_interface(struct net_device *netdev)
-{
-	int cpu, idx;
-	struct fcoe_percpu_s *pp;
-	struct fcoe_softc *fc;
-	struct fcoe_rcv_info *fr;
-	struct sk_buff_head *list;
-	struct sk_buff *skb, *next;
-	struct sk_buff *head;
-	struct fc_lport *lp;
-
-	fc = fcoe_find_fc_lport(netdev);
-	if (!fc)
-		return -ENODEV;
-
-	lp = fc->lp;
-
-	/* Remove the instance from fcoe's list */
-	write_lock_bh(&fcoe_hostlist_lock);
-	list_del(&fc->list);
-	write_unlock_bh(&fcoe_hostlist_lock);
-
-	/* Don't listen for Ethernet packets anymore */
-	dev_remove_pack(&fc->fcoe_packet_type);
-
-	/* Detach from the scsi-ml */
-	fc_remove_host(lp->host);
-	scsi_remove_host(lp->host);
-
-	/* Cleanup the fc_lport */
-	fc_lport_destroy(lp);
-	fc_fcp_destroy(lp);
-	if (lp->emp)
-		fc_exch_mgr_free(lp->emp);
-
-	/* Delete secondary MAC addresses */
-	/* turn off promscuity */
-	dev_set_promiscuity(fc->real_dev, -1);
-
-	/* Free the per-CPU revieve threads */
-	for (idx = 0; idx < NR_CPUS; idx++) {
-		if (fcoe_percpu[idx]) {
-			pp = fcoe_percpu[idx];
-			spin_lock_bh(&pp->fcoe_rx_list.lock);
-			list = &pp->fcoe_rx_list;
-			head = list->next;
-			for (skb = head; skb != (struct sk_buff *)list;
-			     skb = next) {
-				next = skb->next;
-				fr = fcoe_dev_from_skb(skb);
-				if (fr->fr_dev == fc->lp) {
-					__skb_unlink(skb, list);
-					kfree_skb(skb);
-				}
-			}
-			spin_unlock_bh(&pp->fcoe_rx_list.lock);
-		}
-	}
-
-	/* Free existing skbs */
-	fcoe_clean_pending_queue(lp);
-
-	/* Free memory used by statistical counters */
-	for_each_online_cpu(cpu)
-		kfree(lp->dev_stats[cpu]);
-
-	/* Release the net_device and Scsi_Host */
-	dev_put(fc->real_dev);
-	scsi_host_put(lp->host);
-	return 0;
-}
-
 /*
- * Return zero if link is OK for use by FCoE.
- * Any permanently-disqualifying conditions have been previously checked.
- * This also updates the speed setting, which may change with link for 100/1000.
+ * fcoe_sw_lport_config - sets up the fc_lport
+ * @lp: ptr to the fc_lport
+ * @shost: ptr to the parent scsi host
+ *
+ * Returns: 0 for success
  *
- * This function should probably be checking for PAUSE support at some point
- * in the future. Currently Per-priority-pause is not determinable using
- * ethtool, so we shouldn't be restrictive until that problem is resolved.
  */
-int fcoe_link_ok(struct fc_lport *lp)
-{
-	struct fcoe_softc *fc = (struct fcoe_softc *)lp->drv_priv;
-	struct net_device *dev = fc->real_dev;
-	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;
-
-	return rc;
-}
-
-static struct libfc_function_template fcoe_libfc_fcn_templ = {
-	.frame_send = fcoe_xmit,
-};
-
-static int lport_config(struct fc_lport *lp, struct Scsi_Host *shost)
+static int fcoe_sw_lport_config(struct fc_lport *lp)
 {
 	int i = 0;
 
-	lp->host = shost;
-	lp->drv_priv = (void *)(lp + 1);
-
-	lp->emp = fc_exch_mgr_alloc(lp, FC_CLASS_3,
-				    FCOE_MIN_XID, FCOE_MAX_XID);
-	if (!lp->emp)
-		return -ENOMEM;
-
 	lp->link_status = 0;
 	lp->max_retry_count = 3;
 	lp->e_d_tov = 2 * 1000;	/* FC-FS default */
@@ -312,24 +131,38 @@ static int lport_config(struct fc_lport *lp, struct Scsi_Host *shost)
 		lp->dev_stats[i] = kzalloc(sizeof(struct fcoe_dev_stats),
 					   GFP_KERNEL);
 
-	/* Finish fc_lport configuration */
+	/* lport fc_lport related configuration */
 	fc_lport_config(lp);
 
 	return 0;
 }
 
-static int net_config(struct fc_lport *lp)
+/*
+ * fcoe_sw_netdev_config - sets up fcoe_softc for lport and network
+ * related properties
+ * @lp : ptr to the fc_lport
+ * @netdev : ptr to the associated netdevice struct
+ *
+ * Must be called after fcoe_sw_lport_config() as it will use lport mutex
+ *
+ * Returns : 0 for success
+ *
+ */
+static int fcoe_sw_netdev_config(struct fc_lport *lp, struct net_device *netdev)
 {
 	u32 mfs;
 	u64 wwnn, wwpn;
-	struct net_device *net_dev;
-	struct fcoe_softc *fc = (struct fcoe_softc *)lp->drv_priv;
+	struct fcoe_softc *fc;
+
+	/* Setup lport private data to point to fcoe softc */
+	fc = lport_priv(lp);
+	fc->lp = lp;
+	fc->real_dev = netdev;
+	fc->phys_dev = netdev;
 
 	/* Require support for get_pauseparam ethtool op. */
-	net_dev = fc->real_dev;
-	if (net_dev->priv_flags & IFF_802_1Q_VLAN)
-		net_dev = VLAN_DEV_INFO(net_dev)->real_dev;
-	fc->phys_dev = net_dev;
+	if (netdev->priv_flags & IFF_802_1Q_VLAN)
+		fc->phys_dev = VLAN_DEV_INFO(netdev)->real_dev;
 
 	/* Do not support for bonding device */
 	if ((fc->real_dev->priv_flags & IFF_MASTER_ALB) ||
@@ -351,6 +184,7 @@ static int net_config(struct fc_lport *lp)
 	if (!fcoe_link_ok(lp))
 		lp->link_status |= FC_LINK_UP;
 
+	/* offload features support */
 	if (fc->real_dev->features & NETIF_F_SG)
 		lp->sg_supp = 1;
 
@@ -381,96 +215,202 @@ static int net_config(struct fc_lport *lp)
 	return 0;
 }
 
-static void shost_config(struct fc_lport *lp)
+/*
+ * fcoe_sw_shost_config - sets up fc_lport->host
+ * @lp : ptr to the fc_lport
+ * @shost : ptr to the associated scsi host
+ *
+ * Must be called after fcoe_sw_lport_config) and fcoe_sw_netdev_config()
+ *
+ * Returns : 0 for success
+ *
+ */
+static int fcoe_sw_shost_config(struct fc_lport *lp, struct Scsi_Host *shost)
 {
+	int rc = 0;
+
+	/* lport scsi host config */
+	lp->host = shost;
+
 	lp->host->max_lun = FCOE_MAX_LUN;
 	lp->host->max_id = FCOE_MAX_FCP_TARGET;
 	lp->host->max_channel = 0;
-	lp->host->transportt = fcoe_transport_template;
+	lp->host->transportt = scsi_transport_fcoe_sw;
+
+	/* add the new host to the SCSI-ml */
+	rc = scsi_add_host(lp->host, NULL);
+	if (rc) {
+		FC_DBG("fcoe_sw_shost_config:error on scsi_add_host\n");
+		return rc;
+	}
+	sprintf(fc_host_symbolic_name(lp->host), "%s v%s over %s",
+		FCOE_SW_NAME, FCOE_SW_VERSION,
+		fcoe_netdev(lp)->name);
+
+	return 0;
 }
 
-static int libfc_config(struct fc_lport *lp)
+/*
+ * fcoe_sw_em_config - allocates em for this lport
+ * @lp: the port that em is to allocated for
+ *
+ * Returns : 0 on success
+ */
+static inline int fcoe_sw_em_config(struct fc_lport *lp)
 {
-	/* Set the function pointers set by the LLDD */
-	memcpy(&lp->tt, &fcoe_libfc_fcn_templ,
-	       sizeof(struct libfc_function_template));
+	BUG_ON(lp->emp);
 
-	if (fc_fcp_init(lp))
+	lp->emp = fc_exch_mgr_alloc(lp, FC_CLASS_3,
+				    FCOE_MIN_XID, FCOE_MAX_XID);
+	if (!lp->emp)
 		return -ENOMEM;
-	fc_exch_init(lp);
-	fc_lport_init(lp);
-	fc_rport_init(lp);
-	fc_disc_init(lp);
 
 	return 0;
 }
 
 /*
- * This function creates the fcoe interface
- * create struct fcdev which is a shared structure between opefc
- * and transport level protocol.
+ * fcoe_sw_destroy - FCoE software HBA tear-down function
+ * @netdev: ptr to the associated net_device
+ *
+ * Returns: 0 if link is OK for use by FCoE.
+ */
+static int fcoe_sw_destroy(struct net_device *netdev)
+{
+	int cpu;
+	struct fc_lport *lp = NULL;
+	struct fcoe_softc *fc;
+
+	BUG_ON(!netdev);
+
+	printk(KERN_DEBUG "fcoe_sw_destroy:interface on %s\n",
+	       netdev->name);
+
+	lp = fcoe_hostlist_lookup(netdev);
+	if (!lp)
+		return -ENODEV;
+
+	fc = fcoe_softc(lp);
+
+	/* Remove the instance from fcoe's list */
+	fcoe_hostlist_remove(lp);
+
+	/* Don't listen for Ethernet packets anymore */
+	dev_remove_pack(&fc->fcoe_packet_type);
+
+	/* Cleanup the fc_lport */
+	fc_lport_destroy(lp);
+	fc_fcp_destroy(lp);
+
+	/* Detach from the scsi-ml */
+	fc_remove_host(lp->host);
+	scsi_remove_host(lp->host);
+
+	/* There are no more rports or I/O, free the EM */
+	if (lp->emp)
+		fc_exch_mgr_free(lp->emp);
+
+	/* turn off promscuity */
+	dev_set_promiscuity(fc->real_dev, -1);
+
+	/* Free the per-CPU revieve threads */
+	fcoe_percpu_clean(lp);
+
+	/* Free existing skbs */
+	fcoe_clean_pending_queue(lp);
+
+	/* Free memory used by statistical counters */
+	for_each_online_cpu(cpu)
+		kfree(lp->dev_stats[cpu]);
+
+	/* Release the net_device and Scsi_Host */
+	dev_put(fc->real_dev);
+	scsi_host_put(lp->host);
+
+	return 0;
+}
+
+static struct libfc_function_template fcoe_sw_libfc_fcn_templ = {
+	.frame_send = fcoe_xmit,
+};
+
+/*
+ * fcoe_sw_create - this function creates the fcoe interface
+ * @netdev: pointer the associated netdevice
+ *
+ * Creates fc_lport struct and scsi_host for lport, configures lport
+ * and starts fabric login.
+ *
+ * Returns : 0 on success
  */
-int fcoe_create_interface(struct net_device *netdev)
+static int fcoe_sw_create(struct net_device *netdev)
 {
+	int rc;
 	struct fc_lport *lp = NULL;
 	struct fcoe_softc *fc;
 	struct Scsi_Host *shost;
-	int rc = 0;
 
-	if (fcoe_find_fc_lport(netdev) != NULL)
+	BUG_ON(!netdev);
+
+	printk(KERN_DEBUG "fcoe_sw_create:interface on %s\n",
+	       netdev->name);
+
+	lp = fcoe_hostlist_lookup(netdev);
+	if (lp)
 		return -EEXIST;
 
-	shost = scsi_host_alloc(&fcoe_driver_template,
-				sizeof(struct fc_lport) +
+	shost = fcoe_host_alloc(&fcoe_sw_shost_template,
 				sizeof(struct fcoe_softc));
-
 	if (!shost) {
 		FC_DBG("Could not allocate host structure\n");
 		return -ENOMEM;
 	}
-
 	lp = shost_priv(shost);
-	rc = lport_config(lp, shost);
-	if (rc)
-		goto out_host_put;
-
-	/* Configure the fcoe_softc */
-	fc = (struct fcoe_softc *)lp->drv_priv;
-	fc->lp = lp;
-	fc->real_dev = netdev;
-	shost_config(lp);
+	fc = lport_priv(lp);
 
+	/* configure fc_lport, e.g., em */
+	rc = fcoe_sw_lport_config(lp);
+	if (rc) {
+		FC_DBG("Could not configure lport\n");
+		goto out_host_put;
+	}
 
-	/* Add the new host to the SCSI-ml */
-	rc = scsi_add_host(lp->host, NULL);
+	/* configure lport network properties */
+	rc = fcoe_sw_netdev_config(lp, netdev);
 	if (rc) {
-		FC_DBG("error on scsi_add_host\n");
-		goto out_lp_destroy;
+		FC_DBG("Could not configure netdev for lport\n");
+		goto out_host_put;
 	}
 
-	sprintf(fc_host_symbolic_name(lp->host), "%s v%s over %s",
-		FCOE_DRIVER_NAME, FCOE_VERSION,
-		netdev->name);
+	/* configure lport scsi host properties */
+	rc = fcoe_sw_shost_config(lp, shost);
+	if (rc) {
+		FC_DBG("Could not configure shost for lport\n");
+		goto out_host_put;
+	}
 
-	/* Configure netdev and networking properties of the lp */
-	rc = net_config(lp);
-	if (rc)
-		goto out_lp_destroy;
+	/* lport exch manager allocation */
+	rc = fcoe_sw_em_config(lp);
+	if (rc) {
+		FC_DBG("Could not configure em for lport\n");
+		goto out_host_put;
+	}
 
 	/* Initialize the library */
-	rc = libfc_config(lp);
-	if (rc)
+	rc = fcoe_libfc_config(lp, &fcoe_sw_libfc_fcn_templ);
+	if (rc) {
+		FC_DBG("Could not configure libfc for lport!\n");
 		goto out_lp_destroy;
+	}
 
-	write_lock_bh(&fcoe_hostlist_lock);
-	list_add_tail(&fc->list, &fcoe_hostlist);
-	write_unlock_bh(&fcoe_hostlist_lock);
+	/* add to lports list */
+	fcoe_hostlist_add(lp);
 
 	lp->boot_time = jiffies;
 
 	fc_fabric_login(lp);
 
 	dev_hold(netdev);
+
 	return rc;
 
 out_lp_destroy:
@@ -480,28 +420,55 @@ out_host_put:
 	return rc;
 }
 
-void fcoe_clean_pending_queue(struct fc_lport *lp)
+/*
+ * fcoe_sw_match - the fcoe sw transport match function
+ *
+ * Returns : false always
+ */
+static bool fcoe_sw_match(struct net_device *netdev)
 {
-	struct fcoe_softc  *fc = lp->drv_priv;
-	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);
-		kfree_skb(skb);
-		spin_lock_bh(&fc->fcoe_pending_queue.lock);
-	}
-	spin_unlock_bh(&fc->fcoe_pending_queue.lock);
+	/* FIXME - for sw transport, always return false */
+	return false;
 }
 
+/* the sw hba fcoe transport */
+struct fcoe_transport fcoe_sw_transport = {
+	.name = "fcoesw",
+	.create = fcoe_sw_create,
+	.destroy = fcoe_sw_destroy,
+	.match = fcoe_sw_match,
+	.vendor = 0x0,
+	.device = 0xffff,
+};
+
+/*
+ * fcoe_sw_init - registers fcoe_sw_transport
+ *
+ * Returns : 0 on success
+ */
 int __init fcoe_sw_init(void)
 {
-	fcoe_transport_template =
-		fc_attach_transport(&fcoe_transport_function);
-	return fcoe_transport_template ? 0 : -1;
+	/* attach to scsi transport */
+	scsi_transport_fcoe_sw =
+		fc_attach_transport(&fcoe_sw_transport_function);
+	if (!scsi_transport_fcoe_sw) {
+		printk(KERN_ERR "fcoe_sw_init:fc_attach_transport() failed\n");
+		return -ENODEV;
+	}
+	/* register sw transport */
+	fcoe_transport_register(&fcoe_sw_transport);
+	return 0;
 }
 
-void __exit fcoe_sw_exit(void)
+/*
+ * fcoe_sw_exit - unregisters fcoe_sw_transport
+ *
+ * Returns : 0 on success
+ */
+int __exit fcoe_sw_exit(void)
 {
-	fc_release_transport(fcoe_transport_template);
+	/* dettach the transport */
+	fc_release_transport(scsi_transport_fcoe_sw);
+	fcoe_transport_unregister(&fcoe_sw_transport);
+	return 0;
 }
diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c
index e67fa4a..08cd9a6 100644
--- a/drivers/scsi/fcoe/libfcoe.c
+++ b/drivers/scsi/fcoe/libfcoe.c
@@ -17,10 +17,6 @@
  * Maintained at www.Open-FCoE.org
  */
 
-/*
- * FCOE protocol file
- */
-
 #include <linux/module.h>
 #include <linux/version.h>
 #include <linux/kernel.h>
@@ -28,9 +24,15 @@
 #include <linux/skbuff.h>
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
+#include <linux/ethtool.h>
 #include <linux/if_ether.h>
+#include <linux/if_vlan.h>
 #include <linux/kthread.h>
 #include <linux/crc32.h>
+#include <linux/cpu.h>
+#include <linux/fs.h>
+#include <linux/sysfs.h>
+#include <linux/ctype.h>
 #include <scsi/scsi_tcq.h>
 #include <scsi/scsicam.h>
 #include <scsi/scsi_transport.h>
@@ -38,11 +40,10 @@
 
 #include <scsi/fc/fc_encaps.h>
 
-#include <scsi/libfc/libfc.h>
-#include <scsi/libfc/fc_frame.h>
-
-#include <scsi/fc/fc_fcoe.h>
-#include "fcoe_def.h"
+#include <scsi/libfc.h>
+#include <scsi/fc_frame.h>
+#include <scsi/libfcoe.h>
+#include <scsi/fc_transport_fcoe.h>
 
 static int debug_fcoe;
 
@@ -52,18 +53,128 @@ static int debug_fcoe;
 #define FCOE_GW_ADDR_MODE	    0x00
 #define FCOE_FCOUI_ADDR_MODE	    0x01
 
+#define FCOE_WORD_TO_BYTE  4
+
+MODULE_AUTHOR("Open-FCoE.org");
+MODULE_DESCRIPTION("FCoE");
+MODULE_LICENSE("GPL");
+
+/* fcoe host list */
+LIST_HEAD(fcoe_hostlist);
+DEFINE_RWLOCK(fcoe_hostlist_lock);
+DEFINE_TIMER(fcoe_timer, NULL, 0, 0);
+struct fcoe_percpu_s *fcoe_percpu[NR_CPUS];
+
+
 /* Function Prototyes */
 static int fcoe_check_wait_queue(struct fc_lport *);
 static void fcoe_insert_wait_queue_head(struct fc_lport *, struct sk_buff *);
 static void fcoe_insert_wait_queue(struct fc_lport *, struct sk_buff *);
 static void fcoe_recv_flogi(struct fcoe_softc *, struct fc_frame *, u8 *);
+#ifdef CONFIG_HOTPLUG_CPU
+static int fcoe_cpu_callback(struct notifier_block *, ulong, void *);
+#endif /* CONFIG_HOTPLUG_CPU */
+static int fcoe_device_notification(struct notifier_block *, ulong, void *);
+static void fcoe_dev_setup(void);
+static void fcoe_dev_cleanup(void);
+
+/* notification function from net device */
+static struct notifier_block fcoe_notifier = {
+	.notifier_call = fcoe_device_notification,
+};
+
+
+#ifdef CONFIG_HOTPLUG_CPU
+static struct notifier_block fcoe_cpu_notifier = {
+	.notifier_call = fcoe_cpu_callback,
+};
+
+/**
+ * fcoe_create_percpu_data - creates the associated cpu data
+ * @cpu: index for the cpu where fcoe cpu data will be created
+ *
+ * create percpu stats block, from cpu add notifier
+ *
+ * Returns: none
+ **/
+static void fcoe_create_percpu_data(int cpu)
+{
+	struct fc_lport *lp;
+	struct fcoe_softc *fc;
 
-/*
- * this is the fcoe receive function
- * called by NET_RX_SOFTIRQ
- * this function will receive the packet and
- * build fc frame and pass it up
- */
+	write_lock_bh(&fcoe_hostlist_lock);
+	list_for_each_entry(fc, &fcoe_hostlist, list) {
+		lp = fc->lp;
+		if (lp->dev_stats[cpu] == NULL)
+			lp->dev_stats[cpu] =
+				kzalloc(sizeof(struct fcoe_dev_stats),
+					GFP_KERNEL);
+	}
+	write_unlock_bh(&fcoe_hostlist_lock);
+}
+
+/**
+ * fcoe_destroy_percpu_data - destroys the associated cpu data
+ * @cpu: index for the cpu where fcoe cpu data will destroyed
+ *
+ * destroy percpu stats block called by cpu add/remove notifier
+ *
+ * Retuns: none
+ **/
+static void fcoe_destroy_percpu_data(int cpu)
+{
+	struct fc_lport *lp;
+	struct fcoe_softc *fc;
+
+	write_lock_bh(&fcoe_hostlist_lock);
+	list_for_each_entry(fc, &fcoe_hostlist, list) {
+		lp = fc->lp;
+		kfree(lp->dev_stats[cpu]);
+		lp->dev_stats[cpu] = NULL;
+	}
+	write_unlock_bh(&fcoe_hostlist_lock);
+}
+
+/**
+ * fcoe_cpu_callback - fcoe cpu hotplug event callback
+ * @nfb: callback data block
+ * @action: event triggering the callback
+ * @hcpu: index for the cpu of this event
+ *
+ * this creates or destroys per cpu data for fcoe
+ *
+ * Returns NOTIFY_OK always.
+ **/
+static int fcoe_cpu_callback(struct notifier_block *nfb, unsigned long action,
+			     void *hcpu)
+{
+	unsigned int cpu = (unsigned long)hcpu;
+
+	switch (action) {
+	case CPU_ONLINE:
+		fcoe_create_percpu_data(cpu);
+		break;
+	case CPU_DEAD:
+		fcoe_destroy_percpu_data(cpu);
+		break;
+	default:
+		break;
+	}
+	return NOTIFY_OK;
+}
+#endif /* CONFIG_HOTPLUG_CPU */
+
+/**
+ * foce_rcv - this is the fcoe receive function called by NET_RX_SOFTIRQ
+ * @skb: the receive skb
+ * @dev: associated net device
+ * @ptype: context
+ * @odldev: last device
+ *
+ * this function will receive the packet and build fc frame and pass it up
+ *
+ * Returns: 0 for success
+ **/
 int fcoe_rcv(struct sk_buff *skb, struct net_device *dev,
 	     struct packet_type *ptype, struct net_device *olddev)
 {
@@ -141,7 +252,14 @@ err2:
 	kfree_skb(skb);
 	return -1;
 }
+EXPORT_SYMBOL_GPL(fcoe_rcv);
 
+/**
+ * fcoe_start_io - pass to netdev to start xmit for fcoe
+ * @skb: the skb to be xmitted
+ *
+ * Returns: 0 for success
+ **/
 static inline int fcoe_start_io(struct sk_buff *skb)
 {
 	int rc;
@@ -154,6 +272,13 @@ static inline int fcoe_start_io(struct sk_buff *skb)
 	return 0;
 }
 
+/**
+ * fcoe_get_paged_crc_eof - in case we need alloc a page for crc_eof
+ * @skb: the skb to be xmitted
+ * @tlen: total len
+ *
+ * Returns: 0 for success
+ **/
 static int fcoe_get_paged_crc_eof(struct sk_buff *skb, int tlen)
 {
 	struct fcoe_percpu_s *fps;
@@ -190,12 +315,53 @@ static int fcoe_get_paged_crc_eof(struct sk_buff *skb, int tlen)
 	return 0;
 }
 
-/*
- * this is the frame xmit routine
- */
+/**
+ * fcoe_fc_crc - calculates FC CRC in this fcoe skb
+ * @fp: the fc_frame containg data to be checksummed
+ *
+ * This uses crc32() to calculate the crc for fc frame
+ * Return   : 32 bit crc
+ *
+ **/
+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;
+}
+EXPORT_SYMBOL_GPL(fcoe_fc_crc);
+
+/**
+ * fcoe_xmit - FCoE frame transmit function
+ * @lp:	the associated local port
+ * @fp: the fc_frame to be transmitted
+ *
+ * Return   : 0 for success
+ *
+ **/
 int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
 {
-	int indx;
 	int wlen, rc = 0;
 	u32 crc;
 	struct ethhdr *eh;
@@ -205,15 +371,15 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
 	struct fc_frame_header *fh;
 	unsigned int hlen;		/* header length implies the version */
 	unsigned int tlen;		/* trailer length */
+	unsigned int elen;		/* eth header, may include vlan */
 	int flogi_in_progress = 0;
 	struct fcoe_softc *fc;
-	void *data;
 	u8 sof, eof;
 	struct fcoe_hdr *hp;
 
 	WARN_ON((fr_len(fp) % sizeof(u32)) != 0);
 
-	fc = (struct fcoe_softc *)lp->drv_priv;
+	fc = fcoe_softc(lp);
 	/*
 	 * if it is a flogi then we need to learn gw-addr
 	 * and my own fcid
@@ -242,45 +408,17 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
 	sof = fr_sof(fp);
 	eof = fr_eof(fp);
 
-	crc = ~0;
-	crc = crc32(crc, skb->data, skb_headlen(skb));
-
-	for (indx = 0; indx < skb_shinfo(skb)->nr_frags; indx++) {
-		skb_frag_t *frag = &skb_shinfo(skb)->frags[indx];
-		unsigned long off = frag->page_offset;
-		unsigned long len = frag->size;
-
-		while (len > 0) {
-			unsigned long clen;
-
-			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;
-		}
-	}
-
-	/*
-	 * Get header and trailer lengths.
-	 * This is temporary code until we get rid of the old protocol.
-	 * Both versions have essentially the same trailer layout but T11
-	 * has padding afterwards.
-	 */
+	elen = (fc->real_dev->priv_flags & IFF_802_1Q_VLAN) ?
+		sizeof(struct vlan_ethhdr) : sizeof(struct ethhdr);
 	hlen = sizeof(struct fcoe_hdr);
 	tlen = sizeof(struct fcoe_crc_eof);
+	wlen = (skb->len - tlen + sizeof(crc)) / FCOE_WORD_TO_BYTE;
 
-	/*
-	 * copy fc crc and eof to the skb buff
-	 * Use utility buffer in the fc_frame part of the sk_buff for the
-	 * trailer.
-	 * We don't do a get_page for this frag, since that page may not be
-	 * managed that way.  So that skb_free() doesn't do that either, we
-	 * setup the destructor to remove this frag.
-	 */
+	/* crc offload */
+	skb->ip_summed = CHECKSUM_NONE;
+	crc = fcoe_fc_crc(fp);
+
+	/* copy fc crc and eof to the skb buff */
 	if (skb_is_nonlinear(skb)) {
 		skb_frag_t *frag;
 		if (fcoe_get_paged_crc_eof(skb, tlen)) {
@@ -294,22 +432,26 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
 		cp = (struct fcoe_crc_eof *)skb_put(skb, tlen);
 	}
 
+	memset(cp, 0, sizeof(*cp));
 	cp->fcoe_eof = eof;
 	cp->fcoe_crc32 = cpu_to_le32(~crc);
-	if (tlen == sizeof(*cp))
-		memset(cp->fcoe_resvd, 0, sizeof(cp->fcoe_resvd));
-	wlen = (skb->len - tlen + sizeof(crc)) / FCOE_WORD_TO_BYTE;
 
 	if (skb_is_nonlinear(skb)) {
 		kunmap_atomic(cp, KM_SKB_DATA_SOFTIRQ);
 		cp = NULL;
 	}
 
-	/*
-	 *	Fill in the control structures
-	 */
-	skb->ip_summed = CHECKSUM_NONE;
-	eh = (struct ethhdr *)skb_push(skb, hlen + sizeof(struct ethhdr));
+	/* adjust skb netowrk/transport offsets to match mac/fcoe/fc */
+	skb_push(skb, elen + hlen);
+	skb_reset_mac_header(skb);
+	skb_reset_network_header(skb);
+	skb->mac_len = elen;
+	skb->protocol = htons(ETH_P_802_3);
+	skb->dev = fc->real_dev;
+
+	/* fill up mac and fcoe headers */
+	eh = eth_hdr(skb);
+	eh->h_proto = htons(ETH_P_FCOE);
 	if (fc->address_mode == FCOE_FCOUI_ADDR_MODE)
 		fc_fcoe_set_mac(eh->h_dest, fh->fh_d_id);
 	else
@@ -321,24 +463,20 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
 	else
 		memcpy(eh->h_source, fc->data_src_addr, ETH_ALEN);
 
-	eh->h_proto = htons(ETH_P_FCOE);
-	skb->protocol = htons(ETH_P_802_3);
-	skb_reset_mac_header(skb);
-	skb_reset_network_header(skb);
-
 	hp = (struct fcoe_hdr *)(eh + 1);
 	memset(hp, 0, sizeof(*hp));
 	if (FC_FCOE_VER)
 		FC_FCOE_ENCAPS_VER(hp, FC_FCOE_VER);
 	hp->fcoe_sof = sof;
 
+	/* update tx stats: regardless if LLD fails */
 	stats = lp->dev_stats[smp_processor_id()];
 	if (stats) {
 		stats->TxFrames++;
 		stats->TxWords += wlen;
 	}
-	skb->dev = fc->real_dev;
 
+	/* send down to lld */
 	fr_dev(fp) = lp;
 	if (fc->fcoe_pending_queue.qlen)
 		rc = fcoe_check_wait_queue(lp);
@@ -354,7 +492,15 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
 
 	return 0;
 }
+EXPORT_SYMBOL_GPL(fcoe_xmit);
 
+/*
+ * fcoe_percpu_receive_thread - recv thread per cpu
+ * @arg: ptr to the fcoe per cpu struct
+ *
+ * Return: 0 for success
+ *
+ */
 int fcoe_percpu_receive_thread(void *arg)
 {
 	struct fcoe_percpu_s *p = arg;
@@ -367,7 +513,6 @@ int fcoe_percpu_receive_thread(void *arg)
 	struct fc_frame_header *fh;
 	struct sk_buff *skb;
 	struct fcoe_crc_eof *cp;
-	enum fc_sof sof;
 	struct fc_frame *fp;
 	u8 *mac = NULL;
 	struct fcoe_softc *fc;
@@ -410,7 +555,7 @@ int fcoe_percpu_receive_thread(void *arg)
 		/*
 		 * Save source MAC address before discarding header.
 		 */
-		fc = lp->drv_priv;
+		fc = lport_priv(lp);
 		if (unlikely(fc->flogi_progress))
 			mac = eth_hdr(skb)->h_source;
 
@@ -421,7 +566,6 @@ int fcoe_percpu_receive_thread(void *arg)
 		 * Check the header and pull it off.
 		 */
 		hlen = sizeof(struct fcoe_hdr);
-
 		hp = (struct fcoe_hdr *)skb->data;
 		if (unlikely(FC_FCOE_DECAPS_VER(hp) != FC_FCOE_VER)) {
 			if (stats) {
@@ -433,11 +577,10 @@ int fcoe_percpu_receive_thread(void *arg)
 			kfree_skb(skb);
 			continue;
 		}
-		sof = hp->fcoe_sof;
-		skb_pull(skb, sizeof(*hp));
-		fr_len = skb->len - sizeof(struct fcoe_crc_eof);
-		skb_trim(skb, fr_len);
+		skb_pull(skb, sizeof(struct fcoe_hdr));
 		tlen = sizeof(struct fcoe_crc_eof);
+		fr_len = skb->len - tlen;
+		skb_trim(skb, fr_len);
 
 		if (unlikely(fr_len > skb->len)) {
 			if (stats) {
@@ -455,47 +598,61 @@ int fcoe_percpu_receive_thread(void *arg)
 			stats->RxWords += fr_len / FCOE_WORD_TO_BYTE;
 		}
 
-		fp = (struct fc_frame *) skb;
-		fc_frame_init(fp);
+		fp = (struct fc_frame *)skb;
 		cp = (struct fcoe_crc_eof *)(skb->data + fr_len);
+		fc_frame_init(fp);
 		fr_eof(fp) = cp->fcoe_eof;
-		fr_sof(fp) = sof;
+		fr_sof(fp) = hp->fcoe_sof;
 		fr_dev(fp) = lp;
 
 		/*
-		 * Check the CRC here, unless it's solicited data for SCSI.
-		 * In that case, the SCSI layer can check it during the copy,
-		 * and it'll be more cache-efficient.
+		 * 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 (lp->crc_offload)
+			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;
-			fc_exch_recv(lp, lp->emp, fp);
-		} else if (le32_to_cpu(cp->fcoe_crc32) ==
-			   ~crc32(~0, skb->data, fr_len)) {
-			if (unlikely(fc->flogi_progress))
-				fcoe_recv_flogi(fc, fp, mac);
 			fc_exch_recv(lp, lp->emp, fp);
-		} else {
-			if (debug_fcoe ||
-			    (stats && stats->InvalidCRCCount < 5)) {
-				printk(KERN_WARNING \
-				       "fcoe: dropping frame with CRC error");
-			}
-			if (stats) {
+			continue;
+		}
+		if (fr_flags(fp) & FCPHF_CRC_UNCHECKED) {
+			if (le32_to_cpu(cp->fcoe_crc32) !=
+			    ~crc32(~0, skb->data, fr_len)) {
+				if (debug_fcoe || stats->InvalidCRCCount < 5)
+					printk(KERN_WARNING "fcoe: dropping "
+					       "frame with CRC error\n");
 				stats->InvalidCRCCount++;
 				stats->ErrorFrames++;
+				fc_frame_free(fp);
+				continue;
 			}
-			fc_frame_free(fp);
+			fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED;
 		}
+		/* non flogi and non data exchanges are handled here */
+		if (unlikely(fc->flogi_progress))
+			fcoe_recv_flogi(fc, fp, mac);
+		fc_exch_recv(lp, lp->emp, fp);
 	}
 	return 0;
 }
 
-/*
- * Snoop potential response to FLOGI or even incoming FLOGI.
- */
+/**
+ * fcoe_recv_flogi - flogi receive function
+ * @fc: associated fcoe_softc
+ * @fp: the recieved frame
+ * @sa: the source address of this flogi
+ *
+ * This is responsible to parse the flogi response and sets the corresponding
+ * mac address for the initiator, eitehr OUI based or GW based.
+ *
+ * Returns: none
+ **/
 static void fcoe_recv_flogi(struct fcoe_softc *fc, struct fc_frame *fp, u8 *sa)
 {
 	struct fc_frame_header *fh;
@@ -531,6 +688,16 @@ static void fcoe_recv_flogi(struct fcoe_softc *fc, struct fc_frame *fp, u8 *sa)
 	}
 }
 
+/**
+ * fcoe_watchdog - fcoe timer callback
+ * @vp:
+ *
+ * This checks the pending queue length for fcoe and put fcoe to be paused state
+ * if the FCOE_MAX_QUEUE_DEPTH is reached. This is done for all fc_lport on the
+ * fcoe_hostlist.
+ *
+ * Returns: 0 for success
+ **/
 void fcoe_watchdog(ulong vp)
 {
 	struct fc_lport *lp;
@@ -555,27 +722,23 @@ void fcoe_watchdog(ulong vp)
 	add_timer(&fcoe_timer);
 }
 
-/*
- * the wait_queue is used when the skb transmit fails. skb will go
- * in the wait_queue which will be emptied by the time function OR
- * by the next skb transmit.
- *
- */
 
-/*
- * Function name : fcoe_check_wait_queue()
+/**
+ * fcoe_check_wait_queue - put the skb into fcoe pending xmit queue
+ * @lp: the fc_port for this skb
+ * @skb: the associated skb to be xmitted
  *
- * Return Values : 0 or error
+ * This empties the wait_queue, dequeue the head of the wait_queue queue
+ * and calls fcoe_start_io() for each packet, if all skb have been
+ * transmitted, return 0 if a error occurs, then restore wait_queue and
+ * try again later.
  *
- * Description	 : empties the wait_queue
- *		   dequeue the head of the wait_queue queue and
- *		   calls fcoe_start_io() for each packet
- *		   if all skb have been transmitted, return 0
- *		   if a error occurs, then restore wait_queue and try again
- *		   later
+ * The wait_queue is used when the skb transmit fails. skb will go
+ * in the wait_queue which will be emptied by the time function OR
+ * by the next skb transmit.
  *
- */
-
+ * Returns: 0 for success
+ **/
 static int fcoe_check_wait_queue(struct fc_lport *lp)
 {
 	int rc, unpause = 0;
@@ -583,7 +746,7 @@ static int fcoe_check_wait_queue(struct fc_lport *lp)
 	struct sk_buff *skb;
 	struct fcoe_softc *fc;
 
-	fc = (struct fcoe_softc *)lp->drv_priv;
+	fc = fcoe_softc(lp);
 	spin_lock_bh(&fc->fcoe_pending_queue.lock);
 
 	/*
@@ -610,24 +773,646 @@ static int fcoe_check_wait_queue(struct fc_lport *lp)
 	return fc->fcoe_pending_queue.qlen;
 }
 
+/**
+ * fcoe_insert_wait_queue_head - puts skb to fcoe pending queue head
+ * @lp: the fc_port for this skb
+ * @skb: the associated skb to be xmitted
+ *
+ * Returns: none
+ **/
 static void fcoe_insert_wait_queue_head(struct fc_lport *lp,
 					struct sk_buff *skb)
 {
 	struct fcoe_softc *fc;
 
-	fc = (struct fcoe_softc *)lp->drv_priv;
+	fc = fcoe_softc(lp);
 	spin_lock_bh(&fc->fcoe_pending_queue.lock);
 	__skb_queue_head(&fc->fcoe_pending_queue, skb);
 	spin_unlock_bh(&fc->fcoe_pending_queue.lock);
 }
 
+/**
+ * fcoe_insert_wait_queue - put the skb into fcoe pending queue tail
+ * @lp: the fc_port for this skb
+ * @skb: the associated skb to be xmitted
+ *
+ * Returns: none
+ **/
 static void fcoe_insert_wait_queue(struct fc_lport *lp,
 				   struct sk_buff *skb)
 {
 	struct fcoe_softc *fc;
 
-	fc = (struct fcoe_softc *)lp->drv_priv;
+	fc = fcoe_softc(lp);
 	spin_lock_bh(&fc->fcoe_pending_queue.lock);
 	__skb_queue_tail(&fc->fcoe_pending_queue, skb);
 	spin_unlock_bh(&fc->fcoe_pending_queue.lock);
 }
+
+/**
+ * fcoe_dev_setup - setup link change notification interface
+ *
+ **/
+static void fcoe_dev_setup(void)
+{
+	/*
+	 * here setup a interface specific wd time to
+	 * monitor the link state
+	 */
+	register_netdevice_notifier(&fcoe_notifier);
+}
+
+/**
+ * fcoe_dev_setup - cleanup link change notification interface
+ **/
+static void fcoe_dev_cleanup(void)
+{
+	unregister_netdevice_notifier(&fcoe_notifier);
+}
+
+/**
+ * fcoe_device_notification - netdev event notification callback
+ * @notifier: context of the notification
+ * @event: type of event
+ * @ptr: fixed array for output parsed ifname
+ *
+ * This function is called by the ethernet driver in case of link change event
+ *
+ * Returns: 0 for success
+ **/
+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 fcoe_dev_stats *stats;
+	u16 new_status;
+	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->lp;
+			break;
+		}
+	}
+	read_unlock(&fcoe_hostlist_lock);
+	if (lp == NULL) {
+		rc = NOTIFY_DONE;
+		goto out;
+	}
+
+	new_status = lp->link_status;
+	switch (event) {
+	case NETDEV_DOWN:
+	case NETDEV_GOING_DOWN:
+		new_status &= ~FC_LINK_UP;
+		break;
+	case NETDEV_UP:
+	case NETDEV_CHANGE:
+		new_status &= ~FC_LINK_UP;
+		if (!fcoe_link_ok(lp))
+			new_status |= FC_LINK_UP;
+		break;
+	case NETDEV_CHANGEMTU:
+		mfs = fc->real_dev->mtu -
+			(sizeof(struct fcoe_hdr) +
+			 sizeof(struct fcoe_crc_eof));
+		if (fc->user_mfs && fc->user_mfs < mfs)
+			mfs = fc->user_mfs;
+		if (mfs >= FC_MIN_MAX_FRAME)
+			fc_set_mfs(lp, mfs);
+		new_status &= ~FC_LINK_UP;
+		if (!fcoe_link_ok(lp))
+			new_status |= FC_LINK_UP;
+		break;
+	case NETDEV_REGISTER:
+		break;
+	default:
+		FC_DBG("unknown event %ld call", event);
+	}
+	if (lp->link_status != new_status) {
+		if ((new_status & FC_LINK_UP) == FC_LINK_UP)
+			fc_linkup(lp);
+		else {
+			stats = lp->dev_stats[smp_processor_id()];
+			if (stats)
+				stats->LinkFailureCount++;
+			fc_linkdown(lp);
+			fcoe_clean_pending_queue(lp);
+		}
+	}
+out:
+	return rc;
+}
+
+/**
+ * fcoe_if_to_netdev - parse a name buffer to get netdev
+ * @ifname: fixed array for output parsed ifname
+ * @buffer: incoming buffer to be copied
+ *
+ * Returns: NULL or ptr to netdeive
+ **/
+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(ifname);
+	}
+	return NULL;
+}
+
+/**
+ * fcoe_destroy- handles the destroy from sysfs
+ * @buffer: expcted to be a eth if name
+ * @kp: associated kernel param
+ *
+ * Returns: 0 for success
+ **/
+static int fcoe_destroy(const char *buffer, struct kernel_param *kp)
+{
+	int rc;
+	struct net_device *netdev;
+
+	netdev = fcoe_if_to_netdev(buffer);
+	if (!netdev) {
+		rc = -ENODEV;
+		goto out_nodev;
+	}
+	/* look for existing lport */
+	if (!fcoe_hostlist_lookup(netdev)) {
+		rc = -ENODEV;
+		goto out_putdev;
+	}
+	/* pass to transport */
+	rc = fcoe_transport_release(netdev);
+	if (rc) {
+		printk(KERN_ERR "fcoe: fcoe_transport_release(%s) failed\n",
+		       netdev->name);
+		rc = -EIO;
+		goto out_putdev;
+	}
+	rc = 0;
+out_putdev:
+	dev_put(netdev);
+out_nodev:
+	return rc;
+}
+
+/**
+ * fcoe_create - handles the create call from sysfs
+ * @buffer: expcted to be a eth if name
+ * @kp: associated kernel param
+ *
+ * Returns: 0 for success
+ **/
+static int fcoe_create(const char *buffer, struct kernel_param *kp)
+{
+	int rc;
+	struct net_device *netdev;
+
+	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;
+	}
+
+	/* pass to transport */
+	rc = fcoe_transport_attach(netdev);
+	if (rc) {
+		printk(KERN_ERR "fcoe: fcoe_transport_attach(%s) failed\n",
+		       netdev->name);
+		rc = -EIO;
+		goto out_putdev;
+	}
+	rc = 0;
+out_putdev:
+	dev_put(netdev);
+out_nodev:
+	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_param_call(destroy, fcoe_destroy, NULL, NULL, S_IWUSR);
+__MODULE_PARM_TYPE(destroy, "string");
+MODULE_PARM_DESC(destroy, "Destroy fcoe port");
+
+/*
+ * fcoe_link_ok - check if link is ok for the fc_lport
+ * @lp: ptr to the fc_lport
+ *
+ * Any permanently-disqualifying conditions have been previously checked.
+ * This also updates the speed setting, which may change with link for 100/1000.
+ *
+ * This function should probably be checking for PAUSE support at some point
+ * in the future. Currently Per-priority-pause is not determinable using
+ * ethtool, so we shouldn't be restrictive until that problem is resolved.
+ *
+ * Returns: 0 if link is OK for use by FCoE.
+ *
+ */
+int fcoe_link_ok(struct fc_lport *lp)
+{
+	struct fcoe_softc *fc = fcoe_softc(lp);
+	struct net_device *dev = fc->real_dev;
+	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;
+
+	return rc;
+}
+EXPORT_SYMBOL_GPL(fcoe_link_ok);
+
+/*
+ * fcoe_percpu_clean - frees skb of the corresponding lport from the per
+ * cpu queue.
+ * @lp: the fc_lport
+ */
+void fcoe_percpu_clean(struct fc_lport *lp)
+{
+	int idx;
+	struct fcoe_percpu_s *pp;
+	struct fcoe_rcv_info *fr;
+	struct sk_buff_head *list;
+	struct sk_buff *skb, *next;
+	struct sk_buff *head;
+
+	for (idx = 0; idx < NR_CPUS; idx++) {
+		if (fcoe_percpu[idx]) {
+			pp = fcoe_percpu[idx];
+			spin_lock_bh(&pp->fcoe_rx_list.lock);
+			list = &pp->fcoe_rx_list;
+			head = list->next;
+			for (skb = head; skb != (struct sk_buff *)list;
+			     skb = next) {
+				next = skb->next;
+				fr = fcoe_dev_from_skb(skb);
+				if (fr->fr_dev == lp) {
+					__skb_unlink(skb, list);
+					kfree_skb(skb);
+				}
+			}
+			spin_unlock_bh(&pp->fcoe_rx_list.lock);
+		}
+	}
+}
+EXPORT_SYMBOL_GPL(fcoe_percpu_clean);
+
+/**
+ * fcoe_clean_pending_queue - dequeue skb and free it
+ * @lp: the corresponding fc_lport
+ *
+ * Returns: none
+ **/
+void fcoe_clean_pending_queue(struct fc_lport *lp)
+{
+	struct fcoe_softc  *fc = 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);
+		kfree_skb(skb);
+		spin_lock_bh(&fc->fcoe_pending_queue.lock);
+	}
+	spin_unlock_bh(&fc->fcoe_pending_queue.lock);
+}
+EXPORT_SYMBOL_GPL(fcoe_clean_pending_queue);
+
+/**
+ * libfc_host_alloc - allocate a Scsi_Host with room for the fc_lport
+ * @sht: ptr to the scsi host templ
+ * @priv_size: size of private data after fc_lport
+ *
+ * Returns: ptr to Scsi_Host
+ * TODO - to libfc?
+ */
+static inline struct Scsi_Host *libfc_host_alloc(
+	struct scsi_host_template *sht, int priv_size)
+{
+	return scsi_host_alloc(sht, sizeof(struct fc_lport) + priv_size);
+}
+
+/**
+ * fcoe_host_alloc - allocate a Scsi_Host with room for the fcoe_softc
+ * @sht: ptr to the scsi host templ
+ * @priv_size: size of private data after fc_lport
+ *
+ * Returns: ptr to Scsi_Host
+ */
+struct Scsi_Host *fcoe_host_alloc(struct scsi_host_template *sht, int priv_size)
+{
+	return libfc_host_alloc(sht, sizeof(struct fcoe_softc) + priv_size);
+}
+EXPORT_SYMBOL_GPL(fcoe_host_alloc);
+
+/*
+ * fcoe_reset - resets the fcoe
+ * @shost: shost the reset is from
+ *
+ * Returns: always 0
+ */
+int fcoe_reset(struct Scsi_Host *shost)
+{
+	struct fc_lport *lport = shost_priv(shost);
+	fc_lport_reset(lport);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(fcoe_reset);
+
+/*
+ * fcoe_wwn_from_mac - converts 48-bit IEEE MAC address to 64-bit FC WWN.
+ * @mac: mac address
+ * @scheme: check port
+ * @port: port indicator for converting
+ *
+ * Returns: u64 fc world wide name
+ */
+u64 fcoe_wwn_from_mac(unsigned char mac[MAX_ADDR_LEN],
+		      unsigned int scheme, unsigned int port)
+{
+	u64 wwn;
+	u64 host_mac;
+
+	/* The MAC is in NO, so flip only the low 48 bits */
+	host_mac = ((u64) mac[0] << 40) |
+		((u64) mac[1] << 32) |
+		((u64) mac[2] << 24) |
+		((u64) mac[3] << 16) |
+		((u64) mac[4] << 8) |
+		(u64) mac[5];
+
+	WARN_ON(host_mac >= (1ULL << 48));
+	wwn = host_mac | ((u64) scheme << 60);
+	switch (scheme) {
+	case 1:
+		WARN_ON(port != 0);
+		break;
+	case 2:
+		WARN_ON(port >= 0xfff);
+		wwn |= (u64) port << 48;
+		break;
+	default:
+		WARN_ON(1);
+		break;
+	}
+
+	return wwn;
+}
+EXPORT_SYMBOL_GPL(fcoe_wwn_from_mac);
+/*
+ * fcoe_hostlist_lookup_softc - find the corresponding lport by a given device
+ * @device: this is currently ptr to net_device
+ *
+ * Returns: NULL or the located fcoe_softc
+ */
+static struct fcoe_softc *fcoe_hostlist_lookup_softc(
+	const struct net_device *dev)
+{
+	struct fcoe_softc *fc;
+
+	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;
+		}
+	}
+	read_unlock(&fcoe_hostlist_lock);
+	return NULL;
+}
+
+/*
+ * fcoe_hostlist_lookup - find the corresponding lport by netdev
+ * @netdev: ptr to net_device
+ *
+ * Returns: 0 for success
+ */
+struct fc_lport *fcoe_hostlist_lookup(const struct net_device *netdev)
+{
+	struct fcoe_softc *fc;
+
+	fc = fcoe_hostlist_lookup_softc(netdev);
+
+	return (fc) ? fc->lp : NULL;
+}
+EXPORT_SYMBOL_GPL(fcoe_hostlist_lookup);
+
+/*
+ * fcoe_hostlist_add - add a lport to lports list
+ * @lp: ptr to the fc_lport to badded
+ *
+ * Returns: 0 for success
+ */
+int fcoe_hostlist_add(const struct fc_lport *lp)
+{
+	struct fcoe_softc *fc;
+
+	fc = fcoe_hostlist_lookup_softc(fcoe_netdev(lp));
+	if (!fc) {
+		fc = fcoe_softc(lp);
+		write_lock_bh(&fcoe_hostlist_lock);
+		list_add_tail(&fc->list, &fcoe_hostlist);
+		write_unlock_bh(&fcoe_hostlist_lock);
+	}
+	return 0;
+}
+EXPORT_SYMBOL_GPL(fcoe_hostlist_add);
+
+/*
+ * fcoe_hostlist_remove - remove a lport from lports list
+ * @lp: ptr to the fc_lport to badded
+ *
+ * Returns: 0 for success
+ */
+int fcoe_hostlist_remove(const struct fc_lport *lp)
+{
+	struct fcoe_softc *fc;
+
+	fc = fcoe_hostlist_lookup_softc(fcoe_netdev(lp));
+	BUG_ON(!fc);
+	write_lock_bh(&fcoe_hostlist_lock);
+	list_del(&fc->list);
+	write_unlock_bh(&fcoe_hostlist_lock);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(fcoe_hostlist_remove);
+
+/**
+ * fcoe_libfc_config - sets up libfc related properties for lport
+ * @lp: ptr to the fc_lport
+ * @tt: libfc function template
+ *
+ * Returns : 0 for success
+ **/
+int fcoe_libfc_config(struct fc_lport *lp, struct libfc_function_template *tt)
+{
+	/* Set the function pointers set by the LLDD */
+	memcpy(&lp->tt, tt, sizeof(*tt));
+	if (fc_fcp_init(lp))
+		return -ENOMEM;
+	fc_exch_init(lp);
+	fc_elsct_init(lp);
+	fc_lport_init(lp);
+	fc_rport_init(lp);
+	fc_disc_init(lp);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(fcoe_libfc_config);
+
+/**
+ * fcoe_init - fcoe module loading initialization
+ *
+ * Initialization routine
+ * 1. Will create fc transport software structure
+ * 2. initialize the link list of port information structure
+ *
+ * Returns 0 on success, negative on failure
+ **/
+static int __init fcoe_init(void)
+{
+	int cpu;
+	struct fcoe_percpu_s *p;
+
+
+	INIT_LIST_HEAD(&fcoe_hostlist);
+	rwlock_init(&fcoe_hostlist_lock);
+
+#ifdef CONFIG_HOTPLUG_CPU
+	register_cpu_notifier(&fcoe_cpu_notifier);
+#endif /* CONFIG_HOTPLUG_CPU */
+
+	/*
+	 * initialize per CPU interrupt thread
+	 */
+	for_each_online_cpu(cpu) {
+		p = kzalloc(sizeof(struct fcoe_percpu_s), GFP_KERNEL);
+		if (p) {
+			p->thread = kthread_create(fcoe_percpu_receive_thread,
+						   (void *)p,
+						   "fcoethread/%d", cpu);
+
+			/*
+			 * if there is no error then bind the thread to the cpu
+			 * initialize the semaphore and skb queue head
+			 */
+			if (likely(!IS_ERR(p->thread))) {
+				p->cpu = cpu;
+				fcoe_percpu[cpu] = p;
+				skb_queue_head_init(&p->fcoe_rx_list);
+				kthread_bind(p->thread, cpu);
+				wake_up_process(p->thread);
+			} else {
+				fcoe_percpu[cpu] = NULL;
+				kfree(p);
+
+			}
+		}
+	}
+
+	/*
+	 * setup link change notification
+	 */
+	fcoe_dev_setup();
+
+	init_timer(&fcoe_timer);
+	fcoe_timer.data = 0;
+	fcoe_timer.function = fcoe_watchdog;
+	fcoe_timer.expires = (jiffies + (10 * HZ));
+	add_timer(&fcoe_timer);
+
+	/* initiatlize the fcoe transport */
+	fcoe_transport_init();
+
+	fcoe_sw_init();
+
+	return 0;
+}
+module_init(fcoe_init);
+
+/**
+ * fcoe_exit - fcoe module unloading cleanup
+ *
+ * Returns 0 on success, negative on failure
+ **/
+static void __exit fcoe_exit(void)
+{
+	u32 idx;
+	struct fcoe_softc *fc, *tmp;
+	struct fcoe_percpu_s *p;
+	struct sk_buff *skb;
+
+	/*
+	 * Stop all call back interfaces
+	 */
+#ifdef CONFIG_HOTPLUG_CPU
+	unregister_cpu_notifier(&fcoe_cpu_notifier);
+#endif /* CONFIG_HOTPLUG_CPU */
+	fcoe_dev_cleanup();
+
+	/*
+	 * stop timer
+	 */
+	del_timer_sync(&fcoe_timer);
+
+	/* releases the assocaited fcoe transport for each lport */
+	list_for_each_entry_safe(fc, tmp, &fcoe_hostlist, list)
+		fcoe_transport_release(fc->real_dev);
+
+	for (idx = 0; idx < NR_CPUS; idx++) {
+		if (fcoe_percpu[idx]) {
+			kthread_stop(fcoe_percpu[idx]->thread);
+			p = fcoe_percpu[idx];
+			spin_lock_bh(&p->fcoe_rx_list.lock);
+			while ((skb = __skb_dequeue(&p->fcoe_rx_list)) != NULL)
+				kfree_skb(skb);
+			spin_unlock_bh(&p->fcoe_rx_list.lock);
+			if (fcoe_percpu[idx]->crc_eof_page)
+				put_page(fcoe_percpu[idx]->crc_eof_page);
+			kfree(fcoe_percpu[idx]);
+		}
+	}
+
+	/* remove sw trasnport */
+	fcoe_sw_exit();
+
+	/* detach the transport */
+	fcoe_transport_exit();
+}
+module_exit(fcoe_exit);
diff --git a/drivers/scsi/fnic/cq_desc.h b/drivers/scsi/fnic/cq_desc.h
index c601cb8..d1225cf 100644
--- a/drivers/scsi/fnic/cq_desc.h
+++ b/drivers/scsi/fnic/cq_desc.h
@@ -42,9 +42,10 @@ struct cq_desc {
 	u8 type_color;
 };
 
-#define CQ_DESC_TYPE_BITS        7
+#define CQ_DESC_TYPE_BITS        4
 #define CQ_DESC_TYPE_MASK        ((1 << CQ_DESC_TYPE_BITS) - 1)
 #define CQ_DESC_COLOR_MASK       1
+#define CQ_DESC_COLOR_SHIFT      7
 #define CQ_DESC_Q_NUM_BITS       10
 #define CQ_DESC_Q_NUM_MASK       ((1 << CQ_DESC_Q_NUM_BITS) - 1)
 #define CQ_DESC_COMP_NDX_BITS    12
@@ -56,7 +57,7 @@ static inline void cq_desc_dec(const struct cq_desc *desc_arg,
 	const struct cq_desc *desc = desc_arg;
 	const u8 type_color = desc->type_color;
 
-	*color = (type_color >> CQ_DESC_TYPE_BITS) & CQ_DESC_COLOR_MASK;
+	*color = (type_color >> CQ_DESC_COLOR_SHIFT) & CQ_DESC_COLOR_MASK;
 
 	/*
 	 * Make sure color bit is read from desc *before* other fields
diff --git a/drivers/scsi/fnic/fnic.h b/drivers/scsi/fnic/fnic.h
index bde7cca..ec6671e 100644
--- a/drivers/scsi/fnic/fnic.h
+++ b/drivers/scsi/fnic/fnic.h
@@ -23,7 +23,7 @@
 #include <linux/netdevice.h>
 #include <scsi/scsi_device.h>
 #include <scsi/scsi_transport_fc.h>
-#include <scsi/libfc/libfc.h>
+#include <scsi/libfc.h>
 #include "fnic_io.h"
 #include "fnic_res.h"
 #include "vnic_dev.h"
@@ -41,6 +41,16 @@
 #define PFX			DRV_NAME ": "
 #define DFX                     DRV_NAME "%d: "
 
+/* Define FNIC_DEBUG to turn debugging on */
+#ifdef FNIC_DEBUG
+#define FNIC_DBG(fmt, args...)						\
+	do {								\
+		printk(KERN_DEBUG fmt, ##args);		                \
+	} while (0)
+#else
+#define FNIC_DBG(fmt, args...)
+#endif
+
 #define vnic_fc_config  vnic_scsi_config
 
 #define DESC_CLEAN_LOW_WATERMARK 8
@@ -73,7 +83,7 @@
 #define FNIC_LUN_RESET_TIMEOUT	     10000	/* mSec */
 #define FNIC_HOST_RESET_TIMEOUT	     10000	/* mSec */
 #define FNIC_RMDEVICE_TIMEOUT        1000       /* mSec */
-#define FNIC_HOST_RESET_SETTLE_TIME  10         /* Sec */
+#define FNIC_HOST_RESET_SETTLE_TIME  30         /* Sec */
 
 #define	FNIC_MAX_LUN            1023
 #define FNIC_MAX_FCP_TARGET     256
@@ -116,6 +126,11 @@ enum fnic_state {
 #define FNIC_RQ_MAX 1
 #define FNIC_CQ_MAX (FNIC_WQ_COPY_MAX + FNIC_WQ_MAX + FNIC_RQ_MAX)
 
+enum fnic_link_state {
+	FNIC_LINK_DOWN = 0,
+	FNIC_LINK_UP
+};
+
 /* Per-instance private data structure */
 struct fnic {
 	struct fc_lport *lport;
@@ -146,6 +161,8 @@ struct fnic {
 
 	struct fc_frame *flogi;
 	struct fc_frame *flogi_resp;
+	enum fnic_link_state link_state;
+	u32  link_down_cnt;
 	u16 flogi_oxid;
 	unsigned long s_id;
 	enum fnic_state state;
diff --git a/drivers/scsi/fnic/fnic_attrs.c b/drivers/scsi/fnic/fnic_attrs.c
index dd2d786..f867a06 100644
--- a/drivers/scsi/fnic/fnic_attrs.c
+++ b/drivers/scsi/fnic/fnic_attrs.c
@@ -38,7 +38,7 @@
 static ssize_t fnic_show_state(struct class_device *cdev, char *buf)
 {
 	struct fc_lport *lp = shost_priv(class_to_shost(cdev));
-	struct fnic *fnic = lp->drv_priv;
+	struct fnic *fnic = lport_priv(lp);
 
 	return snprintf(buf, PAGE_SIZE, "%s\n", fnic_state_str[fnic->state]);
 }
diff --git a/drivers/scsi/fnic/fnic_fcs.c b/drivers/scsi/fnic/fnic_fcs.c
index 05e8a23..7212e56 100644
--- a/drivers/scsi/fnic/fnic_fcs.c
+++ b/drivers/scsi/fnic/fnic_fcs.c
@@ -29,13 +29,21 @@
 #include <linux/if_vlan.h>
 #include <scsi/fc/fc_els.h>
 #include <scsi/fc/fc_fcoe.h>
-#include <scsi/libfc/fc_frame.h>
-#include <scsi/libfc/libfc.h>
+#include <scsi/fc_frame.h>
+#include <scsi/libfc.h>
 #include "fnic_io.h"
 #include "fnic.h"
 #include "cq_enet_desc.h"
 #include "cq_exch_desc.h"
 
+static int fnic_fcs_debug;
+
+#define FNIC_DEBUG_FCS(fmt...)			\
+	do {					\
+		if (fnic_fcs_debug)		\
+			FNIC_DBG(fmt);		\
+	} while (0)
+
 struct task_struct *fnic_thread;
 LIST_HEAD(fnic_eventlist);
 spinlock_t	fnic_eventlist_lock;
@@ -160,8 +168,9 @@ static inline int fnic_handle_flogi_resp(struct fnic *fnic,
 		 * issued by libFC might have changed the fnic cached oxid
 		 */
 		if (fnic->flogi_oxid != ntohs(fh->fh_ox_id)) {
-			printk(KERN_DEBUG PFX "Flogi response oxid not"
-			       " matching cached oxid, dropping frame\n");
+			FNIC_DEBUG_FCS(DFX "Flogi response oxid not"
+				       " matching cached oxid, dropping frame"
+				       "\n", fnic->fnic_no);
 			ret = -1;
 			spin_unlock_irqrestore(&fnic->fnic_lock, flags);
 			fnic_fc_frame_free_irq(fp);
@@ -202,9 +211,9 @@ static inline int fnic_handle_flogi_resp(struct fnic *fnic,
 		fnic->state = FNIC_IN_ETH_TRANS_FC_MODE;
 
 	} else {
-		printk(KERN_DEBUG PFX "Unexpected fnic state %s while"
-		       " processing flogi resp\n",
-		       fnic_state_str[fnic->state]);
+		FNIC_DEBUG_FCS(PFX "Unexpected fnic state %s while"
+			       " processing flogi resp\n",
+			       fnic_state_str[fnic->state]);
 		ret = -1;
 		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
 		fnic_fc_frame_free_irq(fp);
@@ -331,10 +340,11 @@ static void fnic_rq_cmpl_frame_recv(struct vnic_rq *rq, struct cq_desc
 	}
 
 	if (!fcs_ok || packet_error || !fcoe_fc_crc_ok || fcoe_enc_error) {
-		printk(KERN_DEBUG DFX "fnic rq_cmpl fcoe x%x fcsok x%x"
-		       " pkterr x%x fcoe_fc_crc_ok x%x, fcoe_enc_err x%x\n",
-		       fnic->fnic_no, fcoe, fcs_ok, packet_error,
-		       fcoe_fc_crc_ok, fcoe_enc_error);
+		FNIC_DEBUG_FCS(DFX "fnic rq_cmpl fcoe x%x fcsok x%x"
+			       " pkterr x%x fcoe_fc_crc_ok x%x, fcoe_enc_err"
+			       " x%x\n",
+			       fnic->fnic_no, fcoe, fcs_ok, packet_error,
+			       fcoe_fc_crc_ok, fcoe_enc_error);
 		goto drop;
 	}
 
@@ -362,8 +372,8 @@ static void fnic_rq_cmpl_frame_recv(struct vnic_rq *rq, struct cq_desc
 	/* Queue the frame for receive thread */
 	event = kmem_cache_alloc(fnic_ev_cache, GFP_ATOMIC);
 	if (event == NULL) {
-		printk(KERN_DEBUG DFX "Cannot allocate a event, "
-		       "dropping the FCS Rx frame\n", fnic->fnic_no);
+		FNIC_DEBUG_FCS(DFX "Cannot allocate a event, "
+			       "dropping the FCS Rx frame\n", fnic->fnic_no);
 		goto drop;
 	}
 
@@ -437,8 +447,8 @@ int fnic_alloc_rq_frame(struct vnic_rq *rq)
 	len = FC_FRAME_HEADROOM + FC_MAX_FRAME + FC_FRAME_TAILROOM;
 	skb = dev_alloc_skb(len);
 	if (!skb) {
-		printk(KERN_DEBUG DFX "Unable to allocate RQ sk_buff\n",
-		       fnic->fnic_no);
+		FNIC_DEBUG_FCS(DFX "Unable to allocate RQ sk_buff\n",
+			       fnic->fnic_no);
 		return -ENOMEM;
 	}
 	skb_reset_mac_header(skb);
@@ -553,7 +563,7 @@ fnic_send_frame_end:
  */
 int fnic_send(struct fc_lport *lp, struct fc_frame *fp)
 {
-	struct fnic *fnic = lp->drv_priv;
+	struct fnic *fnic = lport_priv(lp);
 	struct fc_frame_header *fh;
 	int ret = 0;
 	enum fnic_state old_state;
diff --git a/drivers/scsi/fnic/fnic_isr.c b/drivers/scsi/fnic/fnic_isr.c
index 4e536f6..c36c162 100644
--- a/drivers/scsi/fnic/fnic_isr.c
+++ b/drivers/scsi/fnic/fnic_isr.c
@@ -24,14 +24,22 @@
 #include <linux/skbuff.h>
 #include <linux/mempool.h>
 #include <linux/interrupt.h>
-#include <scsi/libfc/libfc.h>
-#include <scsi/libfc/fc_frame.h>
+#include <scsi/libfc.h>
+#include <scsi/fc_frame.h>
 #include "vnic_dev.h"
 #include "vnic_intr.h"
 #include "vnic_stats.h"
 #include "fnic_io.h"
 #include "fnic.h"
 
+static int fnic_isr_debug;
+
+#define FNIC_DEBUG_ISR(fmt...)			\
+	do {					\
+		if (fnic_isr_debug)		\
+			FNIC_DBG(fmt);		\
+	} while (0)
+
 static irqreturn_t fnic_isr_legacy(int irq, void *data, struct pt_regs *regs)
 {
 	struct fnic *fnic = (struct fnic *)data;
@@ -267,7 +275,7 @@ int fnic_set_intr_mode(struct fnic *fnic)
 			fnic->intr_count = n + m + o + 1;
 			fnic->err_intr_offset = FNIC_MSIX_ERR_NOTIFY;
 
-			printk(KERN_DEBUG PFX "Using MSI-X Interrupts\n");
+			FNIC_DEBUG_ISR(PFX "Using MSI-X Interrupts\n");
 			vnic_dev_set_intr_mode(fnic->vdev,
 					       VNIC_DEV_INTR_MODE_MSIX);
 			return 0;
@@ -293,7 +301,7 @@ int fnic_set_intr_mode(struct fnic *fnic)
 		fnic->intr_count = 1;
 		fnic->err_intr_offset = 0;
 
-		printk(KERN_DEBUG PFX "Using MSI Interrupts\n");
+		FNIC_DEBUG_ISR(PFX "Using MSI Interrupts\n");
 		vnic_dev_set_intr_mode(fnic->vdev, VNIC_DEV_INTR_MODE_MSI);
 
 		return 0;
@@ -318,7 +326,7 @@ int fnic_set_intr_mode(struct fnic *fnic)
 		fnic->cq_count = 3;
 		fnic->intr_count = 3;
 
-		printk(KERN_DEBUG PFX "Using Legacy Interrupts\n");
+		FNIC_DEBUG_ISR(PFX "Using Legacy Interrupts\n");
 		vnic_dev_set_intr_mode(fnic->vdev, VNIC_DEV_INTR_MODE_INTX);
 
 		return 0;
diff --git a/drivers/scsi/fnic/fnic_main.c b/drivers/scsi/fnic/fnic_main.c
index 7912167..7d6b378 100644
--- a/drivers/scsi/fnic/fnic_main.c
+++ b/drivers/scsi/fnic/fnic_main.c
@@ -36,8 +36,8 @@
 #include <scsi/scsi_transport.h>
 #include <scsi/scsi_transport_fc.h>
 #include <scsi/scsi_tcq.h>
-#include <scsi/libfc/libfc.h>
-#include <scsi/libfc/fc_frame.h>
+#include <scsi/libfc.h>
+#include <scsi/fc_frame.h>
 
 #include "vnic_dev.h"
 #include "vnic_intr.h"
@@ -48,6 +48,14 @@
 
 #define PCI_DEVICE_ID_CISCO_FNIC	0x0045
 
+static int fnic_main_debug;
+
+#define FNIC_DEBUG_MAIN(fmt...)			\
+	do {					\
+		if (fnic_main_debug)		\
+			FNIC_DBG(fmt);		\
+	} while (0)
+
 /* timer to poll notification area for events. Used in case of MSI
  * interrupts being used by the device
  */
@@ -76,13 +84,15 @@ MODULE_DEVICE_TABLE(pci, fnic_id_table);
 /* Exported to LibFC layer */
 static struct libfc_function_template fnic_transport_template = {
 	.frame_send = fnic_send,
-	.scsi_abort_io = fnic_scsi_abort_io,
-	.scsi_cleanup = fnic_scsi_cleanup
+	.fcp_abort_io = fnic_scsi_abort_io,
+	.fcp_cleanup = fnic_scsi_cleanup
 };
 
 static int fnic_slave_alloc(struct scsi_device *sdev)
 {
 	struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
+	struct fc_lport *lp = shost_priv(sdev->host);
+	struct fnic *fnic = lport_priv(lp);
 
 	scsi_set_tagged_support(sdev);
 
@@ -93,6 +103,7 @@ static int fnic_slave_alloc(struct scsi_device *sdev)
 		scsi_activate_tcq(sdev, FNIC_DFLT_QUEUE_DEPTH);
 	else
 		scsi_deactivate_tcq(sdev, FNIC_DFLT_QUEUE_DEPTH);
+	rport->dev_loss_tmo = fnic->config.port_down_timeout / 1000;
 
 	return 0;
 }
@@ -127,21 +138,22 @@ static struct fc_function_template fnic_fc_functions = {
 	.show_host_supported_classes = 1,
 	.show_host_supported_fc4s = 1,
 	.show_host_active_fc4s = 1,
-
+	.show_host_maxframe_size = 1,
 	.show_host_port_id = 1,
+	.show_host_supported_speeds = 1,
 	.get_host_speed = fnic_get_host_speed,
 	.show_host_speed = 1,
 	.show_host_port_type = 1,
+	.get_host_port_state = fc_get_host_port_state,
+	.show_host_port_state = 1,
 	.show_host_symbolic_name = 1,
-
 	.show_rport_maxframe_size = 1,
 	.show_rport_supported_classes = 1,
-
 	.show_host_fabric_name = 1,
 	.show_starget_node_name = 1,
 	.show_starget_port_name = 1,
 	.show_starget_port_id = 1,
-
+	.show_rport_dev_loss_tmo = 1,
 	.issue_fc_host_lip = fnic_reset,
 	.get_fc_host_stats = fnic_get_stats,
 	.dd_fcrport_size = sizeof(struct fc_rport_libfc_priv),
@@ -157,7 +169,7 @@ const char *fnic_state_str[] = {
 static void fnic_get_host_speed(struct Scsi_Host *shost)
 {
 	struct fc_lport *lp = shost_priv(shost);
-	struct fnic *fnic = lp->drv_priv;
+	struct fnic *fnic = lport_priv(lp);
 	u32 port_speed = vnic_dev_port_speed(fnic->vdev);
 
 	/* Add in other values as they get defined in fw */
@@ -175,7 +187,7 @@ static struct fc_host_statistics *fnic_get_stats(struct Scsi_Host *host)
 {
 	int ret;
 	struct fc_lport *lp = shost_priv(host);
-	struct fnic *fnic = lp->drv_priv;
+	struct fnic *fnic = lport_priv(lp);
 	struct fc_host_statistics *stats = &lp->host_stats;
 	struct vnic_stats *vs;
 	unsigned long flags;
@@ -189,8 +201,8 @@ static struct fc_host_statistics *fnic_get_stats(struct Scsi_Host *host)
 	spin_unlock_irqrestore(&fnic->fnic_lock, flags);
 
 	if (ret) {
-		printk(KERN_DEBUG DFX "fnic: Get vnic stats failed"
-		       " 0x%x", fnic->fnic_no, ret);
+		FNIC_DEBUG_MAIN(DFX "fnic: Get vnic stats failed"
+				" 0x%x", fnic->fnic_no, ret);
 		return stats;
 	}
 	vs = fnic->stats;
@@ -238,36 +250,90 @@ void fnic_log_q_error(struct fnic *fnic)
 static void fnic_handle_link_event(struct fnic *fnic)
 {
 	int link_status = vnic_dev_link_status(fnic->vdev);
+	u32 link_down_cnt = vnic_dev_link_down_cnt(fnic->vdev);
 	struct fnic_event *event;
-	u8 list_was_empty;
+	u8 list_was_empty = 0;
 	unsigned long flags;
+	u8 send_link_up = 0;
+	u8 send_link_down = 0;
 
-	printk(KERN_DEBUG DFX "link %s\n", fnic->fnic_no,
-	       (link_status ? "up" : "down"));
+	FNIC_DEBUG_MAIN(DFX "link %s\n", fnic->fnic_no,
+			(link_status ? "up" : "down"));
 
-	if (fnic->in_remove)
+	spin_lock_irqsave(&fnic->fnic_lock, flags);
+	if (fnic->in_remove) {
+		fnic->link_down_cnt = link_down_cnt;
+		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
 		return;
+	}
 
-	event = kmem_cache_alloc(fnic_ev_cache, GFP_ATOMIC);
-	if (!event) {
-		printk(KERN_DEBUG DFX "Cannot allocate a event, "
-		       "cannot indicate link down to FCS\n", fnic->fnic_no);
-		return;
+	/*
+	 * If link was down, and down event, do nothing
+	 * If link was down, and up event, send up event
+	 * if link was up, and down event, send down event
+	 * If link was up, and up event, and link down cnt matches, do nothing
+	 * If link was up, and up event, and link down cnt does not match,
+	 * send down followed by up
+	 */
+	if (fnic->link_state == FNIC_LINK_DOWN) {
+		if (!link_status) {
+			fnic->link_down_cnt = link_down_cnt;
+			spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+			return;
+		} else
+			send_link_up = 1;
+	} else {
+		if (!link_status) {
+			send_link_down = 1;
+		} else if (link_down_cnt != fnic->link_down_cnt) {
+			send_link_down = 1;
+			send_link_up = 1;
+		} else {
+			fnic->link_down_cnt = link_down_cnt;
+			spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+			return;
+		}
 	}
+	fnic->link_down_cnt = link_down_cnt;
+	spin_unlock_irqrestore(&fnic->fnic_lock, flags);
 
-	/* Pass the event to thread */
-	memset(event, 0, sizeof(struct fnic_event));
-	event->fnic = fnic;
-	event->ev_type = EV_TYPE_LINK_UP;
-	if (!link_status) {
+	if (send_link_down) {
+		event = kmem_cache_alloc(fnic_ev_cache, GFP_ATOMIC);
+		if (!event) {
+			FNIC_DEBUG_MAIN(DFX "Cannot allocate a event, "
+					"cannot indicate link down to FCS\n",
+					fnic->fnic_no);
+			return;
+		}
+		memset(event, 0, sizeof(struct fnic_event));
+		event->fnic = fnic;
 		event->ev_type = EV_TYPE_LINK_DOWN;
 		fnic->lport->host_stats.link_failure_count++;
+
+		spin_lock_irqsave(&fnic_eventlist_lock, flags);
+		list_was_empty = list_empty(&fnic_eventlist);
+		list_add_tail(&event->list, &fnic_eventlist);
+		spin_unlock_irqrestore(&fnic_eventlist_lock, flags);
+	}
+
+	if (send_link_up) {
+		event = kmem_cache_alloc(fnic_ev_cache, GFP_ATOMIC);
+		if (!event) {
+			FNIC_DEBUG_MAIN(DFX "Cannot allocate a event, "
+					"cannot indicate link up to FCS\n",
+					fnic->fnic_no);
+			return;
+		}
+		memset(event, 0, sizeof(struct fnic_event));
+		event->fnic = fnic;
+		event->ev_type = EV_TYPE_LINK_UP;
+
+		spin_lock_irqsave(&fnic_eventlist_lock, flags);
+		list_was_empty |= list_empty(&fnic_eventlist);
+		list_add_tail(&event->list, &fnic_eventlist);
+		spin_unlock_irqrestore(&fnic_eventlist_lock, flags);
 	}
 
-	spin_lock_irqsave(&fnic_eventlist_lock, flags);
-	list_was_empty = list_empty(&fnic_eventlist);
-	list_add_tail(&event->list, &fnic_eventlist);
-	spin_unlock_irqrestore(&fnic_eventlist_lock, flags);
 	if (list_was_empty)
 		wake_up_process(fnic_thread);
 
@@ -298,7 +364,6 @@ static int fnic_notify_set(struct fnic *fnic)
 		       " before devcmd notify set %d\n", fnic->fnic_no,
 		       vnic_dev_get_intr_mode(fnic->vdev));
 		err = -1;
-		BUG();
 		break;
 	}
 
@@ -334,7 +399,6 @@ static int fnic_dev_wait(struct vnic_dev *vdev,
 	int done;
 	int err;
 
-	BUG_ON(in_interrupt());
 	err = start(vdev, arg);
 	if (err)
 		return err;
@@ -481,8 +545,7 @@ static int __devinit fnic_probe(struct pci_dev *pdev,
 	}
 	lp = shost_priv(host);
 	lp->host = host;
-	lp->drv_priv = (void *)(lp + 1);
-	fnic = lp->drv_priv;
+	fnic = lport_priv(lp);
 	fnic->lport = lp;
 
 	/* fnic number starts from 0 onwards */
@@ -669,6 +732,8 @@ static int __devinit fnic_probe(struct pci_dev *pdev,
 	fnic->flogi = NULL;
 	fnic->flogi_resp = NULL;
 	fnic->state = FNIC_IN_FC_MODE;
+	fnic->link_state = FNIC_LINK_DOWN;
+	fnic->link_down_cnt = 0;
 
 	/* Enable hardware stripping of vlan header on ingress */
 	fnic_set_nic_cfg(fnic, 0, 0, 0, 0, 0, 0, 1);
@@ -686,7 +751,7 @@ static int __devinit fnic_probe(struct pci_dev *pdev,
 	setup_timer(&fnic->notify_timer,
 		    fnic_notify_timer, (unsigned long)fnic);
 
-	printk(KERN_DEBUG DFX "host no %d\n", fnic->fnic_no, host->host_no);
+	FNIC_DEBUG_MAIN(DFX "host no %d\n", fnic->fnic_no, host->host_no);
 
 	/* allocate RQ buffers and post them to RQ*/
 	for (i = 0; i < fnic->rq_count; i++) {
@@ -730,11 +795,13 @@ static int __devinit fnic_probe(struct pci_dev *pdev,
 	lp->boot_time = jiffies;
 	lp->e_d_tov = fnic->config.ed_tov;
 	lp->r_a_tov = fnic->config.ra_tov;
+	lp->link_supported_speeds = FC_PORTSPEED_10GBIT;
 	fc_set_wwnn(lp, fnic->config.node_wwn);
 	fc_set_wwpn(lp, fnic->config.port_wwn);
 
 	fc_exch_init(lp);
 	fc_lport_init(lp);
+	fc_elsct_init(lp);
 	fc_rport_init(lp);
 	fc_disc_init(lp);
 
@@ -745,6 +812,7 @@ static int __devinit fnic_probe(struct pci_dev *pdev,
 		err = -EINVAL;
 		goto err_out_free_exch_mgr;
 	}
+	fc_host_maxframe_size(lp->host) = lp->mfs;
 
 	sprintf(fc_host_symbolic_name(lp->host),
 		DRV_NAME " v" DRV_VERSION " over %s", fnic->name);
@@ -811,10 +879,10 @@ static void __devexit fnic_remove(struct pci_dev *pdev)
 	struct fnic *fnic = pci_get_drvdata(pdev);
 
 	fc_lport_destroy(fnic->lport);
-	fc_exch_mgr_free(fnic->lport->emp);
 	fnic_cleanup(fnic);
 	fc_remove_host(fnic->lport->host);
 	scsi_remove_host(fnic->lport->host);
+	fc_exch_mgr_free(fnic->lport->emp);
 	vnic_dev_notify_unset(fnic->vdev);
 	fnic_free_vnic_resources(fnic);
 	fnic_free_intr(fnic);
diff --git a/drivers/scsi/fnic/fnic_scsi.c b/drivers/scsi/fnic/fnic_scsi.c
index 6f60060..e634a57 100644
--- a/drivers/scsi/fnic/fnic_scsi.c
+++ b/drivers/scsi/fnic/fnic_scsi.c
@@ -39,12 +39,20 @@
 #include <scsi/scsi_tcq.h>
 #include <scsi/fc/fc_els.h>
 #include <scsi/fc/fc_fcoe.h>
-#include <scsi/libfc/libfc.h>
-#include <scsi/libfc/fc_frame.h>
+#include <scsi/libfc.h>
+#include <scsi/fc_frame.h>
 #include "fnic_io.h"
 #include "fnic.h"
 #include "fnic_tag_map.h"
 
+static int fnic_scsi_debug;
+
+#define FNIC_DEBUG_SCSI(fmt...)			\
+	do {					\
+		if (fnic_scsi_debug)		\
+			FNIC_DBG(fmt);		\
+	} while (0)
+
 static const char *fnic_ioreq_state_str[] = {
 	[FNIC_IOREQ_CMD_PENDING] = "FNIC_IOREQ_CMD_PENDING",
 	[FNIC_IOREQ_ABTS_PENDING] = "FNIC_IOREQ_ABTS_PENDING",
@@ -159,10 +167,10 @@ int fnic_fw_reset_handler(struct fnic *fnic)
 	spin_unlock_irqrestore(&fnic->wq_copy_lock[0], flags);
 
 	if (!ret)
-		printk(KERN_DEBUG DFX "Issued fw reset\n", fnic->fnic_no);
+		FNIC_DEBUG_SCSI(DFX "Issued fw reset\n", fnic->fnic_no);
 	else
-		printk(KERN_DEBUG DFX "Failed to issue fw reset\n",
-		       fnic->fnic_no);
+		FNIC_DEBUG_SCSI(DFX "Failed to issue fw reset\n",
+				fnic->fnic_no);
 	return ret;
 }
 
@@ -205,7 +213,7 @@ flogi_reg_ioreq_end:
 	spin_unlock_irqrestore(&fnic->wq_copy_lock[0], flags);
 
 	if (!ret)
-		printk(KERN_DEBUG DFX "flog reg issued\n", fnic->fnic_no);
+		FNIC_DEBUG_SCSI(DFX "flog reg issued\n", fnic->fnic_no);
 
 	return ret;
 }
@@ -364,7 +372,7 @@ int fnic_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
 	}
 
 	/* Get a new io_req for this SCSI IO */
-	fnic = lp->drv_priv;
+	fnic = lport_priv(lp);
 
 	io_req = mempool_alloc(fnic->io_req_pool, GFP_ATOMIC);
 	if (!io_req) {
@@ -472,13 +480,14 @@ static int fnic_fcpio_fw_reset_cmpl_handler(struct fnic *fnic,
 	if (fnic->state == FNIC_IN_FC_TRANS_ETH_MODE) {
 		/* Check status of reset completion */
 		if (!hdr_status) {
-			printk(KERN_DEBUG DFX "reset cmpl success\n",
-			       fnic->fnic_no);
+			FNIC_DEBUG_SCSI(DFX "reset cmpl success\n",
+					fnic->fnic_no);
 			/* Ready to send flogi out */
 			fnic->state = FNIC_IN_ETH_MODE;
 		} else {
-			printk(KERN_DEBUG DFX "fnic fw_reset : failed %s\n",
-			       fnic->fnic_no, fcpio_status_str[hdr_status]);
+			FNIC_DEBUG_SCSI(DFX "fnic fw_reset : failed %s\n",
+					fnic->fnic_no,
+					fcpio_status_str[hdr_status]);
 
 			/* Unable to change to eth mode, cannot send out flogi
 			 * Change state to fc mode, so that subsequent Flogi
@@ -489,8 +498,9 @@ static int fnic_fcpio_fw_reset_cmpl_handler(struct fnic *fnic,
 			ret = -1;
 		}
 	} else {
-		printk(KERN_DEBUG PFX "Unexpected state %s while processing"
-		       " reset cmpl\n", fnic_state_str[fnic->state]);
+		FNIC_DEBUG_SCSI(DFX "Unexpected state %s while processing"
+				" reset cmpl\n", fnic->fnic_no,
+				fnic_state_str[fnic->state]);
 		ret = -1;
 	}
 
@@ -550,19 +560,20 @@ static int fnic_fcpio_flogi_reg_cmpl_handler(struct fnic *fnic,
 
 		/* Check flogi registration completion status */
 		if (!hdr_status) {
-			printk(KERN_DEBUG DFX "flog reg succeeded\n",
-			       fnic->fnic_no);
+			FNIC_DEBUG_SCSI(DFX "flog reg succeeded\n",
+					fnic->fnic_no);
 			fnic->state = FNIC_IN_FC_MODE;
 		} else {
-			printk(KERN_DEBUG DFX "fnic flogi reg :failed %s\n",
-			       fnic->fnic_no, fcpio_status_str[hdr_status]);
+			FNIC_DEBUG_SCSI(DFX "fnic flogi reg :failed %s\n",
+					fnic->fnic_no,
+					fcpio_status_str[hdr_status]);
 			fnic->state = FNIC_IN_ETH_MODE;
 			ret = -1;
 		}
 	} else {
-		printk(KERN_DEBUG PFX "Unexpected fnic state %s while"
-		       " processing flogi reg completion\n",
-		       fnic_state_str[fnic->state]);
+		FNIC_DEBUG_SCSI(DFX "Unexpected fnic state %s while"
+				" processing flogi reg completion\n",
+				fnic->fnic_no, fnic_state_str[fnic->state]);
 		ret = -1;
 	}
 
@@ -854,9 +865,9 @@ static void fnic_fcpio_itmf_cmpl_handler(struct fnic *fnic,
 		CMD_STATE(sc) = FNIC_IOREQ_ABTS_COMPLETE;
 		CMD_ABTS_STATUS(sc) = hdr_status;
 
-		printk(KERN_DEBUG DFX "abort complete. id %d status %s\n",
-		       fnic->fnic_no, (int)(id & FNIC_TAG_MASK),
-		       fcpio_status_str[hdr_status]);
+		FNIC_DEBUG_SCSI(DFX "abts cmpl recd. id %d status %s\n",
+				fnic->fnic_no, (int)(id & FNIC_TAG_MASK),
+				fcpio_status_str[hdr_status]);
 		if (io_req->abts_done)
 			complete(io_req->abts_done);
 
@@ -864,16 +875,16 @@ static void fnic_fcpio_itmf_cmpl_handler(struct fnic *fnic,
 		/* Completion of device reset */
 		CMD_LR_STATUS(sc) = hdr_status;
 		CMD_STATE(sc) = FNIC_IOREQ_CMD_COMPLETE;
-		printk(KERN_DEBUG DFX "dev reset complete. id %d status %s\n",
-		       fnic->fnic_no, (int)(id & FNIC_TAG_MASK),
-		       fcpio_status_str[hdr_status]);
+		FNIC_DEBUG_SCSI(DFX "dev reset cmpl recd. id %d status %s\n",
+				fnic->fnic_no, (int)(id & FNIC_TAG_MASK),
+				fcpio_status_str[hdr_status]);
 		if (io_req->dr_done)
 			complete(io_req->dr_done);
 
 	} else
 		printk(KERN_ERR DFX "Unexpected itmf io state %s tag %x\n",
 		       fnic->fnic_no, fnic_ioreq_state_str[CMD_STATE(sc)], id);
-
+	
 	spin_unlock_irqrestore(io_lock, flags);
 }
 
@@ -910,9 +921,8 @@ static int fnic_fcpio_cmpl_handler(struct vnic_dev *vdev,
 		break;
 
 	default:
-		printk(KERN_DEBUG DFX "firmware completion type %d\n",
-		       fnic->fnic_no, desc->hdr.type);
-		BUG();
+		FNIC_DEBUG_SCSI(DFX "firmware completion type %d\n",
+				fnic->fnic_no, desc->hdr.type);
 		break;
 	}
 
@@ -976,9 +986,9 @@ static void fnic_cleanup_io(struct fnic *fnic, int exclude_id)
 
 cleanup_scsi_cmd:
 		sc->result = DID_TRANSPORT_DISRUPTED << 16;
-		printk(KERN_DEBUG DFX "fnic_cleanup_io:"
-		       " DID_TRANSPORT_DISRUPTED\n", fnic->fnic_no);
-
+		FNIC_DEBUG_SCSI(DFX "fnic_cleanup_io:"
+				" DID_TRANSPORT_DISRUPTED\n", fnic->fnic_no);
+		
 		/* Complete the command to SCSI */
 		if (sc->scsi_done)
 			sc->scsi_done(sc);
@@ -1029,8 +1039,8 @@ void fnic_wq_copy_cleanup_handler(struct vnic_wq_copy *wq,
 
 wq_copy_cleanup_scsi_cmd:
 	sc->result = DID_NO_CONNECT << 16;
-	printk(KERN_DEBUG DFX "wq_copy_cleanup_handler:"
-	       " did_no_connect\n", fnic->fnic_no);
+	FNIC_DEBUG_SCSI(DFX "wq_copy_cleanup_handler:"
+			" DID_NO_CONNECT\n", fnic->fnic_no);
 
 	if (sc->scsi_done)
 		sc->scsi_done(sc);
@@ -1110,8 +1120,11 @@ int fnic_abort_cmd(struct scsi_cmnd *sc)
 	/* Get local-port, check ready and link up */
 	lp = shost_priv(sc->device->host);
 
-	fnic = lp->drv_priv;
-	printk(KERN_DEBUG DFX "fnic_abort_cmd called\n", fnic->fnic_no);
+	fnic = lport_priv(lp);
+	FNIC_DEBUG_SCSI(DFX "Abort Cmd called FCID 0x%x, LUN 0x%x TAG %d\n",
+			fnic->fnic_no,
+			(starget_to_rport(scsi_target(sc->device)))->port_id,
+			sc->device->lun, scsi_cmd_get_tag(sc));
 
 	if (lp->state != LPORT_ST_READY || !(lp->link_status & FC_LINK_UP)) {
 		ret = FAILED;
@@ -1139,9 +1152,6 @@ int fnic_abort_cmd(struct scsi_cmnd *sc)
 		goto fnic_abort_cmd_end;
 	}
 
-	/* The command has to be in pending state */
-	BUG_ON(CMD_STATE(sc) != FNIC_IOREQ_CMD_PENDING);
-
 	/* Command is still pending, need to abort it
 	 * If the firmware completes the command after this point,
 	 * the completion wont be done till mid-layer, since abort
@@ -1219,8 +1229,9 @@ int fnic_abort_cmd(struct scsi_cmnd *sc)
 	mempool_free(io_req, fnic->io_req_pool);
 
 fnic_abort_cmd_end:
-	printk(KERN_DEBUG DFX "Returning from abort cmd %s\n",
-	       fnic->fnic_no, (ret == SUCCESS) ? "SUCCESS" : "FAILED");
+	FNIC_DEBUG_SCSI(DFX "Returning from abort cmd %s\n",
+			fnic->fnic_no, (ret == SUCCESS) ?
+			"SUCCESS" : "FAILED");
 	return ret;
 }
 
@@ -1247,9 +1258,6 @@ static inline int fnic_queue_dr_io_req(struct fnic *fnic,
 	int_to_scsilun(sc->device->lun, &fc_lun);
 	rport = starget_to_rport(scsi_target(sc->device));
 
-	printk(KERN_DEBUG DFX "FCID 0x%x LunID %d\n", fnic->fnic_no,
-	       rport->port_id, sc->device->lun);
-
 	fnic_queue_wq_copy_desc_itmf(
 		wq,
 		scsi_cmd_get_tag(sc) | FNIC_TAG_DEV_RST, /* host request id */
@@ -1309,8 +1317,9 @@ static int fnic_clean_pending_aborts(struct fnic *fnic,
 		/* Found IO that is still pending with firmware and
 		 * belongs to the LUN that we are resetting
 		 */
-		printk(KERN_DEBUG DFX "Found IO in %s on lun\n",
-		       fnic->fnic_no, fnic_ioreq_state_str[CMD_STATE(sc)]);
+		FNIC_DEBUG_SCSI(DFX "Found IO in %s on lun\n",
+				fnic->fnic_no,
+				fnic_ioreq_state_str[CMD_STATE(sc)]);
 
 		io_req->abts_done = &tm_done;
 		spin_unlock_irqrestore(io_lock, flags);
@@ -1387,8 +1396,12 @@ int fnic_device_reset(struct scsi_cmnd *sc)
 	/* Get local-port, check ready and link up */
 	lp = shost_priv(sc->device->host);
 
-	fnic = lp->drv_priv;
-	printk(KERN_DEBUG DFX "Device reset called\n", fnic->fnic_no);
+	fnic = lport_priv(lp);
+	FNIC_DEBUG_SCSI(DFX "Device reset called FCID 0x%x, LUN 0x%x\n",
+			fnic->fnic_no,
+			(starget_to_rport(scsi_target(sc->device)))->port_id,
+			sc->device->lun);
+
 
 	if (lp->state != LPORT_ST_READY || !(lp->link_status & FC_LINK_UP))
 		goto fnic_device_reset_end;
@@ -1400,8 +1413,6 @@ int fnic_device_reset(struct scsi_cmnd *sc)
 
 	/* Allocate tag if not present */
 	if (scsi_cmd_get_tag(sc) == SCSI_NO_TAG) {
-		printk(KERN_DEBUG DFX "Device reset: sc has no tag,"
-		       " allocating one\n", fnic->fnic_no);
 		tag = scsi_host_start_tag(lp->host, sc);
 		if (tag == SCSI_NO_TAG)
 			goto fnic_device_reset_end;
@@ -1429,6 +1440,8 @@ int fnic_device_reset(struct scsi_cmnd *sc)
 	CMD_LR_STATUS(sc) = FCPIO_INVALID_CODE;
 	spin_unlock_irqrestore(io_lock, flags);
 
+	FNIC_DEBUG_SCSI(DFX "TAG %d\n", fnic->fnic_no, scsi_cmd_get_tag(sc));
+
 	/* issue the device reset
 	 * if enqueue failed, let io_req remain allocated and attached
 	 * to SCSI cmd, it will get freed during higher levels of EH
@@ -1464,15 +1477,15 @@ int fnic_device_reset(struct scsi_cmnd *sc)
 	 * gets cleaned up during higher levels of EH
 	 */
 	if (status == FCPIO_INVALID_CODE) {
-		printk(KERN_DEBUG DFX "Device reset timed out\n",
-		       fnic->fnic_no);
+		FNIC_DEBUG_SCSI(DFX "Device reset timed out\n",
+				fnic->fnic_no);
 		goto fnic_device_reset_end;
 	}
 
 	/* Completed, but not successful */
 	if (status != FCPIO_SUCCESS) {
-		printk(KERN_DEBUG DFX "Device reset completed - failed\n",
-		       fnic->fnic_no);
+		FNIC_DEBUG_SCSI(DFX "Device reset completed - failed\n",
+				fnic->fnic_no);
 		goto fnic_device_reset_end;
 	}
 
@@ -1484,9 +1497,9 @@ int fnic_device_reset(struct scsi_cmnd *sc)
 	 * of EH will kick in
 	 */
 	if (fnic_clean_pending_aborts(fnic, sc)) {
-		printk(KERN_DEBUG DFX "Device reset failed"
-		       " since could not abort all IOs\n",
-		       fnic->fnic_no);
+		FNIC_DEBUG_SCSI(DFX "Device reset failed"
+				" since could not abort all IOs\n",
+				fnic->fnic_no);
 		goto fnic_device_reset_end;
 	}
 
@@ -1507,8 +1520,9 @@ int fnic_device_reset(struct scsi_cmnd *sc)
 	ret = SUCCESS;
 
 fnic_device_reset_end:
-	printk(KERN_DEBUG DFX "Returning from device reset %s\n",
-	       fnic->fnic_no, (ret == SUCCESS) ? "SUCCESS" : "FAILED");
+	FNIC_DEBUG_SCSI(DFX "Returning from device reset %s\n",
+			fnic->fnic_no, (ret == SUCCESS) ?
+			"SUCCESS" : "FAILED");
 	return ret;
 }
 
@@ -1517,53 +1531,22 @@ int fnic_reset(struct Scsi_Host *shost)
 {
 	struct fc_lport *lp;
 	struct fnic *fnic;
-	unsigned long flags;
 	int ret = SUCCESS;
-	enum fnic_state old_state;
-	DECLARE_COMPLETION_ONSTACK(reset_wait);
 
 	lp = shost_priv(shost);
-	fnic = lp->drv_priv;
-
-	printk(KERN_DEBUG DFX "fnic_reset called\n", fnic->fnic_no);
-
-	/* Issue firmware reset */
-	spin_lock_irqsave(&fnic->fnic_lock, flags);
-	fnic->reset_wait = &reset_wait;
-	old_state = fnic->state;
-	fnic->state = FNIC_IN_FC_TRANS_ETH_MODE;
-	vnic_dev_del_addr(fnic->vdev, fnic->data_src_addr);
-	spin_unlock_irqrestore(&fnic->fnic_lock, flags);
-
-	if (fnic_fw_reset_handler(fnic)) {
-		spin_lock_irqsave(&fnic->fnic_lock, flags);
-		ret = FAILED;
-		if (fnic->state == FNIC_IN_FC_TRANS_ETH_MODE)
-			fnic->state = old_state;
-		fnic->reset_wait = NULL;
-		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
-		goto fnic_reset_end;
-	}
+	fnic = lport_priv(lp);
 
-	/* fw reset is issued, now wait for it to complete */
-	wait_for_completion_timeout(&reset_wait,
-				    msecs_to_jiffies(FNIC_HOST_RESET_TIMEOUT));
+	FNIC_DEBUG_SCSI(DFX "fnic_reset called\n", fnic->fnic_no);
 
-	/* Check for status */
-	spin_lock_irqsave(&fnic->fnic_lock, flags);
-	fnic->reset_wait = NULL;
-	ret = (fnic->state == FNIC_IN_ETH_MODE) ? SUCCESS : FAILED;
-	spin_unlock_irqrestore(&fnic->fnic_lock, flags);
-
-	/* Now reset local port, this will clean up libFC exchanges,
+	/* Reset local port, this will clean up libFC exchanges,
 	 * reset remote port sessions, and if link is up, begin flogi
 	 */
 	if (lp->tt.lport_reset(lp))
 		ret = FAILED;
 
-fnic_reset_end:
-	printk(KERN_DEBUG DFX "Returning from fnic reset %s\n",
-	       fnic->fnic_no, (ret == SUCCESS) ? "SUCCESS" : "FAILED");
+	FNIC_DEBUG_SCSI(DFX "Returning from fnic reset %s\n",
+			fnic->fnic_no, (ret == SUCCESS) ?
+			"SUCCESS" : "FAILED");
 
 	return ret;
 }
@@ -1579,15 +1562,29 @@ fnic_reset_end:
 int fnic_host_reset(struct scsi_cmnd *sc)
 {
 	int ret;
+	unsigned long wait_host_tmo;
+	struct Scsi_Host *shost = sc->device->host;
+	struct fc_lport *lp = shost_priv(shost);
 
 	/*
 	 * If fnic_reset is successful, wait for fabric login to complete
 	 * scsi-ml tries to send a TUR to every device if host reset is
 	 * successful, so before returning to scsi, fabric should be up
 	 */
-	ret = fnic_reset(sc->device->host);
-	if (ret == SUCCESS)
-		ssleep(FNIC_HOST_RESET_SETTLE_TIME);
+	ret = fnic_reset(shost);
+
+	if (ret == SUCCESS) {
+		wait_host_tmo = jiffies + FNIC_HOST_RESET_SETTLE_TIME * HZ;
+		ret = FAILED;
+		while (time_before(jiffies, wait_host_tmo)) {
+			if ((lp->state == LPORT_ST_READY) &&
+			    (lp->link_status & FC_LINK_UP)) {
+				ret = SUCCESS;
+				break;
+			}
+			ssleep(1);
+		}
+	}
 
 	return ret;
 }
@@ -1600,7 +1597,7 @@ void fnic_scsi_abort_io(struct fc_lport *lp)
 	int err = 0;
 	unsigned long flags;
 	enum fnic_state old_state;
-	struct fnic *fnic = lp->drv_priv;
+	struct fnic *fnic = lport_priv(lp);
 	DECLARE_COMPLETION_ONSTACK(remove_wait);
 
 	/* Issue firmware reset for fnic, wait for reset to complete */
@@ -1628,8 +1625,9 @@ void fnic_scsi_abort_io(struct fc_lport *lp)
 
 	spin_lock_irqsave(&fnic->fnic_lock, flags);
 	fnic->remove_wait = NULL;
-	printk(KERN_DEBUG DFX "fnic_scsi_abort_io %s\n", fnic->fnic_no,
-	       (fnic->state == FNIC_IN_ETH_MODE) ? "SUCCESS" : "FAILED");
+	FNIC_DEBUG_SCSI(DFX "fnic_scsi_abort_io %s\n", fnic->fnic_no,
+			(fnic->state == FNIC_IN_ETH_MODE) ?
+			"SUCCESS" : "FAILED");
 	spin_unlock_irqrestore(&fnic->fnic_lock, flags);
 
 abort_io_end:
@@ -1643,7 +1641,7 @@ void fnic_scsi_cleanup(struct fc_lport *lp)
 {
 	unsigned long flags;
 	enum fnic_state old_state;
-	struct fnic *fnic = lp->drv_priv;
+	struct fnic *fnic = lport_priv(lp);
 
 	/* issue fw reset */
 	spin_lock_irqsave(&fnic->fnic_lock, flags);
diff --git a/drivers/scsi/fnic/fnic_tag_map.c b/drivers/scsi/fnic/fnic_tag_map.c
index 33c345e..912e10a 100644
--- a/drivers/scsi/fnic/fnic_tag_map.c
+++ b/drivers/scsi/fnic/fnic_tag_map.c
@@ -3,7 +3,7 @@
  *
  * This file is derived from block/blk-tag.c, scsi/scsi_tcq.h, bitops.h and
  * others from Linux release 2.6.27-rc5, and shares copyright with the
- * original authors.
+ * original authors (Jens Axboe and others).
  *
  * This program is free software; you may redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -50,7 +50,7 @@ int fnic_init_shared_tag_map(struct Scsi_Host *shost, int depth)
 {
 	struct fnic_host_tag *fht;
 	struct fc_lport *lp = shost_priv(shost);
-	struct fnic *fnic = lp->drv_priv;
+	struct fnic *fnic = lport_priv(lp);
 
 	fht = kmalloc(sizeof(struct fnic_host_tag), GFP_ATOMIC);
 	if (!fht)
@@ -86,7 +86,7 @@ static inline void fnic_free_tags(struct fnic_host_tag *fht)
 void fnic_free_shared_tag_map(struct Scsi_Host *shost)
 {
 	struct fc_lport *lp = shost_priv(shost);
-	struct fnic *fnic = lp->drv_priv;
+	struct fnic *fnic = lport_priv(lp);
 
 	fnic_free_tags(fnic->tags);
 }
@@ -95,7 +95,7 @@ int fnic_host_start_tag(struct Scsi_Host *shost, struct scsi_cmnd *sc)
 {
 
 	struct fc_lport *lp = shost_priv(shost);
-	struct fnic *fnic = lp->drv_priv;
+	struct fnic *fnic = lport_priv(lp);
 	struct fnic_host_tag *fht = fnic->tags;
 	int tag;
 
@@ -115,7 +115,7 @@ int fnic_host_start_tag(struct Scsi_Host *shost, struct scsi_cmnd *sc)
 void fnic_host_end_tag(struct Scsi_Host *shost, struct scsi_cmnd *sc)
 {
 	struct fc_lport *lp = shost_priv(shost);
-	struct fnic *fnic = lp->drv_priv;
+	struct fnic *fnic = lport_priv(lp);
 	struct fnic_host_tag *fht = fnic->tags;
 	int tag = CMD_TAG(sc);
 
@@ -151,7 +151,7 @@ static inline struct scsi_cmnd *fnic_find_tag(struct fnic_host_tag *fht,
 struct scsi_cmnd *fnic_host_find_tag(struct Scsi_Host *shost, int tag)
 {
 	struct fc_lport *lp = shost_priv(shost);
-	struct fnic *fnic = lp->drv_priv;
+	struct fnic *fnic = lport_priv(lp);
 
 	if (tag != SCSI_NO_TAG)
 		return fnic_find_tag(fnic->tags, tag);
diff --git a/drivers/scsi/fnic/fnic_tag_map.h b/drivers/scsi/fnic/fnic_tag_map.h
index 2ba0a67..acd938e 100644
--- a/drivers/scsi/fnic/fnic_tag_map.h
+++ b/drivers/scsi/fnic/fnic_tag_map.h
@@ -3,7 +3,7 @@
  *
  * This file is derived from block/blk-tag.c, scsi/scsi_tcq.h, bitops.h and
  * others from Linux release 2.6.27-rc5, and shares copyright with the
- * original authors.
+ * original authors (Jens Axboe and others).
  *
  * This program is free software; you may redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -43,8 +43,8 @@
 #include <scsi/scsi_transport.h>
 #include <scsi/scsi_transport_fc.h>
 #include <scsi/scsi_tcq.h>
-#include <scsi/libfc/libfc.h>
-#include <scsi/libfc/fc_frame.h>
+#include <scsi/libfc.h>
+#include <scsi/fc_frame.h>
 #include "fnic.h"
 
 #define scsi_init_shared_tag_map         fnic_init_shared_tag_map
diff --git a/drivers/scsi/fnic/vnic_dev.c b/drivers/scsi/fnic/vnic_dev.c
index 8f068f6..37cbd13 100644
--- a/drivers/scsi/fnic/vnic_dev.c
+++ b/drivers/scsi/fnic/vnic_dev.c
@@ -618,6 +618,14 @@ u32 vnic_dev_mtu(struct vnic_dev *vdev)
 	return vdev->notify_copy.mtu;
 }
 
+u32 vnic_dev_link_down_cnt(struct vnic_dev *vdev)
+{
+	if (!vnic_dev_notify_ready(vdev))
+		return 0;
+
+	return vdev->notify_copy.link_down_cnt;
+}
+
 void vnic_dev_set_intr_mode(struct vnic_dev *vdev,
 	enum vnic_dev_intr_mode intr_mode)
 {
diff --git a/drivers/scsi/fnic/vnic_dev.h b/drivers/scsi/fnic/vnic_dev.h
index 4d23a15..3ad9ac9 100644
--- a/drivers/scsi/fnic/vnic_dev.h
+++ b/drivers/scsi/fnic/vnic_dev.h
@@ -87,6 +87,7 @@ int vnic_dev_link_status(struct vnic_dev *vdev);
 u32 vnic_dev_port_speed(struct vnic_dev *vdev);
 u32 vnic_dev_msg_lvl(struct vnic_dev *vdev);
 u32 vnic_dev_mtu(struct vnic_dev *vdev);
+u32 vnic_dev_link_down_cnt(struct vnic_dev *vdev);
 int vnic_dev_close(struct vnic_dev *vdev);
 int vnic_dev_enable(struct vnic_dev *vdev);
 int vnic_dev_disable(struct vnic_dev *vdev);
diff --git a/drivers/scsi/fnic/vnic_devcmd.h b/drivers/scsi/fnic/vnic_devcmd.h
index f60003d..d62b906 100644
--- a/drivers/scsi/fnic/vnic_devcmd.h
+++ b/drivers/scsi/fnic/vnic_devcmd.h
@@ -247,6 +247,7 @@ struct vnic_devcmd_notify {
 	u32 uif;		/* uplink interface */
 	u32 status;		/* status bits (see VNIC_STF_*) */
 	u32 error;		/* error code (see ERR_*) for first ERR */
+	u32 link_down_cnt;	/* running count of link down transitions */
 };
 #define VNIC_STF_FATAL_ERR	0x0001	/* fatal fw error */
 
diff --git a/drivers/scsi/fnic/vnic_intr.h b/drivers/scsi/fnic/vnic_intr.h
index 3925f28..b53ef8b 100644
--- a/drivers/scsi/fnic/vnic_intr.h
+++ b/drivers/scsi/fnic/vnic_intr.h
@@ -75,7 +75,7 @@ static inline void vnic_intr_return_credits(struct vnic_intr *intr,
 
 static inline u32 vnic_intr_legacy_pba(u32 __iomem *legacy_pba)
 {
-	/* get and ack interrupt in one read (clear-and-ack-on-read) */
+	/* read PBA without clearing */
 	return ioread32(legacy_pba);
 }
 
diff --git a/drivers/scsi/fnic/vnic_resource.h b/drivers/scsi/fnic/vnic_resource.h
index 8fc27f3..2d842f7 100644
--- a/drivers/scsi/fnic/vnic_resource.h
+++ b/drivers/scsi/fnic/vnic_resource.h
@@ -36,7 +36,7 @@ enum vnic_res_type {
 	RES_TYPE_INTR_CTRL,		/* Interrupt ctrl table */
 	RES_TYPE_INTR_TABLE,		/* MSI/MSI-X Interrupt table */
 	RES_TYPE_INTR_PBA,		/* MSI/MSI-X PBA table */
-	RES_TYPE_INTR_PBA_LEGACY,	/* Legacy intr status, r2c */
+	RES_TYPE_INTR_PBA_LEGACY,	/* Legacy intr status */
 	RES_TYPE_RSVD6,
 	RES_TYPE_RSVD7,
 	RES_TYPE_DEVCMD,		/* Device command region */
diff --git a/drivers/scsi/fnic/vnic_wq_copy.h b/drivers/scsi/fnic/vnic_wq_copy.h
index 6439210..c292932 100644
--- a/drivers/scsi/fnic/vnic_wq_copy.h
+++ b/drivers/scsi/fnic/vnic_wq_copy.h
@@ -52,7 +52,6 @@ static inline void *vnic_wq_copy_next_desc(struct vnic_wq_copy *wq)
 
 static inline void vnic_wq_copy_post(struct vnic_wq_copy *wq)
 {
-	BUG_ON(!wq->ring.desc_avail);
 
 	((wq->to_use_index + 1) == wq->ring.desc_count) ?
 		(wq->to_use_index = 0) : (wq->to_use_index++);
@@ -80,7 +79,6 @@ static inline void vnic_wq_copy_desc_process(struct vnic_wq_copy *wq, u16 index)
 	wq->to_clean_index = ((index + 1) % wq->ring.desc_count);
 	wq->ring.desc_avail += cnt;
 
-	BUG_ON(wq->ring.desc_avail >= wq->ring.desc_count);
 }
 
 static inline void vnic_wq_copy_service(struct vnic_wq_copy *wq,
@@ -98,8 +96,6 @@ static inline void vnic_wq_copy_service(struct vnic_wq_copy *wq,
 
 		wq->ring.desc_avail++;
 
-		BUG_ON(wq->ring.desc_avail >= wq->ring.desc_count);
-
 		curr_index = wq->to_clean_index;
 
 		/* increment the to-clean index so that we start
diff --git a/drivers/scsi/libfc/Makefile b/drivers/scsi/libfc/Makefile
index e6d4086..55f982d 100644
--- a/drivers/scsi/libfc/Makefile
+++ b/drivers/scsi/libfc/Makefile
@@ -5,6 +5,7 @@ obj-$(CONFIG_LIBFC) += libfc.o
 libfc-objs := \
 	fc_disc.o \
 	fc_exch.o \
+	fc_elsct.o \
 	fc_frame.o \
 	fc_lport.o \
 	fc_rport.o \
diff --git a/drivers/scsi/libfc/fc_disc.c b/drivers/scsi/libfc/fc_disc.c
index a7a4547..cfe4828 100644
--- a/drivers/scsi/libfc/fc_disc.c
+++ b/drivers/scsi/libfc/fc_disc.c
@@ -30,11 +30,13 @@
 
 #include <scsi/fc/fc_gs.h>
 
-#include <scsi/libfc/libfc.h>
+#include <scsi/libfc.h>
 
 #define FC_DISC_RETRY_LIMIT	3	/* max retries */
 #define FC_DISC_RETRY_DELAY	500UL	/* (msecs) delay */
 
+#define	FC_DISC_DELAY		3
+
 static int fc_disc_debug;
 
 #define FC_DEBUG_DISC(fmt...)			\
@@ -43,26 +45,162 @@ static int fc_disc_debug;
 			FC_DBG(fmt);		\
 	} while (0)
 
-static void fc_disc_gpn_ft_req(struct fc_lport *);
+struct fc_disc {
+	unsigned char		retry_count;
+	unsigned char		delay;
+	unsigned char		pending;
+	unsigned char		requested;
+	unsigned short		seq_count;
+	unsigned char		buf_len;
+	enum fc_disc_event	event;
+
+	void (*disc_callback)(struct fc_lport *,
+			      enum fc_disc_event);
+
+	struct list_head	 rports;
+	struct fc_lport		*lport;
+	struct mutex		disc_mutex;
+	struct fc_gpn_ft_resp	partial_buf;	/* partial name buffer */
+	struct work_struct	disc_work;
+};
+
+static void fc_disc_gpn_ft_req(struct fc_disc *);
 static void fc_disc_gpn_ft_resp(struct fc_seq *, struct fc_frame *, void *);
-static int fc_disc_new_target(struct fc_lport *, struct fc_rport *,
+static int fc_disc_new_target(struct fc_disc *, struct fc_rport *,
 			      struct fc_rport_identifiers *);
-static void fc_disc_del_target(struct fc_lport *, struct fc_rport *);
-static void fc_disc_done(struct fc_lport *);
-static void fc_disc_error(struct fc_lport *, struct fc_frame *);
-static void fc_disc_timeout(void *data);
-static void fc_disc_single(struct fc_lport *, struct fc_disc_port *);
-static int fc_disc_restart(struct fc_lport *);
+static void fc_disc_del_target(struct fc_disc *, struct fc_rport *);
+static void fc_disc_done(struct fc_disc *);
+static void fc_disc_timeout(void *);
+static void fc_disc_single(struct fc_disc *, struct fc_disc_port *);
+static void fc_disc_restart(struct fc_disc *);
+
+/**
+ * fc_disc_lookup_rport - lookup a remote port by port_id
+ * @lport: Fibre Channel host port instance
+ * @port_id: remote port port_id to match
+ */
+struct fc_rport *fc_disc_lookup_rport(const struct fc_lport *lport,
+				      u32 port_id)
+{
+	struct fc_disc *disc = lport->disc;
+	struct fc_rport *rport, *found = NULL;
+	struct fc_rport_libfc_priv *rdata;
+	int disc_found = 0;
+
+	if (!disc)
+		return NULL;
+
+	list_for_each_entry(rdata, &disc->rports, peers) {
+		rport = PRIV_TO_RPORT(rdata);
+		if (rport->port_id == port_id) {
+			disc_found = 1;
+			found = rport;
+			get_device(&found->dev);
+			break;
+		}
+	}
+
+	if (!disc_found)
+		found = NULL;
+
+	return found;
+}
+
+/**
+ * fc_disc_alloc - Allocate a discovery work object
+ * @lport: The FC lport associated with the discovery job
+ */
+static inline struct fc_disc *fc_disc_alloc(struct fc_lport *lport)
+{
+	struct fc_disc *disc;
+
+	disc = kzalloc(sizeof(struct fc_disc), GFP_KERNEL);
+	INIT_WORK(&disc->disc_work, fc_disc_timeout, disc);
+	mutex_init(&disc->disc_mutex);
+	INIT_LIST_HEAD(&disc->rports);
+
+	disc->lport = lport;
+	lport->disc = disc;
+	disc->delay = FC_DISC_DELAY;
+	disc->event = DISC_EV_NONE;
+
+	return disc;
+}
+
+/**
+ * fc_disc_stop_rports - delete all the remote ports associated with the lport
+ * @disc: The discovery job to stop rports on
+ *
+ * Locking Note: This function expects that the lport mutex is locked before
+ * calling it.
+ */
+void fc_disc_stop_rports(struct fc_disc *disc)
+{
+	struct fc_lport *lport;
+	struct fc_rport *rport;
+	struct fc_rport_libfc_priv *rdata, *next;
+
+	lport = disc->lport;
+
+	mutex_lock(&disc->disc_mutex);
+	list_for_each_entry_safe(rdata, next, &disc->rports, peers) {
+		rport = PRIV_TO_RPORT(rdata);
+		list_del(&rdata->peers);
+		lport->tt.rport_logoff(rport);
+	}
+
+	mutex_unlock(&disc->disc_mutex);
+}
+
+/**
+ * fc_disc_rport_event - Event handler for rport events
+ * @lport: The lport which is receiving the event
+ * @rport: The rport which the event has occured on
+ * @event: The event that occured
+ *
+ * Locking Note: The rport lock should not be held when calling
+ *		 this function.
+ */
+static void fc_disc_rport_event(struct fc_lport *lport,
+				struct fc_rport *rport,
+				enum fc_lport_event event)
+{
+	struct fc_rport_libfc_priv *rdata = rport->dd_data;
+	struct fc_disc *disc = lport->disc;
+	int found = 0;
+
+	FC_DEBUG_DISC("Received a %d event for port (%6x)\n", event,
+		      rport->port_id);
+
+	if (event == RPORT_EV_CREATED) {
+		if (disc) {
+			found = 1;
+			mutex_lock(&disc->disc_mutex);
+			list_add_tail(&rdata->peers, &disc->rports);
+			mutex_unlock(&disc->disc_mutex);
+		}
+	}
+
+	if (!found)
+		FC_DEBUG_DISC("The rport (%6x) is not maintained "
+			      "by the discovery layer\n", rport->port_id);
+}
 
 /**
  * fc_disc_recv_rscn_req - Handle Registered State Change Notification (RSCN)
  * @sp: Current sequence of the RSCN exchange
  * @fp: RSCN Frame
  * @lport: Fibre Channel host port instance
+ *
+ * Locking Note: This function expects that the disc_mutex is locked
+ *		 before it is called.
  */
 static void fc_disc_recv_rscn_req(struct fc_seq *sp, struct fc_frame *fp,
-				  struct fc_lport *lport)
+				  struct fc_disc *disc)
 {
+	struct fc_lport *lport;
+	struct fc_rport *rport;
+	struct fc_rport_libfc_priv *rdata;
 	struct fc_els_rscn *rp;
 	struct fc_els_rscn_page *pp;
 	struct fc_seq_els_data rjt_data;
@@ -70,9 +208,14 @@ static void fc_disc_recv_rscn_req(struct fc_seq *sp, struct fc_frame *fp,
 	int redisc = 0;
 	enum fc_els_rscn_ev_qual ev_qual;
 	enum fc_els_rscn_addr_fmt fmt;
-	LIST_HEAD(disc_list);
+	LIST_HEAD(disc_ports);
 	struct fc_disc_port *dp, *next;
 
+	lport = disc->lport;
+
+	FC_DEBUG_DISC("Received an RSCN event on port (%6x)\n",
+		      fc_host_port_id(lport->host));
+
 	rp = fc_frame_payload_get(fp, sizeof(*rp));
 
 	if (!rp || rp->rscn_page_len != sizeof(*pp))
@@ -106,7 +249,7 @@ static void fc_disc_recv_rscn_req(struct fc_seq *sp, struct fc_frame *fp,
 			dp->ids.port_name = -1;
 			dp->ids.node_name = -1;
 			dp->ids.roles = FC_RPORT_ROLE_UNKNOWN;
-			list_add_tail(&dp->peers, &disc_list);
+			list_add_tail(&dp->peers, &disc_ports);
 			break;
 		case ELS_ADDR_FMT_AREA:
 		case ELS_ADDR_FMT_DOM:
@@ -120,18 +263,20 @@ static void fc_disc_recv_rscn_req(struct fc_seq *sp, struct fc_frame *fp,
 	lport->tt.seq_els_rsp_send(sp, ELS_LS_ACC, NULL);
 	if (redisc) {
 		FC_DEBUG_DISC("RSCN received: rediscovering\n");
-		list_for_each_entry_safe(dp, next, &disc_list, peers) {
-			list_del(&dp->peers);
-			kfree(dp);
-		}
-		fc_disc_restart(lport);
+		fc_disc_restart(disc);
 	} else {
 		FC_DEBUG_DISC("RSCN received: not rediscovering. "
 			      "redisc %d state %d in_prog %d\n",
-			      redisc, lport->state, lport->disc_pending);
-		list_for_each_entry_safe(dp, next, &disc_list, peers) {
+			      redisc, lport->state, disc->pending);
+		list_for_each_entry_safe(dp, next, &disc_ports, peers) {
 			list_del(&dp->peers);
-			fc_disc_single(lport, dp);
+			rport = lport->tt.rport_lookup(lport, dp->ids.port_id);
+			if (rport) {
+				rdata = RPORT_TO_PRIV(rport);
+				list_del(&rdata->peers);
+				lport->tt.rport_logoff(rport);
+			}
+			fc_disc_single(disc, dp);
 		}
 	}
 	fc_frame_free(fp);
@@ -149,16 +294,29 @@ reject:
  * @sp: Current sequence of the request exchange
  * @fp: The frame
  * @lport: The FC local port
+ *
+ * Locking Note: This function is called from the EM and will lock
+ *		 the disc_mutex before calling the handler for the
+ *		 request.
  */
 static void fc_disc_recv_req(struct fc_seq *sp, struct fc_frame *fp,
 			     struct fc_lport *lport)
 {
 	u8 op;
+	struct fc_disc *disc = lport->disc;
+
+	if (!disc) {
+		FC_DBG("Received a request for an lport not managed "
+		       "by the discovery engine\n");
+		return;
+	}
 
 	op = fc_frame_payload_op(fp);
 	switch (op) {
 	case ELS_RSCN:
-		fc_disc_recv_rscn_req(sp, fp, lport);
+		mutex_lock(&disc->disc_mutex);
+		fc_disc_recv_rscn_req(sp, fp, disc);
+		mutex_unlock(&disc->disc_mutex);
 		break;
 	default:
 		FC_DBG("Received an unsupported request. opcode (%x)\n", op);
@@ -168,16 +326,30 @@ static void fc_disc_recv_req(struct fc_seq *sp, struct fc_frame *fp,
 
 /**
  * fc_disc_restart - Restart discovery
- * @lport: FC local port
+ * @lport: FC discovery context
+ *
+ * Locking Note: This function expects that the disc mutex
+ *		 is already locked.
  */
-static int fc_disc_restart(struct fc_lport *lport)
+static void fc_disc_restart(struct fc_disc *disc)
 {
-	if (!lport->disc_requested && !lport->disc_pending) {
-		schedule_delayed_work(&lport->disc_work,
-				      msecs_to_jiffies(lport->disc_delay * 1000));
+	struct fc_rport *rport;
+	struct fc_rport_libfc_priv *rdata, *next;
+	struct fc_lport *lport = disc->lport;
+
+	FC_DEBUG_DISC("Restarting discovery for port (%6x)\n",
+		      fc_host_port_id(lport->host));
+
+	list_for_each_entry_safe(rdata, next, &disc->rports, peers) {
+		rport = PRIV_TO_RPORT(rdata);
+		FC_DEBUG_DISC("list_del(%6x)\n", rport->port_id);
+		list_del(&rdata->peers);
+		lport->tt.rport_logoff(rport);
 	}
-	lport->disc_requested = 1;
-	return 0;
+
+	disc->requested = 1;
+	if (!disc->pending)
+		fc_disc_gpn_ft_req(disc);
 }
 
 /**
@@ -186,29 +358,48 @@ static int fc_disc_restart(struct fc_lport *lport)
  *
  * Returns non-zero if discovery cannot be started.
  */
-static int fc_disc_start(struct fc_lport *lport)
+static void fc_disc_start(void (*disc_callback)(struct fc_lport *,
+						enum fc_disc_event),
+			  struct fc_lport *lport)
 {
 	struct fc_rport *rport;
-	int error;
 	struct fc_rport_identifiers ids;
+	struct fc_disc *disc = lport->disc;
+
+	if (!disc) {
+		FC_DEBUG_DISC("No existing discovery job, "
+			      "creating one for lport (%6x)\n",
+			      fc_host_port_id(lport->host));
+		disc = fc_disc_alloc(lport);
+	} else
+		FC_DEBUG_DISC("Found an existing discovery job "
+			      "for lport (%6x)\n",
+			      fc_host_port_id(lport->host));
+
+	/*
+	 * At this point we may have a new disc job or an existing
+	 * one. Either way, let's lock when we make changes to it
+	 * and send the GPN_FT request.
+	 */
+	mutex_lock(&disc->disc_mutex);
+
+	disc->disc_callback = disc_callback;
 
 	/*
 	 * If not ready, or already running discovery, just set request flag.
 	 */
-	if (!fc_lport_test_ready(lport) || lport->disc_pending) {
-		lport->disc_requested = 1;
+	disc->requested = 1;
 
-		return 0;
+	if (disc->pending) {
+		mutex_unlock(&disc->disc_mutex);
+		return;
 	}
-	lport->disc_pending = 1;
-	lport->disc_requested = 0;
-	lport->disc_retry_count = 0;
 
 	/*
 	 * Handle point-to-point mode as a simple discovery
-	 * of the remote port.
+	 * of the remote port. Yucky, yucky, yuck, yuck!
 	 */
-	rport = lport->ptp_rp;
+	rport = disc->lport->ptp_rp;
 	if (rport) {
 		ids.port_id = rport->port_id;
 		ids.port_name = rport->port_name;
@@ -216,32 +407,16 @@ static int fc_disc_start(struct fc_lport *lport)
 		ids.roles = FC_RPORT_ROLE_UNKNOWN;
 		get_device(&rport->dev);
 
-		error = fc_disc_new_target(lport, rport, &ids);
+		if (!fc_disc_new_target(disc, rport, &ids)) {
+			disc->event = DISC_EV_SUCCESS;
+			fc_disc_done(disc);
+		}
 		put_device(&rport->dev);
-		if (!error)
-			fc_disc_done(lport);
 	} else {
-		fc_disc_gpn_ft_req(lport);	/* get ports by FC-4 type */
-		error = 0;
+		fc_disc_gpn_ft_req(disc);	/* get ports by FC-4 type */
 	}
-	return error;
-}
-
-/**
- * fc_disc_retry - Retry discovery
- * @lport: FC local port
- */
-static void fc_disc_retry(struct fc_lport *lport)
-{
-	unsigned long delay = FC_DISC_RETRY_DELAY;
 
-	if (!lport->disc_retry_count)
-		delay /= 4;	/* timeout faster first time */
-	if (lport->disc_retry_count++ < FC_DISC_RETRY_LIMIT)
-		schedule_delayed_work(&lport->disc_work,
-				      msecs_to_jiffies(delay));
-	else
-		fc_disc_done(lport);
+	mutex_unlock(&disc->disc_mutex);
 }
 
 /**
@@ -249,11 +424,15 @@ static void fc_disc_retry(struct fc_lport *lport)
  * @lport: FC local port
  * @rport: The previous FC remote port (NULL if new remote port)
  * @ids: Identifiers for the new FC remote port
+ *
+ * Locking Note: This function expects that the disc_mutex is locked
+ *		 before it is called.
  */
-static int fc_disc_new_target(struct fc_lport *lport,
+static int fc_disc_new_target(struct fc_disc *disc,
 			      struct fc_rport *rport,
 			      struct fc_rport_identifiers *ids)
 {
+	struct fc_lport *lport = disc->lport;
 	struct fc_rport_libfc_priv *rp;
 	int error = 0;
 
@@ -272,7 +451,7 @@ static int fc_disc_new_target(struct fc_lport *lport,
 			 * assigned the same FCID.  This should be rare.
 			 * Delete the old one and fall thru to re-create.
 			 */
-			fc_disc_del_target(lport, rport);
+			fc_disc_del_target(disc, rport);
 			rport = NULL;
 		}
 	}
@@ -291,11 +470,11 @@ static int fc_disc_new_target(struct fc_lport *lport,
 				rport = fc_rport_rogue_create(&dp);
 			}
 			if (!rport)
-				error = ENOMEM;
+				error = -ENOMEM;
 		}
 		if (rport) {
 			rp = rport->dd_data;
-			rp->event_callback = lport->tt.event_callback;
+			rp->event_callback = fc_disc_rport_event;
 			rp->rp_state = RPORT_ST_INIT;
 			lport->tt.rport_login(rport);
 		}
@@ -305,99 +484,120 @@ static int fc_disc_new_target(struct fc_lport *lport,
 
 /**
  * fc_disc_del_target - Delete a target
- * @lport: FC local port
+ * @disc: FC discovery context
  * @rport: The remote port to be removed
  */
-static void fc_disc_del_target(struct fc_lport *lport, struct fc_rport *rport)
+static void fc_disc_del_target(struct fc_disc *disc, struct fc_rport *rport)
 {
-	lport->tt.rport_stop(rport);
+	struct fc_lport *lport = disc->lport;
+	struct fc_rport_libfc_priv *rdata = RPORT_TO_PRIV(rport);
+	list_del(&rdata->peers);
+	lport->tt.rport_logoff(rport);
 }
 
 /**
  * fc_disc_done - Discovery has been completed
- * @lport: FC local port
+ * @disc: FC discovery context
  */
-static void fc_disc_done(struct fc_lport *lport)
+static void fc_disc_done(struct fc_disc *disc)
 {
-	lport->disc_done = 1;
-	lport->disc_pending = 0;
-	if (lport->disc_requested)
-		lport->tt.disc_start(lport);
-}
+	struct fc_lport *lport = disc->lport;
 
-/**
- * fc_disc_gpn_ft_req - Send Get Port Names by FC-4 type (GPN_FT) request
- * @lport: FC local port
- */
-static void fc_disc_gpn_ft_req(struct fc_lport *lport)
-{
-	struct fc_frame *fp;
-	struct fc_seq *sp = NULL;
-	struct req {
-		struct fc_ct_hdr ct;
-		struct fc_ns_gid_ft gid;
-	} *rp;
-	int error = 0;
+	FC_DEBUG_DISC("Discovery complete for port (%6x)\n",
+		      fc_host_port_id(lport->host));
 
-	lport->disc_buf_len = 0;
-	lport->disc_seq_count = 0;
-	fp = fc_frame_alloc(lport, sizeof(*rp));
-	if (!fp) {
-		error = ENOMEM;
-	} else {
-		rp = fc_frame_payload_get(fp, sizeof(*rp));
-		fc_fill_dns_hdr(lport, &rp->ct, FC_NS_GPN_FT, sizeof(rp->gid));
-		rp->gid.fn_fc4_type = FC_TYPE_FCP;
-
-		WARN_ON(!fc_lport_test_ready(lport));
-
-		fc_frame_setup(fp, FC_RCTL_DD_UNSOL_CTL, FC_TYPE_CT);
-		sp = lport->tt.exch_seq_send(lport, fp,
-					     fc_disc_gpn_ft_resp, NULL,
-					     lport, lport->e_d_tov,
-					     fc_host_port_id(lport->host),
-					     FC_FID_DIR_SERV,
-					     FC_FC_SEQ_INIT | FC_FC_END_SEQ);
-	}
-	if (error || !sp)
-		fc_disc_retry(lport);
+	disc->disc_callback(lport, disc->event);
+	disc->event = DISC_EV_NONE;
+
+	if (disc->requested)
+		fc_disc_gpn_ft_req(disc);
+	else
+		disc->pending = 0;
 }
 
 /**
  * fc_disc_error - Handle error on dNS request
- * @lport: FC local port
+ * @disc: FC discovery context
  * @fp: The frame pointer
  */
-static void fc_disc_error(struct fc_lport *lport, struct fc_frame *fp)
+static void fc_disc_error(struct fc_disc *disc, struct fc_frame *fp)
 {
-	long err = PTR_ERR(fp);
-
-	FC_DEBUG_DISC("Error %ld, retries %d/%d\n", PTR_ERR(fp),
-		      lport->retry_count, FC_DISC_RETRY_LIMIT);
-    
-	switch (err) {
-	case -FC_EX_TIMEOUT:
-		if (lport->disc_retry_count++ < FC_DISC_RETRY_LIMIT) {
-			fc_disc_gpn_ft_req(lport);
+	struct fc_lport *lport = disc->lport;
+	unsigned long delay = 0;
+	if (fc_disc_debug)
+		FC_DBG("Error %ld, retries %d/%d\n",
+		       PTR_ERR(fp), disc->retry_count,
+		       FC_DISC_RETRY_LIMIT);
+
+	if (!fp || PTR_ERR(fp) == -FC_EX_TIMEOUT) {
+		/*
+		 * Memory allocation failure, or the exchange timed out,
+		 * retry after delay.
+		 */
+		if (disc->retry_count < FC_DISC_RETRY_LIMIT) {
+			/* go ahead and retry */
+			if (!fp)
+				delay = msecs_to_jiffies(FC_DISC_RETRY_DELAY);
+			else {
+				delay = msecs_to_jiffies(lport->e_d_tov);
+
+				/* timeout faster first time */
+				if (!disc->retry_count)
+					delay /= 4;
+			}
+			disc->retry_count++;
+			schedule_delayed_work(&disc->disc_work, delay);
 		} else {
-			fc_disc_done(lport);
+			/* exceeded retries */
+			disc->event = DISC_EV_FAILED;
+			fc_disc_done(disc);
 		}
-		break;
-	default:
-		FC_DBG("Error code %ld not supported\n", err);
-		fc_disc_done(lport);
-		break;
 	}
 }
 
 /**
+ * fc_disc_gpn_ft_req - Send Get Port Names by FC-4 type (GPN_FT) request
+ * @lport: FC discovery context
+ *
+ * Locking Note: This function expects that the disc_mutex is locked
+ *		 before it is called.
+ */
+static void fc_disc_gpn_ft_req(struct fc_disc *disc)
+{
+	struct fc_frame *fp;
+	struct fc_lport *lport = disc->lport;
+
+	WARN_ON(!fc_lport_test_ready(lport));
+
+	disc->pending = 1;
+	disc->requested = 0;
+
+	disc->buf_len = 0;
+	disc->seq_count = 0;
+	fp = fc_frame_alloc(lport,
+			    sizeof(struct fc_ct_hdr) +
+			    sizeof(struct fc_ns_gid_ft));
+	if (!fp)
+		goto err;
+
+	if (lport->tt.elsct_send(lport, NULL, fp,
+				 FC_NS_GPN_FT,
+				 fc_disc_gpn_ft_resp,
+				 disc, lport->e_d_tov))
+		return;
+err:
+	fc_disc_error(disc, fp);
+}
+
+/**
  * fc_disc_gpn_ft_parse - Parse the list of IDs and names resulting from a request
  * @lport: Fibre Channel host port instance
  * @buf: GPN_FT response buffer
  * @len: size of response buffer
  */
-static int fc_disc_gpn_ft_parse(struct fc_lport *lport, void *buf, size_t len)
+static int fc_disc_gpn_ft_parse(struct fc_disc *disc, void *buf, size_t len)
 {
+	struct fc_lport *lport;
 	struct fc_gpn_ft_resp *np;
 	char *bp;
 	size_t plen;
@@ -407,13 +607,15 @@ static int fc_disc_gpn_ft_parse(struct fc_lport *lport, void *buf, size_t len)
 	struct fc_rport *rport;
 	struct fc_rport_libfc_priv *rdata;
 
+	lport = disc->lport;
+
 	/*
 	 * Handle partial name record left over from previous call.
 	 */
 	bp = buf;
 	plen = len;
 	np = (struct fc_gpn_ft_resp *)bp;
-	tlen = lport->disc_buf_len;
+	tlen = disc->buf_len;
 	if (tlen) {
 		WARN_ON(tlen >= sizeof(*np));
 		plen = sizeof(*np) - tlen;
@@ -421,7 +623,7 @@ static int fc_disc_gpn_ft_parse(struct fc_lport *lport, void *buf, size_t len)
 		WARN_ON(plen >= sizeof(*np));
 		if (plen > len)
 			plen = len;
-		np = &lport->disc_buf;
+		np = &disc->partial_buf;
 		memcpy((char *)np + tlen, bp, plen);
 
 		/*
@@ -431,9 +633,9 @@ static int fc_disc_gpn_ft_parse(struct fc_lport *lport, void *buf, size_t len)
 		bp -= tlen;
 		len += tlen;
 		plen += tlen;
-		lport->disc_buf_len = (unsigned char) plen;
+		disc->buf_len = (unsigned char) plen;
 		if (plen == sizeof(*np))
-			lport->disc_buf_len = 0;
+			disc->buf_len = 0;
 	}
 
 	/*
@@ -455,7 +657,7 @@ static int fc_disc_gpn_ft_parse(struct fc_lport *lport, void *buf, size_t len)
 			rport = fc_rport_rogue_create(&dp);
 			if (rport) {
 				rdata = rport->dd_data;
-				rdata->event_callback = lport->tt.event_callback;
+				rdata->event_callback = fc_disc_rport_event;
 				rdata->local_port = lport;
 				lport->tt.rport_login(rport);
 			} else
@@ -465,7 +667,8 @@ static int fc_disc_gpn_ft_parse(struct fc_lport *lport, void *buf, size_t len)
 		}
 
 		if (np->fp_flags & FC_NS_FID_LAST) {
-			fc_disc_done(lport);
+			disc->event = DISC_EV_SUCCESS;
+			fc_disc_done(disc);
 			len = 0;
 			break;
 		}
@@ -479,11 +682,15 @@ static int fc_disc_gpn_ft_parse(struct fc_lport *lport, void *buf, size_t len)
 	 * Save any partial record at the end of the buffer for next time.
 	 */
 	if (error == 0 && len > 0 && len < sizeof(*np)) {
-		if (np != &lport->disc_buf)
-			memcpy(&lport->disc_buf, np, len);
-		lport->disc_buf_len = (unsigned char) len;
+		if (np != &disc->partial_buf) {
+			FC_DEBUG_DISC("Partial buffer remains "
+				      "for discovery by (%6x)\n",
+				      fc_host_port_id(lport->host));
+			memcpy(&disc->partial_buf, np, len);
+		}
+		disc->buf_len = (unsigned char) len;
 	} else {
-		lport->disc_buf_len = 0;
+		disc->buf_len = 0;
 	}
 	return error;
 }
@@ -493,12 +700,12 @@ static int fc_disc_gpn_ft_parse(struct fc_lport *lport, void *buf, size_t len)
  */
 static void fc_disc_timeout(void *data)
 {
-	struct fc_lport *lport = data;
+	struct fc_disc *disc = data;
 
-	if (lport->disc_pending)
-		fc_disc_gpn_ft_req(lport);
-	else
-		lport->tt.disc_start(lport);
+	mutex_lock(&disc->disc_mutex);
+	if (disc->requested && !disc->pending)
+		fc_disc_gpn_ft_req(disc);
+	mutex_unlock(&disc->disc_mutex);
 }
 
 /**
@@ -507,12 +714,13 @@ static void fc_disc_timeout(void *data)
  * @fp: response frame
  * @lp_arg: Fibre Channel host port instance
  *
- * The response may be in multiple frames
+ * Locking Note: This function expects that the disc_mutex is locked
+ *		 before it is called.
  */
 static void fc_disc_gpn_ft_resp(struct fc_seq *sp, struct fc_frame *fp,
-				void *lp_arg)
+				void *disc_arg)
 {
-	struct fc_lport *lport = lp_arg;
+	struct fc_disc *disc = disc_arg;
 	struct fc_ct_hdr *cp;
 	struct fc_frame_header *fh;
 	unsigned int seq_cnt;
@@ -520,8 +728,11 @@ static void fc_disc_gpn_ft_resp(struct fc_seq *sp, struct fc_frame *fp,
 	unsigned int len;
 	int error;
 
+	FC_DEBUG_DISC("Received a GPN_FT response on port (%6x)\n",
+		      fc_host_port_id(disc->lport->host));
+
 	if (IS_ERR(fp)) {
-		fc_disc_error(lport, fp);
+		fc_disc_error(disc, fp);
 		return;
 	}
 
@@ -530,9 +741,9 @@ static void fc_disc_gpn_ft_resp(struct fc_seq *sp, struct fc_frame *fp,
 	len = fr_len(fp) - sizeof(*fh);
 	seq_cnt = ntohs(fh->fh_seq_cnt);
 	if (fr_sof(fp) == FC_SOF_I3 && seq_cnt == 0 &&
-	    lport->disc_seq_count == 0) {
+	    disc->seq_count == 0) {
 		cp = fc_frame_payload_get(fp, sizeof(*cp));
-		if (cp == NULL) {
+		if (!cp) {
 			FC_DBG("GPN_FT response too short, len %d\n",
 			       fr_len(fp));
 		} else if (ntohs(cp->ct_cmd) == FC_FS_ACC) {
@@ -546,25 +757,26 @@ static void fc_disc_gpn_ft_resp(struct fc_seq *sp, struct fc_frame *fp,
 			FC_DBG("GPN_FT rejected reason %x exp %x "
 			       "(check zoning)\n", cp->ct_reason,
 			       cp->ct_explan);
-			fc_disc_done(lport);
+			disc->event = DISC_EV_FAILED;
+			fc_disc_done(disc);
 		} else {
 			FC_DBG("GPN_FT unexpected response code %x\n",
 			       ntohs(cp->ct_cmd));
 		}
 	} else if (fr_sof(fp) == FC_SOF_N3 &&
-		   seq_cnt == lport->disc_seq_count) {
+		   seq_cnt == disc->seq_count) {
 		buf = fh + 1;
 	} else {
 		FC_DBG("GPN_FT unexpected frame - out of sequence? "
 		       "seq_cnt %x expected %x sof %x eof %x\n",
-		       seq_cnt, lport->disc_seq_count, fr_sof(fp), fr_eof(fp));
+		       seq_cnt, disc->seq_count, fr_sof(fp), fr_eof(fp));
 	}
 	if (buf) {
-		error = fc_disc_gpn_ft_parse(lport, buf, len);
+		error = fc_disc_gpn_ft_parse(disc, buf, len);
 		if (error)
-			fc_disc_retry(lport);
+			fc_disc_error(disc, fp);
 		else
-			lport->disc_seq_count++;
+			disc->seq_count++;
 	}
 	fc_frame_free(fp);
 }
@@ -574,27 +786,31 @@ static void fc_disc_gpn_ft_resp(struct fc_seq *sp, struct fc_frame *fp,
  * @lport: FC local port
  * @dp: The port to rediscover
  *
- * This could be from an RSCN that reported a change for the target.
+ * Locking Note: This function expects that the disc_mutex is locked
+ *		 before it is called.
  */
-static void fc_disc_single(struct fc_lport *lport, struct fc_disc_port *dp)
+static void fc_disc_single(struct fc_disc *disc, struct fc_disc_port *dp)
 {
+	struct fc_lport *lport;
 	struct fc_rport *rport;
 	struct fc_rport *new_rport;
 	struct fc_rport_libfc_priv *rdata;
 
+	lport = disc->lport;
+
 	if (dp->ids.port_id == fc_host_port_id(lport->host))
 		goto out;
 
 	rport = lport->tt.rport_lookup(lport, dp->ids.port_id);
 	if (rport) {
-		fc_disc_del_target(lport, rport);
+		fc_disc_del_target(disc, rport);
 		put_device(&rport->dev); /* hold from lookup */
 	}
 
 	new_rport = fc_rport_rogue_create(dp);
 	if (new_rport) {
 		rdata = new_rport->dd_data;
-		rdata->event_callback = lport->tt.event_callback;
+		rdata->event_callback = fc_disc_rport_event;
 		kfree(dp);
 		lport->tt.rport_login(new_rport);
 	}
@@ -604,18 +820,55 @@ out:
 }
 
 /**
+ * fc_disc_stop - Stop discovery for a given lport
+ * @lport: The lport that discovery should stop for
+ */
+void fc_disc_stop(struct fc_lport *lport)
+{
+	struct fc_disc *disc = lport->disc;
+
+	if (disc) {
+		if (!cancel_delayed_work(&disc->disc_work))
+			flush_scheduled_work();
+		fc_disc_stop_rports(disc);
+	}
+}
+
+/**
+ * fc_disc_stop_final - Stop discovery for a given lport
+ * @lport: The lport that discovery should stop for
+ *
+ * This function will block until discovery has been
+ * completely stopped and all rports have been deleted.
+ */
+void fc_disc_stop_final(struct fc_lport *lport)
+{
+	fc_disc_stop(lport);
+	lport->tt.rport_flush_queue();
+}
+
+/**
  * fc_disc_init - Initialize the discovery block
  * @lport: FC local port
  */
 int fc_disc_init(struct fc_lport *lport)
 {
-	INIT_WORK(&lport->disc_work, fc_disc_timeout, lport);
+
 	if (!lport->tt.disc_start)
 		lport->tt.disc_start = fc_disc_start;
 
+	if (!lport->tt.disc_stop)
+		lport->tt.disc_stop = fc_disc_stop;
+
+	if (!lport->tt.disc_stop_final)
+		lport->tt.disc_stop_final = fc_disc_stop_final;
+
 	if (!lport->tt.disc_recv_req)
 		lport->tt.disc_recv_req = fc_disc_recv_req;
 
+	if (!lport->tt.rport_lookup)
+		lport->tt.rport_lookup = fc_disc_lookup_rport;
+
 	return 0;
 }
 EXPORT_SYMBOL(fc_disc_init);
diff --git a/drivers/scsi/libfc/fc_elsct.c b/drivers/scsi/libfc/fc_elsct.c
new file mode 100644
index 0000000..4bca7f2
--- /dev/null
+++ b/drivers/scsi/libfc/fc_elsct.c
@@ -0,0 +1,71 @@
+/*
+ * Copyright(c) 2008 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,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Maintained at www.Open-FCoE.org
+ */
+
+/*
+ * Provide interface to send ELS/CT FC frames
+ */
+
+#include <asm/unaligned.h>
+#include <scsi/libfc.h>
+#include <scsi/fc_encode.h>
+#include <scsi/fc/fc_gs.h>
+#include <scsi/fc/fc_ns.h>
+#include <scsi/fc/fc_els.h>
+
+/*
+ * fc_elsct_send - sends ELS/CT frame
+ */
+static struct fc_seq *fc_elsct_send(struct fc_lport *lport,
+				    struct fc_rport *rport,
+				    struct fc_frame *fp,
+				    unsigned int op,
+				    void (*resp)(struct fc_seq *,
+						 struct fc_frame *fp,
+						 void *arg),
+				    void *arg, u32 timer_msec)
+{
+	enum fc_rctl r_ctl;
+	u32 did;
+	enum fc_fh_type fh_type;
+	int rc;
+
+	/* ELS requests */
+	if ((op >= ELS_LS_RJT) && (op <= ELS_AUTH_ELS))
+		rc = fc_els_fill(lport, rport, fp, op, &r_ctl, &did, &fh_type);
+	else
+		/* CT requests */
+		rc = fc_ct_fill(lport, fp, op, &r_ctl, &did, &fh_type);
+
+	if (rc)
+		return NULL;
+
+	fc_fill_fc_hdr(fp, r_ctl, did, fc_host_port_id(lport->host), fh_type,
+		       FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0);
+
+	return lport->tt.exch_seq_send(lport, fp, resp, NULL, arg, timer_msec);
+}
+
+int fc_elsct_init(struct fc_lport *lport)
+{
+	if (!lport->tt.elsct_send)
+		lport->tt.elsct_send = fc_elsct_send;
+
+	return 0;
+}
+EXPORT_SYMBOL(fc_elsct_init);
diff --git a/drivers/scsi/libfc/fc_exch.c b/drivers/scsi/libfc/fc_exch.c
index c46f909..fb6000f 100644
--- a/drivers/scsi/libfc/fc_exch.c
+++ b/drivers/scsi/libfc/fc_exch.c
@@ -30,7 +30,8 @@
 
 #include <scsi/fc/fc_fc2.h>
 
-#include <scsi/libfc/libfc.h>
+#include <scsi/libfc.h>
+#include <scsi/fc_encode.h>
 
 #define	  FC_DEF_R_A_TOV      (10 * 1000) /* resource allocation timeout */
 
@@ -61,66 +62,6 @@ static struct kmem_cache *fc_em_cachep;	/* cache for exchanges */
  */
 
 /*
- * Sequence.
- */
-struct fc_seq {
-	u8	id;		/* seq ID */
-	u16	ssb_stat;	/* status flags for sequence status block */
-	u16	cnt;		/* frames sent so far on sequence */
-	u32	f_ctl;		/* F_CTL flags for frames */
-	u32	rec_data;	/* FC-4 value for REC */
-};
-
-struct fc_exch;
-
-#define FC_EX_DONE		(1 << 0) /* ep is completed */
-#define FC_EX_RST_CLEANUP	(1 << 1) /* reset is forcing completion */
-
-/*
- * Exchange.
- *
- * Locking notes: The ex_lock protects changes to the following fields:
- *	esb_stat, f_ctl, seq.ssb_stat, seq.f_ctl.
- *	seq_id
- *	sequence allocation
- *
- * If the em_lock and ex_lock must be taken at the same time, the
- * em_lock must be taken before the ex_lock.
- */
-struct fc_exch {
-	struct fc_exch_mgr *em;		/* exchange manager */
-	u32		state;		/* internal driver state */
-	u16		xid;		/* our exchange ID */
-	struct list_head	ex_list;	/* free or busy list linkage */
-	spinlock_t	ex_lock;	/* lock covering exchange state */
-	atomic_t	ex_refcnt;	/* reference counter */
-	struct work_struct timeout_work; /* timer for upper level protocols */
-	struct fc_lport	*lp;		/* fc device instance */
-	u16		oxid;		/* originator's exchange ID */
-	u16		rxid;		/* responder's exchange ID */
-	u32		oid;		/* originator's FCID */
-	u32		sid;		/* source FCID */
-	u32		did;		/* destination FCID */
-	u32		esb_stat;	/* exchange status for ESB */
-	u32		r_a_tov;	/* r_a_tov from rport (msec) */
-	u8		seq_id;		/* next sequence ID to use */
-	u32		f_ctl;		/* F_CTL flags for sequences */
-	u8		fh_type;	/* frame type */
-	enum fc_class	class;		/* class of service */
-	struct fc_seq	seq;		/* single sequence */
-	/*
-	 * Handler for responses to this current exchange.
-	 */
-	void		(*resp)(struct fc_seq *, struct fc_frame *, void *);
-	void		(*destructor)(struct fc_seq *, void *);
-	/*
-	 * arg is passed as void pointer to exchange
-	 * resp and destructor handlers
-	 */
-	void		*arg;
-};
-
-/*
  * Exchange manager.
  *
  * This structure is the center for creating exchanges and sequences.
@@ -132,6 +73,8 @@ struct fc_exch_mgr {
 	u16		last_xid;	/* last allocated exchange ID */
 	u16		min_xid;	/* min exchange ID */
 	u16		max_xid;	/* max exchange ID */
+	u16		max_read;	/* max exchange ID for read */
+	u16		last_read;	/* last xid allocated for read */
 	u32	total_exches;		/* total allocated exchanges */
 	struct list_head	ex_list;	/* allocated exchanges list */
 	struct fc_lport	*lp;		/* fc device instance */
@@ -152,14 +95,12 @@ struct fc_exch_mgr {
 	} stats;
 	struct fc_exch **exches;	/* for exch pointers indexed by xid */
 };
-
 #define	fc_seq_exch(sp) container_of(sp, struct fc_exch, seq)
-#define	fc_exch_next_xid(mp, id) ((id == mp->max_xid) ? mp->min_xid : id + 1)
 
 static void fc_exch_rrq(struct fc_exch *);
 static void fc_seq_ls_acc(struct fc_seq *);
 static void fc_seq_ls_rjt(struct fc_seq *, enum fc_els_rjt_reason,
-		  enum fc_els_rjt_explan);
+			  enum fc_els_rjt_explan);
 static void fc_exch_els_rec(struct fc_seq *, struct fc_frame *);
 static void fc_exch_els_rrq(struct fc_seq *, struct fc_frame *);
 static struct fc_seq *fc_seq_start_next_locked(struct fc_seq *sp);
@@ -275,34 +216,57 @@ static void fc_exch_hold(struct fc_exch *ep)
 }
 
 /*
- * Fill in frame header.
- *
- * The following fields are the responsibility of this routine:
- *	d_id, s_id, df_ctl, oxid, rxid, cs_ctl, seq_id
- *
- * The following fields are handled by the caller.
- *	r_ctl, type, f_ctl, seq_cnt, parm_offset
- *
- * That should be a complete list.
- *
- * We may be the originator or responder to the sequence.
+ * setup fc hdr by initializing few more FC header fields and sof/eof.
+ * Initialized fields by this func:
+ *	- fh_ox_id, fh_rx_id, fh_seq_id, fh_seq_cnt
+ *	- sof and eof
  */
-static void fc_seq_fill_hdr(struct fc_seq *sp, struct fc_frame *fp)
+static void fc_exch_setup_hdr(struct fc_exch *ep, struct fc_frame *fp,
+			      u32 f_ctl)
 {
 	struct fc_frame_header *fh = fc_frame_header_get(fp);
-	struct fc_exch *ep;
+	u16 fill;
 
-	ep = fc_seq_exch(sp);
+	fr_sof(fp) = ep->class;
+	if (ep->seq.cnt)
+		fr_sof(fp) = fc_sof_normal(ep->class);
+
+	if (f_ctl & FC_FC_END_SEQ) {
+		fr_eof(fp) = FC_EOF_T;
+		if (fc_sof_needs_ack(ep->class))
+			fr_eof(fp) = FC_EOF_N;
+		/*
+		 * Form f_ctl.
+		 * The number of fill bytes to make the length a 4-byte
+		 * multiple is the low order 2-bits of the f_ctl.
+		 * The fill itself will have been cleared by the frame
+		 * allocation.
+		 * After this, the length will be even, as expected by
+		 * the transport.
+		 */
+		fill = fr_len(fp) & 3;
+		if (fill) {
+			fill = 4 - fill;
+			/* TODO, this may be a problem with fragmented skb */
+			skb_put(fp_skb(fp), fill);
+			hton24(fh->fh_f_ctl, f_ctl | fill);
+		}
+	} else {
+		WARN_ON(fr_len(fp) % 4 != 0);	/* no pad to non last frame */
+		fr_eof(fp) = FC_EOF_N;
+	}
 
-	hton24(fh->fh_s_id, ep->sid);
-	hton24(fh->fh_d_id, ep->did);
+	/*
+	 * Initialize remainig fh fields
+	 * from fc_fill_fc_hdr
+	 */
 	fh->fh_ox_id = htons(ep->oxid);
 	fh->fh_rx_id = htons(ep->rxid);
-	fh->fh_seq_id = sp->id;
-	fh->fh_cs_ctl = 0;
-	fh->fh_df_ctl = 0;
+	fh->fh_seq_id = ep->seq.id;
+	fh->fh_seq_cnt = htons(ep->seq.cnt);
 }
 
+
 /*
  * Release a reference to an exchange.
  * If the refcnt goes to zero and the exchange is complete, it is freed.
@@ -415,7 +379,6 @@ int fc_seq_exch_abort(const struct fc_seq *req_sp, unsigned int timer_msec)
 		return -ENOMEM;
 	}
 
-	sp->f_ctl |= FC_FC_SEQ_INIT;
 	ep->esb_stat |= ESB_ST_SEQ_INIT | ESB_ST_ABNORMAL;
 	if (timer_msec)
 		fc_exch_timer_set_locked(ep, timer_msec);
@@ -433,8 +396,9 @@ int fc_seq_exch_abort(const struct fc_seq *req_sp, unsigned int timer_msec)
 	 */
 	fp = fc_frame_alloc(ep->lp, 0);
 	if (fp) {
-		fc_frame_setup(fp, FC_RCTL_BA_ABTS, FC_TYPE_BLS);
-		error = fc_seq_send(ep->lp, sp, fp, FC_FC_END_SEQ);
+		fc_fill_fc_hdr(fp, FC_RCTL_BA_ABTS, ep->did, ep->sid,
+			       FC_TYPE_BLS, FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0);
+		error = fc_seq_send(ep->lp, sp, fp);
 	} else
 		error = -ENOBUFS;
 	return error;
@@ -501,43 +465,72 @@ static struct fc_seq *fc_seq_alloc(struct fc_exch *ep, u8 seq_id)
 
 	sp = &ep->seq;
 	sp->ssb_stat = 0;
-	sp->f_ctl = 0;
 	sp->cnt = 0;
 	sp->id = seq_id;
 	return sp;
 }
 
 /*
- * Allocate an exchange.
+ * fc_em_alloc_xid - returns an xid based on request type
+ * @lp : ptr to associated lport
+ * @fp : ptr to the assocated frame
  *
- * if xid is supplied zero then assign next free exchange ID
- * from exchange manager, otherwise use supplied xid.
- * Returns with exch lock held.
+ * check the associated fc_fsp_pkt to get scsi command type and
+ * command direction to decide from which range this exch id
+ * will be allocated from.
+ *
+ * Returns : 0 or an valid xid
  */
-struct fc_exch *fc_exch_alloc(struct fc_exch_mgr *mp, u16 xid)
+static u16 fc_em_alloc_xid(struct fc_exch_mgr *mp, const struct fc_frame *fp)
 {
+	u16 xid, min, max;
+	u16 *plast;
 	struct fc_exch *ep = NULL;
-	u16 min_xid, max_xid;
 
-	min_xid = mp->min_xid;
-	max_xid = mp->max_xid;
-	/*
-	 * if xid is supplied then verify its xid range
-	 */
-	if (xid) {
-		if (unlikely((xid < min_xid) || (xid > max_xid))) {
-			FC_DBG("Invalid xid 0x:%x\n", xid);
-			goto out;
-		}
-		if (unlikely(mp->exches[xid - min_xid] != NULL)) {
-			FC_DBG("xid 0x:%x is already in use\n", xid);
-			goto out;
+	if (mp->max_read) {
+		if (fc_frame_is_read(fp)) {
+			min = mp->min_xid;
+			max = mp->max_read;
+			plast = &mp->last_read;
+		} else {
+			min = mp->max_read + 1;
+			max = mp->max_xid;
+			plast = &mp->last_xid;
 		}
+	} else {
+		min = mp->min_xid;
+		max = mp->max_xid;
+		plast = &mp->last_xid;
 	}
+	xid = *plast;
+	do {
+		xid = (xid == max) ? min : xid + 1;
+		ep = mp->exches[xid - mp->min_xid];
+	} while ((ep != NULL) && (xid != *plast));
 
-	/*
-	 * Allocate new exchange
-	 */
+	if (unlikely(ep))
+		xid = 0;
+	else
+		*plast = xid;
+
+	return xid;
+}
+
+/*
+ * fc_exch_alloc - allocate an exchange.
+ * @mp : ptr to the exchange manager
+ * @xid: input xid
+ *
+ * if xid is supplied zero then assign next free exchange ID
+ * from exchange manager, otherwise use supplied xid.
+ * Returns with exch lock held.
+ */
+struct fc_exch *fc_exch_alloc(struct fc_exch_mgr *mp,
+			      struct fc_frame *fp, u16 xid)
+{
+	struct fc_exch *ep;
+
+	/* allocate memory for exchange */
 	ep = mempool_alloc(mp->ep_pool, GFP_ATOMIC);
 	if (!ep) {
 		atomic_inc(&mp->stats.no_free_exch);
@@ -546,40 +539,26 @@ struct fc_exch *fc_exch_alloc(struct fc_exch_mgr *mp, u16 xid)
 	memset(ep, 0, sizeof(*ep));
 
 	spin_lock_bh(&mp->em_lock);
-
-	/*
-	 * if xid is zero then assign next free exchange ID
-	 */
+	/* alloc xid if input xid 0 */
 	if (!xid) {
-		xid = fc_exch_next_xid(mp, mp->last_xid);
-		/*
-		 * find next free xid using linear search
-		 */
-		while (mp->exches[xid - min_xid] != NULL) {
-			if (xid == mp->last_xid)
-				break;
-			xid = fc_exch_next_xid(mp, xid);
-		}
-
-		if (unlikely(mp->exches[xid - min_xid] != NULL))
+		/* alloc a new xid */
+		xid = fc_em_alloc_xid(mp, fp);
+		if (!xid) {
+			printk(KERN_ERR "fc_em_alloc_xid() failed\n");
 			goto err;
-		mp->last_xid = xid;
+		}
 	}
 
-	/*  lport lock ? */
-	if (mp->lp->state == LPORT_ST_RESET)
-		goto err;	/*  don't add new ep during local port reset  */
-
 	fc_exch_hold(ep);	/* hold for exch in mp */
 	spin_lock_init(&ep->ex_lock);
 	/*
 	 * Hold exch lock for caller to prevent fc_exch_reset()
-	 * from releasing exch  while fc_exch_alloc() caller is
+	 * from releasing exch	while fc_exch_alloc() caller is
 	 * still working on exch.
 	 */
 	spin_lock_bh(&ep->ex_lock);
 
-	mp->exches[xid - min_xid] = ep;
+	mp->exches[xid - mp->min_xid] = ep;
 	list_add_tail(&ep->ex_list, &mp->ex_list);
 	fc_seq_alloc(ep, ep->seq_id++);
 	mp->total_exches++;
@@ -853,8 +832,8 @@ static struct fc_seq *fc_seq_start_next_locked(struct fc_seq *sp)
 	struct fc_exch *ep = fc_seq_exch(sp);
 
 	sp = fc_seq_alloc(ep, ep->seq_id++);
-	FC_DEBUG_EXCH("exch %4x f_ctl %6x seq %2x f_ctl %6x\n",
-		      ep->xid, ep->f_ctl, sp->id, sp->f_ctl);
+	FC_DEBUG_EXCH("exch %4x f_ctl %6x seq %2x\n",
+		      ep->xid, ep->f_ctl, sp->id);
 	return sp;
 }
 /*
@@ -874,55 +853,18 @@ struct fc_seq *fc_seq_start_next(struct fc_seq *sp)
 }
 EXPORT_SYMBOL(fc_seq_start_next);
 
-int fc_seq_send(struct fc_lport *lp, struct fc_seq *sp,
-		struct fc_frame *fp, u32 f_ctl)
+int fc_seq_send(struct fc_lport *lp, struct fc_seq *sp, struct fc_frame *fp)
 {
 	struct fc_exch *ep;
-	struct fc_frame_header *fh;
-	enum fc_class class;
-	u16 fill = 0;
+	struct fc_frame_header *fh = fc_frame_header_get(fp);
 	int error;
+	u32	f_ctl;
 
 	ep = fc_seq_exch(sp);
 	WARN_ON((ep->esb_stat & ESB_ST_SEQ_INIT) != ESB_ST_SEQ_INIT);
 
-	fc_seq_fill_hdr(sp, fp);
-	fh = fc_frame_header_get(fp);
-	class = ep->class;
-	fr_sof(fp) = class;
-	if (sp->cnt)
-		fr_sof(fp) = fc_sof_normal(class);
-
-	if (f_ctl & FC_FC_END_SEQ) {
-		fr_eof(fp) = FC_EOF_T;
-		if (fc_sof_needs_ack(class))
-			fr_eof(fp) = FC_EOF_N;
-		/*
-		 * Form f_ctl.
-		 * The number of fill bytes to make the length a 4-byte
-		 * multiple is the low order 2-bits of the f_ctl.
-		 * The fill itself will have been cleared by the frame
-		 * allocation.
-		 * After this, the length will be even, as expected by
-		 * the transport. Don't include the fill in the f_ctl
-		 * saved in the sequence.
-		 */
-		fill = fr_len(fp) & 3;
-		if (fill) {
-			fill = 4 - fill;
-			/* TODO, this may be a problem with fragmented skb */
-			skb_put(fp_skb(fp), fill);
-		}
-		f_ctl |= sp->f_ctl | ep->f_ctl;
-	} else {
-		WARN_ON(fr_len(fp) % 4 != 0);	/* no pad to non last frame */
-		f_ctl |= sp->f_ctl | ep->f_ctl;
-		f_ctl &= ~FC_FC_SEQ_INIT;
-		fr_eof(fp) = FC_EOF_N;
-	}
-
-	hton24(fh->fh_f_ctl, f_ctl | fill);
-	fh->fh_seq_cnt = htons(sp->cnt);
+	f_ctl = ntoh24(fh->fh_f_ctl);
+	fc_exch_setup_hdr(ep, fp, f_ctl);
 
 	/*
 	 * update sequence count if this frame is carrying
@@ -946,12 +888,9 @@ int fc_seq_send(struct fc_lport *lp, struct fc_seq *sp,
 	 * We can only be called to send once for each sequence.
 	 */
 	spin_lock_bh(&ep->ex_lock);
-	sp->f_ctl = f_ctl;	/* save for possible abort */
-	ep->f_ctl &= ~FC_FC_FIRST_SEQ;	/* not first seq */
-	if (f_ctl & FC_FC_END_SEQ) {
-		if (f_ctl & FC_FC_SEQ_INIT)
-			ep->esb_stat &= ~ESB_ST_SEQ_INIT;
-	}
+	ep->f_ctl = f_ctl & ~FC_FC_FIRST_SEQ;	/* not first seq */
+	if (f_ctl & (FC_FC_END_SEQ | FC_FC_SEQ_INIT))
+		ep->esb_stat &= ~ESB_ST_SEQ_INIT;
 	spin_unlock_bh(&ep->ex_lock);
 	return error;
 }
@@ -986,10 +925,12 @@ static void fc_seq_send_last(struct fc_seq *sp, struct fc_frame *fp,
 			     enum fc_rctl rctl, enum fc_fh_type fh_type)
 {
 	u32 f_ctl;
+	struct fc_exch *ep = fc_seq_exch(sp);
 
-	fc_frame_setup(fp, rctl, fh_type);
-	f_ctl = FC_FC_SEQ_INIT | FC_FC_LAST_SEQ | FC_FC_END_SEQ;
-	fc_seq_send(fc_seq_exch(sp)->lp, sp, fp, f_ctl);
+	f_ctl = FC_FC_LAST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT;
+	f_ctl |= ep->f_ctl;
+	fc_fill_fc_hdr(fp, rctl, ep->did, ep->sid, fh_type, f_ctl, 0);
+	fc_seq_send(ep->lp, sp, fp);
 }
 
 /*
@@ -1001,7 +942,8 @@ static void fc_seq_send_ack(struct fc_seq *sp, const struct fc_frame *rx_fp)
 	struct fc_frame *fp;
 	struct fc_frame_header *rx_fh;
 	struct fc_frame_header *fh;
-	struct fc_lport *lp = fc_seq_exch(sp)->lp;
+	struct fc_exch *ep = fc_seq_exch(sp);
+	struct fc_lport *lp = ep->lp;
 	unsigned int f_ctl;
 
 	/*
@@ -1009,11 +951,9 @@ static void fc_seq_send_ack(struct fc_seq *sp, const struct fc_frame *rx_fp)
 	 */
 	if (fc_sof_needs_ack(fr_sof(rx_fp))) {
 		fp = fc_frame_alloc(lp, 0);
-		BUG_ON(!fp);
 		if (!fp)
 			return;
 
-		fc_seq_fill_hdr(sp, fp);
 		fh = fc_frame_header_get(fp);
 		fh->fh_r_ctl = FC_RCTL_ACK_1;
 		fh->fh_type = FC_TYPE_BLS;
@@ -1034,6 +974,7 @@ static void fc_seq_send_ack(struct fc_seq *sp, const struct fc_frame *rx_fp)
 		f_ctl ^= FC_FC_EX_CTX | FC_FC_SEQ_CTX;
 		hton24(fh->fh_f_ctl, f_ctl);
 
+		fc_exch_setup_hdr(ep, fp, f_ctl);
 		fh->fh_seq_id = rx_fh->fh_seq_id;
 		fh->fh_seq_cnt = rx_fh->fh_seq_cnt;
 		fh->fh_parm_offset = htonl(1);	/* ack single frame */
@@ -1516,8 +1457,7 @@ static void fc_exch_reset(struct fc_exch *ep)
 	 * a deadlock).
 	 */
 	if (cancel_delayed_work(&ep->timeout_work))
-		atomic_dec(&ep->ex_refcnt);     /* drop hold for timer */
-
+		atomic_dec(&ep->ex_refcnt);	/* drop hold for timer */
 	resp = ep->resp;
 	ep->resp = NULL;
 	if (ep->esb_stat & ESB_ST_REC_QUAL)
@@ -1568,22 +1508,6 @@ restart:
 }
 EXPORT_SYMBOL(fc_exch_mgr_reset);
 
-void fc_seq_get_xids(struct fc_seq *sp, u16 *oxid, u16 *rxid)
-{
-	struct fc_exch *ep;
-
-	ep = fc_seq_exch(sp);
-	*oxid = ep->oxid;
-	*rxid = ep->rxid;
-}
-EXPORT_SYMBOL(fc_seq_get_xids);
-
-void fc_seq_set_rec_data(struct fc_seq *sp, u32 rec_data)
-{
-	sp->rec_data = rec_data;
-}
-EXPORT_SYMBOL(fc_seq_set_rec_data);
-
 /*
  * Handle incoming ELS REC - Read Exchange Concise.
  * Note that the requesting port may be different than the S_ID in the request.
@@ -1651,8 +1575,8 @@ static void fc_exch_els_rec(struct fc_seq *sp, struct fc_frame *rfp)
 		hton24(acc->reca_rfid, ep->sid);
 	acc->reca_fc4value = htonl(ep->seq.rec_data);
 	acc->reca_e_stat = htonl(ep->esb_stat & (ESB_ST_RESP |
-						  ESB_ST_SEQ_INIT |
-						  ESB_ST_COMPLETE));
+						 ESB_ST_SEQ_INIT |
+						 ESB_ST_COMPLETE));
 	sp = fc_seq_start_next(sp);
 	fc_seq_send_last(sp, fp, FC_RCTL_ELS_REP, FC_TYPE_ELS);
 out:
@@ -1726,7 +1650,6 @@ static void fc_exch_rrq(struct fc_exch *ep)
 	fp = fc_frame_alloc(lp, sizeof(*rrq));
 	if (!fp)
 		return;
-	fc_frame_setup(fp, FC_RCTL_ELS_REQ, FC_TYPE_ELS);
 	rrq = fc_frame_payload_get(fp, sizeof(*rrq));
 	memset(rrq, 0, sizeof(*rrq));
 	rrq->rrq_cmd = ELS_RRQ;
@@ -1737,9 +1660,13 @@ static void fc_exch_rrq(struct fc_exch *ep)
 	did = ep->did;
 	if (ep->esb_stat & ESB_ST_RESP)
 		did = ep->sid;
+
+	fc_fill_fc_hdr(fp, FC_RCTL_ELS_REQ, did,
+		       fc_host_port_id(lp->host), FC_TYPE_ELS,
+		       FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0);
+
 	rrq_sp = fc_exch_seq_send(lp, fp, fc_exch_rrq_resp, NULL, ep,
-				  lp->e_d_tov, fc_host_port_id(lp->host), did,
-				  FC_FC_SEQ_INIT | FC_FC_END_SEQ);
+				  lp->e_d_tov);
 	if (!rrq_sp) {
 		ep->esb_stat |= ESB_ST_REC_QUAL;
 		fc_exch_timer_set_locked(ep, ep->r_a_tov);
@@ -1794,7 +1721,7 @@ static void fc_exch_els_rrq(struct fc_seq *sp, struct fc_frame *fp)
 	}
 	if (ep->esb_stat & ESB_ST_COMPLETE) {
 		if (cancel_delayed_work(&ep->timeout_work))
-			atomic_dec(&ep->ex_refcnt);	/* drop hold for timer */
+			atomic_dec(&ep->ex_refcnt);	/* drop timer hold */
 	}
 
 	spin_unlock_bh(&ep->ex_lock);
@@ -1830,6 +1757,7 @@ struct fc_exch_mgr *fc_exch_mgr_alloc(struct fc_lport *lp,
 	/*
 	 * Memory need for EM
 	 */
+#define xid_ok(i, m1, m2) (((i) >= (m1)) && ((i) <= (m2)))
 	len = (max_xid - min_xid + 1) * (sizeof(struct fc_exch *));
 	len += sizeof(struct fc_exch_mgr);
 
@@ -1840,10 +1768,22 @@ struct fc_exch_mgr *fc_exch_mgr_alloc(struct fc_lport *lp,
 	mp->class = class;
 	mp->total_exches = 0;
 	mp->exches = (struct fc_exch **)(mp + 1);
-	mp->last_xid = min_xid - 1;
+	mp->lp = lp;
+	/* adjust em exch xid range for offload */
 	mp->min_xid = min_xid;
 	mp->max_xid = max_xid;
-	mp->lp = lp;
+	mp->last_xid = min_xid - 1;
+	mp->max_read = 0;
+	mp->last_read = 0;
+	if (lp->lro_enabled && xid_ok(lp->lro_xid, min_xid, max_xid)) {
+		mp->max_read = lp->lro_xid;
+		mp->last_read = min_xid - 1;
+		mp->last_xid = mp->max_read;
+	} else {
+		/* disable lro if no xid control over read */
+		lp->lro_enabled = 0;
+	}
+
 	INIT_LIST_HEAD(&mp->ex_list);
 	spin_lock_init(&mp->em_lock);
 
@@ -1876,7 +1816,8 @@ struct fc_exch *fc_exch_get(struct fc_lport *lp, struct fc_frame *fp)
 {
 	if (!lp || !lp->emp)
 		return NULL;
-	return fc_exch_alloc(lp->emp, 0);
+
+	return fc_exch_alloc(lp->emp, fp, 0);
 }
 EXPORT_SYMBOL(fc_exch_get);
 
@@ -1886,13 +1827,11 @@ struct fc_seq *fc_exch_seq_send(struct fc_lport *lp,
 					     struct fc_frame *fp,
 					     void *arg),
 				void (*destructor)(struct fc_seq *, void *),
-				void *arg, u32 timer_msec,
-				u32 sid, u32 did, u32 f_ctl)
+				void *arg, u32 timer_msec)
 {
 	struct fc_exch *ep;
 	struct fc_seq *sp = NULL;
 	struct fc_frame_header *fh;
-	u16 fill;
 	int rc = 1;
 
 	ep = lp->tt.exch_get(lp, fp);
@@ -1901,52 +1840,28 @@ struct fc_seq *fc_exch_seq_send(struct fc_lport *lp,
 		return NULL;
 	}
 	ep->esb_stat |= ESB_ST_SEQ_INIT;
-	fc_exch_set_addr(ep, sid, did);
+	fh = fc_frame_header_get(fp);
+	fc_exch_set_addr(ep, ntoh24(fh->fh_s_id), ntoh24(fh->fh_d_id));
 	ep->resp = resp;
 	ep->destructor = destructor;
 	ep->arg = arg;
 	ep->r_a_tov = FC_DEF_R_A_TOV;
 	ep->lp = lp;
 	sp = &ep->seq;
-	WARN_ON((sp->f_ctl & FC_FC_END_SEQ) != 0);
 
-	fr_sof(fp) = ep->class;
-	if (sp->cnt)
-		fr_sof(fp) = fc_sof_normal(ep->class);
-	fr_eof(fp) = FC_EOF_T;
-	if (fc_sof_needs_ack(ep->class))
-		fr_eof(fp) = FC_EOF_N;
-
-	fc_seq_fill_hdr(sp, fp);
-	/*
-	 * Form f_ctl.
-	 * The number of fill bytes to make the length a 4-byte multiple is
-	 * the low order 2-bits of the f_ctl.  The fill itself will have been
-	 * cleared by the frame allocation.
-	 * After this, the length will be even, as expected by the transport.
-	 * Don't include the fill in the f_ctl saved in the sequence.
-	 */
-	fill = fr_len(fp) & 3;
-	if (fill) {
-		fill = 4 - fill;
-		/* TODO, this may be a problem with fragmented skb */
-		skb_put(fp_skb(fp), fill);
-	}
-	f_ctl |= ep->f_ctl;
-	fh = fc_frame_header_get(fp);
-	hton24(fh->fh_f_ctl, f_ctl | fill);
-	fh->fh_seq_cnt = htons(sp->cnt++);
 	ep->fh_type = fh->fh_type; /* save for possbile timeout handling */
+	ep->f_ctl = ntoh24(fh->fh_f_ctl);
+	fc_exch_setup_hdr(ep, fp, ep->f_ctl);
+	sp->cnt++;
 
 	if (unlikely(lp->tt.frame_send(lp, fp)))
 		goto err;
 
 	if (timer_msec)
 		fc_exch_timer_set_locked(ep, timer_msec);
-	sp->f_ctl = f_ctl;	/* save for possible abort */
 	ep->f_ctl &= ~FC_FC_FIRST_SEQ;	/* not first seq */
 
-	if (f_ctl & FC_FC_SEQ_INIT)
+	if (ep->f_ctl & FC_FC_SEQ_INIT)
 		ep->esb_stat &= ~ESB_ST_SEQ_INIT;
 	spin_unlock_bh(&ep->ex_lock);
 	return sp;
@@ -2035,11 +1950,6 @@ int fc_exch_init(struct fc_lport *lp)
 	if (!lp->tt.seq_exch_abort)
 		lp->tt.seq_exch_abort = fc_seq_exch_abort;
 
-	if (!lp->tt.seq_get_xids)
-		lp->tt.seq_get_xids = fc_seq_get_xids;
-
-	if (!lp->tt.seq_set_rec_data)
-		lp->tt.seq_set_rec_data = fc_seq_set_rec_data;
 	return 0;
 }
 EXPORT_SYMBOL(fc_exch_init);
diff --git a/drivers/scsi/libfc/fc_fcp.c b/drivers/scsi/libfc/fc_fcp.c
index 2565176..319c4d7 100644
--- a/drivers/scsi/libfc/fc_fcp.c
+++ b/drivers/scsi/libfc/fc_fcp.c
@@ -36,7 +36,8 @@
 
 #include <scsi/fc/fc_fc2.h>
 
-#include <scsi/libfc/libfc.h>
+#include <scsi/libfc.h>
+#include <scsi/fc_encode.h>
 
 MODULE_AUTHOR("Open-FCoE.org");
 MODULE_DESCRIPTION("libfc");
@@ -63,70 +64,8 @@ static struct kmem_cache *scsi_pkt_cachep;
 #define FC_SRB_FCP_PROCESSING_TMO (1 << 6)	/* timer function processing */
 #define FC_SRB_NOMEM		(1 << 7)	/* dropped to out of mem */
 
-#define FC_SRB_READ		    (1 << 1)
-#define FC_SRB_WRITE		    (1 << 0)
-
-/*
- * scsi request structure, one for each scsi request
- */
-struct fc_fcp_pkt {
-	/*
-	 * housekeeping stuff
-	 */
-	struct fc_lport *lp;	/* handle to hba struct */
-	u16		state;		/* scsi_pkt state state */
-	u16		tgt_flags;	/* target flags	 */
-	atomic_t	ref_cnt;	/* only used byr REC ELS */
-	spinlock_t	scsi_pkt_lock;	/* Must be taken before the host lock
-					 * if both are held at the same time */
-	/*
-	 * SCSI I/O related stuff
-	 */
-	struct scsi_cmnd *cmd;		/* scsi command pointer. set/clear
-					 * under host lock */
-	struct list_head list;		/* tracks queued commands. access under
-					 * host lock */
-	/*
-	 * timeout related stuff
-	 */
-	struct timer_list timer;	/* command timer */
-	struct completion tm_done;
-	int	wait_for_comp;
-	unsigned long	start_time;	/* start jiffie */
-	unsigned long	end_time;	/* end jiffie */
-	unsigned long	last_pkt_time;	 /* jiffies of last frame received */
-
-	/*
-	 * scsi cmd and data transfer information
-	 */
-	u32		data_len;
-	/*
-	 * transport related veriables
-	 */
-	struct fcp_cmnd cdb_cmd;
-	size_t		xfer_len;
-	u32		xfer_contig_end; /* offset of end of contiguous xfer */
-	u16		max_payload;	/* max payload size in bytes */
-
-	/*
-	 * scsi/fcp return status
-	 */
-	u32		io_status;	/* SCSI result upper 24 bits */
-	u8		cdb_status;
-	u8		status_code;	/* FCP I/O status */
-	/* bit 3 Underrun bit 2: overrun */
-	u8		scsi_comp_flags;
-	u32		req_flags;	/* bit 0: read bit:1 write */
-	u32		scsi_resid;	/* residule length */
-
-	struct fc_rport	*rport;		/* remote port pointer */
-	struct fc_seq	*seq_ptr;	/* current sequence pointer */
-	/*
-	 * Error Processing
-	 */
-	u8		recov_retry;	/* count of recovery retries */
-	struct fc_seq	*recov_seq;	/* sequence for REC or SRR */
-};
+#define FC_SRB_READ		(1 << 1)
+#define FC_SRB_WRITE		(1 << 0)
 
 /*
  * The SCp.ptr should be tested and set under the host lock. NULL indicates
@@ -153,11 +92,10 @@ struct fc_fcp_internal {
 static void fc_fcp_recv_data(struct fc_fcp_pkt *, struct fc_frame *);
 static void fc_fcp_recv(struct fc_seq *, struct fc_frame *, void *);
 static void fc_fcp_resp(struct fc_fcp_pkt *, struct fc_frame *);
-static void fc_fcp_complete(struct fc_fcp_pkt *);
+static void fc_fcp_complete_locked(struct fc_fcp_pkt *);
 static void fc_tm_done(struct fc_seq *, struct fc_frame *, void *);
 static void fc_fcp_error(struct fc_fcp_pkt *fsp, struct fc_frame *fp);
 static void fc_timeout_error(struct fc_fcp_pkt *);
-static int fc_fcp_send_cmd(struct fc_fcp_pkt *);
 static void fc_fcp_timeout(unsigned long data);
 static void fc_fcp_rec(struct fc_fcp_pkt *);
 static void fc_fcp_rec_error(struct fc_fcp_pkt *, struct fc_frame *);
@@ -208,46 +146,46 @@ static void fc_fcp_srr_error(struct fc_fcp_pkt *, struct fc_frame *);
 static struct fc_fcp_pkt *fc_fcp_pkt_alloc(struct fc_lport *lp, gfp_t gfp)
 {
 	struct fc_fcp_internal *si = fc_get_scsi_internal(lp);
-	struct fc_fcp_pkt *sp;
-
-	sp = mempool_alloc(si->scsi_pkt_pool, gfp);
-	if (sp) {
-		memset(sp, 0, sizeof(*sp));
-		sp->lp = lp;
-		atomic_set(&sp->ref_cnt, 1);
-		init_timer(&sp->timer);
-		INIT_LIST_HEAD(&sp->list);
-		spin_lock_init(&sp->scsi_pkt_lock);
+	struct fc_fcp_pkt *fsp;
+
+	fsp = mempool_alloc(si->scsi_pkt_pool, gfp);
+	if (fsp) {
+		memset(fsp, 0, sizeof(*fsp));
+		fsp->lp = lp;
+		atomic_set(&fsp->ref_cnt, 1);
+		init_timer(&fsp->timer);
+		INIT_LIST_HEAD(&fsp->list);
+		spin_lock_init(&fsp->scsi_pkt_lock);
 	}
-	return sp;
+	return fsp;
 }
 
 /**
  * fc_fcp_pkt_release - release hold on scsi_pkt packet
- * @sp:		fcp packet struct
+ * @fsp:	fcp packet struct
  *
  * This is used by upper layer scsi driver.
  * Context	: call from process  and interrupt context.
  *		  no locking required
  */
-static void fc_fcp_pkt_release(struct fc_fcp_pkt *sp)
+static void fc_fcp_pkt_release(struct fc_fcp_pkt *fsp)
 {
-	if (atomic_dec_and_test(&sp->ref_cnt)) {
-		struct fc_fcp_internal *si = fc_get_scsi_internal(sp->lp);
+	if (atomic_dec_and_test(&fsp->ref_cnt)) {
+		struct fc_fcp_internal *si = fc_get_scsi_internal(fsp->lp);
 
-		mempool_free(sp, si->scsi_pkt_pool);
+		mempool_free(fsp, si->scsi_pkt_pool);
 	}
 }
 
-static void fc_fcp_pkt_hold(struct fc_fcp_pkt *sp)
+static void fc_fcp_pkt_hold(struct fc_fcp_pkt *fsp)
 {
-	atomic_inc(&sp->ref_cnt);
+	atomic_inc(&fsp->ref_cnt);
 }
 
 /**
  * fc_fcp_pkt_destory - release hold on scsi_pkt packet
  *
- * @sp:		exchange sequence
+ * @seq:		exchange sequence
  * @fsp:	fcp packet struct
  *
  * Release hold on scsi_pkt packet set to keep scsi_pkt
@@ -255,9 +193,9 @@ static void fc_fcp_pkt_hold(struct fc_fcp_pkt *sp)
  * Context	: called from from EM layer.
  *		  no locking required
  */
-static void fc_fcp_pkt_destroy(struct fc_seq *sp, void *arg)
+static void fc_fcp_pkt_destroy(struct fc_seq *seq, void *fsp)
 {
-	fc_fcp_pkt_release(arg);
+	fc_fcp_pkt_release(fsp);
 }
 
 /**
@@ -280,10 +218,9 @@ static void fc_fcp_pkt_destroy(struct fc_seq *sp, void *arg)
 static inline int fc_fcp_lock_pkt(struct fc_fcp_pkt *fsp)
 {
 	spin_lock_bh(&fsp->scsi_pkt_lock);
-	if (!fsp->cmd) {
+	if (fsp->state & FC_SRB_COMPL) {
 		spin_unlock_bh(&fsp->scsi_pkt_lock);
-		FC_DBG("Invalid scsi cmd pointer on fcp packet.\n");
-		return -EINVAL;
+		return -EPERM;
 	}
 
 	fc_fcp_pkt_hold(fsp);
@@ -325,7 +262,7 @@ static void fc_fcp_retry_cmd(struct fc_fcp_pkt *fsp)
 	fsp->state &= ~FC_SRB_ABORT_PENDING;
 	fsp->io_status = SUGGEST_RETRY << 24;
 	fsp->status_code = FC_ERROR;
-	fc_fcp_complete(fsp);
+	fc_fcp_complete_locked(fsp);
 }
 
 #define for_each_sg(sglist, __sg, nr, __i)        \
@@ -466,19 +403,27 @@ crc_err:
 	 */
 	if (unlikely(fsp->state & FC_SRB_RCV_STATUS) &&
 	    fsp->xfer_len == fsp->data_len - fsp->scsi_resid)
-		fc_fcp_complete(fsp);
+		fc_fcp_complete_locked(fsp);
 }
 
 /*
- * Send SCSI data to target.
+ * fc_fcp_send_data -  Send SCSI data to target.
+ * @fsp: ptr to fc_fcp_pkt
+ * @sp: ptr to this sequence
+ * @offset: starting offset for this data request
+ * @seq_blen: the burst length for this data request
+ *
  * Called after receiving a Transfer Ready data descriptor.
  * if LLD is capable of seq offload then send down seq_blen
  * size of data in single frame, otherwise send multiple FC
  * frames of max FC frame payload supported by target port.
+ *
+ * Returns : 0 for success.
  */
-static int fc_fcp_send_data(struct fc_fcp_pkt *fsp, struct fc_seq *sp,
+static int fc_fcp_send_data(struct fc_fcp_pkt *fsp, struct fc_seq *seq,
 			    size_t offset, size_t seq_blen)
 {
+	struct fc_exch *ep;
 	struct scsi_cmnd *sc;
 	struct scatterlist *sg;
 	struct fc_frame *fp = NULL;
@@ -487,13 +432,12 @@ static int fc_fcp_send_data(struct fc_fcp_pkt *fsp, struct fc_seq *sp,
 	size_t t_blen;
 	size_t tlen;
 	size_t sg_bytes;
-	size_t frame_offset;
-	int error;
+	size_t frame_offset, fh_parm_offset;
+	int error, i;
 	void *data = NULL;
 	void *page_addr;
 	int using_sg = lp->sg_supp;
 	u32 f_ctl;
-	int i;
 
 	WARN_ON(seq_blen <= 0);
 	if (unlikely(offset + seq_blen > fsp->data_len)) {
@@ -521,11 +465,11 @@ static int fc_fcp_send_data(struct fc_fcp_pkt *fsp, struct fc_seq *sp,
 	sc = fsp->cmd;
 
 	remaining = seq_blen;
-	frame_offset = offset;
+	fh_parm_offset = frame_offset = offset;
 	tlen = 0;
-	sp = lp->tt.seq_start_next(sp);
+	seq = lp->tt.seq_start_next(seq);
 	f_ctl = FC_FC_REL_OFF;
-	WARN_ON(!sp);
+	WARN_ON(!seq);
 
 	/*
 	 * If a get_page()/put_page() will fail, don't use sg lists
@@ -586,8 +530,7 @@ static int fc_fcp_send_data(struct fc_fcp_pkt *fsp, struct fc_seq *sp,
 				data = (void *)(fr_hdr(fp)) +
 					sizeof(struct fc_frame_header);
 			}
-			fc_frame_setup(fp, FC_RCTL_DD_SOL_DATA, FC_TYPE_FCP);
-			fc_frame_set_offset(fp, frame_offset);
+			fh_parm_offset = frame_offset;
 			fr_max_payload(fp) = fsp->max_payload;
 		}
 		sg_bytes = min(tlen, sg->length - offset);
@@ -624,28 +567,30 @@ static int fc_fcp_send_data(struct fc_fcp_pkt *fsp, struct fc_seq *sp,
 		tlen -= sg_bytes;
 		remaining -= sg_bytes;
 
-		if (remaining == 0) {
-			/*
-			 * Send a request sequence with
-			 * transfer sequence initiative.
-			 */
-			f_ctl |= FC_FC_SEQ_INIT | FC_FC_END_SEQ;
-			error = lp->tt.seq_send(lp, sp, fp, f_ctl);
-		} else if (tlen == 0) {
-			/*
-			 * send fragment using for a sequence.
-			 */
-			error = lp->tt.seq_send(lp, sp, fp, f_ctl);
-		} else {
+		if (tlen)
 			continue;
-		}
-		fp = NULL;
 
+		/*
+		 * Send sequence with transfer sequence initiative in case
+		 * this is last FCP frame of the sequence.
+		 */
+		if (remaining == 0)
+			f_ctl |= FC_FC_SEQ_INIT | FC_FC_END_SEQ;
+
+		ep = fc_seq_exch(seq);
+		fc_fill_fc_hdr(fp, FC_RCTL_DD_SOL_DATA, ep->did, ep->sid,
+			       FC_TYPE_FCP, f_ctl, fh_parm_offset);
+
+		/*
+		 * send fragment using for a sequence.
+		 */
+		error = lp->tt.seq_send(lp, seq, fp);
 		if (error) {
 			WARN_ON(1);		/* send error should be rare */
 			fc_fcp_retry_cmd(fsp);
 			return 0;
 		}
+		fp = NULL;
 	}
 	fsp->xfer_len += seq_blen;	/* premature count? */
 	return 0;
@@ -682,7 +627,7 @@ static void fc_fcp_abts_resp(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
 		if (fsp->wait_for_comp)
 			complete(&fsp->tm_done);
 		else
-			fc_fcp_complete(fsp);
+			fc_fcp_complete_locked(fsp);
 	}
 }
 
@@ -726,7 +671,7 @@ done:
  * Context  : called from Soft IRQ context
  *	      can not called holding list lock
  */
-static void fc_fcp_recv(struct fc_seq *sp, struct fc_frame *fp, void *arg)
+static void fc_fcp_recv(struct fc_seq *seq, struct fc_frame *fp, void *arg)
 {
 	struct fc_fcp_pkt *fsp = (struct fc_fcp_pkt *)arg;
 	struct fc_lport *lp;
@@ -765,11 +710,11 @@ static void fc_fcp_recv(struct fc_seq *sp, struct fc_frame *fp, void *arg)
 		dd = fc_frame_payload_get(fp, sizeof(*dd));
 		WARN_ON(!dd);
 
-		rc = fc_fcp_send_data(fsp, sp,
+		rc = fc_fcp_send_data(fsp, seq,
 				      (size_t) ntohl(dd->ft_data_ro),
 				      (size_t) ntohl(dd->ft_burst_len));
 		if (!rc)
-			lp->tt.seq_set_rec_data(sp, fsp->xfer_len);
+			seq->rec_data = fsp->xfer_len;
 		else if (rc == -ENOMEM)
 			fsp->state |= FC_SRB_NOMEM;
 	} else if (r_ctl == FC_RCTL_DD_SOL_DATA) {
@@ -779,7 +724,7 @@ static void fc_fcp_recv(struct fc_seq *sp, struct fc_frame *fp, void *arg)
 		 */
 		WARN_ON(fr_len(fp) < sizeof(*fh));	/* len may be 0 */
 		fc_fcp_recv_data(fsp, fp);
-		lp->tt.seq_set_rec_data(sp, fsp->xfer_contig_end);
+		seq->rec_data = fsp->xfer_contig_end;
 	} else if (r_ctl == FC_RCTL_DD_CMD_STATUS) {
 		WARN_ON(fr_flags(fp) & FCPHF_CRC_UNCHECKED);
 
@@ -896,7 +841,7 @@ static void fc_fcp_resp(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
 		       fsp->rport->port_id,
 		       fsp->xfer_len, expected_len, fsp->data_len);
 	}
-	fc_fcp_complete(fsp);
+	fc_fcp_complete_locked(fsp);
 	return;
 
 len_err:
@@ -904,20 +849,21 @@ len_err:
 	       flags, fr_len(fp), respl, snsl);
 err:
 	fsp->status_code = FC_ERROR;
-	fc_fcp_complete(fsp);
+	fc_fcp_complete_locked(fsp);
 }
 
 /**
- * fc_fcp_complete - complete processing of a fcp packet
+ * fc_fcp_complete_locked - complete processing of a fcp packet
  * @fsp:	fcp packet
  *
  * This function may sleep if a timer is pending. The packet lock must be
  * held, and the host lock must not be held.
  */
-static void fc_fcp_complete(struct fc_fcp_pkt *fsp)
+static void fc_fcp_complete_locked(struct fc_fcp_pkt *fsp)
 {
 	struct fc_lport *lp = fsp->lp;
-	struct fc_seq *sp;
+	struct fc_seq *seq;
+	struct fc_exch *ep;
 	u32 f_ctl;
 
 	if (fsp->state & FC_SRB_ABORT_PENDING)
@@ -939,24 +885,26 @@ static void fc_fcp_complete(struct fc_fcp_pkt *fsp)
 		}
 	}
 
-	sp = fsp->seq_ptr;
-	if (sp) {
+	seq = fsp->seq_ptr;
+	if (seq) {
 		fsp->seq_ptr = NULL;
 		if (unlikely(fsp->scsi_comp_flags & FCP_CONF_REQ)) {
 			struct fc_frame *conf_frame;
 			struct fc_seq *csp;
 
-			csp = lp->tt.seq_start_next(sp);
+			csp = lp->tt.seq_start_next(seq);
 			conf_frame = fc_frame_alloc(fsp->lp, 0);
 			if (conf_frame) {
-				fc_frame_setup(conf_frame,
-					       FC_RCTL_DD_SOL_CTL, FC_TYPE_FCP);
 				f_ctl = FC_FC_SEQ_INIT;
 				f_ctl |= FC_FC_LAST_SEQ | FC_FC_END_SEQ;
-				lp->tt.seq_send(lp, csp, conf_frame, f_ctl);
+				ep = fc_seq_exch(seq);
+				fc_fill_fc_hdr(conf_frame, FC_RCTL_DD_SOL_CTL,
+					       ep->did, ep->sid,
+					       FC_TYPE_FCP, f_ctl, 0);
+				lp->tt.seq_send(lp, csp, conf_frame);
 			}
 		}
-		lp->tt.exch_done(sp);
+		lp->tt.exch_done(seq);
 	}
 	fc_io_compl(fsp);
 }
@@ -1032,7 +980,7 @@ static void fc_fcp_abort_io(struct fc_lport *lp)
  * This is called by upper layer protocol.
  * Return   : zero for success and -1 for failure
  * Context  : called from queuecommand which can be called from process
- *            or scsi soft irq.
+ *	      or scsi soft irq.
  * Locks    : called with the host lock and irqs disabled.
  */
 static int fc_fcp_pkt_send(struct fc_lport *lp, struct fc_fcp_pkt *fsp)
@@ -1050,7 +998,7 @@ static int fc_fcp_pkt_send(struct fc_lport *lp, struct fc_fcp_pkt *fsp)
 	list_add_tail(&fsp->list, &si->scsi_pkt_queue);
 
 	spin_unlock_irq(lp->host->host_lock);
-	rc = fc_fcp_send_cmd(fsp);
+	rc = lp->tt.fcp_cmd_send(lp, fsp, fc_fcp_recv);
 	spin_lock_irq(lp->host->host_lock);
 	if (rc)
 		list_del(&fsp->list);
@@ -1058,55 +1006,51 @@ static int fc_fcp_pkt_send(struct fc_lport *lp, struct fc_fcp_pkt *fsp)
 	return rc;
 }
 
-static int fc_fcp_send_cmd(struct fc_fcp_pkt *fsp)
+static int fc_fcp_cmd_send(struct fc_lport *lp, struct fc_fcp_pkt *fsp,
+			   void (*resp)(struct fc_seq *,
+					struct fc_frame *fp,
+					void *arg))
 {
-	struct fc_lport *lp;
 	struct fc_frame *fp;
-	struct fc_seq *sp;
+	struct fc_seq *seq;
 	struct fc_rport *rport;
 	struct fc_rport_libfc_priv *rp;
+	const size_t len = sizeof(fsp->cdb_cmd);
 	int rc = 0;
 
 	if (fc_fcp_lock_pkt(fsp))
 		return 0;
 
-	if (fsp->state & FC_SRB_COMPL)
-		goto unlock;
-
-	lp = fsp->lp;
 	fp = fc_frame_alloc(lp, sizeof(fsp->cdb_cmd));
 	if (!fp) {
 		rc = -1;
 		goto unlock;
 	}
 
-	memcpy(fc_frame_payload_get(fp, sizeof(fsp->cdb_cmd)),
-	       &fsp->cdb_cmd, sizeof(fsp->cdb_cmd));
-	fc_frame_setup(fp, FC_RCTL_DD_UNSOL_CMD, FC_TYPE_FCP);
-	fc_frame_set_offset(fp, 0);
+	memcpy(fc_frame_payload_get(fp, len), &fsp->cdb_cmd, len);
+	fr_cmd(fp) = fsp->cmd;
 	rport = fsp->rport;
 	fsp->max_payload = rport->maxframe_size;
 	rp = rport->dd_data;
-	sp = lp->tt.exch_seq_send(lp, fp,
-				  fc_fcp_recv,
-				  fc_fcp_pkt_destroy,
-				  fsp, 0,
-				  fc_host_port_id(rp->local_port->host),
-				  rport->port_id,
-				  FC_FC_SEQ_INIT | FC_FC_END_SEQ);
-	if (!sp) {
+
+	fc_fill_fc_hdr(fp, FC_RCTL_DD_UNSOL_CMD, rport->port_id,
+		       fc_host_port_id(rp->local_port->host), FC_TYPE_FCP,
+		       FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0);
+
+	seq = lp->tt.exch_seq_send(lp, fp, resp, fc_fcp_pkt_destroy, fsp, 0);
+	if (!seq) {
 		fc_frame_free(fp);
 		rc = -1;
 		goto unlock;
 	}
 	fsp->last_pkt_time = jiffies;
-	fsp->seq_ptr = sp;
+	fsp->seq_ptr = seq;
 	fc_fcp_pkt_hold(fsp);	/* hold for fc_fcp_pkt_destroy */
 
 	setup_timer(&fsp->timer, fc_fcp_timeout, (unsigned long)fsp);
 	fc_fcp_timer_set(fsp,
-			(fsp->tgt_flags & FC_RP_FLAGS_REC_SUPPORTED) ?
-			FC_SCSI_REC_TOV : FC_SCSI_ER_TIMEOUT);
+			 (fsp->tgt_flags & FC_RP_FLAGS_REC_SUPPORTED) ?
+			 FC_SCSI_REC_TOV : FC_SCSI_ER_TIMEOUT);
 unlock:
 	fc_fcp_unlock_pkt(fsp);
 	return rc;
@@ -1135,7 +1079,7 @@ static void fc_fcp_error(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
 	 */
 	fsp->state &= ~FC_SRB_ABORT_PENDING;
 	fsp->status_code = FC_CMD_PLOGO;
-	fc_fcp_complete(fsp);
+	fc_fcp_complete_locked(fsp);
 unlock:
 	fc_fcp_unlock_pkt(fsp);
 }
@@ -1165,7 +1109,7 @@ static int fc_fcp_pkt_abort(struct fc_lport *lp, struct fc_fcp_pkt *fsp)
 	} else if (fsp->state & FC_SRB_ABORTED) {
 		FC_DBG("target abort cmd  passed\n");
 		rc = SUCCESS;
-		fc_fcp_complete(fsp);
+		fc_fcp_complete_locked(fsp);
 	}
 
 	return rc;
@@ -1177,47 +1121,16 @@ static int fc_fcp_pkt_abort(struct fc_lport *lp, struct fc_fcp_pkt *fsp)
 static void fc_lun_reset_send(unsigned long data)
 {
 	struct fc_fcp_pkt *fsp = (struct fc_fcp_pkt *)data;
-	const size_t len = sizeof(fsp->cdb_cmd);
 	struct fc_lport *lp = fsp->lp;
-	struct fc_frame *fp;
-	struct fc_seq  *sp;
-	struct fc_rport *rport;
-	struct fc_rport_libfc_priv *rp;
-
-	spin_lock_bh(&fsp->scsi_pkt_lock);
-	if (fsp->state & FC_SRB_COMPL)
-		goto unlock;
-
-	fp = fc_frame_alloc(lp, len);
-	if (!fp)
-		goto retry;
-	memcpy(fc_frame_payload_get(fp, len), &fsp->cdb_cmd, len);
-	fc_frame_setup(fp, FC_RCTL_DD_UNSOL_CMD, FC_TYPE_FCP);
-	fc_frame_set_offset(fp, 0);
-	rport = fsp->rport;
-	rp = rport->dd_data;
-	sp = lp->tt.exch_seq_send(lp, fp,
-				  fc_tm_done,
-				  fc_fcp_pkt_destroy,
-				  fsp, 0,
-				  fc_host_port_id(rp->local_port->host),
-				  rport->port_id,
-				  FC_FC_SEQ_INIT | FC_FC_END_SEQ);
-
-	if (sp) {
-		fsp->seq_ptr = sp;
-		fc_fcp_pkt_hold(fsp);	/* hold for fc_fcp_pkt_destroy */
-		goto unlock;
+	if (lp->tt.fcp_cmd_send(lp, fsp, fc_tm_done)) {
+		if (fsp->recov_retry++ >= FC_MAX_RECOV_RETRY)
+			return;
+		if (fc_fcp_lock_pkt(fsp))
+			return;
+		setup_timer(&fsp->timer, fc_lun_reset_send, (unsigned long)fsp);
+		fc_fcp_timer_set(fsp, FC_SCSI_REC_TOV);
+		fc_fcp_unlock_pkt(fsp);
 	}
-	/*
-	 * Exchange or frame allocation failed.	 Set timer and retry.
-	 */
-	fc_frame_free(fp);
-retry:
-	setup_timer(&fsp->timer, fc_lun_reset_send, (unsigned long)fsp);
-	fc_fcp_timer_set(fsp, FC_SCSI_REC_TOV);
-unlock:
-	spin_unlock_bh(&fsp->scsi_pkt_lock);
 }
 
 /*
@@ -1275,12 +1188,11 @@ static int fc_lun_reset(struct fc_lport *lp, struct fc_fcp_pkt *fsp,
 /*
  * Task Managment response handler
  */
-static void fc_tm_done(struct fc_seq *sp, struct fc_frame *fp, void *arg)
+static void fc_tm_done(struct fc_seq *seq, struct fc_frame *fp, void *arg)
 {
 	struct fc_fcp_pkt *fsp = arg;
 	struct fc_frame_header *fh;
 
-	spin_lock_bh(&fsp->scsi_pkt_lock);
 	if (IS_ERR(fp)) {
 		/*
 		 * If there is an error just let it timeout or wait
@@ -1288,15 +1200,16 @@ static void fc_tm_done(struct fc_seq *sp, struct fc_frame *fp, void *arg)
 		 *
 		 * scsi-eh will escalate for when either happens.
 		 */
-		spin_unlock_bh(&fsp->scsi_pkt_lock);
 		return;
 	}
 
+	if (fc_fcp_lock_pkt(fsp))
+		return;
+
 	/*
 	 * raced with eh timeout handler.
 	 */
-	if ((fsp->state & FC_SRB_COMPL) || !fsp->seq_ptr ||
-	    !fsp->wait_for_comp) {
+	if (!fsp->seq_ptr || !fsp->wait_for_comp) {
 		spin_unlock_bh(&fsp->scsi_pkt_lock);
 		return;
 	}
@@ -1305,9 +1218,9 @@ static void fc_tm_done(struct fc_seq *sp, struct fc_frame *fp, void *arg)
 	if (fh->fh_type != FC_TYPE_BLS)
 		fc_fcp_resp(fsp, fp);
 	fsp->seq_ptr = NULL;
-	fsp->lp->tt.exch_done(sp);
+	fsp->lp->tt.exch_done(seq);
 	fc_frame_free(fp);
-	spin_unlock_bh(&fsp->scsi_pkt_lock);
+	fc_fcp_unlock_pkt(fsp);
 }
 
 static void fc_fcp_cleanup(struct fc_lport *lp)
@@ -1342,8 +1255,9 @@ static void fc_fcp_timeout(unsigned long data)
 	if (fc_fcp_lock_pkt(fsp))
 		return;
 
-	if (fsp->state & FC_SRB_COMPL)
+	if (fsp->cdb_cmd.fc_tm_flags)
 		goto unlock;
+
 	fsp->state |= FC_SRB_FCP_PROCESSING_TMO;
 
 	if (rp->flags & FC_RP_FLAGS_REC_SUPPORTED)
@@ -1352,7 +1266,7 @@ static void fc_fcp_timeout(unsigned long data)
 			       jiffies))
 		fc_fcp_timer_set(fsp, FC_SCSI_ER_TIMEOUT);
 	else if (fsp->state & FC_SRB_RCV_STATUS)
-		fc_fcp_complete(fsp);
+		fc_fcp_complete_locked(fsp);
 	else
 		fc_timeout_error(fsp);
 	fsp->state &= ~FC_SRB_FCP_PROCESSING_TMO;
@@ -1366,50 +1280,33 @@ unlock:
 static void fc_fcp_rec(struct fc_fcp_pkt *fsp)
 {
 	struct fc_lport *lp;
-	struct fc_seq *sp;
 	struct fc_frame *fp;
-	struct fc_els_rec *rec;
 	struct fc_rport *rport;
 	struct fc_rport_libfc_priv *rp;
-	u16 ox_id;
-	u16 rx_id;
 
 	lp = fsp->lp;
 	rport = fsp->rport;
 	rp = rport->dd_data;
-	sp = fsp->seq_ptr;
-	if (!sp || rp->rp_state != RPORT_ST_READY) {
+	if (!fsp->seq_ptr || rp->rp_state != RPORT_ST_READY) {
 		fsp->status_code = FC_HRD_ERROR;
 		fsp->io_status = SUGGEST_RETRY << 24;
-		fc_fcp_complete(fsp);
+		fc_fcp_complete_locked(fsp);
 		return;
 	}
-	lp->tt.seq_get_xids(sp, &ox_id, &rx_id);
-	fp = fc_frame_alloc(lp, sizeof(*rec));
+	fp = fc_frame_alloc(lp, sizeof(struct fc_els_rec));
 	if (!fp)
 		goto retry;
 
-	rec = fc_frame_payload_get(fp, sizeof(*rec));
-	memset(rec, 0, sizeof(*rec));
-	rec->rec_cmd = ELS_REC;
-	hton24(rec->rec_s_id, fc_host_port_id(lp->host));
-	rec->rec_ox_id = htons(ox_id);
-	rec->rec_rx_id = htons(rx_id);
-
-	fc_frame_setup(fp, FC_RCTL_ELS_REQ, FC_TYPE_ELS);
-	fc_frame_set_offset(fp, 0);
-	sp = lp->tt.exch_seq_send(lp, fp,
-				  fc_fcp_rec_resp, NULL,
-				  fsp, jiffies_to_msecs(FC_SCSI_REC_TOV),
-				  fc_host_port_id(rp->local_port->host),
-				  rport->port_id,
-				  FC_FC_SEQ_INIT | FC_FC_END_SEQ);
-
-	if (sp) {
+	fr_seq(fp) = fsp->seq_ptr;
+	fc_fill_fc_hdr(fp, FC_RCTL_ELS_REQ, rport->port_id,
+		       fc_host_port_id(rp->local_port->host), FC_TYPE_ELS,
+		       FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0);
+	if (lp->tt.elsct_send(lp, rport, fp, ELS_REC, fc_fcp_rec_resp,
+			      fsp, jiffies_to_msecs(FC_SCSI_REC_TOV))) {
 		fc_fcp_pkt_hold(fsp);		/* hold while REC outstanding */
 		return;
-	} else
-		fc_frame_free(fp);
+	}
+	fc_frame_free(fp);
 retry:
 	if (fsp->recov_retry++ < FC_MAX_RECOV_RETRY)
 		fc_fcp_timer_set(fsp, FC_SCSI_REC_TOV);
@@ -1424,7 +1321,7 @@ retry:
  * then set the timeout and return otherwise complete the exchange
  * and tell the scsi layer to restart the I/O.
  */
-static void fc_fcp_rec_resp(struct fc_seq *sp, struct fc_frame *fp, void *arg)
+static void fc_fcp_rec_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg)
 {
 	struct fc_fcp_pkt *fsp = (struct fc_fcp_pkt *)arg;
 	struct fc_els_rec_acc *recp;
@@ -1627,17 +1524,15 @@ static void fc_fcp_srr(struct fc_fcp_pkt *fsp, enum fc_rctl r_ctl, u32 offset)
 	struct fc_lport *lp = fsp->lp;
 	struct fc_rport *rport;
 	struct fc_rport_libfc_priv *rp;
-	struct fc_seq *sp;
+	struct fc_exch *ep = fc_seq_exch(fsp->seq_ptr);
+	struct fc_seq *seq;
 	struct fcp_srr *srr;
 	struct fc_frame *fp;
 	u8 cdb_op;
-	u16 ox_id;
-	u16 rx_id;
 
 	rport = fsp->rport;
 	rp = rport->dd_data;
 	cdb_op = fsp->cdb_cmd.fc_cdb[0];
-	lp->tt.seq_get_xids(fsp->seq_ptr, &ox_id, &rx_id);
 
 	if (!(rp->flags & FC_RP_FLAGS_RETRY) || rp->rp_state != RPORT_ST_READY)
 		goto retry;			/* shouldn't happen */
@@ -1648,24 +1543,22 @@ static void fc_fcp_srr(struct fc_fcp_pkt *fsp, enum fc_rctl r_ctl, u32 offset)
 	srr = fc_frame_payload_get(fp, sizeof(*srr));
 	memset(srr, 0, sizeof(*srr));
 	srr->srr_op = ELS_SRR;
-	srr->srr_ox_id = htons(ox_id);
-	srr->srr_rx_id = htons(rx_id);
+	srr->srr_ox_id = htons(ep->oxid);
+	srr->srr_rx_id = htons(ep->rxid);
 	srr->srr_r_ctl = r_ctl;
 	srr->srr_rel_off = htonl(offset);
 
-	fc_frame_setup(fp, FC_RCTL_ELS4_REQ, FC_TYPE_FCP);
-	fc_frame_set_offset(fp, 0);
-	sp = lp->tt.exch_seq_send(lp, fp,
-				  fc_fcp_srr_resp, NULL,
-				  fsp, jiffies_to_msecs(FC_SCSI_REC_TOV),
-				  fc_host_port_id(rp->local_port->host),
-				  rport->port_id,
-				  FC_FC_SEQ_INIT | FC_FC_END_SEQ);
-	if (!sp) {
+	fc_fill_fc_hdr(fp, FC_RCTL_ELS4_REQ, rport->port_id,
+		       fc_host_port_id(rp->local_port->host), FC_TYPE_FCP,
+		       FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0);
+
+	seq = lp->tt.exch_seq_send(lp, fp, fc_fcp_srr_resp, NULL,
+				   fsp, jiffies_to_msecs(FC_SCSI_REC_TOV));
+	if (!seq) {
 		fc_frame_free(fp);
 		goto retry;
 	}
-	fsp->recov_seq = sp;
+	fsp->recov_seq = seq;
 	fsp->xfer_len = offset;
 	fsp->xfer_contig_end = offset;
 	fsp->state &= ~FC_SRB_RCV_STATUS;
@@ -1678,12 +1571,10 @@ retry:
 /*
  * Handle response from SRR.
  */
-static void fc_fcp_srr_resp(struct fc_seq *sp, struct fc_frame *fp, void *arg)
+static void fc_fcp_srr_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg)
 {
 	struct fc_fcp_pkt *fsp = arg;
 	struct fc_frame_header *fh;
-	u16 ox_id;
-	u16 rx_id;
 
 	if (IS_ERR(fp)) {
 		fc_fcp_srr_error(fsp, fp);
@@ -1707,8 +1598,6 @@ static void fc_fcp_srr_resp(struct fc_seq *sp, struct fc_frame *fp, void *arg)
 	}
 
 	fsp->recov_seq = NULL;
-
-	fsp->lp->tt.seq_get_xids(fsp->seq_ptr, &ox_id, &rx_id);
 	switch (fc_frame_payload_op(fp)) {
 	case ELS_LS_ACC:
 		fsp->recov_retry = 0;
@@ -1720,7 +1609,7 @@ static void fc_fcp_srr_resp(struct fc_seq *sp, struct fc_frame *fp, void *arg)
 		break;
 	}
 	fc_fcp_unlock_pkt(fsp);
-	fsp->lp->tt.exch_done(sp);
+	fsp->lp->tt.exch_done(seq);
 out:
 	fc_frame_free(fp);
 	fc_fcp_pkt_release(fsp);	/* drop hold for outstanding SRR */
@@ -1768,7 +1657,7 @@ int fc_queuecommand(struct scsi_cmnd *sc_cmd, void (*done)(struct scsi_cmnd *))
 {
 	struct fc_lport *lp;
 	struct fc_rport *rport = starget_to_rport(scsi_target(sc_cmd->device));
-	struct fc_fcp_pkt *sp;
+	struct fc_fcp_pkt *fsp;
 	struct fc_rport_libfc_priv *rp;
 	int rval;
 	int rc = 0;
@@ -1800,8 +1689,8 @@ int fc_queuecommand(struct scsi_cmnd *sc_cmd, void (*done)(struct scsi_cmnd *))
 		goto out;
 	}
 
-	sp = fc_fcp_pkt_alloc(lp, GFP_ATOMIC);
-	if (sp == NULL) {
+	fsp = fc_fcp_pkt_alloc(lp, GFP_ATOMIC);
+	if (fsp == NULL) {
 		rc = SCSI_MLQUEUE_HOST_BUSY;
 		goto out;
 	}
@@ -1809,53 +1698,53 @@ int fc_queuecommand(struct scsi_cmnd *sc_cmd, void (*done)(struct scsi_cmnd *))
 	/*
 	 * build the libfc request pkt
 	 */
-	sp->cmd = sc_cmd;	/* save the cmd */
-	sp->lp = lp;		/* save the softc ptr */
-	sp->rport = rport;	/* set the remote port ptr */
+	fsp->cmd = sc_cmd;	/* save the cmd */
+	fsp->lp = lp;		/* save the softc ptr */
+	fsp->rport = rport;	/* set the remote port ptr */
 	sc_cmd->scsi_done = done;
 
 	/*
 	 * set up the transfer length
 	 */
-	sp->data_len = scsi_bufflen(sc_cmd);
-	sp->xfer_len = 0;
+	fsp->data_len = scsi_bufflen(sc_cmd);
+	fsp->xfer_len = 0;
 
 	/*
 	 * setup the data direction
 	 */
 	stats = lp->dev_stats[smp_processor_id()];
 	if (sc_cmd->sc_data_direction == DMA_FROM_DEVICE) {
-		sp->req_flags = FC_SRB_READ;
+		fsp->req_flags = FC_SRB_READ;
 		if (stats) {
 			stats->InputRequests++;
-			stats->InputMegabytes += sp->data_len;
+			stats->InputMegabytes = fsp->data_len;
 		}
 	} else if (sc_cmd->sc_data_direction == DMA_TO_DEVICE) {
-		sp->req_flags = FC_SRB_WRITE;
+		fsp->req_flags = FC_SRB_WRITE;
 		if (stats) {
 			stats->OutputRequests++;
-			stats->OutputMegabytes = sp->data_len;
+			stats->OutputMegabytes = fsp->data_len;
 		}
 	} else {
-		sp->req_flags = 0;
+		fsp->req_flags = 0;
 		if (stats)
 			stats->ControlRequests++;
 	}
 
-	sp->tgt_flags = rp->flags;
+	fsp->tgt_flags = rp->flags;
 
-	init_timer(&sp->timer);
-	sp->timer.data = (unsigned long)sp;
+	init_timer(&fsp->timer);
+	fsp->timer.data = (unsigned long)fsp;
 
 	/*
 	 * send it to the lower layer
 	 * if we get -1 return then put the request in the pending
 	 * queue.
 	 */
-	rval = fc_fcp_pkt_send(lp, sp);
+	rval = fc_fcp_pkt_send(lp, fsp);
 	if (rval != 0) {
-		sp->state = FC_SRB_FREE;
-		fc_fcp_pkt_release(sp);
+		fsp->state = FC_SRB_FREE;
+		fc_fcp_pkt_release(fsp);
 		rc = SCSI_MLQUEUE_HOST_BUSY;
 	}
 out:
@@ -1865,30 +1754,30 @@ EXPORT_SYMBOL(fc_queuecommand);
 
 /**
  * fc_io_compl -  Handle responses for completed commands
- * @sp:		scsi packet
+ * @fsp:	scsi packet
  *
  * Translates a error to a Linux SCSI error.
  *
  * The fcp packet lock must be held when calling.
  */
-static void fc_io_compl(struct fc_fcp_pkt *sp)
+static void fc_io_compl(struct fc_fcp_pkt *fsp)
 {
 	struct fc_fcp_internal *si;
 	struct scsi_cmnd *sc_cmd;
 	struct fc_lport *lp;
 	unsigned long flags;
 
-	sp->state |= FC_SRB_COMPL;
-	if (!(sp->state & FC_SRB_FCP_PROCESSING_TMO)) {
-		spin_unlock_bh(&sp->scsi_pkt_lock);
-		del_timer_sync(&sp->timer);
-		spin_lock_bh(&sp->scsi_pkt_lock);
+	fsp->state |= FC_SRB_COMPL;
+	if (!(fsp->state & FC_SRB_FCP_PROCESSING_TMO)) {
+		spin_unlock_bh(&fsp->scsi_pkt_lock);
+		del_timer_sync(&fsp->timer);
+		spin_lock_bh(&fsp->scsi_pkt_lock);
 	}
 
-	lp = sp->lp;
+	lp = fsp->lp;
 	si = fc_get_scsi_internal(lp);
 	spin_lock_irqsave(lp->host->host_lock, flags);
-	if (!sp->cmd) {
+	if (!fsp->cmd) {
 		spin_unlock_irqrestore(lp->host->host_lock, flags);
 		return;
 	}
@@ -1899,28 +1788,28 @@ static void fc_io_compl(struct fc_fcp_pkt *sp)
 	 * try again so clear the throttled flag incase we get more
 	 * time outs.
 	 */
-	if (si->throttled && sp->state & FC_SRB_NOMEM)
+	if (si->throttled && fsp->state & FC_SRB_NOMEM)
 		si->throttled = 0;
 
-	sc_cmd = sp->cmd;
-	sp->cmd = NULL;
+	sc_cmd = fsp->cmd;
+	fsp->cmd = NULL;
 
 	if (!sc_cmd->SCp.ptr) {
 		spin_unlock_irqrestore(lp->host->host_lock, flags);
 		return;
 	}
 
-	CMD_SCSI_STATUS(sc_cmd) = sp->cdb_status;
-	switch (sp->status_code) {
+	CMD_SCSI_STATUS(sc_cmd) = fsp->cdb_status;
+	switch (fsp->status_code) {
 	case FC_COMPLETE:
-		if (sp->cdb_status == 0) {
+		if (fsp->cdb_status == 0) {
 			/*
 			 * good I/O status
 			 */
 			sc_cmd->result = DID_OK << 16;
-			if (sp->scsi_resid)
-				CMD_RESID_LEN(sc_cmd) = sp->scsi_resid;
-		} else if (sp->cdb_status == QUEUE_FULL) {
+			if (fsp->scsi_resid)
+				CMD_RESID_LEN(sc_cmd) = fsp->scsi_resid;
+		} else if (fsp->cdb_status == QUEUE_FULL) {
 			struct scsi_device *tmp_sdev;
 			struct scsi_device *sdev = sc_cmd->device;
 
@@ -1934,44 +1823,44 @@ static void fc_io_compl(struct fc_fcp_pkt *sp)
 							      queue_depth - 1);
 				}
 			}
-			sc_cmd->result = (DID_OK << 16) | sp->cdb_status;
+			sc_cmd->result = (DID_OK << 16) | fsp->cdb_status;
 		} else {
 			/*
 			 * transport level I/O was ok but scsi
 			 * has non zero status
 			 */
-			sc_cmd->result = (DID_OK << 16) | sp->cdb_status;
+			sc_cmd->result = (DID_OK << 16) | fsp->cdb_status;
 		}
 		break;
 	case FC_ERROR:
 		sc_cmd->result = DID_ERROR << 16;
 		break;
 	case FC_DATA_UNDRUN:
-		if (sp->cdb_status == 0) {
+		if (fsp->cdb_status == 0) {
 			/*
 			 * scsi status is good but transport level
 			 * underrun. for read it should be an error??
 			 */
-			sc_cmd->result = (DID_OK << 16) | sp->cdb_status;
+			sc_cmd->result = (DID_OK << 16) | fsp->cdb_status;
 		} else {
 			/*
 			 * scsi got underrun, this is an error
 			 */
-			CMD_RESID_LEN(sc_cmd) = sp->scsi_resid;
-			sc_cmd->result = (DID_ERROR << 16) | sp->cdb_status;
+			CMD_RESID_LEN(sc_cmd) = fsp->scsi_resid;
+			sc_cmd->result = (DID_ERROR << 16) | fsp->cdb_status;
 		}
 		break;
 	case FC_DATA_OVRRUN:
 		/*
 		 * overrun is an error
 		 */
-		sc_cmd->result = (DID_ERROR << 16) | sp->cdb_status;
+		sc_cmd->result = (DID_ERROR << 16) | fsp->cdb_status;
 		break;
 	case FC_CMD_ABORTED:
-		sc_cmd->result = (DID_ABORT << 16) | sp->io_status;
+		sc_cmd->result = (DID_ABORT << 16) | fsp->io_status;
 		break;
 	case FC_CMD_TIME_OUT:
-		sc_cmd->result = (DID_BUS_BUSY << 16) | sp->io_status;
+		sc_cmd->result = (DID_BUS_BUSY << 16) | fsp->io_status;
 		break;
 	case FC_CMD_RESET:
 		sc_cmd->result = (DID_RESET << 16);
@@ -1984,16 +1873,33 @@ static void fc_io_compl(struct fc_fcp_pkt *sp)
 		break;
 	}
 
-	list_del(&sp->list);
+	list_del(&fsp->list);
 	sc_cmd->SCp.ptr = NULL;
 	sc_cmd->scsi_done(sc_cmd);
 	spin_unlock_irqrestore(lp->host->host_lock, flags);
 
 	/* release ref from initial allocation in queue command */
-	fc_fcp_pkt_release(sp);
+	fc_fcp_pkt_release(fsp);
 }
 
 /**
+ * fc_fcp_complete - complete processing of a fcp packet
+ * @fsp:	fcp packet
+ *
+ * This function may sleep if a fsp timer is pending.
+ * The host lock must not be held by caller.
+ */
+void fc_fcp_complete(struct fc_fcp_pkt *fsp)
+{
+	if (fc_fcp_lock_pkt(fsp))
+		return;
+
+	fc_fcp_complete_locked(fsp);
+	fc_fcp_unlock_pkt(fsp);
+}
+EXPORT_SYMBOL(fc_fcp_complete);
+
+/**
  * fc_eh_abort - Abort a command...from scsi host template
  * @sc_cmd:	scsi command to abort
  *
@@ -2002,7 +1908,7 @@ static void fc_io_compl(struct fc_fcp_pkt *sp)
  */
 int fc_eh_abort(struct scsi_cmnd *sc_cmd)
 {
-	struct fc_fcp_pkt *sp;
+	struct fc_fcp_pkt *fsp;
 	struct fc_lport *lp;
 	int rc = FAILED;
 	unsigned long flags;
@@ -2014,27 +1920,27 @@ int fc_eh_abort(struct scsi_cmnd *sc_cmd)
 		return rc;
 
 	spin_lock_irqsave(lp->host->host_lock, flags);
-	sp = CMD_SP(sc_cmd);
-	if (!sp) {
+	fsp = CMD_SP(sc_cmd);
+	if (!fsp) {
 		/* command completed while scsi eh was setting up */
 		spin_unlock_irqrestore(lp->host->host_lock, flags);
 		return SUCCESS;
 	}
-	/* grab a ref so the sp and sc_cmd cannot be relased from under us */
-	fc_fcp_pkt_hold(sp);
+	/* grab a ref so the fsp and sc_cmd cannot be relased from under us */
+	fc_fcp_pkt_hold(fsp);
 	spin_unlock_irqrestore(lp->host->host_lock, flags);
 
-	if (fc_fcp_lock_pkt(sp)) {
+	if (fc_fcp_lock_pkt(fsp)) {
 		/* completed while we were waiting for timer to be deleted */
 		rc = SUCCESS;
 		goto release_pkt;
 	}
 
-	rc = fc_fcp_pkt_abort(lp, sp);
-	fc_fcp_unlock_pkt(sp);
+	rc = fc_fcp_pkt_abort(lp, fsp);
+	fc_fcp_unlock_pkt(fsp);
 
 release_pkt:
-	fc_fcp_pkt_release(sp);
+	fc_fcp_pkt_release(fsp);
 	return rc;
 }
 EXPORT_SYMBOL(fc_eh_abort);
@@ -2049,7 +1955,7 @@ EXPORT_SYMBOL(fc_eh_abort);
 int fc_eh_device_reset(struct scsi_cmnd *sc_cmd)
 {
 	struct fc_lport *lp;
-	struct fc_fcp_pkt *sp;
+	struct fc_fcp_pkt *fsp;
 	struct fc_rport *rport = starget_to_rport(scsi_target(sc_cmd->device));
 	int rc = FAILED;
 	struct fc_rport_libfc_priv *rp;
@@ -2065,8 +1971,8 @@ int fc_eh_device_reset(struct scsi_cmnd *sc_cmd)
 	if (lp->state != LPORT_ST_READY)
 		return rc;
 
-	sp = fc_fcp_pkt_alloc(lp, GFP_NOIO);
-	if (sp == NULL) {
+	fsp = fc_fcp_pkt_alloc(lp, GFP_NOIO);
+	if (fsp == NULL) {
 		FC_DBG("could not allocate scsi_pkt\n");
 		sc_cmd->result = DID_NO_CONNECT << 16;
 		goto out;
@@ -2077,15 +1983,15 @@ int fc_eh_device_reset(struct scsi_cmnd *sc_cmd)
 	 * the sc passed in is not setup for execution like when sent
 	 * through the queuecommand callout.
 	 */
-	sp->lp = lp;		/* save the softc ptr */
-	sp->rport = rport;	/* set the remote port ptr */
+	fsp->lp = lp;		/* save the softc ptr */
+	fsp->rport = rport;	/* set the remote port ptr */
 
 	/*
 	 * flush outstanding commands
 	 */
-	rc = fc_lun_reset(lp, sp, scmd_id(sc_cmd), sc_cmd->device->lun);
-	sp->state = FC_SRB_FREE;
-	fc_fcp_pkt_release(sp);
+	rc = fc_lun_reset(lp, fsp, scmd_id(sc_cmd), sc_cmd->device->lun);
+	fsp->state = FC_SRB_FREE;
+	fc_fcp_pkt_release(fsp);
 
 out:
 	return rc;
@@ -2112,7 +2018,7 @@ int fc_eh_host_reset(struct scsi_cmnd *sc_cmd)
 		return SUCCESS;
 	} else {
 		shost_printk(KERN_INFO, shost, "Host reset failed. "
-			    "lport not ready.\n");
+			     "lport not ready.\n");
 		return FAILED;
 	}
 }
@@ -2184,11 +2090,14 @@ int fc_fcp_init(struct fc_lport *lp)
 	int rc;
 	struct fc_fcp_internal *si;
 
-	if (!lp->tt.scsi_cleanup)
-		lp->tt.scsi_cleanup = fc_fcp_cleanup;
+	if (!lp->tt.fcp_cmd_send)
+		lp->tt.fcp_cmd_send = fc_fcp_cmd_send;
+
+	if (!lp->tt.fcp_cleanup)
+		lp->tt.fcp_cleanup = fc_fcp_cleanup;
 
-	if (!lp->tt.scsi_abort_io)
-		lp->tt.scsi_abort_io = fc_fcp_abort_io;
+	if (!lp->tt.fcp_abort_io)
+		lp->tt.fcp_abort_io = fc_fcp_abort_io;
 
 	si = kzalloc(sizeof(struct fc_fcp_internal), GFP_KERNEL);
 	if (!si)
diff --git a/drivers/scsi/libfc/fc_frame.c b/drivers/scsi/libfc/fc_frame.c
index 388dc6c..0bbeff2 100644
--- a/drivers/scsi/libfc/fc_frame.c
+++ b/drivers/scsi/libfc/fc_frame.c
@@ -25,7 +25,7 @@
 #include <linux/skbuff.h>
 #include <linux/crc32.h>
 
-#include <scsi/libfc/fc_frame.h>
+#include <scsi/fc_frame.h>
 
 /*
  * Check the CRC in a frame.
@@ -82,7 +82,8 @@ struct fc_frame *fc_frame_alloc_fill(struct fc_lport *lp, size_t payload_len)
 	if (fp) {
 		memset((char *) fr_hdr(fp) + payload_len, 0, fill);
 		/* trim is OK, we just allocated it so there are no fragments */
-		skb_trim(fp_skb(fp), payload_len + sizeof(struct fc_frame_header));
+		skb_trim(fp_skb(fp),
+			 payload_len + sizeof(struct fc_frame_header));
 	}
 	return fp;
 }
diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c
index edb6d39..4a9f54a 100644
--- a/drivers/scsi/libfc/fc_lport.c
+++ b/drivers/scsi/libfc/fc_lport.c
@@ -28,7 +28,7 @@
  * the lport to be reset before we fill out the frame header's port_id. The
  * problem is that a reset would cause the lport's port_id to reset to 0.
  * If we don't protect the lport we'd spew incorrect frames.
- * 
+ *
  * At the time of this writing there are two primary mutexes, one for the
  * lport and one for the rport. Since the lport uses the rport and makes
  * calls into that block the rport should never make calls that would cause
@@ -78,7 +78,8 @@
 
 #include <scsi/fc/fc_gs.h>
 
-#include <scsi/libfc/libfc.h>
+#include <scsi/libfc.h>
+#include <scsi/fc_encode.h>
 
 /* Fabric IDs to use for point-to-point mode, chosen on whims. */
 #define FC_LOCAL_PTP_FID_LO   0x010101
@@ -124,71 +125,59 @@ static int fc_frame_drop(struct fc_lport *lport, struct fc_frame *fp)
 }
 
 /**
- * fc_lport_lookup_rport - lookup a remote port by port_id
- * @lport: Fibre Channel host port instance
- * @port_id: remote port port_id to match
- */
-struct fc_rport *fc_lport_lookup_rport(const struct fc_lport *lport,
-				       u32 port_id)
-{
-	struct fc_rport *rport, *found;
-	struct fc_rport_libfc_priv *rdata;
-
-	found = NULL;
-
-	list_for_each_entry(rdata, &lport->rports, peers) {
-		rport = PRIV_TO_RPORT(rdata);
-		if (rport->port_id == port_id) {
-			found = rport;
-			get_device(&found->dev);
-			break;
-		}
-	}
-	return found;
-}
-
-
-
-/**
  * fc_lport_rport_event - Event handler for rport events
  * @lport: The lport which is receiving the event
  * @rport: The rport which the event has occured on
  * @event: The event that occured
  *
  * Locking Note: The rport lock should not be held when calling
- *               this function.
+ *		 this function.
  */
 static void fc_lport_rport_event(struct fc_lport *lport,
 				 struct fc_rport *rport,
 				 enum fc_lport_event event)
 {
-	struct fc_rport_libfc_priv *rdata = rport->dd_data;
-
 	FC_DEBUG_LPORT("Received a %d event for port (%6x)\n", event,
 		       rport->port_id);
 
-	mutex_lock(&lport->lp_mutex);
 	switch (event) {
-	case LPORT_EV_RPORT_CREATED:
+	case RPORT_EV_CREATED:
 		if (rport->port_id == FC_FID_DIR_SERV) {
-			lport->dns_rp = rport;
-			fc_lport_enter_rpn_id(lport);
-		} else {
-			list_add_tail(&rdata->peers, &lport->rports);
-		}
+			mutex_lock(&lport->lp_mutex);
+			if (lport->state == LPORT_ST_DNS) {
+				lport->dns_rp = rport;
+				fc_lport_enter_rpn_id(lport);
+			} else {
+				FC_DEBUG_LPORT("Received an CREATED event on "
+					       "port (%6x) for the directory "
+					       "server, but the lport is not "
+					       "in the DNS state, it's in the "
+					       "%d state", rport->port_id,
+					       lport->state);
+				lport->tt.rport_logoff(rport);
+			}
+			mutex_unlock(&lport->lp_mutex);
+		} else
+			FC_DEBUG_LPORT("Received an event for port (%6x) "
+				       "which is not the directory server\n",
+				       rport->port_id);
 		break;
-	case LPORT_EV_RPORT_LOGO:
-	case LPORT_EV_RPORT_FAILED:
-	case LPORT_EV_RPORT_STOP:
-		if (rport->port_id == FC_FID_DIR_SERV)
+	case RPORT_EV_LOGO:
+	case RPORT_EV_FAILED:
+	case RPORT_EV_STOP:
+		if (rport->port_id == FC_FID_DIR_SERV) {
+			mutex_lock(&lport->lp_mutex);
 			lport->dns_rp = NULL;
-		else
-			list_del(&rdata->peers);
+			mutex_unlock(&lport->lp_mutex);
+
+		} else
+			FC_DEBUG_LPORT("Received an event for port (%6x) "
+				       "which is not the directory server\n",
+				       rport->port_id);
 		break;
-	case LPORT_EV_RPORT_NONE:
+	case RPORT_EV_NONE:
 		break;
 	}
-	mutex_unlock(&lport->lp_mutex);
 }
 
 /**
@@ -225,7 +214,7 @@ static void fc_lport_ptp_setup(struct fc_lport *lport,
 	dp.ids.roles = FC_RPORT_ROLE_UNKNOWN;
 
 	if (lport->ptp_rp) {
-		lport->tt.rport_stop(lport->ptp_rp);
+		lport->tt.rport_logoff(lport->ptp_rp);
 		lport->ptp_rp = NULL;
 	}
 
@@ -380,6 +369,7 @@ static void fc_lport_recv_echo_req(struct fc_seq *sp, struct fc_frame *in_fp,
 				   struct fc_lport *lport)
 {
 	struct fc_frame *fp;
+	struct fc_exch *ep = fc_seq_exch(sp);
 	unsigned int len;
 	void *pp;
 	void *dp;
@@ -400,9 +390,10 @@ static void fc_lport_recv_echo_req(struct fc_seq *sp, struct fc_frame *in_fp,
 		memcpy(dp, pp, len);
 		*((u32 *)dp) = htonl(ELS_LS_ACC << 24);
 		sp = lport->tt.seq_start_next(sp);
-		f_ctl = FC_FC_LAST_SEQ | FC_FC_END_SEQ;
-		fc_frame_setup(fp, FC_RCTL_ELS_REP, FC_TYPE_ELS);
-		lport->tt.seq_send(lport, sp, fp, f_ctl);
+		f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ | FC_FC_END_SEQ;
+		fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid,
+			       FC_TYPE_ELS, f_ctl, 0);
+		lport->tt.seq_send(lport, sp, fp);
 	}
 	fc_frame_free(in_fp);
 }
@@ -420,6 +411,7 @@ static void fc_lport_recv_rnid_req(struct fc_seq *sp, struct fc_frame *in_fp,
 				   struct fc_lport *lport)
 {
 	struct fc_frame *fp;
+	struct fc_exch *ep = fc_seq_exch(sp);
 	struct fc_els_rnid *req;
 	struct {
 		struct fc_els_rnid_resp rnid;
@@ -463,9 +455,61 @@ static void fc_lport_recv_rnid_req(struct fc_seq *sp, struct fc_frame *in_fp,
 				       sizeof(rp->gen));
 			}
 			sp = lport->tt.seq_start_next(sp);
-			f_ctl = FC_FC_SEQ_INIT | FC_FC_LAST_SEQ | FC_FC_END_SEQ;
-			fc_frame_setup(fp, FC_RCTL_ELS_REP, FC_TYPE_ELS);
-			lport->tt.seq_send(lport, sp, fp, f_ctl);
+			f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ;
+			f_ctl |= FC_FC_END_SEQ | FC_FC_SEQ_INIT;
+			fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid,
+				       FC_TYPE_ELS, f_ctl, 0);
+			lport->tt.seq_send(lport, sp, fp);
+		}
+	}
+	fc_frame_free(in_fp);
+}
+
+/**
+ * fc_lport_recv_adisc_req - Handle received Address Discovery Request
+ * @lport: Fibre Channel local port recieving the ADISC
+ * @sp: current sequence in the ADISC exchange
+ * @fp: ADISC request frame
+ *
+ * Locking Note: The lport lock is exected to be held before calling
+ * this function.
+ */
+static void fc_lport_recv_adisc_req(struct fc_seq *sp, struct fc_frame *in_fp,
+				   struct fc_lport *lport)
+{
+	struct fc_frame *fp;
+	struct fc_exch *ep = fc_seq_exch(sp);
+	struct fc_els_adisc *req, *rp;
+	struct fc_seq_els_data rjt_data;
+	size_t len;
+	u32 f_ctl;
+
+	FC_DEBUG_LPORT("Received ADISC request while in state %s\n",
+		       fc_lport_state(lport));
+
+	req = fc_frame_payload_get(in_fp, sizeof(*req));
+	if (!req) {
+		rjt_data.fp = NULL;
+		rjt_data.reason = ELS_RJT_LOGIC;
+		rjt_data.explan = ELS_EXPL_NONE;
+		lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data);
+	} else {
+		len = sizeof(*rp);
+		fp = fc_frame_alloc(lport, len);
+		if (fp) {
+			rp = fc_frame_payload_get(fp, len);
+			memset(rp, 0, len);
+			rp->adisc_cmd = ELS_LS_ACC;
+			rp->adisc_wwpn = htonll(lport->wwpn);
+			rp->adisc_wwnn = htonll(lport->wwnn);
+			hton24(rp->adisc_port_id,
+			       fc_host_port_id(lport->host));
+			sp = lport->tt.seq_start_next(sp);
+			f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ;
+			f_ctl |= FC_FC_END_SEQ | FC_FC_SEQ_INIT;
+			fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid,
+				       FC_TYPE_ELS, f_ctl, 0);
+			lport->tt.seq_send(lport, sp, fp);
 		}
 	}
 	fc_frame_free(in_fp);
@@ -493,7 +537,7 @@ static void fc_lport_recv_logo_req(struct fc_seq *sp, struct fc_frame *fp,
  * @lport: The lport that should log into the fabric
  *
  * Locking Note: This function should not be called
- *               with the lport lock held.
+ *		 with the lport lock held.
  */
 int fc_fabric_login(struct fc_lport *lport)
 {
@@ -516,6 +560,9 @@ EXPORT_SYMBOL(fc_fabric_login);
  */
 void fc_linkup(struct fc_lport *lport)
 {
+	FC_DEBUG_LPORT("Link is up for port (%6x)\n",
+		       fc_host_port_id(lport->host));
+
 	mutex_lock(&lport->lp_mutex);
 	if ((lport->link_status & FC_LINK_UP) != FC_LINK_UP) {
 		lport->link_status |= FC_LINK_UP;
@@ -534,13 +581,14 @@ EXPORT_SYMBOL(fc_linkup);
 void fc_linkdown(struct fc_lport *lport)
 {
 	mutex_lock(&lport->lp_mutex);
+	FC_DEBUG_LPORT("Link is down for port (%6x)\n",
+		       fc_host_port_id(lport->host));
 
 	if ((lport->link_status & FC_LINK_UP) == FC_LINK_UP) {
 		lport->link_status &= ~(FC_LINK_UP);
 		fc_lport_enter_reset(lport);
-		lport->tt.scsi_cleanup(lport);
+		lport->tt.fcp_cleanup(lport);
 	}
-
 	mutex_unlock(&lport->lp_mutex);
 }
 EXPORT_SYMBOL(fc_linkdown);
@@ -578,9 +626,11 @@ EXPORT_SYMBOL(fc_unpause);
  **/
 int fc_fabric_logoff(struct fc_lport *lport)
 {
+	lport->tt.disc_stop_final(lport);
 	mutex_lock(&lport->lp_mutex);
+	kfree(lport->disc);
+	lport->disc = NULL;
 	fc_lport_enter_logo(lport);
-	lport->tt.scsi_cleanup(lport);
 	mutex_unlock(&lport->lp_mutex);
 	return 0;
 }
@@ -600,10 +650,8 @@ EXPORT_SYMBOL(fc_fabric_logoff);
  **/
 int fc_lport_destroy(struct fc_lport *lport)
 {
-	if (!cancel_delayed_work(&lport->disc_work))
-		flush_scheduled_work();
-	lport->tt.scsi_abort_io(lport);
 	lport->tt.frame_send = fc_frame_drop;
+	lport->tt.fcp_abort_io(lport);
 	lport->tt.exch_mgr_reset(lport->emp, 0, 0);
 	return 0;
 }
@@ -628,10 +676,8 @@ int fc_set_mfs(struct fc_lport *lport, u32 mfs)
 		rc = 0;
 	}
 
-	if (!rc && mfs < old_mfs) {
-		lport->disc_done = 0;
+	if (!rc && mfs < old_mfs)
 		fc_lport_enter_reset(lport);
-	}
 
 	mutex_unlock(&lport->lp_mutex);
 
@@ -640,6 +686,31 @@ int fc_set_mfs(struct fc_lport *lport, u32 mfs)
 EXPORT_SYMBOL(fc_set_mfs);
 
 /**
+ * fc_lport_disc_callback - Callback for discovery events
+ * @lport: FC local port
+ * @event: The discovery event
+ */
+void fc_lport_disc_callback(struct fc_lport *lport, enum fc_disc_event event)
+{
+	switch (event) {
+	case DISC_EV_SUCCESS:
+		FC_DEBUG_LPORT("Got a SUCCESS event for port (%6x)\n",
+			       fc_host_port_id(lport->host));
+		break;
+	case DISC_EV_FAILED:
+		FC_DEBUG_LPORT("Got a FAILED event for port (%6x)\n",
+			       fc_host_port_id(lport->host));
+		mutex_lock(&lport->lp_mutex);
+		fc_lport_enter_reset(lport);
+		mutex_unlock(&lport->lp_mutex);
+		break;
+	case DISC_EV_NONE:
+		WARN_ON(1);
+		break;
+	}
+}
+
+/**
  * fc_rport_enter_ready - Enter the ready state and start discovery
  * @lport: Fibre Channel local port that is ready
  *
@@ -653,7 +724,7 @@ static void fc_lport_enter_ready(struct fc_lport *lport)
 
 	fc_lport_state_enter(lport, LPORT_ST_READY);
 
-	lport->tt.disc_start(lport);
+	lport->tt.disc_start(fc_lport_disc_callback, lport);
 }
 
 /**
@@ -676,6 +747,7 @@ static void fc_lport_recv_flogi_req(struct fc_seq *sp_in,
 	struct fc_frame *fp;
 	struct fc_frame_header *fh;
 	struct fc_seq *sp;
+	struct fc_exch *ep;
 	struct fc_els_flogi *flp;
 	struct fc_els_flogi *new_flp;
 	u64 remote_wwpn;
@@ -726,9 +798,11 @@ static void fc_lport_recv_flogi_req(struct fc_seq *sp_in,
 		 * Send the response.  If this fails, the originator should
 		 * repeat the sequence.
 		 */
-		f_ctl = FC_FC_LAST_SEQ | FC_FC_END_SEQ;
-		fc_frame_setup(fp, FC_RCTL_ELS_REP, FC_TYPE_ELS);
-		lport->tt.seq_send(lport, sp, fp, f_ctl);
+		f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ | FC_FC_END_SEQ;
+		ep = fc_seq_exch(sp);
+		fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid,
+			       FC_TYPE_ELS, f_ctl, 0);
+		lport->tt.seq_send(lport, sp, fp);
 
 	} else {
 		fc_lport_error(lport, fp);
@@ -736,8 +810,8 @@ static void fc_lport_recv_flogi_req(struct fc_seq *sp_in,
 	fc_lport_ptp_setup(lport, remote_fid, remote_wwpn,
 			   ntohll(get_unaligned(&flp->fl_wwnn)));
 
-	if (lport->tt.disc_start(lport))
-		FC_DBG("target discovery start error\n");
+	lport->tt.disc_start(fc_lport_disc_callback, lport);
+
 out:
 	sp = fr_seq(rx_fp);
 	fc_frame_free(rx_fp);
@@ -753,7 +827,7 @@ out:
  * if an rport should handle the request.
  *
  * Locking Note: This function should not be called with the lport
- *               lock held becuase it will grab the lock.
+ *		 lock held becuase it will grab the lock.
  */
 static void fc_lport_recv_req(struct fc_lport *lport, struct fc_seq *sp,
 			      struct fc_frame *fp)
@@ -798,6 +872,9 @@ static void fc_lport_recv_req(struct fc_lport *lport, struct fc_seq *sp,
 		case ELS_RNID:
 			recv = fc_lport_recv_rnid_req;
 			break;
+		case ELS_ADISC:
+			recv = fc_lport_recv_adisc_req;
+			break;
 		}
 
 		if (recv)
@@ -810,7 +887,7 @@ static void fc_lport_recv_req(struct fc_lport *lport, struct fc_seq *sp,
 			s_id = ntoh24(fh->fh_s_id);
 			d_id = ntoh24(fh->fh_d_id);
 
-			rport = fc_lport_lookup_rport(lport, s_id);
+			rport = lport->tt.rport_lookup(lport, s_id);
 			if (rport) {
 				lport->tt.rport_recv_req(sp, fp, rport);
 				put_device(&rport->dev); /* hold from lookup */
@@ -842,7 +919,7 @@ static void fc_lport_recv_req(struct fc_lport *lport, struct fc_seq *sp,
  * @lport: The lport which should be reset
  *
  * Locking Note: This functions should not be called with the
- *               lport lock held.
+ *		 lport lock held.
  */
 int fc_lport_reset(struct fc_lport *lport)
 {
@@ -854,24 +931,6 @@ int fc_lport_reset(struct fc_lport *lport)
 EXPORT_SYMBOL(fc_lport_reset);
 
 /**
- * fc_lport_stop_rports - delete all the remote ports associated with the lport
- * @lport: libfc local port instance
- *
- * Locking Note: This function expects that the lport mutex is locked before
- * calling it.
- */
-void fc_lport_stop_rports(struct fc_lport *lport)
-{
-	struct fc_rport *rport;
-	struct fc_rport_libfc_priv *rdata;
-
-	list_for_each_entry(rdata, &lport->rports, peers) {
-		rport = PRIV_TO_RPORT(rdata);
-		lport->tt.rport_stop(rport);
-	}
-}
-
-/**
  * fc_rport_enter_reset - Reset the local port
  * @lport: Fibre Channel local port to be reset
  *
@@ -885,17 +944,15 @@ static void fc_lport_enter_reset(struct fc_lport *lport)
 
 	fc_lport_state_enter(lport, LPORT_ST_RESET);
 
-	if (lport->dns_rp) {
-		lport->tt.rport_stop(lport->dns_rp);
-		lport->dns_rp = NULL;
-	}
+	if (lport->dns_rp)
+		lport->tt.rport_logoff(lport->dns_rp);
 
 	if (lport->ptp_rp) {
-		lport->tt.rport_stop(lport->ptp_rp);
+		lport->tt.rport_logoff(lport->ptp_rp);
 		lport->ptp_rp = NULL;
 	}
 
-	fc_lport_stop_rports(lport);
+	lport->tt.disc_stop(lport);
 
 	lport->tt.exch_mgr_reset(lport->emp, 0, 0);
 	fc_host_fabric_name(lport->host) = 0;
@@ -954,7 +1011,7 @@ static void fc_lport_error(struct fc_lport *lport, struct fc_frame *fp)
 
 /**
  * fc_lport_rft_id_resp - Handle response to Register Fibre
- *                        Channel Types by ID (RPN_ID) request
+ *			  Channel Types by ID (RPN_ID) request
  * @sp: current sequence in RPN_ID exchange
  * @fp: response frame
  * @lp_arg: Fibre Channel host port instance
@@ -1006,7 +1063,7 @@ err:
 
 /**
  * fc_lport_rpn_id_resp - Handle response to Register Port
- *                        Name by ID (RPN_ID) request
+ *			  Name by ID (RPN_ID) request
  * @sp: current sequence in RPN_ID exchange
  * @fp: response frame
  * @lp_arg: Fibre Channel host port instance
@@ -1112,32 +1169,20 @@ err:
 static void fc_lport_enter_scr(struct fc_lport *lport)
 {
 	struct fc_frame *fp;
-	struct fc_els_scr *scr;
 
 	FC_DEBUG_LPORT("Port (%6x) entered SCR state from %s state\n",
 		       fc_host_port_id(lport->host), fc_lport_state(lport));
 
 	fc_lport_state_enter(lport, LPORT_ST_SCR);
 
-	fp = fc_frame_alloc(lport, sizeof(*scr));
+	fp = fc_frame_alloc(lport, sizeof(struct fc_els_scr));
 	if (!fp) {
 		fc_lport_error(lport, fp);
 		return;
 	}
 
-	scr = fc_frame_payload_get(fp, sizeof(*scr));
-	memset(scr, 0, sizeof(*scr));
-	scr->scr_cmd = ELS_SCR;
-	scr->scr_reg_func = ELS_SCRF_FULL;
-	fc_frame_setup(fp, FC_RCTL_ELS_REQ, FC_TYPE_ELS);
-	fc_frame_set_offset(fp, 0);
-
-	if (!lport->tt.exch_seq_send(lport, fp,
-				     fc_lport_scr_resp, NULL,
-				     lport, lport->e_d_tov,
-				     fc_host_port_id(lport->host),
-				     FC_FID_FCTRL,
-				     FC_FC_SEQ_INIT | FC_FC_END_SEQ))
+	if (!lport->tt.elsct_send(lport, NULL, fp, ELS_SCR,
+				  fc_lport_scr_resp, lport, lport->e_d_tov))
 		fc_lport_error(lport, fp);
 }
 
@@ -1151,11 +1196,6 @@ static void fc_lport_enter_scr(struct fc_lport *lport)
 static void fc_lport_enter_rft_id(struct fc_lport *lport)
 {
 	struct fc_frame *fp;
-	struct req {
-		struct fc_ct_hdr ct;
-		struct fc_ns_fid fid;	/* port ID object */
-		struct fc_ns_fts fts;	/* FC4-types object */
-	} *req;
 	struct fc_ns_fts *lps;
 	int i;
 
@@ -1172,31 +1212,20 @@ static void fc_lport_enter_rft_id(struct fc_lport *lport)
 	if (i < 0) {
 		/* nothing to register, move on to SCR */
 		fc_lport_enter_scr(lport);
-	} else {
-		fp = fc_frame_alloc(lport, sizeof(*req));
-		if (!fp) {
-			fc_lport_error(lport, fp);
-			return;
-		}
+		return;
+	}
 
-		req = fc_frame_payload_get(fp, sizeof(*req));
-		fc_fill_dns_hdr(lport, &req->ct,
-				FC_NS_RFT_ID,
-				sizeof(*req) -
-				sizeof(struct fc_ct_hdr));
-		hton24(req->fid.fp_fid, fc_host_port_id(lport->host));
-		req->fts = *lps;
-		fc_frame_setup(fp, FC_RCTL_DD_UNSOL_CTL, FC_TYPE_CT);
-
-		if (!lport->tt.exch_seq_send(lport, fp,
-					     fc_lport_rft_id_resp, NULL,
-					     lport, lport->e_d_tov,
-					     fc_host_port_id(lport->host),
-					     FC_FID_DIR_SERV,
-					     FC_FC_SEQ_INIT |
-					     FC_FC_END_SEQ))
-			fc_lport_error(lport, fp);
+	fp = fc_frame_alloc(lport, sizeof(struct fc_ct_hdr) +
+			    sizeof(struct fc_ns_rft));
+	if (!fp) {
+		fc_lport_error(lport, fp);
+		return;
 	}
+
+	if (!lport->tt.elsct_send(lport, NULL, fp, FC_NS_RFT_ID,
+				  fc_lport_rft_id_resp,
+				  lport, lport->e_d_tov))
+		fc_lport_error(lport, fp);
 }
 
 /**
@@ -1209,37 +1238,23 @@ static void fc_lport_enter_rft_id(struct fc_lport *lport)
 static void fc_lport_enter_rpn_id(struct fc_lport *lport)
 {
 	struct fc_frame *fp;
-	struct req {
-		struct fc_ct_hdr ct;
-		struct fc_ns_rn_id rn;
-	} *req;
 
 	FC_DEBUG_LPORT("Port (%6x) entered RPN_ID state from %s state\n",
 		       fc_host_port_id(lport->host), fc_lport_state(lport));
 
 	fc_lport_state_enter(lport, LPORT_ST_RPN_ID);
 
-	fp = fc_frame_alloc(lport, sizeof(*req));
+	fp = fc_frame_alloc(lport, sizeof(struct fc_ct_hdr) +
+			    sizeof(struct fc_ns_rn_id));
 	if (!fp) {
 		fc_lport_error(lport, fp);
 		return;
 	}
 
-	req = fc_frame_payload_get(fp, sizeof(*req));
-	memset(req, 0, sizeof(*req));
-	fc_fill_dns_hdr(lport, &req->ct, FC_NS_RPN_ID, sizeof(req->rn));
-	hton24(req->rn.fr_fid.fp_fid, fc_host_port_id(lport->host));
-	put_unaligned(htonll(lport->wwpn), &req->rn.fr_wwn);
-	fc_frame_setup(fp, FC_RCTL_DD_UNSOL_CTL, FC_TYPE_CT);
-
-	if (!lport->tt.exch_seq_send(lport, fp,
-				     fc_lport_rpn_id_resp, NULL,
-				     lport, lport->e_d_tov,
-				     fc_host_port_id(lport->host),
-				     FC_FID_DIR_SERV,
-				     FC_FC_SEQ_INIT | FC_FC_END_SEQ))
+	if (!lport->tt.elsct_send(lport, NULL, fp, FC_NS_RPN_ID,
+				  fc_lport_rpn_id_resp,
+				  lport, lport->e_d_tov))
 		fc_lport_error(lport, fp);
-
 }
 
 /**
@@ -1266,16 +1281,10 @@ static void fc_lport_enter_dns(struct fc_lport *lport)
 
 	fc_lport_state_enter(lport, LPORT_ST_DNS);
 
-	if (!lport->dns_rp) {
-		/* Set up a rogue rport to directory server */
-		rport = fc_rport_rogue_create(&dp);
-
-		if (!rport)
-			goto err;
-		lport->dns_rp = rport;
-	}
+	rport = fc_rport_rogue_create(&dp);
+	if (!rport)
+		goto err;
 
-	rport = lport->dns_rp;
 	rdata = rport->dd_data;
 	rdata->event_callback = fc_lport_rport_event;
 	lport->tt.rport_login(rport);
@@ -1388,10 +1397,8 @@ static void fc_lport_enter_logo(struct fc_lport *lport)
 	fc_lport_state_enter(lport, LPORT_ST_LOGO);
 
 	/* DNS session should be closed so we can release it here */
-	if (lport->dns_rp) {
-		lport->tt.rport_logout(lport->dns_rp);
-		lport->dns_rp = NULL;
-	}
+	if (lport->dns_rp)
+		lport->tt.rport_logoff(lport->dns_rp);
 
 	fp = fc_frame_alloc(lport, sizeof(*logo));
 	if (!fp) {
@@ -1399,19 +1406,8 @@ static void fc_lport_enter_logo(struct fc_lport *lport)
 		return;
 	}
 
-	logo = fc_frame_payload_get(fp, sizeof(*logo));
-	memset(logo, 0, sizeof(*logo));
-	logo->fl_cmd = ELS_LOGO;
-	hton24(logo->fl_n_port_id, fc_host_port_id(lport->host));
-	logo->fl_n_port_wwn = htonll(lport->wwpn);
-	fc_frame_setup(fp, FC_RCTL_ELS_REQ, FC_TYPE_ELS);
-	fc_frame_set_offset(fp, 0);
-
-	if (!lport->tt.exch_seq_send(lport, fp,
-				     fc_lport_logo_resp, NULL,
-				     lport, lport->e_d_tov,
-				     fc_host_port_id(lport->host), FC_FID_FLOGI,
-				     FC_FC_SEQ_INIT | FC_FC_END_SEQ))
+	if (!lport->tt.elsct_send(lport, NULL, fp, ELS_LOGO, fc_lport_logo_resp,
+				  lport, lport->e_d_tov))
 		fc_lport_error(lport, fp);
 }
 
@@ -1494,8 +1490,8 @@ static void fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp,
 		if (flp) {
 			csp_flags = ntohs(flp->fl_csp.sp_features);
 			if ((csp_flags & FC_SP_FT_FPORT) == 0) {
-				if (lport->tt.disc_start(lport))
-					FC_DBG("Target disc start error\n");
+				lport->tt.disc_start(fc_lport_disc_callback,
+						     lport);
 			}
 		}
 	} else {
@@ -1518,29 +1514,18 @@ err:
 void fc_lport_enter_flogi(struct fc_lport *lport)
 {
 	struct fc_frame *fp;
-	struct fc_els_flogi *flp;
 
 	FC_DEBUG_LPORT("Processing FLOGI state\n");
 
 	fc_lport_state_enter(lport, LPORT_ST_FLOGI);
 
-	fp = fc_frame_alloc(lport, sizeof(*flp));
+	fp = fc_frame_alloc(lport, sizeof(struct fc_els_flogi));
 	if (!fp)
 		return fc_lport_error(lport, fp);
 
-	flp = fc_frame_payload_get(fp, sizeof(*flp));
-	fc_lport_flogi_fill(lport, flp, ELS_FLOGI);
-
-	fc_frame_setup(fp, FC_RCTL_ELS_REQ, FC_TYPE_ELS);
-	fc_frame_set_offset(fp, 0);
-
-	if (!lport->tt.exch_seq_send(lport, fp,
-				     fc_lport_flogi_resp, NULL,
-				     lport, lport->e_d_tov,
-				     0, FC_FID_FLOGI,
-				     FC_FC_SEQ_INIT | FC_FC_END_SEQ))
+	if (!lport->tt.elsct_send(lport, NULL, fp, ELS_FLOGI,
+				  fc_lport_flogi_resp, lport, lport->e_d_tov))
 		fc_lport_error(lport, fp);
-
 }
 
 /* Configure a fc_lport */
@@ -1548,12 +1533,9 @@ int fc_lport_config(struct fc_lport *lport)
 {
 	INIT_WORK(&lport->retry_work, fc_lport_timeout, lport);
 	mutex_init(&lport->lp_mutex);
-	INIT_LIST_HEAD(&lport->rports);
 
 	fc_lport_state_enter(lport, LPORT_ST_NONE);
 
-	lport->disc_delay = DNS_DELAY;
-
 	fc_lport_add_fc4_type(lport, FC_TYPE_FCP);
 	fc_lport_add_fc4_type(lport, FC_TYPE_CT);
 
@@ -1569,12 +1551,6 @@ int fc_lport_init(struct fc_lport *lport)
 	if (!lport->tt.lport_reset)
 		lport->tt.lport_reset = fc_lport_reset;
 
-	if (!lport->tt.rport_lookup)
-		lport->tt.rport_lookup = fc_lport_lookup_rport;
-
-	if (!lport->tt.event_callback)
-		lport->tt.event_callback = fc_lport_rport_event;
-
 	fc_host_port_type(lport->host) = FC_PORTTYPE_NPORT;
 	fc_host_node_name(lport->host) = lport->wwnn;
 	fc_host_port_name(lport->host) = lport->wwpn;
diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c
index ab131a2..db867dd 100644
--- a/drivers/scsi/libfc/fc_rport.c
+++ b/drivers/scsi/libfc/fc_rport.c
@@ -36,9 +36,9 @@
  * The locking strategy is similar to the lport's strategy. The lock protects
  * the rport's states and is held and released by the entry points to the rport
  * block. All _enter_* functions correspond to rport states and expect the rport
- * mutex to be locked before calling them. This means that rports only handle one
- * request or response at a time, since they're not critical for the I/O path
- * this potential over-use of the mutex is acceptable.
+ * mutex to be locked before calling them. This means that rports only handle
+ * one request or response at a time, since they're not critical for the I/O
+ * path this potential over-use of the mutex is acceptable.
  */
 
 #include <linux/kernel.h>
@@ -49,7 +49,8 @@
 #include <linux/workqueue.h>
 #include <asm/unaligned.h>
 
-#include <scsi/libfc/libfc.h>
+#include <scsi/libfc.h>
+#include <scsi/fc_encode.h>
 
 static int fc_rport_debug;
 
@@ -59,7 +60,7 @@ static int fc_rport_debug;
 			FC_DBG(fmt);		\
 	} while (0)
 
-static struct workqueue_struct *rport_event_queue;
+struct workqueue_struct *rport_event_queue;
 
 static void fc_rport_enter_plogi(struct fc_rport *);
 static void fc_rport_enter_prli(struct fc_rport *);
@@ -122,7 +123,7 @@ struct fc_rport *fc_rport_rogue_create(struct fc_disc_port *dp)
 	rdata->local_port = dp->lp;
 	rdata->trans_state = FC_PORTSTATE_ROGUE;
 	rdata->rp_state = RPORT_ST_INIT;
-	rdata->event = LPORT_EV_RPORT_NONE;
+	rdata->event = RPORT_EV_NONE;
 	rdata->flags = FC_RP_FLAGS_REC_SUPPORTED;
 	rdata->event_callback = NULL;
 	rdata->e_d_tov = dp->lp->e_d_tov;
@@ -196,43 +197,6 @@ fc_plogi_get_maxframe(struct fc_els_flogi *flp, unsigned int maxval)
 }
 
 /**
- * fc_lport_plogi_fill - Fill in PLOGI command for request
- * @lport: Fibre Channel host port instance
- * @plogi: PLOGI command structure to fill (same structure as FLOGI)
- * @op: either ELS_PLOGI for a localy generated request, or ELS_LS_ACC
- */
-static void
-fc_lport_plogi_fill(struct fc_lport *lport,
-		    struct fc_els_flogi *plogi, unsigned int op)
-{
-	struct fc_els_csp *sp;
-	struct fc_els_cssp *cp;
-
-	memset(plogi, 0, sizeof(*plogi));
-	plogi->fl_cmd = (u8) op;
-	put_unaligned(htonll(lport->wwpn), &plogi->fl_wwpn);
-	put_unaligned(htonll(lport->wwnn), &plogi->fl_wwnn);
-
-	sp = &plogi->fl_csp;
-	sp->sp_hi_ver = 0x20;
-	sp->sp_lo_ver = 0x20;
-	sp->sp_bb_cred = htons(10);	/* this gets set by gateway */
-	sp->sp_bb_data = htons((u16) lport->mfs);
-	cp = &plogi->fl_cssp[3 - 1];	/* class 3 parameters */
-	cp->cp_class = htons(FC_CPC_VALID | FC_CPC_SEQ);
-	if (op != ELS_FLOGI) {
-		sp->sp_features = htons(FC_SP_FT_CIRO);
-		sp->sp_tot_seq = htons(255);	/* seq. we accept */
-		sp->sp_rel_off = htons(0x1f);
-		sp->sp_e_d_tov = htonl(lport->e_d_tov);
-
-		cp->cp_rdfs = htons((u16) lport->mfs);
-		cp->cp_con_seq = htons(255);
-		cp->cp_open_seq = 1;
-	}
-}
-
-/**
  * fc_rport_state_enter - Change the rport's state
  * @rport: The rport whose state should change
  * @new: The new state of the rport
@@ -262,7 +226,7 @@ static void fc_rport_work(void *data)
 	event = rdata->event;
 	event_callback = rdata->event_callback;
 
-	if (event == LPORT_EV_RPORT_CREATED) {
+	if (event == RPORT_EV_CREATED) {
 		struct fc_rport *new_rport;
 		struct fc_rport_libfc_priv *new_rdata;
 		struct fc_rport_identifiers ids;
@@ -292,27 +256,28 @@ static void fc_rport_work(void *data)
 			mutex_init(&new_rdata->rp_mutex);
 			INIT_WORK(&new_rdata->retry_work, fc_rport_timeout,
 				  new_rdata);
-			INIT_LIST_HEAD(&new_rdata->peers);
 			INIT_WORK(&new_rdata->event_work, fc_rport_work,
 				  new_rdata);
+			INIT_LIST_HEAD(&new_rdata->peers);
 
 			fc_rport_state_enter(new_rport, RPORT_ST_READY);
 		} else {
 			FC_DBG("Failed to create the rport for port "
 			       "(%6x).\n", ids.port_id);
-			event = LPORT_EV_RPORT_FAILED;
+			event = RPORT_EV_FAILED;
 		}
 		fc_rport_rogue_destroy(rport);
 		rport = new_rport;
 		rdata = new_rport->dd_data;
-		event_callback(lport, rport, event);
-	} else if ((event == LPORT_EV_RPORT_FAILED) ||
-		   (event == LPORT_EV_RPORT_LOGO) ||
-		   (event == LPORT_EV_RPORT_STOP)) {
-
+		if (event_callback)
+			event_callback(lport, rport, event);
+	} else if ((event == RPORT_EV_FAILED) ||
+		   (event == RPORT_EV_LOGO) ||
+		   (event == RPORT_EV_STOP)) {
 		trans_state = rdata->trans_state;
 		mutex_unlock(&rdata->rp_mutex);
-		event_callback(lport, rport, event);
+		if (event_callback)
+			event_callback(lport, rport, event);
 		if (trans_state == FC_PORTSTATE_ROGUE)
 			fc_rport_rogue_destroy(rport);
 		else
@@ -345,45 +310,37 @@ int fc_rport_login(struct fc_rport *rport)
 }
 
 /**
- * fc_rport_logout - Logout of the remote port and delete it
- * @rport: Fibre Channel remote port
+ * fc_rport_logoff - Logoff and remove an rport
+ * @rport: Fibre Channel remote port to be removed
  *
  * Locking Note: Called without the rport lock held. This
  * function will hold the rport lock, call an _enter_*
  * function and then unlock the rport.
  */
-int fc_rport_logout(struct fc_rport *rport)
+int fc_rport_logoff(struct fc_rport *rport)
 {
 	struct fc_rport_libfc_priv *rdata = rport->dd_data;
 
 	mutex_lock(&rdata->rp_mutex);
 
-	FC_DEBUG_RPORT("Logout of port (%6x)\n", rport->port_id);
+	FC_DEBUG_RPORT("Remove port (%6x)\n", rport->port_id);
 
 	fc_rport_enter_logo(rport);
 
-	mutex_unlock(&rdata->rp_mutex);
+	/*
+	 * Change the state to NONE so that we discard
+	 * the response.
+	 */
+	fc_rport_state_enter(rport, RPORT_ST_NONE);
 
-	return 0;
-}
+	mutex_unlock(&rdata->rp_mutex);
 
-/**
- * fc_rport_remove - Remove an rport
- * @rport: Fibre Channel remote port to be removed
- *
- * Locking Note: Called without the rport lock held. This
- * function will hold the rport lock, call an _enter_*
- * function and then unlock the rport.
- */
-int fc_rport_stop(struct fc_rport *rport)
-{
-	struct fc_rport_libfc_priv *rdata = rport->dd_data;
+	if (!cancel_delayed_work(&rdata->retry_work))
+		flush_scheduled_work();
 
 	mutex_lock(&rdata->rp_mutex);
 
-	FC_DEBUG_RPORT("Remove port (%6x)\n", rport->port_id);
-
-	rdata->event = LPORT_EV_RPORT_STOP;
+	rdata->event = RPORT_EV_STOP;
 	queue_work(rport_event_queue, &rdata->event_work);
 
 	mutex_unlock(&rdata->rp_mutex);
@@ -406,7 +363,7 @@ static void fc_rport_enter_ready(struct fc_rport *rport)
 
 	FC_DEBUG_RPORT("Port (%6x) is Ready\n", rport->port_id);
 
-	rdata->event = LPORT_EV_RPORT_CREATED;
+	rdata->event = RPORT_EV_CREATED;
 	queue_work(rport_event_queue, &rdata->event_work);
 }
 
@@ -440,9 +397,7 @@ static void fc_rport_timeout(void *data)
 		break;
 	case RPORT_ST_READY:
 	case RPORT_ST_INIT:
-		break;
 	case RPORT_ST_NONE:
-		BUG();
 		break;
 	}
 	put_device(&rport->dev);
@@ -471,7 +426,7 @@ static void fc_rport_error(struct fc_rport *rport, struct fc_frame *fp)
 		       PTR_ERR(fp), fc_rport_state(rport), rdata->retries);
 
 	if (!fp || PTR_ERR(fp) == -FC_EX_TIMEOUT) {
-		/* 
+		/*
 		 * Memory allocation failure, or the exchange timed out.
 		 *  Retry after delay
 		 */
@@ -486,8 +441,9 @@ static void fc_rport_error(struct fc_rport *rport, struct fc_frame *fp)
 			case RPORT_ST_PLOGI:
 			case RPORT_ST_PRLI:
 			case RPORT_ST_LOGO:
-				rdata->event = LPORT_EV_RPORT_FAILED;
-				queue_work(rport_event_queue, &rdata->event_work);
+				rdata->event = RPORT_EV_FAILED;
+				queue_work(rport_event_queue,
+					   &rdata->event_work);
 				break;
 			case RPORT_ST_RTV:
 				fc_rport_enter_ready(rport);
@@ -495,7 +451,6 @@ static void fc_rport_error(struct fc_rport *rport, struct fc_frame *fp)
 			case RPORT_ST_NONE:
 			case RPORT_ST_READY:
 			case RPORT_ST_INIT:
-				BUG();
 				break;
 			}
 		}
@@ -526,7 +481,8 @@ static void fc_rport_plogi_resp(struct fc_seq *sp, struct fc_frame *fp,
 
 	mutex_lock(&rdata->rp_mutex);
 
-	FC_DEBUG_RPORT("Received a PLOGI response\n");
+	FC_DEBUG_RPORT("Received a PLOGI response from port (%6x)\n",
+		       rport->port_id);
 
 	if (rdata->rp_state != RPORT_ST_PLOGI) {
 		FC_DBG("Received a PLOGI response, but in state %s\n",
@@ -587,7 +543,6 @@ static void fc_rport_enter_plogi(struct fc_rport *rport)
 	struct fc_rport_libfc_priv *rdata = rport->dd_data;
 	struct fc_lport *lport = rdata->local_port;
 	struct fc_frame *fp;
-	struct fc_els_flogi *plogi;
 
 	FC_DEBUG_RPORT("Port (%6x) entered PLOGI state from %s state\n",
 		       rport->port_id, fc_rport_state(rport));
@@ -595,23 +550,15 @@ static void fc_rport_enter_plogi(struct fc_rport *rport)
 	fc_rport_state_enter(rport, RPORT_ST_PLOGI);
 
 	rport->maxframe_size = FC_MIN_MAX_PAYLOAD;
-	fp = fc_frame_alloc(lport, sizeof(*plogi));
+	fp = fc_frame_alloc(lport, sizeof(struct fc_els_flogi));
 	if (!fp) {
 		fc_rport_error(rport, fp);
 		return;
 	}
-
-	plogi = fc_frame_payload_get(fp, sizeof(*plogi));
-	fc_lport_plogi_fill(rdata->local_port, plogi, ELS_PLOGI);
 	rdata->e_d_tov = lport->e_d_tov;
-	fc_frame_setup(fp, FC_RCTL_ELS_REQ, FC_TYPE_ELS);
-
-	if (!lport->tt.exch_seq_send(lport, fp,
-				     fc_rport_plogi_resp, NULL,
-				     rport, lport->e_d_tov,
-				     fc_host_port_id(rdata->local_port->host),
-				     rport->port_id,
-				     FC_FC_SEQ_INIT | FC_FC_END_SEQ))
+
+	if (!lport->tt.elsct_send(lport, rport, fp, ELS_PLOGI,
+				  fc_rport_plogi_resp, rport, lport->e_d_tov))
 		fc_rport_error(rport, fp);
 }
 
@@ -640,7 +587,8 @@ static void fc_rport_prli_resp(struct fc_seq *sp, struct fc_frame *fp,
 
 	mutex_lock(&rdata->rp_mutex);
 
-	FC_DEBUG_RPORT("Received a PRLI response\n");
+	FC_DEBUG_RPORT("Received a PRLI response from port (%6x)\n",
+		       rport->port_id);
 
 	if (rdata->rp_state != RPORT_ST_PRLI) {
 		FC_DBG("Received a PRLI response, but in state %s\n",
@@ -673,7 +621,7 @@ static void fc_rport_prli_resp(struct fc_seq *sp, struct fc_frame *fp,
 
 	} else {
 		FC_DBG("Bad ELS response\n");
-		rdata->event = LPORT_EV_RPORT_FAILED;
+		rdata->event = RPORT_EV_FAILED;
 		queue_work(rport_event_queue, &rdata->event_work);
 	}
 
@@ -702,25 +650,26 @@ static void fc_rport_logo_resp(struct fc_seq *sp, struct fc_frame *fp,
 
 	mutex_lock(&rdata->rp_mutex);
 
-	FC_DEBUG_RPORT("Received a LOGO response\n");
-
-	if (rdata->rp_state != RPORT_ST_LOGO) {
-		FC_DBG("Received a LOGO response, but in state %s\n",
-		       fc_rport_state(rport));
-		goto out;
-	}
+	FC_DEBUG_RPORT("Received a LOGO response from port (%6x)\n",
+		       rport->port_id);
 
 	if (IS_ERR(fp)) {
 		fc_rport_error(rport, fp);
 		goto err;
 	}
 
+	if (rdata->rp_state != RPORT_ST_LOGO) {
+		FC_DEBUG_RPORT("Received a LOGO response, but in state %s\n",
+			       fc_rport_state(rport));
+		goto out;
+	}
+
 	op = fc_frame_payload_op(fp);
 	if (op == ELS_LS_ACC) {
 		fc_rport_enter_rtv(rport);
 	} else {
 		FC_DBG("Bad ELS response\n");
-		rdata->event = LPORT_EV_RPORT_LOGO;
+		rdata->event = RPORT_EV_LOGO;
 		queue_work(rport_event_queue, &rdata->event_work);
 	}
 
@@ -758,22 +707,8 @@ static void fc_rport_enter_prli(struct fc_rport *rport)
 		return;
 	}
 
-	pp = fc_frame_payload_get(fp, sizeof(*pp));
-	memset(pp, 0, sizeof(*pp));
-	pp->prli.prli_cmd = ELS_PRLI;
-	pp->prli.prli_spp_len = sizeof(struct fc_els_spp);
-	pp->prli.prli_len = htons(sizeof(*pp));
-	pp->spp.spp_type = FC_TYPE_FCP;
-	pp->spp.spp_flags = FC_SPP_EST_IMG_PAIR;
-	pp->spp.spp_params = htonl(lport->service_params);
-	fc_frame_setup(fp, FC_RCTL_ELS_REQ, FC_TYPE_ELS);
-
-	if (!lport->tt.exch_seq_send(lport, fp,
-				     fc_rport_prli_resp, NULL,
-				     rport, lport->e_d_tov,
-				     fc_host_port_id(lport->host),
-				     rport->port_id,
-				     FC_FC_SEQ_INIT | FC_FC_END_SEQ))
+	if (!lport->tt.elsct_send(lport, rport, fp, ELS_PRLI,
+				  fc_rport_prli_resp, rport, lport->e_d_tov))
 		fc_rport_error(rport, fp);
 }
 
@@ -798,7 +733,8 @@ static void fc_rport_rtv_resp(struct fc_seq *sp, struct fc_frame *fp,
 
 	mutex_lock(&rdata->rp_mutex);
 
-	FC_DEBUG_RPORT("Received a RTV response\n");
+	FC_DEBUG_RPORT("Received a RTV response from port (%6x)\n",
+		       rport->port_id);
 
 	if (rdata->rp_state != RPORT_ST_RTV) {
 		FC_DBG("Received a RTV response, but in state %s\n",
@@ -850,7 +786,6 @@ err:
  */
 static void fc_rport_enter_rtv(struct fc_rport *rport)
 {
-	struct fc_els_rtv *rtv;
 	struct fc_frame *fp;
 	struct fc_rport_libfc_priv *rdata = rport->dd_data;
 	struct fc_lport *lport = rdata->local_port;
@@ -860,23 +795,14 @@ static void fc_rport_enter_rtv(struct fc_rport *rport)
 
 	fc_rport_state_enter(rport, RPORT_ST_RTV);
 
-	fp = fc_frame_alloc(lport, sizeof(*rtv));
+	fp = fc_frame_alloc(lport, sizeof(struct fc_els_rtv));
 	if (!fp) {
 		fc_rport_error(rport, fp);
 		return;
 	}
 
-	rtv = fc_frame_payload_get(fp, sizeof(*rtv));
-	memset(rtv, 0, sizeof(*rtv));
-	rtv->rtv_cmd = ELS_RTV;
-	fc_frame_setup(fp, FC_RCTL_ELS_REQ, FC_TYPE_ELS);
-
-	if (!lport->tt.exch_seq_send(lport, fp,
-				     fc_rport_rtv_resp, NULL,
-				     rport, lport->e_d_tov,
-				     fc_host_port_id(lport->host),
-				     rport->port_id,
-				     FC_FC_SEQ_INIT | FC_FC_END_SEQ))
+	if (!lport->tt.elsct_send(lport, rport, fp, ELS_RTV,
+				     fc_rport_rtv_resp, rport, lport->e_d_tov))
 		fc_rport_error(rport, fp);
 }
 
@@ -892,32 +818,20 @@ static void fc_rport_enter_logo(struct fc_rport *rport)
 	struct fc_rport_libfc_priv *rdata = rport->dd_data;
 	struct fc_lport *lport = rdata->local_port;
 	struct fc_frame *fp;
-	struct fc_els_logo *logo;
 
 	FC_DEBUG_RPORT("Port (%6x) entered LOGO state from %s state\n",
 		       rport->port_id, fc_rport_state(rport));
 
 	fc_rport_state_enter(rport, RPORT_ST_LOGO);
 
-	fp = fc_frame_alloc(lport, sizeof(*logo));
+	fp = fc_frame_alloc(lport, sizeof(struct fc_els_logo));
 	if (!fp) {
 		fc_rport_error(rport, fp);
 		return;
 	}
 
-	logo = fc_frame_payload_get(fp, sizeof(*logo));
-	memset(logo, 0, sizeof(*logo));
-	logo->fl_cmd = ELS_LOGO;
-	hton24(logo->fl_n_port_id, fc_host_port_id(lport->host));
-	logo->fl_n_port_wwn = htonll(lport->wwpn);
-	fc_frame_setup(fp, FC_RCTL_ELS_REQ, FC_TYPE_ELS);
-
-	if (!lport->tt.exch_seq_send(lport, fp,
-				     fc_rport_logo_resp, NULL,
-				     rport, lport->e_d_tov,
-				     fc_host_port_id(lport->host),
-				     rport->port_id,
-				     FC_FC_SEQ_INIT | FC_FC_END_SEQ))
+	if (!lport->tt.elsct_send(lport, rport, fp, ELS_LOGO,
+				  fc_rport_logo_resp, rport, lport->e_d_tov))
 		fc_rport_error(rport, fp);
 }
 
@@ -998,7 +912,7 @@ static void fc_rport_recv_plogi_req(struct fc_rport *rport,
 	struct fc_rport_libfc_priv *rdata = rport->dd_data;
 	struct fc_lport *lport = rdata->local_port;
 	struct fc_frame *fp = rx_fp;
-
+	struct fc_exch *ep;
 	struct fc_frame_header *fh;
 	struct fc_els_flogi *pl;
 	struct fc_seq_els_data rjt_data;
@@ -1087,17 +1001,18 @@ static void fc_rport_recv_plogi_req(struct fc_rport *rport,
 			rport->maxframe_size =
 				fc_plogi_get_maxframe(pl, lport->mfs);
 			fc_frame_free(rx_fp);
-			pl = fc_frame_payload_get(fp, sizeof(*pl));
-			WARN_ON(!pl);
-			fc_lport_plogi_fill(lport, pl, ELS_LS_ACC);
+			fc_plogi_fill(lport, fp, ELS_LS_ACC);
 
 			/*
 			 * Send LS_ACC.	 If this fails,
 			 * the originator should retry.
 			 */
-			f_ctl = FC_FC_SEQ_INIT | FC_FC_LAST_SEQ | FC_FC_END_SEQ;
-			fc_frame_setup(fp, FC_RCTL_ELS_REP, FC_TYPE_ELS);
-			lport->tt.seq_send(lport, sp, fp, f_ctl);
+			f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ;
+			f_ctl |= FC_FC_END_SEQ | FC_FC_SEQ_INIT;
+			ep = fc_seq_exch(sp);
+			fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid,
+				       FC_TYPE_ELS, f_ctl, 0);
+			lport->tt.seq_send(lport, sp, fp);
 			if (rdata->rp_state == RPORT_ST_PLOGI)
 				fc_rport_enter_prli(rport);
 		}
@@ -1118,7 +1033,7 @@ static void fc_rport_recv_prli_req(struct fc_rport *rport,
 {
 	struct fc_rport_libfc_priv *rdata = rport->dd_data;
 	struct fc_lport *lport = rdata->local_port;
-
+	struct fc_exch *ep;
 	struct fc_frame *fp;
 	struct fc_frame_header *fh;
 	struct {
@@ -1232,9 +1147,12 @@ static void fc_rport_recv_prli_req(struct fc_rport *rport,
 		/*
 		 * Send LS_ACC.	 If this fails, the originator should retry.
 		 */
-		f_ctl = FC_FC_SEQ_INIT | FC_FC_LAST_SEQ | FC_FC_END_SEQ;
-		fc_frame_setup(fp, FC_RCTL_ELS_REP, FC_TYPE_ELS);
-		lport->tt.seq_send(lport, sp, fp, f_ctl);
+		f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ;
+		f_ctl |= FC_FC_END_SEQ | FC_FC_SEQ_INIT;
+		ep = fc_seq_exch(sp);
+		fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid,
+			       FC_TYPE_ELS, f_ctl, 0);
+		lport->tt.seq_send(lport, sp, fp);
 
 		/*
 		 * Get lock and re-check state.
@@ -1305,27 +1223,33 @@ static void fc_rport_recv_logo_req(struct fc_rport *rport, struct fc_seq *sp,
 		       "while in state %s\n", ntoh24(fh->fh_s_id),
 		       fc_rport_state(rport));
 
-	rdata->event = LPORT_EV_RPORT_LOGO;
+	rdata->event = RPORT_EV_LOGO;
 	queue_work(rport_event_queue, &rdata->event_work);
 
 	lport->tt.seq_els_rsp_send(sp, ELS_LS_ACC, NULL);
 	fc_frame_free(fp);
 }
 
+static void fc_rport_flush_queue(void)
+{
+	flush_workqueue(rport_event_queue);
+}
+
+
 int fc_rport_init(struct fc_lport *lport)
 {
 	if (!lport->tt.rport_login)
 		lport->tt.rport_login = fc_rport_login;
 
-	if (!lport->tt.rport_logout)
-		lport->tt.rport_logout = fc_rport_logout;
-
-	if (!lport->tt.rport_stop)
-		lport->tt.rport_stop = fc_rport_stop;
+	if (!lport->tt.rport_logoff)
+		lport->tt.rport_logoff = fc_rport_logoff;
 
 	if (!lport->tt.rport_recv_req)
 		lport->tt.rport_recv_req = fc_rport_recv_req;
 
+	if (!lport->tt.rport_flush_queue)
+		lport->tt.rport_flush_queue = fc_rport_flush_queue;
+
 	return 0;
 }
 EXPORT_SYMBOL(fc_rport_init);
diff --git a/include/scsi/fc/fc_els.h b/include/scsi/fc/fc_els.h
index af4bf0c..195ca01 100644
--- a/include/scsi/fc/fc_els.h
+++ b/include/scsi/fc/fc_els.h
@@ -401,6 +401,20 @@ struct fc_els_prli {
 };
 
 /*
+ * ELS_ADISC payload
+ */
+struct fc_els_adisc {
+	__u8		adisc_cmd;
+	__u8		adisc_resv[3];
+	__u8            adisc_resv1;
+	__u8            adisc_hard_addr[3];
+	__be64          adisc_wwpn;
+	__be64          adisc_wwnn;
+	__u8            adisc_resv2;
+	__u8            adisc_port_id[3];
+} __attribute__((__packed__));
+
+/*
  * ELS_LOGO - process or fabric logout.
  */
 struct fc_els_logo {
diff --git a/include/scsi/fc/fc_fcoe.h b/include/scsi/fc/fc_fcoe.h
index 59c9d0c..a6118a2 100644
--- a/include/scsi/fc/fc_fcoe.h
+++ b/include/scsi/fc/fc_fcoe.h
@@ -31,6 +31,10 @@
 #define	ETH_P_FCOE	0x8906		/* FCOE ether type */
 #endif
 
+#ifndef ETH_P_8021Q
+#define	ETH_P_8021Q	0x8100
+#endif
+
 /*
  * FC_FCOE_OUI hasn't been standardized yet.   XXX TBD.
  */
@@ -81,7 +85,9 @@ struct fcoe_crc_eof {
 } __attribute__((packed));
 
 /*
- * Store OUI + DID into MAC address field.
+ * fc_fcoe_set_mac - Store OUI + DID into MAC address field.
+ * @mac: mac address to be set
+ * @did: fc dest id to use
  */
 static inline void fc_fcoe_set_mac(u8 *mac, u8 *did)
 {
@@ -93,8 +99,4 @@ static inline void fc_fcoe_set_mac(u8 *mac, u8 *did)
 	mac[5] = did[2];
 }
 
-#ifndef ETH_P_8021Q
-#define	ETH_P_8021Q	0x8100
-#endif
-
 #endif /* _FC_FCOE_H_ */
diff --git a/include/scsi/fc/fc_fs.h b/include/scsi/fc/fc_fs.h
index 3897c6c..3e4801d 100644
--- a/include/scsi/fc/fc_fs.h
+++ b/include/scsi/fc/fc_fs.h
@@ -82,6 +82,12 @@ enum fc_rctl {
 	FC_RCTL_ELS4_REQ = 0x32, /* FC-4 ELS request */
 	FC_RCTL_ELS4_REP = 0x33, /* FC-4 ELS reply */
 	/*
+	 * Optional Extended Headers
+	 */
+	FC_RCTL_VFTH = 0x50,	/* virtual fabric tagging header */
+	FC_RCTL_IFRH = 0x51,	/* inter-fabric routing header */
+	FC_RCTL_ENCH = 0x52,	/* encapsulation header */
+	/*
 	 * Basic Link Services fh_r_ctl values.
 	 */
 	FC_RCTL_BA_NOP = 0x80,	/* basic link service NOP */
@@ -200,6 +206,8 @@ enum fc_fh_type {
  * Exchange IDs.
  */
 #define FC_XID_UNKNOWN  0xffff	/* unknown exchange ID */
+#define FC_XID_MIN	0x0	/* supported min exchange ID */
+#define FC_XID_MAX	0xfffe	/* supported max exchange ID */
 
 /*
  * fh_f_ctl - Frame control flags.
diff --git a/include/scsi/fc_encode.h b/include/scsi/fc_encode.h
new file mode 100644
index 0000000..a09df7a
--- /dev/null
+++ b/include/scsi/fc_encode.h
@@ -0,0 +1,308 @@
+/*
+ * Copyright(c) 2008 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,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Maintained at www.Open-FCoE.org
+ */
+
+#ifndef _FC_ENCODE_H_
+#define _FC_ENCODE_H_
+#include <asm/unaligned.h>
+
+struct fc_ns_rft {
+	struct fc_ns_fid fid;	/* port ID object */
+	struct fc_ns_fts fts;	/* FC4-types object */
+};
+
+struct fc_ct_req {
+	struct fc_ct_hdr hdr;
+	union {
+		struct fc_ns_gid_ft gid;
+		struct fc_ns_rn_id  rn;
+		struct fc_ns_rft rft;
+	} payload;
+};
+
+/**
+ * fill FC header fields in specified fc_frame
+ */
+static inline void fc_fill_fc_hdr(struct fc_frame *fp, enum fc_rctl r_ctl,
+				  u32 did, u32 sid, enum fc_fh_type type,
+				  u32 f_ctl, u32 parm_offset)
+{
+	struct fc_frame_header *fh;
+
+	fh = fc_frame_header_get(fp);
+	WARN_ON(r_ctl == 0);
+	fh->fh_r_ctl = r_ctl;
+	hton24(fh->fh_d_id, did);
+	hton24(fh->fh_s_id, sid);
+	fh->fh_type = type;
+	hton24(fh->fh_f_ctl, f_ctl);
+	fh->fh_cs_ctl = 0;
+	fh->fh_df_ctl = 0;
+	fh->fh_parm_offset = htonl(parm_offset);
+}
+
+/**
+ * fc_ct_hdr_fill- fills ct header and reset ct payload
+ * returns pointer to ct request.
+ */
+static inline struct fc_ct_req *fc_ct_hdr_fill(const struct fc_frame *fp,
+					       unsigned int op, size_t req_size)
+{
+	struct fc_ct_req *ct;
+	size_t ct_plen;
+
+	ct_plen  = sizeof(struct fc_ct_hdr) + req_size;
+	ct = fc_frame_payload_get(fp, ct_plen);
+	memset(ct, 0, ct_plen);
+	ct->hdr.ct_rev = FC_CT_REV;
+	ct->hdr.ct_fs_type = FC_FST_DIR;
+	ct->hdr.ct_fs_subtype = FC_NS_SUBTYPE;
+	ct->hdr.ct_cmd = htons((u16) op);
+	return ct;
+}
+
+/**
+ * fc_ct_fill - Fill in a name service request frame
+ */
+static inline int fc_ct_fill(struct fc_lport *lport, struct fc_frame *fp,
+		      unsigned int op, enum fc_rctl *r_ctl, u32 *did,
+		      enum fc_fh_type *fh_type)
+{
+	struct fc_ct_req *ct;
+
+	switch (op) {
+	case FC_NS_GPN_FT:
+		ct = fc_ct_hdr_fill(fp, op, sizeof(struct fc_ns_gid_ft));
+		ct->payload.gid.fn_fc4_type = FC_TYPE_FCP;
+		break;
+
+	case FC_NS_RFT_ID:
+		ct = fc_ct_hdr_fill(fp, op, sizeof(struct fc_ns_rft));
+		hton24(ct->payload.rft.fid.fp_fid,
+		       fc_host_port_id(lport->host));
+		ct->payload.rft.fts = lport->fcts;
+		break;
+
+	case FC_NS_RPN_ID:
+		ct = fc_ct_hdr_fill(fp, op, sizeof(struct fc_ns_rn_id));
+		hton24(ct->payload.rn.fr_fid.fp_fid,
+		       fc_host_port_id(lport->host));
+		ct->payload.rft.fts = lport->fcts;
+		put_unaligned(htonll(lport->wwpn), &ct->payload.rn.fr_wwn);
+		break;
+
+	default:
+		FC_DBG("Invalid op code %x \n", op);
+		return -EINVAL;
+	}
+	*r_ctl = FC_RCTL_DD_UNSOL_CTL;
+	*did = FC_FID_DIR_SERV;
+	*fh_type = FC_TYPE_CT;
+	return 0;
+}
+
+/**
+ * fc_plogi_fill - Fill in plogi request frame
+ */
+static inline void fc_plogi_fill(struct fc_lport *lport, struct fc_frame *fp,
+				 unsigned int op)
+{
+	struct fc_els_flogi *plogi;
+	struct fc_els_csp *csp;
+	struct fc_els_cssp *cp;
+
+	plogi = fc_frame_payload_get(fp, sizeof(*plogi));
+	memset(plogi, 0, sizeof(*plogi));
+	plogi->fl_cmd = (u8) op;
+	put_unaligned(htonll(lport->wwpn), &plogi->fl_wwpn);
+	put_unaligned(htonll(lport->wwnn), &plogi->fl_wwnn);
+	csp = &plogi->fl_csp;
+	csp->sp_hi_ver = 0x20;
+	csp->sp_lo_ver = 0x20;
+	csp->sp_bb_cred = htons(10);	/* this gets set by gateway */
+	csp->sp_bb_data = htons((u16) lport->mfs);
+	cp = &plogi->fl_cssp[3 - 1];	/* class 3 parameters */
+	cp->cp_class = htons(FC_CPC_VALID | FC_CPC_SEQ);
+	csp->sp_features = htons(FC_SP_FT_CIRO);
+	csp->sp_tot_seq = htons(255);	/* seq. we accept */
+	csp->sp_rel_off = htons(0x1f);
+	csp->sp_e_d_tov = htonl(lport->e_d_tov);
+
+	cp->cp_rdfs = htons((u16) lport->mfs);
+	cp->cp_con_seq = htons(255);
+	cp->cp_open_seq = 1;
+}
+
+/**
+ * fc_flogi_fill - Fill in a flogi request frame.
+ */
+static inline void fc_flogi_fill(struct fc_lport *lport, struct fc_frame *fp)
+{
+	struct fc_els_csp *sp;
+	struct fc_els_cssp *cp;
+	struct fc_els_flogi *flogi;
+
+	flogi = fc_frame_payload_get(fp, sizeof(*flogi));
+	memset(flogi, 0, sizeof(*flogi));
+	flogi->fl_cmd = (u8) ELS_FLOGI;
+	put_unaligned(htonll(lport->wwpn), &flogi->fl_wwpn);
+	put_unaligned(htonll(lport->wwnn), &flogi->fl_wwnn);
+	sp = &flogi->fl_csp;
+	sp->sp_hi_ver = 0x20;
+	sp->sp_lo_ver = 0x20;
+	sp->sp_bb_cred = htons(10);	/* this gets set by gateway */
+	sp->sp_bb_data = htons((u16) lport->mfs);
+	cp = &flogi->fl_cssp[3 - 1];	/* class 3 parameters */
+	cp->cp_class = htons(FC_CPC_VALID | FC_CPC_SEQ);
+}
+
+/**
+ * fc_logo_fill - Fill in a logo request frame.
+ */
+static inline void fc_logo_fill(struct fc_lport *lport, struct fc_frame *fp)
+{
+	struct fc_els_logo *logo;
+
+	logo = fc_frame_payload_get(fp, sizeof(*logo));
+	memset(logo, 0, sizeof(*logo));
+	logo->fl_cmd = ELS_LOGO;
+	hton24(logo->fl_n_port_id, fc_host_port_id(lport->host));
+	logo->fl_n_port_wwn = htonll(lport->wwpn);
+}
+
+/**
+ * fc_rtv_fill - Fill in RTV (read timeout value) request frame.
+ */
+static inline void fc_rtv_fill(struct fc_lport *lport, struct fc_frame *fp)
+{
+	struct fc_els_rtv *rtv;
+
+	rtv = fc_frame_payload_get(fp, sizeof(*rtv));
+	memset(rtv, 0, sizeof(*rtv));
+	rtv->rtv_cmd = ELS_RTV;
+}
+
+/**
+ * fc_rec_fill - Fill in rec request frame
+ */
+static inline void fc_rec_fill(struct fc_lport *lport, struct fc_frame *fp)
+{
+	struct fc_els_rec *rec;
+	struct fc_exch *ep = fc_seq_exch(fr_seq(fp));
+
+	rec = fc_frame_payload_get(fp, sizeof(*rec));
+	memset(rec, 0, sizeof(*rec));
+	rec->rec_cmd = ELS_REC;
+	hton24(rec->rec_s_id, fc_host_port_id(lport->host));
+	rec->rec_ox_id = htons(ep->oxid);
+	rec->rec_rx_id = htons(ep->rxid);
+}
+
+/**
+ * fc_prli_fill - Fill in prli request frame
+ */
+static inline void fc_prli_fill(struct fc_lport *lport, struct fc_frame *fp)
+{
+	struct {
+		struct fc_els_prli prli;
+		struct fc_els_spp spp;
+	} *pp;
+
+	pp = fc_frame_payload_get(fp, sizeof(*pp));
+	memset(pp, 0, sizeof(*pp));
+	pp->prli.prli_cmd = ELS_PRLI;
+	pp->prli.prli_spp_len = sizeof(struct fc_els_spp);
+	pp->prli.prli_len = htons(sizeof(*pp));
+	pp->spp.spp_type = FC_TYPE_FCP;
+	pp->spp.spp_flags = FC_SPP_EST_IMG_PAIR;
+	pp->spp.spp_params = htonl(lport->service_params);
+}
+
+/**
+ * fc_scr_fill - Fill in a scr request frame.
+ */
+static inline void fc_scr_fill(struct fc_lport *lport, struct fc_frame *fp)
+{
+	struct fc_els_scr *scr;
+
+	scr = fc_frame_payload_get(fp, sizeof(*scr));
+	memset(scr, 0, sizeof(*scr));
+	scr->scr_cmd = ELS_SCR;
+	scr->scr_reg_func = ELS_SCRF_FULL;
+}
+
+/**
+ * fc_els_fill - Fill in an ELS  request frame
+ */
+static inline int fc_els_fill(struct fc_lport *lport, struct fc_rport *rport,
+		       struct fc_frame *fp, unsigned int op,
+		       enum fc_rctl *r_ctl, u32 *did, enum fc_fh_type *fh_type)
+{
+	switch (op) {
+	case ELS_PLOGI:
+		fc_plogi_fill(lport, fp, ELS_PLOGI);
+		*did = rport->port_id;
+		break;
+
+	case ELS_FLOGI:
+		fc_flogi_fill(lport, fp);
+		*did = FC_FID_FLOGI;
+		break;
+
+	case ELS_LOGO:
+		fc_logo_fill(lport, fp);
+		*did = FC_FID_FLOGI;
+		/*
+		 * if rport is valid then it
+		 * is port logo, therefore
+		 * set did to rport id.
+		 */
+		if (rport)
+			*did = rport->port_id;
+		break;
+
+	case ELS_RTV:
+		fc_rtv_fill(lport, fp);
+		*did = rport->port_id;
+		break;
+
+	case ELS_REC:
+		fc_rec_fill(lport, fp);
+		*did = rport->port_id;
+		break;
+
+	case ELS_PRLI:
+		fc_prli_fill(lport, fp);
+		*did = rport->port_id;
+		break;
+
+	case ELS_SCR:
+		fc_scr_fill(lport, fp);
+		*did = FC_FID_FCTRL;
+		break;
+
+	default:
+		FC_DBG("Invalid op code %x \n", op);
+		return -EINVAL;
+	}
+
+	*r_ctl = FC_RCTL_ELS_REQ;
+	*fh_type = FC_TYPE_ELS;
+	return 0;
+}
+#endif /* _FC_ENCODE_H_ */
diff --git a/include/scsi/fc_frame.h b/include/scsi/fc_frame.h
new file mode 100644
index 0000000..dc5f734
--- /dev/null
+++ b/include/scsi/fc_frame.h
@@ -0,0 +1,239 @@
+/*
+ * Copyright(c) 2007 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,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Maintained at www.Open-FCoE.org
+ */
+
+#ifndef _FC_FRAME_H_
+#define _FC_FRAME_H_
+
+#include <linux/scatterlist.h>
+#include <linux/skbuff.h>
+#include <scsi/scsi_cmnd.h>
+
+#include <scsi/fc/fc_fs.h>
+#include <scsi/fc/fc_fcp.h>
+#include <scsi/fc/fc_encaps.h>
+
+/*
+ * The fc_frame interface is used to pass frame data between functions.
+ * The frame includes the data buffer, length, and SOF / EOF delimiter types.
+ * A pointer to the port structure of the receiving port is also includeded.
+ */
+
+#define	FC_FRAME_HEADROOM	32	/* headroom for VLAN + FCoE headers */
+#define	FC_FRAME_TAILROOM	8	/* trailer space for FCoE */
+
+/*
+ * Information about an individual fibre channel frame received or to be sent.
+ * The buffer may be in up to 4 additional non-contiguous sections,
+ * but the linear section must hold the frame header.
+ */
+#define FC_FRAME_SG_LEN		4	/* scatter/gather list maximum length */
+
+#define fp_skb(fp)	(&((fp)->skb))
+#define fr_hdr(fp)	((fp)->skb.data)
+#define fr_len(fp)	((fp)->skb.len)
+#define fr_cb(fp)	((struct fcoe_rcv_info *)&((fp)->skb.cb[0]))
+#define fr_dev(fp)	(fr_cb(fp)->fr_dev)
+#define fr_seq(fp)	(fr_cb(fp)->fr_seq)
+#define fr_sof(fp)	(fr_cb(fp)->fr_sof)
+#define fr_eof(fp)	(fr_cb(fp)->fr_eof)
+#define fr_flags(fp)	(fr_cb(fp)->fr_flags)
+#define fr_max_payload(fp)	(fr_cb(fp)->fr_max_payload)
+#define fr_cmd(fp)	(fr_cb(fp)->fr_cmd)
+#define fr_dir(fp)	(fr_cmd(fp)->sc_data_direction)
+
+struct fc_frame {
+	struct sk_buff skb;
+};
+
+struct fcoe_rcv_info {
+	struct packet_type  *ptype;
+	struct fc_lport	*fr_dev;	/* transport layer private pointer */
+	struct fc_seq	*fr_seq;	/* for use with exchange manager */
+	struct scsi_cmnd *fr_cmd;	/* for use of scsi command */
+	enum fc_sof	fr_sof;		/* start of frame delimiter */
+	enum fc_eof	fr_eof;		/* end of frame delimiter */
+	u8		fr_flags;	/* flags - see below */
+	u16		fr_max_payload;	/* max FC payload */
+};
+
+/*
+ * Get fc_frame pointer for an skb that's already been imported.
+ */
+static inline struct fcoe_rcv_info *fcoe_dev_from_skb(const struct sk_buff *skb)
+{
+	BUILD_BUG_ON(sizeof(struct fcoe_rcv_info) > sizeof(skb->cb));
+	return (struct fcoe_rcv_info *) skb->cb;
+}
+
+/*
+ * fr_flags.
+ */
+#define	FCPHF_CRC_UNCHECKED	0x01	/* CRC not computed, still appended */
+
+/*
+ * Initialize a frame.
+ * We don't do a complete memset here for performance reasons.
+ * The caller must set fr_free, fr_hdr, fr_len, fr_sof, and fr_eof eventually.
+ */
+static inline void fc_frame_init(struct fc_frame *fp)
+{
+	fr_dev(fp) = NULL;
+	fr_seq(fp) = NULL;
+	fr_flags(fp) = 0;
+}
+
+struct fc_frame *fc_frame_alloc_fill(struct fc_lport *, size_t payload_len);
+
+struct fc_frame *__fc_frame_alloc(size_t payload_len);
+
+/*
+ * Get frame for sending via port.
+ */
+static inline struct fc_frame *_fc_frame_alloc(struct fc_lport *dev,
+					       size_t payload_len)
+{
+	return __fc_frame_alloc(payload_len);
+}
+
+/*
+ * Allocate fc_frame structure and buffer.  Set the initial length to
+ * payload_size + sizeof (struct fc_frame_header).
+ */
+static inline struct fc_frame *fc_frame_alloc(struct fc_lport *dev, size_t len)
+{
+	struct fc_frame *fp;
+
+	/*
+	 * Note: Since len will often be a constant multiple of 4,
+	 * this check will usually be evaluated and eliminated at compile time.
+	 */
+	if ((len % 4) != 0)
+		fp = fc_frame_alloc_fill(dev, len);
+	else
+		fp = _fc_frame_alloc(dev, len);
+	return fp;
+}
+
+/*
+ * Free the fc_frame structure and buffer.
+ */
+static inline void fc_frame_free(struct fc_frame *fp)
+{
+	kfree_skb(fp_skb(fp));
+}
+
+static inline int fc_frame_is_linear(struct fc_frame *fp)
+{
+	return !skb_is_nonlinear(fp_skb(fp));
+}
+
+/*
+ * Get frame header from message in fc_frame structure.
+ * This hides a cast and provides a place to add some checking.
+ */
+static inline
+struct fc_frame_header *fc_frame_header_get(const struct fc_frame *fp)
+{
+	WARN_ON(fr_len(fp) < sizeof(struct fc_frame_header));
+	return (struct fc_frame_header *) fr_hdr(fp);
+}
+
+/*
+ * Get frame payload from message in fc_frame structure.
+ * This hides a cast and provides a place to add some checking.
+ * The len parameter is the minimum length for the payload portion.
+ * Returns NULL if the frame is too short.
+ *
+ * This assumes the interesting part of the payload is in the first part
+ * of the buffer for received data.  This may not be appropriate to use for
+ * buffers being transmitted.
+ */
+static inline void *fc_frame_payload_get(const struct fc_frame *fp,
+					 size_t len)
+{
+	void *pp = NULL;
+
+	if (fr_len(fp) >= sizeof(struct fc_frame_header) + len)
+		pp = fc_frame_header_get(fp) + 1;
+	return pp;
+}
+
+/*
+ * Get frame payload opcode (first byte) from message in fc_frame structure.
+ * This hides a cast and provides a place to add some checking. Return 0
+ * if the frame has no payload.
+ */
+static inline u8 fc_frame_payload_op(const struct fc_frame *fp)
+{
+	u8 *cp;
+
+	cp = fc_frame_payload_get(fp, sizeof(u8));
+	if (!cp)
+		return 0;
+	return *cp;
+
+}
+
+/*
+ * Get FC class from frame.
+ */
+static inline enum fc_class fc_frame_class(const struct fc_frame *fp)
+{
+	return fc_sof_class(fr_sof(fp));
+}
+
+/*
+ * Check the CRC in a frame.
+ * The CRC immediately follows the last data item *AFTER* the length.
+ * The return value is zero if the CRC matches.
+ */
+u32 fc_frame_crc_check(struct fc_frame *);
+
+static inline u8 fc_frame_rctl(const struct fc_frame *fp)
+{
+	return fc_frame_header_get(fp)->fh_r_ctl;
+}
+
+static inline bool fc_frame_is_cmd(const struct fc_frame *fp)
+{
+	return fc_frame_rctl(fp) == FC_RCTL_DD_UNSOL_CMD;
+}
+
+static inline bool fc_frame_is_read(const struct fc_frame *fp)
+{
+	if (fc_frame_is_cmd(fp) && fr_cmd(fp))
+		return fr_dir(fp) == DMA_FROM_DEVICE;
+	return false;
+}
+
+static inline bool fc_frame_is_write(const struct fc_frame *fp)
+{
+	if (fc_frame_is_cmd(fp) && fr_cmd(fp))
+		return fr_dir(fp) == DMA_TO_DEVICE;
+	return false;
+}
+
+/*
+ * Check for leaks.
+ * Print the frame header of any currently allocated frame, assuming there
+ * should be none at this point.
+ */
+void fc_frame_leak_check(void);
+
+#endif /* _FC_FRAME_H_ */
diff --git a/include/scsi/fc_transport_fcoe.h b/include/scsi/fc_transport_fcoe.h
new file mode 100644
index 0000000..2816462
--- /dev/null
+++ b/include/scsi/fc_transport_fcoe.h
@@ -0,0 +1,53 @@
+#ifndef FC_TRANSPORT_FCOE_H
+#define FC_TRANSPORT_FCOE_H
+
+#include <linux/device.h>
+#include <linux/netdevice.h>
+#include <scsi/scsi_host.h>
+#include <scsi/libfc.h>
+
+/**
+ * struct fcoe_transport - FCoE transport struct for generic transport
+ * for Ethernet devices as well as pure HBAs
+ *
+ * @name: name for thsi transport
+ * @bus: physical bus type (pci_bus_type)
+ * @driver: physical bus driver for network device
+ * @create: entry create function
+ * @destroy: exit destroy function
+ * @list: list of transports
+ */
+struct fcoe_transport {
+	char *name;
+	unsigned short vendor;
+	unsigned short device;
+	struct bus_type *bus;
+	struct device_driver *driver;
+	int (*create)(struct net_device *device);
+	int (*destroy)(struct net_device *device);
+	bool (*match)(struct net_device *device);
+	struct list_head list;
+	struct list_head devlist;
+	struct mutex devlock;
+};
+
+/**
+ * MODULE_ALIAS_FCOE_PCI
+ *
+ * some care must be taken with this, vendor and device MUST be a hex value
+ * preceded with 0x and with letters in lower case (0x12ab, not 0x12AB or 12AB)
+ */
+#define MODULE_ALIAS_FCOE_PCI(vendor, device) \
+	MODULE_ALIAS("fcoe-pci-" __stringify(vendor) "-" __stringify(device))
+
+/* exported funcs */
+int fcoe_transport_attach(struct net_device *netdev);
+int fcoe_transport_release(struct net_device *netdev);
+int fcoe_transport_register(struct fcoe_transport *t);
+int fcoe_transport_unregister(struct fcoe_transport *t);
+int __init fcoe_transport_init(void);
+int __exit fcoe_transport_exit(void);
+
+/* fcow_sw is the default transport */
+extern struct fcoe_transport fcoe_sw_transport;
+#endif /* FC_TRANSPORT_FCOE_H */
diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h
new file mode 100644
index 0000000..13b9592
--- /dev/null
+++ b/include/scsi/libfc.h
@@ -0,0 +1,921 @@
+/*
+ * Copyright(c) 2007 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,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Maintained at www.Open-FCoE.org
+ */
+
+#ifndef _LIBFC_H_
+#define _LIBFC_H_
+
+#include <linux/timer.h>
+#include <linux/if.h>
+
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_transport.h>
+#include <scsi/scsi_transport_fc.h>
+
+#include <scsi/fc/fc_fcp.h>
+#include <scsi/fc/fc_ns.h>
+#include <scsi/fc/fc_els.h>
+#include <scsi/fc/fc_gs.h>
+
+#include <scsi/fc_frame.h>
+
+#define LIBFC_DEBUG
+
+#ifdef LIBFC_DEBUG
+/* Log messages */
+#define FC_DBG(fmt, args...)						\
+	do {								\
+		printk(KERN_INFO "%s " fmt, __func__, ##args);		\
+	} while (0)
+#else
+#define FC_DBG(fmt, args...)
+#endif
+
+/*
+ * libfc error codes
+ */
+#define	FC_NO_ERR	0	/* no error */
+#define	FC_EX_TIMEOUT	1	/* Exchange timeout */
+#define	FC_EX_CLOSED	2	/* Exchange closed */
+
+/* some helpful macros */
+
+#define ntohll(x) be64_to_cpu(x)
+#define htonll(x) cpu_to_be64(x)
+
+#define ntoh24(p) (((p)[0] << 16) | ((p)[1] << 8) | ((p)[2]))
+
+#define hton24(p, v)	do {			\
+		p[0] = (((v) >> 16) & 0xFF);	\
+		p[1] = (((v) >> 8) & 0xFF);	\
+		p[2] = ((v) & 0xFF);		\
+	} while (0)
+
+/*
+ * FC HBA status
+ */
+#define FC_PAUSE		    (1 << 1)
+#define FC_LINK_UP		    (1 << 0)
+
+enum fc_lport_state {
+	LPORT_ST_NONE = 0,
+	LPORT_ST_FLOGI,
+	LPORT_ST_DNS,
+	LPORT_ST_RPN_ID,
+	LPORT_ST_RFT_ID,
+	LPORT_ST_SCR,
+	LPORT_ST_READY,
+	LPORT_ST_LOGO,
+	LPORT_ST_RESET
+};
+
+enum fc_disc_event {
+	DISC_EV_NONE = 0,
+	DISC_EV_SUCCESS,
+	DISC_EV_FAILED
+};
+
+enum fc_lport_event {
+	RPORT_EV_NONE = 0,
+	RPORT_EV_CREATED,
+	RPORT_EV_FAILED,
+	RPORT_EV_STOP,
+	RPORT_EV_LOGO
+};
+
+enum fc_rport_state {
+	RPORT_ST_NONE = 0,
+	RPORT_ST_INIT,		/* initialized */
+	RPORT_ST_PLOGI,		/* waiting for PLOGI completion */
+	RPORT_ST_PRLI,		/* waiting for PRLI completion */
+	RPORT_ST_RTV,		/* waiting for RTV completion */
+	RPORT_ST_READY,		/* ready for use */
+	RPORT_ST_LOGO,		/* port logout sent */
+};
+
+enum fc_rport_trans_state {
+	FC_PORTSTATE_ROGUE,
+	FC_PORTSTATE_REAL,
+};
+
+/**
+ * struct fc_disc_port - temporary discovery port to hold rport identifiers
+ * @lp: Fibre Channel host port instance
+ * @peers: node for list management during discovery and RSCN processing
+ * @ids: identifiers structure to pass to fc_remote_port_add()
+ * @rport_work: work struct for starting the rport state machine
+ */
+struct fc_disc_port {
+	struct fc_lport             *lp;
+	struct list_head            peers;
+	struct fc_rport_identifiers ids;
+	struct work_struct	    rport_work;
+};
+
+/**
+ * struct fc_rport_libfc_priv - libfc internal information about a remote port
+ * @local_port: Fibre Channel host port instance
+ * @rp_state: state tracks progress of PLOGI, PRLI, and RTV exchanges
+ * @flags: REC and RETRY supported flags
+ * @max_seq: maximum number of concurrent sequences
+ * @retries: retry count in current state
+ * @e_d_tov: error detect timeout value (in msec)
+ * @r_a_tov: resource allocation timeout value (in msec)
+ * @rp_mutex: mutex protects rport
+ * @retry_work:
+ * @event_callback: Callback for rport READY, FAILED or LOGO
+ */
+struct fc_rport_libfc_priv {
+	struct fc_lport		*local_port;
+	enum fc_rport_state rp_state;
+	u16			flags;
+	#define FC_RP_FLAGS_REC_SUPPORTED	(1 << 0)
+	#define FC_RP_FLAGS_RETRY		(1 << 1)
+	u16		max_seq;
+	unsigned int	retries;
+	unsigned int	e_d_tov;
+	unsigned int	r_a_tov;
+	enum fc_rport_trans_state trans_state;
+	struct mutex    rp_mutex;
+	struct work_struct	retry_work;
+	enum fc_lport_event     event;
+	void (*event_callback)(struct fc_lport *,
+			       struct fc_rport *,
+			       enum fc_lport_event);
+	struct list_head         peers;
+	struct work_struct       event_work;
+};
+
+#define PRIV_TO_RPORT(x)						\
+	(struct fc_rport *)((void *)x - sizeof(struct fc_rport));
+#define RPORT_TO_PRIV(x)						\
+	(struct fc_rport_libfc_priv *)((void *)x + sizeof(struct fc_rport));
+
+struct fc_rport *fc_rport_rogue_create(struct fc_disc_port *);
+void fc_rport_rogue_destroy(struct fc_rport *);
+
+static inline void fc_rport_set_name(struct fc_rport *rport, u64 wwpn, u64 wwnn)
+{
+	rport->node_name = wwnn;
+	rport->port_name = wwpn;
+}
+
+/*
+ * fcoe stats structure
+ */
+struct fcoe_dev_stats {
+	u64		SecondsSinceLastReset;
+	u64		TxFrames;
+	u64		TxWords;
+	u64		RxFrames;
+	u64		RxWords;
+	u64		ErrorFrames;
+	u64		DumpedFrames;
+	u64		LinkFailureCount;
+	u64		LossOfSignalCount;
+	u64		InvalidTxWordCount;
+	u64		InvalidCRCCount;
+	u64		InputRequests;
+	u64		OutputRequests;
+	u64		ControlRequests;
+	u64		InputMegabytes;
+	u64		OutputMegabytes;
+};
+
+/*
+ * els data is used for passing ELS respone specific
+ * data to send ELS response mainly using infomation
+ * in exchange and sequence in EM layer.
+ */
+struct fc_seq_els_data {
+	struct fc_frame *fp;
+	enum fc_els_rjt_reason reason;
+	enum fc_els_rjt_explan explan;
+};
+
+/*
+ * FCP request structure, one for each scsi cmd request
+ */
+struct fc_fcp_pkt {
+	/*
+	 * housekeeping stuff
+	 */
+	struct fc_lport *lp;	/* handle to hba struct */
+	u16		state;		/* scsi_pkt state state */
+	u16		tgt_flags;	/* target flags	 */
+	atomic_t	ref_cnt;	/* fcp pkt ref count */
+	spinlock_t	scsi_pkt_lock;	/* Must be taken before the host lock
+					 * if both are held at the same time */
+	/*
+	 * SCSI I/O related stuff
+	 */
+	struct scsi_cmnd *cmd;		/* scsi command pointer. set/clear
+					 * under host lock */
+	struct list_head list;		/* tracks queued commands. access under
+					 * host lock */
+	/*
+	 * timeout related stuff
+	 */
+	struct timer_list timer;	/* command timer */
+	struct completion tm_done;
+	int	wait_for_comp;
+	unsigned long	start_time;	/* start jiffie */
+	unsigned long	end_time;	/* end jiffie */
+	unsigned long	last_pkt_time;	 /* jiffies of last frame received */
+
+	/*
+	 * scsi cmd and data transfer information
+	 */
+	u32		data_len;
+	/*
+	 * transport related veriables
+	 */
+	struct fcp_cmnd cdb_cmd;
+	size_t		xfer_len;
+	u32		xfer_contig_end; /* offset of end of contiguous xfer */
+	u16		max_payload;	/* max payload size in bytes */
+
+	/*
+	 * scsi/fcp return status
+	 */
+	u32		io_status;	/* SCSI result upper 24 bits */
+	u8		cdb_status;
+	u8		status_code;	/* FCP I/O status */
+	/* bit 3 Underrun bit 2: overrun */
+	u8		scsi_comp_flags;
+	u32		req_flags;	/* bit 0: read bit:1 write */
+	u32		scsi_resid;	/* residule length */
+
+	struct fc_rport	*rport;		/* remote port pointer */
+	struct fc_seq	*seq_ptr;	/* current sequence pointer */
+	/*
+	 * Error Processing
+	 */
+	u8		recov_retry;	/* count of recovery retries */
+	struct fc_seq	*recov_seq;	/* sequence for REC or SRR */
+};
+
+/*
+ * Structure and function definitions for managing Fibre Channel Exchanges
+ * and Sequences
+ *
+ * fc_exch holds state for one exchange and links to its active sequence.
+ *
+ * fc_seq holds the state for an individual sequence.
+ */
+
+struct fc_exch_mgr;
+
+/*
+ * Sequence.
+ */
+struct fc_seq {
+	u8	id;		/* seq ID */
+	u16	ssb_stat;	/* status flags for sequence status block */
+	u16	cnt;		/* frames sent so far on sequence */
+	u32	rec_data;	/* FC-4 value for REC */
+};
+
+#define FC_EX_DONE		(1 << 0) /* ep is completed */
+#define FC_EX_RST_CLEANUP	(1 << 1) /* reset is forcing completion */
+
+/*
+ * Exchange.
+ *
+ * Locking notes: The ex_lock protects changes to the following fields:
+ *	esb_stat, f_ctl, seq.ssb_stat, seq.f_ctl.
+ *	seq_id
+ *	sequence allocation
+ *
+ */
+struct fc_exch {
+	struct fc_exch_mgr *em;		/* exchange manager */
+	u32		state;		/* internal driver state */
+	u16		xid;		/* our exchange ID */
+	struct list_head	ex_list;	/* free or busy list linkage */
+	spinlock_t	ex_lock;	/* lock covering exchange state */
+	atomic_t	ex_refcnt;	/* reference counter */
+	struct work_struct timeout_work; /* timer for upper level protocols */
+	struct fc_lport	*lp;		/* fc device instance */
+	u16		oxid;		/* originator's exchange ID */
+	u16		rxid;		/* responder's exchange ID */
+	u32		oid;		/* originator's FCID */
+	u32		sid;		/* source FCID */
+	u32		did;		/* destination FCID */
+	u32		esb_stat;	/* exchange status for ESB */
+	u32		r_a_tov;	/* r_a_tov from rport (msec) */
+	u8		seq_id;		/* next sequence ID to use */
+	u32		f_ctl;		/* F_CTL flags for sequences */
+	u8		fh_type;	/* frame type */
+	enum fc_class	class;		/* class of service */
+	struct fc_seq	seq;		/* single sequence */
+	/*
+	 * Handler for responses to this current exchange.
+	 */
+	void		(*resp)(struct fc_seq *, struct fc_frame *, void *);
+	void		(*destructor)(struct fc_seq *, void *);
+	/*
+	 * arg is passed as void pointer to exchange
+	 * resp and destructor handlers
+	 */
+	void		*arg;
+};
+#define	fc_seq_exch(sp) container_of(sp, struct fc_exch, seq)
+
+struct libfc_function_template {
+
+	/**
+	 * Mandatory Fields
+	 *
+	 * These handlers must be implemented by the LLD.
+	 */
+
+	/*
+	 * Interface to send a FC frame
+	 */
+	int (*frame_send)(struct fc_lport *lp, struct fc_frame *fp);
+
+	/**
+	 * Optional Fields
+	 *
+	 * The LLD may choose to implement any of the following handlers.
+	 * If LLD doesn't specify hander and leaves its pointer NULL then
+	 * the default libfc function will be used for that handler.
+	 */
+
+	/**
+	 * ELS/CT interfaces
+	 */
+
+	/*
+	 * elsct_send - sends ELS/CT frame
+	 */
+	struct fc_seq *(*elsct_send)(struct fc_lport *lport,
+				     struct fc_rport *rport,
+				     struct fc_frame *fp,
+				     unsigned int op,
+				     void (*resp)(struct fc_seq *,
+					     struct fc_frame *fp,
+					     void *arg),
+				     void *arg, u32 timer_msec);
+	/**
+	 * Exhance Manager interfaces
+	 */
+
+	/*
+	 * Send the FC frame payload using a new exchange and sequence.
+	 *
+	 * The frame pointer with some of the header's fields must be
+	 * filled before calling exch_seq_send(), those fields are,
+	 *
+	 * - routing control
+	 * - FC port did
+	 * - FC port sid
+	 * - FC header type
+	 * - frame control
+	 * - parameter or relative offset
+	 *
+	 * The exchange response handler is set in this routine to resp()
+	 * function pointer. It can be called in two scenarios: if a timeout
+	 * occurs or if a response frame is received for the exchange. The
+	 * fc_frame pointer in response handler will also indicate timeout
+	 * as error using IS_ERR related macros.
+	 *
+	 * The exchange destructor handler is also set in this routine.
+	 * The destructor handler is invoked by EM layer when exchange
+	 * is about to free, this can be used by caller to free its
+	 * resources along with exchange free.
+	 *
+	 * The arg is passed back to resp and destructor handler.
+	 *
+	 * The timeout value (in msec) for an exchange is set if non zero
+	 * timer_msec argument is specified. The timer is canceled when
+	 * it fires or when the exchange is done. The exchange timeout handler
+	 * is registered by EM layer.
+	 */
+	struct fc_seq *(*exch_seq_send)(struct fc_lport *lp,
+					struct fc_frame *fp,
+					void (*resp)(struct fc_seq *sp,
+						     struct fc_frame *fp,
+						     void *arg),
+					void (*destructor)(struct fc_seq *sp,
+							   void *arg),
+					void *arg, unsigned int timer_msec);
+
+	/*
+	 * send a frame using existing sequence and exchange.
+	 */
+	int (*seq_send)(struct fc_lport *lp, struct fc_seq *sp,
+			struct fc_frame *fp);
+
+	/*
+	 * Send ELS response using mainly infomation
+	 * in exchange and sequence in EM layer.
+	 */
+	void (*seq_els_rsp_send)(struct fc_seq *sp, enum fc_els_cmd els_cmd,
+				 struct fc_seq_els_data *els_data);
+
+	/*
+	 * Abort an exchange and sequence. Generally called because of a
+	 * exchange timeout or an abort from the upper layer.
+	 *
+	 * A timer_msec can be specified for abort timeout, if non-zero
+	 * timer_msec value is specified then exchange resp handler
+	 * will be called with timeout error if no response to abort.
+	 */
+	int (*seq_exch_abort)(const struct fc_seq *req_sp,
+			      unsigned int timer_msec);
+
+	/*
+	 * Indicate that an exchange/sequence tuple is complete and the memory
+	 * allocated for the related objects may be freed.
+	 */
+	void (*exch_done)(struct fc_seq *sp);
+
+	/*
+	 * Assigns a EM and a free XID for an new exchange and then
+	 * allocates a new exchange and sequence pair.
+	 * The fp can be used to determine free XID.
+	 */
+	struct fc_exch *(*exch_get)(struct fc_lport *lp, struct fc_frame *fp);
+
+	/*
+	 * Release previously assigned XID by exch_get API.
+	 * The LLD may implement this if XID is assigned by LLD
+	 * in exch_get().
+	 */
+	void (*exch_put)(struct fc_lport *lp, struct fc_exch_mgr *mp,
+			 u16 ex_id);
+
+	/*
+	 * Start a new sequence on the same exchange/sequence tuple.
+	 */
+	struct fc_seq *(*seq_start_next)(struct fc_seq *sp);
+
+	/*
+	 * Reset an exchange manager, completing all sequences and exchanges.
+	 * If s_id is non-zero, reset only exchanges originating from that FID.
+	 * If d_id is non-zero, reset only exchanges sending to that FID.
+	 */
+	void (*exch_mgr_reset)(struct fc_exch_mgr *,
+			       u32 s_id, u32 d_id);
+
+	void (*rport_flush_queue)(void);
+	/**
+	 * Local Port interfaces
+	 */
+
+	/*
+	 * Receive a frame to a local port.
+	 */
+	void (*lport_recv)(struct fc_lport *lp, struct fc_seq *sp,
+			   struct fc_frame *fp);
+
+	int (*lport_reset)(struct fc_lport *);
+
+	/**
+	 * Remote Port interfaces
+	 */
+
+	/*
+	 * Initiates the RP state machine. It is called from the LP module.
+	 * This function will issue the following commands to the N_Port
+	 * identified by the FC ID provided.
+	 *
+	 * - PLOGI
+	 * - PRLI
+	 * - RTV
+	 */
+	int (*rport_login)(struct fc_rport *rport);
+
+	/*
+	 * Logoff, and remove the rport from the transport if
+	 * it had been added. This will send a LOGO to the target.
+	 */
+	int (*rport_logoff)(struct fc_rport *rport);
+
+	/*
+	 * Recieve a request from a remote port.
+	 */
+	void (*rport_recv_req)(struct fc_seq *, struct fc_frame *,
+			       struct fc_rport *);
+
+	struct fc_rport *(*rport_lookup)(const struct fc_lport *, u32);
+
+	/**
+	 * FCP interfaces
+	 */
+
+	/*
+	 * Send a fcp cmd from fsp pkt.
+	 * Called with the SCSI host lock unlocked and irqs disabled.
+	 *
+	 * The resp handler is called when FCP_RSP received.
+	 *
+	 */
+	int (*fcp_cmd_send)(struct fc_lport *lp, struct fc_fcp_pkt *fsp,
+			    void (*resp)(struct fc_seq *, struct fc_frame *fp,
+					 void *arg));
+
+	/*
+	 * Used at least durring linkdown and reset
+	 */
+	void (*fcp_cleanup)(struct fc_lport *lp);
+
+	/*
+	 * Abort all I/O on a local port
+	 */
+	void (*fcp_abort_io)(struct fc_lport *lp);
+
+	/**
+	 * Discovery interfaces
+	 */
+
+	void (*disc_recv_req)(struct fc_seq *,
+			      struct fc_frame *, struct fc_lport *);
+
+	/*
+	 * Start discovery for a local port.
+	 */
+	void (*disc_start)(void (*disc_callback)(struct fc_lport *,
+						 enum fc_disc_event),
+			   struct fc_lport *);
+
+	/*
+	 * Stop discovery for a given lport. This will remove
+	 * all discovered rports
+	 */
+	void (*disc_stop) (struct fc_lport *);
+
+	/*
+	 * Stop discovery for a given lport. This will block
+	 * until all discovered rports are deleted from the
+	 * FC transport class
+	 */
+	void (*disc_stop_final) (struct fc_lport *);
+};
+
+struct fc_disc;
+
+struct fc_lport {
+	struct list_head list;
+
+	/* Associations */
+	struct Scsi_Host	*host;
+	struct fc_exch_mgr	*emp;
+	struct fc_rport		*dns_rp;
+	struct fc_rport		*ptp_rp;
+	struct fc_disc          *disc;
+	void			*scsi_priv;
+
+	/* Operational Information */
+	struct libfc_function_template tt;
+	u16			link_status;
+	enum fc_lport_state	state;
+	unsigned long		boot_time;
+
+	struct fc_host_statistics host_stats;
+	struct fcoe_dev_stats	*dev_stats[NR_CPUS];
+	u64			wwpn;
+	u64			wwnn;
+	u8			retry_count;
+
+	/* Capabilities */
+	u32			sg_supp:1;	/* scatter gather supported */
+	u32			seq_offload:1;	/* seq offload supported */
+	u32			crc_offload:1;	/* crc offload supported */
+	u32			lro_enabled:1;	/* large receive offload */
+	u32			mfs;	        /* max FC payload size */
+	unsigned int		service_params;
+	unsigned int		e_d_tov;
+	unsigned int		r_a_tov;
+	u8			max_retry_count;
+	u16			link_speed;
+	u16			link_supported_speeds;
+	u16			lro_xid;	/* max xid for fcoe lro */
+	struct fc_ns_fts	fcts;	        /* FC-4 type masks */
+	struct fc_els_rnid_gen	rnid_gen;	/* RNID information */
+
+	/* Semaphores */
+	struct mutex lp_mutex;
+
+	/* Miscellaneous */
+	struct work_struct	retry_work;
+	struct work_struct	disc_work;
+};
+
+/**
+ * FC_LPORT HELPER FUNCTIONS
+ *****************************/
+static inline void *lport_priv(const struct fc_lport *lp)
+{
+	return (void *)(lp + 1);
+}
+
+static inline int fc_lport_test_ready(struct fc_lport *lp)
+{
+	return lp->state == LPORT_ST_READY;
+}
+
+static inline void fc_set_wwnn(struct fc_lport *lp, u64 wwnn)
+{
+	lp->wwnn = wwnn;
+}
+
+static inline void fc_set_wwpn(struct fc_lport *lp, u64 wwnn)
+{
+	lp->wwpn = wwnn;
+}
+
+static inline void fc_lport_state_enter(struct fc_lport *lp,
+					enum fc_lport_state state)
+{
+	if (state != lp->state)
+		lp->retry_count = 0;
+	lp->state = state;
+}
+
+
+/**
+ * LOCAL PORT LAYER
+ *****************************/
+int fc_lport_init(struct fc_lport *lp);
+
+/*
+ * Destroy the specified local port by finding and freeing all
+ * fc_rports associated with it and then by freeing the fc_lport
+ * itself.
+ */
+int fc_lport_destroy(struct fc_lport *lp);
+
+/*
+ * Logout the specified local port from the fabric
+ */
+int fc_fabric_logoff(struct fc_lport *lp);
+
+/*
+ * Initiate the LP state machine. This handler will use fc_host_attr
+ * to store the FLOGI service parameters, so fc_host_attr must be
+ * initialized before calling this handler.
+ */
+int fc_fabric_login(struct fc_lport *lp);
+
+/*
+ * The link is up for the given local port.
+ */
+void fc_linkup(struct fc_lport *);
+
+/*
+ * Link is down for the given local port.
+ */
+void fc_linkdown(struct fc_lport *);
+
+/*
+ * Pause and unpause traffic.
+ */
+void fc_pause(struct fc_lport *);
+void fc_unpause(struct fc_lport *);
+
+/*
+ * Configure the local port.
+ */
+int fc_lport_config(struct fc_lport *);
+
+/*
+ * Reset the local port.
+ */
+int fc_lport_reset(struct fc_lport *);
+
+/*
+ * Set the mfs or reset
+ */
+int fc_set_mfs(struct fc_lport *lp, u32 mfs);
+
+
+/**
+ * REMOTE PORT LAYER
+ *****************************/
+int fc_rport_init(struct fc_lport *lp);
+void fc_rport_terminate_io(struct fc_rport *rp);
+
+/**
+ * DISCOVERY LAYER
+ *****************************/
+int fc_disc_init(struct fc_lport *lp);
+
+
+/**
+ * SCSI LAYER
+ *****************************/
+/*
+ * Initialize the SCSI block of libfc
+ */
+int fc_fcp_init(struct fc_lport *);
+
+/*
+ * This section provides an API which allows direct interaction
+ * with the SCSI-ml. Each of these functions satisfies a function
+ * pointer defined in Scsi_Host and therefore is always called
+ * directly from the SCSI-ml.
+ */
+int fc_queuecommand(struct scsi_cmnd *sc_cmd,
+		    void (*done)(struct scsi_cmnd *));
+
+/*
+ * complete processing of a fcp packet
+ *
+ * This function may sleep if a fsp timer is pending.
+ * The host lock must not be held by caller.
+ */
+void fc_fcp_complete(struct fc_fcp_pkt *fsp);
+
+/*
+ * Send an ABTS frame to the target device. The sc_cmd argument
+ * is a pointer to the SCSI command to be aborted.
+ */
+int fc_eh_abort(struct scsi_cmnd *sc_cmd);
+
+/*
+ * Reset a LUN by sending send the tm cmd to the target.
+ */
+int fc_eh_device_reset(struct scsi_cmnd *sc_cmd);
+
+/*
+ * Reset the host adapter.
+ */
+int fc_eh_host_reset(struct scsi_cmnd *sc_cmd);
+
+/*
+ * Check rport status.
+ */
+int fc_slave_alloc(struct scsi_device *sdev);
+
+/*
+ * Adjust the queue depth.
+ */
+int fc_change_queue_depth(struct scsi_device *sdev, int qdepth);
+
+/*
+ * Change the tag type.
+ */
+int fc_change_queue_type(struct scsi_device *sdev, int tag_type);
+
+/*
+ * Free memory pools used by the FCP layer.
+ */
+void fc_fcp_destroy(struct fc_lport *);
+
+/**
+ * ELS/CT interface
+ *****************************/
+/*
+ * Initializes ELS/CT interface
+ */
+int fc_elsct_init(struct fc_lport *lp);
+
+
+/**
+ * EXCHANGE MANAGER LAYER
+ *****************************/
+/*
+ * Initializes Exchange Manager related
+ * function pointers in struct libfc_function_template.
+ */
+int fc_exch_init(struct fc_lport *lp);
+
+/*
+ * Allocates an Exchange Manager (EM).
+ *
+ * The EM manages exchanges for their allocation and
+ * free, also allows exchange lookup for received
+ * frame.
+ *
+ * The class is used for initializing FC class of
+ * allocated exchange from EM.
+ *
+ * The min_xid and max_xid will limit new
+ * exchange ID (XID) within this range for
+ * a new exchange.
+ * The LLD may choose to have multiple EMs,
+ * e.g. one EM instance per CPU receive thread in LLD.
+ * The LLD can use exch_get() of struct libfc_function_template
+ * to specify XID for a new exchange within
+ * a specified EM instance.
+ *
+ * The em_idx to uniquely identify an EM instance.
+ */
+struct fc_exch_mgr *fc_exch_mgr_alloc(struct fc_lport *lp,
+				      enum fc_class class,
+				      u16 min_xid,
+				      u16 max_xid);
+
+/*
+ * Free an exchange manager.
+ */
+void fc_exch_mgr_free(struct fc_exch_mgr *mp);
+
+/*
+ * Receive a frame on specified local port and exchange manager.
+ */
+void fc_exch_recv(struct fc_lport *lp, struct fc_exch_mgr *mp,
+		  struct fc_frame *fp);
+
+/*
+ * This function is for exch_seq_send function pointer in
+ * struct libfc_function_template, see comment block on
+ * exch_seq_send for description of this function.
+ */
+struct fc_seq *fc_exch_seq_send(struct fc_lport *lp,
+				struct fc_frame *fp,
+				void (*resp)(struct fc_seq *sp,
+					     struct fc_frame *fp,
+					     void *arg),
+				void (*destructor)(struct fc_seq *sp,
+						   void *arg),
+				void *arg, u32 timer_msec);
+
+/*
+ * send a frame using existing sequence and exchange.
+ */
+int fc_seq_send(struct fc_lport *lp, struct fc_seq *sp, struct fc_frame *fp);
+
+/*
+ * Send ELS response using mainly infomation
+ * in exchange and sequence in EM layer.
+ */
+void fc_seq_els_rsp_send(struct fc_seq *sp, enum fc_els_cmd els_cmd,
+			 struct fc_seq_els_data *els_data);
+
+/*
+ * This function is for seq_exch_abort function pointer in
+ * struct libfc_function_template, see comment block on
+ * seq_exch_abort for description of this function.
+ */
+int fc_seq_exch_abort(const struct fc_seq *req_sp, unsigned int timer_msec);
+
+/*
+ * Indicate that an exchange/sequence tuple is complete and the memory
+ * allocated for the related objects may be freed.
+ */
+void fc_exch_done(struct fc_seq *sp);
+
+/*
+ * Assigns a EM and XID for a frame and then allocates
+ * a new exchange and sequence pair.
+ * The fp can be used to determine free XID.
+ */
+struct fc_exch *fc_exch_get(struct fc_lport *lp, struct fc_frame *fp);
+
+/*
+ * Allocate a new exchange and sequence pair.
+ * if ex_id is zero then next free exchange id
+ * from specified exchange manger mp will be assigned.
+ */
+struct fc_exch *fc_exch_alloc(struct fc_exch_mgr *mp,
+			      struct fc_frame *fp, u16 ex_id);
+/*
+ * Start a new sequence on the same exchange as the supplied sequence.
+ */
+struct fc_seq *fc_seq_start_next(struct fc_seq *sp);
+
+/*
+ * Reset an exchange manager, completing all sequences and exchanges.
+ * If s_id is non-zero, reset only exchanges originating from that FID.
+ * If d_id is non-zero, reset only exchanges sending to that FID.
+ */
+void fc_exch_mgr_reset(struct fc_exch_mgr *, u32 s_id, u32 d_id);
+
+/*
+ * Functions for fc_functions_template
+ */
+void fc_get_host_speed(struct Scsi_Host *shost);
+void fc_get_host_port_type(struct Scsi_Host *shost);
+void fc_get_host_port_state(struct Scsi_Host *shost);
+void fc_set_rport_loss_tmo(struct fc_rport *rport, u32 timeout);
+struct fc_host_statistics *fc_get_host_stats(struct Scsi_Host *);
+
+/*
+ * module setup functions.
+ */
+int fc_setup_exch_mgr(void);
+void fc_destroy_exch_mgr(void);
+int fc_setup_rport(void);
+void fc_destroy_rport(void);
+
+#endif /* _LIBFC_H_ */
diff --git a/include/scsi/libfc/fc_frame.h b/include/scsi/libfc/fc_frame.h
deleted file mode 100644
index 9508e55..0000000
--- a/include/scsi/libfc/fc_frame.h
+++ /dev/null
@@ -1,238 +0,0 @@
-/*
- * Copyright(c) 2007 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,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Maintained at www.Open-FCoE.org
- */
-
-#ifndef _FC_FRAME_H_
-#define _FC_FRAME_H_
-
-#include <linux/scatterlist.h>
-#include <linux/skbuff.h>
-
-#include <scsi/fc/fc_fs.h>
-#include <scsi/fc/fc_encaps.h>
-
-/*
- * The fc_frame interface is used to pass frame data between functions.
- * The frame includes the data buffer, length, and SOF / EOF delimiter types.
- * A pointer to the port structure of the receiving port is also includeded.
- */
-
-#define	FC_FRAME_HEADROOM	32	/* headroom for VLAN + FCoE headers */
-#define	FC_FRAME_TAILROOM	8	/* trailer space for FCoE */
-
-/*
- * Information about an individual fibre channel frame received or to be sent.
- * The buffer may be in up to 4 additional non-contiguous sections,
- * but the linear section must hold the frame header.
- */
-#define FC_FRAME_SG_LEN		4	/* scatter/gather list maximum length */
-
-#define fp_skb(fp)	(&((fp)->skb))
-#define fr_hdr(fp)	((fp)->skb.data)
-#define fr_len(fp)	((fp)->skb.len)
-#define fr_cb(fp)	((struct fcoe_rcv_info *)&((fp)->skb.cb[0]))
-#define fr_dev(fp)	(fr_cb(fp)->fr_dev)
-#define fr_seq(fp)	(fr_cb(fp)->fr_seq)
-#define fr_sof(fp)	(fr_cb(fp)->fr_sof)
-#define fr_eof(fp)	(fr_cb(fp)->fr_eof)
-#define fr_flags(fp)	(fr_cb(fp)->fr_flags)
-#define fr_max_payload(fp)	(fr_cb(fp)->fr_max_payload)
-
-struct fc_frame {
-	struct sk_buff skb;
-};
-
-struct fcoe_rcv_info {
-	struct packet_type  *ptype;
-	struct fc_lport	*fr_dev;	/* transport layer private pointer */
-	struct fc_seq	*fr_seq;	/* for use with exchange manager */
-	enum fc_sof	fr_sof;		/* start of frame delimiter */
-	enum fc_eof	fr_eof;		/* end of frame delimiter */
-	u8		fr_flags;	/* flags - see below */
-	u16		fr_max_payload;	/* max FC payload */
-};
-
-/*
- * Get fc_frame pointer for an skb that's already been imported.
- */
-static inline struct fcoe_rcv_info *fcoe_dev_from_skb(const struct sk_buff *skb)
-{
-	BUILD_BUG_ON(sizeof(struct fcoe_rcv_info) > sizeof(skb->cb));
-	return (struct fcoe_rcv_info *) skb->cb;
-}
-
-/*
- * fr_flags.
- */
-#define	FCPHF_CRC_UNCHECKED	0x01	/* CRC not computed, still appended */
-
-/*
- * Initialize a frame.
- * We don't do a complete memset here for performance reasons.
- * The caller must set fr_free, fr_hdr, fr_len, fr_sof, and fr_eof eventually.
- */
-static inline void fc_frame_init(struct fc_frame *fp)
-{
-	fr_dev(fp) = NULL;
-	fr_seq(fp) = NULL;
-	fr_flags(fp) = 0;
-}
-
-struct fc_frame *fc_frame_alloc_fill(struct fc_lport *, size_t payload_len);
-
-struct fc_frame *__fc_frame_alloc(size_t payload_len);
-
-/*
- * Get frame for sending via port.
- */
-static inline struct fc_frame *_fc_frame_alloc(struct fc_lport *dev,
-					       size_t payload_len)
-{
-	return __fc_frame_alloc(payload_len);
-}
-
-/*
- * Allocate fc_frame structure and buffer.  Set the initial length to
- * payload_size + sizeof (struct fc_frame_header).
- */
-static inline struct fc_frame *fc_frame_alloc(struct fc_lport *dev, size_t len)
-{
-	struct fc_frame *fp;
-
-	/*
-	 * Note: Since len will often be a constant multiple of 4,
-	 * this check will usually be evaluated and eliminated at compile time.
-	 */
-	if ((len % 4) != 0)
-		fp = fc_frame_alloc_fill(dev, len);
-	else
-		fp = _fc_frame_alloc(dev, len);
-	return fp;
-}
-
-/*
- * Free the fc_frame structure and buffer.
- */
-static inline void fc_frame_free(struct fc_frame *fp)
-{
-	kfree_skb(fp_skb(fp));
-}
-
-static inline int fc_frame_is_linear(struct fc_frame *fp)
-{
-	return !skb_is_nonlinear(fp_skb(fp));
-}
-
-/*
- * Get frame header from message in fc_frame structure.
- * This hides a cast and provides a place to add some checking.
- */
-static inline
-struct fc_frame_header *fc_frame_header_get(const struct fc_frame *fp)
-{
-	WARN_ON(fr_len(fp) < sizeof(struct fc_frame_header));
-	return (struct fc_frame_header *) fr_hdr(fp);
-}
-
-/*
- * Get frame payload from message in fc_frame structure.
- * This hides a cast and provides a place to add some checking.
- * The len parameter is the minimum length for the payload portion.
- * Returns NULL if the frame is too short.
- *
- * This assumes the interesting part of the payload is in the first part
- * of the buffer for received data.  This may not be appropriate to use for
- * buffers being transmitted.
- */
-static inline void *fc_frame_payload_get(const struct fc_frame *fp,
-					 size_t len)
-{
-	void *pp = NULL;
-
-	if (fr_len(fp) >= sizeof(struct fc_frame_header) + len)
-		pp = fc_frame_header_get(fp) + 1;
-	return pp;
-}
-
-/*
- * Get frame payload opcode (first byte) from message in fc_frame structure.
- * This hides a cast and provides a place to add some checking. Return 0
- * if the frame has no payload.
- */
-static inline u8 fc_frame_payload_op(const struct fc_frame *fp)
-{
-	u8 *cp;
-
-	cp = fc_frame_payload_get(fp, sizeof(u8));
-	if (!cp)
-		return 0;
-	return *cp;
-
-}
-
-/*
- * Get FC class from frame.
- */
-static inline enum fc_class fc_frame_class(const struct fc_frame *fp)
-{
-	return fc_sof_class(fr_sof(fp));
-}
-
-/*
- * Set r_ctl and type in preparation for sending frame.
- * This also clears fh_parm_offset.
- */
-static inline void fc_frame_setup(struct fc_frame *fp, enum fc_rctl r_ctl,
-				  enum fc_fh_type type)
-{
-	struct fc_frame_header *fh;
-
-	fh = fc_frame_header_get(fp);
-	WARN_ON(r_ctl == 0);
-	fh->fh_r_ctl = r_ctl;
-	fh->fh_type = type;
-	fh->fh_parm_offset = htonl(0);
-}
-
-/*
- * Set offset in preparation for sending frame.
- */
-static inline void
-fc_frame_set_offset(struct fc_frame *fp, u32 offset)
-{
-	struct fc_frame_header *fh;
-
-	fh = fc_frame_header_get(fp);
-	fh->fh_parm_offset = htonl(offset);
-}
-
-/*
- * Check the CRC in a frame.
- * The CRC immediately follows the last data item *AFTER* the length.
- * The return value is zero if the CRC matches.
- */
-u32 fc_frame_crc_check(struct fc_frame *);
-
-/*
- * Check for leaks.
- * Print the frame header of any currently allocated frame, assuming there
- * should be none at this point.
- */
-void fc_frame_leak_check(void);
-
-#endif /* _FC_FRAME_H_ */
diff --git a/include/scsi/libfc/libfc.h b/include/scsi/libfc/libfc.h
deleted file mode 100644
index 26700cb..0000000
--- a/include/scsi/libfc/libfc.h
+++ /dev/null
@@ -1,781 +0,0 @@
-/*
- * Copyright(c) 2007 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,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Maintained at www.Open-FCoE.org
- */
-
-#ifndef _LIBFC_H_
-#define _LIBFC_H_
-
-#include <linux/timer.h>
-#include <linux/if.h>
-
-#include <scsi/scsi_device.h>
-#include <scsi/scsi_host.h>
-#include <scsi/scsi_transport.h>
-#include <scsi/scsi_transport_fc.h>
-
-#include <scsi/fc/fc_fcp.h>
-#include <scsi/fc/fc_ns.h>
-#include <scsi/fc/fc_els.h>
-#include <scsi/fc/fc_gs.h>
-
-#include <scsi/libfc/fc_frame.h>
-
-#define LIBFC_DEBUG
-
-#ifdef LIBFC_DEBUG
-/* Log messages */
-#define FC_DBG(fmt, args...)						\
-	do {								\
-		printk(KERN_INFO "%s " fmt, __func__, ##args);		\
-	} while (0)
-#else
-#define FC_DBG(fmt, args...)
-#endif
-
-/*
- * libfc error codes
- */
-#define	FC_NO_ERR	0	/* no error */
-#define	FC_EX_TIMEOUT	1	/* Exchange timeout */
-#define	FC_EX_CLOSED	2	/* Exchange closed */
-
-/* some helpful macros */
-
-#define ntohll(x) be64_to_cpu(x)
-#define htonll(x) cpu_to_be64(x)
-
-#define ntoh24(p) (((p)[0] << 16) | ((p)[1] << 8) | ((p)[2]))
-
-#define hton24(p, v)	do {			\
-		p[0] = (((v) >> 16) & 0xFF);	\
-		p[1] = (((v) >> 8) & 0xFF);	\
-		p[2] = ((v) & 0xFF);		\
-	} while (0)
-
-struct fc_exch_mgr;
-
-/*
- * FC HBA status
- */
-#define FC_PAUSE		    (1 << 1)
-#define FC_LINK_UP		    (1 << 0)
-
-enum fc_lport_state {
-	LPORT_ST_NONE = 0,
-	LPORT_ST_FLOGI,
-	LPORT_ST_DNS,
-	LPORT_ST_RPN_ID,
-	LPORT_ST_RFT_ID,
-	LPORT_ST_SCR,
-	LPORT_ST_READY,
-	LPORT_ST_LOGO,
-	LPORT_ST_RESET
-};
-
-enum fc_lport_event {
-	LPORT_EV_RPORT_NONE = 0,
-	LPORT_EV_RPORT_CREATED,
-	LPORT_EV_RPORT_FAILED,
-	LPORT_EV_RPORT_STOP,
-	LPORT_EV_RPORT_LOGO
-};
-
-enum fc_rport_state {
-	RPORT_ST_NONE = 0,
-	RPORT_ST_INIT,		/* initialized */
-	RPORT_ST_PLOGI,		/* waiting for PLOGI completion */
-	RPORT_ST_PRLI,		/* waiting for PRLI completion */
-	RPORT_ST_RTV,		/* waiting for RTV completion */
-	RPORT_ST_READY,		/* ready for use */
-	RPORT_ST_LOGO,		/* port logout sent */
-};
-
-enum fc_rport_trans_state {
-	FC_PORTSTATE_ROGUE,
-	FC_PORTSTATE_REAL,
-};
-
-/**
- * struct fc_disc_port - temporary discovery port to hold rport identifiers
- * @lp: Fibre Channel host port instance
- * @peers: node for list management during discovery and RSCN processing
- * @ids: identifiers structure to pass to fc_remote_port_add()
- * @rport_work: work struct for starting the rport state machine
- */
-struct fc_disc_port {
-	struct fc_lport             *lp;
-	struct list_head            peers;
-	struct fc_rport_identifiers ids;
-	struct work_struct	    rport_work;
-};
-
-/**
- * struct fc_rport_libfc_priv - libfc internal information about a remote port
- * @local_port: Fibre Channel host port instance
- * @rp_state: state tracks progress of PLOGI, PRLI, and RTV exchanges
- * @flags: REC and RETRY supported flags
- * @max_seq: maximum number of concurrent sequences
- * @retries: retry count in current state
- * @e_d_tov: error detect timeout value (in msec)
- * @r_a_tov: resource allocation timeout value (in msec)
- * @rp_mutex: mutex protects rport
- * @retry_work:
- * @event_callback: Callback for rport READY, FAILED or LOGO
- */
-struct fc_rport_libfc_priv {
-	struct fc_lport		*local_port;
-	enum fc_rport_state rp_state;
-	u16			flags;
-	#define FC_RP_FLAGS_REC_SUPPORTED	(1 << 0)
-	#define FC_RP_FLAGS_RETRY		(1 << 1)
-	u16		max_seq;
-	unsigned int	retries;
-	unsigned int	e_d_tov;
-	unsigned int	r_a_tov;
-	enum fc_rport_trans_state trans_state;
-	struct mutex    rp_mutex;
-	struct work_struct	retry_work;
-	enum fc_lport_event     event;
-	void (*event_callback)(struct fc_lport *,
-			       struct fc_rport *,
-			       enum fc_lport_event);
-	struct list_head         peers;
-	struct work_struct       event_work;
-};
-
-#define PRIV_TO_RPORT(x)						\
-	(struct fc_rport*)((void *)x - sizeof(struct fc_rport));
-#define RPORT_TO_PRIV(x)						\
-	(struct fc_rport_libfc_priv*)((void *)x + sizeof(struct fc_rport));
-
-struct fc_rport *fc_rport_rogue_create(struct fc_disc_port *);
-void fc_rport_rogue_destroy(struct fc_rport *);
-
-static inline void fc_rport_set_name(struct fc_rport *rport, u64 wwpn, u64 wwnn)
-{
-	rport->node_name = wwnn;
-	rport->port_name = wwpn;
-}
-
-/*
- * fcoe stats structure
- */
-struct fcoe_dev_stats {
-	u64		SecondsSinceLastReset;
-	u64		TxFrames;
-	u64		TxWords;
-	u64		RxFrames;
-	u64		RxWords;
-	u64		ErrorFrames;
-	u64		DumpedFrames;
-	u64		LinkFailureCount;
-	u64		LossOfSignalCount;
-	u64		InvalidTxWordCount;
-	u64		InvalidCRCCount;
-	u64		InputRequests;
-	u64		OutputRequests;
-	u64		ControlRequests;
-	u64		InputMegabytes;
-	u64		OutputMegabytes;
-};
-
-/*
- * els data is used for passing ELS respone specific
- * data to send ELS response mainly using infomation
- * in exchange and sequence in EM layer.
- */
-struct fc_seq_els_data {
-	struct fc_frame *fp;
-	enum fc_els_rjt_reason reason;
-	enum fc_els_rjt_explan explan;
-};
-
-struct libfc_function_template {
-
-	/**
-	 * Mandatory Fields
-	 *
-	 * These handlers must be implemented by the LLD.
-	 */
-
-	/*
-	 * Interface to send a FC frame
-	 */
-	int (*frame_send)(struct fc_lport *lp, struct fc_frame *fp);
-
-	/**
-	 * Optional Fields
-	 *
-	 * The LLD may choose to implement any of the following handlers.
-	 * If LLD doesn't specify hander and leaves its pointer NULL then
-	 * the default libfc function will be used for that handler.
-	 */
-
-	/**
-	 * Exhance Manager interfaces
-	 */
-
-	/*
-	 * Send the FC frame payload using a new exchange and sequence.
-	 *
-	 * The frame pointer with some of the header's fields must be
-	 * filled before calling exch_seq_send(), those fields are,
-	 *
-	 * - routing control
-	 * - FC header type
-	 * - parameter or relative offset
-	 *
-	 * The exchange response handler is set in this routine to resp()
-	 * function pointer. It can be called in two scenarios: if a timeout
-	 * occurs or if a response frame is received for the exchange. The
-	 * fc_frame pointer in response handler will also indicate timeout
-	 * as error using IS_ERR related macros.
-	 *
-	 * The exchange destructor handler is also set in this routine.
-	 * The destructor handler is invoked by EM layer when exchange
-	 * is about to free, this can be used by caller to free its
-	 * resources along with exchange free.
-	 *
-	 * The arg is passed back to resp and destructor handler.
-	 *
-	 * The timeout value (in msec) for an exchange is set if non zero
-	 * timer_msec argument is specified. The timer is canceled when
-	 * it fires or when the exchange is done. The exchange timeout handler
-	 * is registered by EM layer.
-	 *
-	 * The caller also need to specify FC sid, did and frame control field.
-	 */
-	struct fc_seq *(*exch_seq_send)(struct fc_lport *lp,
-					struct fc_frame *fp,
-					void (*resp)(struct fc_seq *sp,
-						     struct fc_frame *fp,
-						     void *arg),
-					void (*destructor)(struct fc_seq *sp,
-							   void *arg),
-					void *arg, unsigned int timer_msec,
-					u32 sid, u32 did, u32 f_ctl);
-
-	/*
-	 * send a frame using existing sequence and exchange.
-	 */
-	int (*seq_send)(struct fc_lport *lp, struct fc_seq *sp,
-			struct fc_frame *fp, u32 f_ctl);
-
-	/*
-	 * Send ELS response using mainly infomation
-	 * in exchange and sequence in EM layer.
-	 */
-	void (*seq_els_rsp_send)(struct fc_seq *sp, enum fc_els_cmd els_cmd,
-				 struct fc_seq_els_data *els_data);
-
-	/*
-	 * Abort an exchange and sequence. Generally called because of a
-	 * exchange timeout or an abort from the upper layer.
-	 *
-	 * A timer_msec can be specified for abort timeout, if non-zero
-	 * timer_msec value is specified then exchange resp handler
-	 * will be called with timeout error if no response to abort.
-	 */
-	int (*seq_exch_abort)(const struct fc_seq *req_sp,
-			      unsigned int timer_msec);
-
-	/*
-	 * Indicate that an exchange/sequence tuple is complete and the memory
-	 * allocated for the related objects may be freed.
-	 */
-	void (*exch_done)(struct fc_seq *sp);
-
-	/*
-	 * Assigns a EM and a free XID for an new exchange and then
-	 * allocates a new exchange and sequence pair.
-	 * The fp can be used to determine free XID.
-	 */
-	struct fc_exch *(*exch_get)(struct fc_lport *lp, struct fc_frame *fp);
-
-	/*
-	 * Release previously assigned XID by exch_get API.
-	 * The LLD may implement this if XID is assigned by LLD
-	 * in exch_get().
-	 */
-	void (*exch_put)(struct fc_lport *lp, struct fc_exch_mgr *mp,
-			 u16 ex_id);
-
-	/*
-	 * Start a new sequence on the same exchange/sequence tuple.
-	 */
-	struct fc_seq *(*seq_start_next)(struct fc_seq *sp);
-
-	/*
-	 * Reset an exchange manager, completing all sequences and exchanges.
-	 * If s_id is non-zero, reset only exchanges originating from that FID.
-	 * If d_id is non-zero, reset only exchanges sending to that FID.
-	 */
-	void (*exch_mgr_reset)(struct fc_exch_mgr *,
-			       u32 s_id, u32 d_id);
-
-	/*
-	 * Get exchange Ids of a sequence
-	 */
-	void (*seq_get_xids)(struct fc_seq *sp, u16 *oxid, u16 *rxid);
-
-	/*
-	 * Set REC data to a sequence
-	 */
-	void (*seq_set_rec_data)(struct fc_seq *sp, u32 rec_data);
-
-	/**
-	 * Local Port interfaces
-	 */
-
-	/*
-	 * Receive a frame to a local port.
-	 */
-	void (*lport_recv)(struct fc_lport *lp, struct fc_seq *sp,
-			   struct fc_frame *fp);
-
-	int (*lport_reset)(struct fc_lport *);
-
-	void (*event_callback)(struct fc_lport *, struct fc_rport *,
-			       enum fc_lport_event);
-
-	/**
-	 * Remote Port interfaces
-	 */
-
-	/*
-	 * Initiates the RP state machine. It is called from the LP module.
-	 * This function will issue the following commands to the N_Port
-	 * identified by the FC ID provided.
-	 *
-	 * - PLOGI
-	 * - PRLI
-	 * - RTV
-	 */
-	int (*rport_login)(struct fc_rport *rport);
-
-	/*
-	 * Logs the specified local port out of a N_Port identified
-	 * by the ID provided.
-	 */
-	int (*rport_logout)(struct fc_rport *rport);
-
-        /*
-	 * Delete the rport and remove it from the transport if
-	 * it had been added. This will not send a LOGO, use
-	 * rport_logout for a gracefull logout.
-	 */
-	int (*rport_stop)(struct fc_rport *rport);
-
-	/*
-	 * Recieve a request from a remote port.
-	 */
-	void (*rport_recv_req)(struct fc_seq *, struct fc_frame *,
-			       struct fc_rport *);
-
-	struct fc_rport *(*rport_lookup)(const struct fc_lport *, u32);
-
-	/**
-	 * SCSI interfaces
-	 */
-
-	/*
-	 * Used at least durring linkdown and reset
-	 */
-	void (*scsi_cleanup)(struct fc_lport *);
-
-	/*
-	 * Abort all I/O on a local port
-	 */
-	void (*scsi_abort_io)(struct fc_lport *);
-
-	/**
-	 * Discovery interfaces
-	 */
-
-	void (*disc_recv_req)(struct fc_seq *,
-			      struct fc_frame *, struct fc_lport *);
-
-	/*
-	 * Start discovery for a local port.
-	 */
-	int (*disc_start)(struct fc_lport *);
-};
-
-struct fc_lport {
-	struct list_head list;
-
-	/* Associations */
-	struct Scsi_Host	*host;
-	struct fc_exch_mgr	*emp;
-	struct fc_rport		*dns_rp;
-	struct fc_rport		*ptp_rp;
-	void			*scsi_priv;
-	struct list_head         rports;
-
-	/* Operational Information */
-	struct libfc_function_template tt;
-	u16			link_status;
-	u8			disc_done;
-	enum fc_lport_state	state;
-	unsigned long		boot_time;
-
-	struct fc_host_statistics host_stats;
-	struct fcoe_dev_stats	*dev_stats[NR_CPUS];
-
-	u64			wwpn;
-	u64			wwnn;
-	u8			retry_count;
-	unsigned char		disc_retry_count;
-	unsigned char		disc_delay;
-	unsigned char		disc_pending;
-	unsigned char		disc_requested;
-	unsigned short		disc_seq_count;
-	unsigned char		disc_buf_len;
-
-	/* Capabilities */
-	u32			sg_supp:1;	/* scatter gather supported */
-	u32			seq_offload:1;	/* seq offload supported */
-	u32			mfs;	/* max FC payload size */
-	unsigned int		service_params;
-	unsigned int		e_d_tov;
-	unsigned int		r_a_tov;
-	u8			max_retry_count;
-	u16			link_speed;
-	u16			link_supported_speeds;
-	struct fc_ns_fts	fcts;	        /* FC-4 type masks */
-	struct fc_els_rnid_gen	rnid_gen;	/* RNID information */
-
-	/* Semaphores */
-	struct mutex lp_mutex;
-
-	/* Miscellaneous */
-	struct fc_gpn_ft_resp	disc_buf;	/* partial name buffer */
-	struct work_struct	retry_work;
-	struct work_struct	disc_work;
-
-	void			*drv_priv;
-};
-
-/**
- * FC_LPORT HELPER FUNCTIONS
- *****************************/
-
-static inline int fc_lport_test_ready(struct fc_lport *lp)
-{
-	return lp->state == LPORT_ST_READY;
-}
-
-static inline void fc_set_wwnn(struct fc_lport *lp, u64 wwnn)
-{
-	lp->wwnn = wwnn;
-}
-
-static inline void fc_set_wwpn(struct fc_lport *lp, u64 wwnn)
-{
-	lp->wwpn = wwnn;
-}
-
-/**
- * fc_fill_dns_hdr - Fill in a name service request header
- * @lp: Fibre Channel host port instance
- * @ct: Common Transport (CT) header structure
- * @op: Name Service request code
- * @req_size: Full size of Name Service request
- */
-static inline void fc_fill_dns_hdr(struct fc_lport *lp, struct fc_ct_hdr *ct,
-				   unsigned int op, unsigned int req_size)
-{
-	memset(ct, 0, sizeof(*ct) + req_size);
-	ct->ct_rev = FC_CT_REV;
-	ct->ct_fs_type = FC_FST_DIR;
-	ct->ct_fs_subtype = FC_NS_SUBTYPE;
-	ct->ct_cmd = htons((u16) op);
-}
-
-static inline void fc_lport_state_enter(struct fc_lport *lp,
-					enum fc_lport_state state)
-{
-	if (state != lp->state)
-		lp->retry_count = 0;
-	lp->state = state;
-}
-
-
-/**
- * LOCAL PORT LAYER
- *****************************/
-int fc_lport_init(struct fc_lport *lp);
-
-/*
- * Destroy the specified local port by finding and freeing all
- * fc_rports associated with it and then by freeing the fc_lport
- * itself.
- */
-int fc_lport_destroy(struct fc_lport *lp);
-
-/*
- * Logout the specified local port from the fabric
- */
-int fc_fabric_logoff(struct fc_lport *lp);
-
-/*
- * Initiate the LP state machine. This handler will use fc_host_attr
- * to store the FLOGI service parameters, so fc_host_attr must be
- * initialized before calling this handler.
- */
-int fc_fabric_login(struct fc_lport *lp);
-
-/*
- * The link is up for the given local port.
- */
-void fc_linkup(struct fc_lport *);
-
-/*
- * Link is down for the given local port.
- */
-void fc_linkdown(struct fc_lport *);
-
-/*
- * Pause and unpause traffic.
- */
-void fc_pause(struct fc_lport *);
-void fc_unpause(struct fc_lport *);
-
-/*
- * Configure the local port.
- */
-int fc_lport_config(struct fc_lport *);
-
-/*
- * Reset the local port.
- */
-int fc_lport_reset(struct fc_lport *);
-
-/*
- * Set the mfs or reset
- */
-int fc_set_mfs(struct fc_lport *lp, u32 mfs);
-
-
-/**
- * REMOTE PORT LAYER
- *****************************/
-int fc_rport_init(struct fc_lport *lp);
-void fc_rport_terminate_io(struct fc_rport *rp);
-
-/**
- * DISCOVERY LAYER
- *****************************/
-int fc_disc_init(struct fc_lport *lp);
-
-
-/**
- * SCSI LAYER
- *****************************/
-/*
- * Initialize the SCSI block of libfc
- */
-int fc_fcp_init(struct fc_lport *);
-
-/*
- * This section provides an API which allows direct interaction
- * with the SCSI-ml. Each of these functions satisfies a function
- * pointer defined in Scsi_Host and therefore is always called
- * directly from the SCSI-ml.
- */
-int fc_queuecommand(struct scsi_cmnd *sc_cmd,
-		    void (*done)(struct scsi_cmnd *));
-
-/*
- * Send an ABTS frame to the target device. The sc_cmd argument
- * is a pointer to the SCSI command to be aborted.
- */
-int fc_eh_abort(struct scsi_cmnd *sc_cmd);
-
-/*
- * Reset a LUN by sending send the tm cmd to the target.
- */
-int fc_eh_device_reset(struct scsi_cmnd *sc_cmd);
-
-/*
- * Reset the host adapter.
- */
-int fc_eh_host_reset(struct scsi_cmnd *sc_cmd);
-
-/*
- * Check rport status.
- */
-int fc_slave_alloc(struct scsi_device *sdev);
-
-/*
- * Adjust the queue depth.
- */
-int fc_change_queue_depth(struct scsi_device *sdev, int qdepth);
-
-/*
- * Change the tag type.
- */
-int fc_change_queue_type(struct scsi_device *sdev, int tag_type);
-
-/*
- * Free memory pools used by the FCP layer.
- */
-void fc_fcp_destroy(struct fc_lport *);
-
-
-/**
- * EXCHANGE MANAGER LAYER
- *****************************/
-/*
- * Initializes Exchange Manager related
- * function pointers in struct libfc_function_template.
- */
-int fc_exch_init(struct fc_lport *lp);
-
-/*
- * Allocates an Exchange Manager (EM).
- *
- * The EM manages exchanges for their allocation and
- * free, also allows exchange lookup for received
- * frame.
- *
- * The class is used for initializing FC class of
- * allocated exchange from EM.
- *
- * The min_xid and max_xid will limit new
- * exchange ID (XID) within this range for
- * a new exchange.
- * The LLD may choose to have multiple EMs,
- * e.g. one EM instance per CPU receive thread in LLD.
- * The LLD can use exch_get() of struct libfc_function_template
- * to specify XID for a new exchange within
- * a specified EM instance.
- *
- * The em_idx to uniquely identify an EM instance.
- */
-struct fc_exch_mgr *fc_exch_mgr_alloc(struct fc_lport *lp,
-				      enum fc_class class,
-				      u16 min_xid,
-				      u16 max_xid);
-
-/*
- * Free an exchange manager.
- */
-void fc_exch_mgr_free(struct fc_exch_mgr *mp);
-
-/*
- * Receive a frame on specified local port and exchange manager.
- */
-void fc_exch_recv(struct fc_lport *lp, struct fc_exch_mgr *mp,
-		  struct fc_frame *fp);
-
-/*
- * This function is for exch_seq_send function pointer in
- * struct libfc_function_template, see comment block on
- * exch_seq_send for description of this function.
- */
-struct fc_seq *fc_exch_seq_send(struct fc_lport *lp,
-				struct fc_frame *fp,
-				void (*resp)(struct fc_seq *sp,
-					     struct fc_frame *fp,
-					     void *arg),
-				void (*destructor)(struct fc_seq *sp,
-						   void *arg),
-				void *arg, u32 timer_msec,
-				u32 sid, u32 did, u32 f_ctl);
-
-/*
- * send a frame using existing sequence and exchange.
- */
-int fc_seq_send(struct fc_lport *lp, struct fc_seq *sp,
-		struct fc_frame *fp, u32 f_ctl);
-
-/*
- * Send ELS response using mainly infomation
- * in exchange and sequence in EM layer.
- */
-void fc_seq_els_rsp_send(struct fc_seq *sp, enum fc_els_cmd els_cmd,
-			 struct fc_seq_els_data *els_data);
-
-/*
- * This function is for seq_exch_abort function pointer in
- * struct libfc_function_template, see comment block on
- * seq_exch_abort for description of this function.
- */
-int fc_seq_exch_abort(const struct fc_seq *req_sp, unsigned int timer_msec);
-
-/*
- * Indicate that an exchange/sequence tuple is complete and the memory
- * allocated for the related objects may be freed.
- */
-void fc_exch_done(struct fc_seq *sp);
-
-/*
- * Assigns a EM and XID for a frame and then allocates
- * a new exchange and sequence pair.
- * The fp can be used to determine free XID.
- */
-struct fc_exch *fc_exch_get(struct fc_lport *lp, struct fc_frame *fp);
-
-/*
- * Allocate a new exchange and sequence pair.
- * if ex_id is zero then next free exchange id
- * from specified exchange manger mp will be assigned.
- */
-struct fc_exch *fc_exch_alloc(struct fc_exch_mgr *mp, u16 ex_id);
-
-/*
- * Start a new sequence on the same exchange as the supplied sequence.
- */
-struct fc_seq *fc_seq_start_next(struct fc_seq *sp);
-
-/*
- * Reset an exchange manager, completing all sequences and exchanges.
- * If s_id is non-zero, reset only exchanges originating from that FID.
- * If d_id is non-zero, reset only exchanges sending to that FID.
- */
-void fc_exch_mgr_reset(struct fc_exch_mgr *, u32 s_id, u32 d_id);
-
-/*
- * Get exchange Ids of a sequence
- */
-void fc_seq_get_xids(struct fc_seq *sp, u16 *oxid, u16 *rxid);
-
-/*
- * Set REC data to a sequence
- */
-void fc_seq_set_rec_data(struct fc_seq *sp, u32 rec_data);
-
-/*
- * Functions for fc_functions_template
- */
-void fc_get_host_speed(struct Scsi_Host *shost);
-void fc_get_host_port_type(struct Scsi_Host *shost);
-void fc_get_host_port_state(struct Scsi_Host *shost);
-void fc_set_rport_loss_tmo(struct fc_rport *rport, u32 timeout);
-struct fc_host_statistics *fc_get_host_stats(struct Scsi_Host *);
-
-/*
- * module setup functions.
- */
-int fc_setup_exch_mgr(void);
-void fc_destroy_exch_mgr(void);
-int fc_setup_rport(void);
-void fc_destroy_rport(void);
-
-#endif /* _LIBFC_H_ */
diff --git a/include/scsi/libfcoe.h b/include/scsi/libfcoe.h
new file mode 100644
index 0000000..4ca5902
--- /dev/null
+++ b/include/scsi/libfcoe.h
@@ -0,0 +1,177 @@
+/*
+ * Copyright(c) 2007 - 2008 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,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Maintained at www.Open-FCoE.org
+ */
+
+#ifndef _LIBFCOE_H
+#define _LIBFCOE_H
+
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <scsi/fc/fc_fcoe.h>
+#include <scsi/libfc.h>
+
+/*
+ * this percpu struct for fcoe
+ */
+struct fcoe_percpu_s {
+	int		cpu;
+	struct task_struct *thread;
+	struct sk_buff_head fcoe_rx_list;
+	struct page *crc_eof_page;
+	int crc_eof_offset;
+};
+
+/*
+ * the fcoe sw transport private data
+ */
+struct fcoe_softc {
+	struct list_head list;
+	struct fc_lport *lp;
+	struct net_device *real_dev;
+	struct net_device *phys_dev;		/* device with ethtool_ops */
+	struct packet_type  fcoe_packet_type;
+	struct sk_buff_head fcoe_pending_queue;
+	u16 user_mfs;			/* configured max frame size */
+
+	u8 dest_addr[ETH_ALEN];
+	u8 ctl_src_addr[ETH_ALEN];
+	u8 data_src_addr[ETH_ALEN];
+	/*
+	 * fcoe protocol address learning related stuff
+	 */
+	u16 flogi_oxid;
+	u8 flogi_progress;
+	u8 address_mode;
+};
+
+static inline struct fcoe_softc *fcoe_softc(
+	const struct fc_lport *lp)
+{
+	return (struct fcoe_softc *)lport_priv(lp);
+}
+
+static inline struct net_device *fcoe_netdev(
+	const struct fc_lport *lp)
+{
+	return fcoe_softc(lp)->real_dev;
+}
+
+static inline struct fcoe_hdr *skb_fcoe_header(const struct sk_buff *skb)
+{
+	return (struct fcoe_hdr *)skb_network_header(skb);
+}
+
+static inline int skb_fcoe_offset(const struct sk_buff *skb)
+{
+	return skb_network_offset(skb);
+}
+
+static inline struct fc_frame_header *skb_fc_header(const struct sk_buff *skb)
+{
+	return (struct fc_frame_header *)skb_transport_header(skb);
+}
+
+static inline int skb_fc_offset(const struct sk_buff *skb)
+{
+	return skb_transport_offset(skb);
+}
+
+static inline void skb_reset_fc_header(struct sk_buff *skb)
+{
+	skb_reset_network_header(skb);
+	skb_set_transport_header(skb, skb_network_offset(skb) +
+				 sizeof(struct fcoe_hdr));
+}
+
+static inline bool skb_fc_is_data(const struct sk_buff *skb)
+{
+	return skb_fc_header(skb)->fh_r_ctl == FC_RCTL_DD_SOL_DATA;
+}
+
+static inline bool skb_fc_is_cmd(const struct sk_buff *skb)
+{
+	return skb_fc_header(skb)->fh_r_ctl == FC_RCTL_DD_UNSOL_CMD;
+}
+
+static inline bool skb_fc_has_exthdr(const struct sk_buff *skb)
+{
+	return (skb_fc_header(skb)->fh_r_ctl == FC_RCTL_VFTH) ||
+	    (skb_fc_header(skb)->fh_r_ctl == FC_RCTL_IFRH) ||
+	    (skb_fc_header(skb)->fh_r_ctl == FC_RCTL_ENCH);
+}
+
+static inline bool skb_fc_is_roff(const struct sk_buff *skb)
+{
+	return skb_fc_header(skb)->fh_f_ctl[2] & FC_FC_REL_OFF;
+}
+
+static inline u16 skb_fc_oxid(const struct sk_buff *skb)
+{
+	return be16_to_cpu(skb_fc_header(skb)->fh_ox_id);
+}
+
+static inline u16 skb_fc_rxid(const struct sk_buff *skb)
+{
+	return be16_to_cpu(skb_fc_header(skb)->fh_rx_id);
+}
+
+/* FIXME - DMA_BIDIRECTIONAL ? */
+#define skb_cb(skb)	((struct fcoe_rcv_info *)&((skb)->cb[0]))
+#define skb_cmd(skb)	(skb_cb(skb)->fr_cmd)
+#define skb_dir(skb)	(skb_cmd(skb)->sc_data_direction)
+static inline bool skb_fc_is_read(const struct sk_buff *skb)
+{
+	if (skb_fc_is_cmd(skb) && skb_cmd(skb))
+		return skb_dir(skb) == DMA_FROM_DEVICE;
+	return false;
+}
+
+static inline bool skb_fc_is_write(const struct sk_buff *skb)
+{
+	if (skb_fc_is_cmd(skb) && skb_cmd(skb))
+		return skb_dir(skb) == DMA_TO_DEVICE;
+	return false;
+}
+
+/* libfcoe funcs */
+int fcoe_reset(struct Scsi_Host *shost);
+u64 fcoe_wwn_from_mac(unsigned char mac[MAX_ADDR_LEN],
+		      unsigned int scheme, unsigned int port);
+
+u32 fcoe_fc_crc(struct fc_frame *fp);
+int fcoe_xmit(struct fc_lport *, struct fc_frame *);
+int fcoe_rcv(struct sk_buff *, struct net_device *,
+	     struct packet_type *, struct net_device *);
+
+int fcoe_percpu_receive_thread(void *arg);
+void fcoe_clean_pending_queue(struct fc_lport *lp);
+void fcoe_percpu_clean(struct fc_lport *lp);
+void fcoe_watchdog(ulong vp);
+int fcoe_link_ok(struct fc_lport *lp);
+
+struct fc_lport *fcoe_hostlist_lookup(const struct net_device *);
+int fcoe_hostlist_add(const struct fc_lport *);
+int fcoe_hostlist_remove(const struct fc_lport *);
+
+struct Scsi_Host *fcoe_host_alloc(struct scsi_host_template *, int);
+int fcoe_libfc_config(struct fc_lport *, struct libfc_function_template *);
+
+/* fcoe sw hba */
+int __init fcoe_sw_init(void);
+int __exit fcoe_sw_exit(void);
+#endif /* _LIBFCOE_H */
diff --git a/include/scsi/scsi_transport_fc.h b/include/scsi/scsi_transport_fc.h
index dc33796..d2c1515 100644
--- a/include/scsi/scsi_transport_fc.h
+++ b/include/scsi/scsi_transport_fc.h
@@ -218,7 +218,7 @@ struct fc_rport {	/* aka fc_starget_attrs */
 /* bit field values for struct fc_rport "flags" field: */
 #define FC_RPORT_DEVLOSS_PENDING	0x01
 #define FC_RPORT_SCAN_PENDING		0x02
-#define FC_RPORT_FAST_FAIL_TIMEDOUT	0x03
+#define FC_RPORT_FAST_FAIL_TIMEDOUT	0x04
 
 #define	dev_to_rport(d)				\
 	container_of(d, struct fc_rport, dev)