scsi_debug: give unit attention and other errors precedence over TSF

Give existing errors priority over the generation of Task
Set Full (TSF) errors. So that max_queue is not exceeded,
existing errors may be sent back in the invocation thread.
This is done so errors like Unit Attentions are not hidden
and lost by either max_queue exceeded or real/injected
TSFs.

Signed-off-by: Douglas Gilbert <dgilbert@interlog.com>
Reviewed-by: Martin K. Petersen <martin.petersen@oracle.com>
Signed-off-by: Christoph Hellwig <hch@lst.de>
This commit is contained in:
Douglas Gilbert 2014-08-05 12:20:46 +02:00 committed by Christoph Hellwig
parent 01123ef4c3
commit cd62b7dae2
1 changed files with 33 additions and 34 deletions

View File

@ -3006,7 +3006,7 @@ schedule_resp(struct scsi_cmnd *cmnd, struct sdebug_dev_info *devip,
int scsi_result, int delta_jiff) int scsi_result, int delta_jiff)
{ {
unsigned long iflags; unsigned long iflags;
int k, num_in_q, tsf, qdepth, inject; int k, num_in_q, qdepth, inject;
struct sdebug_queued_cmd *sqcp = NULL; struct sdebug_queued_cmd *sqcp = NULL;
struct scsi_device *sdp = cmnd->device; struct scsi_device *sdp = cmnd->device;
@ -3019,55 +3019,48 @@ schedule_resp(struct scsi_cmnd *cmnd, struct sdebug_dev_info *devip,
if ((scsi_result) && (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)) if ((scsi_result) && (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts))
sdev_printk(KERN_INFO, sdp, "%s: non-zero result=0x%x\n", sdev_printk(KERN_INFO, sdp, "%s: non-zero result=0x%x\n",
__func__, scsi_result); __func__, scsi_result);
if (delta_jiff == 0) { if (delta_jiff == 0)
/* using same thread to call back mid-layer */ goto respond_in_thread;
cmnd->result = scsi_result;
cmnd->scsi_done(cmnd);
return 0;
}
/* deferred response cases */ /* schedule the response at a later time if resources permit */
spin_lock_irqsave(&queued_arr_lock, iflags); spin_lock_irqsave(&queued_arr_lock, iflags);
num_in_q = atomic_read(&devip->num_in_q); num_in_q = atomic_read(&devip->num_in_q);
qdepth = cmnd->device->queue_depth; qdepth = cmnd->device->queue_depth;
k = find_first_zero_bit(queued_in_use_bm, scsi_debug_max_queue);
tsf = 0;
inject = 0; inject = 0;
if ((qdepth > 0) && (num_in_q >= qdepth)) if ((qdepth > 0) && (num_in_q >= qdepth)) {
tsf = 1; if (scsi_result) {
else if ((scsi_debug_every_nth != 0) && spin_unlock_irqrestore(&queued_arr_lock, iflags);
(SCSI_DEBUG_OPT_RARE_TSF & scsi_debug_opts)) { goto respond_in_thread;
} else
scsi_result = device_qfull_result;
} else if ((scsi_debug_every_nth != 0) &&
(SCSI_DEBUG_OPT_RARE_TSF & scsi_debug_opts) &&
(scsi_result == 0)) {
if ((num_in_q == (qdepth - 1)) && if ((num_in_q == (qdepth - 1)) &&
(atomic_inc_return(&sdebug_a_tsf) >= (atomic_inc_return(&sdebug_a_tsf) >=
abs(scsi_debug_every_nth))) { abs(scsi_debug_every_nth))) {
atomic_set(&sdebug_a_tsf, 0); atomic_set(&sdebug_a_tsf, 0);
inject = 1; inject = 1;
tsf = 1; scsi_result = device_qfull_result;
} }
} }
/* if (tsf) simulate device reporting SCSI status of TASK SET FULL. k = find_first_zero_bit(queued_in_use_bm, scsi_debug_max_queue);
* Might override existing CHECK CONDITION. */
if (tsf)
scsi_result = device_qfull_result;
if (k >= scsi_debug_max_queue) { if (k >= scsi_debug_max_queue) {
if (SCSI_DEBUG_OPT_ALL_TSF & scsi_debug_opts)
tsf = 1;
spin_unlock_irqrestore(&queued_arr_lock, iflags); spin_unlock_irqrestore(&queued_arr_lock, iflags);
if (scsi_result)
goto respond_in_thread;
else if (SCSI_DEBUG_OPT_ALL_TSF & scsi_debug_opts)
scsi_result = device_qfull_result;
if (SCSI_DEBUG_OPT_Q_NOISE & scsi_debug_opts) if (SCSI_DEBUG_OPT_Q_NOISE & scsi_debug_opts)
sdev_printk(KERN_INFO, sdp, sdev_printk(KERN_INFO, sdp,
"%s: num_in_q=%d, bypass q, %s%s\n", "%s: max_queue=%d exceeded, %s\n",
__func__, num_in_q, __func__, scsi_debug_max_queue,
(inject ? "<inject> " : ""), (scsi_result ? "status: TASK SET FULL" :
(tsf ? "status: TASK SET FULL" : "report: host busy"));
"report: host busy")); if (scsi_result)
if (tsf) { goto respond_in_thread;
/* queued_arr full so respond in same thread */ else
cmnd->result = scsi_result;
cmnd->scsi_done(cmnd);
/* As scsi_done() is called "inline" must return 0 */
return 0;
} else
return SCSI_MLQUEUE_HOST_BUSY; return SCSI_MLQUEUE_HOST_BUSY;
} }
__set_bit(k, queued_in_use_bm); __set_bit(k, queued_in_use_bm);
@ -3117,12 +3110,18 @@ schedule_resp(struct scsi_cmnd *cmnd, struct sdebug_dev_info *devip,
else else
tasklet_schedule(sqcp->tletp); tasklet_schedule(sqcp->tletp);
} }
if (tsf && (SCSI_DEBUG_OPT_Q_NOISE & scsi_debug_opts)) if ((SCSI_DEBUG_OPT_Q_NOISE & scsi_debug_opts) &&
(scsi_result == device_qfull_result))
sdev_printk(KERN_INFO, sdp, sdev_printk(KERN_INFO, sdp,
"%s: num_in_q=%d +1, %s%s\n", __func__, "%s: num_in_q=%d +1, %s%s\n", __func__,
num_in_q, (inject ? "<inject> " : ""), num_in_q, (inject ? "<inject> " : ""),
"status: TASK SET FULL"); "status: TASK SET FULL");
return 0; return 0;
respond_in_thread: /* call back to mid-layer using invocation thread */
cmnd->result = scsi_result;
cmnd->scsi_done(cmnd);
return 0;
} }
/* Note: The following macros create attribute files in the /* Note: The following macros create attribute files in the