Linux kernel contributor path
move · O overview · F fit
01 / 00

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.

the tree topologythe release cyclethe dev loop patch anatomythe review gauntletyour first patch

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-by is 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.

you — a patch in your local tree
↓ git send-email
mailing list + maintainer (review)
↓ applied
subsystem maintainer tree (e.g. bpf-next)
↓ integration
linux-next (tested nightly)
↓ merge window
Linus's mainline → release

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.

explore

Click a tree

your branch → list → maintainer tree → linux-next → mainline → stable
Click any node above to learn what it is and how a patch moves through it.

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.

drive it

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) or net-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 -next before 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).

set up trees

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.

configure + build

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.

live

The inner loop with virtme-ng

edit
change code
a few lines in a .c file
build
make -j
incremental · seconds with ccache
boot
vng
QEMU + your FS, no image, <5s
test
run selftest
e.g. tools/testing/selftests/bpf
read
dmesg / ftrace
printk, KASAN splats, traces

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.

explore

[PATCH bpf-next v2 2/3]

Click a line above to learn what it does and why it's there.

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.

❌ one patch: refactor + new feature + fix a typo
split into
1/3 — refactor (no behavior change)
2/3 — add the feature on top
3/3 — add the selftest

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, and sparse (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.

pre-flight

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 prep starts a tracked series off a base; b4 send builds and mails it, correctly threaded.
  • b4 trailers -u pulls reviewers' Reviewed-by tags 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.

the modern path

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.

drive it

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 the net tree, accepted throughout the cycle.
  • [PATCH net-next] — a feature for the next release. Goes to net-nextclosed 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.

Cc the maintainers from get_maintainer.pl
+
To: the subsystem list (bpf@ / netdev@)
+
Subject prefix names the tree: [PATCH bpf-next]
+
timed for when that tree is open
=
routed, tracked, reviewable

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-by carry 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.

drive it

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 selfteststools/testing/selftests/bpf, tree bpf-next, list bpf@vger.kernel.org. Add/fix a test: valued, low-risk, and the intent is clear.
  • Networking — tree net-next, list netdev@vger.kernel.org. TCP/connect/socket-state knowledge maps straight onto net/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.

a NetWatch attribution gap
↓ leads you to
read net/ipv4, net/ipv6, the socket lifecycle
↓ surfaces
a missing tracepoint · a selftest gap · a doc error
↓ becomes
your first upstream patch

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.

Jump to a stop — click any, or press O to close