Improve interrupt handling during machine kexec
Subsystems (5)
v5 cover Improve interrupt handling during machine kexec
This patch series focuses on improving the machine_kexec_mask_interrupts() function by consolidating its implementation and optimizing its behavior to avoid redundant interrupt masking.
Patch Summary:
[PATCH v5 1/2] Move machine_kexec_mask_interrupts() to kernel/irq/kexec.c,
removing duplicate architecture-specific implementations.
[PATCH v5 2/2] Refine machine_kexec_mask_interrupts() to avoid re-masking
already-masked interrupts, resolving specific warnings triggered in GPIO IRQ flows.
Changes between v4 and v5:
- The function machine_kexec_mask_interrupts() has been moved from kernel/kexec_core.c to a new file kernel/irq/kexec.c.
- A new configuration option, GENERIC_IRQ_KEXEC_CLEAR_VM_FORWARD, has been added.
- The parameters for the machine_kexec_mask_interrupts() function have been defined in reverse Christmas Tree style.
- The comment explaining the call to irq_set_irqchip_state() has been improved for clarity.
- The phrase 'This patch' has been removed from the commit message.
Changes between v3 and v4:
- Add missing <linux/irqdesc.h> and <linux/irq.h> includes.
Eliav Farber (2):
kexec: Consolidate machine_kexec_mask_interrupts() implementation
kexec: Prevent redundant IRQ masking by checking state before shutdown
arch/arm/kernel/machine_kexec.c | 23 -------------------- arch/arm64/Kconfig | 1 + arch/arm64/kernel/machine_kexec.c | 31 -------------------------- arch/powerpc/include/asm/kexec.h | 1 - arch/powerpc/kexec/core.c | 22 ------------------- arch/powerpc/kexec/core_32.c | 1 + arch/riscv/kernel/machine_kexec.c | 23 -------------------- include/linux/irq.h | 3 +++ kernel/irq/Kconfig | 9 ++++++++ kernel/irq/Makefile | 2 +- kernel/irq/kexec.c | 36 +++++++++++++++++++++++++++++++ 11 files changed, 51 insertions(+), 101 deletions(-) create mode 100644 kernel/irq/kexec.c
Signature · 1 line
2.40.1
PATCH v5 1/2 kexec: Consolidate machine_kexec_mask_interrupts() implementation
Consolidate the machine_kexec_mask_interrupts implementation into a common function located in a new file: kernel/irq/kexec.c. This removes duplicate implementations from architecture-specific files in arch/arm, arch/arm64, arch/powerpc, and arch/riscv, reducing code duplication and improving maintainability.
The new implementation retains architecture-specific behavior for CONFIG_GENERIC_IRQ_KEXEC_CLEAR_VM_FORWARD, which was previously implemented for ARM64. When enabled (currently for ARM64), it clears the active state of interrupts forwarded to virtual machines (VMs) before handling other interrupt masking operations.
---
V4 -> V5:
- The function machine_kexec_mask_interrupts() has been moved from kernel/kexec_core.c to a new file kernel/irq/kexec.c.
- A new configuration option, GENERIC_IRQ_KEXEC_CLEAR_VM_FORWARD, has been added.
- The parameters for the machine_kexec_mask_interrupts() function have been defined in reverse Christmas Tree style.
- The comment explaining the call to irq_set_irqchip_state() has been improved for clarity.
arch/arm/kernel/machine_kexec.c | 23 ------------------ arch/arm64/Kconfig | 1 + arch/arm64/kernel/machine_kexec.c | 31 ------------------------ arch/powerpc/include/asm/kexec.h | 1 - arch/powerpc/kexec/core.c | 22 ----------------- arch/powerpc/kexec/core_32.c | 1 + arch/riscv/kernel/machine_kexec.c | 23 ------------------ include/linux/irq.h | 3 +++ kernel/irq/Kconfig | 9 +++++++ kernel/irq/Makefile | 2 +- kernel/irq/kexec.c | 40 +++++++++++++++++++++++++++++++ 11 files changed, 55 insertions(+), 101 deletions(-) create mode 100644 kernel/irq/kexec.c
arch/arm/kernel/machine_kexec.c +0 -23
@@ -127,29 +127,6 @@ void crash_smp_send_stop(void) cpus_stopped =1 ; } -static void machine_kexec_mask_interrupts (void ) -{ -unsigned int i; -struct irq_desc *desc; - -for_each_irq_desc (i, desc) { -struct irq_chip *chip; - - chip =irq_desc_get_chip (desc); -if (!chip) -continue ; - -if (chip->irq_eoi &&irqd_irq_inprogress (&desc->irq_data)) - chip->irq_eoi (&desc->irq_data); - -if (chip->irq_mask) - chip->irq_mask (&desc->irq_data); - -if (chip->irq_disable && !irqd_irq_disabled (&desc->irq_data)) - chip->irq_disable (&desc->irq_data); - } -} -void machine_crash_shutdown (struct pt_regs *regs) {local_irq_disable ();
arch/arm64/Kconfig +1 -0
@@ -150,6 +150,7 @@ config ARM64 select GENERIC_IDLE_POLL_SETUP select GENERIC_IOREMAP select GENERIC_IRQ_IPI + select GENERIC_IRQ_KEXEC_CLEAR_VM_FORWARD select GENERIC_IRQ_PROBE select GENERIC_IRQ_SHOW select GENERIC_IRQ_SHOW_LEVEL
arch/arm64/kernel/machine_kexec.c +0 -31
@@ -207,37 +207,6 @@ void machine_kexec(struct kimage *kimage)BUG ();/* Should never get here. */ } -static void machine_kexec_mask_interrupts (void ) -{ -unsigned int i; -struct irq_desc *desc; - -for_each_irq_desc (i, desc) { -struct irq_chip *chip; -int ret; - - chip =irq_desc_get_chip (desc); -if (!chip) -continue ; - -/* -* First try to remove the active state. If this -* fails, try to EOI the interrupt. -*/ - ret =irq_set_irqchip_state (i,IRQCHIP_STATE_ACTIVE , false); - -if (ret &&irqd_irq_inprogress (&desc->irq_data) && - chip->irq_eoi) - chip->irq_eoi (&desc->irq_data); - -if (chip->irq_mask) - chip->irq_mask (&desc->irq_data); - -if (chip->irq_disable && !irqd_irq_disabled (&desc->irq_data)) - chip->irq_disable (&desc->irq_data); - } -} -/** * machine_crash_shutdown - shutdown non-crashing cpus and save registers */
arch/powerpc/include/asm/kexec.h +0 -1
@@ -61,7 +61,6 @@ struct pt_regs;extern void kexec_smp_wait (void );/* get and clear naca physid, wait for master to copy new code to 0 */ extern void default_machine_kexec (struct kimage *image); -extern void machine_kexec_mask_interrupts (void );void relocate_new_kernel (unsigned long indirection_page,unsigned long reboot_code_buffer,unsigned long start_address)__noreturn ;
arch/powerpc/kexec/core.c +0 -22
@@ -22,28 +22,6 @@#include <asm/setup.h> #include <asm/firmware.h> -void machine_kexec_mask_interrupts (void ) { -unsigned int i; -struct irq_desc *desc; - -for_each_irq_desc (i, desc) { -struct irq_chip *chip; - - chip =irq_desc_get_chip (desc); -if (!chip) -continue ; - -if (chip->irq_eoi &&irqd_irq_inprogress (&desc->irq_data)) - chip->irq_eoi (&desc->irq_data); - -if (chip->irq_mask) - chip->irq_mask (&desc->irq_data); - -if (chip->irq_disable && !irqd_irq_disabled (&desc->irq_data)) - chip->irq_disable (&desc->irq_data); - } -} -#ifdef CONFIG_CRASH_DUMP void machine_crash_shutdown (struct pt_regs *regs) {
arch/powerpc/kexec/core_32.c +1 -0
@@ -7,6 +7,7 @@* Copyright (C) 2005 IBM Corporation. */ +#include <linux/irq.h> #include <linux/kexec.h> #include <linux/mm.h> #include <linux/string.h>
arch/riscv/kernel/machine_kexec.c +0 -23
@@ -114,29 +114,6 @@ void machine_shutdown(void)#endif } -static void machine_kexec_mask_interrupts (void ) -{ -unsigned int i; -struct irq_desc *desc; - -for_each_irq_desc (i, desc) { -struct irq_chip *chip; - - chip =irq_desc_get_chip (desc); -if (!chip) -continue ; - -if (chip->irq_eoi &&irqd_irq_inprogress (&desc->irq_data)) - chip->irq_eoi (&desc->irq_data); - -if (chip->irq_mask) - chip->irq_mask (&desc->irq_data); - -if (chip->irq_disable && !irqd_irq_disabled (&desc->irq_data)) - chip->irq_disable (&desc->irq_data); - } -} -/* * machine_crash_shutdown - Prepare to kexec after a kernel crash *
include/linux/irq.h +3 -0
@@ -694,6 +694,9 @@ extern int irq_chip_request_resources_parent(struct irq_data *data);extern void irq_chip_release_resources_parent (struct irq_data *data);#endif +/* Disable or mask interrupts during a kernel kexec */ +extern void machine_kexec_mask_interrupts (void ); +/* Handling of unhandled and spurious interrupts: */ extern void note_interrupt (struct irq_desc *desc,irqreturn_t action_ret);
kernel/irq/Kconfig +9 -0
@@ -154,3 +154,12 @@ config DEPRECATED_IRQ_CPU_ONOFFLINE bool depends on CAVIUM_OCTEON_SOC default CAVIUM_OCTEON_SOC + +config GENERIC_IRQ_KEXEC_CLEAR_VM_FORWARD + bool "Clear forwarded VM interrupts during kexec" + default n + help + When enabled, this option allows the kernel to clear the active state + of interrupts that are forwarded to virtual machines (VMs) during a + machine kexec. For interrupts that are not forwarded, if supported, + the kernel will attempt to trigger an End of Interrupt (EOI).
kernel/irq/Makefile +1 -1
@@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 -obj-y := irqdesc.o handle.o manage.o spurious.o resend.o chip.o dummychip.o devres.o +obj-y := irqdesc.o handle.o manage.o spurious.o resend.o chip.o dummychip.o devres.o kexec.o obj-$(CONFIG_IRQ_TIMINGS) += timings.o ifeq ($(CONFIG_TEST_IRQ_TIMINGS),y) CFLAGS_timings.o += -DDEBUG
kernel/irq/kexec.c new file +40 -0
@@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/irqdesc.h> +#include <linux/irqnr.h> + +#include "internals.h" + +void machine_kexec_mask_interrupts (void ) +{ +struct irq_desc *desc; +unsigned int i; + +for_each_irq_desc (i, desc) { +struct irq_chip *chip; +int check_eoi =1 ; + + chip =irq_desc_get_chip (desc); +if (!chip) +continue ; + +if (IS_ENABLED (CONFIG_GENERIC_IRQ_KEXEC_CLEAR_VM_FORWARD )) { +/* +* First try to remove the active state from an interrupt which is forwarded +* to a VM. If the interrupt is not forwarded, try to EOI the interrupt. +*/ + check_eoi =irq_set_irqchip_state (i,IRQCHIP_STATE_ACTIVE , false); + } + +if (check_eoi && chip->irq_eoi &&irqd_irq_inprogress (&desc->irq_data)) + chip->irq_eoi (&desc->irq_data); + +if (chip->irq_mask) + chip->irq_mask (&desc->irq_data); + +if (chip->irq_disable && !irqd_irq_disabled (&desc->irq_data)) + chip->irq_disable (&desc->irq_data); + } +}
Signature · 1 line
2.40.1
Re: [PATCH v5 1/2] kexec: Consolidate machine_kexec_mask_interrupts() implementation
kernel/irq/Kconfig config DEPRECATED_IRQ_CPU_ONOFFLINE · line 159
Quoted 3 lines · Eliav Farber
kernel/irq/Kconfig +3
-0+ +config GENERIC_IRQ_KEXEC_CLEAR_VM_FORWARD + bool "Clear forwarded VM interrupts during kexec"
This should not be user selectable. Just keep it as:
config GENERIC_IRQ_KEXEC_CLEAR_VM_FORWARD
bool
which defaults to 'n'. Just add a comment what this is about like it's done with the other options in that file which are only selectable.
Thanks,
tglx
Re: [PATCH v5 1/2] kexec: Consolidate machine_kexec_mask_interrupts() implementation
In reply to Eliav FarberQuoted 11 lines · Eliav Farber
Consolidate the
machine_kexec_mask_interruptsimplementation into a common function located in a new file:kernel/irq/kexec.c. This removes duplicate implementations from architecture-specific files inarch/arm,arch/arm64,arch/powerpc, andarch/riscv, reducing code duplication and improving maintainability.The new implementation retains architecture-specific behavior for
CONFIG_GENERIC_IRQ_KEXEC_CLEAR_VM_FORWARD, which was previously implemented for ARM64. When enabled (currently for ARM64), it clears the active state of interrupts forwarded to virtual machines (VMs) before handling other interrupt masking operations.
...
Quoted 5 lines
kernel/irq/Kconfig +0
-0@@ -154,3 +154,12 @@ config DEPRECATED_IRQ_CPU_ONOFFLINE bool depends on CAVIUM_OCTEON_SOC
kernel/irq/Kconfig config DEPRECATED_IRQ_CPU_ONOFFLINE · line 165
Quoted 10 lines
kernel/irq/Kconfig +9
-0default CAVIUM_OCTEON_SOC + +config GENERIC_IRQ_KEXEC_CLEAR_VM_FORWARD + bool "Clear forwarded VM interrupts during kexec" + default n + help + When enabled, this option allows the kernel to clear the active state + of interrupts that are forwarded to virtual machines (VMs) during a + machine kexec. For interrupts that are not forwarded, if supported, + the kernel will attempt to trigger an End of Interrupt (EOI).
This caught my attention. It looks like you want to allow people toggling it? I believe only arch code should turn it on as you do by "select", not users.
thanks,
Signature · 2 lines
js suse labs
PATCH v5 2/2 kexec: Prevent redundant IRQ masking by checking state before shutdown
In reply to Eliav FarberDuring machine kexec, the function machine_kexec_mask_interrupts() is responsible for disabling or masking all interrupts. While the irq_disable hook ensures that an already-disabled IRQ is not disabled again, the current implementation unconditionally invokes the irq_mask() function for every interrupt descriptor, even when the interrupt is already masked.
A specific issue was observed in the crash kernel flow after unbinding a device (prior to kexec) that used a GPIO as an IRQ source. The warning was triggered by the gpiochip_disable_irq() function, which attempted to clear the FLAG_IRQ_IS_ENABLED flag when FLAG_USED_AS_IRQ was not set:
```
void gpiochip_disable_irq(struct gpio_chip *gc, unsigned int offset)
{
struct gpio_desc *desc = gpiochip_get_desc(gc, offset);
if (!IS_ERR(desc) &&
!WARN_ON(!test_bit(FLAG_USED_AS_IRQ, &desc->flags)))
clear_bit(FLAG_IRQ_IS_ENABLED, &desc->flags);
}
```This issue surfaced after commit a8173820f441 ("gpio: gpiolib: Allow GPIO IRQs to lazy disable") introduced lazy disablement for GPIO IRQs. It replaced disable/enable hooks with mask/unmask hooks. Unlike the disable hook, the mask hook doesn't handle already-masked IRQs.
When a GPIO-IRQ driver is unbound, the IRQ is released, triggering __irq_disable() and irq_state_set_masked(). A subsequent call to machine_kexec_mask_interrupts() re-invokes chip->irq_mask(). This results in a call chain, including gpiochip_irq_mask() and gpiochip_disable_irq(). Since FLAG_USED_AS_IRQ was cleared earlier, a warning occurs.
Replace calls to irq_mask() and irq_disable() hooks with a simplified call to irq_shutdown(), and check if the interrupt is started (irqd_is_started) before calling the shutdown.
---
V4 -> V5:
- The phrase 'This patch' has been removed from the commit message.
kernel/irq/kexec.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-)
kernel/irq/kexec.c +2 -6
@@ -17,7 +17,7 @@ void machine_kexec_mask_interrupts(void)int check_eoi =1 ; chip =irq_desc_get_chip (desc); -if (!chip) +if (!chip || !irqd_is_started (&desc->irq_data))continue ;if (IS_ENABLED (CONFIG_GENERIC_IRQ_KEXEC_CLEAR_VM_FORWARD )) {
@@ -31,10 +31,6 @@ void machine_kexec_mask_interrupts(void)if (check_eoi && chip->irq_eoi &&irqd_irq_inprogress (&desc->irq_data)) chip->irq_eoi (&desc->irq_data); -if (chip->irq_mask) - chip->irq_mask (&desc->irq_data); - -if (chip->irq_disable && !irqd_irq_disabled (&desc->irq_data)) - chip->irq_disable (&desc->irq_data); +irq_shutdown (desc); } }
Signature · 1 line
2.40.1