mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-12-27 13:30:45 -05:00
rust: pin-init: add miscellaneous files from the user-space version
Add readme and contribution guidelines of the user-space version of pin-init. Signed-off-by: Benno Lossin <benno.lossin@proton.me> Reviewed-by: Fiona Behrens <me@kloenk.dev> Reviewed-by: Andreas Hindborg <a.hindborg@kernel.org> Tested-by: Andreas Hindborg <a.hindborg@kernel.org> Link: https://lore.kernel.org/r/20250308110339.2997091-21-benno.lossin@proton.me Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
This commit is contained in:
committed by
Miguel Ojeda
parent
a9fa3a9c6e
commit
2e5f4f3cf2
72
rust/pin-init/CONTRIBUTING.md
Normal file
72
rust/pin-init/CONTRIBUTING.md
Normal file
@@ -0,0 +1,72 @@
|
||||
# Contributing to `pin-init`
|
||||
|
||||
Thanks for showing interest in contributing to `pin-init`! This document outlines the guidelines for
|
||||
contributing to `pin-init`.
|
||||
|
||||
All contributions are double-licensed under Apache 2.0 and MIT. You can find the respective licenses
|
||||
in the `LICENSE-APACHE` and `LICENSE-MIT` files.
|
||||
|
||||
## Non-Code Contributions
|
||||
|
||||
### Bug Reports
|
||||
|
||||
For any type of bug report, please submit an issue using the bug report issue template.
|
||||
|
||||
If the issue is a soundness issue, please privately report it as a security vulnerability via the
|
||||
GitHub web interface.
|
||||
|
||||
### Feature Requests
|
||||
|
||||
If you have any feature requests, please submit an issue using the feature request issue template.
|
||||
|
||||
### Questions and Getting Help
|
||||
|
||||
You can ask questions in the Discussions page of the GitHub repository. If you're encountering
|
||||
problems or just have questions related to `pin-init` in the Linux kernel, you can also ask your
|
||||
questions in the [Rust-for-Linux Zulip](https://rust-for-linux.zulipchat.com/) or see
|
||||
<https://rust-for-linux.com/contact>.
|
||||
|
||||
## Contributing Code
|
||||
|
||||
### Linux Kernel
|
||||
|
||||
`pin-init` is used by the Linux kernel and all commits are synchronized to it. For this reason, the
|
||||
same requirements for commits apply to `pin-init`. See [the kernel's documentation] for details. The
|
||||
rest of this document will also cover some of the rules listed there and additional ones.
|
||||
|
||||
[the kernel's documentation]: https://docs.kernel.org/process/submitting-patches.html
|
||||
|
||||
Contributions to `pin-init` ideally go through the [GitHub repository], because that repository runs
|
||||
a CI with lots of tests not present in the kernel. However, patches are also accepted (though not
|
||||
preferred). Do note that there are some files that are only present in the GitHub repository such as
|
||||
tests, licenses and cargo related files. Making changes to them can only happen via GitHub.
|
||||
|
||||
[GitHub repository]: https://github.com/Rust-for-Linux/pin-init
|
||||
|
||||
### Commit Style
|
||||
|
||||
Everything must compile without errors or warnings and all tests must pass after **every commit**.
|
||||
This is important for bisection and also required by the kernel.
|
||||
|
||||
Each commit should be a single, logically cohesive change. Of course it's best to keep the changes
|
||||
small and digestible, but logically linked changes should be made in the same commit. For example,
|
||||
when fixing typos, create a single commit that fixes all of them instead of one commit per typo.
|
||||
|
||||
Commits must have a meaningful commit title. Commits with changes to files in the `internal`
|
||||
directory should have a title prefixed with `internal:`. The commit message should explain the
|
||||
change and its rationale. You also have to add your `Signed-off-by` tag, see [Developer's
|
||||
Certificate of Origin]. This has to be done for both mailing list submissions as well as GitHub
|
||||
submissions.
|
||||
|
||||
[Developer's Certificate of Origin]: https://docs.kernel.org/process/submitting-patches.html#sign-your-work-the-developer-s-certificate-of-origin
|
||||
|
||||
Any changes made to public APIs must be documented not only in the commit message, but also in the
|
||||
`CHANGELOG.md` file. This is especially important for breaking changes, as those warrant a major
|
||||
version bump.
|
||||
|
||||
If you make changes to the top-level crate documentation, you also need to update the `README.md`
|
||||
via `cargo rdme`.
|
||||
|
||||
Some of these rules can be ignored if the change is done solely to files that are not present in the
|
||||
kernel version of this library. Those files are documented in the `sync-kernel.sh` script at the
|
||||
very bottom in the `--exclude` flag given to the `git am` command.
|
||||
228
rust/pin-init/README.md
Normal file
228
rust/pin-init/README.md
Normal file
@@ -0,0 +1,228 @@
|
||||
[](https://crates.io/crates/pin-init)
|
||||
[](https://docs.rs/pin-init/)
|
||||
[](https://deps.rs/repo/github/Rust-for-Linux/pin-init)
|
||||

|
||||
[](#nightly-only)
|
||||

|
||||
# `pin-init`
|
||||
|
||||
<!-- cargo-rdme start -->
|
||||
|
||||
Library to safely and fallibly initialize pinned `struct`s using in-place constructors.
|
||||
|
||||
[Pinning][pinning] is Rust's way of ensuring data does not move.
|
||||
|
||||
It also allows in-place initialization of big `struct`s that would otherwise produce a stack
|
||||
overflow.
|
||||
|
||||
This library's main use-case is in [Rust-for-Linux]. Although this version can be used
|
||||
standalone.
|
||||
|
||||
There are cases when you want to in-place initialize a struct. For example when it is very big
|
||||
and moving it from the stack is not an option, because it is bigger than the stack itself.
|
||||
Another reason would be that you need the address of the object to initialize it. This stands
|
||||
in direct conflict with Rust's normal process of first initializing an object and then moving
|
||||
it into it's final memory location. For more information, see
|
||||
<https://rust-for-linux.com/the-safe-pinned-initialization-problem>.
|
||||
|
||||
This library allows you to do in-place initialization safely.
|
||||
|
||||
### Nightly Needed for `alloc` feature
|
||||
|
||||
This library requires the [`allocator_api` unstable feature] when the `alloc` feature is
|
||||
enabled and thus this feature can only be used with a nightly compiler. When enabling the
|
||||
`alloc` feature, the user will be required to activate `allocator_api` as well.
|
||||
|
||||
[`allocator_api` unstable feature]: https://doc.rust-lang.org/nightly/unstable-book/library-features/allocator-api.html
|
||||
|
||||
The feature is enabled by default, thus by default `pin-init` will require a nightly compiler.
|
||||
However, using the crate on stable compilers is possible by disabling `alloc`. In practice this
|
||||
will require the `std` feature, because stable compilers have neither `Box` nor `Arc` in no-std
|
||||
mode.
|
||||
|
||||
## Overview
|
||||
|
||||
To initialize a `struct` with an in-place constructor you will need two things:
|
||||
- an in-place constructor,
|
||||
- a memory location that can hold your `struct` (this can be the [stack], an [`Arc<T>`],
|
||||
[`Box<T>`] or any other smart pointer that supports this library).
|
||||
|
||||
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 library also supports in-place construction without
|
||||
pinning, the macros/types/functions are generally named like the pinned variants without the
|
||||
`pin_` prefix.
|
||||
|
||||
## Examples
|
||||
|
||||
Throughout the examples we will often make use of the `CMutex` type which can be found in
|
||||
`../examples/mutex.rs`. It is essentially a userland rebuild of the `struct mutex` type from
|
||||
the Linux kernel. It also uses a wait list and a basic spinlock. Importantly the wait list
|
||||
requires it to be pinned to be locked and thus is a prime candidate for using this library.
|
||||
|
||||
### 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.
|
||||
|
||||
```rust
|
||||
use pin_init::{pin_data, pin_init, InPlaceInit};
|
||||
|
||||
#[pin_data]
|
||||
struct Foo {
|
||||
#[pin]
|
||||
a: CMutex<usize>,
|
||||
b: u32,
|
||||
}
|
||||
|
||||
let foo = pin_init!(Foo {
|
||||
a <- CMutex::new(42),
|
||||
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`:
|
||||
|
||||
```rust
|
||||
let foo: Result<Pin<Box<Foo>>, AllocError> = Box::pin_init(foo);
|
||||
```
|
||||
|
||||
For more information see the [`pin_init!`] macro.
|
||||
|
||||
### Using a custom function/macro that returns an initializer
|
||||
|
||||
Many types that use this library supply a function/macro that returns an initializer, because
|
||||
the above method only works for types where you can access the fields.
|
||||
|
||||
```rust
|
||||
let mtx: Result<Pin<Arc<CMutex<usize>>>, _> = Arc::pin_init(CMutex::new(42));
|
||||
```
|
||||
|
||||
To declare an init macro/function you just return an [`impl PinInit<T, E>`]:
|
||||
|
||||
```rust
|
||||
#[pin_data]
|
||||
struct DriverData {
|
||||
#[pin]
|
||||
status: CMutex<i32>,
|
||||
buffer: Box<[u8; 1_000_000]>,
|
||||
}
|
||||
|
||||
impl DriverData {
|
||||
fn new() -> impl PinInit<Self, Error> {
|
||||
try_pin_init!(Self {
|
||||
status <- CMutex::new(0),
|
||||
buffer: Box::init(pin_init::zeroed())?,
|
||||
}? Error)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 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.
|
||||
|
||||
```rust
|
||||
use pin_init::{pin_data, pinned_drop, PinInit, PinnedDrop, pin_init_from_closure};
|
||||
use core::{
|
||||
ptr::addr_of_mut,
|
||||
marker::PhantomPinned,
|
||||
cell::UnsafeCell,
|
||||
pin::Pin,
|
||||
mem::MaybeUninit,
|
||||
};
|
||||
mod bindings {
|
||||
#[repr(C)]
|
||||
pub struct foo {
|
||||
/* fields from C ... */
|
||||
}
|
||||
extern "C" {
|
||||
pub fn init_foo(ptr: *mut foo);
|
||||
pub fn destroy_foo(ptr: *mut foo);
|
||||
#[must_use = "you must check the error return code"]
|
||||
pub fn enable_foo(ptr: *mut foo, flags: u32) -> i32;
|
||||
}
|
||||
}
|
||||
|
||||
/// # Invariants
|
||||
///
|
||||
/// `foo` is always initialized
|
||||
#[pin_data(PinnedDrop)]
|
||||
pub struct RawFoo {
|
||||
#[pin]
|
||||
_p: PhantomPinned,
|
||||
#[pin]
|
||||
foo: UnsafeCell<MaybeUninit<bindings::foo>>,
|
||||
}
|
||||
|
||||
impl RawFoo {
|
||||
pub fn new(flags: u32) -> impl PinInit<Self, i32> {
|
||||
// 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 {
|
||||
pin_init_from_closure(move |slot: *mut Self| {
|
||||
// `slot` contains uninit memory, avoid creating a reference.
|
||||
let foo = addr_of_mut!((*slot).foo);
|
||||
let foo = UnsafeCell::raw_get(foo).cast::<bindings::foo>();
|
||||
|
||||
// Initialize the `foo`
|
||||
bindings::init_foo(foo);
|
||||
|
||||
// Try to enable it.
|
||||
let err = bindings::enable_foo(foo, flags);
|
||||
if err != 0 {
|
||||
// Enabling has failed, first clean up the foo and then return the error.
|
||||
bindings::destroy_foo(foo);
|
||||
Err(err)
|
||||
} else {
|
||||
// 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().cast::<bindings::foo>()) };
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
[`sync`]: https://rust.docs.kernel.org/kernel/sync/index.html
|
||||
[pinning]: https://doc.rust-lang.org/std/pin/index.html
|
||||
[structurally pinned fields]: https://doc.rust-lang.org/std/pin/index.html#pinning-is-structural-for-field
|
||||
[stack]: https://docs.rs/pin-init/latest/pin_init/macro.stack_pin_init.html
|
||||
[`Arc<T>`]: https://doc.rust-lang.org/stable/alloc/sync/struct.Arc.html
|
||||
[`Box<T>`]: https://doc.rust-lang.org/stable/alloc/boxed/struct.Box.html
|
||||
[`impl PinInit<Foo>`]: https://docs.rs/pin-init/latest/pin_init/trait.PinInit.html
|
||||
[`impl PinInit<T, E>`]: https://docs.rs/pin-init/latest/pin_init/trait.PinInit.html
|
||||
[`impl Init<T, E>`]: https://docs.rs/pin-init/latest/pin_init/trait.Init.html
|
||||
[Rust-for-Linux]: https://rust-for-linux.com/
|
||||
|
||||
<!-- cargo-rdme end -->
|
||||
Reference in New Issue
Block a user