1 // SPDX-License-Identifier: GPL-2.0-only 2 /* Copyright(c) 2024 Intel Corporation. All rights reserved. */ 3 #include <linux/mm.h> 4 #include <linux/notifier.h> 5 #include <linux/set_memory.h> 6 #include <asm/mce.h> 7 #include <cxlmem.h> 8 #include "mce.h" 9 10 static int cxl_handle_mce(struct notifier_block *nb, unsigned long val, 11 void *data) 12 { 13 struct cxl_memdev_state *mds = container_of(nb, struct cxl_memdev_state, 14 mce_notifier); 15 struct cxl_memdev *cxlmd = mds->cxlds.cxlmd; 16 struct cxl_port *endpoint = cxlmd->endpoint; 17 struct mce *mce = data; 18 u64 spa, spa_alias; 19 unsigned long pfn; 20 21 if (!mce || !mce_usable_address(mce)) 22 return NOTIFY_DONE; 23 24 if (!endpoint) 25 return NOTIFY_DONE; 26 27 spa = mce->addr & MCI_ADDR_PHYSADDR; 28 29 pfn = spa >> PAGE_SHIFT; 30 if (!pfn_valid(pfn)) 31 return NOTIFY_DONE; 32 33 spa_alias = cxl_port_get_spa_cache_alias(endpoint, spa); 34 if (spa_alias == ~0ULL) 35 return NOTIFY_DONE; 36 37 pfn = spa_alias >> PAGE_SHIFT; 38 39 /* 40 * Take down the aliased memory page. The original memory page flagged 41 * by the MCE will be taken cared of by the standard MCE handler. 42 */ 43 dev_emerg(mds->cxlds.dev, "Offlining aliased SPA address0: %#llx\n", 44 spa_alias); 45 if (!memory_failure(pfn, 0)) 46 set_mce_nospec(pfn); 47 48 return NOTIFY_OK; 49 } 50 51 static void cxl_unregister_mce_notifier(void *mce_notifier) 52 { 53 mce_unregister_decode_chain(mce_notifier); 54 } 55 56 int devm_cxl_register_mce_notifier(struct device *dev, 57 struct notifier_block *mce_notifier) 58 { 59 mce_notifier->notifier_call = cxl_handle_mce; 60 mce_notifier->priority = MCE_PRIO_UC; 61 mce_register_decode_chain(mce_notifier); 62 63 return devm_add_action_or_reset(dev, cxl_unregister_mce_notifier, 64 mce_notifier); 65 } 66