Skip to content

rust: enhance PointerWrapper. #386

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

Merged
merged 1 commit into from
Jun 23, 2021
Merged
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
8 changes: 8 additions & 0 deletions rust/kernel/of.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ impl OfMatchTable {
}

impl PointerWrapper for OfMatchTable {
type Borrowed = <InnerTable as PointerWrapper>::Borrowed;

fn into_pointer(self) -> *const c_types::c_void {
// Per the invariant above, the generated pointer points to an
// array of `bindings::of_device_id`, where the final element is
Expand All @@ -68,6 +70,12 @@ impl PointerWrapper for OfMatchTable {
self.0.into_pointer()
}

unsafe fn borrow(ptr: *const c_types::c_void) -> Self::Borrowed {
// SAFETY: The safety requirements for this function are the same as the ones for
// `InnerTable::borrow`.
unsafe { InnerTable::borrow(ptr) }
}

unsafe fn from_pointer(p: *const c_types::c_void) -> Self {
// SAFETY: The passed pointer comes from a previous call to [`InnerTable::into_pointer()`].
Self(unsafe { InnerTable::from_pointer(p) })
Expand Down
99 changes: 93 additions & 6 deletions rust/kernel/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,12 @@
//!
//! C header: [`include/linux/types.h`](../../../../include/linux/types.h)

use core::{ops::Deref, pin::Pin};

use crate::{
bindings, c_types,
sync::{Ref, RefCounted},
};
use alloc::{boxed::Box, sync::Arc};

use crate::bindings;
use crate::c_types;
use crate::sync::{Ref, RefCounted};
use core::{ops::Deref, pin::Pin, ptr::NonNull};

/// Permissions.
///
Expand All @@ -37,9 +36,22 @@ impl Mode {
/// in kernel data structures, for example, an implementation of [`FileOperations`] in `struct
/// file::private_data`.
pub trait PointerWrapper {
/// Type of values borrowed between calls to [`PointerWrapper::into_pointer`] and
/// [`PointerWrapper::from_pointer`].
type Borrowed: Deref;

/// Returns the raw pointer.
fn into_pointer(self) -> *const c_types::c_void;

/// Returns a borrowed value.
///
/// # Safety
///
/// `ptr` must have been returned by a previous call to [`PointerWrapper::into_pointer`].
/// Additionally, [`PointerWrapper::from_pointer`] can only be called after *all* values
/// returned by [`PointerWrapper::borrow`] have been dropped.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are there a couple of safety constraints we've omitted here?
Following these # Safety instructions, are we allowed to:

  • call from_pointer() multiple times on the same pointer?
  • call borrow() after the call to from_pointer() ?

If so, does this merit a follow-up PR?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this should be clarified. I'll prepare a follow-up PR.

unsafe fn borrow(ptr: *const c_types::c_void) -> Self::Borrowed;

/// Returns the instance back from the raw pointer.
///
/// # Safety
Expand All @@ -49,46 +61,121 @@ pub trait PointerWrapper {
}

impl<T> PointerWrapper for Box<T> {
type Borrowed = UnsafeReference<T>;

fn into_pointer(self) -> *const c_types::c_void {
Box::into_raw(self) as _
}

unsafe fn borrow(ptr: *const c_types::c_void) -> Self::Borrowed {
// SAFETY: The safety requirements for this function ensure that the object is still alive,
// so it is safe to dereference the raw pointer.
// The safety requirements also ensure that the object remains alive for the lifetime of
// the returned value.
unsafe { UnsafeReference::new(&*ptr.cast()) }
}

unsafe fn from_pointer(ptr: *const c_types::c_void) -> Self {
// SAFETY: The passed pointer comes from a previous call to [`Self::into_pointer()`].
unsafe { Box::from_raw(ptr as _) }
}
}

impl<T: RefCounted> PointerWrapper for Ref<T> {
type Borrowed = UnsafeReference<T>;

fn into_pointer(self) -> *const c_types::c_void {
Ref::into_raw(self) as _
}

unsafe fn borrow(ptr: *const c_types::c_void) -> Self::Borrowed {
// SAFETY: The safety requirements for this function ensure that the object is still alive,
// so it is safe to dereference the raw pointer.
// The safety requirements also ensure that the object remains alive for the lifetime of
// the returned value.
unsafe { UnsafeReference::new(&*ptr.cast()) }
}

unsafe fn from_pointer(ptr: *const c_types::c_void) -> Self {
// SAFETY: The passed pointer comes from a previous call to [`Self::into_pointer()`].
unsafe { Ref::from_raw(ptr as _) }
}
}

impl<T> PointerWrapper for Arc<T> {
type Borrowed = UnsafeReference<T>;

fn into_pointer(self) -> *const c_types::c_void {
Arc::into_raw(self) as _
}

unsafe fn borrow(ptr: *const c_types::c_void) -> Self::Borrowed {
// SAFETY: The safety requirements for this function ensure that the object is still alive,
// so it is safe to dereference the raw pointer.
// The safety requirements also ensure that the object remains alive for the lifetime of
// the returned value.
unsafe { UnsafeReference::new(&*ptr.cast()) }
}

unsafe fn from_pointer(ptr: *const c_types::c_void) -> Self {
// SAFETY: The passed pointer comes from a previous call to [`Self::into_pointer()`].
unsafe { Arc::from_raw(ptr as _) }
}
}

/// A reference with manually-managed lifetime.
///
/// # Invariants
///
/// There are no mutable references to the underlying object, and it remains valid for the lifetime
/// of the [`UnsafeReference`] instance.
pub struct UnsafeReference<T: ?Sized> {
ptr: NonNull<T>,
}

impl<T: ?Sized> UnsafeReference<T> {
/// Creates a new [`UnsafeReference`] instance.
///
/// # Safety
///
/// Callers must ensure the following for the lifetime of the returned [`UnsafeReference`]
/// instance:
/// 1. That `obj` remains valid;
/// 2. That no mutable references to `obj` are created.
unsafe fn new(obj: &T) -> Self {
// INVARIANT: The safety requirements of this function ensure that the invariants hold.
Self {
ptr: NonNull::from(obj),
}
}
}

impl<T: ?Sized> Deref for UnsafeReference<T> {
type Target = T;

fn deref(&self) -> &Self::Target {
// SAFETY: By the type invariant, the object is still valid and alive, and there are no
// mutable references to it.
unsafe { self.ptr.as_ref() }
}
}

impl<T: PointerWrapper + Deref> PointerWrapper for Pin<T> {
type Borrowed = T::Borrowed;

fn into_pointer(self) -> *const c_types::c_void {
// SAFETY: We continue to treat the pointer as pinned by returning just a pointer to it to
// the caller.
let inner = unsafe { Pin::into_inner_unchecked(self) };
inner.into_pointer()
}

unsafe fn borrow(ptr: *const c_types::c_void) -> Self::Borrowed {
// SAFETY: The safety requirements for this function are the same as the ones for
// `T::borrow`.
unsafe { T::borrow(ptr) }
}

unsafe fn from_pointer(p: *const c_types::c_void) -> Self {
// SAFETY: The object was originally pinned.
// The passed pointer comes from a previous call to `inner::into_pointer()`.
Expand Down