Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

rust: pwm: Add UnregisteredChip wrapper around Chip

The `pwm::Registration::register` function provides no guarantee that the
function isn't called twice with the same pwm chip, which is considered
unsafe.

Add `pwm::UnregisteredChip` as wrapper around `pwm::Chip`.
Implement `pwm::UnregisteredChip::register` for the registration. This
function takes ownership of `pwm::UnregisteredChip` and therefore
guarantees that the registration can't be called twice on the same pwm
chip.

Signed-off-by: Markus Probst <markus.probst@posteo.de>
Tested-by: Michal Wilczynski <m.wilczynski@samsung.com>
Acked-by: Michal Wilczynski <m.wilczynski@samsung.com>
Link: https://patch.msgid.link/20251202-pwm_safe_register-v2-1-7a2e0d1e287f@posteo.de
[ukleinek: fixes a typo that Michal pointed out during review]
Signed-off-by: Uwe Kleine-König <ukleinek@kernel.org>

authored by

Markus Probst and committed by
Uwe Kleine-König
85a5ffbd 5025569c

+45 -25
+1 -1
drivers/pwm/pwm_th1520.rs
··· 372 372 }), 373 373 )?; 374 374 375 - pwm::Registration::register(dev, chip)?; 375 + chip.register()?; 376 376 377 377 Ok(Th1520PwmPlatformDriver) 378 378 }
+44 -24
rust/kernel/pwm.rs
··· 16 16 sync::aref::{ARef, AlwaysRefCounted}, 17 17 types::Opaque, // 18 18 }; 19 - use core::{marker::PhantomData, ptr::NonNull}; 19 + use core::{ 20 + marker::PhantomData, 21 + ops::Deref, 22 + ptr::NonNull, // 23 + }; 20 24 21 25 /// Represents a PWM waveform configuration. 22 26 /// Mirrors struct [`struct pwm_waveform`](srctree/include/linux/pwm.h). ··· 178 174 } 179 175 180 176 /// Trait defining the operations for a PWM driver. 181 - pub trait PwmOps: 'static + Sized { 177 + pub trait PwmOps: 'static + Send + Sync + Sized { 182 178 /// The driver-specific hardware representation of a waveform. 183 179 /// 184 180 /// This type must be [`Copy`], [`Default`], and fit within `PWM_WFHWSIZE`. ··· 589 585 /// 590 586 /// Returns an [`ARef<Chip>`] managing the chip's lifetime via refcounting 591 587 /// on its embedded `struct device`. 592 - pub fn new( 593 - parent_dev: &device::Device, 588 + #[allow(clippy::new_ret_no_self)] 589 + pub fn new<'a>( 590 + parent_dev: &'a device::Device<Bound>, 594 591 num_channels: u32, 595 592 data: impl pin_init::PinInit<T, Error>, 596 - ) -> Result<ARef<Self>> { 593 + ) -> Result<UnregisteredChip<'a, T>> { 597 594 let sizeof_priv = core::mem::size_of::<T>(); 598 595 // SAFETY: `pwmchip_alloc` allocates memory for the C struct and our private data. 599 596 let c_chip_ptr_raw = ··· 629 624 // SAFETY: `chip_ptr_as_self` points to a valid `Chip` (layout-compatible with 630 625 // `bindings::pwm_chip`) whose embedded device has refcount 1. 631 626 // `ARef::from_raw` takes this pointer and manages it via `AlwaysRefCounted`. 632 - Ok(unsafe { ARef::from_raw(NonNull::new_unchecked(chip_ptr_as_self)) }) 627 + let chip = unsafe { ARef::from_raw(NonNull::new_unchecked(chip_ptr_as_self)) }; 628 + 629 + Ok(UnregisteredChip { chip, parent_dev }) 633 630 } 634 631 } 635 632 ··· 662 655 // structure's state is managed and synchronized by the kernel's device model 663 656 // and PWM core locking mechanisms. Therefore, it is safe to move the `Chip` 664 657 // wrapper (and the pointer it contains) across threads. 665 - unsafe impl<T: PwmOps + Send> Send for Chip<T> {} 658 + unsafe impl<T: PwmOps> Send for Chip<T> {} 666 659 667 660 // SAFETY: It is safe for multiple threads to have shared access (`&Chip`) because 668 661 // the `Chip` data is immutable from the Rust side without holding the appropriate 669 662 // kernel locks, which the C core is responsible for. Any interior mutability is 670 663 // handled and synchronized by the C kernel code. 671 - unsafe impl<T: PwmOps + Sync> Sync for Chip<T> {} 664 + unsafe impl<T: PwmOps> Sync for Chip<T> {} 672 665 673 - /// A resource guard that ensures `pwmchip_remove` is called on drop. 674 - /// 675 - /// This struct is intended to be managed by the `devres` framework by transferring its ownership 676 - /// via [`devres::register`]. This ties the lifetime of the PWM chip registration 677 - /// to the lifetime of the underlying device. 678 - pub struct Registration<T: PwmOps> { 666 + /// A wrapper around `ARef<Chip<T>>` that ensures that `register` can only be called once. 667 + pub struct UnregisteredChip<'a, T: PwmOps> { 679 668 chip: ARef<Chip<T>>, 669 + parent_dev: &'a device::Device<Bound>, 680 670 } 681 671 682 - impl<T: 'static + PwmOps + Send + Sync> Registration<T> { 672 + impl<T: PwmOps> UnregisteredChip<'_, T> { 683 673 /// Registers a PWM chip with the PWM subsystem. 684 674 /// 685 675 /// Transfers its ownership to the `devres` framework, which ties its lifetime 686 676 /// to the parent device. 687 677 /// On unbind of the parent device, the `devres` entry will be dropped, automatically 688 678 /// calling `pwmchip_remove`. This function should be called from the driver's `probe`. 689 - pub fn register(dev: &device::Device<Bound>, chip: ARef<Chip<T>>) -> Result { 690 - let chip_parent = chip.device().parent().ok_or(EINVAL)?; 691 - if dev.as_raw() != chip_parent.as_raw() { 692 - return Err(EINVAL); 693 - } 694 - 695 - let c_chip_ptr = chip.as_raw(); 679 + pub fn register(self) -> Result<ARef<Chip<T>>> { 680 + let c_chip_ptr = self.chip.as_raw(); 696 681 697 682 // SAFETY: `c_chip_ptr` points to a valid chip with its ops initialized. 698 683 // `__pwmchip_add` is the C function to register the chip with the PWM core. ··· 692 693 to_result(bindings::__pwmchip_add(c_chip_ptr, core::ptr::null_mut()))?; 693 694 } 694 695 695 - let registration = Registration { chip }; 696 + let registration = Registration { 697 + chip: ARef::clone(&self.chip), 698 + }; 696 699 697 - devres::register(dev, registration, GFP_KERNEL) 700 + devres::register(self.parent_dev, registration, GFP_KERNEL)?; 701 + 702 + Ok(self.chip) 698 703 } 704 + } 705 + 706 + impl<T: PwmOps> Deref for UnregisteredChip<'_, T> { 707 + type Target = Chip<T>; 708 + 709 + fn deref(&self) -> &Self::Target { 710 + &self.chip 711 + } 712 + } 713 + 714 + /// A resource guard that ensures `pwmchip_remove` is called on drop. 715 + /// 716 + /// This struct is intended to be managed by the `devres` framework by transferring its ownership 717 + /// via [`devres::register`]. This ties the lifetime of the PWM chip registration 718 + /// to the lifetime of the underlying device. 719 + struct Registration<T: PwmOps> { 720 + chip: ARef<Chip<T>>, 699 721 } 700 722 701 723 impl<T: PwmOps> Drop for Registration<T> {