1
0
Fork 0
forked from wry/wry

udmabuf: move allocator implementation into workspace crate

This commit is contained in:
kossLAN 2026-05-29 11:53:32 -04:00
parent 81a718aa74
commit 1558666601
No known key found for this signature in database
5 changed files with 356 additions and 324 deletions

14
Cargo.lock generated
View file

@ -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"

View file

@ -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" }

View file

@ -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<Option<Option<Rc<Udmabuf>>>>,
}
impl UdmabufHolder {
pub fn get(&self) -> Option<Rc<Udmabuf>> {
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<Self, UdmabufError> {
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<OwnedFd, UdmabufError> {
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<DmaBuf, UdmabufError> {
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<UdmabufBo, UdmabufError> {
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<Rc<dyn BufferObject>, 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<Rc<dyn BufferObject>, 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<Self>) -> Result<Box<dyn MappedBuffer>, AllocatorError> {
self.map_write()
}
fn map_write(self: Rc<Self>) -> Result<Box<dyn MappedBuffer>, 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<UdmabufBo>,
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<UdmabufError> 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::<udmabuf_create>(b'u' as u64, 0x42) as IoctlNumber;
pub use jay_udmabuf::*;

15
udmabuf/Cargo.toml Normal file
View file

@ -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"

324
udmabuf/src/lib.rs Normal file
View file

@ -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<Option<Option<Rc<Udmabuf>>>>,
}
impl UdmabufHolder {
pub fn get(&self) -> Option<Rc<Udmabuf>> {
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<Self, UdmabufError> {
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<OwnedFd, UdmabufError> {
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<DmaBuf, UdmabufError> {
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<UdmabufBo, UdmabufError> {
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<Rc<dyn BufferObject>, 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<Rc<dyn BufferObject>, 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<Self>) -> Result<Box<dyn MappedBuffer>, AllocatorError> {
self.map_write()
}
fn map_write(self: Rc<Self>) -> Result<Box<dyn MappedBuffer>, 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<UdmabufBo>,
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<UdmabufError> 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::<udmabuf_create>(b'u' as u64, 0x42) as IoctlNumber;