diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 047e934d69a4d97d1164462ccf003ce77939a7ed..53ec86ffd2126a1da93a532387bd52fc54729448 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -1242,6 +1242,17 @@ config HISILICON_ERRATUM_161600802 If unsure, say Y. +config HISILICON_ERRATUM_165010801 + bool "Hisilicon erratum 165010801" + depends on ARCH_HISI + default y + help + On HIP12, when GIC receives multiple interrupts of the same priority and + different types, the interrupts are selected in the following sequence: + SPI > LPI > SGI > PPI. This scheduling rule may cause PPI starvation. + To prevent starvation from triggering system watchdog hardlockup, the + interrupt priority is explicitly increased in the arch_timer driver. + config QCOM_FALKOR_ERRATUM_1003 bool "Falkor E1003: Incorrect translation due to ASID change" default y diff --git a/arch/arm64/configs/tencent.config b/arch/arm64/configs/tencent.config index 9fe80baf90a718d5bcf23c9d29901fa49f1b21fe..82483fef0b16903fe0bdc6f03ca0f2f90ceebfab 100644 --- a/arch/arm64/configs/tencent.config +++ b/arch/arm64/configs/tencent.config @@ -609,6 +609,7 @@ CONFIG_CXL_BUS=m CONFIG_DEVTMPFS=y CONFIG_DEVTMPFS_MOUNT=y CONFIG_HISILICON_LPC=y +CONFIG_HISILICON_ERRATUM_165010801=y CONFIG_FSL_MC_BUS=y CONFIG_CONNECTOR=y CONFIG_ARM_SCMI_PROTOCOL=m diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile index c6a5be77d77d5b438fd2a3ba328d74b5ecc620e2..010e23ccadbe4406aefc2aaeefc33ca4e729b69b 100644 --- a/arch/arm64/kernel/Makefile +++ b/arch/arm64/kernel/Makefile @@ -68,6 +68,7 @@ arm64-reloc-test-y := reloc_test_core.o reloc_test_syms.o obj-$(CONFIG_CRASH_DUMP) += crash_dump.o obj-$(CONFIG_CRASH_CORE) += crash_core.o obj-$(CONFIG_ARM_SDE_INTERFACE) += sdei.o +obj-$(CONFIG_SDEI_WATCHDOG) += watchdog_sdei.o obj-$(CONFIG_ARM64_PTR_AUTH) += pointer_auth.o obj-$(CONFIG_ARM64_MTE) += mte.o obj-y += vdso-wrap.o diff --git a/arch/arm64/kernel/machine_kexec.c b/arch/arm64/kernel/machine_kexec.c index 078910db77a41b6ffa60c665debc927dff0c7ca5..40607a4fe3a5751690d4877d082a54fac1993457 100644 --- a/arch/arm64/kernel/machine_kexec.c +++ b/arch/arm64/kernel/machine_kexec.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -262,6 +263,16 @@ void machine_crash_shutdown(struct pt_regs *regs) /* shutdown non-crashing cpus */ crash_smp_send_stop(); + /* + * when we panic in hardlockup detected by sdei_watchdog, the secure + * timer interrupt remains activate here because firmware clear eoi + * after dispatch is completed. This will cause arm_arch_timer + * interrupt failed to trigger in the second kernel. So we clear eoi + * of the secure timer before booting the second kernel. + */ + if (in_nmi()) + sdei_watchdog_clear_eoi(); + /* for crashing cpu */ crash_save_cpu(regs, smp_processor_id()); machine_kexec_mask_interrupts(); diff --git a/arch/arm64/kernel/watchdog_hld.c b/arch/arm64/kernel/watchdog_hld.c index dcd25322127c35293e5e39251d162845d8848681..e55548cb26df08fb8043ef630d98241a11d7e1b3 100644 --- a/arch/arm64/kernel/watchdog_hld.c +++ b/arch/arm64/kernel/watchdog_hld.c @@ -34,3 +34,61 @@ bool __init arch_perf_nmi_is_available(void) */ return arm_pmu_irq_is_nmi(); } + +static int watchdog_perf_update_period(void *data) +{ + int cpu = raw_smp_processor_id(); + u64 max_cpu_freq, new_period; + + max_cpu_freq = cpufreq_get_hw_max_freq(cpu) * 1000UL; + if (!max_cpu_freq) + return 0; + + new_period = watchdog_thresh * max_cpu_freq; + hardlockup_detector_perf_adjust_period(cpu, new_period); + + return 0; +} + +static int watchdog_freq_notifier_callback(struct notifier_block *nb, + unsigned long val, void *data) +{ + struct cpufreq_policy *policy = data; + int cpu; + + if (val != CPUFREQ_CREATE_POLICY) + return NOTIFY_DONE; + + /* + * Let each online CPU related to the policy update the period by their + * own. This will serialize with the framework on start/stop the lockup + * detector (softlockup_{start,stop}_all) and avoid potential race + * condition. Otherwise we may have below theoretical race condition: + * (core 0/1 share the same policy) + * [core 0] [core 1] + * hardlockup_detector_event_create() + * hw_nmi_get_sample_period() + * (cpufreq registered, notifier callback invoked) + * watchdog_freq_notifier_callback() + * watchdog_perf_update_period() + * (since core 1's event's not yet created, + * the period is not set) + * perf_event_create_kernel_counter() + * (event's period is SAFE_MAX_CPU_FREQ) + */ + for_each_cpu(cpu, policy->cpus) + smp_call_on_cpu(cpu, watchdog_perf_update_period, NULL, false); + + return NOTIFY_DONE; +} + +static struct notifier_block watchdog_freq_notifier = { + .notifier_call = watchdog_freq_notifier_callback, +}; + +static int __init init_watchdog_freq_notifier(void) +{ + return cpufreq_register_notifier(&watchdog_freq_notifier, + CPUFREQ_POLICY_NOTIFIER); +} +core_initcall(init_watchdog_freq_notifier); diff --git a/arch/arm64/kernel/watchdog_sdei.c b/arch/arm64/kernel/watchdog_sdei.c new file mode 100644 index 0000000000000000000000000000000000000000..4116386632123e8d2f6bced3fcc8790455d1fb55 --- /dev/null +++ b/arch/arm64/kernel/watchdog_sdei.c @@ -0,0 +1,195 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Detect hard lockups on a system + * + * Note: Most of this code is borrowed heavily from the perf hardlockup + * detector, so thanks to Don for the initial implementation. + */ + +#define pr_fmt(fmt) "SDEI NMI watchdog: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* We use the secure physical timer as SDEI NMI watchdog timer */ +#define SDEI_NMI_WATCHDOG_HWIRQ 29 + +static int sdei_watchdog_event_num; +bool disable_sdei_nmi_watchdog; +static bool sdei_watchdog_registered; +static DEFINE_PER_CPU(ktime_t, last_check_time); + +void sdei_watchdog_hardlockup_enable(unsigned int cpu) +{ + int ret; + + if (!sdei_watchdog_registered) + return; + + /* Skip the first hardlockup check incase BIOS didn't init the + * secure timer correctly */ + watchdog_hardlockup_touch_cpu(cpu); + sdei_api_set_secure_timer_period(watchdog_thresh); + __this_cpu_write(last_check_time, ktime_get_mono_fast_ns()); + + ret = sdei_api_event_enable(sdei_watchdog_event_num); + if (ret) { + pr_err("Enable NMI Watchdog failed on cpu%d\n", + smp_processor_id()); + } +} + +void sdei_watchdog_hardlockup_disable(unsigned int cpu) +{ + int ret; + + if (!sdei_watchdog_registered) + return; + + ret = sdei_api_event_disable(sdei_watchdog_event_num); + if (ret) + pr_err("Disable NMI Watchdog failed on cpu%d\n", + smp_processor_id()); +} + +static int sdei_watchdog_callback(u32 event, + struct pt_regs *regs, void *arg) +{ + ktime_t delta, now = ktime_get_mono_fast_ns(); + + delta = now - __this_cpu_read(last_check_time); + __this_cpu_write(last_check_time, now); + + /* + * Set delta to 4/5 of the actual watchdog threshold period so the + * hrtimer is guaranteed to fire at least once within the real + * watchdog threshold. + */ + if (delta < watchdog_thresh * (u64)NSEC_PER_SEC * 4 / 5) { + pr_err(FW_BUG "SDEI Watchdog event triggered too soon, " + "time to last check:%lld ns\n", delta); + return 0; + } + + watchdog_hardlockup_check(smp_processor_id(), regs); + + return 0; +} +NOKPROBE_SYMBOL(sdei_watchdog_callback); + +static void sdei_nmi_watchdog_bind(void *data) +{ + int ret; + + ret = sdei_api_event_interrupt_bind(SDEI_NMI_WATCHDOG_HWIRQ); + if (ret < 0) + pr_err("SDEI bind failed on cpu%d, return %d\n", + smp_processor_id(), ret); +} + +static int __init disable_sdei_nmi_watchdog_setup(char *str) +{ + disable_sdei_nmi_watchdog = true; + return 1; +} +__setup("disable_sdei_nmi_watchdog", disable_sdei_nmi_watchdog_setup); + +void sdei_watchdog_clear_eoi(void) +{ + if (sdei_watchdog_registered) + sdei_api_clear_eoi(SDEI_NMI_WATCHDOG_HWIRQ); +} + +static int sdei_watchdog_pm_notifier(struct notifier_block *nb, + unsigned long action, void *data) +{ + int rv; + + WARN_ON_ONCE(preemptible()); + + switch (action) { + case CPU_PM_ENTER: + rv = sdei_api_event_disable(sdei_watchdog_event_num); + break; + case CPU_PM_EXIT: + rv = sdei_api_event_enable(sdei_watchdog_event_num); + break; + default: + return NOTIFY_DONE; + } + + if (rv) + return notifier_from_errno(rv); + + return NOTIFY_OK; +} + +static struct notifier_block sdei_watchdog_pm_nb = { + .notifier_call = sdei_watchdog_pm_notifier, +}; + +int __init sdei_watchdog_hardlockup_probe(void) +{ + int ret; + + if (disable_sdei_nmi_watchdog) + return -EINVAL; + + if (!is_hyp_mode_available()) { + pr_err("Disable SDEI NMI Watchdog in VM\n"); + return -EINVAL; + } + + sdei_watchdog_event_num = sdei_api_event_interrupt_bind(SDEI_NMI_WATCHDOG_HWIRQ); + if (sdei_watchdog_event_num < 0) { + pr_err("Bind interrupt failed. Firmware may not support SDEI !\n"); + return sdei_watchdog_event_num; + } + + /* + * After we introduced 'sdei_api_set_secure_timer_period', we disselect + * 'CONFIG_HARDLOCKUP_CHECK_TIMESTAMP'. So we need to make sure that + * firmware can set the period of the secure timer and the timer + * interrupt doesn't trigger too soon. + */ + if (sdei_api_set_secure_timer_period(watchdog_thresh)) { + pr_err("Firmware doesn't support setting the secure timer period, please update your BIOS !\n"); + return -EINVAL; + } + + on_each_cpu(sdei_nmi_watchdog_bind, NULL, true); + + ret = sdei_event_register(sdei_watchdog_event_num, + sdei_watchdog_callback, NULL); + if (ret) { + pr_err("SDEI Watchdog register callback failed\n"); + return ret; + } + + ret = cpu_pm_register_notifier(&sdei_watchdog_pm_nb); + if (ret) { + pr_warn("Failed to register sdei PM notifier...\n"); + return ret; + } + sdei_watchdog_registered = true; + pr_info("SDEI Watchdog registered successfully\n"); + + return 0; +} + +static int __init sdei_watchdog_hardlockup_init(void) +{ + /* sdei_watchdog needs to be initialized after sdei_init */ + if (!disable_sdei_nmi_watchdog) + lockup_detector_retry_init(); + + return 0; +} +device_initcall(sdei_watchdog_hardlockup_init) diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c index 071b04f1ee73092a0b04a1b05d21c3b8919325ff..c545a2fb3fbe05610d94e09368238bf0ff3e670e 100644 --- a/drivers/clocksource/arm_arch_timer.c +++ b/drivers/clocksource/arm_arch_timer.c @@ -29,6 +29,11 @@ #include #include +#ifdef CONFIG_HISILICON_ERRATUM_165010801 +#include +#include +#endif + #include #include @@ -92,6 +97,9 @@ static enum vdso_clock_mode vdso_default = VDSO_CLOCKMODE_ARCHTIMER; #else static enum vdso_clock_mode vdso_default = VDSO_CLOCKMODE_NONE; #endif /* CONFIG_GENERIC_GETTIMEOFDAY */ +#ifdef CONFIG_HISILICON_ERRATUM_165010801 +static bool prio_setup; +#endif static cpumask_t evtstrm_available = CPU_MASK_NONE; static bool evtstrm_enable __ro_after_init = IS_ENABLED(CONFIG_ARM_ARCH_TIMER_EVTSTREAM); @@ -355,6 +363,17 @@ static struct ate_acpi_oem_info hisi_161010101_oem_info[] = { }; #endif +#ifdef CONFIG_HISILICON_ERRATUM_165010801 +static struct ate_acpi_oem_info hisi_165010801_oem_info[] = { + { + .oem_id = "HISI ", + .oem_table_id = "HIP12 ", + .oem_revision = 0, + }, + { /* Sentinel indicating the end of the OEM array */ }, +}; +#endif + #ifdef CONFIG_ARM64_ERRATUM_858921 static u64 notrace arm64_858921_read_cntpct_el0(void) { @@ -485,6 +504,13 @@ static const struct arch_timer_erratum_workaround ool_workarounds[] = { .set_next_event_virt = erratum_set_next_event_virt, }, #endif +#ifdef CONFIG_HISILICON_ERRATUM_165010801 + { + .match_type = ate_match_acpi_oem_info, + .id = hisi_165010801_oem_info, + .desc = "HiSilicon erratum 165010801", + }, +#endif #ifdef CONFIG_ARM64_ERRATUM_858921 { .match_type = ate_match_local_cap_id, @@ -592,6 +618,12 @@ void arch_timer_enable_workaround(const struct arch_timer_erratum_workaround *wa if (wa->read_cntvct_el0 || wa->read_cntpct_el0) atomic_set(&timer_unstable_counter_workaround_in_use, 1); +#ifdef CONFIG_HISILICON_ERRATUM_165010801 + if (!strncmp(wa->desc, "HiSilicon erratum 165010801", + strlen("HiSilicon erratum 165010801"))) + prio_setup = true; +#endif + /* * Don't use the vdso fastpath if errata require using the * out-of-line counter accessor. We may change our mind pretty @@ -1007,6 +1039,18 @@ static int arch_timer_starting_cpu(unsigned int cpu) __arch_timer_setup(ARCH_TIMER_TYPE_CP15, clk); +#ifdef CONFIG_HISILICON_ERRATUM_165010801 + if (prio_setup && is_kernel_in_hyp_mode()) { + struct irq_data *d = irq_get_irq_data(arch_timer_ppi[arch_timer_uses_ppi]); + + if (!d) + pr_warn("WARNING: Invalid arch_timer ppi irq: %d!\n", + arch_timer_ppi[arch_timer_uses_ppi]); + else + gic_irq_set_prio(d, GICD_INT_DEF_PRI & (GICD_INT_DEF_PRI - 1)); + } +#endif + flags = check_ppi_trigger(arch_timer_ppi[arch_timer_uses_ppi]); enable_percpu_irq(arch_timer_ppi[arch_timer_uses_ppi], flags); diff --git a/drivers/firmware/arm_sdei.c b/drivers/firmware/arm_sdei.c index 71e2a9a89f6adabf2848f61bfdcfcbb908f2a732..5df85f08be071351a795e4a5239e060c2456d8ee 100644 --- a/drivers/firmware/arm_sdei.c +++ b/drivers/firmware/arm_sdei.c @@ -188,6 +188,28 @@ int sdei_api_event_context(u32 query, u64 *result) } NOKPROBE_SYMBOL(sdei_api_event_context); +int sdei_api_event_interrupt_bind(int hwirq) +{ + u64 event_number; + + invoke_sdei_fn(SDEI_1_0_FN_SDEI_INTERRUPT_BIND, hwirq, 0, 0, 0, 0, + &event_number); + + return (int)event_number; +} + +int sdei_api_clear_eoi(int hwirq) +{ + return invoke_sdei_fn(SDEI_1_0_FN_SDEI_CLEAR_EOI, hwirq, 0, 0, 0, 0, + NULL); +} + +int sdei_api_set_secure_timer_period(int sec) +{ + return invoke_sdei_fn(SDEI_1_0_FN_SET_SECURE_TIMER_PERIOD, sec, 0, 0, 0, + 0, NULL); +} + static int sdei_api_event_get_info(u32 event, u32 info, u64 *result) { return invoke_sdei_fn(SDEI_1_0_FN_SDEI_EVENT_GET_INFO, event, info, 0, @@ -379,7 +401,7 @@ static int sdei_platform_reset(void) return err; } -static int sdei_api_event_enable(u32 event_num) +int sdei_api_event_enable(u32 event_num) { return invoke_sdei_fn(SDEI_1_0_FN_SDEI_EVENT_ENABLE, event_num, 0, 0, 0, 0, NULL); @@ -426,7 +448,7 @@ int sdei_event_enable(u32 event_num) return err; } -static int sdei_api_event_disable(u32 event_num) +int sdei_api_event_disable(u32 event_num) { return invoke_sdei_fn(SDEI_1_0_FN_SDEI_EVENT_DISABLE, event_num, 0, 0, 0, 0, NULL); diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index 6c7943c516eb09f18d0a73a5e132fc71383b599f..49db1a00577163ae24e840af22acb627588fcf8b 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -504,7 +504,7 @@ static int gic_irq_get_irqchip_state(struct irq_data *d, return 0; } -static void gic_irq_set_prio(struct irq_data *d, u8 prio) +void gic_irq_set_prio(struct irq_data *d, u8 prio) { void __iomem *base = gic_dist_base(d); u32 offset, index; diff --git a/drivers/perf/arm_pmuv3.c b/drivers/perf/arm_pmuv3.c index 6c0efa5f2eca7f178b84dce625a5eb60e3457637..89b5ace0001ae25df5c23ddc1d3b46d889c0bb33 100644 --- a/drivers/perf/arm_pmuv3.c +++ b/drivers/perf/arm_pmuv3.c @@ -1364,7 +1364,7 @@ static int __init armv8_pmu_driver_init(void) else ret = arm_pmu_acpi_probe(armv8_pmuv3_pmu_init); - if (!ret) + if (!ret && disable_sdei_nmi_watchdog) lockup_detector_retry_init(); return ret; diff --git a/include/linux/arm_sdei.h b/include/linux/arm_sdei.h index f652a5028b5907ba914550d92009592c3a8f7838..bb8a436b8a9c9021d9d5a1e8e929c57b294aaa4d 100644 --- a/include/linux/arm_sdei.h +++ b/include/linux/arm_sdei.h @@ -36,6 +36,11 @@ int sdei_event_unregister(u32 event_num); int sdei_event_enable(u32 event_num); int sdei_event_disable(u32 event_num); +int sdei_api_event_interrupt_bind(int hwirq); +int sdei_api_event_disable(u32 event_num); +int sdei_api_event_enable(u32 event_num); +int sdei_api_clear_eoi(int hwirq); +int sdei_api_set_secure_timer_period(int sec); /* GHES register/unregister helpers */ int sdei_register_ghes(struct ghes *ghes, sdei_event_callback *normal_cb, diff --git a/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h index 728691365464c1585b23338184c95ba90f1b65c1..002b16d034d6b953d27d71cd9b0ee868f8f2c988 100644 --- a/include/linux/irqchip/arm-gic-v3.h +++ b/include/linux/irqchip/arm-gic-v3.h @@ -656,6 +656,9 @@ static inline bool gic_enable_sre(void) return !!(val & ICC_SRE_EL1_SRE); } +#ifdef CONFIG_HISILICON_ERRATUM_165010801 +extern void gic_irq_set_prio(struct irq_data *d, u8 prio); +#endif #endif #endif diff --git a/include/linux/nmi.h b/include/linux/nmi.h index e92e378df000fb1eca1e082ebc889fe7849a19d2..95805d31d28da9deaebcd92e69b86d3cbded163e 100644 --- a/include/linux/nmi.h +++ b/include/linux/nmi.h @@ -105,10 +105,12 @@ void watchdog_hardlockup_check(unsigned int cpu, struct pt_regs *regs); extern void hardlockup_detector_perf_stop(void); extern void hardlockup_detector_perf_restart(void); extern void hardlockup_detector_perf_cleanup(void); +extern void hardlockup_detector_perf_adjust_period(int cpu, u64 period); #else static inline void hardlockup_detector_perf_stop(void) { } static inline void hardlockup_detector_perf_restart(void) { } static inline void hardlockup_detector_perf_cleanup(void) { } +static inline void hardlockup_detector_perf_adjust_period(int cpu, u64 period) { } #endif void watchdog_hardlockup_stop(void); @@ -235,4 +237,18 @@ static inline void nmi_backtrace_stall_snap(const struct cpumask *btp) {} static inline void nmi_backtrace_stall_check(const struct cpumask *btp) {} #endif +#ifdef CONFIG_SDEI_WATCHDOG +void sdei_watchdog_hardlockup_enable(unsigned int cpu); +void sdei_watchdog_hardlockup_disable(unsigned int cpu); +void sdei_watchdog_clear_eoi(void); +int sdei_watchdog_hardlockup_probe(void); +extern bool disable_sdei_nmi_watchdog; +#else +static inline void sdei_watchdog_hardlockup_enable(unsigned int cpu) { } +static inline void sdei_watchdog_hardlockup_disable(unsigned int cpu) { } +static inline void sdei_watchdog_clear_eoi(void) { } +static inline int sdei_watchdog_hardlockup_probe(void) { return -ENODEV; } +#define disable_sdei_nmi_watchdog 1 +#endif + #endif diff --git a/include/uapi/linux/arm_sdei.h b/include/uapi/linux/arm_sdei.h index af0630ba5437d4f3f15f4ee0b4ca94843a813a9f..a5375679dd503dbef5acc176442ecd073773aef3 100644 --- a/include/uapi/linux/arm_sdei.h +++ b/include/uapi/linux/arm_sdei.h @@ -24,6 +24,8 @@ #define SDEI_1_0_FN_SDEI_INTERRUPT_RELEASE SDEI_1_0_FN(0x0E) #define SDEI_1_0_FN_SDEI_PRIVATE_RESET SDEI_1_0_FN(0x11) #define SDEI_1_0_FN_SDEI_SHARED_RESET SDEI_1_0_FN(0x12) +#define SDEI_1_0_FN_SDEI_CLEAR_EOI SDEI_1_0_FN(0x18) +#define SDEI_1_0_FN_SET_SECURE_TIMER_PERIOD SDEI_1_0_FN(0x19) #define SDEI_VERSION_MAJOR_SHIFT 48 #define SDEI_VERSION_MAJOR_MASK 0x7fff diff --git a/kernel/stop_machine.c b/kernel/stop_machine.c index cedb17ba158a9bf57f3510fbfc8141adbd12b74b..9466d61d21c981c9fa2558c5db44dc62faa9f63f 100644 --- a/kernel/stop_machine.c +++ b/kernel/stop_machine.c @@ -23,6 +23,10 @@ #include #include +#ifdef CONFIG_ARM64 +#include +#endif + /* * Structure to determine completion condition and record errors. May * be shared by works on different cpus. @@ -234,6 +238,9 @@ static int multi_cpu_stop(void *data) case MULTI_STOP_DISABLE_IRQ: local_irq_disable(); hard_irq_disable(); +#ifdef CONFIG_ARM64 + sdei_mask_local_cpu(); +#endif break; case MULTI_STOP_RUN: if (is_active) @@ -254,6 +261,9 @@ static int multi_cpu_stop(void *data) rcu_momentary_dyntick_idle(); } while (curstate != MULTI_STOP_EXIT); +#ifdef CONFIG_ARM64 + sdei_unmask_local_cpu(); +#endif local_irq_restore(flags); return err; } diff --git a/kernel/watchdog.c b/kernel/watchdog.c index 968cfa1fe8ebbda99ae8c7ae6e6b01f18512a479..2ac90f4b679200e3b55a7377980835b031969a63 100644 --- a/kernel/watchdog.c +++ b/kernel/watchdog.c @@ -26,6 +26,8 @@ #include #include +#include + #include #include #include @@ -139,6 +141,7 @@ static bool is_hardlockup(unsigned int cpu) return false; } +NOKPROBE_SYMBOL(is_hardlockup); static void watchdog_hardlockup_kick(void) { @@ -216,6 +219,7 @@ void watchdog_hardlockup_check(unsigned int cpu, struct pt_regs *regs) per_cpu(watchdog_hardlockup_warned, cpu) = false; } } +NOKPROBE_SYMBOL(watchdog_hardlockup_check); #else /* CONFIG_HARDLOCKUP_DETECTOR_COUNTS_HRTIMER */ @@ -816,8 +820,12 @@ static void watchdog_enable(unsigned int cpu) /* Initialize timestamp */ update_touch_ts(); /* Enable the hardlockup detector */ - if (watchdog_enabled & WATCHDOG_HARDLOCKUP_ENABLED) - watchdog_hardlockup_enable(cpu); + if (watchdog_enabled & WATCHDOG_HARDLOCKUP_ENABLED) { + if (disable_sdei_nmi_watchdog) + watchdog_hardlockup_enable(cpu); + else + sdei_watchdog_hardlockup_enable(cpu); + } } static void watchdog_disable(unsigned int cpu) @@ -831,7 +839,10 @@ static void watchdog_disable(unsigned int cpu) * delay between disabling the timer and disabling the hardlockup * detector causes a false positive. */ - watchdog_hardlockup_disable(cpu); + if (disable_sdei_nmi_watchdog) + watchdog_hardlockup_disable(cpu); + else + sdei_watchdog_hardlockup_disable(cpu); hrtimer_cancel(hrtimer); wait_for_completion(this_cpu_ptr(&softlockup_completion)); } @@ -1245,7 +1256,12 @@ static void __init lockup_detector_delay_init(struct work_struct *work) { int ret; - ret = watchdog_hardlockup_probe(); + if (disable_sdei_nmi_watchdog) { + ret = watchdog_hardlockup_probe(); + } else { + ret = sdei_watchdog_hardlockup_probe(); + } + if (ret) { if (ret == -ENODEV) pr_info("NMI not fully supported\n"); @@ -1304,7 +1320,7 @@ void __init lockup_detector_init(void) cpumask_copy(&watchdog_cpumask, housekeeping_cpumask(HK_TYPE_TIMER)); - if (!watchdog_hardlockup_probe()) + if (disable_sdei_nmi_watchdog && !watchdog_hardlockup_probe()) watchdog_hardlockup_available = true; else allow_lockup_detector_init_retry = true; diff --git a/kernel/watchdog_perf.c b/kernel/watchdog_perf.c index 0052afe18b7fc5c4177f42ace359dee05fd63139..989d30f80951e5abf919acbde8789e4d48b1b476 100644 --- a/kernel/watchdog_perf.c +++ b/kernel/watchdog_perf.c @@ -198,6 +198,29 @@ void hardlockup_detector_perf_cleanup(void) cpumask_clear(&dead_events_mask); } +/** + * hardlockup_detector_perf_adjust_period - Adjust the event period due + * to cpu frequency change + * @cpu: The CPU whose event period will be adjusted + * @period: The target period to be set + */ +void hardlockup_detector_perf_adjust_period(int cpu, u64 period) +{ + struct perf_event *event = per_cpu(watchdog_ev, cpu); + + if (!(watchdog_enabled & WATCHDOG_HARDLOCKUP_ENABLED)) + return; + + if (!event) + return; + + if (event->attr.sample_period == period) + return; + + if (perf_event_period(event, period)) + pr_err("failed to change period to %llu\n", period); +} + /** * hardlockup_detector_perf_stop - Globally stop watchdog events * diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index b10bf8218ad136a73061c049ac51854d569aaa1a..b1f7a22a53522256eeda73fd3eb9c58a6a8ce671 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -1060,6 +1060,12 @@ config HAVE_HARDLOCKUP_DETECTOR_BUDDY depends on SMP default y +config SDEI_WATCHDOG + bool "SDEI NMI Watchdog support" + depends on ARM_SDE_INTERFACE + depends on HARDLOCKUP_DETECTOR + select HARDLOCKUP_DETECTOR_COUNTS_HRTIMER + # # Global switch whether to build a hardlockup detector at all. It is available # only when the architecture supports at least one implementation. There are @@ -1076,6 +1082,7 @@ config HARDLOCKUP_DETECTOR depends on HAVE_HARDLOCKUP_DETECTOR_PERF || HAVE_HARDLOCKUP_DETECTOR_BUDDY || HAVE_HARDLOCKUP_DETECTOR_ARCH imply HARDLOCKUP_DETECTOR_PERF imply HARDLOCKUP_DETECTOR_BUDDY + imply SDEI_WATCHDOG imply HARDLOCKUP_DETECTOR_ARCH select LOCKUP_DETECTOR