Message ID | 20250224235817.2259508-1-alison.schofield@intel.com |
---|---|
State | New |
Headers | show |
Series | cxl/region: Allow 6 & 12 way regions on 3-way HB interleaves | expand |
On 2/24/25 4:58 PM, alison.schofield@intel.com wrote: > From: Alison Schofield <alison.schofield@intel.com> > > The CXL driver requires the granularity of a region and it's root s/it's/its/ > decoder to be the same. This is particularly restrictive for 3-way > host bridge interleaves where the only spec defined interleave > configurations for creating 6-way and 12-way regions on a 3-way HB configuration? > interleave require mixed granularities. requries? > > CXL 3.1 Specification 9.13.1.1: May as well go to 3.2 > Legal Interleaving Configurations: 12-way, 6-way, and 3-way > > Adding support for these new interleaves touches these areas: > > 1) User created regions employing "cxl create-region" fail when the > ndctl tool gets a mixed granularity request. That is addressed in s/ndctl/CXL CLI/ > a patch to the ndctl tool. > > 2) User created regions employing sysfs directly fail at the sysfs > store of the non-matching region granularity. That restriction is > lifted here. Note that the driver immediately allows any reasonable > 3-way HB granularity, but the region config may still fail later > if the ways/gran between region and root decoder are incompatible. > > 3) Auto created regions, in which the drivers role is to reflect > the BIOS created region and provide RAS support, basically sneak on > by. The driver ignores the root decoders granularity and assumes it > is the same as the regions. The impact being that endpoint devices > appear out of order and DPA to HPA address translations that depend > on that order are invalid. Here the driver stops making that same > granularity assumption and checks for an allowable granularity. > > A new helper, interleave_granularity_allow(), is used in both the > user and auto creation path to allow the newly supported configs. > > Another new helper, gran_multiple(), is used in sorting, target > list searching, and distance calculations, supporting the new > root granularity to region granularity multiples. > > And, as noted in 2), there's an added check to see if the > selected sysfs ways/gran make sense when considered together. > > Signed-off-by: Alison Schofield <alison.schofield@intel.com> > --- > drivers/cxl/core/region.c | 101 +++++++++++++++++++++++++++++++------- > 1 file changed, 82 insertions(+), 19 deletions(-) > > diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c > index 3cb91cf0e2dd..9d4716b7fcf8 100644 > --- a/drivers/cxl/core/region.c > +++ b/drivers/cxl/core/region.c > @@ -532,6 +532,31 @@ static ssize_t interleave_granularity_show(struct device *dev, > return rc; > } > > +static bool interleave_granularity_allow(struct cxl_decoder *cxld, u16 ig) > +{ > + /* > + * When the host-bridge is interleaved, disallow region granularity > + * != root granularity with the exception of 3-way HB interleaves. > + * Allow the CXL Spec defined 3-way HB interleaves that can only be > + * configured when host-bridge interleave is greater that the > + * region interleave. (CXL 3.1 Specification 9.13.1.1) spec 3.2 > + * Allow 2+2+2 interleave where HB gran is 2 * region granularity > + * 4+4+4 interleave where HB gran is 4 * region granularity > + * > + * Regions with a granularity greater than the root interleave result > + * in invalid DPA translations (invalid to support). > + */ > + if (cxld->interleave_ways > 1 && ig != cxld->interleave_granularity) { > + if (cxld->interleave_ways != 3) > + return false; > + > + if (cxld->interleave_granularity % (2 * ig) && > + cxld->interleave_granularity % (4 * ig)) > + return false; Can you please explain how the math works here? I'm not understanding the code here vs the comments above. DJ > + } > + return true; > +} > + > static ssize_t interleave_granularity_store(struct device *dev, > struct device_attribute *attr, > const char *buf, size_t len) > @@ -551,15 +576,7 @@ static ssize_t interleave_granularity_store(struct device *dev, > if (rc) > return rc; > > - /* > - * When the host-bridge is interleaved, disallow region granularity != > - * root granularity. Regions with a granularity less than the root > - * interleave result in needing multiple endpoints to support a single > - * slot in the interleave (possible to support in the future). Regions > - * with a granularity greater than the root interleave result in invalid > - * DPA translations (invalid to support). > - */ > - if (cxld->interleave_ways > 1 && val != cxld->interleave_granularity) > + if (!interleave_granularity_allow(cxld, val)) > return -EINVAL; > > rc = down_write_killable(&cxl_region_rwsem); > @@ -1290,6 +1307,23 @@ static int check_interleave_cap(struct cxl_decoder *cxld, int iw, int ig) > return 0; > } > > +static int gran_multiple(struct cxl_region *cxlr) > +{ > + struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(cxlr->dev.parent); > + int root_decoder_gran = cxlrd->cxlsd.cxld.interleave_granularity; > + int region_gran = cxlr->params.interleave_granularity; > + > + /* > + * In regions built upon 3-way HB interleaves, the root decoder > + * granularity can be a multiple of the region granularity. The > + * multiple value is used in sorting and distance calculations. > + */ > + if (cxlrd->cxlsd.cxld.interleave_ways != 3 || !root_decoder_gran) > + return 1; > + > + return root_decoder_gran / region_gran; > +} > + > static int cxl_port_setup_targets(struct cxl_port *port, > struct cxl_region *cxlr, > struct cxl_endpoint_decoder *cxled) > @@ -1321,6 +1355,7 @@ static int cxl_port_setup_targets(struct cxl_port *port, > cxlsd = to_cxl_switch_decoder(&cxld->dev); > if (cxl_rr->nr_targets_set) { > int i, distance = 1; > + int multiple = gran_multiple(cxlr); > struct cxl_region_ref *cxl_rr_iter; > > /* > @@ -1332,12 +1367,17 @@ static int cxl_port_setup_targets(struct cxl_port *port, > * always 1 as every index targets a different host-bridge. At > * each subsequent switch level those ports map every Nth region > * position where N is the width of the switch == distance. > + * > + * With the introduction of mixed granularities in 3-way HB > + * interleaves, divide N by a multiple that represents the root > + * decoder to region granularity. > */ > do { > cxl_rr_iter = cxl_rr_load(iter, cxlr); > distance *= cxl_rr_iter->nr_targets; > iter = to_cxl_port(iter->dev.parent); > } while (!is_cxl_root(iter)); > + distance /= multiple; > distance *= cxlrd->cxlsd.cxld.interleave_ways; > > for (i = 0; i < cxl_rr->nr_targets_set; i++) > @@ -1354,12 +1394,9 @@ static int cxl_port_setup_targets(struct cxl_port *port, > if (is_cxl_root(parent_port)) { > /* > * Root decoder IG is always set to value in CFMWS which > - * may be different than this region's IG. We can use the > - * region's IG here since interleave_granularity_store() > - * does not allow interleaved host-bridges with > - * root IG != region IG. > + * may be different than this region's IG. > */ > - parent_ig = p->interleave_granularity; > + parent_ig = cxlrd->cxlsd.cxld.interleave_granularity; > parent_iw = cxlrd->cxlsd.cxld.interleave_ways; > /* > * For purposes of address bit routing, use power-of-2 math for > @@ -1431,7 +1468,7 @@ static int cxl_port_setup_targets(struct cxl_port *port, > > if (test_bit(CXL_REGION_F_AUTO, &cxlr->flags)) { > if (cxld->interleave_ways != iw || > - cxld->interleave_granularity != ig || > + !interleave_granularity_allow(cxld, ig) || > cxld->hpa_range.start != p->res->start || > cxld->hpa_range.end != p->res->end || > ((cxld->flags & CXL_DECODER_F_ENABLE) == 0)) { > @@ -1661,11 +1698,12 @@ static int cxl_region_attach_position(struct cxl_region *cxlr, > struct cxl_memdev *cxlmd = cxled_to_memdev(cxled); > struct cxl_switch_decoder *cxlsd = &cxlrd->cxlsd; > struct cxl_decoder *cxld = &cxlsd->cxld; > + int multiple = gran_multiple(cxlr); > int iw = cxld->interleave_ways; > struct cxl_port *iter; > int rc; > > - if (dport != cxlrd->cxlsd.target[pos % iw]) { > + if (dport != cxlrd->cxlsd.target[pos / multiple % iw]) { > dev_dbg(&cxlr->dev, "%s:%s invalid target position for %s\n", > dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev), > dev_name(&cxlrd->cxlsd.cxld.dev)); > @@ -1809,7 +1847,8 @@ static int find_pos_and_ways(struct cxl_port *port, struct range *range, > * Return: position >= 0 on success > * -ENXIO on failure > */ > -static int cxl_calc_interleave_pos(struct cxl_endpoint_decoder *cxled) > +static int cxl_calc_interleave_pos(struct cxl_endpoint_decoder *cxled, > + int multiple) > { > struct cxl_port *iter, *port = cxled_to_port(cxled); > struct cxl_memdev *cxlmd = cxled_to_memdev(cxled); > @@ -1855,6 +1894,11 @@ static int cxl_calc_interleave_pos(struct cxl_endpoint_decoder *cxled) > if (rc) > return rc; > > + if (multiple > 1 && is_cxl_root(next_port(iter))) { > + pos = pos + multiple * parent_pos; > + break; > + } > + > pos = pos * parent_ways + parent_pos; > } > > @@ -1869,12 +1913,13 @@ static int cxl_calc_interleave_pos(struct cxl_endpoint_decoder *cxled) > static int cxl_region_sort_targets(struct cxl_region *cxlr) > { > struct cxl_region_params *p = &cxlr->params; > + int multiple = gran_multiple(cxlr); > int i, rc = 0; > > for (i = 0; i < p->nr_targets; i++) { > struct cxl_endpoint_decoder *cxled = p->targets[i]; > > - cxled->pos = cxl_calc_interleave_pos(cxled); > + cxled->pos = cxl_calc_interleave_pos(cxled, multiple); > /* > * Record that sorting failed, but still continue to calc > * cxled->pos so that follow-on code paths can reliably > @@ -1902,6 +1947,23 @@ static int cxl_region_attach(struct cxl_region *cxlr, > struct cxl_dport *dport; > int rc = -ENXIO; > > + /* > + * Protect against improper gran mixes on 3-way HB interleave > + * Expect decoder gran*ways == region gran*ways > + */ > + if (cxlrd->cxlsd.cxld.interleave_ways == 3) { > + if ((cxlrd->cxlsd.cxld.interleave_granularity * 3) != > + (p->interleave_ways * p->interleave_granularity)) { > + dev_dbg(&cxlr->dev, > + "invalid config region:%d/%d decoder %d/%d\n", > + p->interleave_ways, p->interleave_granularity, > + cxlrd->cxlsd.cxld.interleave_ways, > + cxlrd->cxlsd.cxld.interleave_granularity); > + > + return rc; > + } > + } > + > rc = check_interleave_cap(&cxled->cxld, p->interleave_ways, > p->interleave_granularity); > if (rc) { > @@ -2055,9 +2117,10 @@ static int cxl_region_attach(struct cxl_region *cxlr, > */ > for (int i = 0; i < p->nr_targets; i++) { > struct cxl_endpoint_decoder *cxled = p->targets[i]; > + int multiple = gran_multiple(cxlr); > int test_pos; > > - test_pos = cxl_calc_interleave_pos(cxled); > + test_pos = cxl_calc_interleave_pos(cxled, multiple); > dev_dbg(&cxled->cxld.dev, > "Test cxl_calc_interleave_pos(): %s test_pos:%d cxled->pos:%d\n", > (test_pos == cxled->pos) ? "success" : "fail", > > base-commit: 2bb67004903cfd35710750654669a77e7223fcd1
On Mon, Feb 24, 2025 at 05:41:20PM -0700, Dave Jiang wrote: > > > On 2/24/25 4:58 PM, alison.schofield@intel.com wrote: > > From: Alison Schofield <alison.schofield@intel.com> > > > > The CXL driver requires the granularity of a region and it's root > > s/it's/its/ got it > > > decoder to be the same. This is particularly restrictive for 3-way > > host bridge interleaves where the only spec defined interleave > > configurations for creating 6-way and 12-way regions on a 3-way HB > configuration? > > interleave require mixed granularities. > requries? considering that > > > > > CXL 3.1 Specification 9.13.1.1: > > May as well go to 3.2 > got it > > Legal Interleaving Configurations: 12-way, 6-way, and 3-way > > > > Adding support for these new interleaves touches these areas: > > > > 1) User created regions employing "cxl create-region" fail when the > > ndctl tool gets a mixed granularity request. That is addressed in > s/ndctl/CXL CLI/ > got it > > a patch to the ndctl tool. > > snip > > > > +static bool interleave_granularity_allow(struct cxl_decoder *cxld, u16 ig) > > +{ > > + /* > > + * When the host-bridge is interleaved, disallow region granularity > > + * != root granularity with the exception of 3-way HB interleaves. > > + * Allow the CXL Spec defined 3-way HB interleaves that can only be > > + * configured when host-bridge interleave is greater that the > > + * region interleave. (CXL 3.1 Specification 9.13.1.1) > spec 3.2 > > > + * Allow 2+2+2 interleave where HB gran is 2 * region granularity > > + * 4+4+4 interleave where HB gran is 4 * region granularity > > + * > > + * Regions with a granularity greater than the root interleave result > > + * in invalid DPA translations (invalid to support). > > + */ > > + if (cxld->interleave_ways > 1 && ig != cxld->interleave_granularity) { > > + if (cxld->interleave_ways != 3) > > + return false; > > + > > + if (cxld->interleave_granularity % (2 * ig) && > > + cxld->interleave_granularity % (4 * ig)) > > + return false; > > Can you please explain how the math works here? I'm not understanding the code here vs the comments above. I'm guessing you are talking about the last chunk only. We get there after discovering a 3-way HB interleave. (HB interleave and root decoder interleave are the same thing.) Once we know it's a 3-way, there are only 2 possible values and those are defined in the spec as: 2+2+2 interleave where HB gran is 2 * region granularity 4+4+4 interleave where HB gran is 4 * region granularity And now that I've gotten to this part of the explanation I think the code is allowing multiples of 2*ig and 4*ig, when it should be a stricter check for exactly 2*ig or 4*ig. updating that to: if (cxld->interleave_granularity != 2 * ig && cxld->interleave_granularity != 4 * ig) return false; Thanks for that Dave! > > DJ > snip
Hi,
kernel test robot noticed the following build warnings:
[auto build test WARNING on 2bb67004903cfd35710750654669a77e7223fcd1]
url: https://github.com/intel-lab-lkp/linux/commits/alison-schofield-intel-com/cxl-region-Allow-6-12-way-regions-on-3-way-HB-interleaves/20250225-080107
base: 2bb67004903cfd35710750654669a77e7223fcd1
patch link: https://lore.kernel.org/r/20250224235817.2259508-1-alison.schofield%40intel.com
patch subject: [PATCH] cxl/region: Allow 6 & 12 way regions on 3-way HB interleaves
config: riscv-randconfig-001-20250225 (https://download.01.org/0day-ci/archive/20250225/202502251245.NLuc888W-lkp@intel.com/config)
compiler: clang version 15.0.7 (https://github.com/llvm/llvm-project 8dfdcc7b7bf66834a761bd8de445840ef68e4d1a)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250225/202502251245.NLuc888W-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202502251245.NLuc888W-lkp@intel.com/
All warnings (new ones prefixed by >>):
>> drivers/cxl/core/region.c:1852: warning: Function parameter or struct member 'multiple' not described in 'cxl_calc_interleave_pos'
vim +1852 drivers/cxl/core/region.c
a3e00c964fb9439 Alison Schofield 2023-10-27 1834
a3e00c964fb9439 Alison Schofield 2023-10-27 1835 /**
a3e00c964fb9439 Alison Schofield 2023-10-27 1836 * cxl_calc_interleave_pos() - calculate an endpoint position in a region
a3e00c964fb9439 Alison Schofield 2023-10-27 1837 * @cxled: endpoint decoder member of given region
a3e00c964fb9439 Alison Schofield 2023-10-27 1838 *
a3e00c964fb9439 Alison Schofield 2023-10-27 1839 * The endpoint position is calculated by traversing the topology from
a3e00c964fb9439 Alison Schofield 2023-10-27 1840 * the endpoint to the root decoder and iteratively applying this
a3e00c964fb9439 Alison Schofield 2023-10-27 1841 * calculation:
a3e00c964fb9439 Alison Schofield 2023-10-27 1842 *
a3e00c964fb9439 Alison Schofield 2023-10-27 1843 * position = position * parent_ways + parent_pos;
a3e00c964fb9439 Alison Schofield 2023-10-27 1844 *
a3e00c964fb9439 Alison Schofield 2023-10-27 1845 * ...where @position is inferred from switch and root decoder target lists.
a3e00c964fb9439 Alison Schofield 2023-10-27 1846 *
a3e00c964fb9439 Alison Schofield 2023-10-27 1847 * Return: position >= 0 on success
a3e00c964fb9439 Alison Schofield 2023-10-27 1848 * -ENXIO on failure
a3e00c964fb9439 Alison Schofield 2023-10-27 1849 */
22639743643957e Alison Schofield 2025-02-24 1850 static int cxl_calc_interleave_pos(struct cxl_endpoint_decoder *cxled,
22639743643957e Alison Schofield 2025-02-24 1851 int multiple)
a3e00c964fb9439 Alison Schofield 2023-10-27 @1852 {
a3e00c964fb9439 Alison Schofield 2023-10-27 1853 struct cxl_port *iter, *port = cxled_to_port(cxled);
a3e00c964fb9439 Alison Schofield 2023-10-27 1854 struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
a3e00c964fb9439 Alison Schofield 2023-10-27 1855 struct range *range = &cxled->cxld.hpa_range;
a3e00c964fb9439 Alison Schofield 2023-10-27 1856 int parent_ways = 0, parent_pos = 0, pos = 0;
a3e00c964fb9439 Alison Schofield 2023-10-27 1857 int rc;
a3e00c964fb9439 Alison Schofield 2023-10-27 1858
a3e00c964fb9439 Alison Schofield 2023-10-27 1859 /*
a3e00c964fb9439 Alison Schofield 2023-10-27 1860 * Example: the expected interleave order of the 4-way region shown
a3e00c964fb9439 Alison Schofield 2023-10-27 1861 * below is: mem0, mem2, mem1, mem3
a3e00c964fb9439 Alison Schofield 2023-10-27 1862 *
a3e00c964fb9439 Alison Schofield 2023-10-27 1863 * root_port
a3e00c964fb9439 Alison Schofield 2023-10-27 1864 * / \
a3e00c964fb9439 Alison Schofield 2023-10-27 1865 * host_bridge_0 host_bridge_1
a3e00c964fb9439 Alison Schofield 2023-10-27 1866 * | | | |
a3e00c964fb9439 Alison Schofield 2023-10-27 1867 * mem0 mem1 mem2 mem3
a3e00c964fb9439 Alison Schofield 2023-10-27 1868 *
a3e00c964fb9439 Alison Schofield 2023-10-27 1869 * In the example the calculator will iterate twice. The first iteration
a3e00c964fb9439 Alison Schofield 2023-10-27 1870 * uses the mem position in the host-bridge and the ways of the host-
a3e00c964fb9439 Alison Schofield 2023-10-27 1871 * bridge to generate the first, or local, position. The second
a3e00c964fb9439 Alison Schofield 2023-10-27 1872 * iteration uses the host-bridge position in the root_port and the ways
a3e00c964fb9439 Alison Schofield 2023-10-27 1873 * of the root_port to refine the position.
a3e00c964fb9439 Alison Schofield 2023-10-27 1874 *
a3e00c964fb9439 Alison Schofield 2023-10-27 1875 * A trace of the calculation per endpoint looks like this:
a3e00c964fb9439 Alison Schofield 2023-10-27 1876 * mem0: pos = 0 * 2 + 0 mem2: pos = 0 * 2 + 0
a3e00c964fb9439 Alison Schofield 2023-10-27 1877 * pos = 0 * 2 + 0 pos = 0 * 2 + 1
a3e00c964fb9439 Alison Schofield 2023-10-27 1878 * pos: 0 pos: 1
a3e00c964fb9439 Alison Schofield 2023-10-27 1879 *
a3e00c964fb9439 Alison Schofield 2023-10-27 1880 * mem1: pos = 0 * 2 + 1 mem3: pos = 0 * 2 + 1
a3e00c964fb9439 Alison Schofield 2023-10-27 1881 * pos = 1 * 2 + 0 pos = 1 * 2 + 1
a3e00c964fb9439 Alison Schofield 2023-10-27 1882 * pos: 2 pos = 3
a3e00c964fb9439 Alison Schofield 2023-10-27 1883 *
a3e00c964fb9439 Alison Schofield 2023-10-27 1884 * Note that while this example is simple, the method applies to more
a3e00c964fb9439 Alison Schofield 2023-10-27 1885 * complex topologies, including those with switches.
a3e00c964fb9439 Alison Schofield 2023-10-27 1886 */
a3e00c964fb9439 Alison Schofield 2023-10-27 1887
a3e00c964fb9439 Alison Schofield 2023-10-27 1888 /* Iterate from endpoint to root_port refining the position */
a3e00c964fb9439 Alison Schofield 2023-10-27 1889 for (iter = port; iter; iter = next_port(iter)) {
a3e00c964fb9439 Alison Schofield 2023-10-27 1890 if (is_cxl_root(iter))
a3e00c964fb9439 Alison Schofield 2023-10-27 1891 break;
a3e00c964fb9439 Alison Schofield 2023-10-27 1892
a3e00c964fb9439 Alison Schofield 2023-10-27 1893 rc = find_pos_and_ways(iter, range, &parent_pos, &parent_ways);
a3e00c964fb9439 Alison Schofield 2023-10-27 1894 if (rc)
a3e00c964fb9439 Alison Schofield 2023-10-27 1895 return rc;
a3e00c964fb9439 Alison Schofield 2023-10-27 1896
22639743643957e Alison Schofield 2025-02-24 1897 if (multiple > 1 && is_cxl_root(next_port(iter))) {
22639743643957e Alison Schofield 2025-02-24 1898 pos = pos + multiple * parent_pos;
22639743643957e Alison Schofield 2025-02-24 1899 break;
22639743643957e Alison Schofield 2025-02-24 1900 }
22639743643957e Alison Schofield 2025-02-24 1901
a3e00c964fb9439 Alison Schofield 2023-10-27 1902 pos = pos * parent_ways + parent_pos;
a3e00c964fb9439 Alison Schofield 2023-10-27 1903 }
a3e00c964fb9439 Alison Schofield 2023-10-27 1904
a3e00c964fb9439 Alison Schofield 2023-10-27 1905 dev_dbg(&cxlmd->dev,
a3e00c964fb9439 Alison Schofield 2023-10-27 1906 "decoder:%s parent:%s port:%s range:%#llx-%#llx pos:%d\n",
a3e00c964fb9439 Alison Schofield 2023-10-27 1907 dev_name(&cxled->cxld.dev), dev_name(cxlmd->dev.parent),
a3e00c964fb9439 Alison Schofield 2023-10-27 1908 dev_name(&port->dev), range->start, range->end, pos);
a3e00c964fb9439 Alison Schofield 2023-10-27 1909
a3e00c964fb9439 Alison Schofield 2023-10-27 1910 return pos;
a3e00c964fb9439 Alison Schofield 2023-10-27 1911 }
a3e00c964fb9439 Alison Schofield 2023-10-27 1912
diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c index 3cb91cf0e2dd..9d4716b7fcf8 100644 --- a/drivers/cxl/core/region.c +++ b/drivers/cxl/core/region.c @@ -532,6 +532,31 @@ static ssize_t interleave_granularity_show(struct device *dev, return rc; } +static bool interleave_granularity_allow(struct cxl_decoder *cxld, u16 ig) +{ + /* + * When the host-bridge is interleaved, disallow region granularity + * != root granularity with the exception of 3-way HB interleaves. + * Allow the CXL Spec defined 3-way HB interleaves that can only be + * configured when host-bridge interleave is greater that the + * region interleave. (CXL 3.1 Specification 9.13.1.1) + * Allow 2+2+2 interleave where HB gran is 2 * region granularity + * 4+4+4 interleave where HB gran is 4 * region granularity + * + * Regions with a granularity greater than the root interleave result + * in invalid DPA translations (invalid to support). + */ + if (cxld->interleave_ways > 1 && ig != cxld->interleave_granularity) { + if (cxld->interleave_ways != 3) + return false; + + if (cxld->interleave_granularity % (2 * ig) && + cxld->interleave_granularity % (4 * ig)) + return false; + } + return true; +} + static ssize_t interleave_granularity_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) @@ -551,15 +576,7 @@ static ssize_t interleave_granularity_store(struct device *dev, if (rc) return rc; - /* - * When the host-bridge is interleaved, disallow region granularity != - * root granularity. Regions with a granularity less than the root - * interleave result in needing multiple endpoints to support a single - * slot in the interleave (possible to support in the future). Regions - * with a granularity greater than the root interleave result in invalid - * DPA translations (invalid to support). - */ - if (cxld->interleave_ways > 1 && val != cxld->interleave_granularity) + if (!interleave_granularity_allow(cxld, val)) return -EINVAL; rc = down_write_killable(&cxl_region_rwsem); @@ -1290,6 +1307,23 @@ static int check_interleave_cap(struct cxl_decoder *cxld, int iw, int ig) return 0; } +static int gran_multiple(struct cxl_region *cxlr) +{ + struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(cxlr->dev.parent); + int root_decoder_gran = cxlrd->cxlsd.cxld.interleave_granularity; + int region_gran = cxlr->params.interleave_granularity; + + /* + * In regions built upon 3-way HB interleaves, the root decoder + * granularity can be a multiple of the region granularity. The + * multiple value is used in sorting and distance calculations. + */ + if (cxlrd->cxlsd.cxld.interleave_ways != 3 || !root_decoder_gran) + return 1; + + return root_decoder_gran / region_gran; +} + static int cxl_port_setup_targets(struct cxl_port *port, struct cxl_region *cxlr, struct cxl_endpoint_decoder *cxled) @@ -1321,6 +1355,7 @@ static int cxl_port_setup_targets(struct cxl_port *port, cxlsd = to_cxl_switch_decoder(&cxld->dev); if (cxl_rr->nr_targets_set) { int i, distance = 1; + int multiple = gran_multiple(cxlr); struct cxl_region_ref *cxl_rr_iter; /* @@ -1332,12 +1367,17 @@ static int cxl_port_setup_targets(struct cxl_port *port, * always 1 as every index targets a different host-bridge. At * each subsequent switch level those ports map every Nth region * position where N is the width of the switch == distance. + * + * With the introduction of mixed granularities in 3-way HB + * interleaves, divide N by a multiple that represents the root + * decoder to region granularity. */ do { cxl_rr_iter = cxl_rr_load(iter, cxlr); distance *= cxl_rr_iter->nr_targets; iter = to_cxl_port(iter->dev.parent); } while (!is_cxl_root(iter)); + distance /= multiple; distance *= cxlrd->cxlsd.cxld.interleave_ways; for (i = 0; i < cxl_rr->nr_targets_set; i++) @@ -1354,12 +1394,9 @@ static int cxl_port_setup_targets(struct cxl_port *port, if (is_cxl_root(parent_port)) { /* * Root decoder IG is always set to value in CFMWS which - * may be different than this region's IG. We can use the - * region's IG here since interleave_granularity_store() - * does not allow interleaved host-bridges with - * root IG != region IG. + * may be different than this region's IG. */ - parent_ig = p->interleave_granularity; + parent_ig = cxlrd->cxlsd.cxld.interleave_granularity; parent_iw = cxlrd->cxlsd.cxld.interleave_ways; /* * For purposes of address bit routing, use power-of-2 math for @@ -1431,7 +1468,7 @@ static int cxl_port_setup_targets(struct cxl_port *port, if (test_bit(CXL_REGION_F_AUTO, &cxlr->flags)) { if (cxld->interleave_ways != iw || - cxld->interleave_granularity != ig || + !interleave_granularity_allow(cxld, ig) || cxld->hpa_range.start != p->res->start || cxld->hpa_range.end != p->res->end || ((cxld->flags & CXL_DECODER_F_ENABLE) == 0)) { @@ -1661,11 +1698,12 @@ static int cxl_region_attach_position(struct cxl_region *cxlr, struct cxl_memdev *cxlmd = cxled_to_memdev(cxled); struct cxl_switch_decoder *cxlsd = &cxlrd->cxlsd; struct cxl_decoder *cxld = &cxlsd->cxld; + int multiple = gran_multiple(cxlr); int iw = cxld->interleave_ways; struct cxl_port *iter; int rc; - if (dport != cxlrd->cxlsd.target[pos % iw]) { + if (dport != cxlrd->cxlsd.target[pos / multiple % iw]) { dev_dbg(&cxlr->dev, "%s:%s invalid target position for %s\n", dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev), dev_name(&cxlrd->cxlsd.cxld.dev)); @@ -1809,7 +1847,8 @@ static int find_pos_and_ways(struct cxl_port *port, struct range *range, * Return: position >= 0 on success * -ENXIO on failure */ -static int cxl_calc_interleave_pos(struct cxl_endpoint_decoder *cxled) +static int cxl_calc_interleave_pos(struct cxl_endpoint_decoder *cxled, + int multiple) { struct cxl_port *iter, *port = cxled_to_port(cxled); struct cxl_memdev *cxlmd = cxled_to_memdev(cxled); @@ -1855,6 +1894,11 @@ static int cxl_calc_interleave_pos(struct cxl_endpoint_decoder *cxled) if (rc) return rc; + if (multiple > 1 && is_cxl_root(next_port(iter))) { + pos = pos + multiple * parent_pos; + break; + } + pos = pos * parent_ways + parent_pos; } @@ -1869,12 +1913,13 @@ static int cxl_calc_interleave_pos(struct cxl_endpoint_decoder *cxled) static int cxl_region_sort_targets(struct cxl_region *cxlr) { struct cxl_region_params *p = &cxlr->params; + int multiple = gran_multiple(cxlr); int i, rc = 0; for (i = 0; i < p->nr_targets; i++) { struct cxl_endpoint_decoder *cxled = p->targets[i]; - cxled->pos = cxl_calc_interleave_pos(cxled); + cxled->pos = cxl_calc_interleave_pos(cxled, multiple); /* * Record that sorting failed, but still continue to calc * cxled->pos so that follow-on code paths can reliably @@ -1902,6 +1947,23 @@ static int cxl_region_attach(struct cxl_region *cxlr, struct cxl_dport *dport; int rc = -ENXIO; + /* + * Protect against improper gran mixes on 3-way HB interleave + * Expect decoder gran*ways == region gran*ways + */ + if (cxlrd->cxlsd.cxld.interleave_ways == 3) { + if ((cxlrd->cxlsd.cxld.interleave_granularity * 3) != + (p->interleave_ways * p->interleave_granularity)) { + dev_dbg(&cxlr->dev, + "invalid config region:%d/%d decoder %d/%d\n", + p->interleave_ways, p->interleave_granularity, + cxlrd->cxlsd.cxld.interleave_ways, + cxlrd->cxlsd.cxld.interleave_granularity); + + return rc; + } + } + rc = check_interleave_cap(&cxled->cxld, p->interleave_ways, p->interleave_granularity); if (rc) { @@ -2055,9 +2117,10 @@ static int cxl_region_attach(struct cxl_region *cxlr, */ for (int i = 0; i < p->nr_targets; i++) { struct cxl_endpoint_decoder *cxled = p->targets[i]; + int multiple = gran_multiple(cxlr); int test_pos; - test_pos = cxl_calc_interleave_pos(cxled); + test_pos = cxl_calc_interleave_pos(cxled, multiple); dev_dbg(&cxled->cxld.dev, "Test cxl_calc_interleave_pos(): %s test_pos:%d cxled->pos:%d\n", (test_pos == cxled->pos) ? "success" : "fail",