Merge git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi-misc-2.6

* git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi-misc-2.6: (102 commits)
  [SCSI] scsi_dh: fix kconfig related build errors
  [SCSI] sym53c8xx: Fix bogus sym_que_entry re-implementation of container_of
  [SCSI] scsi_cmnd.h: remove double inclusion of linux/blkdev.h
  [SCSI] make struct scsi_{host,target}_type static
  [SCSI] fix locking in host use of blk_plug_device()
  [SCSI] zfcp: Cleanup external header file
  [SCSI] zfcp: Cleanup code in zfcp_erp.c
  [SCSI] zfcp: zfcp_fsf cleanup.
  [SCSI] zfcp: consolidate sysfs things into one file.
  [SCSI] zfcp: Cleanup of code in zfcp_aux.c
  [SCSI] zfcp: Cleanup of code in zfcp_scsi.c
  [SCSI] zfcp: Move status accessors from zfcp to SCSI include file.
  [SCSI] zfcp: Small QDIO cleanups
  [SCSI] zfcp: Adapter reopen for large number of unsolicited status
  [SCSI] zfcp: Fix error checking for ELS ADISC requests
  [SCSI] zfcp: wait until adapter is finished with ERP during auto-port
  [SCSI] ibmvfc: IBM Power Virtual Fibre Channel Adapter Client Driver
  [SCSI] sg: Add target reset support
  [SCSI] lib: Add support for the T10 (SCSI) Data Integrity Field CRC
  [SCSI] sd: Move scsi_disk() accessor function to sd.h
  ...
diff --git a/Documentation/scsi/aacraid.txt b/Documentation/scsi/aacraid.txt
index d16011a..709ca99 100644
--- a/Documentation/scsi/aacraid.txt
+++ b/Documentation/scsi/aacraid.txt
@@ -56,19 +56,33 @@
 	9005:0285:9005:02d1	Adaptec	5405 (Voodoo40)
 	9005:0285:15d9:02d2	SMC	AOC-USAS-S8i-LP
 	9005:0285:15d9:02d3	SMC	AOC-USAS-S8iR-LP
-	9005:0285:9005:02d4	Adaptec	2045 (Voodoo04 Lite)
-	9005:0285:9005:02d5	Adaptec	2405 (Voodoo40 Lite)
-	9005:0285:9005:02d6	Adaptec	2445 (Voodoo44 Lite)
-	9005:0285:9005:02d7	Adaptec	2805 (Voodoo80 Lite)
+	9005:0285:9005:02d4	Adaptec	ASR-2045 (Voodoo04 Lite)
+	9005:0285:9005:02d5	Adaptec	ASR-2405 (Voodoo40 Lite)
+	9005:0285:9005:02d6	Adaptec	ASR-2445 (Voodoo44 Lite)
+	9005:0285:9005:02d7	Adaptec	ASR-2805 (Voodoo80 Lite)
+	9005:0285:9005:02d8	Adaptec	5405G (Voodoo40 PM)
+	9005:0285:9005:02d9	Adaptec	5445G (Voodoo44 PM)
+	9005:0285:9005:02da	Adaptec	5805G (Voodoo80 PM)
+	9005:0285:9005:02db	Adaptec	5085G (Voodoo08 PM)
+	9005:0285:9005:02dc	Adaptec	51245G (Voodoo124 PM)
+	9005:0285:9005:02dd	Adaptec	51645G (Voodoo164 PM)
+	9005:0285:9005:02de	Adaptec	52445G (Voodoo244 PM)
+	9005:0285:9005:02df	Adaptec	ASR-2045G (Voodoo04 Lite PM)
+	9005:0285:9005:02e0	Adaptec	ASR-2405G (Voodoo40 Lite PM)
+	9005:0285:9005:02e1	Adaptec	ASR-2445G (Voodoo44 Lite PM)
+	9005:0285:9005:02e2	Adaptec	ASR-2805G (Voodoo80 Lite PM)
 	1011:0046:9005:0364	Adaptec	5400S (Mustang)
+	1011:0046:9005:0365	Adaptec	5400S (Mustang)
 	9005:0287:9005:0800	Adaptec	Themisto (Jupiter)
 	9005:0200:9005:0200	Adaptec	Themisto (Jupiter)
 	9005:0286:9005:0800	Adaptec	Callisto (Jupiter)
 	1011:0046:9005:1364	Dell	PERC 2/QC (Quad Channel, Mustang)
+	1011:0046:9005:1365	Dell	PERC 2/QC (Quad Channel, Mustang)
 	1028:0001:1028:0001	Dell	PERC 2/Si (Iguana)
 	1028:0003:1028:0003	Dell	PERC 3/Si (SlimFast)
 	1028:0002:1028:0002	Dell	PERC 3/Di (Opal)
-	1028:0004:1028:0004	Dell	PERC 3/DiF (Iguana)
+	1028:0004:1028:0004	Dell	PERC 3/SiF (Iguana)
+	1028:0004:1028:00d0	Dell	PERC 3/DiF (Iguana)
 	1028:0002:1028:00d1	Dell	PERC 3/DiV (Viper)
 	1028:0002:1028:00d9	Dell	PERC 3/DiL (Lexus)
 	1028:000a:1028:0106	Dell	PERC 3/DiJ (Jaguar)
diff --git a/block/bsg.c b/block/bsg.c
index 0b3b282..5fb9b0b 100644
--- a/block/bsg.c
+++ b/block/bsg.c
@@ -740,8 +740,13 @@
 	mutex_lock(&bsg_mutex);
 
 	do_free = atomic_dec_and_test(&bd->ref_count);
-	if (!do_free)
+	if (!do_free) {
+		mutex_unlock(&bsg_mutex);
 		goto out;
+	}
+
+	hlist_del(&bd->dev_list);
+	mutex_unlock(&bsg_mutex);
 
 	dprintk("%s: tearing down\n", bd->name);
 
@@ -757,10 +762,8 @@
 	 */
 	ret = bsg_complete_all_commands(bd);
 
-	hlist_del(&bd->dev_list);
 	kfree(bd);
 out:
-	mutex_unlock(&bsg_mutex);
 	kref_put(&q->bsg_dev.ref, bsg_kref_release_function);
 	if (do_free)
 		blk_put_queue(q);
diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c
index 356fac6..5a1cf25 100644
--- a/drivers/infiniband/ulp/iser/iscsi_iser.c
+++ b/drivers/infiniband/ulp/iser/iscsi_iser.c
@@ -71,6 +71,10 @@
 
 #include "iscsi_iser.h"
 
+static struct scsi_host_template iscsi_iser_sht;
+static struct iscsi_transport iscsi_iser_transport;
+static struct scsi_transport_template *iscsi_iser_scsi_transport;
+
 static unsigned int iscsi_max_lun = 512;
 module_param_named(max_lun, iscsi_max_lun, uint, S_IRUGO);
 
@@ -91,7 +95,6 @@
 		struct iscsi_hdr *hdr, char *rx_data, int rx_data_len)
 {
 	int rc = 0;
-	uint32_t ret_itt;
 	int datalen;
 	int ahslen;
 
@@ -107,12 +110,7 @@
 	/* read AHS */
 	ahslen = hdr->hlength * 4;
 
-	/* verify itt (itt encoding: age+cid+itt) */
-	rc = iscsi_verify_itt(conn, hdr, &ret_itt);
-
-	if (!rc)
-		rc = iscsi_complete_pdu(conn, hdr, rx_data, rx_data_len);
-
+	rc = iscsi_complete_pdu(conn, hdr, rx_data, rx_data_len);
 	if (rc && rc != ISCSI_ERR_NO_SCSI_CMD)
 		goto error;
 
@@ -123,25 +121,33 @@
 
 
 /**
- * iscsi_iser_cmd_init - Initialize iSCSI SCSI_READ or SCSI_WRITE commands
+ * iscsi_iser_task_init - Initialize task
+ * @task: iscsi task
  *
- **/
+ * Initialize the task for the scsi command or mgmt command.
+ */
 static int
-iscsi_iser_cmd_init(struct iscsi_cmd_task *ctask)
+iscsi_iser_task_init(struct iscsi_task *task)
 {
-	struct iscsi_iser_conn     *iser_conn  = ctask->conn->dd_data;
-	struct iscsi_iser_cmd_task *iser_ctask = ctask->dd_data;
+	struct iscsi_iser_conn *iser_conn  = task->conn->dd_data;
+	struct iscsi_iser_task *iser_task = task->dd_data;
 
-	iser_ctask->command_sent = 0;
-	iser_ctask->iser_conn    = iser_conn;
-	iser_ctask_rdma_init(iser_ctask);
+	/* mgmt task */
+	if (!task->sc) {
+		iser_task->desc.data = task->data;
+		return 0;
+	}
+
+	iser_task->command_sent = 0;
+	iser_task->iser_conn    = iser_conn;
+	iser_task_rdma_init(iser_task);
 	return 0;
 }
 
 /**
- * iscsi_mtask_xmit - xmit management(immediate) task
+ * iscsi_iser_mtask_xmit - xmit management(immediate) task
  * @conn: iscsi connection
- * @mtask: task management task
+ * @task: task management task
  *
  * Notes:
  *	The function can return -EAGAIN in which case caller must
@@ -150,20 +156,19 @@
  *
  **/
 static int
-iscsi_iser_mtask_xmit(struct iscsi_conn *conn,
-		      struct iscsi_mgmt_task *mtask)
+iscsi_iser_mtask_xmit(struct iscsi_conn *conn, struct iscsi_task *task)
 {
 	int error = 0;
 
-	debug_scsi("mtask deq [cid %d itt 0x%x]\n", conn->id, mtask->itt);
+	debug_scsi("task deq [cid %d itt 0x%x]\n", conn->id, task->itt);
 
-	error = iser_send_control(conn, mtask);
+	error = iser_send_control(conn, task);
 
-	/* since iser xmits control with zero copy, mtasks can not be recycled
+	/* since iser xmits control with zero copy, tasks can not be recycled
 	 * right after sending them.
 	 * The recycling scheme is based on whether a response is expected
-	 * - if yes, the mtask is recycled at iscsi_complete_pdu
-	 * - if no,  the mtask is recycled at iser_snd_completion
+	 * - if yes, the task is recycled at iscsi_complete_pdu
+	 * - if no,  the task is recycled at iser_snd_completion
 	 */
 	if (error && error != -ENOBUFS)
 		iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
@@ -172,99 +177,88 @@
 }
 
 static int
-iscsi_iser_ctask_xmit_unsol_data(struct iscsi_conn *conn,
-				 struct iscsi_cmd_task *ctask)
+iscsi_iser_task_xmit_unsol_data(struct iscsi_conn *conn,
+				 struct iscsi_task *task)
 {
 	struct iscsi_data  hdr;
 	int error = 0;
 
 	/* Send data-out PDUs while there's still unsolicited data to send */
-	while (ctask->unsol_count > 0) {
-		iscsi_prep_unsolicit_data_pdu(ctask, &hdr);
+	while (task->unsol_count > 0) {
+		iscsi_prep_unsolicit_data_pdu(task, &hdr);
 		debug_scsi("Sending data-out: itt 0x%x, data count %d\n",
-			   hdr.itt, ctask->data_count);
+			   hdr.itt, task->data_count);
 
 		/* the buffer description has been passed with the command */
 		/* Send the command */
-		error = iser_send_data_out(conn, ctask, &hdr);
+		error = iser_send_data_out(conn, task, &hdr);
 		if (error) {
-			ctask->unsol_datasn--;
-			goto iscsi_iser_ctask_xmit_unsol_data_exit;
+			task->unsol_datasn--;
+			goto iscsi_iser_task_xmit_unsol_data_exit;
 		}
-		ctask->unsol_count -= ctask->data_count;
+		task->unsol_count -= task->data_count;
 		debug_scsi("Need to send %d more as data-out PDUs\n",
-			   ctask->unsol_count);
+			   task->unsol_count);
 	}
 
-iscsi_iser_ctask_xmit_unsol_data_exit:
+iscsi_iser_task_xmit_unsol_data_exit:
 	return error;
 }
 
 static int
-iscsi_iser_ctask_xmit(struct iscsi_conn *conn,
-		      struct iscsi_cmd_task *ctask)
+iscsi_iser_task_xmit(struct iscsi_task *task)
 {
-	struct iscsi_iser_cmd_task *iser_ctask = ctask->dd_data;
+	struct iscsi_conn *conn = task->conn;
+	struct iscsi_iser_task *iser_task = task->dd_data;
 	int error = 0;
 
-	if (ctask->sc->sc_data_direction == DMA_TO_DEVICE) {
-		BUG_ON(scsi_bufflen(ctask->sc) == 0);
+	if (!task->sc)
+		return iscsi_iser_mtask_xmit(conn, task);
+
+	if (task->sc->sc_data_direction == DMA_TO_DEVICE) {
+		BUG_ON(scsi_bufflen(task->sc) == 0);
 
 		debug_scsi("cmd [itt %x total %d imm %d unsol_data %d\n",
-			   ctask->itt, scsi_bufflen(ctask->sc),
-			   ctask->imm_count, ctask->unsol_count);
+			   task->itt, scsi_bufflen(task->sc),
+			   task->imm_count, task->unsol_count);
 	}
 
-	debug_scsi("ctask deq [cid %d itt 0x%x]\n",
-		   conn->id, ctask->itt);
+	debug_scsi("task deq [cid %d itt 0x%x]\n",
+		   conn->id, task->itt);
 
 	/* Send the cmd PDU */
-	if (!iser_ctask->command_sent) {
-		error = iser_send_command(conn, ctask);
+	if (!iser_task->command_sent) {
+		error = iser_send_command(conn, task);
 		if (error)
-			goto iscsi_iser_ctask_xmit_exit;
-		iser_ctask->command_sent = 1;
+			goto iscsi_iser_task_xmit_exit;
+		iser_task->command_sent = 1;
 	}
 
 	/* Send unsolicited data-out PDU(s) if necessary */
-	if (ctask->unsol_count)
-		error = iscsi_iser_ctask_xmit_unsol_data(conn, ctask);
+	if (task->unsol_count)
+		error = iscsi_iser_task_xmit_unsol_data(conn, task);
 
- iscsi_iser_ctask_xmit_exit:
+ iscsi_iser_task_xmit_exit:
 	if (error && error != -ENOBUFS)
 		iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
 	return error;
 }
 
 static void
-iscsi_iser_cleanup_ctask(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
+iscsi_iser_cleanup_task(struct iscsi_conn *conn, struct iscsi_task *task)
 {
-	struct iscsi_iser_cmd_task *iser_ctask = ctask->dd_data;
+	struct iscsi_iser_task *iser_task = task->dd_data;
 
-	if (iser_ctask->status == ISER_TASK_STATUS_STARTED) {
-		iser_ctask->status = ISER_TASK_STATUS_COMPLETED;
-		iser_ctask_rdma_finalize(iser_ctask);
+	/* mgmt tasks do not need special cleanup */
+	if (!task->sc)
+		return;
+
+	if (iser_task->status == ISER_TASK_STATUS_STARTED) {
+		iser_task->status = ISER_TASK_STATUS_COMPLETED;
+		iser_task_rdma_finalize(iser_task);
 	}
 }
 
-static struct iser_conn *
-iscsi_iser_ib_conn_lookup(__u64 ep_handle)
-{
-	struct iser_conn *ib_conn;
-	struct iser_conn *uib_conn = (struct iser_conn *)(unsigned long)ep_handle;
-
-	mutex_lock(&ig.connlist_mutex);
-	list_for_each_entry(ib_conn, &ig.connlist, conn_list) {
-		if (ib_conn == uib_conn) {
-			mutex_unlock(&ig.connlist_mutex);
-			return ib_conn;
-		}
-	}
-	mutex_unlock(&ig.connlist_mutex);
-	iser_err("no conn exists for eph %llx\n",(unsigned long long)ep_handle);
-	return NULL;
-}
-
 static struct iscsi_cls_conn *
 iscsi_iser_conn_create(struct iscsi_cls_session *cls_session, uint32_t conn_idx)
 {
@@ -272,7 +266,7 @@
 	struct iscsi_cls_conn *cls_conn;
 	struct iscsi_iser_conn *iser_conn;
 
-	cls_conn = iscsi_conn_setup(cls_session, conn_idx);
+	cls_conn = iscsi_conn_setup(cls_session, sizeof(*iser_conn), conn_idx);
 	if (!cls_conn)
 		return NULL;
 	conn = cls_conn->dd_data;
@@ -283,21 +277,11 @@
 	 */
 	conn->max_recv_dlength = 128;
 
-	iser_conn = kzalloc(sizeof(*iser_conn), GFP_KERNEL);
-	if (!iser_conn)
-		goto conn_alloc_fail;
-
-	/* currently this is the only field which need to be initiated */
-	rwlock_init(&iser_conn->lock);
-
+	iser_conn = conn->dd_data;
 	conn->dd_data = iser_conn;
 	iser_conn->iscsi_conn = conn;
 
 	return cls_conn;
-
-conn_alloc_fail:
-	iscsi_conn_teardown(cls_conn);
-	return NULL;
 }
 
 static void
@@ -305,11 +289,18 @@
 {
 	struct iscsi_conn *conn = cls_conn->dd_data;
 	struct iscsi_iser_conn *iser_conn = conn->dd_data;
+	struct iser_conn *ib_conn = iser_conn->ib_conn;
 
 	iscsi_conn_teardown(cls_conn);
-	if (iser_conn->ib_conn)
-		iser_conn->ib_conn->iser_conn = NULL;
-	kfree(iser_conn);
+	/*
+	 * Userspace will normally call the stop callback and
+	 * already have freed the ib_conn, but if it goofed up then
+	 * we free it here.
+	 */
+	if (ib_conn) {
+		ib_conn->iser_conn = NULL;
+		iser_conn_put(ib_conn);
+	}
 }
 
 static int
@@ -320,6 +311,7 @@
 	struct iscsi_conn *conn = cls_conn->dd_data;
 	struct iscsi_iser_conn *iser_conn;
 	struct iser_conn *ib_conn;
+	struct iscsi_endpoint *ep;
 	int error;
 
 	error = iscsi_conn_bind(cls_session, cls_conn, is_leading);
@@ -328,12 +320,14 @@
 
 	/* the transport ep handle comes from user space so it must be
 	 * verified against the global ib connections list */
-	ib_conn = iscsi_iser_ib_conn_lookup(transport_eph);
-	if (!ib_conn) {
+	ep = iscsi_lookup_endpoint(transport_eph);
+	if (!ep) {
 		iser_err("can't bind eph %llx\n",
 			 (unsigned long long)transport_eph);
 		return -EINVAL;
 	}
+	ib_conn = ep->dd_data;
+
 	/* binds the iSER connection retrieved from the previously
 	 * connected ep_handle to the iSCSI layer connection. exchanges
 	 * connection pointers */
@@ -341,12 +335,32 @@
 	iser_conn = conn->dd_data;
 	ib_conn->iser_conn = iser_conn;
 	iser_conn->ib_conn  = ib_conn;
-
-	conn->recv_lock = &iser_conn->lock;
-
+	iser_conn_get(ib_conn);
 	return 0;
 }
 
+static void
+iscsi_iser_conn_stop(struct iscsi_cls_conn *cls_conn, int flag)
+{
+	struct iscsi_conn *conn = cls_conn->dd_data;
+	struct iscsi_iser_conn *iser_conn = conn->dd_data;
+	struct iser_conn *ib_conn = iser_conn->ib_conn;
+
+	/*
+	 * Userspace may have goofed up and not bound the connection or
+	 * might have only partially setup the connection.
+	 */
+	if (ib_conn) {
+		iscsi_conn_stop(cls_conn, flag);
+		/*
+		 * There is no unbind event so the stop callback
+		 * must release the ref from the bind.
+		 */
+		iser_conn_put(ib_conn);
+	}
+	iser_conn->ib_conn = NULL;
+}
+
 static int
 iscsi_iser_conn_start(struct iscsi_cls_conn *cls_conn)
 {
@@ -360,55 +374,75 @@
 	return iscsi_conn_start(cls_conn);
 }
 
-static struct iscsi_transport iscsi_iser_transport;
+static void iscsi_iser_session_destroy(struct iscsi_cls_session *cls_session)
+{
+	struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
+
+	iscsi_host_remove(shost);
+	iscsi_host_free(shost);
+}
 
 static struct iscsi_cls_session *
-iscsi_iser_session_create(struct iscsi_transport *iscsit,
-			 struct scsi_transport_template *scsit,
-			 uint16_t cmds_max, uint16_t qdepth,
-			 uint32_t initial_cmdsn, uint32_t *hostno)
+iscsi_iser_session_create(struct iscsi_endpoint *ep,
+			  uint16_t cmds_max, uint16_t qdepth,
+			  uint32_t initial_cmdsn, uint32_t *hostno)
 {
 	struct iscsi_cls_session *cls_session;
 	struct iscsi_session *session;
+	struct Scsi_Host *shost;
 	int i;
-	uint32_t hn;
-	struct iscsi_cmd_task  *ctask;
-	struct iscsi_mgmt_task *mtask;
-	struct iscsi_iser_cmd_task *iser_ctask;
-	struct iser_desc *desc;
+	struct iscsi_task *task;
+	struct iscsi_iser_task *iser_task;
+	struct iser_conn *ib_conn;
+
+	shost = iscsi_host_alloc(&iscsi_iser_sht, 0, ISCSI_MAX_CMD_PER_LUN);
+	if (!shost)
+		return NULL;
+	shost->transportt = iscsi_iser_scsi_transport;
+	shost->max_lun = iscsi_max_lun;
+	shost->max_id = 0;
+	shost->max_channel = 0;
+	shost->max_cmd_len = 16;
+
+	/*
+	 * older userspace tools (before 2.0-870) did not pass us
+	 * the leading conn's ep so this will be NULL;
+	 */
+	if (ep)
+		ib_conn = ep->dd_data;
+
+	if (iscsi_host_add(shost,
+			   ep ? ib_conn->device->ib_device->dma_device : NULL))
+		goto free_host;
+	*hostno = shost->host_no;
 
 	/*
 	 * we do not support setting can_queue cmd_per_lun from userspace yet
 	 * because we preallocate so many resources
 	 */
-	cls_session = iscsi_session_setup(iscsit, scsit,
+	cls_session = iscsi_session_setup(&iscsi_iser_transport, shost,
 					  ISCSI_DEF_XMIT_CMDS_MAX,
-					  ISCSI_MAX_CMD_PER_LUN,
-					  sizeof(struct iscsi_iser_cmd_task),
-					  sizeof(struct iser_desc),
-					  initial_cmdsn, &hn);
+					  sizeof(struct iscsi_iser_task),
+					  initial_cmdsn, 0);
 	if (!cls_session)
-	return NULL;
+		goto remove_host;
+	session = cls_session->dd_data;
 
-	*hostno = hn;
-	session = class_to_transport_session(cls_session);
-
+	shost->can_queue = session->scsi_cmds_max;
 	/* libiscsi setup itts, data and pool so just set desc fields */
 	for (i = 0; i < session->cmds_max; i++) {
-		ctask      = session->cmds[i];
-		iser_ctask = ctask->dd_data;
-		ctask->hdr = (struct iscsi_cmd *)&iser_ctask->desc.iscsi_header;
-		ctask->hdr_max = sizeof(iser_ctask->desc.iscsi_header);
+		task = session->cmds[i];
+		iser_task = task->dd_data;
+		task->hdr = (struct iscsi_cmd *)&iser_task->desc.iscsi_header;
+		task->hdr_max = sizeof(iser_task->desc.iscsi_header);
 	}
-
-	for (i = 0; i < session->mgmtpool_max; i++) {
-		mtask      = session->mgmt_cmds[i];
-		desc       = mtask->dd_data;
-		mtask->hdr = &desc->iscsi_header;
-		desc->data = mtask->data;
-	}
-
 	return cls_session;
+
+remove_host:
+	iscsi_host_remove(shost);
+free_host:
+	iscsi_host_free(shost);
+	return NULL;
 }
 
 static int
@@ -481,34 +515,37 @@
 	stats->custom[3].value = conn->fmr_unalign_cnt;
 }
 
-static int
-iscsi_iser_ep_connect(struct sockaddr *dst_addr, int non_blocking,
-		      __u64 *ep_handle)
+static struct iscsi_endpoint *
+iscsi_iser_ep_connect(struct sockaddr *dst_addr, int non_blocking)
 {
 	int err;
 	struct iser_conn *ib_conn;
+	struct iscsi_endpoint *ep;
 
-	err = iser_conn_init(&ib_conn);
-	if (err)
-		goto out;
+	ep = iscsi_create_endpoint(sizeof(*ib_conn));
+	if (!ep)
+		return ERR_PTR(-ENOMEM);
 
-	err = iser_connect(ib_conn, NULL, (struct sockaddr_in *)dst_addr, non_blocking);
-	if (!err)
-		*ep_handle = (__u64)(unsigned long)ib_conn;
+	ib_conn = ep->dd_data;
+	ib_conn->ep = ep;
+	iser_conn_init(ib_conn);
 
-out:
-	return err;
+	err = iser_connect(ib_conn, NULL, (struct sockaddr_in *)dst_addr,
+			   non_blocking);
+	if (err) {
+		iscsi_destroy_endpoint(ep);
+		return ERR_PTR(err);
+	}
+	return ep;
 }
 
 static int
-iscsi_iser_ep_poll(__u64 ep_handle, int timeout_ms)
+iscsi_iser_ep_poll(struct iscsi_endpoint *ep, int timeout_ms)
 {
-	struct iser_conn *ib_conn = iscsi_iser_ib_conn_lookup(ep_handle);
+	struct iser_conn *ib_conn;
 	int rc;
 
-	if (!ib_conn)
-		return -EINVAL;
-
+	ib_conn = ep->dd_data;
 	rc = wait_event_interruptible_timeout(ib_conn->wait,
 			     ib_conn->state == ISER_CONN_UP,
 			     msecs_to_jiffies(timeout_ms));
@@ -530,13 +567,21 @@
 }
 
 static void
-iscsi_iser_ep_disconnect(__u64 ep_handle)
+iscsi_iser_ep_disconnect(struct iscsi_endpoint *ep)
 {
 	struct iser_conn *ib_conn;
 
-	ib_conn = iscsi_iser_ib_conn_lookup(ep_handle);
-	if (!ib_conn)
-		return;
+	ib_conn = ep->dd_data;
+	if (ib_conn->iser_conn)
+		/*
+		 * Must suspend xmit path if the ep is bound to the
+		 * iscsi_conn, so we know we are not accessing the ib_conn
+		 * when we free it.
+		 *
+		 * This may not be bound if the ep poll failed.
+		 */
+		iscsi_suspend_tx(ib_conn->iser_conn->iscsi_conn);
+
 
 	iser_err("ib conn %p state %d\n",ib_conn, ib_conn->state);
 	iser_conn_terminate(ib_conn);
@@ -547,7 +592,6 @@
 	.name                   = "iSCSI Initiator over iSER, v." DRV_VER,
 	.queuecommand           = iscsi_queuecommand,
 	.change_queue_depth	= iscsi_change_queue_depth,
-	.can_queue		= ISCSI_DEF_XMIT_CMDS_MAX - 1,
 	.sg_tablesize           = ISCSI_ISER_SG_TABLESIZE,
 	.max_sectors		= 1024,
 	.cmd_per_lun            = ISCSI_MAX_CMD_PER_LUN,
@@ -581,17 +625,14 @@
 				  ISCSI_USERNAME | ISCSI_PASSWORD |
 				  ISCSI_USERNAME_IN | ISCSI_PASSWORD_IN |
 				  ISCSI_FAST_ABORT | ISCSI_ABORT_TMO |
-				  ISCSI_PING_TMO | ISCSI_RECV_TMO,
+				  ISCSI_PING_TMO | ISCSI_RECV_TMO |
+				  ISCSI_IFACE_NAME | ISCSI_INITIATOR_NAME,
 	.host_param_mask	= ISCSI_HOST_HWADDRESS |
 				  ISCSI_HOST_NETDEV_NAME |
 				  ISCSI_HOST_INITIATOR_NAME,
-	.host_template          = &iscsi_iser_sht,
-	.conndata_size		= sizeof(struct iscsi_conn),
-	.max_lun                = ISCSI_ISER_MAX_LUN,
-	.max_cmd_len            = ISCSI_ISER_MAX_CMD_LEN,
 	/* session management */
 	.create_session         = iscsi_iser_session_create,
-	.destroy_session        = iscsi_session_teardown,
+	.destroy_session        = iscsi_iser_session_destroy,
 	/* connection management */
 	.create_conn            = iscsi_iser_conn_create,
 	.bind_conn              = iscsi_iser_conn_bind,
@@ -600,17 +641,16 @@
 	.get_conn_param		= iscsi_conn_get_param,
 	.get_session_param	= iscsi_session_get_param,
 	.start_conn             = iscsi_iser_conn_start,
-	.stop_conn              = iscsi_conn_stop,
+	.stop_conn              = iscsi_iser_conn_stop,
 	/* iscsi host params */
 	.get_host_param		= iscsi_host_get_param,
 	.set_host_param		= iscsi_host_set_param,
 	/* IO */
 	.send_pdu		= iscsi_conn_send_pdu,
 	.get_stats		= iscsi_iser_conn_get_stats,
-	.init_cmd_task		= iscsi_iser_cmd_init,
-	.xmit_cmd_task		= iscsi_iser_ctask_xmit,
-	.xmit_mgmt_task		= iscsi_iser_mtask_xmit,
-	.cleanup_cmd_task	= iscsi_iser_cleanup_ctask,
+	.init_task		= iscsi_iser_task_init,
+	.xmit_task		= iscsi_iser_task_xmit,
+	.cleanup_task		= iscsi_iser_cleanup_task,
 	/* recovery */
 	.session_recovery_timedout = iscsi_session_recovery_timedout,
 
@@ -630,8 +670,6 @@
 		return -EINVAL;
 	}
 
-	iscsi_iser_transport.max_lun = iscsi_max_lun;
-
 	memset(&ig, 0, sizeof(struct iser_global));
 
 	ig.desc_cache = kmem_cache_create("iser_descriptors",
@@ -647,7 +685,9 @@
 	mutex_init(&ig.connlist_mutex);
 	INIT_LIST_HEAD(&ig.connlist);
 
-	if (!iscsi_register_transport(&iscsi_iser_transport)) {
+	iscsi_iser_scsi_transport = iscsi_register_transport(
+							&iscsi_iser_transport);
+	if (!iscsi_iser_scsi_transport) {
 		iser_err("iscsi_register_transport failed\n");
 		err = -EINVAL;
 		goto register_transport_failure;
diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.h b/drivers/infiniband/ulp/iser/iscsi_iser.h
index 0e10703..81a8262 100644
--- a/drivers/infiniband/ulp/iser/iscsi_iser.h
+++ b/drivers/infiniband/ulp/iser/iscsi_iser.h
@@ -94,7 +94,6 @@
 					/* support upto 512KB in one RDMA */
 #define ISCSI_ISER_SG_TABLESIZE         (0x80000 >> SHIFT_4K)
 #define ISCSI_ISER_MAX_LUN		256
-#define ISCSI_ISER_MAX_CMD_LEN		16
 
 /* QP settings */
 /* Maximal bounds on received asynchronous PDUs */
@@ -172,7 +171,8 @@
 /* fwd declarations */
 struct iser_device;
 struct iscsi_iser_conn;
-struct iscsi_iser_cmd_task;
+struct iscsi_iser_task;
+struct iscsi_endpoint;
 
 struct iser_mem_reg {
 	u32  lkey;
@@ -196,7 +196,7 @@
 #define MAX_REGD_BUF_VECTOR_LEN	2
 
 struct iser_dto {
-	struct iscsi_iser_cmd_task *ctask;
+	struct iscsi_iser_task *task;
 	struct iser_conn *ib_conn;
 	int                        notify_enable;
 
@@ -240,7 +240,9 @@
 
 struct iser_conn {
 	struct iscsi_iser_conn       *iser_conn; /* iser conn for upcalls  */
+	struct iscsi_endpoint	     *ep;
 	enum iser_ib_conn_state	     state;	    /* rdma connection state   */
+	atomic_t		     refcount;
 	spinlock_t		     lock;	    /* used for state changes  */
 	struct iser_device           *device;       /* device context          */
 	struct rdma_cm_id            *cma_id;       /* CMA ID		       */
@@ -259,11 +261,9 @@
 struct iscsi_iser_conn {
 	struct iscsi_conn            *iscsi_conn;/* ptr to iscsi conn */
 	struct iser_conn             *ib_conn;   /* iSER IB conn      */
-
-	rwlock_t		     lock;
 };
 
-struct iscsi_iser_cmd_task {
+struct iscsi_iser_task {
 	struct iser_desc             desc;
 	struct iscsi_iser_conn	     *iser_conn;
 	enum iser_task_status 	     status;
@@ -296,22 +296,26 @@
 /* allocate connection resources needed for rdma functionality */
 int iser_conn_set_full_featured_mode(struct iscsi_conn *conn);
 
-int iser_send_control(struct iscsi_conn      *conn,
-		      struct iscsi_mgmt_task *mtask);
+int iser_send_control(struct iscsi_conn *conn,
+		      struct iscsi_task *task);
 
-int iser_send_command(struct iscsi_conn      *conn,
-		      struct iscsi_cmd_task  *ctask);
+int iser_send_command(struct iscsi_conn *conn,
+		      struct iscsi_task *task);
 
-int iser_send_data_out(struct iscsi_conn     *conn,
-		       struct iscsi_cmd_task *ctask,
-		       struct iscsi_data          *hdr);
+int iser_send_data_out(struct iscsi_conn *conn,
+		       struct iscsi_task *task,
+		       struct iscsi_data *hdr);
 
 void iscsi_iser_recv(struct iscsi_conn *conn,
 		     struct iscsi_hdr       *hdr,
 		     char                   *rx_data,
 		     int                    rx_data_len);
 
-int  iser_conn_init(struct iser_conn **ib_conn);
+void iser_conn_init(struct iser_conn *ib_conn);
+
+void iser_conn_get(struct iser_conn *ib_conn);
+
+void iser_conn_put(struct iser_conn *ib_conn);
 
 void iser_conn_terminate(struct iser_conn *ib_conn);
 
@@ -320,9 +324,9 @@
 
 void iser_snd_completion(struct iser_desc *desc);
 
-void iser_ctask_rdma_init(struct iscsi_iser_cmd_task     *ctask);
+void iser_task_rdma_init(struct iscsi_iser_task *task);
 
-void iser_ctask_rdma_finalize(struct iscsi_iser_cmd_task *ctask);
+void iser_task_rdma_finalize(struct iscsi_iser_task *task);
 
 void iser_dto_buffs_release(struct iser_dto *dto);
 
@@ -332,10 +336,10 @@
 		     struct iser_regd_buf    *regd_buf,
 		     enum dma_data_direction direction);
 
-void iser_finalize_rdma_unaligned_sg(struct iscsi_iser_cmd_task *ctask,
+void iser_finalize_rdma_unaligned_sg(struct iscsi_iser_task *task,
 				     enum iser_data_dir         cmd_dir);
 
-int  iser_reg_rdma_mem(struct iscsi_iser_cmd_task *ctask,
+int  iser_reg_rdma_mem(struct iscsi_iser_task *task,
 		       enum   iser_data_dir        cmd_dir);
 
 int  iser_connect(struct iser_conn   *ib_conn,
@@ -355,10 +359,10 @@
 int iser_conn_state_comp(struct iser_conn *ib_conn,
 			 enum iser_ib_conn_state comp);
 
-int iser_dma_map_task_data(struct iscsi_iser_cmd_task *iser_ctask,
+int iser_dma_map_task_data(struct iscsi_iser_task *iser_task,
 			    struct iser_data_buf       *data,
 			    enum   iser_data_dir       iser_dir,
 			    enum   dma_data_direction  dma_dir);
 
-void iser_dma_unmap_task_data(struct iscsi_iser_cmd_task *iser_ctask);
+void iser_dma_unmap_task_data(struct iscsi_iser_task *iser_task);
 #endif
diff --git a/drivers/infiniband/ulp/iser/iser_initiator.c b/drivers/infiniband/ulp/iser/iser_initiator.c
index 31ad498..cdd2831 100644
--- a/drivers/infiniband/ulp/iser/iser_initiator.c
+++ b/drivers/infiniband/ulp/iser/iser_initiator.c
@@ -64,46 +64,46 @@
 
 /* Register user buffer memory and initialize passive rdma
  *  dto descriptor. Total data size is stored in
- *  iser_ctask->data[ISER_DIR_IN].data_len
+ *  iser_task->data[ISER_DIR_IN].data_len
  */
-static int iser_prepare_read_cmd(struct iscsi_cmd_task *ctask,
+static int iser_prepare_read_cmd(struct iscsi_task *task,
 				 unsigned int edtl)
 
 {
-	struct iscsi_iser_cmd_task *iser_ctask = ctask->dd_data;
+	struct iscsi_iser_task *iser_task = task->dd_data;
 	struct iser_regd_buf *regd_buf;
 	int err;
-	struct iser_hdr *hdr = &iser_ctask->desc.iser_header;
-	struct iser_data_buf *buf_in = &iser_ctask->data[ISER_DIR_IN];
+	struct iser_hdr *hdr = &iser_task->desc.iser_header;
+	struct iser_data_buf *buf_in = &iser_task->data[ISER_DIR_IN];
 
-	err = iser_dma_map_task_data(iser_ctask,
+	err = iser_dma_map_task_data(iser_task,
 				     buf_in,
 				     ISER_DIR_IN,
 				     DMA_FROM_DEVICE);
 	if (err)
 		return err;
 
-	if (edtl > iser_ctask->data[ISER_DIR_IN].data_len) {
+	if (edtl > iser_task->data[ISER_DIR_IN].data_len) {
 		iser_err("Total data length: %ld, less than EDTL: "
 			 "%d, in READ cmd BHS itt: %d, conn: 0x%p\n",
-			 iser_ctask->data[ISER_DIR_IN].data_len, edtl,
-			 ctask->itt, iser_ctask->iser_conn);
+			 iser_task->data[ISER_DIR_IN].data_len, edtl,
+			 task->itt, iser_task->iser_conn);
 		return -EINVAL;
 	}
 
-	err = iser_reg_rdma_mem(iser_ctask,ISER_DIR_IN);
+	err = iser_reg_rdma_mem(iser_task,ISER_DIR_IN);
 	if (err) {
 		iser_err("Failed to set up Data-IN RDMA\n");
 		return err;
 	}
-	regd_buf = &iser_ctask->rdma_regd[ISER_DIR_IN];
+	regd_buf = &iser_task->rdma_regd[ISER_DIR_IN];
 
 	hdr->flags    |= ISER_RSV;
 	hdr->read_stag = cpu_to_be32(regd_buf->reg.rkey);
 	hdr->read_va   = cpu_to_be64(regd_buf->reg.va);
 
 	iser_dbg("Cmd itt:%d READ tags RKEY:%#.4X VA:%#llX\n",
-		 ctask->itt, regd_buf->reg.rkey,
+		 task->itt, regd_buf->reg.rkey,
 		 (unsigned long long)regd_buf->reg.va);
 
 	return 0;
@@ -111,43 +111,43 @@
 
 /* Register user buffer memory and initialize passive rdma
  *  dto descriptor. Total data size is stored in
- *  ctask->data[ISER_DIR_OUT].data_len
+ *  task->data[ISER_DIR_OUT].data_len
  */
 static int
-iser_prepare_write_cmd(struct iscsi_cmd_task *ctask,
+iser_prepare_write_cmd(struct iscsi_task *task,
 		       unsigned int imm_sz,
 		       unsigned int unsol_sz,
 		       unsigned int edtl)
 {
-	struct iscsi_iser_cmd_task *iser_ctask = ctask->dd_data;
+	struct iscsi_iser_task *iser_task = task->dd_data;
 	struct iser_regd_buf *regd_buf;
 	int err;
-	struct iser_dto *send_dto = &iser_ctask->desc.dto;
-	struct iser_hdr *hdr = &iser_ctask->desc.iser_header;
-	struct iser_data_buf *buf_out = &iser_ctask->data[ISER_DIR_OUT];
+	struct iser_dto *send_dto = &iser_task->desc.dto;
+	struct iser_hdr *hdr = &iser_task->desc.iser_header;
+	struct iser_data_buf *buf_out = &iser_task->data[ISER_DIR_OUT];
 
-	err = iser_dma_map_task_data(iser_ctask,
+	err = iser_dma_map_task_data(iser_task,
 				     buf_out,
 				     ISER_DIR_OUT,
 				     DMA_TO_DEVICE);
 	if (err)
 		return err;
 
-	if (edtl > iser_ctask->data[ISER_DIR_OUT].data_len) {
+	if (edtl > iser_task->data[ISER_DIR_OUT].data_len) {
 		iser_err("Total data length: %ld, less than EDTL: %d, "
 			 "in WRITE cmd BHS itt: %d, conn: 0x%p\n",
-			 iser_ctask->data[ISER_DIR_OUT].data_len,
-			 edtl, ctask->itt, ctask->conn);
+			 iser_task->data[ISER_DIR_OUT].data_len,
+			 edtl, task->itt, task->conn);
 		return -EINVAL;
 	}
 
-	err = iser_reg_rdma_mem(iser_ctask,ISER_DIR_OUT);
+	err = iser_reg_rdma_mem(iser_task,ISER_DIR_OUT);
 	if (err != 0) {
 		iser_err("Failed to register write cmd RDMA mem\n");
 		return err;
 	}
 
-	regd_buf = &iser_ctask->rdma_regd[ISER_DIR_OUT];
+	regd_buf = &iser_task->rdma_regd[ISER_DIR_OUT];
 
 	if (unsol_sz < edtl) {
 		hdr->flags     |= ISER_WSV;
@@ -156,13 +156,13 @@
 
 		iser_dbg("Cmd itt:%d, WRITE tags, RKEY:%#.4X "
 			 "VA:%#llX + unsol:%d\n",
-			 ctask->itt, regd_buf->reg.rkey,
+			 task->itt, regd_buf->reg.rkey,
 			 (unsigned long long)regd_buf->reg.va, unsol_sz);
 	}
 
 	if (imm_sz > 0) {
 		iser_dbg("Cmd itt:%d, WRITE, adding imm.data sz: %d\n",
-			 ctask->itt, imm_sz);
+			 task->itt, imm_sz);
 		iser_dto_add_regd_buff(send_dto,
 				       regd_buf,
 				       0,
@@ -314,38 +314,38 @@
 /**
  * iser_send_command - send command PDU
  */
-int iser_send_command(struct iscsi_conn     *conn,
-		      struct iscsi_cmd_task *ctask)
+int iser_send_command(struct iscsi_conn *conn,
+		      struct iscsi_task *task)
 {
 	struct iscsi_iser_conn *iser_conn = conn->dd_data;
-	struct iscsi_iser_cmd_task *iser_ctask = ctask->dd_data;
+	struct iscsi_iser_task *iser_task = task->dd_data;
 	struct iser_dto *send_dto = NULL;
 	unsigned long edtl;
 	int err = 0;
 	struct iser_data_buf *data_buf;
 
-	struct iscsi_cmd *hdr =  ctask->hdr;
-	struct scsi_cmnd *sc  =  ctask->sc;
+	struct iscsi_cmd *hdr =  task->hdr;
+	struct scsi_cmnd *sc  =  task->sc;
 
 	if (!iser_conn_state_comp(iser_conn->ib_conn, ISER_CONN_UP)) {
 		iser_err("Failed to send, conn: 0x%p is not up\n", iser_conn->ib_conn);
 		return -EPERM;
 	}
-	if (iser_check_xmit(conn, ctask))
+	if (iser_check_xmit(conn, task))
 		return -ENOBUFS;
 
 	edtl = ntohl(hdr->data_length);
 
 	/* build the tx desc regd header and add it to the tx desc dto */
-	iser_ctask->desc.type = ISCSI_TX_SCSI_COMMAND;
-	send_dto = &iser_ctask->desc.dto;
-	send_dto->ctask = iser_ctask;
-	iser_create_send_desc(iser_conn, &iser_ctask->desc);
+	iser_task->desc.type = ISCSI_TX_SCSI_COMMAND;
+	send_dto = &iser_task->desc.dto;
+	send_dto->task = iser_task;
+	iser_create_send_desc(iser_conn, &iser_task->desc);
 
 	if (hdr->flags & ISCSI_FLAG_CMD_READ)
-		data_buf = &iser_ctask->data[ISER_DIR_IN];
+		data_buf = &iser_task->data[ISER_DIR_IN];
 	else
-		data_buf = &iser_ctask->data[ISER_DIR_OUT];
+		data_buf = &iser_task->data[ISER_DIR_OUT];
 
 	if (scsi_sg_count(sc)) { /* using a scatter list */
 		data_buf->buf  = scsi_sglist(sc);
@@ -355,15 +355,15 @@
 	data_buf->data_len = scsi_bufflen(sc);
 
 	if (hdr->flags & ISCSI_FLAG_CMD_READ) {
-		err = iser_prepare_read_cmd(ctask, edtl);
+		err = iser_prepare_read_cmd(task, edtl);
 		if (err)
 			goto send_command_error;
 	}
 	if (hdr->flags & ISCSI_FLAG_CMD_WRITE) {
-		err = iser_prepare_write_cmd(ctask,
-					     ctask->imm_count,
-				             ctask->imm_count +
-					     ctask->unsol_count,
+		err = iser_prepare_write_cmd(task,
+					     task->imm_count,
+				             task->imm_count +
+					     task->unsol_count,
 					     edtl);
 		if (err)
 			goto send_command_error;
@@ -378,27 +378,27 @@
 		goto send_command_error;
 	}
 
-	iser_ctask->status = ISER_TASK_STATUS_STARTED;
+	iser_task->status = ISER_TASK_STATUS_STARTED;
 
-	err = iser_post_send(&iser_ctask->desc);
+	err = iser_post_send(&iser_task->desc);
 	if (!err)
 		return 0;
 
 send_command_error:
 	iser_dto_buffs_release(send_dto);
-	iser_err("conn %p failed ctask->itt %d err %d\n",conn, ctask->itt, err);
+	iser_err("conn %p failed task->itt %d err %d\n",conn, task->itt, err);
 	return err;
 }
 
 /**
  * iser_send_data_out - send data out PDU
  */
-int iser_send_data_out(struct iscsi_conn     *conn,
-		       struct iscsi_cmd_task *ctask,
+int iser_send_data_out(struct iscsi_conn *conn,
+		       struct iscsi_task *task,
 		       struct iscsi_data *hdr)
 {
 	struct iscsi_iser_conn *iser_conn = conn->dd_data;
-	struct iscsi_iser_cmd_task *iser_ctask = ctask->dd_data;
+	struct iscsi_iser_task *iser_task = task->dd_data;
 	struct iser_desc *tx_desc = NULL;
 	struct iser_dto *send_dto = NULL;
 	unsigned long buf_offset;
@@ -411,7 +411,7 @@
 		return -EPERM;
 	}
 
-	if (iser_check_xmit(conn, ctask))
+	if (iser_check_xmit(conn, task))
 		return -ENOBUFS;
 
 	itt = (__force uint32_t)hdr->itt;
@@ -432,7 +432,7 @@
 
 	/* build the tx desc regd header and add it to the tx desc dto */
 	send_dto = &tx_desc->dto;
-	send_dto->ctask = iser_ctask;
+	send_dto->task = iser_task;
 	iser_create_send_desc(iser_conn, tx_desc);
 
 	iser_reg_single(iser_conn->ib_conn->device,
@@ -440,15 +440,15 @@
 
 	/* all data was registered for RDMA, we can use the lkey */
 	iser_dto_add_regd_buff(send_dto,
-			       &iser_ctask->rdma_regd[ISER_DIR_OUT],
+			       &iser_task->rdma_regd[ISER_DIR_OUT],
 			       buf_offset,
 			       data_seg_len);
 
-	if (buf_offset + data_seg_len > iser_ctask->data[ISER_DIR_OUT].data_len) {
+	if (buf_offset + data_seg_len > iser_task->data[ISER_DIR_OUT].data_len) {
 		iser_err("Offset:%ld & DSL:%ld in Data-Out "
 			 "inconsistent with total len:%ld, itt:%d\n",
 			 buf_offset, data_seg_len,
-			 iser_ctask->data[ISER_DIR_OUT].data_len, itt);
+			 iser_task->data[ISER_DIR_OUT].data_len, itt);
 		err = -EINVAL;
 		goto send_data_out_error;
 	}
@@ -468,10 +468,11 @@
 }
 
 int iser_send_control(struct iscsi_conn *conn,
-		      struct iscsi_mgmt_task *mtask)
+		      struct iscsi_task *task)
 {
 	struct iscsi_iser_conn *iser_conn = conn->dd_data;
-	struct iser_desc *mdesc = mtask->dd_data;
+	struct iscsi_iser_task *iser_task = task->dd_data;
+	struct iser_desc *mdesc = &iser_task->desc;
 	struct iser_dto *send_dto = NULL;
 	unsigned long data_seg_len;
 	int err = 0;
@@ -483,27 +484,27 @@
 		return -EPERM;
 	}
 
-	if (iser_check_xmit(conn,mtask))
+	if (iser_check_xmit(conn, task))
 		return -ENOBUFS;
 
 	/* build the tx desc regd header and add it to the tx desc dto */
 	mdesc->type = ISCSI_TX_CONTROL;
 	send_dto = &mdesc->dto;
-	send_dto->ctask = NULL;
+	send_dto->task = NULL;
 	iser_create_send_desc(iser_conn, mdesc);
 
 	device = iser_conn->ib_conn->device;
 
 	iser_reg_single(device, send_dto->regd[0], DMA_TO_DEVICE);
 
-	data_seg_len = ntoh24(mtask->hdr->dlength);
+	data_seg_len = ntoh24(task->hdr->dlength);
 
 	if (data_seg_len > 0) {
 		regd_buf = &mdesc->data_regd_buf;
 		memset(regd_buf, 0, sizeof(struct iser_regd_buf));
 		regd_buf->device = device;
-		regd_buf->virt_addr = mtask->data;
-		regd_buf->data_size = mtask->data_count;
+		regd_buf->virt_addr = task->data;
+		regd_buf->data_size = task->data_count;
 		iser_reg_single(device, regd_buf,
 				DMA_TO_DEVICE);
 		iser_dto_add_regd_buff(send_dto, regd_buf,
@@ -533,15 +534,13 @@
 void iser_rcv_completion(struct iser_desc *rx_desc,
 			 unsigned long dto_xfer_len)
 {
-	struct iser_dto        *dto = &rx_desc->dto;
+	struct iser_dto *dto = &rx_desc->dto;
 	struct iscsi_iser_conn *conn = dto->ib_conn->iser_conn;
-	struct iscsi_session *session = conn->iscsi_conn->session;
-	struct iscsi_cmd_task *ctask;
-	struct iscsi_iser_cmd_task *iser_ctask;
+	struct iscsi_task *task;
+	struct iscsi_iser_task *iser_task;
 	struct iscsi_hdr *hdr;
 	char   *rx_data = NULL;
 	int     rx_data_len = 0;
-	unsigned int itt;
 	unsigned char opcode;
 
 	hdr = &rx_desc->iscsi_header;
@@ -557,19 +556,24 @@
 	opcode = hdr->opcode & ISCSI_OPCODE_MASK;
 
 	if (opcode == ISCSI_OP_SCSI_CMD_RSP) {
-	        itt = get_itt(hdr->itt); /* mask out cid and age bits */
-		if (!(itt < session->cmds_max))
-			iser_err("itt can't be matched to task!!! "
-				 "conn %p opcode %d cmds_max %d itt %d\n",
-				 conn->iscsi_conn,opcode,session->cmds_max,itt);
-		/* use the mapping given with the cmds array indexed by itt */
-		ctask = (struct iscsi_cmd_task *)session->cmds[itt];
-		iser_ctask = ctask->dd_data;
-		iser_dbg("itt %d ctask %p\n",itt,ctask);
-		iser_ctask->status = ISER_TASK_STATUS_COMPLETED;
-		iser_ctask_rdma_finalize(iser_ctask);
-	}
+		spin_lock(&conn->iscsi_conn->session->lock);
+		task = iscsi_itt_to_ctask(conn->iscsi_conn, hdr->itt);
+		if (task)
+			__iscsi_get_task(task);
+		spin_unlock(&conn->iscsi_conn->session->lock);
 
+		if (!task)
+			iser_err("itt can't be matched to task!!! "
+				 "conn %p opcode %d itt %d\n",
+				 conn->iscsi_conn, opcode, hdr->itt);
+		else {
+			iser_task = task->dd_data;
+			iser_dbg("itt %d task %p\n",hdr->itt, task);
+			iser_task->status = ISER_TASK_STATUS_COMPLETED;
+			iser_task_rdma_finalize(iser_task);
+			iscsi_put_task(task);
+		}
+	}
 	iser_dto_buffs_release(dto);
 
 	iscsi_iser_recv(conn->iscsi_conn, hdr, rx_data, rx_data_len);
@@ -590,7 +594,7 @@
 	struct iser_conn       *ib_conn = dto->ib_conn;
 	struct iscsi_iser_conn *iser_conn = ib_conn->iser_conn;
 	struct iscsi_conn      *conn = iser_conn->iscsi_conn;
-	struct iscsi_mgmt_task *mtask;
+	struct iscsi_task *task;
 	int resume_tx = 0;
 
 	iser_dbg("Initiator, Data sent dto=0x%p\n", dto);
@@ -613,36 +617,31 @@
 
 	if (tx_desc->type == ISCSI_TX_CONTROL) {
 		/* this arithmetic is legal by libiscsi dd_data allocation */
-		mtask = (void *) ((long)(void *)tx_desc -
-				  sizeof(struct iscsi_mgmt_task));
-		if (mtask->hdr->itt == RESERVED_ITT) {
-			struct iscsi_session *session = conn->session;
-
-			spin_lock(&conn->session->lock);
-			iscsi_free_mgmt_task(conn, mtask);
-			spin_unlock(&session->lock);
-		}
+		task = (void *) ((long)(void *)tx_desc -
+				  sizeof(struct iscsi_task));
+		if (task->hdr->itt == RESERVED_ITT)
+			iscsi_put_task(task);
 	}
 }
 
-void iser_ctask_rdma_init(struct iscsi_iser_cmd_task *iser_ctask)
+void iser_task_rdma_init(struct iscsi_iser_task *iser_task)
 
 {
-	iser_ctask->status = ISER_TASK_STATUS_INIT;
+	iser_task->status = ISER_TASK_STATUS_INIT;
 
-	iser_ctask->dir[ISER_DIR_IN] = 0;
-	iser_ctask->dir[ISER_DIR_OUT] = 0;
+	iser_task->dir[ISER_DIR_IN] = 0;
+	iser_task->dir[ISER_DIR_OUT] = 0;
 
-	iser_ctask->data[ISER_DIR_IN].data_len  = 0;
-	iser_ctask->data[ISER_DIR_OUT].data_len = 0;
+	iser_task->data[ISER_DIR_IN].data_len  = 0;
+	iser_task->data[ISER_DIR_OUT].data_len = 0;
 
-	memset(&iser_ctask->rdma_regd[ISER_DIR_IN], 0,
+	memset(&iser_task->rdma_regd[ISER_DIR_IN], 0,
 	       sizeof(struct iser_regd_buf));
-	memset(&iser_ctask->rdma_regd[ISER_DIR_OUT], 0,
+	memset(&iser_task->rdma_regd[ISER_DIR_OUT], 0,
 	       sizeof(struct iser_regd_buf));
 }
 
-void iser_ctask_rdma_finalize(struct iscsi_iser_cmd_task *iser_ctask)
+void iser_task_rdma_finalize(struct iscsi_iser_task *iser_task)
 {
 	int deferred;
 	int is_rdma_aligned = 1;
@@ -651,17 +650,17 @@
 	/* if we were reading, copy back to unaligned sglist,
 	 * anyway dma_unmap and free the copy
 	 */
-	if (iser_ctask->data_copy[ISER_DIR_IN].copy_buf != NULL) {
+	if (iser_task->data_copy[ISER_DIR_IN].copy_buf != NULL) {
 		is_rdma_aligned = 0;
-		iser_finalize_rdma_unaligned_sg(iser_ctask, ISER_DIR_IN);
+		iser_finalize_rdma_unaligned_sg(iser_task, ISER_DIR_IN);
 	}
-	if (iser_ctask->data_copy[ISER_DIR_OUT].copy_buf != NULL) {
+	if (iser_task->data_copy[ISER_DIR_OUT].copy_buf != NULL) {
 		is_rdma_aligned = 0;
-		iser_finalize_rdma_unaligned_sg(iser_ctask, ISER_DIR_OUT);
+		iser_finalize_rdma_unaligned_sg(iser_task, ISER_DIR_OUT);
 	}
 
-	if (iser_ctask->dir[ISER_DIR_IN]) {
-		regd = &iser_ctask->rdma_regd[ISER_DIR_IN];
+	if (iser_task->dir[ISER_DIR_IN]) {
+		regd = &iser_task->rdma_regd[ISER_DIR_IN];
 		deferred = iser_regd_buff_release(regd);
 		if (deferred) {
 			iser_err("%d references remain for BUF-IN rdma reg\n",
@@ -669,8 +668,8 @@
 		}
 	}
 
-	if (iser_ctask->dir[ISER_DIR_OUT]) {
-		regd = &iser_ctask->rdma_regd[ISER_DIR_OUT];
+	if (iser_task->dir[ISER_DIR_OUT]) {
+		regd = &iser_task->rdma_regd[ISER_DIR_OUT];
 		deferred = iser_regd_buff_release(regd);
 		if (deferred) {
 			iser_err("%d references remain for BUF-OUT rdma reg\n",
@@ -680,7 +679,7 @@
 
        /* if the data was unaligned, it was already unmapped and then copied */
        if (is_rdma_aligned)
-		iser_dma_unmap_task_data(iser_ctask);
+		iser_dma_unmap_task_data(iser_task);
 }
 
 void iser_dto_buffs_release(struct iser_dto *dto)
diff --git a/drivers/infiniband/ulp/iser/iser_memory.c b/drivers/infiniband/ulp/iser/iser_memory.c
index 81e49cb..b9453d0 100644
--- a/drivers/infiniband/ulp/iser/iser_memory.c
+++ b/drivers/infiniband/ulp/iser/iser_memory.c
@@ -99,13 +99,13 @@
 /**
  * iser_start_rdma_unaligned_sg
  */
-static int iser_start_rdma_unaligned_sg(struct iscsi_iser_cmd_task *iser_ctask,
+static int iser_start_rdma_unaligned_sg(struct iscsi_iser_task *iser_task,
 					enum iser_data_dir cmd_dir)
 {
 	int dma_nents;
 	struct ib_device *dev;
 	char *mem = NULL;
-	struct iser_data_buf *data = &iser_ctask->data[cmd_dir];
+	struct iser_data_buf *data = &iser_task->data[cmd_dir];
 	unsigned long  cmd_data_len = data->data_len;
 
 	if (cmd_data_len > ISER_KMALLOC_THRESHOLD)
@@ -138,37 +138,37 @@
 		}
 	}
 
-	sg_init_one(&iser_ctask->data_copy[cmd_dir].sg_single, mem, cmd_data_len);
-	iser_ctask->data_copy[cmd_dir].buf  =
-		&iser_ctask->data_copy[cmd_dir].sg_single;
-	iser_ctask->data_copy[cmd_dir].size = 1;
+	sg_init_one(&iser_task->data_copy[cmd_dir].sg_single, mem, cmd_data_len);
+	iser_task->data_copy[cmd_dir].buf  =
+		&iser_task->data_copy[cmd_dir].sg_single;
+	iser_task->data_copy[cmd_dir].size = 1;
 
-	iser_ctask->data_copy[cmd_dir].copy_buf  = mem;
+	iser_task->data_copy[cmd_dir].copy_buf  = mem;
 
-	dev = iser_ctask->iser_conn->ib_conn->device->ib_device;
+	dev = iser_task->iser_conn->ib_conn->device->ib_device;
 	dma_nents = ib_dma_map_sg(dev,
-				  &iser_ctask->data_copy[cmd_dir].sg_single,
+				  &iser_task->data_copy[cmd_dir].sg_single,
 				  1,
 				  (cmd_dir == ISER_DIR_OUT) ?
 				  DMA_TO_DEVICE : DMA_FROM_DEVICE);
 	BUG_ON(dma_nents == 0);
 
-	iser_ctask->data_copy[cmd_dir].dma_nents = dma_nents;
+	iser_task->data_copy[cmd_dir].dma_nents = dma_nents;
 	return 0;
 }
 
 /**
  * iser_finalize_rdma_unaligned_sg
  */
-void iser_finalize_rdma_unaligned_sg(struct iscsi_iser_cmd_task *iser_ctask,
+void iser_finalize_rdma_unaligned_sg(struct iscsi_iser_task *iser_task,
 				     enum iser_data_dir         cmd_dir)
 {
 	struct ib_device *dev;
 	struct iser_data_buf *mem_copy;
 	unsigned long  cmd_data_len;
 
-	dev = iser_ctask->iser_conn->ib_conn->device->ib_device;
-	mem_copy = &iser_ctask->data_copy[cmd_dir];
+	dev = iser_task->iser_conn->ib_conn->device->ib_device;
+	mem_copy = &iser_task->data_copy[cmd_dir];
 
 	ib_dma_unmap_sg(dev, &mem_copy->sg_single, 1,
 			(cmd_dir == ISER_DIR_OUT) ?
@@ -184,8 +184,8 @@
 		/* copy back read RDMA to unaligned sg */
 		mem	= mem_copy->copy_buf;
 
-		sgl	= (struct scatterlist *)iser_ctask->data[ISER_DIR_IN].buf;
-		sg_size = iser_ctask->data[ISER_DIR_IN].size;
+		sgl	= (struct scatterlist *)iser_task->data[ISER_DIR_IN].buf;
+		sg_size = iser_task->data[ISER_DIR_IN].size;
 
 		p = mem;
 		for_each_sg(sgl, sg, sg_size, i) {
@@ -198,7 +198,7 @@
 		}
 	}
 
-	cmd_data_len = iser_ctask->data[cmd_dir].data_len;
+	cmd_data_len = iser_task->data[cmd_dir].data_len;
 
 	if (cmd_data_len > ISER_KMALLOC_THRESHOLD)
 		free_pages((unsigned long)mem_copy->copy_buf,
@@ -376,15 +376,15 @@
 	}
 }
 
-int iser_dma_map_task_data(struct iscsi_iser_cmd_task *iser_ctask,
-			    struct iser_data_buf       *data,
-			    enum   iser_data_dir       iser_dir,
-			    enum   dma_data_direction  dma_dir)
+int iser_dma_map_task_data(struct iscsi_iser_task *iser_task,
+			    struct iser_data_buf *data,
+			    enum iser_data_dir iser_dir,
+			    enum dma_data_direction dma_dir)
 {
 	struct ib_device *dev;
 
-	iser_ctask->dir[iser_dir] = 1;
-	dev = iser_ctask->iser_conn->ib_conn->device->ib_device;
+	iser_task->dir[iser_dir] = 1;
+	dev = iser_task->iser_conn->ib_conn->device->ib_device;
 
 	data->dma_nents = ib_dma_map_sg(dev, data->buf, data->size, dma_dir);
 	if (data->dma_nents == 0) {
@@ -394,20 +394,20 @@
 	return 0;
 }
 
-void iser_dma_unmap_task_data(struct iscsi_iser_cmd_task *iser_ctask)
+void iser_dma_unmap_task_data(struct iscsi_iser_task *iser_task)
 {
 	struct ib_device *dev;
 	struct iser_data_buf *data;
 
-	dev = iser_ctask->iser_conn->ib_conn->device->ib_device;
+	dev = iser_task->iser_conn->ib_conn->device->ib_device;
 
-	if (iser_ctask->dir[ISER_DIR_IN]) {
-		data = &iser_ctask->data[ISER_DIR_IN];
+	if (iser_task->dir[ISER_DIR_IN]) {
+		data = &iser_task->data[ISER_DIR_IN];
 		ib_dma_unmap_sg(dev, data->buf, data->size, DMA_FROM_DEVICE);
 	}
 
-	if (iser_ctask->dir[ISER_DIR_OUT]) {
-		data = &iser_ctask->data[ISER_DIR_OUT];
+	if (iser_task->dir[ISER_DIR_OUT]) {
+		data = &iser_task->data[ISER_DIR_OUT];
 		ib_dma_unmap_sg(dev, data->buf, data->size, DMA_TO_DEVICE);
 	}
 }
@@ -418,21 +418,21 @@
  *
  * returns 0 on success, errno code on failure
  */
-int iser_reg_rdma_mem(struct iscsi_iser_cmd_task *iser_ctask,
+int iser_reg_rdma_mem(struct iscsi_iser_task *iser_task,
 		      enum   iser_data_dir        cmd_dir)
 {
-	struct iscsi_conn    *iscsi_conn = iser_ctask->iser_conn->iscsi_conn;
-	struct iser_conn     *ib_conn = iser_ctask->iser_conn->ib_conn;
+	struct iscsi_conn    *iscsi_conn = iser_task->iser_conn->iscsi_conn;
+	struct iser_conn     *ib_conn = iser_task->iser_conn->ib_conn;
 	struct iser_device   *device = ib_conn->device;
 	struct ib_device     *ibdev = device->ib_device;
-	struct iser_data_buf *mem = &iser_ctask->data[cmd_dir];
+	struct iser_data_buf *mem = &iser_task->data[cmd_dir];
 	struct iser_regd_buf *regd_buf;
 	int aligned_len;
 	int err;
 	int i;
 	struct scatterlist *sg;
 
-	regd_buf = &iser_ctask->rdma_regd[cmd_dir];
+	regd_buf = &iser_task->rdma_regd[cmd_dir];
 
 	aligned_len = iser_data_buf_aligned_len(mem, ibdev);
 	if (aligned_len != mem->dma_nents) {
@@ -442,13 +442,13 @@
 		iser_data_buf_dump(mem, ibdev);
 
 		/* unmap the command data before accessing it */
-		iser_dma_unmap_task_data(iser_ctask);
+		iser_dma_unmap_task_data(iser_task);
 
 		/* allocate copy buf, if we are writing, copy the */
 		/* unaligned scatterlist, dma map the copy        */
-		if (iser_start_rdma_unaligned_sg(iser_ctask, cmd_dir) != 0)
+		if (iser_start_rdma_unaligned_sg(iser_task, cmd_dir) != 0)
 				return -ENOMEM;
-		mem = &iser_ctask->data_copy[cmd_dir];
+		mem = &iser_task->data_copy[cmd_dir];
 	}
 
 	/* if there a single dma entry, FMR is not needed */
@@ -472,8 +472,9 @@
 		err = iser_reg_page_vec(ib_conn, ib_conn->page_vec, &regd_buf->reg);
 		if (err) {
 			iser_data_buf_dump(mem, ibdev);
-			iser_err("mem->dma_nents = %d (dlength = 0x%x)\n", mem->dma_nents,
-				 ntoh24(iser_ctask->desc.iscsi_header.dlength));
+			iser_err("mem->dma_nents = %d (dlength = 0x%x)\n",
+				 mem->dma_nents,
+				 ntoh24(iser_task->desc.iscsi_header.dlength));
 			iser_err("page_vec: data_size = 0x%x, length = %d, offset = 0x%x\n",
 				 ib_conn->page_vec->data_size, ib_conn->page_vec->length,
 				 ib_conn->page_vec->offset);
diff --git a/drivers/infiniband/ulp/iser/iser_verbs.c b/drivers/infiniband/ulp/iser/iser_verbs.c
index 77cabee..3a917c1 100644
--- a/drivers/infiniband/ulp/iser/iser_verbs.c
+++ b/drivers/infiniband/ulp/iser/iser_verbs.c
@@ -323,7 +323,18 @@
 		iser_device_try_release(device);
 	if (ib_conn->iser_conn)
 		ib_conn->iser_conn->ib_conn = NULL;
-	kfree(ib_conn);
+	iscsi_destroy_endpoint(ib_conn->ep);
+}
+
+void iser_conn_get(struct iser_conn *ib_conn)
+{
+	atomic_inc(&ib_conn->refcount);
+}
+
+void iser_conn_put(struct iser_conn *ib_conn)
+{
+	if (atomic_dec_and_test(&ib_conn->refcount))
+		iser_conn_release(ib_conn);
 }
 
 /**
@@ -347,7 +358,7 @@
 	wait_event_interruptible(ib_conn->wait,
 				 ib_conn->state == ISER_CONN_DOWN);
 
-	iser_conn_release(ib_conn);
+	iser_conn_put(ib_conn);
 }
 
 static void iser_connect_error(struct rdma_cm_id *cma_id)
@@ -481,24 +492,15 @@
 	return ret;
 }
 
-int iser_conn_init(struct iser_conn **ibconn)
+void iser_conn_init(struct iser_conn *ib_conn)
 {
-	struct iser_conn *ib_conn;
-
-	ib_conn = kzalloc(sizeof *ib_conn, GFP_KERNEL);
-	if (!ib_conn) {
-		iser_err("can't alloc memory for struct iser_conn\n");
-		return -ENOMEM;
-	}
 	ib_conn->state = ISER_CONN_INIT;
 	init_waitqueue_head(&ib_conn->wait);
 	atomic_set(&ib_conn->post_recv_buf_count, 0);
 	atomic_set(&ib_conn->post_send_buf_count, 0);
+	atomic_set(&ib_conn->refcount, 1);
 	INIT_LIST_HEAD(&ib_conn->conn_list);
 	spin_lock_init(&ib_conn->lock);
-
-	*ibconn = ib_conn;
-	return 0;
 }
 
  /**
diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig
index 610af91..07d92c1 100644
--- a/drivers/md/Kconfig
+++ b/drivers/md/Kconfig
@@ -252,27 +252,14 @@
 config DM_MULTIPATH
 	tristate "Multipath target"
 	depends on BLK_DEV_DM
+	# nasty syntax but means make DM_MULTIPATH independent
+	# of SCSI_DH if the latter isn't defined but if
+	# it is, DM_MULTIPATH must depend on it.  We get a build
+	# error if SCSI_DH=m and DM_MULTIPATH=y
+	depends on SCSI_DH || !SCSI_DH
 	---help---
 	  Allow volume managers to support multipath hardware.
 
-config DM_MULTIPATH_EMC
-	tristate "EMC CX/AX multipath support"
-	depends on DM_MULTIPATH && BLK_DEV_DM
-	---help---
-	  Multipath support for EMC CX/AX series hardware.
-
-config DM_MULTIPATH_RDAC
-	tristate "LSI/Engenio RDAC multipath support (EXPERIMENTAL)"
-	depends on DM_MULTIPATH && BLK_DEV_DM && SCSI && EXPERIMENTAL
-	---help---
-	  Multipath support for LSI/Engenio RDAC.
-
-config DM_MULTIPATH_HP
-        tristate "HP MSA multipath support (EXPERIMENTAL)"
-        depends on DM_MULTIPATH && BLK_DEV_DM && SCSI && EXPERIMENTAL
-        ---help---
-          Multipath support for HP MSA (Active/Passive) series hardware.
-
 config DM_DELAY
 	tristate "I/O delaying target (EXPERIMENTAL)"
 	depends on BLK_DEV_DM && EXPERIMENTAL
diff --git a/drivers/md/Makefile b/drivers/md/Makefile
index 7be09ee..f1ef33d 100644
--- a/drivers/md/Makefile
+++ b/drivers/md/Makefile
@@ -4,11 +4,9 @@
 
 dm-mod-objs	:= dm.o dm-table.o dm-target.o dm-linear.o dm-stripe.o \
 		   dm-ioctl.o dm-io.o dm-kcopyd.o
-dm-multipath-objs := dm-hw-handler.o dm-path-selector.o dm-mpath.o
+dm-multipath-objs := dm-path-selector.o dm-mpath.o
 dm-snapshot-objs := dm-snap.o dm-exception-store.o
 dm-mirror-objs	:= dm-raid1.o
-dm-rdac-objs	:= dm-mpath-rdac.o
-dm-hp-sw-objs	:= dm-mpath-hp-sw.o
 md-mod-objs     := md.o bitmap.o
 raid456-objs	:= raid5.o raid6algos.o raid6recov.o raid6tables.o \
 		   raid6int1.o raid6int2.o raid6int4.o \
@@ -35,9 +33,6 @@
 obj-$(CONFIG_DM_CRYPT)		+= dm-crypt.o
 obj-$(CONFIG_DM_DELAY)		+= dm-delay.o
 obj-$(CONFIG_DM_MULTIPATH)	+= dm-multipath.o dm-round-robin.o
-obj-$(CONFIG_DM_MULTIPATH_EMC)	+= dm-emc.o
-obj-$(CONFIG_DM_MULTIPATH_HP)	+= dm-hp-sw.o
-obj-$(CONFIG_DM_MULTIPATH_RDAC)	+= dm-rdac.o
 obj-$(CONFIG_DM_SNAPSHOT)	+= dm-snapshot.o
 obj-$(CONFIG_DM_MIRROR)		+= dm-mirror.o dm-log.o
 obj-$(CONFIG_DM_ZERO)		+= dm-zero.o
diff --git a/drivers/md/dm-emc.c b/drivers/md/dm-emc.c
deleted file mode 100644
index 3ea5ad4..0000000
--- a/drivers/md/dm-emc.c
+++ /dev/null
@@ -1,345 +0,0 @@
-/*
- * Copyright (C) 2004 SUSE LINUX Products GmbH. All rights reserved.
- * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
- *
- * This file is released under the GPL.
- *
- * Multipath support for EMC CLARiiON AX/CX-series hardware.
- */
-
-#include "dm.h"
-#include "dm-hw-handler.h"
-#include <scsi/scsi.h>
-#include <scsi/scsi_cmnd.h>
-
-#define DM_MSG_PREFIX "multipath emc"
-
-struct emc_handler {
-	spinlock_t lock;
-
-	/* Whether we should send the short trespass command (FC-series)
-	 * or the long version (default for AX/CX CLARiiON arrays). */
-	unsigned short_trespass;
-	/* Whether or not to honor SCSI reservations when initiating a
-	 * switch-over. Default: Don't. */
-	unsigned hr;
-
-	unsigned char sense[SCSI_SENSE_BUFFERSIZE];
-};
-
-#define TRESPASS_PAGE 0x22
-#define EMC_FAILOVER_TIMEOUT (60 * HZ)
-
-/* Code borrowed from dm-lsi-rdac by Mike Christie */
-
-static inline void free_bio(struct bio *bio)
-{
-	__free_page(bio->bi_io_vec[0].bv_page);
-	bio_put(bio);
-}
-
-static void emc_endio(struct bio *bio, int error)
-{
-	struct dm_path *path = bio->bi_private;
-
-	/* We also need to look at the sense keys here whether or not to
-	 * switch to the next PG etc.
-	 *
-	 * For now simple logic: either it works or it doesn't.
-	 */
-	if (error)
-		dm_pg_init_complete(path, MP_FAIL_PATH);
-	else
-		dm_pg_init_complete(path, 0);
-
-	/* request is freed in block layer */
-	free_bio(bio);
-}
-
-static struct bio *get_failover_bio(struct dm_path *path, unsigned data_size)
-{
-	struct bio *bio;
-	struct page *page;
-
-	bio = bio_alloc(GFP_ATOMIC, 1);
-	if (!bio) {
-		DMERR("get_failover_bio: bio_alloc() failed.");
-		return NULL;
-	}
-
-	bio->bi_rw |= (1 << BIO_RW);
-	bio->bi_bdev = path->dev->bdev;
-	bio->bi_sector = 0;
-	bio->bi_private = path;
-	bio->bi_end_io = emc_endio;
-
-	page = alloc_page(GFP_ATOMIC);
-	if (!page) {
-		DMERR("get_failover_bio: alloc_page() failed.");
-		bio_put(bio);
-		return NULL;
-	}
-
-	if (bio_add_page(bio, page, data_size, 0) != data_size) {
-		DMERR("get_failover_bio: bio_add_page() failed.");
-		__free_page(page);
-		bio_put(bio);
-		return NULL;
-	}
-
-	return bio;
-}
-
-static struct request *get_failover_req(struct emc_handler *h,
-					struct bio *bio, struct dm_path *path)
-{
-	struct request *rq;
-	struct block_device *bdev = bio->bi_bdev;
-	struct request_queue *q = bdev_get_queue(bdev);
-
-	/* FIXME: Figure out why it fails with GFP_ATOMIC. */
-	rq = blk_get_request(q, WRITE, __GFP_WAIT);
-	if (!rq) {
-		DMERR("get_failover_req: blk_get_request failed");
-		return NULL;
-	}
-
-	blk_rq_append_bio(q, rq, bio);
-
-	rq->sense = h->sense;
-	memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE);
-	rq->sense_len = 0;
-
-	rq->timeout = EMC_FAILOVER_TIMEOUT;
-	rq->cmd_type = REQ_TYPE_BLOCK_PC;
-	rq->cmd_flags |= REQ_FAILFAST | REQ_NOMERGE;
-
-	return rq;
-}
-
-static struct request *emc_trespass_get(struct emc_handler *h,
-					struct dm_path *path)
-{
-	struct bio *bio;
-	struct request *rq;
-	unsigned char *page22;
-	unsigned char long_trespass_pg[] = {
-		0, 0, 0, 0,
-		TRESPASS_PAGE,        /* Page code */
-		0x09,                 /* Page length - 2 */
-		h->hr ? 0x01 : 0x81,  /* Trespass code + Honor reservation bit */
-		0xff, 0xff,           /* Trespass target */
-		0, 0, 0, 0, 0, 0      /* Reserved bytes / unknown */
-		};
-	unsigned char short_trespass_pg[] = {
-		0, 0, 0, 0,
-		TRESPASS_PAGE,        /* Page code */
-		0x02,                 /* Page length - 2 */
-		h->hr ? 0x01 : 0x81,  /* Trespass code + Honor reservation bit */
-		0xff,                 /* Trespass target */
-		};
-	unsigned data_size = h->short_trespass ? sizeof(short_trespass_pg) :
-				sizeof(long_trespass_pg);
-
-	/* get bio backing */
-	if (data_size > PAGE_SIZE)
-		/* this should never happen */
-		return NULL;
-
-	bio = get_failover_bio(path, data_size);
-	if (!bio) {
-		DMERR("emc_trespass_get: no bio");
-		return NULL;
-	}
-
-	page22 = (unsigned char *)bio_data(bio);
-	memset(page22, 0, data_size);
-
-	memcpy(page22, h->short_trespass ?
-		short_trespass_pg : long_trespass_pg, data_size);
-
-	/* get request for block layer packet command */
-	rq = get_failover_req(h, bio, path);
-	if (!rq) {
-		DMERR("emc_trespass_get: no rq");
-		free_bio(bio);
-		return NULL;
-	}
-
-	/* Prepare the command. */
-	rq->cmd[0] = MODE_SELECT;
-	rq->cmd[1] = 0x10;
-	rq->cmd[4] = data_size;
-	rq->cmd_len = COMMAND_SIZE(rq->cmd[0]);
-
-	return rq;
-}
-
-static void emc_pg_init(struct hw_handler *hwh, unsigned bypassed,
-			struct dm_path *path)
-{
-	struct request *rq;
-	struct request_queue *q = bdev_get_queue(path->dev->bdev);
-
-	/*
-	 * We can either blindly init the pg (then look at the sense),
-	 * or we can send some commands to get the state here (then
-	 * possibly send the fo cmnd), or we can also have the
-	 * initial state passed into us and then get an update here.
-	 */
-	if (!q) {
-		DMINFO("emc_pg_init: no queue");
-		goto fail_path;
-	}
-
-	/* FIXME: The request should be pre-allocated. */
-	rq = emc_trespass_get(hwh->context, path);
-	if (!rq) {
-		DMERR("emc_pg_init: no rq");
-		goto fail_path;
-	}
-
-	DMINFO("emc_pg_init: sending switch-over command");
-	elv_add_request(q, rq, ELEVATOR_INSERT_FRONT, 1);
-	return;
-
-fail_path:
-	dm_pg_init_complete(path, MP_FAIL_PATH);
-}
-
-static struct emc_handler *alloc_emc_handler(void)
-{
-	struct emc_handler *h = kzalloc(sizeof(*h), GFP_KERNEL);
-
-	if (h)
-		spin_lock_init(&h->lock);
-
-	return h;
-}
-
-static int emc_create(struct hw_handler *hwh, unsigned argc, char **argv)
-{
-	struct emc_handler *h;
-	unsigned hr, short_trespass;
-
-	if (argc == 0) {
-		/* No arguments: use defaults */
-		hr = 0;
-		short_trespass = 0;
-	} else if (argc != 2) {
-		DMWARN("incorrect number of arguments");
-		return -EINVAL;
-	} else {
-		if ((sscanf(argv[0], "%u", &short_trespass) != 1)
-			|| (short_trespass > 1)) {
-			DMWARN("invalid trespass mode selected");
-			return -EINVAL;
-		}
-
-		if ((sscanf(argv[1], "%u", &hr) != 1)
-			|| (hr > 1)) {
-			DMWARN("invalid honor reservation flag selected");
-			return -EINVAL;
-		}
-	}
-
-	h = alloc_emc_handler();
-	if (!h)
-		return -ENOMEM;
-
-	hwh->context = h;
-
-	if ((h->short_trespass = short_trespass))
-		DMWARN("short trespass command will be send");
-	else
-		DMWARN("long trespass command will be send");
-
-	if ((h->hr = hr))
-		DMWARN("honor reservation bit will be set");
-	else
-		DMWARN("honor reservation bit will not be set (default)");
-
-	return 0;
-}
-
-static void emc_destroy(struct hw_handler *hwh)
-{
-	struct emc_handler *h = (struct emc_handler *) hwh->context;
-
-	kfree(h);
-	hwh->context = NULL;
-}
-
-static unsigned emc_error(struct hw_handler *hwh, struct bio *bio)
-{
-	/* FIXME: Patch from axboe still missing */
-#if 0
-	int sense;
-
-	if (bio->bi_error & BIO_SENSE) {
-		sense = bio->bi_error & 0xffffff; /* sense key / asc / ascq */
-
-		if (sense == 0x020403) {
-			/* LUN Not Ready - Manual Intervention Required
-			 * indicates this is a passive path.
-			 *
-			 * FIXME: However, if this is seen and EVPD C0
-			 * indicates that this is due to a NDU in
-			 * progress, we should set FAIL_PATH too.
-			 * This indicates we might have to do a SCSI
-			 * inquiry in the end_io path. Ugh. */
-			return MP_BYPASS_PG | MP_RETRY_IO;
-		} else if (sense == 0x052501) {
-			/* An array based copy is in progress. Do not
-			 * fail the path, do not bypass to another PG,
-			 * do not retry. Fail the IO immediately.
-			 * (Actually this is the same conclusion as in
-			 * the default handler, but lets make sure.) */
-			return 0;
-		} else if (sense == 0x062900) {
-			/* Unit Attention Code. This is the first IO
-			 * to the new path, so just retry. */
-			return MP_RETRY_IO;
-		}
-	}
-#endif
-
-	/* Try default handler */
-	return dm_scsi_err_handler(hwh, bio);
-}
-
-static struct hw_handler_type emc_hwh = {
-	.name = "emc",
-	.module = THIS_MODULE,
-	.create = emc_create,
-	.destroy = emc_destroy,
-	.pg_init = emc_pg_init,
-	.error = emc_error,
-};
-
-static int __init dm_emc_init(void)
-{
-	int r = dm_register_hw_handler(&emc_hwh);
-
-	if (r < 0)
-		DMERR("register failed %d", r);
-
-	DMINFO("version 0.0.3 loaded");
-
-	return r;
-}
-
-static void __exit dm_emc_exit(void)
-{
-	int r = dm_unregister_hw_handler(&emc_hwh);
-
-	if (r < 0)
-		DMERR("unregister failed %d", r);
-}
-
-module_init(dm_emc_init);
-module_exit(dm_emc_exit);
-
-MODULE_DESCRIPTION(DM_NAME " EMC CX/AX/FC-family multipath");
-MODULE_AUTHOR("Lars Marowsky-Bree <lmb@suse.de>");
-MODULE_LICENSE("GPL");
diff --git a/drivers/md/dm-hw-handler.c b/drivers/md/dm-hw-handler.c
deleted file mode 100644
index 2ee84d8..0000000
--- a/drivers/md/dm-hw-handler.c
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
- * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
- *
- * This file is released under the GPL.
- *
- * Multipath hardware handler registration.
- */
-
-#include "dm.h"
-#include "dm-hw-handler.h"
-
-#include <linux/slab.h>
-
-struct hwh_internal {
-	struct hw_handler_type hwht;
-
-	struct list_head list;
-	long use;
-};
-
-#define hwht_to_hwhi(__hwht) container_of((__hwht), struct hwh_internal, hwht)
-
-static LIST_HEAD(_hw_handlers);
-static DECLARE_RWSEM(_hwh_lock);
-
-static struct hwh_internal *__find_hw_handler_type(const char *name)
-{
-	struct hwh_internal *hwhi;
-
-	list_for_each_entry(hwhi, &_hw_handlers, list) {
-		if (!strcmp(name, hwhi->hwht.name))
-			return hwhi;
-	}
-
-	return NULL;
-}
-
-static struct hwh_internal *get_hw_handler(const char *name)
-{
-	struct hwh_internal *hwhi;
-
-	down_read(&_hwh_lock);
-	hwhi = __find_hw_handler_type(name);
-	if (hwhi) {
-		if ((hwhi->use == 0) && !try_module_get(hwhi->hwht.module))
-			hwhi = NULL;
-		else
-			hwhi->use++;
-	}
-	up_read(&_hwh_lock);
-
-	return hwhi;
-}
-
-struct hw_handler_type *dm_get_hw_handler(const char *name)
-{
-	struct hwh_internal *hwhi;
-
-	if (!name)
-		return NULL;
-
-	hwhi = get_hw_handler(name);
-	if (!hwhi) {
-		request_module("dm-%s", name);
-		hwhi = get_hw_handler(name);
-	}
-
-	return hwhi ? &hwhi->hwht : NULL;
-}
-
-void dm_put_hw_handler(struct hw_handler_type *hwht)
-{
-	struct hwh_internal *hwhi;
-
-	if (!hwht)
-		return;
-
-	down_read(&_hwh_lock);
-	hwhi = __find_hw_handler_type(hwht->name);
-	if (!hwhi)
-		goto out;
-
-	if (--hwhi->use == 0)
-		module_put(hwhi->hwht.module);
-
-	BUG_ON(hwhi->use < 0);
-
-      out:
-	up_read(&_hwh_lock);
-}
-
-static struct hwh_internal *_alloc_hw_handler(struct hw_handler_type *hwht)
-{
-	struct hwh_internal *hwhi = kzalloc(sizeof(*hwhi), GFP_KERNEL);
-
-	if (hwhi)
-		hwhi->hwht = *hwht;
-
-	return hwhi;
-}
-
-int dm_register_hw_handler(struct hw_handler_type *hwht)
-{
-	int r = 0;
-	struct hwh_internal *hwhi = _alloc_hw_handler(hwht);
-
-	if (!hwhi)
-		return -ENOMEM;
-
-	down_write(&_hwh_lock);
-
-	if (__find_hw_handler_type(hwht->name)) {
-		kfree(hwhi);
-		r = -EEXIST;
-	} else
-		list_add(&hwhi->list, &_hw_handlers);
-
-	up_write(&_hwh_lock);
-
-	return r;
-}
-
-int dm_unregister_hw_handler(struct hw_handler_type *hwht)
-{
-	struct hwh_internal *hwhi;
-
-	down_write(&_hwh_lock);
-
-	hwhi = __find_hw_handler_type(hwht->name);
-	if (!hwhi) {
-		up_write(&_hwh_lock);
-		return -EINVAL;
-	}
-
-	if (hwhi->use) {
-		up_write(&_hwh_lock);
-		return -ETXTBSY;
-	}
-
-	list_del(&hwhi->list);
-
-	up_write(&_hwh_lock);
-
-	kfree(hwhi);
-
-	return 0;
-}
-
-unsigned dm_scsi_err_handler(struct hw_handler *hwh, struct bio *bio)
-{
-#if 0
-	int sense_key, asc, ascq;
-
-	if (bio->bi_error & BIO_SENSE) {
-		/* FIXME: This is just an initial guess. */
-		/* key / asc / ascq */
-		sense_key = (bio->bi_error >> 16) & 0xff;
-		asc = (bio->bi_error >> 8) & 0xff;
-		ascq = bio->bi_error & 0xff;
-
-		switch (sense_key) {
-			/* This block as a whole comes from the device.
-			 * So no point retrying on another path. */
-		case 0x03:	/* Medium error */
-		case 0x05:	/* Illegal request */
-		case 0x07:	/* Data protect */
-		case 0x08:	/* Blank check */
-		case 0x0a:	/* copy aborted */
-		case 0x0c:	/* obsolete - no clue ;-) */
-		case 0x0d:	/* volume overflow */
-		case 0x0e:	/* data miscompare */
-		case 0x0f:	/* reserved - no idea either. */
-			return MP_ERROR_IO;
-
-			/* For these errors it's unclear whether they
-			 * come from the device or the controller.
-			 * So just lets try a different path, and if
-			 * it eventually succeeds, user-space will clear
-			 * the paths again... */
-		case 0x02:	/* Not ready */
-		case 0x04:	/* Hardware error */
-		case 0x09:	/* vendor specific */
-		case 0x0b:	/* Aborted command */
-			return MP_FAIL_PATH;
-
-		case 0x06:	/* Unit attention - might want to decode */
-			if (asc == 0x04 && ascq == 0x01)
-				/* "Unit in the process of
-				 * becoming ready" */
-				return 0;
-			return MP_FAIL_PATH;
-
-			/* FIXME: For Unit Not Ready we may want
-			 * to have a generic pg activation
-			 * feature (START_UNIT). */
-
-			/* Should these two ever end up in the
-			 * error path? I don't think so. */
-		case 0x00:	/* No sense */
-		case 0x01:	/* Recovered error */
-			return 0;
-		}
-	}
-#endif
-
-	/* We got no idea how to decode the other kinds of errors ->
-	 * assume generic error condition. */
-	return MP_FAIL_PATH;
-}
-
-EXPORT_SYMBOL_GPL(dm_register_hw_handler);
-EXPORT_SYMBOL_GPL(dm_unregister_hw_handler);
-EXPORT_SYMBOL_GPL(dm_scsi_err_handler);
diff --git a/drivers/md/dm-hw-handler.h b/drivers/md/dm-hw-handler.h
deleted file mode 100644
index 46809dc..0000000
--- a/drivers/md/dm-hw-handler.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
- *
- * This file is released under the GPL.
- *
- * Multipath hardware handler registration.
- */
-
-#ifndef	DM_HW_HANDLER_H
-#define	DM_HW_HANDLER_H
-
-#include <linux/device-mapper.h>
-
-#include "dm-mpath.h"
-
-struct hw_handler_type;
-struct hw_handler {
-	struct hw_handler_type *type;
-	struct mapped_device *md;
-	void *context;
-};
-
-/*
- * Constructs a hardware handler object, takes custom arguments
- */
-/* Information about a hardware handler type */
-struct hw_handler_type {
-	char *name;
-	struct module *module;
-
-	int (*create) (struct hw_handler *handler, unsigned int argc,
-		       char **argv);
-	void (*destroy) (struct hw_handler *hwh);
-
-	void (*pg_init) (struct hw_handler *hwh, unsigned bypassed,
-			 struct dm_path *path);
-	unsigned (*error) (struct hw_handler *hwh, struct bio *bio);
-	int (*status) (struct hw_handler *hwh, status_type_t type,
-		       char *result, unsigned int maxlen);
-};
-
-/* Register a hardware handler */
-int dm_register_hw_handler(struct hw_handler_type *type);
-
-/* Unregister a hardware handler */
-int dm_unregister_hw_handler(struct hw_handler_type *type);
-
-/* Returns a registered hardware handler type */
-struct hw_handler_type *dm_get_hw_handler(const char *name);
-
-/* Releases a hardware handler  */
-void dm_put_hw_handler(struct hw_handler_type *hwht);
-
-/* Default err function */
-unsigned dm_scsi_err_handler(struct hw_handler *hwh, struct bio *bio);
-
-/* Error flags for err and dm_pg_init_complete */
-#define MP_FAIL_PATH 1
-#define MP_BYPASS_PG 2
-#define MP_ERROR_IO  4	/* Don't retry this I/O */
-#define MP_RETRY 8
-
-#endif
diff --git a/drivers/md/dm-mpath-hp-sw.c b/drivers/md/dm-mpath-hp-sw.c
deleted file mode 100644
index b63a0ab..0000000
--- a/drivers/md/dm-mpath-hp-sw.c
+++ /dev/null
@@ -1,247 +0,0 @@
-/*
- * Copyright (C) 2005 Mike Christie, All rights reserved.
- * Copyright (C) 2007 Red Hat, Inc. All rights reserved.
- * Authors: Mike Christie
- *          Dave Wysochanski
- *
- * This file is released under the GPL.
- *
- * This module implements the specific path activation code for
- * HP StorageWorks and FSC FibreCat Asymmetric (Active/Passive)
- * storage arrays.
- * These storage arrays have controller-based failover, not
- * LUN-based failover.  However, LUN-based failover is the design
- * of dm-multipath. Thus, this module is written for LUN-based failover.
- */
-#include <linux/blkdev.h>
-#include <linux/list.h>
-#include <linux/types.h>
-#include <scsi/scsi.h>
-#include <scsi/scsi_cmnd.h>
-#include <scsi/scsi_dbg.h>
-
-#include "dm.h"
-#include "dm-hw-handler.h"
-
-#define DM_MSG_PREFIX "multipath hp-sw"
-#define DM_HP_HWH_NAME "hp-sw"
-#define DM_HP_HWH_VER "1.0.0"
-
-struct hp_sw_context {
-	unsigned char sense[SCSI_SENSE_BUFFERSIZE];
-};
-
-/*
- * hp_sw_error_is_retryable - Is an HP-specific check condition retryable?
- * @req: path activation request
- *
- * Examine error codes of request and determine whether the error is retryable.
- * Some error codes are already retried by scsi-ml (see
- * scsi_decide_disposition), but some HP specific codes are not.
- * The intent of this routine is to supply the logic for the HP specific
- * check conditions.
- *
- * Returns:
- *  1 - command completed with retryable error
- *  0 - command completed with non-retryable error
- *
- * Possible optimizations
- * 1. More hardware-specific error codes
- */
-static int hp_sw_error_is_retryable(struct request *req)
-{
-	/*
-	 * NOT_READY is known to be retryable
-	 * For now we just dump out the sense data and call it retryable
-	 */
-	if (status_byte(req->errors) == CHECK_CONDITION)
-		__scsi_print_sense(DM_HP_HWH_NAME, req->sense, req->sense_len);
-
-	/*
-	 * At this point we don't have complete information about all the error
-	 * codes from this hardware, so we are just conservative and retry
-	 * when in doubt.
-	 */
-	return 1;
-}
-
-/*
- * hp_sw_end_io - Completion handler for HP path activation.
- * @req: path activation request
- * @error: scsi-ml error
- *
- *  Check sense data, free request structure, and notify dm that
- *  pg initialization has completed.
- *
- * Context: scsi-ml softirq
- *
- */
-static void hp_sw_end_io(struct request *req, int error)
-{
-	struct dm_path *path = req->end_io_data;
-	unsigned err_flags = 0;
-
-	if (!error) {
-		DMDEBUG("%s path activation command - success",
-			path->dev->name);
-		goto out;
-	}
-
-	if (hp_sw_error_is_retryable(req)) {
-		DMDEBUG("%s path activation command - retry",
-			path->dev->name);
-		err_flags = MP_RETRY;
-		goto out;
-	}
-
-	DMWARN("%s path activation fail - error=0x%x",
-	       path->dev->name, error);
-	err_flags = MP_FAIL_PATH;
-
-out:
-	req->end_io_data = NULL;
-	__blk_put_request(req->q, req);
-	dm_pg_init_complete(path, err_flags);
-}
-
-/*
- * hp_sw_get_request - Allocate an HP specific path activation request
- * @path: path on which request will be sent (needed for request queue)
- *
- * The START command is used for path activation request.
- * These arrays are controller-based failover, not LUN based.
- * One START command issued to a single path will fail over all
- * LUNs for the same controller.
- *
- * Possible optimizations
- * 1. Make timeout configurable
- * 2. Preallocate request
- */
-static struct request *hp_sw_get_request(struct dm_path *path)
-{
-	struct request *req;
-	struct block_device *bdev = path->dev->bdev;
-	struct request_queue *q = bdev_get_queue(bdev);
-	struct hp_sw_context *h = path->hwhcontext;
-
-	req = blk_get_request(q, WRITE, GFP_NOIO);
-	if (!req)
-		goto out;
-
-	req->timeout = 60 * HZ;
-
-	req->errors = 0;
-	req->cmd_type = REQ_TYPE_BLOCK_PC;
-	req->cmd_flags |= REQ_FAILFAST | REQ_NOMERGE;
-	req->end_io_data = path;
-	req->sense = h->sense;
-	memset(req->sense, 0, SCSI_SENSE_BUFFERSIZE);
-
-	req->cmd[0] = START_STOP;
-	req->cmd[4] = 1;
-	req->cmd_len = COMMAND_SIZE(req->cmd[0]);
-
-out:
-	return req;
-}
-
-/*
- * hp_sw_pg_init - HP path activation implementation.
- * @hwh: hardware handler specific data
- * @bypassed: unused; is the path group bypassed? (see dm-mpath.c)
- * @path: path to send initialization command
- *
- * Send an HP-specific path activation command on 'path'.
- * Do not try to optimize in any way, just send the activation command.
- * More than one path activation command may be sent to the same controller.
- * This seems to work fine for basic failover support.
- *
- * Possible optimizations
- * 1. Detect an in-progress activation request and avoid submitting another one
- * 2. Model the controller and only send a single activation request at a time
- * 3. Determine the state of a path before sending an activation request
- *
- * Context: kmpathd (see process_queued_ios() in dm-mpath.c)
- */
-static void hp_sw_pg_init(struct hw_handler *hwh, unsigned bypassed,
-			  struct dm_path *path)
-{
-	struct request *req;
-	struct hp_sw_context *h;
-
-	path->hwhcontext = hwh->context;
-	h = hwh->context;
-
-	req = hp_sw_get_request(path);
-	if (!req) {
-		DMERR("%s path activation command - allocation fail",
-		      path->dev->name);
-		goto retry;
-	}
-
-	DMDEBUG("%s path activation command - sent", path->dev->name);
-
-	blk_execute_rq_nowait(req->q, NULL, req, 1, hp_sw_end_io);
-	return;
-
-retry:
-	dm_pg_init_complete(path, MP_RETRY);
-}
-
-static int hp_sw_create(struct hw_handler *hwh, unsigned argc, char **argv)
-{
-	struct hp_sw_context *h;
-
-	h = kmalloc(sizeof(*h), GFP_KERNEL);
-	if (!h)
-		return -ENOMEM;
-
-	hwh->context = h;
-
-	return 0;
-}
-
-static void hp_sw_destroy(struct hw_handler *hwh)
-{
-	struct hp_sw_context *h = hwh->context;
-
-	kfree(h);
-}
-
-static struct hw_handler_type hp_sw_hwh = {
-	.name = DM_HP_HWH_NAME,
-	.module = THIS_MODULE,
-	.create = hp_sw_create,
-	.destroy = hp_sw_destroy,
-	.pg_init = hp_sw_pg_init,
-};
-
-static int __init hp_sw_init(void)
-{
-	int r;
-
-	r = dm_register_hw_handler(&hp_sw_hwh);
-	if (r < 0)
-		DMERR("register failed %d", r);
-	else
-		DMINFO("version " DM_HP_HWH_VER " loaded");
-
-	return r;
-}
-
-static void __exit hp_sw_exit(void)
-{
-	int r;
-
-	r = dm_unregister_hw_handler(&hp_sw_hwh);
-	if (r < 0)
-		DMERR("unregister failed %d", r);
-}
-
-module_init(hp_sw_init);
-module_exit(hp_sw_exit);
-
-MODULE_DESCRIPTION("DM Multipath HP StorageWorks / FSC FibreCat (A/P) support");
-MODULE_AUTHOR("Mike Christie, Dave Wysochanski <dm-devel@redhat.com>");
-MODULE_LICENSE("GPL");
-MODULE_VERSION(DM_HP_HWH_VER);
diff --git a/drivers/md/dm-mpath-rdac.c b/drivers/md/dm-mpath-rdac.c
deleted file mode 100644
index 95e7773..0000000
--- a/drivers/md/dm-mpath-rdac.c
+++ /dev/null
@@ -1,700 +0,0 @@
-/*
- * Engenio/LSI RDAC DM HW handler
- *
- * Copyright (C) 2005 Mike Christie. All rights reserved.
- * Copyright (C) Chandra Seetharaman, IBM Corp. 2007
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- */
-#include <scsi/scsi.h>
-#include <scsi/scsi_cmnd.h>
-#include <scsi/scsi_eh.h>
-
-#define DM_MSG_PREFIX "multipath rdac"
-
-#include "dm.h"
-#include "dm-hw-handler.h"
-
-#define RDAC_DM_HWH_NAME "rdac"
-#define RDAC_DM_HWH_VER "0.4"
-
-/*
- * LSI mode page stuff
- *
- * These struct definitions and the forming of the
- * mode page were taken from the LSI RDAC 2.4 GPL'd
- * driver, and then converted to Linux conventions.
- */
-#define RDAC_QUIESCENCE_TIME 20;
-/*
- * Page Codes
- */
-#define RDAC_PAGE_CODE_REDUNDANT_CONTROLLER 0x2c
-
-/*
- * Controller modes definitions
- */
-#define RDAC_MODE_TRANSFER_ALL_LUNS		0x01
-#define RDAC_MODE_TRANSFER_SPECIFIED_LUNS	0x02
-
-/*
- * RDAC Options field
- */
-#define RDAC_FORCED_QUIESENCE 0x02
-
-#define RDAC_FAILOVER_TIMEOUT (60 * HZ)
-
-struct rdac_mode_6_hdr {
-	u8	data_len;
-	u8	medium_type;
-	u8	device_params;
-	u8	block_desc_len;
-};
-
-struct rdac_mode_10_hdr {
-	u16	data_len;
-	u8	medium_type;
-	u8	device_params;
-	u16	reserved;
-	u16	block_desc_len;
-};
-
-struct rdac_mode_common {
-	u8	controller_serial[16];
-	u8	alt_controller_serial[16];
-	u8	rdac_mode[2];
-	u8	alt_rdac_mode[2];
-	u8	quiescence_timeout;
-	u8	rdac_options;
-};
-
-struct rdac_pg_legacy {
-	struct rdac_mode_6_hdr hdr;
-	u8	page_code;
-	u8	page_len;
-	struct rdac_mode_common common;
-#define MODE6_MAX_LUN	32
-	u8	lun_table[MODE6_MAX_LUN];
-	u8	reserved2[32];
-	u8	reserved3;
-	u8	reserved4;
-};
-
-struct rdac_pg_expanded {
-	struct rdac_mode_10_hdr hdr;
-	u8	page_code;
-	u8	subpage_code;
-	u8	page_len[2];
-	struct rdac_mode_common common;
-	u8	lun_table[256];
-	u8	reserved3;
-	u8	reserved4;
-};
-
-struct c9_inquiry {
-	u8	peripheral_info;
-	u8	page_code;	/* 0xC9 */
-	u8	reserved1;
-	u8	page_len;
-	u8	page_id[4];	/* "vace" */
-	u8	avte_cvp;
-	u8	path_prio;
-	u8	reserved2[38];
-};
-
-#define SUBSYS_ID_LEN	16
-#define SLOT_ID_LEN	2
-
-struct c4_inquiry {
-	u8	peripheral_info;
-	u8	page_code;	/* 0xC4 */
-	u8	reserved1;
-	u8	page_len;
-	u8	page_id[4];	/* "subs" */
-	u8	subsys_id[SUBSYS_ID_LEN];
-	u8	revision[4];
-	u8	slot_id[SLOT_ID_LEN];
-	u8	reserved[2];
-};
-
-struct rdac_controller {
-	u8			subsys_id[SUBSYS_ID_LEN];
-	u8			slot_id[SLOT_ID_LEN];
-	int			use_10_ms;
-	struct kref		kref;
-	struct list_head	node; /* list of all controllers */
-	spinlock_t		lock;
-	int			submitted;
-	struct list_head	cmd_list; /* list of commands to be submitted */
-	union			{
-		struct rdac_pg_legacy legacy;
-		struct rdac_pg_expanded expanded;
-	} mode_select;
-};
-struct c8_inquiry {
-	u8	peripheral_info;
-	u8	page_code; /* 0xC8 */
-	u8	reserved1;
-	u8	page_len;
-	u8	page_id[4]; /* "edid" */
-	u8	reserved2[3];
-	u8	vol_uniq_id_len;
-	u8	vol_uniq_id[16];
-	u8	vol_user_label_len;
-	u8	vol_user_label[60];
-	u8	array_uniq_id_len;
-	u8	array_unique_id[16];
-	u8	array_user_label_len;
-	u8	array_user_label[60];
-	u8	lun[8];
-};
-
-struct c2_inquiry {
-	u8	peripheral_info;
-	u8	page_code;	/* 0xC2 */
-	u8	reserved1;
-	u8	page_len;
-	u8	page_id[4];	/* "swr4" */
-	u8	sw_version[3];
-	u8	sw_date[3];
-	u8	features_enabled;
-	u8	max_lun_supported;
-	u8	partitions[239]; /* Total allocation length should be 0xFF */
-};
-
-struct rdac_handler {
-	struct list_head	entry; /* list waiting to submit MODE SELECT */
-	unsigned		timeout;
-	struct rdac_controller	*ctlr;
-#define UNINITIALIZED_LUN	(1 << 8)
-	unsigned		lun;
-	unsigned char		sense[SCSI_SENSE_BUFFERSIZE];
-	struct dm_path		*path;
-	struct work_struct	work;
-#define	SEND_C2_INQUIRY		1
-#define	SEND_C4_INQUIRY		2
-#define	SEND_C8_INQUIRY		3
-#define	SEND_C9_INQUIRY		4
-#define	SEND_MODE_SELECT	5
-	int			cmd_to_send;
-	union			{
-		struct c2_inquiry c2;
-		struct c4_inquiry c4;
-		struct c8_inquiry c8;
-		struct c9_inquiry c9;
-	} inq;
-};
-
-static LIST_HEAD(ctlr_list);
-static DEFINE_SPINLOCK(list_lock);
-static struct workqueue_struct *rdac_wkqd;
-
-static inline int had_failures(struct request *req, int error)
-{
-	return (error || host_byte(req->errors) != DID_OK ||
-			msg_byte(req->errors) != COMMAND_COMPLETE);
-}
-
-static void rdac_resubmit_all(struct rdac_handler *h)
-{
-	struct rdac_controller *ctlr = h->ctlr;
-	struct rdac_handler *tmp, *h1;
-
-	spin_lock(&ctlr->lock);
-	list_for_each_entry_safe(h1, tmp, &ctlr->cmd_list, entry) {
-		h1->cmd_to_send = SEND_C9_INQUIRY;
-		queue_work(rdac_wkqd, &h1->work);
-		list_del(&h1->entry);
-	}
-	ctlr->submitted = 0;
-	spin_unlock(&ctlr->lock);
-}
-
-static void mode_select_endio(struct request *req, int error)
-{
-	struct rdac_handler *h = req->end_io_data;
-	struct scsi_sense_hdr sense_hdr;
-	int sense = 0, fail = 0;
-
-	if (had_failures(req, error)) {
-		fail = 1;
-		goto failed;
-	}
-
-	if (status_byte(req->errors) == CHECK_CONDITION) {
-		scsi_normalize_sense(req->sense, SCSI_SENSE_BUFFERSIZE,
-				&sense_hdr);
-		sense = (sense_hdr.sense_key << 16) | (sense_hdr.asc << 8) |
-				sense_hdr.ascq;
-		/* If it is retryable failure, submit the c9 inquiry again */
-		if (sense == 0x59136 || sense == 0x68b02 || sense == 0xb8b02 ||
-		    sense == 0x62900) {
-			/* 0x59136    - Command lock contention
-			 * 0x[6b]8b02 - Quiesense in progress or achieved
-			 * 0x62900    - Power On, Reset, or Bus Device Reset
-			 */
-			h->cmd_to_send = SEND_C9_INQUIRY;
-			queue_work(rdac_wkqd, &h->work);
-			goto done;
-		}
-		if (sense)
-			DMINFO("MODE_SELECT failed on %s with sense 0x%x",
-						h->path->dev->name, sense);
- 	}
-failed:
-	if (fail || sense)
-		dm_pg_init_complete(h->path, MP_FAIL_PATH);
-	else
-		dm_pg_init_complete(h->path, 0);
-
-done:
-	rdac_resubmit_all(h);
-	__blk_put_request(req->q, req);
-}
-
-static struct request *get_rdac_req(struct rdac_handler *h,
-			void *buffer, unsigned buflen, int rw)
-{
-	struct request *rq;
-	struct request_queue *q = bdev_get_queue(h->path->dev->bdev);
-
-	rq = blk_get_request(q, rw, GFP_KERNEL);
-
-	if (!rq) {
-		DMINFO("get_rdac_req: blk_get_request failed");
-		return NULL;
-	}
-
-	if (buflen && blk_rq_map_kern(q, rq, buffer, buflen, GFP_KERNEL)) {
-		blk_put_request(rq);
-		DMINFO("get_rdac_req: blk_rq_map_kern failed");
-		return NULL;
-	}
-
-	rq->sense = h->sense;
-	memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE);
-	rq->sense_len = 0;
-
-	rq->end_io_data = h;
-	rq->timeout = h->timeout;
-	rq->cmd_type = REQ_TYPE_BLOCK_PC;
-	rq->cmd_flags |= REQ_FAILFAST | REQ_NOMERGE;
-	return rq;
-}
-
-static struct request *rdac_failover_get(struct rdac_handler *h)
-{
-	struct request *rq;
-	struct rdac_mode_common *common;
-	unsigned data_size;
-
-	if (h->ctlr->use_10_ms) {
-		struct rdac_pg_expanded *rdac_pg;
-
-		data_size = sizeof(struct rdac_pg_expanded);
-		rdac_pg = &h->ctlr->mode_select.expanded;
-		memset(rdac_pg, 0, data_size);
-		common = &rdac_pg->common;
-		rdac_pg->page_code = RDAC_PAGE_CODE_REDUNDANT_CONTROLLER + 0x40;
-		rdac_pg->subpage_code = 0x1;
-		rdac_pg->page_len[0] = 0x01;
-		rdac_pg->page_len[1] = 0x28;
-		rdac_pg->lun_table[h->lun] = 0x81;
-	} else {
-		struct rdac_pg_legacy *rdac_pg;
-
-		data_size = sizeof(struct rdac_pg_legacy);
-		rdac_pg = &h->ctlr->mode_select.legacy;
-		memset(rdac_pg, 0, data_size);
-		common = &rdac_pg->common;
-		rdac_pg->page_code = RDAC_PAGE_CODE_REDUNDANT_CONTROLLER;
-		rdac_pg->page_len = 0x68;
-		rdac_pg->lun_table[h->lun] = 0x81;
-	}
-	common->rdac_mode[1] = RDAC_MODE_TRANSFER_SPECIFIED_LUNS;
-	common->quiescence_timeout = RDAC_QUIESCENCE_TIME;
-	common->rdac_options = RDAC_FORCED_QUIESENCE;
-
-	/* get request for block layer packet command */
-	rq = get_rdac_req(h, &h->ctlr->mode_select, data_size, WRITE);
-	if (!rq) {
-		DMERR("rdac_failover_get: no rq");
-		return NULL;
-	}
-
-	/* Prepare the command. */
-	if (h->ctlr->use_10_ms) {
-		rq->cmd[0] = MODE_SELECT_10;
-		rq->cmd[7] = data_size >> 8;
-		rq->cmd[8] = data_size & 0xff;
-	} else {
-		rq->cmd[0] = MODE_SELECT;
-		rq->cmd[4] = data_size;
-	}
-	rq->cmd_len = COMMAND_SIZE(rq->cmd[0]);
-
-	return rq;
-}
-
-/* Acquires h->ctlr->lock */
-static void submit_mode_select(struct rdac_handler *h)
-{
-	struct request *rq;
-	struct request_queue *q = bdev_get_queue(h->path->dev->bdev);
-
-	spin_lock(&h->ctlr->lock);
-	if (h->ctlr->submitted) {
-		list_add(&h->entry, &h->ctlr->cmd_list);
-		goto drop_lock;
-	}
-
-	if (!q) {
-		DMINFO("submit_mode_select: no queue");
-		goto fail_path;
-	}
-
-	rq = rdac_failover_get(h);
-	if (!rq) {
-		DMERR("submit_mode_select: no rq");
-		goto fail_path;
-	}
-
-	DMINFO("queueing MODE_SELECT command on %s", h->path->dev->name);
-
-	blk_execute_rq_nowait(q, NULL, rq, 1, mode_select_endio);
-	h->ctlr->submitted = 1;
-	goto drop_lock;
-fail_path:
-	dm_pg_init_complete(h->path, MP_FAIL_PATH);
-drop_lock:
-	spin_unlock(&h->ctlr->lock);
-}
-
-static void release_ctlr(struct kref *kref)
-{
-	struct rdac_controller *ctlr;
-	ctlr = container_of(kref, struct rdac_controller, kref);
-
-	spin_lock(&list_lock);
-	list_del(&ctlr->node);
-	spin_unlock(&list_lock);
-	kfree(ctlr);
-}
-
-static struct rdac_controller *get_controller(u8 *subsys_id, u8 *slot_id)
-{
-	struct rdac_controller *ctlr, *tmp;
-
-	spin_lock(&list_lock);
-
-	list_for_each_entry(tmp, &ctlr_list, node) {
-		if ((memcmp(tmp->subsys_id, subsys_id, SUBSYS_ID_LEN) == 0) &&
-			  (memcmp(tmp->slot_id, slot_id, SLOT_ID_LEN) == 0)) {
-			kref_get(&tmp->kref);
-			spin_unlock(&list_lock);
-			return tmp;
-		}
-	}
-	ctlr = kmalloc(sizeof(*ctlr), GFP_ATOMIC);
-	if (!ctlr)
-		goto done;
-
-	/* initialize fields of controller */
-	memcpy(ctlr->subsys_id, subsys_id, SUBSYS_ID_LEN);
-	memcpy(ctlr->slot_id, slot_id, SLOT_ID_LEN);
-	kref_init(&ctlr->kref);
-	spin_lock_init(&ctlr->lock);
-	ctlr->submitted = 0;
-	ctlr->use_10_ms = -1;
-	INIT_LIST_HEAD(&ctlr->cmd_list);
-	list_add(&ctlr->node, &ctlr_list);
-done:
-	spin_unlock(&list_lock);
-	return ctlr;
-}
-
-static void c4_endio(struct request *req, int error)
-{
-	struct rdac_handler *h = req->end_io_data;
-	struct c4_inquiry *sp;
-
-	if (had_failures(req, error)) {
-		dm_pg_init_complete(h->path, MP_FAIL_PATH);
-		goto done;
-	}
-
-	sp = &h->inq.c4;
-
-	h->ctlr = get_controller(sp->subsys_id, sp->slot_id);
-
-	if (h->ctlr) {
-		h->cmd_to_send = SEND_C9_INQUIRY;
-		queue_work(rdac_wkqd, &h->work);
-	} else
-		dm_pg_init_complete(h->path, MP_FAIL_PATH);
-done:
-	__blk_put_request(req->q, req);
-}
-
-static void c2_endio(struct request *req, int error)
-{
-	struct rdac_handler *h = req->end_io_data;
-	struct c2_inquiry *sp;
-
-	if (had_failures(req, error)) {
-		dm_pg_init_complete(h->path, MP_FAIL_PATH);
-		goto done;
-	}
-
-	sp = &h->inq.c2;
-
-	/* If more than MODE6_MAX_LUN luns are supported, use mode select 10 */
-	if (sp->max_lun_supported >= MODE6_MAX_LUN)
-		h->ctlr->use_10_ms = 1;
-	else
-		h->ctlr->use_10_ms = 0;
-
-	h->cmd_to_send = SEND_MODE_SELECT;
-	queue_work(rdac_wkqd, &h->work);
-done:
-	__blk_put_request(req->q, req);
-}
-
-static void c9_endio(struct request *req, int error)
-{
-	struct rdac_handler *h = req->end_io_data;
-	struct c9_inquiry *sp;
-
-	if (had_failures(req, error)) {
-		dm_pg_init_complete(h->path, MP_FAIL_PATH);
-		goto done;
-	}
-
-	/* We need to look at the sense keys here to take clear action.
-	 * For now simple logic: If the host is in AVT mode or if controller
-	 * owns the lun, return dm_pg_init_complete(), otherwise submit
-	 * MODE SELECT.
-	 */
-	sp = &h->inq.c9;
-
-	/* If in AVT mode, return success */
-	if ((sp->avte_cvp >> 7) == 0x1) {
-		dm_pg_init_complete(h->path, 0);
-		goto done;
-	}
-
-	/* If the controller on this path owns the LUN, return success */
-	if (sp->avte_cvp & 0x1) {
-		dm_pg_init_complete(h->path, 0);
-		goto done;
-	}
-
-	if (h->ctlr) {
-		if (h->ctlr->use_10_ms == -1)
-			h->cmd_to_send = SEND_C2_INQUIRY;
-		else
-			h->cmd_to_send = SEND_MODE_SELECT;
-	} else
-		h->cmd_to_send = SEND_C4_INQUIRY;
-	queue_work(rdac_wkqd, &h->work);
-done:
-	__blk_put_request(req->q, req);
-}
-
-static void c8_endio(struct request *req, int error)
-{
-	struct rdac_handler *h = req->end_io_data;
-	struct c8_inquiry *sp;
-
-	if (had_failures(req, error)) {
-		dm_pg_init_complete(h->path, MP_FAIL_PATH);
-		goto done;
-	}
-
-	/* We need to look at the sense keys here to take clear action.
-	 * For now simple logic: Get the lun from the inquiry page.
-	 */
-	sp = &h->inq.c8;
-	h->lun = sp->lun[7]; /* currently it uses only one byte */
-	h->cmd_to_send = SEND_C9_INQUIRY;
-	queue_work(rdac_wkqd, &h->work);
-done:
-	__blk_put_request(req->q, req);
-}
-
-static void submit_inquiry(struct rdac_handler *h, int page_code,
-		unsigned int len, rq_end_io_fn endio)
-{
-	struct request *rq;
-	struct request_queue *q = bdev_get_queue(h->path->dev->bdev);
-
-	if (!q)
-		goto fail_path;
-
-	rq = get_rdac_req(h, &h->inq, len, READ);
-	if (!rq)
-		goto fail_path;
-
-	/* Prepare the command. */
-	rq->cmd[0] = INQUIRY;
-	rq->cmd[1] = 1;
-	rq->cmd[2] = page_code;
-	rq->cmd[4] = len;
-	rq->cmd_len = COMMAND_SIZE(INQUIRY);
-	blk_execute_rq_nowait(q, NULL, rq, 1, endio);
-	return;
-
-fail_path:
-	dm_pg_init_complete(h->path, MP_FAIL_PATH);
-}
-
-static void service_wkq(struct work_struct *work)
-{
-	struct rdac_handler *h = container_of(work, struct rdac_handler, work);
-
-	switch (h->cmd_to_send) {
-	case SEND_C2_INQUIRY:
-		submit_inquiry(h, 0xC2, sizeof(struct c2_inquiry), c2_endio);
-		break;
-	case SEND_C4_INQUIRY:
-		submit_inquiry(h, 0xC4, sizeof(struct c4_inquiry), c4_endio);
-		break;
-	case SEND_C8_INQUIRY:
-		submit_inquiry(h, 0xC8, sizeof(struct c8_inquiry), c8_endio);
-		break;
-	case SEND_C9_INQUIRY:
-		submit_inquiry(h, 0xC9, sizeof(struct c9_inquiry), c9_endio);
-		break;
-	case SEND_MODE_SELECT:
-		submit_mode_select(h);
-		break;
-	default:
-		BUG();
-	}
-}
-/*
- * only support subpage2c until we confirm that this is just a matter of
- * of updating firmware or not, and RDAC (basic AVT works already) for now
- * but we can add these in in when we get time and testers
- */
-static int rdac_create(struct hw_handler *hwh, unsigned argc, char **argv)
-{
-	struct rdac_handler *h;
-	unsigned timeout;
-
-	if (argc == 0) {
-		/* No arguments: use defaults */
-		timeout = RDAC_FAILOVER_TIMEOUT;
-	} else if (argc != 1) {
-		DMWARN("incorrect number of arguments");
-		return -EINVAL;
-	} else {
-		if (sscanf(argv[1], "%u", &timeout) != 1) {
-			DMWARN("invalid timeout value");
-			return -EINVAL;
-		}
-	}
-
-	h = kzalloc(sizeof(*h), GFP_KERNEL);
-	if (!h)
-		return -ENOMEM;
-
-	hwh->context = h;
-	h->timeout = timeout;
-	h->lun = UNINITIALIZED_LUN;
-	INIT_WORK(&h->work, service_wkq);
-	DMWARN("using RDAC command with timeout %u", h->timeout);
-
-	return 0;
-}
-
-static void rdac_destroy(struct hw_handler *hwh)
-{
-	struct rdac_handler *h = hwh->context;
-
-	if (h->ctlr)
-		kref_put(&h->ctlr->kref, release_ctlr);
-	kfree(h);
-	hwh->context = NULL;
-}
-
-static unsigned rdac_error(struct hw_handler *hwh, struct bio *bio)
-{
-	/* Try default handler */
-	return dm_scsi_err_handler(hwh, bio);
-}
-
-static void rdac_pg_init(struct hw_handler *hwh, unsigned bypassed,
-			struct dm_path *path)
-{
-	struct rdac_handler *h = hwh->context;
-
-	h->path = path;
-	switch (h->lun) {
-	case UNINITIALIZED_LUN:
-		submit_inquiry(h, 0xC8, sizeof(struct c8_inquiry), c8_endio);
-		break;
-	default:
-		submit_inquiry(h, 0xC9, sizeof(struct c9_inquiry), c9_endio);
-	}
-}
-
-static struct hw_handler_type rdac_handler = {
-	.name = RDAC_DM_HWH_NAME,
-	.module = THIS_MODULE,
-	.create = rdac_create,
-	.destroy = rdac_destroy,
-	.pg_init = rdac_pg_init,
-	.error = rdac_error,
-};
-
-static int __init rdac_init(void)
-{
-	int r;
-
-	rdac_wkqd = create_singlethread_workqueue("rdac_wkqd");
-	if (!rdac_wkqd) {
-		DMERR("Failed to create workqueue rdac_wkqd.");
-		return -ENOMEM;
-	}
-
-	r = dm_register_hw_handler(&rdac_handler);
-	if (r < 0) {
-		DMERR("%s: register failed %d", RDAC_DM_HWH_NAME, r);
-		destroy_workqueue(rdac_wkqd);
-		return r;
-	}
-
-	DMINFO("%s: version %s loaded", RDAC_DM_HWH_NAME, RDAC_DM_HWH_VER);
-	return 0;
-}
-
-static void __exit rdac_exit(void)
-{
-	int r = dm_unregister_hw_handler(&rdac_handler);
-
-	destroy_workqueue(rdac_wkqd);
-	if (r < 0)
-		DMERR("%s: unregister failed %d", RDAC_DM_HWH_NAME, r);
-}
-
-module_init(rdac_init);
-module_exit(rdac_exit);
-
-MODULE_DESCRIPTION("DM Multipath LSI/Engenio RDAC support");
-MODULE_AUTHOR("Mike Christie, Chandra Seetharaman");
-MODULE_LICENSE("GPL");
-MODULE_VERSION(RDAC_DM_HWH_VER);
diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c
index e7ee59e..9f7302d 100644
--- a/drivers/md/dm-mpath.c
+++ b/drivers/md/dm-mpath.c
@@ -7,7 +7,6 @@
 
 #include "dm.h"
 #include "dm-path-selector.h"
-#include "dm-hw-handler.h"
 #include "dm-bio-list.h"
 #include "dm-bio-record.h"
 #include "dm-uevent.h"
@@ -20,6 +19,7 @@
 #include <linux/slab.h>
 #include <linux/time.h>
 #include <linux/workqueue.h>
+#include <scsi/scsi_dh.h>
 #include <asm/atomic.h>
 
 #define DM_MSG_PREFIX "multipath"
@@ -61,7 +61,8 @@
 
 	spinlock_t lock;
 
-	struct hw_handler hw_handler;
+	const char *hw_handler_name;
+	struct work_struct activate_path;
 	unsigned nr_priority_groups;
 	struct list_head priority_groups;
 	unsigned pg_init_required;	/* pg_init needs calling? */
@@ -106,9 +107,10 @@
 
 static struct kmem_cache *_mpio_cache;
 
-static struct workqueue_struct *kmultipathd;
+static struct workqueue_struct *kmultipathd, *kmpath_handlerd;
 static void process_queued_ios(struct work_struct *work);
 static void trigger_event(struct work_struct *work);
+static void activate_path(struct work_struct *work);
 
 
 /*-----------------------------------------------
@@ -178,6 +180,7 @@
 		m->queue_io = 1;
 		INIT_WORK(&m->process_queued_ios, process_queued_ios);
 		INIT_WORK(&m->trigger_event, trigger_event);
+		INIT_WORK(&m->activate_path, activate_path);
 		m->mpio_pool = mempool_create_slab_pool(MIN_IOS, _mpio_cache);
 		if (!m->mpio_pool) {
 			kfree(m);
@@ -193,18 +196,13 @@
 static void free_multipath(struct multipath *m)
 {
 	struct priority_group *pg, *tmp;
-	struct hw_handler *hwh = &m->hw_handler;
 
 	list_for_each_entry_safe(pg, tmp, &m->priority_groups, list) {
 		list_del(&pg->list);
 		free_priority_group(pg, m->ti);
 	}
 
-	if (hwh->type) {
-		hwh->type->destroy(hwh);
-		dm_put_hw_handler(hwh->type);
-	}
-
+	kfree(m->hw_handler_name);
 	mempool_destroy(m->mpio_pool);
 	kfree(m);
 }
@@ -216,12 +214,10 @@
 
 static void __switch_pg(struct multipath *m, struct pgpath *pgpath)
 {
-	struct hw_handler *hwh = &m->hw_handler;
-
 	m->current_pg = pgpath->pg;
 
 	/* Must we initialise the PG first, and queue I/O till it's ready? */
-	if (hwh->type && hwh->type->pg_init) {
+	if (m->hw_handler_name) {
 		m->pg_init_required = 1;
 		m->queue_io = 1;
 	} else {
@@ -409,7 +405,6 @@
 {
 	struct multipath *m =
 		container_of(work, struct multipath, process_queued_ios);
-	struct hw_handler *hwh = &m->hw_handler;
 	struct pgpath *pgpath = NULL;
 	unsigned init_required = 0, must_queue = 1;
 	unsigned long flags;
@@ -439,7 +434,7 @@
 	spin_unlock_irqrestore(&m->lock, flags);
 
 	if (init_required)
-		hwh->type->pg_init(hwh, pgpath->pg->bypassed, &pgpath->path);
+		queue_work(kmpath_handlerd, &m->activate_path);
 
 	if (!must_queue)
 		dispatch_queued_ios(m);
@@ -652,8 +647,6 @@
 
 static int parse_hw_handler(struct arg_set *as, struct multipath *m)
 {
-	int r;
-	struct hw_handler_type *hwht;
 	unsigned hw_argc;
 	struct dm_target *ti = m->ti;
 
@@ -661,30 +654,20 @@
 		{0, 1024, "invalid number of hardware handler args"},
 	};
 
-	r = read_param(_params, shift(as), &hw_argc, &ti->error);
-	if (r)
+	if (read_param(_params, shift(as), &hw_argc, &ti->error))
 		return -EINVAL;
 
 	if (!hw_argc)
 		return 0;
 
-	hwht = dm_get_hw_handler(shift(as));
-	if (!hwht) {
+	m->hw_handler_name = kstrdup(shift(as), GFP_KERNEL);
+	request_module("scsi_dh_%s", m->hw_handler_name);
+	if (scsi_dh_handler_exist(m->hw_handler_name) == 0) {
 		ti->error = "unknown hardware handler type";
+		kfree(m->hw_handler_name);
+		m->hw_handler_name = NULL;
 		return -EINVAL;
 	}
-
-	m->hw_handler.md = dm_table_get_md(ti->table);
-	dm_put(m->hw_handler.md);
-
-	r = hwht->create(&m->hw_handler, hw_argc - 1, as->argv);
-	if (r) {
-		dm_put_hw_handler(hwht);
-		ti->error = "hardware handler constructor failed";
-		return r;
-	}
-
-	m->hw_handler.type = hwht;
 	consume(as, hw_argc - 1);
 
 	return 0;
@@ -808,6 +791,7 @@
 {
 	struct multipath *m = (struct multipath *) ti->private;
 
+	flush_workqueue(kmpath_handlerd);
 	flush_workqueue(kmultipathd);
 	free_multipath(m);
 }
@@ -1025,52 +1009,85 @@
 	return limit_reached;
 }
 
-/*
- * pg_init must call this when it has completed its initialisation
- */
-void dm_pg_init_complete(struct dm_path *path, unsigned err_flags)
+static void pg_init_done(struct dm_path *path, int errors)
 {
 	struct pgpath *pgpath = path_to_pgpath(path);
 	struct priority_group *pg = pgpath->pg;
 	struct multipath *m = pg->m;
 	unsigned long flags;
 
-	/*
-	 * If requested, retry pg_init until maximum number of retries exceeded.
-	 * If retry not requested and PG already bypassed, always fail the path.
-	 */
-	if (err_flags & MP_RETRY) {
-		if (pg_init_limit_reached(m, pgpath))
-			err_flags |= MP_FAIL_PATH;
-	} else if (err_flags && pg->bypassed)
-		err_flags |= MP_FAIL_PATH;
-
-	if (err_flags & MP_FAIL_PATH)
+	/* device or driver problems */
+	switch (errors) {
+	case SCSI_DH_OK:
+		break;
+	case SCSI_DH_NOSYS:
+		if (!m->hw_handler_name) {
+			errors = 0;
+			break;
+		}
+		DMERR("Cannot failover device because scsi_dh_%s was not "
+		      "loaded.", m->hw_handler_name);
+		/*
+		 * Fail path for now, so we do not ping pong
+		 */
 		fail_path(pgpath);
-
-	if (err_flags & MP_BYPASS_PG)
+		break;
+	case SCSI_DH_DEV_TEMP_BUSY:
+		/*
+		 * Probably doing something like FW upgrade on the
+		 * controller so try the other pg.
+		 */
 		bypass_pg(m, pg, 1);
+		break;
+	/* TODO: For SCSI_DH_RETRY we should wait a couple seconds */
+	case SCSI_DH_RETRY:
+	case SCSI_DH_IMM_RETRY:
+	case SCSI_DH_RES_TEMP_UNAVAIL:
+		if (pg_init_limit_reached(m, pgpath))
+			fail_path(pgpath);
+		errors = 0;
+		break;
+	default:
+		/*
+		 * We probably do not want to fail the path for a device
+		 * error, but this is what the old dm did. In future
+		 * patches we can do more advanced handling.
+		 */
+		fail_path(pgpath);
+	}
 
 	spin_lock_irqsave(&m->lock, flags);
-	if (err_flags & ~MP_RETRY) {
+	if (errors) {
+		DMERR("Could not failover device. Error %d.", errors);
 		m->current_pgpath = NULL;
 		m->current_pg = NULL;
-	} else if (!m->pg_init_required)
+	} else if (!m->pg_init_required) {
 		m->queue_io = 0;
+		pg->bypassed = 0;
+	}
 
 	m->pg_init_in_progress = 0;
 	queue_work(kmultipathd, &m->process_queued_ios);
 	spin_unlock_irqrestore(&m->lock, flags);
 }
 
+static void activate_path(struct work_struct *work)
+{
+	int ret;
+	struct multipath *m =
+		container_of(work, struct multipath, activate_path);
+	struct dm_path *path = &m->current_pgpath->path;
+
+	ret = scsi_dh_activate(bdev_get_queue(path->dev->bdev));
+	pg_init_done(path, ret);
+}
+
 /*
  * end_io handling
  */
 static int do_end_io(struct multipath *m, struct bio *bio,
 		     int error, struct dm_mpath_io *mpio)
 {
-	struct hw_handler *hwh = &m->hw_handler;
-	unsigned err_flags = MP_FAIL_PATH;	/* Default behavior */
 	unsigned long flags;
 
 	if (!error)
@@ -1097,19 +1114,8 @@
 	}
 	spin_unlock_irqrestore(&m->lock, flags);
 
-	if (hwh->type && hwh->type->error)
-		err_flags = hwh->type->error(hwh, bio);
-
-	if (mpio->pgpath) {
-		if (err_flags & MP_FAIL_PATH)
-			fail_path(mpio->pgpath);
-
-		if (err_flags & MP_BYPASS_PG)
-			bypass_pg(m, mpio->pgpath->pg, 1);
-	}
-
-	if (err_flags & MP_ERROR_IO)
-		return -EIO;
+	if (mpio->pgpath)
+		fail_path(mpio->pgpath);
 
       requeue:
 	dm_bio_restore(&mpio->details, bio);
@@ -1194,7 +1200,6 @@
 	int sz = 0;
 	unsigned long flags;
 	struct multipath *m = (struct multipath *) ti->private;
-	struct hw_handler *hwh = &m->hw_handler;
 	struct priority_group *pg;
 	struct pgpath *p;
 	unsigned pg_num;
@@ -1214,12 +1219,10 @@
 			DMEMIT("pg_init_retries %u ", m->pg_init_retries);
 	}
 
-	if (hwh->type && hwh->type->status)
-		sz += hwh->type->status(hwh, type, result + sz, maxlen - sz);
-	else if (!hwh->type || type == STATUSTYPE_INFO)
+	if (!m->hw_handler_name || type == STATUSTYPE_INFO)
 		DMEMIT("0 ");
 	else
-		DMEMIT("1 %s ", hwh->type->name);
+		DMEMIT("1 %s ", m->hw_handler_name);
 
 	DMEMIT("%u ", m->nr_priority_groups);
 
@@ -1422,6 +1425,21 @@
 		return -ENOMEM;
 	}
 
+	/*
+	 * A separate workqueue is used to handle the device handlers
+	 * to avoid overloading existing workqueue. Overloading the
+	 * old workqueue would also create a bottleneck in the
+	 * path of the storage hardware device activation.
+	 */
+	kmpath_handlerd = create_singlethread_workqueue("kmpath_handlerd");
+	if (!kmpath_handlerd) {
+		DMERR("failed to create workqueue kmpath_handlerd");
+		destroy_workqueue(kmultipathd);
+		dm_unregister_target(&multipath_target);
+		kmem_cache_destroy(_mpio_cache);
+		return -ENOMEM;
+	}
+
 	DMINFO("version %u.%u.%u loaded",
 	       multipath_target.version[0], multipath_target.version[1],
 	       multipath_target.version[2]);
@@ -1433,6 +1451,7 @@
 {
 	int r;
 
+	destroy_workqueue(kmpath_handlerd);
 	destroy_workqueue(kmultipathd);
 
 	r = dm_unregister_target(&multipath_target);
@@ -1441,8 +1460,6 @@
 	kmem_cache_destroy(_mpio_cache);
 }
 
-EXPORT_SYMBOL_GPL(dm_pg_init_complete);
-
 module_init(dm_multipath_init);
 module_exit(dm_multipath_exit);
 
diff --git a/drivers/md/dm-mpath.h b/drivers/md/dm-mpath.h
index b9cdcbb..c198b85 100644
--- a/drivers/md/dm-mpath.h
+++ b/drivers/md/dm-mpath.h
@@ -16,7 +16,6 @@
 	unsigned is_active;	/* Read-only */
 
 	void *pscontext;	/* For path-selector use */
-	void *hwhcontext;	/* For hw-handler use */
 };
 
 /* Callback for hwh_pg_init_fn to use when complete */
diff --git a/drivers/message/fusion/lsi/mpi.h b/drivers/message/fusion/lsi/mpi.h
index 1acbdd61..10b6ef7 100644
--- a/drivers/message/fusion/lsi/mpi.h
+++ b/drivers/message/fusion/lsi/mpi.h
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2000-2007 LSI Corporation.
+ *  Copyright (c) 2000-2008 LSI Corporation.
  *
  *
  *           Name:  mpi.h
diff --git a/drivers/message/fusion/lsi/mpi_cnfg.h b/drivers/message/fusion/lsi/mpi_cnfg.h
index 2bd8ada..b2db333 100644
--- a/drivers/message/fusion/lsi/mpi_cnfg.h
+++ b/drivers/message/fusion/lsi/mpi_cnfg.h
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2000-2007 LSI Corporation.
+ *  Copyright (c) 2000-2008 LSI Corporation.
  *
  *
  *           Name:  mpi_cnfg.h
diff --git a/drivers/message/fusion/mptbase.c b/drivers/message/fusion/mptbase.c
index d40d6d1..75e599b 100644
--- a/drivers/message/fusion/mptbase.c
+++ b/drivers/message/fusion/mptbase.c
@@ -5,7 +5,7 @@
  *      For use with LSI PCI chip/adapter(s)
  *      running LSI Fusion MPT (Message Passing Technology) firmware.
  *
- *  Copyright (c) 1999-2007 LSI Corporation
+ *  Copyright (c) 1999-2008 LSI Corporation
  *  (mailto:DL-MPTFusionLinux@lsi.com)
  *
  */
@@ -103,7 +103,7 @@
  *  Public data...
  */
 
-struct proc_dir_entry *mpt_proc_root_dir;
+static struct proc_dir_entry *mpt_proc_root_dir;
 
 #define WHOINIT_UNKNOWN		0xAA
 
@@ -253,6 +253,55 @@
 	return 0;
 }
 
+/**
+ *	mpt_fault_reset_work - work performed on workq after ioc fault
+ *	@work: input argument, used to derive ioc
+ *
+**/
+static void
+mpt_fault_reset_work(struct work_struct *work)
+{
+	MPT_ADAPTER	*ioc =
+	    container_of(work, MPT_ADAPTER, fault_reset_work.work);
+	u32		 ioc_raw_state;
+	int		 rc;
+	unsigned long	 flags;
+
+	if (ioc->diagPending || !ioc->active)
+		goto out;
+
+	ioc_raw_state = mpt_GetIocState(ioc, 0);
+	if ((ioc_raw_state & MPI_IOC_STATE_MASK) == MPI_IOC_STATE_FAULT) {
+		printk(MYIOC_s_WARN_FMT "IOC is in FAULT state (%04xh)!!!\n",
+		    ioc->name, ioc_raw_state & MPI_DOORBELL_DATA_MASK);
+		printk(MYIOC_s_WARN_FMT "Issuing HardReset from %s!!\n",
+		    ioc->name, __FUNCTION__);
+		rc = mpt_HardResetHandler(ioc, CAN_SLEEP);
+		printk(MYIOC_s_WARN_FMT "%s: HardReset: %s\n", ioc->name,
+		    __FUNCTION__, (rc == 0) ? "success" : "failed");
+		ioc_raw_state = mpt_GetIocState(ioc, 0);
+		if ((ioc_raw_state & MPI_IOC_STATE_MASK) == MPI_IOC_STATE_FAULT)
+			printk(MYIOC_s_WARN_FMT "IOC is in FAULT state after "
+			    "reset (%04xh)\n", ioc->name, ioc_raw_state &
+			    MPI_DOORBELL_DATA_MASK);
+	}
+
+ out:
+	/*
+	 * Take turns polling alternate controller
+	 */
+	if (ioc->alt_ioc)
+		ioc = ioc->alt_ioc;
+
+	/* rearm the timer */
+	spin_lock_irqsave(&ioc->fault_reset_work_lock, flags);
+	if (ioc->reset_work_q)
+		queue_delayed_work(ioc->reset_work_q, &ioc->fault_reset_work,
+			msecs_to_jiffies(MPT_POLLING_INTERVAL));
+	spin_unlock_irqrestore(&ioc->fault_reset_work_lock, flags);
+}
+
+
 /*
  *  Process turbo (context) reply...
  */
@@ -1616,6 +1665,22 @@
 	/* Find lookup slot. */
 	INIT_LIST_HEAD(&ioc->list);
 
+
+	/* Initialize workqueue */
+	INIT_DELAYED_WORK(&ioc->fault_reset_work, mpt_fault_reset_work);
+	spin_lock_init(&ioc->fault_reset_work_lock);
+
+	snprintf(ioc->reset_work_q_name, KOBJ_NAME_LEN, "mpt_poll_%d", ioc->id);
+	ioc->reset_work_q =
+		create_singlethread_workqueue(ioc->reset_work_q_name);
+	if (!ioc->reset_work_q) {
+		printk(MYIOC_s_ERR_FMT "Insufficient memory to add adapter!\n",
+		    ioc->name);
+		pci_release_selected_regions(pdev, ioc->bars);
+		kfree(ioc);
+		return -ENOMEM;
+	}
+
 	dinitprintk(ioc, printk(MYIOC_s_INFO_FMT "facts @ %p, pfacts[0] @ %p\n",
 	    ioc->name, &ioc->facts, &ioc->pfacts[0]));
 
@@ -1727,6 +1792,10 @@
 		iounmap(ioc->memmap);
 		if (r != -5)
 			pci_release_selected_regions(pdev, ioc->bars);
+
+		destroy_workqueue(ioc->reset_work_q);
+		ioc->reset_work_q = NULL;
+
 		kfree(ioc);
 		pci_set_drvdata(pdev, NULL);
 		return r;
@@ -1759,6 +1828,10 @@
 	}
 #endif
 
+	if (!ioc->alt_ioc)
+		queue_delayed_work(ioc->reset_work_q, &ioc->fault_reset_work,
+			msecs_to_jiffies(MPT_POLLING_INTERVAL));
+
 	return 0;
 }
 
@@ -1774,6 +1847,19 @@
 	MPT_ADAPTER 	*ioc = pci_get_drvdata(pdev);
 	char pname[32];
 	u8 cb_idx;
+	unsigned long flags;
+	struct workqueue_struct *wq;
+
+	/*
+	 * Stop polling ioc for fault condition
+	 */
+	spin_lock_irqsave(&ioc->fault_reset_work_lock, flags);
+	wq = ioc->reset_work_q;
+	ioc->reset_work_q = NULL;
+	spin_unlock_irqrestore(&ioc->fault_reset_work_lock, flags);
+	cancel_delayed_work(&ioc->fault_reset_work);
+	destroy_workqueue(wq);
+
 
 	sprintf(pname, MPT_PROCFS_MPTBASEDIR "/%s/summary", ioc->name);
 	remove_proc_entry(pname, NULL);
@@ -7456,7 +7542,6 @@
 EXPORT_SYMBOL(mpt_suspend);
 #endif
 EXPORT_SYMBOL(ioc_list);
-EXPORT_SYMBOL(mpt_proc_root_dir);
 EXPORT_SYMBOL(mpt_register);
 EXPORT_SYMBOL(mpt_deregister);
 EXPORT_SYMBOL(mpt_event_register);
diff --git a/drivers/message/fusion/mptbase.h b/drivers/message/fusion/mptbase.h
index a8f6174..6adab64 100644
--- a/drivers/message/fusion/mptbase.h
+++ b/drivers/message/fusion/mptbase.h
@@ -5,7 +5,7 @@
  *          LSIFC9xx/LSI409xx Fibre Channel
  *      running LSI Fusion MPT (Message Passing Technology) firmware.
  *
- *  Copyright (c) 1999-2007 LSI Corporation
+ *  Copyright (c) 1999-2008 LSI Corporation
  *  (mailto:DL-MPTFusionLinux@lsi.com)
  *
  */
@@ -73,11 +73,11 @@
 #endif
 
 #ifndef COPYRIGHT
-#define COPYRIGHT	"Copyright (c) 1999-2007 " MODULEAUTHOR
+#define COPYRIGHT	"Copyright (c) 1999-2008 " MODULEAUTHOR
 #endif
 
-#define MPT_LINUX_VERSION_COMMON	"3.04.06"
-#define MPT_LINUX_PACKAGE_NAME		"@(#)mptlinux-3.04.06"
+#define MPT_LINUX_VERSION_COMMON	"3.04.07"
+#define MPT_LINUX_PACKAGE_NAME		"@(#)mptlinux-3.04.07"
 #define WHAT_MAGIC_STRING		"@" "(" "#" ")"
 
 #define show_mptmod_ver(s,ver)  \
@@ -176,6 +176,8 @@
 /* debug print string length used for events and iocstatus */
 # define EVENT_DESCR_STR_SZ             100
 
+#define MPT_POLLING_INTERVAL		1000	/* in milliseconds */
+
 #ifdef __KERNEL__	/* { */
 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
 
@@ -709,6 +711,12 @@
 	struct workqueue_struct *fc_rescan_work_q;
 	struct scsi_cmnd	**ScsiLookup;
 	spinlock_t		  scsi_lookup_lock;
+
+	char			 reset_work_q_name[KOBJ_NAME_LEN];
+	struct workqueue_struct *reset_work_q;
+	struct delayed_work	 fault_reset_work;
+	spinlock_t		 fault_reset_work_lock;
+
 } MPT_ADAPTER;
 
 /*
@@ -919,7 +927,6 @@
  *  Public data decl's...
  */
 extern struct list_head	  ioc_list;
-extern struct proc_dir_entry	*mpt_proc_root_dir;
 
 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
 #endif		/* } __KERNEL__ */
diff --git a/drivers/message/fusion/mptctl.c b/drivers/message/fusion/mptctl.c
index c594656..a592042 100644
--- a/drivers/message/fusion/mptctl.c
+++ b/drivers/message/fusion/mptctl.c
@@ -4,7 +4,7 @@
  *      For use with LSI PCI chip/adapters
  *      running LSI Fusion MPT (Message Passing Technology) firmware.
  *
- *  Copyright (c) 1999-2007 LSI Corporation
+ *  Copyright (c) 1999-2008 LSI Corporation
  *  (mailto:DL-MPTFusionLinux@lsi.com)
  *
  */
@@ -66,7 +66,7 @@
 #include <scsi/scsi_host.h>
 #include <scsi/scsi_tcq.h>
 
-#define COPYRIGHT	"Copyright (c) 1999-2007 LSI Corporation"
+#define COPYRIGHT	"Copyright (c) 1999-2008 LSI Corporation"
 #define MODULEAUTHOR	"LSI Corporation"
 #include "mptbase.h"
 #include "mptctl.h"
diff --git a/drivers/message/fusion/mptctl.h b/drivers/message/fusion/mptctl.h
index 2c18901..d564cc9 100644
--- a/drivers/message/fusion/mptctl.h
+++ b/drivers/message/fusion/mptctl.h
@@ -5,7 +5,7 @@
  *          LSIFC9xx/LSI409xx Fibre Channel
  *      running LSI Fusion MPT (Message Passing Technology) firmware.
  *
- *  Copyright (c) 1999-2007 LSI Corporation
+ *  Copyright (c) 1999-2008 LSI Corporation
  *  (mailto:DL-MPTFusionLinux@lsi.com)
  *
  */
diff --git a/drivers/message/fusion/mptdebug.h b/drivers/message/fusion/mptdebug.h
index ffdb0a6..510b9f4 100644
--- a/drivers/message/fusion/mptdebug.h
+++ b/drivers/message/fusion/mptdebug.h
@@ -3,7 +3,7 @@
  *      For use with LSI PCI chip/adapter(s)
  *      running LSI Fusion MPT (Message Passing Technology) firmware.
  *
- *  Copyright (c) 1999-2007 LSI Corporation
+ *  Copyright (c) 1999-2008 LSI Corporation
  *  (mailto:DL-MPTFusionLinux@lsi.com)
  *
  */
diff --git a/drivers/message/fusion/mptfc.c b/drivers/message/fusion/mptfc.c
index 1e24ab4..fc31ca6 100644
--- a/drivers/message/fusion/mptfc.c
+++ b/drivers/message/fusion/mptfc.c
@@ -3,7 +3,7 @@
  *      For use with LSI PCI chip/adapter(s)
  *      running LSI Fusion MPT (Message Passing Technology) firmware.
  *
- *  Copyright (c) 1999-2007 LSI Corporation
+ *  Copyright (c) 1999-2008 LSI Corporation
  *  (mailto:DL-MPTFusionLinux@lsi.com)
  *
  */
diff --git a/drivers/message/fusion/mptlan.c b/drivers/message/fusion/mptlan.c
index 7950fc6..d709d92 100644
--- a/drivers/message/fusion/mptlan.c
+++ b/drivers/message/fusion/mptlan.c
@@ -4,7 +4,7 @@
  *      For use with LSI Fibre Channel PCI chip/adapters
  *      running LSI Fusion MPT (Message Passing Technology) firmware.
  *
- *  Copyright (c) 2000-2007 LSI Corporation
+ *  Copyright (c) 2000-2008 LSI Corporation
  *  (mailto:DL-MPTFusionLinux@lsi.com)
  *
  */
diff --git a/drivers/message/fusion/mptlan.h b/drivers/message/fusion/mptlan.h
index bafb67f..33927ee 100644
--- a/drivers/message/fusion/mptlan.h
+++ b/drivers/message/fusion/mptlan.h
@@ -4,7 +4,7 @@
  *      For use with LSI Fibre Channel PCI chip/adapters
  *      running LSI Fusion MPT (Message Passing Technology) firmware.
  *
- *  Copyright (c) 2000-2007 LSI Corporation
+ *  Copyright (c) 2000-2008 LSI Corporation
  *  (mailto:DL-MPTFusionLinux@lsi.com)
  *
  */
diff --git a/drivers/message/fusion/mptsas.c b/drivers/message/fusion/mptsas.c
index 4d492ba..b1147aa 100644
--- a/drivers/message/fusion/mptsas.c
+++ b/drivers/message/fusion/mptsas.c
@@ -3,7 +3,7 @@
  *      For use with LSI PCI chip/adapter(s)
  *      running LSI Fusion MPT (Message Passing Technology) firmware.
  *
- *  Copyright (c) 1999-2007 LSI Corporation
+ *  Copyright (c) 1999-2008 LSI Corporation
  *  (mailto:DL-MPTFusionLinux@lsi.com)
  */
 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
diff --git a/drivers/message/fusion/mptsas.h b/drivers/message/fusion/mptsas.h
index 7c150f5..2b544e0 100644
--- a/drivers/message/fusion/mptsas.h
+++ b/drivers/message/fusion/mptsas.h
@@ -5,7 +5,7 @@
  *          LSIFC9xx/LSI409xx Fibre Channel
  *      running LSI MPT (Message Passing Technology) firmware.
  *
- *  Copyright (c) 1999-2007 LSI Corporation
+ *  Copyright (c) 1999-2008 LSI Corporation
  *  (mailto:DL-MPTFusionLinux@lsi.com)
  *
  */
diff --git a/drivers/message/fusion/mptscsih.c b/drivers/message/fusion/mptscsih.c
index c68ef00..d142b6b 100644
--- a/drivers/message/fusion/mptscsih.c
+++ b/drivers/message/fusion/mptscsih.c
@@ -3,7 +3,7 @@
  *      For use with LSI PCI chip/adapter(s)
  *      running LSI Fusion MPT (Message Passing Technology) firmware.
  *
- *  Copyright (c) 1999-2007 LSI Corporation
+ *  Copyright (c) 1999-2008 LSI Corporation
  *  (mailto:DL-MPTFusionLinux@lsi.com)
  *
  */
diff --git a/drivers/message/fusion/mptscsih.h b/drivers/message/fusion/mptscsih.h
index 7ea7da0..319aa30 100644
--- a/drivers/message/fusion/mptscsih.h
+++ b/drivers/message/fusion/mptscsih.h
@@ -5,7 +5,7 @@
  *          LSIFC9xx/LSI409xx Fibre Channel
  *      running LSI Fusion MPT (Message Passing Technology) firmware.
  *
- *  Copyright (c) 1999-2007 LSI Corporation
+ *  Copyright (c) 1999-2008 LSI Corporation
  *  (mailto:DL-MPTFusionLinux@lsi.com)
  *
  */
diff --git a/drivers/message/fusion/mptspi.c b/drivers/message/fusion/mptspi.c
index 1effca4..6162014 100644
--- a/drivers/message/fusion/mptspi.c
+++ b/drivers/message/fusion/mptspi.c
@@ -3,7 +3,7 @@
  *      For use with LSI PCI chip/adapter(s)
  *      running LSI Fusion MPT (Message Passing Technology) firmware.
  *
- *  Copyright (c) 1999-2007 LSI Corporation
+ *  Copyright (c) 1999-2008 LSI Corporation
  *  (mailto:DL-MPTFusionLinux@lsi.com)
  *
  */
@@ -447,6 +447,7 @@
 	spi_max_offset(starget) = ioc->spi_data.maxSyncOffset;
 
 	spi_offset(starget) = 0;
+	spi_period(starget) = 0xFF;
 	mptspi_write_width(starget, 0);
 
 	return 0;
diff --git a/drivers/s390/scsi/Makefile b/drivers/s390/scsi/Makefile
index d6a78f1..cb301cc 100644
--- a/drivers/s390/scsi/Makefile
+++ b/drivers/s390/scsi/Makefile
@@ -3,7 +3,6 @@
 #
 
 zfcp-objs := zfcp_aux.o zfcp_ccw.o zfcp_scsi.o zfcp_erp.o zfcp_qdio.o \
-	     zfcp_fsf.o zfcp_dbf.o zfcp_sysfs_adapter.o zfcp_sysfs_port.o \
-	     zfcp_sysfs_unit.o zfcp_sysfs_driver.o
+	     zfcp_fsf.o zfcp_dbf.o zfcp_sysfs.o zfcp_fc.o zfcp_cfdc.o
 
 obj-$(CONFIG_ZFCP) += zfcp.o
diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c
index 8c7e2b7..90abfd0 100644
--- a/drivers/s390/scsi/zfcp_aux.c
+++ b/drivers/s390/scsi/zfcp_aux.c
@@ -1,22 +1,9 @@
 /*
- * This file is part of the zfcp device driver for
- * FCP adapters for IBM System z9 and zSeries.
+ * zfcp device driver
  *
- * (C) Copyright IBM Corp. 2002, 2006
+ * Module interface and handling of zfcp data structures.
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * Copyright IBM Corporation 2002, 2008
  */
 
 /*
@@ -31,93 +18,25 @@
  *            Maxim Shchetynin
  *            Volker Sameske
  *            Ralph Wuerthner
+ *            Michael Loehr
+ *            Swen Schillig
+ *            Christof Schmitt
+ *            Martin Petermann
+ *            Sven Schuetz
  */
 
+#include <linux/miscdevice.h>
 #include "zfcp_ext.h"
 
-/* accumulated log level (module parameter) */
-static u32 loglevel = ZFCP_LOG_LEVEL_DEFAULTS;
 static char *device;
-/*********************** FUNCTION PROTOTYPES *********************************/
-
-/* written against the module interface */
-static int __init  zfcp_module_init(void);
-
-/* FCP related */
-static void zfcp_ns_gid_pn_handler(unsigned long);
-
-/* miscellaneous */
-static int zfcp_sg_list_alloc(struct zfcp_sg_list *, size_t);
-static void zfcp_sg_list_free(struct zfcp_sg_list *);
-static int zfcp_sg_list_copy_from_user(struct zfcp_sg_list *,
-				       void __user *, size_t);
-static int zfcp_sg_list_copy_to_user(void __user *,
-				     struct zfcp_sg_list *, size_t);
-static long zfcp_cfdc_dev_ioctl(struct file *, unsigned int, unsigned long);
-
-#define ZFCP_CFDC_IOC_MAGIC                     0xDD
-#define ZFCP_CFDC_IOC \
-	_IOWR(ZFCP_CFDC_IOC_MAGIC, 0, struct zfcp_cfdc_sense_data)
-
-
-static const struct file_operations zfcp_cfdc_fops = {
-	.unlocked_ioctl = zfcp_cfdc_dev_ioctl,
-#ifdef CONFIG_COMPAT
-	.compat_ioctl = zfcp_cfdc_dev_ioctl
-#endif
-};
-
-static struct miscdevice zfcp_cfdc_misc = {
-	.minor = ZFCP_CFDC_DEV_MINOR,
-	.name = ZFCP_CFDC_DEV_NAME,
-	.fops = &zfcp_cfdc_fops
-};
-
-/*********************** KERNEL/MODULE PARAMETERS  ***************************/
-
-/* declare driver module init/cleanup functions */
-module_init(zfcp_module_init);
 
 MODULE_AUTHOR("IBM Deutschland Entwicklung GmbH - linux390@de.ibm.com");
-MODULE_DESCRIPTION
-    ("FCP (SCSI over Fibre Channel) HBA driver for IBM System z9 and zSeries");
+MODULE_DESCRIPTION("FCP HBA driver");
 MODULE_LICENSE("GPL");
 
 module_param(device, charp, 0400);
 MODULE_PARM_DESC(device, "specify initial device");
 
-module_param(loglevel, uint, 0400);
-MODULE_PARM_DESC(loglevel,
-		 "log levels, 8 nibbles: "
-		 "FC ERP QDIO CIO Config FSF SCSI Other, "
-		 "levels: 0=none 1=normal 2=devel 3=trace");
-
-/****************************************************************/
-/************** Functions without logging ***********************/
-/****************************************************************/
-
-void
-_zfcp_hex_dump(char *addr, int count)
-{
-	int i;
-	for (i = 0; i < count; i++) {
-		printk("%02x", addr[i]);
-		if ((i % 4) == 3)
-			printk(" ");
-		if ((i % 32) == 31)
-			printk("\n");
-	}
-	if (((i-1) % 32) != 31)
-		printk("\n");
-}
-
-
-/****************************************************************/
-/****** Functions to handle the request ID hash table    ********/
-/****************************************************************/
-
-#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_FSF
-
 static int zfcp_reqlist_alloc(struct zfcp_adapter *adapter)
 {
 	int idx;
@@ -132,11 +51,12 @@
 	return 0;
 }
 
-static void zfcp_reqlist_free(struct zfcp_adapter *adapter)
-{
-	kfree(adapter->req_list);
-}
-
+/**
+ * zfcp_reqlist_isempty - is the request list empty
+ * @adapter: pointer to struct zfcp_adapter
+ *
+ * Returns: true if list is empty, false otherwise
+ */
 int zfcp_reqlist_isempty(struct zfcp_adapter *adapter)
 {
 	unsigned int idx;
@@ -147,62 +67,58 @@
 	return 1;
 }
 
-#undef ZFCP_LOG_AREA
-
-/****************************************************************/
-/************** Uncategorised Functions *************************/
-/****************************************************************/
-
-#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_OTHER
-
-/**
- * zfcp_device_setup - setup function
- * @str: pointer to parameter string
- *
- * Parse "device=..." parameter string.
- */
-static int __init
-zfcp_device_setup(char *devstr)
+static int __init zfcp_device_setup(char *devstr)
 {
-	char *tmp, *str;
-	size_t len;
+	char *token;
+	char *str;
 
 	if (!devstr)
 		return 0;
 
-	len = strlen(devstr) + 1;
-	str = kmalloc(len, GFP_KERNEL);
+	/* duplicate devstr and keep the original for sysfs presentation*/
+	str = kmalloc(strlen(devstr) + 1, GFP_KERNEL);
 	if (!str)
-		goto err_out;
-	memcpy(str, devstr, len);
+		return 0;
 
-	tmp = strchr(str, ',');
-	if (!tmp)
-		goto err_out;
-	*tmp++ = '\0';
-	strncpy(zfcp_data.init_busid, str, BUS_ID_SIZE);
-	zfcp_data.init_busid[BUS_ID_SIZE-1] = '\0';
+	strcpy(str, devstr);
 
-	zfcp_data.init_wwpn = simple_strtoull(tmp, &tmp, 0);
-	if (*tmp++ != ',')
+	token = strsep(&str, ",");
+	if (!token || strlen(token) >= BUS_ID_SIZE)
 		goto err_out;
-	if (*tmp == '\0')
+	strncpy(zfcp_data.init_busid, token, BUS_ID_SIZE);
+
+	token = strsep(&str, ",");
+	if (!token || strict_strtoull(token, 0, &zfcp_data.init_wwpn))
 		goto err_out;
 
-	zfcp_data.init_fcp_lun = simple_strtoull(tmp, &tmp, 0);
-	if (*tmp != '\0')
+	token = strsep(&str, ",");
+	if (!token || strict_strtoull(token, 0, &zfcp_data.init_fcp_lun))
 		goto err_out;
+
 	kfree(str);
 	return 1;
 
  err_out:
-	ZFCP_LOG_NORMAL("Parse error for device parameter string %s\n", str);
 	kfree(str);
+	pr_err("zfcp: Parse error for device parameter string %s, "
+	       "device not attached.\n", devstr);
 	return 0;
 }
 
-static void __init
-zfcp_init_device_configure(void)
+static struct zfcp_adapter *zfcp_get_adapter_by_busid(char *bus_id)
+{
+	struct zfcp_adapter *adapter;
+
+	list_for_each_entry(adapter, &zfcp_data.adapter_list_head, list)
+		if ((strncmp(bus_id, adapter->ccw_device->dev.bus_id,
+			     BUS_ID_SIZE) == 0) &&
+		    !(atomic_read(&adapter->status) &
+		      ZFCP_STATUS_COMMON_REMOVE))
+		    return adapter;
+	return NULL;
+}
+
+static void __init zfcp_init_device_configure(void)
 {
 	struct zfcp_adapter *adapter;
 	struct zfcp_port *port;
@@ -215,101 +131,75 @@
 		zfcp_adapter_get(adapter);
 	read_unlock_irq(&zfcp_data.config_lock);
 
-	if (adapter == NULL)
+	if (!adapter)
 		goto out_adapter;
 	port = zfcp_port_enqueue(adapter, zfcp_data.init_wwpn, 0, 0);
-	if (!port)
+	if (IS_ERR(port))
 		goto out_port;
 	unit = zfcp_unit_enqueue(port, zfcp_data.init_fcp_lun);
-	if (!unit)
+	if (IS_ERR(unit))
 		goto out_unit;
 	up(&zfcp_data.config_sema);
 	ccw_device_set_online(adapter->ccw_device);
 	zfcp_erp_wait(adapter);
 	down(&zfcp_data.config_sema);
 	zfcp_unit_put(unit);
- out_unit:
+out_unit:
 	zfcp_port_put(port);
- out_port:
+out_port:
 	zfcp_adapter_put(adapter);
- out_adapter:
+out_adapter:
 	up(&zfcp_data.config_sema);
 	return;
 }
 
-static int calc_alignment(int size)
+static struct kmem_cache *zfcp_cache_create(int size, char *name)
 {
 	int align = 1;
-
-	if (!size)
-		return 0;
-
 	while ((size - align) > 0)
 		align <<= 1;
-
-	return align;
+	return kmem_cache_create(name , size, align, 0, NULL);
 }
 
-static int __init
-zfcp_module_init(void)
+static int __init zfcp_module_init(void)
 {
 	int retval = -ENOMEM;
-	int size, align;
 
-	size = sizeof(struct zfcp_fsf_req_qtcb);
-	align = calc_alignment(size);
-	zfcp_data.fsf_req_qtcb_cache =
-		kmem_cache_create("zfcp_fsf", size, align, 0, NULL);
+	zfcp_data.fsf_req_qtcb_cache = zfcp_cache_create(
+			sizeof(struct zfcp_fsf_req_qtcb), "zfcp_fsf");
 	if (!zfcp_data.fsf_req_qtcb_cache)
 		goto out;
 
-	size = sizeof(struct fsf_status_read_buffer);
-	align = calc_alignment(size);
-	zfcp_data.sr_buffer_cache =
-		kmem_cache_create("zfcp_sr", size, align, 0, NULL);
+	zfcp_data.sr_buffer_cache = zfcp_cache_create(
+			sizeof(struct fsf_status_read_buffer), "zfcp_sr");
 	if (!zfcp_data.sr_buffer_cache)
 		goto out_sr_cache;
 
-	size = sizeof(struct zfcp_gid_pn_data);
-	align = calc_alignment(size);
-	zfcp_data.gid_pn_cache =
-		kmem_cache_create("zfcp_gid", size, align, 0, NULL);
+	zfcp_data.gid_pn_cache = zfcp_cache_create(
+			sizeof(struct zfcp_gid_pn_data), "zfcp_gid");
 	if (!zfcp_data.gid_pn_cache)
 		goto out_gid_cache;
 
-	atomic_set(&zfcp_data.loglevel, loglevel);
-
-	/* initialize adapter list */
 	INIT_LIST_HEAD(&zfcp_data.adapter_list_head);
-
-	/* initialize adapters to be removed list head */
 	INIT_LIST_HEAD(&zfcp_data.adapter_remove_lh);
 
+	sema_init(&zfcp_data.config_sema, 1);
+	rwlock_init(&zfcp_data.config_lock);
+
 	zfcp_data.scsi_transport_template =
 		fc_attach_transport(&zfcp_transport_functions);
 	if (!zfcp_data.scsi_transport_template)
 		goto out_transport;
 
 	retval = misc_register(&zfcp_cfdc_misc);
-	if (retval != 0) {
-		ZFCP_LOG_INFO("registration of misc device "
-			      "zfcp_cfdc failed\n");
+	if (retval) {
+		pr_err("zfcp: registration of misc device zfcp_cfdc failed\n");
 		goto out_misc;
 	}
 
-	ZFCP_LOG_TRACE("major/minor for zfcp_cfdc: %d/%d\n",
-		       ZFCP_CFDC_DEV_MAJOR, zfcp_cfdc_misc.minor);
-
-	/* Initialise proc semaphores */
-	sema_init(&zfcp_data.config_sema, 1);
-
-	/* initialise configuration rw lock */
-	rwlock_init(&zfcp_data.config_lock);
-
-	/* setup dynamic I/O */
 	retval = zfcp_ccw_register();
 	if (retval) {
-		ZFCP_LOG_NORMAL("registration with common I/O layer failed\n");
+		pr_err("zfcp: Registration with common I/O layer failed.\n");
 		goto out_ccw_register;
 	}
 
@@ -318,527 +208,88 @@
 
 	goto out;
 
- out_ccw_register:
+out_ccw_register:
 	misc_deregister(&zfcp_cfdc_misc);
- out_misc:
+out_misc:
 	fc_release_transport(zfcp_data.scsi_transport_template);
- out_transport:
+out_transport:
 	kmem_cache_destroy(zfcp_data.gid_pn_cache);
- out_gid_cache:
+out_gid_cache:
 	kmem_cache_destroy(zfcp_data.sr_buffer_cache);
- out_sr_cache:
+out_sr_cache:
 	kmem_cache_destroy(zfcp_data.fsf_req_qtcb_cache);
- out:
+out:
 	return retval;
 }
 
-/*
- * function:    zfcp_cfdc_dev_ioctl
- *
- * purpose:     Handle control file upload/download transaction via IOCTL
- *		interface
- *
- * returns:     0           - Operation completed successfuly
- *              -ENOTTY     - Unknown IOCTL command
- *              -EINVAL     - Invalid sense data record
- *              -ENXIO      - The FCP adapter is not available
- *              -EOPNOTSUPP - The FCP adapter does not have CFDC support
- *              -ENOMEM     - Insufficient memory
- *              -EFAULT     - User space memory I/O operation fault
- *              -EPERM      - Cannot create or queue FSF request or create SBALs
- *              -ERESTARTSYS- Received signal (is mapped to EAGAIN by VFS)
- */
-static long
-zfcp_cfdc_dev_ioctl(struct file *file, unsigned int command,
-		    unsigned long buffer)
-{
-	struct zfcp_cfdc_sense_data *sense_data, __user *sense_data_user;
-	struct zfcp_adapter *adapter = NULL;
-	struct zfcp_fsf_req *fsf_req = NULL;
-	struct zfcp_sg_list *sg_list = NULL;
-	u32 fsf_command, option;
-	char *bus_id = NULL;
-	int retval = 0;
-
-	sense_data = kmalloc(sizeof(struct zfcp_cfdc_sense_data), GFP_KERNEL);
-	if (sense_data == NULL) {
-		retval = -ENOMEM;
-		goto out;
-	}
-
-	sg_list = kzalloc(sizeof(struct zfcp_sg_list), GFP_KERNEL);
-	if (sg_list == NULL) {
-		retval = -ENOMEM;
-		goto out;
-	}
-
-	if (command != ZFCP_CFDC_IOC) {
-		ZFCP_LOG_INFO("IOC request code 0x%x invalid\n", command);
-		retval = -ENOTTY;
-		goto out;
-	}
-
-	if ((sense_data_user = (void __user *) buffer) == NULL) {
-		ZFCP_LOG_INFO("sense data record is required\n");
-		retval = -EINVAL;
-		goto out;
-	}
-
-	retval = copy_from_user(sense_data, sense_data_user,
-				sizeof(struct zfcp_cfdc_sense_data));
-	if (retval) {
-		retval = -EFAULT;
-		goto out;
-	}
-
-	if (sense_data->signature != ZFCP_CFDC_SIGNATURE) {
-		ZFCP_LOG_INFO("invalid sense data request signature 0x%08x\n",
-			      ZFCP_CFDC_SIGNATURE);
-		retval = -EINVAL;
-		goto out;
-	}
-
-	switch (sense_data->command) {
-
-	case ZFCP_CFDC_CMND_DOWNLOAD_NORMAL:
-		fsf_command = FSF_QTCB_DOWNLOAD_CONTROL_FILE;
-		option = FSF_CFDC_OPTION_NORMAL_MODE;
-		break;
-
-	case ZFCP_CFDC_CMND_DOWNLOAD_FORCE:
-		fsf_command = FSF_QTCB_DOWNLOAD_CONTROL_FILE;
-		option = FSF_CFDC_OPTION_FORCE;
-		break;
-
-	case ZFCP_CFDC_CMND_FULL_ACCESS:
-		fsf_command = FSF_QTCB_DOWNLOAD_CONTROL_FILE;
-		option = FSF_CFDC_OPTION_FULL_ACCESS;
-		break;
-
-	case ZFCP_CFDC_CMND_RESTRICTED_ACCESS:
-		fsf_command = FSF_QTCB_DOWNLOAD_CONTROL_FILE;
-		option = FSF_CFDC_OPTION_RESTRICTED_ACCESS;
-		break;
-
-	case ZFCP_CFDC_CMND_UPLOAD:
-		fsf_command = FSF_QTCB_UPLOAD_CONTROL_FILE;
-		option = 0;
-		break;
-
-	default:
-		ZFCP_LOG_INFO("invalid command code 0x%08x\n",
-			      sense_data->command);
-		retval = -EINVAL;
-		goto out;
-	}
-
-	bus_id = kmalloc(BUS_ID_SIZE, GFP_KERNEL);
-	if (bus_id == NULL) {
-		retval = -ENOMEM;
-		goto out;
-	}
-	snprintf(bus_id, BUS_ID_SIZE, "%d.%d.%04x",
-		(sense_data->devno >> 24),
-		(sense_data->devno >> 16) & 0xFF,
-		(sense_data->devno & 0xFFFF));
-
-	read_lock_irq(&zfcp_data.config_lock);
-	adapter = zfcp_get_adapter_by_busid(bus_id);
-	if (adapter)
-		zfcp_adapter_get(adapter);
-	read_unlock_irq(&zfcp_data.config_lock);
-
-	kfree(bus_id);
-
-	if (adapter == NULL) {
-		ZFCP_LOG_INFO("invalid adapter\n");
-		retval = -ENXIO;
-		goto out;
-	}
-
-	if (sense_data->command & ZFCP_CFDC_WITH_CONTROL_FILE) {
-		retval = zfcp_sg_list_alloc(sg_list,
-					    ZFCP_CFDC_MAX_CONTROL_FILE_SIZE);
-		if (retval) {
-			retval = -ENOMEM;
-			goto out;
-		}
-	}
-
-	if ((sense_data->command & ZFCP_CFDC_DOWNLOAD) &&
-	    (sense_data->command & ZFCP_CFDC_WITH_CONTROL_FILE)) {
-		retval = zfcp_sg_list_copy_from_user(
-			sg_list, &sense_data_user->control_file,
-			ZFCP_CFDC_MAX_CONTROL_FILE_SIZE);
-		if (retval) {
-			retval = -EFAULT;
-			goto out;
-		}
-	}
-
-	retval = zfcp_fsf_control_file(adapter, &fsf_req, fsf_command,
-				       option, sg_list);
-	if (retval)
-		goto out;
-
-	if ((fsf_req->qtcb->prefix.prot_status != FSF_PROT_GOOD) &&
-	    (fsf_req->qtcb->prefix.prot_status != FSF_PROT_FSF_STATUS_PRESENTED)) {
-		retval = -ENXIO;
-		goto out;
-	}
-
-	sense_data->fsf_status = fsf_req->qtcb->header.fsf_status;
-	memcpy(&sense_data->fsf_status_qual,
-	       &fsf_req->qtcb->header.fsf_status_qual,
-	       sizeof(union fsf_status_qual));
-	memcpy(&sense_data->payloads, &fsf_req->qtcb->bottom.support.els, 256);
-
-	retval = copy_to_user(sense_data_user, sense_data,
-		sizeof(struct zfcp_cfdc_sense_data));
-	if (retval) {
-		retval = -EFAULT;
-		goto out;
-	}
-
-	if (sense_data->command & ZFCP_CFDC_UPLOAD) {
-		retval = zfcp_sg_list_copy_to_user(
-			&sense_data_user->control_file, sg_list,
-			ZFCP_CFDC_MAX_CONTROL_FILE_SIZE);
-		if (retval) {
-			retval = -EFAULT;
-			goto out;
-		}
-	}
-
- out:
-	if (fsf_req != NULL)
-		zfcp_fsf_req_free(fsf_req);
-
-	if ((adapter != NULL) && (retval != -ENXIO))
-		zfcp_adapter_put(adapter);
-
-	if (sg_list != NULL) {
-		zfcp_sg_list_free(sg_list);
-		kfree(sg_list);
-	}
-
-	kfree(sense_data);
-
-	return retval;
-}
-
-
-/**
- * zfcp_sg_list_alloc - create a scatter-gather list of the specified size
- * @sg_list: structure describing a scatter gather list
- * @size: size of scatter-gather list
- * Return: 0 on success, else -ENOMEM
- *
- * In sg_list->sg a pointer to the created scatter-gather list is returned,
- * or NULL if we run out of memory. sg_list->count specifies the number of
- * elements of the scatter-gather list. The maximum size of a single element
- * in the scatter-gather list is PAGE_SIZE.
- */
-static int
-zfcp_sg_list_alloc(struct zfcp_sg_list *sg_list, size_t size)
-{
-	struct scatterlist *sg;
-	unsigned int i;
-	int retval = 0;
-	void *address;
-
-	BUG_ON(sg_list == NULL);
-
-	sg_list->count = size >> PAGE_SHIFT;
-	if (size & ~PAGE_MASK)
-		sg_list->count++;
-	sg_list->sg = kcalloc(sg_list->count, sizeof(struct scatterlist),
-			      GFP_KERNEL);
-	if (sg_list->sg == NULL) {
-		sg_list->count = 0;
-		retval = -ENOMEM;
-		goto out;
-	}
-	sg_init_table(sg_list->sg, sg_list->count);
-
-	for (i = 0, sg = sg_list->sg; i < sg_list->count; i++, sg++) {
-		address = (void *) get_zeroed_page(GFP_KERNEL);
-		if (address == NULL) {
-			sg_list->count = i;
-			zfcp_sg_list_free(sg_list);
-			retval = -ENOMEM;
-			goto out;
-		}
-		zfcp_address_to_sg(address, sg, min(size, PAGE_SIZE));
-		size -= sg->length;
-	}
-
- out:
-	return retval;
-}
-
-
-/**
- * zfcp_sg_list_free - free memory of a scatter-gather list
- * @sg_list: structure describing a scatter-gather list
- *
- * Memory for each element in the scatter-gather list is freed.
- * Finally sg_list->sg is freed itself and sg_list->count is reset.
- */
-static void
-zfcp_sg_list_free(struct zfcp_sg_list *sg_list)
-{
-	struct scatterlist *sg;
-	unsigned int i;
-
-	BUG_ON(sg_list == NULL);
-
-	for (i = 0, sg = sg_list->sg; i < sg_list->count; i++, sg++)
-		free_page((unsigned long) zfcp_sg_to_address(sg));
-
-	sg_list->count = 0;
-	kfree(sg_list->sg);
-}
-
-/**
- * zfcp_sg_size - determine size of a scatter-gather list
- * @sg: array of (struct scatterlist)
- * @sg_count: elements in array
- * Return: size of entire scatter-gather list
- */
-static size_t zfcp_sg_size(struct scatterlist *sg, unsigned int sg_count)
-{
-	unsigned int i;
-	struct scatterlist *p;
-	size_t size;
-
-	size = 0;
-	for (i = 0, p = sg; i < sg_count; i++, p++) {
-		BUG_ON(p == NULL);
-		size += p->length;
-	}
-
-	return size;
-}
-
-
-/**
- * zfcp_sg_list_copy_from_user -copy data from user space to scatter-gather list
- * @sg_list: structure describing a scatter-gather list
- * @user_buffer: pointer to buffer in user space
- * @size: number of bytes to be copied
- * Return: 0 on success, -EFAULT if copy_from_user fails.
- */
-static int
-zfcp_sg_list_copy_from_user(struct zfcp_sg_list *sg_list,
-			    void __user *user_buffer,
-                            size_t size)
-{
-	struct scatterlist *sg;
-	unsigned int length;
-	void *zfcp_buffer;
-	int retval = 0;
-
-	BUG_ON(sg_list == NULL);
-
-	if (zfcp_sg_size(sg_list->sg, sg_list->count) < size)
-		return -EFAULT;
-
-	for (sg = sg_list->sg; size > 0; sg++) {
-		length = min((unsigned int)size, sg->length);
-		zfcp_buffer = zfcp_sg_to_address(sg);
-		if (copy_from_user(zfcp_buffer, user_buffer, length)) {
-			retval = -EFAULT;
-			goto out;
-		}
-		user_buffer += length;
-		size -= length;
-	}
-
- out:
-	return retval;
-}
-
-
-/**
- * zfcp_sg_list_copy_to_user - copy data from scatter-gather list to user space
- * @user_buffer: pointer to buffer in user space
- * @sg_list: structure describing a scatter-gather list
- * @size: number of bytes to be copied
- * Return: 0 on success, -EFAULT if copy_to_user fails
- */
-static int
-zfcp_sg_list_copy_to_user(void __user  *user_buffer,
-			  struct zfcp_sg_list *sg_list,
-                          size_t size)
-{
-	struct scatterlist *sg;
-	unsigned int length;
-	void *zfcp_buffer;
-	int retval = 0;
-
-	BUG_ON(sg_list == NULL);
-
-	if (zfcp_sg_size(sg_list->sg, sg_list->count) < size)
-		return -EFAULT;
-
-	for (sg = sg_list->sg; size > 0; sg++) {
-		length = min((unsigned int) size, sg->length);
-		zfcp_buffer = zfcp_sg_to_address(sg);
-		if (copy_to_user(user_buffer, zfcp_buffer, length)) {
-			retval = -EFAULT;
-			goto out;
-		}
-		user_buffer += length;
-		size -= length;
-	}
-
- out:
-	return retval;
-}
-
-
-#undef ZFCP_LOG_AREA
-
-/****************************************************************/
-/****** Functions for configuration/set-up of structures ********/
-/****************************************************************/
-
-#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_CONFIG
+module_init(zfcp_module_init);
 
 /**
  * zfcp_get_unit_by_lun - find unit in unit list of port by FCP LUN
  * @port: pointer to port to search for unit
  * @fcp_lun: FCP LUN to search for
- * Traverse list of all units of a port and return pointer to a unit
- * with the given FCP LUN.
+ *
+ * Returns: pointer to zfcp_unit or NULL
  */
-struct zfcp_unit *
-zfcp_get_unit_by_lun(struct zfcp_port *port, fcp_lun_t fcp_lun)
+struct zfcp_unit *zfcp_get_unit_by_lun(struct zfcp_port *port,
+				       fcp_lun_t fcp_lun)
 {
 	struct zfcp_unit *unit;
-	int found = 0;
 
-	list_for_each_entry(unit, &port->unit_list_head, list) {
+	list_for_each_entry(unit, &port->unit_list_head, list)
 		if ((unit->fcp_lun == fcp_lun) &&
-		    !atomic_test_mask(ZFCP_STATUS_COMMON_REMOVE, &unit->status))
-		{
-			found = 1;
-			break;
-		}
-	}
-	return found ? unit : NULL;
+		    !(atomic_read(&unit->status) & ZFCP_STATUS_COMMON_REMOVE))
+		    return unit;
+	return NULL;
 }
 
 /**
  * zfcp_get_port_by_wwpn - find port in port list of adapter by wwpn
  * @adapter: pointer to adapter to search for port
  * @wwpn: wwpn to search for
- * Traverse list of all ports of an adapter and return pointer to a port
- * with the given wwpn.
+ *
+ * Returns: pointer to zfcp_port or NULL
  */
-struct zfcp_port *
-zfcp_get_port_by_wwpn(struct zfcp_adapter *adapter, wwn_t wwpn)
+struct zfcp_port *zfcp_get_port_by_wwpn(struct zfcp_adapter *adapter,
+					wwn_t wwpn)
 {
 	struct zfcp_port *port;
-	int found = 0;
 
-	list_for_each_entry(port, &adapter->port_list_head, list) {
-		if ((port->wwpn == wwpn) &&
-		    !(atomic_read(&port->status) &
-		      (ZFCP_STATUS_PORT_NO_WWPN | ZFCP_STATUS_COMMON_REMOVE))) {
-			found = 1;
-			break;
-		}
-	}
-	return found ? port : NULL;
+	list_for_each_entry(port, &adapter->port_list_head, list)
+		if ((port->wwpn == wwpn) && !(atomic_read(&port->status) &
+		      (ZFCP_STATUS_PORT_NO_WWPN | ZFCP_STATUS_COMMON_REMOVE)))
+			return port;
+	return NULL;
 }
 
-/**
- * zfcp_get_port_by_did - find port in port list of adapter by d_id
- * @adapter: pointer to adapter to search for port
- * @d_id: d_id to search for
- * Traverse list of all ports of an adapter and return pointer to a port
- * with the given d_id.
- */
-struct zfcp_port *
-zfcp_get_port_by_did(struct zfcp_adapter *adapter, u32 d_id)
+static void zfcp_sysfs_unit_release(struct device *dev)
 {
-	struct zfcp_port *port;
-	int found = 0;
-
-	list_for_each_entry(port, &adapter->port_list_head, list) {
-		if ((port->d_id == d_id) &&
-		    !atomic_test_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status))
-		{
-			found = 1;
-			break;
-		}
-	}
-	return found ? port : NULL;
-}
-
-/**
- * zfcp_get_adapter_by_busid - find adpater in adapter list by bus_id
- * @bus_id: bus_id to search for
- * Traverse list of all adapters and return pointer to an adapter
- * with the given bus_id.
- */
-struct zfcp_adapter *
-zfcp_get_adapter_by_busid(char *bus_id)
-{
-	struct zfcp_adapter *adapter;
-	int found = 0;
-
-	list_for_each_entry(adapter, &zfcp_data.adapter_list_head, list) {
-		if ((strncmp(bus_id, zfcp_get_busid_by_adapter(adapter),
-			     BUS_ID_SIZE) == 0) &&
-		    !atomic_test_mask(ZFCP_STATUS_COMMON_REMOVE,
-				      &adapter->status)){
-			found = 1;
-			break;
-		}
-	}
-	return found ? adapter : NULL;
+	kfree(container_of(dev, struct zfcp_unit, sysfs_device));
 }
 
 /**
  * zfcp_unit_enqueue - enqueue unit to unit list of a port.
  * @port: pointer to port where unit is added
  * @fcp_lun: FCP LUN of unit to be enqueued
- * Return: pointer to enqueued unit on success, NULL on error
+ * Returns: pointer to enqueued unit on success, ERR_PTR on error
  * Locks: config_sema must be held to serialize changes to the unit list
  *
  * Sets up some unit internal structures and creates sysfs entry.
  */
-struct zfcp_unit *
-zfcp_unit_enqueue(struct zfcp_port *port, fcp_lun_t fcp_lun)
+struct zfcp_unit *zfcp_unit_enqueue(struct zfcp_port *port, fcp_lun_t fcp_lun)
 {
 	struct zfcp_unit *unit;
 
-	/*
-	 * check that there is no unit with this FCP_LUN already in list
-	 * and enqueue it.
-	 * Note: Unlike for the adapter and the port, this is an error
-	 */
-	read_lock_irq(&zfcp_data.config_lock);
-	unit = zfcp_get_unit_by_lun(port, fcp_lun);
-	read_unlock_irq(&zfcp_data.config_lock);
-	if (unit)
-		return NULL;
-
-	unit = kzalloc(sizeof (struct zfcp_unit), GFP_KERNEL);
+	unit = kzalloc(sizeof(struct zfcp_unit), GFP_KERNEL);
 	if (!unit)
-		return NULL;
+		return ERR_PTR(-ENOMEM);
 
-	/* initialise reference count stuff */
 	atomic_set(&unit->refcount, 0);
 	init_waitqueue_head(&unit->remove_wq);
 
 	unit->port = port;
 	unit->fcp_lun = fcp_lun;
 
-	/* setup for sysfs registration */
 	snprintf(unit->sysfs_device.bus_id, BUS_ID_SIZE, "0x%016llx", fcp_lun);
 	unit->sysfs_device.parent = &port->sysfs_device;
 	unit->sysfs_device.release = zfcp_sysfs_unit_release;
@@ -847,14 +298,28 @@
 	/* mark unit unusable as long as sysfs registration is not complete */
 	atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &unit->status);
 
-	if (device_register(&unit->sysfs_device)) {
-		kfree(unit);
-		return NULL;
-	}
+	spin_lock_init(&unit->latencies.lock);
+	unit->latencies.write.channel.min = 0xFFFFFFFF;
+	unit->latencies.write.fabric.min = 0xFFFFFFFF;
+	unit->latencies.read.channel.min = 0xFFFFFFFF;
+	unit->latencies.read.fabric.min = 0xFFFFFFFF;
+	unit->latencies.cmd.channel.min = 0xFFFFFFFF;
+	unit->latencies.cmd.fabric.min = 0xFFFFFFFF;
 
-	if (zfcp_sysfs_unit_create_files(&unit->sysfs_device)) {
+	read_lock_irq(&zfcp_data.config_lock);
+	if (zfcp_get_unit_by_lun(port, fcp_lun)) {
+		read_unlock_irq(&zfcp_data.config_lock);
+		goto err_out_free;
+	}
+	read_unlock_irq(&zfcp_data.config_lock);
+
+	if (device_register(&unit->sysfs_device))
+		goto err_out_free;
+
+	if (sysfs_create_group(&unit->sysfs_device.kobj,
+			       &zfcp_sysfs_unit_attrs)) {
 		device_unregister(&unit->sysfs_device);
-		return NULL;
+		return ERR_PTR(-EIO);
 	}
 
 	zfcp_unit_get(unit);
@@ -864,16 +329,27 @@
 	list_add_tail(&unit->list, &port->unit_list_head);
 	atomic_clear_mask(ZFCP_STATUS_COMMON_REMOVE, &unit->status);
 	atomic_set_mask(ZFCP_STATUS_COMMON_RUNNING, &unit->status);
+
 	write_unlock_irq(&zfcp_data.config_lock);
 
 	port->units++;
 	zfcp_port_get(port);
 
 	return unit;
+
+err_out_free:
+	kfree(unit);
+	return ERR_PTR(-EINVAL);
 }
 
-void
-zfcp_unit_dequeue(struct zfcp_unit *unit)
+/**
+ * zfcp_unit_dequeue - dequeue unit
+ * @unit: pointer to zfcp_unit
+ *
+ * waits until all work is done on unit and removes it then from the unit->list
+ * of the associated port.
+ */
+void zfcp_unit_dequeue(struct zfcp_unit *unit)
 {
 	zfcp_unit_wait(unit);
 	write_lock_irq(&zfcp_data.config_lock);
@@ -881,68 +357,51 @@
 	write_unlock_irq(&zfcp_data.config_lock);
 	unit->port->units--;
 	zfcp_port_put(unit->port);
-	zfcp_sysfs_unit_remove_files(&unit->sysfs_device);
+	sysfs_remove_group(&unit->sysfs_device.kobj, &zfcp_sysfs_unit_attrs);
 	device_unregister(&unit->sysfs_device);
 }
 
-/*
- * Allocates a combined QTCB/fsf_req buffer for erp actions and fcp/SCSI
- * commands.
- * It also genrates fcp-nameserver request/response buffer and unsolicited
- * status read fsf_req buffers.
- *
- * locks:       must only be called with zfcp_data.config_sema taken
- */
-static int
-zfcp_allocate_low_mem_buffers(struct zfcp_adapter *adapter)
+static int zfcp_allocate_low_mem_buffers(struct zfcp_adapter *adapter)
 {
+	/* must only be called with zfcp_data.config_sema taken */
 	adapter->pool.fsf_req_erp =
-		mempool_create_slab_pool(ZFCP_POOL_FSF_REQ_ERP_NR,
-					 zfcp_data.fsf_req_qtcb_cache);
+		mempool_create_slab_pool(1, zfcp_data.fsf_req_qtcb_cache);
 	if (!adapter->pool.fsf_req_erp)
 		return -ENOMEM;
 
 	adapter->pool.fsf_req_scsi =
-		mempool_create_slab_pool(ZFCP_POOL_FSF_REQ_SCSI_NR,
-					 zfcp_data.fsf_req_qtcb_cache);
+		mempool_create_slab_pool(1, zfcp_data.fsf_req_qtcb_cache);
 	if (!adapter->pool.fsf_req_scsi)
 		return -ENOMEM;
 
 	adapter->pool.fsf_req_abort =
-		mempool_create_slab_pool(ZFCP_POOL_FSF_REQ_ABORT_NR,
-					 zfcp_data.fsf_req_qtcb_cache);
+		mempool_create_slab_pool(1, zfcp_data.fsf_req_qtcb_cache);
 	if (!adapter->pool.fsf_req_abort)
 		return -ENOMEM;
 
 	adapter->pool.fsf_req_status_read =
-		mempool_create_kmalloc_pool(ZFCP_POOL_STATUS_READ_NR,
+		mempool_create_kmalloc_pool(FSF_STATUS_READS_RECOM,
 					    sizeof(struct zfcp_fsf_req));
 	if (!adapter->pool.fsf_req_status_read)
 		return -ENOMEM;
 
 	adapter->pool.data_status_read =
-		mempool_create_slab_pool(ZFCP_POOL_STATUS_READ_NR,
+		mempool_create_slab_pool(FSF_STATUS_READS_RECOM,
 					 zfcp_data.sr_buffer_cache);
 	if (!adapter->pool.data_status_read)
 		return -ENOMEM;
 
 	adapter->pool.data_gid_pn =
-		mempool_create_slab_pool(ZFCP_POOL_DATA_GID_PN_NR,
-					 zfcp_data.gid_pn_cache);
+		mempool_create_slab_pool(1, zfcp_data.gid_pn_cache);
 	if (!adapter->pool.data_gid_pn)
 		return -ENOMEM;
 
 	return 0;
 }
 
-/**
- * zfcp_free_low_mem_buffers - free memory pools of an adapter
- * @adapter: pointer to zfcp_adapter for which memory pools should be freed
- * locking:  zfcp_data.config_sema must be held
- */
-static void
-zfcp_free_low_mem_buffers(struct zfcp_adapter *adapter)
+static void zfcp_free_low_mem_buffers(struct zfcp_adapter *adapter)
 {
+	/* zfcp_data.config_sema must be held */
 	if (adapter->pool.fsf_req_erp)
 		mempool_destroy(adapter->pool.fsf_req_erp);
 	if (adapter->pool.fsf_req_scsi)
@@ -962,20 +421,61 @@
 	return;
 }
 
-/*
+/**
+ * zfcp_status_read_refill - refill the long running status_read_requests
+ * @adapter: ptr to struct zfcp_adapter for which the buffers should be refilled
+ *
+ * Returns: 0 on success, 1 otherwise
+ *
+ * if there are 16 or more status_read requests missing an adapter_reopen
+ * is triggered
+ */
+int zfcp_status_read_refill(struct zfcp_adapter *adapter)
+{
+	while (atomic_read(&adapter->stat_miss) > 0)
+		if (zfcp_fsf_status_read(adapter)) {
+			if (atomic_read(&adapter->stat_miss) >= 16) {
+				zfcp_erp_adapter_reopen(adapter, 0, 103, NULL);
+				return 1;
+			}
+			break;
+		} else
+			atomic_dec(&adapter->stat_miss);
+	return 0;
+}
+
+static void _zfcp_status_read_scheduler(struct work_struct *work)
+{
+	zfcp_status_read_refill(container_of(work, struct zfcp_adapter,
+					     stat_work));
+}
+
+static int zfcp_nameserver_enqueue(struct zfcp_adapter *adapter)
+{
+	struct zfcp_port *port;
+
+	port = zfcp_port_enqueue(adapter, 0, ZFCP_STATUS_PORT_WKA,
+				 ZFCP_DID_DIRECTORY_SERVICE);
+	if (IS_ERR(port))
+		return PTR_ERR(port);
+	zfcp_port_put(port);
+
+	return 0;
+}
+
+/**
+ * zfcp_adapter_enqueue - enqueue a new adapter to the list
+ * @ccw_device: pointer to the struct cc_device
+ *
+ * Returns:	0             if a new adapter was successfully enqueued
+ *		-ENOMEM       if alloc failed
  * Enqueues an adapter at the end of the adapter list in the driver data.
  * All adapter internal structures are set up.
  * Proc-fs entries are also created.
- *
- * returns:	0             if a new adapter was successfully enqueued
- *              ZFCP_KNOWN    if an adapter with this devno was already present
- *		-ENOMEM       if alloc failed
  * locks:	config_sema must be held to serialise changes to the adapter list
  */
-struct zfcp_adapter *
-zfcp_adapter_enqueue(struct ccw_device *ccw_device)
+int zfcp_adapter_enqueue(struct ccw_device *ccw_device)
 {
-	int retval = 0;
 	struct zfcp_adapter *adapter;
 
 	/*
@@ -983,85 +483,58 @@
 	 * are protected by the config_sema, which must be held to get here
 	 */
 
-	/* try to allocate new adapter data structure (zeroed) */
-	adapter = kzalloc(sizeof (struct zfcp_adapter), GFP_KERNEL);
-	if (!adapter) {
-		ZFCP_LOG_INFO("error: allocation of base adapter "
-			      "structure failed\n");
-		goto out;
-	}
+	adapter = kzalloc(sizeof(struct zfcp_adapter), GFP_KERNEL);
+	if (!adapter)
+		return -ENOMEM;
 
 	ccw_device->handler = NULL;
-
-	/* save ccw_device pointer */
 	adapter->ccw_device = ccw_device;
+	atomic_set(&adapter->refcount, 0);
 
-	retval = zfcp_qdio_allocate_queues(adapter);
-	if (retval)
-		goto queues_alloc_failed;
-
-	retval = zfcp_qdio_allocate(adapter);
-	if (retval)
+	if (zfcp_qdio_allocate(adapter))
 		goto qdio_allocate_failed;
 
-	retval = zfcp_allocate_low_mem_buffers(adapter);
-	if (retval) {
-		ZFCP_LOG_INFO("error: pool allocation failed\n");
+	if (zfcp_allocate_low_mem_buffers(adapter))
 		goto failed_low_mem_buffers;
-	}
 
-	/* initialise reference count stuff */
-	atomic_set(&adapter->refcount, 0);
+	if (zfcp_reqlist_alloc(adapter))
+		goto failed_low_mem_buffers;
+
+	if (zfcp_adapter_debug_register(adapter))
+		goto debug_register_failed;
+
 	init_waitqueue_head(&adapter->remove_wq);
+	init_waitqueue_head(&adapter->erp_thread_wqh);
+	init_waitqueue_head(&adapter->erp_done_wqh);
 
-	/* initialise list of ports */
 	INIT_LIST_HEAD(&adapter->port_list_head);
-
-	/* initialise list of ports to be removed */
 	INIT_LIST_HEAD(&adapter->port_remove_lh);
+	INIT_LIST_HEAD(&adapter->erp_ready_head);
+	INIT_LIST_HEAD(&adapter->erp_running_head);
 
-	/* initialize list of fsf requests */
 	spin_lock_init(&adapter->req_list_lock);
-	retval = zfcp_reqlist_alloc(adapter);
-	if (retval) {
-		ZFCP_LOG_INFO("request list initialization failed\n");
-		goto failed_low_mem_buffers;
-	}
-
-	/* initialize debug locks */
 
 	spin_lock_init(&adapter->hba_dbf_lock);
 	spin_lock_init(&adapter->san_dbf_lock);
 	spin_lock_init(&adapter->scsi_dbf_lock);
 	spin_lock_init(&adapter->rec_dbf_lock);
-
-	retval = zfcp_adapter_debug_register(adapter);
-	if (retval)
-		goto debug_register_failed;
-
-	/* initialize error recovery stuff */
+	spin_lock_init(&adapter->req_q.lock);
 
 	rwlock_init(&adapter->erp_lock);
-	sema_init(&adapter->erp_ready_sem, 0);
-	INIT_LIST_HEAD(&adapter->erp_ready_head);
-	INIT_LIST_HEAD(&adapter->erp_running_head);
-
-	/* initialize abort lock */
 	rwlock_init(&adapter->abort_lock);
 
-	/* initialise some erp stuff */
-	init_waitqueue_head(&adapter->erp_thread_wqh);
-	init_waitqueue_head(&adapter->erp_done_wqh);
+	sema_init(&adapter->erp_ready_sem, 0);
 
-	/* initialize lock of associated request queue */
-	rwlock_init(&adapter->request_queue.queue_lock);
+	INIT_WORK(&adapter->stat_work, _zfcp_status_read_scheduler);
+	INIT_WORK(&adapter->scan_work, _zfcp_scan_ports_later);
 
 	/* mark adapter unusable as long as sysfs registration is not complete */
 	atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status);
 
 	dev_set_drvdata(&ccw_device->dev, adapter);
 
-	if (zfcp_sysfs_adapter_create_files(&ccw_device->dev))
+	if (sysfs_create_group(&ccw_device->dev.kobj,
+			       &zfcp_sysfs_adapter_attrs))
 		goto sysfs_failed;
 
 	adapter->generic_services.parent = &adapter->ccw_device->dev;
@@ -1072,7 +545,6 @@
 	if (device_register(&adapter->generic_services))
 		goto generic_services_failed;
 
-	/* put allocated adapter at list tail */
 	write_lock_irq(&zfcp_data.config_lock);
 	atomic_clear_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status);
 	list_add_tail(&adapter->list, &zfcp_data.adapter_list_head);
@@ -1080,57 +552,49 @@
 
 	zfcp_data.adapters++;
 
-	goto out;
+	zfcp_nameserver_enqueue(adapter);
 
- generic_services_failed:
-	zfcp_sysfs_adapter_remove_files(&adapter->ccw_device->dev);
- sysfs_failed:
+	return 0;
+
+generic_services_failed:
+	sysfs_remove_group(&ccw_device->dev.kobj,
+			   &zfcp_sysfs_adapter_attrs);
+sysfs_failed:
 	zfcp_adapter_debug_unregister(adapter);
- debug_register_failed:
+debug_register_failed:
 	dev_set_drvdata(&ccw_device->dev, NULL);
-	zfcp_reqlist_free(adapter);
- failed_low_mem_buffers:
+	kfree(adapter->req_list);
+failed_low_mem_buffers:
 	zfcp_free_low_mem_buffers(adapter);
-	if (qdio_free(ccw_device) != 0)
-		ZFCP_LOG_NORMAL("bug: qdio_free for adapter %s failed\n",
-				zfcp_get_busid_by_adapter(adapter));
- qdio_allocate_failed:
-	zfcp_qdio_free_queues(adapter);
- queues_alloc_failed:
+qdio_allocate_failed:
+	zfcp_qdio_free(adapter);
 	kfree(adapter);
-	adapter = NULL;
- out:
-	return adapter;
+	return -ENOMEM;
 }
 
-/*
- * returns:	0 - struct zfcp_adapter  data structure successfully removed
- *		!0 - struct zfcp_adapter  data structure could not be removed
- *			(e.g. still used)
+/**
+ * zfcp_adapter_dequeue - remove the adapter from the resource list
+ * @adapter: pointer to struct zfcp_adapter which should be removed
  * locks:	adapter list write lock is assumed to be held by caller
  */
-void
-zfcp_adapter_dequeue(struct zfcp_adapter *adapter)
+void zfcp_adapter_dequeue(struct zfcp_adapter *adapter)
 {
 	int retval = 0;
 	unsigned long flags;
 
+	cancel_work_sync(&adapter->scan_work);
+	cancel_work_sync(&adapter->stat_work);
 	zfcp_adapter_scsi_unregister(adapter);
 	device_unregister(&adapter->generic_services);
-	zfcp_sysfs_adapter_remove_files(&adapter->ccw_device->dev);
+	sysfs_remove_group(&adapter->ccw_device->dev.kobj,
+			   &zfcp_sysfs_adapter_attrs);
 	dev_set_drvdata(&adapter->ccw_device->dev, NULL);
 	/* sanity check: no pending FSF requests */
 	spin_lock_irqsave(&adapter->req_list_lock, flags);
 	retval = zfcp_reqlist_isempty(adapter);
 	spin_unlock_irqrestore(&adapter->req_list_lock, flags);
-	if (!retval) {
-		ZFCP_LOG_NORMAL("bug: adapter %s (%p) still in use, "
-				"%i requests outstanding\n",
-				zfcp_get_busid_by_adapter(adapter), adapter,
-				atomic_read(&adapter->reqs_active));
-		retval = -EBUSY;
-		goto out;
-	}
+	if (!retval)
+		return;
 
 	zfcp_adapter_debug_unregister(adapter);
 
@@ -1142,26 +606,18 @@
 	/* decrease number of adapters in list */
 	zfcp_data.adapters--;
 
-	ZFCP_LOG_TRACE("adapter %s (%p) removed from list, "
-		       "%i adapters still in list\n",
-		       zfcp_get_busid_by_adapter(adapter),
-		       adapter, zfcp_data.adapters);
-
-	retval = qdio_free(adapter->ccw_device);
-	if (retval)
-		ZFCP_LOG_NORMAL("bug: qdio_free for adapter %s failed\n",
-				zfcp_get_busid_by_adapter(adapter));
+	zfcp_qdio_free(adapter);
 
 	zfcp_free_low_mem_buffers(adapter);
-	/* free memory of adapter data structure and queues */
-	zfcp_qdio_free_queues(adapter);
-	zfcp_reqlist_free(adapter);
+	kfree(adapter->req_list);
 	kfree(adapter->fc_stats);
 	kfree(adapter->stats_reset_data);
-	ZFCP_LOG_TRACE("freeing adapter structure\n");
 	kfree(adapter);
- out:
-	return;
+}
+
+static void zfcp_sysfs_port_release(struct device *dev)
+{
+	kfree(container_of(dev, struct zfcp_port, sysfs_device));
 }
 
 /**
@@ -1170,98 +626,90 @@
  * @wwpn: WWPN of the remote port to be enqueued
  * @status: initial status for the port
  * @d_id: destination id of the remote port to be enqueued
- * Return: pointer to enqueued port on success, NULL on error
+ * Returns: pointer to enqueued port on success, ERR_PTR on error
  * Locks: config_sema must be held to serialize changes to the port list
  *
  * All port internal structures are set up and the sysfs entry is generated.
  * d_id is used to enqueue ports with a well known address like the Directory
  * Service for nameserver lookup.
  */
-struct zfcp_port *
-zfcp_port_enqueue(struct zfcp_adapter *adapter, wwn_t wwpn, u32 status,
-		  u32 d_id)
+struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *adapter, wwn_t wwpn,
+				     u32 status, u32 d_id)
 {
 	struct zfcp_port *port;
-	int check_wwpn;
+	int retval;
+	char *bus_id;
 
-	check_wwpn = !(status & ZFCP_STATUS_PORT_NO_WWPN);
-	/*
-	 * check that there is no port with this WWPN already in list
-	 */
-	if (check_wwpn) {
-		read_lock_irq(&zfcp_data.config_lock);
-		port = zfcp_get_port_by_wwpn(adapter, wwpn);
-		read_unlock_irq(&zfcp_data.config_lock);
-		if (port)
-			return NULL;
-	}
-
-	port = kzalloc(sizeof (struct zfcp_port), GFP_KERNEL);
+	port = kzalloc(sizeof(struct zfcp_port), GFP_KERNEL);
 	if (!port)
-		return NULL;
+		return ERR_PTR(-ENOMEM);
 
-	/* initialise reference count stuff */
-	atomic_set(&port->refcount, 0);
 	init_waitqueue_head(&port->remove_wq);
 
 	INIT_LIST_HEAD(&port->unit_list_head);
 	INIT_LIST_HEAD(&port->unit_remove_lh);
 
 	port->adapter = adapter;
+	port->d_id = d_id;
+	port->wwpn = wwpn;
 
-	if (check_wwpn)
-		port->wwpn = wwpn;
+	/* mark port unusable as long as sysfs registration is not complete */
+	atomic_set_mask(status | ZFCP_STATUS_COMMON_REMOVE, &port->status);
+	atomic_set(&port->refcount, 0);
 
-	atomic_set_mask(status, &port->status);
-
-	/* setup for sysfs registration */
 	if (status & ZFCP_STATUS_PORT_WKA) {
 		switch (d_id) {
 		case ZFCP_DID_DIRECTORY_SERVICE:
-			snprintf(port->sysfs_device.bus_id, BUS_ID_SIZE,
-				 "directory");
+			bus_id = "directory";
 			break;
 		case ZFCP_DID_MANAGEMENT_SERVICE:
-			snprintf(port->sysfs_device.bus_id, BUS_ID_SIZE,
-				 "management");
+			bus_id = "management";
 			break;
 		case ZFCP_DID_KEY_DISTRIBUTION_SERVICE:
-			snprintf(port->sysfs_device.bus_id, BUS_ID_SIZE,
-				 "key_distribution");
+			bus_id = "key_distribution";
 			break;
 		case ZFCP_DID_ALIAS_SERVICE:
-			snprintf(port->sysfs_device.bus_id, BUS_ID_SIZE,
-				 "alias");
+			bus_id = "alias";
 			break;
 		case ZFCP_DID_TIME_SERVICE:
-			snprintf(port->sysfs_device.bus_id, BUS_ID_SIZE,
-				 "time");
+			bus_id = "time";
 			break;
 		default:
 			kfree(port);
-			return NULL;
+			return ERR_PTR(-EINVAL);
 		}
-		port->d_id = d_id;
+		snprintf(port->sysfs_device.bus_id, BUS_ID_SIZE, "%s", bus_id);
 		port->sysfs_device.parent = &adapter->generic_services;
 	} else {
 		snprintf(port->sysfs_device.bus_id,
 			 BUS_ID_SIZE, "0x%016llx", wwpn);
 		port->sysfs_device.parent = &adapter->ccw_device->dev;
 	}
+
 	port->sysfs_device.release = zfcp_sysfs_port_release;
 	dev_set_drvdata(&port->sysfs_device, port);
 
-	/* mark port unusable as long as sysfs registration is not complete */
-	atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status);
+	read_lock_irq(&zfcp_data.config_lock);
+	if (!(status & ZFCP_STATUS_PORT_NO_WWPN))
+		if (zfcp_get_port_by_wwpn(adapter, wwpn)) {
+			read_unlock_irq(&zfcp_data.config_lock);
+			goto err_out_free;
+		}
+	read_unlock_irq(&zfcp_data.config_lock);
 
-	if (device_register(&port->sysfs_device)) {
-		kfree(port);
-		return NULL;
-	}
+	if (device_register(&port->sysfs_device))
+		goto err_out_free;
 
-	if (zfcp_sysfs_port_create_files(&port->sysfs_device, status)) {
+	if (status & ZFCP_STATUS_PORT_WKA)
+		retval = sysfs_create_group(&port->sysfs_device.kobj,
+					    &zfcp_sysfs_ns_port_attrs);
+	else
+		retval = sysfs_create_group(&port->sysfs_device.kobj,
+					    &zfcp_sysfs_port_attrs);
+
+	if (retval) {
 		device_unregister(&port->sysfs_device);
-		return NULL;
+		goto err_out;
 	}
 
 	zfcp_port_get(port);
@@ -1274,15 +722,23 @@
 		if (!adapter->nameserver_port)
 			adapter->nameserver_port = port;
 	adapter->ports++;
+
 	write_unlock_irq(&zfcp_data.config_lock);
 
 	zfcp_adapter_get(adapter);
-
 	return port;
+
+err_out_free:
+	kfree(port);
+err_out:
+	return ERR_PTR(-EINVAL);
 }
 
-void
-zfcp_port_dequeue(struct zfcp_port *port)
+/**
+ * zfcp_port_dequeue - dequeues a port from the port list of the adapter
+ * @port: pointer to struct zfcp_port which should be removed
+ */
+void zfcp_port_dequeue(struct zfcp_port *port)
 {
 	zfcp_port_wait(port);
 	write_lock_irq(&zfcp_data.config_lock);
@@ -1293,546 +749,53 @@
 		fc_remote_port_delete(port->rport);
 	port->rport = NULL;
 	zfcp_adapter_put(port->adapter);
-	zfcp_sysfs_port_remove_files(&port->sysfs_device,
-				     atomic_read(&port->status));
+	if (atomic_read(&port->status) & ZFCP_STATUS_PORT_WKA)
+		sysfs_remove_group(&port->sysfs_device.kobj,
+				   &zfcp_sysfs_ns_port_attrs);
+	else
+		sysfs_remove_group(&port->sysfs_device.kobj,
+				   &zfcp_sysfs_port_attrs);
 	device_unregister(&port->sysfs_device);
 }
 
-/* Enqueues a nameserver port */
-int
-zfcp_nameserver_enqueue(struct zfcp_adapter *adapter)
+/**
+ * zfcp_sg_free_table - free memory used by scatterlists
+ * @sg: pointer to scatterlist
+ * @count: number of scatterlist which are to be free'ed
+ * the scatterlist are expected to reference pages always
+ */
+void zfcp_sg_free_table(struct scatterlist *sg, int count)
 {
-	struct zfcp_port *port;
+	int i;
 
-	port = zfcp_port_enqueue(adapter, 0, ZFCP_STATUS_PORT_WKA,
-				 ZFCP_DID_DIRECTORY_SERVICE);
-	if (!port) {
-		ZFCP_LOG_INFO("error: enqueue of nameserver port for "
-			      "adapter %s failed\n",
-			      zfcp_get_busid_by_adapter(adapter));
-		return -ENXIO;
+	for (i = 0; i < count; i++, sg++)
+		if (sg)
+			free_page((unsigned long) sg_virt(sg));
+		else
+			break;
+}
+
+/**
+ * zfcp_sg_setup_table - init scatterlist and allocate, assign buffers
+ * @sg: pointer to struct scatterlist
+ * @count: number of scatterlists which should be assigned with buffers
+ * of size page
+ *
+ * Returns: 0 on success, -ENOMEM otherwise
+ */
+int zfcp_sg_setup_table(struct scatterlist *sg, int count)
+{
+	void *addr;
+	int i;
+
+	sg_init_table(sg, count);
+	for (i = 0; i < count; i++, sg++) {
+		addr = (void *) get_zeroed_page(GFP_KERNEL);
+		if (!addr) {
+			zfcp_sg_free_table(sg, i);
+			return -ENOMEM;
+		}
+		sg_set_buf(sg, addr, PAGE_SIZE);
 	}
-	zfcp_port_put(port);
-
 	return 0;
 }
-
-#undef ZFCP_LOG_AREA
-
-/****************************************************************/
-/******* Fibre Channel Standard related Functions  **************/
-/****************************************************************/
-
-#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FC
-
-static void zfcp_fsf_incoming_els_rscn(struct zfcp_fsf_req *fsf_req)
-{
-	struct fsf_status_read_buffer *status_buffer = (void*)fsf_req->data;
-	struct zfcp_adapter *adapter = fsf_req->adapter;
-	struct fcp_rscn_head *fcp_rscn_head;
-	struct fcp_rscn_element *fcp_rscn_element;
-	struct zfcp_port *port;
-	u16 i;
-	u16 no_entries;
-	u32 range_mask;
-	unsigned long flags;
-
-	fcp_rscn_head = (struct fcp_rscn_head *) status_buffer->payload;
-	fcp_rscn_element = (struct fcp_rscn_element *) status_buffer->payload;
-
-	/* see FC-FS */
-	no_entries = (fcp_rscn_head->payload_len / 4);
-
-	for (i = 1; i < no_entries; i++) {
-		/* skip head and start with 1st element */
-		fcp_rscn_element++;
-		switch (fcp_rscn_element->addr_format) {
-		case ZFCP_PORT_ADDRESS:
-			range_mask = ZFCP_PORTS_RANGE_PORT;
-			break;
-		case ZFCP_AREA_ADDRESS:
-			range_mask = ZFCP_PORTS_RANGE_AREA;
-			break;
-		case ZFCP_DOMAIN_ADDRESS:
-			range_mask = ZFCP_PORTS_RANGE_DOMAIN;
-			break;
-		case ZFCP_FABRIC_ADDRESS:
-			range_mask = ZFCP_PORTS_RANGE_FABRIC;
-			break;
-		default:
-			ZFCP_LOG_INFO("incoming RSCN with unknown "
-				      "address format\n");
-			continue;
-		}
-		read_lock_irqsave(&zfcp_data.config_lock, flags);
-		list_for_each_entry(port, &adapter->port_list_head, list) {
-			if (atomic_test_mask
-			    (ZFCP_STATUS_PORT_WKA, &port->status))
-				continue;
-			/* Do we know this port? If not skip it. */
-			if (!atomic_test_mask
-			    (ZFCP_STATUS_PORT_DID_DID, &port->status)) {
-				ZFCP_LOG_INFO("incoming RSCN, trying to open "
-					      "port 0x%016Lx\n", port->wwpn);
-				zfcp_erp_port_reopen(port,
-						     ZFCP_STATUS_COMMON_ERP_FAILED,
-						     82, fsf_req);
-				continue;
-			}
-
-			/*
-			 * FIXME: race: d_id might being invalidated
-			 * (...DID_DID reset)
-			 */
-			if ((port->d_id & range_mask)
-			    == (fcp_rscn_element->nport_did & range_mask)) {
-				ZFCP_LOG_TRACE("reopen did 0x%08x\n",
-					       fcp_rscn_element->nport_did);
-				/*
-				 * Unfortunately, an RSCN does not specify the
-				 * type of change a target underwent. We assume
-				 * that it makes sense to reopen the link.
-				 * FIXME: Shall we try to find out more about
-				 * the target and link state before closing it?
-				 * How to accomplish this? (nameserver?)
-				 * Where would such code be put in?
-				 * (inside or outside erp)
-				 */
-				ZFCP_LOG_INFO("incoming RSCN, trying to open "
-					      "port 0x%016Lx\n", port->wwpn);
-				zfcp_test_link(port);
-			}
-		}
-		read_unlock_irqrestore(&zfcp_data.config_lock, flags);
-	}
-}
-
-static void zfcp_fsf_incoming_els_plogi(struct zfcp_fsf_req *fsf_req)
-{
-	struct fsf_status_read_buffer *status_buffer = (void*)fsf_req->data;
-	struct zfcp_adapter *adapter = fsf_req->adapter;
-	struct fsf_plogi *els_plogi;
-	struct zfcp_port *port;
-	unsigned long flags;
-
-	els_plogi = (struct fsf_plogi *) status_buffer->payload;
-	read_lock_irqsave(&zfcp_data.config_lock, flags);
-	list_for_each_entry(port, &adapter->port_list_head, list) {
-		if (port->wwpn == (*(wwn_t *) &els_plogi->serv_param.wwpn))
-			break;
-	}
-	read_unlock_irqrestore(&zfcp_data.config_lock, flags);
-
-	if (!port || (port->wwpn != (*(wwn_t *) &els_plogi->serv_param.wwpn))) {
-		ZFCP_LOG_DEBUG("ignored incoming PLOGI for nonexisting port "
-			       "with d_id 0x%06x on adapter %s\n",
-			       status_buffer->d_id,
-			       zfcp_get_busid_by_adapter(adapter));
-	} else {
-		zfcp_erp_port_forced_reopen(port, 0, 83, fsf_req);
-	}
-}
-
-static void zfcp_fsf_incoming_els_logo(struct zfcp_fsf_req *fsf_req)
-{
-	struct fsf_status_read_buffer *status_buffer = (void*)fsf_req->data;
-	struct zfcp_adapter *adapter = fsf_req->adapter;
-	struct fcp_logo *els_logo = (struct fcp_logo *) status_buffer->payload;
-	struct zfcp_port *port;
-	unsigned long flags;
-
-	read_lock_irqsave(&zfcp_data.config_lock, flags);
-	list_for_each_entry(port, &adapter->port_list_head, list) {
-		if (port->wwpn == els_logo->nport_wwpn)
-			break;
-	}
-	read_unlock_irqrestore(&zfcp_data.config_lock, flags);
-
-	if (!port || (port->wwpn != els_logo->nport_wwpn)) {
-		ZFCP_LOG_DEBUG("ignored incoming LOGO for nonexisting port "
-			       "with d_id 0x%06x on adapter %s\n",
-			       status_buffer->d_id,
-			       zfcp_get_busid_by_adapter(adapter));
-	} else {
-		zfcp_erp_port_forced_reopen(port, 0, 84, fsf_req);
-	}
-}
-
-static void
-zfcp_fsf_incoming_els_unknown(struct zfcp_adapter *adapter,
-			      struct fsf_status_read_buffer *status_buffer)
-{
-	ZFCP_LOG_NORMAL("warning: unknown incoming ELS 0x%08x "
-			"for adapter %s\n", *(u32 *) (status_buffer->payload),
-			zfcp_get_busid_by_adapter(adapter));
-
-}
-
-void
-zfcp_fsf_incoming_els(struct zfcp_fsf_req *fsf_req)
-{
-	struct fsf_status_read_buffer *status_buffer;
-	u32 els_type;
-	struct zfcp_adapter *adapter;
-
-	status_buffer = (struct fsf_status_read_buffer *) fsf_req->data;
-	els_type = *(u32 *) (status_buffer->payload);
-	adapter = fsf_req->adapter;
-
-	zfcp_san_dbf_event_incoming_els(fsf_req);
-	if (els_type == LS_PLOGI)
-		zfcp_fsf_incoming_els_plogi(fsf_req);
-	else if (els_type == LS_LOGO)
-		zfcp_fsf_incoming_els_logo(fsf_req);
-	else if ((els_type & 0xffff0000) == LS_RSCN)
-		/* we are only concerned with the command, not the length */
-		zfcp_fsf_incoming_els_rscn(fsf_req);
-	else
-		zfcp_fsf_incoming_els_unknown(adapter, status_buffer);
-}
-
-
-/**
- * zfcp_gid_pn_buffers_alloc - allocate buffers for GID_PN nameserver request
- * @gid_pn: pointer to return pointer to struct zfcp_gid_pn_data
- * @pool: pointer to mempool_t if non-null memory pool is used for allocation
- */
-static int
-zfcp_gid_pn_buffers_alloc(struct zfcp_gid_pn_data **gid_pn, mempool_t *pool)
-{
-	struct zfcp_gid_pn_data *data;
-
-	if (pool != NULL) {
-		data = mempool_alloc(pool, GFP_ATOMIC);
-		if (likely(data != NULL)) {
-			data->ct.pool = pool;
-		}
-	} else {
-		data = kmem_cache_alloc(zfcp_data.gid_pn_cache, GFP_ATOMIC);
-	}
-
-        if (NULL == data)
-                return -ENOMEM;
-
-	memset(data, 0, sizeof(*data));
-	sg_init_table(&data->req , 1);
-	sg_init_table(&data->resp , 1);
-        data->ct.req = &data->req;
-        data->ct.resp = &data->resp;
-	data->ct.req_count = data->ct.resp_count = 1;
-	zfcp_address_to_sg(&data->ct_iu_req, &data->req, sizeof(struct ct_iu_gid_pn_req));
-        zfcp_address_to_sg(&data->ct_iu_resp, &data->resp, sizeof(struct ct_iu_gid_pn_resp));
-
-	*gid_pn = data;
-	return 0;
-}
-
-/**
- * zfcp_gid_pn_buffers_free - free buffers for GID_PN nameserver request
- * @gid_pn: pointer to struct zfcp_gid_pn_data which has to be freed
- */
-static void zfcp_gid_pn_buffers_free(struct zfcp_gid_pn_data *gid_pn)
-{
-	if (gid_pn->ct.pool)
-		mempool_free(gid_pn, gid_pn->ct.pool);
-	else
-		kmem_cache_free(zfcp_data.gid_pn_cache, gid_pn);
-}
-
-/**
- * zfcp_ns_gid_pn_request - initiate GID_PN nameserver request
- * @erp_action: pointer to zfcp_erp_action where GID_PN request is needed
- */
-int
-zfcp_ns_gid_pn_request(struct zfcp_erp_action *erp_action)
-{
-	int ret;
-        struct ct_iu_gid_pn_req *ct_iu_req;
-        struct zfcp_gid_pn_data *gid_pn;
-        struct zfcp_adapter *adapter = erp_action->adapter;
-
-	ret = zfcp_gid_pn_buffers_alloc(&gid_pn, adapter->pool.data_gid_pn);
-	if (ret < 0) {
-		ZFCP_LOG_INFO("error: buffer allocation for gid_pn nameserver "
-			      "request failed for adapter %s\n",
-			      zfcp_get_busid_by_adapter(adapter));
-		goto out;
-	}
-
-	/* setup nameserver request */
-        ct_iu_req = zfcp_sg_to_address(gid_pn->ct.req);
-        ct_iu_req->header.revision = ZFCP_CT_REVISION;
-        ct_iu_req->header.gs_type = ZFCP_CT_DIRECTORY_SERVICE;
-        ct_iu_req->header.gs_subtype = ZFCP_CT_NAME_SERVER;
-        ct_iu_req->header.options = ZFCP_CT_SYNCHRONOUS;
-        ct_iu_req->header.cmd_rsp_code = ZFCP_CT_GID_PN;
-        ct_iu_req->header.max_res_size = ZFCP_CT_MAX_SIZE;
-	ct_iu_req->wwpn = erp_action->port->wwpn;
-
-        /* setup parameters for send generic command */
-        gid_pn->ct.port = adapter->nameserver_port;
-	gid_pn->ct.handler = zfcp_ns_gid_pn_handler;
-	gid_pn->ct.handler_data = (unsigned long) gid_pn;
-        gid_pn->ct.timeout = ZFCP_NS_GID_PN_TIMEOUT;
-	gid_pn->port = erp_action->port;
-
-	ret = zfcp_fsf_send_ct(&gid_pn->ct, adapter->pool.fsf_req_erp,
-			       erp_action);
-	if (ret) {
-		ZFCP_LOG_INFO("error: initiation of gid_pn nameserver request "
-                              "failed for adapter %s\n",
-			      zfcp_get_busid_by_adapter(adapter));
-
-                zfcp_gid_pn_buffers_free(gid_pn);
-	}
-
- out:
-	return ret;
-}
-
-/**
- * zfcp_ns_gid_pn_handler - handler for GID_PN nameserver request
- * @data: unsigned long, contains pointer to struct zfcp_gid_pn_data
- */
-static void zfcp_ns_gid_pn_handler(unsigned long data)
-{
-	struct zfcp_port *port;
-        struct zfcp_send_ct *ct;
-	struct ct_iu_gid_pn_req *ct_iu_req;
-	struct ct_iu_gid_pn_resp *ct_iu_resp;
-        struct zfcp_gid_pn_data *gid_pn;
-
-
-	gid_pn = (struct zfcp_gid_pn_data *) data;
-	port = gid_pn->port;
-        ct = &gid_pn->ct;
-	ct_iu_req = zfcp_sg_to_address(ct->req);
-	ct_iu_resp = zfcp_sg_to_address(ct->resp);
-
-	if (ct->status != 0)
-		goto failed;
-
-	if (zfcp_check_ct_response(&ct_iu_resp->header)) {
-		/* FIXME: do we need some specific erp entry points */
-		atomic_set_mask(ZFCP_STATUS_PORT_INVALID_WWPN, &port->status);
-		goto failed;
-	}
-	/* paranoia */
-	if (ct_iu_req->wwpn != port->wwpn) {
-		ZFCP_LOG_NORMAL("bug: wwpn 0x%016Lx returned by nameserver "
-				"lookup does not match expected wwpn 0x%016Lx "
-				"for adapter %s\n", ct_iu_req->wwpn, port->wwpn,
-				zfcp_get_busid_by_port(port));
-		goto mismatch;
-	}
-
-	/* looks like a valid d_id */
-        port->d_id = ct_iu_resp->d_id & ZFCP_DID_MASK;
-	atomic_set_mask(ZFCP_STATUS_PORT_DID_DID, &port->status);
-	ZFCP_LOG_DEBUG("adapter %s:  wwpn=0x%016Lx ---> d_id=0x%06x\n",
-		       zfcp_get_busid_by_port(port), port->wwpn, port->d_id);
-	goto out;
-
- mismatch:
-	ZFCP_LOG_DEBUG("CT IUs do not match:\n");
-	ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG, (char *) ct_iu_req,
-		      sizeof(struct ct_iu_gid_pn_req));
-	ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG, (char *) ct_iu_resp,
-		      sizeof(struct ct_iu_gid_pn_resp));
-
- failed:
-	ZFCP_LOG_NORMAL("warning: failed gid_pn nameserver request for wwpn "
-			"0x%016Lx for adapter %s\n",
-			port->wwpn, zfcp_get_busid_by_port(port));
- out:
-        zfcp_gid_pn_buffers_free(gid_pn);
-	return;
-}
-
-/* reject CT_IU reason codes acc. to FC-GS-4 */
-static const struct zfcp_rc_entry zfcp_ct_rc[] = {
-	{0x01, "invalid command code"},
-	{0x02, "invalid version level"},
-	{0x03, "logical error"},
-	{0x04, "invalid CT_IU size"},
-	{0x05, "logical busy"},
-	{0x07, "protocol error"},
-	{0x09, "unable to perform command request"},
-	{0x0b, "command not supported"},
-	{0x0d, "server not available"},
-	{0x0e, "session could not be established"},
-	{0xff, "vendor specific error"},
-	{0, NULL},
-};
-
-/* LS_RJT reason codes acc. to FC-FS */
-static const struct zfcp_rc_entry zfcp_ls_rjt_rc[] = {
-	{0x01, "invalid LS_Command code"},
-	{0x03, "logical error"},
-	{0x05, "logical busy"},
-	{0x07, "protocol error"},
-	{0x09, "unable to perform command request"},
-	{0x0b, "command not supported"},
-	{0x0e, "command already in progress"},
-	{0xff, "vendor specific error"},
-	{0, NULL},
-};
-
-/* reject reason codes according to FC-PH/FC-FS */
-static const struct zfcp_rc_entry zfcp_p_rjt_rc[] = {
-	{0x01, "invalid D_ID"},
-	{0x02, "invalid S_ID"},
-	{0x03, "Nx_Port not available, temporary"},
-	{0x04, "Nx_Port not available, permament"},
-	{0x05, "class not supported"},
-	{0x06, "delimiter usage error"},
-	{0x07, "TYPE not supported"},
-	{0x08, "invalid Link_Control"},
-	{0x09, "invalid R_CTL field"},
-	{0x0a, "invalid F_CTL field"},
-	{0x0b, "invalid OX_ID"},
-	{0x0c, "invalid RX_ID"},
-	{0x0d, "invalid SEQ_ID"},
-	{0x0e, "invalid DF_CTL"},
-	{0x0f, "invalid SEQ_CNT"},
-	{0x10, "invalid parameter field"},
-	{0x11, "exchange error"},
-	{0x12, "protocol error"},
-	{0x13, "incorrect length"},
-	{0x14, "unsupported ACK"},
-	{0x15, "class of service not supported by entity at FFFFFE"},
-	{0x16, "login required"},
-	{0x17, "excessive sequences attempted"},
-	{0x18, "unable to establish exchange"},
-	{0x1a, "fabric path not available"},
-	{0x1b, "invalid VC_ID (class 4)"},
-	{0x1c, "invalid CS_CTL field"},
-	{0x1d, "insufficient resources for VC (class 4)"},
-	{0x1f, "invalid class of service"},
-	{0x20, "preemption request rejected"},
-	{0x21, "preemption not enabled"},
-	{0x22, "multicast error"},
-	{0x23, "multicast error terminate"},
-	{0x24, "process login required"},
-	{0xff, "vendor specific reject"},
-	{0, NULL},
-};
-
-/**
- * zfcp_rc_description - return description for given reaon code
- * @code: reason code
- * @rc_table: table of reason codes and descriptions
- */
-static const char *
-zfcp_rc_description(u8 code, const struct zfcp_rc_entry *rc_table)
-{
-	const char *descr = "unknown reason code";
-
-	do {
-		if (code == rc_table->code) {
-			descr = rc_table->description;
-			break;
-		}
-		rc_table++;
-	} while (rc_table->code && rc_table->description);
-
-	return descr;
-}
-
-/**
- * zfcp_check_ct_response - evaluate reason code for CT_IU
- * @rjt: response payload to an CT_IU request
- * Return: 0 for accept CT_IU, 1 for reject CT_IU or invlid response code
- */
-int
-zfcp_check_ct_response(struct ct_hdr *rjt)
-{
-	if (rjt->cmd_rsp_code == ZFCP_CT_ACCEPT)
-		return 0;
-
-	if (rjt->cmd_rsp_code != ZFCP_CT_REJECT) {
-		ZFCP_LOG_NORMAL("error: invalid Generic Service command/"
-				"response code (0x%04hx)\n",
-				rjt->cmd_rsp_code);
-		return 1;
-	}
-
-	ZFCP_LOG_INFO("Generic Service command rejected\n");
-	ZFCP_LOG_INFO("%s (0x%02x, 0x%02x, 0x%02x)\n",
-		      zfcp_rc_description(rjt->reason_code, zfcp_ct_rc),
-		      (u32) rjt->reason_code, (u32) rjt->reason_code_expl,
-		      (u32) rjt->vendor_unique);
-
-	return 1;
-}
-
-/**
- * zfcp_print_els_rjt - print reject parameter and description for ELS reject
- * @rjt_par: reject parameter acc. to FC-PH/FC-FS
- * @rc_table: table of reason codes and descriptions
- */
-static void
-zfcp_print_els_rjt(struct zfcp_ls_rjt_par *rjt_par,
-		   const struct zfcp_rc_entry *rc_table)
-{
-	ZFCP_LOG_INFO("%s (%02x %02x %02x %02x)\n",
-		      zfcp_rc_description(rjt_par->reason_code, rc_table),
-		      (u32) rjt_par->action, (u32) rjt_par->reason_code,
-		      (u32) rjt_par->reason_expl, (u32) rjt_par->vendor_unique);
-}
-
-/**
- * zfcp_fsf_handle_els_rjt - evaluate status qualifier/reason code on ELS reject
- * @sq: status qualifier word
- * @rjt_par: reject parameter as described in FC-PH and FC-FS
- * Return: -EROMTEIO for LS_RJT, -EREMCHG for invalid D_ID, -EIO else
- */
-int
-zfcp_handle_els_rjt(u32 sq, struct zfcp_ls_rjt_par *rjt_par)
-{
-	int ret = -EIO;
-
-	if (sq == FSF_IOSTAT_NPORT_RJT) {
-		ZFCP_LOG_INFO("ELS rejected (P_RJT)\n");
-		zfcp_print_els_rjt(rjt_par, zfcp_p_rjt_rc);
-		/* invalid d_id */
-		if (rjt_par->reason_code == 0x01)
-			ret = -EREMCHG;
-	} else if (sq == FSF_IOSTAT_FABRIC_RJT) {
-		ZFCP_LOG_INFO("ELS rejected (F_RJT)\n");
-		zfcp_print_els_rjt(rjt_par, zfcp_p_rjt_rc);
-		/* invalid d_id */
-		if (rjt_par->reason_code == 0x01)
-			ret = -EREMCHG;
-	} else if (sq == FSF_IOSTAT_LS_RJT) {
-		ZFCP_LOG_INFO("ELS rejected (LS_RJT)\n");
-		zfcp_print_els_rjt(rjt_par, zfcp_ls_rjt_rc);
-		ret = -EREMOTEIO;
-	} else
-		ZFCP_LOG_INFO("unexpected SQ: 0x%02x\n", sq);
-
-	return ret;
-}
-
-/**
- * zfcp_plogi_evaluate - evaluate PLOGI playload and copy important fields
- * into zfcp_port structure
- * @port: zfcp_port structure
- * @plogi: plogi payload
- */
-void
-zfcp_plogi_evaluate(struct zfcp_port *port, struct fsf_plogi *plogi)
-{
-	port->maxframe_size = plogi->serv_param.common_serv_param[7] |
-		((plogi->serv_param.common_serv_param[6] & 0x0F) << 8);
-	if (plogi->serv_param.class1_serv_param[0] & 0x80)
-		port->supported_classes |= FC_COS_CLASS1;
-	if (plogi->serv_param.class2_serv_param[0] & 0x80)
-		port->supported_classes |= FC_COS_CLASS2;
-	if (plogi->serv_param.class3_serv_param[0] & 0x80)
-		port->supported_classes |= FC_COS_CLASS3;
-	if (plogi->serv_param.class4_serv_param[0] & 0x80)
-		port->supported_classes |= FC_COS_CLASS4;
-}
-
-#undef ZFCP_LOG_AREA
diff --git a/drivers/s390/scsi/zfcp_ccw.c b/drivers/s390/scsi/zfcp_ccw.c
index 66d3b88..391dd29 100644
--- a/drivers/s390/scsi/zfcp_ccw.c
+++ b/drivers/s390/scsi/zfcp_ccw.c
@@ -1,64 +1,13 @@
 /*
- * This file is part of the zfcp device driver for
- * FCP adapters for IBM System z9 and zSeries.
+ * zfcp device driver
  *
- * (C) Copyright IBM Corp. 2002, 2006
+ * Registration and callback for the s390 common I/O layer.
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * Copyright IBM Corporation 2002, 2008
  */
 
 #include "zfcp_ext.h"
 
-#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_CONFIG
-
-static int zfcp_ccw_probe(struct ccw_device *);
-static void zfcp_ccw_remove(struct ccw_device *);
-static int zfcp_ccw_set_online(struct ccw_device *);
-static int zfcp_ccw_set_offline(struct ccw_device *);
-static int zfcp_ccw_notify(struct ccw_device *, int);
-static void zfcp_ccw_shutdown(struct ccw_device *);
-
-static struct ccw_device_id zfcp_ccw_device_id[] = {
-	{CCW_DEVICE_DEVTYPE(ZFCP_CONTROL_UNIT_TYPE,
-			    ZFCP_CONTROL_UNIT_MODEL,
-			    ZFCP_DEVICE_TYPE,
-			    ZFCP_DEVICE_MODEL)},
-	{CCW_DEVICE_DEVTYPE(ZFCP_CONTROL_UNIT_TYPE,
-			    ZFCP_CONTROL_UNIT_MODEL,
-			    ZFCP_DEVICE_TYPE,
-			    ZFCP_DEVICE_MODEL_PRIV)},
-	{},
-};
-
-static struct ccw_driver zfcp_ccw_driver = {
-	.owner       = THIS_MODULE,
-	.name        = ZFCP_NAME,
-	.ids         = zfcp_ccw_device_id,
-	.probe       = zfcp_ccw_probe,
-	.remove      = zfcp_ccw_remove,
-	.set_online  = zfcp_ccw_set_online,
-	.set_offline = zfcp_ccw_set_offline,
-	.notify      = zfcp_ccw_notify,
-	.shutdown    = zfcp_ccw_shutdown,
-	.driver = {
-		.groups = zfcp_driver_attr_groups,
-	},
-};
-
-MODULE_DEVICE_TABLE(ccw, zfcp_ccw_device_id);
-
 /**
  * zfcp_ccw_probe - probe function of zfcp driver
  * @ccw_device: pointer to belonging ccw device
@@ -69,19 +18,16 @@
  * In addition the nameserver port will be added to the ports of the adapter
  * and its sysfs representation will be created too.
  */
-static int
-zfcp_ccw_probe(struct ccw_device *ccw_device)
+static int zfcp_ccw_probe(struct ccw_device *ccw_device)
 {
-	struct zfcp_adapter *adapter;
 	int retval = 0;
 
 	down(&zfcp_data.config_sema);
-	adapter = zfcp_adapter_enqueue(ccw_device);
-	if (!adapter)
+	if (zfcp_adapter_enqueue(ccw_device)) {
+		dev_err(&ccw_device->dev,
+			"Setup of data structures failed.\n");
 		retval = -EINVAL;
-	else
-		ZFCP_LOG_DEBUG("Probed adapter %s\n",
-			       zfcp_get_busid_by_adapter(adapter));
+	}
 	up(&zfcp_data.config_sema);
 	return retval;
 }
@@ -95,8 +41,7 @@
  * ports that belong to this adapter. And in addition all resources of this
  * adapter will be freed too.
  */
-static void
-zfcp_ccw_remove(struct ccw_device *ccw_device)
+static void zfcp_ccw_remove(struct ccw_device *ccw_device)
 {
 	struct zfcp_adapter *adapter;
 	struct zfcp_port *port, *p;
@@ -106,8 +51,6 @@
 	down(&zfcp_data.config_sema);
 	adapter = dev_get_drvdata(&ccw_device->dev);
 
-	ZFCP_LOG_DEBUG("Removing adapter %s\n",
-		       zfcp_get_busid_by_adapter(adapter));
 	write_lock_irq(&zfcp_data.config_lock);
 	list_for_each_entry_safe(port, p, &adapter->port_list_head, list) {
 		list_for_each_entry_safe(unit, u, &port->unit_list_head, list) {
@@ -145,8 +88,7 @@
  * registered with the SCSI stack, that the QDIO queues will be set up
  * and that the adapter will be opened (asynchronously).
  */
-static int
-zfcp_ccw_set_online(struct ccw_device *ccw_device)
+static int zfcp_ccw_set_online(struct ccw_device *ccw_device)
 {
 	struct zfcp_adapter *adapter;
 	int retval;
@@ -155,12 +97,8 @@
 	adapter = dev_get_drvdata(&ccw_device->dev);
 
 	retval = zfcp_erp_thread_setup(adapter);
-	if (retval) {
-		ZFCP_LOG_INFO("error: start of error recovery thread for "
-			      "adapter %s failed\n",
-			      zfcp_get_busid_by_adapter(adapter));
+	if (retval)
 		goto out;
-	}
 
 	retval = zfcp_adapter_scsi_register(adapter);
 	if (retval)
@@ -191,8 +129,7 @@
  * This function gets called by the common i/o layer and sets an adapter
  * into state offline.
  */
-static int
-zfcp_ccw_set_offline(struct ccw_device *ccw_device)
+static int zfcp_ccw_set_offline(struct ccw_device *ccw_device)
 {
 	struct zfcp_adapter *adapter;
 
@@ -206,15 +143,14 @@
 }
 
 /**
- * zfcp_ccw_notify
+ * zfcp_ccw_notify - ccw notify function
  * @ccw_device: pointer to belonging ccw device
  * @event: indicates if adapter was detached or attached
  *
  * This function gets called by the common i/o layer if an adapter has gone
  * or reappeared.
  */
-static int
-zfcp_ccw_notify(struct ccw_device *ccw_device, int event)
+static int zfcp_ccw_notify(struct ccw_device *ccw_device, int event)
 {
 	struct zfcp_adapter *adapter;
 
@@ -222,18 +158,15 @@
 	adapter = dev_get_drvdata(&ccw_device->dev);
 	switch (event) {
 	case CIO_GONE:
-		ZFCP_LOG_NORMAL("adapter %s: device gone\n",
-				zfcp_get_busid_by_adapter(adapter));
+		dev_warn(&adapter->ccw_device->dev, "device gone\n");
 		zfcp_erp_adapter_shutdown(adapter, 0, 87, NULL);
 		break;
 	case CIO_NO_PATH:
-		ZFCP_LOG_NORMAL("adapter %s: no path\n",
-				zfcp_get_busid_by_adapter(adapter));
+		dev_warn(&adapter->ccw_device->dev, "no path\n");
 		zfcp_erp_adapter_shutdown(adapter, 0, 88, NULL);
 		break;
 	case CIO_OPER:
-		ZFCP_LOG_NORMAL("adapter %s: operational again\n",
-				zfcp_get_busid_by_adapter(adapter));
+		dev_info(&adapter->ccw_device->dev, "operational again\n");
 		zfcp_erp_modify_adapter_status(adapter, 11, NULL,
 					       ZFCP_STATUS_COMMON_RUNNING,
 					       ZFCP_SET);
@@ -247,24 +180,10 @@
 }
 
 /**
- * zfcp_ccw_register - ccw register function
- *
- * Registers the driver at the common i/o layer. This function will be called
- * at module load time/system start.
+ * zfcp_ccw_shutdown - handle shutdown from cio
+ * @cdev: device for adapter to shutdown.
  */
-int __init
-zfcp_ccw_register(void)
-{
-	return ccw_driver_register(&zfcp_ccw_driver);
-}
-
-/**
- * zfcp_ccw_shutdown - gets called on reboot/shutdown
- *
- * Makes sure that QDIO queues are down when the system gets stopped.
- */
-static void
-zfcp_ccw_shutdown(struct ccw_device *cdev)
+static void zfcp_ccw_shutdown(struct ccw_device *cdev)
 {
 	struct zfcp_adapter *adapter;
 
@@ -275,4 +194,33 @@
 	up(&zfcp_data.config_sema);
 }
 
-#undef ZFCP_LOG_AREA
+static struct ccw_device_id zfcp_ccw_device_id[] = {
+	{ CCW_DEVICE_DEVTYPE(0x1731, 0x3, 0x1732, 0x3) },
+	{ CCW_DEVICE_DEVTYPE(0x1731, 0x3, 0x1732, 0x4) }, /* priv. */
+	{},
+};
+
+MODULE_DEVICE_TABLE(ccw, zfcp_ccw_device_id);
+
+static struct ccw_driver zfcp_ccw_driver = {
+	.owner       = THIS_MODULE,
+	.name        = "zfcp",
+	.ids         = zfcp_ccw_device_id,
+	.probe       = zfcp_ccw_probe,
+	.remove      = zfcp_ccw_remove,
+	.set_online  = zfcp_ccw_set_online,
+	.set_offline = zfcp_ccw_set_offline,
+	.notify      = zfcp_ccw_notify,
+	.shutdown    = zfcp_ccw_shutdown,
+};
+
+/**
+ * zfcp_ccw_register - ccw register function
+ *
+ * Registers the driver at the common i/o layer. This function will be called
+ * at module load time/system start.
+ */
+int __init zfcp_ccw_register(void)
+{
+	return ccw_driver_register(&zfcp_ccw_driver);
+}
diff --git a/drivers/s390/scsi/zfcp_cfdc.c b/drivers/s390/scsi/zfcp_cfdc.c
new file mode 100644
index 0000000..ec2abce
--- /dev/null
+++ b/drivers/s390/scsi/zfcp_cfdc.c
@@ -0,0 +1,259 @@
+/*
+ * zfcp device driver
+ *
+ * Userspace interface for accessing the
+ * Access Control Lists / Control File Data Channel
+ *
+ * Copyright IBM Corporation 2008
+ */
+
+#include <linux/types.h>
+#include <linux/miscdevice.h>
+#include <asm/ccwdev.h>
+#include "zfcp_def.h"
+#include "zfcp_ext.h"
+#include "zfcp_fsf.h"
+
+#define ZFCP_CFDC_CMND_DOWNLOAD_NORMAL		0x00010001
+#define ZFCP_CFDC_CMND_DOWNLOAD_FORCE		0x00010101
+#define ZFCP_CFDC_CMND_FULL_ACCESS		0x00000201
+#define ZFCP_CFDC_CMND_RESTRICTED_ACCESS	0x00000401
+#define ZFCP_CFDC_CMND_UPLOAD			0x00010002
+
+#define ZFCP_CFDC_DOWNLOAD			0x00000001
+#define ZFCP_CFDC_UPLOAD			0x00000002
+#define ZFCP_CFDC_WITH_CONTROL_FILE		0x00010000
+
+#define ZFCP_CFDC_IOC_MAGIC                     0xDD
+#define ZFCP_CFDC_IOC \
+	_IOWR(ZFCP_CFDC_IOC_MAGIC, 0, struct zfcp_cfdc_data)
+
+/**
+ * struct zfcp_cfdc_data - data for ioctl cfdc interface
+ * @signature: request signature
+ * @devno: FCP adapter device number
+ * @command: command code
+ * @fsf_status: returns status of FSF command to userspace
+ * @fsf_status_qual: returned to userspace
+ * @payloads: access conflicts list
+ * @control_file: access control table
+ */
+struct zfcp_cfdc_data {
+	u32 signature;
+	u32 devno;
+	u32 command;
+	u32 fsf_status;
+	u8  fsf_status_qual[FSF_STATUS_QUALIFIER_SIZE];
+	u8  payloads[256];
+	u8  control_file[0];
+};
+
+static int zfcp_cfdc_copy_from_user(struct scatterlist *sg,
+				    void __user *user_buffer)
+{
+	unsigned int length;
+	unsigned int size = ZFCP_CFDC_MAX_SIZE;
+
+	while (size) {
+		length = min((unsigned int)size, sg->length);
+		if (copy_from_user(sg_virt(sg++), user_buffer, length))
+			return -EFAULT;
+		user_buffer += length;
+		size -= length;
+	}
+	return 0;
+}
+
+static int zfcp_cfdc_copy_to_user(void __user  *user_buffer,
+				  struct scatterlist *sg)
+{
+	unsigned int length;
+	unsigned int size = ZFCP_CFDC_MAX_SIZE;
+
+	while (size) {
+		length = min((unsigned int) size, sg->length);
+		if (copy_to_user(user_buffer, sg_virt(sg++), length))
+			return -EFAULT;
+		user_buffer += length;
+		size -= length;
+	}
+	return 0;
+}
+
+static struct zfcp_adapter *zfcp_cfdc_get_adapter(u32 devno)
+{
+	struct zfcp_adapter *adapter = NULL, *cur_adapter;
+	struct ccw_dev_id dev_id;
+
+	read_lock_irq(&zfcp_data.config_lock);
+	list_for_each_entry(cur_adapter, &zfcp_data.adapter_list_head, list) {
+		ccw_device_get_id(cur_adapter->ccw_device, &dev_id);
+		if (dev_id.devno == devno) {
+			adapter = cur_adapter;
+			zfcp_adapter_get(adapter);
+			break;
+		}
+	}
+	read_unlock_irq(&zfcp_data.config_lock);
+	return adapter;
+}
+
+static int zfcp_cfdc_set_fsf(struct zfcp_fsf_cfdc *fsf_cfdc, int command)
+{
+	switch (command) {
+	case ZFCP_CFDC_CMND_DOWNLOAD_NORMAL:
+		fsf_cfdc->command = FSF_QTCB_DOWNLOAD_CONTROL_FILE;
+		fsf_cfdc->option = FSF_CFDC_OPTION_NORMAL_MODE;
+		break;
+	case ZFCP_CFDC_CMND_DOWNLOAD_FORCE:
+		fsf_cfdc->command = FSF_QTCB_DOWNLOAD_CONTROL_FILE;
+		fsf_cfdc->option = FSF_CFDC_OPTION_FORCE;
+		break;
+	case ZFCP_CFDC_CMND_FULL_ACCESS:
+		fsf_cfdc->command = FSF_QTCB_DOWNLOAD_CONTROL_FILE;
+		fsf_cfdc->option = FSF_CFDC_OPTION_FULL_ACCESS;
+		break;
+	case ZFCP_CFDC_CMND_RESTRICTED_ACCESS:
+		fsf_cfdc->command = FSF_QTCB_DOWNLOAD_CONTROL_FILE;
+		fsf_cfdc->option = FSF_CFDC_OPTION_RESTRICTED_ACCESS;
+		break;
+	case ZFCP_CFDC_CMND_UPLOAD:
+		fsf_cfdc->command = FSF_QTCB_UPLOAD_CONTROL_FILE;
+		fsf_cfdc->option = 0;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int zfcp_cfdc_sg_setup(int command, struct scatterlist *sg,
+			      u8 __user *control_file)
+{
+	int retval;
+	retval = zfcp_sg_setup_table(sg, ZFCP_CFDC_PAGES);
+	if (retval)
+		return retval;
+
+	sg[ZFCP_CFDC_PAGES - 1].length = ZFCP_CFDC_MAX_SIZE % PAGE_SIZE;
+
+	if (command & ZFCP_CFDC_WITH_CONTROL_FILE &&
+	    command & ZFCP_CFDC_DOWNLOAD) {
+		retval = zfcp_cfdc_copy_from_user(sg, control_file);
+		if (retval) {
+			zfcp_sg_free_table(sg, ZFCP_CFDC_PAGES);
+			return -EFAULT;
+		}
+	}
+
+	return 0;
+}
+
+static void zfcp_cfdc_req_to_sense(struct zfcp_cfdc_data *data,
+				   struct zfcp_fsf_req *req)
+{
+	data->fsf_status = req->qtcb->header.fsf_status;
+	memcpy(&data->fsf_status_qual, &req->qtcb->header.fsf_status_qual,
+	       sizeof(union fsf_status_qual));
+	memcpy(&data->payloads, &req->qtcb->bottom.support.els,
+	       sizeof(req->qtcb->bottom.support.els));
+}
+
+static long zfcp_cfdc_dev_ioctl(struct file *file, unsigned int command,
+				unsigned long buffer)
+{
+	struct zfcp_cfdc_data *data;
+	struct zfcp_cfdc_data __user *data_user;
+	struct zfcp_adapter *adapter;
+	struct zfcp_fsf_req *req;
+	struct zfcp_fsf_cfdc *fsf_cfdc;
+	int retval;
+
+	if (command != ZFCP_CFDC_IOC)
+		return -ENOTTY;
+
+	data_user = (void __user *) buffer;
+	if (!data_user)
+		return -EINVAL;
+
+	fsf_cfdc = kmalloc(sizeof(struct zfcp_fsf_cfdc), GFP_KERNEL);
+	if (!fsf_cfdc)
+		return -ENOMEM;
+
+	data = kmalloc(sizeof(struct zfcp_cfdc_data), GFP_KERNEL);
+	if (!data) {
+		retval = -ENOMEM;
+		goto no_mem_sense;
+	}
+
+	retval = copy_from_user(data, data_user, sizeof(*data));
+	if (retval) {
+		retval = -EFAULT;
+		goto free_buffer;
+	}
+
+	if (data->signature != 0xCFDCACDF) {
+		retval = -EINVAL;
+		goto free_buffer;
+	}
+
+	retval = zfcp_cfdc_set_fsf(fsf_cfdc, data->command);
+
+	adapter = zfcp_cfdc_get_adapter(data->devno);
+	if (!adapter) {
+		retval = -ENXIO;
+		goto free_buffer;
+	}
+
+	retval = zfcp_cfdc_sg_setup(data->command, fsf_cfdc->sg,
+				    data_user->control_file);
+	if (retval)
+		goto adapter_put;
+	req = zfcp_fsf_control_file(adapter, fsf_cfdc);
+	if (IS_ERR(req)) {
+		retval = PTR_ERR(req);
+		goto free_sg;
+	}
+
+	if (req->status & ZFCP_STATUS_FSFREQ_ERROR) {
+		retval = -ENXIO;
+		goto free_fsf;
+	}
+
+	zfcp_cfdc_req_to_sense(data, req);
+	retval = copy_to_user(data_user, data, sizeof(*data_user));
+	if (retval) {
+		retval = -EFAULT;
+		goto free_fsf;
+	}
+
+	if (data->command & ZFCP_CFDC_UPLOAD)
+		retval = zfcp_cfdc_copy_to_user(&data_user->control_file,
+						fsf_cfdc->sg);
+
+ free_fsf:
+	zfcp_fsf_req_free(req);
+ free_sg:
+	zfcp_sg_free_table(fsf_cfdc->sg, ZFCP_CFDC_PAGES);
+ adapter_put:
+	zfcp_adapter_put(adapter);
+ free_buffer:
+	kfree(data);
+ no_mem_sense:
+	kfree(fsf_cfdc);
+	return retval;
+}
+
+static const struct file_operations zfcp_cfdc_fops = {
+	.unlocked_ioctl = zfcp_cfdc_dev_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl = zfcp_cfdc_dev_ioctl
+#endif
+};
+
+struct miscdevice zfcp_cfdc_misc = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = "zfcp_cfdc",
+	.fops = &zfcp_cfdc_fops,
+};
diff --git a/drivers/s390/scsi/zfcp_dbf.c b/drivers/s390/scsi/zfcp_dbf.c
index c8bad67..36169c6 100644
--- a/drivers/s390/scsi/zfcp_dbf.c
+++ b/drivers/s390/scsi/zfcp_dbf.c
@@ -1,22 +1,9 @@
 /*
- * This file is part of the zfcp device driver for
- * FCP adapters for IBM System z9 and zSeries.
+ * zfcp device driver
  *
- * (C) Copyright IBM Corp. 2002, 2006
+ * Debug traces for zfcp.
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * Copyright IBM Corporation 2002, 2008
  */
 
 #include <linux/ctype.h>
@@ -29,8 +16,6 @@
 MODULE_PARM_DESC(dbfsize,
 		 "number of pages for each debug feature area (default 4)");
 
-#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_OTHER
-
 static void zfcp_dbf_hexdump(debug_info_t *dbf, void *to, int to_len,
 			     int level, char *from, int from_len)
 {
@@ -186,8 +171,8 @@
 	       fsf_status_qual, FSF_STATUS_QUALIFIER_SIZE);
 	response->fsf_req_status = fsf_req->status;
 	response->sbal_first = fsf_req->sbal_first;
-	response->sbal_curr = fsf_req->sbal_curr;
 	response->sbal_last = fsf_req->sbal_last;
+	response->sbal_response = fsf_req->sbal_response;
 	response->pool = fsf_req->pool != NULL;
 	response->erp_action = (unsigned long)fsf_req->erp_action;
 
@@ -268,7 +253,7 @@
 	strncpy(rec->tag, "stat", ZFCP_DBF_TAG_SIZE);
 	strncpy(rec->tag2, tag, ZFCP_DBF_TAG_SIZE);
 
-	rec->u.status.failed = adapter->status_read_failed;
+	rec->u.status.failed = atomic_read(&adapter->stat_miss);
 	if (status_buffer != NULL) {
 		rec->u.status.status_type = status_buffer->status_type;
 		rec->u.status.status_subtype = status_buffer->status_subtype;
@@ -355,8 +340,8 @@
 		      FSF_STATUS_QUALIFIER_SIZE, 0, FSF_STATUS_QUALIFIER_SIZE);
 	zfcp_dbf_out(p, "fsf_req_status", "0x%08x", r->fsf_req_status);
 	zfcp_dbf_out(p, "sbal_first", "0x%02x", r->sbal_first);
-	zfcp_dbf_out(p, "sbal_curr", "0x%02x", r->sbal_curr);
 	zfcp_dbf_out(p, "sbal_last", "0x%02x", r->sbal_last);
+	zfcp_dbf_out(p, "sbal_response", "0x%02x", r->sbal_response);
 	zfcp_dbf_out(p, "pool", "0x%02x", r->pool);
 
 	switch (r->fsf_command) {
@@ -515,13 +500,13 @@
 	[52]	= "port boxed close unit",
 	[53]	= "port boxed fcp",
 	[54]	= "unit boxed fcp",
-	[55]	= "port access denied ct",
-	[56]	= "port access denied els",
-	[57]	= "port access denied open port",
-	[58]	= "port access denied close physical",
-	[59]	= "unit access denied open unit",
+	[55]	= "port access denied",
+	[56]	= "",
+	[57]	= "",
+	[58]	= "",
+	[59]	= "unit access denied",
 	[60]	= "shared unit access denied open unit",
-	[61]	= "unit access denied fcp",
+	[61]	= "",
 	[62]	= "request timeout",
 	[63]	= "adisc link test reject or timeout",
 	[64]	= "adisc link test d_id changed",
@@ -546,8 +531,8 @@
 	[80]	= "exclusive read-only unit access unsupported",
 	[81]	= "shared read-write unit access unsupported",
 	[82]	= "incoming rscn",
-	[83]	= "incoming plogi",
-	[84]	= "incoming logo",
+	[83]	= "incoming wwpn",
+	[84]	= "",
 	[85]	= "online",
 	[86]	= "offline",
 	[87]	= "ccw device gone",
@@ -586,8 +571,8 @@
 	[120]	= "unknown fsf command",
 	[121]	= "no recommendation for status qualifier",
 	[122]	= "status read physical port closed in error",
-	[123]	= "fc service class not supported ct",
-	[124]	= "fc service class not supported els",
+	[123]	= "fc service class not supported",
+	[124]	= "",
 	[125]	= "need newer zfcp",
 	[126]	= "need newer microcode",
 	[127]	= "arbitrated loop not supported",
@@ -595,7 +580,7 @@
 	[129]	= "qtcb size mismatch",
 	[130]	= "unknown fsf status ecd",
 	[131]	= "fcp request too big",
-	[132]	= "fc service class not supported fcp",
+	[132]	= "",
 	[133]	= "data direction not valid fcp",
 	[134]	= "command length not valid fcp",
 	[135]	= "status read act update",
@@ -603,13 +588,18 @@
 	[137]	= "hbaapi port open",
 	[138]	= "hbaapi unit open",
 	[139]	= "hbaapi unit shutdown",
-	[140]	= "qdio error",
+	[140]	= "qdio error outbound",
 	[141]	= "scsi host reset",
 	[142]	= "dismissing fsf request for recovery action",
 	[143]	= "recovery action timed out",
 	[144]	= "recovery action gone",
 	[145]	= "recovery action being processed",
 	[146]	= "recovery action ready for next step",
+	[147]	= "qdio error inbound",
+	[148]   = "nameserver needed for port scan",
+	[149]   = "port scan",
+	[150]	= "ptp attach",
+	[151]   = "port validation failed",
 };
 
 static int zfcp_rec_dbf_view_format(debug_info_t *id, struct debug_view *view,
@@ -670,24 +660,20 @@
  * zfcp_rec_dbf_event_thread - trace event related to recovery thread operation
  * @id2: identifier for event
  * @adapter: adapter
- * @lock: non-zero value indicates that erp_lock has not yet been acquired
+ * This function assumes that the caller is holding erp_lock.
  */
-void zfcp_rec_dbf_event_thread(u8 id2, struct zfcp_adapter *adapter, int lock)
+void zfcp_rec_dbf_event_thread(u8 id2, struct zfcp_adapter *adapter)
 {
 	struct zfcp_rec_dbf_record *r = &adapter->rec_dbf_buf;
 	unsigned long flags = 0;
 	struct list_head *entry;
 	unsigned ready = 0, running = 0, total;
 
-	if (lock)
-		read_lock_irqsave(&adapter->erp_lock, flags);
 	list_for_each(entry, &adapter->erp_ready_head)
 		ready++;
 	list_for_each(entry, &adapter->erp_running_head)
 		running++;
 	total = adapter->erp_total_count;
-	if (lock)
-		read_unlock_irqrestore(&adapter->erp_lock, flags);
 
 	spin_lock_irqsave(&adapter->rec_dbf_lock, flags);
 	memset(r, 0, sizeof(*r));
@@ -696,10 +682,25 @@
 	r->u.thread.total = total;
 	r->u.thread.ready = ready;
 	r->u.thread.running = running;
-	debug_event(adapter->rec_dbf, 5, r, sizeof(*r));
+	debug_event(adapter->rec_dbf, 6, r, sizeof(*r));
 	spin_unlock_irqrestore(&adapter->rec_dbf_lock, flags);
 }
 
+/**
+ * zfcp_rec_dbf_event_thread - trace event related to recovery thread operation
+ * @id2: identifier for event
+ * @adapter: adapter
+ * This function assumes that the caller does not hold erp_lock.
+ */
+void zfcp_rec_dbf_event_thread_lock(u8 id2, struct zfcp_adapter *adapter)
+{
+	unsigned long flags;
+
+	read_lock_irqsave(&adapter->erp_lock, flags);
+	zfcp_rec_dbf_event_thread(id2, adapter);
+	read_unlock_irqrestore(&adapter->erp_lock, flags);
+}
+
 static void zfcp_rec_dbf_event_target(u8 id2, void *ref,
 				      struct zfcp_adapter *adapter,
 				      atomic_t *status, atomic_t *erp_count,
@@ -823,7 +824,7 @@
 	r->u.action.status = erp_action->status;
 	r->u.action.step = erp_action->step;
 	r->u.action.fsf_req = (unsigned long)erp_action->fsf_req;
-	debug_event(adapter->rec_dbf, 4, r, sizeof(*r));
+	debug_event(adapter->rec_dbf, 5, r, sizeof(*r));
 	spin_unlock_irqrestore(&adapter->rec_dbf_lock, flags);
 }
 
@@ -960,7 +961,7 @@
 
 	zfcp_san_dbf_event_els("iels", 1, fsf_req, buf->d_id,
 			       fc_host_port_id(adapter->scsi_host),
-			       *(u8 *)buf->payload, (void *)buf->payload,
+			       buf->payload.data[0], (void *)buf->payload.data,
 			       length);
 }
 
@@ -1064,8 +1065,7 @@
 			if (fsf_req != NULL) {
 				fcp_rsp = (struct fcp_rsp_iu *)
 				    &(fsf_req->qtcb->bottom.io.fcp_rsp);
-				fcp_rsp_info =
-				    zfcp_get_fcp_rsp_info_ptr(fcp_rsp);
+				fcp_rsp_info = (unsigned char *) &fcp_rsp[1];
 				fcp_sns_info =
 				    zfcp_get_fcp_sns_info_ptr(fcp_rsp);
 
@@ -1279,5 +1279,3 @@
 	adapter->hba_dbf = NULL;
 	adapter->rec_dbf = NULL;
 }
-
-#undef ZFCP_LOG_AREA
diff --git a/drivers/s390/scsi/zfcp_dbf.h b/drivers/s390/scsi/zfcp_dbf.h
index 54c34e4..d04aea6 100644
--- a/drivers/s390/scsi/zfcp_dbf.h
+++ b/drivers/s390/scsi/zfcp_dbf.h
@@ -38,7 +38,7 @@
 	u32 total;
 	u32 ready;
 	u32 running;
-} __attribute__ ((packed));
+};
 
 struct zfcp_rec_dbf_record_target {
 	u64 ref;
@@ -47,7 +47,7 @@
 	u64 wwpn;
 	u64 fcp_lun;
 	u32 erp_count;
-} __attribute__ ((packed));
+};
 
 struct zfcp_rec_dbf_record_trigger {
 	u8 want;
@@ -59,14 +59,14 @@
 	u64 action;
 	u64 wwpn;
 	u64 fcp_lun;
-} __attribute__ ((packed));
+};
 
 struct zfcp_rec_dbf_record_action {
 	u32 status;
 	u32 step;
 	u64 action;
 	u64 fsf_req;
-} __attribute__ ((packed));
+};
 
 struct zfcp_rec_dbf_record {
 	u8 id;
@@ -77,7 +77,7 @@
 		struct zfcp_rec_dbf_record_target target;
 		struct zfcp_rec_dbf_record_trigger trigger;
 	} u;
-} __attribute__ ((packed));
+};
 
 enum {
 	ZFCP_REC_DBF_ID_ACTION,
@@ -97,8 +97,8 @@
 	u8 fsf_status_qual[FSF_STATUS_QUALIFIER_SIZE];
 	u32 fsf_req_status;
 	u8 sbal_first;
-	u8 sbal_curr;
 	u8 sbal_last;
+	u8 sbal_response;
 	u8 pool;
 	u64 erp_action;
 	union {
diff --git a/drivers/s390/scsi/zfcp_def.h b/drivers/s390/scsi/zfcp_def.h
index bda8c77..67f45fc 100644
--- a/drivers/s390/scsi/zfcp_def.h
+++ b/drivers/s390/scsi/zfcp_def.h
@@ -1,22 +1,9 @@
 /*
- * This file is part of the zfcp device driver for
- * FCP adapters for IBM System z9 and zSeries.
+ * zfcp device driver
  *
- * (C) Copyright IBM Corp. 2002, 2006
+ * Global definitions for the zfcp device driver.
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * Copyright IBM Corporation 2002, 2008
  */
 
 #ifndef ZFCP_DEF_H
@@ -26,7 +13,6 @@
 
 #include <linux/init.h>
 #include <linux/moduleparam.h>
-#include <linux/miscdevice.h>
 #include <linux/major.h>
 #include <linux/blkdev.h>
 #include <linux/delay.h>
@@ -53,9 +39,6 @@
 
 /********************* GENERAL DEFINES *********************************/
 
-/* zfcp version number, it consists of major, minor, and patch-level number */
-#define ZFCP_VERSION		"4.8.0"
-
 /**
  * zfcp_sg_to_address - determine kernel address from struct scatterlist
  * @list: struct scatterlist
@@ -93,11 +76,6 @@
 #define ZFCP_DEVICE_MODEL       0x03
 #define ZFCP_DEVICE_MODEL_PRIV	0x04
 
-/* allow as many chained SBALs as are supported by hardware */
-#define ZFCP_MAX_SBALS_PER_REQ		FSF_MAX_SBALS_PER_REQ
-#define ZFCP_MAX_SBALS_PER_CT_REQ	FSF_MAX_SBALS_PER_REQ
-#define ZFCP_MAX_SBALS_PER_ELS_REQ	FSF_MAX_SBALS_PER_ELS_REQ
-
 /* DMQ bug workaround: don't use last SBALE */
 #define ZFCP_MAX_SBALES_PER_SBAL	(QDIO_MAX_ELEMENTS_PER_BUFFER - 1)
 
@@ -106,42 +84,17 @@
 
 /* max. number of (data buffer) SBALEs in largest SBAL chain */
 #define ZFCP_MAX_SBALES_PER_REQ		\
-	(ZFCP_MAX_SBALS_PER_REQ * ZFCP_MAX_SBALES_PER_SBAL - 2)
+	(FSF_MAX_SBALS_PER_REQ * ZFCP_MAX_SBALES_PER_SBAL - 2)
         /* request ID + QTCB in SBALE 0 + 1 of first SBAL in chain */
 
 #define ZFCP_MAX_SECTORS (ZFCP_MAX_SBALES_PER_REQ * 8)
         /* max. number of (data buffer) SBALEs in largest SBAL chain
            multiplied with number of sectors per 4k block */
 
-/* FIXME(tune): free space should be one max. SBAL chain plus what? */
-#define ZFCP_QDIO_PCI_INTERVAL		(QDIO_MAX_BUFFERS_PER_Q \
-                                         - (ZFCP_MAX_SBALS_PER_REQ + 4))
-
-#define ZFCP_SBAL_TIMEOUT               (5*HZ)
-
-#define ZFCP_TYPE2_RECOVERY_TIME        8	/* seconds */
-
-/* queue polling (values in microseconds) */
-#define ZFCP_MAX_INPUT_THRESHOLD 	5000	/* FIXME: tune */
-#define ZFCP_MAX_OUTPUT_THRESHOLD 	1000	/* FIXME: tune */
-#define ZFCP_MIN_INPUT_THRESHOLD 	1	/* ignored by QDIO layer */
-#define ZFCP_MIN_OUTPUT_THRESHOLD 	1	/* ignored by QDIO layer */
-
-#define QDIO_SCSI_QFMT			1	/* 1 for FSF */
-#define QBUFF_PER_PAGE			(PAGE_SIZE / sizeof(struct qdio_buffer))
-
 /********************* FSF SPECIFIC DEFINES *********************************/
 
-#define ZFCP_ULP_INFO_VERSION                   26
-#define ZFCP_QTCB_VERSION	FSF_QTCB_CURRENT_VERSION
 /* ATTENTION: value must not be used by hardware */
 #define FSF_QTCB_UNSOLICITED_STATUS		0x6305
-#define ZFCP_STATUS_READ_FAILED_THRESHOLD	3
-#define ZFCP_STATUS_READS_RECOM		        FSF_STATUS_READS_RECOM
-
-/* Do 1st retry in 1 second, then double the timeout for each following retry */
-#define ZFCP_EXCHANGE_CONFIG_DATA_FIRST_SLEEP	1
-#define ZFCP_EXCHANGE_CONFIG_DATA_RETRIES	7
 
 /* timeout value for "default timer" for fsf requests */
 #define ZFCP_FSF_REQUEST_TIMEOUT (60*HZ)
@@ -153,17 +106,9 @@
 /* data length field may be at variable position in FCP-2 FCP_CMND IU */
 typedef unsigned int       fcp_dl_t;
 
-#define ZFCP_FC_SERVICE_CLASS_DEFAULT	FSF_CLASS_3
-
 /* timeout for name-server lookup (in seconds) */
 #define ZFCP_NS_GID_PN_TIMEOUT		10
 
-/* largest SCSI command we can process */
-/* FCP-2 (FCP_CMND IU) allows up to (255-3+16) */
-#define ZFCP_MAX_SCSI_CMND_LENGTH	255
-/* maximum number of commands in LUN queue (tagged queueing) */
-#define ZFCP_CMND_PER_LUN               32
-
 /* task attribute values in FCP-2 FCP_CMND IU */
 #define SIMPLE_Q	0
 #define HEAD_OF_Q	1
@@ -224,9 +169,9 @@
 #define RSP_CODE_TASKMAN_FAILED	 5
 
 /* see fc-fs */
-#define LS_RSCN  0x61040000
-#define LS_LOGO  0x05000000
-#define LS_PLOGI 0x03000000
+#define LS_RSCN  0x61
+#define LS_LOGO  0x05
+#define LS_PLOGI 0x03
 
 struct fcp_rscn_head {
         u8  command;
@@ -266,7 +211,6 @@
  * FC-FS stuff
  */
 #define R_A_TOV				10 /* seconds */
-#define ZFCP_ELS_TIMEOUT		(2 * R_A_TOV)
 
 #define ZFCP_LS_RLS			0x0f
 #define ZFCP_LS_ADISC			0x52
@@ -311,7 +255,10 @@
 #define ZFCP_CT_DIRECTORY_SERVICE	0xFC
 #define ZFCP_CT_NAME_SERVER		0x02
 #define ZFCP_CT_SYNCHRONOUS		0x00
+#define ZFCP_CT_SCSI_FCP		0x08
+#define ZFCP_CT_UNABLE_TO_PERFORM_CMD	0x09
 #define ZFCP_CT_GID_PN			0x0121
+#define ZFCP_CT_GPN_FT			0x0172
 #define ZFCP_CT_MAX_SIZE		0x1020
 #define ZFCP_CT_ACCEPT			0x8002
 #define ZFCP_CT_REJECT			0x8001
@@ -321,107 +268,6 @@
  */
 #define ZFCP_CT_TIMEOUT			(3 * R_A_TOV)
 
-/******************** LOGGING MACROS AND DEFINES *****************************/
-
-/*
- * Logging may be applied on certain kinds of driver operations
- * independently. Additionally, different log-levels are supported for
- * each of these areas.
- */
-
-#define ZFCP_NAME               "zfcp"
-
-/* independent log areas */
-#define ZFCP_LOG_AREA_OTHER	0
-#define ZFCP_LOG_AREA_SCSI	1
-#define ZFCP_LOG_AREA_FSF	2
-#define ZFCP_LOG_AREA_CONFIG	3
-#define ZFCP_LOG_AREA_CIO	4
-#define ZFCP_LOG_AREA_QDIO	5
-#define ZFCP_LOG_AREA_ERP	6
-#define ZFCP_LOG_AREA_FC	7
-
-/* log level values*/
-#define ZFCP_LOG_LEVEL_NORMAL	0
-#define ZFCP_LOG_LEVEL_INFO	1
-#define ZFCP_LOG_LEVEL_DEBUG	2
-#define ZFCP_LOG_LEVEL_TRACE	3
-
-/*
- * this allows removal of logging code by the preprocessor
- * (the most detailed log level still to be compiled in is specified,
- * higher log levels are removed)
- */
-#define ZFCP_LOG_LEVEL_LIMIT	ZFCP_LOG_LEVEL_TRACE
-
-/* get "loglevel" nibble assignment */
-#define ZFCP_GET_LOG_VALUE(zfcp_lognibble) \
-	       ((atomic_read(&zfcp_data.loglevel) >> (zfcp_lognibble<<2)) & 0xF)
-
-/* set "loglevel" nibble */
-#define ZFCP_SET_LOG_NIBBLE(value, zfcp_lognibble) \
-	       (value << (zfcp_lognibble << 2))
-
-/* all log-level defaults are combined to generate initial log-level */
-#define ZFCP_LOG_LEVEL_DEFAULTS \
-	(ZFCP_SET_LOG_NIBBLE(ZFCP_LOG_LEVEL_NORMAL, ZFCP_LOG_AREA_OTHER) | \
-	 ZFCP_SET_LOG_NIBBLE(ZFCP_LOG_LEVEL_NORMAL, ZFCP_LOG_AREA_SCSI) | \
-	 ZFCP_SET_LOG_NIBBLE(ZFCP_LOG_LEVEL_NORMAL, ZFCP_LOG_AREA_FSF) | \
-	 ZFCP_SET_LOG_NIBBLE(ZFCP_LOG_LEVEL_NORMAL, ZFCP_LOG_AREA_CONFIG) | \
-	 ZFCP_SET_LOG_NIBBLE(ZFCP_LOG_LEVEL_NORMAL, ZFCP_LOG_AREA_CIO) | \
-	 ZFCP_SET_LOG_NIBBLE(ZFCP_LOG_LEVEL_NORMAL, ZFCP_LOG_AREA_QDIO) | \
-	 ZFCP_SET_LOG_NIBBLE(ZFCP_LOG_LEVEL_NORMAL, ZFCP_LOG_AREA_ERP) | \
-	 ZFCP_SET_LOG_NIBBLE(ZFCP_LOG_LEVEL_NORMAL, ZFCP_LOG_AREA_FC))
-
-/* check whether we have the right level for logging */
-#define ZFCP_LOG_CHECK(level) \
-	((ZFCP_GET_LOG_VALUE(ZFCP_LOG_AREA)) >= level)
-
-/* logging routine for zfcp */
-#define _ZFCP_LOG(fmt, args...) \
-	printk(KERN_ERR ZFCP_NAME": %s(%d): " fmt, __func__, \
-	       __LINE__ , ##args)
-
-#define ZFCP_LOG(level, fmt, args...) \
-do { \
-	if (ZFCP_LOG_CHECK(level)) \
-		_ZFCP_LOG(fmt, ##args); \
-} while (0)
-
-#if ZFCP_LOG_LEVEL_LIMIT < ZFCP_LOG_LEVEL_NORMAL
-# define ZFCP_LOG_NORMAL(fmt, args...)	do { } while (0)
-#else
-# define ZFCP_LOG_NORMAL(fmt, args...) \
-do { \
-	if (ZFCP_LOG_CHECK(ZFCP_LOG_LEVEL_NORMAL)) \
-		printk(KERN_ERR ZFCP_NAME": " fmt, ##args); \
-} while (0)
-#endif
-
-#if ZFCP_LOG_LEVEL_LIMIT < ZFCP_LOG_LEVEL_INFO
-# define ZFCP_LOG_INFO(fmt, args...)	do { } while (0)
-#else
-# define ZFCP_LOG_INFO(fmt, args...) \
-do { \
-	if (ZFCP_LOG_CHECK(ZFCP_LOG_LEVEL_INFO)) \
-		printk(KERN_ERR ZFCP_NAME": " fmt, ##args); \
-} while (0)
-#endif
-
-#if ZFCP_LOG_LEVEL_LIMIT < ZFCP_LOG_LEVEL_DEBUG
-# define ZFCP_LOG_DEBUG(fmt, args...)	do { } while (0)
-#else
-# define ZFCP_LOG_DEBUG(fmt, args...) \
-	ZFCP_LOG(ZFCP_LOG_LEVEL_DEBUG, fmt , ##args)
-#endif
-
-#if ZFCP_LOG_LEVEL_LIMIT < ZFCP_LOG_LEVEL_TRACE
-# define ZFCP_LOG_TRACE(fmt, args...)	do { } while (0)
-#else
-# define ZFCP_LOG_TRACE(fmt, args...) \
-	ZFCP_LOG(ZFCP_LOG_LEVEL_TRACE, fmt , ##args)
-#endif
-
 /*************** ADAPTER/PORT/UNIT AND FSF_REQ STATUS FLAGS ******************/
 
 /*
@@ -441,6 +287,7 @@
 #define ZFCP_STATUS_COMMON_ERP_INUSE		0x01000000
 #define ZFCP_STATUS_COMMON_ACCESS_DENIED	0x00800000
 #define ZFCP_STATUS_COMMON_ACCESS_BOXED		0x00400000
+#define ZFCP_STATUS_COMMON_NOESC		0x00200000
 
 /* adapter status */
 #define ZFCP_STATUS_ADAPTER_QDIOUP		0x00000002
@@ -496,77 +343,6 @@
 #define ZFCP_STATUS_FSFREQ_RETRY                0x00000800
 #define ZFCP_STATUS_FSFREQ_DISMISSED            0x00001000
 
-/*********************** ERROR RECOVERY PROCEDURE DEFINES ********************/
-
-#define ZFCP_MAX_ERPS                   3
-
-#define ZFCP_ERP_FSFREQ_TIMEOUT		(30 * HZ)
-#define ZFCP_ERP_MEMWAIT_TIMEOUT	HZ
-
-#define ZFCP_STATUS_ERP_TIMEDOUT	0x10000000
-#define ZFCP_STATUS_ERP_CLOSE_ONLY	0x01000000
-#define ZFCP_STATUS_ERP_DISMISSING	0x00100000
-#define ZFCP_STATUS_ERP_DISMISSED	0x00200000
-#define ZFCP_STATUS_ERP_LOWMEM		0x00400000
-
-#define ZFCP_ERP_STEP_UNINITIALIZED	0x00000000
-#define ZFCP_ERP_STEP_FSF_XCONFIG	0x00000001
-#define ZFCP_ERP_STEP_PHYS_PORT_CLOSING	0x00000010
-#define ZFCP_ERP_STEP_PORT_CLOSING	0x00000100
-#define ZFCP_ERP_STEP_NAMESERVER_OPEN	0x00000200
-#define ZFCP_ERP_STEP_NAMESERVER_LOOKUP	0x00000400
-#define ZFCP_ERP_STEP_PORT_OPENING	0x00000800
-#define ZFCP_ERP_STEP_UNIT_CLOSING	0x00001000
-#define ZFCP_ERP_STEP_UNIT_OPENING	0x00002000
-
-/* Ordered by escalation level (necessary for proper erp-code operation) */
-#define ZFCP_ERP_ACTION_REOPEN_ADAPTER		0x4
-#define ZFCP_ERP_ACTION_REOPEN_PORT_FORCED	0x3
-#define ZFCP_ERP_ACTION_REOPEN_PORT		0x2
-#define ZFCP_ERP_ACTION_REOPEN_UNIT		0x1
-
-#define ZFCP_ERP_ACTION_RUNNING			0x1
-#define ZFCP_ERP_ACTION_READY			0x2
-
-#define ZFCP_ERP_SUCCEEDED	0x0
-#define ZFCP_ERP_FAILED		0x1
-#define ZFCP_ERP_CONTINUES	0x2
-#define ZFCP_ERP_EXIT		0x3
-#define ZFCP_ERP_DISMISSED	0x4
-#define ZFCP_ERP_NOMEM		0x5
-
-
-/******************** CFDC SPECIFIC STUFF *****************************/
-
-/* Firewall data channel sense data record */
-struct zfcp_cfdc_sense_data {
-	u32 signature;           /* Request signature */
-	u32 devno;               /* FCP adapter device number */
-	u32 command;             /* Command code */
-	u32 fsf_status;          /* FSF request status and status qualifier */
-	u8  fsf_status_qual[FSF_STATUS_QUALIFIER_SIZE];
-	u8  payloads[256];       /* Access conflicts list */
-	u8  control_file[0];     /* Access control table */
-};
-
-#define ZFCP_CFDC_SIGNATURE			0xCFDCACDF
-
-#define ZFCP_CFDC_CMND_DOWNLOAD_NORMAL		0x00010001
-#define ZFCP_CFDC_CMND_DOWNLOAD_FORCE		0x00010101
-#define ZFCP_CFDC_CMND_FULL_ACCESS		0x00000201
-#define ZFCP_CFDC_CMND_RESTRICTED_ACCESS	0x00000401
-#define ZFCP_CFDC_CMND_UPLOAD			0x00010002
-
-#define ZFCP_CFDC_DOWNLOAD			0x00000001
-#define ZFCP_CFDC_UPLOAD			0x00000002
-#define ZFCP_CFDC_WITH_CONTROL_FILE		0x00010000
-
-#define ZFCP_CFDC_DEV_NAME			"zfcp_cfdc"
-#define ZFCP_CFDC_DEV_MAJOR			MISC_MAJOR
-#define ZFCP_CFDC_DEV_MINOR			MISC_DYNAMIC_MINOR
-
-#define ZFCP_CFDC_MAX_CONTROL_FILE_SIZE		127 * 1024
-
 /************************* STRUCTURE DEFINITIONS *****************************/
 
 struct zfcp_fsf_req;
@@ -623,7 +399,6 @@
  * @resp_count: number of elements in response scatter-gather list
  * @handler: handler function (called for response to the request)
  * @handler_data: data passed to handler function
- * @pool: pointer to memory pool for ct request structure
  * @timeout: FSF timeout for this request
  * @completion: completion for synchronization purposes
  * @status: used to pass error status to calling function
@@ -636,7 +411,6 @@
 	unsigned int resp_count;
 	zfcp_send_ct_handler_t handler;
 	unsigned long handler_data;
-	mempool_t *pool;
 	int timeout;
 	struct completion *completion;
 	int status;
@@ -685,13 +459,13 @@
 };
 
 struct zfcp_qdio_queue {
-	struct qdio_buffer *buffer[QDIO_MAX_BUFFERS_PER_Q]; /* SBALs */
-	u8		   free_index;	      /* index of next free bfr
+	struct qdio_buffer *sbal[QDIO_MAX_BUFFERS_PER_Q]; /* SBALs */
+	u8		   first;	      /* index of next free bfr
 						 in queue (free_count>0) */
-	atomic_t           free_count;	      /* number of free buffers
+	atomic_t           count;	      /* number of free buffers
 						 in queue */
-	rwlock_t	   queue_lock;	      /* lock for operations on queue */
-        int                distance_from_int; /* SBALs used since PCI indication
+	spinlock_t	   lock;	      /* lock for operations on queue */
+	int                pci_batch;	      /* SBALs since PCI indication
 						 was last set */
 };
 
@@ -708,6 +482,24 @@
 	struct timer_list timer;
 };
 
+struct fsf_latency_record {
+	u32 min;
+	u32 max;
+	u64 sum;
+};
+
+struct latency_cont {
+	struct fsf_latency_record channel;
+	struct fsf_latency_record fabric;
+	u64 counter;
+};
+
+struct zfcp_latencies {
+	struct latency_cont read;
+	struct latency_cont write;
+	struct latency_cont cmd;
+	spinlock_t lock;
+};
 
 struct zfcp_adapter {
 	struct list_head	list;              /* list of adapters */
@@ -723,24 +515,25 @@
 	u32			adapter_features;  /* FCP channel features */
 	u32			connection_features; /* host connection features */
         u32			hardware_version;  /* of FCP channel */
+	u16			timer_ticks;       /* time int for a tick */
 	struct Scsi_Host	*scsi_host;	   /* Pointer to mid-layer */
 	struct list_head	port_list_head;	   /* remote port list */
 	struct list_head        port_remove_lh;    /* head of ports to be
 						      removed */
 	u32			ports;	           /* number of remote ports */
-	atomic_t		reqs_active;	   /* # active FSF reqs */
 	unsigned long		req_no;		   /* unique FSF req number */
 	struct list_head	*req_list;	   /* list of pending reqs */
 	spinlock_t		req_list_lock;	   /* request list lock */
-	struct zfcp_qdio_queue	request_queue;	   /* request queue */
+	struct zfcp_qdio_queue	req_q;		   /* request queue */
 	u32			fsf_req_seq_no;	   /* FSF cmnd seq number */
 	wait_queue_head_t	request_wq;	   /* can be used to wait for
 						      more avaliable SBALs */
-	struct zfcp_qdio_queue	response_queue;	   /* response queue */
+	struct zfcp_qdio_queue	resp_q;	   /* response queue */
 	rwlock_t		abort_lock;        /* Protects against SCSI
 						      stack abort/command
 						      completion races */
-	u16			status_read_failed; /* # failed status reads */
+	atomic_t		stat_miss;	   /* # missing status reads*/
+	struct work_struct	stat_work;
 	atomic_t		status;	           /* status of this adapter */
 	struct list_head	erp_ready_head;	   /* error recovery for this
 						      adapter/devices */
@@ -774,13 +567,9 @@
 	struct fc_host_statistics *fc_stats;
 	struct fsf_qtcb_bottom_port *stats_reset_data;
 	unsigned long		stats_reset;
+	struct work_struct	scan_work;
 };
 
-/*
- * the struct device sysfs_device must be at the beginning of this structure.
- * pointer to struct device is used to free port structure in release function
- * of the device. don't change!
- */
 struct zfcp_port {
 	struct device          sysfs_device;   /* sysfs device */
 	struct fc_rport        *rport;         /* rport of fc transport class */
@@ -804,10 +593,6 @@
 	u32                    supported_classes;
 };
 
-/* the struct device sysfs_device must be at the beginning of this structure.
- * pointer to struct device is used to free unit structure in release function
- * of the device. don't change!
- */
 struct zfcp_unit {
 	struct device          sysfs_device;   /* sysfs device */
 	struct list_head       list;	       /* list of logical units */
@@ -822,6 +607,7 @@
         struct scsi_device     *device;        /* scsi device struct pointer */
 	struct zfcp_erp_action erp_action;     /* pending error recovery */
         atomic_t               erp_counter;
+	struct zfcp_latencies	latencies;
 };
 
 /* FSF request */
@@ -831,19 +617,19 @@
 	struct zfcp_adapter    *adapter;       /* adapter request belongs to */
 	u8		       sbal_number;    /* nr of SBALs free for use */
 	u8		       sbal_first;     /* first SBAL for this request */
-	u8		       sbal_last;      /* last possible SBAL for
+	u8		       sbal_last;      /* last SBAL for this request */
+	u8		       sbal_limit;      /* last possible SBAL for
 						  this reuest */
-	u8		       sbal_curr;      /* current SBAL during creation
-						  of request */
 	u8		       sbale_curr;     /* current SBALE during creation
 						  of request */
+	u8			sbal_response;	/* SBAL used in interrupt */
 	wait_queue_head_t      completion_wq;  /* can be used by a routine
 						  to wait for completion */
 	volatile u32	       status;	       /* status of this request */
 	u32		       fsf_command;    /* FSF Command copy */
 	struct fsf_qtcb	       *qtcb;	       /* address of associated QTCB */
 	u32		       seq_no;         /* Sequence number of request */
-	unsigned long	       data;           /* private data of request */
+	void			*data;           /* private data of request */
 	struct timer_list     timer;	       /* used for erp or scsi er */
 	struct zfcp_erp_action *erp_action;    /* used if this request is
 						  issued on behalf of erp */
@@ -851,10 +637,9 @@
 						  from emergency pool */
 	unsigned long long     issued;         /* request sent time (STCK) */
 	struct zfcp_unit       *unit;
+	void			(*handler)(struct zfcp_fsf_req *);
 };
 
-typedef void zfcp_fsf_req_handler_t(struct zfcp_fsf_req*);
-
 /* driver data */
 struct zfcp_data {
 	struct scsi_host_template scsi_host_template;
@@ -873,29 +658,11 @@
 	char                    init_busid[BUS_ID_SIZE];
 	wwn_t                   init_wwpn;
 	fcp_lun_t               init_fcp_lun;
-	char 			*driver_version;
 	struct kmem_cache		*fsf_req_qtcb_cache;
 	struct kmem_cache		*sr_buffer_cache;
 	struct kmem_cache		*gid_pn_cache;
 };
 
-/**
- * struct zfcp_sg_list - struct describing a scatter-gather list
- * @sg: pointer to array of (struct scatterlist)
- * @count: number of elements in scatter-gather list
- */
-struct zfcp_sg_list {
-	struct scatterlist *sg;
-	unsigned int count;
-};
-
-/* number of elements for various memory pools */
-#define ZFCP_POOL_FSF_REQ_ERP_NR	1
-#define ZFCP_POOL_FSF_REQ_SCSI_NR	1
-#define ZFCP_POOL_FSF_REQ_ABORT_NR	1
-#define ZFCP_POOL_STATUS_READ_NR	ZFCP_STATUS_READS_RECOM
-#define ZFCP_POOL_DATA_GID_PN_NR	1
-
 /* struct used by memory pools for fsf_requests */
 struct zfcp_fsf_req_qtcb {
 	struct zfcp_fsf_req fsf_req;
@@ -905,7 +672,6 @@
 /********************** ZFCP SPECIFIC DEFINES ********************************/
 
 #define ZFCP_REQ_AUTO_CLEANUP	0x00000002
-#define ZFCP_WAIT_FOR_SBAL	0x00000004
 #define ZFCP_REQ_NO_QTCB	0x00000008
 
 #define ZFCP_SET                0x00000100
@@ -916,12 +682,6 @@
            ((atomic_read(target) & mask) == mask)
 #endif
 
-extern void _zfcp_hex_dump(char *, int);
-#define ZFCP_HEX_DUMP(level, addr, count) \
-		if (ZFCP_LOG_CHECK(level)) { \
-			_zfcp_hex_dump(addr, count); \
-		}
-
 #define zfcp_get_busid_by_adapter(adapter) (adapter->ccw_device->dev.bus_id)
 #define zfcp_get_busid_by_port(port) (zfcp_get_busid_by_adapter(port->adapter))
 #define zfcp_get_busid_by_unit(unit) (zfcp_get_busid_by_port(unit->port))
@@ -934,15 +694,6 @@
 	return req_id % REQUEST_LIST_SIZE;
 }
 
-static inline void zfcp_reqlist_add(struct zfcp_adapter *adapter,
-				    struct zfcp_fsf_req *fsf_req)
-{
-	unsigned int idx;
-
-	idx = zfcp_reqlist_hash(fsf_req->req_id);
-	list_add_tail(&fsf_req->list, &adapter->req_list[idx]);
-}
-
 static inline void zfcp_reqlist_remove(struct zfcp_adapter *adapter,
 				       struct zfcp_fsf_req *fsf_req)
 {
diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c
index 8054846..643ac4b 100644
--- a/drivers/s390/scsi/zfcp_erp.c
+++ b/drivers/s390/scsi/zfcp_erp.c
@@ -1,641 +1,406 @@
 /*
- * This file is part of the zfcp device driver for
- * FCP adapters for IBM System z9 and zSeries.
+ * zfcp device driver
  *
- * (C) Copyright IBM Corp. 2002, 2006
+ * Error Recovery Procedures (ERP).
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * Copyright IBM Corporation 2002, 2008
  */
 
-#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-
 #include "zfcp_ext.h"
 
-static int zfcp_erp_adisc(struct zfcp_port *);
-static void zfcp_erp_adisc_handler(unsigned long);
+#define ZFCP_MAX_ERPS                   3
 
-static int zfcp_erp_adapter_reopen_internal(struct zfcp_adapter *, int, u8,
-					    void *);
-static int zfcp_erp_port_forced_reopen_internal(struct zfcp_port *, int, u8,
-						void *);
-static int zfcp_erp_port_reopen_internal(struct zfcp_port *, int, u8, void *);
-static int zfcp_erp_unit_reopen_internal(struct zfcp_unit *, int, u8, void *);
+enum zfcp_erp_act_flags {
+	ZFCP_STATUS_ERP_TIMEDOUT	= 0x10000000,
+	ZFCP_STATUS_ERP_CLOSE_ONLY	= 0x01000000,
+	ZFCP_STATUS_ERP_DISMISSING	= 0x00100000,
+	ZFCP_STATUS_ERP_DISMISSED	= 0x00200000,
+	ZFCP_STATUS_ERP_LOWMEM		= 0x00400000,
+};
 
-static int zfcp_erp_port_reopen_all_internal(struct zfcp_adapter *, int, u8,
-					     void *);
-static int zfcp_erp_unit_reopen_all_internal(struct zfcp_port *, int, u8,
-					     void *);
+enum zfcp_erp_steps {
+	ZFCP_ERP_STEP_UNINITIALIZED	= 0x0000,
+	ZFCP_ERP_STEP_FSF_XCONFIG	= 0x0001,
+	ZFCP_ERP_STEP_PHYS_PORT_CLOSING	= 0x0010,
+	ZFCP_ERP_STEP_PORT_CLOSING	= 0x0100,
+	ZFCP_ERP_STEP_NAMESERVER_OPEN	= 0x0200,
+	ZFCP_ERP_STEP_NAMESERVER_LOOKUP	= 0x0400,
+	ZFCP_ERP_STEP_PORT_OPENING	= 0x0800,
+	ZFCP_ERP_STEP_UNIT_CLOSING	= 0x1000,
+	ZFCP_ERP_STEP_UNIT_OPENING	= 0x2000,
+};
 
-static void zfcp_erp_adapter_block(struct zfcp_adapter *, int);
-static void zfcp_erp_adapter_unblock(struct zfcp_adapter *);
-static void zfcp_erp_port_block(struct zfcp_port *, int);
-static void zfcp_erp_port_unblock(struct zfcp_port *);
-static void zfcp_erp_unit_block(struct zfcp_unit *, int);
-static void zfcp_erp_unit_unblock(struct zfcp_unit *);
+enum zfcp_erp_act_type {
+	ZFCP_ERP_ACTION_REOPEN_UNIT        = 1,
+	ZFCP_ERP_ACTION_REOPEN_PORT	   = 2,
+	ZFCP_ERP_ACTION_REOPEN_PORT_FORCED = 3,
+	ZFCP_ERP_ACTION_REOPEN_ADAPTER     = 4,
+};
 
-static int zfcp_erp_thread(void *);
+enum zfcp_erp_act_state {
+	ZFCP_ERP_ACTION_RUNNING = 1,
+	ZFCP_ERP_ACTION_READY   = 2,
+};
 
-static int zfcp_erp_strategy(struct zfcp_erp_action *);
+enum zfcp_erp_act_result {
+	ZFCP_ERP_SUCCEEDED = 0,
+	ZFCP_ERP_FAILED    = 1,
+	ZFCP_ERP_CONTINUES = 2,
+	ZFCP_ERP_EXIT      = 3,
+	ZFCP_ERP_DISMISSED = 4,
+	ZFCP_ERP_NOMEM     = 5,
+};
 
-static int zfcp_erp_strategy_do_action(struct zfcp_erp_action *);
-static int zfcp_erp_strategy_memwait(struct zfcp_erp_action *);
-static int zfcp_erp_strategy_check_target(struct zfcp_erp_action *, int);
-static int zfcp_erp_strategy_check_unit(struct zfcp_unit *, int);
-static int zfcp_erp_strategy_check_port(struct zfcp_port *, int);
-static int zfcp_erp_strategy_check_adapter(struct zfcp_adapter *, int);
-static int zfcp_erp_strategy_statechange(int, u32, struct zfcp_adapter *,
-					 struct zfcp_port *,
-					 struct zfcp_unit *, int);
-static int zfcp_erp_strategy_statechange_detected(atomic_t *, u32);
-static int zfcp_erp_strategy_followup_actions(int, struct zfcp_adapter *,
-					      struct zfcp_port *,
-					      struct zfcp_unit *, int);
-static int zfcp_erp_strategy_check_queues(struct zfcp_adapter *);
-static int zfcp_erp_strategy_check_action(struct zfcp_erp_action *, int);
-
-static int zfcp_erp_adapter_strategy(struct zfcp_erp_action *);
-static int zfcp_erp_adapter_strategy_generic(struct zfcp_erp_action *, int);
-static int zfcp_erp_adapter_strategy_close(struct zfcp_erp_action *);
-static int zfcp_erp_adapter_strategy_open(struct zfcp_erp_action *);
-static int zfcp_erp_adapter_strategy_open_qdio(struct zfcp_erp_action *);
-static int zfcp_erp_adapter_strategy_open_fsf(struct zfcp_erp_action *);
-static int zfcp_erp_adapter_strategy_open_fsf_xconfig(struct zfcp_erp_action *);
-static int zfcp_erp_adapter_strategy_open_fsf_xport(struct zfcp_erp_action *);
-static int zfcp_erp_adapter_strategy_open_fsf_statusread(
-	struct zfcp_erp_action *);
-
-static int zfcp_erp_port_forced_strategy(struct zfcp_erp_action *);
-static int zfcp_erp_port_forced_strategy_close(struct zfcp_erp_action *);
-
-static int zfcp_erp_port_strategy(struct zfcp_erp_action *);
-static int zfcp_erp_port_strategy_clearstati(struct zfcp_port *);
-static int zfcp_erp_port_strategy_close(struct zfcp_erp_action *);
-static int zfcp_erp_port_strategy_open(struct zfcp_erp_action *);
-static int zfcp_erp_port_strategy_open_nameserver(struct zfcp_erp_action *);
-static int zfcp_erp_port_strategy_open_nameserver_wakeup(
-	struct zfcp_erp_action *);
-static int zfcp_erp_port_strategy_open_common(struct zfcp_erp_action *);
-static int zfcp_erp_port_strategy_open_common_lookup(struct zfcp_erp_action *);
-static int zfcp_erp_port_strategy_open_port(struct zfcp_erp_action *);
-
-static int zfcp_erp_unit_strategy(struct zfcp_erp_action *);
-static int zfcp_erp_unit_strategy_clearstati(struct zfcp_unit *);
-static int zfcp_erp_unit_strategy_close(struct zfcp_erp_action *);
-static int zfcp_erp_unit_strategy_open(struct zfcp_erp_action *);
-
-static void zfcp_erp_action_dismiss_adapter(struct zfcp_adapter *);
-static void zfcp_erp_action_dismiss_port(struct zfcp_port *);
-static void zfcp_erp_action_dismiss_unit(struct zfcp_unit *);
-static void zfcp_erp_action_dismiss(struct zfcp_erp_action *);
-
-static int zfcp_erp_action_enqueue(int, struct zfcp_adapter *,
-				   struct zfcp_port *, struct zfcp_unit *,
-				   u8 id, void *ref);
-static int zfcp_erp_action_dequeue(struct zfcp_erp_action *);
-static void zfcp_erp_action_cleanup(int, struct zfcp_adapter *,
-				    struct zfcp_port *, struct zfcp_unit *,
-				    int);
-
-static void zfcp_erp_action_ready(struct zfcp_erp_action *);
-static int  zfcp_erp_action_exists(struct zfcp_erp_action *);
-
-static void zfcp_erp_action_to_ready(struct zfcp_erp_action *);
-static void zfcp_erp_action_to_running(struct zfcp_erp_action *);
-
-static void zfcp_erp_memwait_handler(unsigned long);
-
-/**
- * zfcp_close_qdio - close qdio queues for an adapter
- */
-static void zfcp_close_qdio(struct zfcp_adapter *adapter)
+static void zfcp_erp_adapter_block(struct zfcp_adapter *adapter, int mask)
 {
-	struct zfcp_qdio_queue *req_queue;
-	int first, count;
+	zfcp_erp_modify_adapter_status(adapter, 15, NULL,
+				       ZFCP_STATUS_COMMON_UNBLOCKED | mask,
+				       ZFCP_CLEAR);
+}
 
-	if (!atomic_test_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &adapter->status))
-		return;
+static int zfcp_erp_action_exists(struct zfcp_erp_action *act)
+{
+	struct zfcp_erp_action *curr_act;
 
-	/* clear QDIOUP flag, thus do_QDIO is not called during qdio_shutdown */
-	req_queue = &adapter->request_queue;
-	write_lock_irq(&req_queue->queue_lock);
-	atomic_clear_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &adapter->status);
-	write_unlock_irq(&req_queue->queue_lock);
+	list_for_each_entry(curr_act, &act->adapter->erp_running_head, list)
+		if (act == curr_act)
+			return ZFCP_ERP_ACTION_RUNNING;
+	return 0;
+}
 
-	while (qdio_shutdown(adapter->ccw_device,
-			     QDIO_FLAG_CLEANUP_USING_CLEAR) == -EINPROGRESS)
-		ssleep(1);
+static void zfcp_erp_action_ready(struct zfcp_erp_action *act)
+{
+	struct zfcp_adapter *adapter = act->adapter;
 
-	/* cleanup used outbound sbals */
-	count = atomic_read(&req_queue->free_count);
-	if (count < QDIO_MAX_BUFFERS_PER_Q) {
-		first = (req_queue->free_index+count) % QDIO_MAX_BUFFERS_PER_Q;
-		count = QDIO_MAX_BUFFERS_PER_Q - count;
-		zfcp_qdio_zero_sbals(req_queue->buffer, first, count);
+	list_move(&act->list, &act->adapter->erp_ready_head);
+	zfcp_rec_dbf_event_action(146, act);
+	up(&adapter->erp_ready_sem);
+	zfcp_rec_dbf_event_thread(2, adapter);
+}
+
+static void zfcp_erp_action_dismiss(struct zfcp_erp_action *act)
+{
+	act->status |= ZFCP_STATUS_ERP_DISMISSED;
+	if (zfcp_erp_action_exists(act) == ZFCP_ERP_ACTION_RUNNING)
+		zfcp_erp_action_ready(act);
+}
+
+static void zfcp_erp_action_dismiss_unit(struct zfcp_unit *unit)
+{
+	if (atomic_read(&unit->status) & ZFCP_STATUS_COMMON_ERP_INUSE)
+		zfcp_erp_action_dismiss(&unit->erp_action);
+}
+
+static void zfcp_erp_action_dismiss_port(struct zfcp_port *port)
+{
+	struct zfcp_unit *unit;
+
+	if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_ERP_INUSE)
+		zfcp_erp_action_dismiss(&port->erp_action);
+	else
+		list_for_each_entry(unit, &port->unit_list_head, list)
+		    zfcp_erp_action_dismiss_unit(unit);
+}
+
+static void zfcp_erp_action_dismiss_adapter(struct zfcp_adapter *adapter)
+{
+	struct zfcp_port *port;
+
+	if (atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_ERP_INUSE)
+		zfcp_erp_action_dismiss(&adapter->erp_action);
+	else
+		list_for_each_entry(port, &adapter->port_list_head, list)
+		    zfcp_erp_action_dismiss_port(port);
+}
+
+static int zfcp_erp_required_act(int want, struct zfcp_adapter *adapter,
+				 struct zfcp_port *port,
+				 struct zfcp_unit *unit)
+{
+	int need = want;
+	int u_status, p_status, a_status;
+
+	switch (want) {
+	case ZFCP_ERP_ACTION_REOPEN_UNIT:
+		u_status = atomic_read(&unit->status);
+		if (u_status & ZFCP_STATUS_COMMON_ERP_INUSE)
+			return 0;
+		p_status = atomic_read(&port->status);
+		if (!(p_status & ZFCP_STATUS_COMMON_RUNNING) ||
+		      p_status & ZFCP_STATUS_COMMON_ERP_FAILED)
+			return 0;
+		if (!(p_status & ZFCP_STATUS_COMMON_UNBLOCKED))
+			need = ZFCP_ERP_ACTION_REOPEN_PORT;
+		/* fall through */
+	case ZFCP_ERP_ACTION_REOPEN_PORT:
+	case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED:
+		p_status = atomic_read(&port->status);
+		if (p_status & ZFCP_STATUS_COMMON_ERP_INUSE)
+			return 0;
+		a_status = atomic_read(&adapter->status);
+		if (!(a_status & ZFCP_STATUS_COMMON_RUNNING) ||
+		      a_status & ZFCP_STATUS_COMMON_ERP_FAILED)
+			return 0;
+		if (!(a_status & ZFCP_STATUS_COMMON_UNBLOCKED))
+			need = ZFCP_ERP_ACTION_REOPEN_ADAPTER;
+		/* fall through */
+	case ZFCP_ERP_ACTION_REOPEN_ADAPTER:
+		a_status = atomic_read(&adapter->status);
+		if (a_status & ZFCP_STATUS_COMMON_ERP_INUSE)
+			return 0;
 	}
-	req_queue->free_index = 0;
-	atomic_set(&req_queue->free_count, 0);
-	req_queue->distance_from_int = 0;
-	adapter->response_queue.free_index = 0;
-	atomic_set(&adapter->response_queue.free_count, 0);
+
+	return need;
 }
 
-/**
- * zfcp_close_fsf - stop FSF operations for an adapter
- *
- * Dismiss and cleanup all pending fsf_reqs (this wakes up all initiators of
- * requests waiting for completion; especially this returns SCSI commands
- * with error state).
- */
-static void zfcp_close_fsf(struct zfcp_adapter *adapter)
+static struct zfcp_erp_action *zfcp_erp_setup_act(int need,
+						  struct zfcp_adapter *adapter,
+						  struct zfcp_port *port,
+						  struct zfcp_unit *unit)
 {
-	/* close queues to ensure that buffers are not accessed by adapter */
-	zfcp_close_qdio(adapter);
-	zfcp_fsf_req_dismiss_all(adapter);
-	/* reset FSF request sequence number */
-	adapter->fsf_req_seq_no = 0;
-	/* all ports and units are closed */
-	zfcp_erp_modify_adapter_status(adapter, 24, NULL,
-				       ZFCP_STATUS_COMMON_OPEN, ZFCP_CLEAR);
+	struct zfcp_erp_action *erp_action;
+	u32 status = 0;
+
+	switch (need) {
+	case ZFCP_ERP_ACTION_REOPEN_UNIT:
+		zfcp_unit_get(unit);
+		atomic_set_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &unit->status);
+		erp_action = &unit->erp_action;
+		if (!(atomic_read(&unit->status) & ZFCP_STATUS_COMMON_RUNNING))
+			status = ZFCP_STATUS_ERP_CLOSE_ONLY;
+		break;
+
+	case ZFCP_ERP_ACTION_REOPEN_PORT:
+	case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED:
+		zfcp_port_get(port);
+		zfcp_erp_action_dismiss_port(port);
+		atomic_set_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &port->status);
+		erp_action = &port->erp_action;
+		if (!(atomic_read(&port->status) & ZFCP_STATUS_COMMON_RUNNING))
+			status = ZFCP_STATUS_ERP_CLOSE_ONLY;
+		break;
+
+	case ZFCP_ERP_ACTION_REOPEN_ADAPTER:
+		zfcp_adapter_get(adapter);
+		zfcp_erp_action_dismiss_adapter(adapter);
+		atomic_set_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &adapter->status);
+		erp_action = &adapter->erp_action;
+		if (!(atomic_read(&adapter->status) &
+		      ZFCP_STATUS_COMMON_RUNNING))
+			status = ZFCP_STATUS_ERP_CLOSE_ONLY;
+		break;
+
+	default:
+		return NULL;
+	}
+
+	memset(erp_action, 0, sizeof(struct zfcp_erp_action));
+	erp_action->adapter = adapter;
+	erp_action->port = port;
+	erp_action->unit = unit;
+	erp_action->action = need;
+	erp_action->status = status;
+
+	return erp_action;
 }
 
-/**
- * zfcp_fsf_request_timeout_handler - called if a request timed out
- * @data: pointer to adapter for handler function
- *
- * This function needs to be called if requests (ELS, Generic Service,
- * or SCSI commands) exceed a certain time limit. The assumption is
- * that after the time limit the adapter get stuck. So we trigger a reopen of
- * the adapter.
- */
-static void zfcp_fsf_request_timeout_handler(unsigned long data)
+static int zfcp_erp_action_enqueue(int want, struct zfcp_adapter *adapter,
+				   struct zfcp_port *port,
+				   struct zfcp_unit *unit, u8 id, void *ref)
 {
-	struct zfcp_adapter *adapter = (struct zfcp_adapter *) data;
-	zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED, 62,
-				NULL);
+	int retval = 1, need;
+	struct zfcp_erp_action *act = NULL;
+
+	if (!(atomic_read(&adapter->status) &
+	      ZFCP_STATUS_ADAPTER_ERP_THREAD_UP))
+		return -EIO;
+
+	need = zfcp_erp_required_act(want, adapter, port, unit);
+	if (!need)
+		goto out;
+
+	atomic_set_mask(ZFCP_STATUS_ADAPTER_ERP_PENDING, &adapter->status);
+	act = zfcp_erp_setup_act(need, adapter, port, unit);
+	if (!act)
+		goto out;
+	++adapter->erp_total_count;
+	list_add_tail(&act->list, &adapter->erp_ready_head);
+	up(&adapter->erp_ready_sem);
+	zfcp_rec_dbf_event_thread(1, adapter);
+	retval = 0;
+ out:
+	zfcp_rec_dbf_event_trigger(id, ref, want, need, act,
+				   adapter, port, unit);
+	return retval;
 }
 
-void zfcp_fsf_start_timer(struct zfcp_fsf_req *fsf_req, unsigned long timeout)
+static int _zfcp_erp_adapter_reopen(struct zfcp_adapter *adapter,
+				    int clear_mask, u8 id, void *ref)
 {
-	fsf_req->timer.function = zfcp_fsf_request_timeout_handler;
-	fsf_req->timer.data = (unsigned long) fsf_req->adapter;
-	fsf_req->timer.expires = jiffies + timeout;
-	add_timer(&fsf_req->timer);
-}
-
-/*
- * function:
- *
- * purpose:	called if an adapter failed,
- *		initiates adapter recovery which is done
- *		asynchronously
- *
- * returns:	0	- initiated action successfully
- *		<0	- failed to initiate action
- */
-static int zfcp_erp_adapter_reopen_internal(struct zfcp_adapter *adapter,
-					    int clear_mask, u8 id, void *ref)
-{
-	int retval;
-
-	ZFCP_LOG_DEBUG("reopen adapter %s\n",
-		       zfcp_get_busid_by_adapter(adapter));
-
 	zfcp_erp_adapter_block(adapter, clear_mask);
 
-	if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &adapter->status)) {
-		ZFCP_LOG_DEBUG("skipped reopen of failed adapter %s\n",
-			       zfcp_get_busid_by_adapter(adapter));
-		/* ensure propagation of failed status to new devices */
+	/* ensure propagation of failed status to new devices */
+	if (atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_ERP_FAILED) {
 		zfcp_erp_adapter_failed(adapter, 13, NULL);
-		retval = -EIO;
-		goto out;
+		return -EIO;
 	}
-	retval = zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_ADAPTER,
-					 adapter, NULL, NULL, id, ref);
-
- out:
-	return retval;
+	return zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_ADAPTER,
+				       adapter, NULL, NULL, id, ref);
 }
 
-/*
- * function:
- *
- * purpose:	Wrappper for zfcp_erp_adapter_reopen_internal
- *              used to ensure the correct locking
- *
- * returns:	0	- initiated action successfully
- *		<0	- failed to initiate action
+/**
+ * zfcp_erp_adapter_reopen - Reopen adapter.
+ * @adapter: Adapter to reopen.
+ * @clear: Status flags to clear.
+ * @id: Id for debug trace event.
+ * @ref: Reference for debug trace event.
  */
-int zfcp_erp_adapter_reopen(struct zfcp_adapter *adapter, int clear_mask,
-			    u8 id, void *ref)
+void zfcp_erp_adapter_reopen(struct zfcp_adapter *adapter, int clear,
+			     u8 id, void *ref)
 {
-	int retval;
 	unsigned long flags;
 
 	read_lock_irqsave(&zfcp_data.config_lock, flags);
 	write_lock(&adapter->erp_lock);
-	retval = zfcp_erp_adapter_reopen_internal(adapter, clear_mask, id, ref);
+	_zfcp_erp_adapter_reopen(adapter, clear, id, ref);
 	write_unlock(&adapter->erp_lock);
 	read_unlock_irqrestore(&zfcp_data.config_lock, flags);
-
-	return retval;
 }
 
-int zfcp_erp_adapter_shutdown(struct zfcp_adapter *adapter, int clear_mask,
-			      u8 id, void *ref)
-{
-	int retval;
-
-	retval = zfcp_erp_adapter_reopen(adapter,
-					 ZFCP_STATUS_COMMON_RUNNING |
-					 ZFCP_STATUS_COMMON_ERP_FAILED |
-					 clear_mask, id, ref);
-
-	return retval;
-}
-
-int zfcp_erp_port_shutdown(struct zfcp_port *port, int clear_mask, u8 id,
-			   void *ref)
-{
-	int retval;
-
-	retval = zfcp_erp_port_reopen(port,
-				      ZFCP_STATUS_COMMON_RUNNING |
-				      ZFCP_STATUS_COMMON_ERP_FAILED |
-				      clear_mask, id, ref);
-
-	return retval;
-}
-
-int zfcp_erp_unit_shutdown(struct zfcp_unit *unit, int clear_mask, u8 id,
-			   void *ref)
-{
-	int retval;
-
-	retval = zfcp_erp_unit_reopen(unit,
-				      ZFCP_STATUS_COMMON_RUNNING |
-				      ZFCP_STATUS_COMMON_ERP_FAILED |
-				      clear_mask, id, ref);
-
-	return retval;
-}
-
-
 /**
- * zfcp_erp_adisc - send ADISC ELS command
- * @port: port structure
+ * zfcp_erp_adapter_shutdown - Shutdown adapter.
+ * @adapter: Adapter to shut down.
+ * @clear: Status flags to clear.
+ * @id: Id for debug trace event.
+ * @ref: Reference for debug trace event.
  */
-static int
-zfcp_erp_adisc(struct zfcp_port *port)
+void zfcp_erp_adapter_shutdown(struct zfcp_adapter *adapter, int clear,
+			       u8 id, void *ref)
 {
+	int flags = ZFCP_STATUS_COMMON_RUNNING | ZFCP_STATUS_COMMON_ERP_FAILED;
+	zfcp_erp_adapter_reopen(adapter, clear | flags, id, ref);
+}
+
+/**
+ * zfcp_erp_port_shutdown - Shutdown port
+ * @port: Port to shut down.
+ * @clear: Status flags to clear.
+ * @id: Id for debug trace event.
+ * @ref: Reference for debug trace event.
+ */
+void zfcp_erp_port_shutdown(struct zfcp_port *port, int clear, u8 id, void *ref)
+{
+	int flags = ZFCP_STATUS_COMMON_RUNNING | ZFCP_STATUS_COMMON_ERP_FAILED;
+	zfcp_erp_port_reopen(port, clear | flags, id, ref);
+}
+
+/**
+ * zfcp_erp_unit_shutdown - Shutdown unit
+ * @unit: Unit to shut down.
+ * @clear: Status flags to clear.
+ * @id: Id for debug trace event.
+ * @ref: Reference for debug trace event.
+ */
+void zfcp_erp_unit_shutdown(struct zfcp_unit *unit, int clear, u8 id, void *ref)
+{
+	int flags = ZFCP_STATUS_COMMON_RUNNING | ZFCP_STATUS_COMMON_ERP_FAILED;
+	zfcp_erp_unit_reopen(unit, clear | flags, id, ref);
+}
+
+static void zfcp_erp_port_block(struct zfcp_port *port, int clear)
+{
+	zfcp_erp_modify_port_status(port, 17, NULL,
+				    ZFCP_STATUS_COMMON_UNBLOCKED | clear,
+				    ZFCP_CLEAR);
+}
+
+static void _zfcp_erp_port_forced_reopen(struct zfcp_port *port,
+					 int clear, u8 id, void *ref)
+{
+	zfcp_erp_port_block(port, clear);
+
+	if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_ERP_FAILED)
+		return;
+
+	zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_PORT_FORCED,
+				port->adapter, port, NULL, id, ref);
+}
+
+/**
+ * zfcp_erp_port_forced_reopen - Forced close of port and open again
+ * @port: Port to force close and to reopen.
+ * @id: Id for debug trace event.
+ * @ref: Reference for debug trace event.
+ */
+void zfcp_erp_port_forced_reopen(struct zfcp_port *port, int clear, u8 id,
+				 void *ref)
+{
+	unsigned long flags;
 	struct zfcp_adapter *adapter = port->adapter;
-	struct zfcp_send_els *send_els;
-	struct zfcp_ls_adisc *adisc;
-	void *address = NULL;
-	int retval = 0;
 
-	send_els = kzalloc(sizeof(struct zfcp_send_els), GFP_ATOMIC);
-	if (send_els == NULL)
-		goto nomem;
-
-	send_els->req = kmalloc(sizeof(struct scatterlist), GFP_ATOMIC);
-	if (send_els->req == NULL)
-		goto nomem;
-	sg_init_table(send_els->req, 1);
-
-	send_els->resp = kmalloc(sizeof(struct scatterlist), GFP_ATOMIC);
-	if (send_els->resp == NULL)
-		goto nomem;
-	sg_init_table(send_els->resp, 1);
-
-	address = (void *) get_zeroed_page(GFP_ATOMIC);
-	if (address == NULL)
-		goto nomem;
-
-	zfcp_address_to_sg(address, send_els->req, sizeof(struct zfcp_ls_adisc));
-	address += PAGE_SIZE >> 1;
-	zfcp_address_to_sg(address, send_els->resp, sizeof(struct zfcp_ls_adisc_acc));
-	send_els->req_count = send_els->resp_count = 1;
-
-	send_els->adapter = adapter;
-	send_els->port = port;
-	send_els->d_id = port->d_id;
-	send_els->handler = zfcp_erp_adisc_handler;
-	send_els->handler_data = (unsigned long) send_els;
-
-	adisc = zfcp_sg_to_address(send_els->req);
-	send_els->ls_code = adisc->code = ZFCP_LS_ADISC;
-
-	/* acc. to FC-FS, hard_nport_id in ADISC should not be set for ports
-	   without FC-AL-2 capability, so we don't set it */
-	adisc->wwpn = fc_host_port_name(adapter->scsi_host);
-	adisc->wwnn = fc_host_node_name(adapter->scsi_host);
-	adisc->nport_id = fc_host_port_id(adapter->scsi_host);
-	ZFCP_LOG_INFO("ADISC request from s_id 0x%06x to d_id 0x%06x "
-		      "(wwpn=0x%016Lx, wwnn=0x%016Lx, "
-		      "hard_nport_id=0x%06x, nport_id=0x%06x)\n",
-		      adisc->nport_id, send_els->d_id, (wwn_t) adisc->wwpn,
-		      (wwn_t) adisc->wwnn, adisc->hard_nport_id,
-		      adisc->nport_id);
-
-	retval = zfcp_fsf_send_els(send_els);
-	if (retval != 0) {
-		ZFCP_LOG_NORMAL("error: initiation of Send ELS failed for port "
-				"0x%06x on adapter %s\n", send_els->d_id,
-				zfcp_get_busid_by_adapter(adapter));
-		goto freemem;
-	}
-
-	goto out;
-
- nomem:
-	retval = -ENOMEM;
- freemem:
-	if (address != NULL)
-		__free_pages(sg_page(send_els->req), 0);
-	if (send_els != NULL) {
-		kfree(send_els->req);
-		kfree(send_els->resp);
-		kfree(send_els);
-	}
- out:
-	return retval;
-}
-
-
-/**
- * zfcp_erp_adisc_handler - handler for ADISC ELS command
- * @data: pointer to struct zfcp_send_els
- *
- * If ADISC failed (LS_RJT or timed out) forced reopen of the port is triggered.
- */
-static void
-zfcp_erp_adisc_handler(unsigned long data)
-{
-	struct zfcp_send_els *send_els;
-	struct zfcp_port *port;
-	struct zfcp_adapter *adapter;
-	u32 d_id;
-	struct zfcp_ls_adisc_acc *adisc;
-
-	send_els = (struct zfcp_send_els *) data;
-	adapter = send_els->adapter;
-	port = send_els->port;
-	d_id = send_els->d_id;
-
-	/* request rejected or timed out */
-	if (send_els->status != 0) {
-		ZFCP_LOG_NORMAL("ELS request rejected/timed out, "
-				"force physical port reopen "
-				"(adapter %s, port d_id=0x%06x)\n",
-				zfcp_get_busid_by_adapter(adapter), d_id);
-		if (zfcp_erp_port_forced_reopen(port, 0, 63, NULL))
-			ZFCP_LOG_NORMAL("failed reopen of port "
-					"(adapter %s, wwpn=0x%016Lx)\n",
-					zfcp_get_busid_by_port(port),
-					port->wwpn);
-		goto out;
-	}
-
-	adisc = zfcp_sg_to_address(send_els->resp);
-
-	ZFCP_LOG_INFO("ADISC response from d_id 0x%06x to s_id "
-		      "0x%06x (wwpn=0x%016Lx, wwnn=0x%016Lx, "
-		      "hard_nport_id=0x%06x, nport_id=0x%06x)\n",
-		      d_id, fc_host_port_id(adapter->scsi_host),
-		      (wwn_t) adisc->wwpn, (wwn_t) adisc->wwnn,
-		      adisc->hard_nport_id, adisc->nport_id);
-
-	/* set wwnn for port */
-	if (port->wwnn == 0)
-		port->wwnn = adisc->wwnn;
-
-	if (port->wwpn != adisc->wwpn) {
-		ZFCP_LOG_NORMAL("d_id assignment changed, reopening "
-				"port (adapter %s, wwpn=0x%016Lx, "
-				"adisc_resp_wwpn=0x%016Lx)\n",
-				zfcp_get_busid_by_port(port),
-				port->wwpn, (wwn_t) adisc->wwpn);
-		if (zfcp_erp_port_reopen(port, 0, 64, NULL))
-			ZFCP_LOG_NORMAL("failed reopen of port "
-					"(adapter %s, wwpn=0x%016Lx)\n",
-					zfcp_get_busid_by_port(port),
-					port->wwpn);
-	}
-
- out:
-	zfcp_port_put(port);
-	__free_pages(sg_page(send_els->req), 0);
-	kfree(send_els->req);
-	kfree(send_els->resp);
-	kfree(send_els);
-}
-
-
-/**
- * zfcp_test_link - lightweight link test procedure
- * @port: port to be tested
- *
- * Test status of a link to a remote port using the ELS command ADISC.
- */
-int
-zfcp_test_link(struct zfcp_port *port)
-{
-	int retval;
-
-	zfcp_port_get(port);
-	retval = zfcp_erp_adisc(port);
-	if (retval != 0 && retval != -EBUSY) {
-		zfcp_port_put(port);
-		ZFCP_LOG_NORMAL("reopen needed for port 0x%016Lx "
-				"on adapter %s\n ", port->wwpn,
-				zfcp_get_busid_by_port(port));
-		retval = zfcp_erp_port_forced_reopen(port, 0, 65, NULL);
-		if (retval != 0) {
-			ZFCP_LOG_NORMAL("reopen of remote port 0x%016Lx "
-					"on adapter %s failed\n", port->wwpn,
-					zfcp_get_busid_by_port(port));
-			retval = -EPERM;
-		}
-	}
-
-	return retval;
-}
-
-
-/*
- * function:
- *
- * purpose:	called if a port failed to be opened normally
- *		initiates Forced Reopen recovery which is done
- *		asynchronously
- *
- * returns:	0	- initiated action successfully
- *		<0	- failed to initiate action
- */
-static int zfcp_erp_port_forced_reopen_internal(struct zfcp_port *port,
-						int clear_mask, u8 id,
-						void *ref)
-{
-	int retval;
-
-	ZFCP_LOG_DEBUG("forced reopen of port 0x%016Lx on adapter %s\n",
-		       port->wwpn, zfcp_get_busid_by_port(port));
-
-	zfcp_erp_port_block(port, clear_mask);
-
-	if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &port->status)) {
-		ZFCP_LOG_DEBUG("skipped forced reopen of failed port 0x%016Lx "
-			       "on adapter %s\n", port->wwpn,
-			       zfcp_get_busid_by_port(port));
-		retval = -EIO;
-		goto out;
-	}
-
-	retval = zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_PORT_FORCED,
-					 port->adapter, port, NULL, id, ref);
-
- out:
-	return retval;
-}
-
-/*
- * function:
- *
- * purpose:	Wrappper for zfcp_erp_port_forced_reopen_internal
- *              used to ensure the correct locking
- *
- * returns:	0	- initiated action successfully
- *		<0	- failed to initiate action
- */
-int zfcp_erp_port_forced_reopen(struct zfcp_port *port, int clear_mask, u8 id,
-				void *ref)
-{
-	int retval;
-	unsigned long flags;
-	struct zfcp_adapter *adapter;
-
-	adapter = port->adapter;
 	read_lock_irqsave(&zfcp_data.config_lock, flags);
 	write_lock(&adapter->erp_lock);
-	retval = zfcp_erp_port_forced_reopen_internal(port, clear_mask, id,
-						      ref);
+	_zfcp_erp_port_forced_reopen(port, clear, id, ref);
 	write_unlock(&adapter->erp_lock);
 	read_unlock_irqrestore(&zfcp_data.config_lock, flags);
-
-	return retval;
 }
 
-/*
- * function:
- *
- * purpose:	called if a port is to be opened
- *		initiates Reopen recovery which is done
- *		asynchronously
- *
- * returns:	0	- initiated action successfully
- *		<0	- failed to initiate action
- */
-static int zfcp_erp_port_reopen_internal(struct zfcp_port *port, int clear_mask,
-					 u8 id, void *ref)
+static int _zfcp_erp_port_reopen(struct zfcp_port *port, int clear, u8 id,
+				 void *ref)
 {
-	int retval;
+	zfcp_erp_port_block(port, clear);
 
-	ZFCP_LOG_DEBUG("reopen of port 0x%016Lx on adapter %s\n",
-		       port->wwpn, zfcp_get_busid_by_port(port));
-
-	zfcp_erp_port_block(port, clear_mask);
-
-	if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &port->status)) {
-		ZFCP_LOG_DEBUG("skipped reopen of failed port 0x%016Lx "
-			       "on adapter %s\n", port->wwpn,
-			       zfcp_get_busid_by_port(port));
+	if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_ERP_FAILED) {
 		/* ensure propagation of failed status to new devices */
 		zfcp_erp_port_failed(port, 14, NULL);
-		retval = -EIO;
-		goto out;
+		return -EIO;
 	}
 
-	retval = zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_PORT,
-					 port->adapter, port, NULL, id, ref);
-
- out:
-	return retval;
+	return zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_PORT,
+				       port->adapter, port, NULL, id, ref);
 }
 
 /**
- * zfcp_erp_port_reopen - initiate reopen of a remote port
- * @port: port to be reopened
- * @clear_mask: specifies flags in port status to be cleared
- * Return: 0 on success, < 0 on error
+ * zfcp_erp_port_reopen - trigger remote port recovery
+ * @port: port to recover
+ * @clear_mask: flags in port status to be cleared
  *
- * This is a wrappper function for zfcp_erp_port_reopen_internal. It ensures
- * correct locking. An error recovery task is initiated to do the reopen.
- * To wait for the completion of the reopen zfcp_erp_wait should be used.
+ * Returns 0 if recovery has been triggered, < 0 if not.
  */
-int zfcp_erp_port_reopen(struct zfcp_port *port, int clear_mask, u8 id,
-			 void *ref)
+int zfcp_erp_port_reopen(struct zfcp_port *port, int clear, u8 id, void *ref)
 {
-	int retval;
 	unsigned long flags;
+	int retval;
 	struct zfcp_adapter *adapter = port->adapter;
 
 	read_lock_irqsave(&zfcp_data.config_lock, flags);
 	write_lock(&adapter->erp_lock);
-	retval = zfcp_erp_port_reopen_internal(port, clear_mask, id, ref);
+	retval = _zfcp_erp_port_reopen(port, clear, id, ref);
 	write_unlock(&adapter->erp_lock);
 	read_unlock_irqrestore(&zfcp_data.config_lock, flags);
 
 	return retval;
 }
 
-/*
- * function:
- *
- * purpose:	called if a unit is to be opened
- *		initiates Reopen recovery which is done
- *		asynchronously
- *
- * returns:	0	- initiated action successfully
- *		<0	- failed to initiate action
- */
-static int zfcp_erp_unit_reopen_internal(struct zfcp_unit *unit, int clear_mask,
-					 u8 id, void *ref)
+static void zfcp_erp_unit_block(struct zfcp_unit *unit, int clear_mask)
 {
-	int retval;
+	zfcp_erp_modify_unit_status(unit, 19, NULL,
+				    ZFCP_STATUS_COMMON_UNBLOCKED | clear_mask,
+				    ZFCP_CLEAR);
+}
+
+static void _zfcp_erp_unit_reopen(struct zfcp_unit *unit, int clear, u8 id,
+				  void *ref)
+{
 	struct zfcp_adapter *adapter = unit->port->adapter;
 
-	ZFCP_LOG_DEBUG("reopen of unit 0x%016Lx on port 0x%016Lx "
-		       "on adapter %s\n", unit->fcp_lun,
-		       unit->port->wwpn, zfcp_get_busid_by_unit(unit));
+	zfcp_erp_unit_block(unit, clear);
 
-	zfcp_erp_unit_block(unit, clear_mask);
+	if (atomic_read(&unit->status) & ZFCP_STATUS_COMMON_ERP_FAILED)
+		return;
 
-	if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &unit->status)) {
-		ZFCP_LOG_DEBUG("skipped reopen of failed unit 0x%016Lx "
-			       "on port 0x%016Lx on adapter %s\n",
-			       unit->fcp_lun, unit->port->wwpn,
-			       zfcp_get_busid_by_unit(unit));
-		retval = -EIO;
-		goto out;
-	}
-
-	retval = zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_UNIT,
-					 adapter, unit->port, unit, id, ref);
- out:
-	return retval;
+	zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_UNIT,
+				adapter, unit->port, unit, id, ref);
 }
 
 /**
@@ -643,725 +408,726 @@
  * @unit: unit to be reopened
  * @clear_mask: specifies flags in unit status to be cleared
  * Return: 0 on success, < 0 on error
- *
- * This is a wrappper for zfcp_erp_unit_reopen_internal. It ensures correct
- * locking. An error recovery task is initiated to do the reopen.
- * To wait for the completion of the reopen zfcp_erp_wait should be used.
  */
-int zfcp_erp_unit_reopen(struct zfcp_unit *unit, int clear_mask, u8 id,
-			 void *ref)
+void zfcp_erp_unit_reopen(struct zfcp_unit *unit, int clear, u8 id, void *ref)
 {
-	int retval;
 	unsigned long flags;
-	struct zfcp_adapter *adapter;
-	struct zfcp_port *port;
-
-	port = unit->port;
-	adapter = port->adapter;
+	struct zfcp_port *port = unit->port;
+	struct zfcp_adapter *adapter = port->adapter;
 
 	read_lock_irqsave(&zfcp_data.config_lock, flags);
 	write_lock(&adapter->erp_lock);
-	retval = zfcp_erp_unit_reopen_internal(unit, clear_mask, id, ref);
+	_zfcp_erp_unit_reopen(unit, clear, id, ref);
 	write_unlock(&adapter->erp_lock);
 	read_unlock_irqrestore(&zfcp_data.config_lock, flags);
-
-	return retval;
 }
 
-/**
- * zfcp_erp_adapter_block - mark adapter as blocked, block scsi requests
- */
-static void zfcp_erp_adapter_block(struct zfcp_adapter *adapter, int clear_mask)
+static int status_change_set(unsigned long mask, atomic_t *status)
 {
-	zfcp_erp_modify_adapter_status(adapter, 15, NULL,
-				       ZFCP_STATUS_COMMON_UNBLOCKED |
-				       clear_mask, ZFCP_CLEAR);
+	return (atomic_read(status) ^ mask) & mask;
 }
 
-/* FIXME: isn't really atomic */
-/*
- * returns the mask which has not been set so far, i.e.
- * 0 if no bit has been changed, !0 if some bit has been changed
- */
-static int atomic_test_and_set_mask(unsigned long mask, atomic_t *v)
+static int status_change_clear(unsigned long mask, atomic_t *status)
 {
-	int changed_bits = (atomic_read(v) /*XOR*/^ mask) & mask;
-	atomic_set_mask(mask, v);
-	return changed_bits;
+	return atomic_read(status) & mask;
 }
 
-/* FIXME: isn't really atomic */
-/*
- * returns the mask which has not been cleared so far, i.e.
- * 0 if no bit has been changed, !0 if some bit has been changed
- */
-static int atomic_test_and_clear_mask(unsigned long mask, atomic_t *v)
-{
-	int changed_bits = atomic_read(v) & mask;
-	atomic_clear_mask(mask, v);
-	return changed_bits;
-}
-
-/**
- * zfcp_erp_adapter_unblock - mark adapter as unblocked, allow scsi requests
- */
 static void zfcp_erp_adapter_unblock(struct zfcp_adapter *adapter)
 {
-	if (atomic_test_and_set_mask(ZFCP_STATUS_COMMON_UNBLOCKED,
-				     &adapter->status))
+	if (status_change_set(ZFCP_STATUS_COMMON_UNBLOCKED, &adapter->status))
 		zfcp_rec_dbf_event_adapter(16, NULL, adapter);
+	atomic_set_mask(ZFCP_STATUS_COMMON_UNBLOCKED, &adapter->status);
 }
 
-/*
- * function:
- *
- * purpose:	disable I/O,
- *		return any open requests and clean them up,
- *		aim: no pending and incoming I/O
- *
- * returns:
- */
-static void
-zfcp_erp_port_block(struct zfcp_port *port, int clear_mask)
+static void zfcp_erp_port_unblock(struct zfcp_port *port)
 {
-	zfcp_erp_modify_port_status(port, 17, NULL,
-				    ZFCP_STATUS_COMMON_UNBLOCKED | clear_mask,
-				    ZFCP_CLEAR);
-}
-
-/*
- * function:
- *
- * purpose:	enable I/O
- *
- * returns:
- */
-static void
-zfcp_erp_port_unblock(struct zfcp_port *port)
-{
-	if (atomic_test_and_set_mask(ZFCP_STATUS_COMMON_UNBLOCKED,
-				     &port->status))
+	if (status_change_set(ZFCP_STATUS_COMMON_UNBLOCKED, &port->status))
 		zfcp_rec_dbf_event_port(18, NULL, port);
+	atomic_set_mask(ZFCP_STATUS_COMMON_UNBLOCKED, &port->status);
 }
 
-/*
- * function:
- *
- * purpose:	disable I/O,
- *		return any open requests and clean them up,
- *		aim: no pending and incoming I/O
- *
- * returns:
- */
-static void
-zfcp_erp_unit_block(struct zfcp_unit *unit, int clear_mask)
+static void zfcp_erp_unit_unblock(struct zfcp_unit *unit)
 {
-	zfcp_erp_modify_unit_status(unit, 19, NULL,
-				    ZFCP_STATUS_COMMON_UNBLOCKED | clear_mask,
-				    ZFCP_CLEAR);
-}
-
-/*
- * function:
- *
- * purpose:	enable I/O
- *
- * returns:
- */
-static void
-zfcp_erp_unit_unblock(struct zfcp_unit *unit)
-{
-	if (atomic_test_and_set_mask(ZFCP_STATUS_COMMON_UNBLOCKED,
-				     &unit->status))
+	if (status_change_set(ZFCP_STATUS_COMMON_UNBLOCKED, &unit->status))
 		zfcp_rec_dbf_event_unit(20, NULL, unit);
+	atomic_set_mask(ZFCP_STATUS_COMMON_UNBLOCKED, &unit->status);
 }
 
-static void
-zfcp_erp_action_ready(struct zfcp_erp_action *erp_action)
+static void zfcp_erp_action_to_running(struct zfcp_erp_action *erp_action)
 {
-	struct zfcp_adapter *adapter = erp_action->adapter;
-
-	zfcp_erp_action_to_ready(erp_action);
-	up(&adapter->erp_ready_sem);
-	zfcp_rec_dbf_event_thread(2, adapter, 0);
+	list_move(&erp_action->list, &erp_action->adapter->erp_running_head);
+	zfcp_rec_dbf_event_action(145, erp_action);
 }
 
-/*
- * function:
- *
- * purpose:
- *
- * returns:	<0			erp_action not found in any list
- *		ZFCP_ERP_ACTION_READY	erp_action is in ready list
- *		ZFCP_ERP_ACTION_RUNNING	erp_action is in running list
- *
- * locks:	erp_lock must be held
- */
-static int
-zfcp_erp_action_exists(struct zfcp_erp_action *erp_action)
+static void zfcp_erp_strategy_check_fsfreq(struct zfcp_erp_action *act)
 {
-	int retval = -EINVAL;
-	struct list_head *entry;
-	struct zfcp_erp_action *entry_erp_action;
-	struct zfcp_adapter *adapter = erp_action->adapter;
+	struct zfcp_adapter *adapter = act->adapter;
 
-	/* search in running list */
-	list_for_each(entry, &adapter->erp_running_head) {
-		entry_erp_action =
-		    list_entry(entry, struct zfcp_erp_action, list);
-		if (entry_erp_action == erp_action) {
-			retval = ZFCP_ERP_ACTION_RUNNING;
-			goto out;
+	if (!act->fsf_req)
+		return;
+
+	spin_lock(&adapter->req_list_lock);
+	if (zfcp_reqlist_find_safe(adapter, act->fsf_req) &&
+	    act->fsf_req->erp_action == act) {
+		if (act->status & (ZFCP_STATUS_ERP_DISMISSED |
+				   ZFCP_STATUS_ERP_TIMEDOUT)) {
+			act->fsf_req->status |= ZFCP_STATUS_FSFREQ_DISMISSED;
+			zfcp_rec_dbf_event_action(142, act);
 		}
-	}
-	/* search in ready list */
-	list_for_each(entry, &adapter->erp_ready_head) {
-		entry_erp_action =
-		    list_entry(entry, struct zfcp_erp_action, list);
-		if (entry_erp_action == erp_action) {
-			retval = ZFCP_ERP_ACTION_READY;
-			goto out;
-		}
-	}
-
- out:
-	return retval;
-}
-
-/*
- * purpose:	checks current status of action (timed out, dismissed, ...)
- *		and does appropriate preparations (dismiss fsf request, ...)
- *
- * locks:	called under erp_lock (disabled interrupts)
- */
-static void
-zfcp_erp_strategy_check_fsfreq(struct zfcp_erp_action *erp_action)
-{
-	struct zfcp_adapter *adapter = erp_action->adapter;
-
-	if (erp_action->fsf_req) {
-		/* take lock to ensure that request is not deleted meanwhile */
-		spin_lock(&adapter->req_list_lock);
-		if (zfcp_reqlist_find_safe(adapter, erp_action->fsf_req) &&
-		    erp_action->fsf_req->erp_action == erp_action) {
-			/* fsf_req still exists */
-			/* dismiss fsf_req of timed out/dismissed erp_action */
-			if (erp_action->status & (ZFCP_STATUS_ERP_DISMISSED |
-						  ZFCP_STATUS_ERP_TIMEDOUT)) {
-				erp_action->fsf_req->status |=
-					ZFCP_STATUS_FSFREQ_DISMISSED;
-				zfcp_rec_dbf_event_action(142, erp_action);
-			}
-			if (erp_action->status & ZFCP_STATUS_ERP_TIMEDOUT) {
-				zfcp_rec_dbf_event_action(143, erp_action);
-				ZFCP_LOG_NORMAL("error: erp step timed out "
-						"(action=%d, fsf_req=%p)\n ",
-						erp_action->action,
-						erp_action->fsf_req);
-			}
-			/*
-			 * If fsf_req is neither dismissed nor completed
-			 * then keep it running asynchronously and don't mess
-			 * with the association of erp_action and fsf_req.
-			 */
-			if (erp_action->fsf_req->status &
-					(ZFCP_STATUS_FSFREQ_COMPLETED |
-					       ZFCP_STATUS_FSFREQ_DISMISSED)) {
-				/* forget about association between fsf_req
-				   and erp_action */
-				erp_action->fsf_req = NULL;
-			}
-		} else {
-			/*
-			 * even if this fsf_req has gone, forget about
-			 * association between erp_action and fsf_req
-			 */
-			erp_action->fsf_req = NULL;
-		}
-		spin_unlock(&adapter->req_list_lock);
-	}
+		if (act->status & ZFCP_STATUS_ERP_TIMEDOUT)
+			zfcp_rec_dbf_event_action(143, act);
+		if (act->fsf_req->status & (ZFCP_STATUS_FSFREQ_COMPLETED |
+					    ZFCP_STATUS_FSFREQ_DISMISSED))
+			act->fsf_req = NULL;
+	} else
+		act->fsf_req = NULL;
+	spin_unlock(&adapter->req_list_lock);
 }
 
 /**
- * zfcp_erp_async_handler_nolock - complete erp_action
- *
- * Used for normal completion, time-out, dismissal and failure after
- * low memory condition.
+ * zfcp_erp_notify - Trigger ERP action.
+ * @erp_action: ERP action to continue.
+ * @set_mask: ERP action status flags to set.
  */
-static void zfcp_erp_async_handler_nolock(struct zfcp_erp_action *erp_action,
-					  unsigned long set_mask)
-{
-	if (zfcp_erp_action_exists(erp_action) == ZFCP_ERP_ACTION_RUNNING) {
-		erp_action->status |= set_mask;
-		zfcp_erp_action_ready(erp_action);
-	} else {
-		/* action is ready or gone - nothing to do */
-	}
-}
-
-/**
- * zfcp_erp_async_handler - wrapper for erp_async_handler_nolock w/ locking
- */
-void zfcp_erp_async_handler(struct zfcp_erp_action *erp_action,
-			    unsigned long set_mask)
+void zfcp_erp_notify(struct zfcp_erp_action *erp_action, unsigned long set_mask)
 {
 	struct zfcp_adapter *adapter = erp_action->adapter;
 	unsigned long flags;
 
 	write_lock_irqsave(&adapter->erp_lock, flags);
-	zfcp_erp_async_handler_nolock(erp_action, set_mask);
+	if (zfcp_erp_action_exists(erp_action) == ZFCP_ERP_ACTION_RUNNING) {
+		erp_action->status |= set_mask;
+		zfcp_erp_action_ready(erp_action);
+	}
 	write_unlock_irqrestore(&adapter->erp_lock, flags);
 }
 
-/*
- * purpose:	is called for erp_action which was slept waiting for
- *		memory becoming avaliable,
- *		will trigger that this action will be continued
- */
-static void
-zfcp_erp_memwait_handler(unsigned long data)
-{
-	struct zfcp_erp_action *erp_action = (struct zfcp_erp_action *) data;
-
-	zfcp_erp_async_handler(erp_action, 0);
-}
-
-/*
- * purpose:	is called if an asynchronous erp step timed out,
- *		action gets an appropriate flag and will be processed
- *		accordingly
- */
-static void zfcp_erp_timeout_handler(unsigned long data)
-{
-	struct zfcp_erp_action *erp_action = (struct zfcp_erp_action *) data;
-
-	zfcp_erp_async_handler(erp_action, ZFCP_STATUS_ERP_TIMEDOUT);
-}
-
 /**
- * zfcp_erp_action_dismiss - dismiss an erp_action
- *
- * adapter->erp_lock must be held
- *
- * Dismissal of an erp_action is usually required if an erp_action of
- * higher priority is generated.
+ * zfcp_erp_timeout_handler - Trigger ERP action from timed out ERP request
+ * @data: ERP action (from timer data)
  */
-static void zfcp_erp_action_dismiss(struct zfcp_erp_action *erp_action)
+void zfcp_erp_timeout_handler(unsigned long data)
 {
-	erp_action->status |= ZFCP_STATUS_ERP_DISMISSED;
-	if (zfcp_erp_action_exists(erp_action) == ZFCP_ERP_ACTION_RUNNING)
-		zfcp_erp_action_ready(erp_action);
+	struct zfcp_erp_action *act = (struct zfcp_erp_action *) data;
+	zfcp_erp_notify(act, ZFCP_STATUS_ERP_TIMEDOUT);
 }
 
-int
-zfcp_erp_thread_setup(struct zfcp_adapter *adapter)
+static void zfcp_erp_memwait_handler(unsigned long data)
 {
-	int retval = 0;
-
-	atomic_clear_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_UP, &adapter->status);
-
-	retval = kernel_thread(zfcp_erp_thread, adapter, SIGCHLD);
-	if (retval < 0) {
-		ZFCP_LOG_NORMAL("error: creation of erp thread failed for "
-				"adapter %s\n",
-				zfcp_get_busid_by_adapter(adapter));
-	} else {
-		wait_event(adapter->erp_thread_wqh,
-			   atomic_test_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_UP,
-					    &adapter->status));
-	}
-
-	return (retval < 0);
+	zfcp_erp_notify((struct zfcp_erp_action *)data, 0);
 }
 
-/*
- * function:
- *
- * purpose:
- *
- * returns:
- *
- * context:	process (i.e. proc-fs or rmmod/insmod)
- *
- * note:	The caller of this routine ensures that the specified
- *		adapter has been shut down and that this operation
- *		has been completed. Thus, there are no pending erp_actions
- *		which would need to be handled here.
- */
-int
-zfcp_erp_thread_kill(struct zfcp_adapter *adapter)
+static void zfcp_erp_strategy_memwait(struct zfcp_erp_action *erp_action)
 {
-	int retval = 0;
-
-	atomic_set_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_KILL, &adapter->status);
-	up(&adapter->erp_ready_sem);
-	zfcp_rec_dbf_event_thread(2, adapter, 1);
-
-	wait_event(adapter->erp_thread_wqh,
-		   !atomic_test_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_UP,
-				     &adapter->status));
-
-	atomic_clear_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_KILL,
-			  &adapter->status);
-
-	return retval;
-}
-
-/*
- * purpose:	is run as a kernel thread,
- *		goes through list of error recovery actions of associated adapter
- *		and delegates single action to execution
- *
- * returns:	0
- */
-static int
-zfcp_erp_thread(void *data)
-{
-	struct zfcp_adapter *adapter = (struct zfcp_adapter *) data;
-	struct list_head *next;
-	struct zfcp_erp_action *erp_action;
-	unsigned long flags;
-
-	daemonize("zfcperp%s", zfcp_get_busid_by_adapter(adapter));
-	/* Block all signals */
-	siginitsetinv(&current->blocked, 0);
-	atomic_set_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_UP, &adapter->status);
-	wake_up(&adapter->erp_thread_wqh);
-
-	while (!atomic_test_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_KILL,
-				 &adapter->status)) {
-
-		write_lock_irqsave(&adapter->erp_lock, flags);
-		next = adapter->erp_ready_head.next;
-		write_unlock_irqrestore(&adapter->erp_lock, flags);
-
-		if (next != &adapter->erp_ready_head) {
-			erp_action =
-			    list_entry(next, struct zfcp_erp_action, list);
-			/*
-			 * process action (incl. [re]moving it
-			 * from 'ready' queue)
-			 */
-			zfcp_erp_strategy(erp_action);
-		}
-
-		/*
-		 * sleep as long as there is nothing to do, i.e.
-		 * no action in 'ready' queue to be processed and
-		 * thread is not to be killed
-		 */
-		zfcp_rec_dbf_event_thread(4, adapter, 1);
-		down_interruptible(&adapter->erp_ready_sem);
-		zfcp_rec_dbf_event_thread(5, adapter, 1);
-	}
-
-	atomic_clear_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_UP, &adapter->status);
-	wake_up(&adapter->erp_thread_wqh);
-
-	return 0;
-}
-
-/*
- * function:
- *
- * purpose:	drives single error recovery action and schedules higher and
- *		subordinate actions, if necessary
- *
- * returns:	ZFCP_ERP_CONTINUES	- action continues (asynchronously)
- *		ZFCP_ERP_SUCCEEDED	- action finished successfully (deqd)
- *		ZFCP_ERP_FAILED		- action finished unsuccessfully (deqd)
- *		ZFCP_ERP_EXIT		- action finished (dequeued), offline
- *		ZFCP_ERP_DISMISSED	- action canceled (dequeued)
- */
-static int
-zfcp_erp_strategy(struct zfcp_erp_action *erp_action)
-{
-	int retval = 0;
-	struct zfcp_adapter *adapter = erp_action->adapter;
-	struct zfcp_port *port = erp_action->port;
-	struct zfcp_unit *unit = erp_action->unit;
-	int action = erp_action->action;
-	u32 status = erp_action->status;
-	unsigned long flags;
-
-	/* serialise dismissing, timing out, moving, enqueueing */
-	read_lock_irqsave(&zfcp_data.config_lock, flags);
-	write_lock(&adapter->erp_lock);
-
-	/* dequeue dismissed action and leave, if required */
-	retval = zfcp_erp_strategy_check_action(erp_action, retval);
-	if (retval == ZFCP_ERP_DISMISSED) {
-		goto unlock;
-	}
-
-	/*
-	 * move action to 'running' queue before processing it
-	 * (to avoid a race condition regarding moving the
-	 * action to the 'running' queue and back)
-	 */
-	zfcp_erp_action_to_running(erp_action);
-
-	/*
-	 * try to process action as far as possible,
-	 * no lock to allow for blocking operations (kmalloc, qdio, ...),
-	 * afterwards the lock is required again for the following reasons:
-	 * - dequeueing of finished action and enqueueing of
-	 *   follow-up actions must be atomic so that any other
-	 *   reopen-routine does not believe there is nothing to do
-	 *   and that it is safe to enqueue something else,
-	 * - we want to force any control thread which is dismissing
-	 *   actions to finish this before we decide about
-	 *   necessary steps to be taken here further
-	 */
-	write_unlock(&adapter->erp_lock);
-	read_unlock_irqrestore(&zfcp_data.config_lock, flags);
-	retval = zfcp_erp_strategy_do_action(erp_action);
-	read_lock_irqsave(&zfcp_data.config_lock, flags);
-	write_lock(&adapter->erp_lock);
-
-	/*
-	 * check for dismissed status again to avoid follow-up actions,
-	 * failing of targets and so on for dismissed actions,
-	 * we go through down() here because there has been an up()
-	 */
-	if (erp_action->status & ZFCP_STATUS_ERP_DISMISSED)
-		retval = ZFCP_ERP_CONTINUES;
-
-	switch (retval) {
-	case ZFCP_ERP_NOMEM:
-		/* no memory to continue immediately, let it sleep */
-		if (!(erp_action->status & ZFCP_STATUS_ERP_LOWMEM)) {
-			++adapter->erp_low_mem_count;
-			erp_action->status |= ZFCP_STATUS_ERP_LOWMEM;
-		}
-		/* This condition is true if there is no memory available
-		   for any erp_action on this adapter. This implies that there
-		   are no elements in the memory pool(s) left for erp_actions.
-		   This might happen if an erp_action that used a memory pool
-		   element was timed out.
-		 */
-		if (adapter->erp_total_count == adapter->erp_low_mem_count) {
-			ZFCP_LOG_NORMAL("error: no mempool elements available, "
-					"restarting I/O on adapter %s "
-					"to free mempool\n",
-					zfcp_get_busid_by_adapter(adapter));
-			zfcp_erp_adapter_reopen_internal(adapter, 0, 66, NULL);
-		} else {
-		retval = zfcp_erp_strategy_memwait(erp_action);
-		}
-		goto unlock;
-	case ZFCP_ERP_CONTINUES:
-		/* leave since this action runs asynchronously */
-		if (erp_action->status & ZFCP_STATUS_ERP_LOWMEM) {
-			--adapter->erp_low_mem_count;
-			erp_action->status &= ~ZFCP_STATUS_ERP_LOWMEM;
-		}
-		goto unlock;
-	}
-	/* ok, finished action (whatever its result is) */
-
-	/* check for unrecoverable targets */
-	retval = zfcp_erp_strategy_check_target(erp_action, retval);
-
-	/* action must be dequeued (here to allow for further ones) */
-	zfcp_erp_action_dequeue(erp_action);
-
-	/*
-	 * put this target through the erp mill again if someone has
-	 * requested to change the status of a target being online
-	 * to offline or the other way around
-	 * (old retval is preserved if nothing has to be done here)
-	 */
-	retval = zfcp_erp_strategy_statechange(action, status, adapter,
-					       port, unit, retval);
-
-	/*
-	 * leave if target is in permanent error state or if
-	 * action is repeated in order to process state change
-	 */
-	if (retval == ZFCP_ERP_EXIT) {
-		goto unlock;
-	}
-
-	/* trigger follow up actions */
-	zfcp_erp_strategy_followup_actions(action, adapter, port, unit, retval);
-
- unlock:
-	write_unlock(&adapter->erp_lock);
-	read_unlock_irqrestore(&zfcp_data.config_lock, flags);
-
-	if (retval != ZFCP_ERP_CONTINUES)
-		zfcp_erp_action_cleanup(action, adapter, port, unit, retval);
-
-	/*
-	 * a few tasks remain when the erp queues are empty
-	 * (don't do that if the last action evaluated was dismissed
-	 * since this clearly indicates that there is more to come) :
-	 * - close the name server port if it is open yet
-	 *   (enqueues another [probably] final action)
-	 * - otherwise, wake up whoever wants to be woken when we are
-	 *   done with erp
-	 */
-	if (retval != ZFCP_ERP_DISMISSED)
-		zfcp_erp_strategy_check_queues(adapter);
-
-	return retval;
-}
-
-/*
- * function:
- *
- * purpose:
- *
- * returns:	ZFCP_ERP_DISMISSED	- if action has been dismissed
- *		retval			- otherwise
- */
-static int
-zfcp_erp_strategy_check_action(struct zfcp_erp_action *erp_action, int retval)
-{
-	zfcp_erp_strategy_check_fsfreq(erp_action);
-
-	if (erp_action->status & ZFCP_STATUS_ERP_DISMISSED) {
-		zfcp_erp_action_dequeue(erp_action);
-		retval = ZFCP_ERP_DISMISSED;
-	}
-
-	return retval;
-}
-
-static int
-zfcp_erp_strategy_do_action(struct zfcp_erp_action *erp_action)
-{
-	int retval = ZFCP_ERP_FAILED;
-
-	/*
-	 * try to execute/continue action as far as possible,
-	 * note: no lock in subsequent strategy routines
-	 * (this allows these routine to call schedule, e.g.
-	 * kmalloc with such flags or qdio_initialize & friends)
-	 * Note: in case of timeout, the separate strategies will fail
-	 * anyhow. No need for a special action. Even worse, a nameserver
-	 * failure would not wake up waiting ports without the call.
-	 */
-	switch (erp_action->action) {
-
-	case ZFCP_ERP_ACTION_REOPEN_ADAPTER:
-		retval = zfcp_erp_adapter_strategy(erp_action);
-		break;
-
-	case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED:
-		retval = zfcp_erp_port_forced_strategy(erp_action);
-		break;
-
-	case ZFCP_ERP_ACTION_REOPEN_PORT:
-		retval = zfcp_erp_port_strategy(erp_action);
-		break;
-
-	case ZFCP_ERP_ACTION_REOPEN_UNIT:
-		retval = zfcp_erp_unit_strategy(erp_action);
-		break;
-
-	default:
-		ZFCP_LOG_NORMAL("bug: unknown erp action requested on "
-				"adapter %s (action=%d)\n",
-				zfcp_get_busid_by_adapter(erp_action->adapter),
-				erp_action->action);
-	}
-
-	return retval;
-}
-
-/*
- * function:
- *
- * purpose:	triggers retry of this action after a certain amount of time
- *		by means of timer provided by erp_action
- *
- * returns:	ZFCP_ERP_CONTINUES - erp_action sleeps in erp running queue
- */
-static int
-zfcp_erp_strategy_memwait(struct zfcp_erp_action *erp_action)
-{
-	int retval = ZFCP_ERP_CONTINUES;
-
 	init_timer(&erp_action->timer);
 	erp_action->timer.function = zfcp_erp_memwait_handler;
 	erp_action->timer.data = (unsigned long) erp_action;
-	erp_action->timer.expires = jiffies + ZFCP_ERP_MEMWAIT_TIMEOUT;
+	erp_action->timer.expires = jiffies + HZ;
 	add_timer(&erp_action->timer);
+}
+
+static void _zfcp_erp_port_reopen_all(struct zfcp_adapter *adapter,
+				      int clear, u8 id, void *ref)
+{
+	struct zfcp_port *port;
+
+	list_for_each_entry(port, &adapter->port_list_head, list)
+		if (!(atomic_read(&port->status) & ZFCP_STATUS_PORT_WKA))
+			_zfcp_erp_port_reopen(port, clear, id, ref);
+}
+
+static void _zfcp_erp_unit_reopen_all(struct zfcp_port *port, int clear, u8 id,
+				      void *ref)
+{
+	struct zfcp_unit *unit;
+
+	list_for_each_entry(unit, &port->unit_list_head, list)
+		_zfcp_erp_unit_reopen(unit, clear, id, ref);
+}
+
+static void zfcp_erp_strategy_followup_actions(struct zfcp_erp_action *act)
+{
+	struct zfcp_adapter *adapter = act->adapter;
+	struct zfcp_port *port = act->port;
+	struct zfcp_unit *unit = act->unit;
+	u32 status = act->status;
+
+	/* initiate follow-up actions depending on success of finished action */
+	switch (act->action) {
+
+	case ZFCP_ERP_ACTION_REOPEN_ADAPTER:
+		if (status == ZFCP_ERP_SUCCEEDED)
+			_zfcp_erp_port_reopen_all(adapter, 0, 70, NULL);
+		else
+			_zfcp_erp_adapter_reopen(adapter, 0, 71, NULL);
+		break;
+
+	case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED:
+		if (status == ZFCP_ERP_SUCCEEDED)
+			_zfcp_erp_port_reopen(port, 0, 72, NULL);
+		else
+			_zfcp_erp_adapter_reopen(adapter, 0, 73, NULL);
+		break;
+
+	case ZFCP_ERP_ACTION_REOPEN_PORT:
+		if (status == ZFCP_ERP_SUCCEEDED)
+			_zfcp_erp_unit_reopen_all(port, 0, 74, NULL);
+		else
+			_zfcp_erp_port_forced_reopen(port, 0, 75, NULL);
+		break;
+
+	case ZFCP_ERP_ACTION_REOPEN_UNIT:
+		if (status != ZFCP_ERP_SUCCEEDED)
+			_zfcp_erp_port_reopen(unit->port, 0, 76, NULL);
+		break;
+	}
+}
+
+static void zfcp_erp_wakeup(struct zfcp_adapter *adapter)
+{
+	unsigned long flags;
+
+	read_lock_irqsave(&zfcp_data.config_lock, flags);
+	read_lock(&adapter->erp_lock);
+	if (list_empty(&adapter->erp_ready_head) &&
+	    list_empty(&adapter->erp_running_head)) {
+			atomic_clear_mask(ZFCP_STATUS_ADAPTER_ERP_PENDING,
+					  &adapter->status);
+			wake_up(&adapter->erp_done_wqh);
+	}
+	read_unlock(&adapter->erp_lock);
+	read_unlock_irqrestore(&zfcp_data.config_lock, flags);
+}
+
+static int zfcp_erp_adapter_strategy_open_qdio(struct zfcp_erp_action *act)
+{
+	if (zfcp_qdio_open(act->adapter))
+		return ZFCP_ERP_FAILED;
+	init_waitqueue_head(&act->adapter->request_wq);
+	atomic_set_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &act->adapter->status);
+	return ZFCP_ERP_SUCCEEDED;
+}
+
+static void zfcp_erp_enqueue_ptp_port(struct zfcp_adapter *adapter)
+{
+	struct zfcp_port *port;
+	port = zfcp_port_enqueue(adapter, adapter->peer_wwpn, 0,
+				 adapter->peer_d_id);
+	if (IS_ERR(port)) /* error or port already attached */
+		return;
+	_zfcp_erp_port_reopen(port, 0, 150, NULL);
+}
+
+static int zfcp_erp_adapter_strat_fsf_xconf(struct zfcp_erp_action *erp_action)
+{
+	int retries;
+	int sleep = 1;
+	struct zfcp_adapter *adapter = erp_action->adapter;
+
+	atomic_clear_mask(ZFCP_STATUS_ADAPTER_XCONFIG_OK, &adapter->status);
+
+	for (retries = 7; retries; retries--) {
+		atomic_clear_mask(ZFCP_STATUS_ADAPTER_HOST_CON_INIT,
+				  &adapter->status);
+		write_lock_irq(&adapter->erp_lock);
+		zfcp_erp_action_to_running(erp_action);
+		write_unlock_irq(&adapter->erp_lock);
+		if (zfcp_fsf_exchange_config_data(erp_action)) {
+			atomic_clear_mask(ZFCP_STATUS_ADAPTER_HOST_CON_INIT,
+					  &adapter->status);
+			return ZFCP_ERP_FAILED;
+		}
+
+		zfcp_rec_dbf_event_thread_lock(6, adapter);
+		down(&adapter->erp_ready_sem);
+		zfcp_rec_dbf_event_thread_lock(7, adapter);
+		if (erp_action->status & ZFCP_STATUS_ERP_TIMEDOUT)
+			break;
+
+		if (!(atomic_read(&adapter->status) &
+		      ZFCP_STATUS_ADAPTER_HOST_CON_INIT))
+			break;
+
+		ssleep(sleep);
+		sleep *= 2;
+	}
+
+	atomic_clear_mask(ZFCP_STATUS_ADAPTER_HOST_CON_INIT,
+			  &adapter->status);
+
+	if (!(atomic_read(&adapter->status) & ZFCP_STATUS_ADAPTER_XCONFIG_OK))
+		return ZFCP_ERP_FAILED;
+
+	if (fc_host_port_type(adapter->scsi_host) == FC_PORTTYPE_PTP)
+		zfcp_erp_enqueue_ptp_port(adapter);
+
+	return ZFCP_ERP_SUCCEEDED;
+}
+
+static int zfcp_erp_adapter_strategy_open_fsf_xport(struct zfcp_erp_action *act)
+{
+	int ret;
+	struct zfcp_adapter *adapter = act->adapter;
+
+	atomic_clear_mask(ZFCP_STATUS_ADAPTER_XPORT_OK, &adapter->status);
+
+	write_lock_irq(&adapter->erp_lock);
+	zfcp_erp_action_to_running(act);
+	write_unlock_irq(&adapter->erp_lock);
+
+	ret = zfcp_fsf_exchange_port_data(act);
+	if (ret == -EOPNOTSUPP)
+		return ZFCP_ERP_SUCCEEDED;
+	if (ret)
+		return ZFCP_ERP_FAILED;
+
+	zfcp_rec_dbf_event_thread_lock(8, adapter);
+	down(&adapter->erp_ready_sem);
+	zfcp_rec_dbf_event_thread_lock(9, adapter);
+	if (act->status & ZFCP_STATUS_ERP_TIMEDOUT)
+		return ZFCP_ERP_FAILED;
+
+	return ZFCP_ERP_SUCCEEDED;
+}
+
+static int zfcp_erp_adapter_strategy_open_fsf(struct zfcp_erp_action *act)
+{
+	if (zfcp_erp_adapter_strat_fsf_xconf(act) == ZFCP_ERP_FAILED)
+		return ZFCP_ERP_FAILED;
+
+	if (zfcp_erp_adapter_strategy_open_fsf_xport(act) == ZFCP_ERP_FAILED)
+		return ZFCP_ERP_FAILED;
+
+	atomic_set(&act->adapter->stat_miss, 16);
+	if (zfcp_status_read_refill(act->adapter))
+		return ZFCP_ERP_FAILED;
+
+	return ZFCP_ERP_SUCCEEDED;
+}
+
+static int zfcp_erp_adapter_strategy_generic(struct zfcp_erp_action *act,
+					     int close)
+{
+	int retval = ZFCP_ERP_SUCCEEDED;
+	struct zfcp_adapter *adapter = act->adapter;
+
+	if (close)
+		goto close_only;
+
+	retval = zfcp_erp_adapter_strategy_open_qdio(act);
+	if (retval != ZFCP_ERP_SUCCEEDED)
+		goto failed_qdio;
+
+	retval = zfcp_erp_adapter_strategy_open_fsf(act);
+	if (retval != ZFCP_ERP_SUCCEEDED)
+		goto failed_openfcp;
+
+	atomic_set_mask(ZFCP_STATUS_COMMON_OPEN, &act->adapter->status);
+	schedule_work(&act->adapter->scan_work);
+
+	return ZFCP_ERP_SUCCEEDED;
+
+ close_only:
+	atomic_clear_mask(ZFCP_STATUS_COMMON_OPEN,
+			  &act->adapter->status);
+
+ failed_openfcp:
+	/* close queues to ensure that buffers are not accessed by adapter */
+	zfcp_qdio_close(adapter);
+	zfcp_fsf_req_dismiss_all(adapter);
+	adapter->fsf_req_seq_no = 0;
+	/* all ports and units are closed */
+	zfcp_erp_modify_adapter_status(adapter, 24, NULL,
+				       ZFCP_STATUS_COMMON_OPEN, ZFCP_CLEAR);
+ failed_qdio:
+	atomic_clear_mask(ZFCP_STATUS_ADAPTER_XCONFIG_OK |
+			  ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED |
+			  ZFCP_STATUS_ADAPTER_XPORT_OK,
+			  &act->adapter->status);
+	return retval;
+}
+
+static int zfcp_erp_adapter_strategy(struct zfcp_erp_action *act)
+{
+	int retval;
+
+	atomic_set_mask(ZFCP_STATUS_COMMON_CLOSING, &act->adapter->status);
+	zfcp_erp_adapter_strategy_generic(act, 1); /* close */
+	atomic_clear_mask(ZFCP_STATUS_COMMON_CLOSING, &act->adapter->status);
+	if (act->status & ZFCP_STATUS_ERP_CLOSE_ONLY)
+		return ZFCP_ERP_EXIT;
+
+	atomic_set_mask(ZFCP_STATUS_COMMON_OPENING, &act->adapter->status);
+	retval = zfcp_erp_adapter_strategy_generic(act, 0); /* open */
+	atomic_clear_mask(ZFCP_STATUS_COMMON_OPENING, &act->adapter->status);
+
+	if (retval == ZFCP_ERP_FAILED)
+		ssleep(8);
 
 	return retval;
 }
 
-/*
- * function:    zfcp_erp_adapter_failed
- *
- * purpose:     sets the adapter and all underlying devices to ERP_FAILED
- *
- */
-void
-zfcp_erp_adapter_failed(struct zfcp_adapter *adapter, u8 id, void *ref)
+static int zfcp_erp_port_forced_strategy_close(struct zfcp_erp_action *act)
 {
-	zfcp_erp_modify_adapter_status(adapter, id, ref,
-				       ZFCP_STATUS_COMMON_ERP_FAILED, ZFCP_SET);
-	ZFCP_LOG_NORMAL("adapter erp failed on adapter %s\n",
-			zfcp_get_busid_by_adapter(adapter));
+	int retval;
+
+	retval = zfcp_fsf_close_physical_port(act);
+	if (retval == -ENOMEM)
+		return ZFCP_ERP_NOMEM;
+	act->step = ZFCP_ERP_STEP_PHYS_PORT_CLOSING;
+	if (retval)
+		return ZFCP_ERP_FAILED;
+
+	return ZFCP_ERP_CONTINUES;
 }
 
-/*
- * function:    zfcp_erp_port_failed
- *
- * purpose:     sets the port and all underlying devices to ERP_FAILED
- *
- */
-void
-zfcp_erp_port_failed(struct zfcp_port *port, u8 id, void *ref)
+static void zfcp_erp_port_strategy_clearstati(struct zfcp_port *port)
 {
-	zfcp_erp_modify_port_status(port, id, ref,
-				    ZFCP_STATUS_COMMON_ERP_FAILED, ZFCP_SET);
+	atomic_clear_mask(ZFCP_STATUS_COMMON_OPENING |
+			  ZFCP_STATUS_COMMON_CLOSING |
+			  ZFCP_STATUS_COMMON_ACCESS_DENIED |
+			  ZFCP_STATUS_PORT_DID_DID |
+			  ZFCP_STATUS_PORT_PHYS_CLOSING |
+			  ZFCP_STATUS_PORT_INVALID_WWPN,
+			  &port->status);
+}
 
-	if (atomic_test_mask(ZFCP_STATUS_PORT_WKA, &port->status))
-		ZFCP_LOG_NORMAL("port erp failed (adapter %s, "
-				"port d_id=0x%06x)\n",
-				zfcp_get_busid_by_port(port), port->d_id);
+static int zfcp_erp_port_forced_strategy(struct zfcp_erp_action *erp_action)
+{
+	struct zfcp_port *port = erp_action->port;
+	int status = atomic_read(&port->status);
+
+	switch (erp_action->step) {
+	case ZFCP_ERP_STEP_UNINITIALIZED:
+		zfcp_erp_port_strategy_clearstati(port);
+		if ((status & ZFCP_STATUS_PORT_PHYS_OPEN) &&
+		    (status & ZFCP_STATUS_COMMON_OPEN))
+			return zfcp_erp_port_forced_strategy_close(erp_action);
+		else
+			return ZFCP_ERP_FAILED;
+
+	case ZFCP_ERP_STEP_PHYS_PORT_CLOSING:
+		if (status & ZFCP_STATUS_PORT_PHYS_OPEN)
+			return ZFCP_ERP_SUCCEEDED;
+	}
+	return ZFCP_ERP_FAILED;
+}
+
+static int zfcp_erp_port_strategy_close(struct zfcp_erp_action *erp_action)
+{
+	int retval;
+
+	retval = zfcp_fsf_close_port(erp_action);
+	if (retval == -ENOMEM)
+		return ZFCP_ERP_NOMEM;
+	erp_action->step = ZFCP_ERP_STEP_PORT_CLOSING;
+	if (retval)
+		return ZFCP_ERP_FAILED;
+	return ZFCP_ERP_CONTINUES;
+}
+
+static int zfcp_erp_port_strategy_open_port(struct zfcp_erp_action *erp_action)
+{
+	int retval;
+
+	retval = zfcp_fsf_open_port(erp_action);
+	if (retval == -ENOMEM)
+		return ZFCP_ERP_NOMEM;
+	erp_action->step = ZFCP_ERP_STEP_PORT_OPENING;
+	if (retval)
+		return ZFCP_ERP_FAILED;
+	return ZFCP_ERP_CONTINUES;
+}
+
+static void zfcp_erp_port_strategy_open_ns_wake(struct zfcp_erp_action *ns_act)
+{
+	unsigned long flags;
+	struct zfcp_adapter *adapter = ns_act->adapter;
+	struct zfcp_erp_action *act, *tmp;
+	int status;
+
+	read_lock_irqsave(&adapter->erp_lock, flags);
+	list_for_each_entry_safe(act, tmp, &adapter->erp_running_head, list) {
+		if (act->step == ZFCP_ERP_STEP_NAMESERVER_OPEN) {
+			status = atomic_read(&adapter->nameserver_port->status);
+			if (status & ZFCP_STATUS_COMMON_ERP_FAILED)
+				zfcp_erp_port_failed(act->port, 27, NULL);
+			zfcp_erp_action_ready(act);
+		}
+	}
+	read_unlock_irqrestore(&adapter->erp_lock, flags);
+}
+
+static int zfcp_erp_port_strategy_open_nameserver(struct zfcp_erp_action *act)
+{
+	int retval;
+
+	switch (act->step) {
+	case ZFCP_ERP_STEP_UNINITIALIZED:
+	case ZFCP_ERP_STEP_PHYS_PORT_CLOSING:
+	case ZFCP_ERP_STEP_PORT_CLOSING:
+		return zfcp_erp_port_strategy_open_port(act);
+
+	case ZFCP_ERP_STEP_PORT_OPENING:
+		if (atomic_read(&act->port->status) & ZFCP_STATUS_COMMON_OPEN)
+			retval = ZFCP_ERP_SUCCEEDED;
+		else
+			retval = ZFCP_ERP_FAILED;
+		/* this is needed anyway  */
+		zfcp_erp_port_strategy_open_ns_wake(act);
+		return retval;
+
+	default:
+		return ZFCP_ERP_FAILED;
+	}
+}
+
+static int zfcp_erp_port_strategy_open_lookup(struct zfcp_erp_action *act)
+{
+	int retval;
+
+	retval = zfcp_fc_ns_gid_pn_request(act);
+	if (retval == -ENOMEM)
+		return ZFCP_ERP_NOMEM;
+	act->step = ZFCP_ERP_STEP_NAMESERVER_LOOKUP;
+	if (retval)
+		return ZFCP_ERP_FAILED;
+	return ZFCP_ERP_CONTINUES;
+}
+
+static int zfcp_erp_open_ptp_port(struct zfcp_erp_action *act)
+{
+	struct zfcp_adapter *adapter = act->adapter;
+	struct zfcp_port *port = act->port;
+
+	if (port->wwpn != adapter->peer_wwpn) {
+		dev_err(&adapter->ccw_device->dev,
+			"Failed to open port 0x%016Lx, "
+			"Peer WWPN 0x%016Lx does not "
+			"match.\n", port->wwpn,
+			adapter->peer_wwpn);
+		zfcp_erp_port_failed(port, 25, NULL);
+		return ZFCP_ERP_FAILED;
+	}
+	port->d_id = adapter->peer_d_id;
+	atomic_set_mask(ZFCP_STATUS_PORT_DID_DID, &port->status);
+	return zfcp_erp_port_strategy_open_port(act);
+}
+
+static int zfcp_erp_port_strategy_open_common(struct zfcp_erp_action *act)
+{
+	struct zfcp_adapter *adapter = act->adapter;
+	struct zfcp_port *port = act->port;
+	struct zfcp_port *ns_port = adapter->nameserver_port;
+	int p_status = atomic_read(&port->status);
+
+	switch (act->step) {
+	case ZFCP_ERP_STEP_UNINITIALIZED:
+	case ZFCP_ERP_STEP_PHYS_PORT_CLOSING:
+	case ZFCP_ERP_STEP_PORT_CLOSING:
+		if (fc_host_port_type(adapter->scsi_host) == FC_PORTTYPE_PTP)
+			return zfcp_erp_open_ptp_port(act);
+		if (!ns_port) {
+			dev_err(&adapter->ccw_device->dev,
+				"Nameserver port unavailable.\n");
+			return ZFCP_ERP_FAILED;
+		}
+		if (!(atomic_read(&ns_port->status) &
+		      ZFCP_STATUS_COMMON_UNBLOCKED)) {
+			/* nameserver port may live again */
+			atomic_set_mask(ZFCP_STATUS_COMMON_RUNNING,
+					&ns_port->status);
+			if (zfcp_erp_port_reopen(ns_port, 0, 77, act) >= 0) {
+				act->step = ZFCP_ERP_STEP_NAMESERVER_OPEN;
+				return ZFCP_ERP_CONTINUES;
+			}
+			return ZFCP_ERP_FAILED;
+		}
+		/* else nameserver port is already open, fall through */
+	case ZFCP_ERP_STEP_NAMESERVER_OPEN:
+		if (!(atomic_read(&ns_port->status) & ZFCP_STATUS_COMMON_OPEN))
+			return ZFCP_ERP_FAILED;
+		return zfcp_erp_port_strategy_open_lookup(act);
+
+	case ZFCP_ERP_STEP_NAMESERVER_LOOKUP:
+		if (!(p_status & ZFCP_STATUS_PORT_DID_DID)) {
+			if (p_status & (ZFCP_STATUS_PORT_INVALID_WWPN)) {
+				zfcp_erp_port_failed(port, 26, NULL);
+				return ZFCP_ERP_EXIT;
+			}
+			return ZFCP_ERP_FAILED;
+		}
+		return zfcp_erp_port_strategy_open_port(act);
+
+	case ZFCP_ERP_STEP_PORT_OPENING:
+		/* D_ID might have changed during open */
+		if ((p_status & ZFCP_STATUS_COMMON_OPEN) &&
+		    (p_status & ZFCP_STATUS_PORT_DID_DID))
+			return ZFCP_ERP_SUCCEEDED;
+		/* fall through otherwise */
+	}
+	return ZFCP_ERP_FAILED;
+}
+
+static int zfcp_erp_port_strategy_open(struct zfcp_erp_action *act)
+{
+	if (atomic_read(&act->port->status) & (ZFCP_STATUS_PORT_WKA))
+		return zfcp_erp_port_strategy_open_nameserver(act);
+	return zfcp_erp_port_strategy_open_common(act);
+}
+
+static int zfcp_erp_port_strategy(struct zfcp_erp_action *erp_action)
+{
+	struct zfcp_port *port = erp_action->port;
+
+	switch (erp_action->step) {
+	case ZFCP_ERP_STEP_UNINITIALIZED:
+		zfcp_erp_port_strategy_clearstati(port);
+		if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_OPEN)
+			return zfcp_erp_port_strategy_close(erp_action);
+		break;
+
+	case ZFCP_ERP_STEP_PORT_CLOSING:
+		if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_OPEN)
+			return ZFCP_ERP_FAILED;
+		break;
+	}
+	if (erp_action->status & ZFCP_STATUS_ERP_CLOSE_ONLY)
+		return ZFCP_ERP_EXIT;
 	else
-		ZFCP_LOG_NORMAL("port erp failed (adapter %s, wwpn=0x%016Lx)\n",
-				zfcp_get_busid_by_port(port), port->wwpn);
+		return zfcp_erp_port_strategy_open(erp_action);
+
+	return ZFCP_ERP_FAILED;
 }
 
-/*
- * function:    zfcp_erp_unit_failed
- *
- * purpose:     sets the unit to ERP_FAILED
- *
- */
-void
-zfcp_erp_unit_failed(struct zfcp_unit *unit, u8 id, void *ref)
+static void zfcp_erp_unit_strategy_clearstati(struct zfcp_unit *unit)
 {
-	zfcp_erp_modify_unit_status(unit, id, ref,
-				    ZFCP_STATUS_COMMON_ERP_FAILED, ZFCP_SET);
-
-	ZFCP_LOG_NORMAL("unit erp failed on unit 0x%016Lx on port 0x%016Lx "
-			" on adapter %s\n", unit->fcp_lun,
-			unit->port->wwpn, zfcp_get_busid_by_unit(unit));
+	atomic_clear_mask(ZFCP_STATUS_COMMON_OPENING |
+			  ZFCP_STATUS_COMMON_CLOSING |
+			  ZFCP_STATUS_COMMON_ACCESS_DENIED |
+			  ZFCP_STATUS_UNIT_SHARED |
+			  ZFCP_STATUS_UNIT_READONLY,
+			  &unit->status);
 }
 
-/*
- * function:	zfcp_erp_strategy_check_target
- *
- * purpose:	increments the erp action count on the device currently in
- *              recovery if the action failed or resets the count in case of
- *              success. If a maximum count is exceeded the device is marked
- *              as ERP_FAILED.
- *		The 'blocked' state of a target which has been recovered
- *              successfully is reset.
- *
- * returns:	ZFCP_ERP_CONTINUES	- action continues (not considered)
- *		ZFCP_ERP_SUCCEEDED	- action finished successfully
- *		ZFCP_ERP_EXIT		- action failed and will not continue
- */
-static int
-zfcp_erp_strategy_check_target(struct zfcp_erp_action *erp_action, int result)
+static int zfcp_erp_unit_strategy_close(struct zfcp_erp_action *erp_action)
+{
+	int retval = zfcp_fsf_close_unit(erp_action);
+	if (retval == -ENOMEM)
+		return ZFCP_ERP_NOMEM;
+	erp_action->step = ZFCP_ERP_STEP_UNIT_CLOSING;
+	if (retval)
+		return ZFCP_ERP_FAILED;
+	return ZFCP_ERP_CONTINUES;
+}
+
+static int zfcp_erp_unit_strategy_open(struct zfcp_erp_action *erp_action)
+{
+	int retval = zfcp_fsf_open_unit(erp_action);
+	if (retval == -ENOMEM)
+		return ZFCP_ERP_NOMEM;
+	erp_action->step = ZFCP_ERP_STEP_UNIT_OPENING;
+	if (retval)
+		return  ZFCP_ERP_FAILED;
+	return ZFCP_ERP_CONTINUES;
+}
+
+static int zfcp_erp_unit_strategy(struct zfcp_erp_action *erp_action)
+{
+	struct zfcp_unit *unit = erp_action->unit;
+
+	switch (erp_action->step) {
+	case ZFCP_ERP_STEP_UNINITIALIZED:
+		zfcp_erp_unit_strategy_clearstati(unit);
+		if (atomic_read(&unit->status) & ZFCP_STATUS_COMMON_OPEN)
+			return zfcp_erp_unit_strategy_close(erp_action);
+		/* already closed, fall through */
+	case ZFCP_ERP_STEP_UNIT_CLOSING:
+		if (atomic_read(&unit->status) & ZFCP_STATUS_COMMON_OPEN)
+			return ZFCP_ERP_FAILED;
+		if (erp_action->status & ZFCP_STATUS_ERP_CLOSE_ONLY)
+			return ZFCP_ERP_EXIT;
+		return zfcp_erp_unit_strategy_open(erp_action);
+
+	case ZFCP_ERP_STEP_UNIT_OPENING:
+		if (atomic_read(&unit->status) & ZFCP_STATUS_COMMON_OPEN)
+			return ZFCP_ERP_SUCCEEDED;
+	}
+	return ZFCP_ERP_FAILED;
+}
+
+static int zfcp_erp_strategy_check_unit(struct zfcp_unit *unit, int result)
+{
+	switch (result) {
+	case ZFCP_ERP_SUCCEEDED :
+		atomic_set(&unit->erp_counter, 0);
+		zfcp_erp_unit_unblock(unit);
+		break;
+	case ZFCP_ERP_FAILED :
+		atomic_inc(&unit->erp_counter);
+		if (atomic_read(&unit->erp_counter) > ZFCP_MAX_ERPS)
+			zfcp_erp_unit_failed(unit, 21, NULL);
+		break;
+	}
+
+	if (atomic_read(&unit->status) & ZFCP_STATUS_COMMON_ERP_FAILED) {
+		zfcp_erp_unit_block(unit, 0);
+		result = ZFCP_ERP_EXIT;
+	}
+	return result;
+}
+
+static int zfcp_erp_strategy_check_port(struct zfcp_port *port, int result)
+{
+	switch (result) {
+	case ZFCP_ERP_SUCCEEDED :
+		atomic_set(&port->erp_counter, 0);
+		zfcp_erp_port_unblock(port);
+		break;
+
+	case ZFCP_ERP_FAILED :
+		if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_NOESC) {
+			zfcp_erp_port_block(port, 0);
+			result = ZFCP_ERP_EXIT;
+		}
+		atomic_inc(&port->erp_counter);
+		if (atomic_read(&port->erp_counter) > ZFCP_MAX_ERPS)
+			zfcp_erp_port_failed(port, 22, NULL);
+		break;
+	}
+
+	if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_ERP_FAILED) {
+		zfcp_erp_port_block(port, 0);
+		result = ZFCP_ERP_EXIT;
+	}
+	return result;
+}
+
+static int zfcp_erp_strategy_check_adapter(struct zfcp_adapter *adapter,
+					   int result)
+{
+	switch (result) {
+	case ZFCP_ERP_SUCCEEDED :
+		atomic_set(&adapter->erp_counter, 0);
+		zfcp_erp_adapter_unblock(adapter);
+		break;
+
+	case ZFCP_ERP_FAILED :
+		atomic_inc(&adapter->erp_counter);
+		if (atomic_read(&adapter->erp_counter) > ZFCP_MAX_ERPS)
+			zfcp_erp_adapter_failed(adapter, 23, NULL);
+		break;
+	}
+
+	if (atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_ERP_FAILED) {
+		zfcp_erp_adapter_block(adapter, 0);
+		result = ZFCP_ERP_EXIT;
+	}
+	return result;
+}
+
+static int zfcp_erp_strategy_check_target(struct zfcp_erp_action *erp_action,
+					  int result)
 {
 	struct zfcp_adapter *adapter = erp_action->adapter;
 	struct zfcp_port *port = erp_action->port;
@@ -1382,142 +1148,94 @@
 		result = zfcp_erp_strategy_check_adapter(adapter, result);
 		break;
 	}
-
 	return result;
 }
 
-static int
-zfcp_erp_strategy_statechange(int action,
-			      u32 status,
-			      struct zfcp_adapter *adapter,
-			      struct zfcp_port *port,
-			      struct zfcp_unit *unit, int retval)
+static int zfcp_erp_strat_change_det(atomic_t *target_status, u32 erp_status)
 {
-	switch (action) {
+	int status = atomic_read(target_status);
 
+	if ((status & ZFCP_STATUS_COMMON_RUNNING) &&
+	    (erp_status & ZFCP_STATUS_ERP_CLOSE_ONLY))
+		return 1; /* take it online */
+
+	if (!(status & ZFCP_STATUS_COMMON_RUNNING) &&
+	    !(erp_status & ZFCP_STATUS_ERP_CLOSE_ONLY))
+		return 1; /* take it offline */
+
+	return 0;
+}
+
+static int zfcp_erp_strategy_statechange(struct zfcp_erp_action *act, int ret)
+{
+	int action = act->action;
+	struct zfcp_adapter *adapter = act->adapter;
+	struct zfcp_port *port = act->port;
+	struct zfcp_unit *unit = act->unit;
+	u32 erp_status = act->status;
+
+	switch (action) {
 	case ZFCP_ERP_ACTION_REOPEN_ADAPTER:
-		if (zfcp_erp_strategy_statechange_detected(&adapter->status,
-							   status)) {
-			zfcp_erp_adapter_reopen_internal(adapter,
-						ZFCP_STATUS_COMMON_ERP_FAILED,
-						67, NULL);
-			retval = ZFCP_ERP_EXIT;
+		if (zfcp_erp_strat_change_det(&adapter->status, erp_status)) {
+			_zfcp_erp_adapter_reopen(adapter,
+						 ZFCP_STATUS_COMMON_ERP_FAILED,
+						 67, NULL);
+			return ZFCP_ERP_EXIT;
 		}
 		break;
 
 	case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED:
 	case ZFCP_ERP_ACTION_REOPEN_PORT:
-		if (zfcp_erp_strategy_statechange_detected(&port->status,
-							   status)) {
-			zfcp_erp_port_reopen_internal(port,
-						ZFCP_STATUS_COMMON_ERP_FAILED,
-						68, NULL);
-			retval = ZFCP_ERP_EXIT;
+		if (zfcp_erp_strat_change_det(&port->status, erp_status)) {
+			_zfcp_erp_port_reopen(port,
+					      ZFCP_STATUS_COMMON_ERP_FAILED,
+					      68, NULL);
+			return ZFCP_ERP_EXIT;
 		}
 		break;
 
 	case ZFCP_ERP_ACTION_REOPEN_UNIT:
-		if (zfcp_erp_strategy_statechange_detected(&unit->status,
-							   status)) {
-			zfcp_erp_unit_reopen_internal(unit,
-						ZFCP_STATUS_COMMON_ERP_FAILED,
-						69, NULL);
-			retval = ZFCP_ERP_EXIT;
+		if (zfcp_erp_strat_change_det(&unit->status, erp_status)) {
+			_zfcp_erp_unit_reopen(unit,
+					      ZFCP_STATUS_COMMON_ERP_FAILED,
+					      69, NULL);
+			return ZFCP_ERP_EXIT;
 		}
 		break;
 	}
-
-	return retval;
+	return ret;
 }
 
-static int
-zfcp_erp_strategy_statechange_detected(atomic_t * target_status, u32 erp_status)
+static void zfcp_erp_action_dequeue(struct zfcp_erp_action *erp_action)
 {
-	return
-	    /* take it online */
-	    (atomic_test_mask(ZFCP_STATUS_COMMON_RUNNING, target_status) &&
-	     (ZFCP_STATUS_ERP_CLOSE_ONLY & erp_status)) ||
-	    /* take it offline */
-	    (!atomic_test_mask(ZFCP_STATUS_COMMON_RUNNING, target_status) &&
-	     !(ZFCP_STATUS_ERP_CLOSE_ONLY & erp_status));
-}
+	struct zfcp_adapter *adapter = erp_action->adapter;
 
-static int
-zfcp_erp_strategy_check_unit(struct zfcp_unit *unit, int result)
-{
-	switch (result) {
-	case ZFCP_ERP_SUCCEEDED :
-		atomic_set(&unit->erp_counter, 0);
-		zfcp_erp_unit_unblock(unit);
-		break;
-	case ZFCP_ERP_FAILED :
-		atomic_inc(&unit->erp_counter);
-		if (atomic_read(&unit->erp_counter) > ZFCP_MAX_ERPS)
-			zfcp_erp_unit_failed(unit, 21, NULL);
-		break;
-	case ZFCP_ERP_EXIT :
-		/* nothing */
-		break;
+	adapter->erp_total_count--;
+	if (erp_action->status & ZFCP_STATUS_ERP_LOWMEM) {
+		adapter->erp_low_mem_count--;
+		erp_action->status &= ~ZFCP_STATUS_ERP_LOWMEM;
 	}
 
-	if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &unit->status)) {
-		zfcp_erp_unit_block(unit, 0); /* for ZFCP_ERP_SUCCEEDED */
-		result = ZFCP_ERP_EXIT;
-	}
+	list_del(&erp_action->list);
+	zfcp_rec_dbf_event_action(144, erp_action);
 
-	return result;
-}
+	switch (erp_action->action) {
+	case ZFCP_ERP_ACTION_REOPEN_UNIT:
+		atomic_clear_mask(ZFCP_STATUS_COMMON_ERP_INUSE,
+				  &erp_action->unit->status);
+		break;
 
-static int
-zfcp_erp_strategy_check_port(struct zfcp_port *port, int result)
-{
-	switch (result) {
-	case ZFCP_ERP_SUCCEEDED :
-		atomic_set(&port->erp_counter, 0);
-		zfcp_erp_port_unblock(port);
+	case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED:
+	case ZFCP_ERP_ACTION_REOPEN_PORT:
+		atomic_clear_mask(ZFCP_STATUS_COMMON_ERP_INUSE,
+				  &erp_action->port->status);
 		break;
-	case ZFCP_ERP_FAILED :
-		atomic_inc(&port->erp_counter);
-		if (atomic_read(&port->erp_counter) > ZFCP_MAX_ERPS)
-			zfcp_erp_port_failed(port, 22, NULL);
-		break;
-	case ZFCP_ERP_EXIT :
-		/* nothing */
+
+	case ZFCP_ERP_ACTION_REOPEN_ADAPTER:
+		atomic_clear_mask(ZFCP_STATUS_COMMON_ERP_INUSE,
+				  &erp_action->adapter->status);
 		break;
 	}
-
-	if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &port->status)) {
-		zfcp_erp_port_block(port, 0); /* for ZFCP_ERP_SUCCEEDED */
-		result = ZFCP_ERP_EXIT;
-	}
-
-	return result;
-}
-
-static int
-zfcp_erp_strategy_check_adapter(struct zfcp_adapter *adapter, int result)
-{
-	switch (result) {
-	case ZFCP_ERP_SUCCEEDED :
-		atomic_set(&adapter->erp_counter, 0);
-		zfcp_erp_adapter_unblock(adapter);
-		break;
-	case ZFCP_ERP_FAILED :
-		atomic_inc(&adapter->erp_counter);
-		if (atomic_read(&adapter->erp_counter) > ZFCP_MAX_ERPS)
-			zfcp_erp_adapter_failed(adapter, 23, NULL);
-		break;
-	case ZFCP_ERP_EXIT :
-		/* nothing */
-		break;
-	}
-
-	if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &adapter->status)) {
-		zfcp_erp_adapter_block(adapter, 0); /* for ZFCP_ERP_SUCCEEDED */
-		result = ZFCP_ERP_EXIT;
-	}
-
-	return result;
 }
 
 struct zfcp_erp_add_work {
@@ -1525,12 +1243,6 @@
 	struct work_struct work;
 };
 
-/**
- * zfcp_erp_scsi_scan
- * @data: pointer to a struct zfcp_erp_add_work
- *
- * Registers a logical unit with the SCSI stack.
- */
 static void zfcp_erp_scsi_scan(struct work_struct *work)
 {
 	struct zfcp_erp_add_work *p =
@@ -1544,26 +1256,16 @@
 	kfree(p);
 }
 
-/**
- * zfcp_erp_schedule_work
- * @unit: pointer to unit which should be registered with SCSI stack
- *
- * Schedules work which registers a unit with the SCSI stack
- */
-static void
-zfcp_erp_schedule_work(struct zfcp_unit *unit)
+static void zfcp_erp_schedule_work(struct zfcp_unit *unit)
 {
 	struct zfcp_erp_add_work *p;
 
 	p = kzalloc(sizeof(*p), GFP_KERNEL);
 	if (!p) {
-		ZFCP_LOG_NORMAL("error: Out of resources. Could not register "
-				"the FCP-LUN 0x%Lx connected to "
-				"the port with WWPN 0x%Lx connected to "
-				"the adapter %s with the SCSI stack.\n",
-				unit->fcp_lun,
-				unit->port->wwpn,
-				zfcp_get_busid_by_unit(unit));
+		dev_err(&unit->port->adapter->ccw_device->dev,
+			"Out of resources. Could not register unit "
+			"0x%016Lx on port 0x%016Lx with SCSI stack.\n",
+			unit->fcp_lun, unit->port->wwpn);
 		return;
 	}
 
@@ -1574,1486 +1276,406 @@
 	schedule_work(&p->work);
 }
 
-/*
- * function:
- *
- * purpose:	remaining things in good cases,
- *		escalation in bad cases
- *
- * returns:
- */
-static int
-zfcp_erp_strategy_followup_actions(int action,
-				   struct zfcp_adapter *adapter,
-				   struct zfcp_port *port,
-				   struct zfcp_unit *unit, int status)
+static void zfcp_erp_rport_register(struct zfcp_port *port)
 {
-	/* initiate follow-up actions depending on success of finished action */
-	switch (action) {
-
-	case ZFCP_ERP_ACTION_REOPEN_ADAPTER:
-		if (status == ZFCP_ERP_SUCCEEDED)
-			zfcp_erp_port_reopen_all_internal(adapter, 0, 70, NULL);
-		else
-			zfcp_erp_adapter_reopen_internal(adapter, 0, 71, NULL);
-		break;
-
-	case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED:
-		if (status == ZFCP_ERP_SUCCEEDED)
-			zfcp_erp_port_reopen_internal(port, 0, 72, NULL);
-		else
-			zfcp_erp_adapter_reopen_internal(adapter, 0, 73, NULL);
-		break;
-
-	case ZFCP_ERP_ACTION_REOPEN_PORT:
-		if (status == ZFCP_ERP_SUCCEEDED)
-			zfcp_erp_unit_reopen_all_internal(port, 0, 74, NULL);
-		else
-			zfcp_erp_port_forced_reopen_internal(port, 0, 75, NULL);
-		break;
-
-	case ZFCP_ERP_ACTION_REOPEN_UNIT:
-		/* Nothing to do if status == ZFCP_ERP_SUCCEEDED */
-		if (status != ZFCP_ERP_SUCCEEDED)
-			zfcp_erp_port_reopen_internal(unit->port, 0, 76, NULL);
-		break;
+	struct fc_rport_identifiers ids;
+	ids.node_name = port->wwnn;
+	ids.port_name = port->wwpn;
+	ids.port_id = port->d_id;
+	ids.roles = FC_RPORT_ROLE_FCP_TARGET;
+	port->rport = fc_remote_port_add(port->adapter->scsi_host, 0, &ids);
+	if (!port->rport) {
+		dev_err(&port->adapter->ccw_device->dev,
+			"Failed registration of rport "
+			"0x%016Lx.\n", port->wwpn);
+		return;
 	}
 
-	return 0;
+	scsi_target_unblock(&port->rport->dev);
+	port->rport->maxframe_size = port->maxframe_size;
+	port->rport->supported_classes = port->supported_classes;
 }
 
-static int
-zfcp_erp_strategy_check_queues(struct zfcp_adapter *adapter)
-{
-	unsigned long flags;
-
-	read_lock_irqsave(&zfcp_data.config_lock, flags);
-	read_lock(&adapter->erp_lock);
-	if (list_empty(&adapter->erp_ready_head) &&
-	    list_empty(&adapter->erp_running_head)) {
-			atomic_clear_mask(ZFCP_STATUS_ADAPTER_ERP_PENDING,
-					  &adapter->status);
-			wake_up(&adapter->erp_done_wqh);
-	}
-	read_unlock(&adapter->erp_lock);
-	read_unlock_irqrestore(&zfcp_data.config_lock, flags);
-
-	return 0;
-}
-
-/**
- * zfcp_erp_wait - wait for completion of error recovery on an adapter
- * @adapter: adapter for which to wait for completion of its error recovery
- * Return: 0
- */
-int
-zfcp_erp_wait(struct zfcp_adapter *adapter)
-{
-	int retval = 0;
-
-	wait_event(adapter->erp_done_wqh,
-		   !atomic_test_mask(ZFCP_STATUS_ADAPTER_ERP_PENDING,
-				     &adapter->status));
-
-	return retval;
-}
-
-void zfcp_erp_modify_adapter_status(struct zfcp_adapter *adapter, u8 id,
-				    void *ref, u32 mask, int set_or_clear)
+static void zfcp_erp_rports_del(struct zfcp_adapter *adapter)
 {
 	struct zfcp_port *port;
-	u32 changed, common_mask = mask & ZFCP_COMMON_FLAGS;
-
-	if (set_or_clear == ZFCP_SET) {
-		changed = atomic_test_and_set_mask(mask, &adapter->status);
-	} else {
-		changed = atomic_test_and_clear_mask(mask, &adapter->status);
-		if (mask & ZFCP_STATUS_COMMON_ERP_FAILED)
-			atomic_set(&adapter->erp_counter, 0);
-	}
-	if (changed)
-		zfcp_rec_dbf_event_adapter(id, ref, adapter);
-
-	/* Deal with all underlying devices, only pass common_mask */
-	if (common_mask)
-		list_for_each_entry(port, &adapter->port_list_head, list)
-			zfcp_erp_modify_port_status(port, id, ref, common_mask,
-						    set_or_clear);
-}
-
-/*
- * function:	zfcp_erp_modify_port_status
- *
- * purpose:	sets the port and all underlying devices to ERP_FAILED
- *
- */
-void zfcp_erp_modify_port_status(struct zfcp_port *port, u8 id, void *ref,
-				 u32 mask, int set_or_clear)
-{
-	struct zfcp_unit *unit;
-	u32 changed, common_mask = mask & ZFCP_COMMON_FLAGS;
-
-	if (set_or_clear == ZFCP_SET) {
-		changed = atomic_test_and_set_mask(mask, &port->status);
-	} else {
-		changed = atomic_test_and_clear_mask(mask, &port->status);
-		if (mask & ZFCP_STATUS_COMMON_ERP_FAILED)
-			atomic_set(&port->erp_counter, 0);
-	}
-	if (changed)
-		zfcp_rec_dbf_event_port(id, ref, port);
-
-	/* Modify status of all underlying devices, only pass common mask */
-	if (common_mask)
-		list_for_each_entry(unit, &port->unit_list_head, list)
-			zfcp_erp_modify_unit_status(unit, id, ref, common_mask,
-						    set_or_clear);
-}
-
-/*
- * function:	zfcp_erp_modify_unit_status
- *
- * purpose:	sets the unit to ERP_FAILED
- *
- */
-void zfcp_erp_modify_unit_status(struct zfcp_unit *unit, u8 id, void *ref,
-				 u32 mask, int set_or_clear)
-{
-	u32 changed;
-
-	if (set_or_clear == ZFCP_SET) {
-		changed = atomic_test_and_set_mask(mask, &unit->status);
-	} else {
-		changed = atomic_test_and_clear_mask(mask, &unit->status);
-		if (mask & ZFCP_STATUS_COMMON_ERP_FAILED) {
-			atomic_set(&unit->erp_counter, 0);
-		}
-	}
-	if (changed)
-		zfcp_rec_dbf_event_unit(id, ref, unit);
-}
-
-/*
- * function:
- *
- * purpose:	Wrappper for zfcp_erp_port_reopen_all_internal
- *              used to ensure the correct locking
- *
- * returns:	0	- initiated action successfully
- *		<0	- failed to initiate action
- */
-int zfcp_erp_port_reopen_all(struct zfcp_adapter *adapter, int clear_mask,
-			     u8 id, void *ref)
-{
-	int retval;
-	unsigned long flags;
-
-	read_lock_irqsave(&zfcp_data.config_lock, flags);
-	write_lock(&adapter->erp_lock);
-	retval = zfcp_erp_port_reopen_all_internal(adapter, clear_mask, id,
-						   ref);
-	write_unlock(&adapter->erp_lock);
-	read_unlock_irqrestore(&zfcp_data.config_lock, flags);
-
-	return retval;
-}
-
-static int zfcp_erp_port_reopen_all_internal(struct zfcp_adapter *adapter,
-					     int clear_mask, u8 id, void *ref)
-{
-	int retval = 0;
-	struct zfcp_port *port;
-
 	list_for_each_entry(port, &adapter->port_list_head, list)
-		if (!atomic_test_mask(ZFCP_STATUS_PORT_WKA, &port->status))
-			zfcp_erp_port_reopen_internal(port, clear_mask, id,
-						      ref);
-
-	return retval;
-}
-
-/*
- * function:
- *
- * purpose:
- *
- * returns:	FIXME
- */
-static int zfcp_erp_unit_reopen_all_internal(struct zfcp_port *port,
-					     int clear_mask, u8 id, void *ref)
-{
-	int retval = 0;
-	struct zfcp_unit *unit;
-
-	list_for_each_entry(unit, &port->unit_list_head, list)
-		zfcp_erp_unit_reopen_internal(unit, clear_mask, id, ref);
-
-	return retval;
-}
-
-/*
- * function:
- *
- * purpose:	this routine executes the 'Reopen Adapter' action
- *		(the entire action is processed synchronously, since
- *		there are no actions which might be run concurrently
- *		per definition)
- *
- * returns:	ZFCP_ERP_SUCCEEDED	- action finished successfully
- *		ZFCP_ERP_FAILED		- action finished unsuccessfully
- */
-static int
-zfcp_erp_adapter_strategy(struct zfcp_erp_action *erp_action)
-{
-	int retval;
-	struct zfcp_adapter *adapter = erp_action->adapter;
-
-	retval = zfcp_erp_adapter_strategy_close(erp_action);
-	if (erp_action->status & ZFCP_STATUS_ERP_CLOSE_ONLY)
-		retval = ZFCP_ERP_EXIT;
-	else
-		retval = zfcp_erp_adapter_strategy_open(erp_action);
-
-	if (retval == ZFCP_ERP_FAILED) {
-		ZFCP_LOG_INFO("Waiting to allow the adapter %s "
-			      "to recover itself\n",
-			      zfcp_get_busid_by_adapter(adapter));
-		ssleep(ZFCP_TYPE2_RECOVERY_TIME);
-	}
-
-	return retval;
-}
-
-/*
- * function:
- *
- * purpose:
- *
- * returns:	ZFCP_ERP_SUCCEEDED      - action finished successfully
- *              ZFCP_ERP_FAILED         - action finished unsuccessfully
- */
-static int
-zfcp_erp_adapter_strategy_close(struct zfcp_erp_action *erp_action)
-{
-	int retval;
-
-	atomic_set_mask(ZFCP_STATUS_COMMON_CLOSING,
-			&erp_action->adapter->status);
-	retval = zfcp_erp_adapter_strategy_generic(erp_action, 1);
-	atomic_clear_mask(ZFCP_STATUS_COMMON_CLOSING,
-			  &erp_action->adapter->status);
-
-	return retval;
-}
-
-/*
- * function:
- *
- * purpose:
- *
- * returns:	ZFCP_ERP_SUCCEEDED      - action finished successfully
- *              ZFCP_ERP_FAILED         - action finished unsuccessfully
- */
-static int
-zfcp_erp_adapter_strategy_open(struct zfcp_erp_action *erp_action)
-{
-	int retval;
-
-	atomic_set_mask(ZFCP_STATUS_COMMON_OPENING,
-			&erp_action->adapter->status);
-	retval = zfcp_erp_adapter_strategy_generic(erp_action, 0);
-	atomic_clear_mask(ZFCP_STATUS_COMMON_OPENING,
-			  &erp_action->adapter->status);
-
-	return retval;
-}
-
-/*
- * function:    zfcp_register_adapter
- *
- * purpose:	allocate the irq associated with this devno and register
- *		the FSF adapter with the SCSI stack
- *
- * returns:
- */
-static int
-zfcp_erp_adapter_strategy_generic(struct zfcp_erp_action *erp_action, int close)
-{
-	int retval = ZFCP_ERP_SUCCEEDED;
-
-	if (close)
-		goto close_only;
-
-	retval = zfcp_erp_adapter_strategy_open_qdio(erp_action);
-	if (retval != ZFCP_ERP_SUCCEEDED)
-		goto failed_qdio;
-
-	retval = zfcp_erp_adapter_strategy_open_fsf(erp_action);
-	if (retval != ZFCP_ERP_SUCCEEDED)
-		goto failed_openfcp;
-
-	atomic_set_mask(ZFCP_STATUS_COMMON_OPEN, &erp_action->adapter->status);
-	goto out;
-
- close_only:
-	atomic_clear_mask(ZFCP_STATUS_COMMON_OPEN,
-			  &erp_action->adapter->status);
-
- failed_openfcp:
-	zfcp_close_fsf(erp_action->adapter);
- failed_qdio:
-	atomic_clear_mask(ZFCP_STATUS_ADAPTER_XCONFIG_OK |
-			  ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED |
-			  ZFCP_STATUS_ADAPTER_XPORT_OK,
-			  &erp_action->adapter->status);
- out:
-	return retval;
-}
-
-/*
- * function:    zfcp_qdio_init
- *
- * purpose:	setup QDIO operation for specified adapter
- *
- * returns:	0 - successful setup
- *		!0 - failed setup
- */
-static int
-zfcp_erp_adapter_strategy_open_qdio(struct zfcp_erp_action *erp_action)
-{
-	int retval;
-	int i;
-	volatile struct qdio_buffer_element *sbale;
-	struct zfcp_adapter *adapter = erp_action->adapter;
-
-	if (atomic_test_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &adapter->status)) {
-		ZFCP_LOG_NORMAL("bug: second attempt to set up QDIO on "
-				"adapter %s\n",
-				zfcp_get_busid_by_adapter(adapter));
-		goto failed_sanity;
-	}
-
-	if (qdio_establish(&adapter->qdio_init_data) != 0) {
-		ZFCP_LOG_INFO("error: establishment of QDIO queues failed "
-			      "on adapter %s\n",
-			      zfcp_get_busid_by_adapter(adapter));
-		goto failed_qdio_establish;
-	}
-
-	if (qdio_activate(adapter->ccw_device, 0) != 0) {
-		ZFCP_LOG_INFO("error: activation of QDIO queues failed "
-			      "on adapter %s\n",
-			      zfcp_get_busid_by_adapter(adapter));
-		goto failed_qdio_activate;
-	}
-
-	/*
-	 * put buffers into response queue,
-	 */
-	for (i = 0; i < QDIO_MAX_BUFFERS_PER_Q; i++) {
-		sbale = &(adapter->response_queue.buffer[i]->element[0]);
-		sbale->length = 0;
-		sbale->flags = SBAL_FLAGS_LAST_ENTRY;
-		sbale->addr = NULL;
-	}
-
-	ZFCP_LOG_TRACE("calling do_QDIO on adapter %s (flags=0x%x, "
-		       "queue_no=%i, index_in_queue=%i, count=%i)\n",
-		       zfcp_get_busid_by_adapter(adapter),
-		       QDIO_FLAG_SYNC_INPUT, 0, 0, QDIO_MAX_BUFFERS_PER_Q);
-
-	retval = do_QDIO(adapter->ccw_device,
-			 QDIO_FLAG_SYNC_INPUT,
-			 0, 0, QDIO_MAX_BUFFERS_PER_Q, NULL);
-
-	if (retval) {
-		ZFCP_LOG_NORMAL("bug: setup of QDIO failed (retval=%d)\n",
-				retval);
-		goto failed_do_qdio;
-	} else {
-		adapter->response_queue.free_index = 0;
-		atomic_set(&adapter->response_queue.free_count, 0);
-		ZFCP_LOG_DEBUG("%i buffers successfully enqueued to "
-			       "response queue\n", QDIO_MAX_BUFFERS_PER_Q);
-	}
-	/* set index of first avalable SBALS / number of available SBALS */
-	adapter->request_queue.free_index = 0;
-	atomic_set(&adapter->request_queue.free_count, QDIO_MAX_BUFFERS_PER_Q);
-	adapter->request_queue.distance_from_int = 0;
-
-	/* initialize waitqueue used to wait for free SBALs in requests queue */
-	init_waitqueue_head(&adapter->request_wq);
-
-	/* ok, we did it - skip all cleanups for different failures */
-	atomic_set_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &adapter->status);
-	retval = ZFCP_ERP_SUCCEEDED;
-	goto out;
-
- failed_do_qdio:
-	/* NOP */
-
- failed_qdio_activate:
-	while (qdio_shutdown(adapter->ccw_device,
-			     QDIO_FLAG_CLEANUP_USING_CLEAR) == -EINPROGRESS)
-		ssleep(1);
-
- failed_qdio_establish:
- failed_sanity:
-	retval = ZFCP_ERP_FAILED;
-
- out:
-	return retval;
-}
-
-
-static int
-zfcp_erp_adapter_strategy_open_fsf(struct zfcp_erp_action *erp_action)
-{
-	int retval;
-
-	retval = zfcp_erp_adapter_strategy_open_fsf_xconfig(erp_action);
-	if (retval == ZFCP_ERP_FAILED)
-		return ZFCP_ERP_FAILED;
-
-	retval = zfcp_erp_adapter_strategy_open_fsf_xport(erp_action);
-	if (retval == ZFCP_ERP_FAILED)
-		return ZFCP_ERP_FAILED;
-
-	return zfcp_erp_adapter_strategy_open_fsf_statusread(erp_action);
-}
-
-static int
-zfcp_erp_adapter_strategy_open_fsf_xconfig(struct zfcp_erp_action *erp_action)
-{
-	int retval = ZFCP_ERP_SUCCEEDED;
-	int retries;
-	int sleep = ZFCP_EXCHANGE_CONFIG_DATA_FIRST_SLEEP;
-	struct zfcp_adapter *adapter = erp_action->adapter;
-
-	atomic_clear_mask(ZFCP_STATUS_ADAPTER_XCONFIG_OK, &adapter->status);
-
-	for (retries = ZFCP_EXCHANGE_CONFIG_DATA_RETRIES; retries; retries--) {
-		atomic_clear_mask(ZFCP_STATUS_ADAPTER_HOST_CON_INIT,
-				  &adapter->status);
-		ZFCP_LOG_DEBUG("Doing exchange config data\n");
-		write_lock_irq(&adapter->erp_lock);
-		zfcp_erp_action_to_running(erp_action);
-		write_unlock_irq(&adapter->erp_lock);
-		if (zfcp_fsf_exchange_config_data(erp_action)) {
-			retval = ZFCP_ERP_FAILED;
-			ZFCP_LOG_INFO("error:  initiation of exchange of "
-				      "configuration data failed for "
-				      "adapter %s\n",
-				      zfcp_get_busid_by_adapter(adapter));
-			break;
+		if (port->rport && !(atomic_read(&port->status) &
+					ZFCP_STATUS_PORT_WKA)) {
+			fc_remote_port_delete(port->rport);
+			port->rport = NULL;
 		}
-		ZFCP_LOG_DEBUG("Xchange underway\n");
-
-		/*
-		 * Why this works:
-		 * Both the normal completion handler as well as the timeout
-		 * handler will do an 'up' when the 'exchange config data'
-		 * request completes or times out. Thus, the signal to go on
-		 * won't be lost utilizing this semaphore.
-		 * Furthermore, this 'adapter_reopen' action is
-		 * guaranteed to be the only action being there (highest action
-		 * which prevents other actions from being created).
-		 * Resulting from that, the wake signal recognized here
-		 * _must_ be the one belonging to the 'exchange config
-		 * data' request.
-		 */
-		zfcp_rec_dbf_event_thread(6, adapter, 1);
-		down(&adapter->erp_ready_sem);
-		zfcp_rec_dbf_event_thread(7, adapter, 1);
-		if (erp_action->status & ZFCP_STATUS_ERP_TIMEDOUT) {
-			ZFCP_LOG_INFO("error: exchange of configuration data "
-				      "for adapter %s timed out\n",
-				      zfcp_get_busid_by_adapter(adapter));
-			break;
-		}
-
-		if (!atomic_test_mask(ZFCP_STATUS_ADAPTER_HOST_CON_INIT,
-				     &adapter->status))
-			break;
-
-		ZFCP_LOG_DEBUG("host connection still initialising... "
-			       "waiting and retrying...\n");
-		/* sleep a little bit before retry */
-		ssleep(sleep);
-		sleep *= 2;
-	}
-
-	atomic_clear_mask(ZFCP_STATUS_ADAPTER_HOST_CON_INIT,
-			  &adapter->status);
-
-	if (!atomic_test_mask(ZFCP_STATUS_ADAPTER_XCONFIG_OK,
-			      &adapter->status)) {
-		ZFCP_LOG_INFO("error: exchange of configuration data for "
-			      "adapter %s failed\n",
-			      zfcp_get_busid_by_adapter(adapter));
-		retval = ZFCP_ERP_FAILED;
-	}
-
-	return retval;
 }
 
-static int
-zfcp_erp_adapter_strategy_open_fsf_xport(struct zfcp_erp_action *erp_action)
+static void zfcp_erp_action_cleanup(struct zfcp_erp_action *act, int result)
 {
-	int ret;
-	struct zfcp_adapter *adapter;
+	struct zfcp_adapter *adapter = act->adapter;
+	struct zfcp_port *port = act->port;
+	struct zfcp_unit *unit = act->unit;
 
-	adapter = erp_action->adapter;
-	atomic_clear_mask(ZFCP_STATUS_ADAPTER_XPORT_OK, &adapter->status);
-
-	write_lock_irq(&adapter->erp_lock);
-	zfcp_erp_action_to_running(erp_action);
-	write_unlock_irq(&adapter->erp_lock);
-
-	ret = zfcp_fsf_exchange_port_data(erp_action);
-	if (ret == -EOPNOTSUPP) {
-		return ZFCP_ERP_SUCCEEDED;
-	} else if (ret) {
-		return ZFCP_ERP_FAILED;
-	}
-
-	ret = ZFCP_ERP_SUCCEEDED;
-	zfcp_rec_dbf_event_thread(8, adapter, 1);
-	down(&adapter->erp_ready_sem);
-	zfcp_rec_dbf_event_thread(9, adapter, 1);
-	if (erp_action->status & ZFCP_STATUS_ERP_TIMEDOUT) {
-		ZFCP_LOG_INFO("error: exchange port data timed out (adapter "
-			      "%s)\n", zfcp_get_busid_by_adapter(adapter));
-		ret = ZFCP_ERP_FAILED;
-	}
-
-	/* don't treat as error for the sake of compatibility */
-	if (!atomic_test_mask(ZFCP_STATUS_ADAPTER_XPORT_OK, &adapter->status))
-		ZFCP_LOG_INFO("warning: exchange port data failed (adapter "
-			      "%s\n", zfcp_get_busid_by_adapter(adapter));
-
-	return ret;
-}
-
-static int
-zfcp_erp_adapter_strategy_open_fsf_statusread(struct zfcp_erp_action
-					      *erp_action)
-{
-	int retval = ZFCP_ERP_SUCCEEDED;
-	int temp_ret;
-	struct zfcp_adapter *adapter = erp_action->adapter;
-	int i;
-
-	adapter->status_read_failed = 0;
-	for (i = 0; i < ZFCP_STATUS_READS_RECOM; i++) {
-		temp_ret = zfcp_fsf_status_read(adapter, ZFCP_WAIT_FOR_SBAL);
-		if (temp_ret < 0) {
-			ZFCP_LOG_INFO("error: set-up of unsolicited status "
-				      "notification failed on adapter %s\n",
-				      zfcp_get_busid_by_adapter(adapter));
-			retval = ZFCP_ERP_FAILED;
-			i--;
-			break;
-		}
-	}
-
-	return retval;
-}
-
-/*
- * function:
- *
- * purpose:	this routine executes the 'Reopen Physical Port' action
- *
- * returns:	ZFCP_ERP_CONTINUES	- action continues (asynchronously)
- *		ZFCP_ERP_SUCCEEDED	- action finished successfully
- *		ZFCP_ERP_FAILED		- action finished unsuccessfully
- */
-static int
-zfcp_erp_port_forced_strategy(struct zfcp_erp_action *erp_action)
-{
-	int retval = ZFCP_ERP_FAILED;
-	struct zfcp_port *port = erp_action->port;
-
-	switch (erp_action->step) {
-
-		/*
-		 * FIXME:
-		 * the ULP spec. begs for waiting for oustanding commands
-		 */
-	case ZFCP_ERP_STEP_UNINITIALIZED:
-		zfcp_erp_port_strategy_clearstati(port);
-		/*
-		 * it would be sufficient to test only the normal open flag
-		 * since the phys. open flag cannot be set if the normal
-		 * open flag is unset - however, this is for readabilty ...
-		 */
-		if (atomic_test_mask((ZFCP_STATUS_PORT_PHYS_OPEN |
-				      ZFCP_STATUS_COMMON_OPEN),
-			             &port->status)) {
-			ZFCP_LOG_DEBUG("port 0x%016Lx is open -> trying "
-				       "close physical\n", port->wwpn);
-			retval =
-			    zfcp_erp_port_forced_strategy_close(erp_action);
-		} else
-			retval = ZFCP_ERP_FAILED;
-		break;
-
-	case ZFCP_ERP_STEP_PHYS_PORT_CLOSING:
-		if (atomic_test_mask(ZFCP_STATUS_PORT_PHYS_OPEN,
-				     &port->status)) {
-			ZFCP_LOG_DEBUG("close physical failed for port "
-				       "0x%016Lx\n", port->wwpn);
-			retval = ZFCP_ERP_FAILED;
-		} else
-			retval = ZFCP_ERP_SUCCEEDED;
-		break;
-	}
-
-	return retval;
-}
-
-/*
- * function:
- *
- * purpose:	this routine executes the 'Reopen Port' action
- *
- * returns:	ZFCP_ERP_CONTINUES	- action continues (asynchronously)
- *		ZFCP_ERP_SUCCEEDED	- action finished successfully
- *		ZFCP_ERP_FAILED		- action finished unsuccessfully
- */
-static int
-zfcp_erp_port_strategy(struct zfcp_erp_action *erp_action)
-{
-	int retval = ZFCP_ERP_FAILED;
-	struct zfcp_port *port = erp_action->port;
-
-	switch (erp_action->step) {
-
-		/*
-		 * FIXME:
-		 * the ULP spec. begs for waiting for oustanding commands
-		 */
-	case ZFCP_ERP_STEP_UNINITIALIZED:
-		zfcp_erp_port_strategy_clearstati(port);
-		if (atomic_test_mask(ZFCP_STATUS_COMMON_OPEN, &port->status)) {
-			ZFCP_LOG_DEBUG("port 0x%016Lx is open -> trying "
-				       "close\n", port->wwpn);
-			retval = zfcp_erp_port_strategy_close(erp_action);
-			goto out;
-		}		/* else it's already closed, open it */
-		break;
-
-	case ZFCP_ERP_STEP_PORT_CLOSING:
-		if (atomic_test_mask(ZFCP_STATUS_COMMON_OPEN, &port->status)) {
-			ZFCP_LOG_DEBUG("close failed for port 0x%016Lx\n",
-				       port->wwpn);
-			retval = ZFCP_ERP_FAILED;
-			goto out;
-		}		/* else it's closed now, open it */
-		break;
-	}
-	if (erp_action->status & ZFCP_STATUS_ERP_CLOSE_ONLY)
-		retval = ZFCP_ERP_EXIT;
-	else
-		retval = zfcp_erp_port_strategy_open(erp_action);
-
- out:
-	return retval;
-}
-
-static int
-zfcp_erp_port_strategy_open(struct zfcp_erp_action *erp_action)
-{
-	int retval;
-
-	if (atomic_test_mask(ZFCP_STATUS_PORT_WKA,
-			     &erp_action->port->status))
-		retval = zfcp_erp_port_strategy_open_nameserver(erp_action);
-	else
-		retval = zfcp_erp_port_strategy_open_common(erp_action);
-
-	return retval;
-}
-
-static int
-zfcp_erp_port_strategy_open_common(struct zfcp_erp_action *erp_action)
-{
-	int retval = 0;
-	struct zfcp_adapter *adapter = erp_action->adapter;
-	struct zfcp_port *port = erp_action->port;
-
-	switch (erp_action->step) {
-
-	case ZFCP_ERP_STEP_UNINITIALIZED:
-	case ZFCP_ERP_STEP_PHYS_PORT_CLOSING:
-	case ZFCP_ERP_STEP_PORT_CLOSING:
-		if (fc_host_port_type(adapter->scsi_host) == FC_PORTTYPE_PTP) {
-			if (port->wwpn != adapter->peer_wwpn) {
-				ZFCP_LOG_NORMAL("Failed to open port 0x%016Lx "
-						"on adapter %s.\nPeer WWPN "
-						"0x%016Lx does not match\n",
-						port->wwpn,
-						zfcp_get_busid_by_adapter(adapter),
-						adapter->peer_wwpn);
-				zfcp_erp_port_failed(port, 25, NULL);
-				retval = ZFCP_ERP_FAILED;
-				break;
-			}
-			port->d_id = adapter->peer_d_id;
-			atomic_set_mask(ZFCP_STATUS_PORT_DID_DID, &port->status);
-			retval = zfcp_erp_port_strategy_open_port(erp_action);
-			break;
-		}
-		if (!(adapter->nameserver_port)) {
-			retval = zfcp_nameserver_enqueue(adapter);
-			if (retval != 0) {
-				ZFCP_LOG_NORMAL("error: nameserver port "
-						"unavailable for adapter %s\n",
-						zfcp_get_busid_by_adapter(adapter));
-				retval = ZFCP_ERP_FAILED;
-				break;
-			}
-		}
-		if (!atomic_test_mask(ZFCP_STATUS_COMMON_UNBLOCKED,
-				      &adapter->nameserver_port->status)) {
-			ZFCP_LOG_DEBUG("nameserver port is not open -> open "
-				       "nameserver port\n");
-			/* nameserver port may live again */
-			atomic_set_mask(ZFCP_STATUS_COMMON_RUNNING,
-					&adapter->nameserver_port->status);
-			if (zfcp_erp_port_reopen(adapter->nameserver_port, 0,
-						 77, erp_action) >= 0) {
-				erp_action->step =
-					ZFCP_ERP_STEP_NAMESERVER_OPEN;
-				retval = ZFCP_ERP_CONTINUES;
-			} else
-				retval = ZFCP_ERP_FAILED;
-			break;
-		}
-		/* else nameserver port is already open, fall through */
-	case ZFCP_ERP_STEP_NAMESERVER_OPEN:
-		if (!atomic_test_mask(ZFCP_STATUS_COMMON_OPEN,
-				      &adapter->nameserver_port->status)) {
-			ZFCP_LOG_DEBUG("open failed for nameserver port\n");
-			retval = ZFCP_ERP_FAILED;
-		} else {
-			ZFCP_LOG_DEBUG("nameserver port is open -> "
-				       "nameserver look-up for port 0x%016Lx\n",
-				       port->wwpn);
-			retval = zfcp_erp_port_strategy_open_common_lookup
-				(erp_action);
-		}
-		break;
-
-	case ZFCP_ERP_STEP_NAMESERVER_LOOKUP:
-		if (!atomic_test_mask(ZFCP_STATUS_PORT_DID_DID, &port->status)) {
-			if (atomic_test_mask
-			    (ZFCP_STATUS_PORT_INVALID_WWPN, &port->status)) {
-				ZFCP_LOG_DEBUG("nameserver look-up failed "
-					       "for port 0x%016Lx "
-					       "(misconfigured WWPN?)\n",
-					       port->wwpn);
-				zfcp_erp_port_failed(port, 26, NULL);
-				retval = ZFCP_ERP_EXIT;
-			} else {
-				ZFCP_LOG_DEBUG("nameserver look-up failed for "
-					       "port 0x%016Lx\n", port->wwpn);
-				retval = ZFCP_ERP_FAILED;
-			}
-		} else {
-			ZFCP_LOG_DEBUG("port 0x%016Lx has d_id=0x%06x -> "
-				       "trying open\n", port->wwpn, port->d_id);
-			retval = zfcp_erp_port_strategy_open_port(erp_action);
-		}
-		break;
-
-	case ZFCP_ERP_STEP_PORT_OPENING:
-		/* D_ID might have changed during open */
-		if (atomic_test_mask((ZFCP_STATUS_COMMON_OPEN |
-				      ZFCP_STATUS_PORT_DID_DID),
-				     &port->status)) {
-			ZFCP_LOG_DEBUG("port 0x%016Lx is open\n", port->wwpn);
-			retval = ZFCP_ERP_SUCCEEDED;
-		} else {
-			ZFCP_LOG_DEBUG("open failed for port 0x%016Lx\n",
-				       port->wwpn);
-			retval = ZFCP_ERP_FAILED;
-		}
-		break;
-
-	default:
-		ZFCP_LOG_NORMAL("bug: unknown erp step 0x%08x\n",
-				erp_action->step);
-		retval = ZFCP_ERP_FAILED;
-	}
-
-	return retval;
-}
-
-static int
-zfcp_erp_port_strategy_open_nameserver(struct zfcp_erp_action *erp_action)
-{
-	int retval;
-	struct zfcp_port *port = erp_action->port;
-
-	switch (erp_action->step) {
-
-	case ZFCP_ERP_STEP_UNINITIALIZED:
-	case ZFCP_ERP_STEP_PHYS_PORT_CLOSING:
-	case ZFCP_ERP_STEP_PORT_CLOSING:
-		ZFCP_LOG_DEBUG("port 0x%016Lx has d_id=0x%06x -> trying open\n",
-			       port->wwpn, port->d_id);
-		retval = zfcp_erp_port_strategy_open_port(erp_action);
-		break;
-
-	case ZFCP_ERP_STEP_PORT_OPENING:
-		if (atomic_test_mask(ZFCP_STATUS_COMMON_OPEN, &port->status)) {
-			ZFCP_LOG_DEBUG("WKA port is open\n");
-			retval = ZFCP_ERP_SUCCEEDED;
-		} else {
-			ZFCP_LOG_DEBUG("open failed for WKA port\n");
-			retval = ZFCP_ERP_FAILED;
-		}
-		/* this is needed anyway (dont care for retval of wakeup) */
-		ZFCP_LOG_DEBUG("continue other open port operations\n");
-		zfcp_erp_port_strategy_open_nameserver_wakeup(erp_action);
-		break;
-
-	default:
-		ZFCP_LOG_NORMAL("bug: unknown erp step 0x%08x\n",
-				erp_action->step);
-		retval = ZFCP_ERP_FAILED;
-	}
-
-	return retval;
-}
-
-/*
- * function:
- *
- * purpose:	makes the erp thread continue with reopen (physical) port
- *		actions which have been paused until the name server port
- *		is opened (or failed)
- *
- * returns:	0	(a kind of void retval, its not used)
- */
-static int
-zfcp_erp_port_strategy_open_nameserver_wakeup(struct zfcp_erp_action
-					      *ns_erp_action)
-{
-	int retval = 0;
-	unsigned long flags;
-	struct zfcp_adapter *adapter = ns_erp_action->adapter;
-	struct zfcp_erp_action *erp_action, *tmp;
-
-	read_lock_irqsave(&adapter->erp_lock, flags);
-	list_for_each_entry_safe(erp_action, tmp, &adapter->erp_running_head,
-				 list) {
-		if (erp_action->step == ZFCP_ERP_STEP_NAMESERVER_OPEN) {
-			if (atomic_test_mask(
-				    ZFCP_STATUS_COMMON_ERP_FAILED,
-				    &adapter->nameserver_port->status))
-				zfcp_erp_port_failed(erp_action->port, 27,
-						     NULL);
-			zfcp_erp_action_ready(erp_action);
-		}
-	}
-	read_unlock_irqrestore(&adapter->erp_lock, flags);
-
-	return retval;
-}
-
-/*
- * function:
- *
- * purpose:
- *
- * returns:	ZFCP_ERP_CONTINUES	- action continues (asynchronously)
- *		ZFCP_ERP_FAILED		- action finished unsuccessfully
- */
-static int
-zfcp_erp_port_forced_strategy_close(struct zfcp_erp_action *erp_action)
-{
-	int retval;
-
-	retval = zfcp_fsf_close_physical_port(erp_action);
-	if (retval == -ENOMEM) {
-		retval = ZFCP_ERP_NOMEM;
-		goto out;
-	}
-	erp_action->step = ZFCP_ERP_STEP_PHYS_PORT_CLOSING;
-	if (retval != 0) {
-		/* could not send 'open', fail */
-		retval = ZFCP_ERP_FAILED;
-		goto out;
-	}
-	retval = ZFCP_ERP_CONTINUES;
- out:
-	return retval;
-}
-
-static int
-zfcp_erp_port_strategy_clearstati(struct zfcp_port *port)
-{
-	int retval = 0;
-
-	atomic_clear_mask(ZFCP_STATUS_COMMON_OPENING |
-			  ZFCP_STATUS_COMMON_CLOSING |
-			  ZFCP_STATUS_COMMON_ACCESS_DENIED |
-			  ZFCP_STATUS_PORT_DID_DID |
-			  ZFCP_STATUS_PORT_PHYS_CLOSING |
-			  ZFCP_STATUS_PORT_INVALID_WWPN,
-			  &port->status);
-	return retval;
-}
-
-/*
- * function:
- *
- * purpose:
- *
- * returns:	ZFCP_ERP_CONTINUES	- action continues (asynchronously)
- *		ZFCP_ERP_FAILED		- action finished unsuccessfully
- */
-static int
-zfcp_erp_port_strategy_close(struct zfcp_erp_action *erp_action)
-{
-	int retval;
-
-	retval = zfcp_fsf_close_port(erp_action);
-	if (retval == -ENOMEM) {
-		retval = ZFCP_ERP_NOMEM;
-		goto out;
-	}
-	erp_action->step = ZFCP_ERP_STEP_PORT_CLOSING;
-	if (retval != 0) {
-		/* could not send 'close', fail */
-		retval = ZFCP_ERP_FAILED;
-		goto out;
-	}
-	retval = ZFCP_ERP_CONTINUES;
- out:
-	return retval;
-}
-
-/*
- * function:
- *
- * purpose:
- *
- * returns:	ZFCP_ERP_CONTINUES	- action continues (asynchronously)
- *		ZFCP_ERP_FAILED		- action finished unsuccessfully
- */
-static int
-zfcp_erp_port_strategy_open_port(struct zfcp_erp_action *erp_action)
-{
-	int retval;
-
-	retval = zfcp_fsf_open_port(erp_action);
-	if (retval == -ENOMEM) {
-		retval = ZFCP_ERP_NOMEM;
-		goto out;
-	}
-	erp_action->step = ZFCP_ERP_STEP_PORT_OPENING;
-	if (retval != 0) {
-		/* could not send 'open', fail */
-		retval = ZFCP_ERP_FAILED;
-		goto out;
-	}
-	retval = ZFCP_ERP_CONTINUES;
- out:
-	return retval;
-}
-
-/*
- * function:
- *
- * purpose:
- *
- * returns:	ZFCP_ERP_CONTINUES	- action continues (asynchronously)
- *		ZFCP_ERP_FAILED		- action finished unsuccessfully
- */
-static int
-zfcp_erp_port_strategy_open_common_lookup(struct zfcp_erp_action *erp_action)
-{
-	int retval;
-
-	retval = zfcp_ns_gid_pn_request(erp_action);
-	if (retval == -ENOMEM) {
-		retval = ZFCP_ERP_NOMEM;
-		goto out;
-	}
-	erp_action->step = ZFCP_ERP_STEP_NAMESERVER_LOOKUP;
-	if (retval != 0) {
-		/* could not send nameserver request, fail */
-		retval = ZFCP_ERP_FAILED;
-		goto out;
-	}
-	retval = ZFCP_ERP_CONTINUES;
- out:
-	return retval;
-}
-
-/*
- * function:
- *
- * purpose:	this routine executes the 'Reopen Unit' action
- *		currently no retries
- *
- * returns:	ZFCP_ERP_CONTINUES	- action continues (asynchronously)
- *		ZFCP_ERP_SUCCEEDED	- action finished successfully
- *		ZFCP_ERP_FAILED		- action finished unsuccessfully
- */
-static int
-zfcp_erp_unit_strategy(struct zfcp_erp_action *erp_action)
-{
-	int retval = ZFCP_ERP_FAILED;
-	struct zfcp_unit *unit = erp_action->unit;
-
-	switch (erp_action->step) {
-
-		/*
-		 * FIXME:
-		 * the ULP spec. begs for waiting for oustanding commands
-		 */
-	case ZFCP_ERP_STEP_UNINITIALIZED:
-		zfcp_erp_unit_strategy_clearstati(unit);
-		if (atomic_test_mask(ZFCP_STATUS_COMMON_OPEN, &unit->status)) {
-			ZFCP_LOG_DEBUG("unit 0x%016Lx is open -> "
-				       "trying close\n", unit->fcp_lun);
-			retval = zfcp_erp_unit_strategy_close(erp_action);
-			break;
-		}
-		/* else it's already closed, fall through */
-	case ZFCP_ERP_STEP_UNIT_CLOSING:
-		if (atomic_test_mask(ZFCP_STATUS_COMMON_OPEN, &unit->status)) {
-			ZFCP_LOG_DEBUG("close failed for unit 0x%016Lx\n",
-				       unit->fcp_lun);
-			retval = ZFCP_ERP_FAILED;
-		} else {
-			if (erp_action->status & ZFCP_STATUS_ERP_CLOSE_ONLY)
-				retval = ZFCP_ERP_EXIT;
-			else {
-				ZFCP_LOG_DEBUG("unit 0x%016Lx is not open -> "
-					       "trying open\n", unit->fcp_lun);
-				retval =
-				    zfcp_erp_unit_strategy_open(erp_action);
-			}
-		}
-		break;
-
-	case ZFCP_ERP_STEP_UNIT_OPENING:
-		if (atomic_test_mask(ZFCP_STATUS_COMMON_OPEN, &unit->status)) {
-			ZFCP_LOG_DEBUG("unit 0x%016Lx is open\n",
-				       unit->fcp_lun);
-			retval = ZFCP_ERP_SUCCEEDED;
-		} else {
-			ZFCP_LOG_DEBUG("open failed for unit 0x%016Lx\n",
-				       unit->fcp_lun);
-			retval = ZFCP_ERP_FAILED;
-		}
-		break;
-	}
-
-	return retval;
-}
-
-static int
-zfcp_erp_unit_strategy_clearstati(struct zfcp_unit *unit)
-{
-	int retval = 0;
-
-	atomic_clear_mask(ZFCP_STATUS_COMMON_OPENING |
-			  ZFCP_STATUS_COMMON_CLOSING |
-			  ZFCP_STATUS_COMMON_ACCESS_DENIED |
-			  ZFCP_STATUS_UNIT_SHARED |
-			  ZFCP_STATUS_UNIT_READONLY,
-			  &unit->status);
-
-	return retval;
-}
-
-/*
- * function:
- *
- * purpose:
- *
- * returns:	ZFCP_ERP_CONTINUES	- action continues (asynchronously)
- *		ZFCP_ERP_FAILED		- action finished unsuccessfully
- */
-static int
-zfcp_erp_unit_strategy_close(struct zfcp_erp_action *erp_action)
-{
-	int retval;
-
-	retval = zfcp_fsf_close_unit(erp_action);
-	if (retval == -ENOMEM) {
-		retval = ZFCP_ERP_NOMEM;
-		goto out;
-	}
-	erp_action->step = ZFCP_ERP_STEP_UNIT_CLOSING;
-	if (retval != 0) {
-		/* could not send 'close', fail */
-		retval = ZFCP_ERP_FAILED;
-		goto out;
-	}
-	retval = ZFCP_ERP_CONTINUES;
-
- out:
-	return retval;
-}
-
-/*
- * function:
- *
- * purpose:
- *
- * returns:	ZFCP_ERP_CONTINUES	- action continues (asynchronously)
- *		ZFCP_ERP_FAILED		- action finished unsuccessfully
- */
-static int
-zfcp_erp_unit_strategy_open(struct zfcp_erp_action *erp_action)
-{
-	int retval;
-
-	retval = zfcp_fsf_open_unit(erp_action);
-	if (retval == -ENOMEM) {
-		retval = ZFCP_ERP_NOMEM;
-		goto out;
-	}
-	erp_action->step = ZFCP_ERP_STEP_UNIT_OPENING;
-	if (retval != 0) {
-		/* could not send 'open', fail */
-		retval = ZFCP_ERP_FAILED;
-		goto out;
-	}
-	retval = ZFCP_ERP_CONTINUES;
- out:
-	return retval;
-}
-
-void zfcp_erp_start_timer(struct zfcp_fsf_req *fsf_req)
-{
-	BUG_ON(!fsf_req->erp_action);
-	fsf_req->timer.function = zfcp_erp_timeout_handler;
-	fsf_req->timer.data = (unsigned long) fsf_req->erp_action;
-	fsf_req->timer.expires = jiffies + ZFCP_ERP_FSFREQ_TIMEOUT;
-	add_timer(&fsf_req->timer);
-}
-
-/*
- * function:
- *
- * purpose:	enqueue the specified error recovery action, if needed
- *
- * returns:
- */
-static int zfcp_erp_action_enqueue(int want, struct zfcp_adapter *adapter,
-				   struct zfcp_port *port,
-				   struct zfcp_unit *unit, u8 id, void *ref)
-{
-	int retval = 1, need = want;
-	struct zfcp_erp_action *erp_action = NULL;
-	u32 status = 0;
-
-	/*
-	 * We need some rules here which check whether we really need
-	 * this action or whether we should just drop it.
-	 * E.g. if there is a unfinished 'Reopen Port' request then we drop a
-	 * 'Reopen Unit' request for an associated unit since we can't
-	 * satisfy this request now. A 'Reopen Port' action will trigger
-	 * 'Reopen Unit' actions when it completes.
-	 * Thus, there are only actions in the queue which can immediately be
-	 * executed. This makes the processing of the action queue more
-	 * efficient.
-	 */
-
-	if (!atomic_test_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_UP,
-			      &adapter->status))
-		return -EIO;
-
-	/* check whether we really need this */
-	switch (want) {
+	switch (act->action) {
 	case ZFCP_ERP_ACTION_REOPEN_UNIT:
-		if (atomic_test_mask
-		    (ZFCP_STATUS_COMMON_ERP_INUSE, &unit->status)) {
-			goto out;
-		}
-		if (!atomic_test_mask
-		    (ZFCP_STATUS_COMMON_RUNNING, &port->status) ||
-		    atomic_test_mask
-		    (ZFCP_STATUS_COMMON_ERP_FAILED, &port->status)) {
-			goto out;
-		}
-		if (!atomic_test_mask
-		    (ZFCP_STATUS_COMMON_UNBLOCKED, &port->status))
-			need = ZFCP_ERP_ACTION_REOPEN_PORT;
-		/* fall through !!! */
-
-	case ZFCP_ERP_ACTION_REOPEN_PORT:
-		if (atomic_test_mask
-		    (ZFCP_STATUS_COMMON_ERP_INUSE, &port->status)) {
-			goto out;
-		}
-		/* fall through !!! */
-
-	case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED:
-		if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_INUSE,
-				     &port->status)) {
-			if (port->erp_action.action !=
-			    ZFCP_ERP_ACTION_REOPEN_PORT_FORCED) {
-				ZFCP_LOG_INFO("dropped erp action %i (port "
-					      "0x%016Lx, action in use: %i)\n",
-					      want, port->wwpn,
-					      port->erp_action.action);
-			}
-			goto out;
-		}
-		if (!atomic_test_mask
-		    (ZFCP_STATUS_COMMON_RUNNING, &adapter->status) ||
-		    atomic_test_mask
-		    (ZFCP_STATUS_COMMON_ERP_FAILED, &adapter->status)) {
-			goto out;
-		}
-		if (!atomic_test_mask
-		    (ZFCP_STATUS_COMMON_UNBLOCKED, &adapter->status))
-			need = ZFCP_ERP_ACTION_REOPEN_ADAPTER;
-		/* fall through !!! */
-
-	case ZFCP_ERP_ACTION_REOPEN_ADAPTER:
-		if (atomic_test_mask
-		    (ZFCP_STATUS_COMMON_ERP_INUSE, &adapter->status)) {
-			goto out;
-		}
-		break;
-
-	default:
-		ZFCP_LOG_NORMAL("bug: unknown erp action requested "
-				"on adapter %s (action=%d)\n",
-				zfcp_get_busid_by_adapter(adapter), want);
-		goto out;
-	}
-
-	/* check whether we need something stronger first */
-	if (need) {
-		ZFCP_LOG_DEBUG("stronger erp action %d needed before "
-			       "erp action %d on adapter %s\n",
-			       need, want, zfcp_get_busid_by_adapter(adapter));
-	}
-
-	/* mark adapter to have some error recovery pending */
-	atomic_set_mask(ZFCP_STATUS_ADAPTER_ERP_PENDING, &adapter->status);
-
-	/* setup error recovery action */
-	switch (need) {
-
-	case ZFCP_ERP_ACTION_REOPEN_UNIT:
-		zfcp_unit_get(unit);
-		atomic_set_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &unit->status);
-		erp_action = &unit->erp_action;
-		if (!atomic_test_mask
-		    (ZFCP_STATUS_COMMON_RUNNING, &unit->status))
-			status = ZFCP_STATUS_ERP_CLOSE_ONLY;
-		break;
-
-	case ZFCP_ERP_ACTION_REOPEN_PORT:
-	case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED:
-		zfcp_port_get(port);
-		zfcp_erp_action_dismiss_port(port);
-		atomic_set_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &port->status);
-		erp_action = &port->erp_action;
-		if (!atomic_test_mask
-		    (ZFCP_STATUS_COMMON_RUNNING, &port->status))
-			status = ZFCP_STATUS_ERP_CLOSE_ONLY;
-		break;
-
-	case ZFCP_ERP_ACTION_REOPEN_ADAPTER:
-		zfcp_adapter_get(adapter);
-		zfcp_erp_action_dismiss_adapter(adapter);
-		atomic_set_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &adapter->status);
-		erp_action = &adapter->erp_action;
-		if (!atomic_test_mask
-		    (ZFCP_STATUS_COMMON_RUNNING, &adapter->status))
-			status = ZFCP_STATUS_ERP_CLOSE_ONLY;
-		break;
-	}
-
-	memset(erp_action, 0, sizeof (struct zfcp_erp_action));
-	erp_action->adapter = adapter;
-	erp_action->port = port;
-	erp_action->unit = unit;
-	erp_action->action = need;
-	erp_action->status = status;
-
-	++adapter->erp_total_count;
-
-	/* finally put it into 'ready' queue and kick erp thread */
-	list_add_tail(&erp_action->list, &adapter->erp_ready_head);
-	up(&adapter->erp_ready_sem);
-	zfcp_rec_dbf_event_thread(1, adapter, 0);
-	retval = 0;
- out:
-	zfcp_rec_dbf_event_trigger(id, ref, want, need, erp_action,
-				   adapter, port, unit);
-	return retval;
-}
-
-static int
-zfcp_erp_action_dequeue(struct zfcp_erp_action *erp_action)
-{
-	int retval = 0;
-	struct zfcp_adapter *adapter = erp_action->adapter;
-
-	--adapter->erp_total_count;
-	if (erp_action->status & ZFCP_STATUS_ERP_LOWMEM) {
-		--adapter->erp_low_mem_count;
-		erp_action->status &= ~ZFCP_STATUS_ERP_LOWMEM;
-	}
-
-	list_del(&erp_action->list);
-	zfcp_rec_dbf_event_action(144, erp_action);
-
-	switch (erp_action->action) {
-	case ZFCP_ERP_ACTION_REOPEN_UNIT:
-		atomic_clear_mask(ZFCP_STATUS_COMMON_ERP_INUSE,
-				  &erp_action->unit->status);
-		break;
-	case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED:
-	case ZFCP_ERP_ACTION_REOPEN_PORT:
-		atomic_clear_mask(ZFCP_STATUS_COMMON_ERP_INUSE,
-				  &erp_action->port->status);
-		break;
-	case ZFCP_ERP_ACTION_REOPEN_ADAPTER:
-		atomic_clear_mask(ZFCP_STATUS_COMMON_ERP_INUSE,
-				  &erp_action->adapter->status);
-		break;
-	default:
-		/* bug */
-		break;
-	}
-	return retval;
-}
-
-/**
- * zfcp_erp_action_cleanup
- *
- * Register unit with scsi stack if appropriate and fix reference counts.
- * Note: Temporary units are not registered with scsi stack.
- */
-static void
-zfcp_erp_action_cleanup(int action, struct zfcp_adapter *adapter,
-			struct zfcp_port *port, struct zfcp_unit *unit,
-			int result)
-{
-	switch (action) {
-	case ZFCP_ERP_ACTION_REOPEN_UNIT:
-		if ((result == ZFCP_ERP_SUCCEEDED)
-		    && (!atomic_test_mask(ZFCP_STATUS_UNIT_TEMPORARY,
-					  &unit->status))
-		    && !unit->device
-		    && port->rport) {
+		if ((result == ZFCP_ERP_SUCCEEDED) &&
+		    !unit->device && port->rport) {
 			atomic_set_mask(ZFCP_STATUS_UNIT_REGISTERED,
 					&unit->status);
-			if (atomic_test_mask(ZFCP_STATUS_UNIT_SCSI_WORK_PENDING,
-					     &unit->status) == 0)
+			if (!(atomic_read(&unit->status) &
+			      ZFCP_STATUS_UNIT_SCSI_WORK_PENDING))
 				zfcp_erp_schedule_work(unit);
 		}
 		zfcp_unit_put(unit);
 		break;
+
 	case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED:
 	case ZFCP_ERP_ACTION_REOPEN_PORT:
-		if (atomic_test_mask(ZFCP_STATUS_PORT_NO_WWPN,
-				     &port->status)) {
+		if (atomic_read(&port->status) & ZFCP_STATUS_PORT_NO_WWPN) {
 			zfcp_port_put(port);
-			break;
+			return;
 		}
-
-		if ((result == ZFCP_ERP_SUCCEEDED)
-		    && !port->rport) {
-			struct fc_rport_identifiers ids;
-			ids.node_name = port->wwnn;
-			ids.port_name = port->wwpn;
-			ids.port_id = port->d_id;
-			ids.roles = FC_RPORT_ROLE_FCP_TARGET;
-			port->rport =
-				fc_remote_port_add(adapter->scsi_host, 0, &ids);
-			if (!port->rport)
-				ZFCP_LOG_NORMAL("failed registration of rport"
-						"(adapter %s, wwpn=0x%016Lx)\n",
-						zfcp_get_busid_by_port(port),
-						port->wwpn);
-			else {
-				scsi_target_unblock(&port->rport->dev);
-				port->rport->maxframe_size = port->maxframe_size;
-				port->rport->supported_classes =
-					port->supported_classes;
-			}
-		}
+		if ((result == ZFCP_ERP_SUCCEEDED) && !port->rport)
+			zfcp_erp_rport_register(port);
 		if ((result != ZFCP_ERP_SUCCEEDED) && port->rport) {
 			fc_remote_port_delete(port->rport);
 			port->rport = NULL;
 		}
 		zfcp_port_put(port);
 		break;
+
 	case ZFCP_ERP_ACTION_REOPEN_ADAPTER:
-		if (result != ZFCP_ERP_SUCCEEDED) {
-			list_for_each_entry(port, &adapter->port_list_head, list)
-				if (port->rport &&
-				    !atomic_test_mask(ZFCP_STATUS_PORT_WKA,
-						      &port->status)) {
-					fc_remote_port_delete(port->rport);
-					port->rport = NULL;
-				}
-		}
+		if (result != ZFCP_ERP_SUCCEEDED)
+			zfcp_erp_rports_del(adapter);
 		zfcp_adapter_put(adapter);
 		break;
-	default:
-		break;
 	}
 }
 
+static int zfcp_erp_strategy_do_action(struct zfcp_erp_action *erp_action)
+{
+	switch (erp_action->action) {
+	case ZFCP_ERP_ACTION_REOPEN_ADAPTER:
+		return zfcp_erp_adapter_strategy(erp_action);
+	case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED:
+		return zfcp_erp_port_forced_strategy(erp_action);
+	case ZFCP_ERP_ACTION_REOPEN_PORT:
+		return zfcp_erp_port_strategy(erp_action);
+	case ZFCP_ERP_ACTION_REOPEN_UNIT:
+		return zfcp_erp_unit_strategy(erp_action);
+	}
+	return ZFCP_ERP_FAILED;
+}
 
-static void zfcp_erp_action_dismiss_adapter(struct zfcp_adapter *adapter)
+static int zfcp_erp_strategy(struct zfcp_erp_action *erp_action)
+{
+	int retval;
+	struct zfcp_adapter *adapter = erp_action->adapter;
+	unsigned long flags;
+
+	read_lock_irqsave(&zfcp_data.config_lock, flags);
+	write_lock(&adapter->erp_lock);
+
+	zfcp_erp_strategy_check_fsfreq(erp_action);
+
+	if (erp_action->status & ZFCP_STATUS_ERP_DISMISSED) {
+		zfcp_erp_action_dequeue(erp_action);
+		retval = ZFCP_ERP_DISMISSED;
+		goto unlock;
+	}
+
+	zfcp_erp_action_to_running(erp_action);
+
+	/* no lock to allow for blocking operations */
+	write_unlock(&adapter->erp_lock);
+	read_unlock_irqrestore(&zfcp_data.config_lock, flags);
+	retval = zfcp_erp_strategy_do_action(erp_action);
+	read_lock_irqsave(&zfcp_data.config_lock, flags);
+	write_lock(&adapter->erp_lock);
+
+	if (erp_action->status & ZFCP_STATUS_ERP_DISMISSED)
+		retval = ZFCP_ERP_CONTINUES;
+
+	switch (retval) {
+	case ZFCP_ERP_NOMEM:
+		if (!(erp_action->status & ZFCP_STATUS_ERP_LOWMEM)) {
+			++adapter->erp_low_mem_count;
+			erp_action->status |= ZFCP_STATUS_ERP_LOWMEM;
+		}
+		if (adapter->erp_total_count == adapter->erp_low_mem_count)
+			_zfcp_erp_adapter_reopen(adapter, 0, 66, NULL);
+		else {
+			zfcp_erp_strategy_memwait(erp_action);
+			retval = ZFCP_ERP_CONTINUES;
+		}
+		goto unlock;
+
+	case ZFCP_ERP_CONTINUES:
+		if (erp_action->status & ZFCP_STATUS_ERP_LOWMEM) {
+			--adapter->erp_low_mem_count;
+			erp_action->status &= ~ZFCP_STATUS_ERP_LOWMEM;
+		}
+		goto unlock;
+	}
+
+	retval = zfcp_erp_strategy_check_target(erp_action, retval);
+	zfcp_erp_action_dequeue(erp_action);
+	retval = zfcp_erp_strategy_statechange(erp_action, retval);
+	if (retval == ZFCP_ERP_EXIT)
+		goto unlock;
+	zfcp_erp_strategy_followup_actions(erp_action);
+
+ unlock:
+	write_unlock(&adapter->erp_lock);
+	read_unlock_irqrestore(&zfcp_data.config_lock, flags);
+
+	if (retval != ZFCP_ERP_CONTINUES)
+		zfcp_erp_action_cleanup(erp_action, retval);
+
+	return retval;
+}
+
+static int zfcp_erp_thread(void *data)
+{
+	struct zfcp_adapter *adapter = (struct zfcp_adapter *) data;
+	struct list_head *next;
+	struct zfcp_erp_action *act;
+	unsigned long flags;
+
+	daemonize("zfcperp%s", adapter->ccw_device->dev.bus_id);
+	/* Block all signals */
+	siginitsetinv(&current->blocked, 0);
+	atomic_set_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_UP, &adapter->status);
+	wake_up(&adapter->erp_thread_wqh);
+
+	while (!(atomic_read(&adapter->status) &
+		 ZFCP_STATUS_ADAPTER_ERP_THREAD_KILL)) {
+		write_lock_irqsave(&adapter->erp_lock, flags);
+		next = adapter->erp_ready_head.next;
+		write_unlock_irqrestore(&adapter->erp_lock, flags);
+
+		if (next != &adapter->erp_ready_head) {
+			act = list_entry(next, struct zfcp_erp_action, list);
+
+			/* there is more to come after dismission, no notify */
+			if (zfcp_erp_strategy(act) != ZFCP_ERP_DISMISSED)
+				zfcp_erp_wakeup(adapter);
+		}
+
+		zfcp_rec_dbf_event_thread(4, adapter);
+		down_interruptible(&adapter->erp_ready_sem);
+		zfcp_rec_dbf_event_thread(5, adapter);
+	}
+
+	atomic_clear_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_UP, &adapter->status);
+	wake_up(&adapter->erp_thread_wqh);
+
+	return 0;
+}
+
+/**
+ * zfcp_erp_thread_setup - Start ERP thread for adapter
+ * @adapter: Adapter to start the ERP thread for
+ *
+ * Returns 0 on success or error code from kernel_thread()
+ */
+int zfcp_erp_thread_setup(struct zfcp_adapter *adapter)
+{
+	int retval;
+
+	atomic_clear_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_UP, &adapter->status);
+	retval = kernel_thread(zfcp_erp_thread, adapter, SIGCHLD);
+	if (retval < 0) {
+		dev_err(&adapter->ccw_device->dev,
+			"Creation of ERP thread failed.\n");
+		return retval;
+	}
+	wait_event(adapter->erp_thread_wqh,
+		   atomic_read(&adapter->status) &
+			ZFCP_STATUS_ADAPTER_ERP_THREAD_UP);
+	return 0;
+}
+
+/**
+ * zfcp_erp_thread_kill - Stop ERP thread.
+ * @adapter: Adapter where the ERP thread should be stopped.
+ *
+ * The caller of this routine ensures that the specified adapter has
+ * been shut down and that this operation has been completed. Thus,
+ * there are no pending erp_actions which would need to be handled
+ * here.
+ */
+void zfcp_erp_thread_kill(struct zfcp_adapter *adapter)
+{
+	atomic_set_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_KILL, &adapter->status);
+	up(&adapter->erp_ready_sem);
+	zfcp_rec_dbf_event_thread_lock(2, adapter);
+
+	wait_event(adapter->erp_thread_wqh,
+		   !(atomic_read(&adapter->status) &
+				ZFCP_STATUS_ADAPTER_ERP_THREAD_UP));
+
+	atomic_clear_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_KILL,
+			  &adapter->status);
+}
+
+/**
+ * zfcp_erp_adapter_failed - Set adapter status to failed.
+ * @adapter: Failed adapter.
+ * @id: Event id for debug trace.
+ * @ref: Reference for debug trace.
+ */
+void zfcp_erp_adapter_failed(struct zfcp_adapter *adapter, u8 id, void *ref)
+{
+	zfcp_erp_modify_adapter_status(adapter, id, ref,
+				       ZFCP_STATUS_COMMON_ERP_FAILED, ZFCP_SET);
+	dev_err(&adapter->ccw_device->dev, "Adapter ERP failed.\n");
+}
+
+/**
+ * zfcp_erp_port_failed - Set port status to failed.
+ * @port: Failed port.
+ * @id: Event id for debug trace.
+ * @ref: Reference for debug trace.
+ */
+void zfcp_erp_port_failed(struct zfcp_port *port, u8 id, void *ref)
+{
+	zfcp_erp_modify_port_status(port, id, ref,
+				    ZFCP_STATUS_COMMON_ERP_FAILED, ZFCP_SET);
+
+	if (atomic_read(&port->status) & ZFCP_STATUS_PORT_WKA)
+		dev_err(&port->adapter->ccw_device->dev,
+			"Port ERP failed for WKA port d_id=0x%06x.\n",
+			port->d_id);
+	else
+		dev_err(&port->adapter->ccw_device->dev,
+			"Port ERP failed for port wwpn=0x%016Lx.\n",
+			port->wwpn);
+}
+
+/**
+ * zfcp_erp_unit_failed - Set unit status to failed.
+ * @unit: Failed unit.
+ * @id: Event id for debug trace.
+ * @ref: Reference for debug trace.
+ */
+void zfcp_erp_unit_failed(struct zfcp_unit *unit, u8 id, void *ref)
+{
+	zfcp_erp_modify_unit_status(unit, id, ref,
+				    ZFCP_STATUS_COMMON_ERP_FAILED, ZFCP_SET);
+
+	dev_err(&unit->port->adapter->ccw_device->dev,
+		"Unit ERP failed for unit 0x%016Lx on port 0x%016Lx.\n",
+		unit->fcp_lun, unit->port->wwpn);
+}
+
+/**
+ * zfcp_erp_wait - wait for completion of error recovery on an adapter
+ * @adapter: adapter for which to wait for completion of its error recovery
+ */
+void zfcp_erp_wait(struct zfcp_adapter *adapter)
+{
+	wait_event(adapter->erp_done_wqh,
+		   !(atomic_read(&adapter->status) &
+			ZFCP_STATUS_ADAPTER_ERP_PENDING));
+}
+
+/**
+ * zfcp_erp_modify_adapter_status - change adapter status bits
+ * @adapter: adapter to change the status
+ * @id: id for the debug trace
+ * @ref: reference for the debug trace
+ * @mask: status bits to change
+ * @set_or_clear: ZFCP_SET or ZFCP_CLEAR
+ *
+ * Changes in common status bits are propagated to attached ports and units.
+ */
+void zfcp_erp_modify_adapter_status(struct zfcp_adapter *adapter, u8 id,
+				    void *ref, u32 mask, int set_or_clear)
 {
 	struct zfcp_port *port;
+	u32 common_mask = mask & ZFCP_COMMON_FLAGS;
 
-	if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &adapter->status))
-		zfcp_erp_action_dismiss(&adapter->erp_action);
-	else
+	if (set_or_clear == ZFCP_SET) {
+		if (status_change_set(mask, &adapter->status))
+			zfcp_rec_dbf_event_adapter(id, ref, adapter);
+		atomic_set_mask(mask, &adapter->status);
+	} else {
+		if (status_change_clear(mask, &adapter->status))
+			zfcp_rec_dbf_event_adapter(id, ref, adapter);
+		atomic_clear_mask(mask, &adapter->status);
+		if (mask & ZFCP_STATUS_COMMON_ERP_FAILED)
+			atomic_set(&adapter->erp_counter, 0);
+	}
+
+	if (common_mask)
 		list_for_each_entry(port, &adapter->port_list_head, list)
-		    zfcp_erp_action_dismiss_port(port);
+			zfcp_erp_modify_port_status(port, id, ref, common_mask,
+						    set_or_clear);
 }
 
-static void zfcp_erp_action_dismiss_port(struct zfcp_port *port)
+/**
+ * zfcp_erp_modify_port_status - change port status bits
+ * @port: port to change the status bits
+ * @id: id for the debug trace
+ * @ref: reference for the debug trace
+ * @mask: status bits to change
+ * @set_or_clear: ZFCP_SET or ZFCP_CLEAR
+ *
+ * Changes in common status bits are propagated to attached units.
+ */
+void zfcp_erp_modify_port_status(struct zfcp_port *port, u8 id, void *ref,
+				 u32 mask, int set_or_clear)
 {
 	struct zfcp_unit *unit;
+	u32 common_mask = mask & ZFCP_COMMON_FLAGS;
 
-	if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &port->status))
-		zfcp_erp_action_dismiss(&port->erp_action);
-	else
+	if (set_or_clear == ZFCP_SET) {
+		if (status_change_set(mask, &port->status))
+			zfcp_rec_dbf_event_port(id, ref, port);
+		atomic_set_mask(mask, &port->status);
+	} else {
+		if (status_change_clear(mask, &port->status))
+			zfcp_rec_dbf_event_port(id, ref, port);
+		atomic_clear_mask(mask, &port->status);
+		if (mask & ZFCP_STATUS_COMMON_ERP_FAILED)
+			atomic_set(&port->erp_counter, 0);
+	}
+
+	if (common_mask)
 		list_for_each_entry(unit, &port->unit_list_head, list)
-		    zfcp_erp_action_dismiss_unit(unit);
+			zfcp_erp_modify_unit_status(unit, id, ref, common_mask,
+						    set_or_clear);
 }
 
-static void zfcp_erp_action_dismiss_unit(struct zfcp_unit *unit)
+/**
+ * zfcp_erp_modify_unit_status - change unit status bits
+ * @unit: unit to change the status bits
+ * @id: id for the debug trace
+ * @ref: reference for the debug trace
+ * @mask: status bits to change
+ * @set_or_clear: ZFCP_SET or ZFCP_CLEAR
+ */
+void zfcp_erp_modify_unit_status(struct zfcp_unit *unit, u8 id, void *ref,
+				 u32 mask, int set_or_clear)
 {
-	if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &unit->status))
-		zfcp_erp_action_dismiss(&unit->erp_action);
+	if (set_or_clear == ZFCP_SET) {
+		if (status_change_set(mask, &unit->status))
+			zfcp_rec_dbf_event_unit(id, ref, unit);
+		atomic_set_mask(mask, &unit->status);
+	} else {
+		if (status_change_clear(mask, &unit->status))
+			zfcp_rec_dbf_event_unit(id, ref, unit);
+		atomic_clear_mask(mask, &unit->status);
+		if (mask & ZFCP_STATUS_COMMON_ERP_FAILED) {
+			atomic_set(&unit->erp_counter, 0);
+		}
+	}
 }
 
-static void zfcp_erp_action_to_running(struct zfcp_erp_action *erp_action)
-{
-	list_move(&erp_action->list, &erp_action->adapter->erp_running_head);
-	zfcp_rec_dbf_event_action(145, erp_action);
-}
-
-static void zfcp_erp_action_to_ready(struct zfcp_erp_action *erp_action)
-{
-	list_move(&erp_action->list, &erp_action->adapter->erp_ready_head);
-	zfcp_rec_dbf_event_action(146, erp_action);
-}
-
+/**
+ * zfcp_erp_port_boxed - Mark port as "boxed" and start ERP
+ * @port: The "boxed" port.
+ * @id: The debug trace id.
+ * @id: Reference for the debug trace.
+ */
 void zfcp_erp_port_boxed(struct zfcp_port *port, u8 id, void *ref)
 {
 	unsigned long flags;
@@ -3065,6 +1687,12 @@
 	zfcp_erp_port_reopen(port, ZFCP_STATUS_COMMON_ERP_FAILED, id, ref);
 }
 
+/**
+ * zfcp_erp_unit_boxed - Mark unit as "boxed" and start ERP
+ * @port: The "boxed" unit.
+ * @id: The debug trace id.
+ * @id: Reference for the debug trace.
+ */
 void zfcp_erp_unit_boxed(struct zfcp_unit *unit, u8 id, void *ref)
 {
 	zfcp_erp_modify_unit_status(unit, id, ref,
@@ -3072,6 +1700,15 @@
 	zfcp_erp_unit_reopen(unit, ZFCP_STATUS_COMMON_ERP_FAILED, id, ref);
 }
 
+/**
+ * zfcp_erp_port_access_denied - Adapter denied access to port.
+ * @port: port where access has been denied
+ * @id: id for debug trace
+ * @ref: reference for debug trace
+ *
+ * Since the adapter has denied access, stop using the port and the
+ * attached units.
+ */
 void zfcp_erp_port_access_denied(struct zfcp_port *port, u8 id, void *ref)
 {
 	unsigned long flags;
@@ -3083,6 +1720,14 @@
 	read_unlock_irqrestore(&zfcp_data.config_lock, flags);
 }
 
+/**
+ * zfcp_erp_unit_access_denied - Adapter denied access to unit.
+ * @unit: unit where access has been denied
+ * @id: id for debug trace
+ * @ref: reference for debug trace
+ *
+ * Since the adapter has denied access, stop using the unit.
+ */
 void zfcp_erp_unit_access_denied(struct zfcp_unit *unit, u8 id, void *ref)
 {
 	zfcp_erp_modify_unit_status(unit, id, ref,
@@ -3090,6 +1735,40 @@
 				    ZFCP_STATUS_COMMON_ACCESS_DENIED, ZFCP_SET);
 }
 
+static void zfcp_erp_unit_access_changed(struct zfcp_unit *unit, u8 id,
+					 void *ref)
+{
+	int status = atomic_read(&unit->status);
+	if (!(status & (ZFCP_STATUS_COMMON_ACCESS_DENIED |
+			ZFCP_STATUS_COMMON_ACCESS_BOXED)))
+		return;
+
+	zfcp_erp_unit_reopen(unit, ZFCP_STATUS_COMMON_ERP_FAILED, id, ref);
+}
+
+static void zfcp_erp_port_access_changed(struct zfcp_port *port, u8 id,
+					 void *ref)
+{
+	struct zfcp_unit *unit;
+	int status = atomic_read(&port->status);
+
+	if (!(status & (ZFCP_STATUS_COMMON_ACCESS_DENIED |
+			ZFCP_STATUS_COMMON_ACCESS_BOXED))) {
+		if (!(status & ZFCP_STATUS_PORT_WKA))
+			list_for_each_entry(unit, &port->unit_list_head, list)
+				zfcp_erp_unit_access_changed(unit, id, ref);
+		return;
+	}
+
+	zfcp_erp_port_reopen(port, ZFCP_STATUS_COMMON_ERP_FAILED, id, ref);
+}
+
+/**
+ * zfcp_erp_adapter_access_changed - Process change in adapter ACT
+ * @adapter: Adapter where the Access Control Table (ACT) changed
+ * @id: Id for debug trace
+ * @ref: Reference for debug trace
+ */
 void zfcp_erp_adapter_access_changed(struct zfcp_adapter *adapter, u8 id,
 				     void *ref)
 {
@@ -3107,50 +1786,3 @@
 			zfcp_erp_port_access_changed(port, id, ref);
 	read_unlock_irqrestore(&zfcp_data.config_lock, flags);
 }
-
-void zfcp_erp_port_access_changed(struct zfcp_port *port, u8 id, void *ref)
-{
-	struct zfcp_adapter *adapter = port->adapter;
-	struct zfcp_unit *unit;
-
-	if (!atomic_test_mask(ZFCP_STATUS_COMMON_ACCESS_DENIED,
-			      &port->status) &&
-	    !atomic_test_mask(ZFCP_STATUS_COMMON_ACCESS_BOXED,
-			      &port->status)) {
-		if (!atomic_test_mask(ZFCP_STATUS_PORT_WKA, &port->status))
-			list_for_each_entry(unit, &port->unit_list_head, list)
-				zfcp_erp_unit_access_changed(unit, id, ref);
-		return;
-	}
-
-	ZFCP_LOG_NORMAL("reopen of port 0x%016Lx on adapter %s "
-			"(due to ACT update)\n",
-			port->wwpn, zfcp_get_busid_by_adapter(adapter));
-	if (zfcp_erp_port_reopen(port, ZFCP_STATUS_COMMON_ERP_FAILED, id, ref))
-		ZFCP_LOG_NORMAL("failed reopen of port"
-				"(adapter %s, wwpn=0x%016Lx)\n",
-				zfcp_get_busid_by_adapter(adapter), port->wwpn);
-}
-
-void zfcp_erp_unit_access_changed(struct zfcp_unit *unit, u8 id, void *ref)
-{
-	struct zfcp_adapter *adapter = unit->port->adapter;
-
-	if (!atomic_test_mask(ZFCP_STATUS_COMMON_ACCESS_DENIED,
-			      &unit->status) &&
-	    !atomic_test_mask(ZFCP_STATUS_COMMON_ACCESS_BOXED,
-			      &unit->status))
-		return;
-
-	ZFCP_LOG_NORMAL("reopen of unit 0x%016Lx on port 0x%016Lx "
-			" on adapter %s (due to ACT update)\n",
-			unit->fcp_lun, unit->port->wwpn,
-			zfcp_get_busid_by_adapter(adapter));
-	if (zfcp_erp_unit_reopen(unit, ZFCP_STATUS_COMMON_ERP_FAILED, id, ref))
-		ZFCP_LOG_NORMAL("failed reopen of unit (adapter %s, "
-				"wwpn=0x%016Lx, fcp_lun=0x%016Lx)\n",
-				zfcp_get_busid_by_adapter(adapter),
-				unit->port->wwpn, unit->fcp_lun);
-}
-
-#undef ZFCP_LOG_AREA
diff --git a/drivers/s390/scsi/zfcp_ext.h b/drivers/s390/scsi/zfcp_ext.h
index 6abf178..8065b2b 100644
--- a/drivers/s390/scsi/zfcp_ext.h
+++ b/drivers/s390/scsi/zfcp_ext.h
@@ -1,22 +1,9 @@
 /*
- * This file is part of the zfcp device driver for
- * FCP adapters for IBM System z9 and zSeries.
+ * zfcp device driver
  *
- * (C) Copyright IBM Corp. 2002, 2006
+ * External function declarations.
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * Copyright IBM Corporation 2002, 2008
  */
 
 #ifndef ZFCP_EXT_H
@@ -24,172 +11,51 @@
 
 #include "zfcp_def.h"
 
-extern struct zfcp_data zfcp_data;
-
-/******************************** SYSFS  *************************************/
-extern struct attribute_group *zfcp_driver_attr_groups[];
-extern int  zfcp_sysfs_adapter_create_files(struct device *);
-extern void zfcp_sysfs_adapter_remove_files(struct device *);
-extern int  zfcp_sysfs_port_create_files(struct device *, u32);
-extern void zfcp_sysfs_port_remove_files(struct device *, u32);
-extern int  zfcp_sysfs_unit_create_files(struct device *);
-extern void zfcp_sysfs_unit_remove_files(struct device *);
-extern void zfcp_sysfs_port_release(struct device *);
-extern void zfcp_sysfs_unit_release(struct device *);
-
-/**************************** CONFIGURATION  *********************************/
-extern struct zfcp_unit *zfcp_get_unit_by_lun(struct zfcp_port *, fcp_lun_t);
-extern struct zfcp_port *zfcp_get_port_by_wwpn(struct zfcp_adapter *, wwn_t);
-extern struct zfcp_port *zfcp_get_port_by_did(struct zfcp_adapter *, u32);
-struct zfcp_adapter *zfcp_get_adapter_by_busid(char *);
-extern struct zfcp_adapter *zfcp_adapter_enqueue(struct ccw_device *);
-extern int    zfcp_adapter_debug_register(struct zfcp_adapter *);
-extern void   zfcp_adapter_dequeue(struct zfcp_adapter *);
-extern void   zfcp_adapter_debug_unregister(struct zfcp_adapter *);
-extern struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *, wwn_t,
-					   u32, u32);
-extern void   zfcp_port_dequeue(struct zfcp_port *);
+/* zfcp_aux.c */
+extern struct zfcp_unit *zfcp_get_unit_by_lun(struct zfcp_port *,
+					      fcp_lun_t);
+extern struct zfcp_port *zfcp_get_port_by_wwpn(struct zfcp_adapter *,
+					       wwn_t);
+extern int zfcp_adapter_enqueue(struct ccw_device *);
+extern void zfcp_adapter_dequeue(struct zfcp_adapter *);
+extern struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *, wwn_t, u32,
+					   u32);
+extern void zfcp_port_dequeue(struct zfcp_port *);
 extern struct zfcp_unit *zfcp_unit_enqueue(struct zfcp_port *, fcp_lun_t);
-extern void   zfcp_unit_dequeue(struct zfcp_unit *);
+extern void zfcp_unit_dequeue(struct zfcp_unit *);
+extern int zfcp_reqlist_isempty(struct zfcp_adapter *);
+extern void zfcp_sg_free_table(struct scatterlist *, int);
+extern int zfcp_sg_setup_table(struct scatterlist *, int);
 
-/******************************* S/390 IO ************************************/
-extern int  zfcp_ccw_register(void);
+/* zfcp_ccw.c */
+extern int zfcp_ccw_register(void);
 
-extern void zfcp_qdio_zero_sbals(struct qdio_buffer **, int, int);
-extern int  zfcp_qdio_allocate(struct zfcp_adapter *);
-extern int  zfcp_qdio_allocate_queues(struct zfcp_adapter *);
-extern void zfcp_qdio_free_queues(struct zfcp_adapter *);
-extern int  zfcp_qdio_determine_pci(struct zfcp_qdio_queue *,
-				    struct zfcp_fsf_req *);
+/* zfcp_cfdc.c */
+extern struct miscdevice zfcp_cfdc_misc;
 
-extern volatile struct qdio_buffer_element *zfcp_qdio_sbale_req
-	(struct zfcp_fsf_req *, int, int);
-extern volatile struct qdio_buffer_element *zfcp_qdio_sbale_curr
-	(struct zfcp_fsf_req *);
-extern int zfcp_qdio_sbals_from_sg
-	(struct zfcp_fsf_req *, unsigned long, struct scatterlist *, int, int);
-extern int zfcp_qdio_sbals_from_scsicmnd
-	(struct zfcp_fsf_req *, unsigned long, struct scsi_cmnd *);
-
-
-/******************************** FSF ****************************************/
-extern int  zfcp_fsf_open_port(struct zfcp_erp_action *);
-extern int  zfcp_fsf_close_port(struct zfcp_erp_action *);
-extern int  zfcp_fsf_close_physical_port(struct zfcp_erp_action *);
-
-extern int  zfcp_fsf_open_unit(struct zfcp_erp_action *);
-extern int  zfcp_fsf_close_unit(struct zfcp_erp_action *);
-
-extern int  zfcp_fsf_exchange_config_data(struct zfcp_erp_action *);
-extern int  zfcp_fsf_exchange_config_data_sync(struct zfcp_adapter *,
-                                              struct fsf_qtcb_bottom_config *);
-extern int  zfcp_fsf_exchange_port_data(struct zfcp_erp_action *);
-extern int  zfcp_fsf_exchange_port_data_sync(struct zfcp_adapter *,
-                                             struct fsf_qtcb_bottom_port *);
-extern int  zfcp_fsf_control_file(struct zfcp_adapter *, struct zfcp_fsf_req **,
-				  u32, u32, struct zfcp_sg_list *);
-extern void zfcp_fsf_start_timer(struct zfcp_fsf_req *, unsigned long);
-extern void zfcp_erp_start_timer(struct zfcp_fsf_req *);
-extern void zfcp_fsf_req_dismiss_all(struct zfcp_adapter *);
-extern int  zfcp_fsf_status_read(struct zfcp_adapter *, int);
-extern int zfcp_fsf_req_create(struct zfcp_adapter *, u32, int, mempool_t *,
-			       unsigned long *, struct zfcp_fsf_req **);
-extern int zfcp_fsf_send_ct(struct zfcp_send_ct *, mempool_t *,
-			    struct zfcp_erp_action *);
-extern int zfcp_fsf_send_els(struct zfcp_send_els *);
-extern int  zfcp_fsf_send_fcp_command_task(struct zfcp_adapter *,
-					   struct zfcp_unit *,
-					   struct scsi_cmnd *, int, int);
-extern int  zfcp_fsf_req_complete(struct zfcp_fsf_req *);
-extern void zfcp_fsf_incoming_els(struct zfcp_fsf_req *);
-extern void zfcp_fsf_req_free(struct zfcp_fsf_req *);
-extern struct zfcp_fsf_req *zfcp_fsf_send_fcp_command_task_management(
-	struct zfcp_adapter *, struct zfcp_unit *, u8, int);
-extern struct zfcp_fsf_req *zfcp_fsf_abort_fcp_command(
-	unsigned long, struct zfcp_adapter *, struct zfcp_unit *, int);
-
-/******************************* FC/FCP **************************************/
-extern int  zfcp_nameserver_enqueue(struct zfcp_adapter *);
-extern int  zfcp_ns_gid_pn_request(struct zfcp_erp_action *);
-extern int  zfcp_check_ct_response(struct ct_hdr *);
-extern int  zfcp_handle_els_rjt(u32, struct zfcp_ls_rjt_par *);
-extern void zfcp_plogi_evaluate(struct zfcp_port *, struct fsf_plogi *);
-
-/******************************* SCSI ****************************************/
-extern int  zfcp_adapter_scsi_register(struct zfcp_adapter *);
-extern void zfcp_adapter_scsi_unregister(struct zfcp_adapter *);
-extern void zfcp_set_fcp_dl(struct fcp_cmnd_iu *, fcp_dl_t);
-extern char *zfcp_get_fcp_rsp_info_ptr(struct fcp_rsp_iu *);
-extern void set_host_byte(int *, char);
-extern void set_driver_byte(int *, char);
-extern char *zfcp_get_fcp_sns_info_ptr(struct fcp_rsp_iu *);
-extern fcp_dl_t zfcp_get_fcp_dl(struct fcp_cmnd_iu *);
-
-extern int zfcp_scsi_command_async(struct zfcp_adapter *,struct zfcp_unit *,
-				   struct scsi_cmnd *, int);
-extern int zfcp_scsi_command_sync(struct zfcp_unit *, struct scsi_cmnd *, int);
-extern struct fc_function_template zfcp_transport_functions;
-
-/******************************** ERP ****************************************/
-extern void zfcp_erp_modify_adapter_status(struct zfcp_adapter *, u8, void *,
-					   u32, int);
-extern int  zfcp_erp_adapter_reopen(struct zfcp_adapter *, int, u8, void *);
-extern int  zfcp_erp_adapter_shutdown(struct zfcp_adapter *, int, u8, void *);
-extern void zfcp_erp_adapter_failed(struct zfcp_adapter *, u8, void *);
-
-extern void zfcp_erp_modify_port_status(struct zfcp_port *, u8, void *, u32,
-					int);
-extern int  zfcp_erp_port_reopen(struct zfcp_port *, int, u8, void *);
-extern int  zfcp_erp_port_shutdown(struct zfcp_port *, int, u8, void *);
-extern int  zfcp_erp_port_forced_reopen(struct zfcp_port *, int, u8, void *);
-extern void zfcp_erp_port_failed(struct zfcp_port *, u8, void *);
-extern int  zfcp_erp_port_reopen_all(struct zfcp_adapter *, int, u8, void *);
-
-extern void zfcp_erp_modify_unit_status(struct zfcp_unit *, u8, void *, u32,
-					int);
-extern int  zfcp_erp_unit_reopen(struct zfcp_unit *, int, u8, void *);
-extern int  zfcp_erp_unit_shutdown(struct zfcp_unit *, int, u8, void *);
-extern void zfcp_erp_unit_failed(struct zfcp_unit *, u8, void *);
-
-extern int  zfcp_erp_thread_setup(struct zfcp_adapter *);
-extern int  zfcp_erp_thread_kill(struct zfcp_adapter *);
-extern int  zfcp_erp_wait(struct zfcp_adapter *);
-extern void zfcp_erp_async_handler(struct zfcp_erp_action *, unsigned long);
-
-extern int  zfcp_test_link(struct zfcp_port *);
-
-extern void zfcp_erp_port_boxed(struct zfcp_port *, u8 id, void *ref);
-extern void zfcp_erp_unit_boxed(struct zfcp_unit *, u8 id, void *ref);
-extern void zfcp_erp_port_access_denied(struct zfcp_port *, u8 id, void *ref);
-extern void zfcp_erp_unit_access_denied(struct zfcp_unit *, u8 id, void *ref);
-extern void zfcp_erp_adapter_access_changed(struct zfcp_adapter *, u8, void *);
-extern void zfcp_erp_port_access_changed(struct zfcp_port *, u8, void *);
-extern void zfcp_erp_unit_access_changed(struct zfcp_unit *, u8, void *);
-
-/******************************** AUX ****************************************/
-extern void zfcp_rec_dbf_event_thread(u8 id, struct zfcp_adapter *adapter,
-				      int lock);
-extern void zfcp_rec_dbf_event_adapter(u8 id, void *ref, struct zfcp_adapter *);
-extern void zfcp_rec_dbf_event_port(u8 id, void *ref, struct zfcp_port *port);
-extern void zfcp_rec_dbf_event_unit(u8 id, void *ref, struct zfcp_unit *unit);
-extern void zfcp_rec_dbf_event_trigger(u8 id, void *ref, u8 want, u8 need,
-				       void *action, struct zfcp_adapter *,
+/* zfcp_dbf.c */
+extern int zfcp_adapter_debug_register(struct zfcp_adapter *);
+extern void zfcp_adapter_debug_unregister(struct zfcp_adapter *);
+extern void zfcp_rec_dbf_event_thread(u8, struct zfcp_adapter *);
+extern void zfcp_rec_dbf_event_thread_lock(u8, struct zfcp_adapter *);
+extern void zfcp_rec_dbf_event_adapter(u8, void *, struct zfcp_adapter *);
+extern void zfcp_rec_dbf_event_port(u8, void *, struct zfcp_port *);
+extern void zfcp_rec_dbf_event_unit(u8, void *, struct zfcp_unit *);
+extern void zfcp_rec_dbf_event_trigger(u8, void *, u8, u8, void *,
+				       struct zfcp_adapter *,
 				       struct zfcp_port *, struct zfcp_unit *);
-extern void zfcp_rec_dbf_event_action(u8 id, struct zfcp_erp_action *);
-
+extern void zfcp_rec_dbf_event_action(u8, struct zfcp_erp_action *);
 extern void zfcp_hba_dbf_event_fsf_response(struct zfcp_fsf_req *);
 extern void zfcp_hba_dbf_event_fsf_unsol(const char *, struct zfcp_adapter *,
 					 struct fsf_status_read_buffer *);
 extern void zfcp_hba_dbf_event_qdio(struct zfcp_adapter *,
 				    unsigned int, unsigned int, unsigned int,
 				    int, int);
-
 extern void zfcp_san_dbf_event_ct_request(struct zfcp_fsf_req *);
 extern void zfcp_san_dbf_event_ct_response(struct zfcp_fsf_req *);
 extern void zfcp_san_dbf_event_els_request(struct zfcp_fsf_req *);
 extern void zfcp_san_dbf_event_els_response(struct zfcp_fsf_req *);
 extern void zfcp_san_dbf_event_incoming_els(struct zfcp_fsf_req *);
-
 extern void zfcp_scsi_dbf_event_result(const char *, int, struct zfcp_adapter *,
 				       struct scsi_cmnd *,
 				       struct zfcp_fsf_req *);
@@ -198,6 +64,101 @@
 				      unsigned long);
 extern void zfcp_scsi_dbf_event_devreset(const char *, u8, struct zfcp_unit *,
 					 struct scsi_cmnd *);
-extern int zfcp_reqlist_isempty(struct zfcp_adapter *);
+
+/* zfcp_erp.c */
+extern void zfcp_erp_modify_adapter_status(struct zfcp_adapter *, u8, void *,
+					   u32, int);
+extern void zfcp_erp_adapter_reopen(struct zfcp_adapter *, int, u8, void *);
+extern void zfcp_erp_adapter_shutdown(struct zfcp_adapter *, int, u8, void *);
+extern void zfcp_erp_adapter_failed(struct zfcp_adapter *, u8, void *);
+extern void zfcp_erp_modify_port_status(struct zfcp_port *, u8, void *, u32,
+					int);
+extern int  zfcp_erp_port_reopen(struct zfcp_port *, int, u8, void *);
+extern void zfcp_erp_port_shutdown(struct zfcp_port *, int, u8, void *);
+extern void zfcp_erp_port_forced_reopen(struct zfcp_port *, int, u8, void *);
+extern void zfcp_erp_port_failed(struct zfcp_port *, u8, void *);
+extern void zfcp_erp_modify_unit_status(struct zfcp_unit *, u8, void *, u32,
+					int);
+extern void zfcp_erp_unit_reopen(struct zfcp_unit *, int, u8, void *);
+extern void zfcp_erp_unit_shutdown(struct zfcp_unit *, int, u8, void *);
+extern void zfcp_erp_unit_failed(struct zfcp_unit *, u8, void *);
+extern int  zfcp_erp_thread_setup(struct zfcp_adapter *);
+extern void zfcp_erp_thread_kill(struct zfcp_adapter *);
+extern void zfcp_erp_wait(struct zfcp_adapter *);
+extern void zfcp_erp_notify(struct zfcp_erp_action *, unsigned long);
+extern void zfcp_erp_port_boxed(struct zfcp_port *, u8, void *);
+extern void zfcp_erp_unit_boxed(struct zfcp_unit *, u8, void *);
+extern void zfcp_erp_port_access_denied(struct zfcp_port *, u8, void *);
+extern void zfcp_erp_unit_access_denied(struct zfcp_unit *, u8, void *);
+extern void zfcp_erp_adapter_access_changed(struct zfcp_adapter *, u8, void *);
+extern void zfcp_erp_timeout_handler(unsigned long);
+
+/* zfcp_fc.c */
+extern int zfcp_scan_ports(struct zfcp_adapter *);
+extern void _zfcp_scan_ports_later(struct work_struct *);
+extern void zfcp_fc_incoming_els(struct zfcp_fsf_req *);
+extern int zfcp_fc_ns_gid_pn_request(struct zfcp_erp_action *);
+extern void zfcp_fc_plogi_evaluate(struct zfcp_port *, struct fsf_plogi *);
+extern void zfcp_test_link(struct zfcp_port *);
+
+/* zfcp_fsf.c */
+extern int zfcp_fsf_open_port(struct zfcp_erp_action *);
+extern int zfcp_fsf_close_port(struct zfcp_erp_action *);
+extern int zfcp_fsf_close_physical_port(struct zfcp_erp_action *);
+extern int zfcp_fsf_open_unit(struct zfcp_erp_action *);
+extern int zfcp_fsf_close_unit(struct zfcp_erp_action *);
+extern int zfcp_fsf_exchange_config_data(struct zfcp_erp_action *);
+extern int zfcp_fsf_exchange_config_data_sync(struct zfcp_adapter *,
+					      struct fsf_qtcb_bottom_config *);
+extern int zfcp_fsf_exchange_port_data(struct zfcp_erp_action *);
+extern int zfcp_fsf_exchange_port_data_sync(struct zfcp_adapter *,
+					    struct fsf_qtcb_bottom_port *);
+extern struct zfcp_fsf_req *zfcp_fsf_control_file(struct zfcp_adapter *,
+						  struct zfcp_fsf_cfdc *);
+extern void zfcp_fsf_req_dismiss_all(struct zfcp_adapter *);
+extern int zfcp_fsf_status_read(struct zfcp_adapter *);
+extern int zfcp_status_read_refill(struct zfcp_adapter *adapter);
+extern int zfcp_fsf_send_ct(struct zfcp_send_ct *, mempool_t *,
+			    struct zfcp_erp_action *);
+extern int zfcp_fsf_send_els(struct zfcp_send_els *);
+extern int zfcp_fsf_send_fcp_command_task(struct zfcp_adapter *,
+					  struct zfcp_unit *,
+					  struct scsi_cmnd *, int, int);
+extern void zfcp_fsf_req_complete(struct zfcp_fsf_req *);
+extern void zfcp_fsf_req_free(struct zfcp_fsf_req *);
+extern struct zfcp_fsf_req *zfcp_fsf_send_fcp_ctm(struct zfcp_adapter *,
+						  struct zfcp_unit *, u8, int);
+extern struct zfcp_fsf_req *zfcp_fsf_abort_fcp_command(unsigned long,
+						       struct zfcp_adapter *,
+						       struct zfcp_unit *, int);
+
+/* zfcp_qdio.c */
+extern int zfcp_qdio_allocate(struct zfcp_adapter *);
+extern void zfcp_qdio_free(struct zfcp_adapter *);
+extern int zfcp_qdio_send(struct zfcp_fsf_req *);
+extern volatile struct qdio_buffer_element *zfcp_qdio_sbale_req(
+						struct zfcp_fsf_req *);
+extern volatile struct qdio_buffer_element *zfcp_qdio_sbale_curr(
+						struct zfcp_fsf_req *);
+extern int zfcp_qdio_sbals_from_sg(struct zfcp_fsf_req *, unsigned long,
+				   struct scatterlist *, int);
+extern int zfcp_qdio_open(struct zfcp_adapter *);
+extern void zfcp_qdio_close(struct zfcp_adapter *);
+
+/* zfcp_scsi.c */
+extern struct zfcp_data zfcp_data;
+extern int zfcp_adapter_scsi_register(struct zfcp_adapter *);
+extern void zfcp_adapter_scsi_unregister(struct zfcp_adapter *);
+extern void zfcp_set_fcp_dl(struct fcp_cmnd_iu *, fcp_dl_t);
+extern char *zfcp_get_fcp_sns_info_ptr(struct fcp_rsp_iu *);
+extern struct fc_function_template zfcp_transport_functions;
+
+/* zfcp_sysfs.c */
+extern struct attribute_group zfcp_sysfs_unit_attrs;
+extern struct attribute_group zfcp_sysfs_adapter_attrs;
+extern struct attribute_group zfcp_sysfs_ns_port_attrs;
+extern struct attribute_group zfcp_sysfs_port_attrs;
+extern struct device_attribute *zfcp_sysfs_sdev_attrs[];
+extern struct device_attribute *zfcp_sysfs_shost_attrs[];
 
 #endif	/* ZFCP_EXT_H */
diff --git a/drivers/s390/scsi/zfcp_fc.c b/drivers/s390/scsi/zfcp_fc.c
new file mode 100644
index 0000000..e984469
--- /dev/null
+++ b/drivers/s390/scsi/zfcp_fc.c
@@ -0,0 +1,567 @@
+/*
+ * zfcp device driver
+ *
+ * Fibre Channel related functions for the zfcp device driver.
+ *
+ * Copyright IBM Corporation 2008
+ */
+
+#include "zfcp_ext.h"
+
+struct ct_iu_gpn_ft_req {
+	struct ct_hdr header;
+	u8 flags;
+	u8 domain_id_scope;
+	u8 area_id_scope;
+	u8 fc4_type;
+} __attribute__ ((packed));
+
+struct gpn_ft_resp_acc {
+	u8 control;
+	u8 port_id[3];
+	u8 reserved[4];
+	u64 wwpn;
+} __attribute__ ((packed));
+
+#define ZFCP_GPN_FT_ENTRIES ((PAGE_SIZE - sizeof(struct ct_hdr)) \
+				/ sizeof(struct gpn_ft_resp_acc))
+#define ZFCP_GPN_FT_BUFFERS 4
+#define ZFCP_GPN_FT_MAX_ENTRIES ZFCP_GPN_FT_BUFFERS * (ZFCP_GPN_FT_ENTRIES + 1)
+
+struct ct_iu_gpn_ft_resp {
+	struct ct_hdr header;
+	struct gpn_ft_resp_acc accept[ZFCP_GPN_FT_ENTRIES];
+} __attribute__ ((packed));
+
+struct zfcp_gpn_ft {
+	struct zfcp_send_ct ct;
+	struct scatterlist sg_req;
+	struct scatterlist sg_resp[ZFCP_GPN_FT_BUFFERS];
+};
+
+static struct zfcp_port *zfcp_get_port_by_did(struct zfcp_adapter *adapter,
+					      u32 d_id)
+{
+	struct zfcp_port *port;
+
+	list_for_each_entry(port, &adapter->port_list_head, list)
+		if ((port->d_id == d_id) &&
+		    !atomic_test_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status))
+			return port;
+	return NULL;
+}
+
+static void _zfcp_fc_incoming_rscn(struct zfcp_fsf_req *fsf_req, u32 range,
+				   struct fcp_rscn_element *elem)
+{
+	unsigned long flags;
+	struct zfcp_port *port;
+
+	read_lock_irqsave(&zfcp_data.config_lock, flags);
+	list_for_each_entry(port, &fsf_req->adapter->port_list_head, list) {
+		if (atomic_test_mask(ZFCP_STATUS_PORT_WKA, &port->status))
+			continue;
+		/* FIXME: ZFCP_STATUS_PORT_DID_DID check is racy */
+		if (!atomic_test_mask(ZFCP_STATUS_PORT_DID_DID, &port->status))
+			/* Try to connect to unused ports anyway. */
+			zfcp_erp_port_reopen(port,
+					     ZFCP_STATUS_COMMON_ERP_FAILED,
+					     82, fsf_req);
+		else if ((port->d_id & range) == (elem->nport_did & range))
+			/* Check connection status for connected ports */
+			zfcp_test_link(port);
+	}
+	read_unlock_irqrestore(&zfcp_data.config_lock, flags);
+}
+
+static void zfcp_fc_incoming_rscn(struct zfcp_fsf_req *fsf_req)
+{
+	struct fsf_status_read_buffer *status_buffer = (void *)fsf_req->data;
+	struct fcp_rscn_head *fcp_rscn_head;
+	struct fcp_rscn_element *fcp_rscn_element;
+	u16 i;
+	u16 no_entries;
+	u32 range_mask;
+
+	fcp_rscn_head = (struct fcp_rscn_head *) status_buffer->payload.data;
+	fcp_rscn_element = (struct fcp_rscn_element *) fcp_rscn_head;
+
+	/* see FC-FS */
+	no_entries = fcp_rscn_head->payload_len /
+			sizeof(struct fcp_rscn_element);
+
+	for (i = 1; i < no_entries; i++) {
+		/* skip head and start with 1st element */
+		fcp_rscn_element++;
+		switch (fcp_rscn_element->addr_format) {
+		case ZFCP_PORT_ADDRESS:
+			range_mask = ZFCP_PORTS_RANGE_PORT;
+			break;
+		case ZFCP_AREA_ADDRESS:
+			range_mask = ZFCP_PORTS_RANGE_AREA;
+			break;
+		case ZFCP_DOMAIN_ADDRESS:
+			range_mask = ZFCP_PORTS_RANGE_DOMAIN;
+			break;
+		case ZFCP_FABRIC_ADDRESS:
+			range_mask = ZFCP_PORTS_RANGE_FABRIC;
+			break;
+		default:
+			continue;
+		}
+		_zfcp_fc_incoming_rscn(fsf_req, range_mask, fcp_rscn_element);
+	}
+	schedule_work(&fsf_req->adapter->scan_work);
+}
+
+static void zfcp_fc_incoming_wwpn(struct zfcp_fsf_req *req, wwn_t wwpn)
+{
+	struct zfcp_adapter *adapter = req->adapter;
+	struct zfcp_port *port;
+	unsigned long flags;
+
+	read_lock_irqsave(&zfcp_data.config_lock, flags);
+	list_for_each_entry(port, &adapter->port_list_head, list)
+		if (port->wwpn == wwpn)
+			break;
+	read_unlock_irqrestore(&zfcp_data.config_lock, flags);
+
+	if (port && (port->wwpn == wwpn))
+		zfcp_erp_port_forced_reopen(port, 0, 83, req);
+}
+
+static void zfcp_fc_incoming_plogi(struct zfcp_fsf_req *req)
+{
+	struct fsf_status_read_buffer *status_buffer =
+		(struct fsf_status_read_buffer *)req->data;
+	struct fsf_plogi *els_plogi =
+		(struct fsf_plogi *) status_buffer->payload.data;
+
+	zfcp_fc_incoming_wwpn(req, els_plogi->serv_param.wwpn);
+}
+
+static void zfcp_fc_incoming_logo(struct zfcp_fsf_req *req)
+{
+	struct fsf_status_read_buffer *status_buffer =
+		(struct fsf_status_read_buffer *)req->data;
+	struct fcp_logo *els_logo =
+		(struct fcp_logo *) status_buffer->payload.data;
+
+	zfcp_fc_incoming_wwpn(req, els_logo->nport_wwpn);
+}
+
+/**
+ * zfcp_fc_incoming_els - handle incoming ELS
+ * @fsf_req - request which contains incoming ELS
+ */
+void zfcp_fc_incoming_els(struct zfcp_fsf_req *fsf_req)
+{
+	struct fsf_status_read_buffer *status_buffer =
+		(struct fsf_status_read_buffer *) fsf_req->data;
+	unsigned int els_type = status_buffer->payload.data[0];
+
+	zfcp_san_dbf_event_incoming_els(fsf_req);
+	if (els_type == LS_PLOGI)
+		zfcp_fc_incoming_plogi(fsf_req);
+	else if (els_type == LS_LOGO)
+		zfcp_fc_incoming_logo(fsf_req);
+	else if (els_type == LS_RSCN)
+		zfcp_fc_incoming_rscn(fsf_req);
+}
+
+static void zfcp_ns_gid_pn_handler(unsigned long data)
+{
+	struct zfcp_gid_pn_data *gid_pn = (struct zfcp_gid_pn_data *) data;
+	struct zfcp_send_ct *ct = &gid_pn->ct;
+	struct ct_iu_gid_pn_req *ct_iu_req = sg_virt(ct->req);
+	struct ct_iu_gid_pn_resp *ct_iu_resp = sg_virt(ct->resp);
+	struct zfcp_port *port = gid_pn->port;
+
+	if (ct->status)
+		goto out;
+	if (ct_iu_resp->header.cmd_rsp_code != ZFCP_CT_ACCEPT) {
+		atomic_set_mask(ZFCP_STATUS_PORT_INVALID_WWPN, &port->status);
+		goto out;
+	}
+	/* paranoia */
+	if (ct_iu_req->wwpn != port->wwpn)
+		goto out;
+	/* looks like a valid d_id */
+	port->d_id = ct_iu_resp->d_id & ZFCP_DID_MASK;
+	atomic_set_mask(ZFCP_STATUS_PORT_DID_DID, &port->status);
+out:
+	mempool_free(gid_pn, port->adapter->pool.data_gid_pn);
+}
+
+/**
+ * zfcp_fc_ns_gid_pn_request - initiate GID_PN nameserver request
+ * @erp_action: pointer to zfcp_erp_action where GID_PN request is needed
+ * return: -ENOMEM on error, 0 otherwise
+ */
+int zfcp_fc_ns_gid_pn_request(struct zfcp_erp_action *erp_action)
+{
+	int ret;
+	struct zfcp_gid_pn_data *gid_pn;
+	struct zfcp_adapter *adapter = erp_action->adapter;
+
+	gid_pn = mempool_alloc(adapter->pool.data_gid_pn, GFP_ATOMIC);
+	if (!gid_pn)
+		return -ENOMEM;
+
+	memset(gid_pn, 0, sizeof(*gid_pn));
+
+	/* setup parameters for send generic command */
+	gid_pn->port = erp_action->port;
+	gid_pn->ct.port = adapter->nameserver_port;
+	gid_pn->ct.handler = zfcp_ns_gid_pn_handler;
+	gid_pn->ct.handler_data = (unsigned long) gid_pn;
+	gid_pn->ct.timeout = ZFCP_NS_GID_PN_TIMEOUT;
+	gid_pn->ct.req = &gid_pn->req;
+	gid_pn->ct.resp = &gid_pn->resp;
+	gid_pn->ct.req_count = 1;
+	gid_pn->ct.resp_count = 1;
+	sg_init_one(&gid_pn->req, &gid_pn->ct_iu_req,
+		    sizeof(struct ct_iu_gid_pn_req));
+	sg_init_one(&gid_pn->resp, &gid_pn->ct_iu_resp,
+		    sizeof(struct ct_iu_gid_pn_resp));
+
+	/* setup nameserver request */
+	gid_pn->ct_iu_req.header.revision = ZFCP_CT_REVISION;
+	gid_pn->ct_iu_req.header.gs_type = ZFCP_CT_DIRECTORY_SERVICE;
+	gid_pn->ct_iu_req.header.gs_subtype = ZFCP_CT_NAME_SERVER;
+	gid_pn->ct_iu_req.header.options = ZFCP_CT_SYNCHRONOUS;
+	gid_pn->ct_iu_req.header.cmd_rsp_code = ZFCP_CT_GID_PN;
+	gid_pn->ct_iu_req.header.max_res_size = ZFCP_CT_MAX_SIZE;
+	gid_pn->ct_iu_req.wwpn = erp_action->port->wwpn;
+
+	ret = zfcp_fsf_send_ct(&gid_pn->ct, adapter->pool.fsf_req_erp,
+			       erp_action);
+	if (ret)
+		mempool_free(gid_pn, adapter->pool.data_gid_pn);
+	return ret;
+}
+
+/**
+ * zfcp_fc_plogi_evaluate - evaluate PLOGI playload
+ * @port: zfcp_port structure
+ * @plogi: plogi payload
+ *
+ * Evaluate PLOGI playload and copy important fields into zfcp_port structure
+ */
+void zfcp_fc_plogi_evaluate(struct zfcp_port *port, struct fsf_plogi *plogi)
+{
+	port->maxframe_size = plogi->serv_param.common_serv_param[7] |
+		((plogi->serv_param.common_serv_param[6] & 0x0F) << 8);
+	if (plogi->serv_param.class1_serv_param[0] & 0x80)
+		port->supported_classes |= FC_COS_CLASS1;
+	if (plogi->serv_param.class2_serv_param[0] & 0x80)
+		port->supported_classes |= FC_COS_CLASS2;
+	if (plogi->serv_param.class3_serv_param[0] & 0x80)
+		port->supported_classes |= FC_COS_CLASS3;
+	if (plogi->serv_param.class4_serv_param[0] & 0x80)
+		port->supported_classes |= FC_COS_CLASS4;
+}
+
+struct zfcp_els_adisc {
+	struct zfcp_send_els els;
+	struct scatterlist req;
+	struct scatterlist resp;
+	struct zfcp_ls_adisc ls_adisc;
+	struct zfcp_ls_adisc_acc ls_adisc_acc;
+};
+
+static void zfcp_fc_adisc_handler(unsigned long data)
+{
+	struct zfcp_els_adisc *adisc = (struct zfcp_els_adisc *) data;
+	struct zfcp_port *port = adisc->els.port;
+	struct zfcp_ls_adisc_acc *ls_adisc = &adisc->ls_adisc_acc;
+
+	if (adisc->els.status) {
+		/* request rejected or timed out */
+		zfcp_erp_port_forced_reopen(port, 0, 63, NULL);
+		goto out;
+	}
+
+	if (!port->wwnn)
+		port->wwnn = ls_adisc->wwnn;
+
+	if (port->wwpn != ls_adisc->wwpn)
+		zfcp_erp_port_reopen(port, 0, 64, NULL);
+
+ out:
+	zfcp_port_put(port);
+	kfree(adisc);
+}
+
+static int zfcp_fc_adisc(struct zfcp_port *port)
+{
+	struct zfcp_els_adisc *adisc;
+	struct zfcp_adapter *adapter = port->adapter;
+
+	adisc = kzalloc(sizeof(struct zfcp_els_adisc), GFP_ATOMIC);
+	if (!adisc)
+		return -ENOMEM;
+
+	adisc->els.req = &adisc->req;
+	adisc->els.resp = &adisc->resp;
+	sg_init_one(adisc->els.req, &adisc->ls_adisc,
+		    sizeof(struct zfcp_ls_adisc));
+	sg_init_one(adisc->els.resp, &adisc->ls_adisc_acc,
+		    sizeof(struct zfcp_ls_adisc_acc));
+
+	adisc->els.req_count = 1;
+	adisc->els.resp_count = 1;
+	adisc->els.adapter = adapter;
+	adisc->els.port = port;
+	adisc->els.d_id = port->d_id;
+	adisc->els.handler = zfcp_fc_adisc_handler;
+	adisc->els.handler_data = (unsigned long) adisc;
+	adisc->els.ls_code = adisc->ls_adisc.code = ZFCP_LS_ADISC;
+
+	/* acc. to FC-FS, hard_nport_id in ADISC should not be set for ports
+	   without FC-AL-2 capability, so we don't set it */
+	adisc->ls_adisc.wwpn = fc_host_port_name(adapter->scsi_host);
+	adisc->ls_adisc.wwnn = fc_host_node_name(adapter->scsi_host);
+	adisc->ls_adisc.nport_id = fc_host_port_id(adapter->scsi_host);
+
+	return zfcp_fsf_send_els(&adisc->els);
+}
+
+/**
+ * zfcp_test_link - lightweight link test procedure
+ * @port: port to be tested
+ *
+ * Test status of a link to a remote port using the ELS command ADISC.
+ * If there is a problem with the remote port, error recovery steps
+ * will be triggered.
+ */
+void zfcp_test_link(struct zfcp_port *port)
+{
+	int retval;
+
+	zfcp_port_get(port);
+	retval = zfcp_fc_adisc(port);
+	if (retval == 0 || retval == -EBUSY)
+		return;
+
+	/* send of ADISC was not possible */
+	zfcp_port_put(port);
+	zfcp_erp_port_forced_reopen(port, 0, 65, NULL);
+}
+
+static int zfcp_scan_get_nameserver(struct zfcp_adapter *adapter)
+{
+	int ret;
+
+	if (!adapter->nameserver_port)
+		return -EINTR;
+
+	if (!atomic_test_mask(ZFCP_STATUS_COMMON_UNBLOCKED,
+			       &adapter->nameserver_port->status)) {
+		ret = zfcp_erp_port_reopen(adapter->nameserver_port, 0, 148,
+					   NULL);
+		if (ret)
+			return ret;
+		zfcp_erp_wait(adapter);
+		zfcp_port_put(adapter->nameserver_port);
+	}
+	return !atomic_test_mask(ZFCP_STATUS_COMMON_UNBLOCKED,
+				  &adapter->nameserver_port->status);
+}
+
+static void zfcp_gpn_ft_handler(unsigned long _done)
+{
+	complete((struct completion *)_done);
+}
+
+static void zfcp_free_sg_env(struct zfcp_gpn_ft *gpn_ft)
+{
+	struct scatterlist *sg = &gpn_ft->sg_req;
+
+	kfree(sg_virt(sg)); /* free request buffer */
+	zfcp_sg_free_table(gpn_ft->sg_resp, ZFCP_GPN_FT_BUFFERS);
+
+	kfree(gpn_ft);
+}
+
+static struct zfcp_gpn_ft *zfcp_alloc_sg_env(void)
+{
+	struct zfcp_gpn_ft *gpn_ft;
+	struct ct_iu_gpn_ft_req *req;
+
+	gpn_ft = kzalloc(sizeof(*gpn_ft), GFP_KERNEL);
+	if (!gpn_ft)
+		return NULL;
+
+	req = kzalloc(sizeof(struct ct_iu_gpn_ft_req), GFP_KERNEL);
+	if (!req) {
+		kfree(gpn_ft);
+		gpn_ft = NULL;
+		goto out;
+	}
+	sg_init_one(&gpn_ft->sg_req, req, sizeof(*req));
+
+	if (zfcp_sg_setup_table(gpn_ft->sg_resp, ZFCP_GPN_FT_BUFFERS)) {
+		zfcp_free_sg_env(gpn_ft);
+		gpn_ft = NULL;
+	}
+out:
+	return gpn_ft;
+}
+
+
+static int zfcp_scan_issue_gpn_ft(struct zfcp_gpn_ft *gpn_ft,
+				  struct zfcp_adapter *adapter)
+{
+	struct zfcp_send_ct *ct = &gpn_ft->ct;
+	struct ct_iu_gpn_ft_req *req = sg_virt(&gpn_ft->sg_req);
+	struct completion done;
+	int ret;
+
+	/* prepare CT IU for GPN_FT */
+	req->header.revision = ZFCP_CT_REVISION;
+	req->header.gs_type = ZFCP_CT_DIRECTORY_SERVICE;
+	req->header.gs_subtype = ZFCP_CT_NAME_SERVER;
+	req->header.options = ZFCP_CT_SYNCHRONOUS;
+	req->header.cmd_rsp_code = ZFCP_CT_GPN_FT;
+	req->header.max_res_size = (sizeof(struct gpn_ft_resp_acc) *
+					(ZFCP_GPN_FT_MAX_ENTRIES - 1)) >> 2;
+	req->flags = 0;
+	req->domain_id_scope = 0;
+	req->area_id_scope = 0;
+	req->fc4_type = ZFCP_CT_SCSI_FCP;
+
+	/* prepare zfcp_send_ct */
+	ct->port = adapter->nameserver_port;
+	ct->handler = zfcp_gpn_ft_handler;
+	ct->handler_data = (unsigned long)&done;
+	ct->timeout = 10;
+	ct->req = &gpn_ft->sg_req;
+	ct->resp = gpn_ft->sg_resp;
+	ct->req_count = 1;
+	ct->resp_count = ZFCP_GPN_FT_BUFFERS;
+
+	init_completion(&done);
+	ret = zfcp_fsf_send_ct(ct, NULL, NULL);
+	if (!ret)
+		wait_for_completion(&done);
+	return ret;
+}
+
+static void zfcp_validate_port(struct zfcp_port *port)
+{
+	struct zfcp_adapter *adapter = port->adapter;
+
+	atomic_clear_mask(ZFCP_STATUS_COMMON_NOESC, &port->status);
+
+	if (port == adapter->nameserver_port)
+		return;
+	if ((port->supported_classes != 0) || (port->units != 0)) {
+		zfcp_port_put(port);
+		return;
+	}
+	zfcp_erp_port_shutdown(port, 0, 151, NULL);
+	zfcp_erp_wait(adapter);
+	zfcp_port_put(port);
+	zfcp_port_dequeue(port);
+}
+
+static int zfcp_scan_eval_gpn_ft(struct zfcp_gpn_ft *gpn_ft)
+{
+	struct zfcp_send_ct *ct = &gpn_ft->ct;
+	struct scatterlist *sg = gpn_ft->sg_resp;
+	struct ct_hdr *hdr = sg_virt(sg);
+	struct gpn_ft_resp_acc *acc = sg_virt(sg);
+	struct zfcp_adapter *adapter = ct->port->adapter;
+	struct zfcp_port *port, *tmp;
+	u32 d_id;
+	int ret = 0, x;
+
+	if (ct->status)
+		return -EIO;
+
+	if (hdr->cmd_rsp_code != ZFCP_CT_ACCEPT) {
+		if (hdr->reason_code == ZFCP_CT_UNABLE_TO_PERFORM_CMD)
+			return -EAGAIN; /* might be a temporary condition */
+		return -EIO;
+	}
+
+	if (hdr->max_res_size)
+		return -E2BIG;
+
+	down(&zfcp_data.config_sema);
+
+	/* first entry is the header */
+	for (x = 1; x < ZFCP_GPN_FT_MAX_ENTRIES; x++) {
+		if (x % (ZFCP_GPN_FT_ENTRIES + 1))
+			acc++;
+		else
+			acc = sg_virt(++sg);
+
+		d_id = acc->port_id[0] << 16 | acc->port_id[1] << 8 |
+		       acc->port_id[2];
+
+		/* skip the adapter's port and known remote ports */
+		if (acc->wwpn == fc_host_port_name(adapter->scsi_host) ||
+		     zfcp_get_port_by_did(adapter, d_id))
+			continue;
+
+		port = zfcp_port_enqueue(adapter, acc->wwpn,
+					 ZFCP_STATUS_PORT_DID_DID |
+					 ZFCP_STATUS_COMMON_NOESC, d_id);
+		if (IS_ERR(port))
+			ret = PTR_ERR(port);
+		else
+			zfcp_erp_port_reopen(port, 0, 149, NULL);
+		if (acc->control & 0x80) /* last entry */
+			break;
+	}
+
+	zfcp_erp_wait(adapter);
+	list_for_each_entry_safe(port, tmp, &adapter->port_list_head, list)
+		zfcp_validate_port(port);
+	up(&zfcp_data.config_sema);
+	return ret;
+}
+
+/**
+ * zfcp_scan_ports - scan remote ports and attach new ports
+ * @adapter: pointer to struct zfcp_adapter
+ */
+int zfcp_scan_ports(struct zfcp_adapter *adapter)
+{
+	int ret, i;
+	struct zfcp_gpn_ft *gpn_ft;
+
+	zfcp_erp_wait(adapter); /* wait until adapter is finished with ERP */
+	if (fc_host_port_type(adapter->scsi_host) != FC_PORTTYPE_NPORT)
+		return 0;
+
+	ret = zfcp_scan_get_nameserver(adapter);
+	if (ret)
+		return ret;
+
+	gpn_ft = zfcp_alloc_sg_env();
+	if (!gpn_ft)
+		return -ENOMEM;
+
+	for (i = 0; i < 3; i++) {
+		ret = zfcp_scan_issue_gpn_ft(gpn_ft, adapter);
+		if (!ret) {
+			ret = zfcp_scan_eval_gpn_ft(gpn_ft);
+			if (ret == -EAGAIN)
+				ssleep(1);
+			else
+				break;
+		}
+	}
+	zfcp_free_sg_env(gpn_ft);
+
+	return ret;
+}
+
+
+void _zfcp_scan_ports_later(struct work_struct *work)
+{
+	zfcp_scan_ports(container_of(work, struct zfcp_adapter, scan_work));
+}
diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c
index b2ea4ea..19c1ca9 100644
--- a/drivers/s390/scsi/zfcp_fsf.c
+++ b/drivers/s390/scsi/zfcp_fsf.c
@@ -1,54 +1,37 @@
 /*
- * This file is part of the zfcp device driver for
- * FCP adapters for IBM System z9 and zSeries.
+ * zfcp device driver
  *
- * (C) Copyright IBM Corp. 2002, 2006
+ * Implementation of FSF commands.
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * Copyright IBM Corporation 2002, 2008
  */
 
 #include "zfcp_ext.h"
 
-static int zfcp_fsf_exchange_config_data_handler(struct zfcp_fsf_req *);
-static void zfcp_fsf_exchange_port_data_handler(struct zfcp_fsf_req *);
-static int zfcp_fsf_open_port_handler(struct zfcp_fsf_req *);
-static int zfcp_fsf_close_port_handler(struct zfcp_fsf_req *);
-static int zfcp_fsf_close_physical_port_handler(struct zfcp_fsf_req *);
-static int zfcp_fsf_open_unit_handler(struct zfcp_fsf_req *);
-static int zfcp_fsf_close_unit_handler(struct zfcp_fsf_req *);
-static int zfcp_fsf_send_fcp_command_handler(struct zfcp_fsf_req *);
-static int zfcp_fsf_send_fcp_command_task_handler(struct zfcp_fsf_req *);
-static int zfcp_fsf_send_fcp_command_task_management_handler(
-	struct zfcp_fsf_req *);
-static int zfcp_fsf_abort_fcp_command_handler(struct zfcp_fsf_req *);
-static int zfcp_fsf_status_read_handler(struct zfcp_fsf_req *);
-static int zfcp_fsf_send_ct_handler(struct zfcp_fsf_req *);
-static int zfcp_fsf_send_els_handler(struct zfcp_fsf_req *);
-static int zfcp_fsf_control_file_handler(struct zfcp_fsf_req *);
-static inline int zfcp_fsf_req_sbal_check(
-	unsigned long *, struct zfcp_qdio_queue *, int);
-static inline int zfcp_use_one_sbal(
-	struct scatterlist *, int, struct scatterlist *, int);
-static struct zfcp_fsf_req *zfcp_fsf_req_alloc(mempool_t *, int);
-static int zfcp_fsf_req_send(struct zfcp_fsf_req *);
-static int zfcp_fsf_protstatus_eval(struct zfcp_fsf_req *);
-static int zfcp_fsf_fsfstatus_eval(struct zfcp_fsf_req *);
-static int zfcp_fsf_fsfstatus_qual_eval(struct zfcp_fsf_req *);
-static void zfcp_fsf_link_down_info_eval(struct zfcp_fsf_req *, u8,
-	struct fsf_link_down_info *);
-static int zfcp_fsf_req_dispatch(struct zfcp_fsf_req *);
+static void zfcp_fsf_request_timeout_handler(unsigned long data)
+{
+	struct zfcp_adapter *adapter = (struct zfcp_adapter *) data;
+	zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED, 62,
+				NULL);
+}
+
+static void zfcp_fsf_start_timer(struct zfcp_fsf_req *fsf_req,
+				 unsigned long timeout)
+{
+	fsf_req->timer.function = zfcp_fsf_request_timeout_handler;
+	fsf_req->timer.data = (unsigned long) fsf_req->adapter;
+	fsf_req->timer.expires = jiffies + timeout;
+	add_timer(&fsf_req->timer);
+}
+
+static void zfcp_fsf_start_erp_timer(struct zfcp_fsf_req *fsf_req)
+{
+	BUG_ON(!fsf_req->erp_action);
+	fsf_req->timer.function = zfcp_erp_timeout_handler;
+	fsf_req->timer.data = (unsigned long) fsf_req->erp_action;
+	fsf_req->timer.expires = jiffies + 30 * HZ;
+	add_timer(&fsf_req->timer);
+}
 
 /* association between FSF command and FSF QTCB type */
 static u32 fsf_qtcb_type[] = {
@@ -67,96 +50,77 @@
 	[FSF_QTCB_UPLOAD_CONTROL_FILE] =  FSF_SUPPORT_COMMAND
 };
 
-static const char zfcp_act_subtable_type[5][8] = {
+static const char *zfcp_act_subtable_type[] = {
 	"unknown", "OS", "WWPN", "DID", "LUN"
 };
 
-/****************************************************************/
-/*************** FSF related Functions  *************************/
-/****************************************************************/
-
-#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_FSF
-
-/*
- * function:	zfcp_fsf_req_alloc
- *
- * purpose:     Obtains an fsf_req and potentially a qtcb (for all but
- *              unsolicited requests) via helper functions
- *              Does some initial fsf request set-up.
- *
- * returns:	pointer to allocated fsf_req if successfull
- *              NULL otherwise
- *
- * locks:       none
- *
- */
-static struct zfcp_fsf_req *
-zfcp_fsf_req_alloc(mempool_t *pool, int req_flags)
+static void zfcp_act_eval_err(struct zfcp_adapter *adapter, u32 table)
 {
-	size_t size;
-	void *ptr;
-	struct zfcp_fsf_req *fsf_req = NULL;
+	u16 subtable = table >> 16;
+	u16 rule = table & 0xffff;
 
-	if (req_flags & ZFCP_REQ_NO_QTCB)
-		size = sizeof(struct zfcp_fsf_req);
-	else
-		size = sizeof(struct zfcp_fsf_req_qtcb);
-
-	if (likely(pool))
-		ptr = mempool_alloc(pool, GFP_ATOMIC);
-	else {
-		if (req_flags & ZFCP_REQ_NO_QTCB)
-			ptr = kmalloc(size, GFP_ATOMIC);
-		else
-			ptr = kmem_cache_alloc(zfcp_data.fsf_req_qtcb_cache,
-					       GFP_ATOMIC);
-	}
-
-	if (unlikely(!ptr))
-		goto out;
-
-	memset(ptr, 0, size);
-
-	if (req_flags & ZFCP_REQ_NO_QTCB) {
-		fsf_req = (struct zfcp_fsf_req *) ptr;
-	} else {
-		fsf_req = &((struct zfcp_fsf_req_qtcb *) ptr)->fsf_req;
-		fsf_req->qtcb =	&((struct zfcp_fsf_req_qtcb *) ptr)->qtcb;
-	}
-
-	fsf_req->pool = pool;
-
- out:
-	return fsf_req;
+	if (subtable && subtable < ARRAY_SIZE(zfcp_act_subtable_type))
+		dev_warn(&adapter->ccw_device->dev,
+			 "Access denied in subtable %s, rule %d.\n",
+			 zfcp_act_subtable_type[subtable], rule);
 }
 
-/*
- * function:	zfcp_fsf_req_free
- *
- * purpose:     Frees the memory of an fsf_req (and potentially a qtcb) or
- *              returns it into the pool via helper functions.
- *
- * returns:     sod all
- *
- * locks:       none
- */
-void
-zfcp_fsf_req_free(struct zfcp_fsf_req *fsf_req)
+static void zfcp_fsf_access_denied_port(struct zfcp_fsf_req *req,
+					struct zfcp_port *port)
 {
-	if (likely(fsf_req->pool)) {
-		mempool_free(fsf_req, fsf_req->pool);
+	struct fsf_qtcb_header *header = &req->qtcb->header;
+	dev_warn(&req->adapter->ccw_device->dev,
+		 "Access denied, cannot send command to port 0x%016Lx.\n",
+		 port->wwpn);
+	zfcp_act_eval_err(req->adapter, header->fsf_status_qual.halfword[0]);
+	zfcp_act_eval_err(req->adapter, header->fsf_status_qual.halfword[1]);
+	zfcp_erp_port_access_denied(port, 55, req);
+	req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+}
+
+static void zfcp_fsf_access_denied_unit(struct zfcp_fsf_req *req,
+					struct zfcp_unit *unit)
+{
+	struct fsf_qtcb_header *header = &req->qtcb->header;
+	dev_warn(&req->adapter->ccw_device->dev,
+		 "Access denied for unit 0x%016Lx on port 0x%016Lx.\n",
+		 unit->fcp_lun, unit->port->wwpn);
+	zfcp_act_eval_err(req->adapter, header->fsf_status_qual.halfword[0]);
+	zfcp_act_eval_err(req->adapter, header->fsf_status_qual.halfword[1]);
+	zfcp_erp_unit_access_denied(unit, 59, req);
+	req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+}
+
+static void zfcp_fsf_class_not_supp(struct zfcp_fsf_req *req)
+{
+	dev_err(&req->adapter->ccw_device->dev,
+		"Required FC class not supported by adapter, "
+		"shutting down adapter.\n");
+	zfcp_erp_adapter_shutdown(req->adapter, 0, 123, req);
+	req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+}
+
+/**
+ * zfcp_fsf_req_free - free memory used by fsf request
+ * @fsf_req: pointer to struct zfcp_fsf_req
+ */
+void zfcp_fsf_req_free(struct zfcp_fsf_req *req)
+{
+	if (likely(req->pool)) {
+		mempool_free(req, req->pool);
 		return;
 	}
 
-	if (fsf_req->qtcb) {
-		kmem_cache_free(zfcp_data.fsf_req_qtcb_cache, fsf_req);
+	if (req->qtcb) {
+		kmem_cache_free(zfcp_data.fsf_req_qtcb_cache, req);
 		return;
 	}
-
-	kfree(fsf_req);
 }
 
-/*
+/**
+ * zfcp_fsf_req_dismiss_all - dismiss all fsf requests
+ * @adapter: pointer to struct zfcp_adapter
+ *
  * Never ever call this without shutting down the adapter first.
  * Otherwise the adapter would continue using and corrupting s390 storage.
  * Included BUG_ON() call to ensure this is done.
@@ -164,1827 +128,480 @@
  */
 void zfcp_fsf_req_dismiss_all(struct zfcp_adapter *adapter)
 {
-	struct zfcp_fsf_req *fsf_req, *tmp;
+	struct zfcp_fsf_req *req, *tmp;
 	unsigned long flags;
 	LIST_HEAD(remove_queue);
 	unsigned int i;
 
-	BUG_ON(atomic_test_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &adapter->status));
+	BUG_ON(atomic_read(&adapter->status) & ZFCP_STATUS_ADAPTER_QDIOUP);
 	spin_lock_irqsave(&adapter->req_list_lock, flags);
-	atomic_set(&adapter->reqs_active, 0);
 	for (i = 0; i < REQUEST_LIST_SIZE; i++)
 		list_splice_init(&adapter->req_list[i], &remove_queue);
 	spin_unlock_irqrestore(&adapter->req_list_lock, flags);
 
-	list_for_each_entry_safe(fsf_req, tmp, &remove_queue, list) {
-		list_del(&fsf_req->list);
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_DISMISSED;
-		zfcp_fsf_req_complete(fsf_req);
+	list_for_each_entry_safe(req, tmp, &remove_queue, list) {
+		list_del(&req->list);
+		req->status |= ZFCP_STATUS_FSFREQ_DISMISSED;
+		zfcp_fsf_req_complete(req);
 	}
 }
 
-/*
- * function:    zfcp_fsf_req_complete
- *
- * purpose:	Updates active counts and timers for openfcp-reqs
- *              May cleanup request after req_eval returns
- *
- * returns:	0 - success
- *		!0 - failure
- *
- * context:
- */
-int
-zfcp_fsf_req_complete(struct zfcp_fsf_req *fsf_req)
+static void zfcp_fsf_status_read_port_closed(struct zfcp_fsf_req *req)
 {
-	int retval = 0;
-	int cleanup;
+	struct fsf_status_read_buffer *sr_buf = req->data;
+	struct zfcp_adapter *adapter = req->adapter;
+	struct zfcp_port *port;
+	int d_id = sr_buf->d_id & ZFCP_DID_MASK;
+	unsigned long flags;
 
-	if (unlikely(fsf_req->fsf_command == FSF_QTCB_UNSOLICITED_STATUS)) {
-		ZFCP_LOG_DEBUG("Status read response received\n");
-		/*
-		 * Note: all cleanup handling is done in the callchain of
-		 * the function call-chain below.
-		 */
-		zfcp_fsf_status_read_handler(fsf_req);
-		goto out;
-	} else {
-		del_timer(&fsf_req->timer);
-		zfcp_fsf_protstatus_eval(fsf_req);
-	}
-
-	/*
-	 * fsf_req may be deleted due to waking up functions, so
-	 * cleanup is saved here and used later
-	 */
-	if (likely(fsf_req->status & ZFCP_STATUS_FSFREQ_CLEANUP))
-		cleanup = 1;
-	else
-		cleanup = 0;
-
-	fsf_req->status |= ZFCP_STATUS_FSFREQ_COMPLETED;
-
-	/* cleanup request if requested by initiator */
-	if (likely(cleanup)) {
-		ZFCP_LOG_TRACE("removing FSF request %p\n", fsf_req);
-		/*
-		 * lock must not be held here since it will be
-		 * grabed by the called routine, too
-		 */
-		zfcp_fsf_req_free(fsf_req);
-	} else {
-		/* notify initiator waiting for the requests completion */
-		ZFCP_LOG_TRACE("waking initiator of FSF request %p\n",fsf_req);
-		/*
-		 * FIXME: Race! We must not access fsf_req here as it might have been
-		 * cleaned up already due to the set ZFCP_STATUS_FSFREQ_COMPLETED
-		 * flag. It's an improbable case. But, we have the same paranoia for
-		 * the cleanup flag already.
-		 * Might better be handled using complete()?
-		 * (setting the flag and doing wakeup ought to be atomic
-		 *  with regard to checking the flag as long as waitqueue is
-		 *  part of the to be released structure)
-		 */
-		wake_up(&fsf_req->completion_wq);
-	}
-
- out:
-	return retval;
+	read_lock_irqsave(&zfcp_data.config_lock, flags);
+	list_for_each_entry(port, &adapter->port_list_head, list)
+		if (port->d_id == d_id) {
+			read_unlock_irqrestore(&zfcp_data.config_lock, flags);
+			switch (sr_buf->status_subtype) {
+			case FSF_STATUS_READ_SUB_CLOSE_PHYS_PORT:
+				zfcp_erp_port_reopen(port, 0, 101, req);
+				break;
+			case FSF_STATUS_READ_SUB_ERROR_PORT:
+				zfcp_erp_port_shutdown(port, 0, 122, req);
+				break;
+			}
+			return;
+		}
+	read_unlock_irqrestore(&zfcp_data.config_lock, flags);
 }
 
-/*
- * function:    zfcp_fsf_protstatus_eval
- *
- * purpose:	evaluates the QTCB of the finished FSF request
- *		and initiates appropriate actions
- *		(usually calling FSF command specific handlers)
- *
- * returns:
- *
- * context:
- *
- * locks:
- */
-static int
-zfcp_fsf_protstatus_eval(struct zfcp_fsf_req *fsf_req)
+static void zfcp_fsf_bit_error_threshold(struct zfcp_fsf_req *req)
 {
-	int retval = 0;
-	struct zfcp_adapter *adapter = fsf_req->adapter;
-	struct fsf_qtcb *qtcb = fsf_req->qtcb;
-	union fsf_prot_status_qual *prot_status_qual =
-		&qtcb->prefix.prot_status_qual;
+	struct zfcp_adapter *adapter = req->adapter;
+	struct fsf_status_read_buffer *sr_buf = req->data;
+	struct fsf_bit_error_payload *err = &sr_buf->payload.bit_error;
 
-	zfcp_hba_dbf_event_fsf_response(fsf_req);
-
-	if (fsf_req->status & ZFCP_STATUS_FSFREQ_DISMISSED) {
-		ZFCP_LOG_DEBUG("fsf_req 0x%lx has been dismissed\n",
-			       (unsigned long) fsf_req);
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR |
-			ZFCP_STATUS_FSFREQ_RETRY; /* only for SCSI cmnds. */
-		goto skip_protstatus;
-	}
-
-	/* evaluate FSF Protocol Status */
-	switch (qtcb->prefix.prot_status) {
-
-	case FSF_PROT_GOOD:
-	case FSF_PROT_FSF_STATUS_PRESENTED:
-		break;
-
-	case FSF_PROT_QTCB_VERSION_ERROR:
-		ZFCP_LOG_NORMAL("error: The adapter %s contains "
-				"microcode of version 0x%x, the device driver "
-				"only supports 0x%x. Aborting.\n",
-				zfcp_get_busid_by_adapter(adapter),
-				prot_status_qual->version_error.fsf_version,
-				ZFCP_QTCB_VERSION);
-		zfcp_erp_adapter_shutdown(adapter, 0, 117, fsf_req);
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-		break;
-
-	case FSF_PROT_SEQ_NUMB_ERROR:
-		ZFCP_LOG_NORMAL("bug: Sequence number mismatch between "
-				"driver (0x%x) and adapter %s (0x%x). "
-				"Restarting all operations on this adapter.\n",
-				qtcb->prefix.req_seq_no,
-				zfcp_get_busid_by_adapter(adapter),
-				prot_status_qual->sequence_error.exp_req_seq_no);
-		zfcp_erp_adapter_reopen(adapter, 0, 98, fsf_req);
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_RETRY;
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-		break;
-
-	case FSF_PROT_UNSUPP_QTCB_TYPE:
-		ZFCP_LOG_NORMAL("error: Packet header type used by the "
-				"device driver is incompatible with "
-				"that used on adapter %s. "
-				"Stopping all operations on this adapter.\n",
-				zfcp_get_busid_by_adapter(adapter));
-		zfcp_erp_adapter_shutdown(adapter, 0, 118, fsf_req);
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-		break;
-
-	case FSF_PROT_HOST_CONNECTION_INITIALIZING:
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-		atomic_set_mask(ZFCP_STATUS_ADAPTER_HOST_CON_INIT,
-				&(adapter->status));
-		break;
-
-	case FSF_PROT_DUPLICATE_REQUEST_ID:
-			ZFCP_LOG_NORMAL("bug: The request identifier 0x%Lx "
-					"to the adapter %s is ambiguous. "
-				"Stopping all operations on this adapter.\n",
-				*(unsigned long long*)
-				(&qtcb->bottom.support.req_handle),
-					zfcp_get_busid_by_adapter(adapter));
-		zfcp_erp_adapter_shutdown(adapter, 0, 78, fsf_req);
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-		break;
-
-	case FSF_PROT_LINK_DOWN:
-		zfcp_fsf_link_down_info_eval(fsf_req, 37,
-					     &prot_status_qual->link_down_info);
-		/* FIXME: reopening adapter now? better wait for link up */
-		zfcp_erp_adapter_reopen(adapter, 0, 79, fsf_req);
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-		break;
-
-	case FSF_PROT_REEST_QUEUE:
-		ZFCP_LOG_NORMAL("The local link to adapter with "
-			      "%s was re-plugged. "
-			      "Re-starting operations on this adapter.\n",
-			      zfcp_get_busid_by_adapter(adapter));
-		/* All ports should be marked as ready to run again */
-		zfcp_erp_modify_adapter_status(adapter, 28, NULL,
-					       ZFCP_STATUS_COMMON_RUNNING,
-					       ZFCP_SET);
-		zfcp_erp_adapter_reopen(adapter,
-					ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED
-					| ZFCP_STATUS_COMMON_ERP_FAILED,
-					99, fsf_req);
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-		break;
-
-	case FSF_PROT_ERROR_STATE:
-		ZFCP_LOG_NORMAL("error: The adapter %s "
-				"has entered the error state. "
-				"Restarting all operations on this "
-				"adapter.\n",
-				zfcp_get_busid_by_adapter(adapter));
-		zfcp_erp_adapter_reopen(adapter, 0, 100, fsf_req);
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_RETRY;
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-		break;
-
-	default:
-		ZFCP_LOG_NORMAL("bug: Transfer protocol status information "
-				"provided by the adapter %s "
-				"is not compatible with the device driver. "
-				"Stopping all operations on this adapter. "
-				"(debug info 0x%x).\n",
-				zfcp_get_busid_by_adapter(adapter),
-				qtcb->prefix.prot_status);
-		zfcp_erp_adapter_shutdown(adapter, 0, 119, fsf_req);
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-	}
-
- skip_protstatus:
-	/*
-	 * always call specific handlers to give them a chance to do
-	 * something meaningful even in error cases
-	 */
-	zfcp_fsf_fsfstatus_eval(fsf_req);
-	return retval;
+	dev_warn(&adapter->ccw_device->dev,
+		 "Warning: bit error threshold data "
+		 "received for the adapter: "
+		 "link failures = %i, loss of sync errors = %i, "
+		 "loss of signal errors = %i, "
+		 "primitive sequence errors = %i, "
+		 "invalid transmission word errors = %i, "
+		 "CRC errors = %i).\n",
+		 err->link_failure_error_count,
+		 err->loss_of_sync_error_count,
+		 err->loss_of_signal_error_count,
+		 err->primitive_sequence_error_count,
+		 err->invalid_transmission_word_error_count,
+		 err->crc_error_count);
+	dev_warn(&adapter->ccw_device->dev,
+		 "Additional bit error threshold data of the adapter: "
+		 "primitive sequence event time-outs = %i, "
+		 "elastic buffer overrun errors = %i, "
+		 "advertised receive buffer-to-buffer credit = %i, "
+		 "current receice buffer-to-buffer credit = %i, "
+		 "advertised transmit buffer-to-buffer credit = %i, "
+		 "current transmit buffer-to-buffer credit = %i).\n",
+		 err->primitive_sequence_event_timeout_count,
+		 err->elastic_buffer_overrun_error_count,
+		 err->advertised_receive_b2b_credit,
+		 err->current_receive_b2b_credit,
+		 err->advertised_transmit_b2b_credit,
+		 err->current_transmit_b2b_credit);
 }
 
-/*
- * function:	zfcp_fsf_fsfstatus_eval
- *
- * purpose:	evaluates FSF status of completed FSF request
- *		and acts accordingly
- *
- * returns:
- */
-static int
-zfcp_fsf_fsfstatus_eval(struct zfcp_fsf_req *fsf_req)
+static void zfcp_fsf_link_down_info_eval(struct zfcp_fsf_req *req, u8 id,
+					 struct fsf_link_down_info *link_down)
 {
-	int retval = 0;
+	struct zfcp_adapter *adapter = req->adapter;
 
-	if (unlikely(fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR)) {
-		goto skip_fsfstatus;
-	}
-
-	/* evaluate FSF Status */
-	switch (fsf_req->qtcb->header.fsf_status) {
-	case FSF_UNKNOWN_COMMAND:
-		ZFCP_LOG_NORMAL("bug: Command issued by the device driver is "
-				"not known by the adapter %s "
-				"Stopping all operations on this adapter. "
-				"(debug info 0x%x).\n",
-				zfcp_get_busid_by_adapter(fsf_req->adapter),
-				fsf_req->qtcb->header.fsf_command);
-		zfcp_erp_adapter_shutdown(fsf_req->adapter, 0, 120, fsf_req);
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-		break;
-
-	case FSF_FCP_RSP_AVAILABLE:
-		ZFCP_LOG_DEBUG("FCP Sense data will be presented to the "
-			       "SCSI stack.\n");
-		break;
-
-	case FSF_ADAPTER_STATUS_AVAILABLE:
-		zfcp_fsf_fsfstatus_qual_eval(fsf_req);
-		break;
-	}
-
- skip_fsfstatus:
-	/*
-	 * always call specific handlers to give them a chance to do
-	 * something meaningful even in error cases
-	 */
-	zfcp_fsf_req_dispatch(fsf_req);
-
-	return retval;
-}
-
-/*
- * function:	zfcp_fsf_fsfstatus_qual_eval
- *
- * purpose:	evaluates FSF status-qualifier of completed FSF request
- *		and acts accordingly
- *
- * returns:
- */
-static int
-zfcp_fsf_fsfstatus_qual_eval(struct zfcp_fsf_req *fsf_req)
-{
-	int retval = 0;
-
-	switch (fsf_req->qtcb->header.fsf_status_qual.word[0]) {
-	case FSF_SQ_FCP_RSP_AVAILABLE:
-		break;
-	case FSF_SQ_RETRY_IF_POSSIBLE:
-		/* The SCSI-stack may now issue retries or escalate */
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-		break;
-	case FSF_SQ_COMMAND_ABORTED:
-		/* Carry the aborted state on to upper layer */
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ABORTED;
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-		break;
-	case FSF_SQ_NO_RECOM:
-		ZFCP_LOG_NORMAL("bug: No recommendation could be given for a "
-				"problem on the adapter %s "
-				"Stopping all operations on this adapter. ",
-				zfcp_get_busid_by_adapter(fsf_req->adapter));
-		zfcp_erp_adapter_shutdown(fsf_req->adapter, 0, 121, fsf_req);
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-		break;
-	case FSF_SQ_ULP_PROGRAMMING_ERROR:
-		ZFCP_LOG_NORMAL("error: not enough SBALs for data transfer "
-				"(adapter %s)\n",
-				zfcp_get_busid_by_adapter(fsf_req->adapter));
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-		break;
-	case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE:
-	case FSF_SQ_NO_RETRY_POSSIBLE:
-	case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED:
-		/* dealt with in the respective functions */
-		break;
-	default:
-		ZFCP_LOG_NORMAL("bug: Additional status info could "
-				"not be interpreted properly.\n");
-		ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_NORMAL,
-			      (char *) &fsf_req->qtcb->header.fsf_status_qual,
-			      sizeof (union fsf_status_qual));
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-		break;
-	}
-
-	return retval;
-}
-
-/**
- * zfcp_fsf_link_down_info_eval - evaluate link down information block
- */
-static void
-zfcp_fsf_link_down_info_eval(struct zfcp_fsf_req *fsf_req, u8 id,
-			     struct fsf_link_down_info *link_down)
-{
-	struct zfcp_adapter *adapter = fsf_req->adapter;
-
-	if (atomic_test_mask(ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED,
-	                     &adapter->status))
+	if (atomic_read(&adapter->status) & ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED)
 		return;
 
 	atomic_set_mask(ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED, &adapter->status);
 
-	if (link_down == NULL)
+	if (!link_down)
 		goto out;
 
 	switch (link_down->error_code) {
 	case FSF_PSQ_LINK_NO_LIGHT:
-		ZFCP_LOG_NORMAL("The local link to adapter %s is down "
-				"(no light detected)\n",
-				zfcp_get_busid_by_adapter(adapter));
+		dev_warn(&req->adapter->ccw_device->dev,
+			 "The local link is down: no light detected.\n");
 		break;
 	case FSF_PSQ_LINK_WRAP_PLUG:
-		ZFCP_LOG_NORMAL("The local link to adapter %s is down "
-				"(wrap plug detected)\n",
-				zfcp_get_busid_by_adapter(adapter));
+		dev_warn(&req->adapter->ccw_device->dev,
+			 "The local link is down: wrap plug detected.\n");
 		break;
 	case FSF_PSQ_LINK_NO_FCP:
-		ZFCP_LOG_NORMAL("The local link to adapter %s is down "
-				"(adjacent node on link does not support FCP)\n",
-				zfcp_get_busid_by_adapter(adapter));
+		dev_warn(&req->adapter->ccw_device->dev,
+			 "The local link is down: "
+			 "adjacent node on link does not support FCP.\n");
 		break;
 	case FSF_PSQ_LINK_FIRMWARE_UPDATE:
-		ZFCP_LOG_NORMAL("The local link to adapter %s is down "
-				"(firmware update in progress)\n",
-				zfcp_get_busid_by_adapter(adapter));
-			break;
+		dev_warn(&req->adapter->ccw_device->dev,
+			 "The local link is down: "
+			 "firmware update in progress.\n");
+		break;
 	case FSF_PSQ_LINK_INVALID_WWPN:
-		ZFCP_LOG_NORMAL("The local link to adapter %s is down "
-				"(duplicate or invalid WWPN detected)\n",
-				zfcp_get_busid_by_adapter(adapter));
+		dev_warn(&req->adapter->ccw_device->dev,
+			 "The local link is down: "
+			 "duplicate or invalid WWPN detected.\n");
 		break;
 	case FSF_PSQ_LINK_NO_NPIV_SUPPORT:
-		ZFCP_LOG_NORMAL("The local link to adapter %s is down "
-				"(no support for NPIV by Fabric)\n",
-				zfcp_get_busid_by_adapter(adapter));
+		dev_warn(&req->adapter->ccw_device->dev,
+			 "The local link is down: "
+			 "no support for NPIV by Fabric.\n");
 		break;
 	case FSF_PSQ_LINK_NO_FCP_RESOURCES:
-		ZFCP_LOG_NORMAL("The local link to adapter %s is down "
-				"(out of resource in FCP daughtercard)\n",
-				zfcp_get_busid_by_adapter(adapter));
+		dev_warn(&req->adapter->ccw_device->dev,
+			 "The local link is down: "
+			 "out of resource in FCP daughtercard.\n");
 		break;
 	case FSF_PSQ_LINK_NO_FABRIC_RESOURCES:
-		ZFCP_LOG_NORMAL("The local link to adapter %s is down "
-				"(out of resource in Fabric)\n",
-				zfcp_get_busid_by_adapter(adapter));
+		dev_warn(&req->adapter->ccw_device->dev,
+			 "The local link is down: "
+			 "out of resource in Fabric.\n");
 		break;
 	case FSF_PSQ_LINK_FABRIC_LOGIN_UNABLE:
-		ZFCP_LOG_NORMAL("The local link to adapter %s is down "
-				"(unable to Fabric login)\n",
-				zfcp_get_busid_by_adapter(adapter));
+		dev_warn(&req->adapter->ccw_device->dev,
+			 "The local link is down: "
+			 "unable to login to Fabric.\n");
 		break;
 	case FSF_PSQ_LINK_WWPN_ASSIGNMENT_CORRUPTED:
-		ZFCP_LOG_NORMAL("WWPN assignment file corrupted on adapter %s\n",
-				zfcp_get_busid_by_adapter(adapter));
+		dev_warn(&req->adapter->ccw_device->dev,
+			 "WWPN assignment file corrupted on adapter.\n");
 		break;
 	case FSF_PSQ_LINK_MODE_TABLE_CURRUPTED:
-		ZFCP_LOG_NORMAL("Mode table corrupted on adapter %s\n",
-				zfcp_get_busid_by_adapter(adapter));
+		dev_warn(&req->adapter->ccw_device->dev,
+			 "Mode table corrupted on adapter.\n");
 		break;
 	case FSF_PSQ_LINK_NO_WWPN_ASSIGNMENT:
-		ZFCP_LOG_NORMAL("No WWPN for assignment table on adapter %s\n",
-				zfcp_get_busid_by_adapter(adapter));
+		dev_warn(&req->adapter->ccw_device->dev,
+			 "No WWPN for assignment table on adapter.\n");
 		break;
 	default:
-		ZFCP_LOG_NORMAL("The local link to adapter %s is down "
-				"(warning: unknown reason code %d)\n",
-				zfcp_get_busid_by_adapter(adapter),
-				link_down->error_code);
+		dev_warn(&req->adapter->ccw_device->dev,
+			 "The local link to adapter is down.\n");
 	}
-
-	if (adapter->connection_features & FSF_FEATURE_NPIV_MODE)
-		ZFCP_LOG_DEBUG("Debug information to link down: "
-		               "primary_status=0x%02x "
-		               "ioerr_code=0x%02x "
-		               "action_code=0x%02x "
-		               "reason_code=0x%02x "
-		               "explanation_code=0x%02x "
-		               "vendor_specific_code=0x%02x\n",
-				link_down->primary_status,
-				link_down->ioerr_code,
-				link_down->action_code,
-				link_down->reason_code,
-				link_down->explanation_code,
-				link_down->vendor_specific_code);
-
- out:
-	zfcp_erp_adapter_failed(adapter, id, fsf_req);
+out:
+	zfcp_erp_adapter_failed(adapter, id, req);
 }
 
-/*
- * function:	zfcp_fsf_req_dispatch
- *
- * purpose:	calls the appropriate command specific handler
- *
- * returns:
- */
-static int
-zfcp_fsf_req_dispatch(struct zfcp_fsf_req *fsf_req)
+static void zfcp_fsf_status_read_link_down(struct zfcp_fsf_req *req)
 {
-	struct zfcp_erp_action *erp_action = fsf_req->erp_action;
-	struct zfcp_adapter *adapter = fsf_req->adapter;
-	int retval = 0;
+	struct zfcp_adapter *adapter = req->adapter;
+	struct fsf_status_read_buffer *sr_buf = req->data;
+	struct fsf_link_down_info *ldi =
+		(struct fsf_link_down_info *) &sr_buf->payload;
 
-
-	switch (fsf_req->fsf_command) {
-
-	case FSF_QTCB_FCP_CMND:
-		zfcp_fsf_send_fcp_command_handler(fsf_req);
+	switch (sr_buf->status_subtype) {
+	case FSF_STATUS_READ_SUB_NO_PHYSICAL_LINK:
+		dev_warn(&adapter->ccw_device->dev,
+			 "Physical link is down.\n");
+		zfcp_fsf_link_down_info_eval(req, 38, ldi);
 		break;
-
-	case FSF_QTCB_ABORT_FCP_CMND:
-		zfcp_fsf_abort_fcp_command_handler(fsf_req);
+	case FSF_STATUS_READ_SUB_FDISC_FAILED:
+		dev_warn(&adapter->ccw_device->dev,
+			 "Local link is down "
+			 "due to failed FDISC login.\n");
+		zfcp_fsf_link_down_info_eval(req, 39, ldi);
 		break;
-
-	case FSF_QTCB_SEND_GENERIC:
-		zfcp_fsf_send_ct_handler(fsf_req);
-		break;
-
-	case FSF_QTCB_OPEN_PORT_WITH_DID:
-		zfcp_fsf_open_port_handler(fsf_req);
-		break;
-
-	case FSF_QTCB_OPEN_LUN:
-		zfcp_fsf_open_unit_handler(fsf_req);
-		break;
-
-	case FSF_QTCB_CLOSE_LUN:
-		zfcp_fsf_close_unit_handler(fsf_req);
-		break;
-
-	case FSF_QTCB_CLOSE_PORT:
-		zfcp_fsf_close_port_handler(fsf_req);
-		break;
-
-	case FSF_QTCB_CLOSE_PHYSICAL_PORT:
-		zfcp_fsf_close_physical_port_handler(fsf_req);
-		break;
-
-	case FSF_QTCB_EXCHANGE_CONFIG_DATA:
-		zfcp_fsf_exchange_config_data_handler(fsf_req);
-		break;
-
-	case FSF_QTCB_EXCHANGE_PORT_DATA:
-		zfcp_fsf_exchange_port_data_handler(fsf_req);
-		break;
-
-	case FSF_QTCB_SEND_ELS:
-		zfcp_fsf_send_els_handler(fsf_req);
-		break;
-
-	case FSF_QTCB_DOWNLOAD_CONTROL_FILE:
-		zfcp_fsf_control_file_handler(fsf_req);
-		break;
-
-	case FSF_QTCB_UPLOAD_CONTROL_FILE:
-		zfcp_fsf_control_file_handler(fsf_req);
-		break;
-
-	default:
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-		ZFCP_LOG_NORMAL("bug: Command issued by the device driver is "
-				"not supported by the adapter %s\n",
-				zfcp_get_busid_by_adapter(adapter));
-		if (fsf_req->fsf_command != fsf_req->qtcb->header.fsf_command)
-			ZFCP_LOG_NORMAL
-			    ("bug: Command issued by the device driver differs "
-			     "from the command returned by the adapter %s "
-			     "(debug info 0x%x, 0x%x).\n",
-			     zfcp_get_busid_by_adapter(adapter),
-			     fsf_req->fsf_command,
-			     fsf_req->qtcb->header.fsf_command);
-	}
-
-	if (!erp_action)
-		return retval;
-
-	zfcp_erp_async_handler(erp_action, 0);
-
-	return retval;
+	case FSF_STATUS_READ_SUB_FIRMWARE_UPDATE:
+		dev_warn(&adapter->ccw_device->dev,
+			 "Local link is down "
+			 "due to firmware update on adapter.\n");
+		zfcp_fsf_link_down_info_eval(req, 40, NULL);
+	};
 }
 
-/*
- * function:    zfcp_fsf_status_read
- *
- * purpose:	initiates a Status Read command at the specified adapter
- *
- * returns:
- */
-int
-zfcp_fsf_status_read(struct zfcp_adapter *adapter, int req_flags)
+static void zfcp_fsf_status_read_handler(struct zfcp_fsf_req *req)
 {
-	struct zfcp_fsf_req *fsf_req;
-	struct fsf_status_read_buffer *status_buffer;
-	unsigned long lock_flags;
-	volatile struct qdio_buffer_element *sbale;
-	int retval = 0;
+	struct zfcp_adapter *adapter = req->adapter;
+	struct fsf_status_read_buffer *sr_buf = req->data;
 
-	/* setup new FSF request */
-	retval = zfcp_fsf_req_create(adapter, FSF_QTCB_UNSOLICITED_STATUS,
-				     req_flags | ZFCP_REQ_NO_QTCB,
-				     adapter->pool.fsf_req_status_read,
-				     &lock_flags, &fsf_req);
-	if (retval < 0) {
-		ZFCP_LOG_INFO("error: Could not create unsolicited status "
-			      "buffer for adapter %s.\n",
-			      zfcp_get_busid_by_adapter(adapter));
-		goto failed_req_create;
+	if (req->status & ZFCP_STATUS_FSFREQ_DISMISSED) {
+		zfcp_hba_dbf_event_fsf_unsol("dism", adapter, sr_buf);
+		mempool_free(sr_buf, adapter->pool.data_status_read);
+		zfcp_fsf_req_free(req);
+		return;
 	}
 
-	sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
-        sbale[0].flags |= SBAL_FLAGS0_TYPE_STATUS;
-        sbale[2].flags |= SBAL_FLAGS_LAST_ENTRY;
-        fsf_req->sbale_curr = 2;
+	zfcp_hba_dbf_event_fsf_unsol("read", adapter, sr_buf);
 
-	status_buffer =
-		mempool_alloc(adapter->pool.data_status_read, GFP_ATOMIC);
-	if (!status_buffer) {
-		ZFCP_LOG_NORMAL("bug: could not get some buffer\n");
-		goto failed_buf;
-	}
-	memset(status_buffer, 0, sizeof (struct fsf_status_read_buffer));
-	fsf_req->data = (unsigned long) status_buffer;
-
-	/* insert pointer to respective buffer */
-	sbale = zfcp_qdio_sbale_curr(fsf_req);
-	sbale->addr = (void *) status_buffer;
-	sbale->length = sizeof(struct fsf_status_read_buffer);
-
-	retval = zfcp_fsf_req_send(fsf_req);
-	if (retval) {
-		ZFCP_LOG_DEBUG("error: Could not set-up unsolicited status "
-			       "environment.\n");
-		goto failed_req_send;
-	}
-
-	ZFCP_LOG_TRACE("Status Read request initiated (adapter%s)\n",
-		       zfcp_get_busid_by_adapter(adapter));
-	goto out;
-
- failed_req_send:
-	mempool_free(status_buffer, adapter->pool.data_status_read);
-
- failed_buf:
-	zfcp_fsf_req_free(fsf_req);
- failed_req_create:
-	zfcp_hba_dbf_event_fsf_unsol("fail", adapter, NULL);
- out:
-	write_unlock_irqrestore(&adapter->request_queue.queue_lock, lock_flags);
-	return retval;
-}
-
-static int
-zfcp_fsf_status_read_port_closed(struct zfcp_fsf_req *fsf_req)
-{
-	struct fsf_status_read_buffer *status_buffer;
-	struct zfcp_adapter *adapter;
-	struct zfcp_port *port;
-	unsigned long flags;
-
-	status_buffer = (struct fsf_status_read_buffer *) fsf_req->data;
-	adapter = fsf_req->adapter;
-
-	read_lock_irqsave(&zfcp_data.config_lock, flags);
-	list_for_each_entry(port, &adapter->port_list_head, list)
-	    if (port->d_id == (status_buffer->d_id & ZFCP_DID_MASK))
-		break;
-	read_unlock_irqrestore(&zfcp_data.config_lock, flags);
-
-	if (!port || (port->d_id != (status_buffer->d_id & ZFCP_DID_MASK))) {
-		ZFCP_LOG_NORMAL("bug: Reopen port indication received for "
-				"nonexisting port with d_id 0x%06x on "
-				"adapter %s. Ignored.\n",
-				status_buffer->d_id & ZFCP_DID_MASK,
-				zfcp_get_busid_by_adapter(adapter));
-		goto out;
-	}
-
-	switch (status_buffer->status_subtype) {
-
-	case FSF_STATUS_READ_SUB_CLOSE_PHYS_PORT:
-		zfcp_erp_port_reopen(port, 0, 101, fsf_req);
-		break;
-
-	case FSF_STATUS_READ_SUB_ERROR_PORT:
-		zfcp_erp_port_shutdown(port, 0, 122, fsf_req);
-		break;
-
-	default:
-		ZFCP_LOG_NORMAL("bug: Undefined status subtype received "
-				"for a reopen indication on port with "
-				"d_id 0x%06x on the adapter %s. "
-				"Ignored. (debug info 0x%x)\n",
-				status_buffer->d_id,
-				zfcp_get_busid_by_adapter(adapter),
-				status_buffer->status_subtype);
-	}
- out:
-	return 0;
-}
-
-/*
- * function:    zfcp_fsf_status_read_handler
- *
- * purpose:	is called for finished Open Port command
- *
- * returns:
- */
-static int
-zfcp_fsf_status_read_handler(struct zfcp_fsf_req *fsf_req)
-{
-	int retval = 0;
-	struct zfcp_adapter *adapter = fsf_req->adapter;
-	struct fsf_status_read_buffer *status_buffer =
-		(struct fsf_status_read_buffer *) fsf_req->data;
-	struct fsf_bit_error_payload *fsf_bit_error;
-
-	if (fsf_req->status & ZFCP_STATUS_FSFREQ_DISMISSED) {
-		zfcp_hba_dbf_event_fsf_unsol("dism", adapter, status_buffer);
-		mempool_free(status_buffer, adapter->pool.data_status_read);
-		zfcp_fsf_req_free(fsf_req);
-		goto out;
-	}
-
-	zfcp_hba_dbf_event_fsf_unsol("read", adapter, status_buffer);
-
-	switch (status_buffer->status_type) {
-
+	switch (sr_buf->status_type) {
 	case FSF_STATUS_READ_PORT_CLOSED:
-		zfcp_fsf_status_read_port_closed(fsf_req);
+		zfcp_fsf_status_read_port_closed(req);
 		break;
-
 	case FSF_STATUS_READ_INCOMING_ELS:
-		zfcp_fsf_incoming_els(fsf_req);
+		zfcp_fc_incoming_els(req);
 		break;
-
 	case FSF_STATUS_READ_SENSE_DATA_AVAIL:
-		ZFCP_LOG_INFO("unsolicited sense data received (adapter %s)\n",
-			      zfcp_get_busid_by_adapter(adapter));
 		break;
-
 	case FSF_STATUS_READ_BIT_ERROR_THRESHOLD:
-		fsf_bit_error = (struct fsf_bit_error_payload *)
-			status_buffer->payload;
-		ZFCP_LOG_NORMAL("Warning: bit error threshold data "
-		    "received (adapter %s, "
-		    "link failures = %i, loss of sync errors = %i, "
-		    "loss of signal errors = %i, "
-		    "primitive sequence errors = %i, "
-		    "invalid transmission word errors = %i, "
-		    "CRC errors = %i)\n",
-		    zfcp_get_busid_by_adapter(adapter),
-		    fsf_bit_error->link_failure_error_count,
-		    fsf_bit_error->loss_of_sync_error_count,
-		    fsf_bit_error->loss_of_signal_error_count,
-		    fsf_bit_error->primitive_sequence_error_count,
-		    fsf_bit_error->invalid_transmission_word_error_count,
-		    fsf_bit_error->crc_error_count);
-		ZFCP_LOG_INFO("Additional bit error threshold data "
-		    "(adapter %s, "
-		    "primitive sequence event time-outs = %i, "
-		    "elastic buffer overrun errors = %i, "
-		    "advertised receive buffer-to-buffer credit = %i, "
-		    "current receice buffer-to-buffer credit = %i, "
-		    "advertised transmit buffer-to-buffer credit = %i, "
-		    "current transmit buffer-to-buffer credit = %i)\n",
-		    zfcp_get_busid_by_adapter(adapter),
-		    fsf_bit_error->primitive_sequence_event_timeout_count,
-		    fsf_bit_error->elastic_buffer_overrun_error_count,
-		    fsf_bit_error->advertised_receive_b2b_credit,
-		    fsf_bit_error->current_receive_b2b_credit,
-		    fsf_bit_error->advertised_transmit_b2b_credit,
-		    fsf_bit_error->current_transmit_b2b_credit);
+		zfcp_fsf_bit_error_threshold(req);
 		break;
-
 	case FSF_STATUS_READ_LINK_DOWN:
-		switch (status_buffer->status_subtype) {
-		case FSF_STATUS_READ_SUB_NO_PHYSICAL_LINK:
-			ZFCP_LOG_INFO("Physical link to adapter %s is down\n",
-				      zfcp_get_busid_by_adapter(adapter));
-			zfcp_fsf_link_down_info_eval(fsf_req, 38,
-				(struct fsf_link_down_info *)
-				&status_buffer->payload);
-			break;
-		case FSF_STATUS_READ_SUB_FDISC_FAILED:
-			ZFCP_LOG_INFO("Local link to adapter %s is down "
-				      "due to failed FDISC login\n",
-				      zfcp_get_busid_by_adapter(adapter));
-			zfcp_fsf_link_down_info_eval(fsf_req, 39,
-				(struct fsf_link_down_info *)
-				&status_buffer->payload);
-			break;
-		case FSF_STATUS_READ_SUB_FIRMWARE_UPDATE:
-			ZFCP_LOG_INFO("Local link to adapter %s is down "
-				      "due to firmware update on adapter\n",
-				      zfcp_get_busid_by_adapter(adapter));
-			zfcp_fsf_link_down_info_eval(fsf_req, 40, NULL);
-			break;
-		default:
-			ZFCP_LOG_INFO("Local link to adapter %s is down "
-				      "due to unknown reason\n",
-				      zfcp_get_busid_by_adapter(adapter));
-			zfcp_fsf_link_down_info_eval(fsf_req, 41, NULL);
-		};
+		zfcp_fsf_status_read_link_down(req);
 		break;
-
 	case FSF_STATUS_READ_LINK_UP:
-		ZFCP_LOG_NORMAL("Local link to adapter %s was replugged. "
-				"Restarting operations on this adapter\n",
-				zfcp_get_busid_by_adapter(adapter));
+		dev_info(&adapter->ccw_device->dev,
+			 "Local link was replugged.\n");
 		/* All ports should be marked as ready to run again */
 		zfcp_erp_modify_adapter_status(adapter, 30, NULL,
 					       ZFCP_STATUS_COMMON_RUNNING,
 					       ZFCP_SET);
 		zfcp_erp_adapter_reopen(adapter,
-					ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED
-					| ZFCP_STATUS_COMMON_ERP_FAILED,
-					102, fsf_req);
+					ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED |
+					ZFCP_STATUS_COMMON_ERP_FAILED,
+					102, req);
 		break;
-
 	case FSF_STATUS_READ_NOTIFICATION_LOST:
-		ZFCP_LOG_NORMAL("Unsolicited status notification(s) lost: "
-				"adapter %s%s%s%s%s%s%s%s%s\n",
-				zfcp_get_busid_by_adapter(adapter),
-				(status_buffer->status_subtype &
-					FSF_STATUS_READ_SUB_INCOMING_ELS) ?
-					", incoming ELS" : "",
-				(status_buffer->status_subtype &
-					FSF_STATUS_READ_SUB_SENSE_DATA) ?
-					", sense data" : "",
-				(status_buffer->status_subtype &
-					FSF_STATUS_READ_SUB_LINK_STATUS) ?
-					", link status change" : "",
-				(status_buffer->status_subtype &
-					FSF_STATUS_READ_SUB_PORT_CLOSED) ?
-					", port close" : "",
-				(status_buffer->status_subtype &
-					FSF_STATUS_READ_SUB_BIT_ERROR_THRESHOLD) ?
-					", bit error exception" : "",
-				(status_buffer->status_subtype &
-					FSF_STATUS_READ_SUB_ACT_UPDATED) ?
-					", ACT update" : "",
-				(status_buffer->status_subtype &
-					FSF_STATUS_READ_SUB_ACT_HARDENED) ?
-					", ACT hardening" : "",
-				(status_buffer->status_subtype &
-					FSF_STATUS_READ_SUB_FEATURE_UPDATE_ALERT) ?
-					", adapter feature change" : "");
-
-		if (status_buffer->status_subtype &
-		    FSF_STATUS_READ_SUB_ACT_UPDATED)
-			zfcp_erp_adapter_access_changed(adapter, 135, fsf_req);
+		if (sr_buf->status_subtype & FSF_STATUS_READ_SUB_ACT_UPDATED)
+			zfcp_erp_adapter_access_changed(adapter, 135, req);
+		if (sr_buf->status_subtype & FSF_STATUS_READ_SUB_INCOMING_ELS)
+			schedule_work(&adapter->scan_work);
 		break;
-
 	case FSF_STATUS_READ_CFDC_UPDATED:
-		ZFCP_LOG_NORMAL("CFDC has been updated on the adapter %s\n",
-			      zfcp_get_busid_by_adapter(adapter));
-		zfcp_erp_adapter_access_changed(adapter, 136, fsf_req);
+		zfcp_erp_adapter_access_changed(adapter, 136, req);
 		break;
-
-	case FSF_STATUS_READ_CFDC_HARDENED:
-		switch (status_buffer->status_subtype) {
-		case FSF_STATUS_READ_SUB_CFDC_HARDENED_ON_SE:
-			ZFCP_LOG_NORMAL("CFDC of adapter %s saved on SE\n",
-				      zfcp_get_busid_by_adapter(adapter));
-			break;
-		case FSF_STATUS_READ_SUB_CFDC_HARDENED_ON_SE2:
-			ZFCP_LOG_NORMAL("CFDC of adapter %s has been copied "
-				      "to the secondary SE\n",
-				zfcp_get_busid_by_adapter(adapter));
-			break;
-		default:
-			ZFCP_LOG_NORMAL("CFDC of adapter %s has been hardened\n",
-				      zfcp_get_busid_by_adapter(adapter));
-		}
-		break;
-
 	case FSF_STATUS_READ_FEATURE_UPDATE_ALERT:
-		ZFCP_LOG_INFO("List of supported features on adapter %s has "
-			      "been changed from 0x%08X to 0x%08X\n",
-			      zfcp_get_busid_by_adapter(adapter),
-			      *(u32*) (status_buffer->payload + 4),
-			      *(u32*) (status_buffer->payload));
-		adapter->adapter_features = *(u32*) status_buffer->payload;
+		adapter->adapter_features = sr_buf->payload.word[0];
 		break;
+	}
 
-	default:
-		ZFCP_LOG_NORMAL("warning: An unsolicited status packet of unknown "
-				"type was received (debug info 0x%x)\n",
-				status_buffer->status_type);
-		ZFCP_LOG_DEBUG("Dump of status_read_buffer %p:\n",
-			       status_buffer);
-		ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
-			      (char *) status_buffer,
-			      sizeof (struct fsf_status_read_buffer));
-		break;
-	}
-	mempool_free(status_buffer, adapter->pool.data_status_read);
-	zfcp_fsf_req_free(fsf_req);
-	/*
-	 * recycle buffer and start new request repeat until outbound
-	 * queue is empty or adapter shutdown is requested
-	 */
-	/*
-	 * FIXME(qdio):
-	 * we may wait in the req_create for 5s during shutdown, so
-	 * qdio_cleanup will have to wait at least that long before returning
-	 * with failure to allow us a proper cleanup under all circumstances
-	 */
-	/*
-	 * FIXME:
-	 * allocation failure possible? (Is this code needed?)
-	 */
-	retval = zfcp_fsf_status_read(adapter, 0);
-	if (retval < 0) {
-		ZFCP_LOG_INFO("Failed to create unsolicited status read "
-			      "request for the adapter %s.\n",
-			      zfcp_get_busid_by_adapter(adapter));
-		/* temporary fix to avoid status read buffer shortage */
-		adapter->status_read_failed++;
-		if ((ZFCP_STATUS_READS_RECOM - adapter->status_read_failed)
-		    < ZFCP_STATUS_READ_FAILED_THRESHOLD) {
-			ZFCP_LOG_INFO("restart adapter %s due to status read "
-				      "buffer shortage\n",
-				      zfcp_get_busid_by_adapter(adapter));
-			zfcp_erp_adapter_reopen(adapter, 0, 103, fsf_req);
-		}
-	}
- out:
-	return retval;
+	mempool_free(sr_buf, adapter->pool.data_status_read);
+	zfcp_fsf_req_free(req);
+
+	atomic_inc(&adapter->stat_miss);
+	schedule_work(&adapter->stat_work);
 }
 
-/*
- * function:    zfcp_fsf_abort_fcp_command
- *
- * purpose:	tells FSF to abort a running SCSI command
- *
- * returns:	address of initiated FSF request
- *		NULL - request could not be initiated
- *
- * FIXME(design): should be watched by a timeout !!!
- * FIXME(design) shouldn't this be modified to return an int
- *               also...don't know how though
- */
-struct zfcp_fsf_req *
-zfcp_fsf_abort_fcp_command(unsigned long old_req_id,
-			   struct zfcp_adapter *adapter,
-			   struct zfcp_unit *unit, int req_flags)
+static void zfcp_fsf_fsfstatus_qual_eval(struct zfcp_fsf_req *req)
 {
-	volatile struct qdio_buffer_element *sbale;
-	struct zfcp_fsf_req *fsf_req = NULL;
-	unsigned long lock_flags;
-	int retval = 0;
-
-	/* setup new FSF request */
-	retval = zfcp_fsf_req_create(adapter, FSF_QTCB_ABORT_FCP_CMND,
-				     req_flags, adapter->pool.fsf_req_abort,
-				     &lock_flags, &fsf_req);
-	if (retval < 0) {
-		ZFCP_LOG_INFO("error: Failed to create an abort command "
-			      "request for lun 0x%016Lx on port 0x%016Lx "
-			      "on adapter %s.\n",
-			      unit->fcp_lun,
-			      unit->port->wwpn,
-			      zfcp_get_busid_by_adapter(adapter));
-		goto out;
+	switch (req->qtcb->header.fsf_status_qual.word[0]) {
+	case FSF_SQ_FCP_RSP_AVAILABLE:
+	case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE:
+	case FSF_SQ_NO_RETRY_POSSIBLE:
+	case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED:
+		return;
+	case FSF_SQ_COMMAND_ABORTED:
+		req->status |= ZFCP_STATUS_FSFREQ_ABORTED;
+		break;
+	case FSF_SQ_NO_RECOM:
+		dev_err(&req->adapter->ccw_device->dev,
+			"No recommendation could be given for a "
+			"problem on the adapter.\n");
+		zfcp_erp_adapter_shutdown(req->adapter, 0, 121, req);
+		break;
 	}
-
-	if (unlikely(!atomic_test_mask(ZFCP_STATUS_COMMON_UNBLOCKED,
-			&unit->status)))
-		goto unit_blocked;
-
-	sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
-        sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
-        sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
-
-	fsf_req->data = (unsigned long) unit;
-
-	/* set handles of unit and its parent port in QTCB */
-	fsf_req->qtcb->header.lun_handle = unit->handle;
-	fsf_req->qtcb->header.port_handle = unit->port->handle;
-
-	/* set handle of request which should be aborted */
-	fsf_req->qtcb->bottom.support.req_handle = (u64) old_req_id;
-
-	zfcp_fsf_start_timer(fsf_req, ZFCP_SCSI_ER_TIMEOUT);
-	retval = zfcp_fsf_req_send(fsf_req);
-	if (!retval)
-		goto out;
-
- unit_blocked:
-		zfcp_fsf_req_free(fsf_req);
-		fsf_req = NULL;
-
- out:
-	write_unlock_irqrestore(&adapter->request_queue.queue_lock, lock_flags);
-	return fsf_req;
+	/* all non-return stats set FSFREQ_ERROR*/
+	req->status |= ZFCP_STATUS_FSFREQ_ERROR;
 }
 
-/*
- * function:    zfcp_fsf_abort_fcp_command_handler
- *
- * purpose:	is called for finished Abort FCP Command request
- *
- * returns:
- */
-static int
-zfcp_fsf_abort_fcp_command_handler(struct zfcp_fsf_req *new_fsf_req)
+static void zfcp_fsf_fsfstatus_eval(struct zfcp_fsf_req *req)
 {
-	int retval = -EINVAL;
-	struct zfcp_unit *unit;
-	union fsf_status_qual *fsf_stat_qual =
-		&new_fsf_req->qtcb->header.fsf_status_qual;
+	if (unlikely(req->status & ZFCP_STATUS_FSFREQ_ERROR))
+		return;
 
-	if (new_fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
-		/* do not set ZFCP_STATUS_FSFREQ_ABORTSUCCEEDED */
-		goto skip_fsfstatus;
-	}
-
-	unit = (struct zfcp_unit *) new_fsf_req->data;
-
-	/* evaluate FSF status in QTCB */
-	switch (new_fsf_req->qtcb->header.fsf_status) {
-
-	case FSF_PORT_HANDLE_NOT_VALID:
-		if (fsf_stat_qual->word[0] != fsf_stat_qual->word[1]) {
-			/*
-			 * In this case a command that was sent prior to a port
-			 * reopen was aborted (handles are different). This is
-			 * fine.
-			 */
-		} else {
-			ZFCP_LOG_INFO("Temporary port identifier 0x%x for "
-				      "port 0x%016Lx on adapter %s invalid. "
-				      "This may happen occasionally.\n",
-				      unit->port->handle,
-				      unit->port->wwpn,
-				      zfcp_get_busid_by_unit(unit));
-			ZFCP_LOG_INFO("status qualifier:\n");
-			ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_INFO,
-				      (char *) &new_fsf_req->qtcb->header.
-				      fsf_status_qual,
-				      sizeof (union fsf_status_qual));
-			/* Let's hope this sorts out the mess */
-			zfcp_erp_adapter_reopen(unit->port->adapter, 0, 104,
-						new_fsf_req);
-			new_fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-		}
+	switch (req->qtcb->header.fsf_status) {
+	case FSF_UNKNOWN_COMMAND:
+		dev_err(&req->adapter->ccw_device->dev,
+			"Command issued by the device driver (0x%x) is "
+			"not known by the adapter.\n",
+			req->qtcb->header.fsf_command);
+		zfcp_erp_adapter_shutdown(req->adapter, 0, 120, req);
+		req->status |= ZFCP_STATUS_FSFREQ_ERROR;
 		break;
-
-	case FSF_LUN_HANDLE_NOT_VALID:
-		if (fsf_stat_qual->word[0] != fsf_stat_qual->word[1]) {
-			/*
-			 * In this case a command that was sent prior to a unit
-			 * reopen was aborted (handles are different).
-			 * This is fine.
-			 */
-		} else {
-			ZFCP_LOG_INFO
-			    ("Warning: Temporary LUN identifier 0x%x of LUN "
-			     "0x%016Lx on port 0x%016Lx on adapter %s is "
-			     "invalid. This may happen in rare cases. "
-			     "Trying to re-establish link.\n",
-			     unit->handle,
-			     unit->fcp_lun,
-			     unit->port->wwpn,
-			     zfcp_get_busid_by_unit(unit));
-			ZFCP_LOG_DEBUG("Status qualifier data:\n");
-			ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
-				      (char *) &new_fsf_req->qtcb->header.
-				      fsf_status_qual,
-				      sizeof (union fsf_status_qual));
-			/* Let's hope this sorts out the mess */
-			zfcp_erp_port_reopen(unit->port, 0, 105, new_fsf_req);
-			new_fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-		}
-		break;
-
-	case FSF_FCP_COMMAND_DOES_NOT_EXIST:
-		retval = 0;
-		new_fsf_req->status |= ZFCP_STATUS_FSFREQ_ABORTNOTNEEDED;
-		break;
-
-	case FSF_PORT_BOXED:
-		ZFCP_LOG_INFO("Remote port 0x%016Lx on adapter %s needs to "
-			      "be reopened\n", unit->port->wwpn,
-			      zfcp_get_busid_by_unit(unit));
-		zfcp_erp_port_boxed(unit->port, 47, new_fsf_req);
-		new_fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR
-		    | ZFCP_STATUS_FSFREQ_RETRY;
-		break;
-
-	case FSF_LUN_BOXED:
-                ZFCP_LOG_INFO(
-                        "unit 0x%016Lx on port 0x%016Lx on adapter %s needs "
-                        "to be reopened\n",
-                        unit->fcp_lun, unit->port->wwpn,
-                        zfcp_get_busid_by_unit(unit));
-		zfcp_erp_unit_boxed(unit, 48, new_fsf_req);
-                new_fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR
-                        | ZFCP_STATUS_FSFREQ_RETRY;
-                break;
-
 	case FSF_ADAPTER_STATUS_AVAILABLE:
-		switch (new_fsf_req->qtcb->header.fsf_status_qual.word[0]) {
-		case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE:
-			zfcp_test_link(unit->port);
-			new_fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-			break;
-		case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED:
-			/* SCSI stack will escalate */
-			new_fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-			break;
-		default:
-			ZFCP_LOG_NORMAL
-			    ("bug: Wrong status qualifier 0x%x arrived.\n",
-			     new_fsf_req->qtcb->header.fsf_status_qual.word[0]);
-			break;
-		}
+		zfcp_fsf_fsfstatus_qual_eval(req);
 		break;
+	}
+}
 
-	case FSF_GOOD:
-		retval = 0;
-		new_fsf_req->status |= ZFCP_STATUS_FSFREQ_ABORTSUCCEEDED;
+static void zfcp_fsf_protstatus_eval(struct zfcp_fsf_req *req)
+{
+	struct zfcp_adapter *adapter = req->adapter;
+	struct fsf_qtcb *qtcb = req->qtcb;
+	union fsf_prot_status_qual *psq = &qtcb->prefix.prot_status_qual;
+
+	zfcp_hba_dbf_event_fsf_response(req);
+
+	if (req->status & ZFCP_STATUS_FSFREQ_DISMISSED) {
+		req->status |= ZFCP_STATUS_FSFREQ_ERROR |
+			ZFCP_STATUS_FSFREQ_RETRY; /* only for SCSI cmnds. */
+		return;
+	}
+
+	switch (qtcb->prefix.prot_status) {
+	case FSF_PROT_GOOD:
+	case FSF_PROT_FSF_STATUS_PRESENTED:
+		return;
+	case FSF_PROT_QTCB_VERSION_ERROR:
+		dev_err(&adapter->ccw_device->dev,
+			"The QTCB version requested by zfcp (0x%x) is not "
+			"supported by the FCP adapter (lowest supported "
+			"0x%x, highest supported 0x%x).\n",
+			FSF_QTCB_CURRENT_VERSION, psq->word[0],
+			psq->word[1]);
+		zfcp_erp_adapter_shutdown(adapter, 0, 117, req);
 		break;
-
+	case FSF_PROT_ERROR_STATE:
+	case FSF_PROT_SEQ_NUMB_ERROR:
+		zfcp_erp_adapter_reopen(adapter, 0, 98, req);
+		req->status |= ZFCP_STATUS_FSFREQ_RETRY;
+		break;
+	case FSF_PROT_UNSUPP_QTCB_TYPE:
+		dev_err(&adapter->ccw_device->dev,
+			"Packet header type used by the device driver is "
+			"incompatible with that used on the adapter.\n");
+		zfcp_erp_adapter_shutdown(adapter, 0, 118, req);
+		break;
+	case FSF_PROT_HOST_CONNECTION_INITIALIZING:
+		atomic_set_mask(ZFCP_STATUS_ADAPTER_HOST_CON_INIT,
+				&adapter->status);
+		break;
+	case FSF_PROT_DUPLICATE_REQUEST_ID:
+		dev_err(&adapter->ccw_device->dev,
+			"The request identifier 0x%Lx is ambiguous.\n",
+			(unsigned long long)qtcb->bottom.support.req_handle);
+		zfcp_erp_adapter_shutdown(adapter, 0, 78, req);
+		break;
+	case FSF_PROT_LINK_DOWN:
+		zfcp_fsf_link_down_info_eval(req, 37, &psq->link_down_info);
+		/* FIXME: reopening adapter now? better wait for link up */
+		zfcp_erp_adapter_reopen(adapter, 0, 79, req);
+		break;
+	case FSF_PROT_REEST_QUEUE:
+		/* All ports should be marked as ready to run again */
+		zfcp_erp_modify_adapter_status(adapter, 28, NULL,
+					       ZFCP_STATUS_COMMON_RUNNING,
+					       ZFCP_SET);
+		zfcp_erp_adapter_reopen(adapter,
+					ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED |
+					ZFCP_STATUS_COMMON_ERP_FAILED, 99, req);
+		break;
 	default:
-		ZFCP_LOG_NORMAL("bug: An unknown FSF Status was presented "
-				"(debug info 0x%x)\n",
-				new_fsf_req->qtcb->header.fsf_status);
-		break;
+		dev_err(&adapter->ccw_device->dev,
+			"Transfer protocol status information"
+			"provided by the adapter (0x%x) "
+			"is not compatible with the device driver.\n",
+			qtcb->prefix.prot_status);
+		zfcp_erp_adapter_shutdown(adapter, 0, 119, req);
 	}
- skip_fsfstatus:
-	return retval;
+	req->status |= ZFCP_STATUS_FSFREQ_ERROR;
 }
 
 /**
- * zfcp_use_one_sbal - checks whether req buffer and resp bother each fit into
- *	one SBALE
- * Two scatter-gather lists are passed, one for the reqeust and one for the
- * response.
- */
-static inline int
-zfcp_use_one_sbal(struct scatterlist *req, int req_count,
-                  struct scatterlist *resp, int resp_count)
-{
-        return ((req_count == 1) &&
-		(resp_count == 1) &&
-                (((unsigned long) zfcp_sg_to_address(&req[0]) &
-		  PAGE_MASK) ==
-		 ((unsigned long) (zfcp_sg_to_address(&req[0]) +
-				   req[0].length - 1) & PAGE_MASK)) &&
-                (((unsigned long) zfcp_sg_to_address(&resp[0]) &
-		  PAGE_MASK) ==
-                 ((unsigned long) (zfcp_sg_to_address(&resp[0]) +
-				   resp[0].length - 1) & PAGE_MASK)));
-}
-
-/**
- * zfcp_fsf_send_ct - initiate a Generic Service request (FC-GS)
- * @ct: pointer to struct zfcp_send_ct which conatins all needed data for
- *	the request
- * @pool: pointer to memory pool, if non-null this pool is used to allocate
- *	a struct zfcp_fsf_req
- * @erp_action: pointer to erp_action, if non-null the Generic Service request
- *	is sent within error recovery
- */
-int
-zfcp_fsf_send_ct(struct zfcp_send_ct *ct, mempool_t *pool,
-		 struct zfcp_erp_action *erp_action)
-{
-	volatile struct qdio_buffer_element *sbale;
-	struct zfcp_port *port;
-	struct zfcp_adapter *adapter;
-        struct zfcp_fsf_req *fsf_req;
-        unsigned long lock_flags;
-        int bytes;
-	int ret = 0;
-
-	port = ct->port;
-	adapter = port->adapter;
-
-	ret = zfcp_fsf_req_create(adapter, FSF_QTCB_SEND_GENERIC,
-				  ZFCP_WAIT_FOR_SBAL | ZFCP_REQ_AUTO_CLEANUP,
-				  pool, &lock_flags, &fsf_req);
-	if (ret < 0) {
-                ZFCP_LOG_INFO("error: Could not create CT request (FC-GS) for "
-			      "adapter: %s\n",
-			      zfcp_get_busid_by_adapter(adapter));
-		goto failed_req;
-	}
-
-	sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
-        if (zfcp_use_one_sbal(ct->req, ct->req_count,
-                              ct->resp, ct->resp_count)){
-                /* both request buffer and response buffer
-                   fit into one sbale each */
-                sbale[0].flags |= SBAL_FLAGS0_TYPE_WRITE_READ;
-                sbale[2].addr = zfcp_sg_to_address(&ct->req[0]);
-                sbale[2].length = ct->req[0].length;
-                sbale[3].addr = zfcp_sg_to_address(&ct->resp[0]);
-                sbale[3].length = ct->resp[0].length;
-                sbale[3].flags |= SBAL_FLAGS_LAST_ENTRY;
-	} else if (adapter->adapter_features &
-                   FSF_FEATURE_ELS_CT_CHAINED_SBALS) {
-                /* try to use chained SBALs */
-                bytes = zfcp_qdio_sbals_from_sg(fsf_req,
-                                                SBAL_FLAGS0_TYPE_WRITE_READ,
-                                                ct->req, ct->req_count,
-                                                ZFCP_MAX_SBALS_PER_CT_REQ);
-                if (bytes <= 0) {
-                        ZFCP_LOG_INFO("error: creation of CT request failed "
-				      "on adapter %s\n",
-				      zfcp_get_busid_by_adapter(adapter));
-                        if (bytes == 0)
-                                ret = -ENOMEM;
-                        else
-                                ret = bytes;
-
-                        goto failed_send;
-                }
-                fsf_req->qtcb->bottom.support.req_buf_length = bytes;
-                fsf_req->sbale_curr = ZFCP_LAST_SBALE_PER_SBAL;
-                bytes = zfcp_qdio_sbals_from_sg(fsf_req,
-                                                SBAL_FLAGS0_TYPE_WRITE_READ,
-                                                ct->resp, ct->resp_count,
-                                                ZFCP_MAX_SBALS_PER_CT_REQ);
-                if (bytes <= 0) {
-                        ZFCP_LOG_INFO("error: creation of CT request failed "
-				      "on adapter %s\n",
-				      zfcp_get_busid_by_adapter(adapter));
-                        if (bytes == 0)
-                                ret = -ENOMEM;
-                        else
-                                ret = bytes;
-
-                        goto failed_send;
-                }
-                fsf_req->qtcb->bottom.support.resp_buf_length = bytes;
-        } else {
-                /* reject send generic request */
-		ZFCP_LOG_INFO(
-			"error: microcode does not support chained SBALs,"
-                        "CT request too big (adapter %s)\n",
-			zfcp_get_busid_by_adapter(adapter));
-                ret = -EOPNOTSUPP;
-                goto failed_send;
-        }
-
-	/* settings in QTCB */
-	fsf_req->qtcb->header.port_handle = port->handle;
-	fsf_req->qtcb->bottom.support.service_class =
-		ZFCP_FC_SERVICE_CLASS_DEFAULT;
-	fsf_req->qtcb->bottom.support.timeout = ct->timeout;
-        fsf_req->data = (unsigned long) ct;
-
-	zfcp_san_dbf_event_ct_request(fsf_req);
-
-	if (erp_action) {
-		erp_action->fsf_req = fsf_req;
-		fsf_req->erp_action = erp_action;
-		zfcp_erp_start_timer(fsf_req);
-	} else
-		zfcp_fsf_start_timer(fsf_req, ZFCP_FSF_REQUEST_TIMEOUT);
-
-	ret = zfcp_fsf_req_send(fsf_req);
-	if (ret) {
-		ZFCP_LOG_DEBUG("error: initiation of CT request failed "
-			       "(adapter %s, port 0x%016Lx)\n",
-			       zfcp_get_busid_by_adapter(adapter), port->wwpn);
-		goto failed_send;
-	}
-
-	ZFCP_LOG_DEBUG("CT request initiated (adapter %s, port 0x%016Lx)\n",
-		       zfcp_get_busid_by_adapter(adapter), port->wwpn);
-	goto out;
-
- failed_send:
-	zfcp_fsf_req_free(fsf_req);
-        if (erp_action != NULL) {
-                erp_action->fsf_req = NULL;
-        }
- failed_req:
- out:
-        write_unlock_irqrestore(&adapter->request_queue.queue_lock,
-				lock_flags);
-	return ret;
-}
-
-/**
- * zfcp_fsf_send_ct_handler - handler for Generic Service requests
- * @fsf_req: pointer to struct zfcp_fsf_req
+ * zfcp_fsf_req_complete - process completion of a FSF request
+ * @fsf_req: The FSF request that has been completed.
  *
- * Data specific for the Generic Service request is passed using
- * fsf_req->data. There we find the pointer to struct zfcp_send_ct.
- * Usually a specific handler for the CT request is called which is
- * found in this structure.
+ * When a request has been completed either from the FCP adapter,
+ * or it has been dismissed due to a queue shutdown, this function
+ * is called to process the completion status and trigger further
+ * events related to the FSF request.
  */
-static int
-zfcp_fsf_send_ct_handler(struct zfcp_fsf_req *fsf_req)
+void zfcp_fsf_req_complete(struct zfcp_fsf_req *req)
 {
-	struct zfcp_port *port;
-	struct zfcp_adapter *adapter;
-	struct zfcp_send_ct *send_ct;
-	struct fsf_qtcb_header *header;
-	struct fsf_qtcb_bottom_support *bottom;
-	int retval = -EINVAL;
-	u16 subtable, rule, counter;
-
-	adapter = fsf_req->adapter;
-	send_ct = (struct zfcp_send_ct *) fsf_req->data;
-	port = send_ct->port;
-	header = &fsf_req->qtcb->header;
-	bottom = &fsf_req->qtcb->bottom.support;
-
-	if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR)
-		goto skip_fsfstatus;
-
-	/* evaluate FSF status in QTCB */
-	switch (header->fsf_status) {
-
-        case FSF_GOOD:
-		zfcp_san_dbf_event_ct_response(fsf_req);
-                retval = 0;
-		break;
-
-        case FSF_SERVICE_CLASS_NOT_SUPPORTED:
-		ZFCP_LOG_INFO("error: adapter %s does not support fc "
-			      "class %d.\n",
-			      zfcp_get_busid_by_port(port),
-			      ZFCP_FC_SERVICE_CLASS_DEFAULT);
-		/* stop operation for this adapter */
-		zfcp_erp_adapter_shutdown(adapter, 0, 123, fsf_req);
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-		break;
-
-        case FSF_ADAPTER_STATUS_AVAILABLE:
-                switch (header->fsf_status_qual.word[0]){
-                case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE:
-			/* reopening link to port */
-			zfcp_test_link(port);
-			fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-			break;
-                case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED:
-			/* ERP strategy will escalate */
-			fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-			break;
-                default:
-			ZFCP_LOG_INFO("bug: Wrong status qualifier 0x%x "
-				      "arrived.\n",
-				      header->fsf_status_qual.word[0]);
-			break;
-                }
-                break;
-
-	case FSF_ACCESS_DENIED:
-		ZFCP_LOG_NORMAL("access denied, cannot send generic service "
-				"command (adapter %s, port d_id=0x%06x)\n",
-				zfcp_get_busid_by_port(port), port->d_id);
-		for (counter = 0; counter < 2; counter++) {
-			subtable = header->fsf_status_qual.halfword[counter * 2];
-			rule = header->fsf_status_qual.halfword[counter * 2 + 1];
-			switch (subtable) {
-			case FSF_SQ_CFDC_SUBTABLE_OS:
-			case FSF_SQ_CFDC_SUBTABLE_PORT_WWPN:
-			case FSF_SQ_CFDC_SUBTABLE_PORT_DID:
-			case FSF_SQ_CFDC_SUBTABLE_LUN:
-       				ZFCP_LOG_INFO("Access denied (%s rule %d)\n",
-					zfcp_act_subtable_type[subtable], rule);
-				break;
-			}
-		}
-		zfcp_erp_port_access_denied(port, 55, fsf_req);
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-		break;
-
-        case FSF_GENERIC_COMMAND_REJECTED:
-		ZFCP_LOG_INFO("generic service command rejected "
-			      "(adapter %s, port d_id=0x%06x)\n",
-			      zfcp_get_busid_by_port(port), port->d_id);
-		ZFCP_LOG_INFO("status qualifier:\n");
-		ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_INFO,
-			      (char *) &header->fsf_status_qual,
-			      sizeof (union fsf_status_qual));
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-		break;
-
-        case FSF_PORT_HANDLE_NOT_VALID:
-		ZFCP_LOG_DEBUG("Temporary port identifier 0x%x for port "
-			       "0x%016Lx on adapter %s invalid. This may "
-			       "happen occasionally.\n", port->handle,
-			       port->wwpn, zfcp_get_busid_by_port(port));
-		ZFCP_LOG_INFO("status qualifier:\n");
-		ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_INFO,
-			      (char *) &header->fsf_status_qual,
-			      sizeof (union fsf_status_qual));
-		zfcp_erp_adapter_reopen(adapter, 0, 106, fsf_req);
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-		break;
-
-        case FSF_PORT_BOXED:
-		ZFCP_LOG_INFO("port needs to be reopened "
-			      "(adapter %s, port d_id=0x%06x)\n",
-			      zfcp_get_busid_by_port(port), port->d_id);
-		zfcp_erp_port_boxed(port, 49, fsf_req);
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR
-		    | ZFCP_STATUS_FSFREQ_RETRY;
-		break;
-
-	/* following states should never occure, all cases avoided
-	   in zfcp_fsf_send_ct - but who knows ... */
-	case FSF_PAYLOAD_SIZE_MISMATCH:
-		ZFCP_LOG_INFO("payload size mismatch (adapter: %s, "
-			      "req_buf_length=%d, resp_buf_length=%d)\n",
-			      zfcp_get_busid_by_adapter(adapter),
-			      bottom->req_buf_length, bottom->resp_buf_length);
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-		break;
-	case FSF_REQUEST_SIZE_TOO_LARGE:
-		ZFCP_LOG_INFO("request size too large (adapter: %s, "
-			      "req_buf_length=%d)\n",
-			      zfcp_get_busid_by_adapter(adapter),
-			      bottom->req_buf_length);
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-		break;
-	case FSF_RESPONSE_SIZE_TOO_LARGE:
-		ZFCP_LOG_INFO("response size too large (adapter: %s, "
-			      "resp_buf_length=%d)\n",
-			      zfcp_get_busid_by_adapter(adapter),
-			      bottom->resp_buf_length);
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-		break;
-	case FSF_SBAL_MISMATCH:
-		ZFCP_LOG_INFO("SBAL mismatch (adapter: %s, req_buf_length=%d, "
-			      "resp_buf_length=%d)\n",
-			      zfcp_get_busid_by_adapter(adapter),
-			      bottom->req_buf_length, bottom->resp_buf_length);
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-		break;
-
-       default:
-		ZFCP_LOG_NORMAL("bug: An unknown FSF Status was presented "
-				"(debug info 0x%x)\n", header->fsf_status);
-		break;
+	if (unlikely(req->fsf_command == FSF_QTCB_UNSOLICITED_STATUS)) {
+		zfcp_fsf_status_read_handler(req);
+		return;
 	}
 
-skip_fsfstatus:
-	send_ct->status = retval;
+	del_timer(&req->timer);
+	zfcp_fsf_protstatus_eval(req);
+	zfcp_fsf_fsfstatus_eval(req);
+	req->handler(req);
 
-	if (send_ct->handler != NULL)
-		send_ct->handler(send_ct->handler_data);
+	if (req->erp_action)
+		zfcp_erp_notify(req->erp_action, 0);
+	req->status |= ZFCP_STATUS_FSFREQ_COMPLETED;
 
-	return retval;
-}
-
-/**
- * zfcp_fsf_send_els - initiate an ELS command (FC-FS)
- * @els: pointer to struct zfcp_send_els which contains all needed data for
- *	the command.
- */
-int
-zfcp_fsf_send_els(struct zfcp_send_els *els)
-{
-	volatile struct qdio_buffer_element *sbale;
-	struct zfcp_fsf_req *fsf_req;
-	u32 d_id;
-	struct zfcp_adapter *adapter;
-	unsigned long lock_flags;
-        int bytes;
-	int ret = 0;
-
-	d_id = els->d_id;
-	adapter = els->adapter;
-
-        ret = zfcp_fsf_req_create(adapter, FSF_QTCB_SEND_ELS,
-				  ZFCP_REQ_AUTO_CLEANUP,
-				  NULL, &lock_flags, &fsf_req);
-	if (ret < 0) {
-                ZFCP_LOG_INFO("error: creation of ELS request failed "
-			      "(adapter %s, port d_id: 0x%06x)\n",
-                              zfcp_get_busid_by_adapter(adapter), d_id);
-                goto failed_req;
-	}
-
-	if (unlikely(!atomic_test_mask(ZFCP_STATUS_COMMON_UNBLOCKED,
-			&els->port->status))) {
-		ret = -EBUSY;
-		goto port_blocked;
-	}
-
-	sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
-        if (zfcp_use_one_sbal(els->req, els->req_count,
-                              els->resp, els->resp_count)){
-                /* both request buffer and response buffer
-                   fit into one sbale each */
-                sbale[0].flags |= SBAL_FLAGS0_TYPE_WRITE_READ;
-                sbale[2].addr = zfcp_sg_to_address(&els->req[0]);
-                sbale[2].length = els->req[0].length;
-                sbale[3].addr = zfcp_sg_to_address(&els->resp[0]);
-                sbale[3].length = els->resp[0].length;
-                sbale[3].flags |= SBAL_FLAGS_LAST_ENTRY;
-	} else if (adapter->adapter_features &
-                   FSF_FEATURE_ELS_CT_CHAINED_SBALS) {
-                /* try to use chained SBALs */
-                bytes = zfcp_qdio_sbals_from_sg(fsf_req,
-                                                SBAL_FLAGS0_TYPE_WRITE_READ,
-                                                els->req, els->req_count,
-                                                ZFCP_MAX_SBALS_PER_ELS_REQ);
-                if (bytes <= 0) {
-                        ZFCP_LOG_INFO("error: creation of ELS request failed "
-				      "(adapter %s, port d_id: 0x%06x)\n",
-				      zfcp_get_busid_by_adapter(adapter), d_id);
-                        if (bytes == 0) {
-                                ret = -ENOMEM;
-                        } else {
-                                ret = bytes;
-                        }
-                        goto failed_send;
-                }
-                fsf_req->qtcb->bottom.support.req_buf_length = bytes;
-                fsf_req->sbale_curr = ZFCP_LAST_SBALE_PER_SBAL;
-                bytes = zfcp_qdio_sbals_from_sg(fsf_req,
-                                                SBAL_FLAGS0_TYPE_WRITE_READ,
-                                                els->resp, els->resp_count,
-                                                ZFCP_MAX_SBALS_PER_ELS_REQ);
-                if (bytes <= 0) {
-                        ZFCP_LOG_INFO("error: creation of ELS request failed "
-				      "(adapter %s, port d_id: 0x%06x)\n",
-				      zfcp_get_busid_by_adapter(adapter), d_id);
-                        if (bytes == 0) {
-                                ret = -ENOMEM;
-                        } else {
-                                ret = bytes;
-                        }
-                        goto failed_send;
-                }
-                fsf_req->qtcb->bottom.support.resp_buf_length = bytes;
-        } else {
-                /* reject request */
-		ZFCP_LOG_INFO("error: microcode does not support chained SBALs"
-                              ", ELS request too big (adapter %s, "
-			      "port d_id: 0x%06x)\n",
-			      zfcp_get_busid_by_adapter(adapter), d_id);
-                ret = -EOPNOTSUPP;
-                goto failed_send;
-        }
-
-	/* settings in QTCB */
-	fsf_req->qtcb->bottom.support.d_id = d_id;
-	fsf_req->qtcb->bottom.support.service_class =
-		ZFCP_FC_SERVICE_CLASS_DEFAULT;
-	fsf_req->qtcb->bottom.support.timeout = ZFCP_ELS_TIMEOUT;
-	fsf_req->data = (unsigned long) els;
-
-	sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
-
-	zfcp_san_dbf_event_els_request(fsf_req);
-
-	zfcp_fsf_start_timer(fsf_req, ZFCP_FSF_REQUEST_TIMEOUT);
-	ret = zfcp_fsf_req_send(fsf_req);
-	if (ret) {
-		ZFCP_LOG_DEBUG("error: initiation of ELS request failed "
-			       "(adapter %s, port d_id: 0x%06x)\n",
-			       zfcp_get_busid_by_adapter(adapter), d_id);
-		goto failed_send;
-	}
-
-	ZFCP_LOG_DEBUG("ELS request initiated (adapter %s, port d_id: "
-		       "0x%06x)\n", zfcp_get_busid_by_adapter(adapter), d_id);
-	goto out;
-
- port_blocked:
- failed_send:
-	zfcp_fsf_req_free(fsf_req);
-
- failed_req:
- out:
-	write_unlock_irqrestore(&adapter->request_queue.queue_lock,
-				lock_flags);
-
-        return ret;
-}
-
-/**
- * zfcp_fsf_send_els_handler - handler for ELS commands
- * @fsf_req: pointer to struct zfcp_fsf_req
- *
- * Data specific for the ELS command is passed using
- * fsf_req->data. There we find the pointer to struct zfcp_send_els.
- * Usually a specific handler for the ELS command is called which is
- * found in this structure.
- */
-static int zfcp_fsf_send_els_handler(struct zfcp_fsf_req *fsf_req)
-{
-	struct zfcp_adapter *adapter;
-	struct zfcp_port *port;
-	u32 d_id;
-	struct fsf_qtcb_header *header;
-	struct fsf_qtcb_bottom_support *bottom;
-	struct zfcp_send_els *send_els;
-	int retval = -EINVAL;
-	u16 subtable, rule, counter;
-
-	send_els = (struct zfcp_send_els *) fsf_req->data;
-	adapter = send_els->adapter;
-	port = send_els->port;
-	d_id = send_els->d_id;
-	header = &fsf_req->qtcb->header;
-	bottom = &fsf_req->qtcb->bottom.support;
-
-	if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR)
-		goto skip_fsfstatus;
-
-	switch (header->fsf_status) {
-
-	case FSF_GOOD:
-		zfcp_san_dbf_event_els_response(fsf_req);
-		retval = 0;
-		break;
-
-	case FSF_SERVICE_CLASS_NOT_SUPPORTED:
-		ZFCP_LOG_INFO("error: adapter %s does not support fc "
-			      "class %d.\n",
-			      zfcp_get_busid_by_adapter(adapter),
-			      ZFCP_FC_SERVICE_CLASS_DEFAULT);
-		/* stop operation for this adapter */
-		zfcp_erp_adapter_shutdown(adapter, 0, 124, fsf_req);
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-		break;
-
-	case FSF_ADAPTER_STATUS_AVAILABLE:
-		switch (header->fsf_status_qual.word[0]){
-		case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE:
-			if (port && (send_els->ls_code != ZFCP_LS_ADISC))
-				zfcp_test_link(port);
-			fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-			break;
-		case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED:
-			fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-			retval =
-			  zfcp_handle_els_rjt(header->fsf_status_qual.word[1],
-					      (struct zfcp_ls_rjt_par *)
-					      &header->fsf_status_qual.word[2]);
-			break;
-		case FSF_SQ_RETRY_IF_POSSIBLE:
-			fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-			break;
-		default:
-			ZFCP_LOG_INFO("bug: Wrong status qualifier 0x%x\n",
-				      header->fsf_status_qual.word[0]);
-			ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_INFO,
-				(char*)header->fsf_status_qual.word, 16);
-		}
-		break;
-
-	case FSF_ELS_COMMAND_REJECTED:
-		ZFCP_LOG_INFO("ELS has been rejected because command filter "
-			      "prohibited sending "
-			      "(adapter: %s, port d_id: 0x%06x)\n",
-			      zfcp_get_busid_by_adapter(adapter), d_id);
-
-		break;
-
-	case FSF_PAYLOAD_SIZE_MISMATCH:
-		ZFCP_LOG_INFO(
-			"ELS request size and ELS response size must be either "
-			"both 0, or both greater than 0 "
-			"(adapter: %s, req_buf_length=%d resp_buf_length=%d)\n",
-			zfcp_get_busid_by_adapter(adapter),
-			bottom->req_buf_length,
-			bottom->resp_buf_length);
-		break;
-
-	case FSF_REQUEST_SIZE_TOO_LARGE:
-		ZFCP_LOG_INFO(
-			"Length of the ELS request buffer, "
-			"specified in QTCB bottom, "
-			"exceeds the size of the buffers "
-			"that have been allocated for ELS request data "
-			"(adapter: %s, req_buf_length=%d)\n",
-			zfcp_get_busid_by_adapter(adapter),
-			bottom->req_buf_length);
-		break;
-
-	case FSF_RESPONSE_SIZE_TOO_LARGE:
-		ZFCP_LOG_INFO(
-			"Length of the ELS response buffer, "
-			"specified in QTCB bottom, "
-			"exceeds the size of the buffers "
-			"that have been allocated for ELS response data "
-			"(adapter: %s, resp_buf_length=%d)\n",
-			zfcp_get_busid_by_adapter(adapter),
-			bottom->resp_buf_length);
-		break;
-
-	case FSF_SBAL_MISMATCH:
-		/* should never occure, avoided in zfcp_fsf_send_els */
-		ZFCP_LOG_INFO("SBAL mismatch (adapter: %s, req_buf_length=%d, "
-			      "resp_buf_length=%d)\n",
-			      zfcp_get_busid_by_adapter(adapter),
-			      bottom->req_buf_length, bottom->resp_buf_length);
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-		break;
-
-	case FSF_ACCESS_DENIED:
-		ZFCP_LOG_NORMAL("access denied, cannot send ELS command "
-				"(adapter %s, port d_id=0x%06x)\n",
-				zfcp_get_busid_by_adapter(adapter), d_id);
-		for (counter = 0; counter < 2; counter++) {
-			subtable = header->fsf_status_qual.halfword[counter * 2];
-			rule = header->fsf_status_qual.halfword[counter * 2 + 1];
-			switch (subtable) {
-			case FSF_SQ_CFDC_SUBTABLE_OS:
-			case FSF_SQ_CFDC_SUBTABLE_PORT_WWPN:
-			case FSF_SQ_CFDC_SUBTABLE_PORT_DID:
-			case FSF_SQ_CFDC_SUBTABLE_LUN:
-				ZFCP_LOG_INFO("Access denied (%s rule %d)\n",
-					zfcp_act_subtable_type[subtable], rule);
-				break;
-			}
-		}
-		if (port != NULL)
-			zfcp_erp_port_access_denied(port, 56, fsf_req);
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-		break;
-
-	default:
-		ZFCP_LOG_NORMAL(
-			"bug: An unknown FSF Status was presented "
-			"(adapter: %s, fsf_status=0x%08x)\n",
-			zfcp_get_busid_by_adapter(adapter),
-			header->fsf_status);
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-		break;
-	}
-
-skip_fsfstatus:
-	send_els->status = retval;
-
-	if (send_els->handler)
-		send_els->handler(send_els->handler_data);
-
-	return retval;
-}
-
-int
-zfcp_fsf_exchange_config_data(struct zfcp_erp_action *erp_action)
-{
-	volatile struct qdio_buffer_element *sbale;
-	struct zfcp_fsf_req *fsf_req;
-	struct zfcp_adapter *adapter = erp_action->adapter;
-	unsigned long lock_flags;
-	int retval;
-
-	/* setup new FSF request */
-	retval = zfcp_fsf_req_create(adapter,
-				     FSF_QTCB_EXCHANGE_CONFIG_DATA,
-				     ZFCP_REQ_AUTO_CLEANUP,
-				     adapter->pool.fsf_req_erp,
-				     &lock_flags, &fsf_req);
-	if (retval) {
-		ZFCP_LOG_INFO("error: Could not create exchange configuration "
-			      "data request for adapter %s.\n",
-			      zfcp_get_busid_by_adapter(adapter));
-		write_unlock_irqrestore(&adapter->request_queue.queue_lock,
-					lock_flags);
-		return retval;
-	}
-
-	sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
-	sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
-	sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
-
-	fsf_req->qtcb->bottom.config.feature_selection =
-			FSF_FEATURE_CFDC |
-			FSF_FEATURE_LUN_SHARING |
-			FSF_FEATURE_NOTIFICATION_LOST |
-			FSF_FEATURE_UPDATE_ALERT;
-	fsf_req->erp_action = erp_action;
-	erp_action->fsf_req = fsf_req;
-
-	zfcp_erp_start_timer(fsf_req);
-	retval = zfcp_fsf_req_send(fsf_req);
-	write_unlock_irqrestore(&adapter->request_queue.queue_lock,
-				lock_flags);
-	if (retval) {
-		ZFCP_LOG_INFO("error: Could not send exchange configuration "
-			      "data command on the adapter %s\n",
-			      zfcp_get_busid_by_adapter(adapter));
-		zfcp_fsf_req_free(fsf_req);
-		erp_action->fsf_req = NULL;
-	}
+	if (likely(req->status & ZFCP_STATUS_FSFREQ_CLEANUP))
+		zfcp_fsf_req_free(req);
 	else
-		ZFCP_LOG_DEBUG("exchange configuration data request initiated "
-			       "(adapter %s)\n",
-			       zfcp_get_busid_by_adapter(adapter));
-
-	return retval;
+	/* notify initiator waiting for the requests completion */
+	/*
+	 * FIXME: Race! We must not access fsf_req here as it might have been
+	 * cleaned up already due to the set ZFCP_STATUS_FSFREQ_COMPLETED
+	 * flag. It's an improbable case. But, we have the same paranoia for
+	 * the cleanup flag already.
+	 * Might better be handled using complete()?
+	 * (setting the flag and doing wakeup ought to be atomic
+	 *  with regard to checking the flag as long as waitqueue is
+	 *  part of the to be released structure)
+	 */
+		wake_up(&req->completion_wq);
 }
 
-int
-zfcp_fsf_exchange_config_data_sync(struct zfcp_adapter *adapter,
-				struct fsf_qtcb_bottom_config *data)
-{
-	volatile struct qdio_buffer_element *sbale;
-	struct zfcp_fsf_req *fsf_req;
-	unsigned long lock_flags;
-	int retval;
-
-	/* setup new FSF request */
-	retval = zfcp_fsf_req_create(adapter, FSF_QTCB_EXCHANGE_CONFIG_DATA,
-				     ZFCP_WAIT_FOR_SBAL, NULL, &lock_flags,
-				     &fsf_req);
-	if (retval) {
-		ZFCP_LOG_INFO("error: Could not create exchange configuration "
-			      "data request for adapter %s.\n",
-			      zfcp_get_busid_by_adapter(adapter));
-		write_unlock_irqrestore(&adapter->request_queue.queue_lock,
-					lock_flags);
-		return retval;
-	}
-
-	sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
-	sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
-	sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
-
-	fsf_req->qtcb->bottom.config.feature_selection =
-			FSF_FEATURE_CFDC |
-			FSF_FEATURE_LUN_SHARING |
-			FSF_FEATURE_NOTIFICATION_LOST |
-			FSF_FEATURE_UPDATE_ALERT;
-
-	if (data)
-		fsf_req->data = (unsigned long) data;
-
-	zfcp_fsf_start_timer(fsf_req, ZFCP_FSF_REQUEST_TIMEOUT);
-	retval = zfcp_fsf_req_send(fsf_req);
-	write_unlock_irqrestore(&adapter->request_queue.queue_lock,
-				lock_flags);
-	if (retval)
-		ZFCP_LOG_INFO("error: Could not send exchange configuration "
-			      "data command on the adapter %s\n",
-			      zfcp_get_busid_by_adapter(adapter));
-	else
-		wait_event(fsf_req->completion_wq,
-			   fsf_req->status & ZFCP_STATUS_FSFREQ_COMPLETED);
-
-	zfcp_fsf_req_free(fsf_req);
-
-	return retval;
-}
-
-/**
- * zfcp_fsf_exchange_config_evaluate
- * @fsf_req: fsf_req which belongs to xchg config data request
- * @xchg_ok: specifies if xchg config data was incomplete or complete (0/1)
- *
- * returns: -EIO on error, 0 otherwise
- */
-static int
-zfcp_fsf_exchange_config_evaluate(struct zfcp_fsf_req *fsf_req, int xchg_ok)
+static int zfcp_fsf_exchange_config_evaluate(struct zfcp_fsf_req *req)
 {
 	struct fsf_qtcb_bottom_config *bottom;
-	struct zfcp_adapter *adapter = fsf_req->adapter;
+	struct zfcp_adapter *adapter = req->adapter;
 	struct Scsi_Host *shost = adapter->scsi_host;
 
-	bottom = &fsf_req->qtcb->bottom.config;
-	ZFCP_LOG_DEBUG("low/high QTCB version 0x%x/0x%x of FSF\n",
-		       bottom->low_qtcb_version, bottom->high_qtcb_version);
+	bottom = &req->qtcb->bottom.config;
+
+	if (req->data)
+		memcpy(req->data, bottom, sizeof(*bottom));
+
+	fc_host_node_name(shost) = bottom->nport_serv_param.wwnn;
+	fc_host_port_name(shost) = bottom->nport_serv_param.wwpn;
+	fc_host_port_id(shost) = bottom->s_id & ZFCP_DID_MASK;
+	fc_host_speed(shost) = bottom->fc_link_speed;
+	fc_host_supported_classes(shost) = FC_COS_CLASS2 | FC_COS_CLASS3;
+
+	adapter->hydra_version = bottom->adapter_type;
+	adapter->timer_ticks = bottom->timer_interval;
+
+	if (fc_host_permanent_port_name(shost) == -1)
+		fc_host_permanent_port_name(shost) = fc_host_port_name(shost);
+
+	switch (bottom->fc_topology) {
+	case FSF_TOPO_P2P:
+		adapter->peer_d_id = bottom->peer_d_id & ZFCP_DID_MASK;
+		adapter->peer_wwpn = bottom->plogi_payload.wwpn;
+		adapter->peer_wwnn = bottom->plogi_payload.wwnn;
+		fc_host_port_type(shost) = FC_PORTTYPE_PTP;
+		if (req->erp_action)
+			dev_info(&adapter->ccw_device->dev,
+				 "Point-to-Point fibrechannel "
+				 "configuration detected.\n");
+		break;
+	case FSF_TOPO_FABRIC:
+		fc_host_port_type(shost) = FC_PORTTYPE_NPORT;
+		if (req->erp_action)
+			dev_info(&adapter->ccw_device->dev,
+				 "Switched fabric fibrechannel "
+				 "network detected.\n");
+		break;
+	case FSF_TOPO_AL:
+		fc_host_port_type(shost) = FC_PORTTYPE_NLPORT;
+		dev_err(&adapter->ccw_device->dev,
+			"Unsupported arbitrated loop fibrechannel "
+			"topology detected, shutting down "
+			"adapter.\n");
+		zfcp_erp_adapter_shutdown(adapter, 0, 127, req);
+		return -EIO;
+	default:
+		fc_host_port_type(shost) = FC_PORTTYPE_UNKNOWN;
+		dev_err(&adapter->ccw_device->dev,
+			"The fibrechannel topology reported by the"
+			" adapter is not known by the zfcp driver,"
+			" shutting down adapter.\n");
+		zfcp_erp_adapter_shutdown(adapter, 0, 128, req);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static void zfcp_fsf_exchange_config_data_handler(struct zfcp_fsf_req *req)
+{
+	struct zfcp_adapter *adapter = req->adapter;
+	struct fsf_qtcb *qtcb = req->qtcb;
+	struct fsf_qtcb_bottom_config *bottom = &qtcb->bottom.config;
+	struct Scsi_Host *shost = adapter->scsi_host;
+
+	if (req->status & ZFCP_STATUS_FSFREQ_ERROR)
+		return;
+
 	adapter->fsf_lic_version = bottom->lic_version;
 	adapter->adapter_features = bottom->adapter_features;
 	adapter->connection_features = bottom->connection_features;
@@ -1992,40 +609,41 @@
 	adapter->peer_wwnn = 0;
 	adapter->peer_d_id = 0;
 
-	if (xchg_ok) {
+	switch (qtcb->header.fsf_status) {
+	case FSF_GOOD:
+		if (zfcp_fsf_exchange_config_evaluate(req))
+			return;
 
-		if (fsf_req->data)
-			memcpy((struct fsf_qtcb_bottom_config *) fsf_req->data,
-				bottom, sizeof (struct fsf_qtcb_bottom_config));
-
-		fc_host_node_name(shost) = bottom->nport_serv_param.wwnn;
-		fc_host_port_name(shost) = bottom->nport_serv_param.wwpn;
-		fc_host_port_id(shost) = bottom->s_id & ZFCP_DID_MASK;
-		fc_host_speed(shost) = bottom->fc_link_speed;
-		fc_host_supported_classes(shost) =
-				FC_COS_CLASS2 | FC_COS_CLASS3;
-		adapter->hydra_version = bottom->adapter_type;
-		if (fc_host_permanent_port_name(shost) == -1)
-			fc_host_permanent_port_name(shost) =
-				fc_host_port_name(shost);
-		if (bottom->fc_topology == FSF_TOPO_P2P) {
-			adapter->peer_d_id = bottom->peer_d_id & ZFCP_DID_MASK;
-			adapter->peer_wwpn = bottom->plogi_payload.wwpn;
-			adapter->peer_wwnn = bottom->plogi_payload.wwnn;
-			fc_host_port_type(shost) = FC_PORTTYPE_PTP;
-		} else if (bottom->fc_topology == FSF_TOPO_FABRIC)
-			fc_host_port_type(shost) = FC_PORTTYPE_NPORT;
-		else if (bottom->fc_topology == FSF_TOPO_AL)
-			fc_host_port_type(shost) = FC_PORTTYPE_NLPORT;
-		else
-			fc_host_port_type(shost) = FC_PORTTYPE_UNKNOWN;
-	} else {
+		if (bottom->max_qtcb_size < sizeof(struct fsf_qtcb)) {
+			dev_err(&adapter->ccw_device->dev,
+				"Maximum QTCB size (%d bytes) allowed by "
+				"the adapter is lower than the minimum "
+				"required by the driver (%ld bytes).\n",
+				bottom->max_qtcb_size,
+				sizeof(struct fsf_qtcb));
+			zfcp_erp_adapter_shutdown(adapter, 0, 129, req);
+			return;
+		}
+		atomic_set_mask(ZFCP_STATUS_ADAPTER_XCONFIG_OK,
+				&adapter->status);
+		break;
+	case FSF_EXCHANGE_CONFIG_DATA_INCOMPLETE:
 		fc_host_node_name(shost) = 0;
 		fc_host_port_name(shost) = 0;
 		fc_host_port_id(shost) = 0;
 		fc_host_speed(shost) = FC_PORTSPEED_UNKNOWN;
 		fc_host_port_type(shost) = FC_PORTTYPE_UNKNOWN;
 		adapter->hydra_version = 0;
+
+		atomic_set_mask(ZFCP_STATUS_ADAPTER_XCONFIG_OK,
+				&adapter->status);
+
+		zfcp_fsf_link_down_info_eval(req, 42,
+			&qtcb->header.fsf_status_qual.link_down_info);
+		break;
+	default:
+		zfcp_erp_adapter_shutdown(adapter, 0, 130, req);
+		return;
 	}
 
 	if (adapter->adapter_features & FSF_FEATURE_HBAAPI_MANAGEMENT) {
@@ -2036,271 +654,29 @@
 		       min(FC_SERIAL_NUMBER_SIZE, 17));
 	}
 
-	if (fsf_req->erp_action)
-		ZFCP_LOG_NORMAL("The adapter %s reported the following "
-				"characteristics:\n"
-				"WWNN 0x%016Lx, WWPN 0x%016Lx, "
-				"S_ID 0x%06x,\n"
-				"adapter version 0x%x, "
-				"LIC version 0x%x, "
-				"FC link speed %d Gb/s\n",
-				zfcp_get_busid_by_adapter(adapter),
-				(wwn_t) fc_host_node_name(shost),
-				(wwn_t) fc_host_port_name(shost),
-				fc_host_port_id(shost),
-				adapter->hydra_version,
-				adapter->fsf_lic_version,
-				fc_host_speed(shost));
-	if (ZFCP_QTCB_VERSION < bottom->low_qtcb_version) {
-		ZFCP_LOG_NORMAL("error: the adapter %s "
-				"only supports newer control block "
-				"versions in comparison to this device "
-				"driver (try updated device driver)\n",
-				zfcp_get_busid_by_adapter(adapter));
-		zfcp_erp_adapter_shutdown(adapter, 0, 125, fsf_req);
-		return -EIO;
+	if (FSF_QTCB_CURRENT_VERSION < bottom->low_qtcb_version) {
+		dev_err(&adapter->ccw_device->dev,
+			"The adapter only supports newer control block "
+			"versions, try updated device driver.\n");
+		zfcp_erp_adapter_shutdown(adapter, 0, 125, req);
+		return;
 	}
-	if (ZFCP_QTCB_VERSION > bottom->high_qtcb_version) {
-		ZFCP_LOG_NORMAL("error: the adapter %s "
-				"only supports older control block "
-				"versions than this device driver uses"
-				"(consider a microcode upgrade)\n",
-				zfcp_get_busid_by_adapter(adapter));
-		zfcp_erp_adapter_shutdown(adapter, 0, 126, fsf_req);
-		return -EIO;
+	if (FSF_QTCB_CURRENT_VERSION > bottom->high_qtcb_version) {
+		dev_err(&adapter->ccw_device->dev,
+			"The adapter only supports older control block "
+			"versions, consider a microcode upgrade.\n");
+		zfcp_erp_adapter_shutdown(adapter, 0, 126, req);
 	}
-	return 0;
 }
 
-/**
- * function:    zfcp_fsf_exchange_config_data_handler
- *
- * purpose:     is called for finished Exchange Configuration Data command
- *
- * returns:
- */
-static int
-zfcp_fsf_exchange_config_data_handler(struct zfcp_fsf_req *fsf_req)
+static void zfcp_fsf_exchange_port_evaluate(struct zfcp_fsf_req *req)
 {
-	struct fsf_qtcb_bottom_config *bottom;
-	struct zfcp_adapter *adapter = fsf_req->adapter;
-	struct fsf_qtcb *qtcb = fsf_req->qtcb;
+	struct zfcp_adapter *adapter = req->adapter;
+	struct fsf_qtcb_bottom_port *bottom = &req->qtcb->bottom.port;
+	struct Scsi_Host *shost = adapter->scsi_host;
 
-	if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR)
-		return -EIO;
-
-	switch (qtcb->header.fsf_status) {
-
-	case FSF_GOOD:
-		if (zfcp_fsf_exchange_config_evaluate(fsf_req, 1))
-			return -EIO;
-
-		switch (fc_host_port_type(adapter->scsi_host)) {
-		case FC_PORTTYPE_PTP:
-			ZFCP_LOG_NORMAL("Point-to-Point fibrechannel "
-					"configuration detected at adapter %s\n"
-					"Peer WWNN 0x%016llx, "
-					"peer WWPN 0x%016llx, "
-					"peer d_id 0x%06x\n",
-					zfcp_get_busid_by_adapter(adapter),
-					adapter->peer_wwnn,
-					adapter->peer_wwpn,
-					adapter->peer_d_id);
-			break;
-		case FC_PORTTYPE_NLPORT:
-			ZFCP_LOG_NORMAL("error: Arbitrated loop fibrechannel "
-					"topology detected at adapter %s "
-					"unsupported, shutting down adapter\n",
-					zfcp_get_busid_by_adapter(adapter));
-			zfcp_erp_adapter_shutdown(adapter, 0, 127, fsf_req);
-			return -EIO;
-		case FC_PORTTYPE_NPORT:
-			if (fsf_req->erp_action)
-				ZFCP_LOG_NORMAL("Switched fabric fibrechannel "
-						"network detected at adapter "
-						"%s.\n",
-					zfcp_get_busid_by_adapter(adapter));
-			break;
-		default:
-			ZFCP_LOG_NORMAL("bug: The fibrechannel topology "
-					"reported by the exchange "
-					"configuration command for "
-					"the adapter %s is not "
-					"of a type known to the zfcp "
-					"driver, shutting down adapter\n",
-					zfcp_get_busid_by_adapter(adapter));
-			zfcp_erp_adapter_shutdown(adapter, 0, 128, fsf_req);
-			return -EIO;
-		}
-		bottom = &qtcb->bottom.config;
-		if (bottom->max_qtcb_size < sizeof(struct fsf_qtcb)) {
-			ZFCP_LOG_NORMAL("bug: Maximum QTCB size (%d bytes) "
-					"allowed by the adapter %s "
-					"is lower than the minimum "
-					"required by the driver (%ld bytes).\n",
-					bottom->max_qtcb_size,
-					zfcp_get_busid_by_adapter(adapter),
-					sizeof(struct fsf_qtcb));
-			zfcp_erp_adapter_shutdown(adapter, 0, 129, fsf_req);
-			return -EIO;
-		}
-		atomic_set_mask(ZFCP_STATUS_ADAPTER_XCONFIG_OK,
-				&adapter->status);
-		break;
-	case FSF_EXCHANGE_CONFIG_DATA_INCOMPLETE:
-		if (zfcp_fsf_exchange_config_evaluate(fsf_req, 0))
-			return -EIO;
-
-		atomic_set_mask(ZFCP_STATUS_ADAPTER_XCONFIG_OK,
-				&adapter->status);
-
-		zfcp_fsf_link_down_info_eval(fsf_req, 42,
-			&qtcb->header.fsf_status_qual.link_down_info);
-		break;
-	default:
-		zfcp_erp_adapter_shutdown(adapter, 0, 130, fsf_req);
-		return -EIO;
-	}
-	return 0;
-}
-
-/**
- * zfcp_fsf_exchange_port_data - request information about local port
- * @erp_action: ERP action for the adapter for which port data is requested
- */
-int
-zfcp_fsf_exchange_port_data(struct zfcp_erp_action *erp_action)
-{
-	volatile struct qdio_buffer_element *sbale;
-	struct zfcp_fsf_req *fsf_req;
-	struct zfcp_adapter *adapter = erp_action->adapter;
-	unsigned long lock_flags;
-	int retval;
-
-	if (!(adapter->adapter_features & FSF_FEATURE_HBAAPI_MANAGEMENT)) {
-		ZFCP_LOG_INFO("error: exchange port data "
-			      "command not supported by adapter %s\n",
-			      zfcp_get_busid_by_adapter(adapter));
-		return -EOPNOTSUPP;
-	}
-
-	/* setup new FSF request */
-	retval = zfcp_fsf_req_create(adapter, FSF_QTCB_EXCHANGE_PORT_DATA,
-				     ZFCP_REQ_AUTO_CLEANUP,
-				     adapter->pool.fsf_req_erp,
-				     &lock_flags, &fsf_req);
-	if (retval) {
-		ZFCP_LOG_INFO("error: Out of resources. Could not create an "
-			      "exchange port data request for "
-			      "the adapter %s.\n",
-			      zfcp_get_busid_by_adapter(adapter));
-		write_unlock_irqrestore(&adapter->request_queue.queue_lock,
-					lock_flags);
-		return retval;
-	}
-
-	sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
-	sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
-	sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
-
-	erp_action->fsf_req = fsf_req;
-	fsf_req->erp_action = erp_action;
-	zfcp_erp_start_timer(fsf_req);
-
-	retval = zfcp_fsf_req_send(fsf_req);
-	write_unlock_irqrestore(&adapter->request_queue.queue_lock, lock_flags);
-
-	if (retval) {
-		ZFCP_LOG_INFO("error: Could not send an exchange port data "
-			      "command on the adapter %s\n",
-			      zfcp_get_busid_by_adapter(adapter));
-		zfcp_fsf_req_free(fsf_req);
-		erp_action->fsf_req = NULL;
-	}
-	else
-		ZFCP_LOG_DEBUG("exchange port data request initiated "
-			       "(adapter %s)\n",
-			       zfcp_get_busid_by_adapter(adapter));
-	return retval;
-}
-
-
-/**
- * zfcp_fsf_exchange_port_data_sync - request information about local port
- * and wait until information is ready
- */
-int
-zfcp_fsf_exchange_port_data_sync(struct zfcp_adapter *adapter,
-				struct fsf_qtcb_bottom_port *data)
-{
-	volatile struct qdio_buffer_element *sbale;
-	struct zfcp_fsf_req *fsf_req;
-	unsigned long lock_flags;
-	int retval;
-
-	if (!(adapter->adapter_features & FSF_FEATURE_HBAAPI_MANAGEMENT)) {
-		ZFCP_LOG_INFO("error: exchange port data "
-			      "command not supported by adapter %s\n",
-			      zfcp_get_busid_by_adapter(adapter));
-		return -EOPNOTSUPP;
-	}
-
-	/* setup new FSF request */
-	retval = zfcp_fsf_req_create(adapter, FSF_QTCB_EXCHANGE_PORT_DATA,
-				0, NULL, &lock_flags, &fsf_req);
-	if (retval) {
-		ZFCP_LOG_INFO("error: Out of resources. Could not create an "
-			      "exchange port data request for "
-			      "the adapter %s.\n",
-			      zfcp_get_busid_by_adapter(adapter));
-		write_unlock_irqrestore(&adapter->request_queue.queue_lock,
-					lock_flags);
-		return retval;
-	}
-
-	if (data)
-		fsf_req->data = (unsigned long) data;
-
-	sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
-	sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
-	sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
-
-	zfcp_fsf_start_timer(fsf_req, ZFCP_FSF_REQUEST_TIMEOUT);
-	retval = zfcp_fsf_req_send(fsf_req);
-	write_unlock_irqrestore(&adapter->request_queue.queue_lock, lock_flags);
-
-	if (retval)
-		ZFCP_LOG_INFO("error: Could not send an exchange port data "
-			      "command on the adapter %s\n",
-			      zfcp_get_busid_by_adapter(adapter));
-	else
-		wait_event(fsf_req->completion_wq,
-			   fsf_req->status & ZFCP_STATUS_FSFREQ_COMPLETED);
-
-	zfcp_fsf_req_free(fsf_req);
-
-	return retval;
-}
-
-/**
- * zfcp_fsf_exchange_port_evaluate
- * @fsf_req: fsf_req which belongs to xchg port data request
- * @xchg_ok: specifies if xchg port data was incomplete or complete (0/1)
- */
-static void
-zfcp_fsf_exchange_port_evaluate(struct zfcp_fsf_req *fsf_req, int xchg_ok)
-{
-	struct zfcp_adapter *adapter;
-	struct fsf_qtcb_bottom_port *bottom;
-	struct Scsi_Host *shost;
-
-	adapter = fsf_req->adapter;
-	bottom = &fsf_req->qtcb->bottom.port;
-	shost = adapter->scsi_host;
-
-	if (fsf_req->data)
-		memcpy((struct fsf_qtcb_bottom_port*) fsf_req->data, bottom,
-			sizeof(struct fsf_qtcb_bottom_port));
+	if (req->data)
+		memcpy(req->data, bottom, sizeof(*bottom));
 
 	if (adapter->connection_features & FSF_FEATURE_NPIV_MODE)
 		fc_host_permanent_port_name(shost) = bottom->wwpn;
@@ -2310,207 +686,801 @@
 	fc_host_supported_speeds(shost) = bottom->supported_speed;
 }
 
-/**
- * zfcp_fsf_exchange_port_data_handler - handler for exchange_port_data request
- * @fsf_req: pointer to struct zfcp_fsf_req
- */
-static void
-zfcp_fsf_exchange_port_data_handler(struct zfcp_fsf_req *fsf_req)
+static void zfcp_fsf_exchange_port_data_handler(struct zfcp_fsf_req *req)
 {
-	struct zfcp_adapter *adapter;
-	struct fsf_qtcb *qtcb;
+	struct zfcp_adapter *adapter = req->adapter;
+	struct fsf_qtcb *qtcb = req->qtcb;
 
-	adapter = fsf_req->adapter;
-	qtcb = fsf_req->qtcb;
-
-	if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR)
+	if (req->status & ZFCP_STATUS_FSFREQ_ERROR)
 		return;
 
 	switch (qtcb->header.fsf_status) {
-        case FSF_GOOD:
-		zfcp_fsf_exchange_port_evaluate(fsf_req, 1);
+	case FSF_GOOD:
+		zfcp_fsf_exchange_port_evaluate(req);
 		atomic_set_mask(ZFCP_STATUS_ADAPTER_XPORT_OK, &adapter->status);
 		break;
 	case FSF_EXCHANGE_CONFIG_DATA_INCOMPLETE:
-		zfcp_fsf_exchange_port_evaluate(fsf_req, 0);
+		zfcp_fsf_exchange_port_evaluate(req);
 		atomic_set_mask(ZFCP_STATUS_ADAPTER_XPORT_OK, &adapter->status);
-		zfcp_fsf_link_down_info_eval(fsf_req, 43,
+		zfcp_fsf_link_down_info_eval(req, 43,
 			&qtcb->header.fsf_status_qual.link_down_info);
-                break;
+		break;
 	}
 }
 
+static int zfcp_fsf_sbal_check(struct zfcp_qdio_queue *queue)
+{
+	spin_lock(&queue->lock);
+	if (atomic_read(&queue->count))
+		return 1;
+	spin_unlock(&queue->lock);
+	return 0;
+}
 
-/*
- * function:    zfcp_fsf_open_port
- *
- * purpose:
- *
- * returns:	address of initiated FSF request
- *		NULL - request could not be initiated
- */
-int
-zfcp_fsf_open_port(struct zfcp_erp_action *erp_action)
+static int zfcp_fsf_req_sbal_get(struct zfcp_adapter *adapter)
+{
+	long ret;
+	struct zfcp_qdio_queue *req_q = &adapter->req_q;
+
+	spin_unlock(&req_q->lock);
+	ret = wait_event_interruptible_timeout(adapter->request_wq,
+					zfcp_fsf_sbal_check(req_q), 5 * HZ);
+	if (ret > 0)
+		return 0;
+
+	spin_lock(&req_q->lock);
+	return -EIO;
+}
+
+static struct zfcp_fsf_req *zfcp_fsf_alloc_noqtcb(mempool_t *pool)
+{
+	struct zfcp_fsf_req *req;
+	req = mempool_alloc(pool, GFP_ATOMIC);
+	if (!req)
+		return NULL;
+	memset(req, 0, sizeof(*req));
+	return req;
+}
+
+static struct zfcp_fsf_req *zfcp_fsf_alloc_qtcb(mempool_t *pool)
+{
+	struct zfcp_fsf_req_qtcb *qtcb;
+
+	if (likely(pool))
+		qtcb = mempool_alloc(pool, GFP_ATOMIC);
+	else
+		qtcb = kmem_cache_alloc(zfcp_data.fsf_req_qtcb_cache,
+					GFP_ATOMIC);
+	if (unlikely(!qtcb))
+		return NULL;
+
+	memset(qtcb, 0, sizeof(*qtcb));
+	qtcb->fsf_req.qtcb = &qtcb->qtcb;
+	qtcb->fsf_req.pool = pool;
+
+	return &qtcb->fsf_req;
+}
+
+static struct zfcp_fsf_req *zfcp_fsf_req_create(struct zfcp_adapter *adapter,
+						u32 fsf_cmd, int req_flags,
+						mempool_t *pool)
 {
 	volatile struct qdio_buffer_element *sbale;
-	struct zfcp_fsf_req *fsf_req;
-	unsigned long lock_flags;
-	int retval = 0;
 
-	/* setup new FSF request */
-	retval = zfcp_fsf_req_create(erp_action->adapter,
-				     FSF_QTCB_OPEN_PORT_WITH_DID,
-				     ZFCP_WAIT_FOR_SBAL | ZFCP_REQ_AUTO_CLEANUP,
-				     erp_action->adapter->pool.fsf_req_erp,
-				     &lock_flags, &fsf_req);
-	if (retval < 0) {
-		ZFCP_LOG_INFO("error: Could not create open port request "
-			      "for port 0x%016Lx on adapter %s.\n",
-			      erp_action->port->wwpn,
-			      zfcp_get_busid_by_adapter(erp_action->adapter));
+	struct zfcp_fsf_req *req;
+	struct zfcp_qdio_queue *req_q = &adapter->req_q;
+
+	if (req_flags & ZFCP_REQ_NO_QTCB)
+		req = zfcp_fsf_alloc_noqtcb(pool);
+	else
+		req = zfcp_fsf_alloc_qtcb(pool);
+
+	if (unlikely(!req))
+		return ERR_PTR(-EIO);
+
+	if (adapter->req_no == 0)
+		adapter->req_no++;
+
+	INIT_LIST_HEAD(&req->list);
+	init_timer(&req->timer);
+	init_waitqueue_head(&req->completion_wq);
+
+	req->adapter = adapter;
+	req->fsf_command = fsf_cmd;
+	req->req_id = adapter->req_no++;
+	req->sbal_number = 1;
+	req->sbal_first = req_q->first;
+	req->sbal_last = req_q->first;
+	req->sbale_curr = 1;
+
+	sbale = zfcp_qdio_sbale_req(req);
+	sbale[0].addr = (void *) req->req_id;
+	sbale[0].flags |= SBAL_FLAGS0_COMMAND;
+
+	if (likely(req->qtcb)) {
+		req->qtcb->prefix.req_seq_no = req->adapter->fsf_req_seq_no;
+		req->qtcb->prefix.req_id = req->req_id;
+		req->qtcb->prefix.ulp_info = 26;
+		req->qtcb->prefix.qtcb_type = fsf_qtcb_type[req->fsf_command];
+		req->qtcb->prefix.qtcb_version = FSF_QTCB_CURRENT_VERSION;
+		req->qtcb->header.req_handle = req->req_id;
+		req->qtcb->header.fsf_command = req->fsf_command;
+		req->seq_no = adapter->fsf_req_seq_no;
+		req->qtcb->prefix.req_seq_no = adapter->fsf_req_seq_no;
+		sbale[1].addr = (void *) req->qtcb;
+		sbale[1].length = sizeof(struct fsf_qtcb);
+	}
+
+	if (!(atomic_read(&adapter->status) & ZFCP_STATUS_ADAPTER_QDIOUP)) {
+		zfcp_fsf_req_free(req);
+		return ERR_PTR(-EIO);
+	}
+
+	if (likely(req_flags & ZFCP_REQ_AUTO_CLEANUP))
+		req->status |= ZFCP_STATUS_FSFREQ_CLEANUP;
+
+	return req;
+}
+
+static int zfcp_fsf_req_send(struct zfcp_fsf_req *req)
+{
+	struct zfcp_adapter *adapter = req->adapter;
+	struct zfcp_qdio_queue *req_q = &adapter->req_q;
+	int idx;
+
+	/* put allocated FSF request into hash table */
+	spin_lock(&adapter->req_list_lock);
+	idx = zfcp_reqlist_hash(req->req_id);
+	list_add_tail(&req->list, &adapter->req_list[idx]);
+	spin_unlock(&adapter->req_list_lock);
+
+	req->issued = get_clock();
+	if (zfcp_qdio_send(req)) {
+		/* Queues are down..... */
+		del_timer(&req->timer);
+		spin_lock(&adapter->req_list_lock);
+		zfcp_reqlist_remove(adapter, req);
+		spin_unlock(&adapter->req_list_lock);
+		/* undo changes in request queue made for this request */
+		atomic_add(req->sbal_number, &req_q->count);
+		req_q->first -= req->sbal_number;
+		req_q->first += QDIO_MAX_BUFFERS_PER_Q;
+		req_q->first %= QDIO_MAX_BUFFERS_PER_Q; /* wrap */
+		zfcp_erp_adapter_reopen(adapter, 0, 116, req);
+		return -EIO;
+	}
+
+	/* Don't increase for unsolicited status */
+	if (req->qtcb)
+		adapter->fsf_req_seq_no++;
+
+	return 0;
+}
+
+/**
+ * zfcp_fsf_status_read - send status read request
+ * @adapter: pointer to struct zfcp_adapter
+ * @req_flags: request flags
+ * Returns: 0 on success, ERROR otherwise
+ */
+int zfcp_fsf_status_read(struct zfcp_adapter *adapter)
+{
+	struct zfcp_fsf_req *req;
+	struct fsf_status_read_buffer *sr_buf;
+	volatile struct qdio_buffer_element *sbale;
+	int retval = -EIO;
+
+	spin_lock(&adapter->req_q.lock);
+	if (zfcp_fsf_req_sbal_get(adapter))
+		goto out;
+
+	req = zfcp_fsf_req_create(adapter, FSF_QTCB_UNSOLICITED_STATUS,
+				  ZFCP_REQ_NO_QTCB,
+				  adapter->pool.fsf_req_status_read);
+	if (unlikely(IS_ERR(req))) {
+		retval = PTR_ERR(req);
 		goto out;
 	}
 
-	sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
-        sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
-        sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
+	sbale = zfcp_qdio_sbale_req(req);
+	sbale[0].flags |= SBAL_FLAGS0_TYPE_STATUS;
+	sbale[2].flags |= SBAL_FLAGS_LAST_ENTRY;
+	req->sbale_curr = 2;
 
-	fsf_req->qtcb->bottom.support.d_id = erp_action->port->d_id;
-	atomic_set_mask(ZFCP_STATUS_COMMON_OPENING, &erp_action->port->status);
-	fsf_req->data = (unsigned long) erp_action->port;
-	fsf_req->erp_action = erp_action;
-	erp_action->fsf_req = fsf_req;
-
-	zfcp_erp_start_timer(fsf_req);
-	retval = zfcp_fsf_req_send(fsf_req);
-	if (retval) {
-		ZFCP_LOG_INFO("error: Could not send open port request for "
-			      "port 0x%016Lx on adapter %s.\n",
-			      erp_action->port->wwpn,
-			      zfcp_get_busid_by_adapter(erp_action->adapter));
-		zfcp_fsf_req_free(fsf_req);
-		erp_action->fsf_req = NULL;
-		goto out;
+	sr_buf = mempool_alloc(adapter->pool.data_status_read, GFP_ATOMIC);
+	if (!sr_buf) {
+		retval = -ENOMEM;
+		goto failed_buf;
 	}
+	memset(sr_buf, 0, sizeof(*sr_buf));
+	req->data = sr_buf;
+	sbale = zfcp_qdio_sbale_curr(req);
+	sbale->addr = (void *) sr_buf;
+	sbale->length = sizeof(*sr_buf);
 
-	ZFCP_LOG_DEBUG("open port request initiated "
-		       "(adapter %s,  port 0x%016Lx)\n",
-		       zfcp_get_busid_by_adapter(erp_action->adapter),
-		       erp_action->port->wwpn);
- out:
-	write_unlock_irqrestore(&erp_action->adapter->request_queue.queue_lock,
-				lock_flags);
+	retval = zfcp_fsf_req_send(req);
+	if (retval)
+		goto failed_req_send;
+
+	goto out;
+
+failed_req_send:
+	mempool_free(sr_buf, adapter->pool.data_status_read);
+failed_buf:
+	zfcp_fsf_req_free(req);
+	zfcp_hba_dbf_event_fsf_unsol("fail", adapter, NULL);
+out:
+	spin_unlock(&adapter->req_q.lock);
 	return retval;
 }
 
-/*
- * function:    zfcp_fsf_open_port_handler
- *
- * purpose:	is called for finished Open Port command
- *
- * returns:
- */
-static int
-zfcp_fsf_open_port_handler(struct zfcp_fsf_req *fsf_req)
+static void zfcp_fsf_abort_fcp_command_handler(struct zfcp_fsf_req *req)
 {
-	int retval = -EINVAL;
-	struct zfcp_port *port;
-	struct fsf_plogi *plogi;
-	struct fsf_qtcb_header *header;
-	u16 subtable, rule, counter;
+	struct zfcp_unit *unit = req->data;
+	union fsf_status_qual *fsq = &req->qtcb->header.fsf_status_qual;
 
-	port = (struct zfcp_port *) fsf_req->data;
-	header = &fsf_req->qtcb->header;
+	if (req->status & ZFCP_STATUS_FSFREQ_ERROR)
+		return;
 
-	if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
-		/* don't change port status in our bookkeeping */
+	switch (req->qtcb->header.fsf_status) {
+	case FSF_PORT_HANDLE_NOT_VALID:
+		if (fsq->word[0] == fsq->word[1]) {
+			zfcp_erp_adapter_reopen(unit->port->adapter, 0, 104,
+						req);
+			req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+		}
+		break;
+	case FSF_LUN_HANDLE_NOT_VALID:
+		if (fsq->word[0] == fsq->word[1]) {
+			zfcp_erp_port_reopen(unit->port, 0, 105, req);
+			req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+		}
+		break;
+	case FSF_FCP_COMMAND_DOES_NOT_EXIST:
+		req->status |= ZFCP_STATUS_FSFREQ_ABORTNOTNEEDED;
+		break;
+	case FSF_PORT_BOXED:
+		zfcp_erp_port_boxed(unit->port, 47, req);
+		req->status |= ZFCP_STATUS_FSFREQ_ERROR |
+			       ZFCP_STATUS_FSFREQ_RETRY;
+		break;
+	case FSF_LUN_BOXED:
+		zfcp_erp_unit_boxed(unit, 48, req);
+		req->status |= ZFCP_STATUS_FSFREQ_ERROR |
+			       ZFCP_STATUS_FSFREQ_RETRY;
+                break;
+	case FSF_ADAPTER_STATUS_AVAILABLE:
+		switch (fsq->word[0]) {
+		case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE:
+			zfcp_test_link(unit->port);
+		case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED:
+			req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+			break;
+		}
+		break;
+	case FSF_GOOD:
+		req->status |= ZFCP_STATUS_FSFREQ_ABORTSUCCEEDED;
+		break;
+	}
+}
+
+/**
+ * zfcp_fsf_abort_fcp_command - abort running SCSI command
+ * @old_req_id: unsigned long
+ * @adapter: pointer to struct zfcp_adapter
+ * @unit: pointer to struct zfcp_unit
+ * @req_flags: integer specifying the request flags
+ * Returns: pointer to struct zfcp_fsf_req
+ *
+ * FIXME(design): should be watched by a timeout !!!
+ */
+
+struct zfcp_fsf_req *zfcp_fsf_abort_fcp_command(unsigned long old_req_id,
+						struct zfcp_adapter *adapter,
+						struct zfcp_unit *unit,
+						int req_flags)
+{
+	volatile struct qdio_buffer_element *sbale;
+	struct zfcp_fsf_req *req = NULL;
+
+	spin_lock(&adapter->req_q.lock);
+	if (!atomic_read(&adapter->req_q.count))
+		goto out;
+	req = zfcp_fsf_req_create(adapter, FSF_QTCB_ABORT_FCP_CMND,
+				  req_flags, adapter->pool.fsf_req_abort);
+	if (unlikely(IS_ERR(req)))
+		goto out;
+
+	if (unlikely(!(atomic_read(&unit->status) &
+		       ZFCP_STATUS_COMMON_UNBLOCKED)))
+		goto out_error_free;
+
+	sbale = zfcp_qdio_sbale_req(req);
+	sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
+	sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
+
+	req->data = unit;
+	req->handler = zfcp_fsf_abort_fcp_command_handler;
+	req->qtcb->header.lun_handle = unit->handle;
+	req->qtcb->header.port_handle = unit->port->handle;
+	req->qtcb->bottom.support.req_handle = (u64) old_req_id;
+
+	zfcp_fsf_start_timer(req, ZFCP_SCSI_ER_TIMEOUT);
+	if (!zfcp_fsf_req_send(req))
+		goto out;
+
+out_error_free:
+	zfcp_fsf_req_free(req);
+	req = NULL;
+out:
+	spin_unlock(&adapter->req_q.lock);
+	return req;
+}
+
+static void zfcp_fsf_send_ct_handler(struct zfcp_fsf_req *req)
+{
+	struct zfcp_adapter *adapter = req->adapter;
+	struct zfcp_send_ct *send_ct = req->data;
+	struct zfcp_port *port = send_ct->port;
+	struct fsf_qtcb_header *header = &req->qtcb->header;
+
+	send_ct->status = -EINVAL;
+
+	if (req->status & ZFCP_STATUS_FSFREQ_ERROR)
 		goto skip_fsfstatus;
+
+	switch (header->fsf_status) {
+        case FSF_GOOD:
+		zfcp_san_dbf_event_ct_response(req);
+		send_ct->status = 0;
+		break;
+        case FSF_SERVICE_CLASS_NOT_SUPPORTED:
+		zfcp_fsf_class_not_supp(req);
+		break;
+        case FSF_ADAPTER_STATUS_AVAILABLE:
+                switch (header->fsf_status_qual.word[0]){
+                case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE:
+			zfcp_test_link(port);
+                case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED:
+			req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+			break;
+                }
+                break;
+	case FSF_ACCESS_DENIED:
+		zfcp_fsf_access_denied_port(req, port);
+		break;
+        case FSF_PORT_BOXED:
+		zfcp_erp_port_boxed(port, 49, req);
+		req->status |= ZFCP_STATUS_FSFREQ_ERROR |
+			       ZFCP_STATUS_FSFREQ_RETRY;
+		break;
+	case FSF_PORT_HANDLE_NOT_VALID:
+		zfcp_erp_adapter_reopen(adapter, 0, 106, req);
+	case FSF_GENERIC_COMMAND_REJECTED:
+	case FSF_PAYLOAD_SIZE_MISMATCH:
+	case FSF_REQUEST_SIZE_TOO_LARGE:
+	case FSF_RESPONSE_SIZE_TOO_LARGE:
+	case FSF_SBAL_MISMATCH:
+		req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+		break;
 	}
 
-	/* evaluate FSF status in QTCB */
+skip_fsfstatus:
+	if (send_ct->handler)
+		send_ct->handler(send_ct->handler_data);
+}
+
+static int zfcp_fsf_setup_sbals(struct zfcp_fsf_req *req,
+				struct scatterlist *sg_req,
+				struct scatterlist *sg_resp, int max_sbals)
+{
+	int bytes;
+
+	bytes = zfcp_qdio_sbals_from_sg(req, SBAL_FLAGS0_TYPE_WRITE_READ,
+					sg_req, max_sbals);
+	if (bytes <= 0)
+		return -ENOMEM;
+	req->qtcb->bottom.support.req_buf_length = bytes;
+	req->sbale_curr = ZFCP_LAST_SBALE_PER_SBAL;
+
+	bytes = zfcp_qdio_sbals_from_sg(req, SBAL_FLAGS0_TYPE_WRITE_READ,
+					sg_resp, max_sbals);
+	if (bytes <= 0)
+		return -ENOMEM;
+	req->qtcb->bottom.support.resp_buf_length = bytes;
+
+	return 0;
+}
+
+/**
+ * zfcp_fsf_send_ct - initiate a Generic Service request (FC-GS)
+ * @ct: pointer to struct zfcp_send_ct with data for request
+ * @pool: if non-null this mempool is used to allocate struct zfcp_fsf_req
+ * @erp_action: if non-null the Generic Service request sent within ERP
+ */
+int zfcp_fsf_send_ct(struct zfcp_send_ct *ct, mempool_t *pool,
+		     struct zfcp_erp_action *erp_action)
+{
+	struct zfcp_port *port = ct->port;
+	struct zfcp_adapter *adapter = port->adapter;
+	struct zfcp_fsf_req *req;
+	int ret = -EIO;
+
+	spin_lock(&adapter->req_q.lock);
+	if (zfcp_fsf_req_sbal_get(adapter))
+		goto out;
+
+	req = zfcp_fsf_req_create(adapter, FSF_QTCB_SEND_GENERIC,
+				  ZFCP_REQ_AUTO_CLEANUP, pool);
+	if (unlikely(IS_ERR(req))) {
+		ret = PTR_ERR(req);
+		goto out;
+	}
+
+	ret = zfcp_fsf_setup_sbals(req, ct->req, ct->resp,
+				   FSF_MAX_SBALS_PER_REQ);
+	if (ret)
+		goto failed_send;
+
+	req->handler = zfcp_fsf_send_ct_handler;
+	req->qtcb->header.port_handle = port->handle;
+	req->qtcb->bottom.support.service_class = FSF_CLASS_3;
+	req->qtcb->bottom.support.timeout = ct->timeout;
+	req->data = ct;
+
+	zfcp_san_dbf_event_ct_request(req);
+
+	if (erp_action) {
+		erp_action->fsf_req = req;
+		req->erp_action = erp_action;
+		zfcp_fsf_start_erp_timer(req);
+	} else
+		zfcp_fsf_start_timer(req, ZFCP_FSF_REQUEST_TIMEOUT);
+
+	ret = zfcp_fsf_req_send(req);
+	if (ret)
+		goto failed_send;
+
+	goto out;
+
+failed_send:
+	zfcp_fsf_req_free(req);
+	if (erp_action)
+		erp_action->fsf_req = NULL;
+out:
+	spin_unlock(&adapter->req_q.lock);
+	return ret;
+}
+
+static void zfcp_fsf_send_els_handler(struct zfcp_fsf_req *req)
+{
+	struct zfcp_send_els *send_els = req->data;
+	struct zfcp_port *port = send_els->port;
+	struct fsf_qtcb_header *header = &req->qtcb->header;
+
+	send_els->status = -EINVAL;
+
+	if (req->status & ZFCP_STATUS_FSFREQ_ERROR)
+		goto skip_fsfstatus;
+
 	switch (header->fsf_status) {
-
-	case FSF_PORT_ALREADY_OPEN:
-		ZFCP_LOG_NORMAL("bug: remote port 0x%016Lx on adapter %s "
-				"is already open.\n",
-				port->wwpn, zfcp_get_busid_by_port(port));
-		/*
-		 * This is a bug, however operation should continue normally
-		 * if it is simply ignored
-		 */
+	case FSF_GOOD:
+		zfcp_san_dbf_event_els_response(req);
+		send_els->status = 0;
 		break;
-
-	case FSF_ACCESS_DENIED:
-		ZFCP_LOG_NORMAL("Access denied, cannot open port 0x%016Lx "
-				"on adapter %s\n",
-				port->wwpn, zfcp_get_busid_by_port(port));
-		for (counter = 0; counter < 2; counter++) {
-			subtable = header->fsf_status_qual.halfword[counter * 2];
-			rule = header->fsf_status_qual.halfword[counter * 2 + 1];
-			switch (subtable) {
-			case FSF_SQ_CFDC_SUBTABLE_OS:
-			case FSF_SQ_CFDC_SUBTABLE_PORT_WWPN:
-			case FSF_SQ_CFDC_SUBTABLE_PORT_DID:
-			case FSF_SQ_CFDC_SUBTABLE_LUN:
-				ZFCP_LOG_INFO("Access denied (%s rule %d)\n",
-					zfcp_act_subtable_type[subtable], rule);
-				break;
-			}
+	case FSF_SERVICE_CLASS_NOT_SUPPORTED:
+		zfcp_fsf_class_not_supp(req);
+		break;
+	case FSF_ADAPTER_STATUS_AVAILABLE:
+		switch (header->fsf_status_qual.word[0]){
+		case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE:
+			if (port && (send_els->ls_code != ZFCP_LS_ADISC))
+				zfcp_test_link(port);
+			/*fall through */
+		case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED:
+		case FSF_SQ_RETRY_IF_POSSIBLE:
+			req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+			break;
 		}
-		zfcp_erp_port_access_denied(port, 57, fsf_req);
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
 		break;
+	case FSF_ELS_COMMAND_REJECTED:
+	case FSF_PAYLOAD_SIZE_MISMATCH:
+	case FSF_REQUEST_SIZE_TOO_LARGE:
+	case FSF_RESPONSE_SIZE_TOO_LARGE:
+		break;
+	case FSF_ACCESS_DENIED:
+		zfcp_fsf_access_denied_port(req, port);
+		break;
+	case FSF_SBAL_MISMATCH:
+		/* should never occure, avoided in zfcp_fsf_send_els */
+		/* fall through */
+	default:
+		req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+		break;
+	}
+skip_fsfstatus:
+	if (send_els->handler)
+		send_els->handler(send_els->handler_data);
+}
 
+/**
+ * zfcp_fsf_send_els - initiate an ELS command (FC-FS)
+ * @els: pointer to struct zfcp_send_els with data for the command
+ */
+int zfcp_fsf_send_els(struct zfcp_send_els *els)
+{
+	struct zfcp_fsf_req *req;
+	struct zfcp_adapter *adapter = els->adapter;
+	struct fsf_qtcb_bottom_support *bottom;
+	int ret = -EIO;
+
+	if (unlikely(!(atomic_read(&els->port->status) &
+		       ZFCP_STATUS_COMMON_UNBLOCKED)))
+		return -EBUSY;
+
+	spin_lock(&adapter->req_q.lock);
+	if (!atomic_read(&adapter->req_q.count))
+		goto out;
+	req = zfcp_fsf_req_create(adapter, FSF_QTCB_SEND_ELS,
+				  ZFCP_REQ_AUTO_CLEANUP, NULL);
+	if (unlikely(IS_ERR(req))) {
+		ret = PTR_ERR(req);
+		goto out;
+	}
+
+	ret = zfcp_fsf_setup_sbals(req, els->req, els->resp,
+				   FSF_MAX_SBALS_PER_ELS_REQ);
+	if (ret)
+		goto failed_send;
+
+	bottom = &req->qtcb->bottom.support;
+	req->handler = zfcp_fsf_send_els_handler;
+	bottom->d_id = els->d_id;
+	bottom->service_class = FSF_CLASS_3;
+	bottom->timeout = 2 * R_A_TOV;
+	req->data = els;
+
+	zfcp_san_dbf_event_els_request(req);
+
+	zfcp_fsf_start_timer(req, ZFCP_FSF_REQUEST_TIMEOUT);
+	ret = zfcp_fsf_req_send(req);
+	if (ret)
+		goto failed_send;
+
+	goto out;
+
+failed_send:
+	zfcp_fsf_req_free(req);
+out:
+	spin_unlock(&adapter->req_q.lock);
+	return ret;
+}
+
+int zfcp_fsf_exchange_config_data(struct zfcp_erp_action *erp_action)
+{
+	volatile struct qdio_buffer_element *sbale;
+	struct zfcp_fsf_req *req;
+	struct zfcp_adapter *adapter = erp_action->adapter;
+	int retval = -EIO;
+
+	spin_lock(&adapter->req_q.lock);
+	if (!atomic_read(&adapter->req_q.count))
+		goto out;
+	req = zfcp_fsf_req_create(adapter,
+				  FSF_QTCB_EXCHANGE_CONFIG_DATA,
+				  ZFCP_REQ_AUTO_CLEANUP,
+				  adapter->pool.fsf_req_erp);
+	if (unlikely(IS_ERR(req))) {
+		retval = PTR_ERR(req);
+		goto out;
+	}
+
+	sbale = zfcp_qdio_sbale_req(req);
+	sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
+	sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
+
+	req->qtcb->bottom.config.feature_selection =
+			FSF_FEATURE_CFDC |
+			FSF_FEATURE_LUN_SHARING |
+			FSF_FEATURE_NOTIFICATION_LOST |
+			FSF_FEATURE_UPDATE_ALERT;
+	req->erp_action = erp_action;
+	req->handler = zfcp_fsf_exchange_config_data_handler;
+	erp_action->fsf_req = req;
+
+	zfcp_fsf_start_erp_timer(req);
+	retval = zfcp_fsf_req_send(req);
+	if (retval) {
+		zfcp_fsf_req_free(req);
+		erp_action->fsf_req = NULL;
+	}
+out:
+	spin_unlock(&adapter->req_q.lock);
+	return retval;
+}
+
+int zfcp_fsf_exchange_config_data_sync(struct zfcp_adapter *adapter,
+				       struct fsf_qtcb_bottom_config *data)
+{
+	volatile struct qdio_buffer_element *sbale;
+	struct zfcp_fsf_req *req = NULL;
+	int retval = -EIO;
+
+	spin_lock(&adapter->req_q.lock);
+	if (zfcp_fsf_req_sbal_get(adapter))
+		goto out;
+
+	req = zfcp_fsf_req_create(adapter, FSF_QTCB_EXCHANGE_CONFIG_DATA,
+				  0, NULL);
+	if (unlikely(IS_ERR(req))) {
+		retval = PTR_ERR(req);
+		goto out;
+	}
+
+	sbale = zfcp_qdio_sbale_req(req);
+	sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
+	sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
+	req->handler = zfcp_fsf_exchange_config_data_handler;
+
+	req->qtcb->bottom.config.feature_selection =
+			FSF_FEATURE_CFDC |
+			FSF_FEATURE_LUN_SHARING |
+			FSF_FEATURE_NOTIFICATION_LOST |
+			FSF_FEATURE_UPDATE_ALERT;
+
+	if (data)
+		req->data = data;
+
+	zfcp_fsf_start_timer(req, ZFCP_FSF_REQUEST_TIMEOUT);
+	retval = zfcp_fsf_req_send(req);
+out:
+	spin_unlock(&adapter->req_q.lock);
+	if (!retval)
+		wait_event(req->completion_wq,
+			   req->status & ZFCP_STATUS_FSFREQ_COMPLETED);
+
+	zfcp_fsf_req_free(req);
+
+	return retval;
+}
+
+/**
+ * zfcp_fsf_exchange_port_data - request information about local port
+ * @erp_action: ERP action for the adapter for which port data is requested
+ * Returns: 0 on success, error otherwise
+ */
+int zfcp_fsf_exchange_port_data(struct zfcp_erp_action *erp_action)
+{
+	volatile struct qdio_buffer_element *sbale;
+	struct zfcp_fsf_req *req;
+	struct zfcp_adapter *adapter = erp_action->adapter;
+	int retval = -EIO;
+
+	if (!(adapter->adapter_features & FSF_FEATURE_HBAAPI_MANAGEMENT))
+		return -EOPNOTSUPP;
+
+	spin_lock(&adapter->req_q.lock);
+	if (!atomic_read(&adapter->req_q.count))
+		goto out;
+	req = zfcp_fsf_req_create(adapter, FSF_QTCB_EXCHANGE_PORT_DATA,
+				  ZFCP_REQ_AUTO_CLEANUP,
+				  adapter->pool.fsf_req_erp);
+	if (unlikely(IS_ERR(req))) {
+		retval = PTR_ERR(req);
+		goto out;
+	}
+
+	sbale = zfcp_qdio_sbale_req(req);
+	sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
+	sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
+
+	req->handler = zfcp_fsf_exchange_port_data_handler;
+	req->erp_action = erp_action;
+	erp_action->fsf_req = req;
+
+	zfcp_fsf_start_erp_timer(req);
+	retval = zfcp_fsf_req_send(req);
+	if (retval) {
+		zfcp_fsf_req_free(req);
+		erp_action->fsf_req = NULL;
+	}
+out:
+	spin_unlock(&adapter->req_q.lock);
+	return retval;
+}
+
+/**
+ * zfcp_fsf_exchange_port_data_sync - request information about local port
+ * @adapter: pointer to struct zfcp_adapter
+ * @data: pointer to struct fsf_qtcb_bottom_port
+ * Returns: 0 on success, error otherwise
+ */
+int zfcp_fsf_exchange_port_data_sync(struct zfcp_adapter *adapter,
+				     struct fsf_qtcb_bottom_port *data)
+{
+	volatile struct qdio_buffer_element *sbale;
+	struct zfcp_fsf_req *req = NULL;
+	int retval = -EIO;
+
+	if (!(adapter->adapter_features & FSF_FEATURE_HBAAPI_MANAGEMENT))
+		return -EOPNOTSUPP;
+
+	spin_lock(&adapter->req_q.lock);
+	if (!atomic_read(&adapter->req_q.count))
+		goto out;
+
+	req = zfcp_fsf_req_create(adapter, FSF_QTCB_EXCHANGE_PORT_DATA, 0,
+				  NULL);
+	if (unlikely(IS_ERR(req))) {
+		retval = PTR_ERR(req);
+		goto out;
+	}
+
+	if (data)
+		req->data = data;
+
+	sbale = zfcp_qdio_sbale_req(req);
+	sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
+	sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
+
+	req->handler = zfcp_fsf_exchange_port_data_handler;
+	zfcp_fsf_start_timer(req, ZFCP_FSF_REQUEST_TIMEOUT);
+	retval = zfcp_fsf_req_send(req);
+out:
+	spin_unlock(&adapter->req_q.lock);
+	if (!retval)
+		wait_event(req->completion_wq,
+			   req->status & ZFCP_STATUS_FSFREQ_COMPLETED);
+	zfcp_fsf_req_free(req);
+
+	return retval;
+}
+
+static void zfcp_fsf_open_port_handler(struct zfcp_fsf_req *req)
+{
+	struct zfcp_port *port = req->data;
+	struct fsf_qtcb_header *header = &req->qtcb->header;
+	struct fsf_plogi *plogi;
+
+	if (req->status & ZFCP_STATUS_FSFREQ_ERROR)
+		goto skip_fsfstatus;
+
+	switch (header->fsf_status) {
+	case FSF_PORT_ALREADY_OPEN:
+		break;
+	case FSF_ACCESS_DENIED:
+		zfcp_fsf_access_denied_port(req, port);
+		break;
 	case FSF_MAXIMUM_NUMBER_OF_PORTS_EXCEEDED:
-		ZFCP_LOG_INFO("error: The FSF adapter is out of resources. "
-			      "The remote port 0x%016Lx on adapter %s "
-			      "could not be opened. Disabling it.\n",
-			      port->wwpn, zfcp_get_busid_by_port(port));
-		zfcp_erp_port_failed(port, 31, fsf_req);
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+		dev_warn(&req->adapter->ccw_device->dev,
+			 "The adapter is out of resources. The remote port "
+			 "0x%016Lx could not be opened, disabling it.\n",
+			 port->wwpn);
+		zfcp_erp_port_failed(port, 31, req);
+		req->status |= ZFCP_STATUS_FSFREQ_ERROR;
 		break;
-
 	case FSF_ADAPTER_STATUS_AVAILABLE:
 		switch (header->fsf_status_qual.word[0]) {
 		case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE:
-			/* ERP strategy will escalate */
-			fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-			break;
 		case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED:
-			/* ERP strategy will escalate */
-			fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+			req->status |= ZFCP_STATUS_FSFREQ_ERROR;
 			break;
 		case FSF_SQ_NO_RETRY_POSSIBLE:
-			ZFCP_LOG_NORMAL("The remote port 0x%016Lx on "
-					"adapter %s could not be opened. "
-					"Disabling it.\n",
-					port->wwpn,
-					zfcp_get_busid_by_port(port));
-			zfcp_erp_port_failed(port, 32, fsf_req);
-			fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-			break;
-		default:
-			ZFCP_LOG_NORMAL
-			    ("bug: Wrong status qualifier 0x%x arrived.\n",
-			     header->fsf_status_qual.word[0]);
+			dev_warn(&req->adapter->ccw_device->dev,
+				 "The remote port 0x%016Lx could not be "
+				 "opened. Disabling it.\n", port->wwpn);
+			zfcp_erp_port_failed(port, 32, req);
+			req->status |= ZFCP_STATUS_FSFREQ_ERROR;
 			break;
 		}
 		break;
-
 	case FSF_GOOD:
-		/* save port handle assigned by FSF */
 		port->handle = header->port_handle;
-		ZFCP_LOG_INFO("The remote port 0x%016Lx via adapter %s "
-			      "was opened, it's port handle is 0x%x\n",
-			      port->wwpn, zfcp_get_busid_by_port(port),
-			      port->handle);
-		/* mark port as open */
 		atomic_set_mask(ZFCP_STATUS_COMMON_OPEN |
 				ZFCP_STATUS_PORT_PHYS_OPEN, &port->status);
 		atomic_clear_mask(ZFCP_STATUS_COMMON_ACCESS_DENIED |
 		                  ZFCP_STATUS_COMMON_ACCESS_BOXED,
 		                  &port->status);
-		retval = 0;
 		/* check whether D_ID has changed during open */
 		/*
 		 * FIXME: This check is not airtight, as the FCP channel does
@@ -2526,320 +1496,168 @@
 		 * another GID_PN straight after a port has been opened.
 		 * Alternately, an ADISC/PDISC ELS should suffice, as well.
 		 */
-		plogi = (struct fsf_plogi *) fsf_req->qtcb->bottom.support.els;
-		if (!atomic_test_mask(ZFCP_STATUS_PORT_NO_WWPN, &port->status))
-		{
-			if (fsf_req->qtcb->bottom.support.els1_length <
-			    sizeof (struct fsf_plogi)) {
-				ZFCP_LOG_INFO(
-					"warning: insufficient length of "
-					"PLOGI payload (%i)\n",
-					fsf_req->qtcb->bottom.support.els1_length);
-				/* skip sanity check and assume wwpn is ok */
-			} else {
-				if (plogi->serv_param.wwpn != port->wwpn) {
-					ZFCP_LOG_INFO("warning: d_id of port "
-						      "0x%016Lx changed during "
-						      "open\n", port->wwpn);
-					atomic_clear_mask(
-						ZFCP_STATUS_PORT_DID_DID,
-						&port->status);
-				} else {
-					port->wwnn = plogi->serv_param.wwnn;
-					zfcp_plogi_evaluate(port, plogi);
-				}
+		if (atomic_read(&port->status) & ZFCP_STATUS_PORT_NO_WWPN)
+			break;
+
+		plogi = (struct fsf_plogi *) req->qtcb->bottom.support.els;
+		if (req->qtcb->bottom.support.els1_length >= sizeof(*plogi)) {
+			if (plogi->serv_param.wwpn != port->wwpn)
+				atomic_clear_mask(ZFCP_STATUS_PORT_DID_DID,
+						  &port->status);
+			else {
+				port->wwnn = plogi->serv_param.wwnn;
+				zfcp_fc_plogi_evaluate(port, plogi);
 			}
 		}
 		break;
-
 	case FSF_UNKNOWN_OP_SUBTYPE:
-		/* should never occure, subtype not set in zfcp_fsf_open_port */
-		ZFCP_LOG_INFO("unknown operation subtype (adapter: %s, "
-			      "op_subtype=0x%x)\n",
-			      zfcp_get_busid_by_port(port),
-			      fsf_req->qtcb->bottom.support.operation_subtype);
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-		break;
-
-	default:
-		ZFCP_LOG_NORMAL("bug: An unknown FSF Status was presented "
-				"(debug info 0x%x)\n",
-				header->fsf_status);
+		req->status |= ZFCP_STATUS_FSFREQ_ERROR;
 		break;
 	}
 
- skip_fsfstatus:
+skip_fsfstatus:
 	atomic_clear_mask(ZFCP_STATUS_COMMON_OPENING, &port->status);
-	return retval;
 }
 
-/*
- * function:    zfcp_fsf_close_port
- *
- * purpose:     submit FSF command "close port"
- *
- * returns:     address of initiated FSF request
- *              NULL - request could not be initiated
+/**
+ * zfcp_fsf_open_port - create and send open port request
+ * @erp_action: pointer to struct zfcp_erp_action
+ * Returns: 0 on success, error otherwise
  */
-int
-zfcp_fsf_close_port(struct zfcp_erp_action *erp_action)
+int zfcp_fsf_open_port(struct zfcp_erp_action *erp_action)
 {
 	volatile struct qdio_buffer_element *sbale;
-	struct zfcp_fsf_req *fsf_req;
-	unsigned long lock_flags;
-	int retval = 0;
+	struct zfcp_adapter *adapter = erp_action->adapter;
+	struct zfcp_fsf_req *req;
+	int retval = -EIO;
 
-	/* setup new FSF request */
-	retval = zfcp_fsf_req_create(erp_action->adapter,
-				     FSF_QTCB_CLOSE_PORT,
-				     ZFCP_WAIT_FOR_SBAL | ZFCP_REQ_AUTO_CLEANUP,
-				     erp_action->adapter->pool.fsf_req_erp,
-				     &lock_flags, &fsf_req);
-	if (retval < 0) {
-		ZFCP_LOG_INFO("error: Could not create a close port request "
-			      "for port 0x%016Lx on adapter %s.\n",
-			      erp_action->port->wwpn,
-			      zfcp_get_busid_by_adapter(erp_action->adapter));
+	spin_lock(&adapter->req_q.lock);
+	if (zfcp_fsf_req_sbal_get(adapter))
+		goto out;
+
+	req = zfcp_fsf_req_create(adapter,
+				  FSF_QTCB_OPEN_PORT_WITH_DID,
+				  ZFCP_REQ_AUTO_CLEANUP,
+				  adapter->pool.fsf_req_erp);
+	if (unlikely(IS_ERR(req))) {
+		retval = PTR_ERR(req);
 		goto out;
 	}
 
-	sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
+	sbale = zfcp_qdio_sbale_req(req);
         sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
         sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
 
-	atomic_set_mask(ZFCP_STATUS_COMMON_CLOSING, &erp_action->port->status);
-	fsf_req->data = (unsigned long) erp_action->port;
-	fsf_req->erp_action = erp_action;
-	fsf_req->qtcb->header.port_handle = erp_action->port->handle;
-	fsf_req->erp_action = erp_action;
-	erp_action->fsf_req = fsf_req;
+	req->handler = zfcp_fsf_open_port_handler;
+	req->qtcb->bottom.support.d_id = erp_action->port->d_id;
+	req->data = erp_action->port;
+	req->erp_action = erp_action;
+	erp_action->fsf_req = req;
+	atomic_set_mask(ZFCP_STATUS_COMMON_OPENING, &erp_action->port->status);
 
-	zfcp_erp_start_timer(fsf_req);
-	retval = zfcp_fsf_req_send(fsf_req);
+	zfcp_fsf_start_erp_timer(req);
+	retval = zfcp_fsf_req_send(req);
 	if (retval) {
-		ZFCP_LOG_INFO("error: Could not send a close port request for "
-			      "port 0x%016Lx on adapter %s.\n",
-			      erp_action->port->wwpn,
-			      zfcp_get_busid_by_adapter(erp_action->adapter));
-		zfcp_fsf_req_free(fsf_req);
+		zfcp_fsf_req_free(req);
 		erp_action->fsf_req = NULL;
-		goto out;
 	}
-
-	ZFCP_LOG_TRACE("close port request initiated "
-		       "(adapter %s, port 0x%016Lx)\n",
-		       zfcp_get_busid_by_adapter(erp_action->adapter),
-		       erp_action->port->wwpn);
- out:
-	write_unlock_irqrestore(&erp_action->adapter->request_queue.queue_lock,
-				lock_flags);
+out:
+	spin_unlock(&adapter->req_q.lock);
 	return retval;
 }
 
-/*
- * function:    zfcp_fsf_close_port_handler
- *
- * purpose:     is called for finished Close Port FSF command
- *
- * returns:
- */
-static int
-zfcp_fsf_close_port_handler(struct zfcp_fsf_req *fsf_req)
+static void zfcp_fsf_close_port_handler(struct zfcp_fsf_req *req)
 {
-	int retval = -EINVAL;
-	struct zfcp_port *port;
+	struct zfcp_port *port = req->data;
 
-	port = (struct zfcp_port *) fsf_req->data;
-
-	if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
-		/* don't change port status in our bookkeeping */
+	if (req->status & ZFCP_STATUS_FSFREQ_ERROR)
 		goto skip_fsfstatus;
-	}
 
-	/* evaluate FSF status in QTCB */
-	switch (fsf_req->qtcb->header.fsf_status) {
-
+	switch (req->qtcb->header.fsf_status) {
 	case FSF_PORT_HANDLE_NOT_VALID:
-		ZFCP_LOG_INFO("Temporary port identifier 0x%x for port "
-			      "0x%016Lx on adapter %s invalid. This may happen "
-			      "occasionally.\n", port->handle,
-			      port->wwpn, zfcp_get_busid_by_port(port));
-		ZFCP_LOG_DEBUG("status qualifier:\n");
-		ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
-			      (char *) &fsf_req->qtcb->header.fsf_status_qual,
-			      sizeof (union fsf_status_qual));
-		zfcp_erp_adapter_reopen(port->adapter, 0, 107, fsf_req);
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+		zfcp_erp_adapter_reopen(port->adapter, 0, 107, req);
+		req->status |= ZFCP_STATUS_FSFREQ_ERROR;
 		break;
-
 	case FSF_ADAPTER_STATUS_AVAILABLE:
-		/* Note: FSF has actually closed the port in this case.
-		 * The status code is just daft. Fingers crossed for a change
-		 */
-		retval = 0;
 		break;
-
 	case FSF_GOOD:
-		ZFCP_LOG_TRACE("remote port 0x016%Lx on adapter %s closed, "
-			       "port handle 0x%x\n", port->wwpn,
-			       zfcp_get_busid_by_port(port), port->handle);
-		zfcp_erp_modify_port_status(port, 33, fsf_req,
+		zfcp_erp_modify_port_status(port, 33, req,
 					    ZFCP_STATUS_COMMON_OPEN,
 					    ZFCP_CLEAR);
-		retval = 0;
-		break;
-
-	default:
-		ZFCP_LOG_NORMAL("bug: An unknown FSF Status was presented "
-				"(debug info 0x%x)\n",
-				fsf_req->qtcb->header.fsf_status);
 		break;
 	}
 
- skip_fsfstatus:
+skip_fsfstatus:
 	atomic_clear_mask(ZFCP_STATUS_COMMON_CLOSING, &port->status);
-	return retval;
 }
 
-/*
- * function:    zfcp_fsf_close_physical_port
- *
- * purpose:     submit FSF command "close physical port"
- *
- * returns:     address of initiated FSF request
- *              NULL - request could not be initiated
+/**
+ * zfcp_fsf_close_port - create and send close port request
+ * @erp_action: pointer to struct zfcp_erp_action
+ * Returns: 0 on success, error otherwise
  */
-int
-zfcp_fsf_close_physical_port(struct zfcp_erp_action *erp_action)
+int zfcp_fsf_close_port(struct zfcp_erp_action *erp_action)
 {
 	volatile struct qdio_buffer_element *sbale;
-	struct zfcp_fsf_req *fsf_req;
-	unsigned long lock_flags;
-	int retval = 0;
+	struct zfcp_adapter *adapter = erp_action->adapter;
+	struct zfcp_fsf_req *req;
+	int retval = -EIO;
 
-	/* setup new FSF request */
-	retval = zfcp_fsf_req_create(erp_action->adapter,
-				     FSF_QTCB_CLOSE_PHYSICAL_PORT,
-				     ZFCP_WAIT_FOR_SBAL | ZFCP_REQ_AUTO_CLEANUP,
-				     erp_action->adapter->pool.fsf_req_erp,
-				     &lock_flags, &fsf_req);
-	if (retval < 0) {
-		ZFCP_LOG_INFO("error: Could not create close physical port "
-			      "request (adapter %s, port 0x%016Lx)\n",
-			      zfcp_get_busid_by_adapter(erp_action->adapter),
-			      erp_action->port->wwpn);
+	spin_lock(&adapter->req_q.lock);
+	if (zfcp_fsf_req_sbal_get(adapter))
+		goto out;
 
+	req = zfcp_fsf_req_create(adapter, FSF_QTCB_CLOSE_PORT,
+				  ZFCP_REQ_AUTO_CLEANUP,
+				  adapter->pool.fsf_req_erp);
+	if (unlikely(IS_ERR(req))) {
+		retval = PTR_ERR(req);
 		goto out;
 	}
 
-	sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
+	sbale = zfcp_qdio_sbale_req(req);
 	sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
 	sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
 
-	/* mark port as being closed */
-	atomic_set_mask(ZFCP_STATUS_PORT_PHYS_CLOSING,
-			&erp_action->port->status);
-	/* save a pointer to this port */
-	fsf_req->data = (unsigned long) erp_action->port;
-	fsf_req->qtcb->header.port_handle = erp_action->port->handle;
-	fsf_req->erp_action = erp_action;
-	erp_action->fsf_req = fsf_req;
+	req->handler = zfcp_fsf_close_port_handler;
+	req->data = erp_action->port;
+	req->erp_action = erp_action;
+	req->qtcb->header.port_handle = erp_action->port->handle;
+	erp_action->fsf_req = req;
+	atomic_set_mask(ZFCP_STATUS_COMMON_CLOSING, &erp_action->port->status);
 
-	zfcp_erp_start_timer(fsf_req);
-	retval = zfcp_fsf_req_send(fsf_req);
+	zfcp_fsf_start_erp_timer(req);
+	retval = zfcp_fsf_req_send(req);
 	if (retval) {
-		ZFCP_LOG_INFO("error: Could not send close physical port "
-			      "request (adapter %s, port 0x%016Lx)\n",
-			      zfcp_get_busid_by_adapter(erp_action->adapter),
-			      erp_action->port->wwpn);
-		zfcp_fsf_req_free(fsf_req);
+		zfcp_fsf_req_free(req);
 		erp_action->fsf_req = NULL;
-		goto out;
 	}
-
-	ZFCP_LOG_TRACE("close physical port request initiated "
-		       "(adapter %s, port 0x%016Lx)\n",
-		       zfcp_get_busid_by_adapter(erp_action->adapter),
-		       erp_action->port->wwpn);
- out:
-	write_unlock_irqrestore(&erp_action->adapter->request_queue.queue_lock,
-				lock_flags);
+out:
+	spin_unlock(&adapter->req_q.lock);
 	return retval;
 }
 
-/*
- * function:    zfcp_fsf_close_physical_port_handler
- *
- * purpose:     is called for finished Close Physical Port FSF command
- *
- * returns:
- */
-static int
-zfcp_fsf_close_physical_port_handler(struct zfcp_fsf_req *fsf_req)
+static void zfcp_fsf_close_physical_port_handler(struct zfcp_fsf_req *req)
 {
-	int retval = -EINVAL;
-	struct zfcp_port *port;
+	struct zfcp_port *port = req->data;
+	struct fsf_qtcb_header *header = &req->qtcb->header;
 	struct zfcp_unit *unit;
-	struct fsf_qtcb_header *header;
-	u16 subtable, rule, counter;
 
-	port = (struct zfcp_port *) fsf_req->data;
-	header = &fsf_req->qtcb->header;
-
-	if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
-		/* don't change port status in our bookkeeping */
+	if (req->status & ZFCP_STATUS_FSFREQ_ERROR)
 		goto skip_fsfstatus;
-	}
 
-	/* evaluate FSF status in QTCB */
 	switch (header->fsf_status) {
-
 	case FSF_PORT_HANDLE_NOT_VALID:
-		ZFCP_LOG_INFO("Temporary port identifier 0x%x invalid"
-			      "(adapter %s, port 0x%016Lx). "
-			      "This may happen occasionally.\n",
-			      port->handle,
-			      zfcp_get_busid_by_port(port),
-			      port->wwpn);
-		ZFCP_LOG_DEBUG("status qualifier:\n");
-		ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
-			      (char *) &header->fsf_status_qual,
-			      sizeof (union fsf_status_qual));
-		zfcp_erp_adapter_reopen(port->adapter, 0, 108, fsf_req);
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+		zfcp_erp_adapter_reopen(port->adapter, 0, 108, req);
+		req->status |= ZFCP_STATUS_FSFREQ_ERROR;
 		break;
-
 	case FSF_ACCESS_DENIED:
-		ZFCP_LOG_NORMAL("Access denied, cannot close "
-				"physical port 0x%016Lx on adapter %s\n",
-				port->wwpn, zfcp_get_busid_by_port(port));
-		for (counter = 0; counter < 2; counter++) {
-			subtable = header->fsf_status_qual.halfword[counter * 2];
-			rule = header->fsf_status_qual.halfword[counter * 2 + 1];
-			switch (subtable) {
-			case FSF_SQ_CFDC_SUBTABLE_OS:
-			case FSF_SQ_CFDC_SUBTABLE_PORT_WWPN:
-			case FSF_SQ_CFDC_SUBTABLE_PORT_DID:
-			case FSF_SQ_CFDC_SUBTABLE_LUN:
-	       			ZFCP_LOG_INFO("Access denied (%s rule %d)\n",
-					zfcp_act_subtable_type[subtable], rule);
-				break;
-			}
-		}
-		zfcp_erp_port_access_denied(port, 58, fsf_req);
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+		zfcp_fsf_access_denied_port(req, port);
 		break;
-
 	case FSF_PORT_BOXED:
-		ZFCP_LOG_DEBUG("The remote port 0x%016Lx on adapter "
-			       "%s needs to be reopened but it was attempted "
-			       "to close it physically.\n",
-			       port->wwpn,
-			       zfcp_get_busid_by_port(port));
-		zfcp_erp_port_boxed(port, 50, fsf_req);
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR |
-			ZFCP_STATUS_FSFREQ_RETRY;
-
+		zfcp_erp_port_boxed(port, 50, req);
+		req->status |= ZFCP_STATUS_FSFREQ_ERROR |
+			       ZFCP_STATUS_FSFREQ_RETRY;
 		/* can't use generic zfcp_erp_modify_port_status because
 		 * ZFCP_STATUS_COMMON_OPEN must not be reset for the port */
 		atomic_clear_mask(ZFCP_STATUS_PORT_PHYS_OPEN, &port->status);
@@ -2847,154 +1665,88 @@
 			atomic_clear_mask(ZFCP_STATUS_COMMON_OPEN,
 					  &unit->status);
 		break;
-
 	case FSF_ADAPTER_STATUS_AVAILABLE:
 		switch (header->fsf_status_qual.word[0]) {
 		case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE:
-			/* This will now be escalated by ERP */
-			fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-			break;
+			/* fall through */
 		case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED:
-			/* ERP strategy will escalate */
-			fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-			break;
-		default:
-			ZFCP_LOG_NORMAL
-			    ("bug: Wrong status qualifier 0x%x arrived.\n",
-			     header->fsf_status_qual.word[0]);
+			req->status |= ZFCP_STATUS_FSFREQ_ERROR;
 			break;
 		}
 		break;
-
 	case FSF_GOOD:
-		ZFCP_LOG_DEBUG("Remote port 0x%016Lx via adapter %s "
-			       "physically closed, port handle 0x%x\n",
-			       port->wwpn,
-			       zfcp_get_busid_by_port(port), port->handle);
 		/* can't use generic zfcp_erp_modify_port_status because
 		 * ZFCP_STATUS_COMMON_OPEN must not be reset for the port
 		 */
 		atomic_clear_mask(ZFCP_STATUS_PORT_PHYS_OPEN, &port->status);
 		list_for_each_entry(unit, &port->unit_list_head, list)
-		    atomic_clear_mask(ZFCP_STATUS_COMMON_OPEN, &unit->status);
-		retval = 0;
-		break;
-
-	default:
-		ZFCP_LOG_NORMAL("bug: An unknown FSF Status was presented "
-				"(debug info 0x%x)\n",
-				header->fsf_status);
+			atomic_clear_mask(ZFCP_STATUS_COMMON_OPEN,
+					  &unit->status);
 		break;
 	}
-
- skip_fsfstatus:
+skip_fsfstatus:
 	atomic_clear_mask(ZFCP_STATUS_PORT_PHYS_CLOSING, &port->status);
-	return retval;
 }
 
-/*
- * function:    zfcp_fsf_open_unit
- *
- * purpose:
- *
- * returns:
- *
- * assumptions:	This routine does not check whether the associated
- *		remote port has already been opened. This should be
- *		done by calling routines. Otherwise some status
- *		may be presented by FSF
+/**
+ * zfcp_fsf_close_physical_port - close physical port
+ * @erp_action: pointer to struct zfcp_erp_action
+ * Returns: 0 on success
  */
-int
-zfcp_fsf_open_unit(struct zfcp_erp_action *erp_action)
+int zfcp_fsf_close_physical_port(struct zfcp_erp_action *erp_action)
 {
 	volatile struct qdio_buffer_element *sbale;
-	struct zfcp_fsf_req *fsf_req;
-	unsigned long lock_flags;
-	int retval = 0;
+	struct zfcp_adapter *adapter = erp_action->adapter;
+	struct zfcp_fsf_req *req;
+	int retval = -EIO;
 
-	/* setup new FSF request */
-	retval = zfcp_fsf_req_create(erp_action->adapter,
-				     FSF_QTCB_OPEN_LUN,
-				     ZFCP_WAIT_FOR_SBAL | ZFCP_REQ_AUTO_CLEANUP,
-				     erp_action->adapter->pool.fsf_req_erp,
-				     &lock_flags, &fsf_req);
-	if (retval < 0) {
-		ZFCP_LOG_INFO("error: Could not create open unit request for "
-			      "unit 0x%016Lx on port 0x%016Lx on adapter %s.\n",
-			      erp_action->unit->fcp_lun,
-			      erp_action->unit->port->wwpn,
-			      zfcp_get_busid_by_adapter(erp_action->adapter));
+	spin_lock(&adapter->req_q.lock);
+	if (zfcp_fsf_req_sbal_get(adapter))
+		goto out;
+
+	req = zfcp_fsf_req_create(adapter, FSF_QTCB_CLOSE_PHYSICAL_PORT,
+				  ZFCP_REQ_AUTO_CLEANUP,
+				  adapter->pool.fsf_req_erp);
+	if (unlikely(IS_ERR(req))) {
+		retval = PTR_ERR(req);
 		goto out;
 	}
 
-	sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
-        sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
-        sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
+	sbale = zfcp_qdio_sbale_req(req);
+	sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
+	sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
 
-	fsf_req->qtcb->header.port_handle = erp_action->port->handle;
-	fsf_req->qtcb->bottom.support.fcp_lun =	erp_action->unit->fcp_lun;
-	if (!(erp_action->adapter->connection_features & FSF_FEATURE_NPIV_MODE))
-		fsf_req->qtcb->bottom.support.option =
-			FSF_OPEN_LUN_SUPPRESS_BOXING;
-	atomic_set_mask(ZFCP_STATUS_COMMON_OPENING, &erp_action->unit->status);
-	fsf_req->data = (unsigned long) erp_action->unit;
-	fsf_req->erp_action = erp_action;
-	erp_action->fsf_req = fsf_req;
+	req->data = erp_action->port;
+	req->qtcb->header.port_handle = erp_action->port->handle;
+	req->erp_action = erp_action;
+	req->handler = zfcp_fsf_close_physical_port_handler;
+	erp_action->fsf_req = req;
+	atomic_set_mask(ZFCP_STATUS_PORT_PHYS_CLOSING,
+			&erp_action->port->status);
 
-	zfcp_erp_start_timer(fsf_req);
-	retval = zfcp_fsf_req_send(erp_action->fsf_req);
+	zfcp_fsf_start_erp_timer(req);
+	retval = zfcp_fsf_req_send(req);
 	if (retval) {
-		ZFCP_LOG_INFO("error: Could not send an open unit request "
-			      "on the adapter %s, port 0x%016Lx for "
-			      "unit 0x%016Lx\n",
-			      zfcp_get_busid_by_adapter(erp_action->adapter),
-			      erp_action->port->wwpn,
-			      erp_action->unit->fcp_lun);
-		zfcp_fsf_req_free(fsf_req);
+		zfcp_fsf_req_free(req);
 		erp_action->fsf_req = NULL;
-		goto out;
 	}
-
-	ZFCP_LOG_TRACE("Open LUN request initiated (adapter %s, "
-		       "port 0x%016Lx, unit 0x%016Lx)\n",
-		       zfcp_get_busid_by_adapter(erp_action->adapter),
-		       erp_action->port->wwpn, erp_action->unit->fcp_lun);
- out:
-	write_unlock_irqrestore(&erp_action->adapter->request_queue.queue_lock,
-				lock_flags);
+out:
+	spin_unlock(&adapter->req_q.lock);
 	return retval;
 }
 
-/*
- * function:    zfcp_fsf_open_unit_handler
- *
- * purpose:	is called for finished Open LUN command
- *
- * returns:
- */
-static int
-zfcp_fsf_open_unit_handler(struct zfcp_fsf_req *fsf_req)
+static void zfcp_fsf_open_unit_handler(struct zfcp_fsf_req *req)
 {
-	int retval = -EINVAL;
-	struct zfcp_adapter *adapter;
-	struct zfcp_unit *unit;
-	struct fsf_qtcb_header *header;
-	struct fsf_qtcb_bottom_support *bottom;
-	struct fsf_queue_designator *queue_designator;
-	u16 subtable, rule, counter;
+	struct zfcp_adapter *adapter = req->adapter;
+	struct zfcp_unit *unit = req->data;
+	struct fsf_qtcb_header *header = &req->qtcb->header;
+	struct fsf_qtcb_bottom_support *bottom = &req->qtcb->bottom.support;
+	struct fsf_queue_designator *queue_designator =
+				&header->fsf_status_qual.fsf_queue_designator;
 	int exclusive, readwrite;
 
-	unit = (struct zfcp_unit *) fsf_req->data;
-
-	if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
-		/* don't change unit status in our bookkeeping */
+	if (req->status & ZFCP_STATUS_FSFREQ_ERROR)
 		goto skip_fsfstatus;
-	}
-
-	adapter = fsf_req->adapter;
-	header = &fsf_req->qtcb->header;
-	bottom = &fsf_req->qtcb->bottom.support;
-	queue_designator = &header->fsf_status_qual.fsf_queue_designator;
 
 	atomic_clear_mask(ZFCP_STATUS_COMMON_ACCESS_DENIED |
 			  ZFCP_STATUS_COMMON_ACCESS_BOXED |
@@ -3002,155 +1754,65 @@
 			  ZFCP_STATUS_UNIT_READONLY,
 			  &unit->status);
 
-	/* evaluate FSF status in QTCB */
 	switch (header->fsf_status) {
 
 	case FSF_PORT_HANDLE_NOT_VALID:
-		ZFCP_LOG_INFO("Temporary port identifier 0x%x "
-			      "for port 0x%016Lx on adapter %s invalid "
-			      "This may happen occasionally\n",
-			      unit->port->handle,
-			      unit->port->wwpn, zfcp_get_busid_by_unit(unit));
-		ZFCP_LOG_DEBUG("status qualifier:\n");
-		ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
-			      (char *) &header->fsf_status_qual,
-			      sizeof (union fsf_status_qual));
-		zfcp_erp_adapter_reopen(unit->port->adapter, 0, 109, fsf_req);
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-		break;
-
+		zfcp_erp_adapter_reopen(unit->port->adapter, 0, 109, req);
+		/* fall through */
 	case FSF_LUN_ALREADY_OPEN:
-		ZFCP_LOG_NORMAL("bug: Attempted to open unit 0x%016Lx on "
-				"remote port 0x%016Lx on adapter %s twice.\n",
-				unit->fcp_lun,
-				unit->port->wwpn, zfcp_get_busid_by_unit(unit));
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
 		break;
-
 	case FSF_ACCESS_DENIED:
-		ZFCP_LOG_NORMAL("Access denied, cannot open unit 0x%016Lx on "
-				"remote port 0x%016Lx on adapter %s\n",
-				unit->fcp_lun, unit->port->wwpn,
-				zfcp_get_busid_by_unit(unit));
-		for (counter = 0; counter < 2; counter++) {
-			subtable = header->fsf_status_qual.halfword[counter * 2];
-			rule = header->fsf_status_qual.halfword[counter * 2 + 1];
-			switch (subtable) {
-			case FSF_SQ_CFDC_SUBTABLE_OS:
-			case FSF_SQ_CFDC_SUBTABLE_PORT_WWPN:
-			case FSF_SQ_CFDC_SUBTABLE_PORT_DID:
-			case FSF_SQ_CFDC_SUBTABLE_LUN:
-				ZFCP_LOG_INFO("Access denied (%s rule %d)\n",
-					zfcp_act_subtable_type[subtable], rule);
-				break;
-			}
-		}
-		zfcp_erp_unit_access_denied(unit, 59, fsf_req);
-		atomic_clear_mask(ZFCP_STATUS_UNIT_SHARED, &unit->status);
-                atomic_clear_mask(ZFCP_STATUS_UNIT_READONLY, &unit->status);
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-		break;
-
-	case FSF_PORT_BOXED:
-		ZFCP_LOG_DEBUG("The remote port 0x%016Lx on adapter %s "
-			       "needs to be reopened\n",
-			       unit->port->wwpn, zfcp_get_busid_by_unit(unit));
-		zfcp_erp_port_boxed(unit->port, 51, fsf_req);
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR |
-			ZFCP_STATUS_FSFREQ_RETRY;
-		break;
-
-	case FSF_LUN_SHARING_VIOLATION:
-		if (header->fsf_status_qual.word[0] != 0) {
-			ZFCP_LOG_NORMAL("FCP-LUN 0x%Lx at the remote port "
-					"with WWPN 0x%Lx "
-					"connected to the adapter %s "
-					"is already in use in LPAR%d, CSS%d\n",
-					unit->fcp_lun,
-					unit->port->wwpn,
-					zfcp_get_busid_by_unit(unit),
-					queue_designator->hla,
-					queue_designator->cssid);
-		} else {
-			subtable = header->fsf_status_qual.halfword[4];
-			rule = header->fsf_status_qual.halfword[5];
-			switch (subtable) {
-			case FSF_SQ_CFDC_SUBTABLE_OS:
-			case FSF_SQ_CFDC_SUBTABLE_PORT_WWPN:
-			case FSF_SQ_CFDC_SUBTABLE_PORT_DID:
-			case FSF_SQ_CFDC_SUBTABLE_LUN:
-				ZFCP_LOG_NORMAL("Access to FCP-LUN 0x%Lx at the "
-						"remote port with WWPN 0x%Lx "
-						"connected to the adapter %s "
-						"is denied (%s rule %d)\n",
-						unit->fcp_lun,
-						unit->port->wwpn,
-						zfcp_get_busid_by_unit(unit),
-						zfcp_act_subtable_type[subtable],
-						rule);
-				break;
-			}
-		}
-		ZFCP_LOG_DEBUG("status qualifier:\n");
-		ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
-			      (char *) &header->fsf_status_qual,
-			      sizeof (union fsf_status_qual));
-		zfcp_erp_unit_access_denied(unit, 60, fsf_req);
+		zfcp_fsf_access_denied_unit(req, unit);
 		atomic_clear_mask(ZFCP_STATUS_UNIT_SHARED, &unit->status);
 		atomic_clear_mask(ZFCP_STATUS_UNIT_READONLY, &unit->status);
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
 		break;
-
+	case FSF_PORT_BOXED:
+		zfcp_erp_port_boxed(unit->port, 51, req);
+		req->status |= ZFCP_STATUS_FSFREQ_ERROR |
+			       ZFCP_STATUS_FSFREQ_RETRY;
+		break;
+	case FSF_LUN_SHARING_VIOLATION:
+		if (header->fsf_status_qual.word[0])
+			dev_warn(&adapter->ccw_device->dev,
+				 "FCP-LUN 0x%Lx at the remote port "
+				 "with WWPN 0x%Lx "
+				 "connected to the adapter "
+				 "is already in use in LPAR%d, CSS%d.\n",
+				 unit->fcp_lun,
+				 unit->port->wwpn,
+				 queue_designator->hla,
+				 queue_designator->cssid);
+		else
+			zfcp_act_eval_err(adapter,
+					  header->fsf_status_qual.word[2]);
+		zfcp_erp_unit_access_denied(unit, 60, req);
+		atomic_clear_mask(ZFCP_STATUS_UNIT_SHARED, &unit->status);
+		atomic_clear_mask(ZFCP_STATUS_UNIT_READONLY, &unit->status);
+		req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+		break;
 	case FSF_MAXIMUM_NUMBER_OF_LUNS_EXCEEDED:
-		ZFCP_LOG_INFO("error: The adapter ran out of resources. "
-			      "There is no handle (temporary port identifier) "
-			      "available for unit 0x%016Lx on port 0x%016Lx "
-			      "on adapter %s\n",
-			      unit->fcp_lun,
-			      unit->port->wwpn,
-			      zfcp_get_busid_by_unit(unit));
-		zfcp_erp_unit_failed(unit, 34, fsf_req);
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+		dev_warn(&adapter->ccw_device->dev,
+			 "The adapter ran out of resources. There is no "
+			 "handle available for unit 0x%016Lx on port 0x%016Lx.",
+			 unit->fcp_lun, unit->port->wwpn);
+		zfcp_erp_unit_failed(unit, 34, req);
+		/* fall through */
+	case FSF_INVALID_COMMAND_OPTION:
+		req->status |= ZFCP_STATUS_FSFREQ_ERROR;
 		break;
-
 	case FSF_ADAPTER_STATUS_AVAILABLE:
 		switch (header->fsf_status_qual.word[0]) {
 		case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE:
-			/* Re-establish link to port */
 			zfcp_test_link(unit->port);
-			fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-			break;
+			/* fall through */
 		case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED:
-			/* ERP strategy will escalate */
-			fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+			req->status |= ZFCP_STATUS_FSFREQ_ERROR;
 			break;
-		default:
-			ZFCP_LOG_NORMAL
-			    ("bug: Wrong status qualifier 0x%x arrived.\n",
-			     header->fsf_status_qual.word[0]);
 		}
 		break;
 
-	case FSF_INVALID_COMMAND_OPTION:
-		ZFCP_LOG_NORMAL(
-			"Invalid option 0x%x has been specified "
-			"in QTCB bottom sent to the adapter %s\n",
-			bottom->option,
-			zfcp_get_busid_by_adapter(adapter));
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-		retval = -EINVAL;
-		break;
-
 	case FSF_GOOD:
-		/* save LUN handle assigned by FSF */
 		unit->handle = header->lun_handle;
-		ZFCP_LOG_TRACE("unit 0x%016Lx on remote port 0x%016Lx on "
-			       "adapter %s opened, port handle 0x%x\n",
-			       unit->fcp_lun,
-			       unit->port->wwpn,
-			       zfcp_get_busid_by_unit(unit),
-			       unit->handle);
-		/* mark unit as open */
 		atomic_set_mask(ZFCP_STATUS_COMMON_OPEN, &unit->status);
 
 		if (!(adapter->connection_features & FSF_FEATURE_NPIV_MODE) &&
@@ -3168,221 +1830,377 @@
 			if (!readwrite) {
                 		atomic_set_mask(ZFCP_STATUS_UNIT_READONLY,
 						&unit->status);
-                		ZFCP_LOG_NORMAL("read-only access for unit "
-						"(adapter %s, wwpn=0x%016Lx, "
-						"fcp_lun=0x%016Lx)\n",
-						zfcp_get_busid_by_unit(unit),
-						unit->port->wwpn,
-						unit->fcp_lun);
+				dev_info(&adapter->ccw_device->dev,
+					 "Read-only access for unit 0x%016Lx "
+					 "on port 0x%016Lx.\n",
+					 unit->fcp_lun, unit->port->wwpn);
         		}
 
         		if (exclusive && !readwrite) {
-                		ZFCP_LOG_NORMAL("exclusive access of read-only "
-						"unit not supported\n");
-				zfcp_erp_unit_failed(unit, 35, fsf_req);
-				fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-				zfcp_erp_unit_shutdown(unit, 0, 80, fsf_req);
+				dev_err(&adapter->ccw_device->dev,
+					"Exclusive access of read-only unit "
+					"0x%016Lx on port 0x%016Lx not "
+					"supported, disabling unit.\n",
+					unit->fcp_lun, unit->port->wwpn);
+				zfcp_erp_unit_failed(unit, 35, req);
+				req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+				zfcp_erp_unit_shutdown(unit, 0, 80, req);
         		} else if (!exclusive && readwrite) {
-                		ZFCP_LOG_NORMAL("shared access of read-write "
-						"unit not supported\n");
-				zfcp_erp_unit_failed(unit, 36, fsf_req);
-				fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-				zfcp_erp_unit_shutdown(unit, 0, 81, fsf_req);
+				dev_err(&adapter->ccw_device->dev,
+					"Shared access of read-write unit "
+					"0x%016Lx on port 0x%016Lx not "
+					"supported, disabling unit.\n",
+					unit->fcp_lun, unit->port->wwpn);
+				zfcp_erp_unit_failed(unit, 36, req);
+				req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+				zfcp_erp_unit_shutdown(unit, 0, 81, req);
         		}
 		}
-
-		retval = 0;
-		break;
-
-	default:
-		ZFCP_LOG_NORMAL("bug: An unknown FSF Status was presented "
-				"(debug info 0x%x)\n",
-				header->fsf_status);
 		break;
 	}
 
- skip_fsfstatus:
+skip_fsfstatus:
 	atomic_clear_mask(ZFCP_STATUS_COMMON_OPENING, &unit->status);
-	return retval;
 }
 
-/*
- * function:    zfcp_fsf_close_unit
- *
- * purpose:
- *
- * returns:	address of fsf_req - request successfully initiated
- *		NULL -
- *
- * assumptions: This routine does not check whether the associated
- *              remote port/lun has already been opened. This should be
- *              done by calling routines. Otherwise some status
- *              may be presented by FSF
+/**
+ * zfcp_fsf_open_unit - open unit
+ * @erp_action: pointer to struct zfcp_erp_action
+ * Returns: 0 on success, error otherwise
  */
-int
-zfcp_fsf_close_unit(struct zfcp_erp_action *erp_action)
+int zfcp_fsf_open_unit(struct zfcp_erp_action *erp_action)
 {
 	volatile struct qdio_buffer_element *sbale;
-	struct zfcp_fsf_req *fsf_req;
-	unsigned long lock_flags;
-	int retval = 0;
+	struct zfcp_adapter *adapter = erp_action->adapter;
+	struct zfcp_fsf_req *req;
+	int retval = -EIO;
 
-	/* setup new FSF request */
-	retval = zfcp_fsf_req_create(erp_action->adapter,
-				     FSF_QTCB_CLOSE_LUN,
-				     ZFCP_WAIT_FOR_SBAL | ZFCP_REQ_AUTO_CLEANUP,
-				     erp_action->adapter->pool.fsf_req_erp,
-				     &lock_flags, &fsf_req);
-	if (retval < 0) {
-		ZFCP_LOG_INFO("error: Could not create close unit request for "
-			      "unit 0x%016Lx on port 0x%016Lx on adapter %s.\n",
-			      erp_action->unit->fcp_lun,
-			      erp_action->port->wwpn,
-			      zfcp_get_busid_by_adapter(erp_action->adapter));
+	spin_lock(&adapter->req_q.lock);
+	if (zfcp_fsf_req_sbal_get(adapter))
+		goto out;
+
+	req = zfcp_fsf_req_create(adapter, FSF_QTCB_OPEN_LUN,
+				  ZFCP_REQ_AUTO_CLEANUP,
+				  adapter->pool.fsf_req_erp);
+	if (unlikely(IS_ERR(req))) {
+		retval = PTR_ERR(req);
 		goto out;
 	}
 
-	sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
+	sbale = zfcp_qdio_sbale_req(req);
         sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
         sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
 
-	fsf_req->qtcb->header.port_handle = erp_action->port->handle;
-	fsf_req->qtcb->header.lun_handle = erp_action->unit->handle;
-	atomic_set_mask(ZFCP_STATUS_COMMON_CLOSING, &erp_action->unit->status);
-	fsf_req->data = (unsigned long) erp_action->unit;
-	fsf_req->erp_action = erp_action;
-	erp_action->fsf_req = fsf_req;
+	req->qtcb->header.port_handle = erp_action->port->handle;
+	req->qtcb->bottom.support.fcp_lun = erp_action->unit->fcp_lun;
+	req->handler = zfcp_fsf_open_unit_handler;
+	req->data = erp_action->unit;
+	req->erp_action = erp_action;
+	erp_action->fsf_req = req;
 
-	zfcp_erp_start_timer(fsf_req);
-	retval = zfcp_fsf_req_send(erp_action->fsf_req);
+	if (!(adapter->connection_features & FSF_FEATURE_NPIV_MODE))
+		req->qtcb->bottom.support.option = FSF_OPEN_LUN_SUPPRESS_BOXING;
+
+	atomic_set_mask(ZFCP_STATUS_COMMON_OPENING, &erp_action->unit->status);
+
+	zfcp_fsf_start_erp_timer(req);
+	retval = zfcp_fsf_req_send(req);
 	if (retval) {
-		ZFCP_LOG_INFO("error: Could not send a close unit request for "
-			      "unit 0x%016Lx on port 0x%016Lx onadapter %s.\n",
-			      erp_action->unit->fcp_lun,
-			      erp_action->port->wwpn,
-			      zfcp_get_busid_by_adapter(erp_action->adapter));
-		zfcp_fsf_req_free(fsf_req);
+		zfcp_fsf_req_free(req);
 		erp_action->fsf_req = NULL;
-		goto out;
 	}
-
-	ZFCP_LOG_TRACE("Close LUN request initiated (adapter %s, "
-		       "port 0x%016Lx, unit 0x%016Lx)\n",
-		       zfcp_get_busid_by_adapter(erp_action->adapter),
-		       erp_action->port->wwpn, erp_action->unit->fcp_lun);
- out:
-	write_unlock_irqrestore(&erp_action->adapter->request_queue.queue_lock,
-				lock_flags);
+out:
+	spin_unlock(&adapter->req_q.lock);
 	return retval;
 }
 
-/*
- * function:    zfcp_fsf_close_unit_handler
- *
- * purpose:     is called for finished Close LUN FSF command
- *
- * returns:
- */
-static int
-zfcp_fsf_close_unit_handler(struct zfcp_fsf_req *fsf_req)
+static void zfcp_fsf_close_unit_handler(struct zfcp_fsf_req *req)
 {
-	int retval = -EINVAL;
-	struct zfcp_unit *unit;
+	struct zfcp_unit *unit = req->data;
 
-	unit = (struct zfcp_unit *) fsf_req->data;
-
-	if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
-		/* don't change unit status in our bookkeeping */
+	if (req->status & ZFCP_STATUS_FSFREQ_ERROR)
 		goto skip_fsfstatus;
-	}
 
-	/* evaluate FSF status in QTCB */
-	switch (fsf_req->qtcb->header.fsf_status) {
-
+	switch (req->qtcb->header.fsf_status) {
 	case FSF_PORT_HANDLE_NOT_VALID:
-		ZFCP_LOG_INFO("Temporary port identifier 0x%x for port "
-			      "0x%016Lx on adapter %s invalid. This may "
-			      "happen in rare circumstances\n",
-			      unit->port->handle,
-			      unit->port->wwpn,
-			      zfcp_get_busid_by_unit(unit));
-		ZFCP_LOG_DEBUG("status qualifier:\n");
-		ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
-			      (char *) &fsf_req->qtcb->header.fsf_status_qual,
-			      sizeof (union fsf_status_qual));
-		zfcp_erp_adapter_reopen(unit->port->adapter, 0, 110, fsf_req);
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+		zfcp_erp_adapter_reopen(unit->port->adapter, 0, 110, req);
+		req->status |= ZFCP_STATUS_FSFREQ_ERROR;
 		break;
-
 	case FSF_LUN_HANDLE_NOT_VALID:
-		ZFCP_LOG_INFO("Temporary LUN identifier 0x%x of unit "
-			      "0x%016Lx on port 0x%016Lx on adapter %s is "
-			      "invalid. This may happen occasionally.\n",
-			      unit->handle,
-			      unit->fcp_lun,
-			      unit->port->wwpn,
-			      zfcp_get_busid_by_unit(unit));
-		ZFCP_LOG_DEBUG("Status qualifier data:\n");
-		ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
-			      (char *) &fsf_req->qtcb->header.fsf_status_qual,
-			      sizeof (union fsf_status_qual));
-		zfcp_erp_port_reopen(unit->port, 0, 111, fsf_req);
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+		zfcp_erp_port_reopen(unit->port, 0, 111, req);
+		req->status |= ZFCP_STATUS_FSFREQ_ERROR;
 		break;
-
 	case FSF_PORT_BOXED:
-		ZFCP_LOG_DEBUG("The remote port 0x%016Lx on adapter %s "
-			       "needs to be reopened\n",
-			       unit->port->wwpn,
-			       zfcp_get_busid_by_unit(unit));
-		zfcp_erp_port_boxed(unit->port, 52, fsf_req);
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR |
-			ZFCP_STATUS_FSFREQ_RETRY;
+		zfcp_erp_port_boxed(unit->port, 52, req);
+		req->status |= ZFCP_STATUS_FSFREQ_ERROR |
+			       ZFCP_STATUS_FSFREQ_RETRY;
 		break;
-
 	case FSF_ADAPTER_STATUS_AVAILABLE:
-		switch (fsf_req->qtcb->header.fsf_status_qual.word[0]) {
+		switch (req->qtcb->header.fsf_status_qual.word[0]) {
 		case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE:
-			/* re-establish link to port */
 			zfcp_test_link(unit->port);
-			fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-			break;
+			/* fall through */
 		case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED:
-			/* ERP strategy will escalate */
-			fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-			break;
-		default:
-			ZFCP_LOG_NORMAL
-			    ("bug: Wrong status qualifier 0x%x arrived.\n",
-			     fsf_req->qtcb->header.fsf_status_qual.word[0]);
+			req->status |= ZFCP_STATUS_FSFREQ_ERROR;
 			break;
 		}
 		break;
-
 	case FSF_GOOD:
-		ZFCP_LOG_TRACE("unit 0x%016Lx on port 0x%016Lx on adapter %s "
-			       "closed, port handle 0x%x\n",
-			       unit->fcp_lun,
-			       unit->port->wwpn,
-			       zfcp_get_busid_by_unit(unit),
-			       unit->handle);
-		/* mark unit as closed */
 		atomic_clear_mask(ZFCP_STATUS_COMMON_OPEN, &unit->status);
-		retval = 0;
-		break;
-
-	default:
-		ZFCP_LOG_NORMAL("bug: An unknown FSF Status was presented "
-				"(debug info 0x%x)\n",
-				fsf_req->qtcb->header.fsf_status);
 		break;
 	}
-
- skip_fsfstatus:
+skip_fsfstatus:
 	atomic_clear_mask(ZFCP_STATUS_COMMON_CLOSING, &unit->status);
+}
+
+/**
+ * zfcp_fsf_close_unit - close zfcp unit
+ * @erp_action: pointer to struct zfcp_unit
+ * Returns: 0 on success, error otherwise
+ */
+int zfcp_fsf_close_unit(struct zfcp_erp_action *erp_action)
+{
+	volatile struct qdio_buffer_element *sbale;
+	struct zfcp_adapter *adapter = erp_action->adapter;
+	struct zfcp_fsf_req *req;
+	int retval = -EIO;
+
+	spin_lock(&adapter->req_q.lock);
+	if (zfcp_fsf_req_sbal_get(adapter))
+		goto out;
+	req = zfcp_fsf_req_create(adapter, FSF_QTCB_CLOSE_LUN,
+				  ZFCP_REQ_AUTO_CLEANUP,
+				  adapter->pool.fsf_req_erp);
+	if (unlikely(IS_ERR(req))) {
+		retval = PTR_ERR(req);
+		goto out;
+	}
+
+	sbale = zfcp_qdio_sbale_req(req);
+	sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
+	sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
+
+	req->qtcb->header.port_handle = erp_action->port->handle;
+	req->qtcb->header.lun_handle = erp_action->unit->handle;
+	req->handler = zfcp_fsf_close_unit_handler;
+	req->data = erp_action->unit;
+	req->erp_action = erp_action;
+	erp_action->fsf_req = req;
+	atomic_set_mask(ZFCP_STATUS_COMMON_CLOSING, &erp_action->unit->status);
+
+	zfcp_fsf_start_erp_timer(req);
+	retval = zfcp_fsf_req_send(req);
+	if (retval) {
+		zfcp_fsf_req_free(req);
+		erp_action->fsf_req = NULL;
+	}
+out:
+	spin_unlock(&adapter->req_q.lock);
 	return retval;
 }
 
+static void zfcp_fsf_update_lat(struct fsf_latency_record *lat_rec, u32 lat)
+{
+	lat_rec->sum += lat;
+	lat_rec->min = min(lat_rec->min, lat);
+	lat_rec->max = max(lat_rec->max, lat);
+}
+
+static void zfcp_fsf_req_latency(struct zfcp_fsf_req *req)
+{
+	struct fsf_qual_latency_info *lat_inf;
+	struct latency_cont *lat;
+	struct zfcp_unit *unit = req->unit;
+	unsigned long flags;
+
+	lat_inf = &req->qtcb->prefix.prot_status_qual.latency_info;
+
+	switch (req->qtcb->bottom.io.data_direction) {
+	case FSF_DATADIR_READ:
+		lat = &unit->latencies.read;
+		break;
+	case FSF_DATADIR_WRITE:
+		lat = &unit->latencies.write;
+		break;
+	case FSF_DATADIR_CMND:
+		lat = &unit->latencies.cmd;
+		break;
+	default:
+		return;
+	}
+
+	spin_lock_irqsave(&unit->latencies.lock, flags);
+	zfcp_fsf_update_lat(&lat->channel, lat_inf->channel_lat);
+	zfcp_fsf_update_lat(&lat->fabric, lat_inf->fabric_lat);
+	lat->counter++;
+	spin_unlock_irqrestore(&unit->latencies.lock, flags);
+}
+
+static void zfcp_fsf_send_fcp_command_task_handler(struct zfcp_fsf_req *req)
+{
+	struct scsi_cmnd *scpnt = req->data;
+	struct fcp_rsp_iu *fcp_rsp_iu = (struct fcp_rsp_iu *)
+	    &(req->qtcb->bottom.io.fcp_rsp);
+	u32 sns_len;
+	char *fcp_rsp_info = (unsigned char *) &fcp_rsp_iu[1];
+	unsigned long flags;
+
+	if (unlikely(!scpnt))
+		return;
+
+	read_lock_irqsave(&req->adapter->abort_lock, flags);
+
+	if (unlikely(req->status & ZFCP_STATUS_FSFREQ_ABORTED)) {
+		set_host_byte(scpnt, DID_SOFT_ERROR);
+		set_driver_byte(scpnt, SUGGEST_RETRY);
+		goto skip_fsfstatus;
+	}
+
+	if (unlikely(req->status & ZFCP_STATUS_FSFREQ_ERROR)) {
+		set_host_byte(scpnt, DID_ERROR);
+		goto skip_fsfstatus;
+	}
+
+	set_msg_byte(scpnt, COMMAND_COMPLETE);
+
+	scpnt->result |= fcp_rsp_iu->scsi_status;
+
+	if (req->adapter->adapter_features & FSF_FEATURE_MEASUREMENT_DATA)
+		zfcp_fsf_req_latency(req);
+
+	if (unlikely(fcp_rsp_iu->validity.bits.fcp_rsp_len_valid)) {
+		if (fcp_rsp_info[3] == RSP_CODE_GOOD)
+			set_host_byte(scpnt, DID_OK);
+		else {
+			set_host_byte(scpnt, DID_ERROR);
+			goto skip_fsfstatus;
+		}
+	}
+
+	if (unlikely(fcp_rsp_iu->validity.bits.fcp_sns_len_valid)) {
+		sns_len = FSF_FCP_RSP_SIZE - sizeof(struct fcp_rsp_iu) +
+			  fcp_rsp_iu->fcp_rsp_len;
+		sns_len = min(sns_len, (u32) SCSI_SENSE_BUFFERSIZE);
+		sns_len = min(sns_len, fcp_rsp_iu->fcp_sns_len);
+
+		memcpy(scpnt->sense_buffer,
+		       zfcp_get_fcp_sns_info_ptr(fcp_rsp_iu), sns_len);
+	}
+
+	if (unlikely(fcp_rsp_iu->validity.bits.fcp_resid_under)) {
+		scsi_set_resid(scpnt, fcp_rsp_iu->fcp_resid);
+		if (scsi_bufflen(scpnt) - scsi_get_resid(scpnt) <
+		    scpnt->underflow)
+			set_host_byte(scpnt, DID_ERROR);
+	}
+skip_fsfstatus:
+	if (scpnt->result != 0)
+		zfcp_scsi_dbf_event_result("erro", 3, req->adapter, scpnt, req);
+	else if (scpnt->retries > 0)
+		zfcp_scsi_dbf_event_result("retr", 4, req->adapter, scpnt, req);
+	else
+		zfcp_scsi_dbf_event_result("norm", 6, req->adapter, scpnt, req);
+
+	scpnt->host_scribble = NULL;
+	(scpnt->scsi_done) (scpnt);
+	/*
+	 * We must hold this lock until scsi_done has been called.
+	 * Otherwise we may call scsi_done after abort regarding this
+	 * command has completed.
+	 * Note: scsi_done must not block!
+	 */
+	read_unlock_irqrestore(&req->adapter->abort_lock, flags);
+}
+
+static void zfcp_fsf_send_fcp_ctm_handler(struct zfcp_fsf_req *req)
+{
+	struct fcp_rsp_iu *fcp_rsp_iu = (struct fcp_rsp_iu *)
+	    &(req->qtcb->bottom.io.fcp_rsp);
+	char *fcp_rsp_info = (unsigned char *) &fcp_rsp_iu[1];
+
+	if ((fcp_rsp_info[3] != RSP_CODE_GOOD) ||
+	     (req->status & ZFCP_STATUS_FSFREQ_ERROR))
+		req->status |= ZFCP_STATUS_FSFREQ_TMFUNCFAILED;
+}
+
+
+static void zfcp_fsf_send_fcp_command_handler(struct zfcp_fsf_req *req)
+{
+	struct zfcp_unit *unit;
+	struct fsf_qtcb_header *header = &req->qtcb->header;
+
+	if (unlikely(req->status & ZFCP_STATUS_FSFREQ_TASK_MANAGEMENT))
+		unit = req->data;
+	else
+		unit = req->unit;
+
+	if (unlikely(req->status & ZFCP_STATUS_FSFREQ_ERROR))
+		goto skip_fsfstatus;
+
+	switch (header->fsf_status) {
+	case FSF_HANDLE_MISMATCH:
+	case FSF_PORT_HANDLE_NOT_VALID:
+		zfcp_erp_adapter_reopen(unit->port->adapter, 0, 112, req);
+		req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+		break;
+	case FSF_FCPLUN_NOT_VALID:
+	case FSF_LUN_HANDLE_NOT_VALID:
+		zfcp_erp_port_reopen(unit->port, 0, 113, req);
+		req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+		break;
+	case FSF_SERVICE_CLASS_NOT_SUPPORTED:
+		zfcp_fsf_class_not_supp(req);
+		break;
+	case FSF_ACCESS_DENIED:
+		zfcp_fsf_access_denied_unit(req, unit);
+		break;
+	case FSF_DIRECTION_INDICATOR_NOT_VALID:
+		dev_err(&req->adapter->ccw_device->dev,
+			"Invalid data direction (%d) given for unit "
+			"0x%016Lx on port 0x%016Lx, shutting down "
+			"adapter.\n",
+			req->qtcb->bottom.io.data_direction,
+			unit->fcp_lun, unit->port->wwpn);
+		zfcp_erp_adapter_shutdown(unit->port->adapter, 0, 133, req);
+		req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+		break;
+	case FSF_CMND_LENGTH_NOT_VALID:
+		dev_err(&req->adapter->ccw_device->dev,
+			"An invalid control-data-block length field (%d) "
+			"was found in a command for unit 0x%016Lx on port "
+			"0x%016Lx. Shutting down adapter.\n",
+			req->qtcb->bottom.io.fcp_cmnd_length,
+			unit->fcp_lun, unit->port->wwpn);
+		zfcp_erp_adapter_shutdown(unit->port->adapter, 0, 134, req);
+		req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+		break;
+	case FSF_PORT_BOXED:
+		zfcp_erp_port_boxed(unit->port, 53, req);
+		req->status |= ZFCP_STATUS_FSFREQ_ERROR |
+			       ZFCP_STATUS_FSFREQ_RETRY;
+		break;
+	case FSF_LUN_BOXED:
+		zfcp_erp_unit_boxed(unit, 54, req);
+		req->status |= ZFCP_STATUS_FSFREQ_ERROR |
+			       ZFCP_STATUS_FSFREQ_RETRY;
+		break;
+	case FSF_ADAPTER_STATUS_AVAILABLE:
+		if (header->fsf_status_qual.word[0] ==
+		    FSF_SQ_INVOKE_LINK_TEST_PROCEDURE)
+			zfcp_test_link(unit->port);
+		req->status |= ZFCP_STATUS_FSFREQ_ERROR;
+		break;
+	}
+skip_fsfstatus:
+	if (req->status & ZFCP_STATUS_FSFREQ_TASK_MANAGEMENT)
+		zfcp_fsf_send_fcp_ctm_handler(req);
+	else {
+		zfcp_fsf_send_fcp_command_task_handler(req);
+		req->unit = NULL;
+		zfcp_unit_put(unit);
+	}
+}
+
 /**
  * zfcp_fsf_send_fcp_command_task - initiate an FCP command (for a SCSI command)
  * @adapter: adapter where scsi command is issued
@@ -3391,57 +2209,42 @@
  * @timer: timer to be started when request is initiated
  * @req_flags: flags for fsf_request
  */
-int
-zfcp_fsf_send_fcp_command_task(struct zfcp_adapter *adapter,
-			       struct zfcp_unit *unit,
-			       struct scsi_cmnd * scsi_cmnd,
-			       int use_timer, int req_flags)
+int zfcp_fsf_send_fcp_command_task(struct zfcp_adapter *adapter,
+				   struct zfcp_unit *unit,
+				   struct scsi_cmnd *scsi_cmnd,
+				   int use_timer, int req_flags)
 {
-	struct zfcp_fsf_req *fsf_req = NULL;
+	struct zfcp_fsf_req *req;
 	struct fcp_cmnd_iu *fcp_cmnd_iu;
 	unsigned int sbtype;
-	unsigned long lock_flags;
-	int real_bytes = 0;
-	int retval = 0;
-	int mask;
+	int real_bytes, retval = -EIO;
 
-	/* setup new FSF request */
-	retval = zfcp_fsf_req_create(adapter, FSF_QTCB_FCP_CMND, req_flags,
-				     adapter->pool.fsf_req_scsi,
-				     &lock_flags, &fsf_req);
-	if (unlikely(retval < 0)) {
-		ZFCP_LOG_DEBUG("error: Could not create FCP command request "
-			       "for unit 0x%016Lx on port 0x%016Lx on "
-			       "adapter %s\n",
-			       unit->fcp_lun,
-			       unit->port->wwpn,
-			       zfcp_get_busid_by_adapter(adapter));
-		goto failed_req_create;
-	}
+	if (unlikely(!(atomic_read(&unit->status) &
+		       ZFCP_STATUS_COMMON_UNBLOCKED)))
+		return -EBUSY;
 
-	if (unlikely(!atomic_test_mask(ZFCP_STATUS_COMMON_UNBLOCKED,
-			&unit->status))) {
-		retval = -EBUSY;
-		goto unit_blocked;
+	spin_lock(&adapter->req_q.lock);
+	if (!atomic_read(&adapter->req_q.count))
+		goto out;
+	req = zfcp_fsf_req_create(adapter, FSF_QTCB_FCP_CMND, req_flags,
+				  adapter->pool.fsf_req_scsi);
+	if (unlikely(IS_ERR(req))) {
+		retval = PTR_ERR(req);
+		goto out;
 	}
 
 	zfcp_unit_get(unit);
-	fsf_req->unit = unit;
+	req->unit = unit;
+	req->data = scsi_cmnd;
+	req->handler = zfcp_fsf_send_fcp_command_handler;
+	req->qtcb->header.lun_handle = unit->handle;
+	req->qtcb->header.port_handle = unit->port->handle;
+	req->qtcb->bottom.io.service_class = FSF_CLASS_3;
 
-	/* associate FSF request with SCSI request (for look up on abort) */
-	scsi_cmnd->host_scribble = (unsigned char *) fsf_req->req_id;
+	scsi_cmnd->host_scribble = (unsigned char *) req->req_id;
 
-	/* associate SCSI command with FSF request */
-	fsf_req->data = (unsigned long) scsi_cmnd;
-
-	/* set handles of unit and its parent port in QTCB */
-	fsf_req->qtcb->header.lun_handle = unit->handle;
-	fsf_req->qtcb->header.port_handle = unit->port->handle;
-
-	/* FSF does not define the structure of the FCP_CMND IU */
-	fcp_cmnd_iu = (struct fcp_cmnd_iu *)
-	    &(fsf_req->qtcb->bottom.io.fcp_cmnd);
-
+	fcp_cmnd_iu = (struct fcp_cmnd_iu *) &(req->qtcb->bottom.io.fcp_cmnd);
+	fcp_cmnd_iu->fcp_lun = unit->fcp_lun;
 	/*
 	 * set depending on data direction:
 	 *      data direction bits in SBALE (SB Type)
@@ -3450,1246 +2253,206 @@
 	 */
 	switch (scsi_cmnd->sc_data_direction) {
 	case DMA_NONE:
-		fsf_req->qtcb->bottom.io.data_direction = FSF_DATADIR_CMND;
-		/*
-		 * FIXME(qdio):
-		 * what is the correct type for commands
-		 * without 'real' data buffers?
-		 */
+		req->qtcb->bottom.io.data_direction = FSF_DATADIR_CMND;
 		sbtype = SBAL_FLAGS0_TYPE_READ;
 		break;
 	case DMA_FROM_DEVICE:
-		fsf_req->qtcb->bottom.io.data_direction = FSF_DATADIR_READ;
+		req->qtcb->bottom.io.data_direction = FSF_DATADIR_READ;
 		sbtype = SBAL_FLAGS0_TYPE_READ;
 		fcp_cmnd_iu->rddata = 1;
 		break;
 	case DMA_TO_DEVICE:
-		fsf_req->qtcb->bottom.io.data_direction = FSF_DATADIR_WRITE;
+		req->qtcb->bottom.io.data_direction = FSF_DATADIR_WRITE;
 		sbtype = SBAL_FLAGS0_TYPE_WRITE;
 		fcp_cmnd_iu->wddata = 1;
 		break;
 	case DMA_BIDIRECTIONAL:
 	default:
-		/*
-		 * dummy, catch this condition earlier
-		 * in zfcp_scsi_queuecommand
-		 */
+		retval = -EIO;
 		goto failed_scsi_cmnd;
 	}
 
-	/* set FC service class in QTCB (3 per default) */
-	fsf_req->qtcb->bottom.io.service_class = ZFCP_FC_SERVICE_CLASS_DEFAULT;
-
-	/* set FCP_LUN in FCP_CMND IU in QTCB */
-	fcp_cmnd_iu->fcp_lun = unit->fcp_lun;
-
-	mask = ZFCP_STATUS_UNIT_READONLY | ZFCP_STATUS_UNIT_SHARED;
-
-	/* set task attributes in FCP_CMND IU in QTCB */
 	if (likely((scsi_cmnd->device->simple_tags) ||
-		   (atomic_test_mask(mask, &unit->status))))
+		   ((atomic_read(&unit->status) & ZFCP_STATUS_UNIT_READONLY) &&
+		    (atomic_read(&unit->status) & ZFCP_STATUS_UNIT_SHARED))))
 		fcp_cmnd_iu->task_attribute = SIMPLE_Q;
 	else
 		fcp_cmnd_iu->task_attribute = UNTAGGED;
 
-	/* set additional length of FCP_CDB in FCP_CMND IU in QTCB, if needed */
-	if (unlikely(scsi_cmnd->cmd_len > FCP_CDB_LENGTH)) {
-		fcp_cmnd_iu->add_fcp_cdb_length
-		    = (scsi_cmnd->cmd_len - FCP_CDB_LENGTH) >> 2;
-		ZFCP_LOG_TRACE("SCSI CDB length is 0x%x, "
-			       "additional FCP_CDB length is 0x%x "
-			       "(shifted right 2 bits)\n",
-			       scsi_cmnd->cmd_len,
-			       fcp_cmnd_iu->add_fcp_cdb_length);
-	}
-	/*
-	 * copy SCSI CDB (including additional length, if any) to
-	 * FCP_CDB in FCP_CMND IU in QTCB
-	 */
+	if (unlikely(scsi_cmnd->cmd_len > FCP_CDB_LENGTH))
+		fcp_cmnd_iu->add_fcp_cdb_length =
+			(scsi_cmnd->cmd_len - FCP_CDB_LENGTH) >> 2;
+
 	memcpy(fcp_cmnd_iu->fcp_cdb, scsi_cmnd->cmnd, scsi_cmnd->cmd_len);
 
-	/* FCP CMND IU length in QTCB */
-	fsf_req->qtcb->bottom.io.fcp_cmnd_length =
-		sizeof (struct fcp_cmnd_iu) +
-		fcp_cmnd_iu->add_fcp_cdb_length + sizeof (fcp_dl_t);
+	req->qtcb->bottom.io.fcp_cmnd_length = sizeof(struct fcp_cmnd_iu) +
+		fcp_cmnd_iu->add_fcp_cdb_length + sizeof(fcp_dl_t);
 
-	/* generate SBALEs from data buffer */
-	real_bytes = zfcp_qdio_sbals_from_scsicmnd(fsf_req, sbtype, scsi_cmnd);
+	real_bytes = zfcp_qdio_sbals_from_sg(req, sbtype,
+					     scsi_sglist(scsi_cmnd),
+					     FSF_MAX_SBALS_PER_REQ);
 	if (unlikely(real_bytes < 0)) {
-		if (fsf_req->sbal_number < ZFCP_MAX_SBALS_PER_REQ) {
-			ZFCP_LOG_DEBUG(
-				"Data did not fit into available buffer(s), "
-			       "waiting for more...\n");
+		if (req->sbal_number < FSF_MAX_SBALS_PER_REQ)
 			retval = -EIO;
-		} else {
-			ZFCP_LOG_NORMAL("error: No truncation implemented but "
-					"required. Shutting down unit "
-					"(adapter %s, port 0x%016Lx, "
-					"unit 0x%016Lx)\n",
-					zfcp_get_busid_by_unit(unit),
-					unit->port->wwpn,
-					unit->fcp_lun);
-			zfcp_erp_unit_shutdown(unit, 0, 131, fsf_req);
+		else {
+			dev_err(&adapter->ccw_device->dev,
+				"SCSI request too large. "
+				"Shutting down unit 0x%016Lx on port "
+				"0x%016Lx.\n", unit->fcp_lun,
+				unit->port->wwpn);
+			zfcp_erp_unit_shutdown(unit, 0, 131, req);
 			retval = -EINVAL;
 		}
-		goto no_fit;
+		goto failed_scsi_cmnd;
 	}
 
-	/* set length of FCP data length in FCP_CMND IU in QTCB */
 	zfcp_set_fcp_dl(fcp_cmnd_iu, real_bytes);
 
-	ZFCP_LOG_DEBUG("Sending SCSI command:\n");
-	ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
-		      (char *) scsi_cmnd->cmnd, scsi_cmnd->cmd_len);
-
 	if (use_timer)
-		zfcp_fsf_start_timer(fsf_req, ZFCP_FSF_REQUEST_TIMEOUT);
+		zfcp_fsf_start_timer(req, ZFCP_FSF_REQUEST_TIMEOUT);
 
-	retval = zfcp_fsf_req_send(fsf_req);
-	if (unlikely(retval < 0)) {
-		ZFCP_LOG_INFO("error: Could not send FCP command request "
-			      "on adapter %s, port 0x%016Lx, unit 0x%016Lx\n",
-			      zfcp_get_busid_by_adapter(adapter),
-			      unit->port->wwpn,
-			      unit->fcp_lun);
-		goto send_failed;
-	}
-
-	ZFCP_LOG_TRACE("Send FCP Command initiated (adapter %s, "
-		       "port 0x%016Lx, unit 0x%016Lx)\n",
-		       zfcp_get_busid_by_adapter(adapter),
-		       unit->port->wwpn,
-		       unit->fcp_lun);
-	goto success;
-
- send_failed:
- no_fit:
- failed_scsi_cmnd:
-	zfcp_unit_put(unit);
- unit_blocked:
-	zfcp_fsf_req_free(fsf_req);
-	fsf_req = NULL;
-	scsi_cmnd->host_scribble = NULL;
- success:
- failed_req_create:
-	write_unlock_irqrestore(&adapter->request_queue.queue_lock, lock_flags);
-	return retval;
-}
-
-struct zfcp_fsf_req *
-zfcp_fsf_send_fcp_command_task_management(struct zfcp_adapter *adapter,
-					  struct zfcp_unit *unit,
-					  u8 tm_flags, int req_flags)
-{
-	struct zfcp_fsf_req *fsf_req = NULL;
-	int retval = 0;
-	struct fcp_cmnd_iu *fcp_cmnd_iu;
-	unsigned long lock_flags;
-	volatile struct qdio_buffer_element *sbale;
-
-	/* setup new FSF request */
-	retval = zfcp_fsf_req_create(adapter, FSF_QTCB_FCP_CMND, req_flags,
-				     adapter->pool.fsf_req_scsi,
-				     &lock_flags, &fsf_req);
-	if (retval < 0) {
-		ZFCP_LOG_INFO("error: Could not create FCP command (task "
-			      "management) request for adapter %s, port "
-			      " 0x%016Lx, unit 0x%016Lx.\n",
-			      zfcp_get_busid_by_adapter(adapter),
-			      unit->port->wwpn, unit->fcp_lun);
-		goto out;
-	}
-
-	if (unlikely(!atomic_test_mask(ZFCP_STATUS_COMMON_UNBLOCKED,
-			&unit->status)))
-		goto unit_blocked;
-
-	/*
-	 * Used to decide on proper handler in the return path,
-	 * could be either zfcp_fsf_send_fcp_command_task_handler or
-	 * zfcp_fsf_send_fcp_command_task_management_handler */
-
-	fsf_req->status |= ZFCP_STATUS_FSFREQ_TASK_MANAGEMENT;
-
-	/*
-	 * hold a pointer to the unit being target of this
-	 * task management request
-	 */
-	fsf_req->data = (unsigned long) unit;
-
-	/* set FSF related fields in QTCB */
-	fsf_req->qtcb->header.lun_handle = unit->handle;
-	fsf_req->qtcb->header.port_handle = unit->port->handle;
-	fsf_req->qtcb->bottom.io.data_direction = FSF_DATADIR_CMND;
-	fsf_req->qtcb->bottom.io.service_class = ZFCP_FC_SERVICE_CLASS_DEFAULT;
-	fsf_req->qtcb->bottom.io.fcp_cmnd_length =
-		sizeof (struct fcp_cmnd_iu) + sizeof (fcp_dl_t);
-
-	sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
-	sbale[0].flags |= SBAL_FLAGS0_TYPE_WRITE;
-	sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
-
-	/* set FCP related fields in FCP_CMND IU in QTCB */
-	fcp_cmnd_iu = (struct fcp_cmnd_iu *)
-		&(fsf_req->qtcb->bottom.io.fcp_cmnd);
-	fcp_cmnd_iu->fcp_lun = unit->fcp_lun;
-	fcp_cmnd_iu->task_management_flags = tm_flags;
-
-	zfcp_fsf_start_timer(fsf_req, ZFCP_SCSI_ER_TIMEOUT);
-	retval = zfcp_fsf_req_send(fsf_req);
-	if (!retval)
-		goto out;
-
- unit_blocked:
-	zfcp_fsf_req_free(fsf_req);
-	fsf_req = NULL;
-
- out:
-	write_unlock_irqrestore(&adapter->request_queue.queue_lock, lock_flags);
-	return fsf_req;
-}
-
-/*
- * function:    zfcp_fsf_send_fcp_command_handler
- *
- * purpose:	is called for finished Send FCP Command
- *
- * returns:
- */
-static int
-zfcp_fsf_send_fcp_command_handler(struct zfcp_fsf_req *fsf_req)
-{
-	int retval = -EINVAL;
-	struct zfcp_unit *unit;
-	struct fsf_qtcb_header *header;
-	u16 subtable, rule, counter;
-
-	header = &fsf_req->qtcb->header;
-
-	if (unlikely(fsf_req->status & ZFCP_STATUS_FSFREQ_TASK_MANAGEMENT))
-		unit = (struct zfcp_unit *) fsf_req->data;
-	else
-		unit = fsf_req->unit;
-
-	if (unlikely(fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR)) {
-		/* go directly to calls of special handlers */
-		goto skip_fsfstatus;
-	}
-
-	/* evaluate FSF status in QTCB */
-	switch (header->fsf_status) {
-
-	case FSF_PORT_HANDLE_NOT_VALID:
-		ZFCP_LOG_INFO("Temporary port identifier 0x%x for port "
-			      "0x%016Lx on adapter %s invalid\n",
-			      unit->port->handle,
-			      unit->port->wwpn, zfcp_get_busid_by_unit(unit));
-		ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
-			      (char *) &header->fsf_status_qual,
-			      sizeof (union fsf_status_qual));
-		zfcp_erp_adapter_reopen(unit->port->adapter, 0, 112, fsf_req);
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-		break;
-
-	case FSF_LUN_HANDLE_NOT_VALID:
-		ZFCP_LOG_INFO("Temporary LUN identifier 0x%x for unit "
-			      "0x%016Lx on port 0x%016Lx on adapter %s is "
-			      "invalid. This may happen occasionally.\n",
-			      unit->handle,
-			      unit->fcp_lun,
-			      unit->port->wwpn,
-			      zfcp_get_busid_by_unit(unit));
-		ZFCP_LOG_NORMAL("Status qualifier data:\n");
-		ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_NORMAL,
-			      (char *) &header->fsf_status_qual,
-			      sizeof (union fsf_status_qual));
-		zfcp_erp_port_reopen(unit->port, 0, 113, fsf_req);
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-		break;
-
-	case FSF_HANDLE_MISMATCH:
-		ZFCP_LOG_NORMAL("bug: The port handle 0x%x has changed "
-				"unexpectedly. (adapter %s, port 0x%016Lx, "
-				"unit 0x%016Lx)\n",
-				unit->port->handle,
-				zfcp_get_busid_by_unit(unit),
-				unit->port->wwpn,
-				unit->fcp_lun);
-		ZFCP_LOG_NORMAL("status qualifier:\n");
-		ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_NORMAL,
-			      (char *) &header->fsf_status_qual,
-			      sizeof (union fsf_status_qual));
-		zfcp_erp_adapter_reopen(unit->port->adapter, 0, 114, fsf_req);
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-		break;
-
-	case FSF_SERVICE_CLASS_NOT_SUPPORTED:
-		ZFCP_LOG_INFO("error: adapter %s does not support fc "
-			      "class %d.\n",
-			      zfcp_get_busid_by_unit(unit),
-			      ZFCP_FC_SERVICE_CLASS_DEFAULT);
-		/* stop operation for this adapter */
-		zfcp_erp_adapter_shutdown(unit->port->adapter, 0, 132, fsf_req);
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-		break;
-
-	case FSF_FCPLUN_NOT_VALID:
-		ZFCP_LOG_NORMAL("bug: unit 0x%016Lx on port 0x%016Lx on "
-				"adapter %s does not have correct unit "
-				"handle 0x%x\n",
-				unit->fcp_lun,
-				unit->port->wwpn,
-				zfcp_get_busid_by_unit(unit),
-				unit->handle);
-		ZFCP_LOG_DEBUG("status qualifier:\n");
-		ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
-			      (char *) &header->fsf_status_qual,
-			      sizeof (union fsf_status_qual));
-		zfcp_erp_port_reopen(unit->port, 0, 115, fsf_req);
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-		break;
-
-	case FSF_ACCESS_DENIED:
-		ZFCP_LOG_NORMAL("Access denied, cannot send FCP command to "
-				"unit 0x%016Lx on port 0x%016Lx on "
-				"adapter %s\n",	unit->fcp_lun, unit->port->wwpn,
-				zfcp_get_busid_by_unit(unit));
-		for (counter = 0; counter < 2; counter++) {
-			subtable = header->fsf_status_qual.halfword[counter * 2];
-			rule = header->fsf_status_qual.halfword[counter * 2 + 1];
-			switch (subtable) {
-			case FSF_SQ_CFDC_SUBTABLE_OS:
-			case FSF_SQ_CFDC_SUBTABLE_PORT_WWPN:
-			case FSF_SQ_CFDC_SUBTABLE_PORT_DID:
-			case FSF_SQ_CFDC_SUBTABLE_LUN:
-				ZFCP_LOG_INFO("Access denied (%s rule %d)\n",
-					zfcp_act_subtable_type[subtable], rule);
-				break;
-			}
-		}
-		zfcp_erp_unit_access_denied(unit, 61, fsf_req);
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-		break;
-
-	case FSF_DIRECTION_INDICATOR_NOT_VALID:
-		ZFCP_LOG_INFO("bug: Invalid data direction given for unit "
-			      "0x%016Lx on port 0x%016Lx on adapter %s "
-			      "(debug info %d)\n",
-			      unit->fcp_lun,
-			      unit->port->wwpn,
-			      zfcp_get_busid_by_unit(unit),
-			      fsf_req->qtcb->bottom.io.data_direction);
-		/* stop operation for this adapter */
-		zfcp_erp_adapter_shutdown(unit->port->adapter, 0, 133, fsf_req);
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-		break;
-
-	case FSF_CMND_LENGTH_NOT_VALID:
-		ZFCP_LOG_NORMAL
-		    ("bug: An invalid control-data-block length field "
-		     "was found in a command for unit 0x%016Lx on port "
-		     "0x%016Lx on adapter %s " "(debug info %d)\n",
-		     unit->fcp_lun, unit->port->wwpn,
-		     zfcp_get_busid_by_unit(unit),
-		     fsf_req->qtcb->bottom.io.fcp_cmnd_length);
-		/* stop operation for this adapter */
-		zfcp_erp_adapter_shutdown(unit->port->adapter, 0, 134, fsf_req);
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-		break;
-
-	case FSF_PORT_BOXED:
-		ZFCP_LOG_DEBUG("The remote port 0x%016Lx on adapter %s "
-			       "needs to be reopened\n",
-			       unit->port->wwpn, zfcp_get_busid_by_unit(unit));
-		zfcp_erp_port_boxed(unit->port, 53, fsf_req);
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR |
-			ZFCP_STATUS_FSFREQ_RETRY;
-		break;
-
-	case FSF_LUN_BOXED:
-		ZFCP_LOG_NORMAL("unit needs to be reopened (adapter %s, "
-				"wwpn=0x%016Lx, fcp_lun=0x%016Lx)\n",
-				zfcp_get_busid_by_unit(unit),
-				unit->port->wwpn, unit->fcp_lun);
-		zfcp_erp_unit_boxed(unit, 54, fsf_req);
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR
-			| ZFCP_STATUS_FSFREQ_RETRY;
-		break;
-
-	case FSF_ADAPTER_STATUS_AVAILABLE:
-		switch (header->fsf_status_qual.word[0]) {
-		case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE:
-			/* re-establish link to port */
- 			zfcp_test_link(unit->port);
-			break;
-		case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED:
-			/* FIXME(hw) need proper specs for proper action */
-			/* let scsi stack deal with retries and escalation */
-			break;
-		default:
-			ZFCP_LOG_NORMAL
- 			    ("Unknown status qualifier 0x%x arrived.\n",
-			     header->fsf_status_qual.word[0]);
-			break;
-		}
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-		break;
-
-	case FSF_GOOD:
-		break;
-
-	case FSF_FCP_RSP_AVAILABLE:
-		break;
-	}
-
- skip_fsfstatus:
-	if (fsf_req->status & ZFCP_STATUS_FSFREQ_TASK_MANAGEMENT) {
-		retval =
-		    zfcp_fsf_send_fcp_command_task_management_handler(fsf_req);
-	} else {
-		retval = zfcp_fsf_send_fcp_command_task_handler(fsf_req);
-		fsf_req->unit = NULL;
-		zfcp_unit_put(unit);
-	}
-	return retval;
-}
-
-/*
- * function:    zfcp_fsf_send_fcp_command_task_handler
- *
- * purpose:	evaluates FCP_RSP IU
- *
- * returns:
- */
-static int
-zfcp_fsf_send_fcp_command_task_handler(struct zfcp_fsf_req *fsf_req)
-{
-	int retval = 0;
-	struct scsi_cmnd *scpnt;
-	struct fcp_rsp_iu *fcp_rsp_iu = (struct fcp_rsp_iu *)
-	    &(fsf_req->qtcb->bottom.io.fcp_rsp);
-	struct fcp_cmnd_iu *fcp_cmnd_iu = (struct fcp_cmnd_iu *)
-	    &(fsf_req->qtcb->bottom.io.fcp_cmnd);
-	u32 sns_len;
-	char *fcp_rsp_info = zfcp_get_fcp_rsp_info_ptr(fcp_rsp_iu);
-	unsigned long flags;
-	struct zfcp_unit *unit = fsf_req->unit;
-
-	read_lock_irqsave(&fsf_req->adapter->abort_lock, flags);
-	scpnt = (struct scsi_cmnd *) fsf_req->data;
-	if (unlikely(!scpnt)) {
-		ZFCP_LOG_DEBUG
-		    ("Command with fsf_req %p is not associated to "
-		     "a scsi command anymore. Aborted?\n", fsf_req);
-		goto out;
-	}
-	if (unlikely(fsf_req->status & ZFCP_STATUS_FSFREQ_ABORTED)) {
-		/* FIXME: (design) mid-layer should handle DID_ABORT like
-		 *        DID_SOFT_ERROR by retrying the request for devices
-		 *        that allow retries.
-		 */
-		ZFCP_LOG_DEBUG("Setting DID_SOFT_ERROR and SUGGEST_RETRY\n");
-		set_host_byte(&scpnt->result, DID_SOFT_ERROR);
-		set_driver_byte(&scpnt->result, SUGGEST_RETRY);
-		goto skip_fsfstatus;
-	}
-
-	if (unlikely(fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR)) {
-		ZFCP_LOG_DEBUG("Setting DID_ERROR\n");
-		set_host_byte(&scpnt->result, DID_ERROR);
-		goto skip_fsfstatus;
-	}
-
-	/* set message byte of result in SCSI command */
-	scpnt->result |= COMMAND_COMPLETE << 8;
-
-	/*
-	 * copy SCSI status code of FCP_STATUS of FCP_RSP IU to status byte
-	 * of result in SCSI command
-	 */
-	scpnt->result |= fcp_rsp_iu->scsi_status;
-	if (unlikely(fcp_rsp_iu->scsi_status)) {
-		/* DEBUG */
-		ZFCP_LOG_DEBUG("status for SCSI Command:\n");
-		ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
-			      scpnt->cmnd, scpnt->cmd_len);
-		ZFCP_LOG_DEBUG("SCSI status code 0x%x\n",
-				fcp_rsp_iu->scsi_status);
-		ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
-			      (void *) fcp_rsp_iu, sizeof (struct fcp_rsp_iu));
-		ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
-			      zfcp_get_fcp_sns_info_ptr(fcp_rsp_iu),
-			      fcp_rsp_iu->fcp_sns_len);
-	}
-
-	/* check FCP_RSP_INFO */
-	if (unlikely(fcp_rsp_iu->validity.bits.fcp_rsp_len_valid)) {
-		ZFCP_LOG_DEBUG("rsp_len is valid\n");
-		switch (fcp_rsp_info[3]) {
-		case RSP_CODE_GOOD:
-			/* ok, continue */
-			ZFCP_LOG_TRACE("no failure or Task Management "
-				       "Function complete\n");
-			set_host_byte(&scpnt->result, DID_OK);
-			break;
-		case RSP_CODE_LENGTH_MISMATCH:
-			/* hardware bug */
-			ZFCP_LOG_NORMAL("bug: FCP response code indictates "
-					"that the fibrechannel protocol data "
-					"length differs from the burst length. "
-					"The problem occured on unit 0x%016Lx "
-					"on port 0x%016Lx on adapter %s",
-					unit->fcp_lun,
-					unit->port->wwpn,
-					zfcp_get_busid_by_unit(unit));
-			/* dump SCSI CDB as prepared by zfcp */
-			ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
-				      (char *) &fsf_req->qtcb->
-				      bottom.io.fcp_cmnd, FSF_FCP_CMND_SIZE);
-			set_host_byte(&scpnt->result, DID_ERROR);
-			goto skip_fsfstatus;
-		case RSP_CODE_FIELD_INVALID:
-			/* driver or hardware bug */
-			ZFCP_LOG_NORMAL("bug: FCP response code indictates "
-					"that the fibrechannel protocol data "
-					"fields were incorrectly set up. "
-					"The problem occured on the unit "
-					"0x%016Lx on port 0x%016Lx on "
-					"adapter %s",
-					unit->fcp_lun,
-					unit->port->wwpn,
-					zfcp_get_busid_by_unit(unit));
-			/* dump SCSI CDB as prepared by zfcp */
-			ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
-				      (char *) &fsf_req->qtcb->
-				      bottom.io.fcp_cmnd, FSF_FCP_CMND_SIZE);
-			set_host_byte(&scpnt->result, DID_ERROR);
-			goto skip_fsfstatus;
-		case RSP_CODE_RO_MISMATCH:
-			/* hardware bug */
-			ZFCP_LOG_NORMAL("bug: The FCP response code indicates "
-					"that conflicting  values for the "
-					"fibrechannel payload offset from the "
-					"header were found. "
-					"The problem occured on unit 0x%016Lx "
-					"on port 0x%016Lx on adapter %s.\n",
-					unit->fcp_lun,
-					unit->port->wwpn,
-					zfcp_get_busid_by_unit(unit));
-			/* dump SCSI CDB as prepared by zfcp */
-			ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
-				      (char *) &fsf_req->qtcb->
-				      bottom.io.fcp_cmnd, FSF_FCP_CMND_SIZE);
-			set_host_byte(&scpnt->result, DID_ERROR);
-			goto skip_fsfstatus;
-		default:
-			ZFCP_LOG_NORMAL("bug: An invalid FCP response "
-					"code was detected for a command. "
-					"The problem occured on the unit "
-					"0x%016Lx on port 0x%016Lx on "
-					"adapter %s (debug info 0x%x)\n",
-					unit->fcp_lun,
-					unit->port->wwpn,
-					zfcp_get_busid_by_unit(unit),
-					fcp_rsp_info[3]);
-			/* dump SCSI CDB as prepared by zfcp */
-			ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
-				      (char *) &fsf_req->qtcb->
-				      bottom.io.fcp_cmnd, FSF_FCP_CMND_SIZE);
-			set_host_byte(&scpnt->result, DID_ERROR);
-			goto skip_fsfstatus;
-		}
-	}
-
-	/* check for sense data */
-	if (unlikely(fcp_rsp_iu->validity.bits.fcp_sns_len_valid)) {
-		sns_len = FSF_FCP_RSP_SIZE -
-		    sizeof (struct fcp_rsp_iu) + fcp_rsp_iu->fcp_rsp_len;
-		ZFCP_LOG_TRACE("room for %i bytes sense data in QTCB\n",
-			       sns_len);
-		sns_len = min(sns_len, (u32) SCSI_SENSE_BUFFERSIZE);
-		ZFCP_LOG_TRACE("room for %i bytes sense data in SCSI command\n",
-			       SCSI_SENSE_BUFFERSIZE);
-		sns_len = min(sns_len, fcp_rsp_iu->fcp_sns_len);
-		ZFCP_LOG_TRACE("scpnt->result =0x%x, command was:\n",
-			       scpnt->result);
-		ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_TRACE,
-			      scpnt->cmnd, scpnt->cmd_len);
-
-		ZFCP_LOG_TRACE("%i bytes sense data provided by FCP\n",
-			       fcp_rsp_iu->fcp_sns_len);
-		memcpy(scpnt->sense_buffer,
-		       zfcp_get_fcp_sns_info_ptr(fcp_rsp_iu), sns_len);
-		ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_TRACE,
-			      (void *)scpnt->sense_buffer, sns_len);
-	}
-
-	/* check for overrun */
-	if (unlikely(fcp_rsp_iu->validity.bits.fcp_resid_over)) {
-		ZFCP_LOG_INFO("A data overrun was detected for a command. "
-			      "unit 0x%016Lx, port 0x%016Lx, adapter %s. "
-			      "The response data length is "
-			      "%d, the original length was %d.\n",
-			      unit->fcp_lun,
-			      unit->port->wwpn,
-			      zfcp_get_busid_by_unit(unit),
-			      fcp_rsp_iu->fcp_resid,
-			      (int) zfcp_get_fcp_dl(fcp_cmnd_iu));
-	}
-
-	/* check for underrun */
-	if (unlikely(fcp_rsp_iu->validity.bits.fcp_resid_under)) {
-		ZFCP_LOG_INFO("A data underrun was detected for a command. "
-			      "unit 0x%016Lx, port 0x%016Lx, adapter %s. "
-			      "The response data length is "
-			      "%d, the original length was %d.\n",
-			      unit->fcp_lun,
-			      unit->port->wwpn,
-			      zfcp_get_busid_by_unit(unit),
-			      fcp_rsp_iu->fcp_resid,
-			      (int) zfcp_get_fcp_dl(fcp_cmnd_iu));
-
-		scsi_set_resid(scpnt, fcp_rsp_iu->fcp_resid);
-		if (scsi_bufflen(scpnt) - scsi_get_resid(scpnt) <
-		    scpnt->underflow)
-			set_host_byte(&scpnt->result, DID_ERROR);
-	}
-
- skip_fsfstatus:
-	ZFCP_LOG_DEBUG("scpnt->result =0x%x\n", scpnt->result);
-
-	if (scpnt->result != 0)
-		zfcp_scsi_dbf_event_result("erro", 3, fsf_req->adapter, scpnt, fsf_req);
-	else if (scpnt->retries > 0)
-		zfcp_scsi_dbf_event_result("retr", 4, fsf_req->adapter, scpnt, fsf_req);
-	else
-		zfcp_scsi_dbf_event_result("norm", 6, fsf_req->adapter, scpnt, fsf_req);
-
-	/* cleanup pointer (need this especially for abort) */
-	scpnt->host_scribble = NULL;
-
-	/* always call back */
-	(scpnt->scsi_done) (scpnt);
-
-	/*
-	 * We must hold this lock until scsi_done has been called.
-	 * Otherwise we may call scsi_done after abort regarding this
-	 * command has completed.
-	 * Note: scsi_done must not block!
-	 */
- out:
-	read_unlock_irqrestore(&fsf_req->adapter->abort_lock, flags);
-	return retval;
-}
-
-/*
- * function:    zfcp_fsf_send_fcp_command_task_management_handler
- *
- * purpose:	evaluates FCP_RSP IU
- *
- * returns:
- */
-static int
-zfcp_fsf_send_fcp_command_task_management_handler(struct zfcp_fsf_req *fsf_req)
-{
-	int retval = 0;
-	struct fcp_rsp_iu *fcp_rsp_iu = (struct fcp_rsp_iu *)
-	    &(fsf_req->qtcb->bottom.io.fcp_rsp);
-	char *fcp_rsp_info = zfcp_get_fcp_rsp_info_ptr(fcp_rsp_iu);
-	struct zfcp_unit *unit = (struct zfcp_unit *) fsf_req->data;
-
-	if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_TMFUNCFAILED;
-		goto skip_fsfstatus;
-	}
-
-	/* check FCP_RSP_INFO */
-	switch (fcp_rsp_info[3]) {
-	case RSP_CODE_GOOD:
-		/* ok, continue */
-		ZFCP_LOG_DEBUG("no failure or Task Management "
-			       "Function complete\n");
-		break;
-	case RSP_CODE_TASKMAN_UNSUPP:
-		ZFCP_LOG_NORMAL("bug: A reuested task management function "
-				"is not supported on the target device "
-				"unit 0x%016Lx, port 0x%016Lx, adapter %s\n ",
-				unit->fcp_lun,
-				unit->port->wwpn,
-				zfcp_get_busid_by_unit(unit));
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_TMFUNCNOTSUPP;
-		break;
-	case RSP_CODE_TASKMAN_FAILED:
-		ZFCP_LOG_NORMAL("bug: A reuested task management function "
-				"failed to complete successfully. "
-				"unit 0x%016Lx, port 0x%016Lx, adapter %s.\n",
-				unit->fcp_lun,
-				unit->port->wwpn,
-				zfcp_get_busid_by_unit(unit));
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_TMFUNCFAILED;
-		break;
-	default:
-		ZFCP_LOG_NORMAL("bug: An invalid FCP response "
-				"code was detected for a command. "
-				"unit 0x%016Lx, port 0x%016Lx, adapter %s "
-				"(debug info 0x%x)\n",
-				unit->fcp_lun,
-				unit->port->wwpn,
-				zfcp_get_busid_by_unit(unit),
-				fcp_rsp_info[3]);
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_TMFUNCFAILED;
-	}
-
-      skip_fsfstatus:
-	return retval;
-}
-
-
-/*
- * function:    zfcp_fsf_control_file
- *
- * purpose:     Initiator of the control file upload/download FSF requests
- *
- * returns:     0           - FSF request is successfuly created and queued
- *              -EOPNOTSUPP - The FCP adapter does not have Control File support
- *              -EINVAL     - Invalid direction specified
- *              -ENOMEM     - Insufficient memory
- *              -EPERM      - Cannot create FSF request or place it in QDIO queue
- */
-int
-zfcp_fsf_control_file(struct zfcp_adapter *adapter,
-                      struct zfcp_fsf_req **fsf_req_ptr,
-                      u32 fsf_command,
-                      u32 option,
-                      struct zfcp_sg_list *sg_list)
-{
-	struct zfcp_fsf_req *fsf_req;
-	struct fsf_qtcb_bottom_support *bottom;
-	volatile struct qdio_buffer_element *sbale;
-	unsigned long lock_flags;
-	int req_flags = 0;
-	int direction;
-	int retval = 0;
-
-	if (!(adapter->adapter_features & FSF_FEATURE_CFDC)) {
-		ZFCP_LOG_INFO("cfdc not supported (adapter %s)\n",
-			      zfcp_get_busid_by_adapter(adapter));
-		retval = -EOPNOTSUPP;
-		goto out;
-	}
-
-	switch (fsf_command) {
-
-	case FSF_QTCB_DOWNLOAD_CONTROL_FILE:
-		direction = SBAL_FLAGS0_TYPE_WRITE;
-		if ((option != FSF_CFDC_OPTION_FULL_ACCESS) &&
-		    (option != FSF_CFDC_OPTION_RESTRICTED_ACCESS))
-			req_flags = ZFCP_WAIT_FOR_SBAL;
-		break;
-
-	case FSF_QTCB_UPLOAD_CONTROL_FILE:
-		direction = SBAL_FLAGS0_TYPE_READ;
-		break;
-
-	default:
-		ZFCP_LOG_INFO("Invalid FSF command code 0x%08x\n", fsf_command);
-		retval = -EINVAL;
-		goto out;
-	}
-
-	retval = zfcp_fsf_req_create(adapter, fsf_command, req_flags,
-				     NULL, &lock_flags, &fsf_req);
-	if (retval < 0) {
-		ZFCP_LOG_INFO("error: Could not create FSF request for the "
-			      "adapter %s\n",
-			zfcp_get_busid_by_adapter(adapter));
-		retval = -EPERM;
-		goto unlock_queue_lock;
-	}
-
-	sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
-	sbale[0].flags |= direction;
-
-	bottom = &fsf_req->qtcb->bottom.support;
-	bottom->operation_subtype = FSF_CFDC_OPERATION_SUBTYPE;
-	bottom->option = option;
-
-	if (sg_list->count > 0) {
-		int bytes;
-
-		bytes = zfcp_qdio_sbals_from_sg(fsf_req, direction,
-						sg_list->sg, sg_list->count,
-						ZFCP_MAX_SBALS_PER_REQ);
-                if (bytes != ZFCP_CFDC_MAX_CONTROL_FILE_SIZE) {
-			ZFCP_LOG_INFO(
-				"error: Could not create sufficient number of "
-				"SBALS for an FSF request to the adapter %s\n",
-				zfcp_get_busid_by_adapter(adapter));
-			retval = -ENOMEM;
-			goto free_fsf_req;
-		}
-	} else
-		sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
-
-	zfcp_fsf_start_timer(fsf_req, ZFCP_FSF_REQUEST_TIMEOUT);
-	retval = zfcp_fsf_req_send(fsf_req);
-	if (retval < 0) {
-		ZFCP_LOG_INFO("initiation of cfdc up/download failed"
-			      "(adapter %s)\n",
-			      zfcp_get_busid_by_adapter(adapter));
-		retval = -EPERM;
-		goto free_fsf_req;
-	}
-	write_unlock_irqrestore(&adapter->request_queue.queue_lock, lock_flags);
-
-	ZFCP_LOG_NORMAL("Control file %s FSF request has been sent to the "
-			"adapter %s\n",
-			fsf_command == FSF_QTCB_DOWNLOAD_CONTROL_FILE ?
-			"download" : "upload",
-			zfcp_get_busid_by_adapter(adapter));
-
-	wait_event(fsf_req->completion_wq,
-	           fsf_req->status & ZFCP_STATUS_FSFREQ_COMPLETED);
+	retval = zfcp_fsf_req_send(req);
+	if (unlikely(retval))
+		goto failed_scsi_cmnd;
 
-	*fsf_req_ptr = fsf_req;
 	goto out;
 
- free_fsf_req:
-	zfcp_fsf_req_free(fsf_req);
- unlock_queue_lock:
-	write_unlock_irqrestore(&adapter->request_queue.queue_lock, lock_flags);
- out:
+failed_scsi_cmnd:
+	zfcp_unit_put(unit);
+	zfcp_fsf_req_free(req);
+	scsi_cmnd->host_scribble = NULL;
+out:
+	spin_unlock(&adapter->req_q.lock);
 	return retval;
 }
 
-
-/*
- * function:    zfcp_fsf_control_file_handler
- *
- * purpose:     Handler of the control file upload/download FSF requests
- *
- * returns:     0       - FSF request successfuly processed
- *              -EAGAIN - Operation has to be repeated because of a temporary problem
- *              -EACCES - There is no permission to execute an operation
- *              -EPERM  - The control file is not in a right format
- *              -EIO    - There is a problem with the FCP adapter
- *              -EINVAL - Invalid operation
- *              -EFAULT - User space memory I/O operation fault
- */
-static int
-zfcp_fsf_control_file_handler(struct zfcp_fsf_req *fsf_req)
-{
-	struct zfcp_adapter *adapter = fsf_req->adapter;
-	struct fsf_qtcb_header *header = &fsf_req->qtcb->header;
-	struct fsf_qtcb_bottom_support *bottom = &fsf_req->qtcb->bottom.support;
-	int retval = 0;
-
-	if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
-		retval = -EINVAL;
-		goto skip_fsfstatus;
-	}
-
-	switch (header->fsf_status) {
-
-	case FSF_GOOD:
-		ZFCP_LOG_NORMAL(
-			"The FSF request has been successfully completed "
-			"on the adapter %s\n",
-			zfcp_get_busid_by_adapter(adapter));
-		break;
-
-	case FSF_OPERATION_PARTIALLY_SUCCESSFUL:
-		if (bottom->operation_subtype == FSF_CFDC_OPERATION_SUBTYPE) {
-			switch (header->fsf_status_qual.word[0]) {
-
-			case FSF_SQ_CFDC_HARDENED_ON_SE:
-				ZFCP_LOG_NORMAL(
-					"CFDC on the adapter %s has being "
-					"hardened on primary and secondary SE\n",
-					zfcp_get_busid_by_adapter(adapter));
-				break;
-
-			case FSF_SQ_CFDC_COULD_NOT_HARDEN_ON_SE:
-				ZFCP_LOG_NORMAL(
-					"CFDC of the adapter %s could not "
-					"be saved on the SE\n",
-					zfcp_get_busid_by_adapter(adapter));
-				break;
-
-			case FSF_SQ_CFDC_COULD_NOT_HARDEN_ON_SE2:
-				ZFCP_LOG_NORMAL(
-					"CFDC of the adapter %s could not "
-					"be copied to the secondary SE\n",
-					zfcp_get_busid_by_adapter(adapter));
-				break;
-
-			default:
-				ZFCP_LOG_NORMAL(
-					"CFDC could not be hardened "
-					"on the adapter %s\n",
-					zfcp_get_busid_by_adapter(adapter));
-			}
-		}
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-		retval = -EAGAIN;
-		break;
-
-	case FSF_AUTHORIZATION_FAILURE:
-		ZFCP_LOG_NORMAL(
-			"Adapter %s does not accept privileged commands\n",
-			zfcp_get_busid_by_adapter(adapter));
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-		retval = -EACCES;
-		break;
-
-	case FSF_CFDC_ERROR_DETECTED:
-		ZFCP_LOG_NORMAL(
-			"Error at position %d in the CFDC, "
-			"CFDC is discarded by the adapter %s\n",
-			header->fsf_status_qual.word[0],
-			zfcp_get_busid_by_adapter(adapter));
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-		retval = -EPERM;
-		break;
-
-	case FSF_CONTROL_FILE_UPDATE_ERROR:
-		ZFCP_LOG_NORMAL(
-			"Adapter %s cannot harden the control file, "
-			"file is discarded\n",
-			zfcp_get_busid_by_adapter(adapter));
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-		retval = -EIO;
-		break;
-
-	case FSF_CONTROL_FILE_TOO_LARGE:
-		ZFCP_LOG_NORMAL(
-			"Control file is too large, file is discarded "
-			"by the adapter %s\n",
-			zfcp_get_busid_by_adapter(adapter));
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-		retval = -EIO;
-		break;
-
-	case FSF_ACCESS_CONFLICT_DETECTED:
-		if (bottom->operation_subtype == FSF_CFDC_OPERATION_SUBTYPE)
-			ZFCP_LOG_NORMAL(
-				"CFDC has been discarded by the adapter %s, "
-				"because activation would impact "
-				"%d active connection(s)\n",
-				zfcp_get_busid_by_adapter(adapter),
-				header->fsf_status_qual.word[0]);
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-		retval = -EIO;
-		break;
-
-	case FSF_CONFLICTS_OVERRULED:
-		if (bottom->operation_subtype == FSF_CFDC_OPERATION_SUBTYPE)
-			ZFCP_LOG_NORMAL(
-				"CFDC has been activated on the adapter %s, "
-				"but activation has impacted "
-				"%d active connection(s)\n",
-				zfcp_get_busid_by_adapter(adapter),
-				header->fsf_status_qual.word[0]);
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-		retval = -EIO;
-		break;
-
-	case FSF_UNKNOWN_OP_SUBTYPE:
-		ZFCP_LOG_NORMAL("unknown operation subtype (adapter: %s, "
-				"op_subtype=0x%x)\n",
-				zfcp_get_busid_by_adapter(adapter),
-				bottom->operation_subtype);
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-		retval = -EINVAL;
-		break;
-
-	case FSF_INVALID_COMMAND_OPTION:
-		ZFCP_LOG_NORMAL(
-			"Invalid option 0x%x has been specified "
-			"in QTCB bottom sent to the adapter %s\n",
-			bottom->option,
-			zfcp_get_busid_by_adapter(adapter));
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-		retval = -EINVAL;
-		break;
-
-	default:
-		ZFCP_LOG_NORMAL(
-			"bug: An unknown/unexpected FSF status 0x%08x "
-			"was presented on the adapter %s\n",
-			header->fsf_status,
-			zfcp_get_busid_by_adapter(adapter));
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-		retval = -EINVAL;
-		break;
-	}
-
-skip_fsfstatus:
-	return retval;
-}
-
-static inline int
-zfcp_fsf_req_sbal_check(unsigned long *flags,
-			struct zfcp_qdio_queue *queue, int needed)
-{
-	write_lock_irqsave(&queue->queue_lock, *flags);
-	if (likely(atomic_read(&queue->free_count) >= needed))
-		return 1;
-	write_unlock_irqrestore(&queue->queue_lock, *flags);
-	return 0;
-}
-
-/*
- * set qtcb pointer in fsf_req and initialize QTCB
- */
-static void
-zfcp_fsf_req_qtcb_init(struct zfcp_fsf_req *fsf_req)
-{
-	if (likely(fsf_req->qtcb != NULL)) {
-		fsf_req->qtcb->prefix.req_seq_no =
-			fsf_req->adapter->fsf_req_seq_no;
-		fsf_req->qtcb->prefix.req_id = fsf_req->req_id;
-		fsf_req->qtcb->prefix.ulp_info = ZFCP_ULP_INFO_VERSION;
-		fsf_req->qtcb->prefix.qtcb_type =
-			fsf_qtcb_type[fsf_req->fsf_command];
-		fsf_req->qtcb->prefix.qtcb_version = ZFCP_QTCB_VERSION;
-		fsf_req->qtcb->header.req_handle = fsf_req->req_id;
-		fsf_req->qtcb->header.fsf_command = fsf_req->fsf_command;
-	}
-}
-
 /**
- * zfcp_fsf_req_sbal_get - try to get one SBAL in the request queue
- * @adapter: adapter for which request queue is examined
- * @req_flags: flags indicating whether to wait for needed SBAL or not
- * @lock_flags: lock_flags if queue_lock is taken
- * Return: 0 on success, otherwise -EIO, or -ERESTARTSYS
- * Locks: lock adapter->request_queue->queue_lock on success
+ * zfcp_fsf_send_fcp_ctm - send SCSI task management command
+ * @adapter: pointer to struct zfcp-adapter
+ * @unit: pointer to struct zfcp_unit
+ * @tm_flags: unsigned byte for task management flags
+ * @req_flags: int request flags
+ * Returns: on success pointer to struct fsf_req, NULL otherwise
  */
-static int
-zfcp_fsf_req_sbal_get(struct zfcp_adapter *adapter, int req_flags,
-		      unsigned long *lock_flags)
-{
-        long ret;
-        struct zfcp_qdio_queue *req_queue = &adapter->request_queue;
-
-        if (unlikely(req_flags & ZFCP_WAIT_FOR_SBAL)) {
-                ret = wait_event_interruptible_timeout(adapter->request_wq,
-			zfcp_fsf_req_sbal_check(lock_flags, req_queue, 1),
-						       ZFCP_SBAL_TIMEOUT);
-		if (ret < 0)
-			return ret;
-		if (!ret)
-			return -EIO;
-        } else if (!zfcp_fsf_req_sbal_check(lock_flags, req_queue, 1))
-                return -EIO;
-
-        return 0;
-}
-
-/*
- * function:    zfcp_fsf_req_create
- *
- * purpose:	create an FSF request at the specified adapter and
- *		setup common fields
- *
- * returns:	-ENOMEM if there was insufficient memory for a request
- *              -EIO if no qdio buffers could be allocate to the request
- *              -EINVAL/-EPERM on bug conditions in req_dequeue
- *              0 in success
- *
- * note:        The created request is returned by reference.
- *
- * locks:	lock of concerned request queue must not be held,
- *		but is held on completion (write, irqsave)
- */
-int
-zfcp_fsf_req_create(struct zfcp_adapter *adapter, u32 fsf_cmd, int req_flags,
-		    mempool_t *pool, unsigned long *lock_flags,
-		    struct zfcp_fsf_req **fsf_req_p)
+struct zfcp_fsf_req *zfcp_fsf_send_fcp_ctm(struct zfcp_adapter *adapter,
+					   struct zfcp_unit *unit,
+					   u8 tm_flags, int req_flags)
 {
 	volatile struct qdio_buffer_element *sbale;
-	struct zfcp_fsf_req *fsf_req = NULL;
-	int ret = 0;
-	struct zfcp_qdio_queue *req_queue = &adapter->request_queue;
+	struct zfcp_fsf_req *req = NULL;
+	struct fcp_cmnd_iu *fcp_cmnd_iu;
 
-	/* allocate new FSF request */
-	fsf_req = zfcp_fsf_req_alloc(pool, req_flags);
-	if (unlikely(NULL == fsf_req)) {
-		ZFCP_LOG_DEBUG("error: Could not put an FSF request into "
-			       "the outbound (send) queue.\n");
-		ret = -ENOMEM;
-		goto failed_fsf_req;
-	}
+	if (unlikely(!(atomic_read(&unit->status) &
+		       ZFCP_STATUS_COMMON_UNBLOCKED)))
+		return NULL;
 
-	fsf_req->adapter = adapter;
-	fsf_req->fsf_command = fsf_cmd;
-	INIT_LIST_HEAD(&fsf_req->list);
-	init_timer(&fsf_req->timer);
+	spin_lock(&adapter->req_q.lock);
+	if (!atomic_read(&adapter->req_q.count))
+		goto out;
+	req = zfcp_fsf_req_create(adapter, FSF_QTCB_FCP_CMND, req_flags,
+				  adapter->pool.fsf_req_scsi);
+	if (unlikely(IS_ERR(req)))
+		goto out;
 
-	/* initialize waitqueue which may be used to wait on
-	   this request completion */
-	init_waitqueue_head(&fsf_req->completion_wq);
+	req->status |= ZFCP_STATUS_FSFREQ_TASK_MANAGEMENT;
+	req->data = unit;
+	req->handler = zfcp_fsf_send_fcp_command_handler;
+	req->qtcb->header.lun_handle = unit->handle;
+	req->qtcb->header.port_handle = unit->port->handle;
+	req->qtcb->bottom.io.data_direction = FSF_DATADIR_CMND;
+	req->qtcb->bottom.io.service_class = FSF_CLASS_3;
+	req->qtcb->bottom.io.fcp_cmnd_length = 	sizeof(struct fcp_cmnd_iu) +
+						sizeof(fcp_dl_t);
 
-        ret = zfcp_fsf_req_sbal_get(adapter, req_flags, lock_flags);
-        if (ret < 0)
-                goto failed_sbals;
+	sbale = zfcp_qdio_sbale_req(req);
+	sbale[0].flags |= SBAL_FLAGS0_TYPE_WRITE;
+	sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
 
-	/* this is serialized (we are holding req_queue-lock of adapter) */
-	if (adapter->req_no == 0)
-		adapter->req_no++;
-	fsf_req->req_id = adapter->req_no++;
+	fcp_cmnd_iu = (struct fcp_cmnd_iu *) &req->qtcb->bottom.io.fcp_cmnd;
+	fcp_cmnd_iu->fcp_lun = unit->fcp_lun;
+	fcp_cmnd_iu->task_management_flags = tm_flags;
 
-	zfcp_fsf_req_qtcb_init(fsf_req);
+	zfcp_fsf_start_timer(req, ZFCP_SCSI_ER_TIMEOUT);
+	if (!zfcp_fsf_req_send(req))
+		goto out;
 
-	/*
-	 * We hold queue_lock here. Check if QDIOUP is set and let request fail
-	 * if it is not set (see also *_open_qdio and *_close_qdio).
-	 */
-
-	if (!atomic_test_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &adapter->status)) {
-		write_unlock_irqrestore(&req_queue->queue_lock, *lock_flags);
-		ret = -EIO;
-		goto failed_sbals;
-	}
-
-	if (fsf_req->qtcb) {
-		fsf_req->seq_no = adapter->fsf_req_seq_no;
-		fsf_req->qtcb->prefix.req_seq_no = adapter->fsf_req_seq_no;
-	}
-	fsf_req->sbal_number = 1;
-	fsf_req->sbal_first = req_queue->free_index;
-	fsf_req->sbal_curr = req_queue->free_index;
-        fsf_req->sbale_curr = 1;
-
-	if (likely(req_flags & ZFCP_REQ_AUTO_CLEANUP)) {
-		fsf_req->status |= ZFCP_STATUS_FSFREQ_CLEANUP;
-	}
-
-	sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
-
-	/* setup common SBALE fields */
-	sbale[0].addr = (void *) fsf_req->req_id;
-	sbale[0].flags |= SBAL_FLAGS0_COMMAND;
-	if (likely(fsf_req->qtcb != NULL)) {
-		sbale[1].addr = (void *) fsf_req->qtcb;
-		sbale[1].length = sizeof(struct fsf_qtcb);
-	}
-
-	ZFCP_LOG_TRACE("got %i free BUFFERs starting at index %i\n",
-                       fsf_req->sbal_number, fsf_req->sbal_first);
-
-	goto success;
-
- failed_sbals:
-/* dequeue new FSF request previously enqueued */
-	zfcp_fsf_req_free(fsf_req);
-	fsf_req = NULL;
-
- failed_fsf_req:
-	write_lock_irqsave(&req_queue->queue_lock, *lock_flags);
- success:
-	*fsf_req_p = fsf_req;
-	return ret;
+	zfcp_fsf_req_free(req);
+	req = NULL;
+out:
+	spin_unlock(&adapter->req_q.lock);
+	return req;
 }
 
-/*
- * function:    zfcp_fsf_req_send
- *
- * purpose:	start transfer of FSF request via QDIO
- *
- * returns:	0 - request transfer succesfully started
- *		!0 - start of request transfer failed
- */
-static int zfcp_fsf_req_send(struct zfcp_fsf_req *fsf_req)
+static void zfcp_fsf_control_file_handler(struct zfcp_fsf_req *req)
 {
-	struct zfcp_adapter *adapter;
-	struct zfcp_qdio_queue *req_queue;
-	volatile struct qdio_buffer_element *sbale;
-	int inc_seq_no;
-	int new_distance_from_int;
-	int retval = 0;
-
-	adapter = fsf_req->adapter;
-	req_queue = &adapter->request_queue,
-
-
-	/* FIXME(debug): remove it later */
-	sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_first, 0);
-	ZFCP_LOG_DEBUG("SBALE0 flags=0x%x\n", sbale[0].flags);
-	ZFCP_LOG_TRACE("HEX DUMP OF SBALE1 PAYLOAD:\n");
-	ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_TRACE, (char *) sbale[1].addr,
-		      sbale[1].length);
-
-	/* put allocated FSF request into hash table */
-	spin_lock(&adapter->req_list_lock);
-	zfcp_reqlist_add(adapter, fsf_req);
-	spin_unlock(&adapter->req_list_lock);
-
-	inc_seq_no = (fsf_req->qtcb != NULL);
-
-	ZFCP_LOG_TRACE("request queue of adapter %s: "
-		       "next free SBAL is %i, %i free SBALs\n",
-		       zfcp_get_busid_by_adapter(adapter),
-		       req_queue->free_index,
-		       atomic_read(&req_queue->free_count));
-
-	ZFCP_LOG_DEBUG("calling do_QDIO adapter %s, flags=0x%x, queue_no=%i, "
-		       "index_in_queue=%i, count=%i, buffers=%p\n",
-		       zfcp_get_busid_by_adapter(adapter),
-		       QDIO_FLAG_SYNC_OUTPUT,
-		       0, fsf_req->sbal_first, fsf_req->sbal_number,
-		       &req_queue->buffer[fsf_req->sbal_first]);
-
-	/*
-	 * adjust the number of free SBALs in request queue as well as
-	 * position of first one
-	 */
-	atomic_sub(fsf_req->sbal_number, &req_queue->free_count);
-	ZFCP_LOG_TRACE("free_count=%d\n", atomic_read(&req_queue->free_count));
-	req_queue->free_index += fsf_req->sbal_number;	  /* increase */
-	req_queue->free_index %= QDIO_MAX_BUFFERS_PER_Q;  /* wrap if needed */
-	new_distance_from_int = zfcp_qdio_determine_pci(req_queue, fsf_req);
-
-	fsf_req->issued = get_clock();
-
-	retval = do_QDIO(adapter->ccw_device,
-			 QDIO_FLAG_SYNC_OUTPUT,
-			 0, fsf_req->sbal_first, fsf_req->sbal_number, NULL);
-
-	if (unlikely(retval)) {
-		/* Queues are down..... */
-		retval = -EIO;
-		del_timer(&fsf_req->timer);
-		spin_lock(&adapter->req_list_lock);
-		zfcp_reqlist_remove(adapter, fsf_req);
-		spin_unlock(&adapter->req_list_lock);
-		/* undo changes in request queue made for this request */
-		zfcp_qdio_zero_sbals(req_queue->buffer,
-				     fsf_req->sbal_first, fsf_req->sbal_number);
-		atomic_add(fsf_req->sbal_number, &req_queue->free_count);
-		req_queue->free_index -= fsf_req->sbal_number;
-		req_queue->free_index += QDIO_MAX_BUFFERS_PER_Q;
-		req_queue->free_index %= QDIO_MAX_BUFFERS_PER_Q; /* wrap */
-		zfcp_erp_adapter_reopen(adapter, 0, 116, fsf_req);
-	} else {
-		req_queue->distance_from_int = new_distance_from_int;
-		/*
-		 * increase FSF sequence counter -
-		 * this must only be done for request successfully enqueued to
-		 * QDIO this rejected requests may be cleaned up by calling
-		 * routines  resulting in missing sequence counter values
-		 * otherwise,
-		 */
-
-		/* Don't increase for unsolicited status */
-		if (inc_seq_no)
-			adapter->fsf_req_seq_no++;
-
-		/* count FSF requests pending */
-		atomic_inc(&adapter->reqs_active);
-	}
-	return retval;
+	if (req->qtcb->header.fsf_status != FSF_GOOD)
+		req->status |= ZFCP_STATUS_FSFREQ_ERROR;
 }
 
-#undef ZFCP_LOG_AREA
+/**
+ * zfcp_fsf_control_file - control file upload/download
+ * @adapter: pointer to struct zfcp_adapter
+ * @fsf_cfdc: pointer to struct zfcp_fsf_cfdc
+ * Returns: on success pointer to struct zfcp_fsf_req, NULL otherwise
+ */
+struct zfcp_fsf_req *zfcp_fsf_control_file(struct zfcp_adapter *adapter,
+					   struct zfcp_fsf_cfdc *fsf_cfdc)
+{
+	volatile struct qdio_buffer_element *sbale;
+	struct zfcp_fsf_req *req = NULL;
+	struct fsf_qtcb_bottom_support *bottom;
+	int direction, retval = -EIO, bytes;
+
+	if (!(adapter->adapter_features & FSF_FEATURE_CFDC))
+		return ERR_PTR(-EOPNOTSUPP);
+
+	switch (fsf_cfdc->command) {
+	case FSF_QTCB_DOWNLOAD_CONTROL_FILE:
+		direction = SBAL_FLAGS0_TYPE_WRITE;
+		break;
+	case FSF_QTCB_UPLOAD_CONTROL_FILE:
+		direction = SBAL_FLAGS0_TYPE_READ;
+		break;
+	default:
+		return ERR_PTR(-EINVAL);
+	}
+
+	spin_lock(&adapter->req_q.lock);
+	if (zfcp_fsf_req_sbal_get(adapter))
+		goto out;
+
+	req = zfcp_fsf_req_create(adapter, fsf_cfdc->command, 0, NULL);
+	if (unlikely(IS_ERR(req))) {
+		retval = -EPERM;
+		goto out;
+	}
+
+	req->handler = zfcp_fsf_control_file_handler;
+
+	sbale = zfcp_qdio_sbale_req(req);
+	sbale[0].flags |= direction;
+
+	bottom = &req->qtcb->bottom.support;
+	bottom->operation_subtype = FSF_CFDC_OPERATION_SUBTYPE;
+	bottom->option = fsf_cfdc->option;
+
+	bytes = zfcp_qdio_sbals_from_sg(req, direction, fsf_cfdc->sg,
+					FSF_MAX_SBALS_PER_REQ);
+	if (bytes != ZFCP_CFDC_MAX_SIZE) {
+		retval = -ENOMEM;
+		zfcp_fsf_req_free(req);
+		goto out;
+	}
+
+	zfcp_fsf_start_timer(req, ZFCP_FSF_REQUEST_TIMEOUT);
+	retval = zfcp_fsf_req_send(req);
+out:
+	spin_unlock(&adapter->req_q.lock);
+
+	if (!retval) {
+		wait_event(req->completion_wq,
+			   req->status & ZFCP_STATUS_FSFREQ_COMPLETED);
+		return req;
+	}
+	return ERR_PTR(retval);
+}
diff --git a/drivers/s390/scsi/zfcp_fsf.h b/drivers/s390/scsi/zfcp_fsf.h
index 099970b..bf94b4d 100644
--- a/drivers/s390/scsi/zfcp_fsf.h
+++ b/drivers/s390/scsi/zfcp_fsf.h
@@ -1,27 +1,16 @@
 /*
- * This file is part of the zfcp device driver for
- * FCP adapters for IBM System z9 and zSeries.
+ * zfcp device driver
  *
- * (C) Copyright IBM Corp. 2002, 2006
+ * Interface to the FSF support functions.
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * Copyright IBM Corporation 2002, 2008
  */
 
 #ifndef FSF_H
 #define FSF_H
 
+#include <linux/pfn.h>
+
 #define FSF_QTCB_CURRENT_VERSION		0x00000001
 
 /* FSF commands */
@@ -258,6 +247,16 @@
 #define FSF_UNIT_ACCESS_EXCLUSIVE		0x02000000
 #define FSF_UNIT_ACCESS_OUTBOUND_TRANSFER	0x10000000
 
+/* FSF interface for CFDC */
+#define ZFCP_CFDC_MAX_SIZE		127 * 1024
+#define ZFCP_CFDC_PAGES 		PFN_UP(ZFCP_CFDC_MAX_SIZE)
+
+struct zfcp_fsf_cfdc {
+	struct scatterlist sg[ZFCP_CFDC_PAGES];
+	u32 command;
+	u32 option;
+};
+
 struct fsf_queue_designator {
 	u8  cssid;
 	u8  chpid;
@@ -288,29 +287,6 @@
 	u32 current_transmit_b2b_credit;
 } __attribute__ ((packed));
 
-struct fsf_status_read_buffer {
-	u32 status_type;
-	u32 status_subtype;
-	u32 length;
-	u32 res1;
-	struct fsf_queue_designator queue_designator;
-	u32 d_id;
-	u32 class;
-	u64 fcp_lun;
-	u8  res3[24];
-	u8  payload[FSF_STATUS_READ_PAYLOAD_SIZE];
-} __attribute__ ((packed));
-
-struct fsf_qual_version_error {
-	u32 fsf_version;
-	u32 res1[3];
-} __attribute__ ((packed));
-
-struct fsf_qual_sequence_error {
-	u32 exp_req_seq_no;
-	u32 res1[3];
-} __attribute__ ((packed));
-
 struct fsf_link_down_info {
 	u32 error_code;
 	u32 res1;
@@ -323,11 +299,47 @@
 	u8 vendor_specific_code;
 } __attribute__ ((packed));
 
+struct fsf_status_read_buffer {
+	u32 status_type;
+	u32 status_subtype;
+	u32 length;
+	u32 res1;
+	struct fsf_queue_designator queue_designator;
+	u32 d_id;
+	u32 class;
+	u64 fcp_lun;
+	u8  res3[24];
+	union {
+		u8  data[FSF_STATUS_READ_PAYLOAD_SIZE];
+		u32 word[FSF_STATUS_READ_PAYLOAD_SIZE/sizeof(u32)];
+		struct fsf_link_down_info link_down_info;
+		struct fsf_bit_error_payload bit_error;
+	} payload;
+} __attribute__ ((packed));
+
+struct fsf_qual_version_error {
+	u32 fsf_version;
+	u32 res1[3];
+} __attribute__ ((packed));
+
+struct fsf_qual_sequence_error {
+	u32 exp_req_seq_no;
+	u32 res1[3];
+} __attribute__ ((packed));
+
+struct fsf_qual_latency_info {
+	u32 channel_lat;
+	u32 fabric_lat;
+	u8 res1[8];
+} __attribute__ ((packed));
+
 union fsf_prot_status_qual {
+	u32 word[FSF_PROT_STATUS_QUAL_SIZE / sizeof(u32)];
 	u64 doubleword[FSF_PROT_STATUS_QUAL_SIZE / sizeof(u64)];
 	struct fsf_qual_version_error   version_error;
 	struct fsf_qual_sequence_error  sequence_error;
 	struct fsf_link_down_info link_down_info;
+	struct fsf_qual_latency_info latency_info;
 } __attribute__ ((packed));
 
 struct fsf_qtcb_prefix {
@@ -437,7 +449,9 @@
 	u32 fc_link_speed;
 	u32 adapter_type;
 	u32 peer_d_id;
-	u8 res2[12];
+	u8 res1[2];
+	u16 timer_interval;
+	u8 res2[8];
 	u32 s_id;
 	struct fsf_nport_serv_param nport_serv_param;
 	u8 reserved_nport_serv_param[16];
diff --git a/drivers/s390/scsi/zfcp_qdio.c b/drivers/s390/scsi/zfcp_qdio.c
index 8ca5f07..72e3094 100644
--- a/drivers/s390/scsi/zfcp_qdio.c
+++ b/drivers/s390/scsi/zfcp_qdio.c
@@ -1,241 +1,103 @@
 /*
- * This file is part of the zfcp device driver for
- * FCP adapters for IBM System z9 and zSeries.
+ * zfcp device driver
  *
- * (C) Copyright IBM Corp. 2002, 2006
+ * Setup and helper functions to access QDIO.
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * Copyright IBM Corporation 2002, 2008
  */
 
 #include "zfcp_ext.h"
 
-static void zfcp_qdio_sbal_limit(struct zfcp_fsf_req *, int);
-static inline volatile struct qdio_buffer_element *zfcp_qdio_sbale_get
-	(struct zfcp_qdio_queue *, int, int);
-static inline volatile struct qdio_buffer_element *zfcp_qdio_sbale_resp
-	(struct zfcp_fsf_req *, int, int);
-static volatile struct qdio_buffer_element *zfcp_qdio_sbal_chain
-	(struct zfcp_fsf_req *, unsigned long);
-static volatile struct qdio_buffer_element *zfcp_qdio_sbale_next
-	(struct zfcp_fsf_req *, unsigned long);
-static int zfcp_qdio_sbals_zero(struct zfcp_qdio_queue *, int, int);
-static inline int zfcp_qdio_sbals_wipe(struct zfcp_fsf_req *);
-static void zfcp_qdio_sbale_fill
-	(struct zfcp_fsf_req *, unsigned long, void *, int);
-static int zfcp_qdio_sbals_from_segment
-	(struct zfcp_fsf_req *, unsigned long, void *, unsigned long);
+/* FIXME(tune): free space should be one max. SBAL chain plus what? */
+#define ZFCP_QDIO_PCI_INTERVAL	(QDIO_MAX_BUFFERS_PER_Q \
+				- (FSF_MAX_SBALS_PER_REQ + 4))
+#define QBUFF_PER_PAGE		(PAGE_SIZE / sizeof(struct qdio_buffer))
 
-static qdio_handler_t zfcp_qdio_request_handler;
-static qdio_handler_t zfcp_qdio_response_handler;
-static int zfcp_qdio_handler_error_check(struct zfcp_adapter *,
-	unsigned int, unsigned int, unsigned int, int, int);
-
-#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_QDIO
-
-/*
- * Frees BUFFER memory for each of the pointers of the struct qdio_buffer array
- * in the adapter struct sbuf is the pointer array.
- *
- * locks:       must only be called with zfcp_data.config_sema taken
- */
-static void
-zfcp_qdio_buffers_dequeue(struct qdio_buffer **sbuf)
-{
-	int pos;
-
-	for (pos = 0; pos < QDIO_MAX_BUFFERS_PER_Q; pos += QBUFF_PER_PAGE)
-		free_page((unsigned long) sbuf[pos]);
-}
-
-/*
- * Allocates BUFFER memory to each of the pointers of the qdio_buffer_t
- * array in the adapter struct.
- * Cur_buf is the pointer array
- *
- * returns:	zero on success else -ENOMEM
- * locks:       must only be called with zfcp_data.config_sema taken
- */
-static int
-zfcp_qdio_buffers_enqueue(struct qdio_buffer **sbuf)
+static int zfcp_qdio_buffers_enqueue(struct qdio_buffer **sbal)
 {
 	int pos;
 
 	for (pos = 0; pos < QDIO_MAX_BUFFERS_PER_Q; pos += QBUFF_PER_PAGE) {
-		sbuf[pos] = (struct qdio_buffer *) get_zeroed_page(GFP_KERNEL);
-		if (!sbuf[pos]) {
-			zfcp_qdio_buffers_dequeue(sbuf);
+		sbal[pos] = (struct qdio_buffer *) get_zeroed_page(GFP_KERNEL);
+		if (!sbal[pos])
 			return -ENOMEM;
-		}
 	}
 	for (pos = 0; pos < QDIO_MAX_BUFFERS_PER_Q; pos++)
 		if (pos % QBUFF_PER_PAGE)
-			sbuf[pos] = sbuf[pos - 1] + 1;
+			sbal[pos] = sbal[pos - 1] + 1;
 	return 0;
 }
 
-/* locks:       must only be called with zfcp_data.config_sema taken */
-int
-zfcp_qdio_allocate_queues(struct zfcp_adapter *adapter)
+static volatile struct qdio_buffer_element *
+zfcp_qdio_sbale(struct zfcp_qdio_queue *q, int sbal_idx, int sbale_idx)
 {
-	int ret;
-
-	ret = zfcp_qdio_buffers_enqueue(adapter->request_queue.buffer);
-	if (ret)
-		return ret;
-	return zfcp_qdio_buffers_enqueue(adapter->response_queue.buffer);
-}
-
-/* locks:       must only be called with zfcp_data.config_sema taken */
-void
-zfcp_qdio_free_queues(struct zfcp_adapter *adapter)
-{
-	ZFCP_LOG_TRACE("freeing request_queue buffers\n");
-	zfcp_qdio_buffers_dequeue(adapter->request_queue.buffer);
-
-	ZFCP_LOG_TRACE("freeing response_queue buffers\n");
-	zfcp_qdio_buffers_dequeue(adapter->response_queue.buffer);
-}
-
-int
-zfcp_qdio_allocate(struct zfcp_adapter *adapter)
-{
-	struct qdio_initialize *init_data;
-
-	init_data = &adapter->qdio_init_data;
-
-	init_data->cdev = adapter->ccw_device;
-	init_data->q_format = QDIO_SCSI_QFMT;
-	memcpy(init_data->adapter_name, zfcp_get_busid_by_adapter(adapter), 8);
-	ASCEBC(init_data->adapter_name, 8);
-	init_data->qib_param_field_format = 0;
-	init_data->qib_param_field = NULL;
-	init_data->input_slib_elements = NULL;
-	init_data->output_slib_elements = NULL;
-	init_data->min_input_threshold = ZFCP_MIN_INPUT_THRESHOLD;
-	init_data->max_input_threshold = ZFCP_MAX_INPUT_THRESHOLD;
-	init_data->min_output_threshold = ZFCP_MIN_OUTPUT_THRESHOLD;
-	init_data->max_output_threshold = ZFCP_MAX_OUTPUT_THRESHOLD;
-	init_data->no_input_qs = 1;
-	init_data->no_output_qs = 1;
-	init_data->input_handler = zfcp_qdio_response_handler;
-	init_data->output_handler = zfcp_qdio_request_handler;
-	init_data->int_parm = (unsigned long) adapter;
-	init_data->flags = QDIO_INBOUND_0COPY_SBALS |
-	    QDIO_OUTBOUND_0COPY_SBALS | QDIO_USE_OUTBOUND_PCIS;
-	init_data->input_sbal_addr_array =
-	    (void **) (adapter->response_queue.buffer);
-	init_data->output_sbal_addr_array =
-	    (void **) (adapter->request_queue.buffer);
-
-	return qdio_allocate(init_data);
-}
-
-/*
- * function:   	zfcp_qdio_handler_error_check
- *
- * purpose:     called by the response handler to determine error condition
- *
- * returns:	error flag
- *
- */
-static int
-zfcp_qdio_handler_error_check(struct zfcp_adapter *adapter, unsigned int status,
-			      unsigned int qdio_error, unsigned int siga_error,
-			      int first_element, int elements_processed)
-{
-	int retval = 0;
-
-	if (unlikely(status & QDIO_STATUS_LOOK_FOR_ERROR)) {
-		retval = -EIO;
-
-		ZFCP_LOG_INFO("QDIO problem occurred (status=0x%x, "
-			      "qdio_error=0x%x, siga_error=0x%x)\n",
-			      status, qdio_error, siga_error);
-
-		zfcp_hba_dbf_event_qdio(adapter, status, qdio_error, siga_error,
-				first_element, elements_processed);
-               /*
-               	* Restarting IO on the failed adapter from scratch.
-                * Since we have been using this adapter, it is save to assume
-                * that it is not failed but recoverable. The card seems to
-                * report link-up events by self-initiated queue shutdown.
-                * That is why we need to clear the link-down flag
-                * which is set again in case we have missed by a mile.
-                */
-		zfcp_erp_adapter_reopen(adapter,
-					ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED |
-					ZFCP_STATUS_COMMON_ERP_FAILED, 140,
-					NULL);
-	}
-	return retval;
-}
-
-/*
- * function:    zfcp_qdio_request_handler
- *
- * purpose:	is called by QDIO layer for completed SBALs in request queue
- *
- * returns:	(void)
- */
-static void
-zfcp_qdio_request_handler(struct ccw_device *ccw_device,
-			  unsigned int status,
-			  unsigned int qdio_error,
-			  unsigned int siga_error,
-			  unsigned int queue_number,
-			  int first_element,
-			  int elements_processed,
-			  unsigned long int_parm)
-{
-	struct zfcp_adapter *adapter;
-	struct zfcp_qdio_queue *queue;
-
-	adapter = (struct zfcp_adapter *) int_parm;
-	queue = &adapter->request_queue;
-
-	ZFCP_LOG_DEBUG("adapter %s, first=%d, elements_processed=%d\n",
-		       zfcp_get_busid_by_adapter(adapter),
-		       first_element, elements_processed);
-
-	if (unlikely(zfcp_qdio_handler_error_check(adapter, status, qdio_error,
-						   siga_error, first_element,
-						   elements_processed)))
-		goto out;
-	/*
-	 * we stored address of struct zfcp_adapter  data structure
-	 * associated with irq in int_parm
-	 */
-
-	/* cleanup all SBALs being program-owned now */
-	zfcp_qdio_zero_sbals(queue->buffer, first_element, elements_processed);
-
-	/* increase free space in outbound queue */
-	atomic_add(elements_processed, &queue->free_count);
-	ZFCP_LOG_DEBUG("free_count=%d\n", atomic_read(&queue->free_count));
-	wake_up(&adapter->request_wq);
-	ZFCP_LOG_DEBUG("elements_processed=%d, free count=%d\n",
-		       elements_processed, atomic_read(&queue->free_count));
- out:
-	return;
+	return &q->sbal[sbal_idx]->element[sbale_idx];
 }
 
 /**
- * zfcp_qdio_reqid_check - checks for valid reqids.
+ * zfcp_qdio_free - free memory used by request- and resposne queue
+ * @adapter: pointer to the zfcp_adapter structure
  */
+void zfcp_qdio_free(struct zfcp_adapter *adapter)
+{
+	struct qdio_buffer **sbal_req, **sbal_resp;
+	int p;
+
+	if (adapter->ccw_device)
+		qdio_free(adapter->ccw_device);
+
+	sbal_req = adapter->req_q.sbal;
+	sbal_resp = adapter->resp_q.sbal;
+
+	for (p = 0; p < QDIO_MAX_BUFFERS_PER_Q; p += QBUFF_PER_PAGE) {
+		free_page((unsigned long) sbal_req[p]);
+		free_page((unsigned long) sbal_resp[p]);
+	}
+}
+
+static void zfcp_qdio_handler_error(struct zfcp_adapter *adapter, u8 id)
+{
+	dev_warn(&adapter->ccw_device->dev, "QDIO problem occurred.\n");
+
+	zfcp_erp_adapter_reopen(adapter,
+				ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED |
+				ZFCP_STATUS_COMMON_ERP_FAILED, id, NULL);
+}
+
+static void zfcp_qdio_zero_sbals(struct qdio_buffer *sbal[], int first, int cnt)
+{
+	int i, sbal_idx;
+
+	for (i = first; i < first + cnt; i++) {
+		sbal_idx = i % QDIO_MAX_BUFFERS_PER_Q;
+		memset(sbal[sbal_idx], 0, sizeof(struct qdio_buffer));
+	}
+}
+
+static void zfcp_qdio_int_req(struct ccw_device *cdev, unsigned int status,
+			      unsigned int qdio_err, unsigned int siga_err,
+			      unsigned int queue_no, int first, int count,
+			      unsigned long parm)
+{
+	struct zfcp_adapter *adapter = (struct zfcp_adapter *) parm;
+	struct zfcp_qdio_queue *queue = &adapter->req_q;
+
+	if (unlikely(status & QDIO_STATUS_LOOK_FOR_ERROR)) {
+		zfcp_hba_dbf_event_qdio(adapter, status, qdio_err, siga_err,
+					first, count);
+		zfcp_qdio_handler_error(adapter, 140);
+		return;
+	}
+
+	/* cleanup all SBALs being program-owned now */
+	zfcp_qdio_zero_sbals(queue->sbal, first, count);
+
+	atomic_add(count, &queue->count);
+	wake_up(&adapter->request_wq);
+}
+
 static void zfcp_qdio_reqid_check(struct zfcp_adapter *adapter,
-				  unsigned long req_id)
+				  unsigned long req_id, int sbal_idx)
 {
 	struct zfcp_fsf_req *fsf_req;
 	unsigned long flags;
@@ -248,203 +110,117 @@
 		 * Unknown request means that we have potentially memory
 		 * corruption and must stop the machine immediatly.
 		 */
-		panic("error: unknown request id (%ld) on adapter %s.\n",
+		panic("error: unknown request id (%lx) on adapter %s.\n",
 		      req_id, zfcp_get_busid_by_adapter(adapter));
 
 	zfcp_reqlist_remove(adapter, fsf_req);
-	atomic_dec(&adapter->reqs_active);
 	spin_unlock_irqrestore(&adapter->req_list_lock, flags);
 
-	/* finish the FSF request */
+	fsf_req->sbal_response = sbal_idx;
 	zfcp_fsf_req_complete(fsf_req);
 }
 
-/*
- * function:   	zfcp_qdio_response_handler
- *
- * purpose:	is called by QDIO layer for completed SBALs in response queue
- *
- * returns:	(void)
- */
-static void
-zfcp_qdio_response_handler(struct ccw_device *ccw_device,
-			   unsigned int status,
-			   unsigned int qdio_error,
-			   unsigned int siga_error,
-			   unsigned int queue_number,
-			   int first_element,
-			   int elements_processed,
-			   unsigned long int_parm)
+static void zfcp_qdio_resp_put_back(struct zfcp_adapter *adapter, int processed)
 {
-	struct zfcp_adapter *adapter;
-	struct zfcp_qdio_queue *queue;
-	int buffer_index;
-	int i;
-	struct qdio_buffer *buffer;
-	int retval = 0;
-	u8 count;
-	u8 start;
-	volatile struct qdio_buffer_element *buffere = NULL;
-	int buffere_index;
+	struct zfcp_qdio_queue *queue = &adapter->resp_q;
+	struct ccw_device *cdev = adapter->ccw_device;
+	u8 count, start = queue->first;
+	unsigned int retval;
 
-	adapter = (struct zfcp_adapter *) int_parm;
-	queue = &adapter->response_queue;
+	count = atomic_read(&queue->count) + processed;
 
-	if (unlikely(zfcp_qdio_handler_error_check(adapter, status, qdio_error,
-						   siga_error, first_element,
-						   elements_processed)))
-		goto out;
+	retval = do_QDIO(cdev, QDIO_FLAG_SYNC_INPUT | QDIO_FLAG_UNDER_INTERRUPT,
+			 0, start, count, NULL);
 
-	/*
-	 * we stored address of struct zfcp_adapter  data structure
-	 * associated with irq in int_parm
-	 */
+	if (unlikely(retval)) {
+		atomic_set(&queue->count, count);
+		/* FIXME: Recover this with an adapter reopen? */
+	} else {
+		queue->first += count;
+		queue->first %= QDIO_MAX_BUFFERS_PER_Q;
+		atomic_set(&queue->count, 0);
+	}
+}
 
-	buffere = &(queue->buffer[first_element]->element[0]);
-	ZFCP_LOG_DEBUG("first BUFFERE flags=0x%x\n", buffere->flags);
+static void zfcp_qdio_int_resp(struct ccw_device *cdev, unsigned int status,
+			       unsigned int qdio_err, unsigned int siga_err,
+			       unsigned int queue_no, int first, int count,
+			       unsigned long parm)
+{
+	struct zfcp_adapter *adapter = (struct zfcp_adapter *) parm;
+	struct zfcp_qdio_queue *queue = &adapter->resp_q;
+	volatile struct qdio_buffer_element *sbale;
+	int sbal_idx, sbale_idx, sbal_no;
+
+	if (unlikely(status & QDIO_STATUS_LOOK_FOR_ERROR)) {
+		zfcp_hba_dbf_event_qdio(adapter, status, qdio_err, siga_err,
+					first, count);
+		zfcp_qdio_handler_error(adapter, 147);
+		return;
+	}
+
 	/*
 	 * go through all SBALs from input queue currently
 	 * returned by QDIO layer
 	 */
-
-	for (i = 0; i < elements_processed; i++) {
-
-		buffer_index = first_element + i;
-		buffer_index %= QDIO_MAX_BUFFERS_PER_Q;
-		buffer = queue->buffer[buffer_index];
+	for (sbal_no = 0; sbal_no < count; sbal_no++) {
+		sbal_idx = (first + sbal_no) % QDIO_MAX_BUFFERS_PER_Q;
 
 		/* go through all SBALEs of SBAL */
-		for (buffere_index = 0;
-		     buffere_index < QDIO_MAX_ELEMENTS_PER_BUFFER;
-		     buffere_index++) {
-
-			/* look for QDIO request identifiers in SB */
-			buffere = &buffer->element[buffere_index];
+		for (sbale_idx = 0; sbale_idx < QDIO_MAX_ELEMENTS_PER_BUFFER;
+		     sbale_idx++) {
+			sbale = zfcp_qdio_sbale(queue, sbal_idx, sbale_idx);
 			zfcp_qdio_reqid_check(adapter,
-					      (unsigned long) buffere->addr);
-
-			/*
-			 * A single used SBALE per inbound SBALE has been
-			 * implemented by QDIO so far. Hope they will
-			 * do some optimisation. Will need to change to
-			 * unlikely() then.
-			 */
-			if (likely(buffere->flags & SBAL_FLAGS_LAST_ENTRY))
+					      (unsigned long) sbale->addr,
+					      sbal_idx);
+			if (likely(sbale->flags & SBAL_FLAGS_LAST_ENTRY))
 				break;
 		};
 
-		if (unlikely(!(buffere->flags & SBAL_FLAGS_LAST_ENTRY))) {
-			ZFCP_LOG_NORMAL("bug: End of inbound data "
-					"not marked!\n");
-		}
+		if (unlikely(!(sbale->flags & SBAL_FLAGS_LAST_ENTRY)))
+			dev_warn(&adapter->ccw_device->dev,
+				 "Protocol violation by adapter. "
+				 "Continuing operations.\n");
 	}
 
 	/*
 	 * put range of SBALs back to response queue
 	 * (including SBALs which have already been free before)
 	 */
-	count = atomic_read(&queue->free_count) + elements_processed;
-	start = queue->free_index;
-
-	ZFCP_LOG_TRACE("calling do_QDIO on adapter %s (flags=0x%x, "
-		       "queue_no=%i, index_in_queue=%i, count=%i, "
-		       "buffers=0x%lx\n",
-		       zfcp_get_busid_by_adapter(adapter),
-		       QDIO_FLAG_SYNC_INPUT | QDIO_FLAG_UNDER_INTERRUPT,
-		       0, start, count, (unsigned long) &queue->buffer[start]);
-
-	retval = do_QDIO(ccw_device,
-			 QDIO_FLAG_SYNC_INPUT | QDIO_FLAG_UNDER_INTERRUPT,
-			 0, start, count, NULL);
-
-	if (unlikely(retval)) {
-		atomic_set(&queue->free_count, count);
-		ZFCP_LOG_DEBUG("clearing of inbound data regions failed, "
-			       "queues may be down "
-			       "(count=%d, start=%d, retval=%d)\n",
-			       count, start, retval);
-	} else {
-		queue->free_index += count;
-		queue->free_index %= QDIO_MAX_BUFFERS_PER_Q;
-		atomic_set(&queue->free_count, 0);
-		ZFCP_LOG_TRACE("%i buffers enqueued to response "
-			       "queue at position %i\n", count, start);
-	}
- out:
-	return;
+	zfcp_qdio_resp_put_back(adapter, count);
 }
 
 /**
- * zfcp_qdio_sbale_get - return pointer to SBALE of qdio_queue
- * @queue: queue from which SBALE should be returned
- * @sbal: specifies number of SBAL in queue
- * @sbale: specifes number of SBALE in SBAL
- */
-static inline volatile struct qdio_buffer_element *
-zfcp_qdio_sbale_get(struct zfcp_qdio_queue *queue, int sbal, int sbale)
-{
-	return &queue->buffer[sbal]->element[sbale];
-}
-
-/**
- * zfcp_qdio_sbale_req - return pointer to SBALE of request_queue for
- *	a struct zfcp_fsf_req
+ * zfcp_qdio_sbale_req - return ptr to SBALE of req_q for a struct zfcp_fsf_req
+ * @fsf_req: pointer to struct fsf_req
+ * Returns: pointer to qdio_buffer_element (SBALE) structure
  */
 volatile struct qdio_buffer_element *
-zfcp_qdio_sbale_req(struct zfcp_fsf_req *fsf_req, int sbal, int sbale)
+zfcp_qdio_sbale_req(struct zfcp_fsf_req *req)
 {
-	return zfcp_qdio_sbale_get(&fsf_req->adapter->request_queue,
-				   sbal, sbale);
+	return zfcp_qdio_sbale(&req->adapter->req_q, req->sbal_last, 0);
 }
 
 /**
- * zfcp_qdio_sbale_resp - return pointer to SBALE of response_queue for
- *	a struct zfcp_fsf_req
- */
-static inline volatile struct qdio_buffer_element *
-zfcp_qdio_sbale_resp(struct zfcp_fsf_req *fsf_req, int sbal, int sbale)
-{
-	return zfcp_qdio_sbale_get(&fsf_req->adapter->response_queue,
-				   sbal, sbale);
-}
-
-/**
- * zfcp_qdio_sbale_curr - return current SBALE on request_queue for
- *	a struct zfcp_fsf_req
+ * zfcp_qdio_sbale_curr - return curr SBALE on req_q for a struct zfcp_fsf_req
+ * @fsf_req: pointer to struct fsf_req
+ * Returns: pointer to qdio_buffer_element (SBALE) structure
  */
 volatile struct qdio_buffer_element *
-zfcp_qdio_sbale_curr(struct zfcp_fsf_req *fsf_req)
+zfcp_qdio_sbale_curr(struct zfcp_fsf_req *req)
 {
-	return zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr,
-				   fsf_req->sbale_curr);
+	return zfcp_qdio_sbale(&req->adapter->req_q, req->sbal_last,
+			       req->sbale_curr);
 }
 
-/**
- * zfcp_qdio_sbal_limit - determine maximum number of SBALs that can be used
- *	on the request_queue for a struct zfcp_fsf_req
- * @fsf_req: the number of the last SBAL that can be used is stored herein
- * @max_sbals: used to pass an upper limit for the number of SBALs
- *
- * Note: We can assume at least one free SBAL in the request_queue when called.
- */
-static void
-zfcp_qdio_sbal_limit(struct zfcp_fsf_req *fsf_req, int max_sbals)
+static void zfcp_qdio_sbal_limit(struct zfcp_fsf_req *fsf_req, int max_sbals)
 {
-	int count = atomic_read(&fsf_req->adapter->request_queue.free_count);
+	int count = atomic_read(&fsf_req->adapter->req_q.count);
 	count = min(count, max_sbals);
-	fsf_req->sbal_last  = fsf_req->sbal_first;
-	fsf_req->sbal_last += (count - 1);
-	fsf_req->sbal_last %= QDIO_MAX_BUFFERS_PER_Q;
+	fsf_req->sbal_limit = (fsf_req->sbal_first + count - 1)
+					% QDIO_MAX_BUFFERS_PER_Q;
 }
 
-/**
- * zfcp_qdio_sbal_chain - chain SBALs if more than one SBAL is needed for a
- *	request
- * @fsf_req: zfcp_fsf_req to be processed
- * @sbtype: SBAL flags which have to be set in first SBALE of new SBAL
- *
- * This function changes sbal_curr, sbale_curr, sbal_number of fsf_req.
- */
 static volatile struct qdio_buffer_element *
 zfcp_qdio_sbal_chain(struct zfcp_fsf_req *fsf_req, unsigned long sbtype)
 {
@@ -455,16 +231,16 @@
 	sbale->flags |= SBAL_FLAGS_LAST_ENTRY;
 
 	/* don't exceed last allowed SBAL */
-	if (fsf_req->sbal_curr == fsf_req->sbal_last)
+	if (fsf_req->sbal_last == fsf_req->sbal_limit)
 		return NULL;
 
 	/* set chaining flag in first SBALE of current SBAL */
-	sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
+	sbale = zfcp_qdio_sbale_req(fsf_req);
 	sbale->flags |= SBAL_FLAGS0_MORE_SBALS;
 
 	/* calculate index of next SBAL */
-	fsf_req->sbal_curr++;
-	fsf_req->sbal_curr %= QDIO_MAX_BUFFERS_PER_Q;
+	fsf_req->sbal_last++;
+	fsf_req->sbal_last %= QDIO_MAX_BUFFERS_PER_Q;
 
 	/* keep this requests number of SBALs up-to-date */
 	fsf_req->sbal_number++;
@@ -479,214 +255,255 @@
 	return sbale;
 }
 
-/**
- * zfcp_qdio_sbale_next - switch to next SBALE, chain SBALs if needed
- */
 static volatile struct qdio_buffer_element *
 zfcp_qdio_sbale_next(struct zfcp_fsf_req *fsf_req, unsigned long sbtype)
 {
 	if (fsf_req->sbale_curr == ZFCP_LAST_SBALE_PER_SBAL)
 		return zfcp_qdio_sbal_chain(fsf_req, sbtype);
-
 	fsf_req->sbale_curr++;
-
 	return zfcp_qdio_sbale_curr(fsf_req);
 }
 
-/**
- * zfcp_qdio_sbals_zero - initialize SBALs between first and last in queue
- *	with zero from
- */
-static int
-zfcp_qdio_sbals_zero(struct zfcp_qdio_queue *queue, int first, int last)
+static void zfcp_qdio_undo_sbals(struct zfcp_fsf_req *fsf_req)
 {
-	struct qdio_buffer **buf = queue->buffer;
-	int curr = first;
-	int count = 0;
-
-	for(;;) {
-		curr %= QDIO_MAX_BUFFERS_PER_Q;
-		count++;
-		memset(buf[curr], 0, sizeof(struct qdio_buffer));
-		if (curr == last)
-			break;
-		curr++;
-	}
-	return count;
+	struct qdio_buffer **sbal = fsf_req->adapter->req_q.sbal;
+	int first = fsf_req->sbal_first;
+	int last = fsf_req->sbal_last;
+	int count = (last - first + QDIO_MAX_BUFFERS_PER_Q) %
+		QDIO_MAX_BUFFERS_PER_Q + 1;
+	zfcp_qdio_zero_sbals(sbal, first, count);
 }
 
-
-/**
- * zfcp_qdio_sbals_wipe - reset all changes in SBALs for an fsf_req
- */
-static inline int
-zfcp_qdio_sbals_wipe(struct zfcp_fsf_req *fsf_req)
-{
-	return zfcp_qdio_sbals_zero(&fsf_req->adapter->request_queue,
-				    fsf_req->sbal_first, fsf_req->sbal_curr);
-}
-
-
-/**
- * zfcp_qdio_sbale_fill - set address and length in current SBALE
- *	on request_queue
- */
-static void
-zfcp_qdio_sbale_fill(struct zfcp_fsf_req *fsf_req, unsigned long sbtype,
-		     void *addr, int length)
+static int zfcp_qdio_fill_sbals(struct zfcp_fsf_req *fsf_req,
+				unsigned int sbtype, void *start_addr,
+				unsigned int total_length)
 {
 	volatile struct qdio_buffer_element *sbale;
-
-	sbale = zfcp_qdio_sbale_curr(fsf_req);
-	sbale->addr = addr;
-	sbale->length = length;
-}
-
-/**
- * zfcp_qdio_sbals_from_segment - map memory segment to SBALE(s)
- * @fsf_req: request to be processed
- * @sbtype: SBALE flags
- * @start_addr: address of memory segment
- * @total_length: length of memory segment
- *
- * Alignment and length of the segment determine how many SBALEs are needed
- * for the memory segment.
- */
-static int
-zfcp_qdio_sbals_from_segment(struct zfcp_fsf_req *fsf_req, unsigned long sbtype,
-			     void *start_addr, unsigned long total_length)
-{
 	unsigned long remaining, length;
 	void *addr;
 
-	/* split segment up heeding page boundaries */
+	/* split segment up */
 	for (addr = start_addr, remaining = total_length; remaining > 0;
 	     addr += length, remaining -= length) {
-		/* get next free SBALE for new piece */
-		if (NULL == zfcp_qdio_sbale_next(fsf_req, sbtype)) {
-			/* no SBALE left, clean up and leave */
-			zfcp_qdio_sbals_wipe(fsf_req);
+		sbale = zfcp_qdio_sbale_next(fsf_req, sbtype);
+		if (!sbale) {
+			zfcp_qdio_undo_sbals(fsf_req);
 			return -EINVAL;
 		}
-		/* calculate length of new piece */
-		length = min(remaining,
-			     (PAGE_SIZE - ((unsigned long) addr &
-					   (PAGE_SIZE - 1))));
-		/* fill current SBALE with calculated piece */
-		zfcp_qdio_sbale_fill(fsf_req, sbtype, addr, length);
-	}
-	return total_length;
-}
 
+		/* new piece must not exceed next page boundary */
+		length = min(remaining,
+			     (PAGE_SIZE - ((unsigned long)addr &
+					   (PAGE_SIZE - 1))));
+		sbale->addr = addr;
+		sbale->length = length;
+	}
+	return 0;
+}
 
 /**
  * zfcp_qdio_sbals_from_sg - fill SBALs from scatter-gather list
  * @fsf_req: request to be processed
  * @sbtype: SBALE flags
  * @sg: scatter-gather list
- * @sg_count: number of elements in scatter-gather list
  * @max_sbals: upper bound for number of SBALs to be used
+ * Returns: number of bytes, or error (negativ)
  */
-int
-zfcp_qdio_sbals_from_sg(struct zfcp_fsf_req *fsf_req, unsigned long sbtype,
-                        struct scatterlist *sgl, int sg_count, int max_sbals)
+int zfcp_qdio_sbals_from_sg(struct zfcp_fsf_req *fsf_req, unsigned long sbtype,
+			    struct scatterlist *sg, int max_sbals)
 {
-	int sg_index;
-	struct scatterlist *sg_segment;
-	int retval;
 	volatile struct qdio_buffer_element *sbale;
-	int bytes = 0;
+	int retval, bytes = 0;
 
 	/* figure out last allowed SBAL */
 	zfcp_qdio_sbal_limit(fsf_req, max_sbals);
 
-	/* set storage-block type for current SBAL */
-	sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
+	/* set storage-block type for this request */
+	sbale = zfcp_qdio_sbale_req(fsf_req);
 	sbale->flags |= sbtype;
 
-	/* process all segements of scatter-gather list */
-	for_each_sg(sgl, sg_segment, sg_count, sg_index) {
-		retval = zfcp_qdio_sbals_from_segment(
-				fsf_req,
-				sbtype,
-				zfcp_sg_to_address(sg_segment),
-				sg_segment->length);
-		if (retval < 0) {
-			bytes = retval;
-			goto out;
-		} else
-                        bytes += retval;
+	for (; sg; sg = sg_next(sg)) {
+		retval = zfcp_qdio_fill_sbals(fsf_req, sbtype, sg_virt(sg),
+					      sg->length);
+		if (retval < 0)
+			return retval;
+		bytes += sg->length;
 	}
+
 	/* assume that no other SBALEs are to follow in the same SBAL */
 	sbale = zfcp_qdio_sbale_curr(fsf_req);
 	sbale->flags |= SBAL_FLAGS_LAST_ENTRY;
-out:
+
 	return bytes;
 }
 
-
 /**
- * zfcp_qdio_sbals_from_scsicmnd - fill SBALs from scsi command
- * @fsf_req: request to be processed
- * @sbtype: SBALE flags
- * @scsi_cmnd: either scatter-gather list or buffer contained herein is used
- *	to fill SBALs
+ * zfcp_qdio_send - set PCI flag in first SBALE and send req to QDIO
+ * @fsf_req: pointer to struct zfcp_fsf_req
+ * Returns: 0 on success, error otherwise
  */
-int
-zfcp_qdio_sbals_from_scsicmnd(struct zfcp_fsf_req *fsf_req,
-			      unsigned long sbtype, struct scsi_cmnd *scsi_cmnd)
+int zfcp_qdio_send(struct zfcp_fsf_req *fsf_req)
 {
-	return zfcp_qdio_sbals_from_sg(fsf_req,	sbtype, scsi_sglist(scsi_cmnd),
-				       scsi_sg_count(scsi_cmnd),
-				       ZFCP_MAX_SBALS_PER_REQ);
-}
-
-/**
- * zfcp_qdio_determine_pci - set PCI flag in first SBALE on qdio queue if needed
- */
-int
-zfcp_qdio_determine_pci(struct zfcp_qdio_queue *req_queue,
-			struct zfcp_fsf_req *fsf_req)
-{
-	int new_distance_from_int;
-	int pci_pos;
+	struct zfcp_adapter *adapter = fsf_req->adapter;
+	struct zfcp_qdio_queue *req_q = &adapter->req_q;
+	int first = fsf_req->sbal_first;
+	int count = fsf_req->sbal_number;
+	int retval, pci, pci_batch;
 	volatile struct qdio_buffer_element *sbale;
 
-	new_distance_from_int = req_queue->distance_from_int +
-                fsf_req->sbal_number;
-
-	if (unlikely(new_distance_from_int >= ZFCP_QDIO_PCI_INTERVAL)) {
-		new_distance_from_int %= ZFCP_QDIO_PCI_INTERVAL;
-                pci_pos  = fsf_req->sbal_first;
-		pci_pos += fsf_req->sbal_number;
-		pci_pos -= new_distance_from_int;
-		pci_pos -= 1;
-		pci_pos %= QDIO_MAX_BUFFERS_PER_Q;
-		sbale = zfcp_qdio_sbale_req(fsf_req, pci_pos, 0);
+	/* acknowledgements for transferred buffers */
+	pci_batch = req_q->pci_batch + count;
+	if (unlikely(pci_batch >= ZFCP_QDIO_PCI_INTERVAL)) {
+		pci_batch %= ZFCP_QDIO_PCI_INTERVAL;
+		pci = first + count - (pci_batch + 1);
+		pci %= QDIO_MAX_BUFFERS_PER_Q;
+		sbale = zfcp_qdio_sbale(req_q, pci, 0);
 		sbale->flags |= SBAL_FLAGS0_PCI;
 	}
-	return new_distance_from_int;
-}
 
-/*
- * function:	zfcp_zero_sbals
- *
- * purpose:	zeros specified range of SBALs
- *
- * returns:
- */
-void
-zfcp_qdio_zero_sbals(struct qdio_buffer *buf[], int first, int clean_count)
-{
-	int cur_pos;
-	int index;
-
-	for (cur_pos = first; cur_pos < (first + clean_count); cur_pos++) {
-		index = cur_pos % QDIO_MAX_BUFFERS_PER_Q;
-		memset(buf[index], 0, sizeof (struct qdio_buffer));
-		ZFCP_LOG_TRACE("zeroing BUFFER %d at address %p\n",
-			       index, buf[index]);
+	retval = do_QDIO(adapter->ccw_device, QDIO_FLAG_SYNC_OUTPUT, 0, first,
+			 count, NULL);
+	if (unlikely(retval)) {
+		zfcp_qdio_zero_sbals(req_q->sbal, first, count);
+		return retval;
 	}
+
+	/* account for transferred buffers */
+	atomic_sub(count, &req_q->count);
+	req_q->first += count;
+	req_q->first %= QDIO_MAX_BUFFERS_PER_Q;
+	req_q->pci_batch = pci_batch;
+	return 0;
 }
 
-#undef ZFCP_LOG_AREA
+/**
+ * zfcp_qdio_allocate - allocate queue memory and initialize QDIO data
+ * @adapter: pointer to struct zfcp_adapter
+ * Returns: -ENOMEM on memory allocation error or return value from
+ *          qdio_allocate
+ */
+int zfcp_qdio_allocate(struct zfcp_adapter *adapter)
+{
+	struct qdio_initialize *init_data;
+
+	if (zfcp_qdio_buffers_enqueue(adapter->req_q.sbal) ||
+		   zfcp_qdio_buffers_enqueue(adapter->resp_q.sbal))
+		return -ENOMEM;
+
+	init_data = &adapter->qdio_init_data;
+
+	init_data->cdev = adapter->ccw_device;
+	init_data->q_format = QDIO_ZFCP_QFMT;
+	memcpy(init_data->adapter_name, zfcp_get_busid_by_adapter(adapter), 8);
+	ASCEBC(init_data->adapter_name, 8);
+	init_data->qib_param_field_format = 0;
+	init_data->qib_param_field = NULL;
+	init_data->input_slib_elements = NULL;
+	init_data->output_slib_elements = NULL;
+	init_data->min_input_threshold = 1;
+	init_data->max_input_threshold = 5000;
+	init_data->min_output_threshold = 1;
+	init_data->max_output_threshold = 1000;
+	init_data->no_input_qs = 1;
+	init_data->no_output_qs = 1;
+	init_data->input_handler = zfcp_qdio_int_resp;
+	init_data->output_handler = zfcp_qdio_int_req;
+	init_data->int_parm = (unsigned long) adapter;
+	init_data->flags = QDIO_INBOUND_0COPY_SBALS |
+			QDIO_OUTBOUND_0COPY_SBALS | QDIO_USE_OUTBOUND_PCIS;
+	init_data->input_sbal_addr_array =
+			(void **) (adapter->resp_q.sbal);
+	init_data->output_sbal_addr_array =
+			(void **) (adapter->req_q.sbal);
+
+	return qdio_allocate(init_data);
+}
+
+/**
+ * zfcp_close_qdio - close qdio queues for an adapter
+ */
+void zfcp_qdio_close(struct zfcp_adapter *adapter)
+{
+	struct zfcp_qdio_queue *req_q;
+	int first, count;
+
+	if (!atomic_test_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &adapter->status))
+		return;
+
+	/* clear QDIOUP flag, thus do_QDIO is not called during qdio_shutdown */
+	req_q = &adapter->req_q;
+	spin_lock(&req_q->lock);
+	atomic_clear_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &adapter->status);
+	spin_unlock(&req_q->lock);
+
+	while (qdio_shutdown(adapter->ccw_device, QDIO_FLAG_CLEANUP_USING_CLEAR)
+			== -EINPROGRESS)
+		ssleep(1);
+
+	/* cleanup used outbound sbals */
+	count = atomic_read(&req_q->count);
+	if (count < QDIO_MAX_BUFFERS_PER_Q) {
+		first = (req_q->first + count) % QDIO_MAX_BUFFERS_PER_Q;
+		count = QDIO_MAX_BUFFERS_PER_Q - count;
+		zfcp_qdio_zero_sbals(req_q->sbal, first, count);
+	}
+	req_q->first = 0;
+	atomic_set(&req_q->count, 0);
+	req_q->pci_batch = 0;
+	adapter->resp_q.first = 0;
+	atomic_set(&adapter->resp_q.count, 0);
+}
+
+/**
+ * zfcp_qdio_open - prepare and initialize response queue
+ * @adapter: pointer to struct zfcp_adapter
+ * Returns: 0 on success, otherwise -EIO
+ */
+int zfcp_qdio_open(struct zfcp_adapter *adapter)
+{
+	volatile struct qdio_buffer_element *sbale;
+	int cc;
+
+	if (atomic_test_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &adapter->status))
+		return -EIO;
+
+	if (qdio_establish(&adapter->qdio_init_data)) {
+		dev_err(&adapter->ccw_device->dev,
+			 "Establish of QDIO queues failed.\n");
+		return -EIO;
+	}
+
+	if (qdio_activate(adapter->ccw_device, 0)) {
+		dev_err(&adapter->ccw_device->dev,
+			 "Activate of QDIO queues failed.\n");
+		goto failed_qdio;
+	}
+
+	for (cc = 0; cc < QDIO_MAX_BUFFERS_PER_Q; cc++) {
+		sbale = &(adapter->resp_q.sbal[cc]->element[0]);
+		sbale->length = 0;
+		sbale->flags = SBAL_FLAGS_LAST_ENTRY;
+		sbale->addr = NULL;
+	}
+
+	if (do_QDIO(adapter->ccw_device, QDIO_FLAG_SYNC_INPUT, 0, 0,
+		     QDIO_MAX_BUFFERS_PER_Q, NULL)) {
+		dev_err(&adapter->ccw_device->dev,
+			 "Init of QDIO response queue failed.\n");
+		goto failed_qdio;
+	}
+
+	/* set index of first avalable SBALS / number of available SBALS */
+	adapter->req_q.first = 0;
+	atomic_set(&adapter->req_q.count, QDIO_MAX_BUFFERS_PER_Q);
+	adapter->req_q.pci_batch = 0;
+
+	return 0;
+
+failed_qdio:
+	while (qdio_shutdown(adapter->ccw_device, QDIO_FLAG_CLEANUP_USING_CLEAR)
+			== -EINPROGRESS)
+		ssleep(1);
+
+	return -EIO;
+}
diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c
index 0168755..aeae56b 100644
--- a/drivers/s390/scsi/zfcp_scsi.c
+++ b/drivers/s390/scsi/zfcp_scsi.c
@@ -1,152 +1,137 @@
 /*
- * This file is part of the zfcp device driver for
- * FCP adapters for IBM System z9 and zSeries.
+ * zfcp device driver
  *
- * (C) Copyright IBM Corp. 2002, 2006
+ * Interface to Linux SCSI midlayer.
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * Copyright IBM Corporation 2002, 2008
  */
 
-#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_SCSI
-
 #include "zfcp_ext.h"
 #include <asm/atomic.h>
 
-static void zfcp_scsi_slave_destroy(struct scsi_device *sdp);
-static int zfcp_scsi_slave_alloc(struct scsi_device *sdp);
-static int zfcp_scsi_slave_configure(struct scsi_device *sdp);
-static int zfcp_scsi_queuecommand(struct scsi_cmnd *,
-				  void (*done) (struct scsi_cmnd *));
-static int zfcp_scsi_eh_abort_handler(struct scsi_cmnd *);
-static int zfcp_scsi_eh_device_reset_handler(struct scsi_cmnd *);
-static int zfcp_scsi_eh_target_reset_handler(struct scsi_cmnd *);
-static int zfcp_scsi_eh_host_reset_handler(struct scsi_cmnd *);
-static int zfcp_task_management_function(struct zfcp_unit *, u8,
-					 struct scsi_cmnd *);
-
-static struct zfcp_unit *zfcp_unit_lookup(struct zfcp_adapter *, int,
-					  unsigned int, unsigned int);
-
-static struct device_attribute *zfcp_sysfs_sdev_attrs[];
-static struct device_attribute *zfcp_a_stats_attrs[];
-
-struct zfcp_data zfcp_data = {
-	.scsi_host_template = {
-		.name			= ZFCP_NAME,
-		.module			= THIS_MODULE,
-		.proc_name		= "zfcp",
-		.slave_alloc		= zfcp_scsi_slave_alloc,
-		.slave_configure	= zfcp_scsi_slave_configure,
-		.slave_destroy		= zfcp_scsi_slave_destroy,
-		.queuecommand		= zfcp_scsi_queuecommand,
-		.eh_abort_handler	= zfcp_scsi_eh_abort_handler,
-		.eh_device_reset_handler = zfcp_scsi_eh_device_reset_handler,
-		.eh_target_reset_handler = zfcp_scsi_eh_target_reset_handler,
-		.eh_host_reset_handler	= zfcp_scsi_eh_host_reset_handler,
-		.can_queue		= 4096,
-		.this_id		= -1,
-		.sg_tablesize		= ZFCP_MAX_SBALES_PER_REQ,
-		.cmd_per_lun		= 1,
-		.use_clustering		= 1,
-		.sdev_attrs		= zfcp_sysfs_sdev_attrs,
-		.max_sectors		= ZFCP_MAX_SECTORS,
-		.shost_attrs		= zfcp_a_stats_attrs,
-	},
-	.driver_version = ZFCP_VERSION,
-};
-
-/* Find start of Response Information in FCP response unit*/
-char *
-zfcp_get_fcp_rsp_info_ptr(struct fcp_rsp_iu *fcp_rsp_iu)
-{
-	char *fcp_rsp_info_ptr;
-
-	fcp_rsp_info_ptr =
-	    (unsigned char *) fcp_rsp_iu + (sizeof (struct fcp_rsp_iu));
-
-	return fcp_rsp_info_ptr;
-}
-
 /* Find start of Sense Information in FCP response unit*/
-char *
-zfcp_get_fcp_sns_info_ptr(struct fcp_rsp_iu *fcp_rsp_iu)
+char *zfcp_get_fcp_sns_info_ptr(struct fcp_rsp_iu *fcp_rsp_iu)
 {
 	char *fcp_sns_info_ptr;
 
-	fcp_sns_info_ptr =
-	    (unsigned char *) fcp_rsp_iu + (sizeof (struct fcp_rsp_iu));
+	fcp_sns_info_ptr = (unsigned char *) &fcp_rsp_iu[1];
 	if (fcp_rsp_iu->validity.bits.fcp_rsp_len_valid)
-		fcp_sns_info_ptr = (char *) fcp_sns_info_ptr +
-		    fcp_rsp_iu->fcp_rsp_len;
+		fcp_sns_info_ptr += fcp_rsp_iu->fcp_rsp_len;
 
 	return fcp_sns_info_ptr;
 }
 
-static fcp_dl_t *
-zfcp_get_fcp_dl_ptr(struct fcp_cmnd_iu * fcp_cmd)
+void zfcp_set_fcp_dl(struct fcp_cmnd_iu *fcp_cmd, fcp_dl_t fcp_dl)
 {
-	int additional_length = fcp_cmd->add_fcp_cdb_length << 2;
-	fcp_dl_t *fcp_dl_addr;
+	fcp_dl_t *fcp_dl_ptr;
 
-	fcp_dl_addr = (fcp_dl_t *)
-		((unsigned char *) fcp_cmd +
-		 sizeof (struct fcp_cmnd_iu) + additional_length);
 	/*
 	 * fcp_dl_addr = start address of fcp_cmnd structure +
 	 * size of fixed part + size of dynamically sized add_dcp_cdb field
 	 * SEE FCP-2 documentation
 	 */
-	return fcp_dl_addr;
+	fcp_dl_ptr = (fcp_dl_t *) ((unsigned char *) &fcp_cmd[1] +
+				   (fcp_cmd->add_fcp_cdb_length << 2));
+	*fcp_dl_ptr = fcp_dl;
 }
 
-fcp_dl_t
-zfcp_get_fcp_dl(struct fcp_cmnd_iu * fcp_cmd)
+static void zfcp_scsi_slave_destroy(struct scsi_device *sdpnt)
 {
-	return *zfcp_get_fcp_dl_ptr(fcp_cmd);
+	struct zfcp_unit *unit = (struct zfcp_unit *) sdpnt->hostdata;
+	WARN_ON(!unit);
+	if (unit) {
+		atomic_clear_mask(ZFCP_STATUS_UNIT_REGISTERED, &unit->status);
+		sdpnt->hostdata = NULL;
+		unit->device = NULL;
+		zfcp_erp_unit_failed(unit, 12, NULL);
+		zfcp_unit_put(unit);
+	}
 }
 
-void
-zfcp_set_fcp_dl(struct fcp_cmnd_iu *fcp_cmd, fcp_dl_t fcp_dl)
+static int zfcp_scsi_slave_configure(struct scsi_device *sdp)
 {
-	*zfcp_get_fcp_dl_ptr(fcp_cmd) = fcp_dl;
+	if (sdp->tagged_supported)
+		scsi_adjust_queue_depth(sdp, MSG_SIMPLE_TAG, 32);
+	else
+		scsi_adjust_queue_depth(sdp, 0, 1);
+	return 0;
 }
 
-/*
- * note: it's a bit-or operation not an assignment
- * regarding the specified byte
- */
-static inline void
-set_byte(int *result, char status, char pos)
+static void zfcp_scsi_command_fail(struct scsi_cmnd *scpnt, int result)
 {
-	*result |= status << (pos * 8);
+	set_host_byte(scpnt, result);
+	if ((scpnt->device != NULL) && (scpnt->device->host != NULL))
+		zfcp_scsi_dbf_event_result("fail", 4,
+			(struct zfcp_adapter*) scpnt->device->host->hostdata[0],
+			scpnt, NULL);
+	/* return directly */
+	scpnt->scsi_done(scpnt);
 }
 
-void
-set_host_byte(int *result, char status)
+static int zfcp_scsi_queuecommand(struct scsi_cmnd *scpnt,
+				  void (*done) (struct scsi_cmnd *))
 {
-	set_byte(result, status, 2);
+	struct zfcp_unit *unit;
+	struct zfcp_adapter *adapter;
+	int    status;
+	int    ret;
+
+	/* reset the status for this request */
+	scpnt->result = 0;
+	scpnt->host_scribble = NULL;
+	scpnt->scsi_done = done;
+
+	/*
+	 * figure out adapter and target device
+	 * (stored there by zfcp_scsi_slave_alloc)
+	 */
+	adapter = (struct zfcp_adapter *) scpnt->device->host->hostdata[0];
+	unit = scpnt->device->hostdata;
+
+	BUG_ON(!adapter || (adapter != unit->port->adapter));
+	BUG_ON(!scpnt->scsi_done);
+
+	if (unlikely(!unit)) {
+		zfcp_scsi_command_fail(scpnt, DID_NO_CONNECT);
+		return 0;
+	}
+
+	status = atomic_read(&unit->status);
+	if (unlikely((status & ZFCP_STATUS_COMMON_ERP_FAILED) ||
+		     !(status & ZFCP_STATUS_COMMON_RUNNING))) {
+		zfcp_scsi_command_fail(scpnt, DID_ERROR);
+		return 0;;
+	}
+
+	ret = zfcp_fsf_send_fcp_command_task(adapter, unit, scpnt, 0,
+					     ZFCP_REQ_AUTO_CLEANUP);
+	if (unlikely(ret == -EBUSY))
+		zfcp_scsi_command_fail(scpnt, DID_NO_CONNECT);
+	else if (unlikely(ret < 0))
+		return SCSI_MLQUEUE_HOST_BUSY;
+
+	return ret;
 }
 
-void
-set_driver_byte(int *result, char status)
+static struct zfcp_unit *zfcp_unit_lookup(struct zfcp_adapter *adapter,
+					  int channel, unsigned int id,
+					  unsigned int lun)
 {
-	set_byte(result, status, 3);
+	struct zfcp_port *port;
+	struct zfcp_unit *unit;
+
+	list_for_each_entry(port, &adapter->port_list_head, list) {
+		if (!port->rport || (id != port->rport->scsi_target_id))
+			continue;
+		list_for_each_entry(unit, &port->unit_list_head, list)
+			if (lun == unit->scsi_lun)
+				return unit;
+	}
+
+	return NULL;
 }
 
-static int
-zfcp_scsi_slave_alloc(struct scsi_device *sdp)
+static int zfcp_scsi_slave_alloc(struct scsi_device *sdp)
 {
 	struct zfcp_adapter *adapter;
 	struct zfcp_unit *unit;
@@ -159,227 +144,18 @@
 
 	read_lock_irqsave(&zfcp_data.config_lock, flags);
 	unit = zfcp_unit_lookup(adapter, sdp->channel, sdp->id, sdp->lun);
-	if (unit && atomic_test_mask(ZFCP_STATUS_UNIT_REGISTERED,
-				     &unit->status)) {
+	if (unit &&
+	    (atomic_read(&unit->status) & ZFCP_STATUS_UNIT_REGISTERED)) {
 		sdp->hostdata = unit;
 		unit->device = sdp;
 		zfcp_unit_get(unit);
 		retval = 0;
 	}
 	read_unlock_irqrestore(&zfcp_data.config_lock, flags);
- out:
-	return retval;
-}
-
-/**
- * zfcp_scsi_slave_destroy - called when scsi device is removed
- *
- * Remove reference to associated scsi device for an zfcp_unit.
- * Mark zfcp_unit as failed. The scsi device might be deleted via sysfs
- * or a scan for this device might have failed.
- */
-static void zfcp_scsi_slave_destroy(struct scsi_device *sdpnt)
-{
-	struct zfcp_unit *unit = (struct zfcp_unit *) sdpnt->hostdata;
-
-	if (unit) {
-		atomic_clear_mask(ZFCP_STATUS_UNIT_REGISTERED, &unit->status);
-		sdpnt->hostdata = NULL;
-		unit->device = NULL;
-		zfcp_erp_unit_failed(unit, 12, NULL);
-		zfcp_unit_put(unit);
-	} else
-		ZFCP_LOG_NORMAL("bug: no unit associated with SCSI device at "
-				"address %p\n", sdpnt);
-}
-
-/*
- * called from scsi midlayer to allow finetuning of a device.
- */
-static int
-zfcp_scsi_slave_configure(struct scsi_device *sdp)
-{
-	if (sdp->tagged_supported)
-		scsi_adjust_queue_depth(sdp, MSG_SIMPLE_TAG, ZFCP_CMND_PER_LUN);
-	else
-		scsi_adjust_queue_depth(sdp, 0, 1);
-	return 0;
-}
-
-/**
- * zfcp_scsi_command_fail - set result in scsi_cmnd and call scsi_done function
- * @scpnt: pointer to struct scsi_cmnd where result is set
- * @result: result to be set in scpnt (e.g. DID_ERROR)
- */
-static void
-zfcp_scsi_command_fail(struct scsi_cmnd *scpnt, int result)
-{
-	set_host_byte(&scpnt->result, result);
-	if ((scpnt->device != NULL) && (scpnt->device->host != NULL))
-		zfcp_scsi_dbf_event_result("fail", 4,
-			(struct zfcp_adapter*) scpnt->device->host->hostdata[0],
-			scpnt, NULL);
-	/* return directly */
-	scpnt->scsi_done(scpnt);
-}
-
-/**
- * zfcp_scsi_command_async - worker for zfcp_scsi_queuecommand and
- *	zfcp_scsi_command_sync
- * @adapter: adapter where scsi command is issued
- * @unit: unit to which scsi command is sent
- * @scpnt: scsi command to be sent
- * @timer: timer to be started if request is successfully initiated
- *
- * Note: In scsi_done function must be set in scpnt.
- */
-int
-zfcp_scsi_command_async(struct zfcp_adapter *adapter, struct zfcp_unit *unit,
-			struct scsi_cmnd *scpnt, int use_timer)
-{
-	int tmp;
-	int retval;
-
-	retval = 0;
-
-	BUG_ON((adapter == NULL) || (adapter != unit->port->adapter));
-	BUG_ON(scpnt->scsi_done == NULL);
-
-	if (unlikely(NULL == unit)) {
-		zfcp_scsi_command_fail(scpnt, DID_NO_CONNECT);
-		goto out;
-	}
-
-	if (unlikely(
-	      atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &unit->status) ||
-	     !atomic_test_mask(ZFCP_STATUS_COMMON_RUNNING, &unit->status))) {
-		ZFCP_LOG_DEBUG("stopping SCSI I/O on unit 0x%016Lx on port "
-			       "0x%016Lx on adapter %s\n",
-			       unit->fcp_lun, unit->port->wwpn,
-			       zfcp_get_busid_by_adapter(adapter));
-		zfcp_scsi_command_fail(scpnt, DID_ERROR);
-		goto out;
-	}
-
-	tmp = zfcp_fsf_send_fcp_command_task(adapter, unit, scpnt, use_timer,
-					     ZFCP_REQ_AUTO_CLEANUP);
-	if (unlikely(tmp == -EBUSY)) {
-		ZFCP_LOG_DEBUG("adapter %s not ready or unit 0x%016Lx "
-			       "on port 0x%016Lx in recovery\n",
-			       zfcp_get_busid_by_unit(unit),
-			       unit->fcp_lun, unit->port->wwpn);
-		zfcp_scsi_command_fail(scpnt, DID_NO_CONNECT);
-		goto out;
-	}
-
-	if (unlikely(tmp < 0)) {
-		ZFCP_LOG_DEBUG("error: initiation of Send FCP Cmnd failed\n");
-		retval = SCSI_MLQUEUE_HOST_BUSY;
-	}
-
 out:
 	return retval;
 }
 
-static void
-zfcp_scsi_command_sync_handler(struct scsi_cmnd *scpnt)
-{
-	struct completion *wait = (struct completion *) scpnt->SCp.ptr;
-	complete(wait);
-}
-
-
-/**
- * zfcp_scsi_command_sync - send a SCSI command and wait for completion
- * @unit: unit where command is sent to
- * @scpnt: scsi command to be sent
- * @use_timer: indicates whether timer should be setup or not
- * Return: 0
- *
- * Errors are indicated in scpnt->result
- */
-int
-zfcp_scsi_command_sync(struct zfcp_unit *unit, struct scsi_cmnd *scpnt,
-		       int use_timer)
-{
-	int ret;
-	DECLARE_COMPLETION_ONSTACK(wait);
-
-	scpnt->SCp.ptr = (void *) &wait;  /* silent re-use */
-	scpnt->scsi_done = zfcp_scsi_command_sync_handler;
-	ret = zfcp_scsi_command_async(unit->port->adapter, unit, scpnt,
-				      use_timer);
-	if (ret == 0)
-		wait_for_completion(&wait);
-
-	scpnt->SCp.ptr = NULL;
-
-	return 0;
-}
-
-/*
- * function:	zfcp_scsi_queuecommand
- *
- * purpose:	enqueues a SCSI command to the specified target device
- *
- * returns:	0 - success, SCSI command enqueued
- *		!0 - failure
- */
-static int
-zfcp_scsi_queuecommand(struct scsi_cmnd *scpnt,
-		       void (*done) (struct scsi_cmnd *))
-{
-	struct zfcp_unit *unit;
-	struct zfcp_adapter *adapter;
-
-	/* reset the status for this request */
-	scpnt->result = 0;
-	scpnt->host_scribble = NULL;
-	scpnt->scsi_done = done;
-
-	/*
-	 * figure out adapter and target device
-	 * (stored there by zfcp_scsi_slave_alloc)
-	 */
-	adapter = (struct zfcp_adapter *) scpnt->device->host->hostdata[0];
-	unit = (struct zfcp_unit *) scpnt->device->hostdata;
-
-	return zfcp_scsi_command_async(adapter, unit, scpnt, 0);
-}
-
-static struct zfcp_unit *
-zfcp_unit_lookup(struct zfcp_adapter *adapter, int channel, unsigned int id,
-		 unsigned int lun)
-{
-	struct zfcp_port *port;
-	struct zfcp_unit *unit, *retval = NULL;
-
-	list_for_each_entry(port, &adapter->port_list_head, list) {
-		if (!port->rport || (id != port->rport->scsi_target_id))
-			continue;
-		list_for_each_entry(unit, &port->unit_list_head, list)
-			if (lun == unit->scsi_lun) {
-				retval = unit;
-				goto out;
-			}
-	}
- out:
-	return retval;
-}
-
-/**
- * zfcp_scsi_eh_abort_handler - abort the specified SCSI command
- * @scpnt: pointer to scsi_cmnd to be aborted
- * Return: SUCCESS - command has been aborted and cleaned up in internal
- *          bookkeeping, SCSI stack won't be called for aborted command
- *         FAILED - otherwise
- *
- * We do not need to care for a SCSI command which completes normally
- * but late during this abort routine runs.  We are allowed to return
- * late commands to the SCSI stack.  It tracks the state of commands and
- * will handle late commands.  (Usually, the normal completion of late
- * commands is ignored with respect to the running abort operation.)
- */
 static int zfcp_scsi_eh_abort_handler(struct scsi_cmnd *scpnt)
 {
  	struct Scsi_Host *scsi_host;
@@ -387,44 +163,37 @@
 	struct zfcp_unit *unit;
 	struct zfcp_fsf_req *fsf_req;
 	unsigned long flags;
-	unsigned long old_req_id;
+	unsigned long old_req_id = (unsigned long) scpnt->host_scribble;
 	int retval = SUCCESS;
 
 	scsi_host = scpnt->device->host;
 	adapter = (struct zfcp_adapter *) scsi_host->hostdata[0];
-	unit = (struct zfcp_unit *) scpnt->device->hostdata;
-
-	ZFCP_LOG_INFO("aborting scsi_cmnd=%p on adapter %s\n",
-		      scpnt, zfcp_get_busid_by_adapter(adapter));
+	unit = scpnt->device->hostdata;
 
 	/* avoid race condition between late normal completion and abort */
 	write_lock_irqsave(&adapter->abort_lock, flags);
 
 	/* Check whether corresponding fsf_req is still pending */
 	spin_lock(&adapter->req_list_lock);
-	fsf_req = zfcp_reqlist_find(adapter,
-				    (unsigned long) scpnt->host_scribble);
+	fsf_req = zfcp_reqlist_find(adapter, old_req_id);
 	spin_unlock(&adapter->req_list_lock);
 	if (!fsf_req) {
 		write_unlock_irqrestore(&adapter->abort_lock, flags);
 		zfcp_scsi_dbf_event_abort("lte1", adapter, scpnt, NULL, 0);
-		retval = SUCCESS;
-		goto out;
+		return retval;
 	}
-	fsf_req->data = 0;
+	fsf_req->data = NULL;
 	fsf_req->status |= ZFCP_STATUS_FSFREQ_ABORTING;
-	old_req_id = fsf_req->req_id;
 
 	/* don't access old fsf_req after releasing the abort_lock */
 	write_unlock_irqrestore(&adapter->abort_lock, flags);
 
 	fsf_req = zfcp_fsf_abort_fcp_command(old_req_id, adapter, unit, 0);
 	if (!fsf_req) {
-		ZFCP_LOG_INFO("error: initiation of Abort FCP Cmnd failed\n");
 		zfcp_scsi_dbf_event_abort("nres", adapter, scpnt, NULL,
 					  old_req_id);
 		retval = FAILED;
-		goto out;
+		return retval;
 	}
 
 	__wait_event(fsf_req->completion_wq,
@@ -432,66 +201,29 @@
 
 	if (fsf_req->status & ZFCP_STATUS_FSFREQ_ABORTSUCCEEDED) {
 		zfcp_scsi_dbf_event_abort("okay", adapter, scpnt, fsf_req, 0);
-		retval = SUCCESS;
 	} else if (fsf_req->status & ZFCP_STATUS_FSFREQ_ABORTNOTNEEDED) {
 		zfcp_scsi_dbf_event_abort("lte2", adapter, scpnt, fsf_req, 0);
-		retval = SUCCESS;
 	} else {
 		zfcp_scsi_dbf_event_abort("fail", adapter, scpnt, fsf_req, 0);
 		retval = FAILED;
 	}
 	zfcp_fsf_req_free(fsf_req);
- out:
+
 	return retval;
 }
 
-static int zfcp_scsi_eh_device_reset_handler(struct scsi_cmnd *scpnt)
-{
-	int retval;
-	struct zfcp_unit *unit = scpnt->device->hostdata;
-
-	if (!unit) {
-		WARN_ON(1);
-		return SUCCESS;
-	}
-	retval = zfcp_task_management_function(unit,
-					       FCP_LOGICAL_UNIT_RESET,
-					       scpnt);
-	return retval ? FAILED : SUCCESS;
-}
-
-static int zfcp_scsi_eh_target_reset_handler(struct scsi_cmnd *scpnt)
-{
-	int retval;
-	struct zfcp_unit *unit = scpnt->device->hostdata;
-
-	if (!unit) {
-		WARN_ON(1);
-		return SUCCESS;
-	}
-	retval = zfcp_task_management_function(unit, FCP_TARGET_RESET, scpnt);
-	return retval ? FAILED : SUCCESS;
-}
-
-static int
-zfcp_task_management_function(struct zfcp_unit *unit, u8 tm_flags,
-			      struct scsi_cmnd *scpnt)
+static int zfcp_task_mgmt_function(struct zfcp_unit *unit, u8 tm_flags,
+					 struct scsi_cmnd *scpnt)
 {
 	struct zfcp_adapter *adapter = unit->port->adapter;
 	struct zfcp_fsf_req *fsf_req;
-	int retval = 0;
+	int retval = SUCCESS;
 
 	/* issue task management function */
-	fsf_req = zfcp_fsf_send_fcp_command_task_management
-		(adapter, unit, tm_flags, 0);
+	fsf_req = zfcp_fsf_send_fcp_ctm(adapter, unit, tm_flags, 0);
 	if (!fsf_req) {
-		ZFCP_LOG_INFO("error: creation of task management request "
-			      "failed for unit 0x%016Lx on port 0x%016Lx on  "
-			      "adapter %s\n", unit->fcp_lun, unit->port->wwpn,
-			      zfcp_get_busid_by_adapter(adapter));
 		zfcp_scsi_dbf_event_devreset("nres", tm_flags, unit, scpnt);
-		retval = -ENOMEM;
-		goto out;
+		return FAILED;
 	}
 
 	__wait_event(fsf_req->completion_wq,
@@ -502,87 +234,90 @@
 	 */
 	if (fsf_req->status & ZFCP_STATUS_FSFREQ_TMFUNCFAILED) {
 		zfcp_scsi_dbf_event_devreset("fail", tm_flags, unit, scpnt);
-		retval = -EIO;
+		retval = FAILED;
 	} else if (fsf_req->status & ZFCP_STATUS_FSFREQ_TMFUNCNOTSUPP) {
 		zfcp_scsi_dbf_event_devreset("nsup", tm_flags, unit, scpnt);
-		retval = -ENOTSUPP;
+		retval = FAILED;
 	} else
 		zfcp_scsi_dbf_event_devreset("okay", tm_flags, unit, scpnt);
 
 	zfcp_fsf_req_free(fsf_req);
- out:
+
 	return retval;
 }
 
-/**
- * zfcp_scsi_eh_host_reset_handler - handler for host reset
- */
+static int zfcp_scsi_eh_device_reset_handler(struct scsi_cmnd *scpnt)
+{
+	struct zfcp_unit *unit = scpnt->device->hostdata;
+
+	if (!unit) {
+		WARN_ON(1);
+		return SUCCESS;
+	}
+	return zfcp_task_mgmt_function(unit, FCP_LOGICAL_UNIT_RESET, scpnt);
+}
+
+static int zfcp_scsi_eh_target_reset_handler(struct scsi_cmnd *scpnt)
+{
+	struct zfcp_unit *unit = scpnt->device->hostdata;
+
+	if (!unit) {
+		WARN_ON(1);
+		return SUCCESS;
+	}
+	return zfcp_task_mgmt_function(unit, FCP_TARGET_RESET, scpnt);
+}
+
 static int zfcp_scsi_eh_host_reset_handler(struct scsi_cmnd *scpnt)
 {
 	struct zfcp_unit *unit;
 	struct zfcp_adapter *adapter;
 
-	unit = (struct zfcp_unit*) scpnt->device->hostdata;
+	unit = scpnt->device->hostdata;
 	adapter = unit->port->adapter;
-
-	ZFCP_LOG_NORMAL("host reset because of problems with "
-		"unit 0x%016Lx on port 0x%016Lx, adapter %s\n",
-		unit->fcp_lun, unit->port->wwpn,
-		zfcp_get_busid_by_adapter(unit->port->adapter));
-
 	zfcp_erp_adapter_reopen(adapter, 0, 141, scpnt);
 	zfcp_erp_wait(adapter);
 
 	return SUCCESS;
 }
 
-int
-zfcp_adapter_scsi_register(struct zfcp_adapter *adapter)
+int zfcp_adapter_scsi_register(struct zfcp_adapter *adapter)
 {
-	int retval = 0;
-	static unsigned int unique_id = 0;
+	struct ccw_dev_id dev_id;
 
 	if (adapter->scsi_host)
-		goto out;
+		return 0;
 
+	ccw_device_get_id(adapter->ccw_device, &dev_id);
 	/* register adapter as SCSI host with mid layer of SCSI stack */
 	adapter->scsi_host = scsi_host_alloc(&zfcp_data.scsi_host_template,
 					     sizeof (struct zfcp_adapter *));
 	if (!adapter->scsi_host) {
-		ZFCP_LOG_NORMAL("error: registration with SCSI stack failed "
-				"for adapter %s ",
-				zfcp_get_busid_by_adapter(adapter));
-		retval = -EIO;
-		goto out;
+		dev_err(&adapter->ccw_device->dev,
+			"registration with SCSI stack failed.");
+		return -EIO;
 	}
-	ZFCP_LOG_DEBUG("host registered, scsi_host=%p\n", adapter->scsi_host);
 
 	/* tell the SCSI stack some characteristics of this adapter */
 	adapter->scsi_host->max_id = 1;
 	adapter->scsi_host->max_lun = 1;
 	adapter->scsi_host->max_channel = 0;
-	adapter->scsi_host->unique_id = unique_id++;	/* FIXME */
-	adapter->scsi_host->max_cmd_len = ZFCP_MAX_SCSI_CMND_LENGTH;
+	adapter->scsi_host->unique_id = dev_id.devno;
+	adapter->scsi_host->max_cmd_len = 255;
 	adapter->scsi_host->transportt = zfcp_data.scsi_transport_template;
 
-	/*
-	 * save a pointer to our own adapter data structure within
-	 * hostdata field of SCSI host data structure
-	 */
 	adapter->scsi_host->hostdata[0] = (unsigned long) adapter;
 
 	if (scsi_add_host(adapter->scsi_host, &adapter->ccw_device->dev)) {
 		scsi_host_put(adapter->scsi_host);
-		retval = -EIO;
-		goto out;
+		return -EIO;
 	}
 	atomic_set_mask(ZFCP_STATUS_ADAPTER_REGISTERED, &adapter->status);
- out:
-	return retval;
+
+	return 0;
 }
 
-void
-zfcp_adapter_scsi_unregister(struct zfcp_adapter *adapter)
+void zfcp_adapter_scsi_unregister(struct zfcp_adapter *adapter)
 {
 	struct Scsi_Host *shost;
 	struct zfcp_port *port;
@@ -590,10 +325,12 @@
 	shost = adapter->scsi_host;
 	if (!shost)
 		return;
+
 	read_lock_irq(&zfcp_data.config_lock);
 	list_for_each_entry(port, &adapter->port_list_head, list)
 		if (port->rport)
 			port->rport = NULL;
+
 	read_unlock_irq(&zfcp_data.config_lock);
 	fc_remove_host(shost);
 	scsi_remove_host(shost);
@@ -604,9 +341,6 @@
 	return;
 }
 
-/*
- * Support functions for FC transport class
- */
 static struct fc_host_statistics*
 zfcp_init_fc_host_stats(struct zfcp_adapter *adapter)
 {
@@ -622,13 +356,12 @@
 	return adapter->fc_stats;
 }
 
-static void
-zfcp_adjust_fc_host_stats(struct fc_host_statistics *fc_stats,
-			  struct fsf_qtcb_bottom_port *data,
-			  struct fsf_qtcb_bottom_port *old)
+static void zfcp_adjust_fc_host_stats(struct fc_host_statistics *fc_stats,
+				      struct fsf_qtcb_bottom_port *data,
+				      struct fsf_qtcb_bottom_port *old)
 {
-	fc_stats->seconds_since_last_reset = data->seconds_since_last_reset -
-		old->seconds_since_last_reset;
+	fc_stats->seconds_since_last_reset =
+		data->seconds_since_last_reset - old->seconds_since_last_reset;
 	fc_stats->tx_frames = data->tx_frames - old->tx_frames;
 	fc_stats->tx_words = data->tx_words - old->tx_words;
 	fc_stats->rx_frames = data->rx_frames - old->rx_frames;
@@ -639,26 +372,25 @@
 	fc_stats->dumped_frames = data->dumped_frames - old->dumped_frames;
 	fc_stats->link_failure_count = data->link_failure - old->link_failure;
 	fc_stats->loss_of_sync_count = data->loss_of_sync - old->loss_of_sync;
-	fc_stats->loss_of_signal_count = data->loss_of_signal -
-		old->loss_of_signal;
-	fc_stats->prim_seq_protocol_err_count = data->psp_error_counts -
-		old->psp_error_counts;
-	fc_stats->invalid_tx_word_count = data->invalid_tx_words -
-		old->invalid_tx_words;
+	fc_stats->loss_of_signal_count =
+		data->loss_of_signal - old->loss_of_signal;
+	fc_stats->prim_seq_protocol_err_count =
+		data->psp_error_counts - old->psp_error_counts;
+	fc_stats->invalid_tx_word_count =
+		data->invalid_tx_words - old->invalid_tx_words;
 	fc_stats->invalid_crc_count = data->invalid_crcs - old->invalid_crcs;
-	fc_stats->fcp_input_requests = data->input_requests -
-		old->input_requests;
-	fc_stats->fcp_output_requests = data->output_requests -
-		old->output_requests;
-	fc_stats->fcp_control_requests = data->control_requests -
-		old->control_requests;
+	fc_stats->fcp_input_requests =
+		data->input_requests - old->input_requests;
+	fc_stats->fcp_output_requests =
+		data->output_requests - old->output_requests;
+	fc_stats->fcp_control_requests =
+		data->control_requests - old->control_requests;
 	fc_stats->fcp_input_megabytes = data->input_mb - old->input_mb;
 	fc_stats->fcp_output_megabytes = data->output_mb - old->output_mb;
 }
 
-static void
-zfcp_set_fc_host_stats(struct fc_host_statistics *fc_stats,
-		       struct fsf_qtcb_bottom_port *data)
+static void zfcp_set_fc_host_stats(struct fc_host_statistics *fc_stats,
+				   struct fsf_qtcb_bottom_port *data)
 {
 	fc_stats->seconds_since_last_reset = data->seconds_since_last_reset;
 	fc_stats->tx_frames = data->tx_frames;
@@ -682,22 +414,14 @@
 	fc_stats->fcp_output_megabytes = data->output_mb;
 }
 
-/**
- * zfcp_get_fc_host_stats - provide fc_host_statistics for scsi_transport_fc
- *
- * assumption: scsi_transport_fc synchronizes calls of
- *             get_fc_host_stats and reset_fc_host_stats
- *             (XXX to be checked otherwise introduce locking)
- */
-static struct fc_host_statistics *
-zfcp_get_fc_host_stats(struct Scsi_Host *shost)
+static struct fc_host_statistics *zfcp_get_fc_host_stats(struct Scsi_Host *host)
 {
 	struct zfcp_adapter *adapter;
 	struct fc_host_statistics *fc_stats;
 	struct fsf_qtcb_bottom_port *data;
 	int ret;
 
-	adapter = (struct zfcp_adapter *)shost->hostdata[0];
+	adapter = (struct zfcp_adapter *)host->hostdata[0];
 	fc_stats = zfcp_init_fc_host_stats(adapter);
 	if (!fc_stats)
 		return NULL;
@@ -709,26 +433,25 @@
 	ret = zfcp_fsf_exchange_port_data_sync(adapter, data);
 	if (ret) {
 		kfree(data);
-		return NULL; /* XXX return zeroed fc_stats? */
+		return NULL;
 	}
 
 	if (adapter->stats_reset &&
 	    ((jiffies/HZ - adapter->stats_reset) <
-	     data->seconds_since_last_reset)) {
+	     data->seconds_since_last_reset))
 		zfcp_adjust_fc_host_stats(fc_stats, data,
 					  adapter->stats_reset_data);
-	} else
+	else
 		zfcp_set_fc_host_stats(fc_stats, data);
 
 	kfree(data);
 	return fc_stats;
 }
 
-static void
-zfcp_reset_fc_host_stats(struct Scsi_Host *shost)
+static void zfcp_reset_fc_host_stats(struct Scsi_Host *shost)
 {
 	struct zfcp_adapter *adapter;
-	struct fsf_qtcb_bottom_port *data, *old_data;
+	struct fsf_qtcb_bottom_port *data;
 	int ret;
 
 	adapter = (struct zfcp_adapter *)shost->hostdata[0];
@@ -737,17 +460,33 @@
 		return;
 
 	ret = zfcp_fsf_exchange_port_data_sync(adapter, data);
-	if (ret) {
+	if (ret)
 		kfree(data);
-	} else {
+	else {
 		adapter->stats_reset = jiffies/HZ;
-		old_data = adapter->stats_reset_data;
+		kfree(adapter->stats_reset_data);
 		adapter->stats_reset_data = data; /* finally freed in
-						     adater_dequeue */
-		kfree(old_data);
+						     adapter_dequeue */
 	}
 }
 
+static void zfcp_get_host_port_state(struct Scsi_Host *shost)
+{
+	struct zfcp_adapter *adapter =
+		(struct zfcp_adapter *)shost->hostdata[0];
+	int status = atomic_read(&adapter->status);
+
+	if ((status & ZFCP_STATUS_COMMON_RUNNING) &&
+	    !(status & ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED))
+		fc_host_port_state(shost) = FC_PORTSTATE_ONLINE;
+	else if (status & ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED)
+		fc_host_port_state(shost) = FC_PORTSTATE_LINKDOWN;
+	else if (status & ZFCP_STATUS_COMMON_ERP_FAILED)
+		fc_host_port_state(shost) = FC_PORTSTATE_ERROR;
+	else
+		fc_host_port_state(shost) = FC_PORTSTATE_UNKNOWN;
+}
+
 static void zfcp_set_rport_dev_loss_tmo(struct fc_rport *rport, u32 timeout)
 {
 	rport->dev_loss_tmo = timeout;
@@ -770,6 +509,8 @@
 	.get_fc_host_stats = zfcp_get_fc_host_stats,
 	.reset_fc_host_stats = zfcp_reset_fc_host_stats,
 	.set_rport_dev_loss_tmo = zfcp_set_rport_dev_loss_tmo,
+	.get_host_port_state = zfcp_get_host_port_state,
+	.show_host_port_state = 1,
 	/* no functions registered for following dynamic attributes but
 	   directly set by LLDD */
 	.show_host_port_type = 1,
@@ -778,149 +519,26 @@
 	.disable_target_scan = 1,
 };
 
-/**
- * ZFCP_DEFINE_SCSI_ATTR
- * @_name:   name of show attribute
- * @_format: format string
- * @_value:  value to print
- *
- * Generates attribute for a unit.
- */
-#define ZFCP_DEFINE_SCSI_ATTR(_name, _format, _value)                    \
-static ssize_t zfcp_sysfs_scsi_##_name##_show(struct device *dev, struct device_attribute *attr,        \
-                                              char *buf)                 \
-{                                                                        \
-        struct scsi_device *sdev;                                        \
-        struct zfcp_unit *unit;                                          \
-                                                                         \
-        sdev = to_scsi_device(dev);                                      \
-        unit = sdev->hostdata;                                           \
-        return sprintf(buf, _format, _value);                            \
-}                                                                        \
-                                                                         \
-static DEVICE_ATTR(_name, S_IRUGO, zfcp_sysfs_scsi_##_name##_show, NULL);
-
-ZFCP_DEFINE_SCSI_ATTR(hba_id, "%s\n", zfcp_get_busid_by_unit(unit));
-ZFCP_DEFINE_SCSI_ATTR(wwpn, "0x%016llx\n", unit->port->wwpn);
-ZFCP_DEFINE_SCSI_ATTR(fcp_lun, "0x%016llx\n", unit->fcp_lun);
-
-static struct device_attribute *zfcp_sysfs_sdev_attrs[] = {
-	&dev_attr_fcp_lun,
-	&dev_attr_wwpn,
-	&dev_attr_hba_id,
-	NULL
+struct zfcp_data zfcp_data = {
+	.scsi_host_template = {
+		.name			 = "zfcp",
+		.module			 = THIS_MODULE,
+		.proc_name		 = "zfcp",
+		.slave_alloc		 = zfcp_scsi_slave_alloc,
+		.slave_configure	 = zfcp_scsi_slave_configure,
+		.slave_destroy		 = zfcp_scsi_slave_destroy,
+		.queuecommand		 = zfcp_scsi_queuecommand,
+		.eh_abort_handler	 = zfcp_scsi_eh_abort_handler,
+		.eh_device_reset_handler = zfcp_scsi_eh_device_reset_handler,
+		.eh_target_reset_handler = zfcp_scsi_eh_target_reset_handler,
+		.eh_host_reset_handler	 = zfcp_scsi_eh_host_reset_handler,
+		.can_queue		 = 4096,
+		.this_id		 = -1,
+		.sg_tablesize		 = ZFCP_MAX_SBALES_PER_REQ,
+		.cmd_per_lun		 = 1,
+		.use_clustering		 = 1,
+		.sdev_attrs		 = zfcp_sysfs_sdev_attrs,
+		.max_sectors		 = (ZFCP_MAX_SBALES_PER_REQ * 8),
+		.shost_attrs		 = zfcp_sysfs_shost_attrs,
+	},
 };
-
-static ssize_t zfcp_sysfs_adapter_util_show(struct device *dev,
-					    struct device_attribute *attr,
-					    char *buf)
-{
-	struct Scsi_Host *scsi_host = dev_to_shost(dev);
-	struct fsf_qtcb_bottom_port *qtcb_port;
-	int retval;
-	struct zfcp_adapter *adapter;
-
-	adapter = (struct zfcp_adapter *) scsi_host->hostdata[0];
-	if (!(adapter->adapter_features & FSF_FEATURE_MEASUREMENT_DATA))
-		return -EOPNOTSUPP;
-
-	qtcb_port = kzalloc(sizeof(struct fsf_qtcb_bottom_port), GFP_KERNEL);
-	if (!qtcb_port)
-		return -ENOMEM;
-
-	retval = zfcp_fsf_exchange_port_data_sync(adapter, qtcb_port);
-	if (!retval)
-		retval = sprintf(buf, "%u %u %u\n", qtcb_port->cp_util,
-				 qtcb_port->cb_util, qtcb_port->a_util);
-	kfree(qtcb_port);
-	return retval;
-}
-
-static int zfcp_sysfs_adapter_ex_config(struct device *dev,
-					struct fsf_statistics_info *stat_inf)
-{
-	int retval;
-	struct fsf_qtcb_bottom_config *qtcb_config;
-	struct Scsi_Host *scsi_host = dev_to_shost(dev);
-	struct zfcp_adapter *adapter;
-
-	adapter = (struct zfcp_adapter *) scsi_host->hostdata[0];
-	if (!(adapter->adapter_features & FSF_FEATURE_MEASUREMENT_DATA))
-		return -EOPNOTSUPP;
-
-	qtcb_config = kzalloc(sizeof(struct fsf_qtcb_bottom_config),
-			       GFP_KERNEL);
-	if (!qtcb_config)
-		return -ENOMEM;
-
-	retval = zfcp_fsf_exchange_config_data_sync(adapter, qtcb_config);
-	if (!retval)
-		*stat_inf = qtcb_config->stat_info;
-
-	kfree(qtcb_config);
-	return retval;
-}
-
-static ssize_t zfcp_sysfs_adapter_request_show(struct device *dev,
-					       struct device_attribute *attr,
-					       char *buf)
-{
-	struct fsf_statistics_info stat_info;
-	int retval;
-
-	retval = zfcp_sysfs_adapter_ex_config(dev, &stat_info);
-	if (retval)
-		return retval;
-
-	return sprintf(buf, "%llu %llu %llu\n",
-		       (unsigned long long) stat_info.input_req,
-		       (unsigned long long) stat_info.output_req,
-		       (unsigned long long) stat_info.control_req);
-}
-
-static ssize_t zfcp_sysfs_adapter_mb_show(struct device *dev,
-					  struct device_attribute *attr,
-					  char *buf)
-{
-	struct fsf_statistics_info stat_info;
-	int retval;
-
-	retval = zfcp_sysfs_adapter_ex_config(dev, &stat_info);
-	if (retval)
-		return retval;
-
-	return sprintf(buf, "%llu %llu\n",
-		       (unsigned long long) stat_info.input_mb,
-		       (unsigned long long) stat_info.output_mb);
-}
-
-static ssize_t zfcp_sysfs_adapter_sec_active_show(struct device *dev,
-						  struct device_attribute *attr,
-						  char *buf)
-{
-	struct fsf_statistics_info stat_info;
-	int retval;
-
-	retval = zfcp_sysfs_adapter_ex_config(dev, &stat_info);
-	if (retval)
-		return retval;
-
-	return sprintf(buf, "%llu\n",
-		       (unsigned long long) stat_info.seconds_act);
-}
-
-static DEVICE_ATTR(utilization, S_IRUGO, zfcp_sysfs_adapter_util_show, NULL);
-static DEVICE_ATTR(requests, S_IRUGO, zfcp_sysfs_adapter_request_show, NULL);
-static DEVICE_ATTR(megabytes, S_IRUGO, zfcp_sysfs_adapter_mb_show, NULL);
-static DEVICE_ATTR(seconds_active, S_IRUGO,
-		   zfcp_sysfs_adapter_sec_active_show, NULL);
-
-static struct device_attribute *zfcp_a_stats_attrs[] = {
-	&dev_attr_utilization,
-	&dev_attr_requests,
-	&dev_attr_megabytes,
-	&dev_attr_seconds_active,
-	NULL
-};
-
-#undef ZFCP_LOG_AREA
diff --git a/drivers/s390/scsi/zfcp_sysfs.c b/drivers/s390/scsi/zfcp_sysfs.c
new file mode 100644
index 0000000..2e85c6c
--- /dev/null
+++ b/drivers/s390/scsi/zfcp_sysfs.c
@@ -0,0 +1,496 @@
+/*
+ * zfcp device driver
+ *
+ * sysfs attributes.
+ *
+ * Copyright IBM Corporation 2008
+ */
+
+#include "zfcp_ext.h"
+
+#define ZFCP_DEV_ATTR(_feat, _name, _mode, _show, _store) \
+struct device_attribute dev_attr_##_feat##_##_name = __ATTR(_name, _mode,\
+							    _show, _store)
+#define ZFCP_DEFINE_ATTR(_feat_def, _feat, _name, _format, _value)	       \
+static ssize_t zfcp_sysfs_##_feat##_##_name##_show(struct device *dev,	       \
+						   struct device_attribute *at,\
+						   char *buf)		       \
+{									       \
+	struct _feat_def *_feat = dev_get_drvdata(dev);			       \
+									       \
+	return sprintf(buf, _format, _value);				       \
+}									       \
+static ZFCP_DEV_ATTR(_feat, _name, S_IRUGO,				       \
+		     zfcp_sysfs_##_feat##_##_name##_show, NULL);
+
+ZFCP_DEFINE_ATTR(zfcp_adapter, adapter, status, "0x%08x\n",
+		 atomic_read(&adapter->status));
+ZFCP_DEFINE_ATTR(zfcp_adapter, adapter, peer_wwnn, "0x%016llx\n",
+		 adapter->peer_wwnn);
+ZFCP_DEFINE_ATTR(zfcp_adapter, adapter, peer_wwpn, "0x%016llx\n",
+		 adapter->peer_wwpn);
+ZFCP_DEFINE_ATTR(zfcp_adapter, adapter, peer_d_id, "0x%06x\n",
+		 adapter->peer_d_id);
+ZFCP_DEFINE_ATTR(zfcp_adapter, adapter, card_version, "0x%04x\n",
+		 adapter->hydra_version);
+ZFCP_DEFINE_ATTR(zfcp_adapter, adapter, lic_version, "0x%08x\n",
+		 adapter->fsf_lic_version);
+ZFCP_DEFINE_ATTR(zfcp_adapter, adapter, hardware_version, "0x%08x\n",
+		 adapter->hardware_version);
+ZFCP_DEFINE_ATTR(zfcp_adapter, adapter, in_recovery, "%d\n",
+		 (atomic_read(&adapter->status) &
+		  ZFCP_STATUS_COMMON_ERP_INUSE) != 0);
+
+ZFCP_DEFINE_ATTR(zfcp_port, port, status, "0x%08x\n",
+		 atomic_read(&port->status));
+ZFCP_DEFINE_ATTR(zfcp_port, port, in_recovery, "%d\n",
+		 (atomic_read(&port->status) &
+		  ZFCP_STATUS_COMMON_ERP_INUSE) != 0);
+ZFCP_DEFINE_ATTR(zfcp_port, port, access_denied, "%d\n",
+		 (atomic_read(&port->status) &
+		  ZFCP_STATUS_COMMON_ACCESS_DENIED) != 0);
+
+ZFCP_DEFINE_ATTR(zfcp_unit, unit, status, "0x%08x\n",
+		 atomic_read(&unit->status));
+ZFCP_DEFINE_ATTR(zfcp_unit, unit, in_recovery, "%d\n",
+		 (atomic_read(&unit->status) &
+		  ZFCP_STATUS_COMMON_ERP_INUSE) != 0);
+ZFCP_DEFINE_ATTR(zfcp_unit, unit, access_denied, "%d\n",
+		 (atomic_read(&unit->status) &
+		  ZFCP_STATUS_COMMON_ACCESS_DENIED) != 0);
+ZFCP_DEFINE_ATTR(zfcp_unit, unit, access_shared, "%d\n",
+		 (atomic_read(&unit->status) &
+		  ZFCP_STATUS_UNIT_SHARED) != 0);
+ZFCP_DEFINE_ATTR(zfcp_unit, unit, access_readonly, "%d\n",
+		 (atomic_read(&unit->status) &
+		  ZFCP_STATUS_UNIT_READONLY) != 0);
+
+#define ZFCP_SYSFS_FAILED(_feat_def, _feat, _adapter, _mod_id, _reopen_id)     \
+static ssize_t zfcp_sysfs_##_feat##_failed_show(struct device *dev,	       \
+						struct device_attribute *attr, \
+						char *buf)		       \
+{									       \
+	struct _feat_def *_feat = dev_get_drvdata(dev);			       \
+									       \
+	if (atomic_read(&_feat->status) & ZFCP_STATUS_COMMON_ERP_FAILED)       \
+		return sprintf(buf, "1\n");				       \
+	else								       \
+		return sprintf(buf, "0\n");				       \
+}									       \
+static ssize_t zfcp_sysfs_##_feat##_failed_store(struct device *dev,	       \
+						 struct device_attribute *attr,\
+						 const char *buf, size_t count)\
+{									       \
+	struct _feat_def *_feat = dev_get_drvdata(dev);			       \
+	unsigned long val;						       \
+	int retval = 0;							       \
+									       \
+	down(&zfcp_data.config_sema);					       \
+	if (atomic_read(&_feat->status) & ZFCP_STATUS_COMMON_REMOVE) {	       \
+		retval = -EBUSY;					       \
+		goto out;						       \
+	}								       \
+									       \
+	if (strict_strtoul(buf, 0, &val) || val != 0) {			       \
+		retval = -EINVAL;					       \
+		goto out;						       \
+	}								       \
+									       \
+	zfcp_erp_modify_##_feat##_status(_feat, _mod_id, NULL,		       \
+					 ZFCP_STATUS_COMMON_RUNNING, ZFCP_SET);\
+	zfcp_erp_##_feat##_reopen(_feat, ZFCP_STATUS_COMMON_ERP_FAILED,	       \
+				  _reopen_id, NULL);			       \
+	zfcp_erp_wait(_adapter);					       \
+out:									       \
+	up(&zfcp_data.config_sema);					       \
+	return retval ? retval : (ssize_t) count;			       \
+}									       \
+static ZFCP_DEV_ATTR(_feat, failed, S_IWUSR | S_IRUGO,			       \
+		     zfcp_sysfs_##_feat##_failed_show,			       \
+		     zfcp_sysfs_##_feat##_failed_store);
+
+ZFCP_SYSFS_FAILED(zfcp_adapter, adapter, adapter, 44, 93);
+ZFCP_SYSFS_FAILED(zfcp_port, port, port->adapter, 45, 96);
+ZFCP_SYSFS_FAILED(zfcp_unit, unit, unit->port->adapter, 46, 97);
+
+static ssize_t zfcp_sysfs_port_rescan_store(struct device *dev,
+					    struct device_attribute *attr,
+					    const char *buf, size_t count)
+{
+	struct zfcp_adapter *adapter = dev_get_drvdata(dev);
+	int ret;
+
+	if (atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_REMOVE)
+		return -EBUSY;
+
+	ret = zfcp_scan_ports(adapter);
+	return ret ? ret : (ssize_t) count;
+}
+static ZFCP_DEV_ATTR(adapter, port_rescan, S_IWUSR, NULL,
+		     zfcp_sysfs_port_rescan_store);
+
+static ssize_t zfcp_sysfs_port_remove_store(struct device *dev,
+					    struct device_attribute *attr,
+					    const char *buf, size_t count)
+{
+	struct zfcp_adapter *adapter = dev_get_drvdata(dev);
+	struct zfcp_port *port;
+	wwn_t wwpn;
+	int retval = 0;
+
+	down(&zfcp_data.config_sema);
+	if (atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_REMOVE) {
+		retval = -EBUSY;
+		goto out;
+	}
+
+	if (strict_strtoull(buf, 0, &wwpn)) {
+		retval = -EINVAL;
+		goto out;
+	}
+
+	write_lock_irq(&zfcp_data.config_lock);
+	port = zfcp_get_port_by_wwpn(adapter, wwpn);
+	if (port && (atomic_read(&port->refcount) == 0)) {
+		zfcp_port_get(port);
+		atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status);
+		list_move(&port->list, &adapter->port_remove_lh);
+	} else
+		port = NULL;
+	write_unlock_irq(&zfcp_data.config_lock);
+
+	if (!port) {
+		retval = -ENXIO;
+		goto out;
+	}
+
+	zfcp_erp_port_shutdown(port, 0, 92, NULL);
+	zfcp_erp_wait(adapter);
+	zfcp_port_put(port);
+	zfcp_port_dequeue(port);
+ out:
+	up(&zfcp_data.config_sema);
+	return retval ? retval : (ssize_t) count;
+}
+static ZFCP_DEV_ATTR(adapter, port_remove, S_IWUSR, NULL,
+		     zfcp_sysfs_port_remove_store);
+
+static struct attribute *zfcp_adapter_attrs[] = {
+	&dev_attr_adapter_failed.attr,
+	&dev_attr_adapter_in_recovery.attr,
+	&dev_attr_adapter_port_remove.attr,
+	&dev_attr_adapter_port_rescan.attr,
+	&dev_attr_adapter_peer_wwnn.attr,
+	&dev_attr_adapter_peer_wwpn.attr,
+	&dev_attr_adapter_peer_d_id.attr,
+	&dev_attr_adapter_card_version.attr,
+	&dev_attr_adapter_lic_version.attr,
+	&dev_attr_adapter_status.attr,
+	&dev_attr_adapter_hardware_version.attr,
+	NULL
+};
+
+struct attribute_group zfcp_sysfs_adapter_attrs = {
+	.attrs = zfcp_adapter_attrs,
+};
+
+static ssize_t zfcp_sysfs_unit_add_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t count)
+{
+	struct zfcp_port *port = dev_get_drvdata(dev);
+	struct zfcp_unit *unit;
+	fcp_lun_t fcp_lun;
+	int retval = -EINVAL;
+
+	down(&zfcp_data.config_sema);
+	if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_REMOVE) {
+		retval = -EBUSY;
+		goto out;
+	}
+
+	if (strict_strtoull(buf, 0, &fcp_lun))
+		goto out;
+
+	unit = zfcp_unit_enqueue(port, fcp_lun);
+	if (IS_ERR(unit))
+		goto out;
+
+	retval = 0;
+
+	zfcp_erp_unit_reopen(unit, 0, 94, NULL);
+	zfcp_erp_wait(unit->port->adapter);
+	zfcp_unit_put(unit);
+out:
+	up(&zfcp_data.config_sema);
+	return retval ? retval : (ssize_t) count;
+}
+static DEVICE_ATTR(unit_add, S_IWUSR, NULL, zfcp_sysfs_unit_add_store);
+
+static ssize_t zfcp_sysfs_unit_remove_store(struct device *dev,
+					    struct device_attribute *attr,
+					    const char *buf, size_t count)
+{
+	struct zfcp_port *port = dev_get_drvdata(dev);
+	struct zfcp_unit *unit;
+	fcp_lun_t fcp_lun;
+	int retval = 0;
+
+	down(&zfcp_data.config_sema);
+	if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_REMOVE) {
+		retval = -EBUSY;
+		goto out;
+	}
+
+	if (strict_strtoull(buf, 0, &fcp_lun)) {
+		retval = -EINVAL;
+		goto out;
+	}
+
+	write_lock_irq(&zfcp_data.config_lock);
+	unit = zfcp_get_unit_by_lun(port, fcp_lun);
+	if (unit && (atomic_read(&unit->refcount) == 0)) {
+		zfcp_unit_get(unit);
+		atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &unit->status);
+		list_move(&unit->list, &port->unit_remove_lh);
+	} else
+		unit = NULL;
+
+	write_unlock_irq(&zfcp_data.config_lock);
+
+	if (!unit) {
+		retval = -ENXIO;
+		goto out;
+	}
+
+	zfcp_erp_unit_shutdown(unit, 0, 95, NULL);
+	zfcp_erp_wait(unit->port->adapter);
+	zfcp_unit_put(unit);
+	zfcp_unit_dequeue(unit);
+out:
+	up(&zfcp_data.config_sema);
+	return retval ? retval : (ssize_t) count;
+}
+static DEVICE_ATTR(unit_remove, S_IWUSR, NULL, zfcp_sysfs_unit_remove_store);
+
+static struct attribute *zfcp_port_ns_attrs[] = {
+	&dev_attr_port_failed.attr,
+	&dev_attr_port_in_recovery.attr,
+	&dev_attr_port_status.attr,
+	&dev_attr_port_access_denied.attr,
+	NULL
+};
+
+/**
+ * zfcp_sysfs_ns_port_attrs - sysfs attributes for nameserver
+ */
+struct attribute_group zfcp_sysfs_ns_port_attrs = {
+	.attrs = zfcp_port_ns_attrs,
+};
+
+static struct attribute *zfcp_port_no_ns_attrs[] = {
+	&dev_attr_unit_add.attr,
+	&dev_attr_unit_remove.attr,
+	&dev_attr_port_failed.attr,
+	&dev_attr_port_in_recovery.attr,
+	&dev_attr_port_status.attr,
+	&dev_attr_port_access_denied.attr,
+	NULL
+};
+
+/**
+ * zfcp_sysfs_port_attrs - sysfs attributes for all other ports
+ */
+struct attribute_group zfcp_sysfs_port_attrs = {
+	.attrs = zfcp_port_no_ns_attrs,
+};
+
+static struct attribute *zfcp_unit_attrs[] = {
+	&dev_attr_unit_failed.attr,
+	&dev_attr_unit_in_recovery.attr,
+	&dev_attr_unit_status.attr,
+	&dev_attr_unit_access_denied.attr,
+	&dev_attr_unit_access_shared.attr,
+	&dev_attr_unit_access_readonly.attr,
+	NULL
+};
+
+struct attribute_group zfcp_sysfs_unit_attrs = {
+	.attrs = zfcp_unit_attrs,
+};
+
+#define ZFCP_DEFINE_LATENCY_ATTR(_name) 				\
+static ssize_t								\
+zfcp_sysfs_unit_##_name##_latency_show(struct device *dev,		\
+				       struct device_attribute *attr,	\
+				       char *buf) {			\
+	struct scsi_device *sdev = to_scsi_device(dev);			\
+	struct zfcp_unit *unit = sdev->hostdata;			\
+	struct zfcp_latencies *lat = &unit->latencies;			\
+	struct zfcp_adapter *adapter = unit->port->adapter;		\
+	unsigned long flags;						\
+	unsigned long long fsum, fmin, fmax, csum, cmin, cmax, cc;	\
+									\
+	spin_lock_irqsave(&lat->lock, flags);				\
+	fsum = lat->_name.fabric.sum * adapter->timer_ticks;		\
+	fmin = lat->_name.fabric.min * adapter->timer_ticks;		\
+	fmax = lat->_name.fabric.max * adapter->timer_ticks;		\
+	csum = lat->_name.channel.sum * adapter->timer_ticks;		\
+	cmin = lat->_name.channel.min * adapter->timer_ticks;		\
+	cmax = lat->_name.channel.max * adapter->timer_ticks;		\
+	cc  = lat->_name.counter;					\
+	spin_unlock_irqrestore(&lat->lock, flags);			\
+									\
+	do_div(fsum, 1000);						\
+	do_div(fmin, 1000);						\
+	do_div(fmax, 1000);						\
+	do_div(csum, 1000);						\
+	do_div(cmin, 1000);						\
+	do_div(cmax, 1000);						\
+									\
+	return sprintf(buf, "%llu %llu %llu %llu %llu %llu %llu\n",	\
+		       fmin, fmax, fsum, cmin, cmax, csum, cc); 	\
+}									\
+static ssize_t								\
+zfcp_sysfs_unit_##_name##_latency_store(struct device *dev,		\
+					struct device_attribute *attr,	\
+					const char *buf, size_t count)	\
+{									\
+	struct scsi_device *sdev = to_scsi_device(dev);			\
+	struct zfcp_unit *unit = sdev->hostdata;			\
+	struct zfcp_latencies *lat = &unit->latencies;			\
+	unsigned long flags;						\
+									\
+	spin_lock_irqsave(&lat->lock, flags);				\
+	lat->_name.fabric.sum = 0;					\
+	lat->_name.fabric.min = 0xFFFFFFFF;				\
+	lat->_name.fabric.max = 0;					\
+	lat->_name.channel.sum = 0;					\
+	lat->_name.channel.min = 0xFFFFFFFF;				\
+	lat->_name.channel.max = 0;					\
+	lat->_name.counter = 0;						\
+	spin_unlock_irqrestore(&lat->lock, flags);			\
+									\
+	return (ssize_t) count;						\
+}									\
+static DEVICE_ATTR(_name##_latency, S_IWUSR | S_IRUGO,			\
+		   zfcp_sysfs_unit_##_name##_latency_show,		\
+		   zfcp_sysfs_unit_##_name##_latency_store);
+
+ZFCP_DEFINE_LATENCY_ATTR(read);
+ZFCP_DEFINE_LATENCY_ATTR(write);
+ZFCP_DEFINE_LATENCY_ATTR(cmd);
+
+#define ZFCP_DEFINE_SCSI_ATTR(_name, _format, _value)			\
+static ssize_t zfcp_sysfs_scsi_##_name##_show(struct device *dev,	\
+					      struct device_attribute *attr,\
+					      char *buf)                 \
+{                                                                        \
+	struct scsi_device *sdev  = to_scsi_device(dev);		 \
+	struct zfcp_unit *unit = sdev->hostdata;			 \
+									 \
+	return sprintf(buf, _format, _value);                            \
+}                                                                        \
+static DEVICE_ATTR(_name, S_IRUGO, zfcp_sysfs_scsi_##_name##_show, NULL);
+
+ZFCP_DEFINE_SCSI_ATTR(hba_id, "%s\n",
+	unit->port->adapter->ccw_device->dev.bus_id);
+ZFCP_DEFINE_SCSI_ATTR(wwpn, "0x%016llx\n", unit->port->wwpn);
+ZFCP_DEFINE_SCSI_ATTR(fcp_lun, "0x%016llx\n", unit->fcp_lun);
+
+struct device_attribute *zfcp_sysfs_sdev_attrs[] = {
+	&dev_attr_fcp_lun,
+	&dev_attr_wwpn,
+	&dev_attr_hba_id,
+	&dev_attr_read_latency,
+	&dev_attr_write_latency,
+	&dev_attr_cmd_latency,
+	NULL
+};
+
+static ssize_t zfcp_sysfs_adapter_util_show(struct device *dev,
+					    struct device_attribute *attr,
+					    char *buf)
+{
+	struct Scsi_Host *scsi_host = dev_to_shost(dev);
+	struct fsf_qtcb_bottom_port *qtcb_port;
+	struct zfcp_adapter *adapter;
+	int retval;
+
+	adapter = (struct zfcp_adapter *) scsi_host->hostdata[0];
+	if (!(adapter->adapter_features & FSF_FEATURE_MEASUREMENT_DATA))
+		return -EOPNOTSUPP;
+
+	qtcb_port = kzalloc(sizeof(struct fsf_qtcb_bottom_port), GFP_KERNEL);
+	if (!qtcb_port)
+		return -ENOMEM;
+
+	retval = zfcp_fsf_exchange_port_data_sync(adapter, qtcb_port);
+	if (!retval)
+		retval = sprintf(buf, "%u %u %u\n", qtcb_port->cp_util,
+				 qtcb_port->cb_util, qtcb_port->a_util);
+	kfree(qtcb_port);
+	return retval;
+}
+static DEVICE_ATTR(utilization, S_IRUGO, zfcp_sysfs_adapter_util_show, NULL);
+
+static int zfcp_sysfs_adapter_ex_config(struct device *dev,
+					struct fsf_statistics_info *stat_inf)
+{
+	struct Scsi_Host *scsi_host = dev_to_shost(dev);
+	struct fsf_qtcb_bottom_config *qtcb_config;
+	struct zfcp_adapter *adapter;
+	int retval;
+
+	adapter = (struct zfcp_adapter *) scsi_host->hostdata[0];
+	if (!(adapter->adapter_features & FSF_FEATURE_MEASUREMENT_DATA))
+		return -EOPNOTSUPP;
+
+	qtcb_config = kzalloc(sizeof(struct fsf_qtcb_bottom_config),
+			      GFP_KERNEL);
+	if (!qtcb_config)
+		return -ENOMEM;
+
+	retval = zfcp_fsf_exchange_config_data_sync(adapter, qtcb_config);
+	if (!retval)
+		*stat_inf = qtcb_config->stat_info;
+
+	kfree(qtcb_config);
+	return retval;
+}
+
+#define ZFCP_SHOST_ATTR(_name, _format, _arg...)			\
+static ssize_t zfcp_sysfs_adapter_##_name##_show(struct device *dev,	\
+						 struct device_attribute *attr,\
+						 char *buf)		\
+{									\
+	struct fsf_statistics_info stat_info;				\
+	int retval;							\
+									\
+	retval = zfcp_sysfs_adapter_ex_config(dev, &stat_info);		\
+	if (retval)							\
+		return retval;						\
+									\
+	return sprintf(buf, _format, ## _arg);				\
+}									\
+static DEVICE_ATTR(_name, S_IRUGO, zfcp_sysfs_adapter_##_name##_show, NULL);
+
+ZFCP_SHOST_ATTR(requests, "%llu %llu %llu\n",
+		(unsigned long long) stat_info.input_req,
+		(unsigned long long) stat_info.output_req,
+		(unsigned long long) stat_info.control_req);
+
+ZFCP_SHOST_ATTR(megabytes, "%llu %llu\n",
+		(unsigned long long) stat_info.input_mb,
+		(unsigned long long) stat_info.output_mb);
+
+ZFCP_SHOST_ATTR(seconds_active, "%llu\n",
+		(unsigned long long) stat_info.seconds_act);
+
+struct device_attribute *zfcp_sysfs_shost_attrs[] = {
+	&dev_attr_utilization,
+	&dev_attr_requests,
+	&dev_attr_megabytes,
+	&dev_attr_seconds_active,
+	NULL
+};
diff --git a/drivers/s390/scsi/zfcp_sysfs_adapter.c b/drivers/s390/scsi/zfcp_sysfs_adapter.c
deleted file mode 100644
index ccbba4d..0000000
--- a/drivers/s390/scsi/zfcp_sysfs_adapter.c
+++ /dev/null
@@ -1,270 +0,0 @@
-/*
- * This file is part of the zfcp device driver for
- * FCP adapters for IBM System z9 and zSeries.
- *
- * (C) Copyright IBM Corp. 2002, 2006
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include "zfcp_ext.h"
-
-#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_CONFIG
-
-/**
- * ZFCP_DEFINE_ADAPTER_ATTR
- * @_name:   name of show attribute
- * @_format: format string
- * @_value:  value to print
- *
- * Generates attributes for an adapter.
- */
-#define ZFCP_DEFINE_ADAPTER_ATTR(_name, _format, _value)                      \
-static ssize_t zfcp_sysfs_adapter_##_name##_show(struct device *dev, struct device_attribute *attr,          \
-						 char *buf)                   \
-{                                                                             \
-	struct zfcp_adapter *adapter;                                         \
-                                                                              \
-	adapter = dev_get_drvdata(dev);                                       \
-	return sprintf(buf, _format, _value);                                 \
-}                                                                             \
-                                                                              \
-static DEVICE_ATTR(_name, S_IRUGO, zfcp_sysfs_adapter_##_name##_show, NULL);
-
-ZFCP_DEFINE_ADAPTER_ATTR(status, "0x%08x\n", atomic_read(&adapter->status));
-ZFCP_DEFINE_ADAPTER_ATTR(peer_wwnn, "0x%016llx\n", adapter->peer_wwnn);
-ZFCP_DEFINE_ADAPTER_ATTR(peer_wwpn, "0x%016llx\n", adapter->peer_wwpn);
-ZFCP_DEFINE_ADAPTER_ATTR(peer_d_id, "0x%06x\n", adapter->peer_d_id);
-ZFCP_DEFINE_ADAPTER_ATTR(card_version, "0x%04x\n", adapter->hydra_version);
-ZFCP_DEFINE_ADAPTER_ATTR(lic_version, "0x%08x\n", adapter->fsf_lic_version);
-ZFCP_DEFINE_ADAPTER_ATTR(hardware_version, "0x%08x\n",
-			 adapter->hardware_version);
-ZFCP_DEFINE_ADAPTER_ATTR(in_recovery, "%d\n", atomic_test_mask
-			 (ZFCP_STATUS_COMMON_ERP_INUSE, &adapter->status));
-
-/**
- * zfcp_sysfs_port_add_store - add a port to sysfs tree
- * @dev: pointer to belonging device
- * @buf: pointer to input buffer
- * @count: number of bytes in buffer
- *
- * Store function of the "port_add" attribute of an adapter.
- */
-static ssize_t
-zfcp_sysfs_port_add_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
-{
-	wwn_t wwpn;
-	char *endp;
-	struct zfcp_adapter *adapter;
-	struct zfcp_port *port;
-	int retval = -EINVAL;
-
-	down(&zfcp_data.config_sema);
-
-	adapter = dev_get_drvdata(dev);
-	if (atomic_test_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status)) {
-		retval = -EBUSY;
-		goto out;
-	}
-
-	wwpn = simple_strtoull(buf, &endp, 0);
-	if ((endp + 1) < (buf + count))
-		goto out;
-
-	port = zfcp_port_enqueue(adapter, wwpn, 0, 0);
-	if (!port)
-		goto out;
-
-	retval = 0;
-
-	zfcp_erp_port_reopen(port, 0, 91, NULL);
-	zfcp_erp_wait(port->adapter);
-	zfcp_port_put(port);
- out:
-	up(&zfcp_data.config_sema);
-	return retval ? retval : (ssize_t) count;
-}
-
-static DEVICE_ATTR(port_add, S_IWUSR, NULL, zfcp_sysfs_port_add_store);
-
-/**
- * zfcp_sysfs_port_remove_store - remove a port from sysfs tree
- * @dev: pointer to belonging device
- * @buf: pointer to input buffer
- * @count: number of bytes in buffer
- *
- * Store function of the "port_remove" attribute of an adapter.
- */
-static ssize_t
-zfcp_sysfs_port_remove_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
-{
-	struct zfcp_adapter *adapter;
-	struct zfcp_port *port;
-	wwn_t wwpn;
-	char *endp;
-	int retval = 0;
-
-	down(&zfcp_data.config_sema);
-
-	adapter = dev_get_drvdata(dev);
-	if (atomic_test_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status)) {
-		retval = -EBUSY;
-		goto out;
-	}
-
-	wwpn = simple_strtoull(buf, &endp, 0);
-	if ((endp + 1) < (buf + count)) {
-		retval = -EINVAL;
-		goto out;
-	}
-
-	write_lock_irq(&zfcp_data.config_lock);
-	port = zfcp_get_port_by_wwpn(adapter, wwpn);
-	if (port && (atomic_read(&port->refcount) == 0)) {
-		zfcp_port_get(port);
-		atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status);
-		list_move(&port->list, &adapter->port_remove_lh);
-	}
-	else {
-		port = NULL;
-	}
-	write_unlock_irq(&zfcp_data.config_lock);
-
-	if (!port) {
-		retval = -ENXIO;
-		goto out;
-	}
-
-	zfcp_erp_port_shutdown(port, 0, 92, NULL);
-	zfcp_erp_wait(adapter);
-	zfcp_port_put(port);
-	zfcp_port_dequeue(port);
- out:
-	up(&zfcp_data.config_sema);
-	return retval ? retval : (ssize_t) count;
-}
-
-static DEVICE_ATTR(port_remove, S_IWUSR, NULL, zfcp_sysfs_port_remove_store);
-
-/**
- * zfcp_sysfs_adapter_failed_store - failed state of adapter
- * @dev: pointer to belonging device
- * @buf: pointer to input buffer
- * @count: number of bytes in buffer
- *
- * Store function of the "failed" attribute of an adapter.
- * If a "0" gets written to "failed", error recovery will be
- * started for the belonging adapter.
- */
-static ssize_t
-zfcp_sysfs_adapter_failed_store(struct device *dev, struct device_attribute *attr,
-				const char *buf, size_t count)
-{
-	struct zfcp_adapter *adapter;
-	unsigned int val;
-	char *endp;
-	int retval = 0;
-
-	down(&zfcp_data.config_sema);
-
-	adapter = dev_get_drvdata(dev);
-	if (atomic_test_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status)) {
-		retval = -EBUSY;
-		goto out;
-	}
-
-	val = simple_strtoul(buf, &endp, 0);
-	if (((endp + 1) < (buf + count)) || (val != 0)) {
-		retval = -EINVAL;
-		goto out;
-	}
-
-	zfcp_erp_modify_adapter_status(adapter, 44, NULL,
-				       ZFCP_STATUS_COMMON_RUNNING, ZFCP_SET);
-	zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED, 93,
-				NULL);
-	zfcp_erp_wait(adapter);
- out:
-	up(&zfcp_data.config_sema);
-	return retval ? retval : (ssize_t) count;
-}
-
-/**
- * zfcp_sysfs_adapter_failed_show - failed state of adapter
- * @dev: pointer to belonging device
- * @buf: pointer to input buffer
- *
- * Show function of "failed" attribute of adapter. Will be
- * "0" if adapter is working, otherwise "1".
- */
-static ssize_t
-zfcp_sysfs_adapter_failed_show(struct device *dev, struct device_attribute *attr, char *buf)
-{
-	struct zfcp_adapter *adapter;
-
-	adapter = dev_get_drvdata(dev);
-	if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &adapter->status))
-		return sprintf(buf, "1\n");
-	else
-		return sprintf(buf, "0\n");
-}
-
-static DEVICE_ATTR(failed, S_IWUSR | S_IRUGO, zfcp_sysfs_adapter_failed_show,
-		   zfcp_sysfs_adapter_failed_store);
-
-static struct attribute *zfcp_adapter_attrs[] = {
-	&dev_attr_failed.attr,
-	&dev_attr_in_recovery.attr,
-	&dev_attr_port_remove.attr,
-	&dev_attr_port_add.attr,
-	&dev_attr_peer_wwnn.attr,
-	&dev_attr_peer_wwpn.attr,
-	&dev_attr_peer_d_id.attr,
-	&dev_attr_card_version.attr,
-	&dev_attr_lic_version.attr,
-	&dev_attr_status.attr,
-	&dev_attr_hardware_version.attr,
-	NULL
-};
-
-static struct attribute_group zfcp_adapter_attr_group = {
-	.attrs = zfcp_adapter_attrs,
-};
-
-/**
- * zfcp_sysfs_create_adapter_files - create sysfs adapter files
- * @dev: pointer to belonging device
- *
- * Create all attributes of the sysfs representation of an adapter.
- */
-int
-zfcp_sysfs_adapter_create_files(struct device *dev)
-{
-	return sysfs_create_group(&dev->kobj, &zfcp_adapter_attr_group);
-}
-
-/**
- * zfcp_sysfs_remove_adapter_files - remove sysfs adapter files
- * @dev: pointer to belonging device
- *
- * Remove all attributes of the sysfs representation of an adapter.
- */
-void
-zfcp_sysfs_adapter_remove_files(struct device *dev)
-{
-	sysfs_remove_group(&dev->kobj, &zfcp_adapter_attr_group);
-}
-
-#undef ZFCP_LOG_AREA
diff --git a/drivers/s390/scsi/zfcp_sysfs_driver.c b/drivers/s390/scsi/zfcp_sysfs_driver.c
deleted file mode 100644
index 651edd5..0000000
--- a/drivers/s390/scsi/zfcp_sysfs_driver.c
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * This file is part of the zfcp device driver for
- * FCP adapters for IBM System z9 and zSeries.
- *
- * (C) Copyright IBM Corp. 2002, 2006
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include "zfcp_ext.h"
-
-#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_CONFIG
-
-/**
- * ZFCP_DEFINE_DRIVER_ATTR - define for all loglevels sysfs attributes
- * @_name:       name of attribute
- * @_define:     name of ZFCP loglevel define
- *
- * Generates store function for a sysfs loglevel attribute of zfcp driver.
- */
-#define ZFCP_DEFINE_DRIVER_ATTR(_name, _define)                               \
-static ssize_t zfcp_sysfs_loglevel_##_name##_store(struct device_driver *drv, \
-						   const char *buf,           \
-						   size_t count)              \
-{                                                                             \
-	unsigned int loglevel;                                                \
-	unsigned int new_loglevel;                                            \
-	char *endp;                                                           \
-                                                                              \
-	new_loglevel = simple_strtoul(buf, &endp, 0);                         \
-	if ((endp + 1) < (buf + count))                                       \
-		return -EINVAL;                                               \
-	if (new_loglevel > 3)                                                 \
-		return -EINVAL;                                               \
-	down(&zfcp_data.config_sema);                                         \
-	loglevel = atomic_read(&zfcp_data.loglevel);                          \
-	loglevel &= ~((unsigned int) 0xf << (ZFCP_LOG_AREA_##_define << 2));  \
-	loglevel |= new_loglevel << (ZFCP_LOG_AREA_##_define << 2);           \
-	atomic_set(&zfcp_data.loglevel, loglevel);                            \
-	up(&zfcp_data.config_sema);                                           \
-	return count;                                                         \
-}                                                                             \
-                                                                              \
-static ssize_t zfcp_sysfs_loglevel_##_name##_show(struct device_driver *dev,  \
-						  char *buf)                  \
-{                                                                             \
-	return sprintf(buf,"%d\n", (unsigned int)                             \
-		       ZFCP_GET_LOG_VALUE(ZFCP_LOG_AREA_##_define));          \
-}                                                                             \
-                                                                              \
-static DRIVER_ATTR(loglevel_##_name, S_IWUSR | S_IRUGO,                       \
-		   zfcp_sysfs_loglevel_##_name##_show,                        \
-		   zfcp_sysfs_loglevel_##_name##_store);
-
-ZFCP_DEFINE_DRIVER_ATTR(other, OTHER);
-ZFCP_DEFINE_DRIVER_ATTR(scsi, SCSI);
-ZFCP_DEFINE_DRIVER_ATTR(fsf, FSF);
-ZFCP_DEFINE_DRIVER_ATTR(config, CONFIG);
-ZFCP_DEFINE_DRIVER_ATTR(cio, CIO);
-ZFCP_DEFINE_DRIVER_ATTR(qdio, QDIO);
-ZFCP_DEFINE_DRIVER_ATTR(erp, ERP);
-ZFCP_DEFINE_DRIVER_ATTR(fc, FC);
-
-static ssize_t zfcp_sysfs_version_show(struct device_driver *dev,
-					      char *buf)
-{
-	return sprintf(buf, "%s\n", zfcp_data.driver_version);
-}
-
-static DRIVER_ATTR(version, S_IRUGO, zfcp_sysfs_version_show, NULL);
-
-static struct attribute *zfcp_driver_attrs[] = {
-	&driver_attr_loglevel_other.attr,
-	&driver_attr_loglevel_scsi.attr,
-	&driver_attr_loglevel_fsf.attr,
-	&driver_attr_loglevel_config.attr,
-	&driver_attr_loglevel_cio.attr,
-	&driver_attr_loglevel_qdio.attr,
-	&driver_attr_loglevel_erp.attr,
-	&driver_attr_loglevel_fc.attr,
-	&driver_attr_version.attr,
-	NULL
-};
-
-static struct attribute_group zfcp_driver_attr_group = {
-	.attrs = zfcp_driver_attrs,
-};
-
-struct attribute_group *zfcp_driver_attr_groups[] = {
-	&zfcp_driver_attr_group,
-	NULL,
-};
-
-#undef ZFCP_LOG_AREA
diff --git a/drivers/s390/scsi/zfcp_sysfs_port.c b/drivers/s390/scsi/zfcp_sysfs_port.c
deleted file mode 100644
index 703c1b5..0000000
--- a/drivers/s390/scsi/zfcp_sysfs_port.c
+++ /dev/null
@@ -1,295 +0,0 @@
-/*
- * This file is part of the zfcp device driver for
- * FCP adapters for IBM System z9 and zSeries.
- *
- * (C) Copyright IBM Corp. 2002, 2006
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include "zfcp_ext.h"
-
-#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_CONFIG
-
-/**
- * zfcp_sysfs_port_release - gets called when a struct device port is released
- * @dev: pointer to belonging device
- */
-void
-zfcp_sysfs_port_release(struct device *dev)
-{
-	kfree(dev);
-}
-
-/**
- * ZFCP_DEFINE_PORT_ATTR
- * @_name:   name of show attribute
- * @_format: format string
- * @_value:  value to print
- *
- * Generates attributes for a port.
- */
-#define ZFCP_DEFINE_PORT_ATTR(_name, _format, _value)                    \
-static ssize_t zfcp_sysfs_port_##_name##_show(struct device *dev, struct device_attribute *attr,        \
-                                              char *buf)                 \
-{                                                                        \
-        struct zfcp_port *port;                                          \
-                                                                         \
-        port = dev_get_drvdata(dev);                                     \
-        return sprintf(buf, _format, _value);                            \
-}                                                                        \
-                                                                         \
-static DEVICE_ATTR(_name, S_IRUGO, zfcp_sysfs_port_##_name##_show, NULL);
-
-ZFCP_DEFINE_PORT_ATTR(status, "0x%08x\n", atomic_read(&port->status));
-ZFCP_DEFINE_PORT_ATTR(in_recovery, "%d\n", atomic_test_mask
-		      (ZFCP_STATUS_COMMON_ERP_INUSE, &port->status));
-ZFCP_DEFINE_PORT_ATTR(access_denied, "%d\n", atomic_test_mask
-		      (ZFCP_STATUS_COMMON_ACCESS_DENIED, &port->status));
-
-/**
- * zfcp_sysfs_unit_add_store - add a unit to sysfs tree
- * @dev: pointer to belonging device
- * @buf: pointer to input buffer
- * @count: number of bytes in buffer
- *
- * Store function of the "unit_add" attribute of a port.
- */
-static ssize_t
-zfcp_sysfs_unit_add_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
-{
-	fcp_lun_t fcp_lun;
-	char *endp;
-	struct zfcp_port *port;
-	struct zfcp_unit *unit;
-	int retval = -EINVAL;
-
-	down(&zfcp_data.config_sema);
-
-	port = dev_get_drvdata(dev);
-	if (atomic_test_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status)) {
-		retval = -EBUSY;
-		goto out;
-	}
-
-	fcp_lun = simple_strtoull(buf, &endp, 0);
-	if ((endp + 1) < (buf + count))
-		goto out;
-
-	unit = zfcp_unit_enqueue(port, fcp_lun);
-	if (!unit)
-		goto out;
-
-	retval = 0;
-
-	zfcp_erp_unit_reopen(unit, 0, 94, NULL);
-	zfcp_erp_wait(unit->port->adapter);
-	zfcp_unit_put(unit);
- out:
-	up(&zfcp_data.config_sema);
-	return retval ? retval : (ssize_t) count;
-}
-
-static DEVICE_ATTR(unit_add, S_IWUSR, NULL, zfcp_sysfs_unit_add_store);
-
-/**
- * zfcp_sysfs_unit_remove_store - remove a unit from sysfs tree
- * @dev: pointer to belonging device
- * @buf: pointer to input buffer
- * @count: number of bytes in buffer
- */
-static ssize_t
-zfcp_sysfs_unit_remove_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
-{
-	struct zfcp_port *port;
-	struct zfcp_unit *unit;
-	fcp_lun_t fcp_lun;
-	char *endp;
-	int retval = 0;
-
-	down(&zfcp_data.config_sema);
-
-	port = dev_get_drvdata(dev);
-	if (atomic_test_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status)) {
-		retval = -EBUSY;
-		goto out;
-	}
-
-	fcp_lun = simple_strtoull(buf, &endp, 0);
-	if ((endp + 1) < (buf + count)) {
-		retval = -EINVAL;
-		goto out;
-	}
-
-	write_lock_irq(&zfcp_data.config_lock);
-	unit = zfcp_get_unit_by_lun(port, fcp_lun);
-	if (unit && (atomic_read(&unit->refcount) == 0)) {
-		zfcp_unit_get(unit);
-		atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &unit->status);
-		list_move(&unit->list, &port->unit_remove_lh);
-	}
-	else {
-		unit = NULL;
-	}
-	write_unlock_irq(&zfcp_data.config_lock);
-
-	if (!unit) {
-		retval = -ENXIO;
-		goto out;
-	}
-
-	zfcp_erp_unit_shutdown(unit, 0, 95, NULL);
-	zfcp_erp_wait(unit->port->adapter);
-	zfcp_unit_put(unit);
-	zfcp_unit_dequeue(unit);
- out:
-	up(&zfcp_data.config_sema);
-	return retval ? retval : (ssize_t) count;
-}
-
-static DEVICE_ATTR(unit_remove, S_IWUSR, NULL, zfcp_sysfs_unit_remove_store);
-
-/**
- * zfcp_sysfs_port_failed_store - failed state of port
- * @dev: pointer to belonging device
- * @buf: pointer to input buffer
- * @count: number of bytes in buffer
- *
- * Store function of the "failed" attribute of a port.
- * If a "0" gets written to "failed", error recovery will be
- * started for the belonging port.
- */
-static ssize_t
-zfcp_sysfs_port_failed_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
-{
-	struct zfcp_port *port;
-	unsigned int val;
-	char *endp;
-	int retval = 0;
-
-	down(&zfcp_data.config_sema);
-
-	port = dev_get_drvdata(dev);
-	if (atomic_test_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status)) {
-		retval = -EBUSY;
-		goto out;
-	}
-
-	val = simple_strtoul(buf, &endp, 0);
-	if (((endp + 1) < (buf + count)) || (val != 0)) {
-		retval = -EINVAL;
-		goto out;
-	}
-
-	zfcp_erp_modify_port_status(port, 45, NULL,
-				    ZFCP_STATUS_COMMON_RUNNING, ZFCP_SET);
-	zfcp_erp_port_reopen(port, ZFCP_STATUS_COMMON_ERP_FAILED, 96, NULL);
-	zfcp_erp_wait(port->adapter);
- out:
-	up(&zfcp_data.config_sema);
-	return retval ? retval : (ssize_t) count;
-}
-
-/**
- * zfcp_sysfs_port_failed_show - failed state of port
- * @dev: pointer to belonging device
- * @buf: pointer to input buffer
- *
- * Show function of "failed" attribute of port. Will be
- * "0" if port is working, otherwise "1".
- */
-static ssize_t
-zfcp_sysfs_port_failed_show(struct device *dev, struct device_attribute *attr, char *buf)
-{
-	struct zfcp_port *port;
-
-	port = dev_get_drvdata(dev);
-	if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &port->status))
-		return sprintf(buf, "1\n");
-	else
-		return sprintf(buf, "0\n");
-}
-
-static DEVICE_ATTR(failed, S_IWUSR | S_IRUGO, zfcp_sysfs_port_failed_show,
-		   zfcp_sysfs_port_failed_store);
-
-/**
- * zfcp_port_common_attrs
- * sysfs attributes that are common for all kind of fc ports.
- */
-static struct attribute *zfcp_port_common_attrs[] = {
-	&dev_attr_failed.attr,
-	&dev_attr_in_recovery.attr,
-	&dev_attr_status.attr,
-	&dev_attr_access_denied.attr,
-	NULL
-};
-
-static struct attribute_group zfcp_port_common_attr_group = {
-	.attrs = zfcp_port_common_attrs,
-};
-
-/**
- * zfcp_port_no_ns_attrs
- * sysfs attributes not to be used for nameserver ports.
- */
-static struct attribute *zfcp_port_no_ns_attrs[] = {
-	&dev_attr_unit_add.attr,
-	&dev_attr_unit_remove.attr,
-	NULL
-};
-
-static struct attribute_group zfcp_port_no_ns_attr_group = {
-	.attrs = zfcp_port_no_ns_attrs,
-};
-
-/**
- * zfcp_sysfs_port_create_files - create sysfs port files
- * @dev: pointer to belonging device
- *
- * Create all attributes of the sysfs representation of a port.
- */
-int
-zfcp_sysfs_port_create_files(struct device *dev, u32 flags)
-{
-	int retval;
-
-	retval = sysfs_create_group(&dev->kobj, &zfcp_port_common_attr_group);
-
-	if ((flags & ZFCP_STATUS_PORT_WKA) || retval)
-		return retval;
-
-	retval = sysfs_create_group(&dev->kobj, &zfcp_port_no_ns_attr_group);
-	if (retval)
-		sysfs_remove_group(&dev->kobj, &zfcp_port_common_attr_group);
-
-	return retval;
-}
-
-/**
- * zfcp_sysfs_port_remove_files - remove sysfs port files
- * @dev: pointer to belonging device
- *
- * Remove all attributes of the sysfs representation of a port.
- */
-void
-zfcp_sysfs_port_remove_files(struct device *dev, u32 flags)
-{
-	sysfs_remove_group(&dev->kobj, &zfcp_port_common_attr_group);
-	if (!(flags & ZFCP_STATUS_PORT_WKA))
-		sysfs_remove_group(&dev->kobj, &zfcp_port_no_ns_attr_group);
-}
-
-#undef ZFCP_LOG_AREA
diff --git a/drivers/s390/scsi/zfcp_sysfs_unit.c b/drivers/s390/scsi/zfcp_sysfs_unit.c
deleted file mode 100644
index 80fb2c2..0000000
--- a/drivers/s390/scsi/zfcp_sysfs_unit.c
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * This file is part of the zfcp device driver for
- * FCP adapters for IBM System z9 and zSeries.
- *
- * (C) Copyright IBM Corp. 2002, 2006
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include "zfcp_ext.h"
-
-#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_CONFIG
-
-/**
- * zfcp_sysfs_unit_release - gets called when a struct device unit is released
- * @dev: pointer to belonging device
- */
-void
-zfcp_sysfs_unit_release(struct device *dev)
-{
-	kfree(dev);
-}
-
-/**
- * ZFCP_DEFINE_UNIT_ATTR
- * @_name:   name of show attribute
- * @_format: format string
- * @_value:  value to print
- *
- * Generates attribute for a unit.
- */
-#define ZFCP_DEFINE_UNIT_ATTR(_name, _format, _value)                    \
-static ssize_t zfcp_sysfs_unit_##_name##_show(struct device *dev, struct device_attribute *attr,        \
-                                              char *buf)                 \
-{                                                                        \
-        struct zfcp_unit *unit;                                          \
-                                                                         \
-        unit = dev_get_drvdata(dev);                                     \
-        return sprintf(buf, _format, _value);                            \
-}                                                                        \
-                                                                         \
-static DEVICE_ATTR(_name, S_IRUGO, zfcp_sysfs_unit_##_name##_show, NULL);
-
-ZFCP_DEFINE_UNIT_ATTR(status, "0x%08x\n", atomic_read(&unit->status));
-ZFCP_DEFINE_UNIT_ATTR(in_recovery, "%d\n", atomic_test_mask
-		      (ZFCP_STATUS_COMMON_ERP_INUSE, &unit->status));
-ZFCP_DEFINE_UNIT_ATTR(access_denied, "%d\n", atomic_test_mask
-		      (ZFCP_STATUS_COMMON_ACCESS_DENIED, &unit->status));
-ZFCP_DEFINE_UNIT_ATTR(access_shared, "%d\n", atomic_test_mask
-		      (ZFCP_STATUS_UNIT_SHARED, &unit->status));
-ZFCP_DEFINE_UNIT_ATTR(access_readonly, "%d\n", atomic_test_mask
-		      (ZFCP_STATUS_UNIT_READONLY, &unit->status));
-
-/**
- * zfcp_sysfs_unit_failed_store - failed state of unit
- * @dev: pointer to belonging device
- * @buf: pointer to input buffer
- * @count: number of bytes in buffer
- *
- * Store function of the "failed" attribute of a unit.
- * If a "0" gets written to "failed", error recovery will be
- * started for the belonging unit.
- */
-static ssize_t
-zfcp_sysfs_unit_failed_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
-{
-	struct zfcp_unit *unit;
-	unsigned int val;
-	char *endp;
-	int retval = 0;
-
-	down(&zfcp_data.config_sema);
-	unit = dev_get_drvdata(dev);
-	if (atomic_test_mask(ZFCP_STATUS_COMMON_REMOVE, &unit->status)) {
-		retval = -EBUSY;
-		goto out;
-	}
-
-	val = simple_strtoul(buf, &endp, 0);
-	if (((endp + 1) < (buf + count)) || (val != 0)) {
-		retval = -EINVAL;
-		goto out;
-	}
-
-	zfcp_erp_modify_unit_status(unit, 46, NULL,
-				    ZFCP_STATUS_COMMON_RUNNING, ZFCP_SET);
-	zfcp_erp_unit_reopen(unit, ZFCP_STATUS_COMMON_ERP_FAILED, 97, NULL);
-	zfcp_erp_wait(unit->port->adapter);
- out:
-	up(&zfcp_data.config_sema);
-	return retval ? retval : (ssize_t) count;
-}
-
-/**
- * zfcp_sysfs_unit_failed_show - failed state of unit
- * @dev: pointer to belonging device
- * @buf: pointer to input buffer
- *
- * Show function of "failed" attribute of unit. Will be
- * "0" if unit is working, otherwise "1".
- */
-static ssize_t
-zfcp_sysfs_unit_failed_show(struct device *dev, struct device_attribute *attr, char *buf)
-{
-	struct zfcp_unit *unit;
-
-	unit = dev_get_drvdata(dev);
-	if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &unit->status))
-		return sprintf(buf, "1\n");
-	else
-		return sprintf(buf, "0\n");
-}
-
-static DEVICE_ATTR(failed, S_IWUSR | S_IRUGO, zfcp_sysfs_unit_failed_show,
-		   zfcp_sysfs_unit_failed_store);
-
-static struct attribute *zfcp_unit_attrs[] = {
-	&dev_attr_failed.attr,
-	&dev_attr_in_recovery.attr,
-	&dev_attr_status.attr,
-	&dev_attr_access_denied.attr,
-	&dev_attr_access_shared.attr,
-	&dev_attr_access_readonly.attr,
-	NULL
-};
-
-static struct attribute_group zfcp_unit_attr_group = {
-	.attrs = zfcp_unit_attrs,
-};
-
-/**
- * zfcp_sysfs_create_unit_files - create sysfs unit files
- * @dev: pointer to belonging device
- *
- * Create all attributes of the sysfs representation of a unit.
- */
-int
-zfcp_sysfs_unit_create_files(struct device *dev)
-{
-	return sysfs_create_group(&dev->kobj, &zfcp_unit_attr_group);
-}
-
-/**
- * zfcp_sysfs_remove_unit_files - remove sysfs unit files
- * @dev: pointer to belonging device
- *
- * Remove all attributes of the sysfs representation of a unit.
- */
-void
-zfcp_sysfs_unit_remove_files(struct device *dev)
-{
-	sysfs_remove_group(&dev->kobj, &zfcp_unit_attr_group);
-}
-
-#undef ZFCP_LOG_AREA
diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
index 81ccbd7..26be540 100644
--- a/drivers/scsi/Kconfig
+++ b/drivers/scsi/Kconfig
@@ -888,6 +888,25 @@
 	  To compile this driver as a module, choose M here: the
 	  module will be called ibmvstgt.
 
+config SCSI_IBMVFC
+	tristate "IBM Virtual FC support"
+	depends on PPC_PSERIES && SCSI
+	select SCSI_FC_ATTRS
+	help
+	  This is the IBM POWER Virtual FC Client
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ibmvfc.
+
+config SCSI_IBMVFC_TRACE
+	bool "enable driver internal trace"
+	depends on SCSI_IBMVFC
+	default y
+	help
+	  If you say Y here, the driver will trace all commands issued
+	  to the adapter. Performance impact is minimal. Trace can be
+	  dumped using /sys/class/scsi_host/hostXX/trace.
+
 config SCSI_INITIO
 	tristate "Initio 9100U(W) support"
 	depends on PCI && SCSI
@@ -1738,10 +1757,12 @@
 	select SCSI_SPI_ATTRS
 	help
 	  This is the driver for the Sun ESP SCSI host adapter. The ESP
-	  chipset is present in most SPARC SBUS-based computers.
+	  chipset is present in most SPARC SBUS-based computers and
+	  supports the Emulex family of ESP SCSI chips (esp100, esp100A,
+	  esp236, fas101, fas236) as well as the Qlogic fas366 SCSI chip.
 
 	  To compile this driver as a module, choose M here: the
-	  module will be called esp.
+	  module will be called sun_esp.
 
 config ZFCP
 	tristate "FCP host bus adapter driver for IBM eServer zSeries"
@@ -1771,4 +1792,6 @@
 
 source "drivers/scsi/pcmcia/Kconfig"
 
+source "drivers/scsi/device_handler/Kconfig"
+
 endmenu
diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile
index 6c775e3..a814967 100644
--- a/drivers/scsi/Makefile
+++ b/drivers/scsi/Makefile
@@ -34,6 +34,7 @@
 obj-$(CONFIG_SCSI_SAS_ATTRS)	+= scsi_transport_sas.o
 obj-$(CONFIG_SCSI_SAS_LIBSAS)	+= libsas/
 obj-$(CONFIG_SCSI_SRP_ATTRS)	+= scsi_transport_srp.o
+obj-$(CONFIG_SCSI_DH)		+= device_handler/
 
 obj-$(CONFIG_ISCSI_TCP) 	+= libiscsi.o	iscsi_tcp.o
 obj-$(CONFIG_INFINIBAND_ISER) 	+= libiscsi.o
@@ -118,6 +119,7 @@
 obj-$(CONFIG_SCSI_SRP)		+= libsrp.o
 obj-$(CONFIG_SCSI_IBMVSCSI)	+= ibmvscsi/
 obj-$(CONFIG_SCSI_IBMVSCSIS)	+= ibmvscsi/
+obj-$(CONFIG_SCSI_IBMVFC)	+= ibmvscsi/
 obj-$(CONFIG_SCSI_HPTIOP)	+= hptiop.o
 obj-$(CONFIG_SCSI_STEX)		+= stex.o
 obj-$(CONFIG_SCSI_MVSAS)	+= mvsas.o
diff --git a/drivers/scsi/aacraid/commctrl.c b/drivers/scsi/aacraid/commctrl.c
index 5fd83de..a735526 100644
--- a/drivers/scsi/aacraid/commctrl.c
+++ b/drivers/scsi/aacraid/commctrl.c
@@ -41,6 +41,7 @@
 #include <linux/kthread.h>
 #include <linux/semaphore.h>
 #include <asm/uaccess.h>
+#include <scsi/scsi_host.h>
 
 #include "aacraid.h"
 
@@ -581,6 +582,14 @@
 			for (i = 0; i < upsg->count; i++) {
 				u64 addr;
 				void* p;
+				if (upsg->sg[i].count >
+				    (dev->adapter_info.options &
+				     AAC_OPT_NEW_COMM) ?
+				      (dev->scsi_host_ptr->max_sectors << 9) :
+				      65536) {
+					rcode = -EINVAL;
+					goto cleanup;
+				}
 				/* Does this really need to be GFP_DMA? */
 				p = kmalloc(upsg->sg[i].count,GFP_KERNEL|__GFP_DMA);
 				if(!p) {
@@ -625,6 +634,14 @@
 			for (i = 0; i < usg->count; i++) {
 				u64 addr;
 				void* p;
+				if (usg->sg[i].count >
+				    (dev->adapter_info.options &
+				     AAC_OPT_NEW_COMM) ?
+				      (dev->scsi_host_ptr->max_sectors << 9) :
+				      65536) {
+					rcode = -EINVAL;
+					goto cleanup;
+				}
 				/* Does this really need to be GFP_DMA? */
 				p = kmalloc(usg->sg[i].count,GFP_KERNEL|__GFP_DMA);
 				if(!p) {
@@ -667,6 +684,14 @@
 			for (i = 0; i < upsg->count; i++) {
 				uintptr_t addr;
 				void* p;
+				if (usg->sg[i].count >
+				    (dev->adapter_info.options &
+				     AAC_OPT_NEW_COMM) ?
+				      (dev->scsi_host_ptr->max_sectors << 9) :
+				      65536) {
+					rcode = -EINVAL;
+					goto cleanup;
+				}
 				/* Does this really need to be GFP_DMA? */
 				p = kmalloc(usg->sg[i].count,GFP_KERNEL|__GFP_DMA);
 				if(!p) {
@@ -698,6 +723,14 @@
 			for (i = 0; i < upsg->count; i++) {
 				dma_addr_t addr;
 				void* p;
+				if (upsg->sg[i].count >
+				    (dev->adapter_info.options &
+				     AAC_OPT_NEW_COMM) ?
+				      (dev->scsi_host_ptr->max_sectors << 9) :
+				      65536) {
+					rcode = -EINVAL;
+					goto cleanup;
+				}
 				p = kmalloc(upsg->sg[i].count, GFP_KERNEL);
 				if (!p) {
 					dprintk((KERN_DEBUG"aacraid: Could not allocate SG buffer - size = %d buffer number %d of %d\n",
diff --git a/drivers/scsi/aacraid/linit.c b/drivers/scsi/aacraid/linit.c
index 68c140e..9aa301c 100644
--- a/drivers/scsi/aacraid/linit.c
+++ b/drivers/scsi/aacraid/linit.c
@@ -865,7 +865,7 @@
 	return len;
 }
 
-ssize_t aac_show_serial_number(struct device *device,
+static ssize_t aac_show_serial_number(struct device *device,
 			       struct device_attribute *attr, char *buf)
 {
 	struct aac_dev *dev = (struct aac_dev*)class_to_shost(device)->hostdata;
diff --git a/drivers/scsi/device_handler/Kconfig b/drivers/scsi/device_handler/Kconfig
new file mode 100644
index 0000000..2adc0f6
--- /dev/null
+++ b/drivers/scsi/device_handler/Kconfig
@@ -0,0 +1,32 @@
+#
+# SCSI Device Handler configuration
+#
+
+menuconfig SCSI_DH
+	tristate "SCSI Device Handlers"
+	depends on SCSI
+	default n
+	help
+	  SCSI Device Handlers provide device specific support for
+	  devices utilized in multipath configurations. Say Y here to
+	  select support for specific hardware.
+
+config SCSI_DH_RDAC
+	tristate "LSI RDAC Device Handler"
+	depends on SCSI_DH
+	help
+	If you have a LSI RDAC select y. Otherwise, say N.
+
+config SCSI_DH_HP_SW
+	tristate "HP/COMPAQ MSA Device Handler"
+	depends on SCSI_DH
+	help
+	If you have a HP/COMPAQ MSA device that requires START_STOP to
+	be sent to start it and cannot upgrade the firmware then select y.
+	Otherwise, say N.
+
+config SCSI_DH_EMC
+	tristate "EMC CLARiiON Device Handler"
+	depends on SCSI_DH
+	help
+	If you have a EMC CLARiiON select y. Otherwise, say N.
diff --git a/drivers/scsi/device_handler/Makefile b/drivers/scsi/device_handler/Makefile
new file mode 100644
index 0000000..35272e9
--- /dev/null
+++ b/drivers/scsi/device_handler/Makefile
@@ -0,0 +1,7 @@
+#
+# SCSI Device Handler
+#
+obj-$(CONFIG_SCSI_DH)		+= scsi_dh.o
+obj-$(CONFIG_SCSI_DH_RDAC)	+= scsi_dh_rdac.o
+obj-$(CONFIG_SCSI_DH_HP_SW)	+= scsi_dh_hp_sw.o
+obj-$(CONFIG_SCSI_DH_EMC)	+= scsi_dh_emc.o
diff --git a/drivers/scsi/device_handler/scsi_dh.c b/drivers/scsi/device_handler/scsi_dh.c
new file mode 100644
index 0000000..ab6c21c
--- /dev/null
+++ b/drivers/scsi/device_handler/scsi_dh.c
@@ -0,0 +1,162 @@
+/*
+ * SCSI device handler infrastruture.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright IBM Corporation, 2007
+ *      Authors:
+ *               Chandra Seetharaman <sekharan@us.ibm.com>
+ *               Mike Anderson <andmike@linux.vnet.ibm.com>
+ */
+
+#include <scsi/scsi_dh.h>
+#include "../scsi_priv.h"
+
+static DEFINE_SPINLOCK(list_lock);
+static LIST_HEAD(scsi_dh_list);
+
+static struct scsi_device_handler *get_device_handler(const char *name)
+{
+	struct scsi_device_handler *tmp, *found = NULL;
+
+	spin_lock(&list_lock);
+	list_for_each_entry(tmp, &scsi_dh_list, list) {
+		if (!strcmp(tmp->name, name)) {
+			found = tmp;
+			break;
+		}
+	}
+	spin_unlock(&list_lock);
+	return found;
+}
+
+static int scsi_dh_notifier_add(struct device *dev, void *data)
+{
+	struct scsi_device_handler *scsi_dh = data;
+
+	scsi_dh->nb.notifier_call(&scsi_dh->nb, BUS_NOTIFY_ADD_DEVICE, dev);
+	return 0;
+}
+
+/*
+ * scsi_register_device_handler - register a device handler personality
+ *      module.
+ * @scsi_dh - device handler to be registered.
+ *
+ * Returns 0 on success, -EBUSY if handler already registered.
+ */
+int scsi_register_device_handler(struct scsi_device_handler *scsi_dh)
+{
+	int ret = -EBUSY;
+	struct scsi_device_handler *tmp;
+
+	tmp = get_device_handler(scsi_dh->name);
+	if (tmp)
+		goto done;
+
+	ret = bus_register_notifier(&scsi_bus_type, &scsi_dh->nb);
+
+	bus_for_each_dev(&scsi_bus_type, NULL, scsi_dh, scsi_dh_notifier_add);
+	spin_lock(&list_lock);
+	list_add(&scsi_dh->list, &scsi_dh_list);
+	spin_unlock(&list_lock);
+
+done:
+	return ret;
+}
+EXPORT_SYMBOL_GPL(scsi_register_device_handler);
+
+static int scsi_dh_notifier_remove(struct device *dev, void *data)
+{
+	struct scsi_device_handler *scsi_dh = data;
+
+	scsi_dh->nb.notifier_call(&scsi_dh->nb, BUS_NOTIFY_DEL_DEVICE, dev);
+	return 0;
+}
+
+/*
+ * scsi_unregister_device_handler - register a device handler personality
+ *      module.
+ * @scsi_dh - device handler to be unregistered.
+ *
+ * Returns 0 on success, -ENODEV if handler not registered.
+ */
+int scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh)
+{
+	int ret = -ENODEV;
+	struct scsi_device_handler *tmp;
+
+	tmp = get_device_handler(scsi_dh->name);
+	if (!tmp)
+		goto done;
+
+	ret = bus_unregister_notifier(&scsi_bus_type, &scsi_dh->nb);
+
+	bus_for_each_dev(&scsi_bus_type, NULL, scsi_dh,
+					scsi_dh_notifier_remove);
+	spin_lock(&list_lock);
+	list_del(&scsi_dh->list);
+	spin_unlock(&list_lock);
+
+done:
+	return ret;
+}
+EXPORT_SYMBOL_GPL(scsi_unregister_device_handler);
+
+/*
+ * scsi_dh_activate - activate the path associated with the scsi_device
+ *      corresponding to the given request queue.
+ * @q - Request queue that is associated with the scsi_device to be
+ *      activated.
+ */
+int scsi_dh_activate(struct request_queue *q)
+{
+	int err = 0;
+	unsigned long flags;
+	struct scsi_device *sdev;
+	struct scsi_device_handler *scsi_dh = NULL;
+
+	spin_lock_irqsave(q->queue_lock, flags);
+	sdev = q->queuedata;
+	if (sdev && sdev->scsi_dh_data)
+		scsi_dh = sdev->scsi_dh_data->scsi_dh;
+	if (!scsi_dh || !get_device(&sdev->sdev_gendev))
+		err = SCSI_DH_NOSYS;
+	spin_unlock_irqrestore(q->queue_lock, flags);
+
+	if (err)
+		return err;
+
+	if (scsi_dh->activate)
+		err = scsi_dh->activate(sdev);
+	put_device(&sdev->sdev_gendev);
+	return err;
+}
+EXPORT_SYMBOL_GPL(scsi_dh_activate);
+
+/*
+ * scsi_dh_handler_exist - Return TRUE(1) if a device handler exists for
+ *	the given name. FALSE(0) otherwise.
+ * @name - name of the device handler.
+ */
+int scsi_dh_handler_exist(const char *name)
+{
+	return (get_device_handler(name) != NULL);
+}
+EXPORT_SYMBOL_GPL(scsi_dh_handler_exist);
+
+MODULE_DESCRIPTION("SCSI device handler");
+MODULE_AUTHOR("Chandra Seetharaman <sekharan@us.ibm.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/device_handler/scsi_dh_emc.c b/drivers/scsi/device_handler/scsi_dh_emc.c
new file mode 100644
index 0000000..ed53f14
--- /dev/null
+++ b/drivers/scsi/device_handler/scsi_dh_emc.c
@@ -0,0 +1,499 @@
+/*
+ * Target driver for EMC CLARiiON AX/CX-series hardware.
+ * Based on code from Lars Marowsky-Bree <lmb@suse.de>
+ * and Ed Goggin <egoggin@emc.com>.
+ *
+ * Copyright (C) 2006 Red Hat, Inc.  All rights reserved.
+ * Copyright (C) 2006 Mike Christie
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <scsi/scsi.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_dh.h>
+#include <scsi/scsi_device.h>
+
+#define CLARIION_NAME			"emc_clariion"
+
+#define CLARIION_TRESPASS_PAGE		0x22
+#define CLARIION_BUFFER_SIZE		0x80
+#define CLARIION_TIMEOUT		(60 * HZ)
+#define CLARIION_RETRIES		3
+#define CLARIION_UNBOUND_LU		-1
+
+static unsigned char long_trespass[] = {
+	0, 0, 0, 0,
+	CLARIION_TRESPASS_PAGE,	/* Page code */
+	0x09,			/* Page length - 2 */
+	0x81,			/* Trespass code + Honor reservation bit */
+	0xff, 0xff,		/* Trespass target */
+	0, 0, 0, 0, 0, 0	/* Reserved bytes / unknown */
+};
+
+static unsigned char long_trespass_hr[] = {
+	0, 0, 0, 0,
+	CLARIION_TRESPASS_PAGE,	/* Page code */
+	0x09,			/* Page length - 2 */
+	0x01,			/* Trespass code + Honor reservation bit */
+	0xff, 0xff,		/* Trespass target */
+	0, 0, 0, 0, 0, 0	/* Reserved bytes / unknown */
+};
+
+static unsigned char short_trespass[] = {
+	0, 0, 0, 0,
+	CLARIION_TRESPASS_PAGE,	/* Page code */
+	0x02,			/* Page length - 2 */
+	0x81,			/* Trespass code + Honor reservation bit */
+	0xff,			/* Trespass target */
+};
+
+static unsigned char short_trespass_hr[] = {
+	0, 0, 0, 0,
+	CLARIION_TRESPASS_PAGE,	/* Page code */
+	0x02,			/* Page length - 2 */
+	0x01,			/* Trespass code + Honor reservation bit */
+	0xff,			/* Trespass target */
+};
+
+struct clariion_dh_data {
+	/*
+	 * Use short trespass command (FC-series) or the long version
+	 * (default for AX/CX CLARiiON arrays).
+	 */
+	unsigned short_trespass;
+	/*
+	 * Whether or not (default) to honor SCSI reservations when
+	 * initiating a switch-over.
+	 */
+	unsigned hr;
+	/* I/O buffer for both MODE_SELECT and INQUIRY commands. */
+	char buffer[CLARIION_BUFFER_SIZE];
+	/*
+	 * SCSI sense buffer for commands -- assumes serial issuance
+	 * and completion sequence of all commands for same multipath.
+	 */
+	unsigned char sense[SCSI_SENSE_BUFFERSIZE];
+	/* which SP (A=0,B=1,UNBOUND=-1) is dflt SP for path's mapped dev */
+	int default_sp;
+	/* which SP (A=0,B=1,UNBOUND=-1) is active for path's mapped dev */
+	int current_sp;
+};
+
+static inline struct clariion_dh_data
+			*get_clariion_data(struct scsi_device *sdev)
+{
+	struct scsi_dh_data *scsi_dh_data = sdev->scsi_dh_data;
+	BUG_ON(scsi_dh_data == NULL);
+	return ((struct clariion_dh_data *) scsi_dh_data->buf);
+}
+
+/*
+ * Parse MODE_SELECT cmd reply.
+ */
+static int trespass_endio(struct scsi_device *sdev, int result)
+{
+	int err = SCSI_DH_OK;
+	struct scsi_sense_hdr sshdr;
+	struct clariion_dh_data *csdev = get_clariion_data(sdev);
+	char *sense = csdev->sense;
+
+	if (status_byte(result) == CHECK_CONDITION &&
+	    scsi_normalize_sense(sense, SCSI_SENSE_BUFFERSIZE, &sshdr)) {
+		sdev_printk(KERN_ERR, sdev, "Found valid sense data 0x%2x, "
+			    "0x%2x, 0x%2x while sending CLARiiON trespass "
+			    "command.\n", sshdr.sense_key, sshdr.asc,
+			     sshdr.ascq);
+
+		if ((sshdr.sense_key == 0x05) && (sshdr.asc == 0x04) &&
+		     (sshdr.ascq == 0x00)) {
+			/*
+			 * Array based copy in progress -- do not send
+			 * mode_select or copy will be aborted mid-stream.
+			 */
+			sdev_printk(KERN_INFO, sdev, "Array Based Copy in "
+				    "progress while sending CLARiiON trespass "
+				    "command.\n");
+			err = SCSI_DH_DEV_TEMP_BUSY;
+		} else if ((sshdr.sense_key == 0x02) && (sshdr.asc == 0x04) &&
+			    (sshdr.ascq == 0x03)) {
+			/*
+			 * LUN Not Ready - Manual Intervention Required
+			 * indicates in-progress ucode upgrade (NDU).
+			 */
+			sdev_printk(KERN_INFO, sdev, "Detected in-progress "
+				    "ucode upgrade NDU operation while sending "
+				    "CLARiiON trespass command.\n");
+			err = SCSI_DH_DEV_TEMP_BUSY;
+		} else
+			err = SCSI_DH_DEV_FAILED;
+	} else if (result) {
+		sdev_printk(KERN_ERR, sdev, "Error 0x%x while sending "
+			    "CLARiiON trespass command.\n", result);
+		err = SCSI_DH_IO;
+	}
+
+	return err;
+}
+
+static int parse_sp_info_reply(struct scsi_device *sdev, int result,
+		int *default_sp, int *current_sp, int *new_current_sp)
+{
+	int err = SCSI_DH_OK;
+	struct clariion_dh_data *csdev = get_clariion_data(sdev);
+
+	if (result == 0) {
+		/* check for in-progress ucode upgrade (NDU) */
+		if (csdev->buffer[48] != 0) {
+			sdev_printk(KERN_NOTICE, sdev, "Detected in-progress "
+			       "ucode upgrade NDU operation while finding "
+			       "current active SP.");
+			err = SCSI_DH_DEV_TEMP_BUSY;
+		} else {
+			*default_sp = csdev->buffer[5];
+
+			if (csdev->buffer[4] == 2)
+				/* SP for path is current */
+				*current_sp = csdev->buffer[8];
+			else {
+				if (csdev->buffer[4] == 1)
+					/* SP for this path is NOT current */
+					if (csdev->buffer[8] == 0)
+						*current_sp = 1;
+					else
+						*current_sp = 0;
+				else
+					/* unbound LU or LUNZ */
+					*current_sp = CLARIION_UNBOUND_LU;
+			}
+			*new_current_sp =  csdev->buffer[8];
+		}
+	} else {
+		struct scsi_sense_hdr sshdr;
+
+		err = SCSI_DH_IO;
+
+		if (scsi_normalize_sense(csdev->sense, SCSI_SENSE_BUFFERSIZE,
+							   &sshdr))
+			sdev_printk(KERN_ERR, sdev, "Found valid sense data "
+			      "0x%2x, 0x%2x, 0x%2x while finding current "
+			      "active SP.", sshdr.sense_key, sshdr.asc,
+			      sshdr.ascq);
+		else
+			sdev_printk(KERN_ERR, sdev, "Error 0x%x finding "
+			      "current active SP.", result);
+	}
+
+	return err;
+}
+
+static int sp_info_endio(struct scsi_device *sdev, int result,
+					int mode_select_sent, int *done)
+{
+	struct clariion_dh_data *csdev = get_clariion_data(sdev);
+	int err_flags, default_sp, current_sp, new_current_sp;
+
+	err_flags = parse_sp_info_reply(sdev, result, &default_sp,
+					     &current_sp, &new_current_sp);
+
+	if (err_flags != SCSI_DH_OK)
+		goto done;
+
+	if (mode_select_sent) {
+		csdev->default_sp = default_sp;
+		csdev->current_sp = current_sp;
+	} else {
+		/*
+		 * Issue the actual module_selec request IFF either
+		 * (1) we do not know the identity of the current SP OR
+		 * (2) what we think we know is actually correct.
+		 */
+		if ((current_sp != CLARIION_UNBOUND_LU) &&
+		    (new_current_sp != current_sp)) {
+
+			csdev->default_sp = default_sp;
+			csdev->current_sp = current_sp;
+
+			sdev_printk(KERN_INFO, sdev, "Ignoring path group "
+			       "switch-over command for CLARiiON SP%s since "
+			       " mapped device is already initialized.",
+			       current_sp ? "B" : "A");
+			if (done)
+				*done = 1; /* as good as doing it */
+		}
+	}
+done:
+	return err_flags;
+}
+
+/*
+* Get block request for REQ_BLOCK_PC command issued to path.  Currently
+* limited to MODE_SELECT (trespass) and INQUIRY (VPD page 0xC0) commands.
+*
+* Uses data and sense buffers in hardware handler context structure and
+* assumes serial servicing of commands, both issuance and completion.
+*/
+static struct request *get_req(struct scsi_device *sdev, int cmd)
+{
+	struct clariion_dh_data *csdev = get_clariion_data(sdev);
+	struct request *rq;
+	unsigned char *page22;
+	int len = 0;
+
+	rq = blk_get_request(sdev->request_queue,
+			(cmd == MODE_SELECT) ? WRITE : READ, GFP_ATOMIC);
+	if (!rq) {
+		sdev_printk(KERN_INFO, sdev, "get_req: blk_get_request failed");
+		return NULL;
+	}
+
+	memset(&rq->cmd, 0, BLK_MAX_CDB);
+	rq->cmd[0] = cmd;
+	rq->cmd_len = COMMAND_SIZE(rq->cmd[0]);
+
+	switch (cmd) {
+	case MODE_SELECT:
+		if (csdev->short_trespass) {
+			page22 = csdev->hr ? short_trespass_hr : short_trespass;
+			len = sizeof(short_trespass);
+		} else {
+			page22 = csdev->hr ? long_trespass_hr : long_trespass;
+			len = sizeof(long_trespass);
+		}
+		/*
+		 * Can't DMA from kernel BSS -- must copy selected trespass
+		 * command mode page contents to context buffer which is
+		 * allocated by kmalloc.
+		 */
+		BUG_ON((len > CLARIION_BUFFER_SIZE));
+		memcpy(csdev->buffer, page22, len);
+		rq->cmd_flags |= REQ_RW;
+		rq->cmd[1] = 0x10;
+		break;
+	case INQUIRY:
+		rq->cmd[1] = 0x1;
+		rq->cmd[2] = 0xC0;
+		len = CLARIION_BUFFER_SIZE;
+		memset(csdev->buffer, 0, CLARIION_BUFFER_SIZE);
+		break;
+	default:
+		BUG_ON(1);
+		break;
+	}
+
+	rq->cmd[4] = len;
+	rq->cmd_type = REQ_TYPE_BLOCK_PC;
+	rq->cmd_flags |= REQ_FAILFAST;
+	rq->timeout = CLARIION_TIMEOUT;
+	rq->retries = CLARIION_RETRIES;
+
+	rq->sense = csdev->sense;
+	memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE);
+	rq->sense_len = 0;
+
+	if (blk_rq_map_kern(sdev->request_queue, rq, csdev->buffer,
+							len, GFP_ATOMIC)) {
+		__blk_put_request(rq->q, rq);
+		return NULL;
+	}
+
+	return rq;
+}
+
+static int send_cmd(struct scsi_device *sdev, int cmd)
+{
+	struct request *rq = get_req(sdev, cmd);
+
+	if (!rq)
+		return SCSI_DH_RES_TEMP_UNAVAIL;
+
+	return blk_execute_rq(sdev->request_queue, NULL, rq, 1);
+}
+
+static int clariion_activate(struct scsi_device *sdev)
+{
+	int result, done = 0;
+
+	result = send_cmd(sdev, INQUIRY);
+	result = sp_info_endio(sdev, result, 0, &done);
+	if (result || done)
+		goto done;
+
+	result = send_cmd(sdev, MODE_SELECT);
+	result = trespass_endio(sdev, result);
+	if (result)
+		goto done;
+
+	result = send_cmd(sdev, INQUIRY);
+	result = sp_info_endio(sdev, result, 1, NULL);
+done:
+	return result;
+}
+
+static int clariion_check_sense(struct scsi_device *sdev,
+				struct scsi_sense_hdr *sense_hdr)
+{
+	switch (sense_hdr->sense_key) {
+	case NOT_READY:
+		if (sense_hdr->asc == 0x04 && sense_hdr->ascq == 0x03)
+			/*
+			 * LUN Not Ready - Manual Intervention Required
+			 * indicates this is a passive path.
+			 *
+			 * FIXME: However, if this is seen and EVPD C0
+			 * indicates that this is due to a NDU in
+			 * progress, we should set FAIL_PATH too.
+			 * This indicates we might have to do a SCSI
+			 * inquiry in the end_io path. Ugh.
+			 *
+			 * Can return FAILED only when we want the error
+			 * recovery process to kick in.
+			 */
+			return SUCCESS;
+		break;
+	case ILLEGAL_REQUEST:
+		if (sense_hdr->asc == 0x25 && sense_hdr->ascq == 0x01)
+			/*
+			 * An array based copy is in progress. Do not
+			 * fail the path, do not bypass to another PG,
+			 * do not retry. Fail the IO immediately.
+			 * (Actually this is the same conclusion as in
+			 * the default handler, but lets make sure.)
+			 *
+			 * Can return FAILED only when we want the error
+			 * recovery process to kick in.
+			 */
+			return SUCCESS;
+		break;
+	case UNIT_ATTENTION:
+		if (sense_hdr->asc == 0x29 && sense_hdr->ascq == 0x00)
+			/*
+			 * Unit Attention Code. This is the first IO
+			 * to the new path, so just retry.
+			 */
+			return NEEDS_RETRY;
+		break;
+	}
+
+	/* success just means we do not care what scsi-ml does */
+	return SUCCESS;
+}
+
+static const struct {
+	char *vendor;
+	char *model;
+} clariion_dev_list[] = {
+	{"DGC", "RAID"},
+	{"DGC", "DISK"},
+	{NULL, NULL},
+};
+
+static int clariion_bus_notify(struct notifier_block *, unsigned long, void *);
+
+static struct scsi_device_handler clariion_dh = {
+	.name		= CLARIION_NAME,
+	.module		= THIS_MODULE,
+	.nb.notifier_call = clariion_bus_notify,
+	.check_sense	= clariion_check_sense,
+	.activate	= clariion_activate,
+};
+
+/*
+ * TODO: need some interface so we can set trespass values
+ */
+static int clariion_bus_notify(struct notifier_block *nb,
+				unsigned long action, void *data)
+{
+	struct device *dev = data;
+	struct scsi_device *sdev = to_scsi_device(dev);
+	struct scsi_dh_data *scsi_dh_data;
+	struct clariion_dh_data *h;
+	int i, found = 0;
+	unsigned long flags;
+
+	if (action == BUS_NOTIFY_ADD_DEVICE) {
+		for (i = 0; clariion_dev_list[i].vendor; i++) {
+			if (!strncmp(sdev->vendor, clariion_dev_list[i].vendor,
+				     strlen(clariion_dev_list[i].vendor)) &&
+			    !strncmp(sdev->model, clariion_dev_list[i].model,
+				     strlen(clariion_dev_list[i].model))) {
+				found = 1;
+				break;
+			}
+		}
+		if (!found)
+			goto out;
+
+		scsi_dh_data = kzalloc(sizeof(struct scsi_device_handler *)
+				+ sizeof(*h) , GFP_KERNEL);
+		if (!scsi_dh_data) {
+			sdev_printk(KERN_ERR, sdev, "Attach failed %s.\n",
+				    CLARIION_NAME);
+			goto out;
+		}
+
+		scsi_dh_data->scsi_dh = &clariion_dh;
+		h = (struct clariion_dh_data *) scsi_dh_data->buf;
+		h->default_sp = CLARIION_UNBOUND_LU;
+		h->current_sp = CLARIION_UNBOUND_LU;
+
+		spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
+		sdev->scsi_dh_data = scsi_dh_data;
+		spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
+
+		sdev_printk(KERN_NOTICE, sdev, "Attached %s.\n", CLARIION_NAME);
+		try_module_get(THIS_MODULE);
+
+	} else if (action == BUS_NOTIFY_DEL_DEVICE) {
+		if (sdev->scsi_dh_data == NULL ||
+				sdev->scsi_dh_data->scsi_dh != &clariion_dh)
+			goto out;
+
+		spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
+		scsi_dh_data = sdev->scsi_dh_data;
+		sdev->scsi_dh_data = NULL;
+		spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
+
+		sdev_printk(KERN_NOTICE, sdev, "Dettached %s.\n",
+			    CLARIION_NAME);
+
+		kfree(scsi_dh_data);
+		module_put(THIS_MODULE);
+	}
+
+out:
+	return 0;
+}
+
+static int __init clariion_init(void)
+{
+	int r;
+
+	r = scsi_register_device_handler(&clariion_dh);
+	if (r != 0)
+		printk(KERN_ERR "Failed to register scsi device handler.");
+	return r;
+}
+
+static void __exit clariion_exit(void)
+{
+	scsi_unregister_device_handler(&clariion_dh);
+}
+
+module_init(clariion_init);
+module_exit(clariion_exit);
+
+MODULE_DESCRIPTION("EMC CX/AX/FC-family driver");
+MODULE_AUTHOR("Mike Christie <michaelc@cs.wisc.edu>, Chandra Seetharaman <sekharan@us.ibm.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/device_handler/scsi_dh_hp_sw.c b/drivers/scsi/device_handler/scsi_dh_hp_sw.c
new file mode 100644
index 0000000..12ceab7
--- /dev/null
+++ b/drivers/scsi/device_handler/scsi_dh_hp_sw.c
@@ -0,0 +1,202 @@
+/*
+ * Basic HP/COMPAQ MSA 1000 support. This is only needed if your HW cannot be
+ * upgraded.
+ *
+ * Copyright (C) 2006 Red Hat, Inc.  All rights reserved.
+ * Copyright (C) 2006 Mike Christie
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_dbg.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_dh.h>
+
+#define HP_SW_NAME	"hp_sw"
+
+#define HP_SW_TIMEOUT (60 * HZ)
+#define HP_SW_RETRIES 3
+
+struct hp_sw_dh_data {
+	unsigned char sense[SCSI_SENSE_BUFFERSIZE];
+	int retries;
+};
+
+static inline struct hp_sw_dh_data *get_hp_sw_data(struct scsi_device *sdev)
+{
+	struct scsi_dh_data *scsi_dh_data = sdev->scsi_dh_data;
+	BUG_ON(scsi_dh_data == NULL);
+	return ((struct hp_sw_dh_data *) scsi_dh_data->buf);
+}
+
+static int hp_sw_done(struct scsi_device *sdev)
+{
+	struct hp_sw_dh_data *h = get_hp_sw_data(sdev);
+	struct scsi_sense_hdr sshdr;
+	int rc;
+
+	sdev_printk(KERN_INFO, sdev, "hp_sw_done\n");
+
+	rc = scsi_normalize_sense(h->sense, SCSI_SENSE_BUFFERSIZE, &sshdr);
+	if (!rc)
+		goto done;
+	switch (sshdr.sense_key) {
+	case NOT_READY:
+		if ((sshdr.asc == 0x04) && (sshdr.ascq == 3)) {
+			rc = SCSI_DH_RETRY;
+			h->retries++;
+			break;
+		}
+		/* fall through */
+	default:
+		h->retries++;
+		rc = SCSI_DH_IMM_RETRY;
+	}
+
+done:
+	if (rc == SCSI_DH_OK || rc == SCSI_DH_IO)
+		h->retries = 0;
+	else if (h->retries > HP_SW_RETRIES) {
+		h->retries = 0;
+		rc = SCSI_DH_IO;
+	}
+	return rc;
+}
+
+static int hp_sw_activate(struct scsi_device *sdev)
+{
+	struct hp_sw_dh_data *h = get_hp_sw_data(sdev);
+	struct request *req;
+	int ret = SCSI_DH_RES_TEMP_UNAVAIL;
+
+	req = blk_get_request(sdev->request_queue, WRITE, GFP_ATOMIC);
+	if (!req)
+		goto done;
+
+	sdev_printk(KERN_INFO, sdev, "sending START_STOP.");
+
+	req->cmd_type = REQ_TYPE_BLOCK_PC;
+	req->cmd_flags |= REQ_FAILFAST;
+	req->cmd_len = COMMAND_SIZE(START_STOP);
+	memset(req->cmd, 0, MAX_COMMAND_SIZE);
+	req->cmd[0] = START_STOP;
+	req->cmd[4] = 1;	/* Start spin cycle */
+	req->timeout = HP_SW_TIMEOUT;
+	req->sense = h->sense;
+	memset(req->sense, 0, SCSI_SENSE_BUFFERSIZE);
+	req->sense_len = 0;
+
+	ret = blk_execute_rq(req->q, NULL, req, 1);
+	if (!ret) /* SUCCESS */
+		ret = hp_sw_done(sdev);
+	else
+		ret = SCSI_DH_IO;
+done:
+	return ret;
+}
+
+static const struct {
+	char *vendor;
+	char *model;
+} hp_sw_dh_data_list[] = {
+	{"COMPAQ", "MSA"},
+	{"HP", "HSV"},
+	{"DEC", "HSG80"},
+	{NULL, NULL},
+};
+
+static int hp_sw_bus_notify(struct notifier_block *, unsigned long, void *);
+
+static struct scsi_device_handler hp_sw_dh = {
+	.name		= HP_SW_NAME,
+	.module		= THIS_MODULE,
+	.nb.notifier_call = hp_sw_bus_notify,
+	.activate	= hp_sw_activate,
+};
+
+static int hp_sw_bus_notify(struct notifier_block *nb,
+			    unsigned long action, void *data)
+{
+	struct device *dev = data;
+	struct scsi_device *sdev = to_scsi_device(dev);
+	struct scsi_dh_data *scsi_dh_data;
+	int i, found = 0;
+	unsigned long flags;
+
+	if (action == BUS_NOTIFY_ADD_DEVICE) {
+		for (i = 0; hp_sw_dh_data_list[i].vendor; i++) {
+			if (!strncmp(sdev->vendor, hp_sw_dh_data_list[i].vendor,
+				     strlen(hp_sw_dh_data_list[i].vendor)) &&
+			    !strncmp(sdev->model, hp_sw_dh_data_list[i].model,
+				     strlen(hp_sw_dh_data_list[i].model))) {
+				found = 1;
+				break;
+			}
+		}
+		if (!found)
+			goto out;
+
+		scsi_dh_data = kzalloc(sizeof(struct scsi_device_handler *)
+				+ sizeof(struct hp_sw_dh_data) , GFP_KERNEL);
+		if (!scsi_dh_data) {
+			sdev_printk(KERN_ERR, sdev, "Attach Failed %s.\n",
+				    HP_SW_NAME);
+			goto out;
+		}
+
+		scsi_dh_data->scsi_dh = &hp_sw_dh;
+		spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
+		sdev->scsi_dh_data = scsi_dh_data;
+		spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
+		try_module_get(THIS_MODULE);
+
+		sdev_printk(KERN_NOTICE, sdev, "Attached %s.\n", HP_SW_NAME);
+	} else if (action == BUS_NOTIFY_DEL_DEVICE) {
+		if (sdev->scsi_dh_data == NULL ||
+				sdev->scsi_dh_data->scsi_dh != &hp_sw_dh)
+			goto out;
+
+		spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
+		scsi_dh_data = sdev->scsi_dh_data;
+		sdev->scsi_dh_data = NULL;
+		spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
+		module_put(THIS_MODULE);
+
+		sdev_printk(KERN_NOTICE, sdev, "Dettached %s.\n", HP_SW_NAME);
+
+		kfree(scsi_dh_data);
+	}
+
+out:
+	return 0;
+}
+
+static int __init hp_sw_init(void)
+{
+	return scsi_register_device_handler(&hp_sw_dh);
+}
+
+static void __exit hp_sw_exit(void)
+{
+	scsi_unregister_device_handler(&hp_sw_dh);
+}
+
+module_init(hp_sw_init);
+module_exit(hp_sw_exit);
+
+MODULE_DESCRIPTION("HP MSA 1000");
+MODULE_AUTHOR("Mike Christie <michaelc@cs.wisc.edu");
+MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/device_handler/scsi_dh_rdac.c b/drivers/scsi/device_handler/scsi_dh_rdac.c
new file mode 100644
index 0000000..6fff077
--- /dev/null
+++ b/drivers/scsi/device_handler/scsi_dh_rdac.c
@@ -0,0 +1,691 @@
+/*
+ * Engenio/LSI RDAC SCSI Device Handler
+ *
+ * Copyright (C) 2005 Mike Christie. All rights reserved.
+ * Copyright (C) Chandra Seetharaman, IBM Corp. 2007
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+#include <scsi/scsi.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_dh.h>
+
+#define RDAC_NAME "rdac"
+
+/*
+ * LSI mode page stuff
+ *
+ * These struct definitions and the forming of the
+ * mode page were taken from the LSI RDAC 2.4 GPL'd
+ * driver, and then converted to Linux conventions.
+ */
+#define RDAC_QUIESCENCE_TIME 20;
+/*
+ * Page Codes
+ */
+#define RDAC_PAGE_CODE_REDUNDANT_CONTROLLER 0x2c
+
+/*
+ * Controller modes definitions
+ */
+#define RDAC_MODE_TRANSFER_SPECIFIED_LUNS	0x02
+
+/*
+ * RDAC Options field
+ */
+#define RDAC_FORCED_QUIESENCE 0x02
+
+#define RDAC_TIMEOUT	(60 * HZ)
+#define RDAC_RETRIES	3
+
+struct rdac_mode_6_hdr {
+	u8	data_len;
+	u8	medium_type;
+	u8	device_params;
+	u8	block_desc_len;
+};
+
+struct rdac_mode_10_hdr {
+	u16	data_len;
+	u8	medium_type;
+	u8	device_params;
+	u16	reserved;
+	u16	block_desc_len;
+};
+
+struct rdac_mode_common {
+	u8	controller_serial[16];
+	u8	alt_controller_serial[16];
+	u8	rdac_mode[2];
+	u8	alt_rdac_mode[2];
+	u8	quiescence_timeout;
+	u8	rdac_options;
+};
+
+struct rdac_pg_legacy {
+	struct rdac_mode_6_hdr hdr;
+	u8	page_code;
+	u8	page_len;
+	struct rdac_mode_common common;
+#define MODE6_MAX_LUN	32
+	u8	lun_table[MODE6_MAX_LUN];
+	u8	reserved2[32];
+	u8	reserved3;
+	u8	reserved4;
+};
+
+struct rdac_pg_expanded {
+	struct rdac_mode_10_hdr hdr;
+	u8	page_code;
+	u8	subpage_code;
+	u8	page_len[2];
+	struct rdac_mode_common common;
+	u8	lun_table[256];
+	u8	reserved3;
+	u8	reserved4;
+};
+
+struct c9_inquiry {
+	u8	peripheral_info;
+	u8	page_code;	/* 0xC9 */
+	u8	reserved1;
+	u8	page_len;
+	u8	page_id[4];	/* "vace" */
+	u8	avte_cvp;
+	u8	path_prio;
+	u8	reserved2[38];
+};
+
+#define SUBSYS_ID_LEN	16
+#define SLOT_ID_LEN	2
+
+struct c4_inquiry {
+	u8	peripheral_info;
+	u8	page_code;	/* 0xC4 */
+	u8	reserved1;
+	u8	page_len;
+	u8	page_id[4];	/* "subs" */
+	u8	subsys_id[SUBSYS_ID_LEN];
+	u8	revision[4];
+	u8	slot_id[SLOT_ID_LEN];
+	u8	reserved[2];
+};
+
+struct rdac_controller {
+	u8			subsys_id[SUBSYS_ID_LEN];
+	u8			slot_id[SLOT_ID_LEN];
+	int			use_ms10;
+	struct kref		kref;
+	struct list_head	node; /* list of all controllers */
+	union			{
+		struct rdac_pg_legacy legacy;
+		struct rdac_pg_expanded expanded;
+	} mode_select;
+};
+struct c8_inquiry {
+	u8	peripheral_info;
+	u8	page_code; /* 0xC8 */
+	u8	reserved1;
+	u8	page_len;
+	u8	page_id[4]; /* "edid" */
+	u8	reserved2[3];
+	u8	vol_uniq_id_len;
+	u8	vol_uniq_id[16];
+	u8	vol_user_label_len;
+	u8	vol_user_label[60];
+	u8	array_uniq_id_len;
+	u8	array_unique_id[16];
+	u8	array_user_label_len;
+	u8	array_user_label[60];
+	u8	lun[8];
+};
+
+struct c2_inquiry {
+	u8	peripheral_info;
+	u8	page_code;	/* 0xC2 */
+	u8	reserved1;
+	u8	page_len;
+	u8	page_id[4];	/* "swr4" */
+	u8	sw_version[3];
+	u8	sw_date[3];
+	u8	features_enabled;
+	u8	max_lun_supported;
+	u8	partitions[239]; /* Total allocation length should be 0xFF */
+};
+
+struct rdac_dh_data {
+	struct rdac_controller	*ctlr;
+#define UNINITIALIZED_LUN	(1 << 8)
+	unsigned		lun;
+#define RDAC_STATE_ACTIVE	0
+#define RDAC_STATE_PASSIVE	1
+	unsigned char		state;
+	unsigned char		sense[SCSI_SENSE_BUFFERSIZE];
+	union			{
+		struct c2_inquiry c2;
+		struct c4_inquiry c4;
+		struct c8_inquiry c8;
+		struct c9_inquiry c9;
+	} inq;
+};
+
+static LIST_HEAD(ctlr_list);
+static DEFINE_SPINLOCK(list_lock);
+
+static inline struct rdac_dh_data *get_rdac_data(struct scsi_device *sdev)
+{
+	struct scsi_dh_data *scsi_dh_data = sdev->scsi_dh_data;
+	BUG_ON(scsi_dh_data == NULL);
+	return ((struct rdac_dh_data *) scsi_dh_data->buf);
+}
+
+static struct request *get_rdac_req(struct scsi_device *sdev,
+			void *buffer, unsigned buflen, int rw)
+{
+	struct request *rq;
+	struct request_queue *q = sdev->request_queue;
+	struct rdac_dh_data *h = get_rdac_data(sdev);
+
+	rq = blk_get_request(q, rw, GFP_KERNEL);
+
+	if (!rq) {
+		sdev_printk(KERN_INFO, sdev,
+				"get_rdac_req: blk_get_request failed.\n");
+		return NULL;
+	}
+
+	if (buflen && blk_rq_map_kern(q, rq, buffer, buflen, GFP_KERNEL)) {
+		blk_put_request(rq);
+		sdev_printk(KERN_INFO, sdev,
+				"get_rdac_req: blk_rq_map_kern failed.\n");
+		return NULL;
+	}
+
+	memset(&rq->cmd, 0, BLK_MAX_CDB);
+	rq->sense = h->sense;
+	memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE);
+	rq->sense_len = 0;
+
+	rq->cmd_type = REQ_TYPE_BLOCK_PC;
+	rq->cmd_flags |= REQ_FAILFAST | REQ_NOMERGE;
+	rq->retries = RDAC_RETRIES;
+	rq->timeout = RDAC_TIMEOUT;
+
+	return rq;
+}
+
+static struct request *rdac_failover_get(struct scsi_device *sdev)
+{
+	struct request *rq;
+	struct rdac_mode_common *common;
+	unsigned data_size;
+	struct rdac_dh_data *h = get_rdac_data(sdev);
+
+	if (h->ctlr->use_ms10) {
+		struct rdac_pg_expanded *rdac_pg;
+
+		data_size = sizeof(struct rdac_pg_expanded);
+		rdac_pg = &h->ctlr->mode_select.expanded;
+		memset(rdac_pg, 0, data_size);
+		common = &rdac_pg->common;
+		rdac_pg->page_code = RDAC_PAGE_CODE_REDUNDANT_CONTROLLER + 0x40;
+		rdac_pg->subpage_code = 0x1;
+		rdac_pg->page_len[0] = 0x01;
+		rdac_pg->page_len[1] = 0x28;
+		rdac_pg->lun_table[h->lun] = 0x81;
+	} else {
+		struct rdac_pg_legacy *rdac_pg;
+
+		data_size = sizeof(struct rdac_pg_legacy);
+		rdac_pg = &h->ctlr->mode_select.legacy;
+		memset(rdac_pg, 0, data_size);
+		common = &rdac_pg->common;
+		rdac_pg->page_code = RDAC_PAGE_CODE_REDUNDANT_CONTROLLER;
+		rdac_pg->page_len = 0x68;
+		rdac_pg->lun_table[h->lun] = 0x81;
+	}
+	common->rdac_mode[1] = RDAC_MODE_TRANSFER_SPECIFIED_LUNS;
+	common->quiescence_timeout = RDAC_QUIESCENCE_TIME;
+	common->rdac_options = RDAC_FORCED_QUIESENCE;
+
+	/* get request for block layer packet command */
+	rq = get_rdac_req(sdev, &h->ctlr->mode_select, data_size, WRITE);
+	if (!rq)
+		return NULL;
+
+	/* Prepare the command. */
+	if (h->ctlr->use_ms10) {
+		rq->cmd[0] = MODE_SELECT_10;
+		rq->cmd[7] = data_size >> 8;
+		rq->cmd[8] = data_size & 0xff;
+	} else {
+		rq->cmd[0] = MODE_SELECT;
+		rq->cmd[4] = data_size;
+	}
+	rq->cmd_len = COMMAND_SIZE(rq->cmd[0]);
+
+	return rq;
+}
+
+static void release_controller(struct kref *kref)
+{
+	struct rdac_controller *ctlr;
+	ctlr = container_of(kref, struct rdac_controller, kref);
+
+	spin_lock(&list_lock);
+	list_del(&ctlr->node);
+	spin_unlock(&list_lock);
+	kfree(ctlr);
+}
+
+static struct rdac_controller *get_controller(u8 *subsys_id, u8 *slot_id)
+{
+	struct rdac_controller *ctlr, *tmp;
+
+	spin_lock(&list_lock);
+
+	list_for_each_entry(tmp, &ctlr_list, node) {
+		if ((memcmp(tmp->subsys_id, subsys_id, SUBSYS_ID_LEN) == 0) &&
+			  (memcmp(tmp->slot_id, slot_id, SLOT_ID_LEN) == 0)) {
+			kref_get(&tmp->kref);
+			spin_unlock(&list_lock);
+			return tmp;
+		}
+	}
+	ctlr = kmalloc(sizeof(*ctlr), GFP_ATOMIC);
+	if (!ctlr)
+		goto done;
+
+	/* initialize fields of controller */
+	memcpy(ctlr->subsys_id, subsys_id, SUBSYS_ID_LEN);
+	memcpy(ctlr->slot_id, slot_id, SLOT_ID_LEN);
+	kref_init(&ctlr->kref);
+	ctlr->use_ms10 = -1;
+	list_add(&ctlr->node, &ctlr_list);
+done:
+	spin_unlock(&list_lock);
+	return ctlr;
+}
+
+static int submit_inquiry(struct scsi_device *sdev, int page_code,
+		unsigned int len)
+{
+	struct request *rq;
+	struct request_queue *q = sdev->request_queue;
+	struct rdac_dh_data *h = get_rdac_data(sdev);
+	int err = SCSI_DH_RES_TEMP_UNAVAIL;
+
+	rq = get_rdac_req(sdev, &h->inq, len, READ);
+	if (!rq)
+		goto done;
+
+	/* Prepare the command. */
+	rq->cmd[0] = INQUIRY;
+	rq->cmd[1] = 1;
+	rq->cmd[2] = page_code;
+	rq->cmd[4] = len;
+	rq->cmd_len = COMMAND_SIZE(INQUIRY);
+	err = blk_execute_rq(q, NULL, rq, 1);
+	if (err == -EIO)
+		err = SCSI_DH_IO;
+done:
+	return err;
+}
+
+static int get_lun(struct scsi_device *sdev)
+{
+	int err;
+	struct c8_inquiry *inqp;
+	struct rdac_dh_data *h = get_rdac_data(sdev);
+
+	err = submit_inquiry(sdev, 0xC8, sizeof(struct c8_inquiry));
+	if (err == SCSI_DH_OK) {
+		inqp = &h->inq.c8;
+		h->lun = inqp->lun[7]; /* currently it uses only one byte */
+	}
+	return err;
+}
+
+#define RDAC_OWNED	0
+#define RDAC_UNOWNED	1
+#define RDAC_FAILED	2
+static int check_ownership(struct scsi_device *sdev)
+{
+	int err;
+	struct c9_inquiry *inqp;
+	struct rdac_dh_data *h = get_rdac_data(sdev);
+
+	err = submit_inquiry(sdev, 0xC9, sizeof(struct c9_inquiry));
+	if (err == SCSI_DH_OK) {
+		err = RDAC_UNOWNED;
+		inqp = &h->inq.c9;
+		/*
+		 * If in AVT mode or if the path already owns the LUN,
+		 * return RDAC_OWNED;
+		 */
+		if (((inqp->avte_cvp >> 7) == 0x1) ||
+				 ((inqp->avte_cvp & 0x1) != 0))
+			err = RDAC_OWNED;
+	} else
+		err = RDAC_FAILED;
+	return err;
+}
+
+static int initialize_controller(struct scsi_device *sdev)
+{
+	int err;
+	struct c4_inquiry *inqp;
+	struct rdac_dh_data *h = get_rdac_data(sdev);
+
+	err = submit_inquiry(sdev, 0xC4, sizeof(struct c4_inquiry));
+	if (err == SCSI_DH_OK) {
+		inqp = &h->inq.c4;
+		h->ctlr = get_controller(inqp->subsys_id, inqp->slot_id);
+		if (!h->ctlr)
+			err = SCSI_DH_RES_TEMP_UNAVAIL;
+	}
+	return err;
+}
+
+static int set_mode_select(struct scsi_device *sdev)
+{
+	int err;
+	struct c2_inquiry *inqp;
+	struct rdac_dh_data *h = get_rdac_data(sdev);
+
+	err = submit_inquiry(sdev, 0xC2, sizeof(struct c2_inquiry));
+	if (err == SCSI_DH_OK) {
+		inqp = &h->inq.c2;
+		/*
+		 * If more than MODE6_MAX_LUN luns are supported, use
+		 * mode select 10
+		 */
+		if (inqp->max_lun_supported >= MODE6_MAX_LUN)
+			h->ctlr->use_ms10 = 1;
+		else
+			h->ctlr->use_ms10 = 0;
+	}
+	return err;
+}
+
+static int mode_select_handle_sense(struct scsi_device *sdev)
+{
+	struct scsi_sense_hdr sense_hdr;
+	struct rdac_dh_data *h = get_rdac_data(sdev);
+	int sense, err = SCSI_DH_IO, ret;
+
+	ret = scsi_normalize_sense(h->sense, SCSI_SENSE_BUFFERSIZE, &sense_hdr);
+	if (!ret)
+		goto done;
+
+	err = SCSI_DH_OK;
+	sense = (sense_hdr.sense_key << 16) | (sense_hdr.asc << 8) |
+			sense_hdr.ascq;
+	/* If it is retryable failure, submit the c9 inquiry again */
+	if (sense == 0x59136 || sense == 0x68b02 || sense == 0xb8b02 ||
+			    sense == 0x62900) {
+		/* 0x59136    - Command lock contention
+		 * 0x[6b]8b02 - Quiesense in progress or achieved
+		 * 0x62900    - Power On, Reset, or Bus Device Reset
+		 */
+		err = SCSI_DH_RETRY;
+	}
+
+	if (sense)
+		sdev_printk(KERN_INFO, sdev,
+			"MODE_SELECT failed with sense 0x%x.\n", sense);
+done:
+	return err;
+}
+
+static int send_mode_select(struct scsi_device *sdev)
+{
+	struct request *rq;
+	struct request_queue *q = sdev->request_queue;
+	struct rdac_dh_data *h = get_rdac_data(sdev);
+	int err = SCSI_DH_RES_TEMP_UNAVAIL;
+
+	rq = rdac_failover_get(sdev);
+	if (!rq)
+		goto done;
+
+	sdev_printk(KERN_INFO, sdev, "queueing MODE_SELECT command.\n");
+
+	err = blk_execute_rq(q, NULL, rq, 1);
+	if (err != SCSI_DH_OK)
+		err = mode_select_handle_sense(sdev);
+	if (err == SCSI_DH_OK)
+		h->state = RDAC_STATE_ACTIVE;
+done:
+	return err;
+}
+
+static int rdac_activate(struct scsi_device *sdev)
+{
+	struct rdac_dh_data *h = get_rdac_data(sdev);
+	int err = SCSI_DH_OK;
+
+	if (h->lun == UNINITIALIZED_LUN) {
+		err = get_lun(sdev);
+		if (err != SCSI_DH_OK)
+			goto done;
+	}
+
+	err = check_ownership(sdev);
+	switch (err) {
+	case RDAC_UNOWNED:
+		break;
+	case RDAC_OWNED:
+		err = SCSI_DH_OK;
+		goto done;
+	case RDAC_FAILED:
+	default:
+		err = SCSI_DH_IO;
+		goto done;
+	}
+
+	if (!h->ctlr) {
+		err = initialize_controller(sdev);
+		if (err != SCSI_DH_OK)
+			goto done;
+	}
+
+	if (h->ctlr->use_ms10 == -1) {
+		err = set_mode_select(sdev);
+		if (err != SCSI_DH_OK)
+			goto done;
+	}
+
+	err = send_mode_select(sdev);
+done:
+	return err;
+}
+
+static int rdac_prep_fn(struct scsi_device *sdev, struct request *req)
+{
+	struct rdac_dh_data *h = get_rdac_data(sdev);
+	int ret = BLKPREP_OK;
+
+	if (h->state != RDAC_STATE_ACTIVE) {
+		ret = BLKPREP_KILL;
+		req->cmd_flags |= REQ_QUIET;
+	}
+	return ret;
+
+}
+
+static int rdac_check_sense(struct scsi_device *sdev,
+				struct scsi_sense_hdr *sense_hdr)
+{
+	struct rdac_dh_data *h = get_rdac_data(sdev);
+	switch (sense_hdr->sense_key) {
+	case NOT_READY:
+		if (sense_hdr->asc == 0x04 && sense_hdr->ascq == 0x81)
+			/* LUN Not Ready - Storage firmware incompatible
+			 * Manual code synchonisation required.
+			 *
+			 * Nothing we can do here. Try to bypass the path.
+			 */
+			return SUCCESS;
+		if (sense_hdr->asc == 0x04 && sense_hdr->ascq == 0xA1)
+			/* LUN Not Ready - Quiescense in progress
+			 *
+			 * Just retry and wait.
+			 */
+			return NEEDS_RETRY;
+		break;
+	case ILLEGAL_REQUEST:
+		if (sense_hdr->asc == 0x94 && sense_hdr->ascq == 0x01) {
+			/* Invalid Request - Current Logical Unit Ownership.
+			 * Controller is not the current owner of the LUN,
+			 * Fail the path, so that the other path be used.
+			 */
+			h->state = RDAC_STATE_PASSIVE;
+			return SUCCESS;
+		}
+		break;
+	case UNIT_ATTENTION:
+		if (sense_hdr->asc == 0x29 && sense_hdr->ascq == 0x00)
+			/*
+			 * Power On, Reset, or Bus Device Reset, just retry.
+			 */
+			return NEEDS_RETRY;
+		break;
+	}
+	/* success just means we do not care what scsi-ml does */
+	return SCSI_RETURN_NOT_HANDLED;
+}
+
+static const struct {
+	char *vendor;
+	char *model;
+} rdac_dev_list[] = {
+	{"IBM", "1722"},
+	{"IBM", "1724"},
+	{"IBM", "1726"},
+	{"IBM", "1742"},
+	{"IBM", "1814"},
+	{"IBM", "1815"},
+	{"IBM", "1818"},
+	{"IBM", "3526"},
+	{"SGI", "TP9400"},
+	{"SGI", "TP9500"},
+	{"SGI", "IS"},
+	{"STK", "OPENstorage D280"},
+	{"SUN", "CSM200_R"},
+	{"SUN", "LCSM100_F"},
+	{NULL, NULL},
+};
+
+static int rdac_bus_notify(struct notifier_block *, unsigned long, void *);
+
+static struct scsi_device_handler rdac_dh = {
+	.name = RDAC_NAME,
+	.module = THIS_MODULE,
+	.nb.notifier_call = rdac_bus_notify,
+	.prep_fn = rdac_prep_fn,
+	.check_sense = rdac_check_sense,
+	.activate = rdac_activate,
+};
+
+/*
+ * TODO: need some interface so we can set trespass values
+ */
+static int rdac_bus_notify(struct notifier_block *nb,
+			    unsigned long action, void *data)
+{
+	struct device *dev = data;
+	struct scsi_device *sdev = to_scsi_device(dev);
+	struct scsi_dh_data *scsi_dh_data;
+	struct rdac_dh_data *h;
+	int i, found = 0;
+	unsigned long flags;
+
+	if (action == BUS_NOTIFY_ADD_DEVICE) {
+		for (i = 0; rdac_dev_list[i].vendor; i++) {
+			if (!strncmp(sdev->vendor, rdac_dev_list[i].vendor,
+				     strlen(rdac_dev_list[i].vendor)) &&
+			    !strncmp(sdev->model, rdac_dev_list[i].model,
+				     strlen(rdac_dev_list[i].model))) {
+				found = 1;
+				break;
+			}
+		}
+		if (!found)
+			goto out;
+
+		scsi_dh_data = kzalloc(sizeof(struct scsi_device_handler *)
+				+ sizeof(*h) , GFP_KERNEL);
+		if (!scsi_dh_data) {
+			sdev_printk(KERN_ERR, sdev, "Attach failed %s.\n",
+				    RDAC_NAME);
+			goto out;
+		}
+
+		scsi_dh_data->scsi_dh = &rdac_dh;
+		h = (struct rdac_dh_data *) scsi_dh_data->buf;
+		h->lun = UNINITIALIZED_LUN;
+		h->state = RDAC_STATE_ACTIVE;
+		spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
+		sdev->scsi_dh_data = scsi_dh_data;
+		spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
+		try_module_get(THIS_MODULE);
+
+		sdev_printk(KERN_NOTICE, sdev, "Attached %s.\n", RDAC_NAME);
+
+	} else if (action == BUS_NOTIFY_DEL_DEVICE) {
+		if (sdev->scsi_dh_data == NULL ||
+				sdev->scsi_dh_data->scsi_dh != &rdac_dh)
+			goto out;
+
+		spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
+		scsi_dh_data = sdev->scsi_dh_data;
+		sdev->scsi_dh_data = NULL;
+		spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
+
+		h = (struct rdac_dh_data *) scsi_dh_data->buf;
+		if (h->ctlr)
+			kref_put(&h->ctlr->kref, release_controller);
+		kfree(scsi_dh_data);
+		module_put(THIS_MODULE);
+		sdev_printk(KERN_NOTICE, sdev, "Dettached %s.\n", RDAC_NAME);
+	}
+
+out:
+	return 0;
+}
+
+static int __init rdac_init(void)
+{
+	int r;
+
+	r = scsi_register_device_handler(&rdac_dh);
+	if (r != 0)
+		printk(KERN_ERR "Failed to register scsi device handler.");
+	return r;
+}
+
+static void __exit rdac_exit(void)
+{
+	scsi_unregister_device_handler(&rdac_dh);
+}
+
+module_init(rdac_init);
+module_exit(rdac_exit);
+
+MODULE_DESCRIPTION("Multipath LSI/Engenio RDAC driver");
+MODULE_AUTHOR("Mike Christie, Chandra Seetharaman");
+MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/esp_scsi.c b/drivers/scsi/esp_scsi.c
index 59fbef0..62a4618 100644
--- a/drivers/scsi/esp_scsi.c
+++ b/drivers/scsi/esp_scsi.c
@@ -219,19 +219,10 @@
 	/* Now reset the ESP chip */
 	scsi_esp_cmd(esp, ESP_CMD_RC);
 	scsi_esp_cmd(esp, ESP_CMD_NULL | ESP_CMD_DMA);
+	if (esp->rev == FAST)
+		esp_write8(ESP_CONFIG2_FENAB, ESP_CFG2);
 	scsi_esp_cmd(esp, ESP_CMD_NULL | ESP_CMD_DMA);
 
-	/* Reload the configuration registers */
-	esp_write8(esp->cfact, ESP_CFACT);
-
-	esp->prev_stp = 0;
-	esp_write8(esp->prev_stp, ESP_STP);
-
-	esp->prev_soff = 0;
-	esp_write8(esp->prev_soff, ESP_SOFF);
-
-	esp_write8(esp->neg_defp, ESP_TIMEO);
-
 	/* This is the only point at which it is reliable to read
 	 * the ID-code for a fast ESP chip variants.
 	 */
@@ -316,6 +307,17 @@
 		break;
 	}
 
+	/* Reload the configuration registers */
+	esp_write8(esp->cfact, ESP_CFACT);
+
+	esp->prev_stp = 0;
+	esp_write8(esp->prev_stp, ESP_STP);
+
+	esp->prev_soff = 0;
+	esp_write8(esp->prev_soff, ESP_SOFF);
+
+	esp_write8(esp->neg_defp, ESP_TIMEO);
+
 	/* Eat any bitrot in the chip */
 	esp_read8(ESP_INTRPT);
 	udelay(100);
diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c
index c6457bf..35cd892 100644
--- a/drivers/scsi/hosts.c
+++ b/drivers/scsi/hosts.c
@@ -290,7 +290,7 @@
 	kfree(shost);
 }
 
-struct device_type scsi_host_type = {
+static struct device_type scsi_host_type = {
 	.name =		"scsi_host",
 	.release =	scsi_host_dev_release,
 };
diff --git a/drivers/scsi/ibmvscsi/Makefile b/drivers/scsi/ibmvscsi/Makefile
index 6ac0633..a423d96 100644
--- a/drivers/scsi/ibmvscsi/Makefile
+++ b/drivers/scsi/ibmvscsi/Makefile
@@ -5,3 +5,4 @@
 ibmvscsic-$(CONFIG_PPC_PSERIES)	+= rpa_vscsi.o 
 
 obj-$(CONFIG_SCSI_IBMVSCSIS)	+= ibmvstgt.o
+obj-$(CONFIG_SCSI_IBMVFC)	+= ibmvfc.o
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c
new file mode 100644
index 0000000..eb702b9
--- /dev/null
+++ b/drivers/scsi/ibmvscsi/ibmvfc.c
@@ -0,0 +1,3910 @@
+/*
+ * ibmvfc.c -- driver for IBM Power Virtual Fibre Channel Adapter
+ *
+ * Written By: Brian King <brking@linux.vnet.ibm.com>, IBM Corporation
+ *
+ * Copyright (C) IBM Corporation, 2008
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/kthread.h>
+#include <linux/of.h>
+#include <linux/stringify.h>
+#include <asm/firmware.h>
+#include <asm/irq.h>
+#include <asm/vio.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsi_transport_fc.h>
+#include "ibmvfc.h"
+
+static unsigned int init_timeout = IBMVFC_INIT_TIMEOUT;
+static unsigned int default_timeout = IBMVFC_DEFAULT_TIMEOUT;
+static unsigned int max_lun = IBMVFC_MAX_LUN;
+static unsigned int max_targets = IBMVFC_MAX_TARGETS;
+static unsigned int max_requests = IBMVFC_MAX_REQUESTS_DEFAULT;
+static unsigned int disc_threads = IBMVFC_MAX_DISC_THREADS;
+static unsigned int dev_loss_tmo = IBMVFC_DEV_LOSS_TMO;
+static unsigned int ibmvfc_debug = IBMVFC_DEBUG;
+static unsigned int log_level = IBMVFC_DEFAULT_LOG_LEVEL;
+static LIST_HEAD(ibmvfc_head);
+static DEFINE_SPINLOCK(ibmvfc_driver_lock);
+static struct scsi_transport_template *ibmvfc_transport_template;
+
+MODULE_DESCRIPTION("IBM Virtual Fibre Channel Driver");
+MODULE_AUTHOR("Brian King <brking@linux.vnet.ibm.com>");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(IBMVFC_DRIVER_VERSION);
+
+module_param_named(init_timeout, init_timeout, uint, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(init_timeout, "Initialization timeout in seconds. "
+		 "[Default=" __stringify(IBMVFC_INIT_TIMEOUT) "]");
+module_param_named(default_timeout, default_timeout, uint, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(default_timeout,
+		 "Default timeout in seconds for initialization and EH commands. "
+		 "[Default=" __stringify(IBMVFC_DEFAULT_TIMEOUT) "]");
+module_param_named(max_requests, max_requests, uint, S_IRUGO);
+MODULE_PARM_DESC(max_requests, "Maximum requests for this adapter. "
+		 "[Default=" __stringify(IBMVFC_MAX_REQUESTS_DEFAULT) "]");
+module_param_named(max_lun, max_lun, uint, S_IRUGO);
+MODULE_PARM_DESC(max_lun, "Maximum allowed LUN. "
+		 "[Default=" __stringify(IBMVFC_MAX_LUN) "]");
+module_param_named(max_targets, max_targets, uint, S_IRUGO);
+MODULE_PARM_DESC(max_targets, "Maximum allowed targets. "
+		 "[Default=" __stringify(IBMVFC_MAX_TARGETS) "]");
+module_param_named(disc_threads, disc_threads, uint, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(disc_threads, "Number of device discovery threads to use. "
+		 "[Default=" __stringify(IBMVFC_MAX_DISC_THREADS) "]");
+module_param_named(debug, ibmvfc_debug, uint, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Enable driver debug information. "
+		 "[Default=" __stringify(IBMVFC_DEBUG) "]");
+module_param_named(dev_loss_tmo, dev_loss_tmo, uint, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(dev_loss_tmo, "Maximum number of seconds that the FC "
+		 "transport should insulate the loss of a remote port. Once this "
+		 "value is exceeded, the scsi target is removed. "
+		 "[Default=" __stringify(IBMVFC_DEV_LOSS_TMO) "]");
+module_param_named(log_level, log_level, uint, 0);
+MODULE_PARM_DESC(log_level, "Set to 0 - 4 for increasing verbosity of device driver. "
+		 "[Default=" __stringify(IBMVFC_DEFAULT_LOG_LEVEL) "]");
+
+static const struct {
+	u16 status;
+	u16 error;
+	u8 result;
+	u8 retry;
+	int log;
+	char *name;
+} cmd_status [] = {
+	{ IBMVFC_FABRIC_MAPPED, IBMVFC_UNABLE_TO_ESTABLISH, DID_ERROR, 1, 1, "unable to establish" },
+	{ IBMVFC_FABRIC_MAPPED, IBMVFC_XPORT_FAULT, DID_OK, 1, 0, "transport fault" },
+	{ IBMVFC_FABRIC_MAPPED, IBMVFC_CMD_TIMEOUT, DID_TIME_OUT, 1, 1, "command timeout" },
+	{ IBMVFC_FABRIC_MAPPED, IBMVFC_ENETDOWN, DID_NO_CONNECT, 1, 1, "network down" },
+	{ IBMVFC_FABRIC_MAPPED, IBMVFC_HW_FAILURE, DID_ERROR, 1, 1, "hardware failure" },
+	{ IBMVFC_FABRIC_MAPPED, IBMVFC_LINK_DOWN_ERR, DID_REQUEUE, 0, 0, "link down" },
+	{ IBMVFC_FABRIC_MAPPED, IBMVFC_LINK_DEAD_ERR, DID_ERROR, 0, 0, "link dead" },
+	{ IBMVFC_FABRIC_MAPPED, IBMVFC_UNABLE_TO_REGISTER, DID_ERROR, 1, 1, "unable to register" },
+	{ IBMVFC_FABRIC_MAPPED, IBMVFC_XPORT_BUSY, DID_BUS_BUSY, 1, 0, "transport busy" },
+	{ IBMVFC_FABRIC_MAPPED, IBMVFC_XPORT_DEAD, DID_ERROR, 0, 1, "transport dead" },
+	{ IBMVFC_FABRIC_MAPPED, IBMVFC_CONFIG_ERROR, DID_ERROR, 1, 1, "configuration error" },
+	{ IBMVFC_FABRIC_MAPPED, IBMVFC_NAME_SERVER_FAIL, DID_ERROR, 1, 1, "name server failure" },
+	{ IBMVFC_FABRIC_MAPPED, IBMVFC_LINK_HALTED, DID_REQUEUE, 0, 0, "link halted" },
+	{ IBMVFC_FABRIC_MAPPED, IBMVFC_XPORT_GENERAL, DID_OK, 1, 0, "general transport error" },
+
+	{ IBMVFC_VIOS_FAILURE, IBMVFC_CRQ_FAILURE, DID_REQUEUE, 1, 1, "CRQ failure" },
+	{ IBMVFC_VIOS_FAILURE, IBMVFC_SW_FAILURE, DID_ERROR, 0, 1, "software failure" },
+	{ IBMVFC_VIOS_FAILURE, IBMVFC_INVALID_PARAMETER, DID_ABORT, 0, 1, "invalid parameter" },
+	{ IBMVFC_VIOS_FAILURE, IBMVFC_MISSING_PARAMETER, DID_ABORT, 0, 1, "missing parameter" },
+	{ IBMVFC_VIOS_FAILURE, IBMVFC_HOST_IO_BUS, DID_ERROR, 1, 1, "host I/O bus failure" },
+	{ IBMVFC_VIOS_FAILURE, IBMVFC_TRANS_CANCELLED, DID_ABORT, 0, 1, "transaction cancelled" },
+	{ IBMVFC_VIOS_FAILURE, IBMVFC_TRANS_CANCELLED_IMPLICIT, DID_ABORT, 0, 1, "transaction cancelled implicit" },
+	{ IBMVFC_VIOS_FAILURE, IBMVFC_INSUFFICIENT_RESOURCE, DID_REQUEUE, 1, 1, "insufficient resources" },
+	{ IBMVFC_VIOS_FAILURE, IBMVFC_COMMAND_FAILED, DID_ERROR, 1, 1, "command failed" },
+
+	{ IBMVFC_FC_FAILURE, IBMVFC_INVALID_ELS_CMD_CODE, DID_ERROR, 0, 1, "invalid ELS command code" },
+	{ IBMVFC_FC_FAILURE, IBMVFC_INVALID_VERSION, DID_ERROR, 0, 1, "invalid version level" },
+	{ IBMVFC_FC_FAILURE, IBMVFC_LOGICAL_ERROR, DID_ERROR, 1, 1, "logical error" },
+	{ IBMVFC_FC_FAILURE, IBMVFC_INVALID_CT_IU_SIZE, DID_ERROR, 0, 1, "invalid CT_IU size" },
+	{ IBMVFC_FC_FAILURE, IBMVFC_LOGICAL_BUSY, DID_REQUEUE, 1, 0, "logical busy" },
+	{ IBMVFC_FC_FAILURE, IBMVFC_PROTOCOL_ERROR, DID_ERROR, 1, 1, "protocol error" },
+	{ IBMVFC_FC_FAILURE, IBMVFC_UNABLE_TO_PERFORM_REQ, DID_ERROR, 1, 1, "unable to perform request" },
+	{ IBMVFC_FC_FAILURE, IBMVFC_CMD_NOT_SUPPORTED, DID_ERROR, 0, 0, "command not supported" },
+	{ IBMVFC_FC_FAILURE, IBMVFC_SERVER_NOT_AVAIL, DID_ERROR, 0, 1, "server not available" },
+	{ IBMVFC_FC_FAILURE, IBMVFC_CMD_IN_PROGRESS, DID_ERROR, 0, 1, "command already in progress" },
+	{ IBMVFC_FC_FAILURE, IBMVFC_VENDOR_SPECIFIC, DID_ERROR, 1, 1, "vendor specific" },
+
+	{ IBMVFC_FC_SCSI_ERROR, 0, DID_OK, 1, 0, "SCSI error" },
+};
+
+static void ibmvfc_npiv_login(struct ibmvfc_host *);
+static void ibmvfc_tgt_send_prli(struct ibmvfc_target *);
+static void ibmvfc_tgt_send_plogi(struct ibmvfc_target *);
+static void ibmvfc_tgt_query_target(struct ibmvfc_target *);
+
+static const char *unknown_error = "unknown error";
+
+#ifdef CONFIG_SCSI_IBMVFC_TRACE
+/**
+ * ibmvfc_trc_start - Log a start trace entry
+ * @evt:		ibmvfc event struct
+ *
+ **/
+static void ibmvfc_trc_start(struct ibmvfc_event *evt)
+{
+	struct ibmvfc_host *vhost = evt->vhost;
+	struct ibmvfc_cmd *vfc_cmd = &evt->iu.cmd;
+	struct ibmvfc_mad_common *mad = &evt->iu.mad_common;
+	struct ibmvfc_trace_entry *entry;
+
+	entry = &vhost->trace[vhost->trace_index++];
+	entry->evt = evt;
+	entry->time = jiffies;
+	entry->fmt = evt->crq.format;
+	entry->type = IBMVFC_TRC_START;
+
+	switch (entry->fmt) {
+	case IBMVFC_CMD_FORMAT:
+		entry->op_code = vfc_cmd->iu.cdb[0];
+		entry->scsi_id = vfc_cmd->tgt_scsi_id;
+		entry->lun = scsilun_to_int(&vfc_cmd->iu.lun);
+		entry->tmf_flags = vfc_cmd->iu.tmf_flags;
+		entry->u.start.xfer_len = vfc_cmd->iu.xfer_len;
+		break;
+	case IBMVFC_MAD_FORMAT:
+		entry->op_code = mad->opcode;
+		break;
+	default:
+		break;
+	};
+}
+
+/**
+ * ibmvfc_trc_end - Log an end trace entry
+ * @evt:		ibmvfc event struct
+ *
+ **/
+static void ibmvfc_trc_end(struct ibmvfc_event *evt)
+{
+	struct ibmvfc_host *vhost = evt->vhost;
+	struct ibmvfc_cmd *vfc_cmd = &evt->xfer_iu->cmd;
+	struct ibmvfc_mad_common *mad = &evt->xfer_iu->mad_common;
+	struct ibmvfc_trace_entry *entry = &vhost->trace[vhost->trace_index++];
+
+	entry->evt = evt;
+	entry->time = jiffies;
+	entry->fmt = evt->crq.format;
+	entry->type = IBMVFC_TRC_END;
+
+	switch (entry->fmt) {
+	case IBMVFC_CMD_FORMAT:
+		entry->op_code = vfc_cmd->iu.cdb[0];
+		entry->scsi_id = vfc_cmd->tgt_scsi_id;
+		entry->lun = scsilun_to_int(&vfc_cmd->iu.lun);
+		entry->tmf_flags = vfc_cmd->iu.tmf_flags;
+		entry->u.end.status = vfc_cmd->status;
+		entry->u.end.error = vfc_cmd->error;
+		entry->u.end.fcp_rsp_flags = vfc_cmd->rsp.flags;
+		entry->u.end.rsp_code = vfc_cmd->rsp.data.info.rsp_code;
+		entry->u.end.scsi_status = vfc_cmd->rsp.scsi_status;
+		break;
+	case IBMVFC_MAD_FORMAT:
+		entry->op_code = mad->opcode;
+		entry->u.end.status = mad->status;
+		break;
+	default:
+		break;
+
+	};
+}
+
+#else
+#define ibmvfc_trc_start(evt) do { } while (0)
+#define ibmvfc_trc_end(evt) do { } while (0)
+#endif
+
+/**
+ * ibmvfc_get_err_index - Find the index into cmd_status for the fcp response
+ * @status:		status / error class
+ * @error:		error
+ *
+ * Return value:
+ *	index into cmd_status / -EINVAL on failure
+ **/
+static int ibmvfc_get_err_index(u16 status, u16 error)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(cmd_status); i++)
+		if ((cmd_status[i].status & status) == cmd_status[i].status &&
+		    cmd_status[i].error == error)
+			return i;
+
+	return -EINVAL;
+}
+
+/**
+ * ibmvfc_get_cmd_error - Find the error description for the fcp response
+ * @status:		status / error class
+ * @error:		error
+ *
+ * Return value:
+ *	error description string
+ **/
+static const char *ibmvfc_get_cmd_error(u16 status, u16 error)
+{
+	int rc = ibmvfc_get_err_index(status, error);
+	if (rc >= 0)
+		return cmd_status[rc].name;
+	return unknown_error;
+}
+
+/**
+ * ibmvfc_get_err_result - Find the scsi status to return for the fcp response
+ * @vfc_cmd:	ibmvfc command struct
+ *
+ * Return value:
+ *	SCSI result value to return for completed command
+ **/
+static int ibmvfc_get_err_result(struct ibmvfc_cmd *vfc_cmd)
+{
+	int err;
+	struct ibmvfc_fcp_rsp *rsp = &vfc_cmd->rsp;
+	int fc_rsp_len = rsp->fcp_rsp_len;
+
+	if ((rsp->flags & FCP_RSP_LEN_VALID) &&
+	    ((!fc_rsp_len && fc_rsp_len != 4 && fc_rsp_len != 8) ||
+	     rsp->data.info.rsp_code))
+		return DID_ERROR << 16;
+
+	if (!vfc_cmd->status) {
+		if (rsp->flags & FCP_RESID_OVER)
+			return rsp->scsi_status | (DID_ERROR << 16);
+		else
+			return rsp->scsi_status | (DID_OK << 16);
+	}
+
+	err = ibmvfc_get_err_index(vfc_cmd->status, vfc_cmd->error);
+	if (err >= 0)
+		return rsp->scsi_status | (cmd_status[err].result << 16);
+	return rsp->scsi_status | (DID_ERROR << 16);
+}
+
+/**
+ * ibmvfc_retry_cmd - Determine if error status is retryable
+ * @status:		status / error class
+ * @error:		error
+ *
+ * Return value:
+ *	1 if error should be retried / 0 if it should not
+ **/
+static int ibmvfc_retry_cmd(u16 status, u16 error)
+{
+	int rc = ibmvfc_get_err_index(status, error);
+
+	if (rc >= 0)
+		return cmd_status[rc].retry;
+	return 1;
+}
+
+static const char *unknown_fc_explain = "unknown fc explain";
+
+static const struct {
+	u16 fc_explain;
+	char *name;
+} ls_explain [] = {
+	{ 0x00, "no additional explanation" },
+	{ 0x01, "service parameter error - options" },
+	{ 0x03, "service parameter error - initiator control" },
+	{ 0x05, "service parameter error - recipient control" },
+	{ 0x07, "service parameter error - received data field size" },
+	{ 0x09, "service parameter error - concurrent seq" },
+	{ 0x0B, "service parameter error - credit" },
+	{ 0x0D, "invalid N_Port/F_Port_Name" },
+	{ 0x0E, "invalid node/Fabric Name" },
+	{ 0x0F, "invalid common service parameters" },
+	{ 0x11, "invalid association header" },
+	{ 0x13, "association header required" },
+	{ 0x15, "invalid originator S_ID" },
+	{ 0x17, "invalid OX_ID-RX-ID combination" },
+	{ 0x19, "command (request) already in progress" },
+	{ 0x1E, "N_Port Login requested" },
+	{ 0x1F, "Invalid N_Port_ID" },
+};
+
+static const struct {
+	u16 fc_explain;
+	char *name;
+} gs_explain [] = {
+	{ 0x00, "no additional explanation" },
+	{ 0x01, "port identifier not registered" },
+	{ 0x02, "port name not registered" },
+	{ 0x03, "node name not registered" },
+	{ 0x04, "class of service not registered" },
+	{ 0x06, "initial process associator not registered" },
+	{ 0x07, "FC-4 TYPEs not registered" },
+	{ 0x08, "symbolic port name not registered" },
+	{ 0x09, "symbolic node name not registered" },
+	{ 0x0A, "port type not registered" },
+	{ 0xF0, "authorization exception" },
+	{ 0xF1, "authentication exception" },
+	{ 0xF2, "data base full" },
+	{ 0xF3, "data base empty" },
+	{ 0xF4, "processing request" },
+	{ 0xF5, "unable to verify connection" },
+	{ 0xF6, "devices not in a common zone" },
+};
+
+/**
+ * ibmvfc_get_ls_explain - Return the FC Explain description text
+ * @status:	FC Explain status
+ *
+ * Returns:
+ *	error string
+ **/
+static const char *ibmvfc_get_ls_explain(u16 status)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(ls_explain); i++)
+		if (ls_explain[i].fc_explain == status)
+			return ls_explain[i].name;
+
+	return unknown_fc_explain;
+}
+
+/**
+ * ibmvfc_get_gs_explain - Return the FC Explain description text
+ * @status:	FC Explain status
+ *
+ * Returns:
+ *	error string
+ **/
+static const char *ibmvfc_get_gs_explain(u16 status)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(gs_explain); i++)
+		if (gs_explain[i].fc_explain == status)
+			return gs_explain[i].name;
+
+	return unknown_fc_explain;
+}
+
+static const struct {
+	enum ibmvfc_fc_type fc_type;
+	char *name;
+} fc_type [] = {
+	{ IBMVFC_FABRIC_REJECT, "fabric reject" },
+	{ IBMVFC_PORT_REJECT, "port reject" },
+	{ IBMVFC_LS_REJECT, "ELS reject" },
+	{ IBMVFC_FABRIC_BUSY, "fabric busy" },
+	{ IBMVFC_PORT_BUSY, "port busy" },
+	{ IBMVFC_BASIC_REJECT, "basic reject" },
+};
+
+static const char *unknown_fc_type = "unknown fc type";
+
+/**
+ * ibmvfc_get_fc_type - Return the FC Type description text
+ * @status:	FC Type error status
+ *
+ * Returns:
+ *	error string
+ **/
+static const char *ibmvfc_get_fc_type(u16 status)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(fc_type); i++)
+		if (fc_type[i].fc_type == status)
+			return fc_type[i].name;
+
+	return unknown_fc_type;
+}
+
+/**
+ * ibmvfc_set_tgt_action - Set the next init action for the target
+ * @tgt:		ibmvfc target struct
+ * @action:		action to perform
+ *
+ **/
+static void ibmvfc_set_tgt_action(struct ibmvfc_target *tgt,
+				  enum ibmvfc_target_action action)
+{
+	switch (tgt->action) {
+	case IBMVFC_TGT_ACTION_DEL_RPORT:
+		break;
+	default:
+		tgt->action = action;
+		break;
+	}
+}
+
+/**
+ * ibmvfc_set_host_state - Set the state for the host
+ * @vhost:		ibmvfc host struct
+ * @state:		state to set host to
+ *
+ * Returns:
+ *	0 if state changed / non-zero if not changed
+ **/
+static int ibmvfc_set_host_state(struct ibmvfc_host *vhost,
+				  enum ibmvfc_host_state state)
+{
+	int rc = 0;
+
+	switch (vhost->state) {
+	case IBMVFC_HOST_OFFLINE:
+		rc = -EINVAL;
+		break;
+	default:
+		vhost->state = state;
+		break;
+	};
+
+	return rc;
+}
+
+/**
+ * ibmvfc_set_host_action - Set the next init action for the host
+ * @vhost:		ibmvfc host struct
+ * @action:		action to perform
+ *
+ **/
+static void ibmvfc_set_host_action(struct ibmvfc_host *vhost,
+				   enum ibmvfc_host_action action)
+{
+	switch (action) {
+	case IBMVFC_HOST_ACTION_ALLOC_TGTS:
+		if (vhost->action == IBMVFC_HOST_ACTION_INIT_WAIT)
+			vhost->action = action;
+		break;
+	case IBMVFC_HOST_ACTION_INIT_WAIT:
+		if (vhost->action == IBMVFC_HOST_ACTION_INIT)
+			vhost->action = action;
+		break;
+	case IBMVFC_HOST_ACTION_QUERY:
+		switch (vhost->action) {
+		case IBMVFC_HOST_ACTION_INIT_WAIT:
+		case IBMVFC_HOST_ACTION_NONE:
+		case IBMVFC_HOST_ACTION_TGT_ADD:
+			vhost->action = action;
+			break;
+		default:
+			break;
+		};
+		break;
+	case IBMVFC_HOST_ACTION_TGT_INIT:
+		if (vhost->action == IBMVFC_HOST_ACTION_ALLOC_TGTS)
+			vhost->action = action;
+		break;
+	case IBMVFC_HOST_ACTION_INIT:
+	case IBMVFC_HOST_ACTION_TGT_DEL:
+	case IBMVFC_HOST_ACTION_QUERY_TGTS:
+	case IBMVFC_HOST_ACTION_TGT_ADD:
+	case IBMVFC_HOST_ACTION_NONE:
+	default:
+		vhost->action = action;
+		break;
+	};
+}
+
+/**
+ * ibmvfc_reinit_host - Re-start host initialization (no NPIV Login)
+ * @vhost:		ibmvfc host struct
+ *
+ * Return value:
+ *	nothing
+ **/
+static void ibmvfc_reinit_host(struct ibmvfc_host *vhost)
+{
+	if (vhost->action == IBMVFC_HOST_ACTION_NONE) {
+		scsi_block_requests(vhost->host);
+		ibmvfc_set_host_state(vhost, IBMVFC_INITIALIZING);
+		ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY);
+	} else
+		vhost->reinit = 1;
+
+	wake_up(&vhost->work_wait_q);
+}
+
+/**
+ * ibmvfc_link_down - Handle a link down event from the adapter
+ * @vhost:	ibmvfc host struct
+ * @state:	ibmvfc host state to enter
+ *
+ **/
+static void ibmvfc_link_down(struct ibmvfc_host *vhost,
+			     enum ibmvfc_host_state state)
+{
+	struct ibmvfc_target *tgt;
+
+	ENTER;
+	scsi_block_requests(vhost->host);
+	list_for_each_entry(tgt, &vhost->targets, queue)
+		ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
+	ibmvfc_set_host_state(vhost, state);
+	ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_TGT_DEL);
+	vhost->events_to_log |= IBMVFC_AE_LINKDOWN;
+	wake_up(&vhost->work_wait_q);
+	LEAVE;
+}
+
+/**
+ * ibmvfc_init_host - Start host initialization
+ * @vhost:		ibmvfc host struct
+ *
+ * Return value:
+ *	nothing
+ **/
+static void ibmvfc_init_host(struct ibmvfc_host *vhost)
+{
+	struct ibmvfc_target *tgt;
+
+	if (vhost->action == IBMVFC_HOST_ACTION_INIT_WAIT) {
+		if (++vhost->init_retries > IBMVFC_MAX_INIT_RETRIES) {
+			dev_err(vhost->dev,
+				"Host initialization retries exceeded. Taking adapter offline\n");
+			ibmvfc_link_down(vhost, IBMVFC_HOST_OFFLINE);
+			return;
+		}
+	}
+
+	if (!ibmvfc_set_host_state(vhost, IBMVFC_INITIALIZING)) {
+		list_for_each_entry(tgt, &vhost->targets, queue)
+			tgt->need_login = 1;
+		scsi_block_requests(vhost->host);
+		ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT);
+		vhost->job_step = ibmvfc_npiv_login;
+		wake_up(&vhost->work_wait_q);
+	}
+}
+
+/**
+ * ibmvfc_send_crq - Send a CRQ
+ * @vhost:	ibmvfc host struct
+ * @word1:	the first 64 bits of the data
+ * @word2:	the second 64 bits of the data
+ *
+ * Return value:
+ *	0 on success / other on failure
+ **/
+static int ibmvfc_send_crq(struct ibmvfc_host *vhost, u64 word1, u64 word2)
+{
+	struct vio_dev *vdev = to_vio_dev(vhost->dev);
+	return plpar_hcall_norets(H_SEND_CRQ, vdev->unit_address, word1, word2);
+}
+
+/**
+ * ibmvfc_send_crq_init - Send a CRQ init message
+ * @vhost:	ibmvfc host struct
+ *
+ * Return value:
+ *	0 on success / other on failure
+ **/
+static int ibmvfc_send_crq_init(struct ibmvfc_host *vhost)
+{
+	ibmvfc_dbg(vhost, "Sending CRQ init\n");
+	return ibmvfc_send_crq(vhost, 0xC001000000000000LL, 0);
+}
+
+/**
+ * ibmvfc_send_crq_init_complete - Send a CRQ init complete message
+ * @vhost:	ibmvfc host struct
+ *
+ * Return value:
+ *	0 on success / other on failure
+ **/
+static int ibmvfc_send_crq_init_complete(struct ibmvfc_host *vhost)
+{
+	ibmvfc_dbg(vhost, "Sending CRQ init complete\n");
+	return ibmvfc_send_crq(vhost, 0xC002000000000000LL, 0);
+}
+
+/**
+ * ibmvfc_release_crq_queue - Deallocates data and unregisters CRQ
+ * @vhost:	ibmvfc host struct
+ *
+ * Frees irq, deallocates a page for messages, unmaps dma, and unregisters
+ * the crq with the hypervisor.
+ **/
+static void ibmvfc_release_crq_queue(struct ibmvfc_host *vhost)
+{
+	long rc;
+	struct vio_dev *vdev = to_vio_dev(vhost->dev);
+	struct ibmvfc_crq_queue *crq = &vhost->crq;
+
+	ibmvfc_dbg(vhost, "Releasing CRQ\n");
+	free_irq(vdev->irq, vhost);
+	do {
+		rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address);
+	} while (rc == H_BUSY || H_IS_LONG_BUSY(rc));
+
+	vhost->state = IBMVFC_NO_CRQ;
+	dma_unmap_single(vhost->dev, crq->msg_token, PAGE_SIZE, DMA_BIDIRECTIONAL);
+	free_page((unsigned long)crq->msgs);
+}
+
+/**
+ * ibmvfc_reenable_crq_queue - reenables the CRQ
+ * @vhost:	ibmvfc host struct
+ *
+ * Return value:
+ *	0 on success / other on failure
+ **/
+static int ibmvfc_reenable_crq_queue(struct ibmvfc_host *vhost)
+{
+	int rc;
+	struct vio_dev *vdev = to_vio_dev(vhost->dev);
+
+	/* Re-enable the CRQ */
+	do {
+		rc = plpar_hcall_norets(H_ENABLE_CRQ, vdev->unit_address);
+	} while (rc == H_IN_PROGRESS || rc == H_BUSY || H_IS_LONG_BUSY(rc));
+
+	if (rc)
+		dev_err(vhost->dev, "Error enabling adapter (rc=%d)\n", rc);
+
+	return rc;
+}
+
+/**
+ * ibmvfc_reset_crq - resets a crq after a failure
+ * @vhost:	ibmvfc host struct
+ *
+ * Return value:
+ *	0 on success / other on failure
+ **/
+static int ibmvfc_reset_crq(struct ibmvfc_host *vhost)
+{
+	int rc;
+	struct vio_dev *vdev = to_vio_dev(vhost->dev);
+	struct ibmvfc_crq_queue *crq = &vhost->crq;
+
+	/* Close the CRQ */
+	do {
+		rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address);
+	} while (rc == H_BUSY || H_IS_LONG_BUSY(rc));
+
+	vhost->state = IBMVFC_NO_CRQ;
+	ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_NONE);
+
+	/* Clean out the queue */
+	memset(crq->msgs, 0, PAGE_SIZE);
+	crq->cur = 0;
+
+	/* And re-open it again */
+	rc = plpar_hcall_norets(H_REG_CRQ, vdev->unit_address,
+				crq->msg_token, PAGE_SIZE);
+
+	if (rc == H_CLOSED)
+		/* Adapter is good, but other end is not ready */
+		dev_warn(vhost->dev, "Partner adapter not ready\n");
+	else if (rc != 0)
+		dev_warn(vhost->dev, "Couldn't register crq (rc=%d)\n", rc);
+
+	return rc;
+}
+
+/**
+ * ibmvfc_valid_event - Determines if event is valid.
+ * @pool:	event_pool that contains the event
+ * @evt:	ibmvfc event to be checked for validity
+ *
+ * Return value:
+ *	1 if event is valid / 0 if event is not valid
+ **/
+static int ibmvfc_valid_event(struct ibmvfc_event_pool *pool,
+			      struct ibmvfc_event *evt)
+{
+	int index = evt - pool->events;
+	if (index < 0 || index >= pool->size)	/* outside of bounds */
+		return 0;
+	if (evt != pool->events + index)	/* unaligned */
+		return 0;
+	return 1;
+}
+
+/**
+ * ibmvfc_free_event - Free the specified event
+ * @evt:	ibmvfc_event to be freed
+ *
+ **/
+static void ibmvfc_free_event(struct ibmvfc_event *evt)
+{
+	struct ibmvfc_host *vhost = evt->vhost;
+	struct ibmvfc_event_pool *pool = &vhost->pool;
+
+	BUG_ON(!ibmvfc_valid_event(pool, evt));
+	BUG_ON(atomic_inc_return(&evt->free) != 1);
+	list_add_tail(&evt->queue, &vhost->free);
+}
+
+/**
+ * ibmvfc_scsi_eh_done - EH done function for queuecommand commands
+ * @evt:	ibmvfc event struct
+ *
+ * This function does not setup any error status, that must be done
+ * before this function gets called.
+ **/
+static void ibmvfc_scsi_eh_done(struct ibmvfc_event *evt)
+{
+	struct scsi_cmnd *cmnd = evt->cmnd;
+
+	if (cmnd) {
+		scsi_dma_unmap(cmnd);
+		cmnd->scsi_done(cmnd);
+	}
+
+	ibmvfc_free_event(evt);
+}
+
+/**
+ * ibmvfc_fail_request - Fail request with specified error code
+ * @evt:		ibmvfc event struct
+ * @error_code:	error code to fail request with
+ *
+ * Return value:
+ *	none
+ **/
+static void ibmvfc_fail_request(struct ibmvfc_event *evt, int error_code)
+{
+	if (evt->cmnd) {
+		evt->cmnd->result = (error_code << 16);
+		evt->done = ibmvfc_scsi_eh_done;
+	} else
+		evt->xfer_iu->mad_common.status = IBMVFC_MAD_DRIVER_FAILED;
+
+	list_del(&evt->queue);
+	del_timer(&evt->timer);
+	ibmvfc_trc_end(evt);
+	evt->done(evt);
+}
+
+/**
+ * ibmvfc_purge_requests - Our virtual adapter just shut down. Purge any sent requests
+ * @vhost:		ibmvfc host struct
+ * @error_code:	error code to fail requests with
+ *
+ * Return value:
+ *	none
+ **/
+static void ibmvfc_purge_requests(struct ibmvfc_host *vhost, int error_code)
+{
+	struct ibmvfc_event *evt, *pos;
+
+	ibmvfc_dbg(vhost, "Purging all requests\n");
+	list_for_each_entry_safe(evt, pos, &vhost->sent, queue)
+		ibmvfc_fail_request(evt, error_code);
+}
+
+/**
+ * __ibmvfc_reset_host - Reset the connection to the server (no locking)
+ * @vhost:	struct ibmvfc host to reset
+ **/
+static void __ibmvfc_reset_host(struct ibmvfc_host *vhost)
+{
+	int rc;
+
+	scsi_block_requests(vhost->host);
+	ibmvfc_purge_requests(vhost, DID_ERROR);
+	if ((rc = ibmvfc_reset_crq(vhost)) ||
+	    (rc = ibmvfc_send_crq_init(vhost)) ||
+	    (rc = vio_enable_interrupts(to_vio_dev(vhost->dev)))) {
+		dev_err(vhost->dev, "Error after reset rc=%d\n", rc);
+		ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
+	} else
+		ibmvfc_link_down(vhost, IBMVFC_LINK_DOWN);
+}
+
+/**
+ * ibmvfc_reset_host - Reset the connection to the server
+ * @vhost:	struct ibmvfc host to reset
+ **/
+static void ibmvfc_reset_host(struct ibmvfc_host *vhost)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(vhost->host->host_lock, flags);
+	__ibmvfc_reset_host(vhost);
+	spin_unlock_irqrestore(vhost->host->host_lock, flags);
+}
+
+/**
+ * ibmvfc_retry_host_init - Retry host initialization if allowed
+ * @vhost:	ibmvfc host struct
+ *
+ **/
+static void ibmvfc_retry_host_init(struct ibmvfc_host *vhost)
+{
+	if (vhost->action == IBMVFC_HOST_ACTION_INIT_WAIT) {
+		if (++vhost->init_retries > IBMVFC_MAX_INIT_RETRIES) {
+			dev_err(vhost->dev,
+				"Host initialization retries exceeded. Taking adapter offline\n");
+			ibmvfc_link_down(vhost, IBMVFC_HOST_OFFLINE);
+		} else if (vhost->init_retries == IBMVFC_MAX_INIT_RETRIES)
+			__ibmvfc_reset_host(vhost);
+		else
+			ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT);
+	}
+
+	wake_up(&vhost->work_wait_q);
+}
+
+/**
+ * __ibmvfc_find_target - Find the specified scsi_target (no locking)
+ * @starget:	scsi target struct
+ *
+ * Return value:
+ *	ibmvfc_target struct / NULL if not found
+ **/
+static struct ibmvfc_target *__ibmvfc_find_target(struct scsi_target *starget)
+{
+	struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+	struct ibmvfc_host *vhost = shost_priv(shost);
+	struct ibmvfc_target *tgt;
+
+	list_for_each_entry(tgt, &vhost->targets, queue)
+		if (tgt->target_id == starget->id)
+			return tgt;
+	return NULL;
+}
+
+/**
+ * ibmvfc_find_target - Find the specified scsi_target
+ * @starget:	scsi target struct
+ *
+ * Return value:
+ *	ibmvfc_target struct / NULL if not found
+ **/
+static struct ibmvfc_target *ibmvfc_find_target(struct scsi_target *starget)
+{
+	struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+	struct ibmvfc_target *tgt;
+	unsigned long flags;
+
+	spin_lock_irqsave(shost->host_lock, flags);
+	tgt = __ibmvfc_find_target(starget);
+	spin_unlock_irqrestore(shost->host_lock, flags);
+	return tgt;
+}
+
+/**
+ * ibmvfc_get_host_speed - Get host port speed
+ * @shost:		scsi host struct
+ *
+ * Return value:
+ * 	none
+ **/
+static void ibmvfc_get_host_speed(struct Scsi_Host *shost)
+{
+	struct ibmvfc_host *vhost = shost_priv(shost);
+	unsigned long flags;
+
+	spin_lock_irqsave(shost->host_lock, flags);
+	if (vhost->state == IBMVFC_ACTIVE) {
+		switch (vhost->login_buf->resp.link_speed / 100) {
+		case 1:
+			fc_host_speed(shost) = FC_PORTSPEED_1GBIT;
+			break;
+		case 2:
+			fc_host_speed(shost) = FC_PORTSPEED_2GBIT;
+			break;
+		case 4:
+			fc_host_speed(shost) = FC_PORTSPEED_4GBIT;
+			break;
+		case 8:
+			fc_host_speed(shost) = FC_PORTSPEED_8GBIT;
+			break;
+		case 10:
+			fc_host_speed(shost) = FC_PORTSPEED_10GBIT;
+			break;
+		case 16:
+			fc_host_speed(shost) = FC_PORTSPEED_16GBIT;
+			break;
+		default:
+			ibmvfc_log(vhost, 3, "Unknown port speed: %ld Gbit\n",
+				   vhost->login_buf->resp.link_speed / 100);
+			fc_host_speed(shost) = FC_PORTSPEED_UNKNOWN;
+			break;
+		}
+	} else
+		fc_host_speed(shost) = FC_PORTSPEED_UNKNOWN;
+	spin_unlock_irqrestore(shost->host_lock, flags);
+}
+
+/**
+ * ibmvfc_get_host_port_state - Get host port state
+ * @shost:		scsi host struct
+ *
+ * Return value:
+ * 	none
+ **/
+static void ibmvfc_get_host_port_state(struct Scsi_Host *shost)
+{
+	struct ibmvfc_host *vhost = shost_priv(shost);
+	unsigned long flags;
+
+	spin_lock_irqsave(shost->host_lock, flags);
+	switch (vhost->state) {
+	case IBMVFC_INITIALIZING:
+	case IBMVFC_ACTIVE:
+		fc_host_port_state(shost) = FC_PORTSTATE_ONLINE;
+		break;
+	case IBMVFC_LINK_DOWN:
+		fc_host_port_state(shost) = FC_PORTSTATE_LINKDOWN;
+		break;
+	case IBMVFC_LINK_DEAD:
+	case IBMVFC_HOST_OFFLINE:
+		fc_host_port_state(shost) = FC_PORTSTATE_OFFLINE;
+		break;
+	case IBMVFC_HALTED:
+		fc_host_port_state(shost) = FC_PORTSTATE_BLOCKED;
+		break;
+	default:
+		ibmvfc_log(vhost, 3, "Unknown port state: %d\n", vhost->state);
+		fc_host_port_state(shost) = FC_PORTSTATE_UNKNOWN;
+		break;
+	}
+	spin_unlock_irqrestore(shost->host_lock, flags);
+}
+
+/**
+ * ibmvfc_set_rport_dev_loss_tmo - Set rport's device loss timeout
+ * @rport:		rport struct
+ * @timeout:	timeout value
+ *
+ * Return value:
+ * 	none
+ **/
+static void ibmvfc_set_rport_dev_loss_tmo(struct fc_rport *rport, u32 timeout)
+{
+	if (timeout)
+		rport->dev_loss_tmo = timeout;
+	else
+		rport->dev_loss_tmo = 1;
+}
+
+/**
+ * ibmvfc_get_starget_node_name - Get SCSI target's node name
+ * @starget:	scsi target struct
+ *
+ * Return value:
+ * 	none
+ **/
+static void ibmvfc_get_starget_node_name(struct scsi_target *starget)
+{
+	struct ibmvfc_target *tgt = ibmvfc_find_target(starget);
+	fc_starget_port_name(starget) = tgt ? tgt->ids.node_name : 0;
+}
+
+/**
+ * ibmvfc_get_starget_port_name - Get SCSI target's port name
+ * @starget:	scsi target struct
+ *
+ * Return value:
+ * 	none
+ **/
+static void ibmvfc_get_starget_port_name(struct scsi_target *starget)
+{
+	struct ibmvfc_target *tgt = ibmvfc_find_target(starget);
+	fc_starget_port_name(starget) = tgt ? tgt->ids.port_name : 0;
+}
+
+/**
+ * ibmvfc_get_starget_port_id - Get SCSI target's port ID
+ * @starget:	scsi target struct
+ *
+ * Return value:
+ * 	none
+ **/
+static void ibmvfc_get_starget_port_id(struct scsi_target *starget)
+{
+	struct ibmvfc_target *tgt = ibmvfc_find_target(starget);
+	fc_starget_port_id(starget) = tgt ? tgt->scsi_id : -1;
+}
+
+/**
+ * ibmvfc_wait_while_resetting - Wait while the host resets
+ * @vhost:		ibmvfc host struct
+ *
+ * Return value:
+ * 	0 on success / other on failure
+ **/
+static int ibmvfc_wait_while_resetting(struct ibmvfc_host *vhost)
+{
+	long timeout = wait_event_timeout(vhost->init_wait_q,
+					  (vhost->state == IBMVFC_ACTIVE ||
+					   vhost->state == IBMVFC_HOST_OFFLINE ||
+					   vhost->state == IBMVFC_LINK_DEAD),
+					  (init_timeout * HZ));
+
+	return timeout ? 0 : -EIO;
+}
+
+/**
+ * ibmvfc_issue_fc_host_lip - Re-initiate link initialization
+ * @shost:		scsi host struct
+ *
+ * Return value:
+ * 	0 on success / other on failure
+ **/
+static int ibmvfc_issue_fc_host_lip(struct Scsi_Host *shost)
+{
+	struct ibmvfc_host *vhost = shost_priv(shost);
+
+	dev_err(vhost->dev, "Initiating host LIP. Resetting connection\n");
+	ibmvfc_reset_host(vhost);
+	return ibmvfc_wait_while_resetting(vhost);
+}
+
+/**
+ * ibmvfc_gather_partition_info - Gather info about the LPAR
+ *
+ * Return value:
+ *	none
+ **/
+static void ibmvfc_gather_partition_info(struct ibmvfc_host *vhost)
+{
+	struct device_node *rootdn;
+	const char *name;
+	const unsigned int *num;
+
+	rootdn = of_find_node_by_path("/");
+	if (!rootdn)
+		return;
+
+	name = of_get_property(rootdn, "ibm,partition-name", NULL);
+	if (name)
+		strncpy(vhost->partition_name, name, sizeof(vhost->partition_name));
+	num = of_get_property(rootdn, "ibm,partition-no", NULL);
+	if (num)
+		vhost->partition_number = *num;
+	of_node_put(rootdn);
+}
+
+/**
+ * ibmvfc_set_login_info - Setup info for NPIV login
+ * @vhost:	ibmvfc host struct
+ *
+ * Return value:
+ *	none
+ **/
+static void ibmvfc_set_login_info(struct ibmvfc_host *vhost)
+{
+	struct ibmvfc_npiv_login *login_info = &vhost->login_info;
+	struct device_node *of_node = vhost->dev->archdata.of_node;
+	const char *location;
+
+	memset(login_info, 0, sizeof(*login_info));
+
+	login_info->ostype = IBMVFC_OS_LINUX;
+	login_info->max_dma_len = IBMVFC_MAX_SECTORS << 9;
+	login_info->max_payload = sizeof(struct ibmvfc_fcp_cmd_iu);
+	login_info->max_response = sizeof(struct ibmvfc_fcp_rsp);
+	login_info->partition_num = vhost->partition_number;
+	login_info->vfc_frame_version = 1;
+	login_info->fcp_version = 3;
+	if (vhost->client_migrated)
+		login_info->flags = IBMVFC_CLIENT_MIGRATED;
+
+	login_info->max_cmds = max_requests + IBMVFC_NUM_INTERNAL_REQ;
+	login_info->capabilities = IBMVFC_CAN_MIGRATE;
+	login_info->async.va = vhost->async_crq.msg_token;
+	login_info->async.len = vhost->async_crq.size;
+	strncpy(login_info->partition_name, vhost->partition_name, IBMVFC_MAX_NAME);
+	strncpy(login_info->device_name,
+		vhost->host->shost_gendev.bus_id, IBMVFC_MAX_NAME);
+
+	location = of_get_property(of_node, "ibm,loc-code", NULL);
+	location = location ? location : vhost->dev->bus_id;
+	strncpy(login_info->drc_name, location, IBMVFC_MAX_NAME);
+}
+
+/**
+ * ibmvfc_init_event_pool - Allocates and initializes the event pool for a host
+ * @vhost:	ibmvfc host who owns the event pool
+ *
+ * Returns zero on success.
+ **/
+static int ibmvfc_init_event_pool(struct ibmvfc_host *vhost)
+{
+	int i;
+	struct ibmvfc_event_pool *pool = &vhost->pool;
+
+	ENTER;
+	pool->size = max_requests + IBMVFC_NUM_INTERNAL_REQ;
+	pool->events = kcalloc(pool->size, sizeof(*pool->events), GFP_KERNEL);
+	if (!pool->events)
+		return -ENOMEM;
+
+	pool->iu_storage = dma_alloc_coherent(vhost->dev,
+					      pool->size * sizeof(*pool->iu_storage),
+					      &pool->iu_token, 0);
+
+	if (!pool->iu_storage) {
+		kfree(pool->events);
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < pool->size; ++i) {
+		struct ibmvfc_event *evt = &pool->events[i];
+		atomic_set(&evt->free, 1);
+		evt->crq.valid = 0x80;
+		evt->crq.ioba = pool->iu_token + (sizeof(*evt->xfer_iu) * i);
+		evt->xfer_iu = pool->iu_storage + i;
+		evt->vhost = vhost;
+		evt->ext_list = NULL;
+		list_add_tail(&evt->queue, &vhost->free);
+	}
+
+	LEAVE;
+	return 0;
+}
+
+/**
+ * ibmvfc_free_event_pool - Frees memory of the event pool of a host
+ * @vhost:	ibmvfc host who owns the event pool
+ *
+ **/
+static void ibmvfc_free_event_pool(struct ibmvfc_host *vhost)
+{
+	int i;
+	struct ibmvfc_event_pool *pool = &vhost->pool;
+
+	ENTER;
+	for (i = 0; i < pool->size; ++i) {
+		list_del(&pool->events[i].queue);
+		BUG_ON(atomic_read(&pool->events[i].free) != 1);
+		if (pool->events[i].ext_list)
+			dma_pool_free(vhost->sg_pool,
+				      pool->events[i].ext_list,
+				      pool->events[i].ext_list_token);
+	}
+
+	kfree(pool->events);
+	dma_free_coherent(vhost->dev,
+			  pool->size * sizeof(*pool->iu_storage),
+			  pool->iu_storage, pool->iu_token);
+	LEAVE;
+}
+
+/**
+ * ibmvfc_get_event - Gets the next free event in pool
+ * @vhost:	ibmvfc host struct
+ *
+ * Returns a free event from the pool.
+ **/
+static struct ibmvfc_event *ibmvfc_get_event(struct ibmvfc_host *vhost)
+{
+	struct ibmvfc_event *evt;
+
+	BUG_ON(list_empty(&vhost->free));
+	evt = list_entry(vhost->free.next, struct ibmvfc_event, queue);
+	atomic_set(&evt->free, 0);
+	list_del(&evt->queue);
+	return evt;
+}
+
+/**
+ * ibmvfc_init_event - Initialize fields in an event struct that are always
+ *				required.
+ * @evt:	The event
+ * @done:	Routine to call when the event is responded to
+ * @format:	SRP or MAD format
+ **/
+static void ibmvfc_init_event(struct ibmvfc_event *evt,
+			      void (*done) (struct ibmvfc_event *), u8 format)
+{
+	evt->cmnd = NULL;
+	evt->sync_iu = NULL;
+	evt->crq.format = format;
+	evt->done = done;
+}
+
+/**
+ * ibmvfc_map_sg_list - Initialize scatterlist
+ * @scmd:	scsi command struct
+ * @nseg:	number of scatterlist segments
+ * @md:	memory descriptor list to initialize
+ **/
+static void ibmvfc_map_sg_list(struct scsi_cmnd *scmd, int nseg,
+			       struct srp_direct_buf *md)
+{
+	int i;
+	struct scatterlist *sg;
+
+	scsi_for_each_sg(scmd, sg, nseg, i) {
+		md[i].va = sg_dma_address(sg);
+		md[i].len = sg_dma_len(sg);
+		md[i].key = 0;
+	}
+}
+
+/**
+ * ibmvfc_map_sg_data - Maps dma for a scatterlist and initializes decriptor fields
+ * @scmd:		Scsi_Cmnd with the scatterlist
+ * @evt:		ibmvfc event struct
+ * @vfc_cmd:	vfc_cmd that contains the memory descriptor
+ * @dev:		device for which to map dma memory
+ *
+ * Returns:
+ *	0 on success / non-zero on failure
+ **/
+static int ibmvfc_map_sg_data(struct scsi_cmnd *scmd,
+			      struct ibmvfc_event *evt,
+			      struct ibmvfc_cmd *vfc_cmd, struct device *dev)
+{
+
+	int sg_mapped;
+	struct srp_direct_buf *data = &vfc_cmd->ioba;
+	struct ibmvfc_host *vhost = dev_get_drvdata(dev);
+
+	sg_mapped = scsi_dma_map(scmd);
+	if (!sg_mapped) {
+		vfc_cmd->flags |= IBMVFC_NO_MEM_DESC;
+		return 0;
+	} else if (unlikely(sg_mapped < 0)) {
+		if (vhost->log_level > IBMVFC_DEFAULT_LOG_LEVEL)
+			scmd_printk(KERN_ERR, scmd, "Failed to map DMA buffer for command\n");
+		return sg_mapped;
+	}
+
+	if (scmd->sc_data_direction == DMA_TO_DEVICE) {
+		vfc_cmd->flags |= IBMVFC_WRITE;
+		vfc_cmd->iu.add_cdb_len |= IBMVFC_WRDATA;
+	} else {
+		vfc_cmd->flags |= IBMVFC_READ;
+		vfc_cmd->iu.add_cdb_len |= IBMVFC_RDDATA;
+	}
+
+	if (sg_mapped == 1) {
+		ibmvfc_map_sg_list(scmd, sg_mapped, data);
+		return 0;
+	}
+
+	vfc_cmd->flags |= IBMVFC_SCATTERLIST;
+
+	if (!evt->ext_list) {
+		evt->ext_list = dma_pool_alloc(vhost->sg_pool, GFP_ATOMIC,
+					       &evt->ext_list_token);
+
+		if (!evt->ext_list) {
+			scmd_printk(KERN_ERR, scmd, "Can't allocate memory for scatterlist\n");
+			return -ENOMEM;
+		}
+	}
+
+	ibmvfc_map_sg_list(scmd, sg_mapped, evt->ext_list);
+
+	data->va = evt->ext_list_token;
+	data->len = sg_mapped * sizeof(struct srp_direct_buf);
+	data->key = 0;
+	return 0;
+}
+
+/**
+ * ibmvfc_timeout - Internal command timeout handler
+ * @evt:	struct ibmvfc_event that timed out
+ *
+ * Called when an internally generated command times out
+ **/
+static void ibmvfc_timeout(struct ibmvfc_event *evt)
+{
+	struct ibmvfc_host *vhost = evt->vhost;
+	dev_err(vhost->dev, "Command timed out (%p). Resetting connection\n", evt);
+	ibmvfc_reset_host(vhost);
+}
+
+/**
+ * ibmvfc_send_event - Transforms event to u64 array and calls send_crq()
+ * @evt:		event to be sent
+ * @vhost:		ibmvfc host struct
+ * @timeout:	timeout in seconds - 0 means do not time command
+ *
+ * Returns the value returned from ibmvfc_send_crq(). (Zero for success)
+ **/
+static int ibmvfc_send_event(struct ibmvfc_event *evt,
+			     struct ibmvfc_host *vhost, unsigned long timeout)
+{
+	u64 *crq_as_u64 = (u64 *) &evt->crq;
+	int rc;
+
+	/* Copy the IU into the transfer area */
+	*evt->xfer_iu = evt->iu;
+	if (evt->crq.format == IBMVFC_CMD_FORMAT)
+		evt->xfer_iu->cmd.tag = (u64)evt;
+	else if (evt->crq.format == IBMVFC_MAD_FORMAT)
+		evt->xfer_iu->mad_common.tag = (u64)evt;
+	else
+		BUG();
+
+	list_add_tail(&evt->queue, &vhost->sent);
+	init_timer(&evt->timer);
+
+	if (timeout) {
+		evt->timer.data = (unsigned long) evt;
+		evt->timer.expires = jiffies + (timeout * HZ);
+		evt->timer.function = (void (*)(unsigned long))ibmvfc_timeout;
+		add_timer(&evt->timer);
+	}
+
+	if ((rc = ibmvfc_send_crq(vhost, crq_as_u64[0], crq_as_u64[1]))) {
+		list_del(&evt->queue);
+		del_timer(&evt->timer);
+
+		/* If send_crq returns H_CLOSED, return SCSI_MLQUEUE_HOST_BUSY.
+		 * Firmware will send a CRQ with a transport event (0xFF) to
+		 * tell this client what has happened to the transport. This
+		 * will be handled in ibmvfc_handle_crq()
+		 */
+		if (rc == H_CLOSED) {
+			if (printk_ratelimit())
+				dev_warn(vhost->dev, "Send warning. Receive queue closed, will retry.\n");
+			if (evt->cmnd)
+				scsi_dma_unmap(evt->cmnd);
+			ibmvfc_free_event(evt);
+			return SCSI_MLQUEUE_HOST_BUSY;
+		}
+
+		dev_err(vhost->dev, "Send error (rc=%d)\n", rc);
+		if (evt->cmnd) {
+			evt->cmnd->result = DID_ERROR << 16;
+			evt->done = ibmvfc_scsi_eh_done;
+		} else
+			evt->xfer_iu->mad_common.status = IBMVFC_MAD_CRQ_ERROR;
+
+		evt->done(evt);
+	} else
+		ibmvfc_trc_start(evt);
+
+	return 0;
+}
+
+/**
+ * ibmvfc_log_error - Log an error for the failed command if appropriate
+ * @evt:	ibmvfc event to log
+ *
+ **/
+static void ibmvfc_log_error(struct ibmvfc_event *evt)
+{
+	struct ibmvfc_cmd *vfc_cmd = &evt->xfer_iu->cmd;
+	struct ibmvfc_host *vhost = evt->vhost;
+	struct ibmvfc_fcp_rsp *rsp = &vfc_cmd->rsp;
+	struct scsi_cmnd *cmnd = evt->cmnd;
+	const char *err = unknown_error;
+	int index = ibmvfc_get_err_index(vfc_cmd->status, vfc_cmd->error);
+	int logerr = 0;
+	int rsp_code = 0;
+
+	if (index >= 0) {
+		logerr = cmd_status[index].log;
+		err = cmd_status[index].name;
+	}
+
+	if (!logerr && (vhost->log_level <= IBMVFC_DEFAULT_LOG_LEVEL))
+		return;
+
+	if (rsp->flags & FCP_RSP_LEN_VALID)
+		rsp_code = rsp->data.info.rsp_code;
+
+	scmd_printk(KERN_ERR, cmnd, "Command (%02X) failed: %s (%x:%x) "
+		    "flags: %x fcp_rsp: %x, resid=%d, scsi_status: %x\n",
+		    cmnd->cmnd[0], err, vfc_cmd->status, vfc_cmd->error,
+		    rsp->flags, rsp_code, scsi_get_resid(cmnd), rsp->scsi_status);
+}
+
+/**
+ * ibmvfc_scsi_done - Handle responses from commands
+ * @evt:	ibmvfc event to be handled
+ *
+ * Used as a callback when sending scsi cmds.
+ **/
+static void ibmvfc_scsi_done(struct ibmvfc_event *evt)
+{
+	struct ibmvfc_cmd *vfc_cmd = &evt->xfer_iu->cmd;
+	struct ibmvfc_fcp_rsp *rsp = &vfc_cmd->rsp;
+	struct scsi_cmnd *cmnd = evt->cmnd;
+	int rsp_len = 0;
+	int sense_len = rsp->fcp_sense_len;
+
+	if (cmnd) {
+		if (vfc_cmd->response_flags & IBMVFC_ADAPTER_RESID_VALID)
+			scsi_set_resid(cmnd, vfc_cmd->adapter_resid);
+		else if (rsp->flags & FCP_RESID_UNDER)
+			scsi_set_resid(cmnd, rsp->fcp_resid);
+		else
+			scsi_set_resid(cmnd, 0);
+
+		if (vfc_cmd->status) {
+			cmnd->result = ibmvfc_get_err_result(vfc_cmd);
+
+			if (rsp->flags & FCP_RSP_LEN_VALID)
+				rsp_len = rsp->fcp_rsp_len;
+			if ((sense_len + rsp_len) > SCSI_SENSE_BUFFERSIZE)
+				sense_len = SCSI_SENSE_BUFFERSIZE - rsp_len;
+			if ((rsp->flags & FCP_SNS_LEN_VALID) && rsp->fcp_sense_len)
+				memcpy(cmnd->sense_buffer, rsp->data.sense + rsp_len, sense_len);
+
+			ibmvfc_log_error(evt);
+		}
+
+		if (!cmnd->result &&
+		    (scsi_bufflen(cmnd) - scsi_get_resid(cmnd) < cmnd->underflow))
+			cmnd->result = (DID_ERROR << 16);
+
+		scsi_dma_unmap(cmnd);
+		cmnd->scsi_done(cmnd);
+	}
+
+	ibmvfc_free_event(evt);
+}
+
+/**
+ * ibmvfc_host_chkready - Check if the host can accept commands
+ * @vhost:	 struct ibmvfc host
+ *
+ * Returns:
+ *	1 if host can accept command / 0 if not
+ **/
+static inline int ibmvfc_host_chkready(struct ibmvfc_host *vhost)
+{
+	int result = 0;
+
+	switch (vhost->state) {
+	case IBMVFC_LINK_DEAD:
+	case IBMVFC_HOST_OFFLINE:
+		result = DID_NO_CONNECT << 16;
+		break;
+	case IBMVFC_NO_CRQ:
+	case IBMVFC_INITIALIZING:
+	case IBMVFC_HALTED:
+	case IBMVFC_LINK_DOWN:
+		result = DID_REQUEUE << 16;
+		break;
+	case IBMVFC_ACTIVE:
+		result = 0;
+		break;
+	};
+
+	return result;
+}
+
+/**
+ * ibmvfc_queuecommand - The queuecommand function of the scsi template
+ * @cmnd:	struct scsi_cmnd to be executed
+ * @done:	Callback function to be called when cmnd is completed
+ *
+ * Returns:
+ *	0 on success / other on failure
+ **/
+static int ibmvfc_queuecommand(struct scsi_cmnd *cmnd,
+			       void (*done) (struct scsi_cmnd *))
+{
+	struct ibmvfc_host *vhost = shost_priv(cmnd->device->host);
+	struct fc_rport *rport = starget_to_rport(scsi_target(cmnd->device));
+	struct ibmvfc_cmd *vfc_cmd;
+	struct ibmvfc_event *evt;
+	u8 tag[2];
+	int rc;
+
+	if (unlikely((rc = fc_remote_port_chkready(rport))) ||
+	    unlikely((rc = ibmvfc_host_chkready(vhost)))) {
+		cmnd->result = rc;
+		done(cmnd);
+		return 0;
+	}
+
+	cmnd->result = (DID_OK << 16);
+	evt = ibmvfc_get_event(vhost);
+	ibmvfc_init_event(evt, ibmvfc_scsi_done, IBMVFC_CMD_FORMAT);
+	evt->cmnd = cmnd;
+	cmnd->scsi_done = done;
+	vfc_cmd = &evt->iu.cmd;
+	memset(vfc_cmd, 0, sizeof(*vfc_cmd));
+	vfc_cmd->resp.va = (u64)evt->crq.ioba + offsetof(struct ibmvfc_cmd, rsp);
+	vfc_cmd->resp.len = sizeof(vfc_cmd->rsp);
+	vfc_cmd->frame_type = IBMVFC_SCSI_FCP_TYPE;
+	vfc_cmd->payload_len = sizeof(vfc_cmd->iu);
+	vfc_cmd->resp_len = sizeof(vfc_cmd->rsp);
+	vfc_cmd->cancel_key = (unsigned long)cmnd->device->hostdata;
+	vfc_cmd->tgt_scsi_id = rport->port_id;
+	if ((rport->supported_classes & FC_COS_CLASS3) &&
+	    (fc_host_supported_classes(vhost->host) & FC_COS_CLASS3))
+		vfc_cmd->flags = IBMVFC_CLASS_3_ERR;
+	vfc_cmd->iu.xfer_len = scsi_bufflen(cmnd);
+	int_to_scsilun(cmnd->device->lun, &vfc_cmd->iu.lun);
+	memcpy(vfc_cmd->iu.cdb, cmnd->cmnd, cmnd->cmd_len);
+
+	if (scsi_populate_tag_msg(cmnd, tag)) {
+		vfc_cmd->task_tag = tag[1];
+		switch (tag[0]) {
+		case MSG_SIMPLE_TAG:
+			vfc_cmd->iu.pri_task_attr = IBMVFC_SIMPLE_TASK;
+			break;
+		case MSG_HEAD_TAG:
+			vfc_cmd->iu.pri_task_attr = IBMVFC_HEAD_OF_QUEUE;
+			break;
+		case MSG_ORDERED_TAG:
+			vfc_cmd->iu.pri_task_attr = IBMVFC_ORDERED_TASK;
+			break;
+		};
+	}
+
+	if (likely(!(rc = ibmvfc_map_sg_data(cmnd, evt, vfc_cmd, vhost->dev))))
+		return ibmvfc_send_event(evt, vhost, 0);
+
+	ibmvfc_free_event(evt);
+	if (rc == -ENOMEM)
+		return SCSI_MLQUEUE_HOST_BUSY;
+
+	if (vhost->log_level > IBMVFC_DEFAULT_LOG_LEVEL)
+		scmd_printk(KERN_ERR, cmnd,
+			    "Failed to map DMA buffer for command. rc=%d\n", rc);
+
+	cmnd->result = DID_ERROR << 16;
+	done(cmnd);
+	return 0;
+}
+
+/**
+ * ibmvfc_sync_completion - Signal that a synchronous command has completed
+ * @evt:	ibmvfc event struct
+ *
+ **/
+static void ibmvfc_sync_completion(struct ibmvfc_event *evt)
+{
+	/* copy the response back */
+	if (evt->sync_iu)
+		*evt->sync_iu = *evt->xfer_iu;
+
+	complete(&evt->comp);
+}
+
+/**
+ * ibmvfc_reset_device - Reset the device with the specified reset type
+ * @sdev:	scsi device to reset
+ * @type:	reset type
+ * @desc:	reset type description for log messages
+ *
+ * Returns:
+ *	0 on success / other on failure
+ **/
+static int ibmvfc_reset_device(struct scsi_device *sdev, int type, char *desc)
+{
+	struct ibmvfc_host *vhost = shost_priv(sdev->host);
+	struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
+	struct ibmvfc_cmd *tmf;
+	struct ibmvfc_event *evt;
+	union ibmvfc_iu rsp_iu;
+	struct ibmvfc_fcp_rsp *fc_rsp = &rsp_iu.cmd.rsp;
+	int rsp_rc = -EBUSY;
+	unsigned long flags;
+	int rsp_code = 0;
+
+	spin_lock_irqsave(vhost->host->host_lock, flags);
+	if (vhost->state == IBMVFC_ACTIVE) {
+		evt = ibmvfc_get_event(vhost);
+		ibmvfc_init_event(evt, ibmvfc_sync_completion, IBMVFC_CMD_FORMAT);
+
+		tmf = &evt->iu.cmd;
+		memset(tmf, 0, sizeof(*tmf));
+		tmf->resp.va = (u64)evt->crq.ioba + offsetof(struct ibmvfc_cmd, rsp);
+		tmf->resp.len = sizeof(tmf->rsp);
+		tmf->frame_type = IBMVFC_SCSI_FCP_TYPE;
+		tmf->payload_len = sizeof(tmf->iu);
+		tmf->resp_len = sizeof(tmf->rsp);
+		tmf->cancel_key = (unsigned long)sdev->hostdata;
+		tmf->tgt_scsi_id = rport->port_id;
+		int_to_scsilun(sdev->lun, &tmf->iu.lun);
+		tmf->flags = (IBMVFC_NO_MEM_DESC | IBMVFC_TMF);
+		tmf->iu.tmf_flags = type;
+		evt->sync_iu = &rsp_iu;
+
+		init_completion(&evt->comp);
+		rsp_rc = ibmvfc_send_event(evt, vhost, default_timeout);
+	}
+	spin_unlock_irqrestore(vhost->host->host_lock, flags);
+
+	if (rsp_rc != 0) {
+		sdev_printk(KERN_ERR, sdev, "Failed to send %s reset event. rc=%d\n",
+			    desc, rsp_rc);
+		return -EIO;
+	}
+
+	sdev_printk(KERN_INFO, sdev, "Resetting %s\n", desc);
+	wait_for_completion(&evt->comp);
+
+	if (rsp_iu.cmd.status) {
+		if (fc_rsp->flags & FCP_RSP_LEN_VALID)
+			rsp_code = fc_rsp->data.info.rsp_code;
+
+		sdev_printk(KERN_ERR, sdev, "%s reset failed: %s (%x:%x) "
+			    "flags: %x fcp_rsp: %x, scsi_status: %x\n",
+			    desc, ibmvfc_get_cmd_error(rsp_iu.cmd.status, rsp_iu.cmd.error),
+			    rsp_iu.cmd.status, rsp_iu.cmd.error, fc_rsp->flags, rsp_code,
+			    fc_rsp->scsi_status);
+		rsp_rc = -EIO;
+	} else
+		sdev_printk(KERN_INFO, sdev, "%s reset successful\n", desc);
+
+	spin_lock_irqsave(vhost->host->host_lock, flags);
+	ibmvfc_free_event(evt);
+	spin_unlock_irqrestore(vhost->host->host_lock, flags);
+	return rsp_rc;
+}
+
+/**
+ * ibmvfc_abort_task_set - Abort outstanding commands to the device
+ * @sdev:	scsi device to abort commands
+ *
+ * This sends an Abort Task Set to the VIOS for the specified device. This does
+ * NOT send any cancel to the VIOS. That must be done separately.
+ *
+ * Returns:
+ *	0 on success / other on failure
+ **/
+static int ibmvfc_abort_task_set(struct scsi_device *sdev)
+{
+	struct ibmvfc_host *vhost = shost_priv(sdev->host);
+	struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
+	struct ibmvfc_cmd *tmf;
+	struct ibmvfc_event *evt, *found_evt;
+	union ibmvfc_iu rsp_iu;
+	struct ibmvfc_fcp_rsp *fc_rsp = &rsp_iu.cmd.rsp;
+	int rsp_rc = -EBUSY;
+	unsigned long flags;
+	int rsp_code = 0;
+
+	spin_lock_irqsave(vhost->host->host_lock, flags);
+	found_evt = NULL;
+	list_for_each_entry(evt, &vhost->sent, queue) {
+		if (evt->cmnd && evt->cmnd->device == sdev) {
+			found_evt = evt;
+			break;
+		}
+	}
+
+	if (!found_evt) {
+		if (vhost->log_level > IBMVFC_DEFAULT_LOG_LEVEL)
+			sdev_printk(KERN_INFO, sdev, "No events found to abort\n");
+		spin_unlock_irqrestore(vhost->host->host_lock, flags);
+		return 0;
+	}
+
+	if (vhost->state == IBMVFC_ACTIVE) {
+		evt = ibmvfc_get_event(vhost);
+		ibmvfc_init_event(evt, ibmvfc_sync_completion, IBMVFC_CMD_FORMAT);
+
+		tmf = &evt->iu.cmd;
+		memset(tmf, 0, sizeof(*tmf));
+		tmf->resp.va = (u64)evt->crq.ioba + offsetof(struct ibmvfc_cmd, rsp);
+		tmf->resp.len = sizeof(tmf->rsp);
+		tmf->frame_type = IBMVFC_SCSI_FCP_TYPE;
+		tmf->payload_len = sizeof(tmf->iu);
+		tmf->resp_len = sizeof(tmf->rsp);
+		tmf->cancel_key = (unsigned long)sdev->hostdata;
+		tmf->tgt_scsi_id = rport->port_id;
+		int_to_scsilun(sdev->lun, &tmf->iu.lun);
+		tmf->flags = (IBMVFC_NO_MEM_DESC | IBMVFC_TMF);
+		tmf->iu.tmf_flags = IBMVFC_ABORT_TASK_SET;
+		evt->sync_iu = &rsp_iu;
+
+		init_completion(&evt->comp);
+		rsp_rc = ibmvfc_send_event(evt, vhost, default_timeout);
+	}
+
+	spin_unlock_irqrestore(vhost->host->host_lock, flags);
+
+	if (rsp_rc != 0) {
+		sdev_printk(KERN_ERR, sdev, "Failed to send abort. rc=%d\n", rsp_rc);
+		return -EIO;
+	}
+
+	sdev_printk(KERN_INFO, sdev, "Aborting outstanding commands\n");
+	wait_for_completion(&evt->comp);
+
+	if (rsp_iu.cmd.status) {
+		if (fc_rsp->flags & FCP_RSP_LEN_VALID)
+			rsp_code = fc_rsp->data.info.rsp_code;
+
+		sdev_printk(KERN_ERR, sdev, "Abort failed: %s (%x:%x) "
+			    "flags: %x fcp_rsp: %x, scsi_status: %x\n",
+			    ibmvfc_get_cmd_error(rsp_iu.cmd.status, rsp_iu.cmd.error),
+			    rsp_iu.cmd.status, rsp_iu.cmd.error, fc_rsp->flags, rsp_code,
+			    fc_rsp->scsi_status);
+		rsp_rc = -EIO;
+	} else
+		sdev_printk(KERN_INFO, sdev, "Abort successful\n");
+
+	spin_lock_irqsave(vhost->host->host_lock, flags);
+	ibmvfc_free_event(evt);
+	spin_unlock_irqrestore(vhost->host->host_lock, flags);
+	return rsp_rc;
+}
+
+/**
+ * ibmvfc_cancel_all - Cancel all outstanding commands to the device
+ * @sdev:	scsi device to cancel commands
+ * @type:	type of error recovery being performed
+ *
+ * This sends a cancel to the VIOS for the specified device. This does
+ * NOT send any abort to the actual device. That must be done separately.
+ *
+ * Returns:
+ *	0 on success / other on failure
+ **/
+static int ibmvfc_cancel_all(struct scsi_device *sdev, int type)
+{
+	struct ibmvfc_host *vhost = shost_priv(sdev->host);
+	struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
+	struct ibmvfc_tmf *tmf;
+	struct ibmvfc_event *evt, *found_evt;
+	union ibmvfc_iu rsp;
+	int rsp_rc = -EBUSY;
+	unsigned long flags;
+	u16 status;
+
+	ENTER;
+	spin_lock_irqsave(vhost->host->host_lock, flags);
+	found_evt = NULL;
+	list_for_each_entry(evt, &vhost->sent, queue) {
+		if (evt->cmnd && evt->cmnd->device == sdev) {
+			found_evt = evt;
+			break;
+		}
+	}
+
+	if (!found_evt) {
+		if (vhost->log_level > IBMVFC_DEFAULT_LOG_LEVEL)
+			sdev_printk(KERN_INFO, sdev, "No events found to cancel\n");
+		spin_unlock_irqrestore(vhost->host->host_lock, flags);
+		return 0;
+	}
+
+	if (vhost->state == IBMVFC_ACTIVE) {
+		evt = ibmvfc_get_event(vhost);
+		ibmvfc_init_event(evt, ibmvfc_sync_completion, IBMVFC_MAD_FORMAT);
+
+		tmf = &evt->iu.tmf;
+		memset(tmf, 0, sizeof(*tmf));
+		tmf->common.version = 1;
+		tmf->common.opcode = IBMVFC_TMF_MAD;
+		tmf->common.length = sizeof(*tmf);
+		tmf->scsi_id = rport->port_id;
+		int_to_scsilun(sdev->lun, &tmf->lun);
+		tmf->flags = (type | IBMVFC_TMF_LUA_VALID);
+		tmf->cancel_key = (unsigned long)sdev->hostdata;
+		tmf->my_cancel_key = (IBMVFC_TMF_CANCEL_KEY | (unsigned long)sdev->hostdata);
+
+		evt->sync_iu = &rsp;
+		init_completion(&evt->comp);
+		rsp_rc = ibmvfc_send_event(evt, vhost, default_timeout);
+	}
+
+	spin_unlock_irqrestore(vhost->host->host_lock, flags);
+
+	if (rsp_rc != 0) {
+		sdev_printk(KERN_ERR, sdev, "Failed to send cancel event. rc=%d\n", rsp_rc);
+		return -EIO;
+	}
+
+	sdev_printk(KERN_INFO, sdev, "Cancelling outstanding commands.\n");
+
+	wait_for_completion(&evt->comp);
+	status = rsp.mad_common.status;
+	spin_lock_irqsave(vhost->host->host_lock, flags);
+	ibmvfc_free_event(evt);
+	spin_unlock_irqrestore(vhost->host->host_lock, flags);
+
+	if (status != IBMVFC_MAD_SUCCESS) {
+		sdev_printk(KERN_WARNING, sdev, "Cancel failed with rc=%x\n", status);
+		return -EIO;
+	}
+
+	sdev_printk(KERN_INFO, sdev, "Successfully cancelled outstanding commands\n");
+	return 0;
+}
+
+/**
+ * ibmvfc_eh_abort_handler - Abort a command
+ * @cmd:	scsi command to abort
+ *
+ * Returns:
+ *	SUCCESS / FAILED
+ **/
+static int ibmvfc_eh_abort_handler(struct scsi_cmnd *cmd)
+{
+	struct ibmvfc_host *vhost = shost_priv(cmd->device->host);
+	struct ibmvfc_event *evt, *pos;
+	int cancel_rc, abort_rc;
+	unsigned long flags;
+
+	ENTER;
+	ibmvfc_wait_while_resetting(vhost);
+	cancel_rc = ibmvfc_cancel_all(cmd->device, IBMVFC_TMF_ABORT_TASK_SET);
+	abort_rc = ibmvfc_abort_task_set(cmd->device);
+
+	if (!cancel_rc && !abort_rc) {
+		spin_lock_irqsave(vhost->host->host_lock, flags);
+		list_for_each_entry_safe(evt, pos, &vhost->sent, queue) {
+			if (evt->cmnd && evt->cmnd->device == cmd->device)
+				ibmvfc_fail_request(evt, DID_ABORT);
+		}
+		spin_unlock_irqrestore(vhost->host->host_lock, flags);
+		LEAVE;
+		return SUCCESS;
+	}
+
+	LEAVE;
+	return FAILED;
+}
+
+/**
+ * ibmvfc_eh_device_reset_handler - Reset a single LUN
+ * @cmd:	scsi command struct
+ *
+ * Returns:
+ *	SUCCESS / FAILED
+ **/
+static int ibmvfc_eh_device_reset_handler(struct scsi_cmnd *cmd)
+{
+	struct ibmvfc_host *vhost = shost_priv(cmd->device->host);
+	struct ibmvfc_event *evt, *pos;
+	int cancel_rc, reset_rc;
+	unsigned long flags;
+
+	ENTER;
+	ibmvfc_wait_while_resetting(vhost);
+	cancel_rc = ibmvfc_cancel_all(cmd->device, IBMVFC_TMF_LUN_RESET);
+	reset_rc = ibmvfc_reset_device(cmd->device, IBMVFC_LUN_RESET, "LUN");
+
+	if (!cancel_rc && !reset_rc) {
+		spin_lock_irqsave(vhost->host->host_lock, flags);
+		list_for_each_entry_safe(evt, pos, &vhost->sent, queue) {
+			if (evt->cmnd && evt->cmnd->device == cmd->device)
+				ibmvfc_fail_request(evt, DID_ABORT);
+		}
+		spin_unlock_irqrestore(vhost->host->host_lock, flags);
+		LEAVE;
+		return SUCCESS;
+	}
+
+	LEAVE;
+	return FAILED;
+}
+
+/**
+ * ibmvfc_dev_cancel_all - Device iterated cancel all function
+ * @sdev:	scsi device struct
+ * @data:	return code
+ *
+ **/
+static void ibmvfc_dev_cancel_all(struct scsi_device *sdev, void *data)
+{
+	unsigned long *rc = data;
+	*rc |= ibmvfc_cancel_all(sdev, IBMVFC_TMF_TGT_RESET);
+}
+
+/**
+ * ibmvfc_dev_abort_all - Device iterated abort task set function
+ * @sdev:	scsi device struct
+ * @data:	return code
+ *
+ **/
+static void ibmvfc_dev_abort_all(struct scsi_device *sdev, void *data)
+{
+	unsigned long *rc = data;
+	*rc |= ibmvfc_abort_task_set(sdev);
+}
+
+/**
+ * ibmvfc_eh_target_reset_handler - Reset the target
+ * @cmd:	scsi command struct
+ *
+ * Returns:
+ *	SUCCESS / FAILED
+ **/
+static int ibmvfc_eh_target_reset_handler(struct scsi_cmnd *cmd)
+{
+	struct ibmvfc_host *vhost = shost_priv(cmd->device->host);
+	struct scsi_target *starget = scsi_target(cmd->device);
+	struct ibmvfc_event *evt, *pos;
+	int reset_rc;
+	unsigned long cancel_rc = 0;
+	unsigned long flags;
+
+	ENTER;
+	ibmvfc_wait_while_resetting(vhost);
+	starget_for_each_device(starget, &cancel_rc, ibmvfc_dev_cancel_all);
+	reset_rc = ibmvfc_reset_device(cmd->device, IBMVFC_TARGET_RESET, "target");
+
+	if (!cancel_rc && !reset_rc) {
+		spin_lock_irqsave(vhost->host->host_lock, flags);
+		list_for_each_entry_safe(evt, pos, &vhost->sent, queue) {
+			if (evt->cmnd && scsi_target(evt->cmnd->device) == starget)
+				ibmvfc_fail_request(evt, DID_ABORT);
+		}
+		spin_unlock_irqrestore(vhost->host->host_lock, flags);
+		LEAVE;
+		return SUCCESS;
+	}
+
+	LEAVE;
+	return FAILED;
+}
+
+/**
+ * ibmvfc_eh_host_reset_handler - Reset the connection to the server
+ * @cmd:	struct scsi_cmnd having problems
+ *
+ **/
+static int ibmvfc_eh_host_reset_handler(struct scsi_cmnd *cmd)
+{
+	int rc;
+	struct ibmvfc_host *vhost = shost_priv(cmd->device->host);
+
+	dev_err(vhost->dev, "Resetting connection due to error recovery\n");
+	rc = ibmvfc_issue_fc_host_lip(vhost->host);
+	return rc ? FAILED : SUCCESS;
+}
+
+/**
+ * ibmvfc_terminate_rport_io - Terminate all pending I/O to the rport.
+ * @rport:		rport struct
+ *
+ * Return value:
+ * 	none
+ **/
+static void ibmvfc_terminate_rport_io(struct fc_rport *rport)
+{
+	struct scsi_target *starget = to_scsi_target(&rport->dev);
+	struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+	struct ibmvfc_host *vhost = shost_priv(shost);
+	struct ibmvfc_event *evt, *pos;
+	unsigned long cancel_rc = 0;
+	unsigned long abort_rc = 0;
+	unsigned long flags;
+
+	ENTER;
+	starget_for_each_device(starget, &cancel_rc, ibmvfc_dev_cancel_all);
+	starget_for_each_device(starget, &abort_rc, ibmvfc_dev_abort_all);
+
+	if (!cancel_rc && !abort_rc) {
+		spin_lock_irqsave(shost->host_lock, flags);
+		list_for_each_entry_safe(evt, pos, &vhost->sent, queue) {
+			if (evt->cmnd && scsi_target(evt->cmnd->device) == starget)
+				ibmvfc_fail_request(evt, DID_ABORT);
+		}
+		spin_unlock_irqrestore(shost->host_lock, flags);
+	} else
+		ibmvfc_issue_fc_host_lip(shost);
+
+	scsi_target_unblock(&rport->dev);
+	LEAVE;
+}
+
+static const struct {
+	enum ibmvfc_async_event ae;
+	const char *desc;
+} ae_desc [] = {
+	{ IBMVFC_AE_ELS_PLOGI,		"PLOGI" },
+	{ IBMVFC_AE_ELS_LOGO,		"LOGO" },
+	{ IBMVFC_AE_ELS_PRLO,		"PRLO" },
+	{ IBMVFC_AE_SCN_NPORT,		"N-Port SCN" },
+	{ IBMVFC_AE_SCN_GROUP,		"Group SCN" },
+	{ IBMVFC_AE_SCN_DOMAIN,		"Domain SCN" },
+	{ IBMVFC_AE_SCN_FABRIC,		"Fabric SCN" },
+	{ IBMVFC_AE_LINK_UP,		"Link Up" },
+	{ IBMVFC_AE_LINK_DOWN,		"Link Down" },
+	{ IBMVFC_AE_LINK_DEAD,		"Link Dead" },
+	{ IBMVFC_AE_HALT,			"Halt" },
+	{ IBMVFC_AE_RESUME,		"Resume" },
+	{ IBMVFC_AE_ADAPTER_FAILED,	"Adapter Failed" },
+};
+
+static const char *unknown_ae = "Unknown async";
+
+/**
+ * ibmvfc_get_ae_desc - Get text description for async event
+ * @ae:	async event
+ *
+ **/
+static const char *ibmvfc_get_ae_desc(u64 ae)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(ae_desc); i++)
+		if (ae_desc[i].ae == ae)
+			return ae_desc[i].desc;
+
+	return unknown_ae;
+}
+
+/**
+ * ibmvfc_handle_async - Handle an async event from the adapter
+ * @crq:	crq to process
+ * @vhost:	ibmvfc host struct
+ *
+ **/
+static void ibmvfc_handle_async(struct ibmvfc_async_crq *crq,
+				struct ibmvfc_host *vhost)
+{
+	const char *desc = ibmvfc_get_ae_desc(crq->event);
+
+	ibmvfc_log(vhost, 2, "%s event received\n", desc);
+
+	switch (crq->event) {
+	case IBMVFC_AE_LINK_UP:
+	case IBMVFC_AE_RESUME:
+		vhost->events_to_log |= IBMVFC_AE_LINKUP;
+		ibmvfc_init_host(vhost);
+		break;
+	case IBMVFC_AE_SCN_FABRIC:
+		vhost->events_to_log |= IBMVFC_AE_RSCN;
+		ibmvfc_init_host(vhost);
+		break;
+	case IBMVFC_AE_SCN_NPORT:
+	case IBMVFC_AE_SCN_GROUP:
+	case IBMVFC_AE_SCN_DOMAIN:
+		vhost->events_to_log |= IBMVFC_AE_RSCN;
+	case IBMVFC_AE_ELS_LOGO:
+	case IBMVFC_AE_ELS_PRLO:
+	case IBMVFC_AE_ELS_PLOGI:
+		ibmvfc_reinit_host(vhost);
+		break;
+	case IBMVFC_AE_LINK_DOWN:
+	case IBMVFC_AE_ADAPTER_FAILED:
+		ibmvfc_link_down(vhost, IBMVFC_LINK_DOWN);
+		break;
+	case IBMVFC_AE_LINK_DEAD:
+		ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
+		break;
+	case IBMVFC_AE_HALT:
+		ibmvfc_link_down(vhost, IBMVFC_HALTED);
+		break;
+	default:
+		dev_err(vhost->dev, "Unknown async event received: %ld\n", crq->event);
+		break;
+	};
+}
+
+/**
+ * ibmvfc_handle_crq - Handles and frees received events in the CRQ
+ * @crq:	Command/Response queue
+ * @vhost:	ibmvfc host struct
+ *
+ **/
+static void ibmvfc_handle_crq(struct ibmvfc_crq *crq, struct ibmvfc_host *vhost)
+{
+	long rc;
+	struct ibmvfc_event *evt = (struct ibmvfc_event *)crq->ioba;
+
+	switch (crq->valid) {
+	case IBMVFC_CRQ_INIT_RSP:
+		switch (crq->format) {
+		case IBMVFC_CRQ_INIT:
+			dev_info(vhost->dev, "Partner initialized\n");
+			/* Send back a response */
+			rc = ibmvfc_send_crq_init_complete(vhost);
+			if (rc == 0)
+				ibmvfc_init_host(vhost);
+			else
+				dev_err(vhost->dev, "Unable to send init rsp. rc=%ld\n", rc);
+			break;
+		case IBMVFC_CRQ_INIT_COMPLETE:
+			dev_info(vhost->dev, "Partner initialization complete\n");
+			ibmvfc_init_host(vhost);
+			break;
+		default:
+			dev_err(vhost->dev, "Unknown crq message type: %d\n", crq->format);
+		}
+		return;
+	case IBMVFC_CRQ_XPORT_EVENT:
+		vhost->state = IBMVFC_NO_CRQ;
+		ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_NONE);
+		if (crq->format == IBMVFC_PARTITION_MIGRATED) {
+			/* We need to re-setup the interpartition connection */
+			dev_info(vhost->dev, "Re-enabling adapter\n");
+			vhost->client_migrated = 1;
+			ibmvfc_purge_requests(vhost, DID_REQUEUE);
+			if ((rc = ibmvfc_reenable_crq_queue(vhost)) ||
+			    (rc = ibmvfc_send_crq_init(vhost))) {
+				ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
+				dev_err(vhost->dev, "Error after enable (rc=%ld)\n", rc);
+			} else
+				ibmvfc_link_down(vhost, IBMVFC_LINK_DOWN);
+		} else {
+			dev_err(vhost->dev, "Virtual adapter failed (rc=%d)\n", crq->format);
+
+			ibmvfc_purge_requests(vhost, DID_ERROR);
+			if ((rc = ibmvfc_reset_crq(vhost)) ||
+			    (rc = ibmvfc_send_crq_init(vhost))) {
+				ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
+				dev_err(vhost->dev, "Error after reset (rc=%ld)\n", rc);
+			} else
+				ibmvfc_link_down(vhost, IBMVFC_LINK_DOWN);
+		}
+		return;
+	case IBMVFC_CRQ_CMD_RSP:
+		break;
+	default:
+		dev_err(vhost->dev, "Got an invalid message type 0x%02x\n", crq->valid);
+		return;
+	}
+
+	if (crq->format == IBMVFC_ASYNC_EVENT)
+		return;
+
+	/* The only kind of payload CRQs we should get are responses to
+	 * things we send. Make sure this response is to something we
+	 * actually sent
+	 */
+	if (unlikely(!ibmvfc_valid_event(&vhost->pool, evt))) {
+		dev_err(vhost->dev, "Returned correlation_token 0x%08lx is invalid!\n",
+			crq->ioba);
+		return;
+	}
+
+	if (unlikely(atomic_read(&evt->free))) {
+		dev_err(vhost->dev, "Received duplicate correlation_token 0x%08lx!\n",
+			crq->ioba);
+		return;
+	}
+
+	del_timer(&evt->timer);
+	list_del(&evt->queue);
+	ibmvfc_trc_end(evt);
+	evt->done(evt);
+}
+
+/**
+ * ibmvfc_scan_finished - Check if the device scan is done.
+ * @shost:	scsi host struct
+ * @time:	current elapsed time
+ *
+ * Returns:
+ *	0 if scan is not done / 1 if scan is done
+ **/
+static int ibmvfc_scan_finished(struct Scsi_Host *shost, unsigned long time)
+{
+	unsigned long flags;
+	struct ibmvfc_host *vhost = shost_priv(shost);
+	int done = 0;
+
+	spin_lock_irqsave(shost->host_lock, flags);
+	if (time >= (init_timeout * HZ)) {
+		dev_info(vhost->dev, "Scan taking longer than %d seconds, "
+			 "continuing initialization\n", init_timeout);
+		done = 1;
+	}
+
+	if (vhost->state != IBMVFC_NO_CRQ && vhost->action == IBMVFC_HOST_ACTION_NONE)
+		done = 1;
+	spin_unlock_irqrestore(shost->host_lock, flags);
+	return done;
+}
+
+/**
+ * ibmvfc_slave_alloc - Setup the device's task set value
+ * @sdev:	struct scsi_device device to configure
+ *
+ * Set the device's task set value so that error handling works as
+ * expected.
+ *
+ * Returns:
+ *	0 on success / -ENXIO if device does not exist
+ **/
+static int ibmvfc_slave_alloc(struct scsi_device *sdev)
+{
+	struct Scsi_Host *shost = sdev->host;
+	struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
+	struct ibmvfc_host *vhost = shost_priv(shost);
+	unsigned long flags = 0;
+
+	if (!rport || fc_remote_port_chkready(rport))
+		return -ENXIO;
+
+	spin_lock_irqsave(shost->host_lock, flags);
+	sdev->hostdata = (void *)(unsigned long)vhost->task_set++;
+	spin_unlock_irqrestore(shost->host_lock, flags);
+	return 0;
+}
+
+/**
+ * ibmvfc_slave_configure - Configure the device
+ * @sdev:	struct scsi_device device to configure
+ *
+ * Enable allow_restart for a device if it is a disk. Adjust the
+ * queue_depth here also.
+ *
+ * Returns:
+ *	0
+ **/
+static int ibmvfc_slave_configure(struct scsi_device *sdev)
+{
+	struct Scsi_Host *shost = sdev->host;
+	struct fc_rport *rport = starget_to_rport(sdev->sdev_target);
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(shost->host_lock, flags);
+	if (sdev->type == TYPE_DISK)
+		sdev->allow_restart = 1;
+
+	if (sdev->tagged_supported) {
+		scsi_set_tag_type(sdev, MSG_SIMPLE_TAG);
+		scsi_activate_tcq(sdev, sdev->queue_depth);
+	} else
+		scsi_deactivate_tcq(sdev, sdev->queue_depth);
+
+	rport->dev_loss_tmo = dev_loss_tmo;
+	spin_unlock_irqrestore(shost->host_lock, flags);
+	return 0;
+}
+
+/**
+ * ibmvfc_change_queue_depth - Change the device's queue depth
+ * @sdev:	scsi device struct
+ * @qdepth:	depth to set
+ *
+ * Return value:
+ * 	actual depth set
+ **/
+static int ibmvfc_change_queue_depth(struct scsi_device *sdev, int qdepth)
+{
+	if (qdepth > IBMVFC_MAX_CMDS_PER_LUN)
+		qdepth = IBMVFC_MAX_CMDS_PER_LUN;
+
+	scsi_adjust_queue_depth(sdev, 0, qdepth);
+	return sdev->queue_depth;
+}
+
+/**
+ * ibmvfc_change_queue_type - Change the device's queue type
+ * @sdev:		scsi device struct
+ * @tag_type:	type of tags to use
+ *
+ * Return value:
+ * 	actual queue type set
+ **/
+static int ibmvfc_change_queue_type(struct scsi_device *sdev, int tag_type)
+{
+	if (sdev->tagged_supported) {
+		scsi_set_tag_type(sdev, tag_type);
+
+		if (tag_type)
+			scsi_activate_tcq(sdev, sdev->queue_depth);
+		else
+			scsi_deactivate_tcq(sdev, sdev->queue_depth);
+	} else
+		tag_type = 0;
+
+	return tag_type;
+}
+
+static ssize_t ibmvfc_show_host_partition_name(struct device *dev,
+						 struct device_attribute *attr, char *buf)
+{
+	struct Scsi_Host *shost = class_to_shost(dev);
+	struct ibmvfc_host *vhost = shost_priv(shost);
+
+	return snprintf(buf, PAGE_SIZE, "%s\n",
+			vhost->login_buf->resp.partition_name);
+}
+
+static struct device_attribute ibmvfc_host_partition_name = {
+	.attr = {
+		.name = "partition_name",
+		.mode = S_IRUGO,
+	},
+	.show = ibmvfc_show_host_partition_name,
+};
+
+static ssize_t ibmvfc_show_host_device_name(struct device *dev,
+					    struct device_attribute *attr, char *buf)
+{
+	struct Scsi_Host *shost = class_to_shost(dev);
+	struct ibmvfc_host *vhost = shost_priv(shost);
+
+	return snprintf(buf, PAGE_SIZE, "%s\n",
+			vhost->login_buf->resp.device_name);
+}
+
+static struct device_attribute ibmvfc_host_device_name = {
+	.attr = {
+		.name = "device_name",
+		.mode = S_IRUGO,
+	},
+	.show = ibmvfc_show_host_device_name,
+};
+
+static ssize_t ibmvfc_show_host_loc_code(struct device *dev,
+					 struct device_attribute *attr, char *buf)
+{
+	struct Scsi_Host *shost = class_to_shost(dev);
+	struct ibmvfc_host *vhost = shost_priv(shost);
+
+	return snprintf(buf, PAGE_SIZE, "%s\n",
+			vhost->login_buf->resp.port_loc_code);
+}
+
+static struct device_attribute ibmvfc_host_loc_code = {
+	.attr = {
+		.name = "port_loc_code",
+		.mode = S_IRUGO,
+	},
+	.show = ibmvfc_show_host_loc_code,
+};
+
+static ssize_t ibmvfc_show_host_drc_name(struct device *dev,
+					 struct device_attribute *attr, char *buf)
+{
+	struct Scsi_Host *shost = class_to_shost(dev);
+	struct ibmvfc_host *vhost = shost_priv(shost);
+
+	return snprintf(buf, PAGE_SIZE, "%s\n",
+			vhost->login_buf->resp.drc_name);
+}
+
+static struct device_attribute ibmvfc_host_drc_name = {
+	.attr = {
+		.name = "drc_name",
+		.mode = S_IRUGO,
+	},
+	.show = ibmvfc_show_host_drc_name,
+};
+
+static ssize_t ibmvfc_show_host_npiv_version(struct device *dev,
+					     struct device_attribute *attr, char *buf)
+{
+	struct Scsi_Host *shost = class_to_shost(dev);
+	struct ibmvfc_host *vhost = shost_priv(shost);
+	return snprintf(buf, PAGE_SIZE, "%d\n", vhost->login_buf->resp.version);
+}
+
+static struct device_attribute ibmvfc_host_npiv_version = {
+	.attr = {
+		.name = "npiv_version",
+		.mode = S_IRUGO,
+	},
+	.show = ibmvfc_show_host_npiv_version,
+};
+
+/**
+ * ibmvfc_show_log_level - Show the adapter's error logging level
+ * @dev:	class device struct
+ * @buf:	buffer
+ *
+ * Return value:
+ * 	number of bytes printed to buffer
+ **/
+static ssize_t ibmvfc_show_log_level(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	struct Scsi_Host *shost = class_to_shost(dev);
+	struct ibmvfc_host *vhost = shost_priv(shost);
+	unsigned long flags = 0;
+	int len;
+
+	spin_lock_irqsave(shost->host_lock, flags);
+	len = snprintf(buf, PAGE_SIZE, "%d\n", vhost->log_level);
+	spin_unlock_irqrestore(shost->host_lock, flags);
+	return len;
+}
+
+/**
+ * ibmvfc_store_log_level - Change the adapter's error logging level
+ * @dev:	class device struct
+ * @buf:	buffer
+ *
+ * Return value:
+ * 	number of bytes printed to buffer
+ **/
+static ssize_t ibmvfc_store_log_level(struct device *dev,
+				      struct device_attribute *attr,
+				      const char *buf, size_t count)
+{
+	struct Scsi_Host *shost = class_to_shost(dev);
+	struct ibmvfc_host *vhost = shost_priv(shost);
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(shost->host_lock, flags);
+	vhost->log_level = simple_strtoul(buf, NULL, 10);
+	spin_unlock_irqrestore(shost->host_lock, flags);
+	return strlen(buf);
+}
+
+static struct device_attribute ibmvfc_log_level_attr = {
+	.attr = {
+		.name =		"log_level",
+		.mode =		S_IRUGO | S_IWUSR,
+	},
+	.show = ibmvfc_show_log_level,
+	.store = ibmvfc_store_log_level
+};
+
+#ifdef CONFIG_SCSI_IBMVFC_TRACE
+/**
+ * ibmvfc_read_trace - Dump the adapter trace
+ * @kobj:		kobject struct
+ * @bin_attr:	bin_attribute struct
+ * @buf:		buffer
+ * @off:		offset
+ * @count:		buffer size
+ *
+ * Return value:
+ *	number of bytes printed to buffer
+ **/
+static ssize_t ibmvfc_read_trace(struct kobject *kobj,
+				 struct bin_attribute *bin_attr,
+				 char *buf, loff_t off, size_t count)
+{
+	struct device *dev = container_of(kobj, struct device, kobj);
+	struct Scsi_Host *shost = class_to_shost(dev);
+	struct ibmvfc_host *vhost = shost_priv(shost);
+	unsigned long flags = 0;
+	int size = IBMVFC_TRACE_SIZE;
+	char *src = (char *)vhost->trace;
+
+	if (off > size)
+		return 0;
+	if (off + count > size) {
+		size -= off;
+		count = size;
+	}
+
+	spin_lock_irqsave(shost->host_lock, flags);
+	memcpy(buf, &src[off], count);
+	spin_unlock_irqrestore(shost->host_lock, flags);
+	return count;
+}
+
+static struct bin_attribute ibmvfc_trace_attr = {
+	.attr =	{
+		.name = "trace",
+		.mode = S_IRUGO,
+	},
+	.size = 0,
+	.read = ibmvfc_read_trace,
+};
+#endif
+
+static struct device_attribute *ibmvfc_attrs[] = {
+	&ibmvfc_host_partition_name,
+	&ibmvfc_host_device_name,
+	&ibmvfc_host_loc_code,
+	&ibmvfc_host_drc_name,
+	&ibmvfc_host_npiv_version,
+	&ibmvfc_log_level_attr,
+	NULL
+};
+
+static struct scsi_host_template driver_template = {
+	.module = THIS_MODULE,
+	.name = "IBM POWER Virtual FC Adapter",
+	.proc_name = IBMVFC_NAME,
+	.queuecommand = ibmvfc_queuecommand,
+	.eh_abort_handler = ibmvfc_eh_abort_handler,
+	.eh_device_reset_handler = ibmvfc_eh_device_reset_handler,
+	.eh_target_reset_handler = ibmvfc_eh_target_reset_handler,
+	.eh_host_reset_handler = ibmvfc_eh_host_reset_handler,
+	.slave_alloc = ibmvfc_slave_alloc,
+	.slave_configure = ibmvfc_slave_configure,
+	.scan_finished = ibmvfc_scan_finished,
+	.change_queue_depth = ibmvfc_change_queue_depth,
+	.change_queue_type = ibmvfc_change_queue_type,
+	.cmd_per_lun = 16,
+	.can_queue = IBMVFC_MAX_REQUESTS_DEFAULT,
+	.this_id = -1,
+	.sg_tablesize = SG_ALL,
+	.max_sectors = IBMVFC_MAX_SECTORS,
+	.use_clustering = ENABLE_CLUSTERING,
+	.shost_attrs = ibmvfc_attrs,
+};
+
+/**
+ * ibmvfc_next_async_crq - Returns the next entry in async queue
+ * @vhost:	ibmvfc host struct
+ *
+ * Returns:
+ *	Pointer to next entry in queue / NULL if empty
+ **/
+static struct ibmvfc_async_crq *ibmvfc_next_async_crq(struct ibmvfc_host *vhost)
+{
+	struct ibmvfc_async_crq_queue *async_crq = &vhost->async_crq;
+	struct ibmvfc_async_crq *crq;
+
+	crq = &async_crq->msgs[async_crq->cur];
+	if (crq->valid & 0x80) {
+		if (++async_crq->cur == async_crq->size)
+			async_crq->cur = 0;
+	} else
+		crq = NULL;
+
+	return crq;
+}
+
+/**
+ * ibmvfc_next_crq - Returns the next entry in message queue
+ * @vhost:	ibmvfc host struct
+ *
+ * Returns:
+ *	Pointer to next entry in queue / NULL if empty
+ **/
+static struct ibmvfc_crq *ibmvfc_next_crq(struct ibmvfc_host *vhost)
+{
+	struct ibmvfc_crq_queue *queue = &vhost->crq;
+	struct ibmvfc_crq *crq;
+
+	crq = &queue->msgs[queue->cur];
+	if (crq->valid & 0x80) {
+		if (++queue->cur == queue->size)
+			queue->cur = 0;
+	} else
+		crq = NULL;
+
+	return crq;
+}
+
+/**
+ * ibmvfc_interrupt - Interrupt handler
+ * @irq:		number of irq to handle, not used
+ * @dev_instance: ibmvfc_host that received interrupt
+ *
+ * Returns:
+ *	IRQ_HANDLED
+ **/
+static irqreturn_t ibmvfc_interrupt(int irq, void *dev_instance)
+{
+	struct ibmvfc_host *vhost = (struct ibmvfc_host *)dev_instance;
+	struct vio_dev *vdev = to_vio_dev(vhost->dev);
+	struct ibmvfc_crq *crq;
+	struct ibmvfc_async_crq *async;
+	unsigned long flags;
+	int done = 0;
+
+	spin_lock_irqsave(vhost->host->host_lock, flags);
+	vio_disable_interrupts(to_vio_dev(vhost->dev));
+	while (!done) {
+		/* Pull all the valid messages off the CRQ */
+		while ((crq = ibmvfc_next_crq(vhost)) != NULL) {
+			ibmvfc_handle_crq(crq, vhost);
+			crq->valid = 0;
+		}
+
+		/* Pull all the valid messages off the async CRQ */
+		while ((async = ibmvfc_next_async_crq(vhost)) != NULL) {
+			ibmvfc_handle_async(async, vhost);
+			async->valid = 0;
+		}
+
+		vio_enable_interrupts(vdev);
+		if ((crq = ibmvfc_next_crq(vhost)) != NULL) {
+			vio_disable_interrupts(vdev);
+			ibmvfc_handle_crq(crq, vhost);
+			crq->valid = 0;
+		} else if ((async = ibmvfc_next_async_crq(vhost)) != NULL) {
+			vio_disable_interrupts(vdev);
+			ibmvfc_handle_async(async, vhost);
+			crq->valid = 0;
+		} else
+			done = 1;
+	}
+
+	spin_unlock_irqrestore(vhost->host->host_lock, flags);
+	return IRQ_HANDLED;
+}
+
+/**
+ * ibmvfc_init_tgt - Set the next init job step for the target
+ * @tgt:		ibmvfc target struct
+ * @job_step:	job step to perform
+ *
+ **/
+static void ibmvfc_init_tgt(struct ibmvfc_target *tgt,
+			    void (*job_step) (struct ibmvfc_target *))
+{
+	ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_INIT);
+	tgt->job_step = job_step;
+	wake_up(&tgt->vhost->work_wait_q);
+}
+
+/**
+ * ibmvfc_retry_tgt_init - Attempt to retry a step in target initialization
+ * @tgt:		ibmvfc target struct
+ * @job_step:	initialization job step
+ *
+ **/
+static void ibmvfc_retry_tgt_init(struct ibmvfc_target *tgt,
+				  void (*job_step) (struct ibmvfc_target *))
+{
+	if (++tgt->init_retries > IBMVFC_MAX_INIT_RETRIES) {
+		ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
+		wake_up(&tgt->vhost->work_wait_q);
+	} else
+		ibmvfc_init_tgt(tgt, job_step);
+}
+
+/**
+ * ibmvfc_release_tgt - Free memory allocated for a target
+ * @kref:		kref struct
+ *
+ **/
+static void ibmvfc_release_tgt(struct kref *kref)
+{
+	struct ibmvfc_target *tgt = container_of(kref, struct ibmvfc_target, kref);
+	kfree(tgt);
+}
+
+/**
+ * ibmvfc_tgt_prli_done - Completion handler for Process Login
+ * @evt:	ibmvfc event struct
+ *
+ **/
+static void ibmvfc_tgt_prli_done(struct ibmvfc_event *evt)
+{
+	struct ibmvfc_target *tgt = evt->tgt;
+	struct ibmvfc_host *vhost = evt->vhost;
+	struct ibmvfc_process_login *rsp = &evt->xfer_iu->prli;
+	u32 status = rsp->common.status;
+
+	vhost->discovery_threads--;
+	ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
+	switch (status) {
+	case IBMVFC_MAD_SUCCESS:
+		tgt_dbg(tgt, "Process Login succeeded\n");
+		tgt->need_login = 0;
+		ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_ADD_RPORT);
+		break;
+	case IBMVFC_MAD_DRIVER_FAILED:
+		break;
+	case IBMVFC_MAD_CRQ_ERROR:
+		ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_prli);
+		break;
+	case IBMVFC_MAD_FAILED:
+	default:
+		tgt_err(tgt, "Process Login failed: %s (%x:%x) rc=0x%02X\n",
+			ibmvfc_get_cmd_error(rsp->status, rsp->error),
+			rsp->status, rsp->error, status);
+		if (ibmvfc_retry_cmd(rsp->status, rsp->error))
+			ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_prli);
+		break;
+	};
+
+	kref_put(&tgt->kref, ibmvfc_release_tgt);
+	ibmvfc_free_event(evt);
+	wake_up(&vhost->work_wait_q);
+}
+
+/**
+ * ibmvfc_tgt_send_prli - Send a process login
+ * @tgt:	ibmvfc target struct
+ *
+ **/
+static void ibmvfc_tgt_send_prli(struct ibmvfc_target *tgt)
+{
+	struct ibmvfc_process_login *prli;
+	struct ibmvfc_host *vhost = tgt->vhost;
+	struct ibmvfc_event *evt;
+
+	if (vhost->discovery_threads >= disc_threads)
+		return;
+
+	kref_get(&tgt->kref);
+	evt = ibmvfc_get_event(vhost);
+	vhost->discovery_threads++;
+	ibmvfc_init_event(evt, ibmvfc_tgt_prli_done, IBMVFC_MAD_FORMAT);
+	evt->tgt = tgt;
+	prli = &evt->iu.prli;
+	memset(prli, 0, sizeof(*prli));
+	prli->common.version = 1;
+	prli->common.opcode = IBMVFC_PROCESS_LOGIN;
+	prli->common.length = sizeof(*prli);
+	prli->scsi_id = tgt->scsi_id;
+
+	prli->parms.type = IBMVFC_SCSI_FCP_TYPE;
+	prli->parms.flags = IBMVFC_PRLI_EST_IMG_PAIR;
+	prli->parms.service_parms = IBMVFC_PRLI_INITIATOR_FUNC;
+
+	ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_INIT_WAIT);
+	if (ibmvfc_send_event(evt, vhost, default_timeout)) {
+		vhost->discovery_threads--;
+		ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
+		kref_put(&tgt->kref, ibmvfc_release_tgt);
+	} else
+		tgt_dbg(tgt, "Sent process login\n");
+}
+
+/**
+ * ibmvfc_tgt_plogi_done - Completion handler for Port Login
+ * @evt:	ibmvfc event struct
+ *
+ **/
+static void ibmvfc_tgt_plogi_done(struct ibmvfc_event *evt)
+{
+	struct ibmvfc_target *tgt = evt->tgt;
+	struct ibmvfc_host *vhost = evt->vhost;
+	struct ibmvfc_port_login *rsp = &evt->xfer_iu->plogi;
+	u32 status = rsp->common.status;
+
+	vhost->discovery_threads--;
+	ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
+	switch (status) {
+	case IBMVFC_MAD_SUCCESS:
+		tgt_dbg(tgt, "Port Login succeeded\n");
+		if (tgt->ids.port_name &&
+		    tgt->ids.port_name != wwn_to_u64(rsp->service_parms.port_name)) {
+			vhost->reinit = 1;
+			tgt_dbg(tgt, "Port re-init required\n");
+			break;
+		}
+		tgt->ids.node_name = wwn_to_u64(rsp->service_parms.node_name);
+		tgt->ids.port_name = wwn_to_u64(rsp->service_parms.port_name);
+		tgt->ids.port_id = tgt->scsi_id;
+		tgt->ids.roles = FC_PORT_ROLE_FCP_TARGET;
+		memcpy(&tgt->service_parms, &rsp->service_parms,
+		       sizeof(tgt->service_parms));
+		memcpy(&tgt->service_parms_change, &rsp->service_parms_change,
+		       sizeof(tgt->service_parms_change));
+		ibmvfc_init_tgt(tgt, ibmvfc_tgt_send_prli);
+		break;
+	case IBMVFC_MAD_DRIVER_FAILED:
+		break;
+	case IBMVFC_MAD_CRQ_ERROR:
+		ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_plogi);
+		break;
+	case IBMVFC_MAD_FAILED:
+	default:
+		tgt_err(tgt, "Port Login failed: %s (%x:%x) %s (%x) %s (%x) rc=0x%02X\n",
+			ibmvfc_get_cmd_error(rsp->status, rsp->error), rsp->status, rsp->error,
+			ibmvfc_get_fc_type(rsp->fc_type), rsp->fc_type,
+			ibmvfc_get_ls_explain(rsp->fc_explain), rsp->fc_explain, status);
+
+		if (ibmvfc_retry_cmd(rsp->status, rsp->error))
+			ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_plogi);
+		break;
+	};
+
+	kref_put(&tgt->kref, ibmvfc_release_tgt);
+	ibmvfc_free_event(evt);
+	wake_up(&vhost->work_wait_q);
+}
+
+/**
+ * ibmvfc_tgt_send_plogi - Send PLOGI to the specified target
+ * @tgt:	ibmvfc target struct
+ *
+ **/
+static void ibmvfc_tgt_send_plogi(struct ibmvfc_target *tgt)
+{
+	struct ibmvfc_port_login *plogi;
+	struct ibmvfc_host *vhost = tgt->vhost;
+	struct ibmvfc_event *evt;
+
+	if (vhost->discovery_threads >= disc_threads)
+		return;
+
+	kref_get(&tgt->kref);
+	evt = ibmvfc_get_event(vhost);
+	vhost->discovery_threads++;
+	ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_INIT_WAIT);
+	ibmvfc_init_event(evt, ibmvfc_tgt_plogi_done, IBMVFC_MAD_FORMAT);
+	evt->tgt = tgt;
+	plogi = &evt->iu.plogi;
+	memset(plogi, 0, sizeof(*plogi));
+	plogi->common.version = 1;
+	plogi->common.opcode = IBMVFC_PORT_LOGIN;
+	plogi->common.length = sizeof(*plogi);
+	plogi->scsi_id = tgt->scsi_id;
+
+	if (ibmvfc_send_event(evt, vhost, default_timeout)) {
+		vhost->discovery_threads--;
+		ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
+		kref_put(&tgt->kref, ibmvfc_release_tgt);
+	} else
+		tgt_dbg(tgt, "Sent port login\n");
+}
+
+/**
+ * ibmvfc_tgt_implicit_logout_done - Completion handler for Implicit Logout MAD
+ * @evt:	ibmvfc event struct
+ *
+ **/
+static void ibmvfc_tgt_implicit_logout_done(struct ibmvfc_event *evt)
+{
+	struct ibmvfc_target *tgt = evt->tgt;
+	struct ibmvfc_host *vhost = evt->vhost;
+	struct ibmvfc_implicit_logout *rsp = &evt->xfer_iu->implicit_logout;
+	u32 status = rsp->common.status;
+
+	vhost->discovery_threads--;
+	ibmvfc_free_event(evt);
+	ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
+
+	switch (status) {
+	case IBMVFC_MAD_SUCCESS:
+		tgt_dbg(tgt, "Implicit Logout succeeded\n");
+		break;
+	case IBMVFC_MAD_DRIVER_FAILED:
+		kref_put(&tgt->kref, ibmvfc_release_tgt);
+		wake_up(&vhost->work_wait_q);
+		return;
+	case IBMVFC_MAD_FAILED:
+	default:
+		tgt_err(tgt, "Implicit Logout failed: rc=0x%02X\n", status);
+		break;
+	};
+
+	if (vhost->action == IBMVFC_HOST_ACTION_TGT_INIT)
+		ibmvfc_init_tgt(tgt, ibmvfc_tgt_send_plogi);
+	else if (vhost->action == IBMVFC_HOST_ACTION_QUERY_TGTS &&
+		 tgt->scsi_id != tgt->new_scsi_id)
+		ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
+	kref_put(&tgt->kref, ibmvfc_release_tgt);
+	wake_up(&vhost->work_wait_q);
+}
+
+/**
+ * ibmvfc_tgt_implicit_logout - Initiate an Implicit Logout for specified target
+ * @tgt:		ibmvfc target struct
+ *
+ **/
+static void ibmvfc_tgt_implicit_logout(struct ibmvfc_target *tgt)
+{
+	struct ibmvfc_implicit_logout *mad;
+	struct ibmvfc_host *vhost = tgt->vhost;
+	struct ibmvfc_event *evt;
+
+	if (vhost->discovery_threads >= disc_threads)
+		return;
+
+	kref_get(&tgt->kref);
+	evt = ibmvfc_get_event(vhost);
+	vhost->discovery_threads++;
+	ibmvfc_init_event(evt, ibmvfc_tgt_implicit_logout_done, IBMVFC_MAD_FORMAT);
+	evt->tgt = tgt;
+	mad = &evt->iu.implicit_logout;
+	memset(mad, 0, sizeof(*mad));
+	mad->common.version = 1;
+	mad->common.opcode = IBMVFC_IMPLICIT_LOGOUT;
+	mad->common.length = sizeof(*mad);
+	mad->old_scsi_id = tgt->scsi_id;
+
+	ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_INIT_WAIT);
+	if (ibmvfc_send_event(evt, vhost, default_timeout)) {
+		vhost->discovery_threads--;
+		ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
+		kref_put(&tgt->kref, ibmvfc_release_tgt);
+	} else
+		tgt_dbg(tgt, "Sent Implicit Logout\n");
+}
+
+/**
+ * ibmvfc_tgt_query_target_done - Completion handler for Query Target MAD
+ * @evt:	ibmvfc event struct
+ *
+ **/
+static void ibmvfc_tgt_query_target_done(struct ibmvfc_event *evt)
+{
+	struct ibmvfc_target *tgt = evt->tgt;
+	struct ibmvfc_host *vhost = evt->vhost;
+	struct ibmvfc_query_tgt *rsp = &evt->xfer_iu->query_tgt;
+	u32 status = rsp->common.status;
+
+	vhost->discovery_threads--;
+	ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
+	switch (status) {
+	case IBMVFC_MAD_SUCCESS:
+		tgt_dbg(tgt, "Query Target succeeded\n");
+		tgt->new_scsi_id = rsp->scsi_id;
+		if (rsp->scsi_id != tgt->scsi_id)
+			ibmvfc_init_tgt(tgt, ibmvfc_tgt_implicit_logout);
+		break;
+	case IBMVFC_MAD_DRIVER_FAILED:
+		break;
+	case IBMVFC_MAD_CRQ_ERROR:
+		ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_query_target);
+		break;
+	case IBMVFC_MAD_FAILED:
+	default:
+		tgt_err(tgt, "Query Target failed: %s (%x:%x) %s (%x) %s (%x) rc=0x%02X\n",
+			ibmvfc_get_cmd_error(rsp->status, rsp->error), rsp->status, rsp->error,
+			ibmvfc_get_fc_type(rsp->fc_type), rsp->fc_type,
+			ibmvfc_get_gs_explain(rsp->fc_explain), rsp->fc_explain, status);
+
+		if ((rsp->status & IBMVFC_FABRIC_MAPPED) == IBMVFC_FABRIC_MAPPED &&
+		    rsp->error == IBMVFC_UNABLE_TO_PERFORM_REQ &&
+		    rsp->fc_explain == IBMVFC_PORT_NAME_NOT_REG)
+			ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
+		else if (ibmvfc_retry_cmd(rsp->status, rsp->error))
+			ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_query_target);
+		break;
+	};
+
+	kref_put(&tgt->kref, ibmvfc_release_tgt);
+	ibmvfc_free_event(evt);
+	wake_up(&vhost->work_wait_q);
+}
+
+/**
+ * ibmvfc_tgt_query_target - Initiate a Query Target for specified target
+ * @tgt:	ibmvfc target struct
+ *
+ **/
+static void ibmvfc_tgt_query_target(struct ibmvfc_target *tgt)
+{
+	struct ibmvfc_query_tgt *query_tgt;
+	struct ibmvfc_host *vhost = tgt->vhost;
+	struct ibmvfc_event *evt;
+
+	if (vhost->discovery_threads >= disc_threads)
+		return;
+
+	kref_get(&tgt->kref);
+	evt = ibmvfc_get_event(vhost);
+	vhost->discovery_threads++;
+	evt->tgt = tgt;
+	ibmvfc_init_event(evt, ibmvfc_tgt_query_target_done, IBMVFC_MAD_FORMAT);
+	query_tgt = &evt->iu.query_tgt;
+	memset(query_tgt, 0, sizeof(*query_tgt));
+	query_tgt->common.version = 1;
+	query_tgt->common.opcode = IBMVFC_QUERY_TARGET;
+	query_tgt->common.length = sizeof(*query_tgt);
+	query_tgt->wwpn = tgt->ids.port_name;
+
+	ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_INIT_WAIT);
+	if (ibmvfc_send_event(evt, vhost, default_timeout)) {
+		vhost->discovery_threads--;
+		ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
+		kref_put(&tgt->kref, ibmvfc_release_tgt);
+	} else
+		tgt_dbg(tgt, "Sent Query Target\n");
+}
+
+/**
+ * ibmvfc_alloc_target - Allocate and initialize an ibmvfc target
+ * @vhost:		ibmvfc host struct
+ * @scsi_id:	SCSI ID to allocate target for
+ *
+ * Returns:
+ *	0 on success / other on failure
+ **/
+static int ibmvfc_alloc_target(struct ibmvfc_host *vhost, u64 scsi_id)
+{
+	struct ibmvfc_target *tgt;
+	unsigned long flags;
+
+	spin_lock_irqsave(vhost->host->host_lock, flags);
+	list_for_each_entry(tgt, &vhost->targets, queue) {
+		if (tgt->scsi_id == scsi_id) {
+			if (tgt->need_login)
+				ibmvfc_init_tgt(tgt, ibmvfc_tgt_implicit_logout);
+			goto unlock_out;
+		}
+	}
+	spin_unlock_irqrestore(vhost->host->host_lock, flags);
+
+	tgt = mempool_alloc(vhost->tgt_pool, GFP_KERNEL);
+	if (!tgt) {
+		dev_err(vhost->dev, "Target allocation failure for scsi id %08lx\n",
+			scsi_id);
+		return -ENOMEM;
+	}
+
+	tgt->scsi_id = scsi_id;
+	tgt->new_scsi_id = scsi_id;
+	tgt->vhost = vhost;
+	tgt->need_login = 1;
+	kref_init(&tgt->kref);
+	ibmvfc_init_tgt(tgt, ibmvfc_tgt_implicit_logout);
+	spin_lock_irqsave(vhost->host->host_lock, flags);
+	list_add_tail(&tgt->queue, &vhost->targets);
+
+unlock_out:
+	spin_unlock_irqrestore(vhost->host->host_lock, flags);
+	return 0;
+}
+
+/**
+ * ibmvfc_alloc_targets - Allocate and initialize ibmvfc targets
+ * @vhost:		ibmvfc host struct
+ *
+ * Returns:
+ *	0 on success / other on failure
+ **/
+static int ibmvfc_alloc_targets(struct ibmvfc_host *vhost)
+{
+	int i, rc;
+
+	for (i = 0, rc = 0; !rc && i < vhost->num_targets; i++)
+		rc = ibmvfc_alloc_target(vhost,
+					 vhost->disc_buf->scsi_id[i] & IBMVFC_DISC_TGT_SCSI_ID_MASK);
+
+	return rc;
+}
+
+/**
+ * ibmvfc_discover_targets_done - Completion handler for discover targets MAD
+ * @evt:	ibmvfc event struct
+ *
+ **/
+static void ibmvfc_discover_targets_done(struct ibmvfc_event *evt)
+{
+	struct ibmvfc_host *vhost = evt->vhost;
+	struct ibmvfc_discover_targets *rsp = &evt->xfer_iu->discover_targets;
+	u32 mad_status = rsp->common.status;
+
+	switch (mad_status) {
+	case IBMVFC_MAD_SUCCESS:
+		ibmvfc_dbg(vhost, "Discover Targets succeeded\n");
+		vhost->num_targets = rsp->num_written;
+		ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_ALLOC_TGTS);
+		break;
+	case IBMVFC_MAD_FAILED:
+		dev_err(vhost->dev, "Discover Targets failed: %s (%x:%x)\n",
+			ibmvfc_get_cmd_error(rsp->status, rsp->error), rsp->status, rsp->error);
+		ibmvfc_retry_host_init(vhost);
+		break;
+	case IBMVFC_MAD_DRIVER_FAILED:
+		break;
+	default:
+		dev_err(vhost->dev, "Invalid Discover Targets response: 0x%x\n", mad_status);
+		ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
+		break;
+	}
+
+	ibmvfc_free_event(evt);
+	wake_up(&vhost->work_wait_q);
+}
+
+/**
+ * ibmvfc_discover_targets - Send Discover Targets MAD
+ * @vhost:	ibmvfc host struct
+ *
+ **/
+static void ibmvfc_discover_targets(struct ibmvfc_host *vhost)
+{
+	struct ibmvfc_discover_targets *mad;
+	struct ibmvfc_event *evt = ibmvfc_get_event(vhost);
+
+	ibmvfc_init_event(evt, ibmvfc_discover_targets_done, IBMVFC_MAD_FORMAT);
+	mad = &evt->iu.discover_targets;
+	memset(mad, 0, sizeof(*mad));
+	mad->common.version = 1;
+	mad->common.opcode = IBMVFC_DISC_TARGETS;
+	mad->common.length = sizeof(*mad);
+	mad->bufflen = vhost->disc_buf_sz;
+	mad->buffer.va = vhost->disc_buf_dma;
+	mad->buffer.len = vhost->disc_buf_sz;
+	ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT_WAIT);
+
+	if (!ibmvfc_send_event(evt, vhost, default_timeout))
+		ibmvfc_dbg(vhost, "Sent discover targets\n");
+	else
+		ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
+}
+
+/**
+ * ibmvfc_npiv_login_done - Completion handler for NPIV Login
+ * @evt:	ibmvfc event struct
+ *
+ **/
+static void ibmvfc_npiv_login_done(struct ibmvfc_event *evt)
+{
+	struct ibmvfc_host *vhost = evt->vhost;
+	u32 mad_status = evt->xfer_iu->npiv_login.common.status;
+	struct ibmvfc_npiv_login_resp *rsp = &vhost->login_buf->resp;
+	unsigned int npiv_max_sectors;
+
+	switch (mad_status) {
+	case IBMVFC_MAD_SUCCESS:
+		ibmvfc_free_event(evt);
+		break;
+	case IBMVFC_MAD_FAILED:
+		dev_err(vhost->dev, "NPIV Login failed: %s (%x:%x)\n",
+			ibmvfc_get_cmd_error(rsp->status, rsp->error), rsp->status, rsp->error);
+		if (ibmvfc_retry_cmd(rsp->status, rsp->error))
+			ibmvfc_retry_host_init(vhost);
+		else
+			ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
+		ibmvfc_free_event(evt);
+		return;
+	case IBMVFC_MAD_CRQ_ERROR:
+		ibmvfc_retry_host_init(vhost);
+	case IBMVFC_MAD_DRIVER_FAILED:
+		ibmvfc_free_event(evt);
+		return;
+	default:
+		dev_err(vhost->dev, "Invalid NPIV Login response: 0x%x\n", mad_status);
+		ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
+		ibmvfc_free_event(evt);
+		return;
+	}
+
+	vhost->client_migrated = 0;
+
+	if (!(rsp->flags & IBMVFC_NATIVE_FC)) {
+		dev_err(vhost->dev, "Virtual adapter does not support FC. %x\n",
+			rsp->flags);
+		ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
+		wake_up(&vhost->work_wait_q);
+		return;
+	}
+
+	if (rsp->max_cmds <= IBMVFC_NUM_INTERNAL_REQ) {
+		dev_err(vhost->dev, "Virtual adapter supported queue depth too small: %d\n",
+			rsp->max_cmds);
+		ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
+		wake_up(&vhost->work_wait_q);
+		return;
+	}
+
+	npiv_max_sectors = min((uint)(rsp->max_dma_len >> 9), IBMVFC_MAX_SECTORS);
+	dev_info(vhost->dev, "Host partition: %s, device: %s %s %s max sectors %u\n",
+		 rsp->partition_name, rsp->device_name, rsp->port_loc_code,
+		 rsp->drc_name, npiv_max_sectors);
+
+	fc_host_fabric_name(vhost->host) = rsp->node_name;
+	fc_host_node_name(vhost->host) = rsp->node_name;
+	fc_host_port_name(vhost->host) = rsp->port_name;
+	fc_host_port_id(vhost->host) = rsp->scsi_id;
+	fc_host_port_type(vhost->host) = FC_PORTTYPE_NPIV;
+	fc_host_supported_classes(vhost->host) = 0;
+	if (rsp->service_parms.class1_parms[0] & 0x80000000)
+		fc_host_supported_classes(vhost->host) |= FC_COS_CLASS1;
+	if (rsp->service_parms.class2_parms[0] & 0x80000000)
+		fc_host_supported_classes(vhost->host) |= FC_COS_CLASS2;
+	if (rsp->service_parms.class3_parms[0] & 0x80000000)
+		fc_host_supported_classes(vhost->host) |= FC_COS_CLASS3;
+	fc_host_maxframe_size(vhost->host) =
+		rsp->service_parms.common.bb_rcv_sz & 0x0fff;
+
+	vhost->host->can_queue = rsp->max_cmds - IBMVFC_NUM_INTERNAL_REQ;
+	vhost->host->max_sectors = npiv_max_sectors;
+	ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY);
+	wake_up(&vhost->work_wait_q);
+}
+
+/**
+ * ibmvfc_npiv_login - Sends NPIV login
+ * @vhost:	ibmvfc host struct
+ *
+ **/
+static void ibmvfc_npiv_login(struct ibmvfc_host *vhost)
+{
+	struct ibmvfc_npiv_login_mad *mad;
+	struct ibmvfc_event *evt = ibmvfc_get_event(vhost);
+
+	ibmvfc_gather_partition_info(vhost);
+	ibmvfc_set_login_info(vhost);
+	ibmvfc_init_event(evt, ibmvfc_npiv_login_done, IBMVFC_MAD_FORMAT);
+
+	memcpy(vhost->login_buf, &vhost->login_info, sizeof(vhost->login_info));
+	mad = &evt->iu.npiv_login;
+	memset(mad, 0, sizeof(struct ibmvfc_npiv_login_mad));
+	mad->common.version = 1;
+	mad->common.opcode = IBMVFC_NPIV_LOGIN;
+	mad->common.length = sizeof(struct ibmvfc_npiv_login_mad);
+	mad->buffer.va = vhost->login_buf_dma;
+	mad->buffer.len = sizeof(*vhost->login_buf);
+
+	memset(vhost->async_crq.msgs, 0, PAGE_SIZE);
+	vhost->async_crq.cur = 0;
+	ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT_WAIT);
+
+	if (!ibmvfc_send_event(evt, vhost, default_timeout))
+		ibmvfc_dbg(vhost, "Sent NPIV login\n");
+	else
+		ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
+};
+
+/**
+ * ibmvfc_dev_init_to_do - Is there target initialization work to do?
+ * @vhost:		ibmvfc host struct
+ *
+ * Returns:
+ *	1 if work to do / 0 if not
+ **/
+static int ibmvfc_dev_init_to_do(struct ibmvfc_host *vhost)
+{
+	struct ibmvfc_target *tgt;
+
+	list_for_each_entry(tgt, &vhost->targets, queue) {
+		if (tgt->action == IBMVFC_TGT_ACTION_INIT ||
+		    tgt->action == IBMVFC_TGT_ACTION_INIT_WAIT)
+			return 1;
+	}
+
+	return 0;
+}
+
+/**
+ * __ibmvfc_work_to_do - Is there task level work to do? (no locking)
+ * @vhost:		ibmvfc host struct
+ *
+ * Returns:
+ *	1 if work to do / 0 if not
+ **/
+static int __ibmvfc_work_to_do(struct ibmvfc_host *vhost)
+{
+	struct ibmvfc_target *tgt;
+
+	if (kthread_should_stop())
+		return 1;
+	switch (vhost->action) {
+	case IBMVFC_HOST_ACTION_NONE:
+	case IBMVFC_HOST_ACTION_INIT_WAIT:
+		return 0;
+	case IBMVFC_HOST_ACTION_TGT_INIT:
+	case IBMVFC_HOST_ACTION_QUERY_TGTS:
+		if (vhost->discovery_threads == disc_threads)
+			return 0;
+		list_for_each_entry(tgt, &vhost->targets, queue)
+			if (tgt->action == IBMVFC_TGT_ACTION_INIT)
+				return 1;
+		list_for_each_entry(tgt, &vhost->targets, queue)
+			if (tgt->action == IBMVFC_TGT_ACTION_INIT_WAIT)
+				return 0;
+		return 1;
+	case IBMVFC_HOST_ACTION_INIT:
+	case IBMVFC_HOST_ACTION_ALLOC_TGTS:
+	case IBMVFC_HOST_ACTION_TGT_ADD:
+	case IBMVFC_HOST_ACTION_TGT_DEL:
+	case IBMVFC_HOST_ACTION_QUERY:
+	default:
+		break;
+	};
+
+	return 1;
+}
+
+/**
+ * ibmvfc_work_to_do - Is there task level work to do?
+ * @vhost:		ibmvfc host struct
+ *
+ * Returns:
+ *	1 if work to do / 0 if not
+ **/
+static int ibmvfc_work_to_do(struct ibmvfc_host *vhost)
+{
+	unsigned long flags;
+	int rc;
+
+	spin_lock_irqsave(vhost->host->host_lock, flags);
+	rc = __ibmvfc_work_to_do(vhost);
+	spin_unlock_irqrestore(vhost->host->host_lock, flags);
+	return rc;
+}
+
+/**
+ * ibmvfc_log_ae - Log async events if necessary
+ * @vhost:		ibmvfc host struct
+ * @events:		events to log
+ *
+ **/
+static void ibmvfc_log_ae(struct ibmvfc_host *vhost, int events)
+{
+	if (events & IBMVFC_AE_RSCN)
+		fc_host_post_event(vhost->host, fc_get_event_number(), FCH_EVT_RSCN, 0);
+	if ((events & IBMVFC_AE_LINKDOWN) &&
+	    vhost->state >= IBMVFC_HALTED)
+		fc_host_post_event(vhost->host, fc_get_event_number(), FCH_EVT_LINKDOWN, 0);
+	if ((events & IBMVFC_AE_LINKUP) &&
+	    vhost->state == IBMVFC_INITIALIZING)
+		fc_host_post_event(vhost->host, fc_get_event_number(), FCH_EVT_LINKUP, 0);
+}
+
+/**
+ * ibmvfc_tgt_add_rport - Tell the FC transport about a new remote port
+ * @tgt:		ibmvfc target struct
+ *
+ **/
+static void ibmvfc_tgt_add_rport(struct ibmvfc_target *tgt)
+{
+	struct ibmvfc_host *vhost = tgt->vhost;
+	struct fc_rport *rport;
+	unsigned long flags;
+
+	tgt_dbg(tgt, "Adding rport\n");
+	rport = fc_remote_port_add(vhost->host, 0, &tgt->ids);
+	spin_lock_irqsave(vhost->host->host_lock, flags);
+	tgt->rport = rport;
+	ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
+	if (rport) {
+		tgt_dbg(tgt, "rport add succeeded\n");
+		rport->maxframe_size = tgt->service_parms.common.bb_rcv_sz & 0x0fff;
+		rport->supported_classes = 0;
+		if (tgt->service_parms.class1_parms[0] & 0x80000000)
+			rport->supported_classes |= FC_COS_CLASS1;
+		if (tgt->service_parms.class2_parms[0] & 0x80000000)
+			rport->supported_classes |= FC_COS_CLASS2;
+		if (tgt->service_parms.class3_parms[0] & 0x80000000)
+			rport->supported_classes |= FC_COS_CLASS3;
+	} else
+		tgt_dbg(tgt, "rport add failed\n");
+	spin_unlock_irqrestore(vhost->host->host_lock, flags);
+}
+
+/**
+ * ibmvfc_do_work - Do task level work
+ * @vhost:		ibmvfc host struct
+ *
+ **/
+static void ibmvfc_do_work(struct ibmvfc_host *vhost)
+{
+	struct ibmvfc_target *tgt;
+	unsigned long flags;
+	struct fc_rport *rport;
+
+	ibmvfc_log_ae(vhost, vhost->events_to_log);
+	spin_lock_irqsave(vhost->host->host_lock, flags);
+	vhost->events_to_log = 0;
+	switch (vhost->action) {
+	case IBMVFC_HOST_ACTION_NONE:
+	case IBMVFC_HOST_ACTION_INIT_WAIT:
+		break;
+	case IBMVFC_HOST_ACTION_INIT:
+		BUG_ON(vhost->state != IBMVFC_INITIALIZING);
+		vhost->job_step(vhost);
+		break;
+	case IBMVFC_HOST_ACTION_QUERY:
+		list_for_each_entry(tgt, &vhost->targets, queue)
+			ibmvfc_init_tgt(tgt, ibmvfc_tgt_query_target);
+		ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY_TGTS);
+		break;
+	case IBMVFC_HOST_ACTION_QUERY_TGTS:
+		list_for_each_entry(tgt, &vhost->targets, queue) {
+			if (tgt->action == IBMVFC_TGT_ACTION_INIT) {
+				tgt->job_step(tgt);
+				break;
+			}
+		}
+
+		if (!ibmvfc_dev_init_to_do(vhost))
+			ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_TGT_DEL);
+		break;
+	case IBMVFC_HOST_ACTION_TGT_DEL:
+		list_for_each_entry(tgt, &vhost->targets, queue) {
+			if (tgt->action == IBMVFC_TGT_ACTION_DEL_RPORT) {
+				tgt_dbg(tgt, "Deleting rport\n");
+				rport = tgt->rport;
+				tgt->rport = NULL;
+				list_del(&tgt->queue);
+				spin_unlock_irqrestore(vhost->host->host_lock, flags);
+				if (rport)
+					fc_remote_port_delete(rport);
+				kref_put(&tgt->kref, ibmvfc_release_tgt);
+				return;
+			}
+		}
+
+		if (vhost->state == IBMVFC_INITIALIZING) {
+			ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT);
+			vhost->job_step = ibmvfc_discover_targets;
+		} else {
+			ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_NONE);
+			spin_unlock_irqrestore(vhost->host->host_lock, flags);
+			scsi_unblock_requests(vhost->host);
+			wake_up(&vhost->init_wait_q);
+			return;
+		}
+		break;
+	case IBMVFC_HOST_ACTION_ALLOC_TGTS:
+		ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_TGT_INIT);
+		spin_unlock_irqrestore(vhost->host->host_lock, flags);
+		ibmvfc_alloc_targets(vhost);
+		spin_lock_irqsave(vhost->host->host_lock, flags);
+		break;
+	case IBMVFC_HOST_ACTION_TGT_INIT:
+		list_for_each_entry(tgt, &vhost->targets, queue) {
+			if (tgt->action == IBMVFC_TGT_ACTION_INIT) {
+				tgt->job_step(tgt);
+				break;
+			}
+		}
+
+		if (!ibmvfc_dev_init_to_do(vhost)) {
+			ibmvfc_set_host_state(vhost, IBMVFC_ACTIVE);
+			ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_TGT_ADD);
+			vhost->init_retries = 0;
+			spin_unlock_irqrestore(vhost->host->host_lock, flags);
+			scsi_unblock_requests(vhost->host);
+			return;
+		}
+		break;
+	case IBMVFC_HOST_ACTION_TGT_ADD:
+		list_for_each_entry(tgt, &vhost->targets, queue) {
+			if (tgt->action == IBMVFC_TGT_ACTION_ADD_RPORT) {
+				spin_unlock_irqrestore(vhost->host->host_lock, flags);
+				ibmvfc_tgt_add_rport(tgt);
+				return;
+			} else if (tgt->action == IBMVFC_TGT_ACTION_DEL_RPORT) {
+				tgt_dbg(tgt, "Deleting rport\n");
+				rport = tgt->rport;
+				tgt->rport = NULL;
+				list_del(&tgt->queue);
+				spin_unlock_irqrestore(vhost->host->host_lock, flags);
+				if (rport)
+					fc_remote_port_delete(rport);
+				kref_put(&tgt->kref, ibmvfc_release_tgt);
+				return;
+			}
+		}
+
+		if (vhost->reinit) {
+			vhost->reinit = 0;
+			ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY);
+		} else {
+			ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_NONE);
+			wake_up(&vhost->init_wait_q);
+		}
+		break;
+	default:
+		break;
+	};
+
+	spin_unlock_irqrestore(vhost->host->host_lock, flags);
+}
+
+/**
+ * ibmvfc_work - Do task level work
+ * @data:		ibmvfc host struct
+ *
+ * Returns:
+ *	zero
+ **/
+static int ibmvfc_work(void *data)
+{
+	struct ibmvfc_host *vhost = data;
+	int rc;
+
+	set_user_nice(current, -20);
+
+	while (1) {
+		rc = wait_event_interruptible(vhost->work_wait_q,
+					      ibmvfc_work_to_do(vhost));
+
+		BUG_ON(rc);
+
+		if (kthread_should_stop())
+			break;
+
+		ibmvfc_do_work(vhost);
+	}
+
+	ibmvfc_dbg(vhost, "ibmvfc kthread exiting...\n");
+	return 0;
+}
+
+/**
+ * ibmvfc_init_crq - Initializes and registers CRQ with hypervisor
+ * @vhost:	ibmvfc host struct
+ *
+ * Allocates a page for messages, maps it for dma, and registers
+ * the crq with the hypervisor.
+ *
+ * Return value:
+ *	zero on success / other on failure
+ **/
+static int ibmvfc_init_crq(struct ibmvfc_host *vhost)
+{
+	int rc, retrc = -ENOMEM;
+	struct device *dev = vhost->dev;
+	struct vio_dev *vdev = to_vio_dev(dev);
+	struct ibmvfc_crq_queue *crq = &vhost->crq;
+
+	ENTER;
+	crq->msgs = (struct ibmvfc_crq *)get_zeroed_page(GFP_KERNEL);
+
+	if (!crq->msgs)
+		return -ENOMEM;
+
+	crq->size = PAGE_SIZE / sizeof(*crq->msgs);
+	crq->msg_token = dma_map_single(dev, crq->msgs,
+					PAGE_SIZE, DMA_BIDIRECTIONAL);
+
+	if (dma_mapping_error(crq->msg_token))
+		goto map_failed;
+
+	retrc = rc = plpar_hcall_norets(H_REG_CRQ, vdev->unit_address,
+					crq->msg_token, PAGE_SIZE);
+
+	if (rc == H_RESOURCE)
+		/* maybe kexecing and resource is busy. try a reset */
+		retrc = rc = ibmvfc_reset_crq(vhost);
+
+	if (rc == H_CLOSED)
+		dev_warn(dev, "Partner adapter not ready\n");
+	else if (rc) {
+		dev_warn(dev, "Error %d opening adapter\n", rc);
+		goto reg_crq_failed;
+	}
+
+	retrc = 0;
+
+	if ((rc = request_irq(vdev->irq, ibmvfc_interrupt, 0, IBMVFC_NAME, vhost))) {
+		dev_err(dev, "Couldn't register irq 0x%x. rc=%d\n", vdev->irq, rc);
+		goto req_irq_failed;
+	}
+
+	if ((rc = vio_enable_interrupts(vdev))) {
+		dev_err(dev, "Error %d enabling interrupts\n", rc);
+		goto req_irq_failed;
+	}
+
+	crq->cur = 0;
+	LEAVE;
+	return retrc;
+
+req_irq_failed:
+	do {
+		rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address);
+	} while (rc == H_BUSY || H_IS_LONG_BUSY(rc));
+reg_crq_failed:
+	dma_unmap_single(dev, crq->msg_token, PAGE_SIZE, DMA_BIDIRECTIONAL);
+map_failed:
+	free_page((unsigned long)crq->msgs);
+	return retrc;
+}
+
+/**
+ * ibmvfc_free_mem - Free memory for vhost
+ * @vhost:	ibmvfc host struct
+ *
+ * Return value:
+ * 	none
+ **/
+static void ibmvfc_free_mem(struct ibmvfc_host *vhost)
+{
+	struct ibmvfc_async_crq_queue *async_q = &vhost->async_crq;
+
+	ENTER;
+	mempool_destroy(vhost->tgt_pool);
+	kfree(vhost->trace);
+	dma_free_coherent(vhost->dev, vhost->disc_buf_sz, vhost->disc_buf,
+			  vhost->disc_buf_dma);
+	dma_free_coherent(vhost->dev, sizeof(*vhost->login_buf),
+			  vhost->login_buf, vhost->login_buf_dma);
+	dma_pool_destroy(vhost->sg_pool);
+	dma_unmap_single(vhost->dev, async_q->msg_token,
+			 async_q->size * sizeof(*async_q->msgs), DMA_BIDIRECTIONAL);
+	free_page((unsigned long)async_q->msgs);
+	LEAVE;
+}
+
+/**
+ * ibmvfc_alloc_mem - Allocate memory for vhost
+ * @vhost:	ibmvfc host struct
+ *
+ * Return value:
+ * 	0 on success / non-zero on failure
+ **/
+static int ibmvfc_alloc_mem(struct ibmvfc_host *vhost)
+{
+	struct ibmvfc_async_crq_queue *async_q = &vhost->async_crq;
+	struct device *dev = vhost->dev;
+
+	ENTER;
+	async_q->msgs = (struct ibmvfc_async_crq *)get_zeroed_page(GFP_KERNEL);
+	if (!async_q->msgs) {
+		dev_err(dev, "Couldn't allocate async queue.\n");
+		goto nomem;
+	}
+
+	async_q->size = PAGE_SIZE / sizeof(struct ibmvfc_async_crq);
+	async_q->msg_token = dma_map_single(dev, async_q->msgs,
+					    async_q->size * sizeof(*async_q->msgs),
+					    DMA_BIDIRECTIONAL);
+
+	if (dma_mapping_error(async_q->msg_token)) {
+		dev_err(dev, "Failed to map async queue\n");
+		goto free_async_crq;
+	}
+
+	vhost->sg_pool = dma_pool_create(IBMVFC_NAME, dev,
+					 SG_ALL * sizeof(struct srp_direct_buf),
+					 sizeof(struct srp_direct_buf), 0);
+
+	if (!vhost->sg_pool) {
+		dev_err(dev, "Failed to allocate sg pool\n");
+		goto unmap_async_crq;
+	}
+
+	vhost->login_buf = dma_alloc_coherent(dev, sizeof(*vhost->login_buf),
+					      &vhost->login_buf_dma, GFP_KERNEL);
+
+	if (!vhost->login_buf) {
+		dev_err(dev, "Couldn't allocate NPIV login buffer\n");
+		goto free_sg_pool;
+	}
+
+	vhost->disc_buf_sz = sizeof(vhost->disc_buf->scsi_id[0]) * max_targets;
+	vhost->disc_buf = dma_alloc_coherent(dev, vhost->disc_buf_sz,
+					     &vhost->disc_buf_dma, GFP_KERNEL);
+
+	if (!vhost->disc_buf) {
+		dev_err(dev, "Couldn't allocate Discover Targets buffer\n");
+		goto free_login_buffer;
+	}
+
+	vhost->trace = kcalloc(IBMVFC_NUM_TRACE_ENTRIES,
+			       sizeof(struct ibmvfc_trace_entry), GFP_KERNEL);
+
+	if (!vhost->trace)
+		goto free_disc_buffer;
+
+	vhost->tgt_pool = mempool_create_kzalloc_pool(IBMVFC_TGT_MEMPOOL_SZ,
+						      sizeof(struct ibmvfc_target));
+
+	if (!vhost->tgt_pool) {
+		dev_err(dev, "Couldn't allocate target memory pool\n");
+		goto free_trace;
+	}
+
+	LEAVE;
+	return 0;
+
+free_trace:
+	kfree(vhost->trace);
+free_disc_buffer:
+	dma_free_coherent(dev, vhost->disc_buf_sz, vhost->disc_buf,
+			  vhost->disc_buf_dma);
+free_login_buffer:
+	dma_free_coherent(dev, sizeof(*vhost->login_buf),
+			  vhost->login_buf, vhost->login_buf_dma);
+free_sg_pool:
+	dma_pool_destroy(vhost->sg_pool);
+unmap_async_crq:
+	dma_unmap_single(dev, async_q->msg_token,
+			 async_q->size * sizeof(*async_q->msgs), DMA_BIDIRECTIONAL);
+free_async_crq:
+	free_page((unsigned long)async_q->msgs);
+nomem:
+	LEAVE;
+	return -ENOMEM;
+}
+
+/**
+ * ibmvfc_probe - Adapter hot plug add entry point
+ * @vdev:	vio device struct
+ * @id:	vio device id struct
+ *
+ * Return value:
+ * 	0 on success / non-zero on failure
+ **/
+static int ibmvfc_probe(struct vio_dev *vdev, const struct vio_device_id *id)
+{
+	struct ibmvfc_host *vhost;
+	struct Scsi_Host *shost;
+	struct device *dev = &vdev->dev;
+	int rc = -ENOMEM;
+
+	ENTER;
+	shost = scsi_host_alloc(&driver_template, sizeof(*vhost));
+	if (!shost) {
+		dev_err(dev, "Couldn't allocate host data\n");
+		goto out;
+	}
+
+	shost->transportt = ibmvfc_transport_template;
+	shost->can_queue = max_requests;
+	shost->max_lun = max_lun;
+	shost->max_id = max_targets;
+	shost->max_sectors = IBMVFC_MAX_SECTORS;
+	shost->max_cmd_len = IBMVFC_MAX_CDB_LEN;
+	shost->unique_id = shost->host_no;
+
+	vhost = shost_priv(shost);
+	INIT_LIST_HEAD(&vhost->sent);
+	INIT_LIST_HEAD(&vhost->free);
+	INIT_LIST_HEAD(&vhost->targets);
+	sprintf(vhost->name, IBMVFC_NAME);
+	vhost->host = shost;
+	vhost->dev = dev;
+	vhost->partition_number = -1;
+	vhost->log_level = log_level;
+	strcpy(vhost->partition_name, "UNKNOWN");
+	init_waitqueue_head(&vhost->work_wait_q);
+	init_waitqueue_head(&vhost->init_wait_q);
+
+	if ((rc = ibmvfc_alloc_mem(vhost)))
+		goto free_scsi_host;
+
+	vhost->work_thread = kthread_run(ibmvfc_work, vhost, "%s_%d", IBMVFC_NAME,
+					 shost->host_no);
+
+	if (IS_ERR(vhost->work_thread)) {
+		dev_err(dev, "Couldn't create kernel thread: %ld\n",
+			PTR_ERR(vhost->work_thread));
+		goto free_host_mem;
+	}
+
+	if ((rc = ibmvfc_init_crq(vhost))) {
+		dev_err(dev, "Couldn't initialize crq. rc=%d\n", rc);
+		goto kill_kthread;
+	}
+
+	if ((rc = ibmvfc_init_event_pool(vhost))) {
+		dev_err(dev, "Couldn't initialize event pool. rc=%d\n", rc);
+		goto release_crq;
+	}
+
+	if ((rc = scsi_add_host(shost, dev)))
+		goto release_event_pool;
+
+	if ((rc = ibmvfc_create_trace_file(&shost->shost_dev.kobj,
+					   &ibmvfc_trace_attr))) {
+		dev_err(dev, "Failed to create trace file. rc=%d\n", rc);
+		goto remove_shost;
+	}
+
+	dev_set_drvdata(dev, vhost);
+	spin_lock(&ibmvfc_driver_lock);
+	list_add_tail(&vhost->queue, &ibmvfc_head);
+	spin_unlock(&ibmvfc_driver_lock);
+
+	ibmvfc_send_crq_init(vhost);
+	scsi_scan_host(shost);
+	return 0;
+
+remove_shost:
+	scsi_remove_host(shost);
+release_event_pool:
+	ibmvfc_free_event_pool(vhost);
+release_crq:
+	ibmvfc_release_crq_queue(vhost);
+kill_kthread:
+	kthread_stop(vhost->work_thread);
+free_host_mem:
+	ibmvfc_free_mem(vhost);
+free_scsi_host:
+	scsi_host_put(shost);
+out:
+	LEAVE;
+	return rc;
+}
+
+/**
+ * ibmvfc_remove - Adapter hot plug remove entry point
+ * @vdev:	vio device struct
+ *
+ * Return value:
+ * 	0
+ **/
+static int ibmvfc_remove(struct vio_dev *vdev)
+{
+	struct ibmvfc_host *vhost = dev_get_drvdata(&vdev->dev);
+	unsigned long flags;
+
+	ENTER;
+	ibmvfc_remove_trace_file(&vhost->host->shost_dev.kobj, &ibmvfc_trace_attr);
+	kthread_stop(vhost->work_thread);
+	fc_remove_host(vhost->host);
+	scsi_remove_host(vhost->host);
+	ibmvfc_release_crq_queue(vhost);
+
+	spin_lock_irqsave(vhost->host->host_lock, flags);
+	ibmvfc_purge_requests(vhost, DID_ERROR);
+	ibmvfc_free_event_pool(vhost);
+	spin_unlock_irqrestore(vhost->host->host_lock, flags);
+
+	ibmvfc_free_mem(vhost);
+	spin_lock(&ibmvfc_driver_lock);
+	list_del(&vhost->queue);
+	spin_unlock(&ibmvfc_driver_lock);
+	scsi_host_put(vhost->host);
+	LEAVE;
+	return 0;
+}
+
+static struct vio_device_id ibmvfc_device_table[] __devinitdata = {
+	{"fcp", "IBM,vfc-client"},
+	{ "", "" }
+};
+MODULE_DEVICE_TABLE(vio, ibmvfc_device_table);
+
+static struct vio_driver ibmvfc_driver = {
+	.id_table = ibmvfc_device_table,
+	.probe = ibmvfc_probe,
+	.remove = ibmvfc_remove,
+	.driver = {
+		.name = IBMVFC_NAME,
+		.owner = THIS_MODULE,
+	}
+};
+
+static struct fc_function_template ibmvfc_transport_functions = {
+	.show_host_fabric_name = 1,
+	.show_host_node_name = 1,
+	.show_host_port_name = 1,
+	.show_host_supported_classes = 1,
+	.show_host_port_type = 1,
+	.show_host_port_id = 1,
+
+	.get_host_port_state = ibmvfc_get_host_port_state,
+	.show_host_port_state = 1,
+
+	.get_host_speed = ibmvfc_get_host_speed,
+	.show_host_speed = 1,
+
+	.issue_fc_host_lip = ibmvfc_issue_fc_host_lip,
+	.terminate_rport_io = ibmvfc_terminate_rport_io,
+
+	.show_rport_maxframe_size = 1,
+	.show_rport_supported_classes = 1,
+
+	.set_rport_dev_loss_tmo = ibmvfc_set_rport_dev_loss_tmo,
+	.show_rport_dev_loss_tmo = 1,
+
+	.get_starget_node_name = ibmvfc_get_starget_node_name,
+	.show_starget_node_name = 1,
+
+	.get_starget_port_name = ibmvfc_get_starget_port_name,
+	.show_starget_port_name = 1,
+
+	.get_starget_port_id = ibmvfc_get_starget_port_id,
+	.show_starget_port_id = 1,
+};
+
+/**
+ * ibmvfc_module_init - Initialize the ibmvfc module
+ *
+ * Return value:
+ * 	0 on success / other on failure
+ **/
+static int __init ibmvfc_module_init(void)
+{
+	int rc;
+
+	if (!firmware_has_feature(FW_FEATURE_VIO))
+		return -ENODEV;
+
+	printk(KERN_INFO IBMVFC_NAME": IBM Virtual Fibre Channel Driver version: %s %s\n",
+	       IBMVFC_DRIVER_VERSION, IBMVFC_DRIVER_DATE);
+
+	ibmvfc_transport_template = fc_attach_transport(&ibmvfc_transport_functions);
+	if (!ibmvfc_transport_template)
+		return -ENOMEM;
+
+	rc = vio_register_driver(&ibmvfc_driver);
+	if (rc)
+		fc_release_transport(ibmvfc_transport_template);
+	return rc;
+}
+
+/**
+ * ibmvfc_module_exit - Teardown the ibmvfc module
+ *
+ * Return value:
+ * 	nothing
+ **/
+static void __exit ibmvfc_module_exit(void)
+{
+	vio_unregister_driver(&ibmvfc_driver);
+	fc_release_transport(ibmvfc_transport_template);
+}
+
+module_init(ibmvfc_module_init);
+module_exit(ibmvfc_module_exit);
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.h b/drivers/scsi/ibmvscsi/ibmvfc.h
new file mode 100644
index 0000000..057f3c0
--- /dev/null
+++ b/drivers/scsi/ibmvscsi/ibmvfc.h
@@ -0,0 +1,682 @@
+/*
+ * ibmvfc.h -- driver for IBM Power Virtual Fibre Channel Adapter
+ *
+ * Written By: Brian King <brking@linux.vnet.ibm.com>, IBM Corporation
+ *
+ * Copyright (C) IBM Corporation, 2008
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef _IBMVFC_H
+#define _IBMVFC_H
+
+#include <linux/list.h>
+#include <linux/types.h>
+#include "viosrp.h"
+
+#define IBMVFC_NAME	"ibmvfc"
+#define IBMVFC_DRIVER_VERSION		"1.0.0"
+#define IBMVFC_DRIVER_DATE		"(July 1, 2008)"
+
+#define IBMVFC_DEFAULT_TIMEOUT	15
+#define IBMVFC_INIT_TIMEOUT		30
+#define IBMVFC_MAX_REQUESTS_DEFAULT	100
+
+#define IBMVFC_DEBUG			0
+#define IBMVFC_MAX_TARGETS		1024
+#define IBMVFC_MAX_LUN			0xffffffff
+#define IBMVFC_MAX_SECTORS		0xffffu
+#define IBMVFC_MAX_DISC_THREADS	4
+#define IBMVFC_TGT_MEMPOOL_SZ		64
+#define IBMVFC_MAX_CMDS_PER_LUN	64
+#define IBMVFC_MAX_INIT_RETRIES	3
+#define IBMVFC_DEV_LOSS_TMO		(5 * 60)
+#define IBMVFC_DEFAULT_LOG_LEVEL	2
+#define IBMVFC_MAX_CDB_LEN		16
+
+/*
+ * Ensure we have resources for ERP and initialization:
+ * 1 for ERP
+ * 1 for initialization
+ * 1 for each discovery thread
+ */
+#define IBMVFC_NUM_INTERNAL_REQ	(1 + 1 + disc_threads)
+
+#define IBMVFC_MAD_SUCCESS		0x00
+#define IBMVFC_MAD_NOT_SUPPORTED	0xF1
+#define IBMVFC_MAD_FAILED		0xF7
+#define IBMVFC_MAD_DRIVER_FAILED	0xEE
+#define IBMVFC_MAD_CRQ_ERROR		0xEF
+
+enum ibmvfc_crq_valid {
+	IBMVFC_CRQ_CMD_RSP		= 0x80,
+	IBMVFC_CRQ_INIT_RSP		= 0xC0,
+	IBMVFC_CRQ_XPORT_EVENT		= 0xFF,
+};
+
+enum ibmvfc_crq_format {
+	IBMVFC_CRQ_INIT			= 0x01,
+	IBMVFC_CRQ_INIT_COMPLETE	= 0x02,
+	IBMVFC_PARTITION_MIGRATED	= 0x06,
+};
+
+enum ibmvfc_cmd_status_flags {
+	IBMVFC_FABRIC_MAPPED		= 0x0001,
+	IBMVFC_VIOS_FAILURE		= 0x0002,
+	IBMVFC_FC_FAILURE			= 0x0004,
+	IBMVFC_FC_SCSI_ERROR		= 0x0008,
+	IBMVFC_HW_EVENT_LOGGED		= 0x0010,
+	IBMVFC_VIOS_LOGGED		= 0x0020,
+};
+
+enum ibmvfc_fabric_mapped_errors {
+	IBMVFC_UNABLE_TO_ESTABLISH	= 0x0001,
+	IBMVFC_XPORT_FAULT		= 0x0002,
+	IBMVFC_CMD_TIMEOUT		= 0x0003,
+	IBMVFC_ENETDOWN			= 0x0004,
+	IBMVFC_HW_FAILURE			= 0x0005,
+	IBMVFC_LINK_DOWN_ERR		= 0x0006,
+	IBMVFC_LINK_DEAD_ERR		= 0x0007,
+	IBMVFC_UNABLE_TO_REGISTER	= 0x0008,
+	IBMVFC_XPORT_BUSY			= 0x000A,
+	IBMVFC_XPORT_DEAD			= 0x000B,
+	IBMVFC_CONFIG_ERROR		= 0x000C,
+	IBMVFC_NAME_SERVER_FAIL		= 0x000D,
+	IBMVFC_LINK_HALTED		= 0x000E,
+	IBMVFC_XPORT_GENERAL		= 0x8000,
+};
+
+enum ibmvfc_vios_errors {
+	IBMVFC_CRQ_FAILURE			= 0x0001,
+	IBMVFC_SW_FAILURE				= 0x0002,
+	IBMVFC_INVALID_PARAMETER		= 0x0003,
+	IBMVFC_MISSING_PARAMETER		= 0x0004,
+	IBMVFC_HOST_IO_BUS			= 0x0005,
+	IBMVFC_TRANS_CANCELLED			= 0x0006,
+	IBMVFC_TRANS_CANCELLED_IMPLICIT	= 0x0007,
+	IBMVFC_INSUFFICIENT_RESOURCE		= 0x0008,
+	IBMVFC_COMMAND_FAILED			= 0x8000,
+};
+
+enum ibmvfc_mad_types {
+	IBMVFC_NPIV_LOGIN		= 0x0001,
+	IBMVFC_DISC_TARGETS	= 0x0002,
+	IBMVFC_PORT_LOGIN		= 0x0004,
+	IBMVFC_PROCESS_LOGIN	= 0x0008,
+	IBMVFC_QUERY_TARGET	= 0x0010,
+	IBMVFC_IMPLICIT_LOGOUT	= 0x0040,
+	IBMVFC_TMF_MAD		= 0x0100,
+};
+
+struct ibmvfc_mad_common {
+	u32 version;
+	u32 reserved;
+	u32 opcode;
+	u16 status;
+	u16 length;
+	u64 tag;
+}__attribute__((packed, aligned (8)));
+
+struct ibmvfc_npiv_login_mad {
+	struct ibmvfc_mad_common common;
+	struct srp_direct_buf buffer;
+}__attribute__((packed, aligned (8)));
+
+#define IBMVFC_MAX_NAME 256
+
+struct ibmvfc_npiv_login {
+	u32 ostype;
+#define IBMVFC_OS_LINUX	0x02
+	u32 pad;
+	u64 max_dma_len;
+	u32 max_payload;
+	u32 max_response;
+	u32 partition_num;
+	u32 vfc_frame_version;
+	u16 fcp_version;
+	u16 flags;
+#define IBMVFC_CLIENT_MIGRATED	0x01
+#define IBMVFC_FLUSH_ON_HALT		0x02
+	u32 max_cmds;
+	u64 capabilities;
+#define IBMVFC_CAN_MIGRATE		0x01
+	u64 node_name;
+	struct srp_direct_buf async;
+	u8 partition_name[IBMVFC_MAX_NAME];
+	u8 device_name[IBMVFC_MAX_NAME];
+	u8 drc_name[IBMVFC_MAX_NAME];
+	u64 reserved2[2];
+}__attribute__((packed, aligned (8)));
+
+struct ibmvfc_common_svc_parms {
+	u16 fcph_version;
+	u16 b2b_credit;
+	u16 features;
+	u16 bb_rcv_sz; /* upper nibble is BB_SC_N */
+	u32 ratov;
+	u32 edtov;
+}__attribute__((packed, aligned (4)));
+
+struct ibmvfc_service_parms {
+	struct ibmvfc_common_svc_parms common;
+	u8 port_name[8];
+	u8 node_name[8];
+	u32 class1_parms[4];
+	u32 class2_parms[4];
+	u32 class3_parms[4];
+	u32 obsolete[4];
+	u32 vendor_version[4];
+	u32 services_avail[2];
+	u32 ext_len;
+	u32 reserved[30];
+	u32 clk_sync_qos[2];
+}__attribute__((packed, aligned (4)));
+
+struct ibmvfc_npiv_login_resp {
+	u32 version;
+	u16 status;
+	u16 error;
+	u32 flags;
+#define IBMVFC_NATIVE_FC		0x01
+#define IBMVFC_CAN_FLUSH_ON_HALT	0x08
+	u32 reserved;
+	u64 capabilites;
+	u32 max_cmds;
+	u32 scsi_id_sz;
+	u64 max_dma_len;
+	u64 scsi_id;
+	u64 port_name;
+	u64 node_name;
+	u64 link_speed;
+	u8 partition_name[IBMVFC_MAX_NAME];
+	u8 device_name[IBMVFC_MAX_NAME];
+	u8 port_loc_code[IBMVFC_MAX_NAME];
+	u8 drc_name[IBMVFC_MAX_NAME];
+	struct ibmvfc_service_parms service_parms;
+	u64 reserved2;
+}__attribute__((packed, aligned (8)));
+
+union ibmvfc_npiv_login_data {
+	struct ibmvfc_npiv_login login;
+	struct ibmvfc_npiv_login_resp resp;
+}__attribute__((packed, aligned (8)));
+
+struct ibmvfc_discover_targets_buf {
+	u32 scsi_id[1];
+#define IBMVFC_DISC_TGT_SCSI_ID_MASK	0x00ffffff
+};
+
+struct ibmvfc_discover_targets {
+	struct ibmvfc_mad_common common;
+	struct srp_direct_buf buffer;
+	u32 flags;
+	u16 status;
+	u16 error;
+	u32 bufflen;
+	u32 num_avail;
+	u32 num_written;
+	u64 reserved[2];
+}__attribute__((packed, aligned (8)));
+
+enum ibmvfc_fc_reason {
+	IBMVFC_INVALID_ELS_CMD_CODE	= 0x01,
+	IBMVFC_INVALID_VERSION		= 0x02,
+	IBMVFC_LOGICAL_ERROR		= 0x03,
+	IBMVFC_INVALID_CT_IU_SIZE	= 0x04,
+	IBMVFC_LOGICAL_BUSY		= 0x05,
+	IBMVFC_PROTOCOL_ERROR		= 0x07,
+	IBMVFC_UNABLE_TO_PERFORM_REQ	= 0x09,
+	IBMVFC_CMD_NOT_SUPPORTED	= 0x0B,
+	IBMVFC_SERVER_NOT_AVAIL		= 0x0D,
+	IBMVFC_CMD_IN_PROGRESS		= 0x0E,
+	IBMVFC_VENDOR_SPECIFIC		= 0xFF,
+};
+
+enum ibmvfc_fc_type {
+	IBMVFC_FABRIC_REJECT	= 0x01,
+	IBMVFC_PORT_REJECT	= 0x02,
+	IBMVFC_LS_REJECT		= 0x03,
+	IBMVFC_FABRIC_BUSY	= 0x04,
+	IBMVFC_PORT_BUSY		= 0x05,
+	IBMVFC_BASIC_REJECT	= 0x06,
+};
+
+enum ibmvfc_gs_explain {
+	IBMVFC_PORT_NAME_NOT_REG	= 0x02,
+};
+
+struct ibmvfc_port_login {
+	struct ibmvfc_mad_common common;
+	u64 scsi_id;
+	u16 reserved;
+	u16 fc_service_class;
+	u32 blksz;
+	u32 hdr_per_blk;
+	u16 status;
+	u16 error;		/* also fc_reason */
+	u16 fc_explain;
+	u16 fc_type;
+	u32 reserved2;
+	struct ibmvfc_service_parms service_parms;
+	struct ibmvfc_service_parms service_parms_change;
+	u64 reserved3[2];
+}__attribute__((packed, aligned (8)));
+
+struct ibmvfc_prli_svc_parms {
+	u8 type;
+#define IBMVFC_SCSI_FCP_TYPE		0x08
+	u8 type_ext;
+	u16 flags;
+#define IBMVFC_PRLI_ORIG_PA_VALID			0x8000
+#define IBMVFC_PRLI_RESP_PA_VALID			0x4000
+#define IBMVFC_PRLI_EST_IMG_PAIR			0x2000
+	u32 orig_pa;
+	u32 resp_pa;
+	u32 service_parms;
+#define IBMVFC_PRLI_TASK_RETRY			0x00000200
+#define IBMVFC_PRLI_RETRY				0x00000100
+#define IBMVFC_PRLI_DATA_OVERLAY			0x00000040
+#define IBMVFC_PRLI_INITIATOR_FUNC			0x00000020
+#define IBMVFC_PRLI_TARGET_FUNC			0x00000010
+#define IBMVFC_PRLI_READ_FCP_XFER_RDY_DISABLED	0x00000002
+#define IBMVFC_PRLI_WR_FCP_XFER_RDY_DISABLED	0x00000001
+}__attribute__((packed, aligned (4)));
+
+struct ibmvfc_process_login {
+	struct ibmvfc_mad_common common;
+	u64 scsi_id;
+	struct ibmvfc_prli_svc_parms parms;
+	u8 reserved[48];
+	u16 status;
+	u16 error;			/* also fc_reason */
+	u32 reserved2;
+	u64 reserved3[2];
+}__attribute__((packed, aligned (8)));
+
+struct ibmvfc_query_tgt {
+	struct ibmvfc_mad_common common;
+	u64 wwpn;
+	u64 scsi_id;
+	u16 status;
+	u16 error;
+	u16 fc_explain;
+	u16 fc_type;
+	u64 reserved[2];
+}__attribute__((packed, aligned (8)));
+
+struct ibmvfc_implicit_logout {
+	struct ibmvfc_mad_common common;
+	u64 old_scsi_id;
+	u64 reserved[2];
+}__attribute__((packed, aligned (8)));
+
+struct ibmvfc_tmf {
+	struct ibmvfc_mad_common common;
+	u64 scsi_id;
+	struct scsi_lun lun;
+	u32 flags;
+#define IBMVFC_TMF_ABORT_TASK		0x02
+#define IBMVFC_TMF_ABORT_TASK_SET	0x04
+#define IBMVFC_TMF_LUN_RESET		0x10
+#define IBMVFC_TMF_TGT_RESET		0x20
+#define IBMVFC_TMF_LUA_VALID		0x40
+	u32 cancel_key;
+	u32 my_cancel_key;
+#define IBMVFC_TMF_CANCEL_KEY		0x80000000
+	u32 pad;
+	u64 reserved[2];
+}__attribute__((packed, aligned (8)));
+
+enum ibmvfc_fcp_rsp_info_codes {
+	RSP_NO_FAILURE		= 0x00,
+	RSP_TMF_REJECTED		= 0x04,
+	RSP_TMF_FAILED		= 0x05,
+	RSP_TMF_INVALID_LUN	= 0x09,
+};
+
+struct ibmvfc_fcp_rsp_info {
+	u16 reserved;
+	u8 rsp_code;
+	u8 reserved2[4];
+}__attribute__((packed, aligned (2)));
+
+enum ibmvfc_fcp_rsp_flags {
+	FCP_BIDI_RSP			= 0x80,
+	FCP_BIDI_READ_RESID_UNDER	= 0x40,
+	FCP_BIDI_READ_RESID_OVER	= 0x20,
+	FCP_CONF_REQ			= 0x10,
+	FCP_RESID_UNDER			= 0x08,
+	FCP_RESID_OVER			= 0x04,
+	FCP_SNS_LEN_VALID			= 0x02,
+	FCP_RSP_LEN_VALID			= 0x01,
+};
+
+union ibmvfc_fcp_rsp_data {
+	struct ibmvfc_fcp_rsp_info info;
+	u8 sense[SCSI_SENSE_BUFFERSIZE + sizeof(struct ibmvfc_fcp_rsp_info)];
+}__attribute__((packed, aligned (8)));
+
+struct ibmvfc_fcp_rsp {
+	u64 reserved;
+	u16 retry_delay_timer;
+	u8 flags;
+	u8 scsi_status;
+	u32 fcp_resid;
+	u32 fcp_sense_len;
+	u32 fcp_rsp_len;
+	union ibmvfc_fcp_rsp_data data;
+}__attribute__((packed, aligned (8)));
+
+enum ibmvfc_cmd_flags {
+	IBMVFC_SCATTERLIST	= 0x0001,
+	IBMVFC_NO_MEM_DESC	= 0x0002,
+	IBMVFC_READ			= 0x0004,
+	IBMVFC_WRITE		= 0x0008,
+	IBMVFC_TMF			= 0x0080,
+	IBMVFC_CLASS_3_ERR	= 0x0100,
+};
+
+enum ibmvfc_fc_task_attr {
+	IBMVFC_SIMPLE_TASK	= 0x00,
+	IBMVFC_HEAD_OF_QUEUE	= 0x01,
+	IBMVFC_ORDERED_TASK	= 0x02,
+	IBMVFC_ACA_TASK		= 0x04,
+};
+
+enum ibmvfc_fc_tmf_flags {
+	IBMVFC_ABORT_TASK_SET	= 0x02,
+	IBMVFC_LUN_RESET		= 0x10,
+	IBMVFC_TARGET_RESET	= 0x20,
+};
+
+struct ibmvfc_fcp_cmd_iu {
+	struct scsi_lun lun;
+	u8 crn;
+	u8 pri_task_attr;
+	u8 tmf_flags;
+	u8 add_cdb_len;
+#define IBMVFC_RDDATA		0x02
+#define IBMVFC_WRDATA		0x01
+	u8 cdb[IBMVFC_MAX_CDB_LEN];
+	u32 xfer_len;
+}__attribute__((packed, aligned (4)));
+
+struct ibmvfc_cmd {
+	u64 task_tag;
+	u32 frame_type;
+	u32 payload_len;
+	u32 resp_len;
+	u32 adapter_resid;
+	u16 status;
+	u16 error;
+	u16 flags;
+	u16 response_flags;
+#define IBMVFC_ADAPTER_RESID_VALID	0x01
+	u32 cancel_key;
+	u32 exchange_id;
+	struct srp_direct_buf ext_func;
+	struct srp_direct_buf ioba;
+	struct srp_direct_buf resp;
+	u64 correlation;
+	u64 tgt_scsi_id;
+	u64 tag;
+	u64 reserved3[2];
+	struct ibmvfc_fcp_cmd_iu iu;
+	struct ibmvfc_fcp_rsp rsp;
+}__attribute__((packed, aligned (8)));
+
+struct ibmvfc_trace_start_entry {
+	u32 xfer_len;
+}__attribute__((packed));
+
+struct ibmvfc_trace_end_entry {
+	u16 status;
+	u16 error;
+	u8 fcp_rsp_flags;
+	u8 rsp_code;
+	u8 scsi_status;
+	u8 reserved;
+}__attribute__((packed));
+
+struct ibmvfc_trace_entry {
+	struct ibmvfc_event *evt;
+	u32 time;
+	u32 scsi_id;
+	u32 lun;
+	u8 fmt;
+	u8 op_code;
+	u8 tmf_flags;
+	u8 type;
+#define IBMVFC_TRC_START	0x00
+#define IBMVFC_TRC_END		0xff
+	union {
+		struct ibmvfc_trace_start_entry start;
+		struct ibmvfc_trace_end_entry end;
+	} u;
+}__attribute__((packed, aligned (8)));
+
+enum ibmvfc_crq_formats {
+	IBMVFC_CMD_FORMAT		= 0x01,
+	IBMVFC_ASYNC_EVENT	= 0x02,
+	IBMVFC_MAD_FORMAT		= 0x04,
+};
+
+enum ibmvfc_async_event {
+	IBMVFC_AE_ELS_PLOGI		= 0x0001,
+	IBMVFC_AE_ELS_LOGO		= 0x0002,
+	IBMVFC_AE_ELS_PRLO		= 0x0004,
+	IBMVFC_AE_SCN_NPORT		= 0x0008,
+	IBMVFC_AE_SCN_GROUP		= 0x0010,
+	IBMVFC_AE_SCN_DOMAIN		= 0x0020,
+	IBMVFC_AE_SCN_FABRIC		= 0x0040,
+	IBMVFC_AE_LINK_UP			= 0x0080,
+	IBMVFC_AE_LINK_DOWN		= 0x0100,
+	IBMVFC_AE_LINK_DEAD		= 0x0200,
+	IBMVFC_AE_HALT			= 0x0400,
+	IBMVFC_AE_RESUME			= 0x0800,
+	IBMVFC_AE_ADAPTER_FAILED	= 0x1000,
+};
+
+struct ibmvfc_crq {
+	u8 valid;
+	u8 format;
+	u8 reserved[6];
+	u64 ioba;
+}__attribute__((packed, aligned (8)));
+
+struct ibmvfc_crq_queue {
+	struct ibmvfc_crq *msgs;
+	int size, cur;
+	dma_addr_t msg_token;
+};
+
+struct ibmvfc_async_crq {
+	u8 valid;
+	u8 pad[3];
+	u32 pad2;
+	u64 event;
+	u64 scsi_id;
+	u64 wwpn;
+	u64 node_name;
+	u64 reserved;
+}__attribute__((packed, aligned (8)));
+
+struct ibmvfc_async_crq_queue {
+	struct ibmvfc_async_crq *msgs;
+	int size, cur;
+	dma_addr_t msg_token;
+};
+
+union ibmvfc_iu {
+	struct ibmvfc_mad_common mad_common;
+	struct ibmvfc_npiv_login_mad npiv_login;
+	struct ibmvfc_discover_targets discover_targets;
+	struct ibmvfc_port_login plogi;
+	struct ibmvfc_process_login prli;
+	struct ibmvfc_query_tgt query_tgt;
+	struct ibmvfc_implicit_logout implicit_logout;
+	struct ibmvfc_tmf tmf;
+	struct ibmvfc_cmd cmd;
+}__attribute__((packed, aligned (8)));
+
+enum ibmvfc_target_action {
+	IBMVFC_TGT_ACTION_NONE = 0,
+	IBMVFC_TGT_ACTION_INIT,
+	IBMVFC_TGT_ACTION_INIT_WAIT,
+	IBMVFC_TGT_ACTION_ADD_RPORT,
+	IBMVFC_TGT_ACTION_DEL_RPORT,
+};
+
+struct ibmvfc_target {
+	struct list_head queue;
+	struct ibmvfc_host *vhost;
+	u64 scsi_id;
+	u64 new_scsi_id;
+	struct fc_rport *rport;
+	int target_id;
+	enum ibmvfc_target_action action;
+	int need_login;
+	int init_retries;
+	struct ibmvfc_service_parms service_parms;
+	struct ibmvfc_service_parms service_parms_change;
+	struct fc_rport_identifiers ids;
+	void (*job_step) (struct ibmvfc_target *);
+	struct kref kref;
+};
+
+/* a unit of work for the hosting partition */
+struct ibmvfc_event {
+	struct list_head queue;
+	struct ibmvfc_host *vhost;
+	struct ibmvfc_target *tgt;
+	struct scsi_cmnd *cmnd;
+	atomic_t free;
+	union ibmvfc_iu *xfer_iu;
+	void (*done) (struct ibmvfc_event *);
+	struct ibmvfc_crq crq;
+	union ibmvfc_iu iu;
+	union ibmvfc_iu *sync_iu;
+	struct srp_direct_buf *ext_list;
+	dma_addr_t ext_list_token;
+	struct completion comp;
+	struct timer_list timer;
+};
+
+/* a pool of event structs for use */
+struct ibmvfc_event_pool {
+	struct ibmvfc_event *events;
+	u32 size;
+	union ibmvfc_iu *iu_storage;
+	dma_addr_t iu_token;
+};
+
+enum ibmvfc_host_action {
+	IBMVFC_HOST_ACTION_NONE = 0,
+	IBMVFC_HOST_ACTION_INIT,
+	IBMVFC_HOST_ACTION_INIT_WAIT,
+	IBMVFC_HOST_ACTION_QUERY,
+	IBMVFC_HOST_ACTION_QUERY_TGTS,
+	IBMVFC_HOST_ACTION_TGT_DEL,
+	IBMVFC_HOST_ACTION_ALLOC_TGTS,
+	IBMVFC_HOST_ACTION_TGT_INIT,
+	IBMVFC_HOST_ACTION_TGT_ADD,
+};
+
+enum ibmvfc_host_state {
+	IBMVFC_NO_CRQ = 0,
+	IBMVFC_INITIALIZING,
+	IBMVFC_ACTIVE,
+	IBMVFC_HALTED,
+	IBMVFC_LINK_DOWN,
+	IBMVFC_LINK_DEAD,
+	IBMVFC_HOST_OFFLINE,
+};
+
+struct ibmvfc_host {
+	char name[8];
+	struct list_head queue;
+	struct Scsi_Host *host;
+	enum ibmvfc_host_state state;
+	enum ibmvfc_host_action action;
+#define IBMVFC_NUM_TRACE_INDEX_BITS		8
+#define IBMVFC_NUM_TRACE_ENTRIES		(1 << IBMVFC_NUM_TRACE_INDEX_BITS)
+#define IBMVFC_TRACE_SIZE	(sizeof(struct ibmvfc_trace_entry) * IBMVFC_NUM_TRACE_ENTRIES)
+	struct ibmvfc_trace_entry *trace;
+	u32 trace_index:IBMVFC_NUM_TRACE_INDEX_BITS;
+	int num_targets;
+	struct list_head targets;
+	struct list_head sent;
+	struct list_head free;
+	struct device *dev;
+	struct ibmvfc_event_pool pool;
+	struct dma_pool *sg_pool;
+	mempool_t *tgt_pool;
+	struct ibmvfc_crq_queue crq;
+	struct ibmvfc_async_crq_queue async_crq;
+	struct ibmvfc_npiv_login login_info;
+	union ibmvfc_npiv_login_data *login_buf;
+	dma_addr_t login_buf_dma;
+	int disc_buf_sz;
+	int log_level;
+	struct ibmvfc_discover_targets_buf *disc_buf;
+	int task_set;
+	int init_retries;
+	int discovery_threads;
+	int client_migrated;
+	int reinit;
+	int events_to_log;
+#define IBMVFC_AE_LINKUP	0x0001
+#define IBMVFC_AE_LINKDOWN	0x0002
+#define IBMVFC_AE_RSCN		0x0004
+	dma_addr_t disc_buf_dma;
+	unsigned int partition_number;
+	char partition_name[97];
+	void (*job_step) (struct ibmvfc_host *);
+	struct task_struct *work_thread;
+	wait_queue_head_t init_wait_q;
+	wait_queue_head_t work_wait_q;
+};
+
+#define DBG_CMD(CMD) do { if (ibmvfc_debug) CMD; } while (0)
+
+#define tgt_dbg(t, fmt, ...)			\
+	DBG_CMD(dev_info((t)->vhost->dev, "%lX: " fmt, (t)->scsi_id, ##__VA_ARGS__))
+
+#define tgt_err(t, fmt, ...)		\
+	dev_err((t)->vhost->dev, "%lX: " fmt, (t)->scsi_id, ##__VA_ARGS__)
+
+#define ibmvfc_dbg(vhost, ...) \
+	DBG_CMD(dev_info((vhost)->dev, ##__VA_ARGS__))
+
+#define ibmvfc_log(vhost, level, ...) \
+	do { \
+		if (level >= (vhost)->log_level) \
+			dev_err((vhost)->dev, ##__VA_ARGS__); \
+	} while (0)
+
+#define ENTER DBG_CMD(printk(KERN_INFO IBMVFC_NAME": Entering %s\n", __FUNCTION__))
+#define LEAVE DBG_CMD(printk(KERN_INFO IBMVFC_NAME": Leaving %s\n", __FUNCTION__))
+
+#ifdef CONFIG_SCSI_IBMVFC_TRACE
+#define ibmvfc_create_trace_file(kobj, attr) sysfs_create_bin_file(kobj, attr)
+#define ibmvfc_remove_trace_file(kobj, attr) sysfs_remove_bin_file(kobj, attr)
+#else
+#define ibmvfc_create_trace_file(kobj, attr) 0
+#define ibmvfc_remove_trace_file(kobj, attr) do { } while (0)
+#endif
+
+#endif
diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c
index 72b9b2a..2a2f009 100644
--- a/drivers/scsi/iscsi_tcp.c
+++ b/drivers/scsi/iscsi_tcp.c
@@ -64,6 +64,10 @@
 #define BUG_ON(expr)
 #endif
 
+static struct scsi_transport_template *iscsi_tcp_scsi_transport;
+static struct scsi_host_template iscsi_sht;
+static struct iscsi_transport iscsi_tcp_transport;
+
 static unsigned int iscsi_max_lun = 512;
 module_param_named(max_lun, iscsi_max_lun, uint, S_IRUGO);
 
@@ -494,39 +498,43 @@
  * must be called with session lock
  */
 static void
-iscsi_tcp_cleanup_ctask(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
+iscsi_tcp_cleanup_task(struct iscsi_conn *conn, struct iscsi_task *task)
 {
-	struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
+	struct iscsi_tcp_task *tcp_task = task->dd_data;
 	struct iscsi_r2t_info *r2t;
 
-	/* flush ctask's r2t queues */
-	while (__kfifo_get(tcp_ctask->r2tqueue, (void*)&r2t, sizeof(void*))) {
-		__kfifo_put(tcp_ctask->r2tpool.queue, (void*)&r2t,
+	/* nothing to do for mgmt tasks */
+	if (!task->sc)
+		return;
+
+	/* flush task's r2t queues */
+	while (__kfifo_get(tcp_task->r2tqueue, (void*)&r2t, sizeof(void*))) {
+		__kfifo_put(tcp_task->r2tpool.queue, (void*)&r2t,
 			    sizeof(void*));
-		debug_scsi("iscsi_tcp_cleanup_ctask pending r2t dropped\n");
+		debug_scsi("iscsi_tcp_cleanup_task pending r2t dropped\n");
 	}
 
-	r2t = tcp_ctask->r2t;
+	r2t = tcp_task->r2t;
 	if (r2t != NULL) {
-		__kfifo_put(tcp_ctask->r2tpool.queue, (void*)&r2t,
+		__kfifo_put(tcp_task->r2tpool.queue, (void*)&r2t,
 			    sizeof(void*));
-		tcp_ctask->r2t = NULL;
+		tcp_task->r2t = NULL;
 	}
 }
 
 /**
  * iscsi_data_rsp - SCSI Data-In Response processing
  * @conn: iscsi connection
- * @ctask: scsi command task
+ * @task: scsi command task
  **/
 static int
-iscsi_data_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
+iscsi_data_rsp(struct iscsi_conn *conn, struct iscsi_task *task)
 {
 	struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
-	struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
+	struct iscsi_tcp_task *tcp_task = task->dd_data;
 	struct iscsi_data_rsp *rhdr = (struct iscsi_data_rsp *)tcp_conn->in.hdr;
 	struct iscsi_session *session = conn->session;
-	struct scsi_cmnd *sc = ctask->sc;
+	struct scsi_cmnd *sc = task->sc;
 	int datasn = be32_to_cpu(rhdr->datasn);
 	unsigned total_in_length = scsi_in(sc)->length;
 
@@ -534,18 +542,18 @@
 	if (tcp_conn->in.datalen == 0)
 		return 0;
 
-	if (tcp_ctask->exp_datasn != datasn) {
-		debug_tcp("%s: ctask->exp_datasn(%d) != rhdr->datasn(%d)\n",
-		          __FUNCTION__, tcp_ctask->exp_datasn, datasn);
+	if (tcp_task->exp_datasn != datasn) {
+		debug_tcp("%s: task->exp_datasn(%d) != rhdr->datasn(%d)\n",
+		          __func__, tcp_task->exp_datasn, datasn);
 		return ISCSI_ERR_DATASN;
 	}
 
-	tcp_ctask->exp_datasn++;
+	tcp_task->exp_datasn++;
 
-	tcp_ctask->data_offset = be32_to_cpu(rhdr->offset);
-	if (tcp_ctask->data_offset + tcp_conn->in.datalen > total_in_length) {
+	tcp_task->data_offset = be32_to_cpu(rhdr->offset);
+	if (tcp_task->data_offset + tcp_conn->in.datalen > total_in_length) {
 		debug_tcp("%s: data_offset(%d) + data_len(%d) > total_length_in(%d)\n",
-		          __FUNCTION__, tcp_ctask->data_offset,
+		          __func__, tcp_task->data_offset,
 		          tcp_conn->in.datalen, total_in_length);
 		return ISCSI_ERR_DATA_OFFSET;
 	}
@@ -574,7 +582,7 @@
 /**
  * iscsi_solicit_data_init - initialize first Data-Out
  * @conn: iscsi connection
- * @ctask: scsi command task
+ * @task: scsi command task
  * @r2t: R2T info
  *
  * Notes:
@@ -584,7 +592,7 @@
  *	This function is called with connection lock taken.
  **/
 static void
-iscsi_solicit_data_init(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask,
+iscsi_solicit_data_init(struct iscsi_conn *conn, struct iscsi_task *task,
 			struct iscsi_r2t_info *r2t)
 {
 	struct iscsi_data *hdr;
@@ -595,8 +603,8 @@
 	hdr->datasn = cpu_to_be32(r2t->solicit_datasn);
 	r2t->solicit_datasn++;
 	hdr->opcode = ISCSI_OP_SCSI_DATA_OUT;
-	memcpy(hdr->lun, ctask->hdr->lun, sizeof(hdr->lun));
-	hdr->itt = ctask->hdr->itt;
+	memcpy(hdr->lun, task->hdr->lun, sizeof(hdr->lun));
+	hdr->itt = task->hdr->itt;
 	hdr->exp_statsn = r2t->exp_statsn;
 	hdr->offset = cpu_to_be32(r2t->data_offset);
 	if (r2t->data_length > conn->max_xmit_dlength) {
@@ -616,14 +624,14 @@
 /**
  * iscsi_r2t_rsp - iSCSI R2T Response processing
  * @conn: iscsi connection
- * @ctask: scsi command task
+ * @task: scsi command task
  **/
 static int
-iscsi_r2t_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
+iscsi_r2t_rsp(struct iscsi_conn *conn, struct iscsi_task *task)
 {
 	struct iscsi_r2t_info *r2t;
 	struct iscsi_session *session = conn->session;
-	struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
+	struct iscsi_tcp_task *tcp_task = task->dd_data;
 	struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
 	struct iscsi_r2t_rsp *rhdr = (struct iscsi_r2t_rsp *)tcp_conn->in.hdr;
 	int r2tsn = be32_to_cpu(rhdr->r2tsn);
@@ -636,23 +644,23 @@
 		return ISCSI_ERR_DATALEN;
 	}
 
-	if (tcp_ctask->exp_datasn != r2tsn){
-		debug_tcp("%s: ctask->exp_datasn(%d) != rhdr->r2tsn(%d)\n",
-		          __FUNCTION__, tcp_ctask->exp_datasn, r2tsn);
+	if (tcp_task->exp_datasn != r2tsn){
+		debug_tcp("%s: task->exp_datasn(%d) != rhdr->r2tsn(%d)\n",
+		          __func__, tcp_task->exp_datasn, r2tsn);
 		return ISCSI_ERR_R2TSN;
 	}
 
 	/* fill-in new R2T associated with the task */
 	iscsi_update_cmdsn(session, (struct iscsi_nopin*)rhdr);
 
-	if (!ctask->sc || session->state != ISCSI_STATE_LOGGED_IN) {
+	if (!task->sc || session->state != ISCSI_STATE_LOGGED_IN) {
 		iscsi_conn_printk(KERN_INFO, conn,
 				  "dropping R2T itt %d in recovery.\n",
-				  ctask->itt);
+				  task->itt);
 		return 0;
 	}
 
-	rc = __kfifo_get(tcp_ctask->r2tpool.queue, (void*)&r2t, sizeof(void*));
+	rc = __kfifo_get(tcp_task->r2tpool.queue, (void*)&r2t, sizeof(void*));
 	BUG_ON(!rc);
 
 	r2t->exp_statsn = rhdr->statsn;
@@ -660,7 +668,7 @@
 	if (r2t->data_length == 0) {
 		iscsi_conn_printk(KERN_ERR, conn,
 				  "invalid R2T with zero data len\n");
-		__kfifo_put(tcp_ctask->r2tpool.queue, (void*)&r2t,
+		__kfifo_put(tcp_task->r2tpool.queue, (void*)&r2t,
 			    sizeof(void*));
 		return ISCSI_ERR_DATALEN;
 	}
@@ -671,12 +679,12 @@
 			    r2t->data_length, session->max_burst);
 
 	r2t->data_offset = be32_to_cpu(rhdr->data_offset);
-	if (r2t->data_offset + r2t->data_length > scsi_out(ctask->sc)->length) {
+	if (r2t->data_offset + r2t->data_length > scsi_out(task->sc)->length) {
 		iscsi_conn_printk(KERN_ERR, conn,
 				  "invalid R2T with data len %u at offset %u "
 				  "and total length %d\n", r2t->data_length,
-				  r2t->data_offset, scsi_out(ctask->sc)->length);
-		__kfifo_put(tcp_ctask->r2tpool.queue, (void*)&r2t,
+				  r2t->data_offset, scsi_out(task->sc)->length);
+		__kfifo_put(tcp_task->r2tpool.queue, (void*)&r2t,
 			    sizeof(void*));
 		return ISCSI_ERR_DATALEN;
 	}
@@ -684,13 +692,13 @@
 	r2t->ttt = rhdr->ttt; /* no flip */
 	r2t->solicit_datasn = 0;
 
-	iscsi_solicit_data_init(conn, ctask, r2t);
+	iscsi_solicit_data_init(conn, task, r2t);
 
-	tcp_ctask->exp_datasn = r2tsn + 1;
-	__kfifo_put(tcp_ctask->r2tqueue, (void*)&r2t, sizeof(void*));
+	tcp_task->exp_datasn = r2tsn + 1;
+	__kfifo_put(tcp_task->r2tqueue, (void*)&r2t, sizeof(void*));
 	conn->r2t_pdus_cnt++;
 
-	iscsi_requeue_ctask(ctask);
+	iscsi_requeue_task(task);
 	return 0;
 }
 
@@ -733,10 +741,8 @@
 iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
 {
 	int rc = 0, opcode, ahslen;
-	struct iscsi_session *session = conn->session;
 	struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
-	struct iscsi_cmd_task *ctask;
-	uint32_t itt;
+	struct iscsi_task *task;
 
 	/* verify PDU length */
 	tcp_conn->in.datalen = ntoh24(hdr->dlength);
@@ -754,7 +760,7 @@
 
 	opcode = hdr->opcode & ISCSI_OPCODE_MASK;
 	/* verify itt (itt encoding: age+cid+itt) */
-	rc = iscsi_verify_itt(conn, hdr, &itt);
+	rc = iscsi_verify_itt(conn, hdr->itt);
 	if (rc)
 		return rc;
 
@@ -763,16 +769,21 @@
 
 	switch(opcode) {
 	case ISCSI_OP_SCSI_DATA_IN:
-		ctask = session->cmds[itt];
 		spin_lock(&conn->session->lock);
-		rc = iscsi_data_rsp(conn, ctask);
-		spin_unlock(&conn->session->lock);
-		if (rc)
-			return rc;
+		task = iscsi_itt_to_ctask(conn, hdr->itt);
+		if (!task)
+			rc = ISCSI_ERR_BAD_ITT;
+		else
+			rc = iscsi_data_rsp(conn, task);
+		if (rc) {
+			spin_unlock(&conn->session->lock);
+			break;
+		}
+
 		if (tcp_conn->in.datalen) {
-			struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
+			struct iscsi_tcp_task *tcp_task = task->dd_data;
 			struct hash_desc *rx_hash = NULL;
-			struct scsi_data_buffer *sdb = scsi_in(ctask->sc);
+			struct scsi_data_buffer *sdb = scsi_in(task->sc);
 
 			/*
 			 * Setup copy of Data-In into the Scsi_Cmnd
@@ -787,17 +798,21 @@
 
 			debug_tcp("iscsi_tcp_begin_data_in(%p, offset=%d, "
 				  "datalen=%d)\n", tcp_conn,
-				  tcp_ctask->data_offset,
+				  tcp_task->data_offset,
 				  tcp_conn->in.datalen);
-			return iscsi_segment_seek_sg(&tcp_conn->in.segment,
-						     sdb->table.sgl,
-						     sdb->table.nents,
-						     tcp_ctask->data_offset,
-						     tcp_conn->in.datalen,
-						     iscsi_tcp_process_data_in,
-						     rx_hash);
+			rc = iscsi_segment_seek_sg(&tcp_conn->in.segment,
+						   sdb->table.sgl,
+						   sdb->table.nents,
+						   tcp_task->data_offset,
+						   tcp_conn->in.datalen,
+						   iscsi_tcp_process_data_in,
+						   rx_hash);
+			spin_unlock(&conn->session->lock);
+			return rc;
 		}
-		/* fall through */
+		rc = __iscsi_complete_pdu(conn, hdr, NULL, 0);
+		spin_unlock(&conn->session->lock);
+		break;
 	case ISCSI_OP_SCSI_CMD_RSP:
 		if (tcp_conn->in.datalen) {
 			iscsi_tcp_data_recv_prep(tcp_conn);
@@ -806,15 +821,17 @@
 		rc = iscsi_complete_pdu(conn, hdr, NULL, 0);
 		break;
 	case ISCSI_OP_R2T:
-		ctask = session->cmds[itt];
-		if (ahslen)
+		spin_lock(&conn->session->lock);
+		task = iscsi_itt_to_ctask(conn, hdr->itt);
+		if (!task)
+			rc = ISCSI_ERR_BAD_ITT;
+		else if (ahslen)
 			rc = ISCSI_ERR_AHSLEN;
-		else if (ctask->sc->sc_data_direction == DMA_TO_DEVICE) {
-			spin_lock(&session->lock);
-			rc = iscsi_r2t_rsp(conn, ctask);
-			spin_unlock(&session->lock);
-		} else
+		else if (task->sc->sc_data_direction == DMA_TO_DEVICE)
+			rc = iscsi_r2t_rsp(conn, task);
+		else
 			rc = ISCSI_ERR_PROTO;
+		spin_unlock(&conn->session->lock);
 		break;
 	case ISCSI_OP_LOGIN_RSP:
 	case ISCSI_OP_TEXT_RSP:
@@ -1176,7 +1193,7 @@
 {
 	struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
 
-	debug_tcp("%s(%p%s)\n", __FUNCTION__, tcp_conn,
+	debug_tcp("%s(%p%s)\n", __func__, tcp_conn,
 			conn->hdrdgst_en? ", digest enabled" : "");
 
 	/* Clear the data segment - needs to be filled in by the
@@ -1185,7 +1202,7 @@
 
 	/* If header digest is enabled, compute the CRC and
 	 * place the digest into the same buffer. We make
-	 * sure that both iscsi_tcp_ctask and mtask have
+	 * sure that both iscsi_tcp_task and mtask have
 	 * sufficient room.
 	 */
 	if (conn->hdrdgst_en) {
@@ -1217,7 +1234,7 @@
 	struct hash_desc *tx_hash = NULL;
 	unsigned int hdr_spec_len;
 
-	debug_tcp("%s(%p, offset=%d, datalen=%d%s)\n", __FUNCTION__,
+	debug_tcp("%s(%p, offset=%d, datalen=%d%s)\n", __func__,
 			tcp_conn, offset, len,
 			conn->datadgst_en? ", digest enabled" : "");
 
@@ -1242,7 +1259,7 @@
 	struct hash_desc *tx_hash = NULL;
 	unsigned int hdr_spec_len;
 
-	debug_tcp("%s(%p, datalen=%d%s)\n", __FUNCTION__, tcp_conn, len,
+	debug_tcp("%s(%p, datalen=%d%s)\n", __func__, tcp_conn, len,
 		  conn->datadgst_en? ", digest enabled" : "");
 
 	/* Make sure the datalen matches what the caller
@@ -1260,7 +1277,7 @@
 /**
  * iscsi_solicit_data_cont - initialize next Data-Out
  * @conn: iscsi connection
- * @ctask: scsi command task
+ * @task: scsi command task
  * @r2t: R2T info
  * @left: bytes left to transfer
  *
@@ -1271,7 +1288,7 @@
  *	Called under connection lock.
  **/
 static int
-iscsi_solicit_data_cont(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask,
+iscsi_solicit_data_cont(struct iscsi_conn *conn, struct iscsi_task *task,
 			struct iscsi_r2t_info *r2t)
 {
 	struct iscsi_data *hdr;
@@ -1288,8 +1305,8 @@
 	hdr->datasn = cpu_to_be32(r2t->solicit_datasn);
 	r2t->solicit_datasn++;
 	hdr->opcode = ISCSI_OP_SCSI_DATA_OUT;
-	memcpy(hdr->lun, ctask->hdr->lun, sizeof(hdr->lun));
-	hdr->itt = ctask->hdr->itt;
+	memcpy(hdr->lun, task->hdr->lun, sizeof(hdr->lun));
+	hdr->itt = task->hdr->itt;
 	hdr->exp_statsn = r2t->exp_statsn;
 	new_offset = r2t->data_offset + r2t->sent;
 	hdr->offset = cpu_to_be32(new_offset);
@@ -1307,89 +1324,76 @@
 }
 
 /**
- * iscsi_tcp_ctask - Initialize iSCSI SCSI_READ or SCSI_WRITE commands
+ * iscsi_tcp_task - Initialize iSCSI SCSI_READ or SCSI_WRITE commands
  * @conn: iscsi connection
- * @ctask: scsi command task
+ * @task: scsi command task
  * @sc: scsi command
  **/
 static int
-iscsi_tcp_ctask_init(struct iscsi_cmd_task *ctask)
+iscsi_tcp_task_init(struct iscsi_task *task)
 {
-	struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
-	struct iscsi_conn *conn = ctask->conn;
-	struct scsi_cmnd *sc = ctask->sc;
+	struct iscsi_tcp_task *tcp_task = task->dd_data;
+	struct iscsi_conn *conn = task->conn;
+	struct scsi_cmnd *sc = task->sc;
 	int err;
 
-	BUG_ON(__kfifo_len(tcp_ctask->r2tqueue));
-	tcp_ctask->sent = 0;
-	tcp_ctask->exp_datasn = 0;
+	if (!sc) {
+		/*
+		 * mgmt tasks do not have a scatterlist since they come
+		 * in from the iscsi interface.
+		 */
+		debug_scsi("mtask deq [cid %d itt 0x%x]\n", conn->id,
+			   task->itt);
+
+		/* Prepare PDU, optionally w/ immediate data */
+		iscsi_tcp_send_hdr_prep(conn, task->hdr, sizeof(*task->hdr));
+
+		/* If we have immediate data, attach a payload */
+		if (task->data_count)
+			iscsi_tcp_send_linear_data_prepare(conn, task->data,
+							   task->data_count);
+		return 0;
+	}
+
+	BUG_ON(__kfifo_len(tcp_task->r2tqueue));
+	tcp_task->sent = 0;
+	tcp_task->exp_datasn = 0;
 
 	/* Prepare PDU, optionally w/ immediate data */
-	debug_scsi("ctask deq [cid %d itt 0x%x imm %d unsol %d]\n",
-		    conn->id, ctask->itt, ctask->imm_count,
-		    ctask->unsol_count);
-	iscsi_tcp_send_hdr_prep(conn, ctask->hdr, ctask->hdr_len);
+	debug_scsi("task deq [cid %d itt 0x%x imm %d unsol %d]\n",
+		    conn->id, task->itt, task->imm_count,
+		    task->unsol_count);
+	iscsi_tcp_send_hdr_prep(conn, task->hdr, task->hdr_len);
 
-	if (!ctask->imm_count)
+	if (!task->imm_count)
 		return 0;
 
 	/* If we have immediate data, attach a payload */
 	err = iscsi_tcp_send_data_prep(conn, scsi_out(sc)->table.sgl,
 				       scsi_out(sc)->table.nents,
-				       0, ctask->imm_count);
+				       0, task->imm_count);
 	if (err)
 		return err;
-	tcp_ctask->sent += ctask->imm_count;
-	ctask->imm_count = 0;
-	return 0;
-}
-
-/**
- * iscsi_tcp_mtask_xmit - xmit management(immediate) task
- * @conn: iscsi connection
- * @mtask: task management task
- *
- * Notes:
- *	The function can return -EAGAIN in which case caller must
- *	call it again later, or recover. '0' return code means successful
- *	xmit.
- **/
-static int
-iscsi_tcp_mtask_xmit(struct iscsi_conn *conn, struct iscsi_mgmt_task *mtask)
-{
-	int rc;
-
-	/* Flush any pending data first. */
-	rc = iscsi_tcp_flush(conn);
-	if (rc < 0)
-		return rc;
-
-	if (mtask->hdr->itt == RESERVED_ITT) {
-		struct iscsi_session *session = conn->session;
-
-		spin_lock_bh(&session->lock);
-		iscsi_free_mgmt_task(conn, mtask);
-		spin_unlock_bh(&session->lock);
-	}
-
+	tcp_task->sent += task->imm_count;
+	task->imm_count = 0;
 	return 0;
 }
 
 /*
- * iscsi_tcp_ctask_xmit - xmit normal PDU task
- * @conn: iscsi connection
- * @ctask: iscsi command task
+ * iscsi_tcp_task_xmit - xmit normal PDU task
+ * @task: iscsi command task
  *
  * We're expected to return 0 when everything was transmitted succesfully,
  * -EAGAIN if there's still data in the queue, or != 0 for any other kind
  * of error.
  */
 static int
-iscsi_tcp_ctask_xmit(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
+iscsi_tcp_task_xmit(struct iscsi_task *task)
 {
-	struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
-	struct scsi_cmnd *sc = ctask->sc;
-	struct scsi_data_buffer *sdb = scsi_out(sc);
+	struct iscsi_conn *conn = task->conn;
+	struct iscsi_tcp_task *tcp_task = task->dd_data;
+	struct scsi_cmnd *sc = task->sc;
+	struct scsi_data_buffer *sdb;
 	int rc = 0;
 
 flush:
@@ -1398,31 +1402,39 @@
 	if (rc < 0)
 		return rc;
 
+	/* mgmt command */
+	if (!sc) {
+		if (task->hdr->itt == RESERVED_ITT)
+			iscsi_put_task(task);
+		return 0;
+	}
+
 	/* Are we done already? */
 	if (sc->sc_data_direction != DMA_TO_DEVICE)
 		return 0;
 
-	if (ctask->unsol_count != 0) {
-		struct iscsi_data *hdr = &tcp_ctask->unsol_dtask.hdr;
+	sdb = scsi_out(sc);
+	if (task->unsol_count != 0) {
+		struct iscsi_data *hdr = &tcp_task->unsol_dtask.hdr;
 
 		/* Prepare a header for the unsolicited PDU.
 		 * The amount of data we want to send will be
-		 * in ctask->data_count.
+		 * in task->data_count.
 		 * FIXME: return the data count instead.
 		 */
-		iscsi_prep_unsolicit_data_pdu(ctask, hdr);
+		iscsi_prep_unsolicit_data_pdu(task, hdr);
 
 		debug_tcp("unsol dout [itt 0x%x doff %d dlen %d]\n",
-				ctask->itt, tcp_ctask->sent, ctask->data_count);
+				task->itt, tcp_task->sent, task->data_count);
 
 		iscsi_tcp_send_hdr_prep(conn, hdr, sizeof(*hdr));
 		rc = iscsi_tcp_send_data_prep(conn, sdb->table.sgl,
-					      sdb->table.nents, tcp_ctask->sent,
-					      ctask->data_count);
+					      sdb->table.nents, tcp_task->sent,
+					      task->data_count);
 		if (rc)
 			goto fail;
-		tcp_ctask->sent += ctask->data_count;
-		ctask->unsol_count -= ctask->data_count;
+		tcp_task->sent += task->data_count;
+		task->unsol_count -= task->data_count;
 		goto flush;
 	} else {
 		struct iscsi_session *session = conn->session;
@@ -1431,22 +1443,22 @@
 		/* All unsolicited PDUs sent. Check for solicited PDUs.
 		 */
 		spin_lock_bh(&session->lock);
-		r2t = tcp_ctask->r2t;
+		r2t = tcp_task->r2t;
 		if (r2t != NULL) {
 			/* Continue with this R2T? */
-			if (!iscsi_solicit_data_cont(conn, ctask, r2t)) {
+			if (!iscsi_solicit_data_cont(conn, task, r2t)) {
 				debug_scsi("  done with r2t %p\n", r2t);
 
-				__kfifo_put(tcp_ctask->r2tpool.queue,
+				__kfifo_put(tcp_task->r2tpool.queue,
 					    (void*)&r2t, sizeof(void*));
-				tcp_ctask->r2t = r2t = NULL;
+				tcp_task->r2t = r2t = NULL;
 			}
 		}
 
 		if (r2t == NULL) {
-			__kfifo_get(tcp_ctask->r2tqueue, (void*)&tcp_ctask->r2t,
+			__kfifo_get(tcp_task->r2tqueue, (void*)&tcp_task->r2t,
 				    sizeof(void*));
-			r2t = tcp_ctask->r2t;
+			r2t = tcp_task->r2t;
 		}
 		spin_unlock_bh(&session->lock);
 
@@ -1457,7 +1469,7 @@
 		}
 
 		debug_scsi("sol dout %p [dsn %d itt 0x%x doff %d dlen %d]\n",
-			r2t, r2t->solicit_datasn - 1, ctask->itt,
+			r2t, r2t->solicit_datasn - 1, task->itt,
 			r2t->data_offset + r2t->sent, r2t->data_count);
 
 		iscsi_tcp_send_hdr_prep(conn, &r2t->dtask.hdr,
@@ -1469,7 +1481,7 @@
 					      r2t->data_count);
 		if (rc)
 			goto fail;
-		tcp_ctask->sent += r2t->data_count;
+		tcp_task->sent += r2t->data_count;
 		r2t->sent += r2t->data_count;
 		goto flush;
 	}
@@ -1486,7 +1498,7 @@
 	struct iscsi_cls_conn *cls_conn;
 	struct iscsi_tcp_conn *tcp_conn;
 
-	cls_conn = iscsi_conn_setup(cls_session, conn_idx);
+	cls_conn = iscsi_conn_setup(cls_session, sizeof(*tcp_conn), conn_idx);
 	if (!cls_conn)
 		return NULL;
 	conn = cls_conn->dd_data;
@@ -1496,18 +1508,14 @@
 	 */
 	conn->max_recv_dlength = ISCSI_DEF_MAX_RECV_SEG_LEN;
 
-	tcp_conn = kzalloc(sizeof(*tcp_conn), GFP_KERNEL);
-	if (!tcp_conn)
-		goto tcp_conn_alloc_fail;
-
-	conn->dd_data = tcp_conn;
+	tcp_conn = conn->dd_data;
 	tcp_conn->iscsi_conn = conn;
 
 	tcp_conn->tx_hash.tfm = crypto_alloc_hash("crc32c", 0,
 						  CRYPTO_ALG_ASYNC);
 	tcp_conn->tx_hash.flags = 0;
 	if (IS_ERR(tcp_conn->tx_hash.tfm))
-		goto free_tcp_conn;
+		goto free_conn;
 
 	tcp_conn->rx_hash.tfm = crypto_alloc_hash("crc32c", 0,
 						  CRYPTO_ALG_ASYNC);
@@ -1519,14 +1527,12 @@
 
 free_tx_tfm:
 	crypto_free_hash(tcp_conn->tx_hash.tfm);
-free_tcp_conn:
+free_conn:
 	iscsi_conn_printk(KERN_ERR, conn,
 			  "Could not create connection due to crc32c "
 			  "loading error. Make sure the crc32c "
 			  "module is built as a module or into the "
 			  "kernel\n");
-	kfree(tcp_conn);
-tcp_conn_alloc_fail:
 	iscsi_conn_teardown(cls_conn);
 	return NULL;
 }
@@ -1547,7 +1553,6 @@
 
 	spin_lock_bh(&session->lock);
 	tcp_conn->sock = NULL;
-	conn->recv_lock = NULL;
 	spin_unlock_bh(&session->lock);
 	sockfd_put(sock);
 }
@@ -1559,20 +1564,32 @@
 	struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
 
 	iscsi_tcp_release_conn(conn);
-	iscsi_conn_teardown(cls_conn);
 
 	if (tcp_conn->tx_hash.tfm)
 		crypto_free_hash(tcp_conn->tx_hash.tfm);
 	if (tcp_conn->rx_hash.tfm)
 		crypto_free_hash(tcp_conn->rx_hash.tfm);
 
-	kfree(tcp_conn);
+	iscsi_conn_teardown(cls_conn);
 }
 
 static void
 iscsi_tcp_conn_stop(struct iscsi_cls_conn *cls_conn, int flag)
 {
 	struct iscsi_conn *conn = cls_conn->dd_data;
+	struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+
+	/* userspace may have goofed up and not bound us */
+	if (!tcp_conn->sock)
+		return;
+	/*
+	 * Make sure our recv side is stopped.
+	 * Older tools called conn stop before ep_disconnect
+	 * so IO could still be coming in.
+	 */
+	write_lock_bh(&tcp_conn->sock->sk->sk_callback_lock);
+	set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_rx);
+	write_unlock_bh(&tcp_conn->sock->sk->sk_callback_lock);
 
 	iscsi_conn_stop(cls_conn, flag);
 	iscsi_tcp_release_conn(conn);
@@ -1623,6 +1640,8 @@
 		    struct iscsi_cls_conn *cls_conn, uint64_t transport_eph,
 		    int is_leading)
 {
+	struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
+	struct iscsi_host *ihost = shost_priv(shost);
 	struct iscsi_conn *conn = cls_conn->dd_data;
 	struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
 	struct sock *sk;
@@ -1646,8 +1665,8 @@
 	if (err)
 		goto free_socket;
 
-	err = iscsi_tcp_get_addr(conn, sock, conn->local_address,
-				&conn->local_port, kernel_getsockname);
+	err = iscsi_tcp_get_addr(conn, sock, ihost->local_address,
+				&ihost->local_port, kernel_getsockname);
 	if (err)
 		goto free_socket;
 
@@ -1664,13 +1683,6 @@
 	sk->sk_sndtimeo = 15 * HZ; /* FIXME: make it configurable */
 	sk->sk_allocation = GFP_ATOMIC;
 
-	/* FIXME: disable Nagle's algorithm */
-
-	/*
-	 * Intercept TCP callbacks for sendfile like receive
-	 * processing.
-	 */
-	conn->recv_lock = &sk->sk_callback_lock;
 	iscsi_conn_set_callbacks(conn);
 	tcp_conn->sendpage = tcp_conn->sock->ops->sendpage;
 	/*
@@ -1684,21 +1696,6 @@
 	return err;
 }
 
-/* called with host lock */
-static void
-iscsi_tcp_mtask_init(struct iscsi_conn *conn, struct iscsi_mgmt_task *mtask)
-{
-	debug_scsi("mtask deq [cid %d itt 0x%x]\n", conn->id, mtask->itt);
-
-	/* Prepare PDU, optionally w/ immediate data */
-	iscsi_tcp_send_hdr_prep(conn, mtask->hdr, sizeof(*mtask->hdr));
-
-	/* If we have immediate data, attach a payload */
-	if (mtask->data_count)
-		iscsi_tcp_send_linear_data_prepare(conn, mtask->data,
-						   mtask->data_count);
-}
-
 static int
 iscsi_r2tpool_alloc(struct iscsi_session *session)
 {
@@ -1709,8 +1706,8 @@
 	 * initialize per-task: R2T pool and xmit queue
 	 */
 	for (cmd_i = 0; cmd_i < session->cmds_max; cmd_i++) {
-	        struct iscsi_cmd_task *ctask = session->cmds[cmd_i];
-		struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
+	        struct iscsi_task *task = session->cmds[cmd_i];
+		struct iscsi_tcp_task *tcp_task = task->dd_data;
 
 		/*
 		 * pre-allocated x4 as much r2ts to handle race when
@@ -1719,16 +1716,16 @@
 		 */
 
 		/* R2T pool */
-		if (iscsi_pool_init(&tcp_ctask->r2tpool, session->max_r2t * 4, NULL,
+		if (iscsi_pool_init(&tcp_task->r2tpool, session->max_r2t * 4, NULL,
 				    sizeof(struct iscsi_r2t_info))) {
 			goto r2t_alloc_fail;
 		}
 
 		/* R2T xmit queue */
-		tcp_ctask->r2tqueue = kfifo_alloc(
+		tcp_task->r2tqueue = kfifo_alloc(
 		      session->max_r2t * 4 * sizeof(void*), GFP_KERNEL, NULL);
-		if (tcp_ctask->r2tqueue == ERR_PTR(-ENOMEM)) {
-			iscsi_pool_free(&tcp_ctask->r2tpool);
+		if (tcp_task->r2tqueue == ERR_PTR(-ENOMEM)) {
+			iscsi_pool_free(&tcp_task->r2tpool);
 			goto r2t_alloc_fail;
 		}
 	}
@@ -1737,11 +1734,11 @@
 
 r2t_alloc_fail:
 	for (i = 0; i < cmd_i; i++) {
-		struct iscsi_cmd_task *ctask = session->cmds[i];
-		struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
+		struct iscsi_task *task = session->cmds[i];
+		struct iscsi_tcp_task *tcp_task = task->dd_data;
 
-		kfifo_free(tcp_ctask->r2tqueue);
-		iscsi_pool_free(&tcp_ctask->r2tpool);
+		kfifo_free(tcp_task->r2tqueue);
+		iscsi_pool_free(&tcp_task->r2tpool);
 	}
 	return -ENOMEM;
 }
@@ -1752,11 +1749,11 @@
 	int i;
 
 	for (i = 0; i < session->cmds_max; i++) {
-		struct iscsi_cmd_task *ctask = session->cmds[i];
-		struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
+		struct iscsi_task *task = session->cmds[i];
+		struct iscsi_tcp_task *tcp_task = task->dd_data;
 
-		kfifo_free(tcp_ctask->r2tqueue);
-		iscsi_pool_free(&tcp_ctask->r2tpool);
+		kfifo_free(tcp_task->r2tqueue);
+		iscsi_pool_free(&tcp_task->r2tpool);
 	}
 }
 
@@ -1821,29 +1818,6 @@
 	return len;
 }
 
-static int
-iscsi_tcp_host_get_param(struct Scsi_Host *shost, enum iscsi_host_param param,
-			 char *buf)
-{
-        struct iscsi_session *session = iscsi_hostdata(shost->hostdata);
-	int len;
-
-	switch (param) {
-	case ISCSI_HOST_PARAM_IPADDRESS:
-		spin_lock_bh(&session->lock);
-		if (!session->leadconn)
-			len = -ENODEV;
-		else
-			len = sprintf(buf, "%s\n",
-				     session->leadconn->local_address);
-		spin_unlock_bh(&session->lock);
-		break;
-	default:
-		return iscsi_host_get_param(shost, param, buf);
-	}
-	return len;
-}
-
 static void
 iscsi_conn_get_stats(struct iscsi_cls_conn *cls_conn, struct iscsi_stats *stats)
 {
@@ -1869,54 +1843,70 @@
 }
 
 static struct iscsi_cls_session *
-iscsi_tcp_session_create(struct iscsi_transport *iscsit,
-			 struct scsi_transport_template *scsit,
-			 uint16_t cmds_max, uint16_t qdepth,
-			 uint32_t initial_cmdsn, uint32_t *hostno)
+iscsi_tcp_session_create(struct iscsi_endpoint *ep, uint16_t cmds_max,
+			 uint16_t qdepth, uint32_t initial_cmdsn,
+			 uint32_t *hostno)
 {
 	struct iscsi_cls_session *cls_session;
 	struct iscsi_session *session;
-	uint32_t hn;
+	struct Scsi_Host *shost;
 	int cmd_i;
 
-	cls_session = iscsi_session_setup(iscsit, scsit, cmds_max, qdepth,
-					 sizeof(struct iscsi_tcp_cmd_task),
-					 sizeof(struct iscsi_tcp_mgmt_task),
-					 initial_cmdsn, &hn);
-	if (!cls_session)
+	if (ep) {
+		printk(KERN_ERR "iscsi_tcp: invalid ep %p.\n", ep);
 		return NULL;
-	*hostno = hn;
+	}
 
-	session = class_to_transport_session(cls_session);
+	shost = iscsi_host_alloc(&iscsi_sht, 0, qdepth);
+	if (!shost)
+		return NULL;
+	shost->transportt = iscsi_tcp_scsi_transport;
+	shost->max_lun = iscsi_max_lun;
+	shost->max_id = 0;
+	shost->max_channel = 0;
+	shost->max_cmd_len = SCSI_MAX_VARLEN_CDB_SIZE;
+
+	if (iscsi_host_add(shost, NULL))
+		goto free_host;
+	*hostno = shost->host_no;
+
+	cls_session = iscsi_session_setup(&iscsi_tcp_transport, shost, cmds_max,
+					  sizeof(struct iscsi_tcp_task),
+					  initial_cmdsn, 0);
+	if (!cls_session)
+		goto remove_host;
+	session = cls_session->dd_data;
+
+	shost->can_queue = session->scsi_cmds_max;
 	for (cmd_i = 0; cmd_i < session->cmds_max; cmd_i++) {
-		struct iscsi_cmd_task *ctask = session->cmds[cmd_i];
-		struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
+		struct iscsi_task *task = session->cmds[cmd_i];
+		struct iscsi_tcp_task *tcp_task = task->dd_data;
 
-		ctask->hdr = &tcp_ctask->hdr.cmd_hdr;
-		ctask->hdr_max = sizeof(tcp_ctask->hdr) - ISCSI_DIGEST_SIZE;
+		task->hdr = &tcp_task->hdr.cmd_hdr;
+		task->hdr_max = sizeof(tcp_task->hdr) - ISCSI_DIGEST_SIZE;
 	}
 
-	for (cmd_i = 0; cmd_i < session->mgmtpool_max; cmd_i++) {
-		struct iscsi_mgmt_task *mtask = session->mgmt_cmds[cmd_i];
-		struct iscsi_tcp_mgmt_task *tcp_mtask = mtask->dd_data;
-
-		mtask->hdr = (struct iscsi_hdr *) &tcp_mtask->hdr;
-	}
-
-	if (iscsi_r2tpool_alloc(class_to_transport_session(cls_session)))
-		goto r2tpool_alloc_fail;
-
+	if (iscsi_r2tpool_alloc(session))
+		goto remove_session;
 	return cls_session;
 
-r2tpool_alloc_fail:
+remove_session:
 	iscsi_session_teardown(cls_session);
+remove_host:
+	iscsi_host_remove(shost);
+free_host:
+	iscsi_host_free(shost);
 	return NULL;
 }
 
 static void iscsi_tcp_session_destroy(struct iscsi_cls_session *cls_session)
 {
-	iscsi_r2tpool_free(class_to_transport_session(cls_session));
-	iscsi_session_teardown(cls_session);
+	struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
+
+	iscsi_r2tpool_free(cls_session->dd_data);
+
+	iscsi_host_remove(shost);
+	iscsi_host_free(shost);
 }
 
 static int iscsi_tcp_slave_configure(struct scsi_device *sdev)
@@ -1971,14 +1961,11 @@
 				  ISCSI_USERNAME_IN | ISCSI_PASSWORD_IN |
 				  ISCSI_FAST_ABORT | ISCSI_ABORT_TMO |
 				  ISCSI_LU_RESET_TMO |
-				  ISCSI_PING_TMO | ISCSI_RECV_TMO,
+				  ISCSI_PING_TMO | ISCSI_RECV_TMO |
+				  ISCSI_IFACE_NAME | ISCSI_INITIATOR_NAME,
 	.host_param_mask	= ISCSI_HOST_HWADDRESS | ISCSI_HOST_IPADDRESS |
 				  ISCSI_HOST_INITIATOR_NAME |
 				  ISCSI_HOST_NETDEV_NAME,
-	.host_template		= &iscsi_sht,
-	.conndata_size		= sizeof(struct iscsi_conn),
-	.max_conn		= 1,
-	.max_cmd_len		= 16,
 	/* session management */
 	.create_session		= iscsi_tcp_session_create,
 	.destroy_session	= iscsi_tcp_session_destroy,
@@ -1992,16 +1979,14 @@
 	.start_conn		= iscsi_conn_start,
 	.stop_conn		= iscsi_tcp_conn_stop,
 	/* iscsi host params */
-	.get_host_param		= iscsi_tcp_host_get_param,
+	.get_host_param		= iscsi_host_get_param,
 	.set_host_param		= iscsi_host_set_param,
 	/* IO */
 	.send_pdu		= iscsi_conn_send_pdu,
 	.get_stats		= iscsi_conn_get_stats,
-	.init_cmd_task		= iscsi_tcp_ctask_init,
-	.init_mgmt_task		= iscsi_tcp_mtask_init,
-	.xmit_cmd_task		= iscsi_tcp_ctask_xmit,
-	.xmit_mgmt_task		= iscsi_tcp_mtask_xmit,
-	.cleanup_cmd_task	= iscsi_tcp_cleanup_ctask,
+	.init_task		= iscsi_tcp_task_init,
+	.xmit_task		= iscsi_tcp_task_xmit,
+	.cleanup_task		= iscsi_tcp_cleanup_task,
 	/* recovery */
 	.session_recovery_timedout = iscsi_session_recovery_timedout,
 };
@@ -2014,9 +1999,10 @@
 		       iscsi_max_lun);
 		return -EINVAL;
 	}
-	iscsi_tcp_transport.max_lun = iscsi_max_lun;
 
-	if (!iscsi_register_transport(&iscsi_tcp_transport))
+	iscsi_tcp_scsi_transport = iscsi_register_transport(
+							&iscsi_tcp_transport);
+	if (!iscsi_tcp_scsi_transport)
 		return -ENODEV;
 
 	return 0;
diff --git a/drivers/scsi/iscsi_tcp.h b/drivers/scsi/iscsi_tcp.h
index ed0b991..498d8ca 100644
--- a/drivers/scsi/iscsi_tcp.h
+++ b/drivers/scsi/iscsi_tcp.h
@@ -103,11 +103,6 @@
 	char			hdrext[ISCSI_DIGEST_SIZE];/* Header-Digest */
 };
 
-struct iscsi_tcp_mgmt_task {
-	struct iscsi_hdr	hdr;
-	char			hdrext[ISCSI_DIGEST_SIZE]; /* Header-Digest */
-};
-
 struct iscsi_r2t_info {
 	__be32			ttt;		/* copied from R2T */
 	__be32			exp_statsn;	/* copied from R2T */
@@ -119,7 +114,7 @@
 	struct iscsi_data_task	dtask;		/* Data-Out header buf */
 };
 
-struct iscsi_tcp_cmd_task {
+struct iscsi_tcp_task {
 	struct iscsi_hdr_buff {
 		struct iscsi_cmd	cmd_hdr;
 		char			hdrextbuf[ISCSI_MAX_AHS_SIZE +
diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c
index b43bf1d..299e075 100644
--- a/drivers/scsi/libiscsi.c
+++ b/drivers/scsi/libiscsi.c
@@ -38,14 +38,6 @@
 #include <scsi/scsi_transport_iscsi.h>
 #include <scsi/libiscsi.h>
 
-struct iscsi_session *
-class_to_transport_session(struct iscsi_cls_session *cls_session)
-{
-	struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
-	return iscsi_hostdata(shost->hostdata);
-}
-EXPORT_SYMBOL_GPL(class_to_transport_session);
-
 /* Serial Number Arithmetic, 32 bits, less than, RFC1982 */
 #define SNA32_CHECK 2147483648UL
 
@@ -87,68 +79,70 @@
 		 * xmit thread
 		 */
 		if (!list_empty(&session->leadconn->xmitqueue) ||
-		    !list_empty(&session->leadconn->mgmtqueue))
-			scsi_queue_work(session->host,
-					&session->leadconn->xmitwork);
+		    !list_empty(&session->leadconn->mgmtqueue)) {
+			if (!(session->tt->caps & CAP_DATA_PATH_OFFLOAD))
+				scsi_queue_work(session->host,
+						&session->leadconn->xmitwork);
+		}
 	}
 }
 EXPORT_SYMBOL_GPL(iscsi_update_cmdsn);
 
-void iscsi_prep_unsolicit_data_pdu(struct iscsi_cmd_task *ctask,
+void iscsi_prep_unsolicit_data_pdu(struct iscsi_task *task,
 				   struct iscsi_data *hdr)
 {
-	struct iscsi_conn *conn = ctask->conn;
+	struct iscsi_conn *conn = task->conn;
 
 	memset(hdr, 0, sizeof(struct iscsi_data));
 	hdr->ttt = cpu_to_be32(ISCSI_RESERVED_TAG);
-	hdr->datasn = cpu_to_be32(ctask->unsol_datasn);
-	ctask->unsol_datasn++;
+	hdr->datasn = cpu_to_be32(task->unsol_datasn);
+	task->unsol_datasn++;
 	hdr->opcode = ISCSI_OP_SCSI_DATA_OUT;
-	memcpy(hdr->lun, ctask->hdr->lun, sizeof(hdr->lun));
+	memcpy(hdr->lun, task->hdr->lun, sizeof(hdr->lun));
 
-	hdr->itt = ctask->hdr->itt;
+	hdr->itt = task->hdr->itt;
 	hdr->exp_statsn = cpu_to_be32(conn->exp_statsn);
-	hdr->offset = cpu_to_be32(ctask->unsol_offset);
+	hdr->offset = cpu_to_be32(task->unsol_offset);
 
-	if (ctask->unsol_count > conn->max_xmit_dlength) {
+	if (task->unsol_count > conn->max_xmit_dlength) {
 		hton24(hdr->dlength, conn->max_xmit_dlength);
-		ctask->data_count = conn->max_xmit_dlength;
-		ctask->unsol_offset += ctask->data_count;
+		task->data_count = conn->max_xmit_dlength;
+		task->unsol_offset += task->data_count;
 		hdr->flags = 0;
 	} else {
-		hton24(hdr->dlength, ctask->unsol_count);
-		ctask->data_count = ctask->unsol_count;
+		hton24(hdr->dlength, task->unsol_count);
+		task->data_count = task->unsol_count;
 		hdr->flags = ISCSI_FLAG_CMD_FINAL;
 	}
 }
 EXPORT_SYMBOL_GPL(iscsi_prep_unsolicit_data_pdu);
 
-static int iscsi_add_hdr(struct iscsi_cmd_task *ctask, unsigned len)
+static int iscsi_add_hdr(struct iscsi_task *task, unsigned len)
 {
-	unsigned exp_len = ctask->hdr_len + len;
+	unsigned exp_len = task->hdr_len + len;
 
-	if (exp_len > ctask->hdr_max) {
+	if (exp_len > task->hdr_max) {
 		WARN_ON(1);
 		return -EINVAL;
 	}
 
 	WARN_ON(len & (ISCSI_PAD_LEN - 1)); /* caller must pad the AHS */
-	ctask->hdr_len = exp_len;
+	task->hdr_len = exp_len;
 	return 0;
 }
 
 /*
  * make an extended cdb AHS
  */
-static int iscsi_prep_ecdb_ahs(struct iscsi_cmd_task *ctask)
+static int iscsi_prep_ecdb_ahs(struct iscsi_task *task)
 {
-	struct scsi_cmnd *cmd = ctask->sc;
+	struct scsi_cmnd *cmd = task->sc;
 	unsigned rlen, pad_len;
 	unsigned short ahslength;
 	struct iscsi_ecdb_ahdr *ecdb_ahdr;
 	int rc;
 
-	ecdb_ahdr = iscsi_next_hdr(ctask);
+	ecdb_ahdr = iscsi_next_hdr(task);
 	rlen = cmd->cmd_len - ISCSI_CDB_SIZE;
 
 	BUG_ON(rlen > sizeof(ecdb_ahdr->ecdb));
@@ -156,7 +150,7 @@
 
 	pad_len = iscsi_padding(rlen);
 
-	rc = iscsi_add_hdr(ctask, sizeof(ecdb_ahdr->ahslength) +
+	rc = iscsi_add_hdr(task, sizeof(ecdb_ahdr->ahslength) +
 	                   sizeof(ecdb_ahdr->ahstype) + ahslength + pad_len);
 	if (rc)
 		return rc;
@@ -171,19 +165,19 @@
 
 	debug_scsi("iscsi_prep_ecdb_ahs: varlen_cdb_len %d "
 		   "rlen %d pad_len %d ahs_length %d iscsi_headers_size %u\n",
-		   cmd->cmd_len, rlen, pad_len, ahslength, ctask->hdr_len);
+		   cmd->cmd_len, rlen, pad_len, ahslength, task->hdr_len);
 
 	return 0;
 }
 
-static int iscsi_prep_bidi_ahs(struct iscsi_cmd_task *ctask)
+static int iscsi_prep_bidi_ahs(struct iscsi_task *task)
 {
-	struct scsi_cmnd *sc = ctask->sc;
+	struct scsi_cmnd *sc = task->sc;
 	struct iscsi_rlength_ahdr *rlen_ahdr;
 	int rc;
 
-	rlen_ahdr = iscsi_next_hdr(ctask);
-	rc = iscsi_add_hdr(ctask, sizeof(*rlen_ahdr));
+	rlen_ahdr = iscsi_next_hdr(task);
+	rc = iscsi_add_hdr(task, sizeof(*rlen_ahdr));
 	if (rc)
 		return rc;
 
@@ -203,28 +197,28 @@
 
 /**
  * iscsi_prep_scsi_cmd_pdu - prep iscsi scsi cmd pdu
- * @ctask: iscsi cmd task
+ * @task: iscsi task
  *
  * Prep basic iSCSI PDU fields for a scsi cmd pdu. The LLD should set
  * fields like dlength or final based on how much data it sends
  */
-static int iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask)
+static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task)
 {
-	struct iscsi_conn *conn = ctask->conn;
+	struct iscsi_conn *conn = task->conn;
 	struct iscsi_session *session = conn->session;
-	struct iscsi_cmd *hdr = ctask->hdr;
-	struct scsi_cmnd *sc = ctask->sc;
+	struct iscsi_cmd *hdr = task->hdr;
+	struct scsi_cmnd *sc = task->sc;
 	unsigned hdrlength, cmd_len;
 	int rc;
 
-	ctask->hdr_len = 0;
-	rc = iscsi_add_hdr(ctask, sizeof(*hdr));
+	task->hdr_len = 0;
+	rc = iscsi_add_hdr(task, sizeof(*hdr));
 	if (rc)
 		return rc;
 	hdr->opcode = ISCSI_OP_SCSI_CMD;
 	hdr->flags = ISCSI_ATTR_SIMPLE;
 	int_to_scsilun(sc->device->lun, (struct scsi_lun *)hdr->lun);
-	hdr->itt = build_itt(ctask->itt, session->age);
+	hdr->itt = build_itt(task->itt, session->age);
 	hdr->cmdsn = cpu_to_be32(session->cmdsn);
 	session->cmdsn++;
 	hdr->exp_statsn = cpu_to_be32(conn->exp_statsn);
@@ -232,17 +226,17 @@
 	if (cmd_len < ISCSI_CDB_SIZE)
 		memset(&hdr->cdb[cmd_len], 0, ISCSI_CDB_SIZE - cmd_len);
 	else if (cmd_len > ISCSI_CDB_SIZE) {
-		rc = iscsi_prep_ecdb_ahs(ctask);
+		rc = iscsi_prep_ecdb_ahs(task);
 		if (rc)
 			return rc;
 		cmd_len = ISCSI_CDB_SIZE;
 	}
 	memcpy(hdr->cdb, sc->cmnd, cmd_len);
 
-	ctask->imm_count = 0;
+	task->imm_count = 0;
 	if (scsi_bidi_cmnd(sc)) {
 		hdr->flags |= ISCSI_FLAG_CMD_READ;
-		rc = iscsi_prep_bidi_ahs(ctask);
+		rc = iscsi_prep_bidi_ahs(task);
 		if (rc)
 			return rc;
 	}
@@ -264,28 +258,28 @@
 		 *
 		 *      pad_count       bytes to be sent as zero-padding
 		 */
-		ctask->unsol_count = 0;
-		ctask->unsol_offset = 0;
-		ctask->unsol_datasn = 0;
+		task->unsol_count = 0;
+		task->unsol_offset = 0;
+		task->unsol_datasn = 0;
 
 		if (session->imm_data_en) {
 			if (out_len >= session->first_burst)
-				ctask->imm_count = min(session->first_burst,
+				task->imm_count = min(session->first_burst,
 							conn->max_xmit_dlength);
 			else
-				ctask->imm_count = min(out_len,
+				task->imm_count = min(out_len,
 							conn->max_xmit_dlength);
-			hton24(hdr->dlength, ctask->imm_count);
+			hton24(hdr->dlength, task->imm_count);
 		} else
 			zero_data(hdr->dlength);
 
 		if (!session->initial_r2t_en) {
-			ctask->unsol_count = min(session->first_burst, out_len)
-							     - ctask->imm_count;
-			ctask->unsol_offset = ctask->imm_count;
+			task->unsol_count = min(session->first_burst, out_len)
+							     - task->imm_count;
+			task->unsol_offset = task->imm_count;
 		}
 
-		if (!ctask->unsol_count)
+		if (!task->unsol_count)
 			/* No unsolicit Data-Out's */
 			hdr->flags |= ISCSI_FLAG_CMD_FINAL;
 	} else {
@@ -298,7 +292,7 @@
 	}
 
 	/* calculate size of additional header segments (AHSs) */
-	hdrlength = ctask->hdr_len - sizeof(*hdr);
+	hdrlength = task->hdr_len - sizeof(*hdr);
 
 	WARN_ON(hdrlength & (ISCSI_PAD_LEN-1));
 	hdrlength /= ISCSI_PAD_LEN;
@@ -306,76 +300,115 @@
 	WARN_ON(hdrlength >= 256);
 	hdr->hlength = hdrlength & 0xFF;
 
-	if (conn->session->tt->init_cmd_task(conn->ctask))
-		return EIO;
+	if (conn->session->tt->init_task &&
+	    conn->session->tt->init_task(task))
+		return -EIO;
+
+	task->state = ISCSI_TASK_RUNNING;
+	list_move_tail(&task->running, &conn->run_list);
 
 	conn->scsicmd_pdus_cnt++;
-	debug_scsi("iscsi prep [%s cid %d sc %p cdb 0x%x itt 0x%x "
-		"len %d bidi_len %d cmdsn %d win %d]\n",
-		scsi_bidi_cmnd(sc) ? "bidirectional" :
-		     sc->sc_data_direction == DMA_TO_DEVICE ? "write" : "read",
-		conn->id, sc, sc->cmnd[0], ctask->itt,
-		scsi_bufflen(sc), scsi_bidi_cmnd(sc) ? scsi_in(sc)->length : 0,
-		session->cmdsn, session->max_cmdsn - session->exp_cmdsn + 1);
+	debug_scsi("iscsi prep [%s cid %d sc %p cdb 0x%x itt 0x%x len %d "
+		   "bidi_len %d cmdsn %d win %d]\n", scsi_bidi_cmnd(sc) ?
+		   "bidirectional" : sc->sc_data_direction == DMA_TO_DEVICE ?
+		   "write" : "read", conn->id, sc, sc->cmnd[0], task->itt,
+		   scsi_bufflen(sc),
+		   scsi_bidi_cmnd(sc) ? scsi_in(sc)->length : 0,
+		   session->cmdsn, session->max_cmdsn - session->exp_cmdsn + 1);
 	return 0;
 }
 
 /**
- * iscsi_complete_command - return command back to scsi-ml
- * @ctask: iscsi cmd task
+ * iscsi_complete_command - finish a task
+ * @task: iscsi cmd task
  *
  * Must be called with session lock.
- * This function returns the scsi command to scsi-ml and returns
- * the cmd task to the pool of available cmd tasks.
+ * This function returns the scsi command to scsi-ml or cleans
+ * up mgmt tasks then returns the task to the pool.
  */
-static void iscsi_complete_command(struct iscsi_cmd_task *ctask)
+static void iscsi_complete_command(struct iscsi_task *task)
 {
-	struct iscsi_conn *conn = ctask->conn;
+	struct iscsi_conn *conn = task->conn;
 	struct iscsi_session *session = conn->session;
-	struct scsi_cmnd *sc = ctask->sc;
+	struct scsi_cmnd *sc = task->sc;
 
-	ctask->state = ISCSI_TASK_COMPLETED;
-	ctask->sc = NULL;
-	/* SCSI eh reuses commands to verify us */
-	sc->SCp.ptr = NULL;
-	if (conn->ctask == ctask)
-		conn->ctask = NULL;
-	list_del_init(&ctask->running);
-	__kfifo_put(session->cmdpool.queue, (void*)&ctask, sizeof(void*));
-	sc->scsi_done(sc);
+	list_del_init(&task->running);
+	task->state = ISCSI_TASK_COMPLETED;
+	task->sc = NULL;
+
+	if (conn->task == task)
+		conn->task = NULL;
+	/*
+	 * login task is preallocated so do not free
+	 */
+	if (conn->login_task == task)
+		return;
+
+	__kfifo_put(session->cmdpool.queue, (void*)&task, sizeof(void*));
+
+	if (conn->ping_task == task)
+		conn->ping_task = NULL;
+
+	if (sc) {
+		task->sc = NULL;
+		/* SCSI eh reuses commands to verify us */
+		sc->SCp.ptr = NULL;
+		/*
+		 * queue command may call this to free the task, but
+		 * not have setup the sc callback
+		 */
+		if (sc->scsi_done)
+			sc->scsi_done(sc);
+	}
 }
 
-static void __iscsi_get_ctask(struct iscsi_cmd_task *ctask)
+void __iscsi_get_task(struct iscsi_task *task)
 {
-	atomic_inc(&ctask->refcount);
+	atomic_inc(&task->refcount);
+}
+EXPORT_SYMBOL_GPL(__iscsi_get_task);
+
+static void __iscsi_put_task(struct iscsi_task *task)
+{
+	if (atomic_dec_and_test(&task->refcount))
+		iscsi_complete_command(task);
 }
 
-static void __iscsi_put_ctask(struct iscsi_cmd_task *ctask)
+void iscsi_put_task(struct iscsi_task *task)
 {
-	if (atomic_dec_and_test(&ctask->refcount))
-		iscsi_complete_command(ctask);
+	struct iscsi_session *session = task->conn->session;
+
+	spin_lock_bh(&session->lock);
+	__iscsi_put_task(task);
+	spin_unlock_bh(&session->lock);
 }
+EXPORT_SYMBOL_GPL(iscsi_put_task);
 
 /*
  * session lock must be held
  */
-static void fail_command(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask,
+static void fail_command(struct iscsi_conn *conn, struct iscsi_task *task,
 			 int err)
 {
 	struct scsi_cmnd *sc;
 
-	sc = ctask->sc;
+	sc = task->sc;
 	if (!sc)
 		return;
 
-	if (ctask->state == ISCSI_TASK_PENDING)
+	if (task->state == ISCSI_TASK_PENDING)
 		/*
 		 * cmd never made it to the xmit thread, so we should not count
 		 * the cmd in the sequencing
 		 */
 		conn->session->queued_cmdsn--;
 	else
-		conn->session->tt->cleanup_cmd_task(conn, ctask);
+		conn->session->tt->cleanup_task(conn, task);
+	/*
+	 * Check if cleanup_task dropped the lock and the command completed,
+	 */
+	if (!task->sc)
+		return;
 
 	sc->result = err;
 	if (!scsi_bidi_cmnd(sc))
@@ -384,39 +417,63 @@
 		scsi_out(sc)->resid = scsi_out(sc)->length;
 		scsi_in(sc)->resid = scsi_in(sc)->length;
 	}
-	if (conn->ctask == ctask)
-		conn->ctask = NULL;
+
+	if (conn->task == task)
+		conn->task = NULL;
 	/* release ref from queuecommand */
-	__iscsi_put_ctask(ctask);
+	__iscsi_put_task(task);
 }
 
-/**
- * iscsi_free_mgmt_task - return mgmt task back to pool
- * @conn: iscsi connection
- * @mtask: mtask
- *
- * Must be called with session lock.
- */
-void iscsi_free_mgmt_task(struct iscsi_conn *conn,
-			  struct iscsi_mgmt_task *mtask)
+static int iscsi_prep_mgmt_task(struct iscsi_conn *conn,
+				struct iscsi_task *task)
 {
-	list_del_init(&mtask->running);
-	if (conn->login_mtask == mtask)
-		return;
+	struct iscsi_session *session = conn->session;
+	struct iscsi_hdr *hdr = (struct iscsi_hdr *)task->hdr;
+	struct iscsi_nopout *nop = (struct iscsi_nopout *)hdr;
 
-	if (conn->ping_mtask == mtask)
-		conn->ping_mtask = NULL;
-	__kfifo_put(conn->session->mgmtpool.queue,
-		    (void*)&mtask, sizeof(void*));
+	if (conn->session->state == ISCSI_STATE_LOGGING_OUT)
+		return -ENOTCONN;
+
+	if (hdr->opcode != (ISCSI_OP_LOGIN | ISCSI_OP_IMMEDIATE) &&
+	    hdr->opcode != (ISCSI_OP_TEXT | ISCSI_OP_IMMEDIATE))
+		nop->exp_statsn = cpu_to_be32(conn->exp_statsn);
+	/*
+	 * pre-format CmdSN for outgoing PDU.
+	 */
+	nop->cmdsn = cpu_to_be32(session->cmdsn);
+	if (hdr->itt != RESERVED_ITT) {
+		hdr->itt = build_itt(task->itt, session->age);
+		/*
+		 * TODO: We always use immediate, so we never hit this.
+		 * If we start to send tmfs or nops as non-immediate then
+		 * we should start checking the cmdsn numbers for mgmt tasks.
+		 */
+		if (conn->c_stage == ISCSI_CONN_STARTED &&
+		    !(hdr->opcode & ISCSI_OP_IMMEDIATE)) {
+			session->queued_cmdsn++;
+			session->cmdsn++;
+		}
+	}
+
+	if (session->tt->init_task)
+		session->tt->init_task(task);
+
+	if ((hdr->opcode & ISCSI_OPCODE_MASK) == ISCSI_OP_LOGOUT)
+		session->state = ISCSI_STATE_LOGGING_OUT;
+
+	list_move_tail(&task->running, &conn->mgmt_run_list);
+	debug_scsi("mgmtpdu [op 0x%x hdr->itt 0x%x datalen %d]\n",
+		   hdr->opcode & ISCSI_OPCODE_MASK, hdr->itt,
+		   task->data_count);
+	return 0;
 }
-EXPORT_SYMBOL_GPL(iscsi_free_mgmt_task);
 
-static struct iscsi_mgmt_task *
+static struct iscsi_task *
 __iscsi_conn_send_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
 		      char *data, uint32_t data_size)
 {
 	struct iscsi_session *session = conn->session;
-	struct iscsi_mgmt_task *mtask;
+	struct iscsi_task *task;
 
 	if (session->state == ISCSI_STATE_TERMINATE)
 		return NULL;
@@ -426,29 +483,56 @@
 		/*
 		 * Login and Text are sent serially, in
 		 * request-followed-by-response sequence.
-		 * Same mtask can be used. Same ITT must be used.
-		 * Note that login_mtask is preallocated at conn_create().
+		 * Same task can be used. Same ITT must be used.
+		 * Note that login_task is preallocated at conn_create().
 		 */
-		mtask = conn->login_mtask;
+		task = conn->login_task;
 	else {
 		BUG_ON(conn->c_stage == ISCSI_CONN_INITIAL_STAGE);
 		BUG_ON(conn->c_stage == ISCSI_CONN_STOPPED);
 
-		if (!__kfifo_get(session->mgmtpool.queue,
-				 (void*)&mtask, sizeof(void*)))
+		if (!__kfifo_get(session->cmdpool.queue,
+				 (void*)&task, sizeof(void*)))
 			return NULL;
+
+		if ((hdr->opcode == (ISCSI_OP_NOOP_OUT | ISCSI_OP_IMMEDIATE)) &&
+		     hdr->ttt == RESERVED_ITT) {
+			conn->ping_task = task;
+			conn->last_ping = jiffies;
+		}
 	}
+	/*
+	 * released in complete pdu for task we expect a response for, and
+	 * released by the lld when it has transmitted the task for
+	 * pdus we do not expect a response for.
+	 */
+	atomic_set(&task->refcount, 1);
+	task->conn = conn;
+	task->sc = NULL;
 
 	if (data_size) {
-		memcpy(mtask->data, data, data_size);
-		mtask->data_count = data_size;
+		memcpy(task->data, data, data_size);
+		task->data_count = data_size;
 	} else
-		mtask->data_count = 0;
+		task->data_count = 0;
 
-	memcpy(mtask->hdr, hdr, sizeof(struct iscsi_hdr));
-	INIT_LIST_HEAD(&mtask->running);
-	list_add_tail(&mtask->running, &conn->mgmtqueue);
-	return mtask;
+	memcpy(task->hdr, hdr, sizeof(struct iscsi_hdr));
+	INIT_LIST_HEAD(&task->running);
+	list_add_tail(&task->running, &conn->mgmtqueue);
+
+	if (session->tt->caps & CAP_DATA_PATH_OFFLOAD) {
+		if (iscsi_prep_mgmt_task(conn, task)) {
+			__iscsi_put_task(task);
+			return NULL;
+		}
+
+		if (session->tt->xmit_task(task))
+			task = NULL;
+
+	} else
+		scsi_queue_work(conn->session->host, &conn->xmitwork);
+
+	return task;
 }
 
 int iscsi_conn_send_pdu(struct iscsi_cls_conn *cls_conn, struct iscsi_hdr *hdr,
@@ -462,7 +546,6 @@
 	if (!__iscsi_conn_send_pdu(conn, hdr, data, data_size))
 		err = -EPERM;
 	spin_unlock_bh(&session->lock);
-	scsi_queue_work(session->host, &conn->xmitwork);
 	return err;
 }
 EXPORT_SYMBOL_GPL(iscsi_conn_send_pdu);
@@ -471,7 +554,7 @@
  * iscsi_cmd_rsp - SCSI Command Response processing
  * @conn: iscsi connection
  * @hdr: iscsi header
- * @ctask: scsi command task
+ * @task: scsi command task
  * @data: cmd data buffer
  * @datalen: len of buffer
  *
@@ -479,12 +562,12 @@
  * then completes the command and task.
  **/
 static void iscsi_scsi_cmd_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
-			       struct iscsi_cmd_task *ctask, char *data,
+			       struct iscsi_task *task, char *data,
 			       int datalen)
 {
 	struct iscsi_cmd_rsp *rhdr = (struct iscsi_cmd_rsp *)hdr;
 	struct iscsi_session *session = conn->session;
-	struct scsi_cmnd *sc = ctask->sc;
+	struct scsi_cmnd *sc = task->sc;
 
 	iscsi_update_cmdsn(session, (struct iscsi_nopin*)rhdr);
 	conn->exp_statsn = be32_to_cpu(rhdr->statsn) + 1;
@@ -508,7 +591,7 @@
 			goto out;
 		}
 
-		senselen = be16_to_cpu(get_unaligned((__be16 *) data));
+		senselen = get_unaligned_be16(data);
 		if (datalen < senselen)
 			goto invalid_datalen;
 
@@ -544,10 +627,10 @@
 	}
 out:
 	debug_scsi("done [sc %lx res %d itt 0x%x]\n",
-		   (long)sc, sc->result, ctask->itt);
+		   (long)sc, sc->result, task->itt);
 	conn->scsirsp_pdus_cnt++;
 
-	__iscsi_put_ctask(ctask);
+	__iscsi_put_task(task);
 }
 
 static void iscsi_tmf_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
@@ -572,9 +655,9 @@
 static void iscsi_send_nopout(struct iscsi_conn *conn, struct iscsi_nopin *rhdr)
 {
         struct iscsi_nopout hdr;
-	struct iscsi_mgmt_task *mtask;
+	struct iscsi_task *task;
 
-	if (!rhdr && conn->ping_mtask)
+	if (!rhdr && conn->ping_task)
 		return;
 
 	memset(&hdr, 0, sizeof(struct iscsi_nopout));
@@ -588,18 +671,9 @@
 	} else
 		hdr.ttt = RESERVED_ITT;
 
-	mtask = __iscsi_conn_send_pdu(conn, (struct iscsi_hdr *)&hdr, NULL, 0);
-	if (!mtask) {
+	task = __iscsi_conn_send_pdu(conn, (struct iscsi_hdr *)&hdr, NULL, 0);
+	if (!task)
 		iscsi_conn_printk(KERN_ERR, conn, "Could not send nopout\n");
-		return;
-	}
-
-	/* only track our nops */
-	if (!rhdr) {
-		conn->ping_mtask = mtask;
-		conn->last_ping = jiffies;
-	}
-	scsi_queue_work(conn->session->host, &conn->xmitwork);
 }
 
 static int iscsi_handle_reject(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
@@ -628,6 +702,31 @@
 }
 
 /**
+ * iscsi_itt_to_task - look up task by itt
+ * @conn: iscsi connection
+ * @itt: itt
+ *
+ * This should be used for mgmt tasks like login and nops, or if
+ * the LDD's itt space does not include the session age.
+ *
+ * The session lock must be held.
+ */
+static struct iscsi_task *iscsi_itt_to_task(struct iscsi_conn *conn, itt_t itt)
+{
+	struct iscsi_session *session = conn->session;
+	uint32_t i;
+
+	if (itt == RESERVED_ITT)
+		return NULL;
+
+	i = get_itt(itt);
+	if (i >= session->cmds_max)
+		return NULL;
+
+	return session->cmds[i];
+}
+
+/**
  * __iscsi_complete_pdu - complete pdu
  * @conn: iscsi conn
  * @hdr: iscsi header
@@ -638,108 +737,28 @@
  * queuecommand or send generic. session lock must be held and verify
  * itt must have been called.
  */
-static int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
-				char *data, int datalen)
+int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+			 char *data, int datalen)
 {
 	struct iscsi_session *session = conn->session;
 	int opcode = hdr->opcode & ISCSI_OPCODE_MASK, rc = 0;
-	struct iscsi_cmd_task *ctask;
-	struct iscsi_mgmt_task *mtask;
+	struct iscsi_task *task;
 	uint32_t itt;
 
 	conn->last_recv = jiffies;
+	rc = iscsi_verify_itt(conn, hdr->itt);
+	if (rc)
+		return rc;
+
 	if (hdr->itt != RESERVED_ITT)
 		itt = get_itt(hdr->itt);
 	else
 		itt = ~0U;
 
-	if (itt < session->cmds_max) {
-		ctask = session->cmds[itt];
+	debug_scsi("[op 0x%x cid %d itt 0x%x len %d]\n",
+		   opcode, conn->id, itt, datalen);
 
-		debug_scsi("cmdrsp [op 0x%x cid %d itt 0x%x len %d]\n",
-			   opcode, conn->id, ctask->itt, datalen);
-
-		switch(opcode) {
-		case ISCSI_OP_SCSI_CMD_RSP:
-			BUG_ON((void*)ctask != ctask->sc->SCp.ptr);
-			iscsi_scsi_cmd_rsp(conn, hdr, ctask, data,
-					   datalen);
-			break;
-		case ISCSI_OP_SCSI_DATA_IN:
-			BUG_ON((void*)ctask != ctask->sc->SCp.ptr);
-			if (hdr->flags & ISCSI_FLAG_DATA_STATUS) {
-				conn->scsirsp_pdus_cnt++;
-				__iscsi_put_ctask(ctask);
-			}
-			break;
-		case ISCSI_OP_R2T:
-			/* LLD handles this for now */
-			break;
-		default:
-			rc = ISCSI_ERR_BAD_OPCODE;
-			break;
-		}
-	} else if (itt >= ISCSI_MGMT_ITT_OFFSET &&
-		   itt < ISCSI_MGMT_ITT_OFFSET + session->mgmtpool_max) {
-		mtask = session->mgmt_cmds[itt - ISCSI_MGMT_ITT_OFFSET];
-
-		debug_scsi("immrsp [op 0x%x cid %d itt 0x%x len %d]\n",
-			   opcode, conn->id, mtask->itt, datalen);
-
-		iscsi_update_cmdsn(session, (struct iscsi_nopin*)hdr);
-		switch(opcode) {
-		case ISCSI_OP_LOGOUT_RSP:
-			if (datalen) {
-				rc = ISCSI_ERR_PROTO;
-				break;
-			}
-			conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1;
-			/* fall through */
-		case ISCSI_OP_LOGIN_RSP:
-		case ISCSI_OP_TEXT_RSP:
-			/*
-			 * login related PDU's exp_statsn is handled in
-			 * userspace
-			 */
-			if (iscsi_recv_pdu(conn->cls_conn, hdr, data, datalen))
-				rc = ISCSI_ERR_CONN_FAILED;
-			iscsi_free_mgmt_task(conn, mtask);
-			break;
-		case ISCSI_OP_SCSI_TMFUNC_RSP:
-			if (datalen) {
-				rc = ISCSI_ERR_PROTO;
-				break;
-			}
-
-			iscsi_tmf_rsp(conn, hdr);
-			iscsi_free_mgmt_task(conn, mtask);
-			break;
-		case ISCSI_OP_NOOP_IN:
-			if (hdr->ttt != cpu_to_be32(ISCSI_RESERVED_TAG) ||
-			    datalen) {
-				rc = ISCSI_ERR_PROTO;
-				break;
-			}
-			conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1;
-
-			if (conn->ping_mtask != mtask) {
-				/*
-				 * If this is not in response to one of our
-				 * nops then it must be from userspace.
-				 */
-				if (iscsi_recv_pdu(conn->cls_conn, hdr, data,
-						   datalen))
-					rc = ISCSI_ERR_CONN_FAILED;
-			} else
-				mod_timer(&conn->transport_timer,
-					  jiffies + conn->recv_timeout);
-			iscsi_free_mgmt_task(conn, mtask);
-			break;
-		default:
-			rc = ISCSI_ERR_BAD_OPCODE;
-			break;
-		}
-	} else if (itt == ~0U) {
+	if (itt == ~0U) {
 		iscsi_update_cmdsn(session, (struct iscsi_nopin*)hdr);
 
 		switch(opcode) {
@@ -766,11 +785,104 @@
 			rc = ISCSI_ERR_BAD_OPCODE;
 			break;
 		}
-	} else
-		rc = ISCSI_ERR_BAD_ITT;
+		goto out;
+	}
 
+	switch(opcode) {
+	case ISCSI_OP_SCSI_CMD_RSP:
+	case ISCSI_OP_SCSI_DATA_IN:
+		task = iscsi_itt_to_ctask(conn, hdr->itt);
+		if (!task)
+			return ISCSI_ERR_BAD_ITT;
+		break;
+	case ISCSI_OP_R2T:
+		/*
+		 * LLD handles R2Ts if they need to.
+		 */
+		return 0;
+	case ISCSI_OP_LOGOUT_RSP:
+	case ISCSI_OP_LOGIN_RSP:
+	case ISCSI_OP_TEXT_RSP:
+	case ISCSI_OP_SCSI_TMFUNC_RSP:
+	case ISCSI_OP_NOOP_IN:
+		task = iscsi_itt_to_task(conn, hdr->itt);
+		if (!task)
+			return ISCSI_ERR_BAD_ITT;
+		break;
+	default:
+		return ISCSI_ERR_BAD_OPCODE;
+	}
+
+	switch(opcode) {
+	case ISCSI_OP_SCSI_CMD_RSP:
+		iscsi_scsi_cmd_rsp(conn, hdr, task, data, datalen);
+		break;
+	case ISCSI_OP_SCSI_DATA_IN:
+		if (hdr->flags & ISCSI_FLAG_DATA_STATUS) {
+			conn->scsirsp_pdus_cnt++;
+			iscsi_update_cmdsn(session,
+					   (struct iscsi_nopin*) hdr);
+			__iscsi_put_task(task);
+		}
+		break;
+	case ISCSI_OP_LOGOUT_RSP:
+		iscsi_update_cmdsn(session, (struct iscsi_nopin*)hdr);
+		if (datalen) {
+			rc = ISCSI_ERR_PROTO;
+			break;
+		}
+		conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1;
+		goto recv_pdu;
+	case ISCSI_OP_LOGIN_RSP:
+	case ISCSI_OP_TEXT_RSP:
+		iscsi_update_cmdsn(session, (struct iscsi_nopin*)hdr);
+		/*
+		 * login related PDU's exp_statsn is handled in
+		 * userspace
+		 */
+		goto recv_pdu;
+	case ISCSI_OP_SCSI_TMFUNC_RSP:
+		iscsi_update_cmdsn(session, (struct iscsi_nopin*)hdr);
+		if (datalen) {
+			rc = ISCSI_ERR_PROTO;
+			break;
+		}
+
+		iscsi_tmf_rsp(conn, hdr);
+		__iscsi_put_task(task);
+		break;
+	case ISCSI_OP_NOOP_IN:
+		iscsi_update_cmdsn(session, (struct iscsi_nopin*)hdr);
+		if (hdr->ttt != cpu_to_be32(ISCSI_RESERVED_TAG) || datalen) {
+			rc = ISCSI_ERR_PROTO;
+			break;
+		}
+		conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1;
+
+		if (conn->ping_task != task)
+			/*
+			 * If this is not in response to one of our
+			 * nops then it must be from userspace.
+			 */
+			goto recv_pdu;
+
+		mod_timer(&conn->transport_timer, jiffies + conn->recv_timeout);
+		__iscsi_put_task(task);
+		break;
+	default:
+		rc = ISCSI_ERR_BAD_OPCODE;
+		break;
+	}
+
+out:
+	return rc;
+recv_pdu:
+	if (iscsi_recv_pdu(conn->cls_conn, hdr, data, datalen))
+		rc = ISCSI_ERR_CONN_FAILED;
+	__iscsi_put_task(task);
 	return rc;
 }
+EXPORT_SYMBOL_GPL(__iscsi_complete_pdu);
 
 int iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
 		       char *data, int datalen)
@@ -784,52 +896,64 @@
 }
 EXPORT_SYMBOL_GPL(iscsi_complete_pdu);
 
-/* verify itt (itt encoding: age+cid+itt) */
-int iscsi_verify_itt(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
-		     uint32_t *ret_itt)
+int iscsi_verify_itt(struct iscsi_conn *conn, itt_t itt)
 {
 	struct iscsi_session *session = conn->session;
-	struct iscsi_cmd_task *ctask;
-	uint32_t itt;
+	uint32_t i;
 
-	if (hdr->itt != RESERVED_ITT) {
-		if (((__force u32)hdr->itt & ISCSI_AGE_MASK) !=
-		    (session->age << ISCSI_AGE_SHIFT)) {
-			iscsi_conn_printk(KERN_ERR, conn,
-					  "received itt %x expected session "
-					  "age (%x)\n", (__force u32)hdr->itt,
-					  session->age & ISCSI_AGE_MASK);
-			return ISCSI_ERR_BAD_ITT;
-		}
+	if (itt == RESERVED_ITT)
+		return 0;
 
-		itt = get_itt(hdr->itt);
-	} else
-		itt = ~0U;
-
-	if (itt < session->cmds_max) {
-		ctask = session->cmds[itt];
-
-		if (!ctask->sc) {
-			iscsi_conn_printk(KERN_INFO, conn, "dropping ctask "
-					  "with itt 0x%x\n", ctask->itt);
-			/* force drop */
-			return ISCSI_ERR_NO_SCSI_CMD;
-		}
-
-		if (ctask->sc->SCp.phase != session->age) {
-			iscsi_conn_printk(KERN_ERR, conn,
-					  "iscsi: ctask's session age %d, "
-					  "expected %d\n", ctask->sc->SCp.phase,
-					  session->age);
-			return ISCSI_ERR_SESSION_FAILED;
-		}
+	if (((__force u32)itt & ISCSI_AGE_MASK) !=
+	    (session->age << ISCSI_AGE_SHIFT)) {
+		iscsi_conn_printk(KERN_ERR, conn,
+				  "received itt %x expected session age (%x)\n",
+				  (__force u32)itt, session->age);
+		return ISCSI_ERR_BAD_ITT;
 	}
 
-	*ret_itt = itt;
+	i = get_itt(itt);
+	if (i >= session->cmds_max) {
+		iscsi_conn_printk(KERN_ERR, conn,
+				  "received invalid itt index %u (max cmds "
+				   "%u.\n", i, session->cmds_max);
+		return ISCSI_ERR_BAD_ITT;
+	}
 	return 0;
 }
 EXPORT_SYMBOL_GPL(iscsi_verify_itt);
 
+/**
+ * iscsi_itt_to_ctask - look up ctask by itt
+ * @conn: iscsi connection
+ * @itt: itt
+ *
+ * This should be used for cmd tasks.
+ *
+ * The session lock must be held.
+ */
+struct iscsi_task *iscsi_itt_to_ctask(struct iscsi_conn *conn, itt_t itt)
+{
+	struct iscsi_task *task;
+
+	if (iscsi_verify_itt(conn, itt))
+		return NULL;
+
+	task = iscsi_itt_to_task(conn, itt);
+	if (!task || !task->sc)
+		return NULL;
+
+	if (task->sc->SCp.phase != conn->session->age) {
+		iscsi_session_printk(KERN_ERR, conn->session,
+				  "task's session age %d, expected %d\n",
+				  task->sc->SCp.phase, conn->session->age);
+		return NULL;
+	}
+
+	return task;
+}
+EXPORT_SYMBOL_GPL(iscsi_itt_to_ctask);
+
 void iscsi_conn_failure(struct iscsi_conn *conn, enum iscsi_err err)
 {
 	struct iscsi_session *session = conn->session;
@@ -850,61 +974,6 @@
 }
 EXPORT_SYMBOL_GPL(iscsi_conn_failure);
 
-static void iscsi_prep_mtask(struct iscsi_conn *conn,
-			     struct iscsi_mgmt_task *mtask)
-{
-	struct iscsi_session *session = conn->session;
-	struct iscsi_hdr *hdr = mtask->hdr;
-	struct iscsi_nopout *nop = (struct iscsi_nopout *)hdr;
-
-	if (hdr->opcode != (ISCSI_OP_LOGIN | ISCSI_OP_IMMEDIATE) &&
-	    hdr->opcode != (ISCSI_OP_TEXT | ISCSI_OP_IMMEDIATE))
-		nop->exp_statsn = cpu_to_be32(conn->exp_statsn);
-	/*
-	 * pre-format CmdSN for outgoing PDU.
-	 */
-	nop->cmdsn = cpu_to_be32(session->cmdsn);
-	if (hdr->itt != RESERVED_ITT) {
-		hdr->itt = build_itt(mtask->itt, session->age);
-		/*
-		 * TODO: We always use immediate, so we never hit this.
-		 * If we start to send tmfs or nops as non-immediate then
-		 * we should start checking the cmdsn numbers for mgmt tasks.
-		 */
-		if (conn->c_stage == ISCSI_CONN_STARTED &&
-		    !(hdr->opcode & ISCSI_OP_IMMEDIATE)) {
-			session->queued_cmdsn++;
-			session->cmdsn++;
-		}
-	}
-
-	if (session->tt->init_mgmt_task)
-		session->tt->init_mgmt_task(conn, mtask);
-
-	debug_scsi("mgmtpdu [op 0x%x hdr->itt 0x%x datalen %d]\n",
-		   hdr->opcode & ISCSI_OPCODE_MASK, hdr->itt,
-		   mtask->data_count);
-}
-
-static int iscsi_xmit_mtask(struct iscsi_conn *conn)
-{
-	struct iscsi_hdr *hdr = conn->mtask->hdr;
-	int rc;
-
-	if ((hdr->opcode & ISCSI_OPCODE_MASK) == ISCSI_OP_LOGOUT)
-		conn->session->state = ISCSI_STATE_LOGGING_OUT;
-	spin_unlock_bh(&conn->session->lock);
-
-	rc = conn->session->tt->xmit_mgmt_task(conn, conn->mtask);
-	spin_lock_bh(&conn->session->lock);
-	if (rc)
-		return rc;
-
-	/* done with this in-progress mtask */
-	conn->mtask = NULL;
-	return 0;
-}
-
 static int iscsi_check_cmdsn_window_closed(struct iscsi_conn *conn)
 {
 	struct iscsi_session *session = conn->session;
@@ -922,37 +991,38 @@
 	return 0;
 }
 
-static int iscsi_xmit_ctask(struct iscsi_conn *conn)
+static int iscsi_xmit_task(struct iscsi_conn *conn)
 {
-	struct iscsi_cmd_task *ctask = conn->ctask;
+	struct iscsi_task *task = conn->task;
 	int rc;
 
-	__iscsi_get_ctask(ctask);
+	__iscsi_get_task(task);
 	spin_unlock_bh(&conn->session->lock);
-	rc = conn->session->tt->xmit_cmd_task(conn, ctask);
+	rc = conn->session->tt->xmit_task(task);
 	spin_lock_bh(&conn->session->lock);
-	__iscsi_put_ctask(ctask);
+	__iscsi_put_task(task);
 	if (!rc)
-		/* done with this ctask */
-		conn->ctask = NULL;
+		/* done with this task */
+		conn->task = NULL;
 	return rc;
 }
 
 /**
- * iscsi_requeue_ctask - requeue ctask to run from session workqueue
- * @ctask: ctask to requeue
+ * iscsi_requeue_task - requeue task to run from session workqueue
+ * @task: task to requeue
  *
- * LLDs that need to run a ctask from the session workqueue should call
- * this. The session lock must be held.
+ * LLDs that need to run a task from the session workqueue should call
+ * this. The session lock must be held. This should only be called
+ * by software drivers.
  */
-void iscsi_requeue_ctask(struct iscsi_cmd_task *ctask)
+void iscsi_requeue_task(struct iscsi_task *task)
 {
-	struct iscsi_conn *conn = ctask->conn;
+	struct iscsi_conn *conn = task->conn;
 
-	list_move_tail(&ctask->running, &conn->requeue);
+	list_move_tail(&task->running, &conn->requeue);
 	scsi_queue_work(conn->session->host, &conn->xmitwork);
 }
-EXPORT_SYMBOL_GPL(iscsi_requeue_ctask);
+EXPORT_SYMBOL_GPL(iscsi_requeue_task);
 
 /**
  * iscsi_data_xmit - xmit any command into the scheduled connection
@@ -974,14 +1044,8 @@
 		return -ENODATA;
 	}
 
-	if (conn->ctask) {
-		rc = iscsi_xmit_ctask(conn);
-		if (rc)
-			goto again;
-	}
-
-	if (conn->mtask) {
-		rc = iscsi_xmit_mtask(conn);
+	if (conn->task) {
+		rc = iscsi_xmit_task(conn);
 	        if (rc)
 		        goto again;
 	}
@@ -993,17 +1057,14 @@
 	 */
 check_mgmt:
 	while (!list_empty(&conn->mgmtqueue)) {
-		conn->mtask = list_entry(conn->mgmtqueue.next,
-					 struct iscsi_mgmt_task, running);
-		if (conn->session->state == ISCSI_STATE_LOGGING_OUT) {
-			iscsi_free_mgmt_task(conn, conn->mtask);
-			conn->mtask = NULL;
+		conn->task = list_entry(conn->mgmtqueue.next,
+					 struct iscsi_task, running);
+		if (iscsi_prep_mgmt_task(conn, conn->task)) {
+			__iscsi_put_task(conn->task);
+			conn->task = NULL;
 			continue;
 		}
-
-		iscsi_prep_mtask(conn, conn->mtask);
-		list_move_tail(conn->mgmtqueue.next, &conn->mgmt_run_list);
-		rc = iscsi_xmit_mtask(conn);
+		rc = iscsi_xmit_task(conn);
 		if (rc)
 			goto again;
 	}
@@ -1013,24 +1074,21 @@
 		if (conn->tmf_state == TMF_QUEUED)
 			break;
 
-		conn->ctask = list_entry(conn->xmitqueue.next,
-					 struct iscsi_cmd_task, running);
+		conn->task = list_entry(conn->xmitqueue.next,
+					 struct iscsi_task, running);
 		if (conn->session->state == ISCSI_STATE_LOGGING_OUT) {
-			fail_command(conn, conn->ctask, DID_IMM_RETRY << 16);
+			fail_command(conn, conn->task, DID_IMM_RETRY << 16);
 			continue;
 		}
-		if (iscsi_prep_scsi_cmd_pdu(conn->ctask)) {
-			fail_command(conn, conn->ctask, DID_ABORT << 16);
+		if (iscsi_prep_scsi_cmd_pdu(conn->task)) {
+			fail_command(conn, conn->task, DID_ABORT << 16);
 			continue;
 		}
-
-		conn->ctask->state = ISCSI_TASK_RUNNING;
-		list_move_tail(conn->xmitqueue.next, &conn->run_list);
-		rc = iscsi_xmit_ctask(conn);
+		rc = iscsi_xmit_task(conn);
 		if (rc)
 			goto again;
 		/*
-		 * we could continuously get new ctask requests so
+		 * we could continuously get new task requests so
 		 * we need to check the mgmt queue for nops that need to
 		 * be sent to aviod starvation
 		 */
@@ -1048,11 +1106,11 @@
 		if (conn->session->state == ISCSI_STATE_LOGGING_OUT)
 			break;
 
-		conn->ctask = list_entry(conn->requeue.next,
-					 struct iscsi_cmd_task, running);
-		conn->ctask->state = ISCSI_TASK_RUNNING;
+		conn->task = list_entry(conn->requeue.next,
+					 struct iscsi_task, running);
+		conn->task->state = ISCSI_TASK_RUNNING;
 		list_move_tail(conn->requeue.next, &conn->run_list);
-		rc = iscsi_xmit_ctask(conn);
+		rc = iscsi_xmit_task(conn);
 		if (rc)
 			goto again;
 		if (!list_empty(&conn->mgmtqueue))
@@ -1096,11 +1154,12 @@
 
 int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
 {
+	struct iscsi_cls_session *cls_session;
 	struct Scsi_Host *host;
 	int reason = 0;
 	struct iscsi_session *session;
 	struct iscsi_conn *conn;
-	struct iscsi_cmd_task *ctask = NULL;
+	struct iscsi_task *task = NULL;
 
 	sc->scsi_done = done;
 	sc->result = 0;
@@ -1109,10 +1168,11 @@
 	host = sc->device->host;
 	spin_unlock(host->host_lock);
 
-	session = iscsi_hostdata(host->hostdata);
+	cls_session = starget_to_session(scsi_target(sc->device));
+	session = cls_session->dd_data;
 	spin_lock(&session->lock);
 
-	reason = iscsi_session_chkready(session_to_cls(session));
+	reason = iscsi_session_chkready(cls_session);
 	if (reason) {
 		sc->result = reason;
 		goto fault;
@@ -1167,26 +1227,39 @@
 		goto reject;
 	}
 
-	if (!__kfifo_get(session->cmdpool.queue, (void*)&ctask,
+	if (!__kfifo_get(session->cmdpool.queue, (void*)&task,
 			 sizeof(void*))) {
 		reason = FAILURE_OOM;
 		goto reject;
 	}
-	session->queued_cmdsn++;
-
 	sc->SCp.phase = session->age;
-	sc->SCp.ptr = (char *)ctask;
+	sc->SCp.ptr = (char *)task;
 
-	atomic_set(&ctask->refcount, 1);
-	ctask->state = ISCSI_TASK_PENDING;
-	ctask->conn = conn;
-	ctask->sc = sc;
-	INIT_LIST_HEAD(&ctask->running);
+	atomic_set(&task->refcount, 1);
+	task->state = ISCSI_TASK_PENDING;
+	task->conn = conn;
+	task->sc = sc;
+	INIT_LIST_HEAD(&task->running);
+	list_add_tail(&task->running, &conn->xmitqueue);
 
-	list_add_tail(&ctask->running, &conn->xmitqueue);
+	if (session->tt->caps & CAP_DATA_PATH_OFFLOAD) {
+		if (iscsi_prep_scsi_cmd_pdu(task)) {
+			sc->result = DID_ABORT << 16;
+			sc->scsi_done = NULL;
+			iscsi_complete_command(task);
+			goto fault;
+		}
+		if (session->tt->xmit_task(task)) {
+			sc->scsi_done = NULL;
+			iscsi_complete_command(task);
+			reason = FAILURE_SESSION_NOT_READY;
+			goto reject;
+		}
+	} else
+		scsi_queue_work(session->host, &conn->xmitwork);
+
+	session->queued_cmdsn++;
 	spin_unlock(&session->lock);
-
-	scsi_queue_work(host, &conn->xmitwork);
 	spin_lock(host->host_lock);
 	return 0;
 
@@ -1205,7 +1278,7 @@
 		scsi_out(sc)->resid = scsi_out(sc)->length;
 		scsi_in(sc)->resid = scsi_in(sc)->length;
 	}
-	sc->scsi_done(sc);
+	done(sc);
 	spin_lock(host->host_lock);
 	return 0;
 }
@@ -1222,7 +1295,7 @@
 
 void iscsi_session_recovery_timedout(struct iscsi_cls_session *cls_session)
 {
-	struct iscsi_session *session = class_to_transport_session(cls_session);
+	struct iscsi_session *session = cls_session->dd_data;
 
 	spin_lock_bh(&session->lock);
 	if (session->state != ISCSI_STATE_LOGGED_IN) {
@@ -1236,9 +1309,13 @@
 
 int iscsi_eh_host_reset(struct scsi_cmnd *sc)
 {
-	struct Scsi_Host *host = sc->device->host;
-	struct iscsi_session *session = iscsi_hostdata(host->hostdata);
-	struct iscsi_conn *conn = session->leadconn;
+	struct iscsi_cls_session *cls_session;
+	struct iscsi_session *session;
+	struct iscsi_conn *conn;
+
+	cls_session = starget_to_session(scsi_target(sc->device));
+	session = cls_session->dd_data;
+	conn = session->leadconn;
 
 	mutex_lock(&session->eh_mutex);
 	spin_lock_bh(&session->lock);
@@ -1300,11 +1377,11 @@
 				   int timeout)
 {
 	struct iscsi_session *session = conn->session;
-	struct iscsi_mgmt_task *mtask;
+	struct iscsi_task *task;
 
-	mtask = __iscsi_conn_send_pdu(conn, (struct iscsi_hdr *)hdr,
+	task = __iscsi_conn_send_pdu(conn, (struct iscsi_hdr *)hdr,
 				      NULL, 0);
-	if (!mtask) {
+	if (!task) {
 		spin_unlock_bh(&session->lock);
 		iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
 		spin_lock_bh(&session->lock);
@@ -1320,7 +1397,6 @@
 
 	spin_unlock_bh(&session->lock);
 	mutex_unlock(&session->eh_mutex);
-	scsi_queue_work(session->host, &conn->xmitwork);
 
 	/*
 	 * block eh thread until:
@@ -1339,7 +1415,7 @@
 
 	mutex_lock(&session->eh_mutex);
 	spin_lock_bh(&session->lock);
-	/* if the session drops it will clean up the mtask */
+	/* if the session drops it will clean up the task */
 	if (age != session->age ||
 	    session->state != ISCSI_STATE_LOGGED_IN)
 		return -ENOTCONN;
@@ -1353,48 +1429,51 @@
 static void fail_all_commands(struct iscsi_conn *conn, unsigned lun,
 			      int error)
 {
-	struct iscsi_cmd_task *ctask, *tmp;
+	struct iscsi_task *task, *tmp;
 
-	if (conn->ctask && (conn->ctask->sc->device->lun == lun || lun == -1))
-		conn->ctask = NULL;
+	if (conn->task && (conn->task->sc->device->lun == lun || lun == -1))
+		conn->task = NULL;
 
 	/* flush pending */
-	list_for_each_entry_safe(ctask, tmp, &conn->xmitqueue, running) {
-		if (lun == ctask->sc->device->lun || lun == -1) {
+	list_for_each_entry_safe(task, tmp, &conn->xmitqueue, running) {
+		if (lun == task->sc->device->lun || lun == -1) {
 			debug_scsi("failing pending sc %p itt 0x%x\n",
-				   ctask->sc, ctask->itt);
-			fail_command(conn, ctask, error << 16);
+				   task->sc, task->itt);
+			fail_command(conn, task, error << 16);
 		}
 	}
 
-	list_for_each_entry_safe(ctask, tmp, &conn->requeue, running) {
-		if (lun == ctask->sc->device->lun || lun == -1) {
+	list_for_each_entry_safe(task, tmp, &conn->requeue, running) {
+		if (lun == task->sc->device->lun || lun == -1) {
 			debug_scsi("failing requeued sc %p itt 0x%x\n",
-				   ctask->sc, ctask->itt);
-			fail_command(conn, ctask, error << 16);
+				   task->sc, task->itt);
+			fail_command(conn, task, error << 16);
 		}
 	}
 
 	/* fail all other running */
-	list_for_each_entry_safe(ctask, tmp, &conn->run_list, running) {
-		if (lun == ctask->sc->device->lun || lun == -1) {
+	list_for_each_entry_safe(task, tmp, &conn->run_list, running) {
+		if (lun == task->sc->device->lun || lun == -1) {
 			debug_scsi("failing in progress sc %p itt 0x%x\n",
-				   ctask->sc, ctask->itt);
-			fail_command(conn, ctask, DID_BUS_BUSY << 16);
+				   task->sc, task->itt);
+			fail_command(conn, task, DID_BUS_BUSY << 16);
 		}
 	}
 }
 
-static void iscsi_suspend_tx(struct iscsi_conn *conn)
+void iscsi_suspend_tx(struct iscsi_conn *conn)
 {
 	set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx);
-	scsi_flush_work(conn->session->host);
+	if (!(conn->session->tt->caps & CAP_DATA_PATH_OFFLOAD))
+		scsi_flush_work(conn->session->host);
 }
+EXPORT_SYMBOL_GPL(iscsi_suspend_tx);
 
 static void iscsi_start_tx(struct iscsi_conn *conn)
 {
 	clear_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx);
-	scsi_queue_work(conn->session->host, &conn->xmitwork);
+	if (!(conn->session->tt->caps & CAP_DATA_PATH_OFFLOAD))
+		scsi_queue_work(conn->session->host, &conn->xmitwork);
 }
 
 static enum scsi_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *scmd)
@@ -1405,7 +1484,7 @@
 	enum scsi_eh_timer_return rc = EH_NOT_HANDLED;
 
 	cls_session = starget_to_session(scsi_target(scmd->device));
-	session = class_to_transport_session(cls_session);
+	session = cls_session->dd_data;
 
 	debug_scsi("scsi cmd %p timedout\n", scmd);
 
@@ -1443,7 +1522,7 @@
 			   jiffies))
 		rc = EH_RESET_TIMER;
 	/* if in the middle of checking the transport then give us more time */
-	if (conn->ping_mtask)
+	if (conn->ping_task)
 		rc = EH_RESET_TIMER;
 done:
 	spin_unlock(&session->lock);
@@ -1467,7 +1546,7 @@
 
 	recv_timeout *= HZ;
 	last_recv = conn->last_recv;
-	if (conn->ping_mtask &&
+	if (conn->ping_task &&
 	    time_before_eq(conn->last_ping + (conn->ping_timeout * HZ),
 			   jiffies)) {
 		iscsi_conn_printk(KERN_ERR, conn, "ping timeout of %d secs "
@@ -1493,27 +1572,30 @@
 	spin_unlock(&session->lock);
 }
 
-static void iscsi_prep_abort_task_pdu(struct iscsi_cmd_task *ctask,
+static void iscsi_prep_abort_task_pdu(struct iscsi_task *task,
 				      struct iscsi_tm *hdr)
 {
 	memset(hdr, 0, sizeof(*hdr));
 	hdr->opcode = ISCSI_OP_SCSI_TMFUNC | ISCSI_OP_IMMEDIATE;
 	hdr->flags = ISCSI_TM_FUNC_ABORT_TASK & ISCSI_FLAG_TM_FUNC_MASK;
 	hdr->flags |= ISCSI_FLAG_CMD_FINAL;
-	memcpy(hdr->lun, ctask->hdr->lun, sizeof(hdr->lun));
-	hdr->rtt = ctask->hdr->itt;
-	hdr->refcmdsn = ctask->hdr->cmdsn;
+	memcpy(hdr->lun, task->hdr->lun, sizeof(hdr->lun));
+	hdr->rtt = task->hdr->itt;
+	hdr->refcmdsn = task->hdr->cmdsn;
 }
 
 int iscsi_eh_abort(struct scsi_cmnd *sc)
 {
-	struct Scsi_Host *host = sc->device->host;
-	struct iscsi_session *session = iscsi_hostdata(host->hostdata);
+	struct iscsi_cls_session *cls_session;
+	struct iscsi_session *session;
 	struct iscsi_conn *conn;
-	struct iscsi_cmd_task *ctask;
+	struct iscsi_task *task;
 	struct iscsi_tm *hdr;
 	int rc, age;
 
+	cls_session = starget_to_session(scsi_target(sc->device));
+	session = cls_session->dd_data;
+
 	mutex_lock(&session->eh_mutex);
 	spin_lock_bh(&session->lock);
 	/*
@@ -1542,17 +1624,17 @@
 	conn->eh_abort_cnt++;
 	age = session->age;
 
-	ctask = (struct iscsi_cmd_task *)sc->SCp.ptr;
-	debug_scsi("aborting [sc %p itt 0x%x]\n", sc, ctask->itt);
+	task = (struct iscsi_task *)sc->SCp.ptr;
+	debug_scsi("aborting [sc %p itt 0x%x]\n", sc, task->itt);
 
-	/* ctask completed before time out */
-	if (!ctask->sc) {
+	/* task completed before time out */
+	if (!task->sc) {
 		debug_scsi("sc completed while abort in progress\n");
 		goto success;
 	}
 
-	if (ctask->state == ISCSI_TASK_PENDING) {
-		fail_command(conn, ctask, DID_ABORT << 16);
+	if (task->state == ISCSI_TASK_PENDING) {
+		fail_command(conn, task, DID_ABORT << 16);
 		goto success;
 	}
 
@@ -1562,7 +1644,7 @@
 	conn->tmf_state = TMF_QUEUED;
 
 	hdr = &conn->tmhdr;
-	iscsi_prep_abort_task_pdu(ctask, hdr);
+	iscsi_prep_abort_task_pdu(task, hdr);
 
 	if (iscsi_exec_task_mgmt_fn(conn, hdr, age, session->abort_timeout)) {
 		rc = FAILED;
@@ -1572,16 +1654,20 @@
 	switch (conn->tmf_state) {
 	case TMF_SUCCESS:
 		spin_unlock_bh(&session->lock);
+		/*
+		 * stop tx side incase the target had sent a abort rsp but
+		 * the initiator was still writing out data.
+		 */
 		iscsi_suspend_tx(conn);
 		/*
-		 * clean up task if aborted. grab the recv lock as a writer
+		 * we do not stop the recv side because targets have been
+		 * good and have never sent us a successful tmf response
+		 * then sent more data for the cmd.
 		 */
-		write_lock_bh(conn->recv_lock);
 		spin_lock(&session->lock);
-		fail_command(conn, ctask, DID_ABORT << 16);
+		fail_command(conn, task, DID_ABORT << 16);
 		conn->tmf_state = TMF_INITIAL;
 		spin_unlock(&session->lock);
-		write_unlock_bh(conn->recv_lock);
 		iscsi_start_tx(conn);
 		goto success_unlocked;
 	case TMF_TIMEDOUT:
@@ -1591,7 +1677,7 @@
 	case TMF_NOT_FOUND:
 		if (!sc->SCp.ptr) {
 			conn->tmf_state = TMF_INITIAL;
-			/* ctask completed before tmf abort response */
+			/* task completed before tmf abort response */
 			debug_scsi("sc completed while abort in progress\n");
 			goto success;
 		}
@@ -1604,7 +1690,7 @@
 success:
 	spin_unlock_bh(&session->lock);
 success_unlocked:
-	debug_scsi("abort success [sc %lx itt 0x%x]\n", (long)sc, ctask->itt);
+	debug_scsi("abort success [sc %lx itt 0x%x]\n", (long)sc, task->itt);
 	mutex_unlock(&session->eh_mutex);
 	return SUCCESS;
 
@@ -1612,7 +1698,7 @@
 	spin_unlock_bh(&session->lock);
 failed_unlocked:
 	debug_scsi("abort failed [sc %p itt 0x%x]\n", sc,
-		    ctask ? ctask->itt : 0);
+		    task ? task->itt : 0);
 	mutex_unlock(&session->eh_mutex);
 	return FAILED;
 }
@@ -1630,12 +1716,15 @@
 
 int iscsi_eh_device_reset(struct scsi_cmnd *sc)
 {
-	struct Scsi_Host *host = sc->device->host;
-	struct iscsi_session *session = iscsi_hostdata(host->hostdata);
+	struct iscsi_cls_session *cls_session;
+	struct iscsi_session *session;
 	struct iscsi_conn *conn;
 	struct iscsi_tm *hdr;
 	int rc = FAILED;
 
+	cls_session = starget_to_session(scsi_target(sc->device));
+	session = cls_session->dd_data;
+
 	debug_scsi("LU Reset [sc %p lun %u]\n", sc, sc->device->lun);
 
 	mutex_lock(&session->eh_mutex);
@@ -1678,13 +1767,11 @@
 	spin_unlock_bh(&session->lock);
 
 	iscsi_suspend_tx(conn);
-	/* need to grab the recv lock then session lock */
-	write_lock_bh(conn->recv_lock);
+
 	spin_lock(&session->lock);
 	fail_all_commands(conn, sc->device->lun, DID_ERROR);
 	conn->tmf_state = TMF_INITIAL;
 	spin_unlock(&session->lock);
-	write_unlock_bh(conn->recv_lock);
 
 	iscsi_start_tx(conn);
 	goto done;
@@ -1760,177 +1847,203 @@
 }
 EXPORT_SYMBOL_GPL(iscsi_pool_free);
 
-/*
- * iSCSI Session's hostdata organization:
+/**
+ * iscsi_host_add - add host to system
+ * @shost: scsi host
+ * @pdev: parent device
  *
- *    *------------------* <== hostdata_session(host->hostdata)
- *    | ptr to class sess|
- *    |------------------| <== iscsi_hostdata(host->hostdata)
- *    | iscsi_session    |
- *    *------------------*
+ * This should be called by partial offload and software iscsi drivers
+ * to add a host to the system.
  */
+int iscsi_host_add(struct Scsi_Host *shost, struct device *pdev)
+{
+	if (!shost->can_queue)
+		shost->can_queue = ISCSI_DEF_XMIT_CMDS_MAX;
 
-#define hostdata_privsize(_sz)	(sizeof(unsigned long) + _sz + \
-				 _sz % sizeof(unsigned long))
-
-#define hostdata_session(_hostdata) (iscsi_ptr(*(unsigned long *)_hostdata))
+	return scsi_add_host(shost, pdev);
+}
+EXPORT_SYMBOL_GPL(iscsi_host_add);
 
 /**
- * iscsi_session_setup - create iscsi cls session and host and session
- * @scsit: scsi transport template
- * @iscsit: iscsi transport template
- * @cmds_max: scsi host can queue
- * @qdepth: scsi host cmds per lun
- * @cmd_task_size: LLD ctask private data size
- * @mgmt_task_size: LLD mtask private data size
- * @initial_cmdsn: initial CmdSN
- * @hostno: host no allocated
+ * iscsi_host_alloc - allocate a host and driver data
+ * @sht: scsi host template
+ * @dd_data_size: driver host data size
+ * @qdepth: default device queue depth
  *
- * This can be used by software iscsi_transports that allocate
- * a session per scsi host.
- **/
-struct iscsi_cls_session *
-iscsi_session_setup(struct iscsi_transport *iscsit,
-		    struct scsi_transport_template *scsit,
-		    uint16_t cmds_max, uint16_t qdepth,
-		    int cmd_task_size, int mgmt_task_size,
-		    uint32_t initial_cmdsn, uint32_t *hostno)
+ * This should be called by partial offload and software iscsi drivers.
+ * To access the driver specific memory use the iscsi_host_priv() macro.
+ */
+struct Scsi_Host *iscsi_host_alloc(struct scsi_host_template *sht,
+				   int dd_data_size, uint16_t qdepth)
 {
 	struct Scsi_Host *shost;
-	struct iscsi_session *session;
-	struct iscsi_cls_session *cls_session;
-	int cmd_i;
+
+	shost = scsi_host_alloc(sht, sizeof(struct iscsi_host) + dd_data_size);
+	if (!shost)
+		return NULL;
+	shost->transportt->eh_timed_out = iscsi_eh_cmd_timed_out;
 
 	if (qdepth > ISCSI_MAX_CMD_PER_LUN || qdepth < 1) {
 		if (qdepth != 0)
 			printk(KERN_ERR "iscsi: invalid queue depth of %d. "
-			      "Queue depth must be between 1 and %d.\n",
-			      qdepth, ISCSI_MAX_CMD_PER_LUN);
+			       "Queue depth must be between 1 and %d.\n",
+			       qdepth, ISCSI_MAX_CMD_PER_LUN);
 		qdepth = ISCSI_DEF_CMD_PER_LUN;
 	}
+	shost->cmd_per_lun = qdepth;
+	return shost;
+}
+EXPORT_SYMBOL_GPL(iscsi_host_alloc);
 
-	if (!is_power_of_2(cmds_max) || cmds_max >= ISCSI_MGMT_ITT_OFFSET ||
-	    cmds_max < 2) {
-		if (cmds_max != 0)
-			printk(KERN_ERR "iscsi: invalid can_queue of %d. "
-			       "can_queue must be a power of 2 and between "
-			       "2 and %d - setting to %d.\n", cmds_max,
-			       ISCSI_MGMT_ITT_OFFSET, ISCSI_DEF_XMIT_CMDS_MAX);
-		cmds_max = ISCSI_DEF_XMIT_CMDS_MAX;
+/**
+ * iscsi_host_remove - remove host and sessions
+ * @shost: scsi host
+ *
+ * This will also remove any sessions attached to the host, but if userspace
+ * is managing the session at the same time this will break. TODO: add
+ * refcounting to the netlink iscsi interface so a rmmod or host hot unplug
+ * does not remove the memory from under us.
+ */
+void iscsi_host_remove(struct Scsi_Host *shost)
+{
+	iscsi_host_for_each_session(shost, iscsi_session_teardown);
+	scsi_remove_host(shost);
+}
+EXPORT_SYMBOL_GPL(iscsi_host_remove);
+
+void iscsi_host_free(struct Scsi_Host *shost)
+{
+	struct iscsi_host *ihost = shost_priv(shost);
+
+	kfree(ihost->netdev);
+	kfree(ihost->hwaddress);
+	kfree(ihost->initiatorname);
+	scsi_host_put(shost);
+}
+EXPORT_SYMBOL_GPL(iscsi_host_free);
+
+/**
+ * iscsi_session_setup - create iscsi cls session and host and session
+ * @iscsit: iscsi transport template
+ * @shost: scsi host
+ * @cmds_max: session can queue
+ * @cmd_task_size: LLD task private data size
+ * @initial_cmdsn: initial CmdSN
+ *
+ * This can be used by software iscsi_transports that allocate
+ * a session per scsi host.
+ *
+ * Callers should set cmds_max to the largest total numer (mgmt + scsi) of
+ * tasks they support. The iscsi layer reserves ISCSI_MGMT_CMDS_MAX tasks
+ * for nop handling and login/logout requests.
+ */
+struct iscsi_cls_session *
+iscsi_session_setup(struct iscsi_transport *iscsit, struct Scsi_Host *shost,
+		    uint16_t cmds_max, int cmd_task_size,
+		    uint32_t initial_cmdsn, unsigned int id)
+{
+	struct iscsi_session *session;
+	struct iscsi_cls_session *cls_session;
+	int cmd_i, scsi_cmds, total_cmds = cmds_max;
+
+	if (!total_cmds)
+		total_cmds = ISCSI_DEF_XMIT_CMDS_MAX;
+	/*
+	 * The iscsi layer needs some tasks for nop handling and tmfs,
+	 * so the cmds_max must at least be greater than ISCSI_MGMT_CMDS_MAX
+	 * + 1 command for scsi IO.
+	 */
+	if (total_cmds < ISCSI_TOTAL_CMDS_MIN) {
+		printk(KERN_ERR "iscsi: invalid can_queue of %d. can_queue "
+		       "must be a power of two that is at least %d.\n",
+		       total_cmds, ISCSI_TOTAL_CMDS_MIN);
+		return NULL;
 	}
 
-	shost = scsi_host_alloc(iscsit->host_template,
-				hostdata_privsize(sizeof(*session)));
-	if (!shost)
+	if (total_cmds > ISCSI_TOTAL_CMDS_MAX) {
+		printk(KERN_ERR "iscsi: invalid can_queue of %d. can_queue "
+		       "must be a power of 2 less than or equal to %d.\n",
+		       cmds_max, ISCSI_TOTAL_CMDS_MAX);
+		total_cmds = ISCSI_TOTAL_CMDS_MAX;
+	}
+
+	if (!is_power_of_2(total_cmds)) {
+		printk(KERN_ERR "iscsi: invalid can_queue of %d. can_queue "
+		       "must be a power of 2.\n", total_cmds);
+		total_cmds = rounddown_pow_of_two(total_cmds);
+		if (total_cmds < ISCSI_TOTAL_CMDS_MIN)
+			return NULL;
+		printk(KERN_INFO "iscsi: Rounding can_queue to %d.\n",
+		       total_cmds);
+	}
+	scsi_cmds = total_cmds - ISCSI_MGMT_CMDS_MAX;
+
+	cls_session = iscsi_alloc_session(shost, iscsit,
+					  sizeof(struct iscsi_session));
+	if (!cls_session)
 		return NULL;
-
-	/* the iscsi layer takes one task for reserve */
-	shost->can_queue = cmds_max - 1;
-	shost->cmd_per_lun = qdepth;
-	shost->max_id = 1;
-	shost->max_channel = 0;
-	shost->max_lun = iscsit->max_lun;
-	shost->max_cmd_len = iscsit->max_cmd_len;
-	shost->transportt = scsit;
-	shost->transportt->create_work_queue = 1;
-	shost->transportt->eh_timed_out = iscsi_eh_cmd_timed_out;
-	*hostno = shost->host_no;
-
-	session = iscsi_hostdata(shost->hostdata);
-	memset(session, 0, sizeof(struct iscsi_session));
+	session = cls_session->dd_data;
+	session->cls_session = cls_session;
 	session->host = shost;
 	session->state = ISCSI_STATE_FREE;
 	session->fast_abort = 1;
 	session->lu_reset_timeout = 15;
 	session->abort_timeout = 10;
-	session->mgmtpool_max = ISCSI_MGMT_CMDS_MAX;
-	session->cmds_max = cmds_max;
+	session->scsi_cmds_max = scsi_cmds;
+	session->cmds_max = total_cmds;
 	session->queued_cmdsn = session->cmdsn = initial_cmdsn;
 	session->exp_cmdsn = initial_cmdsn + 1;
 	session->max_cmdsn = initial_cmdsn + 1;
 	session->max_r2t = 1;
 	session->tt = iscsit;
 	mutex_init(&session->eh_mutex);
+	spin_lock_init(&session->lock);
 
 	/* initialize SCSI PDU commands pool */
 	if (iscsi_pool_init(&session->cmdpool, session->cmds_max,
 			    (void***)&session->cmds,
-			    cmd_task_size + sizeof(struct iscsi_cmd_task)))
+			    cmd_task_size + sizeof(struct iscsi_task)))
 		goto cmdpool_alloc_fail;
 
 	/* pre-format cmds pool with ITT */
 	for (cmd_i = 0; cmd_i < session->cmds_max; cmd_i++) {
-		struct iscsi_cmd_task *ctask = session->cmds[cmd_i];
+		struct iscsi_task *task = session->cmds[cmd_i];
 
 		if (cmd_task_size)
-			ctask->dd_data = &ctask[1];
-		ctask->itt = cmd_i;
-		INIT_LIST_HEAD(&ctask->running);
+			task->dd_data = &task[1];
+		task->itt = cmd_i;
+		INIT_LIST_HEAD(&task->running);
 	}
 
-	spin_lock_init(&session->lock);
-
-	/* initialize immediate command pool */
-	if (iscsi_pool_init(&session->mgmtpool, session->mgmtpool_max,
-			   (void***)&session->mgmt_cmds,
-			   mgmt_task_size + sizeof(struct iscsi_mgmt_task)))
-		goto mgmtpool_alloc_fail;
-
-
-	/* pre-format immediate cmds pool with ITT */
-	for (cmd_i = 0; cmd_i < session->mgmtpool_max; cmd_i++) {
-		struct iscsi_mgmt_task *mtask = session->mgmt_cmds[cmd_i];
-
-		if (mgmt_task_size)
-			mtask->dd_data = &mtask[1];
-		mtask->itt = ISCSI_MGMT_ITT_OFFSET + cmd_i;
-		INIT_LIST_HEAD(&mtask->running);
-	}
-
-	if (scsi_add_host(shost, NULL))
-		goto add_host_fail;
-
 	if (!try_module_get(iscsit->owner))
+		goto module_get_fail;
+
+	if (iscsi_add_session(cls_session, id))
 		goto cls_session_fail;
-
-	cls_session = iscsi_create_session(shost, iscsit, 0);
-	if (!cls_session)
-		goto module_put;
-	*(unsigned long*)shost->hostdata = (unsigned long)cls_session;
-
 	return cls_session;
 
-module_put:
-	module_put(iscsit->owner);
 cls_session_fail:
-	scsi_remove_host(shost);
-add_host_fail:
-	iscsi_pool_free(&session->mgmtpool);
-mgmtpool_alloc_fail:
+	module_put(iscsit->owner);
+module_get_fail:
 	iscsi_pool_free(&session->cmdpool);
 cmdpool_alloc_fail:
-	scsi_host_put(shost);
+	iscsi_free_session(cls_session);
 	return NULL;
 }
 EXPORT_SYMBOL_GPL(iscsi_session_setup);
 
 /**
  * iscsi_session_teardown - destroy session, host, and cls_session
- * shost: scsi host
+ * @cls_session: iscsi session
  *
- * This can be used by software iscsi_transports that allocate
- * a session per scsi host.
- **/
+ * The driver must have called iscsi_remove_session before
+ * calling this.
+ */
 void iscsi_session_teardown(struct iscsi_cls_session *cls_session)
 {
-	struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
-	struct iscsi_session *session = iscsi_hostdata(shost->hostdata);
+	struct iscsi_session *session = cls_session->dd_data;
 	struct module *owner = cls_session->transport->owner;
 
-	iscsi_remove_session(cls_session);
-	scsi_remove_host(shost);
-
-	iscsi_pool_free(&session->mgmtpool);
 	iscsi_pool_free(&session->cmdpool);
 
 	kfree(session->password);
@@ -1938,12 +2051,10 @@
 	kfree(session->username);
 	kfree(session->username_in);
 	kfree(session->targetname);
-	kfree(session->netdev);
-	kfree(session->hwaddress);
 	kfree(session->initiatorname);
+	kfree(session->ifacename);
 
-	iscsi_free_session(cls_session);
-	scsi_host_put(shost);
+	iscsi_destroy_session(cls_session);
 	module_put(owner);
 }
 EXPORT_SYMBOL_GPL(iscsi_session_teardown);
@@ -1951,22 +2062,26 @@
 /**
  * iscsi_conn_setup - create iscsi_cls_conn and iscsi_conn
  * @cls_session: iscsi_cls_session
+ * @dd_size: private driver data size
  * @conn_idx: cid
- **/
+ */
 struct iscsi_cls_conn *
-iscsi_conn_setup(struct iscsi_cls_session *cls_session, uint32_t conn_idx)
+iscsi_conn_setup(struct iscsi_cls_session *cls_session, int dd_size,
+		 uint32_t conn_idx)
 {
-	struct iscsi_session *session = class_to_transport_session(cls_session);
+	struct iscsi_session *session = cls_session->dd_data;
 	struct iscsi_conn *conn;
 	struct iscsi_cls_conn *cls_conn;
 	char *data;
 
-	cls_conn = iscsi_create_conn(cls_session, conn_idx);
+	cls_conn = iscsi_create_conn(cls_session, sizeof(*conn) + dd_size,
+				     conn_idx);
 	if (!cls_conn)
 		return NULL;
 	conn = cls_conn->dd_data;
-	memset(conn, 0, sizeof(*conn));
+	memset(conn, 0, sizeof(*conn) + dd_size);
 
+	conn->dd_data = cls_conn->dd_data + sizeof(*conn);
 	conn->session = session;
 	conn->cls_conn = cls_conn;
 	conn->c_stage = ISCSI_CONN_INITIAL_STAGE;
@@ -1985,30 +2100,30 @@
 	INIT_LIST_HEAD(&conn->requeue);
 	INIT_WORK(&conn->xmitwork, iscsi_xmitworker);
 
-	/* allocate login_mtask used for the login/text sequences */
+	/* allocate login_task used for the login/text sequences */
 	spin_lock_bh(&session->lock);
-	if (!__kfifo_get(session->mgmtpool.queue,
-                         (void*)&conn->login_mtask,
+	if (!__kfifo_get(session->cmdpool.queue,
+                         (void*)&conn->login_task,
 			 sizeof(void*))) {
 		spin_unlock_bh(&session->lock);
-		goto login_mtask_alloc_fail;
+		goto login_task_alloc_fail;
 	}
 	spin_unlock_bh(&session->lock);
 
 	data = kmalloc(ISCSI_DEF_MAX_RECV_SEG_LEN, GFP_KERNEL);
 	if (!data)
-		goto login_mtask_data_alloc_fail;
-	conn->login_mtask->data = conn->data = data;
+		goto login_task_data_alloc_fail;
+	conn->login_task->data = conn->data = data;
 
 	init_timer(&conn->tmf_timer);
 	init_waitqueue_head(&conn->ehwait);
 
 	return cls_conn;
 
-login_mtask_data_alloc_fail:
-	__kfifo_put(session->mgmtpool.queue, (void*)&conn->login_mtask,
+login_task_data_alloc_fail:
+	__kfifo_put(session->cmdpool.queue, (void*)&conn->login_task,
 		    sizeof(void*));
-login_mtask_alloc_fail:
+login_task_alloc_fail:
 	iscsi_destroy_conn(cls_conn);
 	return NULL;
 }
@@ -2068,7 +2183,7 @@
 	spin_lock_bh(&session->lock);
 	kfree(conn->data);
 	kfree(conn->persistent_address);
-	__kfifo_put(session->mgmtpool.queue, (void*)&conn->login_mtask,
+	__kfifo_put(session->cmdpool.queue, (void*)&conn->login_task,
 		    sizeof(void*));
 	if (session->leadconn == conn)
 		session->leadconn = NULL;
@@ -2140,7 +2255,7 @@
 	}
 	spin_unlock_bh(&session->lock);
 
-	iscsi_unblock_session(session_to_cls(session));
+	iscsi_unblock_session(session->cls_session);
 	wake_up(&conn->ehwait);
 	return 0;
 }
@@ -2149,21 +2264,23 @@
 static void
 flush_control_queues(struct iscsi_session *session, struct iscsi_conn *conn)
 {
-	struct iscsi_mgmt_task *mtask, *tmp;
+	struct iscsi_task *task, *tmp;
 
 	/* handle pending */
-	list_for_each_entry_safe(mtask, tmp, &conn->mgmtqueue, running) {
-		debug_scsi("flushing pending mgmt task itt 0x%x\n", mtask->itt);
-		iscsi_free_mgmt_task(conn, mtask);
+	list_for_each_entry_safe(task, tmp, &conn->mgmtqueue, running) {
+		debug_scsi("flushing pending mgmt task itt 0x%x\n", task->itt);
+		/* release ref from prep task */
+		__iscsi_put_task(task);
 	}
 
 	/* handle running */
-	list_for_each_entry_safe(mtask, tmp, &conn->mgmt_run_list, running) {
-		debug_scsi("flushing running mgmt task itt 0x%x\n", mtask->itt);
-		iscsi_free_mgmt_task(conn, mtask);
+	list_for_each_entry_safe(task, tmp, &conn->mgmt_run_list, running) {
+		debug_scsi("flushing running mgmt task itt 0x%x\n", task->itt);
+		/* release ref from prep task */
+		__iscsi_put_task(task);
 	}
 
-	conn->mtask = NULL;
+	conn->task = NULL;
 }
 
 static void iscsi_start_session_recovery(struct iscsi_session *session,
@@ -2182,17 +2299,6 @@
 	}
 
 	/*
-	 * The LLD either freed/unset the lock on us, or userspace called
-	 * stop but did not create a proper connection (connection was never
-	 * bound or it was unbound then stop was called).
-	 */
-	if (!conn->recv_lock) {
-		spin_unlock_bh(&session->lock);
-		mutex_unlock(&session->eh_mutex);
-		return;
-	}
-
-	/*
 	 * When this is called for the in_login state, we only want to clean
 	 * up the login task and connection. We do not need to block and set
 	 * the recovery state again
@@ -2208,11 +2314,6 @@
 	spin_unlock_bh(&session->lock);
 
 	iscsi_suspend_tx(conn);
-
-	write_lock_bh(conn->recv_lock);
-	set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_rx);
-	write_unlock_bh(conn->recv_lock);
-
 	/*
 	 * for connection level recovery we should not calculate
 	 * header digest. conn->hdr_size used for optimization
@@ -2225,7 +2326,7 @@
 		if (session->state == ISCSI_STATE_IN_RECOVERY &&
 		    old_stop_stage != STOP_CONN_RECOVER) {
 			debug_scsi("blocking session\n");
-			iscsi_block_session(session_to_cls(session));
+			iscsi_block_session(session->cls_session);
 		}
 	}
 
@@ -2260,7 +2361,7 @@
 int iscsi_conn_bind(struct iscsi_cls_session *cls_session,
 		    struct iscsi_cls_conn *cls_conn, int is_leading)
 {
-	struct iscsi_session *session = class_to_transport_session(cls_session);
+	struct iscsi_session *session = cls_session->dd_data;
 	struct iscsi_conn *conn = cls_conn->dd_data;
 
 	spin_lock_bh(&session->lock);
@@ -2399,6 +2500,14 @@
 		if (!conn->persistent_address)
 			return -ENOMEM;
 		break;
+	case ISCSI_PARAM_IFACE_NAME:
+		if (!session->ifacename)
+			session->ifacename = kstrdup(buf, GFP_KERNEL);
+		break;
+	case ISCSI_PARAM_INITIATOR_NAME:
+		if (!session->initiatorname)
+			session->initiatorname = kstrdup(buf, GFP_KERNEL);
+		break;
 	default:
 		return -ENOSYS;
 	}
@@ -2410,8 +2519,7 @@
 int iscsi_session_get_param(struct iscsi_cls_session *cls_session,
 			    enum iscsi_param param, char *buf)
 {
-	struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
-	struct iscsi_session *session = iscsi_hostdata(shost->hostdata);
+	struct iscsi_session *session = cls_session->dd_data;
 	int len;
 
 	switch(param) {
@@ -2466,6 +2574,15 @@
 	case ISCSI_PARAM_PASSWORD_IN:
 		len = sprintf(buf, "%s\n", session->password_in);
 		break;
+	case ISCSI_PARAM_IFACE_NAME:
+		len = sprintf(buf, "%s\n", session->ifacename);
+		break;
+	case ISCSI_PARAM_INITIATOR_NAME:
+		if (!session->initiatorname)
+			len = sprintf(buf, "%s\n", "unknown");
+		else
+			len = sprintf(buf, "%s\n", session->initiatorname);
+		break;
 	default:
 		return -ENOSYS;
 	}
@@ -2525,29 +2642,35 @@
 int iscsi_host_get_param(struct Scsi_Host *shost, enum iscsi_host_param param,
 			 char *buf)
 {
-	struct iscsi_session *session = iscsi_hostdata(shost->hostdata);
+	struct iscsi_host *ihost = shost_priv(shost);
 	int len;
 
 	switch (param) {
 	case ISCSI_HOST_PARAM_NETDEV_NAME:
-		if (!session->netdev)
+		if (!ihost->netdev)
 			len = sprintf(buf, "%s\n", "default");
 		else
-			len = sprintf(buf, "%s\n", session->netdev);
+			len = sprintf(buf, "%s\n", ihost->netdev);
 		break;
 	case ISCSI_HOST_PARAM_HWADDRESS:
-		if (!session->hwaddress)
+		if (!ihost->hwaddress)
 			len = sprintf(buf, "%s\n", "default");
 		else
-			len = sprintf(buf, "%s\n", session->hwaddress);
+			len = sprintf(buf, "%s\n", ihost->hwaddress);
 		break;
 	case ISCSI_HOST_PARAM_INITIATOR_NAME:
-		if (!session->initiatorname)
+		if (!ihost->initiatorname)
 			len = sprintf(buf, "%s\n", "unknown");
 		else
-			len = sprintf(buf, "%s\n", session->initiatorname);
+			len = sprintf(buf, "%s\n", ihost->initiatorname);
 		break;
-
+	case ISCSI_HOST_PARAM_IPADDRESS:
+		if (!strlen(ihost->local_address))
+			len = sprintf(buf, "%s\n", "unknown");
+		else
+			len = sprintf(buf, "%s\n",
+				      ihost->local_address);
+		break;
 	default:
 		return -ENOSYS;
 	}
@@ -2559,20 +2682,20 @@
 int iscsi_host_set_param(struct Scsi_Host *shost, enum iscsi_host_param param,
 			 char *buf, int buflen)
 {
-	struct iscsi_session *session = iscsi_hostdata(shost->hostdata);
+	struct iscsi_host *ihost = shost_priv(shost);
 
 	switch (param) {
 	case ISCSI_HOST_PARAM_NETDEV_NAME:
-		if (!session->netdev)
-			session->netdev = kstrdup(buf, GFP_KERNEL);
+		if (!ihost->netdev)
+			ihost->netdev = kstrdup(buf, GFP_KERNEL);
 		break;
 	case ISCSI_HOST_PARAM_HWADDRESS:
-		if (!session->hwaddress)
-			session->hwaddress = kstrdup(buf, GFP_KERNEL);
+		if (!ihost->hwaddress)
+			ihost->hwaddress = kstrdup(buf, GFP_KERNEL);
 		break;
 	case ISCSI_HOST_PARAM_INITIATOR_NAME:
-		if (!session->initiatorname)
-			session->initiatorname = kstrdup(buf, GFP_KERNEL);
+		if (!ihost->initiatorname)
+			ihost->initiatorname = kstrdup(buf, GFP_KERNEL);
 		break;
 	default:
 		return -ENOSYS;
diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h
index ec0b0f6..e0e018d 100644
--- a/drivers/scsi/lpfc/lpfc.h
+++ b/drivers/scsi/lpfc/lpfc.h
@@ -33,6 +33,7 @@
 #define LPFC_MAX_SG_SEG_CNT	256	/* sg element count per scsi cmnd */
 #define LPFC_IOCB_LIST_CNT	2250	/* list of IOCBs for fast-path usage. */
 #define LPFC_Q_RAMP_UP_INTERVAL 120     /* lun q_depth ramp up interval */
+#define LPFC_VNAME_LEN		100	/* vport symbolic name length */
 
 /*
  * Following time intervals are used of adjusting SCSI device
@@ -59,6 +60,9 @@
 
 #define MAX_HBAEVT	32
 
+/* lpfc wait event data ready flag */
+#define LPFC_DATA_READY		(1<<0)
+
 enum lpfc_polling_flags {
 	ENABLE_FCP_RING_POLLING = 0x1,
 	DISABLE_FCP_RING_INT    = 0x2
@@ -425,9 +429,6 @@
 
 	uint16_t pci_cfg_value;
 
-	uint8_t work_found;
-#define LPFC_MAX_WORKER_ITERATION  4
-
 	uint8_t fc_linkspeed;	/* Link speed after last READ_LA */
 
 	uint32_t fc_eventTag;	/* event tag for link attention */
@@ -489,8 +490,9 @@
 	uint32_t              work_hs;      /* HS stored in case of ERRAT */
 	uint32_t              work_status[2]; /* Extra status from SLIM */
 
-	wait_queue_head_t    *work_wait;
+	wait_queue_head_t    work_waitq;
 	struct task_struct   *worker_thread;
+	long data_flags;
 
 	uint32_t hbq_in_use;		/* HBQs in use flag */
 	struct list_head hbqbuf_in_list;  /* in-fly hbq buffer list */
@@ -637,6 +639,17 @@
 		phba->link_state == LPFC_HBA_READY;
 }
 
+static inline void
+lpfc_worker_wake_up(struct lpfc_hba *phba)
+{
+	/* Set the lpfc data pending flag */
+	set_bit(LPFC_DATA_READY, &phba->data_flags);
+
+	/* Wake up worker thread */
+	wake_up(&phba->work_waitq);
+	return;
+}
+
 #define FC_REG_DUMP_EVENT		0x10	/* Register for Dump events */
 #define FC_REG_TEMPERATURE_EVENT	0x20    /* Register for temperature
 						   event */
diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c
index 960baaf..37bfa0b 100644
--- a/drivers/scsi/lpfc/lpfc_attr.c
+++ b/drivers/scsi/lpfc/lpfc_attr.c
@@ -1995,8 +1995,7 @@
 		/* Don't allow mailbox commands to be sent when blocked
 		 * or when in the middle of discovery
 		 */
-		if (phba->sli.sli_flag & LPFC_BLOCK_MGMT_IO ||
-		    vport->fc_flag & FC_NDISC_ACTIVE) {
+		if (phba->sli.sli_flag & LPFC_BLOCK_MGMT_IO) {
 			sysfs_mbox_idle(phba);
 			spin_unlock_irq(&phba->hbalock);
 			return  -EAGAIN;
diff --git a/drivers/scsi/lpfc/lpfc_crtn.h b/drivers/scsi/lpfc/lpfc_crtn.h
index 7c9f831..1b82452 100644
--- a/drivers/scsi/lpfc/lpfc_crtn.h
+++ b/drivers/scsi/lpfc/lpfc_crtn.h
@@ -142,7 +142,7 @@
 int lpfc_hba_down_prep(struct lpfc_hba *);
 int lpfc_hba_down_post(struct lpfc_hba *);
 void lpfc_hba_init(struct lpfc_hba *, uint32_t *);
-int lpfc_post_buffer(struct lpfc_hba *, struct lpfc_sli_ring *, int, int);
+int lpfc_post_buffer(struct lpfc_hba *, struct lpfc_sli_ring *, int);
 void lpfc_decode_firmware_rev(struct lpfc_hba *, char *, int);
 int lpfc_online(struct lpfc_hba *);
 void lpfc_unblock_mgmt_io(struct lpfc_hba *);
@@ -263,6 +263,7 @@
 extern int lpfc_enable_npiv;
 
 int  lpfc_vport_symbolic_node_name(struct lpfc_vport *, char *, size_t);
+int  lpfc_vport_symbolic_port_name(struct lpfc_vport *, char *,	size_t);
 void lpfc_terminate_rport_io(struct fc_rport *);
 void lpfc_dev_loss_tmo_callbk(struct fc_rport *rport);
 
diff --git a/drivers/scsi/lpfc/lpfc_ct.c b/drivers/scsi/lpfc/lpfc_ct.c
index 153afae..7fc74cf 100644
--- a/drivers/scsi/lpfc/lpfc_ct.c
+++ b/drivers/scsi/lpfc/lpfc_ct.c
@@ -101,7 +101,7 @@
 		/* Not enough posted buffers; Try posting more buffers */
 		phba->fc_stat.NoRcvBuf++;
 		if (!(phba->sli3_options & LPFC_SLI3_HBQ_ENABLED))
-			lpfc_post_buffer(phba, pring, 2, 1);
+			lpfc_post_buffer(phba, pring, 2);
 		return;
 	}
 
@@ -151,7 +151,7 @@
 			}
 			list_del(&iocbq->list);
 			lpfc_sli_release_iocbq(phba, iocbq);
-			lpfc_post_buffer(phba, pring, i, 1);
+			lpfc_post_buffer(phba, pring, i);
 		}
 	}
 }
@@ -990,7 +990,7 @@
 	return;
 }
 
-static int
+int
 lpfc_vport_symbolic_port_name(struct lpfc_vport *vport, char *symbol,
 	size_t size)
 {
@@ -1679,20 +1679,18 @@
 {
 	struct lpfc_vport *vport = (struct lpfc_vport *)ptr;
 	struct lpfc_hba   *phba = vport->phba;
+	uint32_t tmo_posted;
 	unsigned long iflag;
 
 	spin_lock_irqsave(&vport->work_port_lock, iflag);
-	if (!(vport->work_port_events & WORKER_FDMI_TMO)) {
+	tmo_posted = vport->work_port_events & WORKER_FDMI_TMO;
+	if (!tmo_posted)
 		vport->work_port_events |= WORKER_FDMI_TMO;
-		spin_unlock_irqrestore(&vport->work_port_lock, iflag);
+	spin_unlock_irqrestore(&vport->work_port_lock, iflag);
 
-		spin_lock_irqsave(&phba->hbalock, iflag);
-		if (phba->work_wait)
-			lpfc_worker_wake_up(phba);
-		spin_unlock_irqrestore(&phba->hbalock, iflag);
-	}
-	else
-		spin_unlock_irqrestore(&vport->work_port_lock, iflag);
+	if (!tmo_posted)
+		lpfc_worker_wake_up(phba);
+	return;
 }
 
 void
diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c
index 886c5f1..f54e0f7 100644
--- a/drivers/scsi/lpfc/lpfc_els.c
+++ b/drivers/scsi/lpfc/lpfc_els.c
@@ -1754,29 +1754,34 @@
 	struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
 	struct lpfc_work_evt *evtp;
 
+	if (!(nlp->nlp_flag & NLP_DELAY_TMO))
+		return;
 	spin_lock_irq(shost->host_lock);
 	nlp->nlp_flag &= ~NLP_DELAY_TMO;
 	spin_unlock_irq(shost->host_lock);
 	del_timer_sync(&nlp->nlp_delayfunc);
 	nlp->nlp_last_elscmd = 0;
-
 	if (!list_empty(&nlp->els_retry_evt.evt_listp)) {
 		list_del_init(&nlp->els_retry_evt.evt_listp);
 		/* Decrement nlp reference count held for the delayed retry */
 		evtp = &nlp->els_retry_evt;
 		lpfc_nlp_put((struct lpfc_nodelist *)evtp->evt_arg1);
 	}
-
 	if (nlp->nlp_flag & NLP_NPR_2B_DISC) {
 		spin_lock_irq(shost->host_lock);
 		nlp->nlp_flag &= ~NLP_NPR_2B_DISC;
 		spin_unlock_irq(shost->host_lock);
 		if (vport->num_disc_nodes) {
-			/* Check to see if there are more
-			 * PLOGIs to be sent
-			 */
-			lpfc_more_plogi(vport);
-
+			if (vport->port_state < LPFC_VPORT_READY) {
+				/* Check if there are more ADISCs to be sent */
+				lpfc_more_adisc(vport);
+				if ((vport->num_disc_nodes == 0) &&
+				    (vport->fc_npr_cnt))
+					lpfc_els_disc_plogi(vport);
+			} else {
+				/* Check if there are more PLOGIs to be sent */
+				lpfc_more_plogi(vport);
+			}
 			if (vport->num_disc_nodes == 0) {
 				spin_lock_irq(shost->host_lock);
 				vport->fc_flag &= ~FC_NDISC_ACTIVE;
@@ -1798,10 +1803,6 @@
 	unsigned long flags;
 	struct lpfc_work_evt  *evtp = &ndlp->els_retry_evt;
 
-	ndlp = (struct lpfc_nodelist *) ptr;
-	phba = ndlp->vport->phba;
-	evtp = &ndlp->els_retry_evt;
-
 	spin_lock_irqsave(&phba->hbalock, flags);
 	if (!list_empty(&evtp->evt_listp)) {
 		spin_unlock_irqrestore(&phba->hbalock, flags);
@@ -1812,11 +1813,11 @@
 	 * count until the queued work is done
 	 */
 	evtp->evt_arg1  = lpfc_nlp_get(ndlp);
-	evtp->evt       = LPFC_EVT_ELS_RETRY;
-	list_add_tail(&evtp->evt_listp, &phba->work_list);
-	if (phba->work_wait)
+	if (evtp->evt_arg1) {
+		evtp->evt = LPFC_EVT_ELS_RETRY;
+		list_add_tail(&evtp->evt_listp, &phba->work_list);
 		lpfc_worker_wake_up(phba);
-
+	}
 	spin_unlock_irqrestore(&phba->hbalock, flags);
 	return;
 }
@@ -2761,10 +2762,11 @@
 	npr = (PRLI *) pcmd;
 	vpd = &phba->vpd;
 	/*
-	 * If our firmware version is 3.20 or later,
-	 * set the following bits for FC-TAPE support.
+	 * If the remote port is a target and our firmware version is 3.20 or
+	 * later, set the following bits for FC-TAPE support.
 	 */
-	if (vpd->rev.feaLevelHigh >= 0x02) {
+	if ((ndlp->nlp_type & NLP_FCP_TARGET) &&
+	    (vpd->rev.feaLevelHigh >= 0x02)) {
 		npr->ConfmComplAllowed = 1;
 		npr->Retry = 1;
 		npr->TaskRetryIdReq = 1;
@@ -3056,27 +3058,16 @@
 {
 	struct lpfc_nodelist *ndlp = NULL;
 
-	/* Look at all nodes effected by pending RSCNs and move
-	 * them to NPR state.
-	 */
-
+	/* Move all affected nodes by pending RSCNs to NPR state. */
 	list_for_each_entry(ndlp, &vport->fc_nodes, nlp_listp) {
 		if (!NLP_CHK_NODE_ACT(ndlp) ||
-		    ndlp->nlp_state == NLP_STE_UNUSED_NODE ||
-		    lpfc_rscn_payload_check(vport, ndlp->nlp_DID) == 0)
+		    (ndlp->nlp_state == NLP_STE_UNUSED_NODE) ||
+		    !lpfc_rscn_payload_check(vport, ndlp->nlp_DID))
 			continue;
-
 		lpfc_disc_state_machine(vport, ndlp, NULL,
-						NLP_EVT_DEVICE_RECOVERY);
-
-		/*
-		 * Make sure NLP_DELAY_TMO is NOT running after a device
-		 * recovery event.
-		 */
-		if (ndlp->nlp_flag & NLP_DELAY_TMO)
-			lpfc_cancel_retry_delay_tmo(vport, ndlp);
+					NLP_EVT_DEVICE_RECOVERY);
+		lpfc_cancel_retry_delay_tmo(vport, ndlp);
 	}
-
 	return 0;
 }
 
@@ -3781,91 +3772,27 @@
 lpfc_els_rcv_fan(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
 		 struct lpfc_nodelist *fan_ndlp)
 {
-	struct lpfc_dmabuf *pcmd;
-	uint32_t *lp;
-	IOCB_t *icmd;
-	uint32_t cmd, did;
-	FAN *fp;
-	struct lpfc_nodelist *ndlp, *next_ndlp;
 	struct lpfc_hba *phba = vport->phba;
+	uint32_t *lp;
+	FAN *fp;
 
-	/* FAN received */
-	lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
-			 "0265 FAN received\n");
-	icmd = &cmdiocb->iocb;
-	did = icmd->un.elsreq64.remoteID;
-	pcmd = (struct lpfc_dmabuf *)cmdiocb->context2;
-	lp = (uint32_t *)pcmd->virt;
-
-	cmd = *lp++;
-	fp = (FAN *) lp;
-
+	lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS, "0265 FAN received\n");
+	lp = (uint32_t *)((struct lpfc_dmabuf *)cmdiocb->context2)->virt;
+	fp = (FAN *) ++lp;
 	/* FAN received; Fan does not have a reply sequence */
-
-	if (phba->pport->port_state == LPFC_LOCAL_CFG_LINK) {
+	if ((vport == phba->pport) &&
+	    (vport->port_state == LPFC_LOCAL_CFG_LINK)) {
 		if ((memcmp(&phba->fc_fabparam.nodeName, &fp->FnodeName,
-			sizeof(struct lpfc_name)) != 0) ||
+			    sizeof(struct lpfc_name))) ||
 		    (memcmp(&phba->fc_fabparam.portName, &fp->FportName,
-			sizeof(struct lpfc_name)) != 0)) {
-			/*
-			 * This node has switched fabrics.  FLOGI is required
-			 * Clean up the old rpi's
-			 */
-
-			list_for_each_entry_safe(ndlp, next_ndlp,
-						 &vport->fc_nodes, nlp_listp) {
-				if (!NLP_CHK_NODE_ACT(ndlp))
-					continue;
-				if (ndlp->nlp_state != NLP_STE_NPR_NODE)
-					continue;
-				if (ndlp->nlp_type & NLP_FABRIC) {
-					/*
-					 * Clean up old Fabric, Nameserver and
-					 * other NLP_FABRIC logins
-					 */
-					lpfc_drop_node(vport, ndlp);
-
-				} else if (!(ndlp->nlp_flag & NLP_NPR_ADISC)) {
-					/* Fail outstanding I/O now since this
-					 * device is marked for PLOGI
-					 */
-					lpfc_unreg_rpi(vport, ndlp);
-				}
-			}
-
+			    sizeof(struct lpfc_name)))) {
+			/* This port has switched fabrics. FLOGI is required */
 			lpfc_initial_flogi(vport);
-			return 0;
+		} else {
+			/* FAN verified - skip FLOGI */
+			vport->fc_myDID = vport->fc_prevDID;
+			lpfc_issue_fabric_reglogin(vport);
 		}
-		/* Discovery not needed,
-		 * move the nodes to their original state.
-		 */
-		list_for_each_entry_safe(ndlp, next_ndlp, &vport->fc_nodes,
-					 nlp_listp) {
-			if (!NLP_CHK_NODE_ACT(ndlp))
-				continue;
-			if (ndlp->nlp_state != NLP_STE_NPR_NODE)
-				continue;
-
-			switch (ndlp->nlp_prev_state) {
-			case NLP_STE_UNMAPPED_NODE:
-				ndlp->nlp_prev_state = NLP_STE_NPR_NODE;
-				lpfc_nlp_set_state(vport, ndlp,
-						   NLP_STE_UNMAPPED_NODE);
-				break;
-
-			case NLP_STE_MAPPED_NODE:
-				ndlp->nlp_prev_state = NLP_STE_NPR_NODE;
-				lpfc_nlp_set_state(vport, ndlp,
-						   NLP_STE_MAPPED_NODE);
-				break;
-
-			default:
-				break;
-			}
-		}
-
-		/* Start discovery - this should just do CLEAR_LA */
-		lpfc_disc_start(vport);
 	}
 	return 0;
 }
@@ -3875,20 +3802,17 @@
 {
 	struct lpfc_vport *vport = (struct lpfc_vport *) ptr;
 	struct lpfc_hba   *phba = vport->phba;
+	uint32_t tmo_posted;
 	unsigned long iflag;
 
 	spin_lock_irqsave(&vport->work_port_lock, iflag);
-	if ((vport->work_port_events & WORKER_ELS_TMO) == 0) {
+	tmo_posted = vport->work_port_events & WORKER_ELS_TMO;
+	if (!tmo_posted)
 		vport->work_port_events |= WORKER_ELS_TMO;
-		spin_unlock_irqrestore(&vport->work_port_lock, iflag);
+	spin_unlock_irqrestore(&vport->work_port_lock, iflag);
 
-		spin_lock_irqsave(&phba->hbalock, iflag);
-		if (phba->work_wait)
-			lpfc_worker_wake_up(phba);
-		spin_unlock_irqrestore(&phba->hbalock, iflag);
-	}
-	else
-		spin_unlock_irqrestore(&vport->work_port_lock, iflag);
+	if (!tmo_posted)
+		lpfc_worker_wake_up(phba);
 	return;
 }
 
@@ -3933,9 +3857,6 @@
 		    els_command == ELS_CMD_FDISC)
 			continue;
 
-		if (vport != piocb->vport)
-			continue;
-
 		if (piocb->drvrTimeout > 0) {
 			if (piocb->drvrTimeout >= timeout)
 				piocb->drvrTimeout -= timeout;
@@ -4089,7 +4010,7 @@
 	payload = ((struct lpfc_dmabuf *)elsiocb->context2)->virt;
 	cmd = *payload;
 	if ((phba->sli3_options & LPFC_SLI3_HBQ_ENABLED) == 0)
-		lpfc_post_buffer(phba, pring, 1, 1);
+		lpfc_post_buffer(phba, pring, 1);
 
 	did = icmd->un.rcvels.remoteID;
 	if (icmd->ulpStatus) {
@@ -4398,7 +4319,7 @@
 		phba->fc_stat.NoRcvBuf++;
 		/* Not enough posted buffers; Try posting more buffers */
 		if (!(phba->sli3_options & LPFC_SLI3_HBQ_ENABLED))
-			lpfc_post_buffer(phba, pring, 0, 1);
+			lpfc_post_buffer(phba, pring, 0);
 		return;
 	}
 
@@ -4842,18 +4763,16 @@
 	struct lpfc_hba  *phba = (struct lpfc_hba *) ptr;
 	unsigned long iflags;
 	uint32_t tmo_posted;
+
 	spin_lock_irqsave(&phba->pport->work_port_lock, iflags);
 	tmo_posted = phba->pport->work_port_events & WORKER_FABRIC_BLOCK_TMO;
 	if (!tmo_posted)
 		phba->pport->work_port_events |= WORKER_FABRIC_BLOCK_TMO;
 	spin_unlock_irqrestore(&phba->pport->work_port_lock, iflags);
 
-	if (!tmo_posted) {
-		spin_lock_irqsave(&phba->hbalock, iflags);
-		if (phba->work_wait)
-			lpfc_worker_wake_up(phba);
-		spin_unlock_irqrestore(&phba->hbalock, iflags);
-	}
+	if (!tmo_posted)
+		lpfc_worker_wake_up(phba);
+	return;
 }
 
 static void
diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c
index 7cb68fe..a98d11b 100644
--- a/drivers/scsi/lpfc/lpfc_hbadisc.c
+++ b/drivers/scsi/lpfc/lpfc_hbadisc.c
@@ -153,11 +153,11 @@
 	 * count until this queued work is done
 	 */
 	evtp->evt_arg1  = lpfc_nlp_get(ndlp);
-	evtp->evt       = LPFC_EVT_DEV_LOSS;
-	list_add_tail(&evtp->evt_listp, &phba->work_list);
-	if (phba->work_wait)
-		wake_up(phba->work_wait);
-
+	if (evtp->evt_arg1) {
+		evtp->evt = LPFC_EVT_DEV_LOSS;
+		list_add_tail(&evtp->evt_listp, &phba->work_list);
+		lpfc_worker_wake_up(phba);
+	}
 	spin_unlock_irq(&phba->hbalock);
 
 	return;
@@ -276,14 +276,6 @@
 		lpfc_disc_state_machine(vport, ndlp, NULL, NLP_EVT_DEVICE_RM);
 }
 
-
-void
-lpfc_worker_wake_up(struct lpfc_hba *phba)
-{
-	wake_up(phba->work_wait);
-	return;
-}
-
 static void
 lpfc_work_list_done(struct lpfc_hba *phba)
 {
@@ -429,6 +421,8 @@
 		|| (pring->flag & LPFC_DEFERRED_RING_EVENT)) {
 		if (pring->flag & LPFC_STOP_IOCB_EVENT) {
 			pring->flag |= LPFC_DEFERRED_RING_EVENT;
+			/* Set the lpfc data pending flag */
+			set_bit(LPFC_DATA_READY, &phba->data_flags);
 		} else {
 			pring->flag &= ~LPFC_DEFERRED_RING_EVENT;
 			lpfc_sli_handle_slow_ring_event(phba, pring,
@@ -459,69 +453,29 @@
 	lpfc_work_list_done(phba);
 }
 
-static int
-check_work_wait_done(struct lpfc_hba *phba)
-{
-	struct lpfc_vport *vport;
-	struct lpfc_sli_ring *pring = &phba->sli.ring[LPFC_ELS_RING];
-	int rc = 0;
-
-	spin_lock_irq(&phba->hbalock);
-	list_for_each_entry(vport, &phba->port_list, listentry) {
-		if (vport->work_port_events) {
-			rc = 1;
-			break;
-		}
-	}
-	if (rc || phba->work_ha || (!list_empty(&phba->work_list)) ||
-	    kthread_should_stop() || pring->flag & LPFC_DEFERRED_RING_EVENT) {
-		rc = 1;
-		phba->work_found++;
-	} else
-		phba->work_found = 0;
-	spin_unlock_irq(&phba->hbalock);
-	return rc;
-}
-
-
 int
 lpfc_do_work(void *p)
 {
 	struct lpfc_hba *phba = p;
 	int rc;
-	DECLARE_WAIT_QUEUE_HEAD_ONSTACK(work_waitq);
 
 	set_user_nice(current, -20);
-	phba->work_wait = &work_waitq;
-	phba->work_found = 0;
+	phba->data_flags = 0;
 
 	while (1) {
-
-		rc = wait_event_interruptible(work_waitq,
-					      check_work_wait_done(phba));
-
+		/* wait and check worker queue activities */
+		rc = wait_event_interruptible(phba->work_waitq,
+					(test_and_clear_bit(LPFC_DATA_READY,
+							    &phba->data_flags)
+					 || kthread_should_stop()));
 		BUG_ON(rc);
 
 		if (kthread_should_stop())
 			break;
 
+		/* Attend pending lpfc data processing */
 		lpfc_work_done(phba);
-
-		/* If there is alot of slow ring work, like during link up
-		 * check_work_wait_done() may cause this thread to not give
-		 * up the CPU for very long periods of time. This may cause
-		 * soft lockups or other problems. To avoid these situations
-		 * give up the CPU here after LPFC_MAX_WORKER_ITERATION
-		 * consecutive iterations.
-		 */
-		if (phba->work_found >= LPFC_MAX_WORKER_ITERATION) {
-			phba->work_found = 0;
-			schedule();
-		}
 	}
-	spin_lock_irq(&phba->hbalock);
-	phba->work_wait = NULL;
-	spin_unlock_irq(&phba->hbalock);
 	return 0;
 }
 
@@ -551,10 +505,10 @@
 
 	spin_lock_irqsave(&phba->hbalock, flags);
 	list_add_tail(&evtp->evt_listp, &phba->work_list);
-	if (phba->work_wait)
-		lpfc_worker_wake_up(phba);
 	spin_unlock_irqrestore(&phba->hbalock, flags);
 
+	lpfc_worker_wake_up(phba);
+
 	return 1;
 }
 
@@ -963,6 +917,10 @@
 	if (phba->fc_topology == TOPOLOGY_LOOP) {
 		phba->sli3_options &= ~LPFC_SLI3_NPIV_ENABLED;
 
+		if (phba->cfg_enable_npiv)
+			lpfc_printf_log(phba, KERN_ERR, LOG_LINK_EVENT,
+				"1309 Link Up Event npiv not supported in loop "
+				"topology\n");
 				/* Get Loop Map information */
 		if (la->il)
 			vport->fc_flag |= FC_LBIT;
@@ -1087,6 +1045,8 @@
 	MAILBOX_t *mb = &pmb->mb;
 	struct lpfc_dmabuf *mp = (struct lpfc_dmabuf *) (pmb->context1);
 
+	/* Unblock ELS traffic */
+	phba->sli.ring[LPFC_ELS_RING].flag &= ~LPFC_STOP_IOCB_EVENT;
 	/* Check for error */
 	if (mb->mbxStatus) {
 		lpfc_printf_log(phba, KERN_INFO, LOG_LINK_EVENT,
@@ -1650,7 +1610,6 @@
 		ndlp->nlp_DID, old_state, state);
 
 	if (old_state == NLP_STE_NPR_NODE &&
-	    (ndlp->nlp_flag & NLP_DELAY_TMO) != 0 &&
 	    state != NLP_STE_NPR_NODE)
 		lpfc_cancel_retry_delay_tmo(vport, ndlp);
 	if (old_state == NLP_STE_UNMAPPED_NODE) {
@@ -1687,8 +1646,7 @@
 {
 	struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
 
-	if ((ndlp->nlp_flag & NLP_DELAY_TMO) != 0)
-		lpfc_cancel_retry_delay_tmo(vport, ndlp);
+	lpfc_cancel_retry_delay_tmo(vport, ndlp);
 	if (ndlp->nlp_state && !list_empty(&ndlp->nlp_listp))
 		lpfc_nlp_counters(vport, ndlp->nlp_state, -1);
 	spin_lock_irq(shost->host_lock);
@@ -1701,8 +1659,7 @@
 static void
 lpfc_disable_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
 {
-	if ((ndlp->nlp_flag & NLP_DELAY_TMO) != 0)
-		lpfc_cancel_retry_delay_tmo(vport, ndlp);
+	lpfc_cancel_retry_delay_tmo(vport, ndlp);
 	if (ndlp->nlp_state && !list_empty(&ndlp->nlp_listp))
 		lpfc_nlp_counters(vport, ndlp->nlp_state, -1);
 	lpfc_nlp_state_cleanup(vport, ndlp, ndlp->nlp_state,
@@ -2121,10 +2078,8 @@
 	ndlp->nlp_last_elscmd = 0;
 	del_timer_sync(&ndlp->nlp_delayfunc);
 
-	if (!list_empty(&ndlp->els_retry_evt.evt_listp))
-		list_del_init(&ndlp->els_retry_evt.evt_listp);
-	if (!list_empty(&ndlp->dev_loss_evt.evt_listp))
-		list_del_init(&ndlp->dev_loss_evt.evt_listp);
+	list_del_init(&ndlp->els_retry_evt.evt_listp);
+	list_del_init(&ndlp->dev_loss_evt.evt_listp);
 
 	lpfc_unreg_rpi(vport, ndlp);
 
@@ -2144,10 +2099,7 @@
 	LPFC_MBOXQ_t *mbox;
 	int rc;
 
-	if (ndlp->nlp_flag & NLP_DELAY_TMO) {
-		lpfc_cancel_retry_delay_tmo(vport, ndlp);
-	}
-
+	lpfc_cancel_retry_delay_tmo(vport, ndlp);
 	if (ndlp->nlp_flag & NLP_DEFER_RM && !ndlp->nlp_rpi) {
 		/* For this case we need to cleanup the default rpi
 		 * allocated by the firmware.
@@ -2317,8 +2269,7 @@
 			/* Since this node is marked for discovery,
 			 * delay timeout is not needed.
 			 */
-			if (ndlp->nlp_flag & NLP_DELAY_TMO)
-				lpfc_cancel_retry_delay_tmo(vport, ndlp);
+			lpfc_cancel_retry_delay_tmo(vport, ndlp);
 		} else
 			ndlp = NULL;
 	} else {
@@ -2643,21 +2594,20 @@
 {
 	struct lpfc_vport *vport = (struct lpfc_vport *) ptr;
 	struct lpfc_hba   *phba = vport->phba;
+	uint32_t tmo_posted;
 	unsigned long flags = 0;
 
 	if (unlikely(!phba))
 		return;
 
-	if ((vport->work_port_events & WORKER_DISC_TMO) == 0) {
-		spin_lock_irqsave(&vport->work_port_lock, flags);
+	spin_lock_irqsave(&vport->work_port_lock, flags);
+	tmo_posted = vport->work_port_events & WORKER_DISC_TMO;
+	if (!tmo_posted)
 		vport->work_port_events |= WORKER_DISC_TMO;
-		spin_unlock_irqrestore(&vport->work_port_lock, flags);
+	spin_unlock_irqrestore(&vport->work_port_lock, flags);
 
-		spin_lock_irqsave(&phba->hbalock, flags);
-		if (phba->work_wait)
-			lpfc_worker_wake_up(phba);
-		spin_unlock_irqrestore(&phba->hbalock, flags);
-	}
+	if (!tmo_posted)
+		lpfc_worker_wake_up(phba);
 	return;
 }
 
diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c
index fa757b2..5b6e539 100644
--- a/drivers/scsi/lpfc/lpfc_init.c
+++ b/drivers/scsi/lpfc/lpfc_init.c
@@ -145,8 +145,10 @@
 		return -ERESTART;
 	}
 
-	if (phba->sli_rev == 3 && !mb->un.varRdRev.v3rsp)
+	if (phba->sli_rev == 3 && !mb->un.varRdRev.v3rsp) {
+		mempool_free(pmb, phba->mbox_mem_pool);
 		return -EINVAL;
+	}
 
 	/* Save information as VPD data */
 	vp->rev.rBit = 1;
@@ -551,18 +553,18 @@
 lpfc_hb_timeout(unsigned long ptr)
 {
 	struct lpfc_hba *phba;
+	uint32_t tmo_posted;
 	unsigned long iflag;
 
 	phba = (struct lpfc_hba *)ptr;
 	spin_lock_irqsave(&phba->pport->work_port_lock, iflag);
-	if (!(phba->pport->work_port_events & WORKER_HB_TMO))
+	tmo_posted = phba->pport->work_port_events & WORKER_HB_TMO;
+	if (!tmo_posted)
 		phba->pport->work_port_events |= WORKER_HB_TMO;
 	spin_unlock_irqrestore(&phba->pport->work_port_lock, iflag);
 
-	spin_lock_irqsave(&phba->hbalock, iflag);
-	if (phba->work_wait)
-		wake_up(phba->work_wait);
-	spin_unlock_irqrestore(&phba->hbalock, iflag);
+	if (!tmo_posted)
+		lpfc_worker_wake_up(phba);
 	return;
 }
 
@@ -851,6 +853,8 @@
 	lpfc_read_la(phba, pmb, mp);
 	pmb->mbox_cmpl = lpfc_mbx_cmpl_read_la;
 	pmb->vport = vport;
+	/* Block ELS IOCBs until we have processed this mbox command */
+	phba->sli.ring[LPFC_ELS_RING].flag |= LPFC_STOP_IOCB_EVENT;
 	rc = lpfc_sli_issue_mbox (phba, pmb, MBX_NOWAIT);
 	if (rc == MBX_NOT_FINISHED) {
 		rc = 4;
@@ -866,6 +870,7 @@
 	return;
 
 lpfc_handle_latt_free_mbuf:
+	phba->sli.ring[LPFC_ELS_RING].flag &= ~LPFC_STOP_IOCB_EVENT;
 	lpfc_mbuf_free(phba, mp->virt, mp->phys);
 lpfc_handle_latt_free_mp:
 	kfree(mp);
@@ -1194,8 +1199,7 @@
 /*   Returns the number of buffers NOT posted.    */
 /**************************************************/
 int
-lpfc_post_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, int cnt,
-		 int type)
+lpfc_post_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, int cnt)
 {
 	IOCB_t *icmd;
 	struct lpfc_iocbq *iocb;
@@ -1295,7 +1299,7 @@
 	struct lpfc_sli *psli = &phba->sli;
 
 	/* Ring 0, ELS / CT buffers */
-	lpfc_post_buffer(phba, &psli->ring[LPFC_ELS_RING], LPFC_BUF_RING0, 1);
+	lpfc_post_buffer(phba, &psli->ring[LPFC_ELS_RING], LPFC_BUF_RING0);
 	/* Ring 2 - FCP no buffers needed */
 
 	return 0;
@@ -1454,6 +1458,15 @@
 
 		lpfc_disc_state_machine(vport, ndlp, NULL,
 					     NLP_EVT_DEVICE_RM);
+
+		/* nlp_type zero is not defined, nlp_flag zero also not defined,
+		 * nlp_state is unused, this happens when
+		 * an initiator has logged
+		 * into us so cleanup this ndlp.
+		 */
+		if ((ndlp->nlp_type == 0) && (ndlp->nlp_flag == 0) &&
+			(ndlp->nlp_state == 0))
+			lpfc_nlp_put(ndlp);
 	}
 
 	/* At this point, ALL ndlp's should be gone
@@ -2101,6 +2114,9 @@
 	phba->work_ha_mask = (HA_ERATT|HA_MBATT|HA_LATT);
 	phba->work_ha_mask |= (HA_RXMASK << (LPFC_ELS_RING * 4));
 
+	/* Initialize the wait queue head for the kernel thread */
+	init_waitqueue_head(&phba->work_waitq);
+
 	/* Startup the kernel thread for this host adapter. */
 	phba->worker_thread = kthread_run(lpfc_do_work, phba,
 				       "lpfc_worker_%d", phba->brd_no);
diff --git a/drivers/scsi/lpfc/lpfc_nportdisc.c b/drivers/scsi/lpfc/lpfc_nportdisc.c
index d08c4c8..6688a86 100644
--- a/drivers/scsi/lpfc/lpfc_nportdisc.c
+++ b/drivers/scsi/lpfc/lpfc_nportdisc.c
@@ -235,10 +235,7 @@
 			(iocb->iocb_cmpl) (phba, iocb, iocb);
 		}
 	}
-
-	/* If we are delaying issuing an ELS command, cancel it */
-	if (ndlp->nlp_flag & NLP_DELAY_TMO)
-		lpfc_cancel_retry_delay_tmo(phba->pport, ndlp);
+	lpfc_cancel_retry_delay_tmo(phba->pport, ndlp);
 	return 0;
 }
 
@@ -249,7 +246,6 @@
 	struct Scsi_Host   *shost = lpfc_shost_from_vport(vport);
 	struct lpfc_hba    *phba = vport->phba;
 	struct lpfc_dmabuf *pcmd;
-	struct lpfc_work_evt *evtp;
 	uint32_t *lp;
 	IOCB_t *icmd;
 	struct serv_parm *sp;
@@ -425,73 +421,8 @@
 			ndlp, mbox);
 		return 1;
 	}
-
-	/* If the remote NPort logs into us, before we can initiate
-	 * discovery to them, cleanup the NPort from discovery accordingly.
-	 */
-	if (ndlp->nlp_state == NLP_STE_NPR_NODE) {
-		spin_lock_irq(shost->host_lock);
-		ndlp->nlp_flag &= ~NLP_DELAY_TMO;
-		spin_unlock_irq(shost->host_lock);
-		del_timer_sync(&ndlp->nlp_delayfunc);
-		ndlp->nlp_last_elscmd = 0;
-
-		if (!list_empty(&ndlp->els_retry_evt.evt_listp)) {
-			list_del_init(&ndlp->els_retry_evt.evt_listp);
-			/* Decrement ndlp reference count held for the
-			 * delayed retry
-			 */
-			evtp = &ndlp->els_retry_evt;
-			lpfc_nlp_put((struct lpfc_nodelist *)evtp->evt_arg1);
-		}
-
-		if (ndlp->nlp_flag & NLP_NPR_2B_DISC) {
-			spin_lock_irq(shost->host_lock);
-			ndlp->nlp_flag &= ~NLP_NPR_2B_DISC;
-			spin_unlock_irq(shost->host_lock);
-
-			if ((ndlp->nlp_flag & NLP_ADISC_SND) &&
-			    (vport->num_disc_nodes)) {
-				/* Check to see if there are more
-				 * ADISCs to be sent
-				 */
-				lpfc_more_adisc(vport);
-
-				if ((vport->num_disc_nodes == 0) &&
-					(vport->fc_npr_cnt))
-					lpfc_els_disc_plogi(vport);
-
-				if (vport->num_disc_nodes == 0) {
-					spin_lock_irq(shost->host_lock);
-					vport->fc_flag &= ~FC_NDISC_ACTIVE;
-					spin_unlock_irq(shost->host_lock);
-					lpfc_can_disctmo(vport);
-					lpfc_end_rscn(vport);
-				}
-			}
-		}
-	} else if ((ndlp->nlp_state == NLP_STE_PLOGI_ISSUE) &&
-		   (ndlp->nlp_flag & NLP_NPR_2B_DISC) &&
-		   (vport->num_disc_nodes)) {
-		spin_lock_irq(shost->host_lock);
-		ndlp->nlp_flag &= ~NLP_NPR_2B_DISC;
-		spin_unlock_irq(shost->host_lock);
-		/* Check to see if there are more
-		 * PLOGIs to be sent
-		 */
-		lpfc_more_plogi(vport);
-		if (vport->num_disc_nodes == 0) {
-			spin_lock_irq(shost->host_lock);
-			vport->fc_flag &= ~FC_NDISC_ACTIVE;
-			spin_unlock_irq(shost->host_lock);
-			lpfc_can_disctmo(vport);
-			lpfc_end_rscn(vport);
-		}
-	}
-
 	lpfc_els_rsp_acc(vport, ELS_CMD_PLOGI, cmdiocb, ndlp, mbox);
 	return 1;
-
 out:
 	stat.un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC;
 	stat.un.b.lsRjtRsnCodeExp = LSEXP_OUT_OF_RESOURCE;
@@ -574,7 +505,9 @@
 	else
 		lpfc_els_rsp_acc(vport, ELS_CMD_ACC, cmdiocb, ndlp, NULL);
 
-	if (!(ndlp->nlp_type & NLP_FABRIC) ||
+	if ((!(ndlp->nlp_type & NLP_FABRIC) &&
+	     ((ndlp->nlp_type & NLP_FCP_TARGET) ||
+	      !(ndlp->nlp_type & NLP_FCP_INITIATOR))) ||
 	    (ndlp->nlp_state == NLP_STE_ADISC_ISSUE)) {
 		/* Only try to re-login if this is NOT a Fabric Node */
 		mod_timer(&ndlp->nlp_delayfunc, jiffies + HZ * 1);
@@ -751,6 +684,7 @@
 lpfc_rcv_plogi_plogi_issue(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
 			   void *arg, uint32_t evt)
 {
+	struct Scsi_Host   *shost = lpfc_shost_from_vport(vport);
 	struct lpfc_hba   *phba = vport->phba;
 	struct lpfc_iocbq *cmdiocb = arg;
 	struct lpfc_dmabuf *pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
@@ -776,7 +710,22 @@
 		lpfc_els_rsp_reject(vport, stat.un.lsRjtError, cmdiocb, ndlp,
 			NULL);
 	} else {
-		lpfc_rcv_plogi(vport, ndlp, cmdiocb);
+		if (lpfc_rcv_plogi(vport, ndlp, cmdiocb) &&
+		    (ndlp->nlp_flag & NLP_NPR_2B_DISC) &&
+		    (vport->num_disc_nodes)) {
+			spin_lock_irq(shost->host_lock);
+			ndlp->nlp_flag &= ~NLP_NPR_2B_DISC;
+			spin_unlock_irq(shost->host_lock);
+			/* Check if there are more PLOGIs to be sent */
+			lpfc_more_plogi(vport);
+			if (vport->num_disc_nodes == 0) {
+				spin_lock_irq(shost->host_lock);
+				vport->fc_flag &= ~FC_NDISC_ACTIVE;
+				spin_unlock_irq(shost->host_lock);
+				lpfc_can_disctmo(vport);
+				lpfc_end_rscn(vport);
+			}
+		}
 	} /* If our portname was less */
 
 	return ndlp->nlp_state;
@@ -1040,6 +989,7 @@
 lpfc_rcv_plogi_adisc_issue(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
 			   void *arg, uint32_t evt)
 {
+	struct Scsi_Host   *shost = lpfc_shost_from_vport(vport);
 	struct lpfc_hba   *phba = vport->phba;
 	struct lpfc_iocbq *cmdiocb;
 
@@ -1048,9 +998,28 @@
 
 	cmdiocb = (struct lpfc_iocbq *) arg;
 
-	if (lpfc_rcv_plogi(vport, ndlp, cmdiocb))
-		return ndlp->nlp_state;
+	if (lpfc_rcv_plogi(vport, ndlp, cmdiocb)) {
+		if (ndlp->nlp_flag & NLP_NPR_2B_DISC) {
+			spin_lock_irq(shost->host_lock);
+			ndlp->nlp_flag &= ~NLP_NPR_2B_DISC;
+			spin_unlock_irq(shost->host_lock);
 
+			if (vport->num_disc_nodes) {
+				lpfc_more_adisc(vport);
+				if ((vport->num_disc_nodes == 0) &&
+				    (vport->fc_npr_cnt))
+					lpfc_els_disc_plogi(vport);
+				if (vport->num_disc_nodes == 0) {
+					spin_lock_irq(shost->host_lock);
+					vport->fc_flag &= ~FC_NDISC_ACTIVE;
+					spin_unlock_irq(shost->host_lock);
+					lpfc_can_disctmo(vport);
+					lpfc_end_rscn(vport);
+				}
+			}
+		}
+		return ndlp->nlp_state;
+	}
 	ndlp->nlp_prev_state = NLP_STE_ADISC_ISSUE;
 	lpfc_issue_els_plogi(vport, ndlp->nlp_DID, 0);
 	lpfc_nlp_set_state(vport, ndlp, NLP_STE_PLOGI_ISSUE);
@@ -1742,24 +1711,21 @@
 	struct lpfc_iocbq *cmdiocb  = (struct lpfc_iocbq *) arg;
 
 	/* Ignore PLOGI if we have an outstanding LOGO */
-	if (ndlp->nlp_flag & (NLP_LOGO_SND | NLP_LOGO_ACC)) {
+	if (ndlp->nlp_flag & (NLP_LOGO_SND | NLP_LOGO_ACC))
 		return ndlp->nlp_state;
-	}
-
 	if (lpfc_rcv_plogi(vport, ndlp, cmdiocb)) {
+		lpfc_cancel_retry_delay_tmo(vport, ndlp);
 		spin_lock_irq(shost->host_lock);
-		ndlp->nlp_flag &= ~NLP_NPR_ADISC;
+		ndlp->nlp_flag &= ~(NLP_NPR_ADISC | NLP_NPR_2B_DISC);
 		spin_unlock_irq(shost->host_lock);
-		return ndlp->nlp_state;
+	} else if (!(ndlp->nlp_flag & NLP_NPR_2B_DISC)) {
+		/* send PLOGI immediately, move to PLOGI issue state */
+		if (!(ndlp->nlp_flag & NLP_DELAY_TMO)) {
+			ndlp->nlp_prev_state = NLP_STE_NPR_NODE;
+			lpfc_nlp_set_state(vport, ndlp, NLP_STE_PLOGI_ISSUE);
+			lpfc_issue_els_plogi(vport, ndlp->nlp_DID, 0);
+		}
 	}
-
-	/* send PLOGI immediately, move to PLOGI issue state */
-	if (!(ndlp->nlp_flag & NLP_DELAY_TMO)) {
-		ndlp->nlp_prev_state = NLP_STE_NPR_NODE;
-		lpfc_nlp_set_state(vport, ndlp, NLP_STE_PLOGI_ISSUE);
-		lpfc_issue_els_plogi(vport, ndlp->nlp_DID, 0);
-	}
-
 	return ndlp->nlp_state;
 }
 
@@ -1810,7 +1776,6 @@
 	struct lpfc_iocbq *cmdiocb = (struct lpfc_iocbq *) arg;
 
 	lpfc_rcv_padisc(vport, ndlp, cmdiocb);
-
 	/*
 	 * Do not start discovery if discovery is about to start
 	 * or discovery in progress for this node. Starting discovery
@@ -1973,9 +1938,7 @@
 	spin_lock_irq(shost->host_lock);
 	ndlp->nlp_flag &= ~(NLP_NODEV_REMOVE | NLP_NPR_2B_DISC);
 	spin_unlock_irq(shost->host_lock);
-	if (ndlp->nlp_flag & NLP_DELAY_TMO) {
-		lpfc_cancel_retry_delay_tmo(vport, ndlp);
-	}
+	lpfc_cancel_retry_delay_tmo(vport, ndlp);
 	return ndlp->nlp_state;
 }
 
diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c
index 0910a9a..c94da4f 100644
--- a/drivers/scsi/lpfc/lpfc_scsi.c
+++ b/drivers/scsi/lpfc/lpfc_scsi.c
@@ -50,6 +50,7 @@
 lpfc_adjust_queue_depth(struct lpfc_hba *phba)
 {
 	unsigned long flags;
+	uint32_t evt_posted;
 
 	spin_lock_irqsave(&phba->hbalock, flags);
 	atomic_inc(&phba->num_rsrc_err);
@@ -65,17 +66,13 @@
 	spin_unlock_irqrestore(&phba->hbalock, flags);
 
 	spin_lock_irqsave(&phba->pport->work_port_lock, flags);
-	if ((phba->pport->work_port_events &
-		WORKER_RAMP_DOWN_QUEUE) == 0) {
+	evt_posted = phba->pport->work_port_events & WORKER_RAMP_DOWN_QUEUE;
+	if (!evt_posted)
 		phba->pport->work_port_events |= WORKER_RAMP_DOWN_QUEUE;
-	}
 	spin_unlock_irqrestore(&phba->pport->work_port_lock, flags);
 
-	spin_lock_irqsave(&phba->hbalock, flags);
-	if (phba->work_wait)
-		wake_up(phba->work_wait);
-	spin_unlock_irqrestore(&phba->hbalock, flags);
-
+	if (!evt_posted)
+		lpfc_worker_wake_up(phba);
 	return;
 }
 
@@ -89,6 +86,7 @@
 {
 	unsigned long flags;
 	struct lpfc_hba *phba = vport->phba;
+	uint32_t evt_posted;
 	atomic_inc(&phba->num_cmd_success);
 
 	if (vport->cfg_lun_queue_depth <= sdev->queue_depth)
@@ -103,16 +101,14 @@
 	spin_unlock_irqrestore(&phba->hbalock, flags);
 
 	spin_lock_irqsave(&phba->pport->work_port_lock, flags);
-	if ((phba->pport->work_port_events &
-		WORKER_RAMP_UP_QUEUE) == 0) {
+	evt_posted = phba->pport->work_port_events & WORKER_RAMP_UP_QUEUE;
+	if (!evt_posted)
 		phba->pport->work_port_events |= WORKER_RAMP_UP_QUEUE;
-	}
 	spin_unlock_irqrestore(&phba->pport->work_port_lock, flags);
 
-	spin_lock_irqsave(&phba->hbalock, flags);
-	if (phba->work_wait)
-		wake_up(phba->work_wait);
-	spin_unlock_irqrestore(&phba->hbalock, flags);
+	if (!evt_posted)
+		lpfc_worker_wake_up(phba);
+	return;
 }
 
 void
@@ -609,9 +605,6 @@
 	result = cmd->result;
 	sdev = cmd->device;
 	lpfc_scsi_unprep_dma_buf(phba, lpfc_cmd);
-	spin_lock_irqsave(sdev->host->host_lock, flags);
-	lpfc_cmd->pCmd = NULL;	/* This must be done before scsi_done */
-	spin_unlock_irqrestore(sdev->host->host_lock, flags);
 	cmd->scsi_done(cmd);
 
 	if (phba->cfg_poll & ENABLE_FCP_RING_POLLING) {
@@ -620,6 +613,7 @@
 		 * wake up the thread.
 		 */
 		spin_lock_irqsave(sdev->host->host_lock, flags);
+		lpfc_cmd->pCmd = NULL;
 		if (lpfc_cmd->waitq)
 			wake_up(lpfc_cmd->waitq);
 		spin_unlock_irqrestore(sdev->host->host_lock, flags);
@@ -690,6 +684,7 @@
 	 * wake up the thread.
 	 */
 	spin_lock_irqsave(sdev->host->host_lock, flags);
+	lpfc_cmd->pCmd = NULL;
 	if (lpfc_cmd->waitq)
 		wake_up(lpfc_cmd->waitq);
 	spin_unlock_irqrestore(sdev->host->host_lock, flags);
@@ -849,14 +844,15 @@
 	struct lpfc_iocbq *iocbq;
 	struct lpfc_iocbq *iocbqrsp;
 	int ret;
+	int status;
 
 	if (!rdata->pnode || !NLP_CHK_NODE_ACT(rdata->pnode))
 		return FAILED;
 
 	lpfc_cmd->rdata = rdata;
-	ret = lpfc_scsi_prep_task_mgmt_cmd(vport, lpfc_cmd, lun,
+	status = lpfc_scsi_prep_task_mgmt_cmd(vport, lpfc_cmd, lun,
 					   FCP_TARGET_RESET);
-	if (!ret)
+	if (!status)
 		return FAILED;
 
 	iocbq = &lpfc_cmd->cur_iocbq;
@@ -869,12 +865,15 @@
 	lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP,
 			 "0702 Issue Target Reset to TGT %d Data: x%x x%x\n",
 			 tgt_id, rdata->pnode->nlp_rpi, rdata->pnode->nlp_flag);
-	ret = lpfc_sli_issue_iocb_wait(phba,
+	status = lpfc_sli_issue_iocb_wait(phba,
 				       &phba->sli.ring[phba->sli.fcp_ring],
 				       iocbq, iocbqrsp, lpfc_cmd->timeout);
-	if (ret != IOCB_SUCCESS) {
-		if (ret == IOCB_TIMEDOUT)
+	if (status != IOCB_SUCCESS) {
+		if (status == IOCB_TIMEDOUT) {
 			iocbq->iocb_cmpl = lpfc_tskmgmt_def_cmpl;
+			ret = TIMEOUT_ERROR;
+		} else
+			ret = FAILED;
 		lpfc_cmd->status = IOSTAT_DRIVER_REJECT;
 	} else {
 		ret = SUCCESS;
@@ -1142,121 +1141,96 @@
 	struct lpfc_iocbq *iocbq, *iocbqrsp;
 	struct lpfc_rport_data *rdata = cmnd->device->hostdata;
 	struct lpfc_nodelist *pnode = rdata->pnode;
-	uint32_t cmd_result = 0, cmd_status = 0;
-	int ret = FAILED;
-	int iocb_status = IOCB_SUCCESS;
-	int cnt, loopcnt;
+	unsigned long later;
+	int ret = SUCCESS;
+	int status;
+	int cnt;
 
 	lpfc_block_error_handler(cmnd);
-	loopcnt = 0;
 	/*
 	 * If target is not in a MAPPED state, delay the reset until
 	 * target is rediscovered or devloss timeout expires.
 	 */
-	while (1) {
+	later = msecs_to_jiffies(2 * vport->cfg_devloss_tmo * 1000) + jiffies;
+	while (time_after(later, jiffies)) {
 		if (!pnode || !NLP_CHK_NODE_ACT(pnode))
-			goto out;
-
-		if (pnode->nlp_state != NLP_STE_MAPPED_NODE) {
-			schedule_timeout_uninterruptible(msecs_to_jiffies(500));
-			loopcnt++;
-			rdata = cmnd->device->hostdata;
-			if (!rdata ||
-				(loopcnt > ((vport->cfg_devloss_tmo * 2) + 1))){
-				lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP,
-						 "0721 LUN Reset rport "
-						 "failure: cnt x%x rdata x%p\n",
-						 loopcnt, rdata);
-				goto out;
-			}
-			pnode = rdata->pnode;
-			if (!pnode || !NLP_CHK_NODE_ACT(pnode))
-				goto out;
-		}
+			return FAILED;
 		if (pnode->nlp_state == NLP_STE_MAPPED_NODE)
 			break;
+		schedule_timeout_uninterruptible(msecs_to_jiffies(500));
+		rdata = cmnd->device->hostdata;
+		if (!rdata)
+			break;
+		pnode = rdata->pnode;
 	}
-
+	if (!rdata || pnode->nlp_state != NLP_STE_MAPPED_NODE) {
+		lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP,
+				 "0721 LUN Reset rport "
+				 "failure: msec x%x rdata x%p\n",
+				 jiffies_to_msecs(jiffies - later), rdata);
+		return FAILED;
+	}
 	lpfc_cmd = lpfc_get_scsi_buf(phba);
 	if (lpfc_cmd == NULL)
-		goto out;
-
+		return FAILED;
 	lpfc_cmd->timeout = 60;
 	lpfc_cmd->rdata = rdata;
 
-	ret = lpfc_scsi_prep_task_mgmt_cmd(vport, lpfc_cmd, cmnd->device->lun,
-					   FCP_TARGET_RESET);
-	if (!ret)
-		goto out_free_scsi_buf;
-
+	status = lpfc_scsi_prep_task_mgmt_cmd(vport, lpfc_cmd,
+					      cmnd->device->lun,
+					      FCP_TARGET_RESET);
+	if (!status) {
+		lpfc_release_scsi_buf(phba, lpfc_cmd);
+		return FAILED;
+	}
 	iocbq = &lpfc_cmd->cur_iocbq;
 
 	/* get a buffer for this IOCB command response */
 	iocbqrsp = lpfc_sli_get_iocbq(phba);
-	if (iocbqrsp == NULL)
-		goto out_free_scsi_buf;
-
+	if (iocbqrsp == NULL) {
+		lpfc_release_scsi_buf(phba, lpfc_cmd);
+		return FAILED;
+	}
 	lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP,
 			 "0703 Issue target reset to TGT %d LUN %d "
 			 "rpi x%x nlp_flag x%x\n", cmnd->device->id,
 			 cmnd->device->lun, pnode->nlp_rpi, pnode->nlp_flag);
-	iocb_status = lpfc_sli_issue_iocb_wait(phba,
-				       &phba->sli.ring[phba->sli.fcp_ring],
-				       iocbq, iocbqrsp, lpfc_cmd->timeout);
-
-	if (iocb_status == IOCB_TIMEDOUT)
+	status = lpfc_sli_issue_iocb_wait(phba,
+					  &phba->sli.ring[phba->sli.fcp_ring],
+					  iocbq, iocbqrsp, lpfc_cmd->timeout);
+	if (status == IOCB_TIMEDOUT) {
 		iocbq->iocb_cmpl = lpfc_tskmgmt_def_cmpl;
-
-	if (iocb_status == IOCB_SUCCESS)
-		ret = SUCCESS;
-	else
-		ret = iocb_status;
-
-	cmd_result = iocbqrsp->iocb.un.ulpWord[4];
-	cmd_status = iocbqrsp->iocb.ulpStatus;
-
-	lpfc_sli_release_iocbq(phba, iocbqrsp);
-
-	/*
-	 * All outstanding txcmplq I/Os should have been aborted by the device.
-	 * Unfortunately, some targets do not abide by this forcing the driver
-	 * to double check.
-	 */
-	cnt = lpfc_sli_sum_iocb(vport, cmnd->device->id, cmnd->device->lun,
-				LPFC_CTX_LUN);
-	if (cnt)
-		lpfc_sli_abort_iocb(vport, &phba->sli.ring[phba->sli.fcp_ring],
-				    cmnd->device->id, cmnd->device->lun,
-				    LPFC_CTX_LUN);
-	loopcnt = 0;
-	while(cnt) {
-		schedule_timeout_uninterruptible(LPFC_RESET_WAIT*HZ);
-
-		if (++loopcnt
-		    > (2 * vport->cfg_devloss_tmo)/LPFC_RESET_WAIT)
-			break;
-
-		cnt = lpfc_sli_sum_iocb(vport, cmnd->device->id,
-					cmnd->device->lun, LPFC_CTX_LUN);
-	}
-
-	if (cnt) {
-		lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP,
-				 "0719 device reset I/O flush failure: "
-				 "cnt x%x\n", cnt);
-		ret = FAILED;
-	}
-
-out_free_scsi_buf:
-	if (iocb_status != IOCB_TIMEDOUT) {
+		ret = TIMEOUT_ERROR;
+	} else {
+		if (status != IOCB_SUCCESS)
+			ret = FAILED;
 		lpfc_release_scsi_buf(phba, lpfc_cmd);
 	}
 	lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP,
 			 "0713 SCSI layer issued device reset (%d, %d) "
 			 "return x%x status x%x result x%x\n",
 			 cmnd->device->id, cmnd->device->lun, ret,
-			 cmd_status, cmd_result);
-out:
+			 iocbqrsp->iocb.ulpStatus,
+			 iocbqrsp->iocb.un.ulpWord[4]);
+	lpfc_sli_release_iocbq(phba, iocbqrsp);
+	cnt = lpfc_sli_sum_iocb(vport, cmnd->device->id, cmnd->device->lun,
+				LPFC_CTX_TGT);
+	if (cnt)
+		lpfc_sli_abort_iocb(vport, &phba->sli.ring[phba->sli.fcp_ring],
+				    cmnd->device->id, cmnd->device->lun,
+				    LPFC_CTX_TGT);
+	later = msecs_to_jiffies(2 * vport->cfg_devloss_tmo * 1000) + jiffies;
+	while (time_after(later, jiffies) && cnt) {
+		schedule_timeout_uninterruptible(msecs_to_jiffies(20));
+		cnt = lpfc_sli_sum_iocb(vport, cmnd->device->id,
+					cmnd->device->lun, LPFC_CTX_TGT);
+	}
+	if (cnt) {
+		lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP,
+				 "0719 device reset I/O flush failure: "
+				 "cnt x%x\n", cnt);
+		ret = FAILED;
+	}
 	return ret;
 }
 
@@ -1268,19 +1242,12 @@
 	struct lpfc_hba   *phba = vport->phba;
 	struct lpfc_nodelist *ndlp = NULL;
 	int match;
-	int ret = FAILED, i, err_count = 0;
-	int cnt, loopcnt;
+	int ret = SUCCESS, status, i;
+	int cnt;
 	struct lpfc_scsi_buf * lpfc_cmd;
+	unsigned long later;
 
 	lpfc_block_error_handler(cmnd);
-
-	lpfc_cmd = lpfc_get_scsi_buf(phba);
-	if (lpfc_cmd == NULL)
-		goto out;
-
-	/* The lpfc_cmd storage is reused.  Set all loop invariants. */
-	lpfc_cmd->timeout = 60;
-
 	/*
 	 * Since the driver manages a single bus device, reset all
 	 * targets known to the driver.  Should any target reset
@@ -1294,7 +1261,7 @@
 			if (!NLP_CHK_NODE_ACT(ndlp))
 				continue;
 			if (ndlp->nlp_state == NLP_STE_MAPPED_NODE &&
-			    i == ndlp->nlp_sid &&
+			    ndlp->nlp_sid == i &&
 			    ndlp->rport) {
 				match = 1;
 				break;
@@ -1303,27 +1270,22 @@
 		spin_unlock_irq(shost->host_lock);
 		if (!match)
 			continue;
-
-		ret = lpfc_scsi_tgt_reset(lpfc_cmd, vport, i,
-					  cmnd->device->lun,
-					  ndlp->rport->dd_data);
-		if (ret != SUCCESS) {
+		lpfc_cmd = lpfc_get_scsi_buf(phba);
+		if (lpfc_cmd) {
+			lpfc_cmd->timeout = 60;
+			status = lpfc_scsi_tgt_reset(lpfc_cmd, vport, i,
+						     cmnd->device->lun,
+						     ndlp->rport->dd_data);
+			if (status != TIMEOUT_ERROR)
+				lpfc_release_scsi_buf(phba, lpfc_cmd);
+		}
+		if (!lpfc_cmd || status != SUCCESS) {
 			lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP,
 					 "0700 Bus Reset on target %d failed\n",
 					 i);
-			err_count++;
-			break;
+			ret = FAILED;
 		}
 	}
-
-	if (ret != IOCB_TIMEDOUT)
-		lpfc_release_scsi_buf(phba, lpfc_cmd);
-
-	if (err_count == 0)
-		ret = SUCCESS;
-	else
-		ret = FAILED;
-
 	/*
 	 * All outstanding txcmplq I/Os should have been aborted by
 	 * the targets.  Unfortunately, some targets do not abide by
@@ -1333,27 +1295,19 @@
 	if (cnt)
 		lpfc_sli_abort_iocb(vport, &phba->sli.ring[phba->sli.fcp_ring],
 				    0, 0, LPFC_CTX_HOST);
-	loopcnt = 0;
-	while(cnt) {
-		schedule_timeout_uninterruptible(LPFC_RESET_WAIT*HZ);
-
-		if (++loopcnt
-		    > (2 * vport->cfg_devloss_tmo)/LPFC_RESET_WAIT)
-			break;
-
+	later = msecs_to_jiffies(2 * vport->cfg_devloss_tmo * 1000) + jiffies;
+	while (time_after(later, jiffies) && cnt) {
+		schedule_timeout_uninterruptible(msecs_to_jiffies(20));
 		cnt = lpfc_sli_sum_iocb(vport, 0, 0, LPFC_CTX_HOST);
 	}
-
 	if (cnt) {
 		lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP,
 				 "0715 Bus Reset I/O flush failure: "
 				 "cnt x%x left x%x\n", cnt, i);
 		ret = FAILED;
 	}
-
 	lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP,
 			 "0714 SCSI layer issued Bus Reset Data: x%x\n", ret);
-out:
 	return ret;
 }
 
diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c
index 70a0a9e..f40aa7b 100644
--- a/drivers/scsi/lpfc/lpfc_sli.c
+++ b/drivers/scsi/lpfc/lpfc_sli.c
@@ -324,9 +324,7 @@
 			phba->work_ha |= HA_ERATT;
 			phba->work_hs = HS_FFER3;
 
-			/* hbalock should already be held */
-			if (phba->work_wait)
-				lpfc_worker_wake_up(phba);
+			lpfc_worker_wake_up(phba);
 
 			return NULL;
 		}
@@ -1309,9 +1307,7 @@
 	phba->work_ha |= HA_ERATT;
 	phba->work_hs = HS_FFER3;
 
-	/* hbalock should already be held */
-	if (phba->work_wait)
-		lpfc_worker_wake_up(phba);
+	lpfc_worker_wake_up(phba);
 
 	return;
 }
@@ -2611,12 +2607,9 @@
 		phba->pport->work_port_events |= WORKER_MBOX_TMO;
 	spin_unlock_irqrestore(&phba->pport->work_port_lock, iflag);
 
-	if (!tmo_posted) {
-		spin_lock_irqsave(&phba->hbalock, iflag);
-		if (phba->work_wait)
-			lpfc_worker_wake_up(phba);
-		spin_unlock_irqrestore(&phba->hbalock, iflag);
-	}
+	if (!tmo_posted)
+		lpfc_worker_wake_up(phba);
+	return;
 }
 
 void
@@ -3374,8 +3367,12 @@
 	for (i = 0; i < psli->num_rings; i++) {
 		pring = &psli->ring[i];
 		prev_pring_flag = pring->flag;
-		if (pring->ringno == LPFC_ELS_RING) /* Only slow rings */
+		/* Only slow rings */
+		if (pring->ringno == LPFC_ELS_RING) {
 			pring->flag |= LPFC_DEFERRED_RING_EVENT;
+			/* Set the lpfc data pending flag */
+			set_bit(LPFC_DATA_READY, &phba->data_flags);
+		}
 		/*
 		 * Error everything on the txq since these iocbs have not been
 		 * given to the FW yet.
@@ -3434,8 +3431,12 @@
 	spin_lock_irqsave(&phba->hbalock, flags);
 	for (i = 0; i < psli->num_rings; i++) {
 		pring = &psli->ring[i];
-		if (pring->ringno == LPFC_ELS_RING) /* Only slow rings */
+		/* Only slow rings */
+		if (pring->ringno == LPFC_ELS_RING) {
 			pring->flag |= LPFC_DEFERRED_RING_EVENT;
+			/* Set the lpfc data pending flag */
+			set_bit(LPFC_DATA_READY, &phba->data_flags);
+		}
 
 		/*
 		 * Error everything on the txq since these iocbs have not been
@@ -3762,7 +3763,6 @@
 			   lpfc_ctx_cmd ctx_cmd)
 {
 	struct lpfc_scsi_buf *lpfc_cmd;
-	struct scsi_cmnd *cmnd;
 	int rc = 1;
 
 	if (!(iocbq->iocb_flag &  LPFC_IO_FCP))
@@ -3772,19 +3772,20 @@
 		return rc;
 
 	lpfc_cmd = container_of(iocbq, struct lpfc_scsi_buf, cur_iocbq);
-	cmnd = lpfc_cmd->pCmd;
 
-	if (cmnd == NULL)
+	if (lpfc_cmd->pCmd == NULL)
 		return rc;
 
 	switch (ctx_cmd) {
 	case LPFC_CTX_LUN:
-		if ((cmnd->device->id == tgt_id) &&
-		    (cmnd->device->lun == lun_id))
+		if ((lpfc_cmd->rdata->pnode) &&
+		    (lpfc_cmd->rdata->pnode->nlp_sid == tgt_id) &&
+		    (scsilun_to_int(&lpfc_cmd->fcp_cmnd->fcp_lun) == lun_id))
 			rc = 0;
 		break;
 	case LPFC_CTX_TGT:
-		if (cmnd->device->id == tgt_id)
+		if ((lpfc_cmd->rdata->pnode) &&
+		    (lpfc_cmd->rdata->pnode->nlp_sid == tgt_id))
 			rc = 0;
 		break;
 	case LPFC_CTX_HOST:
@@ -3994,6 +3995,7 @@
 	if (pmboxq->context1)
 		return MBX_NOT_FINISHED;
 
+	pmboxq->mbox_flag &= ~LPFC_MBX_WAKE;
 	/* setup wake call as IOCB callback */
 	pmboxq->mbox_cmpl = lpfc_sli_wake_mbox_wait;
 	/* setup context field to pass wait_queue pointer to wake function  */
@@ -4159,7 +4161,7 @@
 						"pwork:x%x hawork:x%x wait:x%x",
 						phba->work_ha, work_ha_copy,
 						(uint32_t)((unsigned long)
-						phba->work_wait));
+						&phba->work_waitq));
 
 					control &=
 					    ~(HC_R0INT_ENA << LPFC_ELS_RING);
@@ -4172,7 +4174,7 @@
 						"x%x hawork:x%x wait:x%x",
 						phba->work_ha, work_ha_copy,
 						(uint32_t)((unsigned long)
-						phba->work_wait));
+						&phba->work_waitq));
 				}
 				spin_unlock(&phba->hbalock);
 			}
@@ -4297,9 +4299,8 @@
 
 		spin_lock(&phba->hbalock);
 		phba->work_ha |= work_ha_copy;
-		if (phba->work_wait)
-			lpfc_worker_wake_up(phba);
 		spin_unlock(&phba->hbalock);
+		lpfc_worker_wake_up(phba);
 	}
 
 	ha_copy &= ~(phba->work_ha_mask);
diff --git a/drivers/scsi/lpfc/lpfc_version.h b/drivers/scsi/lpfc/lpfc_version.h
index b22b893..ad24cac 100644
--- a/drivers/scsi/lpfc/lpfc_version.h
+++ b/drivers/scsi/lpfc/lpfc_version.h
@@ -18,7 +18,7 @@
  * included with this package.                                     *
  *******************************************************************/
 
-#define LPFC_DRIVER_VERSION "8.2.6"
+#define LPFC_DRIVER_VERSION "8.2.7"
 
 #define LPFC_DRIVER_NAME "lpfc"
 
diff --git a/drivers/scsi/lpfc/lpfc_vport.c b/drivers/scsi/lpfc/lpfc_vport.c
index 6feaf59..109f89d 100644
--- a/drivers/scsi/lpfc/lpfc_vport.c
+++ b/drivers/scsi/lpfc/lpfc_vport.c
@@ -216,6 +216,7 @@
 	int vpi;
 	int rc = VPORT_ERROR;
 	int status;
+	int size;
 
 	if ((phba->sli_rev < 3) ||
 		!(phba->sli3_options & LPFC_SLI3_NPIV_ENABLED)) {
@@ -278,7 +279,20 @@
 
 	memcpy(vport->fc_portname.u.wwn, vport->fc_sparam.portName.u.wwn, 8);
 	memcpy(vport->fc_nodename.u.wwn, vport->fc_sparam.nodeName.u.wwn, 8);
-
+	size = strnlen(fc_vport->symbolic_name, LPFC_VNAME_LEN);
+	if (size) {
+		vport->vname = kzalloc(size+1, GFP_KERNEL);
+		if (!vport->vname) {
+			lpfc_printf_vlog(vport, KERN_ERR, LOG_VPORT,
+					 "1814 Create VPORT failed. "
+					 "vname allocation failed.\n");
+			rc = VPORT_ERROR;
+			lpfc_free_vpi(phba, vpi);
+			destroy_port(vport);
+			goto error_out;
+		}
+		memcpy(vport->vname, fc_vport->symbolic_name, size+1);
+	}
 	if (fc_vport->node_name != 0)
 		u64_to_wwn(fc_vport->node_name, vport->fc_nodename.u.wwn);
 	if (fc_vport->port_name != 0)
diff --git a/drivers/scsi/mesh.c b/drivers/scsi/mesh.c
index fd63b06..11aa917 100644
--- a/drivers/scsi/mesh.c
+++ b/drivers/scsi/mesh.c
@@ -1765,7 +1765,7 @@
 	default:
 		return 0;
 	}
-	if (mesg.event == mdev->ofdev.dev.power.power_state.event)
+	if (ms->phase == sleeping)
 		return 0;
 
 	scsi_block_requests(ms->host);
@@ -1780,8 +1780,6 @@
 	disable_irq(ms->meshintr);
 	set_mesh_power(ms, 0);
 
-	mdev->ofdev.dev.power.power_state = mesg;
-
 	return 0;
 }
 
@@ -1790,7 +1788,7 @@
 	struct mesh_state *ms = (struct mesh_state *)macio_get_drvdata(mdev);
 	unsigned long flags;
 
-	if (mdev->ofdev.dev.power.power_state.event == PM_EVENT_ON)
+	if (ms->phase != sleeping)
 		return 0;
 
 	set_mesh_power(ms, 1);
@@ -1801,8 +1799,6 @@
 	enable_irq(ms->meshintr);
 	scsi_unblock_requests(ms->host);
 
-	mdev->ofdev.dev.power.power_state.event = PM_EVENT_ON;
-
 	return 0;
 }
 
diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c
index 0c78694..5822dd5 100644
--- a/drivers/scsi/qla4xxx/ql4_os.c
+++ b/drivers/scsi/qla4xxx/ql4_os.c
@@ -113,9 +113,6 @@
 	.host_param_mask	= ISCSI_HOST_HWADDRESS |
 				  ISCSI_HOST_IPADDRESS |
 				  ISCSI_HOST_INITIATOR_NAME,
-	.sessiondata_size	= sizeof(struct ddb_entry),
-	.host_template		= &qla4xxx_driver_template,
-
 	.tgt_dscvr		= qla4xxx_tgt_dscvr,
 	.get_conn_param		= qla4xxx_conn_get_param,
 	.get_session_param	= qla4xxx_sess_get_param,
@@ -275,7 +272,7 @@
 		return err;
 	}
 
-	ddb_entry->conn = iscsi_create_conn(ddb_entry->sess, 0);
+	ddb_entry->conn = iscsi_create_conn(ddb_entry->sess, 0, 0);
 	if (!ddb_entry->conn) {
 		iscsi_remove_session(ddb_entry->sess);
 		DEBUG2(printk(KERN_ERR "Could not add connection.\n"));
@@ -292,7 +289,8 @@
 	struct ddb_entry *ddb_entry;
 	struct iscsi_cls_session *sess;
 
-	sess = iscsi_alloc_session(ha->host, &qla4xxx_iscsi_transport);
+	sess = iscsi_alloc_session(ha->host, &qla4xxx_iscsi_transport,
+				   sizeof(struct ddb_entry));
 	if (!sess)
 		return NULL;
 
diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c
index 110e776..36c92f9 100644
--- a/drivers/scsi/scsi.c
+++ b/drivers/scsi/scsi.c
@@ -855,9 +855,18 @@
 
 	good_bytes = scsi_bufflen(cmd);
         if (cmd->request->cmd_type != REQ_TYPE_BLOCK_PC) {
+		int old_good_bytes = good_bytes;
 		drv = scsi_cmd_to_driver(cmd);
 		if (drv->done)
 			good_bytes = drv->done(cmd);
+		/*
+		 * USB may not give sense identifying bad sector and
+		 * simply return a residue instead, so subtract off the
+		 * residue if drv->done() error processing indicates no
+		 * change to the completion length.
+		 */
+		if (good_bytes == old_good_bytes)
+			good_bytes -= scsi_get_resid(cmd);
 	}
 	scsi_io_completion(cmd, good_bytes);
 }
diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index f6600bf..01d11a0 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -94,6 +94,7 @@
 #define DEF_VIRTUAL_GB   0
 #define DEF_FAKE_RW	0
 #define DEF_VPD_USE_HOSTNO 1
+#define DEF_SECTOR_SIZE 512
 
 /* bit mask values for scsi_debug_opts */
 #define SCSI_DEBUG_OPT_NOISE   1
@@ -142,6 +143,7 @@
 static int scsi_debug_virtual_gb = DEF_VIRTUAL_GB;
 static int scsi_debug_fake_rw = DEF_FAKE_RW;
 static int scsi_debug_vpd_use_hostno = DEF_VPD_USE_HOSTNO;
+static int scsi_debug_sector_size = DEF_SECTOR_SIZE;
 
 static int scsi_debug_cmnd_count = 0;
 
@@ -157,11 +159,6 @@
 static int sdebug_cylinders_per;	/* cylinders per surface */
 static int sdebug_sectors_per;		/* sectors per cylinder */
 
-/* default sector size is 512 bytes, 2**9 bytes */
-#define POW2_SECT_SIZE 9
-#define SECT_SIZE (1 << POW2_SECT_SIZE)
-#define SECT_SIZE_PER(TGT) SECT_SIZE
-
 #define SDEBUG_MAX_PARTS 4
 
 #define SDEBUG_SENSE_LEN 32
@@ -646,6 +643,14 @@
 	return sizeof(vpdb0_data);
 }
 
+static int inquiry_evpd_b1(unsigned char *arr)
+{
+	memset(arr, 0, 0x3c);
+	arr[0] = 0;
+	arr[1] = 1;
+
+	return 0x3c;
+}
 
 #define SDEBUG_LONG_INQ_SZ 96
 #define SDEBUG_MAX_INQ_ARR_SZ 584
@@ -701,6 +706,7 @@
 			arr[n++] = 0x88;  /* SCSI ports */
 			arr[n++] = 0x89;  /* ATA information */
 			arr[n++] = 0xb0;  /* Block limits (SBC) */
+			arr[n++] = 0xb1;  /* Block characteristics (SBC) */
 			arr[3] = n - 4;	  /* number of supported VPD pages */
 		} else if (0x80 == cmd[2]) { /* unit serial number */
 			arr[1] = cmd[2];	/*sanity */
@@ -740,6 +746,9 @@
 		} else if (0xb0 == cmd[2]) { /* Block limits (SBC) */
 			arr[1] = cmd[2];        /*sanity */
 			arr[3] = inquiry_evpd_b0(&arr[4]);
+		} else if (0xb1 == cmd[2]) { /* Block characteristics (SBC) */
+			arr[1] = cmd[2];        /*sanity */
+			arr[3] = inquiry_evpd_b1(&arr[4]);
 		} else {
 			/* Illegal request, invalid field in cdb */
 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
@@ -878,8 +887,8 @@
 		arr[2] = 0xff;
 		arr[3] = 0xff;
 	}
-	arr[6] = (SECT_SIZE_PER(target) >> 8) & 0xff;
-	arr[7] = SECT_SIZE_PER(target) & 0xff;
+	arr[6] = (scsi_debug_sector_size >> 8) & 0xff;
+	arr[7] = scsi_debug_sector_size & 0xff;
 	return fill_from_dev_buffer(scp, arr, SDEBUG_READCAP_ARR_SZ);
 }
 
@@ -902,10 +911,10 @@
 	capac = sdebug_capacity - 1;
 	for (k = 0; k < 8; ++k, capac >>= 8)
 		arr[7 - k] = capac & 0xff;
-	arr[8] = (SECT_SIZE_PER(target) >> 24) & 0xff;
-	arr[9] = (SECT_SIZE_PER(target) >> 16) & 0xff;
-	arr[10] = (SECT_SIZE_PER(target) >> 8) & 0xff;
-	arr[11] = SECT_SIZE_PER(target) & 0xff;
+	arr[8] = (scsi_debug_sector_size >> 24) & 0xff;
+	arr[9] = (scsi_debug_sector_size >> 16) & 0xff;
+	arr[10] = (scsi_debug_sector_size >> 8) & 0xff;
+	arr[11] = scsi_debug_sector_size & 0xff;
 	return fill_from_dev_buffer(scp, arr,
 				    min(alloc_len, SDEBUG_READCAP16_ARR_SZ));
 }
@@ -1019,20 +1028,20 @@
 
 static int resp_format_pg(unsigned char * p, int pcontrol, int target)
 {       /* Format device page for mode_sense */
-        unsigned char format_pg[] = {0x3, 0x16, 0, 0, 0, 0, 0, 0,
-                                     0, 0, 0, 0, 0, 0, 0, 0,
-                                     0, 0, 0, 0, 0x40, 0, 0, 0};
+	unsigned char format_pg[] = {0x3, 0x16, 0, 0, 0, 0, 0, 0,
+				     0, 0, 0, 0, 0, 0, 0, 0,
+				     0, 0, 0, 0, 0x40, 0, 0, 0};
 
-        memcpy(p, format_pg, sizeof(format_pg));
-        p[10] = (sdebug_sectors_per >> 8) & 0xff;
-        p[11] = sdebug_sectors_per & 0xff;
-        p[12] = (SECT_SIZE >> 8) & 0xff;
-        p[13] = SECT_SIZE & 0xff;
-        if (DEV_REMOVEABLE(target))
-                p[20] |= 0x20; /* should agree with INQUIRY */
-        if (1 == pcontrol)
-                memset(p + 2, 0, sizeof(format_pg) - 2);
-        return sizeof(format_pg);
+	memcpy(p, format_pg, sizeof(format_pg));
+	p[10] = (sdebug_sectors_per >> 8) & 0xff;
+	p[11] = sdebug_sectors_per & 0xff;
+	p[12] = (scsi_debug_sector_size >> 8) & 0xff;
+	p[13] = scsi_debug_sector_size & 0xff;
+	if (DEV_REMOVEABLE(target))
+		p[20] |= 0x20; /* should agree with INQUIRY */
+	if (1 == pcontrol)
+		memset(p + 2, 0, sizeof(format_pg) - 2);
+	return sizeof(format_pg);
 }
 
 static int resp_caching_pg(unsigned char * p, int pcontrol, int target)
@@ -1206,8 +1215,8 @@
 			ap[2] = (sdebug_capacity >> 8) & 0xff;
 			ap[3] = sdebug_capacity & 0xff;
 		}
-        	ap[6] = (SECT_SIZE_PER(target) >> 8) & 0xff;
-        	ap[7] = SECT_SIZE_PER(target) & 0xff;
+		ap[6] = (scsi_debug_sector_size >> 8) & 0xff;
+		ap[7] = scsi_debug_sector_size & 0xff;
 		offset += bd_len;
 		ap = arr + offset;
 	} else if (16 == bd_len) {
@@ -1215,10 +1224,10 @@
 
         	for (k = 0; k < 8; ++k, capac >>= 8)
                 	ap[7 - k] = capac & 0xff;
-        	ap[12] = (SECT_SIZE_PER(target) >> 24) & 0xff;
-        	ap[13] = (SECT_SIZE_PER(target) >> 16) & 0xff;
-        	ap[14] = (SECT_SIZE_PER(target) >> 8) & 0xff;
-        	ap[15] = SECT_SIZE_PER(target) & 0xff;
+		ap[12] = (scsi_debug_sector_size >> 24) & 0xff;
+		ap[13] = (scsi_debug_sector_size >> 16) & 0xff;
+		ap[14] = (scsi_debug_sector_size >> 8) & 0xff;
+		ap[15] = scsi_debug_sector_size & 0xff;
 		offset += bd_len;
 		ap = arr + offset;
 	}
@@ -1519,10 +1528,10 @@
 	if (block + num > sdebug_store_sectors)
 		rest = block + num - sdebug_store_sectors;
 
-	ret = func(scmd, fake_storep + (block * SECT_SIZE),
-		   (num - rest) * SECT_SIZE);
+	ret = func(scmd, fake_storep + (block * scsi_debug_sector_size),
+		   (num - rest) * scsi_debug_sector_size);
 	if (!ret && rest)
-		ret = func(scmd, fake_storep, rest * SECT_SIZE);
+		ret = func(scmd, fake_storep, rest * scsi_debug_sector_size);
 
 	return ret;
 }
@@ -1575,10 +1584,10 @@
 	write_unlock_irqrestore(&atomic_rw, iflags);
 	if (-1 == ret)
 		return (DID_ERROR << 16);
-	else if ((ret < (num * SECT_SIZE)) &&
+	else if ((ret < (num * scsi_debug_sector_size)) &&
 		 (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts))
 		printk(KERN_INFO "scsi_debug: write: cdb indicated=%u, "
-		       " IO sent=%d bytes\n", num * SECT_SIZE, ret);
+		       " IO sent=%d bytes\n", num * scsi_debug_sector_size, ret);
 	return 0;
 }
 
@@ -2085,6 +2094,7 @@
 module_param_named(virtual_gb, scsi_debug_virtual_gb, int, S_IRUGO | S_IWUSR);
 module_param_named(vpd_use_hostno, scsi_debug_vpd_use_hostno, int,
 		   S_IRUGO | S_IWUSR);
+module_param_named(sector_size, scsi_debug_sector_size, int, S_IRUGO);
 
 MODULE_AUTHOR("Eric Youngdale + Douglas Gilbert");
 MODULE_DESCRIPTION("SCSI debug adapter driver");
@@ -2106,6 +2116,7 @@
 MODULE_PARM_DESC(scsi_level, "SCSI level to simulate(def=5[SPC-3])");
 MODULE_PARM_DESC(virtual_gb, "virtual gigabyte size (def=0 -> use dev_size_mb)");
 MODULE_PARM_DESC(vpd_use_hostno, "0 -> dev ids ignore hostno (def=1 -> unique dev ids)");
+MODULE_PARM_DESC(sector_size, "hardware sector size in bytes (def=512)");
 
 
 static char sdebug_info[256];
@@ -2158,8 +2169,9 @@
 	    scsi_debug_dev_size_mb, scsi_debug_opts, scsi_debug_every_nth,
 	    scsi_debug_cmnd_count, scsi_debug_delay,
 	    scsi_debug_max_luns, scsi_debug_scsi_level,
-	    SECT_SIZE, sdebug_cylinders_per, sdebug_heads, sdebug_sectors_per,
-	    num_aborts, num_dev_resets, num_bus_resets, num_host_resets);
+	    scsi_debug_sector_size, sdebug_cylinders_per, sdebug_heads,
+	    sdebug_sectors_per, num_aborts, num_dev_resets, num_bus_resets,
+	    num_host_resets);
 	if (pos < offset) {
 		len = 0;
 		begin = pos;
@@ -2434,6 +2446,12 @@
 DRIVER_ATTR(vpd_use_hostno, S_IRUGO | S_IWUSR, sdebug_vpd_use_hostno_show,
 	    sdebug_vpd_use_hostno_store);
 
+static ssize_t sdebug_sector_size_show(struct device_driver * ddp, char * buf)
+{
+	return scnprintf(buf, PAGE_SIZE, "%u\n", scsi_debug_sector_size);
+}
+DRIVER_ATTR(sector_size, S_IRUGO, sdebug_sector_size_show, NULL);
+
 /* Note: The following function creates attribute files in the
    /sys/bus/pseudo/drivers/scsi_debug directory. The advantage of these
    files (over those found in the /sys/module/scsi_debug/parameters
@@ -2459,11 +2477,13 @@
 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_scsi_level);
 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_virtual_gb);
 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_vpd_use_hostno);
+	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_sector_size);
 	return ret;
 }
 
 static void do_remove_driverfs_files(void)
 {
+	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_sector_size);
 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_vpd_use_hostno);
 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_virtual_gb);
 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_scsi_level);
@@ -2499,10 +2519,22 @@
 	int k;
 	int ret;
 
+	switch (scsi_debug_sector_size) {
+	case  512:
+	case 1024:
+	case 2048:
+	case 4096:
+		break;
+	default:
+		printk(KERN_ERR "scsi_debug_init: invalid sector_size %u\n",
+		       scsi_debug_sector_size);
+		return -EINVAL;
+	}
+
 	if (scsi_debug_dev_size_mb < 1)
 		scsi_debug_dev_size_mb = 1;  /* force minimum 1 MB ramdisk */
 	sz = (unsigned long)scsi_debug_dev_size_mb * 1048576;
-	sdebug_store_sectors = sz / SECT_SIZE;
+	sdebug_store_sectors = sz / scsi_debug_sector_size;
 	sdebug_capacity = get_sdebug_capacity();
 
 	/* play around with geometry, don't waste too much on track 0 */
diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c
index eaf5a8a..006a959 100644
--- a/drivers/scsi/scsi_error.c
+++ b/drivers/scsi/scsi_error.c
@@ -298,6 +298,7 @@
  */
 static int scsi_check_sense(struct scsi_cmnd *scmd)
 {
+	struct scsi_device *sdev = scmd->device;
 	struct scsi_sense_hdr sshdr;
 
 	if (! scsi_command_normalize_sense(scmd, &sshdr))
@@ -306,6 +307,16 @@
 	if (scsi_sense_is_deferred(&sshdr))
 		return NEEDS_RETRY;
 
+	if (sdev->scsi_dh_data && sdev->scsi_dh_data->scsi_dh &&
+			sdev->scsi_dh_data->scsi_dh->check_sense) {
+		int rc;
+
+		rc = sdev->scsi_dh_data->scsi_dh->check_sense(sdev, &sshdr);
+		if (rc != SCSI_RETURN_NOT_HANDLED)
+			return rc;
+		/* handler does not care. Drop down to default handling */
+	}
+
 	/*
 	 * Previous logic looked for FILEMARK, EOM or ILI which are
 	 * mainly associated with tapes and returned SUCCESS.
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index cbf55d5..88d1b5f 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -65,7 +65,7 @@
 };
 #undef SP
 
-static struct kmem_cache *scsi_bidi_sdb_cache;
+static struct kmem_cache *scsi_sdb_cache;
 
 static void scsi_run_queue(struct request_queue *q);
 
@@ -784,7 +784,7 @@
 		struct scsi_data_buffer *bidi_sdb =
 			cmd->request->next_rq->special;
 		scsi_free_sgtable(bidi_sdb);
-		kmem_cache_free(scsi_bidi_sdb_cache, bidi_sdb);
+		kmem_cache_free(scsi_sdb_cache, bidi_sdb);
 		cmd->request->next_rq->special = NULL;
 	}
 }
@@ -1059,7 +1059,7 @@
 
 	if (blk_bidi_rq(cmd->request)) {
 		struct scsi_data_buffer *bidi_sdb = kmem_cache_zalloc(
-			scsi_bidi_sdb_cache, GFP_ATOMIC);
+			scsi_sdb_cache, GFP_ATOMIC);
 		if (!bidi_sdb) {
 			error = BLKPREP_DEFER;
 			goto err_exit;
@@ -1169,6 +1169,14 @@
 
 	if (ret != BLKPREP_OK)
 		return ret;
+
+	if (unlikely(sdev->scsi_dh_data && sdev->scsi_dh_data->scsi_dh
+			 && sdev->scsi_dh_data->scsi_dh->prep_fn)) {
+		ret = sdev->scsi_dh_data->scsi_dh->prep_fn(sdev, req);
+		if (ret != BLKPREP_OK)
+			return ret;
+	}
+
 	/*
 	 * Filesystem requests must transfer data.
 	 */
@@ -1329,7 +1337,6 @@
 				printk("scsi%d unblocking host at zero depth\n",
 					shost->host_no));
 		} else {
-			blk_plug_device(q);
 			return 0;
 		}
 	}
@@ -1693,11 +1700,11 @@
 		return -ENOMEM;
 	}
 
-	scsi_bidi_sdb_cache = kmem_cache_create("scsi_bidi_sdb",
-					sizeof(struct scsi_data_buffer),
-					0, 0, NULL);
-	if (!scsi_bidi_sdb_cache) {
-		printk(KERN_ERR "SCSI: can't init scsi bidi sdb cache\n");
+	scsi_sdb_cache = kmem_cache_create("scsi_data_buffer",
+					   sizeof(struct scsi_data_buffer),
+					   0, 0, NULL);
+	if (!scsi_sdb_cache) {
+		printk(KERN_ERR "SCSI: can't init scsi sdb cache\n");
 		goto cleanup_io_context;
 	}
 
@@ -1710,7 +1717,7 @@
 		if (!sgp->slab) {
 			printk(KERN_ERR "SCSI: can't init sg slab %s\n",
 					sgp->name);
-			goto cleanup_bidi_sdb;
+			goto cleanup_sdb;
 		}
 
 		sgp->pool = mempool_create_slab_pool(SG_MEMPOOL_SIZE,
@@ -1718,13 +1725,13 @@
 		if (!sgp->pool) {
 			printk(KERN_ERR "SCSI: can't init sg mempool %s\n",
 					sgp->name);
-			goto cleanup_bidi_sdb;
+			goto cleanup_sdb;
 		}
 	}
 
 	return 0;
 
-cleanup_bidi_sdb:
+cleanup_sdb:
 	for (i = 0; i < SG_MEMPOOL_NR; i++) {
 		struct scsi_host_sg_pool *sgp = scsi_sg_pools + i;
 		if (sgp->pool)
@@ -1732,7 +1739,7 @@
 		if (sgp->slab)
 			kmem_cache_destroy(sgp->slab);
 	}
-	kmem_cache_destroy(scsi_bidi_sdb_cache);
+	kmem_cache_destroy(scsi_sdb_cache);
 cleanup_io_context:
 	kmem_cache_destroy(scsi_io_context_cache);
 
@@ -1744,7 +1751,7 @@
 	int i;
 
 	kmem_cache_destroy(scsi_io_context_cache);
-	kmem_cache_destroy(scsi_bidi_sdb_cache);
+	kmem_cache_destroy(scsi_sdb_cache);
 
 	for (i = 0; i < SG_MEMPOOL_NR; i++) {
 		struct scsi_host_sg_pool *sgp = scsi_sg_pools + i;
diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
index a00eee6..196fe3a 100644
--- a/drivers/scsi/scsi_scan.c
+++ b/drivers/scsi/scsi_scan.c
@@ -346,7 +346,7 @@
 	put_device(parent);
 }
 
-struct device_type scsi_target_type = {
+static struct device_type scsi_target_type = {
 	.name =		"scsi_target",
 	.release =	scsi_target_dev_release,
 };
diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c
index 93d2b67..b6e5610 100644
--- a/drivers/scsi/scsi_sysfs.c
+++ b/drivers/scsi/scsi_sysfs.c
@@ -439,6 +439,7 @@
 	.resume		= scsi_bus_resume,
 	.remove		= scsi_bus_remove,
 };
+EXPORT_SYMBOL_GPL(scsi_bus_type);
 
 int scsi_sysfs_register(void)
 {
diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c
index 65d1737..3af7cbc 100644
--- a/drivers/scsi/scsi_transport_iscsi.c
+++ b/drivers/scsi/scsi_transport_iscsi.c
@@ -30,10 +30,11 @@
 #include <scsi/scsi_transport_iscsi.h>
 #include <scsi/iscsi_if.h>
 
-#define ISCSI_SESSION_ATTRS 19
+#define ISCSI_SESSION_ATTRS 21
 #define ISCSI_CONN_ATTRS 13
 #define ISCSI_HOST_ATTRS 4
-#define ISCSI_TRANSPORT_VERSION "2.0-869"
+
+#define ISCSI_TRANSPORT_VERSION "2.0-870"
 
 struct iscsi_internal {
 	int daemon_pid;
@@ -101,16 +102,10 @@
 static DEVICE_ATTR(name, S_IRUGO, show_transport_##name, NULL);
 
 show_transport_attr(caps, "0x%x");
-show_transport_attr(max_lun, "%d");
-show_transport_attr(max_conn, "%d");
-show_transport_attr(max_cmd_len, "%d");
 
 static struct attribute *iscsi_transport_attrs[] = {
 	&dev_attr_handle.attr,
 	&dev_attr_caps.attr,
-	&dev_attr_max_lun.attr,
-	&dev_attr_max_conn.attr,
-	&dev_attr_max_cmd_len.attr,
 	NULL,
 };
 
@@ -118,18 +113,139 @@
 	.attrs = iscsi_transport_attrs,
 };
 
+/*
+ * iSCSI endpoint attrs
+ */
+#define iscsi_dev_to_endpoint(_dev) \
+	container_of(_dev, struct iscsi_endpoint, dev)
 
+#define ISCSI_ATTR(_prefix,_name,_mode,_show,_store)	\
+struct device_attribute dev_attr_##_prefix##_##_name =	\
+        __ATTR(_name,_mode,_show,_store)
+
+static void iscsi_endpoint_release(struct device *dev)
+{
+	struct iscsi_endpoint *ep = iscsi_dev_to_endpoint(dev);
+	kfree(ep);
+}
+
+static struct class iscsi_endpoint_class = {
+	.name = "iscsi_endpoint",
+	.dev_release = iscsi_endpoint_release,
+};
+
+static ssize_t
+show_ep_handle(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct iscsi_endpoint *ep = iscsi_dev_to_endpoint(dev);
+	return sprintf(buf, "%u\n", ep->id);
+}
+static ISCSI_ATTR(ep, handle, S_IRUGO, show_ep_handle, NULL);
+
+static struct attribute *iscsi_endpoint_attrs[] = {
+	&dev_attr_ep_handle.attr,
+	NULL,
+};
+
+static struct attribute_group iscsi_endpoint_group = {
+	.attrs = iscsi_endpoint_attrs,
+};
+
+#define ISCSI_MAX_EPID -1
+
+static int iscsi_match_epid(struct device *dev, void *data)
+{
+	struct iscsi_endpoint *ep = iscsi_dev_to_endpoint(dev);
+	unsigned int *epid = (unsigned int *) data;
+
+	return *epid == ep->id;
+}
+
+struct iscsi_endpoint *
+iscsi_create_endpoint(int dd_size)
+{
+	struct device *dev;
+	struct iscsi_endpoint *ep;
+	unsigned int id;
+	int err;
+
+	for (id = 1; id < ISCSI_MAX_EPID; id++) {
+		dev = class_find_device(&iscsi_endpoint_class, &id,
+					iscsi_match_epid);
+		if (!dev)
+			break;
+	}
+	if (id == ISCSI_MAX_EPID) {
+		printk(KERN_ERR "Too many connections. Max supported %u\n",
+		       ISCSI_MAX_EPID - 1);
+		return NULL;
+	}
+
+	ep = kzalloc(sizeof(*ep) + dd_size, GFP_KERNEL);
+	if (!ep)
+		return NULL;
+
+	ep->id = id;
+	ep->dev.class = &iscsi_endpoint_class;
+	snprintf(ep->dev.bus_id, BUS_ID_SIZE, "ep-%u", id);
+	err = device_register(&ep->dev);
+        if (err)
+                goto free_ep;
+
+	err = sysfs_create_group(&ep->dev.kobj, &iscsi_endpoint_group);
+	if (err)
+		goto unregister_dev;
+
+	if (dd_size)
+		ep->dd_data = &ep[1];
+	return ep;
+
+unregister_dev:
+	device_unregister(&ep->dev);
+	return NULL;
+
+free_ep:
+	kfree(ep);
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(iscsi_create_endpoint);
+
+void iscsi_destroy_endpoint(struct iscsi_endpoint *ep)
+{
+	sysfs_remove_group(&ep->dev.kobj, &iscsi_endpoint_group);
+	device_unregister(&ep->dev);
+}
+EXPORT_SYMBOL_GPL(iscsi_destroy_endpoint);
+
+struct iscsi_endpoint *iscsi_lookup_endpoint(u64 handle)
+{
+	struct iscsi_endpoint *ep;
+	struct device *dev;
+
+	dev = class_find_device(&iscsi_endpoint_class, &handle,
+				iscsi_match_epid);
+	if (!dev)
+		return NULL;
+
+	ep = iscsi_dev_to_endpoint(dev);
+	/*
+	 * we can drop this now because the interface will prevent
+	 * removals and lookups from racing.
+	 */
+	put_device(dev);
+	return ep;
+}
+EXPORT_SYMBOL_GPL(iscsi_lookup_endpoint);
 
 static int iscsi_setup_host(struct transport_container *tc, struct device *dev,
 			    struct device *cdev)
 {
 	struct Scsi_Host *shost = dev_to_shost(dev);
-	struct iscsi_host *ihost = shost->shost_data;
+	struct iscsi_cls_host *ihost = shost->shost_data;
 
 	memset(ihost, 0, sizeof(*ihost));
-	INIT_LIST_HEAD(&ihost->sessions);
-	mutex_init(&ihost->mutex);
 	atomic_set(&ihost->nr_scans, 0);
+	mutex_init(&ihost->mutex);
 
 	snprintf(ihost->scan_workq_name, KOBJ_NAME_LEN, "iscsi_scan_%d",
 		shost->host_no);
@@ -144,7 +260,7 @@
 			     struct device *cdev)
 {
 	struct Scsi_Host *shost = dev_to_shost(dev);
-	struct iscsi_host *ihost = shost->shost_data;
+	struct iscsi_cls_host *ihost = shost->shost_data;
 
 	destroy_workqueue(ihost->scan_workq);
 	return 0;
@@ -287,6 +403,24 @@
 	return dev->release == iscsi_session_release;
 }
 
+static int iscsi_iter_session_fn(struct device *dev, void *data)
+{
+	void (* fn) (struct iscsi_cls_session *) = data;
+
+	if (!iscsi_is_session_dev(dev))
+		return 0;
+	fn(iscsi_dev_to_session(dev));
+	return 0;
+}
+
+void iscsi_host_for_each_session(struct Scsi_Host *shost,
+				 void (*fn)(struct iscsi_cls_session *))
+{
+	device_for_each_child(&shost->shost_gendev, fn,
+			      iscsi_iter_session_fn);
+}
+EXPORT_SYMBOL_GPL(iscsi_host_for_each_session);
+
 /**
  * iscsi_scan_finished - helper to report when running scans are done
  * @shost: scsi host
@@ -297,7 +431,7 @@
  */
 int iscsi_scan_finished(struct Scsi_Host *shost, unsigned long time)
 {
-	struct iscsi_host *ihost = shost->shost_data;
+	struct iscsi_cls_host *ihost = shost->shost_data;
 	/*
 	 * qla4xxx will have kicked off some session unblocks before calling
 	 * scsi_scan_host, so just wait for them to complete.
@@ -306,22 +440,61 @@
 }
 EXPORT_SYMBOL_GPL(iscsi_scan_finished);
 
+struct iscsi_scan_data {
+	unsigned int channel;
+	unsigned int id;
+	unsigned int lun;
+};
+
+static int iscsi_user_scan_session(struct device *dev, void *data)
+{
+	struct iscsi_scan_data *scan_data = data;
+	struct iscsi_cls_session *session;
+	struct Scsi_Host *shost;
+	struct iscsi_cls_host *ihost;
+	unsigned long flags;
+	unsigned int id;
+
+	if (!iscsi_is_session_dev(dev))
+		return 0;
+
+	session = iscsi_dev_to_session(dev);
+	shost = iscsi_session_to_shost(session);
+	ihost = shost->shost_data;
+
+	mutex_lock(&ihost->mutex);
+	spin_lock_irqsave(&session->lock, flags);
+	if (session->state != ISCSI_SESSION_LOGGED_IN) {
+		spin_unlock_irqrestore(&session->lock, flags);
+		mutex_unlock(&ihost->mutex);
+		return 0;
+	}
+	id = session->target_id;
+	spin_unlock_irqrestore(&session->lock, flags);
+
+	if (id != ISCSI_MAX_TARGET) {
+		if ((scan_data->channel == SCAN_WILD_CARD ||
+		     scan_data->channel == 0) &&
+		    (scan_data->id == SCAN_WILD_CARD ||
+		     scan_data->id == id))
+			scsi_scan_target(&session->dev, 0, id,
+					 scan_data->lun, 1);
+	}
+	mutex_unlock(&ihost->mutex);
+	return 0;
+}
+
 static int iscsi_user_scan(struct Scsi_Host *shost, uint channel,
 			   uint id, uint lun)
 {
-	struct iscsi_host *ihost = shost->shost_data;
-	struct iscsi_cls_session *session;
+	struct iscsi_scan_data scan_data;
 
-	mutex_lock(&ihost->mutex);
-	list_for_each_entry(session, &ihost->sessions, host_list) {
-		if ((channel == SCAN_WILD_CARD || channel == 0) &&
-		    (id == SCAN_WILD_CARD || id == session->target_id))
-			scsi_scan_target(&session->dev, 0,
-					 session->target_id, lun, 1);
-	}
-	mutex_unlock(&ihost->mutex);
+	scan_data.channel = channel;
+	scan_data.id = id;
+	scan_data.lun = lun;
 
-	return 0;
+	return device_for_each_child(&shost->shost_gendev, &scan_data,
+				     iscsi_user_scan_session);
 }
 
 static void iscsi_scan_session(struct work_struct *work)
@@ -329,19 +502,14 @@
 	struct iscsi_cls_session *session =
 			container_of(work, struct iscsi_cls_session, scan_work);
 	struct Scsi_Host *shost = iscsi_session_to_shost(session);
-	struct iscsi_host *ihost = shost->shost_data;
-	unsigned long flags;
+	struct iscsi_cls_host *ihost = shost->shost_data;
+	struct iscsi_scan_data scan_data;
 
-	spin_lock_irqsave(&session->lock, flags);
-	if (session->state != ISCSI_SESSION_LOGGED_IN) {
-		spin_unlock_irqrestore(&session->lock, flags);
-		goto done;
-	}
-	spin_unlock_irqrestore(&session->lock, flags);
+	scan_data.channel = 0;
+	scan_data.id = SCAN_WILD_CARD;
+	scan_data.lun = SCAN_WILD_CARD;
 
-	scsi_scan_target(&session->dev, 0, session->target_id,
-			 SCAN_WILD_CARD, 1);
-done:
+	iscsi_user_scan_session(&session->dev, &scan_data);
 	atomic_dec(&ihost->nr_scans);
 }
 
@@ -381,7 +549,7 @@
 			container_of(work, struct iscsi_cls_session,
 				     unblock_work);
 	struct Scsi_Host *shost = iscsi_session_to_shost(session);
-	struct iscsi_host *ihost = shost->shost_data;
+	struct iscsi_cls_host *ihost = shost->shost_data;
 	unsigned long flags;
 
 	/*
@@ -449,15 +617,19 @@
 			container_of(work, struct iscsi_cls_session,
 				     unbind_work);
 	struct Scsi_Host *shost = iscsi_session_to_shost(session);
-	struct iscsi_host *ihost = shost->shost_data;
+	struct iscsi_cls_host *ihost = shost->shost_data;
+	unsigned long flags;
 
 	/* Prevent new scans and make sure scanning is not in progress */
 	mutex_lock(&ihost->mutex);
-	if (list_empty(&session->host_list)) {
+	spin_lock_irqsave(&session->lock, flags);
+	if (session->target_id == ISCSI_MAX_TARGET) {
+		spin_unlock_irqrestore(&session->lock, flags);
 		mutex_unlock(&ihost->mutex);
 		return;
 	}
-	list_del_init(&session->host_list);
+	session->target_id = ISCSI_MAX_TARGET;
+	spin_unlock_irqrestore(&session->lock, flags);
 	mutex_unlock(&ihost->mutex);
 
 	scsi_remove_target(&session->dev);
@@ -467,18 +639,18 @@
 static int iscsi_unbind_session(struct iscsi_cls_session *session)
 {
 	struct Scsi_Host *shost = iscsi_session_to_shost(session);
-	struct iscsi_host *ihost = shost->shost_data;
+	struct iscsi_cls_host *ihost = shost->shost_data;
 
 	return queue_work(ihost->scan_workq, &session->unbind_work);
 }
 
 struct iscsi_cls_session *
-iscsi_alloc_session(struct Scsi_Host *shost,
-		    struct iscsi_transport *transport)
+iscsi_alloc_session(struct Scsi_Host *shost, struct iscsi_transport *transport,
+		    int dd_size)
 {
 	struct iscsi_cls_session *session;
 
-	session = kzalloc(sizeof(*session) + transport->sessiondata_size,
+	session = kzalloc(sizeof(*session) + dd_size,
 			  GFP_KERNEL);
 	if (!session)
 		return NULL;
@@ -487,7 +659,6 @@
 	session->recovery_tmo = 120;
 	session->state = ISCSI_SESSION_FREE;
 	INIT_DELAYED_WORK(&session->recovery_work, session_recovery_timedout);
-	INIT_LIST_HEAD(&session->host_list);
 	INIT_LIST_HEAD(&session->sess_list);
 	INIT_WORK(&session->unblock_work, __iscsi_unblock_session);
 	INIT_WORK(&session->block_work, __iscsi_block_session);
@@ -500,22 +671,57 @@
 	session->dev.parent = &shost->shost_gendev;
 	session->dev.release = iscsi_session_release;
 	device_initialize(&session->dev);
-	if (transport->sessiondata_size)
+	if (dd_size)
 		session->dd_data = &session[1];
 	return session;
 }
 EXPORT_SYMBOL_GPL(iscsi_alloc_session);
 
+static int iscsi_get_next_target_id(struct device *dev, void *data)
+{
+	struct iscsi_cls_session *session;
+	unsigned long flags;
+	int err = 0;
+
+	if (!iscsi_is_session_dev(dev))
+		return 0;
+
+	session = iscsi_dev_to_session(dev);
+	spin_lock_irqsave(&session->lock, flags);
+	if (*((unsigned int *) data) == session->target_id)
+		err = -EEXIST;
+	spin_unlock_irqrestore(&session->lock, flags);
+	return err;
+}
+
 int iscsi_add_session(struct iscsi_cls_session *session, unsigned int target_id)
 {
 	struct Scsi_Host *shost = iscsi_session_to_shost(session);
-	struct iscsi_host *ihost;
+	struct iscsi_cls_host *ihost;
 	unsigned long flags;
+	unsigned int id = target_id;
 	int err;
 
 	ihost = shost->shost_data;
 	session->sid = atomic_add_return(1, &iscsi_session_nr);
-	session->target_id = target_id;
+
+	if (id == ISCSI_MAX_TARGET) {
+		for (id = 0; id < ISCSI_MAX_TARGET; id++) {
+			err = device_for_each_child(&shost->shost_gendev, &id,
+						    iscsi_get_next_target_id);
+			if (!err)
+				break;
+		}
+
+		if (id == ISCSI_MAX_TARGET) {
+			iscsi_cls_session_printk(KERN_ERR, session,
+						 "Too many iscsi targets. Max "
+						 "number of targets is %d.\n",
+						 ISCSI_MAX_TARGET - 1);
+			goto release_host;
+		}
+	}
+	session->target_id = id;
 
 	snprintf(session->dev.bus_id, BUS_ID_SIZE, "session%u",
 		 session->sid);
@@ -531,10 +737,6 @@
 	list_add(&session->sess_list, &sesslist);
 	spin_unlock_irqrestore(&sesslock, flags);
 
-	mutex_lock(&ihost->mutex);
-	list_add(&session->host_list, &ihost->sessions);
-	mutex_unlock(&ihost->mutex);
-
 	iscsi_session_event(session, ISCSI_KEVENT_CREATE_SESSION);
 	return 0;
 
@@ -548,18 +750,18 @@
  * iscsi_create_session - create iscsi class session
  * @shost: scsi host
  * @transport: iscsi transport
+ * @dd_size: private driver data size
  * @target_id: which target
  *
  * This can be called from a LLD or iscsi_transport.
  */
 struct iscsi_cls_session *
-iscsi_create_session(struct Scsi_Host *shost,
-		     struct iscsi_transport *transport,
-		     unsigned int target_id)
+iscsi_create_session(struct Scsi_Host *shost, struct iscsi_transport *transport,
+		     int dd_size, unsigned int target_id)
 {
 	struct iscsi_cls_session *session;
 
-	session = iscsi_alloc_session(shost, transport);
+	session = iscsi_alloc_session(shost, transport, dd_size);
 	if (!session)
 		return NULL;
 
@@ -595,7 +797,7 @@
 void iscsi_remove_session(struct iscsi_cls_session *session)
 {
 	struct Scsi_Host *shost = iscsi_session_to_shost(session);
-	struct iscsi_host *ihost = shost->shost_data;
+	struct iscsi_cls_host *ihost = shost->shost_data;
 	unsigned long flags;
 	int err;
 
@@ -661,6 +863,7 @@
 /**
  * iscsi_create_conn - create iscsi class connection
  * @session: iscsi cls session
+ * @dd_size: private driver data size
  * @cid: connection id
  *
  * This can be called from a LLD or iscsi_transport. The connection
@@ -673,18 +876,17 @@
  * non-zero.
  */
 struct iscsi_cls_conn *
-iscsi_create_conn(struct iscsi_cls_session *session, uint32_t cid)
+iscsi_create_conn(struct iscsi_cls_session *session, int dd_size, uint32_t cid)
 {
 	struct iscsi_transport *transport = session->transport;
 	struct iscsi_cls_conn *conn;
 	unsigned long flags;
 	int err;
 
-	conn = kzalloc(sizeof(*conn) + transport->conndata_size, GFP_KERNEL);
+	conn = kzalloc(sizeof(*conn) + dd_size, GFP_KERNEL);
 	if (!conn)
 		return NULL;
-
-	if (transport->conndata_size)
+	if (dd_size)
 		conn->dd_data = &conn[1];
 
 	INIT_LIST_HEAD(&conn->conn_list);
@@ -1017,21 +1219,20 @@
 EXPORT_SYMBOL_GPL(iscsi_session_event);
 
 static int
-iscsi_if_create_session(struct iscsi_internal *priv, struct iscsi_uevent *ev)
+iscsi_if_create_session(struct iscsi_internal *priv, struct iscsi_endpoint *ep,
+			struct iscsi_uevent *ev, uint32_t initial_cmdsn,
+			uint16_t cmds_max, uint16_t queue_depth)
 {
 	struct iscsi_transport *transport = priv->iscsi_transport;
 	struct iscsi_cls_session *session;
-	uint32_t hostno;
+	uint32_t host_no;
 
-	session = transport->create_session(transport, &priv->t,
-					    ev->u.c_session.cmds_max,
-					    ev->u.c_session.queue_depth,
-					    ev->u.c_session.initial_cmdsn,
-					    &hostno);
+	session = transport->create_session(ep, cmds_max, queue_depth,
+					    initial_cmdsn, &host_no);
 	if (!session)
 		return -ENOMEM;
 
-	ev->r.c_session_ret.host_no = hostno;
+	ev->r.c_session_ret.host_no = host_no;
 	ev->r.c_session_ret.sid = session->sid;
 	return 0;
 }
@@ -1106,6 +1307,7 @@
 iscsi_if_transport_ep(struct iscsi_transport *transport,
 		      struct iscsi_uevent *ev, int msg_type)
 {
+	struct iscsi_endpoint *ep;
 	struct sockaddr *dst_addr;
 	int rc = 0;
 
@@ -1115,22 +1317,33 @@
 			return -EINVAL;
 
 		dst_addr = (struct sockaddr *)((char*)ev + sizeof(*ev));
-		rc = transport->ep_connect(dst_addr,
-					   ev->u.ep_connect.non_blocking,
-					   &ev->r.ep_connect_ret.handle);
+		ep = transport->ep_connect(dst_addr,
+					   ev->u.ep_connect.non_blocking);
+		if (IS_ERR(ep))
+			return PTR_ERR(ep);
+
+		ev->r.ep_connect_ret.handle = ep->id;
 		break;
 	case ISCSI_UEVENT_TRANSPORT_EP_POLL:
 		if (!transport->ep_poll)
 			return -EINVAL;
 
-		ev->r.retcode = transport->ep_poll(ev->u.ep_poll.ep_handle,
+		ep = iscsi_lookup_endpoint(ev->u.ep_poll.ep_handle);
+		if (!ep)
+			return -EINVAL;
+
+		ev->r.retcode = transport->ep_poll(ep,
 						   ev->u.ep_poll.timeout_ms);
 		break;
 	case ISCSI_UEVENT_TRANSPORT_EP_DISCONNECT:
 		if (!transport->ep_disconnect)
 			return -EINVAL;
 
-		transport->ep_disconnect(ev->u.ep_disconnect.ep_handle);
+		ep = iscsi_lookup_endpoint(ev->u.ep_disconnect.ep_handle);
+		if (!ep)
+			return -EINVAL;
+
+		transport->ep_disconnect(ep);
 		break;
 	}
 	return rc;
@@ -1195,6 +1408,7 @@
 	struct iscsi_internal *priv;
 	struct iscsi_cls_session *session;
 	struct iscsi_cls_conn *conn;
+	struct iscsi_endpoint *ep = NULL;
 
 	priv = iscsi_if_transport_lookup(iscsi_ptr(ev->transport_handle));
 	if (!priv)
@@ -1208,7 +1422,22 @@
 
 	switch (nlh->nlmsg_type) {
 	case ISCSI_UEVENT_CREATE_SESSION:
-		err = iscsi_if_create_session(priv, ev);
+		err = iscsi_if_create_session(priv, ep, ev,
+					      ev->u.c_session.initial_cmdsn,
+					      ev->u.c_session.cmds_max,
+					      ev->u.c_session.queue_depth);
+		break;
+	case ISCSI_UEVENT_CREATE_BOUND_SESSION:
+		ep = iscsi_lookup_endpoint(ev->u.c_bound_session.ep_handle);
+		if (!ep) {
+			err = -EINVAL;
+			break;
+		}
+
+		err = iscsi_if_create_session(priv, ep, ev,
+					ev->u.c_bound_session.initial_cmdsn,
+					ev->u.c_bound_session.cmds_max,
+					ev->u.c_bound_session.queue_depth);
 		break;
 	case ISCSI_UEVENT_DESTROY_SESSION:
 		session = iscsi_session_lookup(ev->u.d_session.sid);
@@ -1414,6 +1643,8 @@
 iscsi_session_attr(fast_abort, ISCSI_PARAM_FAST_ABORT, 0);
 iscsi_session_attr(abort_tmo, ISCSI_PARAM_ABORT_TMO, 0);
 iscsi_session_attr(lu_reset_tmo, ISCSI_PARAM_LU_RESET_TMO, 0);
+iscsi_session_attr(ifacename, ISCSI_PARAM_IFACE_NAME, 0);
+iscsi_session_attr(initiatorname, ISCSI_PARAM_INITIATOR_NAME, 0)
 
 static ssize_t
 show_priv_session_state(struct device *dev, struct device_attribute *attr,
@@ -1580,6 +1811,8 @@
 	priv->daemon_pid = -1;
 	priv->iscsi_transport = tt;
 	priv->t.user_scan = iscsi_user_scan;
+	if (!(tt->caps & CAP_DATA_PATH_OFFLOAD))
+		priv->t.create_work_queue = 1;
 
 	priv->dev.class = &iscsi_transport_class;
 	snprintf(priv->dev.bus_id, BUS_ID_SIZE, "%s", tt->name);
@@ -1595,7 +1828,7 @@
 	priv->t.host_attrs.ac.attrs = &priv->host_attrs[0];
 	priv->t.host_attrs.ac.class = &iscsi_host_class.class;
 	priv->t.host_attrs.ac.match = iscsi_host_match;
-	priv->t.host_size = sizeof(struct iscsi_host);
+	priv->t.host_size = sizeof(struct iscsi_cls_host);
 	transport_container_register(&priv->t.host_attrs);
 
 	SETUP_HOST_RD_ATTR(netdev, ISCSI_HOST_NETDEV_NAME);
@@ -1653,6 +1886,8 @@
 	SETUP_SESSION_RD_ATTR(fast_abort, ISCSI_FAST_ABORT);
 	SETUP_SESSION_RD_ATTR(abort_tmo, ISCSI_ABORT_TMO);
 	SETUP_SESSION_RD_ATTR(lu_reset_tmo,ISCSI_LU_RESET_TMO);
+	SETUP_SESSION_RD_ATTR(ifacename, ISCSI_IFACE_NAME);
+	SETUP_SESSION_RD_ATTR(initiatorname, ISCSI_INITIATOR_NAME);
 	SETUP_PRIV_SESSION_RD_ATTR(recovery_tmo);
 	SETUP_PRIV_SESSION_RD_ATTR(state);
 
@@ -1668,6 +1903,7 @@
 
 unregister_dev:
 	device_unregister(&priv->dev);
+	return NULL;
 free_priv:
 	kfree(priv);
 	return NULL;
@@ -1715,10 +1951,14 @@
 	if (err)
 		return err;
 
-	err = transport_class_register(&iscsi_host_class);
+	err = class_register(&iscsi_endpoint_class);
 	if (err)
 		goto unregister_transport_class;
 
+	err = transport_class_register(&iscsi_host_class);
+	if (err)
+		goto unregister_endpoint_class;
+
 	err = transport_class_register(&iscsi_connection_class);
 	if (err)
 		goto unregister_host_class;
@@ -1727,8 +1967,8 @@
 	if (err)
 		goto unregister_conn_class;
 
-	nls = netlink_kernel_create(&init_net, NETLINK_ISCSI, 1, iscsi_if_rx, NULL,
-			THIS_MODULE);
+	nls = netlink_kernel_create(&init_net, NETLINK_ISCSI, 1, iscsi_if_rx,
+				    NULL, THIS_MODULE);
 	if (!nls) {
 		err = -ENOBUFS;
 		goto unregister_session_class;
@@ -1748,6 +1988,8 @@
 	transport_class_unregister(&iscsi_connection_class);
 unregister_host_class:
 	transport_class_unregister(&iscsi_host_class);
+unregister_endpoint_class:
+	class_unregister(&iscsi_endpoint_class);
 unregister_transport_class:
 	class_unregister(&iscsi_transport_class);
 	return err;
@@ -1760,6 +2002,7 @@
 	transport_class_unregister(&iscsi_connection_class);
 	transport_class_unregister(&iscsi_session_class);
 	transport_class_unregister(&iscsi_host_class);
+	class_unregister(&iscsi_endpoint_class);
 	class_unregister(&iscsi_transport_class);
 }
 
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index d53312c..0c63947 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -58,8 +58,8 @@
 #include <scsi/scsi_host.h>
 #include <scsi/scsi_ioctl.h>
 #include <scsi/scsicam.h>
-#include <scsi/sd.h>
 
+#include "sd.h"
 #include "scsi_logging.h"
 
 MODULE_AUTHOR("Eric Youngdale");
@@ -295,11 +295,6 @@
 	}
 }
 
-static inline struct scsi_disk *scsi_disk(struct gendisk *disk)
-{
-	return container_of(disk->private_data, struct scsi_disk, driver);
-}
-
 static struct scsi_disk *__scsi_disk_get(struct gendisk *disk)
 {
 	struct scsi_disk *sdkp = NULL;
diff --git a/include/scsi/sd.h b/drivers/scsi/sd.h
similarity index 91%
rename from include/scsi/sd.h
rename to drivers/scsi/sd.h
index 4f032d4..03a3d45 100644
--- a/include/scsi/sd.h
+++ b/drivers/scsi/sd.h
@@ -48,6 +48,11 @@
 };
 #define to_scsi_disk(obj) container_of(obj,struct scsi_disk,dev)
 
+static inline struct scsi_disk *scsi_disk(struct gendisk *disk)
+{
+	return container_of(disk->private_data, struct scsi_disk, driver);
+}
+
 #define sd_printk(prefix, sdsk, fmt, a...)				\
         (sdsk)->disk ?							\
 	sdev_printk(prefix, (sdsk)->device, "[%s] " fmt,		\
diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c
index fccd2e8..d3b8ebb 100644
--- a/drivers/scsi/sg.c
+++ b/drivers/scsi/sg.c
@@ -1036,6 +1036,9 @@
 		case SG_SCSI_RESET_DEVICE:
 			val = SCSI_TRY_RESET_DEVICE;
 			break;
+		case SG_SCSI_RESET_TARGET:
+			val = SCSI_TRY_RESET_TARGET;
+			break;
 		case SG_SCSI_RESET_BUS:
 			val = SCSI_TRY_RESET_BUS;
 			break;
diff --git a/drivers/scsi/sym53c8xx_2/sym_misc.h b/drivers/scsi/sym53c8xx_2/sym_misc.h
index 0433d5d..4305371 100644
--- a/drivers/scsi/sym53c8xx_2/sym_misc.h
+++ b/drivers/scsi/sym53c8xx_2/sym_misc.h
@@ -121,9 +121,7 @@
 	}
 }
 
-#define sym_que_entry(ptr, type, member) \
-	((type *)((char *)(ptr)-(unsigned int)(&((type *)0)->member)))
-
+#define sym_que_entry(ptr, type, member) container_of(ptr, type, member)
 
 #define sym_insque(new, pos)		__sym_que_add(new, pos, (pos)->flink)
 
diff --git a/include/linux/crc-t10dif.h b/include/linux/crc-t10dif.h
new file mode 100644
index 0000000..a9c96d8
--- /dev/null
+++ b/include/linux/crc-t10dif.h
@@ -0,0 +1,8 @@
+#ifndef _LINUX_CRC_T10DIF_H
+#define _LINUX_CRC_T10DIF_H
+
+#include <linux/types.h>
+
+__u16 crc_t10dif(unsigned char const *, size_t);
+
+#endif
diff --git a/include/scsi/iscsi_if.h b/include/scsi/iscsi_if.h
index e19e584..16be12f 100644
--- a/include/scsi/iscsi_if.h
+++ b/include/scsi/iscsi_if.h
@@ -50,6 +50,7 @@
 	ISCSI_UEVENT_TGT_DSCVR		= UEVENT_BASE + 15,
 	ISCSI_UEVENT_SET_HOST_PARAM	= UEVENT_BASE + 16,
 	ISCSI_UEVENT_UNBIND_SESSION	= UEVENT_BASE + 17,
+	ISCSI_UEVENT_CREATE_BOUND_SESSION	= UEVENT_BASE + 18,
 
 	/* up events */
 	ISCSI_KEVENT_RECV_PDU		= KEVENT_BASE + 1,
@@ -78,6 +79,12 @@
 			uint16_t	cmds_max;
 			uint16_t	queue_depth;
 		} c_session;
+		struct msg_create_bound_session {
+			uint64_t	ep_handle;
+			uint32_t	initial_cmdsn;
+			uint16_t	cmds_max;
+			uint16_t	queue_depth;
+		} c_bound_session;
 		struct msg_destroy_session {
 			uint32_t	sid;
 		} d_session;
@@ -250,42 +257,49 @@
 
 	ISCSI_PARAM_PING_TMO,
 	ISCSI_PARAM_RECV_TMO,
+
+	ISCSI_PARAM_IFACE_NAME,
+	ISCSI_PARAM_ISID,
+	ISCSI_PARAM_INITIATOR_NAME,
 	/* must always be last */
 	ISCSI_PARAM_MAX,
 };
 
-#define ISCSI_MAX_RECV_DLENGTH		(1 << ISCSI_PARAM_MAX_RECV_DLENGTH)
-#define ISCSI_MAX_XMIT_DLENGTH		(1 << ISCSI_PARAM_MAX_XMIT_DLENGTH)
-#define ISCSI_HDRDGST_EN		(1 << ISCSI_PARAM_HDRDGST_EN)
-#define ISCSI_DATADGST_EN		(1 << ISCSI_PARAM_DATADGST_EN)
-#define ISCSI_INITIAL_R2T_EN		(1 << ISCSI_PARAM_INITIAL_R2T_EN)
-#define ISCSI_MAX_R2T			(1 << ISCSI_PARAM_MAX_R2T)
-#define ISCSI_IMM_DATA_EN		(1 << ISCSI_PARAM_IMM_DATA_EN)
-#define ISCSI_FIRST_BURST		(1 << ISCSI_PARAM_FIRST_BURST)
-#define ISCSI_MAX_BURST			(1 << ISCSI_PARAM_MAX_BURST)
-#define ISCSI_PDU_INORDER_EN		(1 << ISCSI_PARAM_PDU_INORDER_EN)
-#define ISCSI_DATASEQ_INORDER_EN	(1 << ISCSI_PARAM_DATASEQ_INORDER_EN)
-#define ISCSI_ERL			(1 << ISCSI_PARAM_ERL)
-#define ISCSI_IFMARKER_EN		(1 << ISCSI_PARAM_IFMARKER_EN)
-#define ISCSI_OFMARKER_EN		(1 << ISCSI_PARAM_OFMARKER_EN)
-#define ISCSI_EXP_STATSN		(1 << ISCSI_PARAM_EXP_STATSN)
-#define ISCSI_TARGET_NAME		(1 << ISCSI_PARAM_TARGET_NAME)
-#define ISCSI_TPGT			(1 << ISCSI_PARAM_TPGT)
-#define ISCSI_PERSISTENT_ADDRESS	(1 << ISCSI_PARAM_PERSISTENT_ADDRESS)
-#define ISCSI_PERSISTENT_PORT		(1 << ISCSI_PARAM_PERSISTENT_PORT)
-#define ISCSI_SESS_RECOVERY_TMO		(1 << ISCSI_PARAM_SESS_RECOVERY_TMO)
-#define ISCSI_CONN_PORT			(1 << ISCSI_PARAM_CONN_PORT)
-#define ISCSI_CONN_ADDRESS		(1 << ISCSI_PARAM_CONN_ADDRESS)
-#define ISCSI_USERNAME			(1 << ISCSI_PARAM_USERNAME)
-#define ISCSI_USERNAME_IN		(1 << ISCSI_PARAM_USERNAME_IN)
-#define ISCSI_PASSWORD			(1 << ISCSI_PARAM_PASSWORD)
-#define ISCSI_PASSWORD_IN		(1 << ISCSI_PARAM_PASSWORD_IN)
-#define ISCSI_FAST_ABORT		(1 << ISCSI_PARAM_FAST_ABORT)
-#define ISCSI_ABORT_TMO			(1 << ISCSI_PARAM_ABORT_TMO)
-#define ISCSI_LU_RESET_TMO		(1 << ISCSI_PARAM_LU_RESET_TMO)
-#define ISCSI_HOST_RESET_TMO		(1 << ISCSI_PARAM_HOST_RESET_TMO)
-#define ISCSI_PING_TMO			(1 << ISCSI_PARAM_PING_TMO)
-#define ISCSI_RECV_TMO			(1 << ISCSI_PARAM_RECV_TMO)
+#define ISCSI_MAX_RECV_DLENGTH		(1ULL << ISCSI_PARAM_MAX_RECV_DLENGTH)
+#define ISCSI_MAX_XMIT_DLENGTH		(1ULL << ISCSI_PARAM_MAX_XMIT_DLENGTH)
+#define ISCSI_HDRDGST_EN		(1ULL << ISCSI_PARAM_HDRDGST_EN)
+#define ISCSI_DATADGST_EN		(1ULL << ISCSI_PARAM_DATADGST_EN)
+#define ISCSI_INITIAL_R2T_EN		(1ULL << ISCSI_PARAM_INITIAL_R2T_EN)
+#define ISCSI_MAX_R2T			(1ULL << ISCSI_PARAM_MAX_R2T)
+#define ISCSI_IMM_DATA_EN		(1ULL << ISCSI_PARAM_IMM_DATA_EN)
+#define ISCSI_FIRST_BURST		(1ULL << ISCSI_PARAM_FIRST_BURST)
+#define ISCSI_MAX_BURST			(1ULL << ISCSI_PARAM_MAX_BURST)
+#define ISCSI_PDU_INORDER_EN		(1ULL << ISCSI_PARAM_PDU_INORDER_EN)
+#define ISCSI_DATASEQ_INORDER_EN	(1ULL << ISCSI_PARAM_DATASEQ_INORDER_EN)
+#define ISCSI_ERL			(1ULL << ISCSI_PARAM_ERL)
+#define ISCSI_IFMARKER_EN		(1ULL << ISCSI_PARAM_IFMARKER_EN)
+#define ISCSI_OFMARKER_EN		(1ULL << ISCSI_PARAM_OFMARKER_EN)
+#define ISCSI_EXP_STATSN		(1ULL << ISCSI_PARAM_EXP_STATSN)
+#define ISCSI_TARGET_NAME		(1ULL << ISCSI_PARAM_TARGET_NAME)
+#define ISCSI_TPGT			(1ULL << ISCSI_PARAM_TPGT)
+#define ISCSI_PERSISTENT_ADDRESS	(1ULL << ISCSI_PARAM_PERSISTENT_ADDRESS)
+#define ISCSI_PERSISTENT_PORT		(1ULL << ISCSI_PARAM_PERSISTENT_PORT)
+#define ISCSI_SESS_RECOVERY_TMO		(1ULL << ISCSI_PARAM_SESS_RECOVERY_TMO)
+#define ISCSI_CONN_PORT			(1ULL << ISCSI_PARAM_CONN_PORT)
+#define ISCSI_CONN_ADDRESS		(1ULL << ISCSI_PARAM_CONN_ADDRESS)
+#define ISCSI_USERNAME			(1ULL << ISCSI_PARAM_USERNAME)
+#define ISCSI_USERNAME_IN		(1ULL << ISCSI_PARAM_USERNAME_IN)
+#define ISCSI_PASSWORD			(1ULL << ISCSI_PARAM_PASSWORD)
+#define ISCSI_PASSWORD_IN		(1ULL << ISCSI_PARAM_PASSWORD_IN)
+#define ISCSI_FAST_ABORT		(1ULL << ISCSI_PARAM_FAST_ABORT)
+#define ISCSI_ABORT_TMO			(1ULL << ISCSI_PARAM_ABORT_TMO)
+#define ISCSI_LU_RESET_TMO		(1ULL << ISCSI_PARAM_LU_RESET_TMO)
+#define ISCSI_HOST_RESET_TMO		(1ULL << ISCSI_PARAM_HOST_RESET_TMO)
+#define ISCSI_PING_TMO			(1ULL << ISCSI_PARAM_PING_TMO)
+#define ISCSI_RECV_TMO			(1ULL << ISCSI_PARAM_RECV_TMO)
+#define ISCSI_IFACE_NAME		(1ULL << ISCSI_PARAM_IFACE_NAME)
+#define ISCSI_ISID			(1ULL << ISCSI_PARAM_ISID)
+#define ISCSI_INITIATOR_NAME		(1ULL << ISCSI_PARAM_INITIATOR_NAME)
 
 /* iSCSI HBA params */
 enum iscsi_host_param {
@@ -296,20 +310,13 @@
 	ISCSI_HOST_PARAM_MAX,
 };
 
-#define ISCSI_HOST_HWADDRESS		(1 << ISCSI_HOST_PARAM_HWADDRESS)
-#define ISCSI_HOST_INITIATOR_NAME	(1 << ISCSI_HOST_PARAM_INITIATOR_NAME)
-#define ISCSI_HOST_NETDEV_NAME		(1 << ISCSI_HOST_PARAM_NETDEV_NAME)
-#define ISCSI_HOST_IPADDRESS		(1 << ISCSI_HOST_PARAM_IPADDRESS)
+#define ISCSI_HOST_HWADDRESS		(1ULL << ISCSI_HOST_PARAM_HWADDRESS)
+#define ISCSI_HOST_INITIATOR_NAME	(1ULL << ISCSI_HOST_PARAM_INITIATOR_NAME)
+#define ISCSI_HOST_NETDEV_NAME		(1ULL << ISCSI_HOST_PARAM_NETDEV_NAME)
+#define ISCSI_HOST_IPADDRESS		(1ULL << ISCSI_HOST_PARAM_IPADDRESS)
 
 #define iscsi_ptr(_handle) ((void*)(unsigned long)_handle)
 #define iscsi_handle(_ptr) ((uint64_t)(unsigned long)_ptr)
-#define hostdata_session(_hostdata) (iscsi_ptr(*(unsigned long *)_hostdata))
-
-/**
- * iscsi_hostdata - get LLD hostdata from scsi_host
- * @_hostdata: pointer to scsi host's hostdata
- **/
-#define iscsi_hostdata(_hostdata) ((void*)_hostdata + sizeof(unsigned long))
 
 /*
  * These flags presents iSCSI Data-Path capabilities.
diff --git a/include/scsi/iscsi_proto.h b/include/scsi/iscsi_proto.h
index e0593bf..f2a2c11 100644
--- a/include/scsi/iscsi_proto.h
+++ b/include/scsi/iscsi_proto.h
@@ -22,6 +22,7 @@
 #define ISCSI_PROTO_H
 
 #include <linux/types.h>
+#include <scsi/scsi.h>
 
 #define ISCSI_DRAFT20_VERSION	0x00
 
@@ -156,7 +157,7 @@
 	uint8_t ahstype;
 	uint8_t reserved;
 	/* 4-byte aligned extended CDB spillover */
-	uint8_t ecdb[260 - ISCSI_CDB_SIZE];
+	uint8_t ecdb[SCSI_MAX_VARLEN_CDB_SIZE - ISCSI_CDB_SIZE];
 };
 
 /* SCSI Response Header */
diff --git a/include/scsi/libiscsi.h b/include/scsi/libiscsi.h
index cd3ca63..5e75bb7 100644
--- a/include/scsi/libiscsi.h
+++ b/include/scsi/libiscsi.h
@@ -24,6 +24,7 @@
 #define LIBISCSI_H
 
 #include <linux/types.h>
+#include <linux/wait.h>
 #include <linux/mutex.h>
 #include <linux/timer.h>
 #include <linux/workqueue.h>
@@ -31,6 +32,7 @@
 #include <scsi/iscsi_if.h>
 
 struct scsi_transport_template;
+struct scsi_host_template;
 struct scsi_device;
 struct Scsi_Host;
 struct scsi_cmnd;
@@ -40,6 +42,7 @@
 struct iscsi_cls_conn;
 struct iscsi_session;
 struct iscsi_nopin;
+struct device;
 
 /* #define DEBUG_SCSI */
 #ifdef DEBUG_SCSI
@@ -49,9 +52,7 @@
 #endif
 
 #define ISCSI_DEF_XMIT_CMDS_MAX	128	/* must be power of 2 */
-#define ISCSI_MGMT_CMDS_MAX	16	/* must be power of 2 */
-
-#define ISCSI_MGMT_ITT_OFFSET	0xa00
+#define ISCSI_MGMT_CMDS_MAX	15
 
 #define ISCSI_DEF_CMD_PER_LUN		32
 #define ISCSI_MAX_CMD_PER_LUN		128
@@ -69,7 +70,10 @@
 /* Connection suspend "bit" */
 #define ISCSI_SUSPEND_BIT		1
 
-#define ISCSI_ITT_MASK			(0xfff)
+#define ISCSI_ITT_MASK			(0x1fff)
+#define ISCSI_TOTAL_CMDS_MAX		4096
+/* this must be a power of two greater than ISCSI_MGMT_CMDS_MAX */
+#define ISCSI_TOTAL_CMDS_MIN		16
 #define ISCSI_AGE_SHIFT			28
 #define ISCSI_AGE_MASK			(0xf << ISCSI_AGE_SHIFT)
 
@@ -82,18 +86,6 @@
 	ISCSI_DIGEST_SIZE = sizeof(__u32),
 };
 
-struct iscsi_mgmt_task {
-	/*
-	 * Becuae LLDs allocate their hdr differently, this is a pointer to
-	 * that storage. It must be setup at session creation time.
-	 */
-	struct iscsi_hdr	*hdr;
-	char			*data;		/* mgmt payload */
-	unsigned		data_count;	/* counts data to be sent */
-	uint32_t		itt;		/* this ITT */
-	void			*dd_data;	/* driver/transport data */
-	struct list_head	running;
-};
 
 enum {
 	ISCSI_TASK_COMPLETED,
@@ -101,7 +93,7 @@
 	ISCSI_TASK_RUNNING,
 };
 
-struct iscsi_cmd_task {
+struct iscsi_task {
 	/*
 	 * Because LLDs allocate their hdr differently, this is a pointer
 	 * and length to that storage. It must be setup at session
@@ -118,6 +110,7 @@
 	/* offset in unsolicited stream (bytes); */
 	unsigned		unsol_offset;
 	unsigned		data_count;	/* remaining Data-Out */
+	char			*data;		/* mgmt payload */
 	struct scsi_cmnd	*sc;		/* associated SCSI cmd*/
 	struct iscsi_conn	*conn;		/* used connection    */
 
@@ -128,9 +121,9 @@
 	void			*dd_data;	/* driver/transport data */
 };
 
-static inline void* iscsi_next_hdr(struct iscsi_cmd_task *ctask)
+static inline void* iscsi_next_hdr(struct iscsi_task *task)
 {
-	return (void*)ctask->hdr + ctask->hdr_len;
+	return (void*)task->hdr + task->hdr_len;
 }
 
 /* Connection's states */
@@ -146,11 +139,6 @@
 	void			*dd_data;	/* iscsi_transport data */
 	struct iscsi_session	*session;	/* parent session */
 	/*
-	 * LLDs should set this lock. It protects the transport recv
-	 * code
-	 */
-	rwlock_t		*recv_lock;
-	/*
 	 * conn_stop() flag: stop to recover, stop to terminate
 	 */
         int			stop_stage;
@@ -159,7 +147,7 @@
 	unsigned long		last_ping;
 	int			ping_timeout;
 	int			recv_timeout;
-	struct iscsi_mgmt_task	*ping_mtask;
+	struct iscsi_task 	*ping_task;
 
 	/* iSCSI connection-wide sequencing */
 	uint32_t		exp_statsn;
@@ -175,9 +163,8 @@
 	 * should always fit in this buffer
 	 */
 	char			*data;
-	struct iscsi_mgmt_task	*login_mtask;	/* mtask used for login/text */
-	struct iscsi_mgmt_task	*mtask;		/* xmit mtask in progress */
-	struct iscsi_cmd_task	*ctask;		/* xmit ctask in progress */
+	struct iscsi_task 	*login_task;	/* mtask used for login/text */
+	struct iscsi_task	*task;		/* xmit task in progress */
 
 	/* xmit */
 	struct list_head	mgmtqueue;	/* mgmt (control) xmit queue */
@@ -208,9 +195,6 @@
 	/* remote portal currently connected to */
 	int			portal_port;
 	char			portal_address[ISCSI_ADDRESS_BUF_LEN];
-	/* local address */
-	int			local_port;
-	char			local_address[ISCSI_ADDRESS_BUF_LEN];
 
 	/* MIB-statistics */
 	uint64_t		txdata_octets;
@@ -246,6 +230,7 @@
 };
 
 struct iscsi_session {
+	struct iscsi_cls_session *cls_session;
 	/*
 	 * Syncs up the scsi eh thread with the iscsi eh thread when sending
 	 * task management functions. This must be taken before the session
@@ -281,10 +266,8 @@
 	char			*password;
 	char			*password_in;
 	char			*targetname;
+	char			*ifacename;
 	char			*initiatorname;
-	/* hw address or netdev iscsi connection is bound to */
-	char			*hwaddress;
-	char			*netdev;
 	/* control data */
 	struct iscsi_transport	*tt;
 	struct Scsi_Host	*host;
@@ -298,12 +281,20 @@
 	int			state;		/* session state           */
 	int			age;		/* counts session re-opens */
 
+	int			scsi_cmds_max; 	/* max scsi commands */
 	int			cmds_max;	/* size of cmds array */
-	struct iscsi_cmd_task	**cmds;		/* Original Cmds arr */
+	struct iscsi_task	**cmds;		/* Original Cmds arr */
 	struct iscsi_pool	cmdpool;	/* PDU's pool */
-	int			mgmtpool_max;	/* size of mgmt array */
-	struct iscsi_mgmt_task	**mgmt_cmds;	/* Original mgmt arr */
-	struct iscsi_pool	mgmtpool;	/* Mgmt PDU's pool */
+};
+
+struct iscsi_host {
+	char			*initiatorname;
+	/* hw address or netdev iscsi connection is bound to */
+	char			*hwaddress;
+	char			*netdev;
+	/* local address */
+	int			local_port;
+	char			local_address[ISCSI_ADDRESS_BUF_LEN];
 };
 
 /*
@@ -316,42 +307,44 @@
 extern int iscsi_queuecommand(struct scsi_cmnd *sc,
 			      void (*done)(struct scsi_cmnd *));
 
-
 /*
  * iSCSI host helpers.
  */
+#define iscsi_host_priv(_shost) \
+	(shost_priv(_shost) + sizeof(struct iscsi_host))
+
 extern int iscsi_host_set_param(struct Scsi_Host *shost,
 				enum iscsi_host_param param, char *buf,
 				int buflen);
 extern int iscsi_host_get_param(struct Scsi_Host *shost,
 				enum iscsi_host_param param, char *buf);
+extern int iscsi_host_add(struct Scsi_Host *shost, struct device *pdev);
+extern struct Scsi_Host *iscsi_host_alloc(struct scsi_host_template *sht,
+					  int dd_data_size, uint16_t qdepth);
+extern void iscsi_host_remove(struct Scsi_Host *shost);
+extern void iscsi_host_free(struct Scsi_Host *shost);
 
 /*
  * session management
  */
 extern struct iscsi_cls_session *
-iscsi_session_setup(struct iscsi_transport *, struct scsi_transport_template *,
-		    uint16_t, uint16_t, int, int, uint32_t, uint32_t *);
+iscsi_session_setup(struct iscsi_transport *, struct Scsi_Host *shost,
+		    uint16_t, int, uint32_t, unsigned int);
 extern void iscsi_session_teardown(struct iscsi_cls_session *);
-extern struct iscsi_session *class_to_transport_session(struct iscsi_cls_session *);
 extern void iscsi_session_recovery_timedout(struct iscsi_cls_session *);
 extern int iscsi_set_param(struct iscsi_cls_conn *cls_conn,
 			   enum iscsi_param param, char *buf, int buflen);
 extern int iscsi_session_get_param(struct iscsi_cls_session *cls_session,
 				   enum iscsi_param param, char *buf);
 
-#define session_to_cls(_sess) \
-	hostdata_session(_sess->host->hostdata)
-
 #define iscsi_session_printk(prefix, _sess, fmt, a...)	\
-	iscsi_cls_session_printk(prefix,		\
-		(struct iscsi_cls_session *)session_to_cls(_sess), fmt, ##a)
+	iscsi_cls_session_printk(prefix, _sess->cls_session, fmt, ##a)
 
 /*
  * connection management
  */
 extern struct iscsi_cls_conn *iscsi_conn_setup(struct iscsi_cls_session *,
-					       uint32_t);
+					       int, uint32_t);
 extern void iscsi_conn_teardown(struct iscsi_cls_conn *);
 extern int iscsi_conn_start(struct iscsi_cls_conn *);
 extern void iscsi_conn_stop(struct iscsi_cls_conn *, int);
@@ -360,25 +353,29 @@
 extern void iscsi_conn_failure(struct iscsi_conn *conn, enum iscsi_err err);
 extern int iscsi_conn_get_param(struct iscsi_cls_conn *cls_conn,
 				enum iscsi_param param, char *buf);
+extern void iscsi_suspend_tx(struct iscsi_conn *conn);
 
 #define iscsi_conn_printk(prefix, _c, fmt, a...) \
-	iscsi_cls_conn_printk(prefix, _c->cls_conn, fmt, ##a)
+	iscsi_cls_conn_printk(prefix, ((struct iscsi_conn *)_c)->cls_conn, \
+			      fmt, ##a)
 
 /*
  * pdu and task processing
  */
 extern void iscsi_update_cmdsn(struct iscsi_session *, struct iscsi_nopin *);
-extern void iscsi_prep_unsolicit_data_pdu(struct iscsi_cmd_task *,
+extern void iscsi_prep_unsolicit_data_pdu(struct iscsi_task *,
 					struct iscsi_data *hdr);
 extern int iscsi_conn_send_pdu(struct iscsi_cls_conn *, struct iscsi_hdr *,
 				char *, uint32_t);
 extern int iscsi_complete_pdu(struct iscsi_conn *, struct iscsi_hdr *,
 			      char *, int);
-extern int iscsi_verify_itt(struct iscsi_conn *, struct iscsi_hdr *,
-			    uint32_t *);
-extern void iscsi_requeue_ctask(struct iscsi_cmd_task *ctask);
-extern void iscsi_free_mgmt_task(struct iscsi_conn *conn,
-				 struct iscsi_mgmt_task *mtask);
+extern int __iscsi_complete_pdu(struct iscsi_conn *, struct iscsi_hdr *,
+				char *, int);
+extern int iscsi_verify_itt(struct iscsi_conn *, itt_t);
+extern struct iscsi_task *iscsi_itt_to_ctask(struct iscsi_conn *, itt_t);
+extern void iscsi_requeue_task(struct iscsi_task *task);
+extern void iscsi_put_task(struct iscsi_task *task);
+extern void __iscsi_get_task(struct iscsi_task *task);
 
 /*
  * generic helpers
diff --git a/include/scsi/scsi.h b/include/scsi/scsi.h
index 32742c4..00137a7 100644
--- a/include/scsi/scsi.h
+++ b/include/scsi/scsi.h
@@ -9,6 +9,7 @@
 #define _SCSI_SCSI_H
 
 #include <linux/types.h>
+#include <scsi/scsi_cmnd.h>
 
 /*
  * The maximum number of SG segments that we will put inside a
@@ -400,6 +401,7 @@
 #define SOFT_ERROR      0x2005
 #define ADD_TO_MLQUEUE  0x2006
 #define TIMEOUT_ERROR   0x2007
+#define SCSI_RETURN_NOT_HANDLED   0x2008
 
 /*
  * Midlevel queue return values.
@@ -424,6 +426,22 @@
 #define driver_byte(result) (((result) >> 24) & 0xff)
 #define suggestion(result)  (driver_byte(result) & SUGGEST_MASK)
 
+static inline void set_msg_byte(struct scsi_cmnd *cmd, char status)
+{
+	cmd->result |= status << 8;
+}
+
+static inline void set_host_byte(struct scsi_cmnd *cmd, char status)
+{
+	cmd->result |= status << 16;
+}
+
+static inline void set_driver_byte(struct scsi_cmnd *cmd, char status)
+{
+	cmd->result |= status << 24;
+}
+
+
 #define sense_class(sense)  (((sense) >> 4) & 0x7)
 #define sense_error(sense)  ((sense) & 0xf)
 #define sense_valid(sense)  ((sense) & 0x80);
diff --git a/include/scsi/scsi_cmnd.h b/include/scsi/scsi_cmnd.h
index 3e46dfa..66c9448 100644
--- a/include/scsi/scsi_cmnd.h
+++ b/include/scsi/scsi_cmnd.h
@@ -7,7 +7,6 @@
 #include <linux/types.h>
 #include <linux/timer.h>
 #include <linux/scatterlist.h>
-#include <linux/blkdev.h>
 
 struct Scsi_Host;
 struct scsi_device;
diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h
index 00b7876..6467f78 100644
--- a/include/scsi/scsi_device.h
+++ b/include/scsi/scsi_device.h
@@ -162,9 +162,29 @@
 
 	struct execute_work	ew; /* used to get process context on put */
 
+	struct scsi_dh_data	*scsi_dh_data;
 	enum scsi_device_state sdev_state;
 	unsigned long		sdev_data[0];
 } __attribute__((aligned(sizeof(unsigned long))));
+
+struct scsi_device_handler {
+	/* Used by the infrastructure */
+	struct list_head list; /* list of scsi_device_handlers */
+	struct notifier_block nb;
+
+	/* Filled by the hardware handler */
+	struct module *module;
+	const char *name;
+	int (*check_sense)(struct scsi_device *, struct scsi_sense_hdr *);
+	int (*activate)(struct scsi_device *);
+	int (*prep_fn)(struct scsi_device *, struct request *);
+};
+
+struct scsi_dh_data {
+	struct scsi_device_handler *scsi_dh;
+	char buf[0];
+};
+
 #define	to_scsi_device(d)	\
 	container_of(d, struct scsi_device, sdev_gendev)
 #define	class_to_sdev(d)	\
@@ -231,7 +251,9 @@
 		uint, uint, uint, void *hostdata);
 extern int scsi_add_device(struct Scsi_Host *host, uint channel,
 			   uint target, uint lun);
+extern int scsi_register_device_handler(struct scsi_device_handler *scsi_dh);
 extern void scsi_remove_device(struct scsi_device *);
+extern int scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh);
 
 extern int scsi_device_get(struct scsi_device *);
 extern void scsi_device_put(struct scsi_device *);
diff --git a/include/scsi/scsi_dh.h b/include/scsi/scsi_dh.h
new file mode 100644
index 0000000..3ad2303
--- /dev/null
+++ b/include/scsi/scsi_dh.h
@@ -0,0 +1,69 @@
+/*
+ * Header file for SCSI device handler infrastruture.
+ *
+ * Modified version of patches posted by Mike Christie <michaelc@cs.wisc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright IBM Corporation, 2007
+ *      Authors:
+ *               Chandra Seetharaman <sekharan@us.ibm.com>
+ *               Mike Anderson <andmike@linux.vnet.ibm.com>
+ */
+
+#include <scsi/scsi_device.h>
+
+enum {
+	SCSI_DH_OK = 0,
+	/*
+	 * device errors
+	 */
+	SCSI_DH_DEV_FAILED,	/* generic device error */
+	SCSI_DH_DEV_TEMP_BUSY,
+	SCSI_DH_DEVICE_MAX,	/* max device blkerr definition */
+
+	/*
+	 * transport errors
+	 */
+	SCSI_DH_NOTCONN = SCSI_DH_DEVICE_MAX + 1,
+	SCSI_DH_CONN_FAILURE,
+	SCSI_DH_TRANSPORT_MAX,	/* max transport blkerr definition */
+
+	/*
+	 * driver and generic errors
+	 */
+	SCSI_DH_IO = SCSI_DH_TRANSPORT_MAX + 1,	/* generic error */
+	SCSI_DH_INVALID_IO,
+	SCSI_DH_RETRY,		/* retry the req, but not immediately */
+	SCSI_DH_IMM_RETRY,	/* immediately retry the req */
+	SCSI_DH_TIMED_OUT,
+	SCSI_DH_RES_TEMP_UNAVAIL,
+	SCSI_DH_DEV_OFFLINED,
+	SCSI_DH_NOSYS,
+	SCSI_DH_DRIVER_MAX,
+};
+#if defined(CONFIG_SCSI_DH) || defined(CONFIG_SCSI_DH_MODULE)
+extern int scsi_dh_activate(struct request_queue *);
+extern int scsi_dh_handler_exist(const char *);
+#else
+static inline int scsi_dh_activate(struct request_queue *req)
+{
+	return 0;
+}
+static inline int scsi_dh_handler_exist(const char *name)
+{
+	return 0;
+}
+#endif
diff --git a/include/scsi/scsi_transport_iscsi.h b/include/scsi/scsi_transport_iscsi.h
index aab1eae..f5444e0 100644
--- a/include/scsi/scsi_transport_iscsi.h
+++ b/include/scsi/scsi_transport_iscsi.h
@@ -30,11 +30,11 @@
 
 struct scsi_transport_template;
 struct iscsi_transport;
+struct iscsi_endpoint;
 struct Scsi_Host;
 struct iscsi_cls_conn;
 struct iscsi_conn;
-struct iscsi_cmd_task;
-struct iscsi_mgmt_task;
+struct iscsi_task;
 struct sockaddr;
 
 /**
@@ -58,19 +58,22 @@
  * @stop_conn:		suspend/recover/terminate connection
  * @send_pdu:		send iSCSI PDU, Login, Logout, NOP-Out, Reject, Text.
  * @session_recovery_timedout: notify LLD a block during recovery timed out
- * @init_cmd_task:	Initialize a iscsi_cmd_task and any internal structs.
- *			Called from queuecommand with session lock held.
- * @init_mgmt_task:	Initialize a iscsi_mgmt_task and any internal structs.
- *			Called from iscsi_conn_send_generic with xmitmutex.
- * @xmit_cmd_task:	Requests LLD to transfer cmd task. Returns 0 or the
+ * @init_task:		Initialize a iscsi_task and any internal structs.
+ *			When offloading the data path, this is called from
+ *			queuecommand with the session lock, or from the
+ *			iscsi_conn_send_pdu context with the session lock.
+ *			When not offloading the data path, this is called
+ *			from the scsi work queue without the session lock.
+ * @xmit_task		Requests LLD to transfer cmd task. Returns 0 or the
  *			the number of bytes transferred on success, and -Exyz
- *			value on error.
- * @xmit_mgmt_task:	Requests LLD to transfer mgmt task. Returns 0 or the
- *			the number of bytes transferred on success, and -Exyz
- *			value on error.
- * @cleanup_cmd_task:	requests LLD to fail cmd task. Called with xmitmutex
- *			and session->lock after the connection has been
- *			suspended and terminated during recovery. If called
+ *			value on error. When offloading the data path, this
+ *			is called from queuecommand with the session lock, or
+ *			from the iscsi_conn_send_pdu context with the session
+ *			lock. When not offloading the data path, this is called
+ *			from the scsi work queue without the session lock.
+ * @cleanup_task:	requests LLD to fail task. Called with session lock
+ *			and after the connection has been suspended and
+ *			terminated during recovery. If called
  *			from abort task then connection is not suspended
  *			or terminated but sk_callback_lock is held
  *
@@ -83,17 +86,9 @@
 	/* LLD sets this to indicate what values it can export to sysfs */
 	uint64_t param_mask;
 	uint64_t host_param_mask;
-	struct scsi_host_template *host_template;
-	/* LLD connection data size */
-	int conndata_size;
-	/* LLD session data size */
-	int sessiondata_size;
-	int max_lun;
-	unsigned int max_conn;
-	unsigned int max_cmd_len;
-	struct iscsi_cls_session *(*create_session) (struct iscsi_transport *it,
-		struct scsi_transport_template *t, uint16_t, uint16_t,
-		uint32_t sn, uint32_t *hn);
+	struct iscsi_cls_session *(*create_session) (struct iscsi_endpoint *ep,
+					uint16_t cmds_max, uint16_t qdepth,
+					uint32_t sn, uint32_t *hn);
 	void (*destroy_session) (struct iscsi_cls_session *session);
 	struct iscsi_cls_conn *(*create_conn) (struct iscsi_cls_session *sess,
 				uint32_t cid);
@@ -118,20 +113,15 @@
 			 char *data, uint32_t data_size);
 	void (*get_stats) (struct iscsi_cls_conn *conn,
 			   struct iscsi_stats *stats);
-	int (*init_cmd_task) (struct iscsi_cmd_task *ctask);
-	void (*init_mgmt_task) (struct iscsi_conn *conn,
-				struct iscsi_mgmt_task *mtask);
-	int (*xmit_cmd_task) (struct iscsi_conn *conn,
-			      struct iscsi_cmd_task *ctask);
-	void (*cleanup_cmd_task) (struct iscsi_conn *conn,
-				  struct iscsi_cmd_task *ctask);
-	int (*xmit_mgmt_task) (struct iscsi_conn *conn,
-			       struct iscsi_mgmt_task *mtask);
+	int (*init_task) (struct iscsi_task *task);
+	int (*xmit_task) (struct iscsi_task *task);
+	void (*cleanup_task) (struct iscsi_conn *conn,
+				  struct iscsi_task *task);
 	void (*session_recovery_timedout) (struct iscsi_cls_session *session);
-	int (*ep_connect) (struct sockaddr *dst_addr, int non_blocking,
-			   uint64_t *ep_handle);
-	int (*ep_poll) (uint64_t ep_handle, int timeout_ms);
-	void (*ep_disconnect) (uint64_t ep_handle);
+	struct iscsi_endpoint *(*ep_connect) (struct sockaddr *dst_addr,
+					      int non_blocking);
+	int (*ep_poll) (struct iscsi_endpoint *ep, int timeout_ms);
+	void (*ep_disconnect) (struct iscsi_endpoint *ep);
 	int (*tgt_dscvr) (struct Scsi_Host *shost, enum iscsi_tgt_dscvr type,
 			  uint32_t enable, struct sockaddr *dst_addr);
 };
@@ -172,9 +162,10 @@
 	ISCSI_SESSION_FREE,
 };
 
+#define ISCSI_MAX_TARGET -1
+
 struct iscsi_cls_session {
 	struct list_head sess_list;		/* item in session_list */
-	struct list_head host_list;
 	struct iscsi_transport *transport;
 	spinlock_t lock;
 	struct work_struct block_work;
@@ -186,7 +177,7 @@
 	int recovery_tmo;
 	struct delayed_work recovery_work;
 
-	int target_id;
+	unsigned int target_id;
 
 	int state;
 	int sid;				/* session id */
@@ -203,14 +194,22 @@
 #define starget_to_session(_stgt) \
 	iscsi_dev_to_session(_stgt->dev.parent)
 
-struct iscsi_host {
-	struct list_head sessions;
+struct iscsi_cls_host {
 	atomic_t nr_scans;
 	struct mutex mutex;
 	struct workqueue_struct *scan_workq;
 	char scan_workq_name[KOBJ_NAME_LEN];
 };
 
+extern void iscsi_host_for_each_session(struct Scsi_Host *shost,
+				void (*fn)(struct iscsi_cls_session *));
+
+struct iscsi_endpoint {
+	void *dd_data;			/* LLD private data */
+	struct device dev;
+	unsigned int id;
+};
+
 /*
  * session and connection functions that can be used by HW iSCSI LLDs
  */
@@ -222,22 +221,26 @@
 
 extern int iscsi_session_chkready(struct iscsi_cls_session *session);
 extern struct iscsi_cls_session *iscsi_alloc_session(struct Scsi_Host *shost,
-					struct iscsi_transport *transport);
+				struct iscsi_transport *transport, int dd_size);
 extern int iscsi_add_session(struct iscsi_cls_session *session,
 			     unsigned int target_id);
 extern int iscsi_session_event(struct iscsi_cls_session *session,
 			       enum iscsi_uevent_e event);
 extern struct iscsi_cls_session *iscsi_create_session(struct Scsi_Host *shost,
 						struct iscsi_transport *t,
+						int dd_size,
 						unsigned int target_id);
 extern void iscsi_remove_session(struct iscsi_cls_session *session);
 extern void iscsi_free_session(struct iscsi_cls_session *session);
 extern int iscsi_destroy_session(struct iscsi_cls_session *session);
 extern struct iscsi_cls_conn *iscsi_create_conn(struct iscsi_cls_session *sess,
-					    uint32_t cid);
+						int dd_size, uint32_t cid);
 extern int iscsi_destroy_conn(struct iscsi_cls_conn *conn);
 extern void iscsi_unblock_session(struct iscsi_cls_session *session);
 extern void iscsi_block_session(struct iscsi_cls_session *session);
 extern int iscsi_scan_finished(struct Scsi_Host *shost, unsigned long time);
+extern struct iscsi_endpoint *iscsi_create_endpoint(int dd_size);
+extern void iscsi_destroy_endpoint(struct iscsi_endpoint *ep);
+extern struct iscsi_endpoint *iscsi_lookup_endpoint(u64 handle);
 
 #endif
diff --git a/include/scsi/sg.h b/include/scsi/sg.h
index 519c49a..934ae38 100644
--- a/include/scsi/sg.h
+++ b/include/scsi/sg.h
@@ -206,6 +206,7 @@
 #define		SG_SCSI_RESET_DEVICE	1
 #define		SG_SCSI_RESET_BUS	2
 #define		SG_SCSI_RESET_HOST	3
+#define		SG_SCSI_RESET_TARGET	4
 
 /* synchronous SCSI command ioctl, (only in version 3 interface) */
 #define SG_IO 0x2285   /* similar effect as write() followed by read() */
diff --git a/lib/Kconfig b/lib/Kconfig
index 8cc8e87..c7ad7a5 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -29,6 +29,13 @@
 	  the kernel tree does. Such modules that use library CRC16
 	  functions require M here.
 
+config CRC_T10DIF
+	tristate "CRC calculation for the T10 Data Integrity Field"
+	help
+	  This option is only needed if a module that's not in the
+	  kernel tree needs to calculate CRC checks for use with the
+	  SCSI data integrity subsystem.
+
 config CRC_ITU_T
 	tristate "CRC ITU-T V.41 functions"
 	help
diff --git a/lib/Makefile b/lib/Makefile
index 4b836a5..2c62a9c 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -54,6 +54,7 @@
 obj-$(CONFIG_BITREVERSE) += bitrev.o
 obj-$(CONFIG_CRC_CCITT)	+= crc-ccitt.o
 obj-$(CONFIG_CRC16)	+= crc16.o
+obj-$(CONFIG_CRC_T10DIF)+= crc-t10dif.o
 obj-$(CONFIG_CRC_ITU_T)	+= crc-itu-t.o
 obj-$(CONFIG_CRC32)	+= crc32.o
 obj-$(CONFIG_CRC7)	+= crc7.o
diff --git a/lib/crc-t10dif.c b/lib/crc-t10dif.c
new file mode 100644
index 0000000..fbbd66e
--- /dev/null
+++ b/lib/crc-t10dif.c
@@ -0,0 +1,67 @@
+/*
+ * T10 Data Integrity Field CRC16 calculation
+ *
+ * Copyright (c) 2007 Oracle Corporation.  All rights reserved.
+ * Written by Martin K. Petersen <martin.petersen@oracle.com>
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+ */
+
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/crc-t10dif.h>
+
+/* Table generated using the following polynomium:
+ * x^16 + x^15 + x^11 + x^9 + x^8 + x^7 + x^5 + x^4 + x^2 + x + 1
+ * gt: 0x8bb7
+ */
+static const __u16 t10_dif_crc_table[256] = {
+	0x0000, 0x8BB7, 0x9CD9, 0x176E, 0xB205, 0x39B2, 0x2EDC, 0xA56B,
+	0xEFBD, 0x640A, 0x7364, 0xF8D3, 0x5DB8, 0xD60F, 0xC161, 0x4AD6,
+	0x54CD, 0xDF7A, 0xC814, 0x43A3, 0xE6C8, 0x6D7F, 0x7A11, 0xF1A6,
+	0xBB70, 0x30C7, 0x27A9, 0xAC1E, 0x0975, 0x82C2, 0x95AC, 0x1E1B,
+	0xA99A, 0x222D, 0x3543, 0xBEF4, 0x1B9F, 0x9028, 0x8746, 0x0CF1,
+	0x4627, 0xCD90, 0xDAFE, 0x5149, 0xF422, 0x7F95, 0x68FB, 0xE34C,
+	0xFD57, 0x76E0, 0x618E, 0xEA39, 0x4F52, 0xC4E5, 0xD38B, 0x583C,
+	0x12EA, 0x995D, 0x8E33, 0x0584, 0xA0EF, 0x2B58, 0x3C36, 0xB781,
+	0xD883, 0x5334, 0x445A, 0xCFED, 0x6A86, 0xE131, 0xF65F, 0x7DE8,
+	0x373E, 0xBC89, 0xABE7, 0x2050, 0x853B, 0x0E8C, 0x19E2, 0x9255,
+	0x8C4E, 0x07F9, 0x1097, 0x9B20, 0x3E4B, 0xB5FC, 0xA292, 0x2925,
+	0x63F3, 0xE844, 0xFF2A, 0x749D, 0xD1F6, 0x5A41, 0x4D2F, 0xC698,
+	0x7119, 0xFAAE, 0xEDC0, 0x6677, 0xC31C, 0x48AB, 0x5FC5, 0xD472,
+	0x9EA4, 0x1513, 0x027D, 0x89CA, 0x2CA1, 0xA716, 0xB078, 0x3BCF,
+	0x25D4, 0xAE63, 0xB90D, 0x32BA, 0x97D1, 0x1C66, 0x0B08, 0x80BF,
+	0xCA69, 0x41DE, 0x56B0, 0xDD07, 0x786C, 0xF3DB, 0xE4B5, 0x6F02,
+	0x3AB1, 0xB106, 0xA668, 0x2DDF, 0x88B4, 0x0303, 0x146D, 0x9FDA,
+	0xD50C, 0x5EBB, 0x49D5, 0xC262, 0x6709, 0xECBE, 0xFBD0, 0x7067,
+	0x6E7C, 0xE5CB, 0xF2A5, 0x7912, 0xDC79, 0x57CE, 0x40A0, 0xCB17,
+	0x81C1, 0x0A76, 0x1D18, 0x96AF, 0x33C4, 0xB873, 0xAF1D, 0x24AA,
+	0x932B, 0x189C, 0x0FF2, 0x8445, 0x212E, 0xAA99, 0xBDF7, 0x3640,
+	0x7C96, 0xF721, 0xE04F, 0x6BF8, 0xCE93, 0x4524, 0x524A, 0xD9FD,
+	0xC7E6, 0x4C51, 0x5B3F, 0xD088, 0x75E3, 0xFE54, 0xE93A, 0x628D,
+	0x285B, 0xA3EC, 0xB482, 0x3F35, 0x9A5E, 0x11E9, 0x0687, 0x8D30,
+	0xE232, 0x6985, 0x7EEB, 0xF55C, 0x5037, 0xDB80, 0xCCEE, 0x4759,
+	0x0D8F, 0x8638, 0x9156, 0x1AE1, 0xBF8A, 0x343D, 0x2353, 0xA8E4,
+	0xB6FF, 0x3D48, 0x2A26, 0xA191, 0x04FA, 0x8F4D, 0x9823, 0x1394,
+	0x5942, 0xD2F5, 0xC59B, 0x4E2C, 0xEB47, 0x60F0, 0x779E, 0xFC29,
+	0x4BA8, 0xC01F, 0xD771, 0x5CC6, 0xF9AD, 0x721A, 0x6574, 0xEEC3,
+	0xA415, 0x2FA2, 0x38CC, 0xB37B, 0x1610, 0x9DA7, 0x8AC9, 0x017E,
+	0x1F65, 0x94D2, 0x83BC, 0x080B, 0xAD60, 0x26D7, 0x31B9, 0xBA0E,
+	0xF0D8, 0x7B6F, 0x6C01, 0xE7B6, 0x42DD, 0xC96A, 0xDE04, 0x55B3
+};
+
+__u16 crc_t10dif(const unsigned char *buffer, size_t len)
+{
+	__u16 crc = 0;
+	unsigned int i;
+
+	for (i = 0 ; i < len ; i++)
+		crc = (crc << 8) ^ t10_dif_crc_table[((crc >> 8) ^ buffer[i]) & 0xff];
+
+	return crc;
+}
+EXPORT_SYMBOL(crc_t10dif);
+
+MODULE_DESCRIPTION("T10 DIF CRC calculation");
+MODULE_LICENSE("GPL");