Skip to content

Commit c72ed3b

Browse files
committed
ANDROID: rust: add READ_ONCE and WRITE_ONCE
Provide Rust versions of READ_ONCE and WRITE_ONCE that have the same semantics as the equivalent C functions. The arm64+LTO implementation of READ_ONCE needs some more work to use the same inline assembly as the C code. Signed-off-by: Alice Ryhl <aliceryhl@google.com>
1 parent 1613e60 commit c72ed3b

File tree

4 files changed

+89
-0
lines changed

4 files changed

+89
-0
lines changed

rust/bindings/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,6 @@ mod bindings_helper {
4848
}
4949

5050
pub use bindings_raw::*;
51+
52+
mod rwonce;
53+
pub use rwonce::{READ_ONCE, WRITE_ONCE};

rust/bindings/rwonce/arm64.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
// Copyright (C) 2024 Google LLC.
4+
5+
#[cfg(not(CONFIG_LTO))]
6+
pub(super) use super::generic::__READ_ONCE;
7+
8+
// When building with LTO, there is an increased risk of the compiler
9+
// converting an address dependency headed by a READ_ONCE() invocation
10+
// into a control dependency and consequently allowing for harmful
11+
// reordering by the CPU.
12+
//
13+
// Ensure that such transformations are harmless by overriding the generic
14+
// READ_ONCE() definition with one that provides RCpc acquire semantics
15+
// when building with LTO.
16+
#[cfg(CONFIG_LTO)]
17+
#[inline(always)]
18+
pub(super) unsafe fn __READ_ONCE<T>(ptr: *const T) -> T {
19+
use core::sync::atomic::*;
20+
21+
macro_rules! load {
22+
($ptr:expr, $atomic:ident) => {{
23+
let ptr = $ptr as *const $atomic;
24+
// TODO: Use inline assembly instead.
25+
let value = (&*ptr).load(Ordering::Acquire);
26+
core::mem::transmute_copy::<_, T>(&value)
27+
}};
28+
}
29+
30+
unsafe {
31+
match core::mem::size_of::<T>() {
32+
1 => load!(ptr, AtomicU8),
33+
2 => load!(ptr, AtomicU16),
34+
4 => load!(ptr, AtomicU32),
35+
8 => load!(ptr, AtomicU64),
36+
_ => core::ptr::read_volatile(ptr)
37+
}
38+
}
39+
}

rust/bindings/rwonce/generic.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
// Copyright (C) 2024 Google LLC.
4+
5+
#[inline(always)]
6+
pub(super) unsafe fn __READ_ONCE<T>(ptr: *const T) -> T {
7+
unsafe { core::ptr::read_volatile(ptr) }
8+
}
9+
10+
#[inline(always)]
11+
pub(super) unsafe fn __WRITE_ONCE<T>(ptr: *mut T, val: T) {
12+
unsafe { core::ptr::write_volatile(ptr, val) }
13+
}

rust/bindings/rwonce/mod.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
// Copyright (C) 2024 Google LLC.
4+
5+
/// Generic implementations.
6+
///
7+
/// Use statements that mention the item by name take precedence over glob imports.
8+
mod generic;
9+
use self::generic::*;
10+
11+
#[cfg(target_arch = "aarch64")]
12+
mod arm64;
13+
#[cfg(target_arch = "aarch64")]
14+
use self::arm64::__READ_ONCE;
15+
16+
/// Read a value once.
17+
///
18+
/// # Safety
19+
///
20+
/// Same requirements as `READ_ONCE` from C.
21+
#[inline(always)]
22+
pub unsafe fn READ_ONCE<T: Copy>(ptr: *const T) -> T {
23+
unsafe { __READ_ONCE(ptr) }
24+
}
25+
26+
/// Write a value once.
27+
///
28+
/// # Safety
29+
///
30+
/// Same requirements as `WRITE_ONCE` from C.
31+
#[inline(always)]
32+
pub unsafe fn WRITE_ONCE<T: Copy>(ptr: *mut T, val: T) {
33+
unsafe { __WRITE_ONCE(ptr, val) }
34+
}

0 commit comments

Comments
 (0)