From d625dab7e50f310d828fc818d20d923a8c92e9e9 Mon Sep 17 00:00:00 2001 From: Adam Bratschi-Kaye Date: Thu, 13 May 2021 11:39:41 +0200 Subject: [PATCH 1/7] 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 9fa042b1e1dbc5..c8345fb779343d 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 4fc6d8e140accf..7e25e135c37f2d 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 e55f709d006b13..eb8b1184d2be8f 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 ac094bd0eba335..6b04e03c67b708 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 0d882c5d8f5a81..cb1179322c2f02 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 a79f3f398b9369..11333971de5739 100644 --- a/rust/kernel/bindings_helper.h +++ b/rust/kernel/bindings_helper.h @@ -5,7 +5,9 @@ #include #include #include +#include #include +#include #include #include #include diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 2209f779fa99b6..3fd38ae82c705e 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -74,7 +74,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; #[doc(hidden)] pub mod std_vendor; 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 e234be7c341ca1..216bb22c8e9663 100644 --- a/samples/rust/Kconfig +++ b/samples/rust/Kconfig @@ -120,4 +120,15 @@ config SAMPLE_RUST_PLATFORM 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 96ffeb7d334ac6..c54ea9968d052f 100644 --- a/samples/rust/Makefile +++ b/samples/rust/Makefile @@ -11,3 +11,4 @@ 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_PLATFORM) += rust_platform.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 80907c078e70dd36b3a5defd95e6f6fce3e6d0ce Mon Sep 17 00:00:00 2001 From: Adam Bratschi-Kaye Date: Thu, 13 May 2021 11:41:20 +0200 Subject: [PATCH 2/7] 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 c616881090154b..d602e1d6ba816a 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -360,6 +360,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: | cat ${{ env.BUILD_DIR }}.config diff --git a/.github/workflows/kernel-arm-debug.config b/.github/workflows/kernel-arm-debug.config index 8fe33e2c0e167d..256eff9fcee553 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 7f2f8a9ac9b0d1..476fa41ad70145 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 c8345fb779343d..ac48354017a49d 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 7e25e135c37f2d..984f5e3bfc46f4 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 040942c451a96a..c166404450cd50 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 242153977e16d7..bc7ded4bddad64 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 cb1179322c2f02..798d384b11371c 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 dab8ac918dc953..6e079e12f2bc91 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 0268b475dde56d..7dac4eccb068b3 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 051aaa72fa499521c8d8865c151f3fef87925f46 Mon Sep 17 00:00:00 2001 From: Adam Bratschi-Kaye Date: Fri, 18 Feb 2022 21:56:47 +0100 Subject: [PATCH 3/7] temporary changes --- .github/workflows/qemu-init.sh | 50 +++++++++++++-------------- .github/workflows/qemu-initramfs.desc | 13 +------ 2 files changed, 26 insertions(+), 37 deletions(-) diff --git a/.github/workflows/qemu-init.sh b/.github/workflows/qemu-init.sh index fe1f9cddd1aa01..6a641d33183164 100755 --- a/.github/workflows/qemu-init.sh +++ b/.github/workflows/qemu-init.sh @@ -3,39 +3,39 @@ busybox insmod rust_minimal.ko busybox rmmod rust_minimal.ko -busybox insmod rust_print.ko -busybox rmmod rust_print.ko +# busybox insmod rust_print.ko +# busybox rmmod rust_print.ko -busybox insmod rust_module_parameters.ko -busybox rmmod rust_module_parameters.ko +# busybox insmod rust_module_parameters.ko +# busybox rmmod rust_module_parameters.ko -busybox insmod rust_sync.ko -busybox rmmod rust_sync.ko +# busybox insmod rust_sync.ko +# busybox rmmod rust_sync.ko -busybox insmod rust_chrdev.ko -busybox rmmod rust_chrdev.ko +# busybox insmod rust_chrdev.ko +# busybox rmmod rust_chrdev.ko -busybox insmod rust_miscdev.ko -busybox rmmod rust_miscdev.ko +# busybox insmod rust_miscdev.ko +# busybox rmmod rust_miscdev.ko -busybox insmod rust_stack_probing.ko -busybox rmmod rust_stack_probing.ko +# busybox insmod rust_stack_probing.ko +# busybox rmmod rust_stack_probing.ko -busybox insmod rust_semaphore.ko -busybox rmmod rust_semaphore.ko +# busybox insmod rust_semaphore.ko +# busybox rmmod rust_semaphore.ko -busybox insmod rust_semaphore_c.ko -busybox rmmod rust_semaphore_c.ko +# busybox insmod rust_semaphore_c.ko +# busybox rmmod rust_semaphore_c.ko -busybox insmod rust_module_parameters_loadable_default.ko -busybox insmod rust_module_parameters_loadable_custom.ko \ - my_bool=n \ - my_i32=345543 \ - my_str=🦀mod \ - my_usize=84 \ - my_array=1,2,3 -busybox rmmod rust_module_parameters_loadable_default.ko -busybox rmmod rust_module_parameters_loadable_custom.ko +# busybox insmod rust_module_parameters_loadable_default.ko +# busybox insmod rust_module_parameters_loadable_custom.ko \ +# my_bool=n \ +# my_i32=345543 \ +# my_str=🦀mod \ +# my_usize=84 \ +# my_array=1,2,3 +# 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 diff --git a/.github/workflows/qemu-initramfs.desc b/.github/workflows/qemu-initramfs.desc index 7dac4eccb068b3..faf8f994c564a2 100644 --- a/.github/workflows/qemu-initramfs.desc +++ b/.github/workflows/qemu-initramfs.desc @@ -1,20 +1,9 @@ dir /bin 0755 0 0 dir /sys 0755 0 0 dir /dev 0755 0 0 -file /bin/busybox busybox 0755 0 0 +file /bin/busybox busybox/busybox 0755 0 0 slink /bin/sh /bin/busybox 0755 0 0 file /init .github/workflows/qemu-init.sh 0755 0 0 file /rust_minimal.ko samples/rust/rust_minimal.ko 0755 0 0 -file /rust_print.ko samples/rust/rust_print.ko 0755 0 0 -file /rust_module_parameters.ko samples/rust/rust_module_parameters.ko 0755 0 0 -file /rust_sync.ko samples/rust/rust_sync.ko 0755 0 0 -file /rust_chrdev.ko samples/rust/rust_chrdev.ko 0755 0 0 -file /rust_miscdev.ko samples/rust/rust_miscdev.ko 0755 0 0 -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 c199401a46a3c81000d960d9ade4934cff80e174 Mon Sep 17 00:00:00 2001 From: Adam Bratschi-Kaye Date: Sat, 19 Feb 2022 20:46:50 +0100 Subject: [PATCH 4/7] impl file operations for seq_file --- fs/seq_file.c | 69 ++++++++++++++++++----------------- rust/kernel/bindings_helper.h | 1 + rust/kernel/seq_file.rs | 64 ++++++++++++++++++++++++++++++-- 3 files changed, 96 insertions(+), 38 deletions(-) diff --git a/fs/seq_file.c b/fs/seq_file.c index f8e1f4ee87ffca..141b24809db260 100644 --- a/fs/seq_file.c +++ b/fs/seq_file.c @@ -150,7 +150,7 @@ static int traverse(struct seq_file *m, loff_t offset) */ ssize_t seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos) { - struct iovec iov = { .iov_base = buf, .iov_len = size}; + struct iovec iov = { .iov_base = buf, .iov_len = size }; struct kiocb kiocb; struct iov_iter iter; ssize_t ret; @@ -217,7 +217,7 @@ ssize_t seq_read_iter(struct kiocb *iocb, struct iov_iter *iter) m->count -= n; m->from += n; copied += n; - if (m->count) // hadn't managed to copy everything + if (m->count) // hadn't managed to copy everything goto Done; } // get a non-empty record in the buffer @@ -225,12 +225,12 @@ ssize_t seq_read_iter(struct kiocb *iocb, struct iov_iter *iter) p = m->op->start(m, &m->index); while (1) { err = PTR_ERR(p); - if (!p || IS_ERR(p)) // EOF or an error + if (!p || IS_ERR(p)) // EOF or an error break; err = m->op->show(m, p); - if (err < 0) // hard error + if (err < 0) // hard error break; - if (unlikely(err)) // ->show() says "skip it" + if (unlikely(err)) // ->show() says "skip it" m->count = 0; if (unlikely(!m->count)) { // empty record p = m->op->next(m, p, &m->index); @@ -261,16 +261,17 @@ ssize_t seq_read_iter(struct kiocb *iocb, struct iov_iter *iter) p = m->op->next(m, p, &m->index); if (pos == m->index) { - pr_info_ratelimited("buggy .next function %ps did not update position index\n", - m->op->next); + pr_info_ratelimited( + "buggy .next function %ps did not update position index\n", + m->op->next); m->index++; } - if (!p || IS_ERR(p)) // no next record for us + if (!p || IS_ERR(p)) // no next record for us break; if (m->count >= iov_iter_count(iter)) break; err = m->op->show(m, p); - if (err > 0) { // ->show() says "skip it" + if (err > 0) { // ->show() says "skip it" m->count = offs; } else if (err || seq_has_overflowed(m)) { m->count = offs; @@ -591,7 +592,7 @@ int single_open(struct file *file, int (*show)(struct seq_file *, void *), EXPORT_SYMBOL(single_open); int single_open_size(struct file *file, int (*show)(struct seq_file *, void *), - void *data, size_t size) + void *data, size_t size) { char *buf = seq_buf_alloc(size); int ret; @@ -610,7 +611,8 @@ EXPORT_SYMBOL(single_open_size); int single_release(struct inode *inode, struct file *file) { - const struct seq_operations *op = ((struct seq_file *)file->private_data)->op; + const struct seq_operations *op = + ((struct seq_file *)file->private_data)->op; int res = seq_release(inode, file); kfree(op); return res; @@ -628,13 +630,14 @@ int seq_release_private(struct inode *inode, struct file *file) EXPORT_SYMBOL(seq_release_private); void *__seq_open_private(struct file *f, const struct seq_operations *ops, - int psize) + int psize) { int rc; void *private; struct seq_file *seq; - private = kzalloc(psize, GFP_KERNEL_ACCOUNT); + private + = kzalloc(psize, GFP_KERNEL_ACCOUNT); if (private == NULL) goto out; @@ -654,7 +657,7 @@ void *__seq_open_private(struct file *f, const struct seq_operations *ops, EXPORT_SYMBOL(__seq_open_private); int seq_open_private(struct file *filp, const struct seq_operations *ops, - int psize) + int psize) { return __seq_open_private(filp, ops, psize) ? 0 : -ENOMEM; } @@ -696,7 +699,7 @@ EXPORT_SYMBOL(seq_puts); * In usual cases, it will be better to use seq_printf(). It's easier to read. */ void seq_put_decimal_ull_width(struct seq_file *m, const char *delimiter, - unsigned long long num, unsigned int width) + unsigned long long num, unsigned int width) { int len; @@ -747,7 +750,7 @@ EXPORT_SYMBOL(seq_put_decimal_ull); * In usual cases, it will be better to use seq_printf(). It's easier to read. */ void seq_put_hex_ll(struct seq_file *m, const char *delimiter, - unsigned long long v, unsigned int width) + unsigned long long v, unsigned int width) { unsigned int len; int i; @@ -780,7 +783,8 @@ void seq_put_hex_ll(struct seq_file *m, const char *delimiter, m->count += len; } -void seq_put_decimal_ll(struct seq_file *m, const char *delimiter, long long num) +void seq_put_decimal_ll(struct seq_file *m, const char *delimiter, + long long num) { int len; @@ -904,7 +908,7 @@ struct list_head *seq_list_start(struct list_head *head, loff_t pos) { struct list_head *lh; - list_for_each(lh, head) + list_for_each (lh, head) if (pos-- == 0) return lh; @@ -942,7 +946,7 @@ struct hlist_node *seq_hlist_start(struct hlist_head *head, loff_t pos) { struct hlist_node *node; - hlist_for_each(node, head) + hlist_for_each (node, head) if (pos-- == 0) return node; return NULL; @@ -998,12 +1002,11 @@ EXPORT_SYMBOL(seq_hlist_next); * the _rcu list-mutation primitives such as hlist_add_head_rcu() * as long as the traversal is guarded by rcu_read_lock(). */ -struct hlist_node *seq_hlist_start_rcu(struct hlist_head *head, - loff_t pos) +struct hlist_node *seq_hlist_start_rcu(struct hlist_head *head, loff_t pos) { struct hlist_node *node; - __hlist_for_each_rcu(node, head) + __hlist_for_each_rcu (node, head) if (pos-- == 0) return node; return NULL; @@ -1022,8 +1025,7 @@ EXPORT_SYMBOL(seq_hlist_start_rcu); * the _rcu list-mutation primitives such as hlist_add_head_rcu() * as long as the traversal is guarded by rcu_read_lock(). */ -struct hlist_node *seq_hlist_start_head_rcu(struct hlist_head *head, - loff_t pos) +struct hlist_node *seq_hlist_start_head_rcu(struct hlist_head *head, loff_t pos) { if (!pos) return SEQ_START_TOKEN; @@ -1044,8 +1046,7 @@ EXPORT_SYMBOL(seq_hlist_start_head_rcu); * the _rcu list-mutation primitives such as hlist_add_head_rcu() * as long as the traversal is guarded by rcu_read_lock(). */ -struct hlist_node *seq_hlist_next_rcu(void *v, - struct hlist_head *head, +struct hlist_node *seq_hlist_next_rcu(void *v, struct hlist_head *head, loff_t *ppos) { struct hlist_node *node = v; @@ -1066,13 +1067,13 @@ EXPORT_SYMBOL(seq_hlist_next_rcu); * * Called at seq_file->op->start(). */ -struct hlist_node * -seq_hlist_start_percpu(struct hlist_head __percpu *head, int *cpu, loff_t pos) +struct hlist_node *seq_hlist_start_percpu(struct hlist_head __percpu *head, + int *cpu, loff_t pos) { struct hlist_node *node; - for_each_possible_cpu(*cpu) { - hlist_for_each(node, per_cpu_ptr(head, *cpu)) { + for_each_possible_cpu (*cpu) { + hlist_for_each (node, per_cpu_ptr(head, *cpu)) { if (pos-- == 0) return node; } @@ -1090,9 +1091,9 @@ EXPORT_SYMBOL(seq_hlist_start_percpu); * * Called at seq_file->op->next(). */ -struct hlist_node * -seq_hlist_next_percpu(void *v, struct hlist_head __percpu *head, - int *cpu, loff_t *pos) +struct hlist_node *seq_hlist_next_percpu(void *v, + struct hlist_head __percpu *head, + int *cpu, loff_t *pos) { struct hlist_node *node = v; @@ -1114,5 +1115,5 @@ EXPORT_SYMBOL(seq_hlist_next_percpu); void __init seq_file_init(void) { - seq_file_cache = KMEM_CACHE(seq_file, SLAB_ACCOUNT|SLAB_PANIC); + seq_file_cache = KMEM_CACHE(seq_file, SLAB_ACCOUNT | SLAB_PANIC); } diff --git a/rust/kernel/bindings_helper.h b/rust/kernel/bindings_helper.h index 11333971de5739..310e265d32e3a8 100644 --- a/rust/kernel/bindings_helper.h +++ b/rust/kernel/bindings_helper.h @@ -2,6 +2,7 @@ #include #include +#include #include #include #include diff --git a/rust/kernel/seq_file.rs b/rust/kernel/seq_file.rs index 031605c490d3bc..d78214d019fb97 100644 --- a/rust/kernel/seq_file.rs +++ b/rust/kernel/seq_file.rs @@ -157,8 +157,7 @@ extern "C" fn start_callback( // 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 data_wrapper = unsafe { T::DataWrapper::from_pointer((*m).private) }; let iterator = data_wrapper.start().ok(); // Data is still used in the `proc_dir_entry`. mem::forget(data_wrapper); @@ -183,14 +182,71 @@ extern "C" fn start_callback( pub(crate) struct SeqFileOperationsVTable(PhantomData); impl SeqFileOperationsVTable { - const VTABLE: bindings::seq_operations = bindings::seq_operations { + const SEQ_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 { + // TODOABK: safety + pub(crate) const unsafe fn seq_build() -> &'static bindings::seq_operations { + &Self::SEQ_VTABLE + } + + extern "C" fn open_callback( + inode: *mut bindings::inode, + file: *mut bindings::file, + ) -> c_types::c_int { + // TODOABK: docs + unsafe { + bindings::seq_open_private( + file, + &Self::SEQ_VTABLE as *const _ as *mut bindings::seq_operations, + // TODOABK: how to convert safely? + mem::size_of::() as i32, + ) + } + } + + const VTABLE: bindings::file_operations = bindings::file_operations { + open: Some(Self::open_callback), + release: Some(bindings::seq_release_private), + read: Some(bindings::seq_read), + llseek: Some(bindings::seq_lseek), + + check_flags: None, + compat_ioctl: None, + copy_file_range: None, + fallocate: None, + fadvise: None, + fasync: None, + flock: None, + flush: None, + fsync: None, + get_unmapped_area: None, + iterate: None, + iterate_shared: None, + iopoll: None, + lock: None, + mmap: None, + mmap_supported_flags: 0, + owner: ptr::null_mut(), + poll: None, + read_iter: None, + remap_file_range: None, + sendpage: None, + setlease: None, + show_fdinfo: None, + splice_read: None, + splice_write: None, + unlocked_ioctl: None, + write: None, + write_iter: None, + }; + + // TODOABK: safety + pub(crate) const unsafe fn build() -> &'static bindings::file_operations { &Self::VTABLE } } From 7822b81c4ea42a091e0bfb111a4d664073ca88f1 Mon Sep 17 00:00:00 2001 From: Adam Bratschi-Kaye Date: Fri, 25 Feb 2022 21:57:45 +0100 Subject: [PATCH 5/7] compiling --- rust/kernel/bindings_helper.h | 1 - rust/kernel/debugfs.rs | 56 +++++++ rust/kernel/lib.rs | 5 +- rust/kernel/seq_file.rs | 130 +++++++++------ samples/rust/rust_seq_file.rs | 253 +++++++++++++++++------------- samples/rust/rust_seq_file_old.rs | 137 ++++++++++++++++ 6 files changed, 423 insertions(+), 159 deletions(-) create mode 100644 rust/kernel/debugfs.rs create mode 100644 samples/rust/rust_seq_file_old.rs diff --git a/rust/kernel/bindings_helper.h b/rust/kernel/bindings_helper.h index 310e265d32e3a8..475e3e2112d7c1 100644 --- a/rust/kernel/bindings_helper.h +++ b/rust/kernel/bindings_helper.h @@ -6,7 +6,6 @@ #include #include #include -#include #include #include #include diff --git a/rust/kernel/debugfs.rs b/rust/kernel/debugfs.rs new file mode 100644 index 00000000000000..8536694324decf --- /dev/null +++ b/rust/kernel/debugfs.rs @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! TODOABK: finish doc + +use core::{ + marker::{PhantomData, Sync}, + ptr, +}; + +use crate::{bindings, c_types, error, str::CStr, types::PointerWrapper, Result}; + +/// TODOABK: finish doc +pub struct DebugFsDirEntry { + dentry: *mut bindings::dentry, + data: *mut c_types::c_void, + _wrapper: PhantomData, +} + +// TODOABK: safety +unsafe impl Sync for DebugFsDirEntry {} + +impl DebugFsDirEntry { + pub(crate) unsafe fn create_file( + name: &CStr, + data: T, + fops: &'static bindings::file_operations, + ) -> Result { + let name = name.as_char_ptr(); + let data = data.into_pointer() as *mut _; + let dentry_ptr = error::from_kernel_err_ptr(unsafe { + bindings::debugfs_create_file(name, 0, ptr::null_mut(), data, fops) + }); + match dentry_ptr { + Err(err) => { + drop(unsafe { T::from_pointer(data) }); + Err(err) + } + Ok(dentry) => Ok(DebugFsDirEntry { + dentry, + data, + _wrapper: PhantomData, + }), + } + } +} + +impl Drop for DebugFsDirEntry { + fn drop(&mut self) { + // TODOABK: safety + unsafe { + bindings::debugfs_remove(self.dentry); + } + // SAFETY: `self.data` was created by a call to `T::into_pointer`. + unsafe { drop(T::from_pointer(self.data)) } + } +} diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 3fd38ae82c705e..c1b70313a109fe 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -48,6 +48,8 @@ pub mod chrdev; #[cfg(CONFIG_COMMON_CLK)] pub mod clk; pub mod cred; +#[cfg(CONFIG_DEBUG_FS)] +pub mod debugfs; pub mod device; pub mod driver; mod error; @@ -75,9 +77,6 @@ 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; diff --git a/rust/kernel/seq_file.rs b/rust/kernel/seq_file.rs index d78214d019fb97..0faedd7ffff344 100644 --- a/rust/kernel/seq_file.rs +++ b/rust/kernel/seq_file.rs @@ -13,14 +13,18 @@ #![cfg(CONFIG_PROC_FS)] use core::{ - iter::{Iterator, Peekable}, + fmt::{self, Display}, marker::PhantomData, - mem, - ops::{Deref, DerefMut}, - ptr, + mem, ptr, }; -use crate::{bindings, c_str, c_types, str::CStr, types::PointerWrapper, Result}; +use crate::{ + bindings, c_str, c_types, + debugfs::DebugFsDirEntry, + str::{CStr, CString}, + types::PointerWrapper, + Result, +}; /// Rust equivalent of the [`seq_operations`] interface on the C side. /// @@ -54,22 +58,30 @@ use crate::{bindings, c_str, c_types, str::CStr, types::PointerWrapper, Result}; /// [`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; + type Item: Display; /// Wrapper used to store a pointer to `Self` on the C side. - type DataWrapper: PointerWrapper + Deref; + type DataWrapper: PointerWrapper; /// Wrapper used to store a pointer to the iterator on the C side. - type IteratorWrapper: PointerWrapper + DerefMut>; + type IteratorWrapper: PointerWrapper; + + /// TODOABK + type OpenData: PointerWrapper + Sync; /// Called once each time the `seq_file` is opened. - fn start(&self) -> Result; + fn start(data: &Self::DataWrapper) -> Option; - /// How the item will be displayed to the reader. - fn display(item: &Self::Item) -> &str; + /// TODOABK: docs + fn next(iterator: &mut Self::IteratorWrapper) -> bool; + + /// TODOABK: docs + fn current(iterator: &Self::IteratorWrapper) -> Option; + + /// TODOABK: docs + fn open<'a>( + open_data: ::Borrowed<'a>, + ) -> Result; } extern "C" fn stop_callback( @@ -105,13 +117,10 @@ extern "C" fn next_callback( *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(), + if !T::next(&mut iterator) { + ptr::null_mut() + } else { + T::IteratorWrapper::into_pointer(iterator) as *mut _ } } @@ -119,7 +128,7 @@ 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"); + const FORMAT: &CStr = c_str!("%pA"); if v.is_null() { return 0; } @@ -128,18 +137,16 @@ extern "C" fn show_callback( // 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); + let iterator = unsafe { T::IteratorWrapper::from_pointer(v) }; + if let Some(item) = T::current(&iterator) { // 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, + (FORMAT as *const _) as *const _, + (&format_args!("{}", item) as *const _) as *const _, ); } } @@ -158,7 +165,7 @@ extern "C" fn start_callback( // `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((*m).private) }; - let iterator = data_wrapper.start().ok(); + let iterator = T::start(&data_wrapper); // 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`. @@ -166,14 +173,11 @@ extern "C" fn start_callback( match iterator { Some(mut wrapper) => { for _ in 0..pos { - if wrapper.next().is_none() { + if !T::next(&mut wrapper) { return ptr::null_mut(); } } - match wrapper.peek() { - Some(_next) => T::IteratorWrapper::into_pointer(wrapper) as *mut _, - None => ptr::null_mut(), - } + T::IteratorWrapper::into_pointer(wrapper) as *mut _ } None => ptr::null_mut(), } @@ -181,7 +185,11 @@ extern "C" fn start_callback( pub(crate) struct SeqFileOperationsVTable(PhantomData); -impl SeqFileOperationsVTable { +impl<'a, T, D: 'a> SeqFileOperationsVTable +where + T: SeqOperations, + D: PointerWrapper, +{ const SEQ_VTABLE: bindings::seq_operations = bindings::seq_operations { start: Some(start_callback::), stop: Some(stop_callback::), @@ -189,24 +197,32 @@ impl SeqFileOperationsVTable { show: Some(show_callback::), }; - // TODOABK: safety - pub(crate) const unsafe fn seq_build() -> &'static bindings::seq_operations { - &Self::SEQ_VTABLE - } - extern "C" fn open_callback( inode: *mut bindings::inode, file: *mut bindings::file, ) -> c_types::c_int { // TODOABK: docs - unsafe { - bindings::seq_open_private( + let result = unsafe { + bindings::seq_open( file, &Self::SEQ_VTABLE as *const _ as *mut bindings::seq_operations, - // TODOABK: how to convert safely? - mem::size_of::() as i32, ) + }; + if result != 0 { + // Close file? + return result; } + + let open_data = unsafe { T::OpenData::borrow((*inode).i_private) }; + + let data_wrapper = match T::open(open_data) { + Ok(data) => data, + Err(err) => return err.to_kernel_errno(), + }; + + unsafe { *((*file).private_data as *mut bindings::seq_file) }.private = + data_wrapper.into_pointer() as *mut _; + result } const VTABLE: bindings::file_operations = bindings::file_operations { @@ -244,9 +260,27 @@ impl SeqFileOperationsVTable { write: None, write_iter: None, }; +} - // TODOABK: safety - pub(crate) const unsafe fn build() -> &'static bindings::file_operations { - &Self::VTABLE - } +/// TODOABK: docs +pub struct SeqFileDebugFsDirEntry { + _debugfs_entry: DebugFsDirEntry, +} + +/// TODOABK: finish doc +pub fn debugfs_create_file<'a, T, D: 'a>( + name: fmt::Arguments<'_>, + data: D, +) -> Result> +where + T: SeqOperations, + D: PointerWrapper + Clone, +{ + let name = CString::try_from_fmt(name)?; + let debugfs_entry = unsafe { + DebugFsDirEntry::create_file(&name, data, &SeqFileOperationsVTable::::VTABLE) + }?; + Ok(SeqFileDebugFsDirEntry { + _debugfs_entry: debugfs_entry, + }) } diff --git a/samples/rust/rust_seq_file.rs b/samples/rust/rust_seq_file.rs index c2611b8bd7a47a..e576b186db8403 100644 --- a/samples/rust/rust_seq_file.rs +++ b/samples/rust/rust_seq_file.rs @@ -1,146 +1,185 @@ // 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, -}; +//! Rust miscellaneous device sample. + +use kernel::prelude::*; 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, + file_operations::FileOperations, + io_buffer::{IoBufferReader, IoBufferWriter}, + miscdev, seq_file, + seq_file::SeqFileDebugFsDirEntry, + sync::{CondVar, Mutex, Ref, RefBorrow, UniqueRef}, }; module! { - type: RustSeqFileDev, - name: b"rust_seq_file", - author: b"Adam Bratschi-Kaye", - description: b"Rust sample using a seq_file", + type: RustMiscdev, + name: b"rust_miscdev", + author: b"Rust for Linux Contributors", + description: b"Rust miscellaneous device sample", 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"); - }, - )?)) - } +const MAX_TOKENS: usize = 3; + +struct SharedStateInner { + token_count: usize, } -impl seq_file::SeqOperations for State { - type Item = String; - type Iterator = Take>; - type DataWrapper = Pin>; - type IteratorWrapper = Box>; +struct SharedState { + state_changed: CondVar, + inner: Mutex, +} - fn display(item: &Self::Item) -> &str { - &item[..] - } +impl SharedState { + fn try_new() -> Result> { + let mut state = Pin::from(UniqueRef::try_new(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 }) }, + })?); - 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; + // SAFETY: `state_changed` is pinned when `state` is. + let pinned = unsafe { state.as_mut().map_unchecked_mut(|s| &mut s.state_changed) }; + kernel::condvar_init!(pinned, "SharedState::state_changed"); - let count = self.0.lock(); - let mut message = String::new(); + // SAFETY: `inner` is pinned when `state` is. + let pinned = unsafe { state.as_mut().map_unchecked_mut(|s| &mut s.inner) }; + kernel::mutex_init!(pinned, "SharedState::inner"); - 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) + Ok(state.into()) } } struct Token; +impl FileOperations for Token { + type Wrapper = Ref; + type OpenData = Ref; -impl FileOpener>> for Token { - fn open(ctx: &Pin>) -> Result { - pr_info!("rust seq_file was opened!\n"); - Ok(ctx.clone()) - } -} + kernel::declare_file_operations!(read, write); -impl FileOperations for Token { - kernel::declare_file_operations!(read); + fn open(shared: &Ref, _file: &File) -> Result { + Ok(shared.clone()) + } - type Wrapper = Pin>; + fn read( + shared: RefBorrow<'_, SharedState>, + _: &File, + data: &mut impl IoBufferWriter, + offset: u64, + ) -> Result { + // Succeed if the caller doesn't provide a buffer or if not at the start. + if data.is_empty() || offset != 0 { + return Ok(0); + } + + { + let mut inner = shared.inner.lock(); + + // Wait until we are allowed to decrement the token count or a signal arrives. + while inner.token_count == 0 { + if shared.state_changed.wait(&mut inner) { + return Err(Error::EINTR); + } + } + + // Consume a token. + inner.token_count -= 1; + } + + // Notify a possible writer waiting. + shared.state_changed.notify_all(); + + // Write a one-byte 1 to the reader. + data.write_slice(&[1u8; 1])?; + Ok(1) + } - fn read(shared: &Ref, _: &File, _: &mut T, _: u64) -> Result { - *(shared.0.lock()) += 1; - Ok(0) + fn write( + shared: RefBorrow<'_, SharedState>, + _: &File, + data: &mut impl IoBufferReader, + _offset: u64, + ) -> Result { + { + let mut inner = shared.inner.lock(); + + // Wait until we are allowed to increment the token count or a signal arrives. + while inner.token_count == MAX_TOKENS { + if shared.state_changed.wait(&mut inner) { + return Err(Error::EINTR); + } + } + + // Increment the number of token so that a reader can be released. + inner.token_count += 1; + } + + // Notify a possible reader waiting. + shared.state_changed.notify_all(); + Ok(data.len()) } } -struct RustSeqFileDev { - _proc: proc_fs::ProcDirEntry>>, - _dev: Pin>>>>, -} +impl seq_file::SeqOperations for Token { + type Item = usize; + type OpenData = Ref; + type DataWrapper = Ref; + type IteratorWrapper = Box<(usize, usize)>; + + fn open<'a>(open_data: RefBorrow<'a, SharedState>) -> Result> { + Ok(open_data.into()) + } + + fn start(data: &Self::DataWrapper) -> Option { + let total = data.inner.lock().token_count; + Box::try_new((total, 1)).ok() + } -impl KernelModule for RustSeqFileDev { - fn init() -> Result { - pr_info!("Rust seq_file sample (init)\n"); + fn next(iterator: &mut Self::IteratorWrapper) -> bool { + let total = iterator.0; + let current = iterator.1; + if total == current { + false + } else { + iterator.1 += 1; + true + } + } - let state = State::try_new()?; + fn current(iterator: &Self::IteratorWrapper) -> core::option::Option { + let total = iterator.0; + let current = iterator.1; + if total > current { + Some(current) + } else { + None + } + } +} - let proc_dir_entry = proc_fs::ProcDirEntry::new_seq_private::( - c_str!("rust_seq_file"), - state.clone(), - )?; +struct RustMiscdev { + _dev: Pin>>, + _debugfs: SeqFileDebugFsDirEntry, +} - let dev_reg = - miscdev::Registration::new_pinned::(c_str!("rust_seq_file"), None, state)?; +impl KernelModule for RustMiscdev { + fn init(name: &'static CStr, _module: &'static ThisModule) -> Result { + pr_info!("Rust miscellaneous device sample (init)\n"); - let dev = RustSeqFileDev { - _proc: proc_dir_entry, - _dev: dev_reg, - }; + let state = SharedState::try_new()?; + let debugfs = seq_file::debugfs_create_file(fmt!("{name}"), state.clone())?; - Ok(dev) + Ok(RustMiscdev { + _dev: miscdev::Registration::new_pinned(fmt!("{name}"), state)?, + _debugfs: debugfs, + }) } } -impl Drop for RustSeqFileDev { +impl Drop for RustMiscdev { fn drop(&mut self) { - pr_info!("Rust seq_file sample (exit)\n"); + pr_info!("Rust seq file device sample (exit)\n"); } } diff --git a/samples/rust/rust_seq_file_old.rs b/samples/rust/rust_seq_file_old.rs new file mode 100644 index 00000000000000..6b75c158868654 --- /dev/null +++ b/samples/rust/rust_seq_file_old.rs @@ -0,0 +1,137 @@ +// 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: + +#![feature(allocator_api, global_asm)] + +use alloc::boxed::Box; +use core::{ + cmp::min, + convert::TryInto, + fmt::Write, + iter::{repeat, Peekable, Repeat, Take}, + pin::Pin, +}; +use kernel::{ + c_str, + debugfs::DebugFsDirEntry, + file::File, + file_operations::FileOperations, + io_buffer::IoBufferWriter, + miscdev, mutex_init, + prelude::*, + 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 = u32; + type DataWrapper = Pin>; + type IteratorWrapper = Box>>>; + + fn start(&self) -> Option { + 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 FileOperations for Token { + type Wrapper = Pin>; + type OpenData = Pin>; + + kernel::declare_file_operations!(read); + + fn open(state: &Pin>, _file: &File) -> Result { + Ok(state.clone()) + } + + fn read(shared: &Ref, _: &File, _: &mut T, _: u64) -> Result { + *(shared.0.lock()) += 1; + Ok(0) + } +} + +struct RustSeqFileDev { + _debugfs: DebugFsDirEntry>>, + _dev: Pin>>, +} + +impl KernelModule for RustSeqFileDev { + fn init() -> Result { + pr_info!("Rust seq_file sample (init)\n"); + + let state = State::try_new()?; + + let debugfs_dir_entry = + seq_file::debugfs_create_file(c_str!("rust_seq_file"), state.clone())?; + + let dev_reg = + miscdev::Registration::new_pinned::(c_str!("rust_seq_file"), None, state)?; + + let dev = RustSeqFileDev { + _debugfs: debugfs_dir_entry, + _dev: dev_reg, + }; + + Ok(dev) + } +} + +impl Drop for RustSeqFileDev { + fn drop(&mut self) { + pr_info!("Rust seq_file sample (exit)\n"); + } +} From 28dc837e1831d050ad8be83004900955a1dd9aa2 Mon Sep 17 00:00:00 2001 From: Adam Bratschi-Kaye Date: Mon, 28 Feb 2022 23:33:02 +0100 Subject: [PATCH 6/7] working --- .github/workflows/qemu-init.sh | 12 +++- fs/debugfs/inode.c | 109 +++++++++++++++------------------ fs/seq_file.c | 6 ++ rust/kernel/debugfs.rs | 2 +- rust/kernel/seq_file.rs | 41 ++++++------- rust/kernel/sync/arc.rs | 11 +++- samples/rust/rust_seq_file.rs | 66 +++++++------------- 7 files changed, 116 insertions(+), 131 deletions(-) diff --git a/.github/workflows/qemu-init.sh b/.github/workflows/qemu-init.sh index 6a641d33183164..3fa20125dfce44 100755 --- a/.github/workflows/qemu-init.sh +++ b/.github/workflows/qemu-init.sh @@ -1,7 +1,7 @@ #!/bin/sh -busybox insmod rust_minimal.ko -busybox rmmod rust_minimal.ko +# busybox insmod rust_minimal.ko +# busybox rmmod rust_minimal.ko # busybox insmod rust_print.ko # busybox rmmod rust_print.ko @@ -40,12 +40,18 @@ busybox rmmod rust_minimal.ko busybox insmod rust_seq_file.ko busybox mkdir proc busybox mount -t proc proc /proc +busybox mkdir debugfs +busybox mount -t debugfs debugfs /debugfs 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 echo "reading from device" busybox cat /dev/rust_seq_file0 busybox cat /dev/rust_seq_file0 -busybox cat /proc/rust_seq_file +busybox echo "reading debug file" +busybox cat /debugfs/rust_seq_file +busybox echo "removing device file" busybox rm /dev/rust_seq_file0 +busybox echo "removing module" busybox rmmod rust_seq_file.ko busybox reboot -f diff --git a/fs/debugfs/inode.c b/fs/debugfs/inode.c index 2f117c57160dc0..509ae08b4caf4a 100644 --- a/fs/debugfs/inode.c +++ b/fs/debugfs/inode.c @@ -10,7 +10,7 @@ * See ./Documentation/core-api/kernel-api.rst for more details. */ -#define pr_fmt(fmt) "debugfs: " fmt +#define pr_fmt(fmt) "debugfs: " fmt #include #include @@ -30,7 +30,7 @@ #include "internal.h" -#define DEBUGFS_DEFAULT_MODE 0700 +#define DEBUGFS_DEFAULT_MODE 0700 static struct vfsmount *debugfs_mount; static int debugfs_mount_count; @@ -56,15 +56,15 @@ static int debugfs_setattr(struct user_namespace *mnt_userns, } static const struct inode_operations debugfs_file_inode_operations = { - .setattr = debugfs_setattr, + .setattr = debugfs_setattr, }; static const struct inode_operations debugfs_dir_inode_operations = { - .lookup = simple_lookup, - .setattr = debugfs_setattr, + .lookup = simple_lookup, + .setattr = debugfs_setattr, }; static const struct inode_operations debugfs_symlink_inode_operations = { - .get_link = simple_get_link, - .setattr = debugfs_setattr, + .get_link = simple_get_link, + .setattr = debugfs_setattr, }; static struct inode *debugfs_get_inode(struct super_block *sb) @@ -72,8 +72,8 @@ static struct inode *debugfs_get_inode(struct super_block *sb) struct inode *inode = new_inode(sb); if (inode) { inode->i_ino = get_next_ino(); - inode->i_atime = inode->i_mtime = - inode->i_ctime = current_time(inode); + inode->i_atime = inode->i_mtime = inode->i_ctime = + current_time(inode); } return inode; } @@ -84,19 +84,12 @@ struct debugfs_mount_opts { umode_t mode; }; -enum { - Opt_uid, - Opt_gid, - Opt_mode, - Opt_err -}; +enum { Opt_uid, Opt_gid, Opt_mode, Opt_err }; -static const match_table_t tokens = { - {Opt_uid, "uid=%u"}, - {Opt_gid, "gid=%u"}, - {Opt_mode, "mode=%o"}, - {Opt_err, NULL} -}; +static const match_table_t tokens = { { Opt_uid, "uid=%u" }, + { Opt_gid, "gid=%u" }, + { Opt_mode, "mode=%o" }, + { Opt_err, NULL } }; struct debugfs_fs_info { struct debugfs_mount_opts mount_opts; @@ -140,7 +133,7 @@ static int debugfs_parse_options(char *data, struct debugfs_mount_opts *opts) return -EINVAL; opts->mode = option & S_IALLUGO; break; - /* + /* * We might like to report bad mount options here; * but traditionally debugfs has ignored all mount options */ @@ -206,10 +199,10 @@ static void debugfs_free_inode(struct inode *inode) } static const struct super_operations debugfs_super_operations = { - .statfs = simple_statfs, - .remount_fs = debugfs_remount, - .show_options = debugfs_show_options, - .free_inode = debugfs_free_inode, + .statfs = simple_statfs, + .remount_fs = debugfs_remount, + .show_options = debugfs_show_options, + .free_inode = debugfs_free_inode, }; static void debugfs_release_dentry(struct dentry *dentry) @@ -235,7 +228,7 @@ static const struct dentry_operations debugfs_dops = { static int debug_fill_super(struct super_block *sb, void *data, int silent) { - static const struct tree_descr debug_files[] = {{""}}; + static const struct tree_descr debug_files[] = { { "" } }; struct debugfs_fs_info *fsi; int err; @@ -250,7 +243,7 @@ static int debug_fill_super(struct super_block *sb, void *data, int silent) if (err) goto fail; - err = simple_fill_super(sb, DEBUGFS_MAGIC, debug_files); + err = simple_fill_super(sb, DEBUGFS_MAGIC, debug_files); if (err) goto fail; @@ -267,9 +260,8 @@ static int debug_fill_super(struct super_block *sb, void *data, int silent) return err; } -static struct dentry *debug_mount(struct file_system_type *fs_type, - int flags, const char *dev_name, - void *data) +static struct dentry *debug_mount(struct file_system_type *fs_type, int flags, + const char *dev_name, void *data) { if (!(debugfs_allow & DEBUGFS_ALLOW_API)) return ERR_PTR(-EPERM); @@ -278,10 +270,10 @@ static struct dentry *debug_mount(struct file_system_type *fs_type, } static struct file_system_type debug_fs_type = { - .owner = THIS_MODULE, - .name = "debugfs", - .mount = debug_mount, - .kill_sb = kill_litter_super, + .owner = THIS_MODULE, + .name = "debugfs", + .mount = debug_mount, + .kill_sb = kill_litter_super, }; MODULE_ALIAS_FS("debugfs"); @@ -383,10 +375,10 @@ static struct dentry *end_creating(struct dentry *dentry) return dentry; } -static struct dentry *__debugfs_create_file(const char *name, umode_t mode, - struct dentry *parent, void *data, - const struct file_operations *proxy_fops, - const struct file_operations *real_fops) +static struct dentry * +__debugfs_create_file(const char *name, umode_t mode, struct dentry *parent, + void *data, const struct file_operations *proxy_fops, + const struct file_operations *real_fops) { struct dentry *dentry; struct inode *inode; @@ -417,7 +409,7 @@ static struct dentry *__debugfs_create_file(const char *name, umode_t mode, inode->i_op = &debugfs_file_inode_operations; inode->i_fop = proxy_fops; dentry->d_fsdata = (void *)((unsigned long)real_fops | - DEBUGFS_FSDATA_IS_REAL_FOPS_BIT); + DEBUGFS_FSDATA_IS_REAL_FOPS_BIT); d_instantiate(dentry, inode); fsnotify_create(d_inode(dentry->d_parent), dentry); @@ -455,11 +447,11 @@ struct dentry *debugfs_create_file(const char *name, umode_t mode, struct dentry *parent, void *data, const struct file_operations *fops) { - - return __debugfs_create_file(name, mode, parent, data, - fops ? &debugfs_full_proxy_file_operations : - &debugfs_noop_file_operations, - fops); + return __debugfs_create_file( + name, mode, parent, data, + fops ? &debugfs_full_proxy_file_operations : + &debugfs_noop_file_operations, + fops); } EXPORT_SYMBOL_GPL(debugfs_create_file); @@ -491,14 +483,14 @@ EXPORT_SYMBOL_GPL(debugfs_create_file); * thus, may be used here. */ struct dentry *debugfs_create_file_unsafe(const char *name, umode_t mode, - struct dentry *parent, void *data, - const struct file_operations *fops) + struct dentry *parent, void *data, + const struct file_operations *fops) { - - return __debugfs_create_file(name, mode, parent, data, - fops ? &debugfs_open_proxy_file_operations : - &debugfs_noop_file_operations, - fops); + return __debugfs_create_file( + name, mode, parent, data, + fops ? &debugfs_open_proxy_file_operations : + &debugfs_noop_file_operations, + fops); } EXPORT_SYMBOL_GPL(debugfs_create_file_unsafe); @@ -596,10 +588,8 @@ EXPORT_SYMBOL_GPL(debugfs_create_dir); * * @f should return what ->d_automount() would. */ -struct dentry *debugfs_create_automount(const char *name, - struct dentry *parent, - debugfs_automount_t f, - void *data) +struct dentry *debugfs_create_automount(const char *name, struct dentry *parent, + debugfs_automount_t f, void *data) { struct dentry *dentry = start_creating(name, parent); struct inode *inode; @@ -705,7 +695,7 @@ static void __debugfs_file_removed(struct dentry *dentry) static void remove_one(struct dentry *victim) { - if (d_is_reg(victim)) + if (d_is_reg(victim)) __debugfs_file_removed(victim); simple_release_fs(&debugfs_mount, &debugfs_mount_count); } @@ -754,7 +744,7 @@ EXPORT_SYMBOL_GPL(debugfs_remove); * returned. */ struct dentry *debugfs_rename(struct dentry *old_dir, struct dentry *old_dentry, - struct dentry *new_dir, const char *new_name) + struct dentry *new_dir, const char *new_name) { int error; struct dentry *dentry = NULL, *trap; @@ -790,8 +780,7 @@ struct dentry *debugfs_rename(struct dentry *old_dir, struct dentry *old_dentry, } d_move(old_dentry, dentry); fsnotify_move(d_inode(old_dir), d_inode(new_dir), &old_name.name, - d_is_dir(old_dentry), - NULL, old_dentry); + d_is_dir(old_dentry), NULL, old_dentry); release_dentry_name_snapshot(&old_name); unlock_rename(new_dir, old_dir); dput(dentry); diff --git a/fs/seq_file.c b/fs/seq_file.c index 141b24809db260..19b839a52af936 100644 --- a/fs/seq_file.c +++ b/fs/seq_file.c @@ -83,6 +83,8 @@ int seq_open(struct file *file, const struct seq_operations *op) * file.open() which calls seq_open() and then sets FMODE_PWRITE. */ file->f_mode &= ~FMODE_PWRITE; + pr_alert("seq_open has file %p and private_data %p", file, + file->private_data); return 0; } EXPORT_SYMBOL(seq_open); @@ -176,6 +178,8 @@ ssize_t seq_read_iter(struct kiocb *iocb, struct iov_iter *iter) void *p; int err = 0; + pr_alert("Start seq_read_iter private is %p", m->private); + if (!iov_iter_count(iter)) return 0; @@ -222,6 +226,8 @@ ssize_t seq_read_iter(struct kiocb *iocb, struct iov_iter *iter) } // get a non-empty record in the buffer m->from = 0; + pr_alert("Start seq_read_iter just before calling start %p", + m->private); p = m->op->start(m, &m->index); while (1) { err = PTR_ERR(p); diff --git a/rust/kernel/debugfs.rs b/rust/kernel/debugfs.rs index 8536694324decf..a63e8ef54635ca 100644 --- a/rust/kernel/debugfs.rs +++ b/rust/kernel/debugfs.rs @@ -51,6 +51,6 @@ impl Drop for DebugFsDirEntry { bindings::debugfs_remove(self.dentry); } // SAFETY: `self.data` was created by a call to `T::into_pointer`. - unsafe { drop(T::from_pointer(self.data)) } + unsafe { T::from_pointer(self.data) }; } } diff --git a/rust/kernel/seq_file.rs b/rust/kernel/seq_file.rs index 0faedd7ffff344..b224545e47b9ee 100644 --- a/rust/kernel/seq_file.rs +++ b/rust/kernel/seq_file.rs @@ -57,8 +57,8 @@ use crate::{ /// /// [`seq_operations`]: ../../../include/linux/seq_file.h pub trait SeqOperations { - /// Type produced on each iteration. - type Item: Display; + /// TODOABK + type OpenData: PointerWrapper + Sync; /// Wrapper used to store a pointer to `Self` on the C side. type DataWrapper: PointerWrapper; @@ -66,11 +66,13 @@ pub trait SeqOperations { /// Wrapper used to store a pointer to the iterator on the C side. type IteratorWrapper: PointerWrapper; - /// TODOABK - type OpenData: PointerWrapper + Sync; + /// Type produced on each iteration. + type Item: Display; /// Called once each time the `seq_file` is opened. - fn start(data: &Self::DataWrapper) -> Option; + fn start<'a>( + data: ::Borrowed<'a>, + ) -> Option; /// TODOABK: docs fn next(iterator: &mut Self::IteratorWrapper) -> bool; @@ -164,10 +166,8 @@ extern "C" fn start_callback( // 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((*m).private) }; - let iterator = T::start(&data_wrapper); - // Data is still used in the `proc_dir_entry`. - mem::forget(data_wrapper); + let data_wrapper = unsafe { T::DataWrapper::borrow((*m).private) }; + let iterator = T::start(data_wrapper); // SAFETY: The caller guarantees that `pos` points to a valid `loff_t`. let pos = unsafe { *pos }; match iterator { @@ -209,7 +209,7 @@ where ) }; if result != 0 { - // Close file? + // TODOABK: Close file? return result; } @@ -220,14 +220,17 @@ where Err(err) => return err.to_kernel_errno(), }; - unsafe { *((*file).private_data as *mut bindings::seq_file) }.private = - data_wrapper.into_pointer() as *mut _; + let data_pointer = data_wrapper.into_pointer() as *mut c_types::c_void; + unsafe { + (*((*file).private_data as *mut bindings::seq_file)).private = + data_pointer as *mut c_types::c_void + }; result } const VTABLE: bindings::file_operations = bindings::file_operations { open: Some(Self::open_callback), - release: Some(bindings::seq_release_private), + release: Some(bindings::seq_release), read: Some(bindings::seq_read), llseek: Some(bindings::seq_lseek), @@ -264,18 +267,14 @@ where /// TODOABK: docs pub struct SeqFileDebugFsDirEntry { - _debugfs_entry: DebugFsDirEntry, + _debugfs_entry: DebugFsDirEntry, } /// TODOABK: finish doc -pub fn debugfs_create_file<'a, T, D: 'a>( +pub fn debugfs_create_file( name: fmt::Arguments<'_>, - data: D, -) -> Result> -where - T: SeqOperations, - D: PointerWrapper + Clone, -{ + data: T::OpenData, +) -> Result> { let name = CString::try_from_fmt(name)?; let debugfs_entry = unsafe { DebugFsDirEntry::create_file(&name, data, &SeqFileOperationsVTable::::VTABLE) diff --git a/rust/kernel/sync/arc.rs b/rust/kernel/sync/arc.rs index cd98806b84ed4e..4b80b4b7cb79eb 100644 --- a/rust/kernel/sync/arc.rs +++ b/rust/kernel/sync/arc.rs @@ -15,7 +15,7 @@ //! //! [`Arc`]: https://doc.rust-lang.org/std/sync/struct.Arc.html -use crate::{bindings, Error, Opaque, Result}; +use crate::{bindings, pr_alert, Error, Opaque, Result}; use alloc::{ alloc::{alloc, dealloc}, vec::Vec, @@ -215,6 +215,10 @@ impl Deref for Ref { impl Clone for Ref { fn clone(&self) -> Self { + pr_alert!( + "------------------------ Incrementing refcount at address {:?}", + self.ptr + ); // INVARIANT: C `refcount_inc` saturates the refcount, so it cannot overflow to zero. // SAFETY: By the type invariant, there is necessarily a reference to the object, so it is // safe to increment the refcount. @@ -235,6 +239,11 @@ impl AsRef for Ref { impl Drop for Ref { fn drop(&mut self) { + pr_alert!( + "------------------------------ Decrementing refcount at address {:?}", + self.ptr + ); + // SAFETY: By the type invariant, there is necessarily a reference to the object. We cannot // touch `refcount` after it's decremented to a non-zero value because another thread/CPU // may concurrently decrement it to zero and free it. It is ok to have a raw pointer to diff --git a/samples/rust/rust_seq_file.rs b/samples/rust/rust_seq_file.rs index e576b186db8403..4fde5ebee1e5df 100644 --- a/samples/rust/rust_seq_file.rs +++ b/samples/rust/rust_seq_file.rs @@ -14,9 +14,9 @@ use kernel::{ module! { type: RustMiscdev, - name: b"rust_miscdev", + name: b"rust_seq_file", author: b"Rust for Linux Contributors", - description: b"Rust miscellaneous device sample", + description: b"Sample Rust miscellaneous device with a debugfs entry using seq_file", license: b"GPL v2", } @@ -27,23 +27,16 @@ struct SharedStateInner { } struct SharedState { - state_changed: CondVar, inner: Mutex, } impl SharedState { fn try_new() -> Result> { let mut state = Pin::from(UniqueRef::try_new(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 when `state` is. - let pinned = unsafe { state.as_mut().map_unchecked_mut(|s| &mut s.state_changed) }; - kernel::condvar_init!(pinned, "SharedState::state_changed"); - // SAFETY: `inner` is pinned when `state` is. let pinned = unsafe { state.as_mut().map_unchecked_mut(|s| &mut s.inner) }; kernel::mutex_init!(pinned, "SharedState::inner"); @@ -76,63 +69,46 @@ impl FileOperations for Token { { let mut inner = shared.inner.lock(); - - // Wait until we are allowed to decrement the token count or a signal arrives. - while inner.token_count == 0 { - if shared.state_changed.wait(&mut inner) { - return Err(Error::EINTR); - } - } + pr_alert!("read called, current count is {}", inner.token_count); // Consume a token. - inner.token_count -= 1; + inner.token_count += 1; } // Notify a possible writer waiting. shared.state_changed.notify_all(); // Write a one-byte 1 to the reader. - data.write_slice(&[1u8; 1])?; + data.write_slice(&[b'a'; 1])?; Ok(1) } +} - fn write( - shared: RefBorrow<'_, SharedState>, - _: &File, - data: &mut impl IoBufferReader, - _offset: u64, - ) -> Result { - { - let mut inner = shared.inner.lock(); - - // Wait until we are allowed to increment the token count or a signal arrives. - while inner.token_count == MAX_TOKENS { - if shared.state_changed.wait(&mut inner) { - return Err(Error::EINTR); - } - } - - // Increment the number of token so that a reader can be released. - inner.token_count += 1; - } +struct Log { + read_id: usize, +} - // Notify a possible reader waiting. - shared.state_changed.notify_all(); - Ok(data.len()) +impl core::fmt::Display for Log { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + writeln!(f, "rust_seq_file read log: {}", self.read_id) } } impl seq_file::SeqOperations for Token { - type Item = usize; type OpenData = Ref; type DataWrapper = Ref; type IteratorWrapper = Box<(usize, usize)>; + type Item = Log; fn open<'a>(open_data: RefBorrow<'a, SharedState>) -> Result> { + pr_alert!( + "While opening, count is {}", + open_data.inner.lock().token_count, + ); Ok(open_data.into()) } - fn start(data: &Self::DataWrapper) -> Option { + fn start<'a>(data: RefBorrow<'a, SharedState>) -> Option { let total = data.inner.lock().token_count; Box::try_new((total, 1)).ok() } @@ -148,11 +124,11 @@ impl seq_file::SeqOperations for Token { } } - fn current(iterator: &Self::IteratorWrapper) -> core::option::Option { + fn current(iterator: &Self::IteratorWrapper) -> core::option::Option { let total = iterator.0; let current = iterator.1; - if total > current { - Some(current) + if total >= current { + Some(Log { read_id: current }) } else { None } From 02cfffbc31580bc290dc8a1f873b1b95aaec1574 Mon Sep 17 00:00:00 2001 From: Adam Bratschi-Kaye Date: Tue, 1 Mar 2022 00:46:04 +0100 Subject: [PATCH 7/7] safety comments --- .github/workflows/busybox.config | 4 +- .github/workflows/ci.yaml | 4 +- .../kernel-arm64-debug-thinlto.config | 1 + .github/workflows/kernel-arm64-debug.config | 2 +- .../kernel-arm64-release-thinlto.config | 6 +- .github/workflows/kernel-arm64-release.config | 2 +- .github/workflows/kernel-riscv64-debug.config | 5 +- .../workflows/kernel-riscv64-release.config | 5 +- .../kernel-x86_64-debug-thinlto.config | 5 +- .github/workflows/kernel-x86_64-debug.config | 6 +- .../kernel-x86_64-release-thinlto.config | 6 +- .github/workflows/qemu-init.sh | 58 ++++--- .github/workflows/qemu-initramfs.desc | 13 +- fs/debugfs/inode.c | 109 +++++++------ fs/seq_file.c | 75 ++++----- rust/kernel/debugfs.rs | 25 ++- rust/kernel/lib.rs | 2 +- rust/kernel/proc_fs.rs | 101 ------------ rust/kernel/seq_file.rs | 146 +++++++++++------- rust/kernel/sync/arc.rs | 11 +- samples/rust/Kconfig | 2 +- samples/rust/rust_seq_file.rs | 33 ++-- samples/rust/rust_seq_file_old.rs | 137 ---------------- 23 files changed, 293 insertions(+), 465 deletions(-) delete mode 100644 rust/kernel/proc_fs.rs delete mode 100644 samples/rust/rust_seq_file_old.rs diff --git a/.github/workflows/busybox.config b/.github/workflows/busybox.config index 81981ec17d1727..18693ef72beda0 100644 --- a/.github/workflows/busybox.config +++ b/.github/workflows/busybox.config @@ -209,7 +209,7 @@ CONFIG_CAT=y # CONFIG_CP is not set # CONFIG_FEATURE_CP_LONG_OPTIONS is not set # CONFIG_FEATURE_CP_REFLINK is not set -CONFIG_CUT=y +# CONFIG_CUT is not set # CONFIG_DATE is not set # CONFIG_FEATURE_DATE_ISOFMT is not set # CONFIG_FEATURE_DATE_NANO 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=y +# CONFIG_GREP is not set # CONFIG_EGREP is not set # CONFIG_FGREP is not set # CONFIG_FEATURE_GREP_CONTEXT is not set diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index d602e1d6ba816a..79721bb0ebfb98 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -11,6 +11,7 @@ jobs: timeout-minutes: 20 strategy: + fail-fast: false matrix: arch: [arm, arm64, ppc64le, riscv64, x86_64] toolchain: [gcc, clang, llvm] @@ -363,7 +364,8 @@ jobs: - 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 + grep 'rust_seq_file read log: 1' qemu-stdout.log + grep 'rust_seq_file read log: 2' qemu-stdout.log # Report - run: | diff --git a/.github/workflows/kernel-arm64-debug-thinlto.config b/.github/workflows/kernel-arm64-debug-thinlto.config index bbeecdf025ba42..51e2d86ad4fdd1 100644 --- a/.github/workflows/kernel-arm64-debug-thinlto.config +++ b/.github/workflows/kernel-arm64-debug-thinlto.config @@ -1437,6 +1437,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-debug.config b/.github/workflows/kernel-arm64-debug.config index ac48354017a49d..c8345fb779343d 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=y +# CONFIG_PROC_FS is not set # CONFIG_PROC_CHILDREN is not set # CONFIG_SYSFS is not set # CONFIG_HUGETLBFS is not set diff --git a/.github/workflows/kernel-arm64-release-thinlto.config b/.github/workflows/kernel-arm64-release-thinlto.config index 30057ad8889c32..d9b6cb86f51ee1 100644 --- a/.github/workflows/kernel-arm64-release-thinlto.config +++ b/.github/workflows/kernel-arm64-release-thinlto.config @@ -1216,7 +1216,10 @@ CONFIG_FRAME_POINTER=y # Generic Kernel Debugging Instruments # # CONFIG_MAGIC_SYSRQ is not set -# CONFIG_DEBUG_FS is not set +CONFIG_DEBUG_FS=y +CONFIG_DEBUG_FS_ALLOW_ALL=y +# CONFIG_DEBUG_FS_DISALLOW_MOUNT is not set +# CONFIG_DEBUG_FS_ALLOW_NONE is not set CONFIG_HAVE_ARCH_KGDB=y # CONFIG_KGDB is not set CONFIG_ARCH_HAS_UBSAN_SANITIZE_ALL=y @@ -1355,6 +1358,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 984f5e3bfc46f4..7e25e135c37f2d 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=y +# CONFIG_PROC_FS is not set # 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 c166404450cd50..d6998e17867c5e 100644 --- a/.github/workflows/kernel-riscv64-debug.config +++ b/.github/workflows/kernel-riscv64-debug.config @@ -1122,7 +1122,10 @@ CONFIG_FRAME_POINTER=y # Generic Kernel Debugging Instruments # # CONFIG_MAGIC_SYSRQ is not set -# CONFIG_DEBUG_FS is not set +CONFIG_DEBUG_FS=y +CONFIG_DEBUG_FS_ALLOW_ALL=y +# CONFIG_DEBUG_FS_DISALLOW_MOUNT is not set +# CONFIG_DEBUG_FS_ALLOW_NONE is not set CONFIG_HAVE_ARCH_KGDB=y CONFIG_HAVE_ARCH_KGDB_QXFER_PKT=y # CONFIG_KGDB is not set diff --git a/.github/workflows/kernel-riscv64-release.config b/.github/workflows/kernel-riscv64-release.config index bc7ded4bddad64..7cee90953a7fe4 100644 --- a/.github/workflows/kernel-riscv64-release.config +++ b/.github/workflows/kernel-riscv64-release.config @@ -1110,7 +1110,10 @@ CONFIG_FRAME_POINTER=y # Generic Kernel Debugging Instruments # # CONFIG_MAGIC_SYSRQ is not set -# CONFIG_DEBUG_FS is not set +CONFIG_DEBUG_FS=y +CONFIG_DEBUG_FS_ALLOW_ALL=y +# CONFIG_DEBUG_FS_DISALLOW_MOUNT is not set +# CONFIG_DEBUG_FS_ALLOW_NONE is not set CONFIG_HAVE_ARCH_KGDB=y CONFIG_HAVE_ARCH_KGDB_QXFER_PKT=y # CONFIG_UBSAN is not set diff --git a/.github/workflows/kernel-x86_64-debug-thinlto.config b/.github/workflows/kernel-x86_64-debug-thinlto.config index 2eee24e6910bdb..19be7f94f8dbb2 100644 --- a/.github/workflows/kernel-x86_64-debug-thinlto.config +++ b/.github/workflows/kernel-x86_64-debug-thinlto.config @@ -1222,9 +1222,9 @@ CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE=0x1 CONFIG_MAGIC_SYSRQ_SERIAL=y CONFIG_MAGIC_SYSRQ_SERIAL_SEQUENCE="" CONFIG_DEBUG_FS=y -# CONFIG_DEBUG_FS_ALLOW_ALL is not set +CONFIG_DEBUG_FS_ALLOW_ALL=y # CONFIG_DEBUG_FS_DISALLOW_MOUNT is not set -CONFIG_DEBUG_FS_ALLOW_NONE=y +# CONFIG_DEBUG_FS_ALLOW_NONE is not set CONFIG_HAVE_ARCH_KGDB=y CONFIG_KGDB=y CONFIG_KGDB_HONOUR_BLOCKLIST=y @@ -1441,6 +1441,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 798d384b11371c..2319e182cf6d88 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=y +# CONFIG_PROC_FS is not set # CONFIG_PROC_CHILDREN is not set CONFIG_KERNFS=y CONFIG_SYSFS=y @@ -1217,9 +1217,9 @@ CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE=0x1 CONFIG_MAGIC_SYSRQ_SERIAL=y CONFIG_MAGIC_SYSRQ_SERIAL_SEQUENCE="" CONFIG_DEBUG_FS=y -# CONFIG_DEBUG_FS_ALLOW_ALL is not set +CONFIG_DEBUG_FS_ALLOW_ALL=y # CONFIG_DEBUG_FS_DISALLOW_MOUNT is not set -CONFIG_DEBUG_FS_ALLOW_NONE=y +# CONFIG_DEBUG_FS_ALLOW_NONE is not set CONFIG_HAVE_ARCH_KGDB=y CONFIG_KGDB=y CONFIG_KGDB_HONOUR_BLOCKLIST=y diff --git a/.github/workflows/kernel-x86_64-release-thinlto.config b/.github/workflows/kernel-x86_64-release-thinlto.config index b4534eb809b2b5..9be9f252389872 100644 --- a/.github/workflows/kernel-x86_64-release-thinlto.config +++ b/.github/workflows/kernel-x86_64-release-thinlto.config @@ -1289,7 +1289,10 @@ CONFIG_STACK_VALIDATION=y # Generic Kernel Debugging Instruments # # CONFIG_MAGIC_SYSRQ is not set -# CONFIG_DEBUG_FS is not set +CONFIG_DEBUG_FS=y +CONFIG_DEBUG_FS_ALLOW_ALL=y +# CONFIG_DEBUG_FS_DISALLOW_MOUNT is not set +# CONFIG_DEBUG_FS_ALLOW_NONE is not set CONFIG_HAVE_ARCH_KGDB=y CONFIG_ARCH_HAS_UBSAN_SANITIZE_ALL=y # CONFIG_UBSAN is not set @@ -1397,6 +1400,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 3fa20125dfce44..dcb8eb7af4e0e9 100755 --- a/.github/workflows/qemu-init.sh +++ b/.github/workflows/qemu-init.sh @@ -1,41 +1,41 @@ #!/bin/sh -# busybox insmod rust_minimal.ko -# busybox rmmod rust_minimal.ko +busybox insmod rust_minimal.ko +busybox rmmod rust_minimal.ko -# busybox insmod rust_print.ko -# busybox rmmod rust_print.ko +busybox insmod rust_print.ko +busybox rmmod rust_print.ko -# busybox insmod rust_module_parameters.ko -# busybox rmmod rust_module_parameters.ko +busybox insmod rust_module_parameters.ko +busybox rmmod rust_module_parameters.ko -# busybox insmod rust_sync.ko -# busybox rmmod rust_sync.ko +busybox insmod rust_sync.ko +busybox rmmod rust_sync.ko -# busybox insmod rust_chrdev.ko -# busybox rmmod rust_chrdev.ko +busybox insmod rust_chrdev.ko +busybox rmmod rust_chrdev.ko -# busybox insmod rust_miscdev.ko -# busybox rmmod rust_miscdev.ko +busybox insmod rust_miscdev.ko +busybox rmmod rust_miscdev.ko -# busybox insmod rust_stack_probing.ko -# busybox rmmod rust_stack_probing.ko +busybox insmod rust_stack_probing.ko +busybox rmmod rust_stack_probing.ko -# busybox insmod rust_semaphore.ko -# busybox rmmod rust_semaphore.ko +busybox insmod rust_semaphore.ko +busybox rmmod rust_semaphore.ko -# busybox insmod rust_semaphore_c.ko -# busybox rmmod rust_semaphore_c.ko +busybox insmod rust_semaphore_c.ko +busybox rmmod rust_semaphore_c.ko -# busybox insmod rust_module_parameters_loadable_default.ko -# busybox insmod rust_module_parameters_loadable_custom.ko \ -# my_bool=n \ -# my_i32=345543 \ -# my_str=🦀mod \ -# my_usize=84 \ -# my_array=1,2,3 -# busybox rmmod rust_module_parameters_loadable_default.ko -# busybox rmmod rust_module_parameters_loadable_custom.ko +busybox insmod rust_module_parameters_loadable_default.ko +busybox insmod rust_module_parameters_loadable_custom.ko \ + my_bool=n \ + my_i32=345543 \ + my_str=🦀mod \ + my_usize=84 \ + my_array=1,2,3 +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 @@ -44,14 +44,10 @@ busybox mkdir debugfs busybox mount -t debugfs debugfs /debugfs 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 echo "reading from device" busybox cat /dev/rust_seq_file0 busybox cat /dev/rust_seq_file0 -busybox echo "reading debug file" busybox cat /debugfs/rust_seq_file -busybox echo "removing device file" busybox rm /dev/rust_seq_file0 -busybox echo "removing module" busybox rmmod rust_seq_file.ko busybox reboot -f diff --git a/.github/workflows/qemu-initramfs.desc b/.github/workflows/qemu-initramfs.desc index faf8f994c564a2..827da30c07f1b6 100644 --- a/.github/workflows/qemu-initramfs.desc +++ b/.github/workflows/qemu-initramfs.desc @@ -1,9 +1,20 @@ dir /bin 0755 0 0 dir /sys 0755 0 0 dir /dev 0755 0 0 -file /bin/busybox busybox/busybox 0755 0 0 +file /bin/busybox busybox 0755 0 0 slink /bin/sh /bin/busybox 0755 0 0 file /init .github/workflows/qemu-init.sh 0755 0 0 +file /rust_print.ko samples/rust/rust_print.ko 0755 0 0 +file /rust_module_parameters.ko samples/rust/rust_module_parameters.ko 0755 0 0 +file /rust_sync.ko samples/rust/rust_sync.ko 0755 0 0 +file /rust_chrdev.ko samples/rust/rust_chrdev.ko 0755 0 0 +file /rust_miscdev.ko samples/rust/rust_miscdev.ko 0755 0 0 +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_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 file /rust_minimal.ko samples/rust/rust_minimal.ko 0755 0 0 file /rust_seq_file.ko samples/rust/rust_seq_file.ko 0755 0 0 diff --git a/fs/debugfs/inode.c b/fs/debugfs/inode.c index 509ae08b4caf4a..2f117c57160dc0 100644 --- a/fs/debugfs/inode.c +++ b/fs/debugfs/inode.c @@ -10,7 +10,7 @@ * See ./Documentation/core-api/kernel-api.rst for more details. */ -#define pr_fmt(fmt) "debugfs: " fmt +#define pr_fmt(fmt) "debugfs: " fmt #include #include @@ -30,7 +30,7 @@ #include "internal.h" -#define DEBUGFS_DEFAULT_MODE 0700 +#define DEBUGFS_DEFAULT_MODE 0700 static struct vfsmount *debugfs_mount; static int debugfs_mount_count; @@ -56,15 +56,15 @@ static int debugfs_setattr(struct user_namespace *mnt_userns, } static const struct inode_operations debugfs_file_inode_operations = { - .setattr = debugfs_setattr, + .setattr = debugfs_setattr, }; static const struct inode_operations debugfs_dir_inode_operations = { - .lookup = simple_lookup, - .setattr = debugfs_setattr, + .lookup = simple_lookup, + .setattr = debugfs_setattr, }; static const struct inode_operations debugfs_symlink_inode_operations = { - .get_link = simple_get_link, - .setattr = debugfs_setattr, + .get_link = simple_get_link, + .setattr = debugfs_setattr, }; static struct inode *debugfs_get_inode(struct super_block *sb) @@ -72,8 +72,8 @@ static struct inode *debugfs_get_inode(struct super_block *sb) struct inode *inode = new_inode(sb); if (inode) { inode->i_ino = get_next_ino(); - inode->i_atime = inode->i_mtime = inode->i_ctime = - current_time(inode); + inode->i_atime = inode->i_mtime = + inode->i_ctime = current_time(inode); } return inode; } @@ -84,12 +84,19 @@ struct debugfs_mount_opts { umode_t mode; }; -enum { Opt_uid, Opt_gid, Opt_mode, Opt_err }; +enum { + Opt_uid, + Opt_gid, + Opt_mode, + Opt_err +}; -static const match_table_t tokens = { { Opt_uid, "uid=%u" }, - { Opt_gid, "gid=%u" }, - { Opt_mode, "mode=%o" }, - { Opt_err, NULL } }; +static const match_table_t tokens = { + {Opt_uid, "uid=%u"}, + {Opt_gid, "gid=%u"}, + {Opt_mode, "mode=%o"}, + {Opt_err, NULL} +}; struct debugfs_fs_info { struct debugfs_mount_opts mount_opts; @@ -133,7 +140,7 @@ static int debugfs_parse_options(char *data, struct debugfs_mount_opts *opts) return -EINVAL; opts->mode = option & S_IALLUGO; break; - /* + /* * We might like to report bad mount options here; * but traditionally debugfs has ignored all mount options */ @@ -199,10 +206,10 @@ static void debugfs_free_inode(struct inode *inode) } static const struct super_operations debugfs_super_operations = { - .statfs = simple_statfs, - .remount_fs = debugfs_remount, - .show_options = debugfs_show_options, - .free_inode = debugfs_free_inode, + .statfs = simple_statfs, + .remount_fs = debugfs_remount, + .show_options = debugfs_show_options, + .free_inode = debugfs_free_inode, }; static void debugfs_release_dentry(struct dentry *dentry) @@ -228,7 +235,7 @@ static const struct dentry_operations debugfs_dops = { static int debug_fill_super(struct super_block *sb, void *data, int silent) { - static const struct tree_descr debug_files[] = { { "" } }; + static const struct tree_descr debug_files[] = {{""}}; struct debugfs_fs_info *fsi; int err; @@ -243,7 +250,7 @@ static int debug_fill_super(struct super_block *sb, void *data, int silent) if (err) goto fail; - err = simple_fill_super(sb, DEBUGFS_MAGIC, debug_files); + err = simple_fill_super(sb, DEBUGFS_MAGIC, debug_files); if (err) goto fail; @@ -260,8 +267,9 @@ static int debug_fill_super(struct super_block *sb, void *data, int silent) return err; } -static struct dentry *debug_mount(struct file_system_type *fs_type, int flags, - const char *dev_name, void *data) +static struct dentry *debug_mount(struct file_system_type *fs_type, + int flags, const char *dev_name, + void *data) { if (!(debugfs_allow & DEBUGFS_ALLOW_API)) return ERR_PTR(-EPERM); @@ -270,10 +278,10 @@ static struct dentry *debug_mount(struct file_system_type *fs_type, int flags, } static struct file_system_type debug_fs_type = { - .owner = THIS_MODULE, - .name = "debugfs", - .mount = debug_mount, - .kill_sb = kill_litter_super, + .owner = THIS_MODULE, + .name = "debugfs", + .mount = debug_mount, + .kill_sb = kill_litter_super, }; MODULE_ALIAS_FS("debugfs"); @@ -375,10 +383,10 @@ static struct dentry *end_creating(struct dentry *dentry) return dentry; } -static struct dentry * -__debugfs_create_file(const char *name, umode_t mode, struct dentry *parent, - void *data, const struct file_operations *proxy_fops, - const struct file_operations *real_fops) +static struct dentry *__debugfs_create_file(const char *name, umode_t mode, + struct dentry *parent, void *data, + const struct file_operations *proxy_fops, + const struct file_operations *real_fops) { struct dentry *dentry; struct inode *inode; @@ -409,7 +417,7 @@ __debugfs_create_file(const char *name, umode_t mode, struct dentry *parent, inode->i_op = &debugfs_file_inode_operations; inode->i_fop = proxy_fops; dentry->d_fsdata = (void *)((unsigned long)real_fops | - DEBUGFS_FSDATA_IS_REAL_FOPS_BIT); + DEBUGFS_FSDATA_IS_REAL_FOPS_BIT); d_instantiate(dentry, inode); fsnotify_create(d_inode(dentry->d_parent), dentry); @@ -447,11 +455,11 @@ struct dentry *debugfs_create_file(const char *name, umode_t mode, struct dentry *parent, void *data, const struct file_operations *fops) { - return __debugfs_create_file( - name, mode, parent, data, - fops ? &debugfs_full_proxy_file_operations : - &debugfs_noop_file_operations, - fops); + + return __debugfs_create_file(name, mode, parent, data, + fops ? &debugfs_full_proxy_file_operations : + &debugfs_noop_file_operations, + fops); } EXPORT_SYMBOL_GPL(debugfs_create_file); @@ -483,14 +491,14 @@ EXPORT_SYMBOL_GPL(debugfs_create_file); * thus, may be used here. */ struct dentry *debugfs_create_file_unsafe(const char *name, umode_t mode, - struct dentry *parent, void *data, - const struct file_operations *fops) + struct dentry *parent, void *data, + const struct file_operations *fops) { - return __debugfs_create_file( - name, mode, parent, data, - fops ? &debugfs_open_proxy_file_operations : - &debugfs_noop_file_operations, - fops); + + return __debugfs_create_file(name, mode, parent, data, + fops ? &debugfs_open_proxy_file_operations : + &debugfs_noop_file_operations, + fops); } EXPORT_SYMBOL_GPL(debugfs_create_file_unsafe); @@ -588,8 +596,10 @@ EXPORT_SYMBOL_GPL(debugfs_create_dir); * * @f should return what ->d_automount() would. */ -struct dentry *debugfs_create_automount(const char *name, struct dentry *parent, - debugfs_automount_t f, void *data) +struct dentry *debugfs_create_automount(const char *name, + struct dentry *parent, + debugfs_automount_t f, + void *data) { struct dentry *dentry = start_creating(name, parent); struct inode *inode; @@ -695,7 +705,7 @@ static void __debugfs_file_removed(struct dentry *dentry) static void remove_one(struct dentry *victim) { - if (d_is_reg(victim)) + if (d_is_reg(victim)) __debugfs_file_removed(victim); simple_release_fs(&debugfs_mount, &debugfs_mount_count); } @@ -744,7 +754,7 @@ EXPORT_SYMBOL_GPL(debugfs_remove); * returned. */ struct dentry *debugfs_rename(struct dentry *old_dir, struct dentry *old_dentry, - struct dentry *new_dir, const char *new_name) + struct dentry *new_dir, const char *new_name) { int error; struct dentry *dentry = NULL, *trap; @@ -780,7 +790,8 @@ struct dentry *debugfs_rename(struct dentry *old_dir, struct dentry *old_dentry, } d_move(old_dentry, dentry); fsnotify_move(d_inode(old_dir), d_inode(new_dir), &old_name.name, - d_is_dir(old_dentry), NULL, old_dentry); + d_is_dir(old_dentry), + NULL, old_dentry); release_dentry_name_snapshot(&old_name); unlock_rename(new_dir, old_dir); dput(dentry); diff --git a/fs/seq_file.c b/fs/seq_file.c index 19b839a52af936..f8e1f4ee87ffca 100644 --- a/fs/seq_file.c +++ b/fs/seq_file.c @@ -83,8 +83,6 @@ int seq_open(struct file *file, const struct seq_operations *op) * file.open() which calls seq_open() and then sets FMODE_PWRITE. */ file->f_mode &= ~FMODE_PWRITE; - pr_alert("seq_open has file %p and private_data %p", file, - file->private_data); return 0; } EXPORT_SYMBOL(seq_open); @@ -152,7 +150,7 @@ static int traverse(struct seq_file *m, loff_t offset) */ ssize_t seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos) { - struct iovec iov = { .iov_base = buf, .iov_len = size }; + struct iovec iov = { .iov_base = buf, .iov_len = size}; struct kiocb kiocb; struct iov_iter iter; ssize_t ret; @@ -178,8 +176,6 @@ ssize_t seq_read_iter(struct kiocb *iocb, struct iov_iter *iter) void *p; int err = 0; - pr_alert("Start seq_read_iter private is %p", m->private); - if (!iov_iter_count(iter)) return 0; @@ -221,22 +217,20 @@ ssize_t seq_read_iter(struct kiocb *iocb, struct iov_iter *iter) m->count -= n; m->from += n; copied += n; - if (m->count) // hadn't managed to copy everything + if (m->count) // hadn't managed to copy everything goto Done; } // get a non-empty record in the buffer m->from = 0; - pr_alert("Start seq_read_iter just before calling start %p", - m->private); p = m->op->start(m, &m->index); while (1) { err = PTR_ERR(p); - if (!p || IS_ERR(p)) // EOF or an error + if (!p || IS_ERR(p)) // EOF or an error break; err = m->op->show(m, p); - if (err < 0) // hard error + if (err < 0) // hard error break; - if (unlikely(err)) // ->show() says "skip it" + if (unlikely(err)) // ->show() says "skip it" m->count = 0; if (unlikely(!m->count)) { // empty record p = m->op->next(m, p, &m->index); @@ -267,17 +261,16 @@ ssize_t seq_read_iter(struct kiocb *iocb, struct iov_iter *iter) p = m->op->next(m, p, &m->index); if (pos == m->index) { - pr_info_ratelimited( - "buggy .next function %ps did not update position index\n", - m->op->next); + pr_info_ratelimited("buggy .next function %ps did not update position index\n", + m->op->next); m->index++; } - if (!p || IS_ERR(p)) // no next record for us + if (!p || IS_ERR(p)) // no next record for us break; if (m->count >= iov_iter_count(iter)) break; err = m->op->show(m, p); - if (err > 0) { // ->show() says "skip it" + if (err > 0) { // ->show() says "skip it" m->count = offs; } else if (err || seq_has_overflowed(m)) { m->count = offs; @@ -598,7 +591,7 @@ int single_open(struct file *file, int (*show)(struct seq_file *, void *), EXPORT_SYMBOL(single_open); int single_open_size(struct file *file, int (*show)(struct seq_file *, void *), - void *data, size_t size) + void *data, size_t size) { char *buf = seq_buf_alloc(size); int ret; @@ -617,8 +610,7 @@ EXPORT_SYMBOL(single_open_size); int single_release(struct inode *inode, struct file *file) { - const struct seq_operations *op = - ((struct seq_file *)file->private_data)->op; + const struct seq_operations *op = ((struct seq_file *)file->private_data)->op; int res = seq_release(inode, file); kfree(op); return res; @@ -636,14 +628,13 @@ int seq_release_private(struct inode *inode, struct file *file) EXPORT_SYMBOL(seq_release_private); void *__seq_open_private(struct file *f, const struct seq_operations *ops, - int psize) + int psize) { int rc; void *private; struct seq_file *seq; - private - = kzalloc(psize, GFP_KERNEL_ACCOUNT); + private = kzalloc(psize, GFP_KERNEL_ACCOUNT); if (private == NULL) goto out; @@ -663,7 +654,7 @@ void *__seq_open_private(struct file *f, const struct seq_operations *ops, EXPORT_SYMBOL(__seq_open_private); int seq_open_private(struct file *filp, const struct seq_operations *ops, - int psize) + int psize) { return __seq_open_private(filp, ops, psize) ? 0 : -ENOMEM; } @@ -705,7 +696,7 @@ EXPORT_SYMBOL(seq_puts); * In usual cases, it will be better to use seq_printf(). It's easier to read. */ void seq_put_decimal_ull_width(struct seq_file *m, const char *delimiter, - unsigned long long num, unsigned int width) + unsigned long long num, unsigned int width) { int len; @@ -756,7 +747,7 @@ EXPORT_SYMBOL(seq_put_decimal_ull); * In usual cases, it will be better to use seq_printf(). It's easier to read. */ void seq_put_hex_ll(struct seq_file *m, const char *delimiter, - unsigned long long v, unsigned int width) + unsigned long long v, unsigned int width) { unsigned int len; int i; @@ -789,8 +780,7 @@ void seq_put_hex_ll(struct seq_file *m, const char *delimiter, m->count += len; } -void seq_put_decimal_ll(struct seq_file *m, const char *delimiter, - long long num) +void seq_put_decimal_ll(struct seq_file *m, const char *delimiter, long long num) { int len; @@ -914,7 +904,7 @@ struct list_head *seq_list_start(struct list_head *head, loff_t pos) { struct list_head *lh; - list_for_each (lh, head) + list_for_each(lh, head) if (pos-- == 0) return lh; @@ -952,7 +942,7 @@ struct hlist_node *seq_hlist_start(struct hlist_head *head, loff_t pos) { struct hlist_node *node; - hlist_for_each (node, head) + hlist_for_each(node, head) if (pos-- == 0) return node; return NULL; @@ -1008,11 +998,12 @@ EXPORT_SYMBOL(seq_hlist_next); * the _rcu list-mutation primitives such as hlist_add_head_rcu() * as long as the traversal is guarded by rcu_read_lock(). */ -struct hlist_node *seq_hlist_start_rcu(struct hlist_head *head, loff_t pos) +struct hlist_node *seq_hlist_start_rcu(struct hlist_head *head, + loff_t pos) { struct hlist_node *node; - __hlist_for_each_rcu (node, head) + __hlist_for_each_rcu(node, head) if (pos-- == 0) return node; return NULL; @@ -1031,7 +1022,8 @@ EXPORT_SYMBOL(seq_hlist_start_rcu); * the _rcu list-mutation primitives such as hlist_add_head_rcu() * as long as the traversal is guarded by rcu_read_lock(). */ -struct hlist_node *seq_hlist_start_head_rcu(struct hlist_head *head, loff_t pos) +struct hlist_node *seq_hlist_start_head_rcu(struct hlist_head *head, + loff_t pos) { if (!pos) return SEQ_START_TOKEN; @@ -1052,7 +1044,8 @@ EXPORT_SYMBOL(seq_hlist_start_head_rcu); * the _rcu list-mutation primitives such as hlist_add_head_rcu() * as long as the traversal is guarded by rcu_read_lock(). */ -struct hlist_node *seq_hlist_next_rcu(void *v, struct hlist_head *head, +struct hlist_node *seq_hlist_next_rcu(void *v, + struct hlist_head *head, loff_t *ppos) { struct hlist_node *node = v; @@ -1073,13 +1066,13 @@ EXPORT_SYMBOL(seq_hlist_next_rcu); * * Called at seq_file->op->start(). */ -struct hlist_node *seq_hlist_start_percpu(struct hlist_head __percpu *head, - int *cpu, loff_t pos) +struct hlist_node * +seq_hlist_start_percpu(struct hlist_head __percpu *head, int *cpu, loff_t pos) { struct hlist_node *node; - for_each_possible_cpu (*cpu) { - hlist_for_each (node, per_cpu_ptr(head, *cpu)) { + for_each_possible_cpu(*cpu) { + hlist_for_each(node, per_cpu_ptr(head, *cpu)) { if (pos-- == 0) return node; } @@ -1097,9 +1090,9 @@ EXPORT_SYMBOL(seq_hlist_start_percpu); * * Called at seq_file->op->next(). */ -struct hlist_node *seq_hlist_next_percpu(void *v, - struct hlist_head __percpu *head, - int *cpu, loff_t *pos) +struct hlist_node * +seq_hlist_next_percpu(void *v, struct hlist_head __percpu *head, + int *cpu, loff_t *pos) { struct hlist_node *node = v; @@ -1121,5 +1114,5 @@ EXPORT_SYMBOL(seq_hlist_next_percpu); void __init seq_file_init(void) { - seq_file_cache = KMEM_CACHE(seq_file, SLAB_ACCOUNT | SLAB_PANIC); + seq_file_cache = KMEM_CACHE(seq_file, SLAB_ACCOUNT|SLAB_PANIC); } diff --git a/rust/kernel/debugfs.rs b/rust/kernel/debugfs.rs index a63e8ef54635ca..6e3e33b3a98240 100644 --- a/rust/kernel/debugfs.rs +++ b/rust/kernel/debugfs.rs @@ -9,17 +9,25 @@ use core::{ use crate::{bindings, c_types, error, str::CStr, types::PointerWrapper, Result}; -/// TODOABK: finish doc -pub struct DebugFsDirEntry { +/// An `inode` in debugfs with a `T` stored in `i_private`. +pub(crate) struct DebugFsDirEntry { dentry: *mut bindings::dentry, data: *mut c_types::c_void, _wrapper: PhantomData, } -// TODOABK: safety +// SAFETY: There are methods available on [`DebugFsDirEntry`] so a thread can't +// actually do anything with a `&DebugFsDirEntry`. This makes it is safe to +// share across threads. unsafe impl Sync for DebugFsDirEntry {} impl DebugFsDirEntry { + /// Create a file in `debugfs`. + /// + /// # Safety + /// + /// `fops` must be valid when opening an `inode` with `data::into_pointer` + /// stored in `i_private`. pub(crate) unsafe fn create_file( name: &CStr, data: T, @@ -27,11 +35,15 @@ impl DebugFsDirEntry { ) -> Result { let name = name.as_char_ptr(); let data = data.into_pointer() as *mut _; + // SAFETY: Calling a C function. `name` will be a valid null-terminated + // string because it came from a `CStr`. The caller guarantees that + // `fops` is valid for an inode with `data` in `i_private`. let dentry_ptr = error::from_kernel_err_ptr(unsafe { bindings::debugfs_create_file(name, 0, ptr::null_mut(), data, fops) }); match dentry_ptr { Err(err) => { + // SAFETY: `data` was created by `T::into_pointer` just above. drop(unsafe { T::from_pointer(data) }); Err(err) } @@ -46,11 +58,14 @@ impl DebugFsDirEntry { impl Drop for DebugFsDirEntry { fn drop(&mut self) { - // TODOABK: safety + // SAFETY: Calling a C function. `dentry` must have been created by a + // call to [`DebugFsDirEntry::create_file`] which always returns a valid + // `dentry`. unsafe { bindings::debugfs_remove(self.dentry); } - // SAFETY: `self.data` was created by a call to `T::into_pointer`. + // SAFETY: `self.data` was created by a call to `T::into_pointer` in + // [`DebugFsDirEntry::create_file`]. unsafe { T::from_pointer(self.data) }; } } diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index c1b70313a109fe..b79f72b8c2d454 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -76,8 +76,8 @@ pub mod module_param; mod build_assert; pub mod prelude; pub mod print; - pub mod random; +#[cfg(CONFIG_DEBUG_FS)] pub mod seq_file; mod static_assert; #[doc(hidden)] diff --git a/rust/kernel/proc_fs.rs b/rust/kernel/proc_fs.rs deleted file mode 100644 index a9429c61f66d0c..00000000000000 --- a/rust/kernel/proc_fs.rs +++ /dev/null @@ -1,101 +0,0 @@ -// 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 index b224545e47b9ee..2612162379c0d8 100644 --- a/rust/kernel/seq_file.rs +++ b/rust/kernel/seq_file.rs @@ -9,13 +9,13 @@ //! //! Reference: -// Currently this module is only usable through proc_fs. -#![cfg(CONFIG_PROC_FS)] +// Currently this module is only usable through debugfs. +#![cfg(CONFIG_DEBUG_FS)] use core::{ fmt::{self, Display}, marker::PhantomData, - mem, ptr, + ptr, }; use crate::{ @@ -31,59 +31,94 @@ use crate::{ /// # Example /// /// ```rust,no_run -/// #![feature(allocator_api)] +/// use kernel::prelude::*; +/// use kernel::{ +/// seq_file, +/// sync::{Mutex, Ref, RefBorrow}, +/// }; /// -/// use core::iter::Peekable; /// use kernel::{Error, Result, seq_file}; +/// struct SharedStateInner { +/// token_count: usize, +/// } +/// +/// struct SharedState { +/// inner: Mutex, +/// } +/// +/// struct Token /// -/// struct Data(&'static [String]); +/// impl seq_file::SeqOperations for Token { +/// type OpenData = Ref; +/// type DataWrapper = Ref; +/// type IteratorWrapper = Box<(usize, usize)>; +/// type Item = usize; /// -/// impl seq_file::SeqOperations for Data { -/// type Item = &'static String; -/// type Iterator = core::slice::Iter<'static, String>; -/// type DataWrapper = Box; -/// type IteratorWrapper = Box>; +/// fn open<'a>(open_data: RefBorrow<'a, SharedState>) -> Result> { +/// Ok(open_data.into()) +/// } +/// +/// fn start<'a>(data: RefBorrow<'a, SharedState>) -> Option { +/// let total = data.inner.lock().token_count; +/// Box::try_new((total, 1)).ok() +/// } /// -/// fn start(&self) -> Result { -/// let iter = self.0.iter(); -/// Box::try_new(iter.peekable()).map_err(|_| Error::ENOMEM) +/// fn next(iterator: &mut Self::IteratorWrapper) -> bool { +/// let total = iterator.0; +/// let current = iterator.1; +/// if total == current { +/// false +/// } else { +/// iterator.1 += 1; +/// true +/// } /// } /// -/// fn display(item: &Self::Item) -> &str { -/// &item[..] +/// fn current(iterator: &Self::IteratorWrapper) -> core::option::Option { +/// let total = iterator.0; +/// let current = iterator.1; +/// if total >= current { +/// Some(current) +/// } else { +/// None +/// } /// } /// } /// ``` /// /// [`seq_operations`]: ../../../include/linux/seq_file.h pub trait SeqOperations { - /// TODOABK + /// Data stored in the seq_file's inode and accesible each time it is opened + /// in [`SeqOperations::open`]. type OpenData: PointerWrapper + Sync; - /// Wrapper used to store a pointer to `Self` on the C side. + /// Data stored in an opened seq_file. type DataWrapper: PointerWrapper; - /// Wrapper used to store a pointer to the iterator on the C side. + /// Data stored in a seq_file as it is being iterated through. type IteratorWrapper: PointerWrapper; - /// Type produced on each iteration. + /// Type produced on iteration. type Item: Display; - /// Called once each time the `seq_file` is opened. + /// Called when the seq_file is opened. + fn open<'a>( + open_data: ::Borrowed<'a>, + ) -> Result; + + /// Called once on each execution of fops->read or fops->read_iter. fn start<'a>( data: ::Borrowed<'a>, ) -> Option; - /// TODOABK: docs + /// Moves the iterator to the next item. Iteration will stop if this returns + /// `false`. fn next(iterator: &mut Self::IteratorWrapper) -> bool; - /// TODOABK: docs - fn current(iterator: &Self::IteratorWrapper) -> Option; - - /// TODOABK: docs - fn open<'a>( - open_data: ::Borrowed<'a>, - ) -> Result; + /// Returns the current item. Items can be skipped by returning `None`. + fn current<'a>( + iterator: ::Borrowed<'a>, + ) -> Option; } extern "C" fn stop_callback( @@ -135,15 +170,13 @@ extern "C" fn show_callback( 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 iterator = unsafe { T::IteratorWrapper::from_pointer(v) }; - if let Some(item) = T::current(&iterator) { - // 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. + // `start_callback` and both functions return either a null pointer or + // pointer generated by `T::IteratorWrapper::into_pointer`. We checked for + // null pointers above. + let iterator = unsafe { T::IteratorWrapper::borrow(v) }; + if let Some(item) = T::current(iterator) { + // SAFETY: Calling a C function. `FORMAT` is null terminated because it + // comes from a `CStr`. unsafe { bindings::seq_printf( m, @@ -152,9 +185,6 @@ extern "C" fn show_callback( ); } } - // Need to forget the iterator because the C side is still using a - // reference to it. - mem::forget(iterator); 0 } @@ -162,10 +192,10 @@ 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. + // SAFETY: This function is only called on the private_data of a file that + // was opened with [`SeqFileOperationsVTable::::open_callback`]. That + // function stores a pointer generated with T::DataWrapper::into_pointer in + // m->private. let data_wrapper = unsafe { T::DataWrapper::borrow((*m).private) }; let iterator = T::start(data_wrapper); // SAFETY: The caller guarantees that `pos` points to a valid `loff_t`. @@ -197,22 +227,27 @@ where show: Some(show_callback::), }; - extern "C" fn open_callback( + /// # Safety + /// + /// The `inode->i_private` must have been created by `T::OpenData::into_pointer`. + unsafe extern "C" fn open_callback( inode: *mut bindings::inode, file: *mut bindings::file, ) -> c_types::c_int { - // TODOABK: docs + // SAFETY: Calling a C function. Caller ensures that `file` is a valid + // file pointer and we've statically constructed the vtable. let result = unsafe { bindings::seq_open( file, - &Self::SEQ_VTABLE as *const _ as *mut bindings::seq_operations, + &Self::SEQ_VTABLE as *const bindings::seq_operations as *mut _, ) }; if result != 0 { - // TODOABK: Close file? return result; } + // SAFETY: Safety condition on this function requires that `i_private` + // was created by `T::OpenData::into_pointer`. let open_data = unsafe { T::OpenData::borrow((*inode).i_private) }; let data_wrapper = match T::open(open_data) { @@ -221,6 +256,9 @@ where }; let data_pointer = data_wrapper.into_pointer() as *mut c_types::c_void; + // SAFETY: The caller guarantees that `file` is a valid `file` pointer + // and `seq_open` stores a valid `seq_file` pointer in + // `file->private_data`. unsafe { (*((*file).private_data as *mut bindings::seq_file)).private = data_pointer as *mut c_types::c_void @@ -232,6 +270,7 @@ where open: Some(Self::open_callback), release: Some(bindings::seq_release), read: Some(bindings::seq_read), + read_iter: Some(bindings::seq_read_iter), llseek: Some(bindings::seq_lseek), check_flags: None, @@ -252,7 +291,6 @@ where mmap_supported_flags: 0, owner: ptr::null_mut(), poll: None, - read_iter: None, remap_file_range: None, sendpage: None, setlease: None, @@ -265,17 +303,21 @@ where }; } -/// TODOABK: docs +/// A file in [`debugfs`] which was created using a `seq_file`. pub struct SeqFileDebugFsDirEntry { _debugfs_entry: DebugFsDirEntry, } -/// TODOABK: finish doc +/// Create a `seq_file` in [`debugfs`]. pub fn debugfs_create_file( name: fmt::Arguments<'_>, data: T::OpenData, ) -> Result> { let name = CString::try_from_fmt(name)?; + // SAFETY: The `open` field of the vtable is valid iff the opened `inode` + // has `i_private` generated by `T::OpenData::into_pointer`. `create_file` + // is safe to call only if the vtable is valid when used on an inode with + // `data::into_pointer` stored in `i_private`. let debugfs_entry = unsafe { DebugFsDirEntry::create_file(&name, data, &SeqFileOperationsVTable::::VTABLE) }?; diff --git a/rust/kernel/sync/arc.rs b/rust/kernel/sync/arc.rs index 4b80b4b7cb79eb..cd98806b84ed4e 100644 --- a/rust/kernel/sync/arc.rs +++ b/rust/kernel/sync/arc.rs @@ -15,7 +15,7 @@ //! //! [`Arc`]: https://doc.rust-lang.org/std/sync/struct.Arc.html -use crate::{bindings, pr_alert, Error, Opaque, Result}; +use crate::{bindings, Error, Opaque, Result}; use alloc::{ alloc::{alloc, dealloc}, vec::Vec, @@ -215,10 +215,6 @@ impl Deref for Ref { impl Clone for Ref { fn clone(&self) -> Self { - pr_alert!( - "------------------------ Incrementing refcount at address {:?}", - self.ptr - ); // INVARIANT: C `refcount_inc` saturates the refcount, so it cannot overflow to zero. // SAFETY: By the type invariant, there is necessarily a reference to the object, so it is // safe to increment the refcount. @@ -239,11 +235,6 @@ impl AsRef for Ref { impl Drop for Ref { fn drop(&mut self) { - pr_alert!( - "------------------------------ Decrementing refcount at address {:?}", - self.ptr - ); - // SAFETY: By the type invariant, there is necessarily a reference to the object. We cannot // touch `refcount` after it's decremented to a non-zero value because another thread/CPU // may concurrently decrement it to zero and free it. It is ok to have a raw pointer to diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig index 216bb22c8e9663..a2eb45153d8e78 100644 --- a/samples/rust/Kconfig +++ b/samples/rust/Kconfig @@ -122,7 +122,7 @@ config SAMPLE_RUST_PLATFORM config SAMPLE_RUST_SEQ_FILE tristate "Seq file" - depends on PROC_FS + depends on DEBUG_FS help This option builds the Rust seq_file sample. diff --git a/samples/rust/rust_seq_file.rs b/samples/rust/rust_seq_file.rs index 4fde5ebee1e5df..bd4b8b3793f03d 100644 --- a/samples/rust/rust_seq_file.rs +++ b/samples/rust/rust_seq_file.rs @@ -1,15 +1,16 @@ // SPDX-License-Identifier: GPL-2.0 -//! Rust miscellaneous device sample. +//! Rust misc device that reports debug information with a seq_file entry in +//! debugfs. use kernel::prelude::*; use kernel::{ file::File, file_operations::FileOperations, - io_buffer::{IoBufferReader, IoBufferWriter}, + io_buffer::IoBufferWriter, miscdev, seq_file, seq_file::SeqFileDebugFsDirEntry, - sync::{CondVar, Mutex, Ref, RefBorrow, UniqueRef}, + sync::{Mutex, Ref, RefBorrow, UniqueRef}, }; module! { @@ -20,10 +21,8 @@ module! { license: b"GPL v2", } -const MAX_TOKENS: usize = 3; - struct SharedStateInner { - token_count: usize, + read_count: usize, } struct SharedState { @@ -34,7 +33,7 @@ impl SharedState { fn try_new() -> Result> { let mut state = Pin::from(UniqueRef::try_new(Self { // SAFETY: `mutex_init!` is called below. - inner: unsafe { Mutex::new(SharedStateInner { token_count: 0 }) }, + inner: unsafe { Mutex::new(SharedStateInner { read_count: 0 }) }, })?); // SAFETY: `inner` is pinned when `state` is. @@ -69,15 +68,9 @@ impl FileOperations for Token { { let mut inner = shared.inner.lock(); - pr_alert!("read called, current count is {}", inner.token_count); - - // Consume a token. - inner.token_count += 1; + inner.read_count += 1; } - // Notify a possible writer waiting. - shared.state_changed.notify_all(); - // Write a one-byte 1 to the reader. data.write_slice(&[b'a'; 1])?; Ok(1) @@ -101,15 +94,11 @@ impl seq_file::SeqOperations for Token { type Item = Log; fn open<'a>(open_data: RefBorrow<'a, SharedState>) -> Result> { - pr_alert!( - "While opening, count is {}", - open_data.inner.lock().token_count, - ); Ok(open_data.into()) } fn start<'a>(data: RefBorrow<'a, SharedState>) -> Option { - let total = data.inner.lock().token_count; + let total = data.inner.lock().read_count; Box::try_new((total, 1)).ok() } @@ -124,7 +113,7 @@ impl seq_file::SeqOperations for Token { } } - fn current(iterator: &Self::IteratorWrapper) -> core::option::Option { + fn current(iterator: &(usize, usize)) -> core::option::Option { let total = iterator.0; let current = iterator.1; if total >= current { @@ -142,7 +131,7 @@ struct RustMiscdev { impl KernelModule for RustMiscdev { fn init(name: &'static CStr, _module: &'static ThisModule) -> Result { - pr_info!("Rust miscellaneous device sample (init)\n"); + pr_info!("Rust seq_file sample (init)\n"); let state = SharedState::try_new()?; let debugfs = seq_file::debugfs_create_file(fmt!("{name}"), state.clone())?; @@ -156,6 +145,6 @@ impl KernelModule for RustMiscdev { impl Drop for RustMiscdev { fn drop(&mut self) { - pr_info!("Rust seq file device sample (exit)\n"); + pr_info!("Rust seq_file sample (exit)\n"); } } diff --git a/samples/rust/rust_seq_file_old.rs b/samples/rust/rust_seq_file_old.rs deleted file mode 100644 index 6b75c158868654..00000000000000 --- a/samples/rust/rust_seq_file_old.rs +++ /dev/null @@ -1,137 +0,0 @@ -// 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: - -#![feature(allocator_api, global_asm)] - -use alloc::boxed::Box; -use core::{ - cmp::min, - convert::TryInto, - fmt::Write, - iter::{repeat, Peekable, Repeat, Take}, - pin::Pin, -}; -use kernel::{ - c_str, - debugfs::DebugFsDirEntry, - file::File, - file_operations::FileOperations, - io_buffer::IoBufferWriter, - miscdev, mutex_init, - prelude::*, - 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 = u32; - type DataWrapper = Pin>; - type IteratorWrapper = Box>>>; - - fn start(&self) -> Option { - 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 FileOperations for Token { - type Wrapper = Pin>; - type OpenData = Pin>; - - kernel::declare_file_operations!(read); - - fn open(state: &Pin>, _file: &File) -> Result { - Ok(state.clone()) - } - - fn read(shared: &Ref, _: &File, _: &mut T, _: u64) -> Result { - *(shared.0.lock()) += 1; - Ok(0) - } -} - -struct RustSeqFileDev { - _debugfs: DebugFsDirEntry>>, - _dev: Pin>>, -} - -impl KernelModule for RustSeqFileDev { - fn init() -> Result { - pr_info!("Rust seq_file sample (init)\n"); - - let state = State::try_new()?; - - let debugfs_dir_entry = - seq_file::debugfs_create_file(c_str!("rust_seq_file"), state.clone())?; - - let dev_reg = - miscdev::Registration::new_pinned::(c_str!("rust_seq_file"), None, state)?; - - let dev = RustSeqFileDev { - _debugfs: debugfs_dir_entry, - _dev: dev_reg, - }; - - Ok(dev) - } -} - -impl Drop for RustSeqFileDev { - fn drop(&mut self) { - pr_info!("Rust seq_file sample (exit)\n"); - } -}