summaryrefslogtreecommitdiff
path: root/drivers/scsi
diff options
context:
space:
mode:
authorDouglas Gilbert <dgilbert@interlog.com>2016-05-06 07:40:26 +0300
committerMartin K. Petersen <martin.petersen@oracle.com>2016-05-11 04:22:38 +0300
commitc483739430f107c14b3fa316e9cdd3d1e065864a (patch)
tree62ee9d86957e952bd844e0656e6e58ec70062b24 /drivers/scsi
parent4e51af9ee7afa0ecf4829e7a44f7055db7968e02 (diff)
downloadlinux-c483739430f107c14b3fa316e9cdd3d1e065864a.tar.xz
scsi_debug: add multiple queue support
Add submit_queue parameter (minimum and default: 1; maximum: nr_cpu_ids) that controls how many queues are built, each with their own lock and in_use bit vector. Add statistics parameter which is default off. Signed-off-by: Douglas Gilbert <dgilbert@interlog.com> Reviewed-by: Bart Van Assche <bart.vanassche@sandisk.com> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
Diffstat (limited to 'drivers/scsi')
-rw-r--r--drivers/scsi/scsi_debug.c702
1 files changed, 446 insertions, 256 deletions
diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index e97ddf0574c8..2ee55d54d125 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -135,6 +135,8 @@ static const char *sdebug_version_date = "20160430";
#define DEF_VPD_USE_HOSTNO 1
#define DEF_WRITESAME_LENGTH 0xFFFF
#define DEF_STRICT 0
+#define DEF_STATISTICS false
+#define DEF_SUBMIT_QUEUES 1
#define JDELAY_OVERRIDDEN -9999
#define SDEBUG_LUN_0_VAL 0
@@ -201,20 +203,17 @@ static const char *sdebug_version_date = "20160430";
* or "peripheral device" addressing (value 0) */
#define SAM2_LUN_ADDRESS_METHOD 0
-/* SCSI_DEBUG_CANQUEUE is the maximum number of commands that can be queued
- * (for response) at one time. Can be reduced by max_queue option. Command
- * responses are not queued when jdelay=0 and ndelay=0. The per-device
- * DEF_CMD_PER_LUN can be changed via sysfs:
- * /sys/class/scsi_device/<h:c:t:l>/device/queue_depth but cannot exceed
- * SCSI_DEBUG_CANQUEUE. */
-#define SCSI_DEBUG_CANQUEUE_WORDS 9 /* a WORD is bits in a long */
-#define SCSI_DEBUG_CANQUEUE (SCSI_DEBUG_CANQUEUE_WORDS * BITS_PER_LONG)
+/* SDEBUG_CANQUEUE is the maximum number of commands that can be queued
+ * (for response) per submit queue at one time. Can be reduced by max_queue
+ * option. Command responses are not queued when jdelay=0 and ndelay=0. The
+ * per-device DEF_CMD_PER_LUN can be changed via sysfs:
+ * /sys/class/scsi_device/<h:c:t:l>/device/queue_depth
+ * but cannot exceed SDEBUG_CANQUEUE .
+ */
+#define SDEBUG_CANQUEUE_WORDS 3 /* a WORD is bits in a long */
+#define SDEBUG_CANQUEUE (SDEBUG_CANQUEUE_WORDS * BITS_PER_LONG)
#define DEF_CMD_PER_LUN 255
-#if DEF_CMD_PER_LUN > SCSI_DEBUG_CANQUEUE
-#warning "Expect DEF_CMD_PER_LUN <= SCSI_DEBUG_CANQUEUE"
-#endif
-
#define F_D_IN 1
#define F_D_OUT 2
#define F_D_OUT_MAYBE 4 /* WRITE SAME, NDOB bit */
@@ -245,7 +244,7 @@ struct sdebug_dev_info {
struct sdebug_host_info *sdbg_host;
unsigned long uas_bm[1];
atomic_t num_in_q;
- char stopped; /* TODO: should be atomic */
+ atomic_t stopped;
bool used;
};
@@ -262,23 +261,36 @@ struct sdebug_host_info {
struct sdebug_defer {
struct hrtimer hrt;
struct execute_work ew;
- int qa_indx;
+ int sqa_idx; /* index of sdebug_queue array */
+ int qc_idx; /* index of sdebug_queued_cmd array within sqa_idx */
+ int issuing_cpu;
};
struct sdebug_queued_cmd {
- /* in_use flagged by a bit in queued_in_use_bm[] */
+ /* corresponding bit set in in_use_bm[] in owning struct sdebug_queue
+ * instance indicates this slot is in use.
+ */
struct sdebug_defer *sd_dp;
struct scsi_cmnd *a_cmnd;
+ unsigned int inj_recovered:1;
+ unsigned int inj_transport:1;
+ unsigned int inj_dif:1;
+ unsigned int inj_dix:1;
+ unsigned int inj_short:1;
};
-struct sdebug_scmd_extra_t {
- bool inj_recovered;
- bool inj_transport;
- bool inj_dif;
- bool inj_dix;
- bool inj_short;
+struct sdebug_queue {
+ struct sdebug_queued_cmd qc_arr[SDEBUG_CANQUEUE];
+ unsigned long in_use_bm[SDEBUG_CANQUEUE_WORDS];
+ spinlock_t qc_lock;
+ atomic_t blocked; /* to temporarily stop more being queued */
};
+static atomic_t sdebug_cmnd_count; /* number of incoming commands */
+static atomic_t sdebug_completions; /* count of deferred completions */
+static atomic_t sdebug_miss_cpus; /* submission + completion cpus differ */
+static atomic_t sdebug_a_tsf; /* 'almost task set full' counter */
+
struct opcode_info_t {
u8 num_attached; /* 0 if this is it (i.e. a leaf); use 0xff */
/* for terminating element */
@@ -326,6 +338,7 @@ enum sdeb_opcode_index {
SDEB_I_LAST_ELEMENT = 30, /* keep this last */
};
+
static const unsigned char opcode_ind_arr[256] = {
/* 0x0; 0x0->0x1f: 6 byte cdbs */
SDEB_I_TEST_UNIT_READY, SDEB_I_REZERO_UNIT, 0, SDEB_I_REQUEST_SENSE,
@@ -563,7 +576,7 @@ static int sdebug_fake_rw = DEF_FAKE_RW;
static unsigned int sdebug_guard = DEF_GUARD;
static int sdebug_lowest_aligned = DEF_LOWEST_ALIGNED;
static int sdebug_max_luns = DEF_MAX_LUNS;
-static int sdebug_max_queue = SCSI_DEBUG_CANQUEUE;
+static int sdebug_max_queue = SDEBUG_CANQUEUE; /* per submit queue */
static atomic_t retired_max_queue; /* if > 0 then was prior max_queue */
static int sdebug_ndelay = DEF_NDELAY; /* if > 0 then unit is nanoseconds */
static int sdebug_no_lun_0 = DEF_NO_LUN_0;
@@ -594,10 +607,8 @@ static bool sdebug_strict = DEF_STRICT;
static bool sdebug_any_injecting_opt;
static bool sdebug_verbose;
static bool have_dif_prot;
-
-static atomic_t sdebug_cmnd_count;
-static atomic_t sdebug_completions;
-static atomic_t sdebug_a_tsf; /* counter of 'almost' TSFs */
+static bool sdebug_statistics = DEF_STATISTICS;
+static bool sdebug_mq_active;
static unsigned int sdebug_store_sectors;
static sector_t sdebug_capacity; /* in sectors */
@@ -625,10 +636,9 @@ static int dix_writes;
static int dix_reads;
static int dif_errors;
-static struct sdebug_queued_cmd queued_arr[SCSI_DEBUG_CANQUEUE];
-static unsigned long queued_in_use_bm[SCSI_DEBUG_CANQUEUE_WORDS];
+static int submit_queues = DEF_SUBMIT_QUEUES; /* > 1 for multi-queue (mq) */
+static struct sdebug_queue *sdebug_q_arr; /* ptr to array of submit queues */
-static DEFINE_SPINLOCK(queued_arr_lock);
static DEFINE_RWLOCK(atomic_rw);
static char sdebug_proc_name[] = MY_NAME;
@@ -1428,16 +1438,15 @@ static int resp_start_stop(struct scsi_cmnd * scp,
struct sdebug_dev_info * devip)
{
unsigned char *cmd = scp->cmnd;
- int power_cond, start;
+ int power_cond, stop;
power_cond = (cmd[4] & 0xf0) >> 4;
if (power_cond) {
mk_sense_invalid_fld(scp, SDEB_IN_CDB, 4, 7);
return check_condition_result;
}
- start = cmd[4] & 1;
- if (start == devip->stopped)
- devip->stopped = !start;
+ stop = !(cmd[4] & 1);
+ atomic_xchg(&devip->stopped, stop);
return 0;
}
@@ -2450,6 +2459,7 @@ static int prot_verify_read(struct scsi_cmnd *SCpnt, sector_t start_sec,
static int resp_read_dt0(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
{
u8 *cmd = scp->cmnd;
+ struct sdebug_queued_cmd *sqcp;
u64 lba;
u32 num;
u32 ei_lba;
@@ -2509,11 +2519,14 @@ static int resp_read_dt0(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
"to DIF device\n");
}
if (unlikely(sdebug_any_injecting_opt)) {
- struct sdebug_scmd_extra_t *ep = scsi_cmd_priv(scp);
+ sqcp = (struct sdebug_queued_cmd *)scp->host_scribble;
- if (ep->inj_short)
- num /= 2;
- }
+ if (sqcp) {
+ if (sqcp->inj_short)
+ num /= 2;
+ }
+ } else
+ sqcp = NULL;
/* inline check_device_access_params() */
if (unlikely(lba + num > sdebug_capacity)) {
@@ -2563,22 +2576,20 @@ static int resp_read_dt0(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
scsi_in(scp)->resid = scsi_bufflen(scp) - ret;
- if (unlikely(sdebug_any_injecting_opt)) {
- struct sdebug_scmd_extra_t *ep = scsi_cmd_priv(scp);
-
- if (ep->inj_recovered) {
+ if (unlikely(sqcp)) {
+ if (sqcp->inj_recovered) {
mk_sense_buffer(scp, RECOVERED_ERROR,
THRESHOLD_EXCEEDED, 0);
return check_condition_result;
- } else if (ep->inj_transport) {
+ } else if (sqcp->inj_transport) {
mk_sense_buffer(scp, ABORTED_COMMAND,
TRANSPORT_PROBLEM, ACK_NAK_TO);
return check_condition_result;
- } else if (ep->inj_dif) {
+ } else if (sqcp->inj_dif) {
/* Logical block guard check failed */
mk_sense_buffer(scp, ABORTED_COMMAND, 0x10, 1);
return illegal_condition_result;
- } else if (ep->inj_dix) {
+ } else if (sqcp->inj_dix) {
mk_sense_buffer(scp, ILLEGAL_REQUEST, 0x10, 1);
return illegal_condition_result;
}
@@ -2851,25 +2862,29 @@ static int resp_write_dt0(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
write_unlock_irqrestore(&atomic_rw, iflags);
if (unlikely(-1 == ret))
return DID_ERROR << 16;
- else if (sdebug_verbose && (ret < (num * sdebug_sector_size)))
+ else if (unlikely(sdebug_verbose &&
+ (ret < (num * sdebug_sector_size))))
sdev_printk(KERN_INFO, scp->device,
"%s: write: cdb indicated=%u, IO sent=%d bytes\n",
my_name, num * sdebug_sector_size, ret);
if (unlikely(sdebug_any_injecting_opt)) {
- struct sdebug_scmd_extra_t *ep = scsi_cmd_priv(scp);
+ struct sdebug_queued_cmd *sqcp =
+ (struct sdebug_queued_cmd *)scp->host_scribble;
- if (ep->inj_recovered) {
- mk_sense_buffer(scp, RECOVERED_ERROR,
- THRESHOLD_EXCEEDED, 0);
- return check_condition_result;
- } else if (ep->inj_dif) {
- /* Logical block guard check failed */
- mk_sense_buffer(scp, ABORTED_COMMAND, 0x10, 1);
- return illegal_condition_result;
- } else if (ep->inj_dix) {
- mk_sense_buffer(scp, ILLEGAL_REQUEST, 0x10, 1);
- return illegal_condition_result;
+ if (sqcp) {
+ if (sqcp->inj_recovered) {
+ mk_sense_buffer(scp, RECOVERED_ERROR,
+ THRESHOLD_EXCEEDED, 0);
+ return check_condition_result;
+ } else if (sqcp->inj_dif) {
+ /* Logical block guard check failed */
+ mk_sense_buffer(scp, ABORTED_COMMAND, 0x10, 1);
+ return illegal_condition_result;
+ } else if (sqcp->inj_dix) {
+ mk_sense_buffer(scp, ILLEGAL_REQUEST, 0x10, 1);
+ return illegal_condition_result;
+ }
}
}
return 0;
@@ -3360,28 +3375,53 @@ static int resp_xdwriteread_10(struct scsi_cmnd *scp,
return resp_xdwriteread(scp, lba, num, devip);
}
-/* Queued command completions converge here. */
+static struct sdebug_queue *get_queue(struct scsi_cmnd *cmnd)
+{
+ struct sdebug_queue *sqp = sdebug_q_arr;
+
+ if (sdebug_mq_active) {
+ u32 tag = blk_mq_unique_tag(cmnd->request);
+ u16 hwq = blk_mq_unique_tag_to_hwq(tag);
+
+ if (unlikely(hwq >= submit_queues)) {
+ pr_warn("Unexpected hwq=%d, apply modulo\n", hwq);
+ hwq %= submit_queues;
+ }
+ pr_debug("tag=%u, hwq=%d\n", tag, hwq);
+ return sqp + hwq;
+ } else
+ return sqp;
+}
+
+/* Queued (deferred) command completions converge here. */
static void sdebug_q_cmd_complete(struct sdebug_defer *sd_dp)
{
- int qa_indx;
+ int qc_idx;
int retiring = 0;
unsigned long iflags;
+ struct sdebug_queue *sqp;
struct sdebug_queued_cmd *sqcp;
struct scsi_cmnd *scp;
struct sdebug_dev_info *devip;
- atomic_inc(&sdebug_completions);
- qa_indx = sd_dp->qa_indx;
- if (unlikely((qa_indx < 0) || (qa_indx >= SCSI_DEBUG_CANQUEUE))) {
- pr_err("wild qa_indx=%d\n", qa_indx);
+ qc_idx = sd_dp->qc_idx;
+ sqp = sdebug_q_arr + sd_dp->sqa_idx;
+ if (sdebug_statistics) {
+ atomic_inc(&sdebug_completions);
+ if (raw_smp_processor_id() != sd_dp->issuing_cpu)
+ atomic_inc(&sdebug_miss_cpus);
+ }
+ if (unlikely((qc_idx < 0) || (qc_idx >= SDEBUG_CANQUEUE))) {
+ pr_err("wild qc_idx=%d\n", qc_idx);
return;
}
- spin_lock_irqsave(&queued_arr_lock, iflags);
- sqcp = &queued_arr[qa_indx];
+ spin_lock_irqsave(&sqp->qc_lock, iflags);
+ sqcp = &sqp->qc_arr[qc_idx];
scp = sqcp->a_cmnd;
if (unlikely(scp == NULL)) {
- spin_unlock_irqrestore(&queued_arr_lock, iflags);
- pr_err("scp is NULL\n");
+ spin_unlock_irqrestore(&sqp->qc_lock, iflags);
+ pr_err("scp is NULL, sqa_idx=%d, qc_idx=%d\n",
+ sd_dp->sqa_idx, qc_idx);
return;
}
devip = (struct sdebug_dev_info *)scp->device->hostdata;
@@ -3393,8 +3433,8 @@ static void sdebug_q_cmd_complete(struct sdebug_defer *sd_dp)
retiring = 1;
sqcp->a_cmnd = NULL;
- if (unlikely(!test_and_clear_bit(qa_indx, queued_in_use_bm))) {
- spin_unlock_irqrestore(&queued_arr_lock, iflags);
+ if (unlikely(!test_and_clear_bit(qc_idx, sqp->in_use_bm))) {
+ spin_unlock_irqrestore(&sqp->qc_lock, iflags);
pr_err("Unexpected completion\n");
return;
}
@@ -3403,18 +3443,18 @@ static void sdebug_q_cmd_complete(struct sdebug_defer *sd_dp)
int k, retval;
retval = atomic_read(&retired_max_queue);
- if (qa_indx >= retval) {
- spin_unlock_irqrestore(&queued_arr_lock, iflags);
+ if (qc_idx >= retval) {
+ spin_unlock_irqrestore(&sqp->qc_lock, iflags);
pr_err("index %d too large\n", retval);
return;
}
- k = find_last_bit(queued_in_use_bm, retval);
+ k = find_last_bit(sqp->in_use_bm, retval);
if ((k < sdebug_max_queue) || (k == retval))
atomic_set(&retired_max_queue, 0);
else
atomic_set(&retired_max_queue, k + 1);
}
- spin_unlock_irqrestore(&queued_arr_lock, iflags);
+ spin_unlock_irqrestore(&sqp->qc_lock, iflags);
scp->scsi_done(scp); /* callback to mid level */
}
@@ -3533,47 +3573,53 @@ static void scsi_debug_slave_destroy(struct scsi_device *sdp)
}
}
+static void stop_qc_helper(struct sdebug_defer *sd_dp)
+{
+ if (!sd_dp)
+ return;
+ if ((sdebug_jdelay > 0) || (sdebug_ndelay > 0))
+ hrtimer_cancel(&sd_dp->hrt);
+ else if (sdebug_jdelay < 0)
+ cancel_work_sync(&sd_dp->ew.work);
+}
+
/* If @cmnd found deletes its timer or work queue and returns true; else
returns false */
static bool stop_queued_cmnd(struct scsi_cmnd *cmnd)
{
unsigned long iflags;
- int k, qmax, r_qmax;
+ int j, k, qmax, r_qmax;
+ struct sdebug_queue *sqp;
struct sdebug_queued_cmd *sqcp;
struct sdebug_dev_info *devip;
struct sdebug_defer *sd_dp;
- spin_lock_irqsave(&queued_arr_lock, iflags);
- qmax = sdebug_max_queue;
- r_qmax = atomic_read(&retired_max_queue);
- if (r_qmax > qmax)
- qmax = r_qmax;
- for (k = 0; k < qmax; ++k) {
- if (test_bit(k, queued_in_use_bm)) {
- sqcp = &queued_arr[k];
- if (cmnd != sqcp->a_cmnd)
- continue;
- /* found command */
- devip = (struct sdebug_dev_info *)
- cmnd->device->hostdata;
- if (devip)
- atomic_dec(&devip->num_in_q);
- sqcp->a_cmnd = NULL;
- sd_dp = sqcp->sd_dp;
- spin_unlock_irqrestore(&queued_arr_lock,
- iflags);
- if (sdebug_jdelay > 0 || sdebug_ndelay > 0) {
- if (sd_dp)
- hrtimer_cancel(&sd_dp->hrt);
- } else if (sdebug_jdelay < 0) {
- if (sd_dp)
- cancel_work_sync(&sd_dp->ew.work);
+ for (j = 0, sqp = sdebug_q_arr; j < submit_queues; ++j, ++sqp) {
+ spin_lock_irqsave(&sqp->qc_lock, iflags);
+ qmax = sdebug_max_queue;
+ r_qmax = atomic_read(&retired_max_queue);
+ if (r_qmax > qmax)
+ qmax = r_qmax;
+ for (k = 0; k < qmax; ++k) {
+ if (test_bit(k, sqp->in_use_bm)) {
+ sqcp = &sqp->qc_arr[k];
+ if (cmnd != sqcp->a_cmnd)
+ continue;
+ /* found */
+ devip = (struct sdebug_dev_info *)
+ cmnd->device->hostdata;
+ if (devip)
+ atomic_dec(&devip->num_in_q);
+ sqcp->a_cmnd = NULL;
+ sd_dp = sqcp->sd_dp;
+ spin_unlock_irqrestore(&sqp->qc_lock, iflags);
+ stop_qc_helper(sd_dp);
+ clear_bit(k, sqp->in_use_bm);
+ return true;
}
- clear_bit(k, queued_in_use_bm);
- return true;
}
+ spin_unlock_irqrestore(&sqp->qc_lock, iflags);
}
- spin_unlock_irqrestore(&queued_arr_lock, iflags);
return false;
}
@@ -3581,48 +3627,48 @@ static bool stop_queued_cmnd(struct scsi_cmnd *cmnd)
static void stop_all_queued(void)
{
unsigned long iflags;
- int k;
+ int j, k;
+ struct sdebug_queue *sqp;
struct sdebug_queued_cmd *sqcp;
struct sdebug_dev_info *devip;
struct sdebug_defer *sd_dp;
- spin_lock_irqsave(&queued_arr_lock, iflags);
- for (k = 0; k < SCSI_DEBUG_CANQUEUE; ++k) {
- if (test_bit(k, queued_in_use_bm)) {
- sqcp = &queued_arr[k];
- if (NULL == sqcp->a_cmnd)
- continue;
- devip = (struct sdebug_dev_info *)
- sqcp->a_cmnd->device->hostdata;
- if (devip)
- atomic_dec(&devip->num_in_q);
- sqcp->a_cmnd = NULL;
- sd_dp = sqcp->sd_dp;
- spin_unlock_irqrestore(&queued_arr_lock, iflags);
- if (sdebug_jdelay > 0 || sdebug_ndelay > 0) {
- if (sd_dp)
- hrtimer_cancel(&sd_dp->hrt);
- } else if (sdebug_jdelay < 0) {
- if (sd_dp)
- cancel_work_sync(&sd_dp->ew.work);
+ for (j = 0, sqp = sdebug_q_arr; j < submit_queues; ++j, ++sqp) {
+ spin_lock_irqsave(&sqp->qc_lock, iflags);
+ for (k = 0; k < SDEBUG_CANQUEUE; ++k) {
+ if (test_bit(k, sqp->in_use_bm)) {
+ sqcp = &sqp->qc_arr[k];
+ if (sqcp->a_cmnd == NULL)
+ continue;
+ devip = (struct sdebug_dev_info *)
+ sqcp->a_cmnd->device->hostdata;
+ if (devip)
+ atomic_dec(&devip->num_in_q);
+ sqcp->a_cmnd = NULL;
+ sd_dp = sqcp->sd_dp;
+ spin_unlock_irqrestore(&sqp->qc_lock, iflags);
+ stop_qc_helper(sd_dp);
+ clear_bit(k, sqp->in_use_bm);
+ spin_lock_irqsave(&sqp->qc_lock, iflags);
}
- clear_bit(k, queued_in_use_bm);
- spin_lock_irqsave(&queued_arr_lock, iflags);
}
+ spin_unlock_irqrestore(&sqp->qc_lock, iflags);
}
- spin_unlock_irqrestore(&queued_arr_lock, iflags);
}
/* Free queued command memory on heap */
static void free_all_queued(void)
{
- int k;
+ int j, k;
+ struct sdebug_queue *sqp;
struct sdebug_queued_cmd *sqcp;
- for (k = 0; k < SCSI_DEBUG_CANQUEUE; ++k) {
- sqcp = &queued_arr[k];
- kfree(sqcp->sd_dp);
- sqcp->sd_dp = NULL;
+ for (j = 0, sqp = sdebug_q_arr; j < submit_queues; ++j, ++sqp) {
+ for (k = 0; k < SDEBUG_CANQUEUE; ++k) {
+ sqcp = &sqp->qc_arr[k];
+ kfree(sqcp->sd_dp);
+ sqcp->sd_dp = NULL;
+ }
}
}
@@ -3801,24 +3847,71 @@ static void __init sdebug_build_parts(unsigned char *ramp,
}
}
+static void block_unblock_all_queues(bool block)
+{
+ int j;
+ struct sdebug_queue *sqp;
+
+ for (j = 0, sqp = sdebug_q_arr; j < submit_queues; ++j, ++sqp)
+ atomic_set(&sqp->blocked, (int)block);
+}
+
+/* Adjust (by rounding down) the sdebug_cmnd_count so abs(every_nth)-1
+ * commands will be processed normally before triggers occur.
+ */
+static void tweak_cmnd_count(void)
+{
+ int count, modulo;
+
+ modulo = abs(sdebug_every_nth);
+ if (modulo < 2)
+ return;
+ block_unblock_all_queues(true);
+ count = atomic_read(&sdebug_cmnd_count);
+ atomic_set(&sdebug_cmnd_count, (count / modulo) * modulo);
+ block_unblock_all_queues(false);
+}
+
+static void clear_queue_stats(void)
+{
+ atomic_set(&sdebug_cmnd_count, 0);
+ atomic_set(&sdebug_completions, 0);
+ atomic_set(&sdebug_miss_cpus, 0);
+ atomic_set(&sdebug_a_tsf, 0);
+}
+
+static void setup_inject(struct sdebug_queue *sqp,
+ struct sdebug_queued_cmd *sqcp)
+{
+ if ((atomic_read(&sdebug_cmnd_count) % abs(sdebug_every_nth)) > 0)
+ return;
+ sqcp->inj_recovered = !!(SDEBUG_OPT_RECOVERED_ERR & sdebug_opts);
+ sqcp->inj_transport = !!(SDEBUG_OPT_TRANSPORT_ERR & sdebug_opts);
+ sqcp->inj_dif = !!(SDEBUG_OPT_DIF_ERR & sdebug_opts);
+ sqcp->inj_dix = !!(SDEBUG_OPT_DIX_ERR & sdebug_opts);
+ sqcp->inj_short = !!(SDEBUG_OPT_SHORT_TRANSFER & sdebug_opts);
+}
+
+/* Complete the processing of the thread that queued a SCSI command to this
+ * driver. It either completes the command by calling cmnd_done() or
+ * schedules a hr timer or work queue then returns 0. Returns
+ * SCSI_MLQUEUE_HOST_BUSY if temporarily out of resources.
+ */
static int schedule_resp(struct scsi_cmnd *cmnd, struct sdebug_dev_info *devip,
int scsi_result, int delta_jiff)
{
unsigned long iflags;
int k, num_in_q, qdepth, inject;
- struct sdebug_queued_cmd *sqcp = NULL;
+ struct sdebug_queue *sqp;
+ struct sdebug_queued_cmd *sqcp;
struct scsi_device *sdp;
struct sdebug_defer *sd_dp;
- if (unlikely(WARN_ON(!cmnd)))
- return SCSI_MLQUEUE_HOST_BUSY;
-
if (unlikely(devip == NULL)) {
if (scsi_result == 0)
scsi_result = DID_NO_CONNECT << 16;
goto respond_in_thread;
}
-
sdp = cmnd->device;
if (unlikely(sdebug_verbose && scsi_result))
@@ -3828,17 +3921,22 @@ static int schedule_resp(struct scsi_cmnd *cmnd, struct sdebug_dev_info *devip,
goto respond_in_thread;
/* schedule the response at a later time if resources permit */
- spin_lock_irqsave(&queued_arr_lock, iflags);
+ sqp = get_queue(cmnd);
+ spin_lock_irqsave(&sqp->qc_lock, iflags);
+ if (unlikely(atomic_read(&sqp->blocked))) {
+ spin_unlock_irqrestore(&sqp->qc_lock, iflags);
+ return SCSI_MLQUEUE_HOST_BUSY;
+ }
num_in_q = atomic_read(&devip->num_in_q);
qdepth = cmnd->device->queue_depth;
inject = 0;
if (unlikely((qdepth > 0) && (num_in_q >= qdepth))) {
if (scsi_result) {
- spin_unlock_irqrestore(&queued_arr_lock, iflags);
+ spin_unlock_irqrestore(&sqp->qc_lock, iflags);
goto respond_in_thread;
} else
scsi_result = device_qfull_result;
- } else if (unlikely((sdebug_every_nth != 0) &&
+ } else if (unlikely(sdebug_every_nth &&
(SDEBUG_OPT_RARE_TSF & sdebug_opts) &&
(scsi_result == 0))) {
if ((num_in_q == (qdepth - 1)) &&
@@ -3850,9 +3948,9 @@ static int schedule_resp(struct scsi_cmnd *cmnd, struct sdebug_dev_info *devip,
}
}
- k = find_first_zero_bit(queued_in_use_bm, sdebug_max_queue);
+ k = find_first_zero_bit(sqp->in_use_bm, sdebug_max_queue);
if (unlikely(k >= sdebug_max_queue)) {
- spin_unlock_irqrestore(&queued_arr_lock, iflags);
+ spin_unlock_irqrestore(&sqp->qc_lock, iflags);
if (scsi_result)
goto respond_in_thread;
else if (SDEBUG_OPT_ALL_TSF & sdebug_opts)
@@ -3868,13 +3966,16 @@ static int schedule_resp(struct scsi_cmnd *cmnd, struct sdebug_dev_info *devip,
else
return SCSI_MLQUEUE_HOST_BUSY;
}
- __set_bit(k, queued_in_use_bm);
+ __set_bit(k, sqp->in_use_bm);
atomic_inc(&devip->num_in_q);
- sqcp = &queued_arr[k];
+ sqcp = &sqp->qc_arr[k];
sqcp->a_cmnd = cmnd;
+ cmnd->host_scribble = (unsigned char *)sqcp;
cmnd->result = scsi_result;
- spin_unlock_irqrestore(&queued_arr_lock, iflags);
sd_dp = sqcp->sd_dp;
+ spin_unlock_irqrestore(&sqp->qc_lock, iflags);
+ if (unlikely(sdebug_every_nth && sdebug_any_injecting_opt))
+ setup_inject(sqp, sqcp);
if (delta_jiff > 0 || sdebug_ndelay > 0) {
ktime_t kt;
@@ -3891,20 +3992,26 @@ static int schedule_resp(struct scsi_cmnd *cmnd, struct sdebug_dev_info *devip,
return SCSI_MLQUEUE_HOST_BUSY;
sqcp->sd_dp = sd_dp;
hrtimer_init(&sd_dp->hrt, CLOCK_MONOTONIC,
- HRTIMER_MODE_REL);
+ HRTIMER_MODE_REL_PINNED);
sd_dp->hrt.function = sdebug_q_cmd_hrt_complete;
- sd_dp->qa_indx = k;
+ sd_dp->sqa_idx = sqp - sdebug_q_arr;
+ sd_dp->qc_idx = k;
}
- hrtimer_start(&sd_dp->hrt, kt, HRTIMER_MODE_REL);
- } else { /* jdelay < 0 */
+ if (sdebug_statistics)
+ sd_dp->issuing_cpu = raw_smp_processor_id();
+ hrtimer_start(&sd_dp->hrt, kt, HRTIMER_MODE_REL_PINNED);
+ } else { /* jdelay < 0, use work queue */
if (NULL == sd_dp) {
sd_dp = kzalloc(sizeof(*sqcp->sd_dp), GFP_ATOMIC);
if (NULL == sd_dp)
return SCSI_MLQUEUE_HOST_BUSY;
sqcp->sd_dp = sd_dp;
- sd_dp->qa_indx = k;
+ sd_dp->sqa_idx = sqp - sdebug_q_arr;
+ sd_dp->qc_idx = k;
INIT_WORK(&sd_dp->ew.work, sdebug_q_cmd_wq_complete);
}
+ if (sdebug_statistics)
+ sd_dp->issuing_cpu = raw_smp_processor_id();
schedule_work(&sd_dp->ew.work);
}
if (unlikely((SDEBUG_OPT_Q_NOISE & sdebug_opts) &&
@@ -3958,7 +4065,9 @@ module_param_named(ptype, sdebug_ptype, int, S_IRUGO | S_IWUSR);
module_param_named(removable, sdebug_removable, bool, S_IRUGO | S_IWUSR);
module_param_named(scsi_level, sdebug_scsi_level, int, S_IRUGO);
module_param_named(sector_size, sdebug_sector_size, int, S_IRUGO);
+module_param_named(statistics, sdebug_statistics, bool, S_IRUGO | S_IWUSR);
module_param_named(strict, sdebug_strict, bool, S_IRUGO | S_IWUSR);
+module_param_named(submit_queues, submit_queues, int, S_IRUGO);
module_param_named(unmap_alignment, sdebug_unmap_alignment, int, S_IRUGO);
module_param_named(unmap_granularity, sdebug_unmap_granularity, int, S_IRUGO);
module_param_named(unmap_max_blocks, sdebug_unmap_max_blocks, int, S_IRUGO);
@@ -4005,7 +4114,9 @@ MODULE_PARM_DESC(ptype, "SCSI peripheral type(def=0[disk])");
MODULE_PARM_DESC(removable, "claim to have removable media (def=0)");
MODULE_PARM_DESC(scsi_level, "SCSI level to simulate(def=6[SPC-4])");
MODULE_PARM_DESC(sector_size, "logical block size in bytes (def=512)");
+MODULE_PARM_DESC(statistics, "collect statistics on commands, queues (def=0)");
MODULE_PARM_DESC(strict, "stricter checks: reserved field in cdb (def=0)");
+MODULE_PARM_DESC(submit_queues, "support for block multi-queue (def=1)");
MODULE_PARM_DESC(unmap_alignment, "lowest aligned thin provisioning lba (def=0)");
MODULE_PARM_DESC(unmap_granularity, "thin provisioning granularity in blocks (def=1)");
MODULE_PARM_DESC(unmap_max_blocks, "max # of blocks can be unmapped in one cmd (def=0xffffffff)");
@@ -4018,10 +4129,17 @@ static char sdebug_info[256];
static const char * scsi_debug_info(struct Scsi_Host * shp)
{
- sprintf(sdebug_info,
- "scsi_debug, version %s [%s], dev_size_mb=%d, opts=0x%x",
- SDEBUG_VERSION, sdebug_version_date, sdebug_dev_size_mb,
- sdebug_opts);
+ int k;
+
+ k = scnprintf(sdebug_info, sizeof(sdebug_info),
+ "%s: version %s [%s], dev_size_mb=%d, opts=0x%x\n",
+ my_name, SDEBUG_VERSION, sdebug_version_date,
+ sdebug_dev_size_mb, sdebug_opts);
+ if (k >= (sizeof(sdebug_info) - 1))
+ return sdebug_info;
+ scnprintf(sdebug_info + k, sizeof(sdebug_info) - k,
+ "%s: submit_queues=%d, statistics=%d\n", my_name,
+ submit_queues, (int)sdebug_statistics);
return sdebug_info;
}
@@ -4043,7 +4161,7 @@ static int scsi_debug_write_info(struct Scsi_Host *host, char *buffer,
sdebug_verbose = !!(SDEBUG_OPT_NOISE & opts);
sdebug_any_injecting_opt = !!(SDEBUG_OPT_ALL_INJECTING & opts);
if (sdebug_every_nth != 0)
- atomic_set(&sdebug_cmnd_count, 0);
+ tweak_cmnd_count();
return length;
}
@@ -4052,39 +4170,43 @@ static int scsi_debug_write_info(struct Scsi_Host *host, char *buffer,
* output are not atomics so might be inaccurate in a busy system. */
static int scsi_debug_show_info(struct seq_file *m, struct Scsi_Host *host)
{
- int f, l;
- char b[32];
-
- if (sdebug_every_nth > 0)
- snprintf(b, sizeof(b), " (curr:%d)",
- ((SDEBUG_OPT_RARE_TSF & sdebug_opts) ?
- atomic_read(&sdebug_a_tsf) :
- atomic_read(&sdebug_cmnd_count)));
- else
- b[0] = '\0';
-
- seq_printf(m, "scsi_debug adapter driver, version %s [%s]\n"
- "num_tgts=%d, shared (ram) size=%d MB, opts=0x%x, "
- "every_nth=%d%s\n"
- "delay=%d, ndelay=%d, max_luns=%d, q_completions=%d\n"
- "sector_size=%d bytes, cylinders=%d, heads=%d, sectors=%d\n"
- "command aborts=%d; RESETs: device=%d, target=%d, bus=%d, "
- "host=%d\ndix_reads=%d dix_writes=%d dif_errors=%d "
- "usec_in_jiffy=%lu\n",
- SDEBUG_VERSION, sdebug_version_date,
- sdebug_num_tgts, sdebug_dev_size_mb, sdebug_opts,
- sdebug_every_nth, b, sdebug_jdelay, sdebug_ndelay,
- sdebug_max_luns, atomic_read(&sdebug_completions),
- sdebug_sector_size, sdebug_cylinders_per, sdebug_heads,
- sdebug_sectors_per, num_aborts, num_dev_resets,
- num_target_resets, num_bus_resets, num_host_resets,
- dix_reads, dix_writes, dif_errors, TICK_NSEC / 1000);
-
- f = find_first_bit(queued_in_use_bm, sdebug_max_queue);
- if (f != sdebug_max_queue) {
- l = find_last_bit(queued_in_use_bm, sdebug_max_queue);
- seq_printf(m, " %s BUSY: first,last bits set: %d,%d\n",
- "queued_in_use_bm", f, l);
+ int f, j, l;
+ struct sdebug_queue *sqp;
+
+ seq_printf(m, "scsi_debug adapter driver, version %s [%s]\n",
+ SDEBUG_VERSION, sdebug_version_date);
+ seq_printf(m, "num_tgts=%d, %ssize=%d MB, opts=0x%x, every_nth=%d\n",
+ sdebug_num_tgts, "shared (ram) ", sdebug_dev_size_mb,
+ sdebug_opts, sdebug_every_nth);
+ seq_printf(m, "delay=%d, ndelay=%d, max_luns=%d, sector_size=%d %s\n",
+ sdebug_jdelay, sdebug_ndelay, sdebug_max_luns,
+ sdebug_sector_size, "bytes");
+ seq_printf(m, "cylinders=%d, heads=%d, sectors=%d, command aborts=%d\n",
+ sdebug_cylinders_per, sdebug_heads, sdebug_sectors_per,
+ num_aborts);
+ seq_printf(m, "RESETs: device=%d, target=%d, bus=%d, host=%d\n",
+ num_dev_resets, num_target_resets, num_bus_resets,
+ num_host_resets);
+ seq_printf(m, "dix_reads=%d, dix_writes=%d, dif_errors=%d\n",
+ dix_reads, dix_writes, dif_errors);
+ seq_printf(m, "usec_in_jiffy=%lu, %s=%d, mq_active=%d\n",
+ TICK_NSEC / 1000, "statistics", sdebug_statistics,
+ sdebug_mq_active);
+ seq_printf(m, "cmnd_count=%d, completions=%d, %s=%d, a_tsf=%d\n",
+ atomic_read(&sdebug_cmnd_count),
+ atomic_read(&sdebug_completions),
+ "miss_cpus", atomic_read(&sdebug_miss_cpus),
+ atomic_read(&sdebug_a_tsf));
+
+ seq_printf(m, "submit_queues=%d\n", submit_queues);
+ for (j = 0, sqp = sdebug_q_arr; j < submit_queues; ++j, ++sqp) {
+ seq_printf(m, " queue %d:\n", j);
+ f = find_first_bit(sqp->in_use_bm, sdebug_max_queue);
+ if (f != sdebug_max_queue) {
+ l = find_last_bit(sqp->in_use_bm, sdebug_max_queue);
+ seq_printf(m, " in_use_bm BUSY: %s: %d,%d\n",
+ "first,last bits", f, l);
+ }
}
return 0;
}
@@ -4093,7 +4215,9 @@ static ssize_t delay_show(struct device_driver *ddp, char *buf)
{
return scnprintf(buf, PAGE_SIZE, "%d\n", sdebug_jdelay);
}
-/* Returns -EBUSY if jdelay is being changed and commands are queued */
+/* Returns -EBUSY if jdelay is being changed and commands are queued. The unit
+ * of delay is jiffies.
+ */
static ssize_t delay_store(struct device_driver *ddp, const char *buf,
size_t count)
{
@@ -4102,21 +4226,27 @@ static ssize_t delay_store(struct device_driver *ddp, const char *buf,
if (count > 0 && sscanf(buf, "%d", &jdelay) == 1) {
res = count;
if (sdebug_jdelay != jdelay) {
- unsigned long iflags;
- int k;
-
- spin_lock_irqsave(&queued_arr_lock, iflags);
- k = find_first_bit(queued_in_use_bm, sdebug_max_queue);
- if (k != sdebug_max_queue)
- res = -EBUSY; /* have queued commands */
- else {
+ int j, k;
+ struct sdebug_queue *sqp;
+
+ block_unblock_all_queues(true);
+ for (j = 0, sqp = sdebug_q_arr; j < submit_queues;
+ ++j, ++sqp) {
+ k = find_first_bit(sqp->in_use_bm,
+ sdebug_max_queue);
+ if (k != sdebug_max_queue) {
+ res = -EBUSY; /* queued commands */
+ break;
+ }
+ }
+ if (res > 0) {
/* make sure sdebug_defer instances get
* re-allocated for new delay variant */
free_all_queued();
sdebug_jdelay = jdelay;
sdebug_ndelay = 0;
}
- spin_unlock_irqrestore(&queued_arr_lock, iflags);
+ block_unblock_all_queues(false);
}
return res;
}
@@ -4133,18 +4263,26 @@ static ssize_t ndelay_show(struct device_driver *ddp, char *buf)
static ssize_t ndelay_store(struct device_driver *ddp, const char *buf,
size_t count)
{
- unsigned long iflags;
- int ndelay, res, k;
+ int ndelay, res;
if ((count > 0) && (1 == sscanf(buf, "%d", &ndelay)) &&
- (ndelay >= 0) && (ndelay < 1000000000)) {
+ (ndelay >= 0) && (ndelay < (1000 * 1000 * 1000))) {
res = count;
if (sdebug_ndelay != ndelay) {
- spin_lock_irqsave(&queued_arr_lock, iflags);
- k = find_first_bit(queued_in_use_bm, sdebug_max_queue);
- if (k != sdebug_max_queue)
- res = -EBUSY; /* have queued commands */
- else {
+ int j, k;
+ struct sdebug_queue *sqp;
+
+ block_unblock_all_queues(true);
+ for (j = 0, sqp = sdebug_q_arr; j < submit_queues;
+ ++j, ++sqp) {
+ k = find_first_bit(sqp->in_use_bm,
+ sdebug_max_queue);
+ if (k != sdebug_max_queue) {
+ res = -EBUSY; /* queued commands */
+ break;
+ }
+ }
+ if (res > 0) {
/* make sure sdebug_defer instances get
* re-allocated for new delay variant */
free_all_queued();
@@ -4152,7 +4290,7 @@ static ssize_t ndelay_store(struct device_driver *ddp, const char *buf,
sdebug_jdelay = ndelay ? JDELAY_OVERRIDDEN
: DEF_JDELAY;
}
- spin_unlock_irqrestore(&queued_arr_lock, iflags);
+ block_unblock_all_queues(false);
}
return res;
}
@@ -4185,8 +4323,7 @@ opts_done:
sdebug_opts = opts;
sdebug_verbose = !!(SDEBUG_OPT_NOISE & opts);
sdebug_any_injecting_opt = !!(SDEBUG_OPT_ALL_INJECTING & opts);
- atomic_set(&sdebug_cmnd_count, 0);
- atomic_set(&sdebug_a_tsf, 0);
+ tweak_cmnd_count();
return count;
}
static DRIVER_ATTR_RW(opts);
@@ -4316,7 +4453,11 @@ static ssize_t every_nth_store(struct device_driver *ddp, const char *buf,
if ((count > 0) && (1 == sscanf(buf, "%d", &nth))) {
sdebug_every_nth = nth;
- atomic_set(&sdebug_cmnd_count, 0);
+ if (nth && !sdebug_statistics) {
+ pr_info("every_nth needs statistics=1, set it\n");
+ sdebug_statistics = true;
+ }
+ tweak_cmnd_count();
return count;
}
return -EINVAL;
@@ -4371,21 +4512,27 @@ static ssize_t max_queue_show(struct device_driver *ddp, char *buf)
static ssize_t max_queue_store(struct device_driver *ddp, const char *buf,
size_t count)
{
- unsigned long iflags;
- int n, k;
+ int j, n, k, a;
+ struct sdebug_queue *sqp;
if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n > 0) &&
- (n <= SCSI_DEBUG_CANQUEUE)) {
- spin_lock_irqsave(&queued_arr_lock, iflags);
- k = find_last_bit(queued_in_use_bm, SCSI_DEBUG_CANQUEUE);
+ (n <= SDEBUG_CANQUEUE)) {
+ block_unblock_all_queues(true);
+ k = 0;
+ for (j = 0, sqp = sdebug_q_arr; j < submit_queues;
+ ++j, ++sqp) {
+ a = find_last_bit(sqp->in_use_bm, SDEBUG_CANQUEUE);
+ if (a > k)
+ k = a;
+ }
sdebug_max_queue = n;
- if (SCSI_DEBUG_CANQUEUE == k)
+ if (k == SDEBUG_CANQUEUE)
atomic_set(&retired_max_queue, 0);
else if (k >= n)
atomic_set(&retired_max_queue, k + 1);
else
atomic_set(&retired_max_queue, 0);
- spin_unlock_irqrestore(&queued_arr_lock, iflags);
+ block_unblock_all_queues(false);
return count;
}
return -EINVAL;
@@ -4484,12 +4631,40 @@ static ssize_t vpd_use_hostno_store(struct device_driver *ddp, const char *buf,
}
static DRIVER_ATTR_RW(vpd_use_hostno);
+static ssize_t statistics_show(struct device_driver *ddp, char *buf)
+{
+ return scnprintf(buf, PAGE_SIZE, "%d\n", (int)sdebug_statistics);
+}
+static ssize_t statistics_store(struct device_driver *ddp, const char *buf,
+ size_t count)
+{
+ int n;
+
+ if ((count > 0) && (sscanf(buf, "%d", &n) == 1) && (n >= 0)) {
+ if (n > 0)
+ sdebug_statistics = true;
+ else {
+ clear_queue_stats();
+ sdebug_statistics = false;
+ }
+ return count;
+ }
+ return -EINVAL;
+}
+static DRIVER_ATTR_RW(statistics);
+
static ssize_t sector_size_show(struct device_driver *ddp, char *buf)
{
return scnprintf(buf, PAGE_SIZE, "%u\n", sdebug_sector_size);
}
static DRIVER_ATTR_RO(sector_size);
+static ssize_t submit_queues_show(struct device_driver *ddp, char *buf)
+{
+ return scnprintf(buf, PAGE_SIZE, "%d\n", submit_queues);
+}
+static DRIVER_ATTR_RO(submit_queues);
+
static ssize_t dix_show(struct device_driver *ddp, char *buf)
{
return scnprintf(buf, PAGE_SIZE, "%d\n", sdebug_dix);
@@ -4610,6 +4785,8 @@ static struct attribute *sdebug_drv_attrs[] = {
&driver_attr_add_host.attr,
&driver_attr_vpd_use_hostno.attr,
&driver_attr_sector_size.attr,
+ &driver_attr_statistics.attr,
+ &driver_attr_submit_queues.attr,
&driver_attr_dix.attr,
&driver_attr_dif.attr,
&driver_attr_guard.attr,
@@ -4632,8 +4809,6 @@ static int __init scsi_debug_init(void)
int k;
int ret;
- atomic_set(&sdebug_cmnd_count, 0);
- atomic_set(&sdebug_completions, 0);
atomic_set(&retired_max_queue, 0);
if (sdebug_ndelay >= 1000 * 1000 * 1000) {
@@ -4692,6 +4867,17 @@ static int __init scsi_debug_init(void)
return -EINVAL;
}
+ if (submit_queues < 1) {
+ pr_err("submit_queues must be 1 or more\n");
+ return -EINVAL;
+ }
+ sdebug_q_arr = kcalloc(submit_queues, sizeof(struct sdebug_queue),
+ GFP_KERNEL);
+ if (sdebug_q_arr == NULL)
+ return -ENOMEM;
+ for (k = 0; k < submit_queues; ++k)
+ spin_lock_init(&sdebug_q_arr[k].qc_lock);
+
if (sdebug_dev_size_mb < 1)
sdebug_dev_size_mb = 1; /* force minimum 1 MB ramdisk */
sz = (unsigned long)sdebug_dev_size_mb * 1048576;
@@ -4719,7 +4905,8 @@ static int __init scsi_debug_init(void)
fake_storep = vmalloc(sz);
if (NULL == fake_storep) {
pr_err("out of memory, 1\n");
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto free_q_arr;
}
memset(fake_storep, 0, sz);
if (sdebug_num_parts > 0)
@@ -4758,7 +4945,8 @@ static int __init scsi_debug_init(void)
sdebug_unmap_granularity <=
sdebug_unmap_alignment) {
pr_err("ERR: unmap_granularity <= unmap_alignment\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto free_vm;
}
map_size = lba_to_map_index(sdebug_store_sectors - 1) + 1;
@@ -4819,7 +5007,8 @@ free_vm:
vfree(map_storep);
vfree(dif_storep);
vfree(fake_storep);
-
+free_q_arr:
+ kfree(sdebug_q_arr);
return ret;
}
@@ -4837,6 +5026,7 @@ static void __exit scsi_debug_exit(void)
vfree(dif_storep);
vfree(fake_storep);
+ kfree(sdebug_q_arr);
}
device_initcall(scsi_debug_init);
@@ -4925,62 +5115,43 @@ static void sdebug_remove_adapter(void)
static int sdebug_change_qdepth(struct scsi_device *sdev, int qdepth)
{
int num_in_q = 0;
- unsigned long iflags;
struct sdebug_dev_info *devip;
- spin_lock_irqsave(&queued_arr_lock, iflags);
+ block_unblock_all_queues(true);
devip = (struct sdebug_dev_info *)sdev->hostdata;
if (NULL == devip) {
- spin_unlock_irqrestore(&queued_arr_lock, iflags);
+ block_unblock_all_queues(false);
return -ENODEV;
}
num_in_q = atomic_read(&devip->num_in_q);
- spin_unlock_irqrestore(&queued_arr_lock, iflags);
if (qdepth < 1)
qdepth = 1;
- /* allow to exceed max host queued_arr elements for testing */
- if (qdepth > SCSI_DEBUG_CANQUEUE + 10)
- qdepth = SCSI_DEBUG_CANQUEUE + 10;
+ /* allow to exceed max host qc_arr elements for testing */
+ if (qdepth > SDEBUG_CANQUEUE + 10)
+ qdepth = SDEBUG_CANQUEUE + 10;
scsi_change_queue_depth(sdev, qdepth);
if (SDEBUG_OPT_Q_NOISE & sdebug_opts) {
- sdev_printk(KERN_INFO, sdev,
- "%s: qdepth=%d, num_in_q=%d\n",
+ sdev_printk(KERN_INFO, sdev, "%s: qdepth=%d, num_in_q=%d\n",
__func__, qdepth, num_in_q);
}
+ block_unblock_all_queues(false);
return sdev->queue_depth;
}
-static int check_inject(struct scsi_cmnd *scp)
+static bool fake_timeout(struct scsi_cmnd *scp)
{
- struct sdebug_scmd_extra_t *ep = scsi_cmd_priv(scp);
-
- memset(ep, 0, sizeof(struct sdebug_scmd_extra_t));
-
- if (atomic_inc_return(&sdebug_cmnd_count) >= abs(sdebug_every_nth)) {
- atomic_set(&sdebug_cmnd_count, 0);
+ if (0 == (atomic_read(&sdebug_cmnd_count) % abs(sdebug_every_nth))) {
if (sdebug_every_nth < -1)
sdebug_every_nth = -1;
if (SDEBUG_OPT_TIMEOUT & sdebug_opts)
- return 1; /* ignore command causing timeout */
+ return true; /* ignore command causing timeout */
else if (SDEBUG_OPT_MAC_TIMEOUT & sdebug_opts &&
scsi_medium_access_command(scp))
- return 1; /* time out reads and writes */
- if (sdebug_any_injecting_opt) {
- if (SDEBUG_OPT_RECOVERED_ERR & sdebug_opts)
- ep->inj_recovered = true;
- if (SDEBUG_OPT_TRANSPORT_ERR & sdebug_opts)
- ep->inj_transport = true;
- if (SDEBUG_OPT_DIF_ERR & sdebug_opts)
- ep->inj_dif = true;
- if (SDEBUG_OPT_DIX_ERR & sdebug_opts)
- ep->inj_dix = true;
- if (SDEBUG_OPT_SHORT_TRANSFER & sdebug_opts)
- ep->inj_short = true;
- }
+ return true; /* time out reads and writes */
}
- return 0;
+ return false;
}
static int scsi_debug_queuecommand(struct Scsi_Host *shost,
@@ -5001,6 +5172,8 @@ static int scsi_debug_queuecommand(struct Scsi_Host *shost,
bool has_wlun_rl;
scsi_set_resid(scp, 0);
+ if (sdebug_statistics)
+ atomic_inc(&sdebug_cmnd_count);
if (unlikely(sdebug_verbose &&
!(SDEBUG_OPT_NO_CDB_NOISE & sdebug_opts))) {
char b[120];
@@ -5015,7 +5188,13 @@ static int scsi_debug_queuecommand(struct Scsi_Host *shost,
n += scnprintf(b + n, sb - n, "%02x ",
(u32)cmd[k]);
}
- sdev_printk(KERN_INFO, sdp, "%s: cmd %s\n", my_name, b);
+ if (sdebug_mq_active)
+ sdev_printk(KERN_INFO, sdp, "%s: tag=%u, cmd %s\n",
+ my_name, blk_mq_unique_tag(scp->request),
+ b);
+ else
+ sdev_printk(KERN_INFO, sdp, "%s: cmd %s\n", my_name,
+ b);
}
has_wlun_rl = (sdp->lun == SCSI_W_LUN_REPORT_LUNS);
if (unlikely((sdp->lun >= sdebug_max_luns) && !has_wlun_rl))
@@ -5093,7 +5272,7 @@ static int scsi_debug_queuecommand(struct Scsi_Host *shost,
if (errsts)
goto check_cond;
}
- if (unlikely((F_M_ACCESS & flags) && devip->stopped)) {
+ if (unlikely((F_M_ACCESS & flags) && atomic_read(&devip->stopped))) {
mk_sense_buffer(scp, NOT_READY, LOGICAL_UNIT_NOT_READY, 0x2);
if (sdebug_verbose)
sdev_printk(KERN_INFO, sdp, "%s reports: Not ready: "
@@ -5105,7 +5284,7 @@ static int scsi_debug_queuecommand(struct Scsi_Host *shost,
if (sdebug_fake_rw && (F_FAKE_RW & flags))
goto fini;
if (unlikely(sdebug_every_nth)) {
- if (check_inject(scp))
+ if (fake_timeout(scp))
return 0; /* ignore command: make trouble */
}
if (likely(oip->pfp))
@@ -5139,7 +5318,7 @@ static struct scsi_host_template sdebug_driver_template = {
.eh_target_reset_handler = scsi_debug_target_reset,
.eh_bus_reset_handler = scsi_debug_bus_reset,
.eh_host_reset_handler = scsi_debug_host_reset,
- .can_queue = SCSI_DEBUG_CANQUEUE,
+ .can_queue = SDEBUG_CANQUEUE,
.this_id = 7,
.sg_tablesize = SG_MAX_SEGMENTS,
.cmd_per_lun = DEF_CMD_PER_LUN,
@@ -5147,7 +5326,6 @@ static struct scsi_host_template sdebug_driver_template = {
.use_clustering = DISABLE_CLUSTERING,
.module = THIS_MODULE,
.track_queue_depth = 1,
- .cmd_size = sizeof(struct sdebug_scmd_extra_t),
};
static int sdebug_driver_probe(struct device * dev)
@@ -5168,6 +5346,16 @@ static int sdebug_driver_probe(struct device * dev)
error = -ENODEV;
return error;
}
+ if (submit_queues > nr_cpu_ids) {
+ pr_warn("%s: trim submit_queues (was %d) to nr_cpu_ids=%d\n",
+ my_name, submit_queues, nr_cpu_ids);
+ submit_queues = nr_cpu_ids;
+ }
+ /* Decide whether to tell scsi subsystem that we want mq */
+ /* Following should give the same answer for each host */
+ sdebug_mq_active = shost_use_blk_mq(hpnt) && (submit_queues > 1);
+ if (sdebug_mq_active)
+ hpnt->nr_hw_queues = submit_queues;
sdbg_host->shost = hpnt;
*((struct sdebug_host_info **)hpnt->hostdata) = sdbg_host;
@@ -5225,6 +5413,8 @@ static int sdebug_driver_probe(struct device * dev)
sdebug_verbose = !!(SDEBUG_OPT_NOISE & sdebug_opts);
sdebug_any_injecting_opt = !!(SDEBUG_OPT_ALL_INJECTING & sdebug_opts);
+ if (sdebug_every_nth) /* need stats counters for every_nth */
+ sdebug_statistics = true;
error = scsi_add_host(hpnt, &sdbg_host->dev);
if (error) {
pr_err("scsi_add_host failed\n");