root/drivers/cxl/core/mce.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);
}