Tech News
← Back to articles

Rust in the Linux kernel: part 2

read original related products more articles

How to write Rust in the kernel: part 2 [LWN subscriber-only content]

In 2023, Fujita Tomonori wrote a Rust version of the existing driver for the Asix AX88796B embedded Ethernet controller. At slightly more than 100 lines, it's about as simple as a driver can be, and therefore is a useful touchstone for the differences between writing Rust and C in the kernel. Looking at the Rust syntax, types, and APIs used by the driver and contrasting them with the C version will help illustrate those differences.

Readers who are already conversant with Rust may find this article retreads some basics, but it is my hope that it can still serve as a useful reference for implementing simple drivers in Rust. The C version and the Rust version of the AX88796B driver are remarkably similar, but there are still some important differences that could trip up a developer performing a naive rewrite from one to the other.

The setup

The least-different thing between the two versions is the legalities. The Rust driver starts with an SPDX comment asserting that the file is covered by the GPL, as many files in the kernel do. Below that is a documentation comment:

//! Rust Asix PHYs driver //! //! C version of this driver: [`drivers/net/phy/ax88796b.c`](./ax88796b.c)

As mentioned in the previous article, comments starting with //! contain documentation that applies to the entire file. The next few lines are a use statement, the Rust analogue of #include :

use kernel::{ c_str, net::phy::{self, reg::C22, DeviceId, Driver}, prelude::*, uapi, };

Like C, Rust modules are located starting from a search path and then continuing down a directory tree. Unlike C, a use statement can selectively import only some items defined in a module. For example, DeviceId is not a separate module, but rather a specific item inside the kernel::net::phy module. By importing both kernel::net::phy::DeviceId and kernel::net::phy as a whole, the Rust module can refer to DeviceId directly, and anything else from the PHY module as phy::name . These items can always be referred to by their full paths; a use statement just introduces a shorter local alias. If a name would be ambiguous, the compiler will complain.

All of these imported items come from the kernel crate (Rust library), which contains the bindings between the main kernel and Rust code. In a user-space Rust project, a program would usually also have some imports from std , Rust's standard library, but that isn't possible in the kernel, since the kernel needs more precise control over allocation and other details that the standard library abstracts away. Kernel C developers can't use functions from libc in the kernel for much the same reason. The kernel::prelude module contains kernel replacements for many common standard-library functions; the remainder can be found in core , the subset of std that doesn't allocate.

... continue reading