Skip to content

Commit a3e00c9

Browse files
AlisonSchofielddjbw
authored andcommitted
cxl/region: Calculate a target position in a region interleave
Introduce a calculation to find a target's position in a region interleave. Perform a self-test of the calculation on user-defined regions. The region driver uses the kernel sort() function to put region targets in relative order. Positions are assigned based on each target's index in that sorted list. That relative sort doesn't consider the offset of a port into its parent port which causes some auto-discovered regions to fail creation. In one failure case, a 2 + 2 config (2 host bridges each with 2 endpoints), the sort puts all the targets of one port ahead of another port when they were expected to be interleaved. In preparation for repairing the autodiscovery region assembly, introduce a new method for discovering a target position in the region interleave. cxl_calc_interleave_pos() adds a method to find the target position by ascending from an endpoint to a root decoder. The calculation starts with the endpoint's local position and position in the parent port. It traverses towards the root decoder and examines both position and ways in order to allow the position to be refined all the way to the root decoder. This calculation: position = position * parent_ways + parent_pos; applied iteratively yields the correct position. Include a self-test that exercises this new position calculation against every successfully configured user-defined region. Signed-off-by: Alison Schofield <alison.schofield@intel.com> Link: https://lore.kernel.org/r/0ac32c75cf81dd8b86bf07d70ff139d33c2300bc.1698263080.git.alison.schofield@intel.com Signed-off-by: Dan Williams <dan.j.williams@intel.com>
1 parent 1110581 commit a3e00c9

File tree

1 file changed

+127
-0
lines changed

1 file changed

+127
-0
lines changed

drivers/cxl/core/region.c

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1497,6 +1497,113 @@ static int match_switch_decoder_by_range(struct device *dev, void *data)
14971497
return (r1->start == r2->start && r1->end == r2->end);
14981498
}
14991499

1500+
static int find_pos_and_ways(struct cxl_port *port, struct range *range,
1501+
int *pos, int *ways)
1502+
{
1503+
struct cxl_switch_decoder *cxlsd;
1504+
struct cxl_port *parent;
1505+
struct device *dev;
1506+
int rc = -ENXIO;
1507+
1508+
parent = next_port(port);
1509+
if (!parent)
1510+
return rc;
1511+
1512+
dev = device_find_child(&parent->dev, range,
1513+
match_switch_decoder_by_range);
1514+
if (!dev) {
1515+
dev_err(port->uport_dev,
1516+
"failed to find decoder mapping %#llx-%#llx\n",
1517+
range->start, range->end);
1518+
return rc;
1519+
}
1520+
cxlsd = to_cxl_switch_decoder(dev);
1521+
*ways = cxlsd->cxld.interleave_ways;
1522+
1523+
for (int i = 0; i < *ways; i++) {
1524+
if (cxlsd->target[i] == port->parent_dport) {
1525+
*pos = i;
1526+
rc = 0;
1527+
break;
1528+
}
1529+
}
1530+
put_device(dev);
1531+
1532+
return rc;
1533+
}
1534+
1535+
/**
1536+
* cxl_calc_interleave_pos() - calculate an endpoint position in a region
1537+
* @cxled: endpoint decoder member of given region
1538+
*
1539+
* The endpoint position is calculated by traversing the topology from
1540+
* the endpoint to the root decoder and iteratively applying this
1541+
* calculation:
1542+
*
1543+
* position = position * parent_ways + parent_pos;
1544+
*
1545+
* ...where @position is inferred from switch and root decoder target lists.
1546+
*
1547+
* Return: position >= 0 on success
1548+
* -ENXIO on failure
1549+
*/
1550+
static int cxl_calc_interleave_pos(struct cxl_endpoint_decoder *cxled)
1551+
{
1552+
struct cxl_port *iter, *port = cxled_to_port(cxled);
1553+
struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
1554+
struct range *range = &cxled->cxld.hpa_range;
1555+
int parent_ways = 0, parent_pos = 0, pos = 0;
1556+
int rc;
1557+
1558+
/*
1559+
* Example: the expected interleave order of the 4-way region shown
1560+
* below is: mem0, mem2, mem1, mem3
1561+
*
1562+
* root_port
1563+
* / \
1564+
* host_bridge_0 host_bridge_1
1565+
* | | | |
1566+
* mem0 mem1 mem2 mem3
1567+
*
1568+
* In the example the calculator will iterate twice. The first iteration
1569+
* uses the mem position in the host-bridge and the ways of the host-
1570+
* bridge to generate the first, or local, position. The second
1571+
* iteration uses the host-bridge position in the root_port and the ways
1572+
* of the root_port to refine the position.
1573+
*
1574+
* A trace of the calculation per endpoint looks like this:
1575+
* mem0: pos = 0 * 2 + 0 mem2: pos = 0 * 2 + 0
1576+
* pos = 0 * 2 + 0 pos = 0 * 2 + 1
1577+
* pos: 0 pos: 1
1578+
*
1579+
* mem1: pos = 0 * 2 + 1 mem3: pos = 0 * 2 + 1
1580+
* pos = 1 * 2 + 0 pos = 1 * 2 + 1
1581+
* pos: 2 pos = 3
1582+
*
1583+
* Note that while this example is simple, the method applies to more
1584+
* complex topologies, including those with switches.
1585+
*/
1586+
1587+
/* Iterate from endpoint to root_port refining the position */
1588+
for (iter = port; iter; iter = next_port(iter)) {
1589+
if (is_cxl_root(iter))
1590+
break;
1591+
1592+
rc = find_pos_and_ways(iter, range, &parent_pos, &parent_ways);
1593+
if (rc)
1594+
return rc;
1595+
1596+
pos = pos * parent_ways + parent_pos;
1597+
}
1598+
1599+
dev_dbg(&cxlmd->dev,
1600+
"decoder:%s parent:%s port:%s range:%#llx-%#llx pos:%d\n",
1601+
dev_name(&cxled->cxld.dev), dev_name(cxlmd->dev.parent),
1602+
dev_name(&port->dev), range->start, range->end, pos);
1603+
1604+
return pos;
1605+
}
1606+
15001607
static void find_positions(const struct cxl_switch_decoder *cxlsd,
15011608
const struct cxl_port *iter_a,
15021609
const struct cxl_port *iter_b, int *a_pos,
@@ -1766,6 +1873,26 @@ static int cxl_region_attach(struct cxl_region *cxlr,
17661873
.end = p->res->end,
17671874
};
17681875

1876+
if (p->nr_targets != p->interleave_ways)
1877+
return 0;
1878+
1879+
/*
1880+
* Test the auto-discovery position calculator function
1881+
* against this successfully created user-defined region.
1882+
* A fail message here means that this interleave config
1883+
* will fail when presented as CXL_REGION_F_AUTO.
1884+
*/
1885+
for (int i = 0; i < p->nr_targets; i++) {
1886+
struct cxl_endpoint_decoder *cxled = p->targets[i];
1887+
int test_pos;
1888+
1889+
test_pos = cxl_calc_interleave_pos(cxled);
1890+
dev_dbg(&cxled->cxld.dev,
1891+
"Test cxl_calc_interleave_pos(): %s test_pos:%d cxled->pos:%d\n",
1892+
(test_pos == cxled->pos) ? "success" : "fail",
1893+
test_pos, cxled->pos);
1894+
}
1895+
17691896
return 0;
17701897
}
17711898

0 commit comments

Comments
 (0)