forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
76 lines
2.4 KiB
76 lines
2.4 KiB
/* SPDX-License-Identifier: GPL-2.0-only */ |
|
/* |
|
* Based on arch/arm/include/asm/barrier.h |
|
* |
|
* Copyright (C) 2012 ARM Ltd. |
|
* Copyright (C) 2013 Regents of the University of California |
|
* Copyright (C) 2017 SiFive |
|
*/ |
|
|
|
#ifndef _ASM_RISCV_BARRIER_H |
|
#define _ASM_RISCV_BARRIER_H |
|
|
|
#ifndef __ASSEMBLY__ |
|
|
|
#define nop() __asm__ __volatile__ ("nop") |
|
|
|
#define RISCV_FENCE(p, s) \ |
|
__asm__ __volatile__ ("fence " #p "," #s : : : "memory") |
|
|
|
/* These barriers need to enforce ordering on both devices or memory. */ |
|
#define mb() RISCV_FENCE(iorw,iorw) |
|
#define rmb() RISCV_FENCE(ir,ir) |
|
#define wmb() RISCV_FENCE(ow,ow) |
|
|
|
/* These barriers do not need to enforce ordering on devices, just memory. */ |
|
#define __smp_mb() RISCV_FENCE(rw,rw) |
|
#define __smp_rmb() RISCV_FENCE(r,r) |
|
#define __smp_wmb() RISCV_FENCE(w,w) |
|
|
|
#define __smp_store_release(p, v) \ |
|
do { \ |
|
compiletime_assert_atomic_type(*p); \ |
|
RISCV_FENCE(rw,w); \ |
|
WRITE_ONCE(*p, v); \ |
|
} while (0) |
|
|
|
#define __smp_load_acquire(p) \ |
|
({ \ |
|
typeof(*p) ___p1 = READ_ONCE(*p); \ |
|
compiletime_assert_atomic_type(*p); \ |
|
RISCV_FENCE(r,rw); \ |
|
___p1; \ |
|
}) |
|
|
|
/* |
|
* This is a very specific barrier: it's currently only used in two places in |
|
* the kernel, both in the scheduler. See include/linux/spinlock.h for the two |
|
* orderings it guarantees, but the "critical section is RCsc" guarantee |
|
* mandates a barrier on RISC-V. The sequence looks like: |
|
* |
|
* lr.aq lock |
|
* sc lock <= LOCKED |
|
* smp_mb__after_spinlock() |
|
* // critical section |
|
* lr lock |
|
* sc.rl lock <= UNLOCKED |
|
* |
|
* The AQ/RL pair provides a RCpc critical section, but there's not really any |
|
* way we can take advantage of that here because the ordering is only enforced |
|
* on that one lock. Thus, we're just doing a full fence. |
|
* |
|
* Since we allow writeX to be called from preemptive regions we need at least |
|
* an "o" in the predecessor set to ensure device writes are visible before the |
|
* task is marked as available for scheduling on a new hart. While I don't see |
|
* any concrete reason we need a full IO fence, it seems safer to just upgrade |
|
* this in order to avoid any IO crossing a scheduling boundary. In both |
|
* instances the scheduler pairs this with an mb(), so nothing is necessary on |
|
* the new hart. |
|
*/ |
|
#define smp_mb__after_spinlock() RISCV_FENCE(iorw,iorw) |
|
|
|
#include <asm-generic/barrier.h> |
|
|
|
#endif /* __ASSEMBLY__ */ |
|
|
|
#endif /* _ASM_RISCV_BARRIER_H */
|
|
|