From: Alexandre Belloni <alexandre.belloni@bootlin.com>
To: "Rafael J. Wysocki" <rafael@kernel.org>
Cc: "Danilo Krummrich" <dakr@kernel.org>,
"Alvin Sun" <alvin.sun@linux.dev>,
"Miguel Ojeda" <ojeda@kernel.org>,
"Boqun Feng" <boqun.feng@gmail.com>,
"Gary Guo" <gary@garyguo.net>,
"Björn Roy Baron" <bjorn3_gh@protonmail.com>,
"Benno Lossin" <lossin@kernel.org>,
"Andreas Hindborg" <a.hindborg@kernel.org>,
"Alice Ryhl" <aliceryhl@google.com>,
"Trevor Gross" <tmgross@umich.edu>,
linux-rtc@vger.kernel.org, rust-for-linux@vger.kernel.org,
"Greg Kroah-Hartman" <gregkh@linuxfoundation.org>
Subject: Re: [RFC PATCH v3 1/5] rtc: add device selector for rtc_class_ops callbacks
Date: Tue, 24 Feb 2026 15:57:21 +0100 [thread overview]
Message-ID: <20260224145721a9e69e4d@mail.local> (raw)
In-Reply-To: <CAJZ5v0habtTt32bTiJF2keifwea4i0=j_s_x7AzOn_xmaO+RWQ@mail.gmail.com>
On 24/02/2026 14:28:48+0100, Rafael J. Wysocki wrote:
> On Tue, Feb 24, 2026 at 1:12 AM Danilo Krummrich <dakr@kernel.org> wrote:
> >
> > On Sun Feb 22, 2026 at 5:13 PM CET, Danilo Krummrich wrote:
> > > There is no limitation from the Rust side of things, as mentioned in [1], this
> > > should work perfectly fine, but it might be slightly less convinient:
> >
> > When I wrote this on Sunday, I should have been much more precise about this,
> > since it actually does not work perfectly fine without further changes that
> > would (re-)introduce ordering constraints for drivers on the probe() side of
> > things that I want to avoid.
> >
> > The "re-" is in braces as we never had those ordering constraints in Rust, but
> > we have quite a lot of them in C, where they cause endless problems. Which is
> > why I want to avoid them in Rust.
> >
> > > Example 1:
> > >
> > > impl rtc::Ops for MyRtcOps {
> > > type BusDeviceType = platform::Device<Bound>;
> > >
> > > fn read_time(
> > > parent: &platform::Device<Bound>,
> > > time: &mut rtc::Time,
> > > ) -> Result {
> > > let drvdata = pdev.as_ref().drvdata::<MyDriver>()?;
> > >
> > > ...
> > > }
> > > }
> >
> > Let's have a look at the corresponding probe() side of things.
> >
> > struct SampleIrqData;
> >
> > // The bus device private data.
> > #[pin_data]
> > struct SampleDriver {
> > #[pin]
> > irq: irq::Registration<SampleIrqData>,
> > rtc: ARef<rtc::Device>,
> > ...,
> > }
> >
> > impl pci::Driver for SampleDriver {
> > fn probe(pdev: &pci::Device<Core>, info: &Self::IdInfo) -> impl PinInit<Self, Error> {
> > let dev = pdev.as_ref();
> >
> > let rtc = rtc::Device::new(dev)?;
> >
> > Ok(impl_pin_init!(Self {
> > irq <- irq::Registration::new(...),
> > rtc,
> > _: {
> > devres::register(rtc::Registration::new(rtc))?;
> > }
> > })
> > }
> > }
> >
> > This is a problem, since even though the registration of the RTC device is the
> > last thing in the initializer, rtc::Ops::read_time() above could race and
> > Device::drvdata() could just fail as it might not be set yet.
> >
> > This is fixable, but - as mentioned - at the price of introducing ordering
> > constraints for drivers.
> >
> > For instance, this could be solved by introducing a pci::ProbeDevice<'a> type,
> > which ensures that pci::ProbeDevice::set_drvdata() can only be called from
> > probe() and can only be called once, as it consumes the pci::ProbeDevice.
> >
> > struct SampleIrqData;
> >
> > // The bus device private data.
> > #[pin_data]
> > struct SampleDriver {
> > #[pin]
> > irq: irq::Registration<SampleIrqData>,
> > rtc: ARef<rtc::Device>,
> > ...,
> > }
> >
> > impl pci::Driver for SampleDriver {
> > fn probe<'a>(pdev: pci::ProbeDevice<'a>, info: &Self::IdInfo) -> Result {
> > let dev = pdev.as_ref();
> >
> > let rtc = rtc::Device::new(dev)?;
> >
> > let data = impl_pin_init!(Self {
> > irq <- irq::Registration::new(...),
> > rtc,
> > });
> >
> > // The `pci::ProbeDevice` has been consumed by value, and
> > // returned a `&pci::Device<Core>`.
> > let pdev = pdev.set_drvdata(data)?;
> >
> > devres::register(rtc::Registration::new(rtc))
> > }
> > }
> >
> > However, note how this wouldn't solve the same problem for irq::Registration, as
> > it lives within the the driver's device private data.
> >
> > If this would be C you would probably just partially initialize the driver's
> > device private data, but in Rust uninitialized data is not allowed for safety
> > reasons.
> >
> > We could probably find other ways to somehow handle this, e.g. by introducing
> > new device context states, additional callbacks, etc., but in the end it would
> > only be workarounds for not having defined ownership, that eventually ends up
> > being more complicated and more error prone.
> >
> > For this reason an irq::Registration has its own private data (which in this
> > example is just an empty struct).
> >
> > Similarly, all other kinds of registrations (and driver entry points) have their
> > own private data, such as class devices, work and workqueues, file operations,
> > timers, etc.
> >
> > I.e. there is a clear separation of ownership and lifetime, which makes more or
> > less (often more) suble ordering constraints obsolete.
> >
> > In fact, in the beginning I did not even plan to expose Device::drvdata() at
> > all, making the driver's device private data only available in bus device
> > callbacks, etc.
> >
> > The reason why I added it was that it was required (and makes sense) when
> > drivers interact with other drivers, e.g. through an auxiliary device.
> >
> > I did also mention this in the commit message of commit 6f61a2637abe ("rust:
> > device: introduce Device::drvdata()"):
> >
> > (2) Having a direct accessor to the driver's private data is not
> > commonly required (at least in Rust): Bus callback methods already
> > provide access to the driver's device private data through a &self
> > argument, while other driver entry points such as IRQs,
> > workqueues, timers, IOCTLs, etc. have their own private data with
> > separate ownership and lifetime.
> >
> > In other words, a driver's device private data is only relevant
> > for driver model contexts (such a file private is only relevant
> > for file contexts).
> >
> > Having that said, the motivation for accessing the driver's device
> > private data with Device<Bound>::drvdata() are interactions between
> > drivers. For instance, when an auxiliary driver calls back into its
> > parent, the parent has to be capable to derive its private data from the
> > corresponding device (i.e. the parent of the auxiliary device).
> >
> > Let's have a look at how probe() looks like for the example below, which is what
> > we do in other subsystems, such as DRM or PWM.
> >
> > > Example 3:
> > >
> > > struct SampleIrqData {
> > > rtc: ARef<rtc::Device>,
> > > };
> > >
> > > // The bus device private data.
> > > #[pin_data]
> > > struct SampleDriver {
> > > #[pin]
> > > irq: irq::Registration<SampleIrqHandler>,
> > > rtc: ARef<rtc::Device<SampleRtcData>>,
> > > }
> > >
> > > // The class device private data.
> > > struct SampleRtcData {
> > > io: Devres<IoMem<PL031_REG_SIZE>>,
> > > hw_variant: VendorVariant,
> > > }
> > >
> > > impl rtc::Ops for MyRtcOps {
> > > type BusDeviceType = platform::Device<Bound>;
> > >
> > > fn read_time(
> > > rtc: &rtc::Device<SampleRtcData>
> > > parent: &platform::Device<Bound>,
> > > time: &mut rtc::Time,
> > > ) -> Result {
> > > let io = rtc.io.access(parent)?;
> > >
> > > match rtc.hw_variant {
> > > VendorVariant::Arm | VendorVariant::StV1 => {
> > > let my_time = io.read(...);
> > >
> > > my_time.write_into(time);
> > > },
> > > VendorVariant::StV2 => { ... },
> > > }
> > > }
> > > }
> > >
> >
> > impl pci::Driver for SampleDriver {
> > fn probe(pdev: &pci::Device<Core>, info: &Self::IdInfo) -> impl PinInit<Self, Error> {
> > let dev = pdev.as_ref();
> >
> > let rtc_data = impl_pin_init!(SampleRtcData {
> > io: iomap_region_sized::<BAR0_SIZE>(0, c"my_rtc/bar0")?,
> > hw_variant: VendorVariant::StV1,
> > });
> >
> > let rtc = rtc::Device::new(dev, rtc_data)?;
> >
> > // Internally calls `devres::register(rtc::Registration::new())`.
> > rtc::Registration::register(rtc)?;
> >
> > Ok(impl_pin_init!(Self {
> > // Give the IRQ handler a reference count of the `rtc::Device`.
> > irq <- irq::Registration::new(..., rtc.clone()),
> > rtc,
> > })
> > }
> > }
> >
> > With this there are no (subtle) ordering constraints the driver has to get
> > right; ownership and lifetimes are well defined.
> >
> > (I.e. whatever order a driver picks, it either works properly or it does not
> > compile in the first place, which is a huge improvement over the situation we
> > have in C.)
>
> Thanks for all of this information and let me confirm my understanding.
>
> In both Example 1 and Example 3 there is a dependency between rtc's
> read_time() callback and the PCI driver probe() because calling the
> former before the latter is complete would be premature (and it would
> fail). However, in Example 1 the compiler does not take that
> dependency into account because rtc is not passed to the callback,
> while in Example 3 the dependency is visible to the compiler and it
> will refuse to compile the code if the dependency may be missed.
>
> If the above is correct, the current calling convention of RTC class
> callbacks is problematic because it causes correctness checks done by
> the Rust compiler to be bypassed (which may allow certain ordering
> issues to be missed and functional problems to appear going forward).
>
> That's convincing and I think that adding an RTC device pointer to the
> list of parameters of RTC class callbacks on the C side would not be a
> very intrusive change. Arguably, it would be less intrusive than
> replacing the parent device pointer with the RTC device pointer in
> them.
But there is no way the rtc callbacks will be called before the rtc is
registered which will only happen in probe, when we have an actual
device to talk to...
--
Alexandre Belloni, co-owner and COO, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
next prev parent reply other threads:[~2026-02-24 14:57 UTC|newest]
Thread overview: 43+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-01-16 16:21 [RFC PATCH v3 0/5] rust: Add RTC driver support Ke Sun
2026-01-16 16:21 ` [RFC PATCH v3 1/5] rtc: add device selector for rtc_class_ops callbacks Ke Sun
2026-01-16 16:24 ` Ke Sun
2026-01-19 14:32 ` Danilo Krummrich
2026-01-20 8:01 ` Ke Sun
2026-02-20 22:53 ` Alexandre Belloni
2026-02-21 9:31 ` Alvin Sun
2026-02-21 11:16 ` Alexandre Belloni
2026-02-21 11:19 ` Rafael J. Wysocki
2026-02-21 14:33 ` Danilo Krummrich
2026-02-22 0:05 ` Alexandre Belloni
2026-02-22 12:49 ` Danilo Krummrich
2026-02-22 14:01 ` Rafael J. Wysocki
2026-02-22 16:13 ` Danilo Krummrich
2026-02-24 0:12 ` Danilo Krummrich
2026-02-24 13:28 ` Rafael J. Wysocki
2026-02-24 14:57 ` Alexandre Belloni [this message]
2026-02-24 15:23 ` Rafael J. Wysocki
2026-02-24 15:36 ` Danilo Krummrich
2026-02-24 15:01 ` Alexandre Belloni
2026-02-24 16:35 ` Danilo Krummrich
2026-02-24 16:42 ` Danilo Krummrich
2026-02-24 17:28 ` Alexandre Belloni
2026-02-24 22:23 ` Danilo Krummrich
2026-02-24 22:44 ` Alexandre Belloni
2026-02-25 3:19 ` Gary Guo
2026-02-25 13:33 ` Rafael J. Wysocki
2026-02-25 16:26 ` Danilo Krummrich
2026-02-25 21:15 ` Rafael J. Wysocki
2026-02-26 12:28 ` Rafael J. Wysocki
2026-02-27 15:09 ` Benno Lossin
2026-02-22 12:25 ` Rafael J. Wysocki
2026-02-22 14:24 ` Rafael J. Wysocki
2026-02-22 15:29 ` Danilo Krummrich
2026-02-22 15:43 ` Rafael J. Wysocki
2026-02-21 16:32 ` Alvin Sun
2026-02-21 17:53 ` Danilo Krummrich
2026-01-16 16:22 ` [RFC PATCH v3 2/5] rust: add AMBA bus driver support Ke Sun
2026-01-16 16:22 ` [RFC PATCH v3 3/5] rust: add device wakeup capability support Ke Sun
2026-01-17 0:44 ` Ke Sun
2026-01-16 16:22 ` [RFC PATCH v3 4/5] rust: add RTC core abstractions and data structures Ke Sun
2026-01-16 16:34 ` [RFC PATCH v3 5/5] rust: add PL031 RTC driver Ke Sun
2026-01-19 9:12 ` Ke Sun
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=20260224145721a9e69e4d@mail.local \
--to=alexandre.belloni@bootlin.com \
--cc=a.hindborg@kernel.org \
--cc=aliceryhl@google.com \
--cc=alvin.sun@linux.dev \
--cc=bjorn3_gh@protonmail.com \
--cc=boqun.feng@gmail.com \
--cc=dakr@kernel.org \
--cc=gary@garyguo.net \
--cc=gregkh@linuxfoundation.org \
--cc=linux-rtc@vger.kernel.org \
--cc=lossin@kernel.org \
--cc=ojeda@kernel.org \
--cc=rafael@kernel.org \
--cc=rust-for-linux@vger.kernel.org \
--cc=tmgross@umich.edu \
/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