From: Linus Torvalds Date: Sat, 6 Dec 2025 05:29:02 +0000 (-0800) Subject: Merge tag 'driver-core-6.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git... X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=416f99c3b16f582a3fc6d64a1f77f39d94b76de5;p=thirdparty%2Fkernel%2Fstable.git Merge tag 'driver-core-6.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/driver-core/driver-core Pull driver core updates from Danilo Krummrich: "Arch Topology: - Move parse_acpi_topology() from arm64 to common code for reuse in RISC-V CPU: - Expose housekeeping CPUs through /sys/devices/system/cpu/housekeeping - Print a newline (or 0x0A) instead of '(null)' reading /sys/devices/system/cpu/nohz_full when nohz_full= is not set debugfs - Remove (broken) 'no-mount' mode - Remove redundant access mode checks in debugfs_get_tree() and debugfs_create_*() functions Devres: - Remove unused devm_free_percpu() helper - Move devm_alloc_percpu() from device.h to devres.h Firmware Loader: - Replace simple_strtol() with kstrtoint() - Do not call cancel_store() when no upload is in progress kernfs: - Increase struct super_block::maxbytes to MAX_LFS_FILESIZE - Fix a missing unwind path in __kernfs_new_node() Misc: - Increase the name size in struct auxiliary_device_id to 40 characters - Replace system_unbound_wq with system_dfl_wq and add WQ_PERCPU to alloc_workqueue() Platform: - Replace ERR_PTR() with IOMEM_ERR_PTR() in platform ioremap functions Rust: - Auxiliary: - Unregister auxiliary device on parent device unbind - Move parent() to impl Device; implement device context aware parent() for Device - Illustrate how to safely obtain a driver's device private data when calling from an auxiliary driver into the parant device driver - DebugFs: - Implement support for binary large objects - Device: - Let probe() return the driver's device private data as pinned initializer, i.e. impl PinInit - Implement safe accessor for a driver's device private data for Device (returned reference can't out-live driver binding and guarantees the correct private data type) - Implement AsBusDevice trait, to be used by class device abstractions to derive the bus device type of the parent device - DMA: - Store raw pointer of allocation as NonNull - Use start_ptr() and start_ptr_mut() to inherit correct mutability of self - FS: - Add file::Offset type alias - I2C: - Add abstractions for I2C device / driver infrastructure - Implement abstractions for manual I2C device registrations - I/O: - Use "kernel vertical" style for imports - Define ResourceSize as resource_size_t - Move ResourceSize to top-level I/O module - Add type alias for phys_addr_t - Implement Rust version of read_poll_timeout_atomic() - PCI: - Use "kernel vertical" style for imports - Move I/O and IRQ infrastructure to separate files - Add support for PCI interrupt vectors - Implement TryInto> for IrqVector<'a> to convert an IrqVector bound to specific pci::Device into an IrqRequest bound to the same pci::Device's parent Device - Leverage pin_init_scope() to get rid of redundant Result in IRQ methods - PinInit: - Add {pin_}init_scope() to execute code before creating an initializer - Platform: - Leverage pin_init_scope() to get rid of redundant Result in IRQ methods - Timekeeping: - Implement abstraction of udelay() - Uaccess: - Implement read_slice_partial() and read_slice_file() for UserSliceReader - Implement write_slice_partial() and write_slice_file() for UserSliceWriter sysfs: - Prepare the constification of struct attribute" * tag 'driver-core-6.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/driver-core/driver-core: (75 commits) rust: pci: fix build failure when CONFIG_PCI_MSI is disabled debugfs: Fix default access mode config check debugfs: Remove broken no-mount mode debugfs: Remove redundant access mode checks driver core: Check drivers_autoprobe for all added devices driver core: WQ_PERCPU added to alloc_workqueue users driver core: replace use of system_unbound_wq with system_dfl_wq tick/nohz: Expose housekeeping CPUs in sysfs tick/nohz: avoid showing '(null)' if nohz_full= not set sysfs/cpu: Use DEVICE_ATTR_RO for nohz_full attribute kernfs: fix memory leak of kernfs_iattrs in __kernfs_new_node fs/kernfs: raise sb->maxbytes to MAX_LFS_FILESIZE mod_devicetable: Bump auxiliary_device_id name size sysfs: simplify attribute definition macros samples/kobject: constify 'struct foo_attribute' samples/kobject: add is_visible() callback to attribute group sysfs: attribute_group: enable const variants of is_visible() sysfs: introduce __SYSFS_FUNCTION_ALTERNATIVE() sysfs: transparently handle const pointers in ATTRIBUTE_GROUPS() sysfs: attribute_group: allow registration of const attribute ... --- 416f99c3b16f582a3fc6d64a1f77f39d94b76de5 diff --cc drivers/gpu/nova-core/driver.rs index d91bbc50cde7e,ca0d5f8ad54bb..b8b0cc0f2d935 --- a/drivers/gpu/nova-core/driver.rs +++ b/drivers/gpu/nova-core/driver.rs @@@ -1,20 -1,14 +1,21 @@@ // SPDX-License-Identifier: GPL-2.0 use kernel::{ - auxiliary, c_str, + auxiliary, + c_str, device::Core, + devres::Devres, + dma::Device, + dma::DmaMask, pci, - pci::{Class, ClassMask, Vendor}, + pci::{ + Class, + ClassMask, + Vendor, // + }, prelude::*, sizes::SZ_16M, - sync::Arc, + sync::Arc, // }; use crate::gpu::Gpu; @@@ -67,31 -53,21 +69,26 @@@ impl pci::Driver for NovaCore type IdInfo = (); const ID_TABLE: pci::IdTable = &PCI_TABLE; - fn probe(pdev: &pci::Device, _info: &Self::IdInfo) -> Result>> { - dev_dbg!(pdev.as_ref(), "Probe Nova Core GPU driver.\n"); - - pdev.enable_device_mem()?; - pdev.set_master(); + fn probe(pdev: &pci::Device, _info: &Self::IdInfo) -> impl PinInit { + pin_init::pin_init_scope(move || { + dev_dbg!(pdev.as_ref(), "Probe Nova Core GPU driver.\n"); - // SAFETY: No concurrent DMA allocations or mappings can be made because - // the device is still being probed and therefore isn't being used by - // other threads of execution. - unsafe { pdev.dma_set_mask_and_coherent(DmaMask::new::())? }; + pdev.enable_device_mem()?; + pdev.set_master(); - let devres_bar = Arc::pin_init( - pdev.iomap_region_sized::(0, c_str!("nova-core/bar0")), - GFP_KERNEL, - )?; ++ // SAFETY: No concurrent DMA allocations or mappings can be made because ++ // the device is still being probed and therefore isn't being used by ++ // other threads of execution. ++ unsafe { pdev.dma_set_mask_and_coherent(DmaMask::new::())? }; + - // Used to provided a `&Bar0` to `Gpu::new` without tying it to the lifetime of - // `devres_bar`. - let bar_clone = Arc::clone(&devres_bar); - let bar = bar_clone.access(pdev.as_ref())?; + let bar = Arc::pin_init( + pdev.iomap_region_sized::(0, c_str!("nova-core/bar0")), + GFP_KERNEL, + )?; - let this = KBox::pin_init( - try_pin_init!(Self { - gpu <- Gpu::new(pdev, devres_bar, bar), - _reg: auxiliary::Registration::new( + Ok(try_pin_init!(Self { + gpu <- Gpu::new(pdev, bar.clone(), bar.access(pdev.as_ref())?), + _reg <- auxiliary::Registration::new( pdev.as_ref(), c_str!("nova-drm"), 0, // TODO[XARR]: Once it lands, use XArray; for now we don't use the ID. diff --cc drivers/pwm/pwm_th1520.rs index 955c359b07fb4,0000000000000..e3b7e77356fc2 mode 100644,000000..100644 --- a/drivers/pwm/pwm_th1520.rs +++ b/drivers/pwm/pwm_th1520.rs @@@ -1,387 -1,0 +1,387 @@@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2025 Samsung Electronics Co., Ltd. +// Author: Michal Wilczynski + +//! Rust T-HEAD TH1520 PWM driver +//! +//! Limitations: +//! - The period and duty cycle are controlled by 32-bit hardware registers, +//! limiting the maximum resolution. +//! - The driver supports continuous output mode only; one-shot mode is not +//! implemented. +//! - The controller hardware provides up to 6 PWM channels. +//! - Reconfiguration is glitch free - new period and duty cycle values are +//! latched and take effect at the start of the next period. +//! - Polarity is handled via a simple hardware inversion bit; arbitrary +//! duty cycle offsets are not supported. +//! - Disabling a channel is achieved by configuring its duty cycle to zero to +//! produce a static low output. Clearing the `start` does not reliably +//! force the static inactive level defined by the `INACTOUT` bit. Hence +//! this method is not used in this driver. +//! + +use core::ops::Deref; +use kernel::{ + c_str, + clk::Clk, + device::{Bound, Core, Device}, + devres, + io::mem::IoMem, + of, platform, + prelude::*, + pwm, time, +}; + +const TH1520_MAX_PWM_NUM: u32 = 6; + +// Register offsets +const fn th1520_pwm_chn_base(n: u32) -> usize { + (n * 0x20) as usize +} + +const fn th1520_pwm_ctrl(n: u32) -> usize { + th1520_pwm_chn_base(n) +} + +const fn th1520_pwm_per(n: u32) -> usize { + th1520_pwm_chn_base(n) + 0x08 +} + +const fn th1520_pwm_fp(n: u32) -> usize { + th1520_pwm_chn_base(n) + 0x0c +} + +// Control register bits +const TH1520_PWM_START: u32 = 1 << 0; +const TH1520_PWM_CFG_UPDATE: u32 = 1 << 2; +const TH1520_PWM_CONTINUOUS_MODE: u32 = 1 << 5; +const TH1520_PWM_FPOUT: u32 = 1 << 8; + +const TH1520_PWM_REG_SIZE: usize = 0xB0; + +fn ns_to_cycles(ns: u64, rate_hz: u64) -> u64 { + const NSEC_PER_SEC_U64: u64 = time::NSEC_PER_SEC as u64; + + (match ns.checked_mul(rate_hz) { + Some(product) => product, + None => u64::MAX, + }) / NSEC_PER_SEC_U64 +} + +fn cycles_to_ns(cycles: u64, rate_hz: u64) -> u64 { + const NSEC_PER_SEC_U64: u64 = time::NSEC_PER_SEC as u64; + + // TODO: Replace with a kernel helper like `mul_u64_u64_div_u64_roundup` + // once available in Rust. + let numerator = cycles + .saturating_mul(NSEC_PER_SEC_U64) + .saturating_add(rate_hz - 1); + + numerator / rate_hz +} + +/// Hardware-specific waveform representation for TH1520. +#[derive(Copy, Clone, Debug, Default)] +struct Th1520WfHw { + period_cycles: u32, + duty_cycles: u32, + ctrl_val: u32, + enabled: bool, +} + +/// The driver's private data struct. It holds all necessary devres managed resources. +#[pin_data(PinnedDrop)] +struct Th1520PwmDriverData { + #[pin] + iomem: devres::Devres>, + clk: Clk, +} + +// This `unsafe` implementation is a temporary necessity because the underlying `kernel::clk::Clk` +// type does not yet expose `Send` and `Sync` implementations. This block should be removed +// as soon as the clock abstraction provides these guarantees directly. +// TODO: Remove those unsafe impl's when Clk will support them itself. + +// SAFETY: The `devres` framework requires the driver's private data to be `Send` and `Sync`. +// We can guarantee this because the PWM core synchronizes all callbacks, preventing concurrent +// access to the contained `iomem` and `clk` resources. +unsafe impl Send for Th1520PwmDriverData {} + +// SAFETY: The same reasoning applies as for `Send`. The PWM core's synchronization +// guarantees that it is safe for multiple threads to have shared access (`&self`) +// to the driver data during callbacks. +unsafe impl Sync for Th1520PwmDriverData {} + +impl pwm::PwmOps for Th1520PwmDriverData { + type WfHw = Th1520WfHw; + + fn round_waveform_tohw( + chip: &pwm::Chip, + _pwm: &pwm::Device, + wf: &pwm::Waveform, + ) -> Result> { + let data = chip.drvdata(); + let mut status = 0; + + if wf.period_length_ns == 0 { + dev_dbg!(chip.device(), "Requested period is 0, disabling PWM.\n"); + + return Ok(pwm::RoundedWaveform { + status: 0, + hardware_waveform: Th1520WfHw { + enabled: false, + ..Default::default() + }, + }); + } + + let rate_hz = data.clk.rate().as_hz() as u64; + + let mut period_cycles = ns_to_cycles(wf.period_length_ns, rate_hz).min(u64::from(u32::MAX)); + + if period_cycles == 0 { + dev_dbg!( + chip.device(), + "Requested period {} ns is too small for clock rate {} Hz, rounding up.\n", + wf.period_length_ns, + rate_hz + ); + + period_cycles = 1; + status = 1; + } + + let mut duty_cycles = ns_to_cycles(wf.duty_length_ns, rate_hz).min(u64::from(u32::MAX)); + + let mut ctrl_val = TH1520_PWM_CONTINUOUS_MODE; + + let is_inversed = wf.duty_length_ns > 0 + && wf.duty_offset_ns > 0 + && wf.duty_offset_ns >= wf.period_length_ns.saturating_sub(wf.duty_length_ns); + if is_inversed { + duty_cycles = period_cycles - duty_cycles; + } else { + ctrl_val |= TH1520_PWM_FPOUT; + } + + let wfhw = Th1520WfHw { + // The cast is safe because the value was clamped with `.min(u64::from(u32::MAX))`. + period_cycles: period_cycles as u32, + duty_cycles: duty_cycles as u32, + ctrl_val, + enabled: true, + }; + + dev_dbg!( + chip.device(), + "Requested: {}/{} ns [+{} ns] -> HW: {}/{} cycles, ctrl 0x{:x}, rate {} Hz\n", + wf.duty_length_ns, + wf.period_length_ns, + wf.duty_offset_ns, + wfhw.duty_cycles, + wfhw.period_cycles, + wfhw.ctrl_val, + rate_hz + ); + + Ok(pwm::RoundedWaveform { + status, + hardware_waveform: wfhw, + }) + } + + fn round_waveform_fromhw( + chip: &pwm::Chip, + _pwm: &pwm::Device, + wfhw: &Self::WfHw, + wf: &mut pwm::Waveform, + ) -> Result { + let data = chip.drvdata(); + let rate_hz = data.clk.rate().as_hz() as u64; + + if wfhw.period_cycles == 0 { + dev_dbg!( + chip.device(), + "HW state has zero period, reporting as disabled.\n" + ); + *wf = pwm::Waveform::default(); + return Ok(()); + } + + wf.period_length_ns = cycles_to_ns(u64::from(wfhw.period_cycles), rate_hz); + + let duty_cycles = u64::from(wfhw.duty_cycles); + + if (wfhw.ctrl_val & TH1520_PWM_FPOUT) != 0 { + wf.duty_length_ns = cycles_to_ns(duty_cycles, rate_hz); + wf.duty_offset_ns = 0; + } else { + let period_cycles = u64::from(wfhw.period_cycles); + let original_duty_cycles = period_cycles.saturating_sub(duty_cycles); + + // For an inverted signal, `duty_length_ns` is the high time (period - low_time). + wf.duty_length_ns = cycles_to_ns(original_duty_cycles, rate_hz); + // The offset is the initial low time, which is what the hardware register provides. + wf.duty_offset_ns = cycles_to_ns(duty_cycles, rate_hz); + } + + Ok(()) + } + + fn read_waveform( + chip: &pwm::Chip, + pwm: &pwm::Device, + parent_dev: &Device, + ) -> Result { + let data = chip.drvdata(); + let hwpwm = pwm.hwpwm(); + let iomem_accessor = data.iomem.access(parent_dev)?; + let iomap = iomem_accessor.deref(); + + let ctrl = iomap.try_read32(th1520_pwm_ctrl(hwpwm))?; + let period_cycles = iomap.try_read32(th1520_pwm_per(hwpwm))?; + let duty_cycles = iomap.try_read32(th1520_pwm_fp(hwpwm))?; + + let wfhw = Th1520WfHw { + period_cycles, + duty_cycles, + ctrl_val: ctrl, + enabled: duty_cycles != 0, + }; + + dev_dbg!( + chip.device(), + "PWM-{}: read_waveform: Read hw state - period: {}, duty: {}, ctrl: 0x{:x}, enabled: {}", + hwpwm, + wfhw.period_cycles, + wfhw.duty_cycles, + wfhw.ctrl_val, + wfhw.enabled + ); + + Ok(wfhw) + } + + fn write_waveform( + chip: &pwm::Chip, + pwm: &pwm::Device, + wfhw: &Self::WfHw, + parent_dev: &Device, + ) -> Result { + let data = chip.drvdata(); + let hwpwm = pwm.hwpwm(); + let iomem_accessor = data.iomem.access(parent_dev)?; + let iomap = iomem_accessor.deref(); + let duty_cycles = iomap.try_read32(th1520_pwm_fp(hwpwm))?; + let was_enabled = duty_cycles != 0; + + if !wfhw.enabled { + dev_dbg!(chip.device(), "PWM-{}: Disabling channel.\n", hwpwm); + if was_enabled { + iomap.try_write32(wfhw.ctrl_val, th1520_pwm_ctrl(hwpwm))?; + iomap.try_write32(0, th1520_pwm_fp(hwpwm))?; + iomap.try_write32( + wfhw.ctrl_val | TH1520_PWM_CFG_UPDATE, + th1520_pwm_ctrl(hwpwm), + )?; + } + return Ok(()); + } + + iomap.try_write32(wfhw.ctrl_val, th1520_pwm_ctrl(hwpwm))?; + iomap.try_write32(wfhw.period_cycles, th1520_pwm_per(hwpwm))?; + iomap.try_write32(wfhw.duty_cycles, th1520_pwm_fp(hwpwm))?; + iomap.try_write32( + wfhw.ctrl_val | TH1520_PWM_CFG_UPDATE, + th1520_pwm_ctrl(hwpwm), + )?; + + // The `TH1520_PWM_START` bit must be written in a separate, final transaction, and + // only when enabling the channel from a disabled state. + if !was_enabled { + iomap.try_write32(wfhw.ctrl_val | TH1520_PWM_START, th1520_pwm_ctrl(hwpwm))?; + } + + dev_dbg!( + chip.device(), + "PWM-{}: Wrote {}/{} cycles", + hwpwm, + wfhw.duty_cycles, + wfhw.period_cycles, + ); + + Ok(()) + } +} + +#[pinned_drop] +impl PinnedDrop for Th1520PwmDriverData { + fn drop(self: Pin<&mut Self>) { + self.clk.disable_unprepare(); + } +} + +struct Th1520PwmPlatformDriver; + +kernel::of_device_table!( + OF_TABLE, + MODULE_OF_TABLE, + ::IdInfo, + [(of::DeviceId::new(c_str!("thead,th1520-pwm")), ())] +); + +impl platform::Driver for Th1520PwmPlatformDriver { + type IdInfo = (); + const OF_ID_TABLE: Option> = Some(&OF_TABLE); + + fn probe( + pdev: &platform::Device, + _id_info: Option<&Self::IdInfo>, - ) -> Result>> { ++ ) -> impl PinInit { + let dev = pdev.as_ref(); + let request = pdev.io_request_by_index(0).ok_or(ENODEV)?; + + let clk = Clk::get(dev, None)?; + + clk.prepare_enable()?; + + // TODO: Get exclusive ownership of the clock to prevent rate changes. + // The Rust equivalent of `clk_rate_exclusive_get()` is not yet available. + // This should be updated once it is implemented. + let rate_hz = clk.rate().as_hz(); + if rate_hz == 0 { + dev_err!(dev, "Clock rate is zero\n"); + return Err(EINVAL); + } + + if rate_hz > time::NSEC_PER_SEC as usize { + dev_err!( + dev, + "Clock rate {} Hz is too high, not supported.\n", + rate_hz + ); + return Err(EINVAL); + } + + let chip = pwm::Chip::new( + dev, + TH1520_MAX_PWM_NUM, + try_pin_init!(Th1520PwmDriverData { + iomem <- request.iomap_sized::(), + clk <- clk, + }), + )?; + + pwm::Registration::register(dev, chip)?; + - Ok(KBox::new(Th1520PwmPlatformDriver, GFP_KERNEL)?.into()) ++ Ok(Th1520PwmPlatformDriver) + } +} + +kernel::module_pwm_platform_driver! { + type: Th1520PwmPlatformDriver, + name: "pwm-th1520", + authors: ["Michal Wilczynski "], + description: "T-HEAD TH1520 PWM driver", + license: "GPL v2", +} diff --cc rust/kernel/debugfs/file_ops.rs index 9ad5e3fa6f699,6c8928902a0b8..8a0442d6dd7a4 --- a/rust/kernel/debugfs/file_ops.rs +++ b/rust/kernel/debugfs/file_ops.rs @@@ -1,9 -1,9 +1,10 @@@ // SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2025 Google LLC. - use super::{Reader, Writer}; + use super::{BinaryReader, BinaryWriter, Reader, Writer}; use crate::debugfs::callback_adapters::Adapter; +use crate::fmt; + use crate::fs::file; use crate::prelude::*; use crate::seq_file::SeqFile; use crate::seq_print; diff --cc rust/kernel/debugfs/traits.rs index e8a8a98f18dca,82441ac8adaab..3eee60463fd59 --- a/rust/kernel/debugfs/traits.rs +++ b/rust/kernel/debugfs/traits.rs @@@ -3,12 -3,20 +3,17 @@@ //! Traits for rendering or updating values exported to DebugFS. + use crate::alloc::Allocator; +use crate::fmt; + use crate::fs::file; use crate::prelude::*; +use crate::sync::atomic::{Atomic, AtomicBasicOps, AtomicType, Relaxed}; + use crate::sync::Arc; use crate::sync::Mutex; - use crate::uaccess::UserSliceReader; + use crate::transmute::{AsBytes, FromBytes}; + use crate::uaccess::{UserSliceReader, UserSliceWriter}; -use core::fmt::{self, Debug, Formatter}; + use core::ops::{Deref, DerefMut}; use core::str::FromStr; -use core::sync::atomic::{ - AtomicI16, AtomicI32, AtomicI64, AtomicI8, AtomicIsize, AtomicU16, AtomicU32, AtomicU64, - AtomicU8, AtomicUsize, Ordering, -}; /// A trait for types that can be written into a string. /// @@@ -63,21 -175,164 +172,148 @@@ impl Reader for Mut } } +impl Reader for Atomic +where + T::Repr: AtomicBasicOps, +{ + fn read_from_slice(&self, reader: &mut UserSliceReader) -> Result { + let mut buf = [0u8; 21]; // Enough for a 64-bit number. + if reader.len() > buf.len() { + return Err(EINVAL); + } + let n = reader.len(); + reader.read_slice(&mut buf[..n])?; + + let s = core::str::from_utf8(&buf[..n]).map_err(|_| EINVAL)?; + let val = s.trim().parse::().map_err(|_| EINVAL)?; + self.store(val, Relaxed); + Ok(()) + } +} ++ + /// Trait for types that can be constructed from a binary representation. + /// + /// See also [`BinaryReader`] for interior mutability. + pub trait BinaryReaderMut { + /// Reads the binary form of `self` from `reader`. + /// + /// Same as [`BinaryReader::read_from_slice`], but takes a mutable reference. + /// + /// `offset` is the requested offset into the binary representation of `self`. + /// + /// On success, returns the number of bytes read from `reader`. + fn read_from_slice_mut( + &mut self, + reader: &mut UserSliceReader, + offset: &mut file::Offset, + ) -> Result; + } + + // Base implementation for any `T: AsBytes + FromBytes`. + impl BinaryReaderMut for T { + fn read_from_slice_mut( + &mut self, + reader: &mut UserSliceReader, + offset: &mut file::Offset, + ) -> Result { + reader.read_slice_file(self.as_bytes_mut(), offset) + } + } + + // Delegate for `Box`: Support a `Box` with an outer lock. + impl BinaryReaderMut for Box { + fn read_from_slice_mut( + &mut self, + reader: &mut UserSliceReader, + offset: &mut file::Offset, + ) -> Result { + self.deref_mut().read_from_slice_mut(reader, offset) + } + } + + // Delegate for `Vec`: Support a `Vec` with an outer lock. + impl BinaryReaderMut for Vec + where + T: AsBytes + FromBytes, + A: Allocator, + { + fn read_from_slice_mut( + &mut self, + reader: &mut UserSliceReader, + offset: &mut file::Offset, + ) -> Result { + let slice = self.as_mut_slice(); + + // SAFETY: `T: AsBytes + FromBytes` allows us to treat `&mut [T]` as `&mut [u8]`. + let buffer = unsafe { + core::slice::from_raw_parts_mut( + slice.as_mut_ptr().cast(), + core::mem::size_of_val(slice), + ) + }; + + reader.read_slice_file(buffer, offset) + } + } + + /// Trait for types that can be constructed from a binary representation. + /// + /// See also [`BinaryReaderMut`] for the mutable version. + pub trait BinaryReader { + /// Reads the binary form of `self` from `reader`. + /// + /// `offset` is the requested offset into the binary representation of `self`. + /// + /// On success, returns the number of bytes read from `reader`. + fn read_from_slice( + &self, + reader: &mut UserSliceReader, + offset: &mut file::Offset, + ) -> Result; + } + + // Delegate for `Mutex`: Support a `T` with an outer `Mutex`. + impl BinaryReader for Mutex { + fn read_from_slice( + &self, + reader: &mut UserSliceReader, + offset: &mut file::Offset, + ) -> Result { + let mut this = self.lock(); + + this.read_from_slice_mut(reader, offset) + } + } + + // Delegate for `Box`: Support a `Box` with an inner lock. + impl BinaryReader for Box { + fn read_from_slice( + &self, + reader: &mut UserSliceReader, + offset: &mut file::Offset, + ) -> Result { + self.deref().read_from_slice(reader, offset) + } + } + + // Delegate for `Pin>`: Support a `Pin>` with an inner lock. + impl BinaryReader for Pin> { + fn read_from_slice( + &self, + reader: &mut UserSliceReader, + offset: &mut file::Offset, + ) -> Result { + self.deref().read_from_slice(reader, offset) + } + } + + // Delegate for `Arc`: Support an `Arc` with an inner lock. + impl BinaryReader for Arc { + fn read_from_slice( + &self, + reader: &mut UserSliceReader, + offset: &mut file::Offset, + ) -> Result { + self.deref().read_from_slice(reader, offset) + } + } - -macro_rules! impl_reader_for_atomic { - ($(($atomic_type:ty, $int_type:ty)),*) => { - $( - impl Reader for $atomic_type { - fn read_from_slice(&self, reader: &mut UserSliceReader) -> Result { - let mut buf = [0u8; 21]; // Enough for a 64-bit number. - if reader.len() > buf.len() { - return Err(EINVAL); - } - let n = reader.len(); - reader.read_slice(&mut buf[..n])?; - - let s = core::str::from_utf8(&buf[..n]).map_err(|_| EINVAL)?; - let val = s.trim().parse::<$int_type>().map_err(|_| EINVAL)?; - self.store(val, Ordering::Relaxed); - Ok(()) - } - } - )* - }; -} - -impl_reader_for_atomic!( - (AtomicI16, i16), - (AtomicI32, i32), - (AtomicI64, i64), - (AtomicI8, i8), - (AtomicIsize, isize), - (AtomicU16, u16), - (AtomicU32, u32), - (AtomicU64, u64), - (AtomicU8, u8), - (AtomicUsize, usize) -); diff --cc rust/kernel/pci/id.rs index 5f5d59ff49fca,a1de70b2176a7..c09125946d9e1 --- a/rust/kernel/pci/id.rs +++ b/rust/kernel/pci/id.rs @@@ -4,7 -4,11 +4,11 @@@ //! //! This module contains PCI class codes, Vendor IDs, and supporting types. - use crate::{bindings, error::code::EINVAL, error::Error, fmt, prelude::*}; + use crate::{ + bindings, ++ fmt, + prelude::*, // + }; -use core::fmt; /// PCI device class codes. /// diff --cc samples/rust/rust_debugfs.rs index 711faa07bece3,c45b568d951b4..025e8f9d12dee --- a/samples/rust/rust_debugfs.rs +++ b/samples/rust/rust_debugfs.rs @@@ -36,8 -38,9 +36,9 @@@ use kernel::c_str use kernel::debugfs::{Dir, File}; use kernel::new_mutex; use kernel::prelude::*; + use kernel::sizes::*; +use kernel::sync::atomic::{Atomic, Relaxed}; use kernel::sync::Mutex; - use kernel::{acpi, device::Core, of, platform, str::CString, types::ARef}; kernel::module_platform_driver! { @@@ -57,9 -60,13 +58,13 @@@ struct RustDebugFs #[pin] _compatible: File, #[pin] - counter: File, + counter: File>, #[pin] inner: File>, + #[pin] + array_blob: File>, + #[pin] + vector_blob: File>>, } #[derive(Debug)] @@@ -104,16 -111,17 +109,17 @@@ impl platform::Driver for RustDebugFs fn probe( pdev: &platform::Device, _info: Option<&Self::IdInfo>, - ) -> Result>> { - let result = KBox::try_pin_init(RustDebugFs::new(pdev), GFP_KERNEL)?; - // We can still mutate fields through the files which are atomic or mutexed: - result.counter.store(91, Relaxed); - { - let mut guard = result.inner.lock(); - guard.x = guard.y; - guard.y = 42; - } - Ok(result) + ) -> impl PinInit { + RustDebugFs::new(pdev).pin_chain(|this| { - this.counter.store(91, Ordering::Relaxed); ++ this.counter.store(91, Relaxed); + { + let mut guard = this.inner.lock(); + guard.x = guard.y; + guard.y = 42; + } + + Ok(()) + }) } } diff --cc samples/rust/rust_debugfs_scoped.rs index 406e6588b17a1,c80312cf168d5..702a6546d3fbe --- a/samples/rust/rust_debugfs_scoped.rs +++ b/samples/rust/rust_debugfs_scoped.rs @@@ -6,9 -6,10 +6,10 @@@ //! `Scope::dir` to create a variety of files without the need to separately //! track them all. -use core::sync::atomic::AtomicUsize; use kernel::debugfs::{Dir, Scope}; use kernel::prelude::*; + use kernel::sizes::*; +use kernel::sync::atomic::Atomic; use kernel::sync::Mutex; use kernel::{c_str, new_mutex, str::CString}; @@@ -109,7 -114,8 +114,8 @@@ impl ModuleData struct DeviceData { name: CString, - nums: KVec, + nums: KVec>, + blob: Pin>>, } fn init_control(base_dir: &Dir, dyn_dirs: Dir) -> impl PinInit> + '_ {