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.
Showing the changes to include/uapi/linux (1 of 8 files). Show all
Unified · Split
| @@ -40,14 +40,33 @@ · 39 unmodified lines | ||
| 40 | 40 | * (non-running threads are de facto in such a |
| 41 | 41 | * state). This covers threads from all processes |
| 42 | 42 | * 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. | |
| 43 | 59 | * |
| 44 | 60 | * Command to be passed to the membarrier system call. The commands need to |
| 45 | 61 | * be a single bit each, except for MEMBARRIER_CMD_QUERY which is assigned to |
| 46 | 62 | * the value 0. |
| 47 | 63 | */ |
| 48 | 64 | 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), | |
| 51 | 70 | }; |
| 52 | 71 | |
| 53 | 72 | #endif /* _UAPI_LINUX_MEMBARRIER_H */ |