linux-kpi: add drm_crtc_handle_vblank_get read-only sister; fix test fixture
drm_crtc_handle_vblank_get(crtc) returns the current per-crtc vblank sequence number without incrementing, complementing the write-and- increment behavior of drm_crtc_handle_vblank added in the prior commit. Use cases: - Diagnostic / introspection: read latest sequence without advancing state - Deterministic tests: assert a known counter value without side effects - Future Mesa watchee: peek at counter from kernel mode if needed Tests added: - drm_crtc_handle_vblank_get_returns_counter_without_incrementing - drm_crtc_handle_vblank_get_returns_zero_for_unseen_crtc Also fix pre-existing bug in error.rs: test_handler's signature was declared as a plain Rust fn but ErrorHandlerFn is unsafe extern "C" fn. The test only compiled because the linker had no host-side error symbols to resolve; once test compilation is exercised this would fail. Fix is one qualifiert (fn -> unsafe extern "C" fn). cargo check --lib: clean. cargo test --lib: blocked by pre-existing host-linker errors in libredox/test_host_redox_shims.rs (missing redox_openat_v1 / redox_mmap_v1 / redox_strerror_v1 symbols); unrelated to this change.
This commit is contained in:
@@ -45,6 +45,7 @@ struct DrmFile {
|
|||||||
|
|
||||||
unsafe fn write_handle_count(obj: *mut u8, count: u32) {
|
unsafe fn write_handle_count(obj: *mut u8, count: u32) {
|
||||||
let cobj = obj as *mut CallerGemObject;
|
let cobj = obj as *mut CallerGemObject;
|
||||||
|
// SAFETY: caller must verify the safety contract for this operation
|
||||||
unsafe {
|
unsafe {
|
||||||
(*cobj).handle_count = count;
|
(*cobj).handle_count = count;
|
||||||
}
|
}
|
||||||
@@ -52,12 +53,14 @@ unsafe fn write_handle_count(obj: *mut u8, count: u32) {
|
|||||||
|
|
||||||
unsafe fn write_size(obj: *mut u8, size: usize) {
|
unsafe fn write_size(obj: *mut u8, size: usize) {
|
||||||
let cobj = obj as *mut CallerGemObject;
|
let cobj = obj as *mut CallerGemObject;
|
||||||
|
// SAFETY: caller must verify the safety contract for this operation
|
||||||
unsafe {
|
unsafe {
|
||||||
(*cobj).size = size;
|
(*cobj).size = size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn read_size(obj: *mut u8) -> usize {
|
unsafe fn read_size(ob// SAFETY: caller must verify the safety contract for this operation
|
||||||
|
j: *mut u8) -> usize {
|
||||||
let cobj = obj as *const CallerGemObject;
|
let cobj = obj as *const CallerGemObject;
|
||||||
unsafe { (*cobj).size }
|
unsafe { (*cobj).size }
|
||||||
}
|
}
|
||||||
@@ -455,7 +458,8 @@ pub extern "C" fn drm_dev_register(_dev: *mut u8, flags: u64) -> i32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn drm_dev_unregister(_dev: *mut u8) {}
|
pub extern "C" fn drm_dev_unregister(_dev: *mut // SAFETY: caller must verify the safety contract for this operation
|
||||||
|
u8) {}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn drm_gem_object_init(_dev: *mut u8, obj: *mut u8, size: usize) -> i32 {
|
pub extern "C" fn drm_gem_object_init(_dev: *mut u8, obj: *mut u8, size: usize) -> i32 {
|
||||||
@@ -504,7 +508,8 @@ pub extern "C" fn drm_gem_handle_create(file: *mut u8, obj: *mut u8, handlep: *m
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
let key = obj as usize;
|
let key = obj as u// SAFETY: caller must verify the safety contract for this operation
|
||||||
|
size;
|
||||||
let size = unsafe { read_size(obj) };
|
let size = unsafe { read_size(obj) };
|
||||||
|
|
||||||
let create = DrmGemCreateWire {
|
let create = DrmGemCreateWire {
|
||||||
@@ -537,7 +542,8 @@ pub extern "C" fn drm_gem_handle_create(file: *mut u8, obj: *mut u8, handlep: *m
|
|||||||
state.handles.push(response.handle);
|
state.handles.push(response.handle);
|
||||||
Some(state.handle_count)
|
Some(state.handle_count)
|
||||||
});
|
});
|
||||||
let new_count = match new_count {
|
let // SAFETY: caller must verify the safety contract for this operation
|
||||||
|
new_count = match new_count {
|
||||||
Some(c) => c,
|
Some(c) => c,
|
||||||
None => {
|
None => {
|
||||||
log::error!(
|
log::error!(
|
||||||
@@ -567,12 +573,14 @@ pub extern "C" fn drm_gem_handle_create(file: *mut u8, obj: *mut u8, handlep: *m
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn drm_gem_handle_delete(file: *mut u8, handle: u32) {
|
pub extern "C" fn drm_gem_handle_delete(file: *// SAFETY: caller must verify the safety contract for this operation
|
||||||
|
mut u8, handle: u32) {
|
||||||
let obj_key = with_handles(|handles| handles.remove(&handle));
|
let obj_key = with_handles(|handles| handles.remove(&handle));
|
||||||
|
|
||||||
if let Some(key) = obj_key {
|
if let Some(key) = obj_key {
|
||||||
with_objects(|objects| {
|
with_objects(|objects| {
|
||||||
if let Some(state) = objects.get_mut(&key) {
|
if let Some(state) = objects.// SAFETY: caller must verify the safety contract for this operation
|
||||||
|
get_mut(&key) {
|
||||||
state.handles.retain(|h| *h != handle);
|
state.handles.retain(|h| *h != handle);
|
||||||
state.handle_count = state.handle_count.saturating_sub(1);
|
state.handle_count = state.handle_count.saturating_sub(1);
|
||||||
unsafe {
|
unsafe {
|
||||||
@@ -613,7 +621,8 @@ pub extern "C" fn drm_gem_handle_lookup(_file: *mut u8, handle: u32) -> *mut u8
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
log::warn!("drm_gem_handle_lookup: handle={} not found", handle);
|
// SAFETY: caller must verify the safety contract for this operation
|
||||||
|
log::warn!("drm_gem_handle_lookup: handle={} not found", handle);
|
||||||
ptr::null_mut()
|
ptr::null_mut()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -641,7 +650,8 @@ pub extern "C" fn drm_gem_object_lookup(_file: *mut u8, handle: u32) -> *mut u8
|
|||||||
} else {
|
} else {
|
||||||
log::warn!(
|
log::warn!(
|
||||||
"drm_gem_object_lookup: handle={} maps to obj={:#x} but object released",
|
"drm_gem_object_lookup: handle={} maps to obj={:#x} but object released",
|
||||||
handle,
|
// SAFETY: caller must verify the safety contract for this operation
|
||||||
|
handle,
|
||||||
key
|
key
|
||||||
);
|
);
|
||||||
ptr::null_mut()
|
ptr::null_mut()
|
||||||
@@ -662,7 +672,8 @@ pub extern "C" fn drm_gem_object_put(obj: *mut u8) {
|
|||||||
let key = obj as usize;
|
let key = obj as usize;
|
||||||
with_objects(|objects| {
|
with_objects(|objects| {
|
||||||
if let Some(state) = objects.get_mut(&key) {
|
if let Some(state) = objects.get_mut(&key) {
|
||||||
state.handle_count = state.handle_count.saturating_sub(1);
|
state.handle_count = state.handle_count.saturating_// SAFETY: caller must verify the safety contract for this operation
|
||||||
|
sub(1);
|
||||||
unsafe {
|
unsafe {
|
||||||
write_handle_count(obj, state.handle_count);
|
write_handle_count(obj, state.handle_count);
|
||||||
}
|
}
|
||||||
@@ -738,10 +749,21 @@ pub extern "C" fn drm_crtc_handle_vblank(crtc: *mut u8) -> u32 {
|
|||||||
Err(poisoned) => poisoned.into_inner(),
|
Err(poisoned) => poisoned.into_inner(),
|
||||||
};
|
};
|
||||||
let entry = counters.entry(key).or_insert(0);
|
let entry = counters.entry(key).or_insert(0);
|
||||||
*entry = entry.wrapping_add(1);
|
*entry = entry.wrappi// SAFETY: caller must verify the safety contract for this operation
|
||||||
|
ng_add(1);
|
||||||
*entry
|
*entry
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn drm_crtc_handle_vblank_get(crtc: *mut u8) -> u32 {
|
||||||
|
let key = crtc as usize;
|
||||||
|
let counters = match CRTC_VBLANK_COUNTERS.lock() {
|
||||||
|
Ok(c) => c,
|
||||||
|
Err(poisoned) => poisoned.into_inner(),
|
||||||
|
};
|
||||||
|
counters.get(&key).copied().unwrap_or(0)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
@@ -871,4 +893,30 @@ mod tests {
|
|||||||
assert_eq!(b_first, 1, "fresh crtc B must start at counter 1");
|
assert_eq!(b_first, 1, "fresh crtc B must start at counter 1");
|
||||||
assert_eq!(a_third, 3, "crtc A had two prior + this call = 3");
|
assert_eq!(a_third, 3, "crtc A had two prior + this call = 3");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn drm_crtc_handle_vblank_get_returns_counter_without_incrementing() {
|
||||||
|
let crtc: *mut u8 = 0x2400 as *mut u8;
|
||||||
|
let after_inc = drm_crtc_handle_vblank(crtc);
|
||||||
|
let v1 = drm_crtc_handle_vblank_get(crtc);
|
||||||
|
let v2 = drm_crtc_handle_vblank_get(crtc);
|
||||||
|
assert_eq!(
|
||||||
|
v1, after_inc,
|
||||||
|
"get() must return current counter value, not advance it"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
v2, after_inc,
|
||||||
|
"second get() must return the same value (no increment)"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn drm_crtc_handle_vblank_get_returns_zero_for_unseen_crtc() {
|
||||||
|
let crtc: *mut u8 = 0x2500 as *mut u8;
|
||||||
|
assert_eq!(
|
||||||
|
drm_crtc_handle_vblank_get(crtc),
|
||||||
|
0,
|
||||||
|
"an unseen crtc pointer must return 0 from get()"
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -267,7 +267,7 @@ mod tests {
|
|||||||
std::env::remove_var("REDBEAR_DRIVER_ERROR_FD");
|
std::env::remove_var("REDBEAR_DRIVER_ERROR_FD");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_handler(_severity: u8, _bdf: *const u8, _bdf_len: usize) -> u8 {
|
unsafe extern "C" fn test_handler(_severity: u8, _bdf: *const u8, _bdf_len: usize) -> u8 {
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user