Re: [TECH TOPIC] Rust for Linux

Miguel Ojeda
2021-07-20T01:21:58Z

On Tue, Jul 20, 2021 at 12:16 AM Linus Walleij <linus.walleij@linaro.org> wrote:
>
> I think it's not a good idea to keep these wrappers in their own
> rust directory, they need to be distributed out into the kernel
> everywhere they are used. We have made this mistake before
> with OF (device tree) that started exactly like this in
> drivers/of/*, and now I have part of the OF GPIO handling
> and tests inside files in that directory as a consequence.

The `rust/kernel/*` folder currently contains all the "Rust
abstractions" that are shared by all the kernel, i.e. in the rest of
the kernel tree we have kernel modules that are users of the
abstractions.

The main reasons behind this approach are that it is the simplest one
and that it makes a clear distinction between abstractions and user
code. In fact, code inside `rust/` has some capabilities that we do
not allow for non-`rust/` code. For instance, `rust/*` code can use
the C bindings and has unrestricted access to Rust unstable features
(though we still carefully pick the ones we use, of course). This is
not the case for "normal" Rust kernel modules elsewhere.

Having said that, this is not set in stone or a hard requirement -- we
can definitely split things and move them elsewhere, e.g. having a
`gpio` crate in `drivers/gpio/`, for instance. We could still follow
special rules for "subsystem objects".

In any case, please note that the compilation model is different in
Rust than in C. In Rust, the translation unit is not each `.rs` file,
but the "crate" (i.e. a set of `.rs` files that are found by the
compiler given a `.rs` entry point file), there are no header files,
etc. Thus some differences apply. For instance, currently having
everything in the same "crate" means the compiler can see everything
even without LTO.

> The wrappers are a hard read, I notice a whole lot of unsafe

The Rust abstractions are indeed harder to read, specially now that
they aren't documented -- Wedson did a great job to have all the
pieces ready quickly to show a working GPIO driver; but later on the
files will be documented, with `SAFETY`/`INVARIANT` comments too (that
we require), etc. as you can see in other files.

In addition, a lot of complexity comes due to using the C APIs. As we
discussed in the driver model discussion the other week, if we all
decide to give the Rust side more freedom to reimplement things,
including entire subsystems without being constrained by the C API,
then we can definitely reduce the complexity (plus reduce the number
of `unsafe` blocks etc.).

Finally, as we get to write more abstractions/subsystems/wrappers, we
will all learn the code patterns that tend to appear, how to factorize
them, how to clean things up, etc.

> keywords here, Chip is missing .set_config() which is understandable
> since PL061 isn't using it, the New call to create an instance
> contains a lot of magic stuff I don't understand like
> state: AtomicU8::new(0) and such but I think that's all right
> it probably only needs a trained eye.

Yeah, that `{ state: ..., ... }` syntax is just initializing a
`struct`, similar to the `{ .state = ..., ... }` designated
initializers in C.

Thus if you have some `struct MyFoo` and you are writing a function
that initializes it, then you can think of that `new()` as something
like:

struct MyFoo myfoo_new() {
return (struct MyFoo) { .a = 42, .b = 43 };
}

vs.

impl myfoo {
pub fn new() -> Self {
Self { a: 42, b: 43 }
}
}

Similarly, `AtomicU8::new(0)` is just a call to a normal function --
you can think of it as `atomicu8_new(0)`, i.e. it is just namespaced
inside a type, but it is a normal / free / plain function (i.e. not a
member function / method).

> (...)
>
> To that end the core of the GPIO library would probably
> have to be rewritten in Rust and used on *all* platforms
> in order to buy us any improved security. I understand that

I do not agree: a bug in a driver can cause havok of all kinds in a
system, security-wise or otherwise. One does not need to rewrite the
entire kernel in Rust to start to see benefits.

Moreover, even if Rust did not buy us any security, writing Rust code
would still have all the other advantages as listed in the RFC.

> this is not currently the plan.

Let me clarify that it is not against the plan either.

In other words, we are following the approach of wrapping C APIs
because we think it is important to show that Rust can actually
cooperate with the C side, plus that one can actually write safe
abstractions for existing C code without leaking a lot of `unsafe`
into drivers.

But things can definitely be written from scratch too -- and that
gives extra advantages, definitely (and not just in security).

Cheers,
Miguel