mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-05 16:54:27 +00:00
66 lines
1.6 KiB
C
66 lines
1.6 KiB
C
![]() |
// 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);
|
||
|
}
|