Merge tag 'locking-core-2026-06-14' of gitolite.kernel.org:pub/scm/linux/kernel/git/tip/tip
Pull locking updates from Ingo Molnar:
"Futex updates:
- Optimize futex hash bucket access patterns (Peter Zijlstra)
- Large series to address the robust futex unlock race for real, by
Thomas Gleixner:
"The robust futex unlock mechanism is racy in respect to the
clearing of the robust_list_head::list_op_pending pointer because
unlock and clearing the pointer are not atomic.
The race window is between the unlock and clearing the pending op
pointer. If the task is forced to exit in this window, exit will
access a potentially invalid pending op pointer when cleaning up
the robust list.
That happens if another task manages to unmap the object
containing the lock before the cleanup, which results in an UAF.
In the worst case this UAF can lead to memory corruption when
unrelated content has been mapped to the same address by the time
the access happens.
User space can't solve this problem without help from the kernel.
This series provides the kernel side infrastructure to help it
along:
1) Combined unlock, pointer clearing, wake-up for the
contended case
2) VDSO based unlock and pointer clearing helpers with a
fix-up function in the kernel when user space was interrupted
within the critical section.
... with help by André Almeida:
- Add a note about robust list race condition (André Almeida)
- Add self-tests for robust release operations (André Almeida)
Context analysis updates:
- Implement context analysis for 'struct rt_mutex'. (Bart Van Assche)
- Bump required Clang version to 23 (Marco Elver)
Guard infrastructure updates:
- Series to remove NULL check from unconditional guards (Dmitry
Ilvokhin)
Lockdep updates:
- Restore self-test migrate_disable() and sched_rt_mutex state on
PREEMPT_RT (Karl Mehltretter)
Membarriers updates:
- Use per-CPU mutexes for targeted commands (Aniket Gattani)
- Modernize membarrier_global_expedited with cleanup guards (Aniket
Gattani)
- Add rseq stress test for CFS throttle interactions (Aniket Gattani)
percpu-rwsems updates:
- Extract __percpu_up_read() to optimize inlining overhead (Dmitry
Ilvokhin)
Seqlocks updates:
- Allow UBSAN_ALIGNMENT to fail optimizing (Heiko Carstens)
Lock tracing:
- Add contended_release tracepoint to sleepable locks such as
mutexes, percpu-rwsems, rtmutexes, rwsems and semaphores (Dmitry
Ilvokhin)
MAINTAINERS updates:
- MAINTAINERS: Add RUST [SYNC] entry (Boqun Feng)
Misc updates and fixes by Randy Dunlap, YE WEI-HONG, Fabricio Parra,
Dmitry Ilvokhin and Peter Zijlstra"
* tag 'locking-core-2026-06-14' of gitolite.kernel.org:pub/scm/linux/kernel/git/tip/tip: (36 commits)
locking: Add contended_release tracepoint to sleepable locks
locking/percpu-rwsem: Extract __percpu_up_read()
tracing/lock: Remove unnecessary linux/sched.h include
futex: Optimize futex hash bucket access patterns
rust: sync: completion: Mark inline complete_all and wait_for_completion
MAINTAINERS: Add RUST [SYNC] entry
cleanup: Specify nonnull argument index
selftests: futex: Add tests for robust release operations
Documentation: futex: Add a note about robust list race condition
x86/vdso: Implement __vdso_futex_robust_try_unlock()
x86/vdso: Prepare for robust futex unlock support
futex: Provide infrastructure to plug the non contended robust futex unlock race
futex: Add robust futex unlock IP range
futex: Add support for unlocking robust futexes
futex: Cleanup UAPI defines
x86: Select ARCH_MEMORY_ORDER_TSO
uaccess: Provide unsafe_atomic_store_release_user()
futex: Provide UABI defines for robust list entry modifiers
futex: Move futex related mm_struct data into a struct
futex: Make futex_mm_init() void
...
This commit is contained in:
commit
764e77d868
71 files changed
+2405
-494
No files matched your search
@@ -1,11 +1,12 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include <linux/irq-entry-common.h>
|
||||
#include <linux/resume_user_mode.h>
|
||||
#include <linux/futex.h>
|
||||
#include <linux/highmem.h>
|
||||
#include <linux/irq-entry-common.h>
|
||||
#include <linux/jump_label.h>
|
||||
#include <linux/kmsan.h>
|
||||
#include <linux/livepatch.h>
|
||||
#include <linux/resume_user_mode.h>
|
||||
#include <linux/tick.h>
|
||||
|
||||
/* Workaround to allow gradual conversion of architecture code */
|
||||
@@ -60,8 +61,10 @@ static __always_inline unsigned long __exit_to_user_mode_loop(struct pt_regs *re
|
||||
if (ti_work & _TIF_PATCH_PENDING)
|
||||
klp_update_patch_state(current);
|
||||
|
||||
if (ti_work & (_TIF_SIGPENDING | _TIF_NOTIFY_SIGNAL))
|
||||
if (ti_work & (_TIF_SIGPENDING | _TIF_NOTIFY_SIGNAL)) {
|
||||
futex_fixup_robust_unlock(regs);
|
||||
arch_do_signal_or_restart(regs);
|
||||
}
|
||||
|
||||
if (ti_work & _TIF_NOTIFY_RESUME)
|
||||
resume_user_mode_work(regs);
|
||||
|
||||
+2
-2
@@ -988,8 +988,8 @@ void __noreturn do_exit(long code)
|
||||
proc_exit_connector(tsk);
|
||||
mpol_put_task_policy(tsk);
|
||||
#ifdef CONFIG_FUTEX
|
||||
if (unlikely(current->pi_state_cache))
|
||||
kfree(current->pi_state_cache);
|
||||
if (unlikely(current->futex.pi_state_cache))
|
||||
kfree(current->futex.pi_state_cache);
|
||||
#endif
|
||||
/*
|
||||
* Make sure we are holding no locks:
|
||||
|
||||
+2
-6
@@ -1103,6 +1103,7 @@ static struct mm_struct *mm_init(struct mm_struct *mm, struct task_struct *p)
|
||||
#endif
|
||||
mm_init_uprobes_state(mm);
|
||||
hugetlb_count_init(mm);
|
||||
futex_mm_init(mm);
|
||||
|
||||
mm_flags_clear_all(mm);
|
||||
if (current->mm) {
|
||||
@@ -1115,11 +1116,8 @@ static struct mm_struct *mm_init(struct mm_struct *mm, struct task_struct *p)
|
||||
mm->def_flags = 0;
|
||||
}
|
||||
|
||||
if (futex_mm_init(mm))
|
||||
goto fail_mm_init;
|
||||
|
||||
if (mm_alloc_pgd(mm))
|
||||
goto fail_nopgd;
|
||||
goto fail_mm_init;
|
||||
|
||||
if (mm_alloc_id(mm))
|
||||
goto fail_noid;
|
||||
@@ -1145,8 +1143,6 @@ fail_nocontext:
|
||||
mm_free_id(mm);
|
||||
fail_noid:
|
||||
mm_free_pgd(mm);
|
||||
fail_nopgd:
|
||||
futex_hash_free(mm);
|
||||
fail_mm_init:
|
||||
free_mm(mm);
|
||||
return NULL;
|
||||
|
||||
+249
-215
File diff suppressed because it is too large.
Load diff
+36
-16
@@ -40,6 +40,8 @@
|
||||
#define FLAGS_NUMA 0x0080
|
||||
#define FLAGS_STRICT 0x0100
|
||||
#define FLAGS_MPOL 0x0200
|
||||
#define FLAGS_ROBUST_UNLOCK 0x0400
|
||||
#define FLAGS_ROBUST_LIST32 0x0800
|
||||
|
||||
/* FUTEX_ to FLAGS_ */
|
||||
static inline unsigned int futex_to_flags(unsigned int op)
|
||||
@@ -52,6 +54,12 @@ static inline unsigned int futex_to_flags(unsigned int op)
|
||||
if (op & FUTEX_CLOCK_REALTIME)
|
||||
flags |= FLAGS_CLOCKRT;
|
||||
|
||||
if (op & FUTEX_ROBUST_UNLOCK)
|
||||
flags |= FLAGS_ROBUST_UNLOCK;
|
||||
|
||||
if (op & FUTEX_ROBUST_LIST32)
|
||||
flags |= FLAGS_ROBUST_LIST32;
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
@@ -126,6 +134,15 @@ static inline bool should_fail_futex(bool fshared)
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline bool futex_key_is_private(union futex_key *key)
|
||||
{
|
||||
/*
|
||||
* Relies on get_futex_key() to set either bit for shared
|
||||
* futexes -- see comment with union futex_key.
|
||||
*/
|
||||
return !(key->both.offset & (FUT_OFF_INODE | FUT_OFF_MMSHARED));
|
||||
}
|
||||
|
||||
/*
|
||||
* Hash buckets are shared by all the futex_keys that hash to the same
|
||||
* location. Each key may have multiple futex_q structures, one for each task
|
||||
@@ -135,7 +152,6 @@ struct futex_hash_bucket {
|
||||
atomic_t waiters;
|
||||
spinlock_t lock;
|
||||
struct plist_head chain;
|
||||
struct futex_private_hash *priv;
|
||||
} ____cacheline_aligned_in_smp;
|
||||
|
||||
/*
|
||||
@@ -175,7 +191,7 @@ typedef void (futex_wake_fn)(struct wake_q_head *wake_q, struct futex_q *q);
|
||||
* @requeue_pi_key: the requeue_pi target futex key
|
||||
* @bitset: bitset for the optional bitmasked wakeup
|
||||
* @requeue_state: State field for futex_requeue_pi()
|
||||
* @drop_hb_ref: Waiter should drop the extra hash bucket reference if true
|
||||
* @drop_fph: Waiter should drop the extra private hash reference when set
|
||||
* @requeue_wait: RCU wait for futex_requeue_pi() (RT only)
|
||||
*
|
||||
* We use this hashed waitqueue, instead of a normal wait_queue_entry_t, so
|
||||
@@ -202,7 +218,7 @@ struct futex_q {
|
||||
union futex_key *requeue_pi_key;
|
||||
u32 bitset;
|
||||
atomic_t requeue_state;
|
||||
bool drop_hb_ref;
|
||||
struct futex_private_hash *drop_fph;
|
||||
#ifdef CONFIG_PREEMPT_RT
|
||||
struct rcuwait requeue_wait;
|
||||
#endif
|
||||
@@ -222,28 +238,29 @@ extern struct hrtimer_sleeper *
|
||||
futex_setup_timer(ktime_t *time, struct hrtimer_sleeper *timeout,
|
||||
int flags, u64 range_ns);
|
||||
|
||||
extern struct futex_hash_bucket *futex_hash(union futex_key *key);
|
||||
#ifdef CONFIG_FUTEX_PRIVATE_HASH
|
||||
extern void futex_hash_get(struct futex_hash_bucket *hb);
|
||||
extern void futex_hash_put(struct futex_hash_bucket *hb);
|
||||
struct futex_bucket_ref {
|
||||
struct futex_hash_bucket *hb;
|
||||
struct futex_private_hash *fph;
|
||||
};
|
||||
|
||||
extern struct futex_private_hash *futex_private_hash(void);
|
||||
#ifdef CONFIG_FUTEX_PRIVATE_HASH
|
||||
extern struct futex_private_hash *futex_private_hash(struct mm_struct *mm);
|
||||
extern void futex_private_hash_put(struct futex_private_hash *fph);
|
||||
|
||||
#else /* !CONFIG_FUTEX_PRIVATE_HASH */
|
||||
static inline void futex_hash_get(struct futex_hash_bucket *hb) { }
|
||||
static inline void futex_hash_put(struct futex_hash_bucket *hb) { }
|
||||
static inline struct futex_private_hash *futex_private_hash(void) { return NULL; }
|
||||
static inline struct futex_private_hash *futex_private_hash(struct mm_struct *mm) { return NULL; }
|
||||
static inline void futex_private_hash_put(struct futex_private_hash *fph) { }
|
||||
#endif
|
||||
|
||||
DEFINE_CLASS(hb, struct futex_hash_bucket *,
|
||||
if (_T) futex_hash_put(_T),
|
||||
extern struct futex_bucket_ref futex_hash(union futex_key *key);
|
||||
|
||||
DEFINE_CLASS(hbr, struct futex_bucket_ref,
|
||||
if (_T.fph) futex_private_hash_put(_T.fph),
|
||||
futex_hash(key), union futex_key *key);
|
||||
|
||||
DEFINE_CLASS(private_hash, struct futex_private_hash *,
|
||||
if (_T) futex_private_hash_put(_T),
|
||||
futex_private_hash(), void);
|
||||
futex_private_hash(mm), struct mm_struct *mm);
|
||||
|
||||
/**
|
||||
* futex_match - Check whether two futex keys are equal
|
||||
@@ -449,13 +466,16 @@ extern int futex_unqueue_multiple(struct futex_vector *v, int count);
|
||||
extern int futex_wait_multiple(struct futex_vector *vs, unsigned int count,
|
||||
struct hrtimer_sleeper *to);
|
||||
|
||||
extern int futex_wake(u32 __user *uaddr, unsigned int flags, int nr_wake, u32 bitset);
|
||||
extern int futex_wake(u32 __user *uaddr, unsigned int flags, void __user *pop,
|
||||
int nr_wake, u32 bitset);
|
||||
|
||||
extern int futex_wake_op(u32 __user *uaddr1, unsigned int flags,
|
||||
u32 __user *uaddr2, int nr_wake, int nr_wake2, int op);
|
||||
|
||||
extern int futex_unlock_pi(u32 __user *uaddr, unsigned int flags);
|
||||
extern int futex_unlock_pi(u32 __user *uaddr, unsigned int flags, void __user *pop);
|
||||
|
||||
extern int futex_lock_pi(u32 __user *uaddr, unsigned int flags, ktime_t *time, int trylock);
|
||||
|
||||
bool futex_robust_list_clear_pending(void __user *pop, unsigned int flags);
|
||||
|
||||
#endif /* _FUTEX_H */
|
||||
+37
-25
@@ -14,7 +14,7 @@ int refill_pi_state_cache(void)
|
||||
{
|
||||
struct futex_pi_state *pi_state;
|
||||
|
||||
if (likely(current->pi_state_cache))
|
||||
if (likely(current->futex.pi_state_cache))
|
||||
return 0;
|
||||
|
||||
pi_state = kzalloc_obj(*pi_state);
|
||||
@@ -28,17 +28,17 @@ int refill_pi_state_cache(void)
|
||||
refcount_set(&pi_state->refcount, 1);
|
||||
pi_state->key = FUTEX_KEY_INIT;
|
||||
|
||||
current->pi_state_cache = pi_state;
|
||||
current->futex.pi_state_cache = pi_state;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct futex_pi_state *alloc_pi_state(void)
|
||||
{
|
||||
struct futex_pi_state *pi_state = current->pi_state_cache;
|
||||
struct futex_pi_state *pi_state = current->futex.pi_state_cache;
|
||||
|
||||
WARN_ON(!pi_state);
|
||||
current->pi_state_cache = NULL;
|
||||
current->futex.pi_state_cache = NULL;
|
||||
|
||||
return pi_state;
|
||||
}
|
||||
@@ -60,7 +60,7 @@ static void pi_state_update_owner(struct futex_pi_state *pi_state,
|
||||
if (new_owner) {
|
||||
raw_spin_lock(&new_owner->pi_lock);
|
||||
WARN_ON(!list_empty(&pi_state->list));
|
||||
list_add(&pi_state->list, &new_owner->pi_state_list);
|
||||
list_add(&pi_state->list, &new_owner->futex.pi_state_list);
|
||||
pi_state->owner = new_owner;
|
||||
raw_spin_unlock(&new_owner->pi_lock);
|
||||
}
|
||||
@@ -96,7 +96,7 @@ void put_pi_state(struct futex_pi_state *pi_state)
|
||||
raw_spin_unlock_irqrestore(&pi_state->pi_mutex.wait_lock, flags);
|
||||
}
|
||||
|
||||
if (current->pi_state_cache) {
|
||||
if (current->futex.pi_state_cache) {
|
||||
kfree(pi_state);
|
||||
} else {
|
||||
/*
|
||||
@@ -106,7 +106,7 @@ void put_pi_state(struct futex_pi_state *pi_state)
|
||||
*/
|
||||
pi_state->owner = NULL;
|
||||
refcount_set(&pi_state->refcount, 1);
|
||||
current->pi_state_cache = pi_state;
|
||||
current->futex.pi_state_cache = pi_state;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,7 +179,7 @@ void put_pi_state(struct futex_pi_state *pi_state)
|
||||
*
|
||||
* p->pi_lock:
|
||||
*
|
||||
* p->pi_state_list -> pi_state->list, relation
|
||||
* p->futex.pi_state_list -> pi_state->list, relation
|
||||
* pi_mutex->owner -> pi_state->owner, relation
|
||||
*
|
||||
* pi_state->refcount:
|
||||
@@ -327,7 +327,7 @@ static int handle_exit_race(u32 __user *uaddr, u32 uval,
|
||||
* If the futex exit state is not yet FUTEX_STATE_DEAD, tell the
|
||||
* caller that the alleged owner is busy.
|
||||
*/
|
||||
if (tsk && tsk->futex_state != FUTEX_STATE_DEAD)
|
||||
if (tsk && tsk->futex.state != FUTEX_STATE_DEAD)
|
||||
return -EBUSY;
|
||||
|
||||
/*
|
||||
@@ -346,8 +346,8 @@ static int handle_exit_race(u32 __user *uaddr, u32 uval,
|
||||
* *uaddr = 0xC0000000; tsk = get_task(PID);
|
||||
* } if (!tsk->flags & PF_EXITING) {
|
||||
* ... attach();
|
||||
* tsk->futex_state = } else {
|
||||
* FUTEX_STATE_DEAD; if (tsk->futex_state !=
|
||||
* tsk->futex.state = } else {
|
||||
* FUTEX_STATE_DEAD; if (tsk->futex.state !=
|
||||
* FUTEX_STATE_DEAD)
|
||||
* return -EAGAIN;
|
||||
* return -ESRCH; <--- FAIL
|
||||
@@ -396,7 +396,7 @@ static void __attach_to_pi_owner(struct task_struct *p, union futex_key *key,
|
||||
pi_state->key = *key;
|
||||
|
||||
WARN_ON(!list_empty(&pi_state->list));
|
||||
list_add(&pi_state->list, &p->pi_state_list);
|
||||
list_add(&pi_state->list, &p->futex.pi_state_list);
|
||||
/*
|
||||
* Assignment without holding pi_state->pi_mutex.wait_lock is safe
|
||||
* because there is no concurrency as the object is not published yet.
|
||||
@@ -440,7 +440,7 @@ static int attach_to_pi_owner(u32 __user *uaddr, u32 uval, union futex_key *key,
|
||||
* in futex_exit_release(), we do this protected by p->pi_lock:
|
||||
*/
|
||||
raw_spin_lock_irq(&p->pi_lock);
|
||||
if (unlikely(p->futex_state != FUTEX_STATE_OK)) {
|
||||
if (unlikely(p->futex.state != FUTEX_STATE_OK)) {
|
||||
/*
|
||||
* The task is on the way out. When the futex state is
|
||||
* FUTEX_STATE_DEAD, we know that the task has finished
|
||||
@@ -945,7 +945,8 @@ retry:
|
||||
|
||||
retry_private:
|
||||
if (1) {
|
||||
CLASS(hb, hb)(&q.key);
|
||||
CLASS(hbr, hbr)(&q.key);
|
||||
auto hb = hbr.hb;
|
||||
|
||||
futex_q_lock(&q, hb);
|
||||
|
||||
@@ -1009,7 +1010,7 @@ retry_private:
|
||||
* the thread, performing resize, will block on hb->lock during
|
||||
* the requeue.
|
||||
*/
|
||||
futex_hash_put(no_free_ptr(hb));
|
||||
futex_private_hash_put(no_free_ptr(hbr.fph));
|
||||
/*
|
||||
* Must be done before we enqueue the waiter, here is unfortunately
|
||||
* under the hb lock, but that *should* work because it does nothing.
|
||||
@@ -1100,11 +1101,9 @@ no_block:
|
||||
__release(&hb->lock);
|
||||
futex_unqueue_pi(&q);
|
||||
spin_unlock(q.lock_ptr);
|
||||
if (q.drop_hb_ref) {
|
||||
CLASS(hb, hb)(&q.key);
|
||||
/* Additional reference from futex_unlock_pi() */
|
||||
futex_hash_put(hb);
|
||||
}
|
||||
|
||||
/* Additional reference from futex_unlock_pi() */
|
||||
futex_private_hash_put(q.drop_fph);
|
||||
goto out;
|
||||
|
||||
out_unlock_put_key:
|
||||
@@ -1139,7 +1138,7 @@ out:
|
||||
* This is the in-kernel slowpath: we look up the PI state (if any),
|
||||
* and do the rt-mutex unlock.
|
||||
*/
|
||||
int futex_unlock_pi(u32 __user *uaddr, unsigned int flags)
|
||||
static int __futex_unlock_pi(u32 __user *uaddr, unsigned int flags)
|
||||
{
|
||||
u32 curval, uval, vpid = task_pid_vnr(current);
|
||||
union futex_key key = FUTEX_KEY_INIT;
|
||||
@@ -1148,7 +1147,6 @@ int futex_unlock_pi(u32 __user *uaddr, unsigned int flags)
|
||||
|
||||
if (!IS_ENABLED(CONFIG_FUTEX_PI))
|
||||
return -ENOSYS;
|
||||
|
||||
retry:
|
||||
if (get_user(uval, uaddr))
|
||||
return -EFAULT;
|
||||
@@ -1162,7 +1160,8 @@ retry:
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
CLASS(hb, hb)(&key);
|
||||
CLASS(hbr, hbr)(&key);
|
||||
auto hb = hbr.hb;
|
||||
spin_lock(&hb->lock);
|
||||
retry_hb:
|
||||
|
||||
@@ -1219,8 +1218,9 @@ retry_hb:
|
||||
* Acquire a reference for the leaving waiter to ensure
|
||||
* valid futex_q::lock_ptr.
|
||||
*/
|
||||
futex_hash_get(hb);
|
||||
top_waiter->drop_hb_ref = true;
|
||||
if (futex_key_is_private(&key))
|
||||
top_waiter->drop_fph = futex_private_hash(key.private.mm);
|
||||
|
||||
__futex_unqueue(top_waiter);
|
||||
raw_spin_unlock_irq(&pi_state->pi_mutex.wait_lock);
|
||||
goto retry_hb;
|
||||
@@ -1302,3 +1302,15 @@ pi_faulted:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int futex_unlock_pi(u32 __user *uaddr, unsigned int flags, void __user *pop)
|
||||
{
|
||||
int ret = __futex_unlock_pi(uaddr, flags);
|
||||
|
||||
if (ret || !(flags & FLAGS_ROBUST_UNLOCK))
|
||||
return ret;
|
||||
|
||||
if (!futex_robust_list_clear_pending(pop, flags))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
+10
-10
@@ -241,8 +241,8 @@ void requeue_pi_wake_futex(struct futex_q *q, union futex_key *key,
|
||||
* Acquire a reference for the waiter to ensure valid
|
||||
* futex_q::lock_ptr.
|
||||
*/
|
||||
futex_hash_get(hb);
|
||||
q->drop_hb_ref = true;
|
||||
if (futex_key_is_private(key))
|
||||
q->drop_fph = futex_private_hash(key->private.mm);
|
||||
q->lock_ptr = &hb->lock;
|
||||
task = READ_ONCE(q->task);
|
||||
|
||||
@@ -459,8 +459,10 @@ retry:
|
||||
|
||||
retry_private:
|
||||
if (1) {
|
||||
CLASS(hb, hb1)(&key1);
|
||||
CLASS(hb, hb2)(&key2);
|
||||
CLASS(hbr, hbr1)(&key1);
|
||||
CLASS(hbr, hbr2)(&key2);
|
||||
auto hb1 = hbr1.hb;
|
||||
auto hb2 = hbr2.hb;
|
||||
|
||||
futex_hb_waiters_inc(hb2);
|
||||
double_lock_hb(hb1, hb2);
|
||||
@@ -838,7 +840,8 @@ int futex_wait_requeue_pi(u32 __user *uaddr, unsigned int flags,
|
||||
switch (futex_requeue_pi_wakeup_sync(&q)) {
|
||||
case Q_REQUEUE_PI_IGNORE:
|
||||
{
|
||||
CLASS(hb, hb)(&q.key);
|
||||
CLASS(hbr, hbr)(&q.key);
|
||||
auto hb = hbr.hb;
|
||||
/* The waiter is still on uaddr1 */
|
||||
spin_lock(&hb->lock);
|
||||
ret = handle_early_requeue_pi_wakeup(hb, &q, to);
|
||||
@@ -908,11 +911,8 @@ int futex_wait_requeue_pi(u32 __user *uaddr, unsigned int flags,
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
if (q.drop_hb_ref) {
|
||||
CLASS(hb, hb)(&q.key);
|
||||
/* Additional reference from requeue_pi_wake_futex() */
|
||||
futex_hash_put(hb);
|
||||
}
|
||||
/* Additional reference from requeue_pi_wake_futex() */
|
||||
futex_private_hash_put(q.drop_fph);
|
||||
|
||||
out:
|
||||
if (to) {
|
||||
|
||||
+18
-18
@@ -25,17 +25,13 @@
|
||||
* @head: pointer to the list-head
|
||||
* @len: length of the list-head, as userspace expects
|
||||
*/
|
||||
SYSCALL_DEFINE2(set_robust_list, struct robust_list_head __user *, head,
|
||||
size_t, len)
|
||||
SYSCALL_DEFINE2(set_robust_list, struct robust_list_head __user *, head, size_t, len)
|
||||
{
|
||||
/*
|
||||
* The kernel knows only one size for now:
|
||||
*/
|
||||
/* The kernel knows only one size for now. */
|
||||
if (unlikely(len != sizeof(*head)))
|
||||
return -EINVAL;
|
||||
|
||||
current->robust_list = head;
|
||||
|
||||
current->futex.robust_list = head;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -43,9 +39,9 @@ static inline void __user *futex_task_robust_list(struct task_struct *p, bool co
|
||||
{
|
||||
#ifdef CONFIG_COMPAT
|
||||
if (compat)
|
||||
return p->compat_robust_list;
|
||||
return p->futex.compat_robust_list;
|
||||
#endif
|
||||
return p->robust_list;
|
||||
return p->futex.robust_list;
|
||||
}
|
||||
|
||||
static void __user *futex_get_robust_list_common(int pid, bool compat)
|
||||
@@ -122,6 +118,13 @@ long do_futex(u32 __user *uaddr, int op, u32 val, ktime_t *timeout,
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
if (flags & FLAGS_ROBUST_UNLOCK) {
|
||||
if (cmd != FUTEX_WAKE &&
|
||||
cmd != FUTEX_WAKE_BITSET &&
|
||||
cmd != FUTEX_UNLOCK_PI)
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case FUTEX_WAIT:
|
||||
val3 = FUTEX_BITSET_MATCH_ANY;
|
||||
@@ -132,7 +135,7 @@ long do_futex(u32 __user *uaddr, int op, u32 val, ktime_t *timeout,
|
||||
val3 = FUTEX_BITSET_MATCH_ANY;
|
||||
fallthrough;
|
||||
case FUTEX_WAKE_BITSET:
|
||||
return futex_wake(uaddr, flags, val, val3);
|
||||
return futex_wake(uaddr, flags, uaddr2, val, val3);
|
||||
case FUTEX_REQUEUE:
|
||||
return futex_requeue(uaddr, flags, uaddr2, flags, val, val2, NULL, 0);
|
||||
case FUTEX_CMP_REQUEUE:
|
||||
@@ -145,7 +148,7 @@ long do_futex(u32 __user *uaddr, int op, u32 val, ktime_t *timeout,
|
||||
case FUTEX_LOCK_PI2:
|
||||
return futex_lock_pi(uaddr, flags, timeout, 0);
|
||||
case FUTEX_UNLOCK_PI:
|
||||
return futex_unlock_pi(uaddr, flags);
|
||||
return futex_unlock_pi(uaddr, flags, uaddr2);
|
||||
case FUTEX_TRYLOCK_PI:
|
||||
return futex_lock_pi(uaddr, flags, NULL, 1);
|
||||
case FUTEX_WAIT_REQUEUE_PI:
|
||||
@@ -379,7 +382,7 @@ SYSCALL_DEFINE4(futex_wake,
|
||||
if (!futex_validate_input(flags, mask))
|
||||
return -EINVAL;
|
||||
|
||||
return futex_wake(uaddr, FLAGS_STRICT | flags, nr, mask);
|
||||
return futex_wake(uaddr, FLAGS_STRICT | flags, NULL, nr, mask);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -475,15 +478,13 @@ SYSCALL_DEFINE4(futex_requeue,
|
||||
}
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
COMPAT_SYSCALL_DEFINE2(set_robust_list,
|
||||
struct compat_robust_list_head __user *, head,
|
||||
compat_size_t, len)
|
||||
COMPAT_SYSCALL_DEFINE2(set_robust_list, struct compat_robust_list_head __user *, head,
|
||||
compat_size_t, len)
|
||||
{
|
||||
if (unlikely(len != sizeof(*head)))
|
||||
return -EINVAL;
|
||||
|
||||
current->compat_robust_list = head;
|
||||
|
||||
current->futex.compat_robust_list = head;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -523,4 +524,3 @@ SYSCALL_DEFINE6(futex_time32, u32 __user *, uaddr, int, op, u32, val,
|
||||
return do_futex(uaddr, op, val, tp, uaddr2, (unsigned long)utime, val3);
|
||||
}
|
||||
#endif /* CONFIG_COMPAT_32BIT_TIME */
|
||||
|
||||
+39
-8
@@ -149,13 +149,36 @@ void futex_wake_mark(struct wake_q_head *wake_q, struct futex_q *q)
|
||||
wake_q_add_safe(wake_q, p);
|
||||
}
|
||||
|
||||
/*
|
||||
* If requested, clear the robust list pending op and unlock the futex
|
||||
*/
|
||||
static bool futex_robust_unlock(u32 __user *uaddr, unsigned int flags, void __user *pop)
|
||||
{
|
||||
if (!(flags & FLAGS_ROBUST_UNLOCK))
|
||||
return true;
|
||||
|
||||
/* First unlock the futex, which requires release semantics. */
|
||||
scoped_user_write_access(uaddr, efault)
|
||||
unsafe_atomic_store_release_user(0, uaddr, efault);
|
||||
|
||||
/*
|
||||
* Clear the pending list op now. If that fails, then the task is in
|
||||
* deeper trouble as the robust list head is usually part of the TLS.
|
||||
* The chance of survival is close to zero.
|
||||
*/
|
||||
return futex_robust_list_clear_pending(pop, flags);
|
||||
|
||||
efault:
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Wake up waiters matching bitset queued on this futex (uaddr).
|
||||
*/
|
||||
int futex_wake(u32 __user *uaddr, unsigned int flags, int nr_wake, u32 bitset)
|
||||
int futex_wake(u32 __user *uaddr, unsigned int flags, void __user *pop, int nr_wake, u32 bitset)
|
||||
{
|
||||
struct futex_q *this, *next;
|
||||
union futex_key key = FUTEX_KEY_INIT;
|
||||
struct futex_q *this, *next;
|
||||
DEFINE_WAKE_Q(wake_q);
|
||||
int ret;
|
||||
|
||||
@@ -166,10 +189,14 @@ int futex_wake(u32 __user *uaddr, unsigned int flags, int nr_wake, u32 bitset)
|
||||
if (unlikely(ret != 0))
|
||||
return ret;
|
||||
|
||||
if (!futex_robust_unlock(uaddr, flags, pop))
|
||||
return -EFAULT;
|
||||
|
||||
if ((flags & FLAGS_STRICT) && !nr_wake)
|
||||
return 0;
|
||||
|
||||
CLASS(hb, hb)(&key);
|
||||
CLASS(hbr, hbr)(&key);
|
||||
auto hb = hbr.hb;
|
||||
|
||||
/* Make sure we really have tasks to wakeup */
|
||||
if (!futex_hb_waiters_pending(hb))
|
||||
@@ -266,8 +293,10 @@ retry:
|
||||
|
||||
retry_private:
|
||||
if (1) {
|
||||
CLASS(hb, hb1)(&key1);
|
||||
CLASS(hb, hb2)(&key2);
|
||||
CLASS(hbr, hbr1)(&key1);
|
||||
CLASS(hbr, hbr2)(&key2);
|
||||
auto hb1 = hbr1.hb;
|
||||
auto hb2 = hbr2.hb;
|
||||
|
||||
double_lock_hb(hb1, hb2);
|
||||
op_ret = futex_atomic_op_inuser(op, uaddr2);
|
||||
@@ -409,7 +438,7 @@ int futex_wait_multiple_setup(struct futex_vector *vs, int count, int *woken)
|
||||
* Make sure to have a reference on the private_hash such that we
|
||||
* don't block on rehash after changing the task state below.
|
||||
*/
|
||||
guard(private_hash)();
|
||||
guard(private_hash)(current->mm);
|
||||
|
||||
/*
|
||||
* Enqueuing multiple futexes is tricky, because we need to enqueue
|
||||
@@ -446,7 +475,8 @@ retry:
|
||||
u32 val = vs[i].w.val;
|
||||
|
||||
if (1) {
|
||||
CLASS(hb, hb)(&q->key);
|
||||
CLASS(hbr, hbr)(&q->key);
|
||||
auto hb = hbr.hb;
|
||||
|
||||
futex_q_lock(q, hb);
|
||||
ret = futex_get_value_locked(&uval, uaddr);
|
||||
@@ -621,7 +651,8 @@ retry:
|
||||
|
||||
retry_private:
|
||||
if (1) {
|
||||
CLASS(hb, hb)(&q->key);
|
||||
CLASS(hbr, hbr)(&q->key);
|
||||
auto hb = hbr.hb;
|
||||
|
||||
futex_q_lock(q, hb);
|
||||
|
||||
|
||||
@@ -194,7 +194,7 @@ void __irq_put_desc_unlock(struct irq_desc *desc, unsigned long flags, bool bus)
|
||||
|
||||
__DEFINE_CLASS_IS_CONDITIONAL(irqdesc_lock, true);
|
||||
__DEFINE_UNLOCK_GUARD(irqdesc_lock, struct irq_desc,
|
||||
__irq_put_desc_unlock(_T->lock, _T->flags, _T->bus),
|
||||
if (_T->lock) __irq_put_desc_unlock(_T->lock, _T->flags, _T->bus),
|
||||
unsigned long flags; bool bus);
|
||||
|
||||
static inline class_irqdesc_lock_t class_irqdesc_lock_constructor(unsigned int irq, bool bus,
|
||||
|
||||
@@ -1023,6 +1023,9 @@ static noinline void __sched __mutex_unlock_slowpath(struct mutex *lock, unsigne
|
||||
wake_q_add(&wake_q, next);
|
||||
}
|
||||
|
||||
if (trace_contended_release_enabled() && waiter)
|
||||
trace_call__contended_release(lock);
|
||||
|
||||
if (owner & MUTEX_FLAG_HANDOFF)
|
||||
__mutex_handoff(lock, next);
|
||||
|
||||
@@ -1220,6 +1223,7 @@ EXPORT_SYMBOL(ww_mutex_lock_interruptible);
|
||||
|
||||
EXPORT_TRACEPOINT_SYMBOL_GPL(contention_begin);
|
||||
EXPORT_TRACEPOINT_SYMBOL_GPL(contention_end);
|
||||
EXPORT_TRACEPOINT_SYMBOL_GPL(contended_release);
|
||||
|
||||
/**
|
||||
* atomic_dec_and_mutex_lock - return holding mutex if we dec to 0
|
||||
|
||||
@@ -263,6 +263,9 @@ void percpu_up_write(struct percpu_rw_semaphore *sem)
|
||||
{
|
||||
rwsem_release(&sem->dep_map, _RET_IP_);
|
||||
|
||||
if (trace_contended_release_enabled() && wq_has_sleeper(&sem->waiters))
|
||||
trace_call__contended_release(sem);
|
||||
|
||||
/*
|
||||
* Signal the writer is done, no fast path yet.
|
||||
*
|
||||
@@ -288,3 +291,29 @@ void percpu_up_write(struct percpu_rw_semaphore *sem)
|
||||
rcu_sync_exit(&sem->rss);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(percpu_up_write);
|
||||
|
||||
void __percpu_up_read(struct percpu_rw_semaphore *sem)
|
||||
{
|
||||
lockdep_assert_preemption_disabled();
|
||||
/*
|
||||
* After percpu_up_write() completes, rcu_sync_is_idle() can still
|
||||
* return false during the grace period, forcing readers into this
|
||||
* slowpath. Only trace when a writer is actually waiting for
|
||||
* readers to drain.
|
||||
*/
|
||||
if (trace_contended_release_enabled() && rcuwait_active(&sem->writer))
|
||||
trace_call__contended_release(sem);
|
||||
/*
|
||||
* slowpath; reader will only ever wake a single blocked
|
||||
* writer.
|
||||
*/
|
||||
smp_mb(); /* B matches C */
|
||||
/*
|
||||
* In other words, if they see our decrement (presumably to
|
||||
* aggregate zero, as that is the only time it matters) they
|
||||
* will also see our critical section.
|
||||
*/
|
||||
this_cpu_dec(*sem->read_count);
|
||||
rcuwait_wake_up(&sem->writer);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__percpu_up_read);
|
||||
@@ -484,6 +484,7 @@ static __always_inline bool __waiter_less(struct rb_node *a, const struct rb_nod
|
||||
|
||||
static __always_inline void
|
||||
rt_mutex_enqueue(struct rt_mutex_base *lock, struct rt_mutex_waiter *waiter)
|
||||
__must_hold(&lock->wait_lock)
|
||||
{
|
||||
lockdep_assert_held(&lock->wait_lock);
|
||||
|
||||
@@ -492,6 +493,7 @@ rt_mutex_enqueue(struct rt_mutex_base *lock, struct rt_mutex_waiter *waiter)
|
||||
|
||||
static __always_inline void
|
||||
rt_mutex_dequeue(struct rt_mutex_base *lock, struct rt_mutex_waiter *waiter)
|
||||
__must_hold(&lock->wait_lock)
|
||||
{
|
||||
lockdep_assert_held(&lock->wait_lock);
|
||||
|
||||
@@ -1092,6 +1094,7 @@ static int __sched rt_mutex_adjust_prio_chain(struct task_struct *task,
|
||||
static int __sched
|
||||
try_to_take_rt_mutex(struct rt_mutex_base *lock, struct task_struct *task,
|
||||
struct rt_mutex_waiter *waiter)
|
||||
__must_hold(&lock->wait_lock)
|
||||
{
|
||||
lockdep_assert_held(&lock->wait_lock);
|
||||
|
||||
@@ -1319,6 +1322,7 @@ static int __sched task_blocks_on_rt_mutex(struct rt_mutex_base *lock,
|
||||
*/
|
||||
static void __sched mark_wakeup_next_waiter(struct rt_wake_q_head *wqh,
|
||||
struct rt_mutex_base *lock)
|
||||
__must_hold(&lock->wait_lock)
|
||||
{
|
||||
struct rt_mutex_waiter *waiter;
|
||||
|
||||
@@ -1466,6 +1470,7 @@ static void __sched rt_mutex_slowunlock(struct rt_mutex_base *lock)
|
||||
raw_spin_lock_irqsave(&lock->wait_lock, flags);
|
||||
}
|
||||
|
||||
trace_contended_release(lock);
|
||||
/*
|
||||
* The wakeup next waiter path does not suffer from the above
|
||||
* race. See the comments there.
|
||||
|
||||
@@ -41,6 +41,7 @@ static __always_inline int __rt_mutex_lock_common(struct rt_mutex *lock,
|
||||
unsigned int state,
|
||||
struct lockdep_map *nest_lock,
|
||||
unsigned int subclass)
|
||||
__cond_acquires(0, lock)
|
||||
{
|
||||
int ret;
|
||||
|
||||
@@ -67,13 +68,27 @@ EXPORT_SYMBOL(rt_mutex_base_init);
|
||||
*/
|
||||
void __sched rt_mutex_lock_nested(struct rt_mutex *lock, unsigned int subclass)
|
||||
{
|
||||
__rt_mutex_lock_common(lock, TASK_UNINTERRUPTIBLE, NULL, subclass);
|
||||
if (__rt_mutex_lock_common(lock, TASK_UNINTERRUPTIBLE, NULL, subclass) == 0)
|
||||
return;
|
||||
/*
|
||||
* The code below is never reached because __rt_mutex_lock_common() only
|
||||
* returns an error code if interrupted by a signal or upon a timeout.
|
||||
*/
|
||||
WARN_ON_ONCE(true);
|
||||
__acquire(lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rt_mutex_lock_nested);
|
||||
|
||||
void __sched _rt_mutex_lock_nest_lock(struct rt_mutex *lock, struct lockdep_map *nest_lock)
|
||||
{
|
||||
__rt_mutex_lock_common(lock, TASK_UNINTERRUPTIBLE, nest_lock, 0);
|
||||
if (__rt_mutex_lock_common(lock, TASK_UNINTERRUPTIBLE, nest_lock, 0) == 0)
|
||||
return;
|
||||
/*
|
||||
* The code below is never reached because __rt_mutex_lock_common() only
|
||||
* returns an error code if interrupted by a signal or upon a timeout.
|
||||
*/
|
||||
WARN_ON_ONCE(true);
|
||||
__acquire(lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(_rt_mutex_lock_nest_lock);
|
||||
|
||||
@@ -86,7 +101,14 @@ EXPORT_SYMBOL_GPL(_rt_mutex_lock_nest_lock);
|
||||
*/
|
||||
void __sched rt_mutex_lock(struct rt_mutex *lock)
|
||||
{
|
||||
__rt_mutex_lock_common(lock, TASK_UNINTERRUPTIBLE, NULL, 0);
|
||||
if (__rt_mutex_lock_common(lock, TASK_UNINTERRUPTIBLE, NULL, 0) == 0)
|
||||
return;
|
||||
/*
|
||||
* The code below is never reached because __rt_mutex_lock_common() only
|
||||
* returns an error code if interrupted by a signal or upon a timeout.
|
||||
*/
|
||||
WARN_ON_ONCE(true);
|
||||
__acquire(lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rt_mutex_lock);
|
||||
#endif
|
||||
@@ -157,6 +179,7 @@ void __sched rt_mutex_unlock(struct rt_mutex *lock)
|
||||
{
|
||||
mutex_release(&lock->dep_map, _RET_IP_);
|
||||
__rt_mutex_unlock(&lock->rtmutex);
|
||||
__release(lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rt_mutex_unlock);
|
||||
|
||||
@@ -182,6 +205,7 @@ int __sched __rt_mutex_futex_trylock(struct rt_mutex_base *lock)
|
||||
*/
|
||||
bool __sched __rt_mutex_futex_unlock(struct rt_mutex_base *lock,
|
||||
struct rt_wake_q_head *wqh)
|
||||
__must_hold(&lock->wait_lock)
|
||||
{
|
||||
lockdep_assert_held(&lock->wait_lock);
|
||||
|
||||
@@ -312,6 +336,7 @@ int __sched __rt_mutex_start_proxy_lock(struct rt_mutex_base *lock,
|
||||
struct rt_mutex_waiter *waiter,
|
||||
struct task_struct *task,
|
||||
struct wake_q_head *wake_q)
|
||||
__must_hold(&lock->wait_lock)
|
||||
{
|
||||
int ret;
|
||||
|
||||
|
||||
@@ -174,6 +174,8 @@ static void __sched __rwbase_read_unlock(struct rwbase_rt *rwb,
|
||||
static __always_inline void rwbase_read_unlock(struct rwbase_rt *rwb,
|
||||
unsigned int state)
|
||||
{
|
||||
if (trace_contended_release_enabled() && rt_mutex_owner(&rwb->rtmutex))
|
||||
trace_call__contended_release(rwb);
|
||||
/*
|
||||
* rwb->readers can only hit 0 when a writer is waiting for the
|
||||
* active readers to leave the critical section.
|
||||
@@ -205,6 +207,8 @@ static inline void rwbase_write_unlock(struct rwbase_rt *rwb)
|
||||
unsigned long flags;
|
||||
|
||||
raw_spin_lock_irqsave(&rtm->wait_lock, flags);
|
||||
if (trace_contended_release_enabled() && rt_mutex_has_waiters(rtm))
|
||||
trace_call__contended_release(rwb);
|
||||
__rwbase_write_unlock(rwb, WRITER_BIAS, flags);
|
||||
}
|
||||
|
||||
@@ -214,6 +218,8 @@ static inline void rwbase_write_downgrade(struct rwbase_rt *rwb)
|
||||
unsigned long flags;
|
||||
|
||||
raw_spin_lock_irqsave(&rtm->wait_lock, flags);
|
||||
if (trace_contended_release_enabled() && rt_mutex_has_waiters(rtm))
|
||||
trace_call__contended_release(rwb);
|
||||
/* Release it and account current as reader */
|
||||
__rwbase_write_unlock(rwb, WRITER_BIAS - 1, flags);
|
||||
}
|
||||
|
||||
@@ -1387,6 +1387,8 @@ static inline void __up_read(struct rw_semaphore *sem)
|
||||
rwsem_clear_reader_owned(sem);
|
||||
tmp = atomic_long_add_return_release(-RWSEM_READER_BIAS, &sem->count);
|
||||
DEBUG_RWSEMS_WARN_ON(tmp < 0, sem);
|
||||
if (trace_contended_release_enabled() && (tmp & RWSEM_FLAG_WAITERS))
|
||||
trace_call__contended_release(sem);
|
||||
if (unlikely((tmp & (RWSEM_LOCK_MASK|RWSEM_FLAG_WAITERS)) ==
|
||||
RWSEM_FLAG_WAITERS)) {
|
||||
clear_nonspinnable(sem);
|
||||
@@ -1413,8 +1415,10 @@ static inline void __up_write(struct rw_semaphore *sem)
|
||||
preempt_disable();
|
||||
rwsem_clear_owner(sem);
|
||||
tmp = atomic_long_fetch_add_release(-RWSEM_WRITER_LOCKED, &sem->count);
|
||||
if (unlikely(tmp & RWSEM_FLAG_WAITERS))
|
||||
if (unlikely(tmp & RWSEM_FLAG_WAITERS)) {
|
||||
trace_contended_release(sem);
|
||||
rwsem_wake(sem);
|
||||
}
|
||||
preempt_enable();
|
||||
}
|
||||
|
||||
@@ -1437,8 +1441,10 @@ static inline void __downgrade_write(struct rw_semaphore *sem)
|
||||
tmp = atomic_long_fetch_add_release(
|
||||
-RWSEM_WRITER_LOCKED+RWSEM_READER_BIAS, &sem->count);
|
||||
rwsem_set_reader_owned(sem);
|
||||
if (tmp & RWSEM_FLAG_WAITERS)
|
||||
if (tmp & RWSEM_FLAG_WAITERS) {
|
||||
trace_contended_release(sem);
|
||||
rwsem_downgrade_wake(sem);
|
||||
}
|
||||
preempt_enable();
|
||||
}
|
||||
|
||||
|
||||
@@ -230,6 +230,10 @@ void __sched up(struct semaphore *sem)
|
||||
sem->count++;
|
||||
else
|
||||
__up(sem, &wake_q);
|
||||
|
||||
if (trace_contended_release_enabled() && !wake_q_empty(&wake_q))
|
||||
trace_call__contended_release(sem);
|
||||
|
||||
raw_spin_unlock_irqrestore(&sem->lock, flags);
|
||||
if (!wake_q_empty(&wake_q))
|
||||
wake_up_q(&wake_q);
|
||||
|
||||
+48
-50
@@ -164,8 +164,26 @@
|
||||
| MEMBARRIER_PRIVATE_EXPEDITED_RSEQ_BITMASK \
|
||||
| MEMBARRIER_CMD_GET_REGISTRATIONS)
|
||||
|
||||
/*
|
||||
* Scoped guard for memory barriers on entry and exit.
|
||||
* Matches memory barriers before & after rq->curr modification in scheduler.
|
||||
*/
|
||||
DEFINE_LOCK_GUARD_0(mb, smp_mb(), smp_mb())
|
||||
static DEFINE_MUTEX(membarrier_ipi_mutex);
|
||||
static DEFINE_PER_CPU(struct mutex, membarrier_cpu_mutexes);
|
||||
|
||||
#define SERIALIZE_IPI() guard(mutex)(&membarrier_ipi_mutex)
|
||||
#define SERIALIZE_IPI_CPU(cpu_id) guard(mutex)(&per_cpu(membarrier_cpu_mutexes, cpu_id))
|
||||
|
||||
static int __init membarrier_init(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for_each_possible_cpu(i)
|
||||
mutex_init(&per_cpu(membarrier_cpu_mutexes, i));
|
||||
return 0;
|
||||
}
|
||||
core_initcall(membarrier_init);
|
||||
|
||||
static void ipi_mb(void *info)
|
||||
{
|
||||
@@ -258,23 +276,19 @@ void membarrier_update_current_mm(struct mm_struct *next_mm)
|
||||
|
||||
static int membarrier_global_expedited(void)
|
||||
{
|
||||
cpumask_var_t __free(free_cpumask_var) tmpmask = CPUMASK_VAR_NULL;
|
||||
int cpu;
|
||||
cpumask_var_t tmpmask;
|
||||
|
||||
if (num_online_cpus() == 1)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Matches memory barriers after rq->curr modification in
|
||||
* scheduler.
|
||||
*/
|
||||
smp_mb(); /* system call entry is not a mb. */
|
||||
|
||||
if (!zalloc_cpumask_var(&tmpmask, GFP_KERNEL))
|
||||
return -ENOMEM;
|
||||
|
||||
guard(mb)();
|
||||
SERIALIZE_IPI();
|
||||
cpus_read_lock();
|
||||
guard(cpus_read_lock)();
|
||||
|
||||
rcu_read_lock();
|
||||
for_each_online_cpu(cpu) {
|
||||
struct task_struct *p;
|
||||
@@ -310,21 +324,11 @@ static int membarrier_global_expedited(void)
|
||||
smp_call_function_many(tmpmask, ipi_mb, NULL, 1);
|
||||
preempt_enable();
|
||||
|
||||
free_cpumask_var(tmpmask);
|
||||
cpus_read_unlock();
|
||||
|
||||
/*
|
||||
* Memory barrier on the caller thread _after_ we finished
|
||||
* waiting for the last IPI. Matches memory barriers before
|
||||
* rq->curr modification in scheduler.
|
||||
*/
|
||||
smp_mb(); /* exit from system call is not a mb */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int membarrier_private_expedited(int flags, int cpu_id)
|
||||
{
|
||||
cpumask_var_t tmpmask;
|
||||
struct mm_struct *mm = current->mm;
|
||||
smp_call_func_t ipi_func = ipi_mb;
|
||||
|
||||
@@ -361,30 +365,45 @@ static int membarrier_private_expedited(int flags, int cpu_id)
|
||||
* On RISC-V, this barrier pairing is also needed for the
|
||||
* SYNC_CORE command when switching between processes, cf.
|
||||
* the inline comments in membarrier_arch_switch_mm().
|
||||
*
|
||||
* Memory barrier on the caller thread _after_ we finished
|
||||
* waiting for the last IPI. Matches memory barriers before
|
||||
* rq->curr modification in scheduler.
|
||||
*/
|
||||
smp_mb(); /* system call entry is not a mb. */
|
||||
|
||||
if (cpu_id < 0 && !zalloc_cpumask_var(&tmpmask, GFP_KERNEL))
|
||||
return -ENOMEM;
|
||||
|
||||
SERIALIZE_IPI();
|
||||
cpus_read_lock();
|
||||
|
||||
guard(mb)();
|
||||
if (cpu_id >= 0) {
|
||||
if (cpu_id >= nr_cpu_ids || !cpu_possible(cpu_id))
|
||||
return 0;
|
||||
|
||||
SERIALIZE_IPI_CPU(cpu_id);
|
||||
guard(cpus_read_lock)();
|
||||
struct task_struct *p;
|
||||
|
||||
if (cpu_id >= nr_cpu_ids || !cpu_online(cpu_id))
|
||||
goto out;
|
||||
if (!cpu_online(cpu_id))
|
||||
return 0;
|
||||
|
||||
rcu_read_lock();
|
||||
p = rcu_dereference(cpu_rq(cpu_id)->curr);
|
||||
if (!p || p->mm != mm) {
|
||||
rcu_read_unlock();
|
||||
goto out;
|
||||
return 0;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
/*
|
||||
* smp_call_function_single() will call ipi_func() if cpu_id
|
||||
* is the calling CPU.
|
||||
*/
|
||||
smp_call_function_single(cpu_id, ipi_func, NULL, 1);
|
||||
} else {
|
||||
cpumask_var_t __free(free_cpumask_var) tmpmask = CPUMASK_VAR_NULL;
|
||||
int cpu;
|
||||
|
||||
if (!zalloc_cpumask_var(&tmpmask, GFP_KERNEL))
|
||||
return -ENOMEM;
|
||||
|
||||
SERIALIZE_IPI();
|
||||
guard(cpus_read_lock)();
|
||||
|
||||
rcu_read_lock();
|
||||
for_each_online_cpu(cpu) {
|
||||
struct task_struct *p;
|
||||
@@ -394,15 +413,6 @@ static int membarrier_private_expedited(int flags, int cpu_id)
|
||||
__cpumask_set_cpu(cpu, tmpmask);
|
||||
}
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
if (cpu_id >= 0) {
|
||||
/*
|
||||
* smp_call_function_single() will call ipi_func() if cpu_id
|
||||
* is the calling CPU.
|
||||
*/
|
||||
smp_call_function_single(cpu_id, ipi_func, NULL, 1);
|
||||
} else {
|
||||
/*
|
||||
* For regular membarrier, we can save a few cycles by
|
||||
* skipping the current cpu -- we're about to do smp_mb()
|
||||
@@ -429,18 +439,6 @@ static int membarrier_private_expedited(int flags, int cpu_id)
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
if (cpu_id < 0)
|
||||
free_cpumask_var(tmpmask);
|
||||
cpus_read_unlock();
|
||||
|
||||
/*
|
||||
* Memory barrier on the caller thread _after_ we finished
|
||||
* waiting for the last IPI. Matches memory barriers before
|
||||
* rq->curr modification in scheduler.
|
||||
*/
|
||||
smp_mb(); /* exit from system call is not a mb */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user