Merge tag 'locking-core-2026-08-17' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull locking updates from Ingo Molnar:
 "Futexes:

   - Use runtime constants for futex_hash computation (K Prateek Nayak,
     Peter Zijlstra)

   - Optimise the size check get_futex_key() (Sebastian Andrzej Siewior)

   - Avoid private hash use-after-free on final put (Felix Hoffmann)

   - Tell kmemleak we're not leaking __futex_queues (Peter Zijlstra)

  Rust integration updates:

   - Implement refcounted interrupt disable and SpinLockIrq for Rust
     (Boqun Feng, Heiko Carstens, Joel Fernandes, Lyude Paul)

   - Rust sync: add helpers for mb, dma_mb and friends; add generic
     memory barriers and use LKMM atomics instead of Rust atomics in the
     revocable code (Gary Guo)

   - Add abstraction and integrate synchronize_rcu() (Philipp Stanner)

  Lock debugging:

   - Add qspinlock contended_release tracepoint (Dmitry Ilvokhin, Peter
     Zijlstra)

   - Enable the printing of held locks of remote running tasks and print
     task CPU (Ingo Molnar)

   - percpu-rwsem: Annotate intentional data race in readers_active_check()
     (Sun Shaojie)

  Misc fixes and updates by Boqun Feng, Peter Zijlstra, Fangrui Song,
  Naveen Kumar Chaudhary and Thomas Huth"

* tag 'locking-core-2026-08-17' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (44 commits)
  rust: sync: Introduce SpinLockIrq::lock_with() and friends
  rust: sync: Add SpinLockIrq
  rust: sync: Use super::* in spinlock.rs
  rust: helper: Add spin_{un,}lock_irq_{enable,disable}() helpers
  rust: Introduce interrupt module
  s390/preempt: Enable HAS_SEPARATE_PREEMPT_RESCHED_BITS
  arm64: sched/preempt: Enable HAS_SEPARATE_PREEMPT_RESCHED_BITS
  preempt: Introduce HAS_SEPARATE_PREEMPT_RESCHED_BITS
  sched: Avoid signed comparison of preempt_count() in __cant_migrate()
  sched: Remove the unused preempt_offset parameter of __cant_sleep()
  locking: Switch to _irq_{disable,enable}() variants in cleanup guards
  irq: Add KUnit test for refcounted interrupt enable/disable
  irq,spin_lock: Add counted interrupt disabling/enabling
  openrisc: Include <linux/cpumask.h> in smp.h
  preempt: Introduce __preempt_count_{sub,add}_return()
  preempt: Introduce HARDIRQ_DISABLE_BITS
  preempt: Track NMI nesting to separate per-CPU counter
  futex: Tell kmemleak we're not leaking __futex_queues
  x86/paravirt: Trace contended_release on unlock
  tracing/lock: Use TRACE_EVENT_FN() for contended_release
  ...
This commit is contained in:
Linus Torvalds
2026-08-18 13:07:17 -07:00
70 changed files with 1655 additions and 243 deletions

View File

@@ -2,7 +2,7 @@
#ifndef _ASM_ARM_JUMP_LABEL_H
#define _ASM_ARM_JUMP_LABEL_H
#ifndef __ASSEMBLY__
#ifndef __ASSEMBLER__
#include <linux/types.h>
#include <asm/unified.h>
@@ -49,5 +49,5 @@ struct jump_entry {
jump_label_t key;
};
#endif /* __ASSEMBLY__ */
#endif /* __ASSEMBLER__ */
#endif

View File

@@ -247,6 +247,7 @@ config ARM64
select PCI_SYSCALL if PCI
select POWER_RESET
select POWER_SUPPLY
select HAS_SEPARATE_PREEMPT_RESCHED_BITS
select SPARSE_IRQ
select SWIOTLB
select SYSCTL_EXCEPTION_TRACE

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

@@ -7,6 +7,7 @@
#endif
#include <asm/cacheflush.h>
#include <asm/text-patching.h>
/* Sigh. You can still run arm64 in BE mode */
#include <asm/byteorder.h>
@@ -35,6 +36,17 @@
:"r" (0u+(val))); \
__ret; })
#define runtime_const_mask_32(val, sym) ({ \
unsigned long __ret; \
asm_inline("1:\t" \
"ubfx %w0, %w1, #0, #32\n\t" \
".pushsection runtime_mask_" #sym ",\"a\"\n\t" \
".long 1b - .\n\t" \
".popsection" \
:"=r" (__ret) \
:"r" (0u+(val))); \
__ret; })
#define runtime_const_init(type, sym) do { \
extern s32 __start_runtime_##type##_##sym[]; \
extern s32 __stop_runtime_##type##_##sym[]; \
@@ -50,34 +62,61 @@ static inline void __runtime_fixup_16(__le32 *p, unsigned int val)
u32 insn = le32_to_cpu(*p);
insn &= 0xffe0001f;
insn |= (val & 0xffff) << 5;
*p = cpu_to_le32(insn);
}
static inline void __runtime_fixup_caches(void *where, unsigned int insns)
{
unsigned long va = (unsigned long)where;
caches_clean_inval_pou(va, va + 4*insns);
aarch64_insn_patch_text_nosync(p, insn);
}
static inline void __runtime_fixup_ptr(void *where, unsigned long val)
{
__le32 *p = lm_alias(where);
__le32 *p = where;
__runtime_fixup_16(p, val);
__runtime_fixup_16(p+1, val >> 16);
__runtime_fixup_16(p+2, val >> 32);
__runtime_fixup_16(p+3, val >> 48);
__runtime_fixup_caches(where, 4);
}
/* Immediate value is 6 bits starting at bit #16 */
static inline void __runtime_fixup_shift(void *where, unsigned long val)
{
__le32 *p = lm_alias(where);
__le32 *p = where;
u32 insn = le32_to_cpu(*p);
insn &= 0xffc0ffff;
insn |= (val & 63) << 16;
*p = cpu_to_le32(insn);
__runtime_fixup_caches(where, 1);
aarch64_insn_patch_text_nosync(p, insn);
}
static inline void __runtime_fixup_mask(void *where, unsigned long val)
{
unsigned int width = (val) ? __fls(val) + 1 : 0;
__le32 *p = where;
u32 insn;
/*
* XXX: Current implementation only supports patching masks of
* form GENMASK(n, 0) (n >= 0) using a single UBFX instruction
* to improve performance, density, and covers all the current
* use-cases.
*
* When the need arises to support any generic mask, and this
* BUG_ON() is tripped, consider using a:
*
* movz %w0, #imm16
* movk %w0, #imm16, lsl #16
*
* sequence to load the 32bit const mask, and perform a logical
* and outside the asm block before returning the result. Fixup
* can simply reuse the existing __runtime_fixup_16() to patch
* the individual mov instructions.
*/
BUG_ON(!val || width > 32 || (GENMASK(width - 1, 0) != val));
/*
* The width of the mask is encoded as (width - 1) in imms
* which is 6 bits starting at bit #10.
*/
insn = le32_to_cpu(*p);
insn &= 0xffff03ff;
insn |= ((width - 1) & 0x1f) << 10;
aarch64_insn_patch_text_nosync(p, insn);
}
static inline void runtime_const_fixup(void (*fn)(void *, unsigned long),

View File

@@ -13,12 +13,12 @@
#include <asm-generic/qspinlock_types.h>
#define queued_spin_unlock queued_spin_unlock
#define queued_spin_release queued_spin_release
/**
* queued_spin_unlock - release a queued spinlock
* queued_spin_release - release a queued spinlock
* @lock : Pointer to queued spinlock structure
*/
static inline void queued_spin_unlock(struct qspinlock *lock)
static inline void queued_spin_release(struct qspinlock *lock)
{
/* This could be optimised with ARCH_HAS_MMIOWB */
mmiowb();

View File

@@ -9,6 +9,8 @@
#ifndef __ASM_OPENRISC_SMP_H
#define __ASM_OPENRISC_SMP_H
#include <linux/cpumask.h>
#include <asm/spr.h>
#include <asm/spr_defs.h>

View File

@@ -34,6 +34,7 @@
#define SZREG __REG_SEL(8, 4)
#define LGREG __REG_SEL(3, 2)
#define SRLI __REG_SEL(srliw, srli)
#define SLLI __REG_SEL(slliw, slli)
#if __SIZEOF_POINTER__ == 8
#ifdef __ASSEMBLER__

View File

@@ -15,21 +15,24 @@
#include <linux/uaccess.h>
#define RUNTIME_MAGIC __ASM_STR(0x89ABCDEF)
#ifdef CONFIG_32BIT
#define runtime_const_ptr(sym) \
({ \
typeof(sym) __ret; \
asm_inline(".option push\n\t" \
".option norvc\n\t" \
"1:\t" \
"lui %[__ret],0x89abd\n\t" \
"addi %[__ret],%[__ret],-0x211\n\t" \
".option pop\n\t" \
".pushsection runtime_ptr_" #sym ",\"a\"\n\t" \
".long 1b - .\n\t" \
".popsection" \
: [__ret] "=r" (__ret)); \
__ret; \
#define runtime_const_ptr(sym) \
({ \
typeof(sym) __ret; \
asm_inline(".option push\n\t" \
".option norvc\n\t" \
".option norelax\n\t" \
"1:\t" \
"lui %[__ret], %%hi(" RUNTIME_MAGIC ")\n\t" \
"addi %[__ret],%[__ret], %%lo(" RUNTIME_MAGIC ")\n\t" \
".option pop\n\t" \
".pushsection runtime_ptr_" #sym ",\"a\"\n\t" \
".long 1b - .\n\t" \
".popsection" \
: [__ret] "=r" (__ret)); \
__ret; \
})
#else
/*
@@ -45,11 +48,12 @@
#define RISCV_RUNTIME_CONST_64_PREAMBLE \
".option push\n\t" \
".option norvc\n\t" \
".option norelax\n\t" \
"1:\t" \
"lui %[__ret],0x89abd\n\t" \
"lui %[__tmp],0x1234\n\t" \
"addiw %[__ret],%[__ret],-0x211\n\t" \
"addiw %[__tmp],%[__tmp],0x567\n\t" \
"lui %[__ret], %%hi(" RUNTIME_MAGIC ")\n\t" \
"lui %[__tmp], %%hi(" RUNTIME_MAGIC ")\n\t" \
"addiw %[__ret],%[__ret], %%lo(" RUNTIME_MAGIC ")\n\t" \
"addiw %[__tmp],%[__tmp], %%lo(" RUNTIME_MAGIC ")\n\t" \
#define RISCV_RUNTIME_CONST_64_BASE \
"slli %[__tmp],%[__tmp],32\n\t" \
@@ -157,6 +161,23 @@
__ret; \
})
#define runtime_const_mask_32(val, sym) \
({ \
u32 __ret; \
asm_inline(".option push\n\t" \
".option norvc\n\t" \
"1:\t" \
SLLI " %[__ret],%[__val],12\n\t" \
SRLI " %[__ret],%[__ret],12\n\t" \
".option pop\n\t" \
".pushsection runtime_mask_" #sym ",\"a\"\n\t" \
".long 1b - .\n\t" \
".popsection" \
: [__ret] "=r" (__ret) \
: [__val] "r" (val)); \
__ret; \
})
#define runtime_const_init(type, sym) do { \
extern s32 __start_runtime_##type##_##sym[]; \
extern s32 __stop_runtime_##type##_##sym[]; \
@@ -260,6 +281,33 @@ static inline void __runtime_fixup_shift(void *where, unsigned long val)
mutex_unlock(&text_mutex);
}
static inline void __runtime_fixup_mask(void *where, unsigned long val)
{
unsigned int width = (val) ? __fls(val) + 1 : 0;
/*
* XXX: Current implementation only supports patching masks of
* form GENMASK(width, 0) (width >= 0) using a SRLI + SLLI
* sequence instead of LUI + ADDI + AND sequence to improve
* performance, density, and covers all the current use-cases.
*
* When the need arises to support any generic mask, and this
* BUG_ON() is tripped, consider using a:
*
* lui %[__ret], #imm16
* addi %[__ret], #imm16
*
* sequence to load the 32bit const mask, and perform a logical
* and outside the asm block before returning the result. Fixup
* can simply reuse the existing __runtime_fixup_32() to patch
* the LUI + ADDI sequence.
*/
BUG_ON(!val || width > 31 || (GENMASK(width - 1, 0) != val));
__runtime_fixup_shift(where, 32 - width);
__runtime_fixup_shift(where + 4, 32 - width);
}
static inline void runtime_const_fixup(void (*fn)(void *, unsigned long),
unsigned long val, s32 *start, s32 *end)
{

View File

@@ -273,6 +273,7 @@ config S390
select PCI_MSI if PCI
select PCI_MSI_ARCH_FALLBACKS if PCI_MSI
select PCI_QUIRKS if PCI
select HAS_SEPARATE_PREEMPT_RESCHED_BITS
select SPARSE_IRQ
select SWIOTLB
select SYSCTL_EXCEPTION_TRACE

View File

@@ -160,10 +160,15 @@ struct lowcore {
/* SMP info area */
__u32 cpu_nr; /* 0x03a0 */
__u32 softirq_pending; /* 0x03a4 */
__s32 preempt_count; /* 0x03a8 */
__u32 spinlock_lockval; /* 0x03ac */
__u32 spinlock_index; /* 0x03b0 */
__u8 pad_0x03b4[0x03b8-0x03b4]; /* 0x03b4 */
union {
struct {
__u32 need_resched; /* 0x03a8 */
__u32 count; /* 0x03ac */
} preempt;
__u64 preempt_count; /* 0x03a8 */
};
__u32 spinlock_lockval; /* 0x03b0 */
__u32 spinlock_index; /* 0x03b4 */
__u64 percpu_offset; /* 0x03b8 */
__u8 percpu_register; /* 0x03c0 */
__u8 pad_0x03c1[0x0400-0x03c1]; /* 0x03c1 */

View File

@@ -8,11 +8,8 @@
#include <asm/cmpxchg.h>
#include <asm/march.h>
/*
* Use MSB so it is possible to read preempt_count with LLGT which
* reads the least significant 31 bits with a single instruction.
*/
#define PREEMPT_NEED_RESCHED 0x80000000
/* Use MSB for PREEMPT_NEED_RESCHED mostly because it is available. */
#define PREEMPT_NEED_RESCHED 0x8000000000000000UL
/*
* We use the PREEMPT_NEED_RESCHED bit as an inverted NEED_RESCHED such
@@ -26,25 +23,25 @@
*/
static __always_inline int preempt_count(void)
{
unsigned long lc_preempt, count;
unsigned long lc_preempt;
int count;
BUILD_BUG_ON(sizeof_field(struct lowcore, preempt_count) != sizeof(int));
lc_preempt = offsetof(struct lowcore, preempt_count);
/* READ_ONCE(get_lowcore()->preempt_count) & ~PREEMPT_NEED_RESCHED */
lc_preempt = offsetof(struct lowcore, preempt.count);
/* READ_ONCE(get_lowcore()->preempt.count) (without PREEMPT_NEED_RESCHED) */
asm_inline(
ALTERNATIVE("llgt %[count],%[offzero](%%r0)\n",
"llgt %[count],%[offalt](%%r0)\n",
ALTERNATIVE("ly %[count],%[offzero](%%r0)\n",
"ly %[count],%[offalt](%%r0)\n",
ALT_FEATURE(MFEATURE_LOWCORE))
: [count] "=d" (count)
: [offzero] "i" (lc_preempt),
[offalt] "i" (lc_preempt + LOWCORE_ALT_ADDRESS),
"m" (((struct lowcore *)0)->preempt_count));
"m" (((struct lowcore *)0)->preempt.count));
return count;
}
static __always_inline void preempt_count_set(int pc)
static __always_inline void preempt_count_set(unsigned long pc)
{
int old, new;
unsigned long old, new;
old = READ_ONCE(get_lowcore()->preempt_count);
do {
@@ -63,12 +60,12 @@ static __always_inline void preempt_count_set(int pc)
static __always_inline void set_preempt_need_resched(void)
{
__atomic_and(~PREEMPT_NEED_RESCHED, &get_lowcore()->preempt_count);
__atomic64_and(~PREEMPT_NEED_RESCHED, (long *)&get_lowcore()->preempt_count);
}
static __always_inline void clear_preempt_need_resched(void)
{
__atomic_or(PREEMPT_NEED_RESCHED, &get_lowcore()->preempt_count);
__atomic64_or(PREEMPT_NEED_RESCHED, (long *)&get_lowcore()->preempt_count);
}
static __always_inline bool test_preempt_need_resched(void)
@@ -88,8 +85,8 @@ static __always_inline void __preempt_count_add(int val)
lc_preempt = offsetof(struct lowcore, preempt_count);
asm_inline(
ALTERNATIVE("asi %[offzero](%%r0),%[val]\n",
"asi %[offalt](%%r0),%[val]\n",
ALTERNATIVE("agsi %[offzero](%%r0),%[val]\n",
"agsi %[offalt](%%r0),%[val]\n",
ALT_FEATURE(MFEATURE_LOWCORE))
: "+m" (((struct lowcore *)0)->preempt_count)
: [offzero] "i" (lc_preempt), [val] "i" (val),
@@ -98,7 +95,7 @@ static __always_inline void __preempt_count_add(int val)
return;
}
}
__atomic_add(val, &get_lowcore()->preempt_count);
__atomic64_add(val, (long *)&get_lowcore()->preempt_count);
}
static __always_inline void __preempt_count_sub(int val)
@@ -119,15 +116,15 @@ static __always_inline bool __preempt_count_dec_and_test(void)
lc_preempt = offsetof(struct lowcore, preempt_count);
asm_inline(
ALTERNATIVE("alsi %[offzero](%%r0),%[val]\n",
"alsi %[offalt](%%r0),%[val]\n",
ALTERNATIVE("algsi %[offzero](%%r0),%[val]\n",
"algsi %[offalt](%%r0),%[val]\n",
ALT_FEATURE(MFEATURE_LOWCORE))
: "=@cc" (cc), "+m" (((struct lowcore *)0)->preempt_count)
: [offzero] "i" (lc_preempt), [val] "i" (-1),
[offalt] "i" (lc_preempt + LOWCORE_ALT_ADDRESS));
return (cc == 0) || (cc == 2);
#else
return __atomic_add_const_and_test(-1, &get_lowcore()->preempt_count);
return __atomic64_add_const_and_test(-1, (long *)&get_lowcore()->preempt_count);
#endif
}
@@ -139,6 +136,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 + __atomic64_add(val, (long *)&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

@@ -33,6 +33,20 @@
__ret; \
})
#define runtime_const_mask_32(val, sym) \
({ \
unsigned int __ret = (val); \
\
asm_inline( \
"0: nilf %[__ret],12\n" \
".pushsection runtime_mask_" #sym ",\"a\"\n" \
".long 0b - .\n" \
".popsection" \
: [__ret] "+d" (__ret) \
: : "cc"); \
__ret; \
})
#define runtime_const_init(type, sym) do { \
extern s32 __start_runtime_##type##_##sym[]; \
extern s32 __stop_runtime_##type##_##sym[]; \
@@ -43,12 +57,12 @@
__stop_runtime_##type##_##sym); \
} while (0)
/* 32-bit immediate for iihf and iilf in bits in I2 field */
static inline void __runtime_fixup_32(u32 *p, unsigned int val)
{
s390_kernel_write(p, &val, sizeof(val));
}
/* 32-bit immediate for iihf and iilf in bits in I2 field */
static inline void __runtime_fixup_ptr(void *where, unsigned long val)
{
__runtime_fixup_32(where + 2, val >> 32);
@@ -65,6 +79,12 @@ static inline void __runtime_fixup_shift(void *where, unsigned long val)
s390_kernel_write(where, &insn, sizeof(insn));
}
/* 32-bit immediate for nilf in bits in I2 field */
static inline void __runtime_fixup_mask(void *where, unsigned long val)
{
__runtime_fixup_32(where + 2, val);
}
static inline void runtime_const_fixup(void (*fn)(void *, unsigned long),
unsigned long val, s32 *start, s32 *end)
{

View File

@@ -326,6 +326,7 @@ config X86
select USER_STACKTRACE_SUPPORT
select HAVE_ARCH_KCSAN if X86_64
select PROC_PID_ARCH_STATUS if PROC_FS
select HAS_SEPARATE_PREEMPT_RESCHED_BITS if X86_64 && PREEMPT_COUNT
select HAVE_ARCH_NODE_DEV_GROUP if X86_SGX
select FUNCTION_ALIGNMENT_16B if X86_64 || X86_ALIGNMENT_16
select FUNCTION_ALIGNMENT_4B

View File

@@ -78,8 +78,8 @@ void __init hv_init_spinlocks(void)
pr_info("PV spinlocks enabled\n");
__pv_init_lock_hash();
pv_ops_lock.queued_spin_lock_slowpath = __pv_queued_spin_lock_slowpath;
pv_ops_lock.queued_spin_unlock = PV_CALLEE_SAVE(__pv_queued_spin_unlock);
static_call_update(queued_spin_lock_slowpath, __pv_queued_spin_lock_slowpath);
static_call_update(queued_spin_unlock, __raw_callee_save___pv_queued_spin_unlock);
pv_ops_lock.wait = hv_qlock_wait;
pv_ops_lock.kick = hv_qlock_kick;
pv_ops_lock.vcpu_is_preempted = PV_CALLEE_SAVE(hv_vcpu_is_preempted);

View File

@@ -225,7 +225,7 @@
#define X86_FEATURE_EPT_AD ( 8*32+17) /* "ept_ad" Intel Extended Page Table access-dirty bit */
#define X86_FEATURE_VMCALL ( 8*32+18) /* Hypervisor supports the VMCALL instruction */
#define X86_FEATURE_VMW_VMMCALL ( 8*32+19) /* VMware prefers VMMCALL hypercall instruction */
#define X86_FEATURE_PVUNLOCK ( 8*32+20) /* PV unlock function */
// free: was #define X86_FEATURE_PVUNLOCK ( 8*32+20) /* PV unlock function */
#define X86_FEATURE_VCPUPREEMPT ( 8*32+21) /* PV vcpu_is_preempted function */
#define X86_FEATURE_TDX_GUEST ( 8*32+22) /* "tdx_guest" Intel Trust Domain Extensions Guest */

View File

@@ -3,6 +3,7 @@
#define _ASM_X86_PARAVIRT_SPINLOCK_H
#include <asm/paravirt_types.h>
#include <linux/static_call_types.h>
#ifdef CONFIG_SMP
#include <asm/spinlock_types.h>
@@ -11,9 +12,6 @@
struct qspinlock;
struct pv_lock_ops {
void (*queued_spin_lock_slowpath)(struct qspinlock *lock, u32 val);
struct paravirt_callee_save queued_spin_unlock;
void (*wait)(u8 *ptr, u8 val);
void (*kick)(int cpu);
@@ -26,20 +24,27 @@ extern struct pv_lock_ops pv_ops_lock;
extern void native_queued_spin_lock_slowpath(struct qspinlock *lock, u32 val);
extern void __pv_init_lock_hash(void);
extern void __pv_queued_spin_lock_slowpath(struct qspinlock *lock, u32 val);
extern void __raw_callee_save___native_queued_spin_unlock(struct qspinlock *lock);
extern void __raw_callee_save___pv_queued_spin_unlock(struct qspinlock *lock);
extern bool nopvspin;
DECLARE_STATIC_CALL(queued_spin_lock_slowpath, native_queued_spin_lock_slowpath);
DECLARE_STATIC_CALL(queued_spin_unlock, __raw_callee_save___native_queued_spin_unlock);
static __always_inline void pv_queued_spin_lock_slowpath(struct qspinlock *lock,
u32 val)
{
PVOP_VCALL2(pv_ops_lock, queued_spin_lock_slowpath, lock, val);
static_call_mod(queued_spin_lock_slowpath)(lock, val);
}
static __always_inline void pv_queued_spin_unlock(struct qspinlock *lock)
{
PVOP_ALT_VCALLEE1(pv_ops_lock, queued_spin_unlock, lock,
"movb $0, (%%" _ASM_ARG1 ")",
ALT_NOT(X86_FEATURE_PVUNLOCK));
PVOP_CALL_ARGS;
__STATIC_CALL_MOD_ADDRESSABLE(queued_spin_unlock);
asm volatile ("call " STATIC_CALL_TRAMP_STR(queued_spin_unlock)
: PVOP_VCALLEE_CLOBBERS, ASM_CALL_CONSTRAINT
: PVOP_CALL_ARG1(lock)
: "memory", "cc");
}
static __always_inline bool pv_vcpu_is_preempted(long cpu)
@@ -94,6 +99,8 @@ bool __raw_callee_save___native_vcpu_is_preempted(long cpu);
void __init native_pv_lock_init(void);
__visible void __native_queued_spin_unlock(struct qspinlock *lock);
__visible void native_queued_spin_unlock_traced(struct qspinlock *lock);
__visible void pv_queued_spin_unlock_traced(struct qspinlock *lock);
bool pv_is_native_spin_unlock(void);
__visible bool __native_vcpu_is_preempted(long cpu);
bool pv_is_native_vcpu_is_preempted(void);

View File

@@ -7,10 +7,20 @@
#include <linux/static_call_types.h>
DECLARE_PER_CPU_CACHE_HOT(int, __preempt_count);
DECLARE_PER_CPU_CACHE_HOT(unsigned long, __preempt_count);
/* We use the MSB mostly because its available */
#define PREEMPT_NEED_RESCHED 0x80000000
/*
* We use the MSB for PREEMPT_NEED_RESCHED mostly because it is available.
*/
#define PREEMPT_NEED_RESCHED (~(((unsigned long)-1L) >> 1))
#ifdef CONFIG_HAS_SEPARATE_PREEMPT_RESCHED_BITS
#define __pc_dec "decq"
#define __pc_op(op, ...) raw_cpu_##op##_8(__VA_ARGS__)
#else
#define __pc_dec "decl"
#define __pc_op(op, ...) raw_cpu_##op##_4(__VA_ARGS__)
#endif
/*
* We use the PREEMPT_NEED_RESCHED bit as an inverted NEED_RESCHED such
@@ -24,18 +34,26 @@ DECLARE_PER_CPU_CACHE_HOT(int, __preempt_count);
*/
static __always_inline int preempt_count(void)
{
return raw_cpu_read_4(__preempt_count) & ~PREEMPT_NEED_RESCHED;
return __pc_op(read, __preempt_count) & ~PREEMPT_NEED_RESCHED;
}
static __always_inline void preempt_count_set(int pc)
/*
* unsigned long preempt count parameter works for both 32bit and 64bit cases:
*
* - For 32bit, "int" (the return of preempt_count()) and "unsigned long" have
* the same size.
* - For 64bit, the effective bits of a preempt count sit in 32bit, and we
* preserve the NEED_RESCHED bit from the old count.
*/
static __always_inline void preempt_count_set(unsigned long pc)
{
int old, new;
unsigned long old, new;
old = raw_cpu_read_4(__preempt_count);
old = __pc_op(read, __preempt_count);
do {
new = (old & PREEMPT_NEED_RESCHED) |
(pc & ~PREEMPT_NEED_RESCHED);
} while (!raw_cpu_try_cmpxchg_4(__preempt_count, &old, new));
} while (!__pc_op(try_cmpxchg, __preempt_count, &old, new));
}
/*
@@ -58,17 +76,17 @@ static __always_inline void preempt_count_set(int pc)
static __always_inline void set_preempt_need_resched(void)
{
raw_cpu_and_4(__preempt_count, ~PREEMPT_NEED_RESCHED);
__pc_op(and, __preempt_count, ~PREEMPT_NEED_RESCHED);
}
static __always_inline void clear_preempt_need_resched(void)
{
raw_cpu_or_4(__preempt_count, PREEMPT_NEED_RESCHED);
__pc_op(or, __preempt_count, PREEMPT_NEED_RESCHED);
}
static __always_inline bool test_preempt_need_resched(void)
{
return !(raw_cpu_read_4(__preempt_count) & PREEMPT_NEED_RESCHED);
return !(__pc_op(read, __preempt_count) & PREEMPT_NEED_RESCHED);
}
/*
@@ -77,12 +95,22 @@ static __always_inline bool test_preempt_need_resched(void)
static __always_inline void __preempt_count_add(int val)
{
raw_cpu_add_4(__preempt_count, val);
__pc_op(add, __preempt_count, val);
}
static __always_inline void __preempt_count_sub(int val)
{
raw_cpu_add_4(__preempt_count, -val);
__pc_op(add, __preempt_count, -val);
}
static __always_inline int __preempt_count_add_return(int val)
{
return __pc_op(add_return, __preempt_count, val);
}
static __always_inline int __preempt_count_sub_return(int val)
{
return __pc_op(add_return, __preempt_count, -val);
}
/*
@@ -92,7 +120,7 @@ static __always_inline void __preempt_count_sub(int val)
*/
static __always_inline bool __preempt_count_dec_and_test(void)
{
return GEN_UNARY_RMWcc("decl", __my_cpu_var(__preempt_count), e,
return GEN_UNARY_RMWcc(__pc_dec, __my_cpu_var(__preempt_count), e,
__percpu_arg([var]));
}
@@ -101,7 +129,7 @@ static __always_inline bool __preempt_count_dec_and_test(void)
*/
static __always_inline bool should_resched(int preempt_offset)
{
return unlikely(raw_cpu_read_4(__preempt_count) == preempt_offset);
return unlikely(__pc_op(read, __preempt_count) == preempt_offset);
}
#ifdef CONFIG_PREEMPTION
@@ -148,4 +176,7 @@ do { \
#endif /* PREEMPTION */
#undef __pc_op
#undef __pc_dec
#endif /* __ASM_PREEMPT_H */

View File

@@ -41,6 +41,15 @@
:"+r" (__ret)); \
__ret; })
#define runtime_const_mask_32(val, sym) ({ \
typeof(0u+(val)) __ret = (val); \
asm_inline("and $0x12345678, %k0\n1:\n" \
".pushsection runtime_mask_" #sym ",\"a\"\n\t"\
".long 1b - 4 - .\n" \
".popsection" \
: "+r" (__ret)); \
__ret; })
#define runtime_const_init(type, sym) do { \
extern s32 __start_runtime_##type##_##sym[]; \
extern s32 __stop_runtime_##type##_##sym[]; \
@@ -65,6 +74,11 @@ static inline void __runtime_fixup_shift(void *where, unsigned long val)
*(unsigned char *)where = val;
}
static inline void __runtime_fixup_mask(void *where, unsigned long val)
{
*(unsigned int *)where = val;
}
static inline void runtime_const_fixup(void (*fn)(void *, unsigned long),
unsigned long val, s32 *start, s32 *end)
{

View File

@@ -2236,7 +2236,7 @@ DEFINE_PER_CPU_CACHE_HOT(struct task_struct *, current_task) = &init_task;
EXPORT_PER_CPU_SYMBOL(current_task);
EXPORT_PER_CPU_SYMBOL(const_current_task);
DEFINE_PER_CPU_CACHE_HOT(int, __preempt_count) = INIT_PREEMPT_COUNT;
DEFINE_PER_CPU_CACHE_HOT(unsigned long, __preempt_count) = INIT_PREEMPT_COUNT;
EXPORT_PER_CPU_SYMBOL(__preempt_count);
DEFINE_PER_CPU_CACHE_HOT(unsigned long, cpu_current_top_of_stack) = TOP_OF_INIT_STACK;

View File

@@ -1136,9 +1136,8 @@ void __init kvm_spinlock_init(void)
pr_info("PV spinlocks enabled\n");
__pv_init_lock_hash();
pv_ops_lock.queued_spin_lock_slowpath = __pv_queued_spin_lock_slowpath;
pv_ops_lock.queued_spin_unlock =
PV_CALLEE_SAVE(__pv_queued_spin_unlock);
static_call_update(queued_spin_lock_slowpath, __pv_queued_spin_lock_slowpath);
static_call_update(queued_spin_unlock, __raw_callee_save___pv_queued_spin_unlock);
pv_ops_lock.wait = kvm_wait;
pv_ops_lock.kick = kvm_kick_cpu;

View File

@@ -7,6 +7,7 @@
#include <linux/spinlock.h>
#include <linux/export.h>
#include <linux/jump_label.h>
#include <trace/events/lock.h>
DEFINE_STATIC_KEY_FALSE(virt_spin_lock_key);
@@ -25,10 +26,63 @@ __visible void __native_queued_spin_unlock(struct qspinlock *lock)
}
PV_CALLEE_SAVE_REGS_THUNK(__native_queued_spin_unlock);
DEFINE_STATIC_CALL(queued_spin_lock_slowpath, native_queued_spin_lock_slowpath);
EXPORT_STATIC_CALL_TRAMP(queued_spin_lock_slowpath);
DEFINE_STATIC_CALL(queued_spin_unlock, __raw_callee_save___native_queued_spin_unlock);
EXPORT_STATIC_CALL_TRAMP(queued_spin_unlock);
/*
* Traced unlock variants, swapped in via static_call while the
* contended_release tracepoint is enabled. Two of them, so each tail calls its
* own base directly.
*/
__visible void native_queued_spin_unlock_traced(struct qspinlock *lock)
{
if (queued_spin_is_contended(lock))
trace_call__contended_release(lock);
native_queued_spin_unlock(lock);
}
PV_CALLEE_SAVE_REGS_THUNK(native_queued_spin_unlock_traced);
__visible void pv_queued_spin_unlock_traced(struct qspinlock *lock)
{
if (queued_spin_is_contended(lock))
trace_call__contended_release(lock);
__raw_callee_save___pv_queued_spin_unlock(lock);
}
PV_CALLEE_SAVE_REGS_THUNK(pv_queued_spin_unlock_traced);
bool pv_is_native_spin_unlock(void)
{
return pv_ops_lock.queued_spin_unlock.func ==
__raw_callee_save___native_queued_spin_unlock;
void *unlock = static_call_query(queued_spin_unlock);
return unlock == __raw_callee_save___native_queued_spin_unlock ||
unlock == __raw_callee_save_native_queued_spin_unlock_traced;
}
int arch_contended_release_trace_reg(void)
{
void *cur = static_call_query(queued_spin_unlock);
if (cur == __raw_callee_save___native_queued_spin_unlock)
static_call_update(queued_spin_unlock,
__raw_callee_save_native_queued_spin_unlock_traced);
else if (cur == __raw_callee_save___pv_queued_spin_unlock)
static_call_update(queued_spin_unlock,
__raw_callee_save_pv_queued_spin_unlock_traced);
return 0;
}
void arch_contended_release_trace_unreg(void)
{
void *cur = static_call_query(queued_spin_unlock);
if (cur == __raw_callee_save_native_queued_spin_unlock_traced)
static_call_update(queued_spin_unlock,
__raw_callee_save___native_queued_spin_unlock);
else if (cur == __raw_callee_save_pv_queued_spin_unlock_traced)
static_call_update(queued_spin_unlock,
__raw_callee_save___pv_queued_spin_unlock);
}
__visible bool __native_vcpu_is_preempted(long cpu)
@@ -45,16 +99,11 @@ bool pv_is_native_vcpu_is_preempted(void)
void __init paravirt_set_cap(void)
{
if (!pv_is_native_spin_unlock())
setup_force_cpu_cap(X86_FEATURE_PVUNLOCK);
if (!pv_is_native_vcpu_is_preempted())
setup_force_cpu_cap(X86_FEATURE_VCPUPREEMPT);
}
struct pv_lock_ops pv_ops_lock = {
.queued_spin_lock_slowpath = native_queued_spin_lock_slowpath,
.queued_spin_unlock = PV_CALLEE_SAVE(__native_queued_spin_unlock),
.wait = paravirt_nop,
.kick = paravirt_nop,
.vcpu_is_preempted = PV_CALLEE_SAVE(__native_vcpu_is_preempted),

View File

@@ -4,6 +4,12 @@
#include <linux/bug.h>
#include <asm/text-patching.h>
/* Declared locally to avoid pulling asm/paravirt-spinlock.h header. */
#ifdef CONFIG_PARAVIRT_SPINLOCKS
struct qspinlock;
void __raw_callee_save___native_queued_spin_unlock(struct qspinlock *lock);
#endif
enum insn_type {
CALL = 0, /* site call */
NOP = 1, /* site cond-call */
@@ -31,6 +37,17 @@ static const u8 retinsn[] = { RET_INSN_OPCODE, 0xcc, 0xcc, 0xcc, 0xcc };
*/
static const u8 warninsn[] = { 0x67, 0x48, 0x0f, 0xb9, 0x3a };
#ifdef CONFIG_PARAVIRT_SPINLOCKS
/*
* ds ds movb $0, (_ASM_ARG1)
*/
#ifdef CONFIG_64BIT
static const u8 unlockinsn[] = { 0x3e, 0x3e, 0xc6, 0x07, 0x00 };
#else
static const u8 unlockinsn[] = { 0x3e, 0x3e, 0xc6, 0x00, 0x00 };
#endif
#endif
static u8 __is_Jcc(u8 *insn) /* Jcc.d32 */
{
u8 ret = 0;
@@ -78,6 +95,12 @@ static void __ref __static_call_transform(void *insn, enum insn_type type,
emulate = code;
code = &warninsn;
}
#ifdef CONFIG_PARAVIRT_SPINLOCKS
if (func == &__raw_callee_save___native_queued_spin_unlock) {
emulate = code;
code = &unlockinsn;
}
#endif
break;
case NOP:
@@ -139,6 +162,10 @@ static void __static_call_validate(u8 *insn, bool tail, bool tramp)
!memcmp(insn, xor5rax, 5) ||
!memcmp(insn, warninsn, 5))
return;
#ifdef CONFIG_PARAVIRT_SPINLOCKS
if (!memcmp(insn, unlockinsn, 5))
return;
#endif
}
/*

View File

@@ -134,9 +134,8 @@ void __init xen_init_spinlocks(void)
printk(KERN_DEBUG "xen: PV spinlocks enabled\n");
__pv_init_lock_hash();
pv_ops_lock.queued_spin_lock_slowpath = __pv_queued_spin_lock_slowpath;
pv_ops_lock.queued_spin_unlock =
PV_CALLEE_SAVE(__pv_queued_spin_unlock);
static_call_update(queued_spin_lock_slowpath, __pv_queued_spin_lock_slowpath);
static_call_update(queued_spin_unlock, __raw_callee_save___pv_queued_spin_unlock);
pv_ops_lock.wait = xen_qlock_wait;
pv_ops_lock.kick = xen_qlock_kick;
pv_ops_lock.vcpu_is_preempted = PV_CALLEE_SAVE(xen_vcpu_stolen);

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)
{
/*

View File

@@ -41,6 +41,7 @@
#include <asm-generic/qspinlock_types.h>
#include <linux/atomic.h>
#include <linux/tracepoint-defs.h>
#ifndef queued_spin_is_locked
/**
@@ -115,12 +116,12 @@ static __always_inline void queued_spin_lock(struct qspinlock *lock)
}
#endif
#ifndef queued_spin_unlock
#ifndef queued_spin_release
/**
* queued_spin_unlock - release a queued spinlock
* queued_spin_release - release a queued spinlock
* @lock : Pointer to queued spinlock structure
*/
static __always_inline void queued_spin_unlock(struct qspinlock *lock)
static __always_inline void queued_spin_release(struct qspinlock *lock)
{
/*
* unlock() needs release semantics:
@@ -129,6 +130,37 @@ static __always_inline void queued_spin_unlock(struct qspinlock *lock)
}
#endif
#ifndef queued_spin_unlock
DECLARE_TRACEPOINT(contended_release);
extern void queued_spin_release_traced(struct qspinlock *lock);
/**
* queued_spin_unlock - unlock a queued spinlock
* @lock : Pointer to queued spinlock structure
*
* Generic tracing wrapper around the arch-overridable
* queued_spin_release().
*/
static __always_inline void queued_spin_unlock(struct qspinlock *lock)
{
/*
* Trace and release are combined in queued_spin_release_traced() so
* the compiler does not need to preserve the lock pointer across the
* function call, avoiding callee-saved register save/restore on the
* hot path. queued_spin_release() is therefore called both here and in
* queued_spin_release_traced(). Keep the two in sync.
*/
if (IS_ENABLED(CONFIG_QUEUED_SPINLOCKS_TRACE_CONTENDED_RELEASE) &&
tracepoint_enabled(contended_release)) {
queued_spin_release_traced(lock);
return;
}
queued_spin_release(lock);
}
#endif
#ifndef virt_spin_lock
static __always_inline bool virt_spin_lock(struct qspinlock *lock)
{

View File

@@ -10,6 +10,7 @@
*/
#define runtime_const_ptr(sym) (sym)
#define runtime_const_shift_right_32(val, sym) ((u32)(val)>>(sym))
#define runtime_const_mask_32(val, sym) ((u32)(val)&(sym))
#define runtime_const_init(type,sym) do { } while (0)
#endif

View File

@@ -978,7 +978,10 @@
RUNTIME_CONST(ptr, __dentry_cache) \
RUNTIME_CONST(ptr, __names_cache) \
RUNTIME_CONST(ptr, __filp_cache) \
RUNTIME_CONST(ptr, __bfilp_cache)
RUNTIME_CONST(ptr, __bfilp_cache) \
RUNTIME_CONST(shift, __futex_shift) \
RUNTIME_CONST(mask, __futex_mask) \
RUNTIME_CONST(ptr, __futex_queues)
/* Alignment must be consistent with (kunit_suite *) in include/kunit/test.h */
#define KUNIT_TABLE() \

View File

@@ -92,6 +92,37 @@ void irq_exit_rcu(void);
#define arch_nmi_exit() do { } while (0)
#endif
#ifdef CONFIG_HAS_SEPARATE_PREEMPT_RESCHED_BITS
static __always_inline void __preempt_count_nmi_enter(void)
{
__preempt_count_add(NMI_OFFSET + HARDIRQ_OFFSET);
}
static __always_inline void __preempt_count_nmi_exit(void)
{
__preempt_count_sub(NMI_OFFSET + HARDIRQ_OFFSET);
}
#else
DECLARE_PER_CPU(unsigned int, nmi_nesting);
#define __preempt_count_nmi_enter() \
do { \
__preempt_count_add(HARDIRQ_OFFSET); \
/* Maximum NMI nesting is 15. */ \
BUG_ON(__this_cpu_read(nmi_nesting) >= 15); \
__this_cpu_inc(nmi_nesting); \
preempt_count_set(preempt_count() | NMI_MASK); \
} while (0)
#define __preempt_count_nmi_exit() \
do { \
__preempt_count_sub(HARDIRQ_OFFSET); \
if (!__this_cpu_dec_return(nmi_nesting)) \
preempt_count_set(preempt_count() & ~NMI_MASK); \
} while (0)
#endif
/*
* NMI vs Tracing
* --------------
@@ -102,21 +133,20 @@ void irq_exit_rcu(void);
*/
/*
* nmi_enter() can nest up to 15 times; see NMI_BITS.
* nmi_enter() can nest - nesting is tracked in a per-CPU counter.
*/
#define __nmi_enter() \
do { \
lockdep_off(); \
arch_nmi_enter(); \
BUG_ON(in_nmi() == NMI_MASK); \
__preempt_count_add(NMI_OFFSET + HARDIRQ_OFFSET); \
__preempt_count_nmi_enter(); \
} while (0)
#define nmi_enter() \
do { \
__nmi_enter(); \
lockdep_hardirq_enter(); \
ct_nmi_enter(); \
ct_nmi_enter(); \
instrumentation_begin(); \
ftrace_nmi_enter(); \
instrumentation_end(); \
@@ -125,7 +155,7 @@ void irq_exit_rcu(void);
#define __nmi_exit() \
do { \
BUG_ON(!in_nmi()); \
__preempt_count_sub(NMI_OFFSET + HARDIRQ_OFFSET); \
__preempt_count_nmi_exit(); \
arch_nmi_exit(); \
lockdep_on(); \
} while (0)

View File

@@ -0,0 +1,82 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __LINUX_INTERRUPT_RC_H
#define __LINUX_INTERRUPT_RC_H
/*
* include/linux/interrupt_rc.h - refcounted local processor interrupt
* management.
*
* Since the implementation of this API currently depends on
* local_irq_save()/local_irq_restore(), we split this into its own header to
* make it easier to include without hitting circular header dependencies.
*/
#include <linux/irqflags.h>
#include <linux/preempt.h>
#include <linux/processor.h>
#include <linux/smp.h>
#ifndef MODULE
/* Per-CPU interrupt disabling state for local_interrupt_{disable,enable}(). */
DECLARE_PER_CPU(unsigned long, local_interrupt_disable_state);
static __always_inline void __local_interrupt_disable(void)
{
unsigned long flags;
local_irq_save(flags);
raw_cpu_write(local_interrupt_disable_state, flags);
}
static __always_inline void __local_interrupt_enable(void)
{
unsigned long flags = raw_cpu_read(local_interrupt_disable_state);
local_irq_restore(flags);
}
#ifndef INSTANTIATE_EXPORTED_INTERRUPT_DISABLE
static __always_inline void _local_interrupt_disable(void)
{
__local_interrupt_disable();
}
static __always_inline void _local_interrupt_enable(void)
{
__local_interrupt_enable();
}
#else
extern void _local_interrupt_disable(void);
extern void _local_interrupt_enable(void);
#endif
#else /* !MODULE */
extern void _local_interrupt_disable(void);
extern void _local_interrupt_enable(void);
#endif /* !MODULE */
static inline void local_interrupt_disable(void)
{
int new_count;
WARN_ON_ONCE(in_nmi());
new_count = hardirq_disable_enter();
/* Interrupts can happen here, but it's OK, see __irq_exit_rcu(). */
if ((new_count & HARDIRQ_DISABLE_MASK) == HARDIRQ_DISABLE_OFFSET)
_local_interrupt_disable();
}
static inline void local_interrupt_enable(void)
{
int new_count;
new_count = hardirq_disable_exit();
if ((new_count & HARDIRQ_DISABLE_MASK) == 0)
_local_interrupt_enable();
}
#endif /* !__LINUX_INTERRUPT_RC_H */

View File

@@ -71,7 +71,7 @@
* Additional babbling in: Documentation/staging/static-keys.rst
*/
#ifndef __ASSEMBLY__
#ifndef __ASSEMBLER__
#include <linux/types.h>
#include <linux/compiler.h>
@@ -100,12 +100,12 @@ struct static_key {
#endif /* CONFIG_JUMP_LABEL */
};
#endif /* __ASSEMBLY__ */
#endif /* __ASSEMBLER__ */
#ifdef CONFIG_JUMP_LABEL
#include <asm/jump_label.h>
#ifndef __ASSEMBLY__
#ifndef __ASSEMBLER__
#ifdef CONFIG_HAVE_ARCH_JUMP_LABEL_RELATIVE
struct jump_entry {
@@ -180,7 +180,7 @@ static inline int jump_entry_size(struct jump_entry *entry)
#endif
#endif
#ifndef __ASSEMBLY__
#ifndef __ASSEMBLER__
enum jump_label_type {
JUMP_LABEL_NOP = 0,
@@ -524,6 +524,6 @@ extern bool ____wrong_branch_error(void);
#define static_branch_enable_cpuslocked(x) static_key_enable_cpuslocked(&(x)->key)
#define static_branch_disable_cpuslocked(x) static_key_disable_cpuslocked(&(x)->key)
#endif /* __ASSEMBLY__ */
#endif /* __ASSEMBLER__ */
#endif /* _LINUX_JUMP_LABEL_H */

View File

@@ -72,7 +72,7 @@ extern int dynamic_might_resched(void);
#ifdef CONFIG_DEBUG_ATOMIC_SLEEP
extern void __might_resched(const char *file, int line, unsigned int offsets);
extern void __might_sleep(const char *file, int line);
extern void __cant_sleep(const char *file, int line, int preempt_offset);
extern void __cant_sleep(const char *file, int line);
extern void __cant_migrate(const char *file, int line);
/**
@@ -95,7 +95,7 @@ extern void __cant_migrate(const char *file, int line);
* this macro will print a stack trace if it is executed with preemption enabled
*/
# define cant_sleep() \
do { __cant_sleep(__FILE__, __LINE__, 0); } while (0)
do { __cant_sleep(__FILE__, __LINE__); } while (0)
# define sched_annotate_sleep() (current->task_state_change = 0)
/**

View File

@@ -17,6 +17,9 @@
*
* - bits 0-7 are the preemption count (max preemption depth: 256)
* - bits 8-15 are the softirq count (max # of softirqs: 256)
* - bits 16-23 are the hardirq disable count (max # of hardirq disable: 256)
* - bits 24-27 are the hardirq count (max # of hardirqs: 16)
* - bit 28 is the NMI flag (no nesting count, tracked separately)
*
* The hardirq count could in theory be the same as the number of
* interrupts in the system, but we run all interrupt handlers with
@@ -24,31 +27,56 @@
* there are a few palaeontologic drivers which reenable interrupts in
* the handler, so we need more than one bit here.
*
* NMI nesting depth is tracked in a separate per-CPU variable
* (nmi_nesting) to save bits in preempt_count.
*
* PREEMPT_MASK: 0x000000ff
* SOFTIRQ_MASK: 0x0000ff00
* HARDIRQ_MASK: 0x000f0000
* NMI_MASK: 0x00f00000
* HARDIRQ_DISABLE_MASK: 0x00ff0000
* HARDIRQ_MASK: 0x0f000000
*
* When HAS_SEPARATE_PREEMPT_RESCHED_BITS=y, PREEMPT_NEED_RESCHED is put in a
* separate word and that allows 64bit load-store architectures to 'set'
* PREEMPT_NEED_RESCHED without messing up the otherwise symmetric
* modifications used on preempt_count and still load the whole thing
* (single-copy) atomically, without having to resort to full atomic
* operations.
*
* Because of the above, NMI_MASK bits are different depending on
* HAS_SEPARATE_PREEMPT_RESCHED_BITS:
*
* - HAS_SEPARATE_PREEMPT_RESCHED_BITS=n:
*
* NMI_MASK: 0x10000000
* PREEMPT_NEED_RESCHED: 0x80000000
*
* - HAS_SEPARATE_PREEMPT_RESCHED_BITS=y:
* NMI_MASK: 0xf0000000
* (PREEMPT_NEED_RESCHED is in a different word)
*/
#define PREEMPT_BITS 8
#define SOFTIRQ_BITS 8
#define HARDIRQ_DISABLE_BITS 8
#define HARDIRQ_BITS 4
#define NMI_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 HARDIRQ_SHIFT (SOFTIRQ_SHIFT + SOFTIRQ_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 __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 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 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)
@@ -105,8 +133,8 @@ static __always_inline unsigned char interrupt_context_level(void)
* preempt_count() is commonly implemented with READ_ONCE().
*/
#define nmi_count() (preempt_count() & NMI_MASK)
#define hardirq_count() (preempt_count() & HARDIRQ_MASK)
#define nmi_count() (preempt_count() & NMI_MASK)
#define hardirq_count() (preempt_count() & HARDIRQ_MASK)
#ifdef CONFIG_PREEMPT_RT
# define softirq_count() (current->softirq_disable_cnt & SOFTIRQ_MASK)
# define irq_count() ((preempt_count() & (NMI_MASK | HARDIRQ_MASK)) | softirq_count())
@@ -140,6 +168,10 @@ static __always_inline unsigned char interrupt_context_level(void)
#define in_softirq() (softirq_count())
#define in_interrupt() (irq_count())
#define hardirq_disable_count() ((preempt_count() & HARDIRQ_DISABLE_MASK) >> HARDIRQ_DISABLE_SHIFT)
#define hardirq_disable_enter() __preempt_count_add_return(HARDIRQ_DISABLE_OFFSET)
#define hardirq_disable_exit() __preempt_count_sub_return(HARDIRQ_DISABLE_OFFSET)
/*
* The preempt_count offset after preempt_disable();
*/

View File

@@ -57,6 +57,7 @@
#include <linux/linkage.h>
#include <linux/compiler.h>
#include <linux/irqflags.h>
#include <linux/interrupt_rc.h>
#include <linux/thread_info.h>
#include <linux/stringify.h>
#include <linux/bottom_half.h>
@@ -273,9 +274,11 @@ static inline void do_raw_spin_unlock(raw_spinlock_t *lock) __releases(lock)
#endif
#define raw_spin_lock_irq(lock) _raw_spin_lock_irq(lock)
#define raw_spin_lock_irq_disable(lock) _raw_spin_lock_irq_disable(lock)
#define raw_spin_lock_bh(lock) _raw_spin_lock_bh(lock)
#define raw_spin_unlock(lock) _raw_spin_unlock(lock)
#define raw_spin_unlock_irq(lock) _raw_spin_unlock_irq(lock)
#define raw_spin_unlock_irq_enable(lock) _raw_spin_unlock_irq_enable(lock)
#define raw_spin_unlock_irqrestore(lock, flags) \
do { \
@@ -290,6 +293,8 @@ static inline void do_raw_spin_unlock(raw_spinlock_t *lock) __releases(lock)
#define raw_spin_trylock_irqsave(lock, flags) _raw_spin_trylock_irqsave(lock, &(flags))
#define raw_spin_trylock_irq_disable(lock) _raw_spin_trylock_irq_disable(lock)
#ifndef CONFIG_PREEMPT_RT
/* Include rwlock functions for !RT */
#include <linux/rwlock.h>
@@ -372,6 +377,12 @@ static __always_inline void spin_lock_irq(spinlock_t *lock)
raw_spin_lock_irq(&lock->rlock);
}
static __always_inline void spin_lock_irq_disable(spinlock_t *lock)
__acquires(lock) __no_context_analysis
{
raw_spin_lock_irq_disable(&lock->rlock);
}
#define spin_lock_irqsave(lock, flags) \
do { \
raw_spin_lock_irqsave(spinlock_check(lock), flags); \
@@ -402,6 +413,12 @@ static __always_inline void spin_unlock_irq(spinlock_t *lock)
raw_spin_unlock_irq(&lock->rlock);
}
static __always_inline void spin_unlock_irq_enable(spinlock_t *lock)
__releases(lock) __no_context_analysis
{
raw_spin_unlock_irq_enable(&lock->rlock);
}
static __always_inline void spin_unlock_irqrestore(spinlock_t *lock, unsigned long flags)
__releases(lock) __no_context_analysis
{
@@ -427,6 +444,12 @@ static __always_inline bool _spin_trylock_irqsave(spinlock_t *lock, unsigned lon
}
#define spin_trylock_irqsave(lock, flags) _spin_trylock_irqsave(lock, &(flags))
static __always_inline int spin_trylock_irq_disable(spinlock_t *lock)
__cond_acquires(true, lock) __no_context_analysis
{
return raw_spin_trylock_irq_disable(&lock->rlock);
}
/**
* spin_is_locked() - Check whether a spinlock is locked.
* @lock: Pointer to the spinlock.
@@ -549,12 +572,12 @@ DECLARE_LOCK_GUARD_1_ATTRS(raw_spinlock_nested, __acquires(_T), __releases(*(raw
#define class_raw_spinlock_nested_constructor(_T) WITH_LOCK_GUARD_1_ATTRS(raw_spinlock_nested, _T)
DEFINE_LOCK_GUARD_1(raw_spinlock_irq, raw_spinlock_t,
raw_spin_lock_irq(_T->lock),
raw_spin_unlock_irq(_T->lock))
raw_spin_lock_irq_disable(_T->lock),
raw_spin_unlock_irq_enable(_T->lock))
DECLARE_LOCK_GUARD_1_ATTRS(raw_spinlock_irq, __acquires(_T), __releases(*(raw_spinlock_t **)_T))
#define class_raw_spinlock_irq_constructor(_T) WITH_LOCK_GUARD_1_ATTRS(raw_spinlock_irq, _T)
DEFINE_LOCK_GUARD_1_COND(raw_spinlock_irq, _try, raw_spin_trylock_irq(_T->lock))
DEFINE_LOCK_GUARD_1_COND(raw_spinlock_irq, _try, raw_spin_trylock_irq_disable(_T->lock))
DECLARE_LOCK_GUARD_1_ATTRS(raw_spinlock_irq_try, __acquires(_T), __releases(*(raw_spinlock_t **)_T))
#define class_raw_spinlock_irq_try_constructor(_T) WITH_LOCK_GUARD_1_ATTRS(raw_spinlock_irq_try, _T)
@@ -569,14 +592,13 @@ DECLARE_LOCK_GUARD_1_ATTRS(raw_spinlock_bh_try, __acquires(_T), __releases(*(raw
#define class_raw_spinlock_bh_try_constructor(_T) WITH_LOCK_GUARD_1_ATTRS(raw_spinlock_bh_try, _T)
DEFINE_LOCK_GUARD_1(raw_spinlock_irqsave, raw_spinlock_t,
raw_spin_lock_irqsave(_T->lock, _T->flags),
raw_spin_unlock_irqrestore(_T->lock, _T->flags),
unsigned long flags)
raw_spin_lock_irq_disable(_T->lock),
raw_spin_unlock_irq_enable(_T->lock))
DECLARE_LOCK_GUARD_1_ATTRS(raw_spinlock_irqsave, __acquires(_T), __releases(*(raw_spinlock_t **)_T))
#define class_raw_spinlock_irqsave_constructor(_T) WITH_LOCK_GUARD_1_ATTRS(raw_spinlock_irqsave, _T)
DEFINE_LOCK_GUARD_1_COND(raw_spinlock_irqsave, _try,
raw_spin_trylock_irqsave(_T->lock, _T->flags))
raw_spin_trylock_irq_disable(_T->lock))
DECLARE_LOCK_GUARD_1_ATTRS(raw_spinlock_irqsave_try, __acquires(_T), __releases(*(raw_spinlock_t **)_T))
#define class_raw_spinlock_irqsave_try_constructor(_T) WITH_LOCK_GUARD_1_ATTRS(raw_spinlock_irqsave_try, _T)
@@ -595,13 +617,13 @@ DECLARE_LOCK_GUARD_1_ATTRS(spinlock_try, __acquires(_T), __releases(*(spinlock_t
#define class_spinlock_try_constructor(_T) WITH_LOCK_GUARD_1_ATTRS(spinlock_try, _T)
DEFINE_LOCK_GUARD_1(spinlock_irq, spinlock_t,
spin_lock_irq(_T->lock),
spin_unlock_irq(_T->lock))
spin_lock_irq_disable(_T->lock),
spin_unlock_irq_enable(_T->lock))
DECLARE_LOCK_GUARD_1_ATTRS(spinlock_irq, __acquires(_T), __releases(*(spinlock_t **)_T))
#define class_spinlock_irq_constructor(_T) WITH_LOCK_GUARD_1_ATTRS(spinlock_irq, _T)
DEFINE_LOCK_GUARD_1_COND(spinlock_irq, _try,
spin_trylock_irq(_T->lock))
spin_trylock_irq_disable(_T->lock))
DECLARE_LOCK_GUARD_1_ATTRS(spinlock_irq_try, __acquires(_T), __releases(*(spinlock_t **)_T))
#define class_spinlock_irq_try_constructor(_T) WITH_LOCK_GUARD_1_ATTRS(spinlock_irq_try, _T)
@@ -617,14 +639,13 @@ DECLARE_LOCK_GUARD_1_ATTRS(spinlock_bh_try, __acquires(_T), __releases(*(spinloc
#define class_spinlock_bh_try_constructor(_T) WITH_LOCK_GUARD_1_ATTRS(spinlock_bh_try, _T)
DEFINE_LOCK_GUARD_1(spinlock_irqsave, spinlock_t,
spin_lock_irqsave(_T->lock, _T->flags),
spin_unlock_irqrestore(_T->lock, _T->flags),
unsigned long flags)
spin_lock_irq_disable(_T->lock),
spin_unlock_irq_enable(_T->lock))
DECLARE_LOCK_GUARD_1_ATTRS(spinlock_irqsave, __acquires(_T), __releases(*(spinlock_t **)_T))
#define class_spinlock_irqsave_constructor(_T) WITH_LOCK_GUARD_1_ATTRS(spinlock_irqsave, _T)
DEFINE_LOCK_GUARD_1_COND(spinlock_irqsave, _try,
spin_trylock_irqsave(_T->lock, _T->flags))
spin_trylock_irq_disable(_T->lock))
DECLARE_LOCK_GUARD_1_ATTRS(spinlock_irqsave_try, __acquires(_T), __releases(*(spinlock_t **)_T))
#define class_spinlock_irqsave_try_constructor(_T) WITH_LOCK_GUARD_1_ATTRS(spinlock_irqsave_try, _T)

View File

@@ -28,6 +28,8 @@ _raw_spin_lock_nest_lock(raw_spinlock_t *lock, struct lockdep_map *map)
void __lockfunc _raw_spin_lock_bh(raw_spinlock_t *lock) __acquires(lock);
void __lockfunc _raw_spin_lock_irq(raw_spinlock_t *lock)
__acquires(lock);
void __lockfunc _raw_spin_lock_irq_disable(raw_spinlock_t *lock)
__acquires(lock);
unsigned long __lockfunc _raw_spin_lock_irqsave(raw_spinlock_t *lock)
__acquires(lock);
@@ -39,6 +41,7 @@ int __lockfunc _raw_spin_trylock_bh(raw_spinlock_t *lock) __cond_acquires(true,
void __lockfunc _raw_spin_unlock(raw_spinlock_t *lock) __releases(lock);
void __lockfunc _raw_spin_unlock_bh(raw_spinlock_t *lock) __releases(lock);
void __lockfunc _raw_spin_unlock_irq(raw_spinlock_t *lock) __releases(lock);
void __lockfunc _raw_spin_unlock_irq_enable(raw_spinlock_t *lock) __releases(lock);
void __lockfunc
_raw_spin_unlock_irqrestore(raw_spinlock_t *lock, unsigned long flags)
__releases(lock);
@@ -55,6 +58,11 @@ _raw_spin_unlock_irqrestore(raw_spinlock_t *lock, unsigned long flags)
#define _raw_spin_lock_irq(lock) __raw_spin_lock_irq(lock)
#endif
/* Use the same config as spin_lock_irq() temporarily. */
#ifdef CONFIG_INLINE_SPIN_LOCK_IRQ
#define _raw_spin_lock_irq_disable(lock) __raw_spin_lock_irq_disable(lock)
#endif
#ifdef CONFIG_INLINE_SPIN_LOCK_IRQSAVE
#define _raw_spin_lock_irqsave(lock) __raw_spin_lock_irqsave(lock)
#endif
@@ -79,6 +87,11 @@ _raw_spin_unlock_irqrestore(raw_spinlock_t *lock, unsigned long flags)
#define _raw_spin_unlock_irq(lock) __raw_spin_unlock_irq(lock)
#endif
/* Use the same config as spin_unlock_irq() temporarily. */
#ifdef CONFIG_INLINE_SPIN_UNLOCK_IRQ
#define _raw_spin_unlock_irq_enable(lock) __raw_spin_unlock_irq_enable(lock)
#endif
#ifdef CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE
#define _raw_spin_unlock_irqrestore(lock, flags) __raw_spin_unlock_irqrestore(lock, flags)
#endif
@@ -105,6 +118,16 @@ static __always_inline bool _raw_spin_trylock_irq(raw_spinlock_t *lock)
return false;
}
static __always_inline bool _raw_spin_trylock_irq_disable(raw_spinlock_t *lock)
__cond_acquires(true, lock)
{
local_interrupt_disable();
if (_raw_spin_trylock(lock))
return true;
local_interrupt_enable();
return false;
}
static __always_inline bool _raw_spin_trylock_irqsave(raw_spinlock_t *lock, unsigned long *flags)
__cond_acquires(true, lock)
{
@@ -143,6 +166,15 @@ static inline void __raw_spin_lock_irq(raw_spinlock_t *lock)
LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock);
}
static inline void __raw_spin_lock_irq_disable(raw_spinlock_t *lock)
__acquires(lock) __no_context_analysis
{
local_interrupt_disable();
preempt_disable();
spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);
LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock);
}
static inline void __raw_spin_lock_bh(raw_spinlock_t *lock)
__acquires(lock) __no_context_analysis
{
@@ -188,6 +220,15 @@ static inline void __raw_spin_unlock_irq(raw_spinlock_t *lock)
preempt_enable();
}
static inline void __raw_spin_unlock_irq_enable(raw_spinlock_t *lock)
__releases(lock)
{
spin_release(&lock->dep_map, _RET_IP_);
do_raw_spin_unlock(lock);
local_interrupt_enable();
preempt_enable();
}
static inline void __raw_spin_unlock_bh(raw_spinlock_t *lock)
__releases(lock)
{

View File

@@ -42,6 +42,9 @@
#define __LOCK_IRQSAVE(lock, flags, ...) \
do { local_irq_save(flags); __LOCK(lock, ##__VA_ARGS__); } while (0)
#define __LOCK_IRQ_DISABLE(lock, ...) \
do { local_interrupt_disable(); __LOCK(lock, ##__VA_ARGS__); } while (0)
#define ___UNLOCK_(lock) \
do { __release(lock); (void)(lock); } while (0)
@@ -61,6 +64,9 @@
#define __UNLOCK_IRQRESTORE(lock, flags, ...) \
do { local_irq_restore(flags); __UNLOCK(lock, ##__VA_ARGS__); } while (0)
#define __UNLOCK_IRQ_ENABLE(lock, ...) \
do { __UNLOCK(lock, ##__VA_ARGS__); local_interrupt_enable(); } while (0)
#define _raw_spin_lock(lock) __LOCK(lock)
#define _raw_spin_lock_nested(lock, subclass) __LOCK(lock)
#define _raw_read_lock(lock) __LOCK(lock, shared)
@@ -70,6 +76,7 @@
#define _raw_read_lock_bh(lock) __LOCK_BH(lock, shared)
#define _raw_write_lock_bh(lock) __LOCK_BH(lock)
#define _raw_spin_lock_irq(lock) __LOCK_IRQ(lock)
#define _raw_spin_lock_irq_disable(lock) __LOCK_IRQ_DISABLE(lock)
#define _raw_read_lock_irq(lock) __LOCK_IRQ(lock, shared)
#define _raw_write_lock_irq(lock) __LOCK_IRQ(lock)
#define _raw_spin_lock_irqsave(lock, flags) __LOCK_IRQSAVE(lock, flags)
@@ -97,6 +104,13 @@ static __always_inline int _raw_spin_trylock_irq(raw_spinlock_t *lock)
return 1;
}
static __always_inline int _raw_spin_trylock_irq_disable(raw_spinlock_t *lock)
__cond_acquires(true, lock)
{
__LOCK_IRQ_DISABLE(lock);
return 1;
}
static __always_inline int _raw_spin_trylock_irqsave(raw_spinlock_t *lock, unsigned long *flags)
__cond_acquires(true, lock)
{
@@ -132,6 +146,7 @@ static __always_inline int _raw_write_trylock_irqsave(rwlock_t *lock, unsigned l
#define _raw_write_unlock_bh(lock) __UNLOCK_BH(lock)
#define _raw_read_unlock_bh(lock) __UNLOCK_BH(lock, shared)
#define _raw_spin_unlock_irq(lock) __UNLOCK_IRQ(lock)
#define _raw_spin_unlock_irq_enable(lock) __UNLOCK_IRQ_ENABLE(lock)
#define _raw_read_unlock_irq(lock) __UNLOCK_IRQ(lock, shared)
#define _raw_write_unlock_irq(lock) __UNLOCK_IRQ(lock)
#define _raw_spin_unlock_irqrestore(lock, flags) \

View File

@@ -96,6 +96,12 @@ static __always_inline void spin_lock_irq(spinlock_t *lock)
rt_spin_lock(lock);
}
static __always_inline void spin_lock_irq_disable(spinlock_t *lock)
__acquires(lock)
{
rt_spin_lock(lock);
}
#define spin_lock_irqsave(lock, flags) \
do { \
typecheck(unsigned long, flags); \
@@ -122,6 +128,12 @@ static __always_inline void spin_unlock_irq(spinlock_t *lock)
rt_spin_unlock(lock);
}
static __always_inline void spin_unlock_irq_enable(spinlock_t *lock)
__releases(lock)
{
rt_spin_unlock(lock);
}
static __always_inline void spin_unlock_irqrestore(spinlock_t *lock,
unsigned long flags)
__releases(lock)
@@ -131,6 +143,12 @@ static __always_inline void spin_unlock_irqrestore(spinlock_t *lock,
#define spin_trylock(lock) rt_spin_trylock(lock)
static __always_inline int spin_trylock_irq_disable(spinlock_t *lock)
__cond_acquires(true, lock)
{
return rt_spin_trylock(lock);
}
#define spin_trylock_bh(lock) rt_spin_trylock_bh(lock)
#define spin_trylock_irq(lock) rt_spin_trylock(lock)

View File

@@ -25,7 +25,7 @@
#define STATIC_CALL_SITE_INIT 2UL /* init section */
#define STATIC_CALL_SITE_FLAGS 3UL
#ifndef __ASSEMBLY__
#ifndef __ASSEMBLER__
/*
* The static call site table needs to be created by external tooling (objtool
@@ -102,6 +102,6 @@ struct static_call_key {
#endif /* CONFIG_HAVE_STATIC_CALL */
#endif /* __ASSEMBLY__ */
#endif /* __ASSEMBLER__ */
#endif /* _STATIC_CALL_TYPES_H */

View File

@@ -137,7 +137,11 @@ TRACE_EVENT(contention_end,
TP_printk("%p (ret=%d)", __entry->lock_addr, __entry->ret)
);
TRACE_EVENT(contended_release,
/* kernel/locking/mutex.c */
int arch_contended_release_trace_reg(void);
void arch_contended_release_trace_unreg(void);
TRACE_EVENT_FN(contended_release,
TP_PROTO(void *lock),
@@ -151,7 +155,9 @@ TRACE_EVENT(contended_release,
__entry->lock_addr = lock;
),
TP_printk("%p", __entry->lock_addr)
TP_printk("%p", __entry->lock_addr),
arch_contended_release_trace_reg, arch_contended_release_trace_unreg
);
#endif /* _TRACE_LOCK_H */

View File

@@ -243,6 +243,26 @@ config QUEUED_SPINLOCKS
def_bool y if ARCH_USE_QUEUED_SPINLOCKS
depends on SMP
config QUEUED_SPINLOCKS_TRACE_CONTENDED_RELEASE
bool "Trace contended_release on queued spinlocks"
depends on QUEUED_SPINLOCKS && TRACEPOINTS
help
Fire the lock:contended_release tracepoint when a contended queued
spinlock is released, so it is possible to attribute a contended
spinlock to its holder.
Architectures that can patch the unlock site do this at no cost and
do not need this option.
Everywhere else the check is compiled into queued_spin_unlock() and
a small cost is paid on every unlock even when the tracepoint is
disabled: a static-branch NOP and possibly a few more instructions
to manage a stack frame.
Sleeping locks fire lock:contended_release regardless of this option.
If unsure, say N.
config BPF_ARCH_SPINLOCK
bool

View File

@@ -122,6 +122,10 @@ config PREEMPT_RT_NEEDS_BH_LOCK
config PREEMPT_COUNT
bool
config HAS_SEPARATE_PREEMPT_RESCHED_BITS
bool
depends on PREEMPT_COUNT && 64BIT
config PREEMPTION
bool
select PREEMPT_COUNT

View File

@@ -45,26 +45,23 @@
#include <linux/rseq.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/kmemleak.h>
#include <vdso/futex.h>
#include <asm/runtime-const.h>
#include "futex.h"
#include "../locking/rtmutex_common.h"
/*
* The base of the bucket array and its size are always used together
* (after initialization only in futex_hash()), so ensure that they
* reside in the same cacheline.
*/
static struct {
unsigned long hashmask;
unsigned int hashshift;
struct futex_hash_bucket *queues[MAX_NUMNODES];
} __futex_data __read_mostly __aligned(2*sizeof(long));
static u32 __futex_mask __ro_after_init;
static u32 __futex_shift __ro_after_init;
static struct futex_hash_bucket **__futex_queues __ro_after_init;
#define futex_hashmask (__futex_data.hashmask)
#define futex_hashshift (__futex_data.hashshift)
#define futex_queues (__futex_data.queues)
static __always_inline struct futex_hash_bucket **futex_queues(void)
{
return runtime_const_ptr(__futex_queues);
}
struct futex_private_hash {
int state;
@@ -143,8 +140,14 @@ static bool futex_private_hash_get(struct futex_private_hash *fph)
void futex_private_hash_put(struct futex_private_hash *fph)
{
if (fph && futex_ref_put(fph))
wake_up_var(fph->mm);
struct mm_struct *mm;
if (!fph)
return;
mm = fph->mm;
if (futex_ref_put(fph))
wake_up_var(mm);
}
static struct futex_hash_bucket *
@@ -395,13 +398,13 @@ __futex_hash(union futex_key *key, struct futex_private_hash *fph, struct futex_
* NOTE: this isn't perfectly uniform, but it is fast and
* handles sparse node masks.
*/
node = (hash >> futex_hashshift) % nr_node_ids;
node = runtime_const_shift_right_32(hash, __futex_shift) % nr_node_ids;
if (!node_possible(node)) {
node = find_next_bit_wrap(node_possible_map.bits, nr_node_ids, node);
}
}
return &futex_queues[node][hash & futex_hashmask];
return &futex_queues()[node][runtime_const_mask_32(hash, __futex_mask)];
}
/**
@@ -520,7 +523,7 @@ int get_futex_key(u32 __user *uaddr, unsigned int flags, union futex_key *key,
* The futex address must be "naturally" aligned.
*/
key->both.offset = address % PAGE_SIZE;
if (unlikely((address % size) != 0))
if (unlikely((address & (size-1)) != 0))
return -EINVAL;
address -= key->both.offset;
@@ -1954,7 +1957,7 @@ int futex_hash_allocate_default(void)
* 16 <= threads * 4 <= global hash size
*/
buckets = roundup_pow_of_two(4 * threads);
buckets = clamp(buckets, 16, futex_hashmask + 1);
buckets = clamp(buckets, 16, __futex_mask + 1);
if (current_buckets >= buckets)
return 0;
@@ -2052,10 +2055,22 @@ static int __init futex_init(void)
hashsize = max(4, hashsize);
hashsize = roundup_pow_of_two(hashsize);
#endif
futex_hashshift = ilog2(hashsize);
__futex_mask = hashsize - 1;
__futex_shift = ilog2(hashsize);
size = sizeof(struct futex_hash_bucket) * hashsize;
order = get_order(size);
__futex_queues = kcalloc(nr_node_ids, sizeof(*__futex_queues), GFP_KERNEL);
kmemleak_not_leak(__futex_queues);
runtime_const_init(shift, __futex_shift);
runtime_const_init(mask, __futex_mask);
runtime_const_init(ptr, __futex_queues);
barrier();
BUG_ON(!futex_queues());
for_each_node(n) {
struct futex_hash_bucket *table;
@@ -2069,10 +2084,9 @@ static int __init futex_init(void)
for (i = 0; i < hashsize; i++)
futex_hash_bucket_init(&table[i]);
futex_queues[n] = table;
futex_queues()[n] = table;
}
futex_hashmask = hashsize - 1;
pr_info("futex hash table entries: %lu (%lu bytes on %d NUMA nodes, total %lu KiB, %s).\n",
hashsize, size, num_possible_nodes(), size * num_possible_nodes() / 1024,
order > MAX_PAGE_ORDER ? "vmalloc" : "linear");

View File

@@ -16,3 +16,4 @@ obj-$(CONFIG_SMP) += affinity.o
obj-$(CONFIG_GENERIC_IRQ_DEBUGFS) += debugfs.o
obj-$(CONFIG_GENERIC_IRQ_MATRIX_ALLOCATOR) += matrix.o
obj-$(CONFIG_IRQ_KUNIT_TEST) += irq_test.o
obj-$(CONFIG_KUNIT) += refcount_interrupt_test.o

View File

@@ -0,0 +1,109 @@
// SPDX-License-Identifier: GPL-2.0
/*
* KUnit test for refcounted interrupt enable/disables.
*/
#include <kunit/test.h>
#include <linux/interrupt_rc.h>
#define TEST_IRQ_ON() KUNIT_EXPECT_FALSE(test, irqs_disabled())
#define TEST_IRQ_OFF() KUNIT_EXPECT_TRUE(test, irqs_disabled())
/* ===== Test cases ===== */
static void test_single_irq_change(struct kunit *test)
{
local_interrupt_disable();
TEST_IRQ_OFF();
local_interrupt_enable();
}
static void test_nested_irq_change(struct kunit *test)
{
local_interrupt_disable();
TEST_IRQ_OFF();
local_interrupt_disable();
TEST_IRQ_OFF();
local_interrupt_disable();
TEST_IRQ_OFF();
local_interrupt_enable();
TEST_IRQ_OFF();
local_interrupt_enable();
TEST_IRQ_OFF();
local_interrupt_enable();
TEST_IRQ_ON();
}
static void test_multiple_irq_change(struct kunit *test)
{
local_interrupt_disable();
TEST_IRQ_OFF();
local_interrupt_disable();
TEST_IRQ_OFF();
local_interrupt_enable();
TEST_IRQ_OFF();
local_interrupt_enable();
TEST_IRQ_ON();
local_interrupt_disable();
TEST_IRQ_OFF();
local_interrupt_enable();
TEST_IRQ_ON();
}
static void test_irq_save(struct kunit *test)
{
unsigned long flags;
local_irq_save(flags);
TEST_IRQ_OFF();
local_interrupt_disable();
TEST_IRQ_OFF();
local_interrupt_enable();
TEST_IRQ_OFF();
local_irq_restore(flags);
TEST_IRQ_ON();
local_interrupt_disable();
TEST_IRQ_OFF();
local_irq_save(flags);
TEST_IRQ_OFF();
local_irq_restore(flags);
TEST_IRQ_OFF();
local_interrupt_enable();
TEST_IRQ_ON();
}
static struct kunit_case test_cases[] = {
KUNIT_CASE(test_single_irq_change),
KUNIT_CASE(test_nested_irq_change),
KUNIT_CASE(test_multiple_irq_change),
KUNIT_CASE(test_irq_save),
{},
};
/* init and exit are the same. */
static int test_init(struct kunit *test)
{
TEST_IRQ_ON();
return 0;
}
static void test_exit(struct kunit *test)
{
TEST_IRQ_ON();
}
static struct kunit_suite refcount_interrupt_test_suite = {
.name = "refcount_interrupt",
.test_cases = test_cases,
.init = test_init,
.exit = test_exit,
};
kunit_test_suite(refcount_interrupt_test_suite);
MODULE_AUTHOR("Lyude Paul <lyude@redhat.com>");
MODULE_DESCRIPTION("Refcounted interrupt unit test suite");
MODULE_LICENSE("GPL");

View File

@@ -787,17 +787,33 @@ static void lockdep_print_held_locks(struct task_struct *p)
{
int i, depth = READ_ONCE(p->lockdep_depth);
if (!depth)
printk("no locks held by %s/%d.\n", p->comm, task_pid_nr(p));
else
printk("%d lock%s held by %s/%d:\n", depth,
str_plural(depth), p->comm, task_pid_nr(p));
/*
* It's not reliable to print a task's held locks if it's not sleeping
* and it's not the current task.
* Note that it's always somewhat unreliable to print held locks
* of a task that is running on another CPU, but we cannot guarantee
* the stability of ->held_locks without actually stopping all active
* remote CPUs, which we absolutely do not want to do because it's
* very intrusive and thus slow.
*
* So we do the next best thing here: we print out the held lock
* array on a best-effort basis, without crashing even if the
* fields are being modified on another CPU. Note the careful
* construction of print_lock() so that it never crashes.
*
* We also print out the CPU the task is or was last running on, with
* the message saying 'on CPU...' if the task is running, and
* 'last CPU' if it's not.
*
* Also note that the task_is_running(p) information is fundamentally
* racy: even if the message says the task is 'on CPU', the task may
* have scheduled out already, or if it says 'last CPU', it may just
* have scheduled in on another CPU. But even with these limitations
* it's still useful debuggining information.
*/
if (p != current && task_is_running(p))
return;
printk("locks held by %s/%d: %d, %s CPU#%d%s\n",
p->comm, task_pid_nr(p), depth,
task_is_running(p) ? "last" : "on", task_cpu(p),
depth > 0 ? ":" : "");
for (i = 0; i < depth; i++) {
printk(" #%d: ", i);
print_lock(p->held_locks + i);
@@ -5437,6 +5453,8 @@ __lock_set_class(struct lockdep_map *lock, const char *name,
lock->wait_type_outer,
lock->lock_type);
class = register_lock_class(lock, subclass, 0);
if (!class)
return 0;
hlock->class_idx = class - lock_classes;
curr->lockdep_depth = i;

View File

@@ -1272,6 +1272,10 @@ EXPORT_TRACEPOINT_SYMBOL_GPL(contention_begin);
EXPORT_TRACEPOINT_SYMBOL_GPL(contention_end);
EXPORT_TRACEPOINT_SYMBOL_GPL(contended_release);
__weak int arch_contended_release_trace_reg(void) { return 0; }
__weak void arch_contended_release_trace_unreg(void) { }
/**
* atomic_dec_and_mutex_lock - return holding mutex if we dec to 0
* @cnt: the atomic which we are to dec

View File

@@ -211,7 +211,7 @@ EXPORT_SYMBOL_GPL(percpu_is_read_locked);
*/
static bool readers_active_check(struct percpu_rw_semaphore *sem)
{
if (per_cpu_sum(*sem->read_count) != 0)
if (data_race(per_cpu_sum(*sem->read_count)) != 0)
return false;
/*

View File

@@ -104,6 +104,28 @@ static __always_inline u32 __pv_wait_head_or_lock(struct qspinlock *lock,
#define queued_spin_lock_slowpath native_queued_spin_lock_slowpath
#endif
#if !defined(queued_spin_unlock) && \
IS_ENABLED(CONFIG_QUEUED_SPINLOCKS_TRACE_CONTENDED_RELEASE)
/*
* Out-of-line trace-and-release path for queued_spin_unlock(), used when
* the contended_release tracepoint is enabled.
*
* queued_spin_release() is duplicated here on purpose: doing the release
* in this function (rather than tracing here and releasing in the caller)
* lets queued_spin_unlock() return right after the call, so the
* tracepoint-disabled hot path never has to keep lock live across a call
* in a callee-saved register. Keep this release in sync with the one in
* queued_spin_unlock().
*/
void __lockfunc queued_spin_release_traced(struct qspinlock *lock)
{
if (queued_spin_is_contended(lock))
trace_call__contended_release(lock);
queued_spin_release(lock);
}
EXPORT_SYMBOL(queued_spin_release_traced);
#endif
#endif /* _GEN_PV_LOCK_SLOWPATH */
/**

View File

@@ -129,6 +129,21 @@ static void __lockfunc __raw_##op##_lock_bh(locktype##_t *lock) \
*/
BUILD_LOCK_OPS(spin, raw_spinlock, __acquires);
/* No rwlock_t variants for now, so just build this function by hand */
static void __lockfunc __raw_spin_lock_irq_disable(raw_spinlock_t *lock)
{
for (;;) {
preempt_disable();
local_interrupt_disable();
if (likely(do_raw_spin_trylock(lock)))
break;
local_interrupt_enable();
preempt_enable();
arch_spin_relax(&lock->raw_lock);
}
}
#ifndef CONFIG_PREEMPT_RT
BUILD_LOCK_OPS(read, rwlock, __acquires_shared);
BUILD_LOCK_OPS(write, rwlock, __acquires);
@@ -176,6 +191,14 @@ noinline void __lockfunc _raw_spin_lock_irq(raw_spinlock_t *lock)
EXPORT_SYMBOL(_raw_spin_lock_irq);
#endif
#ifndef CONFIG_INLINE_SPIN_LOCK_IRQ
noinline void __lockfunc _raw_spin_lock_irq_disable(raw_spinlock_t *lock)
{
__raw_spin_lock_irq_disable(lock);
}
EXPORT_SYMBOL_GPL(_raw_spin_lock_irq_disable);
#endif
#ifndef CONFIG_INLINE_SPIN_LOCK_BH
noinline void __lockfunc _raw_spin_lock_bh(raw_spinlock_t *lock)
{
@@ -208,6 +231,14 @@ noinline void __lockfunc _raw_spin_unlock_irq(raw_spinlock_t *lock)
EXPORT_SYMBOL(_raw_spin_unlock_irq);
#endif
#ifndef CONFIG_INLINE_SPIN_UNLOCK_IRQ
noinline void __lockfunc _raw_spin_unlock_irq_enable(raw_spinlock_t *lock)
{
__raw_spin_unlock_irq_enable(lock);
}
EXPORT_SYMBOL_GPL(_raw_spin_unlock_irq_enable);
#endif
#ifndef CONFIG_INLINE_SPIN_UNLOCK_BH
noinline void __lockfunc _raw_spin_unlock_bh(raw_spinlock_t *lock)
{

View File

@@ -5973,8 +5973,13 @@ void preempt_count_add(int val)
#ifdef CONFIG_DEBUG_PREEMPT
/*
* Underflow?
*
* Cannot detect underflow based on the current preempt_count() value
* if using HAS_SEPARATE_PREEMPT_RESCHED_BITS because preempt count takes all 32
* bits.
*/
if (DEBUG_LOCKS_WARN_ON((preempt_count() < 0)))
if (!IS_ENABLED(CONFIG_HAS_SEPARATE_PREEMPT_RESCHED_BITS) &&
DEBUG_LOCKS_WARN_ON((preempt_count() < 0)))
return;
#endif
__preempt_count_add(val);
@@ -6006,7 +6011,10 @@ void preempt_count_sub(int val)
/*
* Underflow?
*/
if (DEBUG_LOCKS_WARN_ON(val > preempt_count()))
unsigned int uval = val;
unsigned int pc = preempt_count();
if (DEBUG_LOCKS_WARN_ON(pc - uval > pc))
return;
/*
* Is the spinlock portion underflowing?
@@ -9199,7 +9207,7 @@ void __might_resched(const char *file, int line, unsigned int offsets)
}
EXPORT_SYMBOL(__might_resched);
void __cant_sleep(const char *file, int line, int preempt_offset)
void __cant_sleep(const char *file, int line)
{
static unsigned long prev_jiffy;
@@ -9209,7 +9217,7 @@ void __cant_sleep(const char *file, int line, int preempt_offset)
if (!IS_ENABLED(CONFIG_PREEMPT_COUNT))
return;
if (preempt_count() > preempt_offset)
if (preempt_count())
return;
if (time_before(jiffies, prev_jiffy + HZ) && prev_jiffy)
@@ -9241,7 +9249,7 @@ void __cant_migrate(const char *file, int line)
if (!IS_ENABLED(CONFIG_PREEMPT_COUNT))
return;
if (preempt_count() > 0)
if (preempt_count())
return;
if (time_before(jiffies, prev_jiffy + HZ) && prev_jiffy)

View File

@@ -7120,7 +7120,7 @@ static bool distribute_cfs_runtime(struct cfs_bandwidth *cfs_b)
* period the timer is deactivated until scheduling resumes; cfs_b->idle is
* used to track this state.
*/
static int do_sched_cfs_period_timer(struct cfs_bandwidth *cfs_b, int overrun, unsigned long flags)
static int do_sched_cfs_period_timer(struct cfs_bandwidth *cfs_b, int overrun)
__must_hold(&cfs_b->lock)
{
int throttled;
@@ -7155,10 +7155,10 @@ static int do_sched_cfs_period_timer(struct cfs_bandwidth *cfs_b, int overrun, u
* This check is repeated as we release cfs_b->lock while we unthrottle.
*/
while (throttled && cfs_b->runtime > 0) {
raw_spin_unlock_irqrestore(&cfs_b->lock, flags);
raw_spin_unlock_irq_enable(&cfs_b->lock);
/* we can't nest cfs_b->lock while distributing bandwidth */
throttled = distribute_cfs_runtime(cfs_b);
raw_spin_lock_irqsave(&cfs_b->lock, flags);
raw_spin_lock_irq_disable(&cfs_b->lock);
}
/*
@@ -7266,7 +7266,7 @@ static __always_inline void return_cfs_rq_runtime(struct cfs_rq *cfs_rq)
static void do_sched_cfs_slack_timer(struct cfs_bandwidth *cfs_b)
{
/* confirm we're still not at a refresh boundary */
scoped_guard(raw_spinlock_irqsave, &cfs_b->lock) {
scoped_guard(raw_spinlock_irq, &cfs_b->lock) {
u64 runtime = 0, slice = sched_cfs_bandwidth_slice();
cfs_b->slack_started = false;
@@ -7351,14 +7351,14 @@ static enum hrtimer_restart sched_cfs_period_timer(struct hrtimer *timer)
int idle = 0;
int count = 0;
CLASS(raw_spinlock_irqsave, cfsb_guard)(&cfs_b->lock);
guard(raw_spinlock_irq)(&cfs_b->lock);
for (;;) {
overrun = hrtimer_forward_now(timer, cfs_b->period);
if (!overrun)
break;
idle = do_sched_cfs_period_timer(cfs_b, overrun, cfsb_guard.flags);
idle = do_sched_cfs_period_timer(cfs_b, overrun);
if (++count > 3) {
u64 new, old = ktime_to_ns(cfs_b->period);

View File

@@ -9,6 +9,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#define INSTANTIATE_EXPORTED_INTERRUPT_DISABLE
#include <linux/export.h>
#include <linux/kernel_stat.h>
#include <linux/interrupt.h>
@@ -88,6 +89,28 @@ EXPORT_PER_CPU_SYMBOL_GPL(hardirqs_enabled);
EXPORT_PER_CPU_SYMBOL_GPL(hardirq_context);
#endif
DEFINE_PER_CPU(unsigned long, local_interrupt_disable_state);
void _local_interrupt_disable(void)
{
__local_interrupt_disable();
}
EXPORT_SYMBOL(_local_interrupt_disable);
void _local_interrupt_enable(void)
{
__local_interrupt_enable();
}
EXPORT_SYMBOL(_local_interrupt_enable);
#ifndef CONFIG_HAS_SEPARATE_PREEMPT_RESCHED_BITS
/*
* Any 32bit architecture that still cares about performance should
* probably ensure this is near preempt_count.
*/
DEFINE_PER_CPU(unsigned int, nmi_nesting);
#endif
/*
* SOFTIRQ_OFFSET usage:
*
@@ -726,10 +749,19 @@ static inline void __irq_exit_rcu(void)
#endif
account_hardirq_exit(current);
preempt_count_sub(HARDIRQ_OFFSET);
if (!in_interrupt() && local_softirq_pending()) {
/*
* Interrupts may happen between hardirq_disable_enter() and
* local_irq_save() in local_interrupt_disable(), if irq_exit() invokes
* softirq here, we may have a softirq handler calling
* local_interrupt_disable() but it won't disable the IRQ because
* hardirq disabling count is already 1, hence we need to prevent
* invoking softirq when a local_interrupt_disable() is ongoing.
*/
if (!in_interrupt() && !hardirq_disable_count() &&
local_softirq_pending()) {
/*
* If we left hrtimers unarmed, make sure to arm them now,
* before enabling interrupts to run SoftIRQ.
* before enabling interrupts to run softirq.
*/
hrtimer_rearm_deferred();
invoke_softirq();

View File

@@ -1429,7 +1429,7 @@ static int unexpected_testcase_failures;
static void dotest(void (*testcase_fn)(void), int expected, int lockclass_mask)
{
int saved_preempt_count = preempt_count();
long saved_preempt_count = preempt_count();
#ifdef CONFIG_PREEMPT_RT
int saved_mgd_count = current->migration_disabled;
int saved_rcu_count = current->rcu_read_lock_nesting;

View File

@@ -2,6 +2,36 @@
#include <asm/barrier.h>
__rust_helper void rust_helper_mb(void)
{
mb();
}
__rust_helper void rust_helper_rmb(void)
{
rmb();
}
__rust_helper void rust_helper_wmb(void)
{
wmb();
}
__rust_helper void rust_helper_dma_mb(void)
{
dma_mb();
}
__rust_helper void rust_helper_dma_rmb(void)
{
dma_rmb();
}
__rust_helper void rust_helper_dma_wmb(void)
{
dma_wmb();
}
__rust_helper void rust_helper_smp_mb(void)
{
smp_mb();

View File

@@ -67,6 +67,7 @@
#include "irq.c"
#include "fs.c"
#include "gpu.c"
#include "interrupt.c"
#include "io.c"
#include "jump_label.c"
#include "kunit.c"

13
rust/helpers/interrupt.c Normal file
View File

@@ -0,0 +1,13 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/spinlock.h>
__rust_helper void rust_helper_local_interrupt_disable(void)
{
local_interrupt_disable();
}
__rust_helper void rust_helper_local_interrupt_enable(void)
{
local_interrupt_enable();
}

View File

@@ -36,3 +36,18 @@ __rust_helper void rust_helper_spin_assert_is_held(spinlock_t *lock)
{
lockdep_assert_held(lock);
}
__rust_helper void rust_helper_spin_lock_irq_disable(spinlock_t *lock)
{
spin_lock_irq_disable(lock);
}
__rust_helper void rust_helper_spin_unlock_irq_enable(spinlock_t *lock)
{
spin_unlock_irq_enable(lock);
}
__rust_helper int rust_helper_spin_trylock_irq_disable(spinlock_t *lock)
{
return spin_trylock_irq_disable(lock);
}

View File

@@ -11,3 +11,8 @@ __rust_helper void rust_helper_lockdep_unregister_key(struct lock_class_key *k)
{
lockdep_unregister_key(k);
}
__rust_helper void rust_helper_lockdep_assert_irqs_disabled(void)
{
lockdep_assert_irqs_disabled();
}

89
rust/kernel/interrupt.rs Normal file
View File

@@ -0,0 +1,89 @@
// SPDX-License-Identifier: GPL-2.0
//! Interrupt controls
//!
//! This module allows Rust code to annotate areas of code where local processor interrupts should
//! be disabled, along with actually disabling local processor interrupts.
//!
//! # ⚠️ Warning! ⚠️
//!
//! The usage of this module can be more complicated than meets the eye, especially surrounding
//! [preemptible kernels]. It's recommended to take care when using the functions and types defined
//! here and familiarize yourself with the various documentation we have before using them, along
//! with the various documents we link to here.
//!
//! # Reading material
//!
//! - [Software interrupts and realtime (LWN)](https://lwn.net/Articles/520076)
//!
//! [preemptible kernels]: https://www.kernel.org/doc/html/latest/locking/preempt-locking.html
use crate::types::NotThreadSafe;
/// A guard that represents local processor interrupt disablement on preemptible kernels.
///
/// [`LocalInterruptDisabled`] is a guard type that represents that local processor interrupts have
/// been disabled on a preemptible kernel.
///
/// Certain functions take an immutable reference of [`LocalInterruptDisabled`] in order to require
/// that they may only be run in local-interrupt-disabled contexts on preemptible kernels.
///
/// This is a marker type; it has no size, and is simply used as a compile-time guarantee that local
/// processor interrupts are disabled on preemptible kernels. Note that no guarantees about the
/// state of interrupts are made by this type on non-preemptible kernels.
///
/// # Invariants
///
/// Local processor interrupts are disabled on preemptible kernels for as long as an object of this
/// type exists.
pub struct LocalInterruptDisabled(NotThreadSafe);
/// Disable local processor interrupts on a preemptible kernel.
///
/// This function disables local processor interrupts on a preemptible kernel, and returns a
/// [`LocalInterruptDisabled`] token as proof of this. On non-preemptible kernels, this function is
/// a no-op.
///
/// **Usage of this function is discouraged** unless you are absolutely sure you know what you are
/// doing, as kernel interfaces for Rust that deal with interrupt state will typically handle local
/// processor interrupt state management on their own and managing this by hand is quite error
/// prone.
#[inline]
pub fn local_interrupt_disable() -> LocalInterruptDisabled {
// SAFETY: It's always safe to call `local_interrupt_disable()`.
unsafe { bindings::local_interrupt_disable() };
LocalInterruptDisabled(NotThreadSafe)
}
impl Drop for LocalInterruptDisabled {
#[inline]
fn drop(&mut self) {
// SAFETY: Per type invariants, a `local_interrupt_disable()` must be called to create this
// object, hence calling the corresponding `local_interrupt_enable()` is safe.
unsafe { bindings::local_interrupt_enable() };
}
}
impl LocalInterruptDisabled {
/// Assume that local processor interrupts are disabled on preemptible kernels.
///
/// This can be used for annotating code that is known to be run in contexts where local
/// processor interrupts are disabled on preemptible kernels. It makes no changes to the local
/// interrupt state on its own.
///
/// # Safety
///
/// For the whole life `'a`, local interrupts must be disabled on preemptible kernels. This
/// could be a context like, for example, an interrupt handler.
#[inline]
pub unsafe fn assume_disabled<'a>() -> &'a LocalInterruptDisabled {
const ASSUME_DISABLED: &LocalInterruptDisabled = &LocalInterruptDisabled(NotThreadSafe);
// Confirm they're actually disabled if lockdep is available
// SAFETY: It's always safe to call `lockdep_assert_irqs_disabled()`.
unsafe { bindings::lockdep_assert_irqs_disabled() };
ASSUME_DISABLED
}
}

View File

@@ -82,6 +82,7 @@
pub mod impl_flags;
pub mod init;
pub mod interop;
pub mod interrupt;
pub mod io;
pub mod ioctl;
pub mod iommu;

View File

@@ -7,12 +7,21 @@
use pin_init::Wrapper;
use crate::{bindings, prelude::*, sync::rcu, types::Opaque};
use crate::{
prelude::*,
sync::{
atomic::{
AtomicFlag,
Relaxed, //
},
rcu, //
},
types::Opaque, //
};
use core::{
marker::PhantomData,
ops::Deref,
ptr::drop_in_place,
sync::atomic::{AtomicBool, Ordering},
ptr::drop_in_place, //
};
/// An object that can become inaccessible at runtime.
@@ -65,7 +74,7 @@
/// ```
#[pin_data(PinnedDrop)]
pub struct Revocable<T> {
is_available: AtomicBool,
is_available: AtomicFlag,
#[pin]
data: Opaque<T>,
}
@@ -84,7 +93,7 @@ impl<T> Revocable<T> {
/// Creates a new revocable instance of the given data.
pub fn new<E>(data: impl PinInit<T, E>) -> impl PinInit<Self, E> {
try_pin_init!(Self {
is_available: AtomicBool::new(true),
is_available: AtomicFlag::new(true),
data <- Opaque::pin_init(data),
}? E)
}
@@ -98,7 +107,7 @@ pub fn new<E>(data: impl PinInit<T, E>) -> impl PinInit<Self, E> {
/// because another CPU may be waiting to complete the revocation of this object.
pub fn try_access(&self) -> Option<RevocableGuard<'_, T>> {
let guard = rcu::read_lock();
if self.is_available.load(Ordering::Relaxed) {
if self.is_available.load(Relaxed) {
// Since `self.is_available` is true, data is initialised and has to remain valid
// because the RCU read side lock prevents it from being dropped.
Some(RevocableGuard::new(self.data.get(), guard))
@@ -116,7 +125,7 @@ pub fn try_access(&self) -> Option<RevocableGuard<'_, T>> {
/// allowed to sleep because another CPU may be waiting to complete the revocation of this
/// object.
pub fn try_access_with_guard<'a>(&'a self, _guard: &'a rcu::Guard) -> Option<&'a T> {
if self.is_available.load(Ordering::Relaxed) {
if self.is_available.load(Relaxed) {
// SAFETY: Since `self.is_available` is true, data is initialised and has to remain
// valid because the RCU read side lock prevents it from being dropped.
Some(unsafe { &*self.data.get() })
@@ -157,12 +166,11 @@ pub unsafe fn access(&self) -> &T {
///
/// Callers must ensure that there are no more concurrent users of the revocable object.
unsafe fn revoke_internal<const SYNC: bool>(&self) -> bool {
let revoke = self.is_available.swap(false, Ordering::Relaxed);
let revoke = self.is_available.xchg(false, Relaxed);
if revoke {
if SYNC {
// SAFETY: Just an FFI call, there are no further requirements.
unsafe { bindings::synchronize_rcu() };
rcu::synchronize_rcu();
}
// SAFETY: We know `self.data` is valid because only one CPU can succeed the

View File

@@ -27,7 +27,14 @@
pub use condvar::{new_condvar, CondVar, CondVarTimeoutResult};
pub use lock::global::{global_lock, GlobalGuard, GlobalLock, GlobalLockBackend, GlobalLockedBy};
pub use lock::mutex::{new_mutex, Mutex, MutexGuard};
pub use lock::spinlock::{new_spinlock, SpinLock, SpinLockGuard};
pub use lock::spinlock::{
new_spinlock,
new_spinlock_irq,
SpinLock,
SpinLockGuard,
SpinLockIrq,
SpinLockIrqGuard, //
};
pub use locked_by::LockedBy;
pub use refcount::Refcount;
pub use set_once::SetOnce;

View File

@@ -15,7 +15,7 @@
//! - It provides ordering between the annotated operation and all the following memory accesses.
//! - It provides ordering between all the preceding memory accesses and all the following memory
//! accesses.
//! - All the orderings are the same strength as a full memory barrier (i.e. `smp_mb()`).
//! - All the orderings are the same strength as a full memory barrier (i.e. `smp_mb(Full)`).
//! - [`Relaxed`] provides no ordering except the dependency orderings. Dependency orderings are
//! described in "DEPENDENCY RELATIONS" in [`LKMM`]'s [`explanation`].
//!

View File

@@ -7,6 +7,38 @@
//!
//! [`LKMM`]: srctree/tools/memory-model/
#![expect(private_bounds, reason = "sealed implementation")]
/// Memory barrier orderings.
///
/// The semantics of these orderings follows the [`LKMM`] definitions and rules.
///
/// - [`Read`] provides ordering between preceding load operations and succeeding load operations.
/// - [`Write`] provides ordering between preceding store operations and succeeding store
/// operations.
/// - [`Full`] provides ordering between all the preceding memory accesses and succeeding memory
/// accesses.
///
/// [`LKMM`]: srctree/tools/memory-model/
pub mod ordering {
pub use crate::sync::atomic::ordering::Full;
/// The annotation type for read-read barrier ordering.
pub struct Read;
/// The annotation type for write-write barrier ordering.
pub struct Write;
}
pub use ordering::{
Full,
Read,
Write, //
};
struct Smp;
struct Dma;
/// A compiler barrier.
///
/// A barrier that prevents compiler from reordering memory accesses across the barrier.
@@ -19,43 +51,82 @@ pub(crate) fn barrier() {
unsafe { core::arch::asm!("") };
}
/// A full memory barrier.
trait MemoryBarrier<Flavour = ()> {
fn run();
}
macro_rules! define_barrier {
($([$flavour:ident])? $ordering:ident, $binding:ident) => {
impl MemoryBarrier$(<$flavour>)? for $ordering {
#[inline]
fn run() {
// SAFETY: barrier methods are safe to call.
unsafe { bindings::$binding() };
}
}
};
}
define_barrier!(Full, mb);
define_barrier!(Read, rmb);
define_barrier!(Write, wmb);
define_barrier!([Dma] Full, dma_mb);
define_barrier!([Dma] Read, dma_rmb);
define_barrier!([Dma] Write, dma_wmb);
define_barrier!([Smp] Full, smp_mb);
define_barrier!([Smp] Read, smp_rmb);
define_barrier!([Smp] Write, smp_wmb);
/// Memory barrier.
///
/// A barrier that prevents compiler and CPU from reordering memory accesses across the barrier.
#[inline(always)]
pub fn smp_mb() {
///
/// The specific forms of reordering can be specified using the parameter.
/// - `mb(Read)` provides a read-read barrier.
/// - `mb(Write)` provides a write-write barrier.
/// - `mb(Full)` provides a full barrier.
///
/// # Examples
///
/// ```
/// # use kernel::sync::barrier::*;
/// mb(Read);
/// mb(Write);
/// mb(Full);
/// ```
#[inline]
#[doc(alias = "rmb")]
#[doc(alias = "wmb")]
pub fn mb<T: MemoryBarrier>(_: T) {
T::run()
}
/// Memory barrier between CPUs.
///
/// A barrier that prevents compiler and CPU from reordering memory accesses across the barrier.
/// Does not prevent re-ordering with respect to other bus-mastering devices.
///
/// See [`mb`] for usage.
#[inline]
#[doc(alias = "smp_rmb")]
#[doc(alias = "smp_wmb")]
pub fn smp_mb<T: MemoryBarrier<Smp>>(_: T) {
if cfg!(CONFIG_SMP) {
// SAFETY: `smp_mb()` is safe to call.
unsafe { bindings::smp_mb() };
T::run()
} else {
barrier();
barrier()
}
}
/// A write-write memory barrier.
/// Memory barrier between local CPU and bus-mastering devices.
///
/// A barrier that prevents compiler and CPU from reordering memory write accesses across the
/// barrier.
#[inline(always)]
pub fn smp_wmb() {
if cfg!(CONFIG_SMP) {
// SAFETY: `smp_wmb()` is safe to call.
unsafe { bindings::smp_wmb() };
} else {
barrier();
}
}
/// A read-read memory barrier.
/// A barrier that prevents compiler and CPU from reordering memory accesses across the barrier.
/// Does not prevent re-ordering with respect to other CPUs.
///
/// A barrier that prevents compiler and CPU from reordering memory read accesses across the
/// barrier.
#[inline(always)]
pub fn smp_rmb() {
if cfg!(CONFIG_SMP) {
// SAFETY: `smp_rmb()` is safe to call.
unsafe { bindings::smp_rmb() };
} else {
barrier();
}
/// See [`mb`] for usage.
#[inline]
#[doc(alias = "dma_rmb")]
#[doc(alias = "dma_wmb")]
pub fn dma_mb<T: MemoryBarrier<Dma>>(_: T) {
T::run()
}

View File

@@ -306,4 +306,7 @@ macro_rules! global_lock_inner {
(backend SpinLock) => {
$crate::sync::lock::spinlock::SpinLockBackend
};
(backend SpinLockIrq) => {
$crate::sync::lock::spinlock::SpinLockIrqBackend
};
}

View File

@@ -3,6 +3,11 @@
//! A kernel spinlock.
//!
//! This module allows Rust code to use the kernel's `spinlock_t`.
use super::*;
use crate::{
interrupt::LocalInterruptDisabled,
prelude::*, //
};
/// Creates a [`SpinLock`] initialiser with the given name and a newly-created lock class.
///
@@ -82,7 +87,7 @@ macro_rules! new_spinlock {
/// ```
///
/// [`spinlock_t`]: srctree/include/linux/spinlock.h
pub type SpinLock<T> = super::Lock<T, SpinLockBackend>;
pub type SpinLock<T> = Lock<T, SpinLockBackend>;
/// A kernel `spinlock_t` lock backend.
pub struct SpinLockBackend;
@@ -91,13 +96,11 @@ macro_rules! new_spinlock {
///
/// This is simply a type alias for a [`Guard`] returned from locking a [`SpinLock`]. It will unlock
/// the [`SpinLock`] upon being dropped.
///
/// [`Guard`]: super::Guard
pub type SpinLockGuard<'a, T> = super::Guard<'a, T, SpinLockBackend>;
pub type SpinLockGuard<'a, T> = Guard<'a, T, SpinLockBackend>;
// SAFETY: The underlying kernel `spinlock_t` object ensures mutual exclusion. `relock` uses the
// default implementation that always calls the same locking method.
unsafe impl super::Backend for SpinLockBackend {
unsafe impl Backend for SpinLockBackend {
type State = bindings::spinlock_t;
type GuardState = ();
@@ -144,3 +147,319 @@ unsafe fn assert_is_held(ptr: *mut Self::State) {
unsafe { bindings::spin_assert_is_held(ptr) }
}
}
/// Creates a [`SpinLockIrq`] initialiser with the given name and a newly-created lock class.
///
/// It uses the name if one is given, otherwise it generates one based on the file name and line
/// number.
#[macro_export]
macro_rules! new_spinlock_irq {
($inner:expr $(, $name:literal)? $(,)?) => {
$crate::sync::SpinLockIrq::new(
$inner, $crate::optional_name!($($name)?), $crate::static_lock_class!())
};
}
pub use new_spinlock_irq;
/// A variant of `SpinLock` that ensures interrupts are disabled in the critical section.
///
/// This lock can be acquired in two ways:
///
/// - Using [`lock()`] like any other type of lock, in which case the bindings will modify the
/// interrupt state to ensure that local processor interrupts remain disabled for at least as
/// long as the [`SpinLockIrqGuard`] exists.
/// - Using [`lock_with()`] in contexts where a [`LocalInterruptDisabled`] token is present and
/// local processor interrupts are already known to be disabled, in which case the local
/// interrupt state will not be touched. This method should be preferred if a
/// [`LocalInterruptDisabled`] token is present in the scope.
///
/// For more info on spinlocks, see [`SpinLock`]. For more information on interrupts,
/// [see the interrupt module](kernel::interrupt).
///
/// # Examples
///
/// The following example shows how to declare, allocate initialise and access a struct (`Example`)
/// that contains an inner struct (`Inner`) that is protected by a spinlock that requires local
/// processor interrupts to be disabled.
///
/// ```
/// use kernel::sync::{new_spinlock_irq, SpinLockIrq};
///
/// struct Inner {
/// a: u32,
/// b: u32,
/// }
///
/// #[pin_data]
/// struct Example {
/// #[pin]
/// c: SpinLockIrq<Inner>,
/// #[pin]
/// d: SpinLockIrq<Inner>,
/// }
///
/// impl Example {
/// fn new() -> impl PinInit<Self> {
/// pin_init!(Self {
/// c <- new_spinlock_irq!(Inner { a: 0, b: 10 }),
/// d <- new_spinlock_irq!(Inner { a: 20, b: 30 }),
/// })
/// }
/// }
///
/// // Allocate a boxed `Example`
/// let e = KBox::pin_init(Example::new(), GFP_KERNEL)?;
///
/// // Accessing an `Example` from a context where interrupts may not be disabled already.
/// let c_guard = e.c.lock(); // interrupts are disabled now, +1 interrupt disable refcount
/// let d_guard = e.d.lock(); // no interrupt state change, +1 interrupt disable refcount
///
/// assert_eq!(c_guard.a, 0);
/// assert_eq!(c_guard.b, 10);
/// assert_eq!(d_guard.a, 20);
/// assert_eq!(d_guard.b, 30);
///
/// drop(c_guard); // Dropping c_guard will not re-enable interrupts just yet, since d_guard is
/// // still in scope.
/// drop(d_guard); // Last interrupt disable reference dropped here, so interrupts are re-enabled
/// // now
/// # Ok::<(), Error>(())
/// ```
///
/// The next example demonstrates locking a [`SpinLockIrq`] using [`lock_with()`] in a function
/// which can only be called when local processor interrupts are already disabled.
///
/// ```
/// use kernel::sync::{new_spinlock_irq, SpinLockIrq};
/// use kernel::interrupt::*;
///
/// struct Inner {
/// a: u32,
/// }
///
/// #[pin_data]
/// struct Example {
/// #[pin]
/// inner: SpinLockIrq<Inner>,
/// }
///
/// impl Example {
/// fn new() -> impl PinInit<Self> {
/// pin_init!(Self {
/// inner <- new_spinlock_irq!(Inner { a: 20 }),
/// })
/// }
/// }
///
/// // Accessing an `Example` from a function that can only be called in no-interrupt contexts.
/// fn noirq_work(e: &Example, interrupt_disabled: &LocalInterruptDisabled) {
/// // Because we know interrupts are disabled from interrupt_disable, we can skip toggling
/// // interrupt state using lock_with() and the provided token
/// assert_eq!(e.inner.lock_with(interrupt_disabled).a, 20);
/// }
///
/// # let e = KBox::pin_init(Example::new(), GFP_KERNEL)?;
/// # let interrupt_guard = local_interrupt_disable();
/// # noirq_work(&e, &interrupt_guard);
/// #
/// # Ok::<(), Error>(())
/// ```
///
/// [`lock()`]: SpinLockIrq::lock
/// [`lock_with()`]: SpinLockIrq::lock_with
pub type SpinLockIrq<T> = super::Lock<T, SpinLockIrqBackend>;
/// A kernel `spinlock_t` lock backend that can only be acquired in interrupt disabled contexts.
pub struct SpinLockIrqBackend;
/// A [`Guard`] acquired from locking a [`SpinLockIrq`] using [`lock()`].
///
/// This is simply a type alias for a [`Guard`] returned from locking a [`SpinLockIrq`] using
/// [`lock()`]. It will unlock the [`SpinLockIrq`] and decrement the local processor's interrupt
/// disablement refcount upon being dropped.
///
/// [`lock()`]: SpinLockIrq::lock
pub type SpinLockIrqGuard<'a, T> = Guard<'a, T, SpinLockIrqBackend>;
// SAFETY: The underlying kernel `spinlock_t` object ensures mutual exclusion. `relock` uses the
// default implementation that always calls the same locking method.
unsafe impl Backend for SpinLockIrqBackend {
type State = bindings::spinlock_t;
type GuardState = ();
#[inline]
unsafe fn init(
ptr: *mut Self::State,
name: *const crate::ffi::c_char,
key: *mut bindings::lock_class_key,
) {
// SAFETY: The safety requirements ensure that `ptr` is valid for writes, and `name` and
// `key` are valid for read indefinitely.
unsafe { bindings::__spin_lock_init(ptr, name, key) }
}
#[inline]
unsafe fn lock(ptr: *mut Self::State) -> Self::GuardState {
// SAFETY: The safety requirements of this function ensure that `ptr` points to valid
// memory, and that it has been initialised before.
unsafe { bindings::spin_lock_irq_disable(ptr) }
}
#[inline]
unsafe fn unlock(ptr: *mut Self::State, _guard_state: &Self::GuardState) {
// SAFETY: The safety requirements of this function ensure that `ptr` is valid and that the
// caller is the owner of the spinlock.
unsafe { bindings::spin_unlock_irq_enable(ptr) }
}
#[inline]
unsafe fn try_lock(ptr: *mut Self::State) -> Option<Self::GuardState> {
// SAFETY: The `ptr` pointer is guaranteed to be valid and initialized before use.
let result = unsafe { bindings::spin_trylock_irq_disable(ptr) };
if result != 0 {
Some(())
} else {
None
}
}
#[inline]
unsafe fn assert_is_held(ptr: *mut Self::State) {
// SAFETY: The `ptr` pointer is guaranteed to be valid and initialized before use.
unsafe { bindings::spin_assert_is_held(ptr) }
}
}
impl<T: ?Sized> Lock<T, SpinLockIrqBackend> {
/// Casts the lock as a `Lock<T, SpinLockBackend>`.
#[inline]
fn as_lock_in_interrupt<'a>(&'a self, _context: &'a LocalInterruptDisabled) -> &'a SpinLock<T> {
// SAFETY:
// - `Lock<T, SpinLockBackend>` and `Lock<T, SpinLockIrqBackend>` both have identical data
// layouts.
// - As long as local interrupts are disabled (which is proven to be true by _context), it
// is safe to treat a lock with SpinLockIrqBackend as a SpinLockBackend lock.
unsafe { core::mem::transmute(self) }
}
/// Acquires the lock without modifying local interrupt state.
///
/// This function should be used in place of the more expensive [`Lock::lock()`] function when
/// possible for [`SpinLockIrq`] locks.
#[inline]
pub fn lock_with<'a>(&'a self, context: &'a LocalInterruptDisabled) -> SpinLockGuard<'a, T> {
self.as_lock_in_interrupt(context).lock()
}
/// Tries to acquire the lock without modifying local interrupt state.
///
/// This function should be used in place of the more expensive [`Lock::try_lock()`] function
/// when possible for [`SpinLockIrq`] locks.
///
/// Returns a guard that can be used to access the data protected by the lock if successful.
#[must_use = "if unused, the lock will be immediately unlocked"]
#[inline]
pub fn try_lock_with<'a>(
&'a self,
context: &'a LocalInterruptDisabled,
) -> Option<SpinLockGuard<'a, T>> {
self.as_lock_in_interrupt(context).try_lock()
}
}
#[kunit_tests(rust_spinlock_irq_condvar)]
mod tests {
use super::*;
use crate::{
sync::*,
workqueue::{
self,
impl_has_work,
new_work,
Work,
WorkItem, //
},
};
struct TestState {
value: u32,
waiter_ready: bool,
}
#[pin_data]
struct Test {
#[pin]
state: SpinLockIrq<TestState>,
#[pin]
state_changed: CondVar,
#[pin]
waiter_state_changed: CondVar,
#[pin]
wait_work: Work<Self>,
}
impl_has_work! {
impl HasWork<Self> for Test { self.wait_work }
}
impl Test {
pub(crate) fn new() -> Result<Arc<Self>> {
Arc::try_pin_init(
try_pin_init!(
Self {
state <- new_spinlock_irq!(TestState {
value: 1,
waiter_ready: false
}),
state_changed <- new_condvar!(),
waiter_state_changed <- new_condvar!(),
wait_work <- new_work!("IrqCondvarTest::wait_work")
}
),
GFP_KERNEL,
)
}
}
impl WorkItem for Test {
type Pointer = Arc<Self>;
fn run(this: Arc<Self>) {
// Wait for the test to be ready to wait for us
let mut state = this.state.lock();
// Make sure the interrupts actually turned off
// SAFETY: It's always safe to call `lockdep_assert_irqs_disabled()`
unsafe { bindings::lockdep_assert_irqs_disabled() };
while !state.waiter_ready {
this.waiter_state_changed.wait(&mut state);
}
// Deliver the exciting value update our test has been waiting for
state.value += 1;
this.state_changed.notify_sync();
}
}
#[test]
fn spinlock_irq_condvar() -> Result {
let testdata = Test::new()?;
let _ = workqueue::system().enqueue(testdata.clone());
// Let the updater know when we're ready to wait
let mut state = testdata.state.lock();
state.waiter_ready = true;
testdata.waiter_state_changed.notify_sync();
// Wait for the exciting value update
testdata.state_changed.wait(&mut state);
assert_eq!(state.value, 2);
Ok(())
}
}

View File

@@ -8,7 +8,11 @@
bindings,
fs::File,
prelude::*,
sync::{CondVar, LockClassKey},
sync::{
rcu::synchronize_rcu,
CondVar,
LockClassKey, //
}, //
};
use core::{marker::PhantomData, ops::Deref};
@@ -99,8 +103,6 @@ fn drop(self: Pin<&mut Self>) {
unsafe { bindings::__wake_up_pollfree(self.inner.wait_queue_head.get()) };
// Wait for epoll items to be properly removed.
//
// SAFETY: Just an FFI call.
unsafe { bindings::synchronize_rcu() };
synchronize_rcu();
}
}

View File

@@ -70,3 +70,19 @@ pub fn rcu_barrier() {
// SAFETY: `rcu_barrier()` is always safe to be called. It just might wait for a grace period.
unsafe { bindings::rcu_barrier() };
}
/// Wait for one RCU grace period.
///
/// Waits for all RCU read-side critical sections (such as those established by
/// a [`Guard`]) at the moment of the function call to finish.
///
/// Does not prevent new read-side critical sections from starting, which may
/// begin and run while this call is blocking.
///
/// Note that this is one of the RCU primitives which must not be called in
/// atomic context.
#[inline]
pub fn synchronize_rcu() {
// SAFETY: `synchronize_rcu()` is always safe to be called from process context.
unsafe { bindings::synchronize_rcu() };
}

View File

@@ -225,7 +225,7 @@
#define X86_FEATURE_EPT_AD ( 8*32+17) /* "ept_ad" Intel Extended Page Table access-dirty bit */
#define X86_FEATURE_VMCALL ( 8*32+18) /* Hypervisor supports the VMCALL instruction */
#define X86_FEATURE_VMW_VMMCALL ( 8*32+19) /* VMware prefers VMMCALL hypercall instruction */
#define X86_FEATURE_PVUNLOCK ( 8*32+20) /* PV unlock function */
// free: was #define X86_FEATURE_PVUNLOCK ( 8*32+20) /* PV unlock function */
#define X86_FEATURE_VCPUPREEMPT ( 8*32+21) /* PV vcpu_is_preempted function */
#define X86_FEATURE_TDX_GUEST ( 8*32+22) /* "tdx_guest" Intel Trust Domain Extensions Guest */

View File

@@ -25,7 +25,7 @@
#define STATIC_CALL_SITE_INIT 2UL /* init section */
#define STATIC_CALL_SITE_FLAGS 3UL
#ifndef __ASSEMBLY__
#ifndef __ASSEMBLER__
/*
* The static call site table needs to be created by external tooling (objtool
@@ -102,6 +102,6 @@ struct static_call_key {
#endif /* CONFIG_HAVE_STATIC_CALL */
#endif /* __ASSEMBLY__ */
#endif /* __ASSEMBLER__ */
#endif /* _STATIC_CALL_TYPES_H */

View File

@@ -369,17 +369,20 @@ extern int bpf_sock_read_xattr(struct socket *sock, const char *name__str,
#define PREEMPT_BITS 8
#define SOFTIRQ_BITS 8
#define HARDIRQ_DISABLE_BITS 8
#define HARDIRQ_BITS 4
#define NMI_BITS 4
#define NMI_BITS 1
#define PREEMPT_SHIFT 0
#define SOFTIRQ_SHIFT (PREEMPT_SHIFT + PREEMPT_BITS)
#define HARDIRQ_SHIFT (SOFTIRQ_SHIFT + SOFTIRQ_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 __IRQ_MASK(x) ((1UL << (x))-1)
#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)