Blame SOURCES/0199-cxl-decoder-add-a-max_available_extent-attribute.patch

e0018b
From bf0c44e79c0db04b0c1eea884022dfbdc011b979 Mon Sep 17 00:00:00 2001
e0018b
From: Vishal Verma <vishal.l.verma@intel.com>
e0018b
Date: Mon, 15 Aug 2022 13:22:14 -0600
e0018b
Subject: [PATCH 199/217] cxl/decoder: add a max_available_extent attribute
e0018b
e0018b
Add a max_available_extent attribute to cxl_decoder. In order to aid in
e0018b
its calculation, change the order of regions in the root decoder's list
e0018b
to be sorted by start HPA of the region.
e0018b
e0018b
Additionally, emit this attribute in decoder listings, and consult it
e0018b
for available space before creating a new region.
e0018b
e0018b
Link: https://lore.kernel.org/r/20220815192214.545800-12-vishal.l.verma@intel.com
e0018b
Cc: Dan Williams <dan.j.williams@intel.com>
e0018b
Reviewed-by: Dan Williams <dan.j.williams@intel.com>
e0018b
Signed-off-by: Vishal Verma <vishal.l.verma@intel.com>
e0018b
---
e0018b
 cxl/json.c         |  8 +++++
e0018b
 cxl/lib/libcxl.c   | 84 +++++++++++++++++++++++++++++++++++++++++++++-
e0018b
 cxl/lib/libcxl.sym |  1 +
e0018b
 cxl/lib/private.h  |  1 +
e0018b
 cxl/libcxl.h       |  3 ++
e0018b
 cxl/region.c       | 14 +++++++-
e0018b
 6 files changed, 109 insertions(+), 2 deletions(-)
e0018b
e0018b
diff --git a/cxl/json.c b/cxl/json.c
e0018b
index 9dc99df..9cec58b 100644
e0018b
--- a/cxl/json.c
e0018b
+++ b/cxl/json.c
e0018b
@@ -499,6 +499,14 @@ struct json_object *util_cxl_decoder_to_json(struct cxl_decoder *decoder,
e0018b
 	}
e0018b
 
e0018b
 	if (cxl_port_is_root(port) && cxl_decoder_is_mem_capable(decoder)) {
e0018b
+		size = cxl_decoder_get_max_available_extent(decoder);
e0018b
+		if (size < ULLONG_MAX) {
e0018b
+			jobj = util_json_object_size(size, flags);
e0018b
+			if (jobj)
e0018b
+				json_object_object_add(jdecoder,
e0018b
+						       "max_available_extent",
e0018b
+						       jobj);
e0018b
+		}
e0018b
 		if (cxl_decoder_is_pmem_capable(decoder)) {
e0018b
 			jobj = json_object_new_boolean(true);
e0018b
 			if (jobj)
e0018b
diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c
e0018b
index fd2ea4f..c7dc2b0 100644
e0018b
--- a/cxl/lib/libcxl.c
e0018b
+++ b/cxl/lib/libcxl.c
e0018b
@@ -455,6 +455,16 @@ CXL_EXPORT int cxl_region_delete(struct cxl_region *region)
e0018b
 	return 0;
e0018b
 }
e0018b
 
e0018b
+static int region_start_cmp(struct cxl_region *r1, struct cxl_region *r2)
e0018b
+{
e0018b
+	if (r1->start == r2->start)
e0018b
+		return 0;
e0018b
+	else if (r1->start < r2->start)
e0018b
+		return -1;
e0018b
+	else
e0018b
+		return 1;
e0018b
+}
e0018b
+
e0018b
 static void *add_cxl_region(void *parent, int id, const char *cxlregion_base)
e0018b
 {
e0018b
 	const char *devname = devpath_to_devname(cxlregion_base);
e0018b
@@ -539,7 +549,7 @@ static void *add_cxl_region(void *parent, int id, const char *cxlregion_base)
e0018b
 			break;
e0018b
 		}
e0018b
 
e0018b
-	list_add(&decoder->regions, &region->list);
e0018b
+	list_add_sorted(&decoder->regions, region, list, region_start_cmp);
e0018b
 
e0018b
 	return region;
e0018b
 err:
e0018b
@@ -1618,6 +1628,70 @@ cxl_endpoint_get_memdev(struct cxl_endpoint *endpoint)
e0018b
 	return NULL;
e0018b
 }
e0018b
 
e0018b
+static bool cxl_region_is_configured(struct cxl_region *region)
e0018b
+{
e0018b
+	return region->size && (region->decode_state != CXL_DECODE_RESET);
e0018b
+}
e0018b
+
e0018b
+/**
e0018b
+ * cxl_decoder_calc_max_available_extent() - calculate max available free space
e0018b
+ * @decoder - the root decoder to calculate the free extents for
e0018b
+ *
e0018b
+ * The add_cxl_region() function  adds regions to the parent decoder's list
e0018b
+ * sorted by the region's start HPAs. It can also be assumed that regions have
e0018b
+ * no overlapped / aliased HPA space. Therefore, calculating each extent is as
e0018b
+ * simple as walking the region list in order, and subtracting the previous
e0018b
+ * region's end HPA from the next region's start HPA (and taking into account
e0018b
+ * the decoder's start and end HPAs as well).
e0018b
+ */
e0018b
+static unsigned long long
e0018b
+cxl_decoder_calc_max_available_extent(struct cxl_decoder *decoder)
e0018b
+{
e0018b
+	u64 prev_end, decoder_end, cur_extent, max_extent = 0;
e0018b
+	struct cxl_port *port = cxl_decoder_get_port(decoder);
e0018b
+	struct cxl_ctx *ctx = cxl_decoder_get_ctx(decoder);
e0018b
+	struct cxl_region *region;
e0018b
+
e0018b
+	if (!cxl_port_is_root(port)) {
e0018b
+		err(ctx, "%s: not a root decoder\n",
e0018b
+		    cxl_decoder_get_devname(decoder));
e0018b
+		return ULLONG_MAX;
e0018b
+	}
e0018b
+
e0018b
+	/*
e0018b
+	 * Preload prev_end with an imaginary region that ends just before
e0018b
+	 * the decoder's start, so that the extent calculation for the
e0018b
+	 * first region Just Works
e0018b
+	 */
e0018b
+	prev_end = decoder->start - 1;
e0018b
+
e0018b
+	cxl_region_foreach(decoder, region) {
e0018b
+		if (!cxl_region_is_configured(region))
e0018b
+			continue;
e0018b
+
e0018b
+		/*
e0018b
+		 * region->start - prev_end would get the difference in
e0018b
+		 * addresses, but a difference of 1 in addresses implies
e0018b
+		 * an extent of 0. Hence the '-1'.
e0018b
+		 */
e0018b
+		cur_extent = region->start - prev_end - 1;
e0018b
+		max_extent = max(max_extent, cur_extent);
e0018b
+		prev_end = region->start + region->size - 1;
e0018b
+	}
e0018b
+
e0018b
+	/*
e0018b
+	 * Finally, consider the extent after the last region, up to the end
e0018b
+	 * of the decoder's address space, if any. If there were no regions,
e0018b
+	 * this simply reduces to decoder->size.
e0018b
+	 * Subtracting two addrs gets us a 'size' directly, no need for +/- 1.
e0018b
+	 */
e0018b
+	decoder_end = decoder->start + decoder->size - 1;
e0018b
+	cur_extent = decoder_end - prev_end;
e0018b
+	max_extent = max(max_extent, cur_extent);
e0018b
+
e0018b
+	return max_extent;
e0018b
+}
e0018b
+
e0018b
 static int decoder_id_cmp(struct cxl_decoder *d1, struct cxl_decoder *d2)
e0018b
 {
e0018b
 	return d1->id - d2->id;
e0018b
@@ -1748,6 +1822,8 @@ static void *add_cxl_decoder(void *parent, int id, const char *cxldecoder_base)
e0018b
 			if (sysfs_read_attr(ctx, path, buf) == 0)
e0018b
 				*(flag->flag) = !!strtoul(buf, NULL, 0);
e0018b
 		}
e0018b
+		decoder->max_available_extent =
e0018b
+			cxl_decoder_calc_max_available_extent(decoder);
e0018b
 		break;
e0018b
 	}
e0018b
 	}
e0018b
@@ -1912,6 +1988,12 @@ cxl_decoder_get_dpa_size(struct cxl_decoder *decoder)
e0018b
 	return decoder->dpa_size;
e0018b
 }
e0018b
 
e0018b
+CXL_EXPORT unsigned long long
e0018b
+cxl_decoder_get_max_available_extent(struct cxl_decoder *decoder)
e0018b
+{
e0018b
+	return decoder->max_available_extent;
e0018b
+}
e0018b
+
e0018b
 CXL_EXPORT int cxl_decoder_set_dpa_size(struct cxl_decoder *decoder,
e0018b
 					unsigned long long size)
e0018b
 {
e0018b
diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym
e0018b
index cb23a0b..549f88d 100644
e0018b
--- a/cxl/lib/libcxl.sym
e0018b
+++ b/cxl/lib/libcxl.sym
e0018b
@@ -213,4 +213,5 @@ global:
e0018b
 	cxl_decoder_get_memdev;
e0018b
 	cxl_decoder_get_interleave_granularity;
e0018b
 	cxl_decoder_get_interleave_ways;
e0018b
+	cxl_decoder_get_max_available_extent;
e0018b
 } LIBCXL_2;
e0018b
diff --git a/cxl/lib/private.h b/cxl/lib/private.h
e0018b
index 8bc9620..437eade 100644
e0018b
--- a/cxl/lib/private.h
e0018b
+++ b/cxl/lib/private.h
e0018b
@@ -104,6 +104,7 @@ struct cxl_decoder {
e0018b
 	u64 size;
e0018b
 	u64 dpa_resource;
e0018b
 	u64 dpa_size;
e0018b
+	u64 max_available_extent;
e0018b
 	void *dev_buf;
e0018b
 	size_t buf_len;
e0018b
 	char *dev_path;
e0018b
diff --git a/cxl/libcxl.h b/cxl/libcxl.h
e0018b
index 69d9c09..61c7fc4 100644
e0018b
--- a/cxl/libcxl.h
e0018b
+++ b/cxl/libcxl.h
e0018b
@@ -134,6 +134,9 @@ unsigned long long cxl_decoder_get_resource(struct cxl_decoder *decoder);
e0018b
 unsigned long long cxl_decoder_get_size(struct cxl_decoder *decoder);
e0018b
 unsigned long long cxl_decoder_get_dpa_resource(struct cxl_decoder *decoder);
e0018b
 unsigned long long cxl_decoder_get_dpa_size(struct cxl_decoder *decoder);
e0018b
+unsigned long long
e0018b
+cxl_decoder_get_max_available_extent(struct cxl_decoder *decoder);
e0018b
+
e0018b
 enum cxl_decoder_mode {
e0018b
 	CXL_DECODER_MODE_NONE,
e0018b
 	CXL_DECODER_MODE_MIXED,
e0018b
diff --git a/cxl/region.c b/cxl/region.c
e0018b
index b22d3c8..a30313c 100644
e0018b
--- a/cxl/region.c
e0018b
+++ b/cxl/region.c
e0018b
@@ -438,9 +438,9 @@ static int create_region(struct cxl_ctx *ctx, int *count,
e0018b
 	struct json_object *jregion;
e0018b
 	unsigned int i, granularity;
e0018b
 	struct cxl_region *region;
e0018b
+	u64 size, max_extent;
e0018b
 	const char *devname;
e0018b
 	uuid_t uuid;
e0018b
-	u64 size;
e0018b
 	int rc;
e0018b
 
e0018b
 	rc = create_region_validate_config(ctx, p);
e0018b
@@ -455,6 +455,18 @@ static int create_region(struct cxl_ctx *ctx, int *count,
e0018b
 		log_err(&rl, "%s: unable to determine region size\n", __func__);
e0018b
 		return -ENXIO;
e0018b
 	}
e0018b
+	max_extent = cxl_decoder_get_max_available_extent(p->root_decoder);
e0018b
+	if (max_extent == ULLONG_MAX) {
e0018b
+		log_err(&rl, "%s: unable to determine max extent\n",
e0018b
+			cxl_decoder_get_devname(p->root_decoder));
e0018b
+		return -EINVAL;
e0018b
+	}
e0018b
+	if (size > max_extent) {
e0018b
+		log_err(&rl,
e0018b
+			"%s: region size %#lx exceeds max available space\n",
e0018b
+			cxl_decoder_get_devname(p->root_decoder), size);
e0018b
+		return -ENOSPC;
e0018b
+	}
e0018b
 
e0018b
 	if (p->mode == CXL_DECODER_MODE_PMEM) {
e0018b
 		region = cxl_decoder_create_pmem_region(p->root_decoder);
e0018b
-- 
e0018b
2.27.0
e0018b