mirror of
https://github.com/SCST-project/scst.git
synced 2026-08-18 13:16:34 +00:00
- BLOCKIO made async
- Docs updates - Debug logging improvements git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@101 d57e44dd-8a1f-0410-8b47-8ef2f437770f
This commit is contained in:
+1
-1
@@ -1,7 +1,7 @@
|
||||
Summary of changes between versions 0.9.5 and 0.9.6
|
||||
---------------------------------------------------
|
||||
|
||||
- FILEIO was renamed to VDISK. BLOCK IO added to it, thanks to Ross S. W.
|
||||
- FILEIO was renamed to VDISK. BLOCKIO added to it, thanks to Ross S. W.
|
||||
Walker and Vu Pham.
|
||||
|
||||
- Internal locking and execution context were reimplemnted. Particularly,
|
||||
|
||||
+23
-17
@@ -108,10 +108,11 @@ block devices comparing to sending SCSI commands directly to SCSI
|
||||
mid-level via scsi_do_req()/scsi_execute_async() has advantage that data
|
||||
are transfered via system cache, so it is possible to fully benefit from
|
||||
caching and read ahead performed by Linux's VM subsystem. The only
|
||||
disadvantage here that there is superfluous data copying between the
|
||||
cache and SCST's buffers. This issue is going to be addressed in the
|
||||
next release. Virtual CDROM's are useful for remote installation. See
|
||||
below for details how to setup and use VDISK device handler.
|
||||
disadvantage here that in the FILEIO mode there is superfluous data
|
||||
copying between the cache and SCST's buffers. This issue is going to be
|
||||
addressed in the next release. Virtual CDROM's are useful for remote
|
||||
installation. See below for details how to setup and use VDISK device
|
||||
handler.
|
||||
|
||||
"Performance" device handlers for disks, MO disks and tapes in their
|
||||
exec() method skip (pretend to execute) all READ and WRITE operations
|
||||
@@ -351,8 +352,8 @@ subdirectories "vdisk" and "vcdrom". They have similar layout:
|
||||
For example, "echo "open disk1 /vdisks/disk1" >/proc/scsi_tgt/vdisk/vdisk"
|
||||
will open file /vdisks/disk1 as virtual VDISK disk with name "disk1".
|
||||
|
||||
IMPORTANT: By default for performance reasons VDISK devices use write back
|
||||
========= caching policy. This is generally safe from the consistence of
|
||||
IMPORTANT: By default for performance reasons VDISK FILEIO devices use write
|
||||
========= back caching policy. This is generally safe from the consistence of
|
||||
journaled file systems, laying over them, point of view, but
|
||||
your unsaved cached data will be lost in case of
|
||||
power/hardware/software failure, so you must supply your
|
||||
@@ -389,8 +390,8 @@ IMPORTANT: Many disk and partition table management utilities don't support
|
||||
first access to it from the remote initiator with another
|
||||
block size.
|
||||
|
||||
BLOCKIO VDISK mode (written by Ross S. W. Walker)
|
||||
-------------------------------------------------
|
||||
BLOCKIO VDISK mode
|
||||
------------------
|
||||
|
||||
This module works best for these types of scenarios:
|
||||
|
||||
@@ -411,20 +412,25 @@ non-discriminate caching.
|
||||
have a consistent view of the primary targets in order to preserve data
|
||||
integrity which a page cache backed IO type might not provide reliably.
|
||||
|
||||
Also it has an advantage over FILEIO that it doesn't copy data between
|
||||
the system cache and the commands data buffers, so it saves a
|
||||
considerable amount of CPU power and memory bandwidth.
|
||||
|
||||
Performance
|
||||
-----------
|
||||
|
||||
Before doing any performance measurements note that:
|
||||
|
||||
I. Currently maximum performance is possible only with real SCSI devices
|
||||
with several simultaneously executed commands (SCSI tagged queuing) or
|
||||
performance handlers. If you have enough CPU power, VDISK handler also
|
||||
could provide the same results, when aggregate throughput is close to
|
||||
the aggregate throughput locally on the target from the same disks. Also
|
||||
note, that currently IO subsystem in Linux implemented on such way, so a
|
||||
VDISK device over a single file occupied entire formatted with some
|
||||
file system device (eg /dev/hdc) could perform considerably better, than
|
||||
a VDISK device over /dev/hdc itself without the file system involved.
|
||||
or VDISK BLOCKIO mode with several simultaneously executed commands
|
||||
(SCSI tagged queuing) or performance handlers. If you have enough CPU
|
||||
power, VDISK FILEIO handler also could provide the same results, when
|
||||
aggregate throughput is close to the aggregate throughput locally on the
|
||||
target from the same disks. Also note, that currently IO subsystem in
|
||||
Linux implemented on such way, so a VDISK FILEIO device over a single
|
||||
file occupied entire formatted with some file system device (eg
|
||||
/dev/hdc) could perform considerably better, than a VDISK FILEIO device
|
||||
over /dev/hdc itself without the file system involved.
|
||||
|
||||
II. In order to get the maximum performance you should:
|
||||
|
||||
@@ -473,7 +479,7 @@ IMPORTANT: Some of those options enabled by default, i.e. SCST is optimized
|
||||
expected.
|
||||
|
||||
IMPORTANT: If you use on initiator some versions of Windows (at least W2K)
|
||||
========= you can't get good write performance for VDISK devices with
|
||||
========= you can't get good write performance for VDISK FILEIO devices with
|
||||
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
|
||||
|
||||
@@ -20,10 +20,6 @@ To be done
|
||||
|
||||
- Move linear searches to hash-table based.
|
||||
|
||||
- Create dev handler for block devices, which would insert commands as
|
||||
block requests in the device's block queue, so they would processed
|
||||
by IO-scheduler then.
|
||||
|
||||
- Redone some semaphores with completion interface.
|
||||
|
||||
- HIGHMEM cleanup. Looks like HIGHMEM usage doesn't worth the effort and
|
||||
|
||||
@@ -665,9 +665,10 @@ static int vdisk_do_job(struct scst_cmd *cmd)
|
||||
case READ_10:
|
||||
case READ_12:
|
||||
case READ_16:
|
||||
if (virt_dev->blockio)
|
||||
if (virt_dev->blockio) {
|
||||
blockio_exec_rw(cmd, thr, lba_start, 0);
|
||||
else
|
||||
goto out;
|
||||
} else
|
||||
vdisk_exec_read(cmd, thr, loff);
|
||||
break;
|
||||
case WRITE_6:
|
||||
@@ -691,9 +692,10 @@ static int vdisk_do_job(struct scst_cmd *cmd)
|
||||
if (vdisk_fsync(thr, 0, 0, cmd) != 0)
|
||||
goto done;
|
||||
}
|
||||
if (virt_dev->blockio)
|
||||
if (virt_dev->blockio) {
|
||||
blockio_exec_rw(cmd, thr, lba_start, 1);
|
||||
else
|
||||
goto out;
|
||||
} else
|
||||
vdisk_exec_write(cmd, thr, loff);
|
||||
/* O_SYNC flag is used for WT devices */
|
||||
if (do_fsync || fua)
|
||||
@@ -2060,7 +2062,6 @@ out:
|
||||
struct blockio_work {
|
||||
atomic_t bios_inflight;
|
||||
struct scst_cmd *cmd;
|
||||
struct completion complete;
|
||||
};
|
||||
|
||||
static int blockio_endio(struct bio *bio, unsigned int bytes_done, int error)
|
||||
@@ -2089,8 +2090,12 @@ static int blockio_endio(struct bio *bio, unsigned int bytes_done, int error)
|
||||
}
|
||||
|
||||
/* Decrement the bios in processing, and if zero signal completion */
|
||||
if (atomic_dec_and_test(&blockio_work->bios_inflight))
|
||||
complete(&blockio_work->complete);
|
||||
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);
|
||||
kfree(blockio_work);
|
||||
}
|
||||
|
||||
bio_put(bio);
|
||||
return 0;
|
||||
@@ -2107,6 +2112,7 @@ static void blockio_exec_rw(struct scst_cmd *cmd, struct scst_vdisk_thr *thr,
|
||||
int need_new_bio;
|
||||
struct scatterlist *sgl = cmd->sg;
|
||||
struct blockio_work *blockio_work;
|
||||
int bios = 0;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
@@ -2117,10 +2123,8 @@ static void blockio_exec_rw(struct scst_cmd *cmd, struct scst_vdisk_thr *thr,
|
||||
blockio_work = kmalloc(sizeof (*blockio_work), GFP_KERNEL);
|
||||
if (blockio_work == NULL)
|
||||
goto out_no_mem;
|
||||
|
||||
atomic_set(&blockio_work->bios_inflight, 0);
|
||||
|
||||
blockio_work->cmd = cmd;
|
||||
init_completion(&blockio_work->complete);
|
||||
|
||||
if (q)
|
||||
max_nr_vecs = min(bio_get_nr_vecs(bdev), BIO_MAX_PAGES);
|
||||
@@ -2147,7 +2151,7 @@ static void blockio_exec_rw(struct scst_cmd *cmd, struct scst_vdisk_thr *thr,
|
||||
goto out_no_bio;
|
||||
}
|
||||
|
||||
atomic_inc(&blockio_work->bios_inflight);
|
||||
bios++;
|
||||
need_new_bio = 0;
|
||||
bio->bi_end_io = blockio_endio;
|
||||
bio->bi_sector = lba_start <<
|
||||
@@ -2180,22 +2184,18 @@ static void blockio_exec_rw(struct scst_cmd *cmd, struct scst_vdisk_thr *thr,
|
||||
|
||||
lba_start += sgl[j].length >> virt_dev->block_shift;
|
||||
}
|
||||
atomic_set(&blockio_work->bios_inflight, bios);
|
||||
|
||||
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);
|
||||
|
||||
wait_for_completion(&blockio_work->complete);
|
||||
|
||||
kfree(blockio_work);
|
||||
|
||||
out:
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
|
||||
@@ -601,7 +601,6 @@ int scst_register_virtual_device(struct scst_dev_type *dev_handler,
|
||||
dev->virt_name = dev_name;
|
||||
|
||||
scst_suspend_activity();
|
||||
|
||||
if (down_interruptible(&scst_mutex) != 0) {
|
||||
res = -EINTR;
|
||||
goto out_free_dev;
|
||||
@@ -715,7 +714,6 @@ int scst_register_dev_driver(struct scst_dev_type *dev_type)
|
||||
#endif
|
||||
|
||||
scst_suspend_activity();
|
||||
|
||||
if (down_interruptible(&scst_mutex) != 0) {
|
||||
res = -EINTR;
|
||||
goto out_err;
|
||||
|
||||
@@ -1193,6 +1193,11 @@ void scst_free_cmd(struct scst_cmd *cmd)
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
if (unlikely(test_bit(SCST_CMD_ABORTED, &cmd->cmd_flags))) {
|
||||
TRACE_MGMT_DBG("Freeing aborted cmd %p (scst_cmd_count %d)",
|
||||
cmd, atomic_read(&scst_cmd_count));
|
||||
}
|
||||
|
||||
sBUG_ON(cmd->blocking);
|
||||
|
||||
#if defined(EXTRACHECKS) && (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18))
|
||||
|
||||
@@ -195,6 +195,8 @@ static int strncasecmp(const char *s1, const char *s2, int n)
|
||||
|
||||
#if defined(DEBUG) || defined(TRACING)
|
||||
|
||||
static DECLARE_MUTEX(scst_log_mutex);
|
||||
|
||||
int scst_proc_log_entry_write(struct file *file, const char *buf,
|
||||
unsigned long length, unsigned long *log_level,
|
||||
unsigned long default_level, const struct scst_proc_log *tbl)
|
||||
@@ -356,7 +358,7 @@ static int scst_proc_scsi_tgt_gen_write_log(struct file *file, const char __user
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
if (down_interruptible(&scst_proc_mutex) != 0) {
|
||||
if (down_interruptible(&scst_log_mutex) != 0) {
|
||||
res = -EINTR;
|
||||
goto out;
|
||||
}
|
||||
@@ -364,7 +366,7 @@ static int scst_proc_scsi_tgt_gen_write_log(struct file *file, const char __user
|
||||
res = scst_proc_log_entry_write(file, buf, length,
|
||||
&trace_flag, SCST_DEFAULT_LOG_FLAGS, scst_proc_local_trace_tbl);
|
||||
|
||||
up(&scst_proc_mutex);
|
||||
up(&scst_log_mutex);
|
||||
|
||||
out:
|
||||
TRACE_EXIT_RES(res);
|
||||
@@ -1882,14 +1884,14 @@ static int log_info_show(struct seq_file *seq, void *v)
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
if (down_interruptible(&scst_proc_mutex) != 0) {
|
||||
if (down_interruptible(&scst_log_mutex) != 0) {
|
||||
res = -EINTR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
res = scst_proc_log_entry_read(seq, trace_flag, scst_proc_local_trace_tbl);
|
||||
|
||||
up(&scst_proc_mutex);
|
||||
up(&scst_log_mutex);
|
||||
|
||||
out:
|
||||
TRACE_EXIT_RES(res);
|
||||
|
||||
+168
-182
@@ -144,7 +144,8 @@ out_redirect:
|
||||
} else {
|
||||
unsigned long flags;
|
||||
spin_lock_irqsave(&scst_init_lock, flags);
|
||||
TRACE_MGMT_DBG("Adding cmd %p to init cmd list", cmd);
|
||||
TRACE_MGMT_DBG("Adding cmd %p to init cmd list (scst_cmd_count "
|
||||
"%d)", cmd, atomic_read(&scst_cmd_count));
|
||||
list_add_tail(&cmd->cmd_list_entry, &scst_init_cmd_list);
|
||||
if (test_bit(SCST_CMD_ABORTED, &cmd->cmd_flags))
|
||||
scst_init_poll_cnt++;
|
||||
@@ -974,161 +975,8 @@ void scst_rx_data(struct scst_cmd *cmd, int status, int pref_context)
|
||||
return;
|
||||
}
|
||||
|
||||
/* No locks supposed to be held */
|
||||
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;
|
||||
int dbl_ua_possible, ua_sent = 0;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
/* If we had a internal bus reset behind us, set the command error UA */
|
||||
if ((dev->scsi_dev != NULL) &&
|
||||
unlikely(cmd->host_status == DID_RESET) &&
|
||||
scst_is_ua_command(cmd))
|
||||
{
|
||||
TRACE(TRACE_MGMT, "DID_RESET: was_reset=%d host_status=%x",
|
||||
dev->scsi_dev->was_reset, cmd->host_status);
|
||||
scst_set_cmd_error(cmd,
|
||||
SCST_LOAD_SENSE(scst_sense_reset_UA));
|
||||
/* just in case */
|
||||
cmd->ua_ignore = 0;
|
||||
/* It looks like it is safe to clear was_reset here */
|
||||
dev->scsi_dev->was_reset = 0;
|
||||
smp_mb();
|
||||
}
|
||||
|
||||
if (rq_sense != NULL) {
|
||||
sense_valid = SCST_SENSE_VALID(rq_sense);
|
||||
if (sense_valid) {
|
||||
/*
|
||||
* We checked that rq_sense_len < sizeof(cmd->sense_buffer)
|
||||
* in init_scst()
|
||||
*/
|
||||
memcpy(cmd->sense_buffer, rq_sense, rq_sense_len);
|
||||
memset(&cmd->sense_buffer[rq_sense_len], 0,
|
||||
sizeof(cmd->sense_buffer) - rq_sense_len);
|
||||
}
|
||||
} else
|
||||
sense_valid = SCST_SENSE_VALID(cmd->sense_buffer);
|
||||
|
||||
dbl_ua_possible = dev->dev_double_ua_possible;
|
||||
TRACE_DBG("cmd %p dbl_ua_possible %d", cmd, dbl_ua_possible);
|
||||
if (unlikely(dbl_ua_possible)) {
|
||||
spin_lock_bh(&dev->dev_lock);
|
||||
barrier(); /* to reread dev_double_ua_possible */
|
||||
dbl_ua_possible = dev->dev_double_ua_possible;
|
||||
if (dbl_ua_possible)
|
||||
ua_sent = dev->dev_reset_ua_sent;
|
||||
else
|
||||
spin_unlock_bh(&dev->dev_lock);
|
||||
}
|
||||
|
||||
if (sense_valid) {
|
||||
TRACE_BUFF_FLAG(TRACE_SCSI, "Sense", cmd->sense_buffer,
|
||||
sizeof(cmd->sense_buffer));
|
||||
/* Check Unit Attention Sense Key */
|
||||
if (cmd->sense_buffer[2] == UNIT_ATTENTION) {
|
||||
if (cmd->sense_buffer[12] == SCST_SENSE_ASC_UA_RESET) {
|
||||
if (dbl_ua_possible)
|
||||
{
|
||||
if (ua_sent) {
|
||||
TRACE(TRACE_MGMT, "%s",
|
||||
"Double UA detected");
|
||||
/* Do retry */
|
||||
TRACE(TRACE_MGMT, "Retrying cmd %p "
|
||||
"(tag %d)", cmd, cmd->tag);
|
||||
cmd->status = 0;
|
||||
cmd->msg_status = 0;
|
||||
cmd->host_status = DID_OK;
|
||||
cmd->driver_status = 0;
|
||||
memset(cmd->sense_buffer, 0,
|
||||
sizeof(cmd->sense_buffer));
|
||||
cmd->retry = 1;
|
||||
*next_state = SCST_CMD_STATE_SEND_TO_MIDLEV;
|
||||
/*
|
||||
* Dev is still blocked by this cmd, so
|
||||
* it's OK to clear SCST_DEV_SERIALIZED
|
||||
* here.
|
||||
*/
|
||||
dev->dev_double_ua_possible = 0;
|
||||
dev->dev_serialized = 0;
|
||||
dev->dev_reset_ua_sent = 0;
|
||||
goto out_unlock;
|
||||
} else
|
||||
dev->dev_reset_ua_sent = 1;
|
||||
}
|
||||
}
|
||||
if (cmd->ua_ignore == 0) {
|
||||
if (unlikely(dbl_ua_possible)) {
|
||||
__scst_process_UA(dev, cmd,
|
||||
cmd->sense_buffer,
|
||||
sizeof(cmd->sense_buffer), 0);
|
||||
} else {
|
||||
scst_process_UA(dev, cmd,
|
||||
cmd->sense_buffer,
|
||||
sizeof(cmd->sense_buffer), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (unlikely(dbl_ua_possible)) {
|
||||
if (ua_sent && scst_is_ua_command(cmd)) {
|
||||
TRACE_MGMT_DBG("%s", "Clearing dbl_ua_possible flag");
|
||||
dev->dev_double_ua_possible = 0;
|
||||
dev->dev_serialized = 0;
|
||||
dev->dev_reset_ua_sent = 0;
|
||||
}
|
||||
spin_unlock_bh(&dev->dev_lock);
|
||||
}
|
||||
|
||||
out:
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
|
||||
out_unlock:
|
||||
spin_unlock_bh(&dev->dev_lock);
|
||||
goto out;
|
||||
}
|
||||
|
||||
static int scst_check_auto_sense(struct scst_cmd *cmd)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
if (unlikely(cmd->status == SAM_STAT_CHECK_CONDITION) &&
|
||||
(!SCST_SENSE_VALID(cmd->sense_buffer) ||
|
||||
SCST_NO_SENSE(cmd->sense_buffer)))
|
||||
{
|
||||
TRACE(TRACE_SCSI|TRACE_MINOR, "CHECK_CONDITION, but no sense: "
|
||||
"cmd->status=%x, cmd->msg_status=%x, "
|
||||
"cmd->host_status=%x, cmd->driver_status=%x", cmd->status,
|
||||
cmd->msg_status, cmd->host_status, cmd->driver_status);
|
||||
res = 1;
|
||||
} else if (unlikely(cmd->host_status)) {
|
||||
if ((cmd->host_status == DID_REQUEUE) ||
|
||||
(cmd->host_status == DID_IMM_RETRY) ||
|
||||
(cmd->host_status == DID_SOFT_ERROR)) {
|
||||
scst_set_busy(cmd);
|
||||
} else {
|
||||
TRACE(TRACE_SCSI|TRACE_MINOR, "Host status %x "
|
||||
"received, returning HARDWARE ERROR instead",
|
||||
cmd->host_status);
|
||||
scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_hardw_error));
|
||||
}
|
||||
}
|
||||
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
static void scst_do_cmd_done(struct scst_cmd *cmd, int result,
|
||||
const uint8_t *rq_sense, int rq_sense_len, int resid,
|
||||
int *next_state)
|
||||
const uint8_t *rq_sense, int rq_sense_len, int resid)
|
||||
{
|
||||
unsigned char type;
|
||||
|
||||
@@ -1148,6 +996,14 @@ static void scst_do_cmd_done(struct scst_cmd *cmd, int result,
|
||||
scst_set_resp_data_len(cmd, cmd->resp_data_len - resid);
|
||||
}
|
||||
|
||||
/*
|
||||
* We checked that rq_sense_len < sizeof(cmd->sense_buffer)
|
||||
* in init_scst()
|
||||
*/
|
||||
memcpy(cmd->sense_buffer, rq_sense, rq_sense_len);
|
||||
memset(&cmd->sense_buffer[rq_sense_len], 0,
|
||||
sizeof(cmd->sense_buffer) - rq_sense_len);
|
||||
|
||||
TRACE(TRACE_SCSI, "result=%x, cmd->status=%x, resid=%d, "
|
||||
"cmd->msg_status=%x, cmd->host_status=%x, "
|
||||
"cmd->driver_status=%x", result, cmd->status, resid,
|
||||
@@ -1170,7 +1026,7 @@ static void scst_do_cmd_done(struct scst_cmd *cmd, int result,
|
||||
if (unlikely(length <= 0)) {
|
||||
PRINT_ERROR_PR("%s: scst_get_buf_first() failed",
|
||||
__func__);
|
||||
goto next;
|
||||
goto out;
|
||||
}
|
||||
if (length > 2 && cmd->cdb[0] == MODE_SENSE) {
|
||||
address[2] |= 0x80; /* Write Protect*/
|
||||
@@ -1181,9 +1037,7 @@ static void scst_do_cmd_done(struct scst_cmd *cmd, int result,
|
||||
scst_put_buf(cmd, address);
|
||||
}
|
||||
|
||||
next:
|
||||
scst_check_sense(cmd, rq_sense, rq_sense_len, next_state);
|
||||
|
||||
out:
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
}
|
||||
@@ -1223,19 +1077,15 @@ static void scst_cmd_done(struct scsi_cmnd *scsi_cmd)
|
||||
{
|
||||
struct scsi_request *req = NULL;
|
||||
struct scst_cmd *cmd;
|
||||
int next_state;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
WARN_ON(in_irq());
|
||||
|
||||
cmd = scst_get_cmd(scsi_cmd, &req);
|
||||
if (cmd == NULL)
|
||||
goto out;
|
||||
|
||||
next_state = SCST_CMD_STATE_DEV_DONE;
|
||||
scst_do_cmd_done(cmd, req->sr_result, req->sr_sense_buffer,
|
||||
sizeof(req->sr_sense_buffer), scsi_cmd->resid, &next_state);
|
||||
sizeof(req->sr_sense_buffer), scsi_cmd->resid);
|
||||
|
||||
/* Clear out request structure */
|
||||
req->sr_use_sg = 0;
|
||||
@@ -1247,7 +1097,7 @@ static void scst_cmd_done(struct scsi_cmnd *scsi_cmd)
|
||||
|
||||
scst_release_request(cmd);
|
||||
|
||||
cmd->state = next_state;
|
||||
cmd->state = SCST_CMD_STATE_DEV_DONE;
|
||||
|
||||
scst_proccess_redirect_cmd(cmd,
|
||||
scst_optimize_post_exec_context(cmd, scst_get_context()), 0);
|
||||
@@ -1260,21 +1110,16 @@ out:
|
||||
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());
|
||||
|
||||
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, resid,
|
||||
&next_state);
|
||||
scst_do_cmd_done(cmd, result, sense, SCSI_SENSE_BUFFERSIZE, resid);
|
||||
|
||||
cmd->state = next_state;
|
||||
cmd->state = SCST_CMD_STATE_DEV_DONE;
|
||||
|
||||
scst_proccess_redirect_cmd(cmd,
|
||||
scst_optimize_post_exec_context(cmd, scst_get_context()), 0);
|
||||
@@ -1289,8 +1134,6 @@ static void scst_cmd_done_local(struct scst_cmd *cmd, int next_state)
|
||||
{
|
||||
TRACE_ENTRY();
|
||||
|
||||
sBUG_ON(in_irq());
|
||||
|
||||
scst_dec_on_dev_cmd(cmd, 0);
|
||||
|
||||
if (next_state == SCST_CMD_STATE_DEFAULT)
|
||||
@@ -1325,15 +1168,7 @@ static void scst_cmd_done_local(struct scst_cmd *cmd, int next_state)
|
||||
SCST_LOAD_SENSE(scst_sense_hardw_error));
|
||||
next_state = SCST_CMD_STATE_DEV_DONE;
|
||||
}
|
||||
|
||||
if (scst_check_auto_sense(cmd)) {
|
||||
PRINT_ERROR_PR("CHECK_CONDITION, but no valid sense for "
|
||||
"opcode %d", cmd->cdb[0]);
|
||||
}
|
||||
#endif
|
||||
|
||||
scst_check_sense(cmd, NULL, 0, &next_state);
|
||||
|
||||
cmd->state = next_state;
|
||||
|
||||
scst_proccess_redirect_cmd(cmd,
|
||||
@@ -2052,6 +1887,147 @@ out:
|
||||
return res;
|
||||
}
|
||||
|
||||
/* No locks supposed to be held */
|
||||
static int scst_check_sense(struct scst_cmd *cmd)
|
||||
{
|
||||
int res = 0;
|
||||
int sense_valid;
|
||||
struct scst_device *dev = cmd->dev;
|
||||
int dbl_ua_possible, ua_sent = 0;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
/* If we had a internal bus reset behind us, set the command error UA */
|
||||
if ((dev->scsi_dev != NULL) &&
|
||||
unlikely(cmd->host_status == DID_RESET) &&
|
||||
scst_is_ua_command(cmd))
|
||||
{
|
||||
TRACE(TRACE_MGMT, "DID_RESET: was_reset=%d host_status=%x",
|
||||
dev->scsi_dev->was_reset, cmd->host_status);
|
||||
scst_set_cmd_error(cmd,
|
||||
SCST_LOAD_SENSE(scst_sense_reset_UA));
|
||||
/* just in case */
|
||||
cmd->ua_ignore = 0;
|
||||
/* It looks like it is safe to clear was_reset here */
|
||||
dev->scsi_dev->was_reset = 0;
|
||||
smp_mb();
|
||||
}
|
||||
|
||||
sense_valid = SCST_SENSE_VALID(cmd->sense_buffer);
|
||||
|
||||
dbl_ua_possible = dev->dev_double_ua_possible;
|
||||
TRACE_DBG("cmd %p dbl_ua_possible %d", cmd, dbl_ua_possible);
|
||||
if (unlikely(dbl_ua_possible)) {
|
||||
spin_lock_bh(&dev->dev_lock);
|
||||
barrier(); /* to reread dev_double_ua_possible */
|
||||
dbl_ua_possible = dev->dev_double_ua_possible;
|
||||
if (dbl_ua_possible)
|
||||
ua_sent = dev->dev_reset_ua_sent;
|
||||
else
|
||||
spin_unlock_bh(&dev->dev_lock);
|
||||
}
|
||||
|
||||
if (sense_valid) {
|
||||
TRACE_BUFF_FLAG(TRACE_SCSI, "Sense", cmd->sense_buffer,
|
||||
sizeof(cmd->sense_buffer));
|
||||
/* Check Unit Attention Sense Key */
|
||||
if (cmd->sense_buffer[2] == UNIT_ATTENTION) {
|
||||
if (cmd->sense_buffer[12] == SCST_SENSE_ASC_UA_RESET) {
|
||||
if (dbl_ua_possible)
|
||||
{
|
||||
if (ua_sent) {
|
||||
TRACE(TRACE_MGMT, "%s",
|
||||
"Double UA detected");
|
||||
/* Do retry */
|
||||
TRACE(TRACE_MGMT, "Retrying cmd %p "
|
||||
"(tag %d)", cmd, cmd->tag);
|
||||
cmd->status = 0;
|
||||
cmd->msg_status = 0;
|
||||
cmd->host_status = DID_OK;
|
||||
cmd->driver_status = 0;
|
||||
memset(cmd->sense_buffer, 0,
|
||||
sizeof(cmd->sense_buffer));
|
||||
cmd->retry = 1;
|
||||
cmd->state = SCST_CMD_STATE_SEND_TO_MIDLEV;
|
||||
res = 1;
|
||||
/*
|
||||
* Dev is still blocked by this cmd, so
|
||||
* it's OK to clear SCST_DEV_SERIALIZED
|
||||
* here.
|
||||
*/
|
||||
dev->dev_double_ua_possible = 0;
|
||||
dev->dev_serialized = 0;
|
||||
dev->dev_reset_ua_sent = 0;
|
||||
goto out_unlock;
|
||||
} else
|
||||
dev->dev_reset_ua_sent = 1;
|
||||
}
|
||||
}
|
||||
if (cmd->ua_ignore == 0) {
|
||||
if (unlikely(dbl_ua_possible)) {
|
||||
__scst_process_UA(dev, cmd,
|
||||
cmd->sense_buffer,
|
||||
sizeof(cmd->sense_buffer), 0);
|
||||
} else {
|
||||
scst_process_UA(dev, cmd,
|
||||
cmd->sense_buffer,
|
||||
sizeof(cmd->sense_buffer), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (unlikely(dbl_ua_possible)) {
|
||||
if (ua_sent && scst_is_ua_command(cmd)) {
|
||||
TRACE_MGMT_DBG("%s", "Clearing dbl_ua_possible flag");
|
||||
dev->dev_double_ua_possible = 0;
|
||||
dev->dev_serialized = 0;
|
||||
dev->dev_reset_ua_sent = 0;
|
||||
}
|
||||
spin_unlock_bh(&dev->dev_lock);
|
||||
}
|
||||
|
||||
out:
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
|
||||
out_unlock:
|
||||
spin_unlock_bh(&dev->dev_lock);
|
||||
goto out;
|
||||
}
|
||||
|
||||
static int scst_check_auto_sense(struct scst_cmd *cmd)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
if (unlikely(cmd->status == SAM_STAT_CHECK_CONDITION) &&
|
||||
(!SCST_SENSE_VALID(cmd->sense_buffer) ||
|
||||
SCST_NO_SENSE(cmd->sense_buffer)))
|
||||
{
|
||||
TRACE(TRACE_SCSI|TRACE_MINOR, "CHECK_CONDITION, but no sense: "
|
||||
"cmd->status=%x, cmd->msg_status=%x, "
|
||||
"cmd->host_status=%x, cmd->driver_status=%x", cmd->status,
|
||||
cmd->msg_status, cmd->host_status, cmd->driver_status);
|
||||
res = 1;
|
||||
} else if (unlikely(cmd->host_status)) {
|
||||
if ((cmd->host_status == DID_REQUEUE) ||
|
||||
(cmd->host_status == DID_IMM_RETRY) ||
|
||||
(cmd->host_status == DID_SOFT_ERROR)) {
|
||||
scst_set_busy(cmd);
|
||||
} else {
|
||||
TRACE(TRACE_SCSI|TRACE_MINOR, "Host status %x "
|
||||
"received, returning HARDWARE ERROR instead",
|
||||
cmd->host_status);
|
||||
scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_hardw_error));
|
||||
}
|
||||
}
|
||||
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int scst_done_cmd_check(struct scst_cmd *cmd, int *pres)
|
||||
{
|
||||
int res = 0, rc;
|
||||
@@ -2077,6 +2053,10 @@ static int scst_done_cmd_check(struct scst_cmd *cmd, int *pres)
|
||||
scst_set_cmd_error(cmd,
|
||||
SCST_LOAD_SENSE(scst_sense_hardw_error));
|
||||
}
|
||||
} else if (scst_check_sense(cmd)) {
|
||||
*pres = SCST_CMD_STATE_RES_CONT_SAME;
|
||||
res = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
type = cmd->dev->handler->type;
|
||||
@@ -2420,6 +2400,12 @@ static int scst_finish_cmd(struct scst_cmd *cmd)
|
||||
list_del(&cmd->search_cmd_list_entry);
|
||||
spin_unlock_irq(&cmd->sess->sess_list_lock);
|
||||
|
||||
if (unlikely(test_bit(SCST_CMD_ABORTED, &cmd->cmd_flags))) {
|
||||
TRACE_MGMT_DBG("Aborted cmd %p finished (cmd_ref %d, "
|
||||
"scst_cmd_count %d)", cmd, atomic_read(&cmd->cmd_ref),
|
||||
atomic_read(&scst_cmd_count));
|
||||
}
|
||||
|
||||
scst_cmd_put(cmd);
|
||||
|
||||
res = SCST_CMD_STATE_RES_CONT_NEXT;
|
||||
|
||||
Reference in New Issue
Block a user