Add two sysfs files for reading and updating the admin and io timeouts of individual NVMe devices. For this, two new nvme_ctrl_ops were added to update the respective timeouts. This patch implements these ops for the pci nvme driver. Therefore, only the timeouts for PCI NVMe devices can be updated.
Original-patch-by: Milan Pandurov <mila...@amazon.com> Signed-off-by: Maximilian Heyne <mhe...@amazon.de> --- drivers/nvme/host/core.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++++ drivers/nvme/host/nvme.h | 2 ++ drivers/nvme/host/pci.c | 28 ++++++++++++++++++ 3 files changed, 104 insertions(+) diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index d0530bf7a677..f77201c508fa 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -2768,6 +2768,74 @@ static ssize_t nvme_sysfs_rescan(struct device *dev, } static DEVICE_ATTR(rescan_controller, S_IWUSR, NULL, nvme_sysfs_rescan); +static ssize_t io_timeout_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct nvme_ctrl *ctrl = dev_get_drvdata(dev); + + return scnprintf(buf, PAGE_SIZE, "%u\n", ctrl->io_timeout/HZ); +} + +static ssize_t io_timeout_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + struct nvme_ctrl *ctrl = dev_get_drvdata(dev); + unsigned int timeout; + int ret; + + ret = kstrtouint(buf, 10u, &timeout); + if (ret == -EINVAL) { + dev_warn(ctrl->dev, "Error parsing timeout value.\n"); + return ret; + } + if (ret == -ERANGE || timeout == 0 || timeout > UINT_MAX/HZ) { + dev_warn(ctrl->dev, + "Timeout value out of range (0 < timeout <= %u).\n", + UINT_MAX/HZ); + return -ERANGE; + } + ret = ctrl->ops->set_io_timeout(ctrl, timeout * HZ); + if (ret < 0) + return ret; + return count; +} +static DEVICE_ATTR_RW(io_timeout); + +static ssize_t admin_timeout_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct nvme_ctrl *ctrl = dev_get_drvdata(dev); + + return scnprintf(buf, PAGE_SIZE, "%u\n", ctrl->admin_timeout/HZ); +} + +static ssize_t admin_timeout_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct nvme_ctrl *ctrl = dev_get_drvdata(dev); + unsigned int timeout; + int ret; + + ret = kstrtouint(buf, 10u, &timeout); + if (ret == -EINVAL) { + dev_warn(ctrl->dev, "Error parsing timeout value.\n"); + return ret; + } + if (ret == -ERANGE || timeout == 0 || timeout > UINT_MAX/HZ) { + dev_warn(ctrl->dev, + "Timeout value out of range (0 < timeout <= %u).\n", + UINT_MAX/HZ); + return -ERANGE; + } + ret = ctrl->ops->set_admin_timeout(ctrl, timeout * HZ); + if (ret < 0) + return ret; + return count; +} +static DEVICE_ATTR_RW(admin_timeout); + static inline struct nvme_ns_head *dev_to_ns_head(struct device *dev) { struct gendisk *disk = dev_to_disk(dev); @@ -3008,6 +3076,8 @@ static struct attribute *nvme_dev_attrs[] = { &dev_attr_address.attr, &dev_attr_state.attr, &dev_attr_numa_node.attr, + &dev_attr_io_timeout.attr, + &dev_attr_admin_timeout.attr, NULL }; @@ -3021,6 +3091,10 @@ static umode_t nvme_dev_attrs_are_visible(struct kobject *kobj, return 0; if (a == &dev_attr_address.attr && !ctrl->ops->get_address) return 0; + if (a == &dev_attr_io_timeout.attr && !ctrl->ops->set_io_timeout) + return 0; + if (a == &dev_attr_admin_timeout.attr && !ctrl->ops->set_admin_timeout) + return 0; return a->mode; } diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h index 1397650edfda..4dc13c5990bd 100644 --- a/drivers/nvme/host/nvme.h +++ b/drivers/nvme/host/nvme.h @@ -367,6 +367,8 @@ struct nvme_ctrl_ops { int (*reg_read32)(struct nvme_ctrl *ctrl, u32 off, u32 *val); int (*reg_write32)(struct nvme_ctrl *ctrl, u32 off, u32 val); int (*reg_read64)(struct nvme_ctrl *ctrl, u32 off, u64 *val); + int (*set_io_timeout)(struct nvme_ctrl *ctrl, unsigned int timeout); + int (*set_admin_timeout)(struct nvme_ctrl *ctrl, unsigned int timeout); void (*free_ctrl)(struct nvme_ctrl *ctrl); void (*submit_async_event)(struct nvme_ctrl *ctrl); void (*delete_ctrl)(struct nvme_ctrl *ctrl); diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c index 16d7a00fecf0..6932895c3519 100644 --- a/drivers/nvme/host/pci.c +++ b/drivers/nvme/host/pci.c @@ -2647,6 +2647,32 @@ static int nvme_pci_get_address(struct nvme_ctrl *ctrl, char *buf, int size) return snprintf(buf, size, "%s", dev_name(&pdev->dev)); } +static int nvme_pci_set_io_timeout(struct nvme_ctrl *ctrl, unsigned int timeout) +{ + struct nvme_dev *dev = to_nvme_dev(ctrl); + struct nvme_ns *ns; + + ctrl->io_timeout = timeout; + dev->tagset.timeout = timeout; + down_write(&ctrl->namespaces_rwsem); + list_for_each_entry(ns, &ctrl->namespaces, list) { + blk_queue_rq_timeout(ns->queue, timeout); + } + up_write(&ctrl->namespaces_rwsem); + return 0; +} + +static int nvme_pci_set_admin_timeout(struct nvme_ctrl *ctrl, + unsigned int timeout) +{ + struct nvme_dev *dev = to_nvme_dev(ctrl); + + ctrl->admin_timeout = timeout; + dev->admin_tagset.timeout = timeout; + blk_queue_rq_timeout(ctrl->admin_q, timeout); + return 0; +} + static const struct nvme_ctrl_ops nvme_pci_ctrl_ops = { .name = "pcie", .module = THIS_MODULE, @@ -2655,6 +2681,8 @@ static const struct nvme_ctrl_ops nvme_pci_ctrl_ops = { .reg_read32 = nvme_pci_reg_read32, .reg_write32 = nvme_pci_reg_write32, .reg_read64 = nvme_pci_reg_read64, + .set_io_timeout = nvme_pci_set_io_timeout, + .set_admin_timeout = nvme_pci_set_admin_timeout, .free_ctrl = nvme_pci_free_ctrl, .submit_async_event = nvme_pci_submit_async_event, .get_address = nvme_pci_get_address, -- 2.16.5 Amazon Development Center Germany GmbH Krausenstr. 38 10117 Berlin Geschaeftsfuehrer: Christian Schlaeger, Ralf Herbrich Ust-ID: DE 289 237 879 Eingetragen am Amtsgericht Charlottenburg HRB 149173 B