diff --git a/Cargo.lock b/Cargo.lock index 853d6566..5fd0995c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -750,6 +750,7 @@ dependencies = [ "jay-toml-config", "jay-tracy", "jay-tree-types", + "jay-udmabuf", "jay-units", "jay-utils", "jay-video-types", @@ -1054,6 +1055,19 @@ dependencies = [ "linearize", ] +[[package]] +name = "jay-udmabuf" +version = "0.1.0" +dependencies = [ + "jay-allocator", + "jay-formats", + "jay-utils", + "jay-video-types", + "log", + "thiserror", + "uapi", +] + [[package]] name = "jay-units" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index d4494401..046ca5f5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,6 +49,7 @@ members = [ "allocator", "output-schedule", "drm-feedback", + "udmabuf", "pango", "libinput", "toml-config", @@ -101,6 +102,7 @@ jay-clientmem = { version = "0.1.0", path = "clientmem" } jay-allocator = { version = "0.1.0", path = "allocator" } jay-output-schedule = { version = "0.1.0", path = "output-schedule" } jay-drm-feedback = { version = "0.1.0", path = "drm-feedback" } +jay-udmabuf = { version = "0.1.0", path = "udmabuf" } jay-pango = { version = "0.1.0", path = "pango" } jay-libinput = { version = "0.1.0", path = "libinput" } diff --git a/src/udmabuf.rs b/src/udmabuf.rs index 7240e512..e588fd9b 100644 --- a/src/udmabuf.rs +++ b/src/udmabuf.rs @@ -1,324 +1 @@ -use { - crate::{ - allocator::{Allocator, AllocatorError, BufferObject, BufferUsage, MappedBuffer}, - format::Format, - utils::{ - clonecell::CloneCell, - compat::IoctlNumber, - errorfmt::ErrorFmt, - once::Once, - oserror::{OsError, OsErrorExt, OsErrorExt2}, - page_size::page_size, - }, - video::{ - LINEAR_MODIFIER, LINEAR_STRIDE_ALIGN, Modifier, - dmabuf::{DmaBuf, DmaBufIds, DmaBufPlane, PlaneVec}, - }, - }, - std::{ptr, rc::Rc}, - thiserror::Error, - uapi::{ - _IOW, OwnedFd, - c::{ - self, F_SEAL_SHRINK, MAP_SHARED, MFD_ALLOW_SEALING, O_RDONLY, PROT_READ, PROT_WRITE, - ioctl, mmap, munmap, - }, - map_err, open, - }, -}; - -#[derive(Debug, Error)] -pub enum UdmabufError { - #[error("Could not open /dev/udmabuf")] - Open(#[source] OsError), - #[error("Only the linear modifier can be allocated")] - Modifier, - #[error("Could not create a memfd")] - Memfd(#[source] OsError), - #[error("Size calculation overflowed")] - Overflow, - #[error("Could not resize the memfd")] - Truncate(#[source] OsError), - #[error("Could not seal the memfd")] - Seal(#[source] OsError), - #[error("Could not create a dmabuf")] - CreateDmabuf(#[source] OsError), - #[error("Only a single plane is supported")] - Planes, - #[error("Stride is invalid")] - Stride, - #[error("Could not stat the dmabuf")] - Stat(#[source] OsError), - #[error("Dmabuf is too small for required size")] - Size, - #[error("Could not map dmabuf")] - Map(#[source] OsError), -} - -#[derive(Default)] -pub struct UdmabufHolder { - logged: Once, - udmabuf: CloneCell>>>, -} - -impl UdmabufHolder { - pub fn get(&self) -> Option> { - if let Some(u) = self.udmabuf.get() { - return u; - } - match Udmabuf::new() { - Ok(u) => { - log::info!("Opened /dev/udmabuf"); - let u = Rc::new(u); - self.udmabuf.set(Some(Some(u.clone()))); - Some(u) - } - Err(e) => { - self.logged.exec(|| { - log::warn!("Unable to open /dev/udmabuf: {}", ErrorFmt(&e)); - }); - if not_matches!(e, UdmabufError::Open(OsError(c::EPERM))) { - self.udmabuf.set(Some(None)); - } - None - } - } - } -} - -pub struct Udmabuf { - fd: OwnedFd, -} - -impl Udmabuf { - pub fn new() -> Result { - let fd = open("/dev/udmabuf", O_RDONLY, 0).map_os_err(UdmabufError::Open)?; - Ok(Self { fd }) - } - - pub fn create_dmabuf_from_memfd( - &self, - memfd: &OwnedFd, - offset: usize, - size: usize, - ) -> Result { - let mut cmd = udmabuf_create { - memfd: memfd.raw() as u32, - flags: UDMABUF_FLAGS_CLOEXEC, - offset: offset as u64, - size: size as u64, - }; - let dmabuf = unsafe { ioctl(self.fd.raw(), UDMABUF_CREATE, &mut cmd) }; - let dmabuf = map_err!(dmabuf) - .map(OwnedFd::new) - .map_os_err(UdmabufError::CreateDmabuf)?; - Ok(dmabuf) - } - - pub fn create_dmabuf( - &self, - dma_buf_ids: &DmaBufIds, - width: i32, - height: i32, - format: &'static Format, - ) -> Result { - Ok(self.create_bo(dma_buf_ids, width, height, format)?.buf) - } - - fn create_bo( - &self, - dma_buf_ids: &DmaBufIds, - width: i32, - height: i32, - format: &'static Format, - ) -> Result { - let height = height as u64; - let width = width as u64; - if height > 1 << 16 || width > 1 << 16 { - return Err(UdmabufError::Overflow); - } - let stride = (width * format.bpp as u64).next_multiple_of(LINEAR_STRIDE_ALIGN); - let size_mask = page_size() as u64 - 1; - let size = (height * stride + size_mask) & !size_mask; - let memfd = - uapi::memfd_create("udmabuf", MFD_ALLOW_SEALING).map_os_err(UdmabufError::Memfd)?; - uapi::ftruncate(memfd.raw(), size as _).map_os_err(UdmabufError::Truncate)?; - uapi::fcntl_add_seals(memfd.raw(), F_SEAL_SHRINK).map_os_err(UdmabufError::Seal)?; - let dmabuf = self.create_dmabuf_from_memfd(&memfd, 0, size as _)?; - let mut planes = PlaneVec::new(); - planes.push(DmaBufPlane { - offset: 0, - stride: stride as _, - fd: Rc::new(dmabuf), - }); - let dmabuf = DmaBuf { - id: dma_buf_ids.next(), - width: width as _, - height: height as _, - format, - modifier: LINEAR_MODIFIER, - planes, - is_disjoint: Default::default(), - }; - Ok(UdmabufBo { - buf: dmabuf, - size: size as _, - }) - } -} - -impl Allocator for Udmabuf { - fn drm(&self) -> Option<&dyn crate::allocator::AllocatorDrm> { - None - } - - fn create_bo( - &self, - dma_buf_ids: &DmaBufIds, - width: i32, - height: i32, - format: &'static Format, - modifiers: &[Modifier], - _usage: BufferUsage, - ) -> Result, AllocatorError> { - if !modifiers.contains(&LINEAR_MODIFIER) { - return Err(UdmabufError::Modifier.into()); - } - Ok(Rc::new(self.create_bo( - dma_buf_ids, - width, - height, - format, - )?)) - } - - fn import_dmabuf( - &self, - dmabuf: &DmaBuf, - _usage: BufferUsage, - ) -> Result, AllocatorError> { - if dmabuf.planes.len() != 1 { - return Err(UdmabufError::Planes.into()); - } - if dmabuf.modifier != LINEAR_MODIFIER { - return Err(UdmabufError::Modifier.into()); - } - let plane = &dmabuf.planes[0]; - let height = dmabuf.height as u64; - let width = dmabuf.width as u64; - let stride = plane.stride as u64; - let offset = plane.offset as u64; - if height > 1 << 16 || width > 1 << 16 { - return Err(UdmabufError::Overflow.into()); - } - if stride < width * dmabuf.format.bpp as u64 { - return Err(UdmabufError::Stride.into()); - } - let size = offset + stride * height; - if usize::try_from(size).is_err() { - return Err(UdmabufError::Overflow.into()); - } - let stat = uapi::fstat(plane.fd.raw()).map_os_err(UdmabufError::Stat)?; - if (stat.st_size as u64) < size { - return Err(UdmabufError::Size.into()); - } - Ok(Rc::new(UdmabufBo { - buf: dmabuf.clone(), - size: size as usize, - })) - } -} - -struct UdmabufBo { - buf: DmaBuf, - size: usize, -} - -impl BufferObject for UdmabufBo { - fn dmabuf(&self) -> &DmaBuf { - &self.buf - } - - fn map_read(self: Rc) -> Result, AllocatorError> { - self.map_write() - } - - fn map_write(self: Rc) -> Result, AllocatorError> { - let plane = &self.buf.planes[0]; - unsafe { - let res = mmap( - ptr::null_mut(), - self.size, - PROT_READ | PROT_WRITE, - MAP_SHARED, - plane.fd.raw(), - 0, - ); - if res == c::MAP_FAILED { - return Err(UdmabufError::Map(OsError::default()).into()); - } - let offset = plane.offset as _; - let data = - std::slice::from_raw_parts_mut((res as *mut u8).add(offset), self.size - offset); - Ok(Box::new(UdmabufMap { - data, - stride: plane.stride as _, - ptr: res, - len: self.size, - _bo: self, - })) - } - } -} - -struct UdmabufMap { - _bo: Rc, - data: *mut [u8], - stride: i32, - ptr: *mut c::c_void, - len: usize, -} - -impl Drop for UdmabufMap { - fn drop(&mut self) { - unsafe { - let res = munmap(self.ptr, self.len); - if let Err(e) = map_err!(res).to_os_error() { - log::error!("Could not unmap udmabuf: {}", e); - } - } - } -} - -impl MappedBuffer for UdmabufMap { - unsafe fn data(&self) -> &[u8] { - unsafe { &*self.data } - } - - fn data_ptr(&self) -> *mut u8 { - self.data as _ - } - - fn stride(&self) -> i32 { - self.stride - } -} - -impl From for AllocatorError { - fn from(value: UdmabufError) -> Self { - Self(Box::new(value)) - } -} - -#[repr(C)] -#[derive(Debug)] -struct udmabuf_create { - memfd: u32, - flags: u32, - offset: u64, - size: u64, -} - -const UDMABUF_FLAGS_CLOEXEC: u32 = 0x01; - -const UDMABUF_CREATE: IoctlNumber = _IOW::(b'u' as u64, 0x42) as IoctlNumber; +pub use jay_udmabuf::*; diff --git a/udmabuf/Cargo.toml b/udmabuf/Cargo.toml new file mode 100644 index 00000000..09c96858 --- /dev/null +++ b/udmabuf/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "jay-udmabuf" +version = "0.1.0" +edition = "2024" +license = "GPL-3.0-only" + +[dependencies] +jay-allocator = { version = "0.1.0", path = "../allocator" } +jay-formats = { version = "0.1.0", path = "../formats" } +jay-utils = { version = "0.1.0", path = "../utils" } +jay-video-types = { version = "0.1.0", path = "../video-types" } + +log = "0.4.20" +thiserror = "2.0.11" +uapi = "0.2.13" diff --git a/udmabuf/src/lib.rs b/udmabuf/src/lib.rs new file mode 100644 index 00000000..2e90536c --- /dev/null +++ b/udmabuf/src/lib.rs @@ -0,0 +1,324 @@ +use { + jay_allocator::{ + Allocator, AllocatorDrm, AllocatorError, BufferObject, BufferUsage, MappedBuffer, + }, + jay_formats::Format, + jay_utils::{ + clonecell::CloneCell, + compat::IoctlNumber, + errorfmt::ErrorFmt, + once::Once, + oserror::{OsError, OsErrorExt, OsErrorExt2}, + page_size::page_size, + }, + jay_video_types::{ + LINEAR_MODIFIER, LINEAR_STRIDE_ALIGN, Modifier, + dmabuf::{DmaBuf, DmaBufIds, DmaBufPlane, PlaneVec}, + }, + std::{ptr, rc::Rc}, + thiserror::Error, + uapi::{ + _IOW, OwnedFd, + c::{ + self, F_SEAL_SHRINK, MAP_SHARED, MFD_ALLOW_SEALING, O_RDONLY, PROT_READ, PROT_WRITE, + ioctl, mmap, munmap, + }, + map_err, open, + }, +}; + +#[derive(Debug, Error)] +pub enum UdmabufError { + #[error("Could not open /dev/udmabuf")] + Open(#[source] OsError), + #[error("Only the linear modifier can be allocated")] + Modifier, + #[error("Could not create a memfd")] + Memfd(#[source] OsError), + #[error("Size calculation overflowed")] + Overflow, + #[error("Could not resize the memfd")] + Truncate(#[source] OsError), + #[error("Could not seal the memfd")] + Seal(#[source] OsError), + #[error("Could not create a dmabuf")] + CreateDmabuf(#[source] OsError), + #[error("Only a single plane is supported")] + Planes, + #[error("Stride is invalid")] + Stride, + #[error("Could not stat the dmabuf")] + Stat(#[source] OsError), + #[error("Dmabuf is too small for required size")] + Size, + #[error("Could not map dmabuf")] + Map(#[source] OsError), +} + +#[derive(Default)] +pub struct UdmabufHolder { + logged: Once, + udmabuf: CloneCell>>>, +} + +impl UdmabufHolder { + pub fn get(&self) -> Option> { + if let Some(u) = self.udmabuf.get() { + return u; + } + match Udmabuf::new() { + Ok(u) => { + log::info!("Opened /dev/udmabuf"); + let u = Rc::new(u); + self.udmabuf.set(Some(Some(u.clone()))); + Some(u) + } + Err(e) => { + self.logged.exec(|| { + log::warn!("Unable to open /dev/udmabuf: {}", ErrorFmt(&e)); + }); + if !matches!(e, UdmabufError::Open(OsError(c::EPERM))) { + self.udmabuf.set(Some(None)); + } + None + } + } + } +} + +pub struct Udmabuf { + fd: OwnedFd, +} + +impl Udmabuf { + pub fn new() -> Result { + let fd = open("/dev/udmabuf", O_RDONLY, 0).map_os_err(UdmabufError::Open)?; + Ok(Self { fd }) + } + + pub fn create_dmabuf_from_memfd( + &self, + memfd: &OwnedFd, + offset: usize, + size: usize, + ) -> Result { + let mut cmd = udmabuf_create { + memfd: memfd.raw() as u32, + flags: UDMABUF_FLAGS_CLOEXEC, + offset: offset as u64, + size: size as u64, + }; + let dmabuf = unsafe { ioctl(self.fd.raw(), UDMABUF_CREATE, &mut cmd) }; + let dmabuf = map_err!(dmabuf) + .map(OwnedFd::new) + .map_os_err(UdmabufError::CreateDmabuf)?; + Ok(dmabuf) + } + + pub fn create_dmabuf( + &self, + dma_buf_ids: &DmaBufIds, + width: i32, + height: i32, + format: &'static Format, + ) -> Result { + Ok(self.create_bo(dma_buf_ids, width, height, format)?.buf) + } + + fn create_bo( + &self, + dma_buf_ids: &DmaBufIds, + width: i32, + height: i32, + format: &'static Format, + ) -> Result { + let height = height as u64; + let width = width as u64; + if height > 1 << 16 || width > 1 << 16 { + return Err(UdmabufError::Overflow); + } + let stride = (width * format.bpp as u64).next_multiple_of(LINEAR_STRIDE_ALIGN); + let size_mask = page_size() as u64 - 1; + let size = (height * stride + size_mask) & !size_mask; + let memfd = + uapi::memfd_create("udmabuf", MFD_ALLOW_SEALING).map_os_err(UdmabufError::Memfd)?; + uapi::ftruncate(memfd.raw(), size as _).map_os_err(UdmabufError::Truncate)?; + uapi::fcntl_add_seals(memfd.raw(), F_SEAL_SHRINK).map_os_err(UdmabufError::Seal)?; + let dmabuf = self.create_dmabuf_from_memfd(&memfd, 0, size as _)?; + let mut planes = PlaneVec::new(); + planes.push(DmaBufPlane { + offset: 0, + stride: stride as _, + fd: Rc::new(dmabuf), + }); + let dmabuf = DmaBuf { + id: dma_buf_ids.next(), + width: width as _, + height: height as _, + format, + modifier: LINEAR_MODIFIER, + planes, + is_disjoint: Default::default(), + }; + Ok(UdmabufBo { + buf: dmabuf, + size: size as _, + }) + } +} + +impl Allocator for Udmabuf { + fn drm(&self) -> Option<&dyn AllocatorDrm> { + None + } + + fn create_bo( + &self, + dma_buf_ids: &DmaBufIds, + width: i32, + height: i32, + format: &'static Format, + modifiers: &[Modifier], + _usage: BufferUsage, + ) -> Result, AllocatorError> { + if !modifiers.contains(&LINEAR_MODIFIER) { + return Err(UdmabufError::Modifier.into()); + } + Ok(Rc::new(self.create_bo( + dma_buf_ids, + width, + height, + format, + )?)) + } + + fn import_dmabuf( + &self, + dmabuf: &DmaBuf, + _usage: BufferUsage, + ) -> Result, AllocatorError> { + if dmabuf.planes.len() != 1 { + return Err(UdmabufError::Planes.into()); + } + if dmabuf.modifier != LINEAR_MODIFIER { + return Err(UdmabufError::Modifier.into()); + } + let plane = &dmabuf.planes[0]; + let height = dmabuf.height as u64; + let width = dmabuf.width as u64; + let stride = plane.stride as u64; + let offset = plane.offset as u64; + if height > 1 << 16 || width > 1 << 16 { + return Err(UdmabufError::Overflow.into()); + } + if stride < width * dmabuf.format.bpp as u64 { + return Err(UdmabufError::Stride.into()); + } + let size = offset + stride * height; + if usize::try_from(size).is_err() { + return Err(UdmabufError::Overflow.into()); + } + let stat = uapi::fstat(plane.fd.raw()).map_os_err(UdmabufError::Stat)?; + if (stat.st_size as u64) < size { + return Err(UdmabufError::Size.into()); + } + Ok(Rc::new(UdmabufBo { + buf: dmabuf.clone(), + size: size as usize, + })) + } +} + +struct UdmabufBo { + buf: DmaBuf, + size: usize, +} + +impl BufferObject for UdmabufBo { + fn dmabuf(&self) -> &DmaBuf { + &self.buf + } + + fn map_read(self: Rc) -> Result, AllocatorError> { + self.map_write() + } + + fn map_write(self: Rc) -> Result, AllocatorError> { + let plane = &self.buf.planes[0]; + unsafe { + let res = mmap( + ptr::null_mut(), + self.size, + PROT_READ | PROT_WRITE, + MAP_SHARED, + plane.fd.raw(), + 0, + ); + if res == c::MAP_FAILED { + return Err(UdmabufError::Map(OsError::default()).into()); + } + let offset = plane.offset as _; + let data = + std::slice::from_raw_parts_mut((res as *mut u8).add(offset), self.size - offset); + Ok(Box::new(UdmabufMap { + data, + stride: plane.stride as _, + ptr: res, + len: self.size, + _bo: self, + })) + } + } +} + +struct UdmabufMap { + _bo: Rc, + data: *mut [u8], + stride: i32, + ptr: *mut c::c_void, + len: usize, +} + +impl Drop for UdmabufMap { + fn drop(&mut self) { + unsafe { + let res = munmap(self.ptr, self.len); + if let Err(e) = map_err!(res).to_os_error() { + log::error!("Could not unmap udmabuf: {}", e); + } + } + } +} + +impl MappedBuffer for UdmabufMap { + unsafe fn data(&self) -> &[u8] { + unsafe { &*self.data } + } + + fn data_ptr(&self) -> *mut u8 { + self.data as _ + } + + fn stride(&self) -> i32 { + self.stride + } +} + +impl From for AllocatorError { + fn from(value: UdmabufError) -> Self { + Self(Box::new(value)) + } +} + +#[repr(C)] +#[derive(Debug)] +struct udmabuf_create { + memfd: u32, + flags: u32, + offset: u64, + size: u64, +} + +const UDMABUF_FLAGS_CLOEXEC: u32 = 0x01; + +const UDMABUF_CREATE: IoctlNumber = _IOW::(b'u' as u64, 0x42) as IoctlNumber;