The kernel requires a region size to be a multiple of SZ_256M * ways. The
cxl-cli command, cxl create-region, accepts an explicit size with -s, or
derives the size using ALIGN_DOWN(). The derived size is not always valid.
The calculation can fail in two ways:
1) When the region interleave ways has a factor of three, SZ_256M * ways is
not a power of two, so ALIGN_DOWN() is not valid for the calculation. The
result depends on the root decoder extent and can appear correct by chance.
For example, a 6-way region with a 3G extent produces the expected 3G size,
while a 12-way region with the same extent is trimmed to 1G.
2) At 16 ways, SZ_256M * ways overflows the int used for the multiplication
and evaluates to zero, so the trim returns zero.
Compute the product in u64 to avoid the overflow and round down by division
so that the derived size is a multiple of SZ_256M * ways. Return -ENOSPC
when the root decoder window is smaller than SZ_256M * ways.
This was found while adding 12-way region test coverage.
Fixes: 2904504721a6 ("cxl/region: Trim region size by max available extent")
Signed-off-by: Alison Schofield <[email protected]>
---
cxl/region.c | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/cxl/region.c b/cxl/region.c
index 85d4d9bb54f2..9d727216b9b8 100644
--- a/cxl/region.c
+++ b/cxl/region.c
@@ -682,8 +682,18 @@ static int create_region(struct cxl_ctx *ctx, int *count,
return -ENOSPC;
}
- if (size > max_extent)
- size = ALIGN_DOWN(max_extent, SZ_256M * p->ways);
+ if (size > max_extent) {
+ u64 min_size = (u64)SZ_256M * p->ways;
+
+ size = (max_extent / min_size) * min_size;
+ if (!size) {
+ log_err(&rl,
+ "%s: max available space (%#lx) is less than
the minimum %d-way region size (%#lx)\n",
+ cxl_decoder_get_devname(p->root_decoder),
+ max_extent, p->ways, min_size);
+ return -ENOSPC;
+ }
+ }
if (p->mode == CXL_DECODER_MODE_PMEM) {
region = cxl_decoder_create_pmem_region(p->root_decoder);
--
2.37.3