From nobody Thu Oct 2 09:19:16 2025 Received: from mail-4319.protonmail.ch (mail-4319.protonmail.ch [185.70.43.19]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9695F2FFDF0 for ; Thu, 18 Sep 2025 14:45:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.70.43.19 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1758206739; cv=none; b=OaoL6Mywob6i3913zmuquJxiJm0VX9gvZfYA6gK2wgZm3vyKPRQwnV9fiK/xM51lPmGHoiStSMmLnS29XdyFAKkfoTedhmZjNZFDdvrWcslGvMYBz7QXALZh2KtLljZMArHtDX7cqSTWGx2S0MvYq8mERZ7tuem/mzlN8Tv4HzU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1758206739; c=relaxed/simple; bh=FqKN3y9EJEW0j8ov4oajdKc8YkhGP3e6hjWAMhhQ43k=; h=Date:To:From:Cc:Subject:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=eePHJ5YmDLoI06ToDOZfcDsxaJ3+83trOEy67034OCiQjmwoa9wbPyd+pE3SI7QD4kIn/Z3mbq2zMABZn8l3UA6XxL5md5fkf4XOfXJZVBC7aQiXEqnEy8h830XoIUfRhGPLJb5LjI1CTUrgh9KH5u0EKFNsm1IWI0CP765v7AU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=protonmail.com; spf=pass smtp.mailfrom=protonmail.com; dkim=pass (2048-bit key) header.d=protonmail.com header.i=@protonmail.com header.b=k1xLcwX/; arc=none smtp.client-ip=185.70.43.19 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=protonmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=protonmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=protonmail.com header.i=@protonmail.com header.b="k1xLcwX/" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=protonmail.com; s=protonmail3; t=1758206735; x=1758465935; bh=m+R27tzJ7AmHjz4WVvVD6BHl+2wxeikQjtVZLPSv+t8=; h=Date:To:From:Cc:Subject:Message-ID:In-Reply-To:References: Feedback-ID:From:To:Cc:Date:Subject:Reply-To:Feedback-ID: Message-ID:BIMI-Selector; b=k1xLcwX/oo9Lf6mtVPDUyNlBda5tO3thhGFMfeEBhwnQWRtC0ayZ7tx1TY2V4Rr95 Fls61q2Bpkt6N4RduYt7ChOWuEa8VSWJHF+gsRvFWhNYFSjuKDo50rriqK+9xMYAPT eh1xXZnBRBHCfwkwcXNXwMK6F/HRj4k2vya+wTbF2Mz1T/pZ0Wm51lD2/Ppbz+F72e wWYzWeNmIWiyjmjra7Lt0ET4S6futlsxErTe5zTYwowUHhUCis5y60Osra5oFHmvXv WEuik1syH5Uhb4rm/jXKExGI3h1P3YW0fAza6q5JFFMDCKhv92WdK5snP42xc3l+D5 JEbLROai/n/gA== Date: Thu, 18 Sep 2025 14:45:28 +0000 To: "aliceryhl@google.com" , "gregkh@linuxfoundation.org" , "arnd@arndb.de" From: ManeraKai Cc: "rust-for-linux@vger.kernel.org" , "linux-fsdevel@vger.kernel.org" , "linux-kernel@vger.kernel.org" , "manerakai@protonmail.com" Subject: [PATCH 1/3] rust: miscdevice: Moved `MiscDevice` to a more general abstraction Message-ID: <20250918144356.28585-2-manerakai@protonmail.com> In-Reply-To: <20250918144356.28585-1-manerakai@protonmail.com> References: <20250918144356.28585-1-manerakai@protonmail.com> Feedback-ID: 38045798:user:proton X-Pm-Message-ID: 78458ab6c2c2afb94af3e6754a82a25015208283 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" This new general abstraction is called `FileOperations`. `struct file_operations` is not only meant for misc. Its methods are accessible from any other driver type. This change, however, doesn't generalize the safe wrapping for all driver types, but rather just the method declarations. The actual safe wrappings are left for every driver type to implement. This may make each implementation simpler. For example, misc can choose not to implement the safe wrapping for `lock`, `sendfile`, or `sendpage`, since they have no use in misc drivers. Signed-off-by: ManeraKai --- rust/kernel/fs.rs | 1 + rust/kernel/fs/file_operations.rs | 91 +++++++++++++++++++++++++++++++ rust/kernel/miscdevice.rs | 86 ++--------------------------- samples/rust/rust_misc_device.rs | 6 +- 4 files changed, 101 insertions(+), 83 deletions(-) create mode 100644 rust/kernel/fs/file_operations.rs diff --git a/rust/kernel/fs.rs b/rust/kernel/fs.rs index 0121b38c59e6..94519b41086b 100644 --- a/rust/kernel/fs.rs +++ b/rust/kernel/fs.rs @@ -5,4 +5,5 @@ //! C headers: [`include/linux/fs.h`](srctree/include/linux/fs.h) =20 pub mod file; +pub mod file_operations; pub use self::file::{File, LocalFile}; diff --git a/rust/kernel/fs/file_operations.rs b/rust/kernel/fs/file_operat= ions.rs new file mode 100644 index 000000000000..aa60cd46a012 --- /dev/null +++ b/rust/kernel/fs/file_operations.rs @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Wrapper for struct file_operations. +//! +//! C headers: [`include/linux/fs.h`](srctree/include/linux/fs.h). + +use macros::vtable; + +#[cfg(CONFIG_COMPAT)] +use crate::fs::File; +use crate::{ + build_error, + error::{Result, VTABLE_DEFAULT_ERROR}, + miscdevice::MiscDeviceRegistration, + mm::virt::VmaNew, + seq_file::SeqFile, + types::ForeignOwnable, +}; + +/// Trait implemented by the private data of an open misc device. +#[vtable] +pub trait FileOperations: Sized { + /// What kind of pointer should `Self` be wrapped in. + type Ptr: ForeignOwnable + Send + Sync; + + /// Called when the misc device is opened. + /// + /// The returned pointer will be stored as the private data for the fi= le. + fn open(_file: &File, _misc: &MiscDeviceRegistration) -> Result<= Self::Ptr>; + + /// Called when the misc device is released. + fn release(device: Self::Ptr, _file: &File) { + drop(device); + } + + /// Handle for mmap. + /// + /// This function is invoked when a user space process invokes the `mm= ap` system call on + /// `file`. The function is a callback that is part of the VMA initial= izer. The kernel will do + /// initial setup of the VMA before calling this function. The functio= n can then interact with + /// the VMA initialization by calling methods of `vma`. If the functio= n does not return an + /// error, the kernel will complete initialization of the VMA accordin= g to the properties of + /// `vma`. + fn mmap( + _device: ::Borrowed<'_>, + _file: &File, + _vma: &VmaNew, + ) -> Result { + build_error!(VTABLE_DEFAULT_ERROR) + } + + /// Handler for ioctls. + /// + /// The `cmd` argument is usually manipulated using the utilities in [= `kernel::ioctl`]. + /// + /// [`kernel::ioctl`]: mod@crate::ioctl + fn ioctl( + _device: ::Borrowed<'_>, + _file: &File, + _cmd: u32, + _arg: usize, + ) -> Result { + build_error!(VTABLE_DEFAULT_ERROR) + } + + /// Handler for ioctls. + /// + /// Used for 32-bit userspace on 64-bit platforms. + /// + /// This method is optional and only needs to be provided if the ioctl= relies on structures + /// that have different layout on 32-bit and 64-bit userspace. If no i= mplementation is + /// provided, then `compat_ptr_ioctl` will be used instead. + #[cfg(CONFIG_COMPAT)] + fn compat_ioctl( + _device: ::Borrowed<'_>, + _file: &File, + _cmd: u32, + _arg: usize, + ) -> Result { + build_error!(VTABLE_DEFAULT_ERROR) + } + + /// Show info for this fd. + fn show_fdinfo( + _device: ::Borrowed<'_>, + _m: &SeqFile, + _file: &File, + ) { + build_error!(VTABLE_DEFAULT_ERROR) + } +} diff --git a/rust/kernel/miscdevice.rs b/rust/kernel/miscdevice.rs index 6373fe183b27..578f33383ce6 100644 --- a/rust/kernel/miscdevice.rs +++ b/rust/kernel/miscdevice.rs @@ -11,9 +11,9 @@ use crate::{ bindings, device::Device, - error::{to_result, Error, Result, VTABLE_DEFAULT_ERROR}, + error::{to_result, Error}, ffi::{c_int, c_long, c_uint, c_ulong}, - fs::File, + fs::{file_operations::FileOperations, File}, mm::virt::VmaNew, prelude::*, seq_file::SeqFile, @@ -30,7 +30,7 @@ pub struct MiscDeviceOptions { =20 impl MiscDeviceOptions { /// Create a raw `struct miscdev` ready for registration. - pub const fn into_raw(self) -> bindings::miscdevice { + pub const fn into_raw(self) -> bindings::miscdevice= { // SAFETY: All zeros is valid for this C type. let mut result: bindings::miscdevice =3D unsafe { MaybeUninit::zer= oed().assume_init() }; result.minor =3D bindings::MISC_DYNAMIC_MINOR as ffi::c_int; @@ -66,7 +66,7 @@ unsafe impl Send for MiscDeviceRegistration {} // parallel. unsafe impl Sync for MiscDeviceRegistration {} =20 -impl MiscDeviceRegistration { +impl MiscDeviceRegistration { /// Register a misc device. pub fn register(opts: MiscDeviceOptions) -> impl PinInit { try_pin_init!(Self { @@ -108,84 +108,10 @@ fn drop(self: Pin<&mut Self>) { unsafe { bindings::misc_deregister(self.inner.get()) }; } } - -/// Trait implemented by the private data of an open misc device. -#[vtable] -pub trait MiscDevice: Sized { - /// What kind of pointer should `Self` be wrapped in. - type Ptr: ForeignOwnable + Send + Sync; - - /// Called when the misc device is opened. - /// - /// The returned pointer will be stored as the private data for the fi= le. - fn open(_file: &File, _misc: &MiscDeviceRegistration) -> Result<= Self::Ptr>; - - /// Called when the misc device is released. - fn release(device: Self::Ptr, _file: &File) { - drop(device); - } - - /// Handle for mmap. - /// - /// This function is invoked when a user space process invokes the `mm= ap` system call on - /// `file`. The function is a callback that is part of the VMA initial= izer. The kernel will do - /// initial setup of the VMA before calling this function. The functio= n can then interact with - /// the VMA initialization by calling methods of `vma`. If the functio= n does not return an - /// error, the kernel will complete initialization of the VMA accordin= g to the properties of - /// `vma`. - fn mmap( - _device: ::Borrowed<'_>, - _file: &File, - _vma: &VmaNew, - ) -> Result { - build_error!(VTABLE_DEFAULT_ERROR) - } - - /// Handler for ioctls. - /// - /// The `cmd` argument is usually manipulated using the utilities in [= `kernel::ioctl`]. - /// - /// [`kernel::ioctl`]: mod@crate::ioctl - fn ioctl( - _device: ::Borrowed<'_>, - _file: &File, - _cmd: u32, - _arg: usize, - ) -> Result { - build_error!(VTABLE_DEFAULT_ERROR) - } - - /// Handler for ioctls. - /// - /// Used for 32-bit userspace on 64-bit platforms. - /// - /// This method is optional and only needs to be provided if the ioctl= relies on structures - /// that have different layout on 32-bit and 64-bit userspace. If no i= mplementation is - /// provided, then `compat_ptr_ioctl` will be used instead. - #[cfg(CONFIG_COMPAT)] - fn compat_ioctl( - _device: ::Borrowed<'_>, - _file: &File, - _cmd: u32, - _arg: usize, - ) -> Result { - build_error!(VTABLE_DEFAULT_ERROR) - } - - /// Show info for this fd. - fn show_fdinfo( - _device: ::Borrowed<'_>, - _m: &SeqFile, - _file: &File, - ) { - build_error!(VTABLE_DEFAULT_ERROR) - } -} - /// A vtable for the file operations of a Rust miscdevice. -struct MiscdeviceVTable(PhantomData); +struct MiscdeviceVTable(PhantomData); =20 -impl MiscdeviceVTable { +impl MiscdeviceVTable { /// # Safety /// /// `file` and `inode` must be the file and inode for a file that is u= ndergoing initialization. diff --git a/samples/rust/rust_misc_device.rs b/samples/rust/rust_misc_devi= ce.rs index e7ab77448f75..d052294cebb8 100644 --- a/samples/rust/rust_misc_device.rs +++ b/samples/rust/rust_misc_device.rs @@ -100,9 +100,9 @@ use kernel::{ c_str, device::Device, - fs::File, + fs::{file_operations::FileOperations, File}, ioctl::{_IO, _IOC_SIZE, _IOR, _IOW}, - miscdevice::{MiscDevice, MiscDeviceOptions, MiscDeviceRegistration}, + miscdevice::{MiscDeviceOptions, MiscDeviceRegistration}, new_mutex, prelude::*, sync::Mutex, @@ -154,7 +154,7 @@ struct RustMiscDevice { } =20 #[vtable] -impl MiscDevice for RustMiscDevice { +impl FileOperations for RustMiscDevice { type Ptr =3D Pin>; =20 fn open(_file: &File, misc: &MiscDeviceRegistration) -> Result>> { --=20 2.43.0 From nobody Thu Oct 2 09:19:16 2025 Received: from mail-4325.protonmail.ch (mail-4325.protonmail.ch [185.70.43.25]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E31B730CB58; Thu, 18 Sep 2025 14:45:43 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.70.43.25 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1758206746; cv=none; b=cVaAn3xTMmfDqhecJzAOldsj1SjCe5UgOQqyeVqQVWMLns9U/wGPoTkOgQ2RIPP3TU5MMkj5nLDTkCFrNPhJEzioaUywNAWsWXCDMxi9CBL/G7SFXP0A2cEwpG1evHCKpaQ2Db9H0S7vjWanS7ux1MPX8N/WegjCd1GpPYa8+Fg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1758206746; c=relaxed/simple; bh=+UgYxx1BHBHGbRZ9HqN539ZaU2MSiTdwwwtcvH6kzZ0=; h=Date:To:From:Cc:Subject:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=MRSTRrVac+4OVULl3PifAWENOUVaMB05pRlRVo9ybe1F/BvxEVHg4cI1NTQXu+dnsESOGZSEnB+KMEP0tkc9E4rgkev10bfVuqQd1VY3wAqJtrWT0It30JPP+eKxVPM9SG0Ax25haiDaT+JfbBZPYN5vw78THHstviLfYNvzl8E= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=protonmail.com; spf=pass smtp.mailfrom=protonmail.com; dkim=pass (2048-bit key) header.d=protonmail.com header.i=@protonmail.com header.b=TO/eaAe8; arc=none smtp.client-ip=185.70.43.25 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=protonmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=protonmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=protonmail.com header.i=@protonmail.com header.b="TO/eaAe8" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=protonmail.com; s=protonmail3; t=1758206741; x=1758465941; bh=IkIWbcDezBOVmVawtSnjMHqDnlXRjl6QRBP3w/Gwm14=; h=Date:To:From:Cc:Subject:Message-ID:In-Reply-To:References: Feedback-ID:From:To:Cc:Date:Subject:Reply-To:Feedback-ID: Message-ID:BIMI-Selector; b=TO/eaAe8ZSmuoy0swQd2y7iM+cMYtGTau6hCPtb6PqChvSymlf8aYOSzf+XaRXqps t4EPFlTPqGDwtEQP4+ts1oAtvE7mVjxHOWlaCkPQmrQtkNtpS7UXXQUf/jybLkkd1O Fagh5UsXhuZVoHopH8hp31xtzaAmpgv1PwF2kRhQYEaHHy9q53YjpAa3BuCxxJiqPF Bi9RdkUEeEVgDGNLohpxmG9tXfNYQVmk1XQCHZHhJYjFgOTiLOznxjswNUh3uX3q7+ Z1NUrXVMGHruQIC9ZEipkrpuwhLMpvjB6I06HHiRd2Vr1xZDZuykDfvCixgJjDVwAg 0Fo13Sq1jh9hA== Date: Thu, 18 Sep 2025 14:45:36 +0000 To: "aliceryhl@google.com" , "gregkh@linuxfoundation.org" , "arnd@arndb.de" From: ManeraKai Cc: "rust-for-linux@vger.kernel.org" , "linux-fsdevel@vger.kernel.org" , "linux-kernel@vger.kernel.org" , "manerakai@protonmail.com" Subject: [PATCH 2/3] rust: miscdevice: Implemented `read` and `write` Message-ID: <20250918144356.28585-3-manerakai@protonmail.com> In-Reply-To: <20250918144356.28585-1-manerakai@protonmail.com> References: <20250918144356.28585-1-manerakai@protonmail.com> Feedback-ID: 38045798:user:proton X-Pm-Message-ID: e9435c9509f46a2a15d96b514e7389f2d7900d88 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Added the general declaration in `FileOperations`. And implemented the safe wrapping for misc. Note: Renamed some raw pointer variables to `raw_`. I'm not sure this way of naming is good or not. I would like your opinion. Signed-off-by: ManeraKai --- rust/kernel/fs/file_operations.rs | 20 +++++- rust/kernel/miscdevice.rs | 108 +++++++++++++++++++++++------- 2 files changed, 104 insertions(+), 24 deletions(-) diff --git a/rust/kernel/fs/file_operations.rs b/rust/kernel/fs/file_operat= ions.rs index aa60cd46a012..b21a2bae4803 100644 --- a/rust/kernel/fs/file_operations.rs +++ b/rust/kernel/fs/file_operations.rs @@ -14,7 +14,7 @@ miscdevice::MiscDeviceRegistration, mm::virt::VmaNew, seq_file::SeqFile, - types::ForeignOwnable, + types::ForeignOwnable, uaccess::{UserSliceReader, UserSliceWriter}, }; =20 /// Trait implemented by the private data of an open misc device. @@ -23,6 +23,24 @@ pub trait FileOperations: Sized { /// What kind of pointer should `Self` be wrapped in. type Ptr: ForeignOwnable + Send + Sync; =20 + /// Handler for read. + fn read( + _device: ::Borrowed<'_>, + _buf: UserSliceWriter, + _offset: &mut i64, + ) -> Result { + build_error!(VTABLE_DEFAULT_ERROR) + } + + /// Handler for write. + fn write( + _device: ::Borrowed<'_>, + mut _buf: UserSliceReader, + _offset: &mut i64, + ) -> Result { + build_error!(VTABLE_DEFAULT_ERROR) + } + /// Called when the misc device is opened. /// /// The returned pointer will be stored as the private data for the fi= le. diff --git a/rust/kernel/miscdevice.rs b/rust/kernel/miscdevice.rs index 578f33383ce6..f4b6388a3742 100644 --- a/rust/kernel/miscdevice.rs +++ b/rust/kernel/miscdevice.rs @@ -8,6 +8,8 @@ //! //! Reference: =20 +use bindings::loff_t; + use crate::{ bindings, device::Device, @@ -17,7 +19,7 @@ mm::virt::VmaNew, prelude::*, seq_file::SeqFile, - types::{ForeignOwnable, Opaque}, + types::{ForeignOwnable, Opaque}, uaccess::UserSlice, }; use core::{marker::PhantomData, mem::MaybeUninit, pin::Pin}; =20 @@ -112,6 +114,54 @@ fn drop(self: Pin<&mut Self>) { struct MiscdeviceVTable(PhantomData); =20 impl MiscdeviceVTable { + unsafe extern "C" fn read( + raw_file: *mut bindings::file, + raw_buf: *mut ffi::c_char, + size: usize, + raw_offset: *mut loff_t, + ) -> c_long { + // SAFETY: The read call of a file can access file and its private= _data. + let raw_device =3D unsafe { (*raw_file).private_data }; + + // SAFETY: The read call of a file can borrow the private_data of = the file. + let device =3D unsafe { ::borrow(raw_dev= ice) }; + + let user_slice =3D UserSlice::new(UserPtr::from_ptr(raw_buf as *mu= t c_void), size); + let user_slice_writer =3D user_slice.writer(); + + // SAFETY: The read call of a file can access and modify the offse= t pointer value. + let offset =3D unsafe { &mut *raw_offset }; + + match T::read(device, user_slice_writer, offset) { + Ok(ret) =3D> ret as c_long, + Err(err) =3D> err.to_errno() as c_long, + } + } + + unsafe extern "C" fn write( + raw_file: *mut bindings::file, + raw_buf: *const ffi::c_char, + size: usize, + raw_offset: *mut loff_t, + ) -> c_long { + // SAFETY: The read call of a file can access file and its private= _data. + let raw_device =3D unsafe { (*raw_file).private_data }; + + // SAFETY: The read call of a file can borrow the private_data of = the file. + let device =3D unsafe { ::borrow(raw_dev= ice) }; + + let user_slice =3D UserSlice::new(UserPtr::from_ptr(raw_buf as *mu= t c_void), size); + let user_slice_reader =3D user_slice.reader(); + + // SAFETY: The read call of a file can access and modify the offse= t pointer value. + let offset =3D unsafe { &mut *raw_offset }; + + match T::write(device, user_slice_reader, offset) { + Ok(ret) =3D> ret as c_long, + Err(err) =3D> err.to_errno() as c_long, + } + } + /// # Safety /// /// `file` and `inode` must be the file and inode for a file that is u= ndergoing initialization. @@ -124,13 +174,13 @@ impl MiscdeviceVTable { } =20 // SAFETY: The open call of a file can access the private data. - let misc_ptr =3D unsafe { (*raw_file).private_data }; + let raw_misc =3D unsafe { (*raw_file).private_data }; =20 // SAFETY: This is a miscdevice, so `misc_open()` set the private = data to a pointer to the // associated `struct miscdevice` before calling into this method.= Furthermore, // `misc_open()` ensures that the miscdevice can't be unregistered= and freed during this // call to `fops_open`. - let misc =3D unsafe { &*misc_ptr.cast::>= () }; + let misc =3D unsafe { &*raw_misc.cast::>= () }; =20 // SAFETY: // * This underlying file is valid for (much longer than) the dura= tion of `T::open`. @@ -157,16 +207,19 @@ impl MiscdeviceVTable { /// /// `file` and `inode` must be the file and inode for a file that is b= eing released. The file /// must be associated with a `MiscDeviceRegistration`. - unsafe extern "C" fn release(_inode: *mut bindings::inode, file: *mut = bindings::file) -> c_int { + unsafe extern "C" fn release( + _inode: *mut bindings::inode, + raw_file: *mut bindings::file, + ) -> c_int { // SAFETY: The release call of a file owns the private data. - let private =3D unsafe { (*file).private_data }; + let raw_device =3D unsafe { (*raw_file).private_data }; // SAFETY: The release call of a file owns the private data. - let ptr =3D unsafe { ::from_foreign(priv= ate) }; + let device =3D unsafe { ::from_foreign(r= aw_device) }; =20 // SAFETY: // * The file is valid for the duration of this call. // * There is no active fdget_pos region on the file on this threa= d. - T::release(ptr, unsafe { File::from_raw_file(file) }); + T::release(device, unsafe { File::from_raw_file(raw_file) }); =20 0 } @@ -176,21 +229,21 @@ impl MiscdeviceVTable { /// `file` must be a valid file that is associated with a `MiscDeviceR= egistration`. /// `vma` must be a vma that is currently being mmap'ed with this file. unsafe extern "C" fn mmap( - file: *mut bindings::file, + raw_file: *mut bindings::file, vma: *mut bindings::vm_area_struct, ) -> c_int { // SAFETY: The mmap call of a file can access the private data. - let private =3D unsafe { (*file).private_data }; + let raw_device =3D unsafe { (*raw_file).private_data }; // SAFETY: This is a Rust Miscdevice, so we call `into_foreign` in= `open` and // `from_foreign` in `release`, and `fops_mmap` is guaranteed to b= e called between those // two operations. - let device =3D unsafe { ::borrow(private= .cast()) }; + let device =3D unsafe { ::borrow(raw_dev= ice) }; // SAFETY: The caller provides a vma that is undergoing initial VM= A setup. let area =3D unsafe { VmaNew::from_raw(vma) }; // SAFETY: // * The file is valid for the duration of this call. // * There is no active fdget_pos region on the file on this threa= d. - let file =3D unsafe { File::from_raw_file(file) }; + let file =3D unsafe { File::from_raw_file(raw_file) }; =20 match T::mmap(device, file, area) { Ok(()) =3D> 0, @@ -201,16 +254,16 @@ impl MiscdeviceVTable { /// # Safety /// /// `file` must be a valid file that is associated with a `MiscDeviceR= egistration`. - unsafe extern "C" fn ioctl(file: *mut bindings::file, cmd: c_uint, arg= : c_ulong) -> c_long { + unsafe extern "C" fn ioctl(raw_file: *mut bindings::file, cmd: c_uint,= arg: c_ulong) -> c_long { // SAFETY: The ioctl call of a file can access the private data. - let private =3D unsafe { (*file).private_data }; + let raw_device =3D unsafe { (*raw_file).private_data }; // SAFETY: Ioctl calls can borrow the private data of the file. - let device =3D unsafe { ::borrow(private= ) }; + let device =3D unsafe { ::borrow(raw_dev= ice) }; =20 // SAFETY: // * The file is valid for the duration of this call. // * There is no active fdget_pos region on the file on this threa= d. - let file =3D unsafe { File::from_raw_file(file) }; + let file =3D unsafe { File::from_raw_file(raw_file) }; =20 match T::ioctl(device, file, cmd, arg) { Ok(ret) =3D> ret as c_long, @@ -223,19 +276,19 @@ impl MiscdeviceVTable { /// `file` must be a valid file that is associated with a `MiscDeviceR= egistration`. #[cfg(CONFIG_COMPAT)] unsafe extern "C" fn compat_ioctl( - file: *mut bindings::file, + raw_file: *mut bindings::file, cmd: c_uint, arg: c_ulong, ) -> c_long { // SAFETY: The compat ioctl call of a file can access the private = data. - let private =3D unsafe { (*file).private_data }; + let raw_device =3D unsafe { (*raw_file).private_data }; // SAFETY: Ioctl calls can borrow the private data of the file. - let device =3D unsafe { ::borrow(private= ) }; + let device =3D unsafe { ::borrow(raw_dev= ice) }; =20 // SAFETY: // * The file is valid for the duration of this call. // * There is no active fdget_pos region on the file on this threa= d. - let file =3D unsafe { File::from_raw_file(file) }; + let file =3D unsafe { File::from_raw_file(raw_file) }; =20 match T::compat_ioctl(device, file, cmd, arg) { Ok(ret) =3D> ret as c_long, @@ -247,15 +300,18 @@ impl MiscdeviceVTable { /// /// - `file` must be a valid file that is associated with a `MiscDevic= eRegistration`. /// - `seq_file` must be a valid `struct seq_file` that we can write t= o. - unsafe extern "C" fn show_fdinfo(seq_file: *mut bindings::seq_file, fi= le: *mut bindings::file) { + unsafe extern "C" fn show_fdinfo( + seq_file: *mut bindings::seq_file, + raw_file: *mut bindings::file, + ) { // SAFETY: The release call of a file owns the private data. - let private =3D unsafe { (*file).private_data }; + let raw_device =3D unsafe { (*raw_file).private_data }; // SAFETY: Ioctl calls can borrow the private data of the file. - let device =3D unsafe { ::borrow(private= ) }; + let device =3D unsafe { ::borrow(raw_dev= ice) }; // SAFETY: // * The file is valid for the duration of this call. // * There is no active fdget_pos region on the file on this threa= d. - let file =3D unsafe { File::from_raw_file(file) }; + let file =3D unsafe { File::from_raw_file(raw_file) }; // SAFETY: The caller ensures that the pointer is valid and exclus= ive for the duration in // which this method is called. let m =3D unsafe { SeqFile::from_raw(seq_file) }; @@ -264,6 +320,12 @@ impl MiscdeviceVTable { } =20 const VTABLE: bindings::file_operations =3D bindings::file_operations { + read: if T::HAS_READ { Some(Self::read) } else { None }, + write: if T::HAS_WRITE { + Some(Self::write) + } else { + None + }, open: Some(Self::open), release: Some(Self::release), mmap: if T::HAS_MMAP { Some(Self::mmap) } else { None }, --=20 2.43.0 From nobody Thu Oct 2 09:19:16 2025 Received: from mail-24431.protonmail.ch (mail-24431.protonmail.ch [109.224.244.31]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 930E530C34D; Thu, 18 Sep 2025 14:46:01 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=109.224.244.31 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1758206763; cv=none; b=QMzugz2GE4Aa8omn1rDQ9ar1OjIJMoY2KIL2cXKmiQoMocsgNnza1ecoa/xdGaaW6exQ49D21aowTBRv1z6kgMyZF2PvYTTOSrMZcaQo25DSqrIqBtAfzMglRgTTUWdPunjnLiWiIVkuuvs66izpLxWyAHlVdhH1pC4CjHLBy5E= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1758206763; c=relaxed/simple; bh=aCIOVZUs2dSGsGdh/1Ts7QgrL0yj95/pBCTntE0uMAI=; h=Date:To:From:Cc:Subject:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=Eir4w0g+y/DGbD3kEDTaAjWS0pbt5fUN6bJrq+qfojn6xpvNH4C8Thm5jYXm8gxF3trSzjt52n9dNxrbGzy6fBV147bM5uJogkd6DOAq4tj+m9QbpQeQFibht93suGDoCpgjnT+sH4eagRYDArZseELZ6kLOoPj+W1NPkpbnH/k= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=protonmail.com; spf=pass smtp.mailfrom=protonmail.com; dkim=pass (2048-bit key) header.d=protonmail.com header.i=@protonmail.com header.b=iytGnq27; arc=none smtp.client-ip=109.224.244.31 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=protonmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=protonmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=protonmail.com header.i=@protonmail.com header.b="iytGnq27" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=protonmail.com; s=protonmail3; t=1758206750; x=1758465950; bh=r6YasiPp7lhsuuq8MRxbmPcGSOWQNI511uYCgolntWc=; h=Date:To:From:Cc:Subject:Message-ID:In-Reply-To:References: Feedback-ID:From:To:Cc:Date:Subject:Reply-To:Feedback-ID: Message-ID:BIMI-Selector; b=iytGnq272gFb2zFF7YnvOG+PCdjKW8smasN3Dx0PZWLbrnvR8YVp+cLAwdXQL/LKh QS2VbQ2P2zY7aAbaSK9G/AeVmBSZJ5lUbrACS3jdExRm/OoUxeASekm3KNghMhkcPY JhEdvfu7yRw/VwGZtjNj790j7XdLJSrqbzQAFTP/LgETl6k7PuKob1BkhiKcvq4L0p 5WCntgkxhS0+zEewcOPXB95L+oYRJYdsm0yxKdm+vmpo54sanQ+9yE4USLlm7k+3Po NltxzHVQjVmAEEx8sX7WIdzzGdfW2ZFgOuSdGUIScAoq3JvZx05CRdLKO3qFIl8EVn LjSKLfwW/xi2g== Date: Thu, 18 Sep 2025 14:45:44 +0000 To: "aliceryhl@google.com" , "gregkh@linuxfoundation.org" , "arnd@arndb.de" From: ManeraKai Cc: "rust-for-linux@vger.kernel.org" , "linux-fsdevel@vger.kernel.org" , "linux-kernel@vger.kernel.org" , "manerakai@protonmail.com" Subject: [PATCH 3/3] samples: rust: Updated the example using the Rust MiscDevice abstraction Message-ID: <20250918144356.28585-4-manerakai@protonmail.com> In-Reply-To: <20250918144356.28585-1-manerakai@protonmail.com> References: <20250918144356.28585-1-manerakai@protonmail.com> Feedback-ID: 38045798:user:proton X-Pm-Message-ID: ec35da6f210f3eb7ba496a869f1f525a3f75fb43 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" This sample driver will now: - use the new general `FileOperations` abstraction. - have `read` and `write` methods, that use a persistent kernel buffer to store data. Signed-off-by: ManeraKai --- samples/rust/rust_misc_device.rs | 283 +++++++++++++++++++++---------- 1 file changed, 195 insertions(+), 88 deletions(-) diff --git a/samples/rust/rust_misc_device.rs b/samples/rust/rust_misc_devi= ce.rs index d052294cebb8..c8e90eb9b9ad 100644 --- a/samples/rust/rust_misc_device.rs +++ b/samples/rust/rust_misc_device.rs @@ -7,92 +7,139 @@ //! Below is an example userspace C program that exercises this sample's f= unctionality. //! //! ```c -//! #include -//! #include -//! #include -//! #include -//! #include -//! #include -//! -//! #define RUST_MISC_DEV_FAIL _IO('|', 0) -//! #define RUST_MISC_DEV_HELLO _IO('|', 0x80) -//! #define RUST_MISC_DEV_GET_VALUE _IOR('|', 0x81, int) -//! #define RUST_MISC_DEV_SET_VALUE _IOW('|', 0x82, int) -//! -//! int main() { -//! int value, new_value; -//! int fd, ret; -//! -//! // Open the device file -//! printf("Opening /dev/rust-misc-device for reading and writing\n"); -//! fd =3D open("/dev/rust-misc-device", O_RDWR); -//! if (fd < 0) { -//! perror("open"); -//! return errno; -//! } -//! -//! // Make call into driver to say "hello" -//! printf("Calling Hello\n"); -//! ret =3D ioctl(fd, RUST_MISC_DEV_HELLO, NULL); -//! if (ret < 0) { -//! perror("ioctl: Failed to call into Hello"); -//! close(fd); -//! return errno; -//! } -//! -//! // Get initial value -//! printf("Fetching initial value\n"); -//! ret =3D ioctl(fd, RUST_MISC_DEV_GET_VALUE, &value); -//! if (ret < 0) { -//! perror("ioctl: Failed to fetch the initial value"); -//! close(fd); -//! return errno; -//! } -//! -//! value++; -//! -//! // Set value to something different -//! printf("Submitting new value (%d)\n", value); -//! ret =3D ioctl(fd, RUST_MISC_DEV_SET_VALUE, &value); -//! if (ret < 0) { -//! perror("ioctl: Failed to submit new value"); -//! close(fd); -//! return errno; -//! } -//! -//! // Ensure new value was applied -//! printf("Fetching new value\n"); -//! ret =3D ioctl(fd, RUST_MISC_DEV_GET_VALUE, &new_value); -//! if (ret < 0) { -//! perror("ioctl: Failed to fetch the new value"); -//! close(fd); -//! return errno; -//! } -//! -//! if (value !=3D new_value) { -//! printf("Failed: Committed and retrieved values are different (%d -= %d)\n", value, new_value); -//! close(fd); -//! return -1; -//! } -//! -//! // Call the unsuccessful ioctl -//! printf("Attempting to call in to an non-existent IOCTL\n"); -//! ret =3D ioctl(fd, RUST_MISC_DEV_FAIL, NULL); -//! if (ret < 0) { -//! perror("ioctl: Succeeded to fail - this was expected"); -//! } else { -//! printf("ioctl: Failed to fail\n"); -//! close(fd); -//! return -1; -//! } -//! -//! // Close the device file -//! printf("Closing /dev/rust-misc-device\n"); -//! close(fd); -//! -//! printf("Success\n"); -//! return 0; -//! } +//!#include +//!#include +//!#include +//!#include +//!#include +//!#include +//!#include +//! +//!#define RUST_MISC_DEV_FAIL _IO('|', 0) +//!#define RUST_MISC_DEV_HELLO _IO('|', 0x80) +//!#define RUST_MISC_DEV_GET_VALUE _IOR('|', 0x81, int) +//!#define RUST_MISC_DEV_SET_VALUE _IOW('|', 0x82, int) +//! +//!int main() { +//! int value, new_value; +//! int fd, ret; +//! +//! // Open the device file +//! printf("Opening /dev/rust-misc-device for reading and writing\n"); +//! fd =3D open("/dev/rust-misc-device", O_RDWR); +//! if (fd < 0) { +//! perror("open"); +//! return errno; +//! } +//! +//! // Make call into driver to say "hello" +//! printf("Calling Hello\n"); +//! ret =3D ioctl(fd, RUST_MISC_DEV_HELLO, NULL); +//! if (ret < 0) { +//! perror("ioctl: Failed to call into Hello"); +//! close(fd); +//! return errno; +//! } +//! +//! // Get initial value +//! printf("Fetching initial value\n"); +//! ret =3D ioctl(fd, RUST_MISC_DEV_GET_VALUE, &value); +//! if (ret < 0) { +//! perror("ioctl: Failed to fetch the initial value"); +//! close(fd); +//! return errno; +//! } +//! +//! value++; +//! +//! // Set value to something different +//! printf("Submitting new value (%d)\n", value); +//! ret =3D ioctl(fd, RUST_MISC_DEV_SET_VALUE, &value); +//! if (ret < 0) { +//! perror("ioctl: Failed to submit new value"); +//! close(fd); +//! return errno; +//! } +//! +//! // Ensure new value was applied +//! printf("Fetching new value\n"); +//! ret =3D ioctl(fd, RUST_MISC_DEV_GET_VALUE, &new_value); +//! if (ret < 0) { +//! perror("ioctl: Failed to fetch the new value"); +//! close(fd); +//! return errno; +//! } +//! +//! if (value !=3D new_value) { +//! printf("Failed: Committed and retrieved values are different (%d -%= d)\n", +//! value, new_value); +//! close(fd); +//! return -1; +//! } +//! +//! // Call the unsuccessful ioctl +//! printf("Attempting to call in to an non-existent IOCTL\n"); +//! ret =3D ioctl(fd, RUST_MISC_DEV_FAIL, NULL); +//! if (ret < 0) { +//! perror("ioctl: Succeeded to fail - this was expected"); +//! } else { +//! printf("ioctl: Failed to fail\n"); +//! close(fd); +//! return -1; +//! } +//! +//! ssize_t bytes_written; +//! +//! // Write +//! char str1[] =3D {'H', 'e', 'l', 'l', 'o', '\0'}; +//! printf("Attempting to write to the file\n"); +//! bytes_written =3D write(fd, str1, sizeof(str1) * sizeof(uint8_t)); +//! if (bytes_written < 0) { +//! perror("Error writing to file"); +//! close(fd); +//! return 1; +//! } +//! +//! // Write with a custom offset +//! printf("Attempting to write to the file with a custom offset\n"); +//! char str2[] =3D {'i', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!', '\0'}; +//! // bytes_written =3D write(fd, str2, sizeof(str2) * sizeof(uint8_t)); +//! bytes_written =3D pwrite(fd, str2, sizeof(str2), 1); +//! if (bytes_written < 0) { +//! perror("Error writing to file 2"); +//! close(fd); +//! return 1; +//! } +//! +//! // Read +//! printf("Attempting to read from the file\n"); +//! char buffer1[20]; +//! ssize_t readCount1 =3D pread(fd, buffer1, sizeof(buffer1), 0); +//! if (readCount1 < 0) { +//! perror("Error reading from file\n"); +//! close(fd); +//! return errno; +//! } +//! printf("Data: \"%s\n\"", buffer1); +//! +//! // Read with a custom offset +//! printf("Attempting to read from the file with a custom offset\n"); +//! char buffer2[20]; +//! ssize_t readCount2 =3D pread(fd, buffer2, sizeof(buffer2), 4); +//! if (readCount2 < 0) { +//! perror("Error reading from file\n"); +//! close(fd); +//! return errno; +//! } +//! printf("Data: \"%s\n\"", buffer2); +//! +//! // Close the device file +//! printf("Closing /dev/rust-misc-device\n"); +//! close(fd); +//! +//! printf("Success\n"); +//! return 0; +//!} //! ``` =20 use core::pin::Pin; @@ -101,12 +148,13 @@ c_str, device::Device, fs::{file_operations::FileOperations, File}, + global_lock, ioctl::{_IO, _IOC_SIZE, _IOR, _IOW}, miscdevice::{MiscDeviceOptions, MiscDeviceRegistration}, new_mutex, prelude::*, sync::Mutex, - types::ARef, + types::{ARef, ForeignOwnable}, uaccess::{UserSlice, UserSliceReader, UserSliceWriter}, }; =20 @@ -128,6 +176,11 @@ struct RustMiscDeviceModule { _miscdev: MiscDeviceRegistration, } =20 +global_lock! { + // SAFETY: Initialized in module initializer before first use. + unsafe(uninit) static DATA: Mutex> =3D KVec::new(); +} + impl kernel::InPlaceModule for RustMiscDeviceModule { fn init(_module: &'static ThisModule) -> impl PinInit { pr_info!("Initialising Rust Misc Device Sample\n"); @@ -136,6 +189,9 @@ fn init(_module: &'static ThisModule) -> impl PinInit { name: c_str!("rust-misc-device"), }; =20 + // SAFETY: Called exactly once. + unsafe { DATA.init() }; + try_pin_init!(Self { _miscdev <- MiscDeviceRegistration::register(options), }) @@ -185,13 +241,64 @@ fn ioctl(me: Pin<&RustMiscDevice>, _file: &File, cmd:= u32, arg: usize) -> Result RUST_MISC_DEV_SET_VALUE =3D> me.set_value(UserSlice::new(arg, = size).reader())?, RUST_MISC_DEV_HELLO =3D> me.hello()?, _ =3D> { - dev_err!(me.dev, "-> IOCTL not recognised: {}\n", cmd); + dev_warn!(me.dev, "-> IOCTL not recognised: {}\n", cmd); return Err(ENOTTY); } }; =20 Ok(0) } + + fn read( + device: ::Borrowed<'_>, + mut buf: UserSliceWriter, + offset: &mut i64, + ) -> Result { + dev_info!(device.dev, "read() called\n"); + + let data =3D DATA.lock(); + + if *offset >=3D data.len() as i64 { + return Ok(0); + } + + let slice =3D &data[(*offset as usize)..]; + + dev_info!(device.dev, "Writing to user: {:?}\n", slice); + + buf.write_slice(slice)?; + *offset +=3D slice.len() as i64; + + Ok(data.len() as i64) + } + + fn write( + device: ::Borrowed<'_>, + buf: UserSliceReader, + offset: &mut i64, + ) -> Result { + dev_info!(device.dev, "write() called\n"); + + let mut data =3D DATA.lock(); + + let mut tmp =3D KVec::with_capacity(buf.len(), GFP_KERNEL)?; + buf.read_all(&mut tmp, GFP_KERNEL)?; + + for (i, val) in tmp.iter().enumerate() { + let idx =3D *offset as usize + i; + if idx < data.len() { + data[idx] =3D *val; + } else { + data.push(*val, GFP_KERNEL)?; + } + } + + *offset +=3D tmp.len() as i64; + + dev_info!(device.dev, "Reading from user: {:?}\n", tmp); + + Ok(*offset) + } } =20 #[pinned_drop] --=20 2.43.0