mbox series

[v2,00/10] Device / Driver and PCI Rust abstractions

Message ID 20240618234025.15036-1-dakr@redhat.com (mailing list archive)
Headers show
Series Device / Driver and PCI Rust abstractions | expand

Message

Danilo Krummrich June 18, 2024, 11:39 p.m. UTC
This patch series implements basic device / driver, PCI and devres Rust
abstractions.

This patch series is sent in the context of [1], and the corresponding patch
series [2], which contains some basic DRM Rust abstractions and a stub
implementation of the Nova GPU driver.

Nova is intended to be developed upstream, starting out with just a stub driver
to lift some initial required infrastructure upstream. A more detailed
explanation can be found in [1].

As mentioned above, a driver serving as example on how these abstractions are
used within a (DRM) driver can be found in [2].

Additionally, the device / driver bits can also be found in [3], all
abstractions required for Nova in [4] and Nova in [5].

This patch series is based on [6] (which has been merged in the driver-core tree
already), as well as two more patches:

- "rust: init: introduce Opaque::try_ffi_init" [7]
- "rust: introduce InPlaceModule" [8]

@Wedson, please let me know if you want to send the two separately or if you
want me to pick them up for this series.

Changes in v2:
==============
- statically initialize driver structures (Greg)
- move base device ID abstractions to a separate source file (Greg)
- remove `DeviceRemoval` trait in favor of using a `Devres` callback to
  unregister drivers
- remove `device::Data`, we don't need this abstraction anymore now that we
  `Devres` to revoke resources and registrations
- pass the module name to `Module::init` and `InPlaceModule::init` in a separate
  patch
- rework of `Io` including compile time boundary checks (Miguel, Wedson)
- adjust PCI abstractions accordingly and implement a `module_pci_driver!` macro
- rework `pci::Bar` to support a const SIZE
- increase the total amount of Documentation, rephrase some safety comments and
  commit messages for less ambiguity
- fix compilation issues with some documentation examples

[1] https://lore.kernel.org/dri-devel/Zfsj0_tb-0-tNrJy@cassiopeiae/T/#u
[2] https://lore.kernel.org/dri-devel/20240618233324.14217-1-dakr@redhat.com/
[3] https://github.com/Rust-for-Linux/linux/tree/staging/rust-device
[4] https://github.com/Rust-for-Linux/linux/tree/staging/dev
[5] https://gitlab.freedesktop.org/drm/nova/-/tree/nova-next
[6] https://lore.kernel.org/rust-for-linux/20240618154841.6716-1-dakr@redhat.com/
[7] https://github.com/Rust-for-Linux/linux/commit/9c49161db95f4eb4e55e62873b835fb6c1a0bb39
[8] https://github.com/Rust-for-Linux/linux/commit/e74d5d33dd2b9361e8cebae77227e3f924b50034

Danilo Krummrich (6):
  rust: pass module name to `Module::init`
  rust: implement generic driver registration
  rust: add `io::Io` base type
  rust: add devres abstraction
  rust: pci: add basic PCI device / driver abstractions
  rust: pci: implement I/O mappable `pci::Bar`

Wedson Almeida Filho (4):
  rust: implement `IdArray`, `IdTable` and `RawDeviceId`
  rust: add rcu abstraction
  rust: add `Revocable` type
  rust: add `dev_*` print macros.

 rust/bindings/bindings_helper.h |   1 +
 rust/helpers.c                  | 145 ++++++++++
 rust/kernel/device.rs           | 319 +++++++++++++++++++++-
 rust/kernel/device_id.rs        | 336 +++++++++++++++++++++++
 rust/kernel/devres.rs           | 168 ++++++++++++
 rust/kernel/driver.rs           | 128 +++++++++
 rust/kernel/io.rs               | 219 +++++++++++++++
 rust/kernel/lib.rs              |  22 +-
 rust/kernel/net/phy.rs          |   2 +-
 rust/kernel/pci.rs              | 467 ++++++++++++++++++++++++++++++++
 rust/kernel/prelude.rs          |   2 +
 rust/kernel/revocable.rs        | 209 ++++++++++++++
 rust/kernel/sync.rs             |   1 +
 rust/kernel/sync/rcu.rs         |  52 ++++
 rust/macros/module.rs           |   3 +-
 samples/rust/rust_minimal.rs    |   2 +-
 samples/rust/rust_print.rs      |   2 +-
 17 files changed, 2069 insertions(+), 9 deletions(-)
 create mode 100644 rust/kernel/device_id.rs
 create mode 100644 rust/kernel/devres.rs
 create mode 100644 rust/kernel/driver.rs
 create mode 100644 rust/kernel/io.rs
 create mode 100644 rust/kernel/pci.rs
 create mode 100644 rust/kernel/revocable.rs
 create mode 100644 rust/kernel/sync/rcu.rs


base-commit: e74d5d33dd2b9361e8cebae77227e3f924b50034

Comments

Viresh Kumar June 19, 2024, 12:04 p.m. UTC | #1
On 19-06-24, 01:39, Danilo Krummrich wrote:
> - move base device ID abstractions to a separate source file (Greg)
> - remove `DeviceRemoval` trait in favor of using a `Devres` callback to
>   unregister drivers
> - remove `device::Data`, we don't need this abstraction anymore now that we
>   `Devres` to revoke resources and registrations

Hi Danilo,

I am working on writing bindings for CPUFreq drivers [1] and was
looking to rebase over staging/rust-device, and I am not sure how to
proceed after device::Data is dropped now.

What I was doing at probe() was something like this:

    fn probe(dev: &mut platform::Device, id_info: Option<&Self::IdInfo>) -> Result<Self::Data> {
        let data = Arc::<DeviceData>::from(kernel::new_device_data!(
            cpufreq::Registration::new(),
            (),
            "CPUFreqDT::Registration"
        )?);

        ...

        // Need a mutable object to be passed to register() here.
        data.registrations()
            .ok_or(ENXIO)?
            .as_pinned_mut()
            .register(c_str!("cpufreq-dt"), ...)?;

        Ok(data)
    }

The register() function of cpufreq core needs a mutable pointer to
`self` and it worked earlier as Data used a RevocableMutex. But with
Devres, we don't have a Mutex anymore and devres.try_access() doesn't
give a mutable object.

I am a bit confused on how to get this going. I looked at how PCI bus
is implemented in the staging/dev but I couldn't find an end driver
using this work.

Maybe I am making an mistake and missing the obvious.

Thanks.
Greg KH June 19, 2024, 12:17 p.m. UTC | #2
On Wed, Jun 19, 2024 at 05:34:07PM +0530, Viresh Kumar wrote:
> On 19-06-24, 01:39, Danilo Krummrich wrote:
> > - move base device ID abstractions to a separate source file (Greg)
> > - remove `DeviceRemoval` trait in favor of using a `Devres` callback to
> >   unregister drivers
> > - remove `device::Data`, we don't need this abstraction anymore now that we
> >   `Devres` to revoke resources and registrations
> 
> Hi Danilo,
> 
> I am working on writing bindings for CPUFreq drivers [1] and was
> looking to rebase over staging/rust-device, and I am not sure how to
> proceed after device::Data is dropped now.

As it should be dropped :)

A struct device does not have a "data" pointer, it has specific other
pointers to hold data in, but they better be accessed by their proper
name if you want rust code to be reviewable by anyone.

Also, you shouldn't be accessing that field directly anyway, that's what
the existing dev_set_drvdata/dev_get_drvdata() calls are for.  Just use
them please.

thanks,

greg k-h
Danilo Krummrich June 19, 2024, 12:36 p.m. UTC | #3
On Wed, Jun 19, 2024 at 05:34:07PM +0530, Viresh Kumar wrote:
> On 19-06-24, 01:39, Danilo Krummrich wrote:
> > - move base device ID abstractions to a separate source file (Greg)
> > - remove `DeviceRemoval` trait in favor of using a `Devres` callback to
> >   unregister drivers
> > - remove `device::Data`, we don't need this abstraction anymore now that we
> >   `Devres` to revoke resources and registrations
> 
> Hi Danilo,
> 
> I am working on writing bindings for CPUFreq drivers [1] and was
> looking to rebase over staging/rust-device, and I am not sure how to
> proceed after device::Data is dropped now.
> 
> What I was doing at probe() was something like this:
> 
>     fn probe(dev: &mut platform::Device, id_info: Option<&Self::IdInfo>) -> Result<Self::Data> {
>         let data = Arc::<DeviceData>::from(kernel::new_device_data!(
>             cpufreq::Registration::new(),
>             (),
>             "CPUFreqDT::Registration"
>         )?);
> 
>         ...
> 
>         // Need a mutable object to be passed to register() here.
>         data.registrations()
>             .ok_or(ENXIO)?
>             .as_pinned_mut()
>             .register(c_str!("cpufreq-dt"), ...)?;
> 
>         Ok(data)
>     }
> 
> The register() function of cpufreq core needs a mutable pointer to
> `self` and it worked earlier as Data used a RevocableMutex. But with
> Devres, we don't have a Mutex anymore and devres.try_access() doesn't
> give a mutable object.

If you want to split `cpufreq::Registration` in `new()` and `register()`, you
probably want to pass the registration object to `Devres` in `register()`
instead.

However, I wouldn't recommend splitting it up (unless you absolutely have to),
it's way cleaner (and probably less racy) if things are registered once the
registration is created.

> 
> I am a bit confused on how to get this going. I looked at how PCI bus
> is implemented in the staging/dev but I couldn't find an end driver
> using this work.

The PCI abstraction did not need to change for that, since it uses the
generalized `driver::Registration`, which is handled by the `Module` structure
instead.

However, staging/dev also contains the `drm::drv::Registration` type [1], which
in principle does the same thing as `cpufreq::Registration` just for a DRM
device.

If you're looking for an example driver making use of this, please have a look
at Nova [1].

[1] https://github.com/Rust-for-Linux/linux/blob/staging/dev/rust/kernel/drm/drv.rs
[2] https://gitlab.freedesktop.org/drm/nova/-/blob/nova-next/drivers/gpu/drm/nova/driver.rs

> 
> Maybe I am making an mistake and missing the obvious.
> 
> Thanks.
> 
> -- 
> viresh
> 
> [1] https://lore.kernel.org/all/cover.1717750631.git.viresh.kumar@linaro.org/
>
Danilo Krummrich June 19, 2024, 12:42 p.m. UTC | #4
On Wed, Jun 19, 2024 at 02:17:58PM +0200, Greg KH wrote:
> On Wed, Jun 19, 2024 at 05:34:07PM +0530, Viresh Kumar wrote:
> > On 19-06-24, 01:39, Danilo Krummrich wrote:
> > > - move base device ID abstractions to a separate source file (Greg)
> > > - remove `DeviceRemoval` trait in favor of using a `Devres` callback to
> > >   unregister drivers
> > > - remove `device::Data`, we don't need this abstraction anymore now that we
> > >   `Devres` to revoke resources and registrations
> > 
> > Hi Danilo,
> > 
> > I am working on writing bindings for CPUFreq drivers [1] and was
> > looking to rebase over staging/rust-device, and I am not sure how to
> > proceed after device::Data is dropped now.
> 
> As it should be dropped :)
> 
> A struct device does not have a "data" pointer, it has specific other
> pointers to hold data in, but they better be accessed by their proper
> name if you want rust code to be reviewable by anyone.
> 
> Also, you shouldn't be accessing that field directly anyway, that's what
> the existing dev_set_drvdata/dev_get_drvdata() calls are for.  Just use
> them please.

Sorry that this was confusing. `device::Data` was a generic type for drivers to
store their private data in. It was meant to be handled by subsystems to store
it in their particular driver structure. Is most cases of course this eventually
ends up in a call to dev_set_drvdata() (e.g. through pci_set_drvdata()).

The reason we had this in the first place was that `device::Data` was also used
to store resources and registrations. With my rework to `Devres` this isn't the
case anymore, and hence `device::Data` does not add any value anymore and was
removed.

> 
> thanks,
> 
> greg k-h
>
Viresh Kumar June 20, 2024, 10:05 a.m. UTC | #5
On 19-06-24, 14:36, Danilo Krummrich wrote:
> If you want to split `cpufreq::Registration` in `new()` and `register()`, you
> probably want to pass the registration object to `Devres` in `register()`
> instead.
> 
> However, I wouldn't recommend splitting it up (unless you absolutely have to),
> it's way cleaner (and probably less racy) if things are registered once the
> registration is created.

> The PCI abstraction did not need to change for that, since it uses the
> generalized `driver::Registration`, which is handled by the `Module` structure
> instead.
> 
> However, staging/dev also contains the `drm::drv::Registration` type [1], which
> in principle does the same thing as `cpufreq::Registration` just for a DRM
> device.
> 
> If you're looking for an example driver making use of this, please have a look
> at Nova [1].

Thanks for the pointers Danilo.

There is more to it now and I still don't know what's the best way
forward. :(

Devres will probably work well with the frameworks that provide a bus,
where a device and driver are matched and probe/remove are called.
Obviously Devres needs a struct device, whose probing/removal can
allocate/free resources.

The CPUFreq framework is a bit different. There is no bus, device or
driver there. The device for the framework is the CPU device, but we
don't (can't) really bind a struct driver to it. There are more layers
in the kernel which use the CPU devices directly, like cpuidle, etc.
And so the CPU device isn't really private to the cpufreq/cpuidle
frameworks.

Most of the cpufreq drivers register with the cpufreq core from their
module_init() function, and unregister from module_exit(). There is no
probe/remove() callbacks available. Some drivers though have a
platform device/driver model implemented over an imaginary platform
device, a hack implemented to get them working because of special
requirements (one of them is to allow defer probing to work). The
driver I am implementing, cpufreq-dt, also has one such platform
device which is created at runtime. But there will be others without a
platform device.

The point is that the Rust cpufreq core can't do the Devres stuff
itself and it can't expect a struct device to be made available to it
by the driver. Some cpufreq drivers will have a platform device, some
won't.

One way to make this whole work is to reintroduce the Data part, just
for cpufreq core, but I really don't want to do it. What else can be
done ?
Danilo Krummrich June 20, 2024, 11:09 a.m. UTC | #6
On Thu, Jun 20, 2024 at 03:35:56PM +0530, Viresh Kumar wrote:
> On 19-06-24, 14:36, Danilo Krummrich wrote:
> > If you want to split `cpufreq::Registration` in `new()` and `register()`, you
> > probably want to pass the registration object to `Devres` in `register()`
> > instead.
> > 
> > However, I wouldn't recommend splitting it up (unless you absolutely have to),
> > it's way cleaner (and probably less racy) if things are registered once the
> > registration is created.
> 
> > The PCI abstraction did not need to change for that, since it uses the
> > generalized `driver::Registration`, which is handled by the `Module` structure
> > instead.
> > 
> > However, staging/dev also contains the `drm::drv::Registration` type [1], which
> > in principle does the same thing as `cpufreq::Registration` just for a DRM
> > device.
> > 
> > If you're looking for an example driver making use of this, please have a look
> > at Nova [1].
> 
> Thanks for the pointers Danilo.
> 
> There is more to it now and I still don't know what's the best way
> forward. :(
> 
> Devres will probably work well with the frameworks that provide a bus,
> where a device and driver are matched and probe/remove are called.
> Obviously Devres needs a struct device, whose probing/removal can
> allocate/free resources.

Indeed, but please note that this was the case before as well. When we had
`device::Data` with a `Revokable<T>` for Registrations this revokable was
revoked through the `DeviceRemoval` trait when the driver was unbound from the
device.

> 
> The CPUFreq framework is a bit different. There is no bus, device or
> driver there. The device for the framework is the CPU device, but we
> don't (can't) really bind a struct driver to it. There are more layers
> in the kernel which use the CPU devices directly, like cpuidle, etc.
> And so the CPU device isn't really private to the cpufreq/cpuidle
> frameworks.

If there is no bus, device or driver, then those abstractions aren't for your
use case. Those are abstractions around the device / driver core.

> 
> Most of the cpufreq drivers register with the cpufreq core from their
> module_init() function, and unregister from module_exit(). There is no
> probe/remove() callbacks available. Some drivers though have a
> platform device/driver model implemented over an imaginary platform
> device, a hack implemented to get them working because of special
> requirements (one of them is to allow defer probing to work). The
> driver I am implementing, cpufreq-dt, also has one such platform
> device which is created at runtime. But there will be others without a
> platform device.
> 
> The point is that the Rust cpufreq core can't do the Devres stuff
> itself and it can't expect a struct device to be made available to it
> by the driver. Some cpufreq drivers will have a platform device, some
> won't.

That seems to be purely a design question for cpufreq drivers then.

What prevents you from always creating a corresponding platform device?

If you really want some drivers to bypass the device / driver model (not sure
if that's a good idea though), you need separate abstractions for that.

> 
> One way to make this whole work is to reintroduce the Data part, just
> for cpufreq core, but I really don't want to do it.

That doesn't help you either. As mentioned above, `device::Data` was supposed to
receive a callback (`DeviceRemoval`) from the underlying driver (platform_driver
in your case) on device detach to revoke the registration.

By using `Devres` instead, nothing changes semantically, but it makes the
resulting code cleaner.

> What else can be done ?

Think about what you want the lifetime of your cpufreq registration to be.

Currently, it seems you want to do both, bind it to probe() / remove(), in case
the driver is implemented as platform_driver, and to module_init() /
module_exit(), in case the device / driver model is bypassed.

> 
> -- 
> viresh
>