Flush icache on android aarch64 too (#5331)

This commit is contained in:
Benjamin Bouvier
2022-11-30 16:15:34 +01:00
committed by GitHub
parent e7cb82af89
commit 2bb1fb08fa
2 changed files with 90 additions and 70 deletions

View File

@@ -19,5 +19,5 @@ features = [
"Win32_System_Diagnostics_Debug", "Win32_System_Diagnostics_Debug",
] ]
[target.'cfg(any(target_os = "linux", target_os = "macos", target_os = "freebsd"))'.dependencies.libc] [target.'cfg(any(target_os = "linux", target_os = "macos", target_os = "freebsd", target_os = "android"))'.dependencies.libc]
version = "0.2.42" version = "0.2.42"

View File

@@ -1,28 +1,35 @@
#![allow(unused)]
use libc::{syscall, EINVAL, EPERM};
use std::ffi::c_void; use std::ffi::c_void;
use std::io::{Error, Result}; use std::io::Result;
const MEMBARRIER_CMD_GLOBAL: libc::c_int = 1; #[cfg(all(
const MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE: libc::c_int = 32; target_arch = "aarch64",
const MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE: libc::c_int = 64; any(target_os = "linux", target_os = "android")
))]
mod details {
use super::*;
use libc::{syscall, EINVAL, EPERM};
use std::io::Error;
/// See docs on [crate::pipeline_flush_mt] for a description of what this function is trying to do. const MEMBARRIER_CMD_GLOBAL: libc::c_int = 1;
#[inline] const MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE: libc::c_int = 32;
pub(crate) fn pipeline_flush_mt() -> Result<()> { const MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE: libc::c_int = 64;
/// See docs on [crate::pipeline_flush_mt] for a description of what this function is trying to do.
#[inline]
pub(crate) fn pipeline_flush_mt() -> Result<()> {
// Ensure that no processor has fetched a stale instruction stream. // Ensure that no processor has fetched a stale instruction stream.
// //
// On AArch64 we try to do this by executing a "broadcast" `ISB` which is not something that the // On AArch64 we try to do this by executing a "broadcast" `ISB` which is not something
// architecture provides us but we can emulate it using the membarrier kernel interface. // that the architecture provides us but we can emulate it using the membarrier kernel
// interface.
// //
// This behaviour was documented in a patch, however it seems that it hasn't been upstreamed yet // This behaviour was documented in a patch, however it seems that it hasn't been
// Nevertheless it clearly explains the guarantees that the Linux kernel provides us regarding the // upstreamed yet Nevertheless it clearly explains the guarantees that the Linux kernel
// membarrier interface, and how to use it for JIT contexts. // provides us regarding the membarrier interface, and how to use it for JIT contexts.
// https://lkml.kernel.org/lkml/07a8b963002cb955b7516e61bad19514a3acaa82.1623813516.git.luto@kernel.org/ // https://lkml.kernel.org/lkml/07a8b963002cb955b7516e61bad19514a3acaa82.1623813516.git.luto@kernel.org/
// //
// I couldn't find the follow up for that patch but there doesn't seem to be disagreement about // I couldn't find the follow up for that patch but there doesn't seem to be disagreement
// that specific part in the replies. // about that specific part in the replies.
// TODO: Check if the kernel has updated the membarrier documentation // TODO: Check if the kernel has updated the membarrier documentation
// //
// See the following issues for more info: // See the following issues for more info:
@@ -30,11 +37,10 @@ pub(crate) fn pipeline_flush_mt() -> Result<()> {
// * https://github.com/bytecodealliance/wasmtime/pull/4997 // * https://github.com/bytecodealliance/wasmtime/pull/4997
// //
// TODO: x86 and s390x have coherent caches so they don't need this, but RISCV does not // TODO: x86 and s390x have coherent caches so they don't need this, but RISCV does not
// guarantee that, so we may need to do something similar for it. However as noted in the above // guarantee that, so we may need to do something similar for it. However as noted in the
// kernel patch the SYNC_CORE membarrier has different guarantees on each architecture // above kernel patch the SYNC_CORE membarrier has different guarantees on each
// so we need follow up and check what it provides us. // architecture so we need follow up and check what it provides us.
// See: https://github.com/bytecodealliance/wasmtime/issues/5033 // See: https://github.com/bytecodealliance/wasmtime/issues/5033
#[cfg(all(target_arch = "aarch64", target_os = "linux"))]
match membarrier(MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE) { match membarrier(MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE) {
Ok(_) => {} Ok(_) => {}
@@ -43,7 +49,8 @@ pub(crate) fn pipeline_flush_mt() -> Result<()> {
// //
// This does have some overhead since on the first time we call this function we // This does have some overhead since on the first time we call this function we
// actually execute three membarriers, but this only happens once per process and only // actually execute three membarriers, but this only happens once per process and only
// one slow membarrier is actually executed (The last one, which actually generates an IPI). // one slow membarrier is actually executed (The last one, which actually generates an
// IPI).
Err(e) if e.raw_os_error().unwrap() == EPERM => { Err(e) if e.raw_os_error().unwrap() == EPERM => {
membarrier(MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE)?; membarrier(MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE)?;
membarrier(MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE)?; membarrier(MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE)?;
@@ -62,10 +69,9 @@ pub(crate) fn pipeline_flush_mt() -> Result<()> {
} }
Ok(()) Ok(())
} }
#[cfg(target_os = "linux")] fn membarrier(barrier: libc::c_int) -> Result<()> {
fn membarrier(barrier: libc::c_int) -> Result<()> {
let flags: libc::c_int = 0; let flags: libc::c_int = 0;
let res = unsafe { syscall(libc::SYS_membarrier, barrier, flags) }; let res = unsafe { syscall(libc::SYS_membarrier, barrier, flags) };
if res == 0 { if res == 0 {
@@ -73,14 +79,28 @@ fn membarrier(barrier: libc::c_int) -> Result<()> {
} else { } else {
Err(Error::last_os_error()) Err(Error::last_os_error())
} }
}
} }
#[cfg(not(all(
target_arch = "aarch64",
any(target_os = "linux", target_os = "android")
)))]
mod details {
pub(crate) fn pipeline_flush_mt() -> std::io::Result<()> {
Ok(())
}
}
pub(crate) use details::*;
/// See docs on [crate::clear_cache] for a description of what this function is trying to do. /// See docs on [crate::clear_cache] for a description of what this function is trying to do.
#[inline] #[inline]
pub(crate) fn clear_cache(_ptr: *const c_void, _len: usize) -> Result<()> { pub(crate) fn clear_cache(_ptr: *const c_void, _len: usize) -> Result<()> {
// TODO: On AArch64 we currently rely on the `mprotect` call that switches the memory from W+R to R+X // TODO: On AArch64 we currently rely on the `mprotect` call that switches the memory from W+R
// to do this for us, however that is an implementation detail and should not be relied upon // to R+X to do this for us, however that is an implementation detail and should not be relied
// We should call some implementation of `clear_cache` here // upon.
// We should call some implementation of `clear_cache` here.
// //
// See: https://github.com/bytecodealliance/wasmtime/issues/3310 // See: https://github.com/bytecodealliance/wasmtime/issues/3310