xref: /linux/drivers/cxl/core/mce.c (revision 570f7e331f5febb30f1384817463c7e42b65ca7d)
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 <cxl.h>
8 #include "core.h"
9 #include "mce.h"
10 
11 static int cxl_handle_mce(struct notifier_block *nb, unsigned long val,
12 			  void *data)
13 {
14 	struct cxl_region *cxlr = container_of(nb, struct cxl_region,
15 					       mce_notifier);
16 	struct cxl_region_params *p = &cxlr->params;
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 	spa = mce->addr & MCI_ADDR_PHYSADDR;
25 
26 	if (!cxl_resource_contains_addr(p->res, spa))
27 		return NOTIFY_DONE;
28 
29 	if (spa >= p->res->start + p->cache_size)
30 		spa_alias = spa - p->cache_size;
31 	else
32 		spa_alias = spa + p->cache_size;
33 
34 	pfn = spa_alias >> PAGE_SHIFT;
35 	if (!pfn_valid(pfn))
36 		return NOTIFY_DONE;
37 
38 	/*
39 	 * Take down the aliased memory page. The original memory page flagged
40 	 * by the MCE will be taken cared of by the standard MCE handler.
41 	 */
42 	dev_emerg(&cxlr->dev, "Offlining aliased SPA address0: %#llx\n",
43 		  spa_alias);
44 	if (!memory_failure(pfn, 0))
45 		set_mce_nospec(pfn);
46 
47 	return NOTIFY_OK;
48 }
49 
50 static void cxl_unregister_mce_notifier(void *mce_notifier)
51 {
52 	mce_unregister_decode_chain(mce_notifier);
53 }
54 
55 int devm_cxl_register_mce_notifier(struct device *dev,
56 				   struct notifier_block *mce_notifier)
57 {
58 	mce_notifier->notifier_call = cxl_handle_mce;
59 	mce_notifier->priority = MCE_PRIO_UC;
60 	mce_register_decode_chain(mce_notifier);
61 
62 	return devm_add_action_or_reset(dev, cxl_unregister_mce_notifier,
63 					mce_notifier);
64 }
65