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>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <4C8F8B35.3080902@vlnb.net>
Date:	Tue, 14 Sep 2010 18:48:21 +0400
From:	Vladislav Bolkhovitin <vst@...b.net>
To:	linux-scsi@...r.kernel.org
CC:	linux-kernel@...r.kernel.org,
	scst-devel <scst-devel@...ts.sourceforge.net>,
	James Bottomley <James.Bottomley@...senPartnership.com>,
	Andrew Morton <akpm@...ux-foundation.org>,
	FUJITA Tomonori <fujita.tomonori@....ntt.co.jp>,
	Mike Christie <michaelc@...wisc.edu>,
	Jeff Garzik <jeff@...zik.org>, Vu Pham <vuhuong@...lanox.com>,
	Bart Van Assche <bart.vanassche@...il.com>,
	James Smart <James.Smart@...lex.Com>,
	Joe Eykholt <jeykholt@...co.com>, Andy Yan <ayan@...vell.com>,
	Chetan Loke <generationgnu@...oo.com>,
	Dmitry Torokhov <dmitry.torokhov@...il.com>,
	Hannes Reinecke <hare@...e.de>,
	Richard Sharpe <realrichardsharpe@...il.com>
Subject: [PATCH 14/17]: SCST pass-through dev handlers

This patch contains SCST pass-through dev handlers.

Signed-off-by: Vladislav Bolkhovitin <vst@...b.net>
---
 scst_cdrom.c       |  245 +++++++++++++++++++++
 scst_changer.c     |  167 ++++++++++++++
 scst_dev_handler.h |   27 ++
 scst_disk.c        |  608 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 scst_modisk.c      |  328 ++++++++++++++++++++++++++++
 scst_processor.c   |  167 ++++++++++++++
 scst_raid.c        |  168 ++++++++++++++
 scst_tape.c        |  361 +++++++++++++++++++++++++++++++
 8 files changed, 2071 insertions(+)

diff -uprN orig/linux-2.6.35/drivers/scst/dev_handlers/scst_cdrom.c linux-2.6.35/drivers/scst/dev_handlers/scst_cdrom.c
--- orig/linux-2.6.35/drivers/scst/dev_handlers/scst_cdrom.c
+++ linux-2.6.35/drivers/scst/dev_handlers/scst_cdrom.c
@@ -0,0 +1,245 @@
+/*
+ *  scst_cdrom.c
+ *
+ *  Copyright (C) 2004 - 2010 Vladislav Bolkhovitin <vst@...b.net>
+ *  Copyright (C) 2004 - 2005 Leonid Stoljar
+ *  Copyright (C) 2007 - 2010 ID7 Ltd.
+ *
+ *  SCSI CDROM (type 5) dev handler
+ *
+ *  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, version 2
+ *  of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ */
+
+#include <linux/cdrom.h>
+#include <scsi/scsi_host.h>
+#include <linux/slab.h>
+
+#define LOG_PREFIX	"dev_cdrom"
+
+#include <scst/scst.h>
+#include "scst_dev_handler.h"
+
+#define CDROM_NAME	"dev_cdrom"
+
+#define CDROM_DEF_BLOCK_SHIFT	11
+
+struct cdrom_params {
+	int block_shift;
+};
+
+static int cdrom_attach(struct scst_device *);
+static void cdrom_detach(struct scst_device *);
+static int cdrom_parse(struct scst_cmd *);
+static int cdrom_done(struct scst_cmd *);
+
+static struct scst_dev_type cdrom_devtype = {
+	.name =			CDROM_NAME,
+	.type =			TYPE_ROM,
+	.threads_num =		1,
+	.parse_atomic =		1,
+	.dev_done_atomic =	1,
+	.attach =		cdrom_attach,
+	.detach =		cdrom_detach,
+	.parse =		cdrom_parse,
+	.dev_done =		cdrom_done,
+#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
+	.default_trace_flags =	SCST_DEFAULT_DEV_LOG_FLAGS,
+	.trace_flags =		&trace_flag,
+#endif
+};
+
+static int cdrom_attach(struct scst_device *dev)
+{
+	int res, rc;
+	uint8_t cmd[10];
+	const int buffer_size = 512;
+	uint8_t *buffer = NULL;
+	int retries;
+	unsigned char sense_buffer[SCSI_SENSE_BUFFERSIZE];
+	enum dma_data_direction data_dir;
+	struct cdrom_params *params;
+
+	if (dev->scsi_dev == NULL ||
+	    dev->scsi_dev->type != dev->type) {
+		PRINT_ERROR("%s", "SCSI device not define or illegal type");
+		res = -ENODEV;
+		goto out;
+	}
+
+	params = kzalloc(sizeof(*params), GFP_KERNEL);
+	if (params == NULL) {
+		TRACE(TRACE_OUT_OF_MEM, "%s",
+		      "Unable to allocate struct cdrom_params");
+		res = -ENOMEM;
+		goto out;
+	}
+
+	buffer = kmalloc(buffer_size, GFP_KERNEL);
+	if (!buffer) {
+		TRACE(TRACE_OUT_OF_MEM, "%s", "Memory allocation failure");
+		res = -ENOMEM;
+		goto out_free_params;
+	}
+
+	/* Clear any existing UA's and get cdrom capacity (cdrom block size) */
+	memset(cmd, 0, sizeof(cmd));
+	cmd[0] = READ_CAPACITY;
+	cmd[1] = (dev->scsi_dev->scsi_level <= SCSI_2) ?
+	    ((dev->scsi_dev->lun << 5) & 0xe0) : 0;
+	retries = SCST_DEV_UA_RETRIES;
+	while (1) {
+		memset(buffer, 0, buffer_size);
+		memset(sense_buffer, 0, sizeof(sense_buffer));
+		data_dir = SCST_DATA_READ;
+
+		TRACE_DBG("%s", "Doing READ_CAPACITY");
+		rc = scsi_execute(dev->scsi_dev, cmd, data_dir, buffer,
+				   buffer_size, sense_buffer,
+				   SCST_GENERIC_CDROM_REG_TIMEOUT, 3, 0
+				   , NULL
+				  );
+
+		TRACE_DBG("READ_CAPACITY done: %x", rc);
+
+		if ((rc == 0) ||
+		    !scst_analyze_sense(sense_buffer,
+				sizeof(sense_buffer), SCST_SENSE_KEY_VALID,
+				UNIT_ATTENTION, 0, 0))
+			break;
+
+		if (!--retries) {
+			PRINT_ERROR("UA not cleared after %d retries",
+				SCST_DEV_UA_RETRIES);
+			params->block_shift = CDROM_DEF_BLOCK_SHIFT;
+			res = -ENODEV;
+			goto out_free_buf;
+		}
+	}
+
+	if (rc == 0) {
+		int sector_size = ((buffer[4] << 24) | (buffer[5] << 16) |
+				      (buffer[6] << 8) | (buffer[7] << 0));
+		if (sector_size == 0)
+			params->block_shift = CDROM_DEF_BLOCK_SHIFT;
+		else
+			params->block_shift =
+				scst_calc_block_shift(sector_size);
+		TRACE_DBG("Sector size is %i scsi_level %d(SCSI_2 %d)",
+			sector_size, dev->scsi_dev->scsi_level, SCSI_2);
+	} else {
+		params->block_shift = CDROM_DEF_BLOCK_SHIFT;
+		TRACE(TRACE_MINOR, "Read capacity failed: %x, using default "
+			"sector size %d", rc, params->block_shift);
+		PRINT_BUFF_FLAG(TRACE_MINOR, "Returned sense", sense_buffer,
+			sizeof(sense_buffer));
+	}
+
+	res = scst_obtain_device_parameters(dev);
+	if (res != 0) {
+		PRINT_ERROR("Failed to obtain control parameters for device "
+			"%s", dev->virt_name);
+		goto out_free_buf;
+	}
+
+out_free_buf:
+	kfree(buffer);
+
+out_free_params:
+	if (res == 0)
+		dev->dh_priv = params;
+	else
+		kfree(params);
+
+out:
+	return res;
+}
+
+static void cdrom_detach(struct scst_device *dev)
+{
+	struct cdrom_params *params =
+		(struct cdrom_params *)dev->dh_priv;
+
+	kfree(params);
+	dev->dh_priv = NULL;
+	return;
+}
+
+static int cdrom_get_block_shift(struct scst_cmd *cmd)
+{
+	struct cdrom_params *params = (struct cdrom_params *)cmd->dev->dh_priv;
+	/*
+	 * No need for locks here, since *_detach() can not be
+	 * called, when there are existing commands.
+	 */
+	return params->block_shift;
+}
+
+static int cdrom_parse(struct scst_cmd *cmd)
+{
+	int res = SCST_CMD_STATE_DEFAULT;
+
+	scst_cdrom_generic_parse(cmd, cdrom_get_block_shift);
+
+	cmd->retries = SCST_PASSTHROUGH_RETRIES;
+
+	return res;
+}
+
+static void cdrom_set_block_shift(struct scst_cmd *cmd, int block_shift)
+{
+	struct cdrom_params *params = (struct cdrom_params *)cmd->dev->dh_priv;
+	/*
+	 * No need for locks here, since *_detach() can not be
+	 * called, when there are existing commands.
+	 */
+	if (block_shift != 0)
+		params->block_shift = block_shift;
+	else
+		params->block_shift = CDROM_DEF_BLOCK_SHIFT;
+	return;
+}
+
+static int cdrom_done(struct scst_cmd *cmd)
+{
+	int res = SCST_CMD_STATE_DEFAULT;
+
+	res = scst_block_generic_dev_done(cmd, cdrom_set_block_shift);
+	return res;
+}
+
+static int __init cdrom_init(void)
+{
+	int res = 0;
+
+	cdrom_devtype.module = THIS_MODULE;
+
+	res = scst_register_dev_driver(&cdrom_devtype);
+	if (res < 0)
+		goto out;
+
+out:
+	return res;
+
+}
+
+static void __exit cdrom_exit(void)
+{
+	scst_unregister_dev_driver(&cdrom_devtype);
+	return;
+}
+
+module_init(cdrom_init);
+module_exit(cdrom_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Vladislav Bolkhovitin & Leonid Stoljar");
+MODULE_DESCRIPTION("SCSI CDROM (type 5) dev handler for SCST");
+MODULE_VERSION(SCST_VERSION_STRING);
diff -uprN orig/linux-2.6.35/drivers/scst/dev_handlers/scst_changer.c linux-2.6.35/drivers/scst/dev_handlers/scst_changer.c
--- orig/linux-2.6.35/drivers/scst/dev_handlers/scst_changer.c
+++ linux-2.6.35/drivers/scst/dev_handlers/scst_changer.c
@@ -0,0 +1,167 @@
+/*
+ *  scst_changer.c
+ *
+ *  Copyright (C) 2004 - 2010 Vladislav Bolkhovitin <vst@...b.net>
+ *  Copyright (C) 2004 - 2005 Leonid Stoljar
+ *  Copyright (C) 2007 - 2010 ID7 Ltd.
+ *
+ *  SCSI medium changer (type 8) dev handler
+ *
+ *  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, version 2
+ *  of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ */
+
+#include <scsi/scsi_host.h>
+#include <linux/slab.h>
+
+#define LOG_PREFIX      "dev_changer"
+
+#include <scst/scst.h>
+#include "scst_dev_handler.h"
+
+#define CHANGER_NAME	"dev_changer"
+
+#define CHANGER_RETRIES       2
+
+static int changer_attach(struct scst_device *);
+/* static void changer_detach(struct scst_device *); */
+static int changer_parse(struct scst_cmd *);
+/* static int changer_done(struct scst_cmd *); */
+
+static struct scst_dev_type changer_devtype = {
+	.name =	CHANGER_NAME,
+	.type =	TYPE_MEDIUM_CHANGER,
+	.threads_num =	1,
+	.parse_atomic =	1,
+/*	.dev_done_atomic =	1, */
+	.attach =	changer_attach,
+/*	.detach =	changer_detach, */
+	.parse =	changer_parse,
+/*	.dev_done =	changer_done */
+#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
+	.default_trace_flags =	SCST_DEFAULT_DEV_LOG_FLAGS,
+	.trace_flags =		&trace_flag,
+#endif
+};
+
+static int changer_attach(struct scst_device *dev)
+{
+	int res, rc;
+	int retries;
+
+	if (dev->scsi_dev == NULL ||
+	    dev->scsi_dev->type != dev->type) {
+		PRINT_ERROR("%s", "SCSI device not define or illegal type");
+		res = -ENODEV;
+		goto out;
+	}
+
+	/*
+	 * If the device is offline, don't try to read capacity or any
+	 * of the other stuff
+	 */
+	if (dev->scsi_dev->sdev_state == SDEV_OFFLINE) {
+		TRACE_DBG("%s", "Device is offline");
+		res = -ENODEV;
+		goto out;
+	}
+
+	retries = SCST_DEV_UA_RETRIES;
+	do {
+		TRACE_DBG("%s", "Doing TEST_UNIT_READY");
+		rc = scsi_test_unit_ready(dev->scsi_dev,
+			SCST_GENERIC_CHANGER_TIMEOUT, CHANGER_RETRIES
+					  , NULL);
+		TRACE_DBG("TEST_UNIT_READY done: %x", rc);
+	} while ((--retries > 0) && rc);
+
+	if (rc) {
+		PRINT_WARNING("Unit not ready: %x", rc);
+		/* Let's try not to be too smart and continue processing */
+	}
+
+	res = scst_obtain_device_parameters(dev);
+	if (res != 0) {
+		PRINT_ERROR("Failed to obtain control parameters for device "
+			"%s", dev->virt_name);
+		goto out;
+	}
+
+out:
+	return res;
+}
+
+#if 0
+void changer_detach(struct scst_device *dev)
+{
+	return;
+}
+#endif
+
+static int changer_parse(struct scst_cmd *cmd)
+{
+	int res = SCST_CMD_STATE_DEFAULT;
+
+	scst_changer_generic_parse(cmd, NULL);
+
+	cmd->retries = SCST_PASSTHROUGH_RETRIES;
+
+	return res;
+}
+
+#if 0
+int changer_done(struct scst_cmd *cmd)
+{
+	int res = SCST_CMD_STATE_DEFAULT;
+
+	/*
+	 * SCST sets good defaults for cmd->is_send_status and
+	 * cmd->resp_data_len based on cmd->status and cmd->data_direction,
+	 * therefore change them only if necessary
+	 */
+
+#if 0
+	switch (cmd->cdb[0]) {
+	default:
+		/* It's all good */
+		break;
+	}
+#endif
+	return res;
+}
+#endif
+
+static int __init changer_init(void)
+{
+	int res = 0;
+
+	changer_devtype.module = THIS_MODULE;
+
+	res = scst_register_dev_driver(&changer_devtype);
+	if (res < 0)
+		goto out;
+
+out:
+	return res;
+}
+
+static void __exit changer_exit(void)
+{
+	scst_unregister_dev_driver(&changer_devtype);
+	return;
+}
+
+module_init(changer_init);
+module_exit(changer_exit);
+
+MODULE_AUTHOR("Vladislav Bolkhovitin & Leonid Stoljar");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("SCSI medium changer (type 8) dev handler for SCST");
+MODULE_VERSION(SCST_VERSION_STRING);
diff -uprN orig/linux-2.6.35/drivers/scst/dev_handlers/scst_dev_handler.h linux-2.6.35/drivers/scst/dev_handlers/scst_dev_handler.h
--- orig/linux-2.6.35/drivers/scst/dev_handlers/scst_dev_handler.h
+++ linux-2.6.35/drivers/scst/dev_handlers/scst_dev_handler.h
@@ -0,0 +1,27 @@
+#ifndef __SCST_DEV_HANDLER_H
+#define __SCST_DEV_HANDLER_H
+
+#include <linux/module.h>
+#include <scsi/scsi_eh.h>
+#include <scst/scst_debug.h>
+
+#define SCST_DEV_UA_RETRIES 5
+#define SCST_PASSTHROUGH_RETRIES	0
+
+#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
+
+#ifdef CONFIG_SCST_DEBUG
+#define SCST_DEFAULT_DEV_LOG_FLAGS (TRACE_OUT_OF_MEM | TRACE_PID | \
+	TRACE_LINE | TRACE_FUNCTION | TRACE_MGMT | TRACE_MINOR | \
+	TRACE_MGMT_DEBUG | TRACE_SPECIAL)
+#else
+#define SCST_DEFAULT_DEV_LOG_FLAGS (TRACE_OUT_OF_MEM | TRACE_MGMT | \
+	TRACE_SPECIAL)
+#endif
+
+static unsigned long dh_trace_flag = SCST_DEFAULT_DEV_LOG_FLAGS;
+#define trace_flag dh_trace_flag
+
+#endif /* defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) */
+
+#endif /* __SCST_DEV_HANDLER_H */
diff -uprN orig/linux-2.6.35/drivers/scst/dev_handlers/scst_disk.c linux-2.6.35/drivers/scst/dev_handlers/scst_disk.c
--- orig/linux-2.6.35/drivers/scst/dev_handlers/scst_disk.c
+++ linux-2.6.35/drivers/scst/dev_handlers/scst_disk.c
@@ -0,0 +1,608 @@
+/*
+ *  scst_disk.c
+ *
+ *  Copyright (C) 2004 - 2010 Vladislav Bolkhovitin <vst@...b.net>
+ *  Copyright (C) 2004 - 2005 Leonid Stoljar
+ *  Copyright (C) 2007 - 2010 ID7 Ltd.
+ *
+ *  SCSI disk (type 0) dev handler
+ *  &
+ *  SCSI disk (type 0) "performance" device handler (skip all READ and WRITE
+ *   operations).
+ *
+ *  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, version 2
+ *  of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <scsi/scsi_host.h>
+#include <linux/slab.h>
+#include <asm/unaligned.h>
+
+#define LOG_PREFIX           "dev_disk"
+
+#include <scst/scst.h>
+#include "scst_dev_handler.h"
+
+# define DISK_NAME           "dev_disk"
+# define DISK_PERF_NAME      "dev_disk_perf"
+
+#define DISK_DEF_BLOCK_SHIFT	9
+
+struct disk_params {
+	int block_shift;
+};
+
+static int disk_attach(struct scst_device *dev);
+static void disk_detach(struct scst_device *dev);
+static int disk_parse(struct scst_cmd *cmd);
+static int disk_perf_exec(struct scst_cmd *cmd);
+static int disk_done(struct scst_cmd *cmd);
+static int disk_exec(struct scst_cmd *cmd);
+static bool disk_on_sg_tablesize_low(struct scst_cmd *cmd);
+
+static struct scst_dev_type disk_devtype = {
+	.name =			DISK_NAME,
+	.type =			TYPE_DISK,
+	.threads_num =		1,
+	.parse_atomic =		1,
+	.dev_done_atomic =	1,
+	.attach =		disk_attach,
+	.detach =		disk_detach,
+	.parse =		disk_parse,
+	.exec =			disk_exec,
+	.on_sg_tablesize_low = disk_on_sg_tablesize_low,
+	.dev_done =		disk_done,
+#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
+	.default_trace_flags = SCST_DEFAULT_DEV_LOG_FLAGS,
+	.trace_flags = &trace_flag,
+#endif
+};
+
+static struct scst_dev_type disk_devtype_perf = {
+	.name =			DISK_PERF_NAME,
+	.type =			TYPE_DISK,
+	.parse_atomic =		1,
+	.dev_done_atomic =	1,
+	.attach =		disk_attach,
+	.detach =		disk_detach,
+	.parse =		disk_parse,
+	.exec =			disk_perf_exec,
+	.dev_done =		disk_done,
+	.on_sg_tablesize_low = disk_on_sg_tablesize_low,
+#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
+	.default_trace_flags =	SCST_DEFAULT_DEV_LOG_FLAGS,
+	.trace_flags =		&trace_flag,
+#endif
+};
+
+static int __init init_scst_disk_driver(void)
+{
+	int res = 0;
+
+	disk_devtype.module = THIS_MODULE;
+
+	res = scst_register_dev_driver(&disk_devtype);
+	if (res < 0)
+		goto out;
+
+	disk_devtype_perf.module = THIS_MODULE;
+
+	res = scst_register_dev_driver(&disk_devtype_perf);
+	if (res < 0)
+		goto out_unreg;
+
+out:
+	return res;
+
+out_unreg:
+	scst_unregister_dev_driver(&disk_devtype);
+	goto out;
+}
+
+static void __exit exit_scst_disk_driver(void)
+{
+
+	scst_unregister_dev_driver(&disk_devtype_perf);
+	scst_unregister_dev_driver(&disk_devtype);
+	return;
+}
+
+module_init(init_scst_disk_driver);
+module_exit(exit_scst_disk_driver);
+
+static int disk_attach(struct scst_device *dev)
+{
+	int res, rc;
+	uint8_t cmd[10];
+	const int buffer_size = 512;
+	uint8_t *buffer = NULL;
+	int retries;
+	unsigned char sense_buffer[SCSI_SENSE_BUFFERSIZE];
+	enum dma_data_direction data_dir;
+	struct disk_params *params;
+
+	if (dev->scsi_dev == NULL ||
+	    dev->scsi_dev->type != dev->type) {
+		PRINT_ERROR("%s", "SCSI device not define or illegal type");
+		res = -ENODEV;
+		goto out;
+	}
+
+	params = kzalloc(sizeof(*params), GFP_KERNEL);
+	if (params == NULL) {
+		TRACE(TRACE_OUT_OF_MEM, "%s",
+		      "Unable to allocate struct disk_params");
+		res = -ENOMEM;
+		goto out;
+	}
+
+	buffer = kmalloc(buffer_size, GFP_KERNEL);
+	if (!buffer) {
+		TRACE(TRACE_OUT_OF_MEM, "%s", "Memory allocation failure");
+		res = -ENOMEM;
+		goto out_free_params;
+	}
+
+	/* Clear any existing UA's and get disk capacity (disk block size) */
+	memset(cmd, 0, sizeof(cmd));
+	cmd[0] = READ_CAPACITY;
+	cmd[1] = (dev->scsi_dev->scsi_level <= SCSI_2) ?
+	    ((dev->scsi_dev->lun << 5) & 0xe0) : 0;
+	retries = SCST_DEV_UA_RETRIES;
+	while (1) {
+		memset(buffer, 0, buffer_size);
+		memset(sense_buffer, 0, sizeof(sense_buffer));
+		data_dir = SCST_DATA_READ;
+
+		TRACE_DBG("%s", "Doing READ_CAPACITY");
+		rc = scsi_execute(dev->scsi_dev, cmd, data_dir, buffer,
+				   buffer_size, sense_buffer,
+				   SCST_GENERIC_DISK_REG_TIMEOUT, 3, 0
+				   , NULL
+				  );
+
+		TRACE_DBG("READ_CAPACITY done: %x", rc);
+
+		if ((rc == 0) ||
+		    !scst_analyze_sense(sense_buffer,
+				sizeof(sense_buffer), SCST_SENSE_KEY_VALID,
+				UNIT_ATTENTION, 0, 0))
+			break;
+		if (!--retries) {
+			PRINT_ERROR("UA not clear after %d retries",
+				SCST_DEV_UA_RETRIES);
+			res = -ENODEV;
+			goto out_free_buf;
+		}
+	}
+	if (rc == 0) {
+		int sector_size = ((buffer[4] << 24) | (buffer[5] << 16) |
+				     (buffer[6] << 8) | (buffer[7] << 0));
+		if (sector_size == 0)
+			params->block_shift = DISK_DEF_BLOCK_SHIFT;
+		else
+			params->block_shift =
+				scst_calc_block_shift(sector_size);
+	} else {
+		params->block_shift = DISK_DEF_BLOCK_SHIFT;
+		TRACE(TRACE_MINOR, "Read capacity failed: %x, using default "
+			"sector size %d", rc, params->block_shift);
+		PRINT_BUFF_FLAG(TRACE_MINOR, "Returned sense", sense_buffer,
+			sizeof(sense_buffer));
+	}
+
+	res = scst_obtain_device_parameters(dev);
+	if (res != 0) {
+		PRINT_ERROR("Failed to obtain control parameters for device "
+			"%s", dev->virt_name);
+		goto out_free_buf;
+	}
+
+out_free_buf:
+	kfree(buffer);
+
+out_free_params:
+	if (res == 0)
+		dev->dh_priv = params;
+	else
+		kfree(params);
+
+out:
+	return res;
+}
+
+static void disk_detach(struct scst_device *dev)
+{
+	struct disk_params *params =
+		(struct disk_params *)dev->dh_priv;
+
+	kfree(params);
+	dev->dh_priv = NULL;
+	return;
+}
+
+static int disk_get_block_shift(struct scst_cmd *cmd)
+{
+	struct disk_params *params = (struct disk_params *)cmd->dev->dh_priv;
+	/*
+	 * No need for locks here, since *_detach() can not be
+	 * called, when there are existing commands.
+	 */
+	return params->block_shift;
+}
+
+static int disk_parse(struct scst_cmd *cmd)
+{
+	int res = SCST_CMD_STATE_DEFAULT;
+
+	scst_sbc_generic_parse(cmd, disk_get_block_shift);
+
+	cmd->retries = SCST_PASSTHROUGH_RETRIES;
+
+	return res;
+}
+
+static void disk_set_block_shift(struct scst_cmd *cmd, int block_shift)
+{
+	struct disk_params *params = (struct disk_params *)cmd->dev->dh_priv;
+	/*
+	 * No need for locks here, since *_detach() can not be
+	 * called, when there are existing commands.
+	 */
+	if (block_shift != 0)
+		params->block_shift = block_shift;
+	else
+		params->block_shift = DISK_DEF_BLOCK_SHIFT;
+	return;
+}
+
+static int disk_done(struct scst_cmd *cmd)
+{
+	int res = SCST_CMD_STATE_DEFAULT;
+
+	res = scst_block_generic_dev_done(cmd, disk_set_block_shift);
+	return res;
+}
+
+static bool disk_on_sg_tablesize_low(struct scst_cmd *cmd)
+{
+	bool res;
+
+	switch (cmd->cdb[0]) {
+	case WRITE_6:
+	case READ_6:
+	case WRITE_10:
+	case READ_10:
+	case WRITE_VERIFY:
+	case WRITE_12:
+	case READ_12:
+	case WRITE_VERIFY_12:
+	case WRITE_16:
+	case READ_16:
+	case WRITE_VERIFY_16:
+		res = true;
+		/* See comment in disk_exec */
+		cmd->inc_expected_sn_on_done = 1;
+		break;
+	default:
+		res = false;
+		break;
+	}
+	return res;
+}
+
+struct disk_work {
+	struct scst_cmd *cmd;
+	struct completion disk_work_cmpl;
+	volatile int result;
+	unsigned int left;
+	uint64_t save_lba;
+	unsigned int save_len;
+	struct scatterlist *save_sg;
+	int save_sg_cnt;
+};
+
+static int disk_cdb_get_transfer_data(const uint8_t *cdb,
+	uint64_t *out_lba, unsigned int *out_length)
+{
+	int res;
+	uint64_t lba;
+	unsigned int len;
+
+	switch (cdb[0]) {
+	case WRITE_6:
+	case READ_6:
+		lba = be16_to_cpu(get_unaligned((__be16 *)&cdb[2]));
+		len = cdb[4];
+		break;
+	case WRITE_10:
+	case READ_10:
+	case WRITE_VERIFY:
+		lba = be32_to_cpu(get_unaligned((__be32 *)&cdb[2]));
+		len = be16_to_cpu(get_unaligned((__be16 *)&cdb[7]));
+		break;
+	case WRITE_12:
+	case READ_12:
+	case WRITE_VERIFY_12:
+		lba = be32_to_cpu(get_unaligned((__be32 *)&cdb[2]));
+		len = be32_to_cpu(get_unaligned((__be32 *)&cdb[6]));
+		break;
+	case WRITE_16:
+	case READ_16:
+	case WRITE_VERIFY_16:
+		lba = be64_to_cpu(get_unaligned((__be64 *)&cdb[2]));
+		len = be32_to_cpu(get_unaligned((__be32 *)&cdb[10]));
+		break;
+	default:
+		res = -EINVAL;
+		goto out;
+	}
+
+	res = 0;
+	*out_lba = lba;
+	*out_length = len;
+
+	TRACE_DBG("LBA %lld, length %d", (unsigned long long)lba, len);
+
+out:
+	return res;
+}
+
+static int disk_cdb_set_transfer_data(uint8_t *cdb,
+	uint64_t lba, unsigned int len)
+{
+	int res;
+
+	switch (cdb[0]) {
+	case WRITE_6:
+	case READ_6:
+		put_unaligned(cpu_to_be16(lba), (__be16 *)&cdb[2]);
+		cdb[4] = len;
+		break;
+	case WRITE_10:
+	case READ_10:
+	case WRITE_VERIFY:
+		put_unaligned(cpu_to_be32(lba), (__be32 *)&cdb[2]);
+		put_unaligned(cpu_to_be16(len), (__be16 *)&cdb[7]);
+		break;
+	case WRITE_12:
+	case READ_12:
+	case WRITE_VERIFY_12:
+		put_unaligned(cpu_to_be32(lba), (__be32 *)&cdb[2]);
+		put_unaligned(cpu_to_be32(len), (__be32 *)&cdb[6]);
+		break;
+	case WRITE_16:
+	case READ_16:
+	case WRITE_VERIFY_16:
+		put_unaligned(cpu_to_be64(lba), (__be64 *)&cdb[2]);
+		put_unaligned(cpu_to_be32(len), (__be32 *)&cdb[10]);
+		break;
+	default:
+		res = -EINVAL;
+		goto out;
+	}
+
+	res = 0;
+
+	TRACE_DBG("LBA %lld, length %d", (unsigned long long)lba, len);
+	TRACE_BUFFER("New CDB", cdb, SCST_MAX_CDB_SIZE);
+
+out:
+	return res;
+}
+
+static void disk_cmd_done(void *data, char *sense, int result, int resid)
+{
+	struct disk_work *work = data;
+
+	TRACE_DBG("work %p, cmd %p, left %d, result %d, sense %p, resid %d",
+		work, work->cmd, work->left, result, sense, resid);
+
+	if (result == SAM_STAT_GOOD)
+		goto out_complete;
+
+	work->result = result;
+
+	disk_cdb_set_transfer_data(work->cmd->cdb, work->save_lba, work->save_len);
+	work->cmd->sg = work->save_sg;
+	work->cmd->sg_cnt = work->save_sg_cnt;
+
+	scst_pass_through_cmd_done(work->cmd, sense, result, resid + work->left);
+
+out_complete:
+	complete_all(&work->disk_work_cmpl);
+	return;
+}
+
+/* Executes command and split CDB, if necessary */
+static int disk_exec(struct scst_cmd *cmd)
+{
+	int res, rc;
+	struct disk_params *params = (struct disk_params *)cmd->dev->dh_priv;
+	struct disk_work work;
+	unsigned int offset, cur_len;
+	struct scatterlist *sg, *start_sg;
+	int cur_sg_cnt;
+	int sg_tablesize = cmd->dev->scsi_dev->host->sg_tablesize;
+	int num, j;
+
+	if (likely((cmd->sg_cnt <= sg_tablesize) &&
+		   (cmd->out_sg_cnt <= sg_tablesize))) {
+		res = SCST_EXEC_NOT_COMPLETED;
+		goto out;
+	}
+
+	memset(&work, 0, sizeof(work));
+	work.cmd = cmd;
+	work.save_sg = cmd->sg;
+	work.save_sg_cnt = cmd->sg_cnt;
+	rc = disk_cdb_get_transfer_data(cmd->cdb, &work.save_lba,
+		&work.save_len);
+	if (rc != 0)
+		goto out_error;
+
+	rc = scst_check_local_events(cmd);
+	if (unlikely(rc != 0))
+		goto out_done;
+
+	cmd->status = 0;
+	cmd->msg_status = 0;
+	cmd->host_status = DID_OK;
+	cmd->driver_status = 0;
+
+	TRACE_DBG("cmd %p, save_sg %p, save_sg_cnt %d, save_lba %lld, "
+		"save_len %d (sg_tablesize %d, sizeof(*sg) 0x%zx)", cmd,
+		work.save_sg, work.save_sg_cnt,
+		(unsigned long long)work.save_lba, work.save_len,
+		sg_tablesize, sizeof(*sg));
+
+	/*
+	 * If we submit all chunks async'ly, it will be very not trivial what
+	 * to do if several of them finish with sense or residual. So, let's
+	 * do it synchronously.
+	 */
+
+	num = 1;
+	j = 0;
+	offset = 0;
+	cur_len = 0;
+	sg = work.save_sg;
+	start_sg = sg;
+	cur_sg_cnt = 0;
+	while (1) {
+		unsigned int l;
+
+		if (unlikely(sg_is_chain(&sg[j]))) {
+			bool reset_start_sg = (start_sg == &sg[j]);
+			sg = sg_chain_ptr(&sg[j]);
+			j = 0;
+			if (reset_start_sg)
+				start_sg = sg;
+		}
+
+		l = sg[j].length >> params->block_shift;
+		cur_len += l;
+		cur_sg_cnt++;
+
+		TRACE_DBG("l %d, j %d, num %d, offset %d, cur_len %d, "
+			"cur_sg_cnt %d, start_sg %p", l, j, num, offset,
+			cur_len, cur_sg_cnt, start_sg);
+
+		if (((num % sg_tablesize) == 0) || (num == work.save_sg_cnt)) {
+			TRACE_DBG("%s", "Execing...");
+
+			disk_cdb_set_transfer_data(cmd->cdb,
+				work.save_lba + offset, cur_len);
+			cmd->sg = start_sg;
+			cmd->sg_cnt = cur_sg_cnt;
+
+			work.left = work.save_len - (offset + cur_len);
+			init_completion(&work.disk_work_cmpl);
+
+			rc = scst_scsi_exec_async(cmd, &work, disk_cmd_done);
+			if (unlikely(rc != 0)) {
+				PRINT_ERROR("scst_scsi_exec_async() failed: %d",
+					rc);
+				goto out_err_restore;
+			}
+
+			wait_for_completion(&work.disk_work_cmpl);
+
+			if (work.result != SAM_STAT_GOOD) {
+				/* cmd can be already dead */
+				res = SCST_EXEC_COMPLETED;
+				goto out;
+			}
+
+			offset += cur_len;
+			cur_len = 0;
+			cur_sg_cnt = 0;
+			start_sg = &sg[j+1];
+
+			if (num == work.save_sg_cnt)
+				break;
+		}
+		num++;
+		j++;
+	}
+
+	cmd->completed = 1;
+
+out_restore:
+	disk_cdb_set_transfer_data(cmd->cdb, work.save_lba, work.save_len);
+	cmd->sg = work.save_sg;
+	cmd->sg_cnt = work.save_sg_cnt;
+
+out_done:
+	res = SCST_EXEC_COMPLETED;
+	cmd->scst_cmd_done(cmd, SCST_CMD_STATE_DEFAULT, SCST_CONTEXT_SAME);
+
+out:
+	return res;
+
+out_err_restore:
+	scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_hardw_error));
+	goto out_restore;
+
+out_error:
+	scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_hardw_error));
+	goto out_done;
+}
+
+static int disk_perf_exec(struct scst_cmd *cmd)
+{
+	int res, rc;
+	int opcode = cmd->cdb[0];
+
+	rc = scst_check_local_events(cmd);
+	if (unlikely(rc != 0))
+		goto out_done;
+
+	cmd->status = 0;
+	cmd->msg_status = 0;
+	cmd->host_status = DID_OK;
+	cmd->driver_status = 0;
+
+	switch (opcode) {
+	case WRITE_6:
+	case WRITE_10:
+	case WRITE_12:
+	case WRITE_16:
+	case READ_6:
+	case READ_10:
+	case READ_12:
+	case READ_16:
+	case WRITE_VERIFY:
+	case WRITE_VERIFY_12:
+	case WRITE_VERIFY_16:
+		goto out_complete;
+	}
+
+	res = SCST_EXEC_NOT_COMPLETED;
+
+out:
+	return res;
+
+out_complete:
+	cmd->completed = 1;
+
+out_done:
+	res = SCST_EXEC_COMPLETED;
+	cmd->scst_cmd_done(cmd, SCST_CMD_STATE_DEFAULT, SCST_CONTEXT_SAME);
+	goto out;
+}
+
+MODULE_AUTHOR("Vladislav Bolkhovitin & Leonid Stoljar");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("SCSI disk (type 0) dev handler for SCST");
+MODULE_VERSION(SCST_VERSION_STRING);
+
diff -uprN orig/linux-2.6.35/drivers/scst/dev_handlers/scst_modisk.c linux-2.6.35/drivers/scst/dev_handlers/scst_modisk.c
--- orig/linux-2.6.35/drivers/scst/dev_handlers/scst_modisk.c
+++ linux-2.6.35/drivers/scst/dev_handlers/scst_modisk.c
@@ -0,0 +1,328 @@
+/*
+ *  scst_modisk.c
+ *
+ *  Copyright (C) 2004 - 2010 Vladislav Bolkhovitin <vst@...b.net>
+ *  Copyright (C) 2004 - 2005 Leonid Stoljar
+ *  Copyright (C) 2007 - 2010 ID7 Ltd.
+ *
+ *  SCSI MO disk (type 7) dev handler
+ *  &
+ *  SCSI MO disk (type 7) "performance" device handler (skip all READ and WRITE
+ *   operations).
+ *
+ *  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, version 2
+ *  of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <scsi/scsi_host.h>
+#include <linux/slab.h>
+
+#define LOG_PREFIX             "dev_modisk"
+
+#include <scst/scst.h>
+#include "scst_dev_handler.h"
+
+# define MODISK_NAME           "dev_modisk"
+# define MODISK_PERF_NAME      "dev_modisk_perf"
+
+#define MODISK_DEF_BLOCK_SHIFT    10
+
+struct modisk_params {
+	int block_shift;
+};
+
+static int modisk_attach(struct scst_device *);
+static void modisk_detach(struct scst_device *);
+static int modisk_parse(struct scst_cmd *);
+static int modisk_done(struct scst_cmd *);
+static int modisk_perf_exec(struct scst_cmd *);
+
+static struct scst_dev_type modisk_devtype = {
+	.name =			MODISK_NAME,
+	.type =			TYPE_MOD,
+	.threads_num =		1,
+	.parse_atomic =		1,
+	.dev_done_atomic =	1,
+	.attach =		modisk_attach,
+	.detach =		modisk_detach,
+	.parse =		modisk_parse,
+	.dev_done =		modisk_done,
+#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
+	.default_trace_flags =	SCST_DEFAULT_DEV_LOG_FLAGS,
+	.trace_flags =		&trace_flag,
+#endif
+};
+
+static struct scst_dev_type modisk_devtype_perf = {
+	.name =			MODISK_PERF_NAME,
+	.type =			TYPE_MOD,
+	.parse_atomic =		1,
+	.dev_done_atomic =	1,
+	.attach =		modisk_attach,
+	.detach =		modisk_detach,
+	.parse =		modisk_parse,
+	.dev_done =		modisk_done,
+	.exec =			modisk_perf_exec,
+#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
+	.default_trace_flags =	SCST_DEFAULT_DEV_LOG_FLAGS,
+	.trace_flags =		&trace_flag,
+#endif
+};
+
+static int __init init_scst_modisk_driver(void)
+{
+	int res = 0;
+
+	modisk_devtype.module = THIS_MODULE;
+
+	res = scst_register_dev_driver(&modisk_devtype);
+	if (res < 0)
+		goto out;
+
+	modisk_devtype_perf.module = THIS_MODULE;
+
+	res = scst_register_dev_driver(&modisk_devtype_perf);
+	if (res < 0)
+		goto out_unreg;
+
+out:
+	return res;
+
+out_unreg:
+	scst_unregister_dev_driver(&modisk_devtype);
+	goto out;
+}
+
+static void __exit exit_scst_modisk_driver(void)
+{
+
+	scst_unregister_dev_driver(&modisk_devtype_perf);
+	scst_unregister_dev_driver(&modisk_devtype);
+	return;
+}
+
+module_init(init_scst_modisk_driver);
+module_exit(exit_scst_modisk_driver);
+
+static int modisk_attach(struct scst_device *dev)
+{
+	int res, rc;
+	uint8_t cmd[10];
+	const int buffer_size = 512;
+	uint8_t *buffer = NULL;
+	int retries;
+	unsigned char sense_buffer[SCSI_SENSE_BUFFERSIZE];
+	enum dma_data_direction data_dir;
+	struct modisk_params *params;
+
+	if (dev->scsi_dev == NULL ||
+	    dev->scsi_dev->type != dev->type) {
+		PRINT_ERROR("%s", "SCSI device not define or illegal type");
+		res = -ENODEV;
+		goto out;
+	}
+
+	params = kzalloc(sizeof(*params), GFP_KERNEL);
+	if (params == NULL) {
+		TRACE(TRACE_OUT_OF_MEM, "%s",
+		      "Unable to allocate struct modisk_params");
+		res = -ENOMEM;
+		goto out;
+	}
+	params->block_shift = MODISK_DEF_BLOCK_SHIFT;
+
+	/*
+	 * If the device is offline, don't try to read capacity or any
+	 * of the other stuff
+	 */
+	if (dev->scsi_dev->sdev_state == SDEV_OFFLINE) {
+		TRACE_DBG("%s", "Device is offline");
+		res = -ENODEV;
+		goto out_free_params;
+	}
+
+	buffer = kmalloc(buffer_size, GFP_KERNEL);
+	if (!buffer) {
+		TRACE(TRACE_OUT_OF_MEM, "%s", "Memory allocation failure");
+		res = -ENOMEM;
+		goto out_free_params;
+	}
+
+	/*
+	 * Clear any existing UA's and get modisk capacity (modisk block
+	 * size).
+	 */
+	memset(cmd, 0, sizeof(cmd));
+	cmd[0] = READ_CAPACITY;
+	cmd[1] = (dev->scsi_dev->scsi_level <= SCSI_2) ?
+	    ((dev->scsi_dev->lun << 5) & 0xe0) : 0;
+	retries = SCST_DEV_UA_RETRIES;
+	while (1) {
+		memset(buffer, 0, buffer_size);
+		memset(sense_buffer, 0, sizeof(sense_buffer));
+		data_dir = SCST_DATA_READ;
+
+		TRACE_DBG("%s", "Doing READ_CAPACITY");
+		rc = scsi_execute(dev->scsi_dev, cmd, data_dir, buffer,
+				   buffer_size, sense_buffer,
+				   SCST_GENERIC_MODISK_REG_TIMEOUT, 3, 0
+				   , NULL
+				  );
+
+		TRACE_DBG("READ_CAPACITY done: %x", rc);
+
+		if (!rc || !scst_analyze_sense(sense_buffer,
+				sizeof(sense_buffer), SCST_SENSE_KEY_VALID,
+				UNIT_ATTENTION, 0, 0))
+			break;
+
+		if (!--retries) {
+			PRINT_ERROR("UA not cleared after %d retries",
+				    SCST_DEV_UA_RETRIES);
+			res = -ENODEV;
+			goto out_free_buf;
+		}
+	}
+
+	if (rc == 0) {
+		int sector_size = ((buffer[4] << 24) | (buffer[5] << 16) |
+				       (buffer[6] << 8) | (buffer[7] << 0));
+		if (sector_size == 0)
+			params->block_shift = MODISK_DEF_BLOCK_SHIFT;
+		else
+			params->block_shift =
+				scst_calc_block_shift(sector_size);
+		TRACE_DBG("Sector size is %i scsi_level %d(SCSI_2 %d)",
+		      sector_size, dev->scsi_dev->scsi_level, SCSI_2);
+	} else {
+		params->block_shift = MODISK_DEF_BLOCK_SHIFT;
+		TRACE(TRACE_MINOR, "Read capacity failed: %x, using default "
+			"sector size %d", rc, params->block_shift);
+		PRINT_BUFF_FLAG(TRACE_MINOR, "Returned sense", sense_buffer,
+			sizeof(sense_buffer));
+	}
+
+	res = scst_obtain_device_parameters(dev);
+	if (res != 0) {
+		PRINT_ERROR("Failed to obtain control parameters for device "
+			"%s: %x", dev->virt_name, res);
+		goto out_free_buf;
+	}
+
+out_free_buf:
+	kfree(buffer);
+
+out_free_params:
+	if (res == 0)
+		dev->dh_priv = params;
+	else
+		kfree(params);
+
+out:
+	return res;
+}
+
+static void modisk_detach(struct scst_device *dev)
+{
+	struct modisk_params *params =
+		(struct modisk_params *)dev->dh_priv;
+
+	kfree(params);
+	dev->dh_priv = NULL;
+	return;
+}
+
+static int modisk_get_block_shift(struct scst_cmd *cmd)
+{
+	struct modisk_params *params =
+		(struct modisk_params *)cmd->dev->dh_priv;
+	/*
+	 * No need for locks here, since *_detach() can not be
+	 * called, when there are existing commands.
+	 */
+	return params->block_shift;
+}
+
+static int modisk_parse(struct scst_cmd *cmd)
+{
+	int res = SCST_CMD_STATE_DEFAULT;
+
+	scst_modisk_generic_parse(cmd, modisk_get_block_shift);
+
+	cmd->retries = SCST_PASSTHROUGH_RETRIES;
+
+	return res;
+}
+
+static void modisk_set_block_shift(struct scst_cmd *cmd, int block_shift)
+{
+	struct modisk_params *params =
+		(struct modisk_params *)cmd->dev->dh_priv;
+	/*
+	 * No need for locks here, since *_detach() can not be
+	 * called, when there are existing commands.
+	 */
+	if (block_shift != 0)
+		params->block_shift = block_shift;
+	else
+		params->block_shift = MODISK_DEF_BLOCK_SHIFT;
+	return;
+}
+
+static int modisk_done(struct scst_cmd *cmd)
+{
+	int res;
+
+	res = scst_block_generic_dev_done(cmd, modisk_set_block_shift);
+	return res;
+}
+
+static int modisk_perf_exec(struct scst_cmd *cmd)
+{
+	int res = SCST_EXEC_NOT_COMPLETED, rc;
+	int opcode = cmd->cdb[0];
+
+	rc = scst_check_local_events(cmd);
+	if (unlikely(rc != 0))
+		goto out_done;
+
+	cmd->status = 0;
+	cmd->msg_status = 0;
+	cmd->host_status = DID_OK;
+	cmd->driver_status = 0;
+
+	switch (opcode) {
+	case WRITE_6:
+	case WRITE_10:
+	case WRITE_12:
+	case WRITE_16:
+	case READ_6:
+	case READ_10:
+	case READ_12:
+	case READ_16:
+		cmd->completed = 1;
+		goto out_done;
+	}
+
+out:
+	return res;
+
+out_done:
+	res = SCST_EXEC_COMPLETED;
+	cmd->scst_cmd_done(cmd, SCST_CMD_STATE_DEFAULT, SCST_CONTEXT_SAME);
+	goto out;
+}
+
+MODULE_AUTHOR("Vladislav Bolkhovitin & Leonid Stoljar");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("SCSI MO disk (type 7) dev handler for SCST");
+MODULE_VERSION(SCST_VERSION_STRING);
diff -uprN orig/linux-2.6.35/drivers/scst/dev_handlers/scst_processor.c linux-2.6.35/drivers/scst/dev_handlers/scst_processor.c
--- orig/linux-2.6.35/drivers/scst/dev_handlers/scst_processor.c
+++ linux-2.6.35/drivers/scst/dev_handlers/scst_processor.c
@@ -0,0 +1,167 @@
+/*
+ *  scst_processor.c
+ *
+ *  Copyright (C) 2004 - 2010 Vladislav Bolkhovitin <vst@...b.net>
+ *  Copyright (C) 2004 - 2005 Leonid Stoljar
+ *  Copyright (C) 2007 - 2010 ID7 Ltd.
+ *
+ *  SCSI medium processor (type 3) dev handler
+ *
+ *  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, version 2
+ *  of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ */
+
+#include <scsi/scsi_host.h>
+#include <linux/slab.h>
+
+#define LOG_PREFIX "dev_processor"
+
+#include <scst/scst.h>
+#include "scst_dev_handler.h"
+
+#define PROCESSOR_NAME	"dev_processor"
+
+#define PROCESSOR_RETRIES	2
+
+static int processor_attach(struct scst_device *);
+/*static void processor_detach(struct scst_device *);*/
+static int processor_parse(struct scst_cmd *);
+/*static int processor_done(struct scst_cmd *);*/
+
+static struct scst_dev_type processor_devtype = {
+	.name =			PROCESSOR_NAME,
+	.type =			TYPE_PROCESSOR,
+	.threads_num =		1,
+	.parse_atomic =		1,
+/*	.dev_done_atomic =	1,*/
+	.attach =		processor_attach,
+/*	.detach =		processor_detach,*/
+	.parse =		processor_parse,
+/*	.dev_done =		processor_done*/
+#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
+	.default_trace_flags =	SCST_DEFAULT_DEV_LOG_FLAGS,
+	.trace_flags =		&trace_flag,
+#endif
+};
+
+static int processor_attach(struct scst_device *dev)
+{
+	int res, rc;
+	int retries;
+
+	if (dev->scsi_dev == NULL ||
+	    dev->scsi_dev->type != dev->type) {
+		PRINT_ERROR("%s", "SCSI device not define or illegal type");
+		res = -ENODEV;
+		goto out;
+	}
+
+	/*
+	 * If the device is offline, don't try to read capacity or any
+	 * of the other stuff
+	 */
+	if (dev->scsi_dev->sdev_state == SDEV_OFFLINE) {
+		TRACE_DBG("%s", "Device is offline");
+		res = -ENODEV;
+		goto out;
+	}
+
+	retries = SCST_DEV_UA_RETRIES;
+	do {
+		TRACE_DBG("%s", "Doing TEST_UNIT_READY");
+		rc = scsi_test_unit_ready(dev->scsi_dev,
+			SCST_GENERIC_PROCESSOR_TIMEOUT, PROCESSOR_RETRIES
+					  , NULL);
+		TRACE_DBG("TEST_UNIT_READY done: %x", rc);
+	} while ((--retries > 0) && rc);
+
+	if (rc) {
+		PRINT_WARNING("Unit not ready: %x", rc);
+		/* Let's try not to be too smart and continue processing */
+	}
+
+	res = scst_obtain_device_parameters(dev);
+	if (res != 0) {
+		PRINT_ERROR("Failed to obtain control parameters for device "
+			"%s", dev->virt_name);
+		goto out;
+	}
+
+out:
+	return res;
+}
+
+#if 0
+void processor_detach(struct scst_device *dev)
+{
+	return;
+}
+#endif
+
+static int processor_parse(struct scst_cmd *cmd)
+{
+	int res = SCST_CMD_STATE_DEFAULT;
+
+	scst_processor_generic_parse(cmd, NULL);
+
+	cmd->retries = SCST_PASSTHROUGH_RETRIES;
+
+	return res;
+}
+
+#if 0
+int processor_done(struct scst_cmd *cmd)
+{
+	int res = SCST_CMD_STATE_DEFAULT;
+
+	/*
+	 * SCST sets good defaults for cmd->is_send_status and
+	 * cmd->resp_data_len based on cmd->status and cmd->data_direction,
+	 * therefore change them only if necessary.
+	 */
+
+#if 0
+	switch (cmd->cdb[0]) {
+	default:
+		/* It's all good */
+		break;
+	}
+#endif
+	return res;
+}
+#endif
+
+static int __init processor_init(void)
+{
+	int res = 0;
+
+	processor_devtype.module = THIS_MODULE;
+
+	res = scst_register_dev_driver(&processor_devtype);
+	if (res < 0)
+		goto out;
+
+out:
+	return res;
+}
+
+static void __exit processor_exit(void)
+{
+	scst_unregister_dev_driver(&processor_devtype);
+	return;
+}
+
+module_init(processor_init);
+module_exit(processor_exit);
+
+MODULE_AUTHOR("Vladislav Bolkhovitin & Leonid Stoljar");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("SCSI medium processor (type 3) dev handler for SCST");
+MODULE_VERSION(SCST_VERSION_STRING);
diff -uprN orig/linux-2.6.35/drivers/scst/dev_handlers/scst_raid.c linux-2.6.35/drivers/scst/dev_handlers/scst_raid.c
--- orig/linux-2.6.35/drivers/scst/dev_handlers/scst_raid.c
+++ linux-2.6.35/drivers/scst/dev_handlers/scst_raid.c
@@ -0,0 +1,168 @@
+/*
+ *  scst_raid.c
+ *
+ *  Copyright (C) 2004 - 2010 Vladislav Bolkhovitin <vst@...b.net>
+ *  Copyright (C) 2004 - 2005 Leonid Stoljar
+ *  Copyright (C) 2007 - 2010 ID7 Ltd.
+ *
+ *  SCSI raid(controller) (type 0xC) dev handler
+ *
+ *  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, version 2
+ *  of the License.
+ *
+ *  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.
+ */
+
+#define LOG_PREFIX      "dev_raid"
+
+#include <scsi/scsi_host.h>
+#include <linux/slab.h>
+
+#include <scst/scst.h>
+#include "scst_dev_handler.h"
+
+#define RAID_NAME	"dev_raid"
+
+#define RAID_RETRIES		2
+
+static int raid_attach(struct scst_device *);
+/* static void raid_detach(struct scst_device *); */
+static int raid_parse(struct scst_cmd *);
+/* static int raid_done(struct scst_cmd *); */
+
+static struct scst_dev_type raid_devtype = {
+	.name =			RAID_NAME,
+	.type =			TYPE_RAID,
+	.threads_num =		1,
+	.parse_atomic =		1,
+/*	.dev_done_atomic =	1,*/
+	.attach =		raid_attach,
+/*	.detach =		raid_detach,*/
+	.parse =		raid_parse,
+/*	.dev_done =		raid_done,*/
+#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
+	.default_trace_flags =	SCST_DEFAULT_DEV_LOG_FLAGS,
+	.trace_flags =		&trace_flag,
+#endif
+};
+
+static int raid_attach(struct scst_device *dev)
+{
+	int res, rc;
+	int retries;
+
+	if (dev->scsi_dev == NULL ||
+	    dev->scsi_dev->type != dev->type) {
+		PRINT_ERROR("%s", "SCSI device not define or illegal type");
+		res = -ENODEV;
+		goto out;
+	}
+
+	/*
+	 * If the device is offline, don't try to read capacity or any
+	 * of the other stuff
+	 */
+	if (dev->scsi_dev->sdev_state == SDEV_OFFLINE) {
+		TRACE_DBG("%s", "Device is offline");
+		res = -ENODEV;
+		goto out;
+	}
+
+	retries = SCST_DEV_UA_RETRIES;
+	do {
+		TRACE_DBG("%s", "Doing TEST_UNIT_READY");
+		rc = scsi_test_unit_ready(dev->scsi_dev,
+			SCST_GENERIC_RAID_TIMEOUT, RAID_RETRIES
+					  , NULL);
+		TRACE_DBG("TEST_UNIT_READY done: %x", rc);
+	} while ((--retries > 0) && rc);
+
+	if (rc) {
+		PRINT_WARNING("Unit not ready: %x", rc);
+		/* Let's try not to be too smart and continue processing */
+	}
+
+	res = scst_obtain_device_parameters(dev);
+	if (res != 0) {
+		PRINT_ERROR("Failed to obtain control parameters for device "
+			"%s", dev->virt_name);
+		goto out;
+	}
+
+out:
+	return res;
+}
+
+#if 0
+void raid_detach(struct scst_device *dev)
+{
+	return;
+}
+#endif
+
+static int raid_parse(struct scst_cmd *cmd)
+{
+	int res = SCST_CMD_STATE_DEFAULT;
+
+	scst_raid_generic_parse(cmd, NULL);
+
+	cmd->retries = SCST_PASSTHROUGH_RETRIES;
+
+	return res;
+}
+
+#if 0
+int raid_done(struct scst_cmd *cmd)
+{
+	int res = SCST_CMD_STATE_DEFAULT;
+
+	/*
+	 * SCST sets good defaults for cmd->is_send_status and
+	 * cmd->resp_data_len based on cmd->status and cmd->data_direction,
+	 * therefore change them only if necessary.
+	 */
+
+#if 0
+	switch (cmd->cdb[0]) {
+	default:
+		/* It's all good */
+		break;
+	}
+#endif
+	return res;
+}
+#endif
+
+static int __init raid_init(void)
+{
+	int res = 0;
+
+	raid_devtype.module = THIS_MODULE;
+
+	res = scst_register_dev_driver(&raid_devtype);
+	if (res < 0)
+		goto out;
+
+out:
+	return res;
+
+}
+
+static void __exit raid_exit(void)
+{
+	scst_unregister_dev_driver(&raid_devtype);
+	return;
+}
+
+module_init(raid_init);
+module_exit(raid_exit);
+
+MODULE_AUTHOR("Vladislav Bolkhovitin & Leonid Stoljar");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("SCSI raid(controller) (type 0xC) dev handler for SCST");
+MODULE_VERSION(SCST_VERSION_STRING);
diff -uprN orig/linux-2.6.35/drivers/scst/dev_handlers/scst_tape.c linux-2.6.35/drivers/scst/dev_handlers/scst_tape.c
--- orig/linux-2.6.35/drivers/scst/dev_handlers/scst_tape.c
+++ linux-2.6.35/drivers/scst/dev_handlers/scst_tape.c
@@ -0,0 +1,361 @@
+/*
+ *  scst_tape.c
+ *
+ *  Copyright (C) 2004 - 2010 Vladislav Bolkhovitin <vst@...b.net>
+ *  Copyright (C) 2004 - 2005 Leonid Stoljar
+ *  Copyright (C) 2007 - 2010 ID7 Ltd.
+ *
+ *  SCSI tape (type 1) dev handler
+ *  &
+ *  SCSI tape (type 1) "performance" device handler (skip all READ and WRITE
+ *   operations).
+ *
+ *  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, version 2
+ *  of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <scsi/scsi_host.h>
+#include <linux/slab.h>
+
+#define LOG_PREFIX           "dev_tape"
+
+#include <scst/scst.h>
+#include "scst_dev_handler.h"
+
+# define TAPE_NAME           "dev_tape"
+# define TAPE_PERF_NAME      "dev_tape_perf"
+
+#define TAPE_RETRIES		2
+
+#define TAPE_DEF_BLOCK_SIZE	512
+
+/* The fixed bit in READ/WRITE/VERIFY */
+#define SILI_BIT		2
+
+struct tape_params {
+	int block_size;
+};
+
+static int tape_attach(struct scst_device *);
+static void tape_detach(struct scst_device *);
+static int tape_parse(struct scst_cmd *);
+static int tape_done(struct scst_cmd *);
+static int tape_perf_exec(struct scst_cmd *);
+
+static struct scst_dev_type tape_devtype = {
+	.name =			TAPE_NAME,
+	.type =			TYPE_TAPE,
+	.threads_num =		1,
+	.parse_atomic =		1,
+	.dev_done_atomic =	1,
+	.attach =		tape_attach,
+	.detach =		tape_detach,
+	.parse =		tape_parse,
+	.dev_done =		tape_done,
+#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
+	.default_trace_flags =	SCST_DEFAULT_DEV_LOG_FLAGS,
+	.trace_flags =		&trace_flag,
+#endif
+};
+
+static struct scst_dev_type tape_devtype_perf = {
+	.name =			TAPE_PERF_NAME,
+	.type =			TYPE_TAPE,
+	.parse_atomic =		1,
+	.dev_done_atomic =	1,
+	.attach =		tape_attach,
+	.detach =		tape_detach,
+	.parse =		tape_parse,
+	.dev_done =		tape_done,
+	.exec =			tape_perf_exec,
+#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
+	.default_trace_flags =	SCST_DEFAULT_DEV_LOG_FLAGS,
+	.trace_flags =		&trace_flag,
+#endif
+};
+
+static int __init init_scst_tape_driver(void)
+{
+	int res = 0;
+
+	tape_devtype.module = THIS_MODULE;
+
+	res = scst_register_dev_driver(&tape_devtype);
+	if (res < 0)
+		goto out;
+
+	tape_devtype_perf.module = THIS_MODULE;
+
+	res = scst_register_dev_driver(&tape_devtype_perf);
+	if (res < 0)
+		goto out_unreg;
+
+out:
+	return res;
+
+out_unreg:
+	scst_unregister_dev_driver(&tape_devtype);
+	goto out;
+}
+
+static void __exit exit_scst_tape_driver(void)
+{
+
+	scst_unregister_dev_driver(&tape_devtype_perf);
+	scst_unregister_dev_driver(&tape_devtype);
+	return;
+}
+
+module_init(init_scst_tape_driver);
+module_exit(exit_scst_tape_driver);
+
+static int tape_attach(struct scst_device *dev)
+{
+	int res, rc;
+	int retries;
+	struct scsi_mode_data data;
+	const int buffer_size = 512;
+	uint8_t *buffer = NULL;
+	struct tape_params *params;
+
+	if (dev->scsi_dev == NULL ||
+	    dev->scsi_dev->type != dev->type) {
+		PRINT_ERROR("%s", "SCSI device not define or illegal type");
+		res = -ENODEV;
+		goto out;
+	}
+
+	params = kzalloc(sizeof(*params), GFP_KERNEL);
+	if (params == NULL) {
+		TRACE(TRACE_OUT_OF_MEM, "%s",
+		      "Unable to allocate struct tape_params");
+		res = -ENOMEM;
+		goto out;
+	}
+
+	params->block_size = TAPE_DEF_BLOCK_SIZE;
+
+	buffer = kmalloc(buffer_size, GFP_KERNEL);
+	if (!buffer) {
+		TRACE(TRACE_OUT_OF_MEM, "%s", "Memory allocation failure");
+		res = -ENOMEM;
+		goto out_free_req;
+	}
+
+	retries = SCST_DEV_UA_RETRIES;
+	do {
+		TRACE_DBG("%s", "Doing TEST_UNIT_READY");
+		rc = scsi_test_unit_ready(dev->scsi_dev,
+			SCST_GENERIC_TAPE_SMALL_TIMEOUT, TAPE_RETRIES
+					  , NULL);
+		TRACE_DBG("TEST_UNIT_READY done: %x", rc);
+	} while ((--retries > 0) && rc);
+
+	if (rc) {
+		PRINT_WARNING("Unit not ready: %x", rc);
+		/* Let's try not to be too smart and continue processing */
+		goto obtain;
+	}
+
+	TRACE_DBG("%s", "Doing MODE_SENSE");
+	rc = scsi_mode_sense(dev->scsi_dev,
+			      ((dev->scsi_dev->scsi_level <= SCSI_2) ?
+			       ((dev->scsi_dev->lun << 5) & 0xe0) : 0),
+			      0 /* Mode Page 0 */,
+			      buffer, buffer_size,
+			      SCST_GENERIC_TAPE_SMALL_TIMEOUT, TAPE_RETRIES,
+			      &data, NULL);
+	TRACE_DBG("MODE_SENSE done: %x", rc);
+
+	if (rc == 0) {
+		int medium_type, mode, speed, density;
+		if (buffer[3] == 8) {
+			params->block_size = ((buffer[9] << 16) |
+					    (buffer[10] << 8) |
+					    (buffer[11] << 0));
+		} else
+			params->block_size = TAPE_DEF_BLOCK_SIZE;
+		medium_type = buffer[1];
+		mode = (buffer[2] & 0x70) >> 4;
+		speed = buffer[2] & 0x0f;
+		density = buffer[4];
+		TRACE_DBG("Tape: lun %d. bs %d. type 0x%02x mode 0x%02x "
+		      "speed 0x%02x dens 0x%02x", dev->scsi_dev->lun,
+		      params->block_size, medium_type, mode, speed, density);
+	} else {
+		PRINT_ERROR("MODE_SENSE failed: %x", rc);
+		res = -ENODEV;
+		goto out_free_buf;
+	}
+
+obtain:
+	res = scst_obtain_device_parameters(dev);
+	if (res != 0) {
+		PRINT_ERROR("Failed to obtain control parameters for device "
+			"%s", dev->virt_name);
+		goto out_free_buf;
+	}
+
+out_free_buf:
+	kfree(buffer);
+
+out_free_req:
+	if (res == 0)
+		dev->dh_priv = params;
+	else
+		kfree(params);
+
+out:
+	return res;
+}
+
+static void tape_detach(struct scst_device *dev)
+{
+	struct tape_params *params =
+		(struct tape_params *)dev->dh_priv;
+
+	kfree(params);
+	dev->dh_priv = NULL;
+	return;
+}
+
+static int tape_get_block_size(struct scst_cmd *cmd)
+{
+	struct tape_params *params = (struct tape_params *)cmd->dev->dh_priv;
+	/*
+	 * No need for locks here, since *_detach() can not be called,
+	 * when there are existing commands.
+	 */
+	return params->block_size;
+}
+
+static int tape_parse(struct scst_cmd *cmd)
+{
+	int res = SCST_CMD_STATE_DEFAULT;
+
+	scst_tape_generic_parse(cmd, tape_get_block_size);
+
+	cmd->retries = SCST_PASSTHROUGH_RETRIES;
+
+	return res;
+}
+
+static void tape_set_block_size(struct scst_cmd *cmd, int block_size)
+{
+	struct tape_params *params = (struct tape_params *)cmd->dev->dh_priv;
+	/*
+	 * No need for locks here, since *_detach() can not be called, when
+	 * there are existing commands.
+	 */
+	params->block_size = block_size;
+	return;
+}
+
+static int tape_done(struct scst_cmd *cmd)
+{
+	int opcode = cmd->cdb[0];
+	int status = cmd->status;
+	int res = SCST_CMD_STATE_DEFAULT;
+
+	if ((status == SAM_STAT_GOOD) || (status == SAM_STAT_CONDITION_MET))
+		res = scst_tape_generic_dev_done(cmd, tape_set_block_size);
+	else if ((status == SAM_STAT_CHECK_CONDITION) &&
+		   SCST_SENSE_VALID(cmd->sense)) {
+		struct tape_params *params;
+
+		TRACE_DBG("Extended sense %x", cmd->sense[0] & 0x7F);
+
+		if ((cmd->sense[0] & 0x7F) != 0x70) {
+			PRINT_ERROR("Sense format 0x%x is not supported",
+				cmd->sense[0] & 0x7F);
+			scst_set_cmd_error(cmd,
+				SCST_LOAD_SENSE(scst_sense_hardw_error));
+			goto out;
+		}
+
+		if (opcode == READ_6 && !(cmd->cdb[1] & SILI_BIT) &&
+		    (cmd->sense[2] & 0xe0)) {
+			/* EOF, EOM, or ILI */
+			int TransferLength, Residue = 0;
+			if ((cmd->sense[2] & 0x0f) == BLANK_CHECK)
+				/* No need for EOM in this case */
+				cmd->sense[2] &= 0xcf;
+			TransferLength = ((cmd->cdb[2] << 16) |
+					  (cmd->cdb[3] << 8) | cmd->cdb[4]);
+			/* Compute the residual count */
+			if ((cmd->sense[0] & 0x80) != 0) {
+				Residue = ((cmd->sense[3] << 24) |
+					   (cmd->sense[4] << 16) |
+					   (cmd->sense[5] << 8) |
+					   cmd->sense[6]);
+			}
+			TRACE_DBG("Checking the sense key "
+				"sn[2]=%x cmd->cdb[0,1]=%x,%x TransLen/Resid"
+				" %d/%d", (int)cmd->sense[2], cmd->cdb[0],
+				cmd->cdb[1], TransferLength, Residue);
+			if (TransferLength > Residue) {
+				int resp_data_len = TransferLength - Residue;
+				if (cmd->cdb[1] & SCST_TRANSFER_LEN_TYPE_FIXED) {
+					/*
+					 * No need for locks here, since
+					 * *_detach() can not be called, when
+					 * there are existing commands.
+					 */
+					params = (struct tape_params *)
+						 cmd->dev->dh_priv;
+					resp_data_len *= params->block_size;
+				}
+				scst_set_resp_data_len(cmd, resp_data_len);
+			}
+		}
+	}
+
+out:
+	TRACE_DBG("cmd->is_send_status=%x, cmd->resp_data_len=%d, "
+	      "res=%d", cmd->is_send_status, cmd->resp_data_len, res);
+	return res;
+}
+
+static int tape_perf_exec(struct scst_cmd *cmd)
+{
+	int res = SCST_EXEC_NOT_COMPLETED, rc;
+	int opcode = cmd->cdb[0];
+
+	rc = scst_check_local_events(cmd);
+	if (unlikely(rc != 0))
+		goto out_done;
+
+	cmd->status = 0;
+	cmd->msg_status = 0;
+	cmd->host_status = DID_OK;
+	cmd->driver_status = 0;
+
+	switch (opcode) {
+	case WRITE_6:
+	case READ_6:
+		cmd->completed = 1;
+		goto out_done;
+	}
+
+out:
+	return res;
+
+out_done:
+	res = SCST_EXEC_COMPLETED;
+	cmd->scst_cmd_done(cmd, SCST_CMD_STATE_DEFAULT, SCST_CONTEXT_SAME);
+	goto out;
+}
+
+MODULE_AUTHOR("Vladislav Bolkhovitin & Leonid Stoljar");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("SCSI tape (type 1) dev handler for SCST");
+MODULE_VERSION(SCST_VERSION_STRING);

--
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