Skip to contentSkip to conversation

NVMe: Introduce CMB allocation scheme

PATCH New Jon Derrick 3 participants 9 messages Dec 30, 2015 – Jan 13, 2016 NVMe

lore
Share & tools
lore
#0 lore raw

cover NVMe: Introduce CMB allocation scheme

Applies to Jens' for-4.5/nvme

This patchset changes the CMB allocation scheme. Instead of reserving the entire range for SQes and automatically placing the SQes in the CMB, this set creates sysfs knobs to manage it. This allows partial usage of the CMB for SQes, so that the remainder can be mmapped or used for other NVMe-defined CMB usages.

Jon Derrick (3):
NVMe: Introduce sysfs entries for submission queues in CMB
NVMe: Generate resource tree for CMB
NVMe: Create CMB resource sysfs file

 drivers/nvme/host/core.c | 161 ++++++++++++++++++++++++++++++++++-
 drivers/nvme/host/nvme.h |  21 +++++
 drivers/nvme/host/pci.c  | 212 ++++++++++++++++++++++++++++++-----------------
 3 files changed, 313 insertions(+), 81 deletions(-)
Signature · 1 line
2.1.4
#1 lore raw

PATCH 1/3 NVMe: Introduce sysfs entries for submission queues in CMB

Currently submission queues are always mapped to the CMB if possible and allowed by a module parameter. To allow userspace more control over the CMB, this patch introduces a sysfs/cmb framework into the core nvme code and refactors the pci portion.

If the controller supports SQes in the CMB, sysfs files cmb_sq_depth and cmb_sq_offset are visible. To apply changes to the queues, users must write the sysfs reset_controller entry after changing cmb parameters.

Signed-off-by Jon Derrick <jonathan.derrick at intel.com>
---
 drivers/nvme/host/core.c | 133 +++++++++++++++++++++++++++++++++++-
 drivers/nvme/host/nvme.h |  22 ++++++
 drivers/nvme/host/pci.c  | 174 +++++++++++++++++++++++++++++------------------
 3 files changed, 259 insertions(+), 70 deletions(-)
drivers/nvme/host/core.c +130 -3
index 1437ff3..6aed4b9 100644
@@ -969,6 +969,87 @@ static ssize_t nvme_sysfs_reset(struct device *dev,
 }
 static DEVICE_ATTR(reset_controller, S_IWUSR, NULL, nvme_sysfs_reset);
 
+static ssize_t nvme_cmb_sq_depth_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
+	struct nvme_cmb *cmb = ctrl->cmb;
+	return sprintf(buf, "%u\n", cmb->sq_depth);
+}
+
+static ssize_t nvme_cmb_sq_depth_store(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf, size_t count)
+{
+	struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
+	struct nvme_cmb *cmb = ctrl->cmb;
+	u32 sq_depth;
+
+	sscanf(buf, "%u", &sq_depth);
+	if (sq_depth > 0 && (sq_depth < 2 || sq_depth > 0xffff))
+		return -EINVAL;
+
+	cmb->sq_depth = sq_depth;
+	return count;
+}
+static DEVICE_ATTR(cmb_sq_depth, S_IWUSR | S_IRUGO, nvme_cmb_sq_depth_show,
+						nvme_cmb_sq_depth_store);
+
+static ssize_t nvme_cmb_sq_offset_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
+	struct nvme_cmb *cmb = ctrl->cmb;
+	return sprintf(buf, "%llu\n", cmb->sq_offset);
+}
+
+static ssize_t nvme_cmb_sq_offset_store(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf, size_t count)
+{
+	struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
+	struct nvme_cmb *cmb = ctrl->cmb;
+	u64 sq_offset;
+
+	sscanf(buf, "%llu", &sq_offset);
+	if (sq_offset >= cmb->size)
+		return -EINVAL;
+
+	cmb->sq_offset = sq_offset;
+	return count;
+}
+static DEVICE_ATTR(cmb_sq_offset, S_IWUSR | S_IRUGO, nvme_cmb_sq_offset_show,
+						nvme_cmb_sq_offset_store);
+
+static struct attribute *nvme_cmb_attrs[] = {
+	&dev_attr_cmb_sq_depth.attr,
+	&dev_attr_cmb_sq_offset.attr,
+	NULL
+};
+
+static umode_t nvme_cmb_attrs_are_visible(struct kobject *kobj,
+		struct attribute *a, int n)
+{
+	struct device *dev = kobj_to_dev(kobj);
+	struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
+	struct nvme_cmb *cmb = ctrl->cmb;
+
+	if ((a == &dev_attr_cmb_sq_depth.attr) ||
+	    (a == &dev_attr_cmb_sq_offset.attr)) {
+		if (!(cmb->flags & NVME_CMB_SQ_SUPPORTED))
+			return 0;
+	}
+	return a->mode;
+}
+
+static struct attribute_group nvme_cmb_attr_group = {
+	.attrs 		= nvme_cmb_attrs,
+	.is_visible	= nvme_cmb_attrs_are_visible,
+};
+
+
 static ssize_t uuid_show(struct device *dev, struct device_attribute *attr,
 								char *buf)
 {
@@ -1000,7 +1081,7 @@ static struct attribute *nvme_ns_attrs[] = {
 	NULL,
 };
 
-static umode_t nvme_attrs_are_visible(struct kobject *kobj,
+static umode_t nvme_ns_attrs_are_visible(struct kobject *kobj,
 		struct attribute *a, int n)
 {
 	struct device *dev = container_of(kobj, struct device, kobj);
@@ -1019,7 +1100,7 @@ static umode_t nvme_attrs_are_visible(struct kobject *kobj,
 
 static const struct attribute_group nvme_ns_attr_group = {
 	.attrs		= nvme_ns_attrs,
-	.is_visible	= nvme_attrs_are_visible,
+	.is_visible	= nvme_ns_attrs_are_visible,
 };
 
 static int ns_cmp(void *priv, struct list_head *a, struct list_head *b)
@@ -1225,6 +1306,45 @@ void nvme_remove_namespaces(struct nvme_ctrl *ctrl)
 		nvme_ns_remove(ns);
 }
 
+static int nvme_init_cmb(struct nvme_ctrl *ctrl)
+{
+	/* Preserve across device resets */
+	if (ctrl->cmb)
+		return 0;
+
+	ctrl->cmb = kzalloc(sizeof(*ctrl->cmb), GFP_KERNEL);
+	if (!ctrl->cmb)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static void nvme_release_cmb(struct nvme_ctrl *ctrl)
+{
+	if (ctrl->cmb) {
+		kfree(ctrl->cmb);
+		ctrl->cmb = NULL;
+	}
+}
+
+void nvme_map_cmb(struct nvme_ctrl *ctrl)
+{
+	struct device *dev = ctrl->device;
+
+	if (ctrl->ops->map_cmb(ctrl))
+		return;
+
+	if (sysfs_create_group(&dev->kobj, &nvme_cmb_attr_group))
+		dev_warn(dev, "failed to create sysfs group for CMB\n");
+}
+
+void nvme_unmap_cmb(struct nvme_ctrl *ctrl)
+{
+	struct device *dev = ctrl->device;
+	ctrl->ops->unmap_cmb(ctrl);
+	sysfs_remove_group(&dev->kobj, &nvme_cmb_attr_group);
+}
+
 static DEFINE_IDA(nvme_instance_ida);
 
 static int nvme_set_instance(struct nvme_ctrl *ctrl)
@@ -1269,6 +1389,7 @@ static void nvme_free_ctrl(struct kref *kref)
 	struct nvme_ctrl *ctrl = container_of(kref, struct nvme_ctrl, kref);
 
 	put_device(ctrl->device);
+	nvme_release_cmb(ctrl);
 	nvme_release_instance(ctrl);
 
 	ctrl->ops->free_ctrl(ctrl);
@@ -1309,16 +1430,22 @@ int nvme_init_ctrl(struct nvme_ctrl *ctrl, struct device *dev,
 	get_device(ctrl->device);
 	dev_set_drvdata(ctrl->device, ctrl);
 
-	ret = device_create_file(ctrl->device, &dev_attr_reset_controller);
+	ret = nvme_init_cmb(ctrl);
 	if (ret)
 		goto out_put_device;
 
+	ret = device_create_file(ctrl->device, &dev_attr_reset_controller);
+	if (ret)
+		goto out_release_cmb;
+
 	spin_lock(&dev_list_lock);
 	list_add_tail(&ctrl->node, &nvme_ctrl_list);
 	spin_unlock(&dev_list_lock);
 
 	return 0;
 
+out_release_cmb:
+	nvme_release_cmb(ctrl);
 out_put_device:
 	put_device(ctrl->device);
 	device_destroy(nvme_class, MKDEV(nvme_char_major, ctrl->instance));
drivers/nvme/host/nvme.h +22 -0
index d88cf45..3360b4e 100644
@@ -71,6 +71,7 @@ struct nvme_ctrl {
 	struct list_head namespaces;
 	struct device *device;	/* char device */
 	struct list_head node;
+	struct nvme_cmb *cmb;
 
 	char name[12];
 	char serial[20];
@@ -115,6 +116,23 @@ struct nvme_ns {
 	u32 mode_select_block_len;
 };
 
+struct nvme_cmb {
+	void __iomem *cmb;
+	dma_addr_t dma_addr;
+	u64 size;
+	u64 sq_offset;
+	u16 sq_depth;
+	unsigned long flags;
+};
+
+enum nvme_cmb_flags {
+	NVME_CMB_SQ_SUPPORTED	= (1 << 0),
+	NVME_CMB_CQ_SUPPORTED	= (1 << 1),
+	NVME_CMB_WD_SUPPORTED	= (1 << 2),
+	NVME_CMB_RD_SUPPORTED	= (1 << 3),
+	NVME_CMB_PRP_SUPPORTED	= (1 << 4),
+};
+
 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);
@@ -122,6 +140,8 @@ struct nvme_ctrl_ops {
 	bool (*io_incapable)(struct nvme_ctrl *ctrl);
 	int (*reset_ctrl)(struct nvme_ctrl *ctrl);
 	void (*free_ctrl)(struct nvme_ctrl *ctrl);
+	int (*map_cmb)(struct nvme_ctrl *ctrl);
+	void (*unmap_cmb)(struct nvme_ctrl *ctrl);
 };
 
 static inline bool nvme_ctrl_ready(struct nvme_ctrl *ctrl)
@@ -236,6 +256,8 @@ int nvme_init_identify(struct nvme_ctrl *ctrl);
 
 void nvme_scan_namespaces(struct nvme_ctrl *ctrl);
 void nvme_remove_namespaces(struct nvme_ctrl *ctrl);
+void nvme_map_cmb(struct nvme_ctrl *ctrl);
+void nvme_unmap_cmb(struct nvme_ctrl *ctrl);
 
 struct request *nvme_alloc_request(struct request_queue *q,
 		struct nvme_command *cmd, unsigned int flags);
drivers/nvme/host/pci.c +107 -67

312 lines not on this page · raw message

Signature · 1 line
2.1.4
#2 lore raw

PATCH 2/3 NVMe: Generate resource tree for CMB

In reply to Jon Derrick

Maintains a resource tree for CMB resources. A sysfs file is exposed which is similar to a pci_dev resources file. The top entry is the range of the whole CMB resource, and the entries below that are the driver-reserved regions (currently only used by SQes).

Signed-off-by Jon Derrick <jonathan.derrick at intel.com>
---
 drivers/nvme/host/core.c |  2 +-
 drivers/nvme/host/nvme.h |  3 +-
 drivers/nvme/host/pci.c  | 76 +++++++++++++++++++++++++++++-------------------
 3 files changed, 48 insertions(+), 33 deletions(-)
drivers/nvme/host/core.c +1 -1
index 6aed4b9..5d12ae4 100644
@@ -1014,7 +1014,7 @@ static ssize_t nvme_cmb_sq_offset_store(struct device *dev,
 	u64 sq_offset;
 
 	sscanf(buf, "%llu", &sq_offset);
-	if (sq_offset >= cmb->size)
+	if (sq_offset >= resource_size(cmb->res))
 		return -EINVAL;
 
 	cmb->sq_offset = sq_offset;
drivers/nvme/host/nvme.h +1 -2
index 3360b4e..dec61a3 100644
@@ -117,9 +117,8 @@ struct nvme_ns {
 };
 
 struct nvme_cmb {
+	struct resource *res;
 	void __iomem *cmb;
-	dma_addr_t dma_addr;
-	u64 size;
 	u64 sq_offset;
 	u16 sq_depth;
 	unsigned long flags;
drivers/nvme/host/pci.c +46 -30

168 lines not on this page · raw message

Signature · 1 line
2.1.4
#3 lore raw

PATCH 2/3 NVMe: Generate resource tree for CMB

Quoted 5 lines

Maintains a resource tree for CMB resources. A sysfs file is exposed which is similar to a pci_dev resources file. The top entry is the range of the whole CMB resource, and the entries below that are the driver-reserved regions (currently only used by SQes).

2 & 3's logs got mixed up when I split them.
If the patches look fine I will resend with better logs.

#4 lore raw

PATCH 3/3 NVMe: Create CMB resource sysfs file

In reply to Jon Derrick

Creates a CMB resource file which lists the whole CMB resource in the first line, and allocations in each line below it. This information is already reflected in /proc/iomem in the PCI NVMe driver.

Signed-off-by Jon Derrick <jonathan.derrick at intel.com>
---
 drivers/nvme/host/core.c | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)
drivers/nvme/host/core.c +28 -0
index 5d12ae4..c691c57 100644
@@ -969,6 +969,29 @@ static ssize_t nvme_sysfs_reset(struct device *dev,
 }
 static DEVICE_ATTR(reset_controller, S_IWUSR, NULL, nvme_sysfs_reset);
 
+static ssize_t nvme_cmb_resource_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
+	struct nvme_cmb *cmb = ctrl->cmb;
+	struct resource *res = cmb->res;
+	char *str = buf;
+
+	str += sprintf(str, "0x%016llx 0x%016llx 0x%016llx\n",
+			(unsigned long long)res->start,
+			(unsigned long long)res->end,
+			(unsigned long long)res->flags);
+	for (res = res->child; res; res = res->sibling) {
+		str += sprintf(str, "0x%016llx 0x%016llx 0x%016llx\n",
+				(unsigned long long)res->start,
+				(unsigned long long)res->end,
+				(unsigned long long)res->flags);
+	}
+	return (str - buf);
+}
+static DEVICE_ATTR(cmb_resource, S_IRUGO, nvme_cmb_resource_show, NULL);
+
 static ssize_t nvme_cmb_sq_depth_show(struct device *dev,
 					struct device_attribute *attr,
 					char *buf)
@@ -1024,6 +1047,7 @@ static DEVICE_ATTR(cmb_sq_offset, S_IWUSR | S_IRUGO, nvme_cmb_sq_offset_show,
 						nvme_cmb_sq_offset_store);
 
 static struct attribute *nvme_cmb_attrs[] = {
+	&dev_attr_cmb_resource.attr,
 	&dev_attr_cmb_sq_depth.attr,
 	&dev_attr_cmb_sq_offset.attr,
 	NULL
@@ -1036,6 +1060,10 @@ static umode_t nvme_cmb_attrs_are_visible(struct kobject *kobj,
 	struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
 	struct nvme_cmb *cmb = ctrl->cmb;
 
+	if (a == &dev_attr_cmb_resource.attr) {
+		if (!cmb->res || !resource_size(cmb->res))
+			return 0;
+	}
 	if ((a == &dev_attr_cmb_sq_depth.attr) ||
 	    (a == &dev_attr_cmb_sq_offset.attr)) {
 		if (!(cmb->flags & NVME_CMB_SQ_SUPPORTED))
Signature · 1 line
2.1.4
#5 lore raw

cover NVMe: Introduce CMB allocation scheme

In reply to Jon Derrick
Quoted 7 lines · Jon Derrick

Applies to Jens' for-4.5/nvme

This patchset changes the CMB allocation scheme. Instead of reserving the entire range for SQes and automatically placing the SQes in the CMB, this set creates sysfs knobs to manage it. This allows partial usage of the CMB for SQes, so that the remainder can be mmapped or used for other NVMe-defined CMB usages.

Thanks, Jon.

In general, I like the control and visibility this provides. With this, I think we're on the verge of warranting a new drivers/nvme/host/sysfs.c file to keep all the nvme sysfs management in one place.

I don't think anyone is in a rush to see this in the 4.5 merge window, so I'll load this on a CMB capable machine and provide a more thorough review in the next week or so.

Quoted 13 lines

Jon Derrick (3):
NVMe: Introduce sysfs entries for submission queues in CMB
NVMe: Generate resource tree for CMB
NVMe: Create CMB resource sysfs file

 drivers/nvme/host/core.c | 161 ++++++++++++++++++++++++++++++++++-
 drivers/nvme/host/nvme.h |  21 +++++
 drivers/nvme/host/pci.c  | 212 ++++++++++++++++++++++++++++++-----------------
 3 files changed, 313 insertions(+), 81 deletions(-)
Signature · 1 line
2.1.4
#6 lore raw

cover NVMe: Introduce CMB allocation scheme

Quoted 7 lines
Quoted 6 lines

This patchset changes the CMB allocation scheme. Instead of reserving the entire range for SQes and automatically placing the SQes in the CMB, this set creates sysfs knobs to manage it. This allows partial usage of the CMB for SQes, so that the remainder can be mmapped or used for other NVMe-defined CMB usages.

Quoted 5 lines

Thanks, Jon.

In general, I like the control and visibility this provides. With this, I think we're on the verge of warranting a new drivers/nvme/host/sysfs.c file to keep all the nvme sysfs management in one place.

I would like to +1 this idea. Seems like there is enough sysfs stuff now to warrant its own file.

Quoted 4 lines

I don't think anyone is in a rush to see this in the 4.5 merge window, so I'll load this on a CMB capable machine and provide a more thorough review in the next week or so.

I will do the same and hope to get you some feedback in the next week or so. Thanks for doing this!

Quoted 16 lines
Quoted 7 lines

Jon Derrick (3):
NVMe: Introduce sysfs entries for submission queues in CMB
NVMe: Generate resource tree for CMB
NVMe: Create CMB resource sysfs file

 drivers/nvme/host/core.c | 161

++++++++++++++++++++++++++++++++++-

Quoted 8 lines
 drivers/nvme/host/nvme.h |  21 +++++
 drivers/nvme/host/pci.c  | 212
++++++++++++++++++++++++++++++-----------------
 3 files changed, 313 insertions(+), 81 deletions(-)

--
2.1.4

#7 lore raw

cover NVMe: Introduce CMB allocation scheme

In reply to Keith Busch
Quoted 9 lines · Keith Busch
Quoted 5 lines · Jon Derrick

This patchset changes the CMB allocation scheme. Instead of reserving the entire range for SQes and automatically placing the SQes in the CMB, this set creates sysfs knobs to manage it. This allows partial usage of the CMB for SQes, so that the remainder can be mmapped or used for other NVMe-defined CMB usages.

Thanks, Jon.

Quoted 7 lines

In general, I like the control and visibility this provides. With this, I think we're on the verge of warranting a new drivers/nvme/host/sysfs.c file to keep all the nvme sysfs management in one place.

I don't think anyone is in a rush to see this in the 4.5 merge window, so I'll load this on a CMB capable machine and provide a more thorough review in the next week or so.

One potential problem, this allows the queue depth to change on a live request_queue. The block layer won't allow that right now. That can also theoretically happen on a controller reset, but doesn't happen in real life (that I know of). For this CMB proposal to really be useful, we need a new blk-mq API to let us change the depth at runtime.

I thought the dynamic h/w contexts patch[1] was 'ok' and may have provided a route to change the depth as well. That patch, though, saw a 0-day boot failure. The failure was incorrectly sent to my spam folder, and I only noticed it this week. If that can be acceptably resolved, then it looks possible to alter the queue depth with a bit more work.

---
[1] http://lists.infradead.org/pipermail/linux-nvme/2015-December/003452.html

#8 lore raw

cover NVMe: Introduce CMB allocation scheme

Quoted 18 lines · Keith Busch
Quoted 9 lines · Keith Busch
Quoted 5 lines · Jon Derrick

This patchset changes the CMB allocation scheme. Instead of reserving the entire range for SQes and automatically placing the SQes in the CMB, this set creates sysfs knobs to manage it. This allows partial usage of the CMB for SQes, so that the remainder can be mmapped or used for other NVMe-defined CMB usages.

Thanks, Jon.

Quoted 7 lines

In general, I like the control and visibility this provides. With this, I think we're on the verge of warranting a new drivers/nvme/host/sysfs.c file to keep all the nvme sysfs management in one place.

I don't think anyone is in a rush to see this in the 4.5 merge window, so I'll load this on a CMB capable machine and provide a more thorough review in the next week or so.

Quoted 6 lines

One potential problem, this allows the queue depth to change on a live request_queue. The block layer won't allow that right now. That can also theoretically happen on a controller reset, but doesn't happen in real life (that I know of). For this CMB proposal to really be useful, we need a new blk-mq API to let us change the depth at runtime.

Yep. I noticed the same thing today. It works coinicidentally -if- the depth remains the same before and after CMB allocation.

Quoted 5 lines

I thought the dynamic h/w contexts patch[1] was 'ok' and may have provided a route to change the depth as well. That patch, though, saw a 0-day boot failure. The failure was incorrectly sent to my spam folder, and I only noticed it this week. If that can be acceptably resolved, then it looks possible to alter the queue depth with a bit more work.

I've cleaned up the nvme-side depth dependencies but still don't have anything to modify the hardware contexts. I'll take a look at your patch in the next couple of days.