Various bugfixes

Switched the validation call 3 times. There's now a lot of bugs - will
need to debug later, esp. the gem_offset. It doesn't have a test as of
now and is marked as `unsafe` as it doesn't seem to function.
master
itycodes 2 weeks ago
parent 55612ec953
commit 9beed69f85

@ -12,6 +12,9 @@ pub struct DrmDeviceNode {
pub path: Option<OsString>, pub path: Option<OsString>,
} }
/// Owned GEM handle
/// Scoped to the device node file descriptor
/// (`drm_file->object_idr` in drm_file.h in the kernel)
#[derive(Debug)] #[derive(Debug)]
pub struct GemHandle<'a> { pub struct GemHandle<'a> {
pub handle: DrmGemHandle, pub handle: DrmGemHandle,
@ -82,10 +85,14 @@ impl GemHandle<'_> {
return unsafe { uapi::i915::close_gem_ref(self.node.fd.as_raw_fd(), &self.handle) }; return unsafe { uapi::i915::close_gem_ref(self.node.fd.as_raw_fd(), &self.handle) };
} }
// Should not be supported on Gen12+ GPUs
// However, the i915 uAPI actually supports it on Gen12+
// See https://patchwork.freedesktop.org/patch/325343/
pub fn get_tiling(&self) -> Result<GemTileInfo, GemIoctlError> { pub fn get_tiling(&self) -> Result<GemTileInfo, GemIoctlError> {
uapi::i915::gem_get_tiling(self.node.fd.as_raw_fd(), &self.handle).map(|info| GemTileInfo::from(info)) uapi::i915::gem_get_tiling(self.node.fd.as_raw_fd(), &self.handle).map(|info| GemTileInfo::from(info))
} }
// Only on iGPUs
pub fn get_caching(&self) -> Result<DrmCacheType, GemIoctlError> { pub fn get_caching(&self) -> Result<DrmCacheType, GemIoctlError> {
uapi::i915::gem_get_caching(self.node.fd.as_raw_fd(), &self.handle).map(|info| DrmCacheType::try_from(info).unwrap()) uapi::i915::gem_get_caching(self.node.fd.as_raw_fd(), &self.handle).map(|info| DrmCacheType::try_from(info).unwrap())
} }
@ -121,6 +128,10 @@ pub fn find_device() -> Option<Device> {
uapi::i915::find_node().and_then(|file| Device::from_path(file.path?)) uapi::i915::find_node().and_then(|file| Device::from_path(file.path?))
} }
pub fn find_all_devices() -> Vec<Device> {
uapi::i915::find_all_nodes().into_iter().filter_map(|file| Device::from_path(file.path.unwrap())).collect()
}
impl Device { impl Device {
pub fn from_fd(fd: OwnedFd) -> Option<Device> { pub fn from_fd(fd: OwnedFd) -> Option<Device> {
let driver = uapi::get_drm_version(fd.as_raw_fd())?; let driver = uapi::get_drm_version(fd.as_raw_fd())?;

@ -230,13 +230,16 @@ pub fn gem_get_tiling(fd: RawFd, handle: &DrmGemHandle) -> Result<DrmGemTileInfo
swizzle_mode: tiling.swizzle_mode, swizzle_mode: tiling.swizzle_mode,
phys_swizzle_mode: tiling.phys_swizzle_mode, phys_swizzle_mode: tiling.phys_swizzle_mode,
}; };
if res == 0 {
return Ok(tile_info);
}
let errno = *libc::__errno_location(); let errno = *libc::__errno_location();
match errno { match errno {
0 => Ok(tile_info), 0 => Ok(tile_info),
libc::ENOENT => Err(GemIoctlError::InvalidHandle), libc::ENOENT => Err(GemIoctlError::InvalidHandle),
libc::EPERM => Err(GemIoctlError::PermissionDenied), libc::EPERM => Err(GemIoctlError::PermissionDenied),
libc::EOPNOTSUPP => Err(GemIoctlError::UnsupportedOnDevice), libc::EOPNOTSUPP => Err(GemIoctlError::UnsupportedOnDevice),
_ => Err(GemIoctlError::Unknown(res)), _ => Err(GemIoctlError::Unknown(errno)),
} }
} }
} }
@ -259,6 +262,9 @@ pub fn gem_get_caching(fd: RawFd, handle: &DrmGemHandle) -> Result<u32, GemIoctl
caching: 0, caching: 0,
}; };
let res = libc::ioctl(fd, native::DRM_IOCTL_I915_GEM_GET_CACHING, &mut caching); let res = libc::ioctl(fd, native::DRM_IOCTL_I915_GEM_GET_CACHING, &mut caching);
if res == 0 {
return Ok(caching.caching);
}
let errno = *libc::__errno_location(); let errno = *libc::__errno_location();
match errno { match errno {
0 => Ok(caching.caching), 0 => Ok(caching.caching),
@ -266,14 +272,63 @@ pub fn gem_get_caching(fd: RawFd, handle: &DrmGemHandle) -> Result<u32, GemIoctl
libc::EPERM => Err(GemIoctlError::PermissionDenied), libc::EPERM => Err(GemIoctlError::PermissionDenied),
libc::ENODEV => Err(GemIoctlError::UnsupportedOnDevice), libc::ENODEV => Err(GemIoctlError::UnsupportedOnDevice),
libc::EOPNOTSUPP => Err(GemIoctlError::UnsupportedOnHandle), libc::EOPNOTSUPP => Err(GemIoctlError::UnsupportedOnHandle),
_ => Err(GemIoctlError::Unknown(res)), _ => Err(GemIoctlError::Unknown(errno)),
}
}
}
// TODO fix, broken rn
pub unsafe fn gem_offset(fd: RawFd, handle: &DrmGemHandle) -> Result<u64, GemIoctlError> {
unsafe {
let mut offset = native::drm_i915_gem_mmap_offset {
handle: handle.handle,
pad: 0,
offset: 0,
flags: native::I915_MMAP_OFFSET_WB as u64,
extensions: 0,
};
let res = libc::ioctl(fd, native::DRM_IOCTL_I915_GEM_MMAP_OFFSET, &mut offset);
if res == 0 {
return Ok(offset.offset);
}
let errno = *libc::__errno_location();
match errno {
0 => Ok(offset.offset),
libc::ENOENT => Err(GemIoctlError::InvalidHandle),
libc::EPERM => Err(GemIoctlError::PermissionDenied),
libc::ENODEV => Err(GemIoctlError::UnsupportedOnDevice),
libc::EOPNOTSUPP => Err(GemIoctlError::UnsupportedOnHandle),
_ => Err(GemIoctlError::Unknown(errno)),
}
}
}
pub fn gem_wait(fd: RawFd, handle: &DrmGemHandle, timeout_ns: i64) -> Result<(), GemIoctlError> {
unsafe {
let mut wait = native::drm_i915_gem_wait {
bo_handle: handle.handle,
flags: 0,
timeout_ns: timeout_ns,
};
let res = libc::ioctl(fd, native::DRM_IOCTL_I915_GEM_WAIT, &mut wait);
if res == 0 {
return Ok(());
}
let errno = *libc::__errno_location();
match errno {
0 => Ok(()),
libc::ENOENT => Err(GemIoctlError::InvalidHandle),
libc::EPERM => Err(GemIoctlError::PermissionDenied),
libc::ENODEV => Err(GemIoctlError::UnsupportedOnDevice),
libc::EOPNOTSUPP => Err(GemIoctlError::UnsupportedOnHandle),
_ => Err(GemIoctlError::Unknown(errno)),
} }
} }
} }
pub fn gem_is_valid(fd: RawFd, handle: &DrmGemHandle) -> Result<bool, GemIoctlError> { pub fn gem_is_valid(fd: RawFd, handle: &DrmGemHandle) -> Result<bool, GemIoctlError> {
let res = gem_has_tiling(fd, handle); let res = gem_wait(fd, handle, 1);
if res.is_ok() && res.unwrap() { if res.is_ok() {
return Ok(true); return Ok(true);
} }
@ -284,24 +339,36 @@ pub fn gem_is_valid(fd: RawFd, handle: &DrmGemHandle) -> Result<bool, GemIoctlEr
return Err(res.unwrap_err()); return Err(res.unwrap_err());
} }
pub fn find_node() -> Option<DrmDeviceNode> { pub fn find_all_nodes() -> Vec<DrmDeviceNode> {
let inodes = fs::read_dir("/dev/dri").ok()?; let inodes = fs::read_dir("/dev/dri").unwrap();
let mut nodes = Vec::new();
for inode in inodes { for inode in inodes {
let inode = inode.ok()?; let inode = inode.unwrap();
let path = inode.path(); let path = inode.path();
let path = path.to_str()?; let path = path.to_str().unwrap();
if path.starts_with("/dev/dri/renderD") { if path.starts_with("/dev/dri/renderD") {
let file = File::open(path).unwrap(); let file = File::open(path).unwrap();
let dev = get_drm_version(file.as_raw_fd()); let dev = get_drm_version(file.as_raw_fd());
if dev.is_some() && dev.unwrap().name.to_str().unwrap() == "i915" { if dev.is_some() && dev.unwrap().name.to_str().unwrap() == "i915" {
return Some(DrmDeviceNode { nodes.push(DrmDeviceNode {
fd: OwnedFd::from(file), fd: OwnedFd::from(file),
path: Some(OsString::from(path)), path: Some(OsString::from(path)),
}); });
} }
} }
} }
None nodes
}
pub fn find_node() -> Option<DrmDeviceNode> {
let mut nodes = find_all_nodes();
// Of course there's no proper .swap_remove method that returns Option<T>...
if nodes.is_empty() {
return None;
} else {
let node = nodes.swap_remove(0);
return Some(node);
}
} }
pub fn find_fd() -> Option<OwnedFd> { pub fn find_fd() -> Option<OwnedFd> {

@ -130,8 +130,9 @@ fn test_i915_gpu_gem_has_tiling() {
#[test] #[test]
fn test_i915_gpu_fences() { fn test_i915_gpu_fences() {
let device = i915::find_device().expect("Failed to find i915 device"); for device in i915::find_all_devices() {
let num_fences = device.get_param(native::I915_PARAM_NUM_FENCES_AVAIL).expect("Failed to get fences"); let _num_fences = device.get_param(native::I915_PARAM_NUM_FENCES_AVAIL).expect("Failed to get fences");
println!("Number of fences: {}", num_fences); // TODO figure out how to skip on DG
assert!(num_fences > 0); //assert!(num_fences > 0);
}
} }

@ -9,18 +9,24 @@ use std::thread;
#[test] #[test]
fn test_i915_uapi_get_version() { fn test_i915_uapi_get_version() {
let node = i915::find_node().expect("Failed to find i915 fd"); let nodes = i915::find_all_nodes();
let drm_version = uapi::get_drm_version(node.fd.as_raw_fd()).expect("Failed to get drm version"); assert!(nodes.len() > 0);
assert_eq!(drm_version.name.to_str().unwrap(), "i915"); for node in nodes {
assert_eq!(drm_version.desc.to_str().unwrap(), "Intel Graphics"); let drm_version = uapi::get_drm_version(node.fd.as_raw_fd()).expect("Failed to get drm version");
assert_eq!(drm_version.name.to_str().unwrap(), "i915");
assert_eq!(drm_version.desc.to_str().unwrap(), "Intel Graphics");
}
} }
#[test] #[test]
fn test_i915_uapi_get_engines() { fn test_i915_uapi_get_engines() {
let node = i915::find_node().expect("Failed to find i915 fd"); let nodes = i915::find_all_nodes();
let _engines = i915::get_engines(node.fd.as_raw_fd()).expect("Failed to get engines"); assert!(nodes.len() > 0);
let fail_fd = File::open("/dev/null").expect("Failed to open /dev/null"); for node in nodes {
assert!(i915::get_engines(fail_fd.as_raw_fd()).is_none()); let _engines = i915::get_engines(node.fd.as_raw_fd()).expect("Failed to get engines");
let fail_fd = File::open("/dev/null").expect("Failed to open /dev/null");
assert!(i915::get_engines(fail_fd.as_raw_fd()).is_none());
}
} }
#[test] #[test]
@ -32,15 +38,19 @@ fn test_i915_uapi_get_param_fail() {
#[test] #[test]
fn test_i915_uapi_native_engine_info() { fn test_i915_uapi_native_engine_info() {
let node = i915::find_node().expect("Failed to find i915 fd"); let nodes = i915::find_all_nodes();
let engines = i915::get_engines(node.fd.as_raw_fd()).expect("Failed to get engines"); assert!(nodes.len() > 0);
for engine in engines { for node in nodes {
let native_engine = engine.to_native(); let engines = i915::get_engines(node.fd.as_raw_fd()).expect("Failed to get engines");
let from_native = i915::EngineInfo::from_native(native_engine); for engine in engines {
assert_eq!(&engine, &from_native); let native_engine = engine.to_native();
let from_native = i915::EngineInfo::from_native(native_engine);
assert_eq!(&engine, &from_native);
}
} }
} }
// TODO
#[test] #[test]
fn test_i915_uapi_find_fd() { fn test_i915_uapi_find_fd() {
let fd = i915::find_fd().expect("Failed to find i915 fd"); let fd = i915::find_fd().expect("Failed to find i915 fd");
@ -51,55 +61,125 @@ fn test_i915_uapi_find_fd() {
#[test] #[test]
fn test_i915_uapi_get_context_param() { fn test_i915_uapi_get_context_param() {
let node = i915::find_node().expect("Failed to find i915 fd"); let nodes = i915::find_all_nodes();
// Mesa uses context id of 0 for init so it's surely okay, right? assert!(nodes.len() > 0);
let param = i915::get_context_param(node.fd.as_raw_fd(), for node in nodes {
0, // Mesa uses context id of 0 for init so it's surely okay, right?
native::I915_CONTEXT_PARAM_GTT_SIZE as u32) let param = i915::get_context_param(node.fd.as_raw_fd(),
.expect("Failed to get context param"); 0,
assert!(param > 0); native::I915_CONTEXT_PARAM_GTT_SIZE as u32)
.expect("Failed to get context param");
assert!(param > 0);
}
} }
#[test] #[test]
fn test_i915_uapi_gem_lifecycle() { fn test_i915_uapi_gem_lifecycle() {
let node = i915::find_node().expect("Failed to find i915 fd"); let nodes = i915::find_all_nodes();
let gem = i915::make_gem(node.fd.as_raw_fd(), 4096).expect("Failed to make gem"); assert!(nodes.len() > 0);
assert!(gem.handle > 0); for node in nodes {
assert!(i915::gem_is_valid(node.fd.as_raw_fd(), &gem).unwrap()); let gem = i915::make_gem(node.fd.as_raw_fd(), 4096).expect("Failed to make gem");
let tmp = i915::DrmGemHandle { handle: gem.handle }; assert!(gem.handle > 0);
i915::close_gem(node.fd.as_raw_fd(), tmp).expect("Failed to close gem"); assert!(i915::gem_is_valid(node.fd.as_raw_fd(), &gem).unwrap());
assert!(!i915::gem_is_valid(node.fd.as_raw_fd(), &gem).unwrap()); let tmp = i915::DrmGemHandle { handle: gem.handle };
let invalid_fd = File::open("/dev/null").expect("Failed to open /dev/null"); i915::close_gem(node.fd.as_raw_fd(), tmp).expect("Failed to close gem");
assert!(i915::make_gem(invalid_fd.as_raw_fd(), 4096).is_none()); assert!(!i915::gem_is_valid(node.fd.as_raw_fd(), &gem).unwrap());
let invalid_fd = File::open("/dev/null").expect("Failed to open /dev/null");
assert!(i915::make_gem(invalid_fd.as_raw_fd(), 4096).is_none());
}
} }
#[test] #[test]
fn test_i915_uapi_gem_tiling() { fn test_i915_uapi_gem_tiling() {
let node = i915::find_node().expect("Failed to find i915 fd"); let nodes = i915::find_all_nodes();
let gem = i915::make_gem(node.fd.as_raw_fd(), 4096).expect("Failed to make gem"); assert!(nodes.len() > 0);
// TODO figure out which devices this holds for for node in nodes {
assert!(i915::gem_has_tiling(node.fd.as_raw_fd(), &gem).is_ok_and(|e| e == true)); let gem = i915::make_gem(node.fd.as_raw_fd(), 4096).expect("Failed to make gem");
i915::close_gem(node.fd.as_raw_fd(), gem).expect("Failed to close gem"); // TODO figure out which devices this holds for
let tiling = i915::gem_get_tiling(node.fd.as_raw_fd(), &gem);
if tiling.is_err() {
// Unsupported on DG
continue;
}
assert!(i915::gem_has_tiling(node.fd.as_raw_fd(), &gem).is_ok_and(|e| e == true));
i915::close_gem(node.fd.as_raw_fd(), gem).expect("Failed to close gem");
}
} }
#[test] #[test]
fn test_i915_uapi_gem_caching() { fn test_i915_uapi_gem_caching() {
let node = i915::find_node().expect("Failed to find i915 fd"); let nodes = i915::find_all_nodes();
let gem = i915::make_gem(node.fd.as_raw_fd(), 4096).expect("Failed to make gem"); assert!(nodes.len() > 0);
let caching = i915::gem_get_caching(node.fd.as_raw_fd(), &gem).expect("Failed to get caching"); for node in nodes {
assert!(caching > 0); let gem = i915::make_gem(node.fd.as_raw_fd(), 4096).expect("Failed to make gem");
i915::close_gem(node.fd.as_raw_fd(), gem).expect("Failed to close gem"); let caching = i915::gem_get_caching(node.fd.as_raw_fd(), &gem);
if caching.is_err() {
// Unsupported on DG
continue;
}
let caching = caching.unwrap();
assert!(caching > 0);
i915::close_gem(node.fd.as_raw_fd(), gem).expect("Failed to close gem");
}
} }
#[test] #[test]
fn test_i915_uapi_gem_thread() { fn test_i915_uapi_gem_thread() {
let node = i915::find_node().expect("Failed to find i915 fd"); let nodes = i915::find_all_nodes();
let gem = i915::make_gem(node.fd.as_raw_fd(), 4096).expect("Failed to make gem"); assert!(nodes.len() > 0);
thread::scope(|s| { for node in nodes {
let handle = s.spawn(|| { let gem = i915::make_gem(node.fd.as_raw_fd(), 4096).expect("Failed to make gem");
thread::scope(|s| {
let handle = s.spawn(|| {
assert!(i915::gem_is_valid(node.fd.as_raw_fd(), &gem).unwrap());
});
assert!(i915::gem_is_valid(node.fd.as_raw_fd(), &gem).unwrap()); assert!(i915::gem_is_valid(node.fd.as_raw_fd(), &gem).unwrap());
handle.join().expect("Failed to join thread");
}); });
assert!(i915::gem_is_valid(node.fd.as_raw_fd(), &gem).unwrap()); }
handle.join().expect("Failed to join thread"); }
});
#[test]
fn test_i915_uapi_gem_fork_parent() {
let nodes = i915::find_all_nodes();
assert!(nodes.len() > 0);
for node in nodes {
let gem = i915::make_gem(node.fd.as_raw_fd(), 4096).expect("Failed to make gem");
let pid = unsafe { libc::fork() };
if pid == 0 {
if i915::gem_is_valid(node.fd.as_raw_fd(), &gem).unwrap() {
std::process::exit(0);
} else {
std::process::exit(-1);
}
} else {
assert!(i915::gem_is_valid(node.fd.as_raw_fd(), &gem).unwrap());
let mut status = 0;
unsafe { libc::waitpid(pid, &mut status, 0) };
assert_eq!(status, 0);
}
}
} }
// TODO: Fix this test
// Shared mem needs to be used to communicate the gem handle between parent and child
// Test both a shared fd and separate fd
// #[test]
// fn test_i915_uapi_gem_fork_child() {
// let pid = unsafe { libc::fork() };
// if pid == 0 {
// let node = i915::find_node().expect("Failed to find i915 fd");
// let gem = i915::make_gem(node.fd.as_raw_fd(), 4096).expect("Failed to make gem");
// if i915::gem_is_valid(node.fd.as_raw_fd(), &gem).unwrap() {
// println!("handle 1: {}", gem.handle);
// std::process::exit(gem.handle as i32);
// } else {
// std::process::exit(-1);
// }
// } else {
// let node = i915::find_node().expect("Failed to find i915 fd");
// let mut status = 0;
// unsafe { libc::waitpid(pid, &mut status, 0) };
// assert!(i915::gem_is_valid(node.fd.as_raw_fd(), &DrmGemHandle {handle: 1}).unwrap());
// }
// }
Loading…
Cancel
Save