Skip to content

Add support for read_iter and write_iter. #231

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
Apr 28, 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
13 changes: 13 additions & 0 deletions rust/helpers.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <linux/sched/signal.h>
#include <linux/gfp.h>
#include <linux/highmem.h>
#include <linux/uio.h>

void rust_helper_BUG(void)
{
Expand Down Expand Up @@ -92,6 +93,18 @@ int rust_helper_cond_resched(void)
}
EXPORT_SYMBOL_GPL(rust_helper_cond_resched);

size_t rust_helper_copy_from_iter(void *addr, size_t bytes, struct iov_iter *i)
{
return copy_from_iter(addr, bytes, i);
}
EXPORT_SYMBOL_GPL(rust_helper_copy_from_iter);

size_t rust_helper_copy_to_iter(const void *addr, size_t bytes, struct iov_iter *i)
{
return copy_to_iter(addr, bytes, i);
}
EXPORT_SYMBOL_GPL(rust_helper_copy_to_iter);

#if !defined(CONFIG_ARM)
// See https://github.com/rust-lang/rust-bindgen/issues/1671
static_assert(__builtin_types_compatible_p(size_t, uintptr_t),
Expand Down
1 change: 1 addition & 0 deletions rust/kernel/bindings_helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <linux/slab.h>
#include <linux/sysctl.h>
#include <linux/uaccess.h>
#include <linux/uio.h>
#include <linux/version.h>
#include <linux/miscdevice.h>
#include <linux/poll.h>
Expand Down
51 changes: 49 additions & 2 deletions rust/kernel/file_operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use crate::{
bindings, c_types,
error::{Error, KernelResult},
io_buffer::{IoBufferReader, IoBufferWriter},
iov_iter::IovIter,
sync::{CondVar, Ref, RefCounted},
user_ptr::{UserSlicePtr, UserSlicePtrReader, UserSlicePtrWriter},
};
Expand Down Expand Up @@ -157,6 +158,21 @@ unsafe extern "C" fn read_callback<T: FileOperations>(
}
}

unsafe extern "C" fn read_iter_callback<T: FileOperations>(
iocb: *mut bindings::kiocb,
raw_iter: *mut bindings::iov_iter,
) -> isize {
from_kernel_result! {
let mut iter = IovIter::from_ptr(raw_iter);
let file = (*iocb).ki_filp;
let offset = (*iocb).ki_pos;
let f = &*((*file).private_data as *const T);
let read = f.read(&File::from_ptr(file), &mut iter, offset.try_into()?)?;
(*iocb).ki_pos += bindings::loff_t::try_from(read).unwrap();
Ok(read as _)
}
}

unsafe extern "C" fn write_callback<T: FileOperations>(
file: *mut bindings::file,
buf: *const c_types::c_char,
Expand All @@ -174,6 +190,21 @@ unsafe extern "C" fn write_callback<T: FileOperations>(
}
}

unsafe extern "C" fn write_iter_callback<T: FileOperations>(
iocb: *mut bindings::kiocb,
raw_iter: *mut bindings::iov_iter,
) -> isize {
from_kernel_result! {
let mut iter = IovIter::from_ptr(raw_iter);
let file = (*iocb).ki_filp;
let offset = (*iocb).ki_pos;
let f = &*((*file).private_data as *const T);
let written = f.write(&File::from_ptr(file), &mut iter, offset.try_into()?)?;
(*iocb).ki_pos += bindings::loff_t::try_from(written).unwrap();
Ok(written as _)
}
}

unsafe extern "C" fn release_callback<T: FileOperations>(
_inode: *mut bindings::inode,
file: *mut bindings::file,
Expand Down Expand Up @@ -323,7 +354,11 @@ impl<A: FileOpenAdapter, T: FileOpener<A::Arg>> FileOperationsVtable<A, T> {
} else {
None
},
read_iter: None,
read_iter: if T::TO_USE.read_iter {
Some(read_iter_callback::<T>)
} else {
None
},
remap_file_range: None,
sendpage: None,
setlease: None,
Expand All @@ -335,7 +370,11 @@ impl<A: FileOpenAdapter, T: FileOpener<A::Arg>> FileOperationsVtable<A, T> {
} else {
None
},
write_iter: None,
write_iter: if T::TO_USE.write_iter {
Some(write_iter_callback::<T>)
} else {
None
},
};

/// Builds an instance of [`struct file_operations`].
Expand All @@ -353,9 +392,15 @@ pub struct ToUse {
/// The `read` field of [`struct file_operations`].
pub read: bool,

/// The `read_iter` field of [`struct file_operations`].
pub read_iter: bool,

/// The `write` field of [`struct file_operations`].
pub write: bool,

/// The `write_iter` field of [`struct file_operations`].
pub write_iter: bool,

/// The `llseek` field of [`struct file_operations`].
pub seek: bool,

Expand All @@ -379,7 +424,9 @@ pub struct ToUse {
/// be set to null pointers.
pub const USE_NONE: ToUse = ToUse {
read: false,
read_iter: false,
write: false,
write_iter: false,
seek: false,
ioctl: false,
compat_ioctl: false,
Expand Down
95 changes: 95 additions & 0 deletions rust/kernel/iov_iter.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// SPDX-License-Identifier: GPL-2.0

//! IO vector iterators.
//!
//! C header: [`include/linux/uio.h`](../../../../include/linux/uio.h)

use crate::{
bindings, c_types,
error::Error,
io_buffer::{IoBufferReader, IoBufferWriter},
KernelResult,
};

extern "C" {
fn rust_helper_copy_to_iter(
addr: *const c_types::c_void,
bytes: usize,
i: *mut bindings::iov_iter,
) -> usize;

fn rust_helper_copy_from_iter(
addr: *mut c_types::c_void,
bytes: usize,
i: *mut bindings::iov_iter,
) -> usize;
}

/// Wraps the kernel's `struct iov_iter`.
///
/// # Invariants
///
/// The pointer [`IovIter::ptr`] is non-null and valid.
pub struct IovIter {
ptr: *mut bindings::iov_iter,
}

impl IovIter {
fn common_len(&self) -> usize {
// SAFETY: `IovIter::ptr` is guaranteed to be valid by the type invariants.
unsafe { (*self.ptr).count }
}

/// Constructs a new [`struct iov_iter`] wrapper.
///
/// # Safety
///
/// The pointer `ptr` must be non-null and valid for the lifetime of the object.
pub(crate) unsafe fn from_ptr(ptr: *mut bindings::iov_iter) -> Self {
// INVARIANTS: the safety contract ensures the type invariant will hold.
Self { ptr }
}
}

impl IoBufferWriter for IovIter {
fn len(&self) -> usize {
self.common_len()
}

fn clear(&mut self, mut len: usize) -> KernelResult {
while len > 0 {
// SAFETY: `IovIter::ptr` is guaranteed to be valid by the type invariants.
let written = unsafe { bindings::iov_iter_zero(len, self.ptr) };
if written == 0 {
return Err(Error::EFAULT);
}

len -= written;
}
Ok(())
}

unsafe fn write_raw(&mut self, data: *const u8, len: usize) -> KernelResult {
let res = rust_helper_copy_to_iter(data as _, len, self.ptr);
if res != len {
Err(Error::EFAULT)
} else {
Ok(())
}
}
}

impl IoBufferReader for IovIter {
fn len(&self) -> usize {
self.common_len()
}

unsafe fn read_raw(&mut self, out: *mut u8, len: usize) -> KernelResult {
let res = rust_helper_copy_from_iter(out as _, len, self.ptr);
if res != len {
Err(Error::EFAULT)
} else {
Ok(())
}
}
}
1 change: 1 addition & 0 deletions rust/kernel/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ pub mod sync;
pub mod sysctl;

pub mod io_buffer;
pub mod iov_iter;
mod types;
pub mod user_ptr;

Expand Down
2 changes: 1 addition & 1 deletion samples/rust/rust_random.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use kernel::{
struct RandomFile;

impl FileOperations for RandomFile {
kernel::declare_file_operations!(read, write);
kernel::declare_file_operations!(read, write, read_iter, write_iter);

fn read<T: IoBufferWriter>(
&self,
Expand Down