diff options
-rw-r--r-- | cxl/json.c | 8 | ||||
-rw-r--r-- | cxl/lib/libcxl.c | 84 | ||||
-rw-r--r-- | cxl/lib/libcxl.sym | 1 | ||||
-rw-r--r-- | cxl/lib/private.h | 1 | ||||
-rw-r--r-- | cxl/libcxl.h | 3 | ||||
-rw-r--r-- | cxl/region.c | 14 |
6 files changed, 109 insertions, 2 deletions
@@ -499,6 +499,14 @@ struct json_object *util_cxl_decoder_to_json(struct cxl_decoder *decoder, } if (cxl_port_is_root(port) && cxl_decoder_is_mem_capable(decoder)) { + size = cxl_decoder_get_max_available_extent(decoder); + if (size < ULLONG_MAX) { + jobj = util_json_object_size(size, flags); + if (jobj) + json_object_object_add(jdecoder, + "max_available_extent", + jobj); + } if (cxl_decoder_is_pmem_capable(decoder)) { jobj = json_object_new_boolean(true); if (jobj) diff --git a/cxl/lib/libcxl.c b/cxl/lib/libcxl.c index 2b1cf7e0..a40b0e6a 100644 --- a/cxl/lib/libcxl.c +++ b/cxl/lib/libcxl.c @@ -455,6 +455,16 @@ CXL_EXPORT int cxl_region_delete(struct cxl_region *region) return 0; } +static int region_start_cmp(struct cxl_region *r1, struct cxl_region *r2) +{ + if (r1->start == r2->start) + return 0; + else if (r1->start < r2->start) + return -1; + else + return 1; +} + static void *add_cxl_region(void *parent, int id, const char *cxlregion_base) { const char *devname = devpath_to_devname(cxlregion_base); @@ -539,7 +549,7 @@ static void *add_cxl_region(void *parent, int id, const char *cxlregion_base) break; } - list_add(&decoder->regions, ®ion->list); + list_add_sorted(&decoder->regions, region, list, region_start_cmp); return region; err: @@ -1617,6 +1627,70 @@ cxl_endpoint_get_memdev(struct cxl_endpoint *endpoint) return NULL; } +static bool cxl_region_is_configured(struct cxl_region *region) +{ + return region->size && (region->decode_state != CXL_DECODE_RESET); +} + +/** + * cxl_decoder_calc_max_available_extent() - calculate max available free space + * @decoder - the root decoder to calculate the free extents for + * + * The add_cxl_region() function adds regions to the parent decoder's list + * sorted by the region's start HPAs. It can also be assumed that regions have + * no overlapped / aliased HPA space. Therefore, calculating each extent is as + * simple as walking the region list in order, and subtracting the previous + * region's end HPA from the next region's start HPA (and taking into account + * the decoder's start and end HPAs as well). + */ +static unsigned long long +cxl_decoder_calc_max_available_extent(struct cxl_decoder *decoder) +{ + u64 prev_end, decoder_end, cur_extent, max_extent = 0; + struct cxl_port *port = cxl_decoder_get_port(decoder); + struct cxl_ctx *ctx = cxl_decoder_get_ctx(decoder); + struct cxl_region *region; + + if (!cxl_port_is_root(port)) { + err(ctx, "%s: not a root decoder\n", + cxl_decoder_get_devname(decoder)); + return ULLONG_MAX; + } + + /* + * Preload prev_end with an imaginary region that ends just before + * the decoder's start, so that the extent calculation for the + * first region Just Works + */ + prev_end = decoder->start - 1; + + cxl_region_foreach(decoder, region) { + if (!cxl_region_is_configured(region)) + continue; + + /* + * region->start - prev_end would get the difference in + * addresses, but a difference of 1 in addresses implies + * an extent of 0. Hence the '-1'. + */ + cur_extent = region->start - prev_end - 1; + max_extent = max(max_extent, cur_extent); + prev_end = region->start + region->size - 1; + } + + /* + * Finally, consider the extent after the last region, up to the end + * of the decoder's address space, if any. If there were no regions, + * this simply reduces to decoder->size. + * Subtracting two addrs gets us a 'size' directly, no need for +/- 1. + */ + decoder_end = decoder->start + decoder->size - 1; + cur_extent = decoder_end - prev_end; + max_extent = max(max_extent, cur_extent); + + return max_extent; +} + static int decoder_id_cmp(struct cxl_decoder *d1, struct cxl_decoder *d2) { return d1->id - d2->id; @@ -1747,6 +1821,8 @@ static void *add_cxl_decoder(void *parent, int id, const char *cxldecoder_base) if (sysfs_read_attr(ctx, path, buf) == 0) *(flag->flag) = !!strtoul(buf, NULL, 0); } + decoder->max_available_extent = + cxl_decoder_calc_max_available_extent(decoder); break; } } @@ -1911,6 +1987,12 @@ cxl_decoder_get_dpa_size(struct cxl_decoder *decoder) return decoder->dpa_size; } +CXL_EXPORT unsigned long long +cxl_decoder_get_max_available_extent(struct cxl_decoder *decoder) +{ + return decoder->max_available_extent; +} + CXL_EXPORT int cxl_decoder_set_dpa_size(struct cxl_decoder *decoder, unsigned long long size) { diff --git a/cxl/lib/libcxl.sym b/cxl/lib/libcxl.sym index cb23a0b0..549f88da 100644 --- a/cxl/lib/libcxl.sym +++ b/cxl/lib/libcxl.sym @@ -213,4 +213,5 @@ global: cxl_decoder_get_memdev; cxl_decoder_get_interleave_granularity; cxl_decoder_get_interleave_ways; + cxl_decoder_get_max_available_extent; } LIBCXL_2; diff --git a/cxl/lib/private.h b/cxl/lib/private.h index 8bc96209..437eadeb 100644 --- a/cxl/lib/private.h +++ b/cxl/lib/private.h @@ -104,6 +104,7 @@ struct cxl_decoder { u64 size; u64 dpa_resource; u64 dpa_size; + u64 max_available_extent; void *dev_buf; size_t buf_len; char *dev_path; diff --git a/cxl/libcxl.h b/cxl/libcxl.h index 69d9c099..61c7fc4e 100644 --- a/cxl/libcxl.h +++ b/cxl/libcxl.h @@ -134,6 +134,9 @@ unsigned long long cxl_decoder_get_resource(struct cxl_decoder *decoder); unsigned long long cxl_decoder_get_size(struct cxl_decoder *decoder); unsigned long long cxl_decoder_get_dpa_resource(struct cxl_decoder *decoder); unsigned long long cxl_decoder_get_dpa_size(struct cxl_decoder *decoder); +unsigned long long +cxl_decoder_get_max_available_extent(struct cxl_decoder *decoder); + enum cxl_decoder_mode { CXL_DECODER_MODE_NONE, CXL_DECODER_MODE_MIXED, diff --git a/cxl/region.c b/cxl/region.c index b22d3c8e..a30313c5 100644 --- a/cxl/region.c +++ b/cxl/region.c @@ -438,9 +438,9 @@ static int create_region(struct cxl_ctx *ctx, int *count, struct json_object *jregion; unsigned int i, granularity; struct cxl_region *region; + u64 size, max_extent; const char *devname; uuid_t uuid; - u64 size; int rc; rc = create_region_validate_config(ctx, p); @@ -455,6 +455,18 @@ static int create_region(struct cxl_ctx *ctx, int *count, log_err(&rl, "%s: unable to determine region size\n", __func__); return -ENXIO; } + max_extent = cxl_decoder_get_max_available_extent(p->root_decoder); + if (max_extent == ULLONG_MAX) { + log_err(&rl, "%s: unable to determine max extent\n", + cxl_decoder_get_devname(p->root_decoder)); + return -EINVAL; + } + if (size > max_extent) { + log_err(&rl, + "%s: region size %#lx exceeds max available space\n", + cxl_decoder_get_devname(p->root_decoder), size); + return -ENOSPC; + } if (p->mode == CXL_DECODER_MODE_PMEM) { region = cxl_decoder_create_pmem_region(p->root_decoder); |