Skip to content

Commit 67dcdd4

Browse files
committed
tools/testing/cxl: Introduce a mocked-up CXL port hierarchy
Create an environment for CXL plumbing unit tests. Especially when it comes to an algorithm for HDM Decoder (Host-managed Device Memory Decoder) programming, the availability of an in-kernel-tree emulation environment for CXL configuration complexity and corner cases speeds development and deters regressions. The approach taken mirrors what was done for tools/testing/nvdimm/. I.e. an external module, cxl_test.ko built out of the tools/testing/cxl/ directory, provides mock implementations of kernel APIs and kernel objects to simulate a real world device hierarchy. One feedback for the tools/testing/nvdimm/ proposal was "why not do this in QEMU?". In fact, the CXL development community has developed a QEMU model for CXL [1]. However, there are a few blocking issues that keep QEMU from being a tight fit for topology + provisioning unit tests: 1/ The QEMU community has yet to show interest in merging any of this support that has had patches on the list since November 2020. So, testing CXL to date involves building custom QEMU with out-of-tree patches. 2/ CXL mechanisms like cross-host-bridge interleave do not have a clear path to be emulated by QEMU without major infrastructure work. This is easier to achieve with the alloc_mock_res() approach taken in this patch to shortcut-define emulated system physical address ranges with interleave behavior. The QEMU enabling has been critical to get the driver off the ground, and may still move forward, but it does not address the ongoing needs of a regression testing environment and test driven development. This patch adds an ACPI CXL Platform definition with emulated CXL multi-ported host-bridges. A follow on patch adds emulated memory expander devices. Acked-by: Ben Widawsky <[email protected]> Reported-by: Vishal Verma <[email protected]> Link: https://lore.kernel.org/r/[email protected] [1] Link: https://lore.kernel.org/r/163164680798.2831381.838684634806668012.stgit@dwillia2-desk3.amr.corp.intel.com Reviewed-by: Jonathan Cameron <[email protected]> Signed-off-by: Dan Williams <[email protected]>
1 parent 2e52b62 commit 67dcdd4

File tree

9 files changed

+908
-15
lines changed

9 files changed

+908
-15
lines changed

drivers/cxl/acpi.c

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -182,15 +182,7 @@ static resource_size_t get_chbcr(struct acpi_cedt_chbs *chbs)
182182
return IS_ERR(chbs) ? CXL_RESOURCE_NONE : chbs->base;
183183
}
184184

185-
struct cxl_walk_context {
186-
struct device *dev;
187-
struct pci_bus *root;
188-
struct cxl_port *port;
189-
int error;
190-
int count;
191-
};
192-
193-
static int match_add_root_ports(struct pci_dev *pdev, void *data)
185+
__mock int match_add_root_ports(struct pci_dev *pdev, void *data)
194186
{
195187
struct cxl_walk_context *ctx = data;
196188
struct pci_bus *root_bus = ctx->root;
@@ -239,7 +231,8 @@ static struct cxl_dport *find_dport_by_dev(struct cxl_port *port, struct device
239231
return NULL;
240232
}
241233

242-
static struct acpi_device *to_cxl_host_bridge(struct device *dev)
234+
__mock struct acpi_device *to_cxl_host_bridge(struct device *host,
235+
struct device *dev)
243236
{
244237
struct acpi_device *adev = to_acpi_device(dev);
245238

@@ -257,9 +250,9 @@ static struct acpi_device *to_cxl_host_bridge(struct device *dev)
257250
*/
258251
static int add_host_bridge_uport(struct device *match, void *arg)
259252
{
260-
struct acpi_device *bridge = to_cxl_host_bridge(match);
261253
struct cxl_port *root_port = arg;
262254
struct device *host = root_port->dev.parent;
255+
struct acpi_device *bridge = to_cxl_host_bridge(host, match);
263256
struct acpi_pci_root *pci_root;
264257
struct cxl_walk_context ctx;
265258
struct cxl_decoder *cxld;
@@ -323,7 +316,7 @@ static int add_host_bridge_dport(struct device *match, void *arg)
323316
struct acpi_cedt_chbs *chbs;
324317
struct cxl_port *root_port = arg;
325318
struct device *host = root_port->dev.parent;
326-
struct acpi_device *bridge = to_cxl_host_bridge(match);
319+
struct acpi_device *bridge = to_cxl_host_bridge(host, match);
327320

328321
if (!bridge)
329322
return 0;
@@ -375,6 +368,17 @@ static int add_root_nvdimm_bridge(struct device *match, void *data)
375368
return 1;
376369
}
377370

371+
static u32 cedt_instance(struct platform_device *pdev)
372+
{
373+
const bool *native_acpi0017 = acpi_device_get_match_data(&pdev->dev);
374+
375+
if (native_acpi0017 && *native_acpi0017)
376+
return 0;
377+
378+
/* for cxl_test request a non-canonical instance */
379+
return U32_MAX;
380+
}
381+
378382
static int cxl_acpi_probe(struct platform_device *pdev)
379383
{
380384
int rc;
@@ -388,7 +392,7 @@ static int cxl_acpi_probe(struct platform_device *pdev)
388392
return PTR_ERR(root_port);
389393
dev_dbg(host, "add: %s\n", dev_name(&root_port->dev));
390394

391-
status = acpi_get_table(ACPI_SIG_CEDT, 0, &acpi_cedt);
395+
status = acpi_get_table(ACPI_SIG_CEDT, cedt_instance(pdev), &acpi_cedt);
392396
if (ACPI_FAILURE(status))
393397
return -ENXIO;
394398

@@ -419,9 +423,11 @@ static int cxl_acpi_probe(struct platform_device *pdev)
419423
return 0;
420424
}
421425

426+
static bool native_acpi0017 = true;
427+
422428
static const struct acpi_device_id cxl_acpi_ids[] = {
423-
{ "ACPI0017", 0 },
424-
{ "", 0 },
429+
{ "ACPI0017", (unsigned long) &native_acpi0017 },
430+
{ },
425431
};
426432
MODULE_DEVICE_TABLE(acpi, cxl_acpi_ids);
427433

drivers/cxl/cxl.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,14 @@ struct cxl_nvdimm {
226226
struct nvdimm *nvdimm;
227227
};
228228

229+
struct cxl_walk_context {
230+
struct device *dev;
231+
struct pci_bus *root;
232+
struct cxl_port *port;
233+
int error;
234+
int count;
235+
};
236+
229237
/**
230238
* struct cxl_port - logical collection of upstream port devices and
231239
* downstream port devices to construct a CXL memory
@@ -325,4 +333,12 @@ struct cxl_nvdimm *to_cxl_nvdimm(struct device *dev);
325333
bool is_cxl_nvdimm(struct device *dev);
326334
int devm_cxl_add_nvdimm(struct device *host, struct cxl_memdev *cxlmd);
327335
struct cxl_nvdimm_bridge *cxl_find_nvdimm_bridge(void);
336+
337+
/*
338+
* Unit test builds overrides this to __weak, find the 'strong' version
339+
* of these symbols in tools/testing/cxl/.
340+
*/
341+
#ifndef __mock
342+
#define __mock static
343+
#endif
328344
#endif /* __CXL_H__ */

tools/testing/cxl/Kbuild

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# SPDX-License-Identifier: GPL-2.0
2+
ldflags-y += --wrap=is_acpi_device_node
3+
ldflags-y += --wrap=acpi_get_table
4+
ldflags-y += --wrap=acpi_put_table
5+
ldflags-y += --wrap=acpi_evaluate_integer
6+
ldflags-y += --wrap=acpi_pci_find_root
7+
ldflags-y += --wrap=pci_walk_bus
8+
ldflags-y += --wrap=nvdimm_bus_register
9+
10+
DRIVERS := ../../../drivers
11+
CXL_SRC := $(DRIVERS)/cxl
12+
CXL_CORE_SRC := $(DRIVERS)/cxl/core
13+
ccflags-y := -I$(srctree)/drivers/cxl/
14+
ccflags-y += -D__mock=__weak
15+
16+
obj-m += cxl_acpi.o
17+
18+
cxl_acpi-y := $(CXL_SRC)/acpi.o
19+
cxl_acpi-y += mock_acpi.o
20+
cxl_acpi-y += config_check.o
21+
22+
obj-m += cxl_pmem.o
23+
24+
cxl_pmem-y := $(CXL_SRC)/pmem.o
25+
cxl_pmem-y += config_check.o
26+
27+
obj-m += cxl_core.o
28+
29+
cxl_core-y := $(CXL_CORE_SRC)/bus.o
30+
cxl_core-y += $(CXL_CORE_SRC)/pmem.o
31+
cxl_core-y += $(CXL_CORE_SRC)/regs.o
32+
cxl_core-y += $(CXL_CORE_SRC)/memdev.o
33+
cxl_core-y += $(CXL_CORE_SRC)/mbox.o
34+
cxl_core-y += config_check.o
35+
36+
obj-m += test/

tools/testing/cxl/config_check.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
#include <linux/bug.h>
3+
4+
void check(void)
5+
{
6+
/*
7+
* These kconfig symbols must be set to "m" for cxl_test to load
8+
* and operate.
9+
*/
10+
BUILD_BUG_ON(!IS_MODULE(CONFIG_CXL_BUS));
11+
BUILD_BUG_ON(!IS_MODULE(CONFIG_CXL_ACPI));
12+
BUILD_BUG_ON(!IS_MODULE(CONFIG_CXL_PMEM));
13+
}

tools/testing/cxl/mock_acpi.c

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
/* Copyright(c) 2021 Intel Corporation. All rights reserved. */
3+
4+
#include <linux/platform_device.h>
5+
#include <linux/device.h>
6+
#include <linux/acpi.h>
7+
#include <linux/pci.h>
8+
#include <cxl.h>
9+
#include "test/mock.h"
10+
11+
struct acpi_device *to_cxl_host_bridge(struct device *host, struct device *dev)
12+
{
13+
int index;
14+
struct acpi_device *adev, *found = NULL;
15+
struct cxl_mock_ops *ops = get_cxl_mock_ops(&index);
16+
17+
if (ops && ops->is_mock_bridge(dev)) {
18+
found = ACPI_COMPANION(dev);
19+
goto out;
20+
}
21+
22+
if (dev->bus == &platform_bus_type)
23+
goto out;
24+
25+
adev = to_acpi_device(dev);
26+
if (!acpi_pci_find_root(adev->handle))
27+
goto out;
28+
29+
if (strcmp(acpi_device_hid(adev), "ACPI0016") == 0) {
30+
found = adev;
31+
dev_dbg(host, "found host bridge %s\n", dev_name(&adev->dev));
32+
}
33+
out:
34+
put_cxl_mock_ops(index);
35+
return found;
36+
}
37+
38+
static int match_add_root_port(struct pci_dev *pdev, void *data)
39+
{
40+
struct cxl_walk_context *ctx = data;
41+
struct pci_bus *root_bus = ctx->root;
42+
struct cxl_port *port = ctx->port;
43+
int type = pci_pcie_type(pdev);
44+
struct device *dev = ctx->dev;
45+
u32 lnkcap, port_num;
46+
int rc;
47+
48+
if (pdev->bus != root_bus)
49+
return 0;
50+
if (!pci_is_pcie(pdev))
51+
return 0;
52+
if (type != PCI_EXP_TYPE_ROOT_PORT)
53+
return 0;
54+
if (pci_read_config_dword(pdev, pci_pcie_cap(pdev) + PCI_EXP_LNKCAP,
55+
&lnkcap) != PCIBIOS_SUCCESSFUL)
56+
return 0;
57+
58+
/* TODO walk DVSEC to find component register base */
59+
port_num = FIELD_GET(PCI_EXP_LNKCAP_PN, lnkcap);
60+
rc = cxl_add_dport(port, &pdev->dev, port_num, CXL_RESOURCE_NONE);
61+
if (rc) {
62+
dev_err(dev, "failed to add dport: %s (%d)\n",
63+
dev_name(&pdev->dev), rc);
64+
ctx->error = rc;
65+
return rc;
66+
}
67+
ctx->count++;
68+
69+
dev_dbg(dev, "add dport%d: %s\n", port_num, dev_name(&pdev->dev));
70+
71+
return 0;
72+
}
73+
74+
static int mock_add_root_port(struct platform_device *pdev, void *data)
75+
{
76+
struct cxl_walk_context *ctx = data;
77+
struct cxl_port *port = ctx->port;
78+
struct device *dev = ctx->dev;
79+
int rc;
80+
81+
rc = cxl_add_dport(port, &pdev->dev, pdev->id, CXL_RESOURCE_NONE);
82+
if (rc) {
83+
dev_err(dev, "failed to add dport: %s (%d)\n",
84+
dev_name(&pdev->dev), rc);
85+
ctx->error = rc;
86+
return rc;
87+
}
88+
ctx->count++;
89+
90+
dev_dbg(dev, "add dport%d: %s\n", pdev->id, dev_name(&pdev->dev));
91+
92+
return 0;
93+
}
94+
95+
int match_add_root_ports(struct pci_dev *dev, void *data)
96+
{
97+
int index, rc;
98+
struct cxl_mock_ops *ops = get_cxl_mock_ops(&index);
99+
struct platform_device *pdev = (struct platform_device *) dev;
100+
101+
if (ops && ops->is_mock_port(pdev))
102+
rc = mock_add_root_port(pdev, data);
103+
else
104+
rc = match_add_root_port(dev, data);
105+
106+
put_cxl_mock_ops(index);
107+
108+
return rc;
109+
}

tools/testing/cxl/test/Kbuild

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# SPDX-License-Identifier: GPL-2.0
2+
obj-m += cxl_test.o
3+
obj-m += cxl_mock.o
4+
5+
cxl_test-y := cxl.o
6+
cxl_mock-y := mock.o

0 commit comments

Comments
 (0)