From 4f0aeff00e8eb03eb05455881a4b4066e6812b7d Mon Sep 17 00:00:00 2001 From: Adam Bratschi-Kaye Date: Thu, 13 May 2021 11:39:41 +0200 Subject: [PATCH 1/3] seq_file.rs: Rust version of seq_operations Adds a trait which allows Rust modules to implement the seq_operations interface and use it to create a `/proc` file. Signed-off-by: Adam Bratschi-Kaye --- .github/workflows/kernel-arm64-debug.config | 1 + .github/workflows/kernel-arm64-release.config | 1 + .github/workflows/kernel-ppc64le-debug.config | 1 + .../workflows/kernel-ppc64le-release.config | 1 + .github/workflows/kernel-x86_64-debug.config | 1 + rust/kernel/bindings_helper.h | 2 + rust/kernel/lib.rs | 5 + rust/kernel/proc_fs.rs | 101 +++++++++ rust/kernel/seq_file.rs | 196 ++++++++++++++++++ samples/rust/Kconfig | 11 + samples/rust/Makefile | 1 + samples/rust/rust_seq_file.rs | 146 +++++++++++++ 12 files changed, 467 insertions(+) create mode 100644 rust/kernel/proc_fs.rs create mode 100644 rust/kernel/seq_file.rs create mode 100644 samples/rust/rust_seq_file.rs diff --git a/.github/workflows/kernel-arm64-debug.config b/.github/workflows/kernel-arm64-debug.config index f7b4a6d37e8562..1ce416f529a25b 100644 --- a/.github/workflows/kernel-arm64-debug.config +++ b/.github/workflows/kernel-arm64-debug.config @@ -1432,6 +1432,7 @@ CONFIG_SAMPLE_RUST_STACK_PROBING=m CONFIG_SAMPLE_RUST_SEMAPHORE=m CONFIG_SAMPLE_RUST_SEMAPHORE_C=m CONFIG_SAMPLE_RUST_RANDOM=m +CONFIG_SAMPLE_RUST_SEQ_FILE=m # # arm64 Debugging diff --git a/.github/workflows/kernel-arm64-release.config b/.github/workflows/kernel-arm64-release.config index 49b53b9722e689..f25ae555a5a4e8 100644 --- a/.github/workflows/kernel-arm64-release.config +++ b/.github/workflows/kernel-arm64-release.config @@ -1350,6 +1350,7 @@ CONFIG_SAMPLE_RUST_STACK_PROBING=m CONFIG_SAMPLE_RUST_SEMAPHORE=m CONFIG_SAMPLE_RUST_SEMAPHORE_C=m CONFIG_SAMPLE_RUST_RANDOM=m +CONFIG_SAMPLE_RUST_SEQ_FILE=m # # arm64 Debugging diff --git a/.github/workflows/kernel-ppc64le-debug.config b/.github/workflows/kernel-ppc64le-debug.config index 5cee6896c4946b..d994fe795ea8c8 100644 --- a/.github/workflows/kernel-ppc64le-debug.config +++ b/.github/workflows/kernel-ppc64le-debug.config @@ -1492,6 +1492,7 @@ CONFIG_SAMPLE_RUST_STACK_PROBING=m CONFIG_SAMPLE_RUST_SEMAPHORE=m CONFIG_SAMPLE_RUST_SEMAPHORE_C=m CONFIG_SAMPLE_RUST_RANDOM=m +CONFIG_SAMPLE_RUST_SEQ_FILE=m CONFIG_ARCH_HAS_DEVMEM_IS_ALLOWED=y # CONFIG_STRICT_DEVMEM is not set diff --git a/.github/workflows/kernel-ppc64le-release.config b/.github/workflows/kernel-ppc64le-release.config index b7c076cb1ac793..8f8503796cf307 100644 --- a/.github/workflows/kernel-ppc64le-release.config +++ b/.github/workflows/kernel-ppc64le-release.config @@ -1454,6 +1454,7 @@ CONFIG_SAMPLE_RUST_STACK_PROBING=m CONFIG_SAMPLE_RUST_SEMAPHORE=m CONFIG_SAMPLE_RUST_SEMAPHORE_C=m CONFIG_SAMPLE_RUST_RANDOM=m +CONFIG_SAMPLE_RUST_SEQ_FILE=m CONFIG_ARCH_HAS_DEVMEM_IS_ALLOWED=y # CONFIG_STRICT_DEVMEM is not set diff --git a/.github/workflows/kernel-x86_64-debug.config b/.github/workflows/kernel-x86_64-debug.config index 05ae0d5af161dc..0afe90bf2bbd08 100644 --- a/.github/workflows/kernel-x86_64-debug.config +++ b/.github/workflows/kernel-x86_64-debug.config @@ -1444,6 +1444,7 @@ CONFIG_SAMPLE_RUST_STACK_PROBING=m CONFIG_SAMPLE_RUST_SEMAPHORE=m CONFIG_SAMPLE_RUST_SEMAPHORE_C=m CONFIG_SAMPLE_RUST_RANDOM=m +CONFIG_SAMPLE_RUST_SEQ_FILE=m CONFIG_ARCH_HAS_DEVMEM_IS_ALLOWED=y # CONFIG_STRICT_DEVMEM is not set diff --git a/rust/kernel/bindings_helper.h b/rust/kernel/bindings_helper.h index 148c75a902bb62..ed2fd50053fa07 100644 --- a/rust/kernel/bindings_helper.h +++ b/rust/kernel/bindings_helper.h @@ -4,7 +4,9 @@ #include #include #include +#include #include +#include #include #include #include diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 45467e38e2d3d5..03b44131240270 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -62,7 +62,12 @@ pub mod module_param; mod build_assert; pub mod prelude; pub mod print; + +#[cfg(CONFIG_PROC_FS)] +pub mod proc_fs; + pub mod random; +pub mod seq_file; mod static_assert; pub mod sync; diff --git a/rust/kernel/proc_fs.rs b/rust/kernel/proc_fs.rs new file mode 100644 index 00000000000000..a9429c61f66d0c --- /dev/null +++ b/rust/kernel/proc_fs.rs @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Type for defining `proc` files. +//! +//! This module allows Rust devices to create entries in `/proc` from a +//! [`bindings::proc_ops`] vtable. +//! +//! C header: [`include/linux/proc_fs.h`](../../../include/linux/proc_fs.h) +//! +//! Reference: + +use core::{ + marker::{PhantomData, Sync}, + ptr, +}; + +use crate::{ + bindings, c_types, + seq_file::{SeqFileOperationsVTable, SeqOperations}, + str::CStr, + types::PointerWrapper, + Error, Result, +}; + +/// An entry under `/proc` containing data of type `T`. +/// +/// This is the Rust equivalent to [`proc_dir_entry`] on the C side. +/// +/// # Invariants +/// +/// The [`ProcDirEntry::proc_dir_entry`] is a valid pointer. +/// [`ProcDirEntry::data`] points to the PDE data of +/// [`ProcDirEntry::proc_dir_entry`]. +/// [`ProcDirEntry::data`] was created by a call to `T::into_pointer`. +/// +/// [`proc_dir_entry`]: ../../../fs/proc/internal.h +pub struct ProcDirEntry { + proc_dir_entry: *mut bindings::proc_dir_entry, + data: *const c_types::c_void, + _wrapper: PhantomData, +} + +// SAFETY: The `proc_dir_entry` and `data` raw pointers aren't accessible. +unsafe impl Sync for ProcDirEntry {} + +impl Drop for ProcDirEntry { + fn drop(&mut self) { + // SAFETY: Calling a C function. `proc_dir_entry` is a valid pointer to + // a `bindings::proc_dir_entry` because it was created by a call to + // `proc_create_data` which only returns valid pointers. + unsafe { + bindings::proc_remove(self.proc_dir_entry); + } + // SAFETY: `self.data` was created by a call to `T::into_pointer`. + unsafe { drop(T::from_pointer(self.data)) } + } +} + +impl ProcDirEntry { + /// Create a seq_file entry in `/proc` containing data of type `S`. + /// + /// Corresponds to [`proc_create_seq_private`] on the C side. + /// + /// [`proc_create_seq_private`]: ../../../fs/proc/generic.c + pub fn new_seq_private(name: &CStr, data: T) -> Result + where + S: SeqOperations, + { + let data = data.into_pointer(); + let name = name.as_char_ptr(); + + // SAFETY: Calling a C function. The vtable for `S` expects a + // `S::DataWrapper = T` pointer in the data field of the associated + // `proc_dir_entry`. `name` is guaranteed to be null terminated + // because it is of type `CStr`. + let proc_dir_entry = unsafe { + bindings::proc_create_seq_private( + name, + 0, + ptr::null_mut(), + SeqFileOperationsVTable::::build(), + 0, + data as *mut c_types::c_void, + ) + }; + if proc_dir_entry.is_null() { + // SAFETY: `data` was created with a call to `T::into_pointer`. + drop(unsafe { T::from_pointer(data) }); + Err(Error::ENOMEM) + } else { + // INVARIANT: `proc_dir_entry` is a valid pointer. + // The `data` points to the data stored in `proc_dir_entry`, and + // `data` was created by `T::into_pointer`. + Ok(ProcDirEntry { + proc_dir_entry, + data, + _wrapper: PhantomData, + }) + } + } +} diff --git a/rust/kernel/seq_file.rs b/rust/kernel/seq_file.rs new file mode 100644 index 00000000000000..031605c490d3bc --- /dev/null +++ b/rust/kernel/seq_file.rs @@ -0,0 +1,196 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Trait for defining `seq_file`s. +//! +//! This module allows Rust devices to implement [`struct seq_operations`] and +//! and create a file under `/proc` based on that implementation. +//! +//! C header: [`include/linux/seq_file.h`](../../../include/linux/seq_file.h) +//! +//! Reference: + +// Currently this module is only usable through proc_fs. +#![cfg(CONFIG_PROC_FS)] + +use core::{ + iter::{Iterator, Peekable}, + marker::PhantomData, + mem, + ops::{Deref, DerefMut}, + ptr, +}; + +use crate::{bindings, c_str, c_types, str::CStr, types::PointerWrapper, Result}; + +/// Rust equivalent of the [`seq_operations`] interface on the C side. +/// +/// # Example +/// +/// ```rust,no_run +/// #![feature(allocator_api)] +/// +/// use core::iter::Peekable; +/// use kernel::{Error, Result, seq_file}; +/// +/// struct Data(&'static [String]); +/// +/// impl seq_file::SeqOperations for Data { +/// type Item = &'static String; +/// type Iterator = core::slice::Iter<'static, String>; +/// type DataWrapper = Box; +/// type IteratorWrapper = Box>; +/// +/// fn start(&self) -> Result { +/// let iter = self.0.iter(); +/// Box::try_new(iter.peekable()).map_err(|_| Error::ENOMEM) +/// } +/// +/// fn display(item: &Self::Item) -> &str { +/// &item[..] +/// } +/// } +/// ``` +/// +/// [`seq_operations`]: ../../../include/linux/seq_file.h +pub trait SeqOperations { + /// Type produced on each iteration. + type Item; + + /// Type created when the seq file is opened. + type Iterator: Iterator; + + /// Wrapper used to store a pointer to `Self` on the C side. + type DataWrapper: PointerWrapper + Deref; + + /// Wrapper used to store a pointer to the iterator on the C side. + type IteratorWrapper: PointerWrapper + DerefMut>; + + /// Called once each time the `seq_file` is opened. + fn start(&self) -> Result; + + /// How the item will be displayed to the reader. + fn display(item: &Self::Item) -> &str; +} + +extern "C" fn stop_callback( + _m: *mut bindings::seq_file, + v: *mut c_types::c_void, +) { + if !v.is_null() { + // SAFETY: `v` was created by a previous call to `next_callback` or + // `start_callback` and both functions return either a null pointer + // or pointer generated by `T::IteratorWrapper::into_pointer`. + drop(unsafe { T::IteratorWrapper::from_pointer(v) }) + } +} + +extern "C" fn next_callback( + _m: *mut bindings::seq_file, + v: *mut c_types::c_void, + pos: *mut bindings::loff_t, +) -> *mut c_types::c_void { + if v.is_null() { + return ptr::null_mut(); + } + + // SAFETY: `v` was created by a previous call to `next_callback` or + // `start_callback` and both functions return either a null pointer + // or pointer generated by `T::IteratorWrapper::into_pointer`. + // We already checked for he null pointer case above. + let mut iterator = unsafe { T::IteratorWrapper::from_pointer(v) }; + + // SAFETY: The caller guarantees tha `pos` is a valid pointer to an + // `loff_t` and expects this function to mutate the value. + unsafe { + *pos += 1; + } + + if iterator.next().is_none() { + return ptr::null_mut(); + } + + match iterator.peek() { + Some(_next) => T::IteratorWrapper::into_pointer(iterator) as *mut _, + None => ptr::null_mut(), + } +} + +extern "C" fn show_callback( + m: *mut bindings::seq_file, + v: *mut c_types::c_void, +) -> c_types::c_int { + const FORMAT: &CStr = c_str!("%.*s"); + if v.is_null() { + return 0; + } + // SAFETY: `v` was created by a previous call to `next_callback` or + // `start_callback` and both functions return either a null pointer + // or pointer generated by `T::IteratorWrapper::into_pointer`. We + // checked for null pointers above. The iterator is forgotten below + // so the pointer on the C side stays valid. + let mut iterator = unsafe { T::IteratorWrapper::from_pointer(v) }; + if let Some(item) = iterator.peek() { + let s = T::display(item); + // SAFETY: Calling a C function. `FORMAT` is null terminated because + // it comes from a `CStr`. `s` does not need to be null terminated + // because we are only printing the first `s.len()` bytes. + unsafe { + bindings::seq_printf( + m, + FORMAT.as_char_ptr(), + s.len(), + s.as_ptr() as *const u8 as *const c_types::c_char, + ); + } + } + // Need to forget the iterator because the C side is still using a + // reference to it. + mem::forget(iterator); + 0 +} + +extern "C" fn start_callback( + m: *mut bindings::seq_file, + pos: *mut bindings::loff_t, +) -> *mut c_types::c_void { + // SAFETY: This function will be called by opening a proc file generated + // from `proc_create_seq_private` on the C side with data created via + // `T::DataWrapper::into_pointer`. We don't move the data in the wrapper + // so the pointer will remain valid for later calls. + let data_wrapper = + unsafe { T::DataWrapper::from_pointer(bindings::PDE_DATA((*(*m).file).f_inode)) }; + let iterator = data_wrapper.start().ok(); + // Data is still used in the `proc_dir_entry`. + mem::forget(data_wrapper); + // SAFETY: The caller guarantees that `pos` points to a valid `loff_t`. + let pos = unsafe { *pos }; + match iterator { + Some(mut wrapper) => { + for _ in 0..pos { + if wrapper.next().is_none() { + return ptr::null_mut(); + } + } + match wrapper.peek() { + Some(_next) => T::IteratorWrapper::into_pointer(wrapper) as *mut _, + None => ptr::null_mut(), + } + } + None => ptr::null_mut(), + } +} + +pub(crate) struct SeqFileOperationsVTable(PhantomData); + +impl SeqFileOperationsVTable { + const VTABLE: bindings::seq_operations = bindings::seq_operations { + start: Some(start_callback::), + stop: Some(stop_callback::), + next: Some(next_callback::), + show: Some(show_callback::), + }; + + pub(crate) const fn build() -> &'static bindings::seq_operations { + &Self::VTABLE + } +} diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig index 183a3c4dc80cd7..2a42d1a16f1a30 100644 --- a/samples/rust/Kconfig +++ b/samples/rust/Kconfig @@ -110,4 +110,15 @@ config SAMPLE_RUST_RANDOM If unsure, say N. +config SAMPLE_RUST_SEQ_FILE + tristate "Seq file" + depends on PROC_FS + help + This option builds the Rust seq_file sample. + + To compile this as a module, choose M here: + the module will be called rust_seq_file. + + If unsure, say N. + endif # SAMPLES_RUST diff --git a/samples/rust/Makefile b/samples/rust/Makefile index 48bc871ea1f8ff..b95e25dfa9099a 100644 --- a/samples/rust/Makefile +++ b/samples/rust/Makefile @@ -10,3 +10,4 @@ obj-$(CONFIG_SAMPLE_RUST_STACK_PROBING) += rust_stack_probing.o obj-$(CONFIG_SAMPLE_RUST_SEMAPHORE) += rust_semaphore.o obj-$(CONFIG_SAMPLE_RUST_SEMAPHORE_C) += rust_semaphore_c.o obj-$(CONFIG_SAMPLE_RUST_RANDOM) += rust_random.o +obj-$(CONFIG_SAMPLE_RUST_SEQ_FILE) += rust_seq_file.o diff --git a/samples/rust/rust_seq_file.rs b/samples/rust/rust_seq_file.rs new file mode 100644 index 00000000000000..c2611b8bd7a47a --- /dev/null +++ b/samples/rust/rust_seq_file.rs @@ -0,0 +1,146 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Example of using a [`seq_file`] in Rust. +//! +//! C header: [`include/linux/seq_file.h`](../../../include/linux/seq_file.h) +//! C header: [`include/linux/proc_fs.h`](../../../include/linux/proc_fs.h) +//! +//! Reference: + +#![no_std] +#![feature(allocator_api, global_asm, try_reserve)] + +use alloc::boxed::Box; +use core::{ + cmp::min, + convert::TryInto, + fmt::Write, + iter::{repeat, Peekable, Repeat, Take}, + pin::Pin, +}; +use kernel::{ + c_str, + file::File, + file_operations::{FileOpener, FileOperations}, + io_buffer::IoBufferWriter, + miscdev, mutex_init, + prelude::*, + proc_fs, seq_file, + sync::{Mutex, Ref}, + Error, Result, +}; + +module! { + type: RustSeqFileDev, + name: b"rust_seq_file", + author: b"Adam Bratschi-Kaye", + description: b"Rust sample using a seq_file", + license: b"GPL v2", +} + +struct State(Mutex); + +impl State { + fn try_new() -> Result>> { + Ok(Ref::pinned(Ref::try_new_and_init( + unsafe { State(Mutex::new(0)) }, + |mut state| { + // SAFETY: Mutex is pinned behind `Ref`. + let pin_state = unsafe { state.as_mut().map_unchecked_mut(|s| &mut s.0) }; + mutex_init!(pin_state, "State::0"); + }, + )?)) + } +} + +impl seq_file::SeqOperations for State { + type Item = String; + type Iterator = Take>; + type DataWrapper = Pin>; + type IteratorWrapper = Box>; + + fn display(item: &Self::Item) -> &str { + &item[..] + } + + fn start(&self) -> Result { + const MAX_DIGITS: usize = 3; + const MAX_LENGTH: usize = MAX_DIGITS + 1; + const MAX_COUNT: u32 = 10u32.pow(MAX_DIGITS as u32) - 1; + + let count = self.0.lock(); + let mut message = String::new(); + + let template = if *count <= MAX_COUNT { + "rust_seq_file: device opened this many times: " + } else { + "rust_seq_file: device opened at least this many times: " + }; + message.try_reserve_exact(template.len() + MAX_LENGTH)?; + // NOPANIC: We reserved space for `template` above. + message.push_str(template); + let message_count = min(*count, MAX_COUNT); + // NOPANIC: There are `MAX_LENGTH` characters remaining in the string which + // leaves space for a `MAX_DIGITS` digit number and the newline. + // `message_count` is `<= MAX_COUNT` means it has less than `MAX_DIGITS` + // digits. + writeln!(&mut message, "{}", message_count).map_err(|_| Error::ENOMEM)?; + + Box::try_new(repeat(message).take((*count).try_into()?).peekable()) + .map_err(|_| Error::ENOMEM) + } +} + +struct Token; + +impl FileOpener>> for Token { + fn open(ctx: &Pin>) -> Result { + pr_info!("rust seq_file was opened!\n"); + Ok(ctx.clone()) + } +} + +impl FileOperations for Token { + kernel::declare_file_operations!(read); + + type Wrapper = Pin>; + + fn read(shared: &Ref, _: &File, _: &mut T, _: u64) -> Result { + *(shared.0.lock()) += 1; + Ok(0) + } +} + +struct RustSeqFileDev { + _proc: proc_fs::ProcDirEntry>>, + _dev: Pin>>>>, +} + +impl KernelModule for RustSeqFileDev { + fn init() -> Result { + pr_info!("Rust seq_file sample (init)\n"); + + let state = State::try_new()?; + + let proc_dir_entry = proc_fs::ProcDirEntry::new_seq_private::( + c_str!("rust_seq_file"), + state.clone(), + )?; + + let dev_reg = + miscdev::Registration::new_pinned::(c_str!("rust_seq_file"), None, state)?; + + let dev = RustSeqFileDev { + _proc: proc_dir_entry, + _dev: dev_reg, + }; + + Ok(dev) + } +} + +impl Drop for RustSeqFileDev { + fn drop(&mut self) { + pr_info!("Rust seq_file sample (exit)\n"); + } +} From 2fc05b510fdd0830ead04d3d882f6eee7df0e659 Mon Sep 17 00:00:00 2001 From: Adam Bratschi-Kaye Date: Thu, 13 May 2021 11:41:20 +0200 Subject: [PATCH 2/3] CI/CD changes for testing seq_file.rs Signed-off-by: Adam Bratschi-Kaye --- .github/workflows/busybox.config | 14 +++++++------- .github/workflows/ci.yaml | 5 +++++ .github/workflows/kernel-arm-debug.config | 1 + .github/workflows/kernel-arm-release.config | 1 + .github/workflows/kernel-arm64-debug.config | 2 +- .github/workflows/kernel-arm64-release.config | 2 +- .github/workflows/kernel-riscv64-debug.config | 1 + .github/workflows/kernel-riscv64-release.config | 1 + .github/workflows/kernel-x86_64-debug.config | 2 +- .github/workflows/kernel-x86_64-release.config | 1 + .github/workflows/qemu-init.sh | 11 +++++++++++ .github/workflows/qemu-initramfs.desc | 1 + 12 files changed, 32 insertions(+), 10 deletions(-) diff --git a/.github/workflows/busybox.config b/.github/workflows/busybox.config index c4175a522a3a60..81981ec17d1727 100644 --- a/.github/workflows/busybox.config +++ b/.github/workflows/busybox.config @@ -196,7 +196,7 @@ CONFIG_GZIP_FAST=0 # Coreutils # # CONFIG_BASENAME is not set -# CONFIG_CAT is not set +CONFIG_CAT=y # CONFIG_FEATURE_CATN is not set # CONFIG_FEATURE_CATV is not set # CONFIG_CHGRP is not set @@ -209,7 +209,7 @@ CONFIG_GZIP_FAST=0 # CONFIG_CP is not set # CONFIG_FEATURE_CP_LONG_OPTIONS is not set # CONFIG_FEATURE_CP_REFLINK is not set -# CONFIG_CUT is not set +CONFIG_CUT=y # CONFIG_DATE is not set # CONFIG_FEATURE_DATE_ISOFMT is not set # CONFIG_FEATURE_DATE_NANO is not set @@ -263,9 +263,9 @@ CONFIG_GZIP_FAST=0 # CONFIG_SHA512SUM is not set # CONFIG_SHA3SUM is not set # CONFIG_FEATURE_MD5_SHA1_SUM_CHECK is not set -# CONFIG_MKDIR is not set +CONFIG_MKDIR=y # CONFIG_MKFIFO is not set -# CONFIG_MKNOD is not set +CONFIG_MKNOD=y # CONFIG_MKTEMP is not set # CONFIG_MV is not set # CONFIG_NICE is not set @@ -280,7 +280,7 @@ CONFIG_GZIP_FAST=0 # CONFIG_READLINK is not set # CONFIG_FEATURE_READLINK_FOLLOW is not set # CONFIG_REALPATH is not set -# CONFIG_RM is not set +CONFIG_RM=y # CONFIG_RMDIR is not set # CONFIG_SEQ is not set # CONFIG_SHRED is not set @@ -449,7 +449,7 @@ CONFIG_FEATURE_VI_UNDO_QUEUE_MAX=0 # CONFIG_FEATURE_FIND_REGEX is not set # CONFIG_FEATURE_FIND_CONTEXT is not set # CONFIG_FEATURE_FIND_LINKS is not set -# CONFIG_GREP is not set +CONFIG_GREP=y # CONFIG_EGREP is not set # CONFIG_FGREP is not set # CONFIG_FEATURE_GREP_CONTEXT is not set @@ -635,7 +635,7 @@ CONFIG_DEFAULT_DEPMOD_FILE="modules.dep" # CONFIG_MKSWAP is not set # CONFIG_FEATURE_MKSWAP_UUID is not set # CONFIG_MORE is not set -# CONFIG_MOUNT is not set +CONFIG_MOUNT=y # CONFIG_FEATURE_MOUNT_FAKE is not set # CONFIG_FEATURE_MOUNT_VERBOSE is not set # CONFIG_FEATURE_MOUNT_HELPERS is not set diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index ff2de145d5e8eb..88acdd13effa6b 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -375,6 +375,11 @@ jobs: grep '] rust_semaphore_c: Rust semaphore sample (in C, for comparison) (init)$' qemu-stdout.log grep '] rust_semaphore_c: Rust semaphore sample (in C, for comparison) (exit)$' qemu-stdout.log + - run: | + grep '] rust_seq_file: Rust seq_file sample (init)$' qemu-stdout.log + grep '] rust_seq_file: Rust seq_file sample (exit)$' qemu-stdout.log + test $(grep -c 'rust_seq_file: device opened this many times: 2' qemu-stdout.log) -eq 2 + # Report - run: | ls -l \ diff --git a/.github/workflows/kernel-arm-debug.config b/.github/workflows/kernel-arm-debug.config index 8f72fd45fde2d5..06e43f285ebdc2 100644 --- a/.github/workflows/kernel-arm-debug.config +++ b/.github/workflows/kernel-arm-debug.config @@ -1780,6 +1780,7 @@ CONFIG_SAMPLE_RUST_STACK_PROBING=m CONFIG_SAMPLE_RUST_SEMAPHORE=m CONFIG_SAMPLE_RUST_SEMAPHORE_C=m CONFIG_SAMPLE_RUST_RANDOM=m +CONFIG_SAMPLE_RUST_SEQ_FILE=m # CONFIG_STRICT_DEVMEM is not set # diff --git a/.github/workflows/kernel-arm-release.config b/.github/workflows/kernel-arm-release.config index afa7b02dd464e2..f8f752c398910b 100644 --- a/.github/workflows/kernel-arm-release.config +++ b/.github/workflows/kernel-arm-release.config @@ -1704,6 +1704,7 @@ CONFIG_SAMPLE_RUST_STACK_PROBING=m CONFIG_SAMPLE_RUST_SEMAPHORE=m CONFIG_SAMPLE_RUST_SEMAPHORE_C=m CONFIG_SAMPLE_RUST_RANDOM=m +CONFIG_SAMPLE_RUST_SEQ_FILE=m # CONFIG_STRICT_DEVMEM is not set # diff --git a/.github/workflows/kernel-arm64-debug.config b/.github/workflows/kernel-arm64-debug.config index 1ce416f529a25b..36fb9b6af493d0 100644 --- a/.github/workflows/kernel-arm64-debug.config +++ b/.github/workflows/kernel-arm64-debug.config @@ -1069,7 +1069,7 @@ CONFIG_DCACHE_WORD_ACCESS=y # # Pseudo filesystems # -# CONFIG_PROC_FS is not set +CONFIG_PROC_FS=y # CONFIG_PROC_CHILDREN is not set # CONFIG_SYSFS is not set # CONFIG_HUGETLBFS is not set diff --git a/.github/workflows/kernel-arm64-release.config b/.github/workflows/kernel-arm64-release.config index f25ae555a5a4e8..1cf87b3ba433e1 100644 --- a/.github/workflows/kernel-arm64-release.config +++ b/.github/workflows/kernel-arm64-release.config @@ -1064,7 +1064,7 @@ CONFIG_DCACHE_WORD_ACCESS=y # # Pseudo filesystems # -# CONFIG_PROC_FS is not set +CONFIG_PROC_FS=y # CONFIG_PROC_CHILDREN is not set # CONFIG_SYSFS is not set # CONFIG_HUGETLBFS is not set diff --git a/.github/workflows/kernel-riscv64-debug.config b/.github/workflows/kernel-riscv64-debug.config index b4e3c4fcd424f9..24f4c8ef342f62 100644 --- a/.github/workflows/kernel-riscv64-debug.config +++ b/.github/workflows/kernel-riscv64-debug.config @@ -1286,6 +1286,7 @@ CONFIG_SAMPLE_RUST_STACK_PROBING=m CONFIG_SAMPLE_RUST_SEMAPHORE=m CONFIG_SAMPLE_RUST_SEMAPHORE_C=m CONFIG_SAMPLE_RUST_RANDOM=m +CONFIG_SAMPLE_RUST_SEQ_FILE=m # CONFIG_STRICT_DEVMEM is not set # diff --git a/.github/workflows/kernel-riscv64-release.config b/.github/workflows/kernel-riscv64-release.config index c0c43ade16999e..d40bb8eb3975ba 100644 --- a/.github/workflows/kernel-riscv64-release.config +++ b/.github/workflows/kernel-riscv64-release.config @@ -1202,6 +1202,7 @@ CONFIG_SAMPLE_RUST_STACK_PROBING=m CONFIG_SAMPLE_RUST_SEMAPHORE=m CONFIG_SAMPLE_RUST_SEMAPHORE_C=m CONFIG_SAMPLE_RUST_RANDOM=m +CONFIG_SAMPLE_RUST_SEQ_FILE=m # CONFIG_STRICT_DEVMEM is not set # diff --git a/.github/workflows/kernel-x86_64-debug.config b/.github/workflows/kernel-x86_64-debug.config index 0afe90bf2bbd08..e951807a31737b 100644 --- a/.github/workflows/kernel-x86_64-debug.config +++ b/.github/workflows/kernel-x86_64-debug.config @@ -1059,7 +1059,7 @@ CONFIG_DCACHE_WORD_ACCESS=y # # Pseudo filesystems # -# CONFIG_PROC_FS is not set +CONFIG_PROC_FS=y # CONFIG_PROC_CHILDREN is not set CONFIG_KERNFS=y CONFIG_SYSFS=y diff --git a/.github/workflows/kernel-x86_64-release.config b/.github/workflows/kernel-x86_64-release.config index f581b18e760500..33e402072a884f 100644 --- a/.github/workflows/kernel-x86_64-release.config +++ b/.github/workflows/kernel-x86_64-release.config @@ -1392,6 +1392,7 @@ CONFIG_SAMPLE_RUST_STACK_PROBING=m CONFIG_SAMPLE_RUST_SEMAPHORE=m CONFIG_SAMPLE_RUST_SEMAPHORE_C=m CONFIG_SAMPLE_RUST_RANDOM=m +CONFIG_SAMPLE_RUST_SEQ_FILE=m CONFIG_ARCH_HAS_DEVMEM_IS_ALLOWED=y # CONFIG_STRICT_DEVMEM is not set diff --git a/.github/workflows/qemu-init.sh b/.github/workflows/qemu-init.sh index 078de0887c6dba..fe1f9cddd1aa01 100755 --- a/.github/workflows/qemu-init.sh +++ b/.github/workflows/qemu-init.sh @@ -37,4 +37,15 @@ busybox insmod rust_module_parameters_loadable_custom.ko \ busybox rmmod rust_module_parameters_loadable_default.ko busybox rmmod rust_module_parameters_loadable_custom.ko +busybox insmod rust_seq_file.ko +busybox mkdir proc +busybox mount -t proc proc /proc +export RUST_SEQ_MINOR=$(busybox cat /proc/misc | busybox grep rust_seq_file | busybox cut -d ' ' -f 1) +busybox mknod /dev/rust_seq_file0 c 10 $RUST_SEQ_MINOR +busybox cat /dev/rust_seq_file0 +busybox cat /dev/rust_seq_file0 +busybox cat /proc/rust_seq_file +busybox rm /dev/rust_seq_file0 +busybox rmmod rust_seq_file.ko + busybox reboot -f diff --git a/.github/workflows/qemu-initramfs.desc b/.github/workflows/qemu-initramfs.desc index 5fdcff849dc004..99a828e045d5db 100644 --- a/.github/workflows/qemu-initramfs.desc +++ b/.github/workflows/qemu-initramfs.desc @@ -14,6 +14,7 @@ file /rust_miscdev.ko samples/rust/rust_miscdev.ko 0755 file /rust_stack_probing.ko samples/rust/rust_stack_probing.ko 0755 0 0 file /rust_semaphore.ko samples/rust/rust_semaphore.ko 0755 0 0 file /rust_semaphore_c.ko samples/rust/rust_semaphore_c.ko 0755 0 0 +file /rust_seq_file.ko samples/rust/rust_seq_file.ko 0755 0 0 file /rust_module_parameters_loadable_default.ko samples/rust/rust_module_parameters_loadable_default.ko 0755 0 0 file /rust_module_parameters_loadable_custom.ko samples/rust/rust_module_parameters_loadable_custom.ko 0755 0 0 From 55813d497b7952dbcbb450ee467c2a7ad6e44165 Mon Sep 17 00:00:00 2001 From: Adam Bratschi-Kaye Date: Mon, 28 Jun 2021 23:10:09 +0200 Subject: [PATCH 3/3] seq_file.rs: Borrow data from C pointer. Instead of taking ownership of the data from the C side, borrow it using the new `PointerWrapper` interface. Signed-off-by: Adam Bratschi-Kaye --- rust/kernel/lib.rs | 2 +- rust/kernel/seq_file.rs | 37 +++++++++++++++++++---------------- samples/rust/rust_seq_file.rs | 6 +++--- 3 files changed, 24 insertions(+), 21 deletions(-) diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 03b44131240270..cb9c1b1782f84c 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -85,7 +85,7 @@ pub mod user_ptr; pub use build_error::build_error; pub use crate::error::{Error, Result}; -pub use crate::types::{Mode, ScopeGuard}; +pub use crate::types::{Mode, ScopeGuard, UnsafeReference}; /// Page size defined in terms of the `PAGE_SHIFT` macro from C. /// diff --git a/rust/kernel/seq_file.rs b/rust/kernel/seq_file.rs index 031605c490d3bc..1f1e4439292bc3 100644 --- a/rust/kernel/seq_file.rs +++ b/rust/kernel/seq_file.rs @@ -16,7 +16,7 @@ use core::{ iter::{Iterator, Peekable}, marker::PhantomData, mem, - ops::{Deref, DerefMut}, + ops::DerefMut, ptr, }; @@ -30,7 +30,7 @@ use crate::{bindings, c_str, c_types, str::CStr, types::PointerWrapper, Result}; /// #![feature(allocator_api)] /// /// use core::iter::Peekable; -/// use kernel::{Error, Result, seq_file}; +/// use kernel::{Error, Result, seq_file, UnsafeReference}; /// /// struct Data(&'static [String]); /// @@ -40,8 +40,8 @@ use crate::{bindings, c_str, c_types, str::CStr, types::PointerWrapper, Result}; /// type DataWrapper = Box; /// type IteratorWrapper = Box>; /// -/// fn start(&self) -> Result { -/// let iter = self.0.iter(); +/// fn start(data: UnsafeReference) -> Result { +/// let iter = data.0.iter(); /// Box::try_new(iter.peekable()).map_err(|_| Error::ENOMEM) /// } /// @@ -56,17 +56,20 @@ pub trait SeqOperations { /// Type produced on each iteration. type Item; - /// Type created when the seq file is opened. + /// Type created when the seq file is read and then iterated through to + /// produce the file's contents. type Iterator: Iterator; - /// Wrapper used to store a pointer to `Self` on the C side. - type DataWrapper: PointerWrapper + Deref; + /// Wrapper used to store a pointer to the data on the C side. + type DataWrapper: PointerWrapper; /// Wrapper used to store a pointer to the iterator on the C side. type IteratorWrapper: PointerWrapper + DerefMut>; - /// Called once each time the `seq_file` is opened. - fn start(&self) -> Result; + /// Called once each time the `seq_file` is read. + fn start( + data: ::Borrowed, + ) -> Result; /// How the item will be displayed to the reader. fn display(item: &Self::Item) -> &str; @@ -153,15 +156,15 @@ extern "C" fn start_callback( m: *mut bindings::seq_file, pos: *mut bindings::loff_t, ) -> *mut c_types::c_void { - // SAFETY: This function will be called by opening a proc file generated + // SAFETY: This function will be called by reading a proc file generated // from `proc_create_seq_private` on the C side with data created via - // `T::DataWrapper::into_pointer`. We don't move the data in the wrapper - // so the pointer will remain valid for later calls. - let data_wrapper = - unsafe { T::DataWrapper::from_pointer(bindings::PDE_DATA((*(*m).file).f_inode)) }; - let iterator = data_wrapper.start().ok(); - // Data is still used in the `proc_dir_entry`. - mem::forget(data_wrapper); + // `T::DataWrapper::into_pointer`. The data will not be destroyed until + // the proc_dir_entry is destroyed and that operation will wait for all + // readers to finish, so the data will be valid for the entire borrow. No + // mutable borrows will be created because this is the only point at which + // the data is accessed (other than when it is freed). + let data = unsafe { T::DataWrapper::borrow(bindings::PDE_DATA((*(*m).file).f_inode)) }; + let iterator = T::start(data).ok(); // SAFETY: The caller guarantees that `pos` points to a valid `loff_t`. let pos = unsafe { *pos }; match iterator { diff --git a/samples/rust/rust_seq_file.rs b/samples/rust/rust_seq_file.rs index c2611b8bd7a47a..989a82121f8b10 100644 --- a/samples/rust/rust_seq_file.rs +++ b/samples/rust/rust_seq_file.rs @@ -26,7 +26,7 @@ use kernel::{ miscdev, mutex_init, prelude::*, proc_fs, seq_file, - sync::{Mutex, Ref}, + sync::{Mutex, Ref, RefBorrow}, Error, Result, }; @@ -63,12 +63,12 @@ impl seq_file::SeqOperations for State { &item[..] } - fn start(&self) -> Result { + fn start(state: RefBorrow) -> Result { const MAX_DIGITS: usize = 3; const MAX_LENGTH: usize = MAX_DIGITS + 1; const MAX_COUNT: u32 = 10u32.pow(MAX_DIGITS as u32) - 1; - let count = self.0.lock(); + let count = state.0.lock(); let mut message = String::new(); let template = if *count <= MAX_COUNT {