Skip to content

Commit a9996d7

Browse files
wenchao-haomartinkpetersen
authored andcommitted
scsi: scsi_debug: Add interface to manage error injection for a single device
This new facility uses the debugfs pseudo file system which is typically mounted under the /sys/kernel/debug directory and requires root permissions to access. The interface file is found at /sys/kernel/debug/scsi_debug/<h:c:t:l>/error where <h:c:t:l> identifies the device (logical unit (LU)) to inject errors on. For the following description the ${error} environment variable is assumed to be set to/sys/kernel/debug/scsi_debug/1:0:0:0/error where 1:0:0:0 is a pseudo device (LU) owned by the scsi_debug driver. Rules are written to ${error} in the normal sysfs fashion (e.g. 'echo "0 -2 0x12" > ${error}'). More than one rule can be active on a device at a time and inactive rules (i.e. those whose error count is 0) remain in the rule listing. The existing rules can be read with 'cat ${error}' with oneline output for each rule. The interface format is line-by-line, each line is an error injection rule. Each rule contains integers separated by spaces, the first three columns correspond to "Error code", "Error count" and "SCSI command", other columns depend on Error code. General rule format: +--------+------+-------------------------------------------------------+ | Column | Type | Description | +--------+------+-------------------------------------------------------+ | 1 | u8 | Error code | | | | 0: timeout SCSI command | | | | 1: fail queuecommand, make queuecommand return | | | | given value | | | | 2: fail command, finish command with SCSI status, | | | | sense key and ASC/ASCQ values | | | | 3: make abort commands for specific command fail | | | | 4: make reset lun for specific command fail | +--------+------+-------------------------------------------------------+ | 2 | s32 | Error count | | | | 0: this rule will be ignored | | | | positive: the rule will always take effect | | | | negative: the rule takes effect n times where -n is | | | | the value given. Ignored after n times | +--------+------+-------------------------------------------------------+ | 3 | x8 | SCSI command opcode, 0xff for all commands | +--------+------+-------------------------------------------------------+ | ... | xxx | Error type specific fields | +--------+------+-------------------------------------------------------+ Notes: - When multiple error inject rules are added for the same SCSI command, the one with smaller error code will take effect (and the others will be ignored). - If the same error (i.e. same Error code and SCSI command) is added, the older one will be overwritten.. - Currently, the basic types are (u8/u16/u32/u64/s8/s16/s32/s64) and the hexadecimal types (x8/x16/x32/x64). - Where a hexadecimal value is expected (e.g. Column 3: SCSI command opcode) the "0x" prefix is optional on the value (e.g. the INQUIRY opcode can be given as '0x12' or '12'). - When the Error count is negative, reading ${error} will show that value incrementing, stopping when it gets to 0. Acked-by: Douglas Gilbert <[email protected]> Signed-off-by: Wenchao Hao <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Martin K. Petersen <[email protected]>
1 parent 6e2d15f commit a9996d7

File tree

1 file changed

+210
-4
lines changed

1 file changed

+210
-4
lines changed

drivers/scsi/scsi_debug.c

+210-4
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,42 @@ struct sdeb_zone_state { /* ZBC: per zone state */
286286
sector_t z_wp;
287287
};
288288

289+
enum sdebug_err_type {
290+
ERR_TMOUT_CMD = 0, /* make specific scsi command timeout */
291+
ERR_FAIL_QUEUE_CMD = 1, /* make specific scsi command's */
292+
/* queuecmd return failed */
293+
ERR_FAIL_CMD = 2, /* make specific scsi command's */
294+
/* queuecmd return succeed but */
295+
/* with errors set in scsi_cmnd */
296+
};
297+
298+
struct sdebug_err_inject {
299+
int type;
300+
struct list_head list;
301+
int cnt;
302+
unsigned char cmd;
303+
struct rcu_head rcu;
304+
305+
union {
306+
/*
307+
* For ERR_FAIL_QUEUE_CMD
308+
*/
309+
int queuecmd_ret;
310+
311+
/*
312+
* For ERR_FAIL_CMD
313+
*/
314+
struct {
315+
unsigned char host_byte;
316+
unsigned char driver_byte;
317+
unsigned char status_byte;
318+
unsigned char sense_key;
319+
unsigned char asc;
320+
unsigned char asq;
321+
};
322+
};
323+
};
324+
289325
struct sdebug_dev_info {
290326
struct list_head dev_list;
291327
unsigned int channel;
@@ -311,6 +347,10 @@ struct sdebug_dev_info {
311347
unsigned int max_open;
312348
ktime_t create_ts; /* time since bootup that this device was created */
313349
struct sdeb_zone_state *zstate;
350+
351+
struct dentry *debugfs_entry;
352+
struct spinlock list_lock;
353+
struct list_head inject_err_list;
314354
};
315355

316356
struct sdebug_host_info {
@@ -865,6 +905,143 @@ static const int condition_met_result = SAM_STAT_CONDITION_MET;
865905

866906
static struct dentry *sdebug_debugfs_root;
867907

908+
static void sdebug_err_free(struct rcu_head *head)
909+
{
910+
struct sdebug_err_inject *inject =
911+
container_of(head, typeof(*inject), rcu);
912+
913+
kfree(inject);
914+
}
915+
916+
static void sdebug_err_add(struct scsi_device *sdev, struct sdebug_err_inject *new)
917+
{
918+
struct sdebug_dev_info *devip = (struct sdebug_dev_info *)sdev->hostdata;
919+
struct sdebug_err_inject *err;
920+
921+
spin_lock(&devip->list_lock);
922+
list_for_each_entry_rcu(err, &devip->inject_err_list, list) {
923+
if (err->type == new->type && err->cmd == new->cmd) {
924+
list_del_rcu(&err->list);
925+
call_rcu(&err->rcu, sdebug_err_free);
926+
}
927+
}
928+
929+
list_add_tail_rcu(&new->list, &devip->inject_err_list);
930+
spin_unlock(&devip->list_lock);
931+
}
932+
933+
static int sdebug_error_show(struct seq_file *m, void *p)
934+
{
935+
struct scsi_device *sdev = (struct scsi_device *)m->private;
936+
struct sdebug_dev_info *devip = (struct sdebug_dev_info *)sdev->hostdata;
937+
struct sdebug_err_inject *err;
938+
939+
seq_puts(m, "Type\tCount\tCommand\n");
940+
941+
rcu_read_lock();
942+
list_for_each_entry_rcu(err, &devip->inject_err_list, list) {
943+
switch (err->type) {
944+
case ERR_TMOUT_CMD:
945+
seq_printf(m, "%d\t%d\t0x%x\n", err->type, err->cnt,
946+
err->cmd);
947+
break;
948+
949+
case ERR_FAIL_QUEUE_CMD:
950+
seq_printf(m, "%d\t%d\t0x%x\t0x%x\n", err->type,
951+
err->cnt, err->cmd, err->queuecmd_ret);
952+
break;
953+
954+
case ERR_FAIL_CMD:
955+
seq_printf(m, "%d\t%d\t0x%x\t0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
956+
err->type, err->cnt, err->cmd,
957+
err->host_byte, err->driver_byte,
958+
err->status_byte, err->sense_key,
959+
err->asc, err->asq);
960+
break;
961+
}
962+
}
963+
rcu_read_unlock();
964+
965+
return 0;
966+
}
967+
968+
static int sdebug_error_open(struct inode *inode, struct file *file)
969+
{
970+
return single_open(file, sdebug_error_show, inode->i_private);
971+
}
972+
973+
static ssize_t sdebug_error_write(struct file *file, const char __user *ubuf,
974+
size_t count, loff_t *ppos)
975+
{
976+
char *buf;
977+
unsigned int inject_type;
978+
struct sdebug_err_inject *inject;
979+
struct scsi_device *sdev = (struct scsi_device *)file->f_inode->i_private;
980+
981+
buf = kmalloc(count, GFP_KERNEL);
982+
if (!buf)
983+
return -ENOMEM;
984+
985+
if (copy_from_user(buf, ubuf, count)) {
986+
kfree(buf);
987+
return -EFAULT;
988+
}
989+
990+
if (sscanf(buf, "%d", &inject_type) != 1) {
991+
kfree(buf);
992+
return -EINVAL;
993+
}
994+
995+
inject = kzalloc(sizeof(struct sdebug_err_inject), GFP_KERNEL);
996+
if (!inject) {
997+
kfree(buf);
998+
return -ENOMEM;
999+
}
1000+
1001+
switch (inject_type) {
1002+
case ERR_TMOUT_CMD:
1003+
if (sscanf(buf, "%d %d %hhx", &inject->type, &inject->cnt,
1004+
&inject->cmd) != 3)
1005+
goto out_error;
1006+
break;
1007+
1008+
case ERR_FAIL_QUEUE_CMD:
1009+
if (sscanf(buf, "%d %d %hhx %x", &inject->type, &inject->cnt,
1010+
&inject->cmd, &inject->queuecmd_ret) != 4)
1011+
goto out_error;
1012+
break;
1013+
1014+
case ERR_FAIL_CMD:
1015+
if (sscanf(buf, "%d %d %hhx %hhx %hhx %hhx %hhx %hhx %hhx",
1016+
&inject->type, &inject->cnt, &inject->cmd,
1017+
&inject->host_byte, &inject->driver_byte,
1018+
&inject->status_byte, &inject->sense_key,
1019+
&inject->asc, &inject->asq) != 9)
1020+
goto out_error;
1021+
break;
1022+
1023+
default:
1024+
goto out_error;
1025+
break;
1026+
}
1027+
1028+
kfree(buf);
1029+
sdebug_err_add(sdev, inject);
1030+
1031+
return count;
1032+
1033+
out_error:
1034+
kfree(buf);
1035+
kfree(inject);
1036+
return -EINVAL;
1037+
}
1038+
1039+
static const struct file_operations sdebug_error_fops = {
1040+
.open = sdebug_error_open,
1041+
.read = seq_read,
1042+
.write = sdebug_error_write,
1043+
.release = single_release,
1044+
};
8681045

8691046
/* Only do the extra work involved in logical block provisioning if one or
8701047
* more of the lbpu, lbpws or lbpws10 parameters are given and we are doing
@@ -5099,6 +5276,8 @@ static struct sdebug_dev_info *sdebug_device_create(
50995276
}
51005277
devip->create_ts = ktime_get_boottime();
51015278
atomic_set(&devip->stopped, (sdeb_tur_ms_to_ready > 0 ? 2 : 0));
5279+
spin_lock_init(&devip->list_lock);
5280+
INIT_LIST_HEAD(&devip->inject_err_list);
51025281
list_add_tail(&devip->dev_list, &sdbg_host->dev_info_list);
51035282
}
51045283
return devip;
@@ -5144,13 +5323,15 @@ static int scsi_debug_slave_alloc(struct scsi_device *sdp)
51445323
if (sdebug_verbose)
51455324
pr_info("slave_alloc <%u %u %u %llu>\n",
51465325
sdp->host->host_no, sdp->channel, sdp->id, sdp->lun);
5326+
51475327
return 0;
51485328
}
51495329

51505330
static int scsi_debug_slave_configure(struct scsi_device *sdp)
51515331
{
51525332
struct sdebug_dev_info *devip =
51535333
(struct sdebug_dev_info *)sdp->hostdata;
5334+
struct dentry *dentry;
51545335

51555336
if (sdebug_verbose)
51565337
pr_info("slave_configure <%u %u %u %llu>\n",
@@ -5166,22 +5347,47 @@ static int scsi_debug_slave_configure(struct scsi_device *sdp)
51665347
if (sdebug_no_uld)
51675348
sdp->no_uld_attach = 1;
51685349
config_cdb_len(sdp);
5350+
5351+
devip->debugfs_entry = debugfs_create_dir(dev_name(&sdp->sdev_dev),
5352+
sdebug_debugfs_root);
5353+
if (IS_ERR_OR_NULL(devip->debugfs_entry))
5354+
pr_info("%s: failed to create debugfs directory for device %s\n",
5355+
__func__, dev_name(&sdp->sdev_gendev));
5356+
5357+
dentry = debugfs_create_file("error", 0600, devip->debugfs_entry, sdp,
5358+
&sdebug_error_fops);
5359+
if (IS_ERR_OR_NULL(dentry))
5360+
pr_info("%s: failed to create error file for device %s\n",
5361+
__func__, dev_name(&sdp->sdev_gendev));
5362+
51695363
return 0;
51705364
}
51715365

51725366
static void scsi_debug_slave_destroy(struct scsi_device *sdp)
51735367
{
51745368
struct sdebug_dev_info *devip =
51755369
(struct sdebug_dev_info *)sdp->hostdata;
5370+
struct sdebug_err_inject *err;
51765371

51775372
if (sdebug_verbose)
51785373
pr_info("slave_destroy <%u %u %u %llu>\n",
51795374
sdp->host->host_no, sdp->channel, sdp->id, sdp->lun);
5180-
if (devip) {
5181-
/* make this slot available for re-use */
5182-
devip->used = false;
5183-
sdp->hostdata = NULL;
5375+
5376+
if (!devip)
5377+
return;
5378+
5379+
spin_lock(&devip->list_lock);
5380+
list_for_each_entry_rcu(err, &devip->inject_err_list, list) {
5381+
list_del_rcu(&err->list);
5382+
call_rcu(&err->rcu, sdebug_err_free);
51845383
}
5384+
spin_unlock(&devip->list_lock);
5385+
5386+
debugfs_remove(devip->debugfs_entry);
5387+
5388+
/* make this slot available for re-use */
5389+
devip->used = false;
5390+
sdp->hostdata = NULL;
51855391
}
51865392

51875393
/* Returns true if we require the queued memory to be freed by the caller. */

0 commit comments

Comments
 (0)