* Re: [PATCH][RFC 0/3/2/5] SCST dev handlers
[not found] <4BC45A32.2010904@vlnb.net>
@ 2010-04-13 13:07 ` Vladislav Bolkhovitin
[not found] ` <4BC46173.7000102@vlnb.net>
1 sibling, 0 replies; 4+ messages in thread
From: Vladislav Bolkhovitin @ 2010-04-13 13:07 UTC (permalink / raw)
To: linux-scsi
Cc: linux-kernel, scst-devel, James Bottomley, Andrew Morton,
FUJITA Tomonori, Mike Christie, Jeff Garzik, Linus Torvalds,
Bart Van Assche
This patchset contains SCST dev handlers. Dev handlers are drivers which
provide backend functionality for SCST.
Vlad
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH][RFC 1/3/2/5] SCST dev handlers' Makefile
[not found] ` <4BC46173.7000102@vlnb.net>
@ 2010-04-13 13:07 ` Vladislav Bolkhovitin
2010-04-13 13:08 ` [PATCH][RFC 2/3/2/5] SCST pass-through dev handlers Vladislav Bolkhovitin
2010-04-13 13:08 ` [PATCH][RFC 3/3/2/5] SCST vdisk dev handler Vladislav Bolkhovitin
2 siblings, 0 replies; 4+ messages in thread
From: Vladislav Bolkhovitin @ 2010-04-13 13:07 UTC (permalink / raw)
To: linux-scsi
Cc: linux-kernel, scst-devel, James Bottomley, Andrew Morton,
FUJITA Tomonori, Mike Christie, Jeff Garzik, Linus Torvalds,
Bart Van Assche
This patch contains SCST dev handlers' Makefile.
Signed-off-by: Vladislav Bolkhovitin <vst@vlnb.net>
---
Makefile | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff -uprN orig/linux-2.6.33/drivers/scst/dev_handlers/Makefile linux-2.6.33/drivers/scst/dev_handlers/Makefile
--- orig/linux-2.6.33/drivers/scst/dev_handlers/Makefile
+++ linux-2.6.33/drivers/scst/dev_handlers/Makefile
@@ -0,0 +1,13 @@
+ccflags-y += -Iinclude/scst -Wno-unused-parameter
+
+obj-m := scst_cdrom.o scst_changer.o scst_disk.o scst_modisk.o scst_tape.o \
+ scst_vdisk.o scst_raid.o scst_processor.o
+
+obj-$(CONFIG_SCST_DISK) += scst_disk.o
+obj-$(CONFIG_SCST_TAPE) += scst_tape.o
+obj-$(CONFIG_SCST_CDROM) += scst_cdrom.o
+obj-$(CONFIG_SCST_MODISK) += scst_modisk.o
+obj-$(CONFIG_SCST_CHANGER) += scst_changer.o
+obj-$(CONFIG_SCST_RAID) += scst_raid.o
+obj-$(CONFIG_SCST_PROCESSOR) += scst_processor.o
+obj-$(CONFIG_SCST_VDISK) += scst_vdisk.o
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH][RFC 2/3/2/5] SCST pass-through dev handlers
[not found] ` <4BC46173.7000102@vlnb.net>
2010-04-13 13:07 ` [PATCH][RFC 1/3/2/5] SCST dev handlers' Makefile Vladislav Bolkhovitin
@ 2010-04-13 13:08 ` Vladislav Bolkhovitin
2010-04-13 13:08 ` [PATCH][RFC 3/3/2/5] SCST vdisk dev handler Vladislav Bolkhovitin
2 siblings, 0 replies; 4+ messages in thread
From: Vladislav Bolkhovitin @ 2010-04-13 13:08 UTC (permalink / raw)
To: linux-scsi
Cc: linux-kernel, scst-devel, James Bottomley, Andrew Morton,
FUJITA Tomonori, Mike Christie, Jeff Garzik, Linus Torvalds,
Bart Van Assche
This patch contains SCST pass-through dev handlers.
Signed-off-by: Vladislav Bolkhovitin <vst@vlnb.net>
---
scst_cdrom.c | 284 ++++++++++++++++++++++++++++++++++++
scst_changer.c | 207 ++++++++++++++++++++++++++
scst_dev_handler.h | 27 +++
scst_disk.c | 361 ++++++++++++++++++++++++++++++++++++++++++++++
scst_modisk.c | 380 ++++++++++++++++++++++++++++++++++++++++++++++++
scst_processor.c | 207 ++++++++++++++++++++++++++
scst_raid.c | 208 ++++++++++++++++++++++++++
scst_tape.c | 413 +++++++++++++++++++++++++++++++++++++++++++++++++++++
8 files changed, 2087 insertions(+)
diff -uprN orig/linux-2.6.33/drivers/scst/dev_handlers/scst_cdrom.c linux-2.6.33/drivers/scst/dev_handlers/scst_cdrom.c
--- orig/linux-2.6.33/drivers/scst/dev_handlers/scst_cdrom.c
+++ linux-2.6.33/drivers/scst/dev_handlers/scst_cdrom.c
@@ -0,0 +1,284 @@
+/*
+ * scst_cdrom.c
+ *
+ * Copyright (C) 2004 - 2010 Vladislav Bolkhovitin <vst@vlnb.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>
+
+#define LOG_PREFIX "dev_cdrom"
+
+#include "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,
+ .pass_through = 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
+};
+
+/**************************************************************
+ * Function: cdrom_attach
+ *
+ * Argument:
+ *
+ * Returns : 1 if attached, error code otherwise
+ *
+ * Description:
+ *************************************************************/
+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;
+}
+
+/************************************************************
+ * Function: cdrom_detach
+ *
+ * Argument:
+ *
+ * Returns : None
+ *
+ * Description: Called to detach this device type driver
+ ************************************************************/
+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;
+}
+
+/********************************************************************
+ * Function: cdrom_parse
+ *
+ * Argument:
+ *
+ * Returns : The state of the command
+ *
+ * Description: This does the parsing of the command
+ *
+ * Note: Not all states are allowed on return
+ ********************************************************************/
+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;
+}
+
+/********************************************************************
+ * Function: cdrom_done
+ *
+ * Argument:
+ *
+ * Returns :
+ *
+ * Description: This is the completion routine for the command,
+ * it is used to extract any necessary information
+ * about a command.
+ ********************************************************************/
+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.33/drivers/scst/dev_handlers/scst_changer.c linux-2.6.33/drivers/scst/dev_handlers/scst_changer.c
--- orig/linux-2.6.33/drivers/scst/dev_handlers/scst_changer.c
+++ linux-2.6.33/drivers/scst/dev_handlers/scst_changer.c
@@ -0,0 +1,207 @@
+/*
+ * scst_changer.c
+ *
+ * Copyright (C) 2004 - 2010 Vladislav Bolkhovitin <vst@vlnb.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>
+
+#define LOG_PREFIX "dev_changer"
+
+#include "scst.h"
+#include "scst_dev_handler.h"
+
+#define CHANGER_NAME "dev_changer"
+
+#define CHANGER_RETRIES 2
+#define READ_CAP_LEN 8
+
+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,
+ .pass_through = 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
+};
+
+/**************************************************************
+ * Function: changer_attach
+ *
+ * Argument:
+ *
+ * Returns : 1 if attached, error code otherwise
+ *
+ * Description:
+ *************************************************************/
+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;
+}
+
+/************************************************************
+ * Function: changer_detach
+ *
+ * Argument:
+ *
+ * Returns : None
+ *
+ * Description: Called to detach this device type driver
+ ************************************************************/
+#if 0
+void changer_detach(struct scst_device *dev)
+{
+ return;
+}
+#endif
+
+/********************************************************************
+ * Function: changer_parse
+ *
+ * Argument:
+ *
+ * Returns : The state of the command
+ *
+ * Description: This does the parsing of the command
+ *
+ * Note: Not all states are allowed on return
+ ********************************************************************/
+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;
+}
+
+/********************************************************************
+ * Function: changer_done
+ *
+ * Argument:
+ *
+ * Returns :
+ *
+ * Description: This is the completion routine for the command,
+ * it is used to extract any necessary information
+ * about a command.
+ ********************************************************************/
+#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.33/drivers/scst/dev_handlers/scst_dev_handler.h linux-2.6.33/drivers/scst/dev_handlers/scst_dev_handler.h
--- orig/linux-2.6.33/drivers/scst/dev_handlers/scst_dev_handler.h
+++ linux-2.6.33/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_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.33/drivers/scst/dev_handlers/scst_disk.c linux-2.6.33/drivers/scst/dev_handlers/scst_disk.c
--- orig/linux-2.6.33/drivers/scst/dev_handlers/scst_disk.c
+++ linux-2.6.33/drivers/scst/dev_handlers/scst_disk.c
@@ -0,0 +1,361 @@
+/*
+ * scst_disk.c
+ *
+ * Copyright (C) 2004 - 2010 Vladislav Bolkhovitin <vst@vlnb.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>
+
+#define LOG_PREFIX "dev_disk"
+
+#include "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_done(struct scst_cmd *cmd);
+static int disk_exec(struct scst_cmd *cmd);
+
+static struct scst_dev_type disk_devtype = {
+ .name = DISK_NAME,
+ .type = TYPE_DISK,
+ .pass_through = 1,
+ .parse_atomic = 1,
+ .dev_done_atomic = 1,
+ .exec_atomic = 1,
+ .attach = disk_attach,
+ .detach = disk_detach,
+ .parse = disk_parse,
+ .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,
+ .pass_through = 1,
+ .parse_atomic = 1,
+ .dev_done_atomic = 1,
+ .exec_atomic = 1,
+ .attach = disk_attach,
+ .detach = disk_detach,
+ .parse = disk_parse,
+ .dev_done = disk_done,
+ .exec = disk_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_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);
+
+/**************************************************************
+ * Function: disk_attach
+ *
+ * Argument:
+ *
+ * Returns : 1 if attached, error code otherwise
+ *
+ * Description:
+ *************************************************************/
+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;
+}
+
+/************************************************************
+ * Function: disk_detach
+ *
+ * Argument:
+ *
+ * Returns : None
+ *
+ * Description: Called to detach this device type driver
+ ************************************************************/
+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;
+}
+
+/********************************************************************
+ * Function: disk_parse
+ *
+ * Argument:
+ *
+ * Returns : The state of the command
+ *
+ * Description: This does the parsing of the command
+ *
+ * Note: Not all states are allowed on return
+ ********************************************************************/
+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;
+}
+
+/********************************************************************
+ * Function: disk_done
+ *
+ * Argument:
+ *
+ * Returns :
+ *
+ * Description: This is the completion routine for the command,
+ * it is used to extract any necessary information
+ * about a command.
+ ********************************************************************/
+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;
+}
+
+/********************************************************************
+ * Function: disk_exec
+ *
+ * Argument:
+ *
+ * Returns :
+ *
+ * Description: Make SCST do nothing for data READs and WRITES.
+ * Intended for raw line performance testing
+ ********************************************************************/
+static int disk_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 disk (type 0) dev handler for SCST");
+MODULE_VERSION(SCST_VERSION_STRING);
diff -uprN orig/linux-2.6.33/drivers/scst/dev_handlers/scst_modisk.c linux-2.6.33/drivers/scst/dev_handlers/scst_modisk.c
--- orig/linux-2.6.33/drivers/scst/dev_handlers/scst_modisk.c
+++ linux-2.6.33/drivers/scst/dev_handlers/scst_modisk.c
@@ -0,0 +1,380 @@
+/*
+ * scst_modisk.c
+ *
+ * Copyright (C) 2004 - 2010 Vladislav Bolkhovitin <vst@vlnb.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>
+
+#define LOG_PREFIX "dev_modisk"
+
+#include "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_exec(struct scst_cmd *);
+
+static struct scst_dev_type modisk_devtype = {
+ .name = MODISK_NAME,
+ .type = TYPE_MOD,
+ .pass_through = 1,
+ .parse_atomic = 1,
+ .dev_done_atomic = 1,
+ .exec_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,
+ .pass_through = 1,
+ .parse_atomic = 1,
+ .dev_done_atomic = 1,
+ .exec_atomic = 1,
+ .attach = modisk_attach,
+ .detach = modisk_detach,
+ .parse = modisk_parse,
+ .dev_done = modisk_done,
+ .exec = modisk_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);
+
+/**************************************************************
+ * Function: modisk_attach
+ *
+ * Argument:
+ *
+ * Returns : 1 if attached, error code otherwise
+ *
+ * Description:
+ *************************************************************/
+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;
+}
+
+/************************************************************
+ * Function: modisk_detach
+ *
+ * Argument:
+ *
+ * Returns : None
+ *
+ * Description: Called to detach this device type driver
+ ************************************************************/
+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;
+}
+
+/********************************************************************
+ * Function: modisk_parse
+ *
+ * Argument:
+ *
+ * Returns : The state of the command
+ *
+ * Description: This does the parsing of the command
+ *
+ * Note: Not all states are allowed on return
+ ********************************************************************/
+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;
+}
+
+/********************************************************************
+ * Function: modisk_done
+ *
+ * Argument:
+ *
+ * Returns :
+ *
+ * Description: This is the completion routine for the command,
+ * it is used to extract any necessary information
+ * about a command.
+ ********************************************************************/
+static int modisk_done(struct scst_cmd *cmd)
+{
+ int res;
+
+ res = scst_block_generic_dev_done(cmd, modisk_set_block_shift);
+ return res;
+}
+
+/********************************************************************
+ * Function: modisk_exec
+ *
+ * Argument:
+ *
+ * Returns :
+ *
+ * Description: Make SCST do nothing for data READs and WRITES.
+ * Intended for raw line performance testing
+ ********************************************************************/
+static int modisk_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.33/drivers/scst/dev_handlers/scst_processor.c linux-2.6.33/drivers/scst/dev_handlers/scst_processor.c
--- orig/linux-2.6.33/drivers/scst/dev_handlers/scst_processor.c
+++ linux-2.6.33/drivers/scst/dev_handlers/scst_processor.c
@@ -0,0 +1,207 @@
+/*
+ * scst_processor.c
+ *
+ * Copyright (C) 2004 - 2010 Vladislav Bolkhovitin <vst@vlnb.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>
+
+#define LOG_PREFIX "dev_processor"
+
+#include "scst.h"
+#include "scst_dev_handler.h"
+
+#define PROCESSOR_NAME "dev_processor"
+
+#define PROCESSOR_RETRIES 2
+#define READ_CAP_LEN 8
+
+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,
+ .pass_through = 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
+};
+
+/**************************************************************
+ * Function: processor_attach
+ *
+ * Argument:
+ *
+ * Returns : 1 if attached, error code otherwise
+ *
+ * Description:
+ *************************************************************/
+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;
+}
+
+/************************************************************
+ * Function: processor_detach
+ *
+ * Argument:
+ *
+ * Returns : None
+ *
+ * Description: Called to detach this device type driver
+ ************************************************************/
+#if 0
+void processor_detach(struct scst_device *dev)
+{
+ return;
+}
+#endif
+
+/********************************************************************
+ * Function: processor_parse
+ *
+ * Argument:
+ *
+ * Returns : The state of the command
+ *
+ * Description: This does the parsing of the command
+ *
+ * Note: Not all states are allowed on return
+ ********************************************************************/
+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;
+}
+
+/********************************************************************
+ * Function: processor_done
+ *
+ * Argument:
+ *
+ * Returns :
+ *
+ * Description: This is the completion routine for the command,
+ * it is used to extract any necessary information
+ * about a command.
+ ********************************************************************/
+#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.33/drivers/scst/dev_handlers/scst_raid.c linux-2.6.33/drivers/scst/dev_handlers/scst_raid.c
--- orig/linux-2.6.33/drivers/scst/dev_handlers/scst_raid.c
+++ linux-2.6.33/drivers/scst/dev_handlers/scst_raid.c
@@ -0,0 +1,208 @@
+/*
+ * scst_raid.c
+ *
+ * Copyright (C) 2004 - 2010 Vladislav Bolkhovitin <vst@vlnb.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 "scst.h"
+#include "scst_dev_handler.h"
+
+#define RAID_NAME "dev_raid"
+
+#define RAID_RETRIES 2
+#define READ_CAP_LEN 8
+
+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,
+ .pass_through = 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
+};
+
+/**************************************************************
+ * Function: raid_attach
+ *
+ * Argument:
+ *
+ * Returns : 1 if attached, error code otherwise
+ *
+ * Description:
+ *************************************************************/
+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;
+}
+
+/************************************************************
+ * Function: raid_detach
+ *
+ * Argument:
+ *
+ * Returns : None
+ *
+ * Description: Called to detach this device type driver
+ ************************************************************/
+#if 0
+void raid_detach(struct scst_device *dev)
+{
+ return;
+}
+#endif
+
+/********************************************************************
+ * Function: raid_parse
+ *
+ * Argument:
+ *
+ * Returns : The state of the command
+ *
+ * Description: This does the parsing of the command
+ *
+ * Note: Not all states are allowed on return
+ ********************************************************************/
+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;
+}
+
+/********************************************************************
+ * Function: raid_done
+ *
+ * Argument:
+ *
+ * Returns :
+ *
+ * Description: This is the completion routine for the command,
+ * it is used to extract any necessary information
+ * about a command.
+ ********************************************************************/
+#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.33/drivers/scst/dev_handlers/scst_tape.c linux-2.6.33/drivers/scst/dev_handlers/scst_tape.c
--- orig/linux-2.6.33/drivers/scst/dev_handlers/scst_tape.c
+++ linux-2.6.33/drivers/scst/dev_handlers/scst_tape.c
@@ -0,0 +1,413 @@
+/*
+ * scst_tape.c
+ *
+ * Copyright (C) 2004 - 2010 Vladislav Bolkhovitin <vst@vlnb.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>
+
+#define LOG_PREFIX "dev_tape"
+
+#include "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_exec(struct scst_cmd *);
+
+static struct scst_dev_type tape_devtype = {
+ .name = TAPE_NAME,
+ .type = TYPE_TAPE,
+ .pass_through = 1,
+ .parse_atomic = 1,
+ .dev_done_atomic = 1,
+ .exec_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,
+ .pass_through = 1,
+ .parse_atomic = 1,
+ .dev_done_atomic = 1,
+ .exec_atomic = 1,
+ .attach = tape_attach,
+ .detach = tape_detach,
+ .parse = tape_parse,
+ .dev_done = tape_done,
+ .exec = tape_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);
+
+/**************************************************************
+ * Function: tape_attach
+ *
+ * Argument:
+ *
+ * Returns : 1 if attached, error code otherwise
+ *
+ * Description:
+ *************************************************************/
+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;
+}
+
+/************************************************************
+ * Function: tape_detach
+ *
+ * Argument:
+ *
+ * Returns : None
+ *
+ * Description: Called to detach this device type driver
+ ************************************************************/
+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;
+}
+
+/********************************************************************
+ * Function: tape_parse
+ *
+ * Argument:
+ *
+ * Returns : The state of the command
+ *
+ * Description: This does the parsing of the command
+ *
+ * Note: Not all states are allowed on return
+ ********************************************************************/
+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;
+}
+
+/********************************************************************
+ * Function: tape_done
+ *
+ * Argument:
+ *
+ * Returns :
+ *
+ * Description: This is the completion routine for the command,
+ * it is used to extract any necessary information
+ * about a command.
+ ********************************************************************/
+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;
+}
+
+/********************************************************************
+ * Function: tape_exec
+ *
+ * Argument:
+ *
+ * Returns :
+ *
+ * Description: Make SCST do nothing for data READs and WRITES.
+ * Intended for raw line performance testing
+ ********************************************************************/
+static int tape_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);
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH][RFC 3/3/2/5] SCST vdisk dev handler
[not found] ` <4BC46173.7000102@vlnb.net>
2010-04-13 13:07 ` [PATCH][RFC 1/3/2/5] SCST dev handlers' Makefile Vladislav Bolkhovitin
2010-04-13 13:08 ` [PATCH][RFC 2/3/2/5] SCST pass-through dev handlers Vladislav Bolkhovitin
@ 2010-04-13 13:08 ` Vladislav Bolkhovitin
2 siblings, 0 replies; 4+ messages in thread
From: Vladislav Bolkhovitin @ 2010-04-13 13:08 UTC (permalink / raw)
To: linux-scsi
Cc: linux-kernel, scst-devel, James Bottomley, Andrew Morton,
FUJITA Tomonori, Mike Christie, Jeff Garzik, Linus Torvalds,
Bart Van Assche
This patch contains SCST vdisk dev handlers. This dev handler allows
to create virtual disks and CDROMs from files on file system.
Signed-off-by: Vladislav Bolkhovitin <vst@vlnb.net>
---
scst_vdisk.c | 3813 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 3813 insertions(+)
diff -uprN orig/linux-2.6.33/drivers/scst/dev_handlers/scst_vdisk.c linux-2.6.33/drivers/scst/dev_handlers/scst_vdisk.c
--- orig/linux-2.6.33/drivers/scst/dev_handlers/scst_vdisk.c
+++ linux-2.6.33/drivers/scst/dev_handlers/scst_vdisk.c
@@ -0,0 +1,3813 @@
+/*
+ * scst_vdisk.c
+ *
+ * Copyright (C) 2004 - 2010 Vladislav Bolkhovitin <vst@vlnb.net>
+ * Copyright (C) 2004 - 2005 Leonid Stoljar
+ * Copyright (C) 2007 Ming Zhang <blackmagic02881 at gmail dot com>
+ * Copyright (C) 2007 Ross Walker <rswwalker at hotmail dot com>
+ * Copyright (C) 2007 - 2010 ID7 Ltd.
+ *
+ * SCSI disk (type 0) and CDROM (type 5) dev handler using files
+ * on file systems or block devices (VDISK)
+ *
+ * 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/file.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/unistd.h>
+#include <linux/smp_lock.h>
+#include <linux/spinlock.h>
+#include <linux/init.h>
+#include <linux/uio.h>
+#include <linux/list.h>
+#include <linux/ctype.h>
+#include <linux/writeback.h>
+#include <linux/vmalloc.h>
+#include <asm/atomic.h>
+#include <linux/kthread.h>
+#include <linux/sched.h>
+#include <linux/version.h>
+#include <asm/div64.h>
+
+#define LOG_PREFIX "dev_vdisk"
+
+#include "scst.h"
+
+#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
+
+#define TRACE_ORDER 0x80000000
+
+static struct scst_trace_log vdisk_local_trace_tbl[] = {
+ { TRACE_ORDER, "order" },
+ { 0, NULL }
+};
+#define trace_log_tbl vdisk_local_trace_tbl
+
+#define VDISK_TRACE_TLB_HELP ", order"
+
+#endif
+
+#include "scst_dev_handler.h"
+
+/* 8 byte ASCII Vendor */
+#define SCST_FIO_VENDOR "SCST_FIO"
+#define SCST_BIO_VENDOR "SCST_BIO"
+/* 4 byte ASCII Product Revision Level - left aligned */
+#define SCST_FIO_REV " 200"
+
+#define MAX_USN_LEN (20+1) /* For '\0' */
+
+#define INQ_BUF_SZ 128
+#define EVPD 0x01
+#define CMDDT 0x02
+
+#define MSENSE_BUF_SZ 256
+#define DBD 0x08 /* disable block descriptor */
+#define WP 0x80 /* write protect */
+#define DPOFUA 0x10 /* DPOFUA bit */
+#define WCE 0x04 /* write cache enable */
+
+#define PF 0x10 /* page format */
+#define SP 0x01 /* save pages */
+#define PS 0x80 /* parameter saveable */
+
+#define BYTE 8
+#define DEF_DISK_BLOCKSIZE_SHIFT 9
+#define DEF_DISK_BLOCKSIZE (1 << DEF_DISK_BLOCKSIZE_SHIFT)
+#define DEF_CDROM_BLOCKSIZE_SHIFT 11
+#define DEF_CDROM_BLOCKSIZE (1 << DEF_CDROM_BLOCKSIZE_SHIFT)
+#define DEF_SECTORS 56
+#define DEF_HEADS 255
+#define LEN_MEM (32 * 1024)
+#define DEF_RD_ONLY 0
+#define DEF_WRITE_THROUGH 0
+#define DEF_NV_CACHE 0
+#define DEF_O_DIRECT 0
+#define DEF_REMOVABLE 0
+
+#define VDISK_NULLIO_SIZE (3LL*1024*1024*1024*1024/2)
+
+#define DEF_TST SCST_CONTR_MODE_SEP_TASK_SETS
+/*
+ * Since we can't control backstorage device's reordering, we have to always
+ * report unrestricted reordering.
+ */
+#define DEF_QUEUE_ALG_WT SCST_CONTR_MODE_QUEUE_ALG_UNRESTRICTED_REORDER
+#define DEF_QUEUE_ALG SCST_CONTR_MODE_QUEUE_ALG_UNRESTRICTED_REORDER
+#define DEF_SWP 0
+#define DEF_TAS 0
+
+#define DEF_DSENSE SCST_CONTR_MODE_FIXED_SENSE
+
+static unsigned int random_values[256] = {
+ 9862592UL, 3744545211UL, 2348289082UL, 4036111983UL,
+ 435574201UL, 3110343764UL, 2383055570UL, 1826499182UL,
+ 4076766377UL, 1549935812UL, 3696752161UL, 1200276050UL,
+ 3878162706UL, 1783530428UL, 2291072214UL, 125807985UL,
+ 3407668966UL, 547437109UL, 3961389597UL, 969093968UL,
+ 56006179UL, 2591023451UL, 1849465UL, 1614540336UL,
+ 3699757935UL, 479961779UL, 3768703953UL, 2529621525UL,
+ 4157893312UL, 3673555386UL, 4091110867UL, 2193909423UL,
+ 2800464448UL, 3052113233UL, 450394455UL, 3424338713UL,
+ 2113709130UL, 4082064373UL, 3708640918UL, 3841182218UL,
+ 3141803315UL, 1032476030UL, 1166423150UL, 1169646901UL,
+ 2686611738UL, 575517645UL, 2829331065UL, 1351103339UL,
+ 2856560215UL, 2402488288UL, 867847666UL, 8524618UL,
+ 704790297UL, 2228765657UL, 231508411UL, 1425523814UL,
+ 2146764591UL, 1287631730UL, 4142687914UL, 3879884598UL,
+ 729945311UL, 310596427UL, 2263511876UL, 1983091134UL,
+ 3500916580UL, 1642490324UL, 3858376049UL, 695342182UL,
+ 780528366UL, 1372613640UL, 1100993200UL, 1314818946UL,
+ 572029783UL, 3775573540UL, 776262915UL, 2684520905UL,
+ 1007252738UL, 3505856396UL, 1974886670UL, 3115856627UL,
+ 4194842288UL, 2135793908UL, 3566210707UL, 7929775UL,
+ 1321130213UL, 2627281746UL, 3587067247UL, 2025159890UL,
+ 2587032000UL, 3098513342UL, 3289360258UL, 130594898UL,
+ 2258149812UL, 2275857755UL, 3966929942UL, 1521739999UL,
+ 4191192765UL, 958953550UL, 4153558347UL, 1011030335UL,
+ 524382185UL, 4099757640UL, 498828115UL, 2396978754UL,
+ 328688935UL, 826399828UL, 3174103611UL, 3921966365UL,
+ 2187456284UL, 2631406787UL, 3930669674UL, 4282803915UL,
+ 1776755417UL, 374959755UL, 2483763076UL, 844956392UL,
+ 2209187588UL, 3647277868UL, 291047860UL, 3485867047UL,
+ 2223103546UL, 2526736133UL, 3153407604UL, 3828961796UL,
+ 3355731910UL, 2322269798UL, 2752144379UL, 519897942UL,
+ 3430536488UL, 1801511593UL, 1953975728UL, 3286944283UL,
+ 1511612621UL, 1050133852UL, 409321604UL, 1037601109UL,
+ 3352316843UL, 4198371381UL, 617863284UL, 994672213UL,
+ 1540735436UL, 2337363549UL, 1242368492UL, 665473059UL,
+ 2330728163UL, 3443103219UL, 2291025133UL, 3420108120UL,
+ 2663305280UL, 1608969839UL, 2278959931UL, 1389747794UL,
+ 2226946970UL, 2131266900UL, 3856979144UL, 1894169043UL,
+ 2692697628UL, 3797290626UL, 3248126844UL, 3922786277UL,
+ 343705271UL, 3739749888UL, 2191310783UL, 2962488787UL,
+ 4119364141UL, 1403351302UL, 2984008923UL, 3822407178UL,
+ 1932139782UL, 2323869332UL, 2793574182UL, 1852626483UL,
+ 2722460269UL, 1136097522UL, 1005121083UL, 1805201184UL,
+ 2212824936UL, 2979547931UL, 4133075915UL, 2585731003UL,
+ 2431626071UL, 134370235UL, 3763236829UL, 1171434827UL,
+ 2251806994UL, 1289341038UL, 3616320525UL, 392218563UL,
+ 1544502546UL, 2993937212UL, 1957503701UL, 3579140080UL,
+ 4270846116UL, 2030149142UL, 1792286022UL, 366604999UL,
+ 2625579499UL, 790898158UL, 770833822UL, 815540197UL,
+ 2747711781UL, 3570468835UL, 3976195842UL, 1257621341UL,
+ 1198342980UL, 1860626190UL, 3247856686UL, 351473955UL,
+ 993440563UL, 340807146UL, 1041994520UL, 3573925241UL,
+ 480246395UL, 2104806831UL, 1020782793UL, 3362132583UL,
+ 2272911358UL, 3440096248UL, 2356596804UL, 259492703UL,
+ 3899500740UL, 252071876UL, 2177024041UL, 4284810959UL,
+ 2775999888UL, 2653420445UL, 2876046047UL, 1025771859UL,
+ 1994475651UL, 3564987377UL, 4112956647UL, 1821511719UL,
+ 3113447247UL, 455315102UL, 1585273189UL, 2311494568UL,
+ 774051541UL, 1898115372UL, 2637499516UL, 247231365UL,
+ 1475014417UL, 803585727UL, 3911097303UL, 1714292230UL,
+ 476579326UL, 2496900974UL, 3397613314UL, 341202244UL,
+ 807790202UL, 4221326173UL, 499979741UL, 1301488547UL,
+ 1056807896UL, 3525009458UL, 1174811641UL, 3049738746UL,
+};
+
+struct scst_vdisk_dev {
+ uint32_t block_size;
+ uint64_t nblocks;
+ int block_shift;
+ loff_t file_size; /* in bytes */
+
+ /*
+ * This lock can be taken on both SIRQ and thread context, but in
+ * all cases for each particular instance it's taken consistenly either
+ * on SIRQ or thread context. Mix of them is forbidden.
+ */
+ spinlock_t flags_lock;
+
+ /*
+ * Below flags are protected by flags_lock or suspended activity
+ * with scst_vdisk_mutex.
+ */
+ unsigned int rd_only:1;
+ unsigned int wt_flag:1;
+ unsigned int nv_cache:1;
+ unsigned int o_direct_flag:1;
+ unsigned int media_changed:1;
+ unsigned int prevent_allow_medium_removal:1;
+ unsigned int nullio:1;
+ unsigned int blockio:1;
+ unsigned int cdrom_empty:1;
+ unsigned int removable:1;
+
+ int virt_id;
+ char name[16+1]; /* Name of the virtual device,
+ must be <= SCSI Model + 1 */
+ char *filename; /* File name, protected by
+ scst_mutex and suspended activities */
+ unsigned int t10_dev_id_set:1; /* true if t10_dev_id manually set */
+ char t10_dev_id[16+8+2]; /* T10 device ID */
+ char usn[MAX_USN_LEN];
+ struct scst_device *dev;
+ struct list_head vdev_list_entry;
+
+ struct mutex vdev_sysfs_mutex;
+ struct scst_dev_type *vdev_devt;
+};
+
+struct scst_vdisk_tgt_dev {
+ /*
+ * Used without locking since SCST core ensures that only commands
+ * with the same ORDERED type per tgt_dev can be processed
+ * simultaneously.
+ */
+ enum scst_cmd_queue_type last_write_cmd_queue_type;
+};
+
+struct scst_vdisk_thr {
+ struct scst_thr_data_hdr hdr;
+ struct file *fd;
+ struct block_device *bdev;
+ struct iovec *iv;
+ int iv_count;
+};
+
+/* Context RA patch supposed to be applied on the kernel */
+#define DEF_NUM_THREADS 8
+static int num_threads = DEF_NUM_THREADS;
+
+module_param_named(num_threads, num_threads, int, S_IRUGO);
+MODULE_PARM_DESC(num_threads, "vdisk threads count");
+
+static int vdisk_attach(struct scst_device *dev);
+static void vdisk_detach(struct scst_device *dev);
+static int vdisk_attach_tgt(struct scst_tgt_dev *tgt_dev);
+static void vdisk_detach_tgt(struct scst_tgt_dev *tgt_dev);
+static int vdisk_parse(struct scst_cmd *);
+static int vdisk_do_job(struct scst_cmd *cmd);
+static int vcdrom_parse(struct scst_cmd *);
+static int vcdrom_exec(struct scst_cmd *cmd);
+static void vdisk_exec_read(struct scst_cmd *cmd,
+ struct scst_vdisk_thr *thr, loff_t loff);
+static void vdisk_exec_write(struct scst_cmd *cmd,
+ struct scst_vdisk_thr *thr, loff_t loff);
+static void blockio_exec_rw(struct scst_cmd *cmd, struct scst_vdisk_thr *thr,
+ u64 lba_start, int write);
+static void vdisk_exec_verify(struct scst_cmd *cmd,
+ struct scst_vdisk_thr *thr, loff_t loff);
+static void vdisk_exec_read_capacity(struct scst_cmd *cmd);
+static void vdisk_exec_read_capacity16(struct scst_cmd *cmd);
+static void vdisk_exec_inquiry(struct scst_cmd *cmd);
+static void vdisk_exec_request_sense(struct scst_cmd *cmd);
+static void vdisk_exec_mode_sense(struct scst_cmd *cmd);
+static void vdisk_exec_mode_select(struct scst_cmd *cmd);
+static void vdisk_exec_log(struct scst_cmd *cmd);
+static void vdisk_exec_read_toc(struct scst_cmd *cmd);
+static void vdisk_exec_prevent_allow_medium_removal(struct scst_cmd *cmd);
+static int vdisk_fsync(struct scst_vdisk_thr *thr, loff_t loff,
+ loff_t len, struct scst_cmd *cmd, struct scst_device *dev);
+static ssize_t vdisk_add_fileio_device(const char *device_name, char *params);
+static ssize_t vdisk_add_blockio_device(const char *device_name, char *params);
+static ssize_t vdisk_add_nullio_device(const char *device_name, char *params);
+static ssize_t vdisk_del_device(const char *device_name);
+static ssize_t vcdrom_add_device(const char *device_name, char *params);
+static ssize_t vcdrom_del_device(const char *device_name);
+static int vdisk_task_mgmt_fn(struct scst_mgmt_cmd *mcmd,
+ struct scst_tgt_dev *tgt_dev);
+static uint64_t vdisk_gen_dev_id_num(const char *virt_dev_name);
+
+/** SYSFS **/
+
+static ssize_t vdev_sysfs_size_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf);
+static ssize_t vdisk_sysfs_blocksize_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf);
+static ssize_t vdisk_sysfs_rd_only_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf);
+static ssize_t vdisk_sysfs_wt_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf);
+static ssize_t vdisk_sysfs_nv_cache_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf);
+static ssize_t vdisk_sysfs_o_direct_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf);
+static ssize_t vdisk_sysfs_removable_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf);
+static ssize_t vdev_sysfs_filename_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf);
+static ssize_t vdisk_sysfs_resync_size_store(struct kobject *kobj,
+ struct kobj_attribute *attr, const char *buf, size_t count);
+static ssize_t vdev_sysfs_t10_dev_id_store(struct kobject *kobj,
+ struct kobj_attribute *attr, const char *buf, size_t count);
+static ssize_t vdev_sysfs_t10_dev_id_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf);
+static ssize_t vdev_sysfs_usn_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf);
+
+static ssize_t vcdrom_sysfs_filename_store(struct kobject *kobj,
+ struct kobj_attribute *attr, const char *buf, size_t count);
+
+static struct kobj_attribute vdev_size_attr =
+ __ATTR(size_mb, S_IRUGO, vdev_sysfs_size_show, NULL);
+static struct kobj_attribute vdisk_blocksize_attr =
+ __ATTR(blocksize, S_IRUGO, vdisk_sysfs_blocksize_show, NULL);
+static struct kobj_attribute vdisk_rd_only_attr =
+ __ATTR(read_only, S_IRUGO, vdisk_sysfs_rd_only_show, NULL);
+static struct kobj_attribute vdisk_wt_attr =
+ __ATTR(write_through, S_IRUGO, vdisk_sysfs_wt_show, NULL);
+static struct kobj_attribute vdisk_nv_cache_attr =
+ __ATTR(nv_cache, S_IRUGO, vdisk_sysfs_nv_cache_show, NULL);
+static struct kobj_attribute vdisk_o_direct_attr =
+ __ATTR(o_direct, S_IRUGO, vdisk_sysfs_o_direct_show, NULL);
+static struct kobj_attribute vdisk_removable_attr =
+ __ATTR(removable, S_IRUGO, vdisk_sysfs_removable_show, NULL);
+static struct kobj_attribute vdisk_filename_attr =
+ __ATTR(filename, S_IRUGO, vdev_sysfs_filename_show, NULL);
+static struct kobj_attribute vdisk_resync_size_attr =
+ __ATTR(resync_size, S_IWUSR, NULL, vdisk_sysfs_resync_size_store);
+static struct kobj_attribute vdev_t10_dev_id_attr =
+ __ATTR(t10_dev_id, S_IWUSR|S_IRUGO, vdev_sysfs_t10_dev_id_show,
+ vdev_sysfs_t10_dev_id_store);
+static struct kobj_attribute vdev_usn_attr =
+ __ATTR(usn, S_IRUGO, vdev_sysfs_usn_show, NULL);
+
+static struct kobj_attribute vcdrom_filename_attr =
+ __ATTR(filename, S_IRUGO|S_IWUSR, vdev_sysfs_filename_show,
+ vcdrom_sysfs_filename_store);
+
+static const struct attribute *vdisk_fileio_attrs[] = {
+ &vdev_size_attr.attr,
+ &vdisk_blocksize_attr.attr,
+ &vdisk_rd_only_attr.attr,
+ &vdisk_wt_attr.attr,
+ &vdisk_nv_cache_attr.attr,
+ &vdisk_o_direct_attr.attr,
+ &vdisk_removable_attr.attr,
+ &vdisk_filename_attr.attr,
+ &vdisk_resync_size_attr.attr,
+ &vdev_t10_dev_id_attr.attr,
+ &vdev_usn_attr.attr,
+ NULL,
+};
+
+static const struct attribute *vdisk_blockio_attrs[] = {
+ &vdev_size_attr.attr,
+ &vdisk_blocksize_attr.attr,
+ &vdisk_rd_only_attr.attr,
+ &vdisk_removable_attr.attr,
+ &vdisk_filename_attr.attr,
+ &vdisk_resync_size_attr.attr,
+ &vdev_t10_dev_id_attr.attr,
+ &vdev_usn_attr.attr,
+ NULL,
+};
+
+static const struct attribute *vdisk_nullio_attrs[] = {
+ &vdev_size_attr.attr,
+ &vdisk_blocksize_attr.attr,
+ &vdisk_rd_only_attr.attr,
+ &vdisk_removable_attr.attr,
+ &vdev_t10_dev_id_attr.attr,
+ &vdev_usn_attr.attr,
+ NULL,
+};
+
+static const struct attribute *vcdrom_attrs[] = {
+ &vdev_size_attr.attr,
+ &vcdrom_filename_attr.attr,
+ &vdev_t10_dev_id_attr.attr,
+ &vdev_usn_attr.attr,
+ NULL,
+};
+
+/* Protects vdisks addition/deletion and related activities, like search */
+static DEFINE_MUTEX(scst_vdisk_mutex);
+static DEFINE_RWLOCK(vdisk_t10_dev_id_rwlock);
+
+/* Protected by scst_vdisk_mutex */
+static LIST_HEAD(vdev_list);
+
+static struct kmem_cache *vdisk_thr_cachep;
+
+/*
+ * Be careful changing "name" field, since it is the name of the corresponding
+ * /sys/kernel/scst_tgt entry, hence a part of user space ABI.
+ */
+
+static struct scst_dev_type vdisk_file_devtype = {
+ .name = "vdisk_fileio",
+ .type = TYPE_DISK,
+ .exec_sync = 1,
+ .threads_num = -1,
+ .parse_atomic = 1,
+ .exec_atomic = 0,
+ .dev_done_atomic = 1,
+ .attach = vdisk_attach,
+ .detach = vdisk_detach,
+ .attach_tgt = vdisk_attach_tgt,
+ .detach_tgt = vdisk_detach_tgt,
+ .parse = vdisk_parse,
+ .exec = vdisk_do_job,
+ .task_mgmt_fn = vdisk_task_mgmt_fn,
+ .add_device = vdisk_add_fileio_device,
+ .del_device = vdisk_del_device,
+ .dev_attrs = vdisk_fileio_attrs,
+ .add_device_parameters_help = "filename, blocksize, write_through, "
+ "nv_cache, o_direct, read_only, removable",
+#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
+ .default_trace_flags = SCST_DEFAULT_DEV_LOG_FLAGS,
+ .trace_flags = &trace_flag,
+ .trace_tbl = vdisk_local_trace_tbl,
+ .trace_tbl_help = VDISK_TRACE_TLB_HELP,
+#endif
+};
+
+static struct kmem_cache *blockio_work_cachep;
+
+static struct scst_dev_type vdisk_blk_devtype = {
+ .name = "vdisk_blockio",
+ .type = TYPE_DISK,
+ .threads_num = 0,
+ .parse_atomic = 1,
+ .exec_atomic = 0,
+ .dev_done_atomic = 1,
+ .attach = vdisk_attach,
+ .detach = vdisk_detach,
+ .attach_tgt = vdisk_attach_tgt,
+ .detach_tgt = vdisk_detach_tgt,
+ .parse = vdisk_parse,
+ .exec = vdisk_do_job,
+ .task_mgmt_fn = vdisk_task_mgmt_fn,
+ .add_device = vdisk_add_blockio_device,
+ .del_device = vdisk_del_device,
+ .dev_attrs = vdisk_blockio_attrs,
+ .add_device_parameters_help = "filename, blocksize, read_only, "
+ "removable",
+#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
+ .default_trace_flags = SCST_DEFAULT_DEV_LOG_FLAGS,
+ .trace_flags = &trace_flag,
+ .trace_tbl = vdisk_local_trace_tbl,
+ .trace_tbl_help = VDISK_TRACE_TLB_HELP,
+#endif
+};
+
+static struct scst_dev_type vdisk_null_devtype = {
+ .name = "vdisk_nullio",
+ .type = TYPE_DISK,
+ .threads_num = 0,
+ .parse_atomic = 1,
+ .exec_atomic = 1,
+ .dev_done_atomic = 1,
+ .attach = vdisk_attach,
+ .detach = vdisk_detach,
+ .attach_tgt = vdisk_attach_tgt,
+ .detach_tgt = vdisk_detach_tgt,
+ .parse = vdisk_parse,
+ .exec = vdisk_do_job,
+ .task_mgmt_fn = vdisk_task_mgmt_fn,
+ .add_device = vdisk_add_nullio_device,
+ .del_device = vdisk_del_device,
+ .dev_attrs = vdisk_nullio_attrs,
+ .add_device_parameters_help = "blocksize, read_only, removable",
+#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
+ .default_trace_flags = SCST_DEFAULT_DEV_LOG_FLAGS,
+ .trace_flags = &trace_flag,
+ .trace_tbl = vdisk_local_trace_tbl,
+ .trace_tbl_help = VDISK_TRACE_TLB_HELP,
+#endif
+};
+
+static struct scst_dev_type vcdrom_devtype = {
+ .name = "vcdrom",
+ .type = TYPE_ROM,
+ .exec_sync = 1,
+ .threads_num = -1,
+ .parse_atomic = 1,
+ .exec_atomic = 0,
+ .dev_done_atomic = 1,
+ .attach = vdisk_attach,
+ .detach = vdisk_detach,
+ .attach_tgt = vdisk_attach_tgt,
+ .detach_tgt = vdisk_detach_tgt,
+ .parse = vcdrom_parse,
+ .exec = vcdrom_exec,
+ .task_mgmt_fn = vdisk_task_mgmt_fn,
+ .add_device = vcdrom_add_device,
+ .del_device = vcdrom_del_device,
+ .dev_attrs = vcdrom_attrs,
+ .add_device_parameters_help = NULL,
+#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
+ .default_trace_flags = SCST_DEFAULT_DEV_LOG_FLAGS,
+ .trace_flags = &trace_flag,
+ .trace_tbl = vdisk_local_trace_tbl,
+ .trace_tbl_help = VDISK_TRACE_TLB_HELP,
+#endif
+};
+
+static struct scst_vdisk_thr nullio_thr_data;
+
+static const char *vdev_get_filename(const struct scst_vdisk_dev *virt_dev)
+{
+ if (virt_dev->filename != NULL)
+ return virt_dev->filename;
+ else
+ return "none";
+}
+
+/* Returns fd, use IS_ERR(fd) to get error status */
+static struct file *vdev_open_fd(const struct scst_vdisk_dev *virt_dev)
+{
+ int open_flags = 0;
+ struct file *fd;
+
+ if (virt_dev->dev->rd_only)
+ open_flags |= O_RDONLY;
+ else
+ open_flags |= O_RDWR;
+ if (virt_dev->o_direct_flag)
+ open_flags |= O_DIRECT;
+ if (virt_dev->wt_flag && !virt_dev->nv_cache)
+ open_flags |= O_SYNC;
+ TRACE_DBG("Opening file %s, flags 0x%x",
+ virt_dev->filename, open_flags);
+ fd = filp_open(virt_dev->filename, O_LARGEFILE | open_flags, 0600);
+ return fd;
+}
+
+/* Returns 0 on success and file size in *file_size, error code otherwise */
+static int vdisk_get_file_size(const char *filename, bool blockio,
+ loff_t *file_size)
+{
+ struct inode *inode;
+ int res = 0;
+ struct file *fd;
+
+ *file_size = 0;
+
+ fd = filp_open(filename, O_LARGEFILE | O_RDONLY, 0600);
+ if (IS_ERR(fd)) {
+ res = PTR_ERR(fd);
+ PRINT_ERROR("filp_open(%s) returned error %d", filename, res);
+ goto out;
+ }
+
+ inode = fd->f_dentry->d_inode;
+
+ if (blockio && !S_ISBLK(inode->i_mode)) {
+ PRINT_ERROR("File %s is NOT a block device", filename);
+ res = -EINVAL;
+ goto out_close;
+ }
+
+ if (S_ISREG(inode->i_mode))
+ /* Nothing to do*/;
+ else if (S_ISBLK(inode->i_mode))
+ inode = inode->i_bdev->bd_inode;
+ else {
+ res = -EINVAL;
+ goto out_close;
+ }
+
+ *file_size = inode->i_size;
+
+out_close:
+ filp_close(fd, NULL);
+
+out:
+ return res;
+}
+
+static int vdisk_attach(struct scst_device *dev)
+{
+ int res = 0;
+ loff_t err;
+ struct scst_vdisk_dev *virt_dev = NULL, *vv;
+
+ TRACE_DBG("virt_id %d (%s)", dev->virt_id, dev->virt_name);
+
+ if (dev->virt_id == 0) {
+ PRINT_ERROR("%s", "Not a virtual device");
+ res = -EINVAL;
+ goto out;
+ }
+
+ /*
+ * scst_vdisk_mutex must be already taken before
+ * scst_register_virtual_device()
+ */
+ list_for_each_entry(vv, &vdev_list, vdev_list_entry) {
+ if (strcmp(vv->name, dev->virt_name) == 0) {
+ virt_dev = vv;
+ break;
+ }
+ }
+
+ if (virt_dev == NULL) {
+ PRINT_ERROR("Device %s not found", dev->virt_name);
+ res = -EINVAL;
+ goto out;
+ }
+
+ virt_dev->dev = dev;
+
+ dev->rd_only = virt_dev->rd_only;
+
+ if (!virt_dev->cdrom_empty) {
+ if (virt_dev->nullio)
+ err = VDISK_NULLIO_SIZE;
+ else {
+ res = vdisk_get_file_size(virt_dev->filename,
+ virt_dev->blockio, &err);
+ if (res != 0)
+ goto out;
+ }
+ virt_dev->file_size = err;
+ TRACE_DBG("size of file: %lld", (long long unsigned int)err);
+ } else
+ virt_dev->file_size = 0;
+
+ virt_dev->nblocks = virt_dev->file_size >> virt_dev->block_shift;
+
+ if (!virt_dev->cdrom_empty) {
+ PRINT_INFO("Attached SCSI target virtual %s %s "
+ "(file=\"%s\", fs=%lldMB, bs=%d, nblocks=%lld,"
+ " cyln=%lld%s)",
+ (dev->type == TYPE_DISK) ? "disk" : "cdrom",
+ virt_dev->name, vdev_get_filename(virt_dev),
+ virt_dev->file_size >> 20, virt_dev->block_size,
+ (long long unsigned int)virt_dev->nblocks,
+ (long long unsigned int)virt_dev->nblocks/64/32,
+ virt_dev->nblocks < 64*32
+ ? " !WARNING! cyln less than 1" : "");
+ } else {
+ PRINT_INFO("Attached empty SCSI target virtual cdrom %s",
+ virt_dev->name);
+ }
+
+ dev->dh_priv = virt_dev;
+
+ dev->tst = DEF_TST;
+ dev->d_sense = DEF_DSENSE;
+ if (virt_dev->wt_flag && !virt_dev->nv_cache)
+ dev->queue_alg = DEF_QUEUE_ALG_WT;
+ else
+ dev->queue_alg = DEF_QUEUE_ALG;
+ dev->swp = DEF_SWP;
+ dev->tas = DEF_TAS;
+
+out:
+ return res;
+}
+
+/* scst_mutex supposed to be held */
+static void vdisk_detach(struct scst_device *dev)
+{
+ struct scst_vdisk_dev *virt_dev =
+ (struct scst_vdisk_dev *)dev->dh_priv;
+
+ TRACE_DBG("virt_id %d", dev->virt_id);
+
+ PRINT_INFO("Detached SCSI target virtual device %s (\"%s\")",
+ virt_dev->name, vdev_get_filename(virt_dev));
+
+ /* virt_dev will be freed by the caller */
+ dev->dh_priv = NULL;
+ return;
+}
+
+static void vdisk_free_thr_data(struct scst_thr_data_hdr *d)
+{
+ struct scst_vdisk_thr *thr =
+ container_of(d, struct scst_vdisk_thr, hdr);
+
+ if (thr->fd)
+ filp_close(thr->fd, NULL);
+
+ kfree(thr->iv);
+
+ kmem_cache_free(vdisk_thr_cachep, thr);
+ return;
+}
+
+static struct scst_vdisk_thr *vdisk_init_thr_data(
+ struct scst_tgt_dev *tgt_dev)
+{
+ struct scst_vdisk_thr *res;
+ struct scst_vdisk_dev *virt_dev =
+ (struct scst_vdisk_dev *)tgt_dev->dev->dh_priv;
+
+ EXTRACHECKS_BUG_ON(virt_dev->nullio);
+
+ res = kmem_cache_zalloc(vdisk_thr_cachep, GFP_KERNEL);
+ if (res == NULL) {
+ TRACE(TRACE_OUT_OF_MEM, "%s", "Unable to allocate struct "
+ "scst_vdisk_thr");
+ goto out;
+ }
+
+ if (!virt_dev->cdrom_empty) {
+ res->fd = vdev_open_fd(virt_dev);
+ if (IS_ERR(res->fd)) {
+ PRINT_ERROR("filp_open(%s) returned an error %ld",
+ virt_dev->filename, PTR_ERR(res->fd));
+ goto out_free;
+ }
+ if (virt_dev->blockio)
+ res->bdev = res->fd->f_dentry->d_inode->i_bdev;
+ else
+ res->bdev = NULL;
+ } else
+ res->fd = NULL;
+
+ scst_add_thr_data(tgt_dev, &res->hdr, vdisk_free_thr_data);
+
+out:
+ return res;
+
+out_free:
+ kmem_cache_free(vdisk_thr_cachep, res);
+ res = NULL;
+ goto out;
+}
+
+static int vdisk_attach_tgt(struct scst_tgt_dev *tgt_dev)
+{
+ struct scst_vdisk_tgt_dev *ftgt_dev;
+ int res = 0;
+
+ ftgt_dev = kzalloc(sizeof(*ftgt_dev), GFP_KERNEL);
+ if (ftgt_dev == NULL) {
+ TRACE(TRACE_OUT_OF_MEM, "%s", "Allocation of per-session "
+ "virtual device failed");
+ res = -ENOMEM;
+ goto out;
+ }
+
+ tgt_dev->dh_priv = ftgt_dev;
+
+out:
+ return res;
+}
+
+static void vdisk_detach_tgt(struct scst_tgt_dev *tgt_dev)
+{
+ struct scst_vdisk_tgt_dev *ftgt_dev =
+ (struct scst_vdisk_tgt_dev *)tgt_dev->dh_priv;
+
+ scst_del_all_thr_data(tgt_dev);
+
+ kfree(ftgt_dev);
+ tgt_dev->dh_priv = NULL;
+ return;
+}
+
+static inline int vdisk_sync_queue_type(enum scst_cmd_queue_type qt)
+{
+ switch (qt) {
+ case SCST_CMD_QUEUE_ORDERED:
+ case SCST_CMD_QUEUE_HEAD_OF_QUEUE:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static inline int vdisk_need_pre_sync(enum scst_cmd_queue_type cur,
+ enum scst_cmd_queue_type last)
+{
+ if (vdisk_sync_queue_type(cur))
+ if (!vdisk_sync_queue_type(last))
+ return 1;
+ return 0;
+}
+
+static int vdisk_do_job(struct scst_cmd *cmd)
+{
+ int rc, res;
+ uint64_t lba_start = 0;
+ loff_t data_len = 0;
+ uint8_t *cdb = cmd->cdb;
+ int opcode = cdb[0];
+ loff_t loff;
+ struct scst_device *dev = cmd->dev;
+ struct scst_tgt_dev *tgt_dev = cmd->tgt_dev;
+ struct scst_vdisk_dev *virt_dev =
+ (struct scst_vdisk_dev *)dev->dh_priv;
+ struct scst_thr_data_hdr *d;
+ struct scst_vdisk_thr *thr = NULL;
+ int fua = 0;
+
+ switch (cmd->queue_type) {
+ case SCST_CMD_QUEUE_ORDERED:
+ TRACE(TRACE_ORDER, "ORDERED cmd %p (op %x)", cmd, cmd->cdb[0]);
+ break;
+ case SCST_CMD_QUEUE_HEAD_OF_QUEUE:
+ TRACE(TRACE_ORDER, "HQ cmd %p (op %x)", cmd, cmd->cdb[0]);
+ break;
+ default:
+ break;
+ }
+
+ 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;
+
+ if (!virt_dev->nullio) {
+ d = scst_find_thr_data(tgt_dev);
+ if (unlikely(d == NULL)) {
+ thr = vdisk_init_thr_data(tgt_dev);
+ if (thr == NULL) {
+ scst_set_busy(cmd);
+ goto out_compl;
+ }
+ scst_thr_data_get(&thr->hdr);
+ } else
+ thr = container_of(d, struct scst_vdisk_thr, hdr);
+ } else {
+ thr = &nullio_thr_data;
+ scst_thr_data_get(&thr->hdr);
+ }
+
+ switch (opcode) {
+ case READ_6:
+ case WRITE_6:
+ case VERIFY_6:
+ lba_start = (((cdb[1] & 0x1f) << (BYTE * 2)) +
+ (cdb[2] << (BYTE * 1)) +
+ (cdb[3] << (BYTE * 0)));
+ data_len = cmd->bufflen;
+ break;
+ case READ_10:
+ case READ_12:
+ case WRITE_10:
+ case WRITE_12:
+ case VERIFY:
+ case WRITE_VERIFY:
+ case WRITE_VERIFY_12:
+ case VERIFY_12:
+ lba_start |= ((u64)cdb[2]) << 24;
+ lba_start |= ((u64)cdb[3]) << 16;
+ lba_start |= ((u64)cdb[4]) << 8;
+ lba_start |= ((u64)cdb[5]);
+ data_len = cmd->bufflen;
+ break;
+ case READ_16:
+ case WRITE_16:
+ case WRITE_VERIFY_16:
+ case VERIFY_16:
+ lba_start |= ((u64)cdb[2]) << 56;
+ lba_start |= ((u64)cdb[3]) << 48;
+ lba_start |= ((u64)cdb[4]) << 40;
+ lba_start |= ((u64)cdb[5]) << 32;
+ lba_start |= ((u64)cdb[6]) << 24;
+ lba_start |= ((u64)cdb[7]) << 16;
+ lba_start |= ((u64)cdb[8]) << 8;
+ lba_start |= ((u64)cdb[9]);
+ data_len = cmd->bufflen;
+ break;
+ case SYNCHRONIZE_CACHE:
+ lba_start |= ((u64)cdb[2]) << 24;
+ lba_start |= ((u64)cdb[3]) << 16;
+ lba_start |= ((u64)cdb[4]) << 8;
+ lba_start |= ((u64)cdb[5]);
+ data_len = ((cdb[7] << (BYTE * 1)) + (cdb[8] << (BYTE * 0)))
+ << virt_dev->block_shift;
+ if (data_len == 0)
+ data_len = virt_dev->file_size -
+ ((loff_t)lba_start << virt_dev->block_shift);
+ break;
+ }
+
+ loff = (loff_t)lba_start << virt_dev->block_shift;
+ TRACE_DBG("cmd %p, lba_start %lld, loff %lld, data_len %lld", cmd,
+ (long long unsigned int)lba_start,
+ (long long unsigned int)loff,
+ (long long unsigned int)data_len);
+ if (unlikely(loff < 0) || unlikely(data_len < 0) ||
+ unlikely((loff + data_len) > virt_dev->file_size)) {
+ PRINT_INFO("Access beyond the end of the device "
+ "(%lld of %lld, len %lld)",
+ (long long unsigned int)loff,
+ (long long unsigned int)virt_dev->file_size,
+ (long long unsigned int)data_len);
+ scst_set_cmd_error(cmd, SCST_LOAD_SENSE(
+ scst_sense_block_out_range_error));
+ goto out_compl;
+ }
+
+ switch (opcode) {
+ case WRITE_10:
+ case WRITE_12:
+ case WRITE_16:
+ fua = (cdb[1] & 0x8);
+ if (fua) {
+ TRACE(TRACE_ORDER, "FUA: loff=%lld, "
+ "data_len=%lld", (long long unsigned int)loff,
+ (long long unsigned int)data_len);
+ }
+ break;
+ }
+
+ switch (opcode) {
+ case READ_6:
+ case READ_10:
+ case READ_12:
+ case READ_16:
+ if (virt_dev->blockio) {
+ blockio_exec_rw(cmd, thr, lba_start, 0);
+ goto out_thr;
+ } else
+ vdisk_exec_read(cmd, thr, loff);
+ break;
+ case WRITE_6:
+ case WRITE_10:
+ case WRITE_12:
+ case WRITE_16:
+ {
+ int do_fsync = vdisk_sync_queue_type(cmd->queue_type);
+ struct scst_vdisk_tgt_dev *ftgt_dev =
+ (struct scst_vdisk_tgt_dev *)tgt_dev->dh_priv;
+ enum scst_cmd_queue_type last_queue_type =
+ ftgt_dev->last_write_cmd_queue_type;
+ ftgt_dev->last_write_cmd_queue_type = cmd->queue_type;
+ if (vdisk_need_pre_sync(cmd->queue_type, last_queue_type)) {
+ TRACE(TRACE_ORDER, "ORDERED WRITE(%d): loff=%lld, "
+ "data_len=%lld", cmd->queue_type,
+ (long long unsigned int)loff,
+ (long long unsigned int)data_len);
+ do_fsync = 1;
+ if (vdisk_fsync(thr, 0, 0, cmd, dev) != 0)
+ goto out_compl;
+ }
+ if (virt_dev->blockio) {
+ blockio_exec_rw(cmd, thr, lba_start, 1);
+ goto out_thr;
+ } else
+ vdisk_exec_write(cmd, thr, loff);
+ /* O_SYNC flag is used for WT devices */
+ if (do_fsync || fua)
+ vdisk_fsync(thr, loff, data_len, cmd, dev);
+ break;
+ }
+ case WRITE_VERIFY:
+ case WRITE_VERIFY_12:
+ case WRITE_VERIFY_16:
+ {
+ int do_fsync = vdisk_sync_queue_type(cmd->queue_type);
+ struct scst_vdisk_tgt_dev *ftgt_dev =
+ (struct scst_vdisk_tgt_dev *) tgt_dev->dh_priv;
+ enum scst_cmd_queue_type last_queue_type =
+ ftgt_dev->last_write_cmd_queue_type;
+ ftgt_dev->last_write_cmd_queue_type = cmd->queue_type;
+ if (vdisk_need_pre_sync(cmd->queue_type, last_queue_type)) {
+ TRACE(TRACE_ORDER, "ORDERED "
+ "WRITE_VERIFY(%d): loff=%lld,"
+ " data_len=%lld", cmd->queue_type,
+ (long long unsigned int)loff,
+ (long long unsigned int)data_len);
+ do_fsync = 1;
+ if (vdisk_fsync(thr, 0, 0, cmd, dev) != 0)
+ goto out_compl;
+ }
+ /* ToDo: BLOCKIO VERIFY */
+ vdisk_exec_write(cmd, thr, loff);
+ /* O_SYNC flag is used for WT devices */
+ if (scsi_status_is_good(cmd->status))
+ vdisk_exec_verify(cmd, thr, loff);
+ else if (do_fsync)
+ vdisk_fsync(thr, loff, data_len, cmd, dev);
+ break;
+ }
+ case SYNCHRONIZE_CACHE:
+ {
+ int immed = cdb[1] & 0x2;
+ TRACE(TRACE_ORDER, "SYNCHRONIZE_CACHE: "
+ "loff=%lld, data_len=%lld, immed=%d",
+ (long long unsigned int)loff,
+ (long long unsigned int)data_len, immed);
+ if (immed) {
+ scst_cmd_get(cmd); /* to protect dev */
+ cmd->completed = 1;
+ cmd->scst_cmd_done(cmd, SCST_CMD_STATE_DEFAULT,
+ SCST_CONTEXT_SAME);
+ vdisk_fsync(thr, loff, data_len, NULL, dev);
+ /* ToDo: vdisk_fsync() error processing */
+ scst_cmd_put(cmd);
+ goto out_thr;
+ } else {
+ vdisk_fsync(thr, loff, data_len, cmd, dev);
+ break;
+ }
+ }
+ case VERIFY_6:
+ case VERIFY:
+ case VERIFY_12:
+ case VERIFY_16:
+ vdisk_exec_verify(cmd, thr, loff);
+ break;
+ case MODE_SENSE:
+ case MODE_SENSE_10:
+ vdisk_exec_mode_sense(cmd);
+ break;
+ case MODE_SELECT:
+ case MODE_SELECT_10:
+ vdisk_exec_mode_select(cmd);
+ break;
+ case LOG_SELECT:
+ case LOG_SENSE:
+ vdisk_exec_log(cmd);
+ break;
+ case ALLOW_MEDIUM_REMOVAL:
+ vdisk_exec_prevent_allow_medium_removal(cmd);
+ break;
+ case READ_TOC:
+ vdisk_exec_read_toc(cmd);
+ break;
+ case START_STOP:
+ vdisk_fsync(thr, 0, virt_dev->file_size, cmd, dev);
+ break;
+ case RESERVE:
+ case RESERVE_10:
+ case RELEASE:
+ case RELEASE_10:
+ case TEST_UNIT_READY:
+ break;
+ case INQUIRY:
+ vdisk_exec_inquiry(cmd);
+ break;
+ case REQUEST_SENSE:
+ vdisk_exec_request_sense(cmd);
+ break;
+ case READ_CAPACITY:
+ vdisk_exec_read_capacity(cmd);
+ break;
+ case SERVICE_ACTION_IN:
+ if ((cmd->cdb[1] & 0x1f) == SAI_READ_CAPACITY_16) {
+ vdisk_exec_read_capacity16(cmd);
+ break;
+ }
+ /* else go through */
+ case REPORT_LUNS:
+ default:
+ TRACE_DBG("Invalid opcode %d", opcode);
+ scst_set_cmd_error(cmd,
+ SCST_LOAD_SENSE(scst_sense_invalid_opcode));
+ }
+
+out_compl:
+ cmd->completed = 1;
+
+out_done:
+ cmd->scst_cmd_done(cmd, SCST_CMD_STATE_DEFAULT, SCST_CONTEXT_SAME);
+
+out_thr:
+ if (likely(thr != NULL))
+ scst_thr_data_put(&thr->hdr);
+
+ res = SCST_EXEC_COMPLETED;
+ return res;
+}
+
+static int vdisk_get_block_shift(struct scst_cmd *cmd)
+{
+ struct scst_vdisk_dev *virt_dev =
+ (struct scst_vdisk_dev *)cmd->dev->dh_priv;
+ return virt_dev->block_shift;
+}
+
+static int vdisk_parse(struct scst_cmd *cmd)
+{
+ scst_sbc_generic_parse(cmd, vdisk_get_block_shift);
+ return SCST_CMD_STATE_DEFAULT;
+}
+
+static int vcdrom_parse(struct scst_cmd *cmd)
+{
+ scst_cdrom_generic_parse(cmd, vdisk_get_block_shift);
+ return SCST_CMD_STATE_DEFAULT;
+}
+
+static int vcdrom_exec(struct scst_cmd *cmd)
+{
+ int res = SCST_EXEC_COMPLETED;
+ int opcode = cmd->cdb[0];
+ struct scst_vdisk_dev *virt_dev =
+ (struct scst_vdisk_dev *)cmd->dev->dh_priv;
+
+ cmd->status = 0;
+ cmd->msg_status = 0;
+ cmd->host_status = DID_OK;
+ cmd->driver_status = 0;
+
+ if (virt_dev->cdrom_empty && (opcode != INQUIRY)) {
+ TRACE_DBG("%s", "CDROM empty");
+ scst_set_cmd_error(cmd,
+ SCST_LOAD_SENSE(scst_sense_not_ready));
+ goto out_done;
+ }
+
+ if (virt_dev->media_changed && scst_is_ua_command(cmd)) {
+ spin_lock(&virt_dev->flags_lock);
+ if (virt_dev->media_changed) {
+ virt_dev->media_changed = 0;
+ TRACE_DBG("%s", "Reporting media changed");
+ scst_set_cmd_error(cmd,
+ SCST_LOAD_SENSE(scst_sense_medium_changed_UA));
+ spin_unlock(&virt_dev->flags_lock);
+ goto out_done;
+ }
+ spin_unlock(&virt_dev->flags_lock);
+ }
+
+ res = vdisk_do_job(cmd);
+
+out:
+ return res;
+
+out_done:
+ cmd->scst_cmd_done(cmd, SCST_CMD_STATE_DEFAULT, SCST_CONTEXT_SAME);
+ goto out;
+}
+
+static uint64_t vdisk_gen_dev_id_num(const char *virt_dev_name)
+{
+ unsigned int dev_id_num, i;
+
+ for (dev_id_num = 0, i = 0; i < strlen(virt_dev_name); i++) {
+ unsigned int rv = random_values[(int)(virt_dev_name[i])];
+ /* Do some rotating of the bits */
+ dev_id_num ^= ((rv << i) | (rv >> (32 - i)));
+ }
+
+ return ((uint64_t)scst_get_setup_id() << 32) | dev_id_num;
+}
+
+static void vdisk_exec_inquiry(struct scst_cmd *cmd)
+{
+ int32_t length, i, resp_len = 0;
+ uint8_t *address;
+ uint8_t *buf;
+ struct scst_vdisk_dev *virt_dev =
+ (struct scst_vdisk_dev *)cmd->dev->dh_priv;
+
+ /* ToDo: Performance Boost:
+ * 1. remove kzalloc, buf
+ * 2. do all checks before touching *address
+ * 3. zero *address
+ * 4. write directly to *address
+ */
+
+ buf = kzalloc(INQ_BUF_SZ,
+ scst_cmd_atomic(cmd) ? GFP_ATOMIC : GFP_KERNEL);
+ if (buf == NULL) {
+ scst_set_busy(cmd);
+ goto out;
+ }
+
+ length = scst_get_buf_first(cmd, &address);
+ TRACE_DBG("length %d", length);
+ if (unlikely(length <= 0)) {
+ if (length < 0) {
+ PRINT_ERROR("scst_get_buf_first() failed: %d", length);
+ scst_set_cmd_error(cmd,
+ SCST_LOAD_SENSE(scst_sense_hardw_error));
+ }
+ goto out_free;
+ }
+
+ if (cmd->cdb[1] & CMDDT) {
+ TRACE_DBG("%s", "INQUIRY: CMDDT is unsupported");
+ scst_set_cmd_error(cmd,
+ SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb));
+ goto out_put;
+ }
+
+ buf[0] = cmd->dev->type; /* type dev */
+ if (virt_dev->removable)
+ buf[1] = 0x80; /* removable */
+ /* Vital Product */
+ if (cmd->cdb[1] & EVPD) {
+ if (0 == cmd->cdb[2]) {
+ /* supported vital product data pages */
+ buf[3] = 3;
+ buf[4] = 0x0; /* this page */
+ buf[5] = 0x80; /* unit serial number */
+ buf[6] = 0x83; /* device identification */
+ resp_len = buf[3] + 4;
+ } else if (0x80 == cmd->cdb[2]) {
+ /* unit serial number */
+ int usn_len = strlen(virt_dev->usn);
+ buf[1] = 0x80;
+ buf[3] = usn_len;
+ strncpy(&buf[4], virt_dev->usn, usn_len);
+ resp_len = buf[3] + 4;
+ } else if (0x83 == cmd->cdb[2]) {
+ /* device identification */
+ int num = 4;
+
+ buf[1] = 0x83;
+ /* T10 vendor identifier field format (faked) */
+ buf[num + 0] = 0x2; /* ASCII */
+ buf[num + 1] = 0x1; /* Vendor ID */
+ if (virt_dev->blockio)
+ memcpy(&buf[num + 4], SCST_BIO_VENDOR, 8);
+ else
+ memcpy(&buf[num + 4], SCST_FIO_VENDOR, 8);
+
+ read_lock_bh(&vdisk_t10_dev_id_rwlock);
+ i = strlen(virt_dev->t10_dev_id);
+ memcpy(&buf[num + 12], virt_dev->t10_dev_id, i);
+ read_unlock_bh(&vdisk_t10_dev_id_rwlock);
+
+ buf[num + 3] = 8 + i;
+ num += buf[num + 3];
+
+ num += 4;
+
+ /* Binary */
+ buf[num + 0] = 0x01;
+
+ /* EUI-64 */
+ buf[num + 1] = 0x02;
+ buf[num + 2] = 0x00;
+ buf[num + 3] = 0x08;
+
+ /* IEEE id */
+ buf[num + 4] = virt_dev->t10_dev_id[0];
+ buf[num + 5] = virt_dev->t10_dev_id[1];
+ buf[num + 6] = virt_dev->t10_dev_id[2];
+
+ /* IEEE ext id */
+ buf[num + 7] = virt_dev->t10_dev_id[3];
+ buf[num + 8] = virt_dev->t10_dev_id[4];
+ buf[num + 9] = virt_dev->t10_dev_id[5];
+ buf[num + 10] = virt_dev->t10_dev_id[6];
+ buf[num + 11] = virt_dev->t10_dev_id[7];
+ num += buf[num + 3];
+
+ resp_len = num;
+ buf[2] = (resp_len >> 8) & 0xFF;
+ buf[3] = resp_len & 0xFF;
+ resp_len += 4;
+ } else {
+ TRACE_DBG("INQUIRY: Unsupported EVPD page %x",
+ cmd->cdb[2]);
+ scst_set_cmd_error(cmd,
+ SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb));
+ goto out_put;
+ }
+ } else {
+ int len;
+
+ if (cmd->cdb[2] != 0) {
+ TRACE_DBG("INQUIRY: Unsupported page %x", cmd->cdb[2]);
+ scst_set_cmd_error(cmd,
+ SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb));
+ goto out_put;
+ }
+
+ buf[2] = 5; /* Device complies to SPC-3 */
+ buf[3] = 0x12; /* HiSup + data in format specified in SPC */
+ buf[4] = 31;/* n - 4 = 35 - 4 = 31 for full 36 byte data */
+ buf[6] = 1; /* MultiP 1 */
+ buf[7] = 2; /* CMDQUE 1, BQue 0 => commands queuing supported */
+
+ /*
+ * 8 byte ASCII Vendor Identification of the target
+ * - left aligned.
+ */
+ if (virt_dev->blockio)
+ memcpy(&buf[8], SCST_BIO_VENDOR, 8);
+ else
+ memcpy(&buf[8], SCST_FIO_VENDOR, 8);
+
+ /*
+ * 16 byte ASCII Product Identification of the target - left
+ * aligned.
+ */
+ memset(&buf[16], ' ', 16);
+ len = min(strlen(virt_dev->name), (size_t)16);
+ memcpy(&buf[16], virt_dev->name, len);
+
+ /*
+ * 4 byte ASCII Product Revision Level of the target - left
+ * aligned.
+ */
+ memcpy(&buf[32], SCST_FIO_REV, 4);
+ resp_len = buf[4] + 5;
+ }
+
+ BUG_ON(resp_len >= INQ_BUF_SZ);
+ if (length > resp_len)
+ length = resp_len;
+ memcpy(address, buf, length);
+
+out_put:
+ scst_put_buf(cmd, address);
+ if (length < cmd->resp_data_len)
+ scst_set_resp_data_len(cmd, length);
+
+out_free:
+ kfree(buf);
+
+out:
+ return;
+}
+
+static void vdisk_exec_request_sense(struct scst_cmd *cmd)
+{
+ int32_t length, sl;
+ uint8_t *address;
+ uint8_t b[SCST_STANDARD_SENSE_LEN];
+
+ sl = scst_set_sense(b, sizeof(b), cmd->dev->d_sense,
+ SCST_LOAD_SENSE(scst_sense_no_sense));
+
+ length = scst_get_buf_first(cmd, &address);
+ TRACE_DBG("length %d", length);
+ if (length < 0) {
+ PRINT_ERROR("scst_get_buf_first() failed: %d)", length);
+ scst_set_cmd_error(cmd,
+ SCST_LOAD_SENSE(scst_sense_hardw_error));
+ goto out;
+ }
+
+ length = min(sl, length);
+ memcpy(address, b, length);
+ scst_set_resp_data_len(cmd, length);
+
+ scst_put_buf(cmd, address);
+
+out:
+ return;
+}
+
+/*
+ * <<Following mode pages info copied from ST318451LW with some corrections>>
+ *
+ * ToDo: revise them
+ */
+static int vdisk_err_recov_pg(unsigned char *p, int pcontrol,
+ struct scst_vdisk_dev *virt_dev)
+{ /* Read-Write Error Recovery page for mode_sense */
+ const unsigned char err_recov_pg[] = {0x1, 0xa, 0xc0, 11, 240, 0, 0, 0,
+ 5, 0, 0xff, 0xff};
+
+ memcpy(p, err_recov_pg, sizeof(err_recov_pg));
+ if (1 == pcontrol)
+ memset(p + 2, 0, sizeof(err_recov_pg) - 2);
+ return sizeof(err_recov_pg);
+}
+
+static int vdisk_disconnect_pg(unsigned char *p, int pcontrol,
+ struct scst_vdisk_dev *virt_dev)
+{ /* Disconnect-Reconnect page for mode_sense */
+ const unsigned char disconnect_pg[] = {0x2, 0xe, 128, 128, 0, 10, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0};
+
+ memcpy(p, disconnect_pg, sizeof(disconnect_pg));
+ if (1 == pcontrol)
+ memset(p + 2, 0, sizeof(disconnect_pg) - 2);
+ return sizeof(disconnect_pg);
+}
+
+static int vdisk_rigid_geo_pg(unsigned char *p, int pcontrol,
+ struct scst_vdisk_dev *virt_dev)
+{
+ unsigned char geo_m_pg[] = {0x04, 0x16, 0, 0, 0, DEF_HEADS, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0x3a, 0x98/* 15K RPM */, 0, 0};
+ int32_t ncyl, n, rem;
+ uint64_t dividend;
+
+ memcpy(p, geo_m_pg, sizeof(geo_m_pg));
+ /*
+ * Divide virt_dev->nblocks by (DEF_HEADS * DEF_SECTORS) and store
+ * the quotient in ncyl and the remainder in rem.
+ */
+ dividend = virt_dev->nblocks;
+ rem = do_div(dividend, DEF_HEADS * DEF_SECTORS);
+ ncyl = dividend;
+ if (rem != 0)
+ ncyl++;
+ memcpy(&n, p + 2, sizeof(u32));
+ n = n | (cpu_to_be32(ncyl) >> 8);
+ memcpy(p + 2, &n, sizeof(u32));
+ if (1 == pcontrol)
+ memset(p + 2, 0, sizeof(geo_m_pg) - 2);
+ return sizeof(geo_m_pg);
+}
+
+static int vdisk_format_pg(unsigned char *p, int pcontrol,
+ struct scst_vdisk_dev *virt_dev)
+{ /* Format device page for mode_sense */
+ const unsigned char format_pg[] = {0x3, 0x16, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0x40, 0, 0, 0};
+
+ memcpy(p, format_pg, sizeof(format_pg));
+ p[10] = (DEF_SECTORS >> 8) & 0xff;
+ p[11] = DEF_SECTORS & 0xff;
+ p[12] = (virt_dev->block_size >> 8) & 0xff;
+ p[13] = virt_dev->block_size & 0xff;
+ if (1 == pcontrol)
+ memset(p + 2, 0, sizeof(format_pg) - 2);
+ return sizeof(format_pg);
+}
+
+static int vdisk_caching_pg(unsigned char *p, int pcontrol,
+ struct scst_vdisk_dev *virt_dev)
+{ /* Caching page for mode_sense */
+ const unsigned char caching_pg[] = {0x8, 18, 0x10, 0, 0xff, 0xff, 0, 0,
+ 0xff, 0xff, 0xff, 0xff, 0x80, 0x14, 0, 0, 0, 0, 0, 0};
+
+ memcpy(p, caching_pg, sizeof(caching_pg));
+ p[2] |= !(virt_dev->wt_flag || virt_dev->nv_cache) ? WCE : 0;
+ if (1 == pcontrol)
+ memset(p + 2, 0, sizeof(caching_pg) - 2);
+ return sizeof(caching_pg);
+}
+
+static int vdisk_ctrl_m_pg(unsigned char *p, int pcontrol,
+ struct scst_vdisk_dev *virt_dev)
+{ /* Control mode page for mode_sense */
+ const unsigned char ctrl_m_pg[] = {0xa, 0xa, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0x2, 0x4b};
+
+ memcpy(p, ctrl_m_pg, sizeof(ctrl_m_pg));
+ switch (pcontrol) {
+ case 0:
+ p[2] |= virt_dev->dev->tst << 5;
+ p[2] |= virt_dev->dev->d_sense << 2;
+ p[3] |= virt_dev->dev->queue_alg << 4;
+ p[4] |= virt_dev->dev->swp << 3;
+ p[5] |= virt_dev->dev->tas << 6;
+ break;
+ case 1:
+ memset(p + 2, 0, sizeof(ctrl_m_pg) - 2);
+#if 0 /*
+ * It's too early to implement it, since we can't control the
+ * backstorage device parameters. ToDo
+ */
+ p[2] |= 7 << 5; /* TST */
+ p[3] |= 0xF << 4; /* QUEUE ALGORITHM MODIFIER */
+#endif
+ p[2] |= 1 << 2; /* D_SENSE */
+ p[4] |= 1 << 3; /* SWP */
+ p[5] |= 1 << 6; /* TAS */
+ break;
+ case 2:
+ p[2] |= DEF_TST << 5;
+ p[2] |= DEF_DSENSE << 2;
+ if (virt_dev->wt_flag || virt_dev->nv_cache)
+ p[3] |= DEF_QUEUE_ALG_WT << 4;
+ else
+ p[3] |= DEF_QUEUE_ALG << 4;
+ p[4] |= DEF_SWP << 3;
+ p[5] |= DEF_TAS << 6;
+ break;
+ default:
+ BUG();
+ }
+ return sizeof(ctrl_m_pg);
+}
+
+static int vdisk_iec_m_pg(unsigned char *p, int pcontrol,
+ struct scst_vdisk_dev *virt_dev)
+{ /* Informational Exceptions control mode page for mode_sense */
+ const unsigned char iec_m_pg[] = {0x1c, 0xa, 0x08, 0, 0, 0, 0, 0,
+ 0, 0, 0x0, 0x0};
+ memcpy(p, iec_m_pg, sizeof(iec_m_pg));
+ if (1 == pcontrol)
+ memset(p + 2, 0, sizeof(iec_m_pg) - 2);
+ return sizeof(iec_m_pg);
+}
+
+static void vdisk_exec_mode_sense(struct scst_cmd *cmd)
+{
+ int32_t length;
+ uint8_t *address;
+ uint8_t *buf;
+ struct scst_vdisk_dev *virt_dev;
+ uint32_t blocksize;
+ uint64_t nblocks;
+ unsigned char dbd, type;
+ int pcontrol, pcode, subpcode;
+ unsigned char dev_spec;
+ int msense_6, offset = 0, len;
+ unsigned char *bp;
+
+ buf = kzalloc(MSENSE_BUF_SZ,
+ scst_cmd_atomic(cmd) ? GFP_ATOMIC : GFP_KERNEL);
+ if (buf == NULL) {
+ scst_set_busy(cmd);
+ goto out;
+ }
+
+ virt_dev = (struct scst_vdisk_dev *)cmd->dev->dh_priv;
+ blocksize = virt_dev->block_size;
+ nblocks = virt_dev->nblocks;
+
+ type = cmd->dev->type; /* type dev */
+ dbd = cmd->cdb[1] & DBD;
+ pcontrol = (cmd->cdb[2] & 0xc0) >> 6;
+ pcode = cmd->cdb[2] & 0x3f;
+ subpcode = cmd->cdb[3];
+ msense_6 = (MODE_SENSE == cmd->cdb[0]);
+ dev_spec = ((virt_dev->dev->rd_only ||
+ cmd->tgt_dev->acg_dev->rd_only) ? WP : 0) | DPOFUA;
+
+ length = scst_get_buf_first(cmd, &address);
+ if (unlikely(length <= 0)) {
+ if (length < 0) {
+ PRINT_ERROR("scst_get_buf_first() failed: %d", length);
+ scst_set_cmd_error(cmd,
+ SCST_LOAD_SENSE(scst_sense_hardw_error));
+ }
+ goto out_free;
+ }
+
+ if (0x3 == pcontrol) {
+ TRACE_DBG("%s", "MODE SENSE: Saving values not supported");
+ scst_set_cmd_error(cmd,
+ SCST_LOAD_SENSE(scst_sense_saving_params_unsup));
+ goto out_put;
+ }
+
+ if (msense_6) {
+ buf[1] = type;
+ buf[2] = dev_spec;
+ offset = 4;
+ } else {
+ buf[2] = type;
+ buf[3] = dev_spec;
+ offset = 8;
+ }
+
+ if (0 != subpcode) {
+ /* TODO: Control Extension page */
+ TRACE_DBG("%s", "MODE SENSE: Only subpage 0 is supported");
+ scst_set_cmd_error(cmd,
+ SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb));
+ goto out_put;
+ }
+
+ if (!dbd) {
+ /* Create block descriptor */
+ buf[offset - 1] = 0x08; /* block descriptor length */
+ if (nblocks >> 32) {
+ buf[offset + 0] = 0xFF;
+ buf[offset + 1] = 0xFF;
+ buf[offset + 2] = 0xFF;
+ buf[offset + 3] = 0xFF;
+ } else {
+ /* num blks */
+ buf[offset + 0] = (nblocks >> (BYTE * 3)) & 0xFF;
+ buf[offset + 1] = (nblocks >> (BYTE * 2)) & 0xFF;
+ buf[offset + 2] = (nblocks >> (BYTE * 1)) & 0xFF;
+ buf[offset + 3] = (nblocks >> (BYTE * 0)) & 0xFF;
+ }
+ buf[offset + 4] = 0; /* density code */
+ buf[offset + 5] = (blocksize >> (BYTE * 2)) & 0xFF;/* blklen */
+ buf[offset + 6] = (blocksize >> (BYTE * 1)) & 0xFF;
+ buf[offset + 7] = (blocksize >> (BYTE * 0)) & 0xFF;
+
+ offset += 8; /* increment offset */
+ }
+
+ bp = buf + offset;
+
+ switch (pcode) {
+ case 0x1: /* Read-Write error recovery page, direct access */
+ len = vdisk_err_recov_pg(bp, pcontrol, virt_dev);
+ break;
+ case 0x2: /* Disconnect-Reconnect page, all devices */
+ len = vdisk_disconnect_pg(bp, pcontrol, virt_dev);
+ break;
+ case 0x3: /* Format device page, direct access */
+ len = vdisk_format_pg(bp, pcontrol, virt_dev);
+ break;
+ case 0x4: /* Rigid disk geometry */
+ len = vdisk_rigid_geo_pg(bp, pcontrol, virt_dev);
+ break;
+ case 0x8: /* Caching page, direct access */
+ len = vdisk_caching_pg(bp, pcontrol, virt_dev);
+ break;
+ case 0xa: /* Control Mode page, all devices */
+ len = vdisk_ctrl_m_pg(bp, pcontrol, virt_dev);
+ break;
+ case 0x1c: /* Informational Exceptions Mode page, all devices */
+ len = vdisk_iec_m_pg(bp, pcontrol, virt_dev);
+ break;
+ case 0x3f: /* Read all Mode pages */
+ len = vdisk_err_recov_pg(bp, pcontrol, virt_dev);
+ len += vdisk_disconnect_pg(bp + len, pcontrol, virt_dev);
+ len += vdisk_format_pg(bp + len, pcontrol, virt_dev);
+ len += vdisk_caching_pg(bp + len, pcontrol, virt_dev);
+ len += vdisk_ctrl_m_pg(bp + len, pcontrol, virt_dev);
+ len += vdisk_iec_m_pg(bp + len, pcontrol, virt_dev);
+ len += vdisk_rigid_geo_pg(bp + len, pcontrol, virt_dev);
+ break;
+ default:
+ TRACE_DBG("MODE SENSE: Unsupported page %x", pcode);
+ scst_set_cmd_error(cmd,
+ SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb));
+ goto out_put;
+ }
+
+ offset += len;
+
+ if (msense_6)
+ buf[0] = offset - 1;
+ else {
+ buf[0] = ((offset - 2) >> 8) & 0xff;
+ buf[1] = (offset - 2) & 0xff;
+ }
+
+ if (offset > length)
+ offset = length;
+ memcpy(address, buf, offset);
+
+out_put:
+ scst_put_buf(cmd, address);
+ if (offset < cmd->resp_data_len)
+ scst_set_resp_data_len(cmd, offset);
+
+out_free:
+ kfree(buf);
+
+out:
+ return;
+}
+
+static int vdisk_set_wt(struct scst_vdisk_dev *virt_dev, int wt)
+{
+ int res = 0;
+
+ if ((virt_dev->wt_flag == wt) || virt_dev->nullio || virt_dev->nv_cache)
+ goto out;
+
+ spin_lock(&virt_dev->flags_lock);
+ virt_dev->wt_flag = wt;
+ spin_unlock(&virt_dev->flags_lock);
+
+ scst_dev_del_all_thr_data(virt_dev->dev);
+
+out:
+ return res;
+}
+
+static void vdisk_ctrl_m_pg_select(unsigned char *p,
+ struct scst_vdisk_dev *virt_dev)
+{
+ struct scst_device *dev = virt_dev->dev;
+ int old_swp = dev->swp, old_tas = dev->tas, old_dsense = dev->d_sense;
+
+#if 0
+ /* Not implemented yet, see comment in vdisk_ctrl_m_pg() */
+ dev->tst = p[2] >> 5;
+ dev->queue_alg = p[3] >> 4;
+#endif
+ dev->swp = (p[4] & 0x8) >> 3;
+ dev->tas = (p[5] & 0x40) >> 6;
+ dev->d_sense = (p[2] & 0x4) >> 2;
+
+ PRINT_INFO("Device %s: new control mode page parameters: SWP %x "
+ "(was %x), TAS %x (was %x), D_SENSE %d (was %d)",
+ virt_dev->name, dev->swp, old_swp, dev->tas, old_tas,
+ dev->d_sense, old_dsense);
+ return;
+}
+
+static void vdisk_exec_mode_select(struct scst_cmd *cmd)
+{
+ int32_t length;
+ uint8_t *address;
+ struct scst_vdisk_dev *virt_dev;
+ int mselect_6, offset;
+
+ virt_dev = (struct scst_vdisk_dev *)cmd->dev->dh_priv;
+ mselect_6 = (MODE_SELECT == cmd->cdb[0]);
+
+ length = scst_get_buf_first(cmd, &address);
+ if (unlikely(length <= 0)) {
+ if (length < 0) {
+ PRINT_ERROR("scst_get_buf_first() failed: %d", length);
+ scst_set_cmd_error(cmd,
+ SCST_LOAD_SENSE(scst_sense_hardw_error));
+ }
+ goto out;
+ }
+
+ if (!(cmd->cdb[1] & PF) || (cmd->cdb[1] & SP)) {
+ TRACE(TRACE_MINOR|TRACE_SCSI, "MODE SELECT: Unsupported "
+ "value(s) of PF and/or SP bits (cdb[1]=%x)",
+ cmd->cdb[1]);
+ scst_set_cmd_error(cmd,
+ SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb));
+ goto out_put;
+ }
+
+ if (mselect_6)
+ offset = 4;
+ else
+ offset = 8;
+
+ if (address[offset - 1] == 8) {
+ offset += 8;
+ } else if (address[offset - 1] != 0) {
+ PRINT_ERROR("%s", "MODE SELECT: Wrong parameters list "
+ "lenght");
+ scst_set_cmd_error(cmd,
+ SCST_LOAD_SENSE(scst_sense_invalid_field_in_parm_list));
+ goto out_put;
+ }
+
+ while (length > offset + 2) {
+ if (address[offset] & PS) {
+ PRINT_ERROR("%s", "MODE SELECT: Illegal PS bit");
+ scst_set_cmd_error(cmd, SCST_LOAD_SENSE(
+ scst_sense_invalid_field_in_parm_list));
+ goto out_put;
+ }
+ if ((address[offset] & 0x3f) == 0x8) {
+ /* Caching page */
+ if (address[offset + 1] != 18) {
+ PRINT_ERROR("%s", "MODE SELECT: Invalid "
+ "caching page request");
+ scst_set_cmd_error(cmd, SCST_LOAD_SENSE(
+ scst_sense_invalid_field_in_parm_list));
+ goto out_put;
+ }
+ if (vdisk_set_wt(virt_dev,
+ (address[offset + 2] & WCE) ? 0 : 1) != 0) {
+ scst_set_cmd_error(cmd,
+ SCST_LOAD_SENSE(scst_sense_hardw_error));
+ goto out_put;
+ }
+ break;
+ } else if ((address[offset] & 0x3f) == 0xA) {
+ /* Control page */
+ if (address[offset + 1] != 0xA) {
+ PRINT_ERROR("%s", "MODE SELECT: Invalid "
+ "control page request");
+ scst_set_cmd_error(cmd, SCST_LOAD_SENSE(
+ scst_sense_invalid_field_in_parm_list));
+ goto out_put;
+ }
+ vdisk_ctrl_m_pg_select(&address[offset], virt_dev);
+ } else {
+ PRINT_ERROR("MODE SELECT: Invalid request %x",
+ address[offset] & 0x3f);
+ scst_set_cmd_error(cmd, SCST_LOAD_SENSE(
+ scst_sense_invalid_field_in_parm_list));
+ goto out_put;
+ }
+ offset += address[offset + 1];
+ }
+
+out_put:
+ scst_put_buf(cmd, address);
+
+out:
+ return;
+}
+
+static void vdisk_exec_log(struct scst_cmd *cmd)
+{
+
+ /* No log pages are supported */
+ scst_set_cmd_error(cmd,
+ SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb));
+ return;
+}
+
+static void vdisk_exec_read_capacity(struct scst_cmd *cmd)
+{
+ int32_t length;
+ uint8_t *address;
+ struct scst_vdisk_dev *virt_dev;
+ uint32_t blocksize;
+ uint64_t nblocks;
+ uint8_t buffer[READ_CAP_LEN];
+
+ virt_dev = (struct scst_vdisk_dev *)cmd->dev->dh_priv;
+ blocksize = virt_dev->block_size;
+ nblocks = virt_dev->nblocks;
+
+ /* last block on the virt_dev is (nblocks-1) */
+ memset(buffer, 0, sizeof(buffer));
+ if (nblocks >> 32) {
+ buffer[0] = 0xFF;
+ buffer[1] = 0xFF;
+ buffer[2] = 0xFF;
+ buffer[3] = 0xFF;
+ } else {
+ buffer[0] = ((nblocks - 1) >> (BYTE * 3)) & 0xFF;
+ buffer[1] = ((nblocks - 1) >> (BYTE * 2)) & 0xFF;
+ buffer[2] = ((nblocks - 1) >> (BYTE * 1)) & 0xFF;
+ buffer[3] = ((nblocks - 1) >> (BYTE * 0)) & 0xFF;
+ }
+ buffer[4] = (blocksize >> (BYTE * 3)) & 0xFF;
+ buffer[5] = (blocksize >> (BYTE * 2)) & 0xFF;
+ buffer[6] = (blocksize >> (BYTE * 1)) & 0xFF;
+ buffer[7] = (blocksize >> (BYTE * 0)) & 0xFF;
+
+ length = scst_get_buf_first(cmd, &address);
+ if (unlikely(length <= 0)) {
+ if (length < 0) {
+ PRINT_ERROR("scst_get_buf_first() failed: %d", length);
+ scst_set_cmd_error(cmd,
+ SCST_LOAD_SENSE(scst_sense_hardw_error));
+ }
+ goto out;
+ }
+
+ if (length > READ_CAP_LEN)
+ length = READ_CAP_LEN;
+ memcpy(address, buffer, length);
+
+ scst_put_buf(cmd, address);
+
+ if (length < cmd->resp_data_len)
+ scst_set_resp_data_len(cmd, length);
+
+out:
+ return;
+}
+
+static void vdisk_exec_read_capacity16(struct scst_cmd *cmd)
+{
+ int32_t length;
+ uint8_t *address;
+ struct scst_vdisk_dev *virt_dev;
+ uint32_t blocksize;
+ uint64_t nblocks;
+ uint8_t buffer[READ_CAP16_LEN];
+
+ virt_dev = (struct scst_vdisk_dev *)cmd->dev->dh_priv;
+ blocksize = virt_dev->block_size;
+ nblocks = virt_dev->nblocks - 1;
+
+ memset(buffer, 0, sizeof(buffer));
+
+ buffer[0] = nblocks >> 56;
+ buffer[1] = (nblocks >> 48) & 0xFF;
+ buffer[2] = (nblocks >> 40) & 0xFF;
+ buffer[3] = (nblocks >> 32) & 0xFF;
+ buffer[4] = (nblocks >> 24) & 0xFF;
+ buffer[5] = (nblocks >> 16) & 0xFF;
+ buffer[6] = (nblocks >> 8) & 0xFF;
+ buffer[7] = nblocks & 0xFF;
+
+ buffer[8] = (blocksize >> (BYTE * 3)) & 0xFF;
+ buffer[9] = (blocksize >> (BYTE * 2)) & 0xFF;
+ buffer[10] = (blocksize >> (BYTE * 1)) & 0xFF;
+ buffer[11] = (blocksize >> (BYTE * 0)) & 0xFF;
+
+ switch (blocksize) {
+ case 512:
+ buffer[13] = 3;
+ break;
+ case 1024:
+ buffer[13] = 2;
+ break;
+ case 2048:
+ buffer[13] = 1;
+ break;
+ default:
+ PRINT_ERROR("%s: Unexpected block size %d",
+ cmd->op_name, blocksize);
+ /* go through */
+ case 4096:
+ buffer[13] = 0;
+ break;
+ }
+
+ length = scst_get_buf_first(cmd, &address);
+ if (unlikely(length <= 0)) {
+ if (length < 0) {
+ PRINT_ERROR("scst_get_buf_first() failed: %d", length);
+ scst_set_cmd_error(cmd,
+ SCST_LOAD_SENSE(scst_sense_hardw_error));
+ }
+ goto out;
+ }
+
+ /*
+ * Some versions of Windows have a bug, which makes them consider
+ * response of READ CAPACITY(16) longer than 12 bytes as a faulty one.
+ * As the result, such Windows'es refuse to see SCST exported
+ * devices >2TB in size. This is fixed by MS in latter Windows
+ * versions, probably, by some hotfix.
+ *
+ * But if you're using such buggy Windows and experience this problem,
+ * change this '1' to '0'.
+ */
+#if 0 /* there are too many such hosts */
+ if (length > READ_CAP16_LEN)
+ length = READ_CAP16_LEN;
+#else
+ if (length > 12)
+ length = 12;
+#endif
+ memcpy(address, buffer, length);
+
+ scst_put_buf(cmd, address);
+
+ if (length < cmd->resp_data_len)
+ scst_set_resp_data_len(cmd, length);
+
+out:
+ return;
+}
+
+static void vdisk_exec_read_toc(struct scst_cmd *cmd)
+{
+ int32_t length, off = 0;
+ uint8_t *address;
+ struct scst_vdisk_dev *virt_dev;
+ uint32_t nblocks;
+ uint8_t buffer[4+8+8] = { 0x00, 0x0a, 0x01, 0x01, 0x00, 0x14,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+ if (cmd->dev->type != TYPE_ROM) {
+ PRINT_ERROR("%s", "READ TOC for non-CDROM device");
+ scst_set_cmd_error(cmd,
+ SCST_LOAD_SENSE(scst_sense_invalid_opcode));
+ goto out;
+ }
+
+ if (cmd->cdb[2] & 0x0e/*Format*/) {
+ PRINT_ERROR("%s", "READ TOC: invalid requested data format");
+ scst_set_cmd_error(cmd,
+ SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb));
+ goto out;
+ }
+
+ if ((cmd->cdb[6] != 0 && (cmd->cdb[2] & 0x01)) ||
+ (cmd->cdb[6] > 1 && cmd->cdb[6] != 0xAA)) {
+ PRINT_ERROR("READ TOC: invalid requested track number %x",
+ cmd->cdb[6]);
+ scst_set_cmd_error(cmd,
+ SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb));
+ goto out;
+ }
+
+ length = scst_get_buf_first(cmd, &address);
+ if (unlikely(length <= 0)) {
+ if (length < 0) {
+ PRINT_ERROR("scst_get_buf_first() failed: %d", length);
+ scst_set_cmd_error(cmd,
+ SCST_LOAD_SENSE(scst_sense_hardw_error));
+ }
+ goto out;
+ }
+
+ virt_dev = (struct scst_vdisk_dev *)cmd->dev->dh_priv;
+ /* ToDo when you have > 8TB ROM device. */
+ nblocks = (uint32_t)virt_dev->nblocks;
+
+ /* Header */
+ memset(buffer, 0, sizeof(buffer));
+ buffer[2] = 0x01; /* First Track/Session */
+ buffer[3] = 0x01; /* Last Track/Session */
+ off = 4;
+ if (cmd->cdb[6] <= 1) {
+ /* Fistr TOC Track Descriptor */
+ /* ADDR 0x10 - Q Sub-channel encodes current position data
+ CONTROL 0x04 - Data track, recoreded uninterrupted */
+ buffer[off+1] = 0x14;
+ /* Track Number */
+ buffer[off+2] = 0x01;
+ off += 8;
+ }
+ if (!(cmd->cdb[2] & 0x01)) {
+ /* Lead-out area TOC Track Descriptor */
+ buffer[off+1] = 0x14;
+ /* Track Number */
+ buffer[off+2] = 0xAA;
+ /* Track Start Address */
+ buffer[off+4] = (nblocks >> (BYTE * 3)) & 0xFF;
+ buffer[off+5] = (nblocks >> (BYTE * 2)) & 0xFF;
+ buffer[off+6] = (nblocks >> (BYTE * 1)) & 0xFF;
+ buffer[off+7] = (nblocks >> (BYTE * 0)) & 0xFF;
+ off += 8;
+ }
+
+ buffer[1] = off - 2; /* Data Length */
+
+ if (off > length)
+ off = length;
+ memcpy(address, buffer, off);
+
+ scst_put_buf(cmd, address);
+
+ if (off < cmd->resp_data_len)
+ scst_set_resp_data_len(cmd, off);
+
+out:
+ return;
+}
+
+static void vdisk_exec_prevent_allow_medium_removal(struct scst_cmd *cmd)
+{
+ struct scst_vdisk_dev *virt_dev =
+ (struct scst_vdisk_dev *)cmd->dev->dh_priv;
+
+ TRACE_DBG("PERSIST/PREVENT 0x%02x", cmd->cdb[4]);
+
+ if (cmd->dev->type == TYPE_ROM) {
+ spin_lock(&virt_dev->flags_lock);
+ virt_dev->prevent_allow_medium_removal =
+ cmd->cdb[4] & 0x01 ? 1 : 0;
+ spin_unlock(&virt_dev->flags_lock);
+ }
+
+ return;
+}
+
+static int vdisk_fsync(struct scst_vdisk_thr *thr, loff_t loff,
+ loff_t len, struct scst_cmd *cmd, struct scst_device *dev)
+{
+ int res = 0;
+ struct scst_vdisk_dev *virt_dev =
+ (struct scst_vdisk_dev *)dev->dh_priv;
+ struct file *file = thr->fd;
+
+ /* Hopefully, the compiler will generate the single comparison */
+ if (virt_dev->nv_cache || virt_dev->blockio || virt_dev->wt_flag ||
+ virt_dev->o_direct_flag || virt_dev->nullio)
+ goto out;
+
+#if 0 /* For sparse files we might need to sync metadata as well */
+ res = generic_write_sync(file, loff, len);
+#else
+ res = filemap_write_and_wait_range(file->f_mapping, loff, len);
+#endif
+ if (unlikely(res != 0)) {
+ PRINT_ERROR("sync range failed (%d)", res);
+ if (cmd != NULL) {
+ scst_set_cmd_error(cmd,
+ SCST_LOAD_SENSE(scst_sense_write_error));
+ }
+ }
+
+ /* ToDo: flush the device cache, if needed */
+
+out:
+ return res;
+}
+
+static struct iovec *vdisk_alloc_iv(struct scst_cmd *cmd,
+ struct scst_vdisk_thr *thr)
+{
+ int iv_count;
+
+ iv_count = min_t(int, scst_get_buf_count(cmd), UIO_MAXIOV);
+ if (iv_count > thr->iv_count) {
+ kfree(thr->iv);
+ /* It can't be called in atomic context */
+ thr->iv = kmalloc(sizeof(*thr->iv) * iv_count, GFP_KERNEL);
+ if (thr->iv == NULL) {
+ PRINT_ERROR("Unable to allocate iv (%d)", iv_count);
+ scst_set_busy(cmd);
+ goto out;
+ }
+ thr->iv_count = iv_count;
+ }
+
+out:
+ return thr->iv;
+}
+
+static void vdisk_exec_read(struct scst_cmd *cmd,
+ struct scst_vdisk_thr *thr, loff_t loff)
+{
+ mm_segment_t old_fs;
+ loff_t err;
+ ssize_t length, full_len;
+ uint8_t __user *address;
+ struct scst_vdisk_dev *virt_dev =
+ (struct scst_vdisk_dev *)cmd->dev->dh_priv;
+ struct file *fd = thr->fd;
+ struct iovec *iv;
+ int iv_count, i;
+ bool finished = false;
+
+ if (virt_dev->nullio)
+ goto out;
+
+ iv = vdisk_alloc_iv(cmd, thr);
+ if (iv == NULL)
+ goto out;
+
+ length = scst_get_buf_first(cmd, (uint8_t __force **)&address);
+ if (unlikely(length < 0)) {
+ PRINT_ERROR("scst_get_buf_first() failed: %zd", length);
+ scst_set_cmd_error(cmd,
+ SCST_LOAD_SENSE(scst_sense_hardw_error));
+ goto out;
+ }
+
+ old_fs = get_fs();
+ set_fs(get_ds());
+
+ while (1) {
+ iv_count = 0;
+ full_len = 0;
+ i = -1;
+ while (length > 0) {
+ full_len += length;
+ i++;
+ iv_count++;
+ iv[i].iov_base = address;
+ iv[i].iov_len = length;
+ if (iv_count == UIO_MAXIOV)
+ break;
+ length = scst_get_buf_next(cmd,
+ (uint8_t __force **)&address);
+ }
+ if (length == 0) {
+ finished = true;
+ if (unlikely(iv_count == 0))
+ break;
+ } else if (unlikely(length < 0)) {
+ PRINT_ERROR("scst_get_buf_next() failed: %zd", length);
+ scst_set_cmd_error(cmd,
+ SCST_LOAD_SENSE(scst_sense_hardw_error));
+ goto out_set_fs;
+ }
+
+ TRACE_DBG("(iv_count %d, full_len %zd)", iv_count, full_len);
+ /* SEEK */
+ if (fd->f_op->llseek)
+ err = fd->f_op->llseek(fd, loff, 0/*SEEK_SET*/);
+ else
+ err = default_llseek(fd, loff, 0/*SEEK_SET*/);
+ if (err != loff) {
+ PRINT_ERROR("lseek trouble %lld != %lld",
+ (long long unsigned int)err,
+ (long long unsigned int)loff);
+ scst_set_cmd_error(cmd,
+ SCST_LOAD_SENSE(scst_sense_hardw_error));
+ goto out_set_fs;
+ }
+
+ /* READ */
+ err = vfs_readv(fd, (struct iovec __force __user *)iv, iv_count,
+ &fd->f_pos);
+
+ if ((err < 0) || (err < full_len)) {
+ PRINT_ERROR("readv() returned %lld from %zd",
+ (long long unsigned int)err,
+ full_len);
+ if (err == -EAGAIN)
+ scst_set_busy(cmd);
+ else {
+ scst_set_cmd_error(cmd,
+ SCST_LOAD_SENSE(scst_sense_read_error));
+ }
+ goto out_set_fs;
+ }
+
+ for (i = 0; i < iv_count; i++)
+ scst_put_buf(cmd, (void __force *)(iv[i].iov_base));
+
+ if (finished)
+ break;
+
+ loff += full_len;
+ length = scst_get_buf_next(cmd, (uint8_t __force **)&address);
+ };
+
+ set_fs(old_fs);
+
+out:
+ return;
+
+out_set_fs:
+ set_fs(old_fs);
+ for (i = 0; i < iv_count; i++)
+ scst_put_buf(cmd, (void __force *)(iv[i].iov_base));
+ goto out;
+}
+
+static void vdisk_exec_write(struct scst_cmd *cmd,
+ struct scst_vdisk_thr *thr, loff_t loff)
+{
+ mm_segment_t old_fs;
+ loff_t err;
+ ssize_t length, full_len, saved_full_len;
+ uint8_t __user *address;
+ struct scst_vdisk_dev *virt_dev =
+ (struct scst_vdisk_dev *)cmd->dev->dh_priv;
+ struct file *fd = thr->fd;
+ struct iovec *iv, *eiv;
+ int i, iv_count, eiv_count;
+ bool finished = false;
+
+ if (virt_dev->nullio)
+ goto out;
+
+ iv = vdisk_alloc_iv(cmd, thr);
+ if (iv == NULL)
+ goto out;
+
+ length = scst_get_buf_first(cmd, (uint8_t __force **)&address);
+ if (unlikely(length < 0)) {
+ PRINT_ERROR("scst_get_buf_first() failed: %zd", length);
+ scst_set_cmd_error(cmd,
+ SCST_LOAD_SENSE(scst_sense_hardw_error));
+ goto out;
+ }
+
+ old_fs = get_fs();
+ set_fs(get_ds());
+
+ while (1) {
+ iv_count = 0;
+ full_len = 0;
+ i = -1;
+ while (length > 0) {
+ full_len += length;
+ i++;
+ iv_count++;
+ iv[i].iov_base = address;
+ iv[i].iov_len = length;
+ if (iv_count == UIO_MAXIOV)
+ break;
+ length = scst_get_buf_next(cmd,
+ (uint8_t __force **)&address);
+ }
+ if (length == 0) {
+ finished = true;
+ if (unlikely(iv_count == 0))
+ break;
+ } else if (unlikely(length < 0)) {
+ PRINT_ERROR("scst_get_buf_next() failed: %zd", length);
+ scst_set_cmd_error(cmd,
+ SCST_LOAD_SENSE(scst_sense_hardw_error));
+ goto out_set_fs;
+ }
+
+ saved_full_len = full_len;
+ eiv = iv;
+ eiv_count = iv_count;
+restart:
+ TRACE_DBG("writing(eiv_count %d, full_len %zd)", eiv_count, full_len);
+
+ /* SEEK */
+ if (fd->f_op->llseek)
+ err = fd->f_op->llseek(fd, loff, 0 /*SEEK_SET */);
+ else
+ err = default_llseek(fd, loff, 0 /*SEEK_SET */);
+ if (err != loff) {
+ PRINT_ERROR("lseek trouble %lld != %lld",
+ (long long unsigned int)err,
+ (long long unsigned int)loff);
+ scst_set_cmd_error(cmd,
+ SCST_LOAD_SENSE(scst_sense_hardw_error));
+ goto out_set_fs;
+ }
+
+ /* WRITE */
+ err = vfs_writev(fd, (struct iovec __force __user *)eiv, eiv_count,
+ &fd->f_pos);
+
+ if (err < 0) {
+ PRINT_ERROR("write() returned %lld from %zd",
+ (long long unsigned int)err,
+ full_len);
+ if (err == -EAGAIN)
+ scst_set_busy(cmd);
+ else {
+ scst_set_cmd_error(cmd,
+ SCST_LOAD_SENSE(scst_sense_write_error));
+ }
+ goto out_set_fs;
+ } else if (err < full_len) {
+ /*
+ * Probably that's wrong, but sometimes write() returns
+ * value less, than requested. Let's restart.
+ */
+ int i, e = eiv_count;
+ TRACE_MGMT_DBG("write() returned %d from %zd "
+ "(iv_count=%d)", (int)err, full_len,
+ eiv_count);
+ if (err == 0) {
+ PRINT_INFO("Suspicious: write() returned 0 from "
+ "%zd (iv_count=%d)", full_len, eiv_count);
+ }
+ full_len -= err;
+ for (i = 0; i < e; i++) {
+ if ((long long)eiv->iov_len < err) {
+ err -= eiv->iov_len;
+ eiv++;
+ eiv_count--;
+ } else {
+ eiv->iov_base =
+ (uint8_t __force __user *)eiv->iov_base + err;
+ eiv->iov_len -= err;
+ break;
+ }
+ }
+ goto restart;
+ }
+
+ for (i = 0; i < iv_count; i++)
+ scst_put_buf(cmd, (void __force *)(iv[i].iov_base));
+
+ if (finished)
+ break;
+
+ loff += saved_full_len;
+ length = scst_get_buf_next(cmd, (uint8_t __force **)&address);
+ }
+
+ set_fs(old_fs);
+
+out:
+ return;
+
+out_set_fs:
+ set_fs(old_fs);
+ for (i = 0; i < iv_count; i++)
+ scst_put_buf(cmd, (void __force *)(iv[i].iov_base));
+ goto out;
+}
+
+struct scst_blockio_work {
+ atomic_t bios_inflight;
+ struct scst_cmd *cmd;
+};
+
+static inline void blockio_check_finish(struct scst_blockio_work *blockio_work)
+{
+ /* Decrement the bios in processing, and if zero signal completion */
+ if (atomic_dec_and_test(&blockio_work->bios_inflight)) {
+ blockio_work->cmd->completed = 1;
+ blockio_work->cmd->scst_cmd_done(blockio_work->cmd,
+ SCST_CMD_STATE_DEFAULT, scst_estimate_context());
+ kmem_cache_free(blockio_work_cachep, blockio_work);
+ }
+ return;
+}
+
+static void blockio_endio(struct bio *bio, int error)
+{
+ struct scst_blockio_work *blockio_work = bio->bi_private;
+
+ if (unlikely(!test_bit(BIO_UPTODATE, &bio->bi_flags))) {
+ if (error == 0) {
+ PRINT_ERROR("Not up to date bio with error 0 for "
+ "cmd %p, returning -EIO", blockio_work->cmd);
+ error = -EIO;
+ }
+ }
+
+ if (unlikely(error != 0)) {
+ static DEFINE_SPINLOCK(blockio_endio_lock);
+
+ PRINT_ERROR("cmd %p returned error %d", blockio_work->cmd,
+ error);
+
+ /* To protect from several bios finishing simultaneously */
+ spin_lock_bh(&blockio_endio_lock);
+
+ if (bio->bi_rw & WRITE)
+ scst_set_cmd_error(blockio_work->cmd,
+ SCST_LOAD_SENSE(scst_sense_write_error));
+ else
+ scst_set_cmd_error(blockio_work->cmd,
+ SCST_LOAD_SENSE(scst_sense_read_error));
+
+ spin_unlock_bh(&blockio_endio_lock);
+ }
+
+ blockio_check_finish(blockio_work);
+
+ bio_put(bio);
+ return;
+}
+
+static void blockio_exec_rw(struct scst_cmd *cmd, struct scst_vdisk_thr *thr,
+ u64 lba_start, int write)
+{
+ struct scst_vdisk_dev *virt_dev =
+ (struct scst_vdisk_dev *)cmd->dev->dh_priv;
+ struct block_device *bdev = thr->bdev;
+ struct request_queue *q = bdev_get_queue(bdev);
+ int length, max_nr_vecs = 0;
+ uint8_t *address;
+ struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
+ int need_new_bio;
+ struct scst_blockio_work *blockio_work;
+ int bios = 0;
+
+ if (virt_dev->nullio)
+ goto out;
+
+ /* Allocate and initialize blockio_work struct */
+ blockio_work = kmem_cache_alloc(blockio_work_cachep, GFP_KERNEL);
+ if (blockio_work == NULL)
+ goto out_no_mem;
+
+ blockio_work->cmd = cmd;
+
+ if (q)
+ max_nr_vecs = min(bio_get_nr_vecs(bdev), BIO_MAX_PAGES);
+ else
+ max_nr_vecs = 1;
+
+ need_new_bio = 1;
+
+ length = scst_get_buf_first(cmd, &address);
+ while (length > 0) {
+ int len, bytes, off, thislen;
+ uint8_t *addr;
+ u64 lba_start0;
+
+ addr = address;
+ off = offset_in_page(addr);
+ len = length;
+ thislen = 0;
+ lba_start0 = lba_start;
+
+ while (len > 0) {
+ int rc;
+ struct page *page = virt_to_page(addr);
+
+ if (need_new_bio) {
+ bio = bio_kmalloc(GFP_KERNEL, max_nr_vecs);
+ if (!bio) {
+ PRINT_ERROR("Failed to create bio "
+ "for data segment %d (cmd %p)",
+ cmd->get_sg_buf_entry_num, cmd);
+ goto out_no_bio;
+ }
+
+ bios++;
+ need_new_bio = 0;
+ bio->bi_end_io = blockio_endio;
+ bio->bi_sector = lba_start0 <<
+ (virt_dev->block_shift - 9);
+ bio->bi_bdev = bdev;
+ bio->bi_private = blockio_work;
+#if 0 /* It could be win, but could be not, so a performance study is needed */
+ bio->bi_rw |= 1 << BIO_RW_SYNC;
+#endif
+ if (!hbio)
+ hbio = tbio = bio;
+ else
+ tbio = tbio->bi_next = bio;
+ }
+
+ bytes = min_t(unsigned int, len, PAGE_SIZE - off);
+
+ rc = bio_add_page(bio, page, bytes, off);
+ if (rc < bytes) {
+ BUG_ON(rc != 0);
+ need_new_bio = 1;
+ lba_start0 += thislen >> virt_dev->block_shift;
+ thislen = 0;
+ continue;
+ }
+
+ addr += PAGE_SIZE;
+ thislen += bytes;
+ len -= bytes;
+ off = 0;
+ }
+
+ lba_start += length >> virt_dev->block_shift;
+
+ scst_put_buf(cmd, address);
+ length = scst_get_buf_next(cmd, &address);
+ }
+
+ /* +1 to prevent erroneous too early command completion */
+ atomic_set(&blockio_work->bios_inflight, bios+1);
+
+ while (hbio) {
+ bio = hbio;
+ hbio = hbio->bi_next;
+ bio->bi_next = NULL;
+ submit_bio(write, bio);
+ }
+
+ if (q && q->unplug_fn)
+ q->unplug_fn(q);
+
+ blockio_check_finish(blockio_work);
+
+out:
+ return;
+
+out_no_bio:
+ while (hbio) {
+ bio = hbio;
+ hbio = hbio->bi_next;
+ bio_put(bio);
+ }
+ kmem_cache_free(blockio_work_cachep, blockio_work);
+
+out_no_mem:
+ scst_set_busy(cmd);
+ goto out;
+}
+
+static void vdisk_exec_verify(struct scst_cmd *cmd,
+ struct scst_vdisk_thr *thr, loff_t loff)
+{
+ mm_segment_t old_fs;
+ loff_t err;
+ ssize_t length, len_mem = 0;
+ uint8_t *address_sav, *address;
+ int compare;
+ struct scst_vdisk_dev *virt_dev =
+ (struct scst_vdisk_dev *)cmd->dev->dh_priv;
+ struct file *fd = thr->fd;
+ uint8_t *mem_verify = NULL;
+
+ if (vdisk_fsync(thr, loff, cmd->bufflen, cmd, cmd->dev) != 0)
+ goto out;
+
+ /*
+ * Until the cache is cleared prior the verifying, there is not
+ * much point in this code. ToDo.
+ *
+ * Nevertherless, this code is valuable if the data have not read
+ * from the file/disk yet.
+ */
+
+ /* SEEK */
+ old_fs = get_fs();
+ set_fs(get_ds());
+
+ if (!virt_dev->nullio) {
+ if (fd->f_op->llseek)
+ err = fd->f_op->llseek(fd, loff, 0/*SEEK_SET*/);
+ else
+ err = default_llseek(fd, loff, 0/*SEEK_SET*/);
+ if (err != loff) {
+ PRINT_ERROR("lseek trouble %lld != %lld",
+ (long long unsigned int)err,
+ (long long unsigned int)loff);
+ scst_set_cmd_error(cmd,
+ SCST_LOAD_SENSE(scst_sense_hardw_error));
+ goto out_set_fs;
+ }
+ }
+
+ mem_verify = vmalloc(LEN_MEM);
+ if (mem_verify == NULL) {
+ PRINT_ERROR("Unable to allocate memory %d for verify",
+ LEN_MEM);
+ scst_set_cmd_error(cmd,
+ SCST_LOAD_SENSE(scst_sense_hardw_error));
+ goto out_set_fs;
+ }
+
+ length = scst_get_buf_first(cmd, &address);
+ address_sav = address;
+ if (!length && cmd->data_len) {
+ length = cmd->data_len;
+ compare = 0;
+ } else
+ compare = 1;
+
+ while (length > 0) {
+ len_mem = (length > LEN_MEM) ? LEN_MEM : length;
+ TRACE_DBG("Verify: length %zd - len_mem %zd", length, len_mem);
+
+ if (!virt_dev->nullio)
+ err = vfs_read(fd, (char __force __user *)mem_verify,
+ len_mem, &fd->f_pos);
+ else
+ err = len_mem;
+ if ((err < 0) || (err < len_mem)) {
+ PRINT_ERROR("verify() returned %lld from %zd",
+ (long long unsigned int)err, len_mem);
+ if (err == -EAGAIN)
+ scst_set_busy(cmd);
+ else {
+ scst_set_cmd_error(cmd,
+ SCST_LOAD_SENSE(scst_sense_read_error));
+ }
+ if (compare)
+ scst_put_buf(cmd, address_sav);
+ goto out_set_fs;
+ }
+ if (compare && memcmp(address, mem_verify, len_mem) != 0) {
+ TRACE_DBG("Verify: error memcmp length %zd", length);
+ scst_set_cmd_error(cmd,
+ SCST_LOAD_SENSE(scst_sense_miscompare_error));
+ scst_put_buf(cmd, address_sav);
+ goto out_set_fs;
+ }
+ length -= len_mem;
+ address += len_mem;
+ if (compare && length <= 0) {
+ scst_put_buf(cmd, address_sav);
+ length = scst_get_buf_next(cmd, &address);
+ address_sav = address;
+ }
+ }
+
+ if (length < 0) {
+ PRINT_ERROR("scst_get_buf_() failed: %zd", length);
+ scst_set_cmd_error(cmd,
+ SCST_LOAD_SENSE(scst_sense_hardw_error));
+ }
+
+out_set_fs:
+ set_fs(old_fs);
+ if (mem_verify)
+ vfree(mem_verify);
+
+out:
+ return;
+}
+
+static int vdisk_task_mgmt_fn(struct scst_mgmt_cmd *mcmd,
+ struct scst_tgt_dev *tgt_dev)
+{
+
+ if ((mcmd->fn == SCST_LUN_RESET) || (mcmd->fn == SCST_TARGET_RESET)) {
+ /* Restore default values */
+ struct scst_device *dev = tgt_dev->dev;
+ struct scst_vdisk_dev *virt_dev =
+ (struct scst_vdisk_dev *)dev->dh_priv;
+
+ dev->tst = DEF_TST;
+ dev->d_sense = DEF_DSENSE;
+ if (virt_dev->wt_flag && !virt_dev->nv_cache)
+ dev->queue_alg = DEF_QUEUE_ALG_WT;
+ else
+ dev->queue_alg = DEF_QUEUE_ALG;
+ dev->swp = DEF_SWP;
+ dev->tas = DEF_TAS;
+
+ virt_dev->prevent_allow_medium_removal = 0;
+ }
+ return SCST_DEV_TM_NOT_COMPLETED;
+}
+
+static void vdisk_report_registering(const struct scst_vdisk_dev *virt_dev)
+{
+ char buf[128];
+ int i, j;
+
+ i = snprintf(buf, sizeof(buf), "Registering virtual %s device %s ",
+ virt_dev->vdev_devt->name, virt_dev->name);
+ j = i;
+
+ if (virt_dev->wt_flag)
+ i += snprintf(&buf[i], sizeof(buf) - i, "(WRITE_THROUGH");
+
+ if (virt_dev->nv_cache)
+ i += snprintf(&buf[i], sizeof(buf) - i, "%sNV_CACHE",
+ (j == i) ? "(" : ", ");
+
+ if (virt_dev->rd_only)
+ i += snprintf(&buf[i], sizeof(buf) - i, "%sREAD_ONLY",
+ (j == i) ? "(" : ", ");
+
+ if (virt_dev->o_direct_flag)
+ i += snprintf(&buf[i], sizeof(buf) - i, "%sO_DIRECT",
+ (j == i) ? "(" : ", ");
+
+ if (virt_dev->nullio)
+ i += snprintf(&buf[i], sizeof(buf) - i, "%sNULLIO",
+ (j == i) ? "(" : ", ");
+
+ if (virt_dev->blockio)
+ i += snprintf(&buf[i], sizeof(buf) - i, "%sBLOCKIO",
+ (j == i) ? "(" : ", ");
+
+ if (virt_dev->removable)
+ i += snprintf(&buf[i], sizeof(buf) - i, "%sREMOVABLE",
+ (j == i) ? "(" : ", ");
+
+ if (j == i)
+ PRINT_INFO("%s", buf);
+ else
+ PRINT_INFO("%s)", buf);
+
+ return;
+}
+
+static int vdisk_resync_size(struct scst_vdisk_dev *virt_dev)
+{
+ loff_t file_size;
+ int res = 0;
+
+ if (mutex_lock_interruptible(&virt_dev->vdev_sysfs_mutex) != 0) {
+ res = -EINTR;
+ goto out;
+ }
+
+ BUG_ON(virt_dev->nullio);
+
+ res = vdisk_get_file_size(virt_dev->filename,
+ virt_dev->blockio, &file_size);
+ if (res != 0)
+ goto out_unlock;
+
+ if (file_size == virt_dev->file_size) {
+ PRINT_INFO("Size of virtual disk %s remained the same",
+ virt_dev->name);
+ goto out_unlock;
+ }
+
+ res = scst_suspend_activity(true);
+ if (res != 0)
+ goto out_unlock;
+
+ virt_dev->file_size = file_size;
+ virt_dev->nblocks = virt_dev->file_size >> virt_dev->block_shift;
+
+ scst_dev_del_all_thr_data(virt_dev->dev);
+
+ PRINT_INFO("New size of SCSI target virtual disk %s "
+ "(fs=%lldMB, bs=%d, nblocks=%lld, cyln=%lld%s)",
+ virt_dev->name, virt_dev->file_size >> 20,
+ virt_dev->block_size,
+ (long long unsigned int)virt_dev->nblocks,
+ (long long unsigned int)virt_dev->nblocks/64/32,
+ virt_dev->nblocks < 64*32 ? " !WARNING! cyln less "
+ "than 1" : "");
+
+ scst_capacity_data_changed(virt_dev->dev);
+
+ scst_resume_activity();
+
+out_unlock:
+ mutex_unlock(&virt_dev->vdev_sysfs_mutex);
+
+out:
+ return res;
+}
+
+static int vdev_create(struct scst_dev_type *devt,
+ const char *name, struct scst_vdisk_dev **res_virt_dev)
+{
+ int res = 0;
+ struct scst_vdisk_dev *virt_dev;
+ uint64_t dev_id_num;
+ int dev_id_len;
+ char dev_id_str[17];
+ int32_t i;
+
+ virt_dev = kzalloc(sizeof(*virt_dev), GFP_KERNEL);
+ if (virt_dev == NULL) {
+ PRINT_ERROR("Allocation of virtual device %s failed",
+ devt->name);
+ res = -ENOMEM;
+ goto out;
+ }
+
+ spin_lock_init(&virt_dev->flags_lock);
+ mutex_init(&virt_dev->vdev_sysfs_mutex);
+ virt_dev->vdev_devt = devt;
+
+ virt_dev->rd_only = DEF_RD_ONLY;
+ virt_dev->removable = DEF_REMOVABLE;
+
+ virt_dev->block_size = DEF_DISK_BLOCKSIZE;
+ virt_dev->block_shift = DEF_DISK_BLOCKSIZE_SHIFT;
+
+ if (strlen(name) >= sizeof(virt_dev->name)) {
+ PRINT_ERROR("Name %s is too long (max allowed %zd)", name,
+ sizeof(virt_dev->name)-1);
+ res = -EINVAL;
+ goto out_free;
+ }
+ strcpy(virt_dev->name, name);
+
+ dev_id_num = vdisk_gen_dev_id_num(virt_dev->name);
+ dev_id_len = scnprintf(dev_id_str, sizeof(dev_id_str), "%llx",
+ dev_id_num);
+
+ i = strlen(virt_dev->name) + 1; /* for ' ' */
+ memset(virt_dev->t10_dev_id, ' ', i + dev_id_len);
+ memcpy(virt_dev->t10_dev_id, virt_dev->name, i-1);
+ memcpy(virt_dev->t10_dev_id + i, dev_id_str, dev_id_len);
+ TRACE_DBG("t10_dev_id %s", virt_dev->t10_dev_id);
+
+ scnprintf(virt_dev->usn, sizeof(virt_dev->usn), "%llx", dev_id_num);
+ TRACE_DBG("usn %s", virt_dev->usn);
+
+ *res_virt_dev = virt_dev;
+
+out:
+ return res;
+
+out_free:
+ kfree(virt_dev);
+ goto out;
+}
+
+static void vdev_destroy(struct scst_vdisk_dev *virt_dev)
+{
+ kfree(virt_dev->filename);
+ kfree(virt_dev);
+ return;
+}
+
+/* scst_vdisk_mutex supposed to be held */
+static struct scst_vdisk_dev *vdev_find(const char *name)
+{
+ struct scst_vdisk_dev *res, *vv;
+
+ res = NULL;
+ list_for_each_entry(vv, &vdev_list, vdev_list_entry) {
+ if (strcmp(vv->name, name) == 0) {
+ res = vv;
+ break;
+ }
+ }
+ return res;
+}
+
+static int vdev_parse_add_dev_params(struct scst_vdisk_dev *virt_dev,
+ char *params, const char *allowed_params[])
+{
+ int res = 0;
+ unsigned long val;
+ char *param, *p, *pp;
+
+ while (1) {
+ param = scst_get_next_token_str(¶ms);
+ if (param == NULL)
+ break;
+
+ p = scst_get_next_lexem(¶m);
+ if (*p == '\0') {
+ PRINT_ERROR("Syntax error at %s (device %s)",
+ param, virt_dev->name);
+ res = -EINVAL;
+ goto out;
+ }
+
+ if (allowed_params != NULL) {
+ const char **a = allowed_params;
+ bool allowed = false;
+
+ while (*a != NULL) {
+ if (!strcasecmp(*a, p)) {
+ allowed = true;
+ break;
+ }
+ a++;
+ }
+
+ if (!allowed) {
+ PRINT_ERROR("Unknown parameter %s (device %s)", p,
+ virt_dev->name);
+ res = -EINVAL;
+ goto out;
+ }
+ }
+
+ pp = scst_get_next_lexem(¶m);
+ if (*pp == '\0') {
+ PRINT_ERROR("Parameter %s value missed for device %s",
+ p, virt_dev->name);
+ res = -EINVAL;
+ goto out;
+ }
+
+ if (scst_get_next_lexem(¶m)[0] != '\0') {
+ PRINT_ERROR("Too many parameter's %s values (device %s)",
+ p, virt_dev->name);
+ res = -EINVAL;
+ goto out;
+ }
+
+ if (!strcasecmp("filename", p)) {
+ if (*pp != '/') {
+ PRINT_ERROR("Filename %s must be global "
+ "(device %s)", pp, virt_dev->name);
+ res = -EINVAL;
+ goto out;
+ }
+
+ virt_dev->filename = kstrdup(pp, GFP_KERNEL);
+ if (virt_dev->filename == NULL) {
+ PRINT_ERROR("Unable to duplicate file name %s "
+ "(device %s)", pp, virt_dev->name);
+ res = -ENOMEM;
+ goto out;
+ }
+ continue;
+ }
+
+ res = strict_strtoul(pp, 0, &val);
+ if (res != 0) {
+ PRINT_ERROR("strict_strtoul() for %s failed: %d "
+ "(device %s)", pp, res, virt_dev->name);
+ goto out;
+ }
+
+ if (!strcasecmp("write_through", p)) {
+ virt_dev->wt_flag = val;
+ TRACE_DBG("WRITE THROUGH %d", virt_dev->wt_flag);
+ } else if (!strcasecmp("nv_cache", p)) {
+ virt_dev->nv_cache = val;
+ TRACE_DBG("NON-VOLATILE CACHE %d", virt_dev->nv_cache);
+ } else if (!strcasecmp("o_direct", p)) {
+#if 0
+ virt_dev->o_direct_flag = val;
+ TRACE_DBG("O_DIRECT %d", virt_dev->o_direct_flag);
+#else
+ PRINT_INFO("O_DIRECT flag doesn't currently"
+ " work, ignoring it, use fileio_tgt "
+ "in O_DIRECT mode instead (device %s)", virt_dev->name);
+#endif
+ } else if (!strcasecmp("read_only", p)) {
+ virt_dev->rd_only = val;
+ TRACE_DBG("READ ONLY %d", virt_dev->rd_only);
+ } else if (!strcasecmp("removable", p)) {
+ virt_dev->removable = val;
+ TRACE_DBG("REMOVABLE %d", virt_dev->removable);
+ } else if (!strcasecmp("blocksize", p)) {
+ virt_dev->block_size = val;
+ virt_dev->block_shift = scst_calc_block_shift(
+ virt_dev->block_size);
+ if (virt_dev->block_shift < 9) {
+ res = -EINVAL;
+ goto out;
+ }
+ TRACE_DBG("block_size %d, block_shift %d",
+ virt_dev->block_size,
+ virt_dev->block_shift);
+ } else {
+ PRINT_ERROR("Unknown parameter %s (device %s)", p,
+ virt_dev->name);
+ res = -EINVAL;
+ goto out;
+ }
+ }
+
+out:
+ return res;
+}
+
+/* scst_vdisk_mutex supposed to be held */
+static int vdev_fileio_add_device(const char *device_name, char *params)
+{
+ int res = 0;
+ struct scst_vdisk_dev *virt_dev;
+
+ res = vdev_create(&vdisk_file_devtype, device_name, &virt_dev);
+ if (res != 0)
+ goto out;
+
+ virt_dev->wt_flag = DEF_WRITE_THROUGH;
+ virt_dev->nv_cache = DEF_NV_CACHE;
+ virt_dev->o_direct_flag = DEF_O_DIRECT;
+
+ res = vdev_parse_add_dev_params(virt_dev, params, NULL);
+ if (res != 0)
+ goto out_destroy;
+
+ if (virt_dev->rd_only && (virt_dev->wt_flag || virt_dev->nv_cache)) {
+ PRINT_ERROR("Write options on read only device %s",
+ virt_dev->name);
+ res = -EINVAL;
+ goto out_destroy;
+ }
+
+ if (virt_dev->filename == NULL) {
+ PRINT_ERROR("File name required (device %s)", virt_dev->name);
+ res = -EINVAL;
+ goto out_destroy;
+ }
+
+ list_add_tail(&virt_dev->vdev_list_entry, &vdev_list);
+
+ vdisk_report_registering(virt_dev);
+
+ virt_dev->virt_id = scst_register_virtual_device(virt_dev->vdev_devt,
+ virt_dev->name);
+ if (virt_dev->virt_id < 0) {
+ res = virt_dev->virt_id;
+ goto out_del;
+ }
+
+ TRACE_DBG("Registered virt_dev %s with id %d", virt_dev->name,
+ virt_dev->virt_id);
+
+out:
+ return res;
+
+out_del:
+ list_del(&virt_dev->vdev_list_entry);
+
+out_destroy:
+ vdev_destroy(virt_dev);
+ goto out;
+}
+
+/* scst_vdisk_mutex supposed to be held */
+static int vdev_blockio_add_device(const char *device_name, char *params)
+{
+ int res = 0;
+ const char *allowed_params[] = { "filename", "read_only", "removable",
+ "blocksize", NULL };
+ struct scst_vdisk_dev *virt_dev;
+
+ res = vdev_create(&vdisk_blk_devtype, device_name, &virt_dev);
+ if (res != 0)
+ goto out;
+
+ virt_dev->blockio = 1;
+
+ res = vdev_parse_add_dev_params(virt_dev, params, allowed_params);
+ if (res != 0)
+ goto out_destroy;
+
+ if (virt_dev->filename == NULL) {
+ PRINT_ERROR("File name required (device %s)", virt_dev->name);
+ res = -EINVAL;
+ goto out_destroy;
+ }
+
+ list_add_tail(&virt_dev->vdev_list_entry, &vdev_list);
+
+ vdisk_report_registering(virt_dev);
+
+ virt_dev->virt_id = scst_register_virtual_device(virt_dev->vdev_devt,
+ virt_dev->name);
+ if (virt_dev->virt_id < 0) {
+ res = virt_dev->virt_id;
+ goto out_del;
+ }
+
+ TRACE_DBG("Registered virt_dev %s with id %d", virt_dev->name,
+ virt_dev->virt_id);
+
+out:
+ return res;
+
+out_del:
+ list_del(&virt_dev->vdev_list_entry);
+
+out_destroy:
+ vdev_destroy(virt_dev);
+ goto out;
+}
+
+/* scst_vdisk_mutex supposed to be held */
+static int vdev_nullio_add_device(const char *device_name, char *params)
+{
+ int res = 0;
+ const char *allowed_params[] = { "read_only", "removable",
+ "blocksize", NULL };
+ struct scst_vdisk_dev *virt_dev;
+
+ res = vdev_create(&vdisk_null_devtype, device_name, &virt_dev);
+ if (res != 0)
+ goto out;
+
+ virt_dev->nullio = 1;
+
+ res = vdev_parse_add_dev_params(virt_dev, params, allowed_params);
+ if (res != 0)
+ goto out_destroy;
+
+ list_add_tail(&virt_dev->vdev_list_entry, &vdev_list);
+
+ vdisk_report_registering(virt_dev);
+
+ virt_dev->virt_id = scst_register_virtual_device(virt_dev->vdev_devt,
+ virt_dev->name);
+ if (virt_dev->virt_id < 0) {
+ res = virt_dev->virt_id;
+ goto out_del;
+ }
+
+ TRACE_DBG("Registered virt_dev %s with id %d", virt_dev->name,
+ virt_dev->virt_id);
+
+out:
+ return res;
+
+out_del:
+ list_del(&virt_dev->vdev_list_entry);
+
+out_destroy:
+ vdev_destroy(virt_dev);
+ goto out;
+}
+
+static ssize_t vdisk_add_fileio_device(const char *device_name, char *params)
+{
+ int res;
+
+ if (mutex_lock_interruptible(&scst_vdisk_mutex) != 0) {
+ res = -EINTR;
+ goto out;
+ }
+
+ res = vdev_fileio_add_device(device_name, params);
+
+ mutex_unlock(&scst_vdisk_mutex);
+
+out:
+ return res;
+}
+
+static ssize_t vdisk_add_blockio_device(const char *device_name, char *params)
+{
+ int res;
+
+ if (mutex_lock_interruptible(&scst_vdisk_mutex) != 0) {
+ res = -EINTR;
+ goto out;
+ }
+
+ res = vdev_blockio_add_device(device_name, params);
+
+ mutex_unlock(&scst_vdisk_mutex);
+
+out:
+ return res;
+
+}
+
+static ssize_t vdisk_add_nullio_device(const char *device_name, char *params)
+{
+ int res;
+
+ if (mutex_lock_interruptible(&scst_vdisk_mutex) != 0) {
+ res = -EINTR;
+ goto out;
+ }
+
+ res = vdev_nullio_add_device(device_name, params);
+
+ mutex_unlock(&scst_vdisk_mutex);
+
+out:
+ return res;
+
+}
+
+/* scst_vdisk_mutex supposed to be held */
+static void vdev_del_device(struct scst_vdisk_dev *virt_dev)
+{
+
+ scst_unregister_virtual_device(virt_dev->virt_id);
+
+ list_del(&virt_dev->vdev_list_entry);
+
+ PRINT_INFO("Virtual device %s unregistered", virt_dev->name);
+ TRACE_DBG("virt_id %d unregistered", virt_dev->virt_id);
+
+ vdev_destroy(virt_dev);
+
+ return;
+}
+
+static ssize_t vdisk_del_device(const char *device_name)
+{
+ int res = 0;
+ struct scst_vdisk_dev *virt_dev;
+
+ if (mutex_lock_interruptible(&scst_vdisk_mutex) != 0) {
+ res = -EINTR;
+ goto out;
+ }
+
+ virt_dev = vdev_find(device_name);
+ if (virt_dev == NULL) {
+ PRINT_ERROR("Device %s not found", device_name);
+ res = -EINVAL;
+ goto out_unlock;
+ }
+
+ vdev_del_device(virt_dev);
+
+out_unlock:
+ mutex_unlock(&scst_vdisk_mutex);
+
+out:
+ return res;
+}
+
+/* scst_vdisk_mutex supposed to be held */
+static ssize_t __vcdrom_add_device(const char *device_name, char *params)
+{
+ int res = 0;
+ const char *allowed_params[] = { NULL }; /* no params */
+ struct scst_vdisk_dev *virt_dev;
+
+ res = vdev_create(&vcdrom_devtype, device_name, &virt_dev);
+ if (res != 0)
+ goto out;
+
+ virt_dev->rd_only = 1;
+ virt_dev->removable = 1;
+ virt_dev->cdrom_empty = 1;
+
+ virt_dev->block_size = DEF_CDROM_BLOCKSIZE;
+ virt_dev->block_shift = DEF_CDROM_BLOCKSIZE_SHIFT;
+
+ res = vdev_parse_add_dev_params(virt_dev, params, allowed_params);
+ if (res != 0)
+ goto out_destroy;
+
+ list_add_tail(&virt_dev->vdev_list_entry, &vdev_list);
+
+ vdisk_report_registering(virt_dev);
+
+ virt_dev->virt_id = scst_register_virtual_device(virt_dev->vdev_devt,
+ virt_dev->name);
+ if (virt_dev->virt_id < 0) {
+ res = virt_dev->virt_id;
+ goto out_del;
+ }
+
+ TRACE_DBG("Registered virt_dev %s with id %d", virt_dev->name,
+ virt_dev->virt_id);
+
+out:
+ return res;
+
+out_del:
+ list_del(&virt_dev->vdev_list_entry);
+
+out_destroy:
+ vdev_destroy(virt_dev);
+ goto out;
+}
+
+static ssize_t vcdrom_add_device(const char *device_name, char *params)
+{
+ int res;
+
+ if (mutex_lock_interruptible(&scst_vdisk_mutex) != 0) {
+ res = -EINTR;
+ goto out;
+ }
+
+ res = __vcdrom_add_device(device_name, params);
+
+ mutex_unlock(&scst_vdisk_mutex);
+
+out:
+ return res;
+
+}
+
+static ssize_t vcdrom_del_device(const char *device_name)
+{
+ int res = 0;
+ struct scst_vdisk_dev *virt_dev;
+
+ if (mutex_lock_interruptible(&scst_vdisk_mutex) != 0) {
+ res = -EINTR;
+ goto out;
+ }
+
+ virt_dev = vdev_find(device_name);
+ if (virt_dev == NULL) {
+ PRINT_ERROR("Device %s not found", device_name);
+ res = -EINVAL;
+ goto out_unlock;
+ }
+
+ vdev_del_device(virt_dev);
+
+out_unlock:
+ mutex_unlock(&scst_vdisk_mutex);
+
+out:
+ return res;
+}
+
+static int vcdrom_change(struct scst_vdisk_dev *virt_dev,
+ const char *buffer, int length)
+{
+ loff_t err;
+ char *old_fn, *i_buf, *p, *pp;
+ const char *filename = NULL;
+ int res = 0;
+
+ i_buf = kmalloc(length+1, GFP_KERNEL);
+ if (i_buf == NULL) {
+ PRINT_ERROR("Unable to alloc intermediate buffer with size %d",
+ length+1);
+ res = -ENOMEM;
+ goto out;
+ }
+
+ memcpy(i_buf, buffer, length);
+ i_buf[length] = '\0';
+ p = i_buf;
+
+ while (isspace(*p) && *p != '\0')
+ p++;
+ filename = p;
+ p = &i_buf[length-1];
+ pp = p;
+ while (isspace(*p) && *p != '\0') {
+ pp = p;
+ p--;
+ }
+ *pp = '\0';
+
+ if (mutex_lock_interruptible(&virt_dev->vdev_sysfs_mutex) != 0) {
+ res = -EINTR;
+ goto out_free;
+ }
+
+ res = scst_suspend_activity(true);
+ if (res != 0)
+ goto out_sysfs_unlock;
+
+ /* To sync with detach*() functions */
+ mutex_lock(&scst_mutex);
+
+ if (*filename == '\0') {
+ virt_dev->cdrom_empty = 1;
+ TRACE_DBG("%s", "No media");
+ } else if (*filename != '/') {
+ PRINT_ERROR("File path \"%s\" is not "
+ "absolute", filename);
+ res = -EINVAL;
+ goto out_unlock;
+ } else
+ virt_dev->cdrom_empty = 0;
+
+ old_fn = virt_dev->filename;
+
+ if (!virt_dev->cdrom_empty) {
+ int len = strlen(filename) + 1;
+ char *fn = kmalloc(len, GFP_KERNEL);
+ if (fn == NULL) {
+ TRACE(TRACE_OUT_OF_MEM, "%s",
+ "Allocation of filename failed");
+ res = -ENOMEM;
+ goto out_unlock;
+ }
+
+ strlcpy(fn, filename, len);
+ virt_dev->filename = fn;
+
+ res = vdisk_get_file_size(virt_dev->filename,
+ virt_dev->blockio, &err);
+ if (res != 0)
+ goto out_free_fn;
+ } else {
+ err = 0;
+ virt_dev->filename = NULL;
+ }
+
+ if (virt_dev->prevent_allow_medium_removal) {
+ PRINT_ERROR("Prevent medium removal for "
+ "virtual device with name %s", virt_dev->name);
+ res = -EINVAL;
+ goto out_free_fn;
+ }
+
+ virt_dev->file_size = err;
+ virt_dev->nblocks = virt_dev->file_size >> virt_dev->block_shift;
+ if (!virt_dev->cdrom_empty)
+ virt_dev->media_changed = 1;
+
+ mutex_unlock(&scst_mutex);
+
+ scst_dev_del_all_thr_data(virt_dev->dev);
+
+ if (!virt_dev->cdrom_empty) {
+ PRINT_INFO("Changed SCSI target virtual cdrom %s "
+ "(file=\"%s\", fs=%lldMB, bs=%d, nblocks=%lld,"
+ " cyln=%lld%s)", virt_dev->name,
+ vdev_get_filename(virt_dev),
+ virt_dev->file_size >> 20, virt_dev->block_size,
+ (long long unsigned int)virt_dev->nblocks,
+ (long long unsigned int)virt_dev->nblocks/64/32,
+ virt_dev->nblocks < 64*32 ? " !WARNING! cyln less "
+ "than 1" : "");
+ } else {
+ PRINT_INFO("Removed media from SCSI target virtual cdrom %s",
+ virt_dev->name);
+ }
+
+ kfree(old_fn);
+
+out_resume:
+ scst_resume_activity();
+
+out_sysfs_unlock:
+ mutex_unlock(&virt_dev->vdev_sysfs_mutex);
+
+out_free:
+ kfree(i_buf);
+
+out:
+ return res;
+
+out_free_fn:
+ kfree(virt_dev->filename);
+ virt_dev->filename = old_fn;
+
+out_unlock:
+ mutex_unlock(&scst_mutex);
+ goto out_resume;
+}
+
+static ssize_t vcdrom_sysfs_filename_store(struct kobject *kobj,
+ struct kobj_attribute *attr, const char *buf, size_t count)
+{
+ int res;
+ struct scst_device *dev;
+ struct scst_vdisk_dev *virt_dev;
+
+ dev = container_of(kobj, struct scst_device, dev_kobj);
+ virt_dev = (struct scst_vdisk_dev *)dev->dh_priv;
+
+ res = vcdrom_change(virt_dev, buf, count);
+ if (res != 0)
+ goto out;
+
+ res = count;
+
+out:
+ return res;
+}
+
+static ssize_t vdev_sysfs_size_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ int pos = 0;
+ struct scst_device *dev;
+ struct scst_vdisk_dev *virt_dev;
+
+ dev = container_of(kobj, struct scst_device, dev_kobj);
+ virt_dev = (struct scst_vdisk_dev *)dev->dh_priv;
+
+ if (mutex_lock_interruptible(&virt_dev->vdev_sysfs_mutex) != 0) {
+ pos = -EINTR;
+ goto out;
+ }
+
+ pos = sprintf(buf, "%lld\n", virt_dev->file_size / 1024 / 1024);
+
+ mutex_unlock(&virt_dev->vdev_sysfs_mutex);
+
+out:
+ return pos;
+}
+
+static ssize_t vdisk_sysfs_blocksize_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ int pos = 0;
+ struct scst_device *dev;
+ struct scst_vdisk_dev *virt_dev;
+
+ dev = container_of(kobj, struct scst_device, dev_kobj);
+ virt_dev = (struct scst_vdisk_dev *)dev->dh_priv;
+
+ pos = sprintf(buf, "%d\n%s", (int)virt_dev->block_size,
+ (virt_dev->block_size == DEF_DISK_BLOCKSIZE) ? "" :
+ SCST_SYSFS_KEY_MARK "\n");
+ return pos;
+}
+
+static ssize_t vdisk_sysfs_rd_only_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ int pos = 0;
+ struct scst_device *dev;
+ struct scst_vdisk_dev *virt_dev;
+
+ dev = container_of(kobj, struct scst_device, dev_kobj);
+ virt_dev = (struct scst_vdisk_dev *)dev->dh_priv;
+
+ pos = sprintf(buf, "%d\n%s", virt_dev->rd_only ? 1 : 0,
+ (virt_dev->rd_only == DEF_RD_ONLY) ? "" :
+ SCST_SYSFS_KEY_MARK "");
+ return pos;
+}
+
+static ssize_t vdisk_sysfs_wt_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ int pos = 0;
+ struct scst_device *dev;
+ struct scst_vdisk_dev *virt_dev;
+
+ dev = container_of(kobj, struct scst_device, dev_kobj);
+ virt_dev = (struct scst_vdisk_dev *)dev->dh_priv;
+
+ pos = sprintf(buf, "%d\n%s", virt_dev->wt_flag ? 1 : 0,
+ (virt_dev->wt_flag == DEF_WRITE_THROUGH) ? "" :
+ SCST_SYSFS_KEY_MARK "");
+ return pos;
+}
+
+static ssize_t vdisk_sysfs_nv_cache_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ int pos = 0;
+ struct scst_device *dev;
+ struct scst_vdisk_dev *virt_dev;
+
+ dev = container_of(kobj, struct scst_device, dev_kobj);
+ virt_dev = (struct scst_vdisk_dev *)dev->dh_priv;
+
+ pos = sprintf(buf, "%d\n%s", virt_dev->nv_cache ? 1 : 0,
+ (virt_dev->nv_cache == DEF_NV_CACHE) ? "" :
+ SCST_SYSFS_KEY_MARK "");
+ return pos;
+}
+
+static ssize_t vdisk_sysfs_o_direct_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ int pos = 0;
+ struct scst_device *dev;
+ struct scst_vdisk_dev *virt_dev;
+
+ dev = container_of(kobj, struct scst_device, dev_kobj);
+ virt_dev = (struct scst_vdisk_dev *)dev->dh_priv;
+
+ pos = sprintf(buf, "%d\n%s", virt_dev->o_direct_flag ? 1 : 0,
+ (virt_dev->o_direct_flag == DEF_O_DIRECT) ? "" :
+ SCST_SYSFS_KEY_MARK "");
+ return pos;
+}
+
+static ssize_t vdisk_sysfs_removable_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ int pos = 0;
+ struct scst_device *dev;
+ struct scst_vdisk_dev *virt_dev;
+
+ dev = container_of(kobj, struct scst_device, dev_kobj);
+ virt_dev = (struct scst_vdisk_dev *)dev->dh_priv;
+
+ pos = sprintf(buf, "%d\n", virt_dev->removable ? 1 : 0);
+
+ if ((virt_dev->dev->type != TYPE_ROM) &&
+ (virt_dev->removable != DEF_REMOVABLE))
+ pos += sprintf(&buf[pos], "%s\n", SCST_SYSFS_KEY_MARK);
+ return pos;
+}
+
+static ssize_t vdev_sysfs_filename_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ int pos = 0;
+ struct scst_device *dev;
+ struct scst_vdisk_dev *virt_dev;
+
+ dev = container_of(kobj, struct scst_device, dev_kobj);
+ virt_dev = (struct scst_vdisk_dev *)dev->dh_priv;
+
+ if (mutex_lock_interruptible(&virt_dev->vdev_sysfs_mutex) != 0) {
+ pos = -EINTR;
+ goto out;
+ }
+
+ pos = sprintf(buf, "%s\n", vdev_get_filename(virt_dev));
+
+ if (virt_dev->dev->type != TYPE_ROM)
+ pos += sprintf(&buf[pos], "%s\n", SCST_SYSFS_KEY_MARK);
+
+ mutex_unlock(&virt_dev->vdev_sysfs_mutex);
+
+out:
+ return pos;
+}
+
+static ssize_t vdisk_sysfs_resync_size_store(struct kobject *kobj,
+ struct kobj_attribute *attr, const char *buf, size_t count)
+{
+ int res;
+ struct scst_device *dev;
+ struct scst_vdisk_dev *virt_dev;
+
+ dev = container_of(kobj, struct scst_device, dev_kobj);
+ virt_dev = (struct scst_vdisk_dev *)dev->dh_priv;
+
+ res = vdisk_resync_size(virt_dev);
+ if (res != 0)
+ goto out;
+
+ res = count;
+
+out:
+ return res;
+}
+
+static ssize_t vdev_sysfs_t10_dev_id_store(struct kobject *kobj,
+ struct kobj_attribute *attr, const char *buf, size_t count)
+{
+ int res, i;
+ struct scst_device *dev;
+ struct scst_vdisk_dev *virt_dev;
+
+ dev = container_of(kobj, struct scst_device, dev_kobj);
+ virt_dev = (struct scst_vdisk_dev *)dev->dh_priv;
+
+ write_lock_bh(&vdisk_t10_dev_id_rwlock);
+
+ if ((count > sizeof(virt_dev->t10_dev_id)) ||
+ ((count == sizeof(virt_dev->t10_dev_id)) &&
+ (buf[count-1] != '\n'))) {
+ PRINT_ERROR("T10 device id is too long (max %zd "
+ "characters)", sizeof(virt_dev->t10_dev_id)-1);
+ res = -EINVAL;
+ goto out_unlock;
+ }
+
+ memset(virt_dev->t10_dev_id, 0, sizeof(virt_dev->t10_dev_id));
+ memcpy(virt_dev->t10_dev_id, buf, count);
+
+ i = 0;
+ while (i < sizeof(virt_dev->t10_dev_id)) {
+ if (virt_dev->t10_dev_id[i] == '\n') {
+ virt_dev->t10_dev_id[i] = '\0';
+ break;
+ }
+ i++;
+ }
+
+ virt_dev->t10_dev_id_set = 1;
+
+ res = count;
+
+ PRINT_INFO("T10 device id for device %s changed to %s", virt_dev->name,
+ virt_dev->t10_dev_id);
+
+out_unlock:
+ write_unlock_bh(&vdisk_t10_dev_id_rwlock);
+ return res;
+}
+
+static ssize_t vdev_sysfs_t10_dev_id_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ int pos = 0;
+ struct scst_device *dev;
+ struct scst_vdisk_dev *virt_dev;
+
+ dev = container_of(kobj, struct scst_device, dev_kobj);
+ virt_dev = (struct scst_vdisk_dev *)dev->dh_priv;
+
+ read_lock_bh(&vdisk_t10_dev_id_rwlock);
+ pos = sprintf(buf, "%s\n%s", virt_dev->t10_dev_id,
+ virt_dev->t10_dev_id_set ? SCST_SYSFS_KEY_MARK "\n" : "");
+ read_unlock_bh(&vdisk_t10_dev_id_rwlock);
+ return pos;
+}
+
+static ssize_t vdev_sysfs_usn_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ int pos = 0;
+ struct scst_device *dev;
+ struct scst_vdisk_dev *virt_dev;
+
+ dev = container_of(kobj, struct scst_device, dev_kobj);
+ virt_dev = (struct scst_vdisk_dev *)dev->dh_priv;
+
+ pos = sprintf(buf, "%s\n", virt_dev->usn);
+ return pos;
+}
+
+static int __init init_scst_vdisk(struct scst_dev_type *devtype)
+{
+ int res = 0;
+
+ devtype->module = THIS_MODULE;
+
+ res = scst_register_virtual_dev_driver(devtype);
+ if (res < 0)
+ goto out;
+
+out:
+ return res;
+
+}
+
+static void exit_scst_vdisk(struct scst_dev_type *devtype)
+{
+
+ mutex_lock(&scst_vdisk_mutex);
+ while (1) {
+ struct scst_vdisk_dev *virt_dev;
+
+ if (list_empty(&vdev_list))
+ break;
+
+ virt_dev = list_entry(vdev_list.next, typeof(*virt_dev),
+ vdev_list_entry);
+
+ vdev_del_device(virt_dev);
+ }
+ mutex_unlock(&scst_vdisk_mutex);
+
+ scst_unregister_virtual_dev_driver(devtype);
+ return;
+}
+
+static int __init init_scst_vdisk_driver(void)
+{
+ int res;
+
+ vdisk_thr_cachep = KMEM_CACHE(scst_vdisk_thr, SCST_SLAB_FLAGS);
+ if (vdisk_thr_cachep == NULL) {
+ res = -ENOMEM;
+ goto out;
+ }
+
+ blockio_work_cachep = KMEM_CACHE(scst_blockio_work, SCST_SLAB_FLAGS);
+ if (blockio_work_cachep == NULL) {
+ res = -ENOMEM;
+ goto out_free_vdisk_cache;
+ }
+
+ if (num_threads < 1) {
+ PRINT_ERROR("num_threads can not be less than 1, use "
+ "default %d", DEF_NUM_THREADS);
+ num_threads = DEF_NUM_THREADS;
+ }
+
+ vdisk_file_devtype.threads_num = num_threads;
+ vcdrom_devtype.threads_num = num_threads;
+
+ atomic_set(&nullio_thr_data.hdr.ref, 1); /* never destroy it */
+
+ res = init_scst_vdisk(&vdisk_file_devtype);
+ if (res != 0)
+ goto out_free_slab;
+
+ res = init_scst_vdisk(&vdisk_blk_devtype);
+ if (res != 0)
+ goto out_free_vdisk;
+
+ res = init_scst_vdisk(&vdisk_null_devtype);
+ if (res != 0)
+ goto out_free_blk;
+
+ res = init_scst_vdisk(&vcdrom_devtype);
+ if (res != 0)
+ goto out_free_null;
+
+out:
+ return res;
+
+out_free_null:
+ exit_scst_vdisk(&vdisk_null_devtype);
+
+out_free_blk:
+ exit_scst_vdisk(&vdisk_blk_devtype);
+
+out_free_vdisk:
+ exit_scst_vdisk(&vdisk_file_devtype);
+
+out_free_slab:
+ kmem_cache_destroy(blockio_work_cachep);
+
+out_free_vdisk_cache:
+ kmem_cache_destroy(vdisk_thr_cachep);
+ goto out;
+}
+
+static void __exit exit_scst_vdisk_driver(void)
+{
+ exit_scst_vdisk(&vdisk_null_devtype);
+ exit_scst_vdisk(&vdisk_blk_devtype);
+ exit_scst_vdisk(&vdisk_file_devtype);
+ exit_scst_vdisk(&vcdrom_devtype);
+
+ kmem_cache_destroy(blockio_work_cachep);
+ kmem_cache_destroy(vdisk_thr_cachep);
+}
+
+module_init(init_scst_vdisk_driver);
+module_exit(exit_scst_vdisk_driver);
+
+MODULE_AUTHOR("Vladislav Bolkhovitin & Leonid Stoljar");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("SCSI disk (type 0) and CDROM (type 5) dev handler for "
+ "SCST using files on file systems or block devices");
+MODULE_VERSION(SCST_VERSION_STRING);
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2010-04-13 13:08 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <4BC45A32.2010904@vlnb.net>
2010-04-13 13:07 ` [PATCH][RFC 0/3/2/5] SCST dev handlers Vladislav Bolkhovitin
[not found] ` <4BC46173.7000102@vlnb.net>
2010-04-13 13:07 ` [PATCH][RFC 1/3/2/5] SCST dev handlers' Makefile Vladislav Bolkhovitin
2010-04-13 13:08 ` [PATCH][RFC 2/3/2/5] SCST pass-through dev handlers Vladislav Bolkhovitin
2010-04-13 13:08 ` [PATCH][RFC 3/3/2/5] SCST vdisk dev handler Vladislav Bolkhovitin
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).