nvme: allow user toggling hmb usage
authorKeith Busch <kbusch@kernel.org>
Tue, 27 Jul 2021 16:40:43 +0000 (09:40 -0700)
committerChristoph Hellwig <hch@lst.de>
Mon, 16 Aug 2021 12:42:24 +0000 (14:42 +0200)
The NVMe host memory buffer may consume a non-negligable amount of
memory. Controllers are required to function without the host memory
buffer enabled, but with possibly degraded performance. Export a sysfs
property to toggle this feature on a per-device granularity so users may
choose to reclaim memory at the expense of storage performance.

Signed-off-by: Keith Busch <kbusch@kernel.org>
Reviewed-by: Sagi Grimberg <sagi@grimberg.me>
Signed-off-by: Christoph Hellwig <hch@lst.de>
drivers/nvme/host/pci.c

index 5b23d58..b82492c 100644 (file)
@@ -137,6 +137,7 @@ struct nvme_dev {
        u32 cmbloc;
        struct nvme_ctrl ctrl;
        u32 last_ps;
+       bool hmb;
 
        mempool_t *iod_mempool;
 
@@ -1896,7 +1897,9 @@ static int nvme_set_host_mem(struct nvme_dev *dev, u32 bits)
                dev_warn(dev->ctrl.device,
                         "failed to set host mem (err %d, flags %#x).\n",
                         ret, bits);
-       }
+       } else
+               dev->hmb = bits & NVME_HOST_MEM_ENABLE;
+
        return ret;
 }
 
@@ -2081,6 +2084,42 @@ static ssize_t cmbsz_show(struct device *dev, struct device_attribute *attr,
 }
 static DEVICE_ATTR_RO(cmbsz);
 
+static ssize_t hmb_show(struct device *dev, struct device_attribute *attr,
+                       char *buf)
+{
+       struct nvme_dev *ndev = to_nvme_dev(dev_get_drvdata(dev));
+
+       return sysfs_emit(buf, "%d\n", ndev->hmb);
+}
+
+static ssize_t hmb_store(struct device *dev, struct device_attribute *attr,
+                        const char *buf, size_t count)
+{
+       struct nvme_dev *ndev = to_nvme_dev(dev_get_drvdata(dev));
+       bool new;
+       int ret;
+
+       if (strtobool(buf, &new) < 0)
+               return -EINVAL;
+
+       if (new == ndev->hmb)
+               return count;
+
+       if (new) {
+               ret = nvme_setup_host_mem(ndev);
+       } else {
+               ret = nvme_set_host_mem(ndev, 0);
+               if (!ret)
+                       nvme_free_host_mem(ndev);
+       }
+
+       if (ret < 0)
+               return ret;
+
+       return count;
+}
+static DEVICE_ATTR_RW(hmb);
+
 static umode_t nvme_pci_attrs_are_visible(struct kobject *kobj,
                struct attribute *a, int n)
 {
@@ -2094,6 +2133,9 @@ static umode_t nvme_pci_attrs_are_visible(struct kobject *kobj,
                if (!dev->cmbsz)
                        return 0;
        }
+       if (a == &dev_attr_hmb.attr && !ctrl->hmpre)
+               return 0;
+
        return a->mode;
 }
 
@@ -2101,6 +2143,7 @@ static struct attribute *nvme_pci_attrs[] = {
        &dev_attr_cmb.attr,
        &dev_attr_cmbloc.attr,
        &dev_attr_cmbsz.attr,
+       &dev_attr_hmb.attr,
        NULL,
 };