cxl_region_determine_granularity() rejects any user-supplied granularity that does not exactly match the root decoder's granularity, making it impossible to create mixed-granularity regions from userspace even when the kernel driver accepts them.
Relax the check to allow region_gran <= root_gran. This function only needs to reject what it can determine on its own is always wrong - a region granularity greater than the root decoder's. The kernel does the rest of the validation. Reject a granularity that is not a power of two, or is below 256, since the previous exact-match test that screened those out implicitly is now gone. Update the cxl-create-region man page to document the new behavior. Signed-off-by: Alison Schofield <[email protected]> --- Documentation/cxl/cxl-create-region.txt | 21 ++++++++++++++++----- cxl/region.c | 25 +++++++++++++++++++------ 2 files changed, 35 insertions(+), 11 deletions(-) diff --git a/Documentation/cxl/cxl-create-region.txt b/Documentation/cxl/cxl-create-region.txt index b244af60b8a6..d5998ad725ee 100644 --- a/Documentation/cxl/cxl-create-region.txt +++ b/Documentation/cxl/cxl-create-region.txt @@ -93,11 +93,22 @@ include::bus-option.txt[] -g:: --granularity=:: - The interleave granularity for the new region. Must match the selected - root decoder's (if provided) granularity. If the root decoder is - interleaved across more than one host-bridge then this value must match - that granularity. Otherwise, for non-interleaved decode windows, any - granularity can be specified as long as all devices support that setting. + The interleave granularity for the new region. A granularity coarser + than the root decoder's is never legal. If the root decoder is + interleaved across more than one host bridge, then for a given number + of ways at most two granularities are legal: + + * "root_ways * root_granularity / ways", which creates a + mixed-granularity region. The root interleaves at its own coarser + granularity and the levels below it, host bridges and switches, + interleave at the finer region granularity. + * The root decoder granularity itself, which repeats the root + interleave pattern across a wider region. This is available only + when the root decoder ways are a power of two. + + The two coincide when the region ways equal the root decoder ways. For + non-interleaved root decoders, any granularity can be specified as + long as all devices support that setting. -d:: --decoder=:: diff --git a/cxl/region.c b/cxl/region.c index 9d727216b9b8..de9f487ddff9 100644 --- a/cxl/region.c +++ b/cxl/region.c @@ -620,6 +620,18 @@ static int cxl_region_determine_granularity(struct cxl_region *region, if (!p->granularity) return granularity; + /* + * The CXL specification defines interleave granularities of 256 through + * 16K in powers of two. Reject anything else here, rather than leave the + * user with a bare EINVAL from the kernel. + */ + if (p->granularity < 256 || !is_power_of_2(p->granularity)) { + log_err(&rl, + "%s: granularity (%d) must be a power of 2, 256 or greater\n", + devname, p->granularity); + return -EINVAL; + } + ways = cxl_decoder_get_interleave_ways(p->root_decoder); if (ways == 0 || ways == -1) { log_err(&rl, "%s: unable to determine root decoder ways\n", @@ -632,15 +644,16 @@ static int cxl_region_determine_granularity(struct cxl_region *region, return p->granularity; /* - * For ways > 1, only allow the same granularity as the selected - * root decoder + * For ways > 1, allow any region granularity up to and including the + * root decoder granularity. A finer region granularity produces a + * mixed-granularity configuration and a coarser one is always invalid. */ - if (p->granularity == granularity) - return granularity; + if (p->granularity <= granularity) + return p->granularity; log_err(&rl, - "%s: For an x%d root, only root decoder granularity (%d) permitted\n", - devname, ways, granularity); + "%s: region granularity (%d) cannot exceed root decoder granularity (%d)\n", + devname, p->granularity, granularity); return -EINVAL; } -- 2.37.3
