Skip to content

Commit 5e932f0

Browse files
author
Sven Van Asbroeck
committed
rust/kernel: introduce zero-cost from_kernel_int_result()
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 0 on success, or a non-zero errno on failure. Signed-off-by: Sven Van Asbroeck <[email protected]>
1 parent 5c65d82 commit 5e932f0

File tree

6 files changed

+64
-23
lines changed

6 files changed

+64
-23
lines changed

rust/kernel/chrdev.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use core::pin::Pin;
1515

1616
use crate::bindings;
1717
use crate::c_types;
18-
use crate::error::{Error, Result};
18+
use crate::error::{from_kernel_int_result, Error, Result};
1919
use crate::file_operations;
2020
use crate::str::CStr;
2121

@@ -60,10 +60,9 @@ impl Cdev {
6060
// - [`(*self.0).owner`] will live at least as long as the
6161
// module, which is an implicit requirement.
6262
let rc = unsafe { bindings::cdev_add(self.0, dev, count) };
63-
if rc != 0 {
64-
return Err(Error::from_kernel_errno(rc));
65-
}
66-
Ok(())
63+
// SAFETY: `bindings::cdev_add()` returns zero on success,
64+
// or a valid negative `errno` on error.
65+
unsafe { from_kernel_int_result(rc) }
6766
}
6867
}
6968

@@ -149,8 +148,10 @@ impl<const N: usize> Registration<{ N }> {
149148
this.name.as_char_ptr(),
150149
)
151150
};
152-
if res != 0 {
153-
return Err(Error::from_kernel_errno(res));
151+
// SAFETY: `bindings::alloc_chrdev_region()` returns zero on
152+
// success, or a valid negative `errno` on error.
153+
unsafe {
154+
from_kernel_int_result(res)?;
154155
}
155156
const NONE: Option<Cdev> = None;
156157
this.inner = Some(RegistrationInner {

rust/kernel/error.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,3 +263,36 @@ pub(crate) fn from_kernel_err_ptr<T>(ptr: *mut T) -> Result<*mut T> {
263263
}
264264
Ok(ptr)
265265
}
266+
267+
/// Transform a kernel integer result to a [`Result`].
268+
///
269+
/// Some kernel C API functions return a result in the form of an integer:
270+
/// zero if ok, a negative errno on error. This function converts such a
271+
/// return value into an idiomatic [`Result`].
272+
///
273+
/// Use this function when the C function only returns 0 on success or
274+
/// negative on error. If the C function returns a useful value on the
275+
/// happy path, use [`from_kernel_int_result_uint`] instead.
276+
///
277+
/// # Safety
278+
///
279+
/// `retval` must be non-negative or a valid negative errno (i.e. `retval` must
280+
/// be in `[-MAX_ERRNO..]`).
281+
///
282+
/// # Examples
283+
///
284+
/// ```rust,no_run
285+
/// let ret = unsafe { bindings::misc_register(&mut this.mdev) };
286+
/// // SAFETY: `misc_register` returns zero on success, or a valid
287+
/// // negative errno on failure.
288+
/// unsafe { from_kernel_int_result(ret)?; }
289+
/// ```
290+
pub(crate) unsafe fn from_kernel_int_result(retval: c_types::c_int) -> Result {
291+
if retval < 0 {
292+
// SAFETY: This condition together with the function precondition
293+
// guarantee that `errno` is a valid negative `errno`.
294+
return Err(unsafe { Error::from_kernel_errno_unchecked(retval) });
295+
}
296+
Ok(())
297+
}
298+
}

rust/kernel/miscdev.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
//! Reference: <https://www.kernel.org/doc/html/latest/driver-api/misc_devices.html>
88
99
use crate::bindings;
10-
use crate::error::{Error, Result};
10+
use crate::error::{from_kernel_int_result, Error, Result};
1111
use crate::file_operations::{FileOpenAdapter, FileOpener, FileOperationsVtable};
1212
use crate::str::CStr;
1313
use alloc::boxed::Box;
@@ -73,8 +73,10 @@ impl<T: Sync> Registration<T> {
7373
this.mdev.minor = minor.unwrap_or(bindings::MISC_DYNAMIC_MINOR as i32);
7474

7575
let ret = unsafe { bindings::misc_register(&mut this.mdev) };
76-
if ret < 0 {
77-
return Err(Error::from_kernel_errno(ret));
76+
// SAFETY: `bindings::alloc_chrdev_region()` returns zero on
77+
// success, or a valid negative `errno` on error.
78+
unsafe {
79+
from_kernel_int_result(ret)?;
7880
}
7981
this.registered = true;
8082
Ok(())

rust/kernel/pages.rs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
//! TODO: This module is a work in progress.
66
77
use crate::{
8-
bindings, c_types, io_buffer::IoBufferReader, user_ptr::UserSlicePtrReader, Error, Result,
9-
PAGE_SIZE,
8+
bindings, c_types, error::from_kernel_int_result, io_buffer::IoBufferReader,
9+
user_ptr::UserSlicePtrReader, Error, Result, PAGE_SIZE,
1010
};
1111
use core::{marker::PhantomData, ptr};
1212

@@ -65,11 +65,9 @@ impl<const ORDER: u32> Pages<ORDER> {
6565
// SAFETY: We check above that the allocation is of order 0. The range of `address` is
6666
// already checked by `vm_insert_page`.
6767
let ret = unsafe { bindings::vm_insert_page(vma, address as _, self.pages) };
68-
if ret != 0 {
69-
Err(Error::from_kernel_errno(ret))
70-
} else {
71-
Ok(())
72-
}
68+
// SAFETY: `bindings::vm_insert_page()` returns zero on success,
69+
// or a valid negative `errno` on error.
70+
unsafe { from_kernel_int_result(ret) }
7371
}
7472

7573
/// Copies data from the given [`UserSlicePtrReader`] into the pages.

rust/kernel/platdev.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
99
use crate::{
1010
bindings, c_types,
11-
error::{Error, Result},
11+
error::{from_kernel_int_result, Error, Result},
1212
from_kernel_result,
1313
of::OfMatchTable,
1414
str::CStr,
@@ -83,8 +83,10 @@ impl Registration {
8383
// `bindings::platform_driver_unregister()`, or
8484
// - null.
8585
let ret = unsafe { bindings::__platform_driver_register(&mut this.pdrv, module.0) };
86-
if ret < 0 {
87-
return Err(Error::from_kernel_errno(ret));
86+
// SAFETY: `bindings::__platform_driver_register()` returns zero on
87+
// success, or a valid negative `errno` on error.
88+
unsafe {
89+
from_kernel_int_result(ret)?;
8890
}
8991
this.registered = true;
9092
Ok(())

rust/kernel/random.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,21 @@
66
77
use core::convert::TryInto;
88

9-
use crate::{bindings, c_types, error};
9+
use crate::{
10+
bindings, c_types,
11+
error::{self, from_kernel_int_result},
12+
};
1013

1114
/// Fills a byte slice with random bytes generated from the kernel's CSPRNG.
1215
///
1316
/// Ensures that the CSPRNG has been seeded before generating any random bytes,
1417
/// and will block until it is ready.
1518
pub fn getrandom(dest: &mut [u8]) -> error::Result {
1619
let res = unsafe { bindings::wait_for_random_bytes() };
17-
if res != 0 {
18-
return Err(error::Error::from_kernel_errno(res));
20+
// SAFETY: `bindings::wait_for_random_bytes()` returns zero on success,
21+
// or a valid negative `errno` on error.
22+
unsafe {
23+
from_kernel_int_result(res)?;
1924
}
2025

2126
unsafe {

0 commit comments

Comments
 (0)