mbox series

[0/5] Rust abstractions for network device drivers

Message ID 20230613045326.3938283-1-fujita.tomonori@gmail.com (mailing list archive)
Headers show
Series Rust abstractions for network device drivers | expand

Message

FUJITA Tomonori June 13, 2023, 4:53 a.m. UTC
This patchset adds minimum Rust abstractions for network device
drivers and an example of a Rust network device driver, a simpler
version of drivers/net/dummy.c.

The dummy network device driver doesn't attach any bus such as PCI so
the dependency is minimum. Hopefully, it would make reviewing easier.

Thanks a lot for reviewing on RFC patchset at rust-for-linux ml.
Hopefully, I've addressed all the issues.

FUJITA Tomonori (5):
  rust: core abstractions for network device drivers
  rust: add support for ethernet operations
  rust: add support for get_stats64 in struct net_device_ops
  rust: add methods for configure net_device
  samples: rust: add dummy network driver

 rust/bindings/bindings_helper.h |   3 +
 rust/helpers.c                  |  23 ++
 rust/kernel/lib.rs              |   3 +
 rust/kernel/net.rs              |   5 +
 rust/kernel/net/dev.rs          | 697 ++++++++++++++++++++++++++++++++
 samples/rust/Kconfig            |  12 +
 samples/rust/Makefile           |   1 +
 samples/rust/rust_net_dummy.rs  |  81 ++++
 scripts/Makefile.build          |   2 +-
 9 files changed, 826 insertions(+), 1 deletion(-)
 create mode 100644 rust/kernel/net.rs
 create mode 100644 rust/kernel/net/dev.rs
 create mode 100644 samples/rust/rust_net_dummy.rs


base-commit: d2e3115d717197cb2bc020dd1f06b06538474ac3

Comments

Jakub Kicinski June 15, 2023, 6:01 a.m. UTC | #1
On Tue, 13 Jun 2023 13:53:21 +0900 FUJITA Tomonori wrote:
> This patchset adds minimum Rust abstractions for network device
> drivers and an example of a Rust network device driver, a simpler
> version of drivers/net/dummy.c.
> 
> The dummy network device driver doesn't attach any bus such as PCI so
> the dependency is minimum. Hopefully, it would make reviewing easier.
> 
> Thanks a lot for reviewing on RFC patchset at rust-for-linux ml.
> Hopefully, I've addressed all the issues.

First things first, what are the current expectations for subsystems
accepting rust code?

I was hoping someone from the Rust side is going to review this.
We try to review stuff within 48h at netdev, and there's no review :S

My immediate instinct is that I'd rather not merge toy implementations
unless someone within the netdev community can vouch for the code.

>  rust/bindings/bindings_helper.h |   3 +
>  rust/helpers.c                  |  23 ++
>  rust/kernel/lib.rs              |   3 +
>  rust/kernel/net.rs              |   5 +
>  rust/kernel/net/dev.rs          | 697 ++++++++++++++++++++++++++++++++
>  samples/rust/Kconfig            |  12 +
>  samples/rust/Makefile           |   1 +
>  samples/rust/rust_net_dummy.rs  |  81 ++++
>  scripts/Makefile.build          |   2 +-

You seem to create a rust/net/ directory without adding anything 
to MAINTAINERS. Are we building a parallel directory structure?
Are the maintainers also different?
Miguel Ojeda June 15, 2023, 8:58 a.m. UTC | #2
On Thu, Jun 15, 2023 at 8:01 AM Jakub Kicinski <kuba@kernel.org> wrote:
>
> First things first, what are the current expectations for subsystems
> accepting rust code?

If you mean generally: it is up to each subsystem to decide whether to
accept (or not) Rust code. Though we ask maintainers to make an effort
to be flexible when things are so "core" that they could potentially
block all other work, in order to conduct the Rust experiment and to
try things out.

On our side, we have a few guidelines for contributors [1].

[1] https://rust-for-linux.com/contributing#the-rust-subsystem

> I was hoping someone from the Rust side is going to review this.
> We try to review stuff within 48h at netdev, and there's no review :S

I think the version number got reset, but Tomonori had a couple
versions on the rust-for-linux@vger list [2][3].

Andrew Lunn was taking a look, and there were some other comments going on, too.

The email threading is broken in [2][3], though, so it may be easiest
to use a query like "f:lunn" [4] to find those.

[2] https://lore.kernel.org/rust-for-linux/01010188843258ec-552cca54-4849-4424-b671-7a5bf9b8651a-000000@us-west-2.amazonses.com/
[3] https://lore.kernel.org/rust-for-linux/01010188a42d5244-fffbd047-446b-4cbf-8a62-9c036d177276-000000@us-west-2.amazonses.com/
[4] https://lore.kernel.org/rust-for-linux/?q=f%3Alunn

> My immediate instinct is that I'd rather not merge toy implementations
> unless someone within the netdev community can vouch for the code.

Yes, in general, the goal is that maintainers actually understand what
is getting merged, get involved, etc. So patch submitters of Rust
code, at this time, should be expected/ready to explain Rust if
needed. We can also help from the Rust subsystem side on that.

But, yeah, knowledgeable people should review the code.

> You seem to create a rust/net/ directory without adding anything
> to MAINTAINERS. Are we building a parallel directory structure?
> Are the maintainers also different?

The plan is to split the `kernel` crate and move the files to their
proper subsystems if the experiment goes well.

But, indeed, it is best if a `F:` entry is added wherever you think it
is best. Some subsystems may just add it to their entry (e.g. KUnit
wants to do that). Others may decide to split the Rust part into
another entry, so that maintainers may be a subset (or a different set
-- sometimes this could be done, for instance, if a new maintainer
shows up that wants to take care of the Rust abstractions).

Cheers,
Miguel
Andrew Lunn June 15, 2023, 12:51 p.m. UTC | #3
On Wed, Jun 14, 2023 at 11:01:28PM -0700, Jakub Kicinski wrote:
> On Tue, 13 Jun 2023 13:53:21 +0900 FUJITA Tomonori wrote:
> > This patchset adds minimum Rust abstractions for network device
> > drivers and an example of a Rust network device driver, a simpler
> > version of drivers/net/dummy.c.
> > 
> > The dummy network device driver doesn't attach any bus such as PCI so
> > the dependency is minimum. Hopefully, it would make reviewing easier.
> > 
> > Thanks a lot for reviewing on RFC patchset at rust-for-linux ml.
> > Hopefully, I've addressed all the issues.
> 
> First things first, what are the current expectations for subsystems
> accepting rust code?
> 
> I was hoping someone from the Rust side is going to review this.
> We try to review stuff within 48h at netdev, and there's no review :S

As pointed out elsewhere, i've looked the code over. I cannot say
anything about the rust, but i don't see anything too obviously wrong
with the way it use a few netdev API calls.

> My immediate instinct is that I'd rather not merge toy implementations
> unless someone within the netdev community can vouch for the code.

It is definitely toy. But you have to start somewhere.

What might be useful is an idea of the roadmap. How does this go from
toy to something useful?

I see two different threads of work.

One is getting enough in place to find where rust is lacking. netdev
uses a lot of inline C functions, which don't seem to map too well to
Rust. And rust does not seem to have the concept of per CPU memory,
needed for statistics. So it would be good to be able to have some
code which can be profiled to see how bad this actually is, and then
provide a test bed for finding solutions for these problems.

The second is just wrapping more API calls to allow more capable
drivers to be written. Implementing the loopback driver seems like the
next obvious step. Then maybe a big jump to virtio_net? Like dummy and
loopback, it is something which everybody can have, no physical
hardware needed. It also has a lot of netdev features, NAPI, RSS,
statistics, BPF & XDP. So it is a good playground, both for features
and to profiling and performance optimisation.

    Andrew
Jakub Kicinski June 16, 2023, 2:02 a.m. UTC | #4
On Thu, 15 Jun 2023 14:51:10 +0200 Andrew Lunn wrote:
> On Wed, Jun 14, 2023 at 11:01:28PM -0700, Jakub Kicinski wrote:
> > On Tue, 13 Jun 2023 13:53:21 +0900 FUJITA Tomonori wrote:  
> > > This patchset adds minimum Rust abstractions for network device
> > > drivers and an example of a Rust network device driver, a simpler
> > > version of drivers/net/dummy.c.
> > > 
> > > The dummy network device driver doesn't attach any bus such as PCI so
> > > the dependency is minimum. Hopefully, it would make reviewing easier.
> > > 
> > > Thanks a lot for reviewing on RFC patchset at rust-for-linux ml.
> > > Hopefully, I've addressed all the issues.  
> > 
> > First things first, what are the current expectations for subsystems
> > accepting rust code?
> > 
> > I was hoping someone from the Rust side is going to review this.
> > We try to review stuff within 48h at netdev, and there's no review :S  
> 
> As pointed out elsewhere, i've looked the code over. I cannot say
> anything about the rust, but i don't see anything too obviously wrong
> with the way it use a few netdev API calls.

The skb freeing looks shady from functional perspective.
I'm guessing some form of destructor frees the skb automatically
in xmit handler(?), but (a) no reason support, (b) kfree_skb_reason()
is most certainly not safe to call on all xmit paths...

> > My immediate instinct is that I'd rather not merge toy implementations
> > unless someone within the netdev community can vouch for the code.  
> 
> It is definitely toy. But you have to start somewhere.
> 
> What might be useful is an idea of the roadmap. How does this go from
> toy to something useful?
> 
> I see two different threads of work.
> 
> One is getting enough in place to find where rust is lacking. netdev
> uses a lot of inline C functions, which don't seem to map too well to
> Rust. And rust does not seem to have the concept of per CPU memory,
> needed for statistics. So it would be good to be able to have some
> code which can be profiled to see how bad this actually is, and then
> provide a test bed for finding solutions for these problems.
> 
> The second is just wrapping more API calls to allow more capable
> drivers to be written. Implementing the loopback driver seems like the
> next obvious step. Then maybe a big jump to virtio_net?

I'd have thought that a simple SPI driver or some such would be 
a better match.

> Like dummy and loopback, it is something which everybody can have, no
> physical hardware needed. It also has a lot of netdev features, NAPI,
> RSS, statistics, BPF & XDP. So it is a good playground, both for
> features and to profiling and performance optimisation.

IMO we have too much useless playground stuff in C already, don't get me
started. Let's get a real device prove that this thing can actually work
:( We can waste years implementing imaginary device drivers that nobody
ends up using.
Jakub Kicinski June 16, 2023, 2:19 a.m. UTC | #5
On Thu, 15 Jun 2023 10:58:50 +0200 Miguel Ojeda wrote:
> On Thu, Jun 15, 2023 at 8:01 AM Jakub Kicinski <kuba@kernel.org> wrote:
> > I was hoping someone from the Rust side is going to review this.
> > We try to review stuff within 48h at netdev, and there's no review :S  
> 
> I think the version number got reset, but Tomonori had a couple
> versions on the rust-for-linux@vger list [2][3].
> 
> Andrew Lunn was taking a look, and there were some other comments going on, too.
> 
> The email threading is broken in [2][3], though, so it may be easiest
> to use a query like "f:lunn" [4] to find those.
> 
> [2] https://lore.kernel.org/rust-for-linux/01010188843258ec-552cca54-4849-4424-b671-7a5bf9b8651a-000000@us-west-2.amazonses.com/
> [3] https://lore.kernel.org/rust-for-linux/01010188a42d5244-fffbd047-446b-4cbf-8a62-9c036d177276-000000@us-west-2.amazonses.com/
> [4] https://lore.kernel.org/rust-for-linux/?q=f%3Alunn
> 
> > My immediate instinct is that I'd rather not merge toy implementations
> > unless someone within the netdev community can vouch for the code.  
> 
> Yes, in general, the goal is that maintainers actually understand what
> is getting merged, get involved, etc. So patch submitters of Rust
> code, at this time, should be expected/ready to explain Rust if
> needed. We can also help from the Rust subsystem side on that.
> 
> But, yeah, knowledgeable people should review the code.

All sounds pretty reasonable, thanks for the pointers.

TBH I was hoping that the code will be more like reading "modern C++"
for a C developer. I can't understand much of what's going on.

Taking an example of what I have randomly on the screen as I'm writing
this email:

+    /// Updates TX stats.
+    pub fn set_tx_stats(&mut self, packets: u64, bytes: u64, errors: u64, dropped: u64) {
+        // SAFETY: We have exclusive access to the `rtnl_link_stats64`, so writing to it is okay.
+        unsafe {
+            let inner = Opaque::get(&self.0);
+            (*inner).tx_packets = packets;
+            (*inner).tx_bytes = bytes;
+            (*inner).tx_errors = errors;
+            (*inner).tx_dropped = dropped;
+        }
+    }

What is this supposed to be doing? Who needs to _set_ unrelated
statistics from a function call? Yet no reviewer is complaining
which either means I don't understand, or people aren't really 
paying attention :(

> > You seem to create a rust/net/ directory without adding anything
> > to MAINTAINERS. Are we building a parallel directory structure?
> > Are the maintainers also different?  
> 
> The plan is to split the `kernel` crate and move the files to their
> proper subsystems if the experiment goes well.

I see.

> But, indeed, it is best if a `F:` entry is added wherever you think it
> is best. Some subsystems may just add it to their entry (e.g. KUnit
> wants to do that). Others may decide to split the Rust part into
> another entry, so that maintainers may be a subset (or a different set
> -- sometimes this could be done, for instance, if a new maintainer
> shows up that wants to take care of the Rust abstractions).

I think F: would work for us.

Are there success stories in any subsystem for getting a driver for
real HW supported? I think the best way to focus the effort would be 
to set a target on a relatively simple device.

Actually Andrew is interested, and PHY drivers seem relatively simple..
/me runs away
Richard Cochran June 16, 2023, 3:47 a.m. UTC | #6
On Thu, Jun 15, 2023 at 07:02:52PM -0700, Jakub Kicinski wrote:

> IMO we have too much useless playground stuff in C already, don't get me
> started. Let's get a real device prove that this thing can actually work
> :( We can waste years implementing imaginary device drivers that nobody
> ends up using.

Yes, the long standing policy seems to be not to merge "dummy"
drivers.  The original PTP patch set had one, but at the time the
reviews said firmly "No" to that.

Thanks,
Richard
FUJITA Tomonori June 16, 2023, 12:18 p.m. UTC | #7
Hi,

On Thu, 15 Jun 2023 19:19:31 -0700
Jakub Kicinski <kuba@kernel.org> wrote:

> TBH I was hoping that the code will be more like reading "modern C++"
> for a C developer. I can't understand much of what's going on.
> 
> Taking an example of what I have randomly on the screen as I'm writing
> this email:
> 
> +    /// Updates TX stats.
> +    pub fn set_tx_stats(&mut self, packets: u64, bytes: u64, errors: u64, dropped: u64) {
> +        // SAFETY: We have exclusive access to the `rtnl_link_stats64`, so writing to it is okay.
> +        unsafe {
> +            let inner = Opaque::get(&self.0);
> +            (*inner).tx_packets = packets;
> +            (*inner).tx_bytes = bytes;
> +            (*inner).tx_errors = errors;
> +            (*inner).tx_dropped = dropped;
> +        }
> +    }
> 
> What is this supposed to be doing? Who needs to _set_ unrelated
> statistics from a function call? Yet no reviewer is complaining
> which either means I don't understand, or people aren't really 
> paying attention :(

Sorry, this function was used in the dummy driver to implement
net_device_ops->ndo_get_stats64:

https://lore.kernel.org/rust-for-linux/01010188a025632b-16a4fb69-5601-4f46-a170-52b5f2921ed2-000000@us-west-2.amazonses.com/T/#m518550baea9c76224524e44ab3ee5a0ecd01b1b9

The old version uses atomic types in xmit path to save tx stats for
ndo_get_stats64. But Andrew said that using atomic types in xmit path
isn't a good idea in even sample driver so I removed that code.


>> But, indeed, it is best if a `F:` entry is added wherever you think it
>> is best. Some subsystems may just add it to their entry (e.g. KUnit
>> wants to do that). Others may decide to split the Rust part into
>> another entry, so that maintainers may be a subset (or a different set
>> -- sometimes this could be done, for instance, if a new maintainer
>> shows up that wants to take care of the Rust abstractions).
> 
> I think F: would work for us.
> 
> Are there success stories in any subsystem for getting a driver for
> real HW supported? I think the best way to focus the effort would be 
> to set a target on a relatively simple device.

As far as I know, no subsystem has accepted Rust bindings yet.

Replacing the existing C driver for real HW with Rust new one doesn't
make sense, right? So a necessary condition of getting Rust bindings
for a subsystem accepted is that a HW verndor implements both a driver
and bindings for their new HW?

thanks,
Alice Ryhl June 16, 2023, 12:28 p.m. UTC | #8
On 6/15/23 08:01, Jakub Kicinski wrote:> I was hoping someone from the 
Rust side is going to review this.
> We try to review stuff within 48h at netdev, and there's no review :S

I'm from the Rust side, and I intend to review this, but I've taken this 
week off, so it wont be until next week.

> What is this supposed to be doing? Who needs to _set_ unrelated
> statistics from a function call? Yet no reviewer is complaining
> which either means I don't understand, or people aren't really 
> paying attention 
FUJITA Tomonori June 16, 2023, 1:02 p.m. UTC | #9
Hi,

On Thu, 15 Jun 2023 19:02:52 -0700
Jakub Kicinski <kuba@kernel.org> wrote:

> On Thu, 15 Jun 2023 14:51:10 +0200 Andrew Lunn wrote:
>> On Wed, Jun 14, 2023 at 11:01:28PM -0700, Jakub Kicinski wrote:
>> > On Tue, 13 Jun 2023 13:53:21 +0900 FUJITA Tomonori wrote:  
>> > > This patchset adds minimum Rust abstractions for network device
>> > > drivers and an example of a Rust network device driver, a simpler
>> > > version of drivers/net/dummy.c.
>> > > 
>> > > The dummy network device driver doesn't attach any bus such as PCI so
>> > > the dependency is minimum. Hopefully, it would make reviewing easier.
>> > > 
>> > > Thanks a lot for reviewing on RFC patchset at rust-for-linux ml.
>> > > Hopefully, I've addressed all the issues.  
>> > 
>> > First things first, what are the current expectations for subsystems
>> > accepting rust code?
>> > 
>> > I was hoping someone from the Rust side is going to review this.
>> > We try to review stuff within 48h at netdev, and there's no review :S  
>> 
>> As pointed out elsewhere, i've looked the code over. I cannot say
>> anything about the rust, but i don't see anything too obviously wrong
>> with the way it use a few netdev API calls.
> 
> The skb freeing looks shady from functional perspective.
> I'm guessing some form of destructor frees the skb automatically
> in xmit handler(?), but (a) no reason support, (b) kfree_skb_reason()
> is most certainly not safe to call on all xmit paths...

Yeah, I assume that a driver keeps a skb in private data structure
(such as tx ring) then removes the skb from it after the completion of
tx; automatically the drop() method runs (where we need to free the
skb).

I thought that calling dev_kfree_skb() is fine but no? We also need
something different for drivers that use other ways to free the skb
though.

I use kfree_skb_reason() because dev_kfree_skb() is a macro so it
can't be called directly from Rust. But I should have used
dev_kfree_skb() with a helper function.

thanks,
Andrew Lunn June 16, 2023, 1:04 p.m. UTC | #10
> Actually Andrew is interested, and PHY drivers seem relatively simple..
> /me runs away

:-)

I think because they are so simple, there is not much to gain by
implementing them in rust.

	Andrew
Andrew Lunn June 16, 2023, 1:14 p.m. UTC | #11
> I use kfree_skb_reason() because dev_kfree_skb() is a macro so it
> can't be called directly from Rust. But I should have used
> dev_kfree_skb() with a helper function.

I think this is something you need to get addressed at a language
level very soon. Lots of netdev API calls will be to macros. The API
to manipulate skbs is pretty much always used on the hot path, so i
expect that it will have a large number of macros. It is unclear to me
how well it will scale if you need to warp them all?

~/linux/include/linux$ grep inline skbuff.h  | wc
    349    2487   23010

Do you really want to write 300+ wrappers?

   Andrew
Miguel Ojeda June 16, 2023, 1:18 p.m. UTC | #12
On Fri, Jun 16, 2023 at 4:19 AM Jakub Kicinski <kuba@kernel.org> wrote:
>
> TBH I was hoping that the code will be more like reading "modern C++"
> for a C developer. I can't understand much of what's going on.

Yeah, there are some subtleties in the language, especially in
`unsafe` land and dealing with FFI, but please feel free to ask. I
think if one understands the complexities of modern C++, then
high-level Rust can be mapped fairly well.

> Are there success stories in any subsystem for getting a driver for
> real HW supported? I think the best way to focus the effort would be
> to set a target on a relatively simple device.

DRM is looking into start merging things [1] soon, as far as I
understand, and the GPU driver is being used in Asahi Linux.

If we go a bit outside "real HW", but still "non-sample/toy modules",
my understanding is that Binder is also looking into merging the Rust
implementation in the next few months [2] (they are submitting the
abstractions for their dependencies already).

Then there are other efforts like the NVMe driver, which is
progressing nicely and Andreas has shown nice performance results so
far [3]. Though the upstreaming story for some of those efforts may be
less clear (for different reasons).

Of course, I don't speak for any of them -- I am just trying to give a
summary of how things are going, and a positive outlook :) In the end,
if a subsystem is willing to give things a try, we appreciate it,
because it also makes things easier for others in the future, too.

Perhaps you may want to treat it as an experiment (possibly marking
things as experimental/staging/... explicitly if needed) that allows
you to both learn Rust and whether it suits your subsystem, as well as
a way to possibly get new people/future maintainers involved.

[1] https://lore.kernel.org/rust-for-linux/20230307-rust-drm-v1-0-917ff5bc80a8@asahilina.net/
[2] https://rust-for-linux.com/android-binder-driver
[3] https://rust-for-linux.com/nvme-driver

Cheers,
Miguel
Andrew Lunn June 16, 2023, 1:20 p.m. UTC | #13
> As for this being a single function rather than four functions, that's
> definitely a debatable decision. You would only do that if it makes sense to
> merge them together and if you would always assign all of them together. I
> don't know enough about these fields to say whether it makes sense here.

It can actually make sense to do them all together, because the source
of these is likely to be a per CPU data structure protected by a per
CPU sequence lock. You iterate over all CPUs, doing a transaction,
taking the sequence lock, copy the values, and then releasing the
lock. Taking and releases the lock per value is unnecessary expense.

      Andrew
Miguel Ojeda June 16, 2023, 1:23 p.m. UTC | #14
On Fri, Jun 16, 2023 at 2:18 PM FUJITA Tomonori
<fujita.tomonori@gmail.com> wrote:
>
> As far as I know, no subsystem has accepted Rust bindings yet.

For abstractions in general (see my previous reply for "real HW"
etc.), the KUnit subsystem [1] which is onboard and taking some
patches through their tree / ownership of the code.

[1] https://lore.kernel.org/rust-for-linux/CABVgOSnprvxzi-z42KFjOZsiRUv7u7E2poVGJNmTfS2OU4x4AA@mail.gmail.com/

> Replacing the existing C driver for real HW with Rust new one doesn't
> make sense, right? So a necessary condition of getting Rust bindings
> for a subsystem accepted is that a HW verndor implements both a driver
> and bindings for their new HW?

Not necessarily. It is true that, in general, the kernel does not
want/accept duplicate implementations.

However, this is a bit of a special situation, and there may be some
reasons to allow for it in a given subsystem. For instance:

  - The need to experiment with Rust.

  - To have an actual in-tree user that allows to develop the
abstractions for a subsystem, so that later they are ready to be used
for future, actual new drivers.

  - Pending redesigns: sometimes subsystems may have a
redesign/refactor/experiment that they have wanted to do for a while,
so they may take the chance to also try to write it in Rust anyway. Of
course, that could conflate two experiments, but... :)

  - Security: there may be some modules that have been problematic in
the past (especially if due to memory safety issues / data races), and
the subsystem may be willing to accept a parallel implementation to
see if it would be an improvement thanks to Rust's properties.

Cheers,
Miguel
Alice Ryhl June 16, 2023, 1:24 p.m. UTC | #15
On 6/16/23 15:20, Andrew Lunn wrote:
>> As for this being a single function rather than four functions, that's
>> definitely a debatable decision. You would only do that if it makes sense to
>> merge them together and if you would always assign all of them together. I
>> don't know enough about these fields to say whether it makes sense here.
> 
> It can actually make sense to do them all together, because the source
> of these is likely to be a per CPU data structure protected by a per
> CPU sequence lock. You iterate over all CPUs, doing a transaction,
> taking the sequence lock, copy the values, and then releasing the
> lock. Taking and releases the lock per value is unnecessary expense.

It can probably be split into several methods without introducing a lock 
call for each one, if the API is designed right.

Alice
FUJITA Tomonori June 16, 2023, 1:41 p.m. UTC | #16
Hi,

On Fri, 16 Jun 2023 15:23:01 +0200
Miguel Ojeda <miguel.ojeda.sandonis@gmail.com> wrote:

> On Fri, Jun 16, 2023 at 2:18 PM FUJITA Tomonori
> <fujita.tomonori@gmail.com> wrote:
>>
>> As far as I know, no subsystem has accepted Rust bindings yet.
> 
> For abstractions in general (see my previous reply for "real HW"
> etc.), the KUnit subsystem [1] which is onboard and taking some
> patches through their tree / ownership of the code.
> 
> [1] https://lore.kernel.org/rust-for-linux/CABVgOSnprvxzi-z42KFjOZsiRUv7u7E2poVGJNmTfS2OU4x4AA@mail.gmail.com/
> 
>> Replacing the existing C driver for real HW with Rust new one doesn't
>> make sense, right? So a necessary condition of getting Rust bindings
>> for a subsystem accepted is that a HW verndor implements both a driver
>> and bindings for their new HW?
> 
> Not necessarily. It is true that, in general, the kernel does not
> want/accept duplicate implementations.
> 
> However, this is a bit of a special situation, and there may be some
> reasons to allow for it in a given subsystem. For instance:
> 
>   - The need to experiment with Rust.
> 
>   - To have an actual in-tree user that allows to develop the
> abstractions for a subsystem, so that later they are ready to be used
> for future, actual new drivers.
> 
>   - Pending redesigns: sometimes subsystems may have a
> redesign/refactor/experiment that they have wanted to do for a while,
> so they may take the chance to also try to write it in Rust anyway. Of
> course, that could conflate two experiments, but... :)
> 
>   - Security: there may be some modules that have been problematic in
> the past (especially if due to memory safety issues / data races), and
> the subsystem may be willing to accept a parallel implementation to
> see if it would be an improvement thanks to Rust's properties.

Yeah, I know :) But what I want to know is if there are such reasons
for netdev subsystem, a necessary condition of getting Rust bindings
accepted for the netdev maintainers.

thanks,
Miguel Ojeda June 16, 2023, 1:48 p.m. UTC | #17
On Fri, Jun 16, 2023 at 3:14 PM Andrew Lunn <andrew@lunn.ch> wrote:
>
> I think this is something you need to get addressed at a language
> level very soon. Lots of netdev API calls will be to macros. The API
> to manipulate skbs is pretty much always used on the hot path, so i
> expect that it will have a large number of macros. It is unclear to me
> how well it will scale if you need to warp them all?
>
> ~/linux/include/linux$ grep inline skbuff.h  | wc
>     349    2487   23010
>
> Do you really want to write 300+ wrappers?

It would be very nice if at least `bindgen` (or even the Rust
compiler... :) could cover many of these one-liners. We have discussed
and asked for this in the past, and messages like this reinforce the
need/request for this clearly, so thanks for this.

Since `bindgen` 0.64.0 earlier this year [1] there is an experimental
feature for this (`--wrap-static-fns`), so that is nice -- though we
need to see how well it works. We are upgrading `bindgen` to the
latest version after the merge window, so we can play with this soon.

In particular, given:

    static inline int foo(int a, int b) {
        return a + b;
    }

It generates a C file with e.g.:

    #include "a.h"

    // Static wrappers

    int foo__extern(int a, int b) { return foo(a, b); }

And then in the usual Rust bindings:

    extern "C" {
        #[link_name = "foo__extern"]
        pub fn foo(a: ::std::os::raw::c_int, b: ::std::os::raw::c_int)
-> ::std::os::raw::c_int;
    }

Cheers,
Miguel
Andrew Lunn June 16, 2023, 2:43 p.m. UTC | #18
On Fri, Jun 16, 2023 at 03:48:31PM +0200, Miguel Ojeda wrote:
> On Fri, Jun 16, 2023 at 3:14 PM Andrew Lunn <andrew@lunn.ch> wrote:
> >
> > I think this is something you need to get addressed at a language
> > level very soon. Lots of netdev API calls will be to macros. The API
> > to manipulate skbs is pretty much always used on the hot path, so i
> > expect that it will have a large number of macros. It is unclear to me
> > how well it will scale if you need to warp them all?
> >
> > ~/linux/include/linux$ grep inline skbuff.h  | wc
> >     349    2487   23010
> >
> > Do you really want to write 300+ wrappers?
> 
> It would be very nice if at least `bindgen` (or even the Rust
> compiler... :) could cover many of these one-liners. We have discussed
> and asked for this in the past, and messages like this reinforce the
> need/request for this clearly, so thanks for this.
> 
> Since `bindgen` 0.64.0 earlier this year [1] there is an experimental
> feature for this (`--wrap-static-fns`), so that is nice -- though we
> need to see how well it works. We are upgrading `bindgen` to the
> latest version after the merge window, so we can play with this soon.
> 
> In particular, given:
> 
>     static inline int foo(int a, int b) {
>         return a + b;
>     }
> 
> It generates a C file with e.g.:
> 
>     #include "a.h"
> 
>     // Static wrappers
> 
>     int foo__extern(int a, int b) { return foo(a, b); }
> 
> And then in the usual Rust bindings:
> 
>     extern "C" {
>         #[link_name = "foo__extern"]
>         pub fn foo(a: ::std::os::raw::c_int, b: ::std::os::raw::c_int)
> -> ::std::os::raw::c_int;
>     }

I said in another email, i don't want to suggest premature
optimisation, before profiling is done. But in C, these functions are
inline for a reason. We don't want the cost of a subroutine call. We
want the compiler to be able to inline the code, and the optimiser to
be able to see it and generate the best code it can.

Can the rust compile inline the binding including the FFI call?

skb are used on the hot path.  For a 10G Ethernet, it is dealing with
nearly 1 million packets per second for big Ethernet frames, or worst
case 14Mpps for small frames. Subroutine calls add up when you only
have around 200 instructions to deal with a Ethernet frame.

     Andrew
Miguel Ojeda June 16, 2023, 4:01 p.m. UTC | #19
On Fri, Jun 16, 2023 at 4:43 PM Andrew Lunn <andrew@lunn.ch> wrote:
>
> I said in another email, i don't want to suggest premature
> optimisation, before profiling is done. But in C, these functions are
> inline for a reason. We don't want the cost of a subroutine call. We
> want the compiler to be able to inline the code, and the optimiser to
> be able to see it and generate the best code it can.

See my reply in v2 to your message where I mentioned some of the
options we are considering [1] -- not sure if you saw it:

> Yeah, other use cases will also need that solved, e.g. Andreas for his
> NVMe work.
>
> We discussed reimplementing performance-critical bits in Rust as you
> suggest, as well as cross-language LTO. We also talked about possible
> alternative approaches like "manual local LTO" for the helpers only
> via feeding their LLVM IR to `rustc`, which may recover most of the
> performance without having to go for full LTO and its associated
> kernel link times.

[1] https://lore.kernel.org/rust-for-linux/CANiq72kyUhvmG6KB32X1vuhNzOOJbs7R1JbK+vnPELX4tG73RA@mail.gmail.com/

Cheers,
Miguel
Andrew Lunn June 16, 2023, 5:59 p.m. UTC | #20
On Thu, Jun 15, 2023 at 08:47:43PM -0700, Richard Cochran wrote:
> On Thu, Jun 15, 2023 at 07:02:52PM -0700, Jakub Kicinski wrote:
> 
> > IMO we have too much useless playground stuff in C already, don't get me
> > started. Let's get a real device prove that this thing can actually work
> > :( We can waste years implementing imaginary device drivers that nobody
> > ends up using.
> 
> Yes, the long standing policy seems to be not to merge "dummy"
> drivers.  The original PTP patch set had one, but at the time the
> reviews said firmly "No" to that.

Hi Richard

I think in the modern day, you could probably add code to netdevsim to
aid testing PTP. It should however by accompanied with selftests which
actually make use of what you add.

	Andrew
Jakub Kicinski June 16, 2023, 6:26 p.m. UTC | #21
On Fri, 16 Jun 2023 15:23:01 +0200 Miguel Ojeda wrote:
> Not necessarily. It is true that, in general, the kernel does not
> want/accept duplicate implementations.
> 
> However, this is a bit of a special situation, and there may be some
> reasons to allow for it in a given subsystem. For instance:
> 
>   - The need to experiment with Rust.

Duplicated driver in a new language means nobody has a real incentive
to use it in production. That really mutes the signal we get out of the
experiment. At the same time IIUC building the Rust code is not trivial,
so IDK if we're ready to force people to use it. Ugh.

Do you have any idea how long it will take until one can 
 dnf install $rust
and have that be enough to be build a kernel (for the two major arches)?
Jakub Kicinski June 16, 2023, 6:31 p.m. UTC | #22
On Fri, 16 Jun 2023 15:04:59 +0200 Andrew Lunn wrote:
> > Actually Andrew is interested, and PHY drivers seem relatively simple..
> > /me runs away  
> 
> :-)
> 
> I think because they are so simple, there is not much to gain by
> implementing them in rust.

I see many benefits :) Its a smallish and self-contained piece so it's
achievable. Yet it interacts (to the extent the complexity of the PHY
calls for it) with core netdev structures. Some PHYs even do
timestamping which brings in interactions with skbs etc.

Major benefit number 2 is that Rust is currently missing any real life
bus bindings (AFAIU) and PHYs and the bus they live on are maintained
by the same person 
Jakub Kicinski June 16, 2023, 6:40 p.m. UTC | #23
On Fri, 16 Jun 2023 22:02:20 +0900 (JST) FUJITA Tomonori wrote:
> > The skb freeing looks shady from functional perspective.
> > I'm guessing some form of destructor frees the skb automatically
> > in xmit handler(?), but (a) no reason support, (b) kfree_skb_reason()
> > is most certainly not safe to call on all xmit paths...  
> 
> Yeah, I assume that a driver keeps a skb in private data structure
> (such as tx ring) then removes the skb from it after the completion of
> tx; automatically the drop() method runs (where we need to free the
> skb).
> 
> I thought that calling dev_kfree_skb() is fine but no? We also need
> something different for drivers that use other ways to free the skb
> though.
> 
> I use kfree_skb_reason() because dev_kfree_skb() is a macro so it
> can't be called directly from Rust. But I should have used
> dev_kfree_skb() with a helper function.

skbs (generally) can't be freed in an interrupt context.
dev_kfree_skb_any_reason() is probably the most general implementation.
But then we also have a set of functions used in known contexts for fast
object recycling like napi_consume_skb().
How would complex object destruction rules fit in in the Rust world?
Alice Ryhl June 16, 2023, 7 p.m. UTC | #24
On 6/16/23 20:40, Jakub Kicinski wrote:
> On Fri, 16 Jun 2023 22:02:20 +0900 (JST) FUJITA Tomonori wrote:
>>> The skb freeing looks shady from functional perspective.
>>> I'm guessing some form of destructor frees the skb automatically
>>> in xmit handler(?), but (a) no reason support, (b) kfree_skb_reason()
>>> is most certainly not safe to call on all xmit paths...
>>
>> Yeah, I assume that a driver keeps a skb in private data structure
>> (such as tx ring) then removes the skb from it after the completion of
>> tx; automatically the drop() method runs (where we need to free the
>> skb).
>>
>> I thought that calling dev_kfree_skb() is fine but no? We also need
>> something different for drivers that use other ways to free the skb
>> though.
>>
>> I use kfree_skb_reason() because dev_kfree_skb() is a macro so it
>> can't be called directly from Rust. But I should have used
>> dev_kfree_skb() with a helper function.
> 
> skbs (generally) can't be freed in an interrupt context.
> dev_kfree_skb_any_reason() is probably the most general implementation.
> But then we also have a set of functions used in known contexts for fast
> object recycling like napi_consume_skb().
> How would complex object destruction rules fit in in the Rust world?

A Rust method can be defined to take the struct "by value", which 
consumes the struct and prevents you from using it again. This can let 
you provide many different cleanup methods that each clean it up in 
different ways.

However, you cannot force the user to use one of those methods. They 
always have the option of letting the value go out of scope, which calls 
the destructor. And they can do this at any time.

That said, the destructor of the value does not necessarily *have* to 
translate to immediately freeing the value. If the value if refcounted, 
the destructor could just drop the refcount. It would also be possible 
for a destructor to schedule the cleanup operation to a workqueue. Or 
you could do something more clever.

Alice
Jakub Kicinski June 16, 2023, 7:10 p.m. UTC | #25
On Fri, 16 Jun 2023 21:00:36 +0200 Alice Ryhl wrote:
> A Rust method can be defined to take the struct "by value", which 
> consumes the struct and prevents you from using it again. This can let 
> you provide many different cleanup methods that each clean it up in 
> different ways.
> 
> However, you cannot force the user to use one of those methods. They 
> always have the option of letting the value go out of scope, which calls 
> the destructor. And they can do this at any time.
> 
> That said, the destructor of the value does not necessarily *have* to 
> translate to immediately freeing the value. If the value if refcounted, 
> the destructor could just drop the refcount. It would also be possible 
> for a destructor to schedule the cleanup operation to a workqueue. Or 
> you could do something more clever.

Can we put a WARN_ON() in the destructor and expect object to never be
implicitly freed?  skbs represent packets (most of the time) and for
tracking which part of the stack is dropping packets we try to provide
a drop reason along the freed skb. It'd be great if for Rust we could
from the get-go direct everyone towards the APIs with an explicit reason
code.
Alice Ryhl June 16, 2023, 7:23 p.m. UTC | #26
On 6/16/23 21:10, Jakub Kicinski wrote:
> On Fri, 16 Jun 2023 21:00:36 +0200 Alice Ryhl wrote:
>> A Rust method can be defined to take the struct "by value", which
>> consumes the struct and prevents you from using it again. This can let
>> you provide many different cleanup methods that each clean it up in
>> different ways.
>>
>> However, you cannot force the user to use one of those methods. They
>> always have the option of letting the value go out of scope, which calls
>> the destructor. And they can do this at any time.
>>
>> That said, the destructor of the value does not necessarily *have* to
>> translate to immediately freeing the value. If the value if refcounted,
>> the destructor could just drop the refcount. It would also be possible
>> for a destructor to schedule the cleanup operation to a workqueue. Or
>> you could do something more clever.
> 
> Can we put a WARN_ON() in the destructor and expect object to never be
> implicitly freed?  skbs represent packets (most of the time) and for
> tracking which part of the stack is dropping packets we try to provide
> a drop reason along the freed skb. It'd be great if for Rust we could
> from the get-go direct everyone towards the APIs with an explicit reason
> code.

Yes, you can certainly put a WARN_ON in the destructor.

Another possibility is to use a scope to clean up. I don't know anything 
about these skb objects are used, but you could have the user define a 
"process this socket" function that you pass a pointer to the skb, then 
make the return value be something that explains what should be done 
with the packet. Since you must return a value of the right type, this 
forces you to choose.

Of course, this requires that the processing of packets can be expressed 
as a function call, where it only inspects the packet for the duration 
of that function call. (Lifetimes can ensure that the skb pointer does 
not escape the function.)

Would something like that work?

Alice
Andrew Lunn June 16, 2023, 8:04 p.m. UTC | #27
> Yes, you can certainly put a WARN_ON in the destructor.
> 
> Another possibility is to use a scope to clean up. I don't know anything
> about these skb objects are used, but you could have the user define a
> "process this socket" function that you pass a pointer to the skb, then make
> the return value be something that explains what should be done with the
> packet. Since you must return a value of the right type, this forces you to
> choose.
> 
> Of course, this requires that the processing of packets can be expressed as
> a function call, where it only inspects the packet for the duration of that
> function call. (Lifetimes can ensure that the skb pointer does not escape
> the function.)
> 
> Would something like that work?

I don't think so, at least not in the contest of an Rust Ethernet
driver.

There are two main flows.

A packet is received. An skb is allocated and the received packet is
placed into the skb. The Ethernet driver then hands the packet over to
the network stack. The network stack is free to do whatever it wants
with the packet. Things can go wrong within the driver, so at times it
needs to free the skb rather than pass it to the network stack, which
would be a drop.

The second flow is that the network stack has a packet it wants sent
out an Ethernet port, in the form of an skb. The skb gets passed to
the Ethernet driver. The driver will do whatever it needs to do to
pass the contents of the skb to the hardware. Once the hardware has
it, the driver frees the skb. Again, things can go wrong and it needs
to free the skb without sending it, which is a drop.

So the lifetime is not a simple function call.

The drop reason indicates why the packet was dropped. It should give
some indication of what problem occurred which caused the drop. So
ideally we don't want an anonymous drop. The C code does not enforce
that, but it would be nice if the rust wrapper to dispose of an skb
did enforce it.

I would also say that this dummy driver and the C dummy driver is
actually wrong in 'dropping' the frame. Its whole purpose in life is to
be a black hole. It should only drop the packet if for some reason it
cannot throw the packet into the black hole.

       Andrew
Miguel Ojeda June 16, 2023, 8:05 p.m. UTC | #28
On Fri, Jun 16, 2023 at 8:26 PM Jakub Kicinski <kuba@kernel.org> wrote:
>
> Duplicated driver in a new language means nobody has a real incentive
> to use it in production. That really mutes the signal we get out of the
> experiment.

What I was trying to say is that there may be other incentives for
using the new one, like the ones I gave in the list.

Even if there is no incentive for using it right now, one may still
want to build/design it in order to evaluate Rust and/or prepare
abstractions for future drivers. This was the reason for the NVMe
driver request we got back then, for instance.

> At the same time IIUC building the Rust code is not trivial,
> so IDK if we're ready to force people to use it. Ugh.

I am not sure what you mean -- building works the same way as you have
done now, i.e. your usual build commands with `make`.

If you mean installing the toolchain, then we have a Quick Start guide
[1], and a script that checks for the requirements.

[1] https://docs.kernel.org/rust/quick-start.html

> Do you have any idea how long it will take until one can
>  dnf install $rust
> and have that be enough to be build a kernel (for the two major arches)?

You may do it today if the version matches, which for Fedora it should
e.g. next week or so when we merge the upgrade to 1.70, since Fedora
has now 1.70 [2].

But since they may not always align, I would recommend using `rustup`
(or the standalone installers) until we can establish a minimum
version. Please see the Quick Start guide for details [1].

Other distributions may provide a custom package with the required
dependencies for building Rust kernel code, like Ubuntu does.

[2] https://packages.fedoraproject.org/pkgs/rust/rust/

Cheers,
Miguel
Alice Ryhl June 17, 2023, 10:08 a.m. UTC | #29
On 6/16/23 22:04, Andrew Lunn wrote:
>> Yes, you can certainly put a WARN_ON in the destructor.
>>
>> Another possibility is to use a scope to clean up. I don't know anything
>> about these skb objects are used, but you could have the user define a
>> "process this socket" function that you pass a pointer to the skb, then make
>> the return value be something that explains what should be done with the
>> packet. Since you must return a value of the right type, this forces you to
>> choose.
>>
>> Of course, this requires that the processing of packets can be expressed as
>> a function call, where it only inspects the packet for the duration of that
>> function call. (Lifetimes can ensure that the skb pointer does not escape
>> the function.)
>>
>> Would something like that work?
> 
> I don't think so, at least not in the contest of an Rust Ethernet
> driver.
> 
> There are two main flows.
> 
> A packet is received. An skb is allocated and the received packet is
> placed into the skb. The Ethernet driver then hands the packet over to
> the network stack. The network stack is free to do whatever it wants
> with the packet. Things can go wrong within the driver, so at times it
> needs to free the skb rather than pass it to the network stack, which
> would be a drop.
> 
> The second flow is that the network stack has a packet it wants sent
> out an Ethernet port, in the form of an skb. The skb gets passed to
> the Ethernet driver. The driver will do whatever it needs to do to
> pass the contents of the skb to the hardware. Once the hardware has
> it, the driver frees the skb. Again, things can go wrong and it needs
> to free the skb without sending it, which is a drop.
> 
> So the lifetime is not a simple function call.
> 
> The drop reason indicates why the packet was dropped. It should give
> some indication of what problem occurred which caused the drop. So
> ideally we don't want an anonymous drop. The C code does not enforce
> that, but it would be nice if the rust wrapper to dispose of an skb
> did enforce it.

It sounds like a destructor with WARN_ON is the best approach right now.

Unfortunately, I don't think we can enforce that the destructor is not 
used today. That said, in the future it may be possible to implement a 
linter that detects it - I know that there have already been experiments 
with other custom lints for the kernel (e.g., enforcing that you don't 
sleep while holding a spinlock).

> I would also say that this dummy driver and the C dummy driver is
> actually wrong in 'dropping' the frame. Its whole purpose in life is to
> be a black hole. It should only drop the packet if for some reason it
> cannot throw the packet into the black hole.

Ah, I suppose that we would also need a "by value" cleanup method for 
that case.

Alice
Greg Kroah-Hartman June 17, 2023, 10:15 a.m. UTC | #30
On Sat, Jun 17, 2023 at 12:08:26PM +0200, Alice Ryhl wrote:
> On 6/16/23 22:04, Andrew Lunn wrote:
> > > Yes, you can certainly put a WARN_ON in the destructor.
> > > 
> > > Another possibility is to use a scope to clean up. I don't know anything
> > > about these skb objects are used, but you could have the user define a
> > > "process this socket" function that you pass a pointer to the skb, then make
> > > the return value be something that explains what should be done with the
> > > packet. Since you must return a value of the right type, this forces you to
> > > choose.
> > > 
> > > Of course, this requires that the processing of packets can be expressed as
> > > a function call, where it only inspects the packet for the duration of that
> > > function call. (Lifetimes can ensure that the skb pointer does not escape
> > > the function.)
> > > 
> > > Would something like that work?
> > 
> > I don't think so, at least not in the contest of an Rust Ethernet
> > driver.
> > 
> > There are two main flows.
> > 
> > A packet is received. An skb is allocated and the received packet is
> > placed into the skb. The Ethernet driver then hands the packet over to
> > the network stack. The network stack is free to do whatever it wants
> > with the packet. Things can go wrong within the driver, so at times it
> > needs to free the skb rather than pass it to the network stack, which
> > would be a drop.
> > 
> > The second flow is that the network stack has a packet it wants sent
> > out an Ethernet port, in the form of an skb. The skb gets passed to
> > the Ethernet driver. The driver will do whatever it needs to do to
> > pass the contents of the skb to the hardware. Once the hardware has
> > it, the driver frees the skb. Again, things can go wrong and it needs
> > to free the skb without sending it, which is a drop.
> > 
> > So the lifetime is not a simple function call.
> > 
> > The drop reason indicates why the packet was dropped. It should give
> > some indication of what problem occurred which caused the drop. So
> > ideally we don't want an anonymous drop. The C code does not enforce
> > that, but it would be nice if the rust wrapper to dispose of an skb
> > did enforce it.
> 
> It sounds like a destructor with WARN_ON is the best approach right now.

Note, if that WARN_ON fires, you just rebooted the machine as the huge
majority of all Linux systems in the world run with "panic on warn"
enabled :(

So almost NEVER do a WARN* call if at all possible, unless there is no
way back, and you can not recover from the mess, and crashing the system
is your only recourse (instead of locking it up.)

So please, don't do that, recover and report an error to the caller if
possible and let them handle it properly.  Yes, in a descructor that's
almost impossible, but then perhaps if this is a real problem, something
needs to be changed somewhere else in the callpath.

thanks,

greg k-h
FUJITA Tomonori June 19, 2023, 8:50 a.m. UTC | #31
Hi,

On Sat, 17 Jun 2023 12:08:26 +0200
Alice Ryhl <alice@ryhl.io> wrote:

> On 6/16/23 22:04, Andrew Lunn wrote:
>>> Yes, you can certainly put a WARN_ON in the destructor.
>>>
>>> Another possibility is to use a scope to clean up. I don't know
>>> anything
>>> about these skb objects are used, but you could have the user define a
>>> "process this socket" function that you pass a pointer to the skb,
>>> then make
>>> the return value be something that explains what should be done with
>>> the
>>> packet. Since you must return a value of the right type, this forces
>>> you to
>>> choose.
>>>
>>> Of course, this requires that the processing of packets can be
>>> expressed as
>>> a function call, where it only inspects the packet for the duration of
>>> that
>>> function call. (Lifetimes can ensure that the skb pointer does not
>>> escape
>>> the function.)
>>>
>>> Would something like that work?
>> I don't think so, at least not in the contest of an Rust Ethernet
>> driver.
>> There are two main flows.
>> A packet is received. An skb is allocated and the received packet is
>> placed into the skb. The Ethernet driver then hands the packet over to
>> the network stack. The network stack is free to do whatever it wants
>> with the packet. Things can go wrong within the driver, so at times it
>> needs to free the skb rather than pass it to the network stack, which
>> would be a drop.
>> The second flow is that the network stack has a packet it wants sent
>> out an Ethernet port, in the form of an skb. The skb gets passed to
>> the Ethernet driver. The driver will do whatever it needs to do to
>> pass the contents of the skb to the hardware. Once the hardware has
>> it, the driver frees the skb. Again, things can go wrong and it needs
>> to free the skb without sending it, which is a drop.
>> So the lifetime is not a simple function call.
>> The drop reason indicates why the packet was dropped. It should give
>> some indication of what problem occurred which caused the drop. So
>> ideally we don't want an anonymous drop. The C code does not enforce
>> that, but it would be nice if the rust wrapper to dispose of an skb
>> did enforce it.
> 
> It sounds like a destructor with WARN_ON is the best approach right
> now.

Better to simply BUG()? We want to make sure that a device driver
explicity calls a function that consumes a skb object (on tx path,
e.g., napi_consume_skb()). If a device driver doesn't call such, it's
a bug that should be found easily and fixed during the development. It
would be even better if the compiler could find such though.

If Rust bindings for netdev could help device developpers in such way,
it's worth an experiments? because looks like netdev subsystem accepts
more drivers for new hardware than other subsystems.

thanks,
Greg KH June 19, 2023, 9:46 a.m. UTC | #32
On Mon, Jun 19, 2023 at 05:50:03PM +0900, FUJITA Tomonori wrote:
> Hi,
> 
> On Sat, 17 Jun 2023 12:08:26 +0200
> Alice Ryhl <alice@ryhl.io> wrote:
> 
> > On 6/16/23 22:04, Andrew Lunn wrote:
> >>> Yes, you can certainly put a WARN_ON in the destructor.
> >>>
> >>> Another possibility is to use a scope to clean up. I don't know
> >>> anything
> >>> about these skb objects are used, but you could have the user define a
> >>> "process this socket" function that you pass a pointer to the skb,
> >>> then make
> >>> the return value be something that explains what should be done with
> >>> the
> >>> packet. Since you must return a value of the right type, this forces
> >>> you to
> >>> choose.
> >>>
> >>> Of course, this requires that the processing of packets can be
> >>> expressed as
> >>> a function call, where it only inspects the packet for the duration of
> >>> that
> >>> function call. (Lifetimes can ensure that the skb pointer does not
> >>> escape
> >>> the function.)
> >>>
> >>> Would something like that work?
> >> I don't think so, at least not in the contest of an Rust Ethernet
> >> driver.
> >> There are two main flows.
> >> A packet is received. An skb is allocated and the received packet is
> >> placed into the skb. The Ethernet driver then hands the packet over to
> >> the network stack. The network stack is free to do whatever it wants
> >> with the packet. Things can go wrong within the driver, so at times it
> >> needs to free the skb rather than pass it to the network stack, which
> >> would be a drop.
> >> The second flow is that the network stack has a packet it wants sent
> >> out an Ethernet port, in the form of an skb. The skb gets passed to
> >> the Ethernet driver. The driver will do whatever it needs to do to
> >> pass the contents of the skb to the hardware. Once the hardware has
> >> it, the driver frees the skb. Again, things can go wrong and it needs
> >> to free the skb without sending it, which is a drop.
> >> So the lifetime is not a simple function call.
> >> The drop reason indicates why the packet was dropped. It should give
> >> some indication of what problem occurred which caused the drop. So
> >> ideally we don't want an anonymous drop. The C code does not enforce
> >> that, but it would be nice if the rust wrapper to dispose of an skb
> >> did enforce it.
> > 
> > It sounds like a destructor with WARN_ON is the best approach right
> > now.
> 
> Better to simply BUG()? We want to make sure that a device driver
> explicity calls a function that consumes a skb object (on tx path,
> e.g., napi_consume_skb()). If a device driver doesn't call such, it's
> a bug that should be found easily and fixed during the development. It
> would be even better if the compiler could find such though.

No, BUG() means "I have given up all hope here because the hardware is
broken and beyond repair so the machine will now crash and take all of
your data with it because I don't know how to properly recover".  That
should NEVER happen in a device driver, as that's very presumptious of
it, and means the driver itself is broken.

Report the error back up the chain and handle it properly, that's the
correct thing to do.

> If Rust bindings for netdev could help device developpers in such way,
> it's worth an experiments? because looks like netdev subsystem accepts
> more drivers for new hardware than other subsystems.

Have you looked at the IIO subsystem?  :)

thanks,

greg k-h
FUJITA Tomonori June 19, 2023, 11:05 a.m. UTC | #33
Hi,

On Mon, 19 Jun 2023 11:46:38 +0200
Greg KH <greg@kroah.com> wrote:

> On Mon, Jun 19, 2023 at 05:50:03PM +0900, FUJITA Tomonori wrote:
>> Hi,
>> 
>> On Sat, 17 Jun 2023 12:08:26 +0200
>> Alice Ryhl <alice@ryhl.io> wrote:
>> 
>> > On 6/16/23 22:04, Andrew Lunn wrote:
>> >>> Yes, you can certainly put a WARN_ON in the destructor.
>> >>>
>> >>> Another possibility is to use a scope to clean up. I don't know
>> >>> anything
>> >>> about these skb objects are used, but you could have the user define a
>> >>> "process this socket" function that you pass a pointer to the skb,
>> >>> then make
>> >>> the return value be something that explains what should be done with
>> >>> the
>> >>> packet. Since you must return a value of the right type, this forces
>> >>> you to
>> >>> choose.
>> >>>
>> >>> Of course, this requires that the processing of packets can be
>> >>> expressed as
>> >>> a function call, where it only inspects the packet for the duration of
>> >>> that
>> >>> function call. (Lifetimes can ensure that the skb pointer does not
>> >>> escape
>> >>> the function.)
>> >>>
>> >>> Would something like that work?
>> >> I don't think so, at least not in the contest of an Rust Ethernet
>> >> driver.
>> >> There are two main flows.
>> >> A packet is received. An skb is allocated and the received packet is
>> >> placed into the skb. The Ethernet driver then hands the packet over to
>> >> the network stack. The network stack is free to do whatever it wants
>> >> with the packet. Things can go wrong within the driver, so at times it
>> >> needs to free the skb rather than pass it to the network stack, which
>> >> would be a drop.
>> >> The second flow is that the network stack has a packet it wants sent
>> >> out an Ethernet port, in the form of an skb. The skb gets passed to
>> >> the Ethernet driver. The driver will do whatever it needs to do to
>> >> pass the contents of the skb to the hardware. Once the hardware has
>> >> it, the driver frees the skb. Again, things can go wrong and it needs
>> >> to free the skb without sending it, which is a drop.
>> >> So the lifetime is not a simple function call.
>> >> The drop reason indicates why the packet was dropped. It should give
>> >> some indication of what problem occurred which caused the drop. So
>> >> ideally we don't want an anonymous drop. The C code does not enforce
>> >> that, but it would be nice if the rust wrapper to dispose of an skb
>> >> did enforce it.
>> > 
>> > It sounds like a destructor with WARN_ON is the best approach right
>> > now.
>> 
>> Better to simply BUG()? We want to make sure that a device driver
>> explicity calls a function that consumes a skb object (on tx path,
>> e.g., napi_consume_skb()). If a device driver doesn't call such, it's
>> a bug that should be found easily and fixed during the development. It
>> would be even better if the compiler could find such though.
> 
> No, BUG() means "I have given up all hope here because the hardware is
> broken and beyond repair so the machine will now crash and take all of
> your data with it because I don't know how to properly recover".  That
> should NEVER happen in a device driver, as that's very presumptious of
> it, and means the driver itself is broken.
> 
> Report the error back up the chain and handle it properly, that's the
> correct thing to do.

I see. Then netdev_warn() should be used instead.

Is it possible to handle the case where a device driver wrongly
doesn't consume a skb object?


>> If Rust bindings for netdev could help device developpers in such way,
>> it's worth an experiments? because looks like netdev subsystem accepts
>> more drivers for new hardware than other subsystems.
> 
> Have you looked at the IIO subsystem?  :)

No, I've not. Are there possible drivers that Rust could be useful
for?

thanks,
Greg KH June 19, 2023, 11:14 a.m. UTC | #34
On Mon, Jun 19, 2023 at 08:05:59PM +0900, FUJITA Tomonori wrote:
> On Mon, 19 Jun 2023 11:46:38 +0200
> Greg KH <greg@kroah.com> wrote:
> 
> > On Mon, Jun 19, 2023 at 05:50:03PM +0900, FUJITA Tomonori wrote:
> >> Hi,
> >> 
> >> On Sat, 17 Jun 2023 12:08:26 +0200
> >> Alice Ryhl <alice@ryhl.io> wrote:
> >> 
> >> > On 6/16/23 22:04, Andrew Lunn wrote:
> >> >>> Yes, you can certainly put a WARN_ON in the destructor.
> >> >>>
> >> >>> Another possibility is to use a scope to clean up. I don't know
> >> >>> anything
> >> >>> about these skb objects are used, but you could have the user define a
> >> >>> "process this socket" function that you pass a pointer to the skb,
> >> >>> then make
> >> >>> the return value be something that explains what should be done with
> >> >>> the
> >> >>> packet. Since you must return a value of the right type, this forces
> >> >>> you to
> >> >>> choose.
> >> >>>
> >> >>> Of course, this requires that the processing of packets can be
> >> >>> expressed as
> >> >>> a function call, where it only inspects the packet for the duration of
> >> >>> that
> >> >>> function call. (Lifetimes can ensure that the skb pointer does not
> >> >>> escape
> >> >>> the function.)
> >> >>>
> >> >>> Would something like that work?
> >> >> I don't think so, at least not in the contest of an Rust Ethernet
> >> >> driver.
> >> >> There are two main flows.
> >> >> A packet is received. An skb is allocated and the received packet is
> >> >> placed into the skb. The Ethernet driver then hands the packet over to
> >> >> the network stack. The network stack is free to do whatever it wants
> >> >> with the packet. Things can go wrong within the driver, so at times it
> >> >> needs to free the skb rather than pass it to the network stack, which
> >> >> would be a drop.
> >> >> The second flow is that the network stack has a packet it wants sent
> >> >> out an Ethernet port, in the form of an skb. The skb gets passed to
> >> >> the Ethernet driver. The driver will do whatever it needs to do to
> >> >> pass the contents of the skb to the hardware. Once the hardware has
> >> >> it, the driver frees the skb. Again, things can go wrong and it needs
> >> >> to free the skb without sending it, which is a drop.
> >> >> So the lifetime is not a simple function call.
> >> >> The drop reason indicates why the packet was dropped. It should give
> >> >> some indication of what problem occurred which caused the drop. So
> >> >> ideally we don't want an anonymous drop. The C code does not enforce
> >> >> that, but it would be nice if the rust wrapper to dispose of an skb
> >> >> did enforce it.
> >> > 
> >> > It sounds like a destructor with WARN_ON is the best approach right
> >> > now.
> >> 
> >> Better to simply BUG()? We want to make sure that a device driver
> >> explicity calls a function that consumes a skb object (on tx path,
> >> e.g., napi_consume_skb()). If a device driver doesn't call such, it's
> >> a bug that should be found easily and fixed during the development. It
> >> would be even better if the compiler could find such though.
> > 
> > No, BUG() means "I have given up all hope here because the hardware is
> > broken and beyond repair so the machine will now crash and take all of
> > your data with it because I don't know how to properly recover".  That
> > should NEVER happen in a device driver, as that's very presumptious of
> > it, and means the driver itself is broken.
> > 
> > Report the error back up the chain and handle it properly, that's the
> > correct thing to do.
> 
> I see. Then netdev_warn() should be used instead.

Yes.

> Is it possible to handle the case where a device driver wrongly
> doesn't consume a skb object?

I have no idea as I do not know the network driver stack, sorry.

> >> If Rust bindings for netdev could help device developpers in such way,
> >> it's worth an experiments? because looks like netdev subsystem accepts
> >> more drivers for new hardware than other subsystems.
> > 
> > Have you looked at the IIO subsystem?  :)
> 
> No, I've not. Are there possible drivers that Rust could be useful
> for?

Who knows, you said you were looking for subsystems with lots and lots
of new drivers, and the IIO subsystem has over a thousand of them, all
just added in the past few years.

good luck!

greg k-h
Emilio Cobos Álvarez June 19, 2023, 11:27 a.m. UTC | #35
Hi Andrew, Miguel,

On 6/16/23 16:43, Andrew Lunn wrote:
> I said in another email, i don't want to suggest premature
> optimisation, before profiling is done. But in C, these functions are
> inline for a reason. We don't want the cost of a subroutine call. We
> want the compiler to be able to inline the code, and the optimiser to
> be able to see it and generate the best code it can.
> 
> Can the rust compile inline the binding including the FFI call?

This is possible, with cross-language LTO, see:

   https://blog.llvm.org/2019/09/closing-gap-cross-language-lto-between.html

There are some requirements that need to happen for that to work 
(mainly, I believe, that the LLVM version used by rustc and clang agree).

But in general it is possible. We use it extensively on Firefox. Of 
course the requirements of Firefox and the kernel might be different.

I think we rely heavily on PGO instrumentation to make the linker inline 
ffi functions, but there might be other ways of forcing the linker to 
inline particular calls that bindgen could generate or what not.

Cheers,

  -- Emilio
Andrew Lunn June 19, 2023, 1:20 p.m. UTC | #36
> > Report the error back up the chain and handle it properly, that's the
> > correct thing to do.
> 
> I see. Then netdev_warn() should be used instead.
> 
> Is it possible to handle the case where a device driver wrongly
> doesn't consume a skb object?

The skb is then likely leaked. After a while, you will consume all the
memory, the OOM handler will start killing processes, until eventually
the machine dies.

This is unlikely to happen on the main path, it would be a rather dumb
developer who messed up something so obvious. Leaks are more likely to
happen in the error paths, and that tends to happen less often, and so
the machine is going to live for longer.

But we are talking about different things here. Jakub wanted to ensure
that when the driver does drop an skb, it includes a reason code
indicating why it dropped it. If the driver tries to drop the frame
without providing a reason, we want to know the code path, so it can
be fixed. Even better if this can be done at compile time.

Not releasing the frame at all is a different problem, and probably
not easy to fix. There is some degree of handover of ownership of the
skb. When asked to transmit it, the driver should eventually release
the skb. However, that is often sometime in the future after the
hardware has confirmed it has DMAed a copy of the frame into its own
memory. On the receive side, in the normal path the driver could
allocate an skb, setup the DMA to copy the frame into it, and then
wait for an indication the DMA is complete. Then it passes it to the
network stack, at which point the network stack becomes the owner.

But there are no simple scope rules to detect an skb has been leaked.

    Andrew
David Laight June 20, 2023, 11:16 a.m. UTC | #37
....
> Not releasing the frame at all is a different problem, and probably
> not easy to fix. There is some degree of handover of ownership of the
> skb. When asked to transmit it, the driver should eventually release
> the skb. However, that is often sometime in the future after the
> hardware has confirmed it has DMAed a copy of the frame into its own
> memory. On the receive side, in the normal path the driver could
> allocate an skb, setup the DMA to copy the frame into it, and then
> wait for an indication the DMA is complete. Then it passes it to the
> network stack, at which point the network stack becomes the owner.
> 
> But there are no simple scope rules to detect an skb has been leaked.

You can require/enforce that the pointer the driver has is set to
NULL when the skb is successfully passed on.
But that tends to require passing the pointer by reference.
Ok for long-lived items but a likely performance hit for skb.

	David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK
Registration No: 1397386 (Wales)
Jakub Kicinski June 20, 2023, 3:47 p.m. UTC | #38
On Sat, 17 Jun 2023 12:08:26 +0200 Alice Ryhl wrote:
> > The drop reason indicates why the packet was dropped. It should give
> > some indication of what problem occurred which caused the drop. So
> > ideally we don't want an anonymous drop. The C code does not enforce
> > that, but it would be nice if the rust wrapper to dispose of an skb
> > did enforce it.  
> 
> It sounds like a destructor with WARN_ON is the best approach right now.
> 
> Unfortunately, I don't think we can enforce that the destructor is not 
> used today. That said, in the future it may be possible to implement a 
> linter that detects it - I know that there have already been experiments 
> with other custom lints for the kernel (e.g., enforcing that you don't 
> sleep while holding a spinlock).

Can we do something to hide the destructor from the linker?
Alice Ryhl June 20, 2023, 4:56 p.m. UTC | #39
On 6/20/23 17:47, Jakub Kicinski wrote:
> On Sat, 17 Jun 2023 12:08:26 +0200 Alice Ryhl wrote:
>>> The drop reason indicates why the packet was dropped. It should give
>>> some indication of what problem occurred which caused the drop. So
>>> ideally we don't want an anonymous drop. The C code does not enforce
>>> that, but it would be nice if the rust wrapper to dispose of an skb
>>> did enforce it.
>>
>> It sounds like a destructor with WARN_ON is the best approach right now.
>>
>> Unfortunately, I don't think we can enforce that the destructor is not
>> used today. That said, in the future it may be possible to implement a
>> linter that detects it - I know that there have already been experiments
>> with other custom lints for the kernel (e.g., enforcing that you don't
>> sleep while holding a spinlock).
> 
> Can we do something to hide the destructor from the linker?

We could probably have the destructor call some method that the linker 
wont be able to find, then mark the destructor with #[inline] so that 
dead-code elimination removes it if unused.

(At least, in godbolt the inline marker was necessary for the destructor 
to get removed when not used.)
Miguel Ojeda June 20, 2023, 5:44 p.m. UTC | #40
On Tue, Jun 20, 2023 at 6:55 PM Alice Ryhl <alice@ryhl.io> wrote:
>
> We could probably have the destructor call some method that the linker
> wont be able to find, then mark the destructor with #[inline] so that
> dead-code elimination removes it if unused.
>
> (At least, in godbolt the inline marker was necessary for the destructor
> to get removed when not used.)

Yeah, and we could use `build_assert!(false);` to ensure we don't ever
call it (by users, or by the the custom destructor methods) -- it
seems to work [1], but I am Cc'ing Gary in case he has some concerns
(IIRC we discussed this in the past; I may be forgetting an issue with
this).

Cheers,
Miguel

[1]

diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index c20b37e88ab2..7c313c75ff14 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -261,3 +261,28 @@ fn panic(info: &core::panic::PanicInfo<'_>) -> ! {
     // instead of `!`. See
<https://github.com/rust-lang/rust-bindgen/issues/2094>.
     loop {}
 }
+
+/// Custom destructor example.
+pub struct S;
+
+impl S {
+    /// Custom destructor 1.
+    pub fn close1(self) {
+        pr_info!("close1");
+        core::mem::forget(self); // If commented out, it is a build error.
+    }
+
+    /// Custom destructor 2.
+    pub fn close2(self) {
+        pr_info!("close2");
+        core::mem::forget(self); // If commented out, it is a build error.
+    }
+}
+
+impl Drop for S {
+    #[inline(always)]
+    fn drop(&mut self) {
+        // This should never be called.
+        build_assert!(false);
+    }
+}
diff --git a/samples/rust/rust_minimal.rs b/samples/rust/rust_minimal.rs
index 54de32b78cec..f07064ff6341 100644
--- a/samples/rust/rust_minimal.rs
+++ b/samples/rust/rust_minimal.rs
@@ -3,6 +3,7 @@
 //! Rust minimal sample.

 use kernel::prelude::*;
+use kernel::S;

 module! {
     type: RustMinimal,
@@ -26,6 +27,12 @@ impl kernel::Module for RustMinimal {
         numbers.try_push(108)?;
         numbers.try_push(200)?;

+        let _s = S;
+        _s.close1(); // If commented out, it is a build error.
+
+        let _s = S;
+        _s.close2(); // If commented out, it is a build error.
+
         Ok(RustMinimal { numbers })
     }
 }
Miguel Ojeda June 20, 2023, 5:55 p.m. UTC | #41
On Tue, Jun 20, 2023 at 7:44 PM Miguel Ojeda
<miguel.ojeda.sandonis@gmail.com> wrote:
>
> Yeah, and we could use `build_assert!(false);` to ensure we don't ever
> call it (by users, or by the the custom destructor methods) -- it

i.e. `build_assert!` is a similar trick we have to produce a linker
error like Alice suggested. It was "nicely packaged" by Gary a while
ago :)

Actually, it should be `build_error!("Normal destructor should never
be called");` -- same thing (I just forgot we had that one).

Cheers,
Miguel
Miguel Ojeda June 20, 2023, 6:09 p.m. UTC | #42
On Mon, Jun 19, 2023 at 1:27 PM Emilio Cobos Álvarez <emilio@crisal.io> wrote:
>
> Hi Andrew, Miguel,
>
> On 6/16/23 16:43, Andrew Lunn wrote:
> > I said in another email, i don't want to suggest premature
> > optimisation, before profiling is done. But in C, these functions are
> > inline for a reason. We don't want the cost of a subroutine call. We
> > want the compiler to be able to inline the code, and the optimiser to
> > be able to see it and generate the best code it can.
> >
> > Can the rust compile inline the binding including the FFI call?
>
> This is possible, with cross-language LTO, see:
>
>    https://blog.llvm.org/2019/09/closing-gap-cross-language-lto-between.html
>
> There are some requirements that need to happen for that to work
> (mainly, I believe, that the LLVM version used by rustc and clang agree).
>
> But in general it is possible. We use it extensively on Firefox. Of
> course the requirements of Firefox and the kernel might be different.
>
> I think we rely heavily on PGO instrumentation to make the linker inline
> ffi functions, but there might be other ways of forcing the linker to
> inline particular calls that bindgen could generate or what not.

Thanks Emilio! It is nice to hear cross-language LTO is working well
for Firefox.

Andreas took a look at cross-language LTO some weeks ago, if I
remember correctly (Cc'd).

I am not sure about the latest status on kernel PGO, though.

Cheers,
Miguel
Andreas Hindborg June 20, 2023, 7:12 p.m. UTC | #43
Hi All,

Miguel Ojeda <miguel.ojeda.sandonis@gmail.com> writes:

> On Mon, Jun 19, 2023 at 1:27 PM Emilio Cobos Álvarez <emilio@crisal.io> wrote:
>>
>> Hi Andrew, Miguel,
>>
>> On 6/16/23 16:43, Andrew Lunn wrote:
>> > I said in another email, i don't want to suggest premature
>> > optimisation, before profiling is done. But in C, these functions are
>> > inline for a reason. We don't want the cost of a subroutine call. We
>> > want the compiler to be able to inline the code, and the optimiser to
>> > be able to see it and generate the best code it can.
>> >
>> > Can the rust compile inline the binding including the FFI call?
>>
>> This is possible, with cross-language LTO, see:
>>
>>    https://blog.llvm.org/2019/09/closing-gap-cross-language-lto-between.html
>>
>> There are some requirements that need to happen for that to work
>> (mainly, I believe, that the LLVM version used by rustc and clang agree).
>>
>> But in general it is possible. We use it extensively on Firefox. Of
>> course the requirements of Firefox and the kernel might be different.
>>
>> I think we rely heavily on PGO instrumentation to make the linker inline
>> ffi functions, but there might be other ways of forcing the linker to
>> inline particular calls that bindgen could generate or what not.
>
> Thanks Emilio! It is nice to hear cross-language LTO is working well
> for Firefox.
>
> Andreas took a look at cross-language LTO some weeks ago, if I
> remember correctly (Cc'd).
>
> I am not sure about the latest status on kernel PGO, though.

I hacked it to work a while back for the NVMe and null_blk drivers. You
need to build the C and Rust parts of the kernel with compatible
clang/rustc compilers (same major version I believe), and then pass the
right compiler flags to get rustc and clang to emit llvm bitcode instead
of ELF files with machine code. As far as I recall, some infrastructure
is present in kbuild, but I had to add some bits to make it build.

Also, I had some issues with LLVM not doing the inline properly and I
had to use `llvm-link` on the bitcode of my module to link in the
bitcode of the C code I wanted to inline. Without that step, inlining
did not happen.

I was able to build and execute in qemu like this, but I was not able to
boot on bare metal. I think the LTO breaks something in C land. I did
not investigate that further.

Eventually I paused the work because I did not observe conclusive
speedups in my benchmarks. I plan to resume the work at some point and
do more rigorous benchmarking to see if I can observe a statistically
significant speedup. Not sure when that will happen though.

Best regards,
Andreas
Andreas Hindborg June 21, 2023, 12:30 p.m. UTC | #44
Miguel Ojeda <miguel.ojeda.sandonis@gmail.com> writes:

> On Fri, Jun 16, 2023 at 3:14 PM Andrew Lunn <andrew@lunn.ch> wrote:
>>
>> I think this is something you need to get addressed at a language
>> level very soon. Lots of netdev API calls will be to macros. The API
>> to manipulate skbs is pretty much always used on the hot path, so i
>> expect that it will have a large number of macros. It is unclear to me
>> how well it will scale if you need to warp them all?
>>
>> ~/linux/include/linux$ grep inline skbuff.h  | wc
>>     349    2487   23010
>>
>> Do you really want to write 300+ wrappers?
>
> It would be very nice if at least `bindgen` (or even the Rust
> compiler... :) could cover many of these one-liners. We have discussed
> and asked for this in the past, and messages like this reinforce the
> need/request for this clearly, so thanks for this.
>
> Since `bindgen` 0.64.0 earlier this year [1] there is an experimental
> feature for this (`--wrap-static-fns`), so that is nice -- though we
> need to see how well it works. We are upgrading `bindgen` to the
> latest version after the merge window, so we can play with this soon.
>
> In particular, given:
>
>     static inline int foo(int a, int b) {
>         return a + b;
>     }
>
> It generates a C file with e.g.:
>
>     #include "a.h"
>
>     // Static wrappers
>
>     int foo__extern(int a, int b) { return foo(a, b); }
>
> And then in the usual Rust bindings:
>
>     extern "C" {
>         #[link_name = "foo__extern"]
>         pub fn foo(a: ::std::os::raw::c_int, b: ::std::os::raw::c_int)
> -> ::std::os::raw::c_int;
>     }

This is nice! It would be awesome if we could have something similar for
macros. I am not sure if it is possible though. For the null_block
demonstrator I had to move some C macros to Rust, for instance to
implement iterators of bvec [1]. In the particular case of
`bvec_iter_bvec()` it is not possible to wrap the macro in a function
because the macro operates on a value, not a reference. We would have to
pass an argument by value to the wrapping function in order to invoke
the macro on this stack local variable, and then return the value again.
Not really efficient.

[1] https://lore.kernel.org/rust-for-linux/20230503090708.2524310-5-nmi@metaspace.dk/