From 3b8e1fa8df9a5507083805978103d639e2675126 Mon Sep 17 00:00:00 2001 From: Carles Cufi Date: Wed, 4 Jun 2025 13:23:26 +0200 Subject: [PATCH] doc: kernel: Clarify relationship between direct and ZLI interrupts The documentation did not state clearly that any zero-latency IRQ must also be declared as a direct ISR. This is critical because failure to do so may cause race conditions between the ZLI and regular ISRs when executing the preable/postamble code in regular interrupts. Signed-off-by: Carles Cufi --- doc/kernel/services/interrupts.rst | 36 +++++++++++++++++++++--------- include/zephyr/arch/arm/irq.h | 6 +++++ include/zephyr/irq.h | 12 ++++++++++ 3 files changed, 44 insertions(+), 10 deletions(-) diff --git a/doc/kernel/services/interrupts.rst b/doc/kernel/services/interrupts.rst index c424e53a61b..a208e46a167 100644 --- a/doc/kernel/services/interrupts.rst +++ b/doc/kernel/services/interrupts.rst @@ -162,6 +162,8 @@ The IRQ must be subsequently **enabled** to permit the ISR to execute. Disabling an IRQ prevents *all* threads in the system from being preempted by the associated ISR, not just the thread that disabled the IRQ. +.. _zlis: + Zero Latency Interrupts ----------------------- @@ -173,10 +175,14 @@ The kernel addresses such use-cases by allowing interrupts with critical latency constraints to execute at a priority level that cannot be blocked by interrupt locking. These interrupts are defined as *zero-latency interrupts*. The support for zero-latency interrupts requires -:kconfig:option:`CONFIG_ZERO_LATENCY_IRQS` to be enabled. In addition to that, the -flag :c:macro:`IRQ_ZERO_LATENCY` must be passed to :c:macro:`IRQ_CONNECT` or -:c:macro:`IRQ_DIRECT_CONNECT` macros to configure the particular interrupt -with zero latency. +:kconfig:option:`CONFIG_ZERO_LATENCY_IRQS` to be enabled. Any interrupts +configured as zero-latency must also be declared as :ref:`direct ISRs +` (and must not use the :c:macro:`ISR_DIRECT_PM` in them), since +regular ISRs interact with the kernel. In addition to that, the flag +:c:macro:`IRQ_ZERO_LATENCY` must be passed to the :c:macro:`IRQ_DIRECT_CONNECT` +macro to configure the particular interrupt with +zero latency. Declaring a zero-latency interrupt ISR to be both direct and +dynamic is possible on some architectures, see :ref:`direct_isrs`. Zero-latency interrupts are expected to be used to manage hardware events directly, and not to interoperate with the kernel code at all. They should @@ -306,6 +312,8 @@ Dynamic interrupts require the :kconfig:option:`CONFIG_DYNAMIC_INTERRUPTS` optio be enabled. Removing or re-configuring a dynamic interrupt is currently unsupported. +.. _direct_isrs: + Defining a 'direct' ISR ======================= @@ -322,7 +330,10 @@ for some low-latency use-cases. Specifically: need to switch to the interrupt stack in code * After the interrupt is serviced, the OS then performs some logic to - potentially make a scheduling decision. + potentially make a scheduling decision + +* :ref:`zlis` must always be declared as direct ISRs, since regular + ISRs interact with the kernel Zephyr supports so-called 'direct' interrupts, which are installed via :c:macro:`IRQ_DIRECT_CONNECT` and whose handlers are declared using @@ -341,8 +352,12 @@ The following code demonstrates a direct ISR: ISR_DIRECT_DECLARE(my_isr) { do_stuff(); - ISR_DIRECT_PM(); /* PM done after servicing interrupt for best latency */ - return 1; /* We should check if scheduling decision should be made */ + /* PM done after servicing interrupt for best latency. This cannot be + used for zero-latency IRQs because it accesses kernel data. */ + ISR_DIRECT_PM(); + /* Ask the kernel to check if scheduling decision should be made. If the + ISR is for a zero-latency IRQ then the return value must always be 0. */ + return 1; } void my_isr_installer(void) @@ -354,9 +369,10 @@ The following code demonstrates a direct ISR: } Installation of dynamic direct interrupts is supported on an -architecture-specific basis. (The feature is currently implemented in -ARM Cortex-M architecture variant. Dynamic direct interrupts feature is -exposed to the user via an ARM-only API.) +architecture-specific basis. The feature is currently implemented in the Arm +Cortex-M architecture variant via the macro +:c:macro:`ARM_IRQ_DIRECT_DYNAMIC_CONNECT`, which can be used to declare a direct +and dynamic interrupt. Sharing an interrupt line ========================= diff --git a/include/zephyr/arch/arm/irq.h b/include/zephyr/arch/arm/irq.h index f4a07e3f4eb..783a7d96fca 100644 --- a/include/zephyr/arch/arm/irq.h +++ b/include/zephyr/arch/arm/irq.h @@ -231,12 +231,18 @@ extern void z_arm_irq_direct_dynamic_dispatch_no_reschedule(void); * direct interrupts, the decisions must be made at build time. * They are controlled by @param resch to this macro. * + * @warning + * Just like with regular direct ISRs, any ISRs that serve IRQs configured with + * the IRQ_ZERO_LATENCY flag must not use the ISR_DIRECT_PM() macro and must + * return 0 (i.e. resch must be no_reschedule). + * * @param irq_p IRQ line number. * @param priority_p Interrupt priority. * @param flags_p Architecture-specific IRQ configuration flags. * @param resch Set flag to 'reschedule' to request thread * re-scheduling upon ISR function. Set flag * 'no_reschedule' to skip thread re-scheduling + * Must be 'no_reschedule' for zero-latency interrupts * * Note: the function is an ARM Cortex-M only API. * diff --git a/include/zephyr/irq.h b/include/zephyr/irq.h index 4810a27b3e0..7cbff4d581c 100644 --- a/include/zephyr/irq.h +++ b/include/zephyr/irq.h @@ -128,6 +128,10 @@ irq_disconnect_dynamic(unsigned int irq, unsigned int priority, * Although this routine is invoked at run-time, all of its arguments must be * computable by the compiler at build time. * + * @note + * All IRQs configured with the IRQ_ZERO_LATENCY flag must be declared as + * direct. + * * @param irq_p IRQ line number. * @param priority_p Interrupt priority. * @param isr_p Address of interrupt service routine. @@ -170,6 +174,10 @@ irq_disconnect_dynamic(unsigned int irq, unsigned int priority, * and IRQ_DIRECT_FOOTER() invocations. It performs tasks necessary to * exit power management idle state. It takes no parameters and returns no * arguments. It may be omitted, but be careful! + * + * @warning + * This macro must not be used at all with IRQs configured with the + * IRQ_ZERO_LATENCY flag. */ #define ISR_DIRECT_PM() ARCH_ISR_DIRECT_PM() @@ -186,6 +194,10 @@ irq_disconnect_dynamic(unsigned int irq, unsigned int priority, * registers by the ISR, this will always generate code for the 'fast' * interrupt type. * + * @warning + * Any ISRs that serve IRQs configured with the IRQ_ZERO_LATENCY flag must + * always return 0 in this macro. + * * Example usage: * * ISR_DIRECT_DECLARE(my_isr)