From 0d8a7c7bf47aa1002e0df792cb1d0652bc824cba Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Tue, 10 Dec 2024 09:39:00 +0000 Subject: [PATCH 01/68] rust: miscdevice: access file in fops This allows fops to access information about the underlying struct file for the miscdevice. For example, the Binder driver needs to inspect the O_NONBLOCK flag inside the fops->ioctl() hook. Signed-off-by: Alice Ryhl Reviewed-by: Lee Jones Tested-by: Lee Jones Reviewed-by: Danilo Krummrich Link: https://lore.kernel.org/r/20241210-miscdevice-file-param-v3-1-b2a79b666dc5@google.com Signed-off-by: Greg Kroah-Hartman --- rust/kernel/miscdevice.rs | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/rust/kernel/miscdevice.rs b/rust/kernel/miscdevice.rs index 7e2a79b3ae26..0cb79676c139 100644 --- a/rust/kernel/miscdevice.rs +++ b/rust/kernel/miscdevice.rs @@ -11,6 +11,7 @@ use crate::{ bindings, error::{to_result, Error, Result, VTABLE_DEFAULT_ERROR}, + fs::File, prelude::*, str::CStr, types::{ForeignOwnable, Opaque}, @@ -103,10 +104,10 @@ pub trait MiscDevice { /// Called when the misc device is opened. /// /// The returned pointer will be stored as the private data for the file. - fn open() -> Result; + fn open(_file: &File) -> Result; /// Called when the misc device is released. - fn release(device: Self::Ptr) { + fn release(device: Self::Ptr, _file: &File) { drop(device); } @@ -117,6 +118,7 @@ pub trait MiscDevice { /// [`kernel::ioctl`]: mod@crate::ioctl fn ioctl( _device: ::Borrowed<'_>, + _file: &File, _cmd: u32, _arg: usize, ) -> Result { @@ -133,6 +135,7 @@ pub trait MiscDevice { #[cfg(CONFIG_COMPAT)] fn compat_ioctl( _device: ::Borrowed<'_>, + _file: &File, _cmd: u32, _arg: usize, ) -> Result { @@ -187,7 +190,10 @@ unsafe extern "C" fn fops_open( return ret; } - let ptr = match T::open() { + // SAFETY: + // * The file is valid for the duration of this call. + // * There is no active fdget_pos region on the file on this thread. + let ptr = match T::open(unsafe { File::from_raw_file(file) }) { Ok(ptr) => ptr, Err(err) => return err.to_errno(), }; @@ -211,7 +217,10 @@ unsafe extern "C" fn fops_release( // SAFETY: The release call of a file owns the private data. let ptr = unsafe { ::from_foreign(private) }; - T::release(ptr); + // SAFETY: + // * The file is valid for the duration of this call. + // * There is no active fdget_pos region on the file on this thread. + T::release(ptr, unsafe { File::from_raw_file(file) }); 0 } @@ -229,7 +238,12 @@ unsafe extern "C" fn fops_ioctl( // SAFETY: Ioctl calls can borrow the private data of the file. let device = unsafe { ::borrow(private) }; - match T::ioctl(device, cmd, arg as usize) { + // SAFETY: + // * The file is valid for the duration of this call. + // * There is no active fdget_pos region on the file on this thread. + let file = unsafe { File::from_raw_file(file) }; + + match T::ioctl(device, file, cmd, arg as usize) { Ok(ret) => ret as c_long, Err(err) => err.to_errno() as c_long, } @@ -249,7 +263,12 @@ unsafe extern "C" fn fops_compat_ioctl( // SAFETY: Ioctl calls can borrow the private data of the file. let device = unsafe { ::borrow(private) }; - match T::compat_ioctl(device, cmd, arg as usize) { + // SAFETY: + // * The file is valid for the duration of this call. + // * There is no active fdget_pos region on the file on this thread. + let file = unsafe { File::from_raw_file(file) }; + + match T::compat_ioctl(device, file, cmd, arg as usize) { Ok(ret) => ret as c_long, Err(err) => err.to_errno() as c_long, } From 88441d5c6d17211bcbd5b429205b09c25598f756 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Tue, 10 Dec 2024 09:39:01 +0000 Subject: [PATCH 02/68] rust: miscdevice: access the `struct miscdevice` from fops->open() Providing access to the underlying `struct miscdevice` is useful for various reasons. For example, this allows you access the miscdevice's internal `struct device` for use with the `dev_*` printing macros. Note that since the underlying `struct miscdevice` could get freed at any point after the fops->open() call (if misc_deregister is called), only the open call is given access to it. To use `dev_*` printing macros from other fops hooks, take a refcount on `miscdevice->this_device` to keep it alive. See the linked thread for further discussion on the lifetime of `struct miscdevice`. Link: https://lore.kernel.org/r/2024120951-botanist-exhale-4845@gregkh Signed-off-by: Alice Ryhl Reviewed-by: Lee Jones Tested-by: Lee Jones Reviewed-by: Danilo Krummrich Link: https://lore.kernel.org/r/20241210-miscdevice-file-param-v3-2-b2a79b666dc5@google.com Signed-off-by: Greg Kroah-Hartman --- rust/kernel/miscdevice.rs | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/rust/kernel/miscdevice.rs b/rust/kernel/miscdevice.rs index 0cb79676c139..75a9d26c8001 100644 --- a/rust/kernel/miscdevice.rs +++ b/rust/kernel/miscdevice.rs @@ -97,14 +97,14 @@ impl PinnedDrop for MiscDeviceRegistration { /// Trait implemented by the private data of an open misc device. #[vtable] -pub trait MiscDevice { +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 file. - fn open(_file: &File) -> Result; + fn open(_file: &File, _misc: &MiscDeviceRegistration) -> Result; /// Called when the misc device is released. fn release(device: Self::Ptr, _file: &File) { @@ -182,24 +182,38 @@ const fn create_vtable() -> &'static bindings::file_operations { /// The file must be associated with a `MiscDeviceRegistration`. unsafe extern "C" fn fops_open( inode: *mut bindings::inode, - file: *mut bindings::file, + raw_file: *mut bindings::file, ) -> c_int { // SAFETY: The pointers are valid and for a file being opened. - let ret = unsafe { bindings::generic_file_open(inode, file) }; + let ret = unsafe { bindings::generic_file_open(inode, raw_file) }; if ret != 0 { return ret; } + // SAFETY: The open call of a file can access the private data. + let misc_ptr = unsafe { (*raw_file).private_data }; + + // 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 = unsafe { &*misc_ptr.cast::>() }; + // SAFETY: - // * The file is valid for the duration of this call. + // * This underlying file is valid for (much longer than) the duration of `T::open`. // * There is no active fdget_pos region on the file on this thread. - let ptr = match T::open(unsafe { File::from_raw_file(file) }) { + let file = unsafe { File::from_raw_file(raw_file) }; + + let ptr = match T::open(file, misc) { Ok(ptr) => ptr, Err(err) => return err.to_errno(), }; - // SAFETY: The open call of a file owns the private data. - unsafe { (*file).private_data = ptr.into_foreign().cast_mut() }; + // This overwrites the private data with the value specified by the user, changing the type of + // this file's private data. All future accesses to the private data is performed by other + // fops_* methods in this file, which all correctly cast the private data to the new type. + // + // SAFETY: The open call of a file can access the private data. + unsafe { (*raw_file).private_data = ptr.into_foreign().cast_mut() }; 0 } From 284ae0be4dcafff0a154c1e69e7430c144a7ddc2 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 10 Dec 2024 09:39:02 +0000 Subject: [PATCH 03/68] rust: miscdevice: Provide accessor to pull out miscdevice::this_device There are situations where a pointer to a `struct device` will become necessary (e.g. for calling into dev_*() functions). This accessor allows callers to pull this out from the `struct miscdevice`. Signed-off-by: Lee Jones Signed-off-by: Alice Ryhl Tested-by: Lee Jones Reviewed-by: Danilo Krummrich Link: https://lore.kernel.org/r/20241210-miscdevice-file-param-v3-3-b2a79b666dc5@google.com Signed-off-by: Greg Kroah-Hartman --- rust/kernel/miscdevice.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/rust/kernel/miscdevice.rs b/rust/kernel/miscdevice.rs index 75a9d26c8001..20895e809607 100644 --- a/rust/kernel/miscdevice.rs +++ b/rust/kernel/miscdevice.rs @@ -10,6 +10,7 @@ use crate::{ bindings, + device::Device, error::{to_result, Error, Result, VTABLE_DEFAULT_ERROR}, fs::File, prelude::*, @@ -85,6 +86,16 @@ impl MiscDeviceRegistration { pub fn as_raw(&self) -> *mut bindings::miscdevice { self.inner.get() } + + /// Access the `this_device` field. + pub fn device(&self) -> &Device { + // SAFETY: This can only be called after a successful register(), which always + // initialises `this_device` with a valid device. Furthermore, the signature of this + // function tells the borrow-checker that the `&Device` reference must not outlive the + // `&MiscDeviceRegistration` used to obtain it, so the last use of the reference must be + // before the underlying `struct miscdevice` is destroyed. + unsafe { Device::as_ref((*self.as_raw()).this_device) } + } } #[pinned_drop] From 4a9ce18874068aad0df0bee877d6cdbc26365b30 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Fri, 13 Dec 2024 13:47:06 +0000 Subject: [PATCH 04/68] Documentation: ioctl-number: Carve out some identifiers for use by sample drivers 32 IDs should be plenty (at yet, not too greedy) since a lot of sample drivers will use their own subsystem allocations. Sample drivers predominately reside in /samples, but there should be no issue with in-place example drivers using them. Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20241213134715.601415-2-lee@kernel.org Signed-off-by: Greg Kroah-Hartman --- Documentation/userspace-api/ioctl/ioctl-number.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/userspace-api/ioctl/ioctl-number.rst index 243f1f1b554a..dc4bc0cab69f 100644 --- a/Documentation/userspace-api/ioctl/ioctl-number.rst +++ b/Documentation/userspace-api/ioctl/ioctl-number.rst @@ -311,6 +311,7 @@ Code Seq# Include File Comments 'z' 10-4F drivers/s390/crypto/zcrypt_api.h conflict! '|' 00-7F linux/media.h +'|' 80-9F samples/ Any sample and example drivers 0x80 00-1F linux/fb.h 0x81 00-1F linux/vduse.h 0x89 00-06 arch/x86/include/asm/sockios.h From fdb1ac6c302689b286eca9bb7247111bffc7db17 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Fri, 13 Dec 2024 13:47:07 +0000 Subject: [PATCH 05/68] samples: rust: Provide example using the new Rust MiscDevice abstraction This sample driver demonstrates the following basic operations: * Register a Misc Device * Create /dev/rust-misc-device * Provide open call-back for the aforementioned character device * Operate on the character device via a simple ioctl() * Provide close call-back for the character device Signed-off-by: Lee Jones Reviewed-by: Danilo Krummrich Link: https://lore.kernel.org/r/20241213134715.601415-3-lee@kernel.org Signed-off-by: Greg Kroah-Hartman --- samples/rust/Kconfig | 10 ++++ samples/rust/Makefile | 1 + samples/rust/rust_misc_device.rs | 87 ++++++++++++++++++++++++++++++++ 3 files changed, 98 insertions(+) create mode 100644 samples/rust/rust_misc_device.rs diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig index b0f74a81c8f9..df384e679901 100644 --- a/samples/rust/Kconfig +++ b/samples/rust/Kconfig @@ -20,6 +20,16 @@ config SAMPLE_RUST_MINIMAL If unsure, say N. +config SAMPLE_RUST_MISC_DEVICE + tristate "Misc device" + help + This option builds the Rust misc device. + + To compile this as a module, choose M here: + the module will be called rust_misc_device. + + If unsure, say N. + config SAMPLE_RUST_PRINT tristate "Printing macros" help diff --git a/samples/rust/Makefile b/samples/rust/Makefile index c1a5c1655395..ad4b97a98580 100644 --- a/samples/rust/Makefile +++ b/samples/rust/Makefile @@ -2,6 +2,7 @@ ccflags-y += -I$(src) # needed for trace events obj-$(CONFIG_SAMPLE_RUST_MINIMAL) += rust_minimal.o +obj-$(CONFIG_SAMPLE_RUST_MISC_DEVICE) += rust_misc_device.o obj-$(CONFIG_SAMPLE_RUST_PRINT) += rust_print.o rust_print-y := rust_print_main.o rust_print_events.o diff --git a/samples/rust/rust_misc_device.rs b/samples/rust/rust_misc_device.rs new file mode 100644 index 000000000000..324c3696ae3f --- /dev/null +++ b/samples/rust/rust_misc_device.rs @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: GPL-2.0 + +// Copyright (C) 2024 Google LLC. + +//! Rust misc device sample. + +use kernel::{ + c_str, + device::Device, + fs::File, + ioctl::_IO, + miscdevice::{MiscDevice, MiscDeviceOptions, MiscDeviceRegistration}, + prelude::*, + types::ARef, +}; + +const RUST_MISC_DEV_HELLO: u32 = _IO('|' as u32, 0x80); + +module! { + type: RustMiscDeviceModule, + name: "rust_misc_device", + author: "Lee Jones", + description: "Rust misc device sample", + license: "GPL", +} + +#[pin_data] +struct RustMiscDeviceModule { + #[pin] + _miscdev: MiscDeviceRegistration, +} + +impl kernel::InPlaceModule for RustMiscDeviceModule { + fn init(_module: &'static ThisModule) -> impl PinInit { + pr_info!("Initialising Rust Misc Device Sample\n"); + + let options = MiscDeviceOptions { + name: c_str!("rust-misc-device"), + }; + + try_pin_init!(Self { + _miscdev <- MiscDeviceRegistration::register(options), + }) + } +} + +struct RustMiscDevice { + dev: ARef, +} + +#[vtable] +impl MiscDevice for RustMiscDevice { + type Ptr = KBox; + + fn open(_file: &File, misc: &MiscDeviceRegistration) -> Result> { + let dev = ARef::from(misc.device()); + + dev_info!(dev, "Opening Rust Misc Device Sample\n"); + + Ok(KBox::new(RustMiscDevice { dev }, GFP_KERNEL)?) + } + + fn ioctl( + me: ::Borrowed<'_>, + _file: &File, + cmd: u32, + _arg: usize, + ) -> Result { + dev_info!(me.dev, "IOCTLing Rust Misc Device Sample\n"); + + match cmd { + RUST_MISC_DEV_HELLO => dev_info!(me.dev, "Hello from the Rust Misc Device\n"), + _ => { + dev_err!(me.dev, "-> IOCTL not recognised: {}\n", cmd); + return Err(ENOTTY); + } + } + + Ok(0) + } +} + +impl Drop for RustMiscDevice { + fn drop(&mut self) { + dev_info!(self.dev, "Exiting the Rust Misc Device Sample\n"); + } +} From 42523ceba5f6735df4d02c6982d1733a1465afbd Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Fri, 13 Dec 2024 13:47:08 +0000 Subject: [PATCH 06/68] samples: rust_misc_device: Demonstrate additional get/set value functionality Expand the complexity of the sample driver by providing the ability to get and set an integer. The value is protected by a mutex. Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20241213134715.601415-4-lee@kernel.org Signed-off-by: Greg Kroah-Hartman --- samples/rust/rust_misc_device.rs | 89 +++++++++++++++++++++++++++----- 1 file changed, 75 insertions(+), 14 deletions(-) diff --git a/samples/rust/rust_misc_device.rs b/samples/rust/rust_misc_device.rs index 324c3696ae3f..ae1474a451f1 100644 --- a/samples/rust/rust_misc_device.rs +++ b/samples/rust/rust_misc_device.rs @@ -4,17 +4,24 @@ //! Rust misc device sample. +use core::pin::Pin; + use kernel::{ c_str, device::Device, fs::File, - ioctl::_IO, + ioctl::{_IO, _IOC_SIZE, _IOR, _IOW}, miscdevice::{MiscDevice, MiscDeviceOptions, MiscDeviceRegistration}, + new_mutex, prelude::*, + sync::Mutex, types::ARef, + uaccess::{UserSlice, UserSliceReader, UserSliceWriter}, }; const RUST_MISC_DEV_HELLO: u32 = _IO('|' as u32, 0x80); +const RUST_MISC_DEV_GET_VALUE: u32 = _IOR::('|' as u32, 0x81); +const RUST_MISC_DEV_SET_VALUE: u32 = _IOW::('|' as u32, 0x82); module! { type: RustMiscDeviceModule, @@ -44,44 +51,98 @@ impl kernel::InPlaceModule for RustMiscDeviceModule { } } +struct Inner { + value: i32, +} + +#[pin_data(PinnedDrop)] struct RustMiscDevice { + #[pin] + inner: Mutex, dev: ARef, } #[vtable] impl MiscDevice for RustMiscDevice { - type Ptr = KBox; + type Ptr = Pin>; - fn open(_file: &File, misc: &MiscDeviceRegistration) -> Result> { + fn open(_file: &File, misc: &MiscDeviceRegistration) -> Result>> { let dev = ARef::from(misc.device()); dev_info!(dev, "Opening Rust Misc Device Sample\n"); - Ok(KBox::new(RustMiscDevice { dev }, GFP_KERNEL)?) + KBox::try_pin_init( + try_pin_init! { + RustMiscDevice { + inner <- new_mutex!( Inner{ value: 0_i32 } ), + dev: dev, + } + }, + GFP_KERNEL, + ) } - fn ioctl( - me: ::Borrowed<'_>, - _file: &File, - cmd: u32, - _arg: usize, - ) -> Result { + fn ioctl(me: Pin<&RustMiscDevice>, _file: &File, cmd: u32, arg: usize) -> Result { dev_info!(me.dev, "IOCTLing Rust Misc Device Sample\n"); + let size = _IOC_SIZE(cmd); + match cmd { - RUST_MISC_DEV_HELLO => dev_info!(me.dev, "Hello from the Rust Misc Device\n"), + RUST_MISC_DEV_GET_VALUE => me.get_value(UserSlice::new(arg, size).writer())?, + RUST_MISC_DEV_SET_VALUE => me.set_value(UserSlice::new(arg, size).reader())?, + RUST_MISC_DEV_HELLO => me.hello()?, _ => { dev_err!(me.dev, "-> IOCTL not recognised: {}\n", cmd); return Err(ENOTTY); } - } + }; Ok(0) } } -impl Drop for RustMiscDevice { - fn drop(&mut self) { +#[pinned_drop] +impl PinnedDrop for RustMiscDevice { + fn drop(self: Pin<&mut Self>) { dev_info!(self.dev, "Exiting the Rust Misc Device Sample\n"); } } + +impl RustMiscDevice { + fn set_value(&self, mut reader: UserSliceReader) -> Result { + let new_value = reader.read::()?; + let mut guard = self.inner.lock(); + + dev_info!( + self.dev, + "-> Copying data from userspace (value: {})\n", + new_value + ); + + guard.value = new_value; + Ok(0) + } + + fn get_value(&self, mut writer: UserSliceWriter) -> Result { + let guard = self.inner.lock(); + let value = guard.value; + + // Free-up the lock and use our locally cached instance from here + drop(guard); + + dev_info!( + self.dev, + "-> Copying data to userspace (value: {})\n", + &value + ); + + writer.write::(&value)?; + Ok(0) + } + + fn hello(&self) -> Result { + dev_info!(self.dev, "-> Hello from the Rust Misc Device\n"); + + Ok(0) + } +} From ff9feb05672e165bcdc3dcca5be630071d9e25b4 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Fri, 13 Dec 2024 13:47:09 +0000 Subject: [PATCH 07/68] MAINTAINERS: Add Rust Misc Sample to MISC entry Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20241213134715.601415-5-lee@kernel.org Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index baf0eeb9a355..78fcc7e8219c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5321,6 +5321,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc.git F: drivers/char/ F: drivers/misc/ F: include/linux/miscdevice.h +F: samples/rust/rust_misc_device.rs X: drivers/char/agp/ X: drivers/char/hw_random/ X: drivers/char/ipmi/ From 8d9b095b8f898bddc6c59a3eb9f50c1aa194c57a Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Fri, 13 Dec 2024 13:47:10 +0000 Subject: [PATCH 08/68] samples: rust_misc_device: Provide an example C program to exercise functionality Here is the expected output (manually spliced together): USERSPACE: Opening /dev/rust-misc-device for reading and writing KERNEL: rust_misc_device: Opening Rust Misc Device Sample USERSPACE: Calling Hello KERNEL: rust_misc_device: IOCTLing Rust Misc Device Sample KERNEL: rust_misc_device: -> Hello from the Rust Misc Device USERSPACE: Fetching initial value KERNEL: rust_misc_device: IOCTLing Rust Misc Device Sample KERNEL: rust_misc_device: -> Copying data to userspace (value: 0) USERSPACE: Submitting new value (1) KERNEL: rust_misc_device: IOCTLing Rust Misc Device Sample KERNEL: rust_misc_device: -> Copying data from userspace (value: 1) USERSPACE: Fetching new value KERNEL: rust_misc_device: IOCTLing Rust Misc Device Sample KERNEL: rust_misc_device: -> Copying data to userspace (value: 1) USERSPACE: Attempting to call in to an non-existent IOCTL KERNEL: rust_misc_device: IOCTLing Rust Misc Device Sample KERNEL: rust_misc_device: -> IOCTL not recognised: 20992 USERSPACE: ioctl: Succeeded to fail - this was expected: Inappropriate ioctl for device USERSPACE: Closing /dev/rust-misc-device KERNEL: rust_misc_device: Exiting the Rust Misc Device Sample USERSPACE: Success Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20241213134715.601415-6-lee@kernel.org Signed-off-by: Greg Kroah-Hartman --- samples/rust/rust_misc_device.rs | 90 ++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/samples/rust/rust_misc_device.rs b/samples/rust/rust_misc_device.rs index ae1474a451f1..40ad7266c225 100644 --- a/samples/rust/rust_misc_device.rs +++ b/samples/rust/rust_misc_device.rs @@ -4,6 +4,96 @@ //! Rust misc device sample. +/// Below is an example userspace C program that exercises this sample's functionality. +/// +/// ```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 = 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 = 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 = 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 = 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 = 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 != 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 = 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; +/// } +/// ``` use core::pin::Pin; use kernel::{ From 5bcc8bfe841b29f7d62f4bb7738bb085ecc51aad Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Tue, 3 Dec 2024 12:34:38 +0000 Subject: [PATCH 09/68] rust: miscdevice: add fops->show_fdinfo() hook File descriptors should generally provide a fops->show_fdinfo() hook for debugging purposes. Thus, add such a hook to the miscdevice abstractions. Signed-off-by: Alice Ryhl Link: https://lore.kernel.org/r/20241203-miscdevice-showfdinfo-v1-1-7e990732d430@google.com Signed-off-by: Greg Kroah-Hartman --- rust/kernel/miscdevice.rs | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/rust/kernel/miscdevice.rs b/rust/kernel/miscdevice.rs index 20895e809607..ebc82e7dfc80 100644 --- a/rust/kernel/miscdevice.rs +++ b/rust/kernel/miscdevice.rs @@ -14,6 +14,7 @@ use crate::{ error::{to_result, Error, Result, VTABLE_DEFAULT_ERROR}, fs::File, prelude::*, + seq_file::SeqFile, str::CStr, types::{ForeignOwnable, Opaque}, }; @@ -152,6 +153,15 @@ pub trait MiscDevice: Sized { ) -> Result { kernel::build_error(VTABLE_DEFAULT_ERROR) } + + /// Show info for this fd. + fn show_fdinfo( + _device: ::Borrowed<'_>, + _m: &SeqFile, + _file: &File, + ) { + kernel::build_error(VTABLE_DEFAULT_ERROR) + } } const fn create_vtable() -> &'static bindings::file_operations { @@ -179,6 +189,7 @@ const fn create_vtable() -> &'static bindings::file_operations { } else { None }, + show_fdinfo: maybe_fn(T::HAS_SHOW_FDINFO, fops_show_fdinfo::), // SAFETY: All zeros is a valid value for `bindings::file_operations`. ..unsafe { MaybeUninit::zeroed().assume_init() } }; @@ -298,3 +309,26 @@ unsafe extern "C" fn fops_compat_ioctl( Err(err) => err.to_errno() as c_long, } } + +/// # Safety +/// +/// - `file` must be a valid file that is associated with a `MiscDeviceRegistration`. +/// - `seq_file` must be a valid `struct seq_file` that we can write to. +unsafe extern "C" fn fops_show_fdinfo( + seq_file: *mut bindings::seq_file, + file: *mut bindings::file, +) { + // SAFETY: The release call of a file owns the private data. + let private = unsafe { (*file).private_data }; + // SAFETY: Ioctl calls can borrow the private data of the file. + let device = unsafe { ::borrow(private) }; + // SAFETY: + // * The file is valid for the duration of this call. + // * There is no active fdget_pos region on the file on this thread. + let file = unsafe { File::from_raw_file(file) }; + // SAFETY: The caller ensures that the pointer is valid and exclusive for the duration in which + // this method is called. + let m = unsafe { SeqFile::from_raw(seq_file) }; + + T::show_fdinfo(device, m, file); +} From a790265c7f663c382ac25ecb841e241a023f0590 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Thu, 19 Dec 2024 18:04:03 +0100 Subject: [PATCH 10/68] rust: module: add trait `ModuleMetadata` In order to access static metadata of a Rust kernel module, add the `ModuleMetadata` trait. In particular, this trait provides the name of a Rust kernel module as specified by the `module!` macro. Signed-off-by: Danilo Krummrich Tested-by: Dirk Behme Tested-by: Fabien Parent Link: https://lore.kernel.org/r/20241219170425.12036-2-dakr@kernel.org Signed-off-by: Greg Kroah-Hartman --- rust/kernel/lib.rs | 6 ++++++ rust/macros/module.rs | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index e1065a7551a3..61b82b78b915 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -116,6 +116,12 @@ impl InPlaceModule for T { } } +/// Metadata attached to a [`Module`] or [`InPlaceModule`]. +pub trait ModuleMetadata { + /// The name of the module as specified in the `module!` macro. + const NAME: &'static crate::str::CStr; +} + /// Equivalent to `THIS_MODULE` in the C API. /// /// C header: [`include/linux/init.h`](srctree/include/linux/init.h) diff --git a/rust/macros/module.rs b/rust/macros/module.rs index 2587f41b0d39..cdf94f4982df 100644 --- a/rust/macros/module.rs +++ b/rust/macros/module.rs @@ -228,6 +228,10 @@ pub(crate) fn module(ts: TokenStream) -> TokenStream { kernel::ThisModule::from_ptr(core::ptr::null_mut()) }}; + impl kernel::ModuleMetadata for {type_} {{ + const NAME: &'static kernel::str::CStr = kernel::c_str!(\"{name}\"); + }} + // Double nested modules, since then nobody can access the public items inside. mod __module_init {{ mod __module_init {{ From ea7e18289f44b0aa597026f16e7f4f6daa0f13ee Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Thu, 19 Dec 2024 18:04:04 +0100 Subject: [PATCH 11/68] rust: implement generic driver registration Implement the generic `Registration` type and the `RegistrationOps` trait. The `Registration` structure is the common type that represents a driver registration and is typically bound to the lifetime of a module. However, it doesn't implement actual calls to the kernel's driver core to register drivers itself. Instead the `RegistrationOps` trait is provided to subsystems, which have to implement `RegistrationOps::register` and `RegistrationOps::unregister`. Subsystems have to provide an implementation for both of those methods where the subsystem specific variants to register / unregister a driver have to implemented. For instance, the PCI subsystem would call __pci_register_driver() from `RegistrationOps::register` and pci_unregister_driver() from `DrvierOps::unregister`. Co-developed-by: Wedson Almeida Filho Signed-off-by: Wedson Almeida Filho Signed-off-by: Danilo Krummrich Tested-by: Dirk Behme Tested-by: Fabien Parent Link: https://lore.kernel.org/r/20241219170425.12036-3-dakr@kernel.org Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 1 + rust/kernel/driver.rs | 117 ++++++++++++++++++++++++++++++++++++++++++ rust/kernel/lib.rs | 1 + 3 files changed, 119 insertions(+) create mode 100644 rust/kernel/driver.rs diff --git a/MAINTAINERS b/MAINTAINERS index 78fcc7e8219c..7adf84aacb3f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7034,6 +7034,7 @@ F: include/linux/kobj* F: include/linux/property.h F: lib/kobj* F: rust/kernel/device.rs +F: rust/kernel/driver.rs DRIVERS FOR OMAP ADAPTIVE VOLTAGE SCALING (AVS) M: Nishanth Menon diff --git a/rust/kernel/driver.rs b/rust/kernel/driver.rs new file mode 100644 index 000000000000..c1957ee7bb7e --- /dev/null +++ b/rust/kernel/driver.rs @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Generic support for drivers of different buses (e.g., PCI, Platform, Amba, etc.). +//! +//! Each bus / subsystem is expected to implement [`RegistrationOps`], which allows drivers to +//! register using the [`Registration`] class. + +use crate::error::{Error, Result}; +use crate::{init::PinInit, str::CStr, try_pin_init, types::Opaque, ThisModule}; +use core::pin::Pin; +use macros::{pin_data, pinned_drop}; + +/// The [`RegistrationOps`] trait serves as generic interface for subsystems (e.g., PCI, Platform, +/// Amba, etc.) to provide the corresponding subsystem specific implementation to register / +/// unregister a driver of the particular type (`RegType`). +/// +/// For instance, the PCI subsystem would set `RegType` to `bindings::pci_driver` and call +/// `bindings::__pci_register_driver` from `RegistrationOps::register` and +/// `bindings::pci_unregister_driver` from `RegistrationOps::unregister`. +pub trait RegistrationOps { + /// The type that holds information about the registration. This is typically a struct defined + /// by the C portion of the kernel. + type RegType: Default; + + /// Registers a driver. + /// + /// On success, `reg` must remain pinned and valid until the matching call to + /// [`RegistrationOps::unregister`]. + fn register( + reg: &Opaque, + name: &'static CStr, + module: &'static ThisModule, + ) -> Result; + + /// Unregisters a driver previously registered with [`RegistrationOps::register`]. + fn unregister(reg: &Opaque); +} + +/// A [`Registration`] is a generic type that represents the registration of some driver type (e.g. +/// `bindings::pci_driver`). Therefore a [`Registration`] must be initialized with a type that +/// implements the [`RegistrationOps`] trait, such that the generic `T::register` and +/// `T::unregister` calls result in the subsystem specific registration calls. +/// +///Once the `Registration` structure is dropped, the driver is unregistered. +#[pin_data(PinnedDrop)] +pub struct Registration { + #[pin] + reg: Opaque, +} + +// SAFETY: `Registration` has no fields or methods accessible via `&Registration`, so it is safe to +// share references to it with multiple threads as nothing can be done. +unsafe impl Sync for Registration {} + +// SAFETY: Both registration and unregistration are implemented in C and safe to be performed from +// any thread, so `Registration` is `Send`. +unsafe impl Send for Registration {} + +impl Registration { + /// Creates a new instance of the registration object. + pub fn new(name: &'static CStr, module: &'static ThisModule) -> impl PinInit { + try_pin_init!(Self { + reg <- Opaque::try_ffi_init(|ptr: *mut T::RegType| { + // SAFETY: `try_ffi_init` guarantees that `ptr` is valid for write. + unsafe { ptr.write(T::RegType::default()) }; + + // SAFETY: `try_ffi_init` guarantees that `ptr` is valid for write, and it has + // just been initialised above, so it's also valid for read. + let drv = unsafe { &*(ptr as *const Opaque) }; + + T::register(drv, name, module) + }), + }) + } +} + +#[pinned_drop] +impl PinnedDrop for Registration { + fn drop(self: Pin<&mut Self>) { + T::unregister(&self.reg); + } +} + +/// Declares a kernel module that exposes a single driver. +/// +/// It is meant to be used as a helper by other subsystems so they can more easily expose their own +/// macros. +#[macro_export] +macro_rules! module_driver { + (<$gen_type:ident>, $driver_ops:ty, { type: $type:ty, $($f:tt)* }) => { + type Ops<$gen_type> = $driver_ops; + + #[$crate::prelude::pin_data] + struct DriverModule { + #[pin] + _driver: $crate::driver::Registration>, + } + + impl $crate::InPlaceModule for DriverModule { + fn init( + module: &'static $crate::ThisModule + ) -> impl $crate::init::PinInit { + $crate::try_pin_init!(Self { + _driver <- $crate::driver::Registration::new( + ::NAME, + module, + ), + }) + } + } + + $crate::prelude::module! { + type: DriverModule, + $($f)* + } + } +} diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 61b82b78b915..7818407f9aac 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -35,6 +35,7 @@ pub mod block; mod build_assert; pub mod cred; pub mod device; +pub mod driver; pub mod error; #[cfg(CONFIG_RUST_FW_LOADER_ABSTRACTIONS)] pub mod firmware; From 9b90864bb42befdc10fa5c60dd1d8033c8535726 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Thu, 19 Dec 2024 18:04:05 +0100 Subject: [PATCH 12/68] rust: implement `IdArray`, `IdTable` and `RawDeviceId` Most subsystems use some kind of ID to match devices and drivers. Hence, we have to provide Rust drivers an abstraction to register an ID table for the driver to match. Generally, those IDs are subsystem specific and hence need to be implemented by the corresponding subsystem. However, the `IdArray`, `IdTable` and `RawDeviceId` types provide a generalized implementation that makes the life of subsystems easier to do so. Co-developed-by: Wedson Almeida Filho Signed-off-by: Wedson Almeida Filho Co-developed-by: Gary Guo Signed-off-by: Gary Guo Co-developed-by: Fabien Parent Signed-off-by: Fabien Parent Signed-off-by: Danilo Krummrich Tested-by: Dirk Behme Tested-by: Fabien Parent Link: https://lore.kernel.org/r/20241219170425.12036-4-dakr@kernel.org Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 1 + rust/kernel/device_id.rs | 165 +++++++++++++++++++++++++++++++++++++++ rust/kernel/lib.rs | 6 ++ 3 files changed, 172 insertions(+) create mode 100644 rust/kernel/device_id.rs diff --git a/MAINTAINERS b/MAINTAINERS index 7adf84aacb3f..f330a08cd293 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7034,6 +7034,7 @@ F: include/linux/kobj* F: include/linux/property.h F: lib/kobj* F: rust/kernel/device.rs +F: rust/kernel/device_id.rs F: rust/kernel/driver.rs DRIVERS FOR OMAP ADAPTIVE VOLTAGE SCALING (AVS) diff --git a/rust/kernel/device_id.rs b/rust/kernel/device_id.rs new file mode 100644 index 000000000000..e5859217a579 --- /dev/null +++ b/rust/kernel/device_id.rs @@ -0,0 +1,165 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Generic implementation of device IDs. +//! +//! Each bus / subsystem that matches device and driver through a bus / subsystem specific ID is +//! expected to implement [`RawDeviceId`]. + +use core::mem::MaybeUninit; + +/// Marker trait to indicate a Rust device ID type represents a corresponding C device ID type. +/// +/// This is meant to be implemented by buses/subsystems so that they can use [`IdTable`] to +/// guarantee (at compile-time) zero-termination of device id tables provided by drivers. +/// +/// # Safety +/// +/// Implementers must ensure that: +/// - `Self` is layout-compatible with [`RawDeviceId::RawType`]; i.e. it's safe to transmute to +/// `RawDeviceId`. +/// +/// This requirement is needed so `IdArray::new` can convert `Self` to `RawType` when building +/// the ID table. +/// +/// Ideally, this should be achieved using a const function that does conversion instead of +/// transmute; however, const trait functions relies on `const_trait_impl` unstable feature, +/// which is broken/gone in Rust 1.73. +/// +/// - `DRIVER_DATA_OFFSET` is the offset of context/data field of the device ID (usually named +/// `driver_data`) of the device ID, the field is suitable sized to write a `usize` value. +/// +/// Similar to the previous requirement, the data should ideally be added during `Self` to +/// `RawType` conversion, but there's currently no way to do it when using traits in const. +pub unsafe trait RawDeviceId { + /// The raw type that holds the device id. + /// + /// Id tables created from [`Self`] are going to hold this type in its zero-terminated array. + type RawType: Copy; + + /// The offset to the context/data field. + const DRIVER_DATA_OFFSET: usize; + + /// The index stored at `DRIVER_DATA_OFFSET` of the implementor of the [`RawDeviceId`] trait. + fn index(&self) -> usize; +} + +/// A zero-terminated device id array. +#[repr(C)] +pub struct RawIdArray { + ids: [T::RawType; N], + sentinel: MaybeUninit, +} + +impl RawIdArray { + #[doc(hidden)] + pub const fn size(&self) -> usize { + core::mem::size_of::() + } +} + +/// A zero-terminated device id array, followed by context data. +#[repr(C)] +pub struct IdArray { + raw_ids: RawIdArray, + id_infos: [U; N], +} + +impl IdArray { + /// Creates a new instance of the array. + /// + /// The contents are derived from the given identifiers and context information. + pub const fn new(ids: [(T, U); N]) -> Self { + let mut raw_ids = [const { MaybeUninit::::uninit() }; N]; + let mut infos = [const { MaybeUninit::uninit() }; N]; + + let mut i = 0usize; + while i < N { + // SAFETY: by the safety requirement of `RawDeviceId`, we're guaranteed that `T` is + // layout-wise compatible with `RawType`. + raw_ids[i] = unsafe { core::mem::transmute_copy(&ids[i].0) }; + // SAFETY: by the safety requirement of `RawDeviceId`, this would be effectively + // `raw_ids[i].driver_data = i;`. + unsafe { + raw_ids[i] + .as_mut_ptr() + .byte_offset(T::DRIVER_DATA_OFFSET as _) + .cast::() + .write(i); + } + + // SAFETY: this is effectively a move: `infos[i] = ids[i].1`. We make a copy here but + // later forget `ids`. + infos[i] = MaybeUninit::new(unsafe { core::ptr::read(&ids[i].1) }); + i += 1; + } + + core::mem::forget(ids); + + Self { + raw_ids: RawIdArray { + // SAFETY: this is effectively `array_assume_init`, which is unstable, so we use + // `transmute_copy` instead. We have initialized all elements of `raw_ids` so this + // `array_assume_init` is safe. + ids: unsafe { core::mem::transmute_copy(&raw_ids) }, + sentinel: MaybeUninit::zeroed(), + }, + // SAFETY: We have initialized all elements of `infos` so this `array_assume_init` is + // safe. + id_infos: unsafe { core::mem::transmute_copy(&infos) }, + } + } + + /// Reference to the contained [`RawIdArray`]. + pub const fn raw_ids(&self) -> &RawIdArray { + &self.raw_ids + } +} + +/// A device id table. +/// +/// This trait is only implemented by `IdArray`. +/// +/// The purpose of this trait is to allow `&'static dyn IdArray` to be in context when `N` in +/// `IdArray` doesn't matter. +pub trait IdTable { + /// Obtain the pointer to the ID table. + fn as_ptr(&self) -> *const T::RawType; + + /// Obtain the pointer to the bus specific device ID from an index. + fn id(&self, index: usize) -> &T::RawType; + + /// Obtain the pointer to the driver-specific information from an index. + fn info(&self, index: usize) -> &U; +} + +impl IdTable for IdArray { + fn as_ptr(&self) -> *const T::RawType { + // This cannot be `self.ids.as_ptr()`, as the return pointer must have correct provenance + // to access the sentinel. + (self as *const Self).cast() + } + + fn id(&self, index: usize) -> &T::RawType { + &self.raw_ids.ids[index] + } + + fn info(&self, index: usize) -> &U { + &self.id_infos[index] + } +} + +/// Create device table alias for modpost. +#[macro_export] +macro_rules! module_device_table { + ($table_type: literal, $module_table_name:ident, $table_name:ident) => { + #[rustfmt::skip] + #[export_name = + concat!("__mod_device_table__", $table_type, + "__", module_path!(), + "_", line!(), + "_", stringify!($table_name)) + ] + static $module_table_name: [core::mem::MaybeUninit; $table_name.raw_ids().size()] = + unsafe { core::mem::transmute_copy($table_name.raw_ids()) }; + }; +} diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 7818407f9aac..66149ac5c0c9 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -18,6 +18,11 @@ #![feature(inline_const)] #![feature(lint_reasons)] #![feature(unsize)] +// Stable in Rust 1.83 +#![feature(const_maybe_uninit_as_mut_ptr)] +#![feature(const_mut_refs)] +#![feature(const_ptr_write)] +#![feature(const_refs_to_cell)] // Ensure conditional compilation based on the kernel configuration works; // otherwise we may silently break things like initcall handling. @@ -35,6 +40,7 @@ pub mod block; mod build_assert; pub mod cred; pub mod device; +pub mod device_id; pub mod driver; pub mod error; #[cfg(CONFIG_RUST_FW_LOADER_ABSTRACTIONS)] From 51158207294108898e5b72bb78fa51a7e848844f Mon Sep 17 00:00:00 2001 From: Wedson Almeida Filho Date: Thu, 19 Dec 2024 18:04:06 +0100 Subject: [PATCH 13/68] rust: add rcu abstraction Add a simple abstraction to guard critical code sections with an rcu read lock. Reviewed-by: Boqun Feng Signed-off-by: Wedson Almeida Filho Co-developed-by: Danilo Krummrich Signed-off-by: Danilo Krummrich Tested-by: Dirk Behme Tested-by: Fabien Parent Link: https://lore.kernel.org/r/20241219170425.12036-5-dakr@kernel.org Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 1 + rust/helpers/helpers.c | 1 + rust/helpers/rcu.c | 13 ++++++++++++ rust/kernel/sync.rs | 1 + rust/kernel/sync/rcu.rs | 47 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 63 insertions(+) create mode 100644 rust/helpers/rcu.c create mode 100644 rust/kernel/sync/rcu.rs diff --git a/MAINTAINERS b/MAINTAINERS index f330a08cd293..8e02ab45184a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -19691,6 +19691,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/paulmck/linux-rcu.git dev F: Documentation/RCU/ F: include/linux/rcu* F: kernel/rcu/ +F: rust/kernel/sync/rcu.rs X: Documentation/RCU/torture.rst X: include/linux/srcu*.h X: kernel/rcu/srcu*.c diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c index dcf827a61b52..060750af6524 100644 --- a/rust/helpers/helpers.c +++ b/rust/helpers/helpers.c @@ -20,6 +20,7 @@ #include "page.c" #include "pid_namespace.c" #include "rbtree.c" +#include "rcu.c" #include "refcount.c" #include "security.c" #include "signal.c" diff --git a/rust/helpers/rcu.c b/rust/helpers/rcu.c new file mode 100644 index 000000000000..f1cec6583513 --- /dev/null +++ b/rust/helpers/rcu.c @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include + +void rust_helper_rcu_read_lock(void) +{ + rcu_read_lock(); +} + +void rust_helper_rcu_read_unlock(void) +{ + rcu_read_unlock(); +} diff --git a/rust/kernel/sync.rs b/rust/kernel/sync.rs index 1eab7ebf25fd..0654008198b2 100644 --- a/rust/kernel/sync.rs +++ b/rust/kernel/sync.rs @@ -12,6 +12,7 @@ mod condvar; pub mod lock; mod locked_by; pub mod poll; +pub mod rcu; pub use arc::{Arc, ArcBorrow, UniqueArc}; pub use condvar::{new_condvar, CondVar, CondVarTimeoutResult}; diff --git a/rust/kernel/sync/rcu.rs b/rust/kernel/sync/rcu.rs new file mode 100644 index 000000000000..b51d9150ffe2 --- /dev/null +++ b/rust/kernel/sync/rcu.rs @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! RCU support. +//! +//! C header: [`include/linux/rcupdate.h`](srctree/include/linux/rcupdate.h) + +use crate::{bindings, types::NotThreadSafe}; + +/// Evidence that the RCU read side lock is held on the current thread/CPU. +/// +/// The type is explicitly not `Send` because this property is per-thread/CPU. +/// +/// # Invariants +/// +/// The RCU read side lock is actually held while instances of this guard exist. +pub struct Guard(NotThreadSafe); + +impl Guard { + /// Acquires the RCU read side lock and returns a guard. + pub fn new() -> Self { + // SAFETY: An FFI call with no additional requirements. + unsafe { bindings::rcu_read_lock() }; + // INVARIANT: The RCU read side lock was just acquired above. + Self(NotThreadSafe) + } + + /// Explicitly releases the RCU read side lock. + pub fn unlock(self) {} +} + +impl Default for Guard { + fn default() -> Self { + Self::new() + } +} + +impl Drop for Guard { + fn drop(&mut self) { + // SAFETY: By the type invariants, the RCU read side is locked, so it is ok to unlock it. + unsafe { bindings::rcu_read_unlock() }; + } +} + +/// Acquires the RCU read side lock. +pub fn read_lock() -> Guard { + Guard::new() +} From 2d3bf6ffe26439444b55dd5af7b06d1aca3a042d Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Thu, 19 Dec 2024 18:04:07 +0100 Subject: [PATCH 14/68] rust: types: add `Opaque::pin_init` Analogous to `Opaque::new` add `Opaque::pin_init`, which instead of a value `T` takes a `PinInit` and returns a `PinInit>`. Reviewed-by: Alice Ryhl Suggested-by: Alice Ryhl Signed-off-by: Danilo Krummrich Tested-by: Dirk Behme Tested-by: Fabien Parent Link: https://lore.kernel.org/r/20241219170425.12036-6-dakr@kernel.org Signed-off-by: Greg Kroah-Hartman --- rust/kernel/types.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/rust/kernel/types.rs b/rust/kernel/types.rs index ec6457bb3084..3aea6af9a0bc 100644 --- a/rust/kernel/types.rs +++ b/rust/kernel/types.rs @@ -281,6 +281,17 @@ impl Opaque { } } + /// Create an opaque pin-initializer from the given pin-initializer. + pub fn pin_init(slot: impl PinInit) -> impl PinInit { + Self::ffi_init(|ptr: *mut T| { + // SAFETY: + // - `ptr` is a valid pointer to uninitialized memory, + // - `slot` is not accessed on error; the call is infallible, + // - `slot` is pinned in memory. + let _ = unsafe { init::PinInit::::__pinned_init(slot, ptr) }; + }) + } + /// Creates a pin-initializer from the given initializer closure. /// /// The returned initializer calls the given closure with the pointer to the inner `T` of this From 0494d9c82b0c722d8ce2af7dc5f92be6aef4625b Mon Sep 17 00:00:00 2001 From: Wedson Almeida Filho Date: Thu, 19 Dec 2024 18:04:08 +0100 Subject: [PATCH 15/68] rust: add `Revocable` type Revocable allows access to objects to be safely revoked at run time. This is useful, for example, for resources allocated during device probe; when the device is removed, the driver should stop accessing the device resources even if another state is kept in memory due to existing references (i.e., device context data is ref-counted and has a non-zero refcount after removal of the device). Signed-off-by: Wedson Almeida Filho Co-developed-by: Danilo Krummrich Signed-off-by: Danilo Krummrich Tested-by: Dirk Behme Link: https://lore.kernel.org/r/20241219170425.12036-7-dakr@kernel.org Signed-off-by: Greg Kroah-Hartman --- rust/kernel/lib.rs | 1 + rust/kernel/revocable.rs | 219 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 220 insertions(+) create mode 100644 rust/kernel/revocable.rs diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 66149ac5c0c9..5702ce32ec8e 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -60,6 +60,7 @@ pub mod pid_namespace; pub mod prelude; pub mod print; pub mod rbtree; +pub mod revocable; pub mod security; pub mod seq_file; pub mod sizes; diff --git a/rust/kernel/revocable.rs b/rust/kernel/revocable.rs new file mode 100644 index 000000000000..1e5a9d25c21b --- /dev/null +++ b/rust/kernel/revocable.rs @@ -0,0 +1,219 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Revocable objects. +//! +//! The [`Revocable`] type wraps other types and allows access to them to be revoked. The existence +//! of a [`RevocableGuard`] ensures that objects remain valid. + +use crate::{bindings, prelude::*, sync::rcu, types::Opaque}; +use core::{ + marker::PhantomData, + ops::Deref, + ptr::drop_in_place, + sync::atomic::{AtomicBool, Ordering}, +}; + +/// An object that can become inaccessible at runtime. +/// +/// Once access is revoked and all concurrent users complete (i.e., all existing instances of +/// [`RevocableGuard`] are dropped), the wrapped object is also dropped. +/// +/// # Examples +/// +/// ``` +/// # use kernel::revocable::Revocable; +/// +/// struct Example { +/// a: u32, +/// b: u32, +/// } +/// +/// fn add_two(v: &Revocable) -> Option { +/// let guard = v.try_access()?; +/// Some(guard.a + guard.b) +/// } +/// +/// let v = KBox::pin_init(Revocable::new(Example { a: 10, b: 20 }), GFP_KERNEL).unwrap(); +/// assert_eq!(add_two(&v), Some(30)); +/// v.revoke(); +/// assert_eq!(add_two(&v), None); +/// ``` +/// +/// Sample example as above, but explicitly using the rcu read side lock. +/// +/// ``` +/// # use kernel::revocable::Revocable; +/// use kernel::sync::rcu; +/// +/// struct Example { +/// a: u32, +/// b: u32, +/// } +/// +/// fn add_two(v: &Revocable) -> Option { +/// let guard = rcu::read_lock(); +/// let e = v.try_access_with_guard(&guard)?; +/// Some(e.a + e.b) +/// } +/// +/// let v = KBox::pin_init(Revocable::new(Example { a: 10, b: 20 }), GFP_KERNEL).unwrap(); +/// assert_eq!(add_two(&v), Some(30)); +/// v.revoke(); +/// assert_eq!(add_two(&v), None); +/// ``` +#[pin_data(PinnedDrop)] +pub struct Revocable { + is_available: AtomicBool, + #[pin] + data: Opaque, +} + +// SAFETY: `Revocable` is `Send` if the wrapped object is also `Send`. This is because while the +// functionality exposed by `Revocable` can be accessed from any thread/CPU, it is possible that +// this isn't supported by the wrapped object. +unsafe impl Send for Revocable {} + +// SAFETY: `Revocable` is `Sync` if the wrapped object is both `Send` and `Sync`. We require `Send` +// from the wrapped object as well because of `Revocable::revoke`, which can trigger the `Drop` +// implementation of the wrapped object from an arbitrary thread. +unsafe impl Sync for Revocable {} + +impl Revocable { + /// Creates a new revocable instance of the given data. + pub fn new(data: impl PinInit) -> impl PinInit { + pin_init!(Self { + is_available: AtomicBool::new(true), + data <- Opaque::pin_init(data), + }) + } + + /// Tries to access the revocable wrapped object. + /// + /// Returns `None` if the object has been revoked and is therefore no longer accessible. + /// + /// Returns a guard that gives access to the object otherwise; the object is guaranteed to + /// remain accessible while the guard is alive. In such cases, callers are not allowed to sleep + /// because another CPU may be waiting to complete the revocation of this object. + pub fn try_access(&self) -> Option> { + let guard = rcu::read_lock(); + if self.is_available.load(Ordering::Relaxed) { + // Since `self.is_available` is true, data is initialised and has to remain valid + // because the RCU read side lock prevents it from being dropped. + Some(RevocableGuard::new(self.data.get(), guard)) + } else { + None + } + } + + /// Tries to access the revocable wrapped object. + /// + /// Returns `None` if the object has been revoked and is therefore no longer accessible. + /// + /// Returns a shared reference to the object otherwise; the object is guaranteed to + /// remain accessible while the rcu read side guard is alive. In such cases, callers are not + /// allowed to sleep because another CPU may be waiting to complete the revocation of this + /// object. + pub fn try_access_with_guard<'a>(&'a self, _guard: &'a rcu::Guard) -> Option<&'a T> { + if self.is_available.load(Ordering::Relaxed) { + // SAFETY: Since `self.is_available` is true, data is initialised and has to remain + // valid because the RCU read side lock prevents it from being dropped. + Some(unsafe { &*self.data.get() }) + } else { + None + } + } + + /// # Safety + /// + /// Callers must ensure that there are no more concurrent users of the revocable object. + unsafe fn revoke_internal(&self) { + if self.is_available.swap(false, Ordering::Relaxed) { + if SYNC { + // SAFETY: Just an FFI call, there are no further requirements. + unsafe { bindings::synchronize_rcu() }; + } + + // SAFETY: We know `self.data` is valid because only one CPU can succeed the + // `compare_exchange` above that takes `is_available` from `true` to `false`. + unsafe { drop_in_place(self.data.get()) }; + } + } + + /// Revokes access to and drops the wrapped object. + /// + /// Access to the object is revoked immediately to new callers of [`Revocable::try_access`], + /// expecting that there are no concurrent users of the object. + /// + /// # Safety + /// + /// Callers must ensure that there are no more concurrent users of the revocable object. + pub unsafe fn revoke_nosync(&self) { + // SAFETY: By the safety requirement of this function, the caller ensures that nobody is + // accessing the data anymore and hence we don't have to wait for the grace period to + // finish. + unsafe { self.revoke_internal::() } + } + + /// Revokes access to and drops the wrapped object. + /// + /// Access to the object is revoked immediately to new callers of [`Revocable::try_access`]. + /// + /// If there are concurrent users of the object (i.e., ones that called + /// [`Revocable::try_access`] beforehand and still haven't dropped the returned guard), this + /// function waits for the concurrent access to complete before dropping the wrapped object. + pub fn revoke(&self) { + // SAFETY: By passing `true` we ask `revoke_internal` to wait for the grace period to + // finish. + unsafe { self.revoke_internal::() } + } +} + +#[pinned_drop] +impl PinnedDrop for Revocable { + fn drop(self: Pin<&mut Self>) { + // Drop only if the data hasn't been revoked yet (in which case it has already been + // dropped). + // SAFETY: We are not moving out of `p`, only dropping in place + let p = unsafe { self.get_unchecked_mut() }; + if *p.is_available.get_mut() { + // SAFETY: We know `self.data` is valid because no other CPU has changed + // `is_available` to `false` yet, and no other CPU can do it anymore because this CPU + // holds the only reference (mutable) to `self` now. + unsafe { drop_in_place(p.data.get()) }; + } + } +} + +/// A guard that allows access to a revocable object and keeps it alive. +/// +/// CPUs may not sleep while holding on to [`RevocableGuard`] because it's in atomic context +/// holding the RCU read-side lock. +/// +/// # Invariants +/// +/// The RCU read-side lock is held while the guard is alive. +pub struct RevocableGuard<'a, T> { + data_ref: *const T, + _rcu_guard: rcu::Guard, + _p: PhantomData<&'a ()>, +} + +impl RevocableGuard<'_, T> { + fn new(data_ref: *const T, rcu_guard: rcu::Guard) -> Self { + Self { + data_ref, + _rcu_guard: rcu_guard, + _p: PhantomData, + } + } +} + +impl Deref for RevocableGuard<'_, T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + // SAFETY: By the type invariants, we hold the rcu read-side lock, so the object is + // guaranteed to remain valid. + unsafe { &*self.data_ref } + } +} From ce30d94e6855a4f6dc687f658e63c225fcc1d690 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Thu, 19 Dec 2024 18:04:09 +0100 Subject: [PATCH 16/68] rust: add `io::{Io, IoRaw}` base types I/O memory is typically either mapped through direct calls to ioremap() or subsystem / bus specific ones such as pci_iomap(). Even though subsystem / bus specific functions to map I/O memory are based on ioremap() / iounmap() it is not desirable to re-implement them in Rust. Instead, implement a base type for I/O mapped memory, which generically provides the corresponding accessors, such as `Io::readb` or `Io:try_readb`. `Io` supports an optional const generic, such that a driver can indicate the minimal expected and required size of the mapping at compile time. Correspondingly, calls to the 'non-try' accessors, support compile time checks of the I/O memory offset to read / write, while the 'try' accessors, provide boundary checks on runtime. `IoRaw` is meant to be embedded into a structure (e.g. pci::Bar or io::IoMem) which creates the actual I/O memory mapping and initializes `IoRaw` accordingly. To ensure that I/O mapped memory can't out-live the device it may be bound to, subsystems must embed the corresponding I/O memory type (e.g. pci::Bar) into a `Devres` container, such that it gets revoked once the device is unbound. Reviewed-by: Alice Ryhl Tested-by: Daniel Almeida Reviewed-by: Daniel Almeida Signed-off-by: Danilo Krummrich Tested-by: Dirk Behme Link: https://lore.kernel.org/r/20241219170425.12036-8-dakr@kernel.org Signed-off-by: Greg Kroah-Hartman --- rust/helpers/helpers.c | 1 + rust/helpers/io.c | 101 ++++++++++++++++ rust/kernel/io.rs | 260 +++++++++++++++++++++++++++++++++++++++++ rust/kernel/lib.rs | 1 + 4 files changed, 363 insertions(+) create mode 100644 rust/helpers/io.c create mode 100644 rust/kernel/io.rs diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c index 060750af6524..63f9b1da179f 100644 --- a/rust/helpers/helpers.c +++ b/rust/helpers/helpers.c @@ -14,6 +14,7 @@ #include "cred.c" #include "err.c" #include "fs.c" +#include "io.c" #include "jump_label.c" #include "kunit.c" #include "mutex.c" diff --git a/rust/helpers/io.c b/rust/helpers/io.c new file mode 100644 index 000000000000..4c2401ccd720 --- /dev/null +++ b/rust/helpers/io.c @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include + +void __iomem *rust_helper_ioremap(phys_addr_t offset, size_t size) +{ + return ioremap(offset, size); +} + +void rust_helper_iounmap(volatile void __iomem *addr) +{ + iounmap(addr); +} + +u8 rust_helper_readb(const volatile void __iomem *addr) +{ + return readb(addr); +} + +u16 rust_helper_readw(const volatile void __iomem *addr) +{ + return readw(addr); +} + +u32 rust_helper_readl(const volatile void __iomem *addr) +{ + return readl(addr); +} + +#ifdef CONFIG_64BIT +u64 rust_helper_readq(const volatile void __iomem *addr) +{ + return readq(addr); +} +#endif + +void rust_helper_writeb(u8 value, volatile void __iomem *addr) +{ + writeb(value, addr); +} + +void rust_helper_writew(u16 value, volatile void __iomem *addr) +{ + writew(value, addr); +} + +void rust_helper_writel(u32 value, volatile void __iomem *addr) +{ + writel(value, addr); +} + +#ifdef CONFIG_64BIT +void rust_helper_writeq(u64 value, volatile void __iomem *addr) +{ + writeq(value, addr); +} +#endif + +u8 rust_helper_readb_relaxed(const volatile void __iomem *addr) +{ + return readb_relaxed(addr); +} + +u16 rust_helper_readw_relaxed(const volatile void __iomem *addr) +{ + return readw_relaxed(addr); +} + +u32 rust_helper_readl_relaxed(const volatile void __iomem *addr) +{ + return readl_relaxed(addr); +} + +#ifdef CONFIG_64BIT +u64 rust_helper_readq_relaxed(const volatile void __iomem *addr) +{ + return readq_relaxed(addr); +} +#endif + +void rust_helper_writeb_relaxed(u8 value, volatile void __iomem *addr) +{ + writeb_relaxed(value, addr); +} + +void rust_helper_writew_relaxed(u16 value, volatile void __iomem *addr) +{ + writew_relaxed(value, addr); +} + +void rust_helper_writel_relaxed(u32 value, volatile void __iomem *addr) +{ + writel_relaxed(value, addr); +} + +#ifdef CONFIG_64BIT +void rust_helper_writeq_relaxed(u64 value, volatile void __iomem *addr) +{ + writeq_relaxed(value, addr); +} +#endif diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs new file mode 100644 index 000000000000..d4a73e52e3ee --- /dev/null +++ b/rust/kernel/io.rs @@ -0,0 +1,260 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Memory-mapped IO. +//! +//! C header: [`include/asm-generic/io.h`](srctree/include/asm-generic/io.h) + +use crate::error::{code::EINVAL, Result}; +use crate::{bindings, build_assert}; + +/// Raw representation of an MMIO region. +/// +/// By itself, the existence of an instance of this structure does not provide any guarantees that +/// the represented MMIO region does exist or is properly mapped. +/// +/// Instead, the bus specific MMIO implementation must convert this raw representation into an `Io` +/// instance providing the actual memory accessors. Only by the conversion into an `Io` structure +/// any guarantees are given. +pub struct IoRaw { + addr: usize, + maxsize: usize, +} + +impl IoRaw { + /// Returns a new `IoRaw` instance on success, an error otherwise. + pub fn new(addr: usize, maxsize: usize) -> Result { + if maxsize < SIZE { + return Err(EINVAL); + } + + Ok(Self { addr, maxsize }) + } + + /// Returns the base address of the MMIO region. + #[inline] + pub fn addr(&self) -> usize { + self.addr + } + + /// Returns the maximum size of the MMIO region. + #[inline] + pub fn maxsize(&self) -> usize { + self.maxsize + } +} + +/// IO-mapped memory, starting at the base address @addr and spanning @maxlen bytes. +/// +/// The creator (usually a subsystem / bus such as PCI) is responsible for creating the +/// mapping, performing an additional region request etc. +/// +/// # Invariant +/// +/// `addr` is the start and `maxsize` the length of valid I/O mapped memory region of size +/// `maxsize`. +/// +/// # Examples +/// +/// ```no_run +/// # use kernel::{bindings, io::{Io, IoRaw}}; +/// # use core::ops::Deref; +/// +/// // See also [`pci::Bar`] for a real example. +/// struct IoMem(IoRaw); +/// +/// impl IoMem { +/// /// # Safety +/// /// +/// /// [`paddr`, `paddr` + `SIZE`) must be a valid MMIO region that is mappable into the CPUs +/// /// virtual address space. +/// unsafe fn new(paddr: usize) -> Result{ +/// // SAFETY: By the safety requirements of this function [`paddr`, `paddr` + `SIZE`) is +/// // valid for `ioremap`. +/// let addr = unsafe { bindings::ioremap(paddr as _, SIZE as _) }; +/// if addr.is_null() { +/// return Err(ENOMEM); +/// } +/// +/// Ok(IoMem(IoRaw::new(addr as _, SIZE)?)) +/// } +/// } +/// +/// impl Drop for IoMem { +/// fn drop(&mut self) { +/// // SAFETY: `self.0.addr()` is guaranteed to be properly mapped by `Self::new`. +/// unsafe { bindings::iounmap(self.0.addr() as _); }; +/// } +/// } +/// +/// impl Deref for IoMem { +/// type Target = Io; +/// +/// fn deref(&self) -> &Self::Target { +/// // SAFETY: The memory range stored in `self` has been properly mapped in `Self::new`. +/// unsafe { Io::from_raw(&self.0) } +/// } +/// } +/// +///# fn no_run() -> Result<(), Error> { +/// // SAFETY: Invalid usage for example purposes. +/// let iomem = unsafe { IoMem::<{ core::mem::size_of::() }>::new(0xBAAAAAAD)? }; +/// iomem.writel(0x42, 0x0); +/// assert!(iomem.try_writel(0x42, 0x0).is_ok()); +/// assert!(iomem.try_writel(0x42, 0x4).is_err()); +/// # Ok(()) +/// # } +/// ``` +#[repr(transparent)] +pub struct Io(IoRaw); + +macro_rules! define_read { + ($(#[$attr:meta])* $name:ident, $try_name:ident, $type_name:ty) => { + /// Read IO data from a given offset known at compile time. + /// + /// Bound checks are performed on compile time, hence if the offset is not known at compile + /// time, the build will fail. + $(#[$attr])* + #[inline] + pub fn $name(&self, offset: usize) -> $type_name { + let addr = self.io_addr_assert::<$type_name>(offset); + + // SAFETY: By the type invariant `addr` is a valid address for MMIO operations. + unsafe { bindings::$name(addr as _) } + } + + /// Read IO data from a given offset. + /// + /// Bound checks are performed on runtime, it fails if the offset (plus the type size) is + /// out of bounds. + $(#[$attr])* + pub fn $try_name(&self, offset: usize) -> Result<$type_name> { + let addr = self.io_addr::<$type_name>(offset)?; + + // SAFETY: By the type invariant `addr` is a valid address for MMIO operations. + Ok(unsafe { bindings::$name(addr as _) }) + } + }; +} + +macro_rules! define_write { + ($(#[$attr:meta])* $name:ident, $try_name:ident, $type_name:ty) => { + /// Write IO data from a given offset known at compile time. + /// + /// Bound checks are performed on compile time, hence if the offset is not known at compile + /// time, the build will fail. + $(#[$attr])* + #[inline] + pub fn $name(&self, value: $type_name, offset: usize) { + let addr = self.io_addr_assert::<$type_name>(offset); + + // SAFETY: By the type invariant `addr` is a valid address for MMIO operations. + unsafe { bindings::$name(value, addr as _, ) } + } + + /// Write IO data from a given offset. + /// + /// Bound checks are performed on runtime, it fails if the offset (plus the type size) is + /// out of bounds. + $(#[$attr])* + pub fn $try_name(&self, value: $type_name, offset: usize) -> Result { + let addr = self.io_addr::<$type_name>(offset)?; + + // SAFETY: By the type invariant `addr` is a valid address for MMIO operations. + unsafe { bindings::$name(value, addr as _) } + Ok(()) + } + }; +} + +impl Io { + /// Converts an `IoRaw` into an `Io` instance, providing the accessors to the MMIO mapping. + /// + /// # Safety + /// + /// Callers must ensure that `addr` is the start of a valid I/O mapped memory region of size + /// `maxsize`. + pub unsafe fn from_raw(raw: &IoRaw) -> &Self { + // SAFETY: `Io` is a transparent wrapper around `IoRaw`. + unsafe { &*core::ptr::from_ref(raw).cast() } + } + + /// Returns the base address of this mapping. + #[inline] + pub fn addr(&self) -> usize { + self.0.addr() + } + + /// Returns the maximum size of this mapping. + #[inline] + pub fn maxsize(&self) -> usize { + self.0.maxsize() + } + + #[inline] + const fn offset_valid(offset: usize, size: usize) -> bool { + let type_size = core::mem::size_of::(); + if let Some(end) = offset.checked_add(type_size) { + end <= size && offset % type_size == 0 + } else { + false + } + } + + #[inline] + fn io_addr(&self, offset: usize) -> Result { + if !Self::offset_valid::(offset, self.maxsize()) { + return Err(EINVAL); + } + + // Probably no need to check, since the safety requirements of `Self::new` guarantee that + // this can't overflow. + self.addr().checked_add(offset).ok_or(EINVAL) + } + + #[inline] + fn io_addr_assert(&self, offset: usize) -> usize { + build_assert!(Self::offset_valid::(offset, SIZE)); + + self.addr() + offset + } + + define_read!(readb, try_readb, u8); + define_read!(readw, try_readw, u16); + define_read!(readl, try_readl, u32); + define_read!( + #[cfg(CONFIG_64BIT)] + readq, + try_readq, + u64 + ); + + define_read!(readb_relaxed, try_readb_relaxed, u8); + define_read!(readw_relaxed, try_readw_relaxed, u16); + define_read!(readl_relaxed, try_readl_relaxed, u32); + define_read!( + #[cfg(CONFIG_64BIT)] + readq_relaxed, + try_readq_relaxed, + u64 + ); + + define_write!(writeb, try_writeb, u8); + define_write!(writew, try_writew, u16); + define_write!(writel, try_writel, u32); + define_write!( + #[cfg(CONFIG_64BIT)] + writeq, + try_writeq, + u64 + ); + + define_write!(writeb_relaxed, try_writeb_relaxed, u8); + define_write!(writew_relaxed, try_writew_relaxed, u16); + define_write!(writel_relaxed, try_writel_relaxed, u32); + define_write!( + #[cfg(CONFIG_64BIT)] + writeq_relaxed, + try_writeq_relaxed, + u64 + ); +} diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 5702ce32ec8e..6c836ab73771 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -79,6 +79,7 @@ pub mod workqueue; #[doc(hidden)] pub use bindings; +pub mod io; pub use macros; pub use uapi; From 76c01ded724bfb464878e22c89f7ecce26f5d50e Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Thu, 19 Dec 2024 18:04:10 +0100 Subject: [PATCH 17/68] rust: add devres abstraction Add a Rust abstraction for the kernel's devres (device resource management) implementation. The Devres type acts as a container to manage the lifetime and accessibility of device bound resources. Therefore it registers a devres callback and revokes access to the resource on invocation. Users of the Devres abstraction can simply free the corresponding resources in their Drop implementation, which is invoked when either the Devres instance goes out of scope or the devres callback leads to the resource being revoked, which implies a call to drop_in_place(). Signed-off-by: Danilo Krummrich Tested-by: Dirk Behme Link: https://lore.kernel.org/r/20241219170425.12036-9-dakr@kernel.org Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 1 + rust/helpers/device.c | 10 +++ rust/helpers/helpers.c | 1 + rust/kernel/devres.rs | 178 +++++++++++++++++++++++++++++++++++++++++ rust/kernel/lib.rs | 1 + 5 files changed, 191 insertions(+) create mode 100644 rust/helpers/device.c create mode 100644 rust/kernel/devres.rs diff --git a/MAINTAINERS b/MAINTAINERS index 8e02ab45184a..fa361bb6c817 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7035,6 +7035,7 @@ F: include/linux/property.h F: lib/kobj* F: rust/kernel/device.rs F: rust/kernel/device_id.rs +F: rust/kernel/devres.rs F: rust/kernel/driver.rs DRIVERS FOR OMAP ADAPTIVE VOLTAGE SCALING (AVS) diff --git a/rust/helpers/device.c b/rust/helpers/device.c new file mode 100644 index 000000000000..b2135c6686b0 --- /dev/null +++ b/rust/helpers/device.c @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include + +int rust_helper_devm_add_action(struct device *dev, + void (*action)(void *), + void *data) +{ + return devm_add_action(dev, action, data); +} diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c index 63f9b1da179f..a3b52aa021de 100644 --- a/rust/helpers/helpers.c +++ b/rust/helpers/helpers.c @@ -12,6 +12,7 @@ #include "build_assert.c" #include "build_bug.c" #include "cred.c" +#include "device.c" #include "err.c" #include "fs.c" #include "io.c" diff --git a/rust/kernel/devres.rs b/rust/kernel/devres.rs new file mode 100644 index 000000000000..9c9dd39584eb --- /dev/null +++ b/rust/kernel/devres.rs @@ -0,0 +1,178 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Devres abstraction +//! +//! [`Devres`] represents an abstraction for the kernel devres (device resource management) +//! implementation. + +use crate::{ + alloc::Flags, + bindings, + device::Device, + error::{Error, Result}, + prelude::*, + revocable::Revocable, + sync::Arc, +}; + +use core::ops::Deref; + +#[pin_data] +struct DevresInner { + #[pin] + data: Revocable, +} + +/// This abstraction is meant to be used by subsystems to containerize [`Device`] bound resources to +/// manage their lifetime. +/// +/// [`Device`] bound resources should be freed when either the resource goes out of scope or the +/// [`Device`] is unbound respectively, depending on what happens first. +/// +/// To achieve that [`Devres`] registers a devres callback on creation, which is called once the +/// [`Device`] is unbound, revoking access to the encapsulated resource (see also [`Revocable`]). +/// +/// After the [`Devres`] has been unbound it is not possible to access the encapsulated resource +/// anymore. +/// +/// [`Devres`] users should make sure to simply free the corresponding backing resource in `T`'s +/// [`Drop`] implementation. +/// +/// # Example +/// +/// ```no_run +/// # use kernel::{bindings, c_str, device::Device, devres::Devres, io::{Io, IoRaw}}; +/// # use core::ops::Deref; +/// +/// // See also [`pci::Bar`] for a real example. +/// struct IoMem(IoRaw); +/// +/// impl IoMem { +/// /// # Safety +/// /// +/// /// [`paddr`, `paddr` + `SIZE`) must be a valid MMIO region that is mappable into the CPUs +/// /// virtual address space. +/// unsafe fn new(paddr: usize) -> Result{ +/// // SAFETY: By the safety requirements of this function [`paddr`, `paddr` + `SIZE`) is +/// // valid for `ioremap`. +/// let addr = unsafe { bindings::ioremap(paddr as _, SIZE as _) }; +/// if addr.is_null() { +/// return Err(ENOMEM); +/// } +/// +/// Ok(IoMem(IoRaw::new(addr as _, SIZE)?)) +/// } +/// } +/// +/// impl Drop for IoMem { +/// fn drop(&mut self) { +/// // SAFETY: `self.0.addr()` is guaranteed to be properly mapped by `Self::new`. +/// unsafe { bindings::iounmap(self.0.addr() as _); }; +/// } +/// } +/// +/// impl Deref for IoMem { +/// type Target = Io; +/// +/// fn deref(&self) -> &Self::Target { +/// // SAFETY: The memory range stored in `self` has been properly mapped in `Self::new`. +/// unsafe { Io::from_raw(&self.0) } +/// } +/// } +/// # fn no_run() -> Result<(), Error> { +/// # // SAFETY: Invalid usage; just for the example to get an `ARef` instance. +/// # let dev = unsafe { Device::get_device(core::ptr::null_mut()) }; +/// +/// // SAFETY: Invalid usage for example purposes. +/// let iomem = unsafe { IoMem::<{ core::mem::size_of::() }>::new(0xBAAAAAAD)? }; +/// let devres = Devres::new(&dev, iomem, GFP_KERNEL)?; +/// +/// let res = devres.try_access().ok_or(ENXIO)?; +/// res.writel(0x42, 0x0); +/// # Ok(()) +/// # } +/// ``` +pub struct Devres(Arc>); + +impl DevresInner { + fn new(dev: &Device, data: T, flags: Flags) -> Result>> { + let inner = Arc::pin_init( + pin_init!( DevresInner { + data <- Revocable::new(data), + }), + flags, + )?; + + // Convert `Arc` into a raw pointer and make devres own this reference until + // `Self::devres_callback` is called. + let data = inner.clone().into_raw(); + + // SAFETY: `devm_add_action` guarantees to call `Self::devres_callback` once `dev` is + // detached. + let ret = unsafe { + bindings::devm_add_action(dev.as_raw(), Some(Self::devres_callback), data as _) + }; + + if ret != 0 { + // SAFETY: We just created another reference to `inner` in order to pass it to + // `bindings::devm_add_action`. If `bindings::devm_add_action` fails, we have to drop + // this reference accordingly. + let _ = unsafe { Arc::from_raw(data) }; + return Err(Error::from_errno(ret)); + } + + Ok(inner) + } + + #[allow(clippy::missing_safety_doc)] + unsafe extern "C" fn devres_callback(ptr: *mut kernel::ffi::c_void) { + let ptr = ptr as *mut DevresInner; + // Devres owned this memory; now that we received the callback, drop the `Arc` and hence the + // reference. + // SAFETY: Safe, since we leaked an `Arc` reference to devm_add_action() in + // `DevresInner::new`. + let inner = unsafe { Arc::from_raw(ptr) }; + + inner.data.revoke(); + } +} + +impl Devres { + /// Creates a new [`Devres`] instance of the given `data`. The `data` encapsulated within the + /// returned `Devres` instance' `data` will be revoked once the device is detached. + pub fn new(dev: &Device, data: T, flags: Flags) -> Result { + let inner = DevresInner::new(dev, data, flags)?; + + Ok(Devres(inner)) + } + + /// Same as [`Devres::new`], but does not return a `Devres` instance. Instead the given `data` + /// is owned by devres and will be revoked / dropped, once the device is detached. + pub fn new_foreign_owned(dev: &Device, data: T, flags: Flags) -> Result { + let _ = DevresInner::new(dev, data, flags)?; + + Ok(()) + } +} + +impl Deref for Devres { + type Target = Revocable; + + fn deref(&self) -> &Self::Target { + &self.0.data + } +} + +impl Drop for Devres { + fn drop(&mut self) { + // Revoke the data, such that it gets dropped already and the actual resource is freed. + // + // `DevresInner` has to stay alive until the devres callback has been called. This is + // necessary since we don't know when `Devres` is dropped and calling + // `devm_remove_action()` instead could race with `devres_release_all()`. + // + // SAFETY: When `drop` runs, it's guaranteed that nobody is accessing the revocable data + // anymore, hence it is safe not to wait for the grace period to finish. + unsafe { self.revoke_nosync() }; + } +} diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 6c836ab73771..2b61bf99d1ee 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -41,6 +41,7 @@ mod build_assert; pub mod cred; pub mod device; pub mod device_id; +pub mod devres; pub mod driver; pub mod error; #[cfg(CONFIG_RUST_FW_LOADER_ABSTRACTIONS)] From 1bd8b6b2c5d38d9881d59928b986eacba40f9da8 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Thu, 19 Dec 2024 18:04:11 +0100 Subject: [PATCH 18/68] rust: pci: add basic PCI device / driver abstractions Implement the basic PCI abstractions required to write a basic PCI driver. This includes the following data structures: The `pci::Driver` trait represents the interface to the driver and provides `pci::Driver::probe` for the driver to implement. The `pci::Device` abstraction represents a `struct pci_dev` and provides abstractions for common functions, such as `pci::Device::set_master`. In order to provide the PCI specific parts to a generic `driver::Registration` the `driver::RegistrationOps` trait is implemented by `pci::Adapter`. `pci::DeviceId` implements PCI device IDs based on the generic `device_id::RawDevceId` abstraction. Co-developed-by: FUJITA Tomonori Signed-off-by: FUJITA Tomonori Signed-off-by: Danilo Krummrich Tested-by: Dirk Behme Link: https://lore.kernel.org/r/20241219170425.12036-10-dakr@kernel.org Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 1 + rust/bindings/bindings_helper.h | 1 + rust/helpers/helpers.c | 1 + rust/helpers/pci.c | 18 ++ rust/kernel/lib.rs | 2 + rust/kernel/pci.rs | 292 ++++++++++++++++++++++++++++++++ 6 files changed, 315 insertions(+) create mode 100644 rust/helpers/pci.c create mode 100644 rust/kernel/pci.rs diff --git a/MAINTAINERS b/MAINTAINERS index fa361bb6c817..60ae26513396 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -18105,6 +18105,7 @@ F: include/asm-generic/pci* F: include/linux/of_pci.h F: include/linux/pci* F: include/uapi/linux/pci* +F: rust/kernel/pci.rs PCIE BANDWIDTH CONTROLLER M: Ilpo Järvinen diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index 5c4dfe22f41a..6d7a68e2ecb7 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c index a3b52aa021de..3fda33cd42d4 100644 --- a/rust/helpers/helpers.c +++ b/rust/helpers/helpers.c @@ -20,6 +20,7 @@ #include "kunit.c" #include "mutex.c" #include "page.c" +#include "pci.c" #include "pid_namespace.c" #include "rbtree.c" #include "rcu.c" diff --git a/rust/helpers/pci.c b/rust/helpers/pci.c new file mode 100644 index 000000000000..8ba22f911459 --- /dev/null +++ b/rust/helpers/pci.c @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include + +void rust_helper_pci_set_drvdata(struct pci_dev *pdev, void *data) +{ + pci_set_drvdata(pdev, data); +} + +void *rust_helper_pci_get_drvdata(struct pci_dev *pdev) +{ + return pci_get_drvdata(pdev); +} + +resource_size_t rust_helper_pci_resource_len(struct pci_dev *pdev, int bar) +{ + return pci_resource_len(pdev, bar); +} diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 2b61bf99d1ee..1dc7eda6b480 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -82,6 +82,8 @@ pub mod workqueue; pub use bindings; pub mod io; pub use macros; +#[cfg(all(CONFIG_PCI, CONFIG_PCI_MSI))] +pub mod pci; pub use uapi; #[doc(hidden)] diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs new file mode 100644 index 000000000000..581a2d140c8d --- /dev/null +++ b/rust/kernel/pci.rs @@ -0,0 +1,292 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Abstractions for the PCI bus. +//! +//! C header: [`include/linux/pci.h`](srctree/include/linux/pci.h) + +use crate::{ + bindings, container_of, device, + device_id::RawDeviceId, + driver, + error::{to_result, Result}, + str::CStr, + types::{ARef, ForeignOwnable, Opaque}, + ThisModule, +}; +use core::ptr::addr_of_mut; +use kernel::prelude::*; + +/// An adapter for the registration of PCI drivers. +pub struct Adapter(T); + +impl driver::RegistrationOps for Adapter { + type RegType = bindings::pci_driver; + + fn register( + pdrv: &Opaque, + name: &'static CStr, + module: &'static ThisModule, + ) -> Result { + // SAFETY: It's safe to set the fields of `struct pci_driver` on initialization. + unsafe { + (*pdrv.get()).name = name.as_char_ptr(); + (*pdrv.get()).probe = Some(Self::probe_callback); + (*pdrv.get()).remove = Some(Self::remove_callback); + (*pdrv.get()).id_table = T::ID_TABLE.as_ptr(); + } + + // SAFETY: `pdrv` is guaranteed to be a valid `RegType`. + to_result(unsafe { + bindings::__pci_register_driver(pdrv.get(), module.0, name.as_char_ptr()) + }) + } + + fn unregister(pdrv: &Opaque) { + // SAFETY: `pdrv` is guaranteed to be a valid `RegType`. + unsafe { bindings::pci_unregister_driver(pdrv.get()) } + } +} + +impl Adapter { + extern "C" fn probe_callback( + pdev: *mut bindings::pci_dev, + id: *const bindings::pci_device_id, + ) -> kernel::ffi::c_int { + // SAFETY: The PCI bus only ever calls the probe callback with a valid pointer to a + // `struct pci_dev`. + let dev = unsafe { device::Device::get_device(addr_of_mut!((*pdev).dev)) }; + // SAFETY: `dev` is guaranteed to be embedded in a valid `struct pci_dev` by the call + // above. + let mut pdev = unsafe { Device::from_dev(dev) }; + + // SAFETY: `DeviceId` is a `#[repr(transparent)` wrapper of `struct pci_device_id` and + // does not add additional invariants, so it's safe to transmute. + let id = unsafe { &*id.cast::() }; + let info = T::ID_TABLE.info(id.index()); + + match T::probe(&mut pdev, info) { + Ok(data) => { + // Let the `struct pci_dev` own a reference of the driver's private data. + // SAFETY: By the type invariant `pdev.as_raw` returns a valid pointer to a + // `struct pci_dev`. + unsafe { bindings::pci_set_drvdata(pdev.as_raw(), data.into_foreign() as _) }; + } + Err(err) => return Error::to_errno(err), + } + + 0 + } + + extern "C" fn remove_callback(pdev: *mut bindings::pci_dev) { + // SAFETY: The PCI bus only ever calls the remove callback with a valid pointer to a + // `struct pci_dev`. + let ptr = unsafe { bindings::pci_get_drvdata(pdev) }; + + // SAFETY: `remove_callback` is only ever called after a successful call to + // `probe_callback`, hence it's guaranteed that `ptr` points to a valid and initialized + // `KBox` pointer created through `KBox::into_foreign`. + let _ = unsafe { KBox::::from_foreign(ptr) }; + } +} + +/// Declares a kernel module that exposes a single PCI driver. +/// +/// # Example +/// +///```ignore +/// kernel::module_pci_driver! { +/// type: MyDriver, +/// name: "Module name", +/// author: "Author name", +/// description: "Description", +/// license: "GPL v2", +/// } +///``` +#[macro_export] +macro_rules! module_pci_driver { +($($f:tt)*) => { + $crate::module_driver!(, $crate::pci::Adapter, { $($f)* }); +}; +} + +/// Abstraction for bindings::pci_device_id. +#[repr(transparent)] +#[derive(Clone, Copy)] +pub struct DeviceId(bindings::pci_device_id); + +impl DeviceId { + const PCI_ANY_ID: u32 = !0; + + /// Equivalent to C's `PCI_DEVICE` macro. + /// + /// Create a new `pci::DeviceId` from a vendor and device ID number. + pub const fn from_id(vendor: u32, device: u32) -> Self { + Self(bindings::pci_device_id { + vendor, + device, + subvendor: DeviceId::PCI_ANY_ID, + subdevice: DeviceId::PCI_ANY_ID, + class: 0, + class_mask: 0, + driver_data: 0, + override_only: 0, + }) + } + + /// Equivalent to C's `PCI_DEVICE_CLASS` macro. + /// + /// Create a new `pci::DeviceId` from a class number and mask. + pub const fn from_class(class: u32, class_mask: u32) -> Self { + Self(bindings::pci_device_id { + vendor: DeviceId::PCI_ANY_ID, + device: DeviceId::PCI_ANY_ID, + subvendor: DeviceId::PCI_ANY_ID, + subdevice: DeviceId::PCI_ANY_ID, + class, + class_mask, + driver_data: 0, + override_only: 0, + }) + } +} + +// SAFETY: +// * `DeviceId` is a `#[repr(transparent)` wrapper of `pci_device_id` and does not add +// additional invariants, so it's safe to transmute to `RawType`. +// * `DRIVER_DATA_OFFSET` is the offset to the `driver_data` field. +unsafe impl RawDeviceId for DeviceId { + type RawType = bindings::pci_device_id; + + const DRIVER_DATA_OFFSET: usize = core::mem::offset_of!(bindings::pci_device_id, driver_data); + + fn index(&self) -> usize { + self.0.driver_data as _ + } +} + +/// IdTable type for PCI +pub type IdTable = &'static dyn kernel::device_id::IdTable; + +/// Create a PCI `IdTable` with its alias for modpost. +#[macro_export] +macro_rules! pci_device_table { + ($table_name:ident, $module_table_name:ident, $id_info_type: ty, $table_data: expr) => { + const $table_name: $crate::device_id::IdArray< + $crate::pci::DeviceId, + $id_info_type, + { $table_data.len() }, + > = $crate::device_id::IdArray::new($table_data); + + $crate::module_device_table!("pci", $module_table_name, $table_name); + }; +} + +/// The PCI driver trait. +/// +/// # Example +/// +///``` +/// # use kernel::{bindings, pci}; +/// +/// struct MyDriver; +/// +/// kernel::pci_device_table!( +/// PCI_TABLE, +/// MODULE_PCI_TABLE, +/// ::IdInfo, +/// [ +/// (pci::DeviceId::from_id(bindings::PCI_VENDOR_ID_REDHAT, bindings::PCI_ANY_ID as _), ()) +/// ] +/// ); +/// +/// impl pci::Driver for MyDriver { +/// type IdInfo = (); +/// const ID_TABLE: pci::IdTable = &PCI_TABLE; +/// +/// fn probe( +/// _pdev: &mut pci::Device, +/// _id_info: &Self::IdInfo, +/// ) -> Result>> { +/// Err(ENODEV) +/// } +/// } +///``` +/// Drivers must implement this trait in order to get a PCI driver registered. Please refer to the +/// `Adapter` documentation for an example. +pub trait Driver { + /// The type holding information about each device id supported by the driver. + /// + /// TODO: Use associated_type_defaults once stabilized: + /// + /// type IdInfo: 'static = (); + type IdInfo: 'static; + + /// The table of device ids supported by the driver. + const ID_TABLE: IdTable; + + /// PCI driver probe. + /// + /// Called when a new platform device is added or discovered. + /// Implementers should attempt to initialize the device here. + fn probe(dev: &mut Device, id_info: &Self::IdInfo) -> Result>>; +} + +/// The PCI device representation. +/// +/// A PCI device is based on an always reference counted `device:Device` instance. Cloning a PCI +/// device, hence, also increments the base device' reference count. +#[derive(Clone)] +pub struct Device(ARef); + +impl Device { + /// Create a PCI Device instance from an existing `device::Device`. + /// + /// # Safety + /// + /// `dev` must be an `ARef` whose underlying `bindings::device` is a member of + /// a `bindings::pci_dev`. + pub unsafe fn from_dev(dev: ARef) -> Self { + Self(dev) + } + + fn as_raw(&self) -> *mut bindings::pci_dev { + // SAFETY: By the type invariant `self.0.as_raw` is a pointer to the `struct device` + // embedded in `struct pci_dev`. + unsafe { container_of!(self.0.as_raw(), bindings::pci_dev, dev) as _ } + } + + /// Returns the PCI vendor ID. + pub fn vendor_id(&self) -> u16 { + // SAFETY: `self.as_raw` is a valid pointer to a `struct pci_dev`. + unsafe { (*self.as_raw()).vendor } + } + + /// Returns the PCI device ID. + pub fn device_id(&self) -> u16 { + // SAFETY: `self.as_raw` is a valid pointer to a `struct pci_dev`. + unsafe { (*self.as_raw()).device } + } + + /// Enable memory resources for this device. + pub fn enable_device_mem(&self) -> Result { + // SAFETY: `self.as_raw` is guaranteed to be a pointer to a valid `struct pci_dev`. + let ret = unsafe { bindings::pci_enable_device_mem(self.as_raw()) }; + if ret != 0 { + Err(Error::from_errno(ret)) + } else { + Ok(()) + } + } + + /// Enable bus-mastering for this device. + pub fn set_master(&self) { + // SAFETY: `self.as_raw` is guaranteed to be a pointer to a valid `struct pci_dev`. + unsafe { bindings::pci_set_master(self.as_raw()) }; + } +} + +impl AsRef for Device { + fn as_ref(&self) -> &device::Device { + &self.0 + } +} From bf9651f84b4e49ca006fd8b5534f16a38dae875c Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Thu, 19 Dec 2024 18:04:12 +0100 Subject: [PATCH 19/68] rust: pci: implement I/O mappable `pci::Bar` Implement `pci::Bar`, `pci::Device::iomap_region` and `pci::Device::iomap_region_sized` to allow for I/O mappings of PCI BARs. To ensure that a `pci::Bar`, and hence the I/O memory mapping, can't out-live the PCI device, the `pci::Bar` type is always embedded into a `Devres` container, such that the `pci::Bar` is revoked once the device is unbound and hence the I/O mapped memory is unmapped. A `pci::Bar` can be requested with (`pci::Device::iomap_region_sized`) or without (`pci::Device::iomap_region`) a const generic representing the minimal requested size of the I/O mapped memory region. In case of the latter only runtime checked I/O reads / writes are possible. Co-developed-by: Philipp Stanner Signed-off-by: Philipp Stanner Signed-off-by: Danilo Krummrich Tested-by: Dirk Behme Link: https://lore.kernel.org/r/20241219170425.12036-11-dakr@kernel.org Signed-off-by: Greg Kroah-Hartman --- rust/kernel/pci.rs | 142 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 141 insertions(+), 1 deletion(-) diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs index 581a2d140c8d..d5e7f0b15303 100644 --- a/rust/kernel/pci.rs +++ b/rust/kernel/pci.rs @@ -5,15 +5,19 @@ //! C header: [`include/linux/pci.h`](srctree/include/linux/pci.h) use crate::{ + alloc::flags::*, bindings, container_of, device, device_id::RawDeviceId, + devres::Devres, driver, error::{to_result, Result}, + io::Io, + io::IoRaw, str::CStr, types::{ARef, ForeignOwnable, Opaque}, ThisModule, }; -use core::ptr::addr_of_mut; +use core::{ops::Deref, ptr::addr_of_mut}; use kernel::prelude::*; /// An adapter for the registration of PCI drivers. @@ -235,9 +239,115 @@ pub trait Driver { /// /// A PCI device is based on an always reference counted `device:Device` instance. Cloning a PCI /// device, hence, also increments the base device' reference count. +/// +/// # Invariants +/// +/// `Device` hold a valid reference of `ARef` whose underlying `struct device` is a +/// member of a `struct pci_dev`. #[derive(Clone)] pub struct Device(ARef); +/// A PCI BAR to perform I/O-Operations on. +/// +/// # Invariants +/// +/// `Bar` always holds an `IoRaw` inststance that holds a valid pointer to the start of the I/O +/// memory mapped PCI bar and its size. +pub struct Bar { + pdev: Device, + io: IoRaw, + num: i32, +} + +impl Bar { + fn new(pdev: Device, num: u32, name: &CStr) -> Result { + let len = pdev.resource_len(num)?; + if len == 0 { + return Err(ENOMEM); + } + + // Convert to `i32`, since that's what all the C bindings use. + let num = i32::try_from(num)?; + + // SAFETY: + // `pdev` is valid by the invariants of `Device`. + // `num` is checked for validity by a previous call to `Device::resource_len`. + // `name` is always valid. + let ret = unsafe { bindings::pci_request_region(pdev.as_raw(), num, name.as_char_ptr()) }; + if ret != 0 { + return Err(EBUSY); + } + + // SAFETY: + // `pdev` is valid by the invariants of `Device`. + // `num` is checked for validity by a previous call to `Device::resource_len`. + // `name` is always valid. + let ioptr: usize = unsafe { bindings::pci_iomap(pdev.as_raw(), num, 0) } as usize; + if ioptr == 0 { + // SAFETY: + // `pdev` valid by the invariants of `Device`. + // `num` is checked for validity by a previous call to `Device::resource_len`. + unsafe { bindings::pci_release_region(pdev.as_raw(), num) }; + return Err(ENOMEM); + } + + let io = match IoRaw::new(ioptr, len as usize) { + Ok(io) => io, + Err(err) => { + // SAFETY: + // `pdev` is valid by the invariants of `Device`. + // `ioptr` is guaranteed to be the start of a valid I/O mapped memory region. + // `num` is checked for validity by a previous call to `Device::resource_len`. + unsafe { Self::do_release(&pdev, ioptr, num) }; + return Err(err); + } + }; + + Ok(Bar { pdev, io, num }) + } + + /// # Safety + /// + /// `ioptr` must be a valid pointer to the memory mapped PCI bar number `num`. + unsafe fn do_release(pdev: &Device, ioptr: usize, num: i32) { + // SAFETY: + // `pdev` is valid by the invariants of `Device`. + // `ioptr` is valid by the safety requirements. + // `num` is valid by the safety requirements. + unsafe { + bindings::pci_iounmap(pdev.as_raw(), ioptr as _); + bindings::pci_release_region(pdev.as_raw(), num); + } + } + + fn release(&self) { + // SAFETY: The safety requirements are guaranteed by the type invariant of `self.pdev`. + unsafe { Self::do_release(&self.pdev, self.io.addr(), self.num) }; + } +} + +impl Bar { + fn index_is_valid(index: u32) -> bool { + // A `struct pci_dev` owns an array of resources with at most `PCI_NUM_RESOURCES` entries. + index < bindings::PCI_NUM_RESOURCES + } +} + +impl Drop for Bar { + fn drop(&mut self) { + self.release(); + } +} + +impl Deref for Bar { + type Target = Io; + + fn deref(&self) -> &Self::Target { + // SAFETY: By the type invariant of `Self`, the MMIO range in `self.io` is properly mapped. + unsafe { Io::from_raw(&self.io) } + } +} + impl Device { /// Create a PCI Device instance from an existing `device::Device`. /// @@ -283,6 +393,36 @@ impl Device { // SAFETY: `self.as_raw` is guaranteed to be a pointer to a valid `struct pci_dev`. unsafe { bindings::pci_set_master(self.as_raw()) }; } + + /// Returns the size of the given PCI bar resource. + pub fn resource_len(&self, bar: u32) -> Result { + if !Bar::index_is_valid(bar) { + return Err(EINVAL); + } + + // SAFETY: + // - `bar` is a valid bar number, as guaranteed by the above call to `Bar::index_is_valid`, + // - by its type invariant `self.as_raw` is always a valid pointer to a `struct pci_dev`. + Ok(unsafe { bindings::pci_resource_len(self.as_raw(), bar.try_into()?) }) + } + + /// Mapps an entire PCI-BAR after performing a region-request on it. I/O operation bound checks + /// can be performed on compile time for offsets (plus the requested type size) < SIZE. + pub fn iomap_region_sized( + &self, + bar: u32, + name: &CStr, + ) -> Result>> { + let bar = Bar::::new(self.clone(), bar, name)?; + let devres = Devres::new(self.as_ref(), bar, GFP_KERNEL)?; + + Ok(devres) + } + + /// Mapps an entire PCI-BAR after performing a region-request on it. + pub fn iomap_region(&self, bar: u32, name: &CStr) -> Result> { + self.iomap_region_sized::<0>(bar, name) + } } impl AsRef for Device { From 685376d18e9ae2f08ab6ac36285dc3a949c8cb77 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Thu, 19 Dec 2024 18:04:13 +0100 Subject: [PATCH 20/68] samples: rust: add Rust PCI sample driver This commit adds a sample Rust PCI driver for QEMU's "pci-testdev" device. To enable this device QEMU has to be called with `-device pci-testdev`. The same driver shows how to use the PCI device / driver abstractions, as well as how to request and map PCI BARs, including a short sequence of MMIO operations. Signed-off-by: Danilo Krummrich Tested-by: Dirk Behme Link: https://lore.kernel.org/r/20241219170425.12036-12-dakr@kernel.org Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 1 + samples/rust/Kconfig | 11 ++++ samples/rust/Makefile | 1 + samples/rust/rust_driver_pci.rs | 110 ++++++++++++++++++++++++++++++++ 4 files changed, 123 insertions(+) create mode 100644 samples/rust/rust_driver_pci.rs diff --git a/MAINTAINERS b/MAINTAINERS index 60ae26513396..2a334f2a5954 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -18106,6 +18106,7 @@ F: include/linux/of_pci.h F: include/linux/pci* F: include/uapi/linux/pci* F: rust/kernel/pci.rs +F: samples/rust/rust_driver_pci.rs PCIE BANDWIDTH CONTROLLER M: Ilpo Järvinen diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig index df384e679901..002af98e70be 100644 --- a/samples/rust/Kconfig +++ b/samples/rust/Kconfig @@ -40,6 +40,17 @@ config SAMPLE_RUST_PRINT If unsure, say N. +config SAMPLE_RUST_DRIVER_PCI + tristate "PCI Driver" + depends on PCI + help + This option builds the Rust PCI driver sample. + + To compile this as a module, choose M here: + the module will be called driver_pci. + + If unsure, say N. + config SAMPLE_RUST_HOSTPROGS bool "Host programs" help diff --git a/samples/rust/Makefile b/samples/rust/Makefile index ad4b97a98580..b4581473ca1b 100644 --- a/samples/rust/Makefile +++ b/samples/rust/Makefile @@ -4,6 +4,7 @@ ccflags-y += -I$(src) # needed for trace events obj-$(CONFIG_SAMPLE_RUST_MINIMAL) += rust_minimal.o obj-$(CONFIG_SAMPLE_RUST_MISC_DEVICE) += rust_misc_device.o obj-$(CONFIG_SAMPLE_RUST_PRINT) += rust_print.o +obj-$(CONFIG_SAMPLE_RUST_DRIVER_PCI) += rust_driver_pci.o rust_print-y := rust_print_main.o rust_print_events.o diff --git a/samples/rust/rust_driver_pci.rs b/samples/rust/rust_driver_pci.rs new file mode 100644 index 000000000000..1fb6e44f3395 --- /dev/null +++ b/samples/rust/rust_driver_pci.rs @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Rust PCI driver sample (based on QEMU's `pci-testdev`). +//! +//! To make this driver probe, QEMU must be run with `-device pci-testdev`. + +use kernel::{bindings, c_str, devres::Devres, pci, prelude::*}; + +struct Regs; + +impl Regs { + const TEST: usize = 0x0; + const OFFSET: usize = 0x4; + const DATA: usize = 0x8; + const COUNT: usize = 0xC; + const END: usize = 0x10; +} + +type Bar0 = pci::Bar<{ Regs::END }>; + +#[derive(Debug)] +struct TestIndex(u8); + +impl TestIndex { + const NO_EVENTFD: Self = Self(0); +} + +struct SampleDriver { + pdev: pci::Device, + bar: Devres, +} + +kernel::pci_device_table!( + PCI_TABLE, + MODULE_PCI_TABLE, + ::IdInfo, + [( + pci::DeviceId::from_id(bindings::PCI_VENDOR_ID_REDHAT, 0x5), + TestIndex::NO_EVENTFD + )] +); + +impl SampleDriver { + fn testdev(index: &TestIndex, bar: &Bar0) -> Result { + // Select the test. + bar.writeb(index.0, Regs::TEST); + + let offset = u32::from_le(bar.readl(Regs::OFFSET)) as usize; + let data = bar.readb(Regs::DATA); + + // Write `data` to `offset` to increase `count` by one. + // + // Note that we need `try_writeb`, since `offset` can't be checked at compile-time. + bar.try_writeb(data, offset)?; + + Ok(bar.readl(Regs::COUNT)) + } +} + +impl pci::Driver for SampleDriver { + type IdInfo = TestIndex; + + const ID_TABLE: pci::IdTable = &PCI_TABLE; + + fn probe(pdev: &mut pci::Device, info: &Self::IdInfo) -> Result>> { + dev_dbg!( + pdev.as_ref(), + "Probe Rust PCI driver sample (PCI ID: 0x{:x}, 0x{:x}).\n", + pdev.vendor_id(), + pdev.device_id() + ); + + pdev.enable_device_mem()?; + pdev.set_master(); + + let bar = pdev.iomap_region_sized::<{ Regs::END }>(0, c_str!("rust_driver_pci"))?; + + let drvdata = KBox::new( + Self { + pdev: pdev.clone(), + bar, + }, + GFP_KERNEL, + )?; + + let bar = drvdata.bar.try_access().ok_or(ENXIO)?; + + dev_info!( + pdev.as_ref(), + "pci-testdev data-match count: {}\n", + Self::testdev(info, &bar)? + ); + + Ok(drvdata.into()) + } +} + +impl Drop for SampleDriver { + fn drop(&mut self) { + dev_dbg!(self.pdev.as_ref(), "Remove Rust PCI driver sample.\n"); + } +} + +kernel::module_pci_driver! { + type: SampleDriver, + name: "rust_driver_pci", + author: "Danilo Krummrich", + description: "Rust PCI driver", + license: "GPL v2", +} From bbe3b4d1580dac8ea0e1451e38d1be9590a89ddc Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Thu, 19 Dec 2024 18:04:14 +0100 Subject: [PATCH 21/68] rust: of: add `of::DeviceId` abstraction `of::DeviceId` is an abstraction around `struct of_device_id`. This is used by subsequent patches, in particular the platform bus abstractions, to create OF device ID tables. Reviewed-by: Rob Herring (Arm) Signed-off-by: Danilo Krummrich Tested-by: Dirk Behme Tested-by: Fabien Parent Link: https://lore.kernel.org/r/20241219170425.12036-13-dakr@kernel.org Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 1 + rust/kernel/lib.rs | 1 + rust/kernel/of.rs | 60 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+) create mode 100644 rust/kernel/of.rs diff --git a/MAINTAINERS b/MAINTAINERS index 2a334f2a5954..8f22024fa759 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17505,6 +17505,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git F: Documentation/ABI/testing/sysfs-firmware-ofw F: drivers/of/ F: include/linux/of*.h +F: rust/kernel/of.rs F: scripts/dtc/ F: tools/testing/selftests/dt/ K: of_overlay_notifier_ diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 1dc7eda6b480..27f914b0769b 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -56,6 +56,7 @@ pub mod list; pub mod miscdevice; #[cfg(CONFIG_NET)] pub mod net; +pub mod of; pub mod page; pub mod pid_namespace; pub mod prelude; diff --git a/rust/kernel/of.rs b/rust/kernel/of.rs new file mode 100644 index 000000000000..04f2d8ef29cb --- /dev/null +++ b/rust/kernel/of.rs @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Device Tree / Open Firmware abstractions. + +use crate::{bindings, device_id::RawDeviceId, prelude::*}; + +/// IdTable type for OF drivers. +pub type IdTable = &'static dyn kernel::device_id::IdTable; + +/// An open firmware device id. +#[repr(transparent)] +#[derive(Clone, Copy)] +pub struct DeviceId(bindings::of_device_id); + +// SAFETY: +// * `DeviceId` is a `#[repr(transparent)` wrapper of `struct of_device_id` and does not add +// additional invariants, so it's safe to transmute to `RawType`. +// * `DRIVER_DATA_OFFSET` is the offset to the `data` field. +unsafe impl RawDeviceId for DeviceId { + type RawType = bindings::of_device_id; + + const DRIVER_DATA_OFFSET: usize = core::mem::offset_of!(bindings::of_device_id, data); + + fn index(&self) -> usize { + self.0.data as _ + } +} + +impl DeviceId { + /// Create a new device id from an OF 'compatible' string. + pub const fn new(compatible: &'static CStr) -> Self { + let src = compatible.as_bytes_with_nul(); + // Replace with `bindings::of_device_id::default()` once stabilized for `const`. + // SAFETY: FFI type is valid to be zero-initialized. + let mut of: bindings::of_device_id = unsafe { core::mem::zeroed() }; + + // TODO: Use `clone_from_slice` once the corresponding types do match. + let mut i = 0; + while i < src.len() { + of.compatible[i] = src[i] as _; + i += 1; + } + + Self(of) + } +} + +/// Create an OF `IdTable` with an "alias" for modpost. +#[macro_export] +macro_rules! of_device_table { + ($table_name:ident, $module_table_name:ident, $id_info_type: ty, $table_data: expr) => { + const $table_name: $crate::device_id::IdArray< + $crate::of::DeviceId, + $id_info_type, + { $table_data.len() }, + > = $crate::device_id::IdArray::new($table_data); + + $crate::module_device_table!("of", $module_table_name, $table_name); + }; +} From 7a718a1f26d1697465f0f4e402a69e29d6c4dd33 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Thu, 19 Dec 2024 18:04:15 +0100 Subject: [PATCH 22/68] rust: driver: implement `Adapter` In order to not duplicate code in bus specific implementations (e.g. platform), implement a generic `driver::Adapter` to represent the connection of matched drivers and devices. Bus specific `Adapter` implementations can simply implement this trait to inherit generic functionality, such as matching OF or ACPI device IDs and ID table entries. Suggested-by: Rob Herring (Arm) Signed-off-by: Danilo Krummrich Tested-by: Dirk Behme Link: https://lore.kernel.org/r/20241219170425.12036-14-dakr@kernel.org Signed-off-by: Greg Kroah-Hartman --- rust/bindings/bindings_helper.h | 1 + rust/kernel/driver.rs | 58 ++++++++++++++++++++++++++++++++- 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index 6d7a68e2ecb7..8fe70183a392 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/kernel/driver.rs b/rust/kernel/driver.rs index c1957ee7bb7e..c630e65098ed 100644 --- a/rust/kernel/driver.rs +++ b/rust/kernel/driver.rs @@ -6,7 +6,7 @@ //! register using the [`Registration`] class. use crate::error::{Error, Result}; -use crate::{init::PinInit, str::CStr, try_pin_init, types::Opaque, ThisModule}; +use crate::{device, init::PinInit, of, str::CStr, try_pin_init, types::Opaque, ThisModule}; use core::pin::Pin; use macros::{pin_data, pinned_drop}; @@ -115,3 +115,59 @@ macro_rules! module_driver { } } } + +/// The bus independent adapter to match a drivers and a devices. +/// +/// This trait should be implemented by the bus specific adapter, which represents the connection +/// of a device and a driver. +/// +/// It provides bus independent functions for device / driver interactions. +pub trait Adapter { + /// The type holding driver private data about each device id supported by the driver. + type IdInfo: 'static; + + /// The [`of::IdTable`] of the corresponding driver. + fn of_id_table() -> Option>; + + /// Returns the driver's private data from the matching entry in the [`of::IdTable`], if any. + /// + /// If this returns `None`, it means there is no match with an entry in the [`of::IdTable`]. + #[cfg(CONFIG_OF)] + fn of_id_info(dev: &device::Device) -> Option<&'static Self::IdInfo> { + let table = Self::of_id_table()?; + + // SAFETY: + // - `table` has static lifetime, hence it's valid for read, + // - `dev` is guaranteed to be valid while it's alive, and so is `pdev.as_ref().as_raw()`. + let raw_id = unsafe { bindings::of_match_device(table.as_ptr(), dev.as_raw()) }; + + if raw_id.is_null() { + None + } else { + // SAFETY: `DeviceId` is a `#[repr(transparent)` wrapper of `struct of_device_id` and + // does not add additional invariants, so it's safe to transmute. + let id = unsafe { &*raw_id.cast::() }; + + Some(table.info(::index(id))) + } + } + + #[cfg(not(CONFIG_OF))] + #[allow(missing_docs)] + fn of_id_info(_dev: &device::Device) -> Option<&'static Self::IdInfo> { + None + } + + /// Returns the driver's private data from the matching entry of any of the ID tables, if any. + /// + /// If this returns `None`, it means that there is no match in any of the ID tables directly + /// associated with a [`device::Device`]. + fn id_info(dev: &device::Device) -> Option<&'static Self::IdInfo> { + let id = Self::of_id_info(dev); + if id.is_some() { + return id; + } + + None + } +} From 683a63befc7385bf7f19ba30fc0b4b14961114c5 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Thu, 19 Dec 2024 18:04:16 +0100 Subject: [PATCH 23/68] rust: platform: add basic platform device / driver abstractions Implement the basic platform bus abstractions required to write a basic platform driver. This includes the following data structures: The `platform::Driver` trait represents the interface to the driver and provides `platform::Driver::probe` for the driver to implement. The `platform::Device` abstraction represents a `struct platform_device`. In order to provide the platform bus specific parts to a generic `driver::Registration` the `driver::RegistrationOps` trait is implemented by `platform::Adapter`. Reviewed-by: Rob Herring (Arm) Signed-off-by: Danilo Krummrich Tested-by: Dirk Behme Link: https://lore.kernel.org/r/20241219170425.12036-15-dakr@kernel.org Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 1 + rust/bindings/bindings_helper.h | 1 + rust/helpers/helpers.c | 1 + rust/helpers/platform.c | 13 +++ rust/kernel/lib.rs | 1 + rust/kernel/platform.rs | 198 ++++++++++++++++++++++++++++++++ 6 files changed, 215 insertions(+) create mode 100644 rust/helpers/platform.c create mode 100644 rust/kernel/platform.rs diff --git a/MAINTAINERS b/MAINTAINERS index 8f22024fa759..0da80deb14b5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7037,6 +7037,7 @@ F: rust/kernel/device.rs F: rust/kernel/device_id.rs F: rust/kernel/devres.rs F: rust/kernel/driver.rs +F: rust/kernel/platform.rs DRIVERS FOR OMAP ADAPTIVE VOLTAGE SCALING (AVS) M: Nishanth Menon diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index 8fe70183a392..e9fdceb568b8 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c index 3fda33cd42d4..0640b7e115be 100644 --- a/rust/helpers/helpers.c +++ b/rust/helpers/helpers.c @@ -20,6 +20,7 @@ #include "kunit.c" #include "mutex.c" #include "page.c" +#include "platform.c" #include "pci.c" #include "pid_namespace.c" #include "rbtree.c" diff --git a/rust/helpers/platform.c b/rust/helpers/platform.c new file mode 100644 index 000000000000..ab9b9f317301 --- /dev/null +++ b/rust/helpers/platform.c @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include + +void *rust_helper_platform_get_drvdata(const struct platform_device *pdev) +{ + return platform_get_drvdata(pdev); +} + +void rust_helper_platform_set_drvdata(struct platform_device *pdev, void *data) +{ + platform_set_drvdata(pdev, data); +} diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 27f914b0769b..e59250dc6c6e 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -59,6 +59,7 @@ pub mod net; pub mod of; pub mod page; pub mod pid_namespace; +pub mod platform; pub mod prelude; pub mod print; pub mod rbtree; diff --git a/rust/kernel/platform.rs b/rust/kernel/platform.rs new file mode 100644 index 000000000000..03287794f9d0 --- /dev/null +++ b/rust/kernel/platform.rs @@ -0,0 +1,198 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Abstractions for the platform bus. +//! +//! C header: [`include/linux/platform_device.h`](srctree/include/linux/platform_device.h) + +use crate::{ + bindings, container_of, device, driver, + error::{to_result, Result}, + of, + prelude::*, + str::CStr, + types::{ARef, ForeignOwnable, Opaque}, + ThisModule, +}; + +use core::ptr::addr_of_mut; + +/// An adapter for the registration of platform drivers. +pub struct Adapter(T); + +impl driver::RegistrationOps for Adapter { + type RegType = bindings::platform_driver; + + fn register( + pdrv: &Opaque, + name: &'static CStr, + module: &'static ThisModule, + ) -> Result { + let of_table = match T::OF_ID_TABLE { + Some(table) => table.as_ptr(), + None => core::ptr::null(), + }; + + // SAFETY: It's safe to set the fields of `struct platform_driver` on initialization. + unsafe { + (*pdrv.get()).driver.name = name.as_char_ptr(); + (*pdrv.get()).probe = Some(Self::probe_callback); + (*pdrv.get()).remove = Some(Self::remove_callback); + (*pdrv.get()).driver.of_match_table = of_table; + } + + // SAFETY: `pdrv` is guaranteed to be a valid `RegType`. + to_result(unsafe { bindings::__platform_driver_register(pdrv.get(), module.0) }) + } + + fn unregister(pdrv: &Opaque) { + // SAFETY: `pdrv` is guaranteed to be a valid `RegType`. + unsafe { bindings::platform_driver_unregister(pdrv.get()) }; + } +} + +impl Adapter { + extern "C" fn probe_callback(pdev: *mut bindings::platform_device) -> kernel::ffi::c_int { + // SAFETY: The platform bus only ever calls the probe callback with a valid `pdev`. + let dev = unsafe { device::Device::get_device(addr_of_mut!((*pdev).dev)) }; + // SAFETY: `dev` is guaranteed to be embedded in a valid `struct platform_device` by the + // call above. + let mut pdev = unsafe { Device::from_dev(dev) }; + + let info = ::id_info(pdev.as_ref()); + match T::probe(&mut pdev, info) { + Ok(data) => { + // Let the `struct platform_device` own a reference of the driver's private data. + // SAFETY: By the type invariant `pdev.as_raw` returns a valid pointer to a + // `struct platform_device`. + unsafe { bindings::platform_set_drvdata(pdev.as_raw(), data.into_foreign() as _) }; + } + Err(err) => return Error::to_errno(err), + } + + 0 + } + + extern "C" fn remove_callback(pdev: *mut bindings::platform_device) { + // SAFETY: `pdev` is a valid pointer to a `struct platform_device`. + let ptr = unsafe { bindings::platform_get_drvdata(pdev) }; + + // SAFETY: `remove_callback` is only ever called after a successful call to + // `probe_callback`, hence it's guaranteed that `ptr` points to a valid and initialized + // `KBox` pointer created through `KBox::into_foreign`. + let _ = unsafe { KBox::::from_foreign(ptr) }; + } +} + +impl driver::Adapter for Adapter { + type IdInfo = T::IdInfo; + + fn of_id_table() -> Option> { + T::OF_ID_TABLE + } +} + +/// Declares a kernel module that exposes a single platform driver. +/// +/// # Examples +/// +/// ```ignore +/// kernel::module_platform_driver! { +/// type: MyDriver, +/// name: "Module name", +/// author: "Author name", +/// description: "Description", +/// license: "GPL v2", +/// } +/// ``` +#[macro_export] +macro_rules! module_platform_driver { + ($($f:tt)*) => { + $crate::module_driver!(, $crate::platform::Adapter, { $($f)* }); + }; +} + +/// The platform driver trait. +/// +/// Drivers must implement this trait in order to get a platform driver registered. +/// +/// # Example +/// +///``` +/// # use kernel::{bindings, c_str, of, platform}; +/// +/// struct MyDriver; +/// +/// kernel::of_device_table!( +/// OF_TABLE, +/// MODULE_OF_TABLE, +/// ::IdInfo, +/// [ +/// (of::DeviceId::new(c_str!("test,device")), ()) +/// ] +/// ); +/// +/// impl platform::Driver for MyDriver { +/// type IdInfo = (); +/// const OF_ID_TABLE: Option> = Some(&OF_TABLE); +/// +/// fn probe( +/// _pdev: &mut platform::Device, +/// _id_info: Option<&Self::IdInfo>, +/// ) -> Result>> { +/// Err(ENODEV) +/// } +/// } +///``` +pub trait Driver { + /// The type holding driver private data about each device id supported by the driver. + /// + /// TODO: Use associated_type_defaults once stabilized: + /// + /// type IdInfo: 'static = (); + type IdInfo: 'static; + + /// The table of OF device ids supported by the driver. + const OF_ID_TABLE: Option>; + + /// Platform driver probe. + /// + /// Called when a new platform device is added or discovered. + /// Implementers should attempt to initialize the device here. + fn probe(dev: &mut Device, id_info: Option<&Self::IdInfo>) -> Result>>; +} + +/// The platform device representation. +/// +/// A platform device is based on an always reference counted `device:Device` instance. Cloning a +/// platform device, hence, also increments the base device' reference count. +/// +/// # Invariants +/// +/// `Device` holds a valid reference of `ARef` whose underlying `struct device` is a +/// member of a `struct platform_device`. +#[derive(Clone)] +pub struct Device(ARef); + +impl Device { + /// Convert a raw kernel device into a `Device` + /// + /// # Safety + /// + /// `dev` must be an `Aref` whose underlying `bindings::device` is a member of a + /// `bindings::platform_device`. + unsafe fn from_dev(dev: ARef) -> Self { + Self(dev) + } + + fn as_raw(&self) -> *mut bindings::platform_device { + // SAFETY: By the type invariant `self.0.as_raw` is a pointer to the `struct device` + // embedded in `struct platform_device`. + unsafe { container_of!(self.0.as_raw(), bindings::platform_device, dev) }.cast_mut() + } +} + +impl AsRef for Device { + fn as_ref(&self) -> &device::Device { + &self.0 + } +} From b2e8a83242c0b8d5d382a1aeceed18aa9bcb9a00 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Thu, 19 Dec 2024 18:04:17 +0100 Subject: [PATCH 24/68] samples: rust: add Rust platform sample driver Add a sample Rust platform driver illustrating the usage of the platform bus abstractions. This driver probes through either a match of device / driver name or a match within the OF ID table. Reviewed-by: Rob Herring (Arm) Signed-off-by: Danilo Krummrich Tested-by: Dirk Behme Tested-by: Fabien Parent Link: https://lore.kernel.org/r/20241219170425.12036-16-dakr@kernel.org Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 1 + drivers/of/unittest-data/tests-platform.dtsi | 5 ++ samples/rust/Kconfig | 10 ++++ samples/rust/Makefile | 1 + samples/rust/rust_driver_platform.rs | 49 ++++++++++++++++++++ 5 files changed, 66 insertions(+) create mode 100644 samples/rust/rust_driver_platform.rs diff --git a/MAINTAINERS b/MAINTAINERS index 0da80deb14b5..c9673877f7f8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7038,6 +7038,7 @@ F: rust/kernel/device_id.rs F: rust/kernel/devres.rs F: rust/kernel/driver.rs F: rust/kernel/platform.rs +F: samples/rust/rust_driver_platform.rs DRIVERS FOR OMAP ADAPTIVE VOLTAGE SCALING (AVS) M: Nishanth Menon diff --git a/drivers/of/unittest-data/tests-platform.dtsi b/drivers/of/unittest-data/tests-platform.dtsi index fa39611071b3..2caaf1c10ee6 100644 --- a/drivers/of/unittest-data/tests-platform.dtsi +++ b/drivers/of/unittest-data/tests-platform.dtsi @@ -33,6 +33,11 @@ reg = <0x100>; }; }; + + test-device@2 { + compatible = "test,rust-device"; + reg = <0x2>; + }; }; }; }; diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig index 002af98e70be..918dbead2c0b 100644 --- a/samples/rust/Kconfig +++ b/samples/rust/Kconfig @@ -51,6 +51,16 @@ config SAMPLE_RUST_DRIVER_PCI If unsure, say N. +config SAMPLE_RUST_DRIVER_PLATFORM + tristate "Platform Driver" + help + This option builds the Rust Platform driver sample. + + To compile this as a module, choose M here: + the module will be called rust_driver_platform. + + If unsure, say N. + config SAMPLE_RUST_HOSTPROGS bool "Host programs" help diff --git a/samples/rust/Makefile b/samples/rust/Makefile index b4581473ca1b..5a8ab0df0567 100644 --- a/samples/rust/Makefile +++ b/samples/rust/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_SAMPLE_RUST_MINIMAL) += rust_minimal.o obj-$(CONFIG_SAMPLE_RUST_MISC_DEVICE) += rust_misc_device.o obj-$(CONFIG_SAMPLE_RUST_PRINT) += rust_print.o obj-$(CONFIG_SAMPLE_RUST_DRIVER_PCI) += rust_driver_pci.o +obj-$(CONFIG_SAMPLE_RUST_DRIVER_PLATFORM) += rust_driver_platform.o rust_print-y := rust_print_main.o rust_print_events.o diff --git a/samples/rust/rust_driver_platform.rs b/samples/rust/rust_driver_platform.rs new file mode 100644 index 000000000000..8120609e2940 --- /dev/null +++ b/samples/rust/rust_driver_platform.rs @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Rust Platform driver sample. + +use kernel::{c_str, of, platform, prelude::*}; + +struct SampleDriver { + pdev: platform::Device, +} + +struct Info(u32); + +kernel::of_device_table!( + OF_TABLE, + MODULE_OF_TABLE, + ::IdInfo, + [(of::DeviceId::new(c_str!("test,rust-device")), Info(42))] +); + +impl platform::Driver for SampleDriver { + type IdInfo = Info; + const OF_ID_TABLE: Option> = Some(&OF_TABLE); + + fn probe(pdev: &mut platform::Device, info: Option<&Self::IdInfo>) -> Result>> { + dev_dbg!(pdev.as_ref(), "Probe Rust Platform driver sample.\n"); + + if let Some(info) = info { + dev_info!(pdev.as_ref(), "Probed with info: '{}'.\n", info.0); + } + + let drvdata = KBox::new(Self { pdev: pdev.clone() }, GFP_KERNEL)?; + + Ok(drvdata.into()) + } +} + +impl Drop for SampleDriver { + fn drop(&mut self) { + dev_dbg!(self.pdev.as_ref(), "Remove Rust Platform driver sample.\n"); + } +} + +kernel::module_platform_driver! { + type: SampleDriver, + name: "rust_driver_platform", + author: "Danilo Krummrich", + description: "Rust Platform driver", + license: "GPL v2", +} From e62fedef0aa51134b6848951dcd007fd9338705a Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Thu, 19 Dec 2024 18:04:18 +0100 Subject: [PATCH 25/68] MAINTAINERS: add Danilo to DRIVER CORE Let's keep an eye on the Rust abstractions; add myself as reviewer to DRIVER CORE. Signed-off-by: Danilo Krummrich Tested-by: Dirk Behme Tested-by: Fabien Parent Link: https://lore.kernel.org/r/20241219170425.12036-17-dakr@kernel.org Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index c9673877f7f8..3d33859ba957 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7022,6 +7022,7 @@ F: include/linux/component.h DRIVER CORE, KOBJECTS, DEBUGFS AND SYSFS M: Greg Kroah-Hartman R: "Rafael J. Wysocki" +R: Danilo Krummrich S: Supported T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core.git F: Documentation/core-api/kobject.rst From 5ab5a3778dd1745bafa426e5d6f7c71bf9e14d84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Sat, 21 Dec 2024 15:09:34 +0100 Subject: [PATCH 26/68] kheaders: Simplify attribute through __BIN_ATTR_SIMPLE_RO() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The utility macro from the sysfs core is sufficient to implement this attribute. Make use of it. Signed-off-by: Thomas Weißschuh Link: https://lore.kernel.org/r/20241221-sysfs-const-bin_attr-kheaders-v2-1-8205538aa012@weissschuh.net Signed-off-by: Greg Kroah-Hartman --- kernel/kheaders.c | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/kernel/kheaders.c b/kernel/kheaders.c index 42163c9e94e5..378088b07f46 100644 --- a/kernel/kheaders.c +++ b/kernel/kheaders.c @@ -29,25 +29,12 @@ asm ( extern char kernel_headers_data[]; extern char kernel_headers_data_end[]; -static ssize_t -ikheaders_read(struct file *file, struct kobject *kobj, - struct bin_attribute *bin_attr, - char *buf, loff_t off, size_t len) -{ - memcpy(buf, &kernel_headers_data[off], len); - return len; -} - -static struct bin_attribute kheaders_attr __ro_after_init = { - .attr = { - .name = "kheaders.tar.xz", - .mode = 0444, - }, - .read = &ikheaders_read, -}; +static struct bin_attribute kheaders_attr __ro_after_init = + __BIN_ATTR_SIMPLE_RO(kheaders.tar.xz, 0444); static int __init ikheaders_init(void) { + kheaders_attr.private = kernel_headers_data; kheaders_attr.size = (kernel_headers_data_end - kernel_headers_data); return sysfs_create_bin_file(kernel_kobj, &kheaders_attr); From 1b1bb7b29b1052e4124c0f99eff65200ef141caf Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Mon, 16 Dec 2024 12:11:42 -0800 Subject: [PATCH 27/68] drivers: base: Don't match devices with NULL of_node/fwnode/etc of_find_device_by_node(), bus_find_device_by_of_node(), bus_find_device_by_fwnode(), ..., all produce arbitrary results when provided with a NULL of_node, fwnode, ACPI handle, etc. This is counterintuitive, and the source of a few bugs, such as the one fixed by commit 5c8418cf4025 ("PCI/pwrctrl: Unregister platform device only if one actually exists"). It's hard to imagine a good reason that these device_match_*() APIs should return 'true' for a NULL argument. Augment these to return 0 (false). Reviewed-by: Rob Herring (Arm) Acked-by: Rafael J. Wysocki Signed-off-by: Brian Norris Acked-by: David Gow Acked-by: Shuah Khan Link: https://lore.kernel.org/r/20241216201148.535115-2-briannorris@chromium.org Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/base/core.c b/drivers/base/core.c index 94865c9d8adc..2b7b13fc36d7 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -5246,13 +5246,13 @@ EXPORT_SYMBOL_GPL(device_match_name); int device_match_of_node(struct device *dev, const void *np) { - return dev->of_node == np; + return np && dev->of_node == np; } EXPORT_SYMBOL_GPL(device_match_of_node); int device_match_fwnode(struct device *dev, const void *fwnode) { - return dev_fwnode(dev) == fwnode; + return fwnode && dev_fwnode(dev) == fwnode; } EXPORT_SYMBOL_GPL(device_match_fwnode); @@ -5264,13 +5264,13 @@ EXPORT_SYMBOL_GPL(device_match_devt); int device_match_acpi_dev(struct device *dev, const void *adev) { - return ACPI_COMPANION(dev) == adev; + return adev && ACPI_COMPANION(dev) == adev; } EXPORT_SYMBOL(device_match_acpi_dev); int device_match_acpi_handle(struct device *dev, const void *handle) { - return ACPI_HANDLE(dev) == handle; + return handle && ACPI_HANDLE(dev) == handle; } EXPORT_SYMBOL(device_match_acpi_handle); From 55b7aee990ef786251e0e37f8285c32b4193f419 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Mon, 16 Dec 2024 12:11:43 -0800 Subject: [PATCH 28/68] drivers: base: test: Enable device model tests with KUNIT_ALL_TESTS Per commit bebe94b53eb7 ("drivers: base: default KUNIT_* fragments to KUNIT_ALL_TESTS"), it seems like we should default to KUNIT_ALL_TESTS. This enables these platform_device tests for common configurations, such as with: ./tools/testing/kunit/kunit.py run Signed-off-by: Brian Norris Reviewed-by: David Gow Reviewed-by: Maxime Ripard Reviewed-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20241216201148.535115-3-briannorris@chromium.org Signed-off-by: Greg Kroah-Hartman --- drivers/base/test/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/base/test/Kconfig b/drivers/base/test/Kconfig index 5c7fac80611c..2756870615cc 100644 --- a/drivers/base/test/Kconfig +++ b/drivers/base/test/Kconfig @@ -12,6 +12,7 @@ config TEST_ASYNC_DRIVER_PROBE config DM_KUNIT_TEST tristate "KUnit Tests for the device model" if !KUNIT_ALL_TESTS depends on KUNIT + default KUNIT_ALL_TESTS config DRIVER_PE_KUNIT_TEST tristate "KUnit Tests for property entry API" if !KUNIT_ALL_TESTS From 86a5f32ed8813d5534d3d6c77aad29184a9f99b6 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Mon, 16 Dec 2024 12:11:44 -0800 Subject: [PATCH 29/68] drivers: base: test: Add ...find_device_by...(... NULL) tests We recently updated these device_match*() (and therefore, various *find_device_by*()) functions to return a consistent 'false' value when trying to match a NULL handle. Add tests for this. This provides regression-testing coverage for the sorts of bugs that underly commit 5c8418cf4025 ("PCI/pwrctrl: Unregister platform device only if one actually exists"). Reviewed-by: Maxime Ripard Signed-off-by: Brian Norris Acked-by: Shuah Khan Reviewed-by: David Gow Link: https://lore.kernel.org/r/20241216201148.535115-4-briannorris@chromium.org Signed-off-by: Greg Kroah-Hartman --- drivers/base/test/platform-device-test.c | 41 +++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/drivers/base/test/platform-device-test.c b/drivers/base/test/platform-device-test.c index ea05b8785743..6355a2231b74 100644 --- a/drivers/base/test/platform-device-test.c +++ b/drivers/base/test/platform-device-test.c @@ -1,8 +1,11 @@ // SPDX-License-Identifier: GPL-2.0 +#include #include #include +#include +#include #include #define DEVICE_NAME "test" @@ -217,7 +220,43 @@ static struct kunit_suite platform_device_devm_test_suite = { .test_cases = platform_device_devm_tests, }; -kunit_test_suite(platform_device_devm_test_suite); +static void platform_device_find_by_null_test(struct kunit *test) +{ + struct platform_device *pdev; + int ret; + + pdev = kunit_platform_device_alloc(test, DEVICE_NAME, PLATFORM_DEVID_NONE); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev); + + ret = kunit_platform_device_add(test, pdev); + KUNIT_ASSERT_EQ(test, ret, 0); + + KUNIT_EXPECT_PTR_EQ(test, of_find_device_by_node(NULL), NULL); + + KUNIT_EXPECT_PTR_EQ(test, bus_find_device_by_of_node(&platform_bus_type, NULL), NULL); + KUNIT_EXPECT_PTR_EQ(test, bus_find_device_by_fwnode(&platform_bus_type, NULL), NULL); + KUNIT_EXPECT_PTR_EQ(test, bus_find_device_by_acpi_dev(&platform_bus_type, NULL), NULL); + + KUNIT_EXPECT_FALSE(test, device_match_of_node(&pdev->dev, NULL)); + KUNIT_EXPECT_FALSE(test, device_match_fwnode(&pdev->dev, NULL)); + KUNIT_EXPECT_FALSE(test, device_match_acpi_dev(&pdev->dev, NULL)); + KUNIT_EXPECT_FALSE(test, device_match_acpi_handle(&pdev->dev, NULL)); +} + +static struct kunit_case platform_device_match_tests[] = { + KUNIT_CASE(platform_device_find_by_null_test), + {} +}; + +static struct kunit_suite platform_device_match_test_suite = { + .name = "platform-device-match", + .test_cases = platform_device_match_tests, +}; + +kunit_test_suites( + &platform_device_devm_test_suite, + &platform_device_match_test_suite, +); MODULE_DESCRIPTION("Test module for platform devices"); MODULE_AUTHOR("Maxime Ripard "); From a7512bda7c176553536a5775fa9540223eef3c1f Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Tue, 24 Dec 2024 21:05:00 +0800 Subject: [PATCH 30/68] libnvdimm: Replace namespace_match() with device_find_child_by_name() Simplify nd_namespace_store() implementation by using device_find_child_by_name(). Reviewed-by: Alison Schofield Reviewed-by: Jonathan Cameron Signed-off-by: Zijun Hu Link: https://lore.kernel.org/r/20241224-const_dfc_done-v5-1-6623037414d4@quicinc.com Signed-off-by: Greg Kroah-Hartman --- drivers/nvdimm/claim.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/drivers/nvdimm/claim.c b/drivers/nvdimm/claim.c index 030dbde6b088..9e84ab411564 100644 --- a/drivers/nvdimm/claim.c +++ b/drivers/nvdimm/claim.c @@ -67,13 +67,6 @@ bool nd_attach_ndns(struct device *dev, struct nd_namespace_common *attach, return claimed; } -static int namespace_match(struct device *dev, void *data) -{ - char *name = data; - - return strcmp(name, dev_name(dev)) == 0; -} - static bool is_idle(struct device *dev, struct nd_namespace_common *ndns) { struct nd_region *nd_region = to_nd_region(dev->parent); @@ -168,7 +161,7 @@ ssize_t nd_namespace_store(struct device *dev, goto out; } - found = device_find_child(dev->parent, name, namespace_match); + found = device_find_child_by_name(dev->parent, name); if (!found) { dev_dbg(dev, "'%s' not found under %s\n", name, dev_name(dev->parent)); From 064aa528bbc5da6530283a761fbb905d52b04916 Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Tue, 24 Dec 2024 21:05:01 +0800 Subject: [PATCH 31/68] slimbus: core: Constify slim_eaddr_equal() bool slim_eaddr_equal(struct slim_eaddr *a, struct slim_eaddr *b) does not modify @*a or @*b. To prepare for constifying API device_find_child() later. Constify this comparison function by simply changing its parameter type to 'const struct slim_eaddr *'. Reviewed-by: Jonathan Cameron Signed-off-by: Zijun Hu Link: https://lore.kernel.org/r/20241224-const_dfc_done-v5-2-6623037414d4@quicinc.com Signed-off-by: Greg Kroah-Hartman --- drivers/slimbus/core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/slimbus/core.c b/drivers/slimbus/core.c index 65e5515f7555..b5d5bbb9fdb6 100644 --- a/drivers/slimbus/core.c +++ b/drivers/slimbus/core.c @@ -328,7 +328,8 @@ void slim_report_absent(struct slim_device *sbdev) } EXPORT_SYMBOL_GPL(slim_report_absent); -static bool slim_eaddr_equal(struct slim_eaddr *a, struct slim_eaddr *b) +static bool slim_eaddr_equal(const struct slim_eaddr *a, + const struct slim_eaddr *b) { return (a->manf_id == b->manf_id && a->prod_code == b->prod_code && From e9451ab968bb34f2b37fdd91c376d5f36c4745ee Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Tue, 24 Dec 2024 21:05:02 +0800 Subject: [PATCH 32/68] bus: fsl-mc: Constify fsl_mc_device_match() fsl_mc_device_match() does not modify caller's inputs. To prepare for constifying API device_find_child() later. Constify this comparison function by simply changing its parameter types to const pointer. Reviewed-by: Jonathan Cameron Signed-off-by: Zijun Hu Link: https://lore.kernel.org/r/20241224-const_dfc_done-v5-3-6623037414d4@quicinc.com Signed-off-by: Greg Kroah-Hartman --- drivers/bus/fsl-mc/dprc-driver.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/bus/fsl-mc/dprc-driver.c b/drivers/bus/fsl-mc/dprc-driver.c index 4b68c84ef485..11c8fadcf851 100644 --- a/drivers/bus/fsl-mc/dprc-driver.c +++ b/drivers/bus/fsl-mc/dprc-driver.c @@ -22,8 +22,8 @@ struct fsl_mc_child_objs { struct fsl_mc_obj_desc *child_array; }; -static bool fsl_mc_device_match(struct fsl_mc_device *mc_dev, - struct fsl_mc_obj_desc *obj_desc) +static bool fsl_mc_device_match(const struct fsl_mc_device *mc_dev, + const struct fsl_mc_obj_desc *obj_desc) { return mc_dev->obj_desc.id == obj_desc->id && strcmp(mc_dev->obj_desc.type, obj_desc->type) == 0; From f1e8bf56320a7fb32095b6c51b707459361b403b Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Tue, 24 Dec 2024 21:05:03 +0800 Subject: [PATCH 33/68] driver core: Constify API device_find_child() and adapt for various usages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Constify the following API: struct device *device_find_child(struct device *dev, void *data, int (*match)(struct device *dev, void *data)); To : struct device *device_find_child(struct device *dev, const void *data, device_match_t match); typedef int (*device_match_t)(struct device *dev, const void *data); with the following reasons: - Protect caller's match data @*data which is for comparison and lookup and the API does not actually need to modify @*data. - Make the API's parameters (@match)() and @data have the same type as all of other device finding APIs (bus|class|driver)_find_device(). - All kinds of existing device match functions can be directly taken as the API's argument, they were exported by driver core. Constify the API and adapt for various existing usages. BTW, various subsystem changes are squashed into this commit to meet 'git bisect' requirement, and this commit has the minimal and simplest changes to complement squashing shortcoming, and that may bring extra code improvement. Reviewed-by: Alison Schofield Reviewed-by: Takashi Sakamoto Acked-by: Uwe Kleine-König # for drivers/pwm Signed-off-by: Zijun Hu Reviewed-by: Jonathan Cameron Reviewed-by: Mathieu Poirier Link: https://lore.kernel.org/r/20241224-const_dfc_done-v5-4-6623037414d4@quicinc.com Signed-off-by: Greg Kroah-Hartman --- arch/sparc/kernel/vio.c | 6 +++--- drivers/base/core.c | 6 +++--- drivers/block/sunvdc.c | 6 +++--- drivers/bus/fsl-mc/dprc-driver.c | 4 ++-- drivers/cxl/core/pci.c | 4 ++-- drivers/cxl/core/pmem.c | 2 +- drivers/cxl/core/region.c | 21 ++++++++++++--------- drivers/firewire/core-device.c | 4 ++-- drivers/firmware/arm_scmi/bus.c | 4 ++-- drivers/firmware/efi/dev-path-parser.c | 4 ++-- drivers/gpio/gpio-sim.c | 2 +- drivers/gpu/drm/mediatek/mtk_drm_drv.c | 2 +- drivers/hwmon/hwmon.c | 2 +- drivers/media/pci/mgb4/mgb4_core.c | 4 ++-- drivers/nvdimm/bus.c | 2 +- drivers/pwm/core.c | 2 +- drivers/rpmsg/rpmsg_core.c | 4 ++-- drivers/scsi/qla4xxx/ql4_os.c | 3 ++- drivers/scsi/scsi_transport_iscsi.c | 10 +++++----- drivers/slimbus/core.c | 8 ++++---- drivers/thunderbolt/retimer.c | 2 +- drivers/thunderbolt/xdomain.c | 2 +- drivers/tty/serial/serial_core.c | 4 ++-- drivers/usb/typec/class.c | 8 ++++---- include/linux/device.h | 4 ++-- include/scsi/scsi_transport_iscsi.h | 4 ++-- net/dsa/dsa.c | 2 +- tools/testing/cxl/test/cxl.c | 2 +- 28 files changed, 66 insertions(+), 62 deletions(-) diff --git a/arch/sparc/kernel/vio.c b/arch/sparc/kernel/vio.c index 07933d75ac81..1a1a9d6b8f2e 100644 --- a/arch/sparc/kernel/vio.c +++ b/arch/sparc/kernel/vio.c @@ -419,13 +419,13 @@ struct vio_remove_node_data { u64 node; }; -static int vio_md_node_match(struct device *dev, void *arg) +static int vio_md_node_match(struct device *dev, const void *arg) { struct vio_dev *vdev = to_vio_dev(dev); - struct vio_remove_node_data *node_data; + const struct vio_remove_node_data *node_data; u64 node; - node_data = (struct vio_remove_node_data *)arg; + node_data = (const struct vio_remove_node_data *)arg; node = vio_vdev_node(node_data->hp, vdev); diff --git a/drivers/base/core.c b/drivers/base/core.c index 2b7b13fc36d7..b242993f816d 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -4079,8 +4079,8 @@ EXPORT_SYMBOL_GPL(device_for_each_child_reverse_from); * * NOTE: you will need to drop the reference with put_device() after use. */ -struct device *device_find_child(struct device *parent, void *data, - int (*match)(struct device *dev, void *data)) +struct device *device_find_child(struct device *parent, const void *data, + device_match_t match) { struct klist_iter i; struct device *child; @@ -4125,7 +4125,7 @@ struct device *device_find_child_by_name(struct device *parent, } EXPORT_SYMBOL_GPL(device_find_child_by_name); -static int match_any(struct device *dev, void *unused) +static int match_any(struct device *dev, const void *unused) { return 1; } diff --git a/drivers/block/sunvdc.c b/drivers/block/sunvdc.c index 2d38331ee667..386643ceed59 100644 --- a/drivers/block/sunvdc.c +++ b/drivers/block/sunvdc.c @@ -918,12 +918,12 @@ struct vdc_check_port_data { char *type; }; -static int vdc_device_probed(struct device *dev, void *arg) +static int vdc_device_probed(struct device *dev, const void *arg) { struct vio_dev *vdev = to_vio_dev(dev); - struct vdc_check_port_data *port_data; + const struct vdc_check_port_data *port_data; - port_data = (struct vdc_check_port_data *)arg; + port_data = (const struct vdc_check_port_data *)arg; if ((vdev->dev_no == port_data->dev_no) && (!(strcmp((char *)&vdev->type, port_data->type))) && diff --git a/drivers/bus/fsl-mc/dprc-driver.c b/drivers/bus/fsl-mc/dprc-driver.c index 11c8fadcf851..52053f7c6d9a 100644 --- a/drivers/bus/fsl-mc/dprc-driver.c +++ b/drivers/bus/fsl-mc/dprc-driver.c @@ -112,9 +112,9 @@ void dprc_remove_devices(struct fsl_mc_device *mc_bus_dev, } EXPORT_SYMBOL_GPL(dprc_remove_devices); -static int __fsl_mc_device_match(struct device *dev, void *data) +static int __fsl_mc_device_match(struct device *dev, const void *data) { - struct fsl_mc_obj_desc *obj_desc = data; + const struct fsl_mc_obj_desc *obj_desc = data; struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); return fsl_mc_device_match(mc_dev, obj_desc); diff --git a/drivers/cxl/core/pci.c b/drivers/cxl/core/pci.c index 9d58ab9d33c5..a3c57f96138a 100644 --- a/drivers/cxl/core/pci.c +++ b/drivers/cxl/core/pci.c @@ -252,9 +252,9 @@ static int devm_cxl_enable_mem(struct device *host, struct cxl_dev_state *cxlds) } /* require dvsec ranges to be covered by a locked platform window */ -static int dvsec_range_allowed(struct device *dev, void *arg) +static int dvsec_range_allowed(struct device *dev, const void *arg) { - struct range *dev_range = arg; + const struct range *dev_range = arg; struct cxl_decoder *cxld; if (!is_root_decoder(dev)) diff --git a/drivers/cxl/core/pmem.c b/drivers/cxl/core/pmem.c index b3378d3f6acb..a8473de24ebf 100644 --- a/drivers/cxl/core/pmem.c +++ b/drivers/cxl/core/pmem.c @@ -57,7 +57,7 @@ bool is_cxl_nvdimm_bridge(struct device *dev) } EXPORT_SYMBOL_NS_GPL(is_cxl_nvdimm_bridge, "CXL"); -static int match_nvdimm_bridge(struct device *dev, void *data) +static int match_nvdimm_bridge(struct device *dev, const void *data) { return is_cxl_nvdimm_bridge(dev); } diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c index d77899650798..bfecd71040c2 100644 --- a/drivers/cxl/core/region.c +++ b/drivers/cxl/core/region.c @@ -792,7 +792,7 @@ static int check_commit_order(struct device *dev, const void *data) return 0; } -static int match_free_decoder(struct device *dev, void *data) +static int match_free_decoder(struct device *dev, const void *data) { struct cxl_port *port = to_cxl_port(dev->parent); struct cxl_decoder *cxld; @@ -824,9 +824,9 @@ static int match_free_decoder(struct device *dev, void *data) return 1; } -static int match_auto_decoder(struct device *dev, void *data) +static int match_auto_decoder(struct device *dev, const void *data) { - struct cxl_region_params *p = data; + const struct cxl_region_params *p = data; struct cxl_decoder *cxld; struct range *r; @@ -1722,10 +1722,12 @@ static struct cxl_port *next_port(struct cxl_port *port) return port->parent_dport->port; } -static int match_switch_decoder_by_range(struct device *dev, void *data) +static int match_switch_decoder_by_range(struct device *dev, + const void *data) { struct cxl_switch_decoder *cxlsd; - struct range *r1, *r2 = data; + const struct range *r1, *r2 = data; + if (!is_switch_decoder(dev)) return 0; @@ -3176,9 +3178,10 @@ err: return rc; } -static int match_root_decoder_by_range(struct device *dev, void *data) +static int match_root_decoder_by_range(struct device *dev, + const void *data) { - struct range *r1, *r2 = data; + const struct range *r1, *r2 = data; struct cxl_root_decoder *cxlrd; if (!is_root_decoder(dev)) @@ -3189,11 +3192,11 @@ static int match_root_decoder_by_range(struct device *dev, void *data) return range_contains(r1, r2); } -static int match_region_by_range(struct device *dev, void *data) +static int match_region_by_range(struct device *dev, const void *data) { struct cxl_region_params *p; struct cxl_region *cxlr; - struct range *r = data; + const struct range *r = data; int rc = 0; if (!is_cxl_region(dev)) diff --git a/drivers/firewire/core-device.c b/drivers/firewire/core-device.c index a99fe35f1f0d..ec3e21ad2025 100644 --- a/drivers/firewire/core-device.c +++ b/drivers/firewire/core-device.c @@ -988,7 +988,7 @@ int fw_device_set_broadcast_channel(struct device *dev, void *gen) return 0; } -static int compare_configuration_rom(struct device *dev, void *data) +static int compare_configuration_rom(struct device *dev, const void *data) { const struct fw_device *old = fw_device(dev); const u32 *config_rom = data; @@ -1039,7 +1039,7 @@ static void fw_device_init(struct work_struct *work) // // serialize config_rom access. scoped_guard(rwsem_read, &fw_device_rwsem) { - found = device_find_child(card->device, (void *)device->config_rom, + found = device_find_child(card->device, device->config_rom, compare_configuration_rom); } if (found) { diff --git a/drivers/firmware/arm_scmi/bus.c b/drivers/firmware/arm_scmi/bus.c index 157172a5f2b5..a3386bf36de5 100644 --- a/drivers/firmware/arm_scmi/bus.c +++ b/drivers/firmware/arm_scmi/bus.c @@ -238,10 +238,10 @@ static int scmi_dev_match(struct device *dev, const struct device_driver *drv) return 0; } -static int scmi_match_by_id_table(struct device *dev, void *data) +static int scmi_match_by_id_table(struct device *dev, const void *data) { struct scmi_device *sdev = to_scmi_dev(dev); - struct scmi_device_id *id_table = data; + const struct scmi_device_id *id_table = data; return sdev->protocol_id == id_table->protocol_id && (id_table->name && !strcmp(sdev->name, id_table->name)); diff --git a/drivers/firmware/efi/dev-path-parser.c b/drivers/firmware/efi/dev-path-parser.c index 937be269fee8..13ea141c0def 100644 --- a/drivers/firmware/efi/dev-path-parser.c +++ b/drivers/firmware/efi/dev-path-parser.c @@ -47,9 +47,9 @@ static long __init parse_acpi_path(const struct efi_dev_path *node, return 0; } -static int __init match_pci_dev(struct device *dev, void *data) +static int __init match_pci_dev(struct device *dev, const void *data) { - unsigned int devfn = *(unsigned int *)data; + unsigned int devfn = *(const unsigned int *)data; return dev_is_pci(dev) && to_pci_dev(dev)->devfn == devfn; } diff --git a/drivers/gpio/gpio-sim.c b/drivers/gpio/gpio-sim.c index f387dad81f29..370b71513bdb 100644 --- a/drivers/gpio/gpio-sim.c +++ b/drivers/gpio/gpio-sim.c @@ -413,7 +413,7 @@ static int gpio_sim_setup_sysfs(struct gpio_sim_chip *chip) return devm_add_action_or_reset(dev, gpio_sim_sysfs_remove, chip); } -static int gpio_sim_dev_match_fwnode(struct device *dev, void *data) +static int gpio_sim_dev_match_fwnode(struct device *dev, const void *data) { return device_match_fwnode(dev, data); } diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c index 0829ceb9967c..4aeb393b58e6 100644 --- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c +++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c @@ -359,7 +359,7 @@ static const struct of_device_id mtk_drm_of_ids[] = { }; MODULE_DEVICE_TABLE(of, mtk_drm_of_ids); -static int mtk_drm_match(struct device *dev, void *data) +static int mtk_drm_match(struct device *dev, const void *data) { if (!strncmp(dev_name(dev), "mediatek-drm", sizeof("mediatek-drm") - 1)) return true; diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c index bbb9cc44e29f..6552ee518689 100644 --- a/drivers/hwmon/hwmon.c +++ b/drivers/hwmon/hwmon.c @@ -341,7 +341,7 @@ static int hwmon_attr_base(enum hwmon_sensor_types type) static DEFINE_MUTEX(hwmon_pec_mutex); -static int hwmon_match_device(struct device *dev, void *data) +static int hwmon_match_device(struct device *dev, const void *data) { return dev->class == &hwmon_class; } diff --git a/drivers/media/pci/mgb4/mgb4_core.c b/drivers/media/pci/mgb4/mgb4_core.c index bc63dc81bcae..697d50bedfe2 100644 --- a/drivers/media/pci/mgb4/mgb4_core.c +++ b/drivers/media/pci/mgb4/mgb4_core.c @@ -123,7 +123,7 @@ static const struct hwmon_chip_info temp_chip_info = { }; #endif -static int match_i2c_adap(struct device *dev, void *data) +static int match_i2c_adap(struct device *dev, const void *data) { return i2c_verify_adapter(dev) ? 1 : 0; } @@ -139,7 +139,7 @@ static struct i2c_adapter *get_i2c_adap(struct platform_device *pdev) return dev ? to_i2c_adapter(dev) : NULL; } -static int match_spi_adap(struct device *dev, void *data) +static int match_spi_adap(struct device *dev, const void *data) { return to_spi_device(dev) ? 1 : 0; } diff --git a/drivers/nvdimm/bus.c b/drivers/nvdimm/bus.c index 2237715e42eb..0ccf4a9e523a 100644 --- a/drivers/nvdimm/bus.c +++ b/drivers/nvdimm/bus.c @@ -1212,7 +1212,7 @@ enum nd_ioctl_mode { DIMM_IOCTL, }; -static int match_dimm(struct device *dev, void *data) +static int match_dimm(struct device *dev, const void *data) { long id = (long) data; diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index 675b252d9c8c..14144d0fa38e 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -1276,7 +1276,7 @@ static int pwm_export_child(struct device *pwmchip_dev, struct pwm_device *pwm) return 0; } -static int pwm_unexport_match(struct device *pwm_dev, void *data) +static int pwm_unexport_match(struct device *pwm_dev, const void *data) { return pwm_from_dev(pwm_dev) == data; } diff --git a/drivers/rpmsg/rpmsg_core.c b/drivers/rpmsg/rpmsg_core.c index 712c06c02696..207b64c0a2fe 100644 --- a/drivers/rpmsg/rpmsg_core.c +++ b/drivers/rpmsg/rpmsg_core.c @@ -377,9 +377,9 @@ EXPORT_SYMBOL(rpmsg_get_mtu); * this is used to make sure we're not creating rpmsg devices for channels * that already exist. */ -static int rpmsg_device_match(struct device *dev, void *data) +static int rpmsg_device_match(struct device *dev, const void *data) { - struct rpmsg_channel_info *chinfo = data; + const struct rpmsg_channel_info *chinfo = data; struct rpmsg_device *rpdev = to_rpmsg_device(dev); if (chinfo->src != RPMSG_ADDR_ANY && chinfo->src != rpdev->src) diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c index d91f54a6e752..133f36457b28 100644 --- a/drivers/scsi/qla4xxx/ql4_os.c +++ b/drivers/scsi/qla4xxx/ql4_os.c @@ -7189,7 +7189,8 @@ exit_new_nt_list: * 1: if flashnode entry is non-persistent * 0: if flashnode entry is persistent **/ -static int qla4xxx_sysfs_ddb_is_non_persistent(struct device *dev, void *data) +static int qla4xxx_sysfs_ddb_is_non_persistent(struct device *dev, + const void *data) { struct iscsi_bus_flash_session *fnode_sess; diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c index fde7de3b1e55..0d474de2d960 100644 --- a/drivers/scsi/scsi_transport_iscsi.c +++ b/drivers/scsi/scsi_transport_iscsi.c @@ -1324,7 +1324,7 @@ EXPORT_SYMBOL_GPL(iscsi_create_flashnode_conn); * 1 on success * 0 on failure */ -static int iscsi_is_flashnode_conn_dev(struct device *dev, void *data) +static int iscsi_is_flashnode_conn_dev(struct device *dev, const void *data) { return dev->bus == &iscsi_flashnode_bus; } @@ -1335,7 +1335,7 @@ static int iscsi_destroy_flashnode_conn(struct iscsi_bus_flash_conn *fnode_conn) return 0; } -static int flashnode_match_index(struct device *dev, void *data) +static int flashnode_match_index(struct device *dev, const void *data) { struct iscsi_bus_flash_session *fnode_sess = NULL; int ret = 0; @@ -1344,7 +1344,7 @@ static int flashnode_match_index(struct device *dev, void *data) goto exit_match_index; fnode_sess = iscsi_dev_to_flash_session(dev); - ret = (fnode_sess->target_id == *((int *)data)) ? 1 : 0; + ret = (fnode_sess->target_id == *((const int *)data)) ? 1 : 0; exit_match_index: return ret; @@ -1389,8 +1389,8 @@ iscsi_get_flashnode_by_index(struct Scsi_Host *shost, uint32_t idx) * %NULL on failure */ struct device * -iscsi_find_flashnode_sess(struct Scsi_Host *shost, void *data, - int (*fn)(struct device *dev, void *data)) +iscsi_find_flashnode_sess(struct Scsi_Host *shost, const void *data, + device_match_t fn) { return device_find_child(&shost->shost_gendev, data, fn); } diff --git a/drivers/slimbus/core.c b/drivers/slimbus/core.c index b5d5bbb9fdb6..ab927fd077cb 100644 --- a/drivers/slimbus/core.c +++ b/drivers/slimbus/core.c @@ -337,9 +337,9 @@ static bool slim_eaddr_equal(const struct slim_eaddr *a, a->instance == b->instance); } -static int slim_match_dev(struct device *dev, void *data) +static int slim_match_dev(struct device *dev, const void *data) { - struct slim_eaddr *e_addr = data; + const struct slim_eaddr *e_addr = data; struct slim_device *sbdev = to_slim_device(dev); return slim_eaddr_equal(&sbdev->e_addr, e_addr); @@ -385,9 +385,9 @@ struct slim_device *slim_get_device(struct slim_controller *ctrl, } EXPORT_SYMBOL_GPL(slim_get_device); -static int of_slim_match_dev(struct device *dev, void *data) +static int of_slim_match_dev(struct device *dev, const void *data) { - struct device_node *np = data; + const struct device_node *np = data; struct slim_device *sbdev = to_slim_device(dev); return (sbdev->dev.of_node == np); diff --git a/drivers/thunderbolt/retimer.c b/drivers/thunderbolt/retimer.c index 89d2919d0193..21d2902c6102 100644 --- a/drivers/thunderbolt/retimer.c +++ b/drivers/thunderbolt/retimer.c @@ -461,7 +461,7 @@ struct tb_retimer_lookup { u8 index; }; -static int retimer_match(struct device *dev, void *data) +static int retimer_match(struct device *dev, const void *data) { const struct tb_retimer_lookup *lookup = data; struct tb_retimer *rt = tb_to_retimer(dev); diff --git a/drivers/thunderbolt/xdomain.c b/drivers/thunderbolt/xdomain.c index 11a50c86a1e4..b0630e6d9472 100644 --- a/drivers/thunderbolt/xdomain.c +++ b/drivers/thunderbolt/xdomain.c @@ -1026,7 +1026,7 @@ static int remove_missing_service(struct device *dev, void *data) return 0; } -static int find_service(struct device *dev, void *data) +static int find_service(struct device *dev, const void *data) { const struct tb_property *p = data; struct tb_service *svc; diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 74fa02b23772..8e0aa2c76d40 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -2365,9 +2365,9 @@ struct uart_match { struct uart_driver *driver; }; -static int serial_match_port(struct device *dev, void *data) +static int serial_match_port(struct device *dev, const void *data) { - struct uart_match *match = data; + const struct uart_match *match = data; struct tty_driver *tty_drv = match->driver->tty_driver; dev_t devt = MKDEV(tty_drv->major, tty_drv->minor_start) + match->port->line; diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index 4b3047e055a3..601a81aa1e10 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -229,10 +229,10 @@ static const char * const usb_modes[] = { /* ------------------------------------------------------------------------- */ /* Alternate Modes */ -static int altmode_match(struct device *dev, void *data) +static int altmode_match(struct device *dev, const void *data) { struct typec_altmode *adev = to_typec_altmode(dev); - struct typec_device_id *id = data; + const struct typec_device_id *id = data; if (!is_typec_altmode(dev)) return 0; @@ -1282,7 +1282,7 @@ const struct device_type typec_cable_dev_type = { .release = typec_cable_release, }; -static int cable_match(struct device *dev, void *data) +static int cable_match(struct device *dev, const void *data) { return is_typec_cable(dev); } @@ -2028,7 +2028,7 @@ const struct device_type typec_port_dev_type = { /* --------------------------------------- */ /* Driver callbacks to report role updates */ -static int partner_match(struct device *dev, void *data) +static int partner_match(struct device *dev, const void *data) { return is_typec_partner(dev); } diff --git a/include/linux/device.h b/include/linux/device.h index 667cb6db9019..0e0bc9bfe0d1 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -1081,8 +1081,8 @@ int device_for_each_child_reverse(struct device *dev, void *data, int device_for_each_child_reverse_from(struct device *parent, struct device *from, const void *data, int (*fn)(struct device *, const void *)); -struct device *device_find_child(struct device *dev, void *data, - int (*match)(struct device *dev, void *data)); +struct device *device_find_child(struct device *dev, const void *data, + device_match_t match); struct device *device_find_child_by_name(struct device *parent, const char *name); struct device *device_find_any_child(struct device *parent); diff --git a/include/scsi/scsi_transport_iscsi.h b/include/scsi/scsi_transport_iscsi.h index bd1243657c01..4d3baf324900 100644 --- a/include/scsi/scsi_transport_iscsi.h +++ b/include/scsi/scsi_transport_iscsi.h @@ -497,8 +497,8 @@ extern void iscsi_destroy_all_flashnode(struct Scsi_Host *shost); extern int iscsi_flashnode_bus_match(struct device *dev, const struct device_driver *drv); extern struct device * -iscsi_find_flashnode_sess(struct Scsi_Host *shost, void *data, - int (*fn)(struct device *dev, void *data)); +iscsi_find_flashnode_sess(struct Scsi_Host *shost, const void *data, + device_match_t fn); extern struct device * iscsi_find_flashnode_conn(struct iscsi_bus_flash_session *fnode_sess); diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index 5a7c0e565a89..e827775baf2e 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -1367,7 +1367,7 @@ static int dsa_switch_parse_of(struct dsa_switch *ds, struct device_node *dn) return dsa_switch_parse_ports_of(ds, dn); } -static int dev_is_class(struct device *dev, void *class) +static int dev_is_class(struct device *dev, const void *class) { if (dev->class != NULL && !strcmp(dev->class->name, class)) return 1; diff --git a/tools/testing/cxl/test/cxl.c b/tools/testing/cxl/test/cxl.c index d0337c11f9ee..cc8948f49117 100644 --- a/tools/testing/cxl/test/cxl.c +++ b/tools/testing/cxl/test/cxl.c @@ -725,7 +725,7 @@ static void default_mock_decoder(struct cxl_decoder *cxld) cxld->reset = mock_decoder_reset; } -static int first_decoder(struct device *dev, void *data) +static int first_decoder(struct device *dev, const void *data) { struct cxl_decoder *cxld; From d784b43c2d8bf24c2c80ef45ccbc41588945c415 Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Tue, 24 Dec 2024 21:05:04 +0800 Subject: [PATCH 34/68] driver core: Simplify API device_find_child_by_name() implementation Simplify device_find_child_by_name() implementation by both existing API device_find_child() and device_match_name(). Reviewed-by: Jonathan Cameron Signed-off-by: Zijun Hu Link: https://lore.kernel.org/r/20241224-const_dfc_done-v5-5-6623037414d4@quicinc.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/drivers/base/core.c b/drivers/base/core.c index b242993f816d..ee778d380777 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -4110,18 +4110,7 @@ EXPORT_SYMBOL_GPL(device_find_child); struct device *device_find_child_by_name(struct device *parent, const char *name) { - struct klist_iter i; - struct device *child; - - if (!parent) - return NULL; - - klist_iter_init(&parent->p->klist_children, &i); - while ((child = next_device(&i))) - if (sysfs_streq(dev_name(child), name) && get_device(child)) - break; - klist_iter_exit(&i); - return child; + return device_find_child(parent, name, device_match_name); } EXPORT_SYMBOL_GPL(device_find_child_by_name); From 6890fdc856014d731708d684a7c6f9dafec17d60 Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Tue, 24 Dec 2024 21:05:05 +0800 Subject: [PATCH 35/68] driver core: Remove match_any() Static match_any() is now exactly same as API device_match_any(). Remove the former and use the later instead. Reviewed-by: Jonathan Cameron Signed-off-by: Zijun Hu Link: https://lore.kernel.org/r/20241224-const_dfc_done-v5-6-6623037414d4@quicinc.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/base/core.c b/drivers/base/core.c index ee778d380777..6f1b2a1dbbf7 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -4114,11 +4114,6 @@ struct device *device_find_child_by_name(struct device *parent, } EXPORT_SYMBOL_GPL(device_find_child_by_name); -static int match_any(struct device *dev, const void *unused) -{ - return 1; -} - /** * device_find_any_child - device iterator for locating a child device, if any. * @parent: parent struct device @@ -4130,7 +4125,7 @@ static int match_any(struct device *dev, const void *unused) */ struct device *device_find_any_child(struct device *parent) { - return device_find_child(parent, NULL, match_any); + return device_find_child(parent, NULL, device_match_any); } EXPORT_SYMBOL_GPL(device_find_any_child); From 989e2b3569bf7eed912144ba86aab003c6cf858c Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Tue, 24 Dec 2024 21:05:06 +0800 Subject: [PATCH 36/68] slimbus: core: Remove of_slim_match_dev() static of_slim_match_dev() has same function as API device_match_of_node(). Remove the former and use the later instead. Reviewed-by: Jonathan Cameron Signed-off-by: Zijun Hu Link: https://lore.kernel.org/r/20241224-const_dfc_done-v5-7-6623037414d4@quicinc.com Signed-off-by: Greg Kroah-Hartman --- drivers/slimbus/core.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/drivers/slimbus/core.c b/drivers/slimbus/core.c index ab927fd077cb..005fa2ef100f 100644 --- a/drivers/slimbus/core.c +++ b/drivers/slimbus/core.c @@ -385,21 +385,13 @@ struct slim_device *slim_get_device(struct slim_controller *ctrl, } EXPORT_SYMBOL_GPL(slim_get_device); -static int of_slim_match_dev(struct device *dev, const void *data) -{ - const struct device_node *np = data; - struct slim_device *sbdev = to_slim_device(dev); - - return (sbdev->dev.of_node == np); -} - static struct slim_device *of_find_slim_device(struct slim_controller *ctrl, struct device_node *np) { struct slim_device *sbdev; struct device *dev; - dev = device_find_child(ctrl->dev, np, of_slim_match_dev); + dev = device_find_child(ctrl->dev, np, device_match_of_node); if (dev) { sbdev = to_slim_device(dev); return sbdev; From 6687f282e98b2236cad827fc7dba7393b7ef57f1 Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Tue, 24 Dec 2024 21:05:07 +0800 Subject: [PATCH 37/68] gpio: sim: Remove gpio_sim_dev_match_fwnode() gpio_sim_dev_match_fwnode() is a simple wrapper of API device_match_fwnode(). Remove the needless wrapper and use the API instead. Acked-by: Bartosz Golaszewski Reviewed-by: Jonathan Cameron Signed-off-by: Zijun Hu Link: https://lore.kernel.org/r/20241224-const_dfc_done-v5-8-6623037414d4@quicinc.com Signed-off-by: Greg Kroah-Hartman --- drivers/gpio/gpio-sim.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/gpio/gpio-sim.c b/drivers/gpio/gpio-sim.c index 370b71513bdb..b1f33cbaaaa7 100644 --- a/drivers/gpio/gpio-sim.c +++ b/drivers/gpio/gpio-sim.c @@ -413,11 +413,6 @@ static int gpio_sim_setup_sysfs(struct gpio_sim_chip *chip) return devm_add_action_or_reset(dev, gpio_sim_sysfs_remove, chip); } -static int gpio_sim_dev_match_fwnode(struct device *dev, const void *data) -{ - return device_match_fwnode(dev, data); -} - static int gpio_sim_add_bank(struct fwnode_handle *swnode, struct device *dev) { struct gpio_sim_chip *chip; @@ -503,7 +498,7 @@ static int gpio_sim_add_bank(struct fwnode_handle *swnode, struct device *dev) if (ret) return ret; - chip->dev = device_find_child(dev, swnode, gpio_sim_dev_match_fwnode); + chip->dev = device_find_child(dev, swnode, device_match_fwnode); if (!chip->dev) return -ENODEV; From adf908c965798c33d1148393927a7c0c5d08053c Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Tue, 24 Dec 2024 21:05:08 +0800 Subject: [PATCH 38/68] driver core: Introduce an device matching API device_match_type() Introduce device_match_type() for purposes below: - Test if a device matches with a specified device type. - As argument of various device finding APIs to find a device with specified type. device_find_child() will use it to simplify operations later. Reviewed-by: Jonathan Cameron Signed-off-by: Zijun Hu Link: https://lore.kernel.org/r/20241224-const_dfc_done-v5-9-6623037414d4@quicinc.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 6 ++++++ include/linux/device/bus.h | 1 + 2 files changed, 7 insertions(+) diff --git a/drivers/base/core.c b/drivers/base/core.c index 6f1b2a1dbbf7..d4c20e9b23da 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -5228,6 +5228,12 @@ int device_match_name(struct device *dev, const void *name) } EXPORT_SYMBOL_GPL(device_match_name); +int device_match_type(struct device *dev, const void *type) +{ + return dev->type == type; +} +EXPORT_SYMBOL_GPL(device_match_type); + int device_match_of_node(struct device *dev, const void *np) { return np && dev->of_node == np; diff --git a/include/linux/device/bus.h b/include/linux/device/bus.h index cdc4757217f9..bc3fd74bb763 100644 --- a/include/linux/device/bus.h +++ b/include/linux/device/bus.h @@ -131,6 +131,7 @@ typedef int (*device_match_t)(struct device *dev, const void *data); /* Generic device matching functions that all busses can use to match with */ int device_match_name(struct device *dev, const void *name); +int device_match_type(struct device *dev, const void *type); int device_match_of_node(struct device *dev, const void *np); int device_match_fwnode(struct device *dev, const void *fwnode); int device_match_devt(struct device *dev, const void *pdevt); From 98d4a843437401429fea1c1957d5cce242f5e4c4 Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Tue, 24 Dec 2024 21:05:09 +0800 Subject: [PATCH 39/68] cxl/pmem: Replace match_nvdimm_bridge() with API device_match_type() Static match_nvdimm_bridge(), as matching function of device_find_child() matches a device with device type @cxl_nvdimm_bridge_type, and its task can be simplified by the recently introduced API device_match_type(). Replace match_nvdimm_bridge() usage with device_match_type(). Reviewed-by: Alison Schofield Signed-off-by: Zijun Hu Reviewed-by: Jonathan Cameron Link: https://lore.kernel.org/r/20241224-const_dfc_done-v5-10-6623037414d4@quicinc.com Signed-off-by: Greg Kroah-Hartman --- drivers/cxl/core/pmem.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/cxl/core/pmem.c b/drivers/cxl/core/pmem.c index a8473de24ebf..0f8166e793e1 100644 --- a/drivers/cxl/core/pmem.c +++ b/drivers/cxl/core/pmem.c @@ -57,11 +57,6 @@ bool is_cxl_nvdimm_bridge(struct device *dev) } EXPORT_SYMBOL_NS_GPL(is_cxl_nvdimm_bridge, "CXL"); -static int match_nvdimm_bridge(struct device *dev, const void *data) -{ - return is_cxl_nvdimm_bridge(dev); -} - /** * cxl_find_nvdimm_bridge() - find a bridge device relative to a port * @port: any descendant port of an nvdimm-bridge associated @@ -75,7 +70,9 @@ struct cxl_nvdimm_bridge *cxl_find_nvdimm_bridge(struct cxl_port *port) if (!cxl_root) return NULL; - dev = device_find_child(&cxl_root->port.dev, NULL, match_nvdimm_bridge); + dev = device_find_child(&cxl_root->port.dev, + &cxl_nvdimm_bridge_type, + device_match_type); if (!dev) return NULL; From 6a6fef929d2828ad2e5e5b03b369eb07c19c61a6 Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Tue, 24 Dec 2024 21:05:10 +0800 Subject: [PATCH 40/68] cxl/pmem: Remove is_cxl_nvdimm_bridge() Remove is_cxl_nvdimm_bridge() which has no caller now. Signed-off-by: Zijun Hu Reviewed-by: Jonathan Cameron Link: https://lore.kernel.org/r/20241224-const_dfc_done-v5-11-6623037414d4@quicinc.com Signed-off-by: Greg Kroah-Hartman --- drivers/cxl/core/pmem.c | 6 ------ drivers/cxl/cxl.h | 1 - 2 files changed, 7 deletions(-) diff --git a/drivers/cxl/core/pmem.c b/drivers/cxl/core/pmem.c index 0f8166e793e1..8853415c106a 100644 --- a/drivers/cxl/core/pmem.c +++ b/drivers/cxl/core/pmem.c @@ -51,12 +51,6 @@ struct cxl_nvdimm_bridge *to_cxl_nvdimm_bridge(struct device *dev) } EXPORT_SYMBOL_NS_GPL(to_cxl_nvdimm_bridge, "CXL"); -bool is_cxl_nvdimm_bridge(struct device *dev) -{ - return dev->type == &cxl_nvdimm_bridge_type; -} -EXPORT_SYMBOL_NS_GPL(is_cxl_nvdimm_bridge, "CXL"); - /** * cxl_find_nvdimm_bridge() - find a bridge device relative to a port * @port: any descendant port of an nvdimm-bridge associated diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h index f6015f24ad38..064a15cf559f 100644 --- a/drivers/cxl/cxl.h +++ b/drivers/cxl/cxl.h @@ -864,7 +864,6 @@ struct cxl_nvdimm_bridge *devm_cxl_add_nvdimm_bridge(struct device *host, struct cxl_port *port); struct cxl_nvdimm *to_cxl_nvdimm(struct device *dev); bool is_cxl_nvdimm(struct device *dev); -bool is_cxl_nvdimm_bridge(struct device *dev); int devm_cxl_add_nvdimm(struct cxl_port *parent_port, struct cxl_memdev *cxlmd); struct cxl_nvdimm_bridge *cxl_find_nvdimm_bridge(struct cxl_port *port); From cf7da549cf7235ea6c5b29cc547a05dbdeb3ef08 Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Tue, 24 Dec 2024 21:05:11 +0800 Subject: [PATCH 41/68] usb: typec: class: Remove both cable_match() and partner_match() cable_match(), as matching function of device_find_child(), matches a device with device type @typec_cable_dev_type, and its task can be simplified by the recently introduced API device_match_type(). partner_match() is similar with cable_match() but with a different device type @typec_partner_dev_type. Remove both functions and use the API plus respective device type instead. Signed-off-by: Zijun Hu Link: https://lore.kernel.org/r/20241224-const_dfc_done-v5-12-6623037414d4@quicinc.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/class.c | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index 601a81aa1e10..3a4e0bd01317 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -1282,11 +1282,6 @@ const struct device_type typec_cable_dev_type = { .release = typec_cable_release, }; -static int cable_match(struct device *dev, const void *data) -{ - return is_typec_cable(dev); -} - /** * typec_cable_get - Get a reference to the USB Type-C cable * @port: The USB Type-C Port the cable is connected to @@ -1298,7 +1293,8 @@ struct typec_cable *typec_cable_get(struct typec_port *port) { struct device *dev; - dev = device_find_child(&port->dev, NULL, cable_match); + dev = device_find_child(&port->dev, &typec_cable_dev_type, + device_match_type); if (!dev) return NULL; @@ -2028,16 +2024,12 @@ const struct device_type typec_port_dev_type = { /* --------------------------------------- */ /* Driver callbacks to report role updates */ -static int partner_match(struct device *dev, const void *data) -{ - return is_typec_partner(dev); -} - static struct typec_partner *typec_get_partner(struct typec_port *port) { struct device *dev; - dev = device_find_child(&port->dev, NULL, partner_match); + dev = device_find_child(&port->dev, &typec_partner_dev_type, + device_match_type); if (!dev) return NULL; @@ -2170,7 +2162,9 @@ void typec_set_pwr_opmode(struct typec_port *port, sysfs_notify(&port->dev.kobj, NULL, "power_operation_mode"); kobject_uevent(&port->dev.kobj, KOBJ_CHANGE); - partner_dev = device_find_child(&port->dev, NULL, partner_match); + partner_dev = device_find_child(&port->dev, + &typec_partner_dev_type, + device_match_type); if (partner_dev) { struct typec_partner *partner = to_typec_partner(partner_dev); @@ -2334,7 +2328,9 @@ int typec_get_negotiated_svdm_version(struct typec_port *port) enum usb_pd_svdm_ver svdm_version; struct device *partner_dev; - partner_dev = device_find_child(&port->dev, NULL, partner_match); + partner_dev = device_find_child(&port->dev, + &typec_partner_dev_type, + device_match_type); if (!partner_dev) return -ENODEV; @@ -2361,7 +2357,8 @@ int typec_get_cable_svdm_version(struct typec_port *port) enum usb_pd_svdm_ver svdm_version; struct device *cable_dev; - cable_dev = device_find_child(&port->dev, NULL, cable_match); + cable_dev = device_find_child(&port->dev, &typec_cable_dev_type, + device_match_type); if (!cable_dev) return -ENODEV; From 7687c66c18c66d4ccd9949c6f641c0e7b5773483 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Fri, 13 Dec 2024 10:08:23 -0800 Subject: [PATCH 42/68] kunit: platform: Resolve 'struct completion' warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the header is included in a test without certain other headers, it produces compiler warnings like: In file included from [...] ../include/kunit/platform_device.h:15:57: warning: ‘struct completion’ declared inside parameter list will not be visible outside of this definition or declaration 15 | struct completion *x); | ^~~~~~~~~~ Add a 'struct completion' forward declaration to resolve this. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202412241958.dbAImJsA-lkp@intel.com/ Signed-off-by: Brian Norris Reviewed-by: David Gow Link: https://lore.kernel.org/r/20241213180841.3023843-1-briannorris@chromium.org Signed-off-by: Greg Kroah-Hartman --- include/kunit/platform_device.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/kunit/platform_device.h b/include/kunit/platform_device.h index 0fc0999d2420..f8236a8536f7 100644 --- a/include/kunit/platform_device.h +++ b/include/kunit/platform_device.h @@ -2,6 +2,7 @@ #ifndef _KUNIT_PLATFORM_DRIVER_H #define _KUNIT_PLATFORM_DRIVER_H +struct completion; struct kunit; struct platform_device; struct platform_driver; From 7e16820fe538e1d36a3bc5aab42f7d2c9d14d0fe Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Fri, 3 Jan 2025 17:46:01 +0100 Subject: [PATCH 43/68] rust: pci: do not depend on CONFIG_PCI_MSI The PCI abstractions do not actually depend on CONFIG_PCI_MSI; it also breaks drivers that only depend on CONFIG_PCI, hence drop it. While at it, move the module entry to its correct location. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202501030744.4ucqC1cB-lkp@intel.com/ Fixes: 1bd8b6b2c5d3 ("rust: pci: add basic PCI device / driver abstractions") Signed-off-by: Danilo Krummrich Link: https://lore.kernel.org/r/20250103164655.96590-2-dakr@kernel.org Signed-off-by: Greg Kroah-Hartman --- rust/kernel/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index e59250dc6c6e..b7351057ed9c 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -58,6 +58,8 @@ pub mod miscdevice; pub mod net; pub mod of; pub mod page; +#[cfg(CONFIG_PCI)] +pub mod pci; pub mod pid_namespace; pub mod platform; pub mod prelude; @@ -84,8 +86,6 @@ pub mod workqueue; pub use bindings; pub mod io; pub use macros; -#[cfg(all(CONFIG_PCI, CONFIG_PCI_MSI))] -pub mod pci; pub use uapi; #[doc(hidden)] From 9b880189327b9727640147253f3236ec5b3f704f Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Fri, 3 Jan 2025 17:46:02 +0100 Subject: [PATCH 44/68] rust: io: move module entry to its correct location The module entry of `io` falsely ended up in the "use" block instead of the "mod" block, hence move it to its correct location. Signed-off-by: Danilo Krummrich Link: https://lore.kernel.org/r/20250103164655.96590-3-dakr@kernel.org Signed-off-by: Greg Kroah-Hartman --- rust/kernel/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index b7351057ed9c..b11fa08de3c0 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -48,6 +48,7 @@ pub mod error; pub mod firmware; pub mod fs; pub mod init; +pub mod io; pub mod ioctl; pub mod jump_label; #[cfg(CONFIG_KUNIT)] @@ -84,7 +85,6 @@ pub mod workqueue; #[doc(hidden)] pub use bindings; -pub mod io; pub use macros; pub use uapi; From e1a51c2bf4b3b20868a0e6e9520b11639bd363f1 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Fri, 3 Jan 2025 17:46:03 +0100 Subject: [PATCH 45/68] rust: driver: address soundness issue in `RegistrationOps` The `RegistrationOps` trait holds some obligations to the caller and implementers. While being documented, the trait and the corresponding functions haven't been marked as unsafe. Hence, markt the trait and functions unsafe and add the corresponding safety comments. This patch does not include any fuctional changes. Reported-by: Gary Guo Closes: https://lore.kernel.org/rust-for-linux/20241224195821.3b43302b.gary@garyguo.net/ Signed-off-by: Danilo Krummrich Reviewed-by: Gary Guo Link: https://lore.kernel.org/r/20250103164655.96590-4-dakr@kernel.org Signed-off-by: Greg Kroah-Hartman --- rust/kernel/driver.rs | 25 ++++++++++++++++++++----- rust/kernel/pci.rs | 8 +++++--- rust/kernel/platform.rs | 8 +++++--- 3 files changed, 30 insertions(+), 11 deletions(-) diff --git a/rust/kernel/driver.rs b/rust/kernel/driver.rs index c630e65098ed..2a16d5e64e6c 100644 --- a/rust/kernel/driver.rs +++ b/rust/kernel/driver.rs @@ -17,23 +17,35 @@ use macros::{pin_data, pinned_drop}; /// For instance, the PCI subsystem would set `RegType` to `bindings::pci_driver` and call /// `bindings::__pci_register_driver` from `RegistrationOps::register` and /// `bindings::pci_unregister_driver` from `RegistrationOps::unregister`. -pub trait RegistrationOps { +/// +/// # Safety +/// +/// A call to [`RegistrationOps::unregister`] for a given instance of `RegType` is only valid if a +/// preceding call to [`RegistrationOps::register`] has been successful. +pub unsafe trait RegistrationOps { /// The type that holds information about the registration. This is typically a struct defined /// by the C portion of the kernel. type RegType: Default; /// Registers a driver. /// + /// # Safety + /// /// On success, `reg` must remain pinned and valid until the matching call to /// [`RegistrationOps::unregister`]. - fn register( + unsafe fn register( reg: &Opaque, name: &'static CStr, module: &'static ThisModule, ) -> Result; /// Unregisters a driver previously registered with [`RegistrationOps::register`]. - fn unregister(reg: &Opaque); + /// + /// # Safety + /// + /// Must only be called after a preceding successful call to [`RegistrationOps::register`] for + /// the same `reg`. + unsafe fn unregister(reg: &Opaque); } /// A [`Registration`] is a generic type that represents the registration of some driver type (e.g. @@ -68,7 +80,8 @@ impl Registration { // just been initialised above, so it's also valid for read. let drv = unsafe { &*(ptr as *const Opaque) }; - T::register(drv, name, module) + // SAFETY: `drv` is guaranteed to be pinned until `T::unregister`. + unsafe { T::register(drv, name, module) } }), }) } @@ -77,7 +90,9 @@ impl Registration { #[pinned_drop] impl PinnedDrop for Registration { fn drop(self: Pin<&mut Self>) { - T::unregister(&self.reg); + // SAFETY: The existence of `self` guarantees that `self.reg` has previously been + // successfully registered with `T::register` + unsafe { T::unregister(&self.reg) }; } } diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs index d5e7f0b15303..4c98b5b9aa1e 100644 --- a/rust/kernel/pci.rs +++ b/rust/kernel/pci.rs @@ -23,10 +23,12 @@ use kernel::prelude::*; /// An adapter for the registration of PCI drivers. pub struct Adapter(T); -impl driver::RegistrationOps for Adapter { +// SAFETY: A call to `unregister` for a given instance of `RegType` is guaranteed to be valid if +// a preceding call to `register` has been successful. +unsafe impl driver::RegistrationOps for Adapter { type RegType = bindings::pci_driver; - fn register( + unsafe fn register( pdrv: &Opaque, name: &'static CStr, module: &'static ThisModule, @@ -45,7 +47,7 @@ impl driver::RegistrationOps for Adapter { }) } - fn unregister(pdrv: &Opaque) { + unsafe fn unregister(pdrv: &Opaque) { // SAFETY: `pdrv` is guaranteed to be a valid `RegType`. unsafe { bindings::pci_unregister_driver(pdrv.get()) } } diff --git a/rust/kernel/platform.rs b/rust/kernel/platform.rs index 03287794f9d0..50e6b0421813 100644 --- a/rust/kernel/platform.rs +++ b/rust/kernel/platform.rs @@ -19,10 +19,12 @@ use core::ptr::addr_of_mut; /// An adapter for the registration of platform drivers. pub struct Adapter(T); -impl driver::RegistrationOps for Adapter { +// SAFETY: A call to `unregister` for a given instance of `RegType` is guaranteed to be valid if +// a preceding call to `register` has been successful. +unsafe impl driver::RegistrationOps for Adapter { type RegType = bindings::platform_driver; - fn register( + unsafe fn register( pdrv: &Opaque, name: &'static CStr, module: &'static ThisModule, @@ -44,7 +46,7 @@ impl driver::RegistrationOps for Adapter { to_result(unsafe { bindings::__platform_driver_register(pdrv.get(), module.0) }) } - fn unregister(pdrv: &Opaque) { + unsafe fn unregister(pdrv: &Opaque) { // SAFETY: `pdrv` is guaranteed to be a valid `RegType`. unsafe { bindings::platform_driver_unregister(pdrv.get()) }; } From 0f9e1f3a6e1e873bc70c7a47870a18c05647360e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Thu, 21 Nov 2024 17:48:47 +0100 Subject: [PATCH 46/68] kernel/ksysfs.c: simplify bin_attribute definition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The notes attribute can be implemented in terms of BIN_ATTR_SIMPLE(). This saves memory at runtime and is a preparation for the constification of struct bin_attribute. Cc: Greg Kroah-Hartman Signed-off-by: Thomas Weißschuh Link: https://lore.kernel.org/r/20241121-sysfs-const-bin_attr-ksysfs-v1-1-972faced149d@weissschuh.net Signed-off-by: Greg Kroah-Hartman --- kernel/ksysfs.c | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/kernel/ksysfs.c b/kernel/ksysfs.c index 1bab21b4718f..eefb67d9883c 100644 --- a/kernel/ksysfs.c +++ b/kernel/ksysfs.c @@ -239,21 +239,7 @@ extern const void __start_notes; extern const void __stop_notes; #define notes_size (&__stop_notes - &__start_notes) -static ssize_t notes_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, - char *buf, loff_t off, size_t count) -{ - memcpy(buf, &__start_notes + off, count); - return count; -} - -static struct bin_attribute notes_attr __ro_after_init = { - .attr = { - .name = "notes", - .mode = S_IRUGO, - }, - .read = ¬es_read, -}; +static __ro_after_init BIN_ATTR_SIMPLE_RO(notes); struct kobject *kernel_kobj; EXPORT_SYMBOL_GPL(kernel_kobj); @@ -307,8 +293,9 @@ static int __init ksysfs_init(void) goto kset_exit; if (notes_size > 0) { - notes_attr.size = notes_size; - error = sysfs_create_bin_file(kernel_kobj, ¬es_attr); + bin_attr_notes.private = (void *)&__start_notes; + bin_attr_notes.size = notes_size; + error = sysfs_create_bin_file(kernel_kobj, &bin_attr_notes); if (error) goto group_exit; } From 7c9bf0305662da44676e5a75d1941cecd0dd73be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Thu, 5 Dec 2024 17:43:27 +0100 Subject: [PATCH 47/68] MAINTAINERS: add include/linux/sysfs.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit include/linux/sysfs.h is maintained as part of "DRIVER CORE, KOBJECT, DEBUGFS AND SYSFS. Signed-off-by: Thomas Weißschuh Link: https://lore.kernel.org/r/20241205-sysfs-maintainers-v1-1-53694b690d67@weissschuh.net Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 3d33859ba957..2bcadcc65c81 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7033,6 +7033,7 @@ F: include/linux/debugfs.h F: include/linux/fwnode.h F: include/linux/kobj* F: include/linux/property.h +F: include/linux/sysfs.h F: lib/kobj* F: rust/kernel/device.rs F: rust/kernel/device_id.rs From 92d6254f58120011c93610b4cb7def214409731d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Thu, 5 Dec 2024 18:07:31 +0100 Subject: [PATCH 48/68] sysfs: constify macro BIN_ATTRIBUTE_GROUPS() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As there is only one in-tree user, avoid a transition phase and switch that user in the same commit. As there are some interdependencies between the constness of the different symbols in the s390 driver, covert the whole driver at once. Signed-off-by: Thomas Weißschuh Link: https://lore.kernel.org/r/20241205-sysfs-const-bin_attr-groups_macro-v1-1-ac5e855031e8@weissschuh.net Signed-off-by: Greg Kroah-Hartman --- drivers/s390/cio/chp.c | 28 ++++++++++++++-------------- include/linux/sysfs.h | 2 +- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/drivers/s390/cio/chp.c b/drivers/s390/cio/chp.c index cba2d048a96b..4a0b3f19bd8e 100644 --- a/drivers/s390/cio/chp.c +++ b/drivers/s390/cio/chp.c @@ -128,7 +128,7 @@ static int s390_vary_chpid(struct chp_id chpid, int on) * Channel measurement related functions */ static ssize_t measurement_chars_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, + const struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct channel_path *chp; @@ -142,11 +142,11 @@ static ssize_t measurement_chars_read(struct file *filp, struct kobject *kobj, return memory_read_from_buffer(buf, count, &off, &chp->cmg_chars, sizeof(chp->cmg_chars)); } -static BIN_ATTR_ADMIN_RO(measurement_chars, sizeof(struct cmg_chars)); +static const BIN_ATTR_ADMIN_RO(measurement_chars, sizeof(struct cmg_chars)); static ssize_t measurement_chars_full_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, + const struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct channel_path *chp = to_channelpath(kobj_to_dev(kobj)); @@ -196,22 +196,22 @@ static ssize_t chp_measurement_copy_block(void *buf, loff_t off, size_t count, } static ssize_t measurement_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, + const struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { return chp_measurement_copy_block(buf, off, count, kobj, false); } -static BIN_ATTR_ADMIN_RO(measurement, sizeof(struct cmg_entry)); +static const BIN_ATTR_ADMIN_RO(measurement, sizeof(struct cmg_entry)); static ssize_t ext_measurement_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, + const struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { return chp_measurement_copy_block(buf, off, count, kobj, true); } -static BIN_ATTR_ADMIN_RO(ext_measurement, sizeof(struct cmg_ext_entry)); +static const BIN_ATTR_ADMIN_RO(ext_measurement, sizeof(struct cmg_ext_entry)); -static struct bin_attribute *measurement_attrs[] = { +static const struct bin_attribute *measurement_attrs[] = { &bin_attr_measurement_chars, &bin_attr_measurement_chars_full, &bin_attr_measurement, @@ -435,7 +435,7 @@ static ssize_t speed_bps_show(struct device *dev, static DEVICE_ATTR_RO(speed_bps); static ssize_t util_string_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, char *buf, + const struct bin_attribute *attr, char *buf, loff_t off, size_t count) { struct channel_path *chp = to_channelpath(kobj_to_dev(kobj)); @@ -448,10 +448,10 @@ static ssize_t util_string_read(struct file *filp, struct kobject *kobj, return rc; } -static BIN_ATTR_RO(util_string, - sizeof(((struct channel_path_desc_fmt3 *)0)->util_str)); +static const BIN_ATTR_RO(util_string, + sizeof(((struct channel_path_desc_fmt3 *)0)->util_str)); -static struct bin_attribute *chp_bin_attrs[] = { +static const struct bin_attribute *const chp_bin_attrs[] = { &bin_attr_util_string, NULL, }; @@ -468,9 +468,9 @@ static struct attribute *chp_attrs[] = { &dev_attr_speed_bps.attr, NULL, }; -static struct attribute_group chp_attr_group = { +static const struct attribute_group chp_attr_group = { .attrs = chp_attrs, - .bin_attrs = chp_bin_attrs, + .bin_attrs_new = chp_bin_attrs, }; static const struct attribute_group *chp_attr_groups[] = { &chp_attr_group, diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index 0f2fcd244523..b4368377fac9 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -293,7 +293,7 @@ __ATTRIBUTE_GROUPS(_name) #define BIN_ATTRIBUTE_GROUPS(_name) \ static const struct attribute_group _name##_group = { \ - .bin_attrs = _name##_attrs, \ + .bin_attrs_new = _name##_attrs, \ }; \ __ATTRIBUTE_GROUPS(_name) From 3675a926feefdf3afabea12f806f31ea582065e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Sat, 28 Dec 2024 09:43:41 +0100 Subject: [PATCH 49/68] sysfs: constify bin_attribute argument of sysfs_bin_attr_simple_read() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Most users use this function through the BIN_ATTR_SIMPLE* macros, they can handle the switch transparently. Also adapt the two non-macro users in the same change. Signed-off-by: Thomas Weißschuh Acked-by: Madhavan Srinivasan Reviewed-by: Mahesh Salgaonkar Tested-by: Aditya Gupta Link: https://lore.kernel.org/r/20241228-sysfs-const-bin_attr-simple-v2-1-7c6f3f1767a3@weissschuh.net Signed-off-by: Greg Kroah-Hartman --- arch/powerpc/platforms/powernv/opal.c | 2 +- fs/sysfs/file.c | 2 +- include/linux/sysfs.h | 4 ++-- kernel/module/sysfs.c | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c index 5d0f35bb917e..013637e2b2a8 100644 --- a/arch/powerpc/platforms/powernv/opal.c +++ b/arch/powerpc/platforms/powernv/opal.c @@ -818,7 +818,7 @@ static int opal_add_one_export(struct kobject *parent, const char *export_name, sysfs_bin_attr_init(attr); attr->attr.name = name; attr->attr.mode = 0400; - attr->read = sysfs_bin_attr_simple_read; + attr->read_new = sysfs_bin_attr_simple_read; attr->private = __va(vals[0]); attr->size = vals[1]; diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index 785408861c01..6931308876c4 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -817,7 +817,7 @@ EXPORT_SYMBOL_GPL(sysfs_emit_at); * Returns number of bytes written to @buf. */ ssize_t sysfs_bin_attr_simple_read(struct file *file, struct kobject *kobj, - struct bin_attribute *attr, char *buf, + const struct bin_attribute *attr, char *buf, loff_t off, size_t count) { memcpy(buf, attr->private + off, count); diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index b4368377fac9..18f7e1fd093c 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -511,7 +511,7 @@ __printf(3, 4) int sysfs_emit_at(char *buf, int at, const char *fmt, ...); ssize_t sysfs_bin_attr_simple_read(struct file *file, struct kobject *kobj, - struct bin_attribute *attr, char *buf, + const struct bin_attribute *attr, char *buf, loff_t off, size_t count); #else /* CONFIG_SYSFS */ @@ -774,7 +774,7 @@ static inline int sysfs_emit_at(char *buf, int at, const char *fmt, ...) static inline ssize_t sysfs_bin_attr_simple_read(struct file *file, struct kobject *kobj, - struct bin_attribute *attr, + const struct bin_attribute *attr, char *buf, loff_t off, size_t count) { diff --git a/kernel/module/sysfs.c b/kernel/module/sysfs.c index 456358e1fdc4..254017b58b64 100644 --- a/kernel/module/sysfs.c +++ b/kernel/module/sysfs.c @@ -196,7 +196,7 @@ static int add_notes_attrs(struct module *mod, const struct load_info *info) nattr->attr.mode = 0444; nattr->size = info->sechdrs[i].sh_size; nattr->private = (void *)info->sechdrs[i].sh_addr; - nattr->read = sysfs_bin_attr_simple_read; + nattr->read_new = sysfs_bin_attr_simple_read; ++nattr; } ++loaded; From 42369b9a1ecf777ab2f6075747ecfb825db4552b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Sat, 28 Dec 2024 09:43:42 +0100 Subject: [PATCH 50/68] btf: Switch vmlinux BTF attribute to sysfs_bin_attr_simple_read() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The generic function from the sysfs core can replace the custom one. Signed-off-by: Thomas Weißschuh Acked-by: Andrii Nakryiko Link: https://lore.kernel.org/r/20241228-sysfs-const-bin_attr-simple-v2-2-7c6f3f1767a3@weissschuh.net Signed-off-by: Greg Kroah-Hartman --- kernel/bpf/sysfs_btf.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/kernel/bpf/sysfs_btf.c b/kernel/bpf/sysfs_btf.c index fedb54c94cdb..81d6cf90584a 100644 --- a/kernel/bpf/sysfs_btf.c +++ b/kernel/bpf/sysfs_btf.c @@ -12,24 +12,16 @@ extern char __start_BTF[]; extern char __stop_BTF[]; -static ssize_t -btf_vmlinux_read(struct file *file, struct kobject *kobj, - struct bin_attribute *bin_attr, - char *buf, loff_t off, size_t len) -{ - memcpy(buf, __start_BTF + off, len); - return len; -} - static struct bin_attribute bin_attr_btf_vmlinux __ro_after_init = { .attr = { .name = "vmlinux", .mode = 0444, }, - .read = btf_vmlinux_read, + .read_new = sysfs_bin_attr_simple_read, }; struct kobject *btf_kobj; static int __init btf_vmlinux_init(void) { + bin_attr_btf_vmlinux.private = __start_BTF; bin_attr_btf_vmlinux.size = __stop_BTF - __start_BTF; if (bin_attr_btf_vmlinux.size == 0) From 18032c6bc0e204c8f836b09707ef671991e4fe87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Sat, 28 Dec 2024 09:43:43 +0100 Subject: [PATCH 51/68] btf: Switch module BTF attribute to sysfs_bin_attr_simple_read() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The generic function from the sysfs core can replace the custom one. Signed-off-by: Thomas Weißschuh Acked-by: Andrii Nakryiko Link: https://lore.kernel.org/r/20241228-sysfs-const-bin_attr-simple-v2-3-7c6f3f1767a3@weissschuh.net Signed-off-by: Greg Kroah-Hartman --- kernel/bpf/btf.c | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index e5a5f023cedd..76de36d25520 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -8011,17 +8011,6 @@ struct btf_module { static LIST_HEAD(btf_modules); static DEFINE_MUTEX(btf_module_mutex); -static ssize_t -btf_module_read(struct file *file, struct kobject *kobj, - struct bin_attribute *bin_attr, - char *buf, loff_t off, size_t len) -{ - const struct btf *btf = bin_attr->private; - - memcpy(buf, btf->data + off, len); - return len; -} - static void purge_cand_cache(struct btf *btf); static int btf_module_notify(struct notifier_block *nb, unsigned long op, @@ -8082,8 +8071,8 @@ static int btf_module_notify(struct notifier_block *nb, unsigned long op, attr->attr.name = btf->name; attr->attr.mode = 0444; attr->size = btf->data_size; - attr->private = btf; - attr->read = btf_module_read; + attr->private = btf->data; + attr->read_new = sysfs_bin_attr_simple_read; err = sysfs_create_bin_file(btf_kobj, attr); if (err) { From 7685ad5f08d9297f5a3d30b1818391ac94813171 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 5 Dec 2024 09:56:05 +0100 Subject: [PATCH 52/68] mux: constify mux class All class functions used here take a const pointer to the class structure so we can make the struct itself constant. Signed-off-by: Bartosz Golaszewski Link: https://lore.kernel.org/r/20241205085605.9501-1-brgl@bgdev.pl Signed-off-by: Greg Kroah-Hartman --- drivers/mux/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mux/core.c b/drivers/mux/core.c index 78c0022697ec..02be4ba37257 100644 --- a/drivers/mux/core.c +++ b/drivers/mux/core.c @@ -42,7 +42,7 @@ struct mux_state { unsigned int state; }; -static struct class mux_class = { +static const struct class mux_class = { .name = "mux", }; From 2a8d6abdf5cfdc7df934968f2e8292d050f20b20 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Fri, 29 Nov 2024 18:35:54 -0800 Subject: [PATCH 53/68] devcoredump: cleanup some comments Correct a spello, remove an extra space between words, and fix one kernel-doc warning: drivers/base/devcoredump.c:292: warning: No description found for return value of 'devcd_read_from_sgtable' Fixes: 522566376a3f ("devcoredump: add scatterlist support") Fixes: 01daccf74832 ("devcoredump : Serialize devcd_del work") Signed-off-by: Randy Dunlap Cc: Johannes Berg Cc: Greg Kroah-Hartman Cc: Rafael J. Wysocki Cc: Aviya Erenfeld Cc: Mukesh Ojha Link: https://lore.kernel.org/r/20241130023554.538820-1-rdunlap@infradead.org Signed-off-by: Greg Kroah-Hartman --- drivers/base/devcoredump.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/base/devcoredump.c b/drivers/base/devcoredump.c index c795edad1b96..2a0e0b2fdb98 100644 --- a/drivers/base/devcoredump.c +++ b/drivers/base/devcoredump.c @@ -186,9 +186,9 @@ static ssize_t disabled_show(const struct class *class, const struct class_attri * mutex_lock(&devcd->mutex); * * - * In the above diagram, It looks like disabled_store() would be racing with parallely + * In the above diagram, it looks like disabled_store() would be racing with parallelly * running devcd_del() and result in memory abort while acquiring devcd->mutex which - * is called after kfree of devcd memory after dropping its last reference with + * is called after kfree of devcd memory after dropping its last reference with * put_device(). However, this will not happens as fn(dev, data) runs * with its own reference to device via klist_node so it is not its last reference. * so, above situation would not occur. @@ -285,6 +285,8 @@ static void devcd_free_sgtable(void *data) * @offset: start copy from @offset@ bytes from the head of the data * in the given scatterlist * @data_len: the length of the data in the sg_table + * + * Returns: the number of bytes copied */ static ssize_t devcd_read_from_sgtable(char *buffer, loff_t offset, size_t buf_len, void *data, From c1ecb860a48d0bf56b0fc8391c5d59b0c3b42516 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Sun, 22 Dec 2024 21:20:50 +0100 Subject: [PATCH 54/68] firmware_loader: Constify 'struct bin_attribute' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The sysfs core now allows instances of 'struct bin_attribute' to be moved into read-only memory. Make use of that to protect them against accidental or malicious modifications. Signed-off-by: Thomas Weißschuh Reviewed-by: Russ Weight Link: https://lore.kernel.org/r/20241222-sysfs-const-bin_attr-firmware-v1-1-c35e56bfb4eb@weissschuh.net Signed-off-by: Greg Kroah-Hartman --- drivers/base/firmware_loader/sysfs.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/base/firmware_loader/sysfs.c b/drivers/base/firmware_loader/sysfs.c index c9c93b47d9a5..d254ceb56d84 100644 --- a/drivers/base/firmware_loader/sysfs.c +++ b/drivers/base/firmware_loader/sysfs.c @@ -259,7 +259,7 @@ static void firmware_rw(struct fw_priv *fw_priv, char *buffer, } static ssize_t firmware_data_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, + const struct bin_attribute *bin_attr, char *buffer, loff_t offset, size_t count) { struct device *dev = kobj_to_dev(kobj); @@ -316,7 +316,7 @@ static int fw_realloc_pages(struct fw_sysfs *fw_sysfs, int min_size) * the driver as a firmware image. **/ static ssize_t firmware_data_write(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, + const struct bin_attribute *bin_attr, char *buffer, loff_t offset, size_t count) { struct device *dev = kobj_to_dev(kobj); @@ -356,11 +356,11 @@ out: return retval; } -static struct bin_attribute firmware_attr_data = { +static const struct bin_attribute firmware_attr_data = { .attr = { .name = "data", .mode = 0644 }, .size = 0, - .read = firmware_data_read, - .write = firmware_data_write, + .read_new = firmware_data_read, + .write_new = firmware_data_write, }; static struct attribute *fw_dev_attrs[] = { @@ -374,14 +374,14 @@ static struct attribute *fw_dev_attrs[] = { NULL }; -static struct bin_attribute *fw_dev_bin_attrs[] = { +static const struct bin_attribute *const fw_dev_bin_attrs[] = { &firmware_attr_data, NULL }; static const struct attribute_group fw_dev_attr_group = { .attrs = fw_dev_attrs, - .bin_attrs = fw_dev_bin_attrs, + .bin_attrs_new = fw_dev_bin_attrs, #ifdef CONFIG_FW_UPLOAD .is_visible = fw_upload_is_visible, #endif From bf2aa7df2687a24ebb52cec4a24443121ac3126d Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Fri, 10 Jan 2025 10:14:59 +0000 Subject: [PATCH 55/68] miscdevice: rust: use build_error! macro instead of function The function called build_error is an implementation detail of the macro of the same name. Thus, update miscdevice to use the macro rather than the function. See [1] for more information on this. These use the macro with the kernel:: prefix as it has not yet been added to the prelude. Reported-by: Stephen Rothwell Link: https://lore.kernel.org/r/20250110162828.38614c1b@canb.auug.org.au Link: https://lore.kernel.org/all/20241123222849.350287-2-ojeda@kernel.org/ [1] Signed-off-by: Alice Ryhl Acked-by: Miguel Ojeda Link: https://lore.kernel.org/r/20250110101459.536726-1-aliceryhl@google.com Signed-off-by: Greg Kroah-Hartman --- rust/kernel/miscdevice.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rust/kernel/miscdevice.rs b/rust/kernel/miscdevice.rs index ebc82e7dfc80..dfb363630c70 100644 --- a/rust/kernel/miscdevice.rs +++ b/rust/kernel/miscdevice.rs @@ -134,7 +134,7 @@ pub trait MiscDevice: Sized { _cmd: u32, _arg: usize, ) -> Result { - kernel::build_error(VTABLE_DEFAULT_ERROR) + kernel::build_error!(VTABLE_DEFAULT_ERROR) } /// Handler for ioctls. @@ -151,7 +151,7 @@ pub trait MiscDevice: Sized { _cmd: u32, _arg: usize, ) -> Result { - kernel::build_error(VTABLE_DEFAULT_ERROR) + kernel::build_error!(VTABLE_DEFAULT_ERROR) } /// Show info for this fd. @@ -160,7 +160,7 @@ pub trait MiscDevice: Sized { _m: &SeqFile, _file: &File, ) { - kernel::build_error(VTABLE_DEFAULT_ERROR) + kernel::build_error!(VTABLE_DEFAULT_ERROR) } } From 896be785015c0e0ba73442f73b8d4d9f5ccfc54c Mon Sep 17 00:00:00 2001 From: "Ricardo B. Marliere" Date: Wed, 4 Sep 2024 11:17:23 -0300 Subject: [PATCH 56/68] bus: fsl-mc: constify the struct device_type usage Since commit aed65af1cc2f ("drivers: make device_type const"), the driver core can properly handle constant struct device_type. Move all the device_type variables used in the bus to be constant structures as well, placing it into read-only memory which can not be modified at runtime. Cc: Greg Kroah-Hartman Suggested-by: Greg Kroah-Hartman Signed-off-by: Ricardo B. Marliere Link: https://lore.kernel.org/r/20240904-class_cleanup-fsl-mc-bus-v2-1-83fa25cbdc68@suse.com Signed-off-by: Greg Kroah-Hartman --- drivers/bus/fsl-mc/fsl-mc-bus.c | 36 ++++++++++++++++----------------- include/linux/fsl/mc.h | 30 +++++++++++++-------------- 2 files changed, 33 insertions(+), 33 deletions(-) diff --git a/drivers/bus/fsl-mc/fsl-mc-bus.c b/drivers/bus/fsl-mc/fsl-mc-bus.c index 2916d1333649..d1f3d327ddd1 100644 --- a/drivers/bus/fsl-mc/fsl-mc-bus.c +++ b/drivers/bus/fsl-mc/fsl-mc-bus.c @@ -320,90 +320,90 @@ const struct bus_type fsl_mc_bus_type = { }; EXPORT_SYMBOL_GPL(fsl_mc_bus_type); -struct device_type fsl_mc_bus_dprc_type = { +const struct device_type fsl_mc_bus_dprc_type = { .name = "fsl_mc_bus_dprc" }; EXPORT_SYMBOL_GPL(fsl_mc_bus_dprc_type); -struct device_type fsl_mc_bus_dpni_type = { +const struct device_type fsl_mc_bus_dpni_type = { .name = "fsl_mc_bus_dpni" }; EXPORT_SYMBOL_GPL(fsl_mc_bus_dpni_type); -struct device_type fsl_mc_bus_dpio_type = { +const struct device_type fsl_mc_bus_dpio_type = { .name = "fsl_mc_bus_dpio" }; EXPORT_SYMBOL_GPL(fsl_mc_bus_dpio_type); -struct device_type fsl_mc_bus_dpsw_type = { +const struct device_type fsl_mc_bus_dpsw_type = { .name = "fsl_mc_bus_dpsw" }; EXPORT_SYMBOL_GPL(fsl_mc_bus_dpsw_type); -struct device_type fsl_mc_bus_dpbp_type = { +const struct device_type fsl_mc_bus_dpbp_type = { .name = "fsl_mc_bus_dpbp" }; EXPORT_SYMBOL_GPL(fsl_mc_bus_dpbp_type); -struct device_type fsl_mc_bus_dpcon_type = { +const struct device_type fsl_mc_bus_dpcon_type = { .name = "fsl_mc_bus_dpcon" }; EXPORT_SYMBOL_GPL(fsl_mc_bus_dpcon_type); -struct device_type fsl_mc_bus_dpmcp_type = { +const struct device_type fsl_mc_bus_dpmcp_type = { .name = "fsl_mc_bus_dpmcp" }; EXPORT_SYMBOL_GPL(fsl_mc_bus_dpmcp_type); -struct device_type fsl_mc_bus_dpmac_type = { +const struct device_type fsl_mc_bus_dpmac_type = { .name = "fsl_mc_bus_dpmac" }; EXPORT_SYMBOL_GPL(fsl_mc_bus_dpmac_type); -struct device_type fsl_mc_bus_dprtc_type = { +const struct device_type fsl_mc_bus_dprtc_type = { .name = "fsl_mc_bus_dprtc" }; EXPORT_SYMBOL_GPL(fsl_mc_bus_dprtc_type); -struct device_type fsl_mc_bus_dpseci_type = { +const struct device_type fsl_mc_bus_dpseci_type = { .name = "fsl_mc_bus_dpseci" }; EXPORT_SYMBOL_GPL(fsl_mc_bus_dpseci_type); -struct device_type fsl_mc_bus_dpdmux_type = { +const struct device_type fsl_mc_bus_dpdmux_type = { .name = "fsl_mc_bus_dpdmux" }; EXPORT_SYMBOL_GPL(fsl_mc_bus_dpdmux_type); -struct device_type fsl_mc_bus_dpdcei_type = { +const struct device_type fsl_mc_bus_dpdcei_type = { .name = "fsl_mc_bus_dpdcei" }; EXPORT_SYMBOL_GPL(fsl_mc_bus_dpdcei_type); -struct device_type fsl_mc_bus_dpaiop_type = { +const struct device_type fsl_mc_bus_dpaiop_type = { .name = "fsl_mc_bus_dpaiop" }; EXPORT_SYMBOL_GPL(fsl_mc_bus_dpaiop_type); -struct device_type fsl_mc_bus_dpci_type = { +const struct device_type fsl_mc_bus_dpci_type = { .name = "fsl_mc_bus_dpci" }; EXPORT_SYMBOL_GPL(fsl_mc_bus_dpci_type); -struct device_type fsl_mc_bus_dpdmai_type = { +const struct device_type fsl_mc_bus_dpdmai_type = { .name = "fsl_mc_bus_dpdmai" }; EXPORT_SYMBOL_GPL(fsl_mc_bus_dpdmai_type); -struct device_type fsl_mc_bus_dpdbg_type = { +const struct device_type fsl_mc_bus_dpdbg_type = { .name = "fsl_mc_bus_dpdbg" }; EXPORT_SYMBOL_GPL(fsl_mc_bus_dpdbg_type); -static struct device_type *fsl_mc_get_device_type(const char *type) +static const struct device_type *fsl_mc_get_device_type(const char *type) { static const struct { - struct device_type *dev_type; + const struct device_type *dev_type; const char *type; } dev_types[] = { { &fsl_mc_bus_dprc_type, "dprc" }, diff --git a/include/linux/fsl/mc.h b/include/linux/fsl/mc.h index c90ec889bfc2..99f30c7d6208 100644 --- a/include/linux/fsl/mc.h +++ b/include/linux/fsl/mc.h @@ -438,21 +438,21 @@ struct fsl_mc_device *fsl_mc_get_endpoint(struct fsl_mc_device *mc_dev, extern const struct bus_type fsl_mc_bus_type; -extern struct device_type fsl_mc_bus_dprc_type; -extern struct device_type fsl_mc_bus_dpni_type; -extern struct device_type fsl_mc_bus_dpio_type; -extern struct device_type fsl_mc_bus_dpsw_type; -extern struct device_type fsl_mc_bus_dpbp_type; -extern struct device_type fsl_mc_bus_dpcon_type; -extern struct device_type fsl_mc_bus_dpmcp_type; -extern struct device_type fsl_mc_bus_dpmac_type; -extern struct device_type fsl_mc_bus_dprtc_type; -extern struct device_type fsl_mc_bus_dpseci_type; -extern struct device_type fsl_mc_bus_dpdmux_type; -extern struct device_type fsl_mc_bus_dpdcei_type; -extern struct device_type fsl_mc_bus_dpaiop_type; -extern struct device_type fsl_mc_bus_dpci_type; -extern struct device_type fsl_mc_bus_dpdmai_type; +extern const struct device_type fsl_mc_bus_dprc_type; +extern const struct device_type fsl_mc_bus_dpni_type; +extern const struct device_type fsl_mc_bus_dpio_type; +extern const struct device_type fsl_mc_bus_dpsw_type; +extern const struct device_type fsl_mc_bus_dpbp_type; +extern const struct device_type fsl_mc_bus_dpcon_type; +extern const struct device_type fsl_mc_bus_dpmcp_type; +extern const struct device_type fsl_mc_bus_dpmac_type; +extern const struct device_type fsl_mc_bus_dprtc_type; +extern const struct device_type fsl_mc_bus_dpseci_type; +extern const struct device_type fsl_mc_bus_dpdmux_type; +extern const struct device_type fsl_mc_bus_dpdcei_type; +extern const struct device_type fsl_mc_bus_dpaiop_type; +extern const struct device_type fsl_mc_bus_dpci_type; +extern const struct device_type fsl_mc_bus_dpdmai_type; static inline bool is_fsl_mc_bus_dprc(const struct fsl_mc_device *mc_dev) { From e128f82f7006991c99a58114f70ef61e937b1ac1 Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Sun, 5 Jan 2025 16:34:02 +0800 Subject: [PATCH 57/68] driver core: class: Fix wild pointer dereferences in API class_dev_iter_next() There are a potential wild pointer dereferences issue regarding APIs class_dev_iter_(init|next|exit)(), as explained by below typical usage: // All members of @iter are wild pointers. struct class_dev_iter iter; // class_dev_iter_init(@iter, @class, ...) checks parameter @class for // potential class_to_subsys() error, and it returns void type and does // not initialize its output parameter @iter, so caller can not detect // the error and continues to invoke class_dev_iter_next(@iter) even if // @iter still contains wild pointers. class_dev_iter_init(&iter, ...); // Dereference these wild pointers in @iter here once suffer the error. while (dev = class_dev_iter_next(&iter)) { ... }; // Also dereference these wild pointers here. class_dev_iter_exit(&iter); Actually, all callers of these APIs have such usage pattern in kernel tree. Fix by: - Initialize output parameter @iter by memset() in class_dev_iter_init() and give callers prompt by pr_crit() for the error. - Check if @iter is valid in class_dev_iter_next(). Fixes: 7b884b7f24b4 ("driver core: class.c: convert to only use class_to_subsys") Reviewed-by: Jonathan Cameron Signed-off-by: Zijun Hu Link: https://lore.kernel.org/r/20250105-class_fix-v6-1-3a2f1768d4d4@quicinc.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/class.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/base/class.c b/drivers/base/class.c index 582b5a02a5c4..d57f277978dc 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -323,8 +323,12 @@ void class_dev_iter_init(struct class_dev_iter *iter, const struct class *class, struct subsys_private *sp = class_to_subsys(class); struct klist_node *start_knode = NULL; - if (!sp) + memset(iter, 0, sizeof(*iter)); + if (!sp) { + pr_crit("%s: class %p was not registered yet\n", + __func__, class); return; + } if (start) start_knode = &start->p->knode_class; @@ -351,6 +355,9 @@ struct device *class_dev_iter_next(struct class_dev_iter *iter) struct klist_node *knode; struct device *dev; + if (!iter->sp) + return NULL; + while (1) { knode = klist_next(&iter->ki); if (!knode) From d1248436cbef1f924c04255367ff4845ccd9025e Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Sun, 5 Jan 2025 16:34:03 +0800 Subject: [PATCH 58/68] blk-cgroup: Fix class @block_class's subsystem refcount leakage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit blkcg_fill_root_iostats() iterates over @block_class's devices by class_dev_iter_(init|next)(), but does not end iterating with class_dev_iter_exit(), so causes the class's subsystem refcount leakage. Fix by ending the iterating with class_dev_iter_exit(). Fixes: ef45fe470e1e ("blk-cgroup: show global disk stats in root cgroup io.stat") Reviewed-by: Michal Koutný Cc: Greg Kroah-Hartman Cc: stable@vger.kernel.org Acked-by: Tejun Heo Signed-off-by: Zijun Hu Link: https://lore.kernel.org/r/20250105-class_fix-v6-2-3a2f1768d4d4@quicinc.com Signed-off-by: Greg Kroah-Hartman --- block/blk-cgroup.c | 1 + 1 file changed, 1 insertion(+) diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c index 45a395862fbc..f1cf7f2909f3 100644 --- a/block/blk-cgroup.c +++ b/block/blk-cgroup.c @@ -1138,6 +1138,7 @@ static void blkcg_fill_root_iostats(void) blkg_iostat_set(&blkg->iostat.cur, &tmp); u64_stats_update_end_irqrestore(&blkg->iostat.sync, flags); } + class_dev_iter_exit(&iter); } static void blkcg_print_one_stat(struct blkcg_gq *blkg, struct seq_file *s) From 3f58ee540d190e9c52b91e055683d15c8ed81112 Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Sun, 5 Jan 2025 16:34:04 +0800 Subject: [PATCH 59/68] driver core: Move true expression out of if condition in 3 device finding APIs For bus_find_device(), driver_find_device(), and device_find_child(), all of their function body have pattern below: { struct klist_iter i; struct device *dev; ... while ((dev = next_device(&i))) if (match(dev, data) && get_device(dev)) break; ... } The expression 'get_device(dev)' in the if condition always returns true since @dev != NULL. Move the expression to if body to make logic of these APIs more clearer. Reviewed-by: Fan Ni Reviewed-by: Jonathan Cameron Signed-off-by: Zijun Hu Link: https://lore.kernel.org/r/20250105-class_fix-v6-3-3a2f1768d4d4@quicinc.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 7 +++++-- drivers/base/core.c | 7 +++++-- drivers/base/driver.c | 7 +++++-- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 657c93c38b0d..73a56f376d3a 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -402,9 +402,12 @@ struct device *bus_find_device(const struct bus_type *bus, klist_iter_init_node(&sp->klist_devices, &i, (start ? &start->p->knode_bus : NULL)); - while ((dev = next_device(&i))) - if (match(dev, data) && get_device(dev)) + while ((dev = next_device(&i))) { + if (match(dev, data)) { + get_device(dev); break; + } + } klist_iter_exit(&i); subsys_put(sp); return dev; diff --git a/drivers/base/core.c b/drivers/base/core.c index d4c20e9b23da..a83a1350fb5b 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -4089,9 +4089,12 @@ struct device *device_find_child(struct device *parent, const void *data, return NULL; klist_iter_init(&parent->p->klist_children, &i); - while ((child = next_device(&i))) - if (match(child, data) && get_device(child)) + while ((child = next_device(&i))) { + if (match(child, data)) { + get_device(child); break; + } + } klist_iter_exit(&i); return child; } diff --git a/drivers/base/driver.c b/drivers/base/driver.c index b4eb5b89c4ee..6f033a741aa7 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -160,9 +160,12 @@ struct device *driver_find_device(const struct device_driver *drv, klist_iter_init_node(&drv->p->klist_devices, &i, (start ? &start->p->knode_driver : NULL)); - while ((dev = next_device(&i))) - if (match(dev, data) && get_device(dev)) + while ((dev = next_device(&i))) { + if (match(dev, data)) { + get_device(dev); break; + } + } klist_iter_exit(&i); return dev; } From ab017a15fdb2222002cdc6bdf86699fb21a0721a Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Sun, 5 Jan 2025 16:34:05 +0800 Subject: [PATCH 60/68] driver core: Rename declaration parameter name for API device_find_child() cluster For APIs: device_find_child() device_for_each_child() device_for_each_child_reverse() Their declaration has parameter name 'dev', but their defination changes the name to 'parent'. Rename declaration name to defination 'parent' to make both have the same name. Reviewed-by: Fan Ni Reviewed-by: Jonathan Cameron Signed-off-by: Zijun Hu Link: https://lore.kernel.org/r/20250105-class_fix-v6-4-3a2f1768d4d4@quicinc.com Signed-off-by: Greg Kroah-Hartman --- include/linux/device.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/linux/device.h b/include/linux/device.h index 0e0bc9bfe0d1..a9d928398895 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -1074,14 +1074,14 @@ void device_del(struct device *dev); DEFINE_FREE(device_del, struct device *, if (_T) device_del(_T)) -int device_for_each_child(struct device *dev, void *data, +int device_for_each_child(struct device *parent, void *data, int (*fn)(struct device *dev, void *data)); -int device_for_each_child_reverse(struct device *dev, void *data, +int device_for_each_child_reverse(struct device *parent, void *data, int (*fn)(struct device *dev, void *data)); int device_for_each_child_reverse_from(struct device *parent, struct device *from, const void *data, int (*fn)(struct device *, const void *)); -struct device *device_find_child(struct device *dev, const void *data, +struct device *device_find_child(struct device *parent, const void *data, device_match_t match); struct device *device_find_child_by_name(struct device *parent, const char *name); From 037116a6cca3e4dbc97905b6e254e8fe7475d502 Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Sun, 5 Jan 2025 16:34:06 +0800 Subject: [PATCH 61/68] driver core: Correct parameter check for API device_for_each_child_reverse_from() device_for_each_child_reverse_from() checks (!parent->p) for its parameter @parent, and that is not consistent with other APIs of its cluster as shown below: device_for_each_child_reverse_from() // check (!parent->p) device_for_each_child_reverse() // check (!parent || !parent->p) device_for_each_child() // same above device_find_child() // same above Correct the API's parameter @parent check by (!parent || !parent->p). Reviewed-by: Jonathan Cameron Signed-off-by: Zijun Hu Link: https://lore.kernel.org/r/20250105-class_fix-v6-5-3a2f1768d4d4@quicinc.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/base/core.c b/drivers/base/core.c index a83a1350fb5b..d3800f0dc5bb 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -4050,7 +4050,7 @@ int device_for_each_child_reverse_from(struct device *parent, struct device *child; int error = 0; - if (!parent->p) + if (!parent || !parent->p) return 0; klist_iter_init_node(&parent->p->klist_children, &i, From 523c6b3ed7702a638e0f8fd02708a7ed4f938269 Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Sun, 5 Jan 2025 16:34:07 +0800 Subject: [PATCH 62/68] driver core: Correct API device_for_each_child_reverse_from() prototype For API device_for_each_child_reverse_from(..., const void *data, int (*fn)(struct device *dev, const void *data)) - Type of @data is const pointer, and means caller's data @*data is not allowed to be modified, but that usually is not proper for such non finding device iterating API. - Types for both @data and @fn are not consistent with all other for_each device iterating APIs device_for_each_child(_reverse)(), bus_for_each_dev() and (driver|class)_for_each_device(). Correct its prototype by removing const from parameter types, then adapt for various existing usages. An dedicated typedef device_iter_t will be introduced as @fn() type for various for_each device interating APIs later. Reviewed-by: Jonathan Cameron Signed-off-by: Zijun Hu Link: https://lore.kernel.org/r/20250105-class_fix-v6-6-3a2f1768d4d4@quicinc.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 4 ++-- drivers/cxl/core/hdm.c | 2 +- drivers/cxl/core/region.c | 2 +- include/linux/device.h | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/base/core.c b/drivers/base/core.c index d3800f0dc5bb..5ee53b3bca6a 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -4043,8 +4043,8 @@ EXPORT_SYMBOL_GPL(device_for_each_child_reverse); * device_for_each_child_reverse_from(); */ int device_for_each_child_reverse_from(struct device *parent, - struct device *from, const void *data, - int (*fn)(struct device *, const void *)) + struct device *from, void *data, + int (*fn)(struct device *, void *)) { struct klist_iter i; struct device *child; diff --git a/drivers/cxl/core/hdm.c b/drivers/cxl/core/hdm.c index 28edd5822486..50e6a45b30ba 100644 --- a/drivers/cxl/core/hdm.c +++ b/drivers/cxl/core/hdm.c @@ -703,7 +703,7 @@ static int cxl_decoder_commit(struct cxl_decoder *cxld) return 0; } -static int commit_reap(struct device *dev, const void *data) +static int commit_reap(struct device *dev, void *data) { struct cxl_port *port = to_cxl_port(dev->parent); struct cxl_decoder *cxld; diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c index bfecd71040c2..a4cff4c266e7 100644 --- a/drivers/cxl/core/region.c +++ b/drivers/cxl/core/region.c @@ -778,7 +778,7 @@ out: return rc; } -static int check_commit_order(struct device *dev, const void *data) +static int check_commit_order(struct device *dev, void *data) { struct cxl_decoder *cxld = to_cxl_decoder(dev); diff --git a/include/linux/device.h b/include/linux/device.h index a9d928398895..025bac08fca7 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -1079,8 +1079,8 @@ int device_for_each_child(struct device *parent, void *data, int device_for_each_child_reverse(struct device *parent, void *data, int (*fn)(struct device *dev, void *data)); int device_for_each_child_reverse_from(struct device *parent, - struct device *from, const void *data, - int (*fn)(struct device *, const void *)); + struct device *from, void *data, + int (*fn)(struct device *, void *)); struct device *device_find_child(struct device *parent, const void *data, device_match_t match); struct device *device_find_child_by_name(struct device *parent, From 767b74e0d1fc7890a94d1770acf05a442474bd87 Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Sun, 5 Jan 2025 16:34:08 +0800 Subject: [PATCH 63/68] driver core: Introduce device_iter_t for device iterating APIs There are several for_each APIs which has parameter with type below: int (*fn)(struct device *dev, void *data) They iterate over various device lists and call @fn() for each device with caller provided data @*data, and they usually need to modify @*data. Give the type an dedicated typedef with advantages shown below: typedef int (*device_iter_t)(struct device *dev, void *data) - Shorter API declarations and definitions - Prevent further for_each APIs from using bad parameter type So introduce device_iter_t and apply it to various existing APIs below: bus_for_each_dev() (class|driver)_for_each_device() device_for_each_child(_reverse|_reverse_from)(). Reviewed-by: Jonathan Cameron Signed-off-by: Zijun Hu Link: https://lore.kernel.org/r/20250105-class_fix-v6-7-3a2f1768d4d4@quicinc.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 2 +- drivers/base/class.c | 2 +- drivers/base/core.c | 6 +++--- drivers/base/driver.c | 2 +- include/linux/device.h | 6 +++--- include/linux/device/bus.h | 7 +++++-- include/linux/device/class.h | 4 ++-- include/linux/device/driver.h | 2 +- 8 files changed, 17 insertions(+), 14 deletions(-) diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 73a56f376d3a..6b9e65a42cd2 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -354,7 +354,7 @@ static struct device *next_device(struct klist_iter *i) * count in the supplied callback. */ int bus_for_each_dev(const struct bus_type *bus, struct device *start, - void *data, int (*fn)(struct device *, void *)) + void *data, device_iter_t fn) { struct subsys_private *sp = bus_to_subsys(bus); struct klist_iter i; diff --git a/drivers/base/class.c b/drivers/base/class.c index d57f277978dc..70ee6a7ba5a3 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -402,7 +402,7 @@ EXPORT_SYMBOL_GPL(class_dev_iter_exit); * code. There's no locking restriction. */ int class_for_each_device(const struct class *class, const struct device *start, - void *data, int (*fn)(struct device *, void *)) + void *data, device_iter_t fn) { struct subsys_private *sp = class_to_subsys(class); struct class_dev_iter iter; diff --git a/drivers/base/core.c b/drivers/base/core.c index 5ee53b3bca6a..7d79a0549ac7 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -3980,7 +3980,7 @@ const char *device_get_devnode(const struct device *dev, * other than 0, we break out and return that value. */ int device_for_each_child(struct device *parent, void *data, - int (*fn)(struct device *dev, void *data)) + device_iter_t fn) { struct klist_iter i; struct device *child; @@ -4010,7 +4010,7 @@ EXPORT_SYMBOL_GPL(device_for_each_child); * other than 0, we break out and return that value. */ int device_for_each_child_reverse(struct device *parent, void *data, - int (*fn)(struct device *dev, void *data)) + device_iter_t fn) { struct klist_iter i; struct device *child; @@ -4044,7 +4044,7 @@ EXPORT_SYMBOL_GPL(device_for_each_child_reverse); */ int device_for_each_child_reverse_from(struct device *parent, struct device *from, void *data, - int (*fn)(struct device *, void *)) + device_iter_t fn) { struct klist_iter i; struct device *child; diff --git a/drivers/base/driver.c b/drivers/base/driver.c index 6f033a741aa7..8ab010ddf709 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -115,7 +115,7 @@ EXPORT_SYMBOL_GPL(driver_set_override); * Iterate over the @drv's list of devices calling @fn for each one. */ int driver_for_each_device(struct device_driver *drv, struct device *start, - void *data, int (*fn)(struct device *, void *)) + void *data, device_iter_t fn) { struct klist_iter i; struct device *dev; diff --git a/include/linux/device.h b/include/linux/device.h index 025bac08fca7..36d1a1607712 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -1075,12 +1075,12 @@ void device_del(struct device *dev); DEFINE_FREE(device_del, struct device *, if (_T) device_del(_T)) int device_for_each_child(struct device *parent, void *data, - int (*fn)(struct device *dev, void *data)); + device_iter_t fn); int device_for_each_child_reverse(struct device *parent, void *data, - int (*fn)(struct device *dev, void *data)); + device_iter_t fn); int device_for_each_child_reverse_from(struct device *parent, struct device *from, void *data, - int (*fn)(struct device *, void *)); + device_iter_t fn); struct device *device_find_child(struct device *parent, const void *data, device_match_t match); struct device *device_find_child_by_name(struct device *parent, diff --git a/include/linux/device/bus.h b/include/linux/device/bus.h index bc3fd74bb763..3d3517da41a1 100644 --- a/include/linux/device/bus.h +++ b/include/linux/device/bus.h @@ -139,9 +139,12 @@ int device_match_acpi_dev(struct device *dev, const void *adev); int device_match_acpi_handle(struct device *dev, const void *handle); int device_match_any(struct device *dev, const void *unused); +/* Device iterating function type for various driver core for_each APIs */ +typedef int (*device_iter_t)(struct device *dev, void *data); + /* iterator helpers for buses */ -int bus_for_each_dev(const struct bus_type *bus, struct device *start, void *data, - int (*fn)(struct device *dev, void *data)); +int bus_for_each_dev(const struct bus_type *bus, struct device *start, + void *data, device_iter_t fn); struct device *bus_find_device(const struct bus_type *bus, struct device *start, const void *data, device_match_t match); /** diff --git a/include/linux/device/class.h b/include/linux/device/class.h index 518c9c83d64b..aa67d4736816 100644 --- a/include/linux/device/class.h +++ b/include/linux/device/class.h @@ -92,8 +92,8 @@ void class_dev_iter_init(struct class_dev_iter *iter, const struct class *class, struct device *class_dev_iter_next(struct class_dev_iter *iter); void class_dev_iter_exit(struct class_dev_iter *iter); -int class_for_each_device(const struct class *class, const struct device *start, void *data, - int (*fn)(struct device *dev, void *data)); +int class_for_each_device(const struct class *class, const struct device *start, + void *data, device_iter_t fn); struct device *class_find_device(const struct class *class, const struct device *start, const void *data, device_match_t match); diff --git a/include/linux/device/driver.h b/include/linux/device/driver.h index 5c04b8e3833b..cd8e0f0a634b 100644 --- a/include/linux/device/driver.h +++ b/include/linux/device/driver.h @@ -154,7 +154,7 @@ void driver_remove_file(const struct device_driver *driver, int driver_set_override(struct device *dev, const char **override, const char *s, size_t len); int __must_check driver_for_each_device(struct device_driver *drv, struct device *start, - void *data, int (*fn)(struct device *dev, void *)); + void *data, device_iter_t fn); struct device *driver_find_device(const struct device_driver *drv, struct device *start, const void *data, device_match_t match); From 51796f5e2960130fe53e9a71d07152622d5e024c Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Sun, 5 Jan 2025 16:34:09 +0800 Subject: [PATCH 64/68] driver core: Move two simple APIs for finding child device to header The following two APIs are for finding child device, and both only have one line code in function body. device_find_child_by_name() device_find_any_child() Move them to header as static inline function. Reviewed-by: Jonathan Cameron Signed-off-by: Zijun Hu Link: https://lore.kernel.org/r/20250105-class_fix-v6-8-3a2f1768d4d4@quicinc.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 32 -------------------------------- include/linux/device.h | 32 +++++++++++++++++++++++++++++--- 2 files changed, 29 insertions(+), 35 deletions(-) diff --git a/drivers/base/core.c b/drivers/base/core.c index 7d79a0549ac7..5a1f05198114 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -4100,38 +4100,6 @@ struct device *device_find_child(struct device *parent, const void *data, } EXPORT_SYMBOL_GPL(device_find_child); -/** - * device_find_child_by_name - device iterator for locating a child device. - * @parent: parent struct device - * @name: name of the child device - * - * This is similar to the device_find_child() function above, but it - * returns a reference to a device that has the name @name. - * - * NOTE: you will need to drop the reference with put_device() after use. - */ -struct device *device_find_child_by_name(struct device *parent, - const char *name) -{ - return device_find_child(parent, name, device_match_name); -} -EXPORT_SYMBOL_GPL(device_find_child_by_name); - -/** - * device_find_any_child - device iterator for locating a child device, if any. - * @parent: parent struct device - * - * This is similar to the device_find_child() function above, but it - * returns a reference to a child device, if any. - * - * NOTE: you will need to drop the reference with put_device() after use. - */ -struct device *device_find_any_child(struct device *parent) -{ - return device_find_child(parent, NULL, device_match_any); -} -EXPORT_SYMBOL_GPL(device_find_any_child); - int __init devices_init(void) { devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL); diff --git a/include/linux/device.h b/include/linux/device.h index 36d1a1607712..1e9aded9a086 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -1083,9 +1083,35 @@ int device_for_each_child_reverse_from(struct device *parent, device_iter_t fn); struct device *device_find_child(struct device *parent, const void *data, device_match_t match); -struct device *device_find_child_by_name(struct device *parent, - const char *name); -struct device *device_find_any_child(struct device *parent); +/** + * device_find_child_by_name - device iterator for locating a child device. + * @parent: parent struct device + * @name: name of the child device + * + * This is similar to the device_find_child() function above, but it + * returns a reference to a device that has the name @name. + * + * NOTE: you will need to drop the reference with put_device() after use. + */ +static inline struct device *device_find_child_by_name(struct device *parent, + const char *name) +{ + return device_find_child(parent, name, device_match_name); +} + +/** + * device_find_any_child - device iterator for locating a child device, if any. + * @parent: parent struct device + * + * This is similar to the device_find_child() function above, but it + * returns a reference to a child device, if any. + * + * NOTE: you will need to drop the reference with put_device() after use. + */ +static inline struct device *device_find_any_child(struct device *parent) +{ + return device_find_child(parent, NULL, device_match_any); +} int device_rename(struct device *dev, const char *new_name); int device_move(struct device *dev, struct device *new_parent, From f554b68eafb6d28c23e3b3f029db363894b72378 Mon Sep 17 00:00:00 2001 From: Kunwu Chan Date: Fri, 23 Aug 2024 16:14:44 +0800 Subject: [PATCH 65/68] ARM: riscpc: make ecard_bus_type constant Since commit d492cc2573a0 ("driver core: device.h: make struct bus_type a const *"), the driver core can properly handle constant struct bus_type, move the ecard_bus_type variable to be a constant structure as well, placing it into read-only memory which can not be modified at runtime. Cc: Greg Kroah-Hartman Suggested-by: Greg Kroah-Hartman Signed-off-by: Kunwu Chan Reviewed-by: Greg Kroah-Hartman Link: https://lore.kernel.org/r/20240823081444.150976-1-kunwu.chan@linux.dev Signed-off-by: Greg Kroah-Hartman --- arch/arm/include/asm/ecard.h | 2 +- arch/arm/mach-rpc/ecard.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/include/asm/ecard.h b/arch/arm/include/asm/ecard.h index 4befe8d2ae19..7cbe001bf9cc 100644 --- a/arch/arm/include/asm/ecard.h +++ b/arch/arm/include/asm/ecard.h @@ -195,7 +195,7 @@ void __iomem *ecardm_iomap(struct expansion_card *ec, unsigned int res, unsigned long offset, unsigned long maxsize); #define ecardm_iounmap(__ec, __addr) devm_iounmap(&(__ec)->dev, __addr) -extern struct bus_type ecard_bus_type; +extern const struct bus_type ecard_bus_type; #define ECARD_DEV(_d) container_of((_d), struct expansion_card, dev) diff --git a/arch/arm/mach-rpc/ecard.c b/arch/arm/mach-rpc/ecard.c index 9f7454b8efa7..2cde4c83b7f9 100644 --- a/arch/arm/mach-rpc/ecard.c +++ b/arch/arm/mach-rpc/ecard.c @@ -1124,7 +1124,7 @@ static int ecard_match(struct device *_dev, const struct device_driver *_drv) return ret; } -struct bus_type ecard_bus_type = { +const struct bus_type ecard_bus_type = { .name = "ecard", .dev_groups = ecard_dev_groups, .match = ecard_match, From 827ed8b1590d4d29dae837283d606709ffeebe37 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Thu, 19 Dec 2024 22:48:17 +0100 Subject: [PATCH 66/68] drivers: core: remove device_link argument from class_compat_[create|remove]_link After 7e722083fcc3 ("i2c: Remove I2C_COMPAT config symbol and related code") there's no caller left passing a non-null device_link argument. So remove this argument to simplify the code. Signed-off-by: Heiner Kallweit Link: https://lore.kernel.org/r/db49131d-fd79-4f23-93f2-0ab541a345fa@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/class.c | 31 +++---------------------------- drivers/vfio/mdev/mdev_core.c | 4 ++-- include/linux/device/class.h | 6 ++---- 3 files changed, 7 insertions(+), 34 deletions(-) diff --git a/drivers/base/class.c b/drivers/base/class.c index 70ee6a7ba5a3..2526c57d924e 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -601,30 +601,10 @@ EXPORT_SYMBOL_GPL(class_compat_unregister); * a bus device * @cls: the compatibility class * @dev: the target bus device - * @device_link: an optional device to which a "device" link should be created */ -int class_compat_create_link(struct class_compat *cls, struct device *dev, - struct device *device_link) +int class_compat_create_link(struct class_compat *cls, struct device *dev) { - int error; - - error = sysfs_create_link(cls->kobj, &dev->kobj, dev_name(dev)); - if (error) - return error; - - /* - * Optionally add a "device" link (typically to the parent), as a - * class device would have one and we want to provide as much - * backwards compatibility as possible. - */ - if (device_link) { - error = sysfs_create_link(&dev->kobj, &device_link->kobj, - "device"); - if (error) - sysfs_remove_link(cls->kobj, dev_name(dev)); - } - - return error; + return sysfs_create_link(cls->kobj, &dev->kobj, dev_name(dev)); } EXPORT_SYMBOL_GPL(class_compat_create_link); @@ -633,14 +613,9 @@ EXPORT_SYMBOL_GPL(class_compat_create_link); * a bus device * @cls: the compatibility class * @dev: the target bus device - * @device_link: an optional device to which a "device" link was previously - * created */ -void class_compat_remove_link(struct class_compat *cls, struct device *dev, - struct device *device_link) +void class_compat_remove_link(struct class_compat *cls, struct device *dev) { - if (device_link) - sysfs_remove_link(&dev->kobj, "device"); sysfs_remove_link(cls->kobj, dev_name(dev)); } EXPORT_SYMBOL_GPL(class_compat_remove_link); diff --git a/drivers/vfio/mdev/mdev_core.c b/drivers/vfio/mdev/mdev_core.c index ed4737de4528..f2e686f8f1ef 100644 --- a/drivers/vfio/mdev/mdev_core.c +++ b/drivers/vfio/mdev/mdev_core.c @@ -76,7 +76,7 @@ int mdev_register_parent(struct mdev_parent *parent, struct device *dev, if (ret) return ret; - ret = class_compat_create_link(mdev_bus_compat_class, dev, NULL); + ret = class_compat_create_link(mdev_bus_compat_class, dev); if (ret) dev_warn(dev, "Failed to create compatibility class link\n"); @@ -98,7 +98,7 @@ void mdev_unregister_parent(struct mdev_parent *parent) dev_info(parent->dev, "MDEV: Unregistering\n"); down_write(&parent->unreg_sem); - class_compat_remove_link(mdev_bus_compat_class, parent->dev, NULL); + class_compat_remove_link(mdev_bus_compat_class, parent->dev); device_for_each_child(parent->dev, NULL, mdev_device_remove_cb); parent_remove_sysfs_files(parent); up_write(&parent->unreg_sem); diff --git a/include/linux/device/class.h b/include/linux/device/class.h index aa67d4736816..45ee3a634999 100644 --- a/include/linux/device/class.h +++ b/include/linux/device/class.h @@ -82,10 +82,8 @@ bool class_is_registered(const struct class *class); struct class_compat; struct class_compat *class_compat_register(const char *name); void class_compat_unregister(struct class_compat *cls); -int class_compat_create_link(struct class_compat *cls, struct device *dev, - struct device *device_link); -void class_compat_remove_link(struct class_compat *cls, struct device *dev, - struct device *device_link); +int class_compat_create_link(struct class_compat *cls, struct device *dev); +void class_compat_remove_link(struct class_compat *cls, struct device *dev); void class_dev_iter_init(struct class_dev_iter *iter, const struct class *class, const struct device *start, const struct device_type *type); From f1725160fd28a2e65e47166637aa44856a1a7f89 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Tue, 7 Jan 2025 13:25:10 +0100 Subject: [PATCH 67/68] devres: add devm_remove_action_nowarn() devm_remove_action() warns if the action to remove does not exist (anymore). The Rust devres abstraction, however, has a use-case to call devm_remove_action() at a point where it can't be guaranteed that the corresponding action hasn't been released yet. In particular, an instance of `Devres` may be dropped after the action has been released. So far, `Devres` worked around this by keeping the inner type alive. Hence, add devm_remove_action_nowarn(), which returns an error code if the action has been removed already. A subsequent patch uses devm_remove_action_nowarn() to remove the action when `Devres` is dropped. Signed-off-by: Danilo Krummrich Link: https://lore.kernel.org/r/20250107122609.8135-1-dakr@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/base/devres.c | 23 ++++++++++++++++++----- include/linux/device.h | 18 +++++++++++++++++- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/drivers/base/devres.c b/drivers/base/devres.c index 2152eec0c135..93e7779ef21e 100644 --- a/drivers/base/devres.c +++ b/drivers/base/devres.c @@ -750,25 +750,38 @@ int __devm_add_action(struct device *dev, void (*action)(void *), void *data, co EXPORT_SYMBOL_GPL(__devm_add_action); /** - * devm_remove_action() - removes previously added custom action + * devm_remove_action_nowarn() - removes previously added custom action * @dev: Device that owns the action * @action: Function implementing the action * @data: Pointer to data passed to @action implementation * * Removes instance of @action previously added by devm_add_action(). * Both action and data should match one of the existing entries. + * + * In contrast to devm_remove_action(), this function does not WARN() if no + * entry could have been found. + * + * This should only be used if the action is contained in an object with + * independent lifetime management, e.g. the Devres rust abstraction. + * + * Causing the warning from regular driver code most likely indicates an abuse + * of the devres API. + * + * Returns: 0 on success, -ENOENT if no entry could have been found. */ -void devm_remove_action(struct device *dev, void (*action)(void *), void *data) +int devm_remove_action_nowarn(struct device *dev, + void (*action)(void *), + void *data) { struct action_devres devres = { .data = data, .action = action, }; - WARN_ON(devres_destroy(dev, devm_action_release, devm_action_match, - &devres)); + return devres_destroy(dev, devm_action_release, devm_action_match, + &devres); } -EXPORT_SYMBOL_GPL(devm_remove_action); +EXPORT_SYMBOL_GPL(devm_remove_action_nowarn); /** * devm_release_action() - release previously added custom action diff --git a/include/linux/device.h b/include/linux/device.h index 1e9aded9a086..80a5b3268986 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -399,7 +399,23 @@ void __iomem *devm_of_iomap(struct device *dev, #endif /* allows to add/remove a custom action to devres stack */ -void devm_remove_action(struct device *dev, void (*action)(void *), void *data); +int devm_remove_action_nowarn(struct device *dev, void (*action)(void *), void *data); + +/** + * devm_remove_action() - removes previously added custom action + * @dev: Device that owns the action + * @action: Function implementing the action + * @data: Pointer to data passed to @action implementation + * + * Removes instance of @action previously added by devm_add_action(). + * Both action and data should match one of the existing entries. + */ +static inline +void devm_remove_action(struct device *dev, void (*action)(void *), void *data) +{ + WARN_ON(devm_remove_action_nowarn(dev, action, data)); +} + void devm_release_action(struct device *dev, void (*action)(void *), void *data); int __devm_add_action(struct device *dev, void (*action)(void *), void *data, const char *name); From 8ff656643d3075154419680470dbfdbd6092e31f Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Tue, 7 Jan 2025 13:25:11 +0100 Subject: [PATCH 68/68] rust: devres: remove action in `Devres::drop` So far `DevresInner` is kept alive, even if `Devres` is dropped until the devres callback is executed to avoid a WARN() when the action has been released already. With the introduction of devm_remove_action_nowarn() we can remove the action in `Devres::drop`, handle the case where the action has been released already and hence also free `DevresInner`. Signed-off-by: Danilo Krummrich Link: https://lore.kernel.org/r/20250107122609.8135-2-dakr@kernel.org Signed-off-by: Greg Kroah-Hartman --- rust/kernel/devres.rs | 47 ++++++++++++++++++++++++++++++++----------- 1 file changed, 35 insertions(+), 12 deletions(-) diff --git a/rust/kernel/devres.rs b/rust/kernel/devres.rs index 9c9dd39584eb..942376f6f3af 100644 --- a/rust/kernel/devres.rs +++ b/rust/kernel/devres.rs @@ -10,15 +10,19 @@ use crate::{ bindings, device::Device, error::{Error, Result}, + ffi::c_void, prelude::*, revocable::Revocable, sync::Arc, + types::ARef, }; use core::ops::Deref; #[pin_data] struct DevresInner { + dev: ARef, + callback: unsafe extern "C" fn(*mut c_void), #[pin] data: Revocable, } @@ -98,6 +102,8 @@ impl DevresInner { fn new(dev: &Device, data: T, flags: Flags) -> Result>> { let inner = Arc::pin_init( pin_init!( DevresInner { + dev: dev.into(), + callback: Self::devres_callback, data <- Revocable::new(data), }), flags, @@ -109,9 +115,8 @@ impl DevresInner { // SAFETY: `devm_add_action` guarantees to call `Self::devres_callback` once `dev` is // detached. - let ret = unsafe { - bindings::devm_add_action(dev.as_raw(), Some(Self::devres_callback), data as _) - }; + let ret = + unsafe { bindings::devm_add_action(dev.as_raw(), Some(inner.callback), data as _) }; if ret != 0 { // SAFETY: We just created another reference to `inner` in order to pass it to @@ -124,6 +129,32 @@ impl DevresInner { Ok(inner) } + fn as_ptr(&self) -> *const Self { + self as _ + } + + fn remove_action(this: &Arc) { + // SAFETY: + // - `self.inner.dev` is a valid `Device`, + // - the `action` and `data` pointers are the exact same ones as given to devm_add_action() + // previously, + // - `self` is always valid, even if the action has been released already. + let ret = unsafe { + bindings::devm_remove_action_nowarn( + this.dev.as_raw(), + Some(this.callback), + this.as_ptr() as _, + ) + }; + + if ret == 0 { + // SAFETY: We leaked an `Arc` reference to devm_add_action() in `DevresInner::new`; if + // devm_remove_action_nowarn() was successful we can (and have to) claim back ownership + // of this reference. + let _ = unsafe { Arc::from_raw(this.as_ptr()) }; + } + } + #[allow(clippy::missing_safety_doc)] unsafe extern "C" fn devres_callback(ptr: *mut kernel::ffi::c_void) { let ptr = ptr as *mut DevresInner; @@ -165,14 +196,6 @@ impl Deref for Devres { impl Drop for Devres { fn drop(&mut self) { - // Revoke the data, such that it gets dropped already and the actual resource is freed. - // - // `DevresInner` has to stay alive until the devres callback has been called. This is - // necessary since we don't know when `Devres` is dropped and calling - // `devm_remove_action()` instead could race with `devres_release_all()`. - // - // SAFETY: When `drop` runs, it's guaranteed that nobody is accessing the revocable data - // anymore, hence it is safe not to wait for the grace period to finish. - unsafe { self.revoke_nosync() }; + DevresInner::remove_action(&self.0); } }