Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/crates/jit-icache-coherence/src/libc.rs
1691 views
1
use std::ffi::c_void;
2
3
#[cfg(any(target_os = "linux", target_os = "android"))]
4
pub use std::io::Result;
5
6
#[cfg(not(any(target_os = "linux", target_os = "android")))]
7
pub use anyhow::Result;
8
9
#[cfg(all(
10
target_arch = "aarch64",
11
any(target_os = "linux", target_os = "android")
12
))]
13
mod details {
14
15
use super::*;
16
use libc::{EINVAL, EPERM, syscall};
17
use std::io::Error;
18
19
const MEMBARRIER_CMD_GLOBAL: libc::c_int = 1;
20
const MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE: libc::c_int = 32;
21
const MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE: libc::c_int = 64;
22
23
/// See docs on [crate::pipeline_flush_mt] for a description of what this function is trying to do.
24
#[inline]
25
pub(crate) fn pipeline_flush_mt() -> Result<()> {
26
// Ensure that no processor has fetched a stale instruction stream.
27
//
28
// On AArch64 we try to do this by executing a "broadcast" `ISB` which is not something
29
// that the architecture provides us but we can emulate it using the membarrier kernel
30
// interface.
31
//
32
// This behaviour was documented in a patch, however it seems that it hasn't been
33
// upstreamed yet Nevertheless it clearly explains the guarantees that the Linux kernel
34
// provides us regarding the membarrier interface, and how to use it for JIT contexts.
35
// https://lkml.kernel.org/lkml/07a8b963002cb955b7516e61bad19514a3acaa82.1623813516.git.luto@kernel.org/
36
//
37
// I couldn't find the follow up for that patch but there doesn't seem to be disagreement
38
// about that specific part in the replies.
39
// TODO: Check if the kernel has updated the membarrier documentation
40
//
41
// See the following issues for more info:
42
// * https://github.com/bytecodealliance/wasmtime/pull/3426
43
// * https://github.com/bytecodealliance/wasmtime/pull/4997
44
//
45
// TODO: x86 and s390x have coherent caches so they don't need this, but RISCV does not
46
// guarantee that, so we may need to do something similar for it. However as noted in the
47
// above kernel patch the SYNC_CORE membarrier has different guarantees on each
48
// architecture so we need follow up and check what it provides us.
49
// See: https://github.com/bytecodealliance/wasmtime/issues/5033
50
match membarrier(MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE) {
51
Ok(_) => {}
52
53
// EPERM happens if the calling process hasn't yet called the register membarrier.
54
// We can call the register membarrier now, and then retry the actual membarrier,
55
//
56
// This does have some overhead since on the first time we call this function we
57
// actually execute three membarriers, but this only happens once per process and only
58
// one slow membarrier is actually executed (The last one, which actually generates an
59
// IPI).
60
Err(e) if e.raw_os_error().unwrap() == EPERM => {
61
membarrier(MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE)?;
62
membarrier(MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE)?;
63
}
64
65
// On kernels older than 4.16 the above syscall does not exist, so we can
66
// fallback to MEMBARRIER_CMD_GLOBAL which is an alias for MEMBARRIER_CMD_SHARED
67
// that has existed since 4.3. GLOBAL is a lot slower, but allows us to have
68
// compatibility with older kernels.
69
Err(e) if e.raw_os_error().unwrap() == EINVAL => {
70
membarrier(MEMBARRIER_CMD_GLOBAL)?;
71
}
72
73
// In any other case we got an actual error, so lets propagate that up
74
e => e?,
75
}
76
77
Ok(())
78
}
79
80
fn membarrier(barrier: libc::c_int) -> Result<()> {
81
let flags: libc::c_int = 0;
82
let res = unsafe { syscall(libc::SYS_membarrier, barrier, flags) };
83
if res == 0 {
84
Ok(())
85
} else {
86
Err(Error::last_os_error())
87
}
88
}
89
}
90
91
#[cfg(not(all(
92
target_arch = "aarch64",
93
any(target_os = "linux", target_os = "android")
94
)))]
95
mod details {
96
// NB: this uses `anyhow::Result` instead of `std::io::Result` to compile on
97
// `no_std`.
98
pub(crate) fn pipeline_flush_mt() -> super::Result<()> {
99
Ok(())
100
}
101
}
102
103
#[cfg(all(target_arch = "riscv64", target_os = "linux"))]
104
fn riscv_flush_icache(start: u64, end: u64) -> Result<()> {
105
cfg_if::cfg_if! {
106
if #[cfg(feature = "one-core")] {
107
use std::arch::asm;
108
let _ = (start, end);
109
unsafe {
110
asm!("fence.i");
111
};
112
Ok(())
113
} else {
114
#[expect(non_upper_case_globals, reason = "matching C style")]
115
match unsafe {
116
libc::syscall(
117
{
118
// The syscall isn't defined in `libc`, so we define the syscall number here.
119
// https://github.com/torvalds/linux/search?q=__NR_arch_specific_syscall
120
const __NR_arch_specific_syscall :i64 = 244;
121
// https://github.com/torvalds/linux/blob/5bfc75d92efd494db37f5c4c173d3639d4772966/tools/arch/riscv/include/uapi/asm/unistd.h#L40
122
const sys_riscv_flush_icache :i64 = __NR_arch_specific_syscall + 15;
123
sys_riscv_flush_icache
124
},
125
// Currently these parameters are not used, but they are still defined.
126
start, // start
127
end, // end
128
{
129
const SYS_RISCV_FLUSH_ICACHE_LOCAL :i64 = 1;
130
const SYS_RISCV_FLUSH_ICACHE_ALL :i64 = SYS_RISCV_FLUSH_ICACHE_LOCAL;
131
SYS_RISCV_FLUSH_ICACHE_ALL
132
}, // flags
133
)
134
} {
135
0 => { Ok(()) }
136
_ => Err(std::io::Error::last_os_error()),
137
}
138
}
139
}
140
}
141
142
pub(crate) use details::*;
143
144
/// See docs on [crate::clear_cache] for a description of what this function is trying to do.
145
#[inline]
146
pub(crate) fn clear_cache(_ptr: *const c_void, _len: usize) -> Result<()> {
147
// TODO: On AArch64 we currently rely on the `mprotect` call that switches the memory from W+R
148
// to R+X to do this for us, however that is an implementation detail and should not be relied
149
// upon.
150
// We should call some implementation of `clear_cache` here.
151
//
152
// See: https://github.com/bytecodealliance/wasmtime/issues/3310
153
#[cfg(all(target_arch = "riscv64", target_os = "linux"))]
154
riscv_flush_icache(_ptr as u64, (_ptr as u64) + (_len as u64))?;
155
Ok(())
156
}
157
158