Skip to content

Commit

Permalink
Merge pull request torvalds#617 from wedsonaf/file-opener
Browse files Browse the repository at this point in the history
rust: simplify file operations by removing `FileOpener`
  • Loading branch information
wedsonaf committed Jan 12, 2022
2 parents 00bb9f4 + 6b7165b commit cd969b5
Show file tree
Hide file tree
Showing 10 changed files with 83 additions and 98 deletions.
13 changes: 6 additions & 7 deletions drivers/android/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use kernel::{
bindings, c_types,
cred::Credential,
file::File,
file_operations::{FileOpener, FileOperations, IoctlCommand, IoctlHandler, PollTable},
file_operations::{FileOperations, IoctlCommand, IoctlHandler, PollTable},
io_buffer::{IoBufferReader, IoBufferWriter},
linked_list::List,
pages::Pages,
Expand Down Expand Up @@ -806,17 +806,16 @@ impl IoctlHandler for Process {
}
}

impl FileOpener<Ref<Context>> for Process {
fn open(ctx: &Ref<Context>, file: &File) -> Result<Self::Wrapper> {
Self::new(ctx.clone(), file.cred().clone())
}
}

impl FileOperations for Process {
type Wrapper = Ref<Self>;
type OpenData = Ref<Context>;

kernel::declare_file_operations!(ioctl, compat_ioctl, mmap, poll);

fn open(ctx: &Ref<Context>, file: &File) -> Result<Self::Wrapper> {
Self::new(ctx.clone(), file.cred().clone())
}

fn release(obj: Self::Wrapper, _file: &File) {
// Mark this process as dead. We'll do the same for the threads later.
obj.inner.lock().is_dead = true;
Expand Down
4 changes: 2 additions & 2 deletions drivers/android/rust_binder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,13 +102,13 @@ const fn ptr_align(value: usize) -> usize {
unsafe impl Sync for BinderModule {}

struct BinderModule {
_reg: Pin<Box<Registration<Ref<Context>>>>,
_reg: Pin<Box<Registration<process::Process>>>,
}

impl KernelModule for BinderModule {
fn init(name: &'static CStr, _module: &'static kernel::ThisModule) -> Result<Self> {
let ctx = Context::new()?;
let reg = Registration::new_pinned::<process::Process>(name, None, ctx)?;
let reg = Registration::new_pinned(name, None, ctx)?;
Ok(Self { _reg: reg })
}
}
10 changes: 6 additions & 4 deletions drivers/char/hw_random/bcm2835_rng_rust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,15 @@ module! {
license: b"GPL v2",
}

#[derive(Default)]
struct RngDevice;

impl FileOperations for RngDevice {
kernel::declare_file_operations!(read);

fn open(_open_data: &(), _file: &File) -> Result<Self::Wrapper> {
Ok(Box::try_new(RngDevice)?)
}

fn read(_: &Self, _: &File, data: &mut impl IoBufferWriter, offset: u64) -> Result<usize> {
// Succeed if the caller doesn't provide a buffer or if not at the start.
if data.is_empty() || offset != 0 {
Expand All @@ -38,12 +41,11 @@ impl FileOperations for RngDevice {
struct RngDriver;

impl PlatformDriver for RngDriver {
type DrvData = Pin<Box<miscdev::Registration<()>>>;
type DrvData = Pin<Box<miscdev::Registration<RngDevice>>>;

fn probe(device_id: i32) -> Result<Self::DrvData> {
pr_info!("probing discovered hwrng with id {}\n", device_id);
let drv_data =
miscdev::Registration::new_pinned::<RngDevice>(c_str!("rust_hwrng"), None, ())?;
let drv_data = miscdev::Registration::new_pinned(c_str!("rust_hwrng"), None, ())?;
Ok(drv_data)
}

Expand Down
13 changes: 5 additions & 8 deletions rust/kernel/chrdev.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,9 @@ impl<const N: usize> Registration<{ N }> {
/// Registers a character device.
///
/// You may call this once per device type, up to `N` times.
pub fn register<T: file_operations::FileOpener<()>>(self: Pin<&mut Self>) -> Result {
pub fn register<T: file_operations::FileOperations<OpenData = ()>>(
self: Pin<&mut Self>,
) -> Result {
// SAFETY: We must ensure that we never move out of `this`.
let this = unsafe { self.get_unchecked_mut() };
if this.inner.is_none() {
Expand Down Expand Up @@ -177,13 +179,8 @@ impl<const N: usize> Registration<{ N }> {
}
}

impl<const N: usize> file_operations::FileOpenAdapter for Registration<{ N }> {
type Arg = ();

unsafe fn convert(
_inode: *mut bindings::inode,
_file: *mut bindings::file,
) -> *const Self::Arg {
impl<const N: usize> file_operations::FileOpenAdapter<()> for Registration<{ N }> {
unsafe fn convert(_inode: *mut bindings::inode, _file: *mut bindings::file) -> *const () {
// TODO: Update the SAFETY comment on the call to `FileOperationsVTable::build` above once
// this is updated to retrieve state.
&()
Expand Down
39 changes: 12 additions & 27 deletions rust/kernel/file_operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ pub enum SeekFrom {
///
/// The returned value of `A::convert` must be a valid non-null pointer and
/// `T:open` must return a valid non-null pointer on an `Ok` result.
unsafe extern "C" fn open_callback<A: FileOpenAdapter, T: FileOpener<A::Arg>>(
unsafe extern "C" fn open_callback<A: FileOpenAdapter<T::OpenData>, T: FileOperations>(
inode: *mut bindings::inode,
file: *mut bindings::file,
) -> c_types::c_int {
Expand Down Expand Up @@ -312,7 +312,7 @@ unsafe extern "C" fn poll_callback<T: FileOperations>(

pub(crate) struct FileOperationsVtable<A, T>(marker::PhantomData<A>, marker::PhantomData<T>);

impl<A: FileOpenAdapter, T: FileOpener<A::Arg>> FileOperationsVtable<A, T> {
impl<A: FileOpenAdapter<T::OpenData>, T: FileOperations> FileOperationsVtable<A, T> {
const VTABLE: bindings::file_operations = bindings::file_operations {
open: Some(open_callback::<A, T>),
release: Some(release_callback::<T>),
Expand Down Expand Up @@ -568,10 +568,7 @@ impl IoctlCommand {
/// Trait for extracting file open arguments from kernel data structures.
///
/// This is meant to be implemented by registration managers.
pub trait FileOpenAdapter {
/// The type of argument this adapter extracts.
type Arg;

pub trait FileOpenAdapter<T: Sync> {
/// Converts untyped data stored in [`struct inode`] and [`struct file`] (when [`struct
/// file_operations::open`] is called) into the given type. For example, for `miscdev`
/// devices, a pointer to the registered [`struct miscdev`] is stored in [`struct
Expand All @@ -582,27 +579,7 @@ pub trait FileOpenAdapter {
/// This function must be called only when [`struct file_operations::open`] is being called for
/// a file that was registered by the implementer. The returned pointer must be valid and
/// not-null.
unsafe fn convert(_inode: *mut bindings::inode, _file: *mut bindings::file)
-> *const Self::Arg;
}

/// Trait for implementers of kernel files.
///
/// In addition to the methods in [`FileOperations`], implementers must also provide
/// [`FileOpener::open`] with a customised argument. This allows a single implementation of
/// [`FileOperations`] to be used for different types of registrations, for example, `miscdev` and
/// `chrdev`.
pub trait FileOpener<T: ?Sized>: FileOperations {
/// Creates a new instance of this file.
///
/// Corresponds to the `open` function pointer in `struct file_operations`.
fn open(context: &T, file: &File) -> Result<Self::Wrapper>;
}

impl<T: FileOperations<Wrapper = Box<T>> + Default> FileOpener<()> for T {
fn open(_: &(), _file: &File) -> Result<Self::Wrapper> {
Ok(Box::try_new(T::default())?)
}
unsafe fn convert(_inode: *mut bindings::inode, _file: *mut bindings::file) -> *const T;
}

/// Corresponds to the kernel's `struct file_operations`.
Expand All @@ -619,6 +596,14 @@ pub trait FileOperations: Send + Sync + Sized + 'static {
/// The pointer type that will be used to hold ourselves.
type Wrapper: PointerWrapper = Box<Self>;

/// The type of the context data passed to [`FileOperations::open`].
type OpenData: Sync = ();

/// Creates a new instance of this file.
///
/// Corresponds to the `open` function pointer in `struct file_operations`.
fn open(context: &Self::OpenData, file: &File) -> Result<Self::Wrapper>;

/// Cleans up after the last reference to the file goes away.
///
/// Note that the object is moved, so it will be freed automatically unless the implementation
Expand Down
56 changes: 27 additions & 29 deletions rust/kernel/miscdev.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,28 @@

use crate::bindings;
use crate::error::{Error, Result};
use crate::file_operations::{FileOpenAdapter, FileOpener, FileOperationsVtable};
use crate::file_operations::{FileOpenAdapter, FileOperations, FileOperationsVtable};
use crate::{str::CStr, KernelModule, ThisModule};
use alloc::boxed::Box;
use core::marker::{PhantomData, PhantomPinned};
use core::marker::PhantomPinned;
use core::{mem::MaybeUninit, pin::Pin};

/// A registration of a miscellaneous device.
///
/// # Invariants
///
/// `Context` is always initialised when `registered` is `true`, and not initialised otherwise.
pub struct Registration<T: Sync = ()> {
pub struct Registration<T: FileOperations> {
registered: bool,
mdev: bindings::miscdevice,
_pin: PhantomPinned,

/// Context initialised on construction and made available to all file instances on
/// [`FileOpener::open`].
open_data: MaybeUninit<T>,
/// [`FileOperations::open`].
open_data: MaybeUninit<T::OpenData>,
}

impl<T: Sync> Registration<T> {
impl<T: FileOperations> Registration<T> {
/// Creates a new [`Registration`] but does not register it yet.
///
/// It is allowed to move.
Expand All @@ -46,25 +46,25 @@ impl<T: Sync> Registration<T> {
/// Registers a miscellaneous device.
///
/// Returns a pinned heap-allocated representation of the registration.
pub fn new_pinned<F: FileOpener<T>>(
pub fn new_pinned(
name: &'static CStr,
minor: Option<i32>,
open_data: T,
open_data: T::OpenData,
) -> Result<Pin<Box<Self>>> {
let mut r = Pin::from(Box::try_new(Self::new())?);
r.as_mut().register::<F>(name, minor, open_data)?;
r.as_mut().register(name, minor, open_data)?;
Ok(r)
}

/// Registers a miscellaneous device with the rest of the kernel.
///
/// It must be pinned because the memory block that represents the registration is
/// self-referential. If a minor is not given, the kernel allocates a new one if possible.
pub fn register<F: FileOpener<T>>(
pub fn register(
self: Pin<&mut Self>,
name: &'static CStr,
minor: Option<i32>,
open_data: T,
open_data: T::OpenData,
) -> Result {
// SAFETY: We must ensure that we never move out of `this`.
let this = unsafe { self.get_unchecked_mut() };
Expand All @@ -74,7 +74,7 @@ impl<T: Sync> Registration<T> {
}

// SAFETY: The adapter is compatible with `misc_register`.
this.mdev.fops = unsafe { FileOperationsVtable::<Self, F>::build() };
this.mdev.fops = unsafe { FileOperationsVtable::<Self, T>::build() };
this.mdev.name = name.as_char_ptr();
this.mdev.minor = minor.unwrap_or(bindings::MISC_DYNAMIC_MINOR as i32);

Expand All @@ -98,16 +98,17 @@ impl<T: Sync> Registration<T> {
}
}

impl<T: Sync> Default for Registration<T> {
impl<T: FileOperations> Default for Registration<T> {
fn default() -> Self {
Self::new()
}
}

impl<T: Sync> FileOpenAdapter for Registration<T> {
type Arg = T;

unsafe fn convert(_inode: *mut bindings::inode, file: *mut bindings::file) -> *const Self::Arg {
impl<T: FileOperations> FileOpenAdapter<T::OpenData> for Registration<T> {
unsafe fn convert(
_inode: *mut bindings::inode,
file: *mut bindings::file,
) -> *const T::OpenData {
// SAFETY: the caller must guarantee that `file` is valid.
let reg = crate::container_of!(unsafe { (*file).private_data }, Self, mdev);

Expand All @@ -120,14 +121,13 @@ impl<T: Sync> FileOpenAdapter for Registration<T> {

// SAFETY: The only method is `register()`, which requires a (pinned) mutable `Registration`, so it
// is safe to pass `&Registration` to multiple threads because it offers no interior mutability.
unsafe impl<T: Sync> Sync for Registration<T> {}
unsafe impl<T: FileOperations> Sync for Registration<T> {}

// SAFETY: All functions work from any thread. So as long as the `Registration::open_data` is
// `Send`, so is `Registration<T>`. `T` needs to be `Sync` because it's a requirement of
// `Registration<T>`.
unsafe impl<T: Send + Sync> Send for Registration<T> {}
// `Send`, so is `Registration<T>`.
unsafe impl<T: FileOperations> Send for Registration<T> where T::OpenData: Send {}

impl<T: Sync> Drop for Registration<T> {
impl<T: FileOperations> Drop for Registration<T> {
/// Removes the registration from the kernel if it has completed successfully before.
fn drop(&mut self) {
if self.registered {
Expand All @@ -142,17 +142,15 @@ impl<T: Sync> Drop for Registration<T> {
}
}

/// Kernel module that exposes a single miscdev device implemented by `F`.
pub struct Module<F: FileOpener<()>> {
_dev: Pin<Box<Registration>>,
_p: PhantomData<F>,
/// Kernel module that exposes a single miscdev device implemented by `T`.
pub struct Module<T: FileOperations<OpenData = ()>> {
_dev: Pin<Box<Registration<T>>>,
}

impl<F: FileOpener<()>> KernelModule for Module<F> {
impl<T: FileOperations<OpenData = ()>> KernelModule for Module<T> {
fn init(name: &'static CStr, _module: &'static ThisModule) -> Result<Self> {
Ok(Self {
_p: PhantomData,
_dev: Registration::new_pinned::<F>(name, None, ())?,
_dev: Registration::new_pinned(name, None, ())?,
})
}
}
Expand Down
7 changes: 5 additions & 2 deletions samples/rust/rust_chrdev.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
#![feature(allocator_api, global_asm)]

use kernel::prelude::*;
use kernel::{chrdev, file_operations::FileOperations};
use kernel::{chrdev, file, file_operations::FileOperations};

module! {
type: RustChrdev,
Expand All @@ -16,11 +16,14 @@ module! {
license: b"GPL v2",
}

#[derive(Default)]
struct RustFile;

impl FileOperations for RustFile {
kernel::declare_file_operations!();

fn open(_shared: &(), _file: &file::File) -> Result<Self::Wrapper> {
Ok(Box::try_new(RustFile)?)
}
}

struct RustChrdev {
Expand Down
18 changes: 8 additions & 10 deletions samples/rust/rust_miscdev.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
use kernel::prelude::*;
use kernel::{
file::File,
file_operations::{FileOpener, FileOperations},
file_operations::FileOperations,
io_buffer::{IoBufferReader, IoBufferWriter},
miscdev,
sync::{CondVar, Mutex, Ref, RefBorrow, UniqueRef},
Expand Down Expand Up @@ -55,18 +55,16 @@ impl SharedState {
}

struct Token;

impl FileOpener<Ref<SharedState>> for Token {
fn open(shared: &Ref<SharedState>, _file: &File) -> Result<Self::Wrapper> {
Ok(shared.clone())
}
}

impl FileOperations for Token {
type Wrapper = Ref<SharedState>;
type OpenData = Ref<SharedState>;

kernel::declare_file_operations!(read, write);

fn open(shared: &Ref<SharedState>, _file: &File) -> Result<Self::Wrapper> {
Ok(shared.clone())
}

fn read(
shared: RefBorrow<'_, SharedState>,
_: &File,
Expand Down Expand Up @@ -127,7 +125,7 @@ impl FileOperations for Token {
}

struct RustMiscdev {
_dev: Pin<Box<miscdev::Registration<Ref<SharedState>>>>,
_dev: Pin<Box<miscdev::Registration<Token>>>,
}

impl KernelModule for RustMiscdev {
Expand All @@ -137,7 +135,7 @@ impl KernelModule for RustMiscdev {
let state = SharedState::try_new()?;

Ok(RustMiscdev {
_dev: miscdev::Registration::new_pinned::<Token>(name, None, state)?,
_dev: miscdev::Registration::new_pinned(name, None, state)?,
})
}
}
Expand Down
Loading

0 comments on commit cd969b5

Please sign in to comment.