Skip to main content
Rust support in the Linux kernel was merged in Linux 6.1. The goal is to provide a second language for kernel development that offers stronger memory safety guarantees than C, reducing entire classes of bugs (use-after-free, data races, uninitialized memory) at compile time.
Rust in the kernel uses only core (the #![no_std] subset of the Rust standard library). The full std library is not available. All kernel Rust code must use #![no_std].

Current status and subsystems

Rust is an opt-in language in the kernel. It must be enabled with CONFIG_RUST. Current Rust support exists in:

Core abstractions

rust/kernel/ contains abstractions for memory allocation, synchronization primitives (Mutex, SpinLock, RwLock), task management, work queues, IRQ handling, and more.

Drivers

Device drivers in drivers/ can be written in Rust. The Apple AGX GPU driver, several PCI and platform drivers, and misc device drivers use Rust.

Filesystems

rust/kernel/fs.rs and rust/kernel/fs/ provide filesystem abstractions. The null_blk driver has a Rust implementation.

Networking

rust/kernel/net/ provides basic networking abstractions.
The rust/kernel/ directory contains abstractions for:
  • sync/ — Mutex, SpinLock, CondVar, RcuRef
  • mm/ — memory management
  • net/ — networking
  • fs/ — filesystem support
  • drm/ — DRM/GPU infrastructure
  • pci.rs, platform.rs — bus abstractions
  • device.rs, driver.rs — device model
  • irq.rs — interrupt handling
  • workqueue.rs — work queue API
  • task.rs — task/process management
  • print.rspr_info!, pr_err! macros
  • error.rsResult<T, Error> with kernel error codes

Setting up the Rust toolchain

1

Install distribution packages (preferred)

pacman -S rust rust-src rust-bindgen
2

Or install via rustup

# Install rustup
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

# In the kernel source directory, set the toolchain
rustup override set stable

# Add required components
rustup component add rust-src rustfmt clippy

# Install bindgen
cargo install --locked bindgen-cli
3

Verify requirements are met

# This checks all Rust requirements and explains any failures
make LLVM=1 rustavailable
4

Enable Rust in kernel configuration

# Via menuconfig: General setup -> Rust support
make LLVM=1 menuconfig

# Or directly
scripts/config --enable CONFIG_RUST
5

Build with LLVM

# Full LLVM toolchain is the best-supported setup
make LLVM=1

# Generate rust-analyzer configuration for IDE support
make LLVM=1 rust-analyzer

# Generate rustdoc API documentation
make LLVM=1 rustdoc
xdg-open Documentation/output/rust/rustdoc/kernel/index.html
The libclang (part of LLVM) is required for bindgen to parse C headers. Building with LLVM=1 ensures a consistent LLVM version across the C and Rust toolchains.

Architecture: abstractions vs. bindings

The kernel’s Rust architecture separates concerns into two layers:
                              rust/bindings/
                             (rust/helpers/)

                               include/ -----+ <-+
                                             |   |
drivers/        rust/kernel/  +----------+ <-+   |
  fs/                         | bindgen  |       |
 .../      +----------------+ +----------+ --+   |
           | Abstractions   |                |   |
+--------+ | +----+ +-----+ |  +----------+ |   |
| my_foo | | |sync| | net | |  | Bindings | <-+ |
| driver | | +----+ +-----+ |  |  crate   |     |
+--------+ | kernel crate   |  +----------+ <---+
    |      +----------------+
    |
    +--------# FORBIDDEN #---------+
             (no direct bindings)   |
  • Bindings (rust/bindings/): auto-generated by bindgen from C headers. Raw, unsafe FFI declarations.
  • Abstractions (rust/kernel/): safe Rust wrappers around bindings. Encode invariants in the type system.
  • Drivers/subsystems: use only the safe abstraction layer. Direct use of bindings by drivers is forbidden.
C inline functions and non-trivial macros that bindgen cannot handle go in rust/helpers/ as small wrapper functions.

Writing a simple kernel module in Rust

The canonical example from samples/rust/:
// SPDX-License-Identifier: GPL-2.0

//! Rust minimal sample.

use kernel::prelude::*;

module! {
    type: RustMinimal,
    name: "rust_minimal",
    author: "Rust for Linux Contributors",
    description: "Rust minimal sample",
    license: "GPL",
}

struct RustMinimal {
    numbers: Vec<i32>,
}

impl kernel::Module for RustMinimal {
    fn init(_module: &'static ThisModule) -> Result<Self> {
        pr_info!("Rust minimal sample (init)\n");

        let mut numbers = Vec::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");
    }
}
The module! macro generates the module metadata (MODULE_AUTHOR, MODULE_LICENSE, etc.) and the init_module/cleanup_module symbols expected by the kernel module loader. Module initialization returns Result<Self> — returning Err(...) causes the module load to fail with the corresponding error code. Cleanup happens in Drop::drop, which runs when the module is unloaded.

Safety requirements

Rust’s safety model in the kernel is stricter than standard Rust. The kernel uses unsafe code to interface with C, but encapsulates it carefully.

The unsafe contract

// Every unsafe block MUST have a // SAFETY: comment explaining why
// the code is sound — why it cannot cause undefined behavior.

// SAFETY: `ptr` is valid for writes of `size` bytes as guaranteed
// by the caller's contract documented in the # Safety section.
unsafe { core::ptr::copy_nonoverlapping(src, dst, size) };
// SAFETY: comments are mandatory before every unsafe block. They document not just that you checked, but what you checked and why it’s sufficient.

Safety sections in documentation

/// Dereferences a raw pointer.
///
/// # Safety
///
/// - `ptr` must be non-null and properly aligned.
/// - `ptr` must point to a valid, initialized `T`.
/// - The pointed-to value must not be aliased by any mutable reference
///   for the duration of the returned reference's lifetime.
pub unsafe fn deref_ptr<'a, T>(ptr: *const T) -> &'a T {
    // SAFETY: All invariants verified by the caller per the # Safety section.
    unsafe { &*ptr }
}

deny(unsafe_code) for leaf modules

Drivers and subsystem code that only uses the abstraction layer (not bindings directly) should annotate unsafe code appropriately:
// Leaf drivers that use only safe abstractions often have no unsafe blocks at all.
// The kernel crate's abstractions encapsulate all necessary unsafe internally.

use kernel::prelude::*;
use kernel::sync::Mutex;
use kernel::net::SkBuff;

Kernel Rust API

Error handling

Kernel Rust uses kernel::error::Result<T> which is core::result::Result<T, kernel::error::Error>. The Error type wraps kernel error codes (-ENOMEM, -EINVAL, etc.):
use kernel::prelude::*;
use kernel::error::{code, Result};

fn allocate_buffer(size: usize) -> Result<Vec<u8>> {
    if size == 0 {
        return Err(code::EINVAL);
    }
    if size > MAX_BUFFER_SIZE {
        return Err(code::ENOMEM);
    }
    let mut buf = Vec::new();
    buf.try_resize(size, 0, GFP_KERNEL)?;
    Ok(buf)
}
The ? operator propagates errors, converting from C error codes to Rust Result automatically.

Synchronization primitives

use kernel::prelude::*;
use kernel::sync::{Arc, Mutex};

struct SharedState {
    // Data protected by the mutex is stored inside it
    count: Mutex<u32>,
}

impl SharedState {
    fn increment(&self) -> Result {
        let mut guard = self.count.lock();
        *guard += 1;
        Ok(())
    }
}
The Mutex<T> type enforces that the data inside can only be accessed while the lock is held — this is enforced at compile time by the borrow checker, unlike C’s struct mutex.

Printing

use kernel::prelude::*;

// pr_info!, pr_err!, pr_warn!, pr_debug!, pr_notice!, pr_cont!
pr_info!("Module loaded, version {}\n", VERSION);
pr_err!("Failed to initialize: {}\n", err);
pr_debug!("Processing entry id={}\n", id);

// dev_info!, dev_err!, dev_warn!, dev_dbg! (device-associated)
dev_info!(dev, "Device found at address {:#x}\n", addr);
These macros correspond to their C counterparts (pr_info(), dev_info(), etc.) and use the same kernel log levels.

Memory allocation

use kernel::prelude::*;
use kernel::alloc::flags;

// Box::new_in with GFP flags
let boxed = Box::new(MyStruct { field: 42 }, GFP_KERNEL)?;

// Vec with kernel allocator
let mut v: Vec<u32> = Vec::new();
v.push(1, GFP_KERNEL)?;
v.push(2, GFP_KERNEL)?;

// Arc for shared ownership
let shared = Arc::new(MyData::default(), GFP_KERNEL)?;
let clone = shared.clone();
Kernel Rust allocation always requires explicit GFP flags and returns Result — allocation failure is always handled explicitly, never silently.

C interoperability via bindgen

bindgen generates Rust FFI bindings from C headers automatically during the build.

How bindings are generated

# Headers listed in rust/bindings/bindings_helper.h are processed by bindgen
cat rust/bindings/bindings_helper.h
# #include <linux/blk-mq.h>
# #include <linux/blk_types.h>
# #include <linux/blkdev.h>
# ...

# After build, generated bindings appear in:
ls rust/bindings/*_generated.rs
To add bindings for a new C header, add it to rust/bindings/bindings_helper.h.

Using bindings in abstractions

// In rust/kernel/
use crate::bindings;

/// Safe wrapper around struct mutex.
#[repr(transparent)]
pub struct Mutex(Opaque<bindings::mutex>);

impl Mutex {
    /// Creates a new initialized mutex.
    pub fn new() -> impl PinInit<Self> {
        pin_init!(Self(Opaque::ffi_init(|p: *mut bindings::mutex| {
            // SAFETY: `p` is a valid pointer to an uninitialized mutex.
            unsafe { bindings::mutex_init(p) };
        })))
    }

    /// Acquires the mutex, blocking.
    pub fn lock(&self) -> Guard<'_> {
        // SAFETY: `self.0.get()` is a valid mutex pointer.
        unsafe { bindings::mutex_lock(self.0.get()) };
        Guard { mutex: self }
    }
}

C FFI types

Use type aliases from the kernel prelude for C types, not core::ffi:
use kernel::prelude::*;
// c_int, c_uint, c_char, c_void, c_ulong, etc. are available
// via the kernel crate — do NOT use core::ffi aliases directly

fn my_callback(arg: c_int) -> c_int {
    // ...
    0
}

Rust coding style for the kernel

Formatting

Kernel Rust code is formatted with rustfmt using default settings (4-space indentation, unlike C’s 8-character tabs):
# Format all Rust sources
make LLVM=1 rustfmt

# Check formatting (prints diff without modifying)
make LLVM=1 rustfmtcheck

# Run Clippy linter
make LLVM=1 CLIPPY=1

Import style

The kernel uses a vertical import layout to minimize merge conflicts:
// Correct — one item per line, trailing empty comment
use crate::{
    sync::{
        Arc,
        Mutex,
        SpinLock, //
    },
    task::Task, //
};

// Avoid — condensed style prone to merge conflicts
use crate::{sync::{Arc, Mutex, SpinLock}, task::Task};

Naming

Follow standard Rust naming conventions (CamelCase for types, snake_case for functions and variables, SCREAMING_SNAKE_CASE for constants). When wrapping C concepts, use a name as close as reasonably possible to the C side:
// C: pr_info() -> Rust: pr_info!()
// C: struct mutex -> Rust: Mutex
// C: GPIO_LINE_DIRECTION_IN -> Rust: gpio::LineDirection::In

Lints: prefer expect over allow

// Prefer #[expect] — warns if the lint no longer fires (dead annotation)
#[expect(dead_code)]
fn temporarily_unused() {}

// Use #[allow] only when expect cannot be used:
// - Conditional compilation (cfg) affects whether lint fires
// - Inside macros with variable expansion
// - Architecture-specific code
#[allow(dead_code)]
fn arch_specific_helper() {}

// Conditional expect for cfg-gated items
#[cfg_attr(not(CONFIG_FEATURE_X), expect(dead_code))]
fn feature_x_helper() {}

Documentation

Rust kernel code is documented with rustdoc (Markdown), not kernel-doc:
/// Returns the number of active connections.
///
/// Returns `0` if the device has not been initialized.
///
/// # Examples
///
/// ```
/// let dev = MyDevice::new()?;
/// assert_eq!(dev.active_connections(), 0);
/// ```
pub fn active_connections(&self) -> usize {
    self.inner.lock().connections.len()
}
Use /// for public-facing documentation (rendered by rustdoc) and // for implementation notes. Safety preconditions go under # Safety. Potential panics go under # Panics. Avoid panics — return Result instead.
Browse the generated API documentation at https://rust.docs.kernel.org or generate it locally with make LLVM=1 rustdoc. The docs include the full kernel crate API with search and cross-links.

Build docs developers (and LLMs) love