udmabuf: move allocator implementation into workspace crate
This commit is contained in:
parent
81a718aa74
commit
1558666601
5 changed files with 356 additions and 324 deletions
14
Cargo.lock
generated
14
Cargo.lock
generated
|
|
@ -750,6 +750,7 @@ dependencies = [
|
||||||
"jay-toml-config",
|
"jay-toml-config",
|
||||||
"jay-tracy",
|
"jay-tracy",
|
||||||
"jay-tree-types",
|
"jay-tree-types",
|
||||||
|
"jay-udmabuf",
|
||||||
"jay-units",
|
"jay-units",
|
||||||
"jay-utils",
|
"jay-utils",
|
||||||
"jay-video-types",
|
"jay-video-types",
|
||||||
|
|
@ -1054,6 +1055,19 @@ dependencies = [
|
||||||
"linearize",
|
"linearize",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jay-udmabuf"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"jay-allocator",
|
||||||
|
"jay-formats",
|
||||||
|
"jay-utils",
|
||||||
|
"jay-video-types",
|
||||||
|
"log",
|
||||||
|
"thiserror",
|
||||||
|
"uapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jay-units"
|
name = "jay-units"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,7 @@ members = [
|
||||||
"allocator",
|
"allocator",
|
||||||
"output-schedule",
|
"output-schedule",
|
||||||
"drm-feedback",
|
"drm-feedback",
|
||||||
|
"udmabuf",
|
||||||
"pango",
|
"pango",
|
||||||
"libinput",
|
"libinput",
|
||||||
"toml-config",
|
"toml-config",
|
||||||
|
|
@ -101,6 +102,7 @@ jay-clientmem = { version = "0.1.0", path = "clientmem" }
|
||||||
jay-allocator = { version = "0.1.0", path = "allocator" }
|
jay-allocator = { version = "0.1.0", path = "allocator" }
|
||||||
jay-output-schedule = { version = "0.1.0", path = "output-schedule" }
|
jay-output-schedule = { version = "0.1.0", path = "output-schedule" }
|
||||||
jay-drm-feedback = { version = "0.1.0", path = "drm-feedback" }
|
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-pango = { version = "0.1.0", path = "pango" }
|
||||||
jay-libinput = { version = "0.1.0", path = "libinput" }
|
jay-libinput = { version = "0.1.0", path = "libinput" }
|
||||||
|
|
||||||
|
|
|
||||||
325
src/udmabuf.rs
325
src/udmabuf.rs
|
|
@ -1,324 +1 @@
|
||||||
use {
|
pub use jay_udmabuf::*;
|
||||||
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;
|
|
||||||
|
|
|
||||||
15
udmabuf/Cargo.toml
Normal file
15
udmabuf/Cargo.toml
Normal 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
324
udmabuf/src/lib.rs
Normal 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;
|
||||||
Loading…
Add table
Add a link
Reference in a new issue