mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [RFC PATCH 10/19] Target_Core_Mod PSCSI <-> Linux/SCSI subsystem plugin
@ 2009-09-12  1:57 Nicholas A. Bellinger
  0 siblings, 0 replies; 2+ messages in thread
From: Nicholas A. Bellinger @ 2009-09-12  1:57 UTC (permalink / raw)
  To: LKML, linux-scsi
  Cc: Andrew Morton, Greg KH, Douglas Gilbert, James Bottomley,
	Hannes Reinecke, FUJITA Tomonori, Mike Christie, Joel Becker,
	Martin K. Petersen, Christoph Hellwig, Linus Torvalds,
	Alan Stern, Boaz Harrosh, Florian Haas, Philipp Reisner

[RFC PATCH 10/19] Target_Core_Mod PSCSI <-> Linux/SCSI subsystem plugin

This patch adds the PSCSI subsystem plugin for accessing struct scsi_device
using struct request from upstream Linux/SCSI code in k.o v2.6.31

Signed-off-by: Nicholas A. Bellinger <nab@linux-iscsi.org>
diff --git a/drivers/target/target_core_pscsi.c b/drivers/target/target_core_pscsi.c
new file mode 100644
index 0000000..85a781b
--- /dev/null
+++ b/drivers/target/target_core_pscsi.c
@@ -0,0 +1,1677 @@
+/*******************************************************************************
+ * Filename:  target_core_pscsi.c
+ *
+ * This file contains the generic target mode <-> Linux SCSI subsystem plugin.
+ *
+ * Copyright (c) 2003, 2004, 2005 PyX Technologies, Inc.
+ * Copyright (c) 2005, 2006, 2007 SBE, Inc.
+ * Copyright (c) 2007-2009 Rising Tide Software, Inc.
+ * Copyright (c) 2008-2009 Linux-iSCSI.org
+ *
+ * Nicholas A. Bellinger <nab@kernel.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ ******************************************************************************/
+
+
+#define TARGET_CORE_PSCSI_C
+
+#include <linux/version.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/blkdev.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/smp_lock.h>
+#include <linux/genhd.h>
+#include <linux/cdrom.h>
+#include <linux/file.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_host.h>
+#include <sd.h>
+#include <sr.h>
+
+#include <target/target_core_base.h>
+#include <target/target_core_device.h>
+#include <target/target_core_transport.h>
+#include <target/target_core_pscsi.h>
+#include <target/target_core_plugin.h>
+#include <target/target_core_seobj.h>
+#include <target/target_core_transport_plugin.h>
+
+#undef TARGET_CORE_PSCSI_C
+
+#define ISPRINT(a)  ((a >= ' ') && (a <= '~'))
+
+/*	pscsi_get_sh():
+ *
+ *
+ */
+static struct Scsi_Host *pscsi_get_sh(u32 host_no)
+{
+	struct Scsi_Host *sh = NULL;
+
+	sh = scsi_host_lookup(host_no);
+	if (IS_ERR(sh)) {
+		printk(KERN_ERR "Unable to locate SCSI HBA with Host ID:"
+				" %u\n", host_no);
+		return NULL;
+	}
+
+	return sh;
+}
+
+/*	pscsi_check_sd():
+ *
+ *	Should be called with scsi_device_get(sd) held
+ */
+int pscsi_check_sd(struct scsi_device *sd)
+{
+	struct gendisk *disk;
+	struct scsi_disk *sdisk;
+
+	if (!sd) {
+		printk(KERN_ERR "struct scsi_device is NULL!\n");
+		return -1;
+	}
+
+	if (sd->type != TYPE_DISK)
+		return 0;
+
+	/*
+	 * Some struct scsi_device of Type: Direct-Access, namely the
+	 * SGI Univerisal Xport do not have a corrasponding block device.
+	 * We skip these for now.
+	 */
+	sdisk = dev_get_drvdata(&sd->sdev_gendev);
+	if (!(sdisk))
+		return -1;
+
+	disk = (struct gendisk *) sdisk->disk;
+	if (!(disk->major)) {
+		printk(KERN_ERR "dev_get_drvdata() failed\n");
+		return -1;
+	}
+
+	if (linux_blockdevice_check(disk->major, disk->first_minor) < 0)
+		return -1;
+
+	return 0;
+}
+
+/*	pscsi_claim_sd():
+ *
+ *	Should be called with scsi_device_get(sd) held
+ */
+int pscsi_claim_sd(struct scsi_device *sd)
+{
+	struct block_device *bdev;
+	struct gendisk *disk;
+	struct scsi_disk *sdisk;
+
+	if (!sd) {
+		printk(KERN_ERR "struct scsi_device is NULL!\n");
+		return -1;
+	}
+
+	if (sd->type != TYPE_DISK)
+		return 0;
+
+	/*
+	 * Some struct scsi_device of Type: Direct-Access, namely the
+	 * SGI Univerisal Xport do not have a corrasponding block device.
+	 * We skip these for now.
+	 */
+	sdisk = dev_get_drvdata(&sd->sdev_gendev);
+	if (!(sdisk))
+		return -1;
+
+	disk = (struct gendisk *) sdisk->disk;
+	if (!(disk->major)) {
+		printk(KERN_ERR "dev_get_drvdata() failed\n");
+		return -1;
+	}
+
+	printk(KERN_INFO "PSCSI: Claiming %p Major:Minor - %d:%d\n",
+		sd, disk->major, disk->first_minor);
+
+	bdev = linux_blockdevice_claim(disk->major, disk->first_minor,
+				(void *)sd);
+	if (!(bdev))
+		return -1;
+
+	return 0;
+}
+
+/*	pscsi_release_sd()
+ *
+ * 	Should be called with scsi_device_get(sd) held
+ */
+int pscsi_release_sd(struct scsi_device *sd)
+{
+	struct gendisk *disk;
+	struct scsi_disk *sdisk;
+
+	if (!sd) {
+		printk(KERN_ERR "struct scsi_device is NULL!\n");
+		return -1;
+	}
+
+	if (sd->type != TYPE_DISK)
+		return 0;
+
+	/*
+	 * Some struct scsi_device of Type: Direct-Access, namely the
+	 * SGI Univerisal Xport do not have a corrasponding block device.
+	 * We skip these for now.
+	 */
+	sdisk = dev_get_drvdata(&sd->sdev_gendev);
+	if (!(sdisk))
+		return -1;
+
+	disk = (struct gendisk *) sdisk->disk;
+	if (!(disk->major)) {
+		printk(KERN_ERR "dev_get_drvdata() failed\n");
+		return -1;
+	}
+
+	printk(KERN_INFO "PSCSI: Releasing Major:Minor - %d:%d\n",
+		disk->major, disk->first_minor);
+
+	return linux_blockdevice_release(disk->major, disk->first_minor, NULL);
+}
+
+/*	pscsi_attach_hba():
+ *
+ * 	pscsi_get_sh() used scsi_host_lookup() to locate struct Scsi_Host.
+ *	from the passed SCSI Host ID.
+ */
+int pscsi_attach_hba(se_hba_t *hba, u32 host_id)
+{
+	int hba_depth, max_sectors;
+	struct Scsi_Host *sh;
+
+	sh = pscsi_get_sh(host_id);
+	if (!(sh))
+		return -EINVAL;
+
+	max_sectors = sh->max_sectors;
+
+	/*
+	 * Usually the SCSI LLD will use the hostt->can_queue value to define
+	 * its HBA TCQ depth.  Some other drivers (like 2.6 megaraid) don't set
+	 * this at all and set sh->can_queue at runtime.
+	 */
+	hba_depth = (sh->hostt->can_queue > sh->can_queue) ?
+		sh->hostt->can_queue : sh->can_queue;
+	atomic_set(&hba->left_queue_depth, hba_depth);
+	atomic_set(&hba->max_queue_depth, hba_depth);
+
+	hba->hba_ptr = (void *) sh;
+	hba->transport = &pscsi_template;
+
+	printk(KERN_INFO "CORE_HBA[%d] - %s SCSI HBA Driver %s on"
+		" Generic Target Core Stack %s\n", hba->hba_id,
+		PYX_ISCSI_VENDOR, PSCSI_VERSION, TARGET_CORE_MOD_VERSION);
+	printk(KERN_INFO "CORE_HBA[%d] - %s\n", hba->hba_id, (sh->hostt->name) ?
+			(sh->hostt->name) : "Unknown");
+	printk(KERN_INFO "CORE_HBA[%d] - Attached SCSI HBA to Generic"
+		" Target Core with TCQ Depth: %d MaxSectors: %hu\n",
+		hba->hba_id, atomic_read(&hba->max_queue_depth), max_sectors);
+
+	return 0;
+}
+
+/*	pscsi_detach_hba(): (Part of se_subsystem_api_t template)
+ *
+ *
+ */
+int pscsi_detach_hba(se_hba_t *hba)
+{
+	struct Scsi_Host *scsi_host = (struct Scsi_Host *) hba->hba_ptr;
+
+	scsi_host_put(scsi_host);
+
+	printk(KERN_INFO "CORE_HBA[%d] - Detached SCSI HBA: %s from"
+		" Generic Target Core\n", hba->hba_id,
+		(scsi_host->hostt->name) ? (scsi_host->hostt->name) :
+		"Unknown");
+
+	hba->hba_ptr = NULL;
+
+	return 0;
+}
+
+/*	pscsi_add_device_to_list():
+ *
+ *
+ */
+se_device_t *pscsi_add_device_to_list(
+	se_hba_t *hba,
+	se_subsystem_dev_t *se_dev,
+	pscsi_dev_virt_t *pdv,
+	struct scsi_device *sd,
+	int dev_flags)
+{
+	se_device_t *dev;
+
+	/*
+	 * Some pseudo SCSI HBAs do not fill in sector_size
+	 * correctly. (See ide-scsi.c)  So go ahead and setup sane
+	 * values.
+	 */
+	if (!sd->sector_size) {
+		switch (sd->type) {
+		case TYPE_DISK:
+			sd->sector_size = 512;
+			break;
+		case TYPE_ROM:
+			sd->sector_size = 2048;
+			break;
+		case TYPE_TAPE: /* The Tape may not be in the drive */
+			break;
+		case TYPE_MEDIUM_CHANGER: /* Control CDBs only */
+			break;
+		default:
+			printk(KERN_ERR "Unable to set sector_size for %d\n",
+					sd->type);
+			return NULL;
+		}
+
+		if (sd->sector_size) {
+			printk(KERN_ERR "Set broken SCSI Device"
+				" %d:%d:%d sector_size to %d\n", sd->channel,
+				sd->id, sd->lun, sd->sector_size);
+		}
+	}
+
+	if (!sd->queue_depth) {
+		sd->queue_depth = PSCSI_DEFAULT_QUEUEDEPTH;
+
+		printk(KERN_ERR "Set broken SCSI Device %d:%d:%d"
+			" queue_depth to %d\n", sd->channel, sd->id,
+				sd->lun, sd->queue_depth);
+	}
+	/*
+	 * Set the pointer pdv->pdv_sd to from passed struct scsi_device,
+	 * which has already been referenced with Linux SCSI code with
+	 * scsi_device_get() in this file's pscsi_create_virtdevice().
+	 *
+	 * The passthrough operations called by the transport_add_device_*
+	 * function below will require this pointer to be set for passthroug
+	 *  ops.
+	 *
+	 * For the shutdown case in pscsi_free_device(), this struct
+	 * scsi_device  reference is released with Linux SCSI code
+	 * scsi_device_put() and the pdv->pdv_sd cleared.
+	 */
+	pdv->pdv_sd = sd;
+
+	dev = transport_add_device_to_core_hba(hba, &pscsi_template,
+				se_dev, dev_flags, (void *)pdv);
+	if (!(dev)) {
+		pdv->pdv_sd = NULL;
+		return NULL;
+	}
+
+	/*
+	 * For TYPE_TAPE, attempt to determine blocksize with MODE_SENSE.
+	 */
+	if (sd->type == TYPE_TAPE) {
+		unsigned char *buf = NULL, cdb[SCSI_CDB_SIZE];
+		se_cmd_t *cmd;
+		u32 blocksize;
+
+		memset(cdb, 0, SCSI_CDB_SIZE);
+		cdb[0] = MODE_SENSE;
+		cdb[4] = 0x0c; /* 12 bytes */
+
+		cmd = transport_allocate_passthrough(&cdb[0],
+				SE_DIRECTION_READ, 0, NULL, 0, 12,
+				DEV_OBJ_API(dev), dev);
+		if (!(cmd)) {
+			printk(KERN_ERR "Unable to determine blocksize for"
+				" TYPE_TAPE\n");
+			goto out;
+		}
+
+		if (transport_generic_passthrough(cmd) < 0) {
+			printk(KERN_ERR "Unable to determine blocksize for"
+				" TYPE_TAPE\n");
+			goto out;
+		}
+
+		buf = (unsigned char *)T_TASK(cmd)->t_task_buf;
+		blocksize = (buf[9] << 16) | (buf[10] << 8) | (buf[11]);
+
+		/*
+		 * If MODE_SENSE still returns zero, set the default value
+		 * to 1024.
+		 */
+		sd->sector_size = blocksize;
+		if (!(sd->sector_size))
+			sd->sector_size = 1024;
+
+		transport_passthrough_release(cmd);
+	}
+out:
+	return dev;
+}
+
+int pscsi_claim_phydevice(se_hba_t *hba, se_device_t *dev)
+{
+	pscsi_dev_virt_t *pdv = (pscsi_dev_virt_t *) dev->dev_ptr;
+	struct scsi_device *sd = (struct scsi_device *)pdv->pdv_sd;
+
+	return pscsi_claim_sd(sd);
+}
+
+int pscsi_release_phydevice(se_device_t *dev)
+{
+	pscsi_dev_virt_t *pdv = (pscsi_dev_virt_t *) dev->dev_ptr;
+	struct scsi_device *sd = (struct scsi_device *)pdv->pdv_sd;
+
+	return pscsi_release_sd(sd);
+}
+
+void *pscsi_allocate_virtdevice(se_hba_t *hba, const char *name)
+{
+	pscsi_dev_virt_t *pdv;
+
+	pdv = kzalloc(sizeof(pscsi_dev_virt_t), GFP_KERNEL);
+	if (!(pdv)) {
+		printk(KERN_ERR "Unable to allocate memory for pscsi_dev_virt_t\n");
+		return NULL;
+	}
+	pdv->pdv_se_hba = hba;
+
+	printk(KERN_INFO "PSCSI: Allocated pdv: %p for %s\n", pdv, name);
+	return (void *)pdv;
+}
+
+/*
+ * Called with struct Scsi_Host->host_lock called.
+ */
+se_device_t *pscsi_create_type_disk(
+	struct scsi_device *sd,
+	pscsi_dev_virt_t *pdv,
+	se_subsystem_dev_t *se_dev,
+	se_hba_t *hba)
+{
+	se_device_t *dev;
+	struct Scsi_Host *sh = sd->host;
+	u32 dev_flags = 0;
+
+	if (scsi_device_get(sd)) {
+		printk(KERN_ERR "scsi_device_get() failed for %d:%d:%d:%d\n",
+			sh->host_no, sd->channel, sd->id, sd->lun);
+		spin_unlock_irq(sh->host_lock);
+		return NULL;
+	}
+	spin_unlock_irq(sh->host_lock);
+
+	if (pscsi_check_sd(sd) < 0) {
+		scsi_device_put(sd);
+		printk(KERN_ERR "pscsi_check_sd() failed for %d:%d:%d:%d\n",
+			sh->host_no, sd->channel, sd->id, sd->lun);
+		return NULL;
+	}
+	if (!(pscsi_claim_sd(sd))) {
+		dev_flags |= DF_CLAIMED_BLOCKDEV;
+		dev_flags |= DF_PERSISTENT_CLAIMED_BLOCKDEV;
+	}
+	dev = pscsi_add_device_to_list(hba, se_dev, pdv, sd, dev_flags);
+	if (!(dev)) {
+		scsi_device_put(sd);
+		return NULL;
+	}
+	printk(KERN_INFO "CORE_PSCSI[%d] - Added TYPE_DISK for %d:%d:%d\n",
+		sh->host_no, sd->channel, sd->id, sd->lun);
+
+	return dev;
+}
+
+/*
+ * Called with struct Scsi_Host->host_lock called.
+ */
+se_device_t *pscsi_create_type_rom(
+	struct scsi_device *sd,
+	pscsi_dev_virt_t *pdv,
+	se_subsystem_dev_t *se_dev,
+	se_hba_t *hba)
+{
+	se_device_t *dev;
+	struct Scsi_Host *sh = sd->host;
+	u32 dev_flags = 0;
+
+	if (scsi_device_get(sd)) {
+		printk(KERN_ERR "scsi_device_get() failed for %d:%d:%d:%d\n",
+			sh->host_no, sd->channel, sd->id, sd->lun);
+		spin_unlock_irq(sh->host_lock);
+		return NULL;
+	}
+	spin_unlock_irq(sh->host_lock);
+
+	dev = pscsi_add_device_to_list(hba, se_dev, pdv, sd, dev_flags);
+	if (!(dev)) {
+		scsi_device_put(sd);
+		return NULL;
+	}
+	printk(KERN_INFO "CORE_PSCSI[%d] - Added Type: %s for %d:%d:%d\n",
+		sh->host_no, scsi_device_type(sd->type), sd->channel,
+		sd->id, sd->lun);
+
+	return dev;
+}
+
+/*
+ *Called with struct Scsi_Host->host_lock called.
+ */
+se_device_t *pscsi_create_type_other(
+	struct scsi_device *sd,
+	pscsi_dev_virt_t *pdv,
+	se_subsystem_dev_t *se_dev,
+	se_hba_t *hba)
+{
+	se_device_t *dev;
+	struct Scsi_Host *sh = sd->host;
+	u32 dev_flags = 0;
+
+	spin_unlock_irq(sh->host_lock);
+	dev = pscsi_add_device_to_list(hba, se_dev, pdv, sd, dev_flags);
+	if (!(dev))
+		return NULL;
+
+	printk(KERN_INFO "CORE_PSCSI[%d] - Added Type: %s for %d:%d:%d\n",
+		sh->host_no, scsi_device_type(sd->type), sd->channel,
+		sd->id, sd->lun);
+
+	return dev;
+}
+
+se_device_t *pscsi_create_virtdevice(
+	se_hba_t *hba,
+	se_subsystem_dev_t *se_dev,
+	void *p)
+{
+	pscsi_dev_virt_t *pdv = (pscsi_dev_virt_t *)p;
+	se_device_t *dev;
+	struct scsi_device *sd;
+	struct Scsi_Host *sh = (struct Scsi_Host *) hba->hba_ptr;
+
+	if (!(pdv)) {
+		printk(KERN_ERR "Unable to locate pscsi_dev_virt_t"
+				" parameter\n");
+		return NULL;
+	}
+
+	spin_lock_irq(sh->host_lock);
+	list_for_each_entry(sd, &sh->__devices, siblings) {
+		if ((pdv->pdv_channel_id != sd->channel) ||
+		    (pdv->pdv_target_id != sd->id) ||
+		    (pdv->pdv_lun_id != sd->lun))
+			continue;
+		/*
+		 * Functions will release the held struct scsi_host->host_lock
+		 * before calling calling pscsi_check_sd() and
+		 * pscsi_add_device_to_list() to register struct scsi_device
+		 * with target_core_mod.
+		 */
+		switch (sd->type) {
+		case TYPE_DISK:
+			dev = pscsi_create_type_disk(sd, pdv, se_dev, hba);
+			break;
+		case TYPE_ROM:
+			dev = pscsi_create_type_rom(sd, pdv, se_dev, hba);
+			break;
+		default:
+			dev = pscsi_create_type_other(sd, pdv, se_dev, hba);
+			break;
+		}
+		if (!(dev)) {
+			pdv->pdv_sd = NULL;
+			return NULL;
+		}
+		return dev;
+	}
+	spin_unlock_irq(sh->host_lock);
+
+	printk(KERN_ERR "Unable to locate %d:%d:%d:%d\n", sh->host_no,
+		pdv->pdv_channel_id,  pdv->pdv_target_id, pdv->pdv_lun_id);
+
+	return NULL;
+}
+
+/*	pscsi_activate_device(): (Part of se_subsystem_api_t template)
+ *
+ *
+ */
+int pscsi_activate_device(se_device_t *dev)
+{
+	pscsi_dev_virt_t *pdv = (pscsi_dev_virt_t *) dev->dev_ptr;
+	struct scsi_device *sd = (struct scsi_device *) pdv->pdv_sd;
+	struct Scsi_Host *sh = sd->host;
+
+	printk(KERN_INFO "CORE_PSCSI[%d] - Activating Device with TCQ: %d at"
+		" SCSI Location (Channel/Target/LUN) %d/%d/%d\n", sh->host_no,
+		 sd->queue_depth, sd->channel, sd->id, sd->lun);
+
+	return 0;
+}
+
+/*	pscsi_deactivate_device(): (Part of se_subsystem_api_t template)
+ *
+ *
+ */
+void pscsi_deactivate_device(se_device_t *dev)
+{
+	pscsi_dev_virt_t *pdv = (pscsi_dev_virt_t *) dev->dev_ptr;
+	struct scsi_device *sd = (struct scsi_device *) pdv->pdv_sd;
+	struct Scsi_Host *sh = sd->host;
+
+	printk(KERN_INFO "CORE_PSCSI[%d] - Deactivating Device with TCQ: %d at"
+		" SCSI Location (Channel/Target/LUN) %d/%d/%d\n", sh->host_no,
+		sd->queue_depth, sd->channel, sd->id, sd->lun);
+}
+
+/*	pscsi_free_device(): (Part of se_subsystem_api_t template)
+ *
+ *
+ */
+void pscsi_free_device(void *p)
+{
+	pscsi_dev_virt_t *pdv = (pscsi_dev_virt_t *) p;
+	struct scsi_device *sd = (struct scsi_device *) pdv->pdv_sd;
+
+	if (sd) {
+		if ((sd->type == TYPE_DISK) || (sd->type == TYPE_ROM))
+			scsi_device_put(sd);
+
+		pdv->pdv_sd = NULL;
+	}
+
+	kfree(pdv);
+}
+
+/*	pscsi_transport_complete():
+ *
+ *
+ */
+int pscsi_transport_complete(se_task_t *task)
+{
+	pscsi_dev_virt_t *pdv = (pscsi_dev_virt_t *) task->se_dev->dev_ptr;
+	struct scsi_device *sd = (struct scsi_device *) pdv->pdv_sd;
+	int result;
+	pscsi_plugin_task_t *pt = (pscsi_plugin_task_t *) task->transport_req;
+	unsigned char *cdb = &pt->pscsi_cdb[0];
+
+	result = pt->pscsi_result;
+
+# ifdef LINUX_VPD_PAGE_CHECK
+	if ((cdb[0] == INQUIRY) && host_byte(result) == DID_OK) {
+		u32 len = 0;
+		unsigned char *dst = (unsigned char *)
+				T_TASK(task->task_se_cmd)->t_task_buf;
+		unsigned char buf[VPD_BUF_LEN], *iqn = NULL;
+		se_subsystem_dev_t *su_dev = TASK_DEV(task)->se_sub_dev;
+		se_hba_t *hba = task->se_dev->se_hba;
+
+		/*
+		 * The Initiator port did not request VPD information.
+		 */
+		if (!(cdb[1] & 0x1)) {
+			task->task_scsi_status = GOOD;
+			return 0;
+		}
+
+		/*
+		 * Assume the SCSI Device did the right thing if an VPD length
+		 * is provided in the INQUIRY response payload.
+		 */
+		if (dst[3] != 0x00) {
+			su_dev->su_dev_flags |= SDF_FIRMWARE_VPD_UNIT_SERIAL;
+			su_dev->su_dev_flags &= ~SDF_EMULATED_VPD_UNIT_SERIAL;
+			task->task_scsi_status = GOOD;
+			return 0;
+		}
+
+		memset(buf, 0, VPD_BUF_LEN);
+		memset(dst, 0, task->task_size);
+		buf[0] = sd->type;
+
+		switch (cdb[2]) {
+		case 0x00:
+			buf[1] = 0x00;
+			buf[3] = 3;
+			buf[4] = 0x0;
+			buf[5] = 0x80;
+			buf[6] = 0x83;
+			len = 3;
+			break;
+		case 0x80:
+			buf[1] = 0x80;
+			if (su_dev->su_dev_flags &
+					SDF_EMULATED_VPD_UNIT_SERIAL)
+				len += sprintf((unsigned char *)&buf[4], "%s",
+					&su_dev->t10_wwn.unit_serial[0]);
+			else {
+				iqn = transport_get_iqn_sn();
+				len += sprintf((unsigned char *)&buf[4],
+					"%s:%u_%u_%u_%u", iqn, hba->hba_id,
+					sd->channel, sd->id, sd->lun);
+			}
+			buf[3] = len;
+			break;
+		case 0x83:
+			buf[1] = 0x83;
+			/* Start Identifier Page */
+			buf[4] = 0x2; /* ASCII */
+			buf[5] = 0x1;
+			buf[6] = 0x0;
+			len += sprintf((unsigned char *)&buf[8], "LIO-ORG");
+
+			if (su_dev->su_dev_flags &
+					SDF_EMULATED_VPD_UNIT_SERIAL) {
+				len += sprintf((unsigned char *)&buf[16],
+					"PSCSI:%s",
+					&su_dev->t10_wwn.unit_serial[0]);
+			} else {
+				iqn = transport_get_iqn_sn();
+				len += sprintf((unsigned char *)&buf[16],
+					"PSCSI:%s:%u_%u_%u_%u", iqn,
+					hba->hba_id, sd->channel,
+					sd->id, sd->lun);
+			}
+			buf[7] = len; /* Identifer Length */
+			len += 4;
+			buf[3] = len; /* Page Length */
+			break;
+		default:
+			break;
+		}
+
+		if ((len + 4) > task->task_size) {
+			printk(KERN_ERR "Inquiry VPD Length: %u larger than"
+				" req->sr_bufflen: %u\n", (len + 4),
+				task->task_size);
+			memcpy(dst, buf, task->task_size);
+		} else
+			memcpy(dst, buf, (len + 4));
+
+		/*
+		 * Fake the GOOD SAM status here too.
+		 */
+		task->task_scsi_status = GOOD;
+		return 0;
+	}
+
+# endif /* LINUX_VPD_PAGE_CHECK */
+
+	/*
+	 * Hack to make sure that Write-Protect modepage is set if R/O mode is
+	 * forced.
+	 */
+	if (((cdb[0] == MODE_SENSE) || (cdb[0] == MODE_SENSE_10)) &&
+	     (status_byte(result) << 1) == SAM_STAT_GOOD) {
+		if (!TASK_CMD(task)->se_deve)
+			goto after_mode_sense;
+
+		if (TASK_CMD(task)->se_deve->lun_flags &
+				TRANSPORT_LUNFLAGS_READ_ONLY) {
+			unsigned char *buf = (unsigned char *)
+				T_TASK(task->task_se_cmd)->t_task_buf;
+
+			if (cdb[0] == MODE_SENSE_10) {
+				if (!(buf[3] & 0x80))
+					buf[3] |= 0x80;
+			} else {
+				if (!(buf[2] & 0x80))
+					buf[2] |= 0x80;
+			}
+		}
+	}
+after_mode_sense:
+
+	if (sd->type != TYPE_TAPE)
+		goto after_mode_select;
+
+	/*
+	 * Hack to correctly obtain the initiator requested blocksize for
+	 * TYPE_TAPE.  Since this value is dependent upon each tape media,
+	 * struct scsi_device->sector_size will not contain the correct value
+	 * by default, so we go ahead and set it so
+	 * TRANSPORT(dev)->get_blockdev() returns the correct value to the
+	 * storage engine.
+	 */
+	if (((cdb[0] == MODE_SELECT) || (cdb[0] == MODE_SELECT_10)) &&
+	      (status_byte(result) << 1) == SAM_STAT_GOOD) {
+		unsigned char *buf;
+		struct scatterlist *sg = task->task_sg;
+		u16 bdl;
+		u32 blocksize;
+
+		buf = sg_virt(&sg[0]);
+		if (!(buf)) {
+			printk(KERN_ERR "Unable to get buf for scatterlist\n");
+			goto after_mode_select;
+		}
+
+		if (cdb[0] == MODE_SELECT)
+			bdl = (buf[3]);
+		else
+			bdl = (buf[6] << 8) | (buf[7]);
+
+		if (!bdl)
+			goto after_mode_select;
+
+		if (cdb[0] == MODE_SELECT)
+			blocksize = (buf[9] << 16) | (buf[10] << 8) |
+					(buf[11]);
+		else
+			blocksize = (buf[13] << 16) | (buf[14] << 8) |
+					(buf[15]);
+
+		sd->sector_size = blocksize;
+	}
+after_mode_select:
+
+	if (status_byte(result) & CHECK_CONDITION)
+		return 1;
+
+	return 0;
+}
+
+/*	pscsi_allocate_request(): (Part of se_subsystem_api_t template)
+ *
+ *
+ */
+void *pscsi_allocate_request(
+	se_task_t *task,
+	se_device_t *dev)
+{
+	pscsi_plugin_task_t *pt;
+
+	pt = kzalloc(sizeof(pscsi_plugin_task_t), GFP_KERNEL);
+	if (!(pt)) {
+		printk(KERN_ERR "Unable to allocate pscsi_plugin_task_t\n");
+		return NULL;
+	}
+
+	return pt;
+}
+
+static inline void pscsi_blk_init_request(
+	se_task_t *task,
+	pscsi_plugin_task_t *pt)
+{
+	/*
+	 * Defined as "scsi command" in include/linux/blkdev.h.
+	 */
+	pt->pscsi_req->cmd_type = REQ_TYPE_BLOCK_PC;
+	/*
+	 * Setup the done function pointer for struct request,
+	 * also set the end_io_data pointer.to se_task_t.
+	 */
+	pt->pscsi_req->end_io = pscsi_req_done;
+	pt->pscsi_req->end_io_data = (void *)task;
+	/*
+	 * Load the referenced se_task_t's SCSI CDB into
+	 * include/linux/blkdev.h:struct request->cmd
+	 */
+	pt->pscsi_req->cmd_len = COMMAND_SIZE(pt->pscsi_cdb[0]);
+	memcpy(pt->pscsi_req->cmd, pt->pscsi_cdb, pt->pscsi_req->cmd_len);
+	/*
+	 * Setup pointer for outgoing sense data.
+	 */
+	pt->pscsi_req->sense = (void *)&pt->pscsi_sense[0];
+	pt->pscsi_req->sense_len = 0;
+}
+
+/*
+ * Used for pSCSI data payloads for all *NON* SCF_SCSI_DATA_SG_IO_CDB
+*/
+static int pscsi_blk_get_request(se_task_t *task)
+{
+	pscsi_plugin_task_t *pt = (pscsi_plugin_task_t *) task->transport_req;
+	pscsi_dev_virt_t *pdv = (pscsi_dev_virt_t *) task->se_dev->dev_ptr;
+
+	pt->pscsi_req = blk_get_request(pdv->pdv_sd->request_queue,
+			(pt->pscsi_direction == DMA_TO_DEVICE), GFP_KERNEL);
+	if (!(pt->pscsi_req) || IS_ERR(pt->pscsi_req)) {
+		printk(KERN_ERR "PSCSI: blk_get_request() failed: %ld\n",
+				IS_ERR(pt->pscsi_req));
+		return PYX_TRANSPORT_LU_COMM_FAILURE;
+	}
+	/*
+	 * Setup the newly allocated struct request for REQ_TYPE_BLOCK_PC,
+	 * and setup rq callback, CDB and sense.
+	 */
+	pscsi_blk_init_request(task, pt);
+	return 0;
+}
+
+/*      pscsi_do_task(): (Part of se_subsystem_api_t template)
+ *
+ *
+ */
+int pscsi_do_task(se_task_t *task)
+{
+	pscsi_plugin_task_t *pt = (pscsi_plugin_task_t *) task->transport_req;
+	pscsi_dev_virt_t *pdv = (pscsi_dev_virt_t *) task->se_dev->dev_ptr;
+	struct gendisk *gd = NULL;
+	/*
+	 * Grab pointer to struct gendisk for TYPE_DISK and TYPE_ROM
+	 * cases (eg: cases where struct scsi_device has a backing
+	 * struct block_device.  Also set the struct request->timeout
+	 * value based on peripheral device type (from SCSI).
+	 */
+	if (pdv->pdv_sd->type == TYPE_DISK) {
+		struct scsi_disk *sdisk = dev_get_drvdata(
+					&pdv->pdv_sd->sdev_gendev);
+		gd = sdisk->disk;
+		pt->pscsi_req->timeout = PS_TIMEOUT_DISK;
+	} else
+		pt->pscsi_req->timeout = PS_TIMEOUT_OTHER;
+
+	pt->pscsi_req->retries = PS_RETRY;
+	/*
+	 * Queue the struct request into the struct scsi_device->request_queue.
+	 */
+	blk_execute_rq_nowait(pdv->pdv_sd->request_queue, gd,
+			      pt->pscsi_req, 1, pscsi_req_done);
+
+	return PYX_TRANSPORT_SENT_TO_TRANSPORT;
+}
+
+/*	pscsi_free_task(): (Part of se_subsystem_api_t template)
+ *
+ *
+ */
+void pscsi_free_task(se_task_t *task)
+{
+	pscsi_plugin_task_t *pt = (pscsi_plugin_task_t *)task->transport_req;
+	/*
+	 * We do not release the bio(s) here associated with this task, as
+	 * this is handled by bio_put() and pscsi_bi_endio().
+	 */
+	kfree(pt);
+}
+
+ssize_t pscsi_set_configfs_dev_params(se_hba_t *hba,
+	se_subsystem_dev_t *se_dev,
+	const char *page,
+	ssize_t count)
+{
+	pscsi_dev_virt_t *pdv = (pscsi_dev_virt_t *) se_dev->se_dev_su_ptr;
+	struct Scsi_Host *sh = (struct Scsi_Host *) hba->hba_ptr;
+	char *buf, *cur, *ptr, *ptr2;
+	unsigned long scsi_channel_id, scsi_target_id, scsi_lun_id;
+	int params = 0, ret;
+
+	buf = kzalloc(count, GFP_KERNEL);
+	if (!(buf)) {
+		printk(KERN_ERR "Unable to allocate memory for temporary"
+				" buffer\n");
+		return -ENOMEM;
+	}
+	memcpy(buf, page, count);
+	cur = buf;
+
+	while (cur) {
+		ptr = strstr(cur, "=");
+		if (!(ptr))
+			goto out;
+
+		*ptr = '\0';
+		ptr++;
+
+		ptr2 = strstr(cur, "scsi_channel_id");
+		if ((ptr2)) {
+			transport_check_dev_params_delim(ptr, &cur);
+			ret = strict_strtoul(ptr, 0, &scsi_channel_id);
+			if (ret < 0) {
+				printk(KERN_ERR "strict_strtoul() failed for"
+					" scsi_channel_id=\n");
+				break;
+			}
+			pdv->pdv_channel_id = (int)scsi_channel_id;
+			printk(KERN_INFO "PSCSI[%d]: Referencing SCSI Channel"
+				" ID: %d\n",  sh->host_no, pdv->pdv_channel_id);
+			pdv->pdv_flags |= PDF_HAS_CHANNEL_ID;
+			params++;
+			continue;
+		}
+		ptr2 = strstr(cur, "scsi_target_id");
+		if ((ptr2)) {
+			transport_check_dev_params_delim(ptr, &cur);
+			ret = strict_strtoul(ptr, 0, &scsi_target_id);
+			if (ret < 0) {
+				printk("strict_strtoul() failed for"
+					" strict_strtoul()\n");
+				break;
+			}
+			pdv->pdv_target_id = (int)scsi_target_id;
+			printk(KERN_INFO "PSCSI[%d]: Referencing SCSI Target"
+				" ID: %d\n", sh->host_no, pdv->pdv_target_id);
+			pdv->pdv_flags |= PDF_HAS_TARGET_ID;
+			params++;
+			continue;
+		}
+		ptr2 = strstr(cur, "scsi_lun_id");
+		if ((ptr2)) {
+			transport_check_dev_params_delim(ptr, &cur);
+			ret = strict_strtoul(ptr, 0, &scsi_lun_id);
+			if (ret < 0) {
+				printk("strict_strtoul() failed for"
+					" scsi_lun_id=\n");
+				break;
+			}
+			pdv->pdv_lun_id = (int)scsi_lun_id;
+			printk(KERN_INFO "PSCSI[%d]: Referencing SCSI LUN ID:"
+				" %d\n", sh->host_no, pdv->pdv_lun_id);
+			pdv->pdv_flags |= PDF_HAS_LUN_ID;
+			params++;
+		} else
+			cur = NULL;
+	}
+
+out:
+	kfree(buf);
+	return (params) ? count : -EINVAL;
+}
+
+ssize_t pscsi_check_configfs_dev_params(
+	se_hba_t *hba,
+	se_subsystem_dev_t *se_dev)
+{
+	pscsi_dev_virt_t *pdv = (pscsi_dev_virt_t *) se_dev->se_dev_su_ptr;
+
+	if (!(pdv->pdv_flags & PDF_HAS_CHANNEL_ID) ||
+	    !(pdv->pdv_flags & PDF_HAS_TARGET_ID) ||
+	    !(pdv->pdv_flags & PDF_HAS_TARGET_ID)) {
+		printk(KERN_ERR "Missing scsi_channel_id=, scsi_target_id= and"
+			" scsi_lun_id= parameters\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+ssize_t pscsi_show_configfs_dev_params(
+	se_hba_t *hba,
+	se_subsystem_dev_t *se_dev,
+	char *page)
+{
+	pscsi_dev_virt_t *pdv = (pscsi_dev_virt_t *) se_dev->se_dev_su_ptr;
+	int bl = 0;
+
+	__pscsi_get_dev_info(pdv, page, &bl);
+	return (ssize_t)bl;
+}
+
+se_device_t *pscsi_create_virtdevice_from_fd(
+	se_subsystem_dev_t *se_dev,
+	const char *page)
+{
+	pscsi_dev_virt_t *pdv = (pscsi_dev_virt_t *) se_dev->se_dev_su_ptr;
+	se_device_t *dev = NULL;
+	se_hba_t *hba = se_dev->se_dev_hba;
+	struct block_device *bd = NULL;
+	struct file *filp;
+	struct gendisk *gd = NULL;
+	struct inode *inode;
+	struct scsi_device *sd = NULL;
+	struct Scsi_Host *sh = (struct Scsi_Host *)hba->hba_ptr;
+	char *p = (char *)page;
+	unsigned long long fd;
+	int ret;
+
+	ret = strict_strtoull(p, 0, (unsigned long long *)&fd);
+	if (ret < 0) {
+		printk(KERN_ERR "strict_strtol() failed for fd\n");
+		return ERR_PTR(-EINVAL);
+	}
+	if ((fd < 3 || fd > 7)) {
+		printk(KERN_ERR "PSCSI: Illegal value of file descriptor:"
+			" %llu\n", fd);
+		return ERR_PTR(-EINVAL);
+	}
+	filp = fget(fd);
+	if (!(filp)) {
+		printk(KERN_ERR "PSCSI: Unable to fget() fd: %llu\n", fd);
+		return ERR_PTR(-EBADF);
+	}
+	inode = igrab(filp->f_mapping->host);
+	if (!(inode)) {
+		printk(KERN_ERR "PSCSI: Unable to locate struct inode for"
+			" struct block_device fd\n");
+		fput(filp);
+		return ERR_PTR(-EINVAL);
+	}
+	/*
+	 * Look for struct scsi_device with a backing struct block_device.
+	 *
+	 * This means struct scsi_device->type == TYPE_DISK && TYPE_ROM.
+	 */
+	if (S_ISBLK(inode->i_mode)) {
+		bd = I_BDEV(filp->f_mapping->host);
+		if (!(bd)) {
+			printk(KERN_ERR "PSCSI: Unable to locate struct"
+				" block_device from I_BDEV()\n");
+			iput(inode);
+			fput(filp);
+			return ERR_PTR(-EINVAL);
+		}
+		gd = bd->bd_disk;
+		if (!(gd)) {
+			printk(KERN_ERR "PSCSI: Unable to locate struct gendisk"
+				" from struct block_device\n");
+			iput(inode);
+			fput(filp);
+			return ERR_PTR(-EINVAL);
+		}
+		/*
+		 * This struct gendisk->driver_fs() is marked as "// remove'
+		 * in include/linux/genhd.h..
+		 *
+		 * Currently in drivers/scsi/s[d,r].c:s[d,r]_probe(), this
+		 * pointer gets set by struct scsi_device->sdev_gendev.
+		 *
+		 * Is there a better way to locate struct scsi_device from
+		 * struct inode..?
+		 */
+		if (!(gd->driverfs_dev)) {
+			printk(KERN_ERR "PSCSI: struct gendisk->driverfs_dev"
+					" is NULL!\n");
+			iput(inode);
+			fput(filp);
+			return ERR_PTR(-EINVAL);
+		}
+		sd = to_scsi_device(gd->driverfs_dev);
+		if (!(sd)) {
+			printk(KERN_ERR "PSCSI: Unable to locate struct"
+				" scsi_device from struct gendisk->"
+				"driverfs_dev\n");
+			iput(inode);
+			fput(filp);
+			return ERR_PTR(-EINVAL);
+		}
+		if (sd->host != sh) {
+			printk(KERN_ERR "PSCSI: Trying to attach scsi_device"
+				" Host ID: %d, but se_hba_t has SCSI Host ID:"
+				" %d\n", sd->host->host_no, sh->host_no);
+			iput(inode);
+			fput(filp);
+			return ERR_PTR(-EINVAL);
+		}
+		/*
+		 * pscsi_create_type_[disk,rom]() will release host_lock..
+		 */
+		spin_lock_irq(sh->host_lock);
+		switch (sd->type) {
+		case TYPE_DISK:
+			dev = pscsi_create_type_disk(sd, pdv, se_dev,
+					se_dev->se_dev_hba);
+			break;
+		case TYPE_ROM:
+			dev = pscsi_create_type_rom(sd, pdv, se_dev,
+					se_dev->se_dev_hba);
+			break;
+		default:
+			printk(KERN_ERR "PSCSI: Unable to handle type S_ISBLK()"
+				" == TRUE Type: %s\n",
+				scsi_device_type(sd->type));
+			spin_unlock_irq(sh->host_lock);
+			iput(inode);
+			fput(filp);
+			return ERR_PTR(-ENOSYS);
+		}
+	} else if (S_ISCHR(inode->i_mode)) {
+		/*
+		 * FIXME: Figure how to get struct scsi_device from character
+		 * device's struct inode.
+		 */
+		printk(KERN_ERR "SCSI Character Device unsupported via"
+			" configfs/fd  method.  Use configfs/control"
+				" instead\n");
+		iput(inode);
+		fput(filp);
+		return ERR_PTR(-ENOSYS);
+	} else {
+		printk(KERN_ERR "PSCSI: File destriptor is not SCSI block or"
+			" character device, ignoring\n");
+		iput(inode);
+		fput(filp);
+		return ERR_PTR(-ENODEV);
+	}
+
+	iput(inode);
+	fput(filp);
+	return dev;
+}
+
+void pscsi_get_plugin_info(void *p, char *b, int *bl)
+{
+	*bl += sprintf(b + *bl, "%s SCSI Plugin %s\n",
+		PYX_ISCSI_VENDOR, PSCSI_VERSION);
+}
+
+void pscsi_get_hba_info(se_hba_t *hba, char *b, int *bl)
+{
+	struct Scsi_Host *sh = (struct Scsi_Host *) hba->hba_ptr;
+
+	*bl += sprintf(b + *bl, "Core Host ID: %u  SCSI Host ID: %u\n",
+			 hba->hba_id, sh->host_no);
+	*bl += sprintf(b + *bl, "        SCSI HBA: %s  <local>\n",
+		(sh->hostt->name) ? (sh->hostt->name) : "Unknown");
+}
+
+void pscsi_get_dev_info(se_device_t *dev, char *b, int *bl)
+{
+	pscsi_dev_virt_t *pdv = (pscsi_dev_virt_t *) dev->dev_ptr;
+
+	__pscsi_get_dev_info(pdv, b, bl);
+}
+
+void __pscsi_get_dev_info(pscsi_dev_virt_t *pdv, char *b, int *bl)
+{
+	int i;
+	struct scsi_device *sd = (struct scsi_device *) pdv->pdv_sd;
+
+	*bl += sprintf(b + *bl, "SCSI Device Bus Location:"
+		" Channel ID: %d Target ID: %d LUN: %d\n",
+		pdv->pdv_channel_id, pdv->pdv_target_id, pdv->pdv_lun_id);
+
+	if (sd) {
+		*bl += sprintf(b + *bl, "        ");
+		*bl += sprintf(b + *bl, "Vendor: ");
+		for (i = 0; i < 8; i++) {
+			if (ISPRINT(sd->vendor[i]))   /* printable character? */
+				*bl += sprintf(b + *bl, "%c", sd->vendor[i]);
+			else
+				*bl += sprintf(b + *bl, " ");
+		}
+		*bl += sprintf(b + *bl, " Model: ");
+		for (i = 0; i < 16; i++) {
+			if (ISPRINT(sd->model[i]))   /* printable character ? */
+				*bl += sprintf(b + *bl, "%c", sd->model[i]);
+			else
+				*bl += sprintf(b + *bl, " ");
+		}
+		*bl += sprintf(b + *bl, " Rev: ");
+		for (i = 0; i < 4; i++) {
+			if (ISPRINT(sd->rev[i]))   /* printable character ? */
+				*bl += sprintf(b + *bl, "%c", sd->rev[i]);
+			else
+				*bl += sprintf(b + *bl, " ");
+		}
+
+		if (sd->type == TYPE_DISK) {
+			struct scsi_disk *sdisk =
+					dev_get_drvdata(&sd->sdev_gendev);
+			struct gendisk *disk = (struct gendisk *) sdisk->disk;
+			struct block_device *bdev = bdget(MKDEV(disk->major,
+						disk->first_minor));
+
+			bdev->bd_disk = disk;
+			*bl += sprintf(b + *bl, "   %s\n", (!bdev->bd_holder) ?
+					"" : (bdev->bd_holder ==
+					(struct scsi_device *)sd) ?
+					"CLAIMED: PSCSI" : "CLAIMED: OS");
+		} else
+			*bl += sprintf(b + *bl, "\n");
+	}
+
+	return;
+}
+
+static void pscsi_bi_endio(struct bio *bio, int error)
+{
+	bio_put(bio);
+}
+
+static inline struct bio *pscsi_get_bio(pscsi_dev_virt_t *pdv, int sg_num)
+{
+	struct bio *bio;
+	/*
+	 * Use bio_malloc() following the comment in for bio -> struct request
+	 * in block/blk-core.c:blk_make_request()
+	 */
+	bio = bio_kmalloc(GFP_KERNEL, sg_num);
+	if (!(bio)) {
+		printk(KERN_ERR "PSCSI: bio_kmalloc() failed\n");
+		return NULL;
+	}
+	bio->bi_end_io = pscsi_bi_endio;
+
+	return bio;
+}
+
+#if 0
+#define DEBUG_PSCSI(x...) printk(x)
+#else
+#define DEBUG_PSCSI(x...)
+#endif
+
+/*      pscsi_map_task_SG():
+ *
+ *
+ */
+int pscsi_map_task_SG(se_task_t *task)
+{
+	pscsi_plugin_task_t *pt = (pscsi_plugin_task_t *) task->transport_req;
+	pscsi_dev_virt_t *pdv = (pscsi_dev_virt_t *) task->se_dev->dev_ptr;
+	struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
+	struct page *page;
+	struct scatterlist *sg;
+	u32 data_len = task->task_size, i, len, bytes, off;
+	int nr_pages = (task->task_size + task->task_sg[0].offset +
+			PAGE_SIZE - 1) >> PAGE_SHIFT;
+	int nr_vecs = 0, ret = 0;
+	int rw = (TASK_CMD(task)->data_direction == SE_DIRECTION_WRITE);
+
+	if (!task->task_size)
+		return 0;
+	/*
+	 * For SCF_SCSI_DATA_SG_IO_CDB, Use fs/bio.c:bio_add_page() to setup
+	 * the bio_vec maplist from LIO-SE se_mem_t -> task->task_sg ->
+	 * struct scatterlist memory.  The se_task_t->task_sg[] currently needs
+	 * to be attached to struct bios for submission to Linux/SCSI using
+	 * struct request to struct scsi_device->request_queue.
+	 *
+	 * Note that this will be changing post v2.6.28 as Target_Core_Mod/pSCSI
+	 * is ported to upstream SCSI passthrough functionality that accepts
+	 * struct scatterlist->page_link or struct page as a paraemeter.
+	 */
+	DEBUG_PSCSI("PSCSI: nr_pages: %d\n", nr_pages);
+
+	for_each_sg(task->task_sg, sg, task->task_sg_num, i) {
+		page = sg_page(sg);
+		off = sg->offset;
+		len = sg->length;
+
+		DEBUG_PSCSI("PSCSI: i: %d page: %p len: %d off: %d\n", i,
+			page, len, off);
+
+		while (len > 0 && data_len > 0) {
+			bytes = min_t(unsigned int, len, PAGE_SIZE - off);
+			bytes = min(bytes, data_len);
+
+			if (!(bio)) {
+				nr_vecs = min_t(int, BIO_MAX_PAGES, nr_pages);
+				nr_pages -= nr_vecs;
+				/*
+				 * Calls bio_kmalloc() and sets bio->bi_end_io()
+				 */
+				bio = pscsi_get_bio(pdv, nr_vecs);
+				if (!(bio))
+					goto fail;
+				/*
+				 * FIXME: Use bio_set_dir() when avaliable
+				 */
+				if (rw)
+					bio->bi_rw |= (1 << BIO_RW);
+
+				DEBUG_PSCSI("PSCSI: Allocated bio: %p,"
+					" dir: %s nr_vecs: %d\n", bio,
+					(rw) ? "rw" : "r", nr_vecs);
+				/*
+				 * Set *hbio pointer to handle the case:
+				 * nr_pages > BIO_MAX_PAGES, where additional
+				 * bios need to be added to complete a given
+				 * se_task_t
+				 */
+				if (!hbio)
+					hbio = tbio = bio;
+				else
+					tbio = tbio->bi_next = bio;
+			}
+
+			DEBUG_PSCSI("PSCSI: Calling bio_add_pc_page() i: %d"
+				" bio: %p page: %p len: %d off: %d\n", i, bio,
+				page, len, off);
+
+			ret = bio_add_pc_page(pdv->pdv_sd->request_queue,
+					bio, page, bytes, off);
+			if (ret != bytes)
+				goto fail;
+
+			DEBUG_PSCSI("PSCSI: bio->bi_vcnt: %d nr_vecs: %d\n",
+				bio->bi_vcnt, nr_vecs);
+
+			if (bio->bi_vcnt > nr_vecs) {
+				DEBUG_PSCSI("PSCSI: Reached bio->bi_vcnt max:"
+					" %d i: %d bio: %p, allocating another"
+					" bio\n", bio->bi_vcnt, i, bio);
+				/*
+				 * Clear the pointer so that another bio will
+				 * be allocated with pscsi_get_bio() above, the
+				 * current bio has already been set *tbio and
+				 * bio->bi_next.
+				 */
+				bio = NULL;
+			}
+
+			page++;
+			len -= bytes;
+			data_len -= bytes;
+			off = 0;
+		}
+	}
+	/*
+	 * Starting with v2.6.31, call blk_make_request() passing in *hbio to
+	 * allocate the pSCSI task a struct request.
+	 */
+	pt->pscsi_req = blk_make_request(pdv->pdv_sd->request_queue,
+				hbio, GFP_KERNEL);
+	if (!(pt->pscsi_req)) {
+		printk(KERN_ERR "pSCSI: blk_make_request() failed\n");
+		goto fail;
+	}
+	/*
+	 * Setup the newly allocated struct request for REQ_TYPE_BLOCK_PC,
+	 * and setup rq callback, CDB and sense.
+	 */
+	pscsi_blk_init_request(task, pt);
+
+	return task->task_sg_num;
+fail:
+	while (hbio) {
+		bio = hbio;
+		hbio = hbio->bi_next;
+		bio->bi_next = NULL;
+		bio_endio(bio, 0);
+	}
+	return ret;
+}
+
+/*	pscsi_map_task_non_SG():
+ *
+ *
+ */
+int pscsi_map_task_non_SG(se_task_t *task)
+{
+	se_cmd_t *cmd = TASK_CMD(task);
+	pscsi_plugin_task_t *pt = (pscsi_plugin_task_t *) task->transport_req;
+	pscsi_dev_virt_t *pdv = (pscsi_dev_virt_t *) task->se_dev->dev_ptr;
+	int ret = 0;
+
+	if (!task->task_size)
+		return 0;
+
+	ret = blk_rq_map_kern(pdv->pdv_sd->request_queue,
+			pt->pscsi_req, T_TASK(cmd)->t_task_buf,
+			task->task_size, GFP_KERNEL);
+	if (ret < 0) {
+		printk(KERN_ERR "PSCSI: blk_rq_map_kern() failed: %d\n", ret);
+		return PYX_TRANSPORT_LU_COMM_FAILURE;
+	}
+	return 0;
+}
+
+/*	pscsi_CDB_inquiry():
+ *
+ *
+ */
+int pscsi_CDB_inquiry(se_task_t *task, u32 size)
+{
+	pscsi_plugin_task_t *pt = (pscsi_plugin_task_t *) task->transport_req;
+
+	pt->pscsi_direction = DMA_FROM_DEVICE;
+	if (pscsi_blk_get_request(task) < 0)
+		return -1;
+
+	return pscsi_map_task_non_SG(task);
+}
+
+int pscsi_CDB_none(se_task_t *task, u32 size)
+{
+	pscsi_plugin_task_t *pt = (pscsi_plugin_task_t *) task->transport_req;
+
+	pt->pscsi_direction = DMA_NONE;
+
+	return pscsi_blk_get_request(task);
+}
+
+/*	pscsi_CDB_read_non_SG():
+ *
+ *
+ */
+int pscsi_CDB_read_non_SG(se_task_t *task, u32 size)
+{
+	pscsi_plugin_task_t *pt = (pscsi_plugin_task_t *) task->transport_req;
+
+	pt->pscsi_direction = DMA_FROM_DEVICE;
+
+	if (pscsi_blk_get_request(task) < 0)
+		return PYX_TRANSPORT_LU_COMM_FAILURE;
+
+	return pscsi_map_task_non_SG(task);
+}
+
+/*	pscsi_CDB_read_SG():
+ *
+ *
+ */
+int pscsi_CDB_read_SG(se_task_t *task, u32 size)
+{
+	pscsi_plugin_task_t *pt = (pscsi_plugin_task_t *) task->transport_req;
+
+	pt->pscsi_direction = DMA_FROM_DEVICE;
+	/*
+	 * pscsi_map_task_SG() calls block/blk-core.c:blk_make_request()
+	 * for >= v2.6.31 pSCSI
+	 */
+	if (pscsi_map_task_SG(task) < 0)
+		return PYX_TRANSPORT_LU_COMM_FAILURE;
+
+	return task->task_sg_num;
+}
+
+/*	pscsi_CDB_write_non_SG():
+ *
+ *
+ */
+int pscsi_CDB_write_non_SG(se_task_t *task, u32 size)
+{
+	pscsi_plugin_task_t *pt = (pscsi_plugin_task_t *) task->transport_req;
+
+	pt->pscsi_direction = DMA_TO_DEVICE;
+
+	if (pscsi_blk_get_request(task) < 0)
+		return PYX_TRANSPORT_LU_COMM_FAILURE;
+
+	return pscsi_map_task_non_SG(task);
+}
+
+/*	pscsi_CDB_write_SG():
+ *
+ *
+ */
+int pscsi_CDB_write_SG(se_task_t *task, u32 size)
+{
+	pscsi_plugin_task_t *pt = (pscsi_plugin_task_t *) task->transport_req;
+
+	pt->pscsi_direction = DMA_TO_DEVICE;
+	/*
+	 * pscsi_map_task_SG() calls block/blk-core.c:blk_make_request()
+	 * for >= v2.6.31 pSCSI
+	 */
+	if (pscsi_map_task_SG(task) < 0)
+		return PYX_TRANSPORT_LU_COMM_FAILURE;
+
+	return task->task_sg_num;
+}
+
+/*	pscsi_check_lba():
+ *
+ *
+ */
+int pscsi_check_lba(unsigned long long lba, se_device_t *dev)
+{
+	return 0;
+}
+
+/*	pscsi_check_for_SG():
+ *
+ *
+ */
+int pscsi_check_for_SG(se_task_t *task)
+{
+	return task->task_sg_num;
+}
+
+/*	pscsi_get_cdb():
+ *
+ *
+ */
+unsigned char *pscsi_get_cdb(se_task_t *task)
+{
+	pscsi_plugin_task_t *pt = (pscsi_plugin_task_t *) task->transport_req;
+
+	return pt->pscsi_cdb;
+}
+
+/*	pscsi_get_sense_buffer():
+ *
+ *
+ */
+unsigned char *pscsi_get_sense_buffer(se_task_t *task)
+{
+	pscsi_plugin_task_t *pt = (pscsi_plugin_task_t *) task->transport_req;
+
+	return (unsigned char *)&pt->pscsi_sense[0];
+}
+
+/*	pscsi_get_blocksize():
+ *
+ *
+ */
+u32 pscsi_get_blocksize(se_device_t *dev)
+{
+	pscsi_dev_virt_t *pdv = (pscsi_dev_virt_t *) dev->dev_ptr;
+	struct scsi_device *sd = (struct scsi_device *) pdv->pdv_sd;
+
+	return sd->sector_size;
+}
+
+/*	pscsi_get_device_rev():
+ *
+ *
+ */
+u32 pscsi_get_device_rev(se_device_t *dev)
+{
+	pscsi_dev_virt_t *pdv = (pscsi_dev_virt_t *) dev->dev_ptr;
+	struct scsi_device *sd = (struct scsi_device *) pdv->pdv_sd;
+
+	return (sd->scsi_level - 1) ? sd->scsi_level - 1 : 1;
+}
+
+/*	pscsi_get_device_type():
+ *
+ *
+ */
+u32 pscsi_get_device_type(se_device_t *dev)
+{
+	pscsi_dev_virt_t *pdv = (pscsi_dev_virt_t *) dev->dev_ptr;
+	struct scsi_device *sd = (struct scsi_device *) pdv->pdv_sd;
+
+	return sd->type;
+}
+
+/*	pscsi_get_dma_length():
+ *
+ *
+ */
+u32 pscsi_get_dma_length(u32 task_size, se_device_t *dev)
+{
+	return PAGE_SIZE;
+}
+
+/*	pscsi_get_max_sectors():
+ *
+ *
+ */
+u32 pscsi_get_max_sectors(se_device_t *dev)
+{
+	pscsi_dev_virt_t *pdv = (pscsi_dev_virt_t *) dev->dev_ptr;
+	struct scsi_device *sd = (struct scsi_device *) pdv->pdv_sd;
+
+	return (sd->host->max_sectors > sd->request_queue->limits.max_sectors) ?
+		sd->request_queue->limits.max_sectors : sd->host->max_sectors;
+}
+
+/*	pscsi_get_queue_depth():
+ *
+ *
+ */
+u32 pscsi_get_queue_depth(se_device_t *dev)
+{
+	pscsi_dev_virt_t *pdv = (pscsi_dev_virt_t *) dev->dev_ptr;
+	struct scsi_device *sd = (struct scsi_device *) pdv->pdv_sd;
+
+	return sd->queue_depth;
+}
+
+/*	pscsi_handle_SAM_STATUS_failures():
+ *
+ *
+ */
+static inline void pscsi_process_SAM_status(
+	se_task_t *task,
+	pscsi_plugin_task_t *pt)
+{
+	task->task_scsi_status = status_byte(pt->pscsi_result);
+	if ((task->task_scsi_status)) {
+		task->task_scsi_status <<= 1;
+		printk(KERN_INFO "PSCSI Status Byte exception at task: %p CDB:"
+			" 0x%02x Result: 0x%08x\n", task, pt->pscsi_cdb[0],
+			pt->pscsi_result);
+	}
+
+	switch (host_byte(pt->pscsi_result)) {
+	case DID_OK:
+		transport_complete_task(task, (!task->task_scsi_status));
+		break;
+	default:
+		printk(KERN_INFO "PSCSI Host Byte exception at task: %p CDB:"
+			" 0x%02x Result: 0x%08x\n", task, pt->pscsi_cdb[0],
+			pt->pscsi_result);
+		task->task_scsi_status = SAM_STAT_CHECK_CONDITION;
+		task->task_error_status = PYX_TRANSPORT_UNKNOWN_SAM_OPCODE;
+		TASK_CMD(task)->transport_error_status =
+					PYX_TRANSPORT_UNKNOWN_SAM_OPCODE;
+		transport_complete_task(task, 0);
+		break;
+	}
+
+	return;
+}
+
+void pscsi_req_done(struct request *req, int uptodate)
+{
+	se_task_t *task = (se_task_t *)req->end_io_data;
+	pscsi_plugin_task_t *pt = (pscsi_plugin_task_t *)task->transport_req;
+
+	pt->pscsi_result = req->errors;
+	pt->pscsi_resid = req->resid_len;
+
+	pscsi_process_SAM_status(task, pt);
+	__blk_put_request(req->q, req);
+	pt->pscsi_req = NULL;
+}
diff --git a/include/target/target_core_pscsi.h b/include/target/target_core_pscsi.h
new file mode 100644
index 0000000..da1d618
--- /dev/null
+++ b/include/target/target_core_pscsi.h
@@ -0,0 +1,186 @@
+/*******************************************************************************
+ * Filename:  target_core_pscsi.h
+ *
+ * This file contains the generic target mode <-> Linux SCSI subsystem plugin.
+ * specific definitions and prototypes.
+ *
+ * Copyright (c) 2003, 2004, 2005 PyX Technologies, Inc.
+ * Copyright (c) 2005, 2006, 2007 SBE, Inc.
+ * Copyright (c) 2007-2009 Rising Tide Software, Inc.
+ * Copyright (c) 2008-2009 Linux-iSCSI.org
+ *
+ * Nicholas A. Bellinger <nab@kernel.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ ******************************************************************************/
+
+
+#ifndef TARGET_CORE_PSCSI_H
+#define TARGET_CORE_PSCSI_H
+
+#define PSCSI_VERSION		"v3.1"
+
+/* used in pscsi_find_alloc_len() */
+#ifndef INQUIRY_DATA_SIZE
+#define INQUIRY_DATA_SIZE	0x24
+#endif
+
+/* used in pscsi_add_device_to_list() */
+#define PSCSI_DEFAULT_QUEUEDEPTH	1
+
+#define PS_RETRY		5
+#define PS_TIMEOUT_DISK		(15*HZ)
+#define PS_TIMEOUT_OTHER	(500*HZ)
+
+extern se_global_t *se_global;
+extern struct block_device *linux_blockdevice_claim(int, int, void *);
+extern int linux_blockdevice_release(int, int, struct block_device *);
+extern int linux_blockdevice_check(int, int);
+
+extern int pscsi_CDB_inquiry(se_task_t *, u32);
+extern int pscsi_CDB_none(se_task_t *, u32);
+extern int pscsi_CDB_read_non_SG(se_task_t *, u32);
+extern int pscsi_CDB_read_SG(se_task_t *, u32);
+extern int pscsi_CDB_write_non_SG(se_task_t *, u32);
+extern int pscsi_CDB_write_SG(se_task_t *, u32);
+
+#ifndef PSCSI_INCLUDE_STRUCTS
+extern int pscsi_attach_hba(se_hba_t *, u32);
+extern int pscsi_detach_hba(se_hba_t *);
+extern int pscsi_claim_phydevice(se_hba_t *, se_device_t *);
+extern int pscsi_release_phydevice(se_device_t *);
+extern void *pscsi_allocate_virtdevice(se_hba_t *, const char *);
+extern se_device_t *pscsi_create_virtdevice(se_hba_t *, se_subsystem_dev_t *,
+					void *);
+extern int pscsi_activate_device(se_device_t *);
+extern void pscsi_deactivate_device(se_device_t *);
+extern void pscsi_free_device(void *);
+extern int pscsi_transport_complete(se_task_t *);
+extern void *pscsi_allocate_request(se_task_t *, se_device_t *);
+extern int pscsi_do_task(se_task_t *);
+extern void pscsi_free_task(se_task_t *);
+extern ssize_t pscsi_set_configfs_dev_params(se_hba_t *, se_subsystem_dev_t *,
+						const char *, ssize_t);
+extern ssize_t pscsi_check_configfs_dev_params(se_hba_t *,
+						se_subsystem_dev_t *);
+extern ssize_t pscsi_show_configfs_dev_params(se_hba_t *, se_subsystem_dev_t *,
+						char *);
+extern se_device_t *pscsi_create_virtdevice_from_fd(se_subsystem_dev_t *,
+						const char *);
+extern void pscsi_get_plugin_info(void *, char *, int *);
+extern void pscsi_get_hba_info(se_hba_t *, char *, int *);
+extern void pscsi_get_dev_info(se_device_t *, char *, int *);
+extern int pscsi_check_lba(unsigned long long, se_device_t *);
+extern int pscsi_check_for_SG(se_task_t *);
+extern unsigned char *pscsi_get_cdb(se_task_t *);
+extern unsigned char *pscsi_get_sense_buffer(se_task_t *);
+extern u32 pscsi_get_blocksize(se_device_t *);
+extern u32 pscsi_get_device_rev(se_device_t *);
+extern u32 pscsi_get_device_type(se_device_t *);
+extern u32 pscsi_get_dma_length(u32, se_device_t *);
+extern u32 pscsi_get_max_sectors(se_device_t *);
+extern u32 pscsi_get_queue_depth(se_device_t *);
+extern void pscsi_req_done(struct request *, int);
+#endif
+
+#include <linux/device.h>
+#include <scsi/scsi_driver.h>
+#include <scsi/scsi_device.h>
+#include <linux/kref.h>
+#include <linux/kobject.h>
+
+typedef struct pscsi_plugin_task_s {
+	unsigned char pscsi_cdb[SCSI_CDB_SIZE];
+	unsigned char pscsi_sense[SCSI_SENSE_BUFFERSIZE];
+	int	pscsi_direction;
+	int	pscsi_result;
+	u32	pscsi_resid;
+	struct request *pscsi_req;
+} pscsi_plugin_task_t;
+
+#define PDF_HAS_CHANNEL_ID	0x01
+#define PDF_HAS_TARGET_ID	0x02
+#define PDF_HAS_LUN_ID		0x04
+#define PDF_HAS_VPD_UNIT_SERIAL 0x08
+#define PDF_HAS_VPD_DEV_IDENT	0x10
+
+typedef struct pscsi_dev_virt_s {
+	int	pdv_flags;
+	int	pdv_channel_id;
+	int	pdv_target_id;
+	int	pdv_lun_id;
+	struct scsi_device *pdv_sd;
+	struct se_hba_s *pdv_se_hba;
+} pscsi_dev_virt_t;
+
+extern void __pscsi_get_dev_info(pscsi_dev_virt_t *, char *, int *);
+
+/*
+ * We use the generic command sequencer, so we must setup
+ * se_subsystem_spc_t.
+ */
+#ifndef PSCSI_INCLUDE_STRUCTS
+
+se_subsystem_spc_t pscsi_template_spc = {
+	.inquiry		= pscsi_CDB_inquiry,
+	.none			= pscsi_CDB_none,
+	.read_non_SG		= pscsi_CDB_read_non_SG,
+	.read_SG		= pscsi_CDB_read_SG,
+	.write_non_SG		= pscsi_CDB_write_non_SG,
+	.write_SG		= pscsi_CDB_write_SG,
+};
+
+se_subsystem_api_t pscsi_template = {
+	.name			= "pscsi",			\
+	.type			= PSCSI,			\
+	.transport_type		= TRANSPORT_PLUGIN_PHBA_PDEV,	\
+	.attach_hba		= pscsi_attach_hba,		\
+	.detach_hba		= pscsi_detach_hba,		\
+	.activate_device	= pscsi_activate_device,	\
+	.deactivate_device	= pscsi_deactivate_device,	\
+	.claim_phydevice	= pscsi_claim_phydevice,	\
+	.allocate_virtdevice	= pscsi_allocate_virtdevice,	\
+	.create_virtdevice	= pscsi_create_virtdevice,	\
+	.free_device		= pscsi_free_device,		\
+	.release_phydevice	= pscsi_release_phydevice,	\
+	.transport_complete	= pscsi_transport_complete,	\
+	.allocate_request	= pscsi_allocate_request,	\
+	.do_task		= pscsi_do_task,		\
+	.free_task		= pscsi_free_task,		\
+	.check_configfs_dev_params = pscsi_check_configfs_dev_params, \
+	.set_configfs_dev_params = pscsi_set_configfs_dev_params, \
+	.show_configfs_dev_params = pscsi_show_configfs_dev_params, \
+	.create_virtdevice_from_fd = pscsi_create_virtdevice_from_fd, \
+	.get_plugin_info	= pscsi_get_plugin_info,	\
+	.get_hba_info		= pscsi_get_hba_info,		\
+	.get_dev_info		= pscsi_get_dev_info,		\
+	.check_lba		= pscsi_check_lba,		\
+	.check_for_SG		= pscsi_check_for_SG,		\
+	.get_cdb		= pscsi_get_cdb,		\
+	.get_sense_buffer	= pscsi_get_sense_buffer,	\
+	.get_blocksize		= pscsi_get_blocksize,		\
+	.get_device_rev		= pscsi_get_device_rev,		\
+	.get_device_type	= pscsi_get_device_type,	\
+	.get_dma_length		= pscsi_get_dma_length,		\
+	.get_max_sectors	= pscsi_get_max_sectors,	\
+	.get_queue_depth	= pscsi_get_queue_depth,	\
+	.write_pending		= NULL,				\
+	.spc			= &pscsi_template_spc,		\
+};
+
+#endif
+
+#endif   /*** TARGET_CORE_PSCSI_H ***/



^ permalink raw reply	[flat|nested] 2+ messages in thread

* [RFC PATCH 10/19] Target_Core_Mod PSCSI <-> Linux/SCSI subsystem plugin
@ 2009-09-18 22:08 Nicholas A. Bellinger
  0 siblings, 0 replies; 2+ messages in thread
From: Nicholas A. Bellinger @ 2009-09-18 22:08 UTC (permalink / raw)
  To: LKML, linux-scsi
  Cc: Andrew Morton, Greg KH, Douglas Gilbert, James Bottomley,
	Hannes Reinecke, FUJITA Tomonori, Mike Christie, Joel Becker,
	Martin K. Petersen, Christoph Hellwig, Linus Torvalds,
	Alan Stern, Boaz Harrosh, Florian Haas, Philipp Reisner,
	Lars Ellenberg, Daniel Walker

[RFC PATCH 10/19] Target_Core_Mod PSCSI <-> Linux/SCSI subsystem plugin

This patch adds the PSCSI subsystem plugin for accessing struct scsi_device
using struct request from upstream Linux/SCSI code in k.o v2.6.31

Signed-off-by: Nicholas A. Bellinger <nab@linux-iscsi.org>
 
diff --git a/drivers/target/target_core_pscsi.c b/drivers/target/target_core_pscsi.c
new file mode 100644
index 0000000..07363a0
--- /dev/null
+++ b/drivers/target/target_core_pscsi.c
@@ -0,0 +1,1676 @@
+/*******************************************************************************
+ * Filename:  target_core_pscsi.c
+ *
+ * This file contains the generic target mode <-> Linux SCSI subsystem plugin.
+ *
+ * Copyright (c) 2003, 2004, 2005 PyX Technologies, Inc.
+ * Copyright (c) 2005, 2006, 2007 SBE, Inc.
+ * Copyright (c) 2007-2009 Rising Tide Software, Inc.
+ * Copyright (c) 2008-2009 Linux-iSCSI.org
+ *
+ * Nicholas A. Bellinger <nab@kernel.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ ******************************************************************************/
+
+
+#define TARGET_CORE_PSCSI_C
+
+#include <linux/version.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/blkdev.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/smp_lock.h>
+#include <linux/genhd.h>
+#include <linux/cdrom.h>
+#include <linux/file.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_host.h>
+#include <sd.h>
+#include <sr.h>
+
+#include <target/target_core_base.h>
+#include <target/target_core_device.h>
+#include <target/target_core_transport.h>
+#include <target/target_core_pscsi.h>
+#include <target/target_core_plugin.h>
+#include <target/target_core_seobj.h>
+#include <target/target_core_transport_plugin.h>
+
+#undef TARGET_CORE_PSCSI_C
+
+#define ISPRINT(a)  ((a >= ' ') && (a <= '~'))
+
+/*	pscsi_get_sh():
+ *
+ *
+ */
+static struct Scsi_Host *pscsi_get_sh(u32 host_no)
+{
+	struct Scsi_Host *sh = NULL;
+
+	sh = scsi_host_lookup(host_no);
+	if (IS_ERR(sh)) {
+		printk(KERN_ERR "Unable to locate SCSI HBA with Host ID:"
+				" %u\n", host_no);
+		return NULL;
+	}
+
+	return sh;
+}
+
+/*	pscsi_check_sd():
+ *
+ *	Should be called with scsi_device_get(sd) held
+ */
+int pscsi_check_sd(struct scsi_device *sd)
+{
+	struct gendisk *disk;
+	struct scsi_disk *sdisk;
+
+	if (!sd) {
+		printk(KERN_ERR "struct scsi_device is NULL!\n");
+		return -1;
+	}
+
+	if (sd->type != TYPE_DISK)
+		return 0;
+
+	/*
+	 * Some struct scsi_device of Type: Direct-Access, namely the
+	 * SGI Univerisal Xport do not have a corrasponding block device.
+	 * We skip these for now.
+	 */
+	sdisk = dev_get_drvdata(&sd->sdev_gendev);
+	if (!(sdisk))
+		return -1;
+
+	disk = (struct gendisk *) sdisk->disk;
+	if (!(disk->major)) {
+		printk(KERN_ERR "dev_get_drvdata() failed\n");
+		return -1;
+	}
+
+	if (linux_blockdevice_check(disk->major, disk->first_minor) < 0)
+		return -1;
+
+	return 0;
+}
+
+/*	pscsi_claim_sd():
+ *
+ *	Should be called with scsi_device_get(sd) held
+ */
+int pscsi_claim_sd(struct scsi_device *sd)
+{
+	struct block_device *bdev;
+	struct gendisk *disk;
+	struct scsi_disk *sdisk;
+
+	if (!sd) {
+		printk(KERN_ERR "struct scsi_device is NULL!\n");
+		return -1;
+	}
+
+	if (sd->type != TYPE_DISK)
+		return 0;
+
+	/*
+	 * Some struct scsi_device of Type: Direct-Access, namely the
+	 * SGI Univerisal Xport do not have a corrasponding block device.
+	 * We skip these for now.
+	 */
+	sdisk = dev_get_drvdata(&sd->sdev_gendev);
+	if (!(sdisk))
+		return -1;
+
+	disk = (struct gendisk *) sdisk->disk;
+	if (!(disk->major)) {
+		printk(KERN_ERR "dev_get_drvdata() failed\n");
+		return -1;
+	}
+
+	printk(KERN_INFO "PSCSI: Claiming %p Major:Minor - %d:%d\n",
+		sd, disk->major, disk->first_minor);
+
+	bdev = linux_blockdevice_claim(disk->major, disk->first_minor,
+				(void *)sd);
+	if (!(bdev))
+		return -1;
+
+	return 0;
+}
+
+/*	pscsi_release_sd()
+ *
+ * 	Should be called with scsi_device_get(sd) held
+ */
+int pscsi_release_sd(struct scsi_device *sd)
+{
+	struct gendisk *disk;
+	struct scsi_disk *sdisk;
+
+	if (!sd) {
+		printk(KERN_ERR "struct scsi_device is NULL!\n");
+		return -1;
+	}
+
+	if (sd->type != TYPE_DISK)
+		return 0;
+
+	/*
+	 * Some struct scsi_device of Type: Direct-Access, namely the
+	 * SGI Univerisal Xport do not have a corrasponding block device.
+	 * We skip these for now.
+	 */
+	sdisk = dev_get_drvdata(&sd->sdev_gendev);
+	if (!(sdisk))
+		return -1;
+
+	disk = (struct gendisk *) sdisk->disk;
+	if (!(disk->major)) {
+		printk(KERN_ERR "dev_get_drvdata() failed\n");
+		return -1;
+	}
+
+	printk(KERN_INFO "PSCSI: Releasing Major:Minor - %d:%d\n",
+		disk->major, disk->first_minor);
+
+	return linux_blockdevice_release(disk->major, disk->first_minor, NULL);
+}
+
+/*	pscsi_attach_hba():
+ *
+ * 	pscsi_get_sh() used scsi_host_lookup() to locate struct Scsi_Host.
+ *	from the passed SCSI Host ID.
+ */
+int pscsi_attach_hba(se_hba_t *hba, u32 host_id)
+{
+	int hba_depth, max_sectors;
+	struct Scsi_Host *sh;
+
+	sh = pscsi_get_sh(host_id);
+	if (!(sh))
+		return -EINVAL;
+
+	max_sectors = sh->max_sectors;
+
+	/*
+	 * Usually the SCSI LLD will use the hostt->can_queue value to define
+	 * its HBA TCQ depth.  Some other drivers (like 2.6 megaraid) don't set
+	 * this at all and set sh->can_queue at runtime.
+	 */
+	hba_depth = (sh->hostt->can_queue > sh->can_queue) ?
+		sh->hostt->can_queue : sh->can_queue;
+	atomic_set(&hba->left_queue_depth, hba_depth);
+	atomic_set(&hba->max_queue_depth, hba_depth);
+
+	hba->hba_ptr = (void *) sh;
+	hba->transport = &pscsi_template;
+
+	printk(KERN_INFO "CORE_HBA[%d] - TCM SCSI HBA Driver %s on"
+		" Generic Target Core Stack %s\n", hba->hba_id,
+		PSCSI_VERSION, TARGET_CORE_MOD_VERSION);
+	printk(KERN_INFO "CORE_HBA[%d] - %s\n", hba->hba_id, (sh->hostt->name) ?
+			(sh->hostt->name) : "Unknown");
+	printk(KERN_INFO "CORE_HBA[%d] - Attached SCSI HBA to Generic"
+		" Target Core with TCQ Depth: %d MaxSectors: %hu\n",
+		hba->hba_id, atomic_read(&hba->max_queue_depth), max_sectors);
+
+	return 0;
+}
+
+/*	pscsi_detach_hba(): (Part of se_subsystem_api_t template)
+ *
+ *
+ */
+int pscsi_detach_hba(se_hba_t *hba)
+{
+	struct Scsi_Host *scsi_host = (struct Scsi_Host *) hba->hba_ptr;
+
+	scsi_host_put(scsi_host);
+
+	printk(KERN_INFO "CORE_HBA[%d] - Detached SCSI HBA: %s from"
+		" Generic Target Core\n", hba->hba_id,
+		(scsi_host->hostt->name) ? (scsi_host->hostt->name) :
+		"Unknown");
+
+	hba->hba_ptr = NULL;
+
+	return 0;
+}
+
+/*	pscsi_add_device_to_list():
+ *
+ *
+ */
+se_device_t *pscsi_add_device_to_list(
+	se_hba_t *hba,
+	se_subsystem_dev_t *se_dev,
+	pscsi_dev_virt_t *pdv,
+	struct scsi_device *sd,
+	int dev_flags)
+{
+	se_device_t *dev;
+
+	/*
+	 * Some pseudo SCSI HBAs do not fill in sector_size
+	 * correctly. (See ide-scsi.c)  So go ahead and setup sane
+	 * values.
+	 */
+	if (!sd->sector_size) {
+		switch (sd->type) {
+		case TYPE_DISK:
+			sd->sector_size = 512;
+			break;
+		case TYPE_ROM:
+			sd->sector_size = 2048;
+			break;
+		case TYPE_TAPE: /* The Tape may not be in the drive */
+			break;
+		case TYPE_MEDIUM_CHANGER: /* Control CDBs only */
+			break;
+		default:
+			printk(KERN_ERR "Unable to set sector_size for %d\n",
+					sd->type);
+			return NULL;
+		}
+
+		if (sd->sector_size) {
+			printk(KERN_ERR "Set broken SCSI Device"
+				" %d:%d:%d sector_size to %d\n", sd->channel,
+				sd->id, sd->lun, sd->sector_size);
+		}
+	}
+
+	if (!sd->queue_depth) {
+		sd->queue_depth = PSCSI_DEFAULT_QUEUEDEPTH;
+
+		printk(KERN_ERR "Set broken SCSI Device %d:%d:%d"
+			" queue_depth to %d\n", sd->channel, sd->id,
+				sd->lun, sd->queue_depth);
+	}
+	/*
+	 * Set the pointer pdv->pdv_sd to from passed struct scsi_device,
+	 * which has already been referenced with Linux SCSI code with
+	 * scsi_device_get() in this file's pscsi_create_virtdevice().
+	 *
+	 * The passthrough operations called by the transport_add_device_*
+	 * function below will require this pointer to be set for passthroug
+	 *  ops.
+	 *
+	 * For the shutdown case in pscsi_free_device(), this struct
+	 * scsi_device  reference is released with Linux SCSI code
+	 * scsi_device_put() and the pdv->pdv_sd cleared.
+	 */
+	pdv->pdv_sd = sd;
+
+	dev = transport_add_device_to_core_hba(hba, &pscsi_template,
+				se_dev, dev_flags, (void *)pdv);
+	if (!(dev)) {
+		pdv->pdv_sd = NULL;
+		return NULL;
+	}
+
+	/*
+	 * For TYPE_TAPE, attempt to determine blocksize with MODE_SENSE.
+	 */
+	if (sd->type == TYPE_TAPE) {
+		unsigned char *buf = NULL, cdb[SCSI_CDB_SIZE];
+		se_cmd_t *cmd;
+		u32 blocksize;
+
+		memset(cdb, 0, SCSI_CDB_SIZE);
+		cdb[0] = MODE_SENSE;
+		cdb[4] = 0x0c; /* 12 bytes */
+
+		cmd = transport_allocate_passthrough(&cdb[0],
+				SE_DIRECTION_READ, 0, NULL, 0, 12,
+				DEV_OBJ_API(dev), dev);
+		if (!(cmd)) {
+			printk(KERN_ERR "Unable to determine blocksize for"
+				" TYPE_TAPE\n");
+			goto out;
+		}
+
+		if (transport_generic_passthrough(cmd) < 0) {
+			printk(KERN_ERR "Unable to determine blocksize for"
+				" TYPE_TAPE\n");
+			goto out;
+		}
+
+		buf = (unsigned char *)T_TASK(cmd)->t_task_buf;
+		blocksize = (buf[9] << 16) | (buf[10] << 8) | (buf[11]);
+
+		/*
+		 * If MODE_SENSE still returns zero, set the default value
+		 * to 1024.
+		 */
+		sd->sector_size = blocksize;
+		if (!(sd->sector_size))
+			sd->sector_size = 1024;
+
+		transport_passthrough_release(cmd);
+	}
+out:
+	return dev;
+}
+
+int pscsi_claim_phydevice(se_hba_t *hba, se_device_t *dev)
+{
+	pscsi_dev_virt_t *pdv = (pscsi_dev_virt_t *) dev->dev_ptr;
+	struct scsi_device *sd = (struct scsi_device *)pdv->pdv_sd;
+
+	return pscsi_claim_sd(sd);
+}
+
+int pscsi_release_phydevice(se_device_t *dev)
+{
+	pscsi_dev_virt_t *pdv = (pscsi_dev_virt_t *) dev->dev_ptr;
+	struct scsi_device *sd = (struct scsi_device *)pdv->pdv_sd;
+
+	return pscsi_release_sd(sd);
+}
+
+void *pscsi_allocate_virtdevice(se_hba_t *hba, const char *name)
+{
+	pscsi_dev_virt_t *pdv;
+
+	pdv = kzalloc(sizeof(pscsi_dev_virt_t), GFP_KERNEL);
+	if (!(pdv)) {
+		printk(KERN_ERR "Unable to allocate memory for pscsi_dev_virt_t\n");
+		return NULL;
+	}
+	pdv->pdv_se_hba = hba;
+
+	printk(KERN_INFO "PSCSI: Allocated pdv: %p for %s\n", pdv, name);
+	return (void *)pdv;
+}
+
+/*
+ * Called with struct Scsi_Host->host_lock called.
+ */
+se_device_t *pscsi_create_type_disk(
+	struct scsi_device *sd,
+	pscsi_dev_virt_t *pdv,
+	se_subsystem_dev_t *se_dev,
+	se_hba_t *hba)
+{
+	se_device_t *dev;
+	struct Scsi_Host *sh = sd->host;
+	u32 dev_flags = 0;
+
+	if (scsi_device_get(sd)) {
+		printk(KERN_ERR "scsi_device_get() failed for %d:%d:%d:%d\n",
+			sh->host_no, sd->channel, sd->id, sd->lun);
+		spin_unlock_irq(sh->host_lock);
+		return NULL;
+	}
+	spin_unlock_irq(sh->host_lock);
+
+	if (pscsi_check_sd(sd) < 0) {
+		scsi_device_put(sd);
+		printk(KERN_ERR "pscsi_check_sd() failed for %d:%d:%d:%d\n",
+			sh->host_no, sd->channel, sd->id, sd->lun);
+		return NULL;
+	}
+	if (!(pscsi_claim_sd(sd))) {
+		dev_flags |= DF_CLAIMED_BLOCKDEV;
+		dev_flags |= DF_PERSISTENT_CLAIMED_BLOCKDEV;
+	}
+	dev = pscsi_add_device_to_list(hba, se_dev, pdv, sd, dev_flags);
+	if (!(dev)) {
+		scsi_device_put(sd);
+		return NULL;
+	}
+	printk(KERN_INFO "CORE_PSCSI[%d] - Added TYPE_DISK for %d:%d:%d\n",
+		sh->host_no, sd->channel, sd->id, sd->lun);
+
+	return dev;
+}
+
+/*
+ * Called with struct Scsi_Host->host_lock called.
+ */
+se_device_t *pscsi_create_type_rom(
+	struct scsi_device *sd,
+	pscsi_dev_virt_t *pdv,
+	se_subsystem_dev_t *se_dev,
+	se_hba_t *hba)
+{
+	se_device_t *dev;
+	struct Scsi_Host *sh = sd->host;
+	u32 dev_flags = 0;
+
+	if (scsi_device_get(sd)) {
+		printk(KERN_ERR "scsi_device_get() failed for %d:%d:%d:%d\n",
+			sh->host_no, sd->channel, sd->id, sd->lun);
+		spin_unlock_irq(sh->host_lock);
+		return NULL;
+	}
+	spin_unlock_irq(sh->host_lock);
+
+	dev = pscsi_add_device_to_list(hba, se_dev, pdv, sd, dev_flags);
+	if (!(dev)) {
+		scsi_device_put(sd);
+		return NULL;
+	}
+	printk(KERN_INFO "CORE_PSCSI[%d] - Added Type: %s for %d:%d:%d\n",
+		sh->host_no, scsi_device_type(sd->type), sd->channel,
+		sd->id, sd->lun);
+
+	return dev;
+}
+
+/*
+ *Called with struct Scsi_Host->host_lock called.
+ */
+se_device_t *pscsi_create_type_other(
+	struct scsi_device *sd,
+	pscsi_dev_virt_t *pdv,
+	se_subsystem_dev_t *se_dev,
+	se_hba_t *hba)
+{
+	se_device_t *dev;
+	struct Scsi_Host *sh = sd->host;
+	u32 dev_flags = 0;
+
+	spin_unlock_irq(sh->host_lock);
+	dev = pscsi_add_device_to_list(hba, se_dev, pdv, sd, dev_flags);
+	if (!(dev))
+		return NULL;
+
+	printk(KERN_INFO "CORE_PSCSI[%d] - Added Type: %s for %d:%d:%d\n",
+		sh->host_no, scsi_device_type(sd->type), sd->channel,
+		sd->id, sd->lun);
+
+	return dev;
+}
+
+se_device_t *pscsi_create_virtdevice(
+	se_hba_t *hba,
+	se_subsystem_dev_t *se_dev,
+	void *p)
+{
+	pscsi_dev_virt_t *pdv = (pscsi_dev_virt_t *)p;
+	se_device_t *dev;
+	struct scsi_device *sd;
+	struct Scsi_Host *sh = (struct Scsi_Host *) hba->hba_ptr;
+
+	if (!(pdv)) {
+		printk(KERN_ERR "Unable to locate pscsi_dev_virt_t"
+				" parameter\n");
+		return NULL;
+	}
+
+	spin_lock_irq(sh->host_lock);
+	list_for_each_entry(sd, &sh->__devices, siblings) {
+		if ((pdv->pdv_channel_id != sd->channel) ||
+		    (pdv->pdv_target_id != sd->id) ||
+		    (pdv->pdv_lun_id != sd->lun))
+			continue;
+		/*
+		 * Functions will release the held struct scsi_host->host_lock
+		 * before calling calling pscsi_check_sd() and
+		 * pscsi_add_device_to_list() to register struct scsi_device
+		 * with target_core_mod.
+		 */
+		switch (sd->type) {
+		case TYPE_DISK:
+			dev = pscsi_create_type_disk(sd, pdv, se_dev, hba);
+			break;
+		case TYPE_ROM:
+			dev = pscsi_create_type_rom(sd, pdv, se_dev, hba);
+			break;
+		default:
+			dev = pscsi_create_type_other(sd, pdv, se_dev, hba);
+			break;
+		}
+		if (!(dev)) {
+			pdv->pdv_sd = NULL;
+			return NULL;
+		}
+		return dev;
+	}
+	spin_unlock_irq(sh->host_lock);
+
+	printk(KERN_ERR "Unable to locate %d:%d:%d:%d\n", sh->host_no,
+		pdv->pdv_channel_id,  pdv->pdv_target_id, pdv->pdv_lun_id);
+
+	return NULL;
+}
+
+/*	pscsi_activate_device(): (Part of se_subsystem_api_t template)
+ *
+ *
+ */
+int pscsi_activate_device(se_device_t *dev)
+{
+	pscsi_dev_virt_t *pdv = (pscsi_dev_virt_t *) dev->dev_ptr;
+	struct scsi_device *sd = (struct scsi_device *) pdv->pdv_sd;
+	struct Scsi_Host *sh = sd->host;
+
+	printk(KERN_INFO "CORE_PSCSI[%d] - Activating Device with TCQ: %d at"
+		" SCSI Location (Channel/Target/LUN) %d/%d/%d\n", sh->host_no,
+		 sd->queue_depth, sd->channel, sd->id, sd->lun);
+
+	return 0;
+}
+
+/*	pscsi_deactivate_device(): (Part of se_subsystem_api_t template)
+ *
+ *
+ */
+void pscsi_deactivate_device(se_device_t *dev)
+{
+	pscsi_dev_virt_t *pdv = (pscsi_dev_virt_t *) dev->dev_ptr;
+	struct scsi_device *sd = (struct scsi_device *) pdv->pdv_sd;
+	struct Scsi_Host *sh = sd->host;
+
+	printk(KERN_INFO "CORE_PSCSI[%d] - Deactivating Device with TCQ: %d at"
+		" SCSI Location (Channel/Target/LUN) %d/%d/%d\n", sh->host_no,
+		sd->queue_depth, sd->channel, sd->id, sd->lun);
+}
+
+/*	pscsi_free_device(): (Part of se_subsystem_api_t template)
+ *
+ *
+ */
+void pscsi_free_device(void *p)
+{
+	pscsi_dev_virt_t *pdv = (pscsi_dev_virt_t *) p;
+	struct scsi_device *sd = (struct scsi_device *) pdv->pdv_sd;
+
+	if (sd) {
+		if ((sd->type == TYPE_DISK) || (sd->type == TYPE_ROM))
+			scsi_device_put(sd);
+
+		pdv->pdv_sd = NULL;
+	}
+
+	kfree(pdv);
+}
+
+/*	pscsi_transport_complete():
+ *
+ *
+ */
+int pscsi_transport_complete(se_task_t *task)
+{
+	pscsi_dev_virt_t *pdv = (pscsi_dev_virt_t *) task->se_dev->dev_ptr;
+	struct scsi_device *sd = (struct scsi_device *) pdv->pdv_sd;
+	int result;
+	pscsi_plugin_task_t *pt = (pscsi_plugin_task_t *) task->transport_req;
+	unsigned char *cdb = &pt->pscsi_cdb[0];
+
+	result = pt->pscsi_result;
+
+# ifdef LINUX_VPD_PAGE_CHECK
+	if ((cdb[0] == INQUIRY) && host_byte(result) == DID_OK) {
+		u32 len = 0;
+		unsigned char *dst = (unsigned char *)
+				T_TASK(task->task_se_cmd)->t_task_buf;
+		unsigned char buf[VPD_BUF_LEN], *iqn = NULL;
+		se_subsystem_dev_t *su_dev = TASK_DEV(task)->se_sub_dev;
+		se_hba_t *hba = task->se_dev->se_hba;
+
+		/*
+		 * The Initiator port did not request VPD information.
+		 */
+		if (!(cdb[1] & 0x1)) {
+			task->task_scsi_status = GOOD;
+			return 0;
+		}
+
+		/*
+		 * Assume the SCSI Device did the right thing if an VPD length
+		 * is provided in the INQUIRY response payload.
+		 */
+		if (dst[3] != 0x00) {
+			su_dev->su_dev_flags |= SDF_FIRMWARE_VPD_UNIT_SERIAL;
+			su_dev->su_dev_flags &= ~SDF_EMULATED_VPD_UNIT_SERIAL;
+			task->task_scsi_status = GOOD;
+			return 0;
+		}
+
+		memset(buf, 0, VPD_BUF_LEN);
+		memset(dst, 0, task->task_size);
+		buf[0] = sd->type;
+
+		switch (cdb[2]) {
+		case 0x00:
+			buf[1] = 0x00;
+			buf[3] = 3;
+			buf[4] = 0x0;
+			buf[5] = 0x80;
+			buf[6] = 0x83;
+			len = 3;
+			break;
+		case 0x80:
+			buf[1] = 0x80;
+			if (su_dev->su_dev_flags &
+					SDF_EMULATED_VPD_UNIT_SERIAL)
+				len += sprintf((unsigned char *)&buf[4], "%s",
+					&su_dev->t10_wwn.unit_serial[0]);
+			else {
+				iqn = transport_get_iqn_sn();
+				len += sprintf((unsigned char *)&buf[4],
+					"%s:%u_%u_%u_%u", iqn, hba->hba_id,
+					sd->channel, sd->id, sd->lun);
+			}
+			buf[3] = len;
+			break;
+		case 0x83:
+			buf[1] = 0x83;
+			/* Start Identifier Page */
+			buf[4] = 0x2; /* ASCII */
+			buf[5] = 0x1;
+			buf[6] = 0x0;
+			len += sprintf((unsigned char *)&buf[8], "LIO-ORG");
+
+			if (su_dev->su_dev_flags &
+					SDF_EMULATED_VPD_UNIT_SERIAL) {
+				len += sprintf((unsigned char *)&buf[16],
+					"PSCSI:%s",
+					&su_dev->t10_wwn.unit_serial[0]);
+			} else {
+				iqn = transport_get_iqn_sn();
+				len += sprintf((unsigned char *)&buf[16],
+					"PSCSI:%s:%u_%u_%u_%u", iqn,
+					hba->hba_id, sd->channel,
+					sd->id, sd->lun);
+			}
+			buf[7] = len; /* Identifer Length */
+			len += 4;
+			buf[3] = len; /* Page Length */
+			break;
+		default:
+			break;
+		}
+
+		if ((len + 4) > task->task_size) {
+			printk(KERN_ERR "Inquiry VPD Length: %u larger than"
+				" req->sr_bufflen: %u\n", (len + 4),
+				task->task_size);
+			memcpy(dst, buf, task->task_size);
+		} else
+			memcpy(dst, buf, (len + 4));
+
+		/*
+		 * Fake the GOOD SAM status here too.
+		 */
+		task->task_scsi_status = GOOD;
+		return 0;
+	}
+
+# endif /* LINUX_VPD_PAGE_CHECK */
+
+	/*
+	 * Hack to make sure that Write-Protect modepage is set if R/O mode is
+	 * forced.
+	 */
+	if (((cdb[0] == MODE_SENSE) || (cdb[0] == MODE_SENSE_10)) &&
+	     (status_byte(result) << 1) == SAM_STAT_GOOD) {
+		if (!TASK_CMD(task)->se_deve)
+			goto after_mode_sense;
+
+		if (TASK_CMD(task)->se_deve->lun_flags &
+				TRANSPORT_LUNFLAGS_READ_ONLY) {
+			unsigned char *buf = (unsigned char *)
+				T_TASK(task->task_se_cmd)->t_task_buf;
+
+			if (cdb[0] == MODE_SENSE_10) {
+				if (!(buf[3] & 0x80))
+					buf[3] |= 0x80;
+			} else {
+				if (!(buf[2] & 0x80))
+					buf[2] |= 0x80;
+			}
+		}
+	}
+after_mode_sense:
+
+	if (sd->type != TYPE_TAPE)
+		goto after_mode_select;
+
+	/*
+	 * Hack to correctly obtain the initiator requested blocksize for
+	 * TYPE_TAPE.  Since this value is dependent upon each tape media,
+	 * struct scsi_device->sector_size will not contain the correct value
+	 * by default, so we go ahead and set it so
+	 * TRANSPORT(dev)->get_blockdev() returns the correct value to the
+	 * storage engine.
+	 */
+	if (((cdb[0] == MODE_SELECT) || (cdb[0] == MODE_SELECT_10)) &&
+	      (status_byte(result) << 1) == SAM_STAT_GOOD) {
+		unsigned char *buf;
+		struct scatterlist *sg = task->task_sg;
+		u16 bdl;
+		u32 blocksize;
+
+		buf = sg_virt(&sg[0]);
+		if (!(buf)) {
+			printk(KERN_ERR "Unable to get buf for scatterlist\n");
+			goto after_mode_select;
+		}
+
+		if (cdb[0] == MODE_SELECT)
+			bdl = (buf[3]);
+		else
+			bdl = (buf[6] << 8) | (buf[7]);
+
+		if (!bdl)
+			goto after_mode_select;
+
+		if (cdb[0] == MODE_SELECT)
+			blocksize = (buf[9] << 16) | (buf[10] << 8) |
+					(buf[11]);
+		else
+			blocksize = (buf[13] << 16) | (buf[14] << 8) |
+					(buf[15]);
+
+		sd->sector_size = blocksize;
+	}
+after_mode_select:
+
+	if (status_byte(result) & CHECK_CONDITION)
+		return 1;
+
+	return 0;
+}
+
+/*	pscsi_allocate_request(): (Part of se_subsystem_api_t template)
+ *
+ *
+ */
+void *pscsi_allocate_request(
+	se_task_t *task,
+	se_device_t *dev)
+{
+	pscsi_plugin_task_t *pt;
+
+	pt = kzalloc(sizeof(pscsi_plugin_task_t), GFP_KERNEL);
+	if (!(pt)) {
+		printk(KERN_ERR "Unable to allocate pscsi_plugin_task_t\n");
+		return NULL;
+	}
+
+	return pt;
+}
+
+static inline void pscsi_blk_init_request(
+	se_task_t *task,
+	pscsi_plugin_task_t *pt)
+{
+	/*
+	 * Defined as "scsi command" in include/linux/blkdev.h.
+	 */
+	pt->pscsi_req->cmd_type = REQ_TYPE_BLOCK_PC;
+	/*
+	 * Setup the done function pointer for struct request,
+	 * also set the end_io_data pointer.to se_task_t.
+	 */
+	pt->pscsi_req->end_io = pscsi_req_done;
+	pt->pscsi_req->end_io_data = (void *)task;
+	/*
+	 * Load the referenced se_task_t's SCSI CDB into
+	 * include/linux/blkdev.h:struct request->cmd
+	 */
+	pt->pscsi_req->cmd_len = COMMAND_SIZE(pt->pscsi_cdb[0]);
+	memcpy(pt->pscsi_req->cmd, pt->pscsi_cdb, pt->pscsi_req->cmd_len);
+	/*
+	 * Setup pointer for outgoing sense data.
+	 */
+	pt->pscsi_req->sense = (void *)&pt->pscsi_sense[0];
+	pt->pscsi_req->sense_len = 0;
+}
+
+/*
+ * Used for pSCSI data payloads for all *NON* SCF_SCSI_DATA_SG_IO_CDB
+*/
+static int pscsi_blk_get_request(se_task_t *task)
+{
+	pscsi_plugin_task_t *pt = (pscsi_plugin_task_t *) task->transport_req;
+	pscsi_dev_virt_t *pdv = (pscsi_dev_virt_t *) task->se_dev->dev_ptr;
+
+	pt->pscsi_req = blk_get_request(pdv->pdv_sd->request_queue,
+			(pt->pscsi_direction == DMA_TO_DEVICE), GFP_KERNEL);
+	if (!(pt->pscsi_req) || IS_ERR(pt->pscsi_req)) {
+		printk(KERN_ERR "PSCSI: blk_get_request() failed: %ld\n",
+				IS_ERR(pt->pscsi_req));
+		return PYX_TRANSPORT_LU_COMM_FAILURE;
+	}
+	/*
+	 * Setup the newly allocated struct request for REQ_TYPE_BLOCK_PC,
+	 * and setup rq callback, CDB and sense.
+	 */
+	pscsi_blk_init_request(task, pt);
+	return 0;
+}
+
+/*      pscsi_do_task(): (Part of se_subsystem_api_t template)
+ *
+ *
+ */
+int pscsi_do_task(se_task_t *task)
+{
+	pscsi_plugin_task_t *pt = (pscsi_plugin_task_t *) task->transport_req;
+	pscsi_dev_virt_t *pdv = (pscsi_dev_virt_t *) task->se_dev->dev_ptr;
+	struct gendisk *gd = NULL;
+	/*
+	 * Grab pointer to struct gendisk for TYPE_DISK and TYPE_ROM
+	 * cases (eg: cases where struct scsi_device has a backing
+	 * struct block_device.  Also set the struct request->timeout
+	 * value based on peripheral device type (from SCSI).
+	 */
+	if (pdv->pdv_sd->type == TYPE_DISK) {
+		struct scsi_disk *sdisk = dev_get_drvdata(
+					&pdv->pdv_sd->sdev_gendev);
+		gd = sdisk->disk;
+		pt->pscsi_req->timeout = PS_TIMEOUT_DISK;
+	} else
+		pt->pscsi_req->timeout = PS_TIMEOUT_OTHER;
+
+	pt->pscsi_req->retries = PS_RETRY;
+	/*
+	 * Queue the struct request into the struct scsi_device->request_queue.
+	 */
+	blk_execute_rq_nowait(pdv->pdv_sd->request_queue, gd,
+			      pt->pscsi_req, 1, pscsi_req_done);
+
+	return PYX_TRANSPORT_SENT_TO_TRANSPORT;
+}
+
+/*	pscsi_free_task(): (Part of se_subsystem_api_t template)
+ *
+ *
+ */
+void pscsi_free_task(se_task_t *task)
+{
+	pscsi_plugin_task_t *pt = (pscsi_plugin_task_t *)task->transport_req;
+	/*
+	 * We do not release the bio(s) here associated with this task, as
+	 * this is handled by bio_put() and pscsi_bi_endio().
+	 */
+	kfree(pt);
+}
+
+ssize_t pscsi_set_configfs_dev_params(se_hba_t *hba,
+	se_subsystem_dev_t *se_dev,
+	const char *page,
+	ssize_t count)
+{
+	pscsi_dev_virt_t *pdv = (pscsi_dev_virt_t *) se_dev->se_dev_su_ptr;
+	struct Scsi_Host *sh = (struct Scsi_Host *) hba->hba_ptr;
+	char *buf, *cur, *ptr, *ptr2;
+	unsigned long scsi_channel_id, scsi_target_id, scsi_lun_id;
+	int params = 0, ret;
+
+	buf = kzalloc(count, GFP_KERNEL);
+	if (!(buf)) {
+		printk(KERN_ERR "Unable to allocate memory for temporary"
+				" buffer\n");
+		return -ENOMEM;
+	}
+	memcpy(buf, page, count);
+	cur = buf;
+
+	while (cur) {
+		ptr = strstr(cur, "=");
+		if (!(ptr))
+			goto out;
+
+		*ptr = '\0';
+		ptr++;
+
+		ptr2 = strstr(cur, "scsi_channel_id");
+		if ((ptr2)) {
+			transport_check_dev_params_delim(ptr, &cur);
+			ret = strict_strtoul(ptr, 0, &scsi_channel_id);
+			if (ret < 0) {
+				printk(KERN_ERR "strict_strtoul() failed for"
+					" scsi_channel_id=\n");
+				break;
+			}
+			pdv->pdv_channel_id = (int)scsi_channel_id;
+			printk(KERN_INFO "PSCSI[%d]: Referencing SCSI Channel"
+				" ID: %d\n",  sh->host_no, pdv->pdv_channel_id);
+			pdv->pdv_flags |= PDF_HAS_CHANNEL_ID;
+			params++;
+			continue;
+		}
+		ptr2 = strstr(cur, "scsi_target_id");
+		if ((ptr2)) {
+			transport_check_dev_params_delim(ptr, &cur);
+			ret = strict_strtoul(ptr, 0, &scsi_target_id);
+			if (ret < 0) {
+				printk("strict_strtoul() failed for"
+					" strict_strtoul()\n");
+				break;
+			}
+			pdv->pdv_target_id = (int)scsi_target_id;
+			printk(KERN_INFO "PSCSI[%d]: Referencing SCSI Target"
+				" ID: %d\n", sh->host_no, pdv->pdv_target_id);
+			pdv->pdv_flags |= PDF_HAS_TARGET_ID;
+			params++;
+			continue;
+		}
+		ptr2 = strstr(cur, "scsi_lun_id");
+		if ((ptr2)) {
+			transport_check_dev_params_delim(ptr, &cur);
+			ret = strict_strtoul(ptr, 0, &scsi_lun_id);
+			if (ret < 0) {
+				printk("strict_strtoul() failed for"
+					" scsi_lun_id=\n");
+				break;
+			}
+			pdv->pdv_lun_id = (int)scsi_lun_id;
+			printk(KERN_INFO "PSCSI[%d]: Referencing SCSI LUN ID:"
+				" %d\n", sh->host_no, pdv->pdv_lun_id);
+			pdv->pdv_flags |= PDF_HAS_LUN_ID;
+			params++;
+		} else
+			cur = NULL;
+	}
+
+out:
+	kfree(buf);
+	return (params) ? count : -EINVAL;
+}
+
+ssize_t pscsi_check_configfs_dev_params(
+	se_hba_t *hba,
+	se_subsystem_dev_t *se_dev)
+{
+	pscsi_dev_virt_t *pdv = (pscsi_dev_virt_t *) se_dev->se_dev_su_ptr;
+
+	if (!(pdv->pdv_flags & PDF_HAS_CHANNEL_ID) ||
+	    !(pdv->pdv_flags & PDF_HAS_TARGET_ID) ||
+	    !(pdv->pdv_flags & PDF_HAS_TARGET_ID)) {
+		printk(KERN_ERR "Missing scsi_channel_id=, scsi_target_id= and"
+			" scsi_lun_id= parameters\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+ssize_t pscsi_show_configfs_dev_params(
+	se_hba_t *hba,
+	se_subsystem_dev_t *se_dev,
+	char *page)
+{
+	pscsi_dev_virt_t *pdv = (pscsi_dev_virt_t *) se_dev->se_dev_su_ptr;
+	int bl = 0;
+
+	__pscsi_get_dev_info(pdv, page, &bl);
+	return (ssize_t)bl;
+}
+
+se_device_t *pscsi_create_virtdevice_from_fd(
+	se_subsystem_dev_t *se_dev,
+	const char *page)
+{
+	pscsi_dev_virt_t *pdv = (pscsi_dev_virt_t *) se_dev->se_dev_su_ptr;
+	se_device_t *dev = NULL;
+	se_hba_t *hba = se_dev->se_dev_hba;
+	struct block_device *bd = NULL;
+	struct file *filp;
+	struct gendisk *gd = NULL;
+	struct inode *inode;
+	struct scsi_device *sd = NULL;
+	struct Scsi_Host *sh = (struct Scsi_Host *)hba->hba_ptr;
+	char *p = (char *)page;
+	unsigned long long fd;
+	int ret;
+
+	ret = strict_strtoull(p, 0, (unsigned long long *)&fd);
+	if (ret < 0) {
+		printk(KERN_ERR "strict_strtol() failed for fd\n");
+		return ERR_PTR(-EINVAL);
+	}
+	if ((fd < 3 || fd > 7)) {
+		printk(KERN_ERR "PSCSI: Illegal value of file descriptor:"
+			" %llu\n", fd);
+		return ERR_PTR(-EINVAL);
+	}
+	filp = fget(fd);
+	if (!(filp)) {
+		printk(KERN_ERR "PSCSI: Unable to fget() fd: %llu\n", fd);
+		return ERR_PTR(-EBADF);
+	}
+	inode = igrab(filp->f_mapping->host);
+	if (!(inode)) {
+		printk(KERN_ERR "PSCSI: Unable to locate struct inode for"
+			" struct block_device fd\n");
+		fput(filp);
+		return ERR_PTR(-EINVAL);
+	}
+	/*
+	 * Look for struct scsi_device with a backing struct block_device.
+	 *
+	 * This means struct scsi_device->type == TYPE_DISK && TYPE_ROM.
+	 */
+	if (S_ISBLK(inode->i_mode)) {
+		bd = I_BDEV(filp->f_mapping->host);
+		if (!(bd)) {
+			printk(KERN_ERR "PSCSI: Unable to locate struct"
+				" block_device from I_BDEV()\n");
+			iput(inode);
+			fput(filp);
+			return ERR_PTR(-EINVAL);
+		}
+		gd = bd->bd_disk;
+		if (!(gd)) {
+			printk(KERN_ERR "PSCSI: Unable to locate struct gendisk"
+				" from struct block_device\n");
+			iput(inode);
+			fput(filp);
+			return ERR_PTR(-EINVAL);
+		}
+		/*
+		 * This struct gendisk->driver_fs() is marked as "// remove'
+		 * in include/linux/genhd.h..
+		 *
+		 * Currently in drivers/scsi/s[d,r].c:s[d,r]_probe(), this
+		 * pointer gets set by struct scsi_device->sdev_gendev.
+		 *
+		 * Is there a better way to locate struct scsi_device from
+		 * struct inode..?
+		 */
+		if (!(gd->driverfs_dev)) {
+			printk(KERN_ERR "PSCSI: struct gendisk->driverfs_dev"
+					" is NULL!\n");
+			iput(inode);
+			fput(filp);
+			return ERR_PTR(-EINVAL);
+		}
+		sd = to_scsi_device(gd->driverfs_dev);
+		if (!(sd)) {
+			printk(KERN_ERR "PSCSI: Unable to locate struct"
+				" scsi_device from struct gendisk->"
+				"driverfs_dev\n");
+			iput(inode);
+			fput(filp);
+			return ERR_PTR(-EINVAL);
+		}
+		if (sd->host != sh) {
+			printk(KERN_ERR "PSCSI: Trying to attach scsi_device"
+				" Host ID: %d, but se_hba_t has SCSI Host ID:"
+				" %d\n", sd->host->host_no, sh->host_no);
+			iput(inode);
+			fput(filp);
+			return ERR_PTR(-EINVAL);
+		}
+		/*
+		 * pscsi_create_type_[disk,rom]() will release host_lock..
+		 */
+		spin_lock_irq(sh->host_lock);
+		switch (sd->type) {
+		case TYPE_DISK:
+			dev = pscsi_create_type_disk(sd, pdv, se_dev,
+					se_dev->se_dev_hba);
+			break;
+		case TYPE_ROM:
+			dev = pscsi_create_type_rom(sd, pdv, se_dev,
+					se_dev->se_dev_hba);
+			break;
+		default:
+			printk(KERN_ERR "PSCSI: Unable to handle type S_ISBLK()"
+				" == TRUE Type: %s\n",
+				scsi_device_type(sd->type));
+			spin_unlock_irq(sh->host_lock);
+			iput(inode);
+			fput(filp);
+			return ERR_PTR(-ENOSYS);
+		}
+	} else if (S_ISCHR(inode->i_mode)) {
+		/*
+		 * FIXME: Figure how to get struct scsi_device from character
+		 * device's struct inode.
+		 */
+		printk(KERN_ERR "SCSI Character Device unsupported via"
+			" configfs/fd  method.  Use configfs/control"
+				" instead\n");
+		iput(inode);
+		fput(filp);
+		return ERR_PTR(-ENOSYS);
+	} else {
+		printk(KERN_ERR "PSCSI: File destriptor is not SCSI block or"
+			" character device, ignoring\n");
+		iput(inode);
+		fput(filp);
+		return ERR_PTR(-ENODEV);
+	}
+
+	iput(inode);
+	fput(filp);
+	return dev;
+}
+
+void pscsi_get_plugin_info(void *p, char *b, int *bl)
+{
+	*bl += sprintf(b + *bl, "TCM SCSI Plugin %s\n", PSCSI_VERSION);
+}
+
+void pscsi_get_hba_info(se_hba_t *hba, char *b, int *bl)
+{
+	struct Scsi_Host *sh = (struct Scsi_Host *) hba->hba_ptr;
+
+	*bl += sprintf(b + *bl, "Core Host ID: %u  SCSI Host ID: %u\n",
+			 hba->hba_id, sh->host_no);
+	*bl += sprintf(b + *bl, "        SCSI HBA: %s  <local>\n",
+		(sh->hostt->name) ? (sh->hostt->name) : "Unknown");
+}
+
+void pscsi_get_dev_info(se_device_t *dev, char *b, int *bl)
+{
+	pscsi_dev_virt_t *pdv = (pscsi_dev_virt_t *) dev->dev_ptr;
+
+	__pscsi_get_dev_info(pdv, b, bl);
+}
+
+void __pscsi_get_dev_info(pscsi_dev_virt_t *pdv, char *b, int *bl)
+{
+	int i;
+	struct scsi_device *sd = (struct scsi_device *) pdv->pdv_sd;
+
+	*bl += sprintf(b + *bl, "SCSI Device Bus Location:"
+		" Channel ID: %d Target ID: %d LUN: %d\n",
+		pdv->pdv_channel_id, pdv->pdv_target_id, pdv->pdv_lun_id);
+
+	if (sd) {
+		*bl += sprintf(b + *bl, "        ");
+		*bl += sprintf(b + *bl, "Vendor: ");
+		for (i = 0; i < 8; i++) {
+			if (ISPRINT(sd->vendor[i]))   /* printable character? */
+				*bl += sprintf(b + *bl, "%c", sd->vendor[i]);
+			else
+				*bl += sprintf(b + *bl, " ");
+		}
+		*bl += sprintf(b + *bl, " Model: ");
+		for (i = 0; i < 16; i++) {
+			if (ISPRINT(sd->model[i]))   /* printable character ? */
+				*bl += sprintf(b + *bl, "%c", sd->model[i]);
+			else
+				*bl += sprintf(b + *bl, " ");
+		}
+		*bl += sprintf(b + *bl, " Rev: ");
+		for (i = 0; i < 4; i++) {
+			if (ISPRINT(sd->rev[i]))   /* printable character ? */
+				*bl += sprintf(b + *bl, "%c", sd->rev[i]);
+			else
+				*bl += sprintf(b + *bl, " ");
+		}
+
+		if (sd->type == TYPE_DISK) {
+			struct scsi_disk *sdisk =
+					dev_get_drvdata(&sd->sdev_gendev);
+			struct gendisk *disk = (struct gendisk *) sdisk->disk;
+			struct block_device *bdev = bdget(MKDEV(disk->major,
+						disk->first_minor));
+
+			bdev->bd_disk = disk;
+			*bl += sprintf(b + *bl, "   %s\n", (!bdev->bd_holder) ?
+					"" : (bdev->bd_holder ==
+					(struct scsi_device *)sd) ?
+					"CLAIMED: PSCSI" : "CLAIMED: OS");
+		} else
+			*bl += sprintf(b + *bl, "\n");
+	}
+
+	return;
+}
+
+static void pscsi_bi_endio(struct bio *bio, int error)
+{
+	bio_put(bio);
+}
+
+static inline struct bio *pscsi_get_bio(pscsi_dev_virt_t *pdv, int sg_num)
+{
+	struct bio *bio;
+	/*
+	 * Use bio_malloc() following the comment in for bio -> struct request
+	 * in block/blk-core.c:blk_make_request()
+	 */
+	bio = bio_kmalloc(GFP_KERNEL, sg_num);
+	if (!(bio)) {
+		printk(KERN_ERR "PSCSI: bio_kmalloc() failed\n");
+		return NULL;
+	}
+	bio->bi_end_io = pscsi_bi_endio;
+
+	return bio;
+}
+
+#if 0
+#define DEBUG_PSCSI(x...) printk(x)
+#else
+#define DEBUG_PSCSI(x...)
+#endif
+
+/*      pscsi_map_task_SG():
+ *
+ *
+ */
+int pscsi_map_task_SG(se_task_t *task)
+{
+	pscsi_plugin_task_t *pt = (pscsi_plugin_task_t *) task->transport_req;
+	pscsi_dev_virt_t *pdv = (pscsi_dev_virt_t *) task->se_dev->dev_ptr;
+	struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
+	struct page *page;
+	struct scatterlist *sg;
+	u32 data_len = task->task_size, i, len, bytes, off;
+	int nr_pages = (task->task_size + task->task_sg[0].offset +
+			PAGE_SIZE - 1) >> PAGE_SHIFT;
+	int nr_vecs = 0, ret = 0;
+	int rw = (TASK_CMD(task)->data_direction == SE_DIRECTION_WRITE);
+
+	if (!task->task_size)
+		return 0;
+	/*
+	 * For SCF_SCSI_DATA_SG_IO_CDB, Use fs/bio.c:bio_add_page() to setup
+	 * the bio_vec maplist from TC< se_mem_t -> task->task_sg ->
+	 * struct scatterlist memory.  The se_task_t->task_sg[] currently needs
+	 * to be attached to struct bios for submission to Linux/SCSI using
+	 * struct request to struct scsi_device->request_queue.
+	 *
+	 * Note that this will be changing post v2.6.28 as Target_Core_Mod/pSCSI
+	 * is ported to upstream SCSI passthrough functionality that accepts
+	 * struct scatterlist->page_link or struct page as a paraemeter.
+	 */
+	DEBUG_PSCSI("PSCSI: nr_pages: %d\n", nr_pages);
+
+	for_each_sg(task->task_sg, sg, task->task_sg_num, i) {
+		page = sg_page(sg);
+		off = sg->offset;
+		len = sg->length;
+
+		DEBUG_PSCSI("PSCSI: i: %d page: %p len: %d off: %d\n", i,
+			page, len, off);
+
+		while (len > 0 && data_len > 0) {
+			bytes = min_t(unsigned int, len, PAGE_SIZE - off);
+			bytes = min(bytes, data_len);
+
+			if (!(bio)) {
+				nr_vecs = min_t(int, BIO_MAX_PAGES, nr_pages);
+				nr_pages -= nr_vecs;
+				/*
+				 * Calls bio_kmalloc() and sets bio->bi_end_io()
+				 */
+				bio = pscsi_get_bio(pdv, nr_vecs);
+				if (!(bio))
+					goto fail;
+				/*
+				 * FIXME: Use bio_set_dir() when avaliable
+				 */
+				if (rw)
+					bio->bi_rw |= (1 << BIO_RW);
+
+				DEBUG_PSCSI("PSCSI: Allocated bio: %p,"
+					" dir: %s nr_vecs: %d\n", bio,
+					(rw) ? "rw" : "r", nr_vecs);
+				/*
+				 * Set *hbio pointer to handle the case:
+				 * nr_pages > BIO_MAX_PAGES, where additional
+				 * bios need to be added to complete a given
+				 * se_task_t
+				 */
+				if (!hbio)
+					hbio = tbio = bio;
+				else
+					tbio = tbio->bi_next = bio;
+			}
+
+			DEBUG_PSCSI("PSCSI: Calling bio_add_pc_page() i: %d"
+				" bio: %p page: %p len: %d off: %d\n", i, bio,
+				page, len, off);
+
+			ret = bio_add_pc_page(pdv->pdv_sd->request_queue,
+					bio, page, bytes, off);
+			if (ret != bytes)
+				goto fail;
+
+			DEBUG_PSCSI("PSCSI: bio->bi_vcnt: %d nr_vecs: %d\n",
+				bio->bi_vcnt, nr_vecs);
+
+			if (bio->bi_vcnt > nr_vecs) {
+				DEBUG_PSCSI("PSCSI: Reached bio->bi_vcnt max:"
+					" %d i: %d bio: %p, allocating another"
+					" bio\n", bio->bi_vcnt, i, bio);
+				/*
+				 * Clear the pointer so that another bio will
+				 * be allocated with pscsi_get_bio() above, the
+				 * current bio has already been set *tbio and
+				 * bio->bi_next.
+				 */
+				bio = NULL;
+			}
+
+			page++;
+			len -= bytes;
+			data_len -= bytes;
+			off = 0;
+		}
+	}
+	/*
+	 * Starting with v2.6.31, call blk_make_request() passing in *hbio to
+	 * allocate the pSCSI task a struct request.
+	 */
+	pt->pscsi_req = blk_make_request(pdv->pdv_sd->request_queue,
+				hbio, GFP_KERNEL);
+	if (!(pt->pscsi_req)) {
+		printk(KERN_ERR "pSCSI: blk_make_request() failed\n");
+		goto fail;
+	}
+	/*
+	 * Setup the newly allocated struct request for REQ_TYPE_BLOCK_PC,
+	 * and setup rq callback, CDB and sense.
+	 */
+	pscsi_blk_init_request(task, pt);
+
+	return task->task_sg_num;
+fail:
+	while (hbio) {
+		bio = hbio;
+		hbio = hbio->bi_next;
+		bio->bi_next = NULL;
+		bio_endio(bio, 0);
+	}
+	return ret;
+}
+
+/*	pscsi_map_task_non_SG():
+ *
+ *
+ */
+int pscsi_map_task_non_SG(se_task_t *task)
+{
+	se_cmd_t *cmd = TASK_CMD(task);
+	pscsi_plugin_task_t *pt = (pscsi_plugin_task_t *) task->transport_req;
+	pscsi_dev_virt_t *pdv = (pscsi_dev_virt_t *) task->se_dev->dev_ptr;
+	int ret = 0;
+
+	if (!task->task_size)
+		return 0;
+
+	ret = blk_rq_map_kern(pdv->pdv_sd->request_queue,
+			pt->pscsi_req, T_TASK(cmd)->t_task_buf,
+			task->task_size, GFP_KERNEL);
+	if (ret < 0) {
+		printk(KERN_ERR "PSCSI: blk_rq_map_kern() failed: %d\n", ret);
+		return PYX_TRANSPORT_LU_COMM_FAILURE;
+	}
+	return 0;
+}
+
+/*	pscsi_CDB_inquiry():
+ *
+ *
+ */
+int pscsi_CDB_inquiry(se_task_t *task, u32 size)
+{
+	pscsi_plugin_task_t *pt = (pscsi_plugin_task_t *) task->transport_req;
+
+	pt->pscsi_direction = DMA_FROM_DEVICE;
+	if (pscsi_blk_get_request(task) < 0)
+		return -1;
+
+	return pscsi_map_task_non_SG(task);
+}
+
+int pscsi_CDB_none(se_task_t *task, u32 size)
+{
+	pscsi_plugin_task_t *pt = (pscsi_plugin_task_t *) task->transport_req;
+
+	pt->pscsi_direction = DMA_NONE;
+
+	return pscsi_blk_get_request(task);
+}
+
+/*	pscsi_CDB_read_non_SG():
+ *
+ *
+ */
+int pscsi_CDB_read_non_SG(se_task_t *task, u32 size)
+{
+	pscsi_plugin_task_t *pt = (pscsi_plugin_task_t *) task->transport_req;
+
+	pt->pscsi_direction = DMA_FROM_DEVICE;
+
+	if (pscsi_blk_get_request(task) < 0)
+		return PYX_TRANSPORT_LU_COMM_FAILURE;
+
+	return pscsi_map_task_non_SG(task);
+}
+
+/*	pscsi_CDB_read_SG():
+ *
+ *
+ */
+int pscsi_CDB_read_SG(se_task_t *task, u32 size)
+{
+	pscsi_plugin_task_t *pt = (pscsi_plugin_task_t *) task->transport_req;
+
+	pt->pscsi_direction = DMA_FROM_DEVICE;
+	/*
+	 * pscsi_map_task_SG() calls block/blk-core.c:blk_make_request()
+	 * for >= v2.6.31 pSCSI
+	 */
+	if (pscsi_map_task_SG(task) < 0)
+		return PYX_TRANSPORT_LU_COMM_FAILURE;
+
+	return task->task_sg_num;
+}
+
+/*	pscsi_CDB_write_non_SG():
+ *
+ *
+ */
+int pscsi_CDB_write_non_SG(se_task_t *task, u32 size)
+{
+	pscsi_plugin_task_t *pt = (pscsi_plugin_task_t *) task->transport_req;
+
+	pt->pscsi_direction = DMA_TO_DEVICE;
+
+	if (pscsi_blk_get_request(task) < 0)
+		return PYX_TRANSPORT_LU_COMM_FAILURE;
+
+	return pscsi_map_task_non_SG(task);
+}
+
+/*	pscsi_CDB_write_SG():
+ *
+ *
+ */
+int pscsi_CDB_write_SG(se_task_t *task, u32 size)
+{
+	pscsi_plugin_task_t *pt = (pscsi_plugin_task_t *) task->transport_req;
+
+	pt->pscsi_direction = DMA_TO_DEVICE;
+	/*
+	 * pscsi_map_task_SG() calls block/blk-core.c:blk_make_request()
+	 * for >= v2.6.31 pSCSI
+	 */
+	if (pscsi_map_task_SG(task) < 0)
+		return PYX_TRANSPORT_LU_COMM_FAILURE;
+
+	return task->task_sg_num;
+}
+
+/*	pscsi_check_lba():
+ *
+ *
+ */
+int pscsi_check_lba(unsigned long long lba, se_device_t *dev)
+{
+	return 0;
+}
+
+/*	pscsi_check_for_SG():
+ *
+ *
+ */
+int pscsi_check_for_SG(se_task_t *task)
+{
+	return task->task_sg_num;
+}
+
+/*	pscsi_get_cdb():
+ *
+ *
+ */
+unsigned char *pscsi_get_cdb(se_task_t *task)
+{
+	pscsi_plugin_task_t *pt = (pscsi_plugin_task_t *) task->transport_req;
+
+	return pt->pscsi_cdb;
+}
+
+/*	pscsi_get_sense_buffer():
+ *
+ *
+ */
+unsigned char *pscsi_get_sense_buffer(se_task_t *task)
+{
+	pscsi_plugin_task_t *pt = (pscsi_plugin_task_t *) task->transport_req;
+
+	return (unsigned char *)&pt->pscsi_sense[0];
+}
+
+/*	pscsi_get_blocksize():
+ *
+ *
+ */
+u32 pscsi_get_blocksize(se_device_t *dev)
+{
+	pscsi_dev_virt_t *pdv = (pscsi_dev_virt_t *) dev->dev_ptr;
+	struct scsi_device *sd = (struct scsi_device *) pdv->pdv_sd;
+
+	return sd->sector_size;
+}
+
+/*	pscsi_get_device_rev():
+ *
+ *
+ */
+u32 pscsi_get_device_rev(se_device_t *dev)
+{
+	pscsi_dev_virt_t *pdv = (pscsi_dev_virt_t *) dev->dev_ptr;
+	struct scsi_device *sd = (struct scsi_device *) pdv->pdv_sd;
+
+	return (sd->scsi_level - 1) ? sd->scsi_level - 1 : 1;
+}
+
+/*	pscsi_get_device_type():
+ *
+ *
+ */
+u32 pscsi_get_device_type(se_device_t *dev)
+{
+	pscsi_dev_virt_t *pdv = (pscsi_dev_virt_t *) dev->dev_ptr;
+	struct scsi_device *sd = (struct scsi_device *) pdv->pdv_sd;
+
+	return sd->type;
+}
+
+/*	pscsi_get_dma_length():
+ *
+ *
+ */
+u32 pscsi_get_dma_length(u32 task_size, se_device_t *dev)
+{
+	return PAGE_SIZE;
+}
+
+/*	pscsi_get_max_sectors():
+ *
+ *
+ */
+u32 pscsi_get_max_sectors(se_device_t *dev)
+{
+	pscsi_dev_virt_t *pdv = (pscsi_dev_virt_t *) dev->dev_ptr;
+	struct scsi_device *sd = (struct scsi_device *) pdv->pdv_sd;
+
+	return (sd->host->max_sectors > sd->request_queue->limits.max_sectors) ?
+		sd->request_queue->limits.max_sectors : sd->host->max_sectors;
+}
+
+/*	pscsi_get_queue_depth():
+ *
+ *
+ */
+u32 pscsi_get_queue_depth(se_device_t *dev)
+{
+	pscsi_dev_virt_t *pdv = (pscsi_dev_virt_t *) dev->dev_ptr;
+	struct scsi_device *sd = (struct scsi_device *) pdv->pdv_sd;
+
+	return sd->queue_depth;
+}
+
+/*	pscsi_handle_SAM_STATUS_failures():
+ *
+ *
+ */
+static inline void pscsi_process_SAM_status(
+	se_task_t *task,
+	pscsi_plugin_task_t *pt)
+{
+	task->task_scsi_status = status_byte(pt->pscsi_result);
+	if ((task->task_scsi_status)) {
+		task->task_scsi_status <<= 1;
+		printk(KERN_INFO "PSCSI Status Byte exception at task: %p CDB:"
+			" 0x%02x Result: 0x%08x\n", task, pt->pscsi_cdb[0],
+			pt->pscsi_result);
+	}
+
+	switch (host_byte(pt->pscsi_result)) {
+	case DID_OK:
+		transport_complete_task(task, (!task->task_scsi_status));
+		break;
+	default:
+		printk(KERN_INFO "PSCSI Host Byte exception at task: %p CDB:"
+			" 0x%02x Result: 0x%08x\n", task, pt->pscsi_cdb[0],
+			pt->pscsi_result);
+		task->task_scsi_status = SAM_STAT_CHECK_CONDITION;
+		task->task_error_status = PYX_TRANSPORT_UNKNOWN_SAM_OPCODE;
+		TASK_CMD(task)->transport_error_status =
+					PYX_TRANSPORT_UNKNOWN_SAM_OPCODE;
+		transport_complete_task(task, 0);
+		break;
+	}
+
+	return;
+}
+
+void pscsi_req_done(struct request *req, int uptodate)
+{
+	se_task_t *task = (se_task_t *)req->end_io_data;
+	pscsi_plugin_task_t *pt = (pscsi_plugin_task_t *)task->transport_req;
+
+	pt->pscsi_result = req->errors;
+	pt->pscsi_resid = req->resid_len;
+
+	pscsi_process_SAM_status(task, pt);
+	__blk_put_request(req->q, req);
+	pt->pscsi_req = NULL;
+}
diff --git a/include/target/target_core_pscsi.h b/include/target/target_core_pscsi.h
new file mode 100644
index 0000000..da1d618
--- /dev/null
+++ b/include/target/target_core_pscsi.h
@@ -0,0 +1,186 @@
+/*******************************************************************************
+ * Filename:  target_core_pscsi.h
+ *
+ * This file contains the generic target mode <-> Linux SCSI subsystem plugin.
+ * specific definitions and prototypes.
+ *
+ * Copyright (c) 2003, 2004, 2005 PyX Technologies, Inc.
+ * Copyright (c) 2005, 2006, 2007 SBE, Inc.
+ * Copyright (c) 2007-2009 Rising Tide Software, Inc.
+ * Copyright (c) 2008-2009 Linux-iSCSI.org
+ *
+ * Nicholas A. Bellinger <nab@kernel.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ ******************************************************************************/
+
+
+#ifndef TARGET_CORE_PSCSI_H
+#define TARGET_CORE_PSCSI_H
+
+#define PSCSI_VERSION		"v3.1"
+
+/* used in pscsi_find_alloc_len() */
+#ifndef INQUIRY_DATA_SIZE
+#define INQUIRY_DATA_SIZE	0x24
+#endif
+
+/* used in pscsi_add_device_to_list() */
+#define PSCSI_DEFAULT_QUEUEDEPTH	1
+
+#define PS_RETRY		5
+#define PS_TIMEOUT_DISK		(15*HZ)
+#define PS_TIMEOUT_OTHER	(500*HZ)
+
+extern se_global_t *se_global;
+extern struct block_device *linux_blockdevice_claim(int, int, void *);
+extern int linux_blockdevice_release(int, int, struct block_device *);
+extern int linux_blockdevice_check(int, int);
+
+extern int pscsi_CDB_inquiry(se_task_t *, u32);
+extern int pscsi_CDB_none(se_task_t *, u32);
+extern int pscsi_CDB_read_non_SG(se_task_t *, u32);
+extern int pscsi_CDB_read_SG(se_task_t *, u32);
+extern int pscsi_CDB_write_non_SG(se_task_t *, u32);
+extern int pscsi_CDB_write_SG(se_task_t *, u32);
+
+#ifndef PSCSI_INCLUDE_STRUCTS
+extern int pscsi_attach_hba(se_hba_t *, u32);
+extern int pscsi_detach_hba(se_hba_t *);
+extern int pscsi_claim_phydevice(se_hba_t *, se_device_t *);
+extern int pscsi_release_phydevice(se_device_t *);
+extern void *pscsi_allocate_virtdevice(se_hba_t *, const char *);
+extern se_device_t *pscsi_create_virtdevice(se_hba_t *, se_subsystem_dev_t *,
+					void *);
+extern int pscsi_activate_device(se_device_t *);
+extern void pscsi_deactivate_device(se_device_t *);
+extern void pscsi_free_device(void *);
+extern int pscsi_transport_complete(se_task_t *);
+extern void *pscsi_allocate_request(se_task_t *, se_device_t *);
+extern int pscsi_do_task(se_task_t *);
+extern void pscsi_free_task(se_task_t *);
+extern ssize_t pscsi_set_configfs_dev_params(se_hba_t *, se_subsystem_dev_t *,
+						const char *, ssize_t);
+extern ssize_t pscsi_check_configfs_dev_params(se_hba_t *,
+						se_subsystem_dev_t *);
+extern ssize_t pscsi_show_configfs_dev_params(se_hba_t *, se_subsystem_dev_t *,
+						char *);
+extern se_device_t *pscsi_create_virtdevice_from_fd(se_subsystem_dev_t *,
+						const char *);
+extern void pscsi_get_plugin_info(void *, char *, int *);
+extern void pscsi_get_hba_info(se_hba_t *, char *, int *);
+extern void pscsi_get_dev_info(se_device_t *, char *, int *);
+extern int pscsi_check_lba(unsigned long long, se_device_t *);
+extern int pscsi_check_for_SG(se_task_t *);
+extern unsigned char *pscsi_get_cdb(se_task_t *);
+extern unsigned char *pscsi_get_sense_buffer(se_task_t *);
+extern u32 pscsi_get_blocksize(se_device_t *);
+extern u32 pscsi_get_device_rev(se_device_t *);
+extern u32 pscsi_get_device_type(se_device_t *);
+extern u32 pscsi_get_dma_length(u32, se_device_t *);
+extern u32 pscsi_get_max_sectors(se_device_t *);
+extern u32 pscsi_get_queue_depth(se_device_t *);
+extern void pscsi_req_done(struct request *, int);
+#endif
+
+#include <linux/device.h>
+#include <scsi/scsi_driver.h>
+#include <scsi/scsi_device.h>
+#include <linux/kref.h>
+#include <linux/kobject.h>
+
+typedef struct pscsi_plugin_task_s {
+	unsigned char pscsi_cdb[SCSI_CDB_SIZE];
+	unsigned char pscsi_sense[SCSI_SENSE_BUFFERSIZE];
+	int	pscsi_direction;
+	int	pscsi_result;
+	u32	pscsi_resid;
+	struct request *pscsi_req;
+} pscsi_plugin_task_t;
+
+#define PDF_HAS_CHANNEL_ID	0x01
+#define PDF_HAS_TARGET_ID	0x02
+#define PDF_HAS_LUN_ID		0x04
+#define PDF_HAS_VPD_UNIT_SERIAL 0x08
+#define PDF_HAS_VPD_DEV_IDENT	0x10
+
+typedef struct pscsi_dev_virt_s {
+	int	pdv_flags;
+	int	pdv_channel_id;
+	int	pdv_target_id;
+	int	pdv_lun_id;
+	struct scsi_device *pdv_sd;
+	struct se_hba_s *pdv_se_hba;
+} pscsi_dev_virt_t;
+
+extern void __pscsi_get_dev_info(pscsi_dev_virt_t *, char *, int *);
+
+/*
+ * We use the generic command sequencer, so we must setup
+ * se_subsystem_spc_t.
+ */
+#ifndef PSCSI_INCLUDE_STRUCTS
+
+se_subsystem_spc_t pscsi_template_spc = {
+	.inquiry		= pscsi_CDB_inquiry,
+	.none			= pscsi_CDB_none,
+	.read_non_SG		= pscsi_CDB_read_non_SG,
+	.read_SG		= pscsi_CDB_read_SG,
+	.write_non_SG		= pscsi_CDB_write_non_SG,
+	.write_SG		= pscsi_CDB_write_SG,
+};
+
+se_subsystem_api_t pscsi_template = {
+	.name			= "pscsi",			\
+	.type			= PSCSI,			\
+	.transport_type		= TRANSPORT_PLUGIN_PHBA_PDEV,	\
+	.attach_hba		= pscsi_attach_hba,		\
+	.detach_hba		= pscsi_detach_hba,		\
+	.activate_device	= pscsi_activate_device,	\
+	.deactivate_device	= pscsi_deactivate_device,	\
+	.claim_phydevice	= pscsi_claim_phydevice,	\
+	.allocate_virtdevice	= pscsi_allocate_virtdevice,	\
+	.create_virtdevice	= pscsi_create_virtdevice,	\
+	.free_device		= pscsi_free_device,		\
+	.release_phydevice	= pscsi_release_phydevice,	\
+	.transport_complete	= pscsi_transport_complete,	\
+	.allocate_request	= pscsi_allocate_request,	\
+	.do_task		= pscsi_do_task,		\
+	.free_task		= pscsi_free_task,		\
+	.check_configfs_dev_params = pscsi_check_configfs_dev_params, \
+	.set_configfs_dev_params = pscsi_set_configfs_dev_params, \
+	.show_configfs_dev_params = pscsi_show_configfs_dev_params, \
+	.create_virtdevice_from_fd = pscsi_create_virtdevice_from_fd, \
+	.get_plugin_info	= pscsi_get_plugin_info,	\
+	.get_hba_info		= pscsi_get_hba_info,		\
+	.get_dev_info		= pscsi_get_dev_info,		\
+	.check_lba		= pscsi_check_lba,		\
+	.check_for_SG		= pscsi_check_for_SG,		\
+	.get_cdb		= pscsi_get_cdb,		\
+	.get_sense_buffer	= pscsi_get_sense_buffer,	\
+	.get_blocksize		= pscsi_get_blocksize,		\
+	.get_device_rev		= pscsi_get_device_rev,		\
+	.get_device_type	= pscsi_get_device_type,	\
+	.get_dma_length		= pscsi_get_dma_length,		\
+	.get_max_sectors	= pscsi_get_max_sectors,	\
+	.get_queue_depth	= pscsi_get_queue_depth,	\
+	.write_pending		= NULL,				\
+	.spc			= &pscsi_template_spc,		\
+};
+
+#endif
+
+#endif   /*** TARGET_CORE_PSCSI_H ***/




^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2009-09-18 22:12 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-09-12  1:57 [RFC PATCH 10/19] Target_Core_Mod PSCSI <-> Linux/SCSI subsystem plugin Nicholas A. Bellinger
2009-09-18 22:08 Nicholas A. Bellinger

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®