public inbox for rust-for-linux@vger.kernel.org
 help / color / mirror / Atom feed
From: "Danilo Krummrich" <dakr@kernel.org>
To: "Rafael J. Wysocki" <rafael@kernel.org>
Cc: "Alexandre Belloni" <alexandre.belloni@bootlin.com>,
	"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 01:12:32 +0100	[thread overview]
Message-ID: <DGMR9XOWP1V0.3C9219TYPXV6J@kernel.org> (raw)
In-Reply-To: <DGLMGEZTM7E2.Y8VV9I6LI1P6@kernel.org>

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.)

  reply	other threads:[~2026-02-24  0:12 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 [this message]
2026-02-24 13:28                           ` Rafael J. Wysocki
2026-02-24 14:57                             ` Alexandre Belloni
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=DGMR9XOWP1V0.3C9219TYPXV6J@kernel.org \
    --to=dakr@kernel.org \
    --cc=a.hindborg@kernel.org \
    --cc=alexandre.belloni@bootlin.com \
    --cc=aliceryhl@google.com \
    --cc=alvin.sun@linux.dev \
    --cc=bjorn3_gh@protonmail.com \
    --cc=boqun.feng@gmail.com \
    --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