Skip to content

Commit 1e6af0f

Browse files
Sven Van Asbroeckojeda
Sven Van Asbroeck
authored andcommitted
rust: error: Add a helper to convert a C ERR_PTR to a Result
Some kernel C API functions return a pointer which embeds an optional `errno`. Callers are supposed to check the returned pointer with `IS_ERR()` and if this returns `true`, retrieve the `errno` using `PTR_ERR()`. Create a Rust helper function to implement the Rust equivalent: transform a `*mut T` to `Result<*mut T>`. Lina: Imported from rust-for-linux/linux, with subsequent refactoring and contributions squashed in and attributed below. Renamed the function to from_err_ptr(). Co-developed-by: Boqun Feng <[email protected]> Signed-off-by: Boqun Feng <[email protected]> Co-developed-by: Miguel Ojeda <[email protected]> Signed-off-by: Miguel Ojeda <[email protected]> Co-developed-by: Fox Chen <[email protected]> Signed-off-by: Fox Chen <[email protected]> Co-developed-by: Gary Guo <[email protected]> Signed-off-by: Gary Guo <[email protected]> Signed-off-by: Sven Van Asbroeck <[email protected]> Reviewed-by: Martin Rodriguez Reboredo <[email protected]> Signed-off-by: Asahi Lina <[email protected]> Link: https://lore.kernel.org/r/[email protected] [ Add a removal of `#[allow(dead_code)]`. ] Signed-off-by: Miguel Ojeda <[email protected]>
1 parent 6378447 commit 1e6af0f

File tree

2 files changed

+61
-1
lines changed

2 files changed

+61
-1
lines changed

rust/helpers.c

+12
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,18 @@ __force void *rust_helper_ERR_PTR(long err)
5353
}
5454
EXPORT_SYMBOL_GPL(rust_helper_ERR_PTR);
5555

56+
bool rust_helper_IS_ERR(__force const void *ptr)
57+
{
58+
return IS_ERR(ptr);
59+
}
60+
EXPORT_SYMBOL_GPL(rust_helper_IS_ERR);
61+
62+
long rust_helper_PTR_ERR(__force const void *ptr)
63+
{
64+
return PTR_ERR(ptr);
65+
}
66+
EXPORT_SYMBOL_GPL(rust_helper_PTR_ERR);
67+
5668
/*
5769
* We use `bindgen`'s `--size_t-is-usize` option to bind the C `size_t` type
5870
* as the Rust `usize` type, so we can use it in contexts where Rust

rust/kernel/error.rs

+49-1
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,6 @@ impl Error {
9696
/// # Safety
9797
///
9898
/// `errno` must be within error code range (i.e. `>= -MAX_ERRNO && < 0`).
99-
#[allow(dead_code)]
10099
unsafe fn from_errno_unchecked(errno: core::ffi::c_int) -> Error {
101100
// INVARIANT: The contract ensures the type invariant
102101
// will hold.
@@ -189,3 +188,52 @@ pub fn to_result(err: core::ffi::c_int) -> Result {
189188
Ok(())
190189
}
191190
}
191+
192+
/// Transform a kernel "error pointer" to a normal pointer.
193+
///
194+
/// Some kernel C API functions return an "error pointer" which optionally
195+
/// embeds an `errno`. Callers are supposed to check the returned pointer
196+
/// for errors. This function performs the check and converts the "error pointer"
197+
/// to a normal pointer in an idiomatic fashion.
198+
///
199+
/// # Examples
200+
///
201+
/// ```ignore
202+
/// # use kernel::from_err_ptr;
203+
/// # use kernel::bindings;
204+
/// fn devm_platform_ioremap_resource(
205+
/// pdev: &mut PlatformDevice,
206+
/// index: u32,
207+
/// ) -> Result<*mut core::ffi::c_void> {
208+
/// // SAFETY: FFI call.
209+
/// unsafe {
210+
/// from_err_ptr(bindings::devm_platform_ioremap_resource(
211+
/// pdev.to_ptr(),
212+
/// index,
213+
/// ))
214+
/// }
215+
/// }
216+
/// ```
217+
// TODO: Remove `dead_code` marker once an in-kernel client is available.
218+
#[allow(dead_code)]
219+
pub(crate) fn from_err_ptr<T>(ptr: *mut T) -> Result<*mut T> {
220+
// CAST: Casting a pointer to `*const core::ffi::c_void` is always valid.
221+
let const_ptr: *const core::ffi::c_void = ptr.cast();
222+
// SAFETY: The FFI function does not deref the pointer.
223+
if unsafe { bindings::IS_ERR(const_ptr) } {
224+
// SAFETY: The FFI function does not deref the pointer.
225+
let err = unsafe { bindings::PTR_ERR(const_ptr) };
226+
// CAST: If `IS_ERR()` returns `true`,
227+
// then `PTR_ERR()` is guaranteed to return a
228+
// negative value greater-or-equal to `-bindings::MAX_ERRNO`,
229+
// which always fits in an `i16`, as per the invariant above.
230+
// And an `i16` always fits in an `i32`. So casting `err` to
231+
// an `i32` can never overflow, and is always valid.
232+
//
233+
// SAFETY: `IS_ERR()` ensures `err` is a
234+
// negative value greater-or-equal to `-bindings::MAX_ERRNO`.
235+
#[allow(clippy::unnecessary_cast)]
236+
return Err(unsafe { Error::from_errno_unchecked(err as core::ffi::c_int) });
237+
}
238+
Ok(ptr)
239+
}

0 commit comments

Comments
 (0)