cxl/mce: Make the MCE notifier per-region
Subsystems (2)
Dave Jiang
maintainerPATCH v2 1/1 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.
---
v2:
- First version for this code, but replaces the previous 2 patches as the fix. Replaces the series "cxl: Fix ednpoint access issues with CXL MCE notifier handler". (Dan)
--- drivers/cxl/core/mbox.c | 8 -------- drivers/cxl/core/mce.c | 27 ++++++++++++------------- drivers/cxl/core/region.c | 42 +++++++++++++-------------------------- drivers/cxl/cxl.h | 8 ++------ drivers/cxl/cxlmem.h | 2 -- 5 files changed, 29 insertions(+), 58 deletions(-)
drivers/cxl/core/mbox.c +0 -8
@@ -11,7 +11,6 @@#include "core.h" #include "trace.h" -#include "mce.h" static bool cxl_raw_allow_all;
@@ -1526,7 +1525,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,
@@ -1538,12 +1536,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" );
drivers/cxl/core/mce.c +13 -14
@@ -4,16 +4,16 @@#include <linux/notifier.h> #include <linux/set_memory.h> #include <asm/mce.h> -#include <cxlmem.h> +#include <cxl.h> +#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);
drivers/cxl/core/region.c +14 -28
60 lines not on this page · raw message
drivers/cxl/cxl.h +2 -6
@@ -462,6 +462,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;
@@ -477,6 +478,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 {
@@ -854,7 +856,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)
@@ -873,11 +874,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;
drivers/cxl/cxlmem.h +0 -2
@@ -409,7 +409,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.
@@ -429,7 +428,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 *
base-commit: 8cd9520d35a6c38db6567e97dd93b1f11f185dc6
Signature · 1 line
2.54.0
Quoted 14 lines · Dave Jiang
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 fromcxl_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.
Quoted 7 lines
Reported-by Flavien Solt <[email protected]>Suggested-by Dan Williams <[email protected]>Fixes 516e5bd0b6bf ("cxl: Add mce notifier to emit aliased address for extended linear cache")Assisted-by Claude:claude-opus-4-8Signed-off-by Dave Jiang <[email protected]>---
One nit below, but looks good otherwise:
[snip]
Quoted 6 lines
static int
is_system_ram(struct resource *res, void *arg){drivers/cxl/core/region.c +0
-0struct cxl_region *cxlr = arg;@@ -4070,6 +4043,19 @@ static int cxl_region_probe(struct device *dev)if (rc)return rc;
drivers/cxl/core/region.c static int cxl_region_probe(struct device *dev) · line 4054
Quoted 10 lines
drivers/cxl/core/region.c +9
-0+/* +* 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,"CXL MCE unsupported\n" );
I would demote this to an info() or dbg() print. A warning print here is a bit overkill for essentially having a config option disabled that's largely dependent on the platform.
If you think a warn() is warranted, I'd change the message to say the config is disabled instead so that the end user is pointed to why it's unsupported.
Thanks,
Ben
Dave Jiang
maintainerQuoted 23 lines · Benjamin
Quoted 14 lines · Dave Jiang
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 fromcxl_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.Quoted 7 lines
Reported-by Flavien Solt <[email protected]>Suggested-by Dan Williams <[email protected]>Fixes 516e5bd0b6bf ("cxl: Add mce notifier to emit aliased address for extended linear cache")Assisted-by Claude:claude-opus-4-8Signed-off-by Dave Jiang <[email protected]>---
Quoted 3 lines
One nit below, but looks good otherwise:
Reviewed-by Ben Cheatham <[email protected]>
Thanks!
Quoted 23 lines
[snip]
Quoted 6 lines
static int
is_system_ram(struct resource *res, void *arg){drivers/cxl/core/region.c +0
-0struct cxl_region *cxlr = arg;@@ -4070,6 +4043,19 @@ static int cxl_region_probe(struct device *dev)if (rc)return rc;drivers/cxl/core/region.c static int cxl_region_probe(struct device *dev) · line 4054
Quoted 10 lines
drivers/cxl/core/region.c +9
-0+/* +* 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,"CXL MCE unsupported\n" );I would demote this to an
info()ordbg()print. A warning print here is a bit overkill for essentially having a config option disabled that's largely dependent on the platform.
Quoted 2 lines
If you think a
warn()is warranted, I'd change the message to say the config is disabled instead so that the end user is pointed to why it's unsupported.
Given that it won't report expected MCE emits and handle dealing with the aliased address, warn is probably reasonable. I'll update the message.
Quoted 3 lines
Thanks,
Ben
Quoted 6 lines · Dave Jiang
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).
Quoted 7 lines
Instead of keeping the memory device and endpoint alive, the correct fix is to move the notifier into 'struct
cxl_region' and register it fromcxl_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.
Quoted 12 lines · Dave Jiang
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 fromcxl_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.
Quoted 8 lines
Remove
cxl_port_get_spa_cache_alias()as it is now dead code.Reported-by Flavien Solt <[email protected]>Suggested-by Dan Williams <[email protected]>Fixes 516e5bd0b6bf ("cxl: Add mce notifier to emit aliased address for extended linear cache")Assisted-by Claude:claude-opus-4-8Signed-off-by Dave Jiang <[email protected]>
Applied to cxl/next775d0f4558f4
Quoted 236 lines
---
v2:
- First version for this code, but replaces the previous 2 patches as the fix. Replaces the series "cxl: Fix ednpoint access issues with CXL MCE notifier handler". (Dan)--- drivers/cxl/core/mbox.c | 8 -------- drivers/cxl/core/mce.c | 27 ++++++++++++------------- drivers/cxl/core/region.c | 42 +++++++++++++-------------------------- drivers/cxl/cxl.h | 8 ++------ drivers/cxl/cxlmem.h | 2 -- 5 files changed, 29 insertions(+), 58 deletions(-)drivers/cxl/core/mbox.c +0
-8@@ -11,7 +11,6 @@#include "core.h" #include "trace.h" -#include "mce.h" static bool cxl_raw_allow_all;@@ -1526,7 +1525,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,@@ -1538,12 +1536,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" );drivers/cxl/core/mce.c +13
-14@@ -4,16 +4,16 @@#include <linux/notifier.h> #include <linux/set_memory.h> #include <asm/mce.h> -#include <cxlmem.h> +#include <cxl.h> +#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);drivers/cxl/core/region.c +14
-28@@ -15,6 +15,7 @@#include <cxlmem.h> #include <cxl.h> #include "core.h" +#include "mce.h" /** * DOC: cxl core region @@ -3809,34 +3810,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;@@ -4070,6 +4043,19 @@ 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,"CXL MCE unsupported\n" ); +else if (rc) +return rc; + } + rc =cxl_region_setup_poison (cxlr);if (rc)return rc;drivers/cxl/cxl.h +2
-6@@ -462,6 +462,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;@@ -477,6 +478,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 {@@ -854,7 +856,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)@@ -873,11 +874,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;drivers/cxl/cxlmem.h +0
-2@@ -409,7 +409,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. @@ -429,7 +428,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 *base-commit:
8cd9520d35a6c38db6567e97dd93b1f11f185dc6