lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [day] [month] [year] [list]
Message-Id: <1285195876-11747-1-git-send-email-nab@linux-iscsi.org>
Date:	Wed, 22 Sep 2010 15:51:16 -0700
From:	"Nicholas A. Bellinger" <nab@...ux-iscsi.org>
To:	linux-scsi <linux-scsi@...r.kernel.org>,
	linux-kernel <linux-kernel@...r.kernel.org>
Cc:	FUJITA Tomonori <fujita.tomonori@....ntt.co.jp>,
	Mike Christie <michaelc@...wisc.edu>,
	Christoph Hellwig <hch@....de>, Hannes Reinecke <hare@...e.de>,
	James Bottomley <James.Bottomley@...e.de>,
	Jens Axboe <axboe@...nel.dk>,
	Boaz Harrosh <bharrosh@...asas.com>,
	Nicholas Bellinger <nab@...ux-iscsi.org>
Subject: [RFC v2 17/21] tcm: Add RAMDISK_DR and RAMDISK_MCP subsystem plugins

From: Nicholas Bellinger <nab@...ux-iscsi.org>

This patch adds the RAMDISK_DR (memory map) and RAMDISK_MCP (memcpy) subsystem plugin
for accessing internally allocated struct scatterlist memory as a TCM storage object.
Note that this is the only TCM subsystem plugin that is still built into target_core_mod.ko

Signed-off-by: Nicholas A. Bellinger <nab@...ux-iscsi.org>
---
 drivers/target/target_core_rd.c | 1555 +++++++++++++++++++++++++++++++++++++++
 drivers/target/target_core_rd.h |   89 +++
 2 files changed, 1644 insertions(+), 0 deletions(-)
 create mode 100644 drivers/target/target_core_rd.c
 create mode 100644 drivers/target/target_core_rd.h

diff --git a/drivers/target/target_core_rd.c b/drivers/target/target_core_rd.c
new file mode 100644
index 0000000..b1f7f56
--- /dev/null
+++ b/drivers/target/target_core_rd.c
@@ -0,0 +1,1555 @@
+/*******************************************************************************
+ * Filename:  target_core_rd.c
+ *
+ * This file contains the Storage Engine <-> Ramdisk transport
+ * specific functions.
+ *
+ * Copyright (c) 2003, 2004, 2005 PyX Technologies, Inc.
+ * Copyright (c) 2005, 2006, 2007 SBE, Inc.
+ * Copyright (c) 2007-2010 Rising Tide Systems
+ * Copyright (c) 2008-2010 Linux-iSCSI.org
+ *
+ * Nicholas A. Bellinger <nab@...nel.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ ******************************************************************************/
+
+#include <linux/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 <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+
+#include <target/target_core_base.h>
+#include <target/target_core_device.h>
+#include <target/target_core_transport.h>
+#include <target/target_core_fabric_ops.h>
+
+#include "target_core_rd.h"
+
+static struct se_subsystem_api rd_dr_template;
+static struct se_subsystem_api rd_mcp_template;
+
+static void __rd_get_dev_info(struct rd_dev *, char *, int *);
+
+/* #define DEBUG_RAMDISK_MCP */
+/* #define DEBUG_RAMDISK_DR */
+
+/*	rd_attach_hba(): (Part of se_subsystem_api_t template)
+ *
+ *
+ */
+static int rd_attach_hba(struct se_hba *hba, u32 host_id)
+{
+	struct rd_host *rd_host;
+
+	rd_host = kzalloc(sizeof(struct rd_host), GFP_KERNEL);
+	if (!(rd_host)) {
+		printk(KERN_ERR "Unable to allocate memory for struct rd_host\n");
+		return -ENOMEM;
+	}
+
+	rd_host->rd_host_id = host_id;
+
+	atomic_set(&hba->left_queue_depth, RD_HBA_QUEUE_DEPTH);
+	atomic_set(&hba->max_queue_depth, RD_HBA_QUEUE_DEPTH);
+	hba->hba_ptr = (void *) rd_host;
+
+	printk(KERN_INFO "CORE_HBA[%d] - TCM Ramdisk HBA Driver %s on"
+		" Generic Target Core Stack %s\n", hba->hba_id,
+		RD_HBA_VERSION, TARGET_CORE_MOD_VERSION);
+	printk(KERN_INFO "CORE_HBA[%d] - Attached Ramdisk HBA: %u to Generic"
+		" Target Core TCQ Depth: %d MaxSectors: %u\n", hba->hba_id,
+		rd_host->rd_host_id, atomic_read(&hba->max_queue_depth),
+		RD_MAX_SECTORS);
+
+	return 0;
+}
+
+/*	rd_detach_hba(): (Part of se_subsystem_api_t template)
+ *
+ *
+ */
+static int rd_detach_hba(struct se_hba *hba)
+{
+	struct rd_host *rd_host;
+
+	if (!hba->hba_ptr) {
+		printk(KERN_ERR "hba->hba_ptr is NULL!\n");
+		return -1;
+	}
+
+	rd_host = (struct rd_host *) hba->hba_ptr;
+
+	printk(KERN_INFO "CORE_HBA[%d] - Detached Ramdisk HBA: %u from"
+		" Generic Target Core\n", hba->hba_id, rd_host->rd_host_id);
+
+	kfree(rd_host);
+	hba->hba_ptr = NULL;
+
+	return 0;
+}
+
+/*	rd_release_device_space():
+ *
+ *
+ */
+static void rd_release_device_space(struct rd_dev *rd_dev)
+{
+	u32 i, j, page_count = 0, sg_per_table;
+	struct rd_dev_sg_table *sg_table;
+	struct page *pg;
+	struct scatterlist *sg;
+
+	if (!rd_dev->sg_table_array || !rd_dev->sg_table_count)
+		return;
+
+	sg_table = rd_dev->sg_table_array;
+
+	for (i = 0; i < rd_dev->sg_table_count; i++) {
+		sg = sg_table[i].sg_table;
+		sg_per_table = sg_table[i].rd_sg_count;
+
+		for (j = 0; j < sg_per_table; j++) {
+			pg = sg_page(&sg[j]);
+			if ((pg)) {
+				__free_page(pg);
+				page_count++;
+			}
+		}
+
+		kfree(sg);
+	}
+
+	printk(KERN_INFO "CORE_RD[%u] - Released device space for Ramdisk"
+		" Device ID: %u, pages %u in %u tables total bytes %lu\n",
+		rd_dev->rd_host->rd_host_id, rd_dev->rd_dev_id, page_count,
+		rd_dev->sg_table_count, (unsigned long)page_count * PAGE_SIZE);
+
+	kfree(sg_table);
+	rd_dev->sg_table_array = NULL;
+	rd_dev->sg_table_count = 0;
+}
+
+
+/*	rd_build_device_space():
+ *
+ *
+ */
+static int rd_build_device_space(struct rd_dev *rd_dev)
+{
+	u32 i = 0, j, page_offset = 0, sg_per_table, sg_tables, total_sg_needed;
+	u32 max_sg_per_table = (RD_MAX_ALLOCATION_SIZE /
+				sizeof(struct scatterlist));
+	struct rd_dev_sg_table *sg_table;
+	struct page *pg;
+	struct scatterlist *sg;
+
+	if (rd_dev->rd_page_count <= 0) {
+		printk(KERN_ERR "Illegal page count: %u for Ramdisk device\n",
+			rd_dev->rd_page_count);
+		return -1;
+	}
+	total_sg_needed = rd_dev->rd_page_count;
+
+	sg_tables = (total_sg_needed / max_sg_per_table) + 1;
+
+	sg_table = kzalloc(sg_tables * sizeof(struct rd_dev_sg_table), GFP_KERNEL);
+	if (!(sg_table)) {
+		printk(KERN_ERR "Unable to allocate memory for Ramdisk"
+			" scatterlist tables\n");
+		return -1;
+	}
+
+	rd_dev->sg_table_array = sg_table;
+	rd_dev->sg_table_count = sg_tables;
+
+	while (total_sg_needed) {
+		sg_per_table = (total_sg_needed > max_sg_per_table) ?
+			max_sg_per_table : total_sg_needed;
+
+		sg = kzalloc(sg_per_table * sizeof(struct scatterlist),
+				GFP_KERNEL);
+		if (!(sg)) {
+			printk(KERN_ERR "Unable to allocate scatterlist array"
+				" for struct rd_dev\n");
+			return -1;
+		}
+
+		sg_init_table((struct scatterlist *)&sg[0], sg_per_table);
+
+		sg_table[i].sg_table = sg;
+		sg_table[i].rd_sg_count = sg_per_table;
+		sg_table[i].page_start_offset = page_offset;
+		sg_table[i++].page_end_offset = (page_offset + sg_per_table)
+						- 1;
+
+		for (j = 0; j < sg_per_table; j++) {
+			pg = (struct page *) alloc_pages(
+					GFP_KERNEL, 0);
+			if (!(pg)) {
+				printk(KERN_ERR "Unable to allocate scatterlist"
+					" pages for struct rd_dev_sg_table\n");
+				return -1;
+			}
+			sg_assign_page(&sg[j], pg);
+			sg[j].length = PAGE_SIZE;
+		}
+
+		page_offset += sg_per_table;
+		total_sg_needed -= sg_per_table;
+	}
+
+	printk(KERN_INFO "CORE_RD[%u] - Built Ramdisk Device ID: %u space of"
+		" %u pages in %u tables\n", rd_dev->rd_host->rd_host_id,
+		rd_dev->rd_dev_id, rd_dev->rd_page_count,
+		rd_dev->sg_table_count);
+
+	return 0;
+}
+
+static void *rd_allocate_virtdevice(
+	struct se_hba *hba,
+	const char *name,
+	int rd_direct)
+{
+	struct rd_dev *rd_dev;
+	struct rd_host *rd_host = (struct rd_host *) hba->hba_ptr;
+
+	rd_dev = kzalloc(sizeof(struct rd_dev), GFP_KERNEL);
+	if (!(rd_dev)) {
+		printk(KERN_ERR "Unable to allocate memory for struct rd_dev\n");
+		return NULL;
+	}
+
+	rd_dev->rd_host = rd_host;
+	rd_dev->rd_direct = rd_direct;
+
+	return rd_dev;
+}
+
+static void *rd_DIRECT_allocate_virtdevice(struct se_hba *hba, const char *name)
+{
+	return rd_allocate_virtdevice(hba, name, 1);
+}
+
+static void *rd_MEMCPY_allocate_virtdevice(struct se_hba *hba, const char *name)
+{
+	return rd_allocate_virtdevice(hba, name, 0);
+}
+
+/*	rd_create_virtdevice():
+ *
+ *
+ */
+static struct se_device *rd_create_virtdevice(
+	struct se_hba *hba,
+	struct se_subsystem_dev *se_dev,
+	void *p,
+	int rd_direct)
+{
+	struct se_device *dev;
+	struct rd_dev *rd_dev = (struct rd_dev *) p;
+	struct rd_host *rd_host = (struct rd_host *) hba->hba_ptr;
+	int dev_flags = 0;
+
+	if (rd_dev->rd_direct)
+		dev_flags |= DF_TRANSPORT_DMA_ALLOC;
+
+	if (rd_build_device_space(rd_dev) < 0)
+		goto fail;
+
+	dev = transport_add_device_to_core_hba(hba,
+			(rd_dev->rd_direct) ? &rd_dr_template :
+			&rd_mcp_template, se_dev, dev_flags, (void *)rd_dev);
+	if (!(dev))
+		goto fail;
+
+	rd_dev->rd_dev_id = rd_host->rd_host_dev_id_count++;
+	rd_dev->rd_queue_depth = dev->queue_depth;
+
+	printk(KERN_INFO "CORE_RD[%u] - Added TCM %s Ramdisk Device ID: %u of"
+		" %u pages in %u tables, %lu total bytes\n",
+		rd_host->rd_host_id, (!rd_dev->rd_direct) ? "MEMCPY" :
+		"DIRECT", rd_dev->rd_dev_id, rd_dev->rd_page_count,
+		rd_dev->sg_table_count,
+		(unsigned long)(rd_dev->rd_page_count * PAGE_SIZE));
+
+	return dev;
+
+fail:
+	rd_release_device_space(rd_dev);
+	return NULL;
+}
+
+static struct se_device *rd_DIRECT_create_virtdevice(
+	struct se_hba *hba,
+	struct se_subsystem_dev *se_dev,
+	void *p)
+{
+	return rd_create_virtdevice(hba, se_dev, p, 1);
+}
+
+static struct se_device *rd_MEMCPY_create_virtdevice(
+	struct se_hba *hba,
+	struct se_subsystem_dev *se_dev,
+	void *p)
+{
+	return rd_create_virtdevice(hba, se_dev, p, 0);
+}
+
+/*	rd_activate_device(): (Part of se_subsystem_api_t template)
+ *
+ *
+ */
+static int rd_activate_device(struct se_device *dev)
+{
+	struct rd_dev *rd_dev = (struct rd_dev *) dev->dev_ptr;
+	struct rd_host *rd_host = rd_dev->rd_host;
+
+	printk(KERN_INFO "CORE_RD[%u] - Activating Device with TCQ: %d at"
+		" Ramdisk Device ID: %d\n", rd_host->rd_host_id,
+		rd_dev->rd_queue_depth, rd_dev->rd_dev_id);
+
+	return 0;
+}
+
+/*	rd_deactivate_device(): (Part of se_subsystem_api_t template)
+ *
+ *
+ */
+static void rd_deactivate_device(struct se_device *dev)
+{
+	struct rd_dev *rd_dev = (struct rd_dev *) dev->dev_ptr;
+	struct rd_host *rd_host = rd_dev->rd_host;
+
+	printk(KERN_INFO "CORE_RD[%u] - Deactivating Device with TCQ: %d at"
+		" Ramdisk Device ID: %d\n", rd_host->rd_host_id,
+		rd_dev->rd_queue_depth, rd_dev->rd_dev_id);
+}
+
+/*	rd_free_device(): (Part of se_subsystem_api_t template)
+ *
+ *
+ */
+static void rd_free_device(void *p)
+{
+	struct rd_dev *rd_dev = (struct rd_dev *) p;
+
+	rd_release_device_space(rd_dev);
+	kfree(rd_dev);
+}
+
+/*	rd_transport_complete(): (Part of se_subsystem_api_t template)
+ *
+ *
+ */
+static int rd_transport_complete(struct se_task *task)
+{
+	return 0;
+}
+
+/*	rd_allocate_request(): (Part of se_subsystem_api_t template)
+ *
+ *
+ */
+static void *rd_allocate_request(
+	struct se_task *task,
+	struct se_device *dev)
+{
+	struct rd_request *rd_req;
+
+	rd_req = kzalloc(sizeof(struct rd_request), GFP_KERNEL);
+	if (!(rd_req)) {
+		printk(KERN_ERR "Unable to allocate struct rd_request\n");
+		return NULL;
+	}
+	rd_req->rd_dev = (struct rd_dev *) dev->dev_ptr;
+
+	return (void *)rd_req;
+}
+
+/*	rd_emulate_inquiry():
+ *
+ *
+ */
+static int rd_emulate_inquiry(struct se_task *task)
+{
+	unsigned char prod[64], se_location[128];
+	struct rd_dev *rd_dev = (struct rd_dev *) task->se_dev->dev_ptr;
+	struct se_cmd *cmd = TASK_CMD(task);
+	struct se_hba *hba = task->se_dev->se_hba;
+
+	memset(prod, 0, 64);
+	memset(se_location, 0, 128);
+
+	sprintf(prod, "RAMDISK-%s", (rd_dev->rd_direct) ? "DR" : "MCP");
+	sprintf(se_location, "%u_%u", hba->hba_id, rd_dev->rd_dev_id);
+
+	return transport_generic_emulate_inquiry(cmd, TYPE_DISK, prod,
+			(hba->transport->do_se_mem_map) ? RD_DR_VERSION :
+			RD_MCP_VERSION, se_location);
+}
+
+/*	rd_emulate_read_cap():
+ *
+ *
+ */
+static int rd_emulate_read_cap(struct se_task *task)
+{
+	struct rd_dev *rd_dev = (struct rd_dev *) task->se_dev->dev_ptr;
+	u32 blocks = ((rd_dev->rd_page_count * PAGE_SIZE) /
+		       DEV_ATTRIB(task->se_dev)->block_size) - 1;
+
+	if ((((rd_dev->rd_page_count * PAGE_SIZE) /
+	       DEV_ATTRIB(task->se_dev)->block_size) - 1) >= 0x00000000ffffffff)
+		blocks = 0xffffffff;
+
+	return transport_generic_emulate_readcapacity(TASK_CMD(task), blocks);
+}
+
+static int rd_emulate_read_cap16(struct se_task *task)
+{
+	struct rd_dev *rd_dev = (struct rd_dev *) task->se_dev->dev_ptr;
+	unsigned long long blocks_long = ((rd_dev->rd_page_count * PAGE_SIZE) /
+				   DEV_ATTRIB(task->se_dev)->block_size) - 1;
+
+	return transport_generic_emulate_readcapacity_16(TASK_CMD(task),
+				blocks_long);
+}
+
+/*	rd_emulate_scsi_cdb():
+ *
+ *
+ */
+static int rd_emulate_scsi_cdb(struct se_task *task)
+{
+	int ret;
+	struct se_cmd *cmd = TASK_CMD(task);
+	struct rd_request *rd_req = (struct rd_request *) task->transport_req;
+
+	switch (rd_req->rd_scsi_cdb[0]) {
+	case INQUIRY:
+		if (rd_emulate_inquiry(task) < 0)
+			return PYX_TRANSPORT_INVALID_CDB_FIELD;
+		break;
+	case READ_CAPACITY:
+		ret = rd_emulate_read_cap(task);
+		if (ret < 0)
+			return ret;
+		break;
+	case MODE_SENSE:
+		ret = transport_generic_emulate_modesense(TASK_CMD(task),
+				rd_req->rd_scsi_cdb, rd_req->rd_buf, 0,
+				TYPE_DISK);
+		if (ret < 0)
+			return ret;
+		break;
+	case MODE_SENSE_10:
+		ret = transport_generic_emulate_modesense(TASK_CMD(task),
+				rd_req->rd_scsi_cdb, rd_req->rd_buf, 1,
+				TYPE_DISK);
+		if (ret < 0)
+			return ret;
+		break;
+	case SERVICE_ACTION_IN:
+		if ((T_TASK(cmd)->t_task_cdb[1] & 0x1f) !=
+		     SAI_READ_CAPACITY_16) {
+			printk(KERN_ERR "Unsupported SA: 0x%02x\n",
+					T_TASK(cmd)->t_task_cdb[1] & 0x1f);
+			return PYX_TRANSPORT_UNKNOWN_SAM_OPCODE;
+		}
+		ret = rd_emulate_read_cap16(task);
+		if (ret < 0)
+			return ret;
+		break;
+	case REQUEST_SENSE:
+		ret = transport_generic_emulate_request_sense(cmd,
+				T_TASK(cmd)->t_task_cdb);
+		if (ret < 0)
+			return ret;
+		break;
+	case ALLOW_MEDIUM_REMOVAL:
+	case ERASE:
+	case REZERO_UNIT:
+	case SEEK_10:
+	case SPACE:
+	case START_STOP:
+	case SYNCHRONIZE_CACHE:
+	case TEST_UNIT_READY:
+	case VERIFY:
+	case WRITE_FILEMARKS:
+	case RESERVE:
+	case RESERVE_10:
+	case RELEASE:
+	case RELEASE_10:
+		break;
+	default:
+		printk(KERN_ERR "Unsupported SCSI Opcode: 0x%02x for"
+			" RAMDISKs\n", rd_req->rd_scsi_cdb[0]);
+		return PYX_TRANSPORT_UNKNOWN_SAM_OPCODE;
+	}
+
+	task->task_scsi_status = GOOD;
+	transport_complete_task(task, 1);
+
+	return PYX_TRANSPORT_SENT_TO_TRANSPORT;
+}
+
+/*	rd_get_sg_table():
+ *
+ *
+ */
+static struct rd_dev_sg_table *rd_get_sg_table(struct rd_dev *rd_dev, u32 page)
+{
+	u32 i;
+	struct rd_dev_sg_table *sg_table;
+
+	for (i = 0; i < rd_dev->sg_table_count; i++) {
+		sg_table = &rd_dev->sg_table_array[i];
+		if ((sg_table->page_start_offset <= page) &&
+		    (sg_table->page_end_offset >= page))
+			return sg_table;
+	}
+
+	printk(KERN_ERR "Unable to locate struct rd_dev_sg_table for page: %u\n",
+			page);
+
+	return NULL;
+}
+
+/*	rd_MEMCPY_read():
+ *
+ *
+ */
+static int rd_MEMCPY_read(struct rd_request *req)
+{
+	struct rd_dev *dev = req->rd_dev;
+	struct rd_dev_sg_table *table;
+	struct scatterlist *sg_d, *sg_s;
+	void *dst, *src;
+	u32 i = 0, j = 0, dst_offset = 0, src_offset = 0;
+	u32 length, page_end = 0, table_sg_end;
+	u32 rd_offset = req->rd_offset;
+
+	table = rd_get_sg_table(dev, req->rd_page);
+	if (!(table))
+		return -1;
+
+	table_sg_end = (table->page_end_offset - req->rd_page);
+	sg_d = (struct scatterlist *) req->rd_buf;
+	sg_s = &table->sg_table[req->rd_page - table->page_start_offset];
+#ifdef DEBUG_RAMDISK_MCP
+	printk(KERN_INFO "RD[%u]: Read LBA: %llu, Size: %u Page: %u, Offset:"
+		" %u\n", dev->rd_dev_id, req->rd_lba, req->rd_size,
+		req->rd_page, req->rd_offset);
+#endif
+	src_offset = rd_offset;
+
+	while (req->rd_size) {
+		if ((sg_d[i].length - dst_offset) <
+		    (sg_s[j].length - src_offset)) {
+			length = (sg_d[i].length - dst_offset);
+#ifdef DEBUG_RAMDISK_MCP
+			printk(KERN_INFO "Step 1 - sg_d[%d]: %p length: %d"
+				" offset: %u sg_s[%d].length: %u\n", i,
+				&sg_d[i], sg_d[i].length, sg_d[i].offset, j,
+				sg_s[j].length);
+			printk(KERN_INFO "Step 1 - length: %u dst_offset: %u"
+				" src_offset: %u\n", length, dst_offset,
+				src_offset);
+#endif
+			if (length > req->rd_size)
+				length = req->rd_size;
+
+			dst = sg_virt(&sg_d[i++]) + dst_offset;
+			if (!dst)
+				BUG();
+
+			src = sg_virt(&sg_s[j]) + src_offset;
+			if (!src)
+				BUG();
+
+			dst_offset = 0;
+			src_offset = length;
+			page_end = 0;
+		} else {
+			length = (sg_s[j].length - src_offset);
+#ifdef DEBUG_RAMDISK_MCP
+			printk(KERN_INFO "Step 2 - sg_d[%d]: %p length: %d"
+				" offset: %u sg_s[%d].length: %u\n", i,
+				&sg_d[i], sg_d[i].length, sg_d[i].offset,
+				j, sg_s[j].length);
+			printk(KERN_INFO "Step 2 - length: %u dst_offset: %u"
+				" src_offset: %u\n", length, dst_offset,
+				src_offset);
+#endif
+			if (length > req->rd_size)
+				length = req->rd_size;
+
+			dst = sg_virt(&sg_d[i]) + dst_offset;
+			if (!dst)
+				BUG();
+
+			if (sg_d[i].length == length) {
+				i++;
+				dst_offset = 0;
+			} else
+				dst_offset = length;
+
+			src = sg_virt(&sg_s[j++]) + src_offset;
+			if (!src)
+				BUG();
+
+			src_offset = 0;
+			page_end = 1;
+		}
+
+		memcpy(dst, src, length);
+
+#ifdef DEBUG_RAMDISK_MCP
+		printk(KERN_INFO "page: %u, remaining size: %u, length: %u,"
+			" i: %u, j: %u\n", req->rd_page,
+			(req->rd_size - length), length, i, j);
+#endif
+		req->rd_size -= length;
+		if (!(req->rd_size))
+			return 0;
+
+		if (!page_end)
+			continue;
+
+		if (++req->rd_page <= table->page_end_offset) {
+#ifdef DEBUG_RAMDISK_MCP
+			printk(KERN_INFO "page: %u in same page table\n",
+				req->rd_page);
+#endif
+			continue;
+		}
+#ifdef DEBUG_RAMDISK_MCP
+		printk(KERN_INFO "getting new page table for page: %u\n",
+				req->rd_page);
+#endif
+		table = rd_get_sg_table(dev, req->rd_page);
+		if (!(table))
+			return -1;
+
+		sg_s = &table->sg_table[j = 0];
+	}
+
+	return 0;
+}
+
+/*	rd_MEMCPY_write():
+ *
+ *
+ */
+static int rd_MEMCPY_write(struct rd_request *req)
+{
+	struct rd_dev *dev = req->rd_dev;
+	struct rd_dev_sg_table *table;
+	struct scatterlist *sg_d, *sg_s;
+	void *dst, *src;
+	u32 i = 0, j = 0, dst_offset = 0, src_offset = 0;
+	u32 length, page_end = 0, table_sg_end;
+	u32 rd_offset = req->rd_offset;
+
+	table = rd_get_sg_table(dev, req->rd_page);
+	if (!(table))
+		return -1;
+
+	table_sg_end = (table->page_end_offset - req->rd_page);
+	sg_d = &table->sg_table[req->rd_page - table->page_start_offset];
+	sg_s = (struct scatterlist *) req->rd_buf;
+#ifdef DEBUG_RAMDISK_MCP
+	printk(KERN_INFO "RD[%d] Write LBA: %llu, Size: %u, Page: %u,"
+		" Offset: %u\n", dev->rd_dev_id, req->rd_lba, req->rd_size,
+		req->rd_page, req->rd_offset);
+#endif
+	dst_offset = rd_offset;
+
+	while (req->rd_size) {
+		if ((sg_s[i].length - src_offset) <
+		    (sg_d[j].length - dst_offset)) {
+			length = (sg_s[i].length - src_offset);
+#ifdef DEBUG_RAMDISK_MCP
+			printk(KERN_INFO "Step 1 - sg_s[%d]: %p length: %d"
+				" offset: %d sg_d[%d].length: %u\n", i,
+				&sg_s[i], sg_s[i].length, sg_s[i].offset,
+				j, sg_d[j].length);
+			printk(KERN_INFO "Step 1 - length: %u src_offset: %u"
+				" dst_offset: %u\n", length, src_offset,
+				dst_offset);
+#endif
+			if (length > req->rd_size)
+				length = req->rd_size;
+
+			src = sg_virt(&sg_s[i++]) + src_offset;
+			if (!src)
+				BUG();
+
+			dst = sg_virt(&sg_d[j]) + dst_offset;
+			if (!dst)
+				BUG();
+
+			src_offset = 0;
+			dst_offset = length;
+			page_end = 0;
+		} else {
+			length = (sg_d[j].length - dst_offset);
+#ifdef DEBUG_RAMDISK_MCP
+			printk(KERN_INFO "Step 2 - sg_s[%d]: %p length: %d"
+				" offset: %d sg_d[%d].length: %u\n", i,
+				&sg_s[i], sg_s[i].length, sg_s[i].offset,
+				j, sg_d[j].length);
+			printk(KERN_INFO "Step 2 - length: %u src_offset: %u"
+				" dst_offset: %u\n", length, src_offset,
+				dst_offset);
+#endif
+			if (length > req->rd_size)
+				length = req->rd_size;
+
+			src = sg_virt(&sg_s[i]) + src_offset;
+			if (!src)
+				BUG();
+
+			if (sg_s[i].length == length) {
+				i++;
+				src_offset = 0;
+			} else
+				src_offset = length;
+
+			dst = sg_virt(&sg_d[j++]) + dst_offset;
+			if (!dst)
+				BUG();
+
+			dst_offset = 0;
+			page_end = 1;
+		}
+
+		memcpy(dst, src, length);
+
+#ifdef DEBUG_RAMDISK_MCP
+		printk(KERN_INFO "page: %u, remaining size: %u, length: %u,"
+			" i: %u, j: %u\n", req->rd_page,
+			(req->rd_size - length), length, i, j);
+#endif
+		req->rd_size -= length;
+		if (!(req->rd_size))
+			return 0;
+
+		if (!page_end)
+			continue;
+
+		if (++req->rd_page <= table->page_end_offset) {
+#ifdef DEBUG_RAMDISK_MCP
+			printk(KERN_INFO "page: %u in same page table\n",
+				req->rd_page);
+#endif
+			continue;
+		}
+#ifdef DEBUG_RAMDISK_MCP
+		printk(KERN_INFO "getting new page table for page: %u\n",
+				req->rd_page);
+#endif
+		table = rd_get_sg_table(dev, req->rd_page);
+		if (!(table))
+			return -1;
+
+		sg_d = &table->sg_table[j = 0];
+	}
+
+	return 0;
+}
+
+/*	rd_MEMCPY_do_task(): (Part of se_subsystem_api_t template)
+ *
+ *
+ */
+static int rd_MEMCPY_do_task(struct se_task *task)
+{
+	struct se_device *dev = task->se_dev;
+	struct rd_request *req = (struct rd_request *) task->transport_req;
+	int ret;
+
+	if (!(TASK_CMD(task)->se_cmd_flags & SCF_SCSI_DATA_SG_IO_CDB))
+		return rd_emulate_scsi_cdb(task);
+
+	req->rd_lba = task->task_lba;
+	req->rd_page = (req->rd_lba * DEV_ATTRIB(dev)->block_size) / PAGE_SIZE;
+	req->rd_offset = (do_div(req->rd_lba,
+			  (PAGE_SIZE / DEV_ATTRIB(dev)->block_size))) *
+			   DEV_ATTRIB(dev)->block_size;
+	req->rd_size = task->task_size;
+
+	if (req->rd_data_direction == RD_DATA_READ)
+		ret = rd_MEMCPY_read(req);
+	else
+		ret = rd_MEMCPY_write(req);
+
+	if (ret != 0)
+		return ret;
+
+	task->task_scsi_status = GOOD;
+	transport_complete_task(task, 1);
+
+	return PYX_TRANSPORT_SENT_TO_TRANSPORT;
+}
+
+/*	rd_DIRECT_with_offset():
+ *
+ *
+ */
+static int rd_DIRECT_with_offset(
+	struct se_task *task,
+	struct list_head *se_mem_list,
+	u32 *se_mem_cnt,
+	u32 *task_offset)
+{
+	struct rd_request *req = (struct rd_request *)task->transport_req;
+	struct rd_dev *dev = req->rd_dev;
+	struct rd_dev_sg_table *table;
+	struct se_mem *se_mem;
+	struct scatterlist *sg_s;
+	u32 j = 0, set_offset = 1;
+	u32 get_next_table = 0, offset_length, table_sg_end;
+
+	table = rd_get_sg_table(dev, req->rd_page);
+	if (!(table))
+		return -1;
+
+	table_sg_end = (table->page_end_offset - req->rd_page);
+	sg_s = &table->sg_table[req->rd_page - table->page_start_offset];
+#ifdef DEBUG_RAMDISK_DR
+	printk(KERN_INFO "%s DIRECT LBA: %llu, Size: %u Page: %u, Offset: %u\n",
+		(req->rd_data_direction != RD_DATA_READ) ? "Write" : "Read",
+		req->rd_lba, req->rd_size, req->rd_page, req->rd_offset);
+#endif
+	while (req->rd_size) {
+		se_mem = kmem_cache_zalloc(se_mem_cache, GFP_KERNEL);
+		if (!(se_mem)) {
+			printk(KERN_ERR "Unable to allocate struct se_mem\n");
+			return -1;
+		}
+		INIT_LIST_HEAD(&se_mem->se_list);
+
+		if (set_offset) {
+			offset_length = sg_s[j].length - req->rd_offset;
+			if (offset_length > req->rd_size)
+				offset_length = req->rd_size;
+
+			se_mem->se_page = sg_page(&sg_s[j++]);
+			se_mem->se_off = req->rd_offset;
+			se_mem->se_len = offset_length;
+
+			set_offset = 0;
+			get_next_table = (j > table_sg_end);
+			goto check_eot;
+		}
+
+		offset_length = (req->rd_size < req->rd_offset) ?
+			req->rd_size : req->rd_offset;
+
+		se_mem->se_page = sg_page(&sg_s[j]);
+		se_mem->se_len = offset_length;
+
+		set_offset = 1;
+
+check_eot:
+#ifdef DEBUG_RAMDISK_DR
+		printk(KERN_INFO "page: %u, size: %u, offset_length: %u, j: %u"
+			" se_mem: %p, se_page: %p se_off: %u se_len: %u\n",
+			req->rd_page, req->rd_size, offset_length, j, se_mem,
+			se_mem->se_page, se_mem->se_off, se_mem->se_len);
+#endif
+		list_add_tail(&se_mem->se_list, se_mem_list);
+		(*se_mem_cnt)++;
+
+		req->rd_size -= offset_length;
+		if (!(req->rd_size))
+			goto out;
+
+		if (!set_offset && !get_next_table)
+			continue;
+
+		if (++req->rd_page <= table->page_end_offset) {
+#ifdef DEBUG_RAMDISK_DR
+			printk(KERN_INFO "page: %u in same page table\n",
+					req->rd_page);
+#endif
+			continue;
+		}
+#ifdef DEBUG_RAMDISK_DR
+		printk(KERN_INFO "getting new page table for page: %u\n",
+				req->rd_page);
+#endif
+		table = rd_get_sg_table(dev, req->rd_page);
+		if (!(table))
+			return -1;
+
+		sg_s = &table->sg_table[j = 0];
+	}
+
+out:
+	T_TASK(task->task_se_cmd)->t_tasks_se_num += *se_mem_cnt;
+#ifdef DEBUG_RAMDISK_DR
+	printk(KERN_INFO "RD_DR - Allocated %u struct se_mem segments for task\n",
+			*se_mem_cnt);
+#endif
+	return 0;
+}
+
+/*	rd_DIRECT_without_offset():
+ *
+ *
+ */
+static int rd_DIRECT_without_offset(
+	struct se_task *task,
+	struct list_head *se_mem_list,
+	u32 *se_mem_cnt,
+	u32 *task_offset)
+{
+	struct rd_request *req = (struct rd_request *)task->transport_req;
+	struct rd_dev *dev = req->rd_dev;
+	struct rd_dev_sg_table *table;
+	struct se_mem *se_mem;
+	struct scatterlist *sg_s;
+	u32 length, j = 0;
+
+	table = rd_get_sg_table(dev, req->rd_page);
+	if (!(table))
+		return -1;
+
+	sg_s = &table->sg_table[req->rd_page - table->page_start_offset];
+#ifdef DEBUG_RAMDISK_DR
+	printk(KERN_INFO "%s DIRECT LBA: %llu, Size: %u, Page: %u\n",
+		(req->rd_data_direction != RD_DATA_READ) ? "Write" : "Read",
+		req->rd_lba, req->rd_size, req->rd_page);
+#endif
+	while (req->rd_size) {
+		se_mem = kmem_cache_zalloc(se_mem_cache, GFP_KERNEL);
+		if (!(se_mem)) {
+			printk(KERN_ERR "Unable to allocate struct se_mem\n");
+			return -1;
+		}
+		INIT_LIST_HEAD(&se_mem->se_list);
+
+		length = (req->rd_size < sg_s[j].length) ?
+			req->rd_size : sg_s[j].length;
+
+		se_mem->se_page = sg_page(&sg_s[j++]);
+		se_mem->se_len = length;
+
+#ifdef DEBUG_RAMDISK_DR
+		printk(KERN_INFO "page: %u, size: %u, j: %u se_mem: %p,"
+			" se_page: %p se_off: %u se_len: %u\n", req->rd_page,
+			req->rd_size, j, se_mem, se_mem->se_page,
+			se_mem->se_off, se_mem->se_len);
+#endif
+		list_add_tail(&se_mem->se_list, se_mem_list);
+		(*se_mem_cnt)++;
+
+		req->rd_size -= length;
+		if (!(req->rd_size))
+			goto out;
+
+		if (++req->rd_page <= table->page_end_offset) {
+#ifdef DEBUG_RAMDISK_DR
+			printk("page: %u in same page table\n",
+				req->rd_page);
+#endif
+			continue;
+		}
+#ifdef DEBUG_RAMDISK_DR
+		printk(KERN_INFO "getting new page table for page: %u\n",
+				req->rd_page);
+#endif
+		table = rd_get_sg_table(dev, req->rd_page);
+		if (!(table))
+			return -1;
+
+		sg_s = &table->sg_table[j = 0];
+	}
+
+out:
+	T_TASK(task->task_se_cmd)->t_tasks_se_num += *se_mem_cnt;
+#ifdef DEBUG_RAMDISK_DR
+	printk(KERN_INFO "RD_DR - Allocated %u struct se_mem segments for task\n",
+			*se_mem_cnt);
+#endif
+	return 0;
+}
+
+/*	rd_DIRECT_do_se_mem_map():
+ *
+ *
+ */
+static int rd_DIRECT_do_se_mem_map(
+	struct se_task *task,
+	struct list_head *se_mem_list,
+	void *in_mem,
+	struct se_mem *in_se_mem,
+	struct se_mem **out_se_mem,
+	u32 *se_mem_cnt,
+	u32 *task_offset_in)
+{
+	struct se_cmd *cmd = task->task_se_cmd;
+	struct rd_request *req = (struct rd_request *) task->transport_req;
+	u32 task_offset = *task_offset_in;
+	int ret;
+
+	req->rd_lba = task->task_lba;
+	req->rd_req_flags = RRF_GOT_LBA;
+	req->rd_page = ((req->rd_lba * DEV_ATTRIB(task->se_dev)->block_size) /
+			PAGE_SIZE);
+	req->rd_offset = (do_div(req->rd_lba,
+			  (PAGE_SIZE / DEV_ATTRIB(task->se_dev)->block_size))) *
+			   DEV_ATTRIB(task->se_dev)->block_size;
+	req->rd_size = task->task_size;
+
+	if (req->rd_offset)
+		ret = rd_DIRECT_with_offset(task, se_mem_list, se_mem_cnt,
+				task_offset_in);
+	else
+		ret = rd_DIRECT_without_offset(task, se_mem_list, se_mem_cnt,
+				task_offset_in);
+
+	if (ret < 0)
+		return ret;
+
+	if (CMD_TFO(cmd)->task_sg_chaining == 0)
+		return 0;
+	/*
+	 * Currently prevent writers from multiple HW fabrics doing
+	 * pci_map_sg() to RD_DR's internal scatterlist memory.
+	 */
+	if (cmd->data_direction == DMA_TO_DEVICE) {
+		printk(KERN_ERR "DMA_TO_DEVICE not supported for"
+				" RAMDISK_DR with task_sg_chaining=1\n");
+		return -1;
+	}
+	/*
+	 * Special case for if task_sg_chaining is enabled, then
+	 * we setup struct se_task->task_sg[], as it will be used by
+	 * transport_do_task_sg_chain() for creating chainged SGLs
+	 * across multiple struct se_task->task_sg[].
+	 *
+	 * Note that kfree(task->task_sg); does not need to be called
+	 * in rd_DIRECT_free_DMA(), because transport_free_dev_tasks()
+	 * will already be taking care of this for all TCM subsystem
+	 * plugins.
+	 */
+	if (!(transport_calc_sg_num(task,
+			list_entry(T_TASK(cmd)->t_mem_list->next,
+				   struct se_mem, se_list),
+			task_offset)))
+		return -1;	
+
+	return transport_map_mem_to_sg(task, se_mem_list, task->task_sg,
+			list_entry(T_TASK(cmd)->t_mem_list->next,
+				   struct se_mem, se_list),
+			out_se_mem, se_mem_cnt, task_offset_in);
+}
+
+/*	rd_DIRECT_free_DMA():
+ *
+ *
+ */
+static void rd_DIRECT_free_DMA(struct se_cmd *cmd)
+{
+	struct se_mem *se_mem, *se_mem_tmp;
+
+	if (!(T_TASK(cmd)->t_mem_list))
+		return;
+	/*
+	 * The scatterlists in the RAMDISK DIRECT case are using the pages
+	 * from the rd_device_t's scatterlist table. They are referencing
+	 * valid memory that is held within the RD transport plugin, so we
+	 * only free the struct se_mem elements.
+	 */
+	list_for_each_entry_safe(se_mem, se_mem_tmp, T_TASK(cmd)->t_mem_list,
+				se_list) {
+		 list_del(&se_mem->se_list);
+		 kmem_cache_free(se_mem_cache, se_mem);
+	}
+	kfree(T_TASK(cmd)->t_mem_list);
+	T_TASK(cmd)->t_mem_list = NULL;
+	T_TASK(cmd)->t_tasks_se_num = 0;
+}
+
+/*	rd_DIRECT_allocate_DMA():
+ *
+ *	Note that rd_DIRECT_do_se_mem_map() actually does the real work.
+ */
+static int rd_DIRECT_allocate_DMA(struct se_cmd *cmd, u32 length, u32 dma_size)
+{
+	T_TASK(cmd)->t_mem_list = kzalloc(sizeof(struct list_head), GFP_KERNEL);
+	if (!(T_TASK(cmd)->t_mem_list)) {
+		printk(KERN_ERR "Unable to allocate memory for T_TASK(cmd)"
+				"->t_mem_list\n");
+		return -1;
+	}
+	INIT_LIST_HEAD(T_TASK(cmd)->t_mem_list);
+
+	return 0;
+}
+
+/*	rd_DIRECT_do_task(): (Part of se_subsystem_api_t template)
+ *
+ *
+ */
+static int rd_DIRECT_do_task(struct se_task *task)
+{
+	if (!(TASK_CMD(task)->se_cmd_flags & SCF_SCSI_DATA_SG_IO_CDB))
+		return rd_emulate_scsi_cdb(task);
+
+	/*
+	 * At this point the locally allocated RD tables have been mapped
+	 * to struct se_mem elements in rd_DIRECT_do_se_mem_map().
+	 */
+	task->task_scsi_status = GOOD;
+	transport_complete_task(task, 1);
+
+	return PYX_TRANSPORT_SENT_TO_TRANSPORT;
+}
+
+/*	rd_free_task(): (Part of se_subsystem_api_t template)
+ *
+ *
+ */
+static void rd_free_task(struct se_task *task)
+{
+	struct rd_request *req;
+	req = (struct rd_request *) task->transport_req;
+
+	kfree(req);
+}
+
+static ssize_t rd_set_configfs_dev_params(
+	struct se_hba *hba,
+	struct se_subsystem_dev *se_dev,
+	const char *page,
+	ssize_t count)
+{
+	struct rd_dev *rd_dev = (struct rd_dev *) se_dev->se_dev_su_ptr;
+	char *buf, *cur, *ptr, *ptr2;
+	unsigned long rd_pages;
+	int params = 0, ret;
+
+	buf = kzalloc(count, GFP_KERNEL);
+	if (!(buf)) {
+		printk(KERN_ERR "Unable to allocate memory for temporary buffer\n");
+		return 0;
+	}
+	memcpy(buf, page, count);
+	cur = buf;
+
+	while (cur) {
+		ptr = strstr(cur, "=");
+		if (!(ptr))
+			goto out;
+
+		*ptr = '\0';
+		ptr++;
+
+		ptr2 = strstr(cur, "rd_pages");
+		if ((ptr2)) {
+			transport_check_dev_params_delim(ptr, &cur);
+			ret = strict_strtoul(ptr, 0, &rd_pages);
+			if (ret < 0) {
+				printk(KERN_ERR "strict_strtoul() failed for"
+					" rd_pages=\n");
+				break;
+			}
+			rd_dev->rd_page_count = (u32)rd_pages;
+			printk(KERN_INFO "RAMDISK: Referencing Page"
+				" Count: %u\n", rd_dev->rd_page_count);
+			rd_dev->rd_flags |= RDF_HAS_PAGE_COUNT;
+			params++;
+		} else
+			cur = NULL;
+	}
+
+out:
+	kfree(buf);
+	return (params) ? count : -EINVAL;
+}
+
+static ssize_t rd_check_configfs_dev_params(struct se_hba *hba, struct se_subsystem_dev *se_dev)
+{
+	struct rd_dev *rd_dev = (struct rd_dev *) se_dev->se_dev_su_ptr;
+
+	if (!(rd_dev->rd_flags & RDF_HAS_PAGE_COUNT)) {
+		printk(KERN_INFO "Missing rd_pages= parameter\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+static ssize_t rd_show_configfs_dev_params(
+	struct se_hba *hba,
+	struct se_subsystem_dev *se_dev,
+	char *page)
+{
+	struct rd_dev *rd_dev = (struct rd_dev *) se_dev->se_dev_su_ptr;
+	int bl = 0;
+
+	 __rd_get_dev_info(rd_dev, page, &bl);
+	return (ssize_t)bl;
+}
+
+static void rd_dr_get_plugin_info(void *p, char *b, int *bl)
+{
+	*bl += sprintf(b + *bl, "TCM RAMDISK_DR Plugin %s\n", RD_DR_VERSION);
+}
+
+static void rd_mcp_get_plugin_info(void *p, char *b, int *bl)
+{
+	*bl += sprintf(b + *bl, "TCM RAMDISK_MCP Plugin %s\n", RD_MCP_VERSION);
+}
+
+static void rd_get_hba_info(struct se_hba *hba, char *b, int *bl)
+{
+	struct rd_host *rd_host = (struct rd_host *)hba->hba_ptr;
+
+	*bl += sprintf(b + *bl, "SE Host ID: %u  RD Host ID: %u\n",
+		hba->hba_id, rd_host->rd_host_id);
+	*bl += sprintf(b + *bl, "        TCM RamDisk HBA\n");
+}
+
+static void rd_get_dev_info(struct se_device *dev, char *b, int *bl)
+{
+	struct rd_dev *rd_dev = (struct rd_dev *) dev->dev_ptr;
+
+	__rd_get_dev_info(rd_dev, b, bl);
+}
+
+static void __rd_get_dev_info(struct rd_dev *rd_dev, char *b, int *bl)
+{
+	*bl += sprintf(b + *bl, "TCM RamDisk ID: %u  RamDisk Makeup: %s\n",
+			rd_dev->rd_dev_id, (rd_dev->rd_direct) ?
+			"rd_direct" : "rd_mcp");
+	*bl += sprintf(b + *bl, "        PAGES/PAGE_SIZE: %u*%lu"
+			"  SG_table_count: %u\n", rd_dev->rd_page_count,
+			PAGE_SIZE, rd_dev->sg_table_count);
+
+	return;
+}
+
+/*	rd_map_task_non_SG():
+ *
+ *
+ */
+static void rd_map_task_non_SG(struct se_task *task)
+{
+	struct se_cmd *cmd = TASK_CMD(task);
+	struct rd_request *req = (struct rd_request *) task->transport_req;
+
+	req->rd_bufflen		= task->task_size;
+	req->rd_buf		= (void *) T_TASK(cmd)->t_task_buf;
+	req->rd_sg_count	= 0;
+}
+
+/*	rd_map_task_SG():
+ *
+ *
+ */
+static void rd_map_task_SG(struct se_task *task)
+{
+	struct rd_request *req = (struct rd_request *) task->transport_req;
+
+	req->rd_bufflen		= task->task_size;
+	req->rd_buf		= task->task_sg;
+	req->rd_sg_count	= task->task_sg_num;
+}
+
+/*      rd_CDB_none():
+ *
+ *
+ */
+static int rd_CDB_none(struct se_task *task, u32 size)
+{
+	struct rd_request *req = (struct rd_request *) task->transport_req;
+
+	req->rd_data_direction	= RD_DATA_NONE;
+	req->rd_bufflen		= 0;
+	req->rd_sg_count	= 0;
+	req->rd_buf		= NULL;
+
+	return 0;
+}
+
+/*	rd_CDB_read_non_SG():
+ *
+ *
+ */
+static int rd_CDB_read_non_SG(struct se_task *task, u32 size)
+{
+	struct rd_request *req = (struct rd_request *) task->transport_req;
+
+	req->rd_data_direction = RD_DATA_READ;
+	rd_map_task_non_SG(task);
+
+	return 0;
+}
+
+/*	rd_CDB_read_SG):
+ *
+ *
+ */
+static int rd_CDB_read_SG(struct se_task *task, u32 size)
+{
+	struct rd_request *req = (struct rd_request *) task->transport_req;
+
+	req->rd_data_direction = RD_DATA_READ;
+	rd_map_task_SG(task);
+
+	return req->rd_sg_count;
+}
+
+/*	rd_CDB_write_non_SG():
+ *
+ *
+ */
+static int rd_CDB_write_non_SG(struct se_task *task, u32 size)
+{
+	struct rd_request *req = (struct rd_request *) task->transport_req;
+
+	req->rd_data_direction = RD_DATA_WRITE;
+	rd_map_task_non_SG(task);
+
+	return 0;
+}
+
+/*	d_CDB_write_SG():
+ *
+ *
+ */
+static int rd_CDB_write_SG(struct se_task *task, u32 size)
+{
+	struct rd_request *req = (struct rd_request *) task->transport_req;
+
+	req->rd_data_direction = RD_DATA_WRITE;
+	rd_map_task_SG(task);
+
+	return req->rd_sg_count;
+}
+
+/*	rd_DIRECT_check_lba():
+ *
+ *
+ */
+static int rd_DIRECT_check_lba(unsigned long long lba, struct se_device *dev)
+{
+	return ((do_div(lba, PAGE_SIZE / DEV_ATTRIB(dev)->block_size)) *
+		 DEV_ATTRIB(dev)->block_size) ? 1 : 0;
+}
+
+/*	rd_MEMCPY_check_lba():
+ *
+ *
+ */
+static int rd_MEMCPY_check_lba(unsigned long long lba, struct se_device *dev)
+{
+	return 0;
+}
+
+/*	rd_check_for_SG(): (Part of se_subsystem_api_t template)
+ *
+ *
+ */
+static int rd_check_for_SG(struct se_task *task)
+{
+	struct rd_request *req = (struct rd_request *) task->transport_req;
+
+	return req->rd_sg_count;
+}
+
+/*	rd_get_cdb(): (Part of se_subsystem_api_t template)
+ *
+ *
+ */
+static unsigned char *rd_get_cdb(struct se_task *task)
+{
+	struct rd_request *req = (struct rd_request *) task->transport_req;
+
+	return req->rd_scsi_cdb;
+}
+
+/*	rd_get_blocksize(): (Part of se_subsystem_api_t template)
+ *
+ *
+ */
+static u32 rd_get_blocksize(struct se_device *dev)
+{
+	return RD_BLOCKSIZE;
+}
+
+static u32 rd_get_device_rev(struct se_device *dev)
+{
+	return SCSI_SPC_2; /* Returns SPC-3 in Initiator Data */
+}
+
+static u32 rd_get_device_type(struct se_device *dev)
+{
+	return TYPE_DISK;
+}
+
+/*	rd_get_dma_length(): (Part of se_subsystem_api_t template)
+ *
+ *
+ */
+static u32 rd_get_dma_length(u32 task_size, struct se_device *dev)
+{
+	return PAGE_SIZE;
+}
+
+/*	rd_get_max_sectors(): (Part of se_subsystem_api_t template)
+ *
+ *
+ */
+static u32 rd_get_max_sectors(struct se_device *dev)
+{
+	return RD_MAX_SECTORS;
+}
+
+/*	rd_get_queue_depth(): (Part of se_subsystem_api_t template)
+ *
+ *
+ */
+static u32 rd_get_queue_depth(struct se_device *dev)
+{
+	return RD_DEVICE_QUEUE_DEPTH;
+}
+
+static u32 rd_get_max_queue_depth(struct se_device *dev)
+{
+	return RD_MAX_DEVICE_QUEUE_DEPTH;
+}
+
+static struct se_subsystem_api rd_dr_template = {
+	.name			= "rd_dr",
+	.type			= RAMDISK_DR,
+	.transport_type		= TRANSPORT_PLUGIN_VHBA_VDEV,
+	.external_submod	= 0,
+	.cdb_none		= rd_CDB_none,
+	.cdb_read_non_SG	= rd_CDB_read_non_SG,
+	.cdb_read_SG		= rd_CDB_read_SG,
+	.cdb_write_non_SG	= rd_CDB_write_non_SG,
+	.cdb_write_SG		= rd_CDB_write_SG,
+	.attach_hba		= rd_attach_hba,
+	.detach_hba		= rd_detach_hba,
+	.allocate_virtdevice	= rd_DIRECT_allocate_virtdevice,
+	.create_virtdevice	= rd_DIRECT_create_virtdevice,
+	.activate_device	= rd_activate_device,
+	.deactivate_device	= rd_deactivate_device,
+	.free_device		= rd_free_device,
+	.transport_complete	= rd_transport_complete,
+	.allocate_DMA		= rd_DIRECT_allocate_DMA,
+	.free_DMA		= rd_DIRECT_free_DMA,
+	.allocate_request	= rd_allocate_request,
+	.do_task		= rd_DIRECT_do_task,
+	.free_task		= rd_free_task,
+	.check_configfs_dev_params = rd_check_configfs_dev_params,
+	.set_configfs_dev_params = rd_set_configfs_dev_params,
+	.show_configfs_dev_params = rd_show_configfs_dev_params,
+	.get_plugin_info	= rd_dr_get_plugin_info,
+	.get_hba_info		= rd_get_hba_info,
+	.get_dev_info		= rd_get_dev_info,
+	.check_lba		= rd_DIRECT_check_lba,
+	.check_for_SG		= rd_check_for_SG,
+	.get_cdb		= rd_get_cdb,
+	.get_blocksize		= rd_get_blocksize,
+	.get_device_rev		= rd_get_device_rev,
+	.get_device_type	= rd_get_device_type,
+	.get_dma_length		= rd_get_dma_length,
+	.get_max_sectors	= rd_get_max_sectors,
+	.get_queue_depth	= rd_get_queue_depth,
+	.get_max_queue_depth	= rd_get_max_queue_depth,
+	.do_se_mem_map		= rd_DIRECT_do_se_mem_map,
+	.write_pending		= NULL,
+};
+
+static struct se_subsystem_api rd_mcp_template = {
+	.name			= "rd_mcp",
+	.type			= RAMDISK_MCP,
+	.transport_type		= TRANSPORT_PLUGIN_VHBA_VDEV,
+	.external_submod	= 0,
+	.cdb_none		= rd_CDB_none,
+	.cdb_read_non_SG	= rd_CDB_read_non_SG,
+	.cdb_read_SG		= rd_CDB_read_SG,
+	.cdb_write_non_SG	= rd_CDB_write_non_SG,
+	.cdb_write_SG		= rd_CDB_write_SG,
+	.attach_hba		= rd_attach_hba,
+	.detach_hba		= rd_detach_hba,
+	.allocate_virtdevice	= rd_MEMCPY_allocate_virtdevice,
+	.create_virtdevice	= rd_MEMCPY_create_virtdevice,
+	.activate_device	= rd_activate_device,
+	.deactivate_device	= rd_deactivate_device,
+	.free_device		= rd_free_device,
+	.transport_complete	= rd_transport_complete,
+	.allocate_request	= rd_allocate_request,
+	.do_task		= rd_MEMCPY_do_task,
+	.free_task		= rd_free_task,
+	.check_configfs_dev_params = rd_check_configfs_dev_params,
+	.set_configfs_dev_params = rd_set_configfs_dev_params,
+	.show_configfs_dev_params = rd_show_configfs_dev_params,
+	.get_plugin_info	= rd_mcp_get_plugin_info,
+	.get_hba_info		= rd_get_hba_info,
+	.get_dev_info		= rd_get_dev_info,
+	.check_lba		= rd_MEMCPY_check_lba,
+	.check_for_SG		= rd_check_for_SG,
+	.get_cdb		= rd_get_cdb,
+	.get_blocksize		= rd_get_blocksize,
+	.get_device_rev		= rd_get_device_rev,
+	.get_device_type	= rd_get_device_type,
+	.get_dma_length		= rd_get_dma_length,
+	.get_max_sectors	= rd_get_max_sectors,
+	.get_queue_depth	= rd_get_queue_depth,
+	.get_max_queue_depth	= rd_get_max_queue_depth,
+	.write_pending		= NULL,
+};
+
+int __init rd_module_init(void)
+{
+	int ret;
+
+	INIT_LIST_HEAD(&rd_dr_template.sub_api_list);
+	INIT_LIST_HEAD(&rd_mcp_template.sub_api_list);
+
+	ret = transport_subsystem_register(&rd_dr_template, NULL);
+	if (ret < 0)
+		return ret;
+
+	ret = transport_subsystem_register(&rd_mcp_template, NULL);
+	if (ret < 0) {
+		transport_subsystem_release(&rd_dr_template);
+		return ret;
+	}
+
+	return 0;
+}
+
+void rd_module_exit(void)
+{
+	transport_subsystem_release(&rd_dr_template);
+	transport_subsystem_release(&rd_mcp_template);
+}
diff --git a/drivers/target/target_core_rd.h b/drivers/target/target_core_rd.h
new file mode 100644
index 0000000..2008807
--- /dev/null
+++ b/drivers/target/target_core_rd.h
@@ -0,0 +1,89 @@
+#ifndef TARGET_CORE_RD_H
+#define TARGET_CORE_RD_H
+
+#define RD_HBA_VERSION		"v4.0"
+#define RD_DR_VERSION		"4.0"
+#define RD_MCP_VERSION		"4.0"
+
+/* Largest piece of memory kmalloc can allocate */
+#define RD_MAX_ALLOCATION_SIZE	65536
+/* Maximum queuedepth for the Ramdisk HBA */
+#define RD_HBA_QUEUE_DEPTH	256
+#define RD_DEVICE_QUEUE_DEPTH	32
+#define RD_MAX_DEVICE_QUEUE_DEPTH 128
+#define RD_BLOCKSIZE		512
+#define RD_MAX_SECTORS		1024
+
+#define RD_DATA_READ		1
+#define RD_DATA_WRITE		2
+#define RD_DATA_NONE		3
+
+extern struct se_global *se_global;
+
+extern struct kmem_cache *se_mem_cache;
+
+/* Used in target_core_init_configfs() for virtual LUN 0 access */
+int __init rd_module_init(void);
+void rd_module_exit(void);
+
+#define RRF_EMULATE_CDB		0x01
+#define RRF_GOT_LBA		0x02
+
+struct rd_request {
+	/* SCSI CDB from iSCSI Command PDU */
+	unsigned char	rd_scsi_cdb[TCM_MAX_COMMAND_SIZE];
+	/* Data Direction */
+	u8		rd_data_direction;
+	/* Total length of request */
+	u32		rd_bufflen;
+	/* RD request flags */
+	u32		rd_req_flags;
+	/* Offset from start of page */
+	u32		rd_offset;
+	/* Starting page in Ramdisk for request */
+	u32		rd_page;
+	/* Total number of pages needed for request */
+	u32		rd_page_count;
+	/* Scatterlist count */
+	u32		rd_sg_count;
+	u32		rd_size;
+	/* Logical Block Address */
+	unsigned long long	rd_lba;
+	 /* Data buffer containing scatterlists(s) or
+	  * contiguous memory segments */
+	void		*rd_buf;
+	/* Ramdisk device */
+	struct rd_dev	*rd_dev;
+} ____cacheline_aligned;
+
+struct rd_dev_sg_table {
+	u32		page_start_offset;
+	u32		page_end_offset;
+	u32		rd_sg_count;
+	struct scatterlist *sg_table;
+} ____cacheline_aligned;
+
+#define RDF_HAS_PAGE_COUNT	0x01
+
+struct rd_dev {
+	int		rd_direct;
+	u32		rd_flags;
+	/* Unique Ramdisk Device ID in Ramdisk HBA */
+	u32		rd_dev_id;
+	/* Total page count for ramdisk device */
+	u32		rd_page_count;
+	/* Number of SG tables in sg_table_array */
+	u32		sg_table_count;
+	u32		rd_queue_depth;
+	/* Array of rd_dev_sg_table_t containing scatterlists */
+	struct rd_dev_sg_table *sg_table_array;
+	/* Ramdisk HBA device is connected to */
+	struct rd_host *rd_host;
+} ____cacheline_aligned;
+
+struct rd_host {
+	u32		rd_host_dev_id_count;
+	u32		rd_host_id;		/* Unique Ramdisk Host ID */
+} ____cacheline_aligned;
+
+#endif /* TARGET_CORE_RD_H */
-- 
1.7.3

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ