Skip to content

Commit

Permalink
Merge pull request torvalds#292 from TheSven73/rust-for-linux-pin-wra…
Browse files Browse the repository at this point in the history
…pper

Improve safety with fallible version of `pointer::pin()`
  • Loading branch information
ojeda authored May 26, 2021
2 parents 0c7a4fa + 5a67315 commit 50f2249
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 11 deletions.
1 change: 1 addition & 0 deletions rust/kernel/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ pub mod file_operations;
pub mod miscdev;
pub mod pages;
pub mod str;
pub mod traits;

pub mod linked_list;
mod raw_list;
Expand Down
2 changes: 2 additions & 0 deletions rust/kernel/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,5 @@ pub use super::{pr_alert, pr_cont, pr_crit, pr_emerg, pr_err, pr_info, pr_notice
pub use super::static_assert;

pub use super::{KernelModule, Result};

pub use crate::traits::TryPin;
26 changes: 26 additions & 0 deletions rust/kernel/traits.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// SPDX-License-Identifier: GPL-2.0

//! Traits useful to drivers, and their implementations for common types.
use core::{ops::Deref, pin::Pin};

use alloc::{alloc::AllocError, sync::Arc};

/// Trait which provides a fallible version of `pin()` for pointer types.
///
/// Common pointer types which implement a `pin()` method include [`Box`], [`Arc`] and [`Rc`].
pub trait TryPin<P: Deref> {
/// Constructs a new `Pin<pointer<T>>`. If `T` does not implement [`Unpin`], then data
/// will be pinned in memory and unable to be moved. An error will be returned
/// if allocation fails.
fn try_pin(data: P::Target) -> core::result::Result<Pin<P>, AllocError>;
}

impl<T> TryPin<Arc<T>> for Arc<T> {
fn try_pin(data: T) -> core::result::Result<Pin<Arc<T>>, AllocError> {
// SAFETY: the data `T` is exposed only through a `Pin<Arc<T>>`, which
// does not allow data to move out of the `Arc`. Therefore it can
// never be moved.
Ok(unsafe { Pin::new_unchecked(Arc::try_new(data)?) })
}
}
19 changes: 8 additions & 11 deletions samples/rust/rust_miscdev.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,19 +39,16 @@ struct SharedState {

impl SharedState {
fn try_new() -> Result<Pin<Arc<Self>>> {
// SAFETY: `state` is pinning `Arc`, which implements `Unpin`.
let state = unsafe {
Pin::new_unchecked(Arc::try_new(Self {
// SAFETY: `condvar_init!` is called below.
state_changed: CondVar::new(),
// SAFETY: `mutex_init!` is called below.
inner: Mutex::new(SharedStateInner { token_count: 0 }),
})?)
};
// SAFETY: `state_changed` is pinned behind `Arc`.
let state = Arc::try_pin(Self {
// SAFETY: `condvar_init!` is called below.
state_changed: unsafe { CondVar::new() },
// SAFETY: `mutex_init!` is called below.
inner: unsafe { Mutex::new(SharedStateInner { token_count: 0 }) },
})?;
// SAFETY: `state_changed` is pinned behind `Pin<Arc>`.
let state_changed = unsafe { Pin::new_unchecked(&state.state_changed) };
kernel::condvar_init!(state_changed, "SharedState::state_changed");
// SAFETY: `inner` is pinned behind `Arc`.
// SAFETY: `inner` is pinned behind `Pin<Arc>`.
let inner = unsafe { Pin::new_unchecked(&state.inner) };
kernel::mutex_init!(inner, "SharedState::inner");
Ok(state)
Expand Down

0 comments on commit 50f2249

Please sign in to comment.