clientmem: move shm mapping into workspace crate
This commit is contained in:
parent
bf3859a026
commit
663cfb3ca3
10 changed files with 383 additions and 338 deletions
14
Cargo.lock
generated
14
Cargo.lock
generated
|
|
@ -665,6 +665,19 @@ dependencies = [
|
||||||
"ahash",
|
"ahash",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jay-clientmem"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"jay-cpu-worker",
|
||||||
|
"jay-gfx-types",
|
||||||
|
"jay-tracy",
|
||||||
|
"jay-utils",
|
||||||
|
"log",
|
||||||
|
"thiserror",
|
||||||
|
"uapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jay-cmm"
|
name = "jay-cmm"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
@ -701,6 +714,7 @@ dependencies = [
|
||||||
"jay-async-engine",
|
"jay-async-engine",
|
||||||
"jay-bufio",
|
"jay-bufio",
|
||||||
"jay-bugs",
|
"jay-bugs",
|
||||||
|
"jay-clientmem",
|
||||||
"jay-cmm",
|
"jay-cmm",
|
||||||
"jay-config",
|
"jay-config",
|
||||||
"jay-cpu-worker",
|
"jay-cpu-worker",
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,7 @@ members = [
|
||||||
"video-types",
|
"video-types",
|
||||||
"gfx-types",
|
"gfx-types",
|
||||||
"theme",
|
"theme",
|
||||||
|
"clientmem",
|
||||||
"pango",
|
"pango",
|
||||||
"libinput",
|
"libinput",
|
||||||
"toml-config",
|
"toml-config",
|
||||||
|
|
@ -93,6 +94,7 @@ jay-logger = { version = "0.1.0", path = "logger" }
|
||||||
jay-video-types = { version = "0.1.0", path = "video-types" }
|
jay-video-types = { version = "0.1.0", path = "video-types" }
|
||||||
jay-gfx-types = { version = "0.1.0", path = "gfx-types" }
|
jay-gfx-types = { version = "0.1.0", path = "gfx-types" }
|
||||||
jay-theme = { version = "0.1.0", path = "theme" }
|
jay-theme = { version = "0.1.0", path = "theme" }
|
||||||
|
jay-clientmem = { version = "0.1.0", path = "clientmem" }
|
||||||
jay-pango = { version = "0.1.0", path = "pango" }
|
jay-pango = { version = "0.1.0", path = "pango" }
|
||||||
jay-libinput = { version = "0.1.0", path = "libinput" }
|
jay-libinput = { version = "0.1.0", path = "libinput" }
|
||||||
|
|
||||||
|
|
@ -158,4 +160,4 @@ opt-level = 3
|
||||||
[features]
|
[features]
|
||||||
rc_tracking = []
|
rc_tracking = []
|
||||||
it = ["jay-async-engine/it", "jay-cpu-worker/it"]
|
it = ["jay-async-engine/it", "jay-cpu-worker/it"]
|
||||||
tracy = ["jay-tracy/tracy", "jay-async-engine/tracy", "jay-cpu-worker/tracy"]
|
tracy = ["jay-tracy/tracy", "jay-async-engine/tracy", "jay-cpu-worker/tracy", "jay-clientmem/tracy"]
|
||||||
|
|
|
||||||
18
clientmem/Cargo.toml
Normal file
18
clientmem/Cargo.toml
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
[package]
|
||||||
|
name = "jay-clientmem"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
license = "GPL-3.0-only"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
jay-cpu-worker = { version = "0.1.0", path = "../cpu-worker" }
|
||||||
|
jay-gfx-types = { version = "0.1.0", path = "../gfx-types" }
|
||||||
|
jay-tracy = { version = "0.1.0", path = "../tracy" }
|
||||||
|
jay-utils = { version = "0.1.0", path = "../utils" }
|
||||||
|
|
||||||
|
log = "0.4.20"
|
||||||
|
thiserror = "2.0.11"
|
||||||
|
uapi = "0.2.13"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
tracy = ["jay-tracy/tracy"]
|
||||||
331
clientmem/src/lib.rs
Normal file
331
clientmem/src/lib.rs
Normal file
|
|
@ -0,0 +1,331 @@
|
||||||
|
use {
|
||||||
|
jay_cpu_worker::{AsyncCpuWork, CpuJob, CpuWork, CpuWorker},
|
||||||
|
jay_gfx_types::{ShmMemory, ShmMemoryBacking},
|
||||||
|
jay_utils::{
|
||||||
|
oserror::{OsError, OsErrorExt2},
|
||||||
|
page_size::page_size,
|
||||||
|
vec_ext::VecExt,
|
||||||
|
},
|
||||||
|
std::{
|
||||||
|
cell::Cell,
|
||||||
|
error::Error,
|
||||||
|
mem::{ManuallyDrop, MaybeUninit},
|
||||||
|
ops::Deref,
|
||||||
|
ptr,
|
||||||
|
rc::Rc,
|
||||||
|
sync::atomic::{Ordering, compiler_fence},
|
||||||
|
},
|
||||||
|
thiserror::Error,
|
||||||
|
uapi::{
|
||||||
|
OwnedFd, Pod,
|
||||||
|
c::{self, raise},
|
||||||
|
ftruncate,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub struct ClientMemClient<'a> {
|
||||||
|
pub comm: &'a str,
|
||||||
|
pub id: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum ClientMemError {
|
||||||
|
#[error("Could not install the sigbus handler")]
|
||||||
|
SigactionFailed(#[source] jay_utils::oserror::OsError),
|
||||||
|
#[error("A SIGBUS occurred while accessing mapped memory")]
|
||||||
|
Sigbus,
|
||||||
|
#[error("mmap failed")]
|
||||||
|
MmapFailed(#[source] jay_utils::oserror::OsError),
|
||||||
|
#[error("Length was not a multiple of the data element size")]
|
||||||
|
InvalidLength,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ClientMem {
|
||||||
|
fd: ManuallyDrop<Rc<OwnedFd>>,
|
||||||
|
failed: Cell<bool>,
|
||||||
|
sigbus_impossible: bool,
|
||||||
|
data: *const [Cell<u8>],
|
||||||
|
cpu: Option<Rc<CpuWorker>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct ClientMemOffset {
|
||||||
|
mem: Rc<ClientMem>,
|
||||||
|
offset: usize,
|
||||||
|
data: *const [Cell<u8>],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ClientMem {
|
||||||
|
pub fn new(
|
||||||
|
fd: &Rc<OwnedFd>,
|
||||||
|
len: usize,
|
||||||
|
read_only: bool,
|
||||||
|
client: Option<ClientMemClient<'_>>,
|
||||||
|
cpu: Option<&Rc<CpuWorker>>,
|
||||||
|
is_udmabuf: bool,
|
||||||
|
) -> Result<Self, ClientMemError> {
|
||||||
|
Self::new2(fd, len, read_only, client, cpu, c::MAP_SHARED, is_udmabuf)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_private(
|
||||||
|
fd: &Rc<OwnedFd>,
|
||||||
|
len: usize,
|
||||||
|
read_only: bool,
|
||||||
|
client: Option<ClientMemClient<'_>>,
|
||||||
|
cpu: Option<&Rc<CpuWorker>>,
|
||||||
|
) -> Result<Self, ClientMemError> {
|
||||||
|
Self::new2(fd, len, read_only, client, cpu, c::MAP_PRIVATE, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new2(
|
||||||
|
fd: &Rc<OwnedFd>,
|
||||||
|
len: usize,
|
||||||
|
read_only: bool,
|
||||||
|
client: Option<ClientMemClient<'_>>,
|
||||||
|
cpu: Option<&Rc<CpuWorker>>,
|
||||||
|
flags: c::c_int,
|
||||||
|
is_udmabuf: bool,
|
||||||
|
) -> Result<Self, ClientMemError> {
|
||||||
|
let mut sigbus_impossible = is_udmabuf;
|
||||||
|
let mut real_size = None;
|
||||||
|
if !sigbus_impossible
|
||||||
|
&& let Ok(seals) = uapi::fcntl_get_seals(fd.raw())
|
||||||
|
&& seals & c::F_SEAL_SHRINK != 0
|
||||||
|
&& let Ok(stat) = uapi::fstat(fd.raw())
|
||||||
|
{
|
||||||
|
real_size = Some(stat.st_size as usize);
|
||||||
|
sigbus_impossible = stat.st_size as u64 >= len as u64;
|
||||||
|
}
|
||||||
|
if !sigbus_impossible && let Some(client) = client {
|
||||||
|
log::debug!(
|
||||||
|
"Client {} ({}) has created a shm buffer that might cause SIGBUS",
|
||||||
|
client.comm,
|
||||||
|
client.id,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let len = len.next_multiple_of(page_size());
|
||||||
|
if let Some(real_size) = real_size
|
||||||
|
&& real_size < len
|
||||||
|
{
|
||||||
|
let _ = ftruncate(fd.raw(), len as _);
|
||||||
|
}
|
||||||
|
let data = if len == 0 {
|
||||||
|
&mut [][..]
|
||||||
|
} else {
|
||||||
|
let prot = match read_only {
|
||||||
|
true => c::PROT_READ,
|
||||||
|
false => c::PROT_READ | c::PROT_WRITE,
|
||||||
|
};
|
||||||
|
unsafe {
|
||||||
|
let data = c::mmap64(ptr::null_mut(), len, prot, flags, fd.raw(), 0);
|
||||||
|
if data == c::MAP_FAILED {
|
||||||
|
return Err(ClientMemError::MmapFailed(OsError::default()));
|
||||||
|
}
|
||||||
|
std::slice::from_raw_parts_mut(data as *mut Cell<u8>, len)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(Self {
|
||||||
|
fd: ManuallyDrop::new(fd.clone()),
|
||||||
|
failed: Cell::new(false),
|
||||||
|
sigbus_impossible,
|
||||||
|
data,
|
||||||
|
cpu: cpu.cloned(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.data.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn offset(self: &Rc<Self>, offset: usize, len: usize) -> ClientMemOffset {
|
||||||
|
let mem = unsafe { &*self.data };
|
||||||
|
ClientMemOffset {
|
||||||
|
mem: self.clone(),
|
||||||
|
offset,
|
||||||
|
data: &mem[offset..][..len],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fd(&self) -> &Rc<OwnedFd> {
|
||||||
|
&self.fd
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_sealed_memfd(&self) -> bool {
|
||||||
|
self.sigbus_impossible
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ClientMemOffset {
|
||||||
|
pub fn pool(&self) -> &ClientMem {
|
||||||
|
&self.mem
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn offset(&self) -> usize {
|
||||||
|
self.offset
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ptr(&self) -> *const [Cell<u8>] {
|
||||||
|
self.data
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn access<T, F: FnOnce(&[Cell<u8>]) -> T>(&self, f: F) -> Result<T, ClientMemError> {
|
||||||
|
unsafe {
|
||||||
|
if self.mem.sigbus_impossible {
|
||||||
|
return Ok(f(&*self.data));
|
||||||
|
}
|
||||||
|
let mref = MemRef {
|
||||||
|
mem: &*self.mem,
|
||||||
|
outer: MEM.get(),
|
||||||
|
};
|
||||||
|
MEM.set(&mref);
|
||||||
|
compiler_fence(Ordering::SeqCst);
|
||||||
|
let res = f(&*self.data);
|
||||||
|
MEM.set(mref.outer);
|
||||||
|
compiler_fence(Ordering::SeqCst);
|
||||||
|
match self.mem.failed.get() {
|
||||||
|
true => Err(ClientMemError::Sigbus),
|
||||||
|
_ => Ok(res),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read<T: Pod>(&self, dst: &mut Vec<T>) -> Result<(), ClientMemError> {
|
||||||
|
if self.data.len().checked_rem(std::mem::size_of::<T>()) != Some(0) {
|
||||||
|
return Err(ClientMemError::InvalidLength);
|
||||||
|
}
|
||||||
|
self.access(|v| {
|
||||||
|
let len_elements = v.len() / std::mem::size_of::<T>();
|
||||||
|
dst.reserve(len_elements);
|
||||||
|
let (_, unused) = dst.split_at_spare_mut_bytes_ext();
|
||||||
|
unused[..v.len()].copy_from_slice(uapi::as_maybe_uninit_bytes(v));
|
||||||
|
unsafe {
|
||||||
|
dst.set_len(dst.len() + len_elements);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for ClientMem {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let fd = unsafe { ManuallyDrop::take(&mut self.fd) };
|
||||||
|
if let Some(cpu) = &self.cpu {
|
||||||
|
let pending = cpu.submit(Box::new(CloseMemWork {
|
||||||
|
fd: Rc::try_unwrap(fd).ok(),
|
||||||
|
data: self.data,
|
||||||
|
}));
|
||||||
|
pending.detach();
|
||||||
|
} else {
|
||||||
|
unsafe {
|
||||||
|
c::munmap(self.data as _, self.len());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MemRef {
|
||||||
|
mem: *const ClientMem,
|
||||||
|
outer: *const MemRef,
|
||||||
|
}
|
||||||
|
|
||||||
|
thread_local! {
|
||||||
|
static MEM: Cell<*const MemRef> = const { Cell::new(ptr::null()) };
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn kill() -> ! {
|
||||||
|
unsafe {
|
||||||
|
c::signal(c::SIGBUS, c::SIG_DFL);
|
||||||
|
raise(c::SIGBUS);
|
||||||
|
}
|
||||||
|
unreachable!();
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "C" fn sigbus(sig: i32, info: &c::siginfo_t, _ucontext: *mut c::c_void) {
|
||||||
|
unsafe {
|
||||||
|
assert_eq!(sig, c::SIGBUS);
|
||||||
|
let mut memr_ptr = MEM.get();
|
||||||
|
while !memr_ptr.is_null() {
|
||||||
|
let memr = &*memr_ptr;
|
||||||
|
let mem = &*memr.mem;
|
||||||
|
let lo = mem.data as *mut u8 as usize;
|
||||||
|
let hi = lo + mem.len();
|
||||||
|
let fault_addr = info.si_addr() as usize;
|
||||||
|
if fault_addr < lo || fault_addr >= hi {
|
||||||
|
memr_ptr = memr.outer;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let res = c::mmap64(
|
||||||
|
lo as _,
|
||||||
|
hi - lo,
|
||||||
|
c::PROT_WRITE | c::PROT_READ,
|
||||||
|
c::MAP_ANONYMOUS | c::MAP_PRIVATE | c::MAP_FIXED,
|
||||||
|
-1,
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
if res == c::MAP_FAILED {
|
||||||
|
kill();
|
||||||
|
}
|
||||||
|
mem.failed.set(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
kill();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init() -> Result<(), ClientMemError> {
|
||||||
|
unsafe {
|
||||||
|
let mut action: c::sigaction = MaybeUninit::zeroed().assume_init();
|
||||||
|
action.sa_sigaction =
|
||||||
|
sigbus as unsafe extern "C" fn(i32, &c::siginfo_t, *mut c::c_void) as _;
|
||||||
|
action.sa_flags = c::SA_NODEFER | c::SA_SIGINFO;
|
||||||
|
let res = c::sigaction(c::SIGBUS, &action, ptr::null_mut());
|
||||||
|
uapi::map_err!(res)
|
||||||
|
.map(drop)
|
||||||
|
.map_os_err(ClientMemError::SigactionFailed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CloseMemWork {
|
||||||
|
fd: Option<OwnedFd>,
|
||||||
|
data: *const [Cell<u8>],
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Send for CloseMemWork {}
|
||||||
|
|
||||||
|
impl CpuJob for CloseMemWork {
|
||||||
|
fn work(&mut self) -> &mut dyn CpuWork {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn completed(self: Box<Self>) {
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CpuWork for CloseMemWork {
|
||||||
|
fn run(&mut self) -> Option<Box<dyn AsyncCpuWork>> {
|
||||||
|
jay_tracy::zone!("CloseMemWork");
|
||||||
|
self.fd.take();
|
||||||
|
unsafe {
|
||||||
|
c::munmap(self.data as _, self.data.len());
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ShmMemory for ClientMemOffset {
|
||||||
|
fn len(&self) -> usize {
|
||||||
|
self.data.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn safe_access(&self) -> ShmMemoryBacking {
|
||||||
|
match self.mem.is_sealed_memfd() {
|
||||||
|
true => ShmMemoryBacking::Ptr(self.data),
|
||||||
|
false => ShmMemoryBacking::Fd(self.mem.fd.deref().clone(), self.offset),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn access(&self, f: &mut dyn FnMut(&[Cell<u8>])) -> Result<(), Box<dyn Error + Sync + Send>> {
|
||||||
|
self.access(f).map_err(|e| e.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
331
src/clientmem.rs
331
src/clientmem.rs
|
|
@ -1,329 +1,8 @@
|
||||||
use {
|
pub use jay_clientmem::*;
|
||||||
crate::{
|
|
||||||
client::Client,
|
|
||||||
cpu_worker::{AsyncCpuWork, CpuJob, CpuWork, CpuWorker},
|
|
||||||
gfx_api::{ShmMemory, ShmMemoryBacking},
|
|
||||||
utils::{
|
|
||||||
oserror::{OsError, OsErrorExt2},
|
|
||||||
page_size::page_size,
|
|
||||||
vec_ext::VecExt,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
std::{
|
|
||||||
cell::Cell,
|
|
||||||
error::Error,
|
|
||||||
mem::{ManuallyDrop, MaybeUninit},
|
|
||||||
ops::Deref,
|
|
||||||
ptr,
|
|
||||||
rc::Rc,
|
|
||||||
sync::atomic::{Ordering, compiler_fence},
|
|
||||||
},
|
|
||||||
thiserror::Error,
|
|
||||||
uapi::{
|
|
||||||
OwnedFd, Pod,
|
|
||||||
c::{self, raise},
|
|
||||||
ftruncate,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
pub fn client_mem_client(client: &crate::client::Client) -> ClientMemClient<'_> {
|
||||||
pub enum ClientMemError {
|
ClientMemClient {
|
||||||
#[error("Could not install the sigbus handler")]
|
comm: &client.pid_info.comm,
|
||||||
SigactionFailed(#[source] crate::utils::oserror::OsError),
|
id: client.id.raw(),
|
||||||
#[error("A SIGBUS occurred while accessing mapped memory")]
|
|
||||||
Sigbus,
|
|
||||||
#[error("mmap failed")]
|
|
||||||
MmapFailed(#[source] crate::utils::oserror::OsError),
|
|
||||||
#[error("Length was not a multiple of the data element size")]
|
|
||||||
InvalidLength,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ClientMem {
|
|
||||||
fd: ManuallyDrop<Rc<OwnedFd>>,
|
|
||||||
failed: Cell<bool>,
|
|
||||||
sigbus_impossible: bool,
|
|
||||||
data: *const [Cell<u8>],
|
|
||||||
cpu: Option<Rc<CpuWorker>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct ClientMemOffset {
|
|
||||||
mem: Rc<ClientMem>,
|
|
||||||
offset: usize,
|
|
||||||
data: *const [Cell<u8>],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ClientMem {
|
|
||||||
pub fn new(
|
|
||||||
fd: &Rc<OwnedFd>,
|
|
||||||
len: usize,
|
|
||||||
read_only: bool,
|
|
||||||
client: Option<&Client>,
|
|
||||||
cpu: Option<&Rc<CpuWorker>>,
|
|
||||||
is_udmabuf: bool,
|
|
||||||
) -> Result<Self, ClientMemError> {
|
|
||||||
Self::new2(fd, len, read_only, client, cpu, c::MAP_SHARED, is_udmabuf)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_private(
|
|
||||||
fd: &Rc<OwnedFd>,
|
|
||||||
len: usize,
|
|
||||||
read_only: bool,
|
|
||||||
client: Option<&Client>,
|
|
||||||
cpu: Option<&Rc<CpuWorker>>,
|
|
||||||
) -> Result<Self, ClientMemError> {
|
|
||||||
Self::new2(fd, len, read_only, client, cpu, c::MAP_PRIVATE, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new2(
|
|
||||||
fd: &Rc<OwnedFd>,
|
|
||||||
len: usize,
|
|
||||||
read_only: bool,
|
|
||||||
client: Option<&Client>,
|
|
||||||
cpu: Option<&Rc<CpuWorker>>,
|
|
||||||
flags: c::c_int,
|
|
||||||
is_udmabuf: bool,
|
|
||||||
) -> Result<Self, ClientMemError> {
|
|
||||||
let mut sigbus_impossible = is_udmabuf;
|
|
||||||
let mut real_size = None;
|
|
||||||
if !sigbus_impossible
|
|
||||||
&& let Ok(seals) = uapi::fcntl_get_seals(fd.raw())
|
|
||||||
&& seals & c::F_SEAL_SHRINK != 0
|
|
||||||
&& let Ok(stat) = uapi::fstat(fd.raw())
|
|
||||||
{
|
|
||||||
real_size = Some(stat.st_size as usize);
|
|
||||||
sigbus_impossible = stat.st_size as u64 >= len as u64;
|
|
||||||
}
|
|
||||||
if !sigbus_impossible && let Some(client) = client {
|
|
||||||
log::debug!(
|
|
||||||
"Client {} ({}) has created a shm buffer that might cause SIGBUS",
|
|
||||||
client.pid_info.comm,
|
|
||||||
client.id,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
let len = len.next_multiple_of(page_size());
|
|
||||||
if let Some(real_size) = real_size
|
|
||||||
&& real_size < len
|
|
||||||
{
|
|
||||||
let _ = ftruncate(fd.raw(), len as _);
|
|
||||||
}
|
|
||||||
let data = if len == 0 {
|
|
||||||
&mut [][..]
|
|
||||||
} else {
|
|
||||||
let prot = match read_only {
|
|
||||||
true => c::PROT_READ,
|
|
||||||
false => c::PROT_READ | c::PROT_WRITE,
|
|
||||||
};
|
|
||||||
unsafe {
|
|
||||||
let data = c::mmap64(ptr::null_mut(), len, prot, flags, fd.raw(), 0);
|
|
||||||
if data == c::MAP_FAILED {
|
|
||||||
return Err(ClientMemError::MmapFailed(OsError::default()));
|
|
||||||
}
|
|
||||||
std::slice::from_raw_parts_mut(data as *mut Cell<u8>, len)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Ok(Self {
|
|
||||||
fd: ManuallyDrop::new(fd.clone()),
|
|
||||||
failed: Cell::new(false),
|
|
||||||
sigbus_impossible,
|
|
||||||
data,
|
|
||||||
cpu: cpu.cloned(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn len(&self) -> usize {
|
|
||||||
self.data.len()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn offset(self: &Rc<Self>, offset: usize, len: usize) -> ClientMemOffset {
|
|
||||||
let mem = unsafe { &*self.data };
|
|
||||||
ClientMemOffset {
|
|
||||||
mem: self.clone(),
|
|
||||||
offset,
|
|
||||||
data: &mem[offset..][..len],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn fd(&self) -> &Rc<OwnedFd> {
|
|
||||||
&self.fd
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_sealed_memfd(&self) -> bool {
|
|
||||||
self.sigbus_impossible
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ClientMemOffset {
|
|
||||||
pub fn pool(&self) -> &ClientMem {
|
|
||||||
&self.mem
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn offset(&self) -> usize {
|
|
||||||
self.offset
|
|
||||||
}
|
|
||||||
|
|
||||||
#[expect(dead_code)]
|
|
||||||
pub fn ptr(&self) -> *const [Cell<u8>] {
|
|
||||||
self.data
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn access<T, F: FnOnce(&[Cell<u8>]) -> T>(&self, f: F) -> Result<T, ClientMemError> {
|
|
||||||
unsafe {
|
|
||||||
if self.mem.sigbus_impossible {
|
|
||||||
return Ok(f(&*self.data));
|
|
||||||
}
|
|
||||||
let mref = MemRef {
|
|
||||||
mem: &*self.mem,
|
|
||||||
outer: MEM.get(),
|
|
||||||
};
|
|
||||||
MEM.set(&mref);
|
|
||||||
compiler_fence(Ordering::SeqCst);
|
|
||||||
let res = f(&*self.data);
|
|
||||||
MEM.set(mref.outer);
|
|
||||||
compiler_fence(Ordering::SeqCst);
|
|
||||||
match self.mem.failed.get() {
|
|
||||||
true => Err(ClientMemError::Sigbus),
|
|
||||||
_ => Ok(res),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read<T: Pod>(&self, dst: &mut Vec<T>) -> Result<(), ClientMemError> {
|
|
||||||
if self.data.len().checked_rem(std::mem::size_of::<T>()) != Some(0) {
|
|
||||||
return Err(ClientMemError::InvalidLength);
|
|
||||||
}
|
|
||||||
self.access(|v| {
|
|
||||||
let len_elements = v.len() / std::mem::size_of::<T>();
|
|
||||||
dst.reserve(len_elements);
|
|
||||||
let (_, unused) = dst.split_at_spare_mut_bytes_ext();
|
|
||||||
unused[..v.len()].copy_from_slice(uapi::as_maybe_uninit_bytes(v));
|
|
||||||
unsafe {
|
|
||||||
dst.set_len(dst.len() + len_elements);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for ClientMem {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
let fd = unsafe { ManuallyDrop::take(&mut self.fd) };
|
|
||||||
if let Some(cpu) = &self.cpu {
|
|
||||||
let pending = cpu.submit(Box::new(CloseMemWork {
|
|
||||||
fd: Rc::try_unwrap(fd).ok(),
|
|
||||||
data: self.data,
|
|
||||||
}));
|
|
||||||
pending.detach();
|
|
||||||
} else {
|
|
||||||
unsafe {
|
|
||||||
c::munmap(self.data as _, self.len());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct MemRef {
|
|
||||||
mem: *const ClientMem,
|
|
||||||
outer: *const MemRef,
|
|
||||||
}
|
|
||||||
|
|
||||||
thread_local! {
|
|
||||||
static MEM: Cell<*const MemRef> = const { Cell::new(ptr::null()) };
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn kill() -> ! {
|
|
||||||
unsafe {
|
|
||||||
c::signal(c::SIGBUS, c::SIG_DFL);
|
|
||||||
raise(c::SIGBUS);
|
|
||||||
}
|
|
||||||
unreachable!();
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe extern "C" fn sigbus(sig: i32, info: &c::siginfo_t, _ucontext: *mut c::c_void) {
|
|
||||||
unsafe {
|
|
||||||
assert_eq!(sig, c::SIGBUS);
|
|
||||||
let mut memr_ptr = MEM.get();
|
|
||||||
while !memr_ptr.is_null() {
|
|
||||||
let memr = &*memr_ptr;
|
|
||||||
let mem = &*memr.mem;
|
|
||||||
let lo = mem.data as *mut u8 as usize;
|
|
||||||
let hi = lo + mem.len();
|
|
||||||
let fault_addr = info.si_addr() as usize;
|
|
||||||
if fault_addr < lo || fault_addr >= hi {
|
|
||||||
memr_ptr = memr.outer;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let res = c::mmap64(
|
|
||||||
lo as _,
|
|
||||||
hi - lo,
|
|
||||||
c::PROT_WRITE | c::PROT_READ,
|
|
||||||
c::MAP_ANONYMOUS | c::MAP_PRIVATE | c::MAP_FIXED,
|
|
||||||
-1,
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
if res == c::MAP_FAILED {
|
|
||||||
kill();
|
|
||||||
}
|
|
||||||
mem.failed.set(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
kill();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn init() -> Result<(), ClientMemError> {
|
|
||||||
unsafe {
|
|
||||||
let mut action: c::sigaction = MaybeUninit::zeroed().assume_init();
|
|
||||||
action.sa_sigaction =
|
|
||||||
sigbus as unsafe extern "C" fn(i32, &c::siginfo_t, *mut c::c_void) as _;
|
|
||||||
action.sa_flags = c::SA_NODEFER | c::SA_SIGINFO;
|
|
||||||
let res = c::sigaction(c::SIGBUS, &action, ptr::null_mut());
|
|
||||||
uapi::map_err!(res)
|
|
||||||
.map(drop)
|
|
||||||
.map_os_err(ClientMemError::SigactionFailed)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct CloseMemWork {
|
|
||||||
fd: Option<OwnedFd>,
|
|
||||||
data: *const [Cell<u8>],
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl Send for CloseMemWork {}
|
|
||||||
|
|
||||||
impl CpuJob for CloseMemWork {
|
|
||||||
fn work(&mut self) -> &mut dyn CpuWork {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn completed(self: Box<Self>) {
|
|
||||||
// nothing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CpuWork for CloseMemWork {
|
|
||||||
fn run(&mut self) -> Option<Box<dyn AsyncCpuWork>> {
|
|
||||||
zone!("CloseMemWork");
|
|
||||||
self.fd.take();
|
|
||||||
unsafe {
|
|
||||||
c::munmap(self.data as _, self.data.len());
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ShmMemory for ClientMemOffset {
|
|
||||||
fn len(&self) -> usize {
|
|
||||||
self.data.len()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn safe_access(&self) -> ShmMemoryBacking {
|
|
||||||
match self.mem.is_sealed_memfd() {
|
|
||||||
true => ShmMemoryBacking::Ptr(self.data),
|
|
||||||
false => ShmMemoryBacking::Fd(self.mem.fd.deref().clone(), self.offset),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn access(&self, f: &mut dyn FnMut(&[Cell<u8>])) -> Result<(), Box<dyn Error + Sync + Send>> {
|
|
||||||
self.access(f).map_err(|e| e.into())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use {
|
||||||
InputDeviceAccelProfile, InputDeviceCapability, InputDeviceClickMethod, InputDeviceId,
|
InputDeviceAccelProfile, InputDeviceCapability, InputDeviceClickMethod, InputDeviceId,
|
||||||
},
|
},
|
||||||
client::{Client, ClientError},
|
client::{Client, ClientError},
|
||||||
clientmem::{ClientMem, ClientMemError},
|
clientmem::{ClientMem, ClientMemError, client_mem_client},
|
||||||
ifs::wl_seat::WlSeatGlobal,
|
ifs::wl_seat::WlSeatGlobal,
|
||||||
kbvm::{KbvmError, KbvmMap},
|
kbvm::{KbvmError, KbvmMap},
|
||||||
leaks::Tracker,
|
leaks::Tracker,
|
||||||
|
|
@ -204,7 +204,7 @@ impl JayInput {
|
||||||
keymap,
|
keymap,
|
||||||
len,
|
len,
|
||||||
true,
|
true,
|
||||||
Some(&self.client),
|
Some(client_mem_client(&self.client)),
|
||||||
None,
|
None,
|
||||||
)?)
|
)?)
|
||||||
.offset(0, len);
|
.offset(0, len);
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use {
|
||||||
crate::{
|
crate::{
|
||||||
backend::KeyState,
|
backend::KeyState,
|
||||||
client::{Client, ClientError},
|
client::{Client, ClientError},
|
||||||
clientmem::{ClientMem, ClientMemError},
|
clientmem::{ClientMem, ClientMemError, client_mem_client},
|
||||||
ifs::{
|
ifs::{
|
||||||
wl_seat::{
|
wl_seat::{
|
||||||
WlSeatGlobal,
|
WlSeatGlobal,
|
||||||
|
|
@ -59,7 +59,8 @@ impl ZwpVirtualKeyboardV1RequestHandler for ZwpVirtualKeyboardV1 {
|
||||||
return Err(ZwpVirtualKeyboardV1Error::OversizedKeymap);
|
return Err(ZwpVirtualKeyboardV1Error::OversizedKeymap);
|
||||||
}
|
}
|
||||||
let size = req.size as usize - 1;
|
let size = req.size as usize - 1;
|
||||||
let client_mem = ClientMem::new_private(&req.fd, size, true, Some(&self.client), None)
|
let client_mem =
|
||||||
|
ClientMem::new_private(&req.fd, size, true, Some(client_mem_client(&self.client)), None)
|
||||||
.map(Rc::new)
|
.map(Rc::new)
|
||||||
.map_err(ZwpVirtualKeyboardV1Error::MapKeymap)?;
|
.map_err(ZwpVirtualKeyboardV1Error::MapKeymap)?;
|
||||||
let mut map = vec![];
|
let mut map = vec![];
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
client::{Client, ClientError},
|
client::{Client, ClientError},
|
||||||
clientmem::{ClientMem, ClientMemError},
|
clientmem::{ClientMem, ClientMemError, client_mem_client},
|
||||||
format::{formats, map_wayland_format_id},
|
format::{formats, map_wayland_format_id},
|
||||||
ifs::wl_buffer::{WlBuffer, WlBufferError},
|
ifs::wl_buffer::{WlBuffer, WlBufferError},
|
||||||
leaks::Tracker,
|
leaks::Tracker,
|
||||||
|
|
@ -40,7 +40,7 @@ impl WlShmPool {
|
||||||
&fd,
|
&fd,
|
||||||
len,
|
len,
|
||||||
false,
|
false,
|
||||||
Some(client),
|
Some(client_mem_client(client)),
|
||||||
Some(&client.state.cpu_worker),
|
Some(&client.state.cpu_worker),
|
||||||
false,
|
false,
|
||||||
)?)),
|
)?)),
|
||||||
|
|
@ -97,7 +97,7 @@ impl WlShmPoolRequestHandler for WlShmPool {
|
||||||
&self.fd,
|
&self.fd,
|
||||||
len,
|
len,
|
||||||
false,
|
false,
|
||||||
Some(&self.client),
|
Some(client_mem_client(&self.client)),
|
||||||
Some(&self.client.state.cpu_worker),
|
Some(&self.client.state.cpu_worker),
|
||||||
false,
|
false,
|
||||||
)?));
|
)?));
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use {
|
||||||
crate::{
|
crate::{
|
||||||
backend::{BackendGammaLut, BackendGammaLutElement},
|
backend::{BackendGammaLut, BackendGammaLutElement},
|
||||||
client::{Client, ClientError, ClientId},
|
client::{Client, ClientError, ClientId},
|
||||||
clientmem::{ClientMem, ClientMemError},
|
clientmem::{ClientMem, ClientMemError, client_mem_client},
|
||||||
ifs::{
|
ifs::{
|
||||||
wl_output::OutputGlobalOpt, zwlr_gamma_control_manager_v1::ZwlrGammaControlManagerV1,
|
wl_output::OutputGlobalOpt, zwlr_gamma_control_manager_v1::ZwlrGammaControlManagerV1,
|
||||||
},
|
},
|
||||||
|
|
@ -119,7 +119,7 @@ impl ZwlrGammaControlV1RequestHandler for ZwlrGammaControlV1 {
|
||||||
&req.fd,
|
&req.fd,
|
||||||
data_size,
|
data_size,
|
||||||
true,
|
true,
|
||||||
Some(&self.client),
|
Some(client_mem_client(&self.client)),
|
||||||
None,
|
None,
|
||||||
)?)
|
)?)
|
||||||
.offset(0, data_size)
|
.offset(0, data_size)
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
client::ClientError,
|
client::ClientError,
|
||||||
clientmem::{ClientMem, ClientMemError},
|
clientmem::{ClientMem, ClientMemError, client_mem_client},
|
||||||
gfx_api::GfxError,
|
gfx_api::GfxError,
|
||||||
ifs::{
|
ifs::{
|
||||||
wl_buffer::{WlBuffer, WlBufferError},
|
wl_buffer::{WlBuffer, WlBufferError},
|
||||||
|
|
@ -119,7 +119,7 @@ impl ZwpLinuxBufferParamsV1 {
|
||||||
&p.fd,
|
&p.fd,
|
||||||
size,
|
size,
|
||||||
true,
|
true,
|
||||||
Some(&self.parent.client),
|
Some(client_mem_client(&self.parent.client)),
|
||||||
Some(&self.parent.client.state.cpu_worker),
|
Some(&self.parent.client.state.cpu_worker),
|
||||||
true,
|
true,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue