Skip to content

Commit

Permalink
NVMe: add sysfs and ioctl controller reset
Browse files Browse the repository at this point in the history
We need the ability to perform an nvme controller reset as discussed on
the mailing list thread:

  http://lists.infradead.org/pipermail/linux-nvme/2015-March/001585.html

This adds a sysfs entry that when written to will reset perform an NVMe
controller reset if the controller was successfully initialized in the
first place.

This also adds locking around resetting the device in the async probe
method so the driver can't schedule two resets.

Signed-off-by: Keith Busch <keith.busch@intel.com>
Cc: Brandon Schultz <brandon.schulz@hgst.com>
Cc: David Sariel <david.sariel@pmcs.com>

Updated by Jens to:

1) Merge this with the ioctl reset patch from David Sariel. The ioctl
   path now shares the reset code from the sysfs path.

2) Don't flush work if we fail issuing the reset.

Signed-off-by: Jens Axboe <axboe@fb.com>
  • Loading branch information
Keith Busch authored and Jens Axboe committed Jun 5, 2015
1 parent 8b70f45 commit 4cc0652
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 0 deletions.
53 changes: 53 additions & 0 deletions drivers/block/nvme-core.c
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ static wait_queue_head_t nvme_kthread_wait;
static struct class *nvme_class;

static void nvme_reset_failed_dev(struct work_struct *ws);
static int nvme_reset(struct nvme_dev *dev);
static int nvme_process_cq(struct nvme_queue *nvmeq);

struct async_cmd_info {
Expand Down Expand Up @@ -2689,6 +2690,9 @@ static long nvme_dev_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
return -ENOTTY;
ns = list_first_entry(&dev->namespaces, struct nvme_ns, list);
return nvme_user_cmd(dev, ns, (void __user *)arg);
case NVME_IOCTL_RESET:
dev_warn(dev->dev, "resetting controller\n");
return nvme_reset(dev);
default:
return -ENOTTY;
}
Expand Down Expand Up @@ -2839,6 +2843,44 @@ static void nvme_reset_workfn(struct work_struct *work)
dev->reset_workfn(work);
}

static int nvme_reset(struct nvme_dev *dev)
{
int ret = -EBUSY;

if (!dev->admin_q || blk_queue_dying(dev->admin_q))
return -ENODEV;

spin_lock(&dev_list_lock);
if (!work_pending(&dev->reset_work)) {
dev->reset_workfn = nvme_reset_failed_dev;
queue_work(nvme_workq, &dev->reset_work);
ret = 0;
}
spin_unlock(&dev_list_lock);

if (!ret) {
flush_work(&dev->reset_work);
return 0;
}

return ret;
}

static ssize_t nvme_sysfs_reset(struct device *dev,
struct device_attribute *attr, const char *buf,
size_t count)
{
struct nvme_dev *ndev = dev_get_drvdata(dev);
int ret;

ret = nvme_reset(ndev);
if (ret < 0)
return ret;

return count;
}
static DEVICE_ATTR(reset_controller, S_IWUSR, NULL, nvme_sysfs_reset);

static void nvme_async_probe(struct work_struct *work);
static int nvme_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
Expand Down Expand Up @@ -2883,12 +2925,20 @@ static int nvme_probe(struct pci_dev *pdev, const struct pci_device_id *id)
goto release_pools;
}
get_device(dev->device);
dev_set_drvdata(dev->device, dev);

result = device_create_file(dev->device, &dev_attr_reset_controller);
if (result)
goto put_dev;

INIT_LIST_HEAD(&dev->node);
INIT_WORK(&dev->probe_work, nvme_async_probe);
schedule_work(&dev->probe_work);
return 0;

put_dev:
device_destroy(nvme_class, MKDEV(nvme_char_major, dev->instance));
put_device(dev->device);
release_pools:
nvme_release_prp_pools(dev);
release:
Expand Down Expand Up @@ -2919,10 +2969,12 @@ static void nvme_async_probe(struct work_struct *work)
nvme_set_irq_hints(dev);
return;
reset:
spin_lock(&dev_list_lock);
if (!work_busy(&dev->reset_work)) {
dev->reset_workfn = nvme_reset_failed_dev;
queue_work(nvme_workq, &dev->reset_work);
}
spin_unlock(&dev_list_lock);
}

static void nvme_reset_notify(struct pci_dev *pdev, bool prepare)
Expand Down Expand Up @@ -2952,6 +3004,7 @@ static void nvme_remove(struct pci_dev *pdev)
pci_set_drvdata(pdev, NULL);
flush_work(&dev->probe_work);
flush_work(&dev->reset_work);
device_remove_file(dev->device, &dev_attr_reset_controller);
nvme_dev_shutdown(dev);
nvme_dev_remove(dev);
nvme_dev_remove_admin(dev);
Expand Down
1 change: 1 addition & 0 deletions include/uapi/linux/nvme.h
Original file line number Diff line number Diff line change
Expand Up @@ -579,5 +579,6 @@ struct nvme_passthru_cmd {
#define NVME_IOCTL_ADMIN_CMD _IOWR('N', 0x41, struct nvme_admin_cmd)
#define NVME_IOCTL_SUBMIT_IO _IOW('N', 0x42, struct nvme_user_io)
#define NVME_IOCTL_IO_CMD _IOWR('N', 0x43, struct nvme_passthru_cmd)
#define NVME_IOCTL_RESET _IO('N', 0x44)

#endif /* _UAPI_LINUX_NVME_H */

0 comments on commit 4cc0652

Please sign in to comment.