Skip to content

Commit fa5cb0b

Browse files
committed
add Rust for Linux material
1 parent 6136169 commit fa5cb0b

29 files changed

+995
-0
lines changed

Diff for: src/SUMMARY.md

+32
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,38 @@
407407
- [Broadcast Chat Application](concurrency/async-exercises/chat-app.md)
408408
- [Solutions](concurrency/async-exercises/solutions.md)
409409

410+
# Rust for Linux
411+
412+
---
413+
414+
- [Welcome](rust-for-linux/welcome.md)
415+
- [Interoperation Requirements](rust-for-linux/basic-requirements.md)
416+
- [Building Kernel Modules](rust-for-linux/modules.md)
417+
- [Type Mapping](rust-for-linux/types.md)
418+
- [Bindings and Safe Interfaces](rust-for-linux/bindings-interfaces.md)
419+
- [Avoiding Bloat](rust-for-linux/bloat.md)
420+
- [Hands-on With Kernel Rust](rust-for-linux/hands-on.md)
421+
- [Rust for Linux](rust-for-linux/rust-for-linux.md)
422+
- [`rust-analyzer` Setup](rust-for-linux/rust-analyzer.md)
423+
- [Macros](rust-for-linux/macros.md)
424+
- [A Rust Kernel Module](rust-for-linux/kernel-module.md)
425+
- [The `module!` Macro](rust-for-linux/modules/module-macro.md)
426+
- [Module Setup and Teardown](rust-for-linux/modules/setup-and-teardown.md)
427+
- [Module Parameters](rust-for-linux/modules/parameters.md)
428+
- [Using Abstractions](rust-for-linux/using-abstractions.md)
429+
- [Complications and Conflicts](rust-for-linux/complications.md)
430+
- [`Pin` and Self-Reference](rust-for-linux/complications/pin.md)
431+
- [The Kernel Rust Safety Model](rust-for-linux/complications/safety.md)
432+
- [Atomic/Task Contexts and Sleep](rust-for-linux/complications/sleeping.md)
433+
- [Memory Models](rust-for-linux/complications/memory-models.md)
434+
- [Separate Compilation and Linking](rust-for-linux/complications/separate-compilation.md)
435+
- [Fallible Allocation](rust-for-linux/complications/fallible-allocation.md)
436+
- [Code Size](rust-for-linux/complications/code-size.md)
437+
- [Documentation](rust-for-linux/complications/kernel-doc.md)
438+
- [Security Mitigations](rust-for-linux/complications/mitigations.md)
439+
- [Async](rust-for-linux/complications/async.md)
440+
- [Next Steps](rust-for-linux/next-steps.md)
441+
410442
# Final Words
411443

412444
---

Diff for: src/rust-for-linux/basic-requirements.md

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# Interoperation Requirements
2+
3+
To use Rust code in Linux, we can start by comparing this situation with C/Rust
4+
interop in userspace.
5+
6+
## Building
7+
8+
In userspace, the most common setup is to use Cargo to compile our Rust and
9+
later integrate into a C build system if needed.
10+
Meanwhile, the Linux Kernel compiles its C code with its custom Kbuild build
11+
system.
12+
In Rust for Linux, the kernel build system invokes the Rust compiler directly,
13+
without Cargo.
14+
15+
## No `libstd`
16+
17+
Unlike typical usage of Rust in userspace, which makes use of the rust standard
18+
library through the `std` crate, Rust in the kernel does not run atop an
19+
operating system, so kernel Rust will have to eschew the standard library.
20+
21+
## Module Support
22+
23+
Much code in the kernel is compiled into kernel modules rather than as part of
24+
the core kernel.
25+
To write kernel modules in Rust we'll need to be able to match the ABI of kernel
26+
modules.
27+
28+
## Safe Wrappers
29+
30+
To reap the benefits of Rust, we want to be able to write as much code as
31+
possible in safe Rust.
32+
This means that we want safe wrappers for as much kernel functionality as
33+
possible.
34+
35+
## Mapping Types
36+
37+
When writing these wrappers, we'll need to refer to the data types of values
38+
passed to and from existing kernel functions in C.
39+
Unlike userspace C, the kernel uses its own set of primitive types rather than
40+
those provided by the C standard.
41+
We'll have to map back and forth between those kernel types and compatible Rust
42+
ones when doing foreign calls.
43+
44+
## Keeping the Kernel Lean
45+
46+
Finally, even the core Rust library assumes a basic level of functionality that
47+
includes some costly operations (e.g. unicode processing) for which the kernel
48+
does not want to pay implementation costs.
49+
To use Rust in the kernel we'll need a way to disable this functionality.
50+
51+
# Outline
52+
53+
{{%segment outline}}

Diff for: src/rust-for-linux/bindgen-mapping.png

55.1 KB
Loading

Diff for: src/rust-for-linux/bindings-interfaces.md

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
---
2+
minutes: 18
3+
---
4+
5+
# Bindings and Safe Interfaces
6+
7+
`bindgen` is used to generate low-level, unsafe bindings for C interfaces.
8+
9+
But to reap the benefits of Rust, we want to use safe, foolproof interfaces to unsafe functionality.
10+
11+
Subsystems are expected to implement safe interfaces on top of the low-level generated bindings.
12+
These safe interfaces are exposed as top-level modules within the [`kernel` crate](https://rust.docs.kernel.org/kernel/).
13+
The top-level `bindings` module holds the unsafe `bindgen`-generated bindings,
14+
which are generated from the C headers included by `rust/bindings/bindings_helper.h`.
15+
16+
In Rust for Linux, unsafe `bindgen`-generated bindings should not be used outside the `kernel` crate.
17+
Drivers and other subsystems will make use of the safe abstractions from this crate.
18+
19+
Only a subset of Linux subsystems currently have such abstractions.
20+
21+
It's worth browsing the [list of modules](https://rust.docs.kernel.org/kernel/#modules)
22+
exposed by the `kernel` crate to see what exists currently.
23+
Many of these subsystems have only partial bindings based on the needs of consumers so far.
24+
25+
## Adding a Module
26+
27+
To add a module for some subsystem, first its header must be added to `bindings_helper.h`.
28+
It may be necessary to write some custom code to wrap macros or `inline` functions
29+
that are not automatically handled by `bindgen`; this code lives in the `rust/helpers/` directory.
30+
31+
Then we need to write a safe abstraction using these bindings and exposing them to the rest of kernel Rust.
32+
33+
Some commits from work-in-progress bindings and abstractions
34+
can provide an idea of what it looks like to expose new kernel functionality:
35+
36+
- GPIO Consumer: [fecb4bd73f06bb2cac8e16aca7ef0e2f1b6acb50](https://github.com/Fabo/linux/commit/fecb4bd73f06bb2cac8e16aca7ef0e2f1b6acb50)
37+
- Regmap: [ec0b740ac5ab299e4c86011a0002919e5bbe5c2d](https://github.com/Fabo/linux/commit/ec0b740ac5ab299e4c86011a0002919e5bbe5c2d)
38+
- I2C: [70ed30fcdf8ec62fa91485c3c0a161a9d0194668](https://github.com/Fabo/linux/commit/70ed30fcdf8ec62fa91485c3c0a161a9d0194668)
39+
40+
## Guidelines for Abstractions
41+
42+
Abstractions may not be perfectly safe, but should try to be as safe as possible.
43+
Unsafe functionality exposed should have its safety conditions documented
44+
so that users have guidance on how to use the functionality and justify such use.
45+
46+
Abstractions should also attempt to present relatively idiomatic Rust in their interfaces:
47+
- Follow Rust naming/capitalization conventions while remaining unsurprising to kernel developers.
48+
- Use RAII instead of manual resource management where possible.
49+
- Avoid raw pointers to bound kernel objects in favor of safer, more limited interfaces.
50+
51+
When exposing types from generated bindings, code should make use of the
52+
[`Opaque<T>`](https://rust.docs.kernel.org/kernel/types/struct.Opaque.html) type
53+
along with native Rust references and the
54+
[`ARef<T>`](https://rust.docs.kernel.org/kernel/types/struct.ARef.html) type for types that are inherently reference-counted.
55+
This type links types' built-in reference count operations to the `Clone` and `Drop` traits.
56+
57+
## Submitting the cyclic dependency
58+
59+
We already know that drivers should not use unsafe bindings directly.
60+
But subsystem maintainers may balk if they see patches submitted that add Rust abstractions without motivation or consumers.
61+
But drivers and subsystem abstractions may have to be submitted separately to different maintainers
62+
due to the distributed nature of Linux development.
63+
64+
So how should a developer submit a driver that requires bindings/abstractions for a subsystem not yet exposed to Rust?
65+
66+
There are two main approaches[^1]:
67+
68+
1. Submit the driver as an RFC before submitting the abstractions it relies upon while referencing the RFC as a potential consumer.
69+
2. Submit a stub driver and fill out non-stub functionality as subsystem abstractions land.
70+
71+
[^1]: <https://rust-for-linux.zulipchat.com/#narrow/channel/288089-General/topic/Upstreaming.20a.20driver.20with.20unsave.20C.20API.20calls.3F/near/471677707>

Diff for: src/rust-for-linux/bloat.md

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
---
2+
minutes: 5
3+
---
4+
5+
# Avoiding Bloat
6+
7+
Rust for Linux makes use of `libcore` to avoid reimplementing all functionality of the Rust standard library.
8+
But even `libcore` has some functionality built-in that is not portable to all targets the kernel
9+
would like to support or that is not necessary for the kernel while occupying valuable code space.
10+
11+
This includes[^1]:
12+
13+
- Support for math with 128-bit integers
14+
- String formatting for floating-point numbers
15+
- Unicode support for strings
16+
17+
Work is ongoing to make these features optional.
18+
In the meantime, the `libcore` used by Rust for Linux is larger and less portable than it could be.
19+
20+
[^1]: <https://github.com/Rust-for-Linux/linux/issues/514>

Diff for: src/rust-for-linux/complications.md

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
---
2+
minutes: 5
3+
---
4+
5+
# Complications and Conflicts
6+
7+
{{%segment outline}}
8+
9+
There are a number of subtleties and unresolved conflicts between the Rust paradigm and the kernel one.
10+
These must be resolved to ship Rust code in the kernel.
11+
12+
Some issues are deeper problems that require additional research and development
13+
before Rust for Linux is ready for the prime-time;
14+
others merely require some additional learning and attention
15+
on behalf of aspiring Rust for Linux developers.
16+
17+
##
18+
19+
Resolving these conflicts involves changes on both sides of the collaboration.
20+
On the Rust side, new features land first in the Nightly edition of the compiler
21+
before being stabilized.
22+
23+
To avoid waiting for stabilization, the kernel uses an
24+
[escape hatch](https://rustc-dev-guide.rust-lang.org/building/bootstrapping/what-bootstrapping-does.html#complications-of-bootstrapping)
25+
to access unstable features even in stable releases of the compiler.
26+
This assists in the goal of eventually deploying Rust for Linux in Linux
27+
distributions that ship only a stable version of the Rust toolchain.
28+
29+
Nonetheless, being able to build Rust for Linux using only stable Rust features
30+
is a significant goal;
31+
the issues blocking this are tracked specifically by both the Rust for Linux
32+
project[^1] and the Rust developers themselves[^2].
33+
34+
In the next slides we'll explore the most significant sources of friction between
35+
Rust and Linux kernel development to be aware of challenges we are likely to encounter
36+
when trying to implement kernel functionality in Rust.
37+
38+
[^1]: <https://github.com/Rust-for-Linux/linux/issues/2>
39+
[^2]: <https://github.com/rust-lang/rust-project-goals/issues/116>

Diff for: src/rust-for-linux/complications/async.md

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
---
2+
minutes: 8
3+
---
4+
5+
# Async
6+
7+
The kernel performs many operations concurrently and involves significant amounts of interaction
8+
between CPU cores and other devices.
9+
For this reason, it would be no surprise to see that async Rust would be a fundamental requirement
10+
for using Rust in the kernel.
11+
But the kernel is central arbitrer of most synchronization and is currently written in regular, synchronous C.
12+
13+
Rust code making use of `async` mostly exists to write composable code that will run atop event loops,
14+
but the Linux kernel is not really organized as an event loop:
15+
user tasks call directly into the kernel; control flow for interrupts is handled by hardware.
16+
17+
As such, `async` support is not critical for most kernel programming tasks.
18+
However, it is possible to view some components of the kernel as async executors,
19+
and some work has been done in this direction.
20+
Wedson Almeida Filho implemented both workqueue-based[^1] and single-threaded async executors as proofs of concept.
21+
22+
There is not a fundamental incompatibility between Rust-for-Linux and Rust `async`,
23+
which is a similar situation to the amenability of `async` to use in embedded Rust programming
24+
(e.g. the Embassy project).
25+
26+
Nonetheless, no killer application of `async` in Rust for Linux has made it a priority.
27+
28+
<details>
29+
30+
[^1]: <https://github.com/Rust-for-Linux/linux/tree/rust/rust/kernel/kasync>
31+
32+
An example of an async server using the kernel async executor may be found
33+
[here](https://github.com/Rust-for-Linux/linux/blob/rust/samples/rust/rust_echo_server.rs).
34+
35+
</details>

Diff for: src/rust-for-linux/complications/code-size.md

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
---
2+
minutes: 10
3+
---
4+
5+
# Code Size
6+
7+
One pitfall when writing Rust code can be the multiplicative increase in generated machine code when using generics.
8+
9+
For the Linux kernel, which must be suitable for space-limited embedded environments,
10+
keeping code size low is a significant concern.
11+
12+
Experiments with Rust in the kernel so far have shown that Rust code can be of similar code size to C,
13+
but may also be larger in some cases[^1].
14+
15+
## Assessing Bloat
16+
17+
Tools exist to help analyze different source code's contribution to the size of compiled code,
18+
such as [`cargo-bloat`](https://github.com/RazrFalcon/cargo-bloat).
19+
20+
## Shrinking Code Size
21+
22+
The reasons for code bloat vary and are not generally specific to Linux kernel usage of Rust.
23+
The most common causes for code bloat are excessive use of generics and forced inlining.
24+
In general, generics should be prefered over trait objects when writing abstractions
25+
that are expected to "compile out" or where generating separate code for different types is critical
26+
for performance (e.g. inner loops or arithmetic on values of a generic type).
27+
28+
In other situations, trait objects should be prefered to allow reusing definitions
29+
without machine-code duplication, which may closer mirror patterns that would be most natural in C.
30+
31+
When accepting generic parameters that get converted to a concrete type before use,
32+
follow the pattern of defining an inner monomorphic function that can be shared[^2]:
33+
34+
```rust
35+
pub fn read_to_string<P: AsRef<Path>>(path: P) -> io::Result<String> {
36+
fn inner(path: &Path) -> io::Result<String> {
37+
let mut file = File::open(path)?;
38+
let size = file.metadata().map(|m| m.len() as usize).ok();
39+
let mut string = String::with_capacity(size.unwrap_or(0));
40+
io::default_read_to_string(&mut file, &mut string, size)?;
41+
Ok(string)
42+
}
43+
inner(path.as_ref())
44+
}
45+
```
46+
47+
[^1]: <https://www.usenix.org/system/files/atc24-li-hongyu.pdf>
48+
[^2]: <https://github.com/rust-lang/rust/blob/ae612bedcbfc7098d1711eb35bc7ca994eb17a4c/library/std/src/fs.rs#L295-L304>
+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
---
2+
minutes: 13
3+
---
4+
5+
# Fallible Allocation
6+
7+
Allocation in Rust is assumed to be infallible:
8+
9+
```rust
10+
let x = Box::new(5);
11+
```
12+
13+
In the Linux kernel, memory allocation is much more complex.
14+
15+
```C
16+
void * kmalloc(size_t size, int flags)
17+
```
18+
19+
`flags` is one of `GFP_KERNEL`, `GFP_NOWAIT`, `GFP_ATOMIC`, etc.[^1]
20+
21+
The return value must be checked against `NULL` to see whether allocation succeeded.
22+
23+
In Rust for Linux, rather than using the infallible allocation APIs provided by `liballoc`,
24+
the kernel library has its own allocation interfaces:
25+
26+
## `KBox`
27+
28+
```rust
29+
let b = KBox::new(24_u64, GFP_KERNEL)?;
30+
assert_eq!(*b, 24_u64);
31+
```
32+
33+
[`KBox::new`](https://rust.docs.kernel.org/kernel/alloc/kbox/struct.Box.html#tymethod.new)
34+
returns a `Result<Self, AllocError>`.
35+
Here we propagate this error with the `?` operator.
36+
37+
## `KVec`
38+
39+
Similarly, [`KVec`](https://rust.docs.kernel.org/kernel/alloc/kvec/type.KVec.html)
40+
presents a similar API to the standard `Vec`, but where operations that may allocate
41+
take a flags parameter:
42+
43+
```rust
44+
let mut v = KVec::new();
45+
v.push(1, GFP_KERNEL)?;
46+
assert_eq!(&v, &[1]);
47+
```
48+
49+
## `FromIterator`
50+
51+
Because the standard [`FromIterator`](https://doc.rust-lang.org/std/iter/trait.FromIterator.html) trait also involves making new collections
52+
often involving memory allocation, the `.collect()` method on iterators
53+
is not available in Rust for Linux in its original form.
54+
Work is ongoing to design an equivalent API[^2], but for now we do without its convenience.
55+
56+
[^1]: <https://docs.kernel.org/core-api/memory-allocation.html>
57+
[^2]: <https://rust-for-linux.zulipchat.com/#narrow/channel/288089-General/topic/flat_map.20collecting.20with.20Kvec>

Diff for: src/rust-for-linux/complications/kernel-doc.md

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
---
2+
minutes: 3
3+
---
4+
5+
# Documentation
6+
7+
Documentation in Rust for Linux is built with the `rustdoc` tool just like for regular Rust code.
8+
9+
Running rustdoc on the kernel is done with the `rustdoc` Make target:
10+
11+
```sh
12+
$ make LLVM=1 rustdoc
13+
```
14+
15+
after which generated docs can be viewed by opening `Documentation/output/rust/rustdoc/kernel/index.html`.
16+
17+
Pre-generated documentation for the current kernel release is available at:
18+
19+
<https://rust.docs.kernel.org/kernel/>
20+
21+
## More information
22+
23+
<https://docs.kernel.org/rust/general-information.html#code-documentation>

0 commit comments

Comments
 (0)