2023-08-15 19:03:55

by Wenchao Hao

[permalink] [raw]
Subject: [PATCH v4 0/9] scsi:scsi_debug: Add error injection for single device

The original error injection mechanism was based on scsi_host which
could not inject fault for a single SCSI device.

This patchset provides the ability to inject errors for a single
SCSI device. Now we supports inject timeout errors, queuecommand
errors, and hostbyte, driverbyte, statusbyte, and sense data for
specific SCSI Command. Two new error injection is defined to make
abort command or reset LUN failed.

Besides error injection for single device, this patchset add a
new interface to make reset target failed for each scsi_target.

The first two patch add an debugfs interface to add and inquiry single
device's error injection info; the third patch defined how to remove
an injection which has been added. The following 5 patches use the
injection info and generate the related error type. The last one just
Add a new interface to make reset target failed.

V4:
- Fix BUG_ON triggered by schedule in atomic context when rmmod scsi_debug
Closes: https://lore.kernel.org/oe-lkp/[email protected]

V3:
- Add two more error types to fail abort command and lun reset
- Fix memleak when rmmod scsi_debug without clearing errors injected
- Fix memkeak because did not implement release in sdebug_error_fops
- Fix possible NULL point access in scsi_debug_slave_destroy
- Move specific error type's description to each single patch which
implement this error type
- Add interface to make target reset fail

V2:
- Using debugfs rather than sysfs attribute interface to manage error

Wenchao Hao (9):
scsi:scsi_debug: create scsi_debug directory in the debugfs filesystem
scsi:scsi_debug: Add interface to manage single device's error inject
scsi:scsi_debug: Define grammar to remove added error injection
scsi:scsi_debug: timeout command if the error is injected
scsi:scsi_debug: Return failed value if the error is injected
scsi:scsi_debug: set command's result and sense data if the error is injected
scsi:scsi_debug: Add new error injection abort failed
scsi:scsi_debug: Add new error injection reset lun failed
scsi:scsi_debug: Add debugfs interface to fail target reset

drivers/scsi/scsi_debug.c | 512 +++++++++++++++++++++++++++++++++++++-
1 file changed, 507 insertions(+), 5 deletions(-)

--
2.35.3



2023-08-15 20:11:58

by Wenchao Hao

[permalink] [raw]
Subject: [PATCH v4 7/9] scsi:scsi_debug: Add new error injection abort failed

Add error injection type 3 to make scsi_debug_abort() return FAILED.
Fail abort command foramt:

+--------+------+-------------------------------------------------------+
| Column | Type | Description |
+--------+------+-------------------------------------------------------+
| 1 | u8 | Error type, fixed to 0x3 |
+--------+------+-------------------------------------------------------+
| 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 |
+--------+------+-------------------------------------------------------+

Examples:
error=/sys/kernel/debug/scsi_debug/0:0:0:1/error
echo "0 -10 0x12" > ${error}
will make the device return FAILED when abort inquiry command 10 times.

Signed-off-by: Wenchao Hao <[email protected]>
---
drivers/scsi/scsi_debug.c | 36 ++++++++++++++++++++++++++++++++++++
1 file changed, 36 insertions(+)

diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index 94c43b800694..9e4ea3b38cd0 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -293,6 +293,8 @@ enum sdebug_err_type {
ERR_FAIL_CMD = 2, /* make specific scsi command's */
/* queuecmd return succeed but */
/* with errors set in scsi_cmnd */
+ ERR_ABORT_CMD_FAILED = 3, /* control return FAILED from */
+ /* scsi_debug_abort() */
};

struct sdebug_err_inject {
@@ -958,6 +960,7 @@ static int sdebug_error_show(struct seq_file *m, void *p)
list_for_each_entry(err, &devip->inject_err_list, list) {
switch (err->type) {
case ERR_TMOUT_CMD:
+ case ERR_ABORT_CMD_FAILED:
seq_printf(m, "%d\t%d\t0x%x\n", err->type, err->cnt,
err->cmd);
break;
@@ -1018,6 +1021,7 @@ static ssize_t sdebug_error_write(struct file *file, const char __user *ubuf,

switch (inject_type) {
case ERR_TMOUT_CMD:
+ case ERR_ABORT_CMD_FAILED:
if (sscanf(buf, "%d %d %hhx", &inject->type, &inject->cnt,
&inject->cmd) != 3)
goto out_error;
@@ -5488,9 +5492,35 @@ static void stop_all_queued(void)
mutex_unlock(&sdebug_host_list_mutex);
}

+static int sdebug_fail_abort(struct scsi_cmnd *cmnd)
+{
+ struct scsi_device *sdp = cmnd->device;
+ struct sdebug_dev_info *devip = (struct sdebug_dev_info *)sdp->hostdata;
+ struct sdebug_err_inject *err;
+ unsigned char *cmd = cmnd->cmnd;
+ int ret = 0;
+
+ if (devip == NULL)
+ return 0;
+
+ list_for_each_entry(err, &devip->inject_err_list, list) {
+ if (err->type == ERR_ABORT_CMD_FAILED &&
+ (err->cmd == cmd[0] || err->cmd == 0xff)) {
+ ret = !!err->cnt;
+ if (err->cnt < 0)
+ err->cnt++;
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
static int scsi_debug_abort(struct scsi_cmnd *SCpnt)
{
bool ok = scsi_debug_abort_cmnd(SCpnt);
+ u8 *cmd = SCpnt->cmnd;
+ u8 opcode = cmd[0];

++num_aborts;

@@ -5499,6 +5529,12 @@ static int scsi_debug_abort(struct scsi_cmnd *SCpnt)
"%s: command%s found\n", __func__,
ok ? "" : " not");

+ if (sdebug_fail_abort(SCpnt)) {
+ scmd_printk(KERN_INFO, SCpnt, "fail abort command 0x%x\n",
+ opcode);
+ return FAILED;
+ }
+
return SUCCESS;
}

--
2.35.3


2023-08-15 23:21:36

by Wenchao Hao

[permalink] [raw]
Subject: [PATCH v4 2/9] scsi:scsi_debug: Add interface to manage single device's error inject

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 one line 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 an 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

Signed-off-by: Wenchao Hao <[email protected]>
Acked-by: Douglas Gilbert <[email protected]>
---
drivers/scsi/scsi_debug.c | 191 +++++++++++++++++++++++++++++++++++++-
1 file changed, 187 insertions(+), 4 deletions(-)

diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index 35c336271b13..d7d0c8af8bff 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -286,6 +286,41 @@ struct sdeb_zone_state { /* ZBC: per zone state */
sector_t z_wp;
};

+enum sdebug_err_type {
+ ERR_TMOUT_CMD = 0, /* make specific scsi command timeout */
+ ERR_FAIL_QUEUE_CMD = 1, /* make specific scsi command's */
+ /* queuecmd return failed */
+ ERR_FAIL_CMD = 2, /* make specific scsi command's */
+ /* queuecmd return succeed but */
+ /* with errors set in scsi_cmnd */
+};
+
+struct sdebug_err_inject {
+ int type;
+ struct list_head list;
+ int cnt;
+ unsigned char cmd;
+
+ union {
+ /*
+ * For ERR_FAIL_QUEUE_CMD
+ */
+ int queuecmd_ret;
+
+ /*
+ * For ERR_FAIL_CMD
+ */
+ struct {
+ unsigned char host_byte;
+ unsigned char driver_byte;
+ unsigned char status_byte;
+ unsigned char sense_key;
+ unsigned char asc;
+ unsigned char asq;
+ };
+ };
+};
+
struct sdebug_dev_info {
struct list_head dev_list;
unsigned int channel;
@@ -311,6 +346,9 @@ struct sdebug_dev_info {
unsigned int max_open;
ktime_t create_ts; /* time since bootup that this device was created */
struct sdeb_zone_state *zstate;
+
+ struct dentry *debugfs_entry;
+ struct list_head inject_err_list;
};

struct sdebug_host_info {
@@ -865,6 +903,133 @@ static const int condition_met_result = SAM_STAT_CONDITION_MET;

static struct dentry *sdebug_debugfs_root;

+static void sdebug_err_add(struct scsi_device *sdev, struct sdebug_err_inject *new)
+{
+ struct sdebug_dev_info *devip = (struct sdebug_dev_info *)sdev->hostdata;
+ struct sdebug_err_inject *tmp, *err;
+
+ list_for_each_entry_safe(err, tmp, &devip->inject_err_list, list) {
+ if (err->type == new->type && err->cmd == new->cmd) {
+ sdev_printk(KERN_INFO, sdev, "Substituted %d 0x%x\n",
+ err->type, err->cmd);
+ list_del(&err->list);
+ kfree(err);
+ }
+ }
+
+ list_add_tail(&new->list, &devip->inject_err_list);
+}
+
+static int sdebug_error_show(struct seq_file *m, void *p)
+{
+ struct scsi_device *sdev = (struct scsi_device *)m->private;
+ struct sdebug_dev_info *devip = (struct sdebug_dev_info *)sdev->hostdata;
+ struct sdebug_err_inject *err;
+
+ seq_puts(m, "Type\tCount\tCommand\n");
+
+ list_for_each_entry(err, &devip->inject_err_list, list) {
+ switch (err->type) {
+ case ERR_TMOUT_CMD:
+ seq_printf(m, "%d\t%d\t0x%x\n", err->type, err->cnt,
+ err->cmd);
+ break;
+
+ case ERR_FAIL_QUEUE_CMD:
+ seq_printf(m, "%d\t%d\t0x%x\t0x%x\n", err->type,
+ err->cnt, err->cmd, err->queuecmd_ret);
+ break;
+
+ case ERR_FAIL_CMD:
+ seq_printf(m, "%d\t%d\t0x%x\t0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
+ err->type, err->cnt, err->cmd,
+ err->host_byte, err->driver_byte,
+ err->status_byte, err->sense_key,
+ err->asc, err->asq);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int sdebug_error_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, sdebug_error_show, inode->i_private);
+}
+
+static ssize_t sdebug_error_write(struct file *file, const char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ char *buf;
+ unsigned int inject_type;
+ struct sdebug_err_inject *inject;
+ struct scsi_device *sdev = (struct scsi_device *)file->f_inode->i_private;
+
+ buf = kmalloc(count, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ if (copy_from_user(buf, ubuf, count)) {
+ kfree(buf);
+ return -EFAULT;
+ }
+
+ if (sscanf(buf, "%d", &inject_type) != 1) {
+ kfree(buf);
+ return -EINVAL;
+ }
+
+ inject = kzalloc(sizeof(struct sdebug_err_inject), GFP_KERNEL);
+ if (!inject) {
+ kfree(buf);
+ return -ENOMEM;
+ }
+
+ switch (inject_type) {
+ case ERR_TMOUT_CMD:
+ if (sscanf(buf, "%d %d %hhx", &inject->type, &inject->cnt,
+ &inject->cmd) != 3)
+ goto out_error;
+ break;
+
+ case ERR_FAIL_QUEUE_CMD:
+ if (sscanf(buf, "%d %d %hhx %x", &inject->type, &inject->cnt,
+ &inject->cmd, &inject->queuecmd_ret) != 4)
+ goto out_error;
+ break;
+
+ case ERR_FAIL_CMD:
+ if (sscanf(buf, "%d %d %hhx %hhx %hhx %hhx %hhx %hhx %hhx",
+ &inject->type, &inject->cnt, &inject->cmd,
+ &inject->host_byte, &inject->driver_byte,
+ &inject->status_byte, &inject->sense_key,
+ &inject->asc, &inject->asq) != 9)
+ goto out_error;
+ break;
+
+ default:
+ goto out_error;
+ break;
+ }
+
+ kfree(buf);
+ sdebug_err_add(sdev, inject);
+
+ return count;
+
+out_error:
+ kfree(buf);
+ kfree(inject);
+ return -EINVAL;
+}
+
+static const struct file_operations sdebug_error_fops = {
+ .open = sdebug_error_open,
+ .read = seq_read,
+ .write = sdebug_error_write,
+ .release = single_release,
+};

/* Only do the extra work involved in logical block provisioning if one or
* more of the lbpu, lbpws or lbpws10 parameters are given and we are doing
@@ -5099,6 +5264,7 @@ static struct sdebug_dev_info *sdebug_device_create(
}
devip->create_ts = ktime_get_boottime();
atomic_set(&devip->stopped, (sdeb_tur_ms_to_ready > 0 ? 2 : 0));
+ INIT_LIST_HEAD(&devip->inject_err_list);
list_add_tail(&devip->dev_list, &sdbg_host->dev_info_list);
}
return devip;
@@ -5144,6 +5310,7 @@ static int scsi_debug_slave_alloc(struct scsi_device *sdp)
if (sdebug_verbose)
pr_info("slave_alloc <%u %u %u %llu>\n",
sdp->host->host_no, sdp->channel, sdp->id, sdp->lun);
+
return 0;
}

@@ -5166,6 +5333,12 @@ static int scsi_debug_slave_configure(struct scsi_device *sdp)
if (sdebug_no_uld)
sdp->no_uld_attach = 1;
config_cdb_len(sdp);
+
+ devip->debugfs_entry = debugfs_create_dir(dev_name(&sdp->sdev_dev),
+ sdebug_debugfs_root);
+ debugfs_create_file("error", 0600, devip->debugfs_entry, sdp,
+ &sdebug_error_fops);
+
return 0;
}

@@ -5173,15 +5346,25 @@ static void scsi_debug_slave_destroy(struct scsi_device *sdp)
{
struct sdebug_dev_info *devip =
(struct sdebug_dev_info *)sdp->hostdata;
+ struct sdebug_err_inject *tmp, *err;

if (sdebug_verbose)
pr_info("slave_destroy <%u %u %u %llu>\n",
sdp->host->host_no, sdp->channel, sdp->id, sdp->lun);
- if (devip) {
- /* make this slot available for re-use */
- devip->used = false;
- sdp->hostdata = NULL;
+
+ if (!devip)
+ return;
+
+ list_for_each_entry_safe(err, tmp, &devip->inject_err_list, list) {
+ list_del(&err->list);
+ kfree(err);
}
+
+ debugfs_remove(devip->debugfs_entry);
+
+ /* make this slot available for re-use */
+ devip->used = false;
+ sdp->hostdata = NULL;
}

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


2023-08-17 03:46:10

by Wenchao Hao

[permalink] [raw]
Subject: [PATCH v4 8/9] scsi:scsi_debug: Add new error injection reset lun failed

Add error injection type 3 to make scsi_debug_device_reset() return FAILED.
Fail abort command foramt:

+--------+------+-------------------------------------------------------+
| Column | Type | Description |
+--------+------+-------------------------------------------------------+
| 1 | u8 | Error type, fixed to 0x4 |
+--------+------+-------------------------------------------------------+
| 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 |
+--------+------+-------------------------------------------------------+

Examples:
error=/sys/kernel/debug/scsi_debug/0:0:0:1/error
echo "0 -10 0x12" > ${error}
will make the device return FAILED when try to reset lun with inquiry
command 10 times.
error=/sys/kernel/debug/scsi_debug/0:0:0:1/error
echo "0 -10 0xff" > ${error}
will make the device return FAILED when try to reset lun 10 times.

Usually we do not care about what command it is when trying to perform
reset LUN, so 0xff could be applied.

Signed-off-by: Wenchao Hao <[email protected]>
---
drivers/scsi/scsi_debug.c | 35 +++++++++++++++++++++++++++++++++++
1 file changed, 35 insertions(+)

diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index 9e4ea3b38cd0..2c16e5f4ab28 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -295,6 +295,8 @@ enum sdebug_err_type {
/* with errors set in scsi_cmnd */
ERR_ABORT_CMD_FAILED = 3, /* control return FAILED from */
/* scsi_debug_abort() */
+ ERR_LUN_RESET_FAILED = 4, /* control return FAILED from */
+ /* scsi_debug_device_reseLUN_RESET_FAILEDt() */
};

struct sdebug_err_inject {
@@ -961,6 +963,7 @@ static int sdebug_error_show(struct seq_file *m, void *p)
switch (err->type) {
case ERR_TMOUT_CMD:
case ERR_ABORT_CMD_FAILED:
+ case ERR_LUN_RESET_FAILED:
seq_printf(m, "%d\t%d\t0x%x\n", err->type, err->cnt,
err->cmd);
break;
@@ -1022,6 +1025,7 @@ static ssize_t sdebug_error_write(struct file *file, const char __user *ubuf,
switch (inject_type) {
case ERR_TMOUT_CMD:
case ERR_ABORT_CMD_FAILED:
+ case ERR_LUN_RESET_FAILED:
if (sscanf(buf, "%d %d %hhx", &inject->type, &inject->cnt,
&inject->cmd) != 3)
goto out_error;
@@ -5558,10 +5562,36 @@ static void scsi_debug_stop_all_queued(struct scsi_device *sdp)
scsi_debug_stop_all_queued_iter, sdp);
}

+static int sdebug_fail_lun_reset(struct scsi_cmnd *cmnd)
+{
+ struct scsi_device *sdp = cmnd->device;
+ struct sdebug_dev_info *devip = (struct sdebug_dev_info *)sdp->hostdata;
+ struct sdebug_err_inject *err;
+ unsigned char *cmd = cmnd->cmnd;
+ int ret = 0;
+
+ if (devip == NULL)
+ return 0;
+
+ list_for_each_entry(err, &devip->inject_err_list, list) {
+ if (err->type == ERR_LUN_RESET_FAILED &&
+ (err->cmd == cmd[0] || err->cmd == 0xff)) {
+ ret = !!err->cnt;
+ if (err->cnt < 0)
+ err->cnt++;
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
static int scsi_debug_device_reset(struct scsi_cmnd *SCpnt)
{
struct scsi_device *sdp = SCpnt->device;
struct sdebug_dev_info *devip = sdp->hostdata;
+ u8 *cmd = SCpnt->cmnd;
+ u8 opcode = cmd[0];

++num_dev_resets;

@@ -5572,6 +5602,11 @@ static int scsi_debug_device_reset(struct scsi_cmnd *SCpnt)
if (devip)
set_bit(SDEBUG_UA_POR, devip->uas_bm);

+ if (sdebug_fail_lun_reset(SCpnt)) {
+ scmd_printk(KERN_INFO, SCpnt, "fail lun reset 0x%x\n", opcode);
+ return FAILED;
+ }
+
return SUCCESS;
}

--
2.35.3


2023-08-17 08:10:32

by Wenchao Hao

[permalink] [raw]
Subject: [PATCH v4 9/9] scsi:scsi_debug: Add debugfs interface to fail target reset

The interface is found at
/sys/kernel/debug/scsi_debug/target<h:c:t>/fail_reset where <h:c:t>
identifies the target to inject errors on. It's a simple bool type
interface which would make this target's reset fail if set to 'Y'.

Signed-off-by: Wenchao Hao <[email protected]>
Reported-by: kernel test robot <[email protected]>
---
drivers/scsi/scsi_debug.c | 106 +++++++++++++++++++++++++++++++++++++-
1 file changed, 105 insertions(+), 1 deletion(-)

diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index 2c16e5f4ab28..d9a34119d6dc 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -42,6 +42,7 @@
#include <linux/xarray.h>
#include <linux/prefetch.h>
#include <linux/debugfs.h>
+#include <linux/async.h>

#include <net/checksum.h>

@@ -355,6 +356,11 @@ struct sdebug_dev_info {
struct list_head inject_err_list;
};

+struct sdebug_target_info {
+ bool reset_fail;
+ struct dentry *debugfs_entry;
+};
+
struct sdebug_host_info {
struct list_head host_list;
int si_idx; /* sdeb_store_info (per host) xarray index */
@@ -1069,6 +1075,83 @@ static const struct file_operations sdebug_error_fops = {
.release = single_release,
};

+static int sdebug_target_reset_fail_show(struct seq_file *m, void *p)
+{
+ struct scsi_target *starget = (struct scsi_target *)m->private;
+ struct sdebug_target_info *targetip =
+ (struct sdebug_target_info *)starget->hostdata;
+
+ if (targetip)
+ seq_printf(m, "%c\n", targetip->reset_fail ? 'Y' : 'N');
+
+ return 0;
+}
+
+static int sdebug_target_reset_fail_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, sdebug_target_reset_fail_show, inode->i_private);
+}
+
+static ssize_t sdebug_target_reset_fail_write(struct file *file,
+ const char __user *ubuf, size_t count, loff_t *ppos)
+{
+ int ret;
+ struct scsi_target *starget =
+ (struct scsi_target *)file->f_inode->i_private;
+ struct sdebug_target_info *targetip =
+ (struct sdebug_target_info *)starget->hostdata;
+
+ if (targetip) {
+ ret = kstrtobool_from_user(ubuf, count, &targetip->reset_fail);
+ return ret < 0 ? ret : count;
+ }
+ return -ENODEV;
+}
+
+static const struct file_operations sdebug_target_reset_fail_fops = {
+ .open = sdebug_target_reset_fail_open,
+ .read = seq_read,
+ .write = sdebug_target_reset_fail_write,
+ .release = single_release,
+};
+
+static int sdebug_target_alloc(struct scsi_target *starget)
+{
+ struct sdebug_target_info *targetip;
+
+ targetip = kzalloc(sizeof(struct sdebug_target_info), GFP_KERNEL);
+ if (!targetip)
+ return -ENOMEM;
+
+ targetip->debugfs_entry = debugfs_create_dir(dev_name(&starget->dev),
+ sdebug_debugfs_root);
+ debugfs_create_file("fail_reset", 0600, targetip->debugfs_entry, starget,
+ &sdebug_target_reset_fail_fops);
+
+ starget->hostdata = targetip;
+
+ return 0;
+}
+
+static void sdebug_tartget_cleanup_async(void *data, async_cookie_t cookie)
+{
+ struct sdebug_target_info *targetip = data;
+
+ debugfs_remove(targetip->debugfs_entry);
+ kfree(targetip);
+}
+
+static void sdebug_target_destroy(struct scsi_target *starget)
+{
+ struct sdebug_target_info *targetip;
+
+ targetip = (struct sdebug_target_info *)starget->hostdata;
+ if (targetip) {
+ starget->hostdata = NULL;
+ async_schedule(sdebug_tartget_cleanup_async, targetip);
+ }
+}
+
/* Only do the extra work involved in logical block provisioning if one or
* more of the lbpu, lbpws or lbpws10 parameters are given and we are doing
* real reads and writes (i.e. not skipping them for speed).
@@ -5610,11 +5693,25 @@ static int scsi_debug_device_reset(struct scsi_cmnd *SCpnt)
return SUCCESS;
}

+static int sdebug_fail_target_reset(struct scsi_cmnd *cmnd)
+{
+ struct scsi_target *starget = scsi_target(cmnd->device);
+ struct sdebug_target_info *targetip =
+ (struct sdebug_target_info *)starget->hostdata;
+
+ if (targetip)
+ return targetip->reset_fail;
+
+ return 0;
+}
+
static int scsi_debug_target_reset(struct scsi_cmnd *SCpnt)
{
struct scsi_device *sdp = SCpnt->device;
struct sdebug_host_info *sdbg_host = shost_to_sdebug_host(sdp->host);
struct sdebug_dev_info *devip;
+ u8 *cmd = SCpnt->cmnd;
+ u8 opcode = cmd[0];
int k = 0;

++num_target_resets;
@@ -5632,6 +5729,12 @@ static int scsi_debug_target_reset(struct scsi_cmnd *SCpnt)
sdev_printk(KERN_INFO, sdp,
"%s: %d device(s) found in target\n", __func__, k);

+ if (sdebug_fail_target_reset(SCpnt)) {
+ scmd_printk(KERN_INFO, SCpnt, "fail target reset 0x%x\n",
+ opcode);
+ return FAILED;
+ }
+
return SUCCESS;
}

@@ -8070,7 +8173,6 @@ static int sdebug_init_cmd_priv(struct Scsi_Host *shost, struct scsi_cmnd *cmd)
return 0;
}

-
static struct scsi_host_template sdebug_driver_template = {
.show_info = scsi_debug_show_info,
.write_info = scsi_debug_write_info,
@@ -8100,6 +8202,8 @@ static struct scsi_host_template sdebug_driver_template = {
.track_queue_depth = 1,
.cmd_size = sizeof(struct sdebug_scsi_cmd),
.init_cmd_priv = sdebug_init_cmd_priv,
+ .target_alloc = sdebug_target_alloc,
+ .target_destroy = sdebug_target_destroy,
};

static int sdebug_driver_probe(struct device *dev)
--
2.35.3


2023-08-20 08:02:10

by Wenchao Hao

[permalink] [raw]
Subject: Re: [PATCH v4 0/9] scsi:scsi_debug: Add error injection for single device

On 2023/8/15 20:23, Wenchao Hao wrote:
> The original error injection mechanism was based on scsi_host which
> could not inject fault for a single SCSI device.
>
> This patchset provides the ability to inject errors for a single
> SCSI device. Now we supports inject timeout errors, queuecommand
> errors, and hostbyte, driverbyte, statusbyte, and sense data for
> specific SCSI Command. Two new error injection is defined to make
> abort command or reset LUN failed.
>
> Besides error injection for single device, this patchset add a
> new interface to make reset target failed for each scsi_target.
>
> The first two patch add an debugfs interface to add and inquiry single
> device's error injection info; the third patch defined how to remove
> an injection which has been added. The following 5 patches use the
> injection info and generate the related error type. The last one just
> Add a new interface to make reset target failed.
>

Friendly ping...

> V4:
> - Fix BUG_ON triggered by schedule in atomic context when rmmod scsi_debug
> Closes: https://lore.kernel.org/oe-lkp/[email protected]
>
> V3:
> - Add two more error types to fail abort command and lun reset
> - Fix memleak when rmmod scsi_debug without clearing errors injected
> - Fix memkeak because did not implement release in sdebug_error_fops
> - Fix possible NULL point access in scsi_debug_slave_destroy
> - Move specific error type's description to each single patch which
> implement this error type
> - Add interface to make target reset fail
>
> V2:
> - Using debugfs rather than sysfs attribute interface to manage error
>
> Wenchao Hao (9):
> scsi:scsi_debug: create scsi_debug directory in the debugfs filesystem
> scsi:scsi_debug: Add interface to manage single device's error inject
> scsi:scsi_debug: Define grammar to remove added error injection
> scsi:scsi_debug: timeout command if the error is injected
> scsi:scsi_debug: Return failed value if the error is injected
> scsi:scsi_debug: set command's result and sense data if the error is injected
> scsi:scsi_debug: Add new error injection abort failed
> scsi:scsi_debug: Add new error injection reset lun failed
> scsi:scsi_debug: Add debugfs interface to fail target reset
>
> drivers/scsi/scsi_debug.c | 512 +++++++++++++++++++++++++++++++++++++-
> 1 file changed, 507 insertions(+), 5 deletions(-)
>