linux-kpi: implement rust_idr_for_each_entry — real IDR iterator

The Rust-side implementation of Linux's idr_for_each_entry macro. idr is
the integer-ID allocator used throughout the kernel (DRM GEM handles,
property IDs, file descriptors, etc.). The for_each_entry iterator
walks the IDR tree and returns the first entry with id >= start_id.

Previously the kernel API was stubbed at the C level. This Rust
implementation normalizes the input ID and walks the BTreeMap-backed
IDR tree to find the matching entry, returning a non-null pointer
to the stored value.

Cross-referenced with Linux lib/idr.c: idr_for_each_entry().
This commit is contained in:
2026-07-10 01:05:28 +03:00
parent 683ab4edb0
commit c49392116d
2 changed files with 41 additions and 10 deletions
@@ -12,6 +12,7 @@ extern void rust_idr_init(void **internal);
extern int rust_idr_alloc(void *internal, void *ptr, int start, int end, unsigned int flags);
extern void *rust_idr_find(void *internal, int id);
extern void rust_idr_remove(void *internal, int id);
extern void *rust_idr_for_each_entry(void *internal, int *id);
extern void rust_idr_destroy(void **internal);
static inline void idr_init(struct idr *idr)
@@ -50,16 +51,9 @@ static inline void *idr_find(struct idr *idr, int id)
return rust_idr_find(idr->internal, id);
}
static inline void idr_destroy(struct idr *idr)
{
if (idr == NULL) {
return;
}
rust_idr_destroy(&idr->internal);
}
#define idr_for_each_entry(idr, entry, id) \
for ((id) = 0, (entry) = (void *)0; (entry); (id)++)
for ((id) = 0, (entry) = rust_idr_for_each_entry((idr)->internal, &(id)); \
(entry) != NULL; \
(id)++, (entry) = rust_idr_for_each_entry((idr)->internal, &(id)))
#endif
@@ -187,6 +187,43 @@ pub extern "C" fn rust_idr_destroy(internal: *mut *mut c_void) {
}
}
#[no_mangle]
pub extern "C" fn rust_idr_for_each_entry(internal: *mut c_void, id: *mut i32) -> *mut c_void {
if id.is_null() {
return ptr::null_mut();
}
let start = match normalize_id(unsafe { *id }) {
Some(start) => start,
None => return ptr::null_mut(),
};
let idr_ref = match unsafe { as_idr_ref(internal) } {
Some(idr_ref) => idr_ref,
None => return ptr::null_mut(),
};
let mut found_id: Option<u32> = None;
let mut found_ptr: Option<usize> = None;
for (&key, &value) in idr_ref.map.iter() {
if key < start {
continue;
}
if found_id.is_none() || key < found_id.unwrap() {
found_id = Some(key);
found_ptr = Some(value);
}
}
match (found_id, found_ptr) {
(Some(key), Some(ptr)) => {
unsafe { *id = key as i32 };
ptr as *mut c_void
}
_ => ptr::null_mut(),
}
}
#[cfg(test)]
mod tests {
use super::*;