diff mbox

ibmvscsis: Initial commit of IBM VSCSI Tgt Driver

Message ID 1464097978-88457-1-git-send-email-bryantly@linux.vnet.ibm.com (mailing list archive)
State Superseded, archived
Headers show

Commit Message

Bryant G. Ly May 24, 2016, 1:52 p.m. UTC
From: bgly <bgly@us.ibm.com>

This initial commit contains WIP of the IBM VSCSI Target Fabric
Module. It currently supports read/writes, and I have tested
the ability to create a file backstore with the driver and install
RHEL VIA NIM and then boot up the partition via filio backstore
through the driver.

Signed-off-by: bryantly <bryantly@linux.vnet.ibm.com>
---
 MAINTAINERS                       |   10 +
 drivers/scsi/Kconfig              |   24 +
 drivers/scsi/Makefile             |    2 +
 drivers/scsi/ibmvscsi/Makefile    |    1 +
 drivers/scsi/ibmvscsi/ibmvscsis.c | 2033 +++++++++++++++++++++++++++++++++++++
 drivers/scsi/ibmvscsi/ibmvscsis.h |  160 +++
 drivers/scsi/libsrp.c             |  387 +++++++
 include/scsi/libsrp.h             |   95 ++
 8 files changed, 2712 insertions(+)
 create mode 100644 drivers/scsi/ibmvscsi/ibmvscsis.c
 create mode 100644 drivers/scsi/ibmvscsi/ibmvscsis.h
 create mode 100644 drivers/scsi/libsrp.c
 create mode 100644 include/scsi/libsrp.h

--

This is a first push up for grok and is WIP code. The driver does function,
but there are alot of features left that need to be handled and will be
addressed in a series of upcoming patches. We currently have a version of the
driver that is closer to being complete, but is pending lawyers approval,
since it was written by referencing our internal VIOS Virtual SCSI driver.

2.5.4 (Apple Git-61)

--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Comments

Joe Perches May 24, 2016, 2:14 p.m. UTC | #1
On Tue, 2016-05-24 at 08:52 -0500, Bryant G. Ly wrote:
> From: bgly <bgly@us.ibm.com>
> 
> This initial commit contains WIP of the IBM VSCSI Target Fabric
> Module. It currently supports read/writes, and I have tested
> the ability to create a file backstore with the driver and install
> RHEL VIA NIM and then boot up the partition via filio backstore
> through the driver.

Only trivial notes:

Maybe try checkpatch with the --strict option and see
if any of the additional messages are important to you.

> diff --git a/MAINTAINERS b/MAINTAINERS
[]
> @@ -5381,6 +5381,16 @@ S:	Supported
>  F:	drivers/scsi/ibmvscsi/ibmvscsi*
>  F:	drivers/scsi/ibmvscsi/viosrp.h
>  
> +IBM Power Virtual SCSI Device Target Driver
> +M:	Bryant G. Ly <bryantly@linux.vnet.ibm.com>
> +L:	linux-scsi@vger.kernel.org
> +L:	target-devel@vger.kernel.org
> +S:	Supported
> +F:	drivers/scsi/ibmvscsi/ibmvscsis.c
> +F:      drivers/scsi/ibmvscsi/ibmvscsis.h
> +F:	drivers/scsi/libsrp.h
> +F:      drivers/scsi/libsrp.c

Please use a tab character consistently after the :

> diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
[]
> @@ -847,6 +847,20 @@ config SCSI_IBMVSCSI
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called ibmvscsi.
>  
> +config SCSI_IBMVSCSIS
> +  	tristate "IBM Virtual SCSI Server support"
> +  	depends on PPC_PSERIES && SCSI_SRP && TARGET_CORE
> +  	help
> +  	  This is the IBM POWER Virtual SCSI Target Server
> +
> +          The userspace component needed to initialize the driver and
> +  	  documentation can be found:

here too.

> +
> +          https://github.com/powervm/ibmvscsis
> +
> +          To compile this driver as a module, choose M here: the
> +  	  module will be called ibmvstgt.
> +

[]

> diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile
[]
> @@ -127,7 +127,9 @@ obj-$(CONFIG_SCSI_LASI700)	+= 53c700.o lasi700.o
>  obj-$(CONFIG_SCSI_SNI_53C710)	+= 53c700.o sni_53c710.o
>  obj-$(CONFIG_SCSI_NSP32)	+= nsp32.o
>  obj-$(CONFIG_SCSI_IPR)		+= ipr.o
> +obj-$(CONFIG_SCSI_SRP)          += libsrp.o
>  obj-$(CONFIG_SCSI_IBMVSCSI)	+= ibmvscsi/
> +obj-$(CONFIG_SCSI_IBMVSCSIS)    += ibmvscsi/

and here

> diff --git a/drivers/scsi/ibmvscsi/ibmvscsis.c b/drivers/scsi/ibmvscsi/ibmvscsis.c
[]

> +static int ibmvscsis_probe(struct vio_dev *vdev,
> +			   const struct vio_device_id *id);
[...]

It might be nice to rearrange the code to avoid these forward
function declarations.

> +static ssize_t ibmvscsis_tpg_enable_store(struct config_item *item,
> +		const char *page, size_t count)
> +{
[]
> > +	ret = kstrtoul(page, 0, &tmp);
> +	if (ret < 0) {
> +		pr_err("Unable to extract ibmvscsis_tpg_store_enable\n");
> +		return -EINVAL;
> +	}

It might be nicer to add:

#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

before any #include to output all the logging messages with
a standardized prefix.  Then all the other logging with an
embedded prefix can have the embedded prefix removed too.

> +
> +	if ((tmp != 0) && (tmp != 1)) {
> +		pr_err("Illegal value for ibmvscsis_tpg_store_enable: %lu\n",
> +			tmp);
> +		return -EINVAL;
> +	}
> +
> +	if (tmp == 1)
> +		tport->enabled = true;
> +	else
> +		tport->enabled = false;

	tport->enabled = tmp;

--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Greg KH May 24, 2016, 2:30 p.m. UTC | #2
On Tue, May 24, 2016 at 08:52:58AM -0500, Bryant G. Ly wrote:
> From: bgly <bgly@us.ibm.com>

Really?  Please go get a proper review from the other internal IBM
developers to fix this, and the other obvious problems with your patch,
before you send it publically and force us to tell you these things...

thanks,

greg k-h
--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Bart Van Assche May 24, 2016, 4:25 p.m. UTC | #3
On 05/24/2016 06:52 AM, Bryant G. Ly wrote:
> +config SCSI_IBMVSCSIS
> +  	tristate "IBM Virtual SCSI Server support"
> +  	depends on PPC_PSERIES && SCSI_SRP && TARGET_CORE
> +  	help
> +  	  This is the IBM POWER Virtual SCSI Target Server
> +
> +          The userspace component needed to initialize the driver and
> +  	  documentation can be found:
> +
> +          https://github.com/powervm/ibmvscsis
> +
> +          To compile this driver as a module, choose M here: the
> +  	  module will be called ibmvstgt.
> +

This driver has confused Linux users before. Most people know SRP as a 
SCSI transport layer that is used for communication between servers. 
This driver however uses the SRP protocol for communication between 
guests and/or the host that run on the same server. Please add this 
information to the above help text, together with a reference to the VIO 
protocol documentation in the POWER architecture manual.

> +#include <generated/utsrelease.h>

Every Linux user can query the kernel version by running the uname 
command. Is it really useful to print the kernel version (UTS_RELEASE) 
when this driver is loaded?

> +#include "ibmvscsi.h"

The above include directive indirectly includes a whole bunch of SCSI 
initiator driver header files. We do not want that initiator header 
files are included in the source code of a target driver. If it is 
necessary to share definitions of symbolic constants between the 
initiator and the target driver please move these into a new header file.

> +static inline long h_send_crq(struct ibmvscsis_adapter *adapter,
> +			u64 word1, u64 word2)
> +{
> +	long rc;
> +	struct vio_dev *vdev = adapter->dma_dev;
> +
> +	pr_debug("ibmvscsis: ibmvscsis_send_crq(0x%x, 0x%016llx, 0x%016llx)\n",
> +			vdev->unit_address, word1, word2);
> +

As Joe Perches already asked, please define pr_fmt() instead of 
including the kernel module name in every pr_debug() statement.

> +static char system_id[64] = "";
> +static char partition_name[97] = "UNKNOWN";

Where do these magic constants come from and what is their meaning? 
Please consider to introduce symbolic names for these constants.

> +static ssize_t ibmvscsis_tpg_enable_show(struct config_item *item,
> +				char *page)
> +{
> +	struct se_portal_group *se_tpg = to_tpg(item);
> +	struct ibmvscsis_tport *tport = container_of(se_tpg,
> +						struct ibmvscsis_tport, se_tpg);
> +
> +	return snprintf(page, PAGE_SIZE, "%d\n", (tport->enabled) ? 1 : 0);
> +}

Have you considered to use MODULE_VERSION() instead of introducing a 
sysfs attribute to export the module version? An advantage of 
MODULE_VERSION() is that it allows to query the module version without 
having to load a kernel module.

> +static void ibmvscsis_modify_std_inquiry(struct se_cmd *se_cmd)
> +{
> +	struct se_device *dev = se_cmd->se_dev;
> +	unsigned char *buf = NULL;
> +	u32 cmd_len = se_cmd->data_length;
> +
> +	if (cmd_len <= INQ_DATA_OFFSET)
> +		return;
> +
> +	buf = transport_kmap_data_sg(se_cmd);
> +	if (buf) {
> +		memcpy(&buf[8], "IBM	     ", 8);
> +		if (dev->transport->get_device_type(dev) == TYPE_ROM)
> +			memcpy(&buf[16], "VOPTA           ", 16);
> +		else
> +			memcpy(&buf[16], "3303      NVDISK", 16);
> +		memcpy(&buf[32], "0001", 4);
> +		transport_kunmap_data_sg(se_cmd);
> +	}
> +}

Shouldn't a sense code be returned to the initiator if this function fails?

> +	default:
> +		pr_err("ibmvscsis: unknown task mgmt func %d\n",
> +						srp_tsk->tsk_mgmt_func);
> +		cmd->se_cmd.se_tmr_req->response =
> +					TMR_TASK_MGMT_FUNCTION_NOT_SUPPORTED;
> +		rc = -1;
> +		break;
> +	}
> +
> +	if (!rc) {

Please consider changing the above "break" into "goto fail" such that 
the if (!rc) can be left out and such that the indentation of the code 
below if (!rc) can be reduced.

> +static int ibmvscsis_queuecommand(struct ibmvscsis_adapter *adapter,
> +				  struct iu_entry *iue)
> +{
> +	struct srp_cmd *cmd = iue->sbuf->buf;
> +	struct scsi_cmnd *sc;
> +	struct ibmvscsis_cmnd *vsc;
> +	int ret;
> +
> +	pr_debug("ibmvscsis: ibmvscsis_queuecommand\n");
> +
> +	vsc = kzalloc(sizeof(*vsc), GFP_KERNEL);

Allocating memory in the command processing path in a SCSI driver 
usually causes suboptimal performance. Other LIO target drivers 
preallocate such buffers before I/O starts.

> +static uint64_t ibmvscsis_unpack_lun(const uint8_t *lun, int len)
> +{
> +	uint64_t res = NO_SUCH_LUN;
> +	int addressing_method;
> +
> +	if (unlikely(len < 2)) {
> +		pr_err("Illegal LUN length %d, expected 2 bytes or more\n",
> +			len);
> +		goto out;
> +	}
> +
> +	switch (len) {
> +	case 8:
> +		if ((*((__be64 *)lun) & cpu_to_be64(0x0000FFFFFFFFFFFFLL)) != 0)
> +			goto out_err;
> +		break;
> +	case 4:
> +		if (*((__be16 *)&lun[2]) != 0)
> +			goto out_err;
> +		break;
> +	case 6:
> +		if (*((__be32 *)&lun[2]) != 0)
> +			goto out_err;
> +		break;
> +	case 2:
> +		break;
> +	default:
> +		goto out_err;
> +	}
> +
> +	addressing_method = (*lun) >> 6; /* highest two bits of byte 0 */
> +	switch (addressing_method) {
> +	case SCSI_LUN_ADDR_METHOD_PERIPHERAL:
> +	case SCSI_LUN_ADDR_METHOD_FLAT:
> +	case SCSI_LUN_ADDR_METHOD_LUN:
> +		res = *(lun + 1) | (((*lun) & 0x3f) << 8);
> +		break;
> +
> +	case SCSI_LUN_ADDR_METHOD_EXTENDED_LUN:
> +	default:
> +		pr_err("Unimplemented LUN addressing method %u\n",
> +			addressing_method);
> +		break;
> +	}
> +
> +out:
> +	return res;
> +out_err:
> +	pr_err("Support for multi-level LUNs has not yet been implemented\n");
> +	goto out;
> +}

In the above function there is nothing that is specific to the VIO 
mechanism. Please consider to merge this function with scsilun_to_int(), 
e.g. by introducing a new function that accepts a SCSI LUN and the 
addressing method as arguments and by making scsilun_to_int() call that 
function with SCSI_LUN_ADDR_METHOD_PERIPHERAL as one of its arguments.

> +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];
> +};

Is this the inquiry data response format from SPC? If so, this is a 
structure definition that is useful for the SCSI initiator core and also 
for other LIO target drivers. There might be a more appropriate header 
file for this definition.

> +enum scsi_lun_addr_method {
> +	SCSI_LUN_ADDR_METHOD_PERIPHERAL   = 0,
> +	SCSI_LUN_ADDR_METHOD_FLAT         = 1,
> +	SCSI_LUN_ADDR_METHOD_LUN          = 2,
> +	SCSI_LUN_ADDR_METHOD_EXTENDED_LUN = 3,
> +};

Same comment here.

> diff --git a/drivers/scsi/libsrp.c b/drivers/scsi/libsrp.c
> new file mode 100644
> index 0000000..0e32abd
> --- /dev/null
> +++ b/drivers/scsi/libsrp.c
> @@ -0,0 +1,387 @@
> +/*******************************************************************************
> + * SCSI RDMA Protocol lib functions

My understanding of the VIO mechanism is that the invocation of 
H_COPY_RDMA is synchronous. This means that this operation only finishes 
after the data has been transferred. If I interpret the code in libsrp.c 
and libsrp.h correctly then this code can only be used by SRP drivers 
use synchronous data transfers and not by SRP drivers that use 
asynchronous data transfers. This means that this code is not useful for 
any of the SRP drivers that are already upstream (ib_srp and ib_srpt). 
Hence my proposal to move the libsrp.c and libsrp.h source files from 
drivers/scsi and include/scsi to the same directory as the ibmvscsis driver.

Thanks,

Bart.
--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Greg KH May 24, 2016, 4:34 p.m. UTC | #4
On Tue, May 24, 2016 at 09:25:05AM -0700, Bart Van Assche wrote:
> > +static inline long h_send_crq(struct ibmvscsis_adapter *adapter,
> > +			u64 word1, u64 word2)
> > +{
> > +	long rc;
> > +	struct vio_dev *vdev = adapter->dma_dev;
> > +
> > +	pr_debug("ibmvscsis: ibmvscsis_send_crq(0x%x, 0x%016llx, 0x%016llx)\n",
> > +			vdev->unit_address, word1, word2);
> > +
> 
> As Joe Perches already asked, please define pr_fmt() instead of including
> the kernel module name in every pr_debug() statement.

Even better, as this is a driver, it should be using dev_*() calls
instead of pr_*() calls to properly identify the device and driver that
is making the message.  No driver should be using pr_*() except in
_very_ limited usages.

thanks,

greg k-h
--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Bart Van Assche May 24, 2016, 4:44 p.m. UTC | #5
On 05/24/2016 09:34 AM, Greg KH wrote:
> On Tue, May 24, 2016 at 09:25:05AM -0700, Bart Van Assche wrote:
>>> +static inline long h_send_crq(struct ibmvscsis_adapter *adapter,
>>> +			u64 word1, u64 word2)
>>> +{
>>> +	long rc;
>>> +	struct vio_dev *vdev = adapter->dma_dev;
>>> +
>>> +	pr_debug("ibmvscsis: ibmvscsis_send_crq(0x%x, 0x%016llx, 0x%016llx)\n",
>>> +			vdev->unit_address, word1, word2);
>>> +
>>
>> As Joe Perches already asked, please define pr_fmt() instead of including
>> the kernel module name in every pr_debug() statement.
>
> Even better, as this is a driver, it should be using dev_*() calls
> instead of pr_*() calls to properly identify the device and driver that
> is making the message.  No driver should be using pr_*() except in
> _very_ limited usages.

Hi Greg,

The reason I recommended pr_debug() is because ibmvscsis is a LIO target 
driver and I don't think a struct device is associated with a LIO target 
driver. See e.g. target_register_template() in 
drivers/target/target_core_configfs.c.

Bart.
--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Greg KH May 24, 2016, 4:50 p.m. UTC | #6
On Tue, May 24, 2016 at 09:44:43AM -0700, Bart Van Assche wrote:
> On 05/24/2016 09:34 AM, Greg KH wrote:
> > On Tue, May 24, 2016 at 09:25:05AM -0700, Bart Van Assche wrote:
> > > > +static inline long h_send_crq(struct ibmvscsis_adapter *adapter,
> > > > +			u64 word1, u64 word2)
> > > > +{
> > > > +	long rc;
> > > > +	struct vio_dev *vdev = adapter->dma_dev;
> > > > +
> > > > +	pr_debug("ibmvscsis: ibmvscsis_send_crq(0x%x, 0x%016llx, 0x%016llx)\n",
> > > > +			vdev->unit_address, word1, word2);
> > > > +
> > > 
> > > As Joe Perches already asked, please define pr_fmt() instead of including
> > > the kernel module name in every pr_debug() statement.
> > 
> > Even better, as this is a driver, it should be using dev_*() calls
> > instead of pr_*() calls to properly identify the device and driver that
> > is making the message.  No driver should be using pr_*() except in
> > _very_ limited usages.
> 
> Hi Greg,
> 
> The reason I recommended pr_debug() is because ibmvscsis is a LIO target
> driver and I don't think a struct device is associated with a LIO target
> driver. See e.g. target_register_template() in
> drivers/target/target_core_configfs.c.

That seems messed up, there should be a struct device somewhere to
use...
--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Bryant G. Ly May 24, 2016, 8 p.m. UTC | #7
Quoting Bart Van Assche <bart.vanassche@sandisk.com>:

> On 05/24/2016 06:52 AM, Bryant G. Ly wrote:
>> +config SCSI_IBMVSCSIS
>> +  	tristate "IBM Virtual SCSI Server support"
>> +  	depends on PPC_PSERIES && SCSI_SRP && TARGET_CORE
>> +  	help
>> +  	  This is the IBM POWER Virtual SCSI Target Server
>> +
>> +          The userspace component needed to initialize the driver and
>> +  	  documentation can be found:
>> +
>> +          https://github.com/powervm/ibmvscsis
>> +
>> +          To compile this driver as a module, choose M here: the
>> +  	  module will be called ibmvstgt.
>> +
>
> This driver has confused Linux users before. Most people know SRP as  
> a SCSI transport layer that is used for communication between  
> servers. This driver however uses the SRP protocol for communication  
> between guests and/or the host that run on the same server. Please  
> add this information to the above help text, together with a  
> reference to the VIO protocol documentation in the POWER  
> architecture manual.
>

Done

>> +#include <generated/utsrelease.h>
>
> Every Linux user can query the kernel version by running the uname  
> command. Is it really useful to print the kernel version  
> (UTS_RELEASE) when this driver is loaded?
>

Done

>> +#include "ibmvscsi.h"
>
> The above include directive indirectly includes a whole bunch of  
> SCSI initiator driver header files. We do not want that initiator  
> header files are included in the source code of a target driver. If  
> it is necessary to share definitions of symbolic constants between  
> the initiator and the target driver please move these into a new  
> header file.
>

Done

>> +static inline long h_send_crq(struct ibmvscsis_adapter *adapter,
>> +			u64 word1, u64 word2)
>> +{
>> +	long rc;
>> +	struct vio_dev *vdev = adapter->dma_dev;
>> +
>> +	pr_debug("ibmvscsis: ibmvscsis_send_crq(0x%x, 0x%016llx, 0x%016llx)\n",
>> +			vdev->unit_address, word1, word2);
>> +
>
> As Joe Perches already asked, please define pr_fmt() instead of  
> including the kernel module name in every pr_debug() statement.
>

Done

>> +static char system_id[64] = "";
>> +static char partition_name[97] = "UNKNOWN";
>
> Where do these magic constants come from and what is their meaning?  
> Please consider to introduce symbolic names for these constants.
>

Done

>> +static ssize_t ibmvscsis_tpg_enable_show(struct config_item *item,
>> +				char *page)
>> +{
>> +	struct se_portal_group *se_tpg = to_tpg(item);
>> +	struct ibmvscsis_tport *tport = container_of(se_tpg,
>> +						struct ibmvscsis_tport, se_tpg);
>> +
>> +	return snprintf(page, PAGE_SIZE, "%d\n", (tport->enabled) ? 1 : 0);
>> +}
>
> Have you considered to use MODULE_VERSION() instead of introducing a  
> sysfs attribute to export the module version? An advantage of  
> MODULE_VERSION() is that it allows to query the module version  
> without having to load a kernel module.
>

Done

>> +static void ibmvscsis_modify_std_inquiry(struct se_cmd *se_cmd)
>> +{
>> +	struct se_device *dev = se_cmd->se_dev;
>> +	unsigned char *buf = NULL;
>> +	u32 cmd_len = se_cmd->data_length;
>> +
>> +	if (cmd_len <= INQ_DATA_OFFSET)
>> +		return;
>> +
>> +	buf = transport_kmap_data_sg(se_cmd);
>> +	if (buf) {
>> +		memcpy(&buf[8], "IBM	     ", 8);
>> +		if (dev->transport->get_device_type(dev) == TYPE_ROM)
>> +			memcpy(&buf[16], "VOPTA           ", 16);
>> +		else
>> +			memcpy(&buf[16], "3303      NVDISK", 16);
>> +		memcpy(&buf[32], "0001", 4);
>> +		transport_kunmap_data_sg(se_cmd);
>> +	}
>> +}
>
> Shouldn't a sense code be returned to the initiator if this function fails?
>

This function never fails? Also this is a temporary function that will go away
soon. It's to address AIX not recognizing target emulation of inquiry, but
we have already discussed internally to add these ODM entries into AIX.

>> +	default:
>> +		pr_err("ibmvscsis: unknown task mgmt func %d\n",
>> +						srp_tsk->tsk_mgmt_func);
>> +		cmd->se_cmd.se_tmr_req->response =
>> +					TMR_TASK_MGMT_FUNCTION_NOT_SUPPORTED;
>> +		rc = -1;
>> +		break;
>> +	}
>> +
>> +	if (!rc) {
>
> Please consider changing the above "break" into "goto fail" such  
> that the if (!rc) can be left out and such that the indentation of  
> the code below if (!rc) can be reduced.
>

Done

>> +static int ibmvscsis_queuecommand(struct ibmvscsis_adapter *adapter,
>> +				  struct iu_entry *iue)
>> +{
>> +	struct srp_cmd *cmd = iue->sbuf->buf;
>> +	struct scsi_cmnd *sc;
>> +	struct ibmvscsis_cmnd *vsc;
>> +	int ret;
>> +
>> +	pr_debug("ibmvscsis: ibmvscsis_queuecommand\n");
>> +
>> +	vsc = kzalloc(sizeof(*vsc), GFP_KERNEL);
>
> Allocating memory in the command processing path in a SCSI driver  
> usually causes suboptimal performance. Other LIO target drivers  
> preallocate such buffers before I/O starts.
>

We have a version of this driver that uses preallocation of the buffers.
It's not currently being pushed up for review because it was written by
referencing the internal VIOS VSCSI driver. Once our lawyers have approved
the open-sourcing of the internal driver we will push up patches to address
this issue and add a lot more features.

>> +static uint64_t ibmvscsis_unpack_lun(const uint8_t *lun, int len)
>> +{
>> +	uint64_t res = NO_SUCH_LUN;
>> +	int addressing_method;
>> +
>> +	if (unlikely(len < 2)) {
>> +		pr_err("Illegal LUN length %d, expected 2 bytes or more\n",
>> +			len);
>> +		goto out;
>> +	}
>> +
>> +	switch (len) {
>> +	case 8:
>> +		if ((*((__be64 *)lun) & cpu_to_be64(0x0000FFFFFFFFFFFFLL)) != 0)
>> +			goto out_err;
>> +		break;
>> +	case 4:
>> +		if (*((__be16 *)&lun[2]) != 0)
>> +			goto out_err;
>> +		break;
>> +	case 6:
>> +		if (*((__be32 *)&lun[2]) != 0)
>> +			goto out_err;
>> +		break;
>> +	case 2:
>> +		break;
>> +	default:
>> +		goto out_err;
>> +	}
>> +
>> +	addressing_method = (*lun) >> 6; /* highest two bits of byte 0 */
>> +	switch (addressing_method) {
>> +	case SCSI_LUN_ADDR_METHOD_PERIPHERAL:
>> +	case SCSI_LUN_ADDR_METHOD_FLAT:
>> +	case SCSI_LUN_ADDR_METHOD_LUN:
>> +		res = *(lun + 1) | (((*lun) & 0x3f) << 8);
>> +		break;
>> +
>> +	case SCSI_LUN_ADDR_METHOD_EXTENDED_LUN:
>> +	default:
>> +		pr_err("Unimplemented LUN addressing method %u\n",
>> +			addressing_method);
>> +		break;
>> +	}
>> +
>> +out:
>> +	return res;
>> +out_err:
>> +	pr_err("Support for multi-level LUNs has not yet been implemented\n");
>> +	goto out;
>> +}
>
> In the above function there is nothing that is specific to the VIO  
> mechanism. Please consider to merge this function with  
> scsilun_to_int(), e.g. by introducing a new function that accepts a  
> SCSI LUN and the addressing method as arguments and by making  
> scsilun_to_int() call that function with  
> SCSI_LUN_ADDR_METHOD_PERIPHERAL as one of its arguments.
>

We are going to keep this here atm, until we can make sure canonical  
can pick up
these changes, since we have a deliverable for a 3rd party vendor who  
is going to
fully utilize this driver. Once they can pick it up in 4.4 kernel I  
will make the
change you recommended / move the scsi_lun_addr_method addressed  
below. I will also
address the changes that ib_srpt needs to utilize this new function too.

>> +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];
>> +};
>
> Is this the inquiry data response format from SPC? If so, this is a  
> structure definition that is useful for the SCSI initiator core and  
> also for other LIO target drivers. There might be a more appropriate  
> header file for this definition.
>

It is from the SPC, but it was already defined. So I removed this and
my driver wasn't using it atm anyways.

>> +enum scsi_lun_addr_method {
>> +	SCSI_LUN_ADDR_METHOD_PERIPHERAL   = 0,
>> +	SCSI_LUN_ADDR_METHOD_FLAT         = 1,
>> +	SCSI_LUN_ADDR_METHOD_LUN          = 2,
>> +	SCSI_LUN_ADDR_METHOD_EXTENDED_LUN = 3,
>> +};
>
> Same comment here.
>
Comment from above.

>> diff --git a/drivers/scsi/libsrp.c b/drivers/scsi/libsrp.c
>> new file mode 100644
>> index 0000000..0e32abd
>> --- /dev/null
>> +++ b/drivers/scsi/libsrp.c
>> @@ -0,0 +1,387 @@
>> +/*******************************************************************************
>> + * SCSI RDMA Protocol lib functions
>
> My understanding of the VIO mechanism is that the invocation of  
> H_COPY_RDMA is synchronous. This means that this operation only  
> finishes after the data has been transferred. If I interpret the  
> code in libsrp.c and libsrp.h correctly then this code can only be  
> used by SRP drivers use synchronous data transfers and not by SRP  
> drivers that use asynchronous data transfers. This means that this  
> code is not useful for any of the SRP drivers that are already  
> upstream (ib_srp and ib_srpt). Hence my proposal to move the  
> libsrp.c and libsrp.h source files from drivers/scsi and  
> include/scsi to the same directory as the ibmvscsis driver.
>
Done

Thanks for the reviews thus far, I apologize for some of the styling  
problems that
Joe had pointed out. I didn't run --strict against the patch and had  
missed a lot of
stuff. It's also my first time working with the Linux community, so I  
apologize.

-Bryant


--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Bryant G. Ly May 25, 2016, 2:17 p.m. UTC | #8
This patch series addresses comments by Joe with runing checkpatch with a
--strict. It cleans up all the misc styling and removes all the forward
declarations.

The patch also addresses all of Bart's comments besides the preallocation of
buffers before IO starts and the merging of unpack_lun with scsiluntoint.
Those will come in later patch sets.

[PATCH 2/3] ibmvscsis: Addressing Bart's comments
[PATCH 3/3] ibmvscsis: clean up functions

--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Bart Van Assche June 10, 2016, 7:03 p.m. UTC | #9
On 05/24/2016 01:00 PM, Bryant G Ly wrote:
> Quoting Bart Van Assche <bart.vanassche@sandisk.com>:
>> On 05/24/2016 06:52 AM, Bryant G. Ly wrote:
>>> +static uint64_t ibmvscsis_unpack_lun(const uint8_t *lun, int len)
>>> +{
>>> +    uint64_t res = NO_SUCH_LUN;
>>> +    int addressing_method;
>>> +
>>> +    if (unlikely(len < 2)) {
>>> +        pr_err("Illegal LUN length %d, expected 2 bytes or more\n",
>>> +            len);
>>> +        goto out;
>>> +    }
>>> +
>>> +    switch (len) {
>>> +    case 8:
>>> +        if ((*((__be64 *)lun) & cpu_to_be64(0x0000FFFFFFFFFFFFLL))
>>> != 0)
>>> +            goto out_err;
>>> +        break;
>>> +    case 4:
>>> +        if (*((__be16 *)&lun[2]) != 0)
>>> +            goto out_err;
>>> +        break;
>>> +    case 6:
>>> +        if (*((__be32 *)&lun[2]) != 0)
>>> +            goto out_err;
>>> +        break;
>>> +    case 2:
>>> +        break;
>>> +    default:
>>> +        goto out_err;
>>> +    }
>>> +
>>> +    addressing_method = (*lun) >> 6; /* highest two bits of byte 0 */
>>> +    switch (addressing_method) {
>>> +    case SCSI_LUN_ADDR_METHOD_PERIPHERAL:
>>> +    case SCSI_LUN_ADDR_METHOD_FLAT:
>>> +    case SCSI_LUN_ADDR_METHOD_LUN:
>>> +        res = *(lun + 1) | (((*lun) & 0x3f) << 8);
>>> +        break;
>>> +
>>> +    case SCSI_LUN_ADDR_METHOD_EXTENDED_LUN:
>>> +    default:
>>> +        pr_err("Unimplemented LUN addressing method %u\n",
>>> +            addressing_method);
>>> +        break;
>>> +    }
>>> +
>>> +out:
>>> +    return res;
>>> +out_err:
>>> +    pr_err("Support for multi-level LUNs has not yet been
>>> implemented\n");
>>> +    goto out;
>>> +}
>>
>> In the above function there is nothing that is specific to the VIO
>> mechanism. Please consider to merge this function with
>> scsilun_to_int(), e.g. by introducing a new function that accepts a
>> SCSI LUN and the addressing method as arguments and by making
>> scsilun_to_int() call that function with
>> SCSI_LUN_ADDR_METHOD_PERIPHERAL as one of its arguments.
>>
>
> We are going to keep this here atm, until we can make sure canonical can
> pick up
> these changes, since we have a deliverable for a 3rd party vendor who is
> going to
> fully utilize this driver. Once they can pick it up in 4.4 kernel I will
> make the
> change you recommended / move the scsi_lun_addr_method addressed below.
> I will also
> address the changes that ib_srpt needs to utilize this new function too.

A much better approach would be to move this function into the SCSI core 
and to add a copy of that function to the backported driver that will be 
provided to Canonical.

Thanks,

Bart.
--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Nicholas A. Bellinger June 14, 2016, 6:23 a.m. UTC | #10
On Fri, 2016-06-10 at 12:03 -0700, Bart Van Assche wrote:
> On 05/24/2016 01:00 PM, Bryant G Ly wrote:
> > Quoting Bart Van Assche <bart.vanassche@sandisk.com>:
> >> On 05/24/2016 06:52 AM, Bryant G. Ly wrote:
> >>> +static uint64_t ibmvscsis_unpack_lun(const uint8_t *lun, int len)
> >>> +{
> >>> +    uint64_t res = NO_SUCH_LUN;
> >>> +    int addressing_method;
> >>> +
> >>> +    if (unlikely(len < 2)) {
> >>> +        pr_err("Illegal LUN length %d, expected 2 bytes or more\n",
> >>> +            len);
> >>> +        goto out;
> >>> +    }
> >>> +
> >>> +    switch (len) {
> >>> +    case 8:
> >>> +        if ((*((__be64 *)lun) & cpu_to_be64(0x0000FFFFFFFFFFFFLL))
> >>> != 0)
> >>> +            goto out_err;
> >>> +        break;
> >>> +    case 4:
> >>> +        if (*((__be16 *)&lun[2]) != 0)
> >>> +            goto out_err;
> >>> +        break;
> >>> +    case 6:
> >>> +        if (*((__be32 *)&lun[2]) != 0)
> >>> +            goto out_err;
> >>> +        break;
> >>> +    case 2:
> >>> +        break;
> >>> +    default:
> >>> +        goto out_err;
> >>> +    }
> >>> +
> >>> +    addressing_method = (*lun) >> 6; /* highest two bits of byte 0 */
> >>> +    switch (addressing_method) {
> >>> +    case SCSI_LUN_ADDR_METHOD_PERIPHERAL:
> >>> +    case SCSI_LUN_ADDR_METHOD_FLAT:
> >>> +    case SCSI_LUN_ADDR_METHOD_LUN:
> >>> +        res = *(lun + 1) | (((*lun) & 0x3f) << 8);
> >>> +        break;
> >>> +
> >>> +    case SCSI_LUN_ADDR_METHOD_EXTENDED_LUN:
> >>> +    default:
> >>> +        pr_err("Unimplemented LUN addressing method %u\n",
> >>> +            addressing_method);
> >>> +        break;
> >>> +    }
> >>> +
> >>> +out:
> >>> +    return res;
> >>> +out_err:
> >>> +    pr_err("Support for multi-level LUNs has not yet been
> >>> implemented\n");
> >>> +    goto out;
> >>> +}
> >>
> >> In the above function there is nothing that is specific to the VIO
> >> mechanism. Please consider to merge this function with
> >> scsilun_to_int(), e.g. by introducing a new function that accepts a
> >> SCSI LUN and the addressing method as arguments and by making
> >> scsilun_to_int() call that function with
> >> SCSI_LUN_ADDR_METHOD_PERIPHERAL as one of its arguments.
> >>
> >
> > We are going to keep this here atm, until we can make sure canonical can
> > pick up
> > these changes, since we have a deliverable for a 3rd party vendor who is
> > going to
> > fully utilize this driver. Once they can pick it up in 4.4 kernel I will
> > make the
> > change you recommended / move the scsi_lun_addr_method addressed below.
> > I will also
> > address the changes that ib_srpt needs to utilize this new function too.
> 
> A much better approach would be to move this function into the SCSI core 
> and to add a copy of that function to the backported driver that will be 
> provided to Canonical.

AFAICT, the problem with this improvement is that it's not stable
material, which means that Canonical would have to carry this separately
beyond linux-4.4.y, in order to support ibmvscsi-tgt.

As-is, it looks like we'll have two target-core fixes to support
ibmvscsi-tgt, both of which are considered stable material at this point
and will eventually make it to linux-4.4.y.

That said Bryant, I still don't have an objection for the initial commit
to use it's own internal ibmvscsis_unpack_lun(), but it would be nice in
the patch series containing the initial ibmvscsi-tgt commit to include
an incremental patch to make this common code, as Bart has highlighted.

--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Christoph Hellwig June 14, 2016, 2:55 p.m. UTC | #11
On Mon, Jun 13, 2016 at 11:23:52PM -0700, Nicholas A. Bellinger wrote:
> AFAICT, the problem with this improvement is that it's not stable
> material, which means that Canonical would have to carry this separately
> beyond linux-4.4.y, in order to support ibmvscsi-tgt.

So who cares?  If they have a business interest in backporting the
driver they'll need to figure out a way.  None of that should matter in
any way whatsover for mainline.
--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/MAINTAINERS b/MAINTAINERS
index 6ee06ea..b520e6c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5381,6 +5381,16 @@  S:	Supported
 F:	drivers/scsi/ibmvscsi/ibmvscsi*
 F:	drivers/scsi/ibmvscsi/viosrp.h
 
+IBM Power Virtual SCSI Device Target Driver
+M:	Bryant G. Ly <bryantly@linux.vnet.ibm.com>
+L:	linux-scsi@vger.kernel.org
+L:	target-devel@vger.kernel.org
+S:	Supported
+F:	drivers/scsi/ibmvscsi/ibmvscsis.c
+F:      drivers/scsi/ibmvscsi/ibmvscsis.h
+F:	drivers/scsi/libsrp.h
+F:      drivers/scsi/libsrp.c
+
 IBM Power Virtual FC Device Drivers
 M:	Tyrel Datwyler <tyreld@linux.vnet.ibm.com>
 L:	linux-scsi@vger.kernel.org
diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
index e2f31c9..6adf8f1 100644
--- a/drivers/scsi/Kconfig
+++ b/drivers/scsi/Kconfig
@@ -847,6 +847,20 @@  config SCSI_IBMVSCSI
 	  To compile this driver as a module, choose M here: the
 	  module will be called ibmvscsi.
 
+config SCSI_IBMVSCSIS
+  	tristate "IBM Virtual SCSI Server support"
+  	depends on PPC_PSERIES && SCSI_SRP && TARGET_CORE
+  	help
+  	  This is the IBM POWER Virtual SCSI Target Server
+
+          The userspace component needed to initialize the driver and
+  	  documentation can be found:
+
+          https://github.com/powervm/ibmvscsis
+
+          To compile this driver as a module, choose M here: the
+  	  module will be called ibmvstgt.
+
 config SCSI_IBMVFC
 	tristate "IBM Virtual FC support"
 	depends on PPC_PSERIES && SCSI
@@ -1728,6 +1742,16 @@  config SCSI_PM8001
 	  This driver supports PMC-Sierra PCIE SAS/SATA 8x6G SPC 8001 chip
 	  based host adapters.
 
+config SCSI_SRP
+  	tristate "SCSI RDMA Protocol helper library"
+  	depends on SCSI && PCI
+  	help
+  	  This scsi srp module is a library for ibmvscsi target driver.
+	  If you wish to use SRP target drivers, say Y.
+
+  	  To compile this driver as a module, choose M here. The module will
+  	  be called libsrp.
+
 config SCSI_BFA_FC
 	tristate "Brocade BFA Fibre Channel Support"
 	depends on PCI && SCSI
diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile
index 862ab4e..8692dd4 100644
--- a/drivers/scsi/Makefile
+++ b/drivers/scsi/Makefile
@@ -127,7 +127,9 @@  obj-$(CONFIG_SCSI_LASI700)	+= 53c700.o lasi700.o
 obj-$(CONFIG_SCSI_SNI_53C710)	+= 53c700.o sni_53c710.o
 obj-$(CONFIG_SCSI_NSP32)	+= nsp32.o
 obj-$(CONFIG_SCSI_IPR)		+= ipr.o
+obj-$(CONFIG_SCSI_SRP)          += libsrp.o
 obj-$(CONFIG_SCSI_IBMVSCSI)	+= ibmvscsi/
+obj-$(CONFIG_SCSI_IBMVSCSIS)    += ibmvscsi/
 obj-$(CONFIG_SCSI_IBMVFC)	+= ibmvscsi/
 obj-$(CONFIG_SCSI_HPTIOP)	+= hptiop.o
 obj-$(CONFIG_SCSI_STEX)		+= stex.o
diff --git a/drivers/scsi/ibmvscsi/Makefile b/drivers/scsi/ibmvscsi/Makefile
index 3840c64..b241a567 100644
--- a/drivers/scsi/ibmvscsi/Makefile
+++ b/drivers/scsi/ibmvscsi/Makefile
@@ -1,2 +1,3 @@ 
 obj-$(CONFIG_SCSI_IBMVSCSI)	+= ibmvscsi.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..c7eb347
--- /dev/null
+++ b/drivers/scsi/ibmvscsi/ibmvscsis.c
@@ -0,0 +1,2033 @@ 
+/*******************************************************************************
+ * IBM 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>
+ * Copyright (C) 2016 Bryant G. Ly <bgly@us.ibm.com> IBM Corp.
+ *
+ * Authors: Bryant G. Ly <bryantly@linux.vnet.ibm.com>
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#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_fabric.h>
+#include <target/target_core_backend.h>
+
+#include <asm/hvcall.h>
+#include <asm/iommu.h>
+#include <asm/prom.h>
+#include <asm/vio.h>
+
+#include "ibmvscsi.h"
+#include "ibmvscsis.h"
+#include "viosrp.h"
+
+#define IBMVSCSIS_VERSION	"v0.1"
+
+#define	INITIAL_SRP_LIMIT	15
+#define	DEFAULT_MAX_SECTORS	256
+
+#define MAX_H_COPY_RDMA		(128*1024)
+
+#define SRP_RSP_SENSE_DATA_LEN	18
+
+static struct workqueue_struct *vtgtd;
+static unsigned max_vdma_size = MAX_H_COPY_RDMA;
+
+static DEFINE_SPINLOCK(ibmvscsis_dev_lock);
+static LIST_HEAD(ibmvscsis_dev_list);
+
+static int ibmvscsis_probe(struct vio_dev *vdev,
+			   const struct vio_device_id *id);
+static void ibmvscsis_dev_release(struct device *dev);
+static void ibmvscsis_modify_rep_luns(struct se_cmd *se_cmd);
+static void ibmvscsis_modify_std_inquiry(struct se_cmd *se_cmd);
+static int read_dma_window(struct vio_dev *vdev,
+				struct ibmvscsis_adapter *adapter);
+static char *ibmvscsis_get_fabric_name(void);
+static char *ibmvscsis_get_fabric_wwn(struct se_portal_group *se_tpg);
+static u16 ibmvscsis_get_tag(struct se_portal_group *se_tpg);
+static u32 ibmvscsis_get_default_depth(struct se_portal_group *se_tpg);
+static int ibmvscsis_check_true(struct se_portal_group *se_tpg);
+static int ibmvscsis_check_false(struct se_portal_group *se_tpg);
+static u32 ibmvscsis_tpg_get_inst_index(struct se_portal_group *se_tpg);
+static int ibmvscsis_check_stop_free(struct se_cmd *se_cmd);
+static void ibmvscsis_release_cmd(struct se_cmd *se_cmd);
+static int ibmvscsis_shutdown_session(struct se_session *se_sess);
+static void ibmvscsis_close_session(struct se_session *se_sess);
+static u32 ibmvscsis_sess_get_index(struct se_session *se_sess);
+static int ibmvscsis_write_pending(struct se_cmd *se_cmd);
+static int ibmvscsis_write_pending_status(struct se_cmd *se_cmd);
+static void ibmvscsis_set_default_node_attrs(struct se_node_acl *nacl);
+static int ibmvscsis_get_cmd_state(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 void ibmvscsis_queue_tm_rsp(struct se_cmd *se_cmd);
+static void ibmvscsis_aborted_task(struct se_cmd *se_cmd);
+static struct se_wwn *ibmvscsis_make_tport(struct target_fabric_configfs *tf,
+					   struct config_group *group,
+					   const char *name);
+static void ibmvscsis_drop_tport(struct se_wwn *wwn);
+static struct se_portal_group *ibmvscsis_make_tpg(struct se_wwn *wwn,
+						  struct config_group *group,
+						  const char *name);
+static void ibmvscsis_drop_tpg(struct se_portal_group *se_tpg);
+static int ibmvscsis_remove(struct vio_dev *vdev);
+static ssize_t system_id_show(struct device *dev,
+			      struct device_attribute *attr,
+			      char *buf);
+static ssize_t partition_number_show(struct device *dev,
+				     struct device_attribute *attr,
+				     char *buf);
+static ssize_t unit_address_show(struct device *dev,
+				 struct device_attribute *attr, char *buf);
+static int get_system_info(void);
+static irqreturn_t ibmvscsis_interrupt(int dummy, void *data);
+static int process_srp_iu(struct iu_entry *iue);
+static void process_iu(struct viosrp_crq *crq,
+		       struct ibmvscsis_adapter *adapter);
+static void process_crq(struct viosrp_crq *crq,
+			struct ibmvscsis_adapter *adapter);
+static void handle_crq(struct work_struct *work);
+static int ibmvscsis_reset_crq_queue(struct ibmvscsis_adapter *adapter);
+static void crq_queue_destroy(struct ibmvscsis_adapter *adapter);
+static inline struct viosrp_crq *next_crq(struct crq_queue *queue);
+static int send_iu(struct iu_entry *iue, u64 length, u8 format);
+static int send_adapter_info(struct iu_entry *iue,
+			     dma_addr_t remote_buffer, u16 length);
+static int process_mad_iu(struct iu_entry *iue);
+static void ibmvscsis_srp_i_logout(struct iu_entry *iue);
+static void process_login(struct iu_entry *iue);
+static void process_tsk_mgmt(struct ibmvscsis_adapter *adapter,
+			     struct iu_entry *iue);
+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);
+static int ibmvscsis_queuecommand(struct ibmvscsis_adapter *adapter,
+				  struct iu_entry *iue);
+static uint64_t ibmvscsis_unpack_lun(const uint8_t *lun, int len);
+static int tcm_queuecommand(struct ibmvscsis_adapter *adapter,
+			    struct ibmvscsis_cmnd *vsc,
+			    struct srp_cmd *scmd);
+static void ibmvscsis_determine_resid(struct se_cmd *se_cmd,
+				      struct srp_rsp *rsp);
+static bool connection_broken(struct ibmvscsis_adapter *adapter);
+
+static inline long h_copy_rdma(s64 length, u64 sliobn, u64 slioba,
+	u64 dliobn, u64 dlioba)
+{
+	long rc = 0;
+
+	/* Ensure all writes to source memory are visible before hcall */
+	mb();
+
+	rc = plpar_hcall_norets(H_COPY_RDMA, length, sliobn, slioba,
+			dliobn, dlioba);
+	return rc;
+}
+
+static inline void h_free_crq(uint32_t unit_address)
+{
+	long rc = 0;
+
+	do {
+		if (H_IS_LONG_BUSY(rc))
+			msleep(get_longbusy_msecs(rc));
+
+		rc = plpar_hcall_norets(H_FREE_CRQ, unit_address);
+	} while ((rc == H_BUSY) || (H_IS_LONG_BUSY(rc)));
+}
+
+static inline long h_send_crq(struct ibmvscsis_adapter *adapter,
+			u64 word1, u64 word2)
+{
+	long rc;
+	struct vio_dev *vdev = adapter->dma_dev;
+
+	pr_debug("ibmvscsis: ibmvscsis_send_crq(0x%x, 0x%016llx, 0x%016llx)\n",
+			vdev->unit_address, word1, word2);
+
+	/*
+	 * Ensure the command buffer is flushed to memory before handing it
+	 * over to the other side to prevent it from fetching any stale data.
+	 */
+	mb();
+	rc = plpar_hcall_norets(H_SEND_CRQ, vdev->unit_address, word1, word2);
+	pr_debug("ibmvscsis: ibmvcsis_send_crq rc = 0x%lx\n", rc);
+
+	return rc;
+}
+
+/*****************************************************************************/
+/* Global device driver data areas                                           */
+/*****************************************************************************/
+
+static const char ibmvscsis_driver_name[] = "ibmvscsis";
+static char system_id[64] = "";
+static char partition_name[97] = "UNKNOWN";
+static unsigned int partition_number = -1;
+
+static struct class_attribute ibmvscsis_class_attrs[] = {
+	__ATTR_NULL,
+};
+
+static struct device_attribute dev_attr_system_id =
+	__ATTR(system_id, S_IRUGO, system_id_show, NULL);
+
+static struct device_attribute dev_attr_partition_number =
+	__ATTR(partition_number, S_IRUGO, partition_number_show, NULL);
+
+static struct device_attribute dev_attr_unit_address =
+	__ATTR(unit_address, S_IRUGO, unit_address_show, NULL);
+
+static struct attribute *ibmvscsis_dev_attrs[] = {
+	&dev_attr_system_id.attr,
+	&dev_attr_partition_number.attr,
+	&dev_attr_unit_address.attr,
+};
+ATTRIBUTE_GROUPS(ibmvscsis_dev);
+
+static struct class ibmvscsis_class = {
+	.name           = "ibmvscsis",
+	.dev_release    = ibmvscsis_dev_release,
+	.class_attrs    = ibmvscsis_class_attrs,
+	.dev_groups     = ibmvscsis_dev_groups,
+};
+
+static ssize_t ibmvscsis_wwn_version_show(struct config_item *item,
+					       char *page)
+{
+	return sprintf(page, "IBMVSCSIS fabric %s on %s/%s on "UTS_RELEASE"\n",
+		       IBMVSCSIS_VERSION, utsname()->sysname,
+		       utsname()->machine);
+}
+CONFIGFS_ATTR_RO(ibmvscsis_wwn_, version);
+
+static struct configfs_attribute *ibmvscsis_wwn_attrs[] = {
+	&ibmvscsis_wwn_attr_version,
+	NULL,
+};
+
+static ssize_t ibmvscsis_tpg_enable_show(struct config_item *item,
+				char *page)
+{
+	struct se_portal_group *se_tpg = to_tpg(item);
+	struct ibmvscsis_tport *tport = container_of(se_tpg,
+						struct ibmvscsis_tport, se_tpg);
+
+	return snprintf(page, PAGE_SIZE, "%d\n", (tport->enabled) ? 1 : 0);
+}
+
+static ssize_t ibmvscsis_tpg_enable_store(struct config_item *item,
+		const char *page, size_t count)
+{
+	struct se_portal_group *se_tpg = to_tpg(item);
+	struct ibmvscsis_tport *tport = container_of(se_tpg,
+						struct ibmvscsis_tport, se_tpg);
+	unsigned long tmp;
+	int ret;
+
+	ret = kstrtoul(page, 0, &tmp);
+	if (ret < 0) {
+		pr_err("Unable to extract ibmvscsis_tpg_store_enable\n");
+		return -EINVAL;
+	}
+
+	if ((tmp != 0) && (tmp != 1)) {
+		pr_err("Illegal value for ibmvscsis_tpg_store_enable: %lu\n",
+			tmp);
+		return -EINVAL;
+	}
+
+	if (tmp == 1)
+		tport->enabled = true;
+	else
+		tport->enabled = false;
+
+	return count;
+}
+CONFIGFS_ATTR(ibmvscsis_tpg_, enable);
+
+static struct configfs_attribute *ibmvscsis_tpg_attrs[] = {
+			&ibmvscsis_tpg_attr_enable,
+			NULL,
+};
+
+static const struct target_core_fabric_ops ibmvscsis_ops = {
+	.module				= THIS_MODULE,
+	.name				= "ibmvscsis",
+	.max_data_sg_nents		= SCSI_MAX_SG_SEGMENTS,
+	.get_fabric_name		= ibmvscsis_get_fabric_name,
+	.tpg_get_wwn			= ibmvscsis_get_fabric_wwn,
+	.tpg_get_tag			= ibmvscsis_get_tag,
+	.tpg_get_default_depth		= ibmvscsis_get_default_depth,
+	.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_get_inst_index		= ibmvscsis_tpg_get_inst_index,
+	.check_stop_free		= ibmvscsis_check_stop_free,
+	.release_cmd			= ibmvscsis_release_cmd,
+	.shutdown_session		= ibmvscsis_shutdown_session,
+	.close_session			= ibmvscsis_close_session,
+	.sess_get_index			= ibmvscsis_sess_get_index,
+	.write_pending			= ibmvscsis_write_pending,
+	.write_pending_status		= ibmvscsis_write_pending_status,
+	.set_default_node_attributes	= ibmvscsis_set_default_node_attrs,
+	.get_cmd_state			= ibmvscsis_get_cmd_state,
+	.queue_data_in			= ibmvscsis_queue_data_in,
+	.queue_status			= ibmvscsis_queue_status,
+	.queue_tm_rsp			= ibmvscsis_queue_tm_rsp,
+	.aborted_task			= ibmvscsis_aborted_task,
+	/*
+	 * Setup function pointers for logic in target_cor_fabric_configfs.c
+	 */
+	.fabric_make_wwn		= ibmvscsis_make_tport,
+	.fabric_drop_wwn		= ibmvscsis_drop_tport,
+	.fabric_make_tpg		= ibmvscsis_make_tpg,
+	.fabric_drop_tpg		= ibmvscsis_drop_tpg,
+
+	.tfc_wwn_attrs			= ibmvscsis_wwn_attrs,
+	.tfc_tpg_base_attrs             = ibmvscsis_tpg_attrs,
+};
+
+static struct vio_device_id ibmvscsis_device_table[] = {
+	{"v-scsi-host", "IBM,v-scsi-host"},
+	{"", ""}
+};
+MODULE_DEVICE_TABLE(vio, ibmvscsis_device_table);
+
+static struct vio_driver ibmvscsis_driver = {
+	.name = ibmvscsis_driver_name,
+	.id_table = ibmvscsis_device_table,
+	.probe = ibmvscsis_probe,
+	.remove = ibmvscsis_remove,
+};
+
+/*****************************************************************************/
+/* End of global device driver data areas                                    */
+/*****************************************************************************/
+static int crq_queue_create(struct crq_queue *queue,
+				struct ibmvscsis_adapter *adapter)
+{
+	int retrc;
+	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;
+
+	retrc = 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)
+		err = ibmvscsis_reset_crq_queue(adapter);
+	if (err == 2) {
+		pr_warn("ibmvscsis: Partner adapter not ready\n");
+		retrc = 0;
+	} else if (err != 0) {
+		pr_err("ibmvscsis: Error 0x%x opening virtual adapter\n", err);
+		goto reg_crq_failed;
+	}
+
+	queue->cur = 0;
+	spin_lock_init(&queue->lock);
+
+	INIT_WORK(&adapter->crq_work, handle_crq);
+
+	err = request_irq(vdev->irq, &ibmvscsis_interrupt,
+			  0, "ibmvscsis", adapter);
+	if (err) {
+		pr_err("ibmvscsis: Error 0x%x h_send_crq\n", err);
+		goto req_irq_failed;
+	}
+
+	err = vio_enable_interrupts(vdev);
+	if (err != 0) {
+		pr_err("ibmvscsis: Error %d enabling interrupts!!!\n", err);
+		goto req_irq_failed;
+	}
+
+	return retrc;
+
+req_irq_failed:
+	h_free_crq(vdev->unit_address);
+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 -1;
+}
+
+/*
+ * ibmvscsis_probe - ibm vscsis target initialize entry point
+ * @param  dev vio device struct
+ * @param  id  vio device id struct
+ * @return	0 - Success
+ *		Non-zero - Failure
+ */
+static int ibmvscsis_probe(struct vio_dev *vdev, const struct vio_device_id *id)
+{
+	int ret = -ENOMEM;
+	struct ibmvscsis_adapter *adapter;
+	struct srp_target *target;
+	struct ibmvscsis_tport *tport;
+	unsigned long flags;
+
+	pr_debug("ibmvscsis: Probe for UA 0x%x\n", vdev->unit_address);
+
+	adapter = kzalloc(sizeof(struct ibmvscsis_adapter), GFP_KERNEL);
+	if (!adapter)
+		return ret;
+	target = kzalloc(sizeof(struct srp_target), GFP_KERNEL);
+	if (!target)
+		goto free_adapter;
+
+	adapter->dma_dev = vdev;
+	adapter->target = target;
+	tport = &adapter->tport;
+
+	tport->enabled = false;
+	snprintf(&adapter->tport.tport_name[0], 256, "%s",
+		 dev_name(&vdev->dev));
+
+	ret = read_dma_window(adapter->dma_dev, adapter);
+	if (ret != 0)
+		goto free_target;
+
+	pr_debug("ibmvscsis: Probe: liobn 0x%x, riobn 0x%x\n", adapter->liobn,
+			adapter->riobn);
+
+	spin_lock_irqsave(&ibmvscsis_dev_lock, flags);
+	list_add_tail(&adapter->list, &ibmvscsis_dev_list);
+	spin_unlock_irqrestore(&ibmvscsis_dev_lock, flags);
+
+	ret = srp_target_alloc(target, &vdev->dev,
+				INITIAL_SRP_LIMIT,
+				SRP_MAX_IU_LEN);
+
+	adapter->target->ldata = adapter;
+
+	if (ret) {
+		pr_err("ibmvscsis: failed target alloc ret: %d\n", ret);
+		goto free_srp_target;
+	}
+
+	ret = crq_queue_create(&adapter->crq_queue, adapter);
+	if (ret != 0 && ret != H_RESOURCE) {
+		pr_err("ibmvscsis: failed crq_queue_create ret: %d\n", ret);
+		ret = -1;
+	}
+
+	if (h_send_crq(adapter, 0xC001000000000000LL, 0) != 0
+			&& ret != H_RESOURCE) {
+		pr_warn("ibmvscsis: Failed to send CRQ message\n");
+		ret = 0;
+	}
+
+	dev_set_drvdata(&vdev->dev, adapter);
+
+	return 0;
+
+free_srp_target:
+	srp_target_free(target);
+free_target:
+	kfree(target);
+free_adapter:
+	kfree(adapter);
+	return ret;
+}
+
+static int ibmvscsis_remove(struct vio_dev *dev)
+{
+	unsigned long flags;
+	struct ibmvscsis_adapter *adapter = dev_get_drvdata(&dev->dev);
+	struct srp_target *target;
+
+	target = adapter->target;
+
+	spin_lock_irqsave(&ibmvscsis_dev_lock, flags);
+	list_del(&adapter->list);
+	spin_unlock_irqrestore(&ibmvscsis_dev_lock, flags);
+
+	crq_queue_destroy(adapter);
+	srp_target_free(target);
+
+	kfree(target);
+	kfree(adapter);
+
+	return 0;
+}
+
+static void ibmvscsis_modify_rep_luns(struct se_cmd *se_cmd)
+{
+	s32 len = se_cmd->data_length;
+	u16 data_len;
+	unsigned char *buf = NULL;
+
+	if (len <= 8)
+		return;
+
+	len -= 8;
+	buf = transport_kmap_data_sg(se_cmd);
+	if (buf) {
+		data_len = be32_to_cpu(*(u32 *)buf);
+		pr_debug("ibmvscsis: modify_rep_luns: len %d data_len %hud\n",
+			len, data_len);
+		if (data_len < len)
+			len = data_len;
+		buf += 8;
+		while (len > 0) {
+			*buf |= SCSI_LUN_ADDR_METHOD_FLAT << 6;
+			len -= 8;
+			buf += 8;
+		}
+		transport_kunmap_data_sg(se_cmd);
+	}
+}
+
+static void ibmvscsis_modify_std_inquiry(struct se_cmd *se_cmd)
+{
+	struct se_device *dev = se_cmd->se_dev;
+	unsigned char *buf = NULL;
+	u32 cmd_len = se_cmd->data_length;
+
+	if (cmd_len <= INQ_DATA_OFFSET)
+		return;
+
+	buf = transport_kmap_data_sg(se_cmd);
+	if (buf) {
+		memcpy(&buf[8], "IBM	     ", 8);
+		if (dev->transport->get_device_type(dev) == TYPE_ROM)
+			memcpy(&buf[16], "VOPTA           ", 16);
+		else
+			memcpy(&buf[16], "3303      NVDISK", 16);
+		memcpy(&buf[32], "0001", 4);
+		transport_kunmap_data_sg(se_cmd);
+	}
+}
+
+static int read_dma_window(struct vio_dev *vdev,
+				struct ibmvscsis_adapter *adapter)
+{
+	const __be32 *dma_window;
+	const __be32 *prop;
+
+	/* TODO Using of_parse_dma_window would be better, but it doesn't give
+	 * a way to read multiple windows without already knowing the size of
+	 * a window or the number of windows
+	 */
+	dma_window =
+		(const __be32 *)vio_get_attribute(vdev, "ibm,my-dma-window",
+						NULL);
+	if (!dma_window) {
+		pr_err("ibmvscsis: Couldn't find ibm,my-dma-window property\n");
+		return -1;
+	}
+
+	adapter->liobn = be32_to_cpu(*dma_window);
+	dma_window++;
+
+	prop = (const __be32 *)vio_get_attribute(vdev, "ibm,#dma-address-cells",
+						NULL);
+	if (!prop) {
+		pr_warn("ibmvscsis: Couldn't find ibm, #dma-address-cells property\n");
+		dma_window++;
+	} else
+		dma_window += be32_to_cpu(*prop);
+
+	prop = (const __be32 *)vio_get_attribute(vdev, "ibm,#dma-size-cells",
+						NULL);
+	if (!prop) {
+		pr_warn("ibmvscsis: Couldn't find ibm, #dma-size-cells property\n");
+		dma_window++;
+	} else
+		dma_window += be32_to_cpu(*prop);
+
+	/* dma_window should point to the second window now */
+	adapter->riobn = be32_to_cpu(*dma_window);
+
+	return 0;
+}
+
+static void ibmvscsis_dev_release(struct device *dev) {};
+
+static char *ibmvscsis_get_fabric_name(void)
+{
+	return "ibmvscsis";
+}
+
+static char *ibmvscsis_get_fabric_wwn(struct se_portal_group *se_tpg)
+{
+	struct ibmvscsis_tport *tport =
+		container_of(se_tpg, struct ibmvscsis_tport, se_tpg);
+
+	return &tport->tport_name[0];
+}
+
+static u16 ibmvscsis_get_tag(struct se_portal_group *se_tpg)
+{
+	struct ibmvscsis_tport *tport =
+		container_of(se_tpg, struct ibmvscsis_tport, se_tpg);
+
+	return tport->tport_tpgt;
+}
+
+static u32 ibmvscsis_get_default_depth(struct se_portal_group *se_tpg)
+{
+	return 1;
+}
+
+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 u32 ibmvscsis_tpg_get_inst_index(struct se_portal_group *se_tpg)
+{
+	return 1;
+}
+
+static int ibmvscsis_check_stop_free(struct se_cmd *se_cmd)
+{
+	struct ibmvscsis_cmnd *cmd = container_of(se_cmd,
+			struct ibmvscsis_cmnd, se_cmd);
+
+	return target_put_sess_cmd(&cmd->se_cmd);
+}
+
+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);
+}
+
+static int ibmvscsis_shutdown_session(struct se_session *se_sess)
+{
+	return 0;
+}
+
+static void ibmvscsis_close_session(struct se_session *se_sess)
+{
+}
+
+static u32 ibmvscsis_sess_get_index(struct se_session *se_sess)
+{
+	return 0;
+}
+
+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;
+
+	pr_debug("ibmvscsis: ibmvscsis_write_pending\n");
+	sc->sdb.length = se_cmd->data_length;
+	sc->sdb.table.nents = se_cmd->t_data_nents;
+	sc->sdb.table.sgl = se_cmd->t_data_sg;
+
+	ret = srp_transfer_data(sc, &vio_iu(iue)->srp.cmd,
+				ibmvscsis_rdma, 1, 1);
+	if (ret) {
+		pr_err("ibmvscsis: srp_transfer_data() failed: %d\n", ret);
+		return -EAGAIN;
+	}
+	/*
+	 * We now tell TCM to add this WRITE CDB directly into the TCM storage
+	 * object execution queue.
+	 */
+	target_execute_cmd(&cmd->se_cmd);
+	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)
+{
+}
+
+static int ibmvscsis_get_cmd_state(struct se_cmd *se_cmd)
+{
+	return 0;
+}
+
+static void ibmvscsis_determine_resid(struct se_cmd *se_cmd,
+				      struct srp_rsp *rsp)
+{
+	if (se_cmd->residual_count) {
+		if (se_cmd->se_cmd_flags & SCF_UNDERFLOW_BIT) {
+			if (se_cmd->data_direction == DMA_TO_DEVICE) {
+				/* residual data from an underflow write */
+				rsp->flags = SRP_RSP_FLAG_DOUNDER;
+				rsp->data_out_res_cnt =
+					cpu_to_be32(se_cmd->residual_count);
+			} else if (se_cmd->data_direction == DMA_FROM_DEVICE) {
+				/* residual data from an underflow read */
+				rsp->flags = SRP_RSP_FLAG_DIUNDER;
+				rsp->data_in_res_cnt =
+					cpu_to_be32(se_cmd->residual_count);
+			}
+		} else if (se_cmd->se_cmd_flags & SCF_OVERFLOW_BIT) {
+			if (se_cmd->data_direction == DMA_TO_DEVICE) {
+				/*  residual data from an overflow write */
+				rsp->flags = SRP_RSP_FLAG_DOOVER;
+				rsp->data_out_res_cnt =
+					cpu_to_be32(se_cmd->residual_count);
+			} else if (se_cmd->data_direction ==
+				   DMA_FROM_DEVICE) {
+				/* residual data from an overflow read */
+				rsp->flags = SRP_RSP_FLAG_DIOVER;
+				rsp->data_in_res_cnt =
+					cpu_to_be32(se_cmd->residual_count);
+			}
+		}
+	}
+}
+
+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;
+	struct iu_entry *iue = (struct iu_entry *)sc->SCp.ptr;
+	struct srp_cmd *srp = (struct srp_cmd *)iue->sbuf->buf;
+	struct srp_rsp *rsp;
+	char *sd;
+	char *data;
+	int ret;
+	uint len;
+
+	struct srp_target *target = iue->target;
+	struct ibmvscsis_adapter *adapter = target->ldata;
+
+	/*
+	 * Check for overflow residual count
+	 */
+	pr_debug("ibmvscsis: ibmvscsis_queue_data_in\n");
+
+	if (se_cmd->se_cmd_flags & SCF_OVERFLOW_BIT)
+		scsi_set_resid(sc, se_cmd->residual_count);
+
+	sc->sdb.length = se_cmd->data_length;
+	sc->sdb.table.nents = se_cmd->t_data_nents;
+	sc->sdb.table.sgl = se_cmd->t_data_sg;
+
+	if (scsi_sg_count(sc)) {
+		if (srp->cdb[0] == REPORT_LUNS &&
+					adapter->client_data.os_type != LINUX)
+			ibmvscsis_modify_rep_luns(se_cmd);
+		if ((srp->cdb[0] == INQUIRY) && ((srp->cdb[1] & 0x1) == 0))
+			ibmvscsis_modify_std_inquiry(se_cmd);
+		ret = srp_transfer_data(sc, &vio_iu(iue)->srp.cmd,
+					ibmvscsis_rdma, 1, 1);
+		if (ret) {
+			pr_err("ibmvscsis: srp_transfer_data failed: %d\n",
+				ret);
+			sd = cmd->se_cmd.sense_buffer;
+			cmd->se_cmd.scsi_sense_length = 18;
+			memset(cmd->se_cmd.sense_buffer, 0,
+				cmd->se_cmd.scsi_sense_length);
+			sd[0] = 0x70;
+			sd[2] = 3;
+			sd[7] = 10;
+			sd[12] = 8;
+			sd[13] = 1;
+		}
+	}
+
+	rsp = &vio_iu(iue)->srp.rsp;
+	len = sizeof(*rsp);
+	memset(rsp, 0, len);
+	data = rsp->data;
+
+	rsp->tag = se_cmd->tag;
+	rsp->req_lim_delta = cpu_to_be32(1);
+	rsp->opcode = SRP_RSP;
+
+	ibmvscsis_determine_resid(se_cmd, rsp);
+	rsp->status = se_cmd->scsi_status;
+
+	if (se_cmd->scsi_sense_length && se_cmd->sense_buffer) {
+		rsp->sense_data_len = cpu_to_be32(se_cmd->scsi_sense_length);
+		rsp->flags |= SRP_RSP_FLAG_SNSVALID;
+		len += se_cmd->scsi_sense_length;
+		memcpy(data, se_cmd->sense_buffer, se_cmd->scsi_sense_length);
+	}
+
+	send_iu(iue, len, VIOSRP_SRP_FORMAT);
+	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;
+	struct iu_entry *iue = (struct iu_entry *) sc->SCp.ptr;
+	struct srp_rsp *rsp;
+	uint len;
+	char *data;
+
+	/*
+	 * Copy any generated SENSE data into sc->sense_buffer and
+	 * set the appropriate sc->result to be translated by
+	 * ibmvscsis_cmnd_done()
+	 */
+	pr_debug("ibmvscsis: ibmvscsis_queue_status\n");
+
+	rsp = &vio_iu(iue)->srp.rsp;
+	len = sizeof(*rsp);
+	memset(rsp, 0, len);
+	data = rsp->data;
+
+	rsp->tag = se_cmd->tag;
+	rsp->req_lim_delta = cpu_to_be32(1);
+	rsp->opcode = SRP_RSP;
+
+	ibmvscsis_determine_resid(se_cmd, rsp);
+	rsp->status = se_cmd->scsi_status;
+
+	if (se_cmd->scsi_sense_length && se_cmd->sense_buffer) {
+		rsp->sense_data_len = cpu_to_be32(se_cmd->scsi_sense_length);
+		rsp->flags |= SRP_RSP_FLAG_SNSVALID;
+		len += se_cmd->scsi_sense_length;
+		memcpy(data, se_cmd->sense_buffer, se_cmd->scsi_sense_length);
+	}
+	send_iu(iue, len, VIOSRP_SRP_FORMAT);
+	return 0;
+}
+
+static void ibmvscsis_queue_tm_rsp(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_target *target = iue->target;
+	struct ibmvscsis_adapter *adapter = target->ldata;
+	struct srp_rsp *rsp;
+	uint len;
+	char *data;
+	u32 *tsk_status;
+	u32 rsp_code;
+
+	pr_debug("ibmvscsis: ibmvscsis_queue_tm_rsp\n");
+	rsp = &vio_iu(iue)->srp.rsp;
+
+	if (transport_check_aborted_status(se_cmd, false) != 0) {
+		pr_debug("ibmvscsis: queue_tm_rsp aborted\n");
+		atomic_inc(&adapter->req_lim_delta);
+		srp_iu_put(iue);
+	} else {
+		rsp->req_lim_delta = cpu_to_be32(1
+				+ atomic_xchg(&adapter->req_lim_delta, 0));
+	}
+
+	len = sizeof(*rsp);
+	memset(rsp, 0, len);
+	data = rsp->data;
+
+	rsp->opcode = SRP_RSP;
+	rsp->tag = se_cmd->se_tmr_req->ref_task_tag;
+	rsp->status = 0;
+	rsp->resp_data_len = cpu_to_be32(4);
+	rsp->flags |= SRP_RSP_FLAG_RSPVALID;
+	rsp->req_lim_delta = cpu_to_be32(1);
+
+	switch (se_cmd->se_tmr_req->response) {
+	case TMR_FUNCTION_COMPLETE:
+	case TMR_TASK_DOES_NOT_EXIST:
+		rsp_code = SRP_TASK_MANAGEMENT_FUNCTION_COMPLETE;
+		break;
+	case TMR_TASK_MGMT_FUNCTION_NOT_SUPPORTED:
+	case TMR_LUN_DOES_NOT_EXIST:
+		rsp_code = SRP_TASK_MANAGEMENT_FUNCTION_NOT_SUPPORTED;
+		break;
+	case TMR_FUNCTION_FAILED:
+	case TMR_FUNCTION_REJECTED:
+	default:
+		rsp_code = SRP_TASK_MANAGEMENT_FUNCTION_FAILED;
+		break;
+	}
+
+	tsk_status = (u32 *)data;
+	*tsk_status = cpu_to_be32(rsp_code);
+	data = (char *)(tsk_status + 1);
+	len += 4;
+
+	send_iu(iue, len, VIOSRP_SRP_FORMAT);
+}
+
+static void ibmvscsis_aborted_task(struct se_cmd *se_cmd)
+{
+}
+
+static struct se_portal_group *ibmvscsis_make_nexus(
+				struct ibmvscsis_tport *tport,
+				const char *name)
+{
+	struct se_node_acl *acl;
+
+	pr_debug("ibmvscsis: make nexus");
+	if (tport->se_sess) {
+		pr_debug("tport->se_sess already exists\n");
+		return &tport->se_tpg;
+	}
+
+	/*
+	 *  Initialize the struct se_session pointer and setup tagpool
+	 *  for struct ibmvscsis_cmd descriptors
+	 */
+	tport->se_sess = transport_init_session(TARGET_PROT_NORMAL);
+	if (IS_ERR(tport->se_sess))
+		goto transport_init_fail;
+
+	/*
+	 * Since we are running in 'demo mode' this call will generate a
+	 * struct se_node_acl for the ibmvscsis struct se_portal_group with
+	 * the SCSI Initiator port name of the passed configfs group 'name'.
+	 */
+
+	acl = core_tpg_check_initiator_node_acl(&tport->se_tpg,
+				(unsigned char *)name);
+	if (!acl) {
+		pr_debug("core_tpg_check_initiator_node_acl() failed for %s\n",
+			name);
+		goto acl_failed;
+	}
+	tport->se_sess->se_node_acl = acl;
+
+	/*
+	 * Now register the TCM ibmvscsis virtual I_T Nexus as active.
+	 */
+	transport_register_session(&tport->se_tpg,
+					tport->se_sess->se_node_acl,
+					tport->se_sess, tport);
+
+	tport->se_sess->se_tpg = &tport->se_tpg;
+
+	return &tport->se_tpg;
+
+acl_failed:
+	transport_free_session(tport->se_sess);
+transport_init_fail:
+	kfree(tport);
+	return ERR_PTR(-ENOMEM);
+}
+
+static int ibmvscsis_drop_nexus(struct ibmvscsis_tport *tport)
+{
+	struct se_session *se_sess;
+
+	se_sess = tport->se_sess;
+	if (!se_sess)
+		return -ENODEV;
+
+	transport_deregister_session(tport->se_sess);
+	transport_free_session(tport->se_sess);
+	return 0;
+}
+
+static struct ibmvscsis_tport *ibmvscsis_lookup_port(const char *name)
+{
+	struct ibmvscsis_tport *tport;
+	struct vio_dev *vdev;
+	struct ibmvscsis_adapter *adapter;
+	int ret;
+	unsigned long flags;
+
+	spin_lock_irqsave(&ibmvscsis_dev_lock, flags);
+	list_for_each_entry(adapter, &ibmvscsis_dev_list, list) {
+		vdev = adapter->dma_dev;
+		ret = strcmp(dev_name(&vdev->dev), name);
+		if (ret == 0)
+			tport = &adapter->tport;
+		if (tport)
+			goto found;
+	}
+	spin_unlock_irqrestore(&ibmvscsis_dev_lock, flags);
+	return NULL;
+found:
+	spin_unlock_irqrestore(&ibmvscsis_dev_lock, flags);
+	return tport;
+}
+
+static struct se_wwn *ibmvscsis_make_tport(struct target_fabric_configfs *tf,
+					   struct config_group *group,
+					   const char *name)
+{
+	struct ibmvscsis_tport *tport;
+	int ret;
+
+	tport = ibmvscsis_lookup_port(name);
+	ret = -EINVAL;
+
+	if (!tport)
+		goto err;
+
+	tport->tport_proto_id = SCSI_PROTOCOL_SRP;
+	pr_debug("ibmvscsis: make_tport(%s), pointer:%p tport_id:%x\n", name,
+					tport, tport->tport_proto_id);
+
+	return &tport->tport_wwn;
+err:
+	return ERR_PTR(ret);
+}
+
+static void ibmvscsis_drop_tport(struct se_wwn *wwn)
+{
+	struct ibmvscsis_tport *tport = container_of(wwn,
+				struct ibmvscsis_tport, tport_wwn);
+
+	pr_debug("drop_tport(%s\n",
+		config_item_name(&tport->tport_wwn.wwn_group.cg_item));
+}
+
+static struct se_portal_group *ibmvscsis_make_tpg(struct se_wwn *wwn,
+						  struct config_group *group,
+						  const char *name)
+{
+	struct ibmvscsis_tport *tport =
+		container_of(wwn, struct ibmvscsis_tport, tport_wwn);
+	int ret;
+
+	tport->releasing = false;
+
+	ret = core_tpg_register(&tport->tport_wwn,
+				&tport->se_tpg,
+				tport->tport_proto_id);
+	if (ret)
+		return ERR_PTR(ret);
+
+	return &tport->se_tpg;
+}
+
+static void ibmvscsis_drop_tpg(struct se_portal_group *se_tpg)
+{
+	struct ibmvscsis_tport *tport = container_of(se_tpg,
+				struct ibmvscsis_tport, se_tpg);
+
+	tport->releasing = true;
+	tport->enabled = false;
+
+	/*
+	 * Release the virtual I_T Nexus for this ibmvscsis TPG
+	 */
+	ibmvscsis_drop_nexus(tport);
+	/*
+	 * Deregister the se_tpg from TCM..
+	 */
+	core_tpg_deregister(se_tpg);
+}
+
+static ssize_t system_id_show(struct device *dev,
+			      struct device_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n", system_id);
+}
+
+static ssize_t partition_number_show(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%x\n", partition_number);
+}
+
+static ssize_t unit_address_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	struct ibmvscsis_adapter *adapter =
+			container_of(dev, struct ibmvscsis_adapter, dev);
+
+	return snprintf(buf, PAGE_SIZE, "%x\n", adapter->dma_dev->unit_address);
+}
+
+static int get_system_info(void)
+{
+	struct device_node *rootdn, *vdevdn;
+	const char *id, *model, *name;
+	const unsigned int *num;
+
+	pr_debug("ibmvscsis: getsysteminfo");
+	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 = of_read_number(num, 1);
+
+	of_node_put(rootdn);
+
+	vdevdn = of_find_node_by_path("/vdevice");
+	vdevdn = of_find_node_by_path("/vdevice");
+	if (vdevdn) {
+		const unsigned *mvds;
+
+		mvds = of_get_property(vdevdn, "ibm,max-virtual-dma-size",
+				       NULL);
+		if (mvds)
+			max_vdma_size = *mvds;
+		of_node_put(vdevdn);
+	}
+
+	return 0;
+};
+
+static irqreturn_t ibmvscsis_interrupt(int dummy, void *data)
+{
+	struct ibmvscsis_adapter *adapter = data;
+
+	pr_debug("ibmvscsis: there is an interrupt\n");
+	vio_disable_interrupts(adapter->dma_dev);
+	queue_work(vtgtd, &adapter->crq_work);
+
+	return IRQ_HANDLED;
+}
+
+static int process_srp_iu(struct iu_entry *iue)
+{
+	union viosrp_iu *iu = vio_iu(iue);
+	struct srp_target *target = iue->target;
+	struct ibmvscsis_adapter *adapter = target->ldata;
+	u8 opcode = iu->srp.rsp.opcode;
+	unsigned long flags;
+	int err = 1;
+
+	spin_lock_irqsave(&target->lock, flags);
+	if (adapter->tport.releasing == true) {
+		pr_err("ibmvscsis: process_srp_iu error, tport is released:%x\n",
+			adapter->tport.releasing);
+		goto done;
+	}
+	if (adapter->tport.enabled == false) {
+		pr_err("ibmvscsis: process_srp_iu, tport not enabled:%x\n",
+			adapter->tport.enabled);
+		goto done;
+	}
+	spin_unlock_irqrestore(&target->lock, flags);
+
+	switch (opcode) {
+	case SRP_LOGIN_REQ:
+		process_login(iue);
+		break;
+	case SRP_TSK_MGMT:
+		process_tsk_mgmt(adapter, iue);
+		break;
+	case SRP_CMD:
+		err = ibmvscsis_queuecommand(adapter, iue);
+		if (err) {
+			srp_iu_put(iue);
+			pr_err("ibmvscsis: can't queue cmd\n");
+		}
+		break;
+	case SRP_LOGIN_RSP:
+	case SRP_I_LOGOUT:
+		ibmvscsis_srp_i_logout(iue);
+		break;
+	case SRP_T_LOGOUT:
+	case SRP_RSP:
+	case SRP_CRED_REQ:
+	case SRP_CRED_RSP:
+	case SRP_AER_REQ:
+	case SRP_AER_RSP:
+		pr_err("ibmvscsis: Unsupported type %u\n", opcode);
+		break;
+	default:
+		pr_err("ibmvscsis: Unknown type %u\n", opcode);
+	}
+	return err;
+
+done:
+	spin_unlock_irqrestore(&target->lock, flags);
+	srp_iu_put(iue);
+	return err;
+}
+
+static void process_iu(struct viosrp_crq *crq,
+		       struct ibmvscsis_adapter *adapter)
+{
+	struct iu_entry *iue;
+	long err;
+
+	iue = srp_iu_get(adapter->target);
+	if (!iue) {
+		pr_err("ibmvscsis: Error getting IU from pool %p\n", iue);
+		return;
+	}
+
+	iue->remote_token = crq->IU_data_ptr;
+
+	err = h_copy_rdma(be16_to_cpu(crq->IU_length), adapter->riobn,
+				be64_to_cpu(crq->IU_data_ptr),
+				adapter->liobn, iue->sbuf->dma);
+
+	switch (err) {
+	case H_SUCCESS:
+		break;
+	case H_PERMISSION:
+	case H_SOURCE_PARM:
+	case H_DEST_PARM:
+		if (connection_broken(adapter))
+			pr_debug("ibmvscsis: rdma connection broken\n");
+	default:
+		pr_err("ibmvscsis: process iu error\n");
+		break;
+	}
+
+	if (crq->format == VIOSRP_MAD_FORMAT)
+		process_mad_iu(iue);
+	else {
+		pr_debug("ibmvscsis: process srpiu");
+		process_srp_iu(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, 0xC002000000000000, 0);
+			break;
+		case 0x02:
+			break;
+		default:
+			pr_err("ibmvscsis: Unknown format %u\n", crq->format);
+		}
+		break;
+	case 0xFF:
+		/* transport event */
+		switch (crq->format) {
+		case MIGRATED:
+		case PARTNER_FAILED:
+		case PARTNER_DEREGISTER:
+			adapter->client_data.os_type = 0;
+			pr_debug("ibmvscsis (%s):trans_event:good format %d\n",
+			dev_name(&adapter->dma_dev->dev), (uint)crq->format);
+			break;
+		default:
+			pr_err("ibmvscsis (%s):trans_event:invalid format %d\n",
+			dev_name(&adapter->dma_dev->dev), (uint)crq->format);
+		}
+		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:
+			pr_err("ibmvscsis: Unsupported format %u\n",
+					crq->format);
+			break;
+		default:
+			pr_err("ibmvscsis: Unknown format %u\n",
+					crq->format);
+		}
+		break;
+	default:
+		pr_err("ibmvscsis: unknown message type 0x%02x!?\n",
+				crq->valid);
+	}
+}
+
+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;
+	}
+}
+
+static int ibmvscsis_reset_crq_queue(struct ibmvscsis_adapter *adapter)
+{
+	int rc = 0;
+	struct vio_dev *vdev = adapter->dma_dev;
+	struct crq_queue *queue = &adapter->crq_queue;
+
+	/* Close the CRQ */
+	h_free_crq(vdev->unit_address);
+
+	/* Clean out the queue */
+	memset(queue->msgs, 0x00, PAGE_SIZE);
+	queue->cur = 0;
+
+	/* And re-open it again */
+	rc = h_reg_crq(vdev->unit_address, queue->msg_token,
+			PAGE_SIZE);
+	if (rc == 2)
+		/* Adapter is good, but other end is not ready */
+		pr_warn("ibmvscsis: Partner adapter not ready\n");
+	else if (rc != 0)
+		pr_err("ibmvscsis: couldn't register crq--rc 0x%x\n", rc);
+
+	return rc;
+}
+
+static void crq_queue_destroy(struct ibmvscsis_adapter *adapter)
+{
+	struct vio_dev *vdev = adapter->dma_dev;
+	struct crq_queue *queue = &adapter->crq_queue;
+
+	free_irq(vdev->irq, (void *)adapter);
+	flush_work(&adapter->crq_work);
+	h_free_crq(vdev->unit_address);
+	dma_unmap_single(&adapter->dma_dev->dev, queue->msg_token,
+			 queue->size * sizeof(*queue->msgs),
+			 DMA_BIDIRECTIONAL);
+
+	free_page((unsigned long)queue->msgs);
+}
+
+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 || crq->valid & 0xFF) {
+		if (++queue->cur == queue->size)
+			queue->cur = 0;
+
+		/* Ensure the read of the valid bit occurs before reading any
+		 * other bits of the CRQ entry
+		 */
+		rmb();
+	} else
+		crq = NULL;
+	spin_unlock_irqrestore(&queue->lock, flags);
+
+	return crq;
+}
+
+static int send_iu(struct iu_entry *iue, u64 length, u8 format)
+{
+	struct srp_target *target = iue->target;
+	struct ibmvscsis_adapter *adapter = target->ldata;
+	struct ibmvscsis_crq_msg crq_msg;
+	struct srp_rsp *rsp;
+	__be64 *crq_as_u64 = (__be64 *)&crq_msg;
+	long rc, rc1;
+
+	rsp = &vio_iu(iue)->srp.rsp;
+	pr_debug("ibmvscsis: send_iu: 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx\n",
+			(unsigned long)length,
+			(unsigned long)adapter->liobn,
+			(unsigned long)iue->sbuf->dma,
+			(unsigned long)adapter->riobn,
+			(unsigned long)be64_to_cpu(iue->remote_token));
+
+	/* First copy the SRP */
+	rc = h_copy_rdma(length, adapter->liobn, iue->sbuf->dma,
+			 adapter->riobn, be64_to_cpu(iue->remote_token));
+
+	switch (rc) {
+	case H_SUCCESS:
+		break;
+	case H_PERMISSION:
+	case H_SOURCE_PARM:
+	case H_DEST_PARM:
+		if (connection_broken(adapter)) {
+			pr_debug("ibmvscsis: rdma connection broken\n");
+			goto end;
+		}
+		break;
+	default:
+		pr_err("ibmvscsis: Error %ld transferring data\n", rc);
+		length = 0;
+		break;
+	}
+
+	pr_debug("ibmvscsis: crq pre cooked: 0x%x, 0x%llx, 0x%llx\n",
+			format, length, vio_iu(iue)->srp.rsp.tag);
+
+	crq_msg.valid = 0x80;
+	crq_msg.format = format;
+	crq_msg.rsvd = 0;
+	if (rc == 0)
+		crq_msg.status = 0x99;
+	else
+		crq_msg.status = rsp->status;
+	crq_msg.rsvd1 = 0;
+	crq_msg.IU_length = cpu_to_be16(length);
+	crq_msg.IU_data_ptr = vio_iu(iue)->srp.rsp.tag;
+
+	pr_debug("ibmvscsis: send crq: 0x%x, 0x%llx, 0x%llx\n",
+			adapter->dma_dev->unit_address,
+			be64_to_cpu(crq_as_u64[0]),
+			be64_to_cpu(crq_as_u64[1]));
+
+	srp_iu_put(iue);
+
+	rc1 = h_send_crq(adapter, be64_to_cpu(crq_as_u64[0]),
+				be64_to_cpu(crq_as_u64[1]));
+
+	if (rc1) {
+		pr_err("ibmvscsis: %ld sending response\n", rc1);
+		return rc1;
+	}
+	return rc;
+end:
+	return rc;
+}
+
+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 viosrp_adapter_info *mad = &vio_iu(iue)->mad.adapter_info;
+	struct mad_adapter_info_data *info;
+	int err;
+
+	mad->common.status = cpu_to_be16(VIOSRP_MAD_SUCCESS);
+
+	if (be16_to_cpu(mad->common.length) > sizeof(*info)) {
+		mad->common.status = cpu_to_be16(VIOSRP_MAD_FAILED);
+		return 0;
+	}
+
+	info = dma_alloc_coherent(&adapter->dma_dev->dev, sizeof(*info),
+				  &data_token, GFP_KERNEL);
+	if (!info) {
+		pr_err("ibmvscsis: bad dma_alloc_coherent %p\n", target);
+		mad->common.status = cpu_to_be16(VIOSRP_MAD_FAILED);
+		return 1;
+	}
+
+	/* Get remote info */
+	err = h_copy_rdma(sizeof(*info), adapter->riobn,
+				be64_to_cpu(remote_buffer),
+				adapter->liobn, data_token);
+
+	if (err == H_SUCCESS) {
+		pr_err("ibmvscsis: Client connect: %s (%d)\n",
+		       info->partition_name, info->partition_number);
+
+		if (adapter->client_data.partition_number == 0)
+			adapter->client_data.partition_number =
+				be32_to_cpu(info->partition_number);
+		strncpy(adapter->client_data.srp_version, info->srp_version,
+			sizeof(adapter->client_data.srp_version));
+		strncpy(adapter->client_data.partition_name,
+			info->partition_name,
+			sizeof(adapter->client_data.partition_name));
+		adapter->client_data.mad_version =
+						be32_to_cpu(info->mad_version);
+		adapter->client_data.os_type = be32_to_cpu(info->os_type);
+		pr_debug("ibmvscsis: adapterinfo client adapter %u\n",
+				adapter->client_data.os_type);
+
+		strcpy(info->srp_version, "16.a");
+		strncpy(info->partition_name, partition_name,
+			sizeof(info->partition_name));
+
+		info->partition_number = cpu_to_be32(partition_number);
+		info->mad_version = cpu_to_be32(1);
+		info->os_type = cpu_to_be32(2);
+		memset(&info->port_max_txu[0], 0, sizeof(info->port_max_txu));
+		info->port_max_txu[0] = cpu_to_be32(SCSI_MAX_SG_SEGMENTS *
+						PAGE_SIZE);
+
+		dma_rmb();
+		/* Send our info to remote */
+		err = h_copy_rdma(sizeof(*info), adapter->liobn, data_token,
+				  adapter->riobn, be64_to_cpu(remote_buffer));
+
+		switch (err) {
+		case H_SUCCESS:
+			break;
+		case H_PERMISSION:
+		case H_SOURCE_PARM:
+		case H_DEST_PARM:
+			if (connection_broken(adapter))
+				pr_debug("ibmvscsis: rdma connection broken\n");
+		default:
+			pr_err("ibmvscsis: Error sending adapter info %d\n",
+			       err);
+			return -EIO;
+		}
+	} else {
+		pr_err("ibmvscsis: Error sending adapter info %d\n", err);
+		return 1;
+	}
+
+	dma_free_coherent(&adapter->dma_dev->dev, sizeof(*info), info,
+			  data_token);
+
+	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 (be32_to_cpu(iu->mad.empty_iu.common.type)) {
+	case VIOSRP_EMPTY_IU_TYPE:
+		pr_err("ibmvscsis: %s\n", "Unsupported EMPTY MAD IU");
+		break;
+	case VIOSRP_ERROR_LOG_TYPE:
+		pr_err("ibmvscsis: %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:
+		pr_err("ibmvscsis: Unknown type %u\n", iu->srp.rsp.opcode);
+		iu->mad.empty_iu.common.status =
+					cpu_to_be16(VIOSRP_MAD_NOT_SUPPORTED);
+		send_iu(iue, sizeof(iu->mad), VIOSRP_MAD_FORMAT);
+		break;
+	}
+
+	return 1;
+}
+
+static void ibmvscsis_srp_i_logout(struct iu_entry *iue)
+{
+	union viosrp_iu *iu = vio_iu(iue);
+	struct srp_i_logout *log_out = &vio_iu(iue)->srp.i_logout;
+	u64 tag = iu->srp.rsp.tag;
+
+	log_out->opcode = SRP_I_LOGOUT;
+	log_out->tag = tag;
+	send_iu(iue, sizeof(*log_out), VIOSRP_SRP_FORMAT);
+}
+
+static void process_login(struct iu_entry *iue)
+{
+	union viosrp_iu *iu = vio_iu(iue);
+	struct srp_login_rsp *rsp = &iu->srp.login_rsp;
+	struct srp_login_rej *rej = &iu->srp.login_rej;
+	struct srp_target *target = iue->target;
+	struct ibmvscsis_adapter *adapter = target->ldata;
+	struct vio_dev *vdev = adapter->dma_dev;
+	struct se_portal_group *se_tpg;
+	char name[16];
+	u64 tag = iu->srp.rsp.tag;
+
+	/*
+	 * TODO handle case that requested size is wrong and buffer
+	 * format is wrong
+	 */
+	memset(iu, 0, max(sizeof(*rsp), sizeof(*rej)));
+
+	snprintf(name, sizeof(name), "%x", vdev->unit_address);
+
+	if (adapter->tport.enabled == false) {
+		rej->reason = cpu_to_be32(SRP_LOGIN_REJ_INSUFFICIENT_RESOURCES);
+		pr_err("ibmvscsis: Rejected SRP_LOGIN_REQ because target %s has not yet been enabled",
+		       name);
+		goto reject;
+	}
+
+	se_tpg = ibmvscsis_make_nexus(&adapter->tport,
+				      &adapter->tport.tport_name[0]);
+	if (se_tpg == NULL) {
+		pr_debug("ibmvscsis: login make nexus fail se_tpg(%p)\n",
+						se_tpg);
+		goto reject;
+	}
+
+	rsp->opcode = SRP_LOGIN_RSP;
+
+	rsp->req_lim_delta = cpu_to_be32(INITIAL_SRP_LIMIT);
+
+	pr_debug("ibmvscsis: process_login, tag:%llu\n", tag);
+
+	rsp->tag = tag;
+	rsp->max_it_iu_len = cpu_to_be32(sizeof(union srp_iu));
+	rsp->max_ti_iu_len = cpu_to_be32(sizeof(union srp_iu));
+	/* direct and indirect */
+	rsp->buf_fmt = cpu_to_be16(SRP_BUF_FORMAT_DIRECT
+					| SRP_BUF_FORMAT_INDIRECT);
+
+	send_iu(iue, sizeof(*rsp), VIOSRP_SRP_FORMAT);
+	return;
+
+reject:
+	rej->opcode = SRP_LOGIN_REJ;
+	rej->tag = tag;
+	rej->buf_fmt = cpu_to_be16(SRP_BUF_FORMAT_DIRECT
+				   | SRP_BUF_FORMAT_INDIRECT);
+
+	send_iu(iue, sizeof(*rej), VIOSRP_SRP_FORMAT);
+}
+
+static void process_tsk_mgmt(struct ibmvscsis_adapter *adapter,
+			     struct iu_entry *iue)
+{
+	struct srp_tsk_mgmt *srp_tsk = &vio_iu(iue)->srp.tsk_mgmt;
+	struct ibmvscsis_cmnd *cmd = adapter->cmd;
+	struct srp_rsp *rsp;
+	u64 unpacked_lun = 0;
+	u64 tag_to_abort = 0;
+	int tcm_type;
+	int rc = 0;
+
+	rsp = &vio_iu(iue)->srp.rsp;
+	unpacked_lun = ibmvscsis_unpack_lun((uint8_t *)&srp_tsk->lun,
+					    sizeof(srp_tsk->lun));
+
+	switch (srp_tsk->tsk_mgmt_func) {
+	case SRP_TSK_ABORT_TASK:
+		tcm_type = TMR_ABORT_TASK;
+		tag_to_abort = be64_to_cpu(srp_tsk->task_tag);
+		srp_iu_put(iue);
+		break;
+	case SRP_TSK_ABORT_TASK_SET:
+		tcm_type = TMR_ABORT_TASK_SET;
+		break;
+	case SRP_TSK_CLEAR_TASK_SET:
+		tcm_type = TMR_CLEAR_TASK_SET;
+		break;
+	case SRP_TSK_LUN_RESET:
+		tcm_type = TMR_LUN_RESET;
+		break;
+	case SRP_TSK_CLEAR_ACA:
+		tcm_type = TMR_CLEAR_ACA;
+		break;
+	default:
+		pr_err("ibmvscsis: unknown task mgmt func %d\n",
+						srp_tsk->tsk_mgmt_func);
+		cmd->se_cmd.se_tmr_req->response =
+					TMR_TASK_MGMT_FUNCTION_NOT_SUPPORTED;
+		rc = -1;
+		break;
+	}
+
+	if (!rc) {
+		cmd->se_cmd.tag = be64_to_cpu(srp_tsk->tag);
+
+		pr_debug("ibmvscsis: calling submit_tmr, func %d\n",
+			 srp_tsk->tsk_mgmt_func);
+		rc = target_submit_tmr(&cmd->se_cmd,
+				       adapter->tport.se_sess, NULL,
+				       unpacked_lun, srp_tsk, tcm_type,
+				       GFP_KERNEL, tag_to_abort,
+				       TARGET_SCF_ACK_KREF);
+		if (rc != 0) {
+			pr_err("ibmvscsis: target_submit_tmr failed, rc %d\n",
+			       rc);
+			cmd->se_cmd.se_tmr_req->response =
+							TMR_FUNCTION_REJECTED;
+			goto fail;
+		}
+	}
+fail:
+	if (rc)
+		transport_send_check_condition_and_sense(&cmd->se_cmd, 0, 0);
+
+}
+
+static bool connection_broken(struct ibmvscsis_adapter *adapter)
+{
+	u64 buffer[2];
+	struct viosrp_crq *crq;
+	long h_return_code;
+	bool rc = false;
+
+	/* create a PING crq */
+	crq = (struct viosrp_crq *)&buffer;
+	buffer[0] = buffer[1] = 0;
+	crq->valid = 0x80;
+	crq->format = 6;
+	crq->status = 0xF5;
+
+	h_return_code = h_send_crq(adapter,
+				   cpu_to_be64(buffer[0]),
+				   cpu_to_be64(buffer[1]));
+
+	pr_debug("ibmvscsis (%s): connection_broken: rc %ld\n",
+			dev_name(&adapter->dma_dev->dev), h_return_code);
+
+	if (h_return_code == H_CLOSED)
+		rc = true;
+
+	return rc;
+}
+
+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, be32_to_cpu(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,
+						  be64_to_cpu(md[i].va) + mdone,
+						  adapter->liobn,
+						  token + soff);
+			else
+				err = h_copy_rdma(slen,
+						  adapter->liobn,
+						  token + soff,
+						  adapter->riobn,
+						  be64_to_cpu(md[i].va)+mdone);
+			switch (err) {
+			case H_SUCCESS:
+				break;
+			case H_PERMISSION:
+			case H_SOURCE_PARM:
+			case H_DEST_PARM:
+				if (connection_broken(adapter))
+					pr_debug("ibmvscsis: rdma connection broken\n");
+			default:
+				pr_err("ibmvscsis: 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) {
+					pr_err("ibmvscsis: out of sg %p %d %d\n",
+						iue, sidx, nsg);
+					return -EIO;
+				}
+			}
+		}
+		rest -= mlen;
+	}
+	return 0;
+}
+
+static int ibmvscsis_queuecommand(struct ibmvscsis_adapter *adapter,
+				  struct iu_entry *iue)
+{
+	struct srp_cmd *cmd = iue->sbuf->buf;
+	struct scsi_cmnd *sc;
+	struct ibmvscsis_cmnd *vsc;
+	int ret;
+
+	pr_debug("ibmvscsis: ibmvscsis_queuecommand\n");
+
+	vsc = kzalloc(sizeof(*vsc), GFP_KERNEL);
+	adapter->cmd = vsc;
+	sc = &vsc->sc;
+	sc->sense_buffer = vsc->se_cmd.sense_buffer;
+	sc->cmnd = cmd->cdb;
+	sc->SCp.ptr = (char *)iue;
+
+	ret = tcm_queuecommand(adapter, vsc, cmd);
+
+	return ret;
+}
+
+static uint64_t ibmvscsis_unpack_lun(const uint8_t *lun, int len)
+{
+	uint64_t res = NO_SUCH_LUN;
+	int addressing_method;
+
+	if (unlikely(len < 2)) {
+		pr_err("Illegal LUN length %d, expected 2 bytes or more\n",
+			len);
+		goto out;
+	}
+
+	switch (len) {
+	case 8:
+		if ((*((__be64 *)lun) & cpu_to_be64(0x0000FFFFFFFFFFFFLL)) != 0)
+			goto out_err;
+		break;
+	case 4:
+		if (*((__be16 *)&lun[2]) != 0)
+			goto out_err;
+		break;
+	case 6:
+		if (*((__be32 *)&lun[2]) != 0)
+			goto out_err;
+		break;
+	case 2:
+		break;
+	default:
+		goto out_err;
+	}
+
+	addressing_method = (*lun) >> 6; /* highest two bits of byte 0 */
+	switch (addressing_method) {
+	case SCSI_LUN_ADDR_METHOD_PERIPHERAL:
+	case SCSI_LUN_ADDR_METHOD_FLAT:
+	case SCSI_LUN_ADDR_METHOD_LUN:
+		res = *(lun + 1) | (((*lun) & 0x3f) << 8);
+		break;
+
+	case SCSI_LUN_ADDR_METHOD_EXTENDED_LUN:
+	default:
+		pr_err("Unimplemented LUN addressing method %u\n",
+			addressing_method);
+		break;
+	}
+
+out:
+	return res;
+out_err:
+	pr_err("Support for multi-level LUNs has not yet been implemented\n");
+	goto out;
+}
+
+static int tcm_queuecommand(struct ibmvscsis_adapter *adapter,
+			    struct ibmvscsis_cmnd *vsc,
+			    struct srp_cmd *scmd)
+{
+	struct se_cmd *se_cmd;
+	int attr;
+	u64 data_len;
+	int ret;
+	uint64_t unpacked_lun;
+
+	switch (scmd->task_attr) {
+	case SRP_SIMPLE_TASK:
+		attr = TCM_SIMPLE_TAG;
+		break;
+	case SRP_ORDERED_TASK:
+		attr = TCM_ORDERED_TAG;
+		break;
+	case SRP_HEAD_TASK:
+		attr = TCM_HEAD_TAG;
+		break;
+	case SRP_ACA_TASK:
+		attr = TCM_ACA_TAG;
+		break;
+	default:
+		pr_err("ibmvscsis: Task attribute %d not supported\n",
+		       scmd->task_attr);
+		attr = TCM_SIMPLE_TAG;
+	}
+
+	pr_debug("ibmvscsis: srp_data_length: %llx, srp_direction:%x\n",
+			srp_data_length(scmd, srp_cmd_direction(scmd)),
+			srp_cmd_direction(scmd));
+	data_len = srp_data_length(scmd, srp_cmd_direction(scmd));
+
+	vsc->se_cmd.tag = scmd->tag;
+	se_cmd = &vsc->se_cmd;
+
+	pr_debug("ibmvscsis: size of lun:%lx, lun:%s\n", sizeof(scmd->lun),
+				&scmd->lun.scsi_lun[0]);
+
+	unpacked_lun = ibmvscsis_unpack_lun((uint8_t *)&scmd->lun,
+				sizeof(scmd->lun));
+
+	ret = target_submit_cmd(se_cmd, adapter->tport.se_sess,
+				&scmd->cdb[0], &vsc->sense_buf[0], unpacked_lun,
+				data_len, attr, srp_cmd_direction(scmd),
+				TARGET_SCF_ACK_KREF);
+	if (ret != 0) {
+		ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+		pr_debug("ibmvscsis: tcm_queuecommand fail submit_cmd\n");
+		goto send_sense;
+	}
+	return 0;
+
+send_sense:
+	transport_send_check_condition_and_sense(&vsc->se_cmd, ret, 0);
+	transport_generic_free_cmd(&vsc->se_cmd, 0);
+	return -1;
+}
+
+/*
+ * ibmvscsis_init() - Kernel Module initialization
+ *
+ * Note: vio_register_driver() registers callback functions, and atleast one
+ * of those call back functions calls TCM - Linux IO Target Subsystem, thus
+ * the SCSI Target template must be registered before vio_register_driver()
+ * is called.
+ */
+static int __init ibmvscsis_init(void)
+{
+	int ret = -ENOMEM;
+
+	pr_info("IBMVSCSIS fabric module %s on %s/%s on "UTS_RELEASE"\n",
+		IBMVSCSIS_VERSION,
+		utsname()->sysname,
+		utsname()->machine);
+
+	ret = get_system_info();
+	if (ret) {
+		pr_err("ibmvscsis: ret %d from get_system_info\n", ret);
+		goto out;
+	}
+
+	ret = class_register(&ibmvscsis_class);
+	if (ret) {
+		pr_err("ibmvscsis failed class register\n");
+		goto out;
+	}
+
+	ret = target_register_template(&ibmvscsis_ops);
+	if (ret) {
+		pr_err("ibmvscsis: ret %d from target_register_template\n",
+				ret);
+		goto unregister_class;
+	}
+
+	vtgtd = create_workqueue("ibmvscsis");
+	if (!vtgtd)
+		goto unregister_target;
+
+	ret = vio_register_driver(&ibmvscsis_driver);
+	if (ret) {
+		pr_err("ibmvscsis: ret %d from vio_register_driver\n", ret);
+		goto destroy_wq;
+	}
+
+	return 0;
+
+destroy_wq:
+	destroy_workqueue(vtgtd);
+unregister_target:
+	target_unregister_template(&ibmvscsis_ops);
+unregister_class:
+	class_unregister(&ibmvscsis_class);
+out:
+	return ret;
+};
+
+static void __exit ibmvscsis_exit(void)
+{
+	pr_info("ibmvscsis: Unregister IBM virtual SCSI driver\n");
+	vio_unregister_driver(&ibmvscsis_driver);
+	destroy_workqueue(vtgtd);
+	target_unregister_template(&ibmvscsis_ops);
+	class_unregister(&ibmvscsis_class);
+};
+
+MODULE_DESCRIPTION("IBMVSCSIS fabric driver");
+MODULE_AUTHOR("Bryant G. Ly");
+MODULE_LICENSE("GPL");
+module_init(ibmvscsis_init);
+module_exit(ibmvscsis_exit);
diff --git a/drivers/scsi/ibmvscsi/ibmvscsis.h b/drivers/scsi/ibmvscsi/ibmvscsis.h
new file mode 100644
index 0000000..bcee92b
--- /dev/null
+++ b/drivers/scsi/ibmvscsi/ibmvscsis.h
@@ -0,0 +1,160 @@ 
+/*******************************************************************************
+ * IBM 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>
+ * Copyright (C) 2016 Bryant G. Ly <bgly@us.ibm.com> IBM Corp.
+ *
+ * Authors: Bryant G. Ly <bryantly@linux.vnet.ibm.com>
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+#ifndef __H_IBMVSCSIS
+#define __H_IBMVSCSIS
+
+#define IBMVSCSIS_NAMELEN       32
+
+#define SCSOLNT_RESP_SHIFT      1
+#define UCSOLNT_RESP_SHIFT      2
+
+#define SCSOLNT         (1 << SCSOLNT_RESP_SHIFT)
+#define UCSOLNT         (1 << UCSOLNT_RESP_SHIFT)
+
+#define INQ_DATA_OFFSET 8
+#define NO_SUCH_LUN ((u64)-1LL)
+
+struct client_info {
+#define SRP_VERSION "16.a"
+	char srp_version[8];
+	/* root node property ibm,partition-name */
+	char partition_name[96];
+	/* root node property ibm,partition-no */
+	uint32_t partition_number;
+	/* initially 1 */
+	uint32_t mad_version;
+	uint32_t os_type;
+};
+
+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];
+	u32 lun;
+};
+
+struct ibmvscsis_crq_msg {
+	u8 valid;
+	u8 format;
+	u8 rsvd;
+	u8 status;
+	u16 rsvd1;
+	__be16 IU_length;
+	__be64 IU_data_ptr;
+};
+
+struct ibmvscsis_tport {
+	/* SCSI protocol the tport is providing */
+	u8 tport_proto_id;
+	/* ASCII formatted WWPN for SRP Target port */
+	char tport_name[IBMVSCSIS_NAMELEN];
+	/* Returned by ibmvscsis_make_tport() */
+	struct se_wwn tport_wwn;
+	int lun_count;
+	/* Returned by ibmvscsis_make_tpg() */
+	struct se_portal_group se_tpg;
+	/* ibmvscsis port target portal group tag for TCM */
+	u16 tport_tpgt;
+	/* Pointer to TCM session for I_T Nexus */
+	struct se_session *se_sess;
+	struct ibmvscsis_cmnd *cmd;
+	bool enabled;
+	bool releasing;
+};
+
+struct ibmvscsis_adapter {
+	struct device dev;
+	struct vio_dev *dma_dev;
+	struct list_head siblings;
+
+	struct crq_queue crq_queue;
+	struct work_struct crq_work;
+
+	atomic_t req_lim_delta;
+	u32 liobn;
+	u32 riobn;
+
+	struct srp_target *target;
+
+	struct list_head list;
+	struct ibmvscsis_tport tport;
+	struct ibmvscsis_cmnd *cmd;
+	struct client_info client_data;
+};
+
+struct ibmvscsis_nacl {
+	/* Returned by ibmvscsis_make_nexus */
+	struct se_node_acl se_node_acl;
+};
+
+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];
+};
+
+enum srp_trans_event {
+	UNUSED_FORMAT = 0,
+	PARTNER_FAILED = 1,
+	PARTNER_DEREGISTER = 2,
+	MIGRATED = 6
+};
+
+enum scsi_lun_addr_method {
+	SCSI_LUN_ADDR_METHOD_PERIPHERAL   = 0,
+	SCSI_LUN_ADDR_METHOD_FLAT         = 1,
+	SCSI_LUN_ADDR_METHOD_LUN          = 2,
+	SCSI_LUN_ADDR_METHOD_EXTENDED_LUN = 3,
+};
+
+enum srp_os_type {
+	OS400 = 1,
+	LINUX = 2,
+	AIX = 3,
+	OFW = 4
+};
+
+#define vio_iu(IUE) ((union viosrp_iu *)((IUE)->sbuf->buf))
+
+#define h_reg_crq(ua, tok, sz)\
+			plpar_hcall_norets(H_REG_CRQ, ua, tok, sz)
+
+#endif
diff --git a/drivers/scsi/libsrp.c b/drivers/scsi/libsrp.c
new file mode 100644
index 0000000..0e32abd
--- /dev/null
+++ b/drivers/scsi/libsrp.c
@@ -0,0 +1,387 @@ 
+/*******************************************************************************
+ * SCSI RDMA Protocol lib functions
+ *
+ * Copyright (C) 2006 FUJITA Tomonori <tomof@acm.org>
+ * Copyright (C) 2016 Bryant G. Ly <bgly@us.ibm.com> IBM Corp.
+ *
+ * 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.
+ *
+ ***********************************************************************/
+
+#include <linux/printk.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/kfifo.h>
+#include <linux/scatterlist.h>
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/srp.h>
+#include <target/target_core_base.h>
+#include <scsi/libsrp.h>
+
+static int srp_iu_pool_alloc(struct srp_queue *q, size_t max,
+			     struct srp_buf **ring)
+{
+	int i;
+	struct iu_entry *iue;
+
+	q->pool = kcalloc(max, sizeof(struct iu_entry *), GFP_KERNEL);
+	if (!q->pool)
+		return -ENOMEM;
+	q->items = kcalloc(max, sizeof(struct iu_entry), GFP_KERNEL);
+	if (!q->items)
+		goto free_pool;
+
+	spin_lock_init(&q->lock);
+	kfifo_init(&q->queue, (void *) q->pool, max * sizeof(void *));
+
+	for (i = 0, iue = q->items; i < max; i++) {
+		kfifo_in(&q->queue, (void *) &iue, sizeof(void *));
+		iue->sbuf = ring[i];
+		iue++;
+	}
+	return 0;
+
+free_pool:
+	kfree(q->pool);
+	return -ENOMEM;
+}
+
+static void srp_iu_pool_free(struct srp_queue *q)
+{
+	kfree(q->items);
+	kfree(q->pool);
+}
+
+static struct srp_buf **srp_ring_alloc(struct device *dev,
+				       size_t max, size_t size)
+{
+	int i;
+	struct srp_buf **ring;
+
+	ring = kcalloc(max, sizeof(struct srp_buf *), GFP_KERNEL);
+	if (!ring)
+		return NULL;
+
+	for (i = 0; i < max; i++) {
+		ring[i] = kzalloc(sizeof(struct srp_buf), GFP_KERNEL);
+		if (!ring[i])
+			goto out;
+		ring[i]->buf = dma_alloc_coherent(dev, size, &ring[i]->dma,
+						  GFP_KERNEL);
+		if (!ring[i]->buf)
+			goto out;
+	}
+	return ring;
+
+out:
+	for (i = 0; i < max && ring[i]; i++) {
+		if (ring[i]->buf) {
+			dma_free_coherent(dev, size, ring[i]->buf,
+						ring[i]->dma);
+		}
+		kfree(ring[i]);
+	}
+	kfree(ring);
+
+	return NULL;
+}
+
+static void srp_ring_free(struct device *dev, struct srp_buf **ring,
+			  size_t max, size_t size)
+{
+	int i;
+
+	for (i = 0; i < max; i++) {
+		dma_free_coherent(dev, size, ring[i]->buf, ring[i]->dma);
+		kfree(ring[i]);
+	}
+	kfree(ring);
+}
+
+int srp_target_alloc(struct srp_target *target, struct device *dev,
+		     size_t nr, size_t iu_size)
+{
+	int err;
+
+	spin_lock_init(&target->lock);
+
+	target->dev = dev;
+
+	target->srp_iu_size = iu_size;
+	target->rx_ring_size = nr;
+	target->rx_ring = srp_ring_alloc(target->dev, nr, iu_size);
+	if (!target->rx_ring)
+		return -ENOMEM;
+	err = srp_iu_pool_alloc(&target->iu_queue, nr, target->rx_ring);
+	if (err)
+		goto free_ring;
+
+	dev_set_drvdata(target->dev, target);
+	return 0;
+
+free_ring:
+	srp_ring_free(target->dev, target->rx_ring, nr, iu_size);
+	return -ENOMEM;
+}
+EXPORT_SYMBOL_GPL(srp_target_alloc);
+
+void srp_target_free(struct srp_target *target)
+{
+	dev_set_drvdata(target->dev, NULL);
+	srp_ring_free(target->dev, target->rx_ring, target->rx_ring_size,
+		      target->srp_iu_size);
+	srp_iu_pool_free(&target->iu_queue);
+}
+EXPORT_SYMBOL_GPL(srp_target_free);
+
+struct iu_entry *srp_iu_get(struct srp_target *target)
+{
+	struct iu_entry *iue = NULL;
+
+	if (kfifo_out_locked(&target->iu_queue.queue, (void *) &iue,
+				sizeof(void *),
+				&target->iu_queue.lock) != sizeof(void *)) {
+		WARN_ONCE(1, "unexpected fifo state");
+		return NULL;
+	}
+	if (!iue)
+		return iue;
+	iue->target = target;
+	iue->flags = 0;
+	return iue;
+}
+EXPORT_SYMBOL_GPL(srp_iu_get);
+
+void srp_iu_put(struct iu_entry *iue)
+{
+	kfifo_in_locked(&iue->target->iu_queue.queue, (void *) &iue,
+			sizeof(void *), &iue->target->iu_queue.lock);
+}
+EXPORT_SYMBOL_GPL(srp_iu_put);
+
+static int srp_direct_data(struct scsi_cmnd *sc, struct srp_direct_buf *md,
+			   enum dma_data_direction dir, srp_rdma_t rdma_io,
+			   int dma_map, int ext_desc)
+{
+	struct iu_entry *iue = NULL;
+	struct scatterlist *sg = NULL;
+	int err, nsg = 0, len;
+
+	if (dma_map) {
+		iue = (struct iu_entry *) sc->SCp.ptr;
+		sg = scsi_sglist(sc);
+		nsg = dma_map_sg(iue->target->dev, sg, scsi_sg_count(sc),
+				 DMA_BIDIRECTIONAL);
+		if (!nsg) {
+			pr_err("libsrp: fail to map %p %d\n",
+				iue, scsi_sg_count(sc));
+			return 0;
+		}
+		len = min(scsi_bufflen(sc), be32_to_cpu(md->len));
+	} else
+		len = be32_to_cpu(md->len);
+
+	err = rdma_io(sc, sg, nsg, md, 1, dir, len);
+
+	if (dma_map)
+		dma_unmap_sg(iue->target->dev, sg, nsg, DMA_BIDIRECTIONAL);
+
+	return err;
+}
+
+static int srp_indirect_data(struct scsi_cmnd *sc, struct srp_cmd *cmd,
+			     struct srp_indirect_buf *id,
+			     enum dma_data_direction dir, srp_rdma_t rdma_io,
+			     int dma_map, int ext_desc)
+{
+	struct iu_entry *iue = NULL;
+	struct srp_direct_buf *md = NULL;
+	struct scatterlist dummy, *sg = NULL;
+	dma_addr_t token = 0;
+	int err = 0;
+	int nmd, nsg = 0, len;
+
+	if (dma_map || ext_desc) {
+		iue = (struct iu_entry *) sc->SCp.ptr;
+		sg = scsi_sglist(sc);
+	}
+
+	nmd = be32_to_cpu(id->table_desc.len) / sizeof(struct srp_direct_buf);
+
+	if ((dir == DMA_FROM_DEVICE && nmd == cmd->data_in_desc_cnt) ||
+	    (dir == DMA_TO_DEVICE && nmd == cmd->data_out_desc_cnt)) {
+		md = &id->desc_list[0];
+		goto rdma;
+	}
+
+	if (ext_desc && dma_map) {
+		md = dma_alloc_coherent(iue->target->dev,
+					be32_to_cpu(id->table_desc.len),
+					&token, GFP_KERNEL);
+		if (!md) {
+			pr_err("libsrp: Can't get dma memory %u\n",
+				be32_to_cpu(id->table_desc.len));
+			return -ENOMEM;
+		}
+
+		sg_init_one(&dummy, md, be32_to_cpu(id->table_desc.len));
+		sg_dma_address(&dummy) = token;
+		sg_dma_len(&dummy) = be32_to_cpu(id->table_desc.len);
+		err = rdma_io(sc, &dummy, 1, &id->table_desc, 1, DMA_TO_DEVICE,
+			      be32_to_cpu(id->table_desc.len));
+		if (err) {
+			pr_err("libsrp: Error copying indirect table %d\n",
+				err);
+			goto free_mem;
+		}
+	} else {
+		pr_err("libsrp: This command uses external indirect buffer\n");
+		return -EINVAL;
+	}
+
+rdma:
+	if (dma_map) {
+		nsg = dma_map_sg(iue->target->dev, sg, scsi_sg_count(sc),
+				 DMA_BIDIRECTIONAL);
+		if (!nsg) {
+			pr_err("libsrp: fail to map %p %d\n",
+				iue, scsi_sg_count(sc));
+			err = -EIO;
+			goto free_mem;
+		}
+		len = min(scsi_bufflen(sc), be32_to_cpu(id->len));
+	} else
+		len = be32_to_cpu(id->len);
+
+	err = rdma_io(sc, sg, nsg, md, nmd, dir, len);
+
+	if (dma_map)
+		dma_unmap_sg(iue->target->dev, sg, nsg, DMA_BIDIRECTIONAL);
+
+free_mem:
+	if (token && dma_map) {
+		dma_free_coherent(iue->target->dev,
+				  be32_to_cpu(id->table_desc.len), md, token);
+	}
+	return err;
+}
+
+static int data_out_desc_size(struct srp_cmd *cmd)
+{
+	int size = 0;
+	u8 fmt = cmd->buf_fmt >> 4;
+
+	switch (fmt) {
+	case SRP_NO_DATA_DESC:
+		break;
+	case SRP_DATA_DESC_DIRECT:
+		size = sizeof(struct srp_direct_buf);
+		break;
+	case SRP_DATA_DESC_INDIRECT:
+		size = sizeof(struct srp_indirect_buf) +
+			sizeof(struct srp_direct_buf) * cmd->data_out_desc_cnt;
+		break;
+	default:
+		pr_err("libsrp: client error. Invalid data_out_format %x\n",
+			fmt);
+		break;
+	}
+	return size;
+}
+
+/*
+ * TODO: this can be called multiple times for a single command if it
+ * has very long data.
+ */
+int srp_transfer_data(struct scsi_cmnd *sc, struct srp_cmd *cmd,
+		      srp_rdma_t rdma_io, int dma_map, int ext_desc)
+{
+	struct srp_direct_buf *md;
+	struct srp_indirect_buf *id;
+	enum dma_data_direction dir;
+	int offset, err = 0;
+	u8 format;
+
+	offset = cmd->add_cdb_len & ~3;
+
+	dir = srp_cmd_direction(cmd);
+	if (dir == DMA_FROM_DEVICE)
+		offset += data_out_desc_size(cmd);
+
+	if (dir == DMA_TO_DEVICE)
+		format = cmd->buf_fmt >> 4;
+	else
+		format = cmd->buf_fmt & ((1U << 4) - 1);
+
+	switch (format) {
+	case SRP_NO_DATA_DESC:
+		break;
+	case SRP_DATA_DESC_DIRECT:
+		md = (struct srp_direct_buf *)
+			(cmd->add_data + offset);
+		err = srp_direct_data(sc, md, dir, rdma_io, dma_map, ext_desc);
+		break;
+	case SRP_DATA_DESC_INDIRECT:
+		id = (struct srp_indirect_buf *)
+			(cmd->add_data + offset);
+		err = srp_indirect_data(sc, cmd, id, dir, rdma_io, dma_map,
+					ext_desc);
+		break;
+	default:
+		pr_err("libsrp: Unknown format %d %x\n", dir, format);
+		err = -EINVAL;
+	}
+
+	return err;
+}
+EXPORT_SYMBOL_GPL(srp_transfer_data);
+
+u64 srp_data_length(struct srp_cmd *cmd, enum dma_data_direction dir)
+{
+	struct srp_direct_buf *md;
+	struct srp_indirect_buf *id;
+	u64 len = 0;
+	unsigned offset = cmd->add_cdb_len & ~3;
+	u8 fmt;
+
+	if (dir == DMA_TO_DEVICE)
+		fmt = cmd->buf_fmt >> 4;
+	else {
+		fmt = cmd->buf_fmt & ((1U << 4) - 1);
+		offset += data_out_desc_size(cmd);
+	}
+
+	switch (fmt) {
+	case SRP_NO_DATA_DESC:
+		break;
+	case SRP_DATA_DESC_DIRECT:
+		md = (struct srp_direct_buf *) (cmd->add_data + offset);
+		len = be32_to_cpu(md->len);
+		break;
+	case SRP_DATA_DESC_INDIRECT:
+		id = (struct srp_indirect_buf *) (cmd->add_data + offset);
+		len = be32_to_cpu(id->len);
+		break;
+	default:
+		pr_err("invalid data format %x\n", fmt);
+		break;
+	}
+	return len;
+}
+EXPORT_SYMBOL_GPL(srp_data_length);
+
+MODULE_DESCRIPTION("SCSI RDMA Protocol lib functions");
+MODULE_AUTHOR("FUJITA Tomonori");
+MODULE_LICENSE("GPL");
diff --git a/include/scsi/libsrp.h b/include/scsi/libsrp.h
new file mode 100644
index 0000000..f9b2d8f
--- /dev/null
+++ b/include/scsi/libsrp.h
@@ -0,0 +1,95 @@ 
+#ifndef __LIBSRP_H__
+#define __LIBSRP_H__
+
+#include <linux/list.h>
+#include <linux/kfifo.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_host.h>
+#include <scsi/srp.h>
+#include <target/target_core_base.h>
+
+enum srp_task_attributes {
+	SRP_SIMPLE_TASK = 0,
+	SRP_HEAD_TASK = 1,
+	SRP_ORDERED_TASK = 2,
+	SRP_ACA_TASK = 4
+};
+
+enum iue_flags {
+	V_DIOVER,
+	V_WRITE,
+	V_LINKED,
+	V_FLYING,
+};
+
+enum {
+	SRP_TASK_MANAGEMENT_FUNCTION_COMPLETE           = 0,
+	SRP_REQUEST_FIELDS_INVALID                      = 2,
+	SRP_TASK_MANAGEMENT_FUNCTION_NOT_SUPPORTED      = 4,
+	SRP_TASK_MANAGEMENT_FUNCTION_FAILED             = 5
+};
+
+struct srp_buf {
+	dma_addr_t dma;
+	void *buf;
+};
+
+struct srp_queue {
+	void *pool;
+	void *items;
+	struct kfifo queue;
+	spinlock_t lock;
+};
+
+struct srp_target {
+	struct Scsi_Host *shost;
+	struct se_device *tgt;
+	struct device *dev;
+
+	spinlock_t lock;
+	struct list_head cmd_queue;
+
+	size_t srp_iu_size;
+	struct srp_queue iu_queue;
+	size_t rx_ring_size;
+	struct srp_buf **rx_ring;
+
+	void *ldata;
+};
+
+struct iu_entry {
+	struct srp_target *target;
+
+	struct list_head ilist;
+	dma_addr_t remote_token;
+	unsigned long flags;
+
+	struct srp_buf *sbuf;
+};
+
+typedef int (srp_rdma_t)(struct scsi_cmnd *, struct scatterlist *, int,
+			 struct srp_direct_buf *, int,
+			 enum dma_data_direction, unsigned int);
+extern int srp_target_alloc(struct srp_target *, struct device *,
+				size_t, size_t);
+extern void srp_target_free(struct srp_target *);
+
+extern struct iu_entry *srp_iu_get(struct srp_target *);
+extern void srp_iu_put(struct iu_entry *);
+
+extern int srp_transfer_data(struct scsi_cmnd *, struct srp_cmd *,
+			     srp_rdma_t, int, int);
+
+static inline struct srp_target *host_to_srp_target(struct Scsi_Host *host)
+{
+	return (struct srp_target *) host->hostdata;
+}
+
+static inline int srp_cmd_direction(struct srp_cmd *cmd)
+{
+	return (cmd->buf_fmt >> 4) ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
+}
+
+extern u64 srp_data_length(struct srp_cmd *cmd, enum dma_data_direction dir);
+
+#endif