mirror of
https://github.com/torvalds/linux.git
synced 2024-11-24 05:02:12 +00:00
scsi: scsi_debug: Implement tur_ms_to_ready parameter
The current driver responds to TEST UNIT READY (TUR) with a GOOD status immediately after a scsi_debug device (LU) is created. This is unrealistic as even SSDs take some time after power-on before accepting media access commands. Add the tur_ms_to_ready parameter whose unit is milliseconds (default 0) and is the period before which a TUR (or any media access command) will set the CHECK CONDITION status with a sense key of NOT READY and an additional sense of "Logical unit is in process of becoming ready". The period starts when each scsi_debug device is created. This patch was prompted by T10 proposal 20-061r2 which was accepted on 2020716. It adds that a TUR in the situation described in the previous paragraph may set the INFO field (or descriptor) in the sense data to the estimated number in milliseconds before a subsequent TUR will yield a GOOD status. This patch follows that advice. Link: https://lore.kernel.org/r/20200724155531.668144-1-dgilbert@interlog.com Reported-by: kernel test robot <lkp@intel.com> Signed-off-by: Douglas Gilbert <dgilbert@interlog.com> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
This commit is contained in:
parent
84905d34f1
commit
fc13638ae9
@ -151,6 +151,7 @@ static const char *sdebug_version_date = "20200710";
|
||||
#define DEF_STRICT 0
|
||||
#define DEF_STATISTICS false
|
||||
#define DEF_SUBMIT_QUEUES 1
|
||||
#define DEF_TUR_MS_TO_READY 0
|
||||
#define DEF_UUID_CTL 0
|
||||
#define JDELAY_OVERRIDDEN -9999
|
||||
|
||||
@ -288,7 +289,7 @@ struct sdebug_dev_info {
|
||||
struct sdebug_host_info *sdbg_host;
|
||||
unsigned long uas_bm[1];
|
||||
atomic_t num_in_q;
|
||||
atomic_t stopped;
|
||||
atomic_t stopped; /* 1: by SSU, 2: device start */
|
||||
bool used;
|
||||
|
||||
/* For ZBC devices */
|
||||
@ -301,6 +302,7 @@ struct sdebug_dev_info {
|
||||
unsigned int nr_exp_open;
|
||||
unsigned int nr_closed;
|
||||
unsigned int max_open;
|
||||
ktime_t create_ts; /* time since bootup that this device was created */
|
||||
struct sdeb_zone_state *zstate;
|
||||
};
|
||||
|
||||
@ -760,6 +762,7 @@ static int sdebug_opt_xferlen_exp = DEF_OPT_XFERLEN_EXP;
|
||||
static int sdebug_ptype = DEF_PTYPE; /* SCSI peripheral device type */
|
||||
static int sdebug_scsi_level = DEF_SCSI_LEVEL;
|
||||
static int sdebug_sector_size = DEF_SECTOR_SIZE;
|
||||
static int sdeb_tur_ms_to_ready = DEF_TUR_MS_TO_READY;
|
||||
static int sdebug_virtual_gb = DEF_VIRTUAL_GB;
|
||||
static int sdebug_vpd_use_hostno = DEF_VPD_USE_HOSTNO;
|
||||
static unsigned int sdebug_lbpu = DEF_LBPU;
|
||||
@ -1770,11 +1773,10 @@ static int resp_requests(struct scsi_cmnd *scp,
|
||||
return fill_from_dev_buffer(scp, arr, min_t(int, len, alloc_len));
|
||||
}
|
||||
|
||||
static int resp_start_stop(struct scsi_cmnd *scp,
|
||||
struct sdebug_dev_info *devip)
|
||||
static int resp_start_stop(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
|
||||
{
|
||||
unsigned char *cmd = scp->cmnd;
|
||||
int power_cond, stop;
|
||||
int power_cond, want_stop, stopped_state;
|
||||
bool changing;
|
||||
|
||||
power_cond = (cmd[4] & 0xf0) >> 4;
|
||||
@ -1782,10 +1784,33 @@ static int resp_start_stop(struct scsi_cmnd *scp,
|
||||
mk_sense_invalid_fld(scp, SDEB_IN_CDB, 4, 7);
|
||||
return check_condition_result;
|
||||
}
|
||||
stop = !(cmd[4] & 1);
|
||||
changing = atomic_read(&devip->stopped) == !stop;
|
||||
atomic_xchg(&devip->stopped, stop);
|
||||
if (!changing || cmd[1] & 0x1) /* state unchanged or IMMED set */
|
||||
want_stop = !(cmd[4] & 1);
|
||||
stopped_state = atomic_read(&devip->stopped);
|
||||
if (stopped_state == 2) {
|
||||
ktime_t now_ts = ktime_get_boottime();
|
||||
|
||||
if (ktime_to_ns(now_ts) > ktime_to_ns(devip->create_ts)) {
|
||||
u64 diff_ns = ktime_to_ns(ktime_sub(now_ts, devip->create_ts));
|
||||
|
||||
if (diff_ns >= ((u64)sdeb_tur_ms_to_ready * 1000000)) {
|
||||
/* tur_ms_to_ready timer extinguished */
|
||||
atomic_set(&devip->stopped, 0);
|
||||
stopped_state = 0;
|
||||
}
|
||||
}
|
||||
if (stopped_state == 2) {
|
||||
if (want_stop) {
|
||||
stopped_state = 1; /* dummy up success */
|
||||
} else { /* Disallow tur_ms_to_ready delay to be overridden */
|
||||
mk_sense_invalid_fld(scp, SDEB_IN_CDB, 4, 0 /* START bit */);
|
||||
return check_condition_result;
|
||||
}
|
||||
}
|
||||
}
|
||||
changing = (stopped_state != want_stop);
|
||||
if (changing)
|
||||
atomic_xchg(&devip->stopped, want_stop);
|
||||
if (!changing || (cmd[1] & 0x1)) /* state unchanged or IMMED bit set in cdb */
|
||||
return SDEG_RES_IMMED_MASK;
|
||||
else
|
||||
return 0;
|
||||
@ -4014,7 +4039,7 @@ static int resp_sync_cache(struct scsi_cmnd *scp,
|
||||
mk_sense_buffer(scp, ILLEGAL_REQUEST, LBA_OUT_OF_RANGE, 0);
|
||||
return check_condition_result;
|
||||
}
|
||||
if (!write_since_sync || cmd[1] & 0x2)
|
||||
if (!write_since_sync || (cmd[1] & 0x2))
|
||||
res = SDEG_RES_IMMED_MASK;
|
||||
else /* delay if write_since_sync and IMMED clear */
|
||||
write_since_sync = false;
|
||||
@ -4903,6 +4928,8 @@ static struct sdebug_dev_info *sdebug_device_create(
|
||||
devip->zmodel = BLK_ZONED_NONE;
|
||||
}
|
||||
devip->sdbg_host = sdbg_host;
|
||||
devip->create_ts = ktime_get_boottime();
|
||||
atomic_set(&devip->stopped, (sdeb_tur_ms_to_ready > 0 ? 2 : 0));
|
||||
list_add_tail(&devip->dev_list, &sdbg_host->dev_info_list);
|
||||
}
|
||||
return devip;
|
||||
@ -5582,6 +5609,7 @@ 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(tur_ms_to_ready, sdeb_tur_ms_to_ready, 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);
|
||||
@ -5650,6 +5678,7 @@ 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(tur_ms_to_ready, "TEST UNIT READY millisecs before initial good status (def=0)");
|
||||
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)");
|
||||
@ -6488,6 +6517,12 @@ static ssize_t zbc_show(struct device_driver *ddp, char *buf)
|
||||
}
|
||||
static DRIVER_ATTR_RO(zbc);
|
||||
|
||||
static ssize_t tur_ms_to_ready_show(struct device_driver *ddp, char *buf)
|
||||
{
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n", sdeb_tur_ms_to_ready);
|
||||
}
|
||||
static DRIVER_ATTR_RO(tur_ms_to_ready);
|
||||
|
||||
/* Note: The following array creates attribute files in the
|
||||
/sys/bus/pseudo/drivers/scsi_debug directory. The advantage of these
|
||||
files (over those found in the /sys/module/scsi_debug/parameters
|
||||
@ -6530,6 +6565,7 @@ static struct attribute *sdebug_drv_attrs[] = {
|
||||
&driver_attr_strict.attr,
|
||||
&driver_attr_uuid_ctl.attr,
|
||||
&driver_attr_cdb_len.attr,
|
||||
&driver_attr_tur_ms_to_ready.attr,
|
||||
&driver_attr_zbc.attr,
|
||||
NULL,
|
||||
};
|
||||
@ -7066,6 +7102,49 @@ static bool fake_timeout(struct scsi_cmnd *scp)
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Response to TUR or media access command when device stopped */
|
||||
static int resp_not_ready(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
|
||||
{
|
||||
int stopped_state;
|
||||
u64 diff_ns = 0;
|
||||
ktime_t now_ts = ktime_get_boottime();
|
||||
struct scsi_device *sdp = scp->device;
|
||||
|
||||
stopped_state = atomic_read(&devip->stopped);
|
||||
if (stopped_state == 2) {
|
||||
if (ktime_to_ns(now_ts) > ktime_to_ns(devip->create_ts)) {
|
||||
diff_ns = ktime_to_ns(ktime_sub(now_ts, devip->create_ts));
|
||||
if (diff_ns >= ((u64)sdeb_tur_ms_to_ready * 1000000)) {
|
||||
/* tur_ms_to_ready timer extinguished */
|
||||
atomic_set(&devip->stopped, 0);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
mk_sense_buffer(scp, NOT_READY, LOGICAL_UNIT_NOT_READY, 0x1);
|
||||
if (sdebug_verbose)
|
||||
sdev_printk(KERN_INFO, sdp,
|
||||
"%s: Not ready: in process of becoming ready\n", my_name);
|
||||
if (scp->cmnd[0] == TEST_UNIT_READY) {
|
||||
u64 tur_nanosecs_to_ready = (u64)sdeb_tur_ms_to_ready * 1000000;
|
||||
|
||||
if (diff_ns <= tur_nanosecs_to_ready)
|
||||
diff_ns = tur_nanosecs_to_ready - diff_ns;
|
||||
else
|
||||
diff_ns = tur_nanosecs_to_ready;
|
||||
/* As per 20-061r2 approved for spc6 by T10 on 20200716 */
|
||||
do_div(diff_ns, 1000000); /* diff_ns becomes milliseconds */
|
||||
scsi_set_sense_information(scp->sense_buffer, SCSI_SENSE_BUFFERSIZE,
|
||||
diff_ns);
|
||||
return check_condition_result;
|
||||
}
|
||||
}
|
||||
mk_sense_buffer(scp, NOT_READY, LOGICAL_UNIT_NOT_READY, 0x2);
|
||||
if (sdebug_verbose)
|
||||
sdev_printk(KERN_INFO, sdp, "%s: Not ready: initializing command required\n",
|
||||
my_name);
|
||||
return check_condition_result;
|
||||
}
|
||||
|
||||
static int scsi_debug_queuecommand(struct Scsi_Host *shost,
|
||||
struct scsi_cmnd *scp)
|
||||
{
|
||||
@ -7190,14 +7269,11 @@ static int scsi_debug_queuecommand(struct Scsi_Host *shost,
|
||||
if (errsts)
|
||||
goto check_cond;
|
||||
}
|
||||
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: "
|
||||
"%s\n", my_name, "initializing command "
|
||||
"required");
|
||||
errsts = check_condition_result;
|
||||
goto fini;
|
||||
if (unlikely(((F_M_ACCESS & flags) || scp->cmnd[0] == TEST_UNIT_READY) &&
|
||||
atomic_read(&devip->stopped))) {
|
||||
errsts = resp_not_ready(scp, devip);
|
||||
if (errsts)
|
||||
goto fini;
|
||||
}
|
||||
if (sdebug_fake_rw && (F_FAKE_RW & flags))
|
||||
goto fini;
|
||||
|
Loading…
Reference in New Issue
Block a user