diff mbox

[v2,3/3] tcm ibmvscsis driver

Message ID 20110214124821T.fujita.tomonori@lab.ntt.co.jp (mailing list archive)
State Superseded
Headers show

Commit Message

FUJITA Tomonori Feb. 14, 2011, 3:48 a.m. UTC
This is the second version of tcm ibmvscsis driver. You can find the
first version at:

http://marc.info/?t=129734085600004&r=1&w=2

The changes are:

- send VIOSRP_MAD_NOT_SUPPORTED for unknown mad type requests.
- fix inquiry typo
- sends task management response (for now, 'NOT SUPPORTED').
- remove dead code.

=
From: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
Subject: [PATCH v2 3/3] tcm ibmvscsis driver

This replaces ibmvstgt driver that uses the old target framework.

Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
---
 drivers/scsi/ibmvscsi/Makefile    |    4 +-
 drivers/scsi/ibmvscsi/ibmvscsis.c | 1760 +++++++++++++++++++++++++++++++++++++
 2 files changed, 1763 insertions(+), 1 deletions(-)
 create mode 100644 drivers/scsi/ibmvscsi/ibmvscsis.c

Comments

Nicholas A. Bellinger Feb. 14, 2011, 9:12 a.m. UTC | #1
On Mon, 2011-02-14 at 12:48 +0900, FUJITA Tomonori wrote:
> This is the second version of tcm ibmvscsis driver. You can find the
> first version at:
> 
> http://marc.info/?t=129734085600004&r=1&w=2
> 
> The changes are:
> 
> - send VIOSRP_MAD_NOT_SUPPORTED for unknown mad type requests.
> - fix inquiry typo
> - sends task management response (for now, 'NOT SUPPORTED').
> - remove dead code.
> 

Your rev2 patch has been merged into lio-core-2.6.git/tcm_ibmvscsis
currently @ .38-rc4.

Thanks!

--nab
diff mbox

Patch

diff --git a/drivers/scsi/ibmvscsi/Makefile b/drivers/scsi/ibmvscsi/Makefile
index a423d96..a615ea5 100644
--- a/drivers/scsi/ibmvscsi/Makefile
+++ b/drivers/scsi/ibmvscsi/Makefile
@@ -1,8 +1,10 @@ 
+EXTRA_CFLAGS += -I$(srctree)/drivers/target/
+
 obj-$(CONFIG_SCSI_IBMVSCSI)	+= ibmvscsic.o
 
 ibmvscsic-y			+= ibmvscsi.o
 ibmvscsic-$(CONFIG_PPC_ISERIES)	+= iseries_vscsi.o 
 ibmvscsic-$(CONFIG_PPC_PSERIES)	+= rpa_vscsi.o 
 
-obj-$(CONFIG_SCSI_IBMVSCSIS)	+= ibmvstgt.o
+obj-$(CONFIG_SCSI_IBMVSCSIS)	+= ibmvscsis.o
 obj-$(CONFIG_SCSI_IBMVFC)	+= ibmvfc.o
diff --git a/drivers/scsi/ibmvscsi/ibmvscsis.c b/drivers/scsi/ibmvscsi/ibmvscsis.c
new file mode 100644
index 0000000..591cedb
--- /dev/null
+++ b/drivers/scsi/ibmvscsi/ibmvscsis.c
@@ -0,0 +1,1760 @@ 
+/*
+ * IBM eServer i/pSeries Virtual SCSI Target Driver
+ * Copyright (C) 2003-2005 Dave Boutcher (boutcher@us.ibm.com) IBM Corp.
+ *			   Santiago Leon (santil@us.ibm.com) IBM Corp.
+ *			   Linda Xie (lxie@us.ibm.com) IBM Corp.
+ *
+ * Copyright (C) 2005-2011 FUJITA Tomonori <tomof@acm.org>
+ * Copyright (C) 2010 Nicholas A. Bellinger <nab@kernel.org>
+ *
+ * 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/slab.h>
+#include <linux/kthread.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/utsname.h>
+#include <asm/unaligned.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/libsrp.h>
+#include <generated/utsrelease.h>
+
+#include <target/target_core_base.h>
+#include <target/target_core_transport.h>
+#include <target/target_core_fabric_ops.h>
+#include <target/target_core_fabric_lib.h>
+#include <target/target_core_fabric_configfs.h>
+#include <target/target_core_device.h>
+#include <target/target_core_tpg.h>
+#include <target/target_core_configfs.h>
+
+#include <asm/hvcall.h>
+#include <asm/iommu.h>
+#include <asm/prom.h>
+#include <asm/vio.h>
+
+#include "ibmvscsi.h"
+#include "viosrp.h"
+
+#define IBMVSCSIS_VERSION  "v0.1"
+#define IBMVSCSIS_NAMELEN 32
+
+#define	INITIAL_SRP_LIMIT	16
+#define	DEFAULT_MAX_SECTORS	256
+
+/*
+ * Hypervisor calls.
+ */
+#define h_copy_rdma(l, sa, sb, da, db) \
+			plpar_hcall_norets(H_COPY_RDMA, l, sa, sb, da, db)
+#define h_send_crq(ua, l, h) \
+			plpar_hcall_norets(H_SEND_CRQ, ua, l, h)
+#define h_reg_crq(ua, tok, sz)\
+			plpar_hcall_norets(H_REG_CRQ, ua, tok, sz);
+#define h_free_crq(ua) \
+			plpar_hcall_norets(H_FREE_CRQ, ua);
+
+#define GETTARGET(x) ((int)((((u64)(x)) >> 56) & 0x003f))
+#define GETBUS(x) ((int)((((u64)(x)) >> 53) & 0x0007))
+#define GETLUN(x) ((int)((((u64)(x)) >> 48) & 0x001f))
+
+/*
+ * These are fixed for the system and come from the Open Firmware device tree.
+ * We just store them here to save getting them every time.
+ */
+static char system_id[64] = "";
+static char partition_name[97] = "UNKNOWN";
+static unsigned int partition_number = -1;
+
+static LIST_HEAD(tpg_list);
+static DEFINE_SPINLOCK(tpg_lock);
+
+struct ibmvscsis_adapter {
+	struct vio_dev *dma_dev;
+	struct list_head siblings;
+
+	struct crq_queue crq_queue;
+
+	struct work_struct crq_work;
+
+	unsigned long liobn;
+	unsigned long riobn;
+
+	/* todo: remove */
+	struct srp_target srpt;
+
+	/* SRP port target portal group tag for TCM */
+	unsigned long tport_tpgt;
+
+	/* Returned by ibmvscsis_make_tpg() */
+	struct se_portal_group se_tpg;
+
+	struct se_session *se_sess;
+
+
+	/* SCSI protocol the tport is providing */
+	u8 tport_proto_id;
+	/* Binary World Wide unique Port Name for SRP Target port */
+	u64 tport_wwpn;
+	/* ASCII formatted WWPN for SRP Target port */
+	char tport_name[IBMVSCSIS_NAMELEN];
+	/* Returned by ibmvscsis_make_tport() */
+	struct se_wwn tport_wwn;
+};
+
+struct ibmvscsis_cmnd {
+	/* Used for libsrp processing callbacks */
+	struct scsi_cmnd sc;
+	/* Used for TCM Core operations */
+	struct se_cmd se_cmd;
+	/* Sense buffer that will be mapped into outgoing status */
+	unsigned char sense_buf[TRANSPORT_SENSE_BUFFER];
+};
+
+static int ibmvscsis_check_true(struct se_portal_group *se_tpg)
+{
+	return 1;
+}
+
+static int ibmvscsis_check_false(struct se_portal_group *se_tpg)
+{
+	return 0;
+}
+
+static char *ibmvscsis_get_fabric_name(void)
+{
+	return "ibmvscsis";
+}
+
+static u8 ibmvscsis_get_fabric_proto_ident(struct se_portal_group *se_tpg)
+{
+	return 4;
+}
+
+static char *ibmvscsis_get_fabric_wwn(struct se_portal_group *se_tpg)
+{
+	struct ibmvscsis_adapter *adapter =
+		container_of(se_tpg, struct ibmvscsis_adapter, se_tpg);
+
+	return adapter->tport_name;
+}
+
+static u16 ibmvscsis_get_tag(struct se_portal_group *se_tpg)
+{
+	struct ibmvscsis_adapter *adapter =
+		container_of(se_tpg, struct ibmvscsis_adapter, se_tpg);
+	return adapter->tport_tpgt;
+}
+
+static u32 ibmvscsis_get_default_depth(struct se_portal_group *se_tpg)
+{
+	return 1;
+}
+
+/* we don't care about the transport id since we never use pr. */
+static u32 ibmvscsis_get_pr_transport_id(struct se_portal_group *se_tpg,
+					 struct se_node_acl *se_nacl,
+					 struct t10_pr_registration *pr_reg,
+					 int *format_code,
+					 unsigned char *buf)
+{
+	return 24;
+}
+
+static u32 ibmvscsis_get_pr_transport_id_len(struct se_portal_group *se_tpg,
+					     struct se_node_acl *se_nacl,
+					     struct t10_pr_registration *pr_reg,
+					     int *format_code)
+{
+	return 24;
+}
+
+static char *ibmvscsis_parse_pr_out_transport_id(struct se_portal_group *se_tpg,
+						 const char *buf,
+						 u32 *out_tid_len,
+						 char **port_nexus_ptr)
+{
+	return NULL;
+}
+
+struct ibmvscsis_nacl {
+	/* Binary World Wide unique Port Name for SRP Initiator port */
+	u64 iport_wwpn;
+	/* ASCII formatted WWPN for Sas Initiator port */
+	char iport_name[IBMVSCSIS_NAMELEN];
+	/* Returned by ibmvscsis_make_nodeacl() */
+	struct se_node_acl se_node_acl;
+};
+
+static struct se_node_acl *ibmvscsis_alloc_fabric_acl(struct se_portal_group *se_tpg)
+{
+	struct ibmvscsis_nacl *nacl;
+
+	nacl = kzalloc(sizeof(struct ibmvscsis_nacl), GFP_KERNEL);
+	if (!(nacl)) {
+		printk(KERN_ERR "Unable to alocate struct ibmvscsis_nacl\n");
+		return NULL;
+	}
+
+	return &nacl->se_node_acl;
+}
+
+static void ibmvscsis_release_fabric_acl(struct se_portal_group *se_tpg,
+					 struct se_node_acl *se_nacl)
+{
+	struct ibmvscsis_nacl *nacl = container_of(se_nacl,
+			struct ibmvscsis_nacl, se_node_acl);
+	kfree(nacl);
+}
+
+static u32 ibmvscsis_tpg_get_inst_index(struct se_portal_group *se_tpg)
+{
+	return 1;
+}
+
+static void ibmvscsis_release_cmd(struct se_cmd *se_cmd)
+{
+	struct ibmvscsis_cmnd *cmd =
+		container_of(se_cmd, struct ibmvscsis_cmnd, se_cmd);
+	kfree(cmd);
+	return;
+}
+
+static int ibmvscsis_shutdown_session(struct se_session *se_sess)
+{
+	return 0;
+}
+
+static void ibmvscsis_close_session(struct se_session *se_sess)
+{
+	return;
+}
+
+static void ibmvscsis_stop_session(struct se_session *se_sess,
+				   int sess_sleep , int conn_sleep)
+{
+	return;
+}
+
+static void ibmvscsis_reset_nexus(struct se_session *se_sess)
+{
+	return;
+}
+
+static int ibmvscsis_sess_logged_in(struct se_session *se_sess)
+{
+	return 0;
+}
+
+static u32 ibmvscsis_sess_get_index(struct se_session *se_sess)
+{
+	return 0;
+}
+
+static int ibmvscsis_write_pending_status(struct se_cmd *se_cmd)
+{
+	return 0;
+}
+
+static void ibmvscsis_set_default_node_attrs(struct se_node_acl *nacl)
+{
+	return;
+}
+
+static u32 ibmvscsis_get_task_tag(struct se_cmd *se_cmd)
+{
+	return 0;
+}
+
+static int ibmvscsis_get_cmd_state(struct se_cmd *se_cmd)
+{
+	return 0;
+}
+
+static void ibmvscsis_new_cmd_failure(struct se_cmd *se_cmd)
+{
+	return;
+}
+
+static int ibmvscsis_queue_tm_rsp(struct se_cmd *se_cmd)
+{
+	return 0;
+}
+
+static u16 ibmvscsis_set_fabric_sense_len(struct se_cmd *se_cmd,
+					  u32 sense_length)
+{
+	return 0;
+}
+
+static u16 ibmvscsis_get_fabric_sense_len(void)
+{
+	return 0;
+}
+
+static int ibmvscsis_is_state_remove(struct se_cmd *se_cmd)
+{
+	return 0;
+}
+
+static u64 make_lun(unsigned int bus, unsigned int target, unsigned int lun);
+
+static u64 ibmvscsis_pack_lun(unsigned int lun)
+{
+	return make_lun(0, lun & 0x003f, 0);
+}
+
+/* Local pointer to allocated TCM configfs fabric module */
+static struct target_fabric_configfs *ibmvscsis_fabric_configfs;
+
+static struct se_portal_group *ibmvscsis_make_tpg(struct se_wwn *wwn,
+						  struct config_group *group,
+						  const char *name)
+{
+	struct ibmvscsis_adapter *adapter =
+		container_of(wwn, struct ibmvscsis_adapter, tport_wwn);
+	struct se_node_acl *acl;
+	int ret;
+	char *dname = (char *)dev_name(&adapter->dma_dev->dev);
+
+	if (strncmp(name, "tpgt_1", 6))
+		return ERR_PTR(-EINVAL);
+
+	ret = core_tpg_register(&ibmvscsis_fabric_configfs->tf_ops, wwn,
+				&adapter->se_tpg, (void *)adapter,
+				TRANSPORT_TPG_TYPE_NORMAL);
+	if (ret)
+		return ERR_PTR(-ENOMEM);
+
+	adapter->se_sess = transport_init_session();
+	if (!adapter->se_sess) {
+		core_tpg_deregister(&adapter->se_tpg);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	acl = core_tpg_check_initiator_node_acl(&adapter->se_tpg, dname);
+	if (!acl) {
+		transport_free_session(adapter->se_sess);
+		adapter->se_sess = NULL;
+		return ERR_PTR(-ENOMEM);
+	}
+	adapter->se_sess->se_node_acl = acl;
+
+	transport_register_session(&adapter->se_tpg,
+				   adapter->se_sess->se_node_acl,
+				   adapter->se_sess, adapter);
+
+	return &adapter->se_tpg;
+}
+
+static void ibmvscsis_drop_tpg(struct se_portal_group *se_tpg)
+{
+	struct ibmvscsis_adapter *adapter =
+		container_of(se_tpg, struct ibmvscsis_adapter, se_tpg);
+	unsigned long flags;
+
+
+	transport_deregister_session_configfs(adapter->se_sess);
+	transport_free_session(adapter->se_sess);
+	core_tpg_deregister(se_tpg);
+
+	spin_lock_irqsave(&tpg_lock, flags);
+	adapter->se_sess = NULL;
+	spin_unlock_irqrestore(&tpg_lock, flags);
+}
+
+static struct se_wwn *ibmvscsis_make_tport(struct target_fabric_configfs *tf,
+					   struct config_group *group,
+					   const char *name)
+{
+	struct ibmvscsis_adapter *adapter;
+	unsigned long tpgt, flags;
+
+	if (strict_strtoul(name, 10, &tpgt))
+		return NULL;
+
+	spin_lock_irqsave(&tpg_lock, flags);
+	list_for_each_entry(adapter, &tpg_list, siblings) {
+		if (tpgt == adapter->tport_tpgt)
+			goto found;
+	}
+
+	spin_unlock_irqrestore(&tpg_lock, flags);
+	return NULL;
+found:
+	spin_unlock_irqrestore(&tpg_lock, flags);
+
+	return &adapter->tport_wwn;
+}
+
+static void ibmvscsis_drop_tport(struct se_wwn *wwn)
+{
+}
+
+static ssize_t ibmvscsis_wwn_show_attr_version(struct target_fabric_configfs *tf,
+					       char *page)
+{
+	return sprintf(page, "IBMVSCSIS fabric module %s on %s/%s"
+		"on "UTS_RELEASE"\n", IBMVSCSIS_VERSION, utsname()->sysname,
+		utsname()->machine);
+}
+
+TF_WWN_ATTR_RO(ibmvscsis, version);
+
+static struct configfs_attribute *ibmvscsis_wwn_attrs[] = {
+	&ibmvscsis_wwn_version.attr,
+	NULL,
+};
+
+static int ibmvscsis_write_pending(struct se_cmd *se_cmd);
+static int ibmvscsis_queue_data_in(struct se_cmd *se_cmd);
+static int ibmvscsis_queue_status(struct se_cmd *se_cmd);
+static int ibmvscsis_new_cmd_map(struct se_cmd *se_cmd);
+static void ibmvscsis_check_stop_free(struct se_cmd *se_cmd);
+
+static struct target_core_fabric_ops ibmvscsis_ops = {
+	.task_sg_chaining		= 1,
+	.get_fabric_name		= ibmvscsis_get_fabric_name,
+	.get_fabric_proto_ident		= ibmvscsis_get_fabric_proto_ident,
+	.tpg_get_wwn			= ibmvscsis_get_fabric_wwn,
+	.tpg_get_tag			= ibmvscsis_get_tag,
+	.tpg_get_default_depth		= ibmvscsis_get_default_depth,
+	.tpg_get_pr_transport_id	= ibmvscsis_get_pr_transport_id,
+	.tpg_get_pr_transport_id_len	= ibmvscsis_get_pr_transport_id_len,
+	.tpg_parse_pr_out_transport_id	= ibmvscsis_parse_pr_out_transport_id,
+	.tpg_check_demo_mode		= ibmvscsis_check_true,
+	.tpg_check_demo_mode_cache	= ibmvscsis_check_true,
+	.tpg_check_demo_mode_write_protect = ibmvscsis_check_false,
+	.tpg_check_prod_mode_write_protect = ibmvscsis_check_false,
+	.tpg_alloc_fabric_acl		= ibmvscsis_alloc_fabric_acl,
+	.tpg_release_fabric_acl		= ibmvscsis_release_fabric_acl,
+	.tpg_get_inst_index		= ibmvscsis_tpg_get_inst_index,
+	.new_cmd_map			= ibmvscsis_new_cmd_map,
+	.check_stop_free		= ibmvscsis_check_stop_free,
+	.release_cmd_to_pool		= ibmvscsis_release_cmd,
+	.release_cmd_direct		= ibmvscsis_release_cmd,
+	.shutdown_session		= ibmvscsis_shutdown_session,
+	.close_session			= ibmvscsis_close_session,
+	.stop_session			= ibmvscsis_stop_session,
+	.fall_back_to_erl0		= ibmvscsis_reset_nexus,
+	.sess_logged_in			= ibmvscsis_sess_logged_in,
+	.sess_get_index			= ibmvscsis_sess_get_index,
+	.sess_get_initiator_sid		= NULL,
+	.write_pending			= ibmvscsis_write_pending,
+	.write_pending_status		= ibmvscsis_write_pending_status,
+	.set_default_node_attributes	= ibmvscsis_set_default_node_attrs,
+	.get_task_tag			= ibmvscsis_get_task_tag,
+	.get_cmd_state			= ibmvscsis_get_cmd_state,
+	.new_cmd_failure		= ibmvscsis_new_cmd_failure,
+	.queue_data_in			= ibmvscsis_queue_data_in,
+	.queue_status			= ibmvscsis_queue_status,
+	.queue_tm_rsp			= ibmvscsis_queue_tm_rsp,
+	.get_fabric_sense_len		= ibmvscsis_get_fabric_sense_len,
+	.set_fabric_sense_len		= ibmvscsis_set_fabric_sense_len,
+	.is_state_remove		= ibmvscsis_is_state_remove,
+	.pack_lun			= ibmvscsis_pack_lun,
+	.fabric_make_wwn		= ibmvscsis_make_tport,
+	.fabric_drop_wwn		= ibmvscsis_drop_tport,
+	.fabric_make_tpg		= ibmvscsis_make_tpg,
+	.fabric_drop_tpg		= ibmvscsis_drop_tpg,
+	.fabric_post_link		= NULL,
+	.fabric_pre_unlink		= NULL,
+	.fabric_make_np			= NULL,
+	.fabric_drop_np			= NULL,
+	.fabric_make_nodeacl		= NULL,
+	.fabric_drop_nodeacl		= NULL,
+};
+
+static inline union viosrp_iu *vio_iu(struct iu_entry *iue)
+{
+	return (union viosrp_iu *)(iue->sbuf->buf);
+}
+
+static int send_iu(struct iu_entry *iue, u64 length, u8 format)
+{
+	struct srp_target *target = iue->target;
+	struct ibmvscsis_adapter *adapter = target->ldata;
+	long rc, rc1;
+	union {
+		struct viosrp_crq cooked;
+		u64 raw[2];
+	} crq;
+
+	/* First copy the SRP */
+	rc = h_copy_rdma(length, adapter->liobn, iue->sbuf->dma,
+			 adapter->riobn, iue->remote_token);
+
+	if (rc)
+		printk(KERN_ERR "Error %ld transferring data\n", rc);
+
+	crq.cooked.valid = 0x80;
+	crq.cooked.format = format;
+	crq.cooked.reserved = 0x00;
+	crq.cooked.timeout = 0x00;
+	crq.cooked.IU_length = length;
+	crq.cooked.IU_data_ptr = vio_iu(iue)->srp.rsp.tag;
+
+	if (rc == 0)
+		crq.cooked.status = 0x99;	/* Just needs to be non-zero */
+	else
+		crq.cooked.status = 0x00;
+
+	rc1 = h_send_crq(adapter->dma_dev->unit_address, crq.raw[0],
+			 crq.raw[1]);
+	if (rc1) {
+		printk(KERN_ERR "%ld sending response\n", rc1);
+		return rc1;
+	}
+
+	return rc;
+}
+
+#define SRP_RSP_SENSE_DATA_LEN	18
+
+static int send_rsp(struct iu_entry *iue, struct scsi_cmnd *sc,
+		    unsigned char status, unsigned char asc)
+{
+	union viosrp_iu *iu = vio_iu(iue);
+	uint64_t tag = iu->srp.rsp.tag;
+
+	/* If the linked bit is on and status is good */
+	if (test_bit(V_LINKED, &iue->flags) && (status == NO_SENSE))
+		status = 0x10;
+
+	memset(iu, 0, sizeof(struct srp_rsp));
+	iu->srp.rsp.opcode = SRP_RSP;
+	iu->srp.rsp.req_lim_delta = 1;
+	iu->srp.rsp.tag = tag;
+
+	if (test_bit(V_DIOVER, &iue->flags))
+		iu->srp.rsp.flags |= SRP_RSP_FLAG_DIOVER;
+
+	iu->srp.rsp.data_in_res_cnt = 0;
+	iu->srp.rsp.data_out_res_cnt = 0;
+
+	iu->srp.rsp.flags &= ~SRP_RSP_FLAG_RSPVALID;
+
+	iu->srp.rsp.resp_data_len = 0;
+	iu->srp.rsp.status = status;
+	if (status) {
+		uint8_t *sense = iu->srp.rsp.data;
+
+		if (sc) {
+			iu->srp.rsp.flags |= SRP_RSP_FLAG_SNSVALID;
+			iu->srp.rsp.sense_data_len = SCSI_SENSE_BUFFERSIZE;
+			memcpy(sense, sc->sense_buffer, SCSI_SENSE_BUFFERSIZE);
+		} else {
+			iu->srp.rsp.status = SAM_STAT_CHECK_CONDITION;
+			iu->srp.rsp.flags |= SRP_RSP_FLAG_SNSVALID;
+			iu->srp.rsp.sense_data_len = SRP_RSP_SENSE_DATA_LEN;
+
+			/* Valid bit and 'current errors' */
+			sense[0] = (0x1 << 7 | 0x70);
+			/* Sense key */
+			sense[2] = status;
+			/* Additional sense length */
+			sense[7] = 0xa;	/* 10 bytes */
+			/* Additional sense code */
+			sense[12] = asc;
+		}
+	}
+
+	send_iu(iue, sizeof(iu->srp.rsp) + SRP_RSP_SENSE_DATA_LEN,
+		VIOSRP_SRP_FORMAT);
+
+	return 0;
+}
+
+static int send_adapter_info(struct iu_entry *iue,
+			     dma_addr_t remote_buffer, u16 length)
+{
+	struct srp_target *target = iue->target;
+	struct ibmvscsis_adapter *adapter = target->ldata;
+	dma_addr_t data_token;
+	struct mad_adapter_info_data *info;
+	int err;
+
+	info = dma_alloc_coherent(&adapter->dma_dev->dev, sizeof(*info),
+				  &data_token, GFP_KERNEL);
+	if (!info) {
+		printk(KERN_ERR "bad dma_alloc_coherent %p\n", target);
+		return 1;
+	}
+
+	/* Get remote info */
+	err = h_copy_rdma(sizeof(*info), adapter->riobn, remote_buffer,
+			  adapter->liobn, data_token);
+	if (err == H_SUCCESS) {
+		printk(KERN_INFO "Client connect: %s (%d)\n",
+		       info->partition_name, info->partition_number);
+	}
+
+	memset(info, 0, sizeof(*info));
+
+	strcpy(info->srp_version, "16.a");
+	strncpy(info->partition_name, partition_name,
+		sizeof(info->partition_name));
+	info->partition_number = partition_number;
+	info->mad_version = 1;
+	info->os_type = 2;
+	info->port_max_txu[0] = DEFAULT_MAX_SECTORS << 9;
+
+	/* Send our info to remote */
+	err = h_copy_rdma(sizeof(*info), adapter->liobn, data_token,
+			  adapter->riobn, remote_buffer);
+
+	dma_free_coherent(&adapter->dma_dev->dev, sizeof(*info), info,
+			  data_token);
+	if (err != H_SUCCESS) {
+		printk(KERN_INFO "Error sending adapter info %d\n", err);
+		return 1;
+	}
+
+	return 0;
+}
+
+static int process_mad_iu(struct iu_entry *iue)
+{
+	union viosrp_iu *iu = vio_iu(iue);
+	struct viosrp_adapter_info *info;
+	struct viosrp_host_config *conf;
+
+	switch (iu->mad.empty_iu.common.type) {
+	case VIOSRP_EMPTY_IU_TYPE:
+		printk(KERN_ERR "%s\n", "Unsupported EMPTY MAD IU");
+		break;
+	case VIOSRP_ERROR_LOG_TYPE:
+		printk(KERN_ERR "%s\n", "Unsupported ERROR LOG MAD IU");
+		iu->mad.error_log.common.status = 1;
+		send_iu(iue, sizeof(iu->mad.error_log),	VIOSRP_MAD_FORMAT);
+		break;
+	case VIOSRP_ADAPTER_INFO_TYPE:
+		info = &iu->mad.adapter_info;
+		info->common.status = send_adapter_info(iue, info->buffer,
+							info->common.length);
+		send_iu(iue, sizeof(*info), VIOSRP_MAD_FORMAT);
+		break;
+	case VIOSRP_HOST_CONFIG_TYPE:
+		conf = &iu->mad.host_config;
+		conf->common.status = 1;
+		send_iu(iue, sizeof(*conf), VIOSRP_MAD_FORMAT);
+		break;
+	default:
+		printk(KERN_ERR "Unknown type %u\n", iu->srp.rsp.opcode);
+		iu->mad.empty_iu.common.status = VIOSRP_MAD_NOT_SUPPORTED;
+		send_iu(iue, sizeof(iu->mad), VIOSRP_MAD_FORMAT);
+		break;
+	}
+
+	return 1;
+}
+
+static void process_login(struct iu_entry *iue)
+{
+	union viosrp_iu *iu = vio_iu(iue);
+	struct srp_login_rsp *rsp = &iu->srp.login_rsp;
+	u64 tag = iu->srp.rsp.tag;
+
+	/*
+	 * TODO handle case that requested size is wrong and buffer
+	 * format is wrong
+	 */
+	memset(iu, 0, sizeof(struct srp_login_rsp));
+	rsp->opcode = SRP_LOGIN_RSP;
+	rsp->req_lim_delta = INITIAL_SRP_LIMIT;
+	rsp->tag = tag;
+	rsp->max_it_iu_len = sizeof(union srp_iu);
+	rsp->max_ti_iu_len = sizeof(union srp_iu);
+	/* direct and indirect */
+	rsp->buf_fmt = SRP_BUF_FORMAT_DIRECT | SRP_BUF_FORMAT_INDIRECT;
+
+	send_iu(iue, sizeof(*rsp), VIOSRP_SRP_FORMAT);
+}
+
+static void process_tsk_mgmt(struct iu_entry *iue)
+{
+	union viosrp_iu *iu = vio_iu(iue);
+	uint64_t tag = iu->srp.rsp.tag;
+	uint8_t *resp_data = iu->srp.rsp.data;
+
+	memset(iu, 0, sizeof(struct srp_rsp));
+	iu->srp.rsp.opcode = SRP_RSP;
+	iu->srp.rsp.req_lim_delta = 1;
+	iu->srp.rsp.tag = tag;
+
+	iu->srp.rsp.data_in_res_cnt = 0;
+	iu->srp.rsp.data_out_res_cnt = 0;
+
+	iu->srp.rsp.flags &= ~SRP_RSP_FLAG_RSPVALID;
+
+	iu->srp.rsp.resp_data_len = 4;
+	/* TASK MANAGEMENT FUNCTION NOT SUPPORTED for now */
+	resp_data[3] = 4;
+
+	send_iu(iue, sizeof(iu->srp.rsp) + iu->srp.rsp.resp_data_len,
+		VIOSRP_SRP_FORMAT);
+}
+
+static int process_srp_iu(struct iu_entry *iue)
+{
+	union viosrp_iu *iu = vio_iu(iue);
+	struct srp_target *target = iue->target;
+	int done = 1;
+	u8 opcode = iu->srp.rsp.opcode;
+	unsigned long flags;
+
+	switch (opcode) {
+	case SRP_LOGIN_REQ:
+		process_login(iue);
+		break;
+	case SRP_TSK_MGMT:
+		process_tsk_mgmt(iue);
+		break;
+	case SRP_CMD:
+		spin_lock_irqsave(&target->lock, flags);
+		list_add_tail(&iue->ilist, &target->cmd_queue);
+		spin_unlock_irqrestore(&target->lock, flags);
+		done = 0;
+		break;
+	case SRP_LOGIN_RSP:
+	case SRP_I_LOGOUT:
+	case SRP_T_LOGOUT:
+	case SRP_RSP:
+	case SRP_CRED_REQ:
+	case SRP_CRED_RSP:
+	case SRP_AER_REQ:
+	case SRP_AER_RSP:
+		printk(KERN_ERR "Unsupported type %u\n", opcode);
+		break;
+	default:
+		printk(KERN_ERR "Unknown type %u\n", opcode);
+	}
+
+	return done;
+}
+
+static void process_iu(struct viosrp_crq *crq,
+		       struct ibmvscsis_adapter *adapter)
+{
+	struct iu_entry *iue;
+	long err;
+	int done = 1;
+
+	iue = srp_iu_get(&adapter->srpt);
+	if (!iue) {
+		printk(KERN_ERR "Error getting IU from pool\n");
+		return;
+	}
+
+	iue->remote_token = crq->IU_data_ptr;
+
+	err = h_copy_rdma(crq->IU_length, adapter->riobn,
+			  iue->remote_token, adapter->liobn, iue->sbuf->dma);
+
+	if (err != H_SUCCESS) {
+		printk(KERN_ERR "%ld transferring data error %p\n", err, iue);
+		goto out;
+	}
+
+	if (crq->format == VIOSRP_MAD_FORMAT)
+		done = process_mad_iu(iue);
+	else
+		done = process_srp_iu(iue);
+out:
+	if (done)
+		srp_iu_put(iue);
+}
+
+static void process_crq(struct viosrp_crq *crq,
+			struct ibmvscsis_adapter *adapter)
+{
+	switch (crq->valid) {
+	case 0xC0:
+		/* initialization */
+		switch (crq->format) {
+		case 0x01:
+			h_send_crq(adapter->dma_dev->unit_address,
+				   0xC002000000000000, 0);
+			break;
+		case 0x02:
+			break;
+		default:
+			printk(KERN_ERR "Unknown format %u\n", crq->format);
+		}
+		break;
+	case 0xFF:
+		/* transport event */
+		break;
+	case 0x80:
+		/* real payload */
+		switch (crq->format) {
+		case VIOSRP_SRP_FORMAT:
+		case VIOSRP_MAD_FORMAT:
+			process_iu(crq, adapter);
+			break;
+		case VIOSRP_OS400_FORMAT:
+		case VIOSRP_AIX_FORMAT:
+		case VIOSRP_LINUX_FORMAT:
+		case VIOSRP_INLINE_FORMAT:
+			printk(KERN_ERR "Unsupported format %u\n", crq->format);
+			break;
+		default:
+			printk(KERN_ERR "Unknown format %u\n", crq->format);
+		}
+		break;
+	default:
+		printk(KERN_ERR "unknown message type 0x%02x!?\n", crq->valid);
+	}
+}
+
+static inline struct viosrp_crq *next_crq(struct crq_queue *queue)
+{
+	struct viosrp_crq *crq;
+	unsigned long flags;
+
+	spin_lock_irqsave(&queue->lock, flags);
+	crq = &queue->msgs[queue->cur];
+	if (crq->valid & 0x80) {
+		if (++queue->cur == queue->size)
+			queue->cur = 0;
+	} else
+		crq = NULL;
+	spin_unlock_irqrestore(&queue->lock, flags);
+
+	return crq;
+}
+
+static int tcm_queuecommand(struct ibmvscsis_adapter *adapter,
+			    struct ibmvscsis_cmnd *vsc,
+			    struct srp_cmd *cmd)
+{
+	struct se_cmd *se_cmd;
+	int attr;
+	int data_len;
+	int ret;
+
+	switch (cmd->task_attr) {
+	case SRP_SIMPLE_TASK:
+		attr = MSG_SIMPLE_TAG;
+		break;
+	case SRP_ORDERED_TASK:
+		attr = MSG_ORDERED_TAG;
+		break;
+	case SRP_HEAD_TASK:
+		attr = MSG_HEAD_TAG;
+		break;
+	default:
+		printk(KERN_WARNING "Task attribute %d not supported\n",
+		       cmd->task_attr);
+		attr = MSG_SIMPLE_TAG;
+	}
+
+	data_len = srp_data_length(cmd, srp_cmd_direction(cmd));
+
+	se_cmd = &vsc->se_cmd;
+
+	transport_init_se_cmd(se_cmd,
+			      adapter->se_tpg.se_tpg_tfo,
+			      adapter->se_sess, data_len,
+			      srp_cmd_direction(cmd),
+			      attr, vsc->sense_buf);
+
+	ret = transport_get_lun_for_cmd(se_cmd, NULL, cmd->lun);
+	if (ret) {
+		printk(KERN_ERR "invalid lun %u\n", GETLUN(cmd->lun));
+		transport_send_check_condition_and_sense(se_cmd,
+							 se_cmd->scsi_sense_reason,
+							 0);
+		return ret;
+	}
+
+	transport_device_setup_cmd(se_cmd);
+	transport_generic_handle_cdb_map(se_cmd);
+
+	return 0;
+}
+
+static int ibmvscsis_new_cmd_map(struct se_cmd *se_cmd)
+{
+	struct ibmvscsis_cmnd *cmd =
+		container_of(se_cmd, struct ibmvscsis_cmnd, se_cmd);
+	struct scsi_cmnd *sc = &cmd->sc;
+	struct iu_entry *iue = (struct iu_entry *)sc->SCp.ptr;
+	struct srp_cmd *scmd = iue->sbuf->buf;
+	int ret;
+
+	/*
+	 * Allocate the necessary tasks to complete the received CDB+data
+	 */
+	ret = transport_generic_allocate_tasks(se_cmd, scmd->cdb);
+	if (ret == -1) {
+		/* Out of Resources */
+		return PYX_TRANSPORT_LU_COMM_FAILURE;
+	} else if (ret == -2) {
+		/*
+		 * Handle case for SAM_STAT_RESERVATION_CONFLICT
+		 */
+		if (se_cmd->se_cmd_flags & SCF_SCSI_RESERVATION_CONFLICT)
+			return PYX_TRANSPORT_RESERVATION_CONFLICT;
+		/*
+		 * Otherwise, return SAM_STAT_CHECK_CONDITION and return
+		 * sense data
+		 */
+		return PYX_TRANSPORT_USE_SENSE_REASON;
+	}
+
+	return 0;
+}
+
+static void ibmvscsis_check_stop_free(struct se_cmd *se_cmd)
+{
+	if (se_cmd->se_tmr_req)
+		return;
+	transport_generic_free_cmd(se_cmd, 0, 1, 0);
+}
+
+static u64 scsi_lun_to_int(u64 lun)
+{
+	if (GETBUS(lun) || GETLUN(lun))
+		return ~0UL;
+	else
+		return GETTARGET(lun);
+}
+
+struct inquiry_data {
+	u8 qual_type;
+	u8 rmb_reserve;
+	u8 version;
+	u8 aerc_naca_hisup_format;
+	u8 addl_len;
+	u8 sccs_reserved;
+	u8 bque_encserv_vs_multip_mchngr_reserved;
+	u8 reladr_reserved_linked_cmdqueue_vs;
+	char vendor[8];
+	char product[16];
+	char revision[4];
+	char vendor_specific[20];
+	char reserved1[2];
+	char version_descriptor[16];
+	char reserved2[22];
+	char unique[158];
+};
+
+static u64 make_lun(unsigned int bus, unsigned int target, unsigned int lun)
+{
+	u16 result = (0x8000 |
+			   ((target & 0x003f) << 8) |
+			   ((bus & 0x0007) << 5) |
+			   (lun & 0x001f));
+	return ((u64) result) << 48;
+}
+
+static int ibmvscsis_inquiry(struct ibmvscsis_adapter *adapter,
+			      struct srp_cmd *cmd, char *data)
+{
+	struct se_portal_group *se_tpg = &adapter->se_tpg;
+	struct inquiry_data *id = (struct inquiry_data *)data;
+	u64 unpacked_lun, lun = cmd->lun;
+	u8 *cdb = cmd->cdb;
+	int len;
+
+	if (!data)
+		printk(KERN_INFO "%s %d: oomu\n", __func__, __LINE__);
+
+	if (((cdb[1] & 0x3) == 0x3) || (!(cdb[1] & 0x3) && cdb[2])) {
+		printk(KERN_INFO "%s %d: invalid req\n", __func__, __LINE__);
+		return 0;
+	}
+
+	if (cdb[1] & 0x3)
+		printk(KERN_INFO "%s %d: needs the normal path\n",
+		       __func__, __LINE__);
+	else {
+		id->qual_type = TYPE_DISK;
+		id->rmb_reserve = 0x00;
+		id->version = 0x84; /* ISO/IE */
+		id->aerc_naca_hisup_format = 0x22; /* naca & fmt 0x02 */
+		id->addl_len = sizeof(*id) - 4;
+		id->bque_encserv_vs_multip_mchngr_reserved = 0x00;
+		id->reladr_reserved_linked_cmdqueue_vs = 0x02; /* CMDQ */
+		memcpy(id->vendor, "IBM	    ", 8);
+		/*
+		 * Don't even ask about the next bit.  AIX uses
+		 * hardcoded device naming to recognize device types
+		 * and their client won't  work unless we use VOPTA and
+		 * VDASD.
+		 */
+		if (id->qual_type == TYPE_ROM)
+			memcpy(id->product, "VOPTA blkdev    ", 16);
+		else
+			memcpy(id->product, "VDASD blkdev    ", 16);
+
+		memcpy(id->revision, "0001", 4);
+
+		snprintf(id->unique, sizeof(id->unique),
+			 "IBM-VSCSI-%s-P%d-%x-%d-%d-%d\n",
+			 system_id,
+			 partition_number,
+			 adapter->dma_dev->unit_address,
+			 GETBUS(lun),
+			 GETTARGET(lun),
+			 GETLUN(lun));
+	}
+
+	len = min_t(int, sizeof(*id), cdb[4]);
+
+	unpacked_lun = scsi_lun_to_int(cmd->lun);
+
+	spin_lock(&se_tpg->tpg_lun_lock);
+
+	if (unpacked_lun < TRANSPORT_MAX_LUNS_PER_TPG &&
+	    se_tpg->tpg_lun_list[unpacked_lun].lun_status ==
+	    TRANSPORT_LUN_STATUS_ACTIVE)
+		;
+	else
+		data[0] = TYPE_NO_LUN;
+
+	spin_unlock(&se_tpg->tpg_lun_lock);
+
+	return len;
+}
+
+static int ibmvscsis_mode_sense(struct ibmvscsis_adapter *adapter,
+				struct srp_cmd *cmd, char *mode)
+{
+	int bytes;
+	struct se_portal_group *se_tpg = &adapter->se_tpg;
+	u64 unpacked_lun;
+	struct se_lun *lun;
+	u32 blocks;
+
+	unpacked_lun = scsi_lun_to_int(cmd->lun);
+
+	spin_lock(&se_tpg->tpg_lun_lock);
+
+	lun = &se_tpg->tpg_lun_list[unpacked_lun];
+
+	blocks = TRANSPORT(lun->lun_se_dev)->get_blocks(lun->lun_se_dev);
+
+	spin_unlock(&se_tpg->tpg_lun_lock);
+
+	switch (cmd->cdb[2]) {
+	case 0:
+	case 0x3f:
+		mode[1] = 0x00;	/* Default medium */
+		/* if (iue->req.vd->b.ro) */
+		if (0)
+			mode[2] = 0x80;	/* device specific  */
+		else
+			mode[2] = 0x00;	/* device specific  */
+
+		/* note the DPOFUA bit is set to zero! */
+		mode[3] = 0x08;	/* block descriptor length */
+		*((u32 *) &mode[4]) = blocks - 1;
+		*((u32 *) &mode[8]) = 512;
+		bytes = mode[0] = 12;	/* length */
+		break;
+
+	case 0x08: /* Cache page */
+		mode[1] = 0x00;	/* Default medium */
+		if (0)
+			mode[2] = 0x80;	/* device specific */
+		else
+			mode[2] = 0x00;	/* device specific */
+
+		/* note the DPOFUA bit is set to zero! */
+		mode[3] = 0x08;	/* block descriptor length */
+		*((u32 *) &mode[4]) = blocks - 1;
+		*((u32 *) &mode[8]) = 512;
+
+		/* Cache page */
+		mode[12] = 0x08;    /* page */
+		mode[13] = 0x12;    /* page length */
+		mode[14] = 0x01;    /* no cache (0x04 for read/write cache) */
+
+		bytes = mode[0] = 12 + mode[13];	/* length */
+		break;
+	}
+
+	return bytes;
+}
+
+static int ibmvscsis_report_luns(struct ibmvscsis_adapter *adapter,
+				 struct srp_cmd *cmd, u64 *data)
+{
+	u64 lun;
+	struct se_portal_group *se_tpg = &adapter->se_tpg;
+	int i, idx;
+	int alen, oalen, nr_luns, rbuflen = 4096;
+
+	alen = get_unaligned_be32(&cmd->cdb[6]);
+
+	alen &= ~(8 - 1);
+	oalen = alen;
+
+	if (cmd->lun) {
+		nr_luns = 1;
+		goto done;
+	}
+
+	alen -= 8;
+	rbuflen -= 8; /* FIXME */
+	idx = 2;
+	nr_luns = 1;
+
+	spin_lock(&se_tpg->tpg_lun_lock);
+	for (i = 0; i < 255; i++) {
+		if (se_tpg->tpg_lun_list[i].lun_status !=
+		    TRANSPORT_LUN_STATUS_ACTIVE)
+			continue;
+
+		lun = make_lun(0, i & 0x003f, 0);
+		data[idx++] = cpu_to_be64(lun);
+		alen -= 8;
+		if (!alen)
+			break;
+		rbuflen -= 8;
+		if (!rbuflen)
+			break;
+
+		nr_luns++;
+	}
+	spin_unlock(&se_tpg->tpg_lun_lock);
+done:
+	put_unaligned_be32(nr_luns * 8, data);
+	return min(oalen, nr_luns * 8 + 8);
+}
+
+static int ibmvscsis_rdma(struct scsi_cmnd *sc, struct scatterlist *sg, int nsg,
+			  struct srp_direct_buf *md, int nmd,
+			  enum dma_data_direction dir, unsigned int rest)
+{
+	struct iu_entry *iue = (struct iu_entry *) sc->SCp.ptr;
+	struct srp_target *target = iue->target;
+	struct ibmvscsis_adapter *adapter = target->ldata;
+	dma_addr_t token;
+	long err;
+	unsigned int done = 0;
+	int i, sidx, soff;
+
+	sidx = soff = 0;
+	token = sg_dma_address(sg + sidx);
+
+	for (i = 0; i < nmd && rest; i++) {
+		unsigned int mdone, mlen;
+
+		mlen = min(rest, md[i].len);
+		for (mdone = 0; mlen;) {
+			int slen = min(sg_dma_len(sg + sidx) - soff, mlen);
+
+			if (dir == DMA_TO_DEVICE)
+				err = h_copy_rdma(slen,
+						  adapter->riobn,
+						  md[i].va + mdone,
+						  adapter->liobn,
+						  token + soff);
+			else
+				err = h_copy_rdma(slen,
+						  adapter->liobn,
+						  token + soff,
+						  adapter->riobn,
+						  md[i].va + mdone);
+
+			if (err != H_SUCCESS) {
+				printk(KERN_ERR "rdma error %d %d %ld\n",
+				       dir, slen, err);
+				return -EIO;
+			}
+
+			mlen -= slen;
+			mdone += slen;
+			soff += slen;
+			done += slen;
+
+			if (soff == sg_dma_len(sg + sidx)) {
+				sidx++;
+				soff = 0;
+				token = sg_dma_address(sg + sidx);
+
+				if (sidx > nsg) {
+					printk(KERN_ERR "out of sg %p %d %d\n",
+						iue, sidx, nsg);
+					return -EIO;
+				}
+			}
+		};
+
+		rest -= mlen;
+	}
+	return 0;
+}
+
+static int ibmvscsis_cmd_done(struct scsi_cmnd *sc)
+{
+	unsigned long flags;
+	struct iu_entry *iue = (struct iu_entry *) sc->SCp.ptr;
+	struct srp_target *target = iue->target;
+	int err = 0;
+
+	if (scsi_sg_count(sc))
+		err = srp_transfer_data(sc, &vio_iu(iue)->srp.cmd,
+					ibmvscsis_rdma, 1, 1);
+
+	spin_lock_irqsave(&target->lock, flags);
+	list_del(&iue->ilist);
+	spin_unlock_irqrestore(&target->lock, flags);
+
+	if (err || sc->result != SAM_STAT_GOOD) {
+		printk(KERN_ERR "operation failed %p %d %x\n",
+		       iue, sc->result, vio_iu(iue)->srp.cmd.cdb[0]);
+		send_rsp(iue, sc, HARDWARE_ERROR, 0x00);
+	} else
+		send_rsp(iue, sc, NO_SENSE, 0x00);
+
+	/* done(sc); */
+	srp_iu_put(iue);
+	return 0;
+}
+
+struct ibmvscsis_cmd {
+	/* Used for libsrp processing callbacks */
+	struct scsi_cmnd sc;
+	/* Used for TCM Core operations */
+	struct se_cmd se_cmd;
+	/* Sense buffer that will be mapped into outgoing status */
+	unsigned char sense_buf[TRANSPORT_SENSE_BUFFER];
+};
+
+static int ibmvscsis_write_pending(struct se_cmd *se_cmd)
+{
+	struct ibmvscsis_cmnd *cmd = container_of(se_cmd,
+			struct ibmvscsis_cmnd, se_cmd);
+	struct scsi_cmnd *sc = &cmd->sc;
+	struct iu_entry *iue = (struct iu_entry *) sc->SCp.ptr;
+	int ret;
+
+	sc->sdb.length = se_cmd->data_length;
+
+	if ((se_cmd->se_cmd_flags & SCF_SCSI_DATA_SG_IO_CDB) ||
+	    (se_cmd->se_cmd_flags & SCF_SCSI_CONTROL_SG_IO_CDB)) {
+		transport_do_task_sg_chain(se_cmd);
+
+		sc->sdb.table.nents = T_TASK(se_cmd)->t_tasks_sg_chained_no;
+		sc->sdb.table.sgl = T_TASK(se_cmd)->t_tasks_sg_chained;
+	} else if (se_cmd->se_cmd_flags & SCF_SCSI_CONTROL_NONSG_IO_CDB) {
+		/*
+		 * Use T_TASK(se_cmd)->t_tasks_sg_bounce for control CDBs
+		 * using a contigious buffer
+		 */
+		sg_init_table(&T_TASK(se_cmd)->t_tasks_sg_bounce, 1);
+		sg_set_buf(&T_TASK(se_cmd)->t_tasks_sg_bounce,
+			T_TASK(se_cmd)->t_task_buf, se_cmd->data_length);
+
+		sc->sdb.table.nents = 1;
+		sc->sdb.table.sgl = &T_TASK(se_cmd)->t_tasks_sg_bounce;
+	}
+
+	ret = srp_transfer_data(sc, &vio_iu(iue)->srp.cmd,
+				ibmvscsis_rdma, 1, 1);
+	if (ret) {
+		printk(KERN_ERR "srp_transfer_data() failed: %d\n", ret);
+		return PYX_TRANSPORT_LU_COMM_FAILURE;
+	}
+	/*
+	 * We now tell TCM to add this WRITE CDB directly into the TCM storage
+	 * object execution queue.
+	 */
+	transport_generic_process_write(se_cmd);
+	return 0;
+}
+
+static int ibmvscsis_queue_data_in(struct se_cmd *se_cmd)
+{
+	struct ibmvscsis_cmnd *cmd = container_of(se_cmd,
+			struct ibmvscsis_cmnd, se_cmd);
+	struct scsi_cmnd *sc = &cmd->sc;
+	/*
+	 * Check for overflow residual count
+	 */
+	if (se_cmd->se_cmd_flags & SCF_OVERFLOW_BIT)
+		scsi_set_resid(sc, se_cmd->residual_count);
+
+	sc->sdb.length = se_cmd->data_length;
+
+	/*
+	 * Setup the struct se_task->task_sg[] chained SG list
+	 */
+	if ((se_cmd->se_cmd_flags & SCF_SCSI_DATA_SG_IO_CDB) ||
+	    (se_cmd->se_cmd_flags & SCF_SCSI_CONTROL_SG_IO_CDB)) {
+		transport_do_task_sg_chain(se_cmd);
+
+		sc->sdb.table.nents = T_TASK(se_cmd)->t_tasks_sg_chained_no;
+		sc->sdb.table.sgl = T_TASK(se_cmd)->t_tasks_sg_chained;
+	} else if (se_cmd->se_cmd_flags & SCF_SCSI_CONTROL_NONSG_IO_CDB) {
+		/*
+		 * Use T_TASK(se_cmd)->t_tasks_sg_bounce for control CDBs
+		 * using a contigious buffer
+		 */
+		sg_init_table(&T_TASK(se_cmd)->t_tasks_sg_bounce, 1);
+		sg_set_buf(&T_TASK(se_cmd)->t_tasks_sg_bounce,
+			T_TASK(se_cmd)->t_task_buf, se_cmd->data_length);
+
+		sc->sdb.table.nents = 1;
+		sc->sdb.table.sgl = &T_TASK(se_cmd)->t_tasks_sg_bounce;
+	}
+	/*
+	 * This will call srp_transfer_data() and post the response
+	 * to VIO via libsrp.
+	 */
+	ibmvscsis_cmd_done(sc);
+	return 0;
+}
+
+static int ibmvscsis_queue_status(struct se_cmd *se_cmd)
+{
+	struct ibmvscsis_cmnd *cmd = container_of(se_cmd,
+						  struct ibmvscsis_cmnd, se_cmd);
+	struct scsi_cmnd *sc = &cmd->sc;
+	/*
+	 * Copy any generated SENSE data into sc->sense_buffer and
+	 * set the appropiate sc->result to be translated by
+	 * ibmvscsis_cmd_done()
+	 */
+	if (se_cmd->sense_buffer &&
+	   ((se_cmd->se_cmd_flags & SCF_TRANSPORT_TASK_SENSE) ||
+	    (se_cmd->se_cmd_flags & SCF_EMULATED_TASK_SENSE))) {
+		memcpy((void *)sc->sense_buffer, (void *)se_cmd->sense_buffer,
+				SCSI_SENSE_BUFFERSIZE);
+		sc->result = host_byte(DID_OK) | driver_byte(DRIVER_SENSE) |
+				SAM_STAT_CHECK_CONDITION;
+	} else
+		sc->result = host_byte(DID_OK) | se_cmd->scsi_status;
+	/*
+	 * Finally post the response to VIO via libsrp.
+	 */
+	ibmvscsis_cmd_done(sc);
+	return 0;
+}
+
+static int ibmvscsis_queuecommand(struct ibmvscsis_adapter *adapter,
+				  struct iu_entry *iue)
+{
+	int data_len;
+	struct srp_cmd *cmd = iue->sbuf->buf;
+	struct scsi_cmnd *sc;
+	struct page *pg;
+	struct ibmvscsis_cmnd *vsc;
+
+	data_len = srp_data_length(cmd, srp_cmd_direction(cmd));
+
+	vsc = kzalloc(sizeof(*vsc), GFP_KERNEL);
+	sc = &vsc->sc;
+	sc->sense_buffer = vsc->sense_buf;
+	sc->cmnd = cmd->cdb;
+	sc->SCp.ptr = (char *)iue;
+
+	switch (cmd->cdb[0]) {
+	case INQUIRY:
+		sg_alloc_table(&sc->sdb.table, 1, GFP_KERNEL);
+		pg = alloc_page(GFP_KERNEL|__GFP_ZERO);
+		sc->sdb.length = ibmvscsis_inquiry(adapter, cmd,
+						   page_address(pg));
+		sg_set_page(sc->sdb.table.sgl, pg, sc->sdb.length, 0);
+		ibmvscsis_cmd_done(sc);
+		sg_free_table(&sc->sdb.table);
+		__free_page(pg);
+		kfree(vsc);
+		break;
+	case REPORT_LUNS:
+		sg_alloc_table(&sc->sdb.table, 1, GFP_KERNEL);
+		pg = alloc_page(GFP_KERNEL|__GFP_ZERO);
+		sc->sdb.length = ibmvscsis_report_luns(adapter, cmd,
+						       page_address(pg));
+		sg_set_page(sc->sdb.table.sgl, pg, sc->sdb.length, 0);
+		ibmvscsis_cmd_done(sc);
+		sg_free_table(&sc->sdb.table);
+		__free_page(pg);
+		kfree(vsc);
+		break;
+	case MODE_SENSE:
+		/* fixme: needs to use tcm */
+		sg_alloc_table(&sc->sdb.table, 1, GFP_KERNEL);
+		pg = alloc_page(GFP_KERNEL|__GFP_ZERO);
+		sc->sdb.length = ibmvscsis_mode_sense(adapter,
+						      cmd, page_address(pg));
+		sg_set_page(sc->sdb.table.sgl, pg, sc->sdb.length, 0);
+		ibmvscsis_cmd_done(sc);
+		sg_free_table(&sc->sdb.table);
+		__free_page(pg);
+		kfree(vsc);
+		break;
+	default:
+		tcm_queuecommand(adapter, vsc, cmd);
+		break;
+	}
+
+	return 0;
+}
+
+static void handle_cmd_queue(struct ibmvscsis_adapter *adapter)
+{
+	struct srp_target *target = &adapter->srpt;
+	struct iu_entry *iue;
+	struct srp_cmd *cmd;
+	unsigned long flags;
+	int err;
+
+retry:
+	spin_lock_irqsave(&target->lock, flags);
+
+	list_for_each_entry(iue, &target->cmd_queue, ilist) {
+		if (!test_and_set_bit(V_FLYING, &iue->flags)) {
+			spin_unlock_irqrestore(&target->lock, flags);
+			err = ibmvscsis_queuecommand(adapter, iue);
+			if (err) {
+				printk(KERN_ERR "cannot queue cmd %p %d\n",
+				       cmd, err);
+				srp_iu_put(iue);
+			}
+			goto retry;
+		}
+	}
+
+	spin_unlock_irqrestore(&target->lock, flags);
+}
+
+static void handle_crq(struct work_struct *work)
+{
+	struct ibmvscsis_adapter *adapter =
+		container_of(work, struct ibmvscsis_adapter, crq_work);
+	struct viosrp_crq *crq;
+	int done = 0;
+
+	while (!done) {
+		while ((crq = next_crq(&adapter->crq_queue)) != NULL) {
+			process_crq(crq, adapter);
+			crq->valid = 0x00;
+		}
+
+		vio_enable_interrupts(adapter->dma_dev);
+
+		crq = next_crq(&adapter->crq_queue);
+		if (crq) {
+			vio_disable_interrupts(adapter->dma_dev);
+			process_crq(crq, adapter);
+			crq->valid = 0x00;
+		} else
+			done = 1;
+	}
+
+	handle_cmd_queue(adapter);
+}
+
+static irqreturn_t ibmvscsis_interrupt(int dummy, void *data)
+{
+	struct ibmvscsis_adapter *adapter = data;
+
+	vio_disable_interrupts(adapter->dma_dev);
+	schedule_work(&adapter->crq_work);
+
+	return IRQ_HANDLED;
+}
+
+static int crq_queue_create(struct crq_queue *queue,
+			    struct ibmvscsis_adapter *adapter)
+{
+	int err;
+	struct vio_dev *vdev = adapter->dma_dev;
+
+	queue->msgs = (struct viosrp_crq *)get_zeroed_page(GFP_KERNEL);
+	if (!queue->msgs)
+		goto malloc_failed;
+	queue->size = PAGE_SIZE / sizeof(*queue->msgs);
+
+	queue->msg_token = dma_map_single(&vdev->dev, queue->msgs,
+					  queue->size * sizeof(*queue->msgs),
+					  DMA_BIDIRECTIONAL);
+
+	if (dma_mapping_error(&vdev->dev, queue->msg_token))
+		goto map_failed;
+
+	err = h_reg_crq(vdev->unit_address, queue->msg_token,
+			PAGE_SIZE);
+
+	/* If the adapter was left active for some reason (like kexec)
+	 * try freeing and re-registering
+	 */
+	if (err == H_RESOURCE) {
+		do {
+			err = h_free_crq(vdev->unit_address);
+		} while (err == H_BUSY || H_IS_LONG_BUSY(err));
+
+		err = h_reg_crq(vdev->unit_address, queue->msg_token,
+				PAGE_SIZE);
+	}
+
+	if (err != H_SUCCESS && err != 2) {
+		printk(KERN_ERR "Error 0x%x opening virtual adapter\n", err);
+		goto reg_crq_failed;
+	}
+
+	err = request_irq(vdev->irq, &ibmvscsis_interrupt,
+			  IRQF_DISABLED, "ibmvscsis", adapter);
+	if (err)
+		goto req_irq_failed;
+
+	vio_enable_interrupts(vdev);
+
+	h_send_crq(vdev->unit_address, 0xC001000000000000, 0);
+
+	queue->cur = 0;
+	spin_lock_init(&queue->lock);
+
+	return 0;
+
+req_irq_failed:
+	do {
+		err = h_free_crq(vdev->unit_address);
+	} while (err == H_BUSY || H_IS_LONG_BUSY(err));
+
+reg_crq_failed:
+	dma_unmap_single(&vdev->dev, queue->msg_token,
+			 queue->size * sizeof(*queue->msgs), DMA_BIDIRECTIONAL);
+map_failed:
+	free_page((unsigned long) queue->msgs);
+
+malloc_failed:
+	return -ENOMEM;
+}
+
+static void crq_queue_destroy(struct ibmvscsis_adapter *adapter)
+{
+	struct crq_queue *queue = &adapter->crq_queue;
+	int err;
+
+	free_irq(adapter->dma_dev->irq, adapter);
+	flush_work_sync(&adapter->crq_work);
+	do {
+		err = h_free_crq(adapter->dma_dev->unit_address);
+	} while (err == H_BUSY || H_IS_LONG_BUSY(err));
+
+	dma_unmap_single(&adapter->dma_dev->dev, queue->msg_token,
+			 queue->size * sizeof(*queue->msgs), DMA_BIDIRECTIONAL);
+
+	free_page((unsigned long)queue->msgs);
+}
+
+static int ibmvscsis_probe(struct vio_dev *dev, const struct vio_device_id *id)
+{
+	unsigned int *dma, dma_size;
+	unsigned long flags;
+	int ret;
+	struct ibmvscsis_adapter *adapter;
+
+	adapter = kzalloc(sizeof(*adapter), GFP_KERNEL);
+	if (!adapter)
+		return -ENOMEM;
+
+	adapter->dma_dev = dev;
+
+	dma = (unsigned int *)vio_get_attribute(dev, "ibm,my-dma-window",
+						&dma_size);
+	if (!dma || dma_size != 40) {
+		printk(KERN_ERR "Couldn't get window property %d\n", dma_size);
+		kfree(adapter);
+		return -EIO;
+	}
+
+	adapter->liobn = dma[0];
+	adapter->riobn = dma[5];
+	ret = strict_strtoul(dev_name(&dev->dev), 10, &adapter->tport_tpgt);
+
+	spin_lock_irqsave(&tpg_lock, flags);
+	list_add(&adapter->siblings, &tpg_list);
+	spin_unlock_irqrestore(&tpg_lock, flags);
+
+	INIT_WORK(&adapter->crq_work, handle_crq);
+
+	dev_set_drvdata(&dev->dev, adapter);
+
+	ret = srp_target_alloc(&adapter->srpt, &dev->dev, INITIAL_SRP_LIMIT,
+			       SRP_MAX_IU_LEN);
+
+	adapter->srpt.ldata = adapter;
+
+	ret = crq_queue_create(&adapter->crq_queue, adapter);
+
+	return 0;
+}
+
+static int ibmvscsis_remove(struct vio_dev *dev)
+{
+	struct ibmvscsis_adapter *adapter = dev_get_drvdata(&dev->dev);
+	unsigned long flags;
+
+	spin_lock_irqsave(&tpg_lock, flags);
+	list_del(&adapter->siblings);
+	spin_unlock_irqrestore(&tpg_lock, flags);
+
+	crq_queue_destroy(adapter);
+
+	srp_target_free(&adapter->srpt);
+
+	kfree(adapter);
+	return 0;
+}
+
+static struct vio_device_id ibmvscsis_device_table[] __devinitdata = {
+	{"v-scsi-host", "IBM,v-scsi-host"},
+	{"", ""}
+};
+
+MODULE_DEVICE_TABLE(vio, ibmvscsis_device_table);
+
+static struct vio_driver ibmvscsis_driver = {
+	.id_table = ibmvscsis_device_table,
+	.probe = ibmvscsis_probe,
+	.remove = ibmvscsis_remove,
+	.driver = {
+		.name = "ibmvscsis",
+		.owner = THIS_MODULE,
+	}
+};
+
+static int get_system_info(void)
+{
+	struct device_node *rootdn;
+	const char *id, *model, *name;
+	const unsigned int *num;
+
+	rootdn = of_find_node_by_path("/");
+	if (!rootdn)
+		return -ENOENT;
+
+	model = of_get_property(rootdn, "model", NULL);
+	id = of_get_property(rootdn, "system-id", NULL);
+	if (model && id)
+		snprintf(system_id, sizeof(system_id), "%s-%s", model, id);
+
+	name = of_get_property(rootdn, "ibm,partition-name", NULL);
+	if (name)
+		strncpy(partition_name, name, sizeof(partition_name));
+
+	num = of_get_property(rootdn, "ibm,partition-no", NULL);
+	if (num)
+		partition_number = *num;
+
+	of_node_put(rootdn);
+	return 0;
+}
+
+static int ibmvscsis_register_configfs(void)
+{
+	struct target_fabric_configfs *fabric;
+	int ret;
+
+	printk(KERN_INFO "IBMVSCSIS fabric module %s on %s/%s"
+		" on "UTS_RELEASE"\n", IBMVSCSIS_VERSION, utsname()->sysname,
+		utsname()->machine);
+	/*
+	 * Register the top level struct config_item_type with TCM core
+	 */
+	fabric = target_fabric_configfs_init(THIS_MODULE, "ibmvscsis");
+	if (!(fabric)) {
+		printk(KERN_ERR "target_fabric_configfs_init() failed\n");
+		return -ENOMEM;
+	}
+	/*
+	 * Setup fabric->tf_ops from our local ibmvscsis_ops
+	 */
+	fabric->tf_ops = ibmvscsis_ops;
+	/*
+	 * Setup default attribute lists for various fabric->tf_cit_tmpl
+	 */
+	TF_CIT_TMPL(fabric)->tfc_wwn_cit.ct_attrs = ibmvscsis_wwn_attrs;
+	TF_CIT_TMPL(fabric)->tfc_tpg_base_cit.ct_attrs = NULL;
+	TF_CIT_TMPL(fabric)->tfc_tpg_attrib_cit.ct_attrs = NULL;
+	TF_CIT_TMPL(fabric)->tfc_tpg_param_cit.ct_attrs = NULL;
+	TF_CIT_TMPL(fabric)->tfc_tpg_np_base_cit.ct_attrs = NULL;
+	TF_CIT_TMPL(fabric)->tfc_tpg_nacl_base_cit.ct_attrs = NULL;
+	TF_CIT_TMPL(fabric)->tfc_tpg_nacl_attrib_cit.ct_attrs = NULL;
+	TF_CIT_TMPL(fabric)->tfc_tpg_nacl_auth_cit.ct_attrs = NULL;
+	TF_CIT_TMPL(fabric)->tfc_tpg_nacl_param_cit.ct_attrs = NULL;
+	/*
+	 * Register the fabric for use within TCM
+	 */
+	ret = target_fabric_configfs_register(fabric);
+	if (ret < 0) {
+		printk(KERN_ERR "target_fabric_configfs_register() failed"
+				" for IBMVSCSIS\n");
+		target_fabric_configfs_deregister(fabric);
+		return ret;
+	}
+	/*
+	 * Setup our local pointer to *fabric
+	 */
+	ibmvscsis_fabric_configfs = fabric;
+	printk(KERN_INFO "IBMVSCSIS[0] - Set fabric -> ibmvscsis_fabric_configfs\n");
+	return 0;
+};
+
+static void ibmvscsis_deregister_configfs(void)
+{
+	if (!(ibmvscsis_fabric_configfs))
+		return;
+
+	target_fabric_configfs_deregister(ibmvscsis_fabric_configfs);
+	ibmvscsis_fabric_configfs = NULL;
+	printk(KERN_INFO "IBMVSCSIS[0] - Cleared ibmvscsis_fabric_configfs\n");
+};
+
+static int __init ibmvscsis_init(void)
+{
+	int ret;
+
+	ret = get_system_info();
+	if (ret)
+		return ret;
+
+	ret = vio_register_driver(&ibmvscsis_driver);
+	if (ret)
+		return ret;
+
+	ret = ibmvscsis_register_configfs();
+	if (ret < 0)
+		return ret;
+
+	return 0;
+};
+
+static void ibmvscsis_exit(void)
+{
+	vio_unregister_driver(&ibmvscsis_driver);
+	ibmvscsis_deregister_configfs();
+};
+
+MODULE_DESCRIPTION("IBMVSCSIS series fabric driver");
+MODULE_AUTHOR("FUJITA Tomonori");
+MODULE_LICENSE("GPL");
+module_init(ibmvscsis_init);
+module_exit(ibmvscsis_exit);