From: Miguel Ojeda <[email protected]>
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:
[email protected]
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
[email protected] 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
From: Miguel Ojeda <[email protected]>
This commit includes also the `Kconfig` entries related to Rust,
the Rust configuration printer, the target definition files,
the version detection script and a few other bits.
In the future, we will likely want to generate the target files
on the fly via a script.
With this in place, we will be adding the rest of the bits piece
by piece and enabling their builds.
Co-developed-by: Alex Gaynor <[email protected]>
Signed-off-by: Alex Gaynor <[email protected]>
Co-developed-by: Geoffrey Thomas <[email protected]>
Signed-off-by: Geoffrey Thomas <[email protected]>
Co-developed-by: Finn Behrens <[email protected]>
Signed-off-by: Finn Behrens <[email protected]>
Co-developed-by: Adam Bratschi-Kaye <[email protected]>
Signed-off-by: Adam Bratschi-Kaye <[email protected]>
Co-developed-by: Wedson Almeida Filho <[email protected]>
Signed-off-by: Wedson Almeida Filho <[email protected]>
Co-developed-by: Michael Ellerman <[email protected]>
Signed-off-by: Michael Ellerman <[email protected]>
Signed-off-by: Miguel Ojeda <[email protected]>
---
.gitignore | 2 +
.rustfmt.toml | 12 +++
Documentation/kbuild/kbuild.rst | 4 +
Documentation/process/changes.rst | 9 ++
Makefile | 120 +++++++++++++++++++++++--
arch/arm64/rust/target.json | 40 +++++++++
arch/powerpc/rust/target.json | 30 +++++++
arch/x86/rust/target.json | 42 +++++++++
init/Kconfig | 27 ++++++
lib/Kconfig.debug | 100 +++++++++++++++++++++
rust/.gitignore | 5 ++
rust/Makefile | 141 ++++++++++++++++++++++++++++++
scripts/Makefile.build | 19 ++++
scripts/Makefile.lib | 12 +++
scripts/kconfig/confdata.c | 67 +++++++++++++-
scripts/rust-version.sh | 31 +++++++
16 files changed, 654 insertions(+), 7 deletions(-)
create mode 100644 .rustfmt.toml
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 rust/.gitignore
create mode 100644 rust/Makefile
create mode 100755 scripts/rust-version.sh
diff --git a/.gitignore b/.gitignore
index 3af66272d6f1..6ba4f516f46c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -37,6 +37,7 @@
*.o
*.o.*
*.patch
+*.rmeta
*.s
*.so
*.so.dbg
@@ -96,6 +97,7 @@ modules.order
!.gitattributes
!.gitignore
!.mailmap
+!.rustfmt.toml
#
# Generated include files
diff --git a/.rustfmt.toml b/.rustfmt.toml
new file mode 100644
index 000000000000..5893c0e3cbde
--- /dev/null
+++ b/.rustfmt.toml
@@ -0,0 +1,12 @@
+edition = "2018"
+format_code_in_doc_comments = true
+newline_style = "Unix"
+
+# Unstable options that help catching some mistakes in formatting and that we may want to enable
+# when they become stable.
+#
+# They are kept here since they are useful to run from time to time.
+#reorder_impl_items = true
+#comment_width = 100
+#wrap_comments = true
+#normalize_comments = true
diff --git a/Documentation/kbuild/kbuild.rst b/Documentation/kbuild/kbuild.rst
index 2d1fc03d346e..1109d18d9377 100644
--- a/Documentation/kbuild/kbuild.rst
+++ b/Documentation/kbuild/kbuild.rst
@@ -57,6 +57,10 @@ CFLAGS_MODULE
-------------
Additional module specific options to use for $(CC).
+KRUSTCFLAGS
+-----------
+Additional options to the Rust compiler (for built-in and modules).
+
LDFLAGS_MODULE
--------------
Additional options used for $(LD) when linking modules.
diff --git a/Documentation/process/changes.rst b/Documentation/process/changes.rst
index dac17711dc11..4b6ba5458706 100644
--- a/Documentation/process/changes.rst
+++ b/Documentation/process/changes.rst
@@ -31,6 +31,8 @@ you probably needn't concern yourself with pcmciautils.
====================== =============== ========================================
GNU C 4.9 gcc --version
Clang/LLVM (optional) 10.0.1 clang --version
+rustc (optional) nightly rustc --version
+bindgen (optional) 0.56.0 bindgen --version
GNU make 3.81 make --version
binutils 2.23 ld -v
flex 2.5.35 flex --version
@@ -56,6 +58,7 @@ iptables 1.4.2 iptables -V
openssl & libcrypto 1.0.0 openssl version
bc 1.06.95 bc --version
Sphinx\ [#f1]_ 1.3 sphinx-build --version
+rustdoc (optional) nightly rustdoc --version
====================== =============== ========================================
.. [#f1] Sphinx is needed only to build the Kernel documentation
@@ -330,6 +333,12 @@ Sphinx
Please see :ref:`sphinx_install` in :ref:`Documentation/doc-guide/sphinx.rst <sphinxdoc>`
for details about Sphinx requirements.
+rustdoc
+-------
+
+``rustdoc`` is used to generate Rust documentation. Please see
+:ref:`Documentation/rust/docs.rst <rust_docs>` for more information.
+
Getting updated software
========================
diff --git a/Makefile b/Makefile
index 9c75354324ed..62b3bba38635 100644
--- a/Makefile
+++ b/Makefile
@@ -120,6 +120,13 @@ endif
export KBUILD_CHECKSRC
+# Enable "clippy" (a linter) as part of the Rust compilation.
+#
+# Use 'make CLIPPY=1' to enable it.
+ifeq ("$(origin CLIPPY)", "command line")
+ KBUILD_CLIPPY := $(CLIPPY)
+endif
+
# Use make M=dir or set the environment variable KBUILD_EXTMOD to specify the
# directory of external module to build. Setting M= takes precedence.
ifeq ("$(origin M)", "command line")
@@ -263,7 +270,7 @@ no-dot-config-targets := $(clean-targets) \
cscope gtags TAGS tags help% %docs check% coccicheck \
$(version_h) headers headers_% archheaders archscripts \
%asm-generic kernelversion %src-pkg dt_binding_check \
- outputmakefile
+ outputmakefile rustfmt rustfmtcheck
no-sync-config-targets := $(no-dot-config-targets) %install kernelrelease \
image_name
single-targets := %.a %.i %.ko %.lds %.ll %.lst %.mod %.o %.s %.symtypes %/
@@ -444,6 +451,10 @@ OBJDUMP = $(CROSS_COMPILE)objdump
READELF = $(CROSS_COMPILE)readelf
STRIP = $(CROSS_COMPILE)strip
endif
+RUSTC = rustc
+RUSTFMT = rustfmt
+CLIPPY_DRIVER = clippy-driver
+BINDGEN = bindgen
PAHOLE = pahole
RESOLVE_BTFIDS = $(objtree)/tools/bpf/resolve_btfids/resolve_btfids
LEX = flex
@@ -467,9 +478,11 @@ CHECKFLAGS := -D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ \
-Wbitwise -Wno-return-void -Wno-unknown-attribute $(CF)
NOSTDINC_FLAGS :=
CFLAGS_MODULE =
+RUSTCFLAGS_MODULE =
AFLAGS_MODULE =
LDFLAGS_MODULE =
CFLAGS_KERNEL =
+RUSTCFLAGS_KERNEL =
AFLAGS_KERNEL =
LDFLAGS_vmlinux =
@@ -498,15 +511,30 @@ KBUILD_CFLAGS := -Wall -Wundef -Werror=strict-prototypes -Wno-trigraphs \
-Werror=return-type -Wno-format-security \
-std=gnu89
KBUILD_CPPFLAGS := -D__KERNEL__
+KBUILD_RUSTCFLAGS := --emit=dep-info,obj,metadata --edition=2018 \
+ -Cpanic=abort -Cembed-bitcode=n -Clto=n -Crpath=n \
+ -Cforce-unwind-tables=n -Ccodegen-units=1 \
+ -Zbinary_dep_depinfo=y -Zsymbol-mangling-version=v0
KBUILD_AFLAGS_KERNEL :=
KBUILD_CFLAGS_KERNEL :=
+KBUILD_RUSTCFLAGS_KERNEL :=
KBUILD_AFLAGS_MODULE := -DMODULE
KBUILD_CFLAGS_MODULE := -DMODULE
+KBUILD_RUSTCFLAGS_MODULE := --cfg MODULE
KBUILD_LDFLAGS_MODULE :=
KBUILD_LDFLAGS :=
CLANG_FLAGS :=
-export ARCH SRCARCH CONFIG_SHELL BASH HOSTCC KBUILD_HOSTCFLAGS CROSS_COMPILE LD CC
+ifeq ($(KBUILD_CLIPPY),1)
+ RUSTC_OR_CLIPPY_QUIET := CLIPPY
+ RUSTC_OR_CLIPPY = $(CLIPPY_DRIVER)
+else
+ RUSTC_OR_CLIPPY_QUIET := RUSTC
+ RUSTC_OR_CLIPPY = $(RUSTC)
+endif
+export RUSTC_OR_CLIPPY_QUIET RUSTC_OR_CLIPPY
+
+export ARCH SRCARCH CONFIG_SHELL BASH HOSTCC KBUILD_HOSTCFLAGS CROSS_COMPILE LD CC RUSTC BINDGEN
export CPP AR NM STRIP OBJCOPY OBJDUMP READELF PAHOLE RESOLVE_BTFIDS LEX YACC AWK INSTALLKERNEL
export PERL PYTHON3 CHECK CHECKFLAGS MAKE UTS_MACHINE HOSTCXX
export KGZIP KBZIP2 KLZOP LZMA LZ4 XZ ZSTD
@@ -514,9 +542,10 @@ export KBUILD_HOSTCXXFLAGS KBUILD_HOSTLDFLAGS KBUILD_HOSTLDLIBS LDFLAGS_MODULE
export KBUILD_CPPFLAGS NOSTDINC_FLAGS LINUXINCLUDE OBJCOPYFLAGS KBUILD_LDFLAGS
export KBUILD_CFLAGS CFLAGS_KERNEL CFLAGS_MODULE
+export KBUILD_RUSTCFLAGS RUSTCFLAGS_KERNEL RUSTCFLAGS_MODULE
export KBUILD_AFLAGS AFLAGS_KERNEL AFLAGS_MODULE
-export KBUILD_AFLAGS_MODULE KBUILD_CFLAGS_MODULE KBUILD_LDFLAGS_MODULE
-export KBUILD_AFLAGS_KERNEL KBUILD_CFLAGS_KERNEL
+export KBUILD_AFLAGS_MODULE KBUILD_CFLAGS_MODULE KBUILD_RUSTCFLAGS_MODULE KBUILD_LDFLAGS_MODULE
+export KBUILD_AFLAGS_KERNEL KBUILD_CFLAGS_KERNEL KBUILD_RUSTCFLAGS_KERNEL
# Files to ignore in find ... statements
@@ -712,7 +741,7 @@ $(KCONFIG_CONFIG):
quiet_cmd_syncconfig = SYNC $@
cmd_syncconfig = $(MAKE) -f $(srctree)/Makefile syncconfig
-%/config/auto.conf %/config/auto.conf.cmd %/generated/autoconf.h: $(KCONFIG_CONFIG)
+%/config/auto.conf %/config/auto.conf.cmd %/generated/autoconf.h %/generated/rustc_cfg: $(KCONFIG_CONFIG)
+$(call cmd,syncconfig)
else # !may-sync-config
# External modules and some install targets need include/generated/autoconf.h
@@ -738,12 +767,43 @@ KBUILD_CFLAGS += $(call cc-disable-warning, format-truncation)
KBUILD_CFLAGS += $(call cc-disable-warning, format-overflow)
KBUILD_CFLAGS += $(call cc-disable-warning, address-of-packed-member)
+ifdef CONFIG_RUST_DEBUG_ASSERTIONS
+KBUILD_RUSTCFLAGS += -Cdebug-assertions=y
+else
+KBUILD_RUSTCFLAGS += -Cdebug-assertions=n
+endif
+
+ifdef CONFIG_RUST_OVERFLOW_CHECKS
+KBUILD_RUSTCFLAGS += -Coverflow-checks=y
+else
+KBUILD_RUSTCFLAGS += -Coverflow-checks=n
+endif
+
ifdef CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE
KBUILD_CFLAGS += -O2
+KBUILD_RUSTCFLAGS_OPT_LEVEL_MAP := 2
else ifdef CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE_O3
KBUILD_CFLAGS += -O3
+KBUILD_RUSTCFLAGS_OPT_LEVEL_MAP := 3
else ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE
KBUILD_CFLAGS += -Os
+KBUILD_RUSTCFLAGS_OPT_LEVEL_MAP := z
+endif
+
+ifdef CONFIG_RUST_OPT_LEVEL_SIMILAR_AS_CHOSEN_FOR_C
+KBUILD_RUSTCFLAGS += -Copt-level=$(KBUILD_RUSTCFLAGS_OPT_LEVEL_MAP)
+else ifdef CONFIG_RUST_OPT_LEVEL_0
+KBUILD_RUSTCFLAGS += -Copt-level=0
+else ifdef CONFIG_RUST_OPT_LEVEL_1
+KBUILD_RUSTCFLAGS += -Copt-level=1
+else ifdef CONFIG_RUST_OPT_LEVEL_2
+KBUILD_RUSTCFLAGS += -Copt-level=2
+else ifdef CONFIG_RUST_OPT_LEVEL_3
+KBUILD_RUSTCFLAGS += -Copt-level=3
+else ifdef CONFIG_RUST_OPT_LEVEL_S
+KBUILD_RUSTCFLAGS += -Copt-level=s
+else ifdef CONFIG_RUST_OPT_LEVEL_Z
+KBUILD_RUSTCFLAGS += -Copt-level=z
endif
# Tell gcc to never replace conditional load with a non-conditional one
@@ -793,6 +853,7 @@ endif
KBUILD_CFLAGS += $(call cc-disable-warning, unused-const-variable)
ifdef CONFIG_FRAME_POINTER
KBUILD_CFLAGS += -fno-omit-frame-pointer -fno-optimize-sibling-calls
+KBUILD_RUSTCFLAGS += -Cforce-frame-pointers=y
else
# Some targets (ARM with Thumb2, for example), can't be built with frame
# pointers. For those, we don't have FUNCTION_TRACER automatically
@@ -826,6 +887,8 @@ ifdef CONFIG_CC_IS_GCC
DEBUG_CFLAGS += $(call cc-ifversion, -lt, 0500, $(call cc-option, -fno-var-tracking-assignments))
endif
+DEBUG_RUSTCFLAGS :=
+
ifdef CONFIG_DEBUG_INFO
ifdef CONFIG_DEBUG_INFO_SPLIT
@@ -836,6 +899,11 @@ endif
ifneq ($(LLVM_IAS),1)
KBUILD_AFLAGS += -Wa,-gdwarf-2
+ifdef CONFIG_DEBUG_INFO_REDUCED
+DEBUG_RUSTCFLAGS += -Cdebuginfo=1
+else
+DEBUG_RUSTCFLAGS += -Cdebuginfo=2
+endif
endif
ifndef CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT
@@ -860,6 +928,9 @@ endif # CONFIG_DEBUG_INFO
KBUILD_CFLAGS += $(DEBUG_CFLAGS)
export DEBUG_CFLAGS
+KBUILD_RUSTCFLAGS += $(DEBUG_RUSTCFLAGS)
+export DEBUG_RUSTCFLAGS
+
ifdef CONFIG_FUNCTION_TRACER
ifdef CONFIG_FTRACE_MCOUNT_USE_CC
CC_FLAGS_FTRACE += -mrecord-mcount
@@ -990,10 +1061,11 @@ include $(addprefix $(srctree)/, $(include-y))
# Do not add $(call cc-option,...) below this line. When you build the kernel
# from the clean source tree, the GCC plugins do not exist at this point.
-# Add user supplied CPPFLAGS, AFLAGS and CFLAGS as the last assignments
+# Add user supplied CPPFLAGS, AFLAGS, CFLAGS and RUSTCFLAGS as the last assignments
KBUILD_CPPFLAGS += $(KCPPFLAGS)
KBUILD_AFLAGS += $(KAFLAGS)
KBUILD_CFLAGS += $(KCFLAGS)
+KBUILD_RUSTCFLAGS += $(KRUSTCFLAGS)
KBUILD_LDFLAGS_MODULE += --build-id=sha1
LDFLAGS_vmlinux += --build-id=sha1
@@ -1138,6 +1210,10 @@ export MODULES_NSDEPS := $(extmod-prefix)modules.nsdeps
ifeq ($(KBUILD_EXTMOD),)
core-y += kernel/ certs/ mm/ fs/ ipc/ security/ crypto/ block/
+ifdef CONFIG_RUST
+core-y += rust/
+endif
+
vmlinux-dirs := $(patsubst %/,%,$(filter %/, \
$(core-y) $(core-m) $(drivers-y) $(drivers-m) \
$(libs-y) $(libs-m)))
@@ -1238,6 +1314,9 @@ archprepare: outputmakefile archheaders archscripts scripts include/config/kerne
prepare0: archprepare
$(Q)$(MAKE) $(build)=scripts/mod
$(Q)$(MAKE) $(build)=.
+ifdef CONFIG_RUST
+ $(Q)$(MAKE) $(build)=rust
+endif
# All the preparing..
prepare: prepare0 prepare-objtool prepare-resolve_btfids
@@ -1648,6 +1727,13 @@ help:
@echo ' kselftest-merge - Merge all the config dependencies of'
@echo ' kselftest to existing .config.'
@echo ''
+ @echo 'Rust targets:'
+ @echo ' rustfmt - Reformat all the Rust code in the kernel'
+ @echo ' rustfmtcheck - Checks if all the Rust code in the kernel'
+ @echo ' is formatted, printing a diff otherwise.'
+ @echo ' rustdoc - Generate Rust documentation'
+ @echo ' (requires kernel .config)'
+ @echo ''
@$(if $(dtstree), \
echo 'Devicetree:'; \
echo '* dtbs - Build device tree blobs for enabled boards'; \
@@ -1719,6 +1805,27 @@ PHONY += $(DOC_TARGETS)
$(DOC_TARGETS):
$(Q)$(MAKE) $(build)=Documentation $@
+
+# Rust targets
+# ---------------------------------------------------------------------------
+
+# Documentation target
+#
+# Using the singular to avoid running afoul of `no-dot-config-targets`.
+PHONY += rustdoc
+rustdoc: prepare0
+ $(Q)$(MAKE) $(build)=rust $@
+
+# Formatting targets
+PHONY += rustfmt rustfmtcheck
+
+rustfmt:
+ find -name '*.rs' | xargs $(RUSTFMT)
+
+rustfmtcheck:
+ find -name '*.rs' | xargs $(RUSTFMT) --check
+
+
# Misc
# ---------------------------------------------------------------------------
@@ -1866,6 +1973,7 @@ clean: $(clean-dirs)
$(call cmd,rmfiles)
@find $(if $(KBUILD_EXTMOD), $(KBUILD_EXTMOD), .) $(RCS_FIND_IGNORE) \
\( -name '*.[aios]' -o -name '*.ko' -o -name '.*.cmd' \
+ -o -name '*.rmeta' \
-o -name '*.ko.*' \
-o -name '*.dtb' -o -name '*.dtbo' -o -name '*.dtb.S' -o -name '*.dt.yaml' \
-o -name '*.dwo' -o -name '*.lst' \
diff --git a/arch/arm64/rust/target.json b/arch/arm64/rust/target.json
new file mode 100644
index 000000000000..44953e2725c4
--- /dev/null
+++ b/arch/arm64/rust/target.json
@@ -0,0 +1,40 @@
+{
+ "arch": "aarch64",
+ "data-layout": "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128",
+ "disable-redzone": true,
+ "eliminate-frame-pointer": false,
+ "emit-debug-gdb-scripts": false,
+ "env": "gnu",
+ "features": "+strict-align,+neon,+fp-armv8",
+ "function-sections": false,
+ "is-builtin": true,
+ "linker-flavor": "gcc",
+ "linker-is-gnu": true,
+ "llvm-target": "aarch64-unknown-none",
+ "max-atomic-width": 128,
+ "needs-plt": true,
+ "os": "none",
+ "panic-strategy": "abort",
+ "position-independent-executables": true,
+ "pre-link-args": {
+ "gcc": [
+ "-Wl,--as-needed",
+ "-Wl,-z,noexecstack",
+ "-m64"
+ ]
+ },
+ "relocation-model": "static",
+ "relro-level": "full",
+ "stack-probes": {
+ "kind": "inline-or-call",
+ "min-llvm-version-for-inline": [
+ 11,
+ 0,
+ 1
+ ]
+ },
+ "target-c-int-width": "32",
+ "target-endian": "little",
+ "target-pointer-width": "64",
+ "vendor": ""
+}
diff --git a/arch/powerpc/rust/target.json b/arch/powerpc/rust/target.json
new file mode 100644
index 000000000000..1e53f8308092
--- /dev/null
+++ b/arch/powerpc/rust/target.json
@@ -0,0 +1,30 @@
+{
+ "arch": "powerpc64",
+ "code-mode": "kernel",
+ "cpu": "ppc64le",
+ "data-layout": "e-m:e-i64:64-n32:64",
+ "env": "gnu",
+ "features": "-altivec,-vsx,-hard-float",
+ "function-sections": false,
+ "is-builtin": true,
+ "linker-flavor": "gcc",
+ "linker-is-gnu": true,
+ "llvm-target": "powerpc64le-elf",
+ "max-atomic-width": 64,
+ "os": "none",
+ "panic-strategy": "abort",
+ "position-independent-executables": true,
+ "pre-link-args": {
+ "gcc": [
+ "-Wl,--as-needed",
+ "-Wl,-z,noexecstack",
+ "-m64"
+ ]
+ },
+ "relocation-model": "static",
+ "relro-level": "full",
+ "target-family": "unix",
+ "target-mcount": "_mcount",
+ "target-endian": "little",
+ "target-pointer-width": "64"
+}
diff --git a/arch/x86/rust/target.json b/arch/x86/rust/target.json
new file mode 100644
index 000000000000..6e1759cd45bf
--- /dev/null
+++ b/arch/x86/rust/target.json
@@ -0,0 +1,42 @@
+{
+ "arch": "x86_64",
+ "code-model": "kernel",
+ "cpu": "x86-64",
+ "data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128",
+ "disable-redzone": true,
+ "eliminate-frame-pointer": false,
+ "emit-debug-gdb-scripts": false,
+ "env": "gnu",
+ "features": "-mmx,-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-3dnow,-3dnowa,-avx,-avx2,+soft-float",
+ "function-sections": false,
+ "is-builtin": true,
+ "linker-flavor": "gcc",
+ "linker-is-gnu": true,
+ "llvm-target": "x86_64-elf",
+ "max-atomic-width": 64,
+ "needs-plt": true,
+ "os": "none",
+ "panic-strategy": "abort",
+ "position-independent-executables": true,
+ "pre-link-args": {
+ "gcc": [
+ "-Wl,--as-needed",
+ "-Wl,-z,noexecstack",
+ "-m64"
+ ]
+ },
+ "relocation-model": "static",
+ "relro-level": "full",
+ "stack-probes": {
+ "kind": "inline-or-call",
+ "min-llvm-version-for-inline": [
+ 11,
+ 0,
+ 1
+ ]
+ },
+ "target-c-int-width": "32",
+ "target-endian": "little",
+ "target-pointer-width": "64",
+ "vendor": "unknown"
+}
diff --git a/init/Kconfig b/init/Kconfig
index 5f5c776ef192..11475840c29c 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -57,6 +57,15 @@ config LLD_VERSION
default $(ld-version) if LD_IS_LLD
default 0
+config HAS_RUST
+ depends on ARM64 || (PPC64 && CPU_LITTLE_ENDIAN) || X86_64
+ def_bool $(success,$(RUSTC) --version)
+
+config RUSTC_VERSION
+ depends on HAS_RUST
+ int
+ default $(shell,$(srctree)/scripts/rust-version.sh $(RUSTC))
+
config CC_CAN_LINK
bool
default $(success,$(srctree)/scripts/cc-can-link.sh $(CC) $(CLANG_FLAGS) $(m64-flag)) if 64BIT
@@ -2027,6 +2036,24 @@ config PROFILING
Say Y here to enable the extended profiling support mechanisms used
by profilers.
+config RUST
+ bool "Rust support"
+ depends on HAS_RUST
+ depends on !COMPILE_TEST
+ default n
+ help
+ Enables Rust support in the kernel.
+
+ This allows other Rust-related options, like drivers written in Rust,
+ to be selected.
+
+ It is also required to be able to load external kernel modules
+ written in Rust.
+
+ See Documentation/rust/ for more information.
+
+ If unsure, say N.
+
#
# Place an empty function call at each tracepoint site. Can be
# dynamically changed for a probe function.
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 2779c29d9981..acf4993baddc 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -2537,6 +2537,106 @@ config HYPERV_TESTING
endmenu # "Kernel Testing and Coverage"
+menu "Rust hacking"
+
+config RUST_DEBUG_ASSERTIONS
+ bool "Debug assertions"
+ default n
+ depends on RUST
+ help
+ Enables rustc's `-Cdebug-assertions` codegen option.
+
+ This flag lets you turn `cfg(debug_assertions)` conditional
+ compilation on or off. This can be used to enable extra debugging
+ code in development but not in production. For example, it controls
+ the behavior of the standard library's `debug_assert!` macro.
+
+ Note that this will apply to all Rust code, including `core`.
+
+ If unsure, say N.
+
+config RUST_OVERFLOW_CHECKS
+ bool "Overflow checks"
+ default y
+ depends on RUST
+ help
+ Enables rustc's `-Coverflow-checks` codegen option.
+
+ This flag allows you to control the behavior of runtime integer
+ overflow. When overflow-checks are enabled, a panic will occur
+ on overflow.
+
+ Note that this will apply to all Rust code, including `core`.
+
+ If unsure, say Y.
+
+choice
+ prompt "Optimization level"
+ default RUST_OPT_LEVEL_SIMILAR_AS_CHOSEN_FOR_C
+ depends on RUST
+ help
+ Controls rustc's `-Copt-level` codegen option.
+
+ This flag controls the optimization level.
+
+ If unsure, say "Similar as chosen for C".
+
+config RUST_OPT_LEVEL_SIMILAR_AS_CHOSEN_FOR_C
+ bool "Similar as chosen for C"
+ help
+ This choice will pick a similar optimization level as chosen in
+ the "Compiler optimization level" for C:
+
+ -O2 is currently mapped to -Copt-level=2
+ -O3 is currently mapped to -Copt-level=3
+ -Os is currently mapped to -Copt-level=z
+
+ The mapping may change over time to follow the intended semantics
+ of the choice for C as sensibly as possible.
+
+ This is the default.
+
+config RUST_OPT_LEVEL_0
+ bool "No optimizations (-Copt-level=0)"
+ help
+ Not recommended for most purposes. It may come in handy for debugging
+ suspected optimizer bugs, unexpected undefined behavior, etc.
+
+ Note that this level will *not* enable debug assertions nor overflow
+ checks on its own (like it happens when interacting with rustc
+ directly). Use the corresponding configuration options to control
+ that instead, orthogonally.
+
+config RUST_OPT_LEVEL_1
+ bool "Basic optimizations (-Copt-level=1)"
+ help
+ Useful for debugging without getting too lost, but without
+ the overhead and boilerplate of no optimizations at all.
+
+config RUST_OPT_LEVEL_2
+ bool "Some optimizations (-Copt-level=2)"
+ help
+ The sensible choice in most cases.
+
+config RUST_OPT_LEVEL_3
+ bool "All optimizations (-Copt-level=3)"
+ help
+ Yet more performance (hopefully).
+
+config RUST_OPT_LEVEL_S
+ bool "Optimize for size (-Copt-level=s)"
+ help
+ Smaller kernel, ideally without too much performance loss.
+
+config RUST_OPT_LEVEL_Z
+ bool "Optimize for size, no loop vectorization (-Copt-level=z)"
+ help
+ Like the previous level, but also turn off loop vectorization.
+
+endchoice
+
+endmenu # "Rust"
+
source "Documentation/Kconfig"
endmenu # Kernel hacking
diff --git a/rust/.gitignore b/rust/.gitignore
new file mode 100644
index 000000000000..8875e08ed0b1
--- /dev/null
+++ b/rust/.gitignore
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0
+
+bindings_generated.rs
+exports_*_generated.h
+doc/
\ No newline at end of file
diff --git a/rust/Makefile b/rust/Makefile
new file mode 100644
index 000000000000..ba4b13e4fc7f
--- /dev/null
+++ b/rust/Makefile
@@ -0,0 +1,141 @@
+# SPDX-License-Identifier: GPL-2.0
+
+RUSTDOC = rustdoc
+
+quiet_cmd_rustdoc = RUSTDOC $<
+ cmd_rustdoc = \
+ RUST_BINDINGS_FILE=$(abspath $(objtree)/rust/bindings_generated.rs) \
+ $(RUSTDOC) $(filter-out --emit=%, $(rustc_flags)) \
+ $(rustdoc_target_flags) -L $(objtree)/rust/ \
+ --output $(objtree)/rust/doc --crate-name $(subst rustdoc-,,$@) \
+ -Fmissing-docs @$(objtree)/include/generated/rustc_cfg $<
+
+rustdoc: rustdoc-module rustdoc-compiler_builtins rustdoc-kernel
+
+rustdoc-module: private rustdoc_target_flags = --crate-type proc-macro \
+ --extern proc_macro
+rustdoc-module: $(srctree)/rust/module.rs FORCE
+ $(call if_changed,rustdoc)
+
+rustdoc-compiler_builtins: $(srctree)/rust/compiler_builtins.rs FORCE
+ $(call if_changed,rustdoc)
+
+rustdoc-kernel: private rustdoc_target_flags = --extern alloc \
+ --extern module=$(objtree)/rust/libmodule.so
+rustdoc-kernel: $(srctree)/rust/kernel/lib.rs rustdoc-module \
+ $(objtree)/rust/libmodule.so $(objtree)/rust/bindings_generated.rs FORCE
+ $(call if_changed,rustdoc)
+
+ifdef CONFIG_CC_IS_CLANG
+bindgen_c_flags = $(c_flags)
+else
+# bindgen relies on libclang to parse C. Ideally, bindgen would support a GCC
+# plugin backend and/or the Clang driver would be perfectly compatible with GCC.
+#
+# For the moment, here we are tweaking the flags on the fly. Some config
+# options may not work (e.g. `GCC_PLUGIN_RANDSTRUCT` if we end up using one
+# of those structs). We might want to redo how Clang flags are kept track of
+# in the general `Makefile` even for GCC builds, similar to what we did with
+# `TENTATIVE_CLANG_FLAGS`.
+bindgen_skip_c_flags := -mno-fp-ret-in-387 -mpreferred-stack-boundary=% \
+ -mskip-rax-setup -mgeneral-regs-only -msign-return-address=% \
+ -mindirect-branch=thunk-extern -mindirect-branch-register -mrecord-mcount \
+ -mabi=lp64 -mstack-protector-guard% -fconserve-stack -falign-jumps=% \
+ -falign-loops=% -fno-ipa-cp-clone -fno-partial-inlining \
+ -fno-reorder-blocks -fno-allow-store-data-races -fasan-shadow-offset=% \
+ -Wno-packed-not-aligned -Wno-format-truncation -Wno-format-overflow \
+ -Wno-stringop-truncation -Wno-unused-but-set-variable \
+ -Wno-stringop-overflow -Wno-restrict -Wno-maybe-uninitialized \
+ -Werror=designated-init -Wno-zero-length-bounds \
+ --param=% --param asan-%
+
+# PowerPC
+bindgen_skip_c_flags += -mtraceback=no -mno-pointers-to-nested-functions \
+ -mno-string -mno-strict-align
+
+bindgen_extra_c_flags = $(TENTATIVE_CLANG_FLAGS) -Wno-address-of-packed-member
+bindgen_c_flags = $(filter-out $(bindgen_skip_c_flags), $(c_flags)) \
+ $(bindgen_extra_c_flags)
+endif
+
+bindgen_opaque_types := xregs_state desc_struct arch_lbr_state
+
+# To avoid several recompilations in PowerPC, which inserts `-D_TASK_CPU`
+bindgen_c_flags_final = $(filter-out -D_TASK_CPU=%, $(bindgen_c_flags))
+
+quiet_cmd_bindgen = BINDGEN $@
+ cmd_bindgen = \
+ $(BINDGEN) $< $(addprefix --opaque-type , $(bindgen_opaque_types)) \
+ --use-core --with-derive-default --ctypes-prefix c_types \
+ --size_t-is-usize -o $@ -- $(bindgen_c_flags_final) -DMODULE
+
+$(objtree)/rust/bindings_generated.rs: $(srctree)/rust/kernel/bindings_helper.h FORCE
+ $(call if_changed_dep,bindgen)
+
+quiet_cmd_exports = EXPORTS $@
+ cmd_exports = \
+ $(NM) -p --defined-only $< \
+ | grep -E ' (T|R|D) ' | cut -d ' ' -f 3 | grep -E '^(__rust_|_R)' \
+ | xargs -Isymbol \
+ echo 'EXPORT_SYMBOL_RUST_GPL(symbol);' > $@
+
+$(objtree)/rust/exports_core_generated.h: $(objtree)/rust/core.o FORCE
+ $(call if_changed,exports)
+
+$(objtree)/rust/exports_alloc_generated.h: $(objtree)/rust/alloc.o FORCE
+ $(call if_changed,exports)
+
+$(objtree)/rust/exports_kernel_generated.h: $(objtree)/rust/kernel.o FORCE
+ $(call if_changed,exports)
+
+# `-Cpanic=unwind -Cforce-unwind-tables=y` overrides `rustc_flags` in order to
+# avoid the https://github.com/rust-lang/rust/issues/82320 rustc crash.
+quiet_cmd_rustc_procmacro = $(RUSTC_OR_CLIPPY_QUIET) P $@
+ cmd_rustc_procmacro = \
+ $(RUSTC_OR_CLIPPY) $(rustc_flags) \
+ --emit=dep-info,link --extern proc_macro \
+ -Cpanic=unwind -Cforce-unwind-tables=y \
+ --crate-type proc-macro --out-dir $(objtree)/rust/ \
+ --crate-name $(patsubst lib%.so,%,$(notdir $@)) $<; \
+ mv $(objtree)/rust/$(patsubst lib%.so,%,$(notdir $@)).d $(depfile); \
+ sed -i '/^\#/d' $(depfile)
+
+$(objtree)/rust/libmodule.so: $(srctree)/rust/module.rs FORCE
+ $(call if_changed_dep,rustc_procmacro)
+
+quiet_cmd_rustc_library = $(if $(skip_clippy),RUSTC,$(RUSTC_OR_CLIPPY_QUIET)) L $@
+ cmd_rustc_library = \
+ RUST_BINDINGS_FILE=$(abspath $(objtree)/rust/bindings_generated.rs) \
+ $(if $(skip_clippy),$(RUSTC),$(RUSTC_OR_CLIPPY)) \
+ $(rustc_flags) $(rustc_cross_flags) $(rustc_target_flags) \
+ --crate-type rlib --out-dir $(objtree)/rust/ -L $(objtree)/rust/ \
+ --crate-name $(patsubst %.o,%,$(notdir $@)) $<; \
+ mv $(objtree)/rust/$(patsubst %.o,%,$(notdir $@)).d $(depfile); \
+ sed -i '/^\#/d' $(depfile) \
+ $(if $(rustc_objcopy),;$(OBJCOPY) $(rustc_objcopy) $@)
+
+# `$(rustc_flags)` is passed in case the user added `--sysroot`.
+rustc_sysroot = $(shell $(RUSTC) $(rustc_flags) --print sysroot)
+rustc_src = $(rustc_sysroot)/lib/rustlib/src/rust
+
+.SECONDEXPANSION:
+$(objtree)/rust/core.o: private skip_clippy = 1
+$(objtree)/rust/core.o: $$(rustc_src)/library/core/src/lib.rs FORCE
+ $(call if_changed_dep,rustc_library)
+
+$(objtree)/rust/compiler_builtins.o: private rustc_objcopy = -w -W '__*'
+$(objtree)/rust/compiler_builtins.o: $(srctree)/rust/compiler_builtins.rs \
+ $(objtree)/rust/core.o FORCE
+ $(call if_changed_dep,rustc_library)
+
+$(objtree)/rust/alloc.o: private skip_clippy = 1
+$(objtree)/rust/alloc.o: $$(rustc_src)/library/alloc/src/lib.rs \
+ $(objtree)/rust/compiler_builtins.o FORCE
+ $(call if_changed_dep,rustc_library)
+
+# ICE on `--extern module`: https://github.com/rust-lang/rust/issues/56935
+$(objtree)/rust/kernel.o: private rustc_target_flags = --extern alloc \
+ --extern module=$(objtree)/rust/libmodule.so
+$(objtree)/rust/kernel.o: $(srctree)/rust/kernel/lib.rs $(objtree)/rust/alloc.o \
+ $(objtree)/rust/libmodule.so $(objtree)/rust/bindings_generated.rs FORCE
+ $(call if_changed_dep,rustc_library)
diff --git a/scripts/Makefile.build b/scripts/Makefile.build
index 1b6094a13034..3665c49c4dcf 100644
--- a/scripts/Makefile.build
+++ b/scripts/Makefile.build
@@ -26,6 +26,7 @@ EXTRA_CPPFLAGS :=
EXTRA_LDFLAGS :=
asflags-y :=
ccflags-y :=
+rustcflags-y :=
cppflags-y :=
ldflags-y :=
@@ -287,6 +288,24 @@ quiet_cmd_cc_lst_c = MKLST $@
$(obj)/%.lst: $(src)/%.c FORCE
$(call if_changed_dep,cc_lst_c)
+# Compile Rust sources (.rs)
+# ---------------------------------------------------------------------------
+
+rustc_cross_flags := --target=$(srctree)/arch/$(SRCARCH)/rust/target.json
+
+quiet_cmd_rustc_o_rs = $(RUSTC_OR_CLIPPY_QUIET) $(quiet_modtag) $@
+ cmd_rustc_o_rs = \
+ RUST_MODFILE=$(modfile) \
+ $(RUSTC_OR_CLIPPY) $(rustc_flags) $(rustc_cross_flags) \
+ --extern alloc --extern kernel \
+ --crate-type rlib --out-dir $(obj) -L $(objtree)/rust/ \
+ --crate-name $(patsubst %.o,%,$(notdir $@)) $<; \
+ mv $(obj)/$(subst .o,,$(notdir $@)).d $(depfile); \
+ sed -i '/^\#/d' $(depfile)
+
+$(obj)/%.o: $(src)/%.rs FORCE
+ $(call if_changed_dep,rustc_o_rs)
+
# Compile assembler sources (.S)
# ---------------------------------------------------------------------------
diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
index 8cd67b1b6d15..bd6cb3562fb4 100644
--- a/scripts/Makefile.lib
+++ b/scripts/Makefile.lib
@@ -8,6 +8,7 @@ ldflags-y += $(EXTRA_LDFLAGS)
# flags that take effect in current and sub directories
KBUILD_AFLAGS += $(subdir-asflags-y)
KBUILD_CFLAGS += $(subdir-ccflags-y)
+KBUILD_RUSTCFLAGS += $(subdir-rustcflags-y)
# Figure out what we need to build from the various variables
# ===========================================================================
@@ -122,6 +123,10 @@ _c_flags = $(filter-out $(CFLAGS_REMOVE_$(target-stem).o), \
$(filter-out $(ccflags-remove-y), \
$(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) $(ccflags-y)) \
$(CFLAGS_$(target-stem).o))
+_rustc_flags = $(filter-out $(RUSTCFLAGS_REMOVE_$(target-stem).o), \
+ $(filter-out $(rustcflags-remove-y), \
+ $(KBUILD_RUSTCFLAGS) $(rustcflags-y)) \
+ $(RUSTCFLAGS_$(target-stem).o))
_a_flags = $(filter-out $(AFLAGS_REMOVE_$(target-stem).o), \
$(filter-out $(asflags-remove-y), \
$(KBUILD_CPPFLAGS) $(KBUILD_AFLAGS) $(asflags-y)) \
@@ -191,6 +196,11 @@ modkern_cflags = \
$(KBUILD_CFLAGS_MODULE) $(CFLAGS_MODULE), \
$(KBUILD_CFLAGS_KERNEL) $(CFLAGS_KERNEL) $(modfile_flags))
+modkern_rustcflags = \
+ $(if $(part-of-module), \
+ $(KBUILD_RUSTCFLAGS_MODULE) $(RUSTCFLAGS_MODULE), \
+ $(KBUILD_RUSTCFLAGS_KERNEL) $(RUSTCFLAGS_KERNEL))
+
modkern_aflags = $(if $(part-of-module), \
$(KBUILD_AFLAGS_MODULE) $(AFLAGS_MODULE), \
$(KBUILD_AFLAGS_KERNEL) $(AFLAGS_KERNEL))
@@ -200,6 +210,8 @@ c_flags = -Wp,-MMD,$(depfile) $(NOSTDINC_FLAGS) $(LINUXINCLUDE) \
$(_c_flags) $(modkern_cflags) \
$(basename_flags) $(modname_flags)
+rustc_flags = $(_rustc_flags) $(modkern_rustcflags) @$(objtree)/include/generated/rustc_cfg
+
a_flags = -Wp,-MMD,$(depfile) $(NOSTDINC_FLAGS) $(LINUXINCLUDE) \
$(_a_flags) $(modkern_aflags)
diff --git a/scripts/kconfig/confdata.c b/scripts/kconfig/confdata.c
index 2568dbe16ed6..a83d646ecef5 100644
--- a/scripts/kconfig/confdata.c
+++ b/scripts/kconfig/confdata.c
@@ -637,6 +637,56 @@ static struct conf_printer kconfig_printer_cb =
.print_comment = kconfig_print_comment,
};
+/*
+ * rustc cfg printer
+ *
+ * This printer is used when generating the resulting rustc configuration
+ * after kconfig invocation and `defconfig` files.
+ */
+static void rustc_cfg_print_symbol(FILE *fp, struct symbol *sym, const char *value, void *arg)
+{
+ const char *str;
+
+ switch (sym->type) {
+ case S_INT:
+ case S_HEX:
+ case S_BOOLEAN:
+ case S_TRISTATE:
+ str = sym_escape_string_value(value);
+
+ /*
+ * We don't care about disabled ones, i.e. no need for
+ * what otherwise are "comments" in other printers.
+ */
+ if (*value == 'n')
+ return;
+
+ /*
+ * To have similar functionality to the C macro `IS_ENABLED()`
+ * we provide an empty `--cfg CONFIG_X` here in both `y`
+ * and `m` cases.
+ *
+ * Then, the common `fprintf()` below will also give us
+ * a `--cfg CONFIG_X="y"` or `--cfg CONFIG_X="m"`, which can
+ * be used as the equivalent of `IS_BUILTIN()`/`IS_MODULE()`.
+ */
+ if (*value == 'y' || *value == 'm')
+ fprintf(fp, "--cfg=%s%s\n", CONFIG_, sym->name);
+
+ break;
+ default:
+ str = value;
+ break;
+ }
+
+ fprintf(fp, "--cfg=%s%s=%s\n", CONFIG_, sym->name, str);
+}
+
+static struct conf_printer rustc_cfg_printer_cb =
+{
+ .print_symbol = rustc_cfg_print_symbol,
+};
+
/*
* Header printer
*
@@ -1044,7 +1094,7 @@ int conf_write_autoconf(int overwrite)
struct symbol *sym;
const char *name;
const char *autoconf_name = conf_get_autoconfig_name();
- FILE *out, *out_h;
+ FILE *out, *out_h, *out_rustc_cfg;
int i;
if (!overwrite && is_present(autoconf_name))
@@ -1065,6 +1115,13 @@ int conf_write_autoconf(int overwrite)
return 1;
}
+ out_rustc_cfg = fopen(".tmp_rustc_cfg", "w");
+ if (!out_rustc_cfg) {
+ fclose(out);
+ fclose(out_h);
+ return 1;
+ }
+
conf_write_heading(out, &kconfig_printer_cb, NULL);
conf_write_heading(out_h, &header_printer_cb, NULL);
@@ -1076,9 +1133,11 @@ int conf_write_autoconf(int overwrite)
/* write symbols to auto.conf and autoconf.h */
conf_write_symbol(out, sym, &kconfig_printer_cb, (void *)1);
conf_write_symbol(out_h, sym, &header_printer_cb, NULL);
+ conf_write_symbol(out_rustc_cfg, sym, &rustc_cfg_printer_cb, NULL);
}
fclose(out);
fclose(out_h);
+ fclose(out_rustc_cfg);
name = getenv("KCONFIG_AUTOHEADER");
if (!name)
@@ -1097,6 +1156,12 @@ int conf_write_autoconf(int overwrite)
if (rename(".tmpconfig", autoconf_name))
return 1;
+ name = "include/generated/rustc_cfg";
+ if (make_parent_dir(name))
+ return 1;
+ if (rename(".tmp_rustc_cfg", name))
+ return 1;
+
return 0;
}
diff --git a/scripts/rust-version.sh b/scripts/rust-version.sh
new file mode 100755
index 000000000000..67b6d31688e2
--- /dev/null
+++ b/scripts/rust-version.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0
+#
+# rust-version rust-command
+#
+# Print the compiler version of `rust-command' in a 5 or 6-digit form
+# such as `14502' for rustc-1.45.2 etc.
+#
+# Returns 0 if not found (so that Kconfig does not complain)
+compiler="$*"
+
+if [ ${#compiler} -eq 0 ]; then
+ echo "Error: No compiler specified." >&2
+ printf "Usage:\n\t$0 <rust-command>\n" >&2
+ exit 1
+fi
+
+if ! command -v $compiler >/dev/null 2>&1; then
+ echo 0
+ exit 0
+fi
+
+VERSION=$($compiler --version | cut -f2 -d' ')
+
+# Cut suffix if any (e.g. `-dev`)
+VERSION=$(echo $VERSION | cut -f1 -d'-')
+
+MAJOR=$(echo $VERSION | cut -f1 -d'.')
+MINOR=$(echo $VERSION | cut -f2 -d'.')
+PATCHLEVEL=$(echo $VERSION | cut -f3 -d'.')
+printf "%d%02d%02d\\n" $MAJOR $MINOR $PATCHLEVEL
--
2.17.1
From: Miguel Ojeda <[email protected]>
Now that all the shared Rust infrastructure is in, we export all
the symbols reusing the `EXPORT_SYMBOL_GPL` macro from C. The lists
of symbols are generated on the fly when compiling the crates.
There are three main sets of symbols to distinguish:
- The ones from the `core` and `alloc` crates (from the Rust
standard library). The code is licensed as Apache/MIT.
- The ones from our abstractions in the `kernel` crate.
- The helpers (already exported since they are not generated).
We export everything as GPL. This ensures we do not mistakenly
expose GPL kernel symbols/features as non-GPL, even indirectly.
Co-developed-by: Alex Gaynor <[email protected]>
Signed-off-by: Alex Gaynor <[email protected]>
Co-developed-by: Geoffrey Thomas <[email protected]>
Signed-off-by: Geoffrey Thomas <[email protected]>
Co-developed-by: Finn Behrens <[email protected]>
Signed-off-by: Finn Behrens <[email protected]>
Co-developed-by: Adam Bratschi-Kaye <[email protected]>
Signed-off-by: Adam Bratschi-Kaye <[email protected]>
Co-developed-by: Wedson Almeida Filho <[email protected]>
Signed-off-by: Wedson Almeida Filho <[email protected]>
Signed-off-by: Miguel Ojeda <[email protected]>
---
rust/Makefile | 2 ++
rust/exports.c | 16 ++++++++++++++++
2 files changed, 18 insertions(+)
create mode 100644 rust/exports.c
diff --git a/rust/Makefile b/rust/Makefile
index dbbbdbad6941..2d1d9d7af77c 100644
--- a/rust/Makefile
+++ b/rust/Makefile
@@ -9,6 +9,8 @@ extra-$(CONFIG_RUST) += bindings_generated.rs
obj-$(CONFIG_RUST) += alloc.o kernel.o
extra-$(CONFIG_RUST) += exports_alloc_generated.h exports_kernel_generated.h
+obj-$(CONFIG_RUST) += exports.o
+
RUSTDOC = rustdoc
quiet_cmd_rustdoc = RUSTDOC $<
diff --git a/rust/exports.c b/rust/exports.c
new file mode 100644
index 000000000000..d7dff1b3b919
--- /dev/null
+++ b/rust/exports.c
@@ -0,0 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// A hack to export Rust symbols for loadable modules without having to redo
+// the entire `include/linux/export.h` logic in Rust.
+//
+// This requires the Rust's new/future `v0` mangling scheme because the default
+// one ("legacy") uses invalid characters for C identifiers (thus we cannot use
+// the `EXPORT_SYMBOL_*` macros).
+
+#include <linux/module.h>
+
+#define EXPORT_SYMBOL_RUST_GPL(sym) extern int sym; EXPORT_SYMBOL_GPL(sym);
+
+#include "exports_core_generated.h"
+#include "exports_alloc_generated.h"
+#include "exports_kernel_generated.h"
--
2.17.1
From: Miguel Ojeda <[email protected]>
To support Rust under GCC-built kernels, we need to save the flags that
would have been passed if the kernel was being compiled with Clang.
The reason is that bindgen -- the tool we use to generate Rust bindings
to the C side of the kernel -- relies on libclang to parse C. Ideally:
- bindgen would support a GCC backend (requested at [1]),
- or the Clang driver would be perfectly compatible with GCC,
including plugins. Unlikely, of course, but perhaps a big
subset of configs may be possible to guarantee to be kept
compatible nevertheless.
This is also the reason why GCC builds are very experimental and some
configurations may not work (e.g. GCC_PLUGIN_RANDSTRUCT). However,
we keep GCC builds working (for some example configs) in the CI
to avoid diverging/regressing further, so that we are better prepared
for the future when a solution might become available.
[1] https://github.com/rust-lang/rust-bindgen/issues/1949
Link: https://github.com/Rust-for-Linux/linux/issues/167
Co-developed-by: Alex Gaynor <[email protected]>
Signed-off-by: Alex Gaynor <[email protected]>
Co-developed-by: Geoffrey Thomas <[email protected]>
Signed-off-by: Geoffrey Thomas <[email protected]>
Co-developed-by: Finn Behrens <[email protected]>
Signed-off-by: Finn Behrens <[email protected]>
Co-developed-by: Adam Bratschi-Kaye <[email protected]>
Signed-off-by: Adam Bratschi-Kaye <[email protected]>
Co-developed-by: Wedson Almeida Filho <[email protected]>
Signed-off-by: Wedson Almeida Filho <[email protected]>
Signed-off-by: Miguel Ojeda <[email protected]>
---
Makefile | 27 ++++++++++++++++-----------
1 file changed, 16 insertions(+), 11 deletions(-)
diff --git a/Makefile b/Makefile
index d4784d181123..9c75354324ed 100644
--- a/Makefile
+++ b/Makefile
@@ -559,26 +559,31 @@ ifdef building_out_of_srctree
{ echo "# this is build directory, ignore it"; echo "*"; } > .gitignore
endif
-# The expansion should be delayed until arch/$(SRCARCH)/Makefile is included.
-# Some architectures define CROSS_COMPILE in arch/$(SRCARCH)/Makefile.
-# CC_VERSION_TEXT is referenced from Kconfig (so it needs export),
-# and from include/config/auto.conf.cmd to detect the compiler upgrade.
-CC_VERSION_TEXT = $(shell $(CC) --version 2>/dev/null | head -n 1 | sed 's/\#//g')
+TENTATIVE_CLANG_FLAGS := -Werror=unknown-warning-option
-ifneq ($(findstring clang,$(CC_VERSION_TEXT)),)
ifneq ($(CROSS_COMPILE),)
-CLANG_FLAGS += --target=$(notdir $(CROSS_COMPILE:%-=%))
+TENTATIVE_CLANG_FLAGS += --target=$(notdir $(CROSS_COMPILE:%-=%))
GCC_TOOLCHAIN_DIR := $(dir $(shell which $(CROSS_COMPILE)elfedit))
-CLANG_FLAGS += --prefix=$(GCC_TOOLCHAIN_DIR)$(notdir $(CROSS_COMPILE))
+TENTATIVE_CLANG_FLAGS += --prefix=$(GCC_TOOLCHAIN_DIR)$(notdir $(CROSS_COMPILE))
GCC_TOOLCHAIN := $(realpath $(GCC_TOOLCHAIN_DIR)/..)
endif
ifneq ($(GCC_TOOLCHAIN),)
-CLANG_FLAGS += --gcc-toolchain=$(GCC_TOOLCHAIN)
+TENTATIVE_CLANG_FLAGS += --gcc-toolchain=$(GCC_TOOLCHAIN)
endif
ifneq ($(LLVM_IAS),1)
-CLANG_FLAGS += -no-integrated-as
+TENTATIVE_CLANG_FLAGS += -no-integrated-as
endif
-CLANG_FLAGS += -Werror=unknown-warning-option
+
+export TENTATIVE_CLANG_FLAGS
+
+# The expansion should be delayed until arch/$(SRCARCH)/Makefile is included.
+# Some architectures define CROSS_COMPILE in arch/$(SRCARCH)/Makefile.
+# CC_VERSION_TEXT is referenced from Kconfig (so it needs export),
+# and from include/config/auto.conf.cmd to detect the compiler upgrade.
+CC_VERSION_TEXT = $(shell $(CC) --version 2>/dev/null | head -n 1 | sed 's/\#//g')
+
+ifneq ($(findstring clang,$(CC_VERSION_TEXT)),)
+CLANG_FLAGS += $(TENTATIVE_CLANG_FLAGS)
KBUILD_CFLAGS += $(CLANG_FLAGS)
KBUILD_AFLAGS += $(CLANG_FLAGS)
export CLANG_FLAGS
--
2.17.1
From: Miguel Ojeda <[email protected]>
A set of Rust modules that showcase how Rust modules look like
and how to use the abstracted kernel features.
At the moment we also use them as poor man's tests in our CI.
However, we plan to implement a proper testing framework.
The semaphore sample comes with a C version for comparison.
Co-developed-by: Alex Gaynor <[email protected]>
Signed-off-by: Alex Gaynor <[email protected]>
Co-developed-by: Geoffrey Thomas <[email protected]>
Signed-off-by: Geoffrey Thomas <[email protected]>
Co-developed-by: Finn Behrens <[email protected]>
Signed-off-by: Finn Behrens <[email protected]>
Co-developed-by: Adam Bratschi-Kaye <[email protected]>
Signed-off-by: Adam Bratschi-Kaye <[email protected]>
Co-developed-by: Wedson Almeida Filho <[email protected]>
Signed-off-by: Wedson Almeida Filho <[email protected]>
Signed-off-by: Miguel Ojeda <[email protected]>
---
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 ++++++++++
13 files changed, 1014 insertions(+)
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
diff --git a/samples/Kconfig b/samples/Kconfig
index e76cdfc50e25..f1f8ba9bee82 100644
--- a/samples/Kconfig
+++ b/samples/Kconfig
@@ -216,4 +216,6 @@ config SAMPLE_WATCH_QUEUE
Build example userspace program to use the new mount_notify(),
sb_notify() syscalls and the KEYCTL_WATCH_KEY keyctl() function.
+source "samples/rust/Kconfig"
+
endif # SAMPLES
diff --git a/samples/Makefile b/samples/Makefile
index c3392a595e4b..68ba3b3da044 100644
--- a/samples/Makefile
+++ b/samples/Makefile
@@ -29,3 +29,4 @@ obj-$(CONFIG_SAMPLE_INTEL_MEI) += mei/
subdir-$(CONFIG_SAMPLE_WATCHDOG) += watchdog
subdir-$(CONFIG_SAMPLE_WATCH_QUEUE) += watch_queue
obj-$(CONFIG_DEBUG_KMEMLEAK_TEST) += kmemleak/
+obj-$(CONFIG_SAMPLES_RUST) += rust/
diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig
new file mode 100644
index 000000000000..8eb5d5ebdf00
--- /dev/null
+++ b/samples/rust/Kconfig
@@ -0,0 +1,103 @@
+# SPDX-License-Identifier: GPL-2.0
+
+menuconfig SAMPLES_RUST
+ bool "Rust samples"
+ depends on RUST
+ help
+ You can build sample Rust kernel code here.
+
+ If unsure, say N.
+
+if SAMPLES_RUST
+
+config SAMPLE_RUST_MINIMAL
+ tristate "Minimal"
+ help
+ This option builds the Rust minimal module sample.
+
+ To compile this as a module, choose M here:
+ the module will be called rust_minimal.
+
+ If unsure, say N.
+
+config SAMPLE_RUST_PRINT
+ tristate "Printing macros"
+ help
+ This option builds the Rust printing macros sample.
+
+ To compile this as a module, choose M here:
+ the module will be called rust_print.
+
+ If unsure, say N.
+
+config SAMPLE_RUST_MODULE_PARAMETERS
+ tristate "Module parameters"
+ help
+ This option builds the Rust module parameters sample.
+
+ To compile this as a module, choose M here:
+ the module will be called rust_module_parameters.
+
+ If unsure, say N.
+
+config SAMPLE_RUST_SYNC
+ tristate "Synchronisation primitives"
+ help
+ This option builds the Rust synchronisation primitives sample.
+
+ To compile this as a module, choose M here:
+ the module will be called rust_sync.
+
+ If unsure, say N.
+
+config SAMPLE_RUST_CHRDEV
+ tristate "Character device"
+ help
+ This option builds the Rust character device sample.
+
+ To compile this as a module, choose M here:
+ the module will be called rust_chrdev.
+
+ If unsure, say N.
+
+config SAMPLE_RUST_MISCDEV
+ tristate "Miscellaneous device"
+ help
+ This option builds the Rust miscellaneous device sample.
+
+ To compile this as a module, choose M here:
+ the module will be called rust_miscdev.
+
+ If unsure, say N.
+
+config SAMPLE_RUST_STACK_PROBING
+ tristate "Stack probing"
+ help
+ This option builds the Rust stack probing sample.
+
+ To compile this as a module, choose M here:
+ the module will be called rust_stack_probing.
+
+ If unsure, say N.
+
+config SAMPLE_RUST_SEMAPHORE
+ tristate "Semaphore"
+ help
+ This option builds the Rust semaphore sample.
+
+ To compile this as a module, choose M here:
+ the module will be called rust_semaphore.
+
+ If unsure, say N.
+
+config SAMPLE_RUST_SEMAPHORE_C
+ tristate "Semaphore (in C, for comparison)"
+ help
+ This option builds the Rust semaphore sample (in C, for comparison).
+
+ To compile this as a module, choose M here:
+ the module will be called rust_semaphore_c.
+
+ If unsure, say N.
+
+endif # SAMPLES_RUST
diff --git a/samples/rust/Makefile b/samples/rust/Makefile
new file mode 100644
index 000000000000..c53a71999d8f
--- /dev/null
+++ b/samples/rust/Makefile
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_SAMPLE_RUST_MINIMAL) += rust_minimal.o
+obj-$(CONFIG_SAMPLE_RUST_PRINT) += rust_print.o
+obj-$(CONFIG_SAMPLE_RUST_MODULE_PARAMETERS) += rust_module_parameters.o
+obj-$(CONFIG_SAMPLE_RUST_SYNC) += rust_sync.o
+obj-$(CONFIG_SAMPLE_RUST_CHRDEV) += rust_chrdev.o
+obj-$(CONFIG_SAMPLE_RUST_MISCDEV) += rust_miscdev.o
+obj-$(CONFIG_SAMPLE_RUST_STACK_PROBING) += rust_stack_probing.o
+obj-$(CONFIG_SAMPLE_RUST_SEMAPHORE) += rust_semaphore.o
+obj-$(CONFIG_SAMPLE_RUST_SEMAPHORE_C) += rust_semaphore_c.o
diff --git a/samples/rust/rust_chrdev.rs b/samples/rust/rust_chrdev.rs
new file mode 100644
index 000000000000..e3231c4f4504
--- /dev/null
+++ b/samples/rust/rust_chrdev.rs
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Rust character device sample
+
+#![no_std]
+#![feature(allocator_api, global_asm)]
+
+use alloc::boxed::Box;
+use core::pin::Pin;
+use kernel::prelude::*;
+use kernel::{
+ chrdev, cstr,
+ file_operations::{FileOpener, FileOperations},
+};
+
+module! {
+ type: RustChrdev,
+ name: b"rust_chrdev",
+ author: b"Rust for Linux Contributors",
+ description: b"Rust character device sample",
+ license: b"GPL v2",
+ params: {
+ },
+}
+
+struct RustFile;
+
+impl FileOpener<()> for RustFile {
+ fn open(_ctx: &()) -> KernelResult<Self::Wrapper> {
+ pr_info!("rust file was opened!\n");
+ Ok(Box::try_new(Self)?)
+ }
+}
+
+impl FileOperations for RustFile {
+ type Wrapper = Box<Self>;
+
+ kernel::declare_file_operations!();
+}
+
+struct RustChrdev {
+ _dev: Pin<Box<chrdev::Registration<2>>>,
+}
+
+impl KernelModule for RustChrdev {
+ fn init() -> KernelResult<Self> {
+ pr_info!("Rust character device sample (init)\n");
+
+ let mut chrdev_reg =
+ chrdev::Registration::new_pinned(cstr!("rust_chrdev"), 0, &THIS_MODULE)?;
+
+ // Register the same kind of device twice, we're just demonstrating
+ // that you can use multiple minors. There are two minors in this case
+ // because its type is `chrdev::Registration<2>`
+ chrdev_reg.as_mut().register::<RustFile>()?;
+ chrdev_reg.as_mut().register::<RustFile>()?;
+
+ Ok(RustChrdev { _dev: chrdev_reg })
+ }
+}
+
+impl Drop for RustChrdev {
+ fn drop(&mut self) {
+ pr_info!("Rust character device sample (exit)\n");
+ }
+}
diff --git a/samples/rust/rust_minimal.rs b/samples/rust/rust_minimal.rs
new file mode 100644
index 000000000000..21627ce5656e
--- /dev/null
+++ b/samples/rust/rust_minimal.rs
@@ -0,0 +1,40 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Rust minimal sample
+
+#![no_std]
+#![feature(allocator_api, global_asm)]
+
+use kernel::prelude::*;
+
+module! {
+ type: RustMinimal,
+ name: b"rust_minimal",
+ author: b"Rust for Linux Contributors",
+ description: b"Rust minimal sample",
+ license: b"GPL v2",
+ params: {
+ },
+}
+
+struct RustMinimal {
+ message: String,
+}
+
+impl KernelModule for RustMinimal {
+ fn init() -> KernelResult<Self> {
+ pr_info!("Rust minimal sample (init)\n");
+ pr_info!("Am I built-in? {}\n", !cfg!(MODULE));
+
+ Ok(RustMinimal {
+ message: "on the heap!".to_owned(),
+ })
+ }
+}
+
+impl Drop for RustMinimal {
+ fn drop(&mut self) {
+ pr_info!("My message is {}\n", self.message);
+ pr_info!("Rust minimal sample (exit)\n");
+ }
+}
diff --git a/samples/rust/rust_miscdev.rs b/samples/rust/rust_miscdev.rs
new file mode 100644
index 000000000000..3d48f1e2fad3
--- /dev/null
+++ b/samples/rust/rust_miscdev.rs
@@ -0,0 +1,145 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Rust miscellaneous device sample
+
+#![no_std]
+#![feature(allocator_api, global_asm)]
+
+use alloc::{boxed::Box, sync::Arc};
+use core::pin::Pin;
+use kernel::prelude::*;
+use kernel::{
+ cstr,
+ file_operations::{File, FileOpener, FileOperations},
+ miscdev,
+ sync::{CondVar, Mutex},
+ user_ptr::{UserSlicePtrReader, UserSlicePtrWriter},
+ Error,
+};
+
+module! {
+ type: RustMiscdev,
+ name: b"rust_miscdev",
+ author: b"Rust for Linux Contributors",
+ description: b"Rust miscellaneous device sample",
+ license: b"GPL v2",
+ params: {
+ },
+}
+
+const MAX_TOKENS: usize = 3;
+
+struct SharedStateInner {
+ token_count: usize,
+}
+
+struct SharedState {
+ state_changed: CondVar,
+ inner: Mutex<SharedStateInner>,
+}
+
+impl SharedState {
+ fn try_new() -> KernelResult<Arc<Self>> {
+ let state = Arc::try_new(Self {
+ // SAFETY: `condvar_init!` is called below.
+ state_changed: unsafe { CondVar::new() },
+ // SAFETY: `mutex_init!` is called below.
+ inner: unsafe { Mutex::new(SharedStateInner { token_count: 0 }) },
+ })?;
+ // SAFETY: `state_changed` is pinned behind `Arc`.
+ let state_changed = unsafe { Pin::new_unchecked(&state.state_changed) };
+ kernel::condvar_init!(state_changed, "SharedState::state_changed");
+ // SAFETY: `inner` is pinned behind `Arc`.
+ let inner = unsafe { Pin::new_unchecked(&state.inner) };
+ kernel::mutex_init!(inner, "SharedState::inner");
+ Ok(state)
+ }
+}
+
+struct Token {
+ shared: Arc<SharedState>,
+}
+
+impl FileOpener<Arc<SharedState>> for Token {
+ fn open(shared: &Arc<SharedState>) -> KernelResult<Self::Wrapper> {
+ Ok(Box::try_new(Self {
+ shared: shared.clone(),
+ })?)
+ }
+}
+
+impl FileOperations for Token {
+ type Wrapper = Box<Self>;
+
+ kernel::declare_file_operations!(read, write);
+
+ fn read(&self, _: &File, data: &mut UserSlicePtrWriter, offset: u64) -> KernelResult<usize> {
+ // Succeed if the caller doesn't provide a buffer or if not at the start.
+ if data.is_empty() || offset != 0 {
+ return Ok(0);
+ }
+
+ {
+ let mut inner = self.shared.inner.lock();
+
+ // Wait until we are allowed to decrement the token count or a signal arrives.
+ while inner.token_count == 0 {
+ if self.shared.state_changed.wait(&mut inner) {
+ return Err(Error::EINTR);
+ }
+ }
+
+ // Consume a token.
+ inner.token_count -= 1;
+ }
+
+ // Notify a possible writer waiting.
+ self.shared.state_changed.notify_all();
+
+ // Write a one-byte 1 to the reader.
+ data.write_slice(&[1u8; 1])?;
+ Ok(1)
+ }
+
+ fn write(&self, data: &mut UserSlicePtrReader, _offset: u64) -> KernelResult<usize> {
+ {
+ let mut inner = self.shared.inner.lock();
+
+ // Wait until we are allowed to increment the token count or a signal arrives.
+ while inner.token_count == MAX_TOKENS {
+ if self.shared.state_changed.wait(&mut inner) {
+ return Err(Error::EINTR);
+ }
+ }
+
+ // Increment the number of token so that a reader can be released.
+ inner.token_count += 1;
+ }
+
+ // Notify a possible reader waiting.
+ self.shared.state_changed.notify_all();
+ Ok(data.len())
+ }
+}
+
+struct RustMiscdev {
+ _dev: Pin<Box<miscdev::Registration<Arc<SharedState>>>>,
+}
+
+impl KernelModule for RustMiscdev {
+ fn init() -> KernelResult<Self> {
+ pr_info!("Rust miscellaneous device sample (init)\n");
+
+ let state = SharedState::try_new()?;
+
+ Ok(RustMiscdev {
+ _dev: miscdev::Registration::new_pinned::<Token>(cstr!("rust_miscdev"), None, state)?,
+ })
+ }
+}
+
+impl Drop for RustMiscdev {
+ fn drop(&mut self) {
+ pr_info!("Rust miscellaneous device sample (exit)\n");
+ }
+}
diff --git a/samples/rust/rust_module_parameters.rs b/samples/rust/rust_module_parameters.rs
new file mode 100644
index 000000000000..d9b6de695384
--- /dev/null
+++ b/samples/rust/rust_module_parameters.rs
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Rust module parameters sample
+
+#![no_std]
+#![feature(allocator_api, global_asm)]
+
+use kernel::prelude::*;
+
+module! {
+ type: RustModuleParameters,
+ name: b"rust_module_parameters",
+ author: b"Rust for Linux Contributors",
+ description: b"Rust module parameters sample",
+ license: b"GPL v2",
+ params: {
+ my_bool: bool {
+ default: true,
+ permissions: 0,
+ description: b"Example of bool",
+ },
+ my_i32: i32 {
+ default: 42,
+ permissions: 0o644,
+ description: b"Example of i32",
+ },
+ my_str: str {
+ default: b"default str val",
+ permissions: 0o644,
+ description: b"Example of a string param",
+ },
+ my_usize: usize {
+ default: 42,
+ permissions: 0o644,
+ description: b"Example of usize",
+ },
+ my_array: ArrayParam<i32, 3> {
+ default: [0, 1],
+ permissions: 0,
+ description: b"Example of array",
+ },
+ },
+}
+
+struct RustModuleParameters;
+
+impl KernelModule for RustModuleParameters {
+ fn init() -> KernelResult<Self> {
+ pr_info!("Rust module parameters sample (init)\n");
+
+ {
+ let lock = THIS_MODULE.kernel_param_lock();
+ pr_info!("Parameters:\n");
+ pr_info!(" my_bool: {}\n", my_bool.read());
+ pr_info!(" my_i32: {}\n", my_i32.read(&lock));
+ pr_info!(
+ " my_str: {}\n",
+ core::str::from_utf8(my_str.read(&lock))?
+ );
+ pr_info!(" my_usize: {}\n", my_usize.read(&lock));
+ pr_info!(" my_array: {:?}\n", my_array.read());
+ }
+
+ Ok(RustModuleParameters)
+ }
+}
+
+impl Drop for RustModuleParameters {
+ fn drop(&mut self) {
+ pr_info!("Rust module parameters sample (exit)\n");
+ }
+}
diff --git a/samples/rust/rust_print.rs b/samples/rust/rust_print.rs
new file mode 100644
index 000000000000..ddfac800f425
--- /dev/null
+++ b/samples/rust/rust_print.rs
@@ -0,0 +1,58 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Rust printing macros sample
+
+#![no_std]
+#![feature(allocator_api, global_asm)]
+
+use kernel::prelude::*;
+
+module! {
+ type: RustPrint,
+ name: b"rust_print",
+ author: b"Rust for Linux Contributors",
+ description: b"Rust printing macros sample",
+ license: b"GPL v2",
+ params: {
+ },
+}
+
+struct RustPrint;
+
+impl KernelModule for RustPrint {
+ fn init() -> KernelResult<Self> {
+ pr_info!("Rust printing macros sample (init)\n");
+
+ pr_emerg!("Emergency message (level 0) without args\n");
+ pr_alert!("Alert message (level 1) without args\n");
+ pr_crit!("Critical message (level 2) without args\n");
+ pr_err!("Error message (level 3) without args\n");
+ pr_warn!("Warning message (level 4) without args\n");
+ pr_notice!("Notice message (level 5) without args\n");
+ pr_info!("Info message (level 6) without args\n");
+
+ pr_info!("A line that");
+ pr_cont!(" is continued");
+ pr_cont!(" without args\n");
+
+ pr_emerg!("{} message (level {}) with args\n", "Emergency", 0);
+ pr_alert!("{} message (level {}) with args\n", "Alert", 1);
+ pr_crit!("{} message (level {}) with args\n", "Critical", 2);
+ pr_err!("{} message (level {}) with args\n", "Error", 3);
+ pr_warn!("{} message (level {}) with args\n", "Warning", 4);
+ pr_notice!("{} message (level {}) with args\n", "Notice", 5);
+ pr_info!("{} message (level {}) with args\n", "Info", 6);
+
+ pr_info!("A {} that", "line");
+ pr_cont!(" is {}", "continued");
+ pr_cont!(" with {}\n", "args");
+
+ Ok(RustPrint)
+ }
+}
+
+impl Drop for RustPrint {
+ fn drop(&mut self) {
+ pr_info!("Rust printing macros sample (exit)\n");
+ }
+}
diff --git a/samples/rust/rust_semaphore.rs b/samples/rust/rust_semaphore.rs
new file mode 100644
index 000000000000..b7300d2aab6d
--- /dev/null
+++ b/samples/rust/rust_semaphore.rs
@@ -0,0 +1,178 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Rust semaphore sample
+//!
+//! A counting semaphore that can be used by userspace.
+//!
+//! The count is incremented by writes to the device. A write of `n` bytes results in an increment
+//! of `n`. It is decremented by reads; each read results in the count being decremented by 1. If
+//! the count is already zero, a read will block until another write increments it.
+//!
+//! This can be used in user space from the shell for example as follows (assuming a node called
+//! `semaphore`): `cat semaphore` decrements the count by 1 (waiting for it to become non-zero
+//! before decrementing); `echo -n 123 > semaphore` increments the semaphore by 3, potentially
+//! unblocking up to 3 blocked readers.
+
+#![no_std]
+#![feature(allocator_api, global_asm)]
+
+use alloc::{boxed::Box, sync::Arc};
+use core::{
+ pin::Pin,
+ sync::atomic::{AtomicU64, Ordering},
+};
+use kernel::{
+ condvar_init, cstr, declare_file_operations,
+ file_operations::{File, FileOpener, FileOperations, IoctlCommand, IoctlHandler},
+ miscdev::Registration,
+ mutex_init,
+ prelude::*,
+ sync::{CondVar, Mutex},
+ user_ptr::{UserSlicePtrReader, UserSlicePtrWriter},
+ Error,
+};
+
+module! {
+ type: RustSemaphore,
+ name: b"rust_semaphore",
+ author: b"Rust for Linux Contributors",
+ description: b"Rust semaphore sample",
+ license: b"GPL v2",
+ params: {},
+}
+
+struct SemaphoreInner {
+ count: usize,
+ max_seen: usize,
+}
+
+struct Semaphore {
+ changed: CondVar,
+ inner: Mutex<SemaphoreInner>,
+}
+
+struct FileState {
+ read_count: AtomicU64,
+ shared: Arc<Semaphore>,
+}
+
+impl FileState {
+ fn consume(&self) -> KernelResult {
+ let mut inner = self.shared.inner.lock();
+ while inner.count == 0 {
+ if self.shared.changed.wait(&mut inner) {
+ return Err(Error::EINTR);
+ }
+ }
+ inner.count -= 1;
+ Ok(())
+ }
+}
+
+impl FileOpener<Arc<Semaphore>> for FileState {
+ fn open(shared: &Arc<Semaphore>) -> KernelResult<Box<Self>> {
+ Ok(Box::try_new(Self {
+ read_count: AtomicU64::new(0),
+ shared: shared.clone(),
+ })?)
+ }
+}
+
+impl FileOperations for FileState {
+ type Wrapper = Box<Self>;
+
+ declare_file_operations!(read, write, ioctl);
+
+ fn read(&self, _: &File, data: &mut UserSlicePtrWriter, offset: u64) -> KernelResult<usize> {
+ if data.is_empty() || offset > 0 {
+ return Ok(0);
+ }
+ self.consume()?;
+ data.write_slice(&[0u8; 1])?;
+ self.read_count.fetch_add(1, Ordering::Relaxed);
+ Ok(1)
+ }
+
+ fn write(&self, data: &mut UserSlicePtrReader, _offset: u64) -> KernelResult<usize> {
+ {
+ let mut inner = self.shared.inner.lock();
+ inner.count = inner.count.saturating_add(data.len());
+ if inner.count > inner.max_seen {
+ inner.max_seen = inner.count;
+ }
+ }
+
+ self.shared.changed.notify_all();
+ Ok(data.len())
+ }
+
+ fn ioctl(&self, file: &File, cmd: &mut IoctlCommand) -> KernelResult<i32> {
+ cmd.dispatch(self, file)
+ }
+
+ fn release(_obj: Box<Self>, _file: &File) {}
+}
+
+struct RustSemaphore {
+ _dev: Pin<Box<Registration<Arc<Semaphore>>>>,
+}
+
+impl KernelModule for RustSemaphore {
+ fn init() -> KernelResult<Self> {
+ pr_info!("Rust semaphore sample (init)\n");
+
+ let sema = Arc::try_new(Semaphore {
+ // SAFETY: `condvar_init!` is called below.
+ changed: unsafe { CondVar::new() },
+
+ // SAFETY: `mutex_init!` is called below.
+ inner: unsafe {
+ Mutex::new(SemaphoreInner {
+ count: 0,
+ max_seen: 0,
+ })
+ },
+ })?;
+
+ // SAFETY: `changed` is pinned behind `Arc`.
+ condvar_init!(Pin::new_unchecked(&sema.changed), "Semaphore::changed");
+
+ // SAFETY: `inner` is pinned behind `Arc`.
+ mutex_init!(Pin::new_unchecked(&sema.inner), "Semaphore::inner");
+
+ Ok(Self {
+ _dev: Registration::new_pinned::<FileState>(cstr!("rust_semaphore"), None, sema)?,
+ })
+ }
+}
+
+impl Drop for RustSemaphore {
+ fn drop(&mut self) {
+ pr_info!("Rust semaphore sample (exit)\n");
+ }
+}
+
+const IOCTL_GET_READ_COUNT: u32 = 0x80086301;
+const IOCTL_SET_READ_COUNT: u32 = 0x40086301;
+
+impl IoctlHandler for FileState {
+ fn read(&self, _: &File, cmd: u32, writer: &mut UserSlicePtrWriter) -> KernelResult<i32> {
+ match cmd {
+ IOCTL_GET_READ_COUNT => {
+ writer.write(&self.read_count.load(Ordering::Relaxed))?;
+ Ok(0)
+ }
+ _ => Err(Error::EINVAL),
+ }
+ }
+
+ fn write(&self, _: &File, cmd: u32, reader: &mut UserSlicePtrReader) -> KernelResult<i32> {
+ match cmd {
+ IOCTL_SET_READ_COUNT => {
+ self.read_count.store(reader.read()?, Ordering::Relaxed);
+ Ok(0)
+ }
+ _ => Err(Error::EINVAL),
+ }
+ }
+}
diff --git a/samples/rust/rust_semaphore_c.c b/samples/rust/rust_semaphore_c.c
new file mode 100644
index 000000000000..cdc121d4030d
--- /dev/null
+++ b/samples/rust/rust_semaphore_c.c
@@ -0,0 +1,212 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Rust semaphore sample (in C, for comparison)
+ *
+ * This is a C implementation of `rust_semaphore.rs`. Refer to the description
+ * in that file for details on the device.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/refcount.h>
+#include <linux/wait.h>
+
+#define IOCTL_GET_READ_COUNT _IOR('c', 1, u64)
+#define IOCTL_SET_READ_COUNT _IOW('c', 1, u64)
+
+struct semaphore_state {
+ struct kref ref;
+ struct miscdevice miscdev;
+ wait_queue_head_t changed;
+ struct mutex mutex;
+ size_t count;
+ size_t max_seen;
+};
+
+struct file_state {
+ atomic64_t read_count;
+ struct semaphore_state *shared;
+};
+
+static int semaphore_consume(struct semaphore_state *state)
+{
+ DEFINE_WAIT(wait);
+
+ mutex_lock(&state->mutex);
+ while (state->count == 0) {
+ prepare_to_wait(&state->changed, &wait, TASK_INTERRUPTIBLE);
+ mutex_unlock(&state->mutex);
+ schedule();
+ finish_wait(&state->changed, &wait);
+ if (signal_pending(current))
+ return -EINTR;
+ mutex_lock(&state->mutex);
+ }
+
+ state->count--;
+ mutex_unlock(&state->mutex);
+
+ return 0;
+}
+
+static int semaphore_open(struct inode *nodp, struct file *filp)
+{
+ struct semaphore_state *shared =
+ container_of(filp->private_data, struct semaphore_state, miscdev);
+ struct file_state *state;
+
+ state = kzalloc(sizeof(*state), GFP_KERNEL);
+ if (!state)
+ return -ENOMEM;
+
+ kref_get(&shared->ref);
+ state->shared = shared;
+ atomic64_set(&state->read_count, 0);
+
+ filp->private_data = state;
+
+ return 0;
+}
+
+static ssize_t semaphore_write(struct file *filp, const char __user *buffer, size_t count,
+ loff_t *ppos)
+{
+ struct file_state *state = filp->private_data;
+ struct semaphore_state *shared = state->shared;
+
+ mutex_lock(&shared->mutex);
+
+ shared->count += count;
+ if (shared->count < count)
+ shared->count = SIZE_MAX;
+
+ if (shared->count > shared->max_seen)
+ shared->max_seen = shared->count;
+
+ mutex_unlock(&shared->mutex);
+
+ wake_up_all(&shared->changed);
+
+ return count;
+}
+
+static ssize_t semaphore_read(struct file *filp, char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ struct file_state *state = filp->private_data;
+ char c = 0;
+ int ret;
+
+ if (count == 0 || *ppos > 0)
+ return 0;
+
+ ret = semaphore_consume(state->shared);
+ if (ret)
+ return ret;
+
+ if (copy_to_user(buffer, &c, sizeof(c)))
+ return -EFAULT;
+
+ atomic64_add(1, &state->read_count);
+ *ppos += 1;
+ return 1;
+}
+
+static long semaphore_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ struct file_state *state = filp->private_data;
+ void __user *buffer = (void __user *)arg;
+ u64 value;
+
+ switch (cmd) {
+ case IOCTL_GET_READ_COUNT:
+ value = atomic64_read(&state->read_count);
+ if (copy_to_user(buffer, &value, sizeof(value)))
+ return -EFAULT;
+ return 0;
+ case IOCTL_SET_READ_COUNT:
+ if (copy_from_user(&value, buffer, sizeof(value)))
+ return -EFAULT;
+ atomic64_set(&state->read_count, value);
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static void semaphore_free(struct kref *kref)
+{
+ struct semaphore_state *device;
+
+ device = container_of(kref, struct semaphore_state, ref);
+ kfree(device);
+}
+
+static int semaphore_release(struct inode *nodp, struct file *filp)
+{
+ struct file_state *state = filp->private_data;
+
+ kref_put(&state->shared->ref, semaphore_free);
+ kfree(state);
+ return 0;
+}
+
+static const struct file_operations semaphore_fops = {
+ .owner = THIS_MODULE,
+ .open = semaphore_open,
+ .read = semaphore_read,
+ .write = semaphore_write,
+ .compat_ioctl = semaphore_ioctl,
+ .release = semaphore_release,
+};
+
+static struct semaphore_state *device;
+
+static int __init semaphore_init(void)
+{
+ int ret;
+ struct semaphore_state *state;
+
+ pr_info("Rust semaphore sample (in C, for comparison) (init)\n");
+
+ state = kzalloc(sizeof(*state), GFP_KERNEL);
+ if (!state)
+ return -ENOMEM;
+
+ mutex_init(&state->mutex);
+ kref_init(&state->ref);
+ init_waitqueue_head(&state->changed);
+
+ state->miscdev.fops = &semaphore_fops;
+ state->miscdev.minor = MISC_DYNAMIC_MINOR;
+ state->miscdev.name = "semaphore";
+
+ ret = misc_register(&state->miscdev);
+ if (ret < 0) {
+ kfree(state);
+ return ret;
+ }
+
+ device = state;
+
+ return 0;
+}
+
+static void __exit semaphore_exit(void)
+{
+ pr_info("Rust semaphore sample (in C, for comparison) (exit)\n");
+
+ misc_deregister(&device->miscdev);
+ kref_put(&device->ref, semaphore_free);
+}
+
+module_init(semaphore_init);
+module_exit(semaphore_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Rust for Linux Contributors");
+MODULE_DESCRIPTION("Rust semaphore sample (in C, for comparison)");
diff --git a/samples/rust/rust_stack_probing.rs b/samples/rust/rust_stack_probing.rs
new file mode 100644
index 000000000000..f99277354565
--- /dev/null
+++ b/samples/rust/rust_stack_probing.rs
@@ -0,0 +1,42 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Rust stack probing sample
+
+#![no_std]
+#![feature(allocator_api, global_asm)]
+#![feature(test)]
+
+use kernel::prelude::*;
+
+module! {
+ type: RustStackProbing,
+ name: b"rust_stack_probing",
+ author: b"Rust for Linux Contributors",
+ description: b"Rust stack probing sample",
+ license: b"GPL v2",
+ params: {
+ },
+}
+
+struct RustStackProbing;
+
+impl KernelModule for RustStackProbing {
+ fn init() -> KernelResult<Self> {
+ pr_info!("Rust stack probing sample (init)\n");
+
+ // Including this large variable on the stack will trigger
+ // stack probing on the supported archs.
+ // This will verify that stack probing does not lead to
+ // any errors if we need to link `__rust_probestack`.
+ let x: [u64; 514] = core::hint::black_box([5; 514]);
+ pr_info!("Large array has length: {}\n", x.len());
+
+ Ok(RustStackProbing)
+ }
+}
+
+impl Drop for RustStackProbing {
+ fn drop(&mut self) {
+ pr_info!("Rust stack probing sample (exit)\n");
+ }
+}
diff --git a/samples/rust/rust_sync.rs b/samples/rust/rust_sync.rs
new file mode 100644
index 000000000000..a921bfd7d55f
--- /dev/null
+++ b/samples/rust/rust_sync.rs
@@ -0,0 +1,84 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Rust synchronisation primitives sample
+
+#![no_std]
+#![feature(allocator_api, global_asm)]
+
+use alloc::boxed::Box;
+use core::pin::Pin;
+use kernel::prelude::*;
+use kernel::{
+ condvar_init, mutex_init, spinlock_init,
+ sync::{CondVar, Mutex, SpinLock},
+};
+
+module! {
+ type: RustSync,
+ name: b"rust_sync",
+ author: b"Rust for Linux Contributors",
+ description: b"Rust synchronisation primitives sample",
+ license: b"GPL v2",
+ params: {
+ },
+}
+
+struct RustSync;
+
+impl KernelModule for RustSync {
+ fn init() -> KernelResult<Self> {
+ pr_info!("Rust synchronisation primitives sample (init)\n");
+
+ // Test mutexes.
+ {
+ // SAFETY: `init` is called below.
+ let data = Pin::from(Box::try_new(unsafe { Mutex::new(0) })?);
+ mutex_init!(data.as_ref(), "RustSync::init::data1");
+ *data.lock() = 10;
+ pr_info!("Value: {}\n", *data.lock());
+
+ // SAFETY: `init` is called below.
+ let cv = Pin::from(Box::try_new(unsafe { CondVar::new() })?);
+ condvar_init!(cv.as_ref(), "RustSync::init::cv1");
+ {
+ let mut guard = data.lock();
+ while *guard != 10 {
+ let _ = cv.wait(&mut guard);
+ }
+ }
+ cv.notify_one();
+ cv.notify_all();
+ cv.free_waiters();
+ }
+
+ // Test spinlocks.
+ {
+ // SAFETY: `init` is called below.
+ let data = Pin::from(Box::try_new(unsafe { SpinLock::new(0) })?);
+ spinlock_init!(data.as_ref(), "RustSync::init::data2");
+ *data.lock() = 10;
+ pr_info!("Value: {}\n", *data.lock());
+
+ // SAFETY: `init` is called below.
+ let cv = Pin::from(Box::try_new(unsafe { CondVar::new() })?);
+ condvar_init!(cv.as_ref(), "RustSync::init::cv2");
+ {
+ let mut guard = data.lock();
+ while *guard != 10 {
+ let _ = cv.wait(&mut guard);
+ }
+ }
+ cv.notify_one();
+ cv.notify_all();
+ cv.free_waiters();
+ }
+
+ Ok(RustSync)
+ }
+}
+
+impl Drop for RustSync {
+ fn drop(&mut self) {
+ pr_info!("Rust synchronisation primitives sample (exit)\n");
+ }
+}
--
2.17.1
From: Miguel Ojeda <[email protected]>
Miguel, Alex and Wedson will be maintaining the Rust support.
Co-developed-by: Alex Gaynor <[email protected]>
Signed-off-by: Alex Gaynor <[email protected]>
Co-developed-by: Wedson Almeida Filho <[email protected]>
Signed-off-by: Wedson Almeida Filho <[email protected]>
Signed-off-by: Miguel Ojeda <[email protected]>
---
MAINTAINERS | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 9e876927c60d..de32aaa5cabd 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -15547,6 +15547,20 @@ L: [email protected]
S: Maintained
F: drivers/infiniband/ulp/rtrs/
+RUST
+M: Miguel Ojeda <[email protected]>
+M: Alex Gaynor <[email protected]>
+M: Wedson Almeida Filho <[email protected]>
+L: [email protected]
+S: Maintained
+W: https://github.com/Rust-for-Linux/linux
+B: https://github.com/Rust-for-Linux/linux/issues
+T: git https://github.com/Rust-for-Linux/linux.git rust-next
+F: rust/
+F: samples/rust/
+F: Documentation/rust/
+K: \b(?i:rust)\b
+
RXRPC SOCKETS (AF_RXRPC)
M: David Howells <[email protected]>
L: [email protected]
--
2.17.1
From: Miguel Ojeda <[email protected]>
Most of the documentation for Rust is written within the source code
itself, as it is idiomatic for Rust projects. This applies to both
the shared infrastructure at `rust/` as well as any other Rust module
(e.g. drivers) written across the kernel.
These documents are general information that do not fit particularly
well in the source code, like the quick start guide.
Co-developed-by: Alex Gaynor <[email protected]>
Signed-off-by: Alex Gaynor <[email protected]>
Co-developed-by: Geoffrey Thomas <[email protected]>
Signed-off-by: Geoffrey Thomas <[email protected]>
Co-developed-by: Finn Behrens <[email protected]>
Signed-off-by: Finn Behrens <[email protected]>
Co-developed-by: Adam Bratschi-Kaye <[email protected]>
Signed-off-by: Adam Bratschi-Kaye <[email protected]>
Co-developed-by: Wedson Almeida Filho <[email protected]>
Signed-off-by: Wedson Almeida Filho <[email protected]>
Signed-off-by: Miguel Ojeda <[email protected]>
---
Documentation/doc-guide/kernel-doc.rst | 3 +
Documentation/index.rst | 1 +
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 +++++++++++++++++++++++++
7 files changed, 457 insertions(+)
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
diff --git a/Documentation/doc-guide/kernel-doc.rst b/Documentation/doc-guide/kernel-doc.rst
index 79aaa55d6bcf..c655fdb9c042 100644
--- a/Documentation/doc-guide/kernel-doc.rst
+++ b/Documentation/doc-guide/kernel-doc.rst
@@ -11,6 +11,9 @@ when it is embedded in source files.
reasons. The kernel source contains tens of thousands of kernel-doc
comments. Please stick to the style described here.
+.. note:: kernel-doc does not cover Rust code: please see
+ :ref:`Documentation/rust/docs.rst <rust_docs>` instead.
+
The kernel-doc structure is extracted from the comments, and proper
`Sphinx C Domain`_ function and type descriptions with anchors are
generated from them. The descriptions are filtered for special kernel-doc
diff --git a/Documentation/index.rst b/Documentation/index.rst
index 31f2adc8542d..3e7c43a48e68 100644
--- a/Documentation/index.rst
+++ b/Documentation/index.rst
@@ -82,6 +82,7 @@ merged much easier.
maintainer/index
fault-injection/index
livepatch/index
+ rust/index
Kernel API documentation
diff --git a/Documentation/rust/arch-support.rst b/Documentation/rust/arch-support.rst
new file mode 100644
index 000000000000..d72ab2f8fa46
--- /dev/null
+++ b/Documentation/rust/arch-support.rst
@@ -0,0 +1,29 @@
+.. _rust_arch_support:
+
+Arch Support
+============
+
+Currently, the Rust compiler (``rustc``) uses LLVM for code generation,
+which limits the supported architectures we can target. In addition, support
+for building the kernel with LLVM/Clang varies (see :ref:`kbuild_llvm`),
+which ``bindgen`` relies on through ``libclang``.
+
+Below is a general summary of architectures that currently work. Level of
+support corresponds to ``S`` values in the ``MAINTAINERS`` file.
+
+.. list-table::
+ :widths: 10 10 10
+ :header-rows: 1
+
+ * - Architecture
+ - Level of support
+ - Constraints
+ * - ``arm64``
+ - Maintained
+ - None
+ * - ``powerpc``
+ - Maintained
+ - ``ppc64le`` only, ``RUST_OPT_LEVEL >= 2``
+ * - ``x86``
+ - Maintained
+ - ``x86_64`` only
diff --git a/Documentation/rust/coding.rst b/Documentation/rust/coding.rst
new file mode 100644
index 000000000000..46da5fb0974c
--- /dev/null
+++ b/Documentation/rust/coding.rst
@@ -0,0 +1,92 @@
+.. _rust_coding:
+
+Coding
+======
+
+This document describes how to write Rust code in the kernel.
+
+
+Coding style
+------------
+
+The code is automatically formatted using the ``rustfmt`` tool. This is very
+good news!
+
+- If you contribute from time to time to the kernel, you do not need to learn
+ and remember one more style guide. You will also need less patch roundtrips
+ to land a change.
+
+- If you are a reviewer or a maintainer, you will not need to spend time on
+ pointing out style issues anymore.
+
+.. note:: Conventions on comments and documentation are not checked by
+ ``rustfmt``. Thus we still need to take care of those: please see
+ :ref:`Documentation/rust/docs.rst <rust_docs>`.
+
+We use the tool's default settings. This means we are following the idiomatic
+Rust style. For instance, we use 4 spaces for indentation rather than tabs.
+
+Typically, you will want to instruct your editor/IDE to format while you type,
+when you save or at commit time. However, if for some reason you want
+to reformat the entire kernel Rust sources at some point, you may run::
+
+ make rustfmt
+
+To check if everything is formatted (printing a diff otherwise), e.g. if you
+have configured a CI for a tree as a maintainer, you may run::
+
+ make rustfmtcheck
+
+Like ``clang-format`` for the rest of the kernel, ``rustfmt`` works on
+individual files, and does not require a kernel configuration. Sometimes it may
+even work with broken code.
+
+
+Extra lints
+-----------
+
+While ``rustc`` is a very helpful compiler, some extra lints and analysis are
+available via ``clippy``, a Rust linter. To enable it, pass ``CLIPPY=1`` to
+the same invocation you use for compilation, e.g.::
+
+ make ARCH=... CROSS_COMPILE=... CC=... -j... CLIPPY=1
+
+At the moment, we do not enforce a "clippy-free" compilation, so you can treat
+the output the same way as the extra warning levels for C, e.g. like ``W=2``.
+Still, we use the default configuration, which is relatively conservative, so
+it is a good idea to read any output it may produce from time to time and fix
+the pointed out issues. The list of enabled lists will be likely tweaked over
+time, and extra levels may end up being introduced, e.g. ``CLIPPY=2``.
+
+
+Abstractions vs. bindings
+-------------------------
+
+We don't have abstractions for all the kernel internal APIs and concepts,
+but we would like to expand coverage as time goes on. Unless there is
+a good reason not to, always use the abstractions instead of calling
+the C bindings directly.
+
+If you are writing some code that requires a call to an internal kernel API
+or concept that isn't abstracted yet, consider providing an (ideally safe)
+abstraction for everyone to use.
+
+
+Conditional compilation
+-----------------------
+
+Rust code has access to conditional compilation based on the kernel
+configuration:
+
+.. code-block:: rust
+
+ #[cfg(CONFIG_X)] // `CONFIG_X` is enabled (`y` or `m`)
+ #[cfg(CONFIG_X="y")] // `CONFIG_X` is enabled as a built-in (`y`)
+ #[cfg(CONFIG_X="m")] // `CONFIG_X` is enabled as a module (`m`)
+ #[cfg(not(CONFIG_X))] // `CONFIG_X` is disabled
+
+
+Documentation
+-------------
+
+Please see :ref:`Documentation/rust/docs.rst <rust_docs>`.
diff --git a/Documentation/rust/docs.rst b/Documentation/rust/docs.rst
new file mode 100644
index 000000000000..58c5f98ccb35
--- /dev/null
+++ b/Documentation/rust/docs.rst
@@ -0,0 +1,109 @@
+.. _rust_docs:
+
+Docs
+====
+
+Rust kernel code is not documented like C kernel code (i.e. via kernel-doc).
+Instead, we use the usual system for documenting Rust code: the ``rustdoc``
+tool, which uses Markdown (a *very* lightweight markup language).
+
+This document describes how to make the most out of the kernel documentation
+for Rust.
+
+
+Reading the docs
+----------------
+
+An advantage of using Markdown is that it attempts to make text look almost as
+you would have written it in plain text. This makes the documentation quite
+pleasant to read even in its source form.
+
+However, the generated HTML docs produced by ``rustdoc`` provide a *very* nice
+experience, including integrated instant search, clickable items (types,
+functions, constants, etc. -- including to all the standard Rust library ones
+that we use in the kernel, e.g. ``core``), categorization, links to the source
+code, etc.
+
+Like for the rest of the kernel documentation, pregenerated HTML docs for
+the libraries (crates) inside ``rust/`` that are used by the rest of the kernel
+are available at `kernel.org`_.
+
+// TODO: link when ready
+
+.. _kernel.org: http://kernel.org/
+
+Otherwise, you can generate them locally. This is quite fast (same order as
+compiling the code itself) and you do not need any special tools or environment.
+This has the added advantage that they will be tailored to your particular
+kernel configuration. To generate them, simply use the ``rustdoc`` target with
+the same invocation you use for compilation, e.g.::
+
+ make ARCH=... CROSS_COMPILE=... CC=... -j... rustdoc
+
+
+Writing the docs
+----------------
+
+If you already know Markdown, learning how to write Rust documentation will be
+a breeze. If not, understanding the basics is a matter of minutes reading other
+code. There are also many guides available out there, a particularly nice one
+is at `GitHub`_.
+
+.. _GitHub: https://guides.github.com/features/mastering-markdown/#syntax
+
+This is how a well-documented Rust function may look like (derived from the Rust
+standard library)::
+
+ /// Returns the contained [`Some`] value, consuming the `self` value,
+ /// without checking that the value is not [`None`].
+ ///
+ /// # Safety
+ ///
+ /// Calling this method on [`None`] is *[undefined behavior]*.
+ ///
+ /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// let x = Some("air");
+ /// assert_eq!(unsafe { x.unwrap_unchecked() }, "air");
+ /// ```
+ pub unsafe fn unwrap_unchecked(self) -> T {
+ match self {
+ Some(val) => val,
+
+ // SAFETY: the safety contract must be upheld by the caller.
+ None => unsafe { hint::unreachable_unchecked() },
+ }
+ }
+
+This example showcases a few ``rustdoc`` features and some common conventions
+(that we also follow in the kernel):
+
+* The first paragraph must be a single sentence briefly describing what
+ the documented item does. Further explanations must go in extra paragraphs.
+
+* ``unsafe`` functions must document the preconditions needed for a call to be
+ safe under a ``Safety`` section.
+
+* While not shown here, if a function may panic, the conditions under which
+ that happens must be described under a ``Panics`` section.
+
+* If providing examples of usage would help readers, they must be written in
+ a section called ``Examples``.
+
+* Rust items (functions, types, constants...) will be automatically linked
+ (``rustdoc`` will find out the URL for you).
+
+* Following the Rust standard library conventions, any ``unsafe`` block must be
+ preceded by a ``SAFETY`` comment describing why the code inside is sound.
+
+ While sometimes the reason might look trivial and therefore unneeded, writing
+ these comments is not just a good way of documenting what has been taken into
+ account, but also that there are no *extra* implicit constraints.
+
+To learn more about how to write documentation for Rust and extra features,
+please take a look at the ``rustdoc`` `book`_.
+
+.. _book: https://doc.rust-lang.org/rustdoc/how-to-write-documentation.html
diff --git a/Documentation/rust/index.rst b/Documentation/rust/index.rst
new file mode 100644
index 000000000000..257cf2b200b8
--- /dev/null
+++ b/Documentation/rust/index.rst
@@ -0,0 +1,20 @@
+Rust
+====
+
+Documentation related to Rust within the kernel. If you are starting out,
+read the :ref:`Documentation/rust/quick-start.rst <rust_quick_start>` guide.
+
+.. toctree::
+ :maxdepth: 1
+
+ quick-start
+ coding
+ docs
+ arch-support
+
+.. only:: subproject and html
+
+ Indices
+ =======
+
+ * :ref:`genindex`
diff --git a/Documentation/rust/quick-start.rst b/Documentation/rust/quick-start.rst
new file mode 100644
index 000000000000..42367e4365c3
--- /dev/null
+++ b/Documentation/rust/quick-start.rst
@@ -0,0 +1,203 @@
+.. _rust_quick_start:
+
+Quick Start
+===========
+
+This document describes how to get started with kernel development in Rust.
+If you have worked previously with Rust, this will only take a moment.
+
+Please note that, at the moment, a very restricted subset of architectures
+is supported, see :doc:`/rust/arch-support`.
+
+
+Requirements: Building
+----------------------
+
+This section explains how to fetch the tools needed for building.
+
+Some of these requirements might be available from your Linux distribution
+under names like ``rustc``, ``rust-src``, ``rust-bindgen``, etc. However,
+at the time of writing, they are likely to not be recent enough.
+
+
+rustc
+*****
+
+A recent *nightly* Rust toolchain (with, at least, ``rustc``) is required,
+e.g. ``nightly-2021-02-20``. Our goal is to use a stable toolchain as soon
+as possible, but for the moment we depend on a handful of nightly features.
+
+If you are using ``rustup``, run::
+
+ rustup default nightly-2021-02-20
+
+Please avoid the very latest nightlies (>= nightly-2021-03-05) until
+https://github.com/Rust-for-Linux/linux/issues/135 is resolved.
+
+Otherwise, fetch a standalone installer or install ``rustup`` from:
+
+ https://www.rust-lang.org
+
+
+Rust standard library source
+****************************
+
+The Rust standard library source is required because the build system will
+cross-compile ``core`` and ``alloc``.
+
+If you are using ``rustup``, run::
+
+ rustup component add rust-src
+
+Otherwise, if you used a standalone installer, you can clone the Rust
+repository into the installation folder of your nightly toolchain::
+
+ git clone --recurse-submodules https://github.com/rust-lang/rust $(rustc --print sysroot)/lib/rustlib/src/rust
+
+
+libclang
+********
+
+``libclang`` (part of LLVM) is used by ``bindgen`` to understand the C code
+in the kernel, which means you will need a recent LLVM installed; like when
+you compile the kernel with ``CC=clang`` or ``LLVM=1``.
+
+Your Linux distribution is likely to have a suitable one available, so it is
+best if you check that first.
+
+There are also some binaries for several systems and architectures uploaded at:
+
+ https://releases.llvm.org/download.html
+
+For Debian-based distributions, you can also fetch them from:
+
+ https://apt.llvm.org
+
+Otherwise, building LLVM takes quite a while, but it is not a complex process:
+
+ https://llvm.org/docs/GettingStarted.html#getting-the-source-code-and-building-llvm
+
+
+bindgen
+*******
+
+The bindings to the C side of the kernel are generated at build time using
+the ``bindgen`` tool. A recent version should work, e.g. ``0.56.0``.
+
+Install it via (this will build the tool from source)::
+
+ cargo install --locked --version 0.56.0 bindgen
+
+
+Requirements: Developing
+------------------------
+
+This section explains how to fetch the tools needed for developing. That is,
+if you only want to build the kernel, you do not need them.
+
+
+rustfmt
+*******
+
+The ``rustfmt`` tool is used to automatically format all the Rust kernel code,
+including the generated C bindings (for details, please see
+:ref:`Documentation/rust/coding.rst <rust_coding>`).
+
+If you are using ``rustup``, its ``default`` profile already installs the tool,
+so you should be good to go. If you are using another profile, you can install
+the component manually::
+
+ rustup component add rustfmt
+
+The standalone installers also come with ``rustfmt``.
+
+
+clippy
+******
+
+``clippy`` is a Rust linter. Installing it allows you to get extra warnings
+for Rust code passing ``CLIPPY=1`` to ``make`` (for details, please see
+:ref:`Documentation/rust/coding.rst <rust_coding>`).
+
+If you are using ``rustup``, its ``default`` profile already installs the tool,
+so you should be good to go. If you are using another profile, you can install
+the component manually::
+
+ rustup component add clippy
+
+The standalone installers also come with ``clippy``.
+
+
+rustdoc
+*******
+
+If you install the ``rustdoc`` tool, then you will be able to generate pretty
+HTML documentation for Rust code, including for the libraries (crates) inside
+``rust/`` that are used by the rest of the kernel (for details, please see
+:ref:`Documentation/rust/docs.rst <rust_docs>`).
+
+If you are using ``rustup``, its ``default`` profile already installs the tool,
+so you should be good to go. If you are using another profile, you can install
+the component manually::
+
+ rustup component add rustdoc
+
+The standalone installers also come with ``rustdoc``.
+
+
+Configuration
+-------------
+
+``Rust support`` (``CONFIG_RUST``) needs to be enabled in the ``General setup``
+menu. The option is only shown if the build system can locate ``rustc``.
+In turn, this will make visible the rest of options that depend on Rust.
+
+Afterwards, go to::
+
+ Kernel hacking
+ -> Sample kernel code
+ -> Rust samples
+
+And enable some sample modules either as built-in or as loadable.
+
+
+Building
+--------
+
+Building a kernel with Clang or a complete LLVM toolchain is the best supported
+setup at the moment. That is::
+
+ make ARCH=... CROSS_COMPILE=... CC=clang -j...
+
+or::
+
+ make ARCH=... CROSS_COMPILE=... LLVM=1 -j...
+
+Using GCC also works for some configurations, but it is *very* experimental at
+the moment.
+
+
+Hacking
+-------
+
+If you want to dive deeper, take a look at the source code of the samples
+at ``samples/rust/``, the Rust support code under ``rust/`` and
+the ``Rust hacking`` menu under ``Kernel hacking``.
+
+If you use GDB/Binutils and Rust symbols aren't getting demangled, the reason
+is your toolchain doesn't support Rust's new v0 mangling scheme yet. There are
+a few ways out:
+
+ - If you don't mind building your own tools, we provide the following fork
+ with the support cherry-picked from GCC on top of very recent releases:
+
+ https://github.com/Rust-for-Linux/binutils-gdb/releases/tag/gdb-10.1-release-rust
+ https://github.com/Rust-for-Linux/binutils-gdb/releases/tag/binutils-2_35_1-rust
+
+ - If you only need GDB and can enable ``CONFIG_DEBUG_INFO``, do so:
+ some versions of GDB (e.g. vanilla GDB 10.1) are able to use
+ the pre-demangled names embedded in the debug info.
+
+ - If you don't need loadable module support, you may compile without
+ the ``-Zsymbol-mangling-version=v0`` flag. However, we don't maintain
+ support for that, so avoid it unless you are in a hurry.
--
2.17.1
From: Miguel Ojeda <[email protected]>
The `kernel` crate currently includes all the abstractions that wrap
kernel features written in C.
These abstractions call the C side of the kernel via the generated
bindings with the `bindgen` tool. Modules developed in Rust should
never call the bindings themselves.
In the future, as the abstractions grow in number, we may need
to split this crate into several, possibly following a similar
subdivision in subsystems as the kernel itself.
For compiling the `kernel` crate, we also need `alloc` from Rust's
standard library. We also need a few new helpers.
Co-developed-by: Alex Gaynor <[email protected]>
Signed-off-by: Alex Gaynor <[email protected]>
Co-developed-by: Geoffrey Thomas <[email protected]>
Signed-off-by: Geoffrey Thomas <[email protected]>
Co-developed-by: Finn Behrens <[email protected]>
Signed-off-by: Finn Behrens <[email protected]>
Co-developed-by: Adam Bratschi-Kaye <[email protected]>
Signed-off-by: Adam Bratschi-Kaye <[email protected]>
Co-developed-by: Wedson Almeida Filho <[email protected]>
Signed-off-by: Wedson Almeida Filho <[email protected]>
Co-developed-by: Michael Ellerman <[email protected]>
Signed-off-by: Michael Ellerman <[email protected]>
Signed-off-by: Miguel Ojeda <[email protected]>
---
include/linux/spinlock.h | 17 +-
kernel/printk/printk.c | 2 +
rust/Makefile | 4 +
rust/helpers.c | 78 ++++
rust/kernel/allocator.rs | 68 ++++
rust/kernel/bindings.rs | 22 ++
rust/kernel/bindings_helper.h | 17 +
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 | 196 ++++++++++
rust/kernel/miscdev.rs | 109 ++++++
rust/kernel/module_param.rs | 497 ++++++++++++++++++++++++
rust/kernel/prelude.rs | 22 ++
rust/kernel/print.rs | 461 +++++++++++++++++++++++
rust/kernel/random.rs | 50 +++
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 ++++++++++++++
29 files changed, 4016 insertions(+), 6 deletions(-)
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/miscdev.rs
create mode 100644 rust/kernel/module_param.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/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
diff --git a/include/linux/spinlock.h b/include/linux/spinlock.h
index 79897841a2cc..a022992725be 100644
--- a/include/linux/spinlock.h
+++ b/include/linux/spinlock.h
@@ -331,12 +331,17 @@ static __always_inline raw_spinlock_t *spinlock_check(spinlock_t *lock)
#ifdef CONFIG_DEBUG_SPINLOCK
-# define spin_lock_init(lock) \
-do { \
- static struct lock_class_key __key; \
- \
- __raw_spin_lock_init(spinlock_check(lock), \
- #lock, &__key, LD_WAIT_CONFIG); \
+static inline void __spin_lock_init(spinlock_t *lock, const char *name,
+ struct lock_class_key *key)
+{
+ __raw_spin_lock_init(spinlock_check(lock), name, key, LD_WAIT_CONFIG);
+}
+
+# define spin_lock_init(lock) \
+do { \
+ static struct lock_class_key __key; \
+ \
+ __spin_lock_init(lock, #lock, &__key); \
} while (0)
#else
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index 575a34b88936..d13be89530c4 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -410,6 +410,8 @@ static u64 clear_seq;
#else
#define PREFIX_MAX 32
#endif
+
+/* Keep in sync with rust/kernel/print.rs */
#define LOG_LINE_MAX (1024 - PREFIX_MAX)
#define LOG_LEVEL(v) ((v) & 0x07)
diff --git a/rust/Makefile b/rust/Makefile
index 3fd827d4ac17..dbbbdbad6941 100644
--- a/rust/Makefile
+++ b/rust/Makefile
@@ -5,6 +5,10 @@ extra-$(CONFIG_RUST) += exports_core_generated.h
extra-$(CONFIG_RUST) += libmodule.so
+extra-$(CONFIG_RUST) += bindings_generated.rs
+obj-$(CONFIG_RUST) += alloc.o kernel.o
+extra-$(CONFIG_RUST) += exports_alloc_generated.h exports_kernel_generated.h
+
RUSTDOC = rustdoc
quiet_cmd_rustdoc = RUSTDOC $<
diff --git a/rust/helpers.c b/rust/helpers.c
index 5c2346dd379b..4cae8c27f8f9 100644
--- a/rust/helpers.c
+++ b/rust/helpers.c
@@ -1,8 +1,86 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/bug.h>
+#include <linux/build_bug.h>
+#include <linux/uaccess.h>
+#include <linux/sched/signal.h>
+#include <linux/gfp.h>
+#include <linux/highmem.h>
void rust_helper_BUG(void)
{
BUG();
}
+
+unsigned long rust_helper_copy_from_user(void *to, const void __user *from, unsigned long n)
+{
+ return copy_from_user(to, from, n);
+}
+
+unsigned long rust_helper_copy_to_user(void __user *to, const void *from, unsigned long n)
+{
+ return copy_to_user(to, from, n);
+}
+
+void rust_helper_spin_lock_init(spinlock_t *lock, const char *name,
+ struct lock_class_key *key)
+{
+#ifdef CONFIG_DEBUG_SPINLOCK
+ __spin_lock_init(lock, name, key);
+#else
+ spin_lock_init(lock);
+#endif
+}
+EXPORT_SYMBOL_GPL(rust_helper_spin_lock_init);
+
+void rust_helper_spin_lock(spinlock_t *lock)
+{
+ spin_lock(lock);
+}
+EXPORT_SYMBOL_GPL(rust_helper_spin_lock);
+
+void rust_helper_spin_unlock(spinlock_t *lock)
+{
+ spin_unlock(lock);
+}
+EXPORT_SYMBOL_GPL(rust_helper_spin_unlock);
+
+void rust_helper_init_wait(struct wait_queue_entry *wq_entry)
+{
+ init_wait(wq_entry);
+}
+EXPORT_SYMBOL_GPL(rust_helper_init_wait);
+
+int rust_helper_current_pid(void)
+{
+ return current->pid;
+}
+EXPORT_SYMBOL_GPL(rust_helper_current_pid);
+
+int rust_helper_signal_pending(void)
+{
+ return signal_pending(current);
+}
+EXPORT_SYMBOL_GPL(rust_helper_signal_pending);
+
+struct page *rust_helper_alloc_pages(gfp_t gfp_mask, unsigned int order)
+{
+ return alloc_pages(gfp_mask, order);
+}
+EXPORT_SYMBOL_GPL(rust_helper_alloc_pages);
+
+void *rust_helper_kmap(struct page *page)
+{
+ return kmap(page);
+}
+EXPORT_SYMBOL_GPL(rust_helper_kmap);
+
+void rust_helper_kunmap(struct page *page)
+{
+ return kunmap(page);
+}
+EXPORT_SYMBOL_GPL(rust_helper_kunmap);
+
+// See https://github.com/rust-lang/rust-bindgen/issues/1671
+static_assert(__builtin_types_compatible_p(size_t, uintptr_t),
+ "size_t must match uintptr_t, what architecture is this??");
diff --git a/rust/kernel/allocator.rs b/rust/kernel/allocator.rs
new file mode 100644
index 000000000000..81104f2d8ffa
--- /dev/null
+++ b/rust/kernel/allocator.rs
@@ -0,0 +1,68 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Allocator support.
+
+use core::alloc::{GlobalAlloc, Layout};
+use core::ptr;
+
+use crate::bindings;
+use crate::c_types;
+
+pub struct KernelAllocator;
+
+unsafe impl GlobalAlloc for KernelAllocator {
+ unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
+ // `krealloc()` is used instead of `kmalloc()` because the latter is
+ // an inline function and cannot be bound to as a result.
+ bindings::krealloc(ptr::null(), layout.size(), bindings::GFP_KERNEL) as *mut u8
+ }
+
+ unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {
+ bindings::kfree(ptr as *const c_types::c_void);
+ }
+}
+
+#[alloc_error_handler]
+fn oom(_layout: Layout) -> ! {
+ panic!("Out of memory!");
+}
+
+// `rustc` only generates these for some crate types. Even then, we would need
+// to extract the object file that has them from the archive. For the moment,
+// let's generate them ourselves instead.
+#[no_mangle]
+pub fn __rust_alloc(size: usize, _align: usize) -> *mut u8 {
+ unsafe { bindings::krealloc(core::ptr::null(), size, bindings::GFP_KERNEL) as *mut u8 }
+}
+
+#[no_mangle]
+pub fn __rust_dealloc(ptr: *mut u8, _size: usize, _align: usize) {
+ unsafe { bindings::kfree(ptr as *const c_types::c_void) };
+}
+
+#[no_mangle]
+pub fn __rust_realloc(ptr: *mut u8, _old_size: usize, _align: usize, new_size: usize) -> *mut u8 {
+ unsafe {
+ bindings::krealloc(
+ ptr as *const c_types::c_void,
+ new_size,
+ bindings::GFP_KERNEL,
+ ) as *mut u8
+ }
+}
+
+#[no_mangle]
+pub fn __rust_alloc_zeroed(size: usize, _align: usize) -> *mut u8 {
+ unsafe {
+ bindings::krealloc(
+ core::ptr::null(),
+ size,
+ bindings::GFP_KERNEL | bindings::__GFP_ZERO,
+ ) as *mut u8
+ }
+}
+
+#[no_mangle]
+pub fn __rust_alloc_error_handler(_size: usize, _align: usize) -> ! {
+ panic!("Out of memory!");
+}
diff --git a/rust/kernel/bindings.rs b/rust/kernel/bindings.rs
new file mode 100644
index 000000000000..6a300f52335c
--- /dev/null
+++ b/rust/kernel/bindings.rs
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Bindings
+//!
+//! Imports the generated bindings by `bindgen`.
+
+#[allow(
+ clippy::all,
+ non_camel_case_types,
+ non_upper_case_globals,
+ non_snake_case,
+ improper_ctypes
+)]
+mod bindings_raw {
+ use crate::c_types;
+ include!(env!("RUST_BINDINGS_FILE"));
+}
+pub use bindings_raw::*;
+
+pub const GFP_KERNEL: gfp_t = BINDINGS_GFP_KERNEL;
+pub const __GFP_ZERO: gfp_t = BINDINGS___GFP_ZERO;
+pub const __GFP_HIGHMEM: gfp_t = ___GFP_HIGHMEM;
diff --git a/rust/kernel/bindings_helper.h b/rust/kernel/bindings_helper.h
new file mode 100644
index 000000000000..75b68235f6c7
--- /dev/null
+++ b/rust/kernel/bindings_helper.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#include <linux/cdev.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/random.h>
+#include <linux/slab.h>
+#include <linux/sysctl.h>
+#include <linux/uaccess.h>
+#include <linux/version.h>
+#include <linux/miscdevice.h>
+#include <linux/poll.h>
+#include <linux/mm.h>
+
+// `bindgen` gets confused at certain things
+const gfp_t BINDINGS_GFP_KERNEL = GFP_KERNEL;
+const gfp_t BINDINGS___GFP_ZERO = __GFP_ZERO;
diff --git a/rust/kernel/buffer.rs b/rust/kernel/buffer.rs
new file mode 100644
index 000000000000..b2502fa968fe
--- /dev/null
+++ b/rust/kernel/buffer.rs
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Struct for writing to a pre-allocated buffer with the [`write!`] macro.
+
+use core::fmt;
+
+/// A pre-allocated buffer that implements [`core::fmt::Write`].
+///
+/// Consecutive writes will append to what has already been written.
+/// Writes that don't fit in the buffer will fail.
+pub struct Buffer<'a> {
+ slice: &'a mut [u8],
+ pos: usize,
+}
+
+impl<'a> Buffer<'a> {
+ /// Create a new buffer from an existing array.
+ pub fn new(slice: &'a mut [u8]) -> Self {
+ Buffer { slice, pos: 0 }
+ }
+
+ /// Number of bytes that have already been written to the buffer.
+ /// This will always be less than the length of the original array.
+ pub fn bytes_written(&self) -> usize {
+ self.pos
+ }
+}
+
+impl<'a> fmt::Write for Buffer<'a> {
+ fn write_str(&mut self, s: &str) -> fmt::Result {
+ if s.len() > self.slice.len() - self.pos {
+ Err(fmt::Error)
+ } else {
+ self.slice[self.pos..self.pos + s.len()].copy_from_slice(s.as_bytes());
+ self.pos += s.len();
+ Ok(())
+ }
+ }
+}
diff --git a/rust/kernel/c_types.rs b/rust/kernel/c_types.rs
new file mode 100644
index 000000000000..10486b41efa9
--- /dev/null
+++ b/rust/kernel/c_types.rs
@@ -0,0 +1,133 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! C types for the bindings.
+//!
+//! The bindings generated by `bindgen` use these types to map to the C ones.
+//!
+//! C's standard integer types may differ in width depending on
+//! the architecture, thus we need to conditionally compile those.
+
+#![allow(non_camel_case_types)]
+
+#[cfg(any(target_arch = "arm", target_arch = "x86"))]
+mod c {
+ /// C `void` type.
+ pub type c_void = core::ffi::c_void;
+
+ /// C `char` type.
+ pub type c_char = i8;
+
+ /// C `signed char` type.
+ pub type c_schar = i8;
+
+ /// C `unsigned char` type.
+ pub type c_uchar = u8;
+
+ /// C `short` type.
+ pub type c_short = i16;
+
+ /// C `unsigned short` type.
+ pub type c_ushort = u16;
+
+ /// C `int` type.
+ pub type c_int = i32;
+
+ /// C `unsigned int` type.
+ pub type c_uint = u32;
+
+ /// C `long` type.
+ pub type c_long = i32;
+
+ /// C `unsigned long` type.
+ pub type c_ulong = u32;
+
+ /// C `long long` type.
+ pub type c_longlong = i64;
+
+ /// C `unsigned long long` type.
+ pub type c_ulonglong = u64;
+
+ /// C `ssize_t` type (typically defined in `<sys/types.h>` by POSIX).
+ ///
+ /// For some 32-bit architectures like this one, the kernel defines it as
+ /// `int`, i.e. it is an [`i32`].
+ pub type c_ssize_t = isize;
+
+ /// C `size_t` type (typically defined in `<stddef.h>`).
+ ///
+ /// For some 32-bit architectures like this one, the kernel defines it as
+ /// `unsigned int`, i.e. it is an [`u32`].
+ pub type c_size_t = usize;
+}
+
+#[cfg(any(
+ target_arch = "aarch64",
+ target_arch = "x86_64",
+ target_arch = "powerpc64"
+))]
+mod c {
+ /// C `void` type.
+ pub type c_void = core::ffi::c_void;
+
+ /// C `char` type.
+ pub type c_char = i8;
+
+ /// C `signed char` type.
+ pub type c_schar = i8;
+
+ /// C `unsigned char` type.
+ pub type c_uchar = u8;
+
+ /// C `short` type.
+ pub type c_short = i16;
+
+ /// C `unsigned short` type.
+ pub type c_ushort = u16;
+
+ /// C `int` type.
+ pub type c_int = i32;
+
+ /// C `unsigned int` type.
+ pub type c_uint = u32;
+
+ /// C `long` type.
+ pub type c_long = i64;
+
+ /// C `unsigned long` type.
+ pub type c_ulong = u64;
+
+ /// C `long long` type.
+ pub type c_longlong = i64;
+
+ /// C `unsigned long long` type.
+ pub type c_ulonglong = u64;
+
+ /// C `ssize_t` type (typically defined in `<sys/types.h>` by POSIX).
+ ///
+ /// For 64-bit architectures like this one, the kernel defines it as
+ /// `long`, i.e. it is an [`i64`].
+ pub type c_ssize_t = isize;
+
+ /// C `size_t` type (typically defined in `<stddef.h>`).
+ ///
+ /// For 64-bit architectures like this one, the kernel defines it as
+ /// `unsigned long`, i.e. it is an [`u64`].
+ pub type c_size_t = usize;
+}
+
+pub use c::*;
+
+/// Reads string until null byte is reached and returns slice excluding the
+/// terminating null.
+///
+/// # Safety
+///
+/// The data from the pointer until the null terminator must be valid for reads
+/// and not mutated for all of `'a`. The length of the string must also be less
+/// than `isize::MAX`. See the documentation on
+/// [`core::slice::from_raw_parts()`] for further details on safety of
+/// converting a pointer to a slice.
+pub unsafe fn c_string_bytes<'a>(ptr: *const crate::c_types::c_char) -> &'a [u8] {
+ let length = crate::bindings::strlen(ptr) as usize;
+ &core::slice::from_raw_parts(ptr as *const u8, length)
+}
diff --git a/rust/kernel/chrdev.rs b/rust/kernel/chrdev.rs
new file mode 100644
index 000000000000..6772a3a925cc
--- /dev/null
+++ b/rust/kernel/chrdev.rs
@@ -0,0 +1,162 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Character devices.
+//!
+//! Also called "char devices", `chrdev`, `cdev`.
+//!
+//! C header: [`include/linux/cdev.h`](../../../../include/linux/cdev.h)
+//!
+//! Reference: <https://www.kernel.org/doc/html/latest/core-api/kernel-api.html#char-devices>
+
+use alloc::boxed::Box;
+use core::convert::TryInto;
+use core::marker::PhantomPinned;
+use core::mem::MaybeUninit;
+use core::pin::Pin;
+
+use crate::bindings;
+use crate::c_types;
+use crate::error::{Error, KernelResult};
+use crate::file_operations;
+use crate::types::CStr;
+
+struct RegistrationInner<const N: usize> {
+ dev: bindings::dev_t,
+ used: usize,
+ cdevs: [MaybeUninit<bindings::cdev>; N],
+ _pin: PhantomPinned,
+}
+
+/// Character device registration.
+///
+/// May contain up to a fixed number (`N`) of devices. Must be pinned.
+pub struct Registration<const N: usize> {
+ name: CStr<'static>,
+ minors_start: u16,
+ this_module: &'static crate::ThisModule,
+ inner: Option<RegistrationInner<N>>,
+}
+
+impl<const N: usize> Registration<{ N }> {
+ /// Creates a [`Registration`] object for a character device.
+ ///
+ /// This does *not* register the device: see [`Self::register()`].
+ ///
+ /// This associated function is intended to be used when you need to avoid
+ /// a memory allocation, e.g. when the [`Registration`] is a member of
+ /// a bigger structure inside your [`crate::KernelModule`] instance. If you
+ /// are going to pin the registration right away, call
+ /// [`Self::new_pinned()`] instead.
+ pub fn new(
+ name: CStr<'static>,
+ minors_start: u16,
+ this_module: &'static crate::ThisModule,
+ ) -> Self {
+ Registration {
+ name,
+ minors_start,
+ this_module,
+ inner: None,
+ }
+ }
+
+ /// Creates a pinned [`Registration`] object for a character device.
+ ///
+ /// This does *not* register the device: see [`Self::register()`].
+ pub fn new_pinned(
+ name: CStr<'static>,
+ minors_start: u16,
+ this_module: &'static crate::ThisModule,
+ ) -> KernelResult<Pin<Box<Self>>> {
+ Ok(Pin::from(Box::try_new(Self::new(
+ name,
+ minors_start,
+ this_module,
+ ))?))
+ }
+
+ /// Registers a character device.
+ ///
+ /// You may call this once per device type, up to `N` times.
+ pub fn register<T: file_operations::FileOpener<()>>(self: Pin<&mut Self>) -> KernelResult {
+ // SAFETY: We must ensure that we never move out of `this`.
+ let this = unsafe { self.get_unchecked_mut() };
+ if this.inner.is_none() {
+ let mut dev: bindings::dev_t = 0;
+ // SAFETY: Calling unsafe function. `this.name` has `'static`
+ // lifetime.
+ let res = unsafe {
+ bindings::alloc_chrdev_region(
+ &mut dev,
+ this.minors_start.into(),
+ N.try_into()?,
+ this.name.as_ptr() as *const c_types::c_char,
+ )
+ };
+ if res != 0 {
+ return Err(Error::from_kernel_errno(res));
+ }
+ this.inner = Some(RegistrationInner {
+ dev,
+ used: 0,
+ cdevs: [MaybeUninit::<bindings::cdev>::uninit(); N],
+ _pin: PhantomPinned,
+ });
+ }
+
+ let mut inner = this.inner.as_mut().unwrap();
+ if inner.used == N {
+ return Err(Error::EINVAL);
+ }
+ let cdev = inner.cdevs[inner.used].as_mut_ptr();
+ // SAFETY: Calling unsafe functions and manipulating `MaybeUninit`
+ // pointer.
+ unsafe {
+ bindings::cdev_init(
+ cdev,
+ // SAFETY: The adapter doesn't retrieve any state yet, so it's compatible with any
+ // registration.
+ file_operations::FileOperationsVtable::<Self, T>::build(),
+ );
+ (*cdev).owner = this.this_module.0;
+ let rc = bindings::cdev_add(cdev, inner.dev + inner.used as bindings::dev_t, 1);
+ if rc != 0 {
+ return Err(Error::from_kernel_errno(rc));
+ }
+ }
+ inner.used += 1;
+ Ok(())
+ }
+}
+
+impl<const N: usize> file_operations::FileOpenAdapter for Registration<{ N }> {
+ type Arg = ();
+
+ unsafe fn convert(
+ _inode: *mut bindings::inode,
+ _file: *mut bindings::file,
+ ) -> *const Self::Arg {
+ // TODO: Update the SAFETY comment on the call to `FileOperationsVTable::build` above once
+ // this is updated to retrieve state.
+ &()
+ }
+}
+
+// SAFETY: `Registration` does not expose any of its state across threads
+// (it is fine for multiple threads to have a shared reference to it).
+unsafe impl<const N: usize> Sync for Registration<{ N }> {}
+
+impl<const N: usize> Drop for Registration<{ N }> {
+ fn drop(&mut self) {
+ if let Some(inner) = self.inner.as_mut() {
+ // SAFETY: Calling unsafe functions, `0..inner.used` of
+ // `inner.cdevs` are initialized in `Registration::register`.
+ unsafe {
+ for i in 0..inner.used {
+ bindings::cdev_del(inner.cdevs[i].as_mut_ptr());
+ }
+ bindings::unregister_chrdev_region(inner.dev, N.try_into().unwrap());
+ }
+ }
+ }
+}
diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs
new file mode 100644
index 000000000000..432d866232c1
--- /dev/null
+++ b/rust/kernel/error.rs
@@ -0,0 +1,106 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Kernel errors.
+//!
+//! C header: [`include/uapi/asm-generic/errno-base.h`](../../../include/uapi/asm-generic/errno-base.h)
+
+use crate::{bindings, c_types};
+use alloc::{alloc::AllocError, collections::TryReserveError};
+use core::{num::TryFromIntError, str::Utf8Error};
+
+/// Generic integer kernel error.
+///
+/// The kernel defines a set of integer generic error codes based on C and
+/// POSIX ones. These codes may have a more specific meaning in some contexts.
+pub struct Error(c_types::c_int);
+
+impl Error {
+ /// Invalid argument.
+ pub const EINVAL: Self = Error(-(bindings::EINVAL as i32));
+
+ /// Out of memory.
+ pub const ENOMEM: Self = Error(-(bindings::ENOMEM as i32));
+
+ /// Bad address.
+ pub const EFAULT: Self = Error(-(bindings::EFAULT as i32));
+
+ /// Illegal seek.
+ pub const ESPIPE: Self = Error(-(bindings::ESPIPE as i32));
+
+ /// Try again.
+ pub const EAGAIN: Self = Error(-(bindings::EAGAIN as i32));
+
+ /// Device or resource busy.
+ pub const EBUSY: Self = Error(-(bindings::EBUSY as i32));
+
+ /// Restart the system call.
+ pub const ERESTARTSYS: Self = Error(-(bindings::ERESTARTSYS as i32));
+
+ /// Operation not permitted.
+ pub const EPERM: Self = Error(-(bindings::EPERM as i32));
+
+ /// No such process.
+ pub const ESRCH: Self = Error(-(bindings::ESRCH as i32));
+
+ /// No such file or directory.
+ pub const ENOENT: Self = Error(-(bindings::ENOENT as i32));
+
+ /// Interrupted system call.
+ pub const EINTR: Self = Error(-(bindings::EINTR as i32));
+
+ /// Creates an [`Error`] from a kernel error code.
+ pub fn from_kernel_errno(errno: c_types::c_int) -> Error {
+ Error(errno)
+ }
+
+ /// Returns the kernel error code.
+ pub fn to_kernel_errno(&self) -> c_types::c_int {
+ self.0
+ }
+}
+
+impl From<TryFromIntError> for Error {
+ fn from(_: TryFromIntError) -> Error {
+ Error::EINVAL
+ }
+}
+
+impl From<Utf8Error> for Error {
+ fn from(_: Utf8Error) -> Error {
+ Error::EINVAL
+ }
+}
+
+impl From<TryReserveError> for Error {
+ fn from(_: TryReserveError) -> Error {
+ Error::ENOMEM
+ }
+}
+
+/// A [`Result`] with an [`Error`] error type.
+///
+/// To be used as the return type for functions that may fail.
+///
+/// # Error codes in C and Rust
+///
+/// In C, it is common that functions indicate success or failure through
+/// their return value; modifying or returning extra data through non-`const`
+/// pointer parameters. In particular, in the kernel, functions that may fail
+/// typically return an `int` that represents a generic error code. We model
+/// those as [`Error`].
+///
+/// In Rust, it is idiomatic to model functions that may fail as returning
+/// a [`Result`]. Since in the kernel many functions return an error code,
+/// [`KernelResult`] is a type alias for a [`Result`] that uses [`Error`] as
+/// its error type.
+///
+/// Note that even if a function does not return anything when it succeeds,
+/// it should still be modeled as returning a `KernelResult` rather than
+/// just an [`Error`].
+pub type KernelResult<T = ()> = Result<T, Error>;
+
+impl From<AllocError> for Error {
+ fn from(_: AllocError) -> Error {
+ Error::ENOMEM
+ }
+}
diff --git a/rust/kernel/file_operations.rs b/rust/kernel/file_operations.rs
new file mode 100644
index 000000000000..f54ddd0b1da0
--- /dev/null
+++ b/rust/kernel/file_operations.rs
@@ -0,0 +1,668 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! File operations.
+//!
+//! C header: [`include/linux/fs.h`](../../../../include/linux/fs.h)
+
+use core::convert::{TryFrom, TryInto};
+use core::{marker, mem, ops::Deref, pin::Pin, ptr};
+
+use alloc::boxed::Box;
+use alloc::sync::Arc;
+
+use crate::bindings;
+use crate::c_types;
+use crate::error::{Error, KernelResult};
+use crate::sync::{CondVar, Ref, RefCounted};
+use crate::user_ptr::{UserSlicePtr, UserSlicePtrReader, UserSlicePtrWriter};
+
+/// Wraps the kernel's `struct file`.
+///
+/// # Invariants
+///
+/// The pointer [`File::ptr`] is non-null and valid.
+pub struct File {
+ ptr: *const bindings::file,
+}
+
+impl File {
+ /// Constructs a new [`struct file`] wrapper.
+ ///
+ /// # Safety
+ ///
+ /// The pointer `ptr` must be non-null and valid for the lifetime of the object.
+ unsafe fn from_ptr(ptr: *const bindings::file) -> File {
+ // INVARIANTS: the safety contract ensures the type invariant will hold.
+ File { ptr }
+ }
+
+ /// Returns the current seek/cursor/pointer position (`struct file::f_pos`).
+ pub fn pos(&self) -> u64 {
+ // SAFETY: `File::ptr` is guaranteed to be valid by the type invariants.
+ unsafe { (*self.ptr).f_pos as u64 }
+ }
+
+ /// Returns whether the file is in blocking mode.
+ pub fn is_blocking(&self) -> bool {
+ // SAFETY: `File::ptr` is guaranteed to be valid by the type invariants.
+ unsafe { (*self.ptr).f_flags & bindings::O_NONBLOCK == 0 }
+ }
+}
+
+/// Wraps the kernel's `struct poll_table_struct`.
+///
+/// # Invariants
+///
+/// The pointer [`PollTable::ptr`] is null or valid.
+pub struct PollTable {
+ ptr: *mut bindings::poll_table_struct,
+}
+
+impl PollTable {
+ /// Constructors a new `struct poll_table_struct` wrapper.
+ ///
+ /// # Safety
+ ///
+ /// The pointer `ptr` must be either null or a valid pointer for the lifetime of the object.
+ unsafe fn from_ptr(ptr: *mut bindings::poll_table_struct) -> Self {
+ Self { ptr }
+ }
+
+ /// Associates the given file and condition variable to this poll table. It means notifying the
+ /// condition variable will notify the poll table as well; additionally, the association
+ /// between the condition variable and the file will automatically be undone by the kernel when
+ /// the file is destructed. To unilaterally remove the association before then, one can call
+ /// [`CondVar::free_waiters`].
+ ///
+ /// # Safety
+ ///
+ /// If the condition variable is destroyed before the file, then [`CondVar::free_waiters`] must
+ /// be called to ensure that all waiters are flushed out.
+ pub unsafe fn register_wait<'a>(&self, file: &'a File, cv: &'a CondVar) {
+ if self.ptr.is_null() {
+ return;
+ }
+
+ // SAFETY: `PollTable::ptr` is guaranteed to be valid by the type invariants and the null
+ // check above.
+ let table = &*self.ptr;
+ if let Some(proc) = table._qproc {
+ // SAFETY: All pointers are known to be valid.
+ proc(file.ptr as _, cv.wait_list.get(), self.ptr)
+ }
+ }
+}
+
+/// Equivalent to [`std::io::SeekFrom`].
+///
+/// [`std::io::SeekFrom`]: https://doc.rust-lang.org/std/io/enum.SeekFrom.html
+pub enum SeekFrom {
+ /// Equivalent to C's `SEEK_SET`.
+ Start(u64),
+
+ /// Equivalent to C's `SEEK_END`.
+ End(i64),
+
+ /// Equivalent to C's `SEEK_CUR`.
+ Current(i64),
+}
+
+fn from_kernel_result<T>(r: KernelResult<T>) -> T
+where
+ T: TryFrom<c_types::c_int>,
+ T::Error: core::fmt::Debug,
+{
+ match r {
+ Ok(v) => v,
+ Err(e) => T::try_from(e.to_kernel_errno()).unwrap(),
+ }
+}
+
+macro_rules! from_kernel_result {
+ ($($tt:tt)*) => {{
+ from_kernel_result((|| {
+ $($tt)*
+ })())
+ }};
+}
+
+unsafe extern "C" fn open_callback<A: FileOpenAdapter, T: FileOpener<A::Arg>>(
+ inode: *mut bindings::inode,
+ file: *mut bindings::file,
+) -> c_types::c_int {
+ from_kernel_result! {
+ let arg = A::convert(inode, file);
+ let ptr = T::open(&*arg)?.into_pointer();
+ (*file).private_data = ptr as *mut c_types::c_void;
+ Ok(0)
+ }
+}
+
+unsafe extern "C" fn read_callback<T: FileOperations>(
+ file: *mut bindings::file,
+ buf: *mut c_types::c_char,
+ len: c_types::c_size_t,
+ offset: *mut bindings::loff_t,
+) -> c_types::c_ssize_t {
+ from_kernel_result! {
+ let mut data = UserSlicePtr::new(buf as *mut c_types::c_void, len).writer();
+ let f = &*((*file).private_data as *const T);
+ // No `FMODE_UNSIGNED_OFFSET` support, so `offset` must be in [0, 2^63).
+ // See discussion in https://github.com/fishinabarrel/linux-kernel-module-rust/pull/113
+ let read = f.read(&File::from_ptr(file), &mut data, (*offset).try_into()?)?;
+ (*offset) += bindings::loff_t::try_from(read).unwrap();
+ Ok(read as _)
+ }
+}
+
+unsafe extern "C" fn write_callback<T: FileOperations>(
+ file: *mut bindings::file,
+ buf: *const c_types::c_char,
+ len: c_types::c_size_t,
+ offset: *mut bindings::loff_t,
+) -> c_types::c_ssize_t {
+ from_kernel_result! {
+ let mut data = UserSlicePtr::new(buf as *mut c_types::c_void, len).reader();
+ let f = &*((*file).private_data as *const T);
+ // No `FMODE_UNSIGNED_OFFSET` support, so `offset` must be in [0, 2^63).
+ // See discussion in https://github.com/fishinabarrel/linux-kernel-module-rust/pull/113
+ let written = f.write(&mut data, (*offset).try_into()?)?;
+ (*offset) += bindings::loff_t::try_from(written).unwrap();
+ Ok(written as _)
+ }
+}
+
+unsafe extern "C" fn release_callback<T: FileOperations>(
+ _inode: *mut bindings::inode,
+ file: *mut bindings::file,
+) -> c_types::c_int {
+ let ptr = mem::replace(&mut (*file).private_data, ptr::null_mut());
+ T::release(T::Wrapper::from_pointer(ptr as _), &File::from_ptr(file));
+ 0
+}
+
+unsafe extern "C" fn llseek_callback<T: FileOperations>(
+ file: *mut bindings::file,
+ offset: bindings::loff_t,
+ whence: c_types::c_int,
+) -> bindings::loff_t {
+ from_kernel_result! {
+ let off = match whence as u32 {
+ bindings::SEEK_SET => SeekFrom::Start(offset.try_into()?),
+ bindings::SEEK_CUR => SeekFrom::Current(offset),
+ bindings::SEEK_END => SeekFrom::End(offset),
+ _ => return Err(Error::EINVAL),
+ };
+ let f = &*((*file).private_data as *const T);
+ let off = f.seek(&File::from_ptr(file), off)?;
+ Ok(off as bindings::loff_t)
+ }
+}
+
+unsafe extern "C" fn unlocked_ioctl_callback<T: FileOperations>(
+ file: *mut bindings::file,
+ cmd: c_types::c_uint,
+ arg: c_types::c_ulong,
+) -> c_types::c_long {
+ from_kernel_result! {
+ let f = &*((*file).private_data as *const T);
+ // SAFETY: This function is called by the kernel, so it must set `fs` appropriately.
+ let mut cmd = IoctlCommand::new(cmd as _, arg as _);
+ let ret = f.ioctl(&File::from_ptr(file), &mut cmd)?;
+ Ok(ret as _)
+ }
+}
+
+unsafe extern "C" fn compat_ioctl_callback<T: FileOperations>(
+ file: *mut bindings::file,
+ cmd: c_types::c_uint,
+ arg: c_types::c_ulong,
+) -> c_types::c_long {
+ from_kernel_result! {
+ let f = &*((*file).private_data as *const T);
+ // SAFETY: This function is called by the kernel, so it must set `fs` appropriately.
+ let mut cmd = IoctlCommand::new(cmd as _, arg as _);
+ let ret = f.compat_ioctl(&File::from_ptr(file), &mut cmd)?;
+ Ok(ret as _)
+ }
+}
+
+unsafe extern "C" fn mmap_callback<T: FileOperations>(
+ file: *mut bindings::file,
+ vma: *mut bindings::vm_area_struct,
+) -> c_types::c_int {
+ from_kernel_result! {
+ let f = &*((*file).private_data as *const T);
+ f.mmap(&File::from_ptr(file), &mut *vma)?;
+ Ok(0)
+ }
+}
+
+unsafe extern "C" fn fsync_callback<T: FileOperations>(
+ file: *mut bindings::file,
+ start: bindings::loff_t,
+ end: bindings::loff_t,
+ datasync: c_types::c_int,
+) -> c_types::c_int {
+ from_kernel_result! {
+ let start = start.try_into()?;
+ let end = end.try_into()?;
+ let datasync = datasync != 0;
+ let f = &*((*file).private_data as *const T);
+ let res = f.fsync(&File::from_ptr(file), start, end, datasync)?;
+ Ok(res.try_into().unwrap())
+ }
+}
+
+unsafe extern "C" fn poll_callback<T: FileOperations>(
+ file: *mut bindings::file,
+ wait: *mut bindings::poll_table_struct,
+) -> bindings::__poll_t {
+ let f = &*((*file).private_data as *const T);
+ match f.poll(&File::from_ptr(file), &PollTable::from_ptr(wait)) {
+ Ok(v) => v,
+ Err(_) => bindings::POLLERR,
+ }
+}
+
+pub(crate) struct FileOperationsVtable<A, T>(marker::PhantomData<A>, marker::PhantomData<T>);
+
+impl<A: FileOpenAdapter, T: FileOpener<A::Arg>> FileOperationsVtable<A, T> {
+ const VTABLE: bindings::file_operations = bindings::file_operations {
+ open: Some(open_callback::<A, T>),
+ release: Some(release_callback::<T>),
+ read: if T::TO_USE.read {
+ Some(read_callback::<T>)
+ } else {
+ None
+ },
+ write: if T::TO_USE.write {
+ Some(write_callback::<T>)
+ } else {
+ None
+ },
+ llseek: if T::TO_USE.seek {
+ Some(llseek_callback::<T>)
+ } else {
+ None
+ },
+
+ check_flags: None,
+ compat_ioctl: if T::TO_USE.compat_ioctl {
+ Some(compat_ioctl_callback::<T>)
+ } else {
+ None
+ },
+ copy_file_range: None,
+ fallocate: None,
+ fadvise: None,
+ fasync: None,
+ flock: None,
+ flush: None,
+ fsync: if T::TO_USE.fsync {
+ Some(fsync_callback::<T>)
+ } else {
+ None
+ },
+ get_unmapped_area: None,
+ iterate: None,
+ iterate_shared: None,
+ iopoll: None,
+ lock: None,
+ mmap: if T::TO_USE.mmap {
+ Some(mmap_callback::<T>)
+ } else {
+ None
+ },
+ mmap_supported_flags: 0,
+ owner: ptr::null_mut(),
+ poll: if T::TO_USE.poll {
+ Some(poll_callback::<T>)
+ } else {
+ None
+ },
+ read_iter: None,
+ remap_file_range: None,
+ sendpage: None,
+ setlease: None,
+ show_fdinfo: None,
+ splice_read: None,
+ splice_write: None,
+ unlocked_ioctl: if T::TO_USE.ioctl {
+ Some(unlocked_ioctl_callback::<T>)
+ } else {
+ None
+ },
+ write_iter: None,
+ };
+
+ /// Builds an instance of [`struct file_operations`].
+ ///
+ /// # Safety
+ ///
+ /// The caller must ensure that the adapter is compatible with the way the device is registered.
+ pub(crate) const unsafe fn build() -> &'static bindings::file_operations {
+ &Self::VTABLE
+ }
+}
+
+/// Represents which fields of [`struct file_operations`] should be populated with pointers.
+pub struct ToUse {
+ /// The `read` field of [`struct file_operations`].
+ pub read: bool,
+
+ /// The `write` field of [`struct file_operations`].
+ pub write: bool,
+
+ /// The `llseek` field of [`struct file_operations`].
+ pub seek: bool,
+
+ /// The `unlocked_ioctl` field of [`struct file_operations`].
+ pub ioctl: bool,
+
+ /// The `compat_ioctl` field of [`struct file_operations`].
+ pub compat_ioctl: bool,
+
+ /// The `fsync` field of [`struct file_operations`].
+ pub fsync: bool,
+
+ /// The `mmap` field of [`struct file_operations`].
+ pub mmap: bool,
+
+ /// The `poll` field of [`struct file_operations`].
+ pub poll: bool,
+}
+
+/// A constant version where all values are to set to `false`, that is, all supported fields will
+/// be set to null pointers.
+pub const USE_NONE: ToUse = ToUse {
+ read: false,
+ write: false,
+ seek: false,
+ ioctl: false,
+ compat_ioctl: false,
+ fsync: false,
+ mmap: false,
+ poll: false,
+};
+
+/// Defines the [`FileOperations::TO_USE`] field based on a list of fields to be populated.
+#[macro_export]
+macro_rules! declare_file_operations {
+ () => {
+ const TO_USE: $crate::file_operations::ToUse = $crate::file_operations::USE_NONE;
+ };
+ ($($i:ident),+) => {
+ const TO_USE: kernel::file_operations::ToUse =
+ $crate::file_operations::ToUse {
+ $($i: true),+ ,
+ ..$crate::file_operations::USE_NONE
+ };
+ };
+}
+
+/// Allows the handling of ioctls defined with the `_IO`, `_IOR`, `_IOW`, and `_IOWR` macros.
+///
+/// For each macro, there is a handler function that takes the appropriate types as arguments.
+pub trait IoctlHandler: Sync {
+ /// Handles ioctls defined with the `_IO` macro, that is, with no buffer as argument.
+ fn pure(&self, _file: &File, _cmd: u32, _arg: usize) -> KernelResult<i32> {
+ Err(Error::EINVAL)
+ }
+
+ /// Handles ioctls defined with the `_IOR` macro, that is, with an output buffer provided as
+ /// argument.
+ fn read(&self, _file: &File, _cmd: u32, _writer: &mut UserSlicePtrWriter) -> KernelResult<i32> {
+ Err(Error::EINVAL)
+ }
+
+ /// Handles ioctls defined with the `_IOW` macro, that is, with an input buffer provided as
+ /// argument.
+ fn write(
+ &self,
+ _file: &File,
+ _cmd: u32,
+ _reader: &mut UserSlicePtrReader,
+ ) -> KernelResult<i32> {
+ Err(Error::EINVAL)
+ }
+
+ /// Handles ioctls defined with the `_IOWR` macro, that is, with a buffer for both input and
+ /// output provided as argument.
+ fn read_write(&self, _file: &File, _cmd: u32, _data: UserSlicePtr) -> KernelResult<i32> {
+ Err(Error::EINVAL)
+ }
+}
+
+/// Represents an ioctl command.
+///
+/// It can use the components of an ioctl command to dispatch ioctls using
+/// [`IoctlCommand::dispatch`].
+pub struct IoctlCommand {
+ cmd: u32,
+ arg: usize,
+ user_slice: Option<UserSlicePtr>,
+}
+
+impl IoctlCommand {
+ /// Constructs a new [`IoctlCommand`].
+ fn new(cmd: u32, arg: usize) -> Self {
+ let size = (cmd >> bindings::_IOC_SIZESHIFT) & bindings::_IOC_SIZEMASK;
+
+ // SAFETY: We only create one instance of the user slice per ioctl call, so TOCTOU issues
+ // are not possible.
+ let user_slice = Some(unsafe { UserSlicePtr::new(arg as _, size as _) });
+ Self {
+ cmd,
+ arg,
+ user_slice,
+ }
+ }
+
+ /// Dispatches the given ioctl to the appropriate handler based on the value of the command. It
+ /// also creates a [`UserSlicePtr`], [`UserSlicePtrReader`], or [`UserSlicePtrWriter`]
+ /// depending on the direction of the buffer of the command.
+ ///
+ /// It is meant to be used in implementations of [`FileOperations::ioctl`] and
+ /// [`FileOperations::compat_ioctl`].
+ pub fn dispatch<T: IoctlHandler>(&mut self, handler: &T, file: &File) -> KernelResult<i32> {
+ let dir = (self.cmd >> bindings::_IOC_DIRSHIFT) & bindings::_IOC_DIRMASK;
+ if dir == bindings::_IOC_NONE {
+ return handler.pure(file, self.cmd, self.arg);
+ }
+
+ let data = self.user_slice.take().ok_or(Error::EINVAL)?;
+ const READ_WRITE: u32 = bindings::_IOC_READ | bindings::_IOC_WRITE;
+ match dir {
+ bindings::_IOC_WRITE => handler.write(file, self.cmd, &mut data.reader()),
+ bindings::_IOC_READ => handler.read(file, self.cmd, &mut data.writer()),
+ READ_WRITE => handler.read_write(file, self.cmd, data),
+ _ => Err(Error::EINVAL),
+ }
+ }
+
+ /// Returns the raw 32-bit value of the command and the ptr-sized argument.
+ pub fn raw(&self) -> (u32, usize) {
+ (self.cmd, self.arg)
+ }
+}
+
+/// Trait for extracting file open arguments from kernel data structures.
+///
+/// This is meant to be implemented by registration managers.
+pub trait FileOpenAdapter {
+ /// The type of argument this adapter extracts.
+ type Arg;
+
+ /// Converts untyped data stored in [`struct inode`] and [`struct file`] (when [`struct
+ /// file_operations::open`] is called) into the given type. For example, for `miscdev`
+ /// devices, a pointer to the registered [`struct miscdev`] is stored in [`struct
+ /// file::private_data`].
+ ///
+ /// # Safety
+ ///
+ /// This function must be called only when [`struct file_operations::open`] is being called for
+ /// a file that was registered by the implementer.
+ unsafe fn convert(_inode: *mut bindings::inode, _file: *mut bindings::file)
+ -> *const Self::Arg;
+}
+
+/// Trait for implementers of kernel files.
+///
+/// In addition to the methods in [`FileOperations`], implementers must also provide
+/// [`FileOpener::open`] with a customised argument. This allows a single implementation of
+/// [`FileOperations`] to be used for different types of registrations, for example, `miscdev` and
+/// `chrdev`.
+pub trait FileOpener<T: ?Sized>: FileOperations {
+ /// Creates a new instance of this file.
+ ///
+ /// Corresponds to the `open` function pointer in `struct file_operations`.
+ fn open(context: &T) -> KernelResult<Self::Wrapper>;
+}
+
+/// Corresponds to the kernel's `struct file_operations`.
+///
+/// You implement this trait whenever you would create a `struct file_operations`.
+///
+/// File descriptors may be used from multiple threads/processes concurrently, so your type must be
+/// [`Sync`]. It must also be [`Send`] because [`FileOperations::release`] will be called from the
+/// thread that decrements that associated file's refcount to zero.
+pub trait FileOperations: Send + Sync + Sized {
+ /// The methods to use to populate [`struct file_operations`].
+ const TO_USE: ToUse;
+
+ /// The pointer type that will be used to hold ourselves.
+ type Wrapper: PointerWrapper<Self>;
+
+ /// Cleans up after the last reference to the file goes away.
+ ///
+ /// Note that the object is moved, so it will be freed automatically unless the implementation
+ /// moves it elsewhere.
+ ///
+ /// Corresponds to the `release` function pointer in `struct file_operations`.
+ fn release(_obj: Self::Wrapper, _file: &File) {}
+
+ /// Reads data from this file to userspace.
+ ///
+ /// Corresponds to the `read` function pointer in `struct file_operations`.
+ fn read(
+ &self,
+ _file: &File,
+ _data: &mut UserSlicePtrWriter,
+ _offset: u64,
+ ) -> KernelResult<usize> {
+ Err(Error::EINVAL)
+ }
+
+ /// Writes data from userspace to this file.
+ ///
+ /// Corresponds to the `write` function pointer in `struct file_operations`.
+ fn write(&self, _data: &mut UserSlicePtrReader, _offset: u64) -> KernelResult<usize> {
+ Err(Error::EINVAL)
+ }
+
+ /// Changes the position of the file.
+ ///
+ /// Corresponds to the `llseek` function pointer in `struct file_operations`.
+ fn seek(&self, _file: &File, _offset: SeekFrom) -> KernelResult<u64> {
+ Err(Error::EINVAL)
+ }
+
+ /// Performs IO control operations that are specific to the file.
+ ///
+ /// Corresponds to the `unlocked_ioctl` function pointer in `struct file_operations`.
+ fn ioctl(&self, _file: &File, _cmd: &mut IoctlCommand) -> KernelResult<i32> {
+ Err(Error::EINVAL)
+ }
+
+ /// Performs 32-bit IO control operations on that are specific to the file on 64-bit kernels.
+ ///
+ /// Corresponds to the `compat_ioctl` function pointer in `struct file_operations`.
+ fn compat_ioctl(&self, _file: &File, _cmd: &mut IoctlCommand) -> KernelResult<i32> {
+ Err(Error::EINVAL)
+ }
+
+ /// Syncs pending changes to this file.
+ ///
+ /// Corresponds to the `fsync` function pointer in `struct file_operations`.
+ fn fsync(&self, _file: &File, _start: u64, _end: u64, _datasync: bool) -> KernelResult<u32> {
+ Err(Error::EINVAL)
+ }
+
+ /// Maps areas of the caller's virtual memory with device/file memory.
+ ///
+ /// Corresponds to the `mmap` function pointer in `struct file_operations`.
+ /// TODO: wrap `vm_area_struct` so that we don't have to expose it.
+ fn mmap(&self, _file: &File, _vma: &mut bindings::vm_area_struct) -> KernelResult {
+ Err(Error::EINVAL)
+ }
+
+ /// Checks the state of the file and optionally registers for notification when the state
+ /// changes.
+ ///
+ /// Corresponds to the `poll` function pointer in `struct file_operations`.
+ fn poll(&self, _file: &File, _table: &PollTable) -> KernelResult<u32> {
+ Ok(bindings::POLLIN | bindings::POLLOUT | bindings::POLLRDNORM | bindings::POLLWRNORM)
+ }
+}
+
+/// Used to convert an object into a raw pointer that represents it.
+///
+/// It can eventually be converted back into the object. This is used to store objects as pointers
+/// in kernel data structures, for example, an implementation of [`FileOperations`] in `struct
+/// file::private_data`.
+pub trait PointerWrapper<T> {
+ /// Returns the raw pointer.
+ fn into_pointer(self) -> *const T;
+
+ /// Returns the instance back from the raw pointer.
+ ///
+ /// # Safety
+ ///
+ /// The passed pointer must come from a previous call to [`PointerWrapper::into_pointer()`].
+ unsafe fn from_pointer(ptr: *const T) -> Self;
+}
+
+impl<T> PointerWrapper<T> for Box<T> {
+ fn into_pointer(self) -> *const T {
+ Box::into_raw(self)
+ }
+
+ unsafe fn from_pointer(ptr: *const T) -> Self {
+ Box::from_raw(ptr as _)
+ }
+}
+
+impl<T: RefCounted> PointerWrapper<T> for Ref<T> {
+ fn into_pointer(self) -> *const T {
+ Ref::into_raw(self)
+ }
+
+ unsafe fn from_pointer(ptr: *const T) -> Self {
+ Ref::from_raw(ptr as _)
+ }
+}
+
+impl<T> PointerWrapper<T> for Arc<T> {
+ fn into_pointer(self) -> *const T {
+ Arc::into_raw(self)
+ }
+
+ unsafe fn from_pointer(ptr: *const T) -> Self {
+ Arc::from_raw(ptr)
+ }
+}
+
+impl<T, W: PointerWrapper<T> + Deref> PointerWrapper<T> for Pin<W> {
+ fn into_pointer(self) -> *const T {
+ // SAFETY: We continue to treat the pointer as pinned by returning just a pointer to it to
+ // the caller.
+ let inner = unsafe { Pin::into_inner_unchecked(self) };
+ inner.into_pointer()
+ }
+
+ unsafe fn from_pointer(p: *const T) -> Self {
+ // SAFETY: The object was originally pinned.
+ Pin::new_unchecked(W::from_pointer(p))
+ }
+}
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
new file mode 100644
index 000000000000..9a06bd60d5c1
--- /dev/null
+++ b/rust/kernel/lib.rs
@@ -0,0 +1,196 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! The `kernel` crate.
+//!
+//! This crate contains the kernel APIs that have been ported or wrapped for
+//! usage by Rust code in the kernel and is shared by all of them.
+//!
+//! In other words, all the rest of the Rust code in the kernel (e.g. kernel
+//! modules written in Rust) depends on [`core`], [`alloc`] and this crate.
+//!
+//! If you need a kernel C API that is not ported or wrapped yet here, then
+//! do so first instead of bypassing this crate.
+
+#![no_std]
+#![feature(
+ allocator_api,
+ alloc_error_handler,
+ const_fn,
+ const_mut_refs,
+ const_panic,
+ try_reserve
+)]
+#![deny(clippy::complexity)]
+#![deny(clippy::correctness)]
+#![deny(clippy::perf)]
+#![deny(clippy::style)]
+
+// Ensure conditional compilation based on the kernel configuration works;
+// otherwise we may silently break things like initcall handling.
+#[cfg(not(CONFIG_RUST))]
+compile_error!("Missing kernel configuration for conditional compilation");
+
+mod allocator;
+
+#[doc(hidden)]
+pub mod bindings;
+
+pub mod buffer;
+pub mod c_types;
+pub mod chrdev;
+mod error;
+pub mod file_operations;
+pub mod miscdev;
+
+#[doc(hidden)]
+pub mod module_param;
+
+pub mod prelude;
+pub mod print;
+pub mod random;
+mod static_assert;
+pub mod sync;
+
+#[cfg(CONFIG_SYSCTL)]
+pub mod sysctl;
+
+mod types;
+pub mod user_ptr;
+
+pub use crate::error::{Error, KernelResult};
+pub use crate::types::{CStr, Mode};
+
+/// Page size defined in terms of the `PAGE_SHIFT` macro from C.
+///
+/// [`PAGE_SHIFT`]: ../../../include/asm-generic/page.h
+pub const PAGE_SIZE: usize = 1 << bindings::PAGE_SHIFT;
+
+/// The top level entrypoint to implementing a kernel module.
+///
+/// For any teardown or cleanup operations, your type may implement [`Drop`].
+pub trait KernelModule: Sized + Sync {
+ /// Called at module initialization time.
+ ///
+ /// Use this method to perform whatever setup or registration your module
+ /// should do.
+ ///
+ /// Equivalent to the `module_init` macro in the C API.
+ fn init() -> KernelResult<Self>;
+}
+
+/// Equivalent to `THIS_MODULE` in the C API.
+///
+/// C header: `include/linux/export.h`
+pub struct ThisModule(*mut bindings::module);
+
+// SAFETY: `THIS_MODULE` may be used from all threads within a module.
+unsafe impl Sync for ThisModule {}
+
+impl ThisModule {
+ /// Creates a [`ThisModule`] given the `THIS_MODULE` pointer.
+ ///
+ /// # Safety
+ ///
+ /// The pointer must be equal to the right `THIS_MODULE`.
+ pub const unsafe fn from_ptr(ptr: *mut bindings::module) -> ThisModule {
+ ThisModule(ptr)
+ }
+
+ /// Locks the module parameters to access them.
+ ///
+ /// Returns a [`KParamGuard`] that will release the lock when dropped.
+ pub fn kernel_param_lock(&self) -> KParamGuard<'_> {
+ // SAFETY: `kernel_param_lock` will check if the pointer is null and
+ // use the built-in mutex in that case.
+ #[cfg(CONFIG_SYSFS)]
+ unsafe {
+ bindings::kernel_param_lock(self.0)
+ }
+
+ KParamGuard { this_module: self }
+ }
+}
+
+/// Scoped lock on the kernel parameters of [`ThisModule`].
+///
+/// Lock will be released when this struct is dropped.
+pub struct KParamGuard<'a> {
+ this_module: &'a ThisModule,
+}
+
+#[cfg(CONFIG_SYSFS)]
+impl<'a> Drop for KParamGuard<'a> {
+ fn drop(&mut self) {
+ // SAFETY: `kernel_param_lock` will check if the pointer is null and
+ // use the built-in mutex in that case. The existance of `self`
+ // guarantees that the lock is held.
+ unsafe { bindings::kernel_param_unlock(self.this_module.0) }
+ }
+}
+
+/// Calculates the offset of a field from the beginning of the struct it belongs to.
+///
+/// # Example
+///
+/// ```
+/// struct Test {
+/// a: u64,
+/// b: u32,
+/// }
+///
+/// fn test() {
+/// // This prints `8`.
+/// pr_info!("{}\n", offset_of!(Test, b));
+/// }
+/// ```
+#[macro_export]
+macro_rules! offset_of {
+ ($type:ty, $($f:tt)*) => {{
+ let tmp = core::mem::MaybeUninit::<$type>::uninit();
+ let outer = tmp.as_ptr();
+ // To avoid warnings when nesting `unsafe` blocks.
+ #[allow(unused_unsafe)]
+ // SAFETY: The pointer is valid and aligned, just not initialised; `addr_of` ensures that
+ // we don't actually read from `outer` (which would be UB) nor create an intermediate
+ // reference.
+ let inner = unsafe { core::ptr::addr_of!((*outer).$($f)*) } as *const u8;
+ // To avoid warnings when nesting `unsafe` blocks.
+ #[allow(unused_unsafe)]
+ // SAFETY: The two pointers are within the same allocation block.
+ unsafe { inner.offset_from(outer as *const u8) }
+ }}
+}
+
+/// Produces a pointer to an object from a pointer to one of its fields.
+///
+/// # Safety
+///
+/// Callers must ensure that the pointer to the field is in fact a pointer to the specified field,
+/// as opposed to a pointer to another object of the same type.
+///
+/// # Example
+///
+/// ```
+/// struct Test {
+/// a: u64,
+/// b: u32,
+/// }
+///
+/// fn test() {
+/// let test = Test { a: 10, b: 20 };
+/// let b_ptr = &test.b;
+/// let test_alias = unsafe { container_of!(b_ptr, Test, b) };
+/// // This prints `true`.
+/// pr_info!("{}\n", core::ptr::eq(&test, test_alias));
+/// }
+/// ```
+#[macro_export]
+macro_rules! container_of {
+ ($ptr:expr, $type:ty, $($f:tt)*) => {{
+ let offset = $crate::offset_of!($type, $($f)*);
+ ($ptr as *const _ as *const u8).offset(-offset) as *const $type
+ }}
+}
+
+#[global_allocator]
+static ALLOCATOR: allocator::KernelAllocator = allocator::KernelAllocator;
diff --git a/rust/kernel/miscdev.rs b/rust/kernel/miscdev.rs
new file mode 100644
index 000000000000..92c2181f3053
--- /dev/null
+++ b/rust/kernel/miscdev.rs
@@ -0,0 +1,109 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Miscellaneous devices.
+//!
+//! C header: [`include/linux/miscdevice.h`](../../../../include/linux/miscdevice.h)
+//!
+//! Reference: <https://www.kernel.org/doc/html/latest/driver-api/misc_devices.html>
+
+use crate::error::{Error, KernelResult};
+use crate::file_operations::{FileOpenAdapter, FileOpener, FileOperationsVtable};
+use crate::{bindings, c_types, CStr};
+use alloc::boxed::Box;
+use core::marker::PhantomPinned;
+use core::pin::Pin;
+
+/// A registration of a miscellaneous device.
+pub struct Registration<T: Sync = ()> {
+ registered: bool,
+ mdev: bindings::miscdevice,
+ _pin: PhantomPinned,
+
+ /// Context initialised on construction and made available to all file instances on
+ /// [`FileOpener::open`].
+ pub context: T,
+}
+
+impl<T: Sync> Registration<T> {
+ /// Creates a new [`Registration`] but does not register it yet.
+ ///
+ /// It is allowed to move.
+ pub fn new(context: T) -> Self {
+ Self {
+ registered: false,
+ mdev: bindings::miscdevice::default(),
+ _pin: PhantomPinned,
+ context,
+ }
+ }
+
+ /// Registers a miscellaneous device.
+ ///
+ /// Returns a pinned heap-allocated representation of the registration.
+ pub fn new_pinned<F: FileOpener<T>>(
+ name: CStr<'static>,
+ minor: Option<i32>,
+ context: T,
+ ) -> KernelResult<Pin<Box<Self>>> {
+ let mut r = Pin::from(Box::try_new(Self::new(context))?);
+ r.as_mut().register::<F>(name, minor)?;
+ Ok(r)
+ }
+
+ /// Registers a miscellaneous device with the rest of the kernel.
+ ///
+ /// It must be pinned because the memory block that represents the registration is
+ /// self-referential. If a minor is not given, the kernel allocates a new one if possible.
+ pub fn register<F: FileOpener<T>>(
+ self: Pin<&mut Self>,
+ name: CStr<'static>,
+ minor: Option<i32>,
+ ) -> KernelResult {
+ // SAFETY: We must ensure that we never move out of `this`.
+ let this = unsafe { self.get_unchecked_mut() };
+ if this.registered {
+ // Already registered.
+ return Err(Error::EINVAL);
+ }
+
+ // SAFETY: The adapter is compatible with `misc_register`.
+ this.mdev.fops = unsafe { FileOperationsVtable::<Self, F>::build() };
+ this.mdev.name = name.as_ptr() as *const c_types::c_char;
+ this.mdev.minor = minor.unwrap_or(bindings::MISC_DYNAMIC_MINOR as i32);
+
+ let ret = unsafe { bindings::misc_register(&mut this.mdev) };
+ if ret < 0 {
+ return Err(Error::from_kernel_errno(ret));
+ }
+ this.registered = true;
+ Ok(())
+ }
+}
+
+impl<T: Sync> FileOpenAdapter for Registration<T> {
+ type Arg = T;
+
+ unsafe fn convert(_inode: *mut bindings::inode, file: *mut bindings::file) -> *const Self::Arg {
+ let reg = crate::container_of!((*file).private_data, Self, mdev);
+ &(*reg).context
+ }
+}
+
+// SAFETY: The only method is `register()`, which requires a (pinned) mutable `Registration`, so it
+// is safe to pass `&Registration` to multiple threads because it offers no interior mutability,
+// except maybe through `Registration::context`, but it is itself `Sync`.
+unsafe impl<T: Sync> Sync for Registration<T> {}
+
+// SAFETY: All functions work from any thread. So as long as the `Registration::context` is
+// `Send`, so is `Registration<T>`. `T` needs to be `Sync` because it's a requirement of
+// `Registration<T>`.
+unsafe impl<T: Send + Sync> Send for Registration<T> {}
+
+impl<T: Sync> Drop for Registration<T> {
+ /// Removes the registration from the kernel if it has completed successfully before.
+ fn drop(&mut self) {
+ if self.registered {
+ unsafe { bindings::misc_deregister(&mut self.mdev) }
+ }
+ }
+}
diff --git a/rust/kernel/module_param.rs b/rust/kernel/module_param.rs
new file mode 100644
index 000000000000..e8d51fe613f5
--- /dev/null
+++ b/rust/kernel/module_param.rs
@@ -0,0 +1,497 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Types for module parameters.
+//!
+//! C header: [`include/linux/moduleparam.h`](../../../include/linux/moduleparam.h)
+
+use core::fmt::Write;
+
+/// Types that can be used for module parameters.
+///
+/// Note that displaying the type in `sysfs` will fail if
+/// [`alloc::string::ToString::to_string`] (as implemented through the
+/// [`core::fmt::Display`] trait) writes more than [`PAGE_SIZE`]
+/// bytes (including an additional null terminator).
+///
+/// [`PAGE_SIZE`]: `crate::PAGE_SIZE`
+pub trait ModuleParam: core::fmt::Display + core::marker::Sized {
+ /// The `ModuleParam` will be used by the kernel module through this type.
+ ///
+ /// This may differ from `Self` if, for example, `Self` needs to track
+ /// ownership without exposing it or allocate extra space for other possible
+ /// parameter values. See [`StringParam`] or [`ArrayParam`] for examples.
+ type Value: ?Sized;
+
+ /// Whether the parameter is allowed to be set without an argument.
+ ///
+ /// Setting this to `true` allows the parameter to be passed without an
+ /// argument (e.g. just `module.param` instead of `module.param=foo`).
+ const NOARG_ALLOWED: bool;
+
+ /// Convert a parameter argument into the parameter value.
+ ///
+ /// `None` should be returned when parsing of the argument fails.
+ /// `arg == None` indicates that the parameter was passed without an
+ /// argument. If `NOARG_ALLOWED` is set to `false` then `arg` is guaranteed
+ /// to always be `Some(_)`.
+ ///
+ /// Parameters passed at boot time will be set before [`kmalloc`] is
+ /// available (even if the module is loaded at a later time). However, in
+ /// this case, the argument buffer will be valid for the entire lifetime of
+ /// the kernel. So implementations of this method which need to allocate
+ /// should first check that the allocator is available (with
+ /// [`crate::bindings::slab_is_available`]) and when it is not available
+ /// provide an alternative implementation which doesn't allocate. In cases
+ /// where the allocator is not available it is safe to save references to
+ /// `arg` in `Self`, but in other cases a copy should be made.
+ ///
+ /// [`kmalloc`]: ../../../include/linux/slab.h
+ fn try_from_param_arg(arg: Option<&'static [u8]>) -> Option<Self>;
+
+ /// Get the current value of the parameter for use in the kernel module.
+ ///
+ /// This function should not be used directly. Instead use the wrapper
+ /// `read` which will be generated by [`module::module`].
+ fn value(&self) -> &Self::Value;
+
+ /// Set the module parameter from a string.
+ ///
+ /// Used to set the parameter value when loading the module or when set
+ /// through `sysfs`.
+ ///
+ /// # Safety
+ ///
+ /// If `val` is non-null then it must point to a valid null-terminated
+ /// string. The `arg` field of `param` must be an instance of `Self`.
+ unsafe extern "C" fn set_param(
+ val: *const crate::c_types::c_char,
+ param: *const crate::bindings::kernel_param,
+ ) -> crate::c_types::c_int {
+ let arg = if val.is_null() {
+ None
+ } else {
+ Some(crate::c_types::c_string_bytes(val))
+ };
+ match Self::try_from_param_arg(arg) {
+ Some(new_value) => {
+ let old_value = (*param).__bindgen_anon_1.arg as *mut Self;
+ let _ = core::ptr::replace(old_value, new_value);
+ 0
+ }
+ None => crate::error::Error::EINVAL.to_kernel_errno(),
+ }
+ }
+
+ /// Write a string representation of the current parameter value to `buf`.
+ ///
+ /// Used for displaying the current parameter value in `sysfs`.
+ ///
+ /// # Safety
+ ///
+ /// `buf` must be a buffer of length at least `kernel::PAGE_SIZE` that is
+ /// writeable. The `arg` field of `param` must be an instance of `Self`.
+ unsafe extern "C" fn get_param(
+ buf: *mut crate::c_types::c_char,
+ param: *const crate::bindings::kernel_param,
+ ) -> crate::c_types::c_int {
+ let slice = core::slice::from_raw_parts_mut(buf as *mut u8, crate::PAGE_SIZE);
+ let mut buf = crate::buffer::Buffer::new(slice);
+ match write!(buf, "{}\0", *((*param).__bindgen_anon_1.arg as *mut Self)) {
+ Err(_) => crate::error::Error::EINVAL.to_kernel_errno(),
+ Ok(()) => buf.bytes_written() as crate::c_types::c_int,
+ }
+ }
+
+ /// Drop the parameter.
+ ///
+ /// Called when unloading a module.
+ ///
+ /// # Safety
+ ///
+ /// The `arg` field of `param` must be an instance of `Self`.
+ unsafe extern "C" fn free(arg: *mut crate::c_types::c_void) {
+ core::ptr::drop_in_place(arg as *mut Self);
+ }
+}
+
+/// Trait for parsing integers.
+///
+/// Strings begining with `0x`, `0o`, or `0b` are parsed as hex, octal, or
+/// binary respectively. Strings beginning with `0` otherwise are parsed as
+/// octal. Anything else is parsed as decimal. A leading `+` or `-` is also
+/// permitted. Any string parsed by [`kstrtol()`] or [`kstrtoul()`] will be
+/// successfully parsed.
+///
+/// [`kstrtol()`]: https://www.kernel.org/doc/html/latest/core-api/kernel-api.html#c.kstrtol
+/// [`kstrtoul()`]: https://www.kernel.org/doc/html/latest/core-api/kernel-api.html#c.kstrtoul
+trait ParseInt: Sized {
+ fn from_str_radix(src: &str, radix: u32) -> Result<Self, core::num::ParseIntError>;
+ fn checked_neg(self) -> Option<Self>;
+
+ fn from_str_unsigned(src: &str) -> Result<Self, core::num::ParseIntError> {
+ let (radix, digits) = if let Some(n) = src.strip_prefix("0x") {
+ (16, n)
+ } else if let Some(n) = src.strip_prefix("0X") {
+ (16, n)
+ } else if let Some(n) = src.strip_prefix("0o") {
+ (8, n)
+ } else if let Some(n) = src.strip_prefix("0O") {
+ (8, n)
+ } else if let Some(n) = src.strip_prefix("0b") {
+ (2, n)
+ } else if let Some(n) = src.strip_prefix("0B") {
+ (2, n)
+ } else if src.starts_with('0') {
+ (8, src)
+ } else {
+ (10, src)
+ };
+ Self::from_str_radix(digits, radix)
+ }
+
+ fn from_str(src: &str) -> Option<Self> {
+ match src.bytes().next() {
+ None => None,
+ Some(b'-') => Self::from_str_unsigned(&src[1..]).ok()?.checked_neg(),
+ Some(b'+') => Some(Self::from_str_unsigned(&src[1..]).ok()?),
+ Some(_) => Some(Self::from_str_unsigned(src).ok()?),
+ }
+ }
+}
+
+macro_rules! impl_parse_int {
+ ($ty:ident) => {
+ impl ParseInt for $ty {
+ fn from_str_radix(src: &str, radix: u32) -> Result<Self, core::num::ParseIntError> {
+ $ty::from_str_radix(src, radix)
+ }
+
+ fn checked_neg(self) -> Option<Self> {
+ self.checked_neg()
+ }
+ }
+ };
+}
+
+impl_parse_int!(i8);
+impl_parse_int!(u8);
+impl_parse_int!(i16);
+impl_parse_int!(u16);
+impl_parse_int!(i32);
+impl_parse_int!(u32);
+impl_parse_int!(i64);
+impl_parse_int!(u64);
+impl_parse_int!(isize);
+impl_parse_int!(usize);
+
+macro_rules! impl_module_param {
+ ($ty:ident) => {
+ impl ModuleParam for $ty {
+ type Value = $ty;
+
+ const NOARG_ALLOWED: bool = false;
+
+ fn try_from_param_arg(arg: Option<&'static [u8]>) -> Option<Self> {
+ let bytes = arg?;
+ let utf8 = core::str::from_utf8(bytes).ok()?;
+ <$ty as crate::module_param::ParseInt>::from_str(utf8)
+ }
+
+ fn value(&self) -> &Self::Value {
+ self
+ }
+ }
+ };
+}
+
+#[doc(hidden)]
+#[macro_export]
+/// Generate a static [`kernel_param_ops`](../../../include/linux/moduleparam.h) struct.
+///
+/// # Example
+/// ```rust
+/// make_param_ops!(
+/// /// Documentation for new param ops.
+/// PARAM_OPS_MYTYPE, // Name for the static.
+/// MyType // A type which implements [`ModuleParam`].
+/// );
+/// ```
+macro_rules! make_param_ops {
+ ($ops:ident, $ty:ty) => {
+ $crate::make_param_ops!(
+ #[doc=""]
+ $ops,
+ $ty
+ );
+ };
+ ($(#[$meta:meta])* $ops:ident, $ty:ty) => {
+ $(#[$meta])*
+ ///
+ /// Static [`kernel_param_ops`](../../../include/linux/moduleparam.h)
+ /// struct generated by [`make_param_ops`].
+ pub static $ops: $crate::bindings::kernel_param_ops = $crate::bindings::kernel_param_ops {
+ flags: if <$ty as $crate::module_param::ModuleParam>::NOARG_ALLOWED {
+ $crate::bindings::KERNEL_PARAM_OPS_FL_NOARG
+ } else {
+ 0
+ },
+ set: Some(<$ty as $crate::module_param::ModuleParam>::set_param),
+ get: Some(<$ty as $crate::module_param::ModuleParam>::get_param),
+ free: Some(<$ty as $crate::module_param::ModuleParam>::free),
+ };
+ };
+}
+
+impl_module_param!(i8);
+impl_module_param!(u8);
+impl_module_param!(i16);
+impl_module_param!(u16);
+impl_module_param!(i32);
+impl_module_param!(u32);
+impl_module_param!(i64);
+impl_module_param!(u64);
+impl_module_param!(isize);
+impl_module_param!(usize);
+
+make_param_ops!(
+ /// Rust implementation of [`kernel_param_ops`](../../../include/linux/moduleparam.h)
+ /// for [`i8`].
+ PARAM_OPS_I8,
+ i8
+);
+make_param_ops!(
+ /// Rust implementation of [`kernel_param_ops`](../../../include/linux/moduleparam.h)
+ /// for [`u8`].
+ PARAM_OPS_U8,
+ u8
+);
+make_param_ops!(
+ /// Rust implementation of [`kernel_param_ops`](../../../include/linux/moduleparam.h)
+ /// for [`i16`].
+ PARAM_OPS_I16,
+ i16
+);
+make_param_ops!(
+ /// Rust implementation of [`kernel_param_ops`](../../../include/linux/moduleparam.h)
+ /// for [`u16`].
+ PARAM_OPS_U16,
+ u16
+);
+make_param_ops!(
+ /// Rust implementation of [`kernel_param_ops`](../../../include/linux/moduleparam.h)
+ /// for [`i32`].
+ PARAM_OPS_I32,
+ i32
+);
+make_param_ops!(
+ /// Rust implementation of [`kernel_param_ops`](../../../include/linux/moduleparam.h)
+ /// for [`u32`].
+ PARAM_OPS_U32,
+ u32
+);
+make_param_ops!(
+ /// Rust implementation of [`kernel_param_ops`](../../../include/linux/moduleparam.h)
+ /// for [`i64`].
+ PARAM_OPS_I64,
+ i64
+);
+make_param_ops!(
+ /// Rust implementation of [`kernel_param_ops`](../../../include/linux/moduleparam.h)
+ /// for [`u64`].
+ PARAM_OPS_U64,
+ u64
+);
+make_param_ops!(
+ /// Rust implementation of [`kernel_param_ops`](../../../include/linux/moduleparam.h)
+ /// for [`isize`].
+ PARAM_OPS_ISIZE,
+ isize
+);
+make_param_ops!(
+ /// Rust implementation of [`kernel_param_ops`](../../../include/linux/moduleparam.h)
+ /// for [`usize`].
+ PARAM_OPS_USIZE,
+ usize
+);
+
+impl ModuleParam for bool {
+ type Value = bool;
+
+ const NOARG_ALLOWED: bool = true;
+
+ fn try_from_param_arg(arg: Option<&'static [u8]>) -> Option<Self> {
+ match arg {
+ None => Some(true),
+ Some(b"y") | Some(b"Y") | Some(b"1") | Some(b"true") => Some(true),
+ Some(b"n") | Some(b"N") | Some(b"0") | Some(b"false") => Some(false),
+ _ => None,
+ }
+ }
+
+ fn value(&self) -> &Self::Value {
+ self
+ }
+}
+
+make_param_ops!(
+ /// Rust implementation of [`kernel_param_ops`](../../../include/linux/moduleparam.h)
+ /// for [`bool`].
+ PARAM_OPS_BOOL,
+ bool
+);
+
+/// An array of at __most__ `N` values.
+///
+/// # Invariant
+///
+/// The first `self.used` elements of `self.values` are initialized.
+pub struct ArrayParam<T, const N: usize> {
+ values: [core::mem::MaybeUninit<T>; N],
+ used: usize,
+}
+
+impl<T, const N: usize> ArrayParam<T, { N }> {
+ fn values(&self) -> &[T] {
+ // SAFETY: The invariant maintained by `ArrayParam` allows us to cast
+ // the first `self.used` elements to `T`.
+ unsafe {
+ &*(&self.values[0..self.used] as *const [core::mem::MaybeUninit<T>] as *const [T])
+ }
+ }
+}
+
+impl<T: Copy, const N: usize> ArrayParam<T, { N }> {
+ const fn new() -> Self {
+ // INVARIANT: The first `self.used` elements of `self.values` are
+ // initialized.
+ ArrayParam {
+ values: [core::mem::MaybeUninit::uninit(); N],
+ used: 0,
+ }
+ }
+
+ const fn push(&mut self, val: T) {
+ if self.used < N {
+ // INVARIANT: The first `self.used` elements of `self.values` are
+ // initialized.
+ self.values[self.used] = core::mem::MaybeUninit::new(val);
+ self.used += 1;
+ }
+ }
+
+ /// Create an instance of `ArrayParam` initialized with `vals`.
+ ///
+ /// This function is only meant to be used in the [`module::module`] macro.
+ pub const fn create(vals: &[T]) -> Self {
+ let mut result = ArrayParam::new();
+ let mut i = 0;
+ while i < vals.len() {
+ result.push(vals[i]);
+ i += 1;
+ }
+ result
+ }
+}
+
+impl<T: core::fmt::Display, const N: usize> core::fmt::Display for ArrayParam<T, { N }> {
+ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+ for val in self.values() {
+ write!(f, "{},", val)?;
+ }
+ Ok(())
+ }
+}
+
+impl<T: Copy + core::fmt::Display + ModuleParam, const N: usize> ModuleParam
+ for ArrayParam<T, { N }>
+{
+ type Value = [T];
+
+ const NOARG_ALLOWED: bool = false;
+
+ fn try_from_param_arg(arg: Option<&'static [u8]>) -> Option<Self> {
+ arg.and_then(|args| {
+ let mut result = Self::new();
+ for arg in args.split(|b| *b == b',') {
+ result.push(T::try_from_param_arg(Some(arg))?);
+ }
+ Some(result)
+ })
+ }
+
+ fn value(&self) -> &Self::Value {
+ self.values()
+ }
+}
+
+/// A C-style string parameter.
+///
+/// The Rust version of the [`charp`] parameter. This type is meant to be
+/// used by the [`module::module`] macro, not handled directly. Instead use the
+/// `read` method generated by that macro.
+///
+/// [`charp`]: ../../../include/linux/moduleparam.h
+pub enum StringParam {
+ /// A borrowed parameter value.
+ ///
+ /// Either the default value (which is static in the module) or borrowed
+ /// from the original argument buffer used to set the value.
+ Ref(&'static [u8]),
+
+ /// A value that was allocated when the parameter was set.
+ ///
+ /// The value needs to be freed when the parameter is reset or the module is
+ /// unloaded.
+ Owned(alloc::vec::Vec<u8>),
+}
+
+impl StringParam {
+ fn bytes(&self) -> &[u8] {
+ match self {
+ StringParam::Ref(bytes) => *bytes,
+ StringParam::Owned(vec) => &vec[..],
+ }
+ }
+}
+
+impl core::fmt::Display for StringParam {
+ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+ let bytes = self.bytes();
+ match core::str::from_utf8(bytes) {
+ Ok(utf8) => write!(f, "{}", utf8),
+ Err(_) => write!(f, "{:?}", bytes),
+ }
+ }
+}
+
+impl ModuleParam for StringParam {
+ type Value = [u8];
+
+ const NOARG_ALLOWED: bool = false;
+
+ fn try_from_param_arg(arg: Option<&'static [u8]>) -> Option<Self> {
+ // SAFETY: It is always safe to call [`slab_is_available`](../../../include/linux/slab.h).
+ let slab_available = unsafe { crate::bindings::slab_is_available() };
+ arg.and_then(|arg| {
+ if slab_available {
+ let mut vec = alloc::vec::Vec::new();
+ vec.try_reserve_exact(arg.len()).ok()?;
+ vec.extend_from_slice(arg);
+ Some(StringParam::Owned(vec))
+ } else {
+ Some(StringParam::Ref(arg))
+ }
+ })
+ }
+
+ fn value(&self) -> &Self::Value {
+ self.bytes()
+ }
+}
+
+make_param_ops!(
+ /// Rust implementation of [`kernel_param_ops`](../../../include/linux/moduleparam.h)
+ /// for [`StringParam`].
+ PARAM_OPS_STR,
+ StringParam
+);
diff --git a/rust/kernel/prelude.rs b/rust/kernel/prelude.rs
new file mode 100644
index 000000000000..06046bf18a4a
--- /dev/null
+++ b/rust/kernel/prelude.rs
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! The `kernel` prelude.
+//!
+//! These are most common items used by Rust code in the kernel, intended to
+//! be imported by all Rust code, for convenience.
+//!
+//! # Examples
+//!
+//! ```rust,no_run
+//! use kernel::prelude::*;
+//! ```
+
+pub use alloc::{borrow::ToOwned, string::String};
+
+pub use module::module;
+
+pub use super::{pr_alert, pr_cont, pr_crit, pr_emerg, pr_err, pr_info, pr_notice, pr_warn};
+
+pub use super::static_assert;
+
+pub use super::{KernelModule, KernelResult};
diff --git a/rust/kernel/print.rs b/rust/kernel/print.rs
new file mode 100644
index 000000000000..71a5ebe500d7
--- /dev/null
+++ b/rust/kernel/print.rs
@@ -0,0 +1,461 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Printing facilities.
+//!
+//! C header: [`include/linux/printk.h`](../../../../include/linux/printk.h)
+//!
+//! Reference: <https://www.kernel.org/doc/html/latest/core-api/printk-basics.html>
+
+use core::cmp;
+use core::fmt;
+
+use crate::bindings;
+use crate::c_types::c_int;
+
+/// Format strings.
+///
+/// Public but hidden since it should only be used from public macros.
+#[doc(hidden)]
+pub mod format_strings {
+ use crate::bindings;
+
+ /// The length we copy from the `KERN_*` kernel prefixes.
+ const LENGTH_PREFIX: usize = 2;
+
+ /// The length of the fixed format strings.
+ pub const LENGTH: usize = 11;
+
+ /// Generates a fixed format string for the kernel's [`printk`].
+ ///
+ /// The format string is always the same for a given level, i.e. for a
+ /// given `prefix`, which are the kernel's `KERN_*` constants.
+ ///
+ /// [`printk`]: ../../../../include/linux/printk.h
+ const fn generate(is_cont: bool, prefix: &[u8; 3]) -> [u8; LENGTH] {
+ // Ensure the `KERN_*` macros are what we expect.
+ assert!(prefix[0] == b'\x01');
+ if is_cont {
+ assert!(prefix[1] == b'c');
+ } else {
+ assert!(prefix[1] >= b'0' && prefix[1] <= b'7');
+ }
+ assert!(prefix[2] == b'\x00');
+
+ let suffix: &[u8; LENGTH - LENGTH_PREFIX] = if is_cont {
+ b"%.*s\0\0\0\0\0"
+ } else {
+ b"%s: %.*s\0"
+ };
+
+ [
+ prefix[0], prefix[1], suffix[0], suffix[1], suffix[2], suffix[3], suffix[4], suffix[5],
+ suffix[6], suffix[7], suffix[8],
+ ]
+ }
+
+ // Generate the format strings at compile-time.
+ //
+ // This avoids the compiler generating the contents on the fly in the stack.
+ //
+ // Furthermore, `static` instead of `const` is used to share the strings
+ // for all the kernel.
+ pub static EMERG: [u8; LENGTH] = generate(false, bindings::KERN_EMERG);
+ pub static ALERT: [u8; LENGTH] = generate(false, bindings::KERN_ALERT);
+ pub static CRIT: [u8; LENGTH] = generate(false, bindings::KERN_CRIT);
+ pub static ERR: [u8; LENGTH] = generate(false, bindings::KERN_ERR);
+ pub static WARNING: [u8; LENGTH] = generate(false, bindings::KERN_WARNING);
+ pub static NOTICE: [u8; LENGTH] = generate(false, bindings::KERN_NOTICE);
+ pub static INFO: [u8; LENGTH] = generate(false, bindings::KERN_INFO);
+ pub static DEBUG: [u8; LENGTH] = generate(false, bindings::KERN_DEBUG);
+ pub static CONT: [u8; LENGTH] = generate(true, bindings::KERN_CONT);
+}
+
+/// Prints a message via the kernel's [`printk`].
+///
+/// Public but hidden since it should only be used from public macros.
+///
+/// # Safety
+///
+/// The format string must be one of the ones in [`format_strings`], and
+/// the module name must be null-terminated.
+///
+/// [`printk`]: ../../../../include/linux/printk.h
+#[doc(hidden)]
+pub unsafe fn call_printk(
+ format_string: &[u8; format_strings::LENGTH],
+ module_name: &[u8],
+ string: &[u8],
+) {
+ // `printk` does not seem to fail in any path.
+ bindings::printk(
+ format_string.as_ptr() as _,
+ module_name.as_ptr(),
+ string.len() as c_int,
+ string.as_ptr(),
+ );
+}
+
+/// Prints a message via the kernel's [`printk`] for the `CONT` level.
+///
+/// Public but hidden since it should only be used from public macros.
+///
+/// [`printk`]: ../../../../include/linux/printk.h
+#[doc(hidden)]
+pub fn call_printk_cont(string: &[u8]) {
+ // `printk` does not seem to fail in any path.
+ //
+ // SAFETY: The format string is fixed.
+ unsafe {
+ bindings::printk(
+ format_strings::CONT.as_ptr() as _,
+ string.len() as c_int,
+ string.as_ptr(),
+ );
+ }
+}
+
+/// The maximum size of a log line in the kernel.
+///
+/// From `kernel/printk/printk.c`.
+const LOG_LINE_MAX: usize = 1024 - 32;
+
+/// The maximum size of a log line in our side.
+///
+/// FIXME: We should be smarter than this, but for the moment, to reduce stack
+/// usage, we only allow this much which should work for most purposes.
+const LOG_LINE_SIZE: usize = 300;
+crate::static_assert!(LOG_LINE_SIZE <= LOG_LINE_MAX);
+
+/// Public but hidden since it should only be used from public macros.
+#[doc(hidden)]
+pub struct LogLineWriter {
+ data: [u8; LOG_LINE_SIZE],
+ pos: usize,
+}
+
+impl LogLineWriter {
+ /// Creates a new [`LogLineWriter`].
+ pub fn new() -> LogLineWriter {
+ LogLineWriter {
+ data: [0u8; LOG_LINE_SIZE],
+ pos: 0,
+ }
+ }
+
+ /// Returns the internal buffer as a byte slice.
+ pub fn as_bytes(&self) -> &[u8] {
+ &self.data[..self.pos]
+ }
+}
+
+impl Default for LogLineWriter {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+impl fmt::Write for LogLineWriter {
+ fn write_str(&mut self, s: &str) -> fmt::Result {
+ let copy_len = cmp::min(LOG_LINE_SIZE - self.pos, s.as_bytes().len());
+ self.data[self.pos..self.pos + copy_len].copy_from_slice(&s.as_bytes()[..copy_len]);
+ self.pos += copy_len;
+ Ok(())
+ }
+}
+
+/// Helper function for the [`print_macro!`] to reduce stack usage.
+///
+/// Public but hidden since it should only be used from public macros.
+///
+/// # Safety
+///
+/// The format string must be one of the ones in [`format_strings`], and
+/// the module name must be null-terminated.
+#[doc(hidden)]
+pub unsafe fn format_and_call<const CONT: bool>(
+ format_string: &[u8; format_strings::LENGTH],
+ module_name: &[u8],
+ args: fmt::Arguments,
+) {
+ // Careful: this object takes quite a bit of stack.
+ let mut writer = LogLineWriter::new();
+
+ match fmt::write(&mut writer, args) {
+ Ok(_) => {
+ if CONT {
+ call_printk_cont(writer.as_bytes());
+ } else {
+ call_printk(format_string, module_name, writer.as_bytes());
+ }
+ }
+
+ Err(_) => {
+ call_printk(
+ &format_strings::CRIT,
+ module_name,
+ b"Failure to format string.\n",
+ );
+ }
+ };
+}
+
+/// Performs formatting and forwards the string to [`call_printk`].
+///
+/// Public but hidden since it should only be used from public macros.
+#[doc(hidden)]
+#[macro_export]
+macro_rules! print_macro (
+ // Without extra arguments: no need to format anything.
+ ($format_string:path, false, $fmt:expr) => (
+ // SAFETY: This hidden macro should only be called by the documented
+ // printing macros which ensure the format string is one of the fixed
+ // ones. All `__MODULE_NAME`s are null-terminated as they are generated
+ // by the `module!` proc macro.
+ unsafe {
+ kernel::print::call_printk(
+ &$format_string,
+ crate::__MODULE_NAME,
+ $fmt.as_bytes(),
+ );
+ }
+ );
+
+ // Without extra arguments: no need to format anything (`CONT` case).
+ ($format_string:path, true, $fmt:expr) => (
+ kernel::print::call_printk_cont(
+ $fmt.as_bytes(),
+ );
+ );
+
+ // With extra arguments: we need to perform formatting.
+ ($format_string:path, $cont:literal, $fmt:expr, $($arg:tt)*) => (
+ // Forwarding the call to a function to perform the formatting
+ // is needed here to avoid stack overflows in non-optimized builds when
+ // invoking the printing macros a lot of times in the same function.
+ // Without it, the compiler reserves one `LogLineWriter` per macro
+ // invocation, which is a huge type.
+ //
+ // We could use an immediately-invoked closure for this, which
+ // seems to lower even more the stack usage at `opt-level=0` because
+ // `fmt::Arguments` objects do not pile up. However, that breaks
+ // the `?` operator if used in one of the arguments.
+ //
+ // At `opt-level=2`, the generated code is basically the same for
+ // all alternatives.
+ //
+ // SAFETY: This hidden macro should only be called by the documented
+ // printing macros which ensure the format string is one of the fixed
+ // ones. All `__MODULE_NAME`s are null-terminated as they are generated
+ // by the `module!` proc macro.
+ unsafe {
+ kernel::print::format_and_call::<$cont>(
+ &$format_string,
+ crate::__MODULE_NAME,
+ format_args!($fmt, $($arg)*),
+ );
+ }
+ );
+);
+
+// We could use a macro to generate these macros. However, doing so ends
+// up being a bit ugly: it requires the dollar token trick to escape `$` as
+// well as playing with the `doc` attribute. Furthermore, they cannot be easily
+// imported in the prelude due to [1]. So, for the moment, we just write them
+// manually, like in the C side; while keeping most of the logic in another
+// macro, i.e. [`print_macro`].
+//
+// [1]: https://github.com/rust-lang/rust/issues/52234
+
+/// Prints an emergency-level message (level 0).
+///
+/// Use this level if the system is unusable.
+///
+/// Equivalent to the kernel's [`pr_emerg`] macro.
+///
+/// Mimics the interface of [`std::print!`]. See [`core::fmt`] and
+/// [`alloc::format!`] for information about the formatting syntax.
+///
+/// [`pr_emerg`]: https://www.kernel.org/doc/html/latest/core-api/printk-basics.html#c.pr_emerg
+/// [`std::print!`]: https://doc.rust-lang.org/std/macro.print.html
+///
+/// # Examples
+///
+/// ```
+/// pr_emerg!("hello {}\n", "there");
+/// ```
+#[macro_export]
+macro_rules! pr_emerg (
+ ($($arg:tt)*) => (
+ $crate::print_macro!($crate::print::format_strings::EMERG, false, $($arg)*)
+ )
+);
+
+/// Prints an alert-level message (level 1).
+///
+/// Use this level if action must be taken immediately.
+///
+/// Equivalent to the kernel's [`pr_alert`] macro.
+///
+/// Mimics the interface of [`std::print!`]. See [`core::fmt`] and
+/// [`alloc::format!`] for information about the formatting syntax.
+///
+/// [`pr_alert`]: https://www.kernel.org/doc/html/latest/core-api/printk-basics.html#c.pr_alert
+/// [`std::print!`]: https://doc.rust-lang.org/std/macro.print.html
+///
+/// # Examples
+///
+/// ```
+/// pr_alert!("hello {}\n", "there");
+/// ```
+#[macro_export]
+macro_rules! pr_alert (
+ ($($arg:tt)*) => (
+ $crate::print_macro!($crate::print::format_strings::ALERT, false, $($arg)*)
+ )
+);
+
+/// Prints a critical-level message (level 2).
+///
+/// Use this level for critical conditions.
+///
+/// Equivalent to the kernel's [`pr_crit`] macro.
+///
+/// Mimics the interface of [`std::print!`]. See [`core::fmt`] and
+/// [`alloc::format!`] for information about the formatting syntax.
+///
+/// [`pr_crit`]: https://www.kernel.org/doc/html/latest/core-api/printk-basics.html#c.pr_crit
+/// [`std::print!`]: https://doc.rust-lang.org/std/macro.print.html
+///
+/// # Examples
+///
+/// ```
+/// pr_crit!("hello {}\n", "there");
+/// ```
+#[macro_export]
+macro_rules! pr_crit (
+ ($($arg:tt)*) => (
+ $crate::print_macro!($crate::print::format_strings::CRIT, false, $($arg)*)
+ )
+);
+
+/// Prints an error-level message (level 3).
+///
+/// Use this level for error conditions.
+///
+/// Equivalent to the kernel's [`pr_err`] macro.
+///
+/// Mimics the interface of [`std::print!`]. See [`core::fmt`] and
+/// [`alloc::format!`] for information about the formatting syntax.
+///
+/// [`pr_err`]: https://www.kernel.org/doc/html/latest/core-api/printk-basics.html#c.pr_err
+/// [`std::print!`]: https://doc.rust-lang.org/std/macro.print.html
+///
+/// # Examples
+///
+/// ```
+/// pr_err!("hello {}\n", "there");
+/// ```
+#[macro_export]
+macro_rules! pr_err (
+ ($($arg:tt)*) => (
+ $crate::print_macro!($crate::print::format_strings::ERR, false, $($arg)*)
+ )
+);
+
+/// Prints a warning-level message (level 4).
+///
+/// Use this level for warning conditions.
+///
+/// Equivalent to the kernel's [`pr_warn`] macro.
+///
+/// Mimics the interface of [`std::print!`]. See [`core::fmt`] and
+/// [`alloc::format!`] for information about the formatting syntax.
+///
+/// [`pr_warn`]: https://www.kernel.org/doc/html/latest/core-api/printk-basics.html#c.pr_warn
+/// [`std::print!`]: https://doc.rust-lang.org/std/macro.print.html
+///
+/// # Examples
+///
+/// ```
+/// pr_warn!("hello {}\n", "there");
+/// ```
+#[macro_export]
+macro_rules! pr_warn (
+ ($($arg:tt)*) => (
+ $crate::print_macro!($crate::print::format_strings::WARNING, false, $($arg)*)
+ )
+);
+
+/// Prints a notice-level message (level 5).
+///
+/// Use this level for normal but significant conditions.
+///
+/// Equivalent to the kernel's [`pr_notice`] macro.
+///
+/// Mimics the interface of [`std::print!`]. See [`core::fmt`] and
+/// [`alloc::format!`] for information about the formatting syntax.
+///
+/// [`pr_notice`]: https://www.kernel.org/doc/html/latest/core-api/printk-basics.html#c.pr_notice
+/// [`std::print!`]: https://doc.rust-lang.org/std/macro.print.html
+///
+/// # Examples
+///
+/// ```
+/// pr_notice!("hello {}\n", "there");
+/// ```
+#[macro_export]
+macro_rules! pr_notice (
+ ($($arg:tt)*) => (
+ $crate::print_macro!($crate::print::format_strings::NOTICE, false, $($arg)*)
+ )
+);
+
+/// Prints an info-level message (level 6).
+///
+/// Use this level for informational messages.
+///
+/// Equivalent to the kernel's [`pr_info`] macro.
+///
+/// Mimics the interface of [`std::print!`]. See [`core::fmt`] and
+/// [`alloc::format!`] for information about the formatting syntax.
+///
+/// [`pr_info`]: https://www.kernel.org/doc/html/latest/core-api/printk-basics.html#c.pr_info
+/// [`std::print!`]: https://doc.rust-lang.org/std/macro.print.html
+///
+/// # Examples
+///
+/// ```
+/// pr_info!("hello {}\n", "there");
+/// ```
+#[macro_export]
+#[doc(alias = "print")]
+macro_rules! pr_info (
+ ($($arg:tt)*) => (
+ $crate::print_macro!($crate::print::format_strings::INFO, false, $($arg)*)
+ )
+);
+
+/// Continues a previous log message in the same line.
+///
+/// Use only when continuing a previous `pr_*!` macro (e.g. [`pr_info!`]).
+///
+/// Equivalent to the kernel's [`pr_cont`] macro.
+///
+/// Mimics the interface of [`std::print!`]. See [`core::fmt`] and
+/// [`alloc::format!`] for information about the formatting syntax.
+///
+/// [`pr_cont`]: https://www.kernel.org/doc/html/latest/core-api/printk-basics.html#c.pr_cont
+/// [`std::print!`]: https://doc.rust-lang.org/std/macro.print.html
+///
+/// # Examples
+///
+/// ```
+/// pr_info!("hello");
+/// pr_cont!(" {}\n", "there");
+/// ```
+#[macro_export]
+macro_rules! pr_cont (
+ ($($arg:tt)*) => (
+ $crate::print_macro!($crate::print::format_strings::CONT, true, $($arg)*)
+ )
+);
diff --git a/rust/kernel/random.rs b/rust/kernel/random.rs
new file mode 100644
index 000000000000..a7df79c1f7bf
--- /dev/null
+++ b/rust/kernel/random.rs
@@ -0,0 +1,50 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Random numbers.
+//!
+//! C header: [`include/linux/random.h`](../../../../include/linux/random.h)
+
+use core::convert::TryInto;
+
+use crate::{bindings, c_types, error};
+
+/// Fills a byte slice with random bytes generated from the kernel's CSPRNG.
+///
+/// Ensures that the CSPRNG has been seeded before generating any random bytes,
+/// and will block until it is ready.
+pub fn getrandom(dest: &mut [u8]) -> error::KernelResult {
+ let res = unsafe { bindings::wait_for_random_bytes() };
+ if res != 0 {
+ return Err(error::Error::from_kernel_errno(res));
+ }
+
+ unsafe {
+ bindings::get_random_bytes(
+ dest.as_mut_ptr() as *mut c_types::c_void,
+ dest.len().try_into()?,
+ );
+ }
+ Ok(())
+}
+
+/// Fills a byte slice with random bytes generated from the kernel's CSPRNG.
+///
+/// If the CSPRNG is not yet seeded, returns an `Err(EAGAIN)` immediately.
+pub fn getrandom_nonblock(dest: &mut [u8]) -> error::KernelResult {
+ if !unsafe { bindings::rng_is_initialized() } {
+ return Err(error::Error::EAGAIN);
+ }
+ getrandom(dest)
+}
+
+/// Contributes the contents of a byte slice to the kernel's entropy pool.
+///
+/// Does *not* credit the kernel entropy counter though.
+pub fn add_randomness(data: &[u8]) {
+ unsafe {
+ bindings::add_device_randomness(
+ data.as_ptr() as *const c_types::c_void,
+ data.len().try_into().unwrap(),
+ );
+ }
+}
diff --git a/rust/kernel/static_assert.rs b/rust/kernel/static_assert.rs
new file mode 100644
index 000000000000..1d8f137155c6
--- /dev/null
+++ b/rust/kernel/static_assert.rs
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Static assert.
+
+/// Static assert (i.e. compile-time assert).
+///
+/// Similar to C11 [`_Static_assert`] and C++11 [`static_assert`].
+///
+/// The feature may be added to Rust in the future: see [RFC 2790].
+///
+/// [`_Static_assert`]: https://en.cppreference.com/w/c/language/_Static_assert
+/// [`static_assert`]: https://en.cppreference.com/w/cpp/language/static_assert
+/// [RFC 2790]: https://github.com/rust-lang/rfcs/issues/2790
+///
+/// # Examples
+///
+/// ```
+/// static_assert!(42 > 24);
+/// static_assert!(core::mem::size_of::<u8>() == 1);
+///
+/// const X: &[u8] = b"bar";
+/// static_assert!(X[1] == 'a' as u8);
+///
+/// const fn f(x: i32) -> i32 {
+/// x + 2
+/// }
+/// static_assert!(f(40) == 42);
+/// ```
+#[macro_export]
+macro_rules! static_assert {
+ ($condition:expr) => {
+ // Based on the latest one in `rustc`'s one before it was [removed].
+ //
+ // [removed]: https://github.com/rust-lang/rust/commit/c2dad1c6b9f9636198d7c561b47a2974f5103f6d
+ #[allow(dead_code)]
+ const _: () = [()][!($condition) as usize];
+ };
+}
diff --git a/rust/kernel/sync/arc.rs b/rust/kernel/sync/arc.rs
new file mode 100644
index 000000000000..9b3ae4951443
--- /dev/null
+++ b/rust/kernel/sync/arc.rs
@@ -0,0 +1,184 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! A reference-counted pointer.
+//!
+//! This module implements a way for users to create reference-counted objects and pointers to
+//! them. Such a pointer automatically increments and decrements the count, and drops the
+//! underlying object when it reaches zero. It is also safe to use concurrently from multiple
+//! threads.
+//!
+//! It is different from the standard library's [`Arc`] in two ways: it does not support weak
+//! references, which allows it to be smaller -- a single pointer-sized integer; it allows users to
+//! safely increment the reference count from a single reference to the underlying object.
+//!
+//! [`Arc`]: https://doc.rust-lang.org/std/sync/struct.Arc.html
+
+use crate::KernelResult;
+use alloc::boxed::Box;
+use core::{
+ mem::ManuallyDrop,
+ ops::Deref,
+ ptr::NonNull,
+ sync::atomic::{fence, AtomicUsize, Ordering},
+};
+
+/// A reference-counted pointer to an instance of `T`.
+///
+/// The reference count is incremented when new instances of [`Ref`] are created, and decremented
+/// when they are dropped. When the count reaches zero, the underlying `T` is also dropped.
+///
+/// # Invariants
+///
+/// The value stored in [`RefCounted::get_count`] corresponds to the number of instances of [`Ref`]
+/// that point to that instance of `T`.
+pub struct Ref<T: RefCounted + ?Sized> {
+ ptr: NonNull<T>,
+}
+
+// SAFETY: It is safe to send `Ref<T>` to another thread when the underlying `T` is `Sync` because
+// it effectively means sharing `&T` (which is safe because `T` is `Sync`); additionally, it needs
+// `T` to be `Send` because any thread that has a `Ref<T>` may ultimately access `T` directly, for
+// example, when the reference count reaches zero and `T` is dropped.
+unsafe impl<T: RefCounted + ?Sized + Sync + Send> Send for Ref<T> {}
+
+// SAFETY: It is safe to send `&Ref<T>` to another thread when the underlying `T` is `Sync` for
+// the same reason as above. `T` needs to be `Send` as well because a thread can clone a `&Ref<T>`
+// into a `Ref<T>`, which may lead to `T` being accessed by the same reasoning as above.
+unsafe impl<T: RefCounted + ?Sized + Sync + Send> Sync for Ref<T> {}
+
+impl<T: RefCounted> Ref<T> {
+ /// Constructs a new reference counted instance of `T`.
+ pub fn try_new(contents: T) -> KernelResult<Self> {
+ let boxed = Box::try_new(contents)?;
+ boxed.get_count().count.store(1, Ordering::Relaxed);
+ let ptr = NonNull::from(Box::leak(boxed));
+ Ok(Ref { ptr })
+ }
+}
+
+impl<T: RefCounted + ?Sized> Ref<T> {
+ /// Creates a new reference-counted pointer to the given instance of `T`.
+ ///
+ /// It works by incrementing the current reference count as part of constructing the new
+ /// pointer.
+ pub fn new_from(obj: &T) -> Self {
+ let ref_count = obj.get_count();
+ let cur = ref_count.count.fetch_add(1, Ordering::Relaxed);
+ if cur == usize::MAX {
+ panic!("Reference count overflowed");
+ }
+ Self {
+ ptr: NonNull::from(obj),
+ }
+ }
+
+ /// Returns a mutable reference to `T` iff the reference count is one. Otherwise returns
+ /// [`None`].
+ pub fn get_mut(&mut self) -> Option<&mut T> {
+ // Synchronises with the decrement in `drop`.
+ if self.get_count().count.load(Ordering::Acquire) != 1 {
+ return None;
+ }
+ // SAFETY: Since there is only one reference, we know it isn't possible for another thread
+ // to concurrently call this.
+ Some(unsafe { self.ptr.as_mut() })
+ }
+
+ /// Determines if two reference-counted pointers point to the same underlying instance of `T`.
+ pub fn ptr_eq(a: &Self, b: &Self) -> bool {
+ core::ptr::eq(a.ptr.as_ptr(), b.ptr.as_ptr())
+ }
+
+ /// Deconstructs a [`Ref`] object into a raw pointer.
+ ///
+ /// It can be reconstructed once via [`Ref::from_raw`].
+ pub fn into_raw(obj: Self) -> *const T {
+ let no_drop = ManuallyDrop::new(obj);
+ no_drop.ptr.as_ptr()
+ }
+
+ /// Recreates a [`Ref`] instance previously deconstructed via [`Ref::into_raw`].
+ ///
+ /// # Safety
+ ///
+ /// `ptr` must have been returned by a previous call to [`Ref::into_raw`]. Additionally, it
+ /// can only be called once for each previous call to [``Ref::into_raw`].
+ pub unsafe fn from_raw(ptr: *const T) -> Self {
+ Ref {
+ ptr: NonNull::new(ptr as _).unwrap(),
+ }
+ }
+}
+
+impl<T: RefCounted + ?Sized> Deref for Ref<T> {
+ type Target = T;
+
+ fn deref(&self) -> &Self::Target {
+ // SAFETY: By the type invariant, there is necessarily a reference to the object, so it is
+ // safe to dereference it.
+ unsafe { self.ptr.as_ref() }
+ }
+}
+
+impl<T: RefCounted + ?Sized> Clone for Ref<T> {
+ fn clone(&self) -> Self {
+ Self::new_from(self)
+ }
+}
+
+impl<T: RefCounted + ?Sized> Drop for Ref<T> {
+ fn drop(&mut self) {
+ {
+ // SAFETY: By the type invariant, there is necessarily a reference to the object.
+ let obj = unsafe { self.ptr.as_ref() };
+
+ // Synchronises with the acquire below or with the acquire in `get_mut`.
+ if obj.get_count().count.fetch_sub(1, Ordering::Release) != 1 {
+ return;
+ }
+ }
+
+ // Synchronises with the release when decrementing above. This ensures that modifications
+ // from all previous threads/CPUs are visible to the underlying object's `drop`.
+ fence(Ordering::Acquire);
+
+ // The count reached zero, we must free the memory.
+ //
+ // SAFETY: The pointer was initialised from the result of `Box::into_raw`.
+ unsafe { Box::from_raw(self.ptr.as_ptr()) };
+ }
+}
+
+/// Trait for reference counted objects.
+///
+/// # Safety
+///
+/// Implementers of [`RefCounted`] must ensure that all of their constructors call
+/// [`Ref::try_new`].
+pub unsafe trait RefCounted {
+ /// Returns a pointer to the object field holds the reference count.
+ fn get_count(&self) -> &RefCount;
+}
+
+/// Holds the reference count of an object.
+///
+/// It is meant to be embedded in objects to be reference-counted, with [`RefCounted::get_count`]
+/// returning a reference to it.
+pub struct RefCount {
+ count: AtomicUsize,
+}
+
+impl RefCount {
+ /// Constructs a new instance of [`RefCount`].
+ pub fn new() -> Self {
+ Self {
+ count: AtomicUsize::new(1),
+ }
+ }
+}
+
+impl Default for RefCount {
+ fn default() -> Self {
+ Self::new()
+ }
+}
diff --git a/rust/kernel/sync/condvar.rs b/rust/kernel/sync/condvar.rs
new file mode 100644
index 000000000000..6d57fb1daea4
--- /dev/null
+++ b/rust/kernel/sync/condvar.rs
@@ -0,0 +1,138 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! A condition variable.
+//!
+//! This module allows Rust code to use the kernel's [`struct wait_queue_head`] as a condition
+//! variable.
+
+use super::{Guard, Lock, NeedsLockClass};
+use crate::{bindings, c_types, CStr};
+use core::{cell::UnsafeCell, marker::PhantomPinned, mem::MaybeUninit, pin::Pin};
+
+extern "C" {
+ fn rust_helper_init_wait(wq: *mut bindings::wait_queue_entry);
+ fn rust_helper_signal_pending() -> c_types::c_int;
+}
+
+/// Safely initialises a [`CondVar`] with the given name, generating a new lock class.
+#[macro_export]
+macro_rules! condvar_init {
+ ($condvar:expr, $name:literal) => {
+ $crate::init_with_lockdep!($condvar, $name)
+ };
+}
+
+// TODO: `bindgen` is not generating this constant. Figure out why.
+const POLLFREE: u32 = 0x4000;
+
+/// Exposes the kernel's [`struct wait_queue_head`] as a condition variable. It allows the caller to
+/// atomically release the given lock and go to sleep. It reacquires the lock when it wakes up. And
+/// it wakes up when notified by another thread (via [`CondVar::notify_one`] or
+/// [`CondVar::notify_all`]) or because the thread received a signal.
+///
+/// [`struct wait_queue_head`]: ../../../include/linux/wait.h
+pub struct CondVar {
+ pub(crate) wait_list: UnsafeCell<bindings::wait_queue_head>,
+
+ /// A condvar needs to be pinned because it contains a [`struct list_head`] that is
+ /// self-referential, so it cannot be safely moved once it is initialised.
+ _pin: PhantomPinned,
+}
+
+// SAFETY: `CondVar` only uses a `struct wait_queue_head`, which is safe to use on any thread.
+unsafe impl Send for CondVar {}
+
+// SAFETY: `CondVar` only uses a `struct wait_queue_head`, which is safe to use on multiple threads
+// concurrently.
+unsafe impl Sync for CondVar {}
+
+impl CondVar {
+ /// Constructs a new conditional variable.
+ ///
+ /// # Safety
+ ///
+ /// The caller must call `CondVar::init` before using the conditional variable.
+ pub unsafe fn new() -> Self {
+ Self {
+ wait_list: UnsafeCell::new(bindings::wait_queue_head::default()),
+ _pin: PhantomPinned,
+ }
+ }
+
+ /// Atomically releases the given lock (whose ownership is proven by the guard) and puts the
+ /// thread to sleep. It wakes up when notified by [`CondVar::notify_one`] or
+ /// [`CondVar::notify_all`], or when the thread receives a signal.
+ ///
+ /// Returns whether there is a signal pending.
+ #[must_use = "wait returns if a signal is pending, so the caller must check the return value"]
+ pub fn wait<L: Lock>(&self, guard: &mut Guard<L>) -> bool {
+ let lock = guard.lock;
+ let mut wait = MaybeUninit::<bindings::wait_queue_entry>::uninit();
+
+ // SAFETY: `wait` points to valid memory.
+ unsafe { rust_helper_init_wait(wait.as_mut_ptr()) };
+
+ // SAFETY: Both `wait` and `wait_list` point to valid memory.
+ unsafe {
+ bindings::prepare_to_wait_exclusive(
+ self.wait_list.get(),
+ wait.as_mut_ptr(),
+ bindings::TASK_INTERRUPTIBLE as _,
+ );
+ }
+
+ // SAFETY: The guard is evidence that the caller owns the lock.
+ unsafe { lock.unlock() };
+
+ // SAFETY: No arguments, switches to another thread.
+ unsafe { bindings::schedule() };
+
+ lock.lock_noguard();
+
+ // SAFETY: Both `wait` and `wait_list` point to valid memory.
+ unsafe { bindings::finish_wait(self.wait_list.get(), wait.as_mut_ptr()) };
+
+ // SAFETY: No arguments, just checks `current` for pending signals.
+ unsafe { rust_helper_signal_pending() != 0 }
+ }
+
+ /// Calls the kernel function to notify the appropriate number of threads with the given flags.
+ fn notify(&self, count: i32, flags: u32) {
+ // SAFETY: `wait_list` points to valid memory.
+ unsafe {
+ bindings::__wake_up(
+ self.wait_list.get(),
+ bindings::TASK_NORMAL,
+ count,
+ flags as _,
+ )
+ };
+ }
+
+ /// Wakes a single waiter up, if any. This is not 'sticky' in the sense that if no thread is
+ /// waiting, the notification is lost completely (as opposed to automatically waking up the
+ /// next waiter).
+ pub fn notify_one(&self) {
+ self.notify(1, 0);
+ }
+
+ /// Wakes all waiters up, if any. This is not 'sticky' in the sense that if no thread is
+ /// waiting, the notification is lost completely (as opposed to automatically waking up the
+ /// next waiter).
+ pub fn notify_all(&self) {
+ self.notify(0, 0);
+ }
+
+ /// Wakes all waiters up. If they were added by `epoll`, they are also removed from the list of
+ /// waiters. This is useful when cleaning up a condition variable that may be waited on by
+ /// threads that use `epoll`.
+ pub fn free_waiters(&self) {
+ self.notify(1, bindings::POLLHUP | POLLFREE);
+ }
+}
+
+impl NeedsLockClass for CondVar {
+ unsafe fn init(self: Pin<&Self>, name: CStr<'static>, key: *mut bindings::lock_class_key) {
+ bindings::__init_waitqueue_head(self.wait_list.get(), name.as_ptr() as _, key);
+ }
+}
diff --git a/rust/kernel/sync/guard.rs b/rust/kernel/sync/guard.rs
new file mode 100644
index 000000000000..84e5d319a5fd
--- /dev/null
+++ b/rust/kernel/sync/guard.rs
@@ -0,0 +1,82 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! A generic lock guard and trait.
+//!
+//! This module contains a lock guard that can be used with any locking primitive that implements
+//! the ([`Lock`]) trait. It also contains the definition of the trait, which can be leveraged by
+//! other constructs to work on generic locking primitives.
+
+/// Allows mutual exclusion primitives that implement the [`Lock`] trait to automatically unlock
+/// when a guard goes out of scope. It also provides a safe and convenient way to access the data
+/// protected by the lock.
+#[must_use = "the lock unlocks immediately when the guard is unused"]
+pub struct Guard<'a, L: Lock + ?Sized> {
+ pub(crate) lock: &'a L,
+}
+
+// SAFETY: `Guard` is sync when the data protected by the lock is also sync. This is more
+// conservative than the default compiler implementation; more details can be found on
+// https://github.com/rust-lang/rust/issues/41622 -- it refers to `MutexGuard` from the standard
+// library.
+unsafe impl<L> Sync for Guard<'_, L>
+where
+ L: Lock + ?Sized,
+ L::Inner: Sync,
+{
+}
+
+impl<L: Lock + ?Sized> core::ops::Deref for Guard<'_, L> {
+ type Target = L::Inner;
+
+ fn deref(&self) -> &Self::Target {
+ // SAFETY: The caller owns the lock, so it is safe to deref the protected data.
+ unsafe { &*self.lock.locked_data().get() }
+ }
+}
+
+impl<L: Lock + ?Sized> core::ops::DerefMut for Guard<'_, L> {
+ fn deref_mut(&mut self) -> &mut L::Inner {
+ // SAFETY: The caller owns the lock, so it is safe to deref the protected data.
+ unsafe { &mut *self.lock.locked_data().get() }
+ }
+}
+
+impl<L: Lock + ?Sized> Drop for Guard<'_, L> {
+ fn drop(&mut self) {
+ // SAFETY: The caller owns the lock, so it is safe to unlock it.
+ unsafe { self.lock.unlock() };
+ }
+}
+
+impl<'a, L: Lock + ?Sized> Guard<'a, L> {
+ /// Constructs a new lock guard.
+ ///
+ /// # Safety
+ ///
+ /// The caller must ensure that it owns the lock.
+ pub(crate) unsafe fn new(lock: &'a L) -> Self {
+ Self { lock }
+ }
+}
+
+/// A generic mutual exclusion primitive.
+///
+/// [`Guard`] is written such that any mutual exclusion primitive that can implement this trait can
+/// also benefit from having an automatic way to unlock itself.
+pub trait Lock {
+ /// The type of the data protected by the lock.
+ type Inner: ?Sized;
+
+ /// Acquires the lock, making the caller its owner.
+ fn lock_noguard(&self);
+
+ /// Releases the lock, giving up ownership of the lock.
+ ///
+ /// # Safety
+ ///
+ /// It must only be called by the current owner of the lock.
+ unsafe fn unlock(&self);
+
+ /// Returns the data protected by the lock.
+ fn locked_data(&self) -> &core::cell::UnsafeCell<Self::Inner>;
+}
diff --git a/rust/kernel/sync/locked_by.rs b/rust/kernel/sync/locked_by.rs
new file mode 100644
index 000000000000..fc540b35c53a
--- /dev/null
+++ b/rust/kernel/sync/locked_by.rs
@@ -0,0 +1,112 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! A wrapper for data protected by a lock that does not wrap it.
+
+use super::{Guard, Lock};
+use core::{cell::UnsafeCell, ops::Deref, ptr};
+
+/// Allows access to some data to be serialised by a lock that does not wrap it.
+///
+/// In most cases, data protected by a lock is wrapped by the appropriate lock type, e.g.,
+/// [`super::Mutex`] or [`super::SpinLock`]. [`LockedBy`] is meant for cases when this is not
+/// possible. For example, if a container has a lock and some data in the contained elements needs
+/// to be protected by the same lock.
+///
+/// [`LockedBy`] wraps the data in lieu of another locking primitive, and only allows access to it
+/// when the caller shows evidence that 'external' lock is locked.
+///
+/// # Example
+///
+/// The following is an example for illustrative purposes: `InnerDirectory::bytes_used` is an
+/// aggregate of all `InnerFile::bytes_used` and must be kept consistent; so we wrap `InnerFile` in
+/// a `LockedBy` so that it shares a lock with `InnerDirectory`. This allows us to enforce at
+/// compile-time that access to `InnerFile` is only granted when an `InnerDirectory` is also
+/// locked; we enforce at run time that the right `InnerDirectory` is locked.
+///
+/// ```
+/// use super::Mutex;
+/// use alloc::{string::String, vec::Vec};
+///
+/// struct InnerFile {
+/// bytes_used: u64,
+/// }
+///
+/// struct File {
+/// name: String,
+/// inner: LockedBy<InnerFile, Mutex<InnerDirectory>>,
+/// }
+///
+/// struct InnerDirectory {
+/// /// The sum of the bytes used by all files.
+/// bytes_used: u64,
+/// files: Vec<File>,
+/// }
+///
+/// struct Directory {
+/// name: String,
+/// inner: Mutex<InnerDirectory>,
+/// }
+/// ```
+pub struct LockedBy<T: ?Sized, L: Lock + ?Sized> {
+ owner: *const L::Inner,
+ data: UnsafeCell<T>,
+}
+
+// SAFETY: `LockedBy` can be transferred across thread boundaries iff the data it protects can.
+unsafe impl<T: ?Sized + Send, L: Lock + ?Sized> Send for LockedBy<T, L> {}
+
+// SAFETY: `LockedBy` serialises the interior mutability it provides, so it is `Sync` as long as the
+// data it protects is `Send`.
+unsafe impl<T: ?Sized + Send, L: Lock + ?Sized> Sync for LockedBy<T, L> {}
+
+impl<T, L: Lock + ?Sized> LockedBy<T, L> {
+ /// Constructs a new instance of [`LockedBy`].
+ ///
+ /// It stores a raw pointer to the owner that is never dereferenced. It is only used to ensure
+ /// that the right owner is being used to access the protected data. If the owner is freed, the
+ /// data becomes inaccessible; if another instance of the owner is allocated *on the same
+ /// memory location*, the data becomes accessible again: none of this affects memory safety
+ /// because in any case at most one thread (or CPU) can access the protected data at a time.
+ pub fn new(owner: &L, data: T) -> Self {
+ Self {
+ owner: owner.locked_data().get(),
+ data: UnsafeCell::new(data),
+ }
+ }
+}
+
+impl<T: ?Sized, L: Lock + ?Sized> LockedBy<T, L> {
+ /// Returns a reference to the protected data when the caller provides evidence (via a
+ /// [`Guard`]) that the owner is locked.
+ pub fn access<'a>(&'a self, guard: &'a Guard<L>) -> &'a T {
+ if !ptr::eq(guard.deref(), self.owner) {
+ panic!("guard does not match owner");
+ }
+
+ // SAFETY: `guard` is evidence that the owner is locked.
+ unsafe { &mut *self.data.get() }
+ }
+
+ /// Returns a mutable reference to the protected data when the caller provides evidence (via a
+ /// mutable [`Guard`]) that the owner is locked mutably.
+ pub fn access_mut<'a>(&'a self, guard: &'a mut Guard<L>) -> &'a mut T {
+ if !ptr::eq(guard.deref().deref(), self.owner) {
+ panic!("guard does not match owner");
+ }
+
+ // SAFETY: `guard` is evidence that the owner is locked.
+ unsafe { &mut *self.data.get() }
+ }
+
+ /// Returns a mutable reference to the protected data when the caller provides evidence (via a
+ /// mutable owner) that the owner is locked mutably. Showing a mutable reference to the owner
+ /// is sufficient because we know no other references can exist to it.
+ pub fn access_from_mut<'a>(&'a self, owner: &'a mut L::Inner) -> &'a mut T {
+ if !ptr::eq(owner, self.owner) {
+ panic!("mismatched owners");
+ }
+
+ // SAFETY: `owner` is evidence that there is only one reference to the owner.
+ unsafe { &mut *self.data.get() }
+ }
+}
diff --git a/rust/kernel/sync/mod.rs b/rust/kernel/sync/mod.rs
new file mode 100644
index 000000000000..25f5109429a8
--- /dev/null
+++ b/rust/kernel/sync/mod.rs
@@ -0,0 +1,68 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Synchronisation primitives.
+//!
+//! This module contains the kernel APIs related to synchronisation that have been ported or
+//! wrapped for usage by Rust code in the kernel and is shared by all of them.
+//!
+//! # Example
+//!
+//! ```
+//! fn test() {
+//! // SAFETY: `init` is called below.
+//! let data = alloc::sync::Arc::pin(unsafe { Mutex::new(0) });
+//! mutex_init!(data.as_ref(), "test::data");
+//! *data.lock() = 10;
+//! pr_info!("{}\n", *data.lock());
+//! }
+//! ```
+
+use crate::{bindings, CStr};
+use core::pin::Pin;
+
+mod arc;
+mod condvar;
+mod guard;
+mod locked_by;
+mod mutex;
+mod spinlock;
+
+pub use arc::{Ref, RefCount, RefCounted};
+pub use condvar::CondVar;
+pub use guard::{Guard, Lock};
+pub use locked_by::LockedBy;
+pub use mutex::Mutex;
+pub use spinlock::SpinLock;
+
+/// Safely initialises an object that has an `init` function that takes a name and a lock class as
+/// arguments, examples of these are [`Mutex`] and [`SpinLock`]. Each of them also provides a more
+/// specialised name that uses this macro.
+#[doc(hidden)]
+#[macro_export]
+macro_rules! init_with_lockdep {
+ ($obj:expr, $name:literal) => {{
+ static mut CLASS: core::mem::MaybeUninit<$crate::bindings::lock_class_key> =
+ core::mem::MaybeUninit::uninit();
+ // SAFETY: `CLASS` is never used by Rust code directly; the kernel may change it though.
+ #[allow(unused_unsafe)]
+ unsafe {
+ $crate::sync::NeedsLockClass::init($obj, $crate::cstr!($name), CLASS.as_mut_ptr())
+ };
+ }};
+}
+
+/// A trait for types that need a lock class during initialisation.
+///
+/// Implementers of this trait benefit from the [`init_with_lockdep`] macro that generates a new
+/// class for each initialisation call site.
+pub trait NeedsLockClass {
+ /// Initialises the type instance so that it can be safely used.
+ ///
+ /// Callers are encouraged to use the [`init_with_lockdep`] macro as it automatically creates a
+ /// new lock class on each usage.
+ ///
+ /// # Safety
+ ///
+ /// `key` must point to a valid memory location as it will be used by the kernel.
+ unsafe fn init(self: Pin<&Self>, name: CStr<'static>, key: *mut bindings::lock_class_key);
+}
diff --git a/rust/kernel/sync/mutex.rs b/rust/kernel/sync/mutex.rs
new file mode 100644
index 000000000000..e528228d16c1
--- /dev/null
+++ b/rust/kernel/sync/mutex.rs
@@ -0,0 +1,101 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! A kernel mutex.
+//!
+//! This module allows Rust code to use the kernel's [`struct mutex`].
+
+use super::{Guard, Lock, NeedsLockClass};
+use crate::{bindings, CStr};
+use core::{cell::UnsafeCell, marker::PhantomPinned, pin::Pin};
+
+/// Safely initialises a [`Mutex`] with the given name, generating a new lock class.
+#[macro_export]
+macro_rules! mutex_init {
+ ($mutex:expr, $name:literal) => {
+ $crate::init_with_lockdep!($mutex, $name)
+ };
+}
+
+/// Exposes the kernel's [`struct mutex`]. When multiple threads attempt to lock the same mutex,
+/// only one at a time is allowed to progress, the others will block (sleep) until the mutex is
+/// unlocked, at which point another thread will be allowed to wake up and make progress.
+///
+/// A [`Mutex`] must first be initialised with a call to [`Mutex::init`] before it can be used. The
+/// [`mutex_init`] macro is provided to automatically assign a new lock class to a mutex instance.
+///
+/// Since it may block, [`Mutex`] needs to be used with care in atomic contexts.
+///
+/// [`struct mutex`]: ../../../include/linux/mutex.h
+pub struct Mutex<T: ?Sized> {
+ /// The kernel `struct mutex` object.
+ mutex: UnsafeCell<bindings::mutex>,
+
+ /// A mutex needs to be pinned because it contains a [`struct list_head`] that is
+ /// self-referential, so it cannot be safely moved once it is initialised.
+ _pin: PhantomPinned,
+
+ /// The data protected by the mutex.
+ data: UnsafeCell<T>,
+}
+
+// SAFETY: `Mutex` can be transferred across thread boundaries iff the data it protects can.
+unsafe impl<T: ?Sized + Send> Send for Mutex<T> {}
+
+// SAFETY: `Mutex` serialises the interior mutability it provides, so it is `Sync` as long as the
+// data it protects is `Send`.
+unsafe impl<T: ?Sized + Send> Sync for Mutex<T> {}
+
+impl<T> Mutex<T> {
+ /// Constructs a new mutex.
+ ///
+ /// # Safety
+ ///
+ /// The caller must call [`Mutex::init`] before using the mutex.
+ pub unsafe fn new(t: T) -> Self {
+ Self {
+ mutex: UnsafeCell::new(bindings::mutex::default()),
+ data: UnsafeCell::new(t),
+ _pin: PhantomPinned,
+ }
+ }
+}
+
+impl<T: ?Sized> Mutex<T> {
+ /// Locks the mutex and gives the caller access to the data protected by it. Only one thread at
+ /// a time is allowed to access the protected data.
+ pub fn lock(&self) -> Guard<Self> {
+ self.lock_noguard();
+ // SAFETY: The mutex was just acquired.
+ unsafe { Guard::new(self) }
+ }
+}
+
+impl<T: ?Sized> NeedsLockClass for Mutex<T> {
+ unsafe fn init(self: Pin<&Self>, name: CStr<'static>, key: *mut bindings::lock_class_key) {
+ bindings::__mutex_init(self.mutex.get(), name.as_ptr() as _, key);
+ }
+}
+
+impl<T: ?Sized> Lock for Mutex<T> {
+ type Inner = T;
+
+ #[cfg(not(CONFIG_DEBUG_LOCK_ALLOC))]
+ fn lock_noguard(&self) {
+ // SAFETY: `mutex` points to valid memory.
+ unsafe { bindings::mutex_lock(self.mutex.get()) };
+ }
+
+ #[cfg(CONFIG_DEBUG_LOCK_ALLOC)]
+ fn lock_noguard(&self) {
+ // SAFETY: `mutex` points to valid memory.
+ unsafe { bindings::mutex_lock_nested(self.mutex.get(), 0) };
+ }
+
+ unsafe fn unlock(&self) {
+ bindings::mutex_unlock(self.mutex.get());
+ }
+
+ fn locked_data(&self) -> &UnsafeCell<T> {
+ &self.data
+ }
+}
diff --git a/rust/kernel/sync/spinlock.rs b/rust/kernel/sync/spinlock.rs
new file mode 100644
index 000000000000..49a7d5fd837b
--- /dev/null
+++ b/rust/kernel/sync/spinlock.rs
@@ -0,0 +1,108 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! A kernel spinlock.
+//!
+//! This module allows Rust code to use the kernel's [`struct spinlock`].
+//!
+//! See <https://www.kernel.org/doc/Documentation/locking/spinlocks.txt>.
+
+use super::{Guard, Lock, NeedsLockClass};
+use crate::{bindings, c_types, CStr};
+use core::{cell::UnsafeCell, marker::PhantomPinned, pin::Pin};
+
+extern "C" {
+ #[allow(improper_ctypes)]
+ fn rust_helper_spin_lock_init(
+ lock: *mut bindings::spinlock_t,
+ name: *const c_types::c_char,
+ key: *mut bindings::lock_class_key,
+ );
+ fn rust_helper_spin_lock(lock: *mut bindings::spinlock);
+ fn rust_helper_spin_unlock(lock: *mut bindings::spinlock);
+}
+
+/// Safely initialises a [`SpinLock`] with the given name, generating a new lock class.
+#[macro_export]
+macro_rules! spinlock_init {
+ ($spinlock:expr, $name:literal) => {
+ $crate::init_with_lockdep!($spinlock, $name)
+ };
+}
+
+/// Exposes the kernel's [`spinlock_t`]. When multiple CPUs attempt to lock the same spinlock, only
+/// one at a time is allowed to progress, the others will block (spinning) until the spinlock is
+/// unlocked, at which point another CPU will be allowed to make progress.
+///
+/// A [`SpinLock`] must first be initialised with a call to [`SpinLock::init`] before it can be
+/// used. The [`spinlock_init`] macro is provided to automatically assign a new lock class to a
+/// spinlock instance.
+///
+/// [`SpinLock`] does not manage the interrupt state, so it can be used in only two cases: (a) when
+/// the caller knows that interrupts are disabled, or (b) when callers never use it in interrupt
+/// handlers (in which case it is ok for interrupts to be enabled).
+///
+/// [`spinlock_t`]: ../../../include/linux/spinlock.h
+pub struct SpinLock<T: ?Sized> {
+ spin_lock: UnsafeCell<bindings::spinlock>,
+
+ /// Spinlocks are architecture-defined. So we conservatively require them to be pinned in case
+ /// some architecture uses self-references now or in the future.
+ _pin: PhantomPinned,
+
+ data: UnsafeCell<T>,
+}
+
+// SAFETY: `SpinLock` can be transferred across thread boundaries iff the data it protects can.
+unsafe impl<T: ?Sized + Send> Send for SpinLock<T> {}
+
+// SAFETY: `SpinLock` serialises the interior mutability it provides, so it is `Sync` as long as the
+// data it protects is `Send`.
+unsafe impl<T: ?Sized + Send> Sync for SpinLock<T> {}
+
+impl<T> SpinLock<T> {
+ /// Constructs a new spinlock.
+ ///
+ /// # Safety
+ ///
+ /// The caller must call [`SpinLock::init`] before using the spinlock.
+ pub unsafe fn new(t: T) -> Self {
+ Self {
+ spin_lock: UnsafeCell::new(bindings::spinlock::default()),
+ data: UnsafeCell::new(t),
+ _pin: PhantomPinned,
+ }
+ }
+}
+
+impl<T: ?Sized> SpinLock<T> {
+ /// Locks the spinlock and gives the caller access to the data protected by it. Only one thread
+ /// at a time is allowed to access the protected data.
+ pub fn lock(&self) -> Guard<Self> {
+ self.lock_noguard();
+ // SAFETY: The spinlock was just acquired.
+ unsafe { Guard::new(self) }
+ }
+}
+
+impl<T: ?Sized> NeedsLockClass for SpinLock<T> {
+ unsafe fn init(self: Pin<&Self>, name: CStr<'static>, key: *mut bindings::lock_class_key) {
+ rust_helper_spin_lock_init(self.spin_lock.get(), name.as_ptr() as _, key);
+ }
+}
+
+impl<T: ?Sized> Lock for SpinLock<T> {
+ type Inner = T;
+
+ fn lock_noguard(&self) {
+ // SAFETY: `spin_lock` points to valid memory.
+ unsafe { rust_helper_spin_lock(self.spin_lock.get()) };
+ }
+
+ unsafe fn unlock(&self) {
+ rust_helper_spin_unlock(self.spin_lock.get());
+ }
+
+ fn locked_data(&self) -> &UnsafeCell<T> {
+ &self.data
+ }
+}
diff --git a/rust/kernel/sysctl.rs b/rust/kernel/sysctl.rs
new file mode 100644
index 000000000000..a1928a8523db
--- /dev/null
+++ b/rust/kernel/sysctl.rs
@@ -0,0 +1,185 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! System control.
+//!
+//! C header: [`include/linux/sysctl.h`](../../../../include/linux/sysctl.h)
+//!
+//! Reference: <https://www.kernel.org/doc/Documentation/sysctl/README>
+
+use alloc::boxed::Box;
+use alloc::vec;
+use core::mem;
+use core::ptr;
+use core::sync::atomic;
+
+use crate::bindings;
+use crate::c_types;
+use crate::error;
+use crate::types;
+use crate::user_ptr::{UserSlicePtr, UserSlicePtrWriter};
+
+/// Sysctl storage.
+pub trait SysctlStorage: Sync {
+ /// Writes a byte slice.
+ fn store_value(&self, data: &[u8]) -> (usize, error::KernelResult);
+
+ /// Reads via a [`UserSlicePtrWriter`].
+ fn read_value(&self, data: &mut UserSlicePtrWriter) -> (usize, error::KernelResult);
+}
+
+fn trim_whitespace(mut data: &[u8]) -> &[u8] {
+ while !data.is_empty() && (data[0] == b' ' || data[0] == b'\t' || data[0] == b'\n') {
+ data = &data[1..];
+ }
+ while !data.is_empty()
+ && (data[data.len() - 1] == b' '
+ || data[data.len() - 1] == b'\t'
+ || data[data.len() - 1] == b'\n')
+ {
+ data = &data[..data.len() - 1];
+ }
+ data
+}
+
+impl<T> SysctlStorage for &T
+where
+ T: SysctlStorage,
+{
+ fn store_value(&self, data: &[u8]) -> (usize, error::KernelResult) {
+ (*self).store_value(data)
+ }
+
+ fn read_value(&self, data: &mut UserSlicePtrWriter) -> (usize, error::KernelResult) {
+ (*self).read_value(data)
+ }
+}
+
+impl SysctlStorage for atomic::AtomicBool {
+ fn store_value(&self, data: &[u8]) -> (usize, error::KernelResult) {
+ let result = match trim_whitespace(data) {
+ b"0" => {
+ self.store(false, atomic::Ordering::Relaxed);
+ Ok(())
+ }
+ b"1" => {
+ self.store(true, atomic::Ordering::Relaxed);
+ Ok(())
+ }
+ _ => Err(error::Error::EINVAL),
+ };
+ (data.len(), result)
+ }
+
+ fn read_value(&self, data: &mut UserSlicePtrWriter) -> (usize, error::KernelResult) {
+ let value = if self.load(atomic::Ordering::Relaxed) {
+ b"1\n"
+ } else {
+ b"0\n"
+ };
+ (value.len(), data.write_slice(value))
+ }
+}
+
+/// Holds a single `sysctl` entry (and its table).
+pub struct Sysctl<T: SysctlStorage> {
+ inner: Box<T>,
+ // Responsible for keeping the `ctl_table` alive.
+ _table: Box<[bindings::ctl_table]>,
+ header: *mut bindings::ctl_table_header,
+}
+
+// SAFETY: The only public method we have is `get()`, which returns `&T`, and
+// `T: Sync`. Any new methods must adhere to this requirement.
+unsafe impl<T: SysctlStorage> Sync for Sysctl<T> {}
+
+unsafe extern "C" fn proc_handler<T: SysctlStorage>(
+ ctl: *mut bindings::ctl_table,
+ write: c_types::c_int,
+ buffer: *mut c_types::c_void,
+ len: *mut usize,
+ ppos: *mut bindings::loff_t,
+) -> c_types::c_int {
+ // If we are reading from some offset other than the beginning of the file,
+ // return an empty read to signal EOF.
+ if *ppos != 0 && write == 0 {
+ *len = 0;
+ return 0;
+ }
+
+ let data = UserSlicePtr::new(buffer, *len);
+ let storage = &*((*ctl).data as *const T);
+ let (bytes_processed, result) = if write != 0 {
+ let data = match data.read_all() {
+ Ok(r) => r,
+ Err(e) => return e.to_kernel_errno(),
+ };
+ storage.store_value(&data)
+ } else {
+ let mut writer = data.writer();
+ storage.read_value(&mut writer)
+ };
+ *len = bytes_processed;
+ *ppos += *len as bindings::loff_t;
+ match result {
+ Ok(()) => 0,
+ Err(e) => e.to_kernel_errno(),
+ }
+}
+
+impl<T: SysctlStorage> Sysctl<T> {
+ /// Registers a single entry in `sysctl`.
+ pub fn register(
+ path: types::CStr<'static>,
+ name: types::CStr<'static>,
+ storage: T,
+ mode: types::Mode,
+ ) -> error::KernelResult<Sysctl<T>> {
+ if name.contains('/') {
+ return Err(error::Error::EINVAL);
+ }
+
+ let storage = Box::try_new(storage)?;
+ let mut table = vec![
+ bindings::ctl_table {
+ procname: name.as_ptr() as *const i8,
+ mode: mode.as_int(),
+ data: &*storage as *const T as *mut c_types::c_void,
+ proc_handler: Some(proc_handler::<T>),
+
+ maxlen: 0,
+ child: ptr::null_mut(),
+ poll: ptr::null_mut(),
+ extra1: ptr::null_mut(),
+ extra2: ptr::null_mut(),
+ },
+ unsafe { mem::zeroed() },
+ ]
+ .into_boxed_slice();
+
+ let result =
+ unsafe { bindings::register_sysctl(path.as_ptr() as *const i8, table.as_mut_ptr()) };
+ if result.is_null() {
+ return Err(error::Error::ENOMEM);
+ }
+
+ Ok(Sysctl {
+ inner: storage,
+ _table: table,
+ header: result,
+ })
+ }
+
+ /// Gets the storage.
+ pub fn get(&self) -> &T {
+ &self.inner
+ }
+}
+
+impl<T: SysctlStorage> Drop for Sysctl<T> {
+ fn drop(&mut self) {
+ unsafe {
+ bindings::unregister_sysctl_table(self.header);
+ }
+ self.header = ptr::null_mut();
+ }
+}
diff --git a/rust/kernel/types.rs b/rust/kernel/types.rs
new file mode 100644
index 000000000000..6207670c3290
--- /dev/null
+++ b/rust/kernel/types.rs
@@ -0,0 +1,73 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Kernel types.
+//!
+//! C header: [`include/linux/types.h`](../../../../include/linux/types.h)
+
+use core::ops::Deref;
+
+use crate::bindings;
+
+/// Permissions.
+///
+/// C header: [`include/uapi/linux/stat.h`](../../../../include/uapi/linux/stat.h)
+///
+/// C header: [`include/linux/stat.h`](../../../../include/linux/stat.h)
+pub struct Mode(bindings::umode_t);
+
+impl Mode {
+ /// Creates a [`Mode`] from an integer.
+ pub fn from_int(m: u16) -> Mode {
+ Mode(m)
+ }
+
+ /// Returns the mode as an integer.
+ pub fn as_int(&self) -> u16 {
+ self.0
+ }
+}
+
+/// A string that is guaranteed to have exactly one `NUL` byte, which is at the
+/// end.
+///
+/// Used for interoperability with kernel APIs that take C strings.
+#[repr(transparent)]
+pub struct CStr<'a>(&'a str);
+
+impl CStr<'_> {
+ /// Creates a [`CStr`] from a [`str`] without performing any additional
+ /// checks.
+ ///
+ /// # Safety
+ ///
+ /// `data` *must* end with a `NUL` byte, and should only have only a single
+ /// `NUL` byte (or the string will be truncated).
+ pub const unsafe fn new_unchecked(data: &str) -> CStr {
+ CStr(data)
+ }
+}
+
+impl Deref for CStr<'_> {
+ type Target = str;
+
+ fn deref(&self) -> &str {
+ self.0
+ }
+}
+
+/// Creates a new `CStr` from a string literal.
+///
+/// The string literal should not contain any `NUL` bytes.
+///
+/// # Examples
+///
+/// ```rust,no_run
+/// const MY_CSTR: CStr<'static> = cstr!("My awesome CStr!");
+/// ```
+#[macro_export]
+macro_rules! cstr {
+ ($str:expr) => {{
+ let s = concat!($str, "\x00");
+ unsafe { $crate::CStr::new_unchecked(s) }
+ }};
+}
diff --git a/rust/kernel/user_ptr.rs b/rust/kernel/user_ptr.rs
new file mode 100644
index 000000000000..d9304d269d06
--- /dev/null
+++ b/rust/kernel/user_ptr.rs
@@ -0,0 +1,282 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! User pointers.
+//!
+//! C header: [`include/linux/uaccess.h`](../../../../include/linux/uaccess.h)
+
+use crate::{c_types, error::Error, KernelResult};
+use alloc::vec::Vec;
+use core::mem::{size_of, MaybeUninit};
+
+extern "C" {
+ fn rust_helper_copy_from_user(
+ to: *mut c_types::c_void,
+ from: *const c_types::c_void,
+ n: c_types::c_ulong,
+ ) -> c_types::c_ulong;
+
+ fn rust_helper_copy_to_user(
+ to: *mut c_types::c_void,
+ from: *const c_types::c_void,
+ n: c_types::c_ulong,
+ ) -> c_types::c_ulong;
+}
+
+/// Specifies that a type is safely readable from byte slices.
+///
+/// Not all types can be safely read from byte slices; examples from
+/// <https://doc.rust-lang.org/reference/behavior-considered-undefined.html> include `bool`
+/// that must be either `0` or `1`, and `char` that cannot be a surrogate or above `char::MAX`.
+///
+/// # Safety
+///
+/// Implementers must ensure that the type is made up only of types that can be safely read from
+/// arbitrary byte sequences (e.g., `u32`, `u64`, etc.).
+pub unsafe trait ReadableFromBytes {}
+
+// SAFETY: All bit patterns are acceptable values of the types below.
+unsafe impl ReadableFromBytes for u8 {}
+unsafe impl ReadableFromBytes for u16 {}
+unsafe impl ReadableFromBytes for u32 {}
+unsafe impl ReadableFromBytes for u64 {}
+unsafe impl ReadableFromBytes for usize {}
+unsafe impl ReadableFromBytes for i8 {}
+unsafe impl ReadableFromBytes for i16 {}
+unsafe impl ReadableFromBytes for i32 {}
+unsafe impl ReadableFromBytes for i64 {}
+unsafe impl ReadableFromBytes for isize {}
+
+/// Specifies that a type is safely writable to byte slices.
+///
+/// This means that we don't read undefined values (which leads to UB) in preparation for writing
+/// to the byte slice. It also ensures that no potentially sensitive information is leaked into the
+/// byte slices.
+///
+/// # Safety
+///
+/// A type must not include padding bytes and must be fully initialised to safely implement
+/// [`WritableToBytes`] (i.e., it doesn't contain [`MaybeUninit`] fields). A composition of
+/// writable types in a structure is not necessarily writable because it may result in padding
+/// bytes.
+pub unsafe trait WritableToBytes {}
+
+// SAFETY: Initialised instances of the following types have no uninitialised portions.
+unsafe impl WritableToBytes for u8 {}
+unsafe impl WritableToBytes for u16 {}
+unsafe impl WritableToBytes for u32 {}
+unsafe impl WritableToBytes for u64 {}
+unsafe impl WritableToBytes for usize {}
+unsafe impl WritableToBytes for i8 {}
+unsafe impl WritableToBytes for i16 {}
+unsafe impl WritableToBytes for i32 {}
+unsafe impl WritableToBytes for i64 {}
+unsafe impl WritableToBytes for isize {}
+
+/// A reference to an area in userspace memory, which can be either
+/// read-only or read-write.
+///
+/// All methods on this struct are safe: invalid pointers return
+/// `EFAULT`. Concurrent access, *including data races to/from userspace
+/// memory*, is permitted, because fundamentally another userspace
+/// thread/process could always be modifying memory at the same time
+/// (in the same way that userspace Rust's [`std::io`] permits data races
+/// with the contents of files on disk). In the presence of a race, the
+/// exact byte values read/written are unspecified but the operation is
+/// well-defined. Kernelspace code should validate its copy of data
+/// after completing a read, and not expect that multiple reads of the
+/// same address will return the same value.
+///
+/// All APIs enforce the invariant that a given byte of memory from userspace
+/// may only be read once. By preventing double-fetches we avoid TOCTOU
+/// vulnerabilities. This is accomplished by taking `self` by value to prevent
+/// obtaining multiple readers on a given [`UserSlicePtr`], and the readers
+/// only permitting forward reads.
+///
+/// Constructing a [`UserSlicePtr`] performs no checks on the provided
+/// address and length, it can safely be constructed inside a kernel thread
+/// with no current userspace process. Reads and writes wrap the kernel APIs
+/// `copy_from_user` and `copy_to_user`, which check the memory map of the
+/// current process and enforce that the address range is within the user
+/// range (no additional calls to `access_ok` are needed).
+///
+/// [`std::io`]: https://doc.rust-lang.org/std/io/index.html
+pub struct UserSlicePtr(*mut c_types::c_void, usize);
+
+impl UserSlicePtr {
+ /// Constructs a user slice from a raw pointer and a length in bytes.
+ ///
+ /// # Safety
+ ///
+ /// Callers must be careful to avoid time-of-check-time-of-use
+ /// (TOCTOU) issues. The simplest way is to create a single instance of
+ /// [`UserSlicePtr`] per user memory block as it reads each byte at
+ /// most once.
+ pub unsafe fn new(ptr: *mut c_types::c_void, length: usize) -> Self {
+ UserSlicePtr(ptr, length)
+ }
+
+ /// Reads the entirety of the user slice.
+ ///
+ /// Returns `EFAULT` if the address does not currently point to
+ /// mapped, readable memory.
+ pub fn read_all(self) -> KernelResult<Vec<u8>> {
+ self.reader().read_all()
+ }
+
+ /// Constructs a [`UserSlicePtrReader`].
+ pub fn reader(self) -> UserSlicePtrReader {
+ UserSlicePtrReader(self.0, self.1)
+ }
+
+ /// Writes the provided slice into the user slice.
+ ///
+ /// Returns `EFAULT` if the address does not currently point to
+ /// mapped, writable memory (in which case some data from before the
+ /// fault may be written), or `data` is larger than the user slice
+ /// (in which case no data is written).
+ pub fn write_all(self, data: &[u8]) -> KernelResult {
+ self.writer().write_slice(data)
+ }
+
+ /// Constructs a [`UserSlicePtrWriter`].
+ pub fn writer(self) -> UserSlicePtrWriter {
+ UserSlicePtrWriter(self.0, self.1)
+ }
+
+ /// Constructs both a [`UserSlicePtrReader`] and a [`UserSlicePtrWriter`].
+ pub fn reader_writer(self) -> (UserSlicePtrReader, UserSlicePtrWriter) {
+ (
+ UserSlicePtrReader(self.0, self.1),
+ UserSlicePtrWriter(self.0, self.1),
+ )
+ }
+}
+
+/// A reader for [`UserSlicePtr`].
+///
+/// Used to incrementally read from the user slice.
+pub struct UserSlicePtrReader(*mut c_types::c_void, usize);
+
+impl UserSlicePtrReader {
+ /// Returns the number of bytes left to be read from this.
+ ///
+ /// Note that even reading less than this number of bytes may fail.
+ pub fn len(&self) -> usize {
+ self.1
+ }
+
+ /// Returns `true` if `self.len()` is 0.
+ pub fn is_empty(&self) -> bool {
+ self.len() == 0
+ }
+
+ /// Reads all data remaining in the user slice.
+ ///
+ /// Returns `EFAULT` if the address does not currently point to
+ /// mapped, readable memory.
+ pub fn read_all(&mut self) -> KernelResult<Vec<u8>> {
+ let mut data = Vec::<u8>::new();
+ data.try_reserve_exact(self.1)?;
+ data.resize(self.1, 0);
+ // SAFETY: The output buffer is valid as we just allocated it.
+ unsafe { self.read_raw(data.as_mut_ptr(), data.len())? };
+ Ok(data)
+ }
+
+ /// Reads a byte slice from the user slice.
+ ///
+ /// Returns `EFAULT` if the byte slice is bigger than the remaining size
+ /// of the user slice or if the address does not currently point to mapped,
+ /// readable memory.
+ pub fn read_slice(&mut self, data: &mut [u8]) -> KernelResult {
+ // SAFETY: The output buffer is valid as it's coming from a live reference.
+ unsafe { self.read_raw(data.as_mut_ptr(), data.len()) }
+ }
+
+ /// Reads raw data from the user slice into a raw kernel buffer.
+ ///
+ /// # Safety
+ ///
+ /// The output buffer must be valid.
+ pub unsafe fn read_raw(&mut self, out: *mut u8, len: usize) -> KernelResult {
+ if len > self.1 || len > u32::MAX as usize {
+ return Err(Error::EFAULT);
+ }
+ let res = rust_helper_copy_from_user(out as _, self.0, len as _);
+ if res != 0 {
+ return Err(Error::EFAULT);
+ }
+ // Since this is not a pointer to a valid object in our program,
+ // we cannot use `add`, which has C-style rules for defined
+ // behavior.
+ self.0 = self.0.wrapping_add(len);
+ self.1 -= len;
+ Ok(())
+ }
+
+ /// Reads the contents of a plain old data (POD) type from the user slice.
+ pub fn read<T: ReadableFromBytes>(&mut self) -> KernelResult<T> {
+ let mut out = MaybeUninit::<T>::uninit();
+ // SAFETY: The buffer is valid as it was just allocated.
+ unsafe { self.read_raw(out.as_mut_ptr() as _, size_of::<T>()) }?;
+ // SAFETY: We just initialised the data.
+ Ok(unsafe { out.assume_init() })
+ }
+}
+
+/// A writer for [`UserSlicePtr`].
+///
+/// Used to incrementally write into the user slice.
+pub struct UserSlicePtrWriter(*mut c_types::c_void, usize);
+
+impl UserSlicePtrWriter {
+ /// Returns the number of bytes left to be written from this.
+ ///
+ /// Note that even writing less than this number of bytes may fail.
+ pub fn len(&self) -> usize {
+ self.1
+ }
+
+ /// Returns `true` if `self.len()` is 0.
+ pub fn is_empty(&self) -> bool {
+ self.len() == 0
+ }
+
+ /// Writes a byte slice to the user slice.
+ ///
+ /// Returns `EFAULT` if the byte slice is bigger than the remaining size
+ /// of the user slice or if the address does not currently point to mapped,
+ /// writable memory.
+ pub fn write_slice(&mut self, data: &[u8]) -> KernelResult {
+ // SAFETY: The input buffer is valid as it's coming from a live reference.
+ unsafe { self.write_raw(data.as_ptr(), data.len()) }
+ }
+
+ /// Writes raw data to the user slice from a raw kernel buffer.
+ ///
+ /// # Safety
+ ///
+ /// The input buffer must be valid.
+ unsafe fn write_raw(&mut self, data: *const u8, len: usize) -> KernelResult {
+ if len > self.1 || len > u32::MAX as usize {
+ return Err(Error::EFAULT);
+ }
+ let res = rust_helper_copy_to_user(self.0, data as _, len as _);
+ if res != 0 {
+ return Err(Error::EFAULT);
+ }
+ // Since this is not a pointer to a valid object in our program,
+ // we cannot use `add`, which has C-style rules for defined
+ // behavior.
+ self.0 = self.0.wrapping_add(len);
+ self.1 -= len;
+ Ok(())
+ }
+
+ /// Writes the contents of the given data into the user slice.
+ pub fn write<T: WritableToBytes>(&mut self, data: &T) -> KernelResult<()> {
+ // SAFETY: The input buffer is valid as it's coming from a live
+ // reference to a type that implements `WritableToBytes`.
+ unsafe { self.write_raw(data as *const T as _, size_of::<T>()) }
+ }
+}
--
2.17.1
Hi Miguel,
On Wed, Apr 14, 2021 at 08:45:54PM +0200, [email protected] wrote:
> From: Miguel Ojeda <[email protected]>
>
> To support Rust under GCC-built kernels, we need to save the flags that
> would have been passed if the kernel was being compiled with Clang.
>
> The reason is that bindgen -- the tool we use to generate Rust bindings
> to the C side of the kernel -- relies on libclang to parse C. Ideally:
>
> - bindgen would support a GCC backend (requested at [1]),
>
> - or the Clang driver would be perfectly compatible with GCC,
> including plugins. Unlikely, of course, but perhaps a big
> subset of configs may be possible to guarantee to be kept
> compatible nevertheless.
>
> This is also the reason why GCC builds are very experimental and some
> configurations may not work (e.g. GCC_PLUGIN_RANDSTRUCT). However,
> we keep GCC builds working (for some example configs) in the CI
> to avoid diverging/regressing further, so that we are better prepared
> for the future when a solution might become available.
>
> [1] https://github.com/rust-lang/rust-bindgen/issues/1949
>
> Link: https://github.com/Rust-for-Linux/linux/issues/167
>
> Co-developed-by: Alex Gaynor <[email protected]>
> Signed-off-by: Alex Gaynor <[email protected]>
> Co-developed-by: Geoffrey Thomas <[email protected]>
> Signed-off-by: Geoffrey Thomas <[email protected]>
> Co-developed-by: Finn Behrens <[email protected]>
> Signed-off-by: Finn Behrens <[email protected]>
> Co-developed-by: Adam Bratschi-Kaye <[email protected]>
> Signed-off-by: Adam Bratschi-Kaye <[email protected]>
> Co-developed-by: Wedson Almeida Filho <[email protected]>
> Signed-off-by: Wedson Almeida Filho <[email protected]>
> Signed-off-by: Miguel Ojeda <[email protected]>
> ---
> Makefile | 27 ++++++++++++++++-----------
> 1 file changed, 16 insertions(+), 11 deletions(-)
>
> diff --git a/Makefile b/Makefile
> index d4784d181123..9c75354324ed 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -559,26 +559,31 @@ ifdef building_out_of_srctree
> { echo "# this is build directory, ignore it"; echo "*"; } > .gitignore
> endif
>
> -# The expansion should be delayed until arch/$(SRCARCH)/Makefile is included.
> -# Some architectures define CROSS_COMPILE in arch/$(SRCARCH)/Makefile.
> -# CC_VERSION_TEXT is referenced from Kconfig (so it needs export),
> -# and from include/config/auto.conf.cmd to detect the compiler upgrade.
> -CC_VERSION_TEXT = $(shell $(CC) --version 2>/dev/null | head -n 1 | sed 's/\#//g')
> +TENTATIVE_CLANG_FLAGS := -Werror=unknown-warning-option
>
> -ifneq ($(findstring clang,$(CC_VERSION_TEXT)),)
> ifneq ($(CROSS_COMPILE),)
> -CLANG_FLAGS += --target=$(notdir $(CROSS_COMPILE:%-=%))
> +TENTATIVE_CLANG_FLAGS += --target=$(notdir $(CROSS_COMPILE:%-=%))
> GCC_TOOLCHAIN_DIR := $(dir $(shell which $(CROSS_COMPILE)elfedit))
Shuffling this around will cause this issue (I never saw you CC'd on the
thread).
https://lore.kernel.org/r/f6218ac526a04fa4d4406f935bcc4eb4a7df65c4.1617917438.git.msuchanek@suse.de/
Perhaps that patch should be added to this series?
> -CLANG_FLAGS += --prefix=$(GCC_TOOLCHAIN_DIR)$(notdir $(CROSS_COMPILE))
> +TENTATIVE_CLANG_FLAGS += --prefix=$(GCC_TOOLCHAIN_DIR)$(notdir $(CROSS_COMPILE))
> GCC_TOOLCHAIN := $(realpath $(GCC_TOOLCHAIN_DIR)/..)
> endif
> ifneq ($(GCC_TOOLCHAIN),)
> -CLANG_FLAGS += --gcc-toolchain=$(GCC_TOOLCHAIN)
> +TENTATIVE_CLANG_FLAGS += --gcc-toolchain=$(GCC_TOOLCHAIN)
> endif
> ifneq ($(LLVM_IAS),1)
> -CLANG_FLAGS += -no-integrated-as
> +TENTATIVE_CLANG_FLAGS += -no-integrated-as
> endif
> -CLANG_FLAGS += -Werror=unknown-warning-option
> +
> +export TENTATIVE_CLANG_FLAGS
> +
> +# The expansion should be delayed until arch/$(SRCARCH)/Makefile is included.
> +# Some architectures define CROSS_COMPILE in arch/$(SRCARCH)/Makefile.
> +# CC_VERSION_TEXT is referenced from Kconfig (so it needs export),
> +# and from include/config/auto.conf.cmd to detect the compiler upgrade.
> +CC_VERSION_TEXT = $(shell $(CC) --version 2>/dev/null | head -n 1 | sed 's/\#//g')
> +
> +ifneq ($(findstring clang,$(CC_VERSION_TEXT)),)
> +CLANG_FLAGS += $(TENTATIVE_CLANG_FLAGS)
> KBUILD_CFLAGS += $(CLANG_FLAGS)
> KBUILD_AFLAGS += $(CLANG_FLAGS)
> export CLANG_FLAGS
> --
> 2.17.1
>
From: Miguel Ojeda <[email protected]>
Rust symbols can become quite long due to namespacing introduced
by modules, types, traits, generics, etc.
Increasing to 255 is not enough in some cases, and therefore
we need to introduce 2-byte lengths to the symbol table. We call
these "big" symbols.
In order to avoid increasing all lengths to 2 bytes (since most
of them only require 1 byte, including many Rust ones), we use
length zero to mark "big" symbols in the table.
Co-developed-by: Alex Gaynor <[email protected]>
Signed-off-by: Alex Gaynor <[email protected]>
Co-developed-by: Geoffrey Thomas <[email protected]>
Signed-off-by: Geoffrey Thomas <[email protected]>
Co-developed-by: Finn Behrens <[email protected]>
Signed-off-by: Finn Behrens <[email protected]>
Co-developed-by: Adam Bratschi-Kaye <[email protected]>
Signed-off-by: Adam Bratschi-Kaye <[email protected]>
Co-developed-by: Wedson Almeida Filho <[email protected]>
Signed-off-by: Wedson Almeida Filho <[email protected]>
Signed-off-by: Miguel Ojeda <[email protected]>
---
kernel/kallsyms.c | 7 +++++++
scripts/kallsyms.c | 31 ++++++++++++++++++++++++++++---
2 files changed, 35 insertions(+), 3 deletions(-)
diff --git a/kernel/kallsyms.c b/kernel/kallsyms.c
index 8043a90aa50e..faba546e9a58 100644
--- a/kernel/kallsyms.c
+++ b/kernel/kallsyms.c
@@ -73,6 +73,13 @@ static unsigned int kallsyms_expand_symbol(unsigned int off,
*/
off += len + 1;
+ /* If zero, it is a "big" symbol, so a two byte length follows. */
+ if (len == 0) {
+ len = (data[0] << 8) | data[1];
+ data += 2;
+ off += len + 2;
+ }
+
/*
* For every byte on the compressed symbol data, copy the table
* entry for that byte.
diff --git a/scripts/kallsyms.c b/scripts/kallsyms.c
index 54ad86d13784..bcdabee13aab 100644
--- a/scripts/kallsyms.c
+++ b/scripts/kallsyms.c
@@ -470,12 +470,37 @@ static void write_src(void)
if ((i & 0xFF) == 0)
markers[i >> 8] = off;
- printf("\t.byte 0x%02x", table[i]->len);
+ /*
+ * There cannot be any symbol of length zero -- we use that
+ * to mark a "big" symbol (and it doesn't make sense anyway).
+ */
+ if (table[i]->len == 0) {
+ fprintf(stderr, "kallsyms failure: "
+ "unexpected zero symbol length\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Only lengths that fit in up to two bytes are supported. */
+ if (table[i]->len > 0xFFFF) {
+ fprintf(stderr, "kallsyms failure: "
+ "unexpected huge symbol length\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (table[i]->len <= 0xFF) {
+ /* Most symbols use a single byte for the length. */
+ printf("\t.byte 0x%02x", table[i]->len);
+ off += table[i]->len + 1;
+ } else {
+ /* "Big" symbols use a zero and then two bytes. */
+ printf("\t.byte 0x00, 0x%02x, 0x%02x",
+ (table[i]->len >> 8) & 0xFF,
+ table[i]->len & 0xFF);
+ off += table[i]->len + 3;
+ }
for (k = 0; k < table[i]->len; k++)
printf(", 0x%02x", table[i]->sym[k]);
printf("\n");
-
- off += table[i]->len + 1;
}
printf("\n");
--
2.17.1
From: Miguel Ojeda <[email protected]>
Rust symbols can become quite long due to namespacing introduced
by modules, types, traits, generics, etc. For instance, for:
pub mod my_module {
pub struct MyType;
pub struct MyGenericType<T>(T);
pub trait MyTrait {
fn my_method() -> u32;
}
impl MyTrait for MyGenericType<MyType> {
fn my_method() -> u32 {
42
}
}
}
generates a symbol of length 96 when using the upcoming v0 mangling scheme:
_RNvXNtCshGpAVYOtgW1_7example9my_moduleINtB2_13MyGenericTypeNtB2_6MyTypeENtB2_7MyTrait9my_method
At the moment, Rust symbols may reach up to 300 in length.
Setting 512 as the maximum seems like a reasonable choice to
keep some headroom.
Co-developed-by: Alex Gaynor <[email protected]>
Signed-off-by: Alex Gaynor <[email protected]>
Co-developed-by: Geoffrey Thomas <[email protected]>
Signed-off-by: Geoffrey Thomas <[email protected]>
Co-developed-by: Finn Behrens <[email protected]>
Signed-off-by: Finn Behrens <[email protected]>
Co-developed-by: Adam Bratschi-Kaye <[email protected]>
Signed-off-by: Adam Bratschi-Kaye <[email protected]>
Co-developed-by: Wedson Almeida Filho <[email protected]>
Signed-off-by: Wedson Almeida Filho <[email protected]>
Signed-off-by: Miguel Ojeda <[email protected]>
---
include/linux/kallsyms.h | 2 +-
kernel/livepatch/core.c | 4 ++--
scripts/kallsyms.c | 2 +-
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 +-
7 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/include/linux/kallsyms.h b/include/linux/kallsyms.h
index 465060acc981..5cdc6903abca 100644
--- a/include/linux/kallsyms.h
+++ b/include/linux/kallsyms.h
@@ -14,7 +14,7 @@
#include <asm/sections.h>
-#define KSYM_NAME_LEN 128
+#define KSYM_NAME_LEN 512
#define KSYM_SYMBOL_LEN (sizeof("%s+%#lx/%#lx [%s]") + (KSYM_NAME_LEN - 1) + \
2*(BITS_PER_LONG*3/10) + (MODULE_NAME_LEN - 1) + 1)
diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c
index 335d988bd811..73874e5edfda 100644
--- a/kernel/livepatch/core.c
+++ b/kernel/livepatch/core.c
@@ -213,7 +213,7 @@ static int klp_resolve_symbols(Elf64_Shdr *sechdrs, const char *strtab,
* we use the smallest/strictest upper bound possible (56, based on
* the current definition of MODULE_NAME_LEN) to prevent overflows.
*/
- BUILD_BUG_ON(MODULE_NAME_LEN < 56 || KSYM_NAME_LEN != 128);
+ BUILD_BUG_ON(MODULE_NAME_LEN < 56 || KSYM_NAME_LEN != 512);
relas = (Elf_Rela *) relasec->sh_addr;
/* For each rela in this klp relocation section */
@@ -227,7 +227,7 @@ static int klp_resolve_symbols(Elf64_Shdr *sechdrs, const char *strtab,
/* Format: .klp.sym.sym_objname.sym_name,sympos */
cnt = sscanf(strtab + sym->st_name,
- ".klp.sym.%55[^.].%127[^,],%lu",
+ ".klp.sym.%55[^.].%511[^,],%lu",
sym_objname, sym_name, &sympos);
if (cnt != 3) {
pr_err("symbol %s has an incorrectly formatted name\n",
diff --git a/scripts/kallsyms.c b/scripts/kallsyms.c
index bcdabee13aab..9bab5f55ade3 100644
--- a/scripts/kallsyms.c
+++ b/scripts/kallsyms.c
@@ -27,7 +27,7 @@
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))
-#define KSYM_NAME_LEN 128
+#define KSYM_NAME_LEN 512
struct sym_entry {
unsigned long long addr;
diff --git a/tools/include/linux/kallsyms.h b/tools/include/linux/kallsyms.h
index efb6c3f5f2a9..5a37ccbec54f 100644
--- a/tools/include/linux/kallsyms.h
+++ b/tools/include/linux/kallsyms.h
@@ -6,7 +6,7 @@
#include <stdio.h>
#include <unistd.h>
-#define KSYM_NAME_LEN 128
+#define KSYM_NAME_LEN 512
struct module;
diff --git a/tools/include/linux/lockdep.h b/tools/include/linux/lockdep.h
index e56997288f2b..d9c163f3ab24 100644
--- a/tools/include/linux/lockdep.h
+++ b/tools/include/linux/lockdep.h
@@ -47,7 +47,7 @@ static inline int debug_locks_off(void)
#define task_pid_nr(tsk) ((tsk)->pid)
-#define KSYM_NAME_LEN 128
+#define KSYM_NAME_LEN 512
#define printk(...) dprintf(STDOUT_FILENO, __VA_ARGS__)
#define pr_err(format, ...) fprintf (stderr, format, ## __VA_ARGS__)
#define pr_warn pr_err
diff --git a/tools/lib/perf/include/perf/event.h b/tools/lib/perf/include/perf/event.h
index d82054225fcc..f5c40325b441 100644
--- a/tools/lib/perf/include/perf/event.h
+++ b/tools/lib/perf/include/perf/event.h
@@ -93,7 +93,7 @@ struct perf_record_throttle {
};
#ifndef KSYM_NAME_LEN
-#define KSYM_NAME_LEN 256
+#define KSYM_NAME_LEN 512
#endif
struct perf_record_ksymbol {
diff --git a/tools/lib/symbol/kallsyms.h b/tools/lib/symbol/kallsyms.h
index 72ab9870454b..542f9b059c3b 100644
--- a/tools/lib/symbol/kallsyms.h
+++ b/tools/lib/symbol/kallsyms.h
@@ -7,7 +7,7 @@
#include <linux/types.h>
#ifndef KSYM_NAME_LEN
-#define KSYM_NAME_LEN 256
+#define KSYM_NAME_LEN 512
#endif
static inline u8 kallsyms2elf_binding(char type)
--
2.17.1
From: Miguel Ojeda <[email protected]>
Implements the `module!` macro that is used by Rust modules
to easily declare the equivalent information to the `MODULE_*`
macros in C modules, e.g.:
module! {
type: RustMinimal,
name: b"rust_minimal",
author: b"Rust for Linux Contributors",
description: b"Rust minimal sample",
license: b"GPL v2",
params: {},
}
This is a Rust procedural macro crate. It is a crate type that allows
to create syntax extensions. It runs at compile-time and can consume
as well as produce Rust syntax.
Co-developed-by: Alex Gaynor <[email protected]>
Signed-off-by: Alex Gaynor <[email protected]>
Co-developed-by: Geoffrey Thomas <[email protected]>
Signed-off-by: Geoffrey Thomas <[email protected]>
Co-developed-by: Finn Behrens <[email protected]>
Signed-off-by: Finn Behrens <[email protected]>
Co-developed-by: Adam Bratschi-Kaye <[email protected]>
Signed-off-by: Adam Bratschi-Kaye <[email protected]>
Co-developed-by: Wedson Almeida Filho <[email protected]>
Signed-off-by: Wedson Almeida Filho <[email protected]>
Co-developed-by: Sumera Priyadarsini <[email protected]>
Signed-off-by: Sumera Priyadarsini <[email protected]>
Signed-off-by: Miguel Ojeda <[email protected]>
---
rust/Makefile | 2 +
rust/module.rs | 685 +++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 687 insertions(+)
create mode 100644 rust/module.rs
diff --git a/rust/Makefile b/rust/Makefile
index 5b96462b4fef..3fd827d4ac17 100644
--- a/rust/Makefile
+++ b/rust/Makefile
@@ -3,6 +3,8 @@
obj-$(CONFIG_RUST) += core.o compiler_builtins.o helpers.o
extra-$(CONFIG_RUST) += exports_core_generated.h
+extra-$(CONFIG_RUST) += libmodule.so
+
RUSTDOC = rustdoc
quiet_cmd_rustdoc = RUSTDOC $<
diff --git a/rust/module.rs b/rust/module.rs
new file mode 100644
index 000000000000..076525a97c9e
--- /dev/null
+++ b/rust/module.rs
@@ -0,0 +1,685 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Proc macro crate implementing the [`module!`] magic.
+//!
+//! C header: [`include/linux/moduleparam.h`](../../../include/linux/moduleparam.h)
+
+#![deny(clippy::complexity)]
+#![deny(clippy::correctness)]
+#![deny(clippy::perf)]
+#![deny(clippy::style)]
+
+use proc_macro::{token_stream, Delimiter, Group, TokenStream, TokenTree};
+
+fn try_ident(it: &mut token_stream::IntoIter) -> Option<String> {
+ if let Some(TokenTree::Ident(ident)) = it.next() {
+ Some(ident.to_string())
+ } else {
+ None
+ }
+}
+
+fn try_literal(it: &mut token_stream::IntoIter) -> Option<String> {
+ if let Some(TokenTree::Literal(literal)) = it.next() {
+ Some(literal.to_string())
+ } else {
+ None
+ }
+}
+
+fn try_byte_string(it: &mut token_stream::IntoIter) -> Option<String> {
+ try_literal(it).and_then(|byte_string| {
+ if byte_string.starts_with("b\"") && byte_string.ends_with('\"') {
+ Some(byte_string[2..byte_string.len() - 1].to_string())
+ } else {
+ None
+ }
+ })
+}
+
+fn expect_ident(it: &mut token_stream::IntoIter) -> String {
+ try_ident(it).expect("Expected Ident")
+}
+
+fn expect_punct(it: &mut token_stream::IntoIter) -> char {
+ if let TokenTree::Punct(punct) = it.next().expect("Reached end of token stream for Punct") {
+ punct.as_char()
+ } else {
+ panic!("Expected Punct");
+ }
+}
+
+fn expect_literal(it: &mut token_stream::IntoIter) -> String {
+ try_literal(it).expect("Expected Literal")
+}
+
+fn expect_group(it: &mut token_stream::IntoIter) -> Group {
+ if let TokenTree::Group(group) = it.next().expect("Reached end of token stream for Group") {
+ group
+ } else {
+ panic!("Expected Group");
+ }
+}
+
+fn expect_byte_string(it: &mut token_stream::IntoIter) -> String {
+ try_byte_string(it).expect("Expected byte string")
+}
+
+#[derive(Clone, PartialEq)]
+enum ParamType {
+ Ident(String),
+ Array { vals: String, max_length: usize },
+}
+
+fn expect_array_fields(it: &mut token_stream::IntoIter) -> ParamType {
+ assert_eq!(expect_punct(it), '<');
+ let vals = expect_ident(it);
+ assert_eq!(expect_punct(it), ',');
+ let max_length_str = expect_literal(it);
+ let max_length = max_length_str
+ .parse::<usize>()
+ .expect("Expected usize length");
+ assert_eq!(expect_punct(it), '>');
+ ParamType::Array { vals, max_length }
+}
+
+fn expect_type(it: &mut token_stream::IntoIter) -> ParamType {
+ if let TokenTree::Ident(ident) = it
+ .next()
+ .expect("Reached end of token stream for param type")
+ {
+ match ident.to_string().as_ref() {
+ "ArrayParam" => expect_array_fields(it),
+ _ => ParamType::Ident(ident.to_string()),
+ }
+ } else {
+ panic!("Expected Param Type")
+ }
+}
+
+fn expect_end(it: &mut token_stream::IntoIter) {
+ if it.next().is_some() {
+ panic!("Expected end");
+ }
+}
+
+fn get_ident(it: &mut token_stream::IntoIter, expected_name: &str) -> String {
+ assert_eq!(expect_ident(it), expected_name);
+ assert_eq!(expect_punct(it), ':');
+ let ident = expect_ident(it);
+ assert_eq!(expect_punct(it), ',');
+ ident
+}
+
+fn get_literal(it: &mut token_stream::IntoIter, expected_name: &str) -> String {
+ assert_eq!(expect_ident(it), expected_name);
+ assert_eq!(expect_punct(it), ':');
+ let literal = expect_literal(it);
+ assert_eq!(expect_punct(it), ',');
+ literal
+}
+
+fn get_group(it: &mut token_stream::IntoIter, expected_name: &str) -> Group {
+ assert_eq!(expect_ident(it), expected_name);
+ assert_eq!(expect_punct(it), ':');
+ let group = expect_group(it);
+ assert_eq!(expect_punct(it), ',');
+ group
+}
+
+fn get_byte_string(it: &mut token_stream::IntoIter, expected_name: &str) -> String {
+ assert_eq!(expect_ident(it), expected_name);
+ assert_eq!(expect_punct(it), ':');
+ let byte_string = expect_byte_string(it);
+ assert_eq!(expect_punct(it), ',');
+ byte_string
+}
+
+fn __build_modinfo_string_base(
+ module: &str,
+ field: &str,
+ content: &str,
+ variable: &str,
+ builtin: bool,
+) -> String {
+ let string = if builtin {
+ // Built-in modules prefix their modinfo strings by `module.`.
+ format!(
+ "{module}.{field}={content}",
+ module = module,
+ field = field,
+ content = content
+ )
+ } else {
+ // Loadable modules' modinfo strings go as-is.
+ format!("{field}={content}", field = field, content = content)
+ };
+
+ format!(
+ "
+ {cfg}
+ #[link_section = \".modinfo\"]
+ #[used]
+ pub static {variable}: [u8; {length}] = *b\"{string}\\0\";
+ ",
+ cfg = if builtin {
+ "#[cfg(not(MODULE))]"
+ } else {
+ "#[cfg(MODULE)]"
+ },
+ variable = variable,
+ length = string.len() + 1,
+ string = string,
+ )
+}
+
+fn __build_modinfo_string_variable(module: &str, field: &str) -> String {
+ format!("__{module}_{field}", module = module, field = field)
+}
+
+fn build_modinfo_string_only_builtin(module: &str, field: &str, content: &str) -> String {
+ __build_modinfo_string_base(
+ module,
+ field,
+ content,
+ &__build_modinfo_string_variable(module, field),
+ true,
+ )
+}
+
+fn build_modinfo_string_only_loadable(module: &str, field: &str, content: &str) -> String {
+ __build_modinfo_string_base(
+ module,
+ field,
+ content,
+ &__build_modinfo_string_variable(module, field),
+ false,
+ )
+}
+
+fn build_modinfo_string(module: &str, field: &str, content: &str) -> String {
+ build_modinfo_string_only_builtin(module, field, content)
+ + &build_modinfo_string_only_loadable(module, field, content)
+}
+
+fn build_modinfo_string_param(module: &str, field: &str, param: &str, content: &str) -> String {
+ let variable = format!(
+ "__{module}_{field}_{param}",
+ module = module,
+ field = field,
+ param = param
+ );
+ let content = format!("{param}:{content}", param = param, content = content);
+ __build_modinfo_string_base(module, field, &content, &variable, true)
+ + &__build_modinfo_string_base(module, field, &content, &variable, false)
+}
+
+fn permissions_are_readonly(perms: &str) -> bool {
+ let (radix, digits) = if let Some(n) = perms.strip_prefix("0x") {
+ (16, n)
+ } else if let Some(n) = perms.strip_prefix("0o") {
+ (8, n)
+ } else if let Some(n) = perms.strip_prefix("0b") {
+ (2, n)
+ } else {
+ (10, perms)
+ };
+ match u32::from_str_radix(digits, radix) {
+ Ok(perms) => perms & 0o222 == 0,
+ Err(_) => false,
+ }
+}
+
+fn param_ops_path(param_type: &str) -> &'static str {
+ match param_type {
+ "bool" => "kernel::module_param::PARAM_OPS_BOOL",
+ "i8" => "kernel::module_param::PARAM_OPS_I8",
+ "u8" => "kernel::module_param::PARAM_OPS_U8",
+ "i16" => "kernel::module_param::PARAM_OPS_I16",
+ "u16" => "kernel::module_param::PARAM_OPS_U16",
+ "i32" => "kernel::module_param::PARAM_OPS_I32",
+ "u32" => "kernel::module_param::PARAM_OPS_U32",
+ "i64" => "kernel::module_param::PARAM_OPS_I64",
+ "u64" => "kernel::module_param::PARAM_OPS_U64",
+ "isize" => "kernel::module_param::PARAM_OPS_ISIZE",
+ "usize" => "kernel::module_param::PARAM_OPS_USIZE",
+ "str" => "kernel::module_param::PARAM_OPS_STR",
+ t => panic!("Unrecognized type {}", t),
+ }
+}
+
+fn try_simple_param_val(
+ param_type: &str,
+) -> Box<dyn Fn(&mut token_stream::IntoIter) -> Option<String>> {
+ match param_type {
+ "bool" => Box::new(|param_it| try_ident(param_it)),
+ "str" => Box::new(|param_it| {
+ try_byte_string(param_it)
+ .map(|s| format!("kernel::module_param::StringParam::Ref(b\"{}\")", s))
+ }),
+ _ => Box::new(|param_it| try_literal(param_it)),
+ }
+}
+
+fn get_default(param_type: &ParamType, param_it: &mut token_stream::IntoIter) -> String {
+ let try_param_val = match param_type {
+ ParamType::Ident(ref param_type)
+ | ParamType::Array {
+ vals: ref param_type,
+ max_length: _,
+ } => try_simple_param_val(param_type),
+ };
+ assert_eq!(expect_ident(param_it), "default");
+ assert_eq!(expect_punct(param_it), ':');
+ let default = match param_type {
+ ParamType::Ident(_) => try_param_val(param_it).expect("Expected default param value"),
+ ParamType::Array {
+ vals: _,
+ max_length: _,
+ } => {
+ let group = expect_group(param_it);
+ assert_eq!(group.delimiter(), Delimiter::Bracket);
+ let mut default_vals = Vec::new();
+ let mut it = group.stream().into_iter();
+
+ while let Some(default_val) = try_param_val(&mut it) {
+ default_vals.push(default_val);
+ match it.next() {
+ Some(TokenTree::Punct(punct)) => assert_eq!(punct.as_char(), ','),
+ None => break,
+ _ => panic!("Expected ',' or end of array default values"),
+ }
+ }
+
+ let mut default_array = "kernel::module_param::ArrayParam::create(&[".to_string();
+ default_array.push_str(
+ &default_vals
+ .iter()
+ .map(|val| val.to_string())
+ .collect::<Vec<String>>()
+ .join(","),
+ );
+ default_array.push_str("])");
+ default_array
+ }
+ };
+ assert_eq!(expect_punct(param_it), ',');
+ default
+}
+
+fn generated_array_ops_name(vals: &str, max_length: usize) -> String {
+ format!(
+ "__generated_array_ops_{vals}_{max_length}",
+ vals = vals,
+ max_length = max_length
+ )
+}
+
+/// Declares a kernel module.
+///
+/// The `type` argument should be a type which implements the [`KernelModule`]
+/// trait. Also accepts various forms of kernel metadata.
+///
+/// [`KernelModule`]: ../kernel/trait.KernelModule.html
+///
+/// # Examples
+///
+/// ```rust,no_run
+/// use kernel::prelude::*;
+///
+/// module!{
+/// type: MyKernelModule,
+/// name: b"my_kernel_module",
+/// author: b"Rust for Linux Contributors",
+/// description: b"My very own kernel module!",
+/// license: b"GPL v2",
+/// params: {
+/// my_i32: i32 {
+/// default: 42,
+/// permissions: 0o000,
+/// description: b"Example of i32",
+/// },
+/// writeable_i32: i32 {
+/// default: 42,
+/// permissions: 0o644,
+/// description: b"Example of i32",
+/// },
+/// },
+/// }
+///
+/// struct MyKernelModule;
+///
+/// impl KernelModule for MyKernelModule {
+/// fn init() -> KernelResult<Self> {
+/// // If the parameter is writeable, then the kparam lock must be
+/// // taken to read the parameter:
+/// {
+/// let lock = THIS_MODULE.kernel_param_lock();
+/// pr_info!("i32 param is: {}\n", writeable_i32.read(&lock));
+/// }
+/// // If the parameter is read only, it can be read without locking
+/// // the kernel parameters:
+/// pr_info!("i32 param is: {}\n", my_i32.read());
+/// Ok(MyKernelModule)
+/// }
+/// }
+/// ```
+///
+/// # Supported parameter types
+///
+/// - `bool`: Corresponds to C `bool` param type.
+/// - `i8`: No equivalent C param type.
+/// - `u8`: Corresponds to C `char` param type.
+/// - `i16`: Corresponds to C `short` param type.
+/// - `u16`: Corresponds to C `ushort` param type.
+/// - `i32`: Corresponds to C `int` param type.
+/// - `u32`: Corresponds to C `uint` param type.
+/// - `i64`: No equivalent C param type.
+/// - `u64`: Corresponds to C `ullong` param type.
+/// - `isize`: No equivalent C param type.
+/// - `usize`: No equivalent C param type.
+/// - `str`: Corresponds to C `charp` param type. Reading returns a byte slice.
+/// - `ArrayParam<T,N>`: Corresponds to C parameters created using `module_param_array`. An array
+/// of `T`'s of length at **most** `N`.
+///
+/// `invbool` is unsupported: it was only ever used in a few modules.
+/// Consider using a `bool` and inverting the logic instead.
+#[proc_macro]
+pub fn module(ts: TokenStream) -> TokenStream {
+ let mut it = ts.into_iter();
+
+ let type_ = get_ident(&mut it, "type");
+ let name = get_byte_string(&mut it, "name");
+ let author = get_byte_string(&mut it, "author");
+ let description = get_byte_string(&mut it, "description");
+ let license = get_byte_string(&mut it, "license");
+ let params = get_group(&mut it, "params");
+
+ expect_end(&mut it);
+
+ assert_eq!(params.delimiter(), Delimiter::Brace);
+
+ let mut it = params.stream().into_iter();
+
+ let mut params_modinfo = String::new();
+
+ let mut array_types_to_generate = Vec::new();
+
+ loop {
+ let param_name = match it.next() {
+ Some(TokenTree::Ident(ident)) => ident.to_string(),
+ Some(_) => panic!("Expected Ident or end"),
+ None => break,
+ };
+
+ assert_eq!(expect_punct(&mut it), ':');
+ let param_type = expect_type(&mut it);
+ let group = expect_group(&mut it);
+ assert_eq!(expect_punct(&mut it), ',');
+
+ assert_eq!(group.delimiter(), Delimiter::Brace);
+
+ let mut param_it = group.stream().into_iter();
+ let param_default = get_default(¶m_type, &mut param_it);
+ let param_permissions = get_literal(&mut param_it, "permissions");
+ let param_description = get_byte_string(&mut param_it, "description");
+ expect_end(&mut param_it);
+
+ // TODO: more primitive types
+ // TODO: other kinds: unsafes, etc.
+ let (param_kernel_type, ops): (String, _) = match param_type {
+ ParamType::Ident(ref param_type) => (
+ param_type.to_string(),
+ param_ops_path(¶m_type).to_string(),
+ ),
+ ParamType::Array {
+ ref vals,
+ max_length,
+ } => {
+ array_types_to_generate.push((vals.clone(), max_length));
+ (
+ format!("__rust_array_param_{}_{}", vals, max_length),
+ generated_array_ops_name(vals, max_length),
+ )
+ }
+ };
+
+ params_modinfo.push_str(&build_modinfo_string_param(
+ &name,
+ "parmtype",
+ ¶m_name,
+ ¶m_kernel_type,
+ ));
+ params_modinfo.push_str(&build_modinfo_string_param(
+ &name,
+ "parm",
+ ¶m_name,
+ ¶m_description,
+ ));
+ let param_type_internal = match param_type {
+ ParamType::Ident(ref param_type) => match param_type.as_ref() {
+ "str" => "kernel::module_param::StringParam".to_string(),
+ other => other.to_string(),
+ },
+ ParamType::Array {
+ ref vals,
+ max_length,
+ } => format!(
+ "kernel::module_param::ArrayParam<{vals}, {max_length}>",
+ vals = vals,
+ max_length = max_length
+ ),
+ };
+ let read_func = if permissions_are_readonly(¶m_permissions) {
+ format!(
+ "
+ fn read(&self) -> &<{param_type_internal} as kernel::module_param::ModuleParam>::Value {{
+ // SAFETY: Parameters do not need to be locked because they are read only or sysfs is not enabled.
+ unsafe {{ <{param_type_internal} as kernel::module_param::ModuleParam>::value(&__{name}_{param_name}_value) }}
+ }}
+ ",
+ name = name,
+ param_name = param_name,
+ param_type_internal = param_type_internal,
+ )
+ } else {
+ format!(
+ "
+ fn read<'lck>(&self, lock: &'lck kernel::KParamGuard) -> &'lck <{param_type_internal} as kernel::module_param::ModuleParam>::Value {{
+ // SAFETY: Parameters are locked by `KParamGuard`.
+ unsafe {{ <{param_type_internal} as kernel::module_param::ModuleParam>::value(&__{name}_{param_name}_value) }}
+ }}
+ ",
+ name = name,
+ param_name = param_name,
+ param_type_internal = param_type_internal,
+ )
+ };
+ let kparam = format!(
+ "
+ kernel::bindings::kernel_param__bindgen_ty_1 {{
+ arg: unsafe {{ &__{name}_{param_name}_value }} as *const _ as *mut kernel::c_types::c_void,
+ }},
+ ",
+ name = name,
+ param_name = param_name,
+ );
+ params_modinfo.push_str(
+ &format!(
+ "
+ static mut __{name}_{param_name}_value: {param_type_internal} = {param_default};
+
+ struct __{name}_{param_name};
+
+ impl __{name}_{param_name} {{ {read_func} }}
+
+ const {param_name}: __{name}_{param_name} = __{name}_{param_name};
+
+ // Note: the C macro that generates the static structs for the `__param` section
+ // asks for them to be `aligned(sizeof(void *))`. However, that was put in place
+ // in 2003 in commit 38d5b085d2 (\"[PATCH] Fix over-alignment problem on x86-64\")
+ // to undo GCC over-alignment of static structs of >32 bytes. It seems that is
+ // not the case anymore, so we simplify to a transparent representation here
+ // in the expectation that it is not needed anymore.
+ // TODO: revisit this to confirm the above comment and remove it if it happened
+ #[repr(transparent)]
+ struct __{name}_{param_name}_RacyKernelParam(kernel::bindings::kernel_param);
+
+ unsafe impl Sync for __{name}_{param_name}_RacyKernelParam {{
+ }}
+
+ #[cfg(not(MODULE))]
+ const __{name}_{param_name}_name: *const kernel::c_types::c_char = b\"{name}.{param_name}\\0\" as *const _ as *const kernel::c_types::c_char;
+
+ #[cfg(MODULE)]
+ const __{name}_{param_name}_name: *const kernel::c_types::c_char = b\"{param_name}\\0\" as *const _ as *const kernel::c_types::c_char;
+
+ #[link_section = \"__param\"]
+ #[used]
+ static __{name}_{param_name}_struct: __{name}_{param_name}_RacyKernelParam = __{name}_{param_name}_RacyKernelParam(kernel::bindings::kernel_param {{
+ name: __{name}_{param_name}_name,
+ // SAFETY: `__this_module` is constructed by the kernel at load time and will not be freed until the module is unloaded.
+ #[cfg(MODULE)]
+ mod_: unsafe {{ &kernel::bindings::__this_module as *const _ as *mut _ }},
+ #[cfg(not(MODULE))]
+ mod_: core::ptr::null_mut(),
+ ops: unsafe {{ &{ops} }} as *const kernel::bindings::kernel_param_ops,
+ perm: {permissions},
+ level: -1,
+ flags: 0,
+ __bindgen_anon_1: {kparam}
+ }});
+ ",
+ name = name,
+ param_type_internal = param_type_internal,
+ read_func = read_func,
+ param_default = param_default,
+ param_name = param_name,
+ ops = ops,
+ permissions = param_permissions,
+ kparam = kparam,
+ )
+ );
+ }
+
+ let mut generated_array_types = String::new();
+
+ for (vals, max_length) in array_types_to_generate {
+ let ops_name = generated_array_ops_name(&vals, max_length);
+ generated_array_types.push_str(&format!(
+ "
+ kernel::make_param_ops!(
+ {ops_name},
+ kernel::module_param::ArrayParam<{vals}, {{ {max_length} }}>
+ );
+ ",
+ ops_name = ops_name,
+ vals = vals,
+ max_length = max_length,
+ ));
+ }
+
+ let file =
+ std::env::var("RUST_MODFILE").expect("Unable to fetch RUST_MODFILE environmental variable");
+
+ format!(
+ "
+ /// The module name.
+ ///
+ /// Used by the printing macros, e.g. [`info!`].
+ const __MODULE_NAME: &[u8] = b\"{name}\\0\";
+
+ static mut __MOD: Option<{type_}> = None;
+
+ // SAFETY: `__this_module` is constructed by the kernel at load time and will not be freed until the module is unloaded.
+ #[cfg(MODULE)]
+ static THIS_MODULE: kernel::ThisModule = unsafe {{ kernel::ThisModule::from_ptr(&kernel::bindings::__this_module as *const _ as *mut _) }};
+ #[cfg(not(MODULE))]
+ static THIS_MODULE: kernel::ThisModule = unsafe {{ kernel::ThisModule::from_ptr(core::ptr::null_mut()) }};
+
+ // Loadable modules need to export the `{{init,cleanup}}_module` identifiers
+ #[cfg(MODULE)]
+ #[no_mangle]
+ pub extern \"C\" fn init_module() -> kernel::c_types::c_int {{
+ __init()
+ }}
+
+ #[cfg(MODULE)]
+ #[no_mangle]
+ pub extern \"C\" fn cleanup_module() {{
+ __exit()
+ }}
+
+ // Built-in modules are initialized through an initcall pointer
+ // and the identifiers need to be unique
+ #[cfg(not(MODULE))]
+ #[cfg(not(CONFIG_HAVE_ARCH_PREL32_RELOCATIONS))]
+ #[link_section = \"{initcall_section}\"]
+ #[used]
+ pub static __{name}_initcall: extern \"C\" fn() -> kernel::c_types::c_int = __{name}_init;
+
+ #[cfg(not(MODULE))]
+ #[cfg(CONFIG_HAVE_ARCH_PREL32_RELOCATIONS)]
+ global_asm!(
+ r#\".section \"{initcall_section}\", \"a\"
+ __{name}_initcall:
+ .long __{name}_init - .
+ .previous
+ \"#
+ );
+
+ #[cfg(not(MODULE))]
+ #[no_mangle]
+ pub extern \"C\" fn __{name}_init() -> kernel::c_types::c_int {{
+ __init()
+ }}
+
+ #[cfg(not(MODULE))]
+ #[no_mangle]
+ pub extern \"C\" fn __{name}_exit() {{
+ __exit()
+ }}
+
+ fn __init() -> kernel::c_types::c_int {{
+ match <{type_} as KernelModule>::init() {{
+ Ok(m) => {{
+ unsafe {{
+ __MOD = Some(m);
+ }}
+ return 0;
+ }}
+ Err(e) => {{
+ return e.to_kernel_errno();
+ }}
+ }}
+ }}
+
+ fn __exit() {{
+ unsafe {{
+ // Invokes `drop()` on `__MOD`, which should be used for cleanup.
+ __MOD = None;
+ }}
+ }}
+
+ {author}
+ {description}
+ {license}
+
+ // Built-in modules also export the `file` modinfo string
+ {file}
+
+ {params_modinfo}
+
+ {generated_array_types}
+ ",
+ type_ = type_,
+ name = name,
+ author = &build_modinfo_string(&name, "author", &author),
+ description = &build_modinfo_string(&name, "description", &description),
+ license = &build_modinfo_string(&name, "license", &license),
+ file = &build_modinfo_string_only_builtin(&name, "file", &file),
+ params_modinfo = params_modinfo,
+ generated_array_types = generated_array_types,
+ initcall_section = ".initcall6.init"
+ ).parse().expect("Error parsing formatted string into token stream.")
+}
--
2.17.1
From: Miguel Ojeda <[email protected]>
These abstractions are work in progress. They are needed for
the next commit, which is the one intended to be reviewed.
Co-developed-by: Alex Gaynor <[email protected]>
Signed-off-by: Alex Gaynor <[email protected]>
Co-developed-by: Geoffrey Thomas <[email protected]>
Signed-off-by: Geoffrey Thomas <[email protected]>
Co-developed-by: Finn Behrens <[email protected]>
Signed-off-by: Finn Behrens <[email protected]>
Co-developed-by: Adam Bratschi-Kaye <[email protected]>
Signed-off-by: Adam Bratschi-Kaye <[email protected]>
Co-developed-by: Wedson Almeida Filho <[email protected]>
Signed-off-by: Wedson Almeida Filho <[email protected]>
Signed-off-by: Miguel Ojeda <[email protected]>
---
rust/kernel/lib.rs | 4 +
rust/kernel/linked_list.rs | 245 +++++++++++++++++++++++++
rust/kernel/pages.rs | 173 ++++++++++++++++++
rust/kernel/raw_list.rs | 361 +++++++++++++++++++++++++++++++++++++
4 files changed, 783 insertions(+)
create mode 100644 rust/kernel/linked_list.rs
create mode 100644 rust/kernel/pages.rs
create mode 100644 rust/kernel/raw_list.rs
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index 9a06bd60d5c1..c9ffeaae18a0 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -41,6 +41,10 @@ pub mod chrdev;
mod error;
pub mod file_operations;
pub mod miscdev;
+pub mod pages;
+
+pub mod linked_list;
+mod raw_list;
#[doc(hidden)]
pub mod module_param;
diff --git a/rust/kernel/linked_list.rs b/rust/kernel/linked_list.rs
new file mode 100644
index 000000000000..5b0b811eae22
--- /dev/null
+++ b/rust/kernel/linked_list.rs
@@ -0,0 +1,245 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Linked lists.
+//!
+//! TODO: This module is a work in progress.
+
+use alloc::{boxed::Box, sync::Arc};
+use core::ptr::NonNull;
+
+pub use crate::raw_list::{Cursor, GetLinks, Links};
+use crate::{raw_list, raw_list::RawList};
+
+// TODO: Use the one from `kernel::file_operations::PointerWrapper` instead.
+/// Wraps an object to be inserted in a linked list.
+pub trait Wrapper<T: ?Sized> {
+ /// Converts the wrapped object into a pointer that represents it.
+ fn into_pointer(self) -> NonNull<T>;
+
+ /// Converts the object back from the pointer representation.
+ ///
+ /// # Safety
+ ///
+ /// The passed pointer must come from a previous call to [`Wrapper::into_pointer()`].
+ unsafe fn from_pointer(ptr: NonNull<T>) -> Self;
+
+ /// Returns a reference to the wrapped object.
+ fn as_ref(&self) -> &T;
+}
+
+impl<T: ?Sized> Wrapper<T> for Box<T> {
+ fn into_pointer(self) -> NonNull<T> {
+ NonNull::new(Box::into_raw(self)).unwrap()
+ }
+
+ unsafe fn from_pointer(ptr: NonNull<T>) -> Self {
+ Box::from_raw(ptr.as_ptr())
+ }
+
+ fn as_ref(&self) -> &T {
+ AsRef::as_ref(self)
+ }
+}
+
+impl<T: ?Sized> Wrapper<T> for Arc<T> {
+ fn into_pointer(self) -> NonNull<T> {
+ NonNull::new(Arc::into_raw(self) as _).unwrap()
+ }
+
+ unsafe fn from_pointer(ptr: NonNull<T>) -> Self {
+ Arc::from_raw(ptr.as_ptr())
+ }
+
+ fn as_ref(&self) -> &T {
+ AsRef::as_ref(self)
+ }
+}
+
+impl<T: ?Sized> Wrapper<T> for &T {
+ fn into_pointer(self) -> NonNull<T> {
+ NonNull::from(self)
+ }
+
+ unsafe fn from_pointer(ptr: NonNull<T>) -> Self {
+ &*ptr.as_ptr()
+ }
+
+ fn as_ref(&self) -> &T {
+ self
+ }
+}
+
+/// A descriptor of wrapped list elements.
+pub trait GetLinksWrapped: GetLinks {
+ /// Specifies which wrapper (e.g., `Box` and `Arc`) wraps the list entries.
+ type Wrapped: Wrapper<Self::EntryType>;
+}
+
+impl<T: ?Sized> GetLinksWrapped for Box<T>
+where
+ Box<T>: GetLinks,
+{
+ type Wrapped = Box<<Box<T> as GetLinks>::EntryType>;
+}
+
+impl<T: GetLinks + ?Sized> GetLinks for Box<T> {
+ type EntryType = T::EntryType;
+ fn get_links(data: &Self::EntryType) -> &Links<Self::EntryType> {
+ <T as GetLinks>::get_links(data)
+ }
+}
+
+impl<T: ?Sized> GetLinksWrapped for Arc<T>
+where
+ Arc<T>: GetLinks,
+{
+ type Wrapped = Arc<<Arc<T> as GetLinks>::EntryType>;
+}
+
+impl<T: GetLinks + ?Sized> GetLinks for Arc<T> {
+ type EntryType = T::EntryType;
+ fn get_links(data: &Self::EntryType) -> &Links<Self::EntryType> {
+ <T as GetLinks>::get_links(data)
+ }
+}
+
+/// A linked list.
+///
+/// Elements in the list are wrapped and ownership is transferred to the list while the element is
+/// in the list.
+pub struct List<G: GetLinksWrapped> {
+ list: RawList<G>,
+}
+
+impl<G: GetLinksWrapped> List<G> {
+ /// Constructs a new empty linked list.
+ pub fn new() -> Self {
+ Self {
+ list: RawList::new(),
+ }
+ }
+
+ /// Returns whether the list is empty.
+ pub fn is_empty(&self) -> bool {
+ self.list.is_empty()
+ }
+
+ /// Adds the given object to the end (back) of the list.
+ ///
+ /// It is dropped if it's already on this (or another) list; this can happen for
+ /// reference-counted objects, so dropping means decrementing the reference count.
+ pub fn push_back(&mut self, data: G::Wrapped) {
+ let ptr = data.into_pointer();
+
+ // SAFETY: We took ownership of the entry, so it is safe to insert it.
+ if !unsafe { self.list.push_back(ptr.as_ref()) } {
+ // If insertion failed, rebuild object so that it can be freed.
+ // SAFETY: We just called `into_pointer` above.
+ unsafe { G::Wrapped::from_pointer(ptr) };
+ }
+ }
+
+ /// Inserts the given object after `existing`.
+ ///
+ /// It is dropped if it's already on this (or another) list; this can happen for
+ /// reference-counted objects, so dropping means decrementing the reference count.
+ ///
+ /// # Safety
+ ///
+ /// Callers must ensure that `existing` points to a valid entry that is on the list.
+ pub unsafe fn insert_after(&mut self, existing: NonNull<G::EntryType>, data: G::Wrapped) {
+ let ptr = data.into_pointer();
+ let entry = &*existing.as_ptr();
+ if !self.list.insert_after(entry, ptr.as_ref()) {
+ // If insertion failed, rebuild object so that it can be freed.
+ G::Wrapped::from_pointer(ptr);
+ }
+ }
+
+ /// Removes the given entry.
+ ///
+ /// # Safety
+ ///
+ /// Callers must ensure that `data` is either on this list or in no list. It being on another
+ /// list leads to memory unsafety.
+ pub unsafe fn remove(&mut self, data: &G::Wrapped) -> Option<G::Wrapped> {
+ let entry_ref = Wrapper::as_ref(data);
+ if self.list.remove(entry_ref) {
+ Some(G::Wrapped::from_pointer(NonNull::from(entry_ref)))
+ } else {
+ None
+ }
+ }
+
+ /// Removes the element currently at the front of the list and returns it.
+ ///
+ /// Returns `None` if the list is empty.
+ pub fn pop_front(&mut self) -> Option<G::Wrapped> {
+ let front = self.list.pop_front()?;
+ // SAFETY: Elements on the list were inserted after a call to `into_pointer `.
+ Some(unsafe { G::Wrapped::from_pointer(front) })
+ }
+
+ /// Returns a cursor starting on the first (front) element of the list.
+ pub fn cursor_front(&self) -> Cursor<'_, G> {
+ self.list.cursor_front()
+ }
+
+ /// Returns a mutable cursor starting on the first (front) element of the list.
+ pub fn cursor_front_mut(&mut self) -> CursorMut<'_, G> {
+ CursorMut::new(self.list.cursor_front_mut())
+ }
+}
+
+impl<G: GetLinksWrapped> Default for List<G> {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+impl<G: GetLinksWrapped> Drop for List<G> {
+ fn drop(&mut self) {
+ while self.pop_front().is_some() {}
+ }
+}
+
+/// A list cursor that allows traversing a linked list and inspecting & mutating elements.
+pub struct CursorMut<'a, G: GetLinksWrapped> {
+ cursor: raw_list::CursorMut<'a, G>,
+}
+
+impl<'a, G: GetLinksWrapped> CursorMut<'a, G> {
+ fn new(cursor: raw_list::CursorMut<'a, G>) -> Self {
+ Self { cursor }
+ }
+
+ /// Returns the element the cursor is currently positioned on.
+ pub fn current(&mut self) -> Option<&mut G::EntryType> {
+ self.cursor.current()
+ }
+
+ /// Removes the element the cursor is currently positioned on.
+ ///
+ /// After removal, it advances the cursor to the next element.
+ pub fn remove_current(&mut self) -> Option<G::Wrapped> {
+ let ptr = self.cursor.remove_current()?;
+
+ // SAFETY: Elements on the list were inserted after a call to `into_pointer `.
+ Some(unsafe { G::Wrapped::from_pointer(ptr) })
+ }
+
+ /// Returns the element immediately after the one the cursor is positioned on.
+ pub fn peek_next(&mut self) -> Option<&mut G::EntryType> {
+ self.cursor.peek_next()
+ }
+
+ /// Returns the element immediately before the one the cursor is positioned on.
+ pub fn peek_prev(&mut self) -> Option<&mut G::EntryType> {
+ self.cursor.peek_prev()
+ }
+
+ /// Moves the cursor to the next element.
+ pub fn move_next(&mut self) {
+ self.cursor.move_next();
+ }
+}
diff --git a/rust/kernel/pages.rs b/rust/kernel/pages.rs
new file mode 100644
index 000000000000..0426d411470d
--- /dev/null
+++ b/rust/kernel/pages.rs
@@ -0,0 +1,173 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Kernel page allocation and management.
+//!
+//! TODO: This module is a work in progress.
+
+use crate::{bindings, c_types, user_ptr::UserSlicePtrReader, Error, KernelResult, PAGE_SIZE};
+use core::{marker::PhantomData, ptr};
+
+extern "C" {
+ #[allow(improper_ctypes)]
+ fn rust_helper_alloc_pages(
+ gfp_mask: bindings::gfp_t,
+ order: c_types::c_uint,
+ ) -> *mut bindings::page;
+
+ #[allow(improper_ctypes)]
+ fn rust_helper_kmap(page: *mut bindings::page) -> *mut c_types::c_void;
+
+ #[allow(improper_ctypes)]
+ fn rust_helper_kunmap(page: *mut bindings::page);
+}
+
+/// A set of physical pages.
+///
+/// `Pages` holds a reference to a set of pages of order `ORDER`. Having the order as a generic
+/// const allows the struct to have the same size as a pointer.
+///
+/// # Invariants
+///
+/// The pointer [`Pages::pages`] is valid and points to 2^ORDER pages.
+pub struct Pages<const ORDER: u32> {
+ pages: *mut bindings::page,
+}
+
+impl<const ORDER: u32> Pages<ORDER> {
+ /// Allocates a new set of contiguous pages.
+ pub fn new() -> KernelResult<Self> {
+ // TODO: Consider whether we want to allow callers to specify flags.
+ // SAFETY: This only allocates pages. We check that it succeeds in the next statement.
+ let pages = unsafe {
+ rust_helper_alloc_pages(
+ bindings::GFP_KERNEL | bindings::__GFP_ZERO | bindings::__GFP_HIGHMEM,
+ ORDER,
+ )
+ };
+ if pages.is_null() {
+ return Err(Error::ENOMEM);
+ }
+ // INVARIANTS: We checked that the allocation above succeeded>
+ Ok(Self { pages })
+ }
+
+ /// Maps a single page at the given address in the given VM area.
+ ///
+ /// This is only meant to be used by pages of order 0.
+ pub fn insert_page(&self, vma: &mut bindings::vm_area_struct, address: usize) -> KernelResult {
+ if ORDER != 0 {
+ return Err(Error::EINVAL);
+ }
+
+ // SAFETY: We check above that the allocation is of order 0. The range of `address` is
+ // already checked by `vm_insert_page`.
+ let ret = unsafe { bindings::vm_insert_page(vma, address as _, self.pages) };
+ if ret != 0 {
+ Err(Error::from_kernel_errno(ret))
+ } else {
+ Ok(())
+ }
+ }
+
+ /// Copies data from the given [`UserSlicePtrReader`] into the pages.
+ pub fn copy_into_page(
+ &self,
+ reader: &mut UserSlicePtrReader,
+ offset: usize,
+ len: usize,
+ ) -> KernelResult {
+ // TODO: For now this only works on the first page.
+ let end = offset.checked_add(len).ok_or(Error::EINVAL)?;
+ if end > PAGE_SIZE {
+ return Err(Error::EINVAL);
+ }
+
+ let mapping = self.kmap(0).ok_or(Error::EINVAL)?;
+
+ // SAFETY: We ensured that the buffer was valid with the check above.
+ unsafe { reader.read_raw((mapping.ptr as usize + offset) as _, len) }?;
+ Ok(())
+ }
+
+ /// Maps the pages and reads from them into the given buffer.
+ ///
+ /// # Safety
+ ///
+ /// Callers must ensure that the destination buffer is valid for the given length.
+ /// Additionally, if the raw buffer is intended to be recast, they must ensure that the data
+ /// can be safely cast; [`crate::user_ptr::ReadableFromBytes`] has more details about it.
+ pub unsafe fn read(&self, dest: *mut u8, offset: usize, len: usize) -> KernelResult {
+ // TODO: For now this only works on the first page.
+ let end = offset.checked_add(len).ok_or(Error::EINVAL)?;
+ if end > PAGE_SIZE {
+ return Err(Error::EINVAL);
+ }
+
+ let mapping = self.kmap(0).ok_or(Error::EINVAL)?;
+ ptr::copy((mapping.ptr as *mut u8).add(offset), dest, len);
+ Ok(())
+ }
+
+ /// Maps the pages and writes into them from the given bufer.
+ ///
+ /// # Safety
+ ///
+ /// Callers must ensure that the buffer is valid for the given length. Additionally, if the
+ /// page is (or will be) mapped by userspace, they must ensure that no kernel data is leaked
+ /// through padding if it was cast from another type; [`crate::user_ptr::WritableToBytes`] has
+ /// more details about it.
+ pub unsafe fn write(&self, src: *const u8, offset: usize, len: usize) -> KernelResult {
+ // TODO: For now this only works on the first page.
+ let end = offset.checked_add(len).ok_or(Error::EINVAL)?;
+ if end > PAGE_SIZE {
+ return Err(Error::EINVAL);
+ }
+
+ let mapping = self.kmap(0).ok_or(Error::EINVAL)?;
+ ptr::copy(src, (mapping.ptr as *mut u8).add(offset), len);
+ Ok(())
+ }
+
+ /// Maps the page at index `index`.
+ fn kmap(&self, index: usize) -> Option<PageMapping> {
+ if index >= 1usize << ORDER {
+ return None;
+ }
+
+ // SAFETY: We checked above that `index` is within range.
+ let page = unsafe { self.pages.add(index) };
+
+ // SAFETY: `page` is valid based on the checks above.
+ let ptr = unsafe { rust_helper_kmap(page) };
+ if ptr.is_null() {
+ return None;
+ }
+
+ Some(PageMapping {
+ page,
+ ptr,
+ _phantom: PhantomData,
+ })
+ }
+}
+
+impl<const ORDER: u32> Drop for Pages<ORDER> {
+ fn drop(&mut self) {
+ // SAFETY: By the type invariants, we know the pages are allocated with the given order.
+ unsafe { bindings::__free_pages(self.pages, ORDER) };
+ }
+}
+
+struct PageMapping<'a> {
+ page: *mut bindings::page,
+ ptr: *mut c_types::c_void,
+ _phantom: PhantomData<&'a i32>,
+}
+
+impl Drop for PageMapping<'_> {
+ fn drop(&mut self) {
+ // SAFETY: An instance of `PageMapping` is created only when `kmap` succeeded for the given
+ // page, so it is safe to unmap it here.
+ unsafe { rust_helper_kunmap(self.page) };
+ }
+}
diff --git a/rust/kernel/raw_list.rs b/rust/kernel/raw_list.rs
new file mode 100644
index 000000000000..0202b44d560a
--- /dev/null
+++ b/rust/kernel/raw_list.rs
@@ -0,0 +1,361 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Raw lists.
+//!
+//! TODO: This module is a work in progress.
+
+use core::{
+ cell::UnsafeCell,
+ ptr,
+ ptr::NonNull,
+ sync::atomic::{AtomicBool, Ordering},
+};
+
+/// A descriptor of list elements.
+///
+/// It describes the type of list elements and provides a function to determine how to get the
+/// links to be used on a list.
+///
+/// A type that may be in multiple lists simultaneously neneds to implement one of these for each
+/// simultaneous list.
+pub trait GetLinks {
+ /// The type of the entries in the list.
+ type EntryType: ?Sized;
+
+ /// Returns the links to be used when linking an entry within a list.
+ fn get_links(data: &Self::EntryType) -> &Links<Self::EntryType>;
+}
+
+/// The links used to link an object on a linked list.
+///
+/// Instances of this type are usually embedded in structures and returned in calls to
+/// [`GetLinks::get_links`].
+pub struct Links<T: ?Sized> {
+ inserted: AtomicBool,
+ entry: UnsafeCell<ListEntry<T>>,
+}
+
+impl<T: ?Sized> Links<T> {
+ /// Constructs a new [`Links`] instance that isn't inserted on any lists yet.
+ pub fn new() -> Self {
+ Self {
+ inserted: AtomicBool::new(false),
+ entry: UnsafeCell::new(ListEntry::new()),
+ }
+ }
+
+ fn acquire_for_insertion(&self) -> bool {
+ self.inserted
+ .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
+ .is_ok()
+ }
+
+ fn release_after_removal(&self) {
+ self.inserted.store(false, Ordering::Release);
+ }
+}
+
+impl<T: ?Sized> Default for Links<T> {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+struct ListEntry<T: ?Sized> {
+ next: Option<NonNull<T>>,
+ prev: Option<NonNull<T>>,
+}
+
+impl<T: ?Sized> ListEntry<T> {
+ fn new() -> Self {
+ Self {
+ next: None,
+ prev: None,
+ }
+ }
+}
+
+/// A linked list.
+///
+/// # Invariants
+///
+/// The links of objects added to a list are owned by the list.
+pub(crate) struct RawList<G: GetLinks> {
+ head: Option<NonNull<G::EntryType>>,
+}
+
+impl<G: GetLinks> RawList<G> {
+ pub(crate) fn new() -> Self {
+ Self { head: None }
+ }
+
+ pub(crate) fn is_empty(&self) -> bool {
+ self.head.is_none()
+ }
+
+ fn insert_after_priv(
+ &mut self,
+ existing: &G::EntryType,
+ new_entry: &mut ListEntry<G::EntryType>,
+ new_ptr: Option<NonNull<G::EntryType>>,
+ ) {
+ {
+ // SAFETY: It's safe to get the previous entry of `existing` because the list cannot
+ // change.
+ let existing_links = unsafe { &mut *G::get_links(existing).entry.get() };
+ new_entry.next = existing_links.next;
+ existing_links.next = new_ptr;
+ }
+
+ new_entry.prev = Some(NonNull::from(existing));
+
+ // SAFETY: It's safe to get the next entry of `existing` because the list cannot change.
+ let next_links =
+ unsafe { &mut *G::get_links(new_entry.next.unwrap().as_ref()).entry.get() };
+ next_links.prev = new_ptr;
+ }
+
+ /// Inserts the given object after `existing`.
+ ///
+ /// # Safety
+ ///
+ /// Callers must ensure that `existing` points to a valid entry that is on the list.
+ pub(crate) unsafe fn insert_after(
+ &mut self,
+ existing: &G::EntryType,
+ new: &G::EntryType,
+ ) -> bool {
+ let links = G::get_links(new);
+ if !links.acquire_for_insertion() {
+ // Nothing to do if already inserted.
+ return false;
+ }
+
+ // SAFETY: The links are now owned by the list, so it is safe to get a mutable reference.
+ let new_entry = &mut *links.entry.get();
+ self.insert_after_priv(existing, new_entry, Some(NonNull::from(new)));
+ true
+ }
+
+ fn push_back_internal(&mut self, new: &G::EntryType) -> bool {
+ let links = G::get_links(new);
+ if !links.acquire_for_insertion() {
+ // Nothing to do if already inserted.
+ return false;
+ }
+
+ // SAFETY: The links are now owned by the list, so it is safe to get a mutable reference.
+ let new_entry = unsafe { &mut *links.entry.get() };
+ let new_ptr = Some(NonNull::from(new));
+ match self.back() {
+ // SAFETY: `back` is valid as the list cannot change.
+ Some(back) => self.insert_after_priv(unsafe { back.as_ref() }, new_entry, new_ptr),
+ None => {
+ self.head = new_ptr;
+ new_entry.next = new_ptr;
+ new_entry.prev = new_ptr;
+ }
+ }
+ true
+ }
+
+ pub(crate) unsafe fn push_back(&mut self, new: &G::EntryType) -> bool {
+ self.push_back_internal(new)
+ }
+
+ fn remove_internal(&mut self, data: &G::EntryType) -> bool {
+ let links = G::get_links(data);
+
+ // SAFETY: The links are now owned by the list, so it is safe to get a mutable reference.
+ let entry = unsafe { &mut *links.entry.get() };
+ let next = if let Some(next) = entry.next {
+ next
+ } else {
+ // Nothing to do if the entry is not on the list.
+ return false;
+ };
+
+ if ptr::eq(data, next.as_ptr()) {
+ // We're removing the only element.
+ self.head = None
+ } else {
+ // Update the head if we're removing it.
+ if let Some(raw_head) = self.head {
+ if ptr::eq(data, raw_head.as_ptr()) {
+ self.head = Some(next);
+ }
+ }
+
+ // SAFETY: It's safe to get the previous entry because the list cannot change.
+ unsafe { &mut *G::get_links(entry.prev.unwrap().as_ref()).entry.get() }.next =
+ entry.next;
+
+ // SAFETY: It's safe to get the next entry because the list cannot change.
+ unsafe { &mut *G::get_links(next.as_ref()).entry.get() }.prev = entry.prev;
+ }
+
+ // Reset the links of the element we're removing so that we know it's not on any list.
+ entry.next = None;
+ entry.prev = None;
+ links.release_after_removal();
+ true
+ }
+
+ /// Removes the given entry.
+ ///
+ /// # Safety
+ ///
+ /// Callers must ensure that `data` is either on this list or in no list. It being on another
+ /// list leads to memory unsafety.
+ pub(crate) unsafe fn remove(&mut self, data: &G::EntryType) -> bool {
+ self.remove_internal(data)
+ }
+
+ fn pop_front_internal(&mut self) -> Option<NonNull<G::EntryType>> {
+ let head = self.head?;
+ // SAFETY: The head is on the list as we just got it from there and it cannot change.
+ unsafe { self.remove(head.as_ref()) };
+ Some(head)
+ }
+
+ pub(crate) fn pop_front(&mut self) -> Option<NonNull<G::EntryType>> {
+ self.pop_front_internal()
+ }
+
+ pub(crate) fn front(&self) -> Option<NonNull<G::EntryType>> {
+ self.head
+ }
+
+ pub(crate) fn back(&self) -> Option<NonNull<G::EntryType>> {
+ // SAFETY: The links of head are owned by the list, so it is safe to get a reference.
+ unsafe { &*G::get_links(self.head?.as_ref()).entry.get() }.prev
+ }
+
+ pub(crate) fn cursor_front(&self) -> Cursor<'_, G> {
+ Cursor::new(self, self.front())
+ }
+
+ pub(crate) fn cursor_front_mut(&mut self) -> CursorMut<'_, G> {
+ CursorMut::new(self, self.front())
+ }
+}
+
+struct CommonCursor<G: GetLinks> {
+ cur: Option<NonNull<G::EntryType>>,
+}
+
+impl<G: GetLinks> CommonCursor<G> {
+ fn new(cur: Option<NonNull<G::EntryType>>) -> Self {
+ Self { cur }
+ }
+
+ fn move_next(&mut self, list: &RawList<G>) {
+ match self.cur.take() {
+ None => self.cur = list.head,
+ Some(cur) => {
+ if let Some(head) = list.head {
+ // SAFETY: We have a shared ref to the linked list, so the links can't change.
+ let links = unsafe { &*G::get_links(cur.as_ref()).entry.get() };
+ if links.next.unwrap() != head {
+ self.cur = links.next;
+ }
+ }
+ }
+ }
+ }
+
+ fn move_prev(&mut self, list: &RawList<G>) {
+ match list.head {
+ None => self.cur = None,
+ Some(head) => {
+ let next = match self.cur.take() {
+ None => head,
+ Some(cur) => {
+ if cur == head {
+ return;
+ }
+ cur
+ }
+ };
+ // SAFETY: There's a shared ref to the list, so the links can't change.
+ let links = unsafe { &*G::get_links(next.as_ref()).entry.get() };
+ self.cur = links.prev;
+ }
+ }
+ }
+}
+
+/// A list cursor that allows traversing a linked list and inspecting elements.
+pub struct Cursor<'a, G: GetLinks> {
+ cursor: CommonCursor<G>,
+ list: &'a RawList<G>,
+}
+
+impl<'a, G: GetLinks> Cursor<'a, G> {
+ fn new(list: &'a RawList<G>, cur: Option<NonNull<G::EntryType>>) -> Self {
+ Self {
+ list,
+ cursor: CommonCursor::new(cur),
+ }
+ }
+
+ /// Returns the element the cursor is currently positioned on.
+ pub fn current(&self) -> Option<&'a G::EntryType> {
+ let cur = self.cursor.cur?;
+ // SAFETY: Objects must be kept alive while on the list.
+ Some(unsafe { &*cur.as_ptr() })
+ }
+
+ /// Moves the cursor to the next element.
+ pub fn move_next(&mut self) {
+ self.cursor.move_next(self.list);
+ }
+}
+
+pub(crate) struct CursorMut<'a, G: GetLinks> {
+ cursor: CommonCursor<G>,
+ list: &'a mut RawList<G>,
+}
+
+impl<'a, G: GetLinks> CursorMut<'a, G> {
+ fn new(list: &'a mut RawList<G>, cur: Option<NonNull<G::EntryType>>) -> Self {
+ Self {
+ list,
+ cursor: CommonCursor::new(cur),
+ }
+ }
+
+ pub(crate) fn current(&mut self) -> Option<&mut G::EntryType> {
+ let cur = self.cursor.cur?;
+ // SAFETY: Objects must be kept alive while on the list.
+ Some(unsafe { &mut *cur.as_ptr() })
+ }
+
+ /// Removes the entry the cursor is pointing to and advances the cursor to the next entry. It
+ /// returns a raw pointer to the removed element (if one is removed).
+ pub(crate) fn remove_current(&mut self) -> Option<NonNull<G::EntryType>> {
+ let entry = self.cursor.cur?;
+ self.cursor.move_next(self.list);
+ // SAFETY: The entry is on the list as we just got it from there and it cannot change.
+ unsafe { self.list.remove(entry.as_ref()) };
+ Some(entry)
+ }
+
+ pub(crate) fn peek_next(&mut self) -> Option<&mut G::EntryType> {
+ let mut new = CommonCursor::new(self.cursor.cur);
+ new.move_next(self.list);
+ // SAFETY: Objects must be kept alive while on the list.
+ Some(unsafe { &mut *new.cur?.as_ptr() })
+ }
+
+ pub(crate) fn peek_prev(&mut self) -> Option<&mut G::EntryType> {
+ let mut new = CommonCursor::new(self.cursor.cur);
+ new.move_prev(self.list);
+ // SAFETY: Objects must be kept alive while on the list.
+ Some(unsafe { &mut *new.cur?.as_ptr() })
+ }
+
+ pub(crate) fn move_next(&mut self) {
+ self.cursor.move_next(self.list);
+ }
+}
--
2.17.1
On Wed, Apr 14, 2021 at 11:47 AM <[email protected]> wrote:
>
> +#[alloc_error_handler]
> +fn oom(_layout: Layout) -> ! {
> + panic!("Out of memory!");
> +}
> +
> +#[no_mangle]
> +pub fn __rust_alloc_error_handler(_size: usize, _align: usize) -> ! {
> + panic!("Out of memory!");
> +}
Again, excuse my lack of internal Rust knowledge, but when do these
end up being an issue?
If the Rust compiler ends up doing hidden allocations, and they then
cause panics, then one of the main *points* of Rustification is
entirely broken. That's 100% the opposite of being memory-safe at
build time.
An allocation failure in some random driver must never ever be
something that the compiler just turns into a panic. It must be
something that is caught and handled synchronously and results in an
ENOMEM error return.
So the fact that the core patches have these kinds of
panic!("Out of memory!");
things in them as part of just the support infrastructure makes me go
"Yeah, that's fundamentally wrong".
And if this is some default that is called only when the Rust code
doesn't have error handling, then once again - I think it needs to be
a *build-time* failure, not a runtime one. Because having unsafe code
that will cause a panic only under very special situations that are
hard to trigger is about the worst possible case.
Linus
On Wed, Apr 14, 2021 at 11:47 AM <[email protected]> wrote:
>
> From: Miguel Ojeda <[email protected]>
>
> A set of Rust modules that showcase how Rust modules look like
> and how to use the abstracted kernel features.
Honestly, I'd like to see a real example. This is fine for testing,
but I'd like to see something a bit more real, and a bit less special
than the Android "binder" WIP that comes a few patches later.
Would there be some kind of real driver or something that people could
use as a example of a real piece of code that actually does something
meaningful?
Linus
On Wed, Apr 14, 2021 at 9:34 PM Linus Torvalds
<[email protected]> wrote:
>
> Honestly, I'd like to see a real example. This is fine for testing,
> but I'd like to see something a bit more real, and a bit less special
> than the Android "binder" WIP that comes a few patches later.
>
> Would there be some kind of real driver or something that people could
> use as a example of a real piece of code that actually does something
> meaningful?
Yeah, we are planning to write a couple of drivers that talk to actual
hardware. Not sure which ones we will do, but we will have them
written.
Cheers,
Miguel
On Wed, Apr 14, 2021 at 08:45:52PM +0200, [email protected] wrote:
> Increasing to 255 is not enough in some cases, and therefore
> we need to introduce 2-byte lengths to the symbol table. We call
> these "big" symbols.
>
> In order to avoid increasing all lengths to 2 bytes (since most
> of them only require 1 byte, including many Rust ones), we use
> length zero to mark "big" symbols in the table.
How about doing something a bit more utf-8-like?
len = data[0];
if (len == 0)
error
else if (len < 128)
return len;
else if (len < 192)
return 128 + (len - 128) * 256 + data[1];
... that takes you all the way out to 16511 bytes. You probably don't
even need the third byte option. But if you do ...
else if (len < 223)
return 16512 + (len - 192) * 256 * 256 +
data[1] * 256 + data[2];
which takes you all the way out to 2,113,663 bytes and leaves 224-255 unused.
Alternatively, if the symbols are really this long, perhaps we should not
do string matches. A sha-1 (... or whatever ...) hash of the function
name is 160 bits. Expressed as hex digits, that's 40 characters.
Expressed in base-64, it's 27 characters. We'd also want a "pretty"
name to go along with the hash, but that seems preferable to printing
out a mangled-with-types-and-who-knows-what name.
> Co-developed-by: Alex Gaynor <[email protected]>
> Signed-off-by: Alex Gaynor <[email protected]>
If you have C-d-b, you don't also need S-o-b.
On Wed, Apr 14, 2021 at 9:31 PM Linus Torvalds
<[email protected]> wrote:
>
> Again, excuse my lack of internal Rust knowledge, but when do these
> end up being an issue?
>
> If the Rust compiler ends up doing hidden allocations, and they then
> cause panics, then one of the main *points* of Rustification is
> entirely broken. That's 100% the opposite of being memory-safe at
> build time.
Of course! What happens here is that we use, for the moment, `alloc`,
which is part of the Rust standard library. However, we will be
customizing/rewriting `alloc` as needed to customize its types (things
like `Box`, `Vec`, etc.) so that we can do things like pass allocation
flags, ensure we always have fallible allocations, perhaps reuse some
of the kernel data structures, etc.
Cheers,
Miguel
On Wed, Apr 14, 2021 at 9:45 PM Matthew Wilcox <[email protected]> wrote:
>
> How about doing something a bit more utf-8-like?
>
> len = data[0];
> if (len == 0)
> error
> else if (len < 128)
> return len;
> else if (len < 192)
> return 128 + (len - 128) * 256 + data[1];
> ... that takes you all the way out to 16511 bytes. You probably don't
That would save some space and allow us to keep the 0 as an error, yeah.
> Alternatively, if the symbols are really this long, perhaps we should not
> do string matches. A sha-1 (... or whatever ...) hash of the function
> name is 160 bits. Expressed as hex digits, that's 40 characters.
> Expressed in base-64, it's 27 characters. We'd also want a "pretty"
> name to go along with the hash, but that seems preferable to printing
> out a mangled-with-types-and-who-knows-what name.
I have seen symbols up to ~300, but I don't think we will ever go up
to more than, say, 1024, unless we start to go crazy with generics,
namespaces and what not.
Hashing could be a nice solution if they really grow, yeah.
> If you have C-d-b, you don't also need S-o-b.
Hmm... `submitting-patches.rst` keeps the S-o-b in the example they
give, is it outdated?
Cheers,
Miguel
On Wed, Apr 14, 2021 at 1:10 PM Matthew Wilcox <[email protected]> wrote:
>
> There's a philosophical point to be discussed here which you're skating
> right over! Should rust-in-the-linux-kernel provide the same memory
> allocation APIs as the rust-standard-library, or should it provide a Rusty
> API to the standard-linux-memory-allocation APIs?
Yeah, I think that the standard Rust API may simply not be acceptable
inside the kernel, if it has similar behavior to the (completely
broken) C++ "new" operator.
So anything that does "panic!" in the normal Rust API model needs to
be (statically) caught, and never exposed as an actual call to
"panic()/BUG()" in the kernel.
So "Result<T, E>" is basically the way to go, and if the standard Rust
library alloc() model is based on "panic!" then that kind of model
must simply not be used in the kernel.
Linus
Linus
On Wed, Apr 14, 2021 at 08:45:51PM +0200, [email protected] wrote:
> - Manish Goregaokar implemented the fallible `Box`, `Arc`, and `Rc`
> allocator APIs in Rust's `alloc` standard library for us.
There's a philosophical point to be discussed here which you're skating
right over! Should rust-in-the-linux-kernel provide the same memory
allocation APIs as the rust-standard-library, or should it provide a Rusty
API to the standard-linux-memory-allocation APIs? You seem to be doing
both ... there was a wrapper around alloc_pages() in the Binder patches,
and then you talk about Box, Arc and Rc here.
Maybe there's some details about when one can use one kind of API and
when to use another. But I fear that we'll have Rust code at interrupt
level trying to use allocators which assume that they can sleep, and
things will go badly wrong.
By the way, I don't think that Rust necessarily has to conform to the
current way that Linux works. If this prompted us to track the current
context (inside spinlock, handling interrupt, performing writeback, etc)
and do away with (some) GFP flags, that's not the end of the world.
We're already moving in that direction to a certain extent with the
scoped memory allocation APIs to replace GFP_NOFS / GFP_NOIO.
On Wed, Apr 14, 2021 at 10:10 PM Matthew Wilcox <[email protected]> wrote:
>
> On Wed, Apr 14, 2021 at 08:45:51PM +0200, [email protected] wrote:
> > - Manish Goregaokar implemented the fallible `Box`, `Arc`, and `Rc`
> > allocator APIs in Rust's `alloc` standard library for us.
>
> There's a philosophical point to be discussed here which you're skating
> right over! Should rust-in-the-linux-kernel provide the same memory
> allocation APIs as the rust-standard-library, or should it provide a Rusty
> API to the standard-linux-memory-allocation APIs? You seem to be doing
> both ... there was a wrapper around alloc_pages() in the Binder patches,
> and then you talk about Box, Arc and Rc here.
Please see my reply to Linus. The Rust standard library team is doing
work on allocators, fallible allocations, etc., but that is very much
a WIP. We hope that our usage and needs inform them in their design.
Manish Goregaokar implemented the `try_reserve` feature since he knew
we wanted to have fallible allocations etc. (I was not really involved
in that, perhaps somebody else can comment); but we will have to
replace `alloc` anyway in the near feature, and we wanted to give
Manish credit for advancing the state of the art there nevertheless.
> Maybe there's some details about when one can use one kind of API and
> when to use another. But I fear that we'll have Rust code at interrupt
> level trying to use allocators which assume that they can sleep, and
> things will go badly wrong.
Definitely. In fact, we want to have all public functions exposed by
Rust infrastructure tagged with the context they can work in, etc.
Ideally, we could propose a language feature like "colored `unsafe`"
so that one can actually inform the compiler that a function is only
safe in some contexts, e.g. `unsafe(interrupt)`. But language features
are a moonshot, for the moment we want to go with the annotation in
the doc-comment, like we do with the `Safety` preconditions and type
invariants.
Cheers,
Miguel
On Wed, Apr 14, 2021 at 11:46 AM <[email protected]> wrote:
>
> 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.
So I replied with my reactions to a couple of the individual patches,
but on the whole I don't hate it.
HOWEVER.
I do think that the "run-time failure panic" is a fundamental issue.
I may not understand the ramifications of when it can happen, so maybe
it's less of an issue than I think it is, but very fundamentally I
think that if some Rust allocation can cause a panic, this is simply
_fundamentally_ not acceptable.
Allocation failures in a driver or non-core code - and that is by
definition all of any new Rust code - can never EVER validly cause
panics. Same goes for "oh, some case I didn't test used 128-bit
integers or floating point".
So if the Rust compiler causes hidden allocations that cannot be
caught and returned as errors, then I seriously think that this whole
approach needs to be entirely NAK'ed, and the Rust infrastructure -
whether at the compiler level or in the kernel wrappers - needs more
work.
So if the panic was just some placeholder for things that _can_ be
caught, then I think that catching code absolutely needs to be
written, and not left as a to-do.
And if the panic situation is some fundamental "this is what the Rust
compiler does for internal allocation failures", then I think it needs
more than just kernel wrapper work - it needs the Rust compiler to be
*fixed*.
Because kernel code is different from random user-space system tools.
Running out of memory simply MUST NOT cause an abort. It needs to
just result in an error return.
I don't know enough about how the out-of-memory situations would be
triggered and caught to actually know whether this is a fundamental
problem or not, so my reaction comes from ignorance, but basically the
rule has to be that there are absolutely zero run-time "panic()"
calls. Unsafe code has to either be caught at compile time, or it has
to be handled dynamically as just a regular error.
With the main point of Rust being safety, there is no way I will ever
accept "panic dynamically" (whether due to out-of-memory or due to
anything else - I also reacted to the "floating point use causes
dynamic panics") as a feature in the Rust model.
Linus
On Wed, Apr 14, 2021 at 09:42:26PM +0200, Miguel Ojeda wrote:
> On Wed, Apr 14, 2021 at 9:34 PM Linus Torvalds
> <[email protected]> wrote:
> >
> > Honestly, I'd like to see a real example. This is fine for testing,
> > but I'd like to see something a bit more real, and a bit less special
> > than the Android "binder" WIP that comes a few patches later.
> >
> > Would there be some kind of real driver or something that people could
> > use as a example of a real piece of code that actually does something
> > meaningful?
>
> Yeah, we are planning to write a couple of drivers that talk to actual
> hardware. Not sure which ones we will do, but we will have them
> written.
I'd suggest NVMe as a target. It's readily available, both as real
hardware and in (eg) qemu. The spec is freely available, and most devices
come pretty close to conforming to the spec until you start to push hard
at the edges. Also then you can do performance tests and see where you
might want to focus performance efforts.
On Wed, Apr 14, 2021 at 9:45 PM Linus Torvalds
<[email protected]> wrote:
>
> With the main point of Rust being safety, there is no way I will ever
> accept "panic dynamically" (whether due to out-of-memory or due to
> anything else - I also reacted to the "floating point use causes
> dynamic panics") as a feature in the Rust model.
Agreed on all your points. As I mentioned in the other message (we
crossed emails), we have a lot of work to do regarding `alloc` and
slicing `core` for things that are not needed for the kernel
(floating-point, etc.).
We haven't done it just yet because it is not a trivial amount of work
and we wanted to have some overall sentiment from you and the
community overall before tackling everything. But it is doable and
there isn't any fundamental reason that prevents it (in fact, the
language supports no-allocation code).
Worst case, we may need to request a few bits here and there to the
`rustc` and standard library teams, but that should be about it.
In summary, to be clear:
- On allocation: this is just our usage of `alloc` in order to speed
development up -- it will be replaced (or customized, we have to
decide how we will approach it) with our own allocation and data
structures.
- On floating-point, 128-bit, etc.: the main issue is that the
`core` library is a single big blob at the moment. I have already
mentioned this to some Rust team folks. We will need a way to "cut"
some things out, for instance with the "feature flags" they already
have for other crates (or they can split `core` in to several, like
`alloc` is for similar reasons). Or we could do it on our side
somehow, but I prefer to avoid that (we cannot easily customize `core`
like we can with `alloc`, because it is tied to the compiler too
tightly).
Thanks a lot for having taken a look so quickly, by the way!
Cheers,
Miguel
On Wed, Apr 14, 2021 at 01:21:52PM -0700, Linus Torvalds wrote:
> On Wed, Apr 14, 2021 at 1:10 PM Matthew Wilcox <[email protected]> wrote:
> >
> > There's a philosophical point to be discussed here which you're skating
> > right over! Should rust-in-the-linux-kernel provide the same memory
> > allocation APIs as the rust-standard-library, or should it provide a Rusty
> > API to the standard-linux-memory-allocation APIs?
>
> Yeah, I think that the standard Rust API may simply not be acceptable
> inside the kernel, if it has similar behavior to the (completely
> broken) C++ "new" operator.
>
> So anything that does "panic!" in the normal Rust API model needs to
> be (statically) caught, and never exposed as an actual call to
> "panic()/BUG()" in the kernel.
Rust has both kinds of allocation APIs: you can call a method like
`Box::new` that panics on allocation failure, or a method like
`Box::try_new` that returns an error on allocation failure.
With some additional infrastructure that's still in progress, we could
just not supply the former kind of methods at all, and *only* supply the
latter, so that you're forced to handle allocation failure. That just
requires introducing some further ability to customize the Rust standard
library.
(There are some cases of methods in the standard library that don't have
a `try_` equivalent, but we could fix that. Right now, for instance,
there isn't a `try_` equivalent of every Vec method, and you're instead
expected to call `try_reserve` to make sure you have enough memory
first; however, that could potentially be changed.)
On Wed, Apr 14, 2021 at 11:50 AM <[email protected]> wrote:
>
> From: Miguel Ojeda <[email protected]>
>
> Miguel, Alex and Wedson will be maintaining the Rust support.
>
> Co-developed-by: Alex Gaynor <[email protected]>
> Signed-off-by: Alex Gaynor <[email protected]>
> Co-developed-by: Wedson Almeida Filho <[email protected]>
> Signed-off-by: Wedson Almeida Filho <[email protected]>
> Signed-off-by: Miguel Ojeda <[email protected]>
> ---
> MAINTAINERS | 14 ++++++++++++++
> 1 file changed, 14 insertions(+)
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 9e876927c60d..de32aaa5cabd 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -15547,6 +15547,20 @@ L: [email protected]
> S: Maintained
> F: drivers/infiniband/ulp/rtrs/
>
> +RUST
> +M: Miguel Ojeda <[email protected]>
> +M: Alex Gaynor <[email protected]>
> +M: Wedson Almeida Filho <[email protected]>
> +L: [email protected]
> +S: Maintained
Assuming this will at least be part of Wedson's core responsibilities,
shouldn't this be "Supported." Per Maintainers:
87 S: *Status*, one of the following:
88 Supported: Someone is actually paid to look after this.
89 Maintained: Someone actually looks after it.
Either way,
Acked-by: Nick Desaulniers <[email protected]>
> +W: https://github.com/Rust-for-Linux/linux
> +B: https://github.com/Rust-for-Linux/linux/issues
> +T: git https://github.com/Rust-for-Linux/linux.git rust-next
> +F: rust/
> +F: samples/rust/
> +F: Documentation/rust/
> +K: \b(?i:rust)\b
> +
> RXRPC SOCKETS (AF_RXRPC)
> M: David Howells <[email protected]>
> L: [email protected]
> --
> 2.17.1
>
--
Thanks,
~Nick Desaulniers
From: Linus Torvalds
> Sent: 14 April 2021 21:22
>
> On Wed, Apr 14, 2021 at 1:10 PM Matthew Wilcox <[email protected]> wrote:
> >
> > There's a philosophical point to be discussed here which you're skating
> > right over! Should rust-in-the-linux-kernel provide the same memory
> > allocation APIs as the rust-standard-library, or should it provide a Rusty
> > API to the standard-linux-memory-allocation APIs?
>
> Yeah, I think that the standard Rust API may simply not be acceptable
> inside the kernel, if it has similar behavior to the (completely
> broken) C++ "new" operator.
ISTM that having memory allocation failure cause a user process
to exit is a complete failure in something designed to run as
and kind of service program.
There are all sorts of reasons why malloc() might fail.
You almost never want a 'real' program to abort on one.
David
-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK
Registration No: 1397386 (Wales)
On Wed, Apr 14, 2021 at 11:50 AM <[email protected]> wrote:
>
> From: Miguel Ojeda <[email protected]>
>
> Most of the documentation for Rust is written within the source code
> itself, as it is idiomatic for Rust projects. This applies to both
> the shared infrastructure at `rust/` as well as any other Rust module
> (e.g. drivers) written across the kernel.
>
> These documents are general information that do not fit particularly
> well in the source code, like the quick start guide.
>
> Co-developed-by: Alex Gaynor <[email protected]>
> Signed-off-by: Alex Gaynor <[email protected]>
> Co-developed-by: Geoffrey Thomas <[email protected]>
> Signed-off-by: Geoffrey Thomas <[email protected]>
> Co-developed-by: Finn Behrens <[email protected]>
> Signed-off-by: Finn Behrens <[email protected]>
> Co-developed-by: Adam Bratschi-Kaye <[email protected]>
> Signed-off-by: Adam Bratschi-Kaye <[email protected]>
> Co-developed-by: Wedson Almeida Filho <[email protected]>
> Signed-off-by: Wedson Almeida Filho <[email protected]>
> Signed-off-by: Miguel Ojeda <[email protected]>
> ---
> Documentation/doc-guide/kernel-doc.rst | 3 +
> Documentation/index.rst | 1 +
> 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 +++++++++++++++++++++++++
> 7 files changed, 457 insertions(+)
> 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
>
> diff --git a/Documentation/doc-guide/kernel-doc.rst b/Documentation/doc-guide/kernel-doc.rst
> index 79aaa55d6bcf..c655fdb9c042 100644
> --- a/Documentation/doc-guide/kernel-doc.rst
> +++ b/Documentation/doc-guide/kernel-doc.rst
> @@ -11,6 +11,9 @@ when it is embedded in source files.
> reasons. The kernel source contains tens of thousands of kernel-doc
> comments. Please stick to the style described here.
>
> +.. note:: kernel-doc does not cover Rust code: please see
> + :ref:`Documentation/rust/docs.rst <rust_docs>` instead.
> +
> The kernel-doc structure is extracted from the comments, and proper
> `Sphinx C Domain`_ function and type descriptions with anchors are
> generated from them. The descriptions are filtered for special kernel-doc
> diff --git a/Documentation/index.rst b/Documentation/index.rst
> index 31f2adc8542d..3e7c43a48e68 100644
> --- a/Documentation/index.rst
> +++ b/Documentation/index.rst
> @@ -82,6 +82,7 @@ merged much easier.
> maintainer/index
> fault-injection/index
> livepatch/index
> + rust/index
>
>
> Kernel API documentation
> diff --git a/Documentation/rust/arch-support.rst b/Documentation/rust/arch-support.rst
> new file mode 100644
> index 000000000000..d72ab2f8fa46
> --- /dev/null
> +++ b/Documentation/rust/arch-support.rst
> @@ -0,0 +1,29 @@
> +.. _rust_arch_support:
> +
> +Arch Support
> +============
> +
> +Currently, the Rust compiler (``rustc``) uses LLVM for code generation,
> +which limits the supported architectures we can target. In addition, support
> +for building the kernel with LLVM/Clang varies (see :ref:`kbuild_llvm`),
> +which ``bindgen`` relies on through ``libclang``.
> +
> +Below is a general summary of architectures that currently work. Level of
> +support corresponds to ``S`` values in the ``MAINTAINERS`` file.
> +
> +.. list-table::
> + :widths: 10 10 10
> + :header-rows: 1
> +
> + * - Architecture
> + - Level of support
> + - Constraints
> + * - ``arm64``
> + - Maintained
> + - None
> + * - ``powerpc``
> + - Maintained
> + - ``ppc64le`` only, ``RUST_OPT_LEVEL >= 2``
> + * - ``x86``
> + - Maintained
> + - ``x86_64`` only
> diff --git a/Documentation/rust/coding.rst b/Documentation/rust/coding.rst
> new file mode 100644
> index 000000000000..46da5fb0974c
> --- /dev/null
> +++ b/Documentation/rust/coding.rst
> @@ -0,0 +1,92 @@
> +.. _rust_coding:
> +
> +Coding
> +======
> +
> +This document describes how to write Rust code in the kernel.
> +
> +
> +Coding style
> +------------
> +
> +The code is automatically formatted using the ``rustfmt`` tool. This is very
> +good news!
> +
> +- If you contribute from time to time to the kernel, you do not need to learn
> + and remember one more style guide. You will also need less patch roundtrips
> + to land a change.
> +
> +- If you are a reviewer or a maintainer, you will not need to spend time on
> + pointing out style issues anymore.
> +
> +.. note:: Conventions on comments and documentation are not checked by
> + ``rustfmt``. Thus we still need to take care of those: please see
> + :ref:`Documentation/rust/docs.rst <rust_docs>`.
> +
> +We use the tool's default settings. This means we are following the idiomatic
> +Rust style. For instance, we use 4 spaces for indentation rather than tabs.
> +
> +Typically, you will want to instruct your editor/IDE to format while you type,
> +when you save or at commit time. However, if for some reason you want
> +to reformat the entire kernel Rust sources at some point, you may run::
> +
> + make rustfmt
> +
> +To check if everything is formatted (printing a diff otherwise), e.g. if you
> +have configured a CI for a tree as a maintainer, you may run::
> +
> + make rustfmtcheck
> +
> +Like ``clang-format`` for the rest of the kernel, ``rustfmt`` works on
> +individual files, and does not require a kernel configuration. Sometimes it may
> +even work with broken code.
> +
> +
> +Extra lints
> +-----------
> +
> +While ``rustc`` is a very helpful compiler, some extra lints and analysis are
> +available via ``clippy``, a Rust linter. To enable it, pass ``CLIPPY=1`` to
> +the same invocation you use for compilation, e.g.::
> +
> + make ARCH=... CROSS_COMPILE=... CC=... -j... CLIPPY=1
> +
> +At the moment, we do not enforce a "clippy-free" compilation, so you can treat
> +the output the same way as the extra warning levels for C, e.g. like ``W=2``.
> +Still, we use the default configuration, which is relatively conservative, so
> +it is a good idea to read any output it may produce from time to time and fix
> +the pointed out issues. The list of enabled lists will be likely tweaked over
> +time, and extra levels may end up being introduced, e.g. ``CLIPPY=2``.
> +
> +
> +Abstractions vs. bindings
> +-------------------------
> +
> +We don't have abstractions for all the kernel internal APIs and concepts,
> +but we would like to expand coverage as time goes on. Unless there is
> +a good reason not to, always use the abstractions instead of calling
> +the C bindings directly.
> +
> +If you are writing some code that requires a call to an internal kernel API
> +or concept that isn't abstracted yet, consider providing an (ideally safe)
> +abstraction for everyone to use.
> +
> +
> +Conditional compilation
> +-----------------------
> +
> +Rust code has access to conditional compilation based on the kernel
> +configuration:
> +
> +.. code-block:: rust
> +
> + #[cfg(CONFIG_X)] // `CONFIG_X` is enabled (`y` or `m`)
> + #[cfg(CONFIG_X="y")] // `CONFIG_X` is enabled as a built-in (`y`)
> + #[cfg(CONFIG_X="m")] // `CONFIG_X` is enabled as a module (`m`)
> + #[cfg(not(CONFIG_X))] // `CONFIG_X` is disabled
> +
> +
> +Documentation
> +-------------
> +
> +Please see :ref:`Documentation/rust/docs.rst <rust_docs>`.
> diff --git a/Documentation/rust/docs.rst b/Documentation/rust/docs.rst
> new file mode 100644
> index 000000000000..58c5f98ccb35
> --- /dev/null
> +++ b/Documentation/rust/docs.rst
> @@ -0,0 +1,109 @@
> +.. _rust_docs:
> +
> +Docs
> +====
> +
> +Rust kernel code is not documented like C kernel code (i.e. via kernel-doc).
> +Instead, we use the usual system for documenting Rust code: the ``rustdoc``
> +tool, which uses Markdown (a *very* lightweight markup language).
> +
> +This document describes how to make the most out of the kernel documentation
> +for Rust.
> +
> +
> +Reading the docs
> +----------------
> +
> +An advantage of using Markdown is that it attempts to make text look almost as
> +you would have written it in plain text. This makes the documentation quite
> +pleasant to read even in its source form.
> +
> +However, the generated HTML docs produced by ``rustdoc`` provide a *very* nice
> +experience, including integrated instant search, clickable items (types,
> +functions, constants, etc. -- including to all the standard Rust library ones
> +that we use in the kernel, e.g. ``core``), categorization, links to the source
> +code, etc.
> +
> +Like for the rest of the kernel documentation, pregenerated HTML docs for
> +the libraries (crates) inside ``rust/`` that are used by the rest of the kernel
> +are available at `kernel.org`_.
> +
> +// TODO: link when ready
Was this TODO meant to be removed, or is it still pending? If pending,
on what? Being able to link back on itself if/once merged?
> +
> +.. _kernel.org: http://kernel.org/
> +
> +Otherwise, you can generate them locally. This is quite fast (same order as
> +compiling the code itself) and you do not need any special tools or environment.
> +This has the added advantage that they will be tailored to your particular
> +kernel configuration. To generate them, simply use the ``rustdoc`` target with
> +the same invocation you use for compilation, e.g.::
> +
> + make ARCH=... CROSS_COMPILE=... CC=... -j... rustdoc
> +
> +
> +Writing the docs
> +----------------
> +
> +If you already know Markdown, learning how to write Rust documentation will be
> +a breeze. If not, understanding the basics is a matter of minutes reading other
> +code. There are also many guides available out there, a particularly nice one
> +is at `GitHub`_.
> +
> +.. _GitHub: https://guides.github.com/features/mastering-markdown/#syntax
> +
> +This is how a well-documented Rust function may look like (derived from the Rust
> +standard library)::
> +
> + /// Returns the contained [`Some`] value, consuming the `self` value,
> + /// without checking that the value is not [`None`].
> + ///
> + /// # Safety
> + ///
> + /// Calling this method on [`None`] is *[undefined behavior]*.
> + ///
> + /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
> + ///
> + /// # Examples
> + ///
> + /// ```
> + /// let x = Some("air");
> + /// assert_eq!(unsafe { x.unwrap_unchecked() }, "air");
> + /// ```
> + pub unsafe fn unwrap_unchecked(self) -> T {
> + match self {
> + Some(val) => val,
> +
> + // SAFETY: the safety contract must be upheld by the caller.
> + None => unsafe { hint::unreachable_unchecked() },
> + }
> + }
> +
> +This example showcases a few ``rustdoc`` features and some common conventions
> +(that we also follow in the kernel):
> +
> +* The first paragraph must be a single sentence briefly describing what
> + the documented item does. Further explanations must go in extra paragraphs.
> +
> +* ``unsafe`` functions must document the preconditions needed for a call to be
> + safe under a ``Safety`` section.
> +
> +* While not shown here, if a function may panic, the conditions under which
> + that happens must be described under a ``Panics`` section.
Consider if the docs need any change here based on behavior related to
Panics based on feedback thus far in the thread.
> +
> +* If providing examples of usage would help readers, they must be written in
> + a section called ``Examples``.
> +
> +* Rust items (functions, types, constants...) will be automatically linked
> + (``rustdoc`` will find out the URL for you).
> +
> +* Following the Rust standard library conventions, any ``unsafe`` block must be
> + preceded by a ``SAFETY`` comment describing why the code inside is sound.
> +
> + While sometimes the reason might look trivial and therefore unneeded, writing
> + these comments is not just a good way of documenting what has been taken into
> + account, but also that there are no *extra* implicit constraints.
> +
> +To learn more about how to write documentation for Rust and extra features,
> +please take a look at the ``rustdoc`` `book`_.
> +
> +.. _book: https://doc.rust-lang.org/rustdoc/how-to-write-documentation.html
> diff --git a/Documentation/rust/index.rst b/Documentation/rust/index.rst
> new file mode 100644
> index 000000000000..257cf2b200b8
> --- /dev/null
> +++ b/Documentation/rust/index.rst
> @@ -0,0 +1,20 @@
> +Rust
> +====
> +
> +Documentation related to Rust within the kernel. If you are starting out,
> +read the :ref:`Documentation/rust/quick-start.rst <rust_quick_start>` guide.
> +
> +.. toctree::
> + :maxdepth: 1
> +
> + quick-start
> + coding
> + docs
> + arch-support
> +
> +.. only:: subproject and html
> +
> + Indices
> + =======
> +
> + * :ref:`genindex`
> diff --git a/Documentation/rust/quick-start.rst b/Documentation/rust/quick-start.rst
> new file mode 100644
> index 000000000000..42367e4365c3
> --- /dev/null
> +++ b/Documentation/rust/quick-start.rst
> @@ -0,0 +1,203 @@
> +.. _rust_quick_start:
> +
> +Quick Start
> +===========
> +
> +This document describes how to get started with kernel development in Rust.
> +If you have worked previously with Rust, this will only take a moment.
> +
> +Please note that, at the moment, a very restricted subset of architectures
> +is supported, see :doc:`/rust/arch-support`.
> +
> +
> +Requirements: Building
> +----------------------
> +
> +This section explains how to fetch the tools needed for building.
> +
> +Some of these requirements might be available from your Linux distribution
> +under names like ``rustc``, ``rust-src``, ``rust-bindgen``, etc. However,
> +at the time of writing, they are likely to not be recent enough.
> +
> +
> +rustc
> +*****
> +
> +A recent *nightly* Rust toolchain (with, at least, ``rustc``) is required,
> +e.g. ``nightly-2021-02-20``. Our goal is to use a stable toolchain as soon
> +as possible, but for the moment we depend on a handful of nightly features.
> +
> +If you are using ``rustup``, run::
> +
> + rustup default nightly-2021-02-20
> +
> +Please avoid the very latest nightlies (>= nightly-2021-03-05) until
> +https://github.com/Rust-for-Linux/linux/issues/135 is resolved.
That link has a comment that this was fixed. Is the comment now stale?
> +
> +Otherwise, fetch a standalone installer or install ``rustup`` from:
> +
> + https://www.rust-lang.org
> +
> +
> +Rust standard library source
> +****************************
> +
> +The Rust standard library source is required because the build system will
> +cross-compile ``core`` and ``alloc``.
> +
> +If you are using ``rustup``, run::
> +
> + rustup component add rust-src
> +
> +Otherwise, if you used a standalone installer, you can clone the Rust
> +repository into the installation folder of your nightly toolchain::
> +
> + git clone --recurse-submodules https://github.com/rust-lang/rust $(rustc --print sysroot)/lib/rustlib/src/rust
> +
> +
> +libclang
> +********
> +
> +``libclang`` (part of LLVM) is used by ``bindgen`` to understand the C code
> +in the kernel, which means you will need a recent LLVM installed; like when
> +you compile the kernel with ``CC=clang`` or ``LLVM=1``.
> +
> +Your Linux distribution is likely to have a suitable one available, so it is
> +best if you check that first.
> +
> +There are also some binaries for several systems and architectures uploaded at:
> +
> + https://releases.llvm.org/download.html
> +
> +For Debian-based distributions, you can also fetch them from:
> +
> + https://apt.llvm.org
> +
> +Otherwise, building LLVM takes quite a while, but it is not a complex process:
> +
> + https://llvm.org/docs/GettingStarted.html#getting-the-source-code-and-building-llvm
Perhaps worth another cross reference to :ref:`kbuild_llvm`?
https://www.kernel.org/doc/html/latest/kbuild/llvm.html#getting-llvm
Perhaps amend that then link to it from here?
> +
> +
> +bindgen
> +*******
> +
> +The bindings to the C side of the kernel are generated at build time using
> +the ``bindgen`` tool. A recent version should work, e.g. ``0.56.0``.
Avoid terms like recent and modern. Otherwise in a few years 0.56.0
will be archaic, not recent, and you'll need to update your docs. So
bindgen does not distribute libclang?
> +
> +Install it via (this will build the tool from source)::
> +
> + cargo install --locked --version 0.56.0 bindgen
> +
> +
> +Requirements: Developing
> +------------------------
> +
> +This section explains how to fetch the tools needed for developing. That is,
> +if you only want to build the kernel, you do not need them.
> +
> +
> +rustfmt
> +*******
> +
> +The ``rustfmt`` tool is used to automatically format all the Rust kernel code,
> +including the generated C bindings (for details, please see
> +:ref:`Documentation/rust/coding.rst <rust_coding>`).
> +
> +If you are using ``rustup``, its ``default`` profile already installs the tool,
> +so you should be good to go. If you are using another profile, you can install
> +the component manually::
> +
> + rustup component add rustfmt
> +
> +The standalone installers also come with ``rustfmt``.
> +
> +
> +clippy
> +******
> +
> +``clippy`` is a Rust linter. Installing it allows you to get extra warnings
> +for Rust code passing ``CLIPPY=1`` to ``make`` (for details, please see
> +:ref:`Documentation/rust/coding.rst <rust_coding>`).
> +
> +If you are using ``rustup``, its ``default`` profile already installs the tool,
> +so you should be good to go. If you are using another profile, you can install
> +the component manually::
> +
> + rustup component add clippy
> +
> +The standalone installers also come with ``clippy``.
> +
> +
> +rustdoc
> +*******
> +
> +If you install the ``rustdoc`` tool, then you will be able to generate pretty
> +HTML documentation for Rust code, including for the libraries (crates) inside
> +``rust/`` that are used by the rest of the kernel (for details, please see
> +:ref:`Documentation/rust/docs.rst <rust_docs>`).
> +
> +If you are using ``rustup``, its ``default`` profile already installs the tool,
> +so you should be good to go. If you are using another profile, you can install
> +the component manually::
> +
> + rustup component add rustdoc
> +
> +The standalone installers also come with ``rustdoc``.
> +
> +
> +Configuration
> +-------------
> +
> +``Rust support`` (``CONFIG_RUST``) needs to be enabled in the ``General setup``
> +menu. The option is only shown if the build system can locate ``rustc``.
> +In turn, this will make visible the rest of options that depend on Rust.
> +
> +Afterwards, go to::
> +
> + Kernel hacking
> + -> Sample kernel code
> + -> Rust samples
> +
> +And enable some sample modules either as built-in or as loadable.
> +
> +
> +Building
> +--------
> +
> +Building a kernel with Clang or a complete LLVM toolchain is the best supported
> +setup at the moment. That is::
> +
> + make ARCH=... CROSS_COMPILE=... CC=clang -j...
> +
> +or::
> +
> + make ARCH=... CROSS_COMPILE=... LLVM=1 -j...
Please reorder; prefer LLVM=1 to CC=clang. Probably worth another
cross reference to :ref:`kbuild_llvm`.
> +
> +Using GCC also works for some configurations, but it is *very* experimental at
> +the moment.
> +
> +
> +Hacking
> +-------
> +
> +If you want to dive deeper, take a look at the source code of the samples
> +at ``samples/rust/``, the Rust support code under ``rust/`` and
> +the ``Rust hacking`` menu under ``Kernel hacking``.
> +
> +If you use GDB/Binutils and Rust symbols aren't getting demangled, the reason
> +is your toolchain doesn't support Rust's new v0 mangling scheme yet. There are
"new" as in changed, or "new" as in Rust previously did not mangle symbols?
> +a few ways out:
> +
> + - If you don't mind building your own tools, we provide the following fork
> + with the support cherry-picked from GCC on top of very recent releases:
> +
> + https://github.com/Rust-for-Linux/binutils-gdb/releases/tag/gdb-10.1-release-rust
> + https://github.com/Rust-for-Linux/binutils-gdb/releases/tag/binutils-2_35_1-rust
> +
> + - If you only need GDB and can enable ``CONFIG_DEBUG_INFO``, do so:
> + some versions of GDB (e.g. vanilla GDB 10.1) are able to use
> + the pre-demangled names embedded in the debug info.
> +
> + - If you don't need loadable module support, you may compile without
> + the ``-Zsymbol-mangling-version=v0`` flag. However, we don't maintain
> + support for that, so avoid it unless you are in a hurry.
> --
> 2.17.1
>
--
Thanks,
~Nick Desaulniers
On Wed, Apr 14, 2021 at 11:50 AM <[email protected]> wrote:
>
> From: Miguel Ojeda <[email protected]>
>
> Miguel, Alex and Wedson will be maintaining the Rust support.
>
> Co-developed-by: Alex Gaynor <[email protected]>
> Signed-off-by: Alex Gaynor <[email protected]>
> Co-developed-by: Wedson Almeida Filho <[email protected]>
> Signed-off-by: Wedson Almeida Filho <[email protected]>
> Signed-off-by: Miguel Ojeda <[email protected]>
> ---
> MAINTAINERS | 14 ++++++++++++++
> 1 file changed, 14 insertions(+)
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 9e876927c60d..de32aaa5cabd 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -15547,6 +15547,20 @@ L: [email protected]
...
> +F: rust/
> +F: samples/rust/
> +F: Documentation/rust/
Probably some other files, too? Like the target.json files under
arch/{arch}/rust/ ?
--
Thanks,
~Nick Desaulniers
On Wed, Apr 14, 2021 at 11:55 PM Nick Desaulniers
<[email protected]> wrote:
>
> Assuming this will at least be part of Wedson's core responsibilities,
> shouldn't this be "Supported." Per Maintainers:
Yeah, we were waiting until a couple other announcements to change it
-- but it will most likely be "Supported" by Wedson and myself :)
Cheers,
Miguel
On Wed, Apr 14, 2021 at 11:48 AM <[email protected]> wrote:
>
> From: Miguel Ojeda <[email protected]>
>
> Rust symbols can become quite long due to namespacing introduced
> by modules, types, traits, generics, etc. For instance, for:
>
> pub mod my_module {
> pub struct MyType;
> pub struct MyGenericType<T>(T);
>
> pub trait MyTrait {
> fn my_method() -> u32;
> }
>
> impl MyTrait for MyGenericType<MyType> {
> fn my_method() -> u32 {
> 42
> }
> }
> }
>
> generates a symbol of length 96 when using the upcoming v0 mangling scheme:
>
> _RNvXNtCshGpAVYOtgW1_7example9my_moduleINtB2_13MyGenericTypeNtB2_6MyTypeENtB2_7MyTrait9my_method
>
> At the moment, Rust symbols may reach up to 300 in length.
> Setting 512 as the maximum seems like a reasonable choice to
> keep some headroom.
What are the implications of this change for someone not using Rust?
Does it change the binary size of vmlinux for a defconfig build, for
example?
>
> Co-developed-by: Alex Gaynor <[email protected]>
> Signed-off-by: Alex Gaynor <[email protected]>
> Co-developed-by: Geoffrey Thomas <[email protected]>
> Signed-off-by: Geoffrey Thomas <[email protected]>
> Co-developed-by: Finn Behrens <[email protected]>
> Signed-off-by: Finn Behrens <[email protected]>
> Co-developed-by: Adam Bratschi-Kaye <[email protected]>
> Signed-off-by: Adam Bratschi-Kaye <[email protected]>
> Co-developed-by: Wedson Almeida Filho <[email protected]>
> Signed-off-by: Wedson Almeida Filho <[email protected]>
> Signed-off-by: Miguel Ojeda <[email protected]>
> ---
> include/linux/kallsyms.h | 2 +-
> kernel/livepatch/core.c | 4 ++--
> scripts/kallsyms.c | 2 +-
> 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 +-
> 7 files changed, 8 insertions(+), 8 deletions(-)
>
> diff --git a/include/linux/kallsyms.h b/include/linux/kallsyms.h
> index 465060acc981..5cdc6903abca 100644
> --- a/include/linux/kallsyms.h
> +++ b/include/linux/kallsyms.h
> @@ -14,7 +14,7 @@
>
> #include <asm/sections.h>
>
> -#define KSYM_NAME_LEN 128
> +#define KSYM_NAME_LEN 512
> #define KSYM_SYMBOL_LEN (sizeof("%s+%#lx/%#lx [%s]") + (KSYM_NAME_LEN - 1) + \
> 2*(BITS_PER_LONG*3/10) + (MODULE_NAME_LEN - 1) + 1)
>
> diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c
> index 335d988bd811..73874e5edfda 100644
> --- a/kernel/livepatch/core.c
> +++ b/kernel/livepatch/core.c
> @@ -213,7 +213,7 @@ static int klp_resolve_symbols(Elf64_Shdr *sechdrs, const char *strtab,
> * we use the smallest/strictest upper bound possible (56, based on
> * the current definition of MODULE_NAME_LEN) to prevent overflows.
> */
> - BUILD_BUG_ON(MODULE_NAME_LEN < 56 || KSYM_NAME_LEN != 128);
> + BUILD_BUG_ON(MODULE_NAME_LEN < 56 || KSYM_NAME_LEN != 512);
>
> relas = (Elf_Rela *) relasec->sh_addr;
> /* For each rela in this klp relocation section */
> @@ -227,7 +227,7 @@ static int klp_resolve_symbols(Elf64_Shdr *sechdrs, const char *strtab,
>
> /* Format: .klp.sym.sym_objname.sym_name,sympos */
> cnt = sscanf(strtab + sym->st_name,
> - ".klp.sym.%55[^.].%127[^,],%lu",
> + ".klp.sym.%55[^.].%511[^,],%lu",
> sym_objname, sym_name, &sympos);
> if (cnt != 3) {
> pr_err("symbol %s has an incorrectly formatted name\n",
> diff --git a/scripts/kallsyms.c b/scripts/kallsyms.c
> index bcdabee13aab..9bab5f55ade3 100644
> --- a/scripts/kallsyms.c
> +++ b/scripts/kallsyms.c
> @@ -27,7 +27,7 @@
>
> #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))
>
> -#define KSYM_NAME_LEN 128
> +#define KSYM_NAME_LEN 512
>
> struct sym_entry {
> unsigned long long addr;
> diff --git a/tools/include/linux/kallsyms.h b/tools/include/linux/kallsyms.h
> index efb6c3f5f2a9..5a37ccbec54f 100644
> --- a/tools/include/linux/kallsyms.h
> +++ b/tools/include/linux/kallsyms.h
> @@ -6,7 +6,7 @@
> #include <stdio.h>
> #include <unistd.h>
>
> -#define KSYM_NAME_LEN 128
> +#define KSYM_NAME_LEN 512
>
> struct module;
>
> diff --git a/tools/include/linux/lockdep.h b/tools/include/linux/lockdep.h
> index e56997288f2b..d9c163f3ab24 100644
> --- a/tools/include/linux/lockdep.h
> +++ b/tools/include/linux/lockdep.h
> @@ -47,7 +47,7 @@ static inline int debug_locks_off(void)
>
> #define task_pid_nr(tsk) ((tsk)->pid)
>
> -#define KSYM_NAME_LEN 128
> +#define KSYM_NAME_LEN 512
> #define printk(...) dprintf(STDOUT_FILENO, __VA_ARGS__)
> #define pr_err(format, ...) fprintf (stderr, format, ## __VA_ARGS__)
> #define pr_warn pr_err
> diff --git a/tools/lib/perf/include/perf/event.h b/tools/lib/perf/include/perf/event.h
> index d82054225fcc..f5c40325b441 100644
> --- a/tools/lib/perf/include/perf/event.h
> +++ b/tools/lib/perf/include/perf/event.h
> @@ -93,7 +93,7 @@ struct perf_record_throttle {
> };
>
> #ifndef KSYM_NAME_LEN
> -#define KSYM_NAME_LEN 256
> +#define KSYM_NAME_LEN 512
> #endif
>
> struct perf_record_ksymbol {
> diff --git a/tools/lib/symbol/kallsyms.h b/tools/lib/symbol/kallsyms.h
> index 72ab9870454b..542f9b059c3b 100644
> --- a/tools/lib/symbol/kallsyms.h
> +++ b/tools/lib/symbol/kallsyms.h
> @@ -7,7 +7,7 @@
> #include <linux/types.h>
>
> #ifndef KSYM_NAME_LEN
> -#define KSYM_NAME_LEN 256
> +#define KSYM_NAME_LEN 512
> #endif
>
> static inline u8 kallsyms2elf_binding(char type)
> --
> 2.17.1
>
--
Thanks,
~Nick Desaulniers
Hi Nick,
Thanks a lot for looking into this!
On Thu, Apr 15, 2021 at 12:18 AM Nick Desaulniers
<[email protected]> wrote:
>
> Was this TODO meant to be removed, or is it still pending? If pending,
> on what? Being able to link back on itself if/once merged?
Still pending -- the plan is to upload the docs to kernel.org rather
than have them on GitHub, and ideally have one version done from the
CI automatically on e.g. every merge, but that requires some sorting
out.
But yeah, I could have put here the link to the temporary docs in
GitHub for the moment, good catch! +1
> Consider if the docs need any change here based on behavior related to
> Panics based on feedback thus far in the thread.
Indeed, it should be a very rare occurrence. I will add a sentence
saying that one needs to be really sure there is no other way out than
panicking.
I have to write a few more bits regarding some new guidelines we follow/enforce.
> That link has a comment that this was fixed. Is the comment now stale?
There were a couple things to resolve regarding LLVM 12 last time I
looked into it, so I didn't update it (also, it is safer in general to
use the recommended nightly -- one never knows when things may break,
plus some nightlies do not include all the tools like `rustfmt`).
So my current plan is to recommend a given nightly, and update it only
when needed, in tandem with the CI builds (we also test
different/newer nightlies in the CI when possible, e.g. when the LLVM
12 bits are resolved, then we will also test one with LLVM 12).
> Perhaps worth another cross reference to :ref:`kbuild_llvm`?
> https://www.kernel.org/doc/html/latest/kbuild/llvm.html#getting-llvm
> Perhaps amend that then link to it from here?
+1
> Avoid terms like recent and modern. Otherwise in a few years 0.56.0
> will be archaic, not recent, and you'll need to update your docs. So
+1
> bindgen does not distribute libclang?
AFAIK, no, `bindgen` expects you to provide/install `libclang` (and
you have some knobs to control how it searches for it). But it looks
like they also allow to link it statically.
I was looking into this a while ago to upload a static version to
kernel.org somewhere (actually, a full Clang/LLVM and the rest of
tools for Rust support), so that kernel developers had an easier time
setting things up. Konstantin Ryabitsev told me it would be possible,
but we need to sort out a few details.
> Please reorder; prefer LLVM=1 to CC=clang. Probably worth another
> cross reference to :ref:`kbuild_llvm`.
Will do!
> "new" as in changed, or "new" as in Rust previously did not mangle symbols?
Changed -- currently (in stable) they use what they call the `legacy`
mangling scheme, which is very similar to C++'s (and used the same
prefix, `_Z`).
Cheers,
Miguel
On Wed, Apr 14, 2021 at 11:48 AM <[email protected]> wrote:
>
> From: Miguel Ojeda <[email protected]>
>
> To support Rust under GCC-built kernels, we need to save the flags that
> would have been passed if the kernel was being compiled with Clang.
>
> The reason is that bindgen -- the tool we use to generate Rust bindings
> to the C side of the kernel -- relies on libclang to parse C. Ideally:
>
> - bindgen would support a GCC backend (requested at [1]),
>
> - or the Clang driver would be perfectly compatible with GCC,
> including plugins. Unlikely, of course, but perhaps a big
> subset of configs may be possible to guarantee to be kept
> compatible nevertheless.
>
> This is also the reason why GCC builds are very experimental and some
> configurations may not work (e.g. GCC_PLUGIN_RANDSTRUCT). However,
> we keep GCC builds working (for some example configs) in the CI
> to avoid diverging/regressing further, so that we are better prepared
> for the future when a solution might become available.
>
> [1] https://github.com/rust-lang/rust-bindgen/issues/1949
>
> Link: https://github.com/Rust-for-Linux/linux/issues/167
>
> Co-developed-by: Alex Gaynor <[email protected]>
> Signed-off-by: Alex Gaynor <[email protected]>
> Co-developed-by: Geoffrey Thomas <[email protected]>
> Signed-off-by: Geoffrey Thomas <[email protected]>
> Co-developed-by: Finn Behrens <[email protected]>
> Signed-off-by: Finn Behrens <[email protected]>
> Co-developed-by: Adam Bratschi-Kaye <[email protected]>
> Signed-off-by: Adam Bratschi-Kaye <[email protected]>
> Co-developed-by: Wedson Almeida Filho <[email protected]>
> Signed-off-by: Wedson Almeida Filho <[email protected]>
> Signed-off-by: Miguel Ojeda <[email protected]>
> ---
> Makefile | 27 ++++++++++++++++-----------
> 1 file changed, 16 insertions(+), 11 deletions(-)
>
> diff --git a/Makefile b/Makefile
> index d4784d181123..9c75354324ed 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -559,26 +559,31 @@ ifdef building_out_of_srctree
> { echo "# this is build directory, ignore it"; echo "*"; } > .gitignore
> endif
>
> -# The expansion should be delayed until arch/$(SRCARCH)/Makefile is included.
> -# Some architectures define CROSS_COMPILE in arch/$(SRCARCH)/Makefile.
> -# CC_VERSION_TEXT is referenced from Kconfig (so it needs export),
> -# and from include/config/auto.conf.cmd to detect the compiler upgrade.
> -CC_VERSION_TEXT = $(shell $(CC) --version 2>/dev/null | head -n 1 | sed 's/\#//g')
> +TENTATIVE_CLANG_FLAGS := -Werror=unknown-warning-option
>
> -ifneq ($(findstring clang,$(CC_VERSION_TEXT)),)
> ifneq ($(CROSS_COMPILE),)
> -CLANG_FLAGS += --target=$(notdir $(CROSS_COMPILE:%-=%))
> +TENTATIVE_CLANG_FLAGS += --target=$(notdir $(CROSS_COMPILE:%-=%))
> GCC_TOOLCHAIN_DIR := $(dir $(shell which $(CROSS_COMPILE)elfedit))
> -CLANG_FLAGS += --prefix=$(GCC_TOOLCHAIN_DIR)$(notdir $(CROSS_COMPILE))
> +TENTATIVE_CLANG_FLAGS += --prefix=$(GCC_TOOLCHAIN_DIR)$(notdir $(CROSS_COMPILE))
> GCC_TOOLCHAIN := $(realpath $(GCC_TOOLCHAIN_DIR)/..)
> endif
> ifneq ($(GCC_TOOLCHAIN),)
> -CLANG_FLAGS += --gcc-toolchain=$(GCC_TOOLCHAIN)
> +TENTATIVE_CLANG_FLAGS += --gcc-toolchain=$(GCC_TOOLCHAIN)
> endif
> ifneq ($(LLVM_IAS),1)
> -CLANG_FLAGS += -no-integrated-as
> +TENTATIVE_CLANG_FLAGS += -no-integrated-as
> endif
> -CLANG_FLAGS += -Werror=unknown-warning-option
> +
> +export TENTATIVE_CLANG_FLAGS
I'm ok with this approach, but I'm curious:
If the user made a copy of the CLANG_FLAGS variable and modified its
copy, would TENTATIVE_CLANG_FLAGS even be necessary? IIUC,
TENTATIVE_CLANG_FLAGS is used to filter out certain flags before
passing them to bindgen?
Or, I'm curious whether you even need to rename this variable (or
create a new variable) at all? Might make for a shorter diff if you
just keep the existing identifier (CLANG_FLAGS), but create them
unconditionally (at least not conditional on CC=clang).
> +
> +# The expansion should be delayed until arch/$(SRCARCH)/Makefile is included.
> +# Some architectures define CROSS_COMPILE in arch/$(SRCARCH)/Makefile.
> +# CC_VERSION_TEXT is referenced from Kconfig (so it needs export),
> +# and from include/config/auto.conf.cmd to detect the compiler upgrade.
> +CC_VERSION_TEXT = $(shell $(CC) --version 2>/dev/null | head -n 1 | sed 's/\#//g')
> +
> +ifneq ($(findstring clang,$(CC_VERSION_TEXT)),)
> +CLANG_FLAGS += $(TENTATIVE_CLANG_FLAGS)
> KBUILD_CFLAGS += $(CLANG_FLAGS)
> KBUILD_AFLAGS += $(CLANG_FLAGS)
> export CLANG_FLAGS
> --
> 2.17.1
>
--
Thanks,
~Nick Desaulniers
On Thu, Apr 15, 2021 at 1:47 AM Nick Desaulniers
<[email protected]> wrote:
>
> I'm ok with this approach, but I'm curious:
> If the user made a copy of the CLANG_FLAGS variable and modified its
> copy, would TENTATIVE_CLANG_FLAGS even be necessary? IIUC,
> TENTATIVE_CLANG_FLAGS is used to filter out certain flags before
> passing them to bindgen?
>
> Or, I'm curious whether you even need to rename this variable (or
> create a new variable) at all? Might make for a shorter diff if you
> just keep the existing identifier (CLANG_FLAGS), but create them
> unconditionally (at least not conditional on CC=clang).
This is only for the GCC builds -- and yeah, it is a hack. I still
need to think a bit more how to do this better; although ultimately
the proper solution is to have `bindgen` use GCC like it does with
`libclang` and avoid this altogether. That way we can ensure the
bindings are correct, support plugins, etc.
Cheers,
Miguel
On Wed, Apr 14, 2021 at 11:48 AM <[email protected]> wrote:
>
> From: Miguel Ojeda <[email protected]>
>
> This commit includes also the `Kconfig` entries related to Rust,
> the Rust configuration printer, the target definition files,
> the version detection script and a few other bits.
>
> In the future, we will likely want to generate the target files
> on the fly via a script.
>
> With this in place, we will be adding the rest of the bits piece
> by piece and enabling their builds.
>
> Co-developed-by: Alex Gaynor <[email protected]>
> Signed-off-by: Alex Gaynor <[email protected]>
> Co-developed-by: Geoffrey Thomas <[email protected]>
> Signed-off-by: Geoffrey Thomas <[email protected]>
> Co-developed-by: Finn Behrens <[email protected]>
> Signed-off-by: Finn Behrens <[email protected]>
> Co-developed-by: Adam Bratschi-Kaye <[email protected]>
> Signed-off-by: Adam Bratschi-Kaye <[email protected]>
> Co-developed-by: Wedson Almeida Filho <[email protected]>
> Signed-off-by: Wedson Almeida Filho <[email protected]>
> Co-developed-by: Michael Ellerman <[email protected]>
> Signed-off-by: Michael Ellerman <[email protected]>
> Signed-off-by: Miguel Ojeda <[email protected]>
> ---
> .gitignore | 2 +
> .rustfmt.toml | 12 +++
> Documentation/kbuild/kbuild.rst | 4 +
> Documentation/process/changes.rst | 9 ++
> Makefile | 120 +++++++++++++++++++++++--
> arch/arm64/rust/target.json | 40 +++++++++
> arch/powerpc/rust/target.json | 30 +++++++
> arch/x86/rust/target.json | 42 +++++++++
> init/Kconfig | 27 ++++++
> lib/Kconfig.debug | 100 +++++++++++++++++++++
> rust/.gitignore | 5 ++
> rust/Makefile | 141 ++++++++++++++++++++++++++++++
> scripts/Makefile.build | 19 ++++
> scripts/Makefile.lib | 12 +++
> scripts/kconfig/confdata.c | 67 +++++++++++++-
> scripts/rust-version.sh | 31 +++++++
> 16 files changed, 654 insertions(+), 7 deletions(-)
> create mode 100644 .rustfmt.toml
> 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 rust/.gitignore
> create mode 100644 rust/Makefile
> create mode 100755 scripts/rust-version.sh
>
> diff --git a/.gitignore b/.gitignore
> index 3af66272d6f1..6ba4f516f46c 100644
> --- a/.gitignore
> +++ b/.gitignore
> @@ -37,6 +37,7 @@
> *.o
> *.o.*
> *.patch
> +*.rmeta
> *.s
> *.so
> *.so.dbg
> @@ -96,6 +97,7 @@ modules.order
> !.gitattributes
> !.gitignore
> !.mailmap
> +!.rustfmt.toml
>
> #
> # Generated include files
> diff --git a/.rustfmt.toml b/.rustfmt.toml
> new file mode 100644
> index 000000000000..5893c0e3cbde
> --- /dev/null
> +++ b/.rustfmt.toml
> @@ -0,0 +1,12 @@
> +edition = "2018"
> +format_code_in_doc_comments = true
> +newline_style = "Unix"
> +
> +# Unstable options that help catching some mistakes in formatting and that we may want to enable
> +# when they become stable.
> +#
> +# They are kept here since they are useful to run from time to time.
> +#reorder_impl_items = true
> +#comment_width = 100
> +#wrap_comments = true
> +#normalize_comments = true
> diff --git a/Documentation/kbuild/kbuild.rst b/Documentation/kbuild/kbuild.rst
> index 2d1fc03d346e..1109d18d9377 100644
> --- a/Documentation/kbuild/kbuild.rst
> +++ b/Documentation/kbuild/kbuild.rst
> @@ -57,6 +57,10 @@ CFLAGS_MODULE
> -------------
> Additional module specific options to use for $(CC).
>
> +KRUSTCFLAGS
> +-----------
> +Additional options to the Rust compiler (for built-in and modules).
> +
> LDFLAGS_MODULE
> --------------
> Additional options used for $(LD) when linking modules.
> diff --git a/Documentation/process/changes.rst b/Documentation/process/changes.rst
> index dac17711dc11..4b6ba5458706 100644
> --- a/Documentation/process/changes.rst
> +++ b/Documentation/process/changes.rst
> @@ -31,6 +31,8 @@ you probably needn't concern yourself with pcmciautils.
> ====================== =============== ========================================
> GNU C 4.9 gcc --version
> Clang/LLVM (optional) 10.0.1 clang --version
> +rustc (optional) nightly rustc --version
> +bindgen (optional) 0.56.0 bindgen --version
> GNU make 3.81 make --version
> binutils 2.23 ld -v
> flex 2.5.35 flex --version
> @@ -56,6 +58,7 @@ iptables 1.4.2 iptables -V
> openssl & libcrypto 1.0.0 openssl version
> bc 1.06.95 bc --version
> Sphinx\ [#f1]_ 1.3 sphinx-build --version
> +rustdoc (optional) nightly rustdoc --version
> ====================== =============== ========================================
>
> .. [#f1] Sphinx is needed only to build the Kernel documentation
> @@ -330,6 +333,12 @@ Sphinx
> Please see :ref:`sphinx_install` in :ref:`Documentation/doc-guide/sphinx.rst <sphinxdoc>`
> for details about Sphinx requirements.
>
> +rustdoc
> +-------
> +
> +``rustdoc`` is used to generate Rust documentation. Please see
> +:ref:`Documentation/rust/docs.rst <rust_docs>` for more information.
> +
> Getting updated software
> ========================
>
> diff --git a/Makefile b/Makefile
> index 9c75354324ed..62b3bba38635 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -120,6 +120,13 @@ endif
>
> export KBUILD_CHECKSRC
>
> +# Enable "clippy" (a linter) as part of the Rust compilation.
> +#
> +# Use 'make CLIPPY=1' to enable it.
> +ifeq ("$(origin CLIPPY)", "command line")
> + KBUILD_CLIPPY := $(CLIPPY)
> +endif
Rather than check the origin (yikes, are we intentionally avoiding env
vars?), can this simply be
ifneq ($(CLIPPY),)
KBUILD_CLIPPY := $(CLIPPY)
endif
Then you can specify whatever value you want, support command line or
env vars, etc.?
> +
> # Use make M=dir or set the environment variable KBUILD_EXTMOD to specify the
> # directory of external module to build. Setting M= takes precedence.
> ifeq ("$(origin M)", "command line")
> @@ -263,7 +270,7 @@ no-dot-config-targets := $(clean-targets) \
> cscope gtags TAGS tags help% %docs check% coccicheck \
> $(version_h) headers headers_% archheaders archscripts \
> %asm-generic kernelversion %src-pkg dt_binding_check \
> - outputmakefile
> + outputmakefile rustfmt rustfmtcheck
> no-sync-config-targets := $(no-dot-config-targets) %install kernelrelease \
> image_name
> single-targets := %.a %.i %.ko %.lds %.ll %.lst %.mod %.o %.s %.symtypes %/
> @@ -444,6 +451,10 @@ OBJDUMP = $(CROSS_COMPILE)objdump
> READELF = $(CROSS_COMPILE)readelf
> STRIP = $(CROSS_COMPILE)strip
> endif
> +RUSTC = rustc
> +RUSTFMT = rustfmt
> +CLIPPY_DRIVER = clippy-driver
> +BINDGEN = bindgen
> PAHOLE = pahole
> RESOLVE_BTFIDS = $(objtree)/tools/bpf/resolve_btfids/resolve_btfids
> LEX = flex
> @@ -467,9 +478,11 @@ CHECKFLAGS := -D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ \
> -Wbitwise -Wno-return-void -Wno-unknown-attribute $(CF)
> NOSTDINC_FLAGS :=
> CFLAGS_MODULE =
> +RUSTCFLAGS_MODULE =
> AFLAGS_MODULE =
> LDFLAGS_MODULE =
> CFLAGS_KERNEL =
> +RUSTCFLAGS_KERNEL =
> AFLAGS_KERNEL =
> LDFLAGS_vmlinux =
>
> @@ -498,15 +511,30 @@ KBUILD_CFLAGS := -Wall -Wundef -Werror=strict-prototypes -Wno-trigraphs \
> -Werror=return-type -Wno-format-security \
> -std=gnu89
> KBUILD_CPPFLAGS := -D__KERNEL__
> +KBUILD_RUSTCFLAGS := --emit=dep-info,obj,metadata --edition=2018 \
> + -Cpanic=abort -Cembed-bitcode=n -Clto=n -Crpath=n \
> + -Cforce-unwind-tables=n -Ccodegen-units=1 \
> + -Zbinary_dep_depinfo=y -Zsymbol-mangling-version=v0
> KBUILD_AFLAGS_KERNEL :=
> KBUILD_CFLAGS_KERNEL :=
> +KBUILD_RUSTCFLAGS_KERNEL :=
> KBUILD_AFLAGS_MODULE := -DMODULE
> KBUILD_CFLAGS_MODULE := -DMODULE
> +KBUILD_RUSTCFLAGS_MODULE := --cfg MODULE
> KBUILD_LDFLAGS_MODULE :=
> KBUILD_LDFLAGS :=
> CLANG_FLAGS :=
>
> -export ARCH SRCARCH CONFIG_SHELL BASH HOSTCC KBUILD_HOSTCFLAGS CROSS_COMPILE LD CC
> +ifeq ($(KBUILD_CLIPPY),1)
> + RUSTC_OR_CLIPPY_QUIET := CLIPPY
> + RUSTC_OR_CLIPPY = $(CLIPPY_DRIVER)
> +else
> + RUSTC_OR_CLIPPY_QUIET := RUSTC
> + RUSTC_OR_CLIPPY = $(RUSTC)
> +endif
> +export RUSTC_OR_CLIPPY_QUIET RUSTC_OR_CLIPPY
> +
> +export ARCH SRCARCH CONFIG_SHELL BASH HOSTCC KBUILD_HOSTCFLAGS CROSS_COMPILE LD CC RUSTC BINDGEN
> export CPP AR NM STRIP OBJCOPY OBJDUMP READELF PAHOLE RESOLVE_BTFIDS LEX YACC AWK INSTALLKERNEL
> export PERL PYTHON3 CHECK CHECKFLAGS MAKE UTS_MACHINE HOSTCXX
> export KGZIP KBZIP2 KLZOP LZMA LZ4 XZ ZSTD
> @@ -514,9 +542,10 @@ export KBUILD_HOSTCXXFLAGS KBUILD_HOSTLDFLAGS KBUILD_HOSTLDLIBS LDFLAGS_MODULE
>
> export KBUILD_CPPFLAGS NOSTDINC_FLAGS LINUXINCLUDE OBJCOPYFLAGS KBUILD_LDFLAGS
> export KBUILD_CFLAGS CFLAGS_KERNEL CFLAGS_MODULE
> +export KBUILD_RUSTCFLAGS RUSTCFLAGS_KERNEL RUSTCFLAGS_MODULE
> export KBUILD_AFLAGS AFLAGS_KERNEL AFLAGS_MODULE
> -export KBUILD_AFLAGS_MODULE KBUILD_CFLAGS_MODULE KBUILD_LDFLAGS_MODULE
> -export KBUILD_AFLAGS_KERNEL KBUILD_CFLAGS_KERNEL
> +export KBUILD_AFLAGS_MODULE KBUILD_CFLAGS_MODULE KBUILD_RUSTCFLAGS_MODULE KBUILD_LDFLAGS_MODULE
> +export KBUILD_AFLAGS_KERNEL KBUILD_CFLAGS_KERNEL KBUILD_RUSTCFLAGS_KERNEL
>
> # Files to ignore in find ... statements
>
> @@ -712,7 +741,7 @@ $(KCONFIG_CONFIG):
> quiet_cmd_syncconfig = SYNC $@
> cmd_syncconfig = $(MAKE) -f $(srctree)/Makefile syncconfig
>
> -%/config/auto.conf %/config/auto.conf.cmd %/generated/autoconf.h: $(KCONFIG_CONFIG)
> +%/config/auto.conf %/config/auto.conf.cmd %/generated/autoconf.h %/generated/rustc_cfg: $(KCONFIG_CONFIG)
> +$(call cmd,syncconfig)
> else # !may-sync-config
> # External modules and some install targets need include/generated/autoconf.h
> @@ -738,12 +767,43 @@ KBUILD_CFLAGS += $(call cc-disable-warning, format-truncation)
> KBUILD_CFLAGS += $(call cc-disable-warning, format-overflow)
> KBUILD_CFLAGS += $(call cc-disable-warning, address-of-packed-member)
>
> +ifdef CONFIG_RUST_DEBUG_ASSERTIONS
> +KBUILD_RUSTCFLAGS += -Cdebug-assertions=y
> +else
> +KBUILD_RUSTCFLAGS += -Cdebug-assertions=n
> +endif
> +
> +ifdef CONFIG_RUST_OVERFLOW_CHECKS
> +KBUILD_RUSTCFLAGS += -Coverflow-checks=y
> +else
> +KBUILD_RUSTCFLAGS += -Coverflow-checks=n
> +endif
> +
> ifdef CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE
> KBUILD_CFLAGS += -O2
> +KBUILD_RUSTCFLAGS_OPT_LEVEL_MAP := 2
> else ifdef CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE_O3
> KBUILD_CFLAGS += -O3
> +KBUILD_RUSTCFLAGS_OPT_LEVEL_MAP := 3
> else ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE
> KBUILD_CFLAGS += -Os
> +KBUILD_RUSTCFLAGS_OPT_LEVEL_MAP := z
-Oz in clang typically generates larger kernel code than -Os; LLVM
seems to aggressively emit libcalls even when the setup for a call
would be larger than the inlined call itself. Is z smaller than s for
the existing rust examples?
> +endif
> +
> +ifdef CONFIG_RUST_OPT_LEVEL_SIMILAR_AS_CHOSEN_FOR_C
> +KBUILD_RUSTCFLAGS += -Copt-level=$(KBUILD_RUSTCFLAGS_OPT_LEVEL_MAP)
> +else ifdef CONFIG_RUST_OPT_LEVEL_0
> +KBUILD_RUSTCFLAGS += -Copt-level=0
> +else ifdef CONFIG_RUST_OPT_LEVEL_1
> +KBUILD_RUSTCFLAGS += -Copt-level=1
> +else ifdef CONFIG_RUST_OPT_LEVEL_2
> +KBUILD_RUSTCFLAGS += -Copt-level=2
> +else ifdef CONFIG_RUST_OPT_LEVEL_3
> +KBUILD_RUSTCFLAGS += -Copt-level=3
> +else ifdef CONFIG_RUST_OPT_LEVEL_S
> +KBUILD_RUSTCFLAGS += -Copt-level=s
> +else ifdef CONFIG_RUST_OPT_LEVEL_Z
> +KBUILD_RUSTCFLAGS += -Copt-level=z
This is a mess; who thought it would be a good idea to support
compiling the rust code at a different optimization level than the
rest of the C code in the kernel? Do we really need that flexibility
for Rust kernel code, or can we drop this feature?
> endif
>
> # Tell gcc to never replace conditional load with a non-conditional one
> @@ -793,6 +853,7 @@ endif
> KBUILD_CFLAGS += $(call cc-disable-warning, unused-const-variable)
> ifdef CONFIG_FRAME_POINTER
> KBUILD_CFLAGS += -fno-omit-frame-pointer -fno-optimize-sibling-calls
> +KBUILD_RUSTCFLAGS += -Cforce-frame-pointers=y
Don't the target.json files all set `"eliminate-frame-pointer":
false,`? Is this necessary then? Also, which targets retain frame
pointers at which optimization levels is quite messy (in C compilers),
as well as your choice of unwinder, which is configurable for certain
architectures.
> else
> # Some targets (ARM with Thumb2, for example), can't be built with frame
> # pointers. For those, we don't have FUNCTION_TRACER automatically
> @@ -826,6 +887,8 @@ ifdef CONFIG_CC_IS_GCC
> DEBUG_CFLAGS += $(call cc-ifversion, -lt, 0500, $(call cc-option, -fno-var-tracking-assignments))
> endif
>
> +DEBUG_RUSTCFLAGS :=
> +
> ifdef CONFIG_DEBUG_INFO
>
> ifdef CONFIG_DEBUG_INFO_SPLIT
> @@ -836,6 +899,11 @@ endif
>
> ifneq ($(LLVM_IAS),1)
> KBUILD_AFLAGS += -Wa,-gdwarf-2
> +ifdef CONFIG_DEBUG_INFO_REDUCED
> +DEBUG_RUSTCFLAGS += -Cdebuginfo=1
> +else
> +DEBUG_RUSTCFLAGS += -Cdebuginfo=2
> +endif
> endif
This should not be in the `ifneq ($(LLVM_IAS),1)` block, otherwise if
I set `LLVM_IAS=1` when invoking `make` then these wont get set.
>
> ifndef CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT
> @@ -860,6 +928,9 @@ endif # CONFIG_DEBUG_INFO
> KBUILD_CFLAGS += $(DEBUG_CFLAGS)
> export DEBUG_CFLAGS
>
> +KBUILD_RUSTCFLAGS += $(DEBUG_RUSTCFLAGS)
> +export DEBUG_RUSTCFLAGS
> +
> ifdef CONFIG_FUNCTION_TRACER
> ifdef CONFIG_FTRACE_MCOUNT_USE_CC
> CC_FLAGS_FTRACE += -mrecord-mcount
> @@ -990,10 +1061,11 @@ include $(addprefix $(srctree)/, $(include-y))
> # Do not add $(call cc-option,...) below this line. When you build the kernel
> # from the clean source tree, the GCC plugins do not exist at this point.
>
> -# Add user supplied CPPFLAGS, AFLAGS and CFLAGS as the last assignments
> +# Add user supplied CPPFLAGS, AFLAGS, CFLAGS and RUSTCFLAGS as the last assignments
> KBUILD_CPPFLAGS += $(KCPPFLAGS)
> KBUILD_AFLAGS += $(KAFLAGS)
> KBUILD_CFLAGS += $(KCFLAGS)
> +KBUILD_RUSTCFLAGS += $(KRUSTCFLAGS)
I can't help but read that as Krusty-flags. :P Are ya' ready kids?
>
> KBUILD_LDFLAGS_MODULE += --build-id=sha1
> LDFLAGS_vmlinux += --build-id=sha1
> @@ -1138,6 +1210,10 @@ export MODULES_NSDEPS := $(extmod-prefix)modules.nsdeps
> ifeq ($(KBUILD_EXTMOD),)
> core-y += kernel/ certs/ mm/ fs/ ipc/ security/ crypto/ block/
>
> +ifdef CONFIG_RUST
> +core-y += rust/
> +endif
> +
> vmlinux-dirs := $(patsubst %/,%,$(filter %/, \
> $(core-y) $(core-m) $(drivers-y) $(drivers-m) \
> $(libs-y) $(libs-m)))
> @@ -1238,6 +1314,9 @@ archprepare: outputmakefile archheaders archscripts scripts include/config/kerne
> prepare0: archprepare
> $(Q)$(MAKE) $(build)=scripts/mod
> $(Q)$(MAKE) $(build)=.
> +ifdef CONFIG_RUST
> + $(Q)$(MAKE) $(build)=rust
> +endif
>
> # All the preparing..
> prepare: prepare0 prepare-objtool prepare-resolve_btfids
> @@ -1648,6 +1727,13 @@ help:
> @echo ' kselftest-merge - Merge all the config dependencies of'
> @echo ' kselftest to existing .config.'
> @echo ''
> + @echo 'Rust targets:'
> + @echo ' rustfmt - Reformat all the Rust code in the kernel'
Seems like a good way for drive by commits where someone reformatted
the whole kernel.
> + @echo ' rustfmtcheck - Checks if all the Rust code in the kernel'
> + @echo ' is formatted, printing a diff otherwise.'
Might be nice to invoke this somehow from checkpatch.pl somehow for
changes to rust source files. Not necessary in the RFC, but perhaps
one day.
> + @echo ' rustdoc - Generate Rust documentation'
> + @echo ' (requires kernel .config)'
> + @echo ''
> @$(if $(dtstree), \
> echo 'Devicetree:'; \
> echo '* dtbs - Build device tree blobs for enabled boards'; \
> @@ -1719,6 +1805,27 @@ PHONY += $(DOC_TARGETS)
> $(DOC_TARGETS):
> $(Q)$(MAKE) $(build)=Documentation $@
>
> +
> +# Rust targets
> +# ---------------------------------------------------------------------------
> +
> +# Documentation target
> +#
> +# Using the singular to avoid running afoul of `no-dot-config-targets`.
> +PHONY += rustdoc
> +rustdoc: prepare0
> + $(Q)$(MAKE) $(build)=rust $@
> +
> +# Formatting targets
> +PHONY += rustfmt rustfmtcheck
> +
> +rustfmt:
> + find -name '*.rs' | xargs $(RUSTFMT)
> +
> +rustfmtcheck:
> + find -name '*.rs' | xargs $(RUSTFMT) --check
> +
> +
> # Misc
> # ---------------------------------------------------------------------------
>
> @@ -1866,6 +1973,7 @@ clean: $(clean-dirs)
> $(call cmd,rmfiles)
> @find $(if $(KBUILD_EXTMOD), $(KBUILD_EXTMOD), .) $(RCS_FIND_IGNORE) \
> \( -name '*.[aios]' -o -name '*.ko' -o -name '.*.cmd' \
> + -o -name '*.rmeta' \
> -o -name '*.ko.*' \
> -o -name '*.dtb' -o -name '*.dtbo' -o -name '*.dtb.S' -o -name '*.dt.yaml' \
> -o -name '*.dwo' -o -name '*.lst' \
> diff --git a/arch/arm64/rust/target.json b/arch/arm64/rust/target.json
> new file mode 100644
> index 000000000000..44953e2725c4
> --- /dev/null
> +++ b/arch/arm64/rust/target.json
> @@ -0,0 +1,40 @@
> +{
> + "arch": "aarch64",
> + "data-layout": "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128",
> + "disable-redzone": true,
> + "eliminate-frame-pointer": false,
> + "emit-debug-gdb-scripts": false,
> + "env": "gnu",
> + "features": "+strict-align,+neon,+fp-armv8",
> + "function-sections": false,
^ Depends on your config. This is implied by LTO, and IIRC
CONFIG_LD_DEAD_CODE_ELIMINATION does set this.
> + "is-builtin": true,
> + "linker-flavor": "gcc",
> + "linker-is-gnu": true,
Is there more documentation on these values? For example, is LLD a GNU
linker? I would think not, but perhaps the flag could have been named
better and this is a non-issue.
> + "llvm-target": "aarch64-unknown-none",
What happens if you use aarch64-linux-gnu, like Clang does?
> + "max-atomic-width": 128,
> + "needs-plt": true,
^ do we?
> + "os": "none",
> + "panic-strategy": "abort",
^ alternatives?
> + "position-independent-executables": true,
> + "pre-link-args": {
> + "gcc": [
^ gcc?
> + "-Wl,--as-needed",
> + "-Wl,-z,noexecstack",
> + "-m64"
Is -m64 necessary?
> + ]
> + },
> + "relocation-model": "static",
> + "relro-level": "full",
> + "stack-probes": {
> + "kind": "inline-or-call",
> + "min-llvm-version-for-inline": [
> + 11,
> + 0,
> + 1
> + ]
> + },
> + "target-c-int-width": "32",
> + "target-endian": "little",
The kernel supports both. Does that mean there would be two different
aarch64 target.json files, each per endianness? Maybe not critical
for initial support, but something to be aware of.
> + "target-pointer-width": "64",
> + "vendor": ""
> +}
> diff --git a/arch/powerpc/rust/target.json b/arch/powerpc/rust/target.json
> new file mode 100644
> index 000000000000..1e53f8308092
> --- /dev/null
> +++ b/arch/powerpc/rust/target.json
> @@ -0,0 +1,30 @@
> +{
> + "arch": "powerpc64",
> + "code-mode": "kernel",
> + "cpu": "ppc64le",
> + "data-layout": "e-m:e-i64:64-n32:64",
Seems like this target doesn't set "eliminate-frame-pointer." I think
it should one way or another to be explicit?
> + "env": "gnu",
> + "features": "-altivec,-vsx,-hard-float",
> + "function-sections": false,
> + "is-builtin": true,
> + "linker-flavor": "gcc",
> + "linker-is-gnu": true,
> + "llvm-target": "powerpc64le-elf",
> + "max-atomic-width": 64,
> + "os": "none",
> + "panic-strategy": "abort",
> + "position-independent-executables": true,
> + "pre-link-args": {
> + "gcc": [
> + "-Wl,--as-needed",
> + "-Wl,-z,noexecstack",
> + "-m64"
> + ]
> + },
> + "relocation-model": "static",
> + "relro-level": "full",
> + "target-family": "unix",
> + "target-mcount": "_mcount",
> + "target-endian": "little",
> + "target-pointer-width": "64"
> +}
> diff --git a/arch/x86/rust/target.json b/arch/x86/rust/target.json
> new file mode 100644
> index 000000000000..6e1759cd45bf
> --- /dev/null
> +++ b/arch/x86/rust/target.json
> @@ -0,0 +1,42 @@
> +{
> + "arch": "x86_64",
> + "code-model": "kernel",
> + "cpu": "x86-64",
> + "data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128",
> + "disable-redzone": true,
> + "eliminate-frame-pointer": false,
> + "emit-debug-gdb-scripts": false,
> + "env": "gnu",
> + "features": "-mmx,-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-3dnow,-3dnowa,-avx,-avx2,+soft-float",
> + "function-sections": false,
> + "is-builtin": true,
> + "linker-flavor": "gcc",
> + "linker-is-gnu": true,
> + "llvm-target": "x86_64-elf",
> + "max-atomic-width": 64,
> + "needs-plt": true,
> + "os": "none",
> + "panic-strategy": "abort",
> + "position-independent-executables": true,
> + "pre-link-args": {
> + "gcc": [
> + "-Wl,--as-needed",
> + "-Wl,-z,noexecstack",
> + "-m64"
> + ]
> + },
> + "relocation-model": "static",
> + "relro-level": "full",
> + "stack-probes": {
> + "kind": "inline-or-call",
> + "min-llvm-version-for-inline": [
> + 11,
> + 0,
> + 1
> + ]
> + },
> + "target-c-int-width": "32",
> + "target-endian": "little",
> + "target-pointer-width": "64",
> + "vendor": "unknown"
> +}
> diff --git a/init/Kconfig b/init/Kconfig
> index 5f5c776ef192..11475840c29c 100644
> --- a/init/Kconfig
> +++ b/init/Kconfig
> @@ -57,6 +57,15 @@ config LLD_VERSION
> default $(ld-version) if LD_IS_LLD
> default 0
>
> +config HAS_RUST
> + depends on ARM64 || (PPC64 && CPU_LITTLE_ENDIAN) || X86_64
> + def_bool $(success,$(RUSTC) --version)
> +
> +config RUSTC_VERSION
> + depends on HAS_RUST
> + int
> + default $(shell,$(srctree)/scripts/rust-version.sh $(RUSTC))
> +
> config CC_CAN_LINK
> bool
> default $(success,$(srctree)/scripts/cc-can-link.sh $(CC) $(CLANG_FLAGS) $(m64-flag)) if 64BIT
> @@ -2027,6 +2036,24 @@ config PROFILING
> Say Y here to enable the extended profiling support mechanisms used
> by profilers.
>
> +config RUST
> + bool "Rust support"
> + depends on HAS_RUST
> + depends on !COMPILE_TEST
> + default n
> + help
> + Enables Rust support in the kernel.
> +
> + This allows other Rust-related options, like drivers written in Rust,
> + to be selected.
> +
> + It is also required to be able to load external kernel modules
> + written in Rust.
> +
> + See Documentation/rust/ for more information.
> +
> + If unsure, say N.
> +
> #
> # Place an empty function call at each tracepoint site. Can be
> # dynamically changed for a probe function.
> diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
> index 2779c29d9981..acf4993baddc 100644
> --- a/lib/Kconfig.debug
> +++ b/lib/Kconfig.debug
> @@ -2537,6 +2537,106 @@ config HYPERV_TESTING
>
> endmenu # "Kernel Testing and Coverage"
>
> +menu "Rust hacking"
> +
> +config RUST_DEBUG_ASSERTIONS
> + bool "Debug assertions"
> + default n
> + depends on RUST
> + help
> + Enables rustc's `-Cdebug-assertions` codegen option.
> +
> + This flag lets you turn `cfg(debug_assertions)` conditional
> + compilation on or off. This can be used to enable extra debugging
> + code in development but not in production. For example, it controls
> + the behavior of the standard library's `debug_assert!` macro.
> +
> + Note that this will apply to all Rust code, including `core`.
> +
> + If unsure, say N.
> +
> +config RUST_OVERFLOW_CHECKS
> + bool "Overflow checks"
> + default y
> + depends on RUST
> + help
> + Enables rustc's `-Coverflow-checks` codegen option.
> +
> + This flag allows you to control the behavior of runtime integer
> + overflow. When overflow-checks are enabled, a panic will occur
> + on overflow.
> +
> + Note that this will apply to all Rust code, including `core`.
> +
> + If unsure, say Y.
> +
> +choice
> + prompt "Optimization level"
> + default RUST_OPT_LEVEL_SIMILAR_AS_CHOSEN_FOR_C
> + depends on RUST
> + help
> + Controls rustc's `-Copt-level` codegen option.
> +
> + This flag controls the optimization level.
> +
> + If unsure, say "Similar as chosen for C".
Yuck. This should be default on and not configurable.
> +
> +config RUST_OPT_LEVEL_SIMILAR_AS_CHOSEN_FOR_C
> + bool "Similar as chosen for C"
> + help
> + This choice will pick a similar optimization level as chosen in
> + the "Compiler optimization level" for C:
> +
> + -O2 is currently mapped to -Copt-level=2
> + -O3 is currently mapped to -Copt-level=3
> + -Os is currently mapped to -Copt-level=z
> +
> + The mapping may change over time to follow the intended semantics
> + of the choice for C as sensibly as possible.
> +
> + This is the default.
> +
> +config RUST_OPT_LEVEL_0
> + bool "No optimizations (-Copt-level=0)"
> + help
> + Not recommended for most purposes. It may come in handy for debugging
> + suspected optimizer bugs, unexpected undefined behavior, etc.
> +
> + Note that this level will *not* enable debug assertions nor overflow
> + checks on its own (like it happens when interacting with rustc
> + directly). Use the corresponding configuration options to control
> + that instead, orthogonally.
> +
> +config RUST_OPT_LEVEL_1
> + bool "Basic optimizations (-Copt-level=1)"
> + help
> + Useful for debugging without getting too lost, but without
> + the overhead and boilerplate of no optimizations at all.
> +
> +config RUST_OPT_LEVEL_2
> + bool "Some optimizations (-Copt-level=2)"
> + help
> + The sensible choice in most cases.
> +
> +config RUST_OPT_LEVEL_3
> + bool "All optimizations (-Copt-level=3)"
> + help
> + Yet more performance (hopefully).
> +
> +config RUST_OPT_LEVEL_S
> + bool "Optimize for size (-Copt-level=s)"
> + help
> + Smaller kernel, ideally without too much performance loss.
> +
> +config RUST_OPT_LEVEL_Z
> + bool "Optimize for size, no loop vectorization (-Copt-level=z)"
> + help
> + Like the previous level, but also turn off loop vectorization.
> +
> +endchoice
> +
> +endmenu # "Rust"
> +
> source "Documentation/Kconfig"
>
> endmenu # Kernel hacking
> diff --git a/rust/.gitignore b/rust/.gitignore
> new file mode 100644
> index 000000000000..8875e08ed0b1
> --- /dev/null
> +++ b/rust/.gitignore
> @@ -0,0 +1,5 @@
> +# SPDX-License-Identifier: GPL-2.0
> +
> +bindings_generated.rs
> +exports_*_generated.h
> +doc/
> \ No newline at end of file
> diff --git a/rust/Makefile b/rust/Makefile
> new file mode 100644
> index 000000000000..ba4b13e4fc7f
> --- /dev/null
> +++ b/rust/Makefile
> @@ -0,0 +1,141 @@
> +# SPDX-License-Identifier: GPL-2.0
> +
> +RUSTDOC = rustdoc
> +
> +quiet_cmd_rustdoc = RUSTDOC $<
> + cmd_rustdoc = \
> + RUST_BINDINGS_FILE=$(abspath $(objtree)/rust/bindings_generated.rs) \
> + $(RUSTDOC) $(filter-out --emit=%, $(rustc_flags)) \
> + $(rustdoc_target_flags) -L $(objtree)/rust/ \
> + --output $(objtree)/rust/doc --crate-name $(subst rustdoc-,,$@) \
> + -Fmissing-docs @$(objtree)/include/generated/rustc_cfg $<
> +
> +rustdoc: rustdoc-module rustdoc-compiler_builtins rustdoc-kernel
> +
> +rustdoc-module: private rustdoc_target_flags = --crate-type proc-macro \
> + --extern proc_macro
> +rustdoc-module: $(srctree)/rust/module.rs FORCE
> + $(call if_changed,rustdoc)
> +
> +rustdoc-compiler_builtins: $(srctree)/rust/compiler_builtins.rs FORCE
> + $(call if_changed,rustdoc)
> +
> +rustdoc-kernel: private rustdoc_target_flags = --extern alloc \
> + --extern module=$(objtree)/rust/libmodule.so
> +rustdoc-kernel: $(srctree)/rust/kernel/lib.rs rustdoc-module \
> + $(objtree)/rust/libmodule.so $(objtree)/rust/bindings_generated.rs FORCE
> + $(call if_changed,rustdoc)
> +
> +ifdef CONFIG_CC_IS_CLANG
> +bindgen_c_flags = $(c_flags)
> +else
> +# bindgen relies on libclang to parse C. Ideally, bindgen would support a GCC
> +# plugin backend and/or the Clang driver would be perfectly compatible with GCC.
> +#
> +# For the moment, here we are tweaking the flags on the fly. Some config
> +# options may not work (e.g. `GCC_PLUGIN_RANDSTRUCT` if we end up using one
> +# of those structs). We might want to redo how Clang flags are kept track of
> +# in the general `Makefile` even for GCC builds, similar to what we did with
> +# `TENTATIVE_CLANG_FLAGS`.
> +bindgen_skip_c_flags := -mno-fp-ret-in-387 -mpreferred-stack-boundary=% \
> + -mskip-rax-setup -mgeneral-regs-only -msign-return-address=% \
> + -mindirect-branch=thunk-extern -mindirect-branch-register -mrecord-mcount \
> + -mabi=lp64 -mstack-protector-guard% -fconserve-stack -falign-jumps=% \
> + -falign-loops=% -fno-ipa-cp-clone -fno-partial-inlining \
> + -fno-reorder-blocks -fno-allow-store-data-races -fasan-shadow-offset=% \
> + -Wno-packed-not-aligned -Wno-format-truncation -Wno-format-overflow \
> + -Wno-stringop-truncation -Wno-unused-but-set-variable \
> + -Wno-stringop-overflow -Wno-restrict -Wno-maybe-uninitialized \
> + -Werror=designated-init -Wno-zero-length-bounds \
> + --param=% --param asan-%
> +
> +# PowerPC
> +bindgen_skip_c_flags += -mtraceback=no -mno-pointers-to-nested-functions \
> + -mno-string -mno-strict-align
> +
> +bindgen_extra_c_flags = $(TENTATIVE_CLANG_FLAGS) -Wno-address-of-packed-member
> +bindgen_c_flags = $(filter-out $(bindgen_skip_c_flags), $(c_flags)) \
> + $(bindgen_extra_c_flags)
> +endif
> +
> +bindgen_opaque_types := xregs_state desc_struct arch_lbr_state
> +
> +# To avoid several recompilations in PowerPC, which inserts `-D_TASK_CPU`
> +bindgen_c_flags_final = $(filter-out -D_TASK_CPU=%, $(bindgen_c_flags))
> +
> +quiet_cmd_bindgen = BINDGEN $@
> + cmd_bindgen = \
> + $(BINDGEN) $< $(addprefix --opaque-type , $(bindgen_opaque_types)) \
> + --use-core --with-derive-default --ctypes-prefix c_types \
> + --size_t-is-usize -o $@ -- $(bindgen_c_flags_final) -DMODULE
> +
> +$(objtree)/rust/bindings_generated.rs: $(srctree)/rust/kernel/bindings_helper.h FORCE
> + $(call if_changed_dep,bindgen)
> +
> +quiet_cmd_exports = EXPORTS $@
> + cmd_exports = \
> + $(NM) -p --defined-only $< \
> + | grep -E ' (T|R|D) ' | cut -d ' ' -f 3 | grep -E '^(__rust_|_R)' \
> + | xargs -Isymbol \
> + echo 'EXPORT_SYMBOL_RUST_GPL(symbol);' > $@
> +
> +$(objtree)/rust/exports_core_generated.h: $(objtree)/rust/core.o FORCE
> + $(call if_changed,exports)
> +
> +$(objtree)/rust/exports_alloc_generated.h: $(objtree)/rust/alloc.o FORCE
> + $(call if_changed,exports)
> +
> +$(objtree)/rust/exports_kernel_generated.h: $(objtree)/rust/kernel.o FORCE
> + $(call if_changed,exports)
> +
> +# `-Cpanic=unwind -Cforce-unwind-tables=y` overrides `rustc_flags` in order to
> +# avoid the https://github.com/rust-lang/rust/issues/82320 rustc crash.
> +quiet_cmd_rustc_procmacro = $(RUSTC_OR_CLIPPY_QUIET) P $@
> + cmd_rustc_procmacro = \
> + $(RUSTC_OR_CLIPPY) $(rustc_flags) \
> + --emit=dep-info,link --extern proc_macro \
> + -Cpanic=unwind -Cforce-unwind-tables=y \
> + --crate-type proc-macro --out-dir $(objtree)/rust/ \
> + --crate-name $(patsubst lib%.so,%,$(notdir $@)) $<; \
> + mv $(objtree)/rust/$(patsubst lib%.so,%,$(notdir $@)).d $(depfile); \
> + sed -i '/^\#/d' $(depfile)
> +
> +$(objtree)/rust/libmodule.so: $(srctree)/rust/module.rs FORCE
> + $(call if_changed_dep,rustc_procmacro)
> +
> +quiet_cmd_rustc_library = $(if $(skip_clippy),RUSTC,$(RUSTC_OR_CLIPPY_QUIET)) L $@
> + cmd_rustc_library = \
> + RUST_BINDINGS_FILE=$(abspath $(objtree)/rust/bindings_generated.rs) \
> + $(if $(skip_clippy),$(RUSTC),$(RUSTC_OR_CLIPPY)) \
> + $(rustc_flags) $(rustc_cross_flags) $(rustc_target_flags) \
> + --crate-type rlib --out-dir $(objtree)/rust/ -L $(objtree)/rust/ \
> + --crate-name $(patsubst %.o,%,$(notdir $@)) $<; \
> + mv $(objtree)/rust/$(patsubst %.o,%,$(notdir $@)).d $(depfile); \
> + sed -i '/^\#/d' $(depfile) \
> + $(if $(rustc_objcopy),;$(OBJCOPY) $(rustc_objcopy) $@)
> +
> +# `$(rustc_flags)` is passed in case the user added `--sysroot`.
> +rustc_sysroot = $(shell $(RUSTC) $(rustc_flags) --print sysroot)
> +rustc_src = $(rustc_sysroot)/lib/rustlib/src/rust
> +
> +.SECONDEXPANSION:
> +$(objtree)/rust/core.o: private skip_clippy = 1
> +$(objtree)/rust/core.o: $$(rustc_src)/library/core/src/lib.rs FORCE
> + $(call if_changed_dep,rustc_library)
> +
> +$(objtree)/rust/compiler_builtins.o: private rustc_objcopy = -w -W '__*'
> +$(objtree)/rust/compiler_builtins.o: $(srctree)/rust/compiler_builtins.rs \
> + $(objtree)/rust/core.o FORCE
> + $(call if_changed_dep,rustc_library)
> +
> +$(objtree)/rust/alloc.o: private skip_clippy = 1
> +$(objtree)/rust/alloc.o: $$(rustc_src)/library/alloc/src/lib.rs \
> + $(objtree)/rust/compiler_builtins.o FORCE
> + $(call if_changed_dep,rustc_library)
> +
> +# ICE on `--extern module`: https://github.com/rust-lang/rust/issues/56935
> +$(objtree)/rust/kernel.o: private rustc_target_flags = --extern alloc \
> + --extern module=$(objtree)/rust/libmodule.so
> +$(objtree)/rust/kernel.o: $(srctree)/rust/kernel/lib.rs $(objtree)/rust/alloc.o \
> + $(objtree)/rust/libmodule.so $(objtree)/rust/bindings_generated.rs FORCE
> + $(call if_changed_dep,rustc_library)
> diff --git a/scripts/Makefile.build b/scripts/Makefile.build
> index 1b6094a13034..3665c49c4dcf 100644
> --- a/scripts/Makefile.build
> +++ b/scripts/Makefile.build
> @@ -26,6 +26,7 @@ EXTRA_CPPFLAGS :=
> EXTRA_LDFLAGS :=
> asflags-y :=
> ccflags-y :=
> +rustcflags-y :=
> cppflags-y :=
> ldflags-y :=
>
> @@ -287,6 +288,24 @@ quiet_cmd_cc_lst_c = MKLST $@
> $(obj)/%.lst: $(src)/%.c FORCE
> $(call if_changed_dep,cc_lst_c)
>
> +# Compile Rust sources (.rs)
> +# ---------------------------------------------------------------------------
> +
> +rustc_cross_flags := --target=$(srctree)/arch/$(SRCARCH)/rust/target.json
> +
> +quiet_cmd_rustc_o_rs = $(RUSTC_OR_CLIPPY_QUIET) $(quiet_modtag) $@
> + cmd_rustc_o_rs = \
> + RUST_MODFILE=$(modfile) \
> + $(RUSTC_OR_CLIPPY) $(rustc_flags) $(rustc_cross_flags) \
> + --extern alloc --extern kernel \
> + --crate-type rlib --out-dir $(obj) -L $(objtree)/rust/ \
> + --crate-name $(patsubst %.o,%,$(notdir $@)) $<; \
> + mv $(obj)/$(subst .o,,$(notdir $@)).d $(depfile); \
> + sed -i '/^\#/d' $(depfile)
> +
> +$(obj)/%.o: $(src)/%.rs FORCE
> + $(call if_changed_dep,rustc_o_rs)
> +
> # Compile assembler sources (.S)
> # ---------------------------------------------------------------------------
>
> diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
> index 8cd67b1b6d15..bd6cb3562fb4 100644
> --- a/scripts/Makefile.lib
> +++ b/scripts/Makefile.lib
> @@ -8,6 +8,7 @@ ldflags-y += $(EXTRA_LDFLAGS)
> # flags that take effect in current and sub directories
> KBUILD_AFLAGS += $(subdir-asflags-y)
> KBUILD_CFLAGS += $(subdir-ccflags-y)
> +KBUILD_RUSTCFLAGS += $(subdir-rustcflags-y)
>
> # Figure out what we need to build from the various variables
> # ===========================================================================
> @@ -122,6 +123,10 @@ _c_flags = $(filter-out $(CFLAGS_REMOVE_$(target-stem).o), \
> $(filter-out $(ccflags-remove-y), \
> $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) $(ccflags-y)) \
> $(CFLAGS_$(target-stem).o))
> +_rustc_flags = $(filter-out $(RUSTCFLAGS_REMOVE_$(target-stem).o), \
> + $(filter-out $(rustcflags-remove-y), \
> + $(KBUILD_RUSTCFLAGS) $(rustcflags-y)) \
> + $(RUSTCFLAGS_$(target-stem).o))
> _a_flags = $(filter-out $(AFLAGS_REMOVE_$(target-stem).o), \
> $(filter-out $(asflags-remove-y), \
> $(KBUILD_CPPFLAGS) $(KBUILD_AFLAGS) $(asflags-y)) \
> @@ -191,6 +196,11 @@ modkern_cflags = \
> $(KBUILD_CFLAGS_MODULE) $(CFLAGS_MODULE), \
> $(KBUILD_CFLAGS_KERNEL) $(CFLAGS_KERNEL) $(modfile_flags))
>
> +modkern_rustcflags = \
> + $(if $(part-of-module), \
> + $(KBUILD_RUSTCFLAGS_MODULE) $(RUSTCFLAGS_MODULE), \
> + $(KBUILD_RUSTCFLAGS_KERNEL) $(RUSTCFLAGS_KERNEL))
> +
> modkern_aflags = $(if $(part-of-module), \
> $(KBUILD_AFLAGS_MODULE) $(AFLAGS_MODULE), \
> $(KBUILD_AFLAGS_KERNEL) $(AFLAGS_KERNEL))
> @@ -200,6 +210,8 @@ c_flags = -Wp,-MMD,$(depfile) $(NOSTDINC_FLAGS) $(LINUXINCLUDE) \
> $(_c_flags) $(modkern_cflags) \
> $(basename_flags) $(modname_flags)
>
> +rustc_flags = $(_rustc_flags) $(modkern_rustcflags) @$(objtree)/include/generated/rustc_cfg
> +
> a_flags = -Wp,-MMD,$(depfile) $(NOSTDINC_FLAGS) $(LINUXINCLUDE) \
> $(_a_flags) $(modkern_aflags)
>
> diff --git a/scripts/kconfig/confdata.c b/scripts/kconfig/confdata.c
> index 2568dbe16ed6..a83d646ecef5 100644
> --- a/scripts/kconfig/confdata.c
> +++ b/scripts/kconfig/confdata.c
> @@ -637,6 +637,56 @@ static struct conf_printer kconfig_printer_cb =
> .print_comment = kconfig_print_comment,
> };
>
> +/*
> + * rustc cfg printer
> + *
> + * This printer is used when generating the resulting rustc configuration
> + * after kconfig invocation and `defconfig` files.
> + */
> +static void rustc_cfg_print_symbol(FILE *fp, struct symbol *sym, const char *value, void *arg)
> +{
> + const char *str;
> +
> + switch (sym->type) {
> + case S_INT:
> + case S_HEX:
> + case S_BOOLEAN:
> + case S_TRISTATE:
> + str = sym_escape_string_value(value);
> +
> + /*
> + * We don't care about disabled ones, i.e. no need for
> + * what otherwise are "comments" in other printers.
> + */
> + if (*value == 'n')
> + return;
> +
> + /*
> + * To have similar functionality to the C macro `IS_ENABLED()`
> + * we provide an empty `--cfg CONFIG_X` here in both `y`
> + * and `m` cases.
> + *
> + * Then, the common `fprintf()` below will also give us
> + * a `--cfg CONFIG_X="y"` or `--cfg CONFIG_X="m"`, which can
> + * be used as the equivalent of `IS_BUILTIN()`/`IS_MODULE()`.
> + */
> + if (*value == 'y' || *value == 'm')
> + fprintf(fp, "--cfg=%s%s\n", CONFIG_, sym->name);
> +
> + break;
> + default:
> + str = value;
> + break;
> + }
> +
> + fprintf(fp, "--cfg=%s%s=%s\n", CONFIG_, sym->name, str);
> +}
> +
> +static struct conf_printer rustc_cfg_printer_cb =
> +{
> + .print_symbol = rustc_cfg_print_symbol,
> +};
> +
> /*
> * Header printer
> *
> @@ -1044,7 +1094,7 @@ int conf_write_autoconf(int overwrite)
> struct symbol *sym;
> const char *name;
> const char *autoconf_name = conf_get_autoconfig_name();
> - FILE *out, *out_h;
> + FILE *out, *out_h, *out_rustc_cfg;
> int i;
>
> if (!overwrite && is_present(autoconf_name))
> @@ -1065,6 +1115,13 @@ int conf_write_autoconf(int overwrite)
> return 1;
> }
>
> + out_rustc_cfg = fopen(".tmp_rustc_cfg", "w");
> + if (!out_rustc_cfg) {
> + fclose(out);
> + fclose(out_h);
> + return 1;
> + }
> +
> conf_write_heading(out, &kconfig_printer_cb, NULL);
> conf_write_heading(out_h, &header_printer_cb, NULL);
>
> @@ -1076,9 +1133,11 @@ int conf_write_autoconf(int overwrite)
> /* write symbols to auto.conf and autoconf.h */
> conf_write_symbol(out, sym, &kconfig_printer_cb, (void *)1);
> conf_write_symbol(out_h, sym, &header_printer_cb, NULL);
> + conf_write_symbol(out_rustc_cfg, sym, &rustc_cfg_printer_cb, NULL);
> }
> fclose(out);
> fclose(out_h);
> + fclose(out_rustc_cfg);
>
> name = getenv("KCONFIG_AUTOHEADER");
> if (!name)
> @@ -1097,6 +1156,12 @@ int conf_write_autoconf(int overwrite)
> if (rename(".tmpconfig", autoconf_name))
> return 1;
>
> + name = "include/generated/rustc_cfg";
> + if (make_parent_dir(name))
> + return 1;
> + if (rename(".tmp_rustc_cfg", name))
> + return 1;
> +
> return 0;
> }
>
> diff --git a/scripts/rust-version.sh b/scripts/rust-version.sh
> new file mode 100755
> index 000000000000..67b6d31688e2
> --- /dev/null
> +++ b/scripts/rust-version.sh
> @@ -0,0 +1,31 @@
> +#!/bin/sh
> +# SPDX-License-Identifier: GPL-2.0
> +#
> +# rust-version rust-command
> +#
> +# Print the compiler version of `rust-command' in a 5 or 6-digit form
> +# such as `14502' for rustc-1.45.2 etc.
> +#
> +# Returns 0 if not found (so that Kconfig does not complain)
> +compiler="$*"
> +
> +if [ ${#compiler} -eq 0 ]; then
> + echo "Error: No compiler specified." >&2
> + printf "Usage:\n\t$0 <rust-command>\n" >&2
> + exit 1
> +fi
> +
> +if ! command -v $compiler >/dev/null 2>&1; then
> + echo 0
> + exit 0
> +fi
> +
> +VERSION=$($compiler --version | cut -f2 -d' ')
> +
> +# Cut suffix if any (e.g. `-dev`)
> +VERSION=$(echo $VERSION | cut -f1 -d'-')
> +
> +MAJOR=$(echo $VERSION | cut -f1 -d'.')
> +MINOR=$(echo $VERSION | cut -f2 -d'.')
> +PATCHLEVEL=$(echo $VERSION | cut -f3 -d'.')
> +printf "%d%02d%02d\\n" $MAJOR $MINOR $PATCHLEVEL
> --
> 2.17.1
>
--
Thanks,
~Nick Desaulniers
On Wed, Apr 14, 2021 at 12:35 PM Linus Torvalds
<[email protected]> wrote:
>
> On Wed, Apr 14, 2021 at 11:47 AM <[email protected]> wrote:
> >
> > From: Miguel Ojeda <[email protected]>
> >
> > A set of Rust modules that showcase how Rust modules look like
> > and how to use the abstracted kernel features.
>
> Honestly, I'd like to see a real example. This is fine for testing,
> but I'd like to see something a bit more real, and a bit less special
> than the Android "binder" WIP that comes a few patches later.
>
> Would there be some kind of real driver or something that people could
> use as a example of a real piece of code that actually does something
> meaningful?
Are you suggesting that they "rewrite it in Rust?" :^P *ducks*
(sorry, I couldn't help myself) Perhaps it would be a good exercise to
demonstrate some of the benefits of using Rust for driver work?
--
Thanks,
~Nick Desaulniers
On Wed, Apr 14, 2021 at 11:47 AM <[email protected]> wrote:
>
> From: Miguel Ojeda <[email protected]>
>
> 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:
>
> [email protected]
>
> and take a look at the project itself at:
>
> https://github.com/Rust-for-Linux
Looks like Wedson's writeup is now live. Nice job Wedson!
https://security.googleblog.com/2021/04/rust-in-linux-kernel.html
--
Thanks,
~Nick Desaulniers
On Thu, Apr 15, 2021 at 1:19 AM Nick Desaulniers
<[email protected]> wrote:
>
> Rather than check the origin (yikes, are we intentionally avoiding env
> vars?), can this simply be
> ifneq ($(CLIPPY),)
> KBUILD_CLIPPY := $(CLIPPY)
> endif
>
> Then you can specify whatever value you want, support command line or
> env vars, etc.?
I was following the other existing cases like `V`. Masahiro can
probably answer why they are done like this.
> -Oz in clang typically generates larger kernel code than -Os; LLVM
> seems to aggressively emit libcalls even when the setup for a call
> would be larger than the inlined call itself. Is z smaller than s for
> the existing rust examples?
I will check if the `s`/`z` flags have the exact same semantics as
they do in Clang, but as a quick test (quite late here, sorry!), yes,
it seems `z` is smaller:
text data bss dec hex filename
126568 8 104 126680 1eed8 drivers/android/rust_binder.o [s]
122923 8 104 123035 1e09b drivers/android/rust_binder.o [z]
212351 0 0 212351 33d7f rust/core.o [s]
207653 0 0 207653 32b25 rust/core.o [z]
> This is a mess; who thought it would be a good idea to support
> compiling the rust code at a different optimization level than the
> rest of the C code in the kernel? Do we really need that flexibility
> for Rust kernel code, or can we drop this feature?
I did :P
The idea is that, since it seemed to work out of the box when I tried,
it could be nice to keep for debugging and for having another degree
of freedom when testing the compiler/nightlies etc.
Also, it is not intended for users, which is why I put it in the
"hacking" menu -- users should still only modify the usual global
option.
However, it is indeed strange for the kernel and I don't mind dropping
it if people want to see it out (one could still do it manually if
needed...).
(Related: from what I have been told, the kernel does not support
lower levels in C just due to old problems with compilers; but those
may be gone now).
> Don't the target.json files all set `"eliminate-frame-pointer":
> false,`? Is this necessary then? Also, which targets retain frame
> pointers at which optimization levels is quite messy (in C compilers),
> as well as your choice of unwinder, which is configurable for certain
> architectures.
For this (and other questions regarding the target definition files
you have below): the situation is quite messy, indeed. Some of these
options can be configured via flags too. Also, the target definition
files are actually unstable in `rustc` because they are too tied to
LLVM. So AFAIK if a command-line flag exists, we should use that. But
I am not sure if the target definition file is supposed to support
removing keys etc.
Anyway, the plan here short-term is to generate the target definition
file on the fly taking into account the options, and keep it working
w.r.t. `rustc` nightlies (this is also why we don't have have big
endian for ppc or 32-bit x86). Longer-term, hopefully `rustc` adds
enough command-line flags to tweak as needed, or stabilizes the target
files somehow, or stabilizes all the target combinations we need (i.e.
as built-in targets in the compiler).
In fact, I will add this to the "unstable features" list.
> Seems like a good way for drive by commits where someone reformatted
> the whole kernel.
We enforce the formatting for all the code at the moment in the CI and
AFAIK `rustfmt` tries to keep formatting stable (as long as one does
not use unstable features), so code should always be formatted.
Having said that, I'm not sure 100% how stable it actually is in
practice over long periods of time -- I guess we will find out soon
enough.
> Might be nice to invoke this somehow from checkpatch.pl somehow for
> changes to rust source files. Not necessary in the RFC, but perhaps
> one day.
We do it in the CI (see above).
> Yuck. This should be default on and not configurable.
See above for the opt-levels.
Cheers,
Miguel
Before anything else: yay! I'm really glad to see this RFC officially
hit LKML. :)
On Wed, Apr 14, 2021 at 10:20:51PM +0200, Miguel Ojeda wrote:
> - On floating-point, 128-bit, etc.: the main issue is that the
> `core` library is a single big blob at the moment. I have already
> mentioned this to some Rust team folks. We will need a way to "cut"
> some things out, for instance with the "feature flags" they already
> have for other crates (or they can split `core` in to several, like
> `alloc` is for similar reasons). Or we could do it on our side
> somehow, but I prefer to avoid that (we cannot easily customize `core`
> like we can with `alloc`, because it is tied to the compiler too
> tightly).
Besides just FP, 128-bit, etc, I remain concerned about just basic
math operations. C has no way to describe the intent of integer
overflow, so the kernel was left with the only "predictable" result:
wrap around. Unfortunately, this is wrong in most cases, and we're left
with entire classes of vulnerability related to such overflows.
When originally learning Rust I was disappointed to see that (by default)
Rust similarly ignores the overflow problem, but I'm glad to see the
very intentional choices in the Rust-in-Linux design to deal with it
directly. I think the default behavior should be saturate-with-WARN
(this will match the ultimate goals of the UBSAN overflow support[1][2]
in the C portions of the kernel). Rust code wanting wrapping/checking
can expressly use those. The list of exploitable overflows is loooong,
and this will remain a weakness in Rust unless we get it right from
the start. What's not clear to me is if it's better to say "math with
undeclared overflow expectation" will saturate" or to say "all math must
declare its overflow expectation".
-Kees
[1] https://github.com/KSPP/linux/issues/26
[2] https://github.com/KSPP/linux/issues/27
--
Kees Cook
On Wed, Apr 14, 2021 at 04:24:45PM -0700, Nick Desaulniers wrote:
> On Wed, Apr 14, 2021 at 12:35 PM Linus Torvalds
> <[email protected]> wrote:
> >
> > On Wed, Apr 14, 2021 at 11:47 AM <[email protected]> wrote:
> > >
> > > From: Miguel Ojeda <[email protected]>
> > >
> > > A set of Rust modules that showcase how Rust modules look like
> > > and how to use the abstracted kernel features.
> >
> > Honestly, I'd like to see a real example. This is fine for testing,
> > but I'd like to see something a bit more real, and a bit less special
> > than the Android "binder" WIP that comes a few patches later.
> >
> > Would there be some kind of real driver or something that people could
> > use as a example of a real piece of code that actually does something
> > meaningful?
>
> Are you suggesting that they "rewrite it in Rust?" :^P *ducks*
Well, that's what they are doing here with the binder code :)
Seriously, binder is not a "normal" driver by any means, the only way
you can squint at it and consider it a driver is that it has a char
device node that it uses to talk to userspace with. Other than that,
it's very stand-alone and does crazy things to kernel internals, which
makes it a good canidate for a rust rewrite in that it is easy to
benchmark and no one outside of one ecosystem relies on it.
The binder code also shows that there is a bunch of "frameworks" that
need to be ported to rust to get it to work, I think the majority of the
rust code for binder is just trying to implement core kernel things like
linked lists and the like. That will need to move into the rust kernel
core eventually.
The binder rewrite here also is missing a number of features that the
in-kernel binder code has gotten over the years, so it is not
feature-complete by any means yet, it's still a "toy example".
> (sorry, I couldn't help myself) Perhaps it would be a good exercise to
> demonstrate some of the benefits of using Rust for driver work?
I've been talking with the developers here about doing a "real" driver,
as the interaction between the rust code which has one set of lifetime
rules, and the driver core/model which has a different set of lifetime
rules, is going to be what shows if this actually can be done or not.
If the two can not successfully be "bridged" together, then there will
be major issues.
Matthew points out that a nvme driver would be a good example, and I
have a few other thoughts about what would be good to start with for
some of the basics that driver authors deal with on a daily basis
(platform driver, gpio driver, pcspkr driver, /dev/zero replacement), or
that might be more suited for a "safety critical language use-case" like
the HID parser or maybe the ACPI parser (but that falls into the rewrite
category that we want to stay away from for now...)
Let's see what happens here, this patchset is a great start that
provides the core "here's how to build rust in the kernel build system",
which was a non-trivial engineering effort. Hats off to them that "all"
I had to do was successfully install the proper rust compiler on my
system (not these developers fault), and then building the kernel code
here did "just work". That's a major achievement.
thanks,
greg k-h
On Thu, Apr 15, 2021 at 12:10 AM Greg Kroah-Hartman
<[email protected]> wrote:
>
> On Wed, Apr 14, 2021 at 04:24:45PM -0700, Nick Desaulniers wrote:
> > On Wed, Apr 14, 2021 at 12:35 PM Linus Torvalds
> > <[email protected]> wrote:
> > >
> > > On Wed, Apr 14, 2021 at 11:47 AM <[email protected]> wrote:
> > > >
> > > > From: Miguel Ojeda <[email protected]>
> > > >
> > > > A set of Rust modules that showcase how Rust modules look like
> > > > and how to use the abstracted kernel features.
> > >
> > > Honestly, I'd like to see a real example. This is fine for testing,
> > > but I'd like to see something a bit more real, and a bit less special
> > > than the Android "binder" WIP that comes a few patches later.
> > >
> > > Would there be some kind of real driver or something that people could
> > > use as a example of a real piece of code that actually does something
> > > meaningful?
> >
> > Are you suggesting that they "rewrite it in Rust?" :^P *ducks*
>
> Well, that's what they are doing here with the binder code :)
I know, but imagine the meme magic if Linus said literally that!
Missed opportunity.
> Seriously, binder is not a "normal" driver by any means, the only way
> you can squint at it and consider it a driver is that it has a char
> device node that it uses to talk to userspace with. Other than that,
> it's very stand-alone and does crazy things to kernel internals, which
> makes it a good canidate for a rust rewrite in that it is easy to
> benchmark and no one outside of one ecosystem relies on it.
>
> The binder code also shows that there is a bunch of "frameworks" that
> need to be ported to rust to get it to work, I think the majority of the
> rust code for binder is just trying to implement core kernel things like
> linked lists and the like. That will need to move into the rust kernel
> core eventually.
>
> The binder rewrite here also is missing a number of features that the
> in-kernel binder code has gotten over the years, so it is not
> feature-complete by any means yet, it's still a "toy example".
>
> > (sorry, I couldn't help myself) Perhaps it would be a good exercise to
> > demonstrate some of the benefits of using Rust for driver work?
>
> I've been talking with the developers here about doing a "real" driver,
> as the interaction between the rust code which has one set of lifetime
> rules, and the driver core/model which has a different set of lifetime
> rules, is going to be what shows if this actually can be done or not.
> If the two can not successfully be "bridged" together, then there will
> be major issues.
>
> Matthew points out that a nvme driver would be a good example, and I
> have a few other thoughts about what would be good to start with for
> some of the basics that driver authors deal with on a daily basis
> (platform driver, gpio driver, pcspkr driver, /dev/zero replacement), or
> that might be more suited for a "safety critical language use-case" like
> the HID parser or maybe the ACPI parser (but that falls into the rewrite
> category that we want to stay away from for now...)
Sage advice, and it won't hurt to come back with more examples.
Perhaps folks in the Rust community who have been itching to get
involved in developing their favorite operating system might be
interested?
One technique for new language adoption I've seen at Mozilla and
Google has been a moratorium that any code in <newlang> needs to have
a fallback in <oldlang> in case <newlang> doesn't work out. Perhaps
that would be a good policy to consider; you MAY rewrite existing
drivers in Rust, but you MUST provide a C implementation or ensure one
exists as fallback until further notice. That might also allay
targetability concerns.
> Let's see what happens here, this patchset is a great start that
> provides the core "here's how to build rust in the kernel build system",
> which was a non-trivial engineering effort. Hats off to them that "all"
> I had to do was successfully install the proper rust compiler on my
> system (not these developers fault), and then building the kernel code
> here did "just work". That's a major achievement.
For sure, kudos folks and thanks Greg for taking the time to try it
out and provide feedback plus ideas for more interesting drivers.
--
Thanks,
~Nick Desaulniers
...
> Besides just FP, 128-bit, etc, I remain concerned about just basic
> math operations. C has no way to describe the intent of integer
> overflow, so the kernel was left with the only "predictable" result:
> wrap around. Unfortunately, this is wrong in most cases, and we're left
> with entire classes of vulnerability related to such overflows.
I'm not sure any of the alternatives (except perhaps panic)
are much better.
Many years ago I used a COBOL system that skipped the assignment
if ADD X to Y (y += x) would overflow.
That gave a very hard to spot error when the sump of a long list
way a little too large.
If it had wrapped the error would be obvious.
There are certainly places where saturate is good.
Mostly when dealing with analogue samples.
I guess the problematic code is stuff that checks:
if (foo->size + constant > limit) goto error;
instead of:
if (foo->size > limit - constant) goto error;
David
-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK
Registration No: 1397386 (Wales)
On Thu, Apr 15, 2021 at 2:23 AM Nick Desaulniers
<[email protected]> wrote:
>
> Looks like Wedson's writeup is now live. Nice job Wedson!
> https://security.googleblog.com/2021/04/rust-in-linux-kernel.html
+1 It is very nicely written and explains the semaphore samples
(included in the RFC) he wrote, with nice tables comparing how
different parts look like between C and Rust!
Anyone interested in this RFC, C or Rust, please take a look!
Cheers,
Miguel
Hi Nathan,
Sorry, with all the other things I ended up not replying to you before
going to sleep.
On Wed, Apr 14, 2021 at 8:59 PM Nathan Chancellor <[email protected]> wrote:
>
> Shuffling this around will cause this issue (I never saw you CC'd on the
> thread).
>
> https://lore.kernel.org/r/f6218ac526a04fa4d4406f935bcc4eb4a7df65c4.1617917438.git.msuchanek@suse.de/
>
> Perhaps that patch should be added to this series?
Ah, thanks for the pointer! Yeah, I was not aware of that thread. It
is good to find these bits though, that's why we are in linux-next
after all! :)
Cheers,
Miguel
On Thu, Apr 15, 2021 at 3:38 AM Kees Cook <[email protected]> wrote:
>
> Before anything else: yay! I'm really glad to see this RFC officially
> hit LKML. :)
Thanks! :)
> When originally learning Rust I was disappointed to see that (by default)
> Rust similarly ignores the overflow problem, but I'm glad to see the
> very intentional choices in the Rust-in-Linux design to deal with it
> directly. I think the default behavior should be saturate-with-WARN
> (this will match the ultimate goals of the UBSAN overflow support[1][2]
> in the C portions of the kernel). Rust code wanting wrapping/checking
> can expressly use those. The list of exploitable overflows is loooong,
> and this will remain a weakness in Rust unless we get it right from
> the start. What's not clear to me is if it's better to say "math with
> undeclared overflow expectation" will saturate" or to say "all math must
> declare its overflow expectation".
+1 Agreed, we need to get this right (and ideally make both the C and
Rust sides agree...).
Cheers,
Miguel
On Thu, Apr 15, 2021 at 9:10 AM Greg Kroah-Hartman
<[email protected]> wrote:
>
> Let's see what happens here, this patchset is a great start that
> provides the core "here's how to build rust in the kernel build system",
> which was a non-trivial engineering effort. Hats off to them that "all"
> I had to do was successfully install the proper rust compiler on my
> system (not these developers fault), and then building the kernel code
> here did "just work". That's a major achievement.
Thanks a lot for the kind words and for trying it!
Let's see if we can make this happen.
Cheers,
Miguel
On Wed, Apr 14, 2021 at 5:43 PM Miguel Ojeda
<[email protected]> wrote:
>
> On Thu, Apr 15, 2021 at 1:19 AM Nick Desaulniers
> <[email protected]> wrote:
> >
> > -Oz in clang typically generates larger kernel code than -Os; LLVM
> > seems to aggressively emit libcalls even when the setup for a call
> > would be larger than the inlined call itself. Is z smaller than s for
> > the existing rust examples?
>
> I will check if the `s`/`z` flags have the exact same semantics as
> they do in Clang, but as a quick test (quite late here, sorry!), yes,
> it seems `z` is smaller:
>
> text data bss dec hex filename
>
> 126568 8 104 126680 1eed8 drivers/android/rust_binder.o [s]
> 122923 8 104 123035 1e09b drivers/android/rust_binder.o [z]
>
> 212351 0 0 212351 33d7f rust/core.o [s]
> 207653 0 0 207653 32b25 rust/core.o [z]
cool, thanks for verifying. LGTM
> > This is a mess; who thought it would be a good idea to support
> > compiling the rust code at a different optimization level than the
> > rest of the C code in the kernel? Do we really need that flexibility
> > for Rust kernel code, or can we drop this feature?
>
> I did :P
>
> The idea is that, since it seemed to work out of the box when I tried,
> it could be nice to keep for debugging and for having another degree
> of freedom when testing the compiler/nightlies etc.
>
> Also, it is not intended for users, which is why I put it in the
> "hacking" menu -- users should still only modify the usual global
> option.
>
> However, it is indeed strange for the kernel and I don't mind dropping
> it if people want to see it out (one could still do it manually if
> needed...).
>
> (Related: from what I have been told, the kernel does not support
> lower levels in C just due to old problems with compilers; but those
> may be gone now).
IIRC the kernel (or at least x86_64 defconfig) cannot be built at -O0,
which is too bad if developers were myopically focused on build times.
It would have been nice to have something like
CONFIG_CC_OPTIMIZE_FOR_COMPILE_TIME to join
CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE and CONFIG_CC_OPTIMIZE_FOR_SIZE,
but maybe it's still possible to support one day. (¿Por qué no los
tres? Perhaps a false-trichotomy? Sorry, but those 3 are somewhat at
odds for compilation).
Until then, I don't see why we need to permit developers to express
such flexibility for just the Rust code, or have it differ from the
intent of the C code. Does it make sense to set RUST_OPT_LEVEL_3 and
CC_OPTIMIZE_FOR_SIZE? I doubt it. That doesn't seem like a development
feature, but a mistake. YAGNI. Instead developers should clarify
what they care about in terms of high level intent; if someone wants
to micromanage optimization level flags in their forks they don't need
a Kconfig to do it (they're either going to hack KBUILD_CFLAGS,
CFLAGS_*.o, or KCFLAGS), and there's probably better mechanisms for
fine-tooth precision of optimizing actually hot code or not via PGO
and AutoFDO.
https://lore.kernel.org/lkml/[email protected]/
--
Thanks,
~Nick Desaulniers
On Thu, Apr 15, 2021 at 08:26:21AM +0000, David Laight wrote:
> ...
> > Besides just FP, 128-bit, etc, I remain concerned about just basic
> > math operations. C has no way to describe the intent of integer
> > overflow, so the kernel was left with the only "predictable" result:
> > wrap around. Unfortunately, this is wrong in most cases, and we're left
> > with entire classes of vulnerability related to such overflows.
>
> I'm not sure any of the alternatives (except perhaps panic)
> are much better.
> Many years ago I used a COBOL system that skipped the assignment
> if ADD X to Y (y += x) would overflow.
> That gave a very hard to spot error when the sump of a long list
> way a little too large.
> If it had wrapped the error would be obvious.
>
> There are certainly places where saturate is good.
> Mostly when dealing with analogue samples.
>
> I guess the problematic code is stuff that checks:
> if (foo->size + constant > limit) goto error;
> instead of:
> if (foo->size > limit - constant) goto error;
Right. This and alloc(size * count) are the primary offenders. :)
--
Kees Cook
On Wed, Apr 14, 2021 at 08:45:51PM +0200, [email protected] wrote:
> 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.
And yet I see not a single mention of the Rust Memory Model and how it
aligns (or not) with the LKMM. The C11 memory model for example is a
really poor fit for LKMM.
> ## 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.
This; can we mercilessly break the .rs bits when refactoring? What
happens the moment we cannot boot x86_64 without Rust crap on?
We can ignore this as a future problem, but I think it's only fair to
discuss now. I really don't care for that future, and IMO adding this
Rust or any other second language is a fail.
> 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.
HTML is not a valid documentation format. Heck, markdown itself is
barely readable.
> 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.
It is really *really* hard to read. It has all sorts of weird things,
like operators at the beginning after a line break:
if (foo
|| bar)
which is just wrong. And it suffers from CamelCase, which is just about
the worst thing ever. Not even the C++ std libs have that (or had, back
when I still did knew C++).
I also see:
if (foo) {
...
}
and
if foo {
}
the latter, ofcourse, being complete rubbish.
> 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!
I cannot view with less or vim. Therefore it looks not at all.
> - Boqun Feng is working hard on the different options for
> threading abstractions and has reviewed most of the `sync` PRs.
Boqun, I know you're familiar with LKMM, can you please talk about how
Rust does things and how it interacts?
On Thu, Apr 15, 2021 at 08:58:16PM +0200, Peter Zijlstra wrote:
> On Wed, Apr 14, 2021 at 08:45:51PM +0200, [email protected] wrote:
>
> > 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.
>
> And yet I see not a single mention of the Rust Memory Model and how it
> aligns (or not) with the LKMM. The C11 memory model for example is a
> really poor fit for LKMM.
We don't intend to directly expose C data structures to Rust code (outside the
kernel crate). Instead, we intend to provide wrappers that expose safe
interfaces even though the implementation may use unsafe blocks. So we expect
the vast majority of Rust code to just care about the Rust memory model.
We admittedly don't have a huge number of wrappers yet, but we do have enough to
implement most of Binder and so far it's been ok. We do intend to eventually
cover other classes of drivers that may unveil unforeseen difficulties, we'll
see.
If you have concerns that we might have overlooked, we'd be happy to hear about
them from you (or anyone else).
> HTML is not a valid documentation format. Heck, markdown itself is
> barely readable.
Are you stating [what you perceive as] a fact or just venting? If the former,
would you mind enlightening us with some evidence?
> It is really *really* hard to read. It has all sorts of weird things,
> like operators at the beginning after a line break:
>
> if (foo
> || bar)
>
> which is just wrong. And it suffers from CamelCase, which is just about
> the worst thing ever. Not even the C++ std libs have that (or had, back
> when I still did knew C++).
>
> I also see:
>
> if (foo) {
> ...
> }
>
> and
>
> if foo {
> }
>
> the latter, ofcourse, being complete rubbish.
There are advantages to adopting the preferred style of a language (when one
exists). We, of course, are not required to adopt it but I am of the opinion
that we should have good reasons to diverge if that's our choice in the end.
"Not having parentheses around the if-clause expression is complete rubbish"
doesn't sound like a good reason to me.
On Fri, Apr 16, 2021 at 03:22:16AM +0100, Wedson Almeida Filho wrote:
> > HTML is not a valid documentation format. Heck, markdown itself is
> > barely readable.
>
> Are you stating [what you perceive as] a fact or just venting? If the former,
> would you mind enlightening us with some evidence?
How about "not everyone uses a browser as a part of their workflow"?
I realize that it might sound ridiculous for folks who spent a while
around Mozilla, but it's really true and kernel community actually
has quite a few of such freaks. And as one of those freaks I can tell
you where exactly I would like you to go and what I would like you to do
with implicit suggestions to start a browser when I need to read some
in-tree documentation.
Linus might have different reasons, obviously.
[Copy LKMM people, Josh, Nick and Wedson]
On Thu, Apr 15, 2021 at 08:58:16PM +0200, Peter Zijlstra wrote:
> On Wed, Apr 14, 2021 at 08:45:51PM +0200, [email protected] wrote:
>
> > 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.
>
> And yet I see not a single mention of the Rust Memory Model and how it
> aligns (or not) with the LKMM. The C11 memory model for example is a
> really poor fit for LKMM.
>
I think Rust currently uses C11 memory model as per:
https://doc.rust-lang.org/nomicon/atomics.html
, also I guess another reason that they pick C11 memory model is because
LLVM has the support by default.
But I think the Rust Community still wants to have a good memory model,
and they are open to any kind of suggestion and input. I think we (LKMM
people) should really get involved, because the recent discussion on
RISC-V's atomics shows that if we didn't people might get a "broken"
design because they thought C11 memory model is good enough:
https://lore.kernel.org/lkml/YGyZPCxJYGOvqYZQ@boqun-archlinux/
And the benefits are mutual: a) Linux Kernel Memory Model (LKMM) is
defined by combining the requirements of developers and the behavior of
hardwares, it's pratical and can be a very good input for memory model
designing in Rust; b) Once Rust has a better memory model, the compiler
technologies whatever Rust compilers use to suppor the memory model can
be adopted to C compilers and we can get that part for free.
At least I personally is very intereted to help Rust on a complete and
pratical memory model ;-)
Josh, I think it's good if we can connect to the people working on Rust
memoryg model, I think the right person is Ralf Jung and the right place
is https://github.com/rust-lang/unsafe-code-guidelines, but you
cerntainly know better than me ;-) Or maybe we can use Rust-for-Linux or
linux-toolchains list to discuss.
[...]
> > - Boqun Feng is working hard on the different options for
> > threading abstractions and has reviewed most of the `sync` PRs.
>
> Boqun, I know you're familiar with LKMM, can you please talk about how
> Rust does things and how it interacts?
As Wedson said in the other email, currently there is no code requiring
synchronization between C side and Rust side, so we are currently fine.
But in the longer term, we need to teach Rust memory model about the
"design patterns" used in Linux kernel for parallel programming.
What I have been doing so far is reviewing patches which have memory
orderings in Rust-for-Linux project, try to make sure we don't include
memory ordering bugs for the beginning.
Regards,
Boqun
On Fri, Apr 16, 2021 at 04:25:34AM +0000, Al Viro wrote:
> > Are you stating [what you perceive as] a fact or just venting? If the former,
> > would you mind enlightening us with some evidence?
>
> How about "not everyone uses a browser as a part of their workflow"?
The documentation is available in markdown alongside the code. You don't need a
browser to see it. I, for one, use neovim and a rust LSP, so I can see the
documentation by pressing shift+k.
> I realize that it might sound ridiculous for folks who spent a while
> around Mozilla, but it's really true and kernel community actually
> has quite a few of such freaks.
I haven't spent any time around Mozilla myself (not that there's anything wrong
with it), so I can't really comment on this.
> And as one of those freaks I can tell
> you where exactly I would like you to go and what I would like you to do
> with implicit suggestions to start a browser when I need to read some
> in-tree documentation.
I could be mistaken but you seem angry. Perhaps it wouldn't be a bad idea to
read your own code of conduct, I don't think you need a browser for that either.
On Fri, Apr 16, 2021 at 06:02:33 +0100, Wedson Almeida Filho wrote:
> On Fri, Apr 16, 2021 at 04:25:34AM +0000, Al Viro wrote:
>
>>> Are you stating [what you perceive as] a fact or just venting? If the former,
>>> would you mind enlightening us with some evidence?
>>
>> How about "not everyone uses a browser as a part of their workflow"?
>
> The documentation is available in markdown alongside the code. You don't need a
> browser to see it. I, for one, use neovim and a rust LSP, so I can see the
> documentation by pressing shift+k.
>
>> I realize that it might sound ridiculous for folks who spent a while
>> around Mozilla, but it's really true and kernel community actually
>> has quite a few of such freaks.
>
> I haven't spent any time around Mozilla myself (not that there's anything wrong
> with it), so I can't really comment on this.
>
>> And as one of those freaks I can tell
>> you where exactly I would like you to go and what I would like you to do
>> with implicit suggestions to start a browser when I need to read some
>> in-tree documentation.
>
> I could be mistaken but you seem angry. Perhaps it wouldn't be a bad idea to
> read your own code of conduct, I don't think you need a browser for that either.
Haven't you folks ever head of lynx? Good old-fashioned command-line tool that
opens html files in a terminal window, supports following links within the file,
good stuff like that. I don't see how the dinosaurs^W traditional folks could
object to that!
-- Paul
On Thu, Apr 15, 2021 at 9:27 PM Boqun Feng <[email protected]> wrote:
>
> [Copy LKMM people, Josh, Nick and Wedson]
>
> On Thu, Apr 15, 2021 at 08:58:16PM +0200, Peter Zijlstra wrote:
> > On Wed, Apr 14, 2021 at 08:45:51PM +0200, [email protected] wrote:
> >
> > > 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.
> >
> > And yet I see not a single mention of the Rust Memory Model and how it
> > aligns (or not) with the LKMM. The C11 memory model for example is a
> > really poor fit for LKMM.
> >
>
> I think Rust currently uses C11 memory model as per:
>
> https://doc.rust-lang.org/nomicon/atomics.html
>
> , also I guess another reason that they pick C11 memory model is because
> LLVM has the support by default.
>
> But I think the Rust Community still wants to have a good memory model,
> and they are open to any kind of suggestion and input. I think we (LKMM
> people) should really get involved, because the recent discussion on
> RISC-V's atomics shows that if we didn't people might get a "broken"
> design because they thought C11 memory model is good enough:
>
> https://lore.kernel.org/lkml/YGyZPCxJYGOvqYZQ@boqun-archlinux/
>
> And the benefits are mutual: a) Linux Kernel Memory Model (LKMM) is
> defined by combining the requirements of developers and the behavior of
> hardwares, it's pratical and can be a very good input for memory model
> designing in Rust; b) Once Rust has a better memory model, the compiler
> technologies whatever Rust compilers use to suppor the memory model can
> be adopted to C compilers and we can get that part for free.
Yes, I agree; I think that's a very good approach. Avoiding the ISO
WG14 is interesting; at least the merits could be debated in the
public and not behind closed doors.
>
> At least I personally is very intereted to help Rust on a complete and
> pratical memory model ;-)
>
> Josh, I think it's good if we can connect to the people working on Rust
> memoryg model, I think the right person is Ralf Jung and the right place
> is https://github.com/rust-lang/unsafe-code-guidelines, but you
> cerntainly know better than me ;-) Or maybe we can use Rust-for-Linux or
> linux-toolchains list to discuss.
>
> [...]
> > > - Boqun Feng is working hard on the different options for
> > > threading abstractions and has reviewed most of the `sync` PRs.
> >
> > Boqun, I know you're familiar with LKMM, can you please talk about how
> > Rust does things and how it interacts?
>
> As Wedson said in the other email, currently there is no code requiring
> synchronization between C side and Rust side, so we are currently fine.
> But in the longer term, we need to teach Rust memory model about the
> "design patterns" used in Linux kernel for parallel programming.
>
> What I have been doing so far is reviewing patches which have memory
> orderings in Rust-for-Linux project, try to make sure we don't include
> memory ordering bugs for the beginning.
>
> Regards,
> Boqun
--
Thanks,
~Nick Desaulniers
On Fri, Apr 16, 2021 at 03:22:16AM +0100, Wedson Almeida Filho wrote:
> On Thu, Apr 15, 2021 at 08:58:16PM +0200, Peter Zijlstra wrote:
> > On Wed, Apr 14, 2021 at 08:45:51PM +0200, [email protected] wrote:
> >
> > > 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.
> >
> > And yet I see not a single mention of the Rust Memory Model and how it
> > aligns (or not) with the LKMM. The C11 memory model for example is a
> > really poor fit for LKMM.
>
> We don't intend to directly expose C data structures to Rust code (outside the
> kernel crate). Instead, we intend to provide wrappers that expose safe
> interfaces even though the implementation may use unsafe blocks. So we expect
> the vast majority of Rust code to just care about the Rust memory model.
>
> We admittedly don't have a huge number of wrappers yet, but we do have enough to
> implement most of Binder and so far it's been ok. We do intend to eventually
> cover other classes of drivers that may unveil unforeseen difficulties, we'll
> see.
>
> If you have concerns that we might have overlooked, we'd be happy to hear about
> them from you (or anyone else).
Well, the obvious example would be seqlocks. C11 can't do them. The not
sharing of data structures would avoid most of that, but will also cost
you in performance.
Simlar thing for RCU; C11 can't optimally do that; it needs to make
rcu_dereference() a load-acquire [something ARM64 has already done in C
because the compiler might be too clever by half when doing LTO :-(].
But it's the compiler needing the acquire semantics, not the computer,
which is just bloody wrong.
And there's more sharp corners to be had. But yes, if you're not
actually sharing anything; and taking the performance hit that comes
with that, you might get away with it.
> > HTML is not a valid documentation format. Heck, markdown itself is
> > barely readable.
>
> Are you stating [what you perceive as] a fact or just venting? If the former,
> would you mind enlightening us with some evidence?
I've yet to see a program that renders HTML (including all the cruft
often used in docs, which might include SVG graphics and whatnot) sanely
in ASCII. Lynx does not qualify, it's output is atrocious crap.
Yes, lynx lets you read HTML in ASCII, but at the cost of bleeding
eyeballs and missing content.
Nothing beats a sane ASCII document with possibly, where really needed
some ASCII art.
Sadly the whole kernel documentation project is moving away from that as
well, which just means I'm back to working on an undocumented codebase.
This rst crap they adopted is unreadable garbage.
> > It is really *really* hard to read. It has all sorts of weird things,
> > like operators at the beginning after a line break:
> >
> > if (foo
> > || bar)
> >
> > which is just wrong. And it suffers from CamelCase, which is just about
> > the worst thing ever. Not even the C++ std libs have that (or had, back
> > when I still did knew C++).
> >
> > I also see:
> >
> > if (foo) {
> > ...
> > }
> >
> > and
> >
> > if foo {
> > }
> >
> > the latter, ofcourse, being complete rubbish.
>
> There are advantages to adopting the preferred style of a language (when one
> exists). We, of course, are not required to adopt it but I am of the opinion
> that we should have good reasons to diverge if that's our choice in the end.
>
> "Not having parentheses around the if-clause expression is complete rubbish"
> doesn't sound like a good reason to me.
Of course it does; my internal lexer keeps screaming syntax error at me;
how am I going to understand code when I can't sanely read it?
The more you make it look like (Kernel) C, the easier it is for us C
people to actually read. My eyes have been reading C for almost 30 years
by now, they have a lexer built in the optical nerve; reading something
that looks vaguely like C but is definitely not C is an utterly painful
experience.
You're asking to join us, not the other way around. I'm fine in a world
without Rust.
On Fri, Apr 16, 2021 at 06:02:33AM +0100, Wedson Almeida Filho wrote:
> On Fri, Apr 16, 2021 at 04:25:34AM +0000, Al Viro wrote:
> > And as one of those freaks I can tell
> > you where exactly I would like you to go and what I would like you to do
> > with implicit suggestions to start a browser when I need to read some
> > in-tree documentation.
>
> I could be mistaken but you seem angry. Perhaps it wouldn't be a bad idea to
> read your own code of conduct, I don't think you need a browser for that either.
Welcome to LKML. CoC does not forbid human emotions just yet. Deal with
it.
On Thu, Apr 15, 2021 at 08:58:16PM +0200, Peter Zijlstra wrote:
>
> This; can we mercilessly break the .rs bits when refactoring? What
> happens the moment we cannot boot x86_64 without Rust crap on?
>
> We can ignore this as a future problem, but I think it's only fair to
> discuss now. I really don't care for that future, and IMO adding this
> Rust or any other second language is a fail.
I believe this is the most important question and we really need
a honest answer in advance: where exactly is this heading? At the moment
and with this experimental RFC, rust stuff can be optional and isolated
but it's obvious that the plan is very different: to have rust all
around the standard kernel tree. (If not, why is the example driver in
drivers/char/ ?)
And I don't see how the two languages might coexist peacefully without
rust toolchain being necessary for building any kernel useful in
practice and anyone seriously involved in kernel development having to
be proficient in both languages. Neither of these looks appealing to
me.
The dependency on rust toolchain was exactly what made me give up on
building Firefox from mercurial snapshots few years ago. To be able to
build them, one needed bleeding edge snapshots of rust toolchain which
my distribution couldn't possibly provide and building them myself
required way too much effort. This very discussion already revealed that
rust kernel code would provide similar experience. I also have my doubts
about the "optional" part; once there are some interesting drivers
written in rust, even if only in the form of out of tree modules, there
will be an enormous pressure on distributions, both community and
enterprise, to enable rust support. Once the major distributions do,
most others will have to follow. And from what I have seen, you need
rust toolchain for build even if you want to only load modules written
in rust.
The other problem is even worse. Once we have non-trivial amount of rust
code around the tree, even if it's "just some drivers", you cannot
completely ignore it. One example would be internal API changes. Today,
if I want to touch e.g. ethtool_ops, I need to adjust all in tree NIC
drivers providing the affected callback and adjust them. Usually most of
the patch is generated by spatch but manual tweaks are often needed here
and there. In the world of bilingual kernel with nontrivial number of
NIC drivers written in rust, I don't see how I could do that without
also being proficient in rust.
Also, how about maintainers and reviewers? What if someone comes with
a new module under foo/ or foo/bar/ and relevant maintainer does not
know rust or just not well enough to be able to review the submission
properly? Can they simply say "Sorry, I don't speak rust so no rust in
foo/bar/"? Leaf drivers are one thing, how about netfilter matches and
targets, TCP congestion control algorighms, qdiscs, filesystems, ...?
Having kernel tree divided into "rusty" and "rustfree" zones does not
sound like a great idea. But if we don't want that, do we expect every
subsystem maintainer and reviewer to learn rust to a level sufficient
for reviewing rust (kernel) code? Rust enthusiasts tell us they want to
open kernel development to more people but the result could as well be
exactly the opposite: it could restrict kernel development to people
proficient in _both_ languages.
As Peter said, it's not an imminent problem but as it's obvious this is
just the first step, we should have a clear idea what the plan is and
what we can and should expect.
Michal
On Fri, Apr 16, 2021 at 10:16:05AM +0200, Michal Kubecek wrote:
> And I don't see how the two languages might coexist peacefully without
> rust toolchain being necessary for building any kernel useful in
> practice and anyone seriously involved in kernel development having to
> be proficient in both languages.
Two languages ? No, one is specified and multiple-implemented, the other
one is the defined as what its only compiler understands at the moment,
so it's not a language, it's a compiler's reference manual at best. I'm
waiting for the day you're force to write things which look wrong with a
big comment around saying "it's not a bug it's a workaround for a bug in
the unique compiler, waiting to be retrofitted into the spec to solve the
problem for every user". Already seen for at least another "language"
implemented by a single vendor 22 years ago.
> Neither of these looks appealing to me.
>
> The dependency on rust toolchain was exactly what made me give up on
> building Firefox from mercurial snapshots few years ago. To be able to
> build them, one needed bleeding edge snapshots of rust toolchain which
> my distribution couldn't possibly provide and building them myself
> required way too much effort. This very discussion already revealed that
> rust kernel code would provide similar experience. I also have my doubts
> about the "optional" part; once there are some interesting drivers
> written in rust, even if only in the form of out of tree modules, there
> will be an enormous pressure on distributions, both community and
> enterprise, to enable rust support.
Yes this scarily looks like the usual "embrace and extend... and abandon
the corpse once it doesn't move anymore".
I've already faced situations where I couldn't compile a recent 5.x kernel
using my previous gcc-4.7 compiler and this really really really pissed me
off because I'd had it in a build farm for many architectures and I had to
give up. But I also know that updating to a newer version will take time,
will be durable and will be worth it for the long term (except for the fact
that gcc doubles the build time every two versions). But here having to use
*the* compiler of the day and being prepared to keep a collection of them
to work with different stable kernels, no!
Also, I'm a bit worried about long-term survival of the new
language-of-the-day-that-makes-you-look-cool-at-beer-events. I was once
told perl would replace C everywhere. Does someone use it outside of
checkpatch.pl anymore ? Then I was told that C was dead because PHP was
appearing everywhere. I've even seen (slow) log processors written with
it. Now PHP seems to only be a WAF-selling argument. Then Ruby was "safe"
and would rule them all. Safe as its tab[-1] which crashed the interpreter.
Anyone heard of it recently ? Then Python, whose 2.7 is still present on
a lot of systems because the forced transition to 3 broke tons of code.
Will there ever be a 4 after this sore experience ? Then JS, Rust, Go,
Zig and I don't know what. What I'm noting is that such languages appear,
serve a purpose well, have their moment of fame, last a decade and
disappear except at a few enthousiasts. C has been there for 50 years
and served as the basis of many newer languages so it's still well
understood. I'm sure about one thing, the C bugs we have today will be
fixable in 20 years. I'm not even sure the Rust code we'll merge today
will still be compilable in 10 years nor will support the relevant
architectures available by then, and probably this code will have to
be rewritten in C to become maintained again.
> The other problem is even worse. Once we have non-trivial amount of rust
> code around the tree, even if it's "just some drivers", you cannot
> completely ignore it. One example would be internal API changes. Today,
> if I want to touch e.g. ethtool_ops, I need to adjust all in tree NIC
> drivers providing the affected callback and adjust them. Usually most of
> the patch is generated by spatch but manual tweaks are often needed here
> and there. In the world of bilingual kernel with nontrivial number of
> NIC drivers written in rust, I don't see how I could do that without
> also being proficient in rust.
You'll simply change the code you're able to change and those in charge
of their driver will use your commit message as instruction to fix the
build on theirs. How do you want it to be otherwise ?
> Rust enthusiasts tell us they want to
> open kernel development to more people but the result could as well be
> exactly the opposite: it could restrict kernel development to people
> proficient in _both_ languages.
This has been my understanding from the very beginning. Language prophets
always want to conquier very visible targets as a gauge of their baby's
popularity.
> As Peter said, it's not an imminent problem but as it's obvious this is
> just the first step, we should have a clear idea what the plan is and
> what we can and should expect.
I think the experience could be... interesting. However, I do note that
I've read quite a few claims about better security yada yada due to a
stricter memory model. Except that I seem to have understood that a lot
of code will have to run in unsafe mode (which partially voids some of
the benefits), that we'll be at the mercy of the unique compiler's bugs,
and that in addition code auditing will be very hard and reviews of the
boundaries between the two languages almost inexistent. This is precisely
what will become the new playground of attackers, and I predict a
significant increase of vulnerabilities past this point. Time will tell,
hoping it's never too late to rollback if it gets crazy. As long as the
code remains readable, it could be rewritten in C to regain control...
> Michal
Willy
Hi,
On 14/04/2021 21:42, Miguel Ojeda wrote:
> On Wed, Apr 14, 2021 at 9:34 PM Linus Torvalds
> <[email protected]> wrote:
>>
>> Honestly, I'd like to see a real example. This is fine for testing,
>> but I'd like to see something a bit more real, and a bit less special
>> than the Android "binder" WIP that comes a few patches later.
>>
>> Would there be some kind of real driver or something that people could
>> use as a example of a real piece of code that actually does something
>> meaningful?
>
> Yeah, we are planning to write a couple of drivers that talk to actual
> hardware. Not sure which ones we will do, but we will have them
> written.
I’m curious what’s the procedure and approach in general to adding new
APIs? I was thinking of trying to port my driver but it needs USB HID
and either LEDs or hwrandom (depending on which part I start porting
first), so obviously it’s not doable right now, but I’m thinking about
maybe helping with at least some of those.
--
Cheers,
Andrej
On Thu, Apr 15, 2021 at 8:03 PM Nick Desaulniers
<[email protected]> wrote:
>
> Until then, I don't see why we need to permit developers to express
> such flexibility for just the Rust code, or have it differ from the
> intent of the C code. Does it make sense to set RUST_OPT_LEVEL_3 and
> CC_OPTIMIZE_FOR_SIZE? I doubt it. That doesn't seem like a development
> feature, but a mistake. YAGNI. Instead developers should clarify
> what they care about in terms of high level intent; if someone wants
> to micromanage optimization level flags in their forks they don't need
> a Kconfig to do it (they're either going to hack KBUILD_CFLAGS,
> CFLAGS_*.o, or KCFLAGS), and there's probably better mechanisms for
> fine-tooth precision of optimizing actually hot code or not via PGO
> and AutoFDO.
I completely agree when we are talking about higher level optimization
levels. From a user perspective, it does not make much sense to want
slightly different optimizations levels or different size/performance
trade-offs between C and Rust. However, I am thinking from the
debugging side, i.e. mostly low or no optimization; rather than about
micromanaging optimizations for performance.
For instance, last year I used `RUST_OPT_LEVEL_0/1` to quickly rule
out optimizer/codegen/etc. bugs on the Rust side when we had some
memory corruption over Rust data
(https://github.com/Rust-for-Linux/linux/pull/28), which is important
when dealing with compiler nightly versions. It was also nice to be
able to easily follow along when stepping, too.
Having said that, I agree that in those cases one can simply tweak the
flags manually -- so that's why I said it is fine dropping the the
`Kconfig` options. There might be some advantages of having them, such
as making developers aware that those builds should work, to keep them
tested/working, etc.; but we can do that manually too in the CI/docs
too.
Cheers,
Miguel
On Fri, Apr 16, 2021 at 09:07:10AM -0400, Sven Van Asbroeck wrote:
> On Thu, Apr 15, 2021 at 3:11 AM Greg Kroah-Hartman
> <[email protected]> wrote:
> >
> > I've been talking with the developers here about doing a "real" driver
>
> Would it be beneficial if the device h/w targeted by the "real" Rust
> driver has QEMU emulation? Perhaps in addition to physical silicon.
Again, as was reported, a nvme driver falls into that category today, no
need to look very far.
thanks,
greg k-h
On Wed, Apr 14, 2021 at 08:45:51PM +0200, [email protected] wrote:
> - Featureful language: sum types, pattern matching, generics,
> RAII, lifetimes, shared & exclusive references, modules &
> visibility, powerful hygienic and procedural macros...
IMO RAII is over-valued, but just in case you care, the below seems to
work just fine. No fancy new language needed, works today. Similarly you
can create refcount_t guards, or with a little more work full blown
smart_ptr crud.
---
diff --git a/include/linux/mutex.h b/include/linux/mutex.h
index e19323521f9c..f03a72dd8cea 100644
--- a/include/linux/mutex.h
+++ b/include/linux/mutex.h
@@ -197,4 +197,22 @@ extern void mutex_unlock(struct mutex *lock);
extern int atomic_dec_and_mutex_lock(atomic_t *cnt, struct mutex *lock);
+struct mutex_guard {
+ struct mutex *mutex;
+};
+
+static inline struct mutex_guard mutex_guard_lock(struct mutex *mutex)
+{
+ mutex_lock(mutex);
+ return (struct mutex_guard){ .mutex = mutex, };
+}
+
+static inline void mutex_guard_unlock(struct mutex_guard *guard)
+{
+ mutex_unlock(guard->mutex);
+}
+
+#define DEFINE_MUTEX_GUARD(name, lock) \
+ struct mutex_guard __attribute__((__cleanup__(mutex_guard_unlock))) name = mutex_guard_lock(lock)
+
#endif /* __LINUX_MUTEX_H */
diff --git a/kernel/events/core.c b/kernel/events/core.c
index 8ee3249de2f0..603d197a83b8 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -5715,16 +5715,15 @@ static long perf_compat_ioctl(struct file *file, unsigned int cmd,
int perf_event_task_enable(void)
{
+ DEFINE_MUTEX_GUARD(event_mutex, ¤t->perf_event_mutex);
struct perf_event_context *ctx;
struct perf_event *event;
- mutex_lock(¤t->perf_event_mutex);
list_for_each_entry(event, ¤t->perf_event_list, owner_entry) {
ctx = perf_event_ctx_lock(event);
perf_event_for_each_child(event, _perf_event_enable);
perf_event_ctx_unlock(event, ctx);
}
- mutex_unlock(¤t->perf_event_mutex);
return 0;
}
On Fri, Apr 16, 2021 at 1:24 PM Peter Zijlstra <[email protected]> wrote:
>
> IMO RAII is over-valued, but just in case you care, the below seems to
> work just fine. No fancy new language needed, works today. Similarly you
> can create refcount_t guards, or with a little more work full blown
> smart_ptr crud.
Please note that even smart pointers (as in C++'s `std::unique_ptr`
etc.) do not guarantee memory safety. Yes, they help a lot writing
sound code (in particular exception-safe C++ code), but they do not
bring the same guarantees.
That's why using C language extensions (the existing ones, that is) to
recreate RAII/guards, smart pointers, etc. would only bring you to a
point closer to C++, but not to Rust.
Cheers,
Miguel
On Fri, Apr 16, 2021 at 02:07:49PM +0100, Wedson Almeida Filho wrote:
> There is nothing in C forcing developers to actually use DEFINE_MUTEX_GUARD. So
> someone may simply forget (or not know that they need) to lock
> current->perf_event_mutex and directly access some field protected by it. This
> is unlikely to happen when one first writes the code, but over time as different
> people modify the code and invariants change, it is possible for this to happen.
>
> In Rust, this isn't possible: the data protected by a lock is only accessible
> when the lock is locked. So developers cannot accidentally make mistakes of this
> kind. And since the enforcement happens at compile time, there is no runtime
> cost.
Well, we could do that in C too.
struct unlocked_inode {
spinlock_t i_lock;
};
struct locked_inode {
spinlock_t i_lock;
unsigned short i_bytes;
blkcnt_t i_blocks;
};
struct locked_inode *lock_inode(struct unlocked_inode *inode)
{
spin_lock(&inode->i_lock);
return (struct locked_inode *)inode;
}
There's a combinatoric explosion when you have multiple locks in a data
structure that protect different things in it (and things in a data
structure that are protected by locks outside that data structure),
but I'm not sufficiently familiar with Rust to know if/how it solves
that problem.
Anyway, my point is that if we believe this is a sufficiently useful
feature to have, and we're willing to churn the kernel, it's less churn
to do this than it is to rewrite in Rust.
> Another scenario: suppose within perf_event_task_enable you need to call a
> function that requires the mutex to be locked and that will unlock it for you on
> error (or unconditionally, doesn't matter). How would you do that in C? In Rust,
> there is a clean idiomatic way of transferring ownership of a guard (or any
> other object) such that the previous owner cannot continue to use it after
> ownership is transferred. Again, this is enforced at compile time. I'm happy to
> provide a small example if that would help.
I think we could do that too with an __attribute__((free)). It isn't,
of course, actually freeing the pointer to the locked_inode, but it will
make the compiler think the pointer is invalid after the function returns.
(hm, looks like gcc doesn't actually have __attribute__((free)) yet.
that's unfortunate. there's a potential solution in gcc-11 that might
do what we need)
On Fri, Apr 16, 2021 at 4:19 PM Peter Zijlstra <[email protected]> wrote:
>
> Does this also not prohibit constructs where modification must be done
> while holding two locks, but reading can be done while holding either
> lock?
Yeah, this came up in some discussions we had. There are some locking
patterns that we need to think about how to model best within Rust's
type system.
But even if some patterns cannot be made safe, that is fine and does
not diminish the advantages everywhere else.
> I would much rather have a language extention where we can associate
> custom assertions with variable access, sorta like a sanitizer:
>
> static inline void assert_foo_bar(struct foo *f)
> {
> lockdep_assert_held(&f->lock);
> }
>
> struct foo {
> spinlock_t lock;
> int bar __assert__(assert_foo_bar);
> };
>
> Such things can be optional and only enabled for debug builds on new
> compilers.
More sanitizers and ways to check "unsafe" code is sound are always
welcome -- not just for C, also for Rust `unsafe` code (e.g. Miri).
However, the main advantage of Rust for us is its safe subset (which
handles quite a lot of patterns, thanks to the lifetime tracking /
borrow checker).
Of course, we could propose something similar for C -- in fact, there
was a recent discussion around this in the C committee triggered by my
n2659 "Safety attributes for C" paper. However, achieving that would
require a lot of work, time, new syntax, etc. It is not something that
is in the radar just yet.
Similarly, if some compiler ends up implementing an extension that
actually realizes the same guarantees as Rust, we would likely end up
wrapping everything with macros like in the guards example you
mentioned, and even then we would not have got the rest of the
advantages that Rust brings to the table.
> C does indeed not have the concept of ownership, unlike modern C++ I
> think. But I would much rather see a C language extention for that than
> go Rust.
Many "resource-like" C++ types model ownership, yes; e.g.
`std::unique_ptr` for memory, as well as a myriad of ones in different
projects for different kinds of resources, plus generic ones like the
proposed P0052. However, they do not enforce their usage is correct.
Cheers,
Miguel
On Thu, Apr 15, 2021 at 3:11 AM Greg Kroah-Hartman
<[email protected]> wrote:
>
> I've been talking with the developers here about doing a "real" driver
Would it be beneficial if the device h/w targeted by the "real" Rust
driver has QEMU emulation? Perhaps in addition to physical silicon.
If developers don't need access to physical hardware, they might be
more likely to get involved with Linux/Rust.
Also, I'm guessing QEMU allows us to embed the emulated device into a
variety of board architectures: x86_64, arm64, powerpc, etc. which
could be useful in smoking out architecture-specific Rust driver
issues.
Sven
On Fri, Apr 16, 2021 at 01:24:23PM +0200, Peter Zijlstra wrote:
> On Wed, Apr 14, 2021 at 08:45:51PM +0200, [email protected] wrote:
> > - Featureful language: sum types, pattern matching, generics,
> > RAII, lifetimes, shared & exclusive references, modules &
> > visibility, powerful hygienic and procedural macros...
>
> IMO RAII is over-valued, but just in case you care, the below seems to
> work just fine. No fancy new language needed, works today. Similarly you
> can create refcount_t guards, or with a little more work full blown
> smart_ptr crud.
Peter, we do care, thank you for posting this. It's a great example for us to
discuss some of the minutiae of what we think Rust brings to the table in
addition to what's already possible in C.
>
> ---
> diff --git a/include/linux/mutex.h b/include/linux/mutex.h
> index e19323521f9c..f03a72dd8cea 100644
> --- a/include/linux/mutex.h
> +++ b/include/linux/mutex.h
> @@ -197,4 +197,22 @@ extern void mutex_unlock(struct mutex *lock);
>
> extern int atomic_dec_and_mutex_lock(atomic_t *cnt, struct mutex *lock);
>
> +struct mutex_guard {
> + struct mutex *mutex;
> +};
> +
> +static inline struct mutex_guard mutex_guard_lock(struct mutex *mutex)
> +{
> + mutex_lock(mutex);
> + return (struct mutex_guard){ .mutex = mutex, };
> +}
> +
> +static inline void mutex_guard_unlock(struct mutex_guard *guard)
> +{
> + mutex_unlock(guard->mutex);
> +}
> +
> +#define DEFINE_MUTEX_GUARD(name, lock) \
> + struct mutex_guard __attribute__((__cleanup__(mutex_guard_unlock))) name = mutex_guard_lock(lock)
> +
> #endif /* __LINUX_MUTEX_H */
> diff --git a/kernel/events/core.c b/kernel/events/core.c
> index 8ee3249de2f0..603d197a83b8 100644
> --- a/kernel/events/core.c
> +++ b/kernel/events/core.c
> @@ -5715,16 +5715,15 @@ static long perf_compat_ioctl(struct file *file, unsigned int cmd,
>
> int perf_event_task_enable(void)
> {
> + DEFINE_MUTEX_GUARD(event_mutex, ¤t->perf_event_mutex);
There is nothing in C forcing developers to actually use DEFINE_MUTEX_GUARD. So
someone may simply forget (or not know that they need) to lock
current->perf_event_mutex and directly access some field protected by it. This
is unlikely to happen when one first writes the code, but over time as different
people modify the code and invariants change, it is possible for this to happen.
In Rust, this isn't possible: the data protected by a lock is only accessible
when the lock is locked. So developers cannot accidentally make mistakes of this
kind. And since the enforcement happens at compile time, there is no runtime
cost.
This, we believe, is fundamental to the discussion: we agree that many of these
idioms can be implemented in C (albeit in this case with a compiler extension),
but their use is optional, people can (and do) still make mistakes that lead to
vulnerabilities; Rust disallows classes of mistakes by construction.
Another scenario: suppose within perf_event_task_enable you need to call a
function that requires the mutex to be locked and that will unlock it for you on
error (or unconditionally, doesn't matter). How would you do that in C? In Rust,
there is a clean idiomatic way of transferring ownership of a guard (or any
other object) such that the previous owner cannot continue to use it after
ownership is transferred. Again, this is enforced at compile time. I'm happy to
provide a small example if that would help.
Again, thanks for bringing this up. And please keep your concerns and feedback
coming, we very much want to have these discussions and try to improve what we
have based on feedback from the community.
> struct perf_event_context *ctx;
> struct perf_event *event;
>
> - mutex_lock(¤t->perf_event_mutex);
> list_for_each_entry(event, ¤t->perf_event_list, owner_entry) {
> ctx = perf_event_ctx_lock(event);
> perf_event_for_each_child(event, _perf_event_enable);
> perf_event_ctx_unlock(event, ctx);
> }
> - mutex_unlock(¤t->perf_event_mutex);
>
> return 0;
> }
On Wed, Apr 14, 2021 at 08:45:55PM +0200, [email protected] wrote:
> diff --git a/scripts/Makefile.build b/scripts/Makefile.build
> index 1b6094a13034..3665c49c4dcf 100644
> --- a/scripts/Makefile.build
> +++ b/scripts/Makefile.build
> @@ -26,6 +26,7 @@ EXTRA_CPPFLAGS :=
> EXTRA_LDFLAGS :=
> asflags-y :=
> ccflags-y :=
> +rustcflags-y :=
> cppflags-y :=
> ldflags-y :=
>
> @@ -287,6 +288,24 @@ quiet_cmd_cc_lst_c = MKLST $@
> $(obj)/%.lst: $(src)/%.c FORCE
> $(call if_changed_dep,cc_lst_c)
>
> +# Compile Rust sources (.rs)
> +# ---------------------------------------------------------------------------
> +
> +rustc_cross_flags := --target=$(srctree)/arch/$(SRCARCH)/rust/target.json
> +
> +quiet_cmd_rustc_o_rs = $(RUSTC_OR_CLIPPY_QUIET) $(quiet_modtag) $@
> + cmd_rustc_o_rs = \
> + RUST_MODFILE=$(modfile) \
> + $(RUSTC_OR_CLIPPY) $(rustc_flags) $(rustc_cross_flags) \
> + --extern alloc --extern kernel \
> + --crate-type rlib --out-dir $(obj) -L $(objtree)/rust/ \
> + --crate-name $(patsubst %.o,%,$(notdir $@)) $<; \
> + mv $(obj)/$(subst .o,,$(notdir $@)).d $(depfile); \
> + sed -i '/^\#/d' $(depfile)
> +
> +$(obj)/%.o: $(src)/%.rs FORCE
> + $(call if_changed_dep,rustc_o_rs)
> +
> # Compile assembler sources (.S)
> # ---------------------------------------------------------------------------
>
So if I read all this right, rust compiles to .o and, like any other .o
file is then fed into objtool (for x86_64). Did you have any problems
with objtool? Does it generate correct ORC unwind information?
AFAICT rust has try/throw/catch exception handling (like
C++/Java/others) which is typically implemented with stack unwinding of
its own.
Does all that interact sanely?
On Fri, Apr 16, 2021 at 02:07:49PM +0100, Wedson Almeida Filho wrote:
> On Fri, Apr 16, 2021 at 01:24:23PM +0200, Peter Zijlstra wrote:
> > int perf_event_task_enable(void)
> > {
> > + DEFINE_MUTEX_GUARD(event_mutex, ¤t->perf_event_mutex);
>
> There is nothing in C forcing developers to actually use DEFINE_MUTEX_GUARD. So
> someone may simply forget (or not know that they need) to lock
> current->perf_event_mutex and directly access some field protected by it. This
> is unlikely to happen when one first writes the code, but over time as different
> people modify the code and invariants change, it is possible for this to happen.
>
> In Rust, this isn't possible: the data protected by a lock is only accessible
> when the lock is locked. So developers cannot accidentally make mistakes of this
> kind. And since the enforcement happens at compile time, there is no runtime
> cost.
>
> This, we believe, is fundamental to the discussion: we agree that many of these
> idioms can be implemented in C (albeit in this case with a compiler extension),
> but their use is optional, people can (and do) still make mistakes that lead to
> vulnerabilities; Rust disallows classes of mistakes by construction.
Does this also not prohibit constructs where modification must be done
while holding two locks, but reading can be done while holding either
lock?
That's a semi common scheme in the kernel, but not something that's
expressible by, for example, the Java sync keyword.
It also very much doesn't work for RCU, where modification must be done
under a lock, but access is done essentially lockless.
I would much rather have a language extention where we can associate
custom assertions with variable access, sorta like a sanitizer:
static inline void assert_foo_bar(struct foo *f)
{
lockdep_assert_held(&f->lock);
}
struct foo {
spinlock_t lock;
int bar __assert__(assert_foo_bar);
};
Such things can be optional and only enabled for debug builds on new
compilers.
> Another scenario: suppose within perf_event_task_enable you need to call a
> function that requires the mutex to be locked and that will unlock it for you on
> error (or unconditionally, doesn't matter). How would you do that in C? In Rust,
> there is a clean idiomatic way of transferring ownership of a guard (or any
> other object) such that the previous owner cannot continue to use it after
> ownership is transferred. Again, this is enforced at compile time. I'm happy to
> provide a small example if that would help.
C does indeed not have the concept of ownership, unlike modern C++ I
think. But I would much rather see a C language extention for that than
go Rust.
This would mean a far more agressive push for newer C compilers than
we've ever done before, but at least it would all still be a single
language. Convertion to the new stuff can be done gradually and where
it makes sense and new extentions can be evaluated on performance impact
etc.
On Fri, Apr 16, 2021 at 04:19:07PM +0200, Peter Zijlstra wrote:
> Does this also not prohibit constructs where modification must be done
> while holding two locks, but reading can be done while holding either
> lock?
I don't believe it does. Remember that we have full control of the abstractions,
so we can (and will when the need arises) build an abstraction that provides the
functionality you describe. For the read path, we can have functions that return
a read-only guard (which is the gateway to the data in Rust) when locking either
of the locks, or when showing evidence that either lock is already locked (i.e.,
by temporarily transferring ownership of another guard). Note that this is
another area where Rust offers advantages: read-only guards (in C, if you take a
read lock, nothing prevents you from making changes to fields you should only be
allowed to read); and the ability to take temporary ownership, giving it back
even within the same function.
Similarly, to access a mutable guard, you'd have to show evidence that both
locks are held.
> That's a semi common scheme in the kernel, but not something that's
> expressible by, for example, the Java sync keyword.
>
> It also very much doesn't work for RCU, where modification must be done
> under a lock, but access is done essentially lockless.
Why not? RCU is a lock -- it may have zero cost in most (all?) architectures on
the read path, but it is a lock. We can model access to variables/fields
protected by it just like any other lock, with the implementation of lock/unlock
optimizing to no-ops on the read path where possible.
In fact, this is also an advantage of Rust. It would *force* developers to
lock/unlock the RCU lock before they can access the protected data.
> I would much rather have a language extention where we can associate
> custom assertions with variable access, sorta like a sanitizer:
>
> static inline void assert_foo_bar(struct foo *f)
> {
> lockdep_assert_held(&f->lock);
> }
>
> struct foo {
> spinlock_t lock;
> int bar __assert__(assert_foo_bar);
> };
>
> Such things can be optional and only enabled for debug builds on new
> compilers.
These would be great, but would still fall short of the compile-time guaranteed
safety that Rust offers in these cases.
> C does indeed not have the concept of ownership, unlike modern C++ I
> think. But I would much rather see a C language extention for that than
> go Rust.
>
> This would mean a far more agressive push for newer C compilers than
> we've ever done before, but at least it would all still be a single
> language. Convertion to the new stuff can be done gradually and where
> it makes sense and new extentions can be evaluated on performance impact
> etc.
I encourage you to pursue this. We'd all benefit from better C. I'd be happy to
review and provide feedback on proposed extensions that are deemed
equivalent/better than what Rust offers.
My background is also in C. I'm no Rust fanboy, I'm just taking what I think is
a pragmatic view of the available options.
On Fri, Apr 16, 2021 at 05:04:41PM +0200, Miguel Ojeda wrote:
> Of course, we could propose something similar for C -- in fact, there
> was a recent discussion around this in the C committee triggered by my
> n2659 "Safety attributes for C" paper.
http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2659.htm
That's just not making any damn sense what so ever. That seems to be
about sprinkling abort() all over the place, which is just total
rubbish.
On Fri, Apr 16, 2021 at 02:07:49PM +0100, Wedson Almeida Filho wrote:
> On Fri, Apr 16, 2021 at 01:24:23PM +0200, Peter Zijlstra wrote:
> > On Wed, Apr 14, 2021 at 08:45:51PM +0200, [email protected] wrote:
> > > - Featureful language: sum types, pattern matching, generics,
> > > RAII, lifetimes, shared & exclusive references, modules &
> > > visibility, powerful hygienic and procedural macros...
> >
> > IMO RAII is over-valued, but just in case you care, the below seems to
> > work just fine. No fancy new language needed, works today. Similarly you
> > can create refcount_t guards, or with a little more work full blown
> > smart_ptr crud.
>
> Peter, we do care, thank you for posting this. It's a great example for us to
> discuss some of the minutiae of what we think Rust brings to the table in
> addition to what's already possible in C.
Another fairly common use case is a lockless, racy test of a
particular field, as an optimization before we take the lock before we
test it for realsies. In this particular case, we can't allocate
memory while holding a spinlock, so we check to see without taking the
spinlock to see whether we should allocate memory (which is expensive,
and unnecessasry most of the time):
alloc_transaction:
/*
* This check is racy but it is just an optimization of allocating new
* transaction early if there are high chances we'll need it. If we
* guess wrong, we'll retry or free the unused transaction.
*/
if (!data_race(journal->j_running_transaction)) {
/*
* If __GFP_FS is not present, then we may be being called from
* inside the fs writeback layer, so we MUST NOT fail.
*/
if ((gfp_mask & __GFP_FS) == 0)
gfp_mask |= __GFP_NOFAIL;
new_transaction = kmem_cache_zalloc(transaction_cache,
gfp_mask);
if (!new_transaction)
return -ENOMEM;
}
...
repeat:
read_lock(&journal->j_state_lock);
...
if (!journal->j_running_transaction) {
read_unlock(&journal->j_state_lock);
if (!new_transaction)
goto alloc_transaction;
write_lock(&journal->j_state_lock);
if (!journal->j_running_transaction &&
(handle->h_reserved || !journal->j_barrier_count)) {
jbd2_get_transaction(journal, new_transaction);
new_transaction = NULL;
}
write_unlock(&journal->j_state_lock);
goto repeat;
}
...
The other thing that I'll note is that diferent elements in thet
journal structure are protected by different spinlocks; we don't have
a global lock protecting the entire structure, which is critical for
scalability on systems with a large number of CPU's with a lot of
threads all wanting to perform file system operations.
So having a guard structure which can't be bypassed on the entire
structure would result in a pretty massive performance penalty for the
ext4 file system. I know that initially the use of Rust in the kernel
is targetted for less performance critical modules, such as device
drivers, but I thought I would mention some of the advantages of more
advanced locking techniques.
Cheers,
- Ted
On Fri, Apr 16, 2021 at 11:58:05AM -0400, Theodore Ts'o wrote:
> Another fairly common use case is a lockless, racy test of a
> particular field, as an optimization before we take the lock before we
> test it for realsies. In this particular case, we can't allocate
> memory while holding a spinlock, so we check to see without taking the
> spinlock to see whether we should allocate memory (which is expensive,
> and unnecessasry most of the time):
I'd have to think more about whether we can build generic safe abstraction for
this pattern. But even if we can't, we always have the unsafe escape hatch: we
can grant unsafe unlocked access to the data; in such cases, the onus is on the
caller to convince themselves that what they're doing is safe, i.e., the
compiler won't offer compile-time guarantees.
However, and I think this is also an advantage of Rust, such unsafe accesses
*must* be explicitly tagged as such (and this is enforced at compile-time), so
you'd do something like:
// SAFETY: The below is safe because...
if !unsafe{ journal.access_unlocked().j_running_transaction } {
}
And the idea is that unsafe blocks like the one above will require additional
scrutiny from reviewers. So this also makes the lives of maintainers/reviewers
easier as they'd know that these sections need more attention.
> The other thing that I'll note is that diferent elements in thet
> journal structure are protected by different spinlocks; we don't have
> a global lock protecting the entire structure, which is critical for
> scalability on systems with a large number of CPU's with a lot of
> threads all wanting to perform file system operations.
Yes, this is fine, the way to do it in Rust would be to break your struct up
into something like (we have something like this in Binder):
struct X {
[...]
}
struct Y {
[...]
}
struct Z {
x: SpinLock<X>,
y: SpinLock<Y>,
a: u32,
[...]
}
> So having a guard structure which can't be bypassed on the entire
> structure would result in a pretty massive performance penalty for the
> ext4 file system. I know that initially the use of Rust in the kernel
> is targetted for less performance critical modules, such as device
> drivers, but I thought I would mention some of the advantages of more
> advanced locking techniques.
Thanks for this. Yes, while the initial target is drivers, we do want to provide
a general framework that could potentially be used anywhere.
Please let us know if you find other patterns that seem problematic.
Cheers,
-Wedson
On Fri, Apr 16, 2021 at 5:43 PM Peter Zijlstra <[email protected]> wrote:
>
> http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2659.htm
>
> That's just not making any damn sense what so ever. That seems to be
> about sprinkling abort() all over the place, which is just total
> rubbish.
No, it is not about that. It is semantically a no-op: N2659 is not
about a safe subset of C -- it just triggered discussions about it in
the reflector.
The point is that we think it is important to improve C for the kernel
too. We are not working on bringing Rust to the kernel "just because
we want Rust", but because we believe it has a sizable amount of
advantages that outweigh the costs.
Cheers,
Miguel
On Fri, Apr 16, 2021 at 04:33:51PM +0100, Wedson Almeida Filho wrote:
> On Fri, Apr 16, 2021 at 04:19:07PM +0200, Peter Zijlstra wrote:
> > Does this also not prohibit constructs where modification must be done
> > while holding two locks, but reading can be done while holding either
> > lock?
>
> I don't believe it does. Remember that we have full control of the abstractions,
> so we can (and will when the need arises) build an abstraction that provides the
> functionality you describe. For the read path, we can have functions that return
> a read-only guard (which is the gateway to the data in Rust) when locking either
> of the locks, or when showing evidence that either lock is already locked (i.e.,
> by temporarily transferring ownership of another guard).
But will this remain syntactically readable/writable by mere humans ?
I mean, I keep extremely bad memories of having tried to write a loop
oconcatenating at most N times a string to another one, where N was a
number provided on the command line, with the compiler shouting at me
all the time until I blindly copy-pasted random pieces of unreadable
code from the net with a horribly complicated syntax that still
resulted in the impossibility for me to check for memory allocation
before failing. So I'm wondering how complicated that can become after
adding all sort of artificial protections on top of this :-/
> Note that this is
> another area where Rust offers advantages: read-only guards (in C, if you take a
> read lock, nothing prevents you from making changes to fields you should only be
> allowed to read);
But I'm happily doing that when I know what I'm doing. What you call a
read lock usually is in fact a shared lock as opposed to an exclusive
lock (generally used for writes). For me it's perfectly valid to perform
atomic writes under a read lock instead of forcing everyone to wait by
taking a write lock. You may for example take a read lock on a structure
to make sure that a field you're accessing in it points to stable memory
that is only modified under the write lock, but the pointer itself is
atomically accessed and swapped under the read lock.
> In fact, this is also an advantage of Rust. It would *force* developers to
> lock/unlock the RCU lock before they can access the protected data.
I'm really afraid by languages which force developers to do this or that.
Many bugs in C come from casts because developers know their use case
better than the compiler's developers, and result in lack of warnings
when the code evolves, leaving pending bugs behind. What is important
in my opinion is to let developers express what they want and report
suspicious constructs, not to force them to dirtily work around rules
that conflict with their use case :-/
Willy
On Fri, Apr 16, 2021 at 6:38 AM Peter Zijlstra <[email protected]> wrote:
>
> AFAICT rust has try/throw/catch exception handling (like
> C++/Java/others) which is typically implemented with stack unwinding of
> its own.
I was assuming that the kernel side would never do that.
There's some kind of "catch_unwind()" thing that catches a Rust
"panic!" thing, but I think it's basically useless for the kernel.
Typical Rust error handling should match the regular kernel
IS_ERR/ERR_PTR/PTR_ERR model fairly well, although the syntax is
fairly different (and it's not limited to pointers).
Linus
On Fri, Apr 16, 2021 at 6:14 PM Willy Tarreau <[email protected]> wrote:
>
> I'm really afraid by languages which force developers to do this or that.
> Many bugs in C come from casts because developers know their use case
> better than the compiler's developers, and result in lack of warnings
> when the code evolves, leaving pending bugs behind. What is important
> in my opinion is to let developers express what they want and report
> suspicious constructs, not to force them to dirtily work around rules
> that conflict with their use case :-/
I understand your concerns. The idea is that by restricting some
patterns (in the safe subset), you gain the ability to guarantee the
absence of UB (as long as the `unsafe` code is sound).
But please note that the `unsafe` side is still there, and you can
reach out for it when needed.
Thus, if you find yourself in a situation where the safe abstractions
are not enough for what you need to convey, you have two options:
ideally, you think about how to model that pattern in a way that can
be exposed as a safe API so that others can reuse it. And if that is
not possible, you reach out for `unsafe` yourself.
Even in those cases where there is no other way around `unsafe`, note
that you still have gained something very important: now you have made
it explicit in the code that this is needed, and you will have written
a `SAFETY` annotation that tells others why your usage is sound (i.e.
why it cannot trigger UB).
And by having the compiler enforce this safe-unsafe split, you can
review safe code without having to constantly worry about UB; and be
extra alert when dealing with `unsafe` blocks.
Of course, UB is only a subset of errors, but it is a major one, and
particularly critical for privileged code.
Cheers,
Miguel
Hi Miguel,
On Fri, Apr 16, 2021 at 07:10:17PM +0200, Miguel Ojeda wrote:
> And by having the compiler enforce this safe-unsafe split, you can
> review safe code without having to constantly worry about UB; and be
> extra alert when dealing with `unsafe` blocks.
I do appreciate this safe/unsafe split and a few other things I've seen
in the language. The equivalent I'm using in C is stronger typing and
"const" modifiers wherever possible. Of course it's much more limited,
it's just to explain that I do value this. I just feel like "unsafe"
is the universal response to any question "how would I do this" while
at the same time "safe" is the best selling argument for the language.
As such, I strongly doubt about the real benefits once facing reality
with everything marked unsafe. Except that it will be easier to blame
the person having written the unsafe one-liner instead of writing 60
cryptic lines doing the functional equivalent using some lesser known
extensions :-/
> Of course, UB is only a subset of errors, but it is a major one, and
> particularly critical for privileged code.
Not in my experience. I do create bugs that very seldomly stem from UB,
like any of us probably. But the vast majority of my bugs are caused by
stupid logic errors. When you invert an error check somewhere because
the function name looks like a boolean but its result works the other
way around, you can pass 10 times over it without noticing, and the
compiler will not help. And these ones are due to the human brain not
being that powerful in front of a computer, and whatever language will
not change this. Or worse, if it's harder to express what I want, I
will write more bugs. It happened to me quite a few times already
trying to work around absurd gcc warnings.
Based on the comments in this thread and the responses often being
around "we'll try to get this done" or "we'll bring the issue to the
compiler team", combined with the difficulty to keep control over
resources usage, I'm really not convinced at all it's suited for
low-level development. I understand the interest of the experiment
to help the language evolve into that direction, but I fear that
the kernel will soon be as bloated and insecure as a browser, and
that's really not to please me.
Cheers,
Willy
On Fri, Apr 16, 2021 at 3:38 PM Peter Zijlstra <[email protected]> wrote:
>
> So if I read all this right, rust compiles to .o and, like any other .o
> file is then fed into objtool (for x86_64). Did you have any problems
> with objtool? Does it generate correct ORC unwind information?
I opened an issue a while ago to take a closer look at the ORC
unwinder etc., so it is in my radar (thanks for raising it up,
nevertheless!).
Currently, causing a panic in a nested non-inlined function (f -> g ->
h) in one of the samples with the ORC unwinder enabled gives me
something like:
[ 0.903456] rust_begin_unwind+0x9/0x10
[ 0.903456] ? _RNvNtCsbDqzXfLQacH_4core9panicking9panic_fmt+0x29/0x30
[ 0.903456] ? _RNvNtCsbDqzXfLQacH_4core9panicking5panic+0x44/0x50
[ 0.903456] ? _RNvCsbDqzXfLQacH_12rust_minimal1h+0x1c/0x20
[ 0.903456] ? _RNvCsbDqzXfLQacH_12rust_minimal1g+0x9/0x10
[ 0.903456] ? _RNvCsbDqzXfLQacH_12rust_minimal1f+0x9/0x10
[ 0.903456] ?
_RNvXCsbDqzXfLQacH_12rust_minimalNtB2_11RustMinimalNtCsbDqzXfLQacH_6kernel12KernelModule4init+0x73/0x80
[ 0.903456] ? _RNvXsa_NtCsbDqzXfLQacH_4core3fmtbNtB5_5Debug3fmt+0x30/0x30
[ 0.903456] ? __rust_minimal_init+0x11/0x20
But it also shows this one below:
[ 0.903456] ?
_RNvXs5_NtCsbDqzXfLQacH_11rust_binder11range_allocNtB5_15DescriptorStateNtNtCsbDqzXfLQacH_4core3fmt5Debug3fmt+0x60/0x60
So something does not look correct.
> AFAICT rust has try/throw/catch exception handling (like
> C++/Java/others) which is typically implemented with stack unwinding of
> its own.
Rust has optional unwinding for panics, yeah, but we don't enable it.
Instead, we tell the compiler to use its "abort" panic strategy. In
the handler currently we just call `BUG()`, but we will need to do
something less aggressive (e.g. kill the current thread).
But please note that it is not our plan to rely on panics for normal
error conditions. For instance, the allocations currently panic due to
our re-use of `alloc`, but will be fallible eventually (`Result`
etc.).
Cheers,
Miguel
On 4/16/21 12:37 PM, Willy Tarreau wrote:
> Hi Miguel,
>
> On Fri, Apr 16, 2021 at 07:10:17PM +0200, Miguel Ojeda wrote:
>> And by having the compiler enforce this safe-unsafe split, you can
>> review safe code without having to constantly worry about UB; and be
>> extra alert when dealing with `unsafe` blocks.
>
> I do appreciate this safe/unsafe split and a few other things I've seen
> in the language. The equivalent I'm using in C is stronger typing and
> "const" modifiers wherever possible. Of course it's much more limited,
> it's just to explain that I do value this. I just feel like "unsafe"
> is the universal response to any question "how would I do this" while
> at the same time "safe" is the best selling argument for the language.
> As such, I strongly doubt about the real benefits once facing reality
> with everything marked unsafe. Except that it will be easier to blame
> the person having written the unsafe one-liner instead of writing 60
> cryptic lines doing the functional equivalent using some lesser known
> extensions :-/
>
It's possible that many of the questions you've been specifically asking
about, by sheer coincidence, are targeted towards the problems that would
indeed require a lower-level abstraction built within an unsafe block; meaning
you've managed to evade the tons of other upper layers that could be written
in safe Rust.
Indeed, at a certain layer, unsafe is unavoidable for the kind of work that
is done in the kernel. The goal is to shrink the unsafe blocks as much as
possible and confirm the correctness of those pieces, then build safe
abstractions on top of it.
For what it's worth, if there was some post-human apocalyptic world where
literally everything had to go inside an unsafe block, the silver lining
in a hypothetical situation like this is that unsafe does not disable all
of Rust's static analysis like the borrow checker, etc. It allows you to
do things like directly dereference a pointer, etc. Unsafe also doesn't
automatically mean that the code is wrong or that it has memory issues;
it just means that the compiler can't guarantee that it doesn't based on
what you do in the unsafe block.
Connor
On Fri, Apr 16, 2021 at 7:05 PM Linus Torvalds
<[email protected]> wrote:
>
> Typical Rust error handling should match the regular kernel
> IS_ERR/ERR_PTR/PTR_ERR model fairly well, although the syntax is
> fairly different (and it's not limited to pointers).
Yeah, exactly. We already have a `KernelResult<T>` type which is a
`Result<T, Error>`, where `Error` is a wrapper for the usual kernel
int errors.
So, for instance, a function that can either fail or return `Data`
would have a declaration like:
pub fn foo() -> KernelResult<Data>
A caller that needs to handle the error can use pattern matching or
one of the methods in `Result`. And if they only need to bubble the
error up, they can use the ? operator:
pub fn bar() -> KernelResult<Data> {
let data = foo()?;
// `data` is already a `Data` here, not a `KernelResult<Data>`
}
Cheers,
Miguel
On Fri, Apr 16, 2021 at 07:47:32PM +0200, Miguel Ojeda wrote:
> On Fri, Apr 16, 2021 at 7:05 PM Linus Torvalds
> <[email protected]> wrote:
> >
> > Typical Rust error handling should match the regular kernel
> > IS_ERR/ERR_PTR/PTR_ERR model fairly well, although the syntax is
> > fairly different (and it's not limited to pointers).
>
> Yeah, exactly. We already have a `KernelResult<T>` type which is a
> `Result<T, Error>`, where `Error` is a wrapper for the usual kernel
> int errors.
>
> So, for instance, a function that can either fail or return `Data`
> would have a declaration like:
>
> pub fn foo() -> KernelResult<Data>
>
> A caller that needs to handle the error can use pattern matching or
> one of the methods in `Result`. And if they only need to bubble the
> error up, they can use the ? operator:
>
> pub fn bar() -> KernelResult<Data> {
> let data = foo()?;
>
> // `data` is already a `Data` here, not a `KernelResult<Data>`
> }
Umm... A fairly common situation is
foo() returns a pointer to struct foo instance or ERR_PTR()
bar() returns a pointer to struct bar instance of ERR_PTR()
bar()
{
struct foo *p;
struct bar *res;
.... // do some work, grab a mutex, etc.
p = foo();
if (IS_ERR(p))
res = ERR_CAST(p); // (void *)p, internally; conceptually it's
// ERR_PTR(PTR_ERR(p));
else
res = blah();
.... // matching cleanup
return res;
}
How well would ? operator fit that pattern? _If_ it's just a syntax sugar
along the lines of "if argument matches Err(_), return Err(_)", the types
shouldn't be an issue, but that might need some fun with releasing resources,
etc. If it's something more elaborate... details, please.
On Fri, Apr 16, 2021 at 07:18:48PM +0200, Peter Zijlstra wrote:
> On Fri, Apr 16, 2021 at 07:10:17PM +0200, Miguel Ojeda wrote:
>
> > Of course, UB is only a subset of errors, but it is a major one, and
> > particularly critical for privileged code.
>
> I've seen relatively few UBSAN warnings that weren't due to UBSAN being
> broken.
Lucky you.
84c34df158cf215b0cd1475ab3b8e6f212f81f23
(i'd argue this is C being broken; promoting only as far as int, when
assigning to an unsigned long is Bad, but until/unless either GCC fixes
that or the language committee realises that being stuck in the 1970s
is Bad, people are going to keep making this kind of mistake)
On Thu, Apr 15, 2021 at 11:04:37PM -0700, Nick Desaulniers wrote:
> On Thu, Apr 15, 2021 at 9:27 PM Boqun Feng <[email protected]> wrote:
> >
> > [Copy LKMM people, Josh, Nick and Wedson]
> >
> > On Thu, Apr 15, 2021 at 08:58:16PM +0200, Peter Zijlstra wrote:
> > > On Wed, Apr 14, 2021 at 08:45:51PM +0200, [email protected] wrote:
> > >
> > > > 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.
> > >
> > > And yet I see not a single mention of the Rust Memory Model and how it
> > > aligns (or not) with the LKMM. The C11 memory model for example is a
> > > really poor fit for LKMM.
> > >
> >
> > I think Rust currently uses C11 memory model as per:
> >
> > https://doc.rust-lang.org/nomicon/atomics.html
> >
> > , also I guess another reason that they pick C11 memory model is because
> > LLVM has the support by default.
> >
> > But I think the Rust Community still wants to have a good memory model,
> > and they are open to any kind of suggestion and input. I think we (LKMM
> > people) should really get involved, because the recent discussion on
> > RISC-V's atomics shows that if we didn't people might get a "broken"
> > design because they thought C11 memory model is good enough:
> >
> > https://lore.kernel.org/lkml/YGyZPCxJYGOvqYZQ@boqun-archlinux/
> >
> > And the benefits are mutual: a) Linux Kernel Memory Model (LKMM) is
> > defined by combining the requirements of developers and the behavior of
> > hardwares, it's pratical and can be a very good input for memory model
> > designing in Rust; b) Once Rust has a better memory model, the compiler
> > technologies whatever Rust compilers use to suppor the memory model can
> > be adopted to C compilers and we can get that part for free.
>
> Yes, I agree; I think that's a very good approach. Avoiding the ISO
> WG14 is interesting; at least the merits could be debated in the
> public and not behind closed doors.
WG14 (C) and WG21 (C++) are at least somewhat open. Here are some of
the proposals a few of us have in flight:
P2055R0 A Relaxed Guide to memory_order_relaxed
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p2055r0.pdf
P0124R7 Linux-Kernel Memory Model (vs. that of C/C++)
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p0124r7.html
P1726R4 Pointer lifetime-end zap
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p1726r4.pdf
https://docs.google.com/document/d/1MfagxTa6H0rTxtq9Oxyh4X53NzKqOt7y3hZBVzO_LMk/edit?usp=sharing
P1121R2 Hazard Pointers: Proposed Interface and Wording for Concurrency TS 2
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p1121r2.pdf
P1382R1 volatile_load<T> and volatile_store<T>
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1382r1.pdf
P1122R2 Proposed Wording for Concurrent Data Structures: Read-Copy-Update (RCU)
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1122r2.pdf
https://docs.google.com/document/d/1MfagxTa6H0rTxtq9Oxyh4X53NzKqOt7y3hZBVzO_LMk/edit?usp=sharing
P0190R4 Proposal for New memory order consume Definition
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0190r4.pdf
P0750R1 Consume
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0750r1.html
P1726R4 is of particular concern, along with consume.
> > At least I personally is very intereted to help Rust on a complete and
> > pratical memory model ;-)
> >
> > Josh, I think it's good if we can connect to the people working on Rust
> > memoryg model, I think the right person is Ralf Jung and the right place
> > is https://github.com/rust-lang/unsafe-code-guidelines, but you
> > cerntainly know better than me ;-) Or maybe we can use Rust-for-Linux or
> > linux-toolchains list to discuss.
> >
> > [...]
> > > > - Boqun Feng is working hard on the different options for
> > > > threading abstractions and has reviewed most of the `sync` PRs.
> > >
> > > Boqun, I know you're familiar with LKMM, can you please talk about how
> > > Rust does things and how it interacts?
> >
> > As Wedson said in the other email, currently there is no code requiring
> > synchronization between C side and Rust side, so we are currently fine.
> > But in the longer term, we need to teach Rust memory model about the
> > "design patterns" used in Linux kernel for parallel programming.
> >
> > What I have been doing so far is reviewing patches which have memory
> > orderings in Rust-for-Linux project, try to make sure we don't include
> > memory ordering bugs for the beginning.
I believe that compatibility with both C/C++ and the Linux kernel are
important.
Thanx, Paul
On Fri, Apr 16, 2021 at 08:57:07PM +0200, Miguel Ojeda wrote:
> On Fri, Apr 16, 2021 at 8:10 PM Al Viro <[email protected]> wrote:
> >
> > How well would ? operator fit that pattern? _If_ it's just a syntax sugar
> > along the lines of "if argument matches Err(_), return Err(_)", the types
> > shouldn't be an issue, but that might need some fun with releasing resources,
> > etc. If it's something more elaborate... details, please.
>
> Yes, it is just syntax sugar -- it doesn't introduce any power to the language.
>
> It was introduced because it is a very common pattern when using the
> `Result` and `Option` enums. In fact, before it existed, it was just a
> simple macro that you could also implement yourself.
>
> For instance, given `Foo` and `Bar` types that need RAII cleanup of
> some kind (let's say `kill_foo()` and `kill_bar()`):
>
> fn foo() -> KernelResult<Foo> {
> if black_box() {
> return Err(EINVAL);
> }
>
> // something that gets you a `Foo`
> let foo = ...;
>
> Ok(foo)
> }
>
> fn bar() -> KernelResult<Bar> {
> let p = foo()?;
>
> // something that gets you a `Bar`, possibly using the `p`
> let bar = ...;
>
> Ok(bar)
> }
>
> This reduces to (full example at https://godbolt.org/z/hjTxd3oP1):
>
> bar:
> push rbx
> mov ebx, 1
> call qword ptr [rip + black_box@GOTPCREL]
> test al, al
> jne .LBB2_2
> call qword ptr [rip + kill_foo@GOTPCREL]
> xor ebx, ebx
> .LBB2_2:
> mov eax, ebx
> mov edx, -1234
> pop rbx
> ret
>
> You can see `bar()` calls `black_box()`. If it failed, it returns the
> EINVAL. Otherwise, it cleans up the `foo` automatically and returns
> the successful `bar`.
So it simply does the equivalent of:
#define EINVAL -1234
struct result {
int status;
int error;
};
extern bool black_box();
extern void kill_foo();
struct result bar()
{
return (struct error){ !!black_box(), EINVAL };
}
struct result foo()
{
struct result res = bar();
if (res.status)
goto leave;
/* ... */
kill_foo(); // only for rust, C doesn't need it
leave:
return res;
}
So it simply returns a pair of values instead of a single one, which
is nothing special but not much conventional in the kernel either given
that ultimately syscalls will usually return a single value anyway. At
some point some code will have to remerge them to provide a composite
result and even take care of ambigous special cases like { true, 0 }
which may or may not indicate an error, and avoiding to consider the
unused error code on success.
For example some code called from mmap() might have to deal with this
this way:
if (result.status == (void *)-1)
return -result.error;
else
return result.status;
But possibly a lot of code feeding this result struct would be tempted
to do something like this to deal with NULL returns:
result.status = foo_alloc();
if (!result.status) {
result.error = -ENOMEM;
return result;
}
And suddenly the error is lost, as a NULL is returned to the upper layers
which does not correspond to an failure status. Conversely, with a unique
result you'd do something like this:
result = foo_alloc();
if (!result)
return -ENOMEM;
So it might possibly be safer to stick to the usually expected return
values instead of introducing composite ones.
I tend to agree that composite results can be better from new projects
started from scratch when all the API follows this principle, but here
there's quite a massive code base that was not designed along these
lines and I easily see how this can become a source of trouble over
time.
Willy
On Fri, Apr 16, 2021 at 07:10:17PM +0200, Miguel Ojeda wrote:
> Of course, UB is only a subset of errors, but it is a major one, and
> particularly critical for privileged code.
I've seen relatively few UBSAN warnings that weren't due to UBSAN being
broken.
On 4/16/21 3:22 PM, Willy Tarreau wrote:
> So it simply does the equivalent of:
>
> #define EINVAL -1234
>
> struct result {
> int status;
> int error;
> };
Result and Option types are more like a union with a tag that
describes which variant it is.
struct foo_result {
/* if ok, then access foo_or_err.successful_foo
* else, access foo_or_err.error
*/
bool ok;
union {
struct foo successful_foo;
int error;
} foo_or_err;
};
> [..]
>
> So it simply returns a pair of values instead of a single one, which
It will only return 1 value.
On Fri, Apr 16, 2021 at 03:34:50PM -0500, Connor Kuehl wrote:
> On 4/16/21 3:22 PM, Willy Tarreau wrote:
> > So it simply does the equivalent of:
> >
> > #define EINVAL -1234
> >
> > struct result {
> > int status;
> > int error;
> > };
>
> Result and Option types are more like a union with a tag that
> describes which variant it is.
>
> struct foo_result {
> /* if ok, then access foo_or_err.successful_foo
> * else, access foo_or_err.error
> */
> bool ok;
> union {
> struct foo successful_foo;
> int error;
> } foo_or_err;
> };
OK.
> > [..]
> >
> > So it simply returns a pair of values instead of a single one, which
>
> It will only return 1 value.
No, two:
- ok in %rax (seems like it's "!ok" technically speaking since it
returns 1 on !ok and 0 on ok)
- foo_or_err in %rdx
However then I'm bothered because Miguel's example showed that regardless
of OK, EINVAL was always returned in foo_or_err, so maybe it's just
because his example was not well chosen but it wasn't very visible from
the source:
bar:
push rbx
mov ebx, 1
call qword ptr [rip + black_box@GOTPCREL]
test al, al
jne .LBB2_2
call qword ptr [rip + kill_foo@GOTPCREL]
xor ebx, ebx
.LBB2_2:
mov eax, ebx
mov edx, -1234
pop rbx
ret
Willy
On Fri, Apr 16, 2021 at 12:27:39PM +0800, Boqun Feng wrote:
> Josh, I think it's good if we can connect to the people working on Rust
> memoryg model, I think the right person is Ralf Jung and the right place
> is https://github.com/rust-lang/unsafe-code-guidelines, but you
> cerntainly know better than me ;-) Or maybe we can use Rust-for-Linux or
> linux-toolchains list to discuss.
Ralf is definitely the right person to talk to. I don't think the UCG
repository is the right place to start that discussion, though. For now,
I'd suggest starting an email thread with Ralf and some C-and-kernel
memory model folks (hi Paul!) to suss out the most important changes
that would be needed.
With my language team hat on, I'd *absolutely* like to see the Rust
memory model support RCU-style deferred reclamation in a sound way,
ideally with as little unsafe code as possible.
On Fri, Apr 16, 2021 at 10:22 PM Willy Tarreau <[email protected]> wrote:
>
> So it simply does the equivalent of:
>
> struct result {
> int status;
> int error;
> };
Not exactly, it is more like a tagged union, as Connor mentioned.
However, and this is the critical bit: it is a compile-time error to
access the inactive variants (in safe code). In C, it is on you to
keep track which one is the current one.
> kill_foo(); // only for rust, C doesn't need it
Please note that `kill_foo()` is not needed in Rust -- it was an
example of possible cleanup (since Al mentioned resources/cleanup)
using RAII.
Cheers,
Miguel
On Fri, Apr 16, 2021 at 11:39:00PM +0200, Miguel Ojeda wrote:
> On Fri, Apr 16, 2021 at 10:58 PM Willy Tarreau <[email protected]> wrote:
> >
> > No, two:
> > - ok in %rax (seems like it's "!ok" technically speaking since it
> > returns 1 on !ok and 0 on ok)
> > - foo_or_err in %rdx
>
> Yes, but that is the implementation -- conceptually you only have one
> or the other, and Rust won't allow you to use the wrong one.
OK so for unions you always pass two values along the whole chain, a
selector and the value itself.
But my point remains that the point of extreme care is at the interface
with the rest of the kernel because there is a change of semantics
there.
> > However then I'm bothered because Miguel's example showed that regardless
> > of OK, EINVAL was always returned in foo_or_err, so maybe it's just
> > because his example was not well chosen but it wasn't very visible from
> > the source:
>
> That is the optimizer being fancy since the error can be put
> unconditionally in `rdx`.
Yes that's what I understood as well. I just didn't know that it had
to be seen as a union.
On Fri, Apr 16, 2021 at 11:19:18PM +0200, Miguel Ojeda wrote:
> On Fri, Apr 16, 2021 at 10:22 PM Willy Tarreau <[email protected]> wrote:
> >
> > So it simply does the equivalent of:
> >
> > struct result {
> > int status;
> > int error;
> > };
>
> Not exactly, it is more like a tagged union, as Connor mentioned.
>
> However, and this is the critical bit: it is a compile-time error to
> access the inactive variants (in safe code). In C, it is on you to
> keep track which one is the current one.
Sure but as I said most often (due to API or ABI inheritance), both
are already exclusive and stored as ranges. Returning 1..4095 for
errno or a pointer including NULL for a success doesn't shock me at
all.
Along thes lines I hardly see how you'd tag pointers by manipulating
their lower unused bits. That's something important both for memory
usage and performance (supports atomic opts).
> > kill_foo(); // only for rust, C doesn't need it
>
> Please note that `kill_foo()` is not needed in Rust -- it was an
> example of possible cleanup (since Al mentioned resources/cleanup)
> using RAII.
Yep but I kept it just to have comparable output code since in C
you'd simply use "goto leave" and not have this function call to
do the cleanup.
Willy
On Fri, Apr 16, 2021 at 10:58 PM Willy Tarreau <[email protected]> wrote:
>
> No, two:
> - ok in %rax (seems like it's "!ok" technically speaking since it
> returns 1 on !ok and 0 on ok)
> - foo_or_err in %rdx
Yes, but that is the implementation -- conceptually you only have one
or the other, and Rust won't allow you to use the wrong one.
> However then I'm bothered because Miguel's example showed that regardless
> of OK, EINVAL was always returned in foo_or_err, so maybe it's just
> because his example was not well chosen but it wasn't very visible from
> the source:
That is the optimizer being fancy since the error can be put
unconditionally in `rdx`.
If you compile:
pub fn it_is_ok() -> KernelResult<Bar> {
Ok(Bar)
}
you will see it just clears `rax`.
Cheers,
Miguel
On Fri, Apr 16, 2021 at 8:10 PM Al Viro <[email protected]> wrote:
>
> How well would ? operator fit that pattern? _If_ it's just a syntax sugar
> along the lines of "if argument matches Err(_), return Err(_)", the types
> shouldn't be an issue, but that might need some fun with releasing resources,
> etc. If it's something more elaborate... details, please.
Yes, it is just syntax sugar -- it doesn't introduce any power to the language.
It was introduced because it is a very common pattern when using the
`Result` and `Option` enums. In fact, before it existed, it was just a
simple macro that you could also implement yourself.
For instance, given `Foo` and `Bar` types that need RAII cleanup of
some kind (let's say `kill_foo()` and `kill_bar()`):
fn foo() -> KernelResult<Foo> {
if black_box() {
return Err(EINVAL);
}
// something that gets you a `Foo`
let foo = ...;
Ok(foo)
}
fn bar() -> KernelResult<Bar> {
let p = foo()?;
// something that gets you a `Bar`, possibly using the `p`
let bar = ...;
Ok(bar)
}
This reduces to (full example at https://godbolt.org/z/hjTxd3oP1):
bar:
push rbx
mov ebx, 1
call qword ptr [rip + black_box@GOTPCREL]
test al, al
jne .LBB2_2
call qword ptr [rip + kill_foo@GOTPCREL]
xor ebx, ebx
.LBB2_2:
mov eax, ebx
mov edx, -1234
pop rbx
ret
You can see `bar()` calls `black_box()`. If it failed, it returns the
EINVAL. Otherwise, it cleans up the `foo` automatically and returns
the successful `bar`.
Cheers,
Miguel
On Sat, Apr 17, 2021 at 12:04:16AM +0200, Willy Tarreau wrote:
> Yep but I kept it just to have comparable output code since in C
> you'd simply use "goto leave" and not have this function call to
> do the cleanup.
... or use any number of other technics; the real question was how
much of cleanups would be skipped by that syntax sugar.
IME anything that interacts with flow control should be as explicit
and unambiguous as possible. _Especially_ concerning how large
a scope are we leaving.
There's a bunch of disciplines that make use of that kind of tools
and do it more or less safely, but they need to be well-specified
and very well understood. And some tools (C++-style exceptions,
for example) simply need to be taken out and shot, but that's
a separate story^Wflamewar...
On Sat, Apr 17, 2021 at 12:04 AM Willy Tarreau <[email protected]> wrote:
>
> But my point remains that the point of extreme care is at the interface
> with the rest of the kernel because there is a change of semantics
> there.
>
> Sure but as I said most often (due to API or ABI inheritance), both
> are already exclusive and stored as ranges. Returning 1..4095 for
> errno or a pointer including NULL for a success doesn't shock me at
> all.
At the point of the interface we definitely need to take care of
converting properly, but for Rust-to-Rust code (i.e. the ones using
`Result` etc.), that would not be a concern.
Just to ensure I understood your concern, for instance, in this case
you mentioned:
result.status = foo_alloc();
if (!result.status) {
result.error = -ENOMEM;
return result;
}
Is your concern is that the caller would mix up the `status` with the
`error`, basically bubbling up the `status` as an `int` and forgetting
about the `error`, and then someone else later understanding that
`int` as a non-error because it is non-negative?
If yes: in C, yes, that could be a concern (if done with raw `int`s).
In Rust, if you get an `Err(ENOMEM)` from somebody, you cannot easily
conflate it with another type and return it by mistake because it is
more strictly typed than C.
Cheers,
Miguel
On Sat, Apr 17, 2021 at 01:46:35AM +0200, Miguel Ojeda wrote:
> On Sat, Apr 17, 2021 at 12:04 AM Willy Tarreau <[email protected]> wrote:
> >
> > But my point remains that the point of extreme care is at the interface
> > with the rest of the kernel because there is a change of semantics
> > there.
> >
> > Sure but as I said most often (due to API or ABI inheritance), both
> > are already exclusive and stored as ranges. Returning 1..4095 for
> > errno or a pointer including NULL for a success doesn't shock me at
> > all.
>
> At the point of the interface we definitely need to take care of
> converting properly, but for Rust-to-Rust code (i.e. the ones using
> `Result` etc.), that would not be a concern.
Sure.
> Just to ensure I understood your concern, for instance, in this case
> you mentioned:
>
> result.status = foo_alloc();
> if (!result.status) {
> result.error = -ENOMEM;
> return result;
> }
Yes I mentioned this when it was my understanding that the composite
result returned was made both of a pointer and an error code, but Connor
explained that it was in fact more of a selector and a union.
> Is your concern is that the caller would mix up the `status` with the
> `error`, basically bubbling up the `status` as an `int` and forgetting
> about the `error`, and then someone else later understanding that
> `int` as a non-error because it is non-negative?
My concern was to know what field to look at to reliably detect an error
from the C side after a sequence doing C -> Rust -> C when the inner C
code uses NULL to mark an error and the upper C code uses NULL as a valid
value and needs to look at an error code instead to rebuild a result. But
if it's more:
if (result.ok)
return result.pointer;
else
return (void *)-result.error;
then it shouldn't be an issue.
Willy
On Fri, Apr 16, 2021 at 4:24 AM Peter Zijlstra <[email protected]> wrote:
> Simlar thing for RCU; C11 can't optimally do that; it needs to make
> rcu_dereference() a load-acquire [something ARM64 has already done in C
> because the compiler might be too clever by half when doing LTO :-(].
> But it's the compiler needing the acquire semantics, not the computer,
> which is just bloody wrong.
You may already know, but perhaps worth clarifying:
C11 does have atomic_signal_fence() which is a compiler fence. But a
compiler fence only ensures the loads will be emitted in the right
order, not that the CPU will execute them in the right order. CPU
architectures tend to guarantee that two loads will be executed in the
right order if the second one's address depends on the first one's
result, but a dependent load can stop being dependent after compiler
optimizations involving value speculation. Using a load-acquire works
around this, not because it stops the compiler from performing any
optimization, but because it tells the computer to execute the loads
in the right order *even if* the compiler has broken the value
dependence.
So C11 atomics don't make the situation worse, compared to Linux's
atomics implementation based on volatile and inline assembly. Both
are unsound in the presence of value speculation. C11 atomics were
*supposed* to make the situation better, with memory_order_consume,
which would have specifically forbidden the compiler from performing
value speculation. But all the compilers punted on getting this to
work and instead just implemented memory_order_consume as
memory_order_acquire.
As for Rust, it compiles to the same LLVM IR that Clang compiles C
into. Volatile, inline assembly, and C11-based atomics: all of these
are available in Rust, and generate exactly the same code as their C
counterparts, for better or for worse. Unfortunately, the Rust
project has relatively limited muscle when it comes to contributing to
LLVM. So while it would definitely be nice if Rust could make RCU
sound, and from a specification perspective I think people would be
quite willing and probably easier to work with than the C committee...
I suspect that implementing this would require the kind of sweeping
change to LLVM that is probably not going to come from Rust.
There are other areas where I think that kind of discussion might be
more fruitful. For example, the Rust documentation currently says
that a volatile read racing with a non-volatile write (i.e. seqlocks)
is undefined behavior. [1] However, I am of the opinion that this is
essentially a spec bug, for reasons that are probably not worth
getting into here.
[1] https://doc.rust-lang.org/nightly/std/ptr/fn.read_volatile.html
On Fri, Apr 16, 2021 at 07:08:29PM +0100, Matthew Wilcox wrote:
> On Fri, Apr 16, 2021 at 07:18:48PM +0200, Peter Zijlstra wrote:
> > On Fri, Apr 16, 2021 at 07:10:17PM +0200, Miguel Ojeda wrote:
> >
> > > Of course, UB is only a subset of errors, but it is a major one, and
> > > particularly critical for privileged code.
> >
> > I've seen relatively few UBSAN warnings that weren't due to UBSAN being
> > broken.
>
> Lucky you.
>
> 84c34df158cf215b0cd1475ab3b8e6f212f81f23
>
> (i'd argue this is C being broken; promoting only as far as int, when
> assigning to an unsigned long is Bad, but until/unless either GCC fixes
> that or the language committee realises that being stuck in the 1970s
> is Bad, people are going to keep making this kind of mistake)
Well, I think the rules actually make sense, at the point in the syntax
tree where + happens, we have 'unsigned char' and 'int', so at that
point we promote to 'int'. Subsequently 'int' gets shifted and bad
things happen.
The 'unsigned long' doesn't happen until quite a bit later.
Anyway, the rules are imo fairly clear and logical, but yes they can be
annoying. The really silly thing here is that << and >> have UB at all,
and I would love a -fwrapv style flag that simply defines it. Yes it
will generate worse code in some cases, but having the UB there is just
stupid.
That of course doesn't help your case here, it would simply misbehave
and not be UB.
Another thing the C rules cannot really express is a 32x32->64
multiplication, some (older) versions of GCC can be tricked into it, but
mostly it just doesn't want to do that sanely and the C rules are
absolutely no help there.
On Sat, Apr 17, 2021 at 01:17:21PM +0200, Peter Zijlstra wrote:
> Well, I think the rules actually make sense, at the point in the syntax
> tree where + happens, we have 'unsigned char' and 'int', so at that
> point we promote to 'int'. Subsequently 'int' gets shifted and bad
> things happen.
That's always the problem caused by signedness being applied to the
type while modern machines do not care about that and use it during
(or even after) the operation instead :-/
We'd need to define some macros to zero-extend and sign-extend some
values to avoid such issues. I'm sure this would be more intuitive
than trying to guess how many casts (and in what order) to place to
make sure an operation works as desired.
> The 'unsigned long' doesn't happen until quite a bit later.
>
> Anyway, the rules are imo fairly clear and logical, but yes they can be
> annoying. The really silly thing here is that << and >> have UB at all,
> and I would love a -fwrapv style flag that simply defines it. Yes it
> will generate worse code in some cases, but having the UB there is just
> stupid.
I'd also love to have a UB-less mode with well defined semantics for
plenty of operations that are known to work well on modern machines,
like integer wrapping, bit shifts ignoring higher bits etc. Lots of
stuff we often have to write useless code for, just to please the
compiler.
> That of course doesn't help your case here, it would simply misbehave
> and not be UB.
>
> Another thing the C rules cannot really express is a 32x32->64
> multiplication, some (older) versions of GCC can be tricked into it, but
> mostly it just doesn't want to do that sanely and the C rules are
> absolutely no help there.
For me the old trick of casting one side as long long still works:
unsigned long long mul3264(unsigned int a, unsigned int b)
{
return (unsigned long long)a * b;
}
i386:
00000000 <mul3264>:
0: 8b 44 24 08 mov 0x8(%esp),%eax
4: f7 64 24 04 mull 0x4(%esp)
8: c3 ret
x86_64:
0000000000000000 <mul3264>:
0: 89 f8 mov %edi,%eax
2: 89 f7 mov %esi,%edi
4: 48 0f af c7 imul %rdi,%rax
8: c3 retq
Or maybe you had something else in mind ?
Willy
From: Peter Zijlstra
> Sent: 16 April 2021 15:19
>
> On Fri, Apr 16, 2021 at 02:07:49PM +0100, Wedson Almeida Filho wrote:
> > On Fri, Apr 16, 2021 at 01:24:23PM +0200, Peter Zijlstra wrote:
>
> > > int perf_event_task_enable(void)
> > > {
> > > + DEFINE_MUTEX_GUARD(event_mutex, ¤t->perf_event_mutex);
> >
> > There is nothing in C forcing developers to actually use DEFINE_MUTEX_GUARD. So
> > someone may simply forget (or not know that they need) to lock
> > current->perf_event_mutex and directly access some field protected by it. This
> > is unlikely to happen when one first writes the code, but over time as different
> > people modify the code and invariants change, it is possible for this to happen.
> >
> > In Rust, this isn't possible: the data protected by a lock is only accessible
> > when the lock is locked. So developers cannot accidentally make mistakes of this
> > kind. And since the enforcement happens at compile time, there is no runtime
> > cost.
> >
> > This, we believe, is fundamental to the discussion: we agree that many of these
> > idioms can be implemented in C (albeit in this case with a compiler extension),
> > but their use is optional, people can (and do) still make mistakes that lead to
> > vulnerabilities; Rust disallows classes of mistakes by construction.
>
> Does this also not prohibit constructs where modification must be done
> while holding two locks, but reading can be done while holding either
> lock?
>
> That's a semi common scheme in the kernel, but not something that's
> expressible by, for example, the Java sync keyword.
>
> It also very much doesn't work for RCU, where modification must be done
> under a lock, but access is done essentially lockless.
...
Or the cases where the locks are released in the 'wrong' order.
Typically for:
lock(table)
item = lookup(table, key)
lock(item)
unlock(table)
...
unlock(item)
(In the kernel the table lock might be RCU.)
Or, with similar data:
write_lock(table);
foreach(item, table)
lock(item)
unlock(item)
/* No items can be locked until we release the write_lock.
...
unlock(table)
You can also easily end up with a 'fubar' we have at work where
someone wrote a C++ condvar class that inherits from mutex.
David
-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK
Registration No: 1397386 (Wales)
..
> The more you make it look like (Kernel) C, the easier it is for us C
> people to actually read. My eyes have been reading C for almost 30 years
> by now, they have a lexer built in the optical nerve; reading something
> that looks vaguely like C but is definitely not C is an utterly painful
> experience.
I'll see your 30 years and raise to over 35.
(And writing code that accesses hardware for 6 or 7 years before that.)
Both Java and go can look more like the K&R style C than any of the
examples from microsoft - which seem to utilise as much vertical space
as humanly? possible.
Those rust examples seemed to be of the horrid microsoft sytle.
Nothing about that style makes reading code easy.
David
-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK
Registration No: 1397386 (Wales)
On Sat, Apr 17, 2021 at 12:41:23PM +0000, David Laight wrote:
> Or the cases where the locks are released in the 'wrong' order.
> Typically for:
> lock(table)
> item = lookup(table, key)
> lock(item)
> unlock(table)
> ...
> unlock(item)
This is expressible in Rust with something like:
table = table_mutex.lock()
item = table.lookup(key).lock()
drop(table)
...
// item will be unlocked when it goes out of scope or on drop(item)
The added bonus here from Rust is that table is not accessible after
drop(table), so a developer cannot accidentally access fields after unlocking
it.
>
> (In the kernel the table lock might be RCU.)
>
> Or, with similar data:
> write_lock(table);
> foreach(item, table)
> lock(item)
> unlock(item)
> /* No items can be locked until we release the write_lock.
> ...
> unlock(table)
I think I'm missing something here. Would you help me understand what part is
out of the ordinary in the code above? It would be expressible in Rust with
something like:
table = table_mutex.write();
for (item_mutex in table)
item = item_mutex.lock
// item is unlocked at the end of the loop iteration (out of scope)
// table gets unlocked when it goes out of scope
Cheers,
-Wedson
On Fri, Apr 16, 2021 at 04:03:07PM +0100, Matthew Wilcox wrote:
> Well, we could do that in C too.
>
> struct unlocked_inode {
> spinlock_t i_lock;
> };
>
> struct locked_inode {
> spinlock_t i_lock;
> unsigned short i_bytes;
> blkcnt_t i_blocks;
> };
>
> struct locked_inode *lock_inode(struct unlocked_inode *inode)
> {
> spin_lock(&inode->i_lock);
> return (struct locked_inode *)inode;
> }
Indeed you can do this kind of thing in C, but as I said before (apologies if
I'm too repetitive on this) Rust forces you to do it the right way, whereas the
lack of enforcement in C leaves room for mistakes.
If you do add extensions to C to add some of these restrictions (and I encourage
you to pursue such extensions as we all benefit from better C), it is likely not
sufficient to reach the level of compile-time guarantee that Rust offers because
you need a whole slew of restrictions/enforcements.
I also note that academics have a formalisation of [a subset of] Rust that show
the soundness of these guarantees and the requirements on unsafe to compose
safely. So we're not talking about guesswork, there are formal machine-checked
proofs published about this (see for example
https://people.mpi-sws.org/~dreyer/papers/safe-sysprog-rust/paper.pdf).
From: Peter Zijlstra
> Sent: 17 April 2021 12:17
...
> > (i'd argue this is C being broken; promoting only as far as int, when
> > assigning to an unsigned long is Bad, but until/unless either GCC fixes
> > that or the language committee realises that being stuck in the 1970s
> > is Bad, people are going to keep making this kind of mistake)
>
> Well, I think the rules actually make sense, at the point in the syntax
> tree where + happens, we have 'unsigned char' and 'int', so at that
> point we promote to 'int'. Subsequently 'int' gets shifted and bad
> things happen.
The 1970s were fine.
K&R C was sign preserving - so 'unsigned char' promoted to 'unsigned int'.
The ANSI C committee broke things by changing it to value preserving
with the strong preference for signed types.
Even with ANSI C the type of ((unsigned char)x + 1) can be unsigned!
All it needs as an architecture where sizeof (int) == 1.
(sizeof (char) has to be 1 - even though that isn't directly explicit.)
Of course, having 32-bit 'char' and 'int' does give problems with
the value for EOF.
David
-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK
Registration No: 1397386 (Wales)
On Fri, Apr 16, 2021 at 06:14:44PM +0200, Willy Tarreau wrote:
> But will this remain syntactically readable/writable by mere humans ?
I would certainly hope so.
> > Note that this is
> > another area where Rust offers advantages: read-only guards (in C, if you take a
> > read lock, nothing prevents you from making changes to fields you should only be
> > allowed to read);
>
> But I'm happily doing that when I know what I'm doing. What you call a
> read lock usually is in fact a shared lock as opposed to an exclusive
> lock (generally used for writes). For me it's perfectly valid to perform
> atomic writes under a read lock instead of forcing everyone to wait by
> taking a write lock. You may for example take a read lock on a structure
> to make sure that a field you're accessing in it points to stable memory
> that is only modified under the write lock, but the pointer itself is
> atomically accessed and swapped under the read lock.
Yes, this is a great example. Also easily expressible in Rust: they have this
concept of interior mutability where certain types allow their contents to be
modified even when shared immutably. Atomics offer such interior mutability, so
the scenario you describe is fine.
Rust in fact has an extra enforcement here that C doesn't: it requires interior
mutability for this scenario to be allowed, so you can't do it with a plain
naked type (say u64) -- you'd need to use something like an atomic64_t, where
you're required to specify memory ordering when accessing them.
In C we of course have atomics but the compiler never alerts us for when we need
them.
> > In fact, this is also an advantage of Rust. It would *force* developers to
> > lock/unlock the RCU lock before they can access the protected data.
>
> I'm really afraid by languages which force developers to do this or that.
When I say that Rust forces developers to do certain things, it's to provide the
compile-time safety guarantees. Some of these requirements are imposed by our
own abstractions -- we can always revisit and try to improve them. In cases when
the abstractions cannot be further refined, developers always have the escape
hatch of unsafety, where they're allowed to do pretty much everything as in C,
but then they also give up the compile-time guarantees for those parts.
On Sat, Apr 17, 2021 at 02:53:10PM +0100, Wedson Almeida Filho wrote:
> > > Note that this is
> > > another area where Rust offers advantages: read-only guards (in C, if you take a
> > > read lock, nothing prevents you from making changes to fields you should only be
> > > allowed to read);
> >
> > But I'm happily doing that when I know what I'm doing. What you call a
> > read lock usually is in fact a shared lock as opposed to an exclusive
> > lock (generally used for writes). For me it's perfectly valid to perform
> > atomic writes under a read lock instead of forcing everyone to wait by
> > taking a write lock. You may for example take a read lock on a structure
> > to make sure that a field you're accessing in it points to stable memory
> > that is only modified under the write lock, but the pointer itself is
> > atomically accessed and swapped under the read lock.
>
> Yes, this is a great example. Also easily expressible in Rust: they have this
> concept of interior mutability where certain types allow their contents to be
> modified even when shared immutably. Atomics offer such interior mutability, so
> the scenario you describe is fine.
>
> Rust in fact has an extra enforcement here that C doesn't: it requires interior
> mutability for this scenario to be allowed, so you can't do it with a plain
> naked type (say u64) -- you'd need to use something like an atomic64_t, where
> you're required to specify memory ordering when accessing them.
>
> In C we of course have atomics but the compiler never alerts us for when we need
> them.
OK thanks for explaining.
> > > In fact, this is also an advantage of Rust. It would *force* developers to
> > > lock/unlock the RCU lock before they can access the protected data.
> >
> > I'm really afraid by languages which force developers to do this or that.
>
> When I say that Rust forces developers to do certain things, it's to provide the
> compile-time safety guarantees. Some of these requirements are imposed by our
> own abstractions -- we can always revisit and try to improve them. In cases when
> the abstractions cannot be further refined, developers always have the escape
> hatch of unsafety, where they're allowed to do pretty much everything as in C,
> but then they also give up the compile-time guarantees for those parts.
Well, I can't express how much I hate abstractions because I constantly
need to know what it's doing under the hood, and I spend my time reading
the output asm code because I always want to confirm my assumptions about
the compiler not cheating on me (and not hitting one of its bugs),
especially after C compilers have become so smart that they completely
replace your code with what they think is better for you, (including
nothing), so I guess all of this is really not for someone like me.
However while I'm pretty sure that based on our respective experiences
we'd probably disagree forever on a wide number of approaches when it
comes to deciding whether the developer or the compiler should have the
last say, I sincerely appreciate that you take the time to calmly explain
your differing views and the rationale behind, so many thanks for this!
Willy
On Sat, Apr 17, 2021 at 01:46:23PM +0200, Willy Tarreau wrote:
> For me the old trick of casting one side as long long still works:
>
> unsigned long long mul3264(unsigned int a, unsigned int b)
> {
> return (unsigned long long)a * b;
> }
>
> i386:
> 00000000 <mul3264>:
> 0: 8b 44 24 08 mov 0x8(%esp),%eax
> 4: f7 64 24 04 mull 0x4(%esp)
> 8: c3 ret
>
> x86_64:
> 0000000000000000 <mul3264>:
> 0: 89 f8 mov %edi,%eax
> 2: 89 f7 mov %esi,%edi
> 4: 48 0f af c7 imul %rdi,%rax
> 8: c3 retq
>
> Or maybe you had something else in mind ?
Last time I tried it, the thing refused :/ which is how we ended up with
mul_u32_u32() in asm.
On Sat, Apr 17, 2021 at 04:24:43PM +0200, Peter Zijlstra wrote:
> On Sat, Apr 17, 2021 at 01:46:23PM +0200, Willy Tarreau wrote:
> > For me the old trick of casting one side as long long still works:
> >
> > unsigned long long mul3264(unsigned int a, unsigned int b)
> > {
> > return (unsigned long long)a * b;
> > }
> >
> > i386:
> > 00000000 <mul3264>:
> > 0: 8b 44 24 08 mov 0x8(%esp),%eax
> > 4: f7 64 24 04 mull 0x4(%esp)
> > 8: c3 ret
> >
> > x86_64:
> > 0000000000000000 <mul3264>:
> > 0: 89 f8 mov %edi,%eax
> > 2: 89 f7 mov %esi,%edi
> > 4: 48 0f af c7 imul %rdi,%rax
> > 8: c3 retq
> >
> > Or maybe you had something else in mind ?
>
> Last time I tried it, the thing refused :/ which is how we ended up with
> mul_u32_u32() in asm.
Oh I trust you, I do remember having noticed it on one gcc version as
well (maybe 4.5). But I've been successfully using this since 2.95, and
could quickly recheck that 4.7, 4.8, 5.4, 6.5, 7.4, 9.3 and 11-trunk do
produce the code above, which is reassuring, as we all prefer to limit
the amount of asm statements.
Willy
On 16/04/21 09:09, Peter Zijlstra wrote:
> Well, the obvious example would be seqlocks. C11 can't do them
Sure it can. C11 requires annotating with (the equivalent of) READ_ONCE
all reads of seqlock-protected fields, but the memory model supports
seqlocks just fine.
> Simlar thing for RCU; C11 can't optimally do that
Technically if you know what you're doing (i.e. that you're not on
Alpha) you can do RCU using a relaxed load followed by an
atomic_signal_fence(memory_order_consume). Which I agree is horrible
and not entirely within the standard, but it works in practice. The
Linux implementation of memory barriers, atomic RMW primitives,
load-acquire/store-release etc. is also completely outside the standard,
so it's not much different and more portable.
The only thing that I really, really miss when programming with C11
atomics is smp_mb__{before,after}_atomic().
Paolo
On 16/04/21 17:58, Theodore Ts'o wrote:
> Another fairly common use case is a lockless, racy test of a
> particular field, as an optimization before we take the lock before we
> test it for realsies. In this particular case, we can't allocate
> memory while holding a spinlock, so we check to see without taking the
> spinlock to see whether we should allocate memory (which is expensive,
> and unnecessasry most of the time):
>
> alloc_transaction:
> /*
> * This check is racy but it is just an optimization of allocating new
> * transaction early if there are high chances we'll need it. If we
> * guess wrong, we'll retry or free the unused transaction.
> */
> if (!data_race(journal->j_running_transaction)) {
> /*
> * If __GFP_FS is not present, then we may be being called from
> * inside the fs writeback layer, so we MUST NOT fail.
> */
> if ((gfp_mask & __GFP_FS) == 0)
> gfp_mask |= __GFP_NOFAIL;
> new_transaction = kmem_cache_zalloc(transaction_cache,
> gfp_mask);
> if (!new_transaction)
> return -ENOMEM;
> }
From my limited experience with Rust, things like these are a bit
annoying indeed, sooner or later Mutex<> just doesn't cut it and you
have to deal with its limitations.
In this particular case you would use an AtomicBool field, place it
outside the Mutex-protected struct, and make sure that is only accessed
under the lock just like in C.
One easy way out is to make the Mutex protect (officially) nothing, i.e.
Mutex<()>, and handle the mutable fields yourself using RefCell (which
gives you run-time checking but has some some space cost) or UnsafeCell
(which is unsafe as the name says). Rust makes it pretty easy to write
smart pointers (Mutex<>'s lock guard itself is a smart pointer) so you
also have the possibility of writing a safe wrapper for the combination
of Mutex<()> and UnsafeCell.
Another example is when yu have a list of XYZ objects and use the same
mutex for both the list of XYZ and a field in struct XYZ. You could
place that field in an UnsafeCell and write a function that receives a
guard for the list lock and returns the field, or something like that.
It *is* quite ugly though.
As an aside, from a teaching standpoint associating a Mutex with a
specific data structure is bad IMNSHO, because it encourages too
fine-grained locking. Sometimes the easiest path to scalability is to
use a more coarse lock and ensure that contention is extremely rare.
But it does work for most simple use cases (and device drivers would
qualify as simple more often than not).
Paolo
On Sat, Apr 17, 2021 at 4:21 PM Willy Tarreau <[email protected]> wrote:
>
> Well, I can't express how much I hate abstractions because I constantly
> need to know what it's doing under the hood, and I spend my time reading
> the output asm code because I always want to confirm my assumptions about
> the compiler not cheating on me (and not hitting one of its bugs),
> especially after C compilers have become so smart that they completely
> replace your code with what they think is better for you, (including
> nothing), so I guess all of this is really not for someone like me.
Concerning compiler bugs etc.: as you mention, nowadays that applies
to both C and Rust.
Manually inspecting the output asm does not really scale anymore
because we need to worry about all compiler versions out there,
including future ones, for both GCC and Clang.
So we would need to disable optimizations, or reduce the set of
supported compiler versions, or automatically check the generated asm
(like in the compiler's own test suites), or keep binary blobs after
checking (like in some safety-critical domains), etc.
Since none of those are really doable for us (except perhaps for small
subsets of code or unit tests), we need other ways to help with this.
Rust provides several here. For instance, the UB-less subset means
less surprises and less double-checking if some particular construct
is UB and may give problems later on.
Similarly, Rust is actually more explicit in many cases than C, to
reduce surprises further. For instance, regarding implicit type
conversions, none of these compile:
fn f1(n: i32) -> i64 { n }
fn f2(n: i32, m: i64) { n + m; }
fn f3(b: bool) -> i32 { b }
fn f4(n: i32) -> bool { n }
fn f5(n: i32) -> i32 { if n { 42 } else { 53 } }
Building abstractions also helps to ensure you get the semantics you
want in the face of smart optimizers -- rather than the opposite. For
instance, continuing with the integer examples, you may use a
`NonZeroU32`. Or a `Wrapping<u32>` for intentional wrapping integer
arithmetic, etc.
Cheers,
Miguel
On Sat, Apr 17, 2021 at 6:24 AM Willy Tarreau <[email protected]> wrote:
>
> My concern was to know what field to look at to reliably detect an error
> from the C side after a sequence doing C -> Rust -> C when the inner C
> code uses NULL to mark an error and the upper C code uses NULL as a valid
> value and needs to look at an error code instead to rebuild a result. But
I see, thanks for clarifying. I don't think we want to change anything
on either of the C sides (at least for the foreseeable future). So the
Rust code in-between must respect whatever conventions both C sides
already use, even if they happen to be different on each side.
Thus the C side will not know there was a `Result` inside the Rust
side and so it does not need to worry about which field to look at.
Cheers,
Miguel
On Thu, Apr 15, 2021 at 9:43 AM Miguel Ojeda
<[email protected]> wrote:
>
> On Thu, Apr 15, 2021 at 1:19 AM Nick Desaulniers
> <[email protected]> wrote:
> >
> > Rather than check the origin (yikes, are we intentionally avoiding env
> > vars?), can this simply be
> > ifneq ($(CLIPPY),)
> > KBUILD_CLIPPY := $(CLIPPY)
> > endif
> >
> > Then you can specify whatever value you want, support command line or
> > env vars, etc.?
>
> I was following the other existing cases like `V`. Masahiro can
> probably answer why they are done like this.
You are asking about this code:
ifeq ("$(origin V)", "command line")
KBUILD_VERBOSE = $(V)
endif
You can pass V=1 from the Make command line,
but not from the environment.
KBUILD_VERBOSE is intended as an environment variable,
but you can use it from the Make command line.
Work:
- make V=1
- make KBUILD_VERBOSE=1
- KBUILD_VERBOSE=1 make
Not work:
- V=1 make
The behavior is like that before I became the maintainer.
In my best guess, the reason is,
V=1 is a useful shorthand of KBUILD_VERBOSE=1,
but it is too short. It should not accidentally
pick up an unintended environment variable.
--
Best Regards
Masahiro Yamada
On Thu, Apr 15, 2021 at 2:41 AM <[email protected]> wrote:
> 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.
Sp this effectively means gcc is a second class citizen and even if
gcc is supported
at some point one needs a super recent gcc *and* rust toolchain to build
a rust-enabeled kernel?
I understand that this is right now not a big deal, but as soon a
non-trival subsystem
is rust-only people are forced to upgrade.
Don't get me wrong, I'm all for having rust support in Linux.
But I'm a bit worried about new dependencies on compiler toolchains.
As someone who works a lot with long supported embedded systems I learned that
as soon an application gains a hard dependency on clang or rust I'm in trouble.
--
Thanks,
//richard
On Wed, Apr 14, 2021 at 09:09:53PM +0100, Matthew Wilcox wrote:
> By the way, I don't think that Rust necessarily has to conform to the
> current way that Linux works. If this prompted us to track the current
> context (inside spinlock, handling interrupt, performing writeback, etc)
> and do away with (some) GFP flags, that's not the end of the world.
> We're already moving in that direction to a certain extent with the
> scoped memory allocation APIs to replace GFP_NOFS / GFP_NOIO.
I hadn't myself considered this option but it looks enticing to me. Do you have
a sense of which GFP flags we wouldn't be able to obviate even if we did track
state?
On Sat, Apr 17, 2021 at 04:21:27PM +0200, Willy Tarreau wrote:
> Well, I can't express how much I hate abstractions because I constantly
> need to know what it's doing under the hood, and I spend my time reading
> the output asm code because I always want to confirm my assumptions about
> the compiler not cheating on me (and not hitting one of its bugs),
> especially after C compilers have become so smart that they completely
> replace your code with what they think is better for you, (including
> nothing),
I understand the feeling. One thing I can say about the abstractions we've been
talking about is that they're zero-cost. So you'd still have the ability to
inspect generated code and relate that to source, although it would still be
subject to optimisations like C (or perhaps more optimisations as the compiler
knows more about the code).
> so I guess all of this is really not for someone like me.
This may indeed be the case. But I'd invite you to try it out for yourself
anyway before discounting it. I used to hate destructors in C++ because they
were called implicitly: C was king because I had full control. Now I find myself
publicly backing Rust. I feel the advantages outweigh the cost.
> However while I'm pretty sure that based on our respective experiences
> we'd probably disagree forever on a wide number of approaches when it
> comes to deciding whether the developer or the compiler should have the
> last say, I sincerely appreciate that you take the time to calmly explain
> your differing views and the rationale behind, so many thanks for this!
Thank you. I also appreciate your willingness to engage with us.
Cheers,
-Wedson
On Sat, Apr 17, 2021 at 04:51:58PM +0200, Paolo Bonzini wrote:
> On 16/04/21 09:09, Peter Zijlstra wrote:
> > Well, the obvious example would be seqlocks. C11 can't do them
>
> Sure it can. C11 requires annotating with (the equivalent of) READ_ONCE all
> reads of seqlock-protected fields, but the memory model supports seqlocks
> just fine.
How does that help?
IIRC there's two problems, one on each side the lock. On the write side
we have:
seq++;
smp_wmb();
X = r;
Y = r;
smp_wmb();
seq++;
Which C11 simply cannot do right because it does't have wmb. You end up
having to use seq_cst for the first wmb or make both X and Y (on top of
the last seq) a store-release, both options are sub-optimal.
On the read side you get:
do {
s = seq;
smp_rmb();
r1 = X;
r2 = Y;
smp_rmb();
} while ((s&1) || seq != s);
And then you get into trouble the last barrier, so the first seq load
can be load-acquire, after which the loads of X, Y come after, but you
need then to happen before the second seq load, for which you then need
seq_cst, or make X and Y load-acquire. Again, not optimal.
I have also seen *many* broken variants of it on the web. Some work on
x86 but are totally broken when you build them on LL/SC ARM64.
On Mon, Apr 19, 2021 at 10:26:57AM +0200, Peter Zijlstra wrote:
> https://godbolt.org/z/85xoPxeE5
That wants _Atomic on the seq definition for clang.
> void writer(void)
> {
> atomic_store_explicit(&seq, seq+1, memory_order_relaxed);
> atomic_thread_fence(memory_order_acquire);
>
> X = 1;
> Y = 2;
>
> atomic_store_explicit(&seq, seq+1, memory_order_release);
> }
>
> gives:
>
> writer:
> adrp x1, .LANCHOR0
> add x0, x1, :lo12:.LANCHOR0
> ldr w2, [x1, #:lo12:.LANCHOR0]
> add w2, w2, 1
> str w2, [x0]
> dmb ishld
> ldr w1, [x1, #:lo12:.LANCHOR0]
> mov w3, 1
> mov w2, 2
> stp w3, w2, [x0, 4]
> add w1, w1, w3
> stlr w1, [x0]
> ret
>
> Which, afaict, is completely buggered. What it seems to be doing is
> turning the seq load into a load-acquire, but what we really need is to
> make sure the seq store (increment) is ordered before the other stores.
Put differently, what you seem to want is store-acquire, but there ain't
no such thing.
On 19/04/21 09:32, Peter Zijlstra wrote:
> On Sat, Apr 17, 2021 at 04:51:58PM +0200, Paolo Bonzini wrote:
>> On 16/04/21 09:09, Peter Zijlstra wrote:
>>> Well, the obvious example would be seqlocks. C11 can't do them
>>
>> Sure it can. C11 requires annotating with (the equivalent of) READ_ONCE all
>> reads of seqlock-protected fields, but the memory model supports seqlocks
>> just fine.
>
> How does that help?
>
> IIRC there's two problems, one on each side the lock. On the write side
> we have:
>
> seq++;
> smp_wmb();
> X = r;
> Y = r;
> smp_wmb();
> seq++;
>
> Which C11 simply cannot do right because it does't have wmb.
It has atomic_thread_fence(memory_order_release), and
atomic_thread_fence(memory_order_acquire) on the read side.
> You end up
> having to use seq_cst for the first wmb or make both X and Y (on top of
> the last seq) a store-release, both options are sub-optimal.
seq_cst (except for the fence which is just smp_mb) is a pile of manure,
no doubt about that. :)
Paolo
On 19/04/21 11:36, Peter Zijlstra wrote:
> On Mon, Apr 19, 2021 at 11:02:12AM +0200, Paolo Bonzini wrote:
>>> void writer(void)
>>> {
>>> atomic_store_explicit(&seq, seq+1, memory_order_relaxed);
>>> atomic_thread_fence(memory_order_acquire);
>>
>> This needs to be memory_order_release. The only change in the resulting
>> assembly is that "dmb ishld" becomes "dmb ish", which is not as good as the
>> "dmb ishst" you get from smp_wmb() but not buggy either.
>
> Yuck! And that is what requires the insides to be
> atomic_store_explicit(), otherwise this fence doesn't have to affect
> them.
Not just that, even the write needs to be atomic_store_explicit in order
to avoid a data race.atomic_store_explicit
> I also don't see how this is better than seq_cst.
It is better than seq_cst on TSO architectures. Another possibility is
to use release stores for everything (both increments and the stores
between them).
> But yes, not broken, but also very much not optimal.
Agreed on that, just like RCU/memory_order_consume.
Paolo
On Mon, Apr 19, 2021 at 09:53:06AM +0200, Paolo Bonzini wrote:
> On 19/04/21 09:32, Peter Zijlstra wrote:
> > On Sat, Apr 17, 2021 at 04:51:58PM +0200, Paolo Bonzini wrote:
> > > On 16/04/21 09:09, Peter Zijlstra wrote:
> > > > Well, the obvious example would be seqlocks. C11 can't do them
> > >
> > > Sure it can. C11 requires annotating with (the equivalent of) READ_ONCE all
> > > reads of seqlock-protected fields, but the memory model supports seqlocks
> > > just fine.
> >
> > How does that help?
> >
> > IIRC there's two problems, one on each side the lock. On the write side
> > we have:
> >
> > seq++;
> > smp_wmb();
> > X = r;
> > Y = r;
> > smp_wmb();
> > seq++;
> >
> > Which C11 simply cannot do right because it does't have wmb.
>
> It has atomic_thread_fence(memory_order_release), and
> atomic_thread_fence(memory_order_acquire) on the read side.
https://godbolt.org/z/85xoPxeE5
void writer(void)
{
atomic_store_explicit(&seq, seq+1, memory_order_relaxed);
atomic_thread_fence(memory_order_acquire);
X = 1;
Y = 2;
atomic_store_explicit(&seq, seq+1, memory_order_release);
}
gives:
writer:
adrp x1, .LANCHOR0
add x0, x1, :lo12:.LANCHOR0
ldr w2, [x1, #:lo12:.LANCHOR0]
add w2, w2, 1
str w2, [x0]
dmb ishld
ldr w1, [x1, #:lo12:.LANCHOR0]
mov w3, 1
mov w2, 2
stp w3, w2, [x0, 4]
add w1, w1, w3
stlr w1, [x0]
ret
Which, afaict, is completely buggered. What it seems to be doing is
turning the seq load into a load-acquire, but what we really need is to
make sure the seq store (increment) is ordered before the other stores.
On 19/04/21 10:26, Peter Zijlstra wrote:
> On Mon, Apr 19, 2021 at 09:53:06AM +0200, Paolo Bonzini wrote:
>> On 19/04/21 09:32, Peter Zijlstra wrote:
>>> On Sat, Apr 17, 2021 at 04:51:58PM +0200, Paolo Bonzini wrote:
>>>> On 16/04/21 09:09, Peter Zijlstra wrote:
>>>>> Well, the obvious example would be seqlocks. C11 can't do them
>>>>
>>>> Sure it can. C11 requires annotating with (the equivalent of) READ_ONCE all
>>>> reads of seqlock-protected fields, but the memory model supports seqlocks
>>>> just fine.
>>>
>>> How does that help?
>>>
>>> IIRC there's two problems, one on each side the lock. On the write side
>>> we have:
>>>
>>> seq++;
>>> smp_wmb();
>>> X = r;
>>> Y = r;
>>> smp_wmb();
>>> seq++;
>>>
>>> Which C11 simply cannot do right because it does't have wmb.
>>
>> It has atomic_thread_fence(memory_order_release), and
>> atomic_thread_fence(memory_order_acquire) on the read side.
>
> https://godbolt.org/z/85xoPxeE5
>
> void writer(void)
> {
> atomic_store_explicit(&seq, seq+1, memory_order_relaxed);
> atomic_thread_fence(memory_order_acquire);
This needs to be memory_order_release. The only change in the resulting
assembly is that "dmb ishld" becomes "dmb ish", which is not as good as
the "dmb ishst" you get from smp_wmb() but not buggy either.
The read side can use "dmb ishld" so it gets the same code as Linux.
LWN needs a "C11 memory model for kernel folks" article. In the
meanwhile there is
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0124r4.html
which is the opposite (Linux kernel memory model for C11 folks).
Paolo
>
> X = 1;
> Y = 2;
>
> atomic_store_explicit(&seq, seq+1, memory_order_release);
> }
>
> gives:
>
> writer:
> adrp x1, .LANCHOR0
> add x0, x1, :lo12:.LANCHOR0
> ldr w2, [x1, #:lo12:.LANCHOR0]
> add w2, w2, 1
> str w2, [x0]
> dmb ishld
> ldr w1, [x1, #:lo12:.LANCHOR0]
> mov w3, 1
> mov w2, 2
> stp w3, w2, [x0, 4]
> add w1, w1, w3
> stlr w1, [x0]
> ret
>
> Which, afaict, is completely buggered. What it seems to be doing is
> turning the seq load into a load-acquire, but what we really need is to
> make sure the seq store (increment) is ordered before the other stores.
>
>
On Mon, Apr 19, 2021 at 11:02:12AM +0200, Paolo Bonzini wrote:
> > void writer(void)
> > {
> > atomic_store_explicit(&seq, seq+1, memory_order_relaxed);
> > atomic_thread_fence(memory_order_acquire);
>
> This needs to be memory_order_release. The only change in the resulting
> assembly is that "dmb ishld" becomes "dmb ish", which is not as good as the
> "dmb ishst" you get from smp_wmb() but not buggy either.
Yuck! And that is what requires the insides to be
atomic_store_explicit(), otherwise this fence doesn't have to affect
them.
I also don't see how this is better than seq_cst.
But yes, not broken, but also very much not optimal.
On Mon, Apr 19, 2021 at 11:40:54AM +0200, Paolo Bonzini wrote:
> On 19/04/21 11:36, Peter Zijlstra wrote:
> > On Mon, Apr 19, 2021 at 11:02:12AM +0200, Paolo Bonzini wrote:
> > > > void writer(void)
> > > > {
> > > > atomic_store_explicit(&seq, seq+1, memory_order_relaxed);
> > > > atomic_thread_fence(memory_order_acquire);
> > >
> > > This needs to be memory_order_release. The only change in the resulting
> > > assembly is that "dmb ishld" becomes "dmb ish", which is not as good as the
> > > "dmb ishst" you get from smp_wmb() but not buggy either.
> >
> > Yuck! And that is what requires the insides to be
> > atomic_store_explicit(), otherwise this fence doesn't have to affect
> > them.
>
> Not just that, even the write needs to be atomic_store_explicit in order to
> avoid a data race.atomic_store_explicit
https://wg21.link/P0690
was an attempt to address this, but I don't know if any of the ideas got
adopted in the end.
Will
On Mon, Apr 19, 2021 at 2:36 AM Peter Zijlstra <[email protected]> wrote:
>
> I also don't see how this is better than seq_cst.
>
> But yes, not broken, but also very much not optimal.
I continue to feel like kernel people should just entirely ignore the
C++ memory ordering standard.
It's inferior to what we already have, and simply not helpful. It
doesn't actually solve any problems as far as the kernel is concerned,
and it generates its own set of issues (ie assuming that the compiler
supports it, and assuming the compiler gets it right).
The really subtle cases that it could have been helpful for (eg RCU,
or the load-store control dependencies) were _too_ subtle for the
standard.
And I do not believe Rust changes _any_ of that.
Any kernel Rust code will simply have to follow the LKMM rules, and
use the kernel model for the interfaces. Things like the C++ memory
model is simply not _relevant_ to the kernel.
Linus
On Fri, Apr 16, 2021 at 11:47 AM Paul E. McKenney <[email protected]> wrote:
>
> On Thu, Apr 15, 2021 at 11:04:37PM -0700, Nick Desaulniers wrote:
> > On Thu, Apr 15, 2021 at 9:27 PM Boqun Feng <[email protected]> wrote:
> > >
> > > But I think the Rust Community still wants to have a good memory model,
> > > and they are open to any kind of suggestion and input. I think we (LKMM
> > > people) should really get involved, because the recent discussion on
> > > RISC-V's atomics shows that if we didn't people might get a "broken"
> > > design because they thought C11 memory model is good enough:
> > >
> > > https://lore.kernel.org/lkml/YGyZPCxJYGOvqYZQ@boqun-archlinux/
> > >
> > > And the benefits are mutual: a) Linux Kernel Memory Model (LKMM) is
> > > defined by combining the requirements of developers and the behavior of
> > > hardwares, it's pratical and can be a very good input for memory model
> > > designing in Rust; b) Once Rust has a better memory model, the compiler
> > > technologies whatever Rust compilers use to suppor the memory model can
> > > be adopted to C compilers and we can get that part for free.
> >
> > Yes, I agree; I think that's a very good approach. Avoiding the ISO
> > WG14 is interesting; at least the merits could be debated in the
> > public and not behind closed doors.
>
> WG14 (C) and WG21 (C++) are at least somewhat open. Here are some of
> the proposals a few of us have in flight:
Wow, the working groups have been busy. Thank you Paul and Boqun (and
anyone else on thread) for authoring many of the proposals listed
below. Looks like I have a lot of reading to do to catch up.
Have any of those been accepted yet, or led to amendments to either
language's specs? Where's the best place to track that?
My point has more to do with _participation_. Rust's RFC process is
well documented (https://rust-lang.github.io/rfcs/introduction.html)
and is done via github pull requests[0].
This is a much different process than drafts thrown over the wall.
What hope do any kernel contributors have to participate in the ISO
WGs, other than hoping their contributions to a draft foresee/address
any concerns members of the committee might have? How do members of
the ISO WG communicate with folks interested in the outcomes of their
decisions?
The two processes are quite different; one doesn't require "joining a
national body" (which I assume involves some monetary transaction, if
not for the individual participant, for their employer) for instance.
(http://www.open-std.org/jtc1/sc22/wg14/www/contributing which links
to https://www.iso.org/members.html; I wonder if we have kernel
contributors in those grayed out countries?)
It was always very ironic to me that the most important body of free
software was subject to decisions made by ISO, for better or for
worse. I would think Rust's RFC process would be more accessible to
kernel developers, modulo the anti-github crowd, but with the Rust's
community's values in inclusion I'm sure they'd be happy to accomodate
folks for the RFC process without requiring github. I'm not sure ISO
can be as flexible for non-members.
Either way, I think Rust's RFC process is something worth adding to
the list of benefits under the heading "Why Rust?" in this proposed
RFC.
>
> P2055R0 A Relaxed Guide to memory_order_relaxed
> http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p2055r0.pdf
> P0124R7 Linux-Kernel Memory Model (vs. that of C/C++)
> http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p0124r7.html
> P1726R4 Pointer lifetime-end zap
> http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p1726r4.pdf
> https://docs.google.com/document/d/1MfagxTa6H0rTxtq9Oxyh4X53NzKqOt7y3hZBVzO_LMk/edit?usp=sharing
> P1121R2 Hazard Pointers: Proposed Interface and Wording for Concurrency TS 2
> http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p1121r2.pdf
> P1382R1 volatile_load<T> and volatile_store<T>
> http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1382r1.pdf
> P1122R2 Proposed Wording for Concurrent Data Structures: Read-Copy-Update (RCU)
> http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1122r2.pdf
> https://docs.google.com/document/d/1MfagxTa6H0rTxtq9Oxyh4X53NzKqOt7y3hZBVzO_LMk/edit?usp=sharing
> P0190R4 Proposal for New memory order consume Definition
> http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0190r4.pdf
> P0750R1 Consume
> http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0750r1.html
Does wg14 not participate in these discussions? (Or, is there a lot of
overlap between participants in both?)
http://93.90.116.65/JTC1/SC22/WG14/www/docs/ seems like a list of
proposals and meeting minutes, but all of the above links look like
WG21. The model of decisions being made for C++ then trickling down
to C is definitely curious. Though perhaps for the topics of memory
orderings there's enough overlap between the two languages for it not
to matter.
>
> P1726R4 is of particular concern, along with consume.
[0] You can see all of the existing ones here:
https://github.com/rust-lang/rfcs/tree/master/text, with links to
discussions for each on github. (Here's one that has not been
accepted yet: https://github.com/rust-lang/rfcs/blob/master/text/1937-ques-in-main.md,
see the link to the issue in the rust issue tracker has lots of
comments _from the community_:
https://github.com/rust-lang/rust/issues/43301). I guess the
equivalents for the ISO WGs would be the meeting minutes?
--
Thanks,
~Nick Desaulniers
Hi David,
On Mon, Apr 19, 2021 at 10:01 PM David Sterba <[email protected]> wrote:
>
> for simple functions it's barely parseable but the following is hardly
> readable
>
> translates to
>
> <rust_binder[8787f43e282added]::range_alloc::DescriptorState as core[8787f43e282added]::fmt::Debug>::fmt
Some details can be omitted without much problem, e.g. try the `-i`
option of `c++filt`:
<rust_binder::range_alloc::DescriptorState as core::fmt::Debug>::fmt
Cheers,
Miguel
On Mon, Apr 19, 2021 at 01:35:56PM -0700, Nick Desaulniers wrote:
> On Fri, Apr 16, 2021 at 11:47 AM Paul E. McKenney <[email protected]> wrote:
> >
> > On Thu, Apr 15, 2021 at 11:04:37PM -0700, Nick Desaulniers wrote:
> > > On Thu, Apr 15, 2021 at 9:27 PM Boqun Feng <[email protected]> wrote:
> > > >
> > > > But I think the Rust Community still wants to have a good memory model,
> > > > and they are open to any kind of suggestion and input. I think we (LKMM
> > > > people) should really get involved, because the recent discussion on
> > > > RISC-V's atomics shows that if we didn't people might get a "broken"
> > > > design because they thought C11 memory model is good enough:
> > > >
> > > > https://lore.kernel.org/lkml/YGyZPCxJYGOvqYZQ@boqun-archlinux/
> > > >
> > > > And the benefits are mutual: a) Linux Kernel Memory Model (LKMM) is
> > > > defined by combining the requirements of developers and the behavior of
> > > > hardwares, it's pratical and can be a very good input for memory model
> > > > designing in Rust; b) Once Rust has a better memory model, the compiler
> > > > technologies whatever Rust compilers use to suppor the memory model can
> > > > be adopted to C compilers and we can get that part for free.
> > >
> > > Yes, I agree; I think that's a very good approach. Avoiding the ISO
> > > WG14 is interesting; at least the merits could be debated in the
> > > public and not behind closed doors.
> >
> > WG14 (C) and WG21 (C++) are at least somewhat open. Here are some of
> > the proposals a few of us have in flight:
>
> Wow, the working groups have been busy. Thank you Paul and Boqun (and
> anyone else on thread) for authoring many of the proposals listed
> below. Looks like I have a lot of reading to do to catch up.
And this is only the proposals relating to low-level concurrency.
There are way more proposals relating to vector processing, GPGPUs,
task-based concurrency, and so on. Here is the list of papers submitted
thus far this year:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/
> Have any of those been accepted yet, or led to amendments to either
> language's specs? Where's the best place to track that?
None of them have yet to be accepted. P1121R2 has been recommended for
Concurrency Technical Specification 2, which is a stepping stone to the
International Standard. I hope that P1122R2 will follow soon.
> My point has more to do with _participation_. Rust's RFC process is
> well documented (https://rust-lang.github.io/rfcs/introduction.html)
> and is done via github pull requests[0].
>
> This is a much different process than drafts thrown over the wall.
> What hope do any kernel contributors have to participate in the ISO
> WGs, other than hoping their contributions to a draft foresee/address
> any concerns members of the committee might have? How do members of
> the ISO WG communicate with folks interested in the outcomes of their
> decisions?
I participate in ISO SC22 WG21 (C++) and to a lesser extent WG14 (C).
Participation is key. The various US National Laboratories send quite a
few people, which has a lot to do with these two standards accommodating
their wishes.
> The two processes are quite different; one doesn't require "joining a
> national body" (which I assume involves some monetary transaction, if
> not for the individual participant, for their employer) for instance.
> (http://www.open-std.org/jtc1/sc22/wg14/www/contributing which links
> to https://www.iso.org/members.html; I wonder if we have kernel
> contributors in those grayed out countries?)
Your employer is already a member of WG21 (C++), so there is no ISO
obstacle to participation by you or by your colleagues. If you contact
me offlist, I would be happy to connect you to your employer's WG21
representative.
> It was always very ironic to me that the most important body of free
> software was subject to decisions made by ISO, for better or for
> worse. I would think Rust's RFC process would be more accessible to
> kernel developers, modulo the anti-github crowd, but with the Rust's
> community's values in inclusion I'm sure they'd be happy to accomodate
> folks for the RFC process without requiring github. I'm not sure ISO
> can be as flexible for non-members.
Being a member is not all that necessary. Yes, at the end of the day,
only national bodies can formally vote, but I have not come across a
case of anyone being barred from discussions, paper submissions, or straw
polls (informal votes used to set direction) due to not being a member.
Given that the national bodies are only permitted to comment on and vote
on finished proposals, you can argue that individuals have more influence.
Similarly, it is not necessary to be personally acquainted with me in
order to get patches into Linux-kernel RCU.
> Either way, I think Rust's RFC process is something worth adding to
> the list of benefits under the heading "Why Rust?" in this proposed
> RFC.
Comparing Rust's process to that of the C or C++ standard committees is
best done by someone who understands both, and that set does not appear
to include either you or me. ;-)
I could easily believe that Rust's process is lighter weight, but on
the other hand, I doubt that Rust's definition has legal standing in
any jurisdiction just yet.
> > P2055R0 A Relaxed Guide to memory_order_relaxed
> > http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p2055r0.pdf
> > P0124R7 Linux-Kernel Memory Model (vs. that of C/C++)
> > http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p0124r7.html
> > P1726R4 Pointer lifetime-end zap
> > http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p1726r4.pdf
> > https://docs.google.com/document/d/1MfagxTa6H0rTxtq9Oxyh4X53NzKqOt7y3hZBVzO_LMk/edit?usp=sharing
> > P1121R2 Hazard Pointers: Proposed Interface and Wording for Concurrency TS 2
> > http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p1121r2.pdf
> > P1382R1 volatile_load<T> and volatile_store<T>
> > http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1382r1.pdf
> > P1122R2 Proposed Wording for Concurrent Data Structures: Read-Copy-Update (RCU)
> > http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1122r2.pdf
> > https://docs.google.com/document/d/1MfagxTa6H0rTxtq9Oxyh4X53NzKqOt7y3hZBVzO_LMk/edit?usp=sharing
> > P0190R4 Proposal for New memory order consume Definition
> > http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0190r4.pdf
> > P0750R1 Consume
> > http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0750r1.html
>
> Does wg14 not participate in these discussions? (Or, is there a lot of
> overlap between participants in both?)
> http://93.90.116.65/JTC1/SC22/WG14/www/docs/ seems like a list of
> proposals and meeting minutes, but all of the above links look like
> WG21. The model of decisions being made for C++ then trickling down
> to C is definitely curious. Though perhaps for the topics of memory
> orderings there's enough overlap between the two languages for it not
> to matter.
Back in 2005, WG14 and WG21 agreed that low-level concurrency proposals
would be handled by WG21, and that WG14 members would participate.
But some things required working with both committees, for example,
there is a WG14 counterpart to P1726R4, namely N2443:
http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2443.pdf
> > P1726R4 is of particular concern, along with consume.
>
>
> [0] You can see all of the existing ones here:
> https://github.com/rust-lang/rfcs/tree/master/text, with links to
> discussions for each on github. (Here's one that has not been
> accepted yet: https://github.com/rust-lang/rfcs/blob/master/text/1937-ques-in-main.md,
> see the link to the issue in the rust issue tracker has lots of
> comments _from the community_:
> https://github.com/rust-lang/rust/issues/43301). I guess the
> equivalents for the ISO WGs would be the meeting minutes?
There are minutes on wikis, github issue trackers, follow-on papers,
and so on.
Once things are reasonably well set for a given proposal, the formal
national-body-level processes take over. I have been happy to be able
to sit out that phase for the most part. ;-)
Thanx, Paul
Hi Nick,
On Mon, Apr 19, 2021 at 10:36 PM Nick Desaulniers
<[email protected]> wrote:
>
> This is a much different process than drafts thrown over the wall.
> What hope do any kernel contributors have to participate in the ISO
> WGs, other than hoping their contributions to a draft foresee/address
> any concerns members of the committee might have? How do members of
> the ISO WG communicate with folks interested in the outcomes of their
> decisions?
For WG21, several folks write trip reports of each meeting, and you
can check the status of papers in GitHub at
https://github.com/cplusplus/papers/issues.
For WG14, there are way less papers going on. It is more or less easy
to follow by reading the list of latest additions in the first pages
of the draft, as well as the Editor's Report.
> The two processes are quite different; one doesn't require "joining a
> national body" (which I assume involves some monetary transaction, if
> not for the individual participant, for their employer) for instance.
> (http://www.open-std.org/jtc1/sc22/wg14/www/contributing which links
> to https://www.iso.org/members.html; I wonder if we have kernel
> contributors in those grayed out countries?)
They are indeed very different processes. Being an ISO standard has
advantages and disadvantages.
In any case, I should note that not everyone that goes to the meetings
pays, e.g. some go as guests, some are funded by their country (or the
EU or other organizations), etc.
In fact, the bigger costs, in my experience, are the time commitment
(a week several times a year) and the costs of traveling (before the
pandemic, that is).
Furthermore, contributing proposals does not actually require
attending the meetings nor joining the committee -- some people
contribute to the standards via proxy, i.e. somebody else presents
their proposals in the committee.
> It was always very ironic to me that the most important body of free
> software was subject to decisions made by ISO, for better or for
> worse. I would think Rust's RFC process would be more accessible to
> kernel developers, modulo the anti-github crowd, but with the Rust's
> community's values in inclusion I'm sure they'd be happy to accomodate
> folks for the RFC process without requiring github. I'm not sure ISO
> can be as flexible for non-members.
Well, the kernel already ignores the C standard here and there. ;-) In
the end, it is "just" a standard -- the kernel and compilers can do
something else when they need.
> Either way, I think Rust's RFC process is something worth adding to
> the list of benefits under the heading "Why Rust?" in this proposed
> RFC.
The Rust RFC process has indeed upsides. It is very dynamic and easy
to participate, and allows for anybody to easily comment on proposals,
even anonymously. But, for better or worse, does not lead to an ISO
standard (which some people & companies really value, e.g. as
requirements in contracts etc.).
In the end, writing an RFC is similar to writing a paper for ISO. The
bigger differences, as mentioned above, are on the requirements if you
actually want to go there and present the paper yourself and/or if you
want to have voting rights etc.
Personally, I think some ISO policies could be improved for some types
of standards (or at least let WGs relax them to some degree), but...
Cheers,
Miguel
On 19/04/21 19:14, Linus Torvalds wrote:
> On Mon, Apr 19, 2021 at 2:36 AM Peter Zijlstra <[email protected]> wrote:
>>
>> I also don't see how this is better than seq_cst.
>>
>> But yes, not broken, but also very much not optimal.
>
> I continue to feel like kernel people should just entirely ignore the
> C++ memory ordering standard.
>
> It's inferior to what we already have, and simply not helpful. It
> doesn't actually solve any problems as far as the kernel is concerned,
> and it generates its own set of issues (ie assuming that the compiler
> supports it, and assuming the compiler gets it right).
>
> The really subtle cases that it could have been helpful for (eg RCU,
> or the load-store control dependencies) were _too_ subtle for the
> standard.
>
> And I do not believe Rust changes _any_ of that.
It changes it for the worse, in that access to fields that are shared
across threads *must* either use atomic types (which boil down to the
same compiler intrinsics as the C/C++ memory model) or synchronization
primitives. LKMM operates in the grey area between the C standard and
what gcc/clang actually implement, but there's no such grey area in Rust
unless somebody wants to rewrite arch/*/asm atomic access primitives and
memory barriers in Rust.
Of course it's possible to say Rust code just uses the C/C++/Rust model
and C code follows the LKMM, but that really only delays the inevitable
until a driver is written part in C part in Rust, and needs to perform
accesses outside synchronization primitives.
Paolo
> Any kernel Rust code will simply have to follow the LKMM rules, and
> use the kernel model for the interfaces. Things like the C++ memory
> model is simply not _relevant_ to the kernel.
On Mon, Apr 19, 2021 at 11:38 AM Paolo Bonzini <[email protected]> wrote:
>
> It changes it for the worse, in that access to fields that are shared
> across threads *must* either use atomic types
Well, we won't be using those broken types in the core kernel, so that
would all be entirely on the Rust side.
And I don't expect the Rust side to do a lot of non-locked accesses,
which presumably shouldn't need any of this anyway.
If Rust code ends up accessing actual real kernel data structures with
memory ordering, then that will be to types that do *not* follow the
useless C++ atomics, and that in turn presumably means that it will be
done as "unsafe" helpers that do what the LKMM does (ie READ_ONCE()
and all the rest of it).
Linus
On Mon, Apr 19, 2021 at 09:58:51PM +0200, David Sterba wrote:
> On Fri, Apr 16, 2021 at 07:34:51PM +0200, Miguel Ojeda wrote:
> > something like:
> >
> > [ 0.903456] rust_begin_unwind+0x9/0x10
> > [ 0.903456] ? _RNvNtCsbDqzXfLQacH_4core9panicking9panic_fmt+0x29/0x30
> > [ 0.903456] ? _RNvNtCsbDqzXfLQacH_4core9panicking5panic+0x44/0x50
> > [ 0.903456] ? _RNvCsbDqzXfLQacH_12rust_minimal1h+0x1c/0x20
> > [ 0.903456] ? _RNvCsbDqzXfLQacH_12rust_minimal1g+0x9/0x10
> > [ 0.903456] ? _RNvCsbDqzXfLQacH_12rust_minimal1f+0x9/0x10
> > [ 0.903456] ?
> > _RNvXCsbDqzXfLQacH_12rust_minimalNtB2_11RustMinimalNtCsbDqzXfLQacH_6kernel12KernelModule4init+0x73/0x80
> > [ 0.903456] ? _RNvXsa_NtCsbDqzXfLQacH_4core3fmtbNtB5_5Debug3fmt+0x30/0x30
> > [ 0.903456] ? __rust_minimal_init+0x11/0x20
>
> Are there plans to unmangle the symbols when printing stacks? c++filt
> says:
>
> rust_begin_unwind+0x9/0x10
> ? core[8787f43e282added]::panicking::panic_fmt+0x29/0x30
> ? core[8787f43e282added]::panicking::panic+0x44/0x50
> ? rust_minimal[8787f43e282added]::h+0x1c/0x20
> ? rust_minimal[8787f43e282added]::g+0x9/0x10
> ? rust_minimal[8787f43e282added]::f+0x9/0x10
> ? <rust_minimal[8787f43e282added]::RustMinimal as kernel[8787f43e282added]::KernelModule>::init+0x73/0x80
> ? <bool as core[8787f43e282added]::fmt::Debug>::fmt+0x30/0x30
> ? __rust_minimal_init+0x11/0x20
>
> for simple functions it's barely parseable but the following is hardly
> readable
>
> > _RNvXs5_NtCsbDqzXfLQacH_11rust_binder11range_allocNtB5_15DescriptorStateNtNtCsbDqzXfLQacH_4core3fmt5Debug3fmt+0x60/0x60
>
> translates to
>
> <rust_binder[8787f43e282added]::range_alloc::DescriptorState as core[8787f43e282added]::fmt::Debug>::fmt
Yes, I agree, we need a better story for name mangling.
My proposal is that we store a pretty name which matches the source
(eg rust_binder::range_alloc) and a sha1 of the mangled symbol
(40 bytes of uninteresting hex). Symbol resolution is performed against
the sha1. Printing is of the pretty name. It should be obvious from
the stack trace which variant of a function is being called, no?
On Fri, Apr 16, 2021 at 07:34:51PM +0200, Miguel Ojeda wrote:
> On Fri, Apr 16, 2021 at 3:38 PM Peter Zijlstra <[email protected]> wrote:
> >
> > So if I read all this right, rust compiles to .o and, like any other .o
> > file is then fed into objtool (for x86_64). Did you have any problems
> > with objtool? Does it generate correct ORC unwind information?
>
> I opened an issue a while ago to take a closer look at the ORC
> unwinder etc., so it is in my radar (thanks for raising it up,
> nevertheless!).
>
> Currently, causing a panic in a nested non-inlined function (f -> g ->
> h) in one of the samples with the ORC unwinder enabled gives me
> something like:
>
> [ 0.903456] rust_begin_unwind+0x9/0x10
> [ 0.903456] ? _RNvNtCsbDqzXfLQacH_4core9panicking9panic_fmt+0x29/0x30
> [ 0.903456] ? _RNvNtCsbDqzXfLQacH_4core9panicking5panic+0x44/0x50
> [ 0.903456] ? _RNvCsbDqzXfLQacH_12rust_minimal1h+0x1c/0x20
> [ 0.903456] ? _RNvCsbDqzXfLQacH_12rust_minimal1g+0x9/0x10
> [ 0.903456] ? _RNvCsbDqzXfLQacH_12rust_minimal1f+0x9/0x10
> [ 0.903456] ?
> _RNvXCsbDqzXfLQacH_12rust_minimalNtB2_11RustMinimalNtCsbDqzXfLQacH_6kernel12KernelModule4init+0x73/0x80
> [ 0.903456] ? _RNvXsa_NtCsbDqzXfLQacH_4core3fmtbNtB5_5Debug3fmt+0x30/0x30
> [ 0.903456] ? __rust_minimal_init+0x11/0x20
Are there plans to unmangle the symbols when printing stacks? c++filt
says:
rust_begin_unwind+0x9/0x10
? core[8787f43e282added]::panicking::panic_fmt+0x29/0x30
? core[8787f43e282added]::panicking::panic+0x44/0x50
? rust_minimal[8787f43e282added]::h+0x1c/0x20
? rust_minimal[8787f43e282added]::g+0x9/0x10
? rust_minimal[8787f43e282added]::f+0x9/0x10
? <rust_minimal[8787f43e282added]::RustMinimal as kernel[8787f43e282added]::KernelModule>::init+0x73/0x80
? <bool as core[8787f43e282added]::fmt::Debug>::fmt+0x30/0x30
? __rust_minimal_init+0x11/0x20
for simple functions it's barely parseable but the following is hardly
readable
> _RNvXs5_NtCsbDqzXfLQacH_11rust_binder11range_allocNtB5_15DescriptorStateNtNtCsbDqzXfLQacH_4core3fmt5Debug3fmt+0x60/0x60
translates to
<rust_binder[8787f43e282added]::range_alloc::DescriptorState as core[8787f43e282added]::fmt::Debug>::fmt
On Mon, Apr 19, 2021 at 10:18 PM Matthew Wilcox <[email protected]> wrote:
>
> Yes, I agree, we need a better story for name mangling.
> My proposal is that we store a pretty name which matches the source
> (eg rust_binder::range_alloc) and a sha1 of the mangled symbol
> (40 bytes of uninteresting hex). Symbol resolution is performed against
> the sha1. Printing is of the pretty name. It should be obvious from
> the stack trace which variant of a function is being called, no?
If the pretty name is only `rust_binder::range_alloc`, that would not
be enough, since (in this case) that is a module name (i.e. the
namespace of the `DescriptorState` type). The function being called
here is `fmt` (the one outside the `<>`), which is a method of the
`Debug` trait.
We could perhaps reduce this down to:
rust_binder::range_alloc::DescriptorState::fmt
without much ambiguity (in most cases).
Cheers,
Miguel
On Fri, Apr 16, 2021 at 10:39 AM Willy Tarreau <[email protected]> wrote:
>
> resources usage, I'm really not convinced at all it's suited for
> low-level development. I understand the interest of the experiment
> to help the language evolve into that direction, but I fear that
> the kernel will soon be as bloated and insecure as a browser, and
> that's really not to please me.
Dunno, I don't think the introduction of Rust made Firefox _more_ insecure.
https://wiki.mozilla.org/Oxidation#Within_Firefox
I pray no executives ever see Dmitry Vyukov's 2019 Linux Plumbers Conf
talk "Reflections on kernel quality, development process and testing."
https://www.youtube.com/watch?v=iAfrrNdl2f4
or his 2018 Linux Security Summit talk "Syzbot and the Tale of
Thousand Kernel Bugs" https://www.youtube.com/watch?v=qrBVXxZDVQY
(and they're just fuzzing the syscall interface and USB devices.
Imagine once folks can more easily craft malformed bluetooth and wifi
packets.)
I'd imagine the first term that comes to mind for them might be
"liability." They are quite sensitive to these vulnerabilities with
silly names, logos, and websites. There are many of us that believe
an incremental approach of introducing a memory safe language to our
existing infrastructure at the very least to attempt to improve the
quality of drivers for those that choose to use such tools is a better
approach.
I think a lot of the current discussion picking nits in syntax, format
of docs, ease of installation, or theoretical memory models for which
no language (not even the one the kernel is implemented in) provides
all rightly should still be added to a revised RFC under "Why not
[Rust]?" but perhaps are severely overlooking the benefits. A
tradeoff for sure though.
Really, a key point is that a lot of common mistakes in C are compile
time errors in Rust. I know no "true" kernel dev would make such
mistakes in C, but is there nothing we can do to help our peers
writing drivers? The point is to transfer cost from runtime to
compile time to avoid costs at runtime; like all of the memory safety
bugs which are costing our industry.
Curiously recurring statistics:
https://www.zdnet.com/article/microsoft-70-percent-of-all-security-bugs-are-memory-safety-issues/
"Microsoft security engineer Matt Miller said that over the last 12
years, around 70 percent of all Microsoft patches were fixes for
memory safety bugs."
https://www.chromium.org/Home/chromium-security/memory-safety
"The Chromium project finds that around 70% of our serious security
bugs are memory safety problems."
https://security.googleblog.com/2021/01/data-driven-security-hardening-in.html
(59% of Critical and High severity vulnerabilities fixed in Android
Security Bulletins in 2019 are classified as "Memory," FWIW)
https://hacks.mozilla.org/2019/02/rewriting-a-browser-component-in-rust/
"If we’d had a time machine and could have written this component in
Rust from the start, 51 (73.9%) of these bugs would not have been
possible."
--
Thanks,
~Nick Desaulniers
Hi Nick,
On Mon, Apr 19, 2021 at 05:24:33PM -0700, Nick Desaulniers wrote:
> I don't think the introduction of Rust made Firefox _more_ insecure.
> https://wiki.mozilla.org/Oxidation#Within_Firefox
Browsers are human interfaces and do not fundamentally require low
level access to memory/hardware/whatever. They can be written in
about any language, only the resource usage and performance will
make a difference. As such, some were even written in Java or JS
for example.
Operating systems, and particularly drivers *do* require low-level
accesses, and stuff that can hardly be abstracted or understood by
a compiler. You may have to perform two 16-bit reads/writes on a
32-bit MMIO address to perform an operation and the compiler does
not have to know it, just to obey.
> Really, a key point is that a lot of common mistakes in C are compile
> time errors in Rust. I know no "true" kernel dev would make such
> mistakes in C,
Everyone makes mistakes, the level of attention varies over time and
the focus often changes when dealing with build errors. How many time
some of us facing a bug remembered having changed the code very late
after a build error, and being less careful from this point when the
goal changed from "let's do it right" to "let's get this to build" ?
> but is there nothing we can do to help our peers
> writing drivers? The point is to transfer cost from runtime to
> compile time to avoid costs at runtime; like all of the memory safety
> bugs which are costing our industry.
And do we have stats on the number of logical bugs, some of which are
caused by developers trying to work around compilers' stubbornness ?
For me, personally speaking, they have *increased* over time, usually
trying to avoid some annoying modern gcc warnings, resulting in integer
casts being placed close to string formats, or returns being placed in
switch/case to avoid the fall-through warning, etc. Thus I'm worried that
a non-negligible part of the 70% of bugs caused by memory safety issues
could be replaced with logic bugs to get to the point where the rust
compiler finally accepts to compile the code. It makes me think about
researchers trying to reduce the causes of certain deaths and claiming
to "save lives" while in the end the people they "save" will simply die
from something else.
And I'm not particularly trying to blindly defend C here. I'm complaining
every single day about some of its shortcomings like the vast amount of
UB, stupid type promotion, counter-intuitive operators precedence when
combining bit-ops with arithmetic, limited size of enums, lack of rotate
operator, strict aliasing, or the recourse to asm() statements every 10
lines to do stuff that can hardly be expressed in a way understandable
by a compiler. I'm just seeing that a lot of the griefs I'm having
against C come from the compiler trying to be too smart or too stubborn,
so giving even more of the handle to a compiler doesn't appeal me at all.
In addition, we all know how painful it is to work around compiler bugs
by writing complex code that carefully avoids certain constructs. I'm
wondering if we'll still have that luxury with a stricter compiler, or
if the only response will have to be between "let's disable this driver
that does not compile" or "please force distros to upgrade their
compilers".
But we'll see :-/
Regards,
Willy
On Mon, Apr 19, 2021 at 05:24:33PM -0700, Nick Desaulniers wrote:
> On Fri, Apr 16, 2021 at 10:39 AM Willy Tarreau <[email protected]> wrote:
> >
> > resources usage, I'm really not convinced at all it's suited for
> > low-level development. I understand the interest of the experiment
> > to help the language evolve into that direction, but I fear that
> > the kernel will soon be as bloated and insecure as a browser, and
> > that's really not to please me.
>
> Dunno, I don't think the introduction of Rust made Firefox _more_ insecure.
> https://wiki.mozilla.org/Oxidation#Within_Firefox
>
> I pray no executives ever see Dmitry Vyukov's 2019 Linux Plumbers Conf
> talk "Reflections on kernel quality, development process and testing."
> https://www.youtube.com/watch?v=iAfrrNdl2f4
> or his 2018 Linux Security Summit talk "Syzbot and the Tale of
> Thousand Kernel Bugs" https://www.youtube.com/watch?v=qrBVXxZDVQY
> (and they're just fuzzing the syscall interface and USB devices.
> Imagine once folks can more easily craft malformed bluetooth and wifi
> packets.)
>
> I'd imagine the first term that comes to mind for them might be
> "liability." They are quite sensitive to these vulnerabilities with
> silly names, logos, and websites. There are many of us that believe
> an incremental approach of introducing a memory safe language to our
> existing infrastructure at the very least to attempt to improve the
> quality of drivers for those that choose to use such tools is a better
> approach.
I would LOVE it if some "executives" would see the above presentations,
because then they would maybe actually fund developers to fix bugs and
maintain the kernel code, instead of only allowing them to add new
features.
Seriously, that's the real problem, that Dmitry's work has exposed, the
lack of people allowed to do this type of bugfixing and maintenance on
company time, for something that the company relies on, is a huge issue.
"executives" feel that they are willing to fund the initial work and
then "throw it over the wall to the community" once it is merged, and
then they can forget about it as "the community" will maintain it for
them for free. And that's a lie, as Dmitry's work shows.
The world creates new use cases and testing ability all the time, which
exposes bugs that have been around in old code. Once the bugs are fixed
in that layer of code, the next layer down can finally be tested and
then look, more corner cases of issues.
Rewriting the kernel in another language is not going to fix the
majority of the issues that fuzzing finds here automagically, as that
work has exposed us to things like fault-injection and malicious USB
packets that no language would have saved us from "automatically". All
of those code paths deal with "unsafe" data that doesn't magically
become "safe" because we switch languages.
And over time, what we have determined is "safe" has changed! People
forget that only a few years ago have we decided that the kernel now has
to protect userspace programs from malicious hardware. That's a major
shift in thinking, now data that we used to blindly trust can not be
trusted at all. And "executives" want us to fix all of those issues for
free, when really it's a major design shift for loads of kernel
subsystems to handle this new threat model.
So yes, please spread that talk around. Maybe then will we actually get
funding and support to FIX the bugs that those tools test. Right now,
the primary fixer of those findings are _INTERNS_ as that's all
companies are willing to fund to fix this type of thing.
And don't get me started on the inability for "executives" to fund other
parts of Linux that they rely on, because they want "other companies" to
do it instead. The tragedy-of-the-commons is a real threat to Linux,
and always has been...
thanks,
greg k-h
On Tue, Apr 20, 2021 at 07:56:18AM +0200, Greg Kroah-Hartman wrote:
> I would LOVE it if some "executives" would see the above presentations,
> because then they would maybe actually fund developers to fix bugs and
> maintain the kernel code, instead of only allowing them to add new
> features.
>
> Seriously, that's the real problem, that Dmitry's work has exposed, the
> lack of people allowed to do this type of bugfixing and maintenance on
> company time, for something that the company relies on, is a huge issue.
> "executives" feel that they are willing to fund the initial work and
> then "throw it over the wall to the community" once it is merged, and
> then they can forget about it as "the community" will maintain it for
> them for free. And that's a lie, as Dmitry's work shows.
That's sadly the eternal situation, and I'm suspecting that software
development and maintenance is not identified as a requirement for a
large number of hardware vendors, especially on the consumer side where
margins are lower. A contractor is paid to develop a driver, *sometimes*
to try to mainline it (and the later they engage with the community, the
longer it takes in round trips), and once the code finally gets merged,
all the initial budget is depleted and no more software work will be
done.
Worse, we could imagine kicking unmaintained drivers faster off the
tree, but that would actually help these unscrupulous vendors by
forcing their customers to switch to the new model :-/ And most of
them wouldn't care either if their contributions were refused based
on their track record of not maintaining their code, since they often
see this as a convenience to please their customers and not something
they need (after all, relying on a bogus and vulnerable BSP has never
prevented from selling a device, quite the opposite).
In short, there is a parallel universe where running highly bogus and
vulnerable out-of-tree code seems like the norm and where there is no
sort of care for what is mainlined as it's possibly just made to look
"cool".
We also need to recognize that it's expectable that some vendors are
not willing to engage on supporting a driver for a decade if they
expect their device to last 5 years only, and maybe we should make
some rules clear about mainlining drivers and what to expect for
users (in which case the end of support would be clear and nobody
would be surprised if the driver is removed at the end of its
maintenance, barring a switch to a community maintainer).
Just my two cents,
Willy
Hi folks,
"we will do less critical stuff, like device drivers, first".
OK I mostly do device drivers. Kind of like it. So I'd like to provide
feedback from that angle.
On Fri, Apr 16, 2021 at 4:22 AM Wedson Almeida Filho
<[email protected]> wrote:
> We don't intend to directly expose C data structures to Rust code (outside the
> kernel crate). Instead, we intend to provide wrappers that expose safe
> interfaces even though the implementation may use unsafe blocks. So we expect
> the vast majority of Rust code to just care about the Rust memory model.
I'm a bit worried about this.
I am sure you are aware of this document:
Documentation/process/stable-api-nonsense.rst
We really like to change the internal APIs of the kernel, and it sounds to
me like Rust really likes a rust-side-vs-C-side approach to APIs, requiring
these wrappers to be written and maintained all over the place, and that
is going to affect the mobility of the kernel-internal APIs and make them
less mobile.
If it means I need to write and review less patches related to NULL
dereference and use-after-free etc etc, then it may very well be worth
it.
But as subsystem maintainer I'd like a clear picture of this wrapper
overhead, what does it usually entail? A typical kernel API has
vtable and a few variables, not much more than that.
I go to patch 12/13 and I see things like this:
+/// A descriptor of wrapped list elements.
+pub trait GetLinksWrapped: GetLinks {
+ /// Specifies which wrapper (e.g., `Box` and `Arc`) wraps the list entries.
+ type Wrapped: Wrapper<Self::EntryType>;
+}
+
+impl<T: ?Sized> GetLinksWrapped for Box<T>
+where
+ Box<T>: GetLinks,
+{
+ type Wrapped = Box<<Box<T> as GetLinks>::EntryType>;
+}
+
+impl<T: GetLinks + ?Sized> GetLinks for Box<T> {
+ type EntryType = T::EntryType;
+ fn get_links(data: &Self::EntryType) -> &Links<Self::EntryType> {
+ <T as GetLinks>::get_links(data)
+ }
+}
My God. Lose the horrible CamelCase to begin with. I hope the
language spec does not mandate that because our kernel C style
does not use it.
It becomes obvious that as subsystem maintainer for the Linux kernel
a casual drive-by experience with Rust is not going to suffice by far.
All subsystem maintainers are expected to understand and maintain
wrappers like these, right? That means all subsystem maintainers need
to be elevated to understand the above without effort if you wake them
up in their sleep at 4 in the morning.
This makes me a bit sceptic.
Get me right, we are of course good at doing really complicated stuff,
that's what engineers do. But we are not Iron Man. We need a clear
way into understanding and maintaining wrappers and we need support
with it when we don't understand it, so the kernel would need a Rust
wrapper maintainer that we can trust to stay around for the long term,
i.e. until their retirement, while actively teaching others for decades.
For an example see how RCU is maintained.
Developing trust in the people Miguel and Wedson is going to be more
important than trust in Google the company for this.
Yours,
Linus Walleij
From: Linus Walleij
> Sent: 22 April 2021 11:03
...
> I go to patch 12/13 and I see things like this:
>
> +/// A descriptor of wrapped list elements.
> +pub trait GetLinksWrapped: GetLinks {
> + /// Specifies which wrapper (e.g., `Box` and `Arc`) wraps the list entries.
> + type Wrapped: Wrapper<Self::EntryType>;
> +}
> +
> +impl<T: ?Sized> GetLinksWrapped for Box<T>
> +where
> + Box<T>: GetLinks,
> +{
> + type Wrapped = Box<<Box<T> as GetLinks>::EntryType>;
> +}
> +
> +impl<T: GetLinks + ?Sized> GetLinks for Box<T> {
> + type EntryType = T::EntryType;
> + fn get_links(data: &Self::EntryType) -> &Links<Self::EntryType> {
> + <T as GetLinks>::get_links(data)
> + }
> +}
>
> My God. Lose the horrible CamelCase to begin with. I hope the
> language spec does not mandate that because our kernel C style
> does not use it.
That:
1) Looks as though it could be generated by token pasting in a #define.
2) Seems to be full of what look like casts.
I really wouldn't want to bump into multiple copies of it.
The other issue is that (all most) all uses of a symbol
can be found by running:
grep -r --include '*.[chsS]' '\<symbol\>' .
often used as:
vi `grep -l -r '\<symbol\>' .`
But it looks like the rust wrappers are going to break that.
David
-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK
Registration No: 1397386 (Wales)
First of all, thanks for the thoughtful feedback.
On Thu, Apr 22, 2021 at 12:03:13PM +0200, Linus Walleij wrote:
> I am sure you are aware of this document:
> Documentation/process/stable-api-nonsense.rst
>
> We really like to change the internal APIs of the kernel, and it sounds to
> me like Rust really likes a rust-side-vs-C-side approach to APIs, requiring
> these wrappers to be written and maintained all over the place, and that
> is going to affect the mobility of the kernel-internal APIs and make them
> less mobile.
The Rust-side-vs-C-side is because we want to provide an environment where we
can write kernel code (e.g., a driver) and if we stick to safe code (i.e.,
we don't use the Rust keyword "unsafe"), then we can be confident that our
code is free of memory safety issues (assuming, of course, that the abstractions
are sound).
Calling C functions directly would not allow us to do this.
As for mobility, I have the impression that this could potentially increase
mobility in that for Rust maintainers would need to change one place (the
wrapper) as opposed to a number of drivers using an API (if it's mostly an
argument/change kind of thing). Of course, if it's something like removing an
API then we'd have to change everywhere.
I'd like to reassure you that it is not our intention to create a stable api,
restrict mobility, or anything of that sort. Though I do acknowledge Rust may
complicate things (more on this below).
> If it means I need to write and review less patches related to NULL
> dereference and use-after-free etc etc, then it may very well be worth
> it.
Indeed that's part of our goal. A class of vulnerabilities is removed by
construction; others are harder to create accidentally. Reviewers would also
know that unsafe blocks need extra attention.
> But as subsystem maintainer I'd like a clear picture of this wrapper
> overhead, what does it usually entail? A typical kernel API has
> vtable and a few variables, not much more than that.
>
> I go to patch 12/13 and I see things like this:
>
> +/// A descriptor of wrapped list elements.
> +pub trait GetLinksWrapped: GetLinks {
> + /// Specifies which wrapper (e.g., `Box` and `Arc`) wraps the list entries.
> + type Wrapped: Wrapper<Self::EntryType>;
> +}
> +
> +impl<T: ?Sized> GetLinksWrapped for Box<T>
> +where
> + Box<T>: GetLinks,
> +{
> + type Wrapped = Box<<Box<T> as GetLinks>::EntryType>;
> +}
> +
> +impl<T: GetLinks + ?Sized> GetLinks for Box<T> {
> + type EntryType = T::EntryType;
> + fn get_links(data: &Self::EntryType) -> &Links<Self::EntryType> {
> + <T as GetLinks>::get_links(data)
> + }
> +}
We want the runtime overhead to be zero. During development, as you rightly
point out, there is the overhead of creating and maintaining these abstractions
for use in Rust. The code above is not a good example of a wrapper because it's
not wrapping kernel C functionality.
A better example is Pages, which wraps a pointer to struct page:
pub struct Pages<const ORDER: u32> {
pages: *mut bindings::page,
}
If you call Pages::new(), alloc_pages() is called and returns a
KernelResult<Pages>. If the allocation fails you get an error back, otherwise
you get the pages: there is no possibility of forgetting to check the return
value and accidentally dereferencing a NULL pointer.
We have ORDER as a compile-time argument to the type, so we know at compile-time
how many pages we have at no additional runtime cost. So, for example, when we
have to free the pages, the destructor knows what the right argument is when
calling free_pages.
The fact that you have a destructor also ensures that you don't accidentally
forget to free the pages, so no leaks. (We don't have it implemented because
we haven't needed it yet, but we can have get_page/put_page with proper
ownership, i.e., after the equivalent of put_page, you can no longer touch the
page, enforced at compile time).
We provide an `insert_page` associated function that maps the given page to a
vma by calling vm_insert_page. (Only works if ORDER is zero.)
We provide a `kmap` associated function that maps one of the pages and returns a
mapping, which itself has a wrapper type that ensures that kunmap is called when
it goes out of scope.
Anyway, what I'm trying to show here is that the wrappers are quite thin and are
intended to enforce safety (where possible) and correct usage. Does it make
sense? I'm glad to go into more details if desired.
> All subsystem maintainers are expected to understand and maintain
> wrappers like these, right? That means all subsystem maintainers need
> to be elevated to understand the above without effort if you wake them
> up in their sleep at 4 in the morning.
I suppose they'd need to understand the wrappers that I talked about above, not
the ones you copied (those are wrapping something else and maintainers of other
subsystems are not expected to write this sort of code).
There are other possible approaches too:
1. Explicitly exclude Rust support from certain subsystems, say, no Rust USB
drivers (just a random example).
2. Maintainers may choose to not care about Rust, breaking it on api changes.
Naturally I'd prefer Rust support to be a first-class citizen but I mention the
above for completeness.
> Get me right, we are of course good at doing really complicated stuff,
> that's what engineers do. But we are not Iron Man. We need a clear
> way into understanding and maintaining wrappers and we need support
> with it when we don't understand it, so the kernel would need a Rust
> wrapper maintainer that we can trust to stay around for the long term,
> i.e. until their retirement, while actively teaching others for decades.
> For an example see how RCU is maintained.
Agreed. The only part that I'm not sure about is whether we need to put all the
burden on a single person for the rest of their career. In the beginning, of
course, but over time I would expect (hope?) experts would emerge and some of
the load would be distributed.
Cheers,
-Wedson
Hi Linus,
Thanks for all those very good questions (and thanks for the positive
tone!). I will try to complement Wedson's answer in a couple places.
On Thu, Apr 22, 2021 at 12:03 PM Linus Walleij <[email protected]> wrote:
>
> But as subsystem maintainer I'd like a clear picture of this wrapper
> overhead, what does it usually entail? A typical kernel API has
> vtable and a few variables, not much more than that.
If you mean runtime-overhead, i.e. performance, it should be very
small or even zero. It should be possible to perform LTO across
languages too.
If you mean source code overhead, or cognitive overhead, then it is
quite a bit, yes. Please see below.
> It becomes obvious that as subsystem maintainer for the Linux kernel
> a casual drive-by experience with Rust is not going to suffice by far.
>
> All subsystem maintainers are expected to understand and maintain
> wrappers like these, right? That means all subsystem maintainers need
> to be elevated to understand the above without effort if you wake them
> up in their sleep at 4 in the morning.
I would say so, at least if longer-term a substantial amount of new
drivers are written in Rust. That is why I mentioned this as the very
first thing in the RFC. Rust does require some learning to use, even
by C experts.
Having said that, someone that is already a kernel developer and/or a
C expert is in a very good position to learn how Rust approaches
things and the main "new" concepts it introduces.
In the end, Rust is addressing some of the familiar problems that we
face when programming in C and C++.
> Get me right, we are of course good at doing really complicated stuff,
> that's what engineers do. But we are not Iron Man. We need a clear
> way into understanding and maintaining wrappers and we need support
> with it when we don't understand it, so the kernel would need a Rust
> wrapper maintainer that we can trust to stay around for the long term,
> i.e. until their retirement, while actively teaching others for decades.
> For an example see how RCU is maintained.
I hear you! I do not think it will take decades for kernel developers
to get up to speed, but I agree that having some help/backup is a very
good idea in the beginning.
Our hope is that, if Rust advantages prove themselves, then it will
the subsystem maintainers the ones that will want to create and
maintain the wrappers so that drivers in their tree are easier to
maintain and less prone to mistakes ;-)
Cheers,
Miguel
Hi Wedson,
I try to provide a good answer so I did sit down and look a bit more
at rust and looked over your Binder example to at least reach the
level of "a little knowledge of something is dangerous".
For the record I kind of like the language.
On Thu, Apr 22, 2021 at 5:24 PM Wedson Almeida Filho
<[email protected]> wrote:
> > We really like to change the internal APIs of the kernel, and it sounds to
> > me like Rust really likes a rust-side-vs-C-side approach to APIs, requiring
> > these wrappers to be written and maintained all over the place, and that
> > is going to affect the mobility of the kernel-internal APIs and make them
> > less mobile.
>
> The Rust-side-vs-C-side is because we want to provide an environment where we
> can write kernel code (e.g., a driver) and if we stick to safe code (i.e.,
> we don't use the Rust keyword "unsafe"), then we can be confident that our
> code is free of memory safety issues (assuming, of course, that the abstractions
> are sound).
>
> Calling C functions directly would not allow us to do this.
I get it. I think.
> > If it means I need to write and review less patches related to NULL
> > dereference and use-after-free etc etc, then it may very well be worth
> > it.
>
> Indeed that's part of our goal. A class of vulnerabilities is removed by
> construction; others are harder to create accidentally. Reviewers would also
> know that unsafe blocks need extra attention.
Fair enough. What we need to know is what these unsafe blocks
are going to be.
> We want the runtime overhead to be zero. During development, as you rightly
> point out, there is the overhead of creating and maintaining these abstractions
> for use in Rust. The code above is not a good example of a wrapper because it's
> not wrapping kernel C functionality.
For device drivers you will certainly have to wrap assembly as well.
Or C calls that only contain assembly to be precise.
A typical example is the way device drivers talk to actual hardware:
readl()/writel(), readw()/writew(), readb()/writeb() for memory-mapped
IO or inb()/outb() for port-mapped I/O.
So there is for example this (drivers/gpio/gpio-pl061.c):
writeb(pl061->csave_regs.gpio_is, pl061->base + GPIOIS);
writeb(pl061->csave_regs.gpio_ibe, pl061->base + GPIOIBE);
writeb(pl061->csave_regs.gpio_iev, pl061->base + GPIOIEV);
writeb(pl061->csave_regs.gpio_ie, pl061->base + GPIOIE);
We write a number of u32 into u32 sized registers, this
pl061->base is a void __iomem * so a pretty unsafe thing to
begin with and then we add an offset to get to the register
we want.
writel() on ARM for example turns into (arch/arm/include/asm/io.h):
static inline void __raw_writel(u32 val, volatile void __iomem *addr)
{
asm volatile("str %1, %0"
: : "Qo" (*(volatile u32 __force *)addr), "r" (val));
}
This is usually sprinkled all over a device driver, called in loops etc.
Some of these will contain things like buffer drains and memory
barriers. Elaborately researched for years so they will need to
be there.
I have no clue how this thing would be expressed in Rust.
Even less how it would call the right code in the end.
That makes me feel unsafe and puzzled so this is a specific
area where "the Rust way" needs to be made very tangible
and easy to understand.
How would I write these 4 registers in Rust? From the actual
statements down to the CPU instructions, top to bottom,
that is what a driver writer wants to know.
If the result of the exercise is that a typical device driver
will contain more unsafe code than not, then device drivers
are not a good starting point for Rust in the Linux kernel.
In that case I would recommend that Rust start at a point
where there is a lot of abstract code that is prone to the
kind of problems that Rust is trying to solve. My intuition
would be such things as network protocols. But I may be
wrong.
I worry that it may become evident that introducing Rust
in device drivers is *only* suggested because the number
of affected platforms can be controlled (lacking some
compiler arch targets?) rather than that being a place
that needs memory safety. And then I think it is just a
playground for Rust experiments and need to be proposed
as such. But the idea was a real deployment I suppose.
> A better example is Pages, which wraps a pointer to struct page:
>
> pub struct Pages<const ORDER: u32> {
> pages: *mut bindings::page,
> }
>
> If you call Pages::new(), alloc_pages() is called and returns a
> KernelResult<Pages>. If the allocation fails you get an error back, otherwise
> you get the pages: there is no possibility of forgetting to check the return
> value and accidentally dereferencing a NULL pointer.
This is really neat. I think it is a good example where Rust
really provides the right tool for the job.
And it is very far away from any device driver. Though some
drivers need pages.
(...)
> Anyway, what I'm trying to show here is that the wrappers are quite thin and are
> intended to enforce safety (where possible) and correct usage. Does it make
> sense? I'm glad to go into more details if desired.
It reminds me of Haskell monads for some reason.
This is true for any constrained language. I suppose we could write
kernel modules in Haskell as well, or Prolog, given the right wrappers,
and that would also attain the same thing: you get the desired
restrictions in the target language by way of this adapter.
I don't have a problem with that.
The syntax and semantic meaning of things with lots of
impl <T: ?Sized> Wrapper<T> for ... is just really intimidating
but I suppose one can learn it. No big deal.
What I need to know as device driver infrastructure maintainer is:
1. If the language is expressive enough to do what device driver
authors need to do in an efficient and readable manner which
is as good or better than what we have today.
2. Worry about double implementations of core library functions.
3. Kickback in practical problem solving.
This will be illustrated below.
Here is a device driver example that I wrote and merged
just the other week (drivers/iio/magnetometer/yamaha-yas530.c)
it's a nasty example, so I provide it to make a point.
static void yas53x_extract_calibration(u8 *data, struct yas5xx_calibration *c)
{
u64 val = get_unaligned_be64(data);
/*
* Bitfield layout for the axis calibration data, for factor
* a2 = 2 etc, k = k, c = clock divider
*
* n 7 6 5 4 3 2 1 0
* 0 [ 2 2 2 2 2 2 3 3 ] bits 63 .. 56
* 1 [ 3 3 4 4 4 4 4 4 ] bits 55 .. 48
* 2 [ 5 5 5 5 5 5 6 6 ] bits 47 .. 40
* 3 [ 6 6 6 6 7 7 7 7 ] bits 39 .. 32
* 4 [ 7 7 7 8 8 8 8 8 ] bits 31 .. 24
* 5 [ 8 9 9 9 9 9 9 9 ] bits 23 .. 16
* 6 [ 9 k k k k k c c ] bits 15 .. 8
* 7 [ c x x x x x x x ] bits 7 .. 0
*/
c->a2 = FIELD_GET(GENMASK_ULL(63, 58), val) - 32;
c->a3 = FIELD_GET(GENMASK_ULL(57, 54), val) - 8;
c->a4 = FIELD_GET(GENMASK_ULL(53, 48), val) - 32;
c->a5 = FIELD_GET(GENMASK_ULL(47, 42), val) + 38;
c->a6 = FIELD_GET(GENMASK_ULL(41, 36), val) - 32;
c->a7 = FIELD_GET(GENMASK_ULL(35, 29), val) - 64;
c->a8 = FIELD_GET(GENMASK_ULL(28, 23), val) - 32;
c->a9 = FIELD_GET(GENMASK_ULL(22, 15), val);
c->k = FIELD_GET(GENMASK_ULL(14, 10), val) + 10;
c->dck = FIELD_GET(GENMASK_ULL(9, 7), val);
}
This extracts calibration for the sensor from an opaque
chunk of bytes. The calibration is stuffed into sequences of
bits to save space at different offsets and lengths. So we turn
the whole shebang passed in the u8 *data into a 64bit
integer and start picking out the pieces we want.
We know a priori that u8 *data will be more than or equal
to 64 bits of data. (Which is another problem but do not
focus on that, let us look at this function.)
I have no idea how to perform this in
Rust despite reading quite a lot of examples. We have
created a lot of these helpers like FIELD_GET() and
that make this kind of operations simple.
1. Expressiveness of language.
If you look in include/linux/bitfield.h you can see how
this is elaborately implemented to be "a bit" typesafe
and if you follow the stuff around you will find that in
some cases it will resolve into per-CPU assembly
bitwise operations for efficiency. It's neat, it has this
nice handicrafty feeling to it, we control the machine
all the way down.
But that took a few years to get here, and wherever
we want to write a device driver in
Rust this kind of stuff is (I suspect) something that is
going to have to be reinvented, in Rust.
So this is where Rust maintainers will be needed. I will
say something like "I need <linux/bitfield.h>
in Rust" which I guess will eventually become a
"use linux::bitfield" or something like that. Please
fill in the blanks. In the beginning pushing tasks like
that back on the driver writers will just encourage them
to go and write the driver in C. So the maintainers need
to pick it up.
2. Duplication of core libraries.
I worry about that this could quite soon result in two
implementations of bitfield: one in C and one in Rust.
Because the language will have its preferred idiomatic
way of dealing with this, on the level above the
per-arch assembly optimized bitwise instructions
that need to be wrapped nicely for performance.
Which means wrappers all the way down. (Oh well.)
But double maintenance. Multiply with the number
of such kernel abstractions we have. So it better not
happen too much or pay off really well.
I would be worried if we need to say to a submitted device
driver written in Rust: "I know better ways to code this in
C so rewrite it in C". That's not gonna be popular, and
I would worry about angry Rust developers being required
to reinvent the world just because of some pesky review
comments about what we can do in C.
3. Kickback in practical problem solving.
Believe it or not device driver authors are not mainly
interested in memory safety, overruns, dangling pointers,
memory leaks etc. Maybe in a perfect world they
would/should. But they are interested in getting hardware
to work and want a toolbox that gives the shortest path
from A to B. Telling them (or subsystem maintainers) all
about how these things are solved elegantly by Rust is
not a selling point.
So if Rust makes it easier or at least equal to express
the logic with less lines of readable code, that is a
selling point. (Less lines of code that is unintelligible
and hard to read is not a good sell.) I am not referring
to matter of taste here: let's assume intermediary
experience with the language and some talent. There
will always be outliers and there is always a threshold
with any language.
One way of being better would be through me not having to
merge patches of NULL checks and misc things found by
static and dynamic inspection tools such as smatch,
coccinelle, KASan, ... etc etc. I think that is where Rust
would provide real kickback. Make
these never happen. Create less of this. But incidentally
that is not very common in my subsystems, only one patch
in 100 or 50 is about this kind of stuff.
So I do see some upsides here, maybe not super much.
> There are other possible approaches too:
> 1. Explicitly exclude Rust support from certain subsystems, say, no Rust USB
> drivers (just a random example).
With the wrappers being written and submitted to the subsystem
maintainer their buy-in will be the only way in, as our little
ecosystem works that way.
> 2. Maintainers may choose to not care about Rust, breaking it on api changes.
It's not like people don't get annoyed at us for things breaking
already. And I already care about Rust since I am writing this reply.
The maintainers are not going to have a Rust sidekick for their
subsystem, they simply have to understand it and manage it
if it is a supported kernel implementation language.
> Agreed. The only part that I'm not sure about is whether we need to put all the
> burden on a single person for the rest of their career. In the beginning, of
> course, but over time I would expect (hope?) experts would emerge and some of
> the load would be distributed.
People will pick up on it if it delivers expected improvements.
We (we device driver maintainers) already
had to learn YAML (see Documentation/devicetree/writing-schema.rst)
and that is certainly much more cognitively demanding than Rust. But it
made things so much better so it was worth it. In short: it delivers
expected improvements (formal validation of device trees, rooting
out nasty, buggy and incoherent device trees).
Have you tried to just sift through the kernel git log and see what
parts of the kernel is experiencing the kind of problems that
Rust can solve? (I haven't.) But if a certain area stand out,
that is likely where you should start. But maybe it is just
everywhere.
Yours,
Linus Walleij
On Thu, Apr 22, 2021 at 11:29 PM Miguel Ojeda
<[email protected]> wrote:
> > But as subsystem maintainer I'd like a clear picture of this wrapper
> > overhead, what does it usually entail? A typical kernel API has
> > vtable and a few variables, not much more than that.
>
> If you mean runtime-overhead, i.e. performance, it should be very
> small or even zero. It should be possible to perform LTO across
> languages too.
>
> If you mean source code overhead, or cognitive overhead, then it is
> quite a bit, yes. Please see below.
Yeah that is what I mean :)
> I hear you! I do not think it will take decades for kernel developers
> to get up to speed, but I agree that having some help/backup is a very
> good idea in the beginning.
>
> Our hope is that, if Rust advantages prove themselves, then it will
> the subsystem maintainers the ones that will want to create and
> maintain the wrappers so that drivers in their tree are easier to
> maintain and less prone to mistakes ;-)
I am not really convinced that (leaf) drivers is where Rust will
help most.
As I mentioned in my mail to Wedson that I think things like network
protocols that deal with abstract entities will have more "pure code"
(not deal with machine registers, just RAM memory).
File systems would be another example.
I think the Rust proponents should be open to the fact that their
work will eventually depend on themselves or someone else
fixing a working compiler for the maintained architectures in
the Linux kernel one way or the other, so they will be able to
work with Rust project anywhere in the kernel.
For example m68k is not going away. Avoiding this question
of compiler support, just waiting and hoping that these old
architectures will disappear is the wrong idea. The right idea
is to recognize that LLVM and/or GCC Rust needs to
support all these architectures so they can all use Rust.
Someone needs to put in the effort.
After all fixing that compiler support is an insignificant amount
of work compared to what Rust in the core kernel will be.
Yours,
Linus Walleij
Hi Linus,
On Mon, Apr 26, 2021 at 2:18 AM Linus Walleij <[email protected]> wrote:
>
> I try to provide a good answer so I did sit down and look a bit more
> at rust and looked over your Binder example to at least reach the
> level of "a little knowledge of something is dangerous".
Thanks *a lot* for having spent some time to get to know the language a bit!
> For the record I kind of like the language.
That is great to hear :)
> A typical example is the way device drivers talk to actual hardware:
> readl()/writel(), readw()/writew(), readb()/writeb() for memory-mapped
> IO or inb()/outb() for port-mapped I/O.
>
> So there is for example this (drivers/gpio/gpio-pl061.c):
>
> writeb(pl061->csave_regs.gpio_is, pl061->base + GPIOIS);
> writeb(pl061->csave_regs.gpio_ibe, pl061->base + GPIOIBE);
> writeb(pl061->csave_regs.gpio_iev, pl061->base + GPIOIEV);
> writeb(pl061->csave_regs.gpio_ie, pl061->base + GPIOIE);
>
> We write a number of u32 into u32 sized registers, this
> pl061->base is a void __iomem * so a pretty unsafe thing to
> begin with and then we add an offset to get to the register
> we want.
>
> [...]
>
> How would I write these 4 registers in Rust? From the actual
> statements down to the CPU instructions, top to bottom,
> that is what a driver writer wants to know.
A function that writes to unconstrained addresses is indeed unsafe.
However, if one constraints them, then the functions might be able to
be made safe.
For instance, we could have a macro where you describe your hardware
registers and then code is generated that only allows to write to
those addresses. Not only that, but also make it properly typed, do
any needed masking/bit twiddling/unit conversion, etc.
This would be very similar to other code generation tools out there
used to simplify talking to hardware and maintain HDL mappings.
So instead of:
writeb(x, pl061->base + GPIOIS);
you could say something like:
pl061.write_gpio_is(x)
and the generated code should be the same (in fact, the Rust code
could forward the actual call to C to avoid rewriting any assembly --
but that can be done too if needed, e.g. if cross-language LTO does
not manage to inline as much as we want).
> If the result of the exercise is that a typical device driver
> will contain more unsafe code than not, then device drivers
> are not a good starting point for Rust in the Linux kernel.
> In that case I would recommend that Rust start at a point
> where there is a lot of abstract code that is prone to the
> kind of problems that Rust is trying to solve. My intuition
> would be such things as network protocols. But I may be
> wrong.
We may have some constructs that cannot be reasonably made safe, but
that is fine! As long as one needs to spell those out as `unsafe`, the
safe/unsafe split would be working as intended.
It is likely that some code will be written in "C style" nevertheless,
specially in the beginning. But, as explained above, we need to have a
mindset of writing safe abstractions wherever possible; and not just
try to mimic the kernel C side in everything.
It is also true that Rust brings some features that can be very useful
for non-HW-IO/"pure" code (state machines, ser/des, net protocols,
etc.) -- if someone wants to use Rust there, that is great, of course.
> I worry that it may become evident that introducing Rust
> in device drivers is *only* suggested because the number
> of affected platforms can be controlled (lacking some
> compiler arch targets?) rather than that being a place
> that needs memory safety. And then I think it is just a
> playground for Rust experiments and need to be proposed
> as such. But the idea was a real deployment I suppose.
We are proposing "leaf" modules not just because of the platforms
issue, but also because they introduce a smaller risk overall, i.e.
Rust support could be more easily dropped if the kernel community ends
up thinking it is not worth it.
If some platforms start seeing benefits from using Rust, it is our
hope that compiler vendors and companies behind arches will start
putting more resources on supporting Rust for their platforms too.
> It reminds me of Haskell monads for some reason.
Indeed! Result is pretty much Either.
> This is true for any constrained language. I suppose we could write
> kernel modules in Haskell as well, or Prolog, given the right wrappers,
> and that would also attain the same thing: you get the desired
> restrictions in the target language by way of this adapter.
You can indeed see Rust as a language that has brought some of the
"good ideas" to the systems programming domains.
However, while other languages can do all the fancy type things Rust
can do, the key is that it also introduces the necessary bits to
achieve manual (but safe) memory management for a lot of patterns;
while at the same time reading pretty much like C and C++ and without
removing some "down to the metal" features needed, such as raw
pointers, inline assembly, etc.
> The syntax and semantic meaning of things with lots of
> impl <T: ?Sized> Wrapper<T> for ... is just really intimidating
> but I suppose one can learn it. No big deal.
That syntax does take some time to get used to, indeed (like any other
generics or parameterized system).
Since one cannot introduce UB by mistake, it is "safe" to "play with
the language", which makes it way easier than e.g. some C++ features.
Plus the compiler is quite helpful.
> I have no idea how to perform this in
> Rust despite reading quite a lot of examples. We have
> created a lot of these helpers like FIELD_GET() and
> that make this kind of operations simple.
Bit twiddling and working with raw data can be done, e.g. take a look
into `u64::from_be_bytes()` or `core::mem::transmute()`. Things like
`offset_of`, `container_of`, intrinsics, inline assembly, etc. are
also possible.
In general, everything low-level you can do in C or C++, you can do in
Rust (and get the same kind of codegen).
When needed to simplify things, macros can be introduced too (we have
a few of those already, e.g. to declare a kernel module, to declare
file ops, etc.).
> 1. Expressiveness of language.
>
> If you look in include/linux/bitfield.h you can see how
> this is elaborately implemented to be "a bit" typesafe
> and if you follow the stuff around you will find that in
> some cases it will resolve into per-CPU assembly
> bitwise operations for efficiency. It's neat, it has this
> nice handicrafty feeling to it, we control the machine
> all the way down.
All that is fine in Rust (see above).
> But that took a few years to get here, and wherever
> we want to write a device driver in
> Rust this kind of stuff is (I suspect) something that is
> going to have to be reinvented, in Rust.
If you mean it in the sense that we need to have "similar" code in
Rust, yes, of course. But we can also forward things to the C side, so
some things do not need to be rewritten. The standard library also
provides quite a few utilities (more than C's), which also helps.
If you mean it in the sense that "Rust might be too high-level", not
really (as explained above etc.). Rust was designed with this usage in
mind; and is being used in embedded projects already.
> So this is where Rust maintainers will be needed. I will
> say something like "I need <linux/bitfield.h>
> in Rust" which I guess will eventually become a
> "use linux::bitfield" or something like that. Please
> fill in the blanks. In the beginning pushing tasks like
> that back on the driver writers will just encourage them
> to go and write the driver in C. So the maintainers need
> to pick it up.
We will try to help here as much as possible :)
This should also get fleshed out more when we have a couple drivers
that talk to hardware directly.
> 2. Duplication of core libraries.
>
> I worry about that this could quite soon result in two
> implementations of bitfield: one in C and one in Rust.
> Because the language will have its preferred idiomatic
> way of dealing with this, on the level above the
> per-arch assembly optimized bitwise instructions
> that need to be wrapped nicely for performance.
> Which means wrappers all the way down. (Oh well.)
>
> But double maintenance. Multiply with the number
> of such kernel abstractions we have. So it better not
> happen too much or pay off really well.
The Rust abstractions should reuse the C wherever possible. So it is
not a very big concern in that sense. But, yes, we need to have those
wrappers.
We expect that some modules will be easier to write than others,
specially at the beginning. So some subsystem may start to see some
drivers if the abstractions are already there or are easy enough to
make; while others may take longer.
> 3. Kickback in practical problem solving.
>
> Believe it or not device driver authors are not mainly
> interested in memory safety, overruns, dangling pointers,
> memory leaks etc. Maybe in a perfect world they
> would/should. But they are interested in getting hardware
> to work and want a toolbox that gives the shortest path
> from A to B. Telling them (or subsystem maintainers) all
> about how these things are solved elegantly by Rust is
> not a selling point.
Some of those (overruns, leaks, etc.) can turn into functional bugs
too (e.g. crashes), so even if some companies only care about "making
it work", they are still a good thing to eliminate (from their
perspective).
Even outside the memory-safety topic, Rust provides extra features to
make things reliable easier (like the strict typing and the error
handling guarantees with `Result` etc. we discussed above), so
companies should be up for it -- assuming the infrastructure is there
already.
But, of course, in the beginning, it will be harder for everyone
involved because we are not accustomed to either the language, the
utility functions ("headers" like `bitfield.h`), the way of writing
drivers in Rust, etc.
Cheers,
Miguel
Linus, again thanks for taking the time to look into this. I think it's great
for us to get into this level of detail.
On Mon, Apr 26, 2021 at 02:18:33AM +0200, Linus Walleij wrote:
> For device drivers you will certainly have to wrap assembly as well.
> Or C calls that only contain assembly to be precise.
Sure, I don't think this would be a problem.
> A typical example is the way device drivers talk to actual hardware:
> readl()/writel(), readw()/writew(), readb()/writeb() for memory-mapped
> IO or inb()/outb() for port-mapped I/O.
>
> So there is for example this (drivers/gpio/gpio-pl061.c):
>
> writeb(pl061->csave_regs.gpio_is, pl061->base + GPIOIS);
> writeb(pl061->csave_regs.gpio_ibe, pl061->base + GPIOIBE);
> writeb(pl061->csave_regs.gpio_iev, pl061->base + GPIOIEV);
> writeb(pl061->csave_regs.gpio_ie, pl061->base + GPIOIE);
>
> We write a number of u32 into u32 sized registers, this
> pl061->base is a void __iomem * so a pretty unsafe thing to
> begin with and then we add an offset to get to the register
> we want.
>
> writel() on ARM for example turns into (arch/arm/include/asm/io.h):
>
> static inline void __raw_writel(u32 val, volatile void __iomem *addr)
> {
> asm volatile("str %1, %0"
> : : "Qo" (*(volatile u32 __force *)addr), "r" (val));
> }
>
> This is usually sprinkled all over a device driver, called in loops etc.
> Some of these will contain things like buffer drains and memory
> barriers. Elaborately researched for years so they will need to
> be there.
>
> I have no clue how this thing would be expressed in Rust.
> Even less how it would call the right code in the end.
> That makes me feel unsafe and puzzled so this is a specific
> area where "the Rust way" needs to be made very tangible
> and easy to understand.
>
> How would I write these 4 registers in Rust? From the actual
> statements down to the CPU instructions, top to bottom,
> that is what a driver writer wants to know.
Here's an example of how this could be implemented. Again, we're happy to
iterate on this (just like any other piece of software, independently of
language), but I think this will give you an idea. We'd begin with an
abstraction for a mapped io region:
pub struct IoMemBlock<const SIZE: usize> {
ptr: *mut u8
}
Note here that we encode the size of the block at compile time. We'll get our
safety guarantees from it.
For this abstraction, we provide the following implementation of the write
function:
impl<const SIZE: usize> IoMemBlock<SIZE> {
pub fn write<T>(&self, value: T, offset: usize) {
if let Some(end) = offset.checked_add(size_of::<T>()) {
if end <= SIZE {
// SAFETY: We just checked above that offset was within bounds.
let ptr = unsafe { self.ptr.add(offset) } as *mut T;
// SAFETY: We just checked that the offset+size was within bounds.
unsafe { ptr.write_volatile(value) };
return;
}
}
// SAFETY: Unimplemented function to cause compilation error.
unsafe { bad_write() };
}
}
Now suppose we have some struct like:
pub struct MyDevice {
base: IoMemBlock<100>,
reg1: u32,
reg2: u64,
}
Then a function similar to your example would be this:
pub fn do_something(pl061: &MyDevice) {
pl061.base.write(pl061.reg1, GPIOIS);
pl061.base.write(pl061.reg2, GPIOIBE);
pl061.base.write(20u8, 99);
}
I have this example here: https://rust.godbolt.org/z/chE3vjacE
The x86 compiled output of the code above is as follows:
mov eax, dword ptr [rdi + 16]
mov rcx, qword ptr [rdi]
mov dword ptr [rcx + 16], eax
mov rax, qword ptr [rdi + 8]
mov qword ptr [rcx + 32], rax
mov byte ptr [rcx + 99], 20
ret
Some observations:
1. do_something is completely safe: all accesses to memory are checked.
2. The only unsafe part that could involve the driver for this would be the
creation of IoMemBlock: my expectation is that this would be implemented by the
bus driver or some library that maps the appropriate region and caps the size.
That is, we can also build a safe abstraction for this.
3. All checks are optimised away because they uses compile-time constants. The
code presented above is as efficient as C.
4. All code is Rust code and therefore type-checked during compilation, there is
no need for macros.
5. Note that the code supports all sizes, and selects which one to use based on
the type of the first argument (the example above has 8, 32, 64 bit examples).
6. If the developer writing a driver accidentally uses an offset beyond the
limit, they will get a compilation error (bad_write is left unimplemented).
Perhaps we could find a better way to indicate this, but a compilation error is
definitely better than corrupting state (potentially silently) at runtime.
7. We could potentially design a way to limit which offsets are available for a
given IoMemBlock, I just haven't thought through it yet, but it would also
reduce the number of mistakes a developer could make.
> If the result of the exercise is that a typical device driver
> will contain more unsafe code than not, then device drivers
> are not a good starting point for Rust in the Linux kernel.
> In that case I would recommend that Rust start at a point
> where there is a lot of abstract code that is prone to the
> kind of problems that Rust is trying to solve. My intuition
> would be such things as network protocols. But I may be
> wrong.
Agreed. But based on the example above, I don't expect a lot (if any) of unsafe
code in drivers due accessing io memory.
> This is really neat. I think it is a good example where Rust
> really provides the right tool for the job.
>
> And it is very far away from any device driver. Though some
> drivers need pages.
Sure, I didn't mean to imply that this is useful in drivers, I just meant it as
an example.
> This is true for any constrained language. I suppose we could write
> kernel modules in Haskell as well, or Prolog, given the right wrappers,
> and that would also attain the same thing: you get the desired
> restrictions in the target language by way of this adapter.
Agreed. Rust is different in that it doesn't need a garbage collector, so it can
achieve performance comparable to C, which is something that we can't claim
about Haskell and Prolog atm -- I actually like Haskell better than Rust, but
it's not practical at the moment for kernel development.
> The syntax and semantic meaning of things with lots of
> impl <T: ?Sized> Wrapper<T> for ... is just really intimidating
> but I suppose one can learn it. No big deal.
I agree it's intimidating, but so are macros like ____MAKE_OP in bitfield.h --
the former has the advantage of being type-checked. Writing macros like
____MAKE_OP is a hit-and-miss exercise in my experience. However, I feel that
both cases benefit from being specialised implementations that are somewhat
rare.
> What I need to know as device driver infrastructure maintainer is:
>
> 1. If the language is expressive enough to do what device driver
> authors need to do in an efficient and readable manner which
> is as good or better than what we have today.
What do you think of the example I provided above? I think that generics give
Rust an edge over C in terms of expressiveness, though abusing it may
significantly reduce readability.
> 2. Worry about double implementations of core library functions.
This indeed may be a problem, but I'm happy to have Rust wrappers call
C/assembly functions. With LTO this should not affect performance.
> 3. Kickback in practical problem solving.
>
> This will be illustrated below.
>
> Here is a device driver example that I wrote and merged
> just the other week (drivers/iio/magnetometer/yamaha-yas530.c)
> it's a nasty example, so I provide it to make a point.
>
> static void yas53x_extract_calibration(u8 *data, struct yas5xx_calibration *c)
> {
> u64 val = get_unaligned_be64(data);
>
> /*
> * Bitfield layout for the axis calibration data, for factor
> * a2 = 2 etc, k = k, c = clock divider
> *
> * n 7 6 5 4 3 2 1 0
> * 0 [ 2 2 2 2 2 2 3 3 ] bits 63 .. 56
> * 1 [ 3 3 4 4 4 4 4 4 ] bits 55 .. 48
> * 2 [ 5 5 5 5 5 5 6 6 ] bits 47 .. 40
> * 3 [ 6 6 6 6 7 7 7 7 ] bits 39 .. 32
> * 4 [ 7 7 7 8 8 8 8 8 ] bits 31 .. 24
> * 5 [ 8 9 9 9 9 9 9 9 ] bits 23 .. 16
> * 6 [ 9 k k k k k c c ] bits 15 .. 8
> * 7 [ c x x x x x x x ] bits 7 .. 0
> */
> c->a2 = FIELD_GET(GENMASK_ULL(63, 58), val) - 32;
> c->a3 = FIELD_GET(GENMASK_ULL(57, 54), val) - 8;
> c->a4 = FIELD_GET(GENMASK_ULL(53, 48), val) - 32;
> c->a5 = FIELD_GET(GENMASK_ULL(47, 42), val) + 38;
> c->a6 = FIELD_GET(GENMASK_ULL(41, 36), val) - 32;
> c->a7 = FIELD_GET(GENMASK_ULL(35, 29), val) - 64;
> c->a8 = FIELD_GET(GENMASK_ULL(28, 23), val) - 32;
> c->a9 = FIELD_GET(GENMASK_ULL(22, 15), val);
> c->k = FIELD_GET(GENMASK_ULL(14, 10), val) + 10;
> c->dck = FIELD_GET(GENMASK_ULL(9, 7), val);
> }
>
> This extracts calibration for the sensor from an opaque
> chunk of bytes. The calibration is stuffed into sequences of
> bits to save space at different offsets and lengths. So we turn
> the whole shebang passed in the u8 *data into a 64bit
> integer and start picking out the pieces we want.
>
> We know a priori that u8 *data will be more than or equal
> to 64 bits of data. (Which is another problem but do not
> focus on that, let us look at this function.)
>
> I have no idea how to perform this in
> Rust despite reading quite a lot of examples. We have
> created a lot of these helpers like FIELD_GET() and
> that make this kind of operations simple.
Would you mind sharing more about which aspect of this you feel is challenging?
I see now that Miguel has already responded to this thread so I'll stop here.
Happy to follow up on anything.
Thanks,
-Wedson
On Mon, Apr 26, 2021 at 4:40 PM Wedson Almeida Filho
<[email protected]> wrote:
>
> I see now that Miguel has already responded to this thread so I'll stop here.
> Happy to follow up on anything.
No, no, the message was directed to you, and you gave very nice examples! :)
I think having both replies is great, we gave different perspectives.
Cheers,
Miguel
On Mon, Apr 26, 2021 at 2:18 AM Linus Walleij <[email protected]> wrote:
>
> static void yas53x_extract_calibration(u8 *data, struct yas5xx_calibration *c)
> {
> u64 val = get_unaligned_be64(data);
>
> c->a2 = FIELD_GET(GENMASK_ULL(63, 58), val) - 32;
> c->a3 = FIELD_GET(GENMASK_ULL(57, 54), val) - 8;
> c->a4 = FIELD_GET(GENMASK_ULL(53, 48), val) - 32;
> c->a5 = FIELD_GET(GENMASK_ULL(47, 42), val) + 38;
> c->a6 = FIELD_GET(GENMASK_ULL(41, 36), val) - 32;
> c->a7 = FIELD_GET(GENMASK_ULL(35, 29), val) - 64;
> c->a8 = FIELD_GET(GENMASK_ULL(28, 23), val) - 32;
> c->a9 = FIELD_GET(GENMASK_ULL(22, 15), val);
> c->k = FIELD_GET(GENMASK_ULL(14, 10), val) + 10;
> c->dck = FIELD_GET(GENMASK_ULL(9, 7), val);
> }
By the way, to give a more concrete example, this function could look like this:
fn yas53x_extract_calibration(data: [u8; 8], c: &mut yas5xx_calibration)
{
let val = u64::from_be_bytes(data);
c.a2 = FIELD_GET(GENMASK_ULL(63, 58), val) - 32;
c.a3 = FIELD_GET(GENMASK_ULL(57, 54), val) - 8;
c.a4 = FIELD_GET(GENMASK_ULL(53, 48), val) - 32;
c.a5 = FIELD_GET(GENMASK_ULL(47, 42), val) + 38;
c.a6 = FIELD_GET(GENMASK_ULL(41, 36), val) - 32;
c.a7 = FIELD_GET(GENMASK_ULL(35, 29), val) - 64;
c.a8 = FIELD_GET(GENMASK_ULL(28, 23), val) - 32;
c.a9 = FIELD_GET(GENMASK_ULL(22, 15), val);
c.k = FIELD_GET(GENMASK_ULL(14, 10), val) + 10;
c.dck = FIELD_GET(GENMASK_ULL(9, 7), val) as u8;
}
assuming `FIELD_GET()` returns `i32`. In particular, `GENMASK_ULL` and
`FIELD_GET` can be written as normal functions, no need for macros
(and can be `const fn` too -- i.e. can be evaluated at compile-time if
needed).
As you see, it looks remarkably similar, and there is no `unsafe`
because we pass the array of bytes instead of a raw pointer.
The caller needs to get the array from somewhere, of course -- if you
only have a raw pointer to start with, then the caller will need an
`unsafe` line to dereference it, as usual.
Cheers,
Miguel
On Mon, Apr 26, 2021 at 2:31 AM Linus Walleij <[email protected]> wrote:
>
> I think the Rust proponents should be open to the fact that their
> work will eventually depend on themselves or someone else
> fixing a working compiler for the maintained architectures in
> the Linux kernel one way or the other, so they will be able to
> work with Rust project anywhere in the kernel.
>
> For example m68k is not going away. Avoiding this question
> of compiler support, just waiting and hoping that these old
> architectures will disappear is the wrong idea. The right idea
> is to recognize that LLVM and/or GCC Rust needs to
> support all these architectures so they can all use Rust.
> Someone needs to put in the effort.
The RFC does not avoid the question -- please note it explicitly
mentions the architecture/platform support issue and the current
dependency on LLVM, as well as the possible ways to solve it.
We would love to not have that issue, of course, because that would
enable Rust to be used in other parts of the kernel where it is likely
to be quite useful too.
But even if we did not have the issue today, it seems like starting
with drivers and other "leaf" modules is a better approach. There are
several reasons:
- If for reason reason we wanted to remove Rust from the kernel,
then it would be easier to do so if only "leaf" bits had been written.
- We cannot compile the Rust support without nightly features yet,
so it does not seem wise to make it a hard requirement right away.
- Kernel developers need time to learn a bit of Rust, thus writing
subsystems or core pieces of the kernel in Rust would mean less people
can understand them.
Given that drivers are a big part of the new code introduced every
release, that they are "leaf" modules and that in some cases they are
only intended to be used with a given architecture, they seem like a
good starting point.
Cheers,
Miguel
Hi Wedson,
thanks for your replies, I have a bigger confidence in Rust for
drivers after your detailed answers.
On Mon, Apr 26, 2021 at 4:40 PM Wedson Almeida Filho
<[email protected]> wrote:
> Note here that we encode the size of the block at compile time. We'll get our
> safety guarantees from it.
>
> For this abstraction, we provide the following implementation of the write
> function:
>
> impl<const SIZE: usize> IoMemBlock<SIZE> {
> pub fn write<T>(&self, value: T, offset: usize) {
> if let Some(end) = offset.checked_add(size_of::<T>()) {
> if end <= SIZE {
> // SAFETY: We just checked above that offset was within bounds.
> let ptr = unsafe { self.ptr.add(offset) } as *mut T;
> // SAFETY: We just checked that the offset+size was within bounds.
> unsafe { ptr.write_volatile(value) };
> return;
> }
> }
> // SAFETY: Unimplemented function to cause compilation error.
> unsafe { bad_write() };
> }
> }
I really like the look of this. I don't fully understand it, but what
is needed for driver developers to adopt rust is something like a
detailed walk-through of examples like this that explains the
syntax 100% all the way down.
We do not need to understand the basic concepts of the
language as much because these are evident, the devil is
in details like this.
> Now suppose we have some struct like:
>
> pub struct MyDevice {
> base: IoMemBlock<100>,
> reg1: u32,
> reg2: u64,
> }
>
> Then a function similar to your example would be this:
>
> pub fn do_something(pl061: &MyDevice) {
> pl061.base.write(pl061.reg1, GPIOIS);
> pl061.base.write(pl061.reg2, GPIOIBE);
> pl061.base.write(20u8, 99);
> }
>
> I have this example here: https://rust.godbolt.org/z/chE3vjacE
>
> The x86 compiled output of the code above is as follows:
>
> mov eax, dword ptr [rdi + 16]
> mov rcx, qword ptr [rdi]
> mov dword ptr [rcx + 16], eax
> mov rax, qword ptr [rdi + 8]
> mov qword ptr [rcx + 32], rax
> mov byte ptr [rcx + 99], 20
> ret
This looks good, but cannot be done like this. The assembly versions
of writel() etc have to be used because the compiler simply will not
emit the right type of assembly for IO access, unless the compiler
(LLVM GCC) gains knowledge of what an IO address is, and so far
they have not.
I mostly work on ARM so I have little understanding of x86
assembly other than superficial.
Port-mapped IO on ARM for ISA/PCI would be a stressful
example, I do not think Rust or any other sane language
(except Turbo Pascal) has taken the effort to create language
abstractions explicitly for port-mapped IO.
See this for ARM:
#define outb(v,p) ({ __iowmb(); __raw_writeb(v,__io(p)); })
So to write a byte to a port we first need to issue a IO write memory
barrier, followed by the actual write to the IO memory where the
port resides. __iowmb() turns into the assembly instruction
wmb on CPUs that support it and a noop on those that do not,
at compile time.
One *could* think about putting awareness about crazy stuff like
that into the language but ... I think you may want to avoid it
and just wrap the assembly. So a bit of low-level control of the
behavior there.
> 2. The only unsafe part that could involve the driver for this would be the
> creation of IoMemBlock: my expectation is that this would be implemented by the
> bus driver or some library that maps the appropriate region and caps the size.
> That is, we can also build a safe abstraction for this.
I suppose this is part of the problem in a way: a language tends to be
imperialistic: the developers will start thinking "it would all be so much
easier if I just rewrote also this thing in Rust".
And that is where you will need compiler support for all targets.
> 7. We could potentially design a way to limit which offsets are available for a
> given IoMemBlock, I just haven't thought through it yet, but it would also
> reduce the number of mistakes a developer could make.
The kernel has an abstraction for memory and register accesses,
which is the regmap, for example MMIO regmap for simple
memory-mapped IO:
drivers/base/regmap/regmap-mmio.c
In a way this is memory safety implemented in C.
Sadly it is not very well documented. But regmap is parameterized
to restrict accesses to certain register areas, using explicit
code in C, so you can provide an algorithm for which addresses
are accessible for write for example, like every fourth address
on a sunday.
A typical usecase is clock drivers which have very fractured
and complex memory maps with random readable/writeable
bits all over the place.
If Rust wants to do this I would strongly recommend it to
try to look like regmap MMIO.
See for example drivers/clk/sprd/common.c:
static const struct regmap_config sprdclk_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.max_register = 0xffff,
.fast_io = true,
};
(...)
regmap = devm_regmap_init_mmio(&pdev->dev, base,
&sprdclk_regmap_config);
It is also possible to provide a callback function to determine
if addresses are readable/writeable.
This is really a devil-in-the-details place where Rust needs
to watch out to not reimplement regmap in a substandard
way from what is already available.
Also in many cases developers do not use regmap MMIO
because it is just too much trouble. They tend to use it
not because "safety is nice" but because a certain register
region is very fractured and it is easy to do mistakes and
write into a read-only register by mistake. So they want
this, optionally, when the situation demands it.
> > What I need to know as device driver infrastructure maintainer is:
> >
> > 1. If the language is expressive enough to do what device driver
> > authors need to do in an efficient and readable manner which
> > is as good or better than what we have today.
>
> What do you think of the example I provided above? I think that generics give
> Rust an edge over C in terms of expressiveness, though abusing it may
> significantly reduce readability.
It looks nice but it is sadly unrealistic because we need to wrap
the real assembly accessors in practice (write memory barriers
and such) and another problem is that it shows that Rust has an
ambition to do a parallel implementation of regmap.
> > 2. Worry about double implementations of core library functions.
>
> This indeed may be a problem, but I'm happy to have Rust wrappers call
> C/assembly functions. With LTO this should not affect performance.
Yeah see above about regmap too.
> > The syntax and semantic meaning of things with lots of
> > impl <T: ?Sized> Wrapper<T> for ... is just really intimidating
> > but I suppose one can learn it. No big deal.
>
> I agree it's intimidating, but so are macros like ____MAKE_OP in bitfield.h --
> the former has the advantage of being type-checked. Writing macros like
> ____MAKE_OP is a hit-and-miss exercise in my experience. However, I feel that
> both cases benefit from being specialised implementations that are somewhat
> rare.
(...)
> > I have no idea how to perform this in
> > Rust despite reading quite a lot of examples. We have
> > created a lot of these helpers like FIELD_GET() and
> > that make this kind of operations simple.
>
> Would you mind sharing more about which aspect of this you feel is challenging?
Good point.
This explanation is going to take some space.
I am not able to express it in Rust at all and that is what
is challenging about it, the examples provided for Rust
are all about nice behaved computer programs like
cutesey fibonnacci series and such things and not really
complex stuff.
Your binder example is however very good, the problem
is that it is not a textbook example so the intricacies of
it are not explained, top down. (I'm not blaming you for
this, I just say we need that kind of text to get to know
Rust in the details.)
As device driver maintainers we especially need to
understand IO access and so I guess that is what
we are discussing above, so we are making progress
here.
What we need is a good resource to learn it, that
skips the trivial aspects of the language and goes immediately
for the interesting details.
It's not like I didn't try.
I consulted the Rust book on the website of coure.
The hard thing to understand in Rust is traits. I don't understand
traits. I have the level of "a little knowledge is dangerous" and
I clearly understand this: all kernel developers must have
a thorough and intuitive understanding of the inner transcendental
meaning of the concept of a TRAIT, how it was devised, how the
authors of the language conceptualized it, what effect it is supposed
to have on generated assembly.
The language book per se is a bit too terse.
For example if I read
https://doc.rust-lang.org/book/appendix-02-operators.html
T: ?Sized : Allow generic type parameter to be a dynamically sized type
This is just self-referential. The description is written in a
strongly context-dependent language to make a pun ...
I think every word in that sentence except "allow"and "to be a"
is dependent on other Rust concepts and thus completely
unreadable without context.
Instead it is described in a later chapter:
https://doc.rust-lang.org/book/ch19-04-advanced-types.html
This is more to the point.
"Rust has a particular trait called the Sized trait to
determine whether or not a type’s size is known at compile time."
(...) "A trait bound on ?Sized is the opposite of a trait bound on
Sized: we would read this as “T may or may not be Sized.” This
syntax is only available for Sized, not any other traits."
But Jesus Christ. This makes me understand less not
more.
So I need to understand what traits are. So back to
https://doc.rust-lang.org/book/ch10-02-traits.html
This chapter is just *really* hard to understand. I
can blame myself for being stupid, but since it is
more convenient to blame the author I'm just going
to complain that this chapter is not very good for
low-level programmers. I'm probably wrong, this is
obviously a personal development exercise.
OK I will give it several second tries. It just feels
very intimidating.
To me, the Rust book is nowhere near "The C
Programming Language" in quality (meaning readability
and ability to transfer complex detailed knowledge) and
that is a serious problem.
Sadly, it is hard to pin down and define what makes it
so hard, but I would take a guess and say that
"The C Programming Language" was written by low
level programmers implementing an operating system
and the Rust book was not. I.e. the authors concept
of the intended audience.
So this is where we need good inroads to understand the
language.
The quality and versatility of the K&R book about The
C Programming Language has been pointed out by
Kernighan in "UNIX: A History and a Memoir"
and I think the Rust community needs to learn something
from this (page78, praising himself and Ritchie):
"We made many alternating passes over the main text (...)
It describes the language with what Bill Plauger
once called 'spine-tingling precision'. The reference
manual is like C itself: precise, elegant, and compact"
I think a main obstacle for getting Rust accepted by kernel
developers is not the language itself, but the lack of textbook
with the same qualities as The C Programming Language.
This is a serious flaw, not with the language itself but with
the surrounding materials.
Kernighan writes about *forcing* Ritchie to write the book
about C ("I twisted his arm harder and eventually he agreed"),
after implementing it, and this made it reflect the
language from the intent of the author and OS usecase
very well.
The Rust book is written "by Steve Klabnik and Carol Nichols,
with contributions from the Rust Community" and I do not mean
to criticize them, because I think they had very clear ideas
of what kind of people were going to read it. And I bet they did
not intend it for OS developers.
What I find very disturbing is that the authors of the Rust
language did NOT write it. I think this may be the source
of a serious flaw. We need this information straight from
the horse's mouth.
I would strongly advice the Rust community to twist the
arms of the original Rust authors to go and review and
edit the Rust book. Possibly rewrite parts of it. This is what
the world needs to get a more adaptable Rust.
I understand this is a thick requirement, but hey, you are
competing with C.
Yours,
Linus Walleij
On Mon, Apr 26, 2021 at 8:18 PM Miguel Ojeda
<[email protected]> wrote:
> On Mon, Apr 26, 2021 at 2:31 AM Linus Walleij <[email protected]> wrote:
> >
> > I think the Rust proponents should be open to the fact that their
> > work will eventually depend on themselves or someone else
> > fixing a working compiler for the maintained architectures in
> > the Linux kernel one way or the other, so they will be able to
> > work with Rust project anywhere in the kernel.
> >
> > For example m68k is not going away. Avoiding this question
> > of compiler support, just waiting and hoping that these old
> > architectures will disappear is the wrong idea. The right idea
> > is to recognize that LLVM and/or GCC Rust needs to
> > support all these architectures so they can all use Rust.
> > Someone needs to put in the effort.
>
> The RFC does not avoid the question -- please note it explicitly
> mentions the architecture/platform support issue and the current
> dependency on LLVM, as well as the possible ways to solve it.
OK true. Sorry for being sloppy.
Actually my reply to Wedson brought up a new issue, which is the
quality of learning resources and the lack of an equivalent to
The C Programming Language book.
> But even if we did not have the issue today, it seems like starting
> with drivers and other "leaf" modules is a better approach. There are
> several reasons:
>
> - If for reason reason we wanted to remove Rust from the kernel,
> then it would be easier to do so if only "leaf" bits had been written.
>
> - We cannot compile the Rust support without nightly features yet,
> so it does not seem wise to make it a hard requirement right away.
>
> - Kernel developers need time to learn a bit of Rust, thus writing
> subsystems or core pieces of the kernel in Rust would mean less people
> can understand them.
>
> Given that drivers are a big part of the new code introduced every
> release, that they are "leaf" modules and that in some cases they are
> only intended to be used with a given architecture, they seem like a
> good starting point.
I'm not sure I agree with this.
I think a good starting point would be to either fix Rust support in
GCC or implement some more important ISAs in LLVM,
whichever is easiest. I don't mind having just *one* compiler but
I mind having *a* compiler for every arch we support.
The situation for LLVM is very well described in the Wikipedia
entry for LLVM: "but most of this hardware is mostly obsolete,
and LLVM developers decided the support and maintenance
costs were no longer justified" - this is what I would call
deprecationism (deletionism). I think this is a detrimental force
for both compilers and kernels. It encourages developers of
compilers and kernels to do the wrong thing: instead of
rewriting their compiler and kernel infrastructure such that
maintenance of older ISAs and architectures becomes a bliss
they do what mathematicians do "let's assume a simpler
version of the problem". And this results in a less versatile
infrastructure and less adaptable code in the end. Which will
affect how agile and adaptive the software is. And when
something new comes along it hits you in the head.
Portability to old systems and ISAs is a virtue in itself
because of the effect it has on code quality, not necessarily
for the support itself.
Deprecationism is more the side effect of a certain business
strategy to toss new technology out every quarter without
having to care about aftermarket or postmarket too much.
This irritates people to the extent that there is now even
a project called "PostmarketOS" (Linux based). It is not
sustainable to use an emotional argument, but that is really
not my point, I care about code quality and diversity of
ISAs and target systems improves code quality in my book.
I might be an extremist, but I do need to state this point.
Yours,
Linus Walleij
Hi Linus.
Thanks for your detailed inputs. I will defer to Wedson to address your
points but I had one suggestion.
On 27.04.2021 12:54, Linus Walleij wrote:
[...]
>To me, the Rust book is nowhere near "The C
>Programming Language" in quality (meaning readability
>and ability to transfer complex detailed knowledge) and
>that is a serious problem.
Compared to the Rust Book - which aims to provide a relatively gentle
and comprehensive introduction to the language, I think the Rust
reference might be more suitable in order to understand the language
support for features like Traits:
https://doc.rust-lang.org/stable/reference/introduction.html
A lot of folks, myself included, convolve the Book with the Reference to
get a stronger handle on concepts.
This is subjective of course but I felt it worth sharing.
Robin
Hi Linus,
I wanted to shed some light on one specific point of your criticism of
The Rust Programming Language:
> What I find very disturbing is that the authors of the Rust language did NOT write it.
Rust, unlike C when the K&R book was written, has already had a pretty
large number of people contribute to its development. Steve Klabnik
and Carol Nichols are 6th and 11th, respectively, on the list of
contributors with the most commits in the Rust compiler & standard
library (from the official "thanks" page, excluding the top committer
"bors", which is part of Rust's CI automation:
https://thanks.rust-lang.org/rust/all-time/).
I hope this clears up some confusion.
> What we need is a good resource to learn it, that skips the trivial aspects of the language and goes immediately for the interesting details.
I realize that what is "trivial" and what is "interesting" is
subjective, but if you find the explanation of traits in the Book
difficult to understand, I would recommend revisiting the earlier
sections in order to be certain you understand the foundations for the
explanation of traits.
For what it's worth, since you would specifically like a lower-level
perspective, in addition to looking at the Reference (as previously
suggested), I recommend trying O'Reilly's Programming Rust by Jim
Blandy and Jason Orendorff.
Kyle Strand
On Tue, Apr 27, 2021 at 5:14 AM Linus Walleij <[email protected]> wrote:
>
> On Mon, Apr 26, 2021 at 8:18 PM Miguel Ojeda
> <[email protected]> wrote:
> > On Mon, Apr 26, 2021 at 2:31 AM Linus Walleij <[email protected]> wrote:
> > >
> > > I think the Rust proponents should be open to the fact that their
> > > work will eventually depend on themselves or someone else
> > > fixing a working compiler for the maintained architectures in
> > > the Linux kernel one way or the other, so they will be able to
> > > work with Rust project anywhere in the kernel.
> > >
> > > For example m68k is not going away. Avoiding this question
> > > of compiler support, just waiting and hoping that these old
> > > architectures will disappear is the wrong idea. The right idea
> > > is to recognize that LLVM and/or GCC Rust needs to
> > > support all these architectures so they can all use Rust.
> > > Someone needs to put in the effort.
> >
> > The RFC does not avoid the question -- please note it explicitly
> > mentions the architecture/platform support issue and the current
> > dependency on LLVM, as well as the possible ways to solve it.
>
> OK true. Sorry for being sloppy.
>
> Actually my reply to Wedson brought up a new issue, which is the
> quality of learning resources and the lack of an equivalent to
> The C Programming Language book.
>
> > But even if we did not have the issue today, it seems like starting
> > with drivers and other "leaf" modules is a better approach. There are
> > several reasons:
> >
> > - If for reason reason we wanted to remove Rust from the kernel,
> > then it would be easier to do so if only "leaf" bits had been written.
> >
> > - We cannot compile the Rust support without nightly features yet,
> > so it does not seem wise to make it a hard requirement right away.
> >
> > - Kernel developers need time to learn a bit of Rust, thus writing
> > subsystems or core pieces of the kernel in Rust would mean less people
> > can understand them.
> >
> > Given that drivers are a big part of the new code introduced every
> > release, that they are "leaf" modules and that in some cases they are
> > only intended to be used with a given architecture, they seem like a
> > good starting point.
>
> I'm not sure I agree with this.
>
> I think a good starting point would be to either fix Rust support in
> GCC or implement some more important ISAs in LLVM,
> whichever is easiest. I don't mind having just *one* compiler but
> I mind having *a* compiler for every arch we support.
>
> The situation for LLVM is very well described in the Wikipedia
> entry for LLVM: "but most of this hardware is mostly obsolete,
> and LLVM developers decided the support and maintenance
> costs were no longer justified" - this is what I would call
> deprecationism (deletionism). I think this is a detrimental force
> for both compilers and kernels. It encourages developers of
> compilers and kernels to do the wrong thing: instead of
> rewriting their compiler and kernel infrastructure such that
> maintenance of older ISAs and architectures becomes a bliss
> they do what mathematicians do "let's assume a simpler
> version of the problem". And this results in a less versatile
> infrastructure and less adaptable code in the end. Which will
> affect how agile and adaptive the software is. And when
> something new comes along it hits you in the head.
>
> Portability to old systems and ISAs is a virtue in itself
> because of the effect it has on code quality, not necessarily
> for the support itself.
>
> Deprecationism is more the side effect of a certain business
> strategy to toss new technology out every quarter without
> having to care about aftermarket or postmarket too much.
> This irritates people to the extent that there is now even
> a project called "PostmarketOS" (Linux based). It is not
> sustainable to use an emotional argument, but that is really
> not my point, I care about code quality and diversity of
> ISAs and target systems improves code quality in my book.
>
> I might be an extremist, but I do need to state this point.
>
> Yours,
> Linus Walleij
On Tue, Apr 27, 2021 at 1:13 PM Linus Walleij <[email protected]> wrote:
>
> Actually my reply to Wedson brought up a new issue, which is the
> quality of learning resources and the lack of an equivalent to
> The C Programming Language book.
I recall having a similar feeling when initially jumping into
individual chapters of The Rust Programming Language book. I think it
is intended to be read from cover to cover instead.
There are other resources, see [1]. For instance, there is The
Embedded Rust Book [2]. Some of those are a WIP, but perhaps others
can recommend better finished/published books.
In any case, Rust has more features than C, some of them quite unique,
and they are routinely used, so it does take some time to learn.
[1] https://www.rust-lang.org/learn
[2] https://docs.rust-embedded.org/book/
> I think a good starting point would be to either fix Rust support in
> GCC or implement some more important ISAs in LLVM,
> whichever is easiest. I don't mind having just *one* compiler but
> I mind having *a* compiler for every arch we support.
>
> [...]
>
> Portability to old systems and ISAs is a virtue in itself
> because of the effect it has on code quality, not necessarily
> for the support itself.
I agree that there are benefits of keeping compiler technology
flexible, but one cannot force or expect any project (including the
Linux kernel) to maintain all code forever.
In the end, we need to balance that adaptability against the benefits
of adding Rust. In particular because nowadays LLVM is able to cover
the majority of devices that want to run the very latest Linux
kernels. Thus those benefits apply to most users. If LLVM only
supported, say, x86_64, I would agree that it would not be enough.
By contrast, compiler flexibility only matters indirectly to users,
and at some point there are diminishing returns to keeping all
architectures around.
In any case, adding Rust (in particular for "leaf" modules) does not
imply that we will lose those architectures any time soon. That would
take at least several years, and would require quite a few things to
happen at the same time:
- That Rust got so widely used in the kernel (because the benefits
turned out to be important) that maintainers went as far as wanting to
drop C drivers from mainline for Rust equivalents.
- That GCC did not get any way to compile Rust (no Rust frontend for
GCC, no GCC backend for `rustc`, etc.) and, moreover, that the plans
for that had been dropped.
- That LLVM did not add support for the missing architectures.
The first point is unlikely any time soon. The second point is
unlikely, too, given there is funding for that now (and I assume those
projects will receive more support if Rust lands in the kernel). The
third point is likely, though.
Cheers,
Miguel
Hello,
First of all IANAL, so I might be wrong regarding the issue below.
On 14/04/2021, [email protected] <[email protected]> wrote:
>
> ## Why not?
>
> Rust also has disadvantages compared to C in the context of
> the Linux kernel:
>
>
> - 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...
>
After reading the interview referenced by https://lwn.net/Articles/854740/
I think there might be issue with licensing - few quotes from the interview:
> And on the other hand, I've seen a lot of BSD (or MIT or similar) licensed open source projects that just fragment when they become big enough to be commercially important, and the involved companies inevitably decide to turn their own parts proprietary.
> So I think the GPLv2 is pretty much the perfect balance of "everybody works under the same rules", and still requires that people give back to the community ("tit-for-tat")
> So forking isn't a problem, as long as you can then merge back the good parts. And that's where the GPLv2 comes in. The right to fork and do your own thing is important, but the other side of the coin is equally important - the right to then always join back together when a fork was shown to be successful.
Rust compiler license doesn't require for people to give back to the
community - corporation can create their own version of rust compiler
adding some proprietary extensions, develop drivers with it and even
if the drivers code will be GPL'd they won't be buildable by anyone
but that corporation. The rust compiler license doesn't require
sharing changes when you modify it. The similar problem has flex and
openssl required to build the kernel, but so far no one thought about
abusing them afaik.
That "single implementation based on LLVM" uses a mix of MIT, Apache,
BSD-compatible and other licenses. It doesn't use strong copyleft
license in contrast to almost every tool required to build the kernel,
except for flex (BSD, no (L)GPL alternative afaik) and openssl (Apache
license, gnutls could be used instead).
I suggest to wait until featureful GPL implementation of rust language
is made (assuming GNU Rust is on the way) before merging any rust code
in the kernel and when that implementation is done make a requirement
that all rust code must be buildable by at least GPL implementation.
Maybe it would also be worthwhile to make the requirement that the
kernel must be buildable with free software (not just open source
software) explicit ?
Best Regards,
Mariusz Ceier
On Wed, Apr 28, 2021 at 11:34 AM Mariusz Ceier <[email protected]> wrote:
>
> Maybe it would also be worthwhile to make the requirement that the
> kernel must be buildable with free software (not just open source
> software) explicit ?
The kernel is already buildable by LLVM (and clang); in fact Android,
CrOS, and Google's production servers already do so.
https://clangbuiltlinux.github.io/
--
Thanks,
~Nick Desaulniers
From: Mariusz Ceier
> Sent: 28 April 2021 19:34
....
>
> I suggest to wait until featureful GPL implementation of rust language
> is made (assuming GNU Rust is on the way) before merging any rust code
> in the kernel and when that implementation is done make a requirement
> that all rust code must be buildable by at least GPL implementation.
>
> Maybe it would also be worthwhile to make the requirement that the
> kernel must be buildable with free software (not just open source
> software) explicit ?
Or put the version of the compiler that works in the source tree
with the kernel and then build it as part of the full build.
It is enough of a PITA having to find libelf-devel in order to
build objtool, never mind having to find the correct version
of something else.
gcc tends to be available and the version doesn't matter too much.
But ever that gives problems.
David
-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK
Registration No: 1397386 (Wales)
On Tue, Apr 27, 2021 at 12:54:00PM +0200, Linus Walleij wrote:
> I really like the look of this. I don't fully understand it, but what
> is needed for driver developers to adopt rust is something like a
> detailed walk-through of examples like this that explains the
> syntax 100% all the way down.
Do you have a suggestion for a good place to host such walk-throughs? Also, do
you have other examples in mind that might be useful?
Have you had a chance to read the example I posted in Google's security blog?
It's not particularly complex stuff but touches on some relevant concepts:
https://security.googleblog.com/2021/04/rust-in-linux-kernel.html
> This looks good, but cannot be done like this. The assembly versions
> of writel() etc have to be used because the compiler simply will not
> emit the right type of assembly for IO access, unless the compiler
> (LLVM GCC) gains knowledge of what an IO address is, and so far
> they have not.
That code does not preclude the use of C/assembly wrappers. One way to do it
would be to define a trait that allows types to specify their read/write
functions. For example:
pub trait IoMemType {
unsafe fn write(ptr: *mut Self, value: Self);
unsafe fn read(ptr: *const Self) -> Self;
}
Then we restrict T in my original example to only allow types that implement
IoMemType. And we implement it for u8/16/32/64 as wrappers to the C/assembly
implementations.
> One *could* think about putting awareness about crazy stuff like
> that into the language but ... I think you may want to avoid it
> and just wrap the assembly. So a bit of low-level control of the
> behavior there.
Yes, I'm happy to have C/assembly be the source of truth, called from Rust
through wrappers.
> > 2. The only unsafe part that could involve the driver for this would be the
> > creation of IoMemBlock: my expectation is that this would be implemented by the
> > bus driver or some library that maps the appropriate region and caps the size.
> > That is, we can also build a safe abstraction for this.
>
> I suppose this is part of the problem in a way: a language tends to be
> imperialistic: the developers will start thinking "it would all be so much
> easier if I just rewrote also this thing in Rust".
I'm not sure I agree with this. I actually just want to hook things up to the
existing C code and expose a Rust interface that allows developers to benefit
from the guarantees it offers. Unnecessarily rewriting things would slow me
down, so my incentive is to avoid rewrites.
> > 7. We could potentially design a way to limit which offsets are available for a
> > given IoMemBlock, I just haven't thought through it yet, but it would also
> > reduce the number of mistakes a developer could make.
>
> The kernel has an abstraction for memory and register accesses,
> which is the regmap, for example MMIO regmap for simple
> memory-mapped IO:
> drivers/base/regmap/regmap-mmio.c
>
> In a way this is memory safety implemented in C.
I wasn't aware of this. I like it. Thanks for sharing.
> If Rust wants to do this I would strongly recommend it to
> try to look like regmap MMIO.
> See for example drivers/clk/sprd/common.c:
>
> static const struct regmap_config sprdclk_regmap_config = {
> .reg_bits = 32,
> .reg_stride = 4,
> .val_bits = 32,
> .max_register = 0xffff,
> .fast_io = true,
> };
> (...)
> regmap = devm_regmap_init_mmio(&pdev->dev, base,
> &sprdclk_regmap_config);
>
> It is also possible to provide a callback function to determine
> if addresses are readable/writeable.
>
> This is really a devil-in-the-details place where Rust needs
> to watch out to not reimplement regmap in a substandard
> way from what is already available.
At the moment we're only providing wrappers for things we need, so it is mostly
restricted to what I needed for Binder.
If someone wants to write a driver that would benefit from this, we will look
into it and possibly wrap the C implementation.
> Also in many cases developers do not use regmap MMIO
> because it is just too much trouble. They tend to use it
> not because "safety is nice" but because a certain register
> region is very fractured and it is easy to do mistakes and
> write into a read-only register by mistake. So they want
> this, optionally, when the situation demands it.
In Rust, we want all accesses to be safe (within reason), so we probably want to
offer something like IoMemBlock for cases when regmap-mmio is too much hassle.
> It looks nice but it is sadly unrealistic because we need to wrap
> the real assembly accessors in practice (write memory barriers
> and such) and another problem is that it shows that Rust has an
> ambition to do a parallel implementation of regmap.
There is no such ambition. The code in my previous email was written on the spot
as a demonstration per your request.
> > Would you mind sharing more about which aspect of this you feel is challenging?
>
> Good point.
>
> This explanation is going to take some space.
Thanks, I appreciate this.
> I am not able to express it in Rust at all and that is what
> is challenging about it, the examples provided for Rust
> are all about nice behaved computer programs like
> cutesey fibonnacci series and such things and not really
> complex stuff.
I'm sure you're able to express functions and arguments, for example. So going
into the details of the code would have been helpful to me.
> Your binder example is however very good, the problem
> is that it is not a textbook example so the intricacies of
> it are not explained, top down. (I'm not blaming you for
> this, I just say we need that kind of text to get to know
> Rust in the details.)
Do you think a write up about some of what's in there would be helpful? I was
planning to publish some information about the code, including performance
numbers and comparisons of past vulnerabilities once I completed the work.
Probably not to the level of detail that you're seeking but I may look into
having more details about the code if there is demand for it.
> What we need is a good resource to learn it, that
> skips the trivial aspects of the language and goes immediately
> for the interesting details.
>
> It's not like I didn't try.
> I consulted the Rust book on the website of coure.
Did you run into 'Rust for Embedded C Programmers' by any chance
(https://docs.opentitan.org/doc/ug/rust_for_c/)? It's not all up to date but I
found it useful.
> The hard thing to understand in Rust is traits. I don't understand
> traits. I have the level of "a little knowledge is dangerous" and
> I clearly understand this: all kernel developers must have
> a thorough and intuitive understanding of the inner transcendental
> meaning of the concept of a TRAIT, how it was devised, how the
> authors of the language conceptualized it, what effect it is supposed
> to have on generated assembly.
Perhaps we need a 'Rust for Linux Kernel Programmers' in a similar vain to the
page I linked above.
> The language book per se is a bit too terse.
> For example if I read
> https://doc.rust-lang.org/book/appendix-02-operators.html
>
> T: ?Sized : Allow generic type parameter to be a dynamically sized type
>
> This is just self-referential. The description is written in a
> strongly context-dependent language to make a pun ...
> I think every word in that sentence except "allow"and "to be a"
> is dependent on other Rust concepts and thus completely
> unreadable without context.
>
> Instead it is described in a later chapter:
> https://doc.rust-lang.org/book/ch19-04-advanced-types.html
>
> This is more to the point.
>
> "Rust has a particular trait called the Sized trait to
> determine whether or not a type’s size is known at compile time."
> (...) "A trait bound on ?Sized is the opposite of a trait bound on
> Sized: we would read this as “T may or may not be Sized.” This
> syntax is only available for Sized, not any other traits."
>
> But Jesus Christ. This makes me understand less not
> more.
I had similar frustrations when I started on the language, which wasn't that
long ago. One thing that I found useful was to read through some of the RFCs
related to the topic I was interested in: it was time-consuming but helped me
understand not only what was going on but the rationale as well.
> What I find very disturbing is that the authors of the Rust
> language did NOT write it. I think this may be the source
> of a serious flaw. We need this information straight from
> the horse's mouth.
Perhaps you're right... I don't share this feeling though.
> I would strongly advice the Rust community to twist the
> arms of the original Rust authors to go and review and
> edit the Rust book. Possibly rewrite parts of it. This is what
> the world needs to get a more adaptable Rust.
>
> I understand this is a thick requirement, but hey, you are
> competing with C.
I don't think of this as a competition. I'm not arguing for C to be replaced,
only for Rust to be an option for those inclined to use it.
Thanks again,
-Wedson
On 28/04/2021, David Laight <[email protected]> wrote:
> From: Mariusz Ceier
>> Sent: 28 April 2021 19:34
> ....
>>
>> I suggest to wait until featureful GPL implementation of rust language
>> is made (assuming GNU Rust is on the way) before merging any rust code
>> in the kernel and when that implementation is done make a requirement
>> that all rust code must be buildable by at least GPL implementation.
>>
>> Maybe it would also be worthwhile to make the requirement that the
>> kernel must be buildable with free software (not just open source
>> software) explicit ?
>
> Or put the version of the compiler that works in the source tree
> with the kernel and then build it as part of the full build.
>
That is an overkill :)
Even hosting a copy of all required tools at kernel.org might be an
overkill (but who knows);
I think having links at
https://www.kernel.org/doc/html/latest/process/changes.html#getting-updated-software
is enough.
> It is enough of a PITA having to find libelf-devel in order to
> build objtool, never mind having to find the correct version
> of something else.
>
> gcc tends to be available and the version doesn't matter too much.
> But ever that gives problems.
>
> David
>
> -
> Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1
> 1PT, UK
> Registration No: 1397386 (Wales)
>
On 28/04/2021, Nick Desaulniers <[email protected]> wrote:
> On Wed, Apr 28, 2021 at 11:34 AM Mariusz Ceier <[email protected]>
> wrote:
>>
>> Maybe it would also be worthwhile to make the requirement that the
>> kernel must be buildable with free software (not just open source
>> software) explicit ?
>
> The kernel is already buildable by LLVM (and clang); in fact Android,
> CrOS, and Google's production servers already do so.
> https://clangbuiltlinux.github.io/
LLVM/clang is not free software (it's just open source), so it doesn't
matter if kernel builds or not with it. What should matter is whether
it is buildable with at least one GPL compiler like GCC.
> --
> Thanks,
> ~Nick Desaulniers
>
From: Mariusz Ceier
> Sent: 29 April 2021 06:21
>
> On 28/04/2021, Nick Desaulniers <[email protected]> wrote:
> > On Wed, Apr 28, 2021 at 11:34 AM Mariusz Ceier <[email protected]>
> > wrote:
> >>
> >> Maybe it would also be worthwhile to make the requirement that the
> >> kernel must be buildable with free software (not just open source
> >> software) explicit ?
> >
> > The kernel is already buildable by LLVM (and clang); in fact Android,
> > CrOS, and Google's production servers already do so.
> > https://clangbuiltlinux.github.io/
>
> LLVM/clang is not free software (it's just open source), so it doesn't
> matter if kernel builds or not with it. What should matter is whether
> it is buildable with at least one GPL compiler like GCC.
I suspect that what matters for most people is whether the required
compilers (etc) are installed by a default-ish install of their
favourite distribution.
David
-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK
Registration No: 1397386 (Wales)
David Laight <[email protected]> writes:
> From: Mariusz Ceier
>> Sent: 28 April 2021 19:34
> ....
>>
>> I suggest to wait until featureful GPL implementation of rust
>> language
>> is made (assuming GNU Rust is on the way) before merging any
>> rust code
>> in the kernel and when that implementation is done make a
>> requirement
>> that all rust code must be buildable by at least GPL
>> implementation.
>>
>> Maybe it would also be worthwhile to make the requirement that
>> the
>> kernel must be buildable with free software (not just open
>> source
>> software) explicit ?
>
> Or put the version of the compiler that works in the source tree
> with the kernel and then build it as part of the full build.
Building compilers takes several hours, I'm pretty sure usually
much more
than the kernel itself. Building the compiler as part of the full
build
would be a gigantic pain for everyone involved. Rustc is even
worse than
most compilers on that front due to the complexity of its runtime
checks.
--
Kind regards,
Kajetan
Mariusz Ceier <[email protected]> writes:
> Rust compiler license doesn't require for people to give back to
> the
> community - corporation can create their own version of rust
> compiler
> adding some proprietary extensions, develop drivers with it and
> even
> if the drivers code will be GPL'd they won't be buildable by
> anyone
> but that corporation. The rust compiler license doesn't require
> sharing changes when you modify it. The similar problem has flex
> and
> openssl required to build the kernel, but so far no one thought
> about
> abusing them afaik.
Could you explain exactly what the issue you see there is?
Surely if someone develops a proprietary compiler and then writes
kernel
drivers that use that compiler, nobody else will be able to build
them.
Because of that, none of the maintainers will be able to run or
test
the code and it'll never actually get merged into the kernel.
Surely they'd effectively be sabotaging themselves.
--
Kind regards,
Kajetan
On 29/04/2021, Kajetan Puchalski <[email protected]> wrote:
>
> Mariusz Ceier <[email protected]> writes:
>
>> Rust compiler license doesn't require for people to give back to
>> the
>> community - corporation can create their own version of rust
>> compiler
>> adding some proprietary extensions, develop drivers with it and
>> even
>> if the drivers code will be GPL'd they won't be buildable by
>> anyone
>> but that corporation. The rust compiler license doesn't require
>> sharing changes when you modify it. The similar problem has flex
>> and
>> openssl required to build the kernel, but so far no one thought
>> about
>> abusing them afaik.
>
> Could you explain exactly what the issue you see there is?
> Surely if someone develops a proprietary compiler and then writes
> kernel
> drivers that use that compiler, nobody else will be able to build
> them.
> Because of that, none of the maintainers will be able to run or
> test
> the code and it'll never actually get merged into the kernel.
> Surely they'd effectively be sabotaging themselves.
>
Let's assume the hipothetical corporation wants to add some
proprietary stuff to the kernel and avoid sharing the code (sharing
the code is GPL requirement) - maybe they're producing proprietary
hardware e.g. risc-v processor with proprietary ISA extension. So
"none of the maintainers will be able to run or test the code and
it'll never actually get merged into the kernel." is exactly what it
wants.
To do this they could modify any non-GPL tool required to build the
kernel e.g. flex, rust or openssl so that for files with .proprietary
extension they would execute some code (like "patch this file") taken
from database of shell codes based just on .proprietary file name (so
that the contents of .proprietary files will be freely modifiable -
citing GPL: "The source code for a work means the preferred form of
the work for making modifications to it.").
These .proprietary files can be GPL'd since they don't contain any
useful information for outsiders - all of it could be in the shell
codes. The source code of the modified tool wouldn't have to be
shared, since their license doesn't require it.
I think such modified kernel source code would still be
GPL-compatible, but not benefit the kernel community. If the tool was
GPL-licensed, corporation would have to share it's source code - and I
assume also the database of shell codes, due to:
> You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, *to be licensed as a whole* at no charge to all third parties under the terms of this License.
The issue here is, non-GPL tools enable development and distribution
of GPL-compatible yet proprietary versions of the kernel, unless I'm
mistaken.
> --
> Kind regards,
> Kajetan
>
On Thu, Apr 29, 2021 at 10:06 AM Mariusz Ceier <[email protected]> wrote:
>
> Let's assume the hipothetical corporation wants to add some
> proprietary stuff to the kernel and avoid sharing the code
Wouldn't Greg KH be itching to remove such patches from the kernel? If
they made it in, in the first place.
GPL is a necessary, but not sufficient condition for code to be merged
into the kernel. AFAIK the kernel community has the absolute
discretion to refuse any GPLed code for any reason.
IANAL.
On Thu, Apr 29, 2021 at 10:13:23AM -0400, Sven Van Asbroeck wrote:
> On Thu, Apr 29, 2021 at 10:06 AM Mariusz Ceier <[email protected]> wrote:
> >
> > Let's assume the hipothetical corporation wants to add some
> > proprietary stuff to the kernel and avoid sharing the code
>
> Wouldn't Greg KH be itching to remove such patches from the kernel? If
> they made it in, in the first place.
That's not what he was saying, he's saying the code could be distributed
(i.e. on the company's github repo for example) to comply with GPL though
they wouldn't care about getting it merged (like plenty of crappy vendors
today).
But the point is irrelevant since this can already be done using, say,
clang which is already capable of building the kernel and where such
extensions could already be added.
I.e. that's just a non-argument, let's move along.
Willy
On Thu, Apr 29, 2021 at 02:06:12PM +0000, Mariusz Ceier wrote:
> > You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, *to be licensed as a whole* at no charge to all third parties under the terms of this License.
>
>
> The issue here is, non-GPL tools enable development and distribution
> of GPL-compatible yet proprietary versions of the kernel, unless I'm
> mistaken.
And? For your argument to work, we'd need to have the kernel somehow
locked into the use of tools that would have no non-GPL equivalents
*and* would be (somehow) protected from getting such equivalents.
How could that be done, anyway? Undocumented and rapidly changing
features of the tools? We would get screwed by those changes ourselves.
Copyrights on interfaces? Software patents? Some other foulness?
I honestly wonder about the mental contortions needed to describe
something of that sort as "free", but fortunately we are nowhere
near such situation anyway.
I don't like Rust as a language and I'm sceptical about its usefulness
in the kernel, but let's not bring "gcc is better 'cuz GPL" crusades
into that - they are irrelevant anyway, since we demonstrably *not*
locked into gcc on all architectures your hypothetical company would
care about, Rust or no Rust.
On 4/20/21 8:16 AM, Willy Tarreau wrote:
> On Tue, Apr 20, 2021 at 07:56:18AM +0200, Greg Kroah-Hartman wrote:
>> I would LOVE it if some "executives" would see the above presentations,
>> because then they would maybe actually fund developers to fix bugs and
>> maintain the kernel code, instead of only allowing them to add new
>> features.
>>
>> Seriously, that's the real problem, that Dmitry's work has exposed, the
>> lack of people allowed to do this type of bugfixing and maintenance on
>> company time, for something that the company relies on, is a huge issue.
>> "executives" feel that they are willing to fund the initial work and
>> then "throw it over the wall to the community" once it is merged, and
>> then they can forget about it as "the community" will maintain it for
>> them for free. And that's a lie, as Dmitry's work shows.
> That's sadly the eternal situation, and I'm suspecting that software
> development and maintenance is not identified as a requirement for a
> large number of hardware vendors, especially on the consumer side where
> margins are lower. A contractor is paid to develop a driver, *sometimes*
> to try to mainline it (and the later they engage with the community, the
> longer it takes in round trips), and once the code finally gets merged,
> all the initial budget is depleted and no more software work will be
> done.
>
> Worse, we could imagine kicking unmaintained drivers faster off the
> tree, but that would actually help these unscrupulous vendors by
> forcing their customers to switch to the new model :-/ And most of
> them wouldn't care either if their contributions were refused based
> on their track record of not maintaining their code, since they often
> see this as a convenience to please their customers and not something
> they need (after all, relying on a bogus and vulnerable BSP has never
> prevented from selling a device, quite the opposite).
>
> In short, there is a parallel universe where running highly bogus and
> vulnerable out-of-tree code seems like the norm and where there is no
> sort of care for what is mainlined as it's possibly just made to look
> "cool".
In the parallel universe where I spent most time everyone
now need to learn how to make their things to work
out-of-tree. And there is not much of business case trying
to fix and improve core parts of linux. The turn around have
increased a lot and there is no edge doing it.
> We also need to recognize that it's expectable that some vendors are
> not willing to engage on supporting a driver for a decade if they
> expect their device to last 5 years only, and maybe we should make
> some rules clear about mainlining drivers and what to expect for
> users (in which case the end of support would be clear and nobody
> would be surprised if the driver is removed at the end of its
> maintenance, barring a switch to a community maintainer).
Things have changed. Once upon a time the community was
happy if it could get hardware specs.
> Just my two cents,
> Willy
On 29/04/2021, Al Viro <[email protected]> wrote:
> On Thu, Apr 29, 2021 at 02:06:12PM +0000, Mariusz Ceier wrote:
>
>> > You must cause any work that you distribute or publish, that in whole or
>> > in part contains or is derived from the Program or any part thereof, *to
>> > be licensed as a whole* at no charge to all third parties under the
>> > terms of this License.
>>
>>
>> The issue here is, non-GPL tools enable development and distribution
>> of GPL-compatible yet proprietary versions of the kernel, unless I'm
>> mistaken.
>
> And? For your argument to work, we'd need to have the kernel somehow
> locked into the use of tools that would have no non-GPL equivalents
> *and* would be (somehow) protected from getting such equivalents.
> How could that be done, anyway? Undocumented and rapidly changing
> features of the tools? We would get screwed by those changes ourselves.
> Copyrights on interfaces? Software patents? Some other foulness?
>
> I honestly wonder about the mental contortions needed to describe
> something of that sort as "free", but fortunately we are nowhere
> near such situation anyway.
>
Equivalents are not a problem - they can exist as long as the
distributed source would be buildable with GPL tools. I was thinking
that adding a requirement that the distributed kernel source should be
buildable by GPL tools would be enough to protect it from proprietary
extensions. But maybe you're right that this is unrealistic.
> I don't like Rust as a language and I'm sceptical about its usefulness
> in the kernel, but let's not bring "gcc is better 'cuz GPL" crusades
> into that - they are irrelevant anyway, since we demonstrably *not*
> locked into gcc on all architectures your hypothetical company would
> care about, Rust or no Rust.
>
I don't mind the language. I'm more concerned about featureful rust
compiler suddenly being developed behind closed doors.
On 29/04/2021 13:25, Kajetan Puchalski wrote:
>
> Mariusz Ceier <[email protected]> writes:
>
>> Rust compiler license doesn't require for people to give back to the
>> community - corporation can create their own version of rust compiler
>> adding some proprietary extensions, develop drivers with it and even
>> if the drivers code will be GPL'd they won't be buildable by anyone
>> but that corporation.
>
> Could you explain exactly what the issue you see there is?
Kajetan and others, this is an interesting discussion for me. Let us
compare the kernel-specific scope with general OpenSource community and
industry scope.
Industry (where I am working) often requires a "second source" to avoid
the so-called "vendor lock-in", which is the key point of this part of
the discussion.
As soon as Copyleft is involved, the requirement of "second source" is
_permanently_ met: anyone may fork it at any time, creating another
source, (theoretically) avoiding a dead end eternally. Lock-in is
prevented at license level.
IMO this is a _requirement_ for Linux, otherwise its "business model"
wouldn't work in the long term (decades as is always necessary for basic
infrastructure / system software).
If the requirement "second source" (by either way) is not met by Rust at
the moment, this needs to be fixed first.
Other limitations like "development resources" might lead to similar
effects than lock-in. I am seeing the latter nearly every workday.
Software becomes "unmanageable" due to factors like technical debts /
resource restrictions / etc. Typical main reasons are almost always at a
_social_ / _human_ level, while purely technical reasons are playing
only a secondary role.
This is the link to what Greg said earlier in this discussion:
development resources and their _dedication_ (e.g. maintenance vs
creation of "new" things) is the absolute key.
Would Rust improve this problem area _provably_ by at least 30% ?
I am insisting on a _quantifiable_ 30% improvement because this is the
"magic theshold" in industry after which the motto "never change a
running system" can be overcome from an investment perspective, and also
from a risk perspective.
After this, another dimension is kicking in: maturity.
You always need to invest a high effort for achieving "sufficient
maturity". According to the Pareto principle, maintenance is typically
around 70% to 90% of total cost for key infrastructure.
In my working area where end-to-end SLAs of >99.98% have to met, the
Pareto ratio may be even higher.
Pareto's law, as well as Zipf's law, are more or less observational
"natural laws" holding for almost _any_ complex / dynamic system. Even
if you try to improve such universal laws, e.g. by investing a lot of
effort / resources / money into maintenance reduction techniques, you
typically end up at a similar _total_ effort for maintenance (including
the extra effort for reduction of "ordinary" maintenance) than before.
Otherwise, you would have found a way for bypassing natural laws like
the observed Pareto law. Even billions of years of biological evolution
on this earth weren't able to change this universal law in statistical
average (in global scale). Otherwise we couldn't observe it anymore.
Even if you could improve the Pareto ratio, my experience is that upper
management will kick in and raise the SLA level so that Pareto holds
again ;)
So I'm sceptical that new technologies like Rust will change fundamental
laws, e.g. with respect to relative maintenance efforts.
However, what _could_ be theoretically possible: _productivity_ gains,
improving both development of "new" things as well as "maintenance"
efforts, in total by more than 30% (but not the Pareto ratio between them).
So the question is: can Rust _provably_ lead to *quantifiable* total
productivity gains of at least 30% ?
If this would be the case, any business case needs further alternatives.
So it needs to be compared at least with alternative B: what would be
the effort and the productivity gain when introducing similar technology
non-disruptively into the current development ecosystem?
Even if this A-B comparison would lead to a conclusion that 30% cannot
be met by a new and partly disruptive technology like Rust, the
discussion can be fruitful. There is always a chance to introduce some
parts of a new technology into a well-proven and mature "old" technology
non-disruptively.
Cheers,
Thomas
From: Thomas Schoebel-Theuer
> Sent: 30 April 2021 07:40
...
> Industry (where I am working) often requires a "second source" to avoid
> the so-called "vendor lock-in", which is the key point of this part of
> the discussion.
There is also the related problem that you need to be able to come
back in 5 years time and re-build the original image.
You can then make minor changes, rebuild, and have a reasonable
confidence that there are no side effects.
This means that web-based and auto-updated tools cannot be used.
Even a VM image might suddenly fall foul of changes to hypervisors.
So you need to keep (at least) 2 system than contain all the build
tools just in case you need to do a maintenance build of an old release.
But even then we can no longer build drivers for some windows
systems because we can't sign them with the old keys.
David
-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK
Registration No: 1397386 (Wales)
On Wed, Apr 28, 2021 at 5:10 AM Miguel Ojeda
<[email protected]> wrote:
> On Tue, Apr 27, 2021 at 1:13 PM Linus Walleij <[email protected]> wrote:
> >
> > Actually my reply to Wedson brought up a new issue, which is the
> > quality of learning resources and the lack of an equivalent to
> > The C Programming Language book.
>
> I recall having a similar feeling when initially jumping into
> individual chapters of The Rust Programming Language book. I think it
> is intended to be read from cover to cover instead.
>
> There are other resources, see [1]. For instance, there is The
> Embedded Rust Book [2]. Some of those are a WIP, but perhaps others
> can recommend better finished/published books.
>
> In any case, Rust has more features than C, some of them quite unique,
> and they are routinely used, so it does take some time to learn.
No joke, I do try. I thought it would be easier since I have written
a fair share of Haskell in my years but Rust is this hybrid of
imperative and functional that just make things a bit hard
to pin down: is this part functional? is it imperative?
is it object oriented? etc.
But the reference that Robin pointed to is better to read. It
is not as talkative and more to the point. So now I am working
with that.
> > I think a good starting point would be to either fix Rust support in
> > GCC or implement some more important ISAs in LLVM,
> > whichever is easiest. I don't mind having just *one* compiler but
> > I mind having *a* compiler for every arch we support.
> >
> > [...]
> >
> > Portability to old systems and ISAs is a virtue in itself
> > because of the effect it has on code quality, not necessarily
> > for the support itself.
>
> I agree that there are benefits of keeping compiler technology
> flexible, but one cannot force or expect any project (including the
> Linux kernel) to maintain all code forever.
>
> In the end, we need to balance that adaptability against the benefits
> of adding Rust. In particular because nowadays LLVM is able to cover
> the majority of devices that want to run the very latest Linux
> kernels. Thus those benefits apply to most users. If LLVM only
> supported, say, x86_64, I would agree that it would not be enough.
>
> By contrast, compiler flexibility only matters indirectly to users,
> and at some point there are diminishing returns to keeping all
> architectures around.
My values in this regard are 180 degrees opposed to yours.
My attitude to the problem, was I to fix it, would be
"let's go fix a frontend for Rust to GCC, how hard can it be"
rather than trying to avoid that work with this kind of
reasoning trying to one way or other prove that it is not
worth the effort.
A GCC front-end will allow you to run Rust on all Linux
target architectures which is a big win. LLVM can be
expanded with backends for all archs as well but that
seems like a much bigger job to me.
Another argument can be made that for Rust to be
perceived as mature, two independent implementations
should exist anyway.
The IETF Standards Process (RFC 2026, updated by
RFC 6410) requires at least two independent and
interoperable implementations for advancing a protocol
specification to Internet Standard.
Why should the kernel programming languages have any
lower standards than that?
The C programming language has earned its place thanks
to perseverance of implementing and reimplementing
compilers for it again and again.
Fixing proper compilers may take a few years, like
5 or 10. But who cares? We are in it for the long run
anyway. When I designed the character device interface
for GPIO I said I expect it to be maintained for at least
100 years. This is my honest perspective of things.
Torvalds has this saying (from Edison) that kernel
engineering is 1% inspiration and 99% perspiration.
Well let's live by that and fix those compilers.
When Linux was developed in 1992 C had existed since
1973 so it was 19 years old.
Now Linux is 28 years old and C is 47 years old.
This discussion needs perspective. And we really
cannot have development with a finger constantly
pushing the fastforward button.
> In any case, adding Rust (in particular for "leaf" modules) does not
> imply that we will lose those architectures any time soon.
But I am not convinced that writing device drivers is the right
thing to use Rust for in the kernel.
There are some stuff in device driver frameworks, such as USB
hierarchies or (battery) charging state machines, that can be
really good to rewrite in Rust. But these rewrites would affect
anything with a USB port for example, including Nios II and
Motorola 68k systems. So then the compiler support for all
archs is needed first.
> That would
> take at least several years, and would require quite a few things to
> happen at the same time:
>
> - That Rust got so widely used in the kernel (because the benefits
> turned out to be important) that maintainers went as far as wanting to
> drop C drivers from mainline for Rust equivalents.
>
> - That GCC did not get any way to compile Rust (no Rust frontend for
> GCC, no GCC backend for `rustc`, etc.) and, moreover, that the plans
> for that had been dropped.
>
> - That LLVM did not add support for the missing architectures.
>
> The first point is unlikely any time soon. The second point is
> unlikely, too, given there is funding for that now (and I assume those
> projects will receive more support if Rust lands in the kernel). The
> third point is likely, though.
What about patience?
I am thrilled to hear that GCC is growing Rust support and has funding
to fix a proper front-end. This is what Rust needs in general and
what the kernel needs in particular.
I think right now the right thing for Rust is to work out-of-tree until
there is Rust support for all archs, while encouraging kernel
developers to learn the language.
Yours,
Linus Walleij
On Tue, May 4, 2021 at 11:21 PM Linus Walleij <[email protected]> wrote:
>
> Another argument can be made that for Rust to be
> perceived as mature, two independent implementations
> should exist anyway.
Many people agree, and in fact it may not be that far away. On related
news, the GCC frontend for Rust is now in Compiler Explorer, e.g.
https://godbolt.org/z/Wjbe5dTTb
I just requested `mrustc` (the Rust transpiler written in C++ for
bootstrapping purposes) to the Compiler Explorer folks to have it
there too.
> Fixing proper compilers may take a few years, like
> 5 or 10. But who cares? We are in it for the long run
I don't think it will take 5 years to see a new frontend (in
particular if only for valid code).
But even if it does, I don't see why we would need to wait for that to
start setting up Rust for the kernel if the decision is made to do so.
In fact, getting into the kernel can be an incentive for a new
frontend to say "we are now able to compile the kernel".
There are also other advantages to start the work now, such as working
out the currently-nightly features we need in the Rust language and
the standard library, getting them stabilized, submitting upstream
fixes (I had to implement a couple small ones), etc.
That way, when the time comes that we announce a minimum Rust stable
version, all that is ready for other frontends too.
> But I am not convinced that writing device drivers is the right
> thing to use Rust for in the kernel.
That is fair, hopefully the picture will be clearer when we get the
first drivers that talk to real hardware.
> There are some stuff in device driver frameworks, such as USB
> hierarchies or (battery) charging state machines, that can be
> really good to rewrite in Rust. But these rewrites would affect
> anything with a USB port for example, including Nios II and
> Motorola 68k systems. So then the compiler support for all
> archs is needed first.
I would avoid a rewrite, but similarly to one of the previous points,
I don't see why work cannot already start if a maintainer is keen on
using Rust (and able to maintain both to some degree).
> I think right now the right thing for Rust is to work out-of-tree until
> there is Rust support for all archs, while encouraging kernel
> developers to learn the language.
That would be an option, yes, but if the decision ends up being made
and we are encouraging kernel developers to learn the language, what
do we achieve by keeping things out-of-tree?
In fact, by getting in-tree people, organizations & companies would be
encouraged to give more support sooner rather than later to the LLVM
backends they care about and/or to the GCC frontend for Rust. So, in a
way, it can be a win for those projects too.
Cheers,
Miguel
On Wed, May 5, 2021 at 1:30 AM Miguel Ojeda
<[email protected]> wrote:
> On Tue, May 4, 2021 at 11:21 PM Linus Walleij <[email protected]> wrote:
> > I think right now the right thing for Rust is to work out-of-tree until
> > there is Rust support for all archs, while encouraging kernel
> > developers to learn the language.
>
> That would be an option, yes, but if the decision ends up being made
> and we are encouraging kernel developers to learn the language, what
> do we achieve by keeping things out-of-tree?
>
> In fact, by getting in-tree people, organizations & companies would be
> encouraged to give more support sooner rather than later to the LLVM
> backends they care about and/or to the GCC frontend for Rust. So, in a
> way, it can be a win for those projects too.
In a way it is a fair point because for example Unix and C evolved
together and were intermingled at the onset. And they kind of
needed each other to evolve.
Right now it seems like those organizations and companies
would be some academic institutions who like rust (because they
study languages and compilers) and Google. But that is a
pretty nice start, and one upside I would see in it is that
the academic people stop writing so many papers and get their
hands dirty and work on practical problems in the kernel. So
if that can be achieved I would be happy.
Yours,
Linus Walleij
On 30.04.21 08:39, Thomas Schoebel-Theuer wrote:
Hi,
> IMO this is a _requirement_ for Linux, otherwise its "business model"
> wouldn't work in the long term (decades as is always necessary for basic
> infrastructure / system software).
ACK. And speaking for embedded world, 20+ product lifetime is pretty
common. During that lifetime you'd need to be able to pick out old
sources, so some changes and rebuild your code and having your system
still running seamlessly after the update. IOW: long-term
reproducability is absolutely vital. Linux does much better here than
many competitors (that eg. need proprietary build tools that don't
even run later machine generations)
> If the requirement "second source" (by either way) is not met by Rust at
> the moment, this needs to be fixed first.
Yes, and also adding long-term reproducability as another vital requirement.
Rust seems to be a fast moving target. Even building a Rust compiler can
be a pretty complex task (if you're not a full time rust developer).
Gcc, in constrast, itself can be built on older compilers (even non-
gcc). How to do that w/ rustc ? According to my observations some while
ago, it needs a fairly recent rustc to compile recent rustc, so when
coming with an old version, one has to do a longer chain of rustc
builds first. Doesn't look exactly appealing for enterprise grade and
long term support.
> Other limitations like "development resources" might lead to similar
> effects than lock-in. I am seeing the latter nearly every workday.
> Software becomes "unmanageable" due to factors like technical debts /
> resource restrictions / etc. Typical main reasons are almost always at a
> _social_ / _human_ level, while purely technical reasons are playing
> only a secondary role.
Correct, the amount of people who understand rust is pretty low, those
who also understand enough of linux kernel development, probably just
a hand full world wide. For any practical business use case this
practically means: unsupported.
I don't like the idea of Linux being catapulted back from enterprise
grade to academic toy.
--mtx
--
---
Hinweis: unverschlüsselte E-Mails können leicht abgehört und manipuliert
werden ! Für eine vertrauliche Kommunikation senden Sie bitte ihren
GPG/PGP-Schlüssel zu.
---
Enrico Weigelt, metux IT consult
Free software and Linux embedded engineering
[email protected] -- +49-151-27565287
On Wed, May 5, 2021 at 1:34 PM Linus Walleij <[email protected]> wrote:
>
> Right now it seems like those organizations and companies
> would be some academic institutions who like rust (because they
> study languages and compilers) and Google. But that is a
Note that there are quite a few major private players already
involved, not just Google! e.g.
- The Rust Foundation has AWS, Facebook, Google, Huawei, Microsoft
and Mozilla: https://foundation.rust-lang.org/
- AWS and Facebook using Rust for a few years now:
https://engineering.fb.com/2021/04/29/developer-tools/rust/ and
https://aws.amazon.com/blogs/opensource/how-our-aws-rust-team-will-contribute-to-rusts-future-successes/
- Microsoft providing official Win32 bindings/docs for Rust:
https://github.com/microsoft/windows-rs and
https://docs.microsoft.com/en-us/windows/dev-environment/rust/overview
Of course, any major company uses most common languages at some point
or another, but their commitment looks significant now (and public).
Cheers,
Miguel
On Wed, May 5, 2021 at 3:59 PM Enrico Weigelt, metux IT consult
<[email protected]> wrote:
>
> ACK. And speaking for embedded world, 20+ product lifetime is pretty
> common. During that lifetime you'd need to be able to pick out old
> sources, so some changes and rebuild your code and having your system
> still running seamlessly after the update. IOW: long-term
> reproducability is absolutely vital. Linux does much better here than
> many competitors (that eg. need proprietary build tools that don't
> even run later machine generations)
You should be able to rebuild old releases with newer compilers.
Like the major C and C++ compilers keep support for old code and old
standards, the main Rust compiler keeps support for old code and old
"editions" too.
> Yes, and also adding long-term reproducability as another vital requirement.
See my sibling replies to Linus W. on the efforts underway around this.
> Rust seems to be a fast moving target. Even building a Rust compiler can
> be a pretty complex task (if you're not a full time rust developer).
It only takes a handful of commands. If you know how to build GCC or
LLVM, building Rust is about the same complexity.
> Gcc, in constrast, itself can be built on older compilers (even non-
> gcc). How to do that w/ rustc ? According to my observations some while
> ago, it needs a fairly recent rustc to compile recent rustc, so when
> coming with an old version, one has to do a longer chain of rustc
> builds first. Doesn't look exactly appealing for enterprise grade and
> long term support.
Why would enterprise users care about bootstrapping? Companies
typically want to use supported software, so they would use the
pre-built compiler their distribution offers support for.
For companies that want more features, they can use newer versions via
the pre-built official binaries from the Rust project itself, which
are routinely used by many projects around the world. Some companies
are even using particular (i.e. frozen) Rust nightly compilers they
picked.
> Correct, the amount of people who understand rust is pretty low, those
> who also understand enough of linux kernel development, probably just
> a hand full world wide. For any practical business use case this
> practically means: unsupported.
This assumes Rust-enabled kernels will be provided by distributions to
businesses from day 1 as soon as supports gets merged.
Instead, what will need to happen first is that we evolve the support
enough to compile the kernel with a Rust stable compiler, some
important drivers get written *and* distributions start shipping those
drivers in their business-oriented releases.
That will take some time, and interested companies (e.g. for drivers)
and their kernel developers will learn how to use Rust in the
meantime.
Cheers,
Miguel
On 05.05.21 16:17, Miguel Ojeda wrote:
> Note that there are quite a few major private players already
> involved, not just Google! e.g.
>
> - The Rust Foundation has AWS, Facebook, Google, Huawei, Microsoft
> and Mozilla: https://foundation.rust-lang.org/
>
> - AWS and Facebook using Rust for a few years now:
> https://engineering.fb.com/2021/04/29/developer-tools/rust/ and
> https://aws.amazon.com/blogs/opensource/how-our-aws-rust-team-will-contribute-to-rusts-future-successes/
>
> - Microsoft providing official Win32 bindings/docs for Rust:
> https://github.com/microsoft/windows-rs and
> https://docs.microsoft.com/en-us/windows/dev-environment/rust/overview
Exactly a list of corporations, I'd never want to rely on.
--mtx
--
---
Hinweis: unverschlüsselte E-Mails können leicht abgehört und manipuliert
werden ! Für eine vertrauliche Kommunikation senden Sie bitte ihren
GPG/PGP-Schlüssel zu.
---
Enrico Weigelt, metux IT consult
Free software and Linux embedded engineering
[email protected] -- +49-151-27565287
On Wed, May 5, 2021 at 4:17 PM Miguel Ojeda
<[email protected]> wrote:
> On Wed, May 5, 2021 at 1:34 PM Linus Walleij <[email protected]> wrote:
> >
> > Right now it seems like those organizations and companies
> > would be some academic institutions who like rust (because they
> > study languages and compilers) and Google. But that is a
>
> Note that there are quite a few major private players already
> involved, not just Google! e.g.
I was referring to entities interested in using Rust for the
Linux kernel. Not just "using rust". And that interest is coming
from Google and a few academic institutions AFAICT.
Yours,
Linus Walleij
On Thu, May 6, 2021 at 2:47 PM Linus Walleij <[email protected]> wrote:
>
> I was referring to entities interested in using Rust for the
> Linux kernel. Not just "using rust". And that interest is coming
> from Google and a few academic institutions AFAICT.
There is interest from a handful of major companies in using Rust for
the Linux kernel. For instance, we had a few present in our latest
informal call.
I am also aware of at least two having or looking to
have someone working on testing the waters of what we have done so
far for their use cases.
Cheers,
Miguel
Hey Miguel and others,
Having followed this for a while, I finally decided it best to at least
share some thoughts in the hopes to make life better for us with some
readability/accessibility issues, such as dyslexia for example.
I apologize for being late to the party and for potentially using the
wrong thread, but I recall somewhere in v5 that it was best to respond
to the RFC for general comments.
On 14-04-2021 20:45, [email protected] wrote:
> From: Miguel Ojeda <[email protected]>
>
> 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.
>
<snip>
>
> 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
Enforcing this is great, but how will you enforce this 'everywhere'?
Right now, you can easily 'bypass' any CI put in place, and while 'for
now' this is only about the Rust infra, where this can be strongly
enforced, once we see actual drivers pop-up; these won't go through the
Rust CI before merging CI forever? A maintainer can 'just merge'
something still, right?
Anyway, what I wanted to criticize, is the so called "keeping with
`rustfmt` defaults". It has been known, that, well Rust's defaults are
pretty biased and opinionated. For the Rust project, that's fair of
course, their code, their rules.
However, there's two arguments against that. For one, using the Rust
'style', now means there's 2 different code styles in the Kernel.
Cognitively alone, that can be quite frustrating and annoying. Having to
go back and forth between two styles can be mentally challenging which
only causes mistakes and frustration. So why change something that
already exists? Also, see my first point. Having to constantly
remember/switch to 'in this file/function the curly brace is on a
different line'. Lets try to stay consistent, the rules may not be
perfect (80 columns ;), but so far consistency is tried. OCD and Autism
etc doesn't help with this ;)
Secondly, and this is really far more important, the Rust default style
is not very inclusive, as it makes readability harder. This has been
brought up by many others in plenty of places, including the `rustfmt`
issue tracker under bug #4067 [0]. While the discussion eventually only
led to the 'fmt-rfcs' [1], where it was basically said 'you could be on
to something, but this ship has sailed 3 years ago (when nobody was
looking caring), and while we hear you, we're not going to change our
defaults anymore.
But I also agree and share these commenters pain. When the tab character
is used for indenting (and not alignment mind you), then visually
impaired (who can still be amazing coders) can more easily read code by
adjusting the width what works best to them.
With even git renaming `master` to `main` to be more inclusive, can we
also be more inclusive to us that have a hard time distinguishing narrow
indentations?
Thanks, and sorry for rubbing any ones nerves, but to "some of us" this
actually matters a great deal.
Olliver
P.S. would we expect inline C/Rust code mixed? What then?
<snip>
[0]: https://github.com/rust-lang/rustfmt/issues/4067#issuecomment-685961408
[1]:
https://github.com/rust-dev-tools/fmt-rfcs/issues/1#issuecomment-911804826
Hi Olliver,
On Mon, Jun 20, 2022 at 5:11 PM Olliver Schinagl <[email protected]> wrote:
>
> I apologize for being late to the party and for potentially using the
> wrong thread, but I recall somewhere in v5 that it was best to respond
> to the RFC for general comments.
No need to apologize! Feel free to use the latest threads or a new
thread in e.g. the rust-for-linux ML.
> On 14-04-2021 20:45, [email protected] wrote:
> > From: Miguel Ojeda <[email protected]>
> >
> > 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
>
> Enforcing this is great, but how will you enforce this 'everywhere'?
> Right now, you can easily 'bypass' any CI put in place, and while 'for
> now' this is only about the Rust infra, where this can be strongly
> enforced, once we see actual drivers pop-up; these won't go through the
> Rust CI before merging CI forever? A maintainer can 'just merge'
> something still, right?
Indeed, but there are workarounds, for instance, we could have a bot
checking -next.
Or we could put it in an opt-in compilation mode (i.e. not for users)
where extra things are checked (like `W=`) that maintainers use so
that e.g. `allmodconfig` builds are kept clean.
> Anyway, what I wanted to criticize, is the so called "keeping with
> `rustfmt` defaults". It has been known, that, well Rust's defaults are
> pretty biased and opinionated. For the Rust project, that's fair of
> course, their code, their rules.
>
> However, there's two arguments against that. For one, using the Rust
> 'style', now means there's 2 different code styles in the Kernel.
> Cognitively alone, that can be quite frustrating and annoying. Having to
> go back and forth between two styles can be mentally challenging which
> only causes mistakes and frustration. So why change something that
> already exists? Also, see my first point. Having to constantly
> remember/switch to 'in this file/function the curly brace is on a
> different line'. Lets try to stay consistent, the rules may not be
> perfect (80 columns ;), but so far consistency is tried. OCD and Autism
> etc doesn't help with this ;)
Note that the point of using `rustfmt` is that one does not need to
care about the details -- one can e.g. run the tool on file save. So
no need to remember how to do it when writing Rust.
Now, it is true that the Rust syntax resembles C in many cases, so
things like the curly braces for function definitions are similar
enough that we could do the same thing in both sides.
However, most Rust code uses `rustfmt` and typically also follow most
of its defaults, including the standard library, books, etc.; which
helps when reading and reusing other code. This is different from C
and C++, where as you know there is no single style (at least as
prevalent as `rustfmt`), thus one needs to become accustomed to each
project's C style (or ideally use `clang-format` to avoid having to
learn it). So while this is not relevant for C, in the case of Rust,
there is value in using the `rustfmt` style.
As for consistency, one could argue that by using `rustfmt` we are
being consistent with the rest of the Rust code out there. This may be
important for those that have expressed interest on sharing some code
between kernel and userspace; as well as if we end up vendoring some
external crates (similar to what we do with `alloc` now).
> Secondly, and this is really far more important, the Rust default style
> is not very inclusive, as it makes readability harder. This has been
> brought up by many others in plenty of places, including the `rustfmt`
> issue tracker under bug #4067 [0]. While the discussion eventually only
> led to the 'fmt-rfcs' [1], where it was basically said 'you could be on
> to something, but this ship has sailed 3 years ago (when nobody was
> looking caring), and while we hear you, we're not going to change our
> defaults anymore.
>
> But I also agree and share these commenters pain. When the tab character
> is used for indenting (and not alignment mind you), then visually
> impaired (who can still be amazing coders) can more easily read code by
> adjusting the width what works best to them.
>
> With even git renaming `master` to `main` to be more inclusive, can we
> also be more inclusive to us that have a hard time distinguishing narrow
> indentations?
As noted in the RFC, we are happy to tweak the style to whatever
kernel developers prefer. We think the particular style is not that
important. Absent other reasons, the defaults seem OK, so we chose
that for simplicity and consistency with as most existing Rust code as
possible.
As for accessibility, I am no expert, so that may be a good point,
especially if editors cannot solve this on their end (so that everyone
could program in all languages/projects regardless of style).
> Thanks, and sorry for rubbing any ones nerves, but to "some of us" this
> actually matters a great deal.
No nerves were damaged :) Thanks for all the input!
> P.S. would we expect inline C/Rust code mixed? What then?
Everything is possible, e.g. we could have Rust proc macros that parse
C and things like that. But if we ended up with such a thing, the
solution would be to format each accordingly to its style (indentation
could be an exception, I guess).
Cheers,
Miguel
Hey Miguel,
Sorry for the late reply ;)
On 27-06-2022 19:44, Miguel Ojeda wrote:
> Hi Olliver,
>
> On Mon, Jun 20, 2022 at 5:11 PM Olliver Schinagl <[email protected]> wrote:
>>
>> I apologize for being late to the party and for potentially using the
>> wrong thread, but I recall somewhere in v5 that it was best to respond
>> to the RFC for general comments.
>
> No need to apologize! Feel free to use the latest threads or a new
> thread in e.g. the rust-for-linux ML.
>
>> On 14-04-2021 20:45, [email protected] wrote:
>>> From: Miguel Ojeda <[email protected]>
>>>
>>> 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
>>
>> Enforcing this is great, but how will you enforce this 'everywhere'?
>> Right now, you can easily 'bypass' any CI put in place, and while 'for
>> now' this is only about the Rust infra, where this can be strongly
>> enforced, once we see actual drivers pop-up; these won't go through the
>> Rust CI before merging CI forever? A maintainer can 'just merge'
>> something still, right?
>
> Indeed, but there are workarounds, for instance, we could have a bot
> checking -next.
Absolutly, but with the many luitenants, many tree's, and not a single
CI source, this would still be tricky in the end; but certainly possible.
>
> Or we could put it in an opt-in compilation mode (i.e. not for users)
> where extra things are checked (like `W=`) that maintainers use so
> that e.g. `allmodconfig` builds are kept clean.
>
>> Anyway, what I wanted to criticize, is the so called "keeping with
>> `rustfmt` defaults". It has been known, that, well Rust's defaults are
>> pretty biased and opinionated. For the Rust project, that's fair of
>> course, their code, their rules.
>>
>> However, there's two arguments against that. For one, using the Rust
>> 'style', now means there's 2 different code styles in the Kernel.
>> Cognitively alone, that can be quite frustrating and annoying. Having to
>> go back and forth between two styles can be mentally challenging which
>> only causes mistakes and frustration. So why change something that
>> already exists? Also, see my first point. Having to constantly
>> remember/switch to 'in this file/function the curly brace is on a
>> different line'. Lets try to stay consistent, the rules may not be
>> perfect (80 columns ;), but so far consistency is tried. OCD and Autism
>> etc doesn't help with this ;)
>
> Note that the point of using `rustfmt` is that one does not need to
> care about the details -- one can e.g. run the tool on file save. So
> no need to remember how to do it when writing Rust.
And that's great of course, I was mearly speaking of the configuration
of rustfmt. I think as a tool it's pretty great!
>
> Now, it is true that the Rust syntax resembles C in many cases, so
> things like the curly braces for function definitions are similar
> enough that we could do the same thing in both sides.
>
> However, most Rust code uses `rustfmt` and typically also follow most
> of its defaults, including the standard library, books, etc.; which
> helps when reading and reusing other code. This is different from C
> and C++, where as you know there is no single style (at least as
> prevalent as `rustfmt`), thus one needs to become accustomed to each
> project's C style (or ideally use `clang-format` to avoid having to
> learn it). So while this is not relevant for C, in the case of Rust,
> there is value in using the `rustfmt` style.
I think this is a pretty poor argument for following Rust's opinionated
view of the world. E.g. it's generally bad to copy/paste code to begin
with. How many 'bugs' that we know of are copy/paste bugs?
Secondly, and more importantly so; you argue 'who cares about people
with disablements, atleast its equally hard to read everywhere' which is
a very poor argument :p
Finally, it must of course be mentioned, that rust is really trying to
do an XKCD here, https://xkcd.com/927/ though I'm sure we'll get it
right this time around ;)
>
> As for consistency, one could argue that by using `rustfmt` we are
> being consistent with the rest of the Rust code out there.
But you are not, only those that follow rust's biased view. Everybody
else that has a different opinion (like die-hard C programmers) that
care enough (I'm sure there's plenty) would setup their rustfmt config
file to resemble their C code; and thus the entire premisis is broken.
Though; yes, in a perfect world it could have worked like this, but xkcd
again :)
> This may be
> important for those that have expressed interest on sharing some code
> between kernel and userspace; as well as if we end up vendoring some
> external crates (similar to what we do with `alloc` now).
This though is a fair argument I understand, it would be weird in having
2 styles in user-space and kernel-space code; though I see this
happening today as well; where developers follow kernel style for kernel
code (obviously) but use their preferred 2 or 3 space style on their
userland code. Trying to 'force' this, usually however never gets the
intended result ...
>
>> Secondly, and this is really far more important, the Rust default style
>> is not very inclusive, as it makes readability harder. This has been
>> brought up by many others in plenty of places, including the `rustfmt`
>> issue tracker under bug #4067 [0]. While the discussion eventually only
>> led to the 'fmt-rfcs' [1], where it was basically said 'you could be on
>> to something, but this ship has sailed 3 years ago (when nobody was
>> looking caring), and while we hear you, we're not going to change our
>> defaults anymore.
>>
>> But I also agree and share these commenters pain. When the tab character
>> is used for indenting (and not alignment mind you), then visually
>> impaired (who can still be amazing coders) can more easily read code by
>> adjusting the width what works best to them.
>>
>> With even git renaming `master` to `main` to be more inclusive, can we
>> also be more inclusive to us that have a hard time distinguishing narrow
>> indentations?
>
> As noted in the RFC, we are happy to tweak the style to whatever
> kernel developers prefer. We think the particular style is not that
> important. Absent other reasons, the defaults seem OK, so we chose
> that for simplicity and consistency with as most existing Rust code as
> possible.
>
> As for accessibility, I am no expert, so that may be a good point,
> especially if editors cannot solve this on their end (so that everyone
> could program in all languages/projects regardless of style).
Yeah, this is a common reasoning. People without disabilities often
oversee cases to those with. E.g. Traffic lights being red and green is
horrible for colorblind people; luckily enough we have 'order' to help
distinguish there for example. While I'm not colorblind myself, I often
have to remind UX designers, with their fancy LED based UI's, to think
of others as well, which always strikes them as odd first, then of
course they only start to realize this.
I'm with you that style is the least important for the functionality, no
argument there. Long-term though; this will matter of course, to those
like me, have hard times here.
>
>> Thanks, and sorry for rubbing any ones nerves, but to "some of us" this
>> actually matters a great deal.
>
> No nerves were damaged :) Thanks for all the input!
>
>> P.S. would we expect inline C/Rust code mixed? What then?
>
> Everything is possible, e.g. we could have Rust proc macros that parse
> C and things like that. But if we ended up with such a thing, the
> solution would be to format each accordingly to its style (indentation
> could be an exception, I guess).
The first exception to the rule starts here already :p
Thanks for your thoughts,
Olliver
>
> Cheers,
> Miguel
On Mon, Jul 18, 2022 at 8:56 AM Olliver Schinagl
<[email protected]> wrote:
>
> Absolutly, but with the many luitenants, many tree's, and not a single
> CI source, this would still be tricky in the end; but certainly possible.
A bot in -next (possibly an existing one) is a single thing to care
about and the amount of maintainers/trees doesn't have an effect on
it, thus I don't think it would be tricky.
> I think this is a pretty poor argument for following Rust's opinionated
> view of the world. E.g. it's generally bad to copy/paste code to begin
> with. How many 'bugs' that we know of are copy/paste bugs?
We will have to disagree. Consistency and simplicity are fine
arguments in my book, not "pretty poor" ones.
I don't see the relevance of the copy/paste code discussion here. But
assuming the analogy makes sense, I don't agree that reusing code is
"generally bad" either.
Anyway, given you mention "bugs", I think you are implying that the
defaults are somehow "incorrect" (not accessible?). In that case, to
improve things for all Rust developers out there, I would suggest
opening an issue in https://github.com/rust-dev-tools/fmt-rfcs.
> Secondly, and more importantly so; you argue 'who cares about people
> with disablements, atleast its equally hard to read everywhere' which is
> a very poor argument :p
No, and I want to be __very__ clear about this: at no point I have
argued "who cares about people with disabilities" or anything like it.
It is insulting that you even suggest it.
Likewise, you are the one claiming it is "hard to read", not me.
And then after constructing those straw men, you call them "a very
poor argument"...
> Finally, it must of course be mentioned, that rust is really trying to
> do an XKCD here, https://xkcd.com/927/ though I'm sure we'll get it
> right this time around ;)
How does that even apply here? There is no "standard" for formatting
across languages, if that is what you are saying.
Actually, what is happening here is that there is an "official" tool,
called rustfmt, that most Rust code out there uses.
By not using it, it is you the one that may be creating a XKCD
situation, if anything.
And to be clear, we don't necessarily follow "Rust's biased view". For
instance, there is also an "official" build tool, called Cargo, that
most Rust code out there uses; yet we are not using it for the kernel.
We are actually doing things how we think are best for the kernel. Not
because "Rust" (whatever or whoever that is) is "trying to do an
XKCD". Not because we are "following Rust's opinionated view of the
world" or "Rust's biased view".
> But you are not, only those that follow rust's biased view. Everybody
> else that has a different opinion (like die-hard C programmers) that
> care enough (I'm sure there's plenty) would setup their rustfmt config
> file to resemble their C code; and thus the entire premisis is broken.
> Though; yes, in a perfect world it could have worked like this, but xkcd
> again :)
No. I said we are being consistent with the majority of the Rust code
out there, not with "everybody".
If, instead, we try to be consistent with the kernel C style, then you
are likely not being consistent with the majority of the Rust code out
there. And you would have to decide exactly how to map the C style to
Rust constructs and which particular kernel style.
Again: I personally don't mind what the particular style is. As a
project, what we value the most is having a single style across it and
not having to think about formatting. Nevertheless, I think there is
also value in being consistent with the majority of the Rust code out
there.
> This though is a fair argument I understand, it would be weird in having
> 2 styles in user-space and kernel-space code; though I see this
> happening today as well; where developers follow kernel style for kernel
> code (obviously) but use their preferred 2 or 3 space style on their
> userland code. Trying to 'force' this, usually however never gets the
> intended result ...
If we follow the usual Rust style in the kernel, I would say it is
more likely that both styles match.
Cheers,
Miguel
On Wed, 20 Jul 2022, Miguel Ojeda wrote:
> On Mon, Jul 18, 2022 at 8:56 AM Olliver Schinagl
> <[email protected]> wrote:
>
> > Secondly, and more importantly so; you argue 'who cares about people
> > with disablements, atleast its equally hard to read everywhere' which is
> > a very poor argument :p
>
> No, and I want to be __very__ clear about this: at no point I have
> argued "who cares about people with disabilities" or anything like it.
> It is insulting that you even suggest it.
What "people with disablements" have to do with this anyway?
I don't get it.
Nicolas
Hey Miguel,
Just to make it very clear from the beginning, while this is a sensitive
subject, and I am very passionate about it; In no way, I am being
offensive, insulting or rude. This would be obvious in a f2f discussion
of course, but alas. I do try to format my text as friendly as I can,
but some things get lost in translation ;)
On 20-07-2022 21:23, Miguel Ojeda wrote:
> On Mon, Jul 18, 2022 at 8:56 AM Olliver Schinagl
> <[email protected]> wrote:
>>
>> Absolutly, but with the many luitenants, many tree's, and not a single
>> CI source, this would still be tricky in the end; but certainly possible.
>
> A bot in -next (possibly an existing one) is a single thing to care
> about and the amount of maintainers/trees doesn't have an effect on
> it, thus I don't think it would be tricky.
>
>> I think this is a pretty poor argument for following Rust's opinionated
>> view of the world. E.g. it's generally bad to copy/paste code to begin
>> with. How many 'bugs' that we know of are copy/paste bugs?
>
> We will have to disagree. Consistency and simplicity are fine
> arguments in my book, not "pretty poor" ones.
Consitency is absolutly important! Zero argument there. My argument is,
the consistency should be within the kernel tree, not 'but the rest of
the world is using style X/Y/Z, lets be consistent with that. In an
utopia, maybe, but the real world doesn't work that way, sadly. So in an
attempt to standardize (rustfmt) they just "invented" a new standard.
Which btw is common, we see this happening every so often, right?
>
> I don't see the relevance of the copy/paste code discussion here. But
> assuming the analogy makes sense, I don't agree that reusing code is
> "generally bad" either.
Copy/pasting is known to cause bugs. There's actually research from NASA
on that. Code-reuse (libraries/functions) are not bad. But (worst kind
of example) copy paste from stack-overflow, or copy/pasting stuff
without actually looking at the content and forgetting to rename
something, causes bugs. Why is this relevant? The whole 'lets be
consistent with the rust codebase of the wrold' argument. E.g. if
everybody uses the same style (which is idealistic and great) then
copy/pasting becomes consistent. Where I say, try to be careful when
copy/pasting code.
>
> Anyway, given you mention "bugs", I think you are implying that the
> defaults are somehow "incorrect" (not accessible?). In that case, to
> improve things for all Rust developers out there, I would suggest
> opening an issue in https://github.com/rust-dev-tools/fmt-rfcs.
There have been, I've linked them in my first post; the devs basically
say 'you are right, we are sorry; but this discussion is over' which
reads as 'we love our style, we think its great, we're not changing it
for people with reading-disabilitles, figure it out' (yes, I paraphrase
it much harsher then what they state, but like with people in
wheelchairs that run into a small staircase (1 or 2 treads), and then
are being told, sorry, it is what it is, we can't fix this, it's been
like this for years, it is what it is)
>
>> Secondly, and more importantly so; you argue 'who cares about people
>> with disablements, atleast its equally hard to read everywhere' which is
>> a very poor argument :p
>
> No, and I want to be __very__ clear about this: at no point I have
> argued "who cares about people with disabilities" or anything like it.
> It is insulting that you even suggest it.
I appose for feeling insulted, that was surely not my intend! What I am
stating however is, that by stating 'but rustfmt is great, their
standard is consistent and simple and amazing', the message implies
(nofi!) that reading disabilities do not matter, because this new
standard is so great.
>
> Likewise, you are the one claiming it is "hard to read", not me.
Yes, as I do suffer from a reading disability, so I know how hard this
is :) but fear not, I'm not alone, just vocal.
>
> And then after constructing those straw men, you call them "a very
> poor argument"...
Obviously I do not intend to cunstruct straw-men, as to me, this is all
very real and painful :)
>
>> Finally, it must of course be mentioned, that rust is really trying to
>> do an XKCD here, https://xkcd.com/927/ though I'm sure we'll get it
>> right this time around ;)
>
> How does that even apply here? There is no "standard" for formatting
> across languages, if that is what you are saying.
I'm not, I'm saying 'every language has its own standard(s), lets make
one that is better then the others'. So instead of rust for example
following the linux kernel standard (or the Go coding standard, or
X/Y/Z), they came up with their own. Not bad, but as mentioned earlier,
requires careful thinking. But it is of course their choice!
>
> Actually, what is happening here is that there is an "official" tool,
> called rustfmt, that most Rust code out there uses.
>
> By not using it, it is you the one that may be creating a XKCD
> situation, if anything.
No, do use it! rustfmt is pretty amazing. And rustfmt knows there's not
a single answer on coding style, so the rustfmt tool, is super
configurable trying to match any code-base needs, without forcing
anybodies style. It is actually like you say, below, the defauflts that
come from the rust group itself.
>
> And to be clear, we don't necessarily follow "Rust's biased view". For
> instance, there is also an "official" build tool, called Cargo, that
> most Rust code out there uses; yet we are not using it for the kernel.
I'd prefer if we keep it to style and readability (rustfmt) :p as cargo
is more a technical direction, and not relevant; but point noted.
>
> We are actually doing things how we think are best for the kernel. Not
> because "Rust" (whatever or whoever that is) is "trying to do an
> XKCD". Not because we are "following Rust's opinionated view of the
> world" or "Rust's biased view".
But if that is the case, why not try to follow the kernels existing
code-style as close as possible with the rust-fmt configuration? I know
code-style has been discussed a few times over the decades; but not many
changes have been done, surely, if there's some codestyle changes that
are best for the kernel, they would have been 'advised'? '4 space
indents are better then 8-size tabs, on new code, try to use them' for
example :p
>
>> But you are not, only those that follow rust's biased view. Everybody
>> else that has a different opinion (like die-hard C programmers) that
>> care enough (I'm sure there's plenty) would setup their rustfmt config
>> file to resemble their C code; and thus the entire premisis is broken.
>> Though; yes, in a perfect world it could have worked like this, but xkcd
>> again :)
>
> No. I said we are being consistent with the majority of the Rust code
> out there, not with "everybody".
But why? Why should we not be consistent with the kernels' code-base
(while yes, that is not rust, but C, but we can follow the same style?)
>
> If, instead, we try to be consistent with the kernel C style, then you
> are likely not being consistent with the majority of the Rust code out
> there. And you would have to decide exactly how to map the C style to
> Rust constructs and which particular kernel style.
But is this a bad thing? Being consistent within the kernel repo? Who
cares what the rest of the ruts code does? I know it matters for
user-space; but I know that my user-space rust code (be it linux, or
micro) actually follows the kernel style, not the rust style :p becaues
of my disability, the rust format is not easy to read/parse in my head
due to small inconsistencies.
>
> Again: I personally don't mind what the particular style is. As a
> project, what we value the most is having a single style across it and
> not having to think about formatting. Nevertheless, I think there is
> also value in being consistent with the majority of the Rust code out
> there.
So I fully agree with the first part; but not with the last part :p As
the rust code style, is poor on readability for people with
reading-disabilities.
>
>> This though is a fair argument I understand, it would be weird in having
>> 2 styles in user-space and kernel-space code; though I see this
>> happening today as well; where developers follow kernel style for kernel
>> code (obviously) but use their preferred 2 or 3 space style on their
>> userland code. Trying to 'force' this, usually however never gets the
>> intended result ...
>
> If we follow the usual Rust style in the kernel, I would say it is
> more likely that both styles match.
That is of course the downside, that if user-space is writing their own
code in their own style, it will either be rust-style, or something
completly different, we have no control of this anyway; but having it
'match' is 'nice' from a consistency pov;
Sadly, I've seen so much vendor code (yeah, I know) which doesn't even
have consistency in their own files ...
> Cheers,
> Miguel
Hey Nicolas,
On 20-07-2022 22:21, Nicolas Pitre wrote:
> On Wed, 20 Jul 2022, Miguel Ojeda wrote:
>
>> On Mon, Jul 18, 2022 at 8:56 AM Olliver Schinagl
>> <[email protected]> wrote:
>>
>>> Secondly, and more importantly so; you argue 'who cares about people
>>> with disablements, atleast its equally hard to read everywhere' which is
>>> a very poor argument :p
>>
>> No, and I want to be __very__ clear about this: at no point I have
>> argued "who cares about people with disabilities" or anything like it.
>> It is insulting that you even suggest it.
>
> What "people with disablements" have to do with this anyway?
> I don't get it.
If you are talking to me; simple, reading disabilities (dyslexia being
the most common one) are real :) and code-style heavily impacts those.
For example, I have a really really hard time reading 2 space indent,
especially in larger code bases. Also, CamelCase is very very hard for
me to read also, as is statements without spaces `if((x<y&0xf)||z>a)`
for example.
So codestyle affects those with reading disabilities, but not well known
to the people without these, which is why I was raising awareness.
Hope this helps to explain things?
>
>
> Nicolas
On Wed, 27 Jul 2022, Olliver Schinagl wrote:
> Hey Nicolas,
>
> On 20-07-2022 22:21, Nicolas Pitre wrote:
> > On Wed, 20 Jul 2022, Miguel Ojeda wrote:
> >
> >> On Mon, Jul 18, 2022 at 8:56 AM Olliver Schinagl
> >> <[email protected]> wrote:
> >>
> >>> Secondly, and more importantly so; you argue 'who cares about people
> >>> with disablements, atleast its equally hard to read everywhere' which is
> >>> a very poor argument :p
> >>
> >> No, and I want to be __very__ clear about this: at no point I have
> >> argued "who cares about people with disabilities" or anything like it.
> >> It is insulting that you even suggest it.
> >
> > What "people with disablements" have to do with this anyway?
> > I don't get it.
> If you are talking to me; simple, reading disabilities (dyslexia being the
> most common one) are real :) and code-style heavily impacts those.
>
> For example, I have a really really hard time reading 2 space indent,
> especially in larger code bases. Also, CamelCase is very very hard for me to
> read also, as is statements without spaces `if((x<y&0xf)||z>a)` for example.
OK, that's a good point.
I asked because such arguments are often brought up in the context of
sight impairment by people who are not affected, and in this regard I'm
well positioned to call those arguments unfounded.
> So codestyle affects those with reading disabilities, but not well known to
> the people without these, which is why I was raising awareness.
Nicolas
Hi Olliver,
On Wed, 27 Jul 2022 10:05:31 +0200
Olliver Schinagl <[email protected]> wrote:
> Consitency is absolutly important! Zero argument there. My argument
> is, the consistency should be within the kernel tree, not 'but the
> rest of the world is using style X/Y/Z, lets be consistent with that.
> In an utopia, maybe, but the real world doesn't work that way, sadly.
> So in an attempt to standardize (rustfmt) they just "invented" a new
> standard. Which btw is common, we see this happening every so often,
> right?
Difference languages have different characteristics and I don't think
it's necessarily good (and often isn't) to blindly apply coding style
of one language onto another. So I don't see rustfmt as "inventing yet
another standard" really, because there aren't many conflicting coding
style standards in Rust world; almost everyone just settled on using
rustfmt, mostly using the default, maybe with a few small
project-specific configuration tweaks.
A small example for C and Rust differences:
Rust requires braces around branches of if expression, and C doesn't.
So in kernel coding style you often have:
if (condition) do_something();
Or
if (condition)
do_something();
But in Rust it will be:
if condition {
do_something();
}
That's just an example of one control flow constructions. There are
differences between Rust match and C switch, etc. Rust's official
coding style takes properties of Rust into consideration, so in many
regards it's a more suitable coding style for Rust code in kernel, then
applying kernel's C coding standard directly on kernel's Rust code.
Your earlier email in the thread also mentions about indentation, and I
have a few things to point out as well.
First, Rust code typically requires more levels of indentation than C
code. For example, many functions might be methods and they are inside
an impl block, which creates one extra level of indentation.
Statements inside match arms' block are two levels more indented than
the match statement itself, as opposed to C's switch (as kernel coding
style doesn't indent the case labels). As a result, 8 spaces for 1 level
can be a bit excessive for Rust code, and thus the 4 space indentation
used in rustfmt default.
Secondly, I don't think the argument about tabs being customisable
holds; in kernel coding style tabs are strictly 8 characters. For line
continuation it's not uncommon to use a series of tabs followed by a
few spaces, e.g.
int function_name(int first_argument,
< tab >< tab >..int second_argument)
changing tab into 4 spaces will break the layout. (and I'll not go into
well-known reasons about non-4-space-tab messing up code in terminal
etc).
> Copy/pasting is known to cause bugs. There's actually research from
> NASA on that. Code-reuse (libraries/functions) are not bad. But
> (worst kind of example) copy paste from stack-overflow, or
> copy/pasting stuff without actually looking at the content and
> forgetting to rename something, causes bugs. Why is this relevant?
> The whole 'lets be consistent with the rust codebase of the wrold'
> argument. E.g. if everybody uses the same style (which is idealistic
> and great) then copy/pasting becomes consistent. Where I say, try to
> be careful when copy/pasting code.
When we vendor in code as a whole (e.g. like we currently do for
alloc crate), it is proper code reuse. With different coding style the
vendored code either diverges from upstream (which makes upstreaming
much more difficult) or diverge from rest of kernel's Rust code base.
> But if that is the case, why not try to follow the kernels existing
> code-style as close as possible with the rust-fmt configuration? I
> know code-style has been discussed a few times over the decades; but
> not many changes have been done, surely, if there's some codestyle
> changes that are best for the kernel, they would have been 'advised'?
> '4 space indents are better then 8-size tabs, on new code, try to use
> them' for example :p
You do realize that you are creating a new coding style by doing this,
right? It feels like creating problems rather than solving problems.
My personal feeling is that it's easier for me to adapt to different
coding style when switching between languages, but it's rather awkward
for me when trying to use different coding styles with the same
language. I find myself no problem switching between 2 spaces when
coding JavaScript to 4 spaces when coding Rust to 8 spaces(tab) when
coding C, but it's rather painful to switch between C projects with
different coding styles. I certainly don't want to switch between Rust
projects with vastly different coding styles.
> But why? Why should we not be consistent with the kernels' code-base
> (while yes, that is not rust, but C, but we can follow the same
> style?)
Difference languages have different characteristics, and one size
doesn't fit them all :)
> Sadly, I've seen so much vendor code (yeah, I know) which doesn't
> even have consistency in their own files ...
That's very true. So when all other Rust code currently follow
(roughly) the same coding style and this situation doesn't currently
exist, let's not make it worse...
Best,
Gary
On Thu, 28 Jul 2022 14:09:57 +0200
Greg Kroah-Hartman <[email protected]> wrote:
> On Thu, Jul 28, 2022 at 11:21:14AM +0100, Gary Guo wrote:
> > Rust requires braces around branches of if expression, and C
> > doesn't. So in kernel coding style you often have:
> >
> > if (condition) do_something();
>
> That is not a valid kernel coding style, and our tools should catch
> this and prevent it from being added to the kernel tree.
Thanks, I stand corrected. I do see those patterns occasionally so
presumably that gives me wrong impression.
rg '\bif \(.*;' $(find -name *.c) | wc -l
gives me 3362 results though. Most of them are from fs and driver
directory so presumably they are vendored or legacy code.
Best,
Gary
On Thu, Jul 28, 2022 at 11:21:14AM +0100, Gary Guo wrote:
> Rust requires braces around branches of if expression, and C doesn't.
> So in kernel coding style you often have:
>
> if (condition) do_something();
That is not a valid kernel coding style, and our tools should catch this
and prevent it from being added to the kernel tree.
thanks,
greg k-h
Hey Greg,
On 28-07-2022 14:09, Greg Kroah-Hartman wrote:
> On Thu, Jul 28, 2022 at 11:21:14AM +0100, Gary Guo wrote:
>> Rust requires braces around branches of if expression, and C doesn't.
>> So in kernel coding style you often have:
>>
>> if (condition) do_something();
>
> That is not a valid kernel coding style, and our tools should catch this
> and prevent it from being added to the kernel tree.
Are you sure? I'm not sure if this isn't true today, but I've certainly
seen old code where this definitely was done. Was all of this cleaned up
in the last 2+ years?
Olliver
>
> thanks,
>
> greg k-h
Hey Gary,
On 28-07-2022 12:21, Gary Guo wrote:
> Hi Olliver,
>
> On Wed, 27 Jul 2022 10:05:31 +0200
> Olliver Schinagl <[email protected]> wrote:
>
>> Consitency is absolutly important! Zero argument there. My argument
>> is, the consistency should be within the kernel tree, not 'but the
>> rest of the world is using style X/Y/Z, lets be consistent with that.
>> In an utopia, maybe, but the real world doesn't work that way, sadly.
>> So in an attempt to standardize (rustfmt) they just "invented" a new
>> standard. Which btw is common, we see this happening every so often,
>> right?
>
> Difference languages have different characteristics and I don't think
> it's necessarily good (and often isn't) to blindly apply coding style
> of one language onto another. So I don't see rustfmt as "inventing yet
> another standard" really, because there aren't many conflicting coding
> style standards in Rust world; almost everyone just settled on using
> rustfmt, mostly using the default, maybe with a few small
> project-specific configuration tweaks.
I was mostly arguing about a) lets look at this and b) having said
configuration tweaks, rather then blindly (pun not really intended)
going with rust's defaults :)
>
> A small example for C and Rust differences:
>
> Rust requires braces around branches of if expression, and C doesn't.
> So in kernel coding style you often have:
>
> if (condition) do_something();
>
> Or
>
> if (condition)
> do_something();
>
> But in Rust it will be:
>
> if condition {
> do_something();
> }
So kernel style kind of says 'no braces around single statements'; but
if your rust compiler doesn't allow this; well then there's nothing to
do. You could even argue to update the kernel C style on this to make it
consistent again. BUT, this inconsistency makes it cognative 'hard'. If
this if a C or a rust function? for example during a review. During
authoring, when writing both C and rust code (due to nececity, not
constant context switching) you cognitivly constantly have to go
back/foward. While I'm sure there's people here that can do this all day
without problem, some of of find this harder then needs to be. Hence the
request to _try_ to keep consistency within the kernel tree.
>
> That's just an example of one control flow constructions. There are
> differences between Rust match and C switch, etc. Rust's official
> coding style takes properties of Rust into consideration, so in many
> regards it's a more suitable coding style for Rust code in kernel, then
> applying kernel's C coding standard directly on kernel's Rust code.
>
> Your earlier email in the thread also mentions about indentation, and I
> have a few things to point out as well.
>
> First, Rust code typically requires more levels of indentation than C
> code. For example, many functions might be methods and they are inside
> an impl block, which creates one extra level of indentation.
> Statements inside match arms' block are two levels more indented than
> the match statement itself, as opposed to C's switch (as kernel coding
> style doesn't indent the case labels). As a result, 8 spaces for 1 level
> can be a bit excessive for Rust code, and thus the 4 space indentation
> used in rustfmt default.
>
> Secondly, I don't think the argument about tabs being customisable
> holds; in kernel coding style tabs are strictly 8 characters. For line
Sure, this rule implies that for alignment, tabs should be set to 8 so
things align nicely. However, nobody forces people to set their editor
to 8 character width. Not doing so, doesn't break anything. At worst,
you may commit something that is poorly aligned (but we _should_ be
using tabs to indent, spaces to align anyway :p, tab == indent has meaning).
With non-tab indentation, this is no longer really possible, or at
least, editors haven't solved that problem yet, as it tends to still
break (due to the mixing of indentation and alignment using a single
character). Maybe once we have AI and ML in our editors though :p
> continuation it's not uncommon to use a series of tabs followed by a
> few spaces, e.g.
>
> int function_name(int first_argument,
> < tab >< tab >..int second_argument)
>
> changing tab into 4 spaces will break the layout. (and I'll not go into
> well-known reasons about non-4-space-tab messing up code in terminal
> etc).
>
>> Copy/pasting is known to cause bugs. There's actually research from
>> NASA on that. Code-reuse (libraries/functions) are not bad. But
>> (worst kind of example) copy paste from stack-overflow, or
>> copy/pasting stuff without actually looking at the content and
>> forgetting to rename something, causes bugs. Why is this relevant?
>> The whole 'lets be consistent with the rust codebase of the wrold'
>> argument. E.g. if everybody uses the same style (which is idealistic
>> and great) then copy/pasting becomes consistent. Where I say, try to
>> be careful when copy/pasting code.
>
> When we vendor in code as a whole (e.g. like we currently do for
> alloc crate), it is proper code reuse. With different coding style the
> vendored code either diverges from upstream (which makes upstreaming
> much more difficult) or diverge from rest of kernel's Rust code base.
Very fair point of course. Though really, we should fix the upstream
rust preferred format, but there it was already stated, that 'too bad,
sorry' which from a developer point of view is fine, your project, your
choice. From a disabilities point of view, sucks of course.
>
>> But if that is the case, why not try to follow the kernels existing
>> code-style as close as possible with the rust-fmt configuration? I
>> know code-style has been discussed a few times over the decades; but
>> not many changes have been done, surely, if there's some codestyle
>> changes that are best for the kernel, they would have been 'advised'?
>> '4 space indents are better then 8-size tabs, on new code, try to use
>> them' for example :p
>
> You do realize that you are creating a new coding style by doing this,
> right? It feels like creating problems rather than solving problems.
>
> My personal feeling is that it's easier for me to adapt to different
> coding style when switching between languages, but it's rather awkward
> for me when trying to use different coding styles with the same
> language. I find myself no problem switching between 2 spaces when
> coding JavaScript to 4 spaces when coding Rust to 8 spaces(tab) when
> coding C, but it's rather painful to switch between C projects with
> different coding styles. I certainly don't want to switch between Rust
> projects with vastly different coding styles.
And I'm happy for you that you can easily take in 2 and 4 spaces. For
me, it is extremly hard to read. So it's not a 'personal preference'
thing. But I suggest to read the earlier posted links, where others at
length explain it as well, how it is like to feel excluded becaues its
just hard to read.
>
>> But why? Why should we not be consistent with the kernels' code-base
>> (while yes, that is not rust, but C, but we can follow the same
>> style?)
>
> Difference languages have different characteristics, and one size
> doesn't fit them all :)
I'm not even arguing this at all :)
I think the biggest issues i'm speaking of really are the braces and the
spaces really, where the braces can be argued for/against, it's
cognitive harder, but can be dealth with (and we can expect
inconsitencies; but the sapces vs tabs thing, personal configuration vs
forced with is the point I was trying to raise.
As said before, 'every building is different, some offer wheelchair
ramps, others do' kind of point, not like 'this building is red, and
that one is blue, and not every color fits all :p
>
>> Sadly, I've seen so much vendor code (yeah, I know) which doesn't
>> even have consistency in their own files ...
>
> That's very true. So when all other Rust code currently follow
> (roughly) the same coding style and this situation doesn't currently
> exist, let's not make it worse...
>
> Best,
> Gary
On Thu, Jul 28, 2022 at 10:45:08PM +0200, Olliver Schinagl wrote:
> Hey Greg,
>
> On 28-07-2022 14:09, Greg Kroah-Hartman wrote:
> > On Thu, Jul 28, 2022 at 11:21:14AM +0100, Gary Guo wrote:
> > > Rust requires braces around branches of if expression, and C doesn't.
> > > So in kernel coding style you often have:
> > >
> > > if (condition) do_something();
> >
> > That is not a valid kernel coding style, and our tools should catch this
> > and prevent it from being added to the kernel tree.
> Are you sure? I'm not sure if this isn't true today, but I've certainly seen
> old code where this definitely was done. Was all of this cleaned up in the
> last 2+ years?
Given that I wrote about this back in 2002, and it was true then:
https://www.linuxjournal.com/article/5780
and:
https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.2.8887&rep=rep1&type=pdf
that is not anything new at all.
Yes, old code still survives that might not be correct, and some
subsystems might have added code over time without the proper style, but
our tools check that the above is not correct, you can check it
yourself:
$ cat foo.c
// SPDX-License-Identifier: GPL-2.0
int foo(int baz)
{
if (baz == 1) do_something();
}
$ ./scripts/checkpatch.pl --file --terse foo.c
foo.c:4: ERROR: trailing statements should be on next line
total: 1 errors, 0 warnings, 6 lines checked
thanks,
greg k-h
As this thread kind of went silent and as the 'big merge' for this
feature is getting closer, here a final plee, inspired by this slashdot
post [0].
The post in itself speaks of a new team forming on working on the Rust
styleguide, which in itself is still evolving. This makes sense, rust is
new, it's not very commonly in use and as with all good things, they evolve.
One comment in that slashdot post [1] I want to bring forward and quote
a piece of:
"i created a new repository, and thought i was being hip and modern, so
i started to evangelize spaces for the 'consistency across environments'
i get approached by not one, but TWO coworkers who unfortunately are
highly visually impaired and each has a different visual impairment
at that moment, i instantaneously conceded — there's just no
counter-argument that even comes close to outweighing the accessibility
needs of valued coworkers"
Visual impairness is a thing, it does not make someone smarter or
dumber. Helping those with visual impairments should be concidered, and
not shunted off by saying 'but the rust guys came up with the perfect
style, so we should use it'.
Find attached, a diff to the .rustfmt.toml, that should keep things more
consistent with the current kernel style.
I'll leave it now to Linus and Greg to concsider this, and will keep my
peace (though I hope they actually read it :p).
Olliver
[0]:
https://developers.slashdot.org/story/22/10/07/2351222/rust-programming-language-announces-new-team-to-evolve-official-coding-style
[1]: https://developers.slashdot.org/comments.pl?sid=22182701&cid=62949323
On 28-07-2022 22:43, Olliver Schinagl wrote:
> Hey Gary,
>
> On 28-07-2022 12:21, Gary Guo wrote:
>> Hi Olliver,
>>
>> On Wed, 27 Jul 2022 10:05:31 +0200
>> Olliver Schinagl <[email protected]> wrote:
>>
>>> Consitency is absolutly important! Zero argument there. My argument
>>> is, the consistency should be within the kernel tree, not 'but the
>>> rest of the world is using style X/Y/Z, lets be consistent with that.
>>> In an utopia, maybe, but the real world doesn't work that way, sadly.
>>> So in an attempt to standardize (rustfmt) they just "invented" a new
>>> standard. Which btw is common, we see this happening every so often,
>>> right?
>>
>> Difference languages have different characteristics and I don't think
>> it's necessarily good (and often isn't) to blindly apply coding style
>> of one language onto another. So I don't see rustfmt as "inventing yet
>> another standard" really, because there aren't many conflicting coding
>> style standards in Rust world; almost everyone just settled on using
>> rustfmt, mostly using the default, maybe with a few small
>> project-specific configuration tweaks.
> I was mostly arguing about a) lets look at this and b) having said
> configuration tweaks, rather then blindly (pun not really intended)
> going with rust's defaults :)
>
>>
>> A small example for C and Rust differences:
>>
>> Rust requires braces around branches of if expression, and C doesn't.
>> So in kernel coding style you often have:
>>
>> if (condition) do_something();
>>
>> Or
>>
>> if (condition)
>> do_something();
>>
>> But in Rust it will be:
>>
>> if condition {
>> do_something();
>> }
> So kernel style kind of says 'no braces around single statements'; but
> if your rust compiler doesn't allow this; well then there's nothing to
> do. You could even argue to update the kernel C style on this to make it
> consistent again. BUT, this inconsistency makes it cognative 'hard'. If
> this if a C or a rust function? for example during a review. During
> authoring, when writing both C and rust code (due to nececity, not
> constant context switching) you cognitivly constantly have to go
> back/foward. While I'm sure there's people here that can do this all day
> without problem, some of of find this harder then needs to be. Hence the
> request to _try_ to keep consistency within the kernel tree.
>
>>
>> That's just an example of one control flow constructions. There are
>> differences between Rust match and C switch, etc. Rust's official
>> coding style takes properties of Rust into consideration, so in many
>> regards it's a more suitable coding style for Rust code in kernel, then
>> applying kernel's C coding standard directly on kernel's Rust code.
>>
>> Your earlier email in the thread also mentions about indentation, and I
>> have a few things to point out as well.
>>
>> First, Rust code typically requires more levels of indentation than C
>> code. For example, many functions might be methods and they are inside
>> an impl block, which creates one extra level of indentation.
>> Statements inside match arms' block are two levels more indented than
>> the match statement itself, as opposed to C's switch (as kernel coding
>> style doesn't indent the case labels). As a result, 8 spaces for 1 level
>> can be a bit excessive for Rust code, and thus the 4 space indentation
>> used in rustfmt default.
>>
>> Secondly, I don't think the argument about tabs being customisable
>> holds; in kernel coding style tabs are strictly 8 characters. For line
> Sure, this rule implies that for alignment, tabs should be set to 8 so
> things align nicely. However, nobody forces people to set their editor
> to 8 character width. Not doing so, doesn't break anything. At worst,
> you may commit something that is poorly aligned (but we _should_ be
> using tabs to indent, spaces to align anyway :p, tab == indent has meaning).
>
> With non-tab indentation, this is no longer really possible, or at
> least, editors haven't solved that problem yet, as it tends to still
> break (due to the mixing of indentation and alignment using a single
> character). Maybe once we have AI and ML in our editors though :p
>
>> continuation it's not uncommon to use a series of tabs followed by a
>> few spaces, e.g.
>>
>> int function_name(int first_argument,
>> < tab >< tab >..int second_argument)
>>
>> changing tab into 4 spaces will break the layout. (and I'll not go into
>> well-known reasons about non-4-space-tab messing up code in terminal
>> etc).
>>
>>> Copy/pasting is known to cause bugs. There's actually research from
>>> NASA on that. Code-reuse (libraries/functions) are not bad. But
>>> (worst kind of example) copy paste from stack-overflow, or
>>> copy/pasting stuff without actually looking at the content and
>>> forgetting to rename something, causes bugs. Why is this relevant?
>>> The whole 'lets be consistent with the rust codebase of the wrold'
>>> argument. E.g. if everybody uses the same style (which is idealistic
>>> and great) then copy/pasting becomes consistent. Where I say, try to
>>> be careful when copy/pasting code.
>>
>> When we vendor in code as a whole (e.g. like we currently do for
>> alloc crate), it is proper code reuse. With different coding style the
>> vendored code either diverges from upstream (which makes upstreaming
>> much more difficult) or diverge from rest of kernel's Rust code base.
> Very fair point of course. Though really, we should fix the upstream
> rust preferred format, but there it was already stated, that 'too bad,
> sorry' which from a developer point of view is fine, your project, your
> choice. From a disabilities point of view, sucks of course.
>
>>
>>> But if that is the case, why not try to follow the kernels existing
>>> code-style as close as possible with the rust-fmt configuration? I
>>> know code-style has been discussed a few times over the decades; but
>>> not many changes have been done, surely, if there's some codestyle
>>> changes that are best for the kernel, they would have been 'advised'?
>>> '4 space indents are better then 8-size tabs, on new code, try to use
>>> them' for example :p
>>
>> You do realize that you are creating a new coding style by doing this,
>> right? It feels like creating problems rather than solving problems.
>>
>> My personal feeling is that it's easier for me to adapt to different
>> coding style when switching between languages, but it's rather awkward
>> for me when trying to use different coding styles with the same
>> language. I find myself no problem switching between 2 spaces when
>> coding JavaScript to 4 spaces when coding Rust to 8 spaces(tab) when
>> coding C, but it's rather painful to switch between C projects with
>> different coding styles. I certainly don't want to switch between Rust
>> projects with vastly different coding styles.
> And I'm happy for you that you can easily take in 2 and 4 spaces. For
> me, it is extremly hard to read. So it's not a 'personal preference'
> thing. But I suggest to read the earlier posted links, where others at
> length explain it as well, how it is like to feel excluded becaues its
> just hard to read.
>
>>
>>> But why? Why should we not be consistent with the kernels' code-base
>>> (while yes, that is not rust, but C, but we can follow the same
>>> style?)
>>
>> Difference languages have different characteristics, and one size
>> doesn't fit them all :)
> I'm not even arguing this at all :)
>
> I think the biggest issues i'm speaking of really are the braces and the
> spaces really, where the braces can be argued for/against, it's
> cognitive harder, but can be dealth with (and we can expect
> inconsitencies; but the sapces vs tabs thing, personal configuration vs
> forced with is the point I was trying to raise.
>
> As said before, 'every building is different, some offer wheelchair
> ramps, others do' kind of point, not like 'this building is red, and
> that one is blue, and not every color fits all :p
>
>>
>>> Sadly, I've seen so much vendor code (yeah, I know) which doesn't
>>> even have consistency in their own files ...
>>
>> That's very true. So when all other Rust code currently follow
>> (roughly) the same coding style and this situation doesn't currently
>> exist, let's not make it worse...
>>
>> Best,
>> Gary
>
On 10/15/22 21:16, Olliver Schinagl wrote:
> As this thread kind of went silent and as the 'big merge' for this feature is getting closer, here a final plee, inspired by this slashdot post [0].
>
> The post in itself speaks of a new team forming on working on the Rust styleguide, which in itself is still evolving. This makes sense, rust is new, it's not very commonly in use and as with all good things, they evolve.
>
> One comment in that slashdot post [1] I want to bring forward and quote a piece of:
> "i created a new repository, and thought i was being hip and modern, so
> i started to evangelize spaces for the 'consistency across environments'
>
> i get approached by not one, but TWO coworkers who unfortunately are highly visually impaired and each has a different visual impairment
>
> at that moment, i instantaneously conceded — there's just no counter-argument that even comes close to outweighing the accessibility needs of valued coworkers"
>
> Visual impairness is a thing, it does not make someone smarter or dumber. Helping those with visual impairments should be concidered, and not shunted off by saying 'but the rust guys came up with the perfect style, so we should use it'.
>
> Find attached, a diff to the .rustfmt.toml, that should keep things more consistent with the current kernel style.
>
> I'll leave it now to Linus and Greg to concsider this, and will keep my peace (though I hope they actually read it :p).
>
I have to say two advices:
First, don't top-post. I don't know what context you're replying to
(in fact I have to cut the reply context below your message).
Second, please post the patch inline, not attached. git format-patch +
git send-email should suffice.
Thanks.
--
An old man doll... just what I always wanted! - Clara
On 10/16/22 08:44, Bagas Sanjaya wrote:
>
> I have to say two advices:
>
> First, don't top-post. I don't know what context you're replying to
> (in fact I have to cut the reply context below your message).
>
> Second, please post the patch inline, not attached. git format-patch +
> git send-email should suffice.
>
Oh, I forget to mention this. Is it RFC? If so, you need to specify
--subject-prefix="RFC PATCH" to git format-patch.
Thanks.
--
An old man doll... just what I always wanted! - Clara
On Sat, Oct 15, 2022 at 04:16:14PM +0200, Olliver Schinagl wrote:
> +indent_style = "Visual"
Without commenting on the rest of this: visual indent style produces a
*lot* of diff noise, and I'd strongly recommend against it. Because it
lines things up, a change to one line can change many adjacent lines,
and make it hard to see what actually changed.