Conversation
The main.rs crate that holds the ports is built with the kernel's rustc_cfg and checks the cfgs it uses. A port of lib/plist.c reads CONFIG_DEBUG_PLIST, which changes plist_check_head() and adds a boot- time test, so every use is an unexpected_cfgs warning until the cfg is declared. Add it next to the others. A cfg that is declared and never tested is not a warning, so nothing changes until the port lands. Assisted-by: LLM [Claude Code] Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Port the three functions lib/plist.c defines: plist_add(), plist_del() and plist_requeue(). lib/plist.rs implements them with the declarations of include/linux/plist.h, and CONFIG_RUST_KERNEL builds it instead of lib/plist.c. The header and its inlines do not change. lib/plist.c has no EXPORT_SYMBOL, so lib/plist_ffi.c has no export lines, and vmlinux.symvers of a CONFIG_MODULES=y build is the same for both, sorted. plist.o stays in lib-y. The licence is the C file's, GPL-2.0-or- later. plist_head and plist_node sit on struct list_head. The port mirrors the three, whose layout is the same in every configuration, with assertions for 64 bits (BTF) and 32 bits (DWARF of clang for i386). The three operations of <linux/list.h> that change a list, list_add(), list_add_tail() and list_del_init(), stay in C behind single-call wrappers, so CONFIG_LIST_HARDENED and CONFIG_DEBUG_LIST keep their checks. The reads are Relaxed loads of atomic fields, which is what a plain C read of a list that a lock protects is, so plist_add() and plist_del() make one call into C for each list change and none for a read. BUG_ON() is a wrapper that takes the condition, since objtool cannot know that an extern function does not return. CONFIG_DEBUG_PLIST is kept, as a cfg: the list walk of plist_check_head() and the boot-time plist_test(), which is in Rust with its module_init() in the ffi file. The boot test differs from the C in that its 241 nodes are not __initdata, and a WARN or BUG reports the wrapper's file and line. The differential test in linux-rust found a bug in plist_requeue(), which the port keeps. After plist_del() it follows the prio_list to the next priority's first node, but when the node's group is the last, the prio_list wraps to the first node of the head, and the node is inserted in front of higher priorities: priorities 2, 3, 3, requeueing the first 3, gives 3, 2, 3. It takes the first node of the last group, a group of two or more, and two or more groups. mm/swapfile.c is the one caller. plist_test() does not reach it, since it requeues only the node it has just added. A fix is a change of behaviour to propose against lib/plist.c, not part of a port. Both CONFIG_RUST_KERNEL=n and =y build, the =y build with CLIPPY=1 without a warning in lib/plist.rs and without an objtool warning, and a kernel of each with CONFIG_DEBUG_PLIST=y boots and runs plist_test() without a BUG. No typed API is added: that waits for one over list_head. unsafe budget: U1 13, U3 30. Assisted-by: LLM [Claude Code] Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
bherrera
force-pushed
the
claude/sonnet-5/feature/plist
branch
from
September 21, 2026 19:17
dee0a2c to
99fe08f
Compare
bherrera
changed the base branch from
claude/sonnet-5/feature/hlist
to
claude/sonnet-5/feature/sort
September 21, 2026 19:17
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
plist: Rust implementation of
lib/plist.cStacked on #31 (
hlist); retarget tolinux-rustwhen its parent lands. Companion harness PR: misttech/linux-rust#53.Replaces
lib/plist.cin place:plist_add(),plist_del()andplist_requeue()with the same names and signatures.lib/plist.chas noEXPORT_SYMBOL, solib/plist_ffi.chas no export lines, andvmlinux.symversof aCONFIG_MODULES=ybuild is identical for both (sorted, 6,315 lines).include/does not change.plist.ostays inlib-y.Commits:
rust: check-cfg CONFIG_DEBUG_PLIST for the portslib: plist: add the Rust implementationThe three
<linux/list.h>operations that change a list stay in C behind single-call wrappers, soCONFIG_LIST_HARDENEDandCONFIG_DEBUG_LISTkeep their checks; reads areRelaxedloads.CONFIG_DEBUG_PLISTis kept, including the boot-timeplist_test(), which is in Rust with itsmodule_init()in the ffi file.A bug in
plist_requeue()in the C, kept by the portThe differential test found that the C reference alone fails the sorted-order invariant. After
plist_del(), the shortcut followsiter->prio_list.next, which wraps to the first node of the head when the node's group is the last, and the node is inserted in front of higher priorities. Priorities 2, 3, 3, requeueing the first 3, gives 3, 2, 3, and the plist is then corrupt (plist_first()is not a leader).plist_test()cannot reach it: it requeues only the node it has just added, which is last in its group (0 hits over 20,000 seeds).mm/swapfile.cis the one caller.swap_alloc_slow()requeues every device it walks, and default swap priorities are equal, so a higher-priority device plus two or more default ones can hit it. That is from reading the code and reproducing the operations; it has not been triggered on a kernel.lib/plist.cfirst. The harness skips that requeue in its runs, and compares it inmisuse(C, Rust and mixed must agree on the wrong order and memory); a mutant that fixes it fails.Decisions for you
lib/plist.rsandlib/plist_ffi.careGPL-2.0-or-later, asplist.cis: the Rust reproduces its header, authors and comments, and narrowing an "or later" grant needs the authors' consent.CLAUDE.mdsays all code isGPL-2.0-only. Proposed edit toCLAUDE.md, not made here: "GPL-2.0-only, or the licence of the C file it replaces". The other ports sayGPL-2.0where their C saysGPL-2.0-only; that is a separate sweep.plistafter a typedlist_head, which does not exist. 43unsafeblocks in 710 lines is the cost of a raw-pointer port: every read is a dereference. The typed layer waits forlist_head.Not done
Benchmarks (
plist_add()is on the futex wait path), arm64, anything at 32 bits, the hardened list checks in the harness, and the warning path ofCONFIG_DEBUG_PLIST(plist_check_head()runs on every operation of the boot test and finds nothing; nothing corrupts a list to make it warn). AWARN_ONorBUG()reportslib/plist_ffi.c, notlib/plist.c.Review
An independent
review-rust-portsaid CHANGES REQUIRED for the first version, on four points, all fixed: anexternfunction declared-> !made objtool reportplist_requeue() falls through to next function(nowc_plist_bug_on(bool), askfifodoes);plist.ohad moved fromlib-ytoobj-y; four FFI calls sat in blocks tagged U3 (each is now in a block of its own, U1); and a clippy warning withCONFIG_DEBUG_PLIST=y. Its semantic comparison found no difference fromplist.c.Kill criteria
loom: not applicable; no shared state, a caller's lock.unsafeat call sites: not applicable; no typed API.unsafebudget: U1 13, U3 30.Verification
Kernel
CONFIG_RUST_KERNEL=nand=ybuild, with clippy and without objtool warnings, also withCONFIG_DEBUG_PLIST=y; a C and a Rust kernel withCONFIG_DEBUG_PLIST=yboot and runplist_test()without aBUG. The harness (linux-rust) runs the C reference, the Rust and a per-operation mix on the same memory (1,889,568 edge sequences and 10 seeds x 100,000 operations), the misuse cases, layout at 64 and 32 bits, and 26 mutants (25 caught, the survivor changes only an ordering on a load).Assisted-by: LLM [Claude Code]
🤖 Generated with Claude Code