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


Reply via email to