Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > fc11cd6e1c513a17304da94a5390f3cd > files > 3667

kernel-2.6.18-194.11.1.el5.src.rpm

From: mchristi@redhat.com <mchristi@redhat.com>
Date: Wed, 22 Apr 2009 02:10:20 -0500
Subject: [scsi] update fnic fcoe driver for RHEL-5.4
Message-id: 1240384221-15379-2-git-send-email-mchristi@redhat.com
O-Subject: [PATCH 2/3] RHEL 5.4: update fnic fcoe driver
Bugzilla: 484438
RH-Acked-by: David Miller <davem@redhat.com>

From: Mike Christie <mchristi@redhat.com>

This is also for BZ 484438. fnic is the primary user of
libfc.

This syncs the driver with what the scsi maintainer is going
to take for upstream (James Bottomley said he was ok with
it but has not got a chance to merge it yet).

I have only compile tested this. Cisco is doing all the
testing.

diff --git a/drivers/scsi/fnic/fnic.h b/drivers/scsi/fnic/fnic.h
index b70ad06..d55e5e3 100644
--- a/drivers/scsi/fnic/fnic.h
+++ b/drivers/scsi/fnic/fnic.h
@@ -18,11 +18,9 @@
 #ifndef _FNIC_H_
 #define _FNIC_H_
 
-#include <linux/mempool.h>
 #include <linux/interrupt.h>
-#include <linux/netdevice.h>
-#include <scsi/scsi_device.h>
-#include <scsi/scsi_transport_fc.h>
+#include <linux/workqueue.h>
+#include <linux/mempool.h>
 #include <scsi/libfc.h>
 #include "fnic_io.h"
 #include "fnic_res.h"
@@ -37,27 +35,15 @@
 
 #define DRV_NAME		"fnic"
 #define DRV_DESCRIPTION		"Cisco FCoE HBA Driver"
-#define DRV_VERSION		"1.0.0"
+#define DRV_VERSION		"1.0.0.1121"
 #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
-#define FNIC_MAX_IO_REQ		2048		/* scsi_cmnd tag map entries */
-#define	FNIC_IO_LOCKS		32		/* I/O locks: power of 2 */
+#define FNIC_MAX_IO_REQ		2048 /* scsi_cmnd tag map entries */
+#define	FNIC_IO_LOCKS		64 /* IO locks: power of 2 */
 #define FNIC_DFLT_QUEUE_DEPTH	32
-#define	FNIC_STATS_RATE_LIMIT	4		/* max stats requests per sec */
+#define	FNIC_STATS_RATE_LIMIT	4 /* limit rate at which stats are pulled up */
 
 /*
  * Tag bits used for special requests.
@@ -85,9 +71,39 @@
 #define FNIC_RMDEVICE_TIMEOUT        1000       /* mSec */
 #define FNIC_HOST_RESET_SETTLE_TIME  30         /* Sec */
 
-#define	FNIC_MAX_LUN            1023
 #define FNIC_MAX_FCP_TARGET     256
 
+extern unsigned int fnic_log_level;
+
+#define FNIC_MAIN_LOGGING 0x01
+#define FNIC_FCS_LOGGING 0x02
+#define FNIC_SCSI_LOGGING 0x04
+#define FNIC_ISR_LOGGING 0x08
+
+#define FNIC_CHECK_LOGGING(LEVEL, CMD)				\
+do {								\
+	if (unlikely(fnic_log_level & LEVEL))			\
+		do {						\
+			CMD;					\
+		} while (0);					\
+} while (0)
+
+#define FNIC_MAIN_DBG(kern_level, host, fmt, args...)		\
+	FNIC_CHECK_LOGGING(FNIC_MAIN_LOGGING,			\
+			 shost_printk(kern_level, host, fmt, ##args);)
+
+#define FNIC_FCS_DBG(kern_level, host, fmt, args...)		\
+	FNIC_CHECK_LOGGING(FNIC_FCS_LOGGING,			\
+			 shost_printk(kern_level, host, fmt, ##args);)
+
+#define FNIC_SCSI_DBG(kern_level, host, fmt, args...)		\
+	FNIC_CHECK_LOGGING(FNIC_SCSI_LOGGING,			\
+			 shost_printk(kern_level, host, fmt, ##args);)
+
+#define FNIC_ISR_DBG(kern_level, host, fmt, args...)		\
+	FNIC_CHECK_LOGGING(FNIC_ISR_LOGGING,			\
+			 shost_printk(kern_level, host, fmt, ##args);)
+
 extern const char *fnic_state_str[];
 
 enum fnic_intx_intr_index {
@@ -97,6 +113,8 @@ enum fnic_intx_intr_index {
 	FNIC_INTX_INTR_MAX,
 };
 
+struct pt_regs;
+
 enum fnic_msix_intr_index {
 	FNIC_MSIX_RQ,
 	FNIC_MSIX_WQ,
@@ -105,8 +123,6 @@ enum fnic_msix_intr_index {
 	FNIC_MSIX_INTR_MAX,
 };
 
-struct pt_regs;
-
 struct fnic_msix_entry {
 	int requested;
 	char devname[IFNAMSIZ];
@@ -126,6 +142,8 @@ enum fnic_state {
 #define FNIC_RQ_MAX 1
 #define FNIC_CQ_MAX (FNIC_WQ_COPY_MAX + FNIC_WQ_MAX + FNIC_RQ_MAX)
 
+struct mempool;
+
 /* Per-instance private data structure */
 struct fnic {
 	struct fc_lport *lport;
@@ -138,7 +156,6 @@ struct fnic {
 	unsigned long stats_time;	/* time of stats update */
 	struct vnic_nic_cfg *nic_cfg;
 	char name[IFNAMSIZ];
-	u32 fnic_no;
 	struct timer_list notify_timer; /* used for MSI interrupts */
 
 	unsigned int err_intr_offset;
@@ -150,9 +167,9 @@ struct fnic {
 	u32 fcoui_mode:1;		/* use fcoui address*/
 	u32 vlan_hw_insert:1;	        /* let hw insert the tag */
 	u32 in_remove:1;                /* fnic device in removal */
+	u32 stop_rx_link_events:1;      /* stop proc. rx frames, link events */
 
 	struct completion *remove_wait; /* device remove thread blocks */
-	struct completion *reset_wait;  /* host reset thread blocks */
 
 	struct fc_frame *flogi;
 	struct fc_frame *flogi_resp;
@@ -167,7 +184,10 @@ struct fnic {
 	u8 data_src_addr[ETH_ALEN];
 	u64 fcp_input_bytes;		/* internal statistic */
 	u64 fcp_output_bytes;		/* internal statistic */
+	u32 link_down_cnt;
+	int link_status;
 
+	struct list_head list;
 	struct pci_dev *pdev;
 	struct vnic_fc_config config;
 	struct vnic_dev *vdev;
@@ -184,6 +204,10 @@ struct fnic {
 	mempool_t *io_sgl_pool[FNIC_SGL_NUM_CACHES];
 	spinlock_t io_req_lock[FNIC_IO_LOCKS];	/* locks for scsi cmnds */
 
+	struct work_struct link_work;
+	struct work_struct frame_work;
+	struct sk_buff_head frame_queue;
+
 	/* copy work queue cache line section */
 	____cacheline_aligned struct vnic_wq_copy wq_copy[FNIC_WQ_COPY_MAX];
 	/* completion queue cache line section */
@@ -202,48 +226,7 @@ struct fnic {
 	____cacheline_aligned struct vnic_intr intr[FNIC_MSIX_INTR_MAX];
 };
 
-/* This is used to pass incoming frames, link notifications from ISR
- * to fnic thread
- */
-enum fnic_thread_event_type {
-	EV_TYPE_LINK_DOWN = 0,
-	EV_TYPE_LINK_UP,
-	EV_TYPE_FRAME,
-};
-
-/* ISR allocates and inserts an event into a list.
- * Fnic thread processes the event
- */
-struct fnic_event {
-	/* list head has to the first field*/
-	struct list_head list;
-	struct fc_frame *fp;
-	struct fnic *fnic;
-	enum   fnic_thread_event_type ev_type;
-	u32    is_flogi_resp_frame:1;
-};
-
-static inline void fnic_fc_frame_free_irq(struct fc_frame *fp)
-{
-	dev_kfree_skb_irq(fp_skb(fp));
-}
-
-static inline void fnic_fc_frame_free(struct fc_frame *fp)
-{
-	dev_kfree_skb(fp_skb(fp));
-}
-
-static inline void fnic_fc_frame_free_any(struct fc_frame *fp)
-{
-	dev_kfree_skb_any(fp_skb(fp));
-}
-
-/* Fnic Thread for handling FCS Rx Frames*/
-extern struct task_struct *fnic_thread;
-extern struct list_head   fnic_eventlist;
-extern spinlock_t         fnic_eventlist_lock;
-extern struct kmem_cache   *fnic_ev_cache;
-extern struct kmem_cache   *fnic_fc_frame_cache;
+extern struct workqueue_struct *fnic_event_queue;
 extern struct class_device_attribute *fnic_attrs[];
 
 void fnic_clear_intr_mode(struct fnic *fnic);
@@ -252,6 +235,14 @@ void fnic_free_intr(struct fnic *fnic);
 int fnic_request_intr(struct fnic *fnic);
 
 int fnic_send(struct fc_lport *, struct fc_frame *);
+void fnic_free_wq_buf(struct vnic_wq *wq, struct vnic_wq_buf *buf);
+void fnic_handle_frame(void *work);
+void fnic_handle_link(void *work);
+int fnic_rq_cmpl_handler(struct fnic *fnic, int);
+int fnic_alloc_rq_frame(struct vnic_rq *rq);
+void fnic_free_rq_buf(struct vnic_rq *rq, struct vnic_rq_buf *buf);
+int fnic_send_frame(struct fnic *fnic, struct fc_frame *fp);
+
 int fnic_queuecommand(struct scsi_cmnd *, void (*done)(struct scsi_cmnd *));
 int fnic_abort_cmd(struct scsi_cmnd *);
 int fnic_device_reset(struct scsi_cmnd *);
@@ -259,22 +250,18 @@ int fnic_host_reset(struct scsi_cmnd *);
 int fnic_reset(struct Scsi_Host *);
 void fnic_scsi_cleanup(struct fc_lport *);
 void fnic_scsi_abort_io(struct fc_lport *);
+void fnic_empty_scsi_cleanup(struct fc_lport *);
+void fnic_exch_mgr_reset(struct fc_lport *, u32, u32);
 int fnic_wq_copy_cmpl_handler(struct fnic *fnic, int);
 int fnic_wq_cmpl_handler(struct fnic *fnic, int);
 int fnic_flogi_reg_handler(struct fnic *fnic);
 void fnic_wq_copy_cleanup_handler(struct vnic_wq_copy *wq,
 				  struct fcpio_host_req *desc);
 int fnic_fw_reset_handler(struct fnic *fnic);
-
-void fnic_free_wq_buf(struct vnic_wq *wq, struct vnic_wq_buf *buf);
-
-int fnic_fc_thread(void *arg);
-int fnic_rq_cmpl_handler(struct fnic *fnic, int);
-int fnic_alloc_rq_frame(struct vnic_rq *rq);
-void fnic_free_rq_buf(struct vnic_rq *rq, struct vnic_rq_buf *buf);
+void fnic_terminate_rport_io(struct fc_rport *);
+const char *fnic_state_to_str(unsigned int state);
 
 void fnic_log_q_error(struct fnic *fnic);
-void fnic_notify_check(struct fnic *fnic);
-int fnic_send_frame(struct fnic *fnic, struct fc_frame *fp);
+void fnic_handle_link_event(struct fnic *fnic);
 
 #endif /* _FNIC_H_ */
diff --git a/drivers/scsi/fnic/fnic_attrs.c b/drivers/scsi/fnic/fnic_attrs.c
index f867a06..ce3f364 100644
--- a/drivers/scsi/fnic/fnic_attrs.c
+++ b/drivers/scsi/fnic/fnic_attrs.c
@@ -15,24 +15,9 @@
  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
-#include <linux/module.h>
-#include <linux/kernel.h>
 #include <linux/string.h>
-#include <linux/errno.h>
-#include <linux/types.h>
-#include <linux/init.h>
-#include <linux/pci.h>
-#include <linux/skbuff.h>
-#include <linux/interrupt.h>
-#include <linux/spinlock.h>
-#include <linux/kthread.h>
 #include <linux/device.h>
-#include <scsi/scsi.h>
 #include <scsi/scsi_host.h>
-#include "vnic_dev.h"
-#include "vnic_intr.h"
-#include "vnic_stats.h"
-#include "fnic_io.h"
 #include "fnic.h"
 
 static ssize_t fnic_show_state(struct class_device *cdev, char *buf)
@@ -52,7 +37,7 @@ static ssize_t fnic_show_link_state(struct class_device *cdev, char *buf)
 {
 	struct fc_lport *lp = shost_priv(class_to_shost(cdev));
 
-	return snprintf(buf, PAGE_SIZE, "%s\n", (lp->link_status & FC_LINK_UP)
+	return snprintf(buf, PAGE_SIZE, "%s\n", (lp->link_up)
 			? "Link Up" : "Link Down");
 }
 
diff --git a/drivers/scsi/fnic/fnic_fcs.c b/drivers/scsi/fnic/fnic_fcs.c
index 7212e56..af2f2b0 100644
--- a/drivers/scsi/fnic/fnic_fcs.c
+++ b/drivers/scsi/fnic/fnic_fcs.c
@@ -15,18 +15,14 @@
  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
-#include <linux/kernel.h>
-#include <linux/string.h>
 #include <linux/errno.h>
-#include <linux/types.h>
-#include <linux/init.h>
 #include <linux/pci.h>
 #include <linux/skbuff.h>
 #include <linux/interrupt.h>
 #include <linux/spinlock.h>
-#include <linux/kthread.h>
 #include <linux/if_ether.h>
 #include <linux/if_vlan.h>
+#include <linux/workqueue.h>
 #include <scsi/fc/fc_els.h>
 #include <scsi/fc/fc_fcoe.h>
 #include <scsi/fc_frame.h>
@@ -36,68 +32,93 @@
 #include "cq_enet_desc.h"
 #include "cq_exch_desc.h"
 
-static int fnic_fcs_debug;
+struct workqueue_struct *fnic_event_queue;
 
-#define FNIC_DEBUG_FCS(fmt...)			\
-	do {					\
-		if (fnic_fcs_debug)		\
-			FNIC_DBG(fmt);		\
-	} while (0)
+void fnic_handle_link(void *work)
+{
+	struct fnic *fnic = container_of(work, struct fnic, link_work);
+	unsigned long flags;
+	int old_link_status;
+	u32 old_link_down_cnt;
+
+	spin_lock_irqsave(&fnic->fnic_lock, flags);
 
-struct task_struct *fnic_thread;
-LIST_HEAD(fnic_eventlist);
-spinlock_t	fnic_eventlist_lock;
+	if (fnic->stop_rx_link_events) {
+		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+		return;
+	}
+
+	old_link_down_cnt = fnic->link_down_cnt;
+	old_link_status = fnic->link_status;
+	fnic->link_status = vnic_dev_link_status(fnic->vdev);
+	fnic->link_down_cnt = vnic_dev_link_down_cnt(fnic->vdev);
+
+	if (old_link_status == fnic->link_status) {
+		if (!fnic->link_status)
+			/* DOWN -> DOWN */
+			spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+		else {
+			if (old_link_down_cnt != fnic->link_down_cnt) {
+				/* UP -> DOWN -> UP */
+				fnic->lport->host_stats.link_failure_count++;
+				spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+				FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
+					     "link down\n");
+				fc_linkdown(fnic->lport);
+				FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
+					     "link up\n");
+				fc_linkup(fnic->lport);
+			} else
+				/* UP -> UP */
+				spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+		}
+	} else if (fnic->link_status) {
+		/* DOWN -> UP */
+		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+		FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host, "link up\n");
+		fc_linkup(fnic->lport);
+	} else {
+		/* UP -> DOWN */
+		fnic->lport->host_stats.link_failure_count++;
+		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+		FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host, "link down\n");
+		fc_linkdown(fnic->lport);
+	}
+
+}
 
 /*
- * This thread polls a list for incoming FCS frames and events, and passes
- * them to LibFC for Processing.
+ * This function passes incoming fabric frames to libFC
  */
-int fnic_fc_thread(void *arg)
+void fnic_handle_frame(void *work)
 {
-	struct fc_lport *lp;
-	struct fnic_event *event;
+	struct fnic *fnic = container_of(work, struct fnic, frame_work);
+	struct fc_lport *lp = fnic->lport;
 	unsigned long flags;
+	struct sk_buff *skb;
+	struct fc_frame *fp;
 
-	set_user_nice(current, -20);
-	while (!kthread_should_stop()) {
-		spin_lock_irqsave(&fnic_eventlist_lock, flags);
-
-		if (!list_empty(&fnic_eventlist)) {
-			event = list_first_entry(&fnic_eventlist,
-						 struct fnic_event, list);
-			list_del(&event->list);
-			spin_unlock_irqrestore(&fnic_eventlist_lock, flags);
-
-			lp = event->fnic->lport;
-			if (event->ev_type == EV_TYPE_FRAME) {
-				if (event->is_flogi_resp_frame) {
-					struct fnic *fnic = event->fnic;
-
-					spin_lock_irqsave(&fnic->fnic_lock,
-							  flags);
-					vnic_dev_add_addr(fnic->vdev,
-							  fnic->data_src_addr);
-					spin_unlock_irqrestore
-					  (&fnic->fnic_lock, flags);
-				}
-				fc_exch_recv(lp, lp->emp, event->fp);
-			} else if (event->ev_type == EV_TYPE_LINK_UP)
-				fc_linkup(lp);
-			else if (event->ev_type == EV_TYPE_LINK_DOWN)
-				fc_linkdown(lp);
-
-			kmem_cache_free(fnic_ev_cache, event);
-
-		} else { /* no frame enqueued*/
-			set_current_state(TASK_INTERRUPTIBLE);
-			spin_unlock_irqrestore(&fnic_eventlist_lock, flags);
-			schedule();
-			set_current_state(TASK_RUNNING);
+	while ((skb = skb_dequeue(&fnic->frame_queue))) {
+
+		spin_lock_irqsave(&fnic->fnic_lock, flags);
+		if (fnic->stop_rx_link_events) {
+			spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+			dev_kfree_skb(skb);
+			return;
+		}
+		fp = (struct fc_frame *)skb;
+		/* if Flogi resp frame, register the address */
+		if (fr_flags(fp)) {
+			vnic_dev_add_addr(fnic->vdev,
+					  fnic->data_src_addr);
+			fr_flags(fp) = 0;
 		}
+		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+
+		fc_exch_recv(lp, lp->emp, fp);
 	}
-	return 0;
-}
 
+}
 
 static inline void fnic_import_rq_fc_frame(struct sk_buff *skb,
 					   u32 len, u8 sof, u8 eof)
@@ -164,16 +185,18 @@ static inline int fnic_handle_flogi_resp(struct fnic *fnic,
 
 	if (fnic->state == FNIC_IN_ETH_MODE) {
 
-		/* Check if oxid matches on taking the lock. A new Flogi
+		/*
+		 * Check if oxid matches on taking the lock. A new Flogi
 		 * issued by libFC might have changed the fnic cached oxid
 		 */
 		if (fnic->flogi_oxid != ntohs(fh->fh_ox_id)) {
-			FNIC_DEBUG_FCS(DFX "Flogi response oxid not"
-				       " matching cached oxid, dropping frame"
-				       "\n", fnic->fnic_no);
+			FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
+				     "Flogi response oxid not"
+				     " matching cached oxid, dropping frame"
+				     "\n");
 			ret = -1;
 			spin_unlock_irqrestore(&fnic->fnic_lock, flags);
-			fnic_fc_frame_free_irq(fp);
+			dev_kfree_skb_irq(fp_skb(fp));
 			goto handle_flogi_resp_end;
 		}
 
@@ -198,7 +221,8 @@ static inline int fnic_handle_flogi_resp(struct fnic *fnic,
 			memcpy(fnic->dest_addr, mac, ETH_ALEN);
 		}
 
-		/* Except for Flogi frame, all outbound frames from us have the
+		/*
+		 * Except for Flogi frame, all outbound frames from us have the
 		 * Eth Src address as FC_FCOE_OUI"our_sid". Flogi frame uses
 		 * the vnic MAC address as the Eth Src address
 		 */
@@ -211,12 +235,13 @@ static inline int fnic_handle_flogi_resp(struct fnic *fnic,
 		fnic->state = FNIC_IN_ETH_TRANS_FC_MODE;
 
 	} else {
-		FNIC_DEBUG_FCS(PFX "Unexpected fnic state %s while"
-			       " processing flogi resp\n",
-			       fnic_state_str[fnic->state]);
+		FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
+			     "Unexpected fnic state %s while"
+			     " processing flogi resp\n",
+			     fnic_state_to_str(fnic->state));
 		ret = -1;
 		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
-		fnic_fc_frame_free_irq(fp);
+		dev_kfree_skb_irq(fp_skb(fp));
 		goto handle_flogi_resp_end;
 	}
 
@@ -224,9 +249,10 @@ static inline int fnic_handle_flogi_resp(struct fnic *fnic,
 
 	/* Drop older cached frame */
 	if (old_flogi_resp)
-		fnic_fc_frame_free_irq(old_flogi_resp);
+		dev_kfree_skb_irq(fp_skb(old_flogi_resp));
 
-	/* send flogi reg request to firmware, this will put the fnic in
+	/*
+	 * send flogi reg request to firmware, this will put the fnic in
 	 * in FC mode
 	 */
 	ret = fnic_flogi_reg_handler(fnic);
@@ -234,7 +260,8 @@ static inline int fnic_handle_flogi_resp(struct fnic *fnic,
 	if (ret < 0) {
 		int free_fp = 1;
 		spin_lock_irqsave(&fnic->fnic_lock, flags);
-		/* free the frame is some other thread is not
+		/*
+		 * free the frame is some other thread is not
 		 * pointing to it
 		 */
 		if (fnic->flogi_resp != fp)
@@ -246,7 +273,7 @@ static inline int fnic_handle_flogi_resp(struct fnic *fnic,
 			fnic->state = FNIC_IN_ETH_MODE;
 		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
 		if (free_fp)
-			fnic_fc_frame_free_irq(fp);
+			dev_kfree_skb_irq(fp_skb(fp));
 	}
 
  handle_flogi_resp_end:
@@ -267,8 +294,7 @@ static inline int is_matching_flogi_resp_frame(struct fnic *fnic,
 	if (fnic->flogi_oxid == ntohs(fh->fh_ox_id) &&
 	    fh->fh_r_ctl == FC_RCTL_ELS_REP &&
 	    (f_ctl & (FC_FC_EX_CTX | FC_FC_SEQ_CTX)) == FC_FC_EX_CTX &&
-	    fh->fh_type == FC_TYPE_ELS &&
-	    fc_frame_payload_op(fp) == ELS_LS_ACC)
+	    fh->fh_type == FC_TYPE_ELS)
 		ret = 1;
 
 	return ret;
@@ -283,8 +309,6 @@ static void fnic_rq_cmpl_frame_recv(struct vnic_rq *rq, struct cq_desc
 	struct sk_buff *skb;
 	struct fc_frame *fp;
 	unsigned int eth_hdrs_stripped;
-	u8 list_was_empty;
-	struct fnic_event *event = NULL;
 	u8 type, color, eop, sop, ingress_port, vlan_stripped;
 	u8 fcoe = 0, fcoe_sof, fcoe_eof;
 	u8 fcoe_fc_crc_ok = 1, fcoe_enc_error = 0;
@@ -301,10 +325,7 @@ static void fnic_rq_cmpl_frame_recv(struct vnic_rq *rq, struct cq_desc
 
 	pci_unmap_single(fnic->pdev, buf->dma_addr, buf->len,
 			 PCI_DMA_FROMDEVICE);
-	/* get frame pointer*/
 	skb = buf->os_buf;
-
-	/* We will now reuse this rq_buf for another frame*/
 	buf->os_buf = NULL;
 
 	cq_desc_dec(cq_desc, &type, &color, &q_number, &completed_index);
@@ -334,17 +355,18 @@ static void fnic_rq_cmpl_frame_recv(struct vnic_rq *rq, struct cq_desc
 
 	} else {
 		/* wrong CQ type*/
-		printk(KERN_ERR DFX "fnic rq_cmpl wrong cq type x%x\n",
-		       fnic->fnic_no, type);
+		shost_printk(KERN_ERR, fnic->lport->host,
+			     "fnic rq_cmpl wrong cq type x%x\n", type);
 		goto drop;
 	}
 
 	if (!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);
+		FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
+			     "fnic rq_cmpl fcoe x%x fcsok x%x"
+			     " pkterr x%x fcoe_fc_crc_ok x%x, fcoe_enc_err"
+			     " x%x\n",
+			     fcoe, fcs_ok, packet_error,
+			     fcoe_fc_crc_ok, fcoe_enc_error);
 		goto drop;
 	}
 
@@ -355,43 +377,42 @@ static void fnic_rq_cmpl_frame_recv(struct vnic_rq *rq, struct cq_desc
 
 	fp = (struct fc_frame *)skb;
 
-	/* If frame is an ELS response that matches the cached FLOGI OX_ID,
-	 * issue flogi_reg_request copy wq request to firmware
+	/*
+	 * If frame is an ELS response that matches the cached FLOGI OX_ID,
+	 * and is accept, issue flogi_reg_request copy wq request to firmware
 	 * to register the S_ID and determine whether FC_OUI mode or GW mode.
 	 */
 	if (is_matching_flogi_resp_frame(fnic, fp)) {
 		if (!eth_hdrs_stripped) {
-			fnic_handle_flogi_resp(fnic, fp);
-			return;
+			if (fc_frame_payload_op(fp) == ELS_LS_ACC) {
+				fnic_handle_flogi_resp(fnic, fp);
+				return;
+			}
+			/*
+			 * Recd. Flogi reject. No point registering
+			 * with fw, but forward to libFC
+			 */
+			goto forward;
 		}
 		goto drop;
 	}
 	if (!eth_hdrs_stripped)
 		goto drop;
 
-	/* Queue the frame for receive thread */
-	event = kmem_cache_alloc(fnic_ev_cache, GFP_ATOMIC);
-	if (event == NULL) {
-		FNIC_DEBUG_FCS(DFX "Cannot allocate a event, "
-			       "dropping the FCS Rx frame\n", fnic->fnic_no);
+forward:
+	spin_lock_irqsave(&fnic->fnic_lock, flags);
+	if (fnic->stop_rx_link_events) {
+		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
 		goto drop;
 	}
+	/* Use fr_flags to indicate whether succ. flogi resp or not */
+	fr_flags(fp) = 0;
+	fr_dev(fp) = fnic->lport;
+	spin_unlock_irqrestore(&fnic->fnic_lock, flags);
 
-	memset(event, 0, sizeof(struct fnic_event));
+	skb_queue_tail(&fnic->frame_queue, skb);
+	queue_work(fnic_event_queue, &fnic->frame_work);
 
-	/* initialize the event structure*/
-	event->fp = fp;
-	fr_dev(fp) = fnic->lport;
-	event->fnic = fnic;
-	event->ev_type = EV_TYPE_FRAME;
-	event->is_flogi_resp_frame = 0;
-
-	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);
 	return;
 drop:
 	dev_kfree_skb_irq(skb);
@@ -423,8 +444,9 @@ int fnic_rq_cmpl_handler(struct fnic *fnic, int rq_work_to_do)
 		if (cur_work_done) {
 			err = vnic_rq_fill(&fnic->rq[i], fnic_alloc_rq_frame);
 			if (err)
-				printk(KERN_ERR DFX "fnic_alloc_rq_frame"
-				       " cant alloc frame\n", fnic->fnic_no);
+				shost_printk(KERN_ERR, fnic->lport->host,
+					     "fnic_alloc_rq_frame cant alloc"
+					     " frame\n");
 		}
 		tot_rq_work_done += cur_work_done;
 	}
@@ -447,8 +469,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) {
-		FNIC_DEBUG_FCS(DFX "Unable to allocate RQ sk_buff\n",
-			       fnic->fnic_no);
+		FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
+			     "Unable to allocate RQ sk_buff\n");
 		return -ENOMEM;
 	}
 	skb_reset_mac_header(skb);
@@ -468,11 +490,10 @@ void fnic_free_rq_buf(struct vnic_rq *rq, struct vnic_rq_buf *buf)
 	pci_unmap_single(fnic->pdev, buf->dma_addr, buf->len,
 			 PCI_DMA_FROMDEVICE);
 
-	fnic_fc_frame_free(fp);
+	dev_kfree_skb(fp_skb(fp));
 	buf->os_buf = NULL;
 }
 
-/* returns 1 for flogi frame, 0 otherwise */
 static inline int is_flogi_frame(struct fc_frame_header *fh)
 {
 	return fh->fh_r_ctl == FC_RCTL_ELS_REQ && *(u8 *)(fh + 1) == ELS_FLOGI;
@@ -480,7 +501,6 @@ static inline int is_flogi_frame(struct fc_frame_header *fh)
 
 int fnic_send_frame(struct fnic *fnic, struct fc_frame *fp)
 {
-	/* for now just the first WQ for raw send */
 	struct vnic_wq *wq = &fnic->wq[0];
 	struct sk_buff *skb;
 	dma_addr_t pa;
@@ -514,16 +534,13 @@ int fnic_send_frame(struct fnic *fnic, struct fc_frame *fp)
 		fc_fcoe_set_mac(eth_hdr->h_dest, fh->fh_d_id);
 		memcpy(eth_hdr->h_source, fnic->mac_addr, ETH_ALEN);
 	} else {
-		/* insert dst addr */
 		if (fnic->fcoui_mode)
 			fc_fcoe_set_mac(eth_hdr->h_dest, fh->fh_d_id);
-		else /* gw addr */
+		else
 			memcpy(eth_hdr->h_dest, fnic->dest_addr, ETH_ALEN);
-		/* insert src addr */
 		memcpy(eth_hdr->h_source, fnic->data_src_addr, ETH_ALEN);
 	}
 
-	/* find total len to program the desc */
 	tot_len = skb->len;
 	BUG_ON(tot_len % 4);
 
@@ -532,10 +549,8 @@ int fnic_send_frame(struct fnic *fnic, struct fc_frame *fp)
 	if (FC_FCOE_VER)
 		FC_FCOE_ENCAPS_VER(fcoe_hdr, FC_FCOE_VER);
 
-	/* map the frame */
 	pa = pci_map_single(fnic->pdev, eth_hdr, tot_len, PCI_DMA_TODEVICE);
 
-	/* get the wq lock */
 	spin_lock_irqsave(&fnic->wq_lock[0], flags);
 
 	if (!vnic_wq_desc_avail(wq)) {
@@ -548,11 +563,10 @@ int fnic_send_frame(struct fnic *fnic, struct fc_frame *fp)
 	fnic_queue_wq_desc(wq, skb, pa, tot_len, fr_eof(fp),
 			   fnic->vlan_hw_insert, fnic->vlan_id, 1, 1, 1);
 fnic_send_frame_end:
-	/* release the wq lock */
 	spin_unlock_irqrestore(&fnic->wq_lock[0], flags);
 
 	if (ret)
-		fnic_fc_frame_free_any(fp);
+		dev_kfree_skb_any(fp_skb(fp));
 
 	return ret;
 }
@@ -572,7 +586,7 @@ int fnic_send(struct fc_lport *lp, struct fc_frame *fp)
 	struct fc_frame *old_flogi_resp = NULL;
 
 	if (fnic->in_remove) {
-		fnic_fc_frame_free(fp);
+		dev_kfree_skb(fp_skb(fp));
 		ret = -1;
 		goto fnic_send_end;
 	}
@@ -604,11 +618,11 @@ again:
 		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
 
 		if (old_flogi) {
-			fnic_fc_frame_free(old_flogi);
+			dev_kfree_skb(fp_skb(old_flogi));
 			old_flogi = NULL;
 		}
 		if (old_flogi_resp) {
-			fnic_fc_frame_free(old_flogi_resp);
+			dev_kfree_skb(fp_skb(old_flogi_resp));
 			old_flogi_resp = NULL;
 		}
 
@@ -620,7 +634,7 @@ again:
 		if (ret) {
 			fnic->state = old_state;
 			spin_unlock_irqrestore(&fnic->fnic_lock, flags);
-			fnic_fc_frame_free(fp);
+			dev_kfree_skb(fp_skb(fp));
 			goto fnic_send_end;
 		}
 		old_flogi = fnic->flogi;
@@ -632,7 +646,8 @@ again:
 		break;
 
 	case FNIC_IN_FC_TRANS_ETH_MODE:
-		/* A reset is pending with the firmware. Store the flogi
+		/*
+		 * A reset is pending with the firmware. Store the flogi
 		 * and its oxid. The transition out of this state happens
 		 * only when Firmware completes the reset, either with
 		 * success or failed. If success, transition to
@@ -645,7 +660,8 @@ again:
 		break;
 
 	case FNIC_IN_ETH_MODE:
-		/* The fw/hw is already in eth mode. Store the oxid,
+		/*
+		 * The fw/hw is already in eth mode. Store the oxid,
 		 * and send the flogi frame out. The transition out of this
 		 * state happens only we receive flogi response from the
 		 * network, and the oxid matches the cached oxid when the
@@ -661,9 +677,9 @@ again:
 
 fnic_send_end:
 	if (old_flogi)
-		fnic_fc_frame_free(old_flogi);
+		dev_kfree_skb(fp_skb(old_flogi));
 	if (old_flogi_resp)
-		fnic_fc_frame_free(old_flogi_resp);
+		dev_kfree_skb(fp_skb(old_flogi_resp));
 	return ret;
 }
 
@@ -677,7 +693,7 @@ static void fnic_wq_complete_frame_send(struct vnic_wq *wq,
 
 	pci_unmap_single(fnic->pdev, buf->dma_addr,
 			 buf->len, PCI_DMA_TODEVICE);
-	fnic_fc_frame_free_irq(fp);
+	dev_kfree_skb_irq(fp_skb(fp));
 	buf->os_buf = NULL;
 }
 
@@ -721,6 +737,6 @@ void fnic_free_wq_buf(struct vnic_wq *wq, struct vnic_wq_buf *buf)
 	pci_unmap_single(fnic->pdev, buf->dma_addr,
 			 buf->len, PCI_DMA_TODEVICE);
 
-	fnic_fc_frame_free(fp);
+	dev_kfree_skb(fp_skb(fp));
 	buf->os_buf = NULL;
 }
diff --git a/drivers/scsi/fnic/fnic_io.h b/drivers/scsi/fnic/fnic_io.h
index d7d6048..f0b8969 100644
--- a/drivers/scsi/fnic/fnic_io.h
+++ b/drivers/scsi/fnic/fnic_io.h
@@ -52,15 +52,16 @@ enum fnic_ioreq_state {
 };
 
 struct fnic_io_req {
-	struct host_sg_desc *sgl_list;	/* sgl list */
-	void  *sgl_list_alloc;          /* sgl list address used for free */
-	dma_addr_t  sense_buf_pa;	/* dma address for sense buffer*/
-	dma_addr_t  sgl_list_pa;	/* dma address for sgl list */
+	struct host_sg_desc *sgl_list; /* sgl list */
+	void *sgl_list_alloc; /* sgl list address used for free */
+	dma_addr_t sense_buf_pa; /* dma address for sense buffer*/
+	dma_addr_t sgl_list_pa;	/* dma address for sgl list */
 	u16 sgl_cnt;
-	u8 sgl_type;			/* device DMA descriptor list type */
-	u8 io_completed:1;		/* set to 1 when fw completes IO */
-	struct completion *abts_done;   /* completion for abts */
-	struct completion *dr_done;     /* completion for device reset */
+	u8 sgl_type; /* device DMA descriptor list type */
+	u8 io_completed:1; /* set to 1 when fw completes IO */
+	u32 port_id; /* remote port DID */
+	struct completion *abts_done; /* completion for abts */
+	struct completion *dr_done; /* completion for device reset */
 };
 
 #endif /* _FNIC_IO_H_ */
diff --git a/drivers/scsi/fnic/fnic_isr.c b/drivers/scsi/fnic/fnic_isr.c
index c36c162..254e555 100644
--- a/drivers/scsi/fnic/fnic_isr.c
+++ b/drivers/scsi/fnic/fnic_isr.c
@@ -15,14 +15,9 @@
  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
-#include <linux/kernel.h>
 #include <linux/string.h>
 #include <linux/errno.h>
-#include <linux/types.h>
-#include <linux/init.h>
 #include <linux/pci.h>
-#include <linux/skbuff.h>
-#include <linux/mempool.h>
 #include <linux/interrupt.h>
 #include <scsi/libfc.h>
 #include <scsi/fc_frame.h>
@@ -32,71 +27,49 @@
 #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;
+	struct fnic *fnic = data;
 	u32 pba;
-	unsigned long  work_done = 0;
-	int i;
-
-	/* mask all interrupts*/
-	for (i = 0; i < fnic->intr_count; i++)
-		vnic_intr_mask(&fnic->intr[i]);
+	unsigned long work_done = 0;
 
 	pba = vnic_intr_legacy_pba(fnic->legacy_pba);
-	if (!pba) {
-		for (i = 0; i < fnic->intr_count; i++)
-			vnic_intr_unmask(&fnic->intr[i]);
-		return IRQ_NONE;	/* not our interrupt */
-	}
+	if (!pba)
+		return IRQ_NONE;
 
-	/* Check for notify */
-	if (pba & (1 << FNIC_INTX_NOTIFY))
-		fnic_notify_check(fnic);
+	if (pba & (1 << FNIC_INTX_NOTIFY)) {
+		vnic_intr_return_all_credits(&fnic->intr[FNIC_INTX_NOTIFY]);
+		fnic_handle_link_event(fnic);
+	}
 
-	/* Check for errors */
-	if (pba & (1 << FNIC_INTX_ERR))
+	if (pba & (1 << FNIC_INTX_ERR)) {
+		vnic_intr_return_all_credits(&fnic->intr[FNIC_INTX_ERR]);
 		fnic_log_q_error(fnic);
+	}
 
-	/* Check for data */
 	if (pba & (1 << FNIC_INTX_WQ_RQ_COPYWQ)) {
 		work_done += fnic_wq_copy_cmpl_handler(fnic, 8);
 		work_done += fnic_wq_cmpl_handler(fnic, 4);
 		work_done += fnic_rq_cmpl_handler(fnic, 4);
-	}
 
-	/*Now return the credit debt to HW.*/
-	vnic_intr_return_credits(&fnic->intr[FNIC_INTX_WQ_RQ_COPYWQ],
-				 work_done,
-				 1 /* unmask intr */,
-				 1 /* reset intr timer */);
-
-	/* unmask notification and error interrupts */
-	vnic_intr_unmask(&fnic->intr[FNIC_INTX_NOTIFY]);
-	vnic_intr_unmask(&fnic->intr[FNIC_INTX_ERR]);
+		vnic_intr_return_credits(&fnic->intr[FNIC_INTX_WQ_RQ_COPYWQ],
+					 work_done,
+					 1 /* unmask intr */,
+					 1 /* reset intr timer */);
+	}
 
 	return IRQ_HANDLED;
 }
 
 static irqreturn_t fnic_isr_msi(int irq, void *data, struct pt_regs *regs)
 {
-	struct fnic *fnic = (struct fnic *)data;
-	unsigned long  work_done = 0;
+	struct fnic *fnic = data;
+	unsigned long work_done = 0;
 
-	/* handle FCS frames and IOs */
 	work_done += fnic_wq_copy_cmpl_handler(fnic, 8);
 	work_done += fnic_wq_cmpl_handler(fnic, 4);
 	work_done += fnic_rq_cmpl_handler(fnic, 4);
 
-	/*Now return the credit debt to HW.*/
 	vnic_intr_return_credits(&fnic->intr[0],
 				 work_done,
 				 1 /* unmask intr */,
@@ -107,8 +80,8 @@ static irqreturn_t fnic_isr_msi(int irq, void *data, struct pt_regs *regs)
 
 static irqreturn_t fnic_isr_msix_rq(int irq, void *data, struct pt_regs *regs)
 {
-	struct fnic *fnic = (struct fnic *)data;
-	unsigned long  rq_work_done = 0;
+	struct fnic *fnic = data;
+	unsigned long rq_work_done = 0;
 
 	rq_work_done = fnic_rq_cmpl_handler(fnic, 4);
 	vnic_intr_return_credits(&fnic->intr[FNIC_MSIX_RQ],
@@ -121,8 +94,8 @@ static irqreturn_t fnic_isr_msix_rq(int irq, void *data, struct pt_regs *regs)
 
 static irqreturn_t fnic_isr_msix_wq(int irq, void *data, struct pt_regs *regs)
 {
-	struct fnic *fnic = (struct fnic *)data;
-	unsigned long  wq_work_done = 0;
+	struct fnic *fnic = data;
+	unsigned long wq_work_done = 0;
 
 	wq_work_done = fnic_wq_cmpl_handler(fnic, 4);
 	vnic_intr_return_credits(&fnic->intr[FNIC_MSIX_WQ],
@@ -135,8 +108,8 @@ static irqreturn_t fnic_isr_msix_wq(int irq, void *data, struct pt_regs *regs)
 static irqreturn_t fnic_isr_msix_wq_copy(int irq, void *data,
 					 struct pt_regs *regs)
 {
-	struct fnic *fnic = (struct fnic *)data;
-	unsigned long  wq_copy_work_done = 0;
+	struct fnic *fnic = data;
+	unsigned long wq_copy_work_done = 0;
 
 	wq_copy_work_done = fnic_wq_copy_cmpl_handler(fnic, 8);
 	vnic_intr_return_credits(&fnic->intr[FNIC_MSIX_WQ_COPY],
@@ -149,11 +122,11 @@ static irqreturn_t fnic_isr_msix_wq_copy(int irq, void *data,
 static irqreturn_t fnic_isr_msix_err_notify(int irq, void *data,
 					    struct pt_regs *regs)
 {
-	struct fnic *fnic = (struct fnic *)data;
+	struct fnic *fnic = data;
 
+	vnic_intr_return_all_credits(&fnic->intr[FNIC_MSIX_ERR_NOTIFY]);
 	fnic_log_q_error(fnic);
-	fnic_notify_check(fnic);
-	vnic_intr_unmask(&fnic->intr[FNIC_MSIX_ERR_NOTIFY]);
+	fnic_handle_link_event(fnic);
 
 	return IRQ_HANDLED;
 }
@@ -161,6 +134,7 @@ static irqreturn_t fnic_isr_msix_err_notify(int irq, void *data,
 void fnic_free_intr(struct fnic *fnic)
 {
 	int i;
+
 	switch (vnic_dev_get_intr_mode(fnic->vdev)) {
 	case VNIC_DEV_INTR_MODE_INTX:
 	case VNIC_DEV_INTR_MODE_MSI:
@@ -225,8 +199,9 @@ int fnic_request_intr(struct fnic *fnic)
 					  fnic->msix[i].devname,
 					  fnic->msix[i].devid);
 			if (err) {
-				printk(KERN_ERR PFX "MSIX: request_irq"
-				       " failed %d\n", err);
+				shost_printk(KERN_ERR, fnic->lport->host,
+					     "MSIX: request_irq"
+					     " failed %d\n", err);
 				fnic_free_intr(fnic);
 				break;
 			}
@@ -248,7 +223,8 @@ int fnic_set_intr_mode(struct fnic *fnic)
 	unsigned int o = ARRAY_SIZE(fnic->wq_copy);
 	unsigned int i;
 
-	/* Set interrupt mode (INTx, MSI, MSI-X) depending
+	/*
+	 * Set interrupt mode (INTx, MSI, MSI-X) depending
 	 * system capabilities.
 	 *
 	 * Try MSI-X first
@@ -275,15 +251,16 @@ int fnic_set_intr_mode(struct fnic *fnic)
 			fnic->intr_count = n + m + o + 1;
 			fnic->err_intr_offset = FNIC_MSIX_ERR_NOTIFY;
 
-			FNIC_DEBUG_ISR(PFX "Using MSI-X Interrupts\n");
+			FNIC_ISR_DBG(KERN_DEBUG, fnic->lport->host,
+				     "Using MSI-X Interrupts\n");
 			vnic_dev_set_intr_mode(fnic->vdev,
 					       VNIC_DEV_INTR_MODE_MSIX);
 			return 0;
 		}
 	}
 
-	/* Next try MSI
-	 *
+	/*
+	 * Next try MSI
 	 * We need 1 RQ, 1 WQ, 1 WQ_COPY, 3 CQs, and 1 INTR
 	 */
 	if (fnic->rq_count >= 1 &&
@@ -301,14 +278,15 @@ int fnic_set_intr_mode(struct fnic *fnic)
 		fnic->intr_count = 1;
 		fnic->err_intr_offset = 0;
 
-		FNIC_DEBUG_ISR(PFX "Using MSI Interrupts\n");
+		FNIC_ISR_DBG(KERN_DEBUG, fnic->lport->host,
+			     "Using MSI Interrupts\n");
 		vnic_dev_set_intr_mode(fnic->vdev, VNIC_DEV_INTR_MODE_MSI);
 
 		return 0;
 	}
 
-	/* Next try INTx
-	 *
+	/*
+	 * Next try INTx
 	 * We need 1 RQ, 1 WQ, 1 WQ_COPY, 3 CQs, and 3 INTRs
 	 * 1 INTR is used for all 3 queues, 1 INTR for queue errors
 	 * 1 INTR for notification area
@@ -326,7 +304,8 @@ int fnic_set_intr_mode(struct fnic *fnic)
 		fnic->cq_count = 3;
 		fnic->intr_count = 3;
 
-		FNIC_DEBUG_ISR(PFX "Using Legacy Interrupts\n");
+		FNIC_ISR_DBG(KERN_DEBUG, fnic->lport->host,
+			     "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_kcompat.h b/drivers/scsi/fnic/fnic_kcompat.h
new file mode 100644
index 0000000..ee64018
--- /dev/null
+++ b/drivers/scsi/fnic/fnic_kcompat.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2009 Cisco Systems, Inc.  All rights reserved.
+ *
+ * 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
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _FNIC_KCOMPAT_H_
+#define _FNIC_KCOMPAT_H_
+
+#if BITS_PER_LONG == 64
+
+#define div_u64(divisor, dividend) \
+	(divisor) / (dividend); \
+
+#else
+
+/*
+ * This is not a generic macro. It is specific to a dividend of 10^6
+ */
+
+#define div_u64(divisor, dividend) \
+	/* \
+	 * To convert to Megabytes, instead of dividing by 10^6, replace \
+	 * by multiplying by (2 ^32 / 10^6 which is approx. 4295) \
+	 * and then divide by 2^32. \
+	 * \
+	 * This avoids link break for 32 bit systems where the 64 bit \
+	 * divide is not defined and keeps division error low, about 0.00076% \
+	 */ \
+	((divisor) * 4295) >> 32; \
+
+#endif
+
+#endif /* _FNIC_KCOMPAT_H_ */
diff --git a/drivers/scsi/fnic/fnic_main.c b/drivers/scsi/fnic/fnic_main.c
index 0f21883..d25ea0f 100644
--- a/drivers/scsi/fnic/fnic_main.c
+++ b/drivers/scsi/fnic/fnic_main.c
@@ -16,19 +16,15 @@
  * SOFTWARE.
  */
 #include <linux/module.h>
-#include <linux/kernel.h>
+#include <linux/mempool.h>
 #include <linux/string.h>
 #include <linux/errno.h>
-#include <linux/types.h>
 #include <linux/init.h>
 #include <linux/pci.h>
 #include <linux/skbuff.h>
 #include <linux/interrupt.h>
 #include <linux/spinlock.h>
-#include <linux/kthread.h>
-#include <asm/atomic.h>
-#include <scsi/scsi.h>
-#include <scsi/scsi_host.h>
+#include <linux/workqueue.h>
 #include <scsi/scsi.h>
 #include <scsi/scsi_host.h>
 #include <scsi/scsi_device.h>
@@ -45,33 +41,22 @@
 #include "fnic_io.h"
 #include "fnic.h"
 #include "fnic_tag_map.h"
+#include "fnic_kcompat.h"
 
 #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
- */
+/* Timer to poll notification area for events. Used for MSI interrupts */
 #define FNIC_NOTIFY_TIMER_PERIOD	(2 * HZ)
 
-/* Cache to pass events from ISR to fnic Thread */
-struct kmem_cache         *fnic_ev_cache;
-
 static struct kmem_cache *fnic_sgl_cache[FNIC_SGL_NUM_CACHES];
 static struct kmem_cache *fnic_io_req_cache;
-static atomic_t fnic_no;
+LIST_HEAD(fnic_list);
+DEFINE_SPINLOCK(fnic_list_lock);
 
 /* Supported devices by fnic module */
 static struct pci_device_id fnic_id_table[] = {
 	{ PCI_DEVICE(PCI_VENDOR_ID_CISCO, PCI_DEVICE_ID_CISCO_FNIC) },
-	{ 0, }	/* end of table */
+	{ 0, }
 };
 
 MODULE_DESCRIPTION(DRV_DESCRIPTION);
@@ -81,11 +66,16 @@ MODULE_LICENSE("GPL v2");
 MODULE_VERSION(DRV_VERSION);
 MODULE_DEVICE_TABLE(pci, fnic_id_table);
 
-/* Exported to LibFC layer */
+unsigned int fnic_log_level;
+module_param(fnic_log_level, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(fnic_log_level, "bit mask of fnic logging levels");
+
+
 static struct libfc_function_template fnic_transport_template = {
 	.frame_send = fnic_send,
-	.fcp_abort_io = fnic_scsi_abort_io,
-	.fcp_cleanup = fnic_scsi_cleanup
+	.fcp_abort_io = fnic_empty_scsi_cleanup,
+	.fcp_cleanup = fnic_empty_scsi_cleanup,
+	.exch_mgr_reset = fnic_exch_mgr_reset
 };
 
 static int fnic_slave_alloc(struct scsi_device *sdev)
@@ -157,13 +147,7 @@ static struct fc_function_template fnic_fc_functions = {
 	.issue_fc_host_lip = fnic_reset,
 	.get_fc_host_stats = fnic_get_stats,
 	.dd_fcrport_size = sizeof(struct fc_rport_libfc_priv),
-};
-
-const char *fnic_state_str[] = {
-	[FNIC_IN_FC_MODE] =           "FNIC_IN_FC_MODE",
-	[FNIC_IN_FC_TRANS_ETH_MODE] = "FNIC_IN_FC_TRANS_ETH_MODE",
-	[FNIC_IN_ETH_MODE] =          "FNIC_IN_ETH_MODE",
-	[FNIC_IN_ETH_TRANS_FC_MODE] = "FNIC_IN_ETH_TRANS_FC_MODE",
+	.terminate_rport_io = fnic_terminate_rport_io,
 };
 
 static void fnic_get_host_speed(struct Scsi_Host *shost)
@@ -201,8 +185,9 @@ static struct fc_host_statistics *fnic_get_stats(struct Scsi_Host *host)
 	spin_unlock_irqrestore(&fnic->fnic_lock, flags);
 
 	if (ret) {
-		FNIC_DEBUG_MAIN(DFX "fnic: Get vnic stats failed"
-				" 0x%x", fnic->fnic_no, ret);
+		FNIC_MAIN_DBG(KERN_DEBUG, fnic->lport->host,
+			      "fnic: Get vnic stats failed"
+			      " 0x%x", ret);
 		return stats;
 	}
 	vs = fnic->stats;
@@ -214,8 +199,8 @@ static struct fc_host_statistics *fnic_get_stats(struct Scsi_Host *host)
 	stats->dumped_frames = vs->tx.tx_drops + vs->rx.rx_drop;
 	stats->invalid_crc_count = vs->rx.rx_crc_errors;
 	stats->seconds_since_last_reset = (jiffies - lp->boot_time) / HZ;
-	stats->fcp_input_megabytes = fnic->fcp_input_bytes / 1000000;
-	stats->fcp_output_megabytes = fnic->fcp_output_bytes / 1000000;
+	stats->fcp_input_megabytes = div_u64(fnic->fcp_input_bytes, 1000000);
+	stats->fcp_output_megabytes = div_u64(fnic->fcp_output_bytes, 1000000);
 
 	return stats;
 }
@@ -228,69 +213,41 @@ void fnic_log_q_error(struct fnic *fnic)
 	for (i = 0; i < fnic->raw_wq_count; i++) {
 		error_status = ioread32(&fnic->wq[i].ctrl->error_status);
 		if (error_status)
-			printk(KERN_ERR DFX "WQ[%d] error_status"
-			       " %d\n", fnic->fnic_no, i, error_status);
+			shost_printk(KERN_ERR, fnic->lport->host,
+				     "WQ[%d] error_status"
+				     " %d\n", i, error_status);
 	}
 
 	for (i = 0; i < fnic->rq_count; i++) {
 		error_status = ioread32(&fnic->rq[i].ctrl->error_status);
 		if (error_status)
-			printk(KERN_ERR DFX "RQ[%d] error_status"
-			       " %d\n", fnic->fnic_no, i, error_status);
+			shost_printk(KERN_ERR, fnic->lport->host,
+				     "RQ[%d] error_status"
+				     " %d\n", i, error_status);
 	}
 
 	for (i = 0; i < fnic->wq_copy_count; i++) {
 		error_status = ioread32(&fnic->wq_copy[i].ctrl->error_status);
 		if (error_status)
-			printk(KERN_ERR DFX "CWQ[%d] error_status"
-			       " %d\n", fnic->fnic_no, i, error_status);
+			shost_printk(KERN_ERR, fnic->lport->host,
+				     "CWQ[%d] error_status"
+				     " %d\n", i, error_status);
 	}
 }
 
-static void fnic_handle_link_event(struct fnic *fnic)
+void fnic_handle_link_event(struct fnic *fnic)
 {
-	int link_status = vnic_dev_link_status(fnic->vdev);
-	struct fnic_event *event;
-	u8 list_was_empty;
 	unsigned long flags;
 
-	FNIC_DEBUG_MAIN(DFX "link %s\n", fnic->fnic_no,
-			(link_status ? "up" : "down"));
-
-	if (fnic->in_remove)
-		return;
-
-	event = kmem_cache_alloc(fnic_ev_cache, GFP_ATOMIC);
-	if (!event) {
-		FNIC_DEBUG_MAIN(DFX "Cannot allocate a event, "
-				"cannot indicate link event to FCS\n",
-				fnic->fnic_no);
+	spin_lock_irqsave(&fnic->fnic_lock, flags);
+	if (fnic->stop_rx_link_events) {
+		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
 		return;
 	}
+	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) {
-		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 (list_was_empty)
-		wake_up_process(fnic_thread);
-
-	return;
-}
+	queue_work(fnic_event_queue, &fnic->link_work);
 
-void fnic_notify_check(struct fnic *fnic)
-{
-	fnic_handle_link_event(fnic);
 }
 
 static int fnic_notify_set(struct fnic *fnic)
@@ -308,9 +265,10 @@ static int fnic_notify_set(struct fnic *fnic)
 		err = vnic_dev_notify_set(fnic->vdev, FNIC_MSIX_ERR_NOTIFY);
 		break;
 	default:
-		printk(KERN_ERR DFX "Interrupt mode should be set up"
-		       " before devcmd notify set %d\n", fnic->fnic_no,
-		       vnic_dev_get_intr_mode(fnic->vdev));
+		shost_printk(KERN_ERR, fnic->lport->host,
+			     "Interrupt mode should be set up"
+			     " before devcmd notify set %d\n",
+			     vnic_dev_get_intr_mode(fnic->vdev));
 		err = -1;
 		break;
 	}
@@ -322,14 +280,19 @@ static void fnic_notify_timer(unsigned long data)
 {
 	struct fnic *fnic = (struct fnic *)data;
 
-	fnic_notify_check(fnic);
-	mod_timer(&fnic->notify_timer, jiffies + FNIC_NOTIFY_TIMER_PERIOD);
+	fnic_handle_link_event(fnic);
+	mod_timer(&fnic->notify_timer,
+		  round_jiffies(jiffies + FNIC_NOTIFY_TIMER_PERIOD));
 }
 
 static void fnic_notify_timer_start(struct fnic *fnic)
 {
 	switch (vnic_dev_get_intr_mode(fnic->vdev)) {
 	case VNIC_DEV_INTR_MODE_MSI:
+		/*
+		 * Schedule first timeout immediately. The driver is
+		 * initiatialized and ready to look for link up notification
+		 */
 		mod_timer(&fnic->notify_timer, jiffies);
 		break;
 	default:
@@ -365,20 +328,6 @@ static int fnic_dev_wait(struct vnic_dev *vdev,
 	return -ETIMEDOUT;
 }
 
-static int fnic_dev_open(struct fnic *fnic)
-{
-	int err;
-
-	err = fnic_dev_wait(fnic->vdev, vnic_dev_open,
-			    vnic_dev_open_done, 0);
-	if (err)
-		printk(KERN_ERR DFX
-		       "vNIC device open failed, err %d.\n",
-		       fnic->fnic_no, err);
-
-	return err;
-}
-
 static int fnic_cleanup(struct fnic *fnic)
 {
 	unsigned int i;
@@ -387,8 +336,6 @@ static int fnic_cleanup(struct fnic *fnic)
 	struct fc_frame *flogi = NULL;
 	struct fc_frame *flogi_resp = NULL;
 
-	del_timer_sync(&fnic->notify_timer);
-
 	vnic_dev_disable(fnic->vdev);
 	for (i = 0; i < fnic->intr_count; i++)
 		vnic_intr_mask(&fnic->intr[i]);
@@ -428,7 +375,8 @@ static int fnic_cleanup(struct fnic *fnic)
 	for (i = 0; i < fnic->intr_count; i++)
 		vnic_intr_clean(&fnic->intr[i]);
 
-	/* Remove cached flogi and flogi resp frames if any
+	/*
+	 * Remove cached flogi and flogi resp frames if any
 	 * These frames are not in any queue, and therefore queue
 	 * cleanup does not clean them. So clean them explicitly
 	 */
@@ -440,10 +388,10 @@ static int fnic_cleanup(struct fnic *fnic)
 	spin_unlock_irqrestore(&fnic->fnic_lock, flags);
 
 	if (flogi)
-		fnic_fc_frame_free(flogi);
+		dev_kfree_skb(fp_skb(flogi));
 
 	if (flogi_resp)
-		fnic_fc_frame_free(flogi_resp);
+		dev_kfree_skb(fp_skb(flogi_resp));
 
 	scsi_free_shared_tag_map(fnic->lport->host);
 
@@ -480,8 +428,10 @@ static int __devinit fnic_probe(struct pci_dev *pdev,
 	mempool_t *pool;
 	int err;
 	int i;
+	unsigned long flags;
 
-	/* Allocate SCSI Host and set up association between host,
+	/*
+	 * Allocate SCSI Host and set up association between host,
 	 * local port, and fnic
 	 */
 	host = scsi_host_alloc(&fnic_host_template,
@@ -496,16 +446,15 @@ static int __devinit fnic_probe(struct pci_dev *pdev,
 	fnic = lport_priv(lp);
 	fnic->lport = lp;
 
-	/* fnic number starts from 0 onwards */
-	fnic->fnic_no = atomic_add_return(1, &fnic_no);
 	snprintf(fnic->name, sizeof(fnic->name) - 1, "%s%d", DRV_NAME,
-		 fnic->fnic_no);
+		 host->host_no);
 
 	host->transportt = fnic_fc_transport;
 
 	err = scsi_init_shared_tag_map(host, FNIC_MAX_IO_REQ);
 	if (err) {
-		printk(KERN_ERR PFX "Unable to alloc shared tag map\n");
+		shost_printk(KERN_ERR, fnic->lport->host,
+			     "Unable to alloc shared tag map\n");
 		goto err_out_free_hba;
 	}
 
@@ -516,15 +465,15 @@ static int __devinit fnic_probe(struct pci_dev *pdev,
 
 	err = pci_enable_device(pdev);
 	if (err) {
-		printk(KERN_ERR DFX "Cannot enable PCI device, aborting.\n",
-		       fnic->fnic_no);
+		shost_printk(KERN_ERR, fnic->lport->host,
+			     "Cannot enable PCI device, aborting.\n");
 		goto err_out_free_tag_map;
 	}
 
 	err = pci_request_regions(pdev, DRV_NAME);
 	if (err) {
-		printk(KERN_ERR DFX "Cannot enable PCI resources, aborting\n",
-		       fnic->fnic_no);
+		shost_printk(KERN_ERR, fnic->lport->host,
+			     "Cannot enable PCI resources, aborting\n");
 		goto err_out_disable_device;
 	}
 
@@ -538,31 +487,32 @@ static int __devinit fnic_probe(struct pci_dev *pdev,
 	if (err) {
 		err = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
 		if (err) {
-			printk(KERN_ERR DFX "No usable DMA configuration "
-			       "aborting\n", fnic->fnic_no);
+			shost_printk(KERN_ERR, fnic->lport->host,
+				     "No usable DMA configuration "
+				     "aborting\n");
 			goto err_out_release_regions;
 		}
 		err = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK);
 		if (err) {
-			printk(KERN_ERR DFX "Unable to obtain 32-bit DMA "
-			       "for consistent allocations, aborting.\n",
-			       fnic->fnic_no);
+			shost_printk(KERN_ERR, fnic->lport->host,
+				     "Unable to obtain 32-bit DMA "
+				     "for consistent allocations, aborting.\n");
 			goto err_out_release_regions;
 		}
 	} else {
 		err = pci_set_consistent_dma_mask(pdev, DMA_40BIT_MASK);
 		if (err) {
-			printk(KERN_ERR DFX "Unable to obtain 40-bit DMA "
-			       "for consistent allocations, aborting.\n",
-			       fnic->fnic_no);
+			shost_printk(KERN_ERR, fnic->lport->host,
+				     "Unable to obtain 40-bit DMA "
+				     "for consistent allocations, aborting.\n");
 			goto err_out_release_regions;
 		}
 	}
 
 	/* Map vNIC resources from BAR0 */
 	if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) {
-		printk(KERN_ERR DFX "BAR0 not memory-map'able, aborting.\n",
-		       fnic->fnic_no);
+		shost_printk(KERN_ERR, fnic->lport->host,
+			     "BAR0 not memory-map'able, aborting.\n");
 		err = -ENODEV;
 		goto err_out_release_regions;
 	}
@@ -572,46 +522,50 @@ static int __devinit fnic_probe(struct pci_dev *pdev,
 	fnic->bar0.len = pci_resource_len(pdev, 0);
 
 	if (!fnic->bar0.vaddr) {
-		printk(KERN_ERR DFX "Cannot memory-map BAR0 res hdr, "
-		       "aborting.\n", fnic->fnic_no);
+		shost_printk(KERN_ERR, fnic->lport->host,
+			     "Cannot memory-map BAR0 res hdr, "
+			     "aborting.\n");
 		err = -ENODEV;
 		goto err_out_release_regions;
 	}
 
 	fnic->vdev = vnic_dev_register(NULL, fnic, pdev, &fnic->bar0);
 	if (!fnic->vdev) {
-		printk(KERN_ERR DFX "vNIC registration failed, "
-		       "aborting.\n", fnic->fnic_no);
+		shost_printk(KERN_ERR, fnic->lport->host,
+			     "vNIC registration failed, "
+			     "aborting.\n");
 		err = -ENODEV;
 		goto err_out_iounmap;
 	}
 
-	err = fnic_dev_open(fnic);
+	err = fnic_dev_wait(fnic->vdev, vnic_dev_open,
+			    vnic_dev_open_done, 0);
 	if (err) {
-		printk(KERN_ERR DFX
-		       "vNIC dev open failed, aborting.\n", fnic->fnic_no);
+		shost_printk(KERN_ERR, fnic->lport->host,
+			     "vNIC dev open failed, aborting.\n");
 		goto err_out_vnic_unregister;
 	}
 
 	err = vnic_dev_init(fnic->vdev, 0);
 	if (err) {
-		printk(KERN_ERR DFX
-		       "vNIC dev init failed, aborting.\n", fnic->fnic_no);
+		shost_printk(KERN_ERR, fnic->lport->host,
+			     "vNIC dev init failed, aborting.\n");
 		goto err_out_dev_close;
 	}
 
 	err = vnic_dev_mac_addr(fnic->vdev, fnic->mac_addr);
 	if (err) {
-		printk(KERN_ERR DFX "vNIC get MAC addr failed \n",
-		       fnic->fnic_no);
+		shost_printk(KERN_ERR, fnic->lport->host,
+			     "vNIC get MAC addr failed \n");
 		goto err_out_dev_close;
 	}
 
 	/* Get vNIC configuration */
 	err = fnic_get_vnic_config(fnic);
 	if (err) {
-		printk(KERN_ERR DFX "Get vNIC configuration failed, "
-		       "aborting.\n", fnic->fnic_no);
+		shost_printk(KERN_ERR, fnic->lport->host,
+			     "Get vNIC configuration failed, "
+			     "aborting.\n");
 		goto err_out_dev_close;
 	}
 	host->max_lun = fnic->config.luns_per_tgt;
@@ -621,21 +575,24 @@ static int __devinit fnic_probe(struct pci_dev *pdev,
 
 	err = fnic_set_intr_mode(fnic);
 	if (err) {
-		printk(KERN_ERR DFX "Failed to set intr mode, "
-		  "aborting.\n", fnic->fnic_no);
+		shost_printk(KERN_ERR, fnic->lport->host,
+			     "Failed to set intr mode, "
+			     "aborting.\n");
 		goto err_out_dev_close;
 	}
 
 	err = fnic_request_intr(fnic);
 	if (err) {
-		printk(KERN_ERR DFX "Unable to request irq.\n", fnic->fnic_no);
+		shost_printk(KERN_ERR, fnic->lport->host,
+			     "Unable to request irq.\n");
 		goto err_out_clear_intr;
 	}
 
 	err = fnic_alloc_vnic_resources(fnic);
 	if (err) {
-		printk(KERN_ERR DFX "Failed to alloc vNIC resources, "
-		       "aborting.\n", fnic->fnic_no);
+		shost_printk(KERN_ERR, fnic->lport->host,
+			     "Failed to alloc vNIC resources, "
+			     "aborting.\n");
 		goto err_out_free_intr;
 	}
 
@@ -661,13 +618,13 @@ static int __devinit fnic_probe(struct pci_dev *pdev,
 		goto err_out_free_resources;
 
 	pool = mempool_create(2, fnic_alloc_slab_dma, mempool_free_slab,
-			      (void *)fnic_sgl_cache[FNIC_SGL_CACHE_DFLT]);
+			      fnic_sgl_cache[FNIC_SGL_CACHE_DFLT]);
 	if (!pool)
 		goto err_out_free_ioreq_pool;
 	fnic->io_sgl_pool[FNIC_SGL_CACHE_DFLT] = pool;
 
 	pool = mempool_create(2, fnic_alloc_slab_dma, mempool_free_slab,
-			      (void *)fnic_sgl_cache[FNIC_SGL_CACHE_MAX]);
+			      fnic_sgl_cache[FNIC_SGL_CACHE_MAX]);
 	if (!pool)
 		goto err_out_free_dflt_pool;
 	fnic->io_sgl_pool[FNIC_SGL_CACHE_MAX] = pool;
@@ -682,46 +639,46 @@ static int __devinit fnic_probe(struct pci_dev *pdev,
 	fnic->state = FNIC_IN_FC_MODE;
 
 	/* Enable hardware stripping of vlan header on ingress */
-	fnic_set_nic_cfg(fnic, 0, 0, 0, 0, 0, 0, 1);
+	fnic_set_nic_config(fnic, 0, 0, 0, 0, 0, 0, 1);
 
 	/* Setup notification buffer area */
 	err = fnic_notify_set(fnic);
 	if (err) {
-		printk(KERN_ERR DFX
-		       "Failed to alloc notify buffer, aborting.\n",
-		       fnic->fnic_no);
+		shost_printk(KERN_ERR, fnic->lport->host,
+			     "Failed to alloc notify buffer, aborting.\n");
 		goto err_out_free_max_pool;
 	}
 
 	/* Setup notify timer when using MSI interrupts */
-	setup_timer(&fnic->notify_timer,
-		    fnic_notify_timer, (unsigned long)fnic);
-
-	FNIC_DEBUG_MAIN(DFX "host no %d\n", fnic->fnic_no, host->host_no);
+	if (vnic_dev_get_intr_mode(fnic->vdev) == VNIC_DEV_INTR_MODE_MSI)
+		setup_timer(&fnic->notify_timer,
+			    fnic_notify_timer, (unsigned long)fnic);
 
 	/* allocate RQ buffers and post them to RQ*/
 	for (i = 0; i < fnic->rq_count; i++) {
 		err = vnic_rq_fill(&fnic->rq[i], fnic_alloc_rq_frame);
 		if (err) {
-			printk(KERN_ERR DFX "fnic_alloc_rq_frame can't alloc "
-			       "frame\n", fnic->fnic_no);
+			shost_printk(KERN_ERR, fnic->lport->host,
+				     "fnic_alloc_rq_frame can't alloc "
+				     "frame\n");
 			goto err_out_free_rq_buf;
 		}
 	}
 
-	/* Initialization done with PCI system, hardware, firmware.
+	/*
+	 * Initialization done with PCI system, hardware, firmware.
 	 * Add host to SCSI
 	 */
 	err = scsi_add_host(lp->host, &pdev->dev);
 	if (err) {
-		printk(KERN_ERR DFX "fnic: scsi_add_host failed...exiting\n",
-		       fnic->fnic_no);
+		shost_printk(KERN_ERR, fnic->lport->host,
+			     "fnic: scsi_add_host failed...exiting\n");
 		goto err_out_free_rq_buf;
 	}
 
 	/* Start local port initiatialization */
 
-	lp->link_status = 0;
+	lp->link_up = 0;
 	lp->tt = fnic_transport_template;
 
 	lp->emp = fc_exch_mgr_alloc(lp, FC_CLASS_3,
@@ -732,7 +689,7 @@ static int __devinit fnic_probe(struct pci_dev *pdev,
 		goto err_out_remove_scsi_host;
 	}
 
-	lp->max_retry_count = 3;
+	lp->max_retry_count = fnic->config.flogi_retries;
 	lp->service_params = (FCP_SPPF_INIT_FCN | FCP_SPPF_RD_XRDY_DIS |
 			      FCP_SPPF_CONF_COMPL);
 	if (fnic->config.flags & VFCF_FCP_SEQ_LVL_ERR)
@@ -763,7 +720,15 @@ static int __devinit fnic_probe(struct pci_dev *pdev,
 	sprintf(fc_host_symbolic_name(lp->host),
 		DRV_NAME " v" DRV_VERSION " over %s", fnic->name);
 
-	/* Enable queues*/
+	spin_lock_irqsave(&fnic_list_lock, flags);
+	list_add_tail(&fnic->list, &fnic_list);
+	spin_unlock_irqrestore(&fnic_list_lock, flags);
+
+	INIT_WORK(&fnic->link_work, fnic_handle_link);
+	INIT_WORK(&fnic->frame_work, fnic_handle_frame);
+	skb_queue_head_init(&fnic->frame_queue);
+
+	/* Enable all queues */
 	for (i = 0; i < fnic->raw_wq_count; i++)
 		vnic_wq_enable(&fnic->wq[i]);
 	for (i = 0; i < fnic->rq_count; i++)
@@ -823,9 +788,54 @@ err_out:
 static void __devexit fnic_remove(struct pci_dev *pdev)
 {
 	struct fnic *fnic = pci_get_drvdata(pdev);
+	unsigned long flags;
+
+	/*
+	 * Mark state so that the workqueue thread stops forwarding
+	 * received frames and link events to the local port. ISR and
+	 * other threads that can queue work items will also stop
+	 * creating work items on the fnic workqueue
+	 */
+	spin_lock_irqsave(&fnic->fnic_lock, flags);
+	fnic->stop_rx_link_events = 1;
+	spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+
+	if (vnic_dev_get_intr_mode(fnic->vdev) == VNIC_DEV_INTR_MODE_MSI)
+		del_timer_sync(&fnic->notify_timer);
+
+	/*
+	 * Flush the fnic event queue. After this call, there should
+	 * be no event queued for this fnic device in the workqueue
+	 */
+	flush_workqueue(fnic_event_queue);
+	skb_queue_purge(&fnic->frame_queue);
+
+	/*
+	 * Log off the fabric. This stops all remote ports, dns port,
+	 * logs off the fabric. This flushes all rport, disc, lport work
+	 * before returning
+	 */
+	fc_fabric_logoff(fnic->lport);
+
+	spin_lock_irqsave(&fnic->fnic_lock, flags);
+	fnic->in_remove = 1;
+	spin_unlock_irqrestore(&fnic->fnic_lock, flags);
 
 	fc_lport_destroy(fnic->lport);
+
+	/*
+	 * This stops the fnic device, masks all interrupts. Completed
+	 * CQ entries are drained. Posted WQ/RQ/Copy-WQ entries are
+	 * cleaned up
+	 */
 	fnic_cleanup(fnic);
+
+	BUG_ON(!skb_queue_empty(&fnic->frame_queue));
+
+	spin_lock_irqsave(&fnic_list_lock, flags);
+	list_del(&fnic->list);
+	spin_unlock_irqrestore(&fnic_list_lock, flags);
+
 	fc_remove_host(fnic->lport->host);
 	scsi_remove_host(fnic->lport->host);
 	fc_exch_mgr_free(fnic->lport->emp);
@@ -861,7 +871,7 @@ static int __init fnic_init_module(void)
 	fnic_sgl_cache[FNIC_SGL_CACHE_DFLT] = kmem_cache_create
 		("fnic_sgl_dflt", len + FNIC_SG_DESC_ALIGN, FNIC_SG_DESC_ALIGN,
 		 SLAB_HWCACHE_ALIGN | SLAB_CACHE_DMA,
-		 NULL, NULL);
+		 NULL);
 	if (!fnic_sgl_cache[FNIC_SGL_CACHE_DFLT]) {
 		printk(KERN_ERR PFX "failed to create fnic dflt sgl slab\n");
 		err = -ENOMEM;
@@ -873,52 +883,32 @@ static int __init fnic_init_module(void)
 	fnic_sgl_cache[FNIC_SGL_CACHE_MAX] = kmem_cache_create
 		("fnic_sgl_max", len + FNIC_SG_DESC_ALIGN, FNIC_SG_DESC_ALIGN,
 		 SLAB_HWCACHE_ALIGN | SLAB_CACHE_DMA,
-		 NULL, NULL);
+		 NULL);
 	if (!fnic_sgl_cache[FNIC_SGL_CACHE_MAX]) {
 		printk(KERN_ERR PFX "failed to create fnic max sgl slab\n");
 		err = -ENOMEM;
 		goto err_create_fnic_sgl_slab_max;
 	}
 
-	/* Create a cache of objects to wrap frames/events sent to openfc*/
-	len = sizeof(struct fnic_event);
-	fnic_ev_cache = kmem_cache_create("fnic_event", len,
-					  0, 0, NULL, NULL);
-	if (!fnic_ev_cache) {
-		printk(KERN_ERR PFX "failed to create fnic event slab\n");
-		err = -ENOMEM;
-		goto err_create_fnic_ev_slab;
-	}
-
 	/* Create a cache of io_req structs for use via mempool */
 	fnic_io_req_cache = kmem_cache_create("fnic_io_req",
 					      sizeof(struct fnic_io_req),
-					      0, SLAB_HWCACHE_ALIGN, NULL,
-					      NULL);
+					      0, SLAB_HWCACHE_ALIGN, NULL);
 	if (!fnic_io_req_cache) {
 		printk(KERN_ERR PFX "failed to create fnic io_req slab\n");
 		err = -ENOMEM;
 		goto err_create_fnic_ioreq_slab;
 	}
 
-	/* initialize the Inbound FC Events list spinlock */
-	spin_lock_init(&fnic_eventlist_lock);
-
-	/* Create a thread for handling FCS Events */
-	fnic_thread = kthread_create(fnic_fc_thread,
-				     NULL,
-				     "fnicthread");
-
-	if (!IS_ERR(fnic_thread)) {
-		wake_up_process(fnic_thread);
-	} else {
-		printk(KERN_ERR PFX "fnic thread create failed\n");
+	fnic_event_queue = create_singlethread_workqueue("fnic_event_wq");
+	if (!fnic_event_queue) {
+		printk(KERN_ERR PFX "fnic work queue create failed\n");
 		err = -ENOMEM;
-		goto err_create_fnic_thread;
+		goto err_create_fnic_workq;
 	}
 
-	/* initialize fnic_no to -1, the first device is numbered 0 */
-	atomic_set(&fnic_no, -1);
+	spin_lock_init(&fnic_list_lock);
+	INIT_LIST_HEAD(&fnic_list);
 
 	fnic_fc_transport = fc_attach_transport(&fnic_fc_functions);
 	if (!fnic_fc_transport) {
@@ -938,12 +928,10 @@ static int __init fnic_init_module(void)
 err_pci_register:
 	fc_release_transport(fnic_fc_transport);
 err_fc_transport:
-	kthread_stop(fnic_thread);
-err_create_fnic_thread:
+	destroy_workqueue(fnic_event_queue);
+err_create_fnic_workq:
 	kmem_cache_destroy(fnic_io_req_cache);
 err_create_fnic_ioreq_slab:
-	kmem_cache_destroy(fnic_ev_cache);
-err_create_fnic_ev_slab:
 	kmem_cache_destroy(fnic_sgl_cache[FNIC_SGL_CACHE_MAX]);
 err_create_fnic_sgl_slab_max:
 	kmem_cache_destroy(fnic_sgl_cache[FNIC_SGL_CACHE_DFLT]);
@@ -953,31 +941,11 @@ err_create_fnic_sgl_slab_dflt:
 
 static void __exit fnic_cleanup_module(void)
 {
-	struct fnic_event *event;
-	unsigned long flags;
-
 	pci_unregister_driver(&fnic_driver);
-
-	kthread_stop(fnic_thread);
-
-	/* Cleanup the Inbound FCS events list */
-	spin_lock_irqsave(&fnic_eventlist_lock, flags);
-	while (!list_empty(&fnic_eventlist)) {
-		event = list_first_entry(&fnic_eventlist,
-					 struct fnic_event, list);
-		list_del(&event->list);
-		if (event->fp)
-			fnic_fc_frame_free(event->fp);
-		kmem_cache_free(fnic_ev_cache, event);
-	}
-	spin_unlock_irqrestore(&fnic_eventlist_lock, flags);
-
-	/* release memory for all global caches */
-	kmem_cache_destroy(fnic_ev_cache);
+	destroy_workqueue(fnic_event_queue);
 	kmem_cache_destroy(fnic_sgl_cache[FNIC_SGL_CACHE_MAX]);
 	kmem_cache_destroy(fnic_sgl_cache[FNIC_SGL_CACHE_DFLT]);
 	kmem_cache_destroy(fnic_io_req_cache);
-
 	fc_release_transport(fnic_fc_transport);
 }
 
diff --git a/drivers/scsi/fnic/fnic_res.c b/drivers/scsi/fnic/fnic_res.c
index b6626a4..7ba61ec 100644
--- a/drivers/scsi/fnic/fnic_res.c
+++ b/drivers/scsi/fnic/fnic_res.c
@@ -15,7 +15,6 @@
  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
-#include <linux/kernel.h>
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/pci.h>
@@ -43,8 +42,9 @@ int fnic_get_vnic_config(struct fnic *fnic)
 				    offsetof(struct vnic_fc_config, m), \
 				    sizeof(c->m), &c->m); \
 		if (err) { \
-			printk(KERN_ERR PFX "Error getting %s, %d\n", #m, \
-			       err); \
+			shost_printk(KERN_ERR, fnic->lport->host, \
+				     "Error getting %s, %d\n", #m, \
+				     err); \
 			return err; \
 		} \
 	} while (0);
@@ -143,34 +143,45 @@ int fnic_get_vnic_config(struct fnic *fnic)
 	c->intr_timer = min_t(u16, VNIC_INTR_TIMER_MAX, c->intr_timer);
 	c->intr_timer_type = c->intr_timer_type;
 
-	printk(KERN_INFO PFX "vNIC MAC addr %02x:%02x:%02x:%02x:%02x:%02x "
-		"wq/wq_copy/rq %d/%d/%d\n",
-		fnic->mac_addr[0], fnic->mac_addr[1], fnic->mac_addr[2],
-		fnic->mac_addr[3], fnic->mac_addr[4], fnic->mac_addr[5],
-		c->wq_enet_desc_count, c->wq_copy_desc_count, c->rq_desc_count);
-	printk(KERN_INFO PFX "vNIC node wwn %llx port wwn %llx\n",
-		c->node_wwn, c->port_wwn);
-	printk(KERN_INFO PFX "vNIC ed_tov %d ra_tov %d\n",
-		c->ed_tov, c->ra_tov);
-	printk(KERN_INFO PFX "vNIC mtu %d intr timer %d\n",
-		c->maxdatafieldsize, c->intr_timer);
-	printk(KERN_INFO PFX "vNIC flags 0x%x luns per tgt %d\n",
-	       c->flags, c->luns_per_tgt);
-	printk(KERN_INFO PFX "vNIC flogi_retries %d flogi timeout %d\n",
-	       c->flogi_retries, c->flogi_timeout);
-	printk(KERN_INFO PFX "vNIC plogi retries %d plogi timeout %d\n",
-	       c->plogi_retries, c->plogi_timeout);
-	printk(KERN_INFO PFX "vNIC io throttle count %d link dn timeout %d\n",
-	       c->io_throttle_count, c->link_down_timeout);
-	printk(KERN_INFO PFX "vNIC port dn io retries %d port dn timeout %d\n",
-	       c->port_down_io_retries, c->port_down_timeout);
+	shost_printk(KERN_INFO, fnic->lport->host,
+		     "vNIC MAC addr %02x:%02x:%02x:%02x:%02x:%02x "
+		     "wq/wq_copy/rq %d/%d/%d\n",
+		     fnic->mac_addr[0], fnic->mac_addr[1], fnic->mac_addr[2],
+		     fnic->mac_addr[3], fnic->mac_addr[4], fnic->mac_addr[5],
+		     c->wq_enet_desc_count, c->wq_copy_desc_count,
+		     c->rq_desc_count);
+	shost_printk(KERN_INFO, fnic->lport->host,
+		     "vNIC node wwn %llx port wwn %llx\n",
+		     c->node_wwn, c->port_wwn);
+	shost_printk(KERN_INFO, fnic->lport->host,
+		     "vNIC ed_tov %d ra_tov %d\n",
+		     c->ed_tov, c->ra_tov);
+	shost_printk(KERN_INFO, fnic->lport->host,
+		     "vNIC mtu %d intr timer %d\n",
+		     c->maxdatafieldsize, c->intr_timer);
+	shost_printk(KERN_INFO, fnic->lport->host,
+		     "vNIC flags 0x%x luns per tgt %d\n",
+		     c->flags, c->luns_per_tgt);
+	shost_printk(KERN_INFO, fnic->lport->host,
+		     "vNIC flogi_retries %d flogi timeout %d\n",
+		     c->flogi_retries, c->flogi_timeout);
+	shost_printk(KERN_INFO, fnic->lport->host,
+		     "vNIC plogi retries %d plogi timeout %d\n",
+		     c->plogi_retries, c->plogi_timeout);
+	shost_printk(KERN_INFO, fnic->lport->host,
+		     "vNIC io throttle count %d link dn timeout %d\n",
+		     c->io_throttle_count, c->link_down_timeout);
+	shost_printk(KERN_INFO, fnic->lport->host,
+		     "vNIC port dn io retries %d port dn timeout %d\n",
+		     c->port_down_io_retries, c->port_down_timeout);
 
 	return 0;
 }
 
-int fnic_set_nic_cfg(struct fnic *fnic, u8 rss_default_cpu, u8 rss_hash_type,
-	u8 rss_hash_bits, u8 rss_base_cpu, u8 rss_enable, u8 tso_ipid_split_en,
-	u8 ig_vlan_strip_en)
+int fnic_set_nic_config(struct fnic *fnic, u8 rss_default_cpu,
+			u8 rss_hash_type,
+			u8 rss_hash_bits, u8 rss_base_cpu, u8 rss_enable,
+			u8 tso_ipid_split_en, u8 ig_vlan_strip_en)
 {
 	u64 a0, a1;
 	u32 nic_cfg;
@@ -230,15 +241,16 @@ int fnic_alloc_vnic_resources(struct fnic *fnic)
 
 	intr_mode = vnic_dev_get_intr_mode(fnic->vdev);
 
-	printk(KERN_INFO PFX "vNIC interrupt mode: %s\n",
-		intr_mode == VNIC_DEV_INTR_MODE_INTX ? "legacy PCI INTx" :
-		intr_mode == VNIC_DEV_INTR_MODE_MSI ? "MSI" :
-		intr_mode == VNIC_DEV_INTR_MODE_MSIX ? "MSI-X" : "unknown");
+	shost_printk(KERN_INFO, fnic->lport->host, "vNIC interrupt mode: %s\n",
+		     intr_mode == VNIC_DEV_INTR_MODE_INTX ? "legacy PCI INTx" :
+		     intr_mode == VNIC_DEV_INTR_MODE_MSI ? "MSI" :
+		     intr_mode == VNIC_DEV_INTR_MODE_MSIX ?
+		     "MSI-X" : "unknown");
 
-	printk(KERN_INFO PFX "vNIC resources avail: "
-	       "wq %d cp_wq %d raw_wq %d rq %d cq %d intr %d\n",
-	       fnic->wq_count, fnic->wq_copy_count, fnic->raw_wq_count,
-	       fnic->rq_count, fnic->cq_count, fnic->intr_count);
+	shost_printk(KERN_INFO, fnic->lport->host, "vNIC resources avail: "
+		     "wq %d cp_wq %d raw_wq %d rq %d cq %d intr %d\n",
+		     fnic->wq_count, fnic->wq_copy_count, fnic->raw_wq_count,
+		     fnic->rq_count, fnic->cq_count, fnic->intr_count);
 
 	/* Allocate Raw WQ used for FCS frames */
 	for (i = 0; i < fnic->raw_wq_count; i++) {
@@ -311,12 +323,14 @@ int fnic_alloc_vnic_resources(struct fnic *fnic)
 				RES_TYPE_INTR_PBA_LEGACY, 0);
 
 	if (!fnic->legacy_pba && intr_mode == VNIC_DEV_INTR_MODE_INTX) {
-		printk(KERN_ERR "Failed to hook legacy pba resource\n");
+		shost_printk(KERN_ERR, fnic->lport->host,
+			     "Failed to hook legacy pba resource\n");
 		err = -ENODEV;
 		goto err_out_cleanup;
 	}
 
-	/* Init RQ/WQ resources.
+	/*
+	 * Init RQ/WQ resources.
 	 *
 	 * RQ[0 to n-1] point to CQ[0 to n-1]
 	 * WQ[0 to m-1] point to CQ[n to n+m-1]
@@ -386,7 +400,8 @@ int fnic_alloc_vnic_resources(struct fnic *fnic)
 			0 /* cq_message_addr */);
 	}
 
-	/* Init INTR resources
+	/*
+	 * Init INTR resources
 	 *
 	 * mask_on_assertion is not used for INTx due to the level-
 	 * triggered nature of INTx
@@ -412,7 +427,8 @@ int fnic_alloc_vnic_resources(struct fnic *fnic)
 	/* init the stats memory by making the first call here */
 	err = vnic_dev_stats_dump(fnic->vdev, &fnic->stats);
 	if (err) {
-		printk(KERN_ERR "vnic_dev_stats_dump failed - x%x\n", err);
+		shost_printk(KERN_ERR, fnic->lport->host,
+			     "vnic_dev_stats_dump failed - x%x\n", err);
 		goto err_out_cleanup;
 	}
 
diff --git a/drivers/scsi/fnic/fnic_res.h b/drivers/scsi/fnic/fnic_res.h
index d738eb2..b6f3102 100644
--- a/drivers/scsi/fnic/fnic_res.h
+++ b/drivers/scsi/fnic/fnic_res.h
@@ -189,8 +189,9 @@ int fnic_get_vnic_config(struct fnic *);
 int fnic_alloc_vnic_resources(struct fnic *);
 void fnic_free_vnic_resources(struct fnic *);
 void fnic_get_res_counts(struct fnic *);
-int fnic_set_nic_cfg(struct fnic *fnic, u8 rss_default_cpu, u8 rss_hash_type,
-	u8 rss_hash_bits, u8 rss_base_cpu, u8 rss_enable, u8 tso_ipid_split_en,
-	u8 ig_vlan_strip_en);
+int fnic_set_nic_config(struct fnic *fnic, u8 rss_default_cpu,
+			u8 rss_hash_type, u8 rss_hash_bits, u8 rss_base_cpu,
+			u8 rss_enable, u8 tso_ipid_split_en,
+			u8 ig_vlan_strip_en);
 
 #endif /* _FNIC_RES_H_ */
diff --git a/drivers/scsi/fnic/fnic_scsi.c b/drivers/scsi/fnic/fnic_scsi.c
index e634a57..c713cea 100644
--- a/drivers/scsi/fnic/fnic_scsi.c
+++ b/drivers/scsi/fnic/fnic_scsi.c
@@ -15,18 +15,14 @@
  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
-#include <linux/kernel.h>
-#include <linux/string.h>
+#include <linux/mempool.h>
 #include <linux/errno.h>
-#include <linux/types.h>
 #include <linux/init.h>
 #include <linux/workqueue.h>
 #include <linux/pci.h>
 #include <linux/scatterlist.h>
 #include <linux/skbuff.h>
-#include <linux/interrupt.h>
 #include <linux/spinlock.h>
-#include <linux/kthread.h>
 #include <linux/if_ether.h>
 #include <linux/if_vlan.h>
 #include <linux/delay.h>
@@ -34,8 +30,6 @@
 #include <scsi/scsi_host.h>
 #include <scsi/scsi_device.h>
 #include <scsi/scsi_cmnd.h>
-#include <scsi/scsi_transport.h>
-#include <scsi/scsi_transport_fc.h>
 #include <scsi/scsi_tcq.h>
 #include <scsi/fc/fc_els.h>
 #include <scsi/fc/fc_fcoe.h>
@@ -45,13 +39,12 @@
 #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)
+const char *fnic_state_str[] = {
+	[FNIC_IN_FC_MODE] =           "FNIC_IN_FC_MODE",
+	[FNIC_IN_FC_TRANS_ETH_MODE] = "FNIC_IN_FC_TRANS_ETH_MODE",
+	[FNIC_IN_ETH_MODE] =          "FNIC_IN_ETH_MODE",
+	[FNIC_IN_ETH_TRANS_FC_MODE] = "FNIC_IN_ETH_TRANS_FC_MODE",
+};
 
 static const char *fnic_ioreq_state_str[] = {
 	[FNIC_IOREQ_CMD_PENDING] = "FNIC_IOREQ_CMD_PENDING",
@@ -82,6 +75,31 @@ static const char *fcpio_status_str[] =  {
 	[FCPIO_LUNMAP_CHNG_PEND] = "FCPIO_LUNHMAP_CHNG_PEND",
 };
 
+const char *fnic_state_to_str(unsigned int state)
+{
+	if (state >= ARRAY_SIZE(fnic_state_str) || !fnic_state_str[state])
+		return "unknown";
+
+	return fnic_state_str[state];
+}
+
+static const char *fnic_ioreq_state_to_str(unsigned int state)
+{
+	if (state >= ARRAY_SIZE(fnic_ioreq_state_str) ||
+	    !fnic_ioreq_state_str[state])
+		return "unknown";
+
+	return fnic_ioreq_state_str[state];
+}
+
+static const char *fnic_fcpio_status_to_str(unsigned int status)
+{
+	if (status >= ARRAY_SIZE(fcpio_status_str) || !fcpio_status_str[status])
+		return "unknown";
+
+	return fcpio_status_str[status];
+}
+
 static void fnic_cleanup_io(struct fnic *fnic, int exclude_id);
 
 static inline spinlock_t *fnic_io_lock_hash(struct fnic *fnic,
@@ -105,6 +123,10 @@ static void fnic_release_ioreq_buf(struct fnic *fnic,
 				 sizeof(io_req->sgl_list[0]) * io_req->sgl_cnt,
 				 PCI_DMA_TODEVICE);
 	scsi_dma_unmap(sc);
+	if (!scsi_sg_count(sc) && scsi_bufflen(sc))
+		pci_unmap_single(fnic->pdev, io_req->sgl_list[0].addr,
+				 scsi_bufflen(sc), sc->sc_data_direction);
+
 	if (io_req->sgl_cnt)
 		mempool_free(io_req->sgl_list_alloc,
 			     fnic->io_sgl_pool[io_req->sgl_type]);
@@ -120,7 +142,8 @@ static int free_wq_copy_descs(struct fnic *fnic, struct vnic_wq_copy *wq)
 	if (!fnic->fw_ack_recd[0])
 		return 1;
 
-	/* Update desc_available count based on number of freed descriptors
+	/*
+	 * Update desc_available count based on number of freed descriptors
 	 * Account for wraparound
 	 */
 	if (wq->to_clean_index <= fnic->fw_ack_index[0])
@@ -131,7 +154,8 @@ static int free_wq_copy_descs(struct fnic *fnic, struct vnic_wq_copy *wq)
 					- wq->to_clean_index
 					+ fnic->fw_ack_index[0] + 1);
 
-	/* just bump clean index to ack_index+1 accounting for wraparound
+	/*
+	 * just bump clean index to ack_index+1 accounting for wraparound
 	 * this will essentially free up all descriptors between
 	 * to_clean_index and fw_ack_index, both inclusive
 	 */
@@ -167,10 +191,11 @@ int fnic_fw_reset_handler(struct fnic *fnic)
 	spin_unlock_irqrestore(&fnic->wq_copy_lock[0], flags);
 
 	if (!ret)
-		FNIC_DEBUG_SCSI(DFX "Issued fw reset\n", fnic->fnic_no);
+		FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+			      "Issued fw reset\n");
 	else
-		FNIC_DEBUG_SCSI(DFX "Failed to issue fw reset\n",
-				fnic->fnic_no);
+		FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+			      "Failed to issue fw reset\n");
 	return ret;
 }
 
@@ -201,31 +226,21 @@ int fnic_flogi_reg_handler(struct fnic *fnic)
 	else
 		memcpy(gw_mac, fnic->dest_addr, ETH_ALEN);
 
-	fnic_queue_wq_copy_desc_flogi_reg(
-		wq,
-		SCSI_NO_TAG,		/* host request id */
-		FCPIO_FLOGI_REG_GW_DEST,/* gw address mode */
-		fnic->s_id,		/* FC source id */
-		gw_mac			/* gateway mac */
-		);
+	fnic_queue_wq_copy_desc_flogi_reg(wq, SCSI_NO_TAG,
+					  FCPIO_FLOGI_REG_GW_DEST,
+					  fnic->s_id,
+					  gw_mac);
 
 flogi_reg_ioreq_end:
 	spin_unlock_irqrestore(&fnic->wq_copy_lock[0], flags);
 
 	if (!ret)
-		FNIC_DEBUG_SCSI(DFX "flog reg issued\n", fnic->fnic_no);
+		FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+			      "flog reg issued\n");
 
 	return ret;
 }
 
-static inline struct scatterlist *sg_next(struct scatterlist *sg)
-{
-	return sg + 1;
-}
-
-#define for_each_sg(sglist, sg, nr, __i)        \
-	for (__i = 0, sg = (sglist); __i < (nr); __i++, sg = sg_next(sg))
-
 /*
  * fnic_queue_wq_copy_desc
  * Routine to enqueue a wq copy desc
@@ -261,14 +276,22 @@ static inline int fnic_queue_wq_copy_desc(struct fnic *fnic,
 			desc++;
 		}
 
-		/* Get the physical address of the SGL that is passed
-		   to device */
+	} else if (scsi_bufflen(sc)) {
+		io_req->sgl_list[0].addr =
+			cpu_to_le64(pci_map_single(
+					    fnic->pdev, scsi_sglist(sc),
+					    scsi_bufflen(sc),
+					    sc->sc_data_direction));
+		io_req->sgl_list[0].len = cpu_to_le32(scsi_bufflen(sc));
+		io_req->sgl_list[0]._resvd = 0;
+	}
+
+	if (io_req->sgl_cnt)
 		io_req->sgl_list_pa = pci_map_single
 			(fnic->pdev,
 			 io_req->sgl_list,
-			 sizeof(io_req->sgl_list[0]) * sg_count,
+			 sizeof(io_req->sgl_list[0]) * io_req->sgl_cnt,
 			 PCI_DMA_TODEVICE);
-	}
 
 	io_req->sense_buf_pa = pci_map_single(fnic->pdev,
 					      sc->sense_buffer,
@@ -305,26 +328,18 @@ static inline int fnic_queue_wq_copy_desc(struct fnic *fnic,
 	    (rp->flags & FC_RP_FLAGS_RETRY))
 		exch_flags |= FCPIO_ICMND_SRFLAG_RETRY;
 
-	fnic_queue_wq_copy_desc_icmnd_16(
-		wq,
-		scsi_cmd_get_tag(sc),		/* IO req id */
-		0,	                        /* lun map id */
-		exch_flags,	                /* exch flags */
-		io_req->sgl_cnt,	        /* SGL count */
-		SCSI_SENSE_BUFFERSIZE,		/* Sense len */
-		io_req->sgl_list_pa,		/* ptr to sgl list */
-		io_req->sense_buf_pa,	        /* Sense buff */
-		0,				/* scsi cmd ref, always 0 */
-		pri_tag,			/* scsi pri and tag */
-		flags,				/* command flags */
-		sc->cmnd,		        /* CDB */
-		scsi_bufflen(sc),		/* data buf len */
-		fc_lun.scsi_lun,	        /* FCP 8 byte lun */
-		rport->port_id,		        /* D_ID */
-		rport->maxframe_size,	        /* max frame size */
-		rp->r_a_tov,		        /* R_A_TOV */
-		rp->e_d_tov		        /* E_D_TOV */
-		);
+	fnic_queue_wq_copy_desc_icmnd_16(wq, scsi_cmd_get_tag(sc),
+					 0, exch_flags, io_req->sgl_cnt,
+					 SCSI_SENSE_BUFFERSIZE,
+					 io_req->sgl_list_pa,
+					 io_req->sense_buf_pa,
+					 0, /* scsi cmd ref, always 0 */
+					 pri_tag, /* scsi pri and tag */
+					 flags,	/* command flags */
+					 sc->cmnd, scsi_bufflen(sc),
+					 fc_lun.scsi_lun, io_req->port_id,
+					 rport->maxframe_size, rp->r_a_tov,
+					 rp->e_d_tov);
 
 	spin_unlock_irqrestore(&fnic->wq_copy_lock[0], intr_flags);
 	return 0;
@@ -345,6 +360,7 @@ int fnic_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
 	int ret;
 	u32 sg_count;
 	unsigned long flags;
+	unsigned long ptr;
 	int tag;
 
 	rport = starget_to_rport(scsi_target(sc->device));
@@ -356,10 +372,11 @@ int fnic_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
 	}
 
 	lp = shost_priv(sc->device->host);
-	if (lp->state != LPORT_ST_READY || !(lp->link_status & FC_LINK_UP))
+	if (lp->state != LPORT_ST_READY || !(lp->link_up))
 		return SCSI_MLQUEUE_HOST_BUSY;
 
-	/* Release host lock, use driver resource specific locks from here.
+	/*
+	 * Release host lock, use driver resource specific locks from here.
 	 * Don't re-enable interrupts in case they were disabled prior to the
 	 * caller disabling them.
 	 */
@@ -385,18 +402,20 @@ int fnic_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
 	/* Map the data buffer */
 	sg_count = scsi_dma_map(sc);
 	if (sg_count < 0) {
-		scsi_host_end_tag(sc->device->host, sc);
 		mempool_free(io_req, fnic->io_req_pool);
+		scsi_host_end_tag(sc->device->host, sc);
 		goto out;
 	}
 
 	/* Determine the type of scatter/gather list we need */
 	io_req->sgl_cnt = sg_count;
+	if (!sg_count && scsi_bufflen(sc))
+		io_req->sgl_cnt = 1;
 	io_req->sgl_type = FNIC_SGL_CACHE_DFLT;
 	if (sg_count > FNIC_DFLT_SG_DESC_CNT)
 		io_req->sgl_type = FNIC_SGL_CACHE_MAX;
 
-	if (sg_count) {
+	if (io_req->sgl_cnt) {
 		io_req->sgl_list =
 			mempool_alloc(fnic->io_sgl_pool[io_req->sgl_type],
 				      GFP_ATOMIC | GFP_DMA);
@@ -407,21 +426,20 @@ int fnic_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
 			mempool_free(io_req, fnic->io_req_pool);
 			goto out;
 		}
+
 		/* Cache sgl list allocated address before alignment */
 		io_req->sgl_list_alloc = io_req->sgl_list;
-		{
-			u_long ptr = (u_long) io_req->sgl_list;
-
-			if (ptr % FNIC_SG_DESC_ALIGN) {
-				io_req->sgl_list = (struct host_sg_desc *)
-					(((u_long) ptr + FNIC_SG_DESC_ALIGN - 1)
-					 & ~(FNIC_SG_DESC_ALIGN - 1));
-			}
+		ptr = (unsigned long) io_req->sgl_list;
+		if (ptr % FNIC_SG_DESC_ALIGN) {
+			io_req->sgl_list = (struct host_sg_desc *)
+				(((unsigned long) ptr
+				  + FNIC_SG_DESC_ALIGN - 1)
+				 & ~(FNIC_SG_DESC_ALIGN - 1));
 		}
-
 	}
 
 	/* initialize rest of io_req */
+	io_req->port_id = rport->port_id;
 	CMD_STATE(sc) = FNIC_IOREQ_CMD_PENDING;
 	CMD_SP(sc) = (char *)io_req;
 	sc->scsi_done = done;
@@ -430,7 +448,8 @@ int fnic_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
 	wq = &fnic->wq_copy[0];
 	ret = fnic_queue_wq_copy_desc(fnic, wq, io_req, sc, sg_count);
 	if (ret) {
-		/* In case another thread cancelled the request,
+		/*
+		 * In case another thread cancelled the request,
 		 * refetch the pointer under the lock.
 		 */
 		spinlock_t *io_lock = fnic_io_lock_hash(fnic, sc);
@@ -480,16 +499,18 @@ 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) {
-			FNIC_DEBUG_SCSI(DFX "reset cmpl success\n",
-					fnic->fnic_no);
+			FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+				      "reset cmpl success\n");
 			/* Ready to send flogi out */
 			fnic->state = FNIC_IN_ETH_MODE;
 		} else {
-			FNIC_DEBUG_SCSI(DFX "fnic fw_reset : failed %s\n",
-					fnic->fnic_no,
-					fcpio_status_str[hdr_status]);
+			FNIC_SCSI_DBG(KERN_DEBUG,
+				      fnic->lport->host,
+				      "fnic fw_reset : failed %s\n",
+				      fnic_fcpio_status_to_str(hdr_status));
 
-			/* Unable to change to eth mode, cannot send out flogi
+			/*
+			 * Unable to change to eth mode, cannot send out flogi
 			 * Change state to fc mode, so that subsequent Flogi
 			 * requests from libFC will cause more attempts to
 			 * reset the firmware. Free the cached flogi
@@ -498,28 +519,26 @@ static int fnic_fcpio_fw_reset_cmpl_handler(struct fnic *fnic,
 			ret = -1;
 		}
 	} else {
-		FNIC_DEBUG_SCSI(DFX "Unexpected state %s while processing"
-				" reset cmpl\n", fnic->fnic_no,
-				fnic_state_str[fnic->state]);
+		FNIC_SCSI_DBG(KERN_DEBUG,
+			      fnic->lport->host,
+			      "Unexpected state %s while processing"
+			      " reset cmpl\n", fnic_state_to_str(fnic->state));
 		ret = -1;
 	}
 
-	/* Thread issuing host reset blocks till firmware reset is complete */
-	if (fnic->reset_wait)
-		complete(fnic->reset_wait);
-
 	/* Thread removing device blocks till firmware reset is complete */
 	if (fnic->remove_wait)
 		complete(fnic->remove_wait);
 
-	/* If fnic is in host reset or being removed, or fw reset failed
+	/*
+	 * If fnic is being removed, or fw reset failed
 	 * free the flogi frame. Else, send it out
 	 */
-	if (fnic->remove_wait || fnic->reset_wait || ret) {
+	if (fnic->remove_wait || ret) {
 		fnic->flogi_oxid = FC_XID_UNKNOWN;
 		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
 		if (flogi)
-			fnic_fc_frame_free_irq(flogi);
+			dev_kfree_skb_irq(fp_skb(flogi));
 		goto reset_cmpl_handler_end;
 	}
 
@@ -542,11 +561,10 @@ static int fnic_fcpio_flogi_reg_cmpl_handler(struct fnic *fnic,
 	u8 type;
 	u8 hdr_status;
 	struct fcpio_tag tag;
-	struct fnic_event *event;
-	u8 list_was_empty;
 	int ret = 0;
 	struct fc_frame *flogi_resp = NULL;
 	unsigned long flags;
+	struct sk_buff *skb;
 
 	fcpio_header_dec(&desc->hdr, &type, &hdr_status, &tag);
 
@@ -560,54 +578,45 @@ static int fnic_fcpio_flogi_reg_cmpl_handler(struct fnic *fnic,
 
 		/* Check flogi registration completion status */
 		if (!hdr_status) {
-			FNIC_DEBUG_SCSI(DFX "flog reg succeeded\n",
-					fnic->fnic_no);
+			FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+				      "flog reg succeeded\n");
 			fnic->state = FNIC_IN_FC_MODE;
 		} else {
-			FNIC_DEBUG_SCSI(DFX "fnic flogi reg :failed %s\n",
-					fnic->fnic_no,
-					fcpio_status_str[hdr_status]);
+			FNIC_SCSI_DBG(KERN_DEBUG,
+				      fnic->lport->host,
+				      "fnic flogi reg :failed %s\n",
+				      fnic_fcpio_status_to_str(hdr_status));
 			fnic->state = FNIC_IN_ETH_MODE;
 			ret = -1;
 		}
 	} else {
-		FNIC_DEBUG_SCSI(DFX "Unexpected fnic state %s while"
-				" processing flogi reg completion\n",
-				fnic->fnic_no, fnic_state_str[fnic->state]);
+		FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+			      "Unexpected fnic state %s while"
+			      " processing flogi reg completion\n",
+			      fnic_state_to_str(fnic->state));
 		ret = -1;
 	}
 
-	spin_unlock_irqrestore(&fnic->fnic_lock, flags);
-
 	/* Successful flogi reg cmpl, pass frame to LibFC */
 	if (!ret && flogi_resp) {
-		event = kmem_cache_alloc(fnic_ev_cache, GFP_ATOMIC);
-
-		if (!event) {
-			fnic_fc_frame_free_irq(flogi_resp);
-			ret = -1;
+		if (fnic->stop_rx_link_events) {
+			spin_unlock_irqrestore(&fnic->fnic_lock, flags);
 			goto reg_cmpl_handler_end;
 		}
+		skb = (struct sk_buff *)flogi_resp;
+		/* Use fr_flags to indicate whether flogi resp or not */
+		fr_flags(flogi_resp) = 1;
+		fr_dev(flogi_resp) = fnic->lport;
+		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
 
-		memset(event, 0, sizeof(struct fnic_event));
+		skb_queue_tail(&fnic->frame_queue, skb);
+		queue_work(fnic_event_queue, &fnic->frame_work);
 
-		/* initialize the event structure */
-		event->fp = flogi_resp;
-		fr_dev(flogi_resp) = fnic->lport;
-		event->fnic = fnic;
-		event->ev_type = EV_TYPE_FRAME;
-		event->is_flogi_resp_frame = 1;
-
-		/* insert it into event queue */
-		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);
-	} else
+	} else {
+		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
 		if (flogi_resp)
-			fnic_fc_frame_free_irq(flogi_resp);
+			dev_kfree_skb_irq(fp_skb(flogi_resp));
+	}
 
 reg_cmpl_handler_end:
 	return ret;
@@ -632,7 +641,8 @@ static inline int is_ack_index_in_range(struct vnic_wq_copy *wq,
 }
 
 
-/* Mark that ack received and store the Ack index. If there are multiple
+/*
+ * Mark that ack received and store the Ack index. If there are multiple
  * acks received before Tx thread cleans it up, the latest value will be
  * used which is correct behavior. This state should be in the copy Wq
  * instead of in the fnic
@@ -698,12 +708,13 @@ static void fnic_fcpio_icmnd_cmpl_handler(struct fnic *fnic,
 	/* firmware completed the io */
 	io_req->io_completed = 1;
 
-	/* if SCSI-ML has already issued abort on this command,
+	/*
+	 *  if SCSI-ML has already issued abort on this command,
 	 * ignore completion of the IO. The abts path will clean it up
 	 */
 	if (CMD_STATE(sc) == FNIC_IOREQ_ABTS_PENDING) {
 		spin_unlock_irqrestore(io_lock, flags);
-		goto icmnd_cmpl_handler_end;
+		return;
 	}
 
 	/* Mark the IO as complete */
@@ -714,31 +725,17 @@ static void fnic_fcpio_icmnd_cmpl_handler(struct fnic *fnic,
 	switch (hdr_status) {
 	case FCPIO_SUCCESS:
 		sc->result = (DID_OK << 16) | icmnd_cmpl->scsi_status;
-
 		xfer_len = scsi_bufflen(sc);
-		if (icmnd_cmpl->flags & FCPIO_ICMND_CMPL_RESID_UNDER) {
-			scsi_set_resid(sc, icmnd_cmpl->residual);
+		scsi_set_resid(sc, icmnd_cmpl->residual);
+
+		if (icmnd_cmpl->flags & FCPIO_ICMND_CMPL_RESID_UNDER)
 			xfer_len -= icmnd_cmpl->residual;
-			if (icmnd_cmpl->scsi_status) {
-				sc->result = (DID_ERROR << 16) |
-					     icmnd_cmpl->scsi_status;
-				xfer_len = 0;
-			}
-		}
-		if (icmnd_cmpl->flags & FCPIO_ICMND_CMPL_RESID_OVER) {
-			scsi_set_resid(sc, icmnd_cmpl->residual);
-			sc->result = (DID_ERROR << 16) |
-				icmnd_cmpl->scsi_status;
-			xfer_len = 0;
-		}
 
-		/* If queue_full, then try to reduce queue depth for all
-		 * LUNS on the target. Note, this should be accompanied
+		/*
+		 * If queue_full, then try to reduce queue depth for all
+		 * LUNS on the target. Todo: this should be accompanied
 		 * by a periodic queue_depth rampup based on successful
-		 * IO completion. Currently, however, LibFC does not have
-		 * state to keep track of when a remote port was last ramped
-		 * up. Once we keep track of that state in libFC, we can
-		 * ramp up the queue_depth in driver
+		 * IO completion.
 		 */
 		if (icmnd_cmpl->scsi_status == QUEUE_FULL) {
 			struct scsi_device *t_sdev;
@@ -754,13 +751,15 @@ static void fnic_fcpio_icmnd_cmpl_handler(struct fnic *fnic,
 						 t_sdev->queue_depth - 1);
 					if (qd == -1)
 						qd = t_sdev->host->cmd_per_lun;
-					printk(KERN_INFO DFX "scsi[%d:%d:%d:%d"
-					       "] queue full detected, new"
-					       " depth = %d\n", fnic->fnic_no,
-					       t_sdev->host->host_no,
-					       t_sdev->channel, t_sdev->id,
-					       t_sdev->lun,
-					       t_sdev->queue_depth);
+					shost_printk(KERN_INFO,
+						     fnic->lport->host,
+						     "scsi[%d:%d:%d:%d"
+						     "] queue full detected,"
+						     "new depth = %d\n",
+						     t_sdev->host->host_no,
+						     t_sdev->channel,
+						     t_sdev->id, t_sdev->lun,
+						     t_sdev->queue_depth);
 				}
 			}
 		}
@@ -771,7 +770,7 @@ static void fnic_fcpio_icmnd_cmpl_handler(struct fnic *fnic,
 		break;
 
 	case FCPIO_ABORTED:          /* request was aborted */
-		sc->result = (DID_ABORT << 16) | icmnd_cmpl->scsi_status;
+		sc->result = (DID_ERROR << 16) | icmnd_cmpl->scsi_status;
 		break;
 
 	case FCPIO_DATA_CNT_MISMATCH: /* recv/sent more/less data than exp. */
@@ -790,8 +789,8 @@ static void fnic_fcpio_icmnd_cmpl_handler(struct fnic *fnic,
 	case FCPIO_MSS_INVALID:      /* request was aborted due to mss error */
 	case FCPIO_FW_ERR:           /* request was terminated due fw error */
 	default:
-		printk(KERN_ERR DFX "hdr status = %s\n", fnic->fnic_no,
-		       fcpio_status_str[hdr_status]);
+		shost_printk(KERN_ERR, fnic->lport->host, "hdr status = %s\n",
+			     fnic_fcpio_status_to_str(hdr_status));
 		sc->result = (DID_ERROR << 16) | icmnd_cmpl->scsi_status;
 		break;
 	}
@@ -819,12 +818,9 @@ static void fnic_fcpio_icmnd_cmpl_handler(struct fnic *fnic,
 	if (sc->scsi_done)
 		sc->scsi_done(sc);
 
-icmnd_cmpl_handler_end:
-	return;
 }
 
 /* fnic_fcpio_itmf_cmpl_handler
- *
  * Routine to handle itmf completions
  */
 static void fnic_fcpio_itmf_cmpl_handler(struct fnic *fnic,
@@ -861,31 +857,61 @@ static void fnic_fcpio_itmf_cmpl_handler(struct fnic *fnic,
 
 	if (id & FNIC_TAG_ABORT) {
 		/* Completion of abort cmd */
-
+		if (CMD_STATE(sc) != FNIC_IOREQ_ABTS_PENDING) {
+			/* This is a late completion. Ignore it */
+			spin_unlock_irqrestore(io_lock, flags);
+			return;
+		}
 		CMD_STATE(sc) = FNIC_IOREQ_ABTS_COMPLETE;
 		CMD_ABTS_STATUS(sc) = 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)
+		FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+			      "abts cmpl recd. id %d status %s\n",
+			      (int)(id & FNIC_TAG_MASK),
+			      fnic_fcpio_status_to_str(hdr_status));
+
+		/*
+		 * If scsi_eh thread is blocked waiting for abts to complete,
+		 * signal completion to it. IO will be cleaned in the thread
+		 * else clean it in this context
+		 */
+		if (io_req->abts_done) {
 			complete(io_req->abts_done);
+			spin_unlock_irqrestore(io_lock, flags);
+		} else {
+			FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+				      "abts cmpl, completing IO\n");
+			CMD_SP(sc) = NULL;
+			sc->result = (DID_ERROR << 16);
+			scsi_host_end_tag(sc->device->host, sc);
+
+			spin_unlock_irqrestore(io_lock, flags);
+
+			fnic_release_ioreq_buf(fnic, io_req, sc);
+			mempool_free(io_req, fnic->io_req_pool);
+			if (sc->scsi_done)
+				sc->scsi_done(sc);
+		}
 
 	} else if (id & FNIC_TAG_DEV_RST) {
 		/* Completion of device reset */
 		CMD_LR_STATUS(sc) = hdr_status;
 		CMD_STATE(sc) = FNIC_IOREQ_CMD_COMPLETE;
-		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]);
+		FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+			      "dev reset cmpl recd. id %d status %s\n",
+			      (int)(id & FNIC_TAG_MASK),
+			      fnic_fcpio_status_to_str(hdr_status));
 		if (io_req->dr_done)
 			complete(io_req->dr_done);
+		spin_unlock_irqrestore(io_lock, flags);
+
+	} else {
+		shost_printk(KERN_ERR, fnic->lport->host,
+			     "Unexpected itmf io state %s tag %x\n",
+			     fnic_ioreq_state_to_str(CMD_STATE(sc)), id);
+		spin_unlock_irqrestore(io_lock, flags);
+	}
 
-	} 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);
 }
 
 /*
@@ -921,8 +947,9 @@ static int fnic_fcpio_cmpl_handler(struct vnic_dev *vdev,
 		break;
 
 	default:
-		FNIC_DEBUG_SCSI(DFX "firmware completion type %d\n",
-				fnic->fnic_no, desc->hdr.type);
+		FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+			      "firmware completion type %d\n",
+			      desc->hdr.type);
 		break;
 	}
 
@@ -978,7 +1005,8 @@ static void fnic_cleanup_io(struct fnic *fnic, int exclude_id)
 
 		spin_unlock_irqrestore(io_lock, flags);
 
-		/* If there is a scsi_cmnd associated with this io_req, then
+		/*
+		 * If there is a scsi_cmnd associated with this io_req, then
 		 * free the corresponding state
 		 */
 		fnic_release_ioreq_buf(fnic, io_req, sc);
@@ -986,9 +1014,9 @@ static void fnic_cleanup_io(struct fnic *fnic, int exclude_id)
 
 cleanup_scsi_cmd:
 		sc->result = DID_TRANSPORT_DISRUPTED << 16;
-		FNIC_DEBUG_SCSI(DFX "fnic_cleanup_io:"
-				" DID_TRANSPORT_DISRUPTED\n", fnic->fnic_no);
-		
+		FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host, "fnic_cleanup_io:"
+			      " DID_TRANSPORT_DISRUPTED\n");
+
 		/* Complete the command to SCSI */
 		if (sc->scsi_done)
 			sc->scsi_done(sc);
@@ -1039,8 +1067,8 @@ void fnic_wq_copy_cleanup_handler(struct vnic_wq_copy *wq,
 
 wq_copy_cleanup_scsi_cmd:
 	sc->result = DID_NO_CONNECT << 16;
-	FNIC_DEBUG_SCSI(DFX "wq_copy_cleanup_handler:"
-			" DID_NO_CONNECT\n", fnic->fnic_no);
+	FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host, "wq_copy_cleanup_handler:"
+		      " DID_NO_CONNECT\n");
 
 	if (sc->scsi_done)
 		sc->scsi_done(sc);
@@ -1048,7 +1076,7 @@ wq_copy_cleanup_scsi_cmd:
 
 static inline int fnic_queue_abort_io_req(struct fnic *fnic, int tag,
 					  u32 task_req, u8 *fc_lun,
-					  struct fc_rport *rport)
+					  struct fnic_io_req *io_req)
 {
 	struct vnic_wq_copy *wq = &fnic->wq_copy[0];
 	unsigned long flags;
@@ -1062,22 +1090,174 @@ static inline int fnic_queue_abort_io_req(struct fnic *fnic, int tag,
 		spin_unlock_irqrestore(&fnic->wq_copy_lock[0], flags);
 		return 1;
 	}
-	fnic_queue_wq_copy_desc_itmf(
-		wq,
-		tag | FNIC_TAG_ABORT,	/* host request id */
-		0,                      /* lun map id */
-		task_req,               /* task mgmt type */
-		tag,			/* id for aborted IO */
-		fc_lun,                 /* FCP lun addr */
-		rport->port_id,	        /* d_id */
-		fnic->config.ra_tov,    /* RATOV in msecs */
-		fnic->config.ed_tov     /* EDTOV in msecs */
-		);
+	fnic_queue_wq_copy_desc_itmf(wq, tag | FNIC_TAG_ABORT,
+				     0, task_req, tag, fc_lun, io_req->port_id,
+				     fnic->config.ra_tov, fnic->config.ed_tov);
 
 	spin_unlock_irqrestore(&fnic->wq_copy_lock[0], flags);
 	return 0;
 }
 
+void fnic_rport_exch_reset(struct fnic *fnic, u32 port_id)
+{
+	int tag;
+	struct fnic_io_req *io_req;
+	spinlock_t *io_lock;
+	unsigned long flags;
+	struct scsi_cmnd *sc;
+	struct scsi_lun fc_lun;
+	enum fnic_ioreq_state old_ioreq_state;
+
+	FNIC_SCSI_DBG(KERN_DEBUG,
+		      fnic->lport->host,
+		      "fnic_rport_reset_exch called portid 0x%06x\n",
+		      port_id);
+
+	if (fnic->in_remove)
+		return;
+
+	for (tag = 0; tag < FNIC_MAX_IO_REQ; tag++) {
+		sc = scsi_host_find_tag(fnic->lport->host, tag);
+		if (!sc)
+			continue;
+
+		io_lock = fnic_io_lock_hash(fnic, sc);
+		spin_lock_irqsave(io_lock, flags);
+
+		io_req = (struct fnic_io_req *)CMD_SP(sc);
+
+		if (!io_req || io_req->port_id != port_id) {
+			spin_unlock_irqrestore(io_lock, flags);
+			continue;
+		}
+
+		/*
+		 * Found IO that is still pending with firmware and
+		 * belongs to rport that went away
+		 */
+		if (CMD_STATE(sc) == FNIC_IOREQ_ABTS_PENDING) {
+			spin_unlock_irqrestore(io_lock, flags);
+			continue;
+		}
+		old_ioreq_state = CMD_STATE(sc);
+		CMD_STATE(sc) = FNIC_IOREQ_ABTS_PENDING;
+		CMD_ABTS_STATUS(sc) = FCPIO_INVALID_CODE;
+
+		BUG_ON(io_req->abts_done);
+
+		FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+			      "fnic_rport_reset_exch: Issuing abts\n");
+
+		spin_unlock_irqrestore(io_lock, flags);
+
+		/* Now queue the abort command to firmware */
+		int_to_scsilun(sc->device->lun, &fc_lun);
+
+		if (fnic_queue_abort_io_req(fnic, tag,
+					    FCPIO_ITMF_ABT_TASK_TERM,
+					    fc_lun.scsi_lun, io_req)) {
+			/*
+			 * Revert the cmd state back to old state, if
+			 * it hasnt changed in between. This cmd will get
+			 * aborted later by scsi_eh, or cleaned up during
+			 * lun reset
+			 */
+			io_lock = fnic_io_lock_hash(fnic, sc);
+
+			spin_lock_irqsave(io_lock, flags);
+			if (CMD_STATE(sc) == FNIC_IOREQ_ABTS_PENDING)
+				CMD_STATE(sc) = old_ioreq_state;
+			spin_unlock_irqrestore(io_lock, flags);
+		}
+	}
+
+}
+
+void fnic_terminate_rport_io(struct fc_rport *rport)
+{
+	int tag;
+	struct fnic_io_req *io_req;
+	spinlock_t *io_lock;
+	unsigned long flags;
+	struct scsi_cmnd *sc;
+	struct scsi_lun fc_lun;
+	struct fc_rport_libfc_priv *rdata = rport->dd_data;
+	struct fc_lport *lport = rdata->local_port;
+	struct fnic *fnic = lport_priv(lport);
+	struct fc_rport *cmd_rport;
+	enum fnic_ioreq_state old_ioreq_state;
+
+	FNIC_SCSI_DBG(KERN_DEBUG,
+		      fnic->lport->host, "fnic_terminate_rport_io called"
+		      " wwpn 0x%llx, wwnn0x%llx, portid 0x%06x\n",
+		      rport->port_name, rport->node_name,
+		      rport->port_id);
+
+	if (fnic->in_remove)
+		return;
+
+	for (tag = 0; tag < FNIC_MAX_IO_REQ; tag++) {
+		sc = scsi_host_find_tag(fnic->lport->host, tag);
+		if (!sc)
+			continue;
+
+		cmd_rport = starget_to_rport(scsi_target(sc->device));
+		if (rport != cmd_rport)
+			continue;
+
+		io_lock = fnic_io_lock_hash(fnic, sc);
+		spin_lock_irqsave(io_lock, flags);
+
+		io_req = (struct fnic_io_req *)CMD_SP(sc);
+
+		if (!io_req || rport != cmd_rport) {
+			spin_unlock_irqrestore(io_lock, flags);
+			continue;
+		}
+
+		/*
+		 * Found IO that is still pending with firmware and
+		 * belongs to rport that went away
+		 */
+		if (CMD_STATE(sc) == FNIC_IOREQ_ABTS_PENDING) {
+			spin_unlock_irqrestore(io_lock, flags);
+			continue;
+		}
+		old_ioreq_state = CMD_STATE(sc);
+		CMD_STATE(sc) = FNIC_IOREQ_ABTS_PENDING;
+		CMD_ABTS_STATUS(sc) = FCPIO_INVALID_CODE;
+
+		BUG_ON(io_req->abts_done);
+
+		FNIC_SCSI_DBG(KERN_DEBUG,
+			      fnic->lport->host,
+			      "fnic_terminate_rport_io: Issuing abts\n");
+
+		spin_unlock_irqrestore(io_lock, flags);
+
+		/* Now queue the abort command to firmware */
+		int_to_scsilun(sc->device->lun, &fc_lun);
+
+		if (fnic_queue_abort_io_req(fnic, tag,
+					    FCPIO_ITMF_ABT_TASK_TERM,
+					    fc_lun.scsi_lun, io_req)) {
+			/*
+			 * Revert the cmd state back to old state, if
+			 * it hasnt changed in between. This cmd will get
+			 * aborted later by scsi_eh, or cleaned up during
+			 * lun reset
+			 */
+			io_lock = fnic_io_lock_hash(fnic, sc);
+
+			spin_lock_irqsave(io_lock, flags);
+			if (CMD_STATE(sc) == FNIC_IOREQ_ABTS_PENDING)
+				CMD_STATE(sc) = old_ioreq_state;
+			spin_unlock_irqrestore(io_lock, flags);
+		}
+	}
+
+}
+
 static void fnic_block_error_handler(struct scsi_cmnd *sc)
 {
 	struct Scsi_Host *shost = sc->device->host;
@@ -1092,14 +1272,12 @@ static void fnic_block_error_handler(struct scsi_cmnd *sc)
 	}
 	spin_unlock_irqrestore(shost->host_lock, flags);
 
-	return;
 }
 
 /*
  * This function is exported to SCSI for sending abort cmnds.
  * A SCSI IO is represented by a io_req in the driver.
  * The ioreq is linked to the SCSI Cmd, thus a link with the ULP's IO.
- *
  */
 int fnic_abort_cmd(struct scsi_cmnd *sc)
 {
@@ -1121,12 +1299,13 @@ int fnic_abort_cmd(struct scsi_cmnd *sc)
 	lp = shost_priv(sc->device->host);
 
 	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));
+	FNIC_SCSI_DBG(KERN_DEBUG,
+		      fnic->lport->host,
+		      "Abort Cmd called FCID 0x%x, LUN 0x%x TAG %d\n",
+		      (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)) {
+	if (lp->state != LPORT_ST_READY || !(lp->link_up)) {
 		ret = FAILED;
 		goto fnic_abort_cmd_end;
 	}
@@ -1146,24 +1325,30 @@ int fnic_abort_cmd(struct scsi_cmnd *sc)
 	io_lock = fnic_io_lock_hash(fnic, sc);
 	spin_lock_irqsave(io_lock, flags);
 	io_req = (struct fnic_io_req *)CMD_SP(sc);
-
 	if (!io_req) {
 		spin_unlock_irqrestore(io_lock, flags);
 		goto fnic_abort_cmd_end;
 	}
 
-	/* Command is still pending, need to abort it
+	io_req->abts_done = &tm_done;
+
+	if (CMD_STATE(sc) == FNIC_IOREQ_ABTS_PENDING) {
+		spin_unlock_irqrestore(io_lock, flags);
+		goto wait_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
 	 * has already started.
 	 */
 	CMD_STATE(sc) = FNIC_IOREQ_ABTS_PENDING;
-
-	io_req->abts_done = &tm_done;
+	CMD_ABTS_STATUS(sc) = FCPIO_INVALID_CODE;
 
 	spin_unlock_irqrestore(io_lock, flags);
 
-	/* Check readiness of the remote port. If the path to remote
+	/*
+	 * Check readiness of the remote port. If the path to remote
 	 * port is up, then send abts to the remote port to terminate
 	 * the IO. Else, just locally terminate the IO in the firmware
 	 */
@@ -1177,7 +1362,7 @@ int fnic_abort_cmd(struct scsi_cmnd *sc)
 	int_to_scsilun(sc->device->lun, &fc_lun);
 
 	if (fnic_queue_abort_io_req(fnic, scsi_cmd_get_tag(sc), task_req,
-				    fc_lun.scsi_lun, rport)) {
+				    fc_lun.scsi_lun, io_req)) {
 		spin_lock_irqsave(io_lock, flags);
 		io_req = (struct fnic_io_req *)CMD_SP(sc);
 		if (io_req)
@@ -1187,10 +1372,12 @@ int fnic_abort_cmd(struct scsi_cmnd *sc)
 		goto fnic_abort_cmd_end;
 	}
 
-	/* We queued an abort IO, wait for its completion.
+	/*
+	 * We queued an abort IO, wait for its completion.
 	 * Once the firmware completes the abort command, it will
 	 * wake up this thread.
 	 */
+ wait_pending:
 	wait_for_completion_timeout(&tm_done,
 				    msecs_to_jiffies
 				    (2 * fnic->config.ra_tov +
@@ -1214,7 +1401,8 @@ int fnic_abort_cmd(struct scsi_cmnd *sc)
 		goto fnic_abort_cmd_end;
 	}
 
-	/* firmware completed the abort, check the status,
+	/*
+	 * firmware completed the abort, check the status,
 	 * free the io_req irrespective of failure or success
 	 */
 	if (CMD_ABTS_STATUS(sc) != FCPIO_SUCCESS)
@@ -1229,17 +1417,18 @@ int fnic_abort_cmd(struct scsi_cmnd *sc)
 	mempool_free(io_req, fnic->io_req_pool);
 
 fnic_abort_cmd_end:
-	FNIC_DEBUG_SCSI(DFX "Returning from abort cmd %s\n",
-			fnic->fnic_no, (ret == SUCCESS) ?
-			"SUCCESS" : "FAILED");
+	FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+		      "Returning from abort cmd %s\n",
+		      (ret == SUCCESS) ?
+		      "SUCCESS" : "FAILED");
 	return ret;
 }
 
 static inline int fnic_queue_dr_io_req(struct fnic *fnic,
-				       struct scsi_cmnd *sc)
+				       struct scsi_cmnd *sc,
+				       struct fnic_io_req *io_req)
 {
 	struct vnic_wq_copy *wq = &fnic->wq_copy[0];
-	struct fc_rport *rport;
 	struct scsi_lun fc_lun;
 	int ret = 0;
 	unsigned long intr_flags;
@@ -1256,19 +1445,12 @@ static inline int fnic_queue_dr_io_req(struct fnic *fnic,
 
 	/* fill in the lun info */
 	int_to_scsilun(sc->device->lun, &fc_lun);
-	rport = starget_to_rport(scsi_target(sc->device));
 
-	fnic_queue_wq_copy_desc_itmf(
-		wq,
-		scsi_cmd_get_tag(sc) | FNIC_TAG_DEV_RST, /* host request id */
-		0,                      /* lun map id */
-		FCPIO_ITMF_LUN_RESET,   /* logical unit reset task mgmt */
-		SCSI_NO_TAG,		/* id not specified */
-		fc_lun.scsi_lun,        /* FCP 8 byte lun addr */
-		rport->port_id,		/* d_id */
-		fnic->config.ra_tov,    /* RATOV in msecs */
-		fnic->config.ed_tov     /* EDTOV in msecs */
-		);
+	fnic_queue_wq_copy_desc_itmf(wq,
+				     scsi_cmd_get_tag(sc) | FNIC_TAG_DEV_RST,
+				     0, FCPIO_ITMF_LUN_RESET, SCSI_NO_TAG,
+				     fc_lun.scsi_lun, io_req->port_id,
+				     fnic->config.ra_tov, fnic->config.ed_tov);
 
 lr_io_req_end:
 	spin_unlock_irqrestore(&fnic->wq_copy_lock[0], intr_flags);
@@ -1298,7 +1480,8 @@ static int fnic_clean_pending_aborts(struct fnic *fnic,
 
 	for (tag = 0; tag < FNIC_MAX_IO_REQ; tag++) {
 		sc = scsi_host_find_tag(fnic->lport->host, tag);
-		/* ignore this lun reset cmd or cmds that do not belong to
+		/*
+		 * ignore this lun reset cmd or cmds that do not belong to
 		 * this lun
 		 */
 		if (!sc || sc == lr_sc || sc->device != lun_dev)
@@ -1314,13 +1497,17 @@ static int fnic_clean_pending_aborts(struct fnic *fnic,
 			continue;
 		}
 
-		/* Found IO that is still pending with firmware and
+		/*
+		 * Found IO that is still pending with firmware and
 		 * belongs to the LUN that we are resetting
 		 */
-		FNIC_DEBUG_SCSI(DFX "Found IO in %s on lun\n",
-				fnic->fnic_no,
-				fnic_ioreq_state_str[CMD_STATE(sc)]);
+		FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+			      "Found IO in %s on lun\n",
+			      fnic_ioreq_state_to_str(CMD_STATE(sc)));
 
+		BUG_ON(CMD_STATE(sc) != FNIC_IOREQ_ABTS_PENDING);
+
+		CMD_ABTS_STATUS(sc) = FCPIO_INVALID_CODE;
 		io_req->abts_done = &tm_done;
 		spin_unlock_irqrestore(io_lock, flags);
 
@@ -1330,7 +1517,7 @@ static int fnic_clean_pending_aborts(struct fnic *fnic,
 
 		if (fnic_queue_abort_io_req(fnic, tag,
 					    FCPIO_ITMF_ABT_TASK_TERM,
-					    fc_lun.scsi_lun, rport)) {
+					    fc_lun.scsi_lun, io_req)) {
 			spin_lock_irqsave(io_lock, flags);
 			io_req = (struct fnic_io_req *)CMD_SP(sc);
 			if (io_req)
@@ -1373,7 +1560,8 @@ clean_pending_aborts_end:
 	return ret;
 }
 
-/* SCSI Eh thread issues a Lun Reset when one or more commands on a LUN
+/*
+ * SCSI Eh thread issues a Lun Reset when one or more commands on a LUN
  * fail to get aborted. It calls driver's eh_device_reset with a SCSI command
  * on the LUN.
  */
@@ -1397,13 +1585,14 @@ int fnic_device_reset(struct scsi_cmnd *sc)
 	lp = shost_priv(sc->device->host);
 
 	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);
+	FNIC_SCSI_DBG(KERN_DEBUG,
+		      fnic->lport->host,
+		      "Device reset called FCID 0x%x, LUN 0x%x\n",
+		      (starget_to_rport(scsi_target(sc->device)))->port_id,
+		      sc->device->lun);
 
 
-	if (lp->state != LPORT_ST_READY || !(lp->link_status & FC_LINK_UP))
+	if (lp->state != LPORT_ST_READY || !(lp->link_up))
 		goto fnic_device_reset_end;
 
 	/* Check if remote port up */
@@ -1433,26 +1622,27 @@ int fnic_device_reset(struct scsi_cmnd *sc)
 			goto fnic_device_reset_end;
 		}
 		memset(io_req, 0, sizeof(*io_req));
-		CMD_STATE(sc) = FNIC_IOREQ_CMD_PENDING;
+		io_req->port_id = rport->port_id;
 		CMD_SP(sc) = (char *)io_req;
 	}
 	io_req->dr_done = &tm_done;
+	CMD_STATE(sc) = FNIC_IOREQ_CMD_PENDING;
 	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));
+	FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host, "TAG %d\n",
+		      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
+	/*
+	 * issue the device reset, if enqueue failed, clean up the ioreq
+	 * and break assoc with scsi cmd
 	 */
-	if (fnic_queue_dr_io_req(fnic, sc)) {
+	if (fnic_queue_dr_io_req(fnic, sc, io_req)) {
 		spin_lock_irqsave(io_lock, flags);
 		io_req = (struct fnic_io_req *)CMD_SP(sc);
 		if (io_req)
 			io_req->dr_done = NULL;
-		spin_unlock_irqrestore(io_lock, flags);
-		goto fnic_device_reset_end;
+		goto fnic_device_reset_clean;
 	}
 
 	/*
@@ -1473,56 +1663,67 @@ int fnic_device_reset(struct scsi_cmnd *sc)
 	status = CMD_LR_STATUS(sc);
 	spin_unlock_irqrestore(io_lock, flags);
 
-	/* If lun reset not completed, bail out with failed. io_req
+	/*
+	 * If lun reset not completed, bail out with failed. io_req
 	 * gets cleaned up during higher levels of EH
 	 */
 	if (status == FCPIO_INVALID_CODE) {
-		FNIC_DEBUG_SCSI(DFX "Device reset timed out\n",
-				fnic->fnic_no);
+		FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+			      "Device reset timed out\n");
 		goto fnic_device_reset_end;
 	}
 
-	/* Completed, but not successful */
+	/* Completed, but not successful, clean up the io_req, return fail */
 	if (status != FCPIO_SUCCESS) {
-		FNIC_DEBUG_SCSI(DFX "Device reset completed - failed\n",
-				fnic->fnic_no);
-		goto fnic_device_reset_end;
+		spin_lock_irqsave(io_lock, flags);
+		FNIC_SCSI_DBG(KERN_DEBUG,
+			      fnic->lport->host,
+			      "Device reset completed - failed\n");
+		io_req = (struct fnic_io_req *)CMD_SP(sc);
+		goto fnic_device_reset_clean;
 	}
 
-	/* Clean up any aborts on this lun that have still not
+	/*
+	 * Clean up any aborts on this lun that have still not
 	 * completed. If any of these fail, then LUN reset fails.
 	 * clean_pending_aborts cleans all cmds on this lun except
-	 * the lun reset cmd. If all cmds get cleaned, then clean
-	 * lun reset, otherswise fail lun reset, and higher levels
-	 * of EH will kick in
+	 * the lun reset cmd. If all cmds get cleaned, the lun reset
+	 * succeeds
 	 */
 	if (fnic_clean_pending_aborts(fnic, sc)) {
-		FNIC_DEBUG_SCSI(DFX "Device reset failed"
-				" since could not abort all IOs\n",
-				fnic->fnic_no);
-		goto fnic_device_reset_end;
+		spin_lock_irqsave(io_lock, flags);
+		io_req = (struct fnic_io_req *)CMD_SP(sc);
+		FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+			      "Device reset failed"
+			      " since could not abort all IOs\n");
+		goto fnic_device_reset_clean;
 	}
 
 	/* Clean lun reset command */
 	spin_lock_irqsave(io_lock, flags);
 	io_req = (struct fnic_io_req *)CMD_SP(sc);
-	CMD_SP(sc) = NULL;
-	scsi_host_end_tag(sc->device->host, sc);
-	spin_unlock_irqrestore(io_lock, flags);
+	if (io_req)
+		/* Completed, and successful */
+		ret = SUCCESS;
 
-	if (!io_req)
-		goto fnic_device_reset_end;
+fnic_device_reset_clean:
+	if (io_req) {
+		CMD_SP(sc) = NULL;
+		scsi_host_end_tag(sc->device->host, sc);
+	}
 
-	fnic_release_ioreq_buf(fnic, io_req, sc);
-	mempool_free(io_req, fnic->io_req_pool);
+	spin_unlock_irqrestore(io_lock, flags);
 
-	/* Completed, and successful */
-	ret = SUCCESS;
+	if (io_req) {
+		fnic_release_ioreq_buf(fnic, io_req, sc);
+		mempool_free(io_req, fnic->io_req_pool);
+	}
 
 fnic_device_reset_end:
-	FNIC_DEBUG_SCSI(DFX "Returning from device reset %s\n",
-			fnic->fnic_no, (ret == SUCCESS) ?
-			"SUCCESS" : "FAILED");
+	FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+		      "Returning from device reset %s\n",
+		      (ret == SUCCESS) ?
+		      "SUCCESS" : "FAILED");
 	return ret;
 }
 
@@ -1536,22 +1737,26 @@ int fnic_reset(struct Scsi_Host *shost)
 	lp = shost_priv(shost);
 	fnic = lport_priv(lp);
 
-	FNIC_DEBUG_SCSI(DFX "fnic_reset called\n", fnic->fnic_no);
+	FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+		      "fnic_reset called\n");
 
-	/* 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_DEBUG_SCSI(DFX "Returning from fnic reset %s\n",
-			fnic->fnic_no, (ret == SUCCESS) ?
-			"SUCCESS" : "FAILED");
+	FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+		      "Returning from fnic reset %s\n",
+		      (ret == SUCCESS) ?
+		      "SUCCESS" : "FAILED");
 
 	return ret;
 }
 
-/* SCSI Error handling calls driver's eh_host_reset if all prior
+/*
+ * SCSI Error handling calls driver's eh_host_reset if all prior
  * error handling levels return FAILED. If host reset completes
  * successfully, and if link is up, then Fabric login begins.
  *
@@ -1572,13 +1777,12 @@ int fnic_host_reset(struct scsi_cmnd *sc)
 	 * successful, so before returning to scsi, fabric should be up
 	 */
 	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)) {
+			    (lp->link_up)) {
 				ret = SUCCESS;
 				break;
 			}
@@ -1603,7 +1807,6 @@ void fnic_scsi_abort_io(struct fc_lport *lp)
 	/* Issue firmware reset for fnic, wait for reset to complete */
 	spin_lock_irqsave(&fnic->fnic_lock, flags);
 	fnic->remove_wait = &remove_wait;
-	fnic->in_remove = 1;
 	old_state = fnic->state;
 	fnic->state = FNIC_IN_FC_TRANS_ETH_MODE;
 	vnic_dev_del_addr(fnic->vdev, fnic->data_src_addr);
@@ -1616,7 +1819,7 @@ void fnic_scsi_abort_io(struct fc_lport *lp)
 			fnic->state = old_state;
 		fnic->remove_wait = NULL;
 		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
-		goto abort_io_end;
+		return;
 	}
 
 	/* Wait for firmware reset to complete */
@@ -1625,13 +1828,12 @@ void fnic_scsi_abort_io(struct fc_lport *lp)
 
 	spin_lock_irqsave(&fnic->fnic_lock, flags);
 	fnic->remove_wait = NULL;
-	FNIC_DEBUG_SCSI(DFX "fnic_scsi_abort_io %s\n", fnic->fnic_no,
-			(fnic->state == FNIC_IN_ETH_MODE) ?
-			"SUCCESS" : "FAILED");
+	FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+		      "fnic_scsi_abort_io %s\n",
+		      (fnic->state == FNIC_IN_ETH_MODE) ?
+		      "SUCCESS" : "FAILED");
 	spin_unlock_irqrestore(&fnic->fnic_lock, flags);
 
-abort_io_end:
-	return;
 }
 
 /*
@@ -1658,3 +1860,35 @@ void fnic_scsi_cleanup(struct fc_lport *lp)
 	}
 
 }
+
+void fnic_empty_scsi_cleanup(struct fc_lport *lp)
+{
+}
+
+void fnic_exch_mgr_reset(struct fc_lport *lp, u32 sid, u32 did)
+{
+	struct fnic *fnic = lport_priv(lp);
+
+	/* Non-zero sid, nothing to do */
+	if (sid)
+		goto call_fc_exch_mgr_reset;
+
+	if (did) {
+		fnic_rport_exch_reset(fnic, did);
+		goto call_fc_exch_mgr_reset;
+	}
+
+	/*
+	 * sid = 0, did = 0
+	 * link down or device being removed
+	 */
+	if (!fnic->in_remove)
+		fnic_scsi_cleanup(lp);
+	else
+		fnic_scsi_abort_io(lp);
+
+	/* call libFC exch mgr reset to reset its exchanges */
+call_fc_exch_mgr_reset:
+	fc_exch_mgr_reset(lp, sid, did);
+
+}
diff --git a/drivers/scsi/fnic/vnic_cq.c b/drivers/scsi/fnic/vnic_cq.c
index ac6ace3..c5db32e 100644
--- a/drivers/scsi/fnic/vnic_cq.c
+++ b/drivers/scsi/fnic/vnic_cq.c
@@ -15,7 +15,6 @@
  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
-#include <linux/kernel.h>
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/pci.h>
diff --git a/drivers/scsi/fnic/vnic_cq.h b/drivers/scsi/fnic/vnic_cq.h
index 2fcfd90..4ede680 100644
--- a/drivers/scsi/fnic/vnic_cq.h
+++ b/drivers/scsi/fnic/vnic_cq.h
@@ -21,6 +21,16 @@
 #include "cq_desc.h"
 #include "vnic_dev.h"
 
+/*
+ * These defines avoid symbol clash between fnic and enic (Cisco 10G Eth
+ * Driver) when both are built with CONFIG options =y
+ */
+#define vnic_cq_service fnic_cq_service
+#define vnic_cq_free fnic_cq_free
+#define vnic_cq_alloc fnic_cq_alloc
+#define vnic_cq_init fnic_cq_init
+#define vnic_cq_clean fnic_cq_clean
+
 /* Completion queue control */
 struct vnic_cq_ctrl {
 	u64 ring_base;			/* 0x00 */
diff --git a/drivers/scsi/fnic/vnic_dev.c b/drivers/scsi/fnic/vnic_dev.c
index 37cbd13..5667706 100644
--- a/drivers/scsi/fnic/vnic_dev.c
+++ b/drivers/scsi/fnic/vnic_dev.c
@@ -667,7 +667,7 @@ struct vnic_dev *vnic_dev_register(struct vnic_dev *vdev,
 	void *priv, struct pci_dev *pdev, struct vnic_dev_bar *bar)
 {
 	if (!vdev) {
-		vdev = kzalloc(sizeof(struct vnic_dev), GFP_ATOMIC);
+		vdev = kzalloc(sizeof(struct vnic_dev), GFP_KERNEL);
 		if (!vdev)
 			return NULL;
 	}
diff --git a/drivers/scsi/fnic/vnic_dev.h b/drivers/scsi/fnic/vnic_dev.h
index 3ad9ac9..f9935a8 100644
--- a/drivers/scsi/fnic/vnic_dev.h
+++ b/drivers/scsi/fnic/vnic_dev.h
@@ -21,10 +21,64 @@
 #include "vnic_resource.h"
 #include "vnic_devcmd.h"
 
+/*
+ * These defines avoid symbol clash between fnic and enic (Cisco 10G Eth
+ * Driver) when both are built with CONFIG options =y
+ */
+#define vnic_dev_priv fnic_dev_priv
+#define vnic_dev_get_res_count fnic_dev_get_res_count
+#define vnic_dev_get_res fnic_dev_get_res
+#define vnic_dev_desc_ring_size fnic_dev_desc_ring_siz
+#define vnic_dev_clear_desc_ring fnic_dev_clear_desc_ring
+#define vnic_dev_alloc_desc_ring fnic_dev_alloc_desc_ring
+#define vnic_dev_free_desc_ring fnic_dev_free_desc_ring
+#define vnic_dev_cmd fnic_dev_cmd
+#define vnic_dev_fw_info fnic_dev_fw_info
+#define vnic_dev_spec fnic_dev_spec
+#define vnic_dev_stats_clear fnic_dev_stats_clear
+#define vnic_dev_stats_dump fnic_dev_stats_dump
+#define vnic_dev_hang_notify fnic_dev_hang_notify
+#define vnic_dev_packet_filter fnic_dev_packet_filter
+#define vnic_dev_add_addr fnic_dev_add_addr
+#define vnic_dev_del_addr fnic_dev_del_addr
+#define vnic_dev_mac_addr fnic_dev_mac_addr
+#define vnic_dev_notify_set fnic_dev_notify_set
+#define vnic_dev_notify_unset fnic_dev_notify_unset
+#define vnic_dev_link_status fnic_dev_link_status
+#define vnic_dev_port_speed fnic_dev_port_speed
+#define vnic_dev_msg_lvl fnic_dev_msg_lvl
+#define vnic_dev_mtu fnic_dev_mtu
+#define vnic_dev_link_down_cnt fnic_dev_link_down_cnt
+#define vnic_dev_close fnic_dev_close
+#define vnic_dev_enable fnic_dev_enable
+#define vnic_dev_disable fnic_dev_disable
+#define vnic_dev_open fnic_dev_open
+#define vnic_dev_open_done fnic_dev_open_done
+#define vnic_dev_init fnic_dev_init
+#define vnic_dev_soft_reset fnic_dev_soft_reset
+#define vnic_dev_soft_reset_done fnic_dev_soft_reset_done
+#define vnic_dev_set_intr_mode fnic_dev_set_intr_mode
+#define vnic_dev_get_intr_mode fnic_dev_get_intr_mode
+#define vnic_dev_unregister fnic_dev_unregister
+#define vnic_dev_register fnic_dev_register
+
 #ifndef VNIC_PADDR_TARGET
 #define VNIC_PADDR_TARGET	0x0000000000000000ULL
 #endif
 
+#ifndef readq
+static inline u64 readq(void __iomem *reg)
+{
+	return ((u64)readl(reg + 0x4UL) << 32) | (u64)readl(reg);
+}
+
+static inline void writeq(u64 val, void __iomem *reg)
+{
+	writel(val & 0xffffffff, reg);
+	writel(val >> 32, reg + 0x4UL);
+}
+#endif
+
 enum vnic_dev_intr_mode {
 	VNIC_DEV_INTR_MODE_UNKNOWN,
 	VNIC_DEV_INTR_MODE_INTX,
diff --git a/drivers/scsi/fnic/vnic_intr.h b/drivers/scsi/fnic/vnic_intr.h
index b53ef8b..d5fb40e 100644
--- a/drivers/scsi/fnic/vnic_intr.h
+++ b/drivers/scsi/fnic/vnic_intr.h
@@ -21,6 +21,21 @@
 #include <linux/pci.h>
 #include "vnic_dev.h"
 
+/*
+ * These defines avoid symbol clash between fnic and enic (Cisco 10G Eth
+ * Driver) when both are built with CONFIG options =y
+ */
+#define vnic_intr_unmask fnic_intr_unmask
+#define vnic_intr_mask fnic_intr_mask
+#define vnic_intr_return_credits fnic_intr_return_credits
+#define vnic_intr_credits fnic_intr_credits
+#define vnic_intr_return_all_credits fnic_intr_return_all_credits
+#define vnic_intr_legacy_pba fnic_intr_legacy_pba
+#define vnic_intr_free fnic_intr_free
+#define vnic_intr_alloc fnic_intr_alloc
+#define vnic_intr_init fnic_intr_init
+#define vnic_intr_clean fnic_intr_clean
+
 #define VNIC_INTR_TIMER_MAX		0xffff
 
 #define VNIC_INTR_TIMER_TYPE_ABS	0
@@ -73,6 +88,20 @@ static inline void vnic_intr_return_credits(struct vnic_intr *intr,
 	iowrite32(int_credit_return, &intr->ctrl->int_credit_return);
 }
 
+static inline unsigned int vnic_intr_credits(struct vnic_intr *intr)
+{
+	return ioread32(&intr->ctrl->int_credits);
+}
+
+static inline void vnic_intr_return_all_credits(struct vnic_intr *intr)
+{
+	unsigned int credits = vnic_intr_credits(intr);
+	int unmask = 1;
+	int reset_timer = 1;
+
+	vnic_intr_return_credits(intr, credits, unmask, reset_timer);
+}
+
 static inline u32 vnic_intr_legacy_pba(u32 __iomem *legacy_pba)
 {
 	/* read PBA without clearing */
diff --git a/drivers/scsi/fnic/vnic_nic.h b/drivers/scsi/fnic/vnic_nic.h
index e3ac365..f15b83e 100644
--- a/drivers/scsi/fnic/vnic_nic.h
+++ b/drivers/scsi/fnic/vnic_nic.h
@@ -18,6 +18,12 @@
 #ifndef _VNIC_NIC_H_
 #define _VNIC_NIC_H_
 
+/*
+ * These defines avoid symbol clash between fnic and enic (Cisco 10G Eth
+ * Driver) when both are built with CONFIG options =y
+ */
+#define vnic_set_nic_cfg fnic_set_nic_cfg
+
 #define NIC_CFG_RSS_DEFAULT_CPU_MASK_FIELD	0xffUL
 #define NIC_CFG_RSS_DEFAULT_CPU_SHIFT		0
 #define NIC_CFG_RSS_HASH_TYPE			(0xffUL << 8)
diff --git a/drivers/scsi/fnic/vnic_rq.c b/drivers/scsi/fnic/vnic_rq.c
index 45e5cd6..bedd0d2 100644
--- a/drivers/scsi/fnic/vnic_rq.c
+++ b/drivers/scsi/fnic/vnic_rq.c
@@ -16,7 +16,6 @@
  * SOFTWARE.
  */
 
-#include <linux/kernel.h>
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/pci.h>
diff --git a/drivers/scsi/fnic/vnic_rq.h b/drivers/scsi/fnic/vnic_rq.h
index 230ae2d..aebdfbd 100644
--- a/drivers/scsi/fnic/vnic_rq.h
+++ b/drivers/scsi/fnic/vnic_rq.h
@@ -22,6 +22,28 @@
 #include "vnic_dev.h"
 #include "vnic_cq.h"
 
+/*
+ * These defines avoid symbol clash between fnic and enic (Cisco 10G Eth
+ * Driver) when both are built with CONFIG options =y
+ */
+#define vnic_rq_desc_avail fnic_rq_desc_avail
+#define vnic_rq_desc_used fnic_rq_desc_used
+#define vnic_rq_next_desc fnic_rq_next_desc
+#define vnic_rq_next_index fnic_rq_next_index
+#define vnic_rq_next_buf_index fnic_rq_next_buf_index
+#define vnic_rq_post fnic_rq_post
+#define vnic_rq_posting_soon fnic_rq_posting_soon
+#define vnic_rq_return_descs fnic_rq_return_descs
+#define vnic_rq_service fnic_rq_service
+#define vnic_rq_fill fnic_rq_fill
+#define vnic_rq_free fnic_rq_free
+#define vnic_rq_alloc fnic_rq_alloc
+#define vnic_rq_init fnic_rq_init
+#define vnic_rq_error_status fnic_rq_error_status
+#define vnic_rq_enable fnic_rq_enable
+#define vnic_rq_disable fnic_rq_disable
+#define vnic_rq_clean fnic_rq_clean
+
 /* Receive queue control */
 struct vnic_rq_ctrl {
 	u64 ring_base;			/* 0x00 */
diff --git a/drivers/scsi/fnic/vnic_scsi.h b/drivers/scsi/fnic/vnic_scsi.h
index 721ca92..46baa52 100644
--- a/drivers/scsi/fnic/vnic_scsi.h
+++ b/drivers/scsi/fnic/vnic_scsi.h
@@ -67,10 +67,10 @@
 #define VNIC_FNIC_PORT_DOWN_IO_RETRIES_MAX  255
 
 #define VNIC_FNIC_LUNS_PER_TARGET_MIN       1
-#define VNIC_FNIC_LUNS_PER_TARGET_MAX       256
+#define VNIC_FNIC_LUNS_PER_TARGET_MAX       1024
 
 /* Device-specific region: scsi configuration */
-struct vnic_scsi_config {
+struct vnic_fc_config {
 	u64 node_wwn;
 	u64 port_wwn;
 	u32 flags;
diff --git a/drivers/scsi/fnic/vnic_wq.c b/drivers/scsi/fnic/vnic_wq.c
index 6e823b8..1f9ea79 100644
--- a/drivers/scsi/fnic/vnic_wq.c
+++ b/drivers/scsi/fnic/vnic_wq.c
@@ -16,7 +16,6 @@
  * SOFTWARE.
  */
 
-#include <linux/kernel.h>
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/pci.h>
diff --git a/drivers/scsi/fnic/vnic_wq.h b/drivers/scsi/fnic/vnic_wq.h
index 14313df..5cd094f 100644
--- a/drivers/scsi/fnic/vnic_wq.h
+++ b/drivers/scsi/fnic/vnic_wq.h
@@ -22,6 +22,23 @@
 #include "vnic_dev.h"
 #include "vnic_cq.h"
 
+/*
+ * These defines avoid symbol clash between fnic and enic (Cisco 10G Eth
+ * Driver) when both are built with CONFIG options =y
+ */
+#define vnic_wq_desc_avail fnic_wq_desc_avail
+#define vnic_wq_desc_used fnic_wq_desc_used
+#define vnic_wq_next_desc fni_cwq_next_desc
+#define vnic_wq_post fnic_wq_post
+#define vnic_wq_service fnic_wq_service
+#define vnic_wq_free fnic_wq_free
+#define vnic_wq_alloc fnic_wq_alloc
+#define vnic_wq_init fnic_wq_init
+#define vnic_wq_error_status fnic_wq_error_status
+#define vnic_wq_enable fnic_wq_enable
+#define vnic_wq_disable fnic_wq_disable
+#define vnic_wq_clean fnic_wq_clean
+
 /* Work queue control */
 struct vnic_wq_ctrl {
 	u64 ring_base;			/* 0x00 */
diff --git a/drivers/scsi/fnic/vnic_wq_copy.c b/drivers/scsi/fnic/vnic_wq_copy.c
index 62f2353..9eab7e7 100644
--- a/drivers/scsi/fnic/vnic_wq_copy.c
+++ b/drivers/scsi/fnic/vnic_wq_copy.c
@@ -16,7 +16,6 @@
  * SOFTWARE.
  */
 
-#include <linux/kernel.h>
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/pci.h>
diff --git a/drivers/scsi/fnic/vnic_wq_copy.h b/drivers/scsi/fnic/vnic_wq_copy.h
index c292932..6aff974 100644
--- a/drivers/scsi/fnic/vnic_wq_copy.h
+++ b/drivers/scsi/fnic/vnic_wq_copy.h
@@ -18,7 +18,6 @@
 #ifndef _VNIC_WQ_COPY_H_
 #define _VNIC_WQ_COPY_H_
 
-#include <linux/kernel.h>
 #include <linux/pci.h>
 #include "vnic_wq.h"
 #include "fcpio.h"