mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-31 03:35:32 -04:00
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:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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__
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 */
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
/*
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() \
|
||||
|
||||
@@ -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)
|
||||
|
||||
82
include/linux/interrupt_rc.h
Normal file
82
include/linux/interrupt_rc.h
Normal 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 */
|
||||
@@ -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 */
|
||||
|
||||
@@ -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)
|
||||
|
||||
/**
|
||||
|
||||
@@ -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();
|
||||
*/
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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) \
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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
|
||||
|
||||
109
kernel/irq/refcount_interrupt_test.c
Normal file
109
kernel/irq/refcount_interrupt_test.c
Normal 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");
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
/*
|
||||
|
||||
@@ -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 */
|
||||
|
||||
/**
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
13
rust/helpers/interrupt.c
Normal 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();
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
89
rust/kernel/interrupt.rs
Normal 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
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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`].
|
||||
//!
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -306,4 +306,7 @@ macro_rules! global_lock_inner {
|
||||
(backend SpinLock) => {
|
||||
$crate::sync::lock::spinlock::SpinLockBackend
|
||||
};
|
||||
(backend SpinLockIrq) => {
|
||||
$crate::sync::lock::spinlock::SpinLockIrqBackend
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() };
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user