From: Boqun Feng <boqun@kernel.org>
To: Thomas Gleixner <tglx@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>,
linux-kernel@vger.kernel.org, linux-tip-commits@vger.kernel.org,
x86@kernel.org
Subject: Re: [PATCH] locking: Revert switching guards to _irq_{disable,enable}()
Date: Fri, 28 Aug 2026 17:45:53 -0700 [thread overview]
Message-ID: <apIrwRYJbPpmjrNo@tardis.local> (raw)
In-Reply-To: <87mru5et6r.ffs@fw13>
On Sat, Aug 29, 2026 at 01:11:56AM +0200, Thomas Gleixner wrote:
> On Fri, Aug 28 2026 at 00:52, Thomas Gleixner wrote:
> > On Thu, Aug 27 2026 at 12:41, Boqun Feng wrote:
> > So I sat down and reverted
> >
> > 1b0866874833 ("locking: Switch to _irq_{disable,enable}() variants in cleanup guards")
> > e901c1510e24 ("irq,spin_lock: Add counted interrupt disabling/enabling")
> >
> > and then hacked it up just to see how far I get before vanishing to bed.
> >
> > Three hours later it surprisingly booted right away into a full distro
> > kernel and survived kernel builds and a few test cases. :)
>
> /FACMEPALM
>
> Yesterday night I was really surprised but too tired to think about it.
>
> When I came around today to look at it again I was more than embarrassed
> to figure out that the KVM script rebuilt the wrong branch over and
> over. So the build numbers kept increasing...
>
> Brown paperbag time ...
>
> Of course the real thing did _NOT_ boot at all, so I sat down and
> figured out what's going wrong and added a pile of debug to it, which is
> sadly non-existing in this magic local_interrupt_dis/enable() code.
>
> The overall fallout is moderate. Some of it are actual (but harmless)
> bugs and the rest are the oddball cases we talked about before.
>
> It builds and boots now for real, but of course your mileage will vary
> depending on hardware and .config. Combo patch on top of the reverts is
> below.
>
> The whole pile can be retrieved from git via:
>
> git://git.kernel.org/pub/scm/linux/kernel/git/tglx/devel.git irqflags
>
> I have some thoughts about how to deal with the overall disaster, but
> that has to wait until my brain is truly awake again...
>
> Thanks,
>
> tglx
> ---
> diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
> index 15fd9ec5ecac..c7229a7cfa7c 100644
> --- a/arch/x86/Kconfig
> +++ b/arch/x86/Kconfig
> @@ -133,6 +133,7 @@ config X86
> select ARCH_USES_CFI_TRAPS if X86_64 && CFI
> select ARCH_SUPPORTS_LTO_CLANG
> select ARCH_SUPPORTS_LTO_CLANG_THIN
> + select ARCH_SUPPORTS_PREEMPT_COUNT_IRQFLAGS
> select ARCH_SUPPORTS_RT
> select ARCH_USE_BUILTIN_BSWAP
> select ARCH_USE_CMPXCHG_LOCKREF
> diff --git a/arch/x86/include/asm/hardirq.h b/arch/x86/include/asm/hardirq.h
> index dea60d66d976..34bdf24b939b 100644
> --- a/arch/x86/include/asm/hardirq.h
> +++ b/arch/x86/include/asm/hardirq.h
> @@ -113,4 +113,6 @@ static __always_inline bool kvm_get_cpu_l1tf_flush_l1d(void)
> static __always_inline void kvm_set_cpu_l1tf_flush_l1d(void) { }
> #endif /* IS_ENABLED(CONFIG_KVM_INTEL) */
>
> +#define __ARCH_IRQ_EXIT_IRQS_DISABLED 1
> +
> #endif /* _ASM_X86_HARDIRQ_H */
> diff --git a/arch/x86/include/asm/preempt.h b/arch/x86/include/asm/preempt.h
> index fafb6f8cdac3..8b4d4cbae52e 100644
> --- a/arch/x86/include/asm/preempt.h
> +++ b/arch/x86/include/asm/preempt.h
> @@ -61,10 +61,20 @@ static __always_inline void preempt_count_set(unsigned long pc)
> */
> #define init_task_preempt_count(p) do { } while (0)
>
> -#define init_idle_preempt_count(p, cpu) do { \
> - per_cpu(__preempt_count, (cpu)) = PREEMPT_DISABLED; \
> +#ifdef CONFIG_PREEMPT_COUNT_IRQFLAGS
> +
> +#define init_idle_preempt_count(p, cpu) do { \
> + per_cpu(__preempt_count, (cpu)) = PREEMPT_DISABLED | HARDIRQ_DISABLE_OFFSET; \
> } while (0)
>
> +#else
> +
> +#define init_idle_preempt_count(p, cpu) do { \
> + per_cpu(__preempt_count, (cpu)) = PREEMPT_DISABLED; \
> +} while (0)
> +
> +#endif
> +
> /*
> * We fold the NEED_RESCHED bit into the preempt count such that
> * preempt_enable() can decrement and test for needing to reschedule with a
> diff --git a/arch/x86/kernel/kvm.c b/arch/x86/kernel/kvm.c
> index 6b0a5861ccb8..0b5a05cb543f 100644
> --- a/arch/x86/kernel/kvm.c
> +++ b/arch/x86/kernel/kvm.c
> @@ -256,9 +256,9 @@ noinstr u32 kvm_read_and_reset_apf_flags(void)
> {
> u32 flags = 0;
>
> - if (__this_cpu_read(async_pf_enabled)) {
> - flags = __this_cpu_read(apf_reason.flags);
> - __this_cpu_write(apf_reason.flags, 0);
> + if (raw_cpu_read(async_pf_enabled)) {
> + flags = raw_cpu_read(apf_reason.flags);
> + raw_cpu_write(apf_reason.flags, 0);
> }
>
> return flags;
> diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c
> index 346c438ac880..b0be24a386f3 100644
> --- a/arch/x86/kernel/process.c
> +++ b/arch/x86/kernel/process.c
> @@ -824,7 +824,7 @@ void __noreturn stop_this_cpu(void *dummy)
> struct cpuinfo_x86 *c = this_cpu_ptr(&cpu_info);
> unsigned int cpu = smp_processor_id();
>
> - local_irq_disable();
> + raw_force_local_irq_disable();
>
> /*
> * Remove this CPU from the online mask and disable it
> diff --git a/arch/x86/kernel/reboot.c b/arch/x86/kernel/reboot.c
> index 0fed6d0d7e32..40104170be24 100644
> --- a/arch/x86/kernel/reboot.c
> +++ b/arch/x86/kernel/reboot.c
> @@ -98,7 +98,7 @@ static int __init set_efi_reboot(const struct dmi_system_id *d)
>
> void __noreturn machine_real_restart(unsigned int type)
> {
> - local_irq_disable();
> + raw_force_local_irq_disable();
>
> /*
> * Write zero to CMOS register number 0x0f, which the BIOS POST
> @@ -535,7 +535,7 @@ static inline void nmi_shootdown_cpus_on_restart(void);
> #if IS_ENABLED(CONFIG_KVM_X86)
> static void emergency_reboot_disable_virtualization(void)
> {
> - local_irq_disable();
> + raw_force_local_irq_disable();
>
> /*
> * Disable virtualization on all CPUs before rebooting to avoid hanging
> @@ -699,7 +699,7 @@ void native_machine_shutdown(void)
> * not receive the per-cpu timer interrupt which may trigger
> * scheduler's load balance.
> */
> - local_irq_disable();
> + raw_force_local_irq_disable();
> stop_other_cpus();
> #endif
>
> @@ -823,7 +823,8 @@ static int crash_nmi_callback(unsigned int val, struct pt_regs *regs)
> */
> if (cpu == crashing_cpu)
> return NMI_HANDLED;
> - local_irq_disable();
> +
> + raw_force_local_irq_disable();
>
> if (shootdown_callback)
> shootdown_callback(cpu, regs);
> @@ -865,7 +866,7 @@ void nmi_shootdown_cpus(nmi_shootdown_cb callback)
> {
> unsigned long msecs;
>
> - local_irq_disable();
> + raw_force_local_irq_disable();
>
> /*
> * Avoid certain doom if a shootdown already occurred; re-registering
> diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c
> index aa88370ce739..3164eab7cecd 100644
> --- a/arch/x86/mm/fault.c
> +++ b/arch/x86/mm/fault.c
> @@ -1486,7 +1486,8 @@ handle_page_fault(struct pt_regs *regs, unsigned long error_code,
> * page fault handling might have reenabled interrupts,
> * make sure to disable them again.
> */
> - local_irq_disable();
> + if (!irqs_disabled())
> + local_irq_disable();
> }
>
> DEFINE_IDTENTRY_RAW_ERRORCODE(exc_page_fault)
> diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c
> index 132a9df98471..f13e88fb7a78 100644
> --- a/drivers/acpi/sleep.c
> +++ b/drivers/acpi/sleep.c
> @@ -1095,7 +1095,7 @@ static int acpi_power_off(struct sys_off_data *data)
> {
> /* acpi_sleep_prepare(ACPI_STATE_S5) should have already been called */
> pr_debug("%s called\n", __func__);
> - local_irq_disable();
> + raw_force_local_irq_disable();
> acpi_enter_sleep_state(ACPI_STATE_S5);
> return NOTIFY_DONE;
> }
> diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h
> index 3bf969ad8fe0..b54afbd6e073 100644
> --- a/include/linux/interrupt.h
> +++ b/include/linux/interrupt.h
> @@ -594,13 +594,14 @@ struct softirq_action
>
> asmlinkage void do_softirq(void);
> asmlinkage void __do_softirq(void);
> +void do_softirq_irqsoff(void);
>
> #ifdef CONFIG_PREEMPT_RT
> extern void do_softirq_post_smp_call_flush(unsigned int was_pending);
> #else
> static inline void do_softirq_post_smp_call_flush(unsigned int unused)
> {
> - do_softirq();
> + do_softirq_irqsoff();
> }
> #endif
>
> diff --git a/include/linux/irq-entry-common.h b/include/linux/irq-entry-common.h
> index 0bb6c03481fa..ba5210fab31f 100644
> --- a/include/linux/irq-entry-common.h
> +++ b/include/linux/irq-entry-common.h
> @@ -97,6 +97,7 @@ static __always_inline bool arch_in_rcu_eqs(void) { return false; }
> */
> static __always_inline void enter_from_user_mode(struct pt_regs *regs)
> {
> + __preempt_count_inc_hardirqs_disable();
> arch_enter_from_user_mode(regs);
> lockdep_hardirqs_off(CALLER_ADDR0);
>
> @@ -275,6 +276,7 @@ static __always_inline void exit_to_user_mode(void)
> user_enter_irqoff();
> arch_exit_to_user_mode();
> lockdep_hardirqs_on(CALLER_ADDR0);
> + __preempt_count_dec_hardirqs_disable();
> }
>
> /**
> @@ -385,6 +387,8 @@ static __always_inline irqentry_state_t irqentry_enter_from_kernel_mode(struct p
> .exit_rcu = false,
> };
>
> + __preempt_count_inc_hardirqs_disable();
> +
> /*
> * If this entry hit the idle task invoke ct_irq_enter() whether
> * RCU is watching or not.
> @@ -498,6 +502,7 @@ irqentry_exit_to_kernel_mode_after_preempt(struct pt_regs *regs, irqentry_state_
> instrumentation_end();
> ct_irq_exit();
> lockdep_hardirqs_on(CALLER_ADDR0);
> + __preempt_count_dec_hardirqs_disable();
> return;
> }
>
> @@ -514,6 +519,7 @@ irqentry_exit_to_kernel_mode_after_preempt(struct pt_regs *regs, irqentry_state_
> if (state.exit_rcu)
> ct_irq_exit();
> }
> + __preempt_count_dec_hardirqs_disable();
> }
>
> /**
> diff --git a/include/linux/irqflags.h b/include/linux/irqflags.h
> index 57b074e0cfbb..dd55786768d1 100644
> --- a/include/linux/irqflags.h
> +++ b/include/linux/irqflags.h
> @@ -13,6 +13,7 @@
> #define _LINUX_TRACE_IRQFLAGS_H
>
> #include <linux/irqflags_types.h>
> +#include <linux/preempt.h>
> #include <linux/typecheck.h>
> #include <linux/cleanup.h>
> #include <asm/irqflags.h>
> @@ -163,33 +164,149 @@ extern void warn_bogus_irq_restore(void);
> #endif
>
> /*
> - * Wrap the arch provided IRQ routines to provide appropriate checks.
> + * Wrap the architecture specific routines to provide appropriate checks.
> */
> -#define raw_local_irq_disable() arch_local_irq_disable()
> -#define raw_local_irq_enable() arch_local_irq_enable()
> +#ifdef CONFIG_PREEMPT_COUNT_IRQFLAGS
> +
> +// FIXME: Convert this into a proper debug mechanism
> +#define debug_assert(c) \
> +do { \
> + WARN_ON(!(c)); \
> +} while (0)
> +
> +static __always_inline void raw_local_irq_disable(void)
> +{
> + debug_assert((preempt_count() & HARDIRQ_DISABLE_MASK) == 0);
> + arch_local_irq_disable();
> + __preempt_count_add(HARDIRQ_DISABLE_OFFSET);
> +}
> +
> +static __always_inline void raw_force_local_irq_disable(void)
> +{
> + arch_local_irq_disable();
> + __preempt_count_add(HARDIRQ_DISABLE_OFFSET);
> +}
> +
> +static __always_inline void raw_local_irq_enable(void)
> +{
> + debug_assert((preempt_count() & HARDIRQ_DISABLE_MASK) == HARDIRQ_DISABLE_OFFSET);
> + __preempt_count_sub(HARDIRQ_DISABLE_OFFSET);
> + arch_local_irq_enable();
> +}
> +
> +static __always_inline unsigned long __raw_local_irq_save(void)
> +{
> + unsigned int cnt = preempt_count() & HARDIRQ_DISABLE_MASK;
> +
> + debug_assert(cnt != HARDIRQ_DISABLE_MASK);
> +
> + if (!cnt)
> + arch_local_irq_disable();
> + __preempt_count_add(HARDIRQ_DISABLE_OFFSET);
> +
> + return cnt;
> +}
> +
> +static __always_inline void __raw_local_irq_restore(unsigned long cnt)
> +{
> + debug_assert((preempt_count() & HARDIRQ_DISABLE_MASK) == (cnt + HARDIRQ_DISABLE_OFFSET));
> +
> + if (!(__preempt_count_sub_return(HARDIRQ_DISABLE_OFFSET) & HARDIRQ_DISABLE_MASK))
> + arch_local_irq_enable();
> +}
> +
So we can change the semantics of local_irq_disable(),
local_irq_enable(), local_irq_save() and local_irq_restore()? Nice!
local_irq_{en,dis}able() are no longer idempotent, and
local_irq_{save,restore}() have to pair with each other or a
local_irq_{en,dis}able(). This would make things much easier. And TBH, I
never think this is an option because there could be so much code not
obeying this (at least not on day 1). Now I see your point on getting
the design correct at the first place :D
Regards,
Boqun
> +static __always_inline unsigned long __raw_local_save_flags(void)
> +{
> + return preempt_count() & HARDIRQ_DISABLE_MASK;
> +}
> +
> +static __always_inline bool __raw_irqs_disabled_flags(unsigned long cnt)
> +{
> + return !!cnt;
> +}
> +
> +static __always_inline bool raw_irqs_disabled(void)
> +{
> + return preempt_count() & HARDIRQ_DISABLE_MASK;
> +}
> +
> +static __always_inline void raw_safe_halt(void)
> +{
> + debug_assert((preempt_count() & HARDIRQ_DISABLE_MASK) == HARDIRQ_DISABLE_OFFSET);
> + __preempt_count_sub(HARDIRQ_DISABLE_OFFSET);
> + arch_safe_halt();
> +}
> +
> +#else
> +
> +static __always_inline void raw_local_irq_disable(void)
> +{
> + arch_local_irq_disable();
> +}
> +
> +static __always_inline void raw_force_local_irq_disable(void)
> +{
> + arch_local_irq_disable();
> +}
> +
> +static __always_inline void raw_local_irq_enable(void)
> +{
> + arch_local_irq_enable();
> +}
> +
> +static __always_inline unsigned long __raw_local_irq_save(void)
> +{
> + return arch_local_irq_save();
> +}
> +
> +static __always_inline void __raw_local_irq_restore(unsigned long flags)
> +{
> + arch_local_irq_restore(flags);
> +}
> +
> +static __always_inline unsigned long __raw_local_save_flags(void)
> +{
> + return arch_local_save_flags();
> +}
> +
> +static __always_inline bool __raw_irqs_disabled_flags(unsigned long flags)
> +{
> + return arch_irqs_disabled_flags(flags);
> +}
> +
> +static __always_inline bool raw_irqs_disabled(void)
> +{
> + return arch_irqs_disabled();
> +}
> +
> +static __always_inline void raw_safe_halt(void)
> +{
> + arch_safe_halt();
> +}
> +
> +#endif
> +
> #define raw_local_irq_save(flags) \
> do { \
> typecheck(unsigned long, flags); \
> - flags = arch_local_irq_save(); \
> + flags = __raw_local_irq_save(); \
> } while (0)
> #define raw_local_irq_restore(flags) \
> do { \
> typecheck(unsigned long, flags); \
> raw_check_bogus_irq_restore(); \
> - arch_local_irq_restore(flags); \
> + __raw_local_irq_restore(flags); \
> } while (0)
> #define raw_local_save_flags(flags) \
> do { \
> typecheck(unsigned long, flags); \
> - flags = arch_local_save_flags(); \
> + flags = __raw_local_save_flags(); \
> } while (0)
> #define raw_irqs_disabled_flags(flags) \
> ({ \
> typecheck(unsigned long, flags); \
> - arch_irqs_disabled_flags(flags); \
> + __raw_irqs_disabled_flags(flags); \
> })
> -#define raw_irqs_disabled() (arch_irqs_disabled())
> -#define raw_safe_halt() arch_safe_halt()
>
> /*
> * The local_irq_*() APIs are equal to the raw_local_irq*()
> diff --git a/include/linux/preempt.h b/include/linux/preempt.h
> index 2e689de7b29a..c953cdfb3cc2 100644
> --- a/include/linux/preempt.h
> +++ b/include/linux/preempt.h
> @@ -54,31 +54,31 @@
> * NMI_MASK: 0xf0000000
> * (PREEMPT_NEED_RESCHED is in a different word)
> */
> -#define PREEMPT_BITS 8
> -#define SOFTIRQ_BITS 8
> +#define PREEMPT_BITS 8
> +#define SOFTIRQ_BITS 8
> #define HARDIRQ_DISABLE_BITS 8
> -#define HARDIRQ_BITS 4
> -#define NMI_BITS (1 + 3*IS_ENABLED(CONFIG_HAS_SEPARATE_PREEMPT_RESCHED_BITS))
> +#define HARDIRQ_BITS 4
> +#define NMI_BITS (1 + 3 * IS_ENABLED(CONFIG_HAS_SEPARATE_PREEMPT_RESCHED_BITS))
>
> -#define PREEMPT_SHIFT 0
> -#define SOFTIRQ_SHIFT (PREEMPT_SHIFT + PREEMPT_BITS)
> +#define PREEMPT_SHIFT 0
> +#define SOFTIRQ_SHIFT (PREEMPT_SHIFT + PREEMPT_BITS)
> #define HARDIRQ_DISABLE_SHIFT (SOFTIRQ_SHIFT + SOFTIRQ_BITS)
> -#define HARDIRQ_SHIFT (HARDIRQ_DISABLE_SHIFT + HARDIRQ_DISABLE_BITS)
> -#define NMI_SHIFT (HARDIRQ_SHIFT + HARDIRQ_BITS)
> +#define HARDIRQ_SHIFT (HARDIRQ_DISABLE_SHIFT + HARDIRQ_DISABLE_BITS)
> +#define NMI_SHIFT (HARDIRQ_SHIFT + HARDIRQ_BITS)
>
> -#define __IRQ_MASK(x) ((1UL << (x))-1)
> +#define __IRQ_MASK(x) ((1UL << (x))-1)
>
> -#define PREEMPT_MASK (__IRQ_MASK(PREEMPT_BITS) << PREEMPT_SHIFT)
> -#define SOFTIRQ_MASK (__IRQ_MASK(SOFTIRQ_BITS) << SOFTIRQ_SHIFT)
> +#define PREEMPT_MASK (__IRQ_MASK(PREEMPT_BITS) << PREEMPT_SHIFT)
> +#define SOFTIRQ_MASK (__IRQ_MASK(SOFTIRQ_BITS) << SOFTIRQ_SHIFT)
> #define HARDIRQ_DISABLE_MASK (__IRQ_MASK(HARDIRQ_DISABLE_BITS) << HARDIRQ_DISABLE_SHIFT)
> -#define HARDIRQ_MASK (__IRQ_MASK(HARDIRQ_BITS) << HARDIRQ_SHIFT)
> -#define NMI_MASK (__IRQ_MASK(NMI_BITS) << NMI_SHIFT)
> +#define HARDIRQ_MASK (__IRQ_MASK(HARDIRQ_BITS) << HARDIRQ_SHIFT)
> +#define NMI_MASK (__IRQ_MASK(NMI_BITS) << NMI_SHIFT)
>
> -#define PREEMPT_OFFSET (1UL << PREEMPT_SHIFT)
> -#define SOFTIRQ_OFFSET (1UL << SOFTIRQ_SHIFT)
> +#define PREEMPT_OFFSET (1UL << PREEMPT_SHIFT)
> +#define SOFTIRQ_OFFSET (1UL << SOFTIRQ_SHIFT)
> #define HARDIRQ_DISABLE_OFFSET (1UL << HARDIRQ_DISABLE_SHIFT)
> -#define HARDIRQ_OFFSET (1UL << HARDIRQ_SHIFT)
> -#define NMI_OFFSET (1UL << NMI_SHIFT)
> +#define HARDIRQ_OFFSET (1UL << HARDIRQ_SHIFT)
> +#define NMI_OFFSET (1UL << NMI_SHIFT)
>
> #define SOFTIRQ_DISABLE_OFFSET (2 * SOFTIRQ_OFFSET)
>
> @@ -90,18 +90,29 @@
> *
> * Reset by start_kernel()->sched_init()->init_idle()->init_idle_preempt_count().
> */
> +
> +#ifdef CONFIG_PREEMPT_COUNT_IRQFLAGS
> +
> +#define INIT_PREEMPT_COUNT (PREEMPT_OFFSET + HARDIRQ_DISABLE_OFFSET)
> +#define SCHED_PREEMPT_COUNT (2 * PREEMPT_DISABLE_OFFSET + HARDIRQ_DISABLE_OFFSET)
> +
> +#else
> +
> #define INIT_PREEMPT_COUNT PREEMPT_OFFSET
> +#define SCHED_PREEMPT_COUNT (2 * PREEMPT_DISABLE_OFFSET)
> +
> +#endif
>
> /*
> * Initial preempt_count value; reflects the preempt_count schedule invariant
> * which states that during context switches:
> *
> - * preempt_count() == 2*PREEMPT_DISABLE_OFFSET
> + * preempt_count() == SCHED_PREEMPT_COUNT
> *
> - * Note: PREEMPT_DISABLE_OFFSET is 0 for !PREEMPT_COUNT kernels.
> + * Note: SCHED_PREEMPT_COUNT is 0 for !PREEMPT_COUNT kernels.
> * Note: See finish_task_switch().
> */
> -#define FORK_PREEMPT_COUNT (2*PREEMPT_DISABLE_OFFSET + PREEMPT_ENABLED)
> +#define FORK_PREEMPT_COUNT (SCHED_PREEMPT_COUNT + PREEMPT_ENABLED)
>
> /* preempt_count() and related functions, depends on PREEMPT_NEED_RESCHED */
> #include <asm/preempt.h>
> @@ -168,6 +179,16 @@ static __always_inline unsigned char interrupt_context_level(void)
> #define in_softirq() (softirq_count())
> #define in_interrupt() (irq_count())
>
> +/*
> + * Check whether a fault happened in an atomic context. Depending on
> + * CONFIG_PREEMPT_COUNT and CONFIG_PREEMPTION this check might be useless.
> + */
> +#ifdef CONFIG_PREEMPT_COUNT_IRQFLAGS
> +# define fault_in_atomic() (preempt_count() != HARDIRQ_DISABLE_OFFSET)
> +#else
> +# define fault_in_atomic() in_atomic()
> +#endif
> +
> /*
> * The preempt_count offset after preempt_disable();
> */
> @@ -322,6 +343,21 @@ do { \
>
> #endif /* CONFIG_PREEMPT_COUNT */
>
> +#ifdef CONFIG_PREEMPT_COUNT_IRQFLAGS
> +static __always_inline void __preempt_count_inc_hardirqs_disable(void)
> +{
> + __preempt_count_add(HARDIRQ_DISABLE_OFFSET);
> +}
> +
> +static __always_inline void __preempt_count_dec_hardirqs_disable(void)
> +{
> + __preempt_count_sub(HARDIRQ_DISABLE_OFFSET);
> +}
> +#else
> +static __always_inline void __preempt_count_inc_hardirqs_disable(void) { }
> +static __always_inline void __preempt_count_dec_hardirqs_disable(void) { }
> +#endif
> +
> #ifdef MODULE
> /*
> * Modules have no business playing preemption tricks.
> diff --git a/include/linux/uaccess.h b/include/linux/uaccess.h
> index eddbbb65ccc4..086de4c18575 100644
> --- a/include/linux/uaccess.h
> +++ b/include/linux/uaccess.h
> @@ -296,9 +296,9 @@ static inline bool pagefault_disabled(void)
> * stick to pagefault_disabled().
> * Please NEVER use preempt_disable() to disable the fault handler. With
> * !CONFIG_PREEMPT_COUNT, this is like a NOP. So the handler won't be disabled.
> - * in_atomic() will report different values based on !CONFIG_PREEMPT_COUNT.
> + * fault_in_atomic() will report different values based on !CONFIG_PREEMPT_COUNT.
> */
> -#define faulthandler_disabled() (pagefault_disabled() || in_atomic())
> +#define faulthandler_disabled() (pagefault_disabled() || fault_in_atomic())
>
> DEFINE_LOCK_GUARD_0(pagefault, pagefault_disable(), pagefault_enable())
>
> diff --git a/init/main.c b/init/main.c
> index 2613d3f9b3ce..fa84ce260b04 100644
> --- a/init/main.c
> +++ b/init/main.c
> @@ -991,7 +991,6 @@ void start_kernel(void)
>
> cgroup_init_early();
>
> - local_irq_disable();
> early_boot_irqs_disabled = true;
>
> /*
> diff --git a/kernel/Kconfig.preempt b/kernel/Kconfig.preempt
> index f294dad43bd7..c44607990219 100644
> --- a/kernel/Kconfig.preempt
> +++ b/kernel/Kconfig.preempt
> @@ -152,6 +152,15 @@ config PREEMPT_DYNAMIC
> Interesting if you want the same pre-built kernel should be used for
> both Server and Desktop workloads.
>
> +config ARCH_SUPPORTS_PREEMPT_COUNT_IRQFLAGS
> + bool
> +
> +config PREEMPT_COUNT_IRQFLAGS
> + bool "Enable reference counted interrupt disable/enable mechanisms"
> + depends on ARCH_SUPPORTS_PREEMPT_COUNT_IRQFLAGS
> + help
> + FIXME: Add some useful blurb
> +
> config SCHED_CORE
> bool "Core Scheduling for SMT"
> depends on SCHED_SMT
> diff --git a/kernel/entry/common.c b/kernel/entry/common.c
> index e3d381fd3d25..3c93ce7f86f6 100644
> --- a/kernel/entry/common.c
> +++ b/kernel/entry/common.c
> @@ -171,6 +171,7 @@ irqentry_state_t noinstr irqentry_nmi_enter(struct pt_regs *regs)
> {
> irqentry_state_t irq_state;
>
> + __preempt_count_inc_hardirqs_disable();
> irq_state.lockdep = lockdep_hardirqs_enabled();
>
> __nmi_enter();
> @@ -202,4 +203,5 @@ void noinstr irqentry_nmi_exit(struct pt_regs *regs, irqentry_state_t irq_state)
> if (irq_state.lockdep)
> lockdep_hardirqs_on(CALLER_ADDR0);
> __nmi_exit();
> + __preempt_count_dec_hardirqs_disable();
> }
> diff --git a/kernel/sched/core.c b/kernel/sched/core.c
> index f78275192036..b6d14feaaf56 100644
> --- a/kernel/sched/core.c
> +++ b/kernel/sched/core.c
> @@ -5343,7 +5343,7 @@ static struct rq *finish_task_switch(struct task_struct *prev)
> *
> * Also, see FORK_PREEMPT_COUNT.
> */
> - if (WARN_ONCE(preempt_count() != 2*PREEMPT_DISABLE_OFFSET,
> + if (WARN_ONCE(preempt_count() != SCHED_PREEMPT_COUNT,
> "corrupted preempt_count: %s/%d/0x%x\n",
> current->comm, current->pid, preempt_count()))
> preempt_count_set(FORK_PREEMPT_COUNT);
> diff --git a/kernel/sched/idle.c b/kernel/sched/idle.c
> index eb73b65ce6c4..7132320035eb 100644
> --- a/kernel/sched/idle.c
> +++ b/kernel/sched/idle.c
> @@ -380,7 +380,8 @@ static void do_idle(void)
> * RCU relies on this call to be done outside of an RCU read-side
> * critical section.
> */
> - flush_smp_call_function_queue();
> + scoped_guard(irq)
> + flush_smp_call_function_queue();
> schedule_idle();
>
> if (unlikely(klp_patch_pending(current)))
> diff --git a/kernel/smp.c b/kernel/smp.c
> index b696bcc60c08..d51e4bc8da1b 100644
> --- a/kernel/smp.c
> +++ b/kernel/smp.c
> @@ -665,19 +665,17 @@ static void __flush_smp_call_function_queue(bool warn_cpu_offline)
> void flush_smp_call_function_queue(void)
> {
> unsigned int was_pending;
> - unsigned long flags;
>
> if (llist_empty(this_cpu_ptr(&call_single_queue)))
> return;
>
> - local_irq_save(flags);
> + lockdep_assert_irqs_disabled();
> +
> /* Get the already pending soft interrupts for RT enabled kernels */
> was_pending = local_softirq_pending();
> __flush_smp_call_function_queue(true);
> if (local_softirq_pending())
> do_softirq_post_smp_call_flush(was_pending);
> -
> - local_irq_restore(flags);
> }
>
> static int __smp_call_function_single(int cpu, smp_call_func_t func,
> diff --git a/kernel/softirq.c b/kernel/softirq.c
> index e1a773e3eb4e..33f82d03ca2b 100644
> --- a/kernel/softirq.c
> +++ b/kernel/softirq.c
> @@ -455,7 +455,10 @@ void __local_bh_enable_ip(unsigned long ip, unsigned int cnt)
> * Run softirq if any pending. And do it in its own stack
> * as we may be calling this deep in a task call stack already.
> */
> - do_softirq();
> + if (IS_ENABLED(CONFIG_TRACE_IRQFLAGS))
> + do_softirq_irqsoff();
> + else
> + do_softirq();
> }
>
> preempt_count_dec();
> @@ -517,20 +520,21 @@ static inline void invoke_softirq(void)
>
> asmlinkage __visible void do_softirq(void)
> {
> - __u32 pending;
> - unsigned long flags;
> -
> if (in_interrupt())
> return;
>
> - local_irq_save(flags);
> + guard(irqsave)();
> + if (local_softirq_pending())
> + do_softirq_own_stack();
> +}
>
> - pending = local_softirq_pending();
> +void do_softirq_irqsoff(void)
> +{
> + if (in_interrupt())
> + return;
>
> - if (pending)
> + if (local_softirq_pending())
> do_softirq_own_stack();
> -
> - local_irq_restore(flags);
> }
>
> #endif /* !CONFIG_PREEMPT_RT */
> diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c
> index 530d61257b9a..977dd8928934 100644
> --- a/kernel/time/hrtimer.c
> +++ b/kernel/time/hrtimer.c
> @@ -2068,7 +2068,7 @@ static void __run_hrtimer(struct hrtimer_cpu_base *cpu_base, struct hrtimer_cloc
>
> lockdep_hrtimer_exit(expires_in_hardirq);
> trace_hrtimer_expire_exit(timer);
> - raw_spin_lock_irq(&cpu_base->lock);
> + raw_spin_lock_irqsave(&cpu_base->lock, flags);
>
> /*
> * Note: We clear the running state after enqueue_hrtimer and
>
>
>
next prev parent reply other threads:[~2026-08-29 0:45 UTC|newest]
Thread overview: 96+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-04 16:14 [PATCH v4 00/17] Refcounted interrupt disable and SpinLockIrq for Rust Boqun Feng
2026-08-04 16:14 ` [PATCH v4 01/17] preempt: Track NMI nesting to separate per-CPU counter Boqun Feng
2026-08-08 20:48 ` [tip: locking/core] " tip-bot2 for Joel Fernandes
2026-08-04 16:14 ` [PATCH v4 02/17] preempt: Introduce HARDIRQ_DISABLE_BITS Boqun Feng
2026-08-05 6:31 ` Peter Zijlstra
2026-08-05 6:59 ` Boqun Feng
2026-08-08 20:48 ` [tip: locking/core] " tip-bot2 for Boqun Feng
2026-08-04 16:14 ` [PATCH v4 03/17] preempt: Introduce __preempt_count_{sub,add}_return() Boqun Feng
2026-08-08 20:48 ` [tip: locking/core] " tip-bot2 for Boqun Feng
2026-08-04 16:14 ` [PATCH v4 04/17] openrisc: Include <linux/cpumask.h> in smp.h Boqun Feng
2026-08-08 20:48 ` [tip: locking/core] " tip-bot2 for Lyude Paul
2026-08-04 16:14 ` [PATCH v4 05/17] irq & spin_lock: Add counted interrupt disabling/enabling Boqun Feng
2026-08-04 18:20 ` Boqun Feng
2026-08-04 18:26 ` [PATCH v4.1 " Boqun Feng
2026-08-08 20:48 ` [tip: locking/core] " tip-bot2 for Boqun Feng
2026-08-10 8:57 ` [tip: locking/core] irq,spin_lock: " tip-bot2 for Boqun Feng
2026-08-04 20:51 ` [PATCH v4 05/17] irq & spin_lock: " Shrikanth Hegde
2026-08-04 21:08 ` Boqun Feng
2026-08-05 6:36 ` Peter Zijlstra
2026-08-05 7:07 ` Boqun Feng
2026-08-05 7:09 ` Shrikanth Hegde
2026-08-05 7:19 ` Boqun Feng
2026-08-05 13:53 ` Boqun Feng
2026-08-05 14:10 ` Shrikanth Hegde
2026-08-05 14:20 ` Boqun Feng
2026-08-05 14:56 ` Shrikanth Hegde
2026-08-05 15:11 ` Boqun Feng
2026-08-05 16:53 ` Shrikanth Hegde
2026-08-05 17:38 ` Boqun Feng
2026-08-05 18:07 ` Boqun Feng
2026-08-04 16:14 ` [PATCH v4 06/17] irq: Add KUnit test for refcounted interrupt enable/disable Boqun Feng
2026-08-08 20:48 ` [tip: locking/core] " tip-bot2 for Lyude Paul
2026-08-10 8:57 ` tip-bot2 for Lyude Paul
2026-08-04 16:14 ` [PATCH v4 07/17] locking: Switch to _irq_{disable,enable}() variants in cleanup guards Boqun Feng
2026-08-08 20:48 ` [tip: locking/core] " tip-bot2 for Boqun Feng
2026-08-10 8:57 ` tip-bot2 for Boqun Feng
2026-08-24 10:47 ` Peter Zijlstra
2026-08-24 10:55 ` [PATCH] locking: Revert switching guards to _irq_{disable,enable}() Peter Zijlstra
2026-08-24 11:01 ` [tip: locking/urgent] " tip-bot2 for Peter Zijlstra
2026-08-25 1:33 ` [PATCH] " Boqun Feng
2026-08-25 22:59 ` Thomas Gleixner
2026-08-25 23:28 ` Boqun Feng
2026-08-25 23:48 ` Boqun Feng
2026-08-26 1:33 ` Boqun Feng
2026-08-27 8:30 ` Thomas Gleixner
2026-08-27 13:14 ` Boqun Feng
2026-08-27 15:43 ` Thomas Gleixner
2026-08-27 16:52 ` Boqun Feng
2026-08-27 18:15 ` Thomas Gleixner
2026-08-27 19:41 ` Boqun Feng
2026-08-27 22:52 ` Thomas Gleixner
2026-08-28 1:56 ` Boqun Feng
2026-08-28 6:42 ` Peter Zijlstra
2026-08-28 23:11 ` Thomas Gleixner
2026-08-29 0:45 ` Boqun Feng [this message]
2026-08-29 20:44 ` Thomas Gleixner
2026-08-29 20:53 ` Boqun Feng
2026-08-29 8:05 ` Peter Zijlstra
2026-08-29 19:52 ` Thomas Gleixner
2026-08-29 23:37 ` Boqun Feng
2026-08-30 15:18 ` Boqun Feng
2026-08-27 20:29 ` Thomas Gleixner
2026-08-27 21:33 ` Boqun Feng
2026-08-28 6:55 ` Peter Zijlstra
2026-08-28 8:22 ` David Laight
2026-08-28 21:26 ` Boqun Feng
2026-08-04 16:14 ` [PATCH v4 08/17] sched: Remove the unused preempt_offset parameter of __cant_sleep() Boqun Feng
2026-08-08 20:48 ` [tip: locking/core] " tip-bot2 for Boqun Feng
2026-08-10 8:57 ` tip-bot2 for Boqun Feng
2026-08-04 16:14 ` [PATCH v4 09/17] sched: Avoid signed comparison of preempt_count() in __cant_migrate() Boqun Feng
2026-08-08 20:48 ` [tip: locking/core] " tip-bot2 for Boqun Feng
2026-08-10 8:57 ` tip-bot2 for Boqun Feng
2026-08-04 16:14 ` [PATCH v4 10/17] preempt: Introduce HAS_SEPARATE_PREEMPT_RESCHED_BITS Boqun Feng
2026-08-04 20:11 ` Shrikanth Hegde
2026-08-05 6:54 ` Boqun Feng
2026-08-05 7:15 ` Shrikanth Hegde
2026-08-05 7:27 ` Boqun Feng
2026-08-06 0:58 ` Boqun Feng
2026-08-04 21:09 ` Shrikanth Hegde
2026-08-04 23:14 ` Boqun Feng
2026-08-08 20:48 ` [tip: locking/core] " tip-bot2 for Boqun Feng
2026-08-10 8:57 ` tip-bot2 for Boqun Feng
2026-08-04 16:14 ` [PATCH v4 11/17] arm64: sched/preempt: Enable HAS_SEPARATE_PREEMPT_RESCHED_BITS Boqun Feng
2026-08-08 20:48 ` [tip: locking/core] " tip-bot2 for Boqun Feng
2026-08-10 8:57 ` tip-bot2 for Boqun Feng
2026-08-04 16:14 ` [PATCH v4 12/17] s390/preempt: " Boqun Feng
2026-08-04 20:27 ` Shrikanth Hegde
2026-08-05 9:42 ` Peter Zijlstra
2026-08-05 12:37 ` Shrikanth Hegde
2026-08-08 20:48 ` [tip: locking/core] " tip-bot2 for Heiko Carstens
2026-08-10 8:57 ` tip-bot2 for Heiko Carstens
2026-08-04 16:14 ` [PATCH v4 13/17] rust: Introduce interrupt module Boqun Feng
2026-08-04 16:14 ` [PATCH v4 14/17] rust: helper: Add spin_{un,}lock_irq_{enable,disable}() helpers Boqun Feng
2026-08-04 16:14 ` [PATCH v4 15/17] rust: sync: Use super::* in spinlock.rs Boqun Feng
2026-08-04 16:14 ` [PATCH v4 16/17] rust: sync: Add SpinLockIrq Boqun Feng
2026-08-04 16:14 ` [PATCH v4 17/17] rust: sync: Introduce SpinLockIrq::lock_with() and friends Boqun Feng
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=apIrwRYJbPpmjrNo@tardis.local \
--to=boqun@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-tip-commits@vger.kernel.org \
--cc=peterz@infradead.org \
--cc=tglx@kernel.org \
--cc=x86@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®