2
0
mirror of https://github.com/edk2-porting/linux-next.git synced 2024-12-23 12:43:55 +08: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:
Douglas Gilbert 2020-07-24 11:55:31 -04:00 committed by Martin K. Petersen
parent 84905d34f1
commit fc13638ae9

View File

@ -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;