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
cxl_handle_mce(struct notifier_block * nb,unsigned long val,void * data)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
cxl_unregister_mce_notifier(void * mce_notifier)51 static void cxl_unregister_mce_notifier(void *mce_notifier)
52 {
53 mce_unregister_decode_chain(mce_notifier);
54 }
55
devm_cxl_register_mce_notifier(struct device * dev,struct notifier_block * mce_notifier)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