[PATCH 00/13] [RFC] Rust support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Miguel Ojeda <ojeda@xxxxxxxxxx>

Some of you have noticed the past few weeks and months that
a serious attempt to bring a second language to the kernel was
being forged. We are finally here, with an RFC that adds support
for Rust to the Linux kernel.

This cover letter is fairly long, since there are quite a few topics
to describe, but I hope it answers as many questions as possible
before the discussion starts.

If you are interested in following this effort, please join us
in the mailing list at:

    rust-for-linux@xxxxxxxxxxxxxxx

and take a look at the project itself at:

    https://github.com/Rust-for-Linux

Cheers,
Miguel


# A second language in the kernel

We know there are huge costs and risks in introducing a new main
language in the kernel. We risk dividing efforts and we increase
the knowledge required to contribute to some parts of the kernel.

Most importantly, any new language introduced means any module
written in that language will be way harder to replace later on
if the support for the new language gets dropped.

Nevertheless, we believe that, even today, the advantages of using
Rust outweighs the cost. We will explain why in the following
sections.

Please note that the Rust support is intended to enable writing
drivers and similar "leaf" modules in Rust, at least for the
foreseeable future. In particular, we do not intend to rewrite
the kernel core nor the major kernel subsystems (e.g. `kernel/`,
`mm/`, `sched/`...). Instead, the Rust support is built on top
of those.


## Goals

By using Rust in the Linux kernel, our hope is that:

  - New code written in Rust has a reduced risk of memory safety bugs,
    data races and logic bugs overall, thanks to the language
    properties mentioned below.

  - Maintainers are more confident in refactoring and accepting
    patches for modules thanks to the safe subset of Rust.

  - New drivers and modules become easier to write, thanks to
    abstractions that are easier to reason about, based on modern
    language features, as well as backed by detailed documentation.

  - More people get involved overall in developing the kernel
    thanks to the usage of a modern language.

  - By taking advantage of Rust tooling, we keep enforcing the
    documentation guidelines we have established so far in the
    project. For instance, we require having all public APIs, safety
    preconditions, `unsafe` blocks and type invariants documented.


## Why Rust?

Rust is a systems programming language that brings several key
advantages over C in the context of the Linux kernel:

  - No undefined behavior in the safe subset (when unsafe code is
    sound), including memory safety and the absence of data races.

  - Stricter type system for further reduction of logic errors.

  - A clear distinction between safe and `unsafe` code.

  - Featureful language: sum types, pattern matching, generics,
    RAII, lifetimes, shared & exclusive references, modules &
    visibility, powerful hygienic and procedural macros...

  - Extensive freestanding standard library: vocabulary types such
    as `Result` and `Option`, iterators, formatting, pinning,
    checked/saturating/wrapping integer arithmetic, etc.

  - Integrated out of the box tooling: documentation generator,
    formatter and linter all based on the compiler itself.

Overall, Rust is a language that has successfully leveraged decades
of experience from system programming languages as well as functional
ones, and added lifetimes and borrow checking on top.


## Why not?

Rust also has disadvantages compared to C in the context of
the Linux kernel:

  - The many years of effort in tooling for C around the kernel,
    including compiler plugins, sanitizers, Coccinelle, lockdep,
    sparse... However, this will likely improve if Rust usage in
    the kernel grows over time.

  - Single implementation based on LLVM. There are third-party
    efforts underway to fix this, such as a GCC frontend,
    a `rustc` backend based on Cranelift and `mrustc`,
    a compiler intended to reduce the bootstrapping chain.
    Any help for those projects would be very welcome!

  - Not standardized. While it is not clear whether standardization
    would be beneficial for the kernel, several points minimize
    this issue in any case: the Rust stability promise, the extensive
    documentation, the WIP reference, the detailed RFCs...

  - Slower compilation in general, due to more complex language
    features and limitations in the current compiler.

  - At the present time, we require certain nightly features.
    That is, features that are not available in the stable compiler.
    Nevertheless, we aim to remove this restriction within a year
    by either `rustc` landing the features in stable or removing
    our usage of them otherwise. We maintain a report here:

        https://github.com/Rust-for-Linux/linux/issues/2

  - Bigger than needed text currently, due to the unused parts from
    the `core` and `alloc` Rust standard libraries. We plan to address
    this over time.

Most of these disadvantages arise from the fact that Rust is a much
younger and less used language. However, we believe Rust is likely
to become an important part of systems programming, just as C has been
during the last decades, and so most of these issues will be reduced
as different industries put resources behind Rust.


## Design

There are a few key design choices to have in mind.

First of all, Rust kernel modules require some shared code that is
enabled via a configuration option (`CONFIG_RUST`). This makes
individual modules way smaller. This support consists of:

  - The Rust standard library. Currently `core` and `alloc`, but
    likely only a subset of `core` in the future. These pieces
    are basically the equivalent of the freestanding subset of
    the C standard library.

  - The abstractions wrapping the kernel APIs. These live inside
    `rust/kernel/`. The intention is to make these as safe as
    possible so that modules written in Rust require the smallest
    amount of `unsafe` code possible.

  - Other bits such as the `module!` procedural macro, the compiler
    builtins, the generated bindings and helpers, etc.

This support takes a fair amount of space, although it will be
reduced since there are some features from the Rust standard library
that we do not use.

Here are some examples from a small x86_64 config we use in the CI:

       text    data     bss      dec

    7464833 1492128 2301996 11258957 vmlinux (without Rust support)
    7682527 1709252 2301996 11693775 vmlinux (with    Rust support)
    7682527 1721540 2301996 11706063 vmlinux (plus overflow checks)

       2224       0      16     2240 samples/rust/rust_semaphore_c.o
       3694       0      10     3704 samples/rust/rust_semaphore.o
       2367     768      16     3151 samples/rust/rust_semaphore_c.ko
       3829     768      10     4607 samples/rust/rust_semaphore.ko

      80554    5904   20249   106707 drivers/android/binder.o
      12365    1240       9    13614 drivers/android/binder_alloc.o
      92818       8      16    92842 drivers/android/rust_binder.o

That is a 3% increase in text and a 4% increase in the total for
`vmlinux` with overflow checking enabled. The modules themselves are
relatively close to their C alternatives.

In the table above we can also see the comparison between Binder and
its Rust port prototype. Please note that while the Rust version is
not equivalent to the C original module yet, it is close enough to
provide a rough estimation. Here, we see the sum of the texts of the
C Binder are less than that of the Rust driver, while the total column
is bigger.

Secondly, modules written in Rust should never use the C kernel APIs
directly. The whole point of using Rust in the kernel is that
we develop safe abstractions so that modules are easier to reason
about and, therefore, to review, refactor, etc.

Furthermore, the bindings to the C side of the kernel are generated
on-the-fly via `bindgen` (an official Rust tool). Using it allows us
to avoid the need to update the bindings on the Rust side.

Macros still need to be handled manually, and some functions are
inlined, which requires us to create helpers to call them from Rust.

Thirdly, in Rust code bases, most documentation is written alongside
the source code, in Markdown. We follow this convention, thus while
we have a few general documents in `Documentation/rust/`, most of
the actual documentation is in the source code itself.

In order to read this documentation easily, Rust provides a tool
to generate HTML documentation, just like Sphinx/kernel-doc, but
suited to Rust code bases and the language concepts.

Moreover, as explained above, we are taking the chance to enforce
some documentation guidelines. We are also enforcing automatic code
formatting, a set of Clippy lints, etc. We decided to go with Rust's
idiomatic style, i.e. keeping `rustfmt` defaults. For instance, this
means 4 spaces are used for indentation, rather than a tab. We are
happy to change that if needed -- we think what is important is
keeping the formatting automated.

Finally, to avoid exposing GPL symbols as non-GPL (even indirectly),
we export all our Rust support symbols in the kernel as GPL.


## Status

The Rust support presented here is experimental and many kernel APIs
and abstractions are, of course, missing. Covering the entire API
surface of the kernel will take a long time to develop and mature.
Other implementation details are also a work in progress.

However, the support is good enough that prototyping modules can
start today. This RFC includes a working port of an existing module:
Binder, the Android IPC mechanism. While it is not meant to be used
in production just yet, it showcases what can already be done and how
actual Rust modules could look like in the future.

Regarding compilers, we support Clang-built kernels as well as
`LLVM=1` builds where possible (i.e. as long as supported by
the ClangBuiltLinux project). We also maintain some configurations
of GCC-built kernels working, but they are not intended to be used
at the present time. Having a `bindgen` backend for GCC would be
ideal to improve support for those builds.

Concerning architectures, we already support `x86_64`, `arm64` and
`ppc64le`. Adding support for variants of those as well as `riscv`,
`s390` and `mips` should be possible with some work.

We also joined `linux-next` (with a special waiver). Currently,
the support is gated behind `!COMPILE_TEST` since we did not want
to break any production CIs by mistake, but if feedback for this RFC
is positive, then we will remove that restriction.


## Upstreaming plan

As usual, getting into mainline early is the best way forward to
sort out any missing details, so we are happy to send these changes
as soon as the upcoming merge window.

However, at which point we submit them will depend on the feedback
we receive on this RFC and what the overall sentiment from
high-level maintainers is.


## Reviewing this RFC

We would like to get comments from the perspective of module writers.
In particular on the samples in patch 9 and on Binder in patch 13.
That is, as a module writer, how do you feel about the Rust code
shown there? Do you see yourself writing similar Rust code in
the future, taking into account the safety/no-UB benefits?

Comments on the Rust abstractions themselves and other details of
this RFC are, of course, welcome, but please note that they are
a work in progress.

Another important topic we would like feedback on is the Rust
"native" documentation that is written alongside the code, as
explained above. We have uploaded it here:

    https://rust-for-linux.github.io/docs/kernel/

We like how this kind of generated documentation looks. Please take
a look and let us know what you think!


## Testing this RFC

If you want to test things out, please follow the Quick Start guide
in `Documentation/rust/quick-start.rst`. It will help you setup Rust
and the rest of the tools needed to build and test this RFC.

At the time of writing, the RFC series matches our main repository,
but if you want to follow along, check out the `rust` branch from
our main tree:

    https://github.com/Rust-for-Linux/linux.git


## Acknowledgements

The signatures in the main commits correspond to the people that
wrote code that has ended up in them at the present time. However,
we would like to give credit to everyone that has contributed in
one way or another to the Rust for Linux project:

  - Alex Gaynor and Geoffrey Thomas wrote the first safe Rust
    abstractions for kernel features (such as `chrdev`, `printk`,
    `random`, `sysctl`...) and used them in a framework to build
    out-of-tree modules in Rust leveraging `bindgen` for bindings.
    They presented their work at the Linux Security Summit 2019.

  - Nick Desaulniers bravely raised the Rust topic in the LKML and
    organized a talk at the Linux Plumbers Conference 2020. He also
    pulled some strings to move things forward!

  - Miguel Ojeda created the Rust for Linux project to group
    the different efforts/people in one place and kickstarted it by
    adding Kbuild support for Rust into the kernel, integrating Alex's
    and Geoffrey's abstractions into what is now `rust/kernel/`
    and adding support for built-in modules and sharing the common
    Rust code. He kept working on writing the infrastructure
    foundations: the `module!` proc macro and new printing macros,
    the exports and compiler builtins magic, the kernel config symbols
    for conditional compilation, the different Rust tooling
    integrations, the documentation, the CI... He fixed a couple bits
    in `rustc` and `rustdoc` that were needed for the kernel.
    He is coordinating the project.

  - Alex Gaynor has spent a lot of time reviewing the majority of
    PRs after the integration took place, cleaned up a few of the
    abstractions further, added support for `THIS_MODULE`...
    He is a maintainer of the project.

  - Wedson Almeida Filho wrote most of the rest of the abstractions,
    including all the synchronization ones in `rust/kernel/sync/`,
    a better abstraction for file operations, support for ioctls,
    miscellaneous devices, failing allocations, `container_of!` and
    `offset_of!`... These are all needed for his Binder (Android IPC)
    Rust module, which is the first Rust kernel module intended for
    (eventual) production. He is a maintainer of the project.

  - Adam Bratschi-Kaye added support for `charp`, array, string and
    integer module parameter types, the `fsync` file operation,
    the stack probing test... He has also attended most meetings and
    reviewed some PRs.

  - Finn Behrens worked on `O=` builds and NixOS support, the Rust
    confdata printer, testing the Kbuild support as well as sending
    proposals for a couple new abstractions. He has attended a few
    meetings and reviewed some PRs even while busy with his studies.

  - Manish Goregaokar implemented the fallible `Box`, `Arc`, and `Rc`
    allocator APIs in Rust's `alloc` standard library for us.

  - Boqun Feng is working hard on the different options for
    threading abstractions and has reviewed most of the `sync` PRs.

  - Michael Ellerman added initial support for ppc64le and actively
    reviews further changes and issues related to it.

  - Dan Robertson is working on adding softdeps to the `module!`
    macro.

  - Sumera Priyadarsini worked on improving the error messages for
    the `module!` macro.

  - Ngo Iok Ui (Wu Yu Wei) worked on generating `core` and `alloc`
    docs locally too, although in the end we could not merge it.

  - Geoffrey Thomas kept giving us a lot of valuable input from his
    experience implementing some of the abstractions and never
    missed a meeting.

  - bjorn3 for his knowledgeable input on `rustc` internals and
    reviewing related code.

  - Josh Triplett helped us move forward the project early on in
    the Plumbers conference and acts as liaison to the core Rust team.

  - John Ericson worked on advancing `cargo`'s `-Zbuild-std` support,
    the Rust compiler targets and joined a few of the meetings.

  - Joshua Abraham reviewed a few PRs and joined some of
    the meetings.

  - Konstantin Ryabitsev for his patience with all the requests
    regarding Rust for Linux within the kernel.org infrastructure.

  - Stephen Rothwell for his flexibility and help on including
    the project into linux-next.

  - John 'Warthog9' Hawley and David S. Miller for setting up the
    rust-for-linux@xxxxxxxxxxxxxxx mailing list.

  - Jonathan Corbet for his feedback on the Rust documentation,
    Markdown and the different choices we will need to discuss.

  - Guillaume Gomez and Joshua Nelson for early feedback on
    a proposal on an external references map file for `rustdoc`
    that would allow us to easily link to Sphinx/C entries.

  - Many folks that have reported issues, tested the project,
    helped spread the word, joined discussions and contributed in
    other ways! In no particular order: Pavel Machek, Geert Stappers,
    Kees Cook, Milan, Daniel Kolsoi, Arnd Bergmann, ahomescu,
    Josh Stone, Manas, Christian Brauner, Boris-Chengbiao Zhou,
    Luis Gerhorst...

Miguel Ojeda (12):
  kallsyms: Support "big" kernel symbols (2-byte lengths)
  kallsyms: Increase maximum kernel symbol length to 512
  Makefile: Generate CLANG_FLAGS even in GCC builds
  Kbuild: Rust support
  Rust: Compiler builtins crate
  Rust: Module crate
  Rust: Kernel crate
  Rust: Export generated symbols
  Samples: Rust examples
  Documentation: Rust general information
  MAINTAINERS: Rust
  Rust: add abstractions for Binder (WIP)

Wedson Almeida Filho (1):
  Android: Binder IPC in Rust (WIP)

 .gitignore                             |   2 +
 .rustfmt.toml                          |  12 +
 Documentation/doc-guide/kernel-doc.rst |   3 +
 Documentation/index.rst                |   1 +
 Documentation/kbuild/kbuild.rst        |   4 +
 Documentation/process/changes.rst      |   9 +
 Documentation/rust/arch-support.rst    |  29 +
 Documentation/rust/coding.rst          |  92 +++
 Documentation/rust/docs.rst            | 109 +++
 Documentation/rust/index.rst           |  20 +
 Documentation/rust/quick-start.rst     | 203 ++++++
 MAINTAINERS                            |  14 +
 Makefile                               | 147 +++-
 arch/arm64/rust/target.json            |  40 ++
 arch/powerpc/rust/target.json          |  30 +
 arch/x86/rust/target.json              |  42 ++
 drivers/android/Kconfig                |   7 +
 drivers/android/Makefile               |   2 +
 drivers/android/allocation.rs          | 252 +++++++
 drivers/android/context.rs             |  80 +++
 drivers/android/defs.rs                |  92 +++
 drivers/android/node.rs                | 479 +++++++++++++
 drivers/android/process.rs             | 950 +++++++++++++++++++++++++
 drivers/android/range_alloc.rs         | 191 +++++
 drivers/android/rust_binder.rs         | 128 ++++
 drivers/android/thread.rs              | 821 +++++++++++++++++++++
 drivers/android/transaction.rs         | 206 ++++++
 include/linux/kallsyms.h               |   2 +-
 include/linux/spinlock.h               |  17 +-
 include/uapi/linux/android/binder.h    |  22 +-
 init/Kconfig                           |  27 +
 kernel/kallsyms.c                      |   7 +
 kernel/livepatch/core.c                |   4 +-
 kernel/printk/printk.c                 |   2 +
 lib/Kconfig.debug                      | 100 +++
 rust/.gitignore                        |   5 +
 rust/Makefile                          | 152 ++++
 rust/compiler_builtins.rs              | 146 ++++
 rust/exports.c                         |  16 +
 rust/helpers.c                         |  86 +++
 rust/kernel/allocator.rs               |  68 ++
 rust/kernel/bindings.rs                |  22 +
 rust/kernel/bindings_helper.h          |  18 +
 rust/kernel/buffer.rs                  |  39 +
 rust/kernel/c_types.rs                 | 133 ++++
 rust/kernel/chrdev.rs                  | 162 +++++
 rust/kernel/error.rs                   | 106 +++
 rust/kernel/file_operations.rs         | 668 +++++++++++++++++
 rust/kernel/lib.rs                     | 200 ++++++
 rust/kernel/linked_list.rs             | 245 +++++++
 rust/kernel/miscdev.rs                 | 109 +++
 rust/kernel/module_param.rs            | 497 +++++++++++++
 rust/kernel/pages.rs                   | 173 +++++
 rust/kernel/prelude.rs                 |  22 +
 rust/kernel/print.rs                   | 461 ++++++++++++
 rust/kernel/random.rs                  |  50 ++
 rust/kernel/raw_list.rs                | 361 ++++++++++
 rust/kernel/static_assert.rs           |  38 +
 rust/kernel/sync/arc.rs                | 184 +++++
 rust/kernel/sync/condvar.rs            | 138 ++++
 rust/kernel/sync/guard.rs              |  82 +++
 rust/kernel/sync/locked_by.rs          | 112 +++
 rust/kernel/sync/mod.rs                |  68 ++
 rust/kernel/sync/mutex.rs              | 101 +++
 rust/kernel/sync/spinlock.rs           | 108 +++
 rust/kernel/sysctl.rs                  | 185 +++++
 rust/kernel/types.rs                   |  73 ++
 rust/kernel/user_ptr.rs                | 282 ++++++++
 rust/module.rs                         | 685 ++++++++++++++++++
 samples/Kconfig                        |   2 +
 samples/Makefile                       |   1 +
 samples/rust/Kconfig                   | 103 +++
 samples/rust/Makefile                  |  11 +
 samples/rust/rust_chrdev.rs            |  66 ++
 samples/rust/rust_minimal.rs           |  40 ++
 samples/rust/rust_miscdev.rs           | 145 ++++
 samples/rust/rust_module_parameters.rs |  72 ++
 samples/rust/rust_print.rs             |  58 ++
 samples/rust/rust_semaphore.rs         | 178 +++++
 samples/rust/rust_semaphore_c.c        | 212 ++++++
 samples/rust/rust_stack_probing.rs     |  42 ++
 samples/rust/rust_sync.rs              |  84 +++
 scripts/Makefile.build                 |  19 +
 scripts/Makefile.lib                   |  12 +
 scripts/kallsyms.c                     |  33 +-
 scripts/kconfig/confdata.c             |  67 +-
 scripts/rust-version.sh                |  31 +
 tools/include/linux/kallsyms.h         |   2 +-
 tools/include/linux/lockdep.h          |   2 +-
 tools/lib/perf/include/perf/event.h    |   2 +-
 tools/lib/symbol/kallsyms.h            |   2 +-
 91 files changed, 11080 insertions(+), 45 deletions(-)
 create mode 100644 .rustfmt.toml
 create mode 100644 Documentation/rust/arch-support.rst
 create mode 100644 Documentation/rust/coding.rst
 create mode 100644 Documentation/rust/docs.rst
 create mode 100644 Documentation/rust/index.rst
 create mode 100644 Documentation/rust/quick-start.rst
 create mode 100644 arch/arm64/rust/target.json
 create mode 100644 arch/powerpc/rust/target.json
 create mode 100644 arch/x86/rust/target.json
 create mode 100644 drivers/android/allocation.rs
 create mode 100644 drivers/android/context.rs
 create mode 100644 drivers/android/defs.rs
 create mode 100644 drivers/android/node.rs
 create mode 100644 drivers/android/process.rs
 create mode 100644 drivers/android/range_alloc.rs
 create mode 100644 drivers/android/rust_binder.rs
 create mode 100644 drivers/android/thread.rs
 create mode 100644 drivers/android/transaction.rs
 create mode 100644 rust/.gitignore
 create mode 100644 rust/Makefile
 create mode 100644 rust/compiler_builtins.rs
 create mode 100644 rust/exports.c
 create mode 100644 rust/helpers.c
 create mode 100644 rust/kernel/allocator.rs
 create mode 100644 rust/kernel/bindings.rs
 create mode 100644 rust/kernel/bindings_helper.h
 create mode 100644 rust/kernel/buffer.rs
 create mode 100644 rust/kernel/c_types.rs
 create mode 100644 rust/kernel/chrdev.rs
 create mode 100644 rust/kernel/error.rs
 create mode 100644 rust/kernel/file_operations.rs
 create mode 100644 rust/kernel/lib.rs
 create mode 100644 rust/kernel/linked_list.rs
 create mode 100644 rust/kernel/miscdev.rs
 create mode 100644 rust/kernel/module_param.rs
 create mode 100644 rust/kernel/pages.rs
 create mode 100644 rust/kernel/prelude.rs
 create mode 100644 rust/kernel/print.rs
 create mode 100644 rust/kernel/random.rs
 create mode 100644 rust/kernel/raw_list.rs
 create mode 100644 rust/kernel/static_assert.rs
 create mode 100644 rust/kernel/sync/arc.rs
 create mode 100644 rust/kernel/sync/condvar.rs
 create mode 100644 rust/kernel/sync/guard.rs
 create mode 100644 rust/kernel/sync/locked_by.rs
 create mode 100644 rust/kernel/sync/mod.rs
 create mode 100644 rust/kernel/sync/mutex.rs
 create mode 100644 rust/kernel/sync/spinlock.rs
 create mode 100644 rust/kernel/sysctl.rs
 create mode 100644 rust/kernel/types.rs
 create mode 100644 rust/kernel/user_ptr.rs
 create mode 100644 rust/module.rs
 create mode 100644 samples/rust/Kconfig
 create mode 100644 samples/rust/Makefile
 create mode 100644 samples/rust/rust_chrdev.rs
 create mode 100644 samples/rust/rust_minimal.rs
 create mode 100644 samples/rust/rust_miscdev.rs
 create mode 100644 samples/rust/rust_module_parameters.rs
 create mode 100644 samples/rust/rust_print.rs
 create mode 100644 samples/rust/rust_semaphore.rs
 create mode 100644 samples/rust/rust_semaphore_c.c
 create mode 100644 samples/rust/rust_stack_probing.rs
 create mode 100644 samples/rust/rust_sync.rs
 create mode 100755 scripts/rust-version.sh

-- 
2.17.1




[Index of Archives]     [Kernel Newbies]     [Security]     [Netfilter]     [Bugtraq]     [Linux FS]     [Yosemite Forum]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Video 4 Linux]     [Device Mapper]     [Linux Resources]

  Powered by Linux