@@ -280,6 +280,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;
@@ -306,6 +341,8 @@ 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 list_head inject_err_list;
};
struct sdebug_host_info {
@@ -5150,6 +5187,7 @@ static struct sdebug_dev_info *sdebug_device_create(
devip->sdbg_host = sdbg_host;
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;
@@ -7597,6 +7635,128 @@ static int sdebug_blk_mq_poll(struct Scsi_Host *shost, unsigned int queue_num)
return num_entries;
}
+static void sdebug_err_add(struct device *dev, struct sdebug_err_inject *new)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ 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 ssize_t error_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int tmp, ret = 0;
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct sdebug_dev_info *devip = (struct sdebug_dev_info *)sdev->hostdata;
+ struct sdebug_err_inject *err;
+
+ tmp = sysfs_emit_at(buf, ret, "Type\tCount\tCommand\n");
+ ret += tmp;
+
+ list_for_each_entry(err, &devip->inject_err_list, list) {
+ switch (err->type) {
+ case ERR_TMOUT_CMD:
+ tmp = sysfs_emit_at(buf, ret, "%d\t%d\t0x%x\n",
+ err->type, err->cnt, err->cmd);
+ ret += tmp;
+ break;
+
+ case ERR_FAIL_QUEUE_CMD:
+ tmp = sysfs_emit_at(buf, ret, "%d\t%d\t0x%x\t0x%x\n",
+ err->type, err->cnt, err->cmd, err->queuecmd_ret);
+ ret += tmp;
+ break;
+
+ case ERR_FAIL_CMD:
+ tmp = sysfs_emit_at(buf, ret,
+ "%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);
+ ret += tmp;
+ break;
+ }
+ }
+
+ return ret;
+}
+static ssize_t error_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned int inject_type;
+ struct sdebug_err_inject *inject;
+
+ if (sscanf(buf, "%d", &inject_type) != 1)
+ return -EINVAL;
+
+ inject = kzalloc(sizeof(struct sdebug_err_inject), GFP_KERNEL);
+ if (!inject)
+ 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;
+ }
+
+ sdebug_err_add(dev, inject);
+
+ return count;
+
+out_error:
+ kfree(inject);
+ return -EINVAL;
+}
+static DEVICE_ATTR_RW(error);
+
+static struct attribute *sdebug_sdev_attrs[] = {
+ &dev_attr_error.attr,
+ NULL
+};
+
+static const struct attribute_group sdebug_sdev_attr_group = {
+ .name = "error_inject",
+ .attrs = sdebug_sdev_attrs,
+};
+
+const struct attribute_group *sdebug_sdev_groups[] = {
+ &sdebug_sdev_attr_group,
+ NULL
+};
+
static int scsi_debug_queuecommand(struct Scsi_Host *shost,
struct scsi_cmnd *scp)
{
@@ -7776,6 +7936,7 @@ static struct scsi_host_template sdebug_driver_template = {
.ioctl = scsi_debug_ioctl,
.queuecommand = scsi_debug_queuecommand,
.change_queue_depth = sdebug_change_qdepth,
+ .sdev_groups = sdebug_sdev_groups,
.map_queues = sdebug_map_queues,
.mq_poll = sdebug_blk_mq_poll,
.eh_abort_handler = scsi_debug_abort,