Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4cc06521 authored by Keith Busch's avatar Keith Busch Committed by Jens Axboe
Browse files

NVMe: add sysfs and ioctl controller reset

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: default avatarKeith 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: default avatarJens Axboe <axboe@fb.com>
parent 8b70f45e
Loading
Loading
Loading
Loading
+53 −0
Original line number Diff line number Diff line
@@ -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 {
@@ -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;
	}
@@ -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)
{
@@ -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:
@@ -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)
@@ -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);
+1 −0
Original line number Diff line number Diff line
@@ -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 */