* [PATCH 0/4] cxl: Add KUnit tests and documentation
@ 2026-10-14 4:17 Alison Schofield
2026-10-14 4:17 ` [PATCH 1/4] Documentation/driver-api/cxl: Add a testing page Alison Schofield
` (3 more replies)
0 siblings, 4 replies; 5+ messages in thread
From: Alison Schofield @ 2026-10-14 4:17 UTC (permalink / raw)
To: Davidlohr Bueso, Jonathan Cameron, Dave Jiang, Alison Schofield,
Vishal Verma, Ira Weiny, Li Ming, Richard Cheng
Cc: linux-cxl
The CXL subsystem has no KUnit tests. Its automated test coverage comes
from cxl_test and the ndctl suite, which exercise the driver stack through
an emulated CXL topology. KUnit complements that coverage by testing self-
contained logic and error paths in isolation.
This series introduces CXL KUnit infrastructure and documentation describing
how it complements the existing cxl_test coverage.
The first KUnit suite, a collection of related test cases, exercises the HDM
decoder interleave encoding helpers, providing a starting point for testing
self-contained CXL logic. The second suite exercises mailbox command handling,
including error paths that are difficult to reach through cxl_test.
The mailbox tests also demonstrate the value of regression coverage. An
unknown device return code can cause an out-of-bounds read and allow a failed
command to be reported as successful. The series corrects that behavior, then
adds tests that reproduce the failure without the correction. With KASAN enabled,
the tests also expose the out-of-bounds read.
Together, the two suites establish a pattern for adding KUnit coverage
to CXL, both for existing functionality and for bugs as they are found.
Alison Schofield (3):
Documentation/driver-api/cxl: Add a testing page
cxl: Add KUnit tests for HDM decoder encoding helpers
cxl/mbox: Add KUnit tests for cxl_internal_send_cmd()
Gaobin Huang (1):
cxl/mbox: Treat unknown device return codes as errors
Documentation/driver-api/cxl/index.rst | 1 +
Documentation/driver-api/cxl/testing.rst | 120 +++++++++++
drivers/cxl/.kunitconfig | 4 +
drivers/cxl/Kconfig | 16 ++
drivers/cxl/Makefile | 1 +
drivers/cxl/core/Makefile | 1 +
drivers/cxl/core/tests/Makefile | 5 +
drivers/cxl/core/tests/mbox_kunit.c | 249 +++++++++++++++++++++++
drivers/cxl/cxlmem.h | 6 +
drivers/cxl/tests/Makefile | 5 +
drivers/cxl/tests/hdm_encoding_kunit.c | 156 ++++++++++++++
11 files changed, 564 insertions(+)
create mode 100644 Documentation/driver-api/cxl/testing.rst
create mode 100644 drivers/cxl/.kunitconfig
create mode 100644 drivers/cxl/core/tests/Makefile
create mode 100644 drivers/cxl/core/tests/mbox_kunit.c
create mode 100644 drivers/cxl/tests/Makefile
create mode 100644 drivers/cxl/tests/hdm_encoding_kunit.c
--
2.37.3
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 1/4] Documentation/driver-api/cxl: Add a testing page
2026-10-14 4:17 [PATCH 0/4] cxl: Add KUnit tests and documentation Alison Schofield
@ 2026-10-14 4:17 ` Alison Schofield
2026-10-14 4:17 ` [PATCH 2/4] cxl: Add KUnit tests for HDM decoder encoding helpers Alison Schofield
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: Alison Schofield @ 2026-10-14 4:17 UTC (permalink / raw)
To: Davidlohr Bueso, Jonathan Cameron, Dave Jiang, Alison Schofield,
Vishal Verma, Ira Weiny, Li Ming, Richard Cheng
Cc: linux-cxl
The CXL documentation does not describe the subsystem's testing
infrastructure or provide guidance on choosing between cxl_test
and KUnit.
Add a testing page that describes when to use each framework, how
to run the tests, and how to add a CXL KUnit test.
Suggested-by: Dave Jiang <dave.jiang@intel.com>
Signed-off-by: Alison Schofield <alison.schofield@intel.com>
---
Documentation/driver-api/cxl/index.rst | 1 +
Documentation/driver-api/cxl/testing.rst | 117 +++++++++++++++++++++++
2 files changed, 118 insertions(+)
create mode 100644 Documentation/driver-api/cxl/testing.rst
diff --git a/Documentation/driver-api/cxl/index.rst b/Documentation/driver-api/cxl/index.rst
index 3dfae1d310ca..786c189366ca 100644
--- a/Documentation/driver-api/cxl/index.rst
+++ b/Documentation/driver-api/cxl/index.rst
@@ -15,6 +15,7 @@ that have impacts on each other. The docs here break up configurations steps.
theory-of-operation
maturity-map
conventions
+ testing
.. toctree::
:maxdepth: 2
diff --git a/Documentation/driver-api/cxl/testing.rst b/Documentation/driver-api/cxl/testing.rst
new file mode 100644
index 000000000000..e3e2613172a2
--- /dev/null
+++ b/Documentation/driver-api/cxl/testing.rst
@@ -0,0 +1,117 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+===========
+Testing CXL
+===========
+
+CXL testing uses two complementary frameworks: cxl_test exercises the driver
+stack against an emulated CXL topology, while KUnit tests self-contained logic
+in isolation. Changes should preserve existing test coverage and extend the
+framework appropriate to the behavior being changed.
+
+cxl_test
+========
+
+The cxl_test modules, built from tools/testing/cxl/, emulate a CXL topology with
+host bridges, switches, memory devices, and mailboxes. This allows the CXL driver
+stack to be tested without hardware. The tests are maintained in the ndctl repo
+at https://github.com/pmem/ndctl, under test/cxl-<name>.sh.
+
+Use cxl_test for behavior that depends on a live topology: port and endpoint
+enumeration, region creation and assembly, decoder commit, and anything that
+crosses the sysfs ABI.
+
+Build the cxl_test modules from the top of the kernel tree::
+
+ make M=tools/testing/cxl
+
+After installing the modules for the running kernel, run the CXL suite from an
+ndctl build::
+
+ meson test -C build --suite cxl
+
+For more on building and running the ndctl unit tests, see:
+
+- the Unit Tests section of the ndctl README,
+ https://github.com/pmem/ndctl?tab=readme-ov-file#unit-tests
+- the ndctl test runner,
+ https://github.com/pmem/ndctl-test-runner?tab=readme-ov-file#ndctl-test-runner
+
+
+KUnit
+=====
+
+KUnit tests run self-contained logic in the kernel without any CXL topology.
+KUnit is suited to logic that can be exercised with controlled test inputs,
+including encode/decode helpers, address translation and interleave calculations,
+parsers of device- or firmware-supplied data, and mailbox command handling
+through a stub transport.
+
+To run all of the CXL KUnit tests, from the top of the kernel tree::
+
+ ./tools/testing/kunit/kunit.py run --kunitconfig=drivers/cxl
+
+This builds a test kernel in .kunit/ using drivers/cxl/.kunitconfig and runs it
+under User Mode Linux (UML). The running kernel is not modified, and no
+installation is required. The test kernel runs the tests, reports the results,
+and exits.
+
+Add --arch=x86_64 to run the tests under QEMU instead.
+
+Running during development
+--------------------------
+
+kunit.py builds in .kunit/, but kbuild does not allow an out-of-tree build when
+the source tree already contains an in-tree build. If you build the kernel
+in-tree, use a separate worktree for KUnit. The worktree contains the committed
+sources at the selected revision::
+
+ git worktree add ../linux-kunit HEAD
+ cd ../linux-kunit
+ ./tools/testing/kunit/kunit.py run --kunitconfig=drivers/cxl
+
+To run a single suite, name it::
+
+ ./tools/testing/kunit/kunit.py run --kunitconfig=drivers/cxl cxl_core_mbox
+
+Enable KASAN when testing memory safety error paths. It can detect an out-of-bounds
+access even when the resulting value does not cause a test assertion to fail::
+
+ ./tools/testing/kunit/kunit.py run --kunitconfig=drivers/cxl \
+ --arch=x86_64 --kconfig_add CONFIG_KASAN=y
+
+Adding a KUnit test
+-------------------
+
+A CXL KUnit test follows these rules:
+
+- Place the test in a tests/ directory beside the code under test, in a
+ file named <name>_kunit.c.
+
+- Derive the module and suite names from the path below drivers/cxl/, excluding
+ tests/. For example, drivers/cxl/core/tests/port_kunit.c produces module
+ cxl_core_port_kunit and suite cxl_core_port, while drivers/cxl/tests/port_kunit.c
+ produces module cxl_port_kunit and suite cxl_port.
+
+- Build new suites under CONFIG_CXL_KUNIT_TEST rather than introducing a separate
+ Kconfig option for each suite. Individual suites can be selected by name with
+ kunit.py or by loading their module.
+
+- If the code under test depends on another configuration option, such as
+ CONFIG_CXL_REGION, make the test build conditional on that option in tests/Makefile
+ and enable it in drivers/cxl/.kunitconfig.
+
+- Use exported interfaces where possible, importing the CXL symbol namespace as needed.
+
+When to add a test
+==================
+
+New or modified self-contained logic should be accompanied by KUnit coverage where
+practical. Extend an existing suite when possible, or introduce a new one for
+functionality that is not yet covered.
+
+Bug fixes should include regression coverage that reproduces the problem without
+the fix.
+
+Changes to topology-dependent behavior should be covered by cxl_test tests in ndctl.
+KUnit coverage complements, but does not replace, cxl_test.
--
2.37.3
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 2/4] cxl: Add KUnit tests for HDM decoder encoding helpers
2026-10-14 4:17 [PATCH 0/4] cxl: Add KUnit tests and documentation Alison Schofield
2026-10-14 4:17 ` [PATCH 1/4] Documentation/driver-api/cxl: Add a testing page Alison Schofield
@ 2026-10-14 4:17 ` Alison Schofield
2026-10-14 4:17 ` [PATCH 3/4] cxl/mbox: Treat unknown device return codes as errors Alison Schofield
2026-10-14 4:17 ` [PATCH 4/4] cxl/mbox: Add KUnit tests for cxl_internal_send_cmd() Alison Schofield
3 siblings, 0 replies; 5+ messages in thread
From: Alison Schofield @ 2026-10-14 4:17 UTC (permalink / raw)
To: Davidlohr Bueso, Jonathan Cameron, Dave Jiang, Alison Schofield,
Vishal Verma, Ira Weiny, Li Ming, Richard Cheng
Cc: linux-cxl
The CXL subsystem has no KUnit infrastructure for testing self-contained
logic.
Introduce CONFIG_CXL_KUNIT_TEST and drivers/cxl/.kunitconfig, along with
an initial suite for the HDM decoder interleave encoding helpers. The
suite checks valid and invalid encodings and round-trip conversions.
Use a single Kconfig option for all CXL KUnit suites, with each suite
built as a separate module. This allows new suites to be added without
requiring additional configuration options.
The HDM decoder helpers provide a starting point for establishing the
location, naming, and configuration conventions for future CXL KUnit tests.
Suggested-by: Dave Jiang <dave.jiang@intel.com>
Signed-off-by: Alison Schofield <alison.schofield@intel.com>
---
drivers/cxl/.kunitconfig | 4 +
drivers/cxl/Kconfig | 16 +++
drivers/cxl/Makefile | 1 +
drivers/cxl/tests/Makefile | 5 +
drivers/cxl/tests/hdm_encoding_kunit.c | 156 +++++++++++++++++++++++++
5 files changed, 182 insertions(+)
create mode 100644 drivers/cxl/.kunitconfig
create mode 100644 drivers/cxl/tests/Makefile
create mode 100644 drivers/cxl/tests/hdm_encoding_kunit.c
diff --git a/drivers/cxl/.kunitconfig b/drivers/cxl/.kunitconfig
new file mode 100644
index 000000000000..4fee684f5ff6
--- /dev/null
+++ b/drivers/cxl/.kunitconfig
@@ -0,0 +1,4 @@
+CONFIG_KUNIT=y
+CONFIG_PCI=y
+CONFIG_CXL_BUS=y
+CONFIG_CXL_KUNIT_TEST=y
diff --git a/drivers/cxl/Kconfig b/drivers/cxl/Kconfig
index 80aeb0d556bd..fdd57a4118db 100644
--- a/drivers/cxl/Kconfig
+++ b/drivers/cxl/Kconfig
@@ -243,4 +243,20 @@ config CXL_ATL
depends on CXL_REGION
depends on ACPI_PRMT && AMD_NB
+config CXL_KUNIT_TEST
+ tristate "KUnit tests for CXL" if !KUNIT_ALL_TESTS
+ depends on KUNIT
+ default KUNIT_ALL_TESTS
+ help
+ This builds the KUnit tests for the CXL subsystem. When built
+ as a module, each test suite is its own cxl_<name>_kunit
+ module. This one option covers every CXL KUnit test, so new
+ tests need no new configuration.
+
+ For more information on KUnit and unit tests in general,
+ please refer to the KUnit documentation in
+ Documentation/dev-tools/kunit/.
+
+ If unsure, say N.
+
endif
diff --git a/drivers/cxl/Makefile b/drivers/cxl/Makefile
index 2caa90fa4bf2..d57fb0e9e7da 100644
--- a/drivers/cxl/Makefile
+++ b/drivers/cxl/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_CXL_ACPI) += cxl_acpi.o
obj-$(CONFIG_CXL_PMEM) += cxl_pmem.o
obj-$(CONFIG_CXL_MEM) += cxl_mem.o
obj-$(CONFIG_CXL_PCI) += cxl_pci.o
+obj-$(CONFIG_CXL_KUNIT_TEST) += tests/
cxl_port-y := port.o
cxl_acpi-y := acpi.o
diff --git a/drivers/cxl/tests/Makefile b/drivers/cxl/tests/Makefile
new file mode 100644
index 000000000000..aa5e4f8e3638
--- /dev/null
+++ b/drivers/cxl/tests/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0
+ccflags-y += -I$(srctree)/drivers/cxl
+
+obj-$(CONFIG_CXL_KUNIT_TEST) += cxl_hdm_encoding_kunit.o
+cxl_hdm_encoding_kunit-y := hdm_encoding_kunit.o
diff --git a/drivers/cxl/tests/hdm_encoding_kunit.c b/drivers/cxl/tests/hdm_encoding_kunit.c
new file mode 100644
index 000000000000..54a7cb078a06
--- /dev/null
+++ b/drivers/cxl/tests/hdm_encoding_kunit.c
@@ -0,0 +1,156 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * KUnit tests for the HDM decoder interleave encode/decode helpers
+ * in drivers/cxl/cxl.h
+ */
+#include <kunit/test.h>
+#include "cxl.h"
+
+struct eiw_case {
+ u8 eiw;
+ int ways;
+};
+
+/* Encodings of the IW field in the HDM Decoder n Control Register */
+static const struct eiw_case valid_eiw[] = {
+ { 0, 1 }, { 1, 2 }, { 2, 4 }, { 3, 8 }, { 4, 16 },
+ { 8, 3 }, { 9, 6 }, { 10, 12 },
+};
+
+static const u8 invalid_eiw[] = { 5, 6, 7, 11, 12, 13, 14, 15, 0xff };
+
+static const unsigned int invalid_ways[] = {
+ 0, 5, 7, 9, 10, 11, 13, 14, 15, 17, 24, 32,
+};
+
+static void eiw_to_ways_valid(struct kunit *test)
+{
+ for (int i = 0; i < ARRAY_SIZE(valid_eiw); i++) {
+ int ways = 0;
+
+ KUNIT_EXPECT_EQ_MSG(test, eiw_to_ways(valid_eiw[i].eiw, &ways),
+ 0, "eiw=%u", valid_eiw[i].eiw);
+ KUNIT_EXPECT_EQ_MSG(test, ways, valid_eiw[i].ways, "eiw=%u",
+ valid_eiw[i].eiw);
+ }
+}
+
+static void eiw_to_ways_invalid(struct kunit *test)
+{
+ for (int i = 0; i < ARRAY_SIZE(invalid_eiw); i++) {
+ int ways;
+
+ KUNIT_EXPECT_EQ_MSG(test, eiw_to_ways(invalid_eiw[i], &ways),
+ -EINVAL, "eiw=%u", invalid_eiw[i]);
+ }
+}
+
+static void ways_to_eiw_valid(struct kunit *test)
+{
+ for (int i = 0; i < ARRAY_SIZE(valid_eiw); i++) {
+ u8 eiw = 0xff;
+
+ KUNIT_EXPECT_EQ_MSG(test, ways_to_eiw(valid_eiw[i].ways, &eiw),
+ 0, "ways=%d", valid_eiw[i].ways);
+ KUNIT_EXPECT_EQ_MSG(test, eiw, valid_eiw[i].eiw, "ways=%d",
+ valid_eiw[i].ways);
+ }
+}
+
+static void ways_to_eiw_invalid(struct kunit *test)
+{
+ for (int i = 0; i < ARRAY_SIZE(invalid_ways); i++) {
+ u8 eiw;
+
+ KUNIT_EXPECT_EQ_MSG(test, ways_to_eiw(invalid_ways[i], &eiw),
+ -EINVAL, "ways=%u", invalid_ways[i]);
+ }
+}
+
+static void eig_to_granularity_valid(struct kunit *test)
+{
+ for (u16 eig = 0; eig <= CXL_DECODER_MAX_ENCODED_IG; eig++) {
+ int gran = 0;
+ u16 back = 0xffff;
+
+ KUNIT_EXPECT_EQ_MSG(test, eig_to_granularity(eig, &gran), 0,
+ "eig=%u", eig);
+ KUNIT_EXPECT_EQ_MSG(test, gran, 256 << eig, "eig=%u", eig);
+
+ /* Round trip back to the encoding */
+ KUNIT_EXPECT_EQ_MSG(test, granularity_to_eig(gran, &back), 0,
+ "gran=%d", gran);
+ KUNIT_EXPECT_EQ_MSG(test, back, eig, "gran=%d", gran);
+ }
+}
+
+static void eig_to_granularity_invalid(struct kunit *test)
+{
+ static const u16 invalid_eig[] = { 7, 8, 15, 0xffff };
+
+ for (int i = 0; i < ARRAY_SIZE(invalid_eig); i++) {
+ int gran;
+
+ KUNIT_EXPECT_EQ_MSG(test,
+ eig_to_granularity(invalid_eig[i], &gran),
+ -EINVAL, "eig=%u", invalid_eig[i]);
+ }
+}
+
+static void granularity_to_eig_invalid(struct kunit *test)
+{
+ static const int invalid_gran[] = {
+ 0, 1, 128, 255, 257, 384, 768, SZ_32K, -256,
+ };
+
+ for (int i = 0; i < ARRAY_SIZE(invalid_gran); i++) {
+ u16 eig;
+
+ KUNIT_EXPECT_EQ_MSG(test,
+ granularity_to_eig(invalid_gran[i], &eig),
+ -EINVAL, "gran=%d", invalid_gran[i]);
+ }
+}
+
+/* Decoder Count field of the CXL HDM Decoder Capability Register */
+static void hdm_decoder_count(struct kunit *test)
+{
+ static const int expect[16] = {
+ 1, 2, 4, 6, 8, 10, 12, 14, 16, 20, 24, 28, 32,
+ -ENXIO, -ENXIO, -ENXIO,
+ };
+
+ for (u32 val = 0; val < ARRAY_SIZE(expect); val++) {
+ u32 cap = FIELD_PREP(CXL_HDM_DECODER_COUNT_MASK, val);
+
+ KUNIT_EXPECT_EQ_MSG(test, cxl_hdm_decoder_count(cap),
+ expect[val], "count field=%u", val);
+ }
+
+ /* Bits outside the count field are ignored */
+ KUNIT_EXPECT_EQ(test,
+ cxl_hdm_decoder_count((u32)~CXL_HDM_DECODER_COUNT_MASK),
+ 1);
+}
+
+static struct kunit_case cxl_hdm_encoding_cases[] = {
+ KUNIT_CASE(eiw_to_ways_valid),
+ KUNIT_CASE(eiw_to_ways_invalid),
+ KUNIT_CASE(ways_to_eiw_valid),
+ KUNIT_CASE(ways_to_eiw_invalid),
+ KUNIT_CASE(eig_to_granularity_valid),
+ KUNIT_CASE(eig_to_granularity_invalid),
+ KUNIT_CASE(granularity_to_eig_invalid),
+ KUNIT_CASE(hdm_decoder_count),
+ {}
+};
+
+static struct kunit_suite cxl_hdm_encoding_suite = {
+ .name = "cxl_hdm_encoding",
+ .test_cases = cxl_hdm_encoding_cases,
+};
+
+kunit_test_suite(cxl_hdm_encoding_suite);
+
+MODULE_DESCRIPTION("KUnit tests for CXL HDM decoder encoding helpers");
+MODULE_LICENSE("GPL");
--
2.37.3
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 3/4] cxl/mbox: Treat unknown device return codes as errors
2026-10-14 4:17 [PATCH 0/4] cxl: Add KUnit tests and documentation Alison Schofield
2026-10-14 4:17 ` [PATCH 1/4] Documentation/driver-api/cxl: Add a testing page Alison Schofield
2026-10-14 4:17 ` [PATCH 2/4] cxl: Add KUnit tests for HDM decoder encoding helpers Alison Schofield
@ 2026-10-14 4:17 ` Alison Schofield
2026-10-14 4:17 ` [PATCH 4/4] cxl/mbox: Add KUnit tests for cxl_internal_send_cmd() Alison Schofield
3 siblings, 0 replies; 5+ messages in thread
From: Alison Schofield @ 2026-10-14 4:17 UTC (permalink / raw)
To: Davidlohr Bueso, Jonathan Cameron, Dave Jiang, Alison Schofield,
Vishal Verma, Ira Weiny, Li Ming, Richard Cheng
Cc: linux-cxl, Gaobin Huang, stable
From: Gaobin Huang <huanggaobin23@semi.ac.cn>
cxl_mbox_cmd_rc2errno() and cxl_mbox_cmd_rc2str() use the device's 16-bit
return code to index cxl_mbox_cmd_rctable[]. A return code outside the
table's range causes an out-of-bounds read, potentially allowing a failed
command to be reported as successful.
Check the return code against the table size. Report unknown codes as
-ENXIO, the errno used for most device-reported errors, and describe them
as "unknown return code".
Found with a QEMU Type-3 device returning codes 001Fh and 0020h, which are defined in CXL 4.0 but not yet recognized by Linux [1].
[1] https://lore.kernel.org/linux-cxl/20260929060747.3550005-1-huanggaobin23@semi.ac.cn/
Fixes: 92fcc1abab09 ("cxl/mbox: Improve handling of mbox_cmd hw return codes")
Cc: stable@vger.kernel.org
Signed-off-by: Gaobin Huang <huanggaobin23@semi.ac.cn>
Co-developed-by: Alison Schofield <alison.schofield@intel.com>
Signed-off-by: Alison Schofield <alison.schofield@intel.com>
---
This patch is independently applicable and backportable. It is
intentionally introduced here as a lead in to the kunit patch
that backstops this fix.
drivers/cxl/cxlmem.h | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
index c401e3a1af06..357d0d873e87 100644
--- a/drivers/cxl/cxlmem.h
+++ b/drivers/cxl/cxlmem.h
@@ -6,6 +6,7 @@
#include <linux/pci.h>
#include <linux/cdev.h>
#include <linux/uuid.h>
+#include <linux/array_size.h>
#include <linux/node.h>
#include <cxl/event.h>
#include <cxl/mailbox.h>
@@ -205,13 +206,18 @@ static const
struct cxl_mbox_cmd_rc cxl_mbox_cmd_rctable[] ={ CMD_CMD_RC_TABLE };
#undef C
+/* The return code comes from the device and may be one this table lacks */
static inline const char *cxl_mbox_cmd_rc2str(struct cxl_mbox_cmd *mbox_cmd)
{
+ if (mbox_cmd->return_code >= ARRAY_SIZE(cxl_mbox_cmd_rctable))
+ return "unknown return code";
return cxl_mbox_cmd_rctable[mbox_cmd->return_code].desc;
}
static inline int cxl_mbox_cmd_rc2errno(struct cxl_mbox_cmd *mbox_cmd)
{
+ if (mbox_cmd->return_code >= ARRAY_SIZE(cxl_mbox_cmd_rctable))
+ return -ENXIO;
return cxl_mbox_cmd_rctable[mbox_cmd->return_code].err;
}
--
2.37.3
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 4/4] cxl/mbox: Add KUnit tests for cxl_internal_send_cmd()
2026-10-14 4:17 [PATCH 0/4] cxl: Add KUnit tests and documentation Alison Schofield
` (2 preceding siblings ...)
2026-10-14 4:17 ` [PATCH 3/4] cxl/mbox: Treat unknown device return codes as errors Alison Schofield
@ 2026-10-14 4:17 ` Alison Schofield
3 siblings, 0 replies; 5+ messages in thread
From: Alison Schofield @ 2026-10-14 4:17 UTC (permalink / raw)
To: Davidlohr Bueso, Jonathan Cameron, Dave Jiang, Alison Schofield,
Vishal Verma, Ira Weiny, Li Ming, Richard Cheng
Cc: linux-cxl
cxl_internal_send_cmd() validates mailbox payload sizes, translates device
return codes to errnos, and checks returned output sizes. Existing cxl_test
coverage does not exercise many of its error paths.
Add a KUnit suite that replaces mbox_send() with a stub transport, allowing
tests to control transport errors, device return codes, and output sizes.
The suite covers command forwarding, payload size limits, transport errors,
defined and unknown device return codes, background commands, and fixed
versus variable output sizes.
The unknown return code cases provide regression coverage for "cxl/mbox:
Treat unknown device return codes as errors". Without that change, the
tests fail and KASAN reports a global out-of-bounds read.
The suite uses the existing CONFIG_CXL_KUNIT_TEST option, allowing it to
run without additional configuration.
Signed-off-by: Alison Schofield <alison.schofield@intel.com>
---
Documentation/driver-api/cxl/testing.rst | 3 +
drivers/cxl/core/Makefile | 1 +
drivers/cxl/core/tests/Makefile | 5 +
drivers/cxl/core/tests/mbox_kunit.c | 249 +++++++++++++++++++++++
4 files changed, 258 insertions(+)
create mode 100644 drivers/cxl/core/tests/Makefile
create mode 100644 drivers/cxl/core/tests/mbox_kunit.c
diff --git a/Documentation/driver-api/cxl/testing.rst b/Documentation/driver-api/cxl/testing.rst
index e3e2613172a2..8d1eb5722334 100644
--- a/Documentation/driver-api/cxl/testing.rst
+++ b/Documentation/driver-api/cxl/testing.rst
@@ -103,6 +103,9 @@ A CXL KUnit test follows these rules:
- Use exported interfaces where possible, importing the CXL symbol namespace as needed.
+For an example of testing mailbox command handling with a stub transport, see
+drivers/cxl/core/tests/mbox_kunit.c.
+
When to add a test
==================
diff --git a/drivers/cxl/core/Makefile b/drivers/cxl/core/Makefile
index ce7213818d3c..b2627933b7fb 100644
--- a/drivers/cxl/core/Makefile
+++ b/drivers/cxl/core/Makefile
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_CXL_BUS) += cxl_core.o
obj-$(CONFIG_CXL_SUSPEND) += suspend.o
+obj-$(CONFIG_CXL_KUNIT_TEST) += tests/
ccflags-y += -I$(srctree)/drivers/cxl
CFLAGS_trace.o = -DTRACE_INCLUDE_PATH=. -I$(src)
diff --git a/drivers/cxl/core/tests/Makefile b/drivers/cxl/core/tests/Makefile
new file mode 100644
index 000000000000..08d97303d0d1
--- /dev/null
+++ b/drivers/cxl/core/tests/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0
+ccflags-y += -I$(srctree)/drivers/cxl
+
+obj-$(CONFIG_CXL_KUNIT_TEST) += cxl_core_mbox_kunit.o
+cxl_core_mbox_kunit-y := mbox_kunit.o
diff --git a/drivers/cxl/core/tests/mbox_kunit.c b/drivers/cxl/core/tests/mbox_kunit.c
new file mode 100644
index 000000000000..68f9fe87d743
--- /dev/null
+++ b/drivers/cxl/core/tests/mbox_kunit.c
@@ -0,0 +1,249 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * KUnit tests for cxl_internal_send_cmd() using a stub mailbox
+ * transport in place of hardware.
+ */
+#include <kunit/test.h>
+#include <cxl/mailbox.h>
+#include "cxlmem.h"
+
+#define TEST_PAYLOAD_SIZE SZ_4K
+
+/*
+ * Stub transport state: what the "device" returns, and a record of
+ * what the core passed down.
+ */
+struct mock_mbox {
+ struct cxl_mailbox cxl_mbox;
+ int send_rc;
+ u16 return_code;
+ size_t size_out;
+ int calls;
+ struct cxl_mbox_cmd seen;
+};
+
+static int mock_mbox_send(struct cxl_mailbox *cxl_mbox,
+ struct cxl_mbox_cmd *cmd)
+{
+ struct mock_mbox *mock;
+
+ mock = container_of(cxl_mbox, struct mock_mbox, cxl_mbox);
+ mock->calls++;
+ mock->seen = *cmd;
+ if (mock->send_rc)
+ return mock->send_rc;
+
+ cmd->return_code = mock->return_code;
+ cmd->size_out = mock->size_out;
+ return 0;
+}
+
+static int cxl_mbox_test_init(struct kunit *test)
+{
+ struct mock_mbox *mock;
+
+ mock = kunit_kzalloc(test, sizeof(*mock), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, mock);
+
+ mock->cxl_mbox.payload_size = TEST_PAYLOAD_SIZE;
+ mock->cxl_mbox.mbox_send = mock_mbox_send;
+ test->priv = mock;
+
+ return 0;
+}
+
+static void send_success(struct kunit *test)
+{
+ struct mock_mbox *mock = test->priv;
+ u8 in[16], out[64];
+ struct cxl_mbox_cmd cmd = {
+ .opcode = CXL_MBOX_OP_IDENTIFY,
+ .payload_in = in,
+ .size_in = sizeof(in),
+ .payload_out = out,
+ .size_out = sizeof(out),
+ };
+
+ mock->size_out = sizeof(out);
+ KUNIT_EXPECT_EQ(test, cxl_internal_send_cmd(&mock->cxl_mbox, &cmd), 0);
+ KUNIT_EXPECT_EQ(test, mock->calls, 1);
+
+ /* The command reaches the transport as the caller built it */
+ KUNIT_EXPECT_EQ(test, mock->seen.opcode, CXL_MBOX_OP_IDENTIFY);
+ KUNIT_EXPECT_PTR_EQ(test, mock->seen.payload_in, (void *)in);
+ KUNIT_EXPECT_EQ(test, mock->seen.size_in, sizeof(in));
+ KUNIT_EXPECT_PTR_EQ(test, mock->seen.payload_out, (void *)out);
+ KUNIT_EXPECT_EQ(test, mock->seen.size_out, sizeof(out));
+}
+
+static void send_no_output(struct kunit *test)
+{
+ struct mock_mbox *mock = test->priv;
+ struct cxl_mbox_cmd cmd = {};
+
+ /* No output was requested, so the returned size is not checked */
+ mock->size_out = 0;
+ KUNIT_EXPECT_EQ(test, cxl_internal_send_cmd(&mock->cxl_mbox, &cmd), 0);
+}
+
+static void send_size_in_too_big(struct kunit *test)
+{
+ struct mock_mbox *mock = test->priv;
+ struct cxl_mbox_cmd cmd = {
+ .size_in = TEST_PAYLOAD_SIZE + 1,
+ };
+
+ KUNIT_EXPECT_EQ(test, cxl_internal_send_cmd(&mock->cxl_mbox, &cmd),
+ -E2BIG);
+ KUNIT_EXPECT_EQ(test, mock->calls, 0);
+}
+
+static void send_size_out_too_big(struct kunit *test)
+{
+ struct mock_mbox *mock = test->priv;
+ struct cxl_mbox_cmd cmd = {
+ .size_out = TEST_PAYLOAD_SIZE + 1,
+ };
+
+ KUNIT_EXPECT_EQ(test, cxl_internal_send_cmd(&mock->cxl_mbox, &cmd),
+ -E2BIG);
+ KUNIT_EXPECT_EQ(test, mock->calls, 0);
+}
+
+static void send_full_payload(struct kunit *test)
+{
+ struct mock_mbox *mock = test->priv;
+ struct cxl_mbox_cmd cmd = {
+ .size_in = TEST_PAYLOAD_SIZE,
+ .size_out = TEST_PAYLOAD_SIZE,
+ };
+
+ mock->size_out = TEST_PAYLOAD_SIZE;
+ KUNIT_EXPECT_EQ(test, cxl_internal_send_cmd(&mock->cxl_mbox, &cmd), 0);
+}
+
+static void send_transport_error(struct kunit *test)
+{
+ struct mock_mbox *mock = test->priv;
+ struct cxl_mbox_cmd cmd = {};
+
+ mock->send_rc = -ETIMEDOUT;
+ KUNIT_EXPECT_EQ(test, cxl_internal_send_cmd(&mock->cxl_mbox, &cmd),
+ -ETIMEDOUT);
+}
+
+static void send_device_error(struct kunit *test)
+{
+ struct mock_mbox *mock = test->priv;
+
+ /* Every defined error code fails the command */
+ for (u16 code = CXL_MBOX_CMD_RC_INPUT;
+ code < ARRAY_SIZE(cxl_mbox_cmd_rctable); code++) {
+ struct cxl_mbox_cmd cmd = {};
+ int expect, rc;
+
+ switch (code) {
+ case CXL_MBOX_CMD_RC_PADDR:
+ expect = -EFAULT;
+ break;
+ case CXL_MBOX_CMD_RC_POISONLMT:
+ expect = -EBUSY;
+ break;
+ default:
+ expect = -ENXIO;
+ break;
+ }
+
+ mock->return_code = code;
+ rc = cxl_internal_send_cmd(&mock->cxl_mbox, &cmd);
+ KUNIT_EXPECT_EQ_MSG(test, rc, expect, "return_code=%#x", code);
+ }
+}
+
+static void send_unknown_return_code(struct kunit *test)
+{
+ static const u16 codes[] = { ARRAY_SIZE(cxl_mbox_cmd_rctable), 0x100,
+ 0xffff };
+ struct mock_mbox *mock = test->priv;
+
+ /* Codes the driver does not know are still device errors */
+ for (int i = 0; i < ARRAY_SIZE(codes); i++) {
+ struct cxl_mbox_cmd cmd = {};
+ int rc;
+
+ mock->return_code = codes[i];
+ rc = cxl_internal_send_cmd(&mock->cxl_mbox, &cmd);
+ KUNIT_EXPECT_EQ_MSG(test, rc, -ENXIO, "return_code=%#x",
+ codes[i]);
+ KUNIT_EXPECT_STREQ_MSG(test, cxl_mbox_cmd_rc2str(&cmd),
+ "unknown return code", "return_code=%#x",
+ codes[i]);
+ }
+}
+
+static void send_background_started(struct kunit *test)
+{
+ struct mock_mbox *mock = test->priv;
+ struct cxl_mbox_cmd cmd = {};
+
+ /* A background command that started is not an error */
+ mock->return_code = CXL_MBOX_CMD_RC_BACKGROUND;
+ KUNIT_EXPECT_EQ(test, cxl_internal_send_cmd(&mock->cxl_mbox, &cmd), 0);
+}
+
+static void send_short_fixed_output(struct kunit *test)
+{
+ struct mock_mbox *mock = test->priv;
+ struct cxl_mbox_cmd cmd = {
+ .size_out = 64,
+ };
+
+ /* Without min_out, the full requested size is required */
+ mock->size_out = 63;
+ KUNIT_EXPECT_EQ(test, cxl_internal_send_cmd(&mock->cxl_mbox, &cmd),
+ -EIO);
+}
+
+static void send_variable_output(struct kunit *test)
+{
+ struct mock_mbox *mock = test->priv;
+ struct cxl_mbox_cmd cmd;
+
+ /* Output at or above min_out is accepted */
+ cmd = (struct cxl_mbox_cmd){ .size_out = 64, .min_out = 8 };
+ mock->size_out = 8;
+ KUNIT_EXPECT_EQ(test, cxl_internal_send_cmd(&mock->cxl_mbox, &cmd), 0);
+
+ /* Output below min_out is rejected */
+ cmd = (struct cxl_mbox_cmd){ .size_out = 64, .min_out = 8 };
+ mock->size_out = 7;
+ KUNIT_EXPECT_EQ(test, cxl_internal_send_cmd(&mock->cxl_mbox, &cmd),
+ -EIO);
+}
+
+static struct kunit_case cxl_core_mbox_cases[] = {
+ KUNIT_CASE(send_success),
+ KUNIT_CASE(send_no_output),
+ KUNIT_CASE(send_size_in_too_big),
+ KUNIT_CASE(send_size_out_too_big),
+ KUNIT_CASE(send_full_payload),
+ KUNIT_CASE(send_transport_error),
+ KUNIT_CASE(send_device_error),
+ KUNIT_CASE(send_unknown_return_code),
+ KUNIT_CASE(send_background_started),
+ KUNIT_CASE(send_short_fixed_output),
+ KUNIT_CASE(send_variable_output),
+ {}
+};
+
+static struct kunit_suite cxl_core_mbox_suite = {
+ .name = "cxl_core_mbox",
+ .init = cxl_mbox_test_init,
+ .test_cases = cxl_core_mbox_cases,
+};
+
+kunit_test_suite(cxl_core_mbox_suite);
+
+MODULE_DESCRIPTION("KUnit tests for the CXL mailbox command core");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("CXL");
--
2.37.3
^ permalink raw reply related [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-10-14 4:18 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-14 4:17 [PATCH 0/4] cxl: Add KUnit tests and documentation Alison Schofield
2026-10-14 4:17 ` [PATCH 1/4] Documentation/driver-api/cxl: Add a testing page Alison Schofield
2026-10-14 4:17 ` [PATCH 2/4] cxl: Add KUnit tests for HDM decoder encoding helpers Alison Schofield
2026-10-14 4:17 ` [PATCH 3/4] cxl/mbox: Treat unknown device return codes as errors Alison Schofield
2026-10-14 4:17 ` [PATCH 4/4] cxl/mbox: Add KUnit tests for cxl_internal_send_cmd() Alison Schofield
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.