1
0
Fork 0
forked from wry/wry

linux-dmabuf: intercept udmabuf buffers

This commit is contained in:
Julian Orth 2025-10-01 17:34:24 +02:00
parent 0570669af2
commit 410281b13e
5 changed files with 131 additions and 32 deletions

View file

@ -54,8 +54,9 @@ impl ClientMem {
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)
Self::new2(fd, len, read_only, client, cpu, c::MAP_SHARED, is_udmabuf)
}
pub fn new_private(
@ -65,7 +66,7 @@ impl ClientMem {
client: Option<&Client>,
cpu: Option<&Rc<CpuWorker>>,
) -> Result<Self, ClientMemError> {
Self::new2(fd, len, read_only, client, cpu, c::MAP_PRIVATE)
Self::new2(fd, len, read_only, client, cpu, c::MAP_PRIVATE, false)
}
fn new2(
@ -75,10 +76,12 @@ impl ClientMem {
client: Option<&Client>,
cpu: Option<&Rc<CpuWorker>>,
flags: c::c_int,
is_udmabuf: bool,
) -> Result<Self, ClientMemError> {
let mut sigbus_impossible = false;
let mut sigbus_impossible = is_udmabuf;
let mut real_size = None;
if let Ok(seals) = uapi::fcntl_get_seals(fd.raw())
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())
{

View file

@ -108,6 +108,7 @@ impl WlBuffer {
stride: i32,
format: &'static Format,
mem: &Rc<ClientMem>,
udmabuf: Option<(&Rc<OwnedFd>, usize)>,
) -> Result<Self, WlBufferError> {
let bytes = stride as u64 * height as u64;
let required = bytes + offset as u64;
@ -119,6 +120,26 @@ impl WlBuffer {
if (stride as u64) < min_row_size {
return Err(WlBufferError::StrideTooSmall);
}
let dmabuf_buffer_params = match udmabuf {
None => DmabufBufferParams {
size: bytes as usize,
udmabuf: None,
udmabuf_offset: 0,
udmabuf_size: 0,
udmabuf_impossible: !mem.pool().is_sealed_memfd(),
host_buffer: None,
host_buffer_impossible: !mem.pool().is_sealed_memfd(),
},
Some((udmabuf, size)) => DmabufBufferParams {
size,
udmabuf: Some(udmabuf.clone()),
udmabuf_offset: offset,
udmabuf_size: size,
udmabuf_impossible: false,
host_buffer: None,
host_buffer_impossible: false,
},
};
Ok(Self {
id,
destroyed: Cell::new(false),
@ -128,15 +149,7 @@ impl WlBuffer {
dmabuf: None,
render_ctx_version: Cell::new(client.state.render_ctx_version.get()),
storage: RefCell::new(Some(WlBufferStorage::Shm {
dmabuf_buffer_params: DmabufBufferParams {
size: bytes as usize,
udmabuf: None,
udmabuf_offset: 0,
udmabuf_size: 0,
udmabuf_impossible: !mem.pool().is_sealed_memfd(),
host_buffer: None,
host_buffer_impossible: !mem.pool().is_sealed_memfd(),
},
dmabuf_buffer_params,
mem,
stride,
})),
@ -191,9 +204,9 @@ impl WlBuffer {
};
let had_texture = match s {
WlBufferStorage::Shm {
mem,
dmabuf_buffer_params:
DmabufBufferParams {
udmabuf_impossible,
host_buffer,
host_buffer_impossible,
..
@ -201,7 +214,7 @@ impl WlBuffer {
..
} => {
host_buffer.take();
*host_buffer_impossible = !mem.pool().is_sealed_memfd();
*host_buffer_impossible = *udmabuf_impossible;
return match surface {
Some(s) => {
s.shm_staging.take();

View file

@ -40,6 +40,7 @@ impl WlShmPool {
false,
Some(client),
Some(&client.state.cpu_worker),
false,
)?)),
fd,
tracker: Default::default(),
@ -68,6 +69,7 @@ impl WlShmPoolRequestHandler for WlShmPool {
req.stride,
format,
&self.mem.get(),
None,
)?);
track!(self.client, buffer);
self.client.add_client_obj(&buffer)?;
@ -92,6 +94,7 @@ impl WlShmPoolRequestHandler for WlShmPool {
false,
Some(&self.client),
Some(&self.client.state.cpu_worker),
false,
)?));
Ok(())
}

View file

@ -1,8 +1,12 @@
use {
crate::{
client::ClientError,
clientmem::{ClientMem, ClientMemError},
gfx_api::GfxError,
ifs::{wl_buffer::WlBuffer, zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1},
ifs::{
wl_buffer::{WlBuffer, WlBufferError},
zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1,
},
leaks::Tracker,
object::Object,
utils::{errorfmt::ErrorFmt, hash_map_ext::HashMapExt},
@ -102,25 +106,52 @@ impl ZwpLinuxBufferParamsV1 {
fd: p.fd,
});
}
let img = ctx.dmabuf_img(&dmabuf)?;
let (is_client_id, buffer_id) = match buffer_id {
Some(i) => (true, i),
None => (false, self.parent.client.new_id()?),
let get_id = || match buffer_id {
None => self.parent.client.new_id(),
Some(i) => Ok(i),
};
let buffer = if format.supports_shm
&& let Some(size) = dmabuf.udmabuf_size()
{
let p = &dmabuf.planes[0];
let client_mem = ClientMem::new(
&p.fd,
size,
true,
Some(&self.parent.client),
Some(&self.parent.client.state.cpu_worker),
true,
)
.map(Rc::new)
.map_err(ZwpLinuxBufferParamsV1Error::CreateClientMem)?;
Rc::new(WlBuffer::new_shm(
get_id()?,
&self.parent.client,
p.offset as usize,
dmabuf.width,
dmabuf.height,
p.stride as _,
format.format,
&client_mem,
Some((&p.fd, size)),
)?)
} else {
let img = ctx.dmabuf_img(&dmabuf)?;
Rc::new(WlBuffer::new_dmabuf(
get_id()?,
&self.parent.client,
format.format,
dmabuf,
&img,
))
};
let buffer = Rc::new(WlBuffer::new_dmabuf(
buffer_id,
&self.parent.client,
format.format,
dmabuf,
&img,
));
track!(self.parent.client, buffer);
if is_client_id {
if buffer_id.is_some() {
self.parent.client.add_client_obj(&buffer)?;
} else {
self.parent.client.add_server_obj(&buffer);
}
Ok(buffer_id)
Ok(buffer.id)
}
}
@ -218,5 +249,10 @@ pub enum ZwpLinuxBufferParamsV1Error {
MissingPlane(usize),
#[error("Could not import the buffer")]
ImportError(#[from] GfxError),
#[error("Could not create ClientMem")]
CreateClientMem(#[source] ClientMemError),
#[error(transparent)]
WlBufferError(Box<WlBufferError>),
}
efrom!(ZwpLinuxBufferParamsV1Error, ClientError);
efrom!(ZwpLinuxBufferParamsV1Error, WlBufferError);

View file

@ -2,11 +2,15 @@ use {
crate::{
format::Format,
utils::{compat::IoctlNumber, oserror::OsError},
video::Modifier,
video::{LINEAR_MODIFIER, Modifier},
},
arrayvec::ArrayVec,
std::rc::Rc,
uapi::{_IOW, _IOWR, OwnedFd, c::ioctl},
std::{rc::Rc, sync::OnceLock},
uapi::{
_IOW, _IOWR, OwnedFd,
c::{self, dev_t, ioctl},
format_ustr,
},
};
#[derive(Clone, Debug)]
@ -53,6 +57,46 @@ impl DmaBuf {
false
}
pub fn udmabuf_size(&self) -> Option<usize> {
if self.planes.len() != 1 {
return None;
}
if self.modifier != LINEAR_MODIFIER {
return None;
}
let stat = match uapi::fstat(self.planes[0].fd.raw()) {
Ok(s) => s,
_ => return None,
};
static DMABUF_DEV: OnceLock<dev_t> = OnceLock::new();
match DMABUF_DEV.get() {
Some(d) => {
if stat.st_dev != *d {
return None;
}
}
None => {
if dma_buf_export_sync_file(&self.planes[0].fd, DMA_BUF_SYNC_READ).is_err() {
return None;
}
let _ = DMABUF_DEV.set(stat.st_dev);
}
}
let path = format_ustr!("/sys/kernel/dmabuf/buffers/{}/exporter_name", stat.st_ino);
let Ok(file) = uapi::open(path, c::O_RDONLY, 0) else {
return None;
};
const MARKER: &[u8] = b"udmabuf\n";
let mut buf = [0u8; MARKER.len()];
if uapi::read(file.raw(), &mut buf).is_err() {
return None;
}
if buf != MARKER {
return None;
}
Some(stat.st_size as usize)
}
pub fn import_sync_file(&self, flags: u32, sync_file: &OwnedFd) -> Result<(), OsError> {
for plane in &self.planes {
dma_buf_import_sync_file(&plane.fd, flags, sync_file)?;