mirror of
https://github.com/torvalds/linux
synced 2026-07-22 18:20:51 +09:00
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
...
687 lines
21 KiB
C
687 lines
21 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* Copyright (C) 2010-2017 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
|
|
*
|
|
* membarrier system call
|
|
*/
|
|
#include <uapi/linux/membarrier.h>
|
|
#include "sched.h"
|
|
|
|
/*
|
|
* For documentation purposes, here are some membarrier ordering
|
|
* scenarios to keep in mind:
|
|
*
|
|
* A) Userspace thread execution after IPI vs membarrier's memory
|
|
* barrier before sending the IPI
|
|
*
|
|
* Userspace variables:
|
|
*
|
|
* int x = 0, y = 0;
|
|
*
|
|
* The memory barrier at the start of membarrier() on CPU0 is necessary in
|
|
* order to enforce the guarantee that any writes occurring on CPU0 before
|
|
* the membarrier() is executed will be visible to any code executing on
|
|
* CPU1 after the IPI-induced memory barrier:
|
|
*
|
|
* CPU0 CPU1
|
|
*
|
|
* x = 1
|
|
* membarrier():
|
|
* a: smp_mb()
|
|
* b: send IPI IPI-induced mb
|
|
* c: smp_mb()
|
|
* r2 = y
|
|
* y = 1
|
|
* barrier()
|
|
* r1 = x
|
|
*
|
|
* BUG_ON(r1 == 0 && r2 == 0)
|
|
*
|
|
* The write to y and load from x by CPU1 are unordered by the hardware,
|
|
* so it's possible to have "r1 = x" reordered before "y = 1" at any
|
|
* point after (b). If the memory barrier at (a) is omitted, then "x = 1"
|
|
* can be reordered after (a) (although not after (c)), so we get r1 == 0
|
|
* and r2 == 0. This violates the guarantee that membarrier() is
|
|
* supposed by provide.
|
|
*
|
|
* The timing of the memory barrier at (a) has to ensure that it executes
|
|
* before the IPI-induced memory barrier on CPU1.
|
|
*
|
|
* B) Userspace thread execution before IPI vs membarrier's memory
|
|
* barrier after completing the IPI
|
|
*
|
|
* Userspace variables:
|
|
*
|
|
* int x = 0, y = 0;
|
|
*
|
|
* The memory barrier at the end of membarrier() on CPU0 is necessary in
|
|
* order to enforce the guarantee that any writes occurring on CPU1 before
|
|
* the membarrier() is executed will be visible to any code executing on
|
|
* CPU0 after the membarrier():
|
|
*
|
|
* CPU0 CPU1
|
|
*
|
|
* x = 1
|
|
* barrier()
|
|
* y = 1
|
|
* r2 = y
|
|
* membarrier():
|
|
* a: smp_mb()
|
|
* b: send IPI IPI-induced mb
|
|
* c: smp_mb()
|
|
* r1 = x
|
|
* BUG_ON(r1 == 0 && r2 == 1)
|
|
*
|
|
* The writes to x and y are unordered by the hardware, so it's possible to
|
|
* have "r2 = 1" even though the write to x doesn't execute until (b). If
|
|
* the memory barrier at (c) is omitted then "r1 = x" can be reordered
|
|
* before (b) (although not before (a)), so we get "r1 = 0". This violates
|
|
* the guarantee that membarrier() is supposed to provide.
|
|
*
|
|
* The timing of the memory barrier at (c) has to ensure that it executes
|
|
* after the IPI-induced memory barrier on CPU1.
|
|
*
|
|
* C) Scheduling userspace thread -> kthread -> userspace thread vs membarrier
|
|
*
|
|
* CPU0 CPU1
|
|
*
|
|
* membarrier():
|
|
* a: smp_mb()
|
|
* d: switch to kthread (includes mb)
|
|
* b: read rq->curr->mm == NULL
|
|
* e: switch to user (includes mb)
|
|
* c: smp_mb()
|
|
*
|
|
* Using the scenario from (A), we can show that (a) needs to be paired
|
|
* with (e). Using the scenario from (B), we can show that (c) needs to
|
|
* be paired with (d).
|
|
*
|
|
* D) exit_mm vs membarrier
|
|
*
|
|
* Two thread groups are created, A and B. Thread group B is created by
|
|
* issuing clone from group A with flag CLONE_VM set, but not CLONE_THREAD.
|
|
* Let's assume we have a single thread within each thread group (Thread A
|
|
* and Thread B). Thread A runs on CPU0, Thread B runs on CPU1.
|
|
*
|
|
* CPU0 CPU1
|
|
*
|
|
* membarrier():
|
|
* a: smp_mb()
|
|
* exit_mm():
|
|
* d: smp_mb()
|
|
* e: current->mm = NULL
|
|
* b: read rq->curr->mm == NULL
|
|
* c: smp_mb()
|
|
*
|
|
* Using scenario (B), we can show that (c) needs to be paired with (d).
|
|
*
|
|
* E) kthread_{use,unuse}_mm vs membarrier
|
|
*
|
|
* CPU0 CPU1
|
|
*
|
|
* membarrier():
|
|
* a: smp_mb()
|
|
* kthread_unuse_mm()
|
|
* d: smp_mb()
|
|
* e: current->mm = NULL
|
|
* b: read rq->curr->mm == NULL
|
|
* kthread_use_mm()
|
|
* f: current->mm = mm
|
|
* g: smp_mb()
|
|
* c: smp_mb()
|
|
*
|
|
* Using the scenario from (A), we can show that (a) needs to be paired
|
|
* with (g). Using the scenario from (B), we can show that (c) needs to
|
|
* be paired with (d).
|
|
*/
|
|
|
|
/*
|
|
* Bitmask made from a "or" of all commands within enum membarrier_cmd,
|
|
* except MEMBARRIER_CMD_QUERY.
|
|
*/
|
|
#ifdef CONFIG_ARCH_HAS_MEMBARRIER_SYNC_CORE
|
|
#define MEMBARRIER_PRIVATE_EXPEDITED_SYNC_CORE_BITMASK \
|
|
(MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE \
|
|
| MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE)
|
|
#else
|
|
#define MEMBARRIER_PRIVATE_EXPEDITED_SYNC_CORE_BITMASK 0
|
|
#endif
|
|
|
|
#ifdef CONFIG_RSEQ
|
|
#define MEMBARRIER_PRIVATE_EXPEDITED_RSEQ_BITMASK \
|
|
(MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ \
|
|
| MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_RSEQ)
|
|
#else
|
|
#define MEMBARRIER_PRIVATE_EXPEDITED_RSEQ_BITMASK 0
|
|
#endif
|
|
|
|
#define MEMBARRIER_CMD_BITMASK \
|
|
(MEMBARRIER_CMD_GLOBAL | MEMBARRIER_CMD_GLOBAL_EXPEDITED \
|
|
| MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITED \
|
|
| MEMBARRIER_CMD_PRIVATE_EXPEDITED \
|
|
| MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED \
|
|
| MEMBARRIER_PRIVATE_EXPEDITED_SYNC_CORE_BITMASK \
|
|
| 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)
|
|
{
|
|
smp_mb(); /* IPIs should be serializing but paranoid. */
|
|
}
|
|
|
|
static void ipi_sync_core(void *info)
|
|
{
|
|
/*
|
|
* The smp_mb() in membarrier after all the IPIs is supposed to
|
|
* ensure that memory on remote CPUs that occur before the IPI
|
|
* become visible to membarrier()'s caller -- see scenario B in
|
|
* the big comment at the top of this file.
|
|
*
|
|
* A sync_core() would provide this guarantee, but
|
|
* sync_core_before_usermode() might end up being deferred until
|
|
* after membarrier()'s smp_mb().
|
|
*/
|
|
smp_mb(); /* IPIs should be serializing but paranoid. */
|
|
|
|
sync_core_before_usermode();
|
|
}
|
|
|
|
static void ipi_rseq(void *info)
|
|
{
|
|
/*
|
|
* Ensure that all stores done by the calling thread are visible
|
|
* to the current task before the current task resumes. We could
|
|
* probably optimize this away on most architectures, but by the
|
|
* time we've already sent an IPI, the cost of the extra smp_mb()
|
|
* is negligible.
|
|
*/
|
|
smp_mb();
|
|
/*
|
|
* Legacy mode requires that IDs are written and the critical section is
|
|
* evaluated. V2 optimized mode handles the critical section and IDs are
|
|
* only updated if they change as a consequence of preemption after
|
|
* return from this IPI.
|
|
*/
|
|
if (rseq_v2(current))
|
|
rseq_sched_switch_event(current);
|
|
else
|
|
rseq_force_update();
|
|
}
|
|
|
|
static void ipi_sync_rq_state(void *info)
|
|
{
|
|
struct mm_struct *mm = (struct mm_struct *) info;
|
|
|
|
if (current->mm != mm)
|
|
return;
|
|
this_cpu_write(runqueues.membarrier_state,
|
|
atomic_read(&mm->membarrier_state));
|
|
/*
|
|
* Issue a memory barrier after setting
|
|
* MEMBARRIER_STATE_GLOBAL_EXPEDITED in the current runqueue to
|
|
* guarantee that no memory access following registration is reordered
|
|
* before registration.
|
|
*/
|
|
smp_mb();
|
|
}
|
|
|
|
void membarrier_exec_mmap(struct mm_struct *mm)
|
|
{
|
|
/*
|
|
* Issue a memory barrier before clearing membarrier_state to
|
|
* guarantee that no memory access prior to exec is reordered after
|
|
* clearing this state.
|
|
*/
|
|
smp_mb();
|
|
atomic_set(&mm->membarrier_state, 0);
|
|
/*
|
|
* Keep the runqueue membarrier_state in sync with this mm
|
|
* membarrier_state.
|
|
*/
|
|
this_cpu_write(runqueues.membarrier_state, 0);
|
|
}
|
|
|
|
void membarrier_update_current_mm(struct mm_struct *next_mm)
|
|
{
|
|
struct rq *rq = this_rq();
|
|
int membarrier_state = 0;
|
|
|
|
if (next_mm)
|
|
membarrier_state = atomic_read(&next_mm->membarrier_state);
|
|
if (READ_ONCE(rq->membarrier_state) == membarrier_state)
|
|
return;
|
|
WRITE_ONCE(rq->membarrier_state, membarrier_state);
|
|
}
|
|
|
|
static int membarrier_global_expedited(void)
|
|
{
|
|
cpumask_var_t __free(free_cpumask_var) tmpmask = CPUMASK_VAR_NULL;
|
|
int cpu;
|
|
|
|
if (num_online_cpus() == 1)
|
|
return 0;
|
|
|
|
if (!zalloc_cpumask_var(&tmpmask, GFP_KERNEL))
|
|
return -ENOMEM;
|
|
|
|
guard(mb)();
|
|
SERIALIZE_IPI();
|
|
guard(cpus_read_lock)();
|
|
|
|
rcu_read_lock();
|
|
for_each_online_cpu(cpu) {
|
|
struct task_struct *p;
|
|
|
|
/*
|
|
* Skipping the current CPU is OK even through we can be
|
|
* migrated at any point. The current CPU, at the point
|
|
* where we read raw_smp_processor_id(), is ensured to
|
|
* be in program order with respect to the caller
|
|
* thread. Therefore, we can skip this CPU from the
|
|
* iteration.
|
|
*/
|
|
if (cpu == raw_smp_processor_id())
|
|
continue;
|
|
|
|
if (!(READ_ONCE(cpu_rq(cpu)->membarrier_state) &
|
|
MEMBARRIER_STATE_GLOBAL_EXPEDITED))
|
|
continue;
|
|
|
|
/*
|
|
* Skip the CPU if it runs a kernel thread which is not using
|
|
* a task mm.
|
|
*/
|
|
p = rcu_dereference(cpu_rq(cpu)->curr);
|
|
if (!p->mm)
|
|
continue;
|
|
|
|
__cpumask_set_cpu(cpu, tmpmask);
|
|
}
|
|
rcu_read_unlock();
|
|
|
|
preempt_disable();
|
|
smp_call_function_many(tmpmask, ipi_mb, NULL, 1);
|
|
preempt_enable();
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int membarrier_private_expedited(int flags, int cpu_id)
|
|
{
|
|
struct mm_struct *mm = current->mm;
|
|
smp_call_func_t ipi_func = ipi_mb;
|
|
|
|
if (flags == MEMBARRIER_FLAG_SYNC_CORE) {
|
|
if (!IS_ENABLED(CONFIG_ARCH_HAS_MEMBARRIER_SYNC_CORE))
|
|
return -EINVAL;
|
|
if (!(atomic_read(&mm->membarrier_state) &
|
|
MEMBARRIER_STATE_PRIVATE_EXPEDITED_SYNC_CORE_READY))
|
|
return -EPERM;
|
|
ipi_func = ipi_sync_core;
|
|
prepare_sync_core_cmd(mm);
|
|
} else if (flags == MEMBARRIER_FLAG_RSEQ) {
|
|
if (!IS_ENABLED(CONFIG_RSEQ))
|
|
return -EINVAL;
|
|
if (!(atomic_read(&mm->membarrier_state) &
|
|
MEMBARRIER_STATE_PRIVATE_EXPEDITED_RSEQ_READY))
|
|
return -EPERM;
|
|
ipi_func = ipi_rseq;
|
|
} else {
|
|
WARN_ON_ONCE(flags);
|
|
if (!(atomic_read(&mm->membarrier_state) &
|
|
MEMBARRIER_STATE_PRIVATE_EXPEDITED_READY))
|
|
return -EPERM;
|
|
}
|
|
|
|
if (flags != MEMBARRIER_FLAG_SYNC_CORE &&
|
|
(atomic_read(&mm->mm_users) == 1 || num_online_cpus() == 1))
|
|
return 0;
|
|
|
|
/*
|
|
* Matches memory barriers after rq->curr modification in
|
|
* scheduler.
|
|
*
|
|
* 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.
|
|
*/
|
|
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_online(cpu_id))
|
|
return 0;
|
|
|
|
rcu_read_lock();
|
|
p = rcu_dereference(cpu_rq(cpu_id)->curr);
|
|
if (!p || p->mm != mm) {
|
|
rcu_read_unlock();
|
|
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;
|
|
|
|
p = rcu_dereference(cpu_rq(cpu)->curr);
|
|
if (p && p->mm == mm)
|
|
__cpumask_set_cpu(cpu, tmpmask);
|
|
}
|
|
rcu_read_unlock();
|
|
/*
|
|
* For regular membarrier, we can save a few cycles by
|
|
* skipping the current cpu -- we're about to do smp_mb()
|
|
* below, and if we migrate to a different cpu, this cpu
|
|
* and the new cpu will execute a full barrier in the
|
|
* scheduler.
|
|
*
|
|
* For SYNC_CORE, we do need a barrier on the current cpu --
|
|
* otherwise, if we are migrated and replaced by a different
|
|
* task in the same mm just before, during, or after
|
|
* membarrier, we will end up with some thread in the mm
|
|
* running without a core sync.
|
|
*
|
|
* For RSEQ, don't invoke rseq_sched_switch_event() on the
|
|
* caller. User code is not supposed to issue syscalls at
|
|
* all from inside an rseq critical section.
|
|
*/
|
|
if (flags != MEMBARRIER_FLAG_SYNC_CORE) {
|
|
preempt_disable();
|
|
smp_call_function_many(tmpmask, ipi_func, NULL, true);
|
|
preempt_enable();
|
|
} else {
|
|
on_each_cpu_mask(tmpmask, ipi_func, NULL, true);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int sync_runqueues_membarrier_state(struct mm_struct *mm)
|
|
{
|
|
int membarrier_state = atomic_read(&mm->membarrier_state);
|
|
cpumask_var_t tmpmask;
|
|
int cpu;
|
|
|
|
if (atomic_read(&mm->mm_users) == 1 || num_online_cpus() == 1) {
|
|
this_cpu_write(runqueues.membarrier_state, membarrier_state);
|
|
|
|
/*
|
|
* For single mm user, we can simply issue a memory barrier
|
|
* after setting MEMBARRIER_STATE_GLOBAL_EXPEDITED in the
|
|
* mm and in the current runqueue to guarantee that no memory
|
|
* access following registration is reordered before
|
|
* registration.
|
|
*/
|
|
smp_mb();
|
|
return 0;
|
|
}
|
|
|
|
if (!zalloc_cpumask_var(&tmpmask, GFP_KERNEL))
|
|
return -ENOMEM;
|
|
|
|
/*
|
|
* For mm with multiple users, we need to ensure all future
|
|
* scheduler executions will observe @mm's new membarrier
|
|
* state.
|
|
*/
|
|
synchronize_rcu();
|
|
|
|
/*
|
|
* For each cpu runqueue, if the task's mm match @mm, ensure that all
|
|
* @mm's membarrier state set bits are also set in the runqueue's
|
|
* membarrier state. This ensures that a runqueue scheduling
|
|
* between threads which are users of @mm has its membarrier state
|
|
* updated.
|
|
*/
|
|
SERIALIZE_IPI();
|
|
cpus_read_lock();
|
|
rcu_read_lock();
|
|
for_each_online_cpu(cpu) {
|
|
struct rq *rq = cpu_rq(cpu);
|
|
struct task_struct *p;
|
|
|
|
p = rcu_dereference(rq->curr);
|
|
if (p && p->mm == mm)
|
|
__cpumask_set_cpu(cpu, tmpmask);
|
|
}
|
|
rcu_read_unlock();
|
|
|
|
on_each_cpu_mask(tmpmask, ipi_sync_rq_state, mm, true);
|
|
|
|
free_cpumask_var(tmpmask);
|
|
cpus_read_unlock();
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int membarrier_register_global_expedited(void)
|
|
{
|
|
struct task_struct *p = current;
|
|
struct mm_struct *mm = p->mm;
|
|
int ret;
|
|
|
|
if (atomic_read(&mm->membarrier_state) &
|
|
MEMBARRIER_STATE_GLOBAL_EXPEDITED_READY)
|
|
return 0;
|
|
atomic_or(MEMBARRIER_STATE_GLOBAL_EXPEDITED, &mm->membarrier_state);
|
|
ret = sync_runqueues_membarrier_state(mm);
|
|
if (ret)
|
|
return ret;
|
|
atomic_or(MEMBARRIER_STATE_GLOBAL_EXPEDITED_READY,
|
|
&mm->membarrier_state);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int membarrier_register_private_expedited(int flags)
|
|
{
|
|
struct task_struct *p = current;
|
|
struct mm_struct *mm = p->mm;
|
|
int ready_state = MEMBARRIER_STATE_PRIVATE_EXPEDITED_READY,
|
|
set_state = MEMBARRIER_STATE_PRIVATE_EXPEDITED,
|
|
ret;
|
|
|
|
if (flags == MEMBARRIER_FLAG_SYNC_CORE) {
|
|
if (!IS_ENABLED(CONFIG_ARCH_HAS_MEMBARRIER_SYNC_CORE))
|
|
return -EINVAL;
|
|
ready_state =
|
|
MEMBARRIER_STATE_PRIVATE_EXPEDITED_SYNC_CORE_READY;
|
|
} else if (flags == MEMBARRIER_FLAG_RSEQ) {
|
|
if (!IS_ENABLED(CONFIG_RSEQ))
|
|
return -EINVAL;
|
|
ready_state =
|
|
MEMBARRIER_STATE_PRIVATE_EXPEDITED_RSEQ_READY;
|
|
} else {
|
|
WARN_ON_ONCE(flags);
|
|
}
|
|
|
|
/*
|
|
* We need to consider threads belonging to different thread
|
|
* groups, which use the same mm. (CLONE_VM but not
|
|
* CLONE_THREAD).
|
|
*/
|
|
if ((atomic_read(&mm->membarrier_state) & ready_state) == ready_state)
|
|
return 0;
|
|
if (flags & MEMBARRIER_FLAG_SYNC_CORE)
|
|
set_state |= MEMBARRIER_STATE_PRIVATE_EXPEDITED_SYNC_CORE;
|
|
if (flags & MEMBARRIER_FLAG_RSEQ)
|
|
set_state |= MEMBARRIER_STATE_PRIVATE_EXPEDITED_RSEQ;
|
|
atomic_or(set_state, &mm->membarrier_state);
|
|
ret = sync_runqueues_membarrier_state(mm);
|
|
if (ret)
|
|
return ret;
|
|
atomic_or(ready_state, &mm->membarrier_state);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int membarrier_get_registrations(void)
|
|
{
|
|
struct task_struct *p = current;
|
|
struct mm_struct *mm = p->mm;
|
|
int registrations_mask = 0, membarrier_state, i;
|
|
static const int states[] = {
|
|
MEMBARRIER_STATE_GLOBAL_EXPEDITED |
|
|
MEMBARRIER_STATE_GLOBAL_EXPEDITED_READY,
|
|
MEMBARRIER_STATE_PRIVATE_EXPEDITED |
|
|
MEMBARRIER_STATE_PRIVATE_EXPEDITED_READY,
|
|
MEMBARRIER_STATE_PRIVATE_EXPEDITED_SYNC_CORE |
|
|
MEMBARRIER_STATE_PRIVATE_EXPEDITED_SYNC_CORE_READY,
|
|
MEMBARRIER_STATE_PRIVATE_EXPEDITED_RSEQ |
|
|
MEMBARRIER_STATE_PRIVATE_EXPEDITED_RSEQ_READY
|
|
};
|
|
static const int registration_cmds[] = {
|
|
MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITED,
|
|
MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED,
|
|
MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE,
|
|
MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_RSEQ
|
|
};
|
|
BUILD_BUG_ON(ARRAY_SIZE(states) != ARRAY_SIZE(registration_cmds));
|
|
|
|
membarrier_state = atomic_read(&mm->membarrier_state);
|
|
for (i = 0; i < ARRAY_SIZE(states); ++i) {
|
|
if (membarrier_state & states[i]) {
|
|
registrations_mask |= registration_cmds[i];
|
|
membarrier_state &= ~states[i];
|
|
}
|
|
}
|
|
WARN_ON_ONCE(membarrier_state != 0);
|
|
return registrations_mask;
|
|
}
|
|
|
|
/**
|
|
* sys_membarrier - issue memory barriers on a set of threads
|
|
* @cmd: Takes command values defined in enum membarrier_cmd.
|
|
* @flags: Currently needs to be 0 for all commands other than
|
|
* MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ: in the latter
|
|
* case it can be MEMBARRIER_CMD_FLAG_CPU, indicating that @cpu_id
|
|
* contains the CPU on which to interrupt (= restart)
|
|
* the RSEQ critical section.
|
|
* @cpu_id: if @flags == MEMBARRIER_CMD_FLAG_CPU, indicates the cpu on which
|
|
* RSEQ CS should be interrupted (@cmd must be
|
|
* MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ).
|
|
*
|
|
* If this system call is not implemented, -ENOSYS is returned. If the
|
|
* command specified does not exist, not available on the running
|
|
* kernel, or if the command argument is invalid, this system call
|
|
* returns -EINVAL. For a given command, with flags argument set to 0,
|
|
* if this system call returns -ENOSYS or -EINVAL, it is guaranteed to
|
|
* always return the same value until reboot. In addition, it can return
|
|
* -ENOMEM if there is not enough memory available to perform the system
|
|
* call.
|
|
*
|
|
* All memory accesses performed in program order from each targeted thread
|
|
* is guaranteed to be ordered with respect to sys_membarrier(). If we use
|
|
* the semantic "barrier()" to represent a compiler barrier forcing memory
|
|
* accesses to be performed in program order across the barrier, and
|
|
* smp_mb() to represent explicit memory barriers forcing full memory
|
|
* ordering across the barrier, we have the following ordering table for
|
|
* each pair of barrier(), sys_membarrier() and smp_mb():
|
|
*
|
|
* The pair ordering is detailed as (O: ordered, X: not ordered):
|
|
*
|
|
* barrier() smp_mb() sys_membarrier()
|
|
* barrier() X X O
|
|
* smp_mb() X O O
|
|
* sys_membarrier() O O O
|
|
*/
|
|
SYSCALL_DEFINE3(membarrier, int, cmd, unsigned int, flags, int, cpu_id)
|
|
{
|
|
switch (cmd) {
|
|
case MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ:
|
|
if (unlikely(flags && flags != MEMBARRIER_CMD_FLAG_CPU))
|
|
return -EINVAL;
|
|
break;
|
|
default:
|
|
if (unlikely(flags))
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!(flags & MEMBARRIER_CMD_FLAG_CPU))
|
|
cpu_id = -1;
|
|
|
|
switch (cmd) {
|
|
case MEMBARRIER_CMD_QUERY:
|
|
{
|
|
int cmd_mask = MEMBARRIER_CMD_BITMASK;
|
|
|
|
if (tick_nohz_full_enabled())
|
|
cmd_mask &= ~MEMBARRIER_CMD_GLOBAL;
|
|
return cmd_mask;
|
|
}
|
|
case MEMBARRIER_CMD_GLOBAL:
|
|
/* MEMBARRIER_CMD_GLOBAL is not compatible with nohz_full. */
|
|
if (tick_nohz_full_enabled())
|
|
return -EINVAL;
|
|
if (num_online_cpus() > 1)
|
|
synchronize_rcu();
|
|
return 0;
|
|
case MEMBARRIER_CMD_GLOBAL_EXPEDITED:
|
|
return membarrier_global_expedited();
|
|
case MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITED:
|
|
return membarrier_register_global_expedited();
|
|
case MEMBARRIER_CMD_PRIVATE_EXPEDITED:
|
|
return membarrier_private_expedited(0, cpu_id);
|
|
case MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED:
|
|
return membarrier_register_private_expedited(0);
|
|
case MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE:
|
|
return membarrier_private_expedited(MEMBARRIER_FLAG_SYNC_CORE, cpu_id);
|
|
case MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE:
|
|
return membarrier_register_private_expedited(MEMBARRIER_FLAG_SYNC_CORE);
|
|
case MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ:
|
|
return membarrier_private_expedited(MEMBARRIER_FLAG_RSEQ, cpu_id);
|
|
case MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_RSEQ:
|
|
return membarrier_register_private_expedited(MEMBARRIER_FLAG_RSEQ);
|
|
case MEMBARRIER_CMD_GET_REGISTRATIONS:
|
|
return membarrier_get_registrations();
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
}
|