Module kernel::init

source · []
Expand description

API to safely and fallibly initialize pinned structs using in-place constructors.

It also allows in-place initialization of big structs that would otherwise produce a stack overflow.

Most structs from the sync module need to be pinned, because they contain self-referential structs from C. Pinning is Rust’s way of ensuring data does not move.

Overview

To initialize a struct with an in-place constructor you will need two things:

To get an in-place constructor there are generally three options:

  • directly creating an in-place constructor using the pin_init! macro,
  • a custom function/macro returning an in-place constructor provided by someone else,
  • using the unsafe function pin_init_from_closure() to manually create an initializer.

Aside from pinned initialization, this API also supports in-place construction without pinning, the macros/types/functions are generally named like the pinned variants without the pin prefix.

Examples

Using the pin_init! macro

If you want to use PinInit, then you will have to annotate your struct with #[pin_data]. It is a macro that uses #[pin] as a marker for structurally pinned fields. After doing this, you can then create an in-place constructor via pin_init!. The syntax is almost the same as normal struct initializers. The difference is that you need to write <- instead of : for fields that you want to initialize in-place.

use kernel::{prelude::*, sync::Mutex, new_mutex};
#[pin_data]
struct Foo {
    #[pin]
    a: Mutex<usize>,
    b: u32,
}

let foo = pin_init!(Foo {
    a <- new_mutex!(42, "Foo::a"),
    b: 24,
});

foo now is of the type impl PinInit<Foo>. We can now use any smart pointer that we like (or just the stack) to actually initialize a Foo:

let foo: Result<Pin<Box<Foo>>> = Box::pin_init(foo);

For more information see the pin_init! macro.

Using a custom function/macro that returns an initializer

Many types from the kernel supply a function/macro that returns an initializer, because the above method only works for types where you can access the fields.

let mtx: Result<Arc<Mutex<usize>>> = Arc::pin_init(new_mutex!(42, "example::mtx"));

To declare an init macro/function you just return an impl PinInit<T, E>:

#[pin_data]
struct DriverData {
    #[pin]
    status: Mutex<i32>,
    buffer: Box<[u8; 1_000_000]>,
}

impl DriverData {
    fn new() -> impl PinInit<Self, Error> {
        try_pin_init!(Self {
            status <- new_mutex!(0, "DriverData::status"),
            buffer: Box::init(kernel::init::zeroed())?,
        })
    }
}

Manual creation of an initializer

Often when working with primitives the previous approaches are not sufficient. That is where pin_init_from_closure() comes in. This unsafe function allows you to create a impl PinInit<T, E> directly from a closure. Of course you have to ensure that the closure actually does the initialization in the correct way. Here are the things to look out for (we are calling the parameter to the closure slot):

  • when the closure returns Ok(()), then it has completed the initialization successfully, so slot now contains a valid bit pattern for the type T,
  • when the closure returns Err(e), then the caller may deallocate the memory at slot, so you need to take care to clean up anything if your initialization fails mid-way,
  • you may assume that slot will stay pinned even after the closure returns until drop of slot gets called.
use kernel::{prelude::*, init};
use core::{ptr::addr_of_mut, marker::PhantomPinned, pin::Pin};
/// # Invariants
///
/// `foo` is always initialized
#[pin_data(PinnedDrop)]
pub struct RawFoo {
    #[pin]
    foo: Opaque<bindings::foo>,
    #[pin]
    _p: PhantomPinned,
}

impl RawFoo {
    pub fn new(flags: u32) -> impl PinInit<Self, Error> {
        // SAFETY:
        // - when the closure returns `Ok(())`, then it has successfully initialized and
        //   enabled `foo`,
        // - when it returns `Err(e)`, then it has cleaned up before
        unsafe {
            init::pin_init_from_closure(move |slot: *mut Self| {
                // `slot` contains uninit memory, avoid creating a reference.
                let foo = addr_of_mut!((*slot).foo);

                // Initialize the `foo`
                bindings::init_foo(Opaque::raw_get(foo));

                // Try to enable it.
                let err = bindings::enable_foo(Opaque::raw_get(foo), flags);
                if err != 0 {
                    // Enabling has failed, first clean up the foo and then return the error.
                    bindings::destroy_foo(Opaque::raw_get(foo));
                    return Err(Error::from_kernel_errno(err));
                }

                // All fields of `RawFoo` have been initialized, since `_p` is a ZST.
                Ok(())
            })
        }
    }
}

#[pinned_drop]
impl PinnedDrop for RawFoo {
    fn drop(self: Pin<&mut Self>) {
        // SAFETY: Since `foo` is initialized, destroying is safe.
        unsafe { bindings::destroy_foo(self.foo.get()) };
    }
}

For the special case where initializing a field is a single FFI-function call that cannot fail, there exist the helper function Opaque::ffi_init. This function initialize a single Opaque field by just delegating to the supplied closure. You can use these in combination with pin_init!.

For more information on how to use pin_init_from_closure(), take a look at the uses inside the kernel crate. The sync module is a good starting point.

Traits

Smart pointer that can initialize memory in-place.

An initializer for T.

A pin-initializer for the type T.

Trait facilitating pinned destruction.

Marker trait for types that can be initialized by writing just zeroes.

Functions

Creates a new Init<T, E> from the given closure.

Creates a new PinInit<T, E> from the given closure.

An initializer that leaves the memory uninitialized.

Create a new zeroed T.