Developing a low-level NVMe driver for microbenchmarks
- Typ der Arbeit: Bachelorarbeit
- Status der Arbeit: reserviert
- Projekte: ParPerOS
- Betreuer: Kenny Albes, Daniel Lohmann
[Generated with AI]
Context
In recent years SSDs have evolved into highly optimized hardware capable of delivering bandwidths up to 14 GB/s. Modern SSD controllers are based on full-fledged ARM cores that perform complex tasks such as advanced bookkeeping, caching, wear leveling and health monitoring. However, their inner workings and performance characteristics are largely hidden from application and driver developers. Aside from unspecific best-case performance metrics in the marketing materials, SSDs only offer scarce performance hints via their NVMe interface. This makes it increasingly harder to take full advantage of the growing bandwidth of modern SSDs. Even for a simple sequential read our initial measurements showed large differences (~1.5x) in throughput for varying command submission scenarios.
Problem
To precisely analyze the micro-architectural behavior of modern NVMe SSDs a specialized setup is required.
The operating systems runtime overhead has to be reduced to minimum, eliminating syscall and scheduling overhead during latency benchmarks.
Furthermore, fine-grained control of the device is required.
Ideally, the benchmarks would directly interface with the SSD controller, which requires a suitable open source driver.
While some open source NVMe drivers exist, they are either to complex (DPDK4), do not expose the SSD controller directly (io_uring
5, libnvme6), or are incomplete and hard to extend (unvme7).
Goal
To enable precise measurements in the future, the goal of this thesis is to develop a low-level NVMe driver for the Linux operating system. By utilizing VFIO2 and iommufd3, the driver shall be implemented in userspace, bypassing the kernel completely. Besides exposing full control over the device to the benchmark application, the codebase should be kept small and easy to extend. Configuring the controller and implementing new features should be easy. Since the driver runs as a “normal” application, it does not need to be implemented in C. C++ or even Rust may also be possible.
The NVMe standard1 contains a large set of optional features and commands a controller may support. It is therefore not required to implement a complete driver in this thesis. However, the implementation must at least contain:
- Helpers for controller initialization and configuration
- Relevant fields in the
id-ctrl
command structure. For example:- Max data transfer size
- Queue depth
- Checking for support of optional commands
- Queue abstraction
- Building PRP lists
- Polling (non-blocking)
- Helpers for data buffer allocation
- Commands:
- Sane helpers/abstractions for construction of different commands
- Relevant admin commands for setup
- Read
- Write
- Helpers for reading/writing chunks spanning multiple commands
- Tests for implemented features
There are also plenty optional features for very motivated students:
- Event log (LPA)
- Persistent Event log (PELS)
- Actual relative performance in different power state (PSD0-31)
- Interrupt based queues
- Commands:
- Flush for volatile write cache
- Setting different host page size
- Endurance groups (CTRATT)
- SMART/Health log page per namespace (SMARTS in LPA)
- Compare
To dampen the learning curve, students should first try to control a SSD using an existing driver implementation before diving into the actual implementation.
Single commands (such as identify
) can also be sent using the nvme-cli
8 utility without writing any code.
Topics: NVMe, VFIO, iommufd, Linux, Driver, C/C++/Rust