Three Kernel Engineering Breakthroughs in Linux rbtree.h
1. Intrusive Embedding & container_of(ptr, type, member)
In C++ std::map or Java TreeMap, inserting an object allocates a heap wrapper containing left, right, parent, color, and a pointer/copy of the payload. In Linux, struct rb_node contains only three unsigned long words (24 bytes): __rb_parent_color, rb_right, and rb_left. It is embedded directly inside the domain struct (and a single domain struct can embed multiple rb_node fields to participate in multiple trees simultaneously with zero heap allocations!). Given a struct rb_node *node, the macro rb_entry(node, struct task_struct, run_node) subtracts the compile-time offsetof(struct task_struct, run_node) in zero CPU instructions (folded into the base-displacement addressing mode mov rax, [rdi - 0x48])!
2. rb_root_cached: $O(1)$ rb_first() for the CFS vruntime Scheduler
The Linux Completely Fair Scheduler (CFS, introduced in 2.6.23 by Ingo Molnar) orders runnable tasks by their virtual runtime (vruntime in nanoseconds). On every context switch (thousands of times per second per core), CFS must pick the task with the smallest vruntime. Walking down the left spine in $O(\log N)$ on every context switch would waste precious cycles. Instead, Linux uses struct rb_root_cached, which stores rb_root alongside a cached pointer rb_leftmost. Whenever a new node is inserted and never branches right on its way down, rb_leftmost is updated in $O(1)$—making pick_next_task_fair() a single $O(1)$ pointer read!
3. Augmented Interval Gap Tree (rb_subtree_gap) in vm_area_struct
When a process calls mmap() to allocate an unmapped virtual address region of length $L$, the kernel used to scan the linked list of Virtual Memory Areas (VMAs) in $O(N)$ time. Since Linux 3.6, struct vm_area_struct embeds an Augmented Red-Black Tree (rbtree_augmented.h) where each VMA node maintains rb_subtree_gap: the maximum contiguous free virtual address gap anywhere in its subtree, computed via custom rotation callbacks (RB_DECLARE_CALLBACKS_MAX) to find a suitable address hole in $O(\log N)$ time! *(Note: In Linux 6.1+, VMA tracking has also introduced the RCU-safe B-tree-like Maple Tree alongside rbtree_augmented used in DRM/interval-tree subsystems.)*
| Feature / Metric | Standard Non-Intrusive RB-Tree (std::map) |
Linux Kernel Intrusive rb_root_cached |
|---|---|---|
| Dynamic Heap Allocations on Insert | 1 operator new call per insert |
0 Allocations (Pre-embedded in struct!) |
| Color Storage Overhead | 4–8 bytes (due to struct alignment padding) | 0 Bytes (Bit 0 of __rb_parent_color) |
Minimum Element Lookup (rb_first) |
O(log N) left-spine walk | O(1) via rb_root_cached.rb_leftmost |
| Cache Lines Touched per Comparison | 2 (Tree node wrapper $\to$ payload pointer) | 1 (rb_node sits in same cache line as key!) |
Bit-Level Anatomy of __rb_parent_color & container_of
Because struct rb_node contains unsigned long fields, the C ABI aligns every struct rb_node to at least a 4-byte or 8-byte boundary. Therefore, the least significant 2 bits of any struct rb_node * address are guaranteed to be 00:
$$\text{rb\_parent}(r) = (\text{struct rb\_node }*)\big(r\to\text{\_\_rb\_parent\_color} \;\&\; \sim 3\text{UL}\big)$$
$$\text{rb\_color}(r) = r\to\text{\_\_rb\_parent\_color} \;\&\; 1\text{UL} \quad (0 = \text{RB\_RED}, \; 1 = \text{RB\_BLACK})$$
And given a pointer ptr to member member inside type, container_of recovers the outer struct pointer via standard byte-pointer arithmetic:
$$\text{container\_of}(\text{ptr}, \text{type}, \text{member}) = (\text{type }*)\big((\text{char }*)\text{ptr} - \text{offsetof}(\text{type}, \text{member})\big)$$
Explore the Augmentation Theorem: maintain subtree maxEnd = max(hi, L.maxEnd, R.maxEnd) for O(log N) interval overlap search and Linux VMA gap allocation, or subtree size sz for O(log N) select(k) / rank(x).
Invariant & State
Hardware Reality: Co-Locating rb_node and vruntime in the Same 64-Byte Cache Line
Look closely at struct sched_entity in include/linux/sched.h: the kernel developers deliberately placed struct rb_node run_node (24 bytes) immediately adjacent to u64 vruntime (8 bytes) inside the first 64-byte cache line of sched_entity. When the CPU traverses node = node->rb_left and loads the cache line containing run_node, the comparison key vruntime is already sitting in that exact same 64-byte L1 cache line! A non-intrusive tree would suffer two dependent cache misses per level (one to load the tree node, a second to chase the pointer to the sched_entity).
Implementation: Linux-Style Intrusive Augmented RB-Node & container_of
// C11 / C++20: Linux Kernel-Style Intrusive RB-Node with Tagged Parent-Color & container_of
#include <cstddef>
#include <cstdint>
struct alignas(8) rb_node {
uintptr_t __rb_parent_color; // Bit 0: 0 = RED, 1 = BLACK; Bits [63:2] = parent pointer
rb_node* rb_right = nullptr;
rb_node* rb_left = nullptr;
rb_node* parent() const noexcept {
return reinterpret_cast<rb_node*>(__rb_parent_color & ~uintptr_t(3));
}
bool is_black() const noexcept { return (__rb_parent_color & 1U) != 0; }
void set_parent_and_color(rb_node* p, int color) noexcept {
__rb_parent_color = reinterpret_cast<uintptr_t>(p) | (color & 1U);
}
};
struct rb_root_cached {
rb_node* rb_node_root = nullptr;
rb_node* rb_leftmost = nullptr; // O(1) access to minimum element
};
#define rb_entry(ptr, type, member) reinterpret_cast<type*>(reinterpret_cast<char*>(ptr) - offsetof(type, member))
// Example Domain Object: Runnable Task Entity with Zero-Allocation Intrusive Node
struct TaskEntity {
uint64_t pid;
rb_node run_node; // Co-located in the same cache line as vruntime!
uint64_t vruntime;
};
inline TaskEntity* pickNextTaskO1(const rb_root_cached& rq) noexcept {
if (!rq.rb_leftmost) return nullptr;
return rb_entry(rq.rb_leftmost, TaskEntity, run_node);
}
Failure Modes & Adversarial Pitfalls
- Inserting an Already-Linked Intrusive
rb_nodeInto a Second Tree Instance: Because an intrusiverb_nodelives inside the object itself, inserting an object into Tree B while it is still linked inside Tree A overwrites itsrb_left/rb_right/__rb_parent_colorpointers in place, instantly corrupting Tree A! If an object must belong to two trees simultaneously, it must embed two distinctrb_nodefields (e.g.,rb_node by_vruntime;andrb_node by_pid;). - Misaligned
rb_nodeCorrupting the Color Bit: If a struct containingrb_nodeis packed with__attribute__((packed))so that&nodesits at an odd byte address, bit 0 of the pointer is1, colliding withRB_BLACK! Always enforcealignas(sizeof(long))onstruct rb_node.