December 11, 2022·6 min read·engineeringrustlinuxkernelownership

Linux 6.1 gives Rust a kernel integration boundary

Linux 6.1 includes initial Rust build support and module infrastructure. The release's own documentation distinguishes generated C bindings from the safe abstractions that kernel code needs above them.

Compiling Rust for a kernel is only part of making it useful there. The code also needs an allocator, module lifecycle integration and a way to interact with existing C interfaces without asking every driver author to reconstruct their safety rules. Linux 6.1 includes the initial infrastructure for that work.

Linus Torvalds's Linux 6.1 release tag is dated 11 December 2022. The tag contains Rust support code, sample modules and documentation describing how the new language participates in the kernel build. The concrete addition is an integration point for further development, with a deliberately limited supported configuration.

I find Rust particularly persuasive where new systems code would otherwise be written in C. That does not make replacing an existing implementation an automatic goal. The useful question here is whether new abstractions can express kernel resource and lifetime rules while still interacting correctly with the C code that owns those facilities.

The kernel build selects the environment

The quick-start guide in the release describes the required tools and configuration. A particular rustc version is required because the kernel currently depends on unstable features. The guide explicitly warns that newer versions may or may not work.

The tool-version script selects Rust 1.62.0 and bindgen 0.56.0. These values describe the released build requirements. An assumption that the newest stable compiler must be interchangeable would miss the dependency on features and interfaces outside Rust's ordinary stable contract.

The build also needs Rust library source because it cross-compiles core and alloc. This is not the same environment as a normal application simply linking the standard library shipped for its host. The kernel establishes its own compilation context and provides the runtime facilities the Rust code uses there.

The guide gives a target for checking availability. It runs the same detection logic used for the relevant configuration decision and explains missing requirements.

make -j 1 LLVM=1 rustavailable

This is a prerequisite check, not a request to build and boot an entire kernel. Its value is that it tests the source tree's own definition of a suitable toolchain. That gives a more precise result than observing that rustc happens to exist on the developer's PATH.

Configuration is intentionally constrained

The released configuration makes Rust support conditional on architecture and toolchain availability. It also excludes combinations including module versioning, GCC plugins, randomized structure layout and BTF debug information. Those conditions are part of what this initial support means in practice.

The architecture guide lists x86-64 as the maintained architecture. It connects the constraints to LLVM code generation and the LLVM/Clang support needed for bindgen. General Rust support for another target does not establish equivalent Rust-in-kernel support in this release.

The quick-start guide calls a complete LLVM toolchain the best-supported setup. Other compiler arrangements have different support levels, with GCC configurations described as very experimental. This is a concrete reason to distinguish the host language toolchain from the kernel configuration being built.

My preference is to read this as a narrow engineering contract. A supported initial path lets maintainers develop the integration with a defined set of assumptions. Describing it as universal language support would obscure the work represented by the configuration restrictions.

Bindings describe C declarations

The release's general information document distinguishes bindings from abstractions. Bindings are Rust declarations corresponding to C functions and types. Bindgen generates them at build time, using libclang to understand the C side.

That is necessary interoperability work, but it is not a complete safe API. A declaration can say which arguments a C function accepts without expressing every ownership, lifetime or execution-context requirement governing its use. A Rust wrapper needs to account for those requirements before exposing a safe operation.

The document gives a mutex wrapper as an example of an abstraction around a C kernel facility. It recommends that leaf modules such as drivers use subsystem abstractions instead of calling bindings directly. The intended layering keeps unsafe obligations concentrated where the relevant kernel rules can be reviewed together.

This distinction is central to the value of Rust in a mixed-language kernel. If every user receives only raw declarations, each user must independently understand the underlying contract. A suitable abstraction can instead preserve the rule through its types and operations, reducing the number of call sites that need to manipulate raw state.

The kernel crate is the shared interface

The kernel crate in Linux 6.1 describes itself as the collection of ported or wrapped kernel APIs shared by Rust code. Its documentation directs authors to add missing wrappers there instead of bypassing the crate for an unwrapped C interface.

The crate uses no_std and relies on core and alloc. It contains the kernel-facing allocator integration outside test configurations, error types and printing support. Those are basic facilities a module needs before it can express useful behavior in the kernel environment.

The crate also checks that kernel configuration information is available to conditional compilation. Its explicit compile-time error prevents a missing configuration from silently changing the Rust code selected for the build.

That organization also identifies where growth must occur. Adding a Rust module that needs another C subsystem requires an appropriate interface to that subsystem. Language syntax alone does not supply it. The integration effort includes reviewing the wrapper's relationship to the existing kernel implementation.

I regard that as more substantial than merely choosing a second compiler. The design asks which invariants can become part of a reusable Rust interface. A wrapper is useful when it reduces what downstream code must assume without changing the kernel facility's actual rules.

A sample module shows initialization and cleanup

The minimal sample in the release declares its module metadata through the module macro. It implements kernel::Module to initialize its state and uses Drop for cleanup behavior. This connects ordinary Rust resource lifetime machinery to the kernel module lifecycle.

The initialization method returns a kernel result. In the sample, a vector receives elements through fallible try_push calls, with errors propagated by the question-mark operator. That is a concrete difference from assuming that every allocation succeeds or that ordinary user-space allocation policy applies unchanged.

The sample also reports whether it was built into the kernel or as a module. The quick-start guide permits selecting sample modules in either form. These examples establish a path for building and observing Rust code through the kernel's existing configuration and module mechanisms.

They should be understood as examples of the new infrastructure. The sample's arithmetic values and log messages do not demonstrate a production driver or broad subsystem coverage. The useful evidence is the lifecycle and allocation behavior that the released code actually contains.

Development tooling follows the kernel configuration

The general information guide provides a rustdoc target for generating local API documentation. It explains that generated documentation reflects the selected kernel configuration. That is useful when APIs and conditional code depend on the build being examined.

Rust-analyzer integration similarly uses a generated rust-project.json file. The quick-start guide provides a Make target for that configuration. The editor therefore needs an account of the kernel's Rust build relationships rather than assuming that a conventional application Cargo manifest defines the whole project.

The guide also distinguishes build tools from development tools. Cargo is used for the Rust tests, while rustdoc supplies documentation tests. Clippy can add analysis through a build option, but the documentation warns that it may affect code generation and should not be enabled for production kernel builds.

These details make the initial support reviewable. A contributor can check availability, generate configuration-aware documentation and examine a sample against the same source tree. The integration has explicit places for the compiler, editor and kernel build to agree about which code is being processed.

Linux 6.1's Rust support gives developers a bounded place to construct and evaluate abstractions alongside the existing C kernel. Useful modules need safe interfaces for the kernel facilities they consume, with ownership and lifetime obligations expressed at that boundary.