From "I write kernel-adjacent code" to "I have patches in mainline"
Contributing to the
Linux kernel.
If you can write systems code, the gap to upstream kernel work usually isn't the C — it's the culture and the machinery: email patches, maintainer trees, the verifier of the social world. This deck builds that machinery from first principles, then points at a forgiving first patch in a friendly corner: BPF and the networking stack.
Five acts · ~21 stops · interactive: explore the trees, scrub a release, decode a patch email, survive review, follow a patch to mainline.
The first thing to unlearn
It's not a repo with a merge button — it's a federation of trees and people
There is no "fork & open PR." The kernel runs on a model that predates and outlives GitHub, and the sooner it clicks, the sooner everything else makes sense.
- Patches are emails. You mail a diff, in plain text, to a mailing list and the right maintainers. Review happens by reply.
- Maintainers, not merges. Each subsystem has a maintainer who curates a tree. They apply your patch to their tree; it flows upward from there.
- Trust is earned in public. Your
Signed-off-byis a legal-ish certification (the DCO). Your track record is the sum of your accepted patches.
The upside: it's radically decentralized, scriptable, and durable. The cost: a workflow you have to learn deliberately — which is the rest of this deck.
No step here has a button. Every arrow is a person, a script, or a convention. We'll walk each.
Where code actually lives
Many trees, one direction of flow — upward to Linus
"The kernel source" isn't one repo you push to. It's a fan-in of maintainer trees that integrate in linux-next and merge into mainline. Click each to see its job — and where you fit.
Click a tree
The mental shortcut: fixes for the current release go to the */fixes
or net tree; features for the next release go to the *-next
tree. Picking the wrong one is the most common rookie miss.
~9–10 weeks, on repeat, forever
When your patch can land depends on where you are in the cycle
The kernel ships on a metronome: a two-week merge
window, then weekly release candidates (rc1…rc7, sometimes rc8),
then a release — and the next window opens. Scrub it to see what's accepted when.
One release cycle
For netdev specifically: net-next closes during the merge window —
feature submissions are turned away until it reopens after rc1. Knowing the date saves
you a bounced series.
Clone the right tree, not just "the kernel"
Work against the subsystem tree you'll submit to
Cloning Linus's mainline is fine for reading, but you develop against the maintainer tree your patch will target — so you're building on what they'll actually apply to.
- Good first targets:
bpf-next(BPF features) ornet-next(networking) — small, well-bounded, friendly to newcomers. - Add it as a remote alongside mainline; develop on a topic branch named for the change.
- Rebase onto the latest
-nextbefore sending, so it applies cleanly.
The MAINTAINERS file lists every subsystem's tree URL,
lists, and maintainers — scripts/get_maintainer.pl reads it for you (slide 14).
The config is half the battle
Build a kernel shaped like the one you run
A full defconfig builds thousands of modules you'll never load.
Start from your running kernel and trim — builds go from "make a coffee" to "make a sip."
make localmodconfig— keep only what's loaded right now. Massively smaller.ccache+make -j$(nproc)— cache and parallelize; incremental rebuilds become seconds.- Turn on the debuggers you'll want:
KASAN,KCSAN,lockdep,DEBUG_INFO_BTF(for BPF CO-RE).
First build is slow; every build after is incremental. Optimize the loop, not the first run.
Iteration speed is the whole game
Edit → build → boot in seconds, not minutes
The single biggest productivity lever in kernel work is how fast you can
run a changed kernel. Don't reboot real hardware. Boot your freshly-built kernel in a VM
against your existing filesystem — virtme-ng makes it one command.
The inner loop with virtme-ng
vng -b builds, vng boots it instantly against
your real root. Keep a physical box only for what genuinely needs real hardware.
You can't attach a debugger to a panic — so instrument
The kernel's own toolbox, from printk to sanitizers
printk / dynamic debug
The workhorse. pr_info(),
and pr_debug() you can flip on per-file/line at runtime via
/sys/kernel/debug/dynamic_debug.
ftrace / trace events
Function and event tracing with near-zero overhead.
trace-cmd and the tracefs interface — see what the kernel
is doing without recompiling.
KASAN / KCSAN / UBSAN
Compile-in sanitizers that catch use-after-free, data races, and undefined behavior with a precise splat. The cheapest bug you'll ever fix is the one KASAN finds.
lockdep
Runtime lock-ordering validator. Turn it on and it screams the moment your change introduces a deadlock-shaped pattern — before it ever deadlocks.
gdb + QEMU
Boot under QEMU with -s and attach gdb with the
kernel's vmlinux-gdb.py scripts for real source-level stepping.
The test robots
The kernel test robot (Intel LKP) and syzbot will build, boot, and fuzz your tree — and email you regressions. Free CI you don't run.
Rule of thumb: reproduce in a VM with sanitizers on, narrow with ftrace, confirm with a printk. A clean KASAN run is worth a paragraph of "looks correct to me."
A patch is a very particular email
Every line is load-bearing — click to decode
This is what you actually send. The format isn't bureaucracy: maintainers apply hundreds of these with tooling, so structure is the interface. Click any region.
[PATCH bpf-next v2 2/3]
The single most-missed detail: the changelog goes below the first ---
(the "scissors") so it documents the review for humans but never enters the permanent git history.
The cardinal rule of patch craft
One patch does one reviewable thing
A patch that does three things is three times as hard to review and impossible to revert cleanly. Split work into a series where each patch stands on its own and builds.
- Each patch compiles & passes tests on its own — bisection must never land on a broken commit.
- Refactor, then change. A "move code" patch and a "change behavior" patch are separate, so the reviewer sees the real diff, not noise.
- A cover letter (
[PATCH 0/N]) explains the series as a whole; each patch explains itself.
"Why," not "what." The diff shows what changed; the commit message must explain the reasoning a reviewer can't infer.
Reviewers reward a clean series with fast review. A 600-line do-everything patch sits unread.
Pass these before a human ever looks
The checks that are non-negotiable and automatable
A class of feedback you can avoid entirely by running the tools first. Maintainers have zero patience for issues a script would have caught.
checkpatch.pl --strict— coding style, common mistakes. Clean it before sending.get_maintainer.pl— the exact people and lists to address. Don't guess.- Clean build with
W=1, andsparse(make C=1) for the subtle type bugs. - Run the relevant selftests — and add one for your change.
CI bots (kernel test robot, BPF CI) run more — but failing the basics in public is an avoidable bad first impression.
git send-email, minus the footguns
Use b4 — it modernizes the whole flow
The mechanics that trip everyone up — threading, versioning, To/Cc from MAINTAINERS, trailers
collected from replies — are exactly what b4 automates.
b4 prepstarts a tracked series off a base;b4 sendbuilds and mails it, correctly threaded.b4 trailers -upulls reviewers'Reviewed-bytags back into your commits for v2.- Send a test patch to yourself first. Confirm it applies with
git am.
Hard rules: plain text only, no HTML mail, no line-wrapping the diff. A mangled patch is dead on arrival.
What gets a patch flamed — and how to dodge it
Toggle the mistakes, then run review
Maintainer review is the social verifier. Toggle on the classic rookie mistakes below and run the gauntlet to see the reply each earns — then turn them all off for the clean path.
maintainer review of your series
None of these are about intelligence — they're about doing the reading first. A clean
submission gets a Reviewed-by; a sloppy one gets a lecture and a closed thread.
Address it right or it bounces
The right list, the right tree, the right moment
get_maintainer.pl tells you the people; you still choose the
tree and the timing. For networking this is sharp:
[PATCH net]— a fix for the current release. Goes to thenettree, accepted throughout the cycle.[PATCH net-next]— a feature for the next release. Goes tonet-next— closed during the merge window.- Put the target in the subject prefix so the maintainer (and patchwork) routes it correctly.
BPF mirrors this: bpf (fixes) vs bpf-next
(features), list bpf@vger.kernel.org, tracked in its own patchwork.
Always Cc the lists and people, never BCC. The public archive (lore) is the system of record.
v1 is the start of the conversation, not the end
Respond, revise, resend — and don't take it personally
Almost nothing lands at v1. Review is terse, sometimes blunt, occasionally silent. The skill that separates contributors from quitters is persistence with grace.
- Reply inline, point by point. Agree and fix, or explain why not — politely, with reasoning.
- Send a v2 as a new thread, with a changelog under the
---and a link to v1 on lore. - Collect trailers:
Reviewed-by,Acked-by,Tested-bycarry forward into each version. - Silence ≠ no. A polite ping after ~1–2 weeks is expected, not rude.
Trailers you'll see & earn
Signed-off-by (you, required) · Reviewed-by /
Acked-by (from reviewers/maintainers) · Tested-by ·
Fixes: (the commit you fix) · Reported-by.
Bluntness is not hostility
The culture optimizes for the code, in public, at scale. "This is wrong because X" is about the patch, not you. Fix X, resend, move on — and your standing compounds with every accepted patch.
From your terminal to a tagged release
The whole journey, one patch
Everything assembled: a single selftest patch, from
git send-email to shipping in a numbered kernel. Step through where it lives at each
stage — the topology and the cycle, in motion.
selftests/bpf: add a test for foo()
Median time from send to mainline for a clean patch: a few weeks to a couple of months, gated by the cycle. Your name is now in the git log of every Linux machine on earth.
You don't have to invent a feature
The best first patches are real bugs someone already found
syzbot real bugs
A continuous fuzzer that files kernel bugs with C reproducers. Pick an open one in BPF/net, reproduce it in your VM, fix it. High credibility — it's a real bug, not a cosmetic cleanup.
BPF selftests friendly start
Gaps in
tools/testing/selftests/bpf are valued and low-risk — and if you know what a BPF
program should do, you can tell what a test should assert. A gentle on-ramp.
Documentation & comments
Wrong or missing kernel docs are real patches that teach you the workflow on low stakes. A correct doc fix is a real contribution.
The kernel test robot
It reports build/regression failures publicly. Fixing one is a self-contained, well-specified task with a built-in reproducer.
Coccinelle / treewide
Semantic-patch cleanups (e.g. converting a deprecated API) across a subsystem — mechanical, reviewable, and they teach you the codebase's patterns.
Your own itch eventually
A feature a tool you use actually needs — like NetWatch's eBPF Phase-2 hooks (Act V). Scratching a real need is the most durable motivation.
Start where the bug is already proven and the scope is bounded. Your first patch should be small and correct, not clever.
Skip the staging-janitor route — go where you have context
Your fastest on-ramp is a subsystem you already understand
Most "first patch" advice points at drivers/staging cleanups. You can
usually do better: start where your existing knowledge is leverage. If you've touched eBPF
or the network stack, that corner is unusually welcoming:
- BPF selftests —
tools/testing/selftests/bpf, treebpf-next, listbpf@vger.kernel.org. Add/fix a test: valued, low-risk, and the intent is clear. - Networking — tree
net-next, listnetdev@vger.kernel.org. TCP/connect/socket-state knowledge maps straight ontonet/ipv4/…. - A syzbot bug in BPF/net — a real, reproducible bug is the highest-credibility first patch.
The first-patch loop
1 · lurk the list for a couple
weeks 2 · pick a selftest gap or open syzbot bug 3 · fix in a virtme-ng VM 4 · checkpatch +
get_maintainer 5 · b4 send 6 · iterate to v2/v3.
Two paths, shared start
Craft / IC signal → stick with one subsystem, build a maintainer relationship. Deepen a tool you maintain → read for fluency, patches are a byproduct. The first month is identical either way.
Start small and correct, not clever — and let the list teach you the norms before you post.
A feature you need is a kernel-onboarding project
The hooks a real tool needs pull you into the source
You don't need a contrived starter task — a real tool will hand you one. Take NetWatch as a worked example: its eBPF Phase 2 wants broader attribution, and every hook it adds means reading the exact kernel networking source a netdev contributor reads.
tcp_v6_connect— IPv6 attribution, the natural sibling of the Phase-1 kprobe.- the UDP send path — so QUIC (which NetWatch already decrypts) gets per-process attribution.
inet_sock_set_state— catch short-lived flows the connect-entry probe misses.
Each is the same question this deck answered for patches, asked of the kernel: which hook sees the event, what's populated when it fires, what's the stable key.
The #38 timing bug from the eBPF deep-dive is exactly this kind of question. Answer enough of them and you’re a kernel contributor — without ever forcing it.
Read the kernel’s conversation before you join it
The handful of sources that actually matter
LWN.net subscribe
The single best source for what’s happening in the kernel and why. Not optional if you’re serious. Weekly edition + deep technical articles.
lore.kernel.org
The mailing-list archive and system of record. Read netdev/bpf for a few weeks to absorb the norms before posting. Every patch and reply, forever.
docs.kernel.org
The in-tree docs are canonical: process/submitting-patches,
coding-style, submit-checklist. Read these once, properly.
kernelnewbies.org
The "first patch" tutorial and FAQ — the gentlest on-ramp to the mechanics, aimed exactly at where you are now.
LFX Mentorship
The Linux Kernel Mentorship Program — structured, mentored cohorts with a maintainer’s attention. Worth it for a guided ramp.
One book, for the model
Robert Love, Linux Kernel Development — dated (pre-6.x) but the cleanest mental model of scheduler/memory/locking. Read for the model; LWN for what’s current.
Tools to install today: b4, virtme-ng,
ccache, and a working git send-email against your mail.
The whole map, once more
From "no PR button" to a patch in mainline
The model Act I
A federation of maintainer trees flowing
upward through linux-next to Linus, on a ~9–10 week metronome. Features to -next,
fixes to the release.
Your machine Act II
The right tree, a trimmed config, and a sub-5-second edit→build→boot loop with virtme-ng. Sanitizers on; instrument, don’t guess.
The patch Act III
One logical change, a precise email,
the mechanical gates, b4 send — and surviving the review gauntlet by doing the
reading first.
Landing it Act IV
Right list, right tree, right moment; iterate to v2/v3 with grace; follow it through -next and the merge window to a tagged release.
Your path Act V
BPF selftests and netdev — a welcoming corner. A real tool's feature gap (like NetWatch's Phase-2 hooks) is a real on-ramp, not a contrived exercise.
Next step
Install b4 + virtme-ng,
lurk bpf@vger for a couple of weeks, then pick a selftest gap or an open syzbot bug.
Press O for the full map · ← to revisit any simulation. The C was never the hard part — now the machinery isn’t either.