Skip to content

Commit

Permalink
rust/kernel: use zero-cost method to construct certain Errors
Browse files Browse the repository at this point in the history
In a previous PR, the type invariant for `Error` is enforced using
a runtime check. This is non-zero cost.

However we may decide to trust the return value of certain kernel C
functions. In such cases, no runtime check is required to enforce the
type invariant. So we can return to zero-cost.

This patch removes invariant checks from kernel C functions that
return a positive value on success, or a non-zero errno on failure.

Signed-off-by: Sven Van Asbroeck <[email protected]>
  • Loading branch information
Sven Van Asbroeck committed Jun 3, 2021
1 parent 4f06a6d commit fa390c4
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 4 deletions.
30 changes: 30 additions & 0 deletions rust/kernel/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -290,3 +290,33 @@ pub(crate) unsafe fn from_kernel_int_result(retval: c_types::c_int) -> Result {
errno => Err(Error::from_kernel_errno_unchecked(errno)),
}
}

/// Transform a kernel integer result to a [`Result<c_types::c_uint>`].
///
/// Some kernel C API functions return a result in the form of an integer:
/// a positive value if ok, a negative errno on error. This function converts
/// such a return value into an idiomatic [`Result<c_types::c_uint>`].
///
/// # Safety
///
/// `retval` must be non-negative or a valid negative errno (i.e. `retval` must
/// be in `[-MAX_ERRNO..]`).
///
/// # Examples
///
/// ```rust,no_run
/// let fd = unsafe { bindings::get_unused_fd_flags(flags) };
/// // SAFETY: `bindings::get_unused_fd_flags()` returns a non-negative
/// // `fd` on success, or a valid negative `errno` on error.
/// let fd = unsafe { from_kernel_int_result_uint(fd)? };
/// ```
pub(crate) unsafe fn from_kernel_int_result_uint(
retval: c_types::c_int,
) -> Result<c_types::c_uint> {
match retval {
// CAST: a non-negative `c_types::c_int` always fits in a `c_types::c_uint`.
success if success >= 0 => Ok(success as c_types::c_uint),
// SAFETY: Safety above and match arm guarantee that `errno` is a valid negative `errno`.
errno => Err(Error::from_kernel_errno_unchecked(errno)),
}
}
12 changes: 8 additions & 4 deletions rust/kernel/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@
//! C headers: [`include/linux/fs.h`](../../../../include/linux/fs.h) and
//! [`include/linux/file.h`](../../../../include/linux/file.h)
use crate::{bindings, error::Error, Result};
use crate::{
bindings,
error::{from_kernel_int_result_uint, Error},
Result,
};
use core::{mem::ManuallyDrop, ops::Deref};

/// Wraps the kernel's `struct file`.
Expand Down Expand Up @@ -96,9 +100,9 @@ impl FileDescriptorReservation {
/// Creates a new file descriptor reservation.
pub fn new(flags: u32) -> Result<Self> {
let fd = unsafe { bindings::get_unused_fd_flags(flags) };
if fd < 0 {
return Err(Error::from_kernel_errno(fd));
}
// SAFETY: `bindings::get_unused_fd_flags()` returns a non-negative
// `fd` on success, or a valid negative `errno` on error.
let fd = unsafe { from_kernel_int_result_uint(fd)? };
Ok(Self { fd: fd as _ })
}

Expand Down

0 comments on commit fa390c4

Please sign in to comment.