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 | * (non-running threads are de facto in such a | 40 | * (non-running threads are de facto in such a |
| 41 | * state). This covers threads from all processes | 41 | * state). This covers threads from all processes |
| 42 | * running on the system. This command returns 0. | 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 | * Command to be passed to the membarrier system call. The commands need to | 60 | * Command to be passed to the membarrier system call. The commands need to |
| 45 | * be a single bit each, except for MEMBARRIER_CMD_QUERY which is assigned to | 61 | * be a single bit each, except for MEMBARRIER_CMD_QUERY which is assigned to |
| 46 | * the value 0. | 62 | * the value 0. |
| 47 | */ | 63 | */ |
| 48 | enum membarrier_cmd { | 64 | enum membarrier_cmd { |
| 49 | MEMBARRIER_CMD_QUERY = 0, | 65 | MEMBARRIER_CMD_QUERY = 0, |
| 50 | MEMBARRIER_CMD_SHARED = (1 << 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 | #endif /* _UAPI_LINUX_MEMBARRIER_H */ | 72 | #endif /* _UAPI_LINUX_MEMBARRIER_H */ |