From 8a13db9f899d149c3aab24abcb668121cfda5a4f Mon Sep 17 00:00:00 2001 From: Richard Cheng Date: Wed, 24 Jun 2026 22:41:47 +0800 Subject: [PATCH 01/14] cxl/mbox: Clamp mailbox output allocation to the payload size CXL_MEM_SEND_COMMAND bounds the user's in.size to the mailbox payload size but leaves out.size unbounded, then cxl_mbox_cmd_ctor() calls kvzalloc(out.size). A large out.size drives a huge allocation, above INT_MAX it WARNs and taints, and with panic_on_warn=1 it panics. The transport __cxl_pci_mbox_send_cmd() already clamps the response copy to min(out.size, payload_size, device len), so the output buffer is never written beyond payload_size. Clamp the allocation to payload_size too, matching the RAW path. Fixes: 583fa5e71cae ("cxl/mem: Add basic IOCTL interface") Reviewed-by: Kai-Heng Feng Reviewed-by: Koba Ko Reviewed-by: Dave Jiang Reviewed-by: Davidlohr Bueso Signed-off-by: Richard Cheng Link: https://patch.msgid.link/20260624144147.53997-1-icheng@nvidia.com Signed-off-by: Dave Jiang --- drivers/cxl/core/mbox.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/cxl/core/mbox.c b/drivers/cxl/core/mbox.c index 7c6c5b7450a5..d9cb02c9f72c 100644 --- a/drivers/cxl/core/mbox.c +++ b/drivers/cxl/core/mbox.c @@ -380,11 +380,7 @@ static int cxl_mbox_cmd_ctor(struct cxl_mbox_cmd *mbox_cmd, } } - /* Prepare to handle a full payload for variable sized output */ - if (out_size == CXL_VARIABLE_PAYLOAD) - mbox_cmd->size_out = cxl_mbox->payload_size; - else - mbox_cmd->size_out = out_size; + mbox_cmd->size_out = min_t(size_t, out_size, cxl_mbox->payload_size); if (mbox_cmd->size_out) { mbox_cmd->payload_out = kvzalloc(mbox_cmd->size_out, GFP_KERNEL); From fe7883d0e99a5558050c43bbfe29e2b44977f97f Mon Sep 17 00:00:00 2001 From: Yousef Alhouseen Date: Wed, 24 Jun 2026 14:28:35 +0200 Subject: [PATCH 02/14] cxl: fix mailbox return code description typo Fix a typo in the CXL mailbox command return code description for the interrupted-command case. Signed-off-by: Yousef Alhouseen Reviewed-by: Richard Cheng Reviewed-by: Ira Weiny Link: https://patch.msgid.link/20260624122835.5656-1-alhouseenyousef@gmail.com Signed-off-by: Dave Jiang --- drivers/cxl/cxlmem.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h index ed419d0c59f2..503ae2864591 100644 --- a/drivers/cxl/cxlmem.h +++ b/drivers/cxl/cxlmem.h @@ -183,7 +183,7 @@ static inline struct cxl_ep *cxl_ep_load(struct cxl_port *port, C(MBUNSUPPORTED, -ENXIO, "unsupported on the mailbox it was issued on"),\ C(PAYLOADLEN, -ENXIO, "invalid payload length"), \ C(LOG, -ENXIO, "invalid or unsupported log page"), \ - C(INTERRUPTED, -ENXIO, "asynchronous event occured"), \ + C(INTERRUPTED, -ENXIO, "asynchronous event occurred"), \ C(FEATUREVERSION, -ENXIO, "unsupported feature version"), \ C(FEATURESELVALUE, -ENXIO, "unsupported feature selection value"), \ C(FEATURETRANSFERIP, -ENXIO, "feature transfer in progress"), \ From a667f1eb71a77859af6cedaadfab528256a99ca7 Mon Sep 17 00:00:00 2001 From: Zenghui Yu Date: Mon, 15 Jun 2026 00:14:58 +0800 Subject: [PATCH 03/14] cxl: docs/linux/dax-driver - fix typos Fix two obvious typos in the "kmem conversion" section. Signed-off-by: Zenghui Yu Reviewed-by: Alison Schofield Reviewed-by: Dongliang Mu Acked-by: Randy Dunlap Link: https://patch.msgid.link/20260614161458.88942-1-zenghui.yu@linux.dev Signed-off-by: Dave Jiang --- Documentation/driver-api/cxl/linux/dax-driver.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/driver-api/cxl/linux/dax-driver.rst b/Documentation/driver-api/cxl/linux/dax-driver.rst index 10d953a2167b..72c85a0f8606 100644 --- a/Documentation/driver-api/cxl/linux/dax-driver.rst +++ b/Documentation/driver-api/cxl/linux/dax-driver.rst @@ -35,9 +35,9 @@ will be exposed to the kernel page allocator in the user-selected memory zone. The :code:`memmap_on_memory` setting (both global and DAX device local) -dictates where the kernell will allocate the :code:`struct folio` descriptors +dictates where the kernel will allocate the :code:`struct folio` descriptors for this memory will come from. If :code:`memmap_on_memory` is set, memory hotplug will set aside a portion of the memory block capacity to allocate folios. If unset, the memory is allocated via a normal :code:`GFP_KERNEL` -allocation - and as a result will most likely land on the local NUM node of the +allocation - and as a result will most likely land on the local NUMA node of the CPU executing the hotplug operation. From d79b81893d0cc93737e811a465b9ef9a00156fd5 Mon Sep 17 00:00:00 2001 From: Wei Hou Date: Sun, 28 Jun 2026 23:58:57 +0800 Subject: [PATCH 04/14] cxl/pci: Remove incorrect mbox.valid check in cxl_pci_type3_init_mailbox() The driver's design intent is that missing or malformed component registers should not prevent mailbox initialization. cxl_pci_probe() already reflects this: the CXL_REGLOC_RBI_COMPONENT setup path only emits a dev_warn() and continues when component registers are absent, rather than returning an error. The check 'if (!cxlds->reg_map.device_map.mbox.valid)' violates this intent and is also technically incorrect for two reasons: 1. Wrong struct: the MEMDEV register block is enumerated into a local variable 'map', not into 'cxlds->reg_map'. The device_map.mbox.valid field inside cxlds->reg_map is never written by the MEMDEV probe and will always read as zero regardless of actual hardware capability. 2. Already validated: cxl_pci_setup_regs(CXL_REGLOC_RBI_MEMDEV) calls cxl_probe_regs() which explicitly checks mbox.valid and returns -ENXIO if the mailbox is absent. If that check passes, the mailbox is guaranteed to be present by the time cxl_pci_type3_init_mailbox() is called. The value that the check actually reads is component_map.ras.valid, which aliases device_map.mbox.valid in the union. This is populated by the COMPONENT probe, not the MEMDEV probe. On devices where the component register BAR does not implement a CXL Component Capability Array (e.g. certain DCD devices), cxl_probe_component_regs() returns early leaving ras.valid=false. Through the union, this makes mbox.valid read as false, causing cxl_pci_type3_init_mailbox() to return -ENODEV (-19) even though the mailbox hardware is fully functional. Remove the check. Mailbox presence has already been validated by cxl_pci_setup_regs(CXL_REGLOC_RBI_MEMDEV). The presence or absence of component registers is irrelevant to mailbox initialization. Fixes: 8d8081cecfb9 ("cxl: Move mailbox related bits to the same context") Reviewed-by: Richard Cheng Signed-off-by: Wei Hou Reviewed-by: Li Ming Link: https://patch.msgid.link/20260628155857.239866-1-wei.hou@scaleflux.com Signed-off-by: Dave Jiang --- drivers/cxl/pci.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c index 267c679b0b3c..0c9d7f8ec9e4 100644 --- a/drivers/cxl/pci.c +++ b/drivers/cxl/pci.c @@ -691,12 +691,6 @@ static int cxl_pci_type3_init_mailbox(struct cxl_dev_state *cxlds) { int rc; - /* - * Fail the init if there's no mailbox. For a type3 this is out of spec. - */ - if (!cxlds->reg_map.device_map.mbox.valid) - return -ENODEV; - rc = cxl_mailbox_init(&cxlds->cxl_mbox, cxlds->dev); if (rc) return rc; From 775d0f4558f4cec0ee0c8966595d1add1791f36e Mon Sep 17 00:00:00 2001 From: Dave Jiang Date: Tue, 16 Jun 2026 15:49:11 -0700 Subject: [PATCH 05/14] cxl/mce: Make the MCE notifier per-region Flavien Solt reported lifetime issues with the CXL MCE notifier, which can lead to NULL dereferences and use-after-free in the MCE handler. The notifier was registered per memory device and stored in 'struct cxl_memdev_state', even though it only needs the region state (the region's SPA range and its extended linear cache size). Instead of keeping the memory device and endpoint alive, the correct fix is to move the notifier into 'struct cxl_region' and register it from cxl_region_probe() as it should be a per-region notifier. Setup the registration to only happen for regions that have an extended linear cache as that is the only current usage. Remove cxl_port_get_spa_cache_alias() as it is now dead code. [ dj: Update dev_warn() when notifier fails due to kconfig. (Ben) ] Reported-by: Flavien Solt Suggested-by: Dan Williams Fixes: 516e5bd0b6bf ("cxl: Add mce notifier to emit aliased address for extended linear cache") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Alison Schofield Reviewed-by: Ben Cheatham Link: https://patch.msgid.link/20260616224912.2567474-1-dave.jiang@intel.com Signed-off-by: Dave Jiang --- drivers/cxl/core/mbox.c | 8 -------- drivers/cxl/core/mce.c | 27 ++++++++++++------------ drivers/cxl/core/region.c | 43 ++++++++++++++------------------------- drivers/cxl/cxl.h | 8 ++------ drivers/cxl/cxlmem.h | 2 -- 5 files changed, 30 insertions(+), 58 deletions(-) diff --git a/drivers/cxl/core/mbox.c b/drivers/cxl/core/mbox.c index d9cb02c9f72c..94b1f7167588 100644 --- a/drivers/cxl/core/mbox.c +++ b/drivers/cxl/core/mbox.c @@ -11,7 +11,6 @@ #include "core.h" #include "trace.h" -#include "mce.h" static bool cxl_raw_allow_all; @@ -1522,7 +1521,6 @@ struct cxl_memdev_state *cxl_memdev_state_create(struct device *dev, u64 serial, u16 dvsec) { struct cxl_memdev_state *mds; - int rc; mds = devm_cxl_dev_state_create(dev, CXL_DEVTYPE_CLASSMEM, serial, dvsec, struct cxl_memdev_state, cxlds, @@ -1534,12 +1532,6 @@ struct cxl_memdev_state *cxl_memdev_state_create(struct device *dev, u64 serial, mutex_init(&mds->event.log_lock); - rc = devm_cxl_register_mce_notifier(dev, &mds->mce_notifier); - if (rc == -EOPNOTSUPP) - dev_warn(dev, "CXL MCE unsupported\n"); - else if (rc) - return ERR_PTR(rc); - return mds; } EXPORT_SYMBOL_NS_GPL(cxl_memdev_state_create, "CXL"); diff --git a/drivers/cxl/core/mce.c b/drivers/cxl/core/mce.c index ff8d078c6ca1..65fed913b221 100644 --- a/drivers/cxl/core/mce.c +++ b/drivers/cxl/core/mce.c @@ -4,16 +4,16 @@ #include #include #include -#include +#include +#include "core.h" #include "mce.h" static int cxl_handle_mce(struct notifier_block *nb, unsigned long val, void *data) { - struct cxl_memdev_state *mds = container_of(nb, struct cxl_memdev_state, - mce_notifier); - struct cxl_memdev *cxlmd = mds->cxlds.cxlmd; - struct cxl_port *endpoint = cxlmd->endpoint; + struct cxl_region *cxlr = container_of(nb, struct cxl_region, + mce_notifier); + struct cxl_region_params *p = &cxlr->params; struct mce *mce = data; u64 spa, spa_alias; unsigned long pfn; @@ -21,26 +21,25 @@ static int cxl_handle_mce(struct notifier_block *nb, unsigned long val, if (!mce || !mce_usable_address(mce)) return NOTIFY_DONE; - if (!endpoint) - return NOTIFY_DONE; - spa = mce->addr & MCI_ADDR_PHYSADDR; - pfn = spa >> PAGE_SHIFT; - if (!pfn_valid(pfn)) + if (!cxl_resource_contains_addr(p->res, spa)) return NOTIFY_DONE; - spa_alias = cxl_port_get_spa_cache_alias(endpoint, spa); - if (spa_alias == ~0ULL) - return NOTIFY_DONE; + if (spa >= p->res->start + p->cache_size) + spa_alias = spa - p->cache_size; + else + spa_alias = spa + p->cache_size; pfn = spa_alias >> PAGE_SHIFT; + if (!pfn_valid(pfn)) + return NOTIFY_DONE; /* * Take down the aliased memory page. The original memory page flagged * by the MCE will be taken cared of by the standard MCE handler. */ - dev_emerg(mds->cxlds.dev, "Offlining aliased SPA address0: %#llx\n", + dev_emerg(&cxlr->dev, "Offlining aliased SPA address0: %#llx\n", spa_alias); if (!memory_failure(pfn, 0)) set_mce_nospec(pfn); diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c index 1e211542b6b6..578622240401 100644 --- a/drivers/cxl/core/region.c +++ b/drivers/cxl/core/region.c @@ -15,6 +15,7 @@ #include #include #include "core.h" +#include "mce.h" /** * DOC: cxl core region @@ -3859,34 +3860,6 @@ int cxl_add_to_region(struct cxl_endpoint_decoder *cxled) } EXPORT_SYMBOL_NS_GPL(cxl_add_to_region, "CXL"); -u64 cxl_port_get_spa_cache_alias(struct cxl_port *endpoint, u64 spa) -{ - struct cxl_region_ref *iter; - unsigned long index; - - if (!endpoint) - return ~0ULL; - - guard(rwsem_write)(&cxl_rwsem.region); - - xa_for_each(&endpoint->regions, index, iter) { - struct cxl_region_params *p = &iter->region->params; - - if (cxl_resource_contains_addr(p->res, spa)) { - if (!p->cache_size) - return ~0ULL; - - if (spa >= p->res->start + p->cache_size) - return spa - p->cache_size; - - return spa + p->cache_size; - } - } - - return ~0ULL; -} -EXPORT_SYMBOL_NS_GPL(cxl_port_get_spa_cache_alias, "CXL"); - static int is_system_ram(struct resource *res, void *arg) { struct cxl_region *cxlr = arg; @@ -4217,6 +4190,20 @@ static int cxl_region_probe(struct device *dev) if (rc) return rc; + /* + * Regions fronted by an extended linear cache need the MCE notifier to + * offline the aliased page on a memory error. + */ + if (p->cache_size) { + rc = devm_cxl_register_mce_notifier(&cxlr->dev, + &cxlr->mce_notifier); + if (rc == -EOPNOTSUPP) + dev_warn(&cxlr->dev, + "CONFIG_CXL_MCE disabled, MCE notifier not registered\n"); + else if (rc) + return rc; + } + rc = cxl_region_setup_poison(cxlr); if (rc) return rc; diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h index c0e5308e4d1b..bdd0132c999d 100644 --- a/drivers/cxl/cxl.h +++ b/drivers/cxl/cxl.h @@ -478,6 +478,7 @@ struct cxl_region_params { * @coord: QoS access coordinates for the region * @node_notifier: notifier for setting the access coordinates to node * @adist_notifier: notifier for calculating the abstract distance of node + * @mce_notifier: notifier for MCE */ struct cxl_region { struct device dev; @@ -493,6 +494,7 @@ struct cxl_region { struct access_coordinate coord[ACCESS_COORDINATE_MAX]; struct notifier_block node_notifier; struct notifier_block adist_notifier; + struct notifier_block mce_notifier; }; struct cxl_nvdimm_bridge { @@ -870,7 +872,6 @@ bool is_cxl_pmem_region(struct device *dev); struct cxl_pmem_region *to_cxl_pmem_region(struct device *dev); int cxl_add_to_region(struct cxl_endpoint_decoder *cxled); struct cxl_dax_region *to_cxl_dax_region(struct device *dev); -u64 cxl_port_get_spa_cache_alias(struct cxl_port *endpoint, u64 spa); bool cxl_region_contains_resource(const struct resource *res); #else static inline bool is_cxl_pmem_region(struct device *dev) @@ -889,11 +890,6 @@ static inline struct cxl_dax_region *to_cxl_dax_region(struct device *dev) { return NULL; } -static inline u64 cxl_port_get_spa_cache_alias(struct cxl_port *endpoint, - u64 spa) -{ - return 0; -} static inline bool cxl_region_contains_resource(const struct resource *res) { return false; diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h index 503ae2864591..c401e3a1af06 100644 --- a/drivers/cxl/cxlmem.h +++ b/drivers/cxl/cxlmem.h @@ -431,7 +431,6 @@ static inline struct cxl_dev_state *mbox_to_cxlds(struct cxl_mailbox *cxl_mbox) * @poison: poison driver state info * @security: security driver state info * @fw: firmware upload / activation state - * @mce_notifier: MCE notifier * * See CXL 3.0 8.2.9.8.2 Capacity Configuration and Label Storage for * details on capacity parameters. @@ -451,7 +450,6 @@ struct cxl_memdev_state { struct cxl_poison_state poison; struct cxl_security_state security; struct cxl_fw_state fw; - struct notifier_block mce_notifier; }; static inline struct cxl_memdev_state * From f687394af983df5660b6afae7e0d06969f3af206 Mon Sep 17 00:00:00 2001 From: Zhenhao Wan Date: Sat, 20 Jun 2026 12:33:15 +0800 Subject: [PATCH 06/14] cxl/features: bound fwctl command payload to the input buffer fwctl_cmd_rpc() copies cmd->in_len bytes into inbuf = kvzalloc(cmd->in_len) and passes inbuf and in_len to ->fw_rpc(). The CXL callback cxlctl_fw_rpc() ignores in_len and never checks the user-controlled op_size against it. cxlctl_set_feature() bounds op_size only from below (op_size <= sizeof(feat_in->hdr)) and then reads op_size - sizeof(hdr) bytes from feat_in->feat_data via cxl_set_feature(). With a small in_len and a large op_size the first memcpy() already reads past the kvzalloc(in_len) buffer; the out-of-bounds bytes are placed in the mailbox payload and sent to the device, and a large enough op_size can walk into unmapped memory and oops the kernel. The Get paths pin op_size to a fixed size but likewise read the input struct without checking in_len. Reject, at the single dispatch point, any request whose fixed header plus op_size does not fit in the copied-in buffer. The lower-bound test guards the subtraction and ensures op_size was copied in before it is read. Fixes: eb5dfcb9e36d ("cxl: Add support to handle user feature commands for set feature") Reported-by: Yuhao Jiang Signed-off-by: Zhenhao Wan Cc: stable@vger.kernel.org Reviewed-by: Jason Gunthorpe Link: https://patch.msgid.link/20260620-cxl-fwctl-oob-v1-1-5758e34d784a@gmail.com Signed-off-by: Dave Jiang --- drivers/cxl/core/features.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/cxl/core/features.c b/drivers/cxl/core/features.c index 85185af46b72..738a89863ee8 100644 --- a/drivers/cxl/core/features.c +++ b/drivers/cxl/core/features.c @@ -649,7 +649,13 @@ static void *cxlctl_fw_rpc(struct fwctl_uctx *uctx, enum fwctl_rpc_scope scope, struct cxl_memdev *cxlmd = fwctl_to_memdev(fwctl_dev); struct cxl_features_state *cxlfs = to_cxlfs(cxlmd->cxlds); const struct fwctl_rpc_cxl *rpc_in = in; - u16 opcode = rpc_in->opcode; + u16 opcode; + + if (in_len < sizeof(rpc_in->hdr) || + rpc_in->op_size > in_len - sizeof(rpc_in->hdr)) + return ERR_PTR(-EINVAL); + + opcode = rpc_in->opcode; if (!cxlctl_validate_hw_command(cxlfs, rpc_in, scope, opcode)) return ERR_PTR(-EINVAL); From af5035e1b3e400067bb003975936e5407377e7a3 Mon Sep 17 00:00:00 2001 From: Guzebing Date: Mon, 13 Jul 2026 19:27:44 +0800 Subject: [PATCH 07/14] cxl/memdev: Fix firmware upload exact-fit handling cxl_fw_prepare() classifies a firmware image as a one-shot transfer only when its Transfer FW input payload is smaller than the mailbox payload size. An image that exactly fills the payload is therefore treated as a multi-part transfer. The firmware loader invokes cxl_fw_write() only once for that image. Since both offset == 0 and remaining == 0, the multi-part action selection sends INITIATE, never sends END, and then attempts to activate the target slot. Include equality in the one-shot classification so exact-fit images use the FULL action. Fixes: 9521875bbe00 ("cxl: add a firmware update mechanism using the sysfs firmware loader") Signed-off-by: Guzebing Reviewed-by: Dave Jiang Link: https://patch.msgid.link/20260713112744.2543829-1-guzebing1612@gmail.com Signed-off-by: Dave Jiang --- drivers/cxl/core/memdev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/cxl/core/memdev.c b/drivers/cxl/core/memdev.c index 33a3d2e7b13a..45e7d2be17e0 100644 --- a/drivers/cxl/core/memdev.c +++ b/drivers/cxl/core/memdev.c @@ -921,7 +921,7 @@ static enum fw_upload_err cxl_fw_prepare(struct fw_upload *fwl, const u8 *data, if (!size) return FW_UPLOAD_ERR_INVALID_SIZE; - mds->fw.oneshot = struct_size(transfer, data, size) < + mds->fw.oneshot = struct_size(transfer, data, size) <= cxl_mbox->payload_size; if (cxl_mem_get_fw_info(mds)) From 8b301c4afbce4bc3f94528441d8d5ce1366504ad Mon Sep 17 00:00:00 2001 From: Dave Jiang Date: Thu, 9 Jul 2026 08:57:14 -0700 Subject: [PATCH 08/14] cxl/mbox: Break poison list loop on an empty payload A device that returns count == 0 with CXL_POISON_FLAG_MORE set on every iteration never advances nr_records, so the max_errors guard never trips and the do/while loops forever while holding poison.mutex. That hangs the sysfs-triggered scan thread and blocks all subsequent poison operations on the device. The existing "Protect against an uncleared _FLAG_MORE" guard was intended to bound a misbehaving device but does not cover the count == 0 case. Stop the loop on an empty payload so a malfunctioning or malicious device cannot wedge the poison scan. Link: https://sashiko.dev/#/patchset/20260702090849.47501-1-icheng@nvidia.com?part=3 Fixes: ed83f7ca398b ("cxl/mbox: Add GET_POISON_LIST mailbox command") Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Alison Schofield Link: https://patch.msgid.link/20260709155714.1893280-1-dave.jiang@intel.com Signed-off-by: Dave Jiang --- drivers/cxl/core/mbox.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/cxl/core/mbox.c b/drivers/cxl/core/mbox.c index 94b1f7167588..241526bb9e80 100644 --- a/drivers/cxl/core/mbox.c +++ b/drivers/cxl/core/mbox.c @@ -1450,6 +1450,11 @@ int cxl_mem_get_poison(struct cxl_memdev *cxlmd, u64 offset, u64 len, if (rc) break; + if (!le16_to_cpu(po->count)) { + dev_dbg(&cxlmd->dev, "Poison empty payload!\n"); + break; + } + for (int i = 0; i < le16_to_cpu(po->count); i++) trace_cxl_poison(cxlmd, cxlr, &po->record[i], po->flags, po->overflow_ts, From 430c502c80e542e77bcf97db13ec0e8cdf9addb0 Mon Sep 17 00:00:00 2001 From: Dave Jiang Date: Mon, 6 Jul 2026 15:43:22 -0700 Subject: [PATCH 09/14] cxl/pci: Honor -EPROBE_DEFER from component register setup cxl_pci_setup_regs() for CXL_REGLOC_RBI_COMPONENT can return -EPROBE_DEFER on a Restricted CXL Host (RCD) when the upstream port has not yet been enumerated and the Component Registers must be extracted from the RCRB. cxl_pci_probe() treats every non-zero return from that call as the benign "component registers not found" case, logs a warning, and continues. The rc is then immediately overwritten by the subsequent cxl_pci_type3_init_mailbox() call, so the deferral is silently swallowed. Return -EPROBE_DEFER instead of continuing so the probe is retried once the upstream port is available. Fixes: 733b57f262b0 ("cxl/pci: Early setup RCH dport component registers from RCRB") Reported-by: sashiko-bot@kernel.org Closes: https://lore.kernel.org/linux-cxl/ajzhsubot_PSYtHQ@MWDK4CY14F/T/#m063bbf76b1c9c293ade52ab311018ae6bba11a44 Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Alison Schofield Link: https://lore.kernel.org/linux-cxl/ajzhsubot_PSYtHQ@MWDK4CY14F/T/#m063bbf76b1c9c293ade52ab311018ae6bba11a44 Link: https://patch.msgid.link/20260706224322.714934-1-dave.jiang@intel.com Signed-off-by: Dave Jiang --- drivers/cxl/pci.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c index 0c9d7f8ec9e4..9d9952e391ac 100644 --- a/drivers/cxl/pci.c +++ b/drivers/cxl/pci.c @@ -823,10 +823,13 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) */ rc = cxl_pci_setup_regs(pdev, CXL_REGLOC_RBI_COMPONENT, &cxlds->reg_map); - if (rc) + if (rc) { + if (rc == -EPROBE_DEFER) + return rc; dev_warn(&pdev->dev, "No component registers (%d)\n", rc); - else if (!cxlds->reg_map.component_map.ras.valid) + } else if (!cxlds->reg_map.component_map.ras.valid) { dev_dbg(&pdev->dev, "RAS registers not found\n"); + } rc = cxl_pci_type3_init_mailbox(cxlds); if (rc) From 77b814c1832fde018c30357b4ec3fcdaa91a1c10 Mon Sep 17 00:00:00 2001 From: Dave Jiang Date: Thu, 9 Jul 2026 08:58:41 -0700 Subject: [PATCH 10/14] cxl/features: Serialize multi-part Get/Set Feature transfers A Get or Set Feature payload larger than the mailbox payload size is split into several mailbox commands. mbox_mutex only serializes individual mailbox commands and is dropped between iterations of these loops. Nothing serializes the multi-part transfer as a whole. cxl_get_feature() and cxl_set_feature() are reachable concurrently from fwctl (per-fd RPCs run under a read-held registration lock) and from the EDAC scrub/ECS/repair paths, so two transfers to the same mailbox can interleave their parts and corrupt the device's transfer context. Add a per-mailbox feat_mutex and hold it across the whole transfer in both functions. It nests outside mbox_mutex (which is taken inside cxl_internal_send_cmd()), and is taken nowhere else, so no lock-ordering inversion is introduced. Link: https://sashiko.dev/#/patchset/20260702090849.47501-1-icheng@nvidia.com?part=1 Fixes: 5e5ac21f629d ("cxl/mbox: Add GET_FEATURE mailbox command") Fixes: 14d502cc2718 ("cxl/mbox: Add SET_FEATURE mailbox command") Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Richard Cheng Link: https://patch.msgid.link/20260709155841.1895915-1-dave.jiang@intel.com Signed-off-by: Dave Jiang --- drivers/cxl/core/features.c | 3 +++ drivers/cxl/core/mbox.c | 1 + include/cxl/mailbox.h | 2 ++ 3 files changed, 6 insertions(+) diff --git a/drivers/cxl/core/features.c b/drivers/cxl/core/features.c index 738a89863ee8..8731b95dd0b5 100644 --- a/drivers/cxl/core/features.c +++ b/drivers/cxl/core/features.c @@ -240,6 +240,8 @@ size_t cxl_get_feature(struct cxl_mailbox *cxl_mbox, const uuid_t *feat_uuid, size_out = min(feat_out_size, cxl_mbox->payload_size); uuid_copy(&pi.uuid, feat_uuid); pi.selection = selection; + + guard(mutex)(&cxl_mbox->feat_mutex); do { data_to_rd_size = min(feat_out_size - data_rcvd_size, cxl_mbox->payload_size); @@ -314,6 +316,7 @@ int cxl_set_feature(struct cxl_mailbox *cxl_mbox, data_in_size = cxl_mbox->payload_size - hdr_size; } + guard(mutex)(&cxl_mbox->feat_mutex); do { int rc; diff --git a/drivers/cxl/core/mbox.c b/drivers/cxl/core/mbox.c index 241526bb9e80..cc479f4322e7 100644 --- a/drivers/cxl/core/mbox.c +++ b/drivers/cxl/core/mbox.c @@ -1516,6 +1516,7 @@ int cxl_mailbox_init(struct cxl_mailbox *cxl_mbox, struct device *host) cxl_mbox->host = host; mutex_init(&cxl_mbox->mbox_mutex); + mutex_init(&cxl_mbox->feat_mutex); rcuwait_init(&cxl_mbox->mbox_wait); return 0; diff --git a/include/cxl/mailbox.h b/include/cxl/mailbox.h index c4e99e2e3a9d..d008b9db07aa 100644 --- a/include/cxl/mailbox.h +++ b/include/cxl/mailbox.h @@ -50,6 +50,7 @@ struct cxl_mbox_cmd { * @payload_size: Size of space for payload * (CXL 3.1 8.2.8.4.3 Mailbox Capabilities Register) * @mbox_mutex: mutex protects device mailbox and firmware + * @feat_mutex: serializes multi-part Get/Set Feature transfers * @mbox_wait: rcuwait for mailbox * @mbox_send: @dev specific transport for transmitting mailbox commands * @feat_cap: Features capability @@ -60,6 +61,7 @@ struct cxl_mailbox { DECLARE_BITMAP(exclusive_cmds, CXL_MEM_COMMAND_ID_MAX); size_t payload_size; struct mutex mbox_mutex; /* lock to protect mailbox context */ + struct mutex feat_mutex; struct rcuwait mbox_wait; int (*mbox_send)(struct cxl_mailbox *cxl_mbox, struct cxl_mbox_cmd *cmd); enum cxl_features_capability feat_cap; From a623128bc2a1c257cbad97d0582f355fbe7be927 Mon Sep 17 00:00:00 2001 From: Alison Schofield Date: Mon, 13 Jul 2026 19:04:35 -0700 Subject: [PATCH 11/14] cxl/port: Restart port enumeration when a sibling adds the dport first Endpoint probes can race while enumerating a shared switch. If a sibling probe adds the dport first, the losing probe finds the dport already present, gets -EBUSY, and fails to enumerate the endpoint. Treat this race the same as the existing port-created case by restarting the port walk, allowing it to find the existing dport and continue enumeration. This race was discovered while testing a cxl_test mixed-granularity topology, where twelve endpoints behind shared switches are probed in parallel during module load. Fixes: 4f06d81e7c6a ("cxl: Defer dport allocation for switch ports") Signed-off-by: Alison Schofield Tested-by: Li Ming Reviewed-by: Li Ming Link: https://patch.msgid.link/20260714020438.1822669-1-alison.schofield@intel.com Signed-off-by: Dave Jiang --- drivers/cxl/core/port.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c index 1215ee4f4035..65f2d2f1eb00 100644 --- a/drivers/cxl/core/port.c +++ b/drivers/cxl/core/port.c @@ -1749,8 +1749,8 @@ static int add_port_attach_ep(struct cxl_memdev *cxlmd, parent_dport, uport_dev, dport_dev); if (IS_ERR(dport)) { - /* Port already exists, restart iteration */ - if (PTR_ERR(dport) == -EAGAIN) + /* Port or dport already exists, restart iteration */ + if (PTR_ERR(dport) == -EAGAIN || PTR_ERR(dport) == -EBUSY) return 0; return PTR_ERR(dport); } From 4bf6bac375076ced2fa4b3fef8739bd985f93456 Mon Sep 17 00:00:00 2001 From: Richard Cheng Date: Fri, 26 Jun 2026 18:41:00 +0800 Subject: [PATCH 12/14] cxl/features: Reject Get Feature count larger than the output buffer cxlctl_get_feature() sizes its output buffer from the user's fwctl_rpc.out_len, but the device is told to write cxl_mbox_get_feat_in.count bytes into rpc_out->payload, which is a separate user-controlled value. Nothing bounds count against out_len, so a small out_len with a large count overflows the kvzalloc()'d buffer. A heap OOB write reachable from FWCTL_RPC. Reject requests where count exceeds the available payload room, before allocating. Fixes: 5908f3ed6dc2 ("cxl: Add support to handle user feature commands for get feature") Reviewed-by: Kai-Heng Feng Reviewed-by: Koba Ko Reviewed-by: Dave Jiang Signed-off-by: Richard Cheng Reviewed-by: Alison Schofield Link: https://patch.msgid.link/20260626104102.53892-2-icheng@nvidia.com Signed-off-by: Dave Jiang --- drivers/cxl/core/features.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/cxl/core/features.c b/drivers/cxl/core/features.c index 8731b95dd0b5..d50e6b58d8fd 100644 --- a/drivers/cxl/core/features.c +++ b/drivers/cxl/core/features.c @@ -474,6 +474,10 @@ static void *cxlctl_get_feature(struct cxl_features_state *cxlfs, if (!count) return ERR_PTR(-EINVAL); + if (out_size < offsetof(struct fwctl_rpc_cxl_out, payload) || + count > out_size - offsetof(struct fwctl_rpc_cxl_out, payload)) + return ERR_PTR(-EINVAL); + struct fwctl_rpc_cxl_out *rpc_out __free(kvfree) = kvzalloc(out_size, GFP_KERNEL); if (!rpc_out) From cde18d6c1d913a67ab0afd3d9475ece4be79da50 Mon Sep 17 00:00:00 2001 From: Richard Cheng Date: Fri, 26 Jun 2026 18:41:01 +0800 Subject: [PATCH 13/14] cxl/features: Reject Set Features output buffer smaller than the header cxlctl_set_feature() sizes its output buffer from the user's fwctl_rpc.out_len but never checks it is large enough to hold even the fwctl_rpc_cxl_out header. With out_len == 0 , kvzalloc() returns ZERO_SIZE_PTR, which passes the !rpc_out check, the subsequent rpc_out->size = 0 then writes through the poison pointer. Reject requests whose output buffer can't hold the response header, before allocating. The Set Feature reply carries no payload, so the header is all that is required. Fixes: eb5dfcb9e36d ("cxl: Add support to handle user feature commands for set feature") Signed-off-by: Richard Cheng Reviewed-by: Dave Jiang Reviewed-by: Alison Schofield Link: https://patch.msgid.link/20260626104102.53892-3-icheng@nvidia.com Signed-off-by: Dave Jiang --- drivers/cxl/core/features.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/cxl/core/features.c b/drivers/cxl/core/features.c index d50e6b58d8fd..2eedabb5f702 100644 --- a/drivers/cxl/core/features.c +++ b/drivers/cxl/core/features.c @@ -523,6 +523,9 @@ static void *cxlctl_set_feature(struct cxl_features_state *cxlfs, flags = le32_to_cpu(feat_in->flags); out_size = *out_len; + if (out_size < offsetof(struct fwctl_rpc_cxl_out, payload)) + return ERR_PTR(-EINVAL); + struct fwctl_rpc_cxl_out *rpc_out __free(kvfree) = kvzalloc(out_size, GFP_KERNEL); if (!rpc_out) From 2aeb21fe557ef154f0cdf4f9745ebd8d5b31ca83 Mon Sep 17 00:00:00 2001 From: Richard Cheng Date: Fri, 26 Jun 2026 18:41:02 +0800 Subject: [PATCH 14/14] cxl/features: Clamp Get Feature output size to the remaining buffer cxl_get_feature() reads a feature in a loop but passes a fixed size_out as the output capacity every iteration. On the last partial iteration the buffer has less room left, so a device that returns more than asked can overflow feat_out. Use the per-iter size data_to_rd_size, which already tracks the remaining room, as the output capacity. Fixes: 5e5ac21f629d ("cxl/mbox: Add GET_FEATURE mailbox command") Signed-off-by: Richard Cheng Reviewed-by: Dave Jiang Reviewed-by: Alison Schofield Link: https://patch.msgid.link/20260626104102.53892-4-icheng@nvidia.com Signed-off-by: Dave Jiang --- drivers/cxl/core/features.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/cxl/core/features.c b/drivers/cxl/core/features.c index 2eedabb5f702..ba6d2a5acb74 100644 --- a/drivers/cxl/core/features.c +++ b/drivers/cxl/core/features.c @@ -225,7 +225,7 @@ size_t cxl_get_feature(struct cxl_mailbox *cxl_mbox, const uuid_t *feat_uuid, void *feat_out, size_t feat_out_size, u16 offset, u16 *return_code) { - size_t data_to_rd_size, size_out; + size_t data_to_rd_size; struct cxl_mbox_get_feat_in pi; struct cxl_mbox_cmd mbox_cmd; size_t data_rcvd_size = 0; @@ -237,7 +237,6 @@ size_t cxl_get_feature(struct cxl_mailbox *cxl_mbox, const uuid_t *feat_uuid, if (!feat_out || !feat_out_size) return 0; - size_out = min(feat_out_size, cxl_mbox->payload_size); uuid_copy(&pi.uuid, feat_uuid); pi.selection = selection; @@ -252,7 +251,7 @@ size_t cxl_get_feature(struct cxl_mailbox *cxl_mbox, const uuid_t *feat_uuid, .opcode = CXL_MBOX_OP_GET_FEATURE, .size_in = sizeof(pi), .payload_in = &pi, - .size_out = size_out, + .size_out = data_to_rd_size, .payload_out = feat_out + data_rcvd_size, .min_out = data_to_rd_size, };