- Fixed many found task management related problems, especially in the
RESETs area. DEBUG_TM compilation option added (see README).
+ - Updated to work on kernels version 2.6.18+.
+
+ - FILEIO_ONLY added. If it's defined, there is no need to patch the
+ kernel, but pass-through modules (scst_disk, scst_tape, etc.) are not
+ supported).
+
- Timer-based retries for targets after SCST_TGT_RES_QUEUE_FULL status
implemented.
There are the following compilation options, that could be commented
in/out in Makefile:
+ - FILEIO_ONLY - if defined, the pass-through device handlers
+ (scst_disk, scst_tape) will not work, but SCST will not require the
+ kernel patching. Defined by default to ease new people try SCST on
+ their kernels.
+
- DEBUG - turns on some debugging code, including some logging. Makes
the driver considerably bigger and slower, producing large amount of
log data.
IMPORTANT: If you use on initiator some versions of Windows (at least W2K)
========= you can't get good write performance for FILEIO devices with
- default 512 bytes block sizes. You could get about 10% of
- the expected one. This is because of "unusual" write access
+ default 512 bytes block sizes. You could get about 10% of the
+ expected one. This is because of "unusual" write access
pattern, with which Windows'es write data and which is
- (simplifying) incompatible with how Linux page cache works.
+ (simplifying) incompatible with how Linux page cache works,
+ so for each write the corresponding block must be read first.
With 4096 bytes block sizes for FILEIO devices the write
- performance will be as expected.
+ performance will be as expected. Actually, any system on
+ initiator, not only Windows, will benefit from block size
+ max(PAGE_SIZE, BLOCK_SIZE_ON_UNDERLYING_FS), where PAGE_SIZE
+ is the page size, BLOCK_SIZE_ON_UNDERLYING_FS is block size on
+ the underlying FS, on which the device file located, or 0, if
+ a device node is used. Both values are on the target.
Just for reference: we had with 0.9.2 and "old" Qlogic driver on 2.4.2x
kernel, where we did carefull performance study, aggregate throuhput
struct scst_tgt_dev *tgt_dev; /* corresponding device for this cmd */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
struct scsi_request *scsi_req; /* SCSI request */
+#endif
/* List entry for tgt_dev's SN related lists */
struct list_head sn_cmd_list_entry;
--- /dev/null
+--- linux-2.6.18.1-scst-dbg/drivers/scsi/scsi_lib.c_scst 2006-09-20 07:42:06.000000000 +0400
++++ linux-2.6.18.1-scst-dbg/drivers/scsi/scsi_lib.c 2006-10-25 20:52:58.000000000 +0400
+@@ -367,7 +367,7 @@
+ }
+
+ /**
+- * scsi_execute_async - insert request
++ * __scsi_execute_async - insert request
+ * @sdev: scsi device
+ * @cmd: scsi command
+ * @cmd_len: length of scsi cdb
+@@ -378,11 +378,14 @@
+ * @timeout: request timeout in seconds
+ * @retries: number of times to retry request
+ * @flags: or into request flags
++ * @at_head: insert request at head or tail of queue
+ **/
+-int scsi_execute_async(struct scsi_device *sdev, const unsigned char *cmd,
++static inline int __scsi_execute_async(struct scsi_device *sdev,
++ const unsigned char *cmd,
+ int cmd_len, int data_direction, void *buffer, unsigned bufflen,
+ int use_sg, int timeout, int retries, void *privdata,
+- void (*done)(void *, char *, int, int), gfp_t gfp)
++ void (*done)(void *, char *, int, int), gfp_t gfp,
++ int at_head)
+ {
+ struct request *req;
+ struct scsi_io_context *sioc;
+@@ -418,7 +421,7 @@
+ sioc->data = privdata;
+ sioc->done = done;
+
+- blk_execute_rq_nowait(req->q, NULL, req, 1, scsi_end_async);
++ blk_execute_rq_nowait(req->q, NULL, req, at_head, scsi_end_async);
+ return 0;
+
+ free_req:
+@@ -427,8 +430,53 @@
+ kfree(sioc);
+ return DRIVER_ERROR << 24;
+ }
++
++/**
++ * scsi_execute_async - insert request
++ * @sdev: scsi device
++ * @cmd: scsi command
++ * @cmd_len: length of scsi cdb
++ * @data_direction: data direction
++ * @buffer: data buffer (this can be a kernel buffer or scatterlist)
++ * @bufflen: len of buffer
++ * @use_sg: if buffer is a scatterlist this is the number of elements
++ * @timeout: request timeout in seconds
++ * @retries: number of times to retry request
++ * @flags: or into request flags
++ **/
++int scsi_execute_async(struct scsi_device *sdev, const unsigned char *cmd,
++ int cmd_len, int data_direction, void *buffer, unsigned bufflen,
++ int use_sg, int timeout, int retries, void *privdata,
++ void (*done)(void *, char *, int, int), gfp_t gfp)
++{
++ return __scsi_execute_async(sdev, cmd, cmd_len, data_direction, buffer,
++ bufflen, use_sg, timeout, retries, privdata, done, gfp, 1);
++}
+ EXPORT_SYMBOL_GPL(scsi_execute_async);
+
++/**
++ * scsi_execute_async_fifi - insert request at tail, in FIFO order
++ * @sdev: scsi device
++ * @cmd: scsi command
++ * @cmd_len: length of scsi cdb
++ * @data_direction: data direction
++ * @buffer: data buffer (this can be a kernel buffer or scatterlist)
++ * @bufflen: len of buffer
++ * @use_sg: if buffer is a scatterlist this is the number of elements
++ * @timeout: request timeout in seconds
++ * @retries: number of times to retry request
++ * @flags: or into request flags
++ **/
++int scsi_execute_async_fifo(struct scsi_device *sdev, const unsigned char *cmd,
++ int cmd_len, int data_direction, void *buffer, unsigned bufflen,
++ int use_sg, int timeout, int retries, void *privdata,
++ void (*done)(void *, char *, int, int), gfp_t gfp)
++{
++ return __scsi_execute_async(sdev, cmd, cmd_len, data_direction, buffer,
++ bufflen, use_sg, timeout, retries, privdata, done, gfp, 0);
++}
++EXPORT_SYMBOL_GPL(scsi_execute_async_fifo);
++
+ /*
+ * Function: scsi_init_cmd_errh()
+ *
+--- linux-2.6.18.1-scst-dbg/include/scsi/scsi_device.h_scst 2006-09-20 07:42:06.000000000 +0400
++++ linux-2.6.18.1-scst-dbg/include/scsi/scsi_device.h 2006-10-25 20:09:23.000000000 +0400
+@@ -297,6 +297,12 @@
+ int timeout, int retries, void *privdata,
+ void (*done)(void *, char *, int, int),
+ gfp_t gfp);
++extern int scsi_execute_async_fifo(struct scsi_device *sdev,
++ const unsigned char *cmd, int cmd_len, int data_direction,
++ void *buffer, unsigned bufflen, int use_sg,
++ int timeout, int retries, void *privdata,
++ void (*done)(void *, char *, int, int),
++ gfp_t gfp);
+
+ static inline void scsi_device_reprobe(struct scsi_device *sdev)
+ {
#EXTRA_CFLAGS += -DSTRICT_SERIALIZING
-EXTRA_CFLAGS += -DEXTRACHECKS
+EXTRA_CFLAGS += -DEXTRACHECKS
+#EXTRA_CFLAGS += -DFILEIO_ONLY
#EXTRA_CFLAGS += -fno-inline
goto out;
}
+#ifdef FILEIO_ONLY
+ if (dev_type->exec == NULL) {
+ PRINT_ERROR_PR("Pass-through dev handlers (handler %s) not "
+ "supported. Recompile SCST with undefined FILEIO_ONLY",
+ dev_type->name);
+ res = -EINVAL;
+ goto out;
+ }
+#endif
+
if (down_interruptible(&scst_mutex) != 0) {
res = -EINTR;
goto out;
{
int res = 0, i;
struct scst_cmd *cmd;
- struct scsi_request *req;
TRACE_ENTRY();
- BUILD_BUG_ON(sizeof(cmd->sense_buffer) != sizeof(req->sr_sense_buffer));
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
+ {
+ struct scsi_request *req;
+ BUILD_BUG_ON(sizeof(cmd->sense_buffer) !=
+ sizeof(req->sr_sense_buffer));
+ }
+#else
+ {
+ struct scsi_sense_hdr *shdr;
+ BUILD_BUG_ON((sizeof(cmd->sense_buffer) < sizeof(*shdr)) &&
+ (sizeof(cmd->sense_buffer) >= SCSI_SENSE_BUFFERSIZE));
+ }
+#endif
scst_num_cpus = get_cpus_count();
static void __exit exit_scst(void)
{
+#ifdef CONFIG_LOCKDEP
+ static /* To hide the lockdep's warning about non-static key */
+#endif
DECLARE_MUTEX_LOCKED(shm);
TRACE_ENTRY();
return res;
}
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
static void scst_req_done(struct scsi_cmnd *scsi_cmd)
{
struct scsi_request *req;
TRACE_EXIT();
return;
}
+#else /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18) */
+static void scst_send_release(struct scst_tgt_dev *tgt_dev)
+{
+ struct scsi_device *scsi_dev;
+ unsigned char cdb[6];
+ unsigned char sense[SCSI_SENSE_BUFFERSIZE];
+ int rc;
+
+ TRACE_ENTRY();
+
+ if (tgt_dev->acg_dev->dev->scsi_dev == NULL)
+ goto out;
+
+ scsi_dev = tgt_dev->acg_dev->dev->scsi_dev;
+
+ memset(cdb, 0, sizeof(cdb));
+ cdb[0] = RELEASE;
+ cdb[1] = (scsi_dev->scsi_level <= SCSI_2) ?
+ ((scsi_dev->lun << 5) & 0xe0) : 0;
+
+ TRACE(TRACE_DEBUG | TRACE_SCSI, "%s", "Sending RELEASE req to SCSI "
+ "mid-level");
+ rc = scsi_execute(scsi_dev, cdb, SCST_DATA_NONE, NULL, 0,
+ sense, SCST_DEFAULT_TIMEOUT,
+ 3, GFP_KERNEL);
+ if (rc) {
+ PRINT_INFO_PR("scsi_execute() failed: %d", rc);
+ goto out;
+ }
+
+out:
+ TRACE_EXIT();
+ return;
+}
+#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18) */
struct scst_session *scst_alloc_session(struct scst_tgt *tgt, int gfp_mask,
const char *initiator_name)
BUG_ON(cmd->blocking);
-#ifdef EXTRACHECKS
+#if defined(EXTRACHECKS) && (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18))
if (cmd->scsi_req) {
PRINT_ERROR_PR("%s: %s", __FUNCTION__, "Cmd with unfreed "
"scsi_req!");
return;
}
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
int scst_alloc_request(struct scst_cmd *cmd)
{
int res = 0;
scsi_release_request(cmd->scsi_req);
cmd->scsi_req = NULL;
}
+#endif
int scst_alloc_space(struct scst_cmd *cmd)
{
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_driver.h>
#include <scsi/scsi_device.h>
-#include <scsi/scsi_request.h>
#include <scsi/scsi_host.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
+#include <scsi/scsi_request.h>
+#endif
+
#define SCST_MAJOR 177
#define TRACE_RETRY 0x80000000
void scst_check_retries(struct scst_tgt *tgt, int processible_env);
void scst_tgt_retry_timer_fn(unsigned long arg);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
int scst_alloc_request(struct scst_cmd *cmd);
void scst_release_request(struct scst_cmd *cmd);
const void *cmnd, void *buffer, unsigned bufflen,
void (*done)(struct scsi_cmnd *), int timeout, int retries)
{
-#ifdef STRICT_SERIALIZING
+ #ifdef STRICT_SERIALIZING
scsi_do_req(sreq, cmnd, buffer, bufflen, done, timeout, retries);
-#else
+ #elif defined(FILEIO_ONLY)
+ BUG();
+ #else
scsi_do_req_fifo(sreq, cmnd, buffer, bufflen, done, timeout, retries);
-#endif
+ #endif
}
+#else
+static inline int scst_exec_req(struct scsi_device *sdev,
+ const unsigned char *cmd, int cmd_len, int data_direction,
+ void *buffer, unsigned bufflen, int use_sg, int timeout, int retries,
+ void *privdata, void (*done)(void *, char *, int, int), gfp_t gfp)
+{
+ #ifdef STRICT_SERIALIZING
+ return scsi_execute_async(sdev, cmd, cmd_len, data_direction, buffer,
+ bufflen, use_sg, timeout, retries, privdata, done, gfp);
+ #elif defined(FILEIO_ONLY)
+ BUG();
+ return -1;
+ #else
+ return scsi_execute_async_fifo(sdev, cmd, cmd_len, data_direction,
+ buffer, bufflen, use_sg, timeout, retries, privdata, done, gfp);
+ #endif
+}
+#endif
+
int scst_alloc_space(struct scst_cmd *cmd);
void scst_release_space(struct scst_cmd *cmd);
void scst_scsi_op_list_init(void);
}
/* No locks supposed to be held */
-static void scst_check_sense(struct scst_cmd *cmd, struct scsi_request *req,
- int *next_state)
+static void scst_check_sense(struct scst_cmd *cmd, const uint8_t *rq_sense,
+ int rq_sense_len, int *next_state)
{
int sense_valid;
struct scst_device *dev = cmd->dev;
smp_mb();
}
- if (req != NULL) {
- sense_valid = SCST_SENSE_VALID(req->sr_sense_buffer);
+ if (rq_sense != NULL) {
+ sense_valid = SCST_SENSE_VALID(rq_sense);
if (sense_valid) {
- memcpy(cmd->sense_buffer, req->sr_sense_buffer,
- sizeof(cmd->sense_buffer));
+ memset(cmd->sense_buffer, 0, sizeof(cmd->sense_buffer));
+ /*
+ * We checked that rq_sense_len < sizeof(cmd->sense_buffer)
+ * in init_scst()
+ */
+ memcpy(cmd->sense_buffer, rq_sense, rq_sense_len);
}
} else
sense_valid = SCST_SENSE_VALID(cmd->sense_buffer);
return res;
}
-static void scst_do_cmd_done(struct scst_cmd *cmd,
- struct scsi_request *req, int *next_state)
+static void scst_do_cmd_done(struct scst_cmd *cmd, int result,
+ const uint8_t *rq_sense, int rq_sense_len, int *next_state)
{
+ unsigned char type;
+
TRACE_ENTRY();
- cmd->status = req->sr_result & 0xff;
- cmd->masked_status = status_byte(req->sr_result);
- cmd->msg_status = msg_byte(req->sr_result);
- cmd->host_status = host_byte(req->sr_result);
- cmd->driver_status = driver_byte(req->sr_result);
- TRACE(TRACE_SCSI, "req->sr_result=%x, cmd->status=%x, "
+ cmd->status = result & 0xff;
+ cmd->masked_status = status_byte(result);
+ cmd->msg_status = msg_byte(result);
+ cmd->host_status = host_byte(result);
+ cmd->driver_status = driver_byte(result);
+ TRACE(TRACE_SCSI, "result=%x, cmd->status=%x, "
"cmd->masked_status=%x, cmd->msg_status=%x, cmd->host_status=%x, "
- "cmd->driver_status=%x", req->sr_result, cmd->status,
+ "cmd->driver_status=%x", result, cmd->status,
cmd->masked_status, cmd->msg_status, cmd->host_status,
cmd->driver_status);
- scst_check_sense(cmd, req, next_state);
+ cmd->completed = 1;
- cmd->bufflen = req->sr_bufflen; //??
+ scst_dec_on_dev_cmd(cmd);
- /* Clear out request structure */
- req->sr_use_sg = 0;
- req->sr_sglist_len = 0;
- req->sr_bufflen = 0;
- req->sr_buffer = NULL;
- req->sr_underflow = 0;
- req->sr_request->rq_disk = NULL; /* disown request blk */ ;
+ type = cmd->dev->handler->type;
+ if ((cmd->cdb[0] == MODE_SENSE || cmd->cdb[0] == MODE_SENSE_10) &&
+ cmd->tgt_dev->acg_dev->rd_only_flag &&
+ (type == TYPE_DISK || type == TYPE_WORM || type == TYPE_MOD ||
+ type == TYPE_TAPE)) {
+ int32_t length;
+ uint8_t *address;
+
+ length = scst_get_buf_first(cmd, &address);
+ TRACE_DBG("length %d", length);
+ if (unlikely(length <= 0)) {
+ PRINT_ERROR_PR("%s: scst_get_buf_first() failed",
+ __func__);
+ goto next;
+ }
+ if (length > 2 && cmd->cdb[0] == MODE_SENSE) {
+ address[2] |= 0x80; /* Write Protect*/
+ }
+ else if (length > 3 && cmd->cdb[0] == MODE_SENSE_10) {
+ address[3] |= 0x80; /* Write Protect*/
+ }
+ scst_put_buf(cmd, address);
+ }
+
+next:
+ scst_check_sense(cmd, rq_sense, rq_sense_len, next_state);
TRACE_EXIT();
return;
}
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
static inline struct scst_cmd *scst_get_cmd(struct scsi_cmnd *scsi_cmd,
struct scsi_request **req)
{
struct scsi_request *req = NULL;
struct scst_cmd *cmd;
int next_state;
- unsigned char type;
TRACE_ENTRY();
if (cmd == NULL)
goto out;
- cmd->completed = 1;
+ next_state = SCST_CMD_STATE_DEV_DONE;
+ scst_do_cmd_done(cmd, req->sr_result, req->sr_sense_buffer,
+ sizeof(req->sr_sense_buffer), &next_state);
- scst_dec_on_dev_cmd(cmd);
+ /* Clear out request structure */
+ req->sr_use_sg = 0;
+ req->sr_sglist_len = 0;
+ req->sr_bufflen = 0;
+ req->sr_buffer = NULL;
+ req->sr_underflow = 0;
+ req->sr_request->rq_disk = NULL; /* disown request blk */
- type = cmd->dev->handler->type;
- if ((cmd->cdb[0] == MODE_SENSE || cmd->cdb[0] == MODE_SENSE_10) &&
- cmd->tgt_dev->acg_dev->rd_only_flag &&
- (type == TYPE_DISK || type == TYPE_WORM || type == TYPE_MOD ||
- type == TYPE_TAPE)) {
- int32_t length;
- uint8_t *address;
+ cmd->bufflen = req->sr_bufflen; //??
- length = scst_get_buf_first(cmd, &address);
- TRACE_DBG("length %d", length);
- if (unlikely(length <= 0)) {
- goto out;
- }
- if (length > 2 && cmd->cdb[0] == MODE_SENSE) {
- address[2] |= 0x80; /* Write Protect*/
- }
- else if (length > 3 && cmd->cdb[0] == MODE_SENSE_10) {
- address[3] |= 0x80; /* Write Protect*/
- }
- scst_put_buf(cmd, address);
- }
+ scst_release_request(cmd);
- next_state = SCST_CMD_STATE_DEV_DONE;
+ cmd->state = next_state;
+ cmd->non_atomic_only = 0;
- scst_do_cmd_done(cmd, req, &next_state);
+ __scst_process_active_cmd(cmd, scst_get_context(), 0);
- scst_release_request(cmd);
+out:
+ TRACE_EXIT();
+ return;
+}
+#else /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18) */
+static void scst_cmd_done(void *data, char *sense, int result, int resid)
+{
+ struct scst_cmd *cmd;
+ int next_state;
+
+ TRACE_ENTRY();
+
+ WARN_ON(in_irq());
+
+ /*
+ * We don't use resid, because:
+ * 1. Many low level initiator drivers don't use (set) this field
+ * 2. We determine the command's buffer size directly from CDB,
+ * so resid is not relevant for us, and target drivers
+ * should know the residual, if necessary, by comparing expected
+ * and actual transfer sizes.
+ */
+
+ cmd = (struct scst_cmd *)data;
+ if (cmd == NULL)
+ goto out;
+
+ next_state = SCST_CMD_STATE_DEV_DONE;
+ scst_do_cmd_done(cmd, result, sense, SCSI_SENSE_BUFFERSIZE,
+ &next_state);
cmd->state = next_state;
cmd->non_atomic_only = 0;
TRACE_EXIT();
return;
}
+#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18) */
static void scst_cmd_done_local(struct scst_cmd *cmd, int next_state)
{
}
#endif
- scst_check_sense(cmd, NULL, &next_state);
+ scst_check_sense(cmd, NULL, 0, &next_state);
cmd->state = next_state;
cmd->non_atomic_only = 0;
(uint64_t)cmd->lun);
goto out_error;
}
-
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
if (scst_alloc_request(cmd) != 0) {
PRINT_INFO_PR("%s", "Unable to allocate request, "
"sending BUSY status");
(void *)cmd->scsi_req->sr_buffer,
cmd->scsi_req->sr_bufflen, scst_cmd_done, cmd->timeout,
cmd->retries);
+#else
+ rc = scst_exec_req(cmd->dev->scsi_dev, cmd->cdb, cmd->cdb_len,
+ cmd->data_direction, cmd->sg, cmd->bufflen, cmd->sg_cnt,
+ cmd->timeout, cmd->retries, cmd, scst_cmd_done,
+ GFP_KERNEL);
+ if (rc) {
+ PRINT_INFO_PR("scst_exec_req() failed: %d", rc);
+ goto out_error;
+ }
+#endif
rc = SCST_EXEC_COMPLETED;
rc = SCST_EXEC_COMPLETED;
scst_cmd_done_local(cmd, SCST_CMD_STATE_DEFAULT);
goto out;
-
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
out_busy:
scst_set_busy(cmd);
cmd->completed = 1;
rc = SCST_EXEC_COMPLETED;
scst_cmd_done_local(cmd, SCST_CMD_STATE_DEFAULT);
goto out;
+#endif
out_aborted:
rc = SCST_EXEC_COMPLETED;