summaryrefslogtreecommitdiffstats
path: root/drivers/cxl/core/mce.c
blob: ff8d078c6ca1e97031296d3ff1c1025cf0cb38c4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright(c) 2024 Intel Corporation. All rights reserved. */
#include <linux/mm.h>
#include <linux/notifier.h>
#include <linux/set_memory.h>
#include <asm/mce.h>
#include <cxlmem.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 mce *mce = data;
	u64 spa, spa_alias;
	unsigned long pfn;

	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))
		return NOTIFY_DONE;

	spa_alias = cxl_port_get_spa_cache_alias(endpoint, spa);
	if (spa_alias == ~0ULL)
		return NOTIFY_DONE;

	pfn = spa_alias >> PAGE_SHIFT;

	/*
	 * 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",
		  spa_alias);
	if (!memory_failure(pfn, 0))
		set_mce_nospec(pfn);

	return NOTIFY_OK;
}

static void cxl_unregister_mce_notifier(void *mce_notifier)
{
	mce_unregister_decode_chain(mce_notifier);
}

int devm_cxl_register_mce_notifier(struct device *dev,
				   struct notifier_block *mce_notifier)
{
	mce_notifier->notifier_call = cxl_handle_mce;
	mce_notifier->priority = MCE_PRIO_UC;
	mce_register_decode_chain(mce_notifier);

	return devm_add_action_or_reset(dev, cxl_unregister_mce_notifier,
					mce_notifier);
}