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;
+       struct rcu_head rcu;
+
+       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;
        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 spinlock list_lock;
+       struct list_head inject_err_list;
 };
 
 struct sdebug_host_info {
 
 static struct dentry *sdebug_debugfs_root;
 
+static void sdebug_err_free(struct rcu_head *head)
+{
+       struct sdebug_err_inject *inject =
+               container_of(head, typeof(*inject), rcu);
+
+       kfree(inject);
+}
+
+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 *err;
+
+       spin_lock(&devip->list_lock);
+       list_for_each_entry_rcu(err, &devip->inject_err_list, list) {
+               if (err->type == new->type && err->cmd == new->cmd) {
+                       list_del_rcu(&err->list);
+                       call_rcu(&err->rcu, sdebug_err_free);
+               }
+       }
+
+       list_add_tail_rcu(&new->list, &devip->inject_err_list);
+       spin_unlock(&devip->list_lock);
+}
+
+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");
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(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;
+               }
+       }
+       rcu_read_unlock();
+
+       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
                }
                devip->create_ts = ktime_get_boottime();
                atomic_set(&devip->stopped, (sdeb_tur_ms_to_ready > 0 ? 2 : 0));
+               spin_lock_init(&devip->list_lock);
+               INIT_LIST_HEAD(&devip->inject_err_list);
                list_add_tail(&devip->dev_list, &sdbg_host->dev_info_list);
        }
        return devip;
        if (sdebug_verbose)
                pr_info("slave_alloc <%u %u %u %llu>\n",
                       sdp->host->host_no, sdp->channel, sdp->id, sdp->lun);
+
        return 0;
 }
 
 {
        struct sdebug_dev_info *devip =
                        (struct sdebug_dev_info *)sdp->hostdata;
+       struct dentry *dentry;
 
        if (sdebug_verbose)
                pr_info("slave_configure <%u %u %u %llu>\n",
        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);
+       if (IS_ERR_OR_NULL(devip->debugfs_entry))
+               pr_info("%s: failed to create debugfs directory for device %s\n",
+                       __func__, dev_name(&sdp->sdev_gendev));
+
+       dentry = debugfs_create_file("error", 0600, devip->debugfs_entry, sdp,
+                               &sdebug_error_fops);
+       if (IS_ERR_OR_NULL(dentry))
+               pr_info("%s: failed to create error file for device %s\n",
+                       __func__, dev_name(&sdp->sdev_gendev));
+
        return 0;
 }
 
 {
        struct sdebug_dev_info *devip =
                (struct sdebug_dev_info *)sdp->hostdata;
+       struct sdebug_err_inject *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;
+
+       spin_lock(&devip->list_lock);
+       list_for_each_entry_rcu(err, &devip->inject_err_list, list) {
+               list_del_rcu(&err->list);
+               call_rcu(&err->rcu, sdebug_err_free);
        }
+       spin_unlock(&devip->list_lock);
+
+       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. */