cd9b29014d
RISC-V uses xRET instructions on return from interrupt and to go back to user-space; the xRET instruction is not core serializing. Use FENCE.I for providing core serialization as follows: - by calling sync_core_before_usermode() on return from interrupt (cf. ipi_sync_core()), - via switch_mm() and sync_core_before_usermode() (respectively, for uthread->uthread and kthread->uthread transitions) before returning to user-space. On RISC-V, the serialization in switch_mm() is activated by resetting the icache_stale_mask of the mm at prepare_sync_core_cmd(). Suggested-by: Palmer Dabbelt <palmer@dabbelt.com> Signed-off-by: Andrea Parri <parri.andrea@gmail.com> Reviewed-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com> Link: https://lore.kernel.org/r/20240131144936.29190-5-parri.andrea@gmail.com Signed-off-by: Palmer Dabbelt <palmer@rivosinc.com>
51 lines
1.7 KiB
C
51 lines
1.7 KiB
C
/* SPDX-License-Identifier: GPL-2.0-only */
|
|
#ifndef _ASM_RISCV_MEMBARRIER_H
|
|
#define _ASM_RISCV_MEMBARRIER_H
|
|
|
|
static inline void membarrier_arch_switch_mm(struct mm_struct *prev,
|
|
struct mm_struct *next,
|
|
struct task_struct *tsk)
|
|
{
|
|
/*
|
|
* Only need the full barrier when switching between processes.
|
|
* Barrier when switching from kernel to userspace is not
|
|
* required here, given that it is implied by mmdrop(). Barrier
|
|
* when switching from userspace to kernel is not needed after
|
|
* store to rq->curr.
|
|
*/
|
|
if (IS_ENABLED(CONFIG_SMP) &&
|
|
likely(!(atomic_read(&next->membarrier_state) &
|
|
(MEMBARRIER_STATE_PRIVATE_EXPEDITED |
|
|
MEMBARRIER_STATE_GLOBAL_EXPEDITED)) || !prev))
|
|
return;
|
|
|
|
/*
|
|
* The membarrier system call requires a full memory barrier
|
|
* after storing to rq->curr, before going back to user-space.
|
|
*
|
|
* This barrier is also needed for the SYNC_CORE command when
|
|
* switching between processes; in particular, on a transition
|
|
* from a thread belonging to another mm to a thread belonging
|
|
* to the mm for which a membarrier SYNC_CORE is done on CPU0:
|
|
*
|
|
* - [CPU0] sets all bits in the mm icache_stale_mask (in
|
|
* prepare_sync_core_cmd());
|
|
*
|
|
* - [CPU1] stores to rq->curr (by the scheduler);
|
|
*
|
|
* - [CPU0] loads rq->curr within membarrier and observes
|
|
* cpu_rq(1)->curr->mm != mm, so the IPI is skipped on
|
|
* CPU1; this means membarrier relies on switch_mm() to
|
|
* issue the sync-core;
|
|
*
|
|
* - [CPU1] switch_mm() loads icache_stale_mask; if the bit
|
|
* is zero, switch_mm() may incorrectly skip the sync-core.
|
|
*
|
|
* Matches a full barrier in the proximity of the membarrier
|
|
* system call entry.
|
|
*/
|
|
smp_mb();
|
|
}
|
|
|
|
#endif /* _ASM_RISCV_MEMBARRIER_H */
|