Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Kernel sync #30

Draft
wants to merge 14 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 10 additions & 6 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Changed

- `InPlaceInit` now only exists when the `alloc` or `std` features are enabled

## [0.0.9] - 2024-12-02

### Added
Expand Down Expand Up @@ -46,7 +50,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

- `PinInit` is now a supertrait of `Init` (before there was a blanket impl)
- `PinInit` is now a supertrait of `Init` (before there was a blanket impl)

### Removed

Expand All @@ -65,8 +69,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [0.0.6] - 2023-04-08

[unreleased]: https://github.com/Rust-for-Linux/pinned-init/compare/v0.0.9...HEAD
[0.0.9]: https://github.com/Rust-for-Linux/pinned-init/compare/v0.0.8...v0.0.9
[0.0.8]: https://github.com/Rust-for-Linux/pinned-init/compare/v0.0.7...v0.0.8
[0.0.7]: https://github.com/Rust-for-Linux/pinned-init/compare/v0.0.6...v0.0.7
[0.0.6]: https://github.com/Rust-for-Linux/pinned-init/releases/tag/v0.0.6
[unreleased]: https://github.com/Rust-for-Linux/pin-init/compare/v0.0.9...HEAD
[0.0.9]: https://github.com/Rust-for-Linux/pin-init/compare/v0.0.8...v0.0.9
[0.0.8]: https://github.com/Rust-for-Linux/pin-init/compare/v0.0.7...v0.0.8
[0.0.7]: https://github.com/Rust-for-Linux/pin-init/compare/v0.0.6...v0.0.7
[0.0.6]: https://github.com/Rust-for-Linux/pin-init/releases/tag/v0.0.6
72 changes: 72 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -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.
6 changes: 3 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 5 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name = "pinned-init"
name = "pin-init"
version = "0.0.9"
edition = "2021"

Expand All @@ -8,14 +8,14 @@ license = "MIT OR Apache-2.0"
description = "Library to facilitate safe pinned initialization"
readme = "README.md"

documentation = "https://docs.rs/pinned-init"
repository = "https://github.com/Rust-for-Linux/pinned-init"
documentation = "https://docs.rs/pin-init"
repository = "https://github.com/Rust-for-Linux/pin-init"
keywords = ["safe", "pin", "init", "no-std", "rust-patterns"]
categories = ["no-std", "rust-patterns", "embedded"]

[dependencies]
paste = "1.0"
pinned-init-macro = { path = "./pinned-init-macro", version = "=0.0.5" }
pin-init-internal = { path = "./internal", version = "=0.0.5" }

[features]
default = ["std", "alloc"]
Expand All @@ -34,7 +34,7 @@ prettyplease = { version = "0.2", features = ["verbatim"] }

[lints.rust]
non_ascii_idents = "deny"
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(NO_UI_TESTS)', 'cfg(NO_ALLOC_FAIL_TESTS)'] }
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(NO_UI_TESTS)', 'cfg(NO_ALLOC_FAIL_TESTS)', 'cfg(kernel)'] }
unsafe_op_in_unsafe_fn = "deny"
unused_attributes = "deny"
warnings = "deny"
Expand Down
91 changes: 52 additions & 39 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
[![Crates.io](https://img.shields.io/crates/v/pinned-init.svg)](https://crates.io/crates/pinned-init)
[![Documentation](https://docs.rs/pinned-init/badge.svg)](https://docs.rs/pinned-init/)
[![Dependency status](https://deps.rs/repo/github/Rust-for-Linux/pinned-init/status.svg)](https://deps.rs/repo/github/Rust-for-Linux/pinned-init)
![License](https://img.shields.io/crates/l/pinned-init)
[![Crates.io](https://img.shields.io/crates/v/pin-init.svg)](https://crates.io/crates/pin-init)
[![Documentation](https://docs.rs/pin-init/badge.svg)](https://docs.rs/pin-init/)
[![Dependency status](https://deps.rs/repo/github/Rust-for-Linux/pin-init/status.svg)](https://deps.rs/repo/github/Rust-for-Linux/pin-init)
![License](https://img.shields.io/crates/l/pin-init)
[![Toolchain](https://img.shields.io/badge/toolchain-nightly-red)](#nightly-only)
![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/Rust-for-Linux/pinned-init/test.yml)
# Pinned-init
![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/Rust-for-Linux/pin-init/test.yml)
# `pin-init`

<!-- cargo-rdme start -->

Expand All @@ -22,44 +22,46 @@ There are cases when you want to in-place initialize a struct. For example when
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.
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.
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.

The feature is enabled by default, thus by default `pinned-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.
[`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 implements [`InPlaceInit`]).
[`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.
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 some examples we will make use of the `CMutex` type which can be found in
`../examples/mutex.rs`. It is essentially a rebuild of the `mutex` from the Linux kernel in userland. So
it also uses a wait list and a basic spinlock. Importantly it needs to be pinned to be locked
and thus is a prime candidate for using this library.
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

Expand All @@ -70,7 +72,8 @@ If you want to use [`PinInit`], then you will have to annotate your `struct` wit
that you need to write `<-` instead of `:` for fields that you want to initialize in-place.

```rust
use pinned_init::*;
use pin_init::{pin_data, pin_init, InPlaceInit};

#[pin_data]
struct Foo {
#[pin]
Expand All @@ -88,7 +91,7 @@ let foo = pin_init!(Foo {
(or just the stack) to actually initialize a `Foo`:

```rust
let foo: Result<Pin<Box<Foo>>, _> = Box::pin_init(foo);
let foo: Result<Pin<Box<Foo>>, AllocError> = Box::pin_init(foo);
```

For more information see the [`pin_init!`] macro.
Expand Down Expand Up @@ -116,7 +119,7 @@ impl DriverData {
fn new() -> impl PinInit<Self, Error> {
try_pin_init!(Self {
status <- CMutex::new(0),
buffer: Box::init(pinned_init::zeroed())?,
buffer: Box::init(pin_init::zeroed())?,
}? Error)
}
}
Expand All @@ -137,11 +140,20 @@ actually does the initialization in the correct way. Here are the things to look
`slot` gets called.

```rust
use pinned_init::*;
use core::{ptr::addr_of_mut, marker::PhantomPinned, cell::UnsafeCell, pin::Pin};
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 type foo;
pub fn init_foo(ptr: *mut foo);
pub fn destroy_foo(ptr: *mut foo);
#[must_use = "you must check the error return code"]
Expand All @@ -157,7 +169,7 @@ pub struct RawFoo {
#[pin]
_p: PhantomPinned,
#[pin]
foo: UnsafeCell<bindings::foo>,
foo: UnsafeCell<MaybeUninit<bindings::foo>>,
}

impl RawFoo {
Expand All @@ -170,15 +182,16 @@ impl RawFoo {
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(UnsafeCell::raw_get(foo));
bindings::init_foo(foo);

// Try to enable it.
let err = bindings::enable_foo(UnsafeCell::raw_get(foo), flags);
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(UnsafeCell::raw_get(foo));
bindings::destroy_foo(foo);
Err(err)
} else {
// All fields of `RawFoo` have been initialized, since `_p` is a ZST.
Expand All @@ -193,23 +206,23 @@ impl RawFoo {
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()) };
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://github.com/Rust-for-Linux/linux/tree/rust-next/rust/kernel/sync
[`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/pinned-init/latest/pinned_init/macro.stack_pin_init.html
[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/pinned-init/latest/pinned_init/trait.PinInit.html
[`impl PinInit<T, E>`]: https://docs.rs/pinned-init/latest/pinned_init/trait.PinInit.html
[`impl Init<T, E>`]: https://docs.rs/pinned-init/latest/pinned_init/trait.Init.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 -->
4 changes: 3 additions & 1 deletion examples/big_struct_in_place.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use pinned_init::*;
// SPDX-License-Identifier: Apache-2.0 OR MIT

use pin_init::*;

// Struct with size over 1GiB
#[derive(Debug)]
Expand Down
2 changes: 2 additions & 0 deletions examples/error.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// SPDX-License-Identifier: Apache-2.0 OR MIT

#![cfg_attr(feature = "alloc", feature(allocator_api))]

use core::convert::Infallible;
Expand Down
4 changes: 3 additions & 1 deletion examples/linked_list.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// SPDX-License-Identifier: Apache-2.0 OR MIT

#![allow(clippy::undocumented_unsafe_blocks)]
#![cfg_attr(feature = "alloc", feature(allocator_api))]

Expand All @@ -9,7 +11,7 @@ use core::{
ptr::{self, NonNull},
};

use pinned_init::*;
use pin_init::*;

#[expect(unused_attributes)]
mod error;
Expand Down
Loading