Commit 7382f9d8 authored by Douglas Gilbert's avatar Douglas Gilbert Committed by Martin K. Petersen

scsi: scsi_debug: add cmd abort option to every_nth

This patch is motivated by a response in the thread:

  Re: [PATCH 0/5]stop normal completion path entering a timeout req

by Jianchao Wang . It generalizes the error injection of
blk_abort_request() to use scsi_debug's "every_nth" mechanism.  Ref with
original patch to scsi_debug:

https://lore.kernel.org/lkml/a68ad043-26a1-d3d8-2009-504ba4230e0f@oracle.com/

Also convert two vmalloc/memset(0) to vzalloc() calls.
Signed-off-by: default avatarDouglas Gilbert <dgilbert@interlog.com>
Reviewed-by: default avatarBart Van Assche <bart.vanassche@wdc.com>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent 4dc98c19
...@@ -164,29 +164,29 @@ static const char *sdebug_version_date = "20180128"; ...@@ -164,29 +164,29 @@ static const char *sdebug_version_date = "20180128";
#define SDEBUG_OPT_RESET_NOISE 0x2000 #define SDEBUG_OPT_RESET_NOISE 0x2000
#define SDEBUG_OPT_NO_CDB_NOISE 0x4000 #define SDEBUG_OPT_NO_CDB_NOISE 0x4000
#define SDEBUG_OPT_HOST_BUSY 0x8000 #define SDEBUG_OPT_HOST_BUSY 0x8000
#define SDEBUG_OPT_CMD_ABORT 0x10000
#define SDEBUG_OPT_ALL_NOISE (SDEBUG_OPT_NOISE | SDEBUG_OPT_Q_NOISE | \ #define SDEBUG_OPT_ALL_NOISE (SDEBUG_OPT_NOISE | SDEBUG_OPT_Q_NOISE | \
SDEBUG_OPT_RESET_NOISE) SDEBUG_OPT_RESET_NOISE)
#define SDEBUG_OPT_ALL_INJECTING (SDEBUG_OPT_RECOVERED_ERR | \ #define SDEBUG_OPT_ALL_INJECTING (SDEBUG_OPT_RECOVERED_ERR | \
SDEBUG_OPT_TRANSPORT_ERR | \ SDEBUG_OPT_TRANSPORT_ERR | \
SDEBUG_OPT_DIF_ERR | SDEBUG_OPT_DIX_ERR | \ SDEBUG_OPT_DIF_ERR | SDEBUG_OPT_DIX_ERR | \
SDEBUG_OPT_SHORT_TRANSFER | \ SDEBUG_OPT_SHORT_TRANSFER | \
SDEBUG_OPT_HOST_BUSY) SDEBUG_OPT_HOST_BUSY | \
SDEBUG_OPT_CMD_ABORT)
/* When "every_nth" > 0 then modulo "every_nth" commands: /* When "every_nth" > 0 then modulo "every_nth" commands:
* - a missing response is simulated if SDEBUG_OPT_TIMEOUT is set * - a missing response is simulated if SDEBUG_OPT_TIMEOUT is set
* - a RECOVERED_ERROR is simulated on successful read and write * - a RECOVERED_ERROR is simulated on successful read and write
* commands if SDEBUG_OPT_RECOVERED_ERR is set. * commands if SDEBUG_OPT_RECOVERED_ERR is set.
* - a TRANSPORT_ERROR is simulated on successful read and write * - a TRANSPORT_ERROR is simulated on successful read and write
* commands if SDEBUG_OPT_TRANSPORT_ERR is set. * commands if SDEBUG_OPT_TRANSPORT_ERR is set.
* - similarly for DIF_ERR, DIX_ERR, SHORT_TRANSFER, HOST_BUSY and
* CMD_ABORT
* *
* When "every_nth" < 0 then after "- every_nth" commands: * When "every_nth" < 0 then after "- every_nth" commands the selected
* - a missing response is simulated if SDEBUG_OPT_TIMEOUT is set * error will be injected. The error will be injected on every subsequent
* - a RECOVERED_ERROR is simulated on successful read and write * command until some other action occurs; for example, the user writing
* commands if SDEBUG_OPT_RECOVERED_ERR is set. * a new value (other than -1 or 1) to every_nth:
* - a TRANSPORT_ERROR is simulated on successful read and write * echo 0 > /sys/bus/pseudo/drivers/scsi_debug/every_nth
* commands if _DEBUG_OPT_TRANSPORT_ERR is set.
* This will continue on every subsequent command until some other action
* occurs (e.g. the user * writing a new value (other than -1 or 1) to
* every_nth via sysfs).
*/ */
/* As indicated in SAM-5 and SPC-4 Unit Attentions (UAs) are returned in /* As indicated in SAM-5 and SPC-4 Unit Attentions (UAs) are returned in
...@@ -281,6 +281,7 @@ struct sdebug_defer { ...@@ -281,6 +281,7 @@ struct sdebug_defer {
int issuing_cpu; int issuing_cpu;
bool init_hrt; bool init_hrt;
bool init_wq; bool init_wq;
bool aborted; /* true when blk_abort_request() already called */
enum sdeb_defer_type defer_t; enum sdeb_defer_type defer_t;
}; };
...@@ -296,6 +297,7 @@ struct sdebug_queued_cmd { ...@@ -296,6 +297,7 @@ struct sdebug_queued_cmd {
unsigned int inj_dix:1; unsigned int inj_dix:1;
unsigned int inj_short:1; unsigned int inj_short:1;
unsigned int inj_host_busy:1; unsigned int inj_host_busy:1;
unsigned int inj_cmd_abort:1;
}; };
struct sdebug_queue { struct sdebug_queue {
...@@ -3792,6 +3794,7 @@ static struct sdebug_queue *get_queue(struct scsi_cmnd *cmnd) ...@@ -3792,6 +3794,7 @@ static struct sdebug_queue *get_queue(struct scsi_cmnd *cmnd)
/* Queued (deferred) command completions converge here. */ /* Queued (deferred) command completions converge here. */
static void sdebug_q_cmd_complete(struct sdebug_defer *sd_dp) static void sdebug_q_cmd_complete(struct sdebug_defer *sd_dp)
{ {
bool aborted = sd_dp->aborted;
int qc_idx; int qc_idx;
int retiring = 0; int retiring = 0;
unsigned long iflags; unsigned long iflags;
...@@ -3801,6 +3804,8 @@ static void sdebug_q_cmd_complete(struct sdebug_defer *sd_dp) ...@@ -3801,6 +3804,8 @@ static void sdebug_q_cmd_complete(struct sdebug_defer *sd_dp)
struct sdebug_dev_info *devip; struct sdebug_dev_info *devip;
sd_dp->defer_t = SDEB_DEFER_NONE; sd_dp->defer_t = SDEB_DEFER_NONE;
if (unlikely(aborted))
sd_dp->aborted = false;
qc_idx = sd_dp->qc_idx; qc_idx = sd_dp->qc_idx;
sqp = sdebug_q_arr + sd_dp->sqa_idx; sqp = sdebug_q_arr + sd_dp->sqa_idx;
if (sdebug_statistics) { if (sdebug_statistics) {
...@@ -3852,6 +3857,11 @@ static void sdebug_q_cmd_complete(struct sdebug_defer *sd_dp) ...@@ -3852,6 +3857,11 @@ static void sdebug_q_cmd_complete(struct sdebug_defer *sd_dp)
atomic_set(&retired_max_queue, k + 1); atomic_set(&retired_max_queue, k + 1);
} }
spin_unlock_irqrestore(&sqp->qc_lock, iflags); spin_unlock_irqrestore(&sqp->qc_lock, iflags);
if (unlikely(aborted)) {
if (sdebug_verbose)
pr_info("bypassing scsi_done() due to aborted cmd\n");
return;
}
scp->scsi_done(scp); /* callback to mid level */ scp->scsi_done(scp); /* callback to mid level */
} }
...@@ -4312,7 +4322,8 @@ static void setup_inject(struct sdebug_queue *sqp, ...@@ -4312,7 +4322,8 @@ static void setup_inject(struct sdebug_queue *sqp,
if (sdebug_every_nth > 0) if (sdebug_every_nth > 0)
sqcp->inj_recovered = sqcp->inj_transport sqcp->inj_recovered = sqcp->inj_transport
= sqcp->inj_dif = sqcp->inj_dif
= sqcp->inj_dix = sqcp->inj_short = 0; = sqcp->inj_dix = sqcp->inj_short
= sqcp->inj_host_busy = sqcp->inj_cmd_abort = 0;
return; return;
} }
sqcp->inj_recovered = !!(SDEBUG_OPT_RECOVERED_ERR & sdebug_opts); sqcp->inj_recovered = !!(SDEBUG_OPT_RECOVERED_ERR & sdebug_opts);
...@@ -4321,6 +4332,7 @@ static void setup_inject(struct sdebug_queue *sqp, ...@@ -4321,6 +4332,7 @@ static void setup_inject(struct sdebug_queue *sqp,
sqcp->inj_dix = !!(SDEBUG_OPT_DIX_ERR & sdebug_opts); sqcp->inj_dix = !!(SDEBUG_OPT_DIX_ERR & sdebug_opts);
sqcp->inj_short = !!(SDEBUG_OPT_SHORT_TRANSFER & sdebug_opts); sqcp->inj_short = !!(SDEBUG_OPT_SHORT_TRANSFER & sdebug_opts);
sqcp->inj_host_busy = !!(SDEBUG_OPT_HOST_BUSY & sdebug_opts); sqcp->inj_host_busy = !!(SDEBUG_OPT_HOST_BUSY & sdebug_opts);
sqcp->inj_cmd_abort = !!(SDEBUG_OPT_CMD_ABORT & sdebug_opts);
} }
/* Complete the processing of the thread that queued a SCSI command to this /* Complete the processing of the thread that queued a SCSI command to this
...@@ -4458,7 +4470,14 @@ static int schedule_resp(struct scsi_cmnd *cmnd, struct sdebug_dev_info *devip, ...@@ -4458,7 +4470,14 @@ static int schedule_resp(struct scsi_cmnd *cmnd, struct sdebug_dev_info *devip,
if (sdebug_statistics) if (sdebug_statistics)
sd_dp->issuing_cpu = raw_smp_processor_id(); sd_dp->issuing_cpu = raw_smp_processor_id();
sd_dp->defer_t = SDEB_DEFER_WQ; sd_dp->defer_t = SDEB_DEFER_WQ;
if (unlikely(sqcp->inj_cmd_abort))
sd_dp->aborted = true;
schedule_work(&sd_dp->ew.work); schedule_work(&sd_dp->ew.work);
if (unlikely(sqcp->inj_cmd_abort)) {
sdev_printk(KERN_INFO, sdp, "abort request tag %d\n",
cmnd->request->tag);
blk_abort_request(cmnd->request);
}
} }
if (unlikely((SDEBUG_OPT_Q_NOISE & sdebug_opts) && if (unlikely((SDEBUG_OPT_Q_NOISE & sdebug_opts) &&
(scsi_result == device_qfull_result))) (scsi_result == device_qfull_result)))
...@@ -4844,12 +4863,11 @@ static ssize_t fake_rw_store(struct device_driver *ddp, const char *buf, ...@@ -4844,12 +4863,11 @@ static ssize_t fake_rw_store(struct device_driver *ddp, const char *buf,
(unsigned long)sdebug_dev_size_mb * (unsigned long)sdebug_dev_size_mb *
1048576; 1048576;
fake_storep = vmalloc(sz); fake_storep = vzalloc(sz);
if (NULL == fake_storep) { if (NULL == fake_storep) {
pr_err("out of memory, 9\n"); pr_err("out of memory, 9\n");
return -ENOMEM; return -ENOMEM;
} }
memset(fake_storep, 0, sz);
} }
sdebug_fake_rw = n; sdebug_fake_rw = n;
} }
...@@ -5391,13 +5409,12 @@ static int __init scsi_debug_init(void) ...@@ -5391,13 +5409,12 @@ static int __init scsi_debug_init(void)
} }
if (sdebug_fake_rw == 0) { if (sdebug_fake_rw == 0) {
fake_storep = vmalloc(sz); fake_storep = vzalloc(sz);
if (NULL == fake_storep) { if (NULL == fake_storep) {
pr_err("out of memory, 1\n"); pr_err("out of memory, 1\n");
ret = -ENOMEM; ret = -ENOMEM;
goto free_q_arr; goto free_q_arr;
} }
memset(fake_storep, 0, sz);
if (sdebug_num_parts > 0) if (sdebug_num_parts > 0)
sdebug_build_parts(fake_storep, sz); sdebug_build_parts(fake_storep, sz);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment