rust-for-linux.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Danilo Krummrich <dakr@kernel.org>
To: Michal Wilczynski <m.wilczynski@samsung.com>
Cc: "Uwe Kleine-König" <ukleinek@kernel.org>,
	"Miguel Ojeda" <ojeda@kernel.org>,
	"Alex Gaynor" <alex.gaynor@gmail.com>,
	"Boqun Feng" <boqun.feng@gmail.com>,
	"Gary Guo" <gary@garyguo.net>,
	"Björn Roy Baron" <bjorn3_gh@protonmail.com>,
	"Andreas Hindborg" <a.hindborg@kernel.org>,
	"Alice Ryhl" <aliceryhl@google.com>,
	"Trevor Gross" <tmgross@umich.edu>, "Guo Ren" <guoren@kernel.org>,
	"Fu Wei" <wefu@redhat.com>, "Rob Herring" <robh@kernel.org>,
	"Krzysztof Kozlowski" <krzk+dt@kernel.org>,
	"Conor Dooley" <conor+dt@kernel.org>,
	"Paul Walmsley" <paul.walmsley@sifive.com>,
	"Palmer Dabbelt" <palmer@dabbelt.com>,
	"Albert Ou" <aou@eecs.berkeley.edu>,
	"Alexandre Ghiti" <alex@ghiti.fr>,
	"Marek Szyprowski" <m.szyprowski@samsung.com>,
	"Benno Lossin" <lossin@kernel.org>,
	"Michael Turquette" <mturquette@baylibre.com>,
	"Drew Fustini" <fustini@kernel.org>,
	linux-kernel@vger.kernel.org, linux-pwm@vger.kernel.org,
	rust-for-linux@vger.kernel.org, linux-riscv@lists.infradead.org,
	devicetree@vger.kernel.org
Subject: Re: [PATCH v7 3/8] rust: pwm: Add core 'Device' and 'Chip' object wrappers
Date: Thu, 3 Jul 2025 23:42:00 +0200	[thread overview]
Message-ID: <aGb5KNWKwTbLgteI@pollux> (raw)
In-Reply-To: <6d9ce601-b81e-4c2a-b9c3-4cba6fa87b8b@samsung.com>

On Thu, Jul 03, 2025 at 01:37:43PM +0200, Michal Wilczynski wrote:
> 
> 
> On 7/2/25 17:13, Danilo Krummrich wrote:
> > On Wed, Jul 02, 2025 at 03:45:31PM +0200, Michal Wilczynski wrote:
> >> Building on the basic data types, this commit introduces the central
> >> object abstractions for the PWM subsystem: Device and Chip. It also
> >> includes the core trait implementations that make the Chip wrapper a
> >> complete, safe, and managed object.
> >>
> >> The main components of this change are:
> >>  - Device and Chip Structs: These structs wrap the underlying struct
> >>    pwm_device and struct pwm_chip C objects, providing safe, idiomatic
> >>    methods to access their fields.
> >>
> >>  - High-Level `Device` API: Exposes safe wrappers for the modern
> >>    `waveform` API, allowing consumers to apply, read, and pre-validate
> >>    hardware configurations.
> >>
> >>  - Core Trait Implementations for Chip:
> >>     - AlwaysRefCounted: Links the Chip's lifetime to its embedded
> >>       struct device reference counter. This enables automatic lifetime
> >>       management via ARef.
> >>     - Send and Sync: Marks the Chip wrapper as safe for use across
> >>       threads. This is sound because the C core handles all necessary
> >>       locking for the underlying object's state.
> >>
> >> These wrappers and traits form a robust foundation for building PWM
> >> drivers in Rust.
> >>
> >> Signed-off-by: Michal Wilczynski <m.wilczynski@samsung.com>
> > 
> > Few more comments below, with those fixed:
> > 
> > 	Reviewed-by: Danilo Krummrich <dakr@kernel.org>
> > 
> >> +/// Wrapper for a PWM device [`struct pwm_device`](srctree/include/linux/pwm.h).
> >> +#[repr(transparent)]
> >> +pub struct Device(Opaque<bindings::pwm_device>);
> >> +
> >> +impl Device {
> > 
> > <snip>
> > 
> >> +    /// Gets a reference to the parent `Chip` that this device belongs to.
> >> +    pub fn chip(&self) -> &Chip {
> >> +        // SAFETY: `self.as_raw()` provides a valid pointer. (*self.as_raw()).chip
> >> +        // is assumed to be a valid pointer to `pwm_chip` managed by the kernel.
> >> +        // Chip::as_ref's safety conditions must be met.
> >> +        unsafe { Chip::as_ref((*self.as_raw()).chip) }
> > 
> > I assume the C API does guarantee that a struct pwm_device *always* holds a
> > valid pointer to a struct pwm_chip?
> > 
> >> +
> >> +/// Wrapper for a PWM chip/controller ([`struct pwm_chip`](srctree/include/linux/pwm.h)).
> >> +#[repr(transparent)]
> >> +pub struct Chip(Opaque<bindings::pwm_chip>);
> >> +
> >> +impl Chip {
> >> +    /// Creates a reference to a [`Chip`] from a valid pointer.
> >> +    ///
> >> +    /// # Safety
> >> +    ///
> >> +    /// The caller must ensure that `ptr` is valid and remains valid for the lifetime of the
> >> +    /// returned [`Chip`] reference.
> >> +    pub(crate) unsafe fn as_ref<'a>(ptr: *mut bindings::pwm_chip) -> &'a Self {
> >> +        // SAFETY: The safety requirements guarantee the validity of the dereference, while the
> >> +        // `Chip` type being transparent makes the cast ok.
> >> +        unsafe { &*ptr.cast::<Self>() }
> >> +    }
> >> +
> >> +    /// Returns a raw pointer to the underlying `pwm_chip`.
> >> +    pub(crate) fn as_raw(&self) -> *mut bindings::pwm_chip {
> >> +        self.0.get()
> >> +    }
> >> +
> >> +    /// Gets the number of PWM channels (hardware PWMs) on this chip.
> >> +    pub fn npwm(&self) -> u32 {
> >> +        // SAFETY: `self.as_raw()` provides a valid pointer for `self`'s lifetime.
> >> +        unsafe { (*self.as_raw()).npwm }
> >> +    }
> >> +
> >> +    /// Returns `true` if the chip supports atomic operations for configuration.
> >> +    pub fn is_atomic(&self) -> bool {
> >> +        // SAFETY: `self.as_raw()` provides a valid pointer for `self`'s lifetime.
> >> +        unsafe { (*self.as_raw()).atomic }
> >> +    }
> >> +
> >> +    /// Returns a reference to the embedded `struct device` abstraction.
> >> +    pub fn device(&self) -> &device::Device {
> >> +        // SAFETY: `self.as_raw()` provides a valid pointer to `bindings::pwm_chip`.
> >> +        // The `dev` field is an instance of `bindings::device` embedded within `pwm_chip`.
> >> +        // Taking a pointer to this embedded field is valid.
> >> +        // `device::Device` is `#[repr(transparent)]`.
> >> +        // The lifetime of the returned reference is tied to `self`.
> >> +        let dev_field_ptr = unsafe { core::ptr::addr_of!((*self.as_raw()).dev) };
> > 
> > I think you can use `&raw` instead.
> > 
> >> +        // SAFETY: `dev_field_ptr` is a valid pointer to `bindings::device`.
> >> +        // Casting and dereferencing is safe due to `repr(transparent)` and lifetime.
> >> +        unsafe { &*(dev_field_ptr.cast::<device::Device>()) }
> > 
> > Please use Device::as_ref() instead.
> > 
> >> +    }
> >> +
> >> +    /// Gets the *typed* driver-specific data associated with this chip's embedded device.
> >> +    pub fn drvdata<T: 'static>(&self) -> &T {
> > 
> > You need to make the whole Chip structure generic over T, i.e.
> > Chip<T: ForeignOwnable>.
> > 
> > Otherwise the API is unsafe, since the caller can pass in any T when calling
> > `chip.drvdata()` regardless of whether you actually stored as private data
> > through Chip::new().
> 
> You were right that the original drvdata<T>() method was unsafe. The
> most direct fix, making Chip generic to Chip<T>, unfortunately creates a
> significant cascade effect:
> 
> - If Chip becomes Chip<T>, then anything holding it, like ARef, must
>   become ARef<Chip<T>>.
> 
> - This in turn forces container structs like Registration to become
>   generic (Registration<T>).
> 
> - Finally, the PwmOps trait itself needs to be aware of T, which
>   complicates the trait and all driver implementations.
> 
> This chain reaction adds a lot of complexity. To avoid it, I've
> figured an alternative:
> 
> The new idea keeps Chip simple and non generic but ensures type safety
> through two main improvements to the abstraction layer:
> 
> 1. A Thread Safe DriverData Wrapper
> 
> The pwm.rs module now provides a generic pwm::DriverData<T> struct. Its
> only job is to wrap the driver's private data and provide the necessary
> unsafe impl Send + Sync.
> 
> // In `rust/kernel/pwm.rs`
> // SAFETY: The contained data is guaranteed by the kernel to have
> // synchronized access during callbacks.
> pub struct DriverData<T>(T);
> unsafe impl<T: Send> Send for DriverData<T> {}
> unsafe impl<T: Sync> Sync for DriverData<T> {}

I think you don't need to implement them explicitly, it's automatically derived
from T.

> 
> // In the driver's `probe` function
> let safe_data = pwm::DriverData::new(Th1520PwmDriverData{ });
> 
> 2. A More Ergonomic PwmOps Trait
> 
> The PwmOps trait methods now receive the driver's data directly as
> &self, which is much more intuitive. We achieve this by providing a
> default associated type for the data owner, which removes boilerplate
> from the driver.
> 
> // In `rust/kernel/pwm.rs`
> pub trait PwmOps: 'static + Sized {
>     type Owner: Deref<Target = DriverData<Self>> + ForeignOwnable =
>         Pin<KBox<DriverData<Self>>>;
>     /// For now I'm getting compiler error here: `associated type defaults are unstable`
>     /// So the driver would need to specify this for now, until this feature
>     /// is stable
> 
> 
>     // Methods now receive `&self`, making them much cleaner to implement.
>     fn round_waveform_tohw(&self, chip: &Chip, pwm: &Device, wf: &Waveform) -> Result<...>;
> }
> 
> // In the driver
> impl pwm::PwmOps for Th1520PwmDriverData {
>     type WfHw = Th1520WfHw;

Shouldn't this be:

	type Owner = Pin<KBox<DriverData<Self>>>;

> 
>     fn round_waveform_tohw(&self, chip: &pwm::Chip, ...) -> Result<...> {

If you accept any ForeignOwnable, I think this has to be Owner::Borrowed.

>         // no drvdata() call here :-)
>         let rate_hz = self.clk.rate().as_hz();
>         // ...
>     }
> }
> 
> This solution seem to address to issue you've pointed (as the user of
> the API never deals with drvdata directly at this point), while making
> it easier to develop PWM drivers in Rust.
> 
> Please let me know what you think.

In DRM [1][2] we used the approach I proposed, but at a first glance what you
propose seems like it should work as well.

Drivers having to set the Owner type seems a bit unfortunate, but otherwise it
seems like a matter of personal preference.

Although, I just notice, isn't this broken if a driver sets Owner to something
else than Self?

[1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/rust/kernel/drm/device.rs
[2] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/rust/kernel/drm/driver.rs

  reply	other threads:[~2025-07-03 21:42 UTC|newest]

Thread overview: 17+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <CGME20250702134953eucas1p2271cd783b615897d24e8432ece4f91cd@eucas1p2.samsung.com>
2025-07-02 13:45 ` [PATCH v7 0/8] Rust Abstractions for PWM subsystem with TH1520 PWM driver Michal Wilczynski
     [not found]   ` <CGME20250702134955eucas1p2ca553f63f44c63a56ba0b2c605edd097@eucas1p2.samsung.com>
2025-07-02 13:45     ` [PATCH v7 1/8] pwm: Expose PWM_WFHWSIZE in public header Michal Wilczynski
2025-07-03  6:39       ` Uwe Kleine-König
     [not found]   ` <CGME20250702134956eucas1p16cacd6588b9f7f9677fba8aa8345524b@eucas1p1.samsung.com>
2025-07-02 13:45     ` [PATCH v7 2/8] rust: pwm: Add Kconfig and basic data structures Michal Wilczynski
     [not found]   ` <CGME20250702134957eucas1p1d84f2ed3014cf98ea3a077c7fae6dea6@eucas1p1.samsung.com>
2025-07-02 13:45     ` [PATCH v7 3/8] rust: pwm: Add core 'Device' and 'Chip' object wrappers Michal Wilczynski
2025-07-02 15:13       ` Danilo Krummrich
2025-07-03  6:42         ` Uwe Kleine-König
2025-07-03 11:37         ` Michal Wilczynski
2025-07-03 21:42           ` Danilo Krummrich [this message]
     [not found]   ` <CGME20250702134958eucas1p26baf0f661006f5b79c31b2afa683baee@eucas1p2.samsung.com>
2025-07-02 13:45     ` [PATCH v7 4/8] rust: pwm: Add driver operations trait and registration support Michal Wilczynski
2025-07-02 15:21       ` Danilo Krummrich
     [not found]   ` <CGME20250702135000eucas1p12d5bc0076b71eb73e5f7de6119903c64@eucas1p1.samsung.com>
2025-07-02 13:45     ` [PATCH v7 5/8] pwm: Add Rust driver for T-HEAD TH1520 SoC Michal Wilczynski
     [not found]   ` <CGME20250702135001eucas1p299eaebac8b9af1efa56184dcdfcfde37@eucas1p2.samsung.com>
2025-07-02 13:45     ` [PATCH v7 6/8] dt-bindings: pwm: thead: Add T-HEAD TH1520 PWM controller Michal Wilczynski
     [not found]   ` <CGME20250702135002eucas1p29d0a0393fec6158a3ca158ea09b61cf1@eucas1p2.samsung.com>
2025-07-02 13:45     ` [PATCH v7 7/8] riscv: dts: thead: Add PWM controller node Michal Wilczynski
2025-07-03 21:14       ` Drew Fustini
     [not found]   ` <CGME20250702135003eucas1p114a5ce5dea469242940b7e2e44a7ad59@eucas1p1.samsung.com>
2025-07-02 13:45     ` [PATCH v7 8/8] riscv: dts: thead: Add PWM fan and thermal control Michal Wilczynski
2025-07-03 21:12       ` Drew Fustini

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=aGb5KNWKwTbLgteI@pollux \
    --to=dakr@kernel.org \
    --cc=a.hindborg@kernel.org \
    --cc=alex.gaynor@gmail.com \
    --cc=alex@ghiti.fr \
    --cc=aliceryhl@google.com \
    --cc=aou@eecs.berkeley.edu \
    --cc=bjorn3_gh@protonmail.com \
    --cc=boqun.feng@gmail.com \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=fustini@kernel.org \
    --cc=gary@garyguo.net \
    --cc=guoren@kernel.org \
    --cc=krzk+dt@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-pwm@vger.kernel.org \
    --cc=linux-riscv@lists.infradead.org \
    --cc=lossin@kernel.org \
    --cc=m.szyprowski@samsung.com \
    --cc=m.wilczynski@samsung.com \
    --cc=mturquette@baylibre.com \
    --cc=ojeda@kernel.org \
    --cc=palmer@dabbelt.com \
    --cc=paul.walmsley@sifive.com \
    --cc=robh@kernel.org \
    --cc=rust-for-linux@vger.kernel.org \
    --cc=tmgross@umich.edu \
    --cc=ukleinek@kernel.org \
    --cc=wefu@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).