preempt: Introduce __preempt_count_{sub,add}_return()

In order to use preempt_count() to track the interrupt disable nesting
level, __preempt_count_{add,sub}_return() are introduced, as their names
suggest, these primitives return the new value of the preempt_count()
after changing it. The following example shows the usage of it in
local_interrupt_disable():

	// increase the HARDIRQ_DISABLE bit
	new_count = __preempt_count_add_return(HARDIRQ_DISABLE_OFFSET);

	// if it's the first-time increment, then disable the interrupt
	// at hardware level.
	if ((new_count & HARDIRQ_DISABLE_MASK) == HARDIRQ_DISABLE_OFFSET) {
		local_irq_save(flags);
		raw_cpu_write(local_interrupt_disable_state, flags);
	}

Having these primitives will avoid a read of preempt_count() after
changing preempt_count() on certain architectures.

Signed-off-by: Boqun Feng <boqun@kernel.org>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Acked-by: Heiko Carstens <hca@linux.ibm.com> # s390
Link: https://patch.msgid.link/20260804161447.84806-4-boqun@kernel.org
This commit is contained in:
Boqun Feng 2026-08-04 09:14:25 -07:00 committed by Peter Zijlstra
parent 9634d860ac
commit 4ad87eee54
4 changed files with 54 additions and 0 deletions

View File

@ -55,6 +55,26 @@ static inline void __preempt_count_sub(int val)
WRITE_ONCE(current_thread_info()->preempt.count, pc);
}
static inline int __preempt_count_add_return(int val)
{
u32 pc = READ_ONCE(current_thread_info()->preempt.count);
pc += val;
WRITE_ONCE(current_thread_info()->preempt.count, pc);
return pc;
}
static inline int __preempt_count_sub_return(int val)
{
u32 pc = READ_ONCE(current_thread_info()->preempt.count);
pc -= val;
WRITE_ONCE(current_thread_info()->preempt.count, pc);
return pc;
}
static inline bool __preempt_count_dec_and_test(void)
{
struct thread_info *ti = current_thread_info();

View File

@ -139,6 +139,16 @@ static __always_inline bool should_resched(int preempt_offset)
return unlikely(READ_ONCE(get_lowcore()->preempt_count) == preempt_offset);
}
static __always_inline int __preempt_count_add_return(int val)
{
return val + __atomic_add(val, &get_lowcore()->preempt_count);
}
static __always_inline int __preempt_count_sub_return(int val)
{
return __preempt_count_add_return(-val);
}
#define init_task_preempt_count(p) do { } while (0)
/* Deferred to CPU bringup time */
#define init_idle_preempt_count(p, cpu) do { } while (0)

View File

@ -85,6 +85,16 @@ static __always_inline void __preempt_count_sub(int val)
raw_cpu_add_4(__preempt_count, -val);
}
static __always_inline int __preempt_count_add_return(int val)
{
return raw_cpu_add_return_4(__preempt_count, val);
}
static __always_inline int __preempt_count_sub_return(int val)
{
return raw_cpu_add_return_4(__preempt_count, -val);
}
/*
* Because we keep PREEMPT_NEED_RESCHED set when we do _not_ need to reschedule
* a decrement which hits zero means we have no preempt_count and should

View File

@ -59,6 +59,20 @@ static __always_inline void __preempt_count_sub(int val)
*preempt_count_ptr() -= val;
}
static __always_inline int __preempt_count_add_return(int val)
{
*preempt_count_ptr() += val;
return *preempt_count_ptr();
}
static __always_inline int __preempt_count_sub_return(int val)
{
*preempt_count_ptr() -= val;
return *preempt_count_ptr();
}
static __always_inline bool __preempt_count_dec_and_test(void)
{
/*