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.tomon...@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.tomon...@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

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 (boutc...@us.ibm.com) IBM Corp.
+ *                        Santiago Leon (san...@us.ibm.com) IBM Corp.
+ *                        Linda Xie (l...@us.ibm.com) IBM Corp.
+ *
+ * Copyright (C) 2005-2011 FUJITA Tomonori <to...@acm.org>
+ * Copyright (C) 2010 Nicholas A. Bellinger <n...@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);
-- 
1.7.2.3

_______________________________________________
Linuxppc-dev mailing list
Linuxppc-dev@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/linuxppc-dev

Reply via email to