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