use core::{
cell::UnsafeCell,
fmt::{Debug, Display},
};
mod tp;
#[cfg(feature = "preempt")]
use kernel_guard::NoPreempt;
pub use tp::*;
#[repr(transparent)]
pub struct PerCpuData<T> {
data: UnsafeCell<T>,
}
unsafe impl<T> Sync for PerCpuData<T> {}
unsafe impl<T> Send for PerCpuData<T> {}
fn with_preempt<F, R>(f: F) -> R
where
F: FnOnce() -> R,
{
#[cfg(feature = "preempt")]
let g = NoPreempt::new();
let res = f();
#[cfg(feature = "preempt")]
drop(g);
res
}
impl<T> PerCpuData<T> {
pub const fn new(data: T) -> PerCpuData<T> {
PerCpuData {
data: UnsafeCell::new(data),
}
}
#[inline]
pub fn offset(&self) -> usize {
self.data.get() as usize - percpu_link_start()
}
#[inline]
pub fn remote_ptr(&self, cpu_idx: usize) -> *mut T {
let addr = unsafe { _percpu_base_ptr(cpu_idx) } as usize + self.offset();
addr as *mut T
}
#[inline]
pub unsafe fn current_ptr(&self) -> *mut T {
let addr = read_percpu_reg() + self.offset();
addr as *mut T
}
#[inline]
pub unsafe fn current_ref_raw(&self) -> &T {
&*self.current_ptr()
}
#[inline]
#[allow(clippy::mut_from_ref)]
pub unsafe fn current_ref_mut_raw(&self) -> &mut T {
unsafe { &mut *self.current_ptr() }
}
pub fn write_current(&self, val: T) {
with_preempt(|| unsafe { self.write_current_raw(val) });
}
pub unsafe fn write_current_raw(&self, val: T) {
unsafe {
*self.current_ptr() = val;
}
}
pub unsafe fn write_remote(&self, cpu_idx: usize, val: T) {
with_preempt(|| unsafe {
*self.remote_ptr(cpu_idx) = val;
})
}
pub fn with_current<F, R>(&self, f: F) -> R
where
F: FnOnce(&mut T) -> R,
{
with_preempt(|| unsafe { f(&mut *self.current_ptr()) })
}
#[inline]
pub unsafe fn remote_ref_raw(&self, cpu_id: usize) -> &T {
&*self.remote_ptr(cpu_id)
}
#[inline]
#[allow(clippy::mut_from_ref)]
pub unsafe fn remote_ref_mut_raw(&self, cpu_id: usize) -> &mut T {
&mut *self.remote_ptr(cpu_id)
}
}
impl<T: Clone> PerCpuData<T> {
pub fn read_current(&self) -> T {
with_preempt(|| unsafe { self.read_current_raw() })
}
pub unsafe fn read_current_raw(&self) -> T {
unsafe { (*self.current_ptr()).clone() }
}
pub fn read_remote(&self, cpu_idx: usize) -> T {
with_preempt(|| unsafe { (*self.remote_ptr(cpu_idx)).clone() })
}
}
impl<T: Debug> Debug for PerCpuData<T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
with_preempt(|| unsafe { &*self.data.get() }.fmt(f))
}
}
impl<T: Display> Display for PerCpuData<T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
with_preempt(|| unsafe { &*self.data.get() }.fmt(f))
}
}
unsafe extern "C" {
fn _percpu_load_start();
fn _percpu_base_ptr(idx: usize) -> *mut u8;
}
#[inline]
fn percpu_link_start() -> usize {
_percpu_load_start as *const () as usize
}
pub fn init() -> usize {
#[cfg(target_os = "linux")]
{
_linux::init(4)
}
#[cfg(not(target_os = "linux"))]
{
0
}
}
pub fn init_percpu_reg(cpu_idx: usize) {
unsafe {
let ptr = _percpu_base_ptr(cpu_idx);
write_percpu_reg(ptr as usize);
}
}
pub fn percpu_area_base(cpu_idx: usize) -> usize {
unsafe { _percpu_base_ptr(cpu_idx) as usize }
}
pub fn percpu_area_size() -> usize {
unsafe extern "C" {
fn _percpu_load_end();
}
_percpu_load_end as *const () as usize - percpu_link_start()
}
#[cfg(target_os = "linux")]
mod _linux {
use std::sync::Mutex;
use super::*;
static PERCPU_DATA: Mutex<Vec<u8>> = Mutex::new(Vec::new());
static mut PERCPU_BASE: usize = 0;
fn percpu_section_size() -> usize {
unsafe extern "C" {
fn _percpu_load_end();
}
_percpu_load_end as *const () as usize - percpu_link_start()
}
pub fn init(cpu_count: usize) -> usize {
let size = cpu_count * percpu_section_size();
let mut g = PERCPU_DATA.lock().unwrap();
g.resize(size, 0);
unsafe {
let base = g.as_slice().as_ptr() as usize;
PERCPU_BASE = base;
println!("alloc percpu data @{:#x}, size: {:#x}", base, size);
}
cpu_count
}
#[unsafe(no_mangle)]
pub unsafe fn _percpu_base_ptr(idx: usize) -> *mut u8 {
unsafe { (PERCPU_BASE + idx * percpu_section_size()) as *mut u8 }
}
}