ipi_nmi.c 1.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
// SPDX-License-Identifier: GPL-2.0-only
/*
 * NMI support for IPIs
 *
 * Copyright (C) 2020 Linaro Limited
 * Author: Sumit Garg <sumit.garg@linaro.org>
 */

#include <linux/interrupt.h>
#include <linux/irq.h>
11
#include <linux/kgdb.h>
12
#include <linux/nmi.h>
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
#include <linux/smp.h>

#include <asm/nmi.h>

static struct irq_desc *ipi_nmi_desc __read_mostly;
static int ipi_nmi_id __read_mostly;

bool arm64_supports_nmi(void)
{
	if (ipi_nmi_desc)
		return true;

	return false;
}

void arm64_send_nmi(cpumask_t *mask)
{
	if (WARN_ON_ONCE(!ipi_nmi_desc))
		return;

	__ipi_send_mask(ipi_nmi_desc, mask);
}

36 37 38 39 40 41 42 43 44 45
bool arch_trigger_cpumask_backtrace(const cpumask_t *mask, bool exclude_self)
{
	if (!ipi_nmi_desc)
		return false;

	nmi_trigger_cpumask_backtrace(mask, exclude_self, arm64_send_nmi);

	return true;
}

46 47
static irqreturn_t ipi_nmi_handler(int irq, void *data)
{
48 49 50 51
	irqreturn_t ret = IRQ_NONE;

	if (nmi_cpu_backtrace(get_irq_regs()))
		ret = IRQ_HANDLED;
52

53 54
#ifdef CONFIG_KGDB
	if (!kgdb_nmicallback(smp_processor_id(), get_irq_regs()))
55
		ret = IRQ_HANDLED;
56
#endif
57

58
	return ret;
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
}

void dynamic_ipi_setup(int cpu)
{
	if (!ipi_nmi_desc)
		return;

	if (!prepare_percpu_nmi(ipi_nmi_id))
		enable_percpu_nmi(ipi_nmi_id, IRQ_TYPE_NONE);
}

void dynamic_ipi_teardown(int cpu)
{
	if (!ipi_nmi_desc)
		return;

	disable_percpu_nmi(ipi_nmi_id);
	teardown_percpu_nmi(ipi_nmi_id);
}

void __init set_smp_dynamic_ipi(int ipi)
{
	if (!request_percpu_nmi(ipi, ipi_nmi_handler, "IPI", &cpu_number)) {
		ipi_nmi_desc = irq_to_desc(ipi);
		ipi_nmi_id = ipi;
	}
}