diff --git a/scst/ChangeLog b/scst/ChangeLog index 2b7e20d69..79b08f4b6 100644 --- a/scst/ChangeLog +++ b/scst/ChangeLog @@ -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, diff --git a/scst/README b/scst/README index 1b229799d..97bff5988 100644 --- a/scst/README +++ b/scst/README @@ -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 diff --git a/scst/ToDo b/scst/ToDo index 755407dd1..daa5d3054 100644 --- a/scst/ToDo +++ b/scst/ToDo @@ -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 diff --git a/scst/src/dev_handlers/scst_vdisk.c b/scst/src/dev_handlers/scst_vdisk.c index 1ffe9ea40..4bde8461a 100644 --- a/scst/src/dev_handlers/scst_vdisk.c +++ b/scst/src/dev_handlers/scst_vdisk.c @@ -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; diff --git a/scst/src/scst.c b/scst/src/scst.c index bf2894b3b..d7db289a1 100644 --- a/scst/src/scst.c +++ b/scst/src/scst.c @@ -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; diff --git a/scst/src/scst_lib.c b/scst/src/scst_lib.c index f5b56fd5b..9c10e0a88 100644 --- a/scst/src/scst_lib.c +++ b/scst/src/scst_lib.c @@ -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)) diff --git a/scst/src/scst_proc.c b/scst/src/scst_proc.c index 66fd9c823..f6116e0b2 100644 --- a/scst/src/scst_proc.c +++ b/scst/src/scst_proc.c @@ -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); diff --git a/scst/src/scst_targ.c b/scst/src/scst_targ.c index 0e320aeb0..49ded0581 100644 --- a/scst/src/scst_targ.c +++ b/scst/src/scst_targ.c @@ -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;