diff --git a/src/SUMMARY.md b/src/SUMMARY.md index ebf5779e92dc..8bc6936d32b4 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -407,6 +407,38 @@ - [Broadcast Chat Application](concurrency/async-exercises/chat-app.md) - [Solutions](concurrency/async-exercises/solutions.md) +# Rust for Linux + +--- + +- [Welcome](rust-for-linux/welcome.md) +- [Interoperation Requirements](rust-for-linux/basic-requirements.md) + - [Building Kernel Modules](rust-for-linux/modules.md) + - [Type Mapping](rust-for-linux/types.md) + - [Bindings and Safe Interfaces](rust-for-linux/bindings-interfaces.md) + - [Avoiding Bloat](rust-for-linux/bloat.md) +- [Hands-on With Kernel Rust](rust-for-linux/hands-on.md) + - [Rust for Linux](rust-for-linux/rust-for-linux.md) + - [`rust-analyzer` Setup](rust-for-linux/rust-analyzer.md) + - [Macros](rust-for-linux/macros.md) + - [A Rust Kernel Module](rust-for-linux/kernel-module.md) + - [The `module!` Macro](rust-for-linux/modules/module-macro.md) + - [Module Setup and Teardown](rust-for-linux/modules/setup-and-teardown.md) + - [Module Parameters](rust-for-linux/modules/parameters.md) + - [Using Abstractions](rust-for-linux/using-abstractions.md) +- [Complications and Conflicts](rust-for-linux/complications.md) + - [`Pin` and Self-Reference](rust-for-linux/complications/pin.md) + - [The Kernel Rust Safety Model](rust-for-linux/complications/safety.md) + - [Atomic/Task Contexts and Sleep](rust-for-linux/complications/sleeping.md) + - [Memory Models](rust-for-linux/complications/memory-models.md) + - [Separate Compilation and Linking](rust-for-linux/complications/separate-compilation.md) + - [Fallible Allocation](rust-for-linux/complications/fallible-allocation.md) + - [Code Size](rust-for-linux/complications/code-size.md) + - [Documentation](rust-for-linux/complications/kernel-doc.md) + - [Security Mitigations](rust-for-linux/complications/mitigations.md) + - [Async](rust-for-linux/complications/async.md) +- [Next Steps](rust-for-linux/next-steps.md) + # Final Words --- diff --git a/src/rust-for-linux/basic-requirements.md b/src/rust-for-linux/basic-requirements.md new file mode 100644 index 000000000000..e8b349d283cb --- /dev/null +++ b/src/rust-for-linux/basic-requirements.md @@ -0,0 +1,53 @@ +# Interoperation Requirements + +To use Rust code in Linux, we can start by comparing this situation with C/Rust +interop in userspace. + +## Building + +In userspace, the most common setup is to use Cargo to compile our Rust and +later integrate into a C build system if needed. +Meanwhile, the Linux Kernel compiles its C code with its custom Kbuild build +system. +In Rust for Linux, the kernel build system invokes the Rust compiler directly, +without Cargo. + +## No `libstd` + +Unlike typical usage of Rust in userspace, which makes use of the rust standard +library through the `std` crate, Rust in the kernel does not run atop an +operating system, so kernel Rust will have to eschew the standard library. + +## Module Support + +Much code in the kernel is compiled into kernel modules rather than as part of +the core kernel. +To write kernel modules in Rust we'll need to be able to match the ABI of kernel +modules. + +## Safe Wrappers + +To reap the benefits of Rust, we want to be able to write as much code as +possible in safe Rust. +This means that we want safe wrappers for as much kernel functionality as +possible. + +## Mapping Types + +When writing these wrappers, we'll need to refer to the data types of values +passed to and from existing kernel functions in C. +Unlike userspace C, the kernel uses its own set of primitive types rather than +those provided by the C standard. +We'll have to map back and forth between those kernel types and compatible Rust +ones when doing foreign calls. + +## Keeping the Kernel Lean + +Finally, even the core Rust library assumes a basic level of functionality that +includes some costly operations (e.g. unicode processing) for which the kernel +does not want to pay implementation costs. +To use Rust in the kernel we'll need a way to disable this functionality. + +# Outline + +{{%segment outline}} diff --git a/src/rust-for-linux/bindgen-mapping.png b/src/rust-for-linux/bindgen-mapping.png new file mode 100644 index 000000000000..9f5eb1736981 Binary files /dev/null and b/src/rust-for-linux/bindgen-mapping.png differ diff --git a/src/rust-for-linux/bindings-interfaces.md b/src/rust-for-linux/bindings-interfaces.md new file mode 100644 index 000000000000..43c70f280213 --- /dev/null +++ b/src/rust-for-linux/bindings-interfaces.md @@ -0,0 +1,71 @@ +--- +minutes: 18 +--- + +# Bindings and Safe Interfaces + +`bindgen` is used to generate low-level, unsafe bindings for C interfaces. + +But to reap the benefits of Rust, we want to use safe, foolproof interfaces to unsafe functionality. + +Subsystems are expected to implement safe interfaces on top of the low-level generated bindings. +These safe interfaces are exposed as top-level modules within the [`kernel` crate](https://rust.docs.kernel.org/kernel/). +The top-level `bindings` module holds the unsafe `bindgen`-generated bindings, +which are generated from the C headers included by `rust/bindings/bindings_helper.h`. + +In Rust for Linux, unsafe `bindgen`-generated bindings should not be used outside the `kernel` crate. +Drivers and other subsystems will make use of the safe abstractions from this crate. + +Only a subset of Linux subsystems currently have such abstractions. + +It's worth browsing the [list of modules](https://rust.docs.kernel.org/kernel/#modules) +exposed by the `kernel` crate to see what exists currently. +Many of these subsystems have only partial bindings based on the needs of consumers so far. + +## Adding a Module + +To add a module for some subsystem, first its header must be added to `bindings_helper.h`. +It may be necessary to write some custom code to wrap macros or `inline` functions +that are not automatically handled by `bindgen`; this code lives in the `rust/helpers/` directory. + +Then we need to write a safe abstraction using these bindings and exposing them to the rest of kernel Rust. + +Some commits from work-in-progress bindings and abstractions +can provide an idea of what it looks like to expose new kernel functionality: + +- GPIO Consumer: [fecb4bd73f06bb2cac8e16aca7ef0e2f1b6acb50](https://github.com/Fabo/linux/commit/fecb4bd73f06bb2cac8e16aca7ef0e2f1b6acb50) +- Regmap: [ec0b740ac5ab299e4c86011a0002919e5bbe5c2d](https://github.com/Fabo/linux/commit/ec0b740ac5ab299e4c86011a0002919e5bbe5c2d) +- I2C: [70ed30fcdf8ec62fa91485c3c0a161a9d0194668](https://github.com/Fabo/linux/commit/70ed30fcdf8ec62fa91485c3c0a161a9d0194668) + +## Guidelines for Abstractions + +Abstractions may not be perfectly safe, but should try to be as safe as possible. +Unsafe functionality exposed should have its safety conditions documented +so that users have guidance on how to use the functionality and justify such use. + +Abstractions should also attempt to present relatively idiomatic Rust in their interfaces: +- Follow Rust naming/capitalization conventions while remaining unsurprising to kernel developers. +- Use RAII instead of manual resource management where possible. +- Avoid raw pointers to bound kernel objects in favor of safer, more limited interfaces. + + When exposing types from generated bindings, code should make use of the + [`Opaque`](https://rust.docs.kernel.org/kernel/types/struct.Opaque.html) type + along with native Rust references and the + [`ARef`](https://rust.docs.kernel.org/kernel/types/struct.ARef.html) type for types that are inherently reference-counted. + This type links types' built-in reference count operations to the `Clone` and `Drop` traits. + +## Submitting the cyclic dependency + +We already know that drivers should not use unsafe bindings directly. +But subsystem maintainers may balk if they see patches submitted that add Rust abstractions without motivation or consumers. +But drivers and subsystem abstractions may have to be submitted separately to different maintainers +due to the distributed nature of Linux development. + +So how should a developer submit a driver that requires bindings/abstractions for a subsystem not yet exposed to Rust? + +There are two main approaches[^1]: + +1. Submit the driver as an RFC before submitting the abstractions it relies upon while referencing the RFC as a potential consumer. +2. Submit a stub driver and fill out non-stub functionality as subsystem abstractions land. + +[^1]: diff --git a/src/rust-for-linux/bloat.md b/src/rust-for-linux/bloat.md new file mode 100644 index 000000000000..b94a1ff0d06f --- /dev/null +++ b/src/rust-for-linux/bloat.md @@ -0,0 +1,20 @@ +--- +minutes: 5 +--- + +# Avoiding Bloat + +Rust for Linux makes use of `libcore` to avoid reimplementing all functionality of the Rust standard library. +But even `libcore` has some functionality built-in that is not portable to all targets the kernel +would like to support or that is not necessary for the kernel while occupying valuable code space. + +This includes[^1]: + +- Support for math with 128-bit integers +- String formatting for floating-point numbers +- Unicode support for strings + +Work is ongoing to make these features optional. +In the meantime, the `libcore` used by Rust for Linux is larger and less portable than it could be. + +[^1]: diff --git a/src/rust-for-linux/complications.md b/src/rust-for-linux/complications.md new file mode 100644 index 000000000000..b38b0cc2d56a --- /dev/null +++ b/src/rust-for-linux/complications.md @@ -0,0 +1,39 @@ +--- +minutes: 5 +--- + +# Complications and Conflicts + +{{%segment outline}} + +There are a number of subtleties and unresolved conflicts between the Rust paradigm and the kernel one. +These must be resolved to ship Rust code in the kernel. + +Some issues are deeper problems that require additional research and development +before Rust for Linux is ready for the prime-time; +others merely require some additional learning and attention +on behalf of aspiring Rust for Linux developers. + +## + +Resolving these conflicts involves changes on both sides of the collaboration. +On the Rust side, new features land first in the Nightly edition of the compiler +before being stabilized. + +To avoid waiting for stabilization, the kernel uses an +[escape hatch](https://rustc-dev-guide.rust-lang.org/building/bootstrapping/what-bootstrapping-does.html#complications-of-bootstrapping) +to access unstable features even in stable releases of the compiler. +This assists in the goal of eventually deploying Rust for Linux in Linux +distributions that ship only a stable version of the Rust toolchain. + +Nonetheless, being able to build Rust for Linux using only stable Rust features +is a significant goal; +the issues blocking this are tracked specifically by both the Rust for Linux +project[^1] and the Rust developers themselves[^2]. + +In the next slides we'll explore the most significant sources of friction between +Rust and Linux kernel development to be aware of challenges we are likely to encounter +when trying to implement kernel functionality in Rust. + +[^1]: +[^2]: diff --git a/src/rust-for-linux/complications/async.md b/src/rust-for-linux/complications/async.md new file mode 100644 index 000000000000..0f4043523d6f --- /dev/null +++ b/src/rust-for-linux/complications/async.md @@ -0,0 +1,35 @@ +--- +minutes: 8 +--- + +# Async + +The kernel performs many operations concurrently and involves significant amounts of interaction +between CPU cores and other devices. +For this reason, it would be no surprise to see that async Rust would be a fundamental requirement +for using Rust in the kernel. +But the kernel is central arbitrer of most synchronization and is currently written in regular, synchronous C. + +Rust code making use of `async` mostly exists to write composable code that will run atop event loops, +but the Linux kernel is not really organized as an event loop: +user tasks call directly into the kernel; control flow for interrupts is handled by hardware. + +As such, `async` support is not critical for most kernel programming tasks. +However, it is possible to view some components of the kernel as async executors, +and some work has been done in this direction. +Wedson Almeida Filho implemented both workqueue-based[^1] and single-threaded async executors as proofs of concept. + +There is not a fundamental incompatibility between Rust-for-Linux and Rust `async`, +which is a similar situation to the amenability of `async` to use in embedded Rust programming +(e.g. the Embassy project). + +Nonetheless, no killer application of `async` in Rust for Linux has made it a priority. + +
+ +[^1]: + +An example of an async server using the kernel async executor may be found +[here](https://github.com/Rust-for-Linux/linux/blob/rust/samples/rust/rust_echo_server.rs). + +
diff --git a/src/rust-for-linux/complications/code-size.md b/src/rust-for-linux/complications/code-size.md new file mode 100644 index 000000000000..ac053de4e0aa --- /dev/null +++ b/src/rust-for-linux/complications/code-size.md @@ -0,0 +1,48 @@ +--- +minutes: 10 +--- + +# Code Size + +One pitfall when writing Rust code can be the multiplicative increase in generated machine code when using generics. + +For the Linux kernel, which must be suitable for space-limited embedded environments, +keeping code size low is a significant concern. + +Experiments with Rust in the kernel so far have shown that Rust code can be of similar code size to C, +but may also be larger in some cases[^1]. + +## Assessing Bloat + +Tools exist to help analyze different source code's contribution to the size of compiled code, +such as [`cargo-bloat`](https://github.com/RazrFalcon/cargo-bloat). + +## Shrinking Code Size + +The reasons for code bloat vary and are not generally specific to Linux kernel usage of Rust. +The most common causes for code bloat are excessive use of generics and forced inlining. +In general, generics should be prefered over trait objects when writing abstractions +that are expected to "compile out" or where generating separate code for different types is critical +for performance (e.g. inner loops or arithmetic on values of a generic type). + +In other situations, trait objects should be prefered to allow reusing definitions +without machine-code duplication, which may closer mirror patterns that would be most natural in C. + +When accepting generic parameters that get converted to a concrete type before use, +follow the pattern of defining an inner monomorphic function that can be shared[^2]: + +```rust +pub fn read_to_string>(path: P) -> io::Result { + fn inner(path: &Path) -> io::Result { + let mut file = File::open(path)?; + let size = file.metadata().map(|m| m.len() as usize).ok(); + let mut string = String::with_capacity(size.unwrap_or(0)); + io::default_read_to_string(&mut file, &mut string, size)?; + Ok(string) + } + inner(path.as_ref()) +} +``` + +[^1]: +[^2]: diff --git a/src/rust-for-linux/complications/fallible-allocation.md b/src/rust-for-linux/complications/fallible-allocation.md new file mode 100644 index 000000000000..e227d0eb6193 --- /dev/null +++ b/src/rust-for-linux/complications/fallible-allocation.md @@ -0,0 +1,57 @@ +--- +minutes: 13 +--- + +# Fallible Allocation + +Allocation in Rust is assumed to be infallible: + +```rust +let x = Box::new(5); +``` + +In the Linux kernel, memory allocation is much more complex. + +```C +void * kmalloc(size_t size, int flags) +``` + +`flags` is one of `GFP_KERNEL`, `GFP_NOWAIT`, `GFP_ATOMIC`, etc.[^1] + +The return value must be checked against `NULL` to see whether allocation succeeded. + +In Rust for Linux, rather than using the infallible allocation APIs provided by `liballoc`, +the kernel library has its own allocation interfaces: + +## `KBox` + +```rust +let b = KBox::new(24_u64, GFP_KERNEL)?; +assert_eq!(*b, 24_u64); +``` + +[`KBox::new`](https://rust.docs.kernel.org/kernel/alloc/kbox/struct.Box.html#tymethod.new) +returns a `Result`. +Here we propagate this error with the `?` operator. + +## `KVec` + +Similarly, [`KVec`](https://rust.docs.kernel.org/kernel/alloc/kvec/type.KVec.html) +presents a similar API to the standard `Vec`, but where operations that may allocate +take a flags parameter: + +```rust +let mut v = KVec::new(); +v.push(1, GFP_KERNEL)?; +assert_eq!(&v, &[1]); +``` + +## `FromIterator` + +Because the standard [`FromIterator`](https://doc.rust-lang.org/std/iter/trait.FromIterator.html) trait also involves making new collections +often involving memory allocation, the `.collect()` method on iterators +is not available in Rust for Linux in its original form. +Work is ongoing to design an equivalent API[^2], but for now we do without its convenience. + +[^1]: +[^2]: diff --git a/src/rust-for-linux/complications/kernel-doc.md b/src/rust-for-linux/complications/kernel-doc.md new file mode 100644 index 000000000000..8463f08f5df7 --- /dev/null +++ b/src/rust-for-linux/complications/kernel-doc.md @@ -0,0 +1,23 @@ +--- +minutes: 3 +--- + +# Documentation + +Documentation in Rust for Linux is built with the `rustdoc` tool just like for regular Rust code. + +Running rustdoc on the kernel is done with the `rustdoc` Make target: + +```sh +$ make LLVM=1 rustdoc +``` + +after which generated docs can be viewed by opening `Documentation/output/rust/rustdoc/kernel/index.html`. + +Pre-generated documentation for the current kernel release is available at: + + + +## More information + + diff --git a/src/rust-for-linux/complications/memory-models.md b/src/rust-for-linux/complications/memory-models.md new file mode 100644 index 000000000000..99cae267d35f --- /dev/null +++ b/src/rust-for-linux/complications/memory-models.md @@ -0,0 +1,54 @@ +--- +minutes: 10 +--- + +# Memory Models: LKMM vs. Rust (C11) Memory Model + +Memory models are their own complex topic which we will not cover in depth, +but it's important to understand how they relate to the Rust for Linux project. + +The Linux Kernel and the Rust language itself use different memory models, +which specify what guarantees are made when different threads interact through +shared memory and low-level synchronization primitives. + +- The kernel has its own memory model (Linux Kernel Memory Model or "LKMM"). + + This is because it predates standardized formal memory models for concurrency + and needs high performance for synchronization as used in RCU and elsewhere. +- Rust inherits the semantics promised by LLVM - from the C++11 specification + (and adopted by the C11 spec). + So Rust essentially uses the C11 MM. +- LKMM relies on orderings provided by address, data, and control dependencies. +- The C11 MM does not provide all of these, so it isn't simple to express the + LKMM in terms of the C11 MM. + - LKMM relies on semantics not guaranteed by the C spec but merely by + compiler behavior. + + This means that conforming to the C standard is not sufficient for an + arbitrary compiler to compile a working kernel. + In practice, the kernel is only compiled with GCC or Clang, which both + implement the desired semantics, so this is fine. + +Because Rust atomics and Linux kernel atomics do not necessarily provide +the same guarantees, using them together could have very surprising results. + +Instead, Kernel Rust should probably re-implement corresponding atomics the +same way the kernel does in C[^1]. + + - This should allow Rust for Linux to interoperate with the rest of the + kernel in an understandable way, + but could subtly alter the behavior of other crates that use atomics if + used in the kernel atop kernel atomics. + +
+ +See these links for more background: + +- +- +- +- + +[^1]: + +
diff --git a/src/rust-for-linux/complications/mitigations.md b/src/rust-for-linux/complications/mitigations.md new file mode 100644 index 000000000000..6145db5dffcc --- /dev/null +++ b/src/rust-for-linux/complications/mitigations.md @@ -0,0 +1,22 @@ +--- +minutes: 5 +--- + +# Security Mitigations + +Even though Rust is memory-safe, larger systems using Rust are not necessarily memory-safe. +The kernel is no exception. +The kernel is often compiled with various security mitigations and hardening flags, +and to avoid undermining these (e.g. by providing gadgets or running afoul of CPU errata), +Rust code compiled into the kernel should also be built with the same set of mitigations. + +Many of these mitigations are already supported by the Rust compiler, +which merely needs to expose the same underlying LLVM functionality offered by Clang. + +## Speculative execution (Meltdown/Spectre) mitigations + +Recent CPU side-channel vulnerabilities in particular require changes to compilers' code generation +("retpolines", etc.) in order to prevent userspace access to kernel data. +Support for these code-generation changes is still pending in `rustc`[^1]. + +[^1]: diff --git a/src/rust-for-linux/complications/pin.md b/src/rust-for-linux/complications/pin.md new file mode 100644 index 000000000000..f63ed0a24d98 --- /dev/null +++ b/src/rust-for-linux/complications/pin.md @@ -0,0 +1,80 @@ +--- +minutes: 15 +--- + +# `Pin` and Self-Reference + +The Linux kernel pervasively relies on intrusive data structures and +programming patterns that rely on the stability of objects' addresses. + +In C, these patterns show up in places like `struct list_head` and the `container_of` macro. + +The programming rules for these data structures require being careful about where instances are +allocated and how they are linked into and removed from containing data structures. + +## Moves + +In Rust, however, instances of data types may change their addresses any time they are moved, and +the compiler is relied on to be aware of any outstanding references that would prevent moving them. +The most common pattern for constructing values in Rust even involves a move-- +simply returning the value from a constructor function. + +This paradigm does not work for values that must be constructed "in-place" to avoid moves, +but the C approach of writing into a blob of uninitialized memory until fully initialized is also an anathema in Rust: +it would force us into writing unsafe code any place we wanted to construct an instance of our type. + +## `Pin` + +A similar concern already exists in Rust for compiler-generated types +that internally contain self references; +these can be occur in the state machines generated by the compiler for `async` functions. + +The `Pin` wrapper type exists to wrap an indirection (such as `&mut T` or `Box`) +in such a way that an `&mut T` cannot be created to the underlying `T` +(as this would allow using a function like `mem::swap` that would effectively change its address). + +## Field projection + +`Pin` also has the effect of requiring a choice for each field of the pinned type: +will it be accessed through a `Pin<&mut Field>` or simply through `&mut Field`? +Either may be acceptable, depending on the semantics of the type, but the two options +must not coexist for a single field as that would allow the `Pin<&mut Field>` to be moved +via `mem::swap` on the `&mut Field`. + +The boilerplate for exposing access to each field of a pinned struct ("projecting" the field) +via only one of `Pin<_>` or directly is handled by the `pin-project` crate in userspace Rust. + +Unfortunately, this crate uses procedural macros to parse Rust code +and these in turn have heavy dependencies that the Rust for Linux project does not want to take on. + +Instead, Rust for Linux has its own solution to pinned initialization and pin projection. + +## `pinned-init` + +The solution employed for these concerns in Rust for Linux is the `pinned-init` crate. +Using this crate looks like the following: + +```rust +use kernel::{prelude::*, sync::Mutex, new_mutex}; +#[pin_data] +struct Foo { + #[pin] + a: Mutex, + b: u32, +} + +let foo = pin_init!(Foo { + a <- new_mutex!(42, "Foo::a"), + b: 24, +}); + +// `foo` now is of the type `impl PinInit`. +// We can now use any smart pointer that we like (or just the stack) to actually initialize a Foo: + +let foo: Result>> = Box::pin_init(foo); +``` + +### Further reading + +- +- diff --git a/src/rust-for-linux/complications/safety.md b/src/rust-for-linux/complications/safety.md new file mode 100644 index 000000000000..a81c136721f6 --- /dev/null +++ b/src/rust-for-linux/complications/safety.md @@ -0,0 +1,49 @@ +--- +minutes: 15 +--- + +# The Kernel Rust Safety Model + +## Soundness + +Safety in normal, userspace Rust is already a subtle topic. +The verification boundary for `unsafe` code is not the unsafe block or even the containing function, +but the privacy boundary of the public interface of the containing module. +And the guarantees that unsafe code can rely on depend on a combination of the semantics of +regular Rust along with the behavior of the underlying compiler, operating system, and hardware. + +In kernel Rust, things are even more complicated. +The golden standard for Rust code making use of `unsafe` is that it must be impossible +for any consumer of the code to trigger undefined behavior through safe interfaces. +But there are many parts of the Linux kernel in which we might want to use Rust that cannot be +fully compartmentalized from the rest of the kernel by a safe, water-tight API. + +Many tasks performed by the kernel are only understandable outside the model of C or Rust language semantics: +for example, writing to CPU registers that control paging or DMA may alter the meaning of pointers, +but models of language semantics do not include notions of the underlying architecture's paging or memory-management system. +Tools like miri cannot analyze programs that perform low-level operations like these, +and static analysis tools similarly lack models of their effects. +So we're forced to live with a less thorough notion of safety than we might have in userspace Rust. + +For now, some kernel components will be suitable for writing fully safe Rust interfaces +(perhaps those with limited interactions with the rest of the system, such as GPIOs), +while others can only offer limited safety. + +This is an area where Rust for Linux is pushing the boundaries of +what Rust's paradigm of memory safety can achieve. + +## Limitations of Type and Memory Safety + +Rust's guarantees of memory safety provide a baseline that can raise our confidence in Rust code +head and shoulders above the status quo writing other low-level languages. +But some desirable properties are difficult or impossible to guarantee through Rust's type system. + +For example, because variables can always be dropped, it's difficult to guarantee liveness properties. + +Similarly, because Rust type- and borrow-checking are local analyses, +they cannot be used to ensure global properties like lock ordering. + +Other tools can perform useful static analyses for Rust code +similar to those that might be performed with standalone C static analysis packages or gcc compiler plugins. +[`Clippy`](https://doc.rust-lang.org/clippy/usage.html) is the most common static analysis tool for Rust code, +but for kernel-specific analyses the [`klint`](https://github.com/Rust-for-Linux/klint) tool also exists. diff --git a/src/rust-for-linux/complications/separate-compilation.md b/src/rust-for-linux/complications/separate-compilation.md new file mode 100644 index 000000000000..f9a2356518b8 --- /dev/null +++ b/src/rust-for-linux/complications/separate-compilation.md @@ -0,0 +1,46 @@ +--- +minutes: 7 +--- + +# Separate Compilation and Linking + +One hiccup integrating Rust into the kernel compilation process is that C is +designed for full separate compilation, +where each source file can be compiled into an object file, +and then these object files are linked into a single loadable archive by the C toolchain's linker. +Rust, however, expects to compile its programs at the granularity of individual crates +and control the linking process. + +In C, the compiler is not responsible for safety, +so the correctness of linking C built with different flags or compilers is left up to the user. +But compiled Rust code has no stable ABI, and so the compiler must be careful not to link together +two libraries compiled with different versions of the Rust compiler, or with different code-generation flags. + +## Target modifiers + +In cases where two crates are linked together, the Rust compiler will attempt to verify that they +have been compiled by the same version of the compiler to ensure that no ABI incompatibility will +undermine the memory safety of their composition. + +However, if one crate was compiled with modifications to its effective ABI relative to the other +(such as forbidding usage of a register, like the `-ffixed-x18` flag does), +then it may not be valid to conclude that the resulting program will behave as intended. + +The Rust compiler currently avoids this situation primarily +by treating each compiler configuration as an entirely separate target; +crates compiled for different targets may not be linked together. +But defining a fully custom target when running the compiler is a feature +only exposed by the unstable nightly version of the compiler, +which Rust for Linux does not want to commit to doing indefinitely. + +The way out is a proposal[^1] to create "target modifiers", +a stable way of specifying variants of standard targets at compile-time. +Compiled crates will be stamped with the target variant +so that the Rust compiler can ensure the target modifiers match at link-time, +but users will not be required to create an entirely new compilation target. + +
+ +[^1]: + +
diff --git a/src/rust-for-linux/complications/sleeping.md b/src/rust-for-linux/complications/sleeping.md new file mode 100644 index 000000000000..28d26d478d50 --- /dev/null +++ b/src/rust-for-linux/complications/sleeping.md @@ -0,0 +1,43 @@ +--- +minutes: 10 +--- + +# Atomic/Task Contexts and Sleep + +One of the safety conditions that the Rust type system does not help us establish is freedom from deadlocks. +In the Linux kernel, a related concern is only sleeping in contexts where doing so is allowed. +In particular, code executing in a task context may sleep, but code executing in an atomic context +(for example, within an interrupt handlers, while holding a spinlock, or in an RCU critical section) +may not. + +Sleeping in the wrong place may lead to kernel hangs, but in the context of RCU, +it can even threaten memory safety: +if a CPU sleeps in an RCU read-side critical section, it will be mistakenly considered to have exited that critical section, +potentially leading to use-after-free[^1]. + +Existing C code in the kernel relies on [`might_sleep`](https://elixir.bootlin.com/linux/v6.12.6/source/include/linux/kernel.h#L93) +and similar annotations which facilitate debugging via runtime tracking when `CONFIG_DEBUG_ATOMIC_SLEEP` is enabled. + +Because of the need to forbid sleep, it is not sufficient to simply use RAII to model RCU in Rust +as we might intuitively want to do. +We need some additional checking to ensure that while RCU guards exist, no sleeps or context switches +are performed. + +# `klint` + +The [`klint`](https://rust-for-linux.com/klint) tool performs static analysis on kernel Rust code +and addresses this problem by tracking preemption count across all functions at compile-time. + +It does so based on annotations added to our functions that specify: +- the expected range of preemption counts when calling the function +- the adjustment performed to the preemption count after the function returns + +If a function is called from a context where preemption count may be outside the function's expectation, +`klint` will emit an error message. + +Recursive functions, generics, and function pointers complicate this analysis, so it is not foolproof, +and conditional control flow around also means `klint`'s analysis is approximate. +But this still catches obvious mistakes in straightforward code, +and `klint` is only likely to improve its analyses. + +[^1] diff --git a/src/rust-for-linux/hands-on.md b/src/rust-for-linux/hands-on.md new file mode 100644 index 000000000000..5f772fa3146c --- /dev/null +++ b/src/rust-for-linux/hands-on.md @@ -0,0 +1,7 @@ +# Hands-on With Kernel Rust + +{{%segment outline}} + +We've talked about the general requirements for using Rust in the Linux kernel. + +Now let's dig into the code as it stands and see how to work with the present state of Rust for Linux. diff --git a/src/rust-for-linux/kernel-module.md b/src/rust-for-linux/kernel-module.md new file mode 100644 index 000000000000..8bef28883f3a --- /dev/null +++ b/src/rust-for-linux/kernel-module.md @@ -0,0 +1,49 @@ +# A Rust Kernel Module + +A minimal Rust kernel module looks like the below +(from [`samples/rust/rust_minimal.rs`](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/samples/rust/rust_minimal.rs) in the Rust for Linux tree): + +```rust +use kernel::prelude::*; + +module! { + type: RustMinimal, + name: "rust_minimal", + author: "Rust for Linux Contributors", + description: "Rust minimal sample", + license: "GPL", +} + +struct RustMinimal { + numbers: KVec, +} + +impl kernel::Module for RustMinimal { + fn init(_module: &'static ThisModule) -> Result { + pr_info!("Rust minimal sample (init)\n"); + pr_info!("Am I built-in? {}\n", !cfg!(MODULE)); + + let mut numbers = KVec::new(); + numbers.push(72, GFP_KERNEL)?; + numbers.push(108, GFP_KERNEL)?; + numbers.push(200, GFP_KERNEL)?; + + Ok(RustMinimal { numbers }) + } +} + +impl Drop for RustMinimal { + fn drop(&mut self) { + pr_info!("My numbers are {:?}\n", self.numbers); + pr_info!("Rust minimal sample (exit)\n"); + } +} +``` + +We'll examine each part of the module definition in the following slides. + +
+ +It is also possible to build Rust kernel modules [out-of-tree](https://github.com/Rust-for-Linux/rust-out-of-tree-module). + +
diff --git a/src/rust-for-linux/macros.md b/src/rust-for-linux/macros.md new file mode 100644 index 000000000000..3a544db363ae --- /dev/null +++ b/src/rust-for-linux/macros.md @@ -0,0 +1,53 @@ +--- +minutes: 10 +--- + +# Macros + +The `kernel` crate exposes some kernel functionality through macros, +and provides other macros to facilitate definitions that follow kernel patterns. + +## Printing macros + +The kernel provides [`pr_info!`](https://rust.docs.kernel.org/kernel/macro.pr_info.html) and +[`dev_info!`](https://rust.docs.kernel.org/kernel/macro.dev_info.html), +which correspond to the identically-named kernel macros in C. +These support string formatting compatible with Rust's `std::print!`. + +```rust +pr_info!("hello {}\n", "there"); +``` + +## Conditional Compilation + +In C, conditional compilation is done with the preprocessor using `#if`/`#ifdef`. + +Rust in the kernel may want to perform conditional compilation +(which is done with `#[cfg]` attributes in Rust) based on the same `CONFIG_FOO` macros as C code might consider. + +The kernel build system exports these as `cfg`s, so they can be used as shown below[^1]: + +```rust +#[cfg(CONFIG_X)] // Enabled (`y` or `m`) +#[cfg(CONFIG_X="y")] // Enabled as a built-in (`y`) +#[cfg(CONFIG_X="m")] // Enabled as a module (`m`) +#[cfg(not(CONFIG_X))] // Disabled +``` + +## Kernel Vtables + +The kernel has slightly different requirements for its vtables than Rust traits provide. +For kernel vtables, unimplemented functions are represented by `NULL` function pointers, +while Rust traits always implement all methods. + +This mismatch is resolved by providing Rust with the +[`vtable!`](https://rust-for-linux.github.io/docs/macros/attr.vtable.html) attribute macro. + +This macro is placed above trait definitions and impls and provides +constant `bool` `HAS_METHODNAME` members for each method. + +## The `module!` macro + +Kernel modules are defined with the `macro!` module, which we'll examine on its own. + +[^1]: diff --git a/src/rust-for-linux/modules.md b/src/rust-for-linux/modules.md new file mode 100644 index 000000000000..5f61a41cd05c --- /dev/null +++ b/src/rust-for-linux/modules.md @@ -0,0 +1,23 @@ +--- +minutes: 5 +--- + +# Building Kernel Modules + +To build kernel modules in Rust, we need to build `.ko` shared objects +that link against the rest of the kernel. + +In C, kernel modules use the `module_init` and `module_exit` macros to specify how to initialize and +deinitialize the module. +For Rust, we'll need some equivalent of these macros. +Ultimately, these macros specify the values of two fields in a `struct this_module` +which is placed in the `.gnu.linkonce.this_module` section of the kernel module object file. + +We can achieve this in Rust with by defining an equivalent struct as a `static` item +and using the `#[unsafe(link_section = ".gnu.linkonce.this_module")]` attribute. +The `.init` and `.exit` fields of our struct will need to be pointers to the appropriate functions, +which leads to our next question: +how do we define Rust functions with the types and calling convention expected here? + +In practice, Rust for Linux has a convenient and safe wrapper around this pattern +which we'll see when we look at a real-world Rust kernel module. diff --git a/src/rust-for-linux/modules/module-macro.md b/src/rust-for-linux/modules/module-macro.md new file mode 100644 index 000000000000..037201c02ed2 --- /dev/null +++ b/src/rust-for-linux/modules/module-macro.md @@ -0,0 +1,22 @@ +--- +minutes: 2 +--- + +# The `module!` Macro + +A kernel module itself is declared with the [`module!`](https://rust.docs.kernel.org/macros/macro.module.html) macro. + +Here we specify the type for the module, upon which we will implement the `kernel::Module` trait, +as well as metadata like the module's name and description. + +```rust +module! { + type: RustMinimal, + name: "rust_minimal", + author: "Rust for Linux Contributors", + description: "Rust minimal sample", + license: "GPL", +} + +struct RustMinimal; +``` diff --git a/src/rust-for-linux/modules/parameters.md b/src/rust-for-linux/modules/parameters.md new file mode 100644 index 000000000000..fcca4e233261 --- /dev/null +++ b/src/rust-for-linux/modules/parameters.md @@ -0,0 +1,13 @@ +--- +minutes: 2 +--- + +# Module Parameters + +Support for defining and accessing module parameters from Rust has not yet landed in mainline Linux. + +However, there is outstanding work toward supporting module parameters[^1]. + +In the meantime, it may be preferable to configure modules through sysfs. + +[^1]: diff --git a/src/rust-for-linux/modules/setup-and-teardown.md b/src/rust-for-linux/modules/setup-and-teardown.md new file mode 100644 index 000000000000..f2496b03da33 --- /dev/null +++ b/src/rust-for-linux/modules/setup-and-teardown.md @@ -0,0 +1,29 @@ +--- +minutes: 4 +--- + +# Module Setup and Teardown + +Our module implements the [`kernel::Module`](https://rust.docs.kernel.org/kernel/trait.Module.html) trait +to specify its entrypoint and perform any necessary set-up: + +```rust +pub trait Module: Sized + Sync { + fn init(name: &'static CStr, module: &'static ThisModule) -> Result; +} +``` + +If some setup fails (e.g. finding device tree nodes or acquiring needed resources), +the `init` method can return `Err`. + +## `Drop` impl + +By implementing `Drop` on our module struct, we can perform any necessary cleanup and teardown. + +```rust +impl Drop for MyModule { + fn drop(&mut self) { + // ... + } +} +``` diff --git a/src/rust-for-linux/next-steps.md b/src/rust-for-linux/next-steps.md new file mode 100644 index 000000000000..b8c8f96d40a1 --- /dev/null +++ b/src/rust-for-linux/next-steps.md @@ -0,0 +1,12 @@ +--- +minutes: 5 +--- + +# Next Steps + +The Linux documentation has a number of +[pointers to further resources](https://docs.kernel.org/process/kernel-docs.html#rust) on using Rust in the kernel. + +The Rust for Linux project lists various avenues for [contacting the project](https://rust-for-linux.com/contact). + +The [Rust for Linux Zulip](https://rust-for-linux.zulipchat.com/) is particularly active and a good starting point for inquiries. diff --git a/src/rust-for-linux/rust-analyzer.md b/src/rust-for-linux/rust-analyzer.md new file mode 100644 index 000000000000..cc358b56c3ed --- /dev/null +++ b/src/rust-for-linux/rust-analyzer.md @@ -0,0 +1,25 @@ +--- +minutes: 3 +--- + +# `rust-analyzer` Setup + +The `rust-analyzer` LSP server provides IDE support for working with Rust. + +First, we install rust-analyzer normally: + +```sh +$ rustup component add rust-analyzer +``` + +To use it with Rust for Linux, we need to generate a configuration file for +`rust-analyzer`[^1]: + +```sh +$ make rust-analyzer +``` + +Then, opening our editor in the directory where the `rust-project.json` file +was created should run the language server with the appropriate settings. + +[^1]: diff --git a/src/rust-for-linux/rust-for-linux.md b/src/rust-for-linux/rust-for-linux.md new file mode 100644 index 000000000000..6608418f4f68 --- /dev/null +++ b/src/rust-for-linux/rust-for-linux.md @@ -0,0 +1,35 @@ +--- +minutes: 4 +--- + +# Getting Rust for Linux + +First, we want a checkout of Linux with Rust support. +The basics have been upstream since + +Then, we can follow the instructions from the Rust for Linux +[quick-start guide](https://github.com/Rust-for-Linux/linux/blob/rust/Documentation/rust/quick-start.rst#rust-analyzer): + +1. Install a Rust toolchain, including standard library sources: + +```sh +$ rustup override set $(scripts/min-tool-version.sh rustc) +$ rustup component add rust-src rustfmt clippy +``` + +This installs the oldest version of the Rust compiler supported by Rust for Linux. +Any changes should also be tested against the latest stable rustc release. + +2. Install bindgen: + +```sh +$ cargo install --locked --version $(scripts/min-tool-version.sh bindgen) bindgen +``` + +2. Enable `CONFIG_RUST` in your kernel build configuration. + +3. When building the kernel, use an LLVM toolchain: + +```sh +$ make LLVM=1 +``` diff --git a/src/rust-for-linux/types.md b/src/rust-for-linux/types.md new file mode 100644 index 000000000000..d24af85b6161 --- /dev/null +++ b/src/rust-for-linux/types.md @@ -0,0 +1,19 @@ +--- +minutes: 5 +--- + +# Type Mapping + +The kernel uses slightly different conventions for its types than other C code. +As such, the normal use of `bindgen` to generate bindings can introduce some problems when applied to the kernel. + +The explicit `{u,s}{8,16,32,64}` types map in the obvious way to Rust `{u,i}{8,16,32,64}` types, +but the potential signedness of `char` as well as +the kernel's requirement that `long`s can hold pointers +introduce some deviations from the expectations of C as implemented by bindgen. + +As such, an alternative bindgen mapping is used for the kernel[^1]: + +![the kernel bindgen mapping](bindgen-mapping.png) + +[^1]: diff --git a/src/rust-for-linux/using-abstractions.md b/src/rust-for-linux/using-abstractions.md new file mode 100644 index 000000000000..ab5d67a07e0a --- /dev/null +++ b/src/rust-for-linux/using-abstractions.md @@ -0,0 +1,11 @@ +--- +minutes: 12 +--- + +# Using Abstractions + +Now that we've seen a trivial driver, let's look at a real one for the +[`Asix ax88796b network PHY`](https://github.com/Rust-for-Linux/linux/blob/rust-next/drivers/net/phy/ax88796b_rust.rs). + +Here we'll see what it looks like to register a driver for a particular subsystem +and implement the needed functionality using abstractions from a subsystem. diff --git a/src/rust-for-linux/welcome.md b/src/rust-for-linux/welcome.md new file mode 100644 index 000000000000..589a0455c02e --- /dev/null +++ b/src/rust-for-linux/welcome.md @@ -0,0 +1,25 @@ +--- +course: Rust for Linux +session: Morning +target_minutes: 180 +--- + +# Welcome to Rust for Linux + +The Linux Kernel is currently working to add support for developing with the +Rust programming language. + +This involves considerations on both sides of the equation - Linux and Rust. + +Rust must conform to the non-negotiable requirements Linux imposes on compile- +and run-time behavior of its core and modules. Meanwhile, Linux must expose its +existing functionality in ways that Rust can access as efficiently and safely +as possible. + +We'll look at the general background, get our hands dirty with some existing +Rust code in Linux, and then explore particular integration challenges, both +resolved and ongoing. + +## Schedule + +{{%session outline}}