History

membarrier: Provide expedited private command

Implement MEMBARRIER_CMD_PRIVATE_EXPEDITED with IPIs using cpumask built from all runqueues for which current thread's mm is the same as the thread calling sys_membarrier. It executes faster than the non-expedited variant (no blocking). It also works on NOHZ_FULL configurations. Scheduler-wise, it requires a memory barrier before and after context switching between processes (which have different mm). The memory barrier before context switch is already present. For the barrier after context switch: * Our TSO archs can do RELEASE without being a full barrier. Look at x86 spin_unlock() being a regular STORE for example. But for those archs, all atomics imply smp_mb and all of them have atomic ops in switch_mm() for mm_cpumask(), and on x86 the CR3 load acts as a full barrier. * From all weakly ordered machines, only ARM64 and PPC can do RELEASE, the rest does indeed do smp_mb(), so there the spin_unlock() is a full barrier and we're good. * ARM64 has a very heavy barrier in switch_to(), which suffices. * PPC just removed its barrier from switch_to(), but appears to be talking about adding something to switch_mm(). So add a smp_mb__after_unlock_lock() for now, until this is settled on the PPC side. Changes since v3: - Properly document the memory barriers provided by each architecture. Changes since v2: - Address comments from Peter Zijlstra, - Add smp_mb__after_unlock_lock() after finish_lock_switch() in finish_task_switch() to add the memory barrier we need after storing to rq->curr. This is much simpler than the previous approach relying on atomic_dec_and_test() in mmdrop(), which actually added a memory barrier in the common case of switching between userspace processes. - Return -EINVAL when MEMBARRIER_CMD_SHARED is used on a nohz_full kernel, rather than having the whole membarrier system call returning -ENOSYS. Indeed, CMD_PRIVATE_EXPEDITED is compatible with nohz_full. Adapt the CMD_QUERY mask accordingly. Changes since v1: - move membarrier code under kernel/sched/ because it uses the scheduler runqueue, - only add the barrier when we switch from a kernel thread. The case where we switch from a user-space thread is already handled by the atomic_dec_and_test() in mmdrop(). - add a comment to mmdrop() documenting the requirement on the implicit memory barrier.

Mathieu Desnoyers committed 9 years ago

Commit 22e4ebb1 parent 955dbdf8 files · +202 −74Browse files

Showing the changes to include/uapi/linux (1 of 8 files). Show all

Unified · Split

include/uapi/linux/membarrier.h+21−2
@@ -40,14 +40,33 @@ · 39 unmodified lines
4040 * (non-running threads are de facto in such a
4141 * state). This covers threads from all processes
4242 * running on the system. This command returns 0.
43+ * @MEMBARRIER_CMD_PRIVATE_EXPEDITED:
44+ * Execute a memory barrier on each running
45+ * thread belonging to the same process as the current
46+ * thread. Upon return from system call, the
47+ * caller thread is ensured that all its running
48+ * threads siblings have passed through a state
49+ * where all memory accesses to user-space
50+ * addresses match program order between entry
51+ * to and return from the system call
52+ * (non-running threads are de facto in such a
53+ * state). This only covers threads from the
54+ * same processes as the caller thread. This
55+ * command returns 0. The "expedited" commands
56+ * complete faster than the non-expedited ones,
57+ * they never block, but have the downside of
58+ * causing extra overhead.
4359 *
4460 * Command to be passed to the membarrier system call. The commands need to
4561 * be a single bit each, except for MEMBARRIER_CMD_QUERY which is assigned to
4662 * the value 0.
4763 */
4864 enum membarrier_cmd {
49− MEMBARRIER_CMD_QUERY = 0,
50− MEMBARRIER_CMD_SHARED = (1 << 0),
65+ MEMBARRIER_CMD_QUERY = 0,
66+ MEMBARRIER_CMD_SHARED = (1 << 0),
67+ /* reserved for MEMBARRIER_CMD_SHARED_EXPEDITED (1 << 1) */
68+ /* reserved for MEMBARRIER_CMD_PRIVATE (1 << 2) */
69+ MEMBARRIER_CMD_PRIVATE_EXPEDITED = (1 << 3),
5170 };
5271
5372 #endif /* _UAPI_LINUX_MEMBARRIER_H */