R0KernelPath
module 101 / 262

Finding Your First Contribution

Find your foothold in the kernel codebase, craft a real patch, and survive the review gauntlet — from first clone to first Signed-off-by.

~18h focused study12 concepts

Overview

The Linux kernel is the most consequential piece of software ever written: it runs on everything from smartwatches to supercomputers, and it is entirely built by volunteers submitting patches to public mailing lists. The first contribution is the hardest step — not because the technical bar is impossibly high, but because the process is unfamiliar. The kernel does not use GitHub pull requests, it does not have a friendly green "merge" button, and a maintainer may reply to your patch with terse criticism that feels brutal to a newcomer. This module is your guide through that unfamiliar terrain.

The goal here is not to ship a world-changing feature. The goal is to ship one real, accepted patch. That patch might be a coding-style fix, a documentation clarification, a staging driver cleanup, or a one-line bug fix. What matters is completing the full cycle: finding something real to fix, writing the patch correctly, running all the checkers, formatting the commit message to kernel standards, and sending it to the right mailing list. Once you have done that once, the second time is easy — you know the map.

This module walks through every stage: where to look for work, how to triage what is approachable, how the scripts/checkpatch.pl and sparse tools catch your mistakes before a maintainer does, how drivers/staging is specifically designed as an on-ramp, what syzbot and Bugzilla tell you about real bugs waiting for a fix, and how to read the feedback you get back without taking it personally. Along the way you will see the kernel from the contributor's vantage point — not as a monolithic wall but as a living, hierarchical tree maintained by real people who were all beginners once.

The Eudyptula Challenge — a famous private email-based kernel tutorial that ran for years — proved that a structured, incremental set of real tasks is the best way to learn. The challenge is no longer active, but its spirit lives on in the community and in this module. Every exercise here is modeled on that philosophy: do the real thing, not a simulation of it.

why this matters for the kernel

Understanding the contribution process is the bridge between "I have been studying the kernel" and "I am a kernel developer." The kernel community is intentionally high-signal: they care about code quality, patch series hygiene, and clear commit messages because the codebase must be maintained for decades. Learning to contribute is therefore learning to think like a professional systems programmer — precise, evidence-driven, respectful of your reader's time.

Concretely, a merged kernel patch is one of the strongest signals you can put on a resume for systems, embedded, or OS engineering roles. More importantly, the review culture forces you to deeply understand what you are changing: you cannot fake your way through a Greg Kroah-Hartman review. By the end of this module you will have the habits — running static analysis before sending, writing a commit message that explains the why, CCing the right maintainer list — that distinguish engineers who ship reliable systems software from those who merely write it.

Kernel Patch Lifecycle

rendering diagram…

From a local fix to a commit in the mainline kernel: every stage of the email-based patch workflow, including the revision loop triggered by reviewer feedback.

The concepts

01Anatomy of the Contribution Pipeline

Before writing a single line of code, understand the full lifecycle of a kernel patch. Unlike most open-source projects, Linux does not use a forge (GitHub/GitLab) as the source of truth. The canonical flow is: write a patch locally → run checkers → email the patch to the correct mailing list → a maintainer or other developers review it → you revise and resend if needed → the maintainer applies it to their tree → it propagates up to Linus's tree for a future merge window.

The key actors are:

  • You (contributor): write the patch, respond to review, revise
  • Subsystem maintainer: reviews patches for their subsystem, maintains a maintainer-tree on kernel.org
  • linux-kernel mailing list (LKML): the master list; CC'd on almost everything but not where most action happens
  • Subsystem mailing list: where the real review happens (e.g., linux-staging@lists.linux.dev, linux-mm@kvack.org)
  • Linus Torvalds: pulls from subsystem maintainers every ~8 weeks during the merge window

There is no 'pending review queue' UI — the entire workflow is email threads. Tools like b4, git send-email, and patchwork.kernel.org make this tractable.

# The conceptual flow (not a real command sequence — just a map)
# 1. Find something to fix
git clone https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git

# 2. Make your change on a branch
git checkout -b fix-staging-foo
# ... edit drivers/staging/foo/bar.c ...
git add -p
git commit -s   # -s adds your Signed-off-by line

# 3. Check your work
scripts/checkpatch.pl --strict -g HEAD

# 4. Find who to send it to
scripts/get_maintainer.pl drivers/staging/foo/bar.c

# 5. Format and send
git format-patch -1 HEAD
git send-email --to=<maintainer> --cc=<list> 0001-fix-staging-foo.patch

02drivers/staging: The Designed-for-Beginners On-Ramp

drivers/staging/ is a special directory in the kernel tree created explicitly as a holding area for drivers that are not yet ready for the mainline. Greg Kroah-Hartman (GKH), who maintains staging, designed it specifically as a place where newcomers can contribute real, impactful cleanup work.

Every staging driver has a TODO file that lists exactly what needs to be done to get the driver promoted out of staging and into the mainline. These TODOs range from easy ('Fix checkpatch warnings') to hard ('Add proper power management'). The easy ones are ideal first patches.

Common staging TODO categories:

  • checkpatch fixes: style issues flagged by scripts/checkpatch.pl
  • Lindent / whitespace cleanup: inconsistent indentation, trailing whitespace, spaces after function names
  • sparse / smatch warnings: semantic warnings from static analysis
  • Remove unnecessary casts: e.g., casting void * from kmalloc
  • Use kernel-idiomatic patterns: usleep_range instead of mdelay, devm_* functions for resource management
  • Fix up error paths: ensure resources are freed on all error returns

Important rule: one logical change per patch. Do not mix a checkpatch fix with a sparse warning fix in the same commit — send two separate patches in a series.

The staging mailing list is linux-staging@lists.linux.dev. GKH is very responsive to well-formatted patches and will often give explicit feedback on style even when the patch is correct.

# Example: a real kind of checkpatch fix you'd send
# BEFORE (triggers 'space prohibited before ...' and 'unnecessary cast')
dev = (struct foo_dev *) kzalloc (sizeof(*dev), GFP_KERNEL);
if (!dev)
    return -ENOMEM;

// AFTER
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
	return -ENOMEM;

# Check it:
$ scripts/checkpatch.pl --strict -g HEAD
total: 0 errors, 0 warnings, 0 checks, 5 lines checked

03checkpatch.pl: Your First Line of Defense

scripts/checkpatch.pl is a Perl script in the kernel tree that checks patches and source files for compliance with the kernel coding style (documented in Documentation/process/coding-style.rst). Running it before sending a patch is mandatory — a patch that fails checkpatch will almost always be rejected immediately.

The script checks for:

  • Indentation (tabs, not spaces; the kernel uses 8-wide tabs)
  • Line length (warn at >80, error on many linters at >100 for non-comments)
  • Brace style (K&R: opening brace on the same line as the statement, except for function definitions)
  • Space after keywords (if (, for (, while (), no space after function names
  • Blank lines and comment style
  • Correct SPDX-License-Identifier headers
  • Commit message format (Subject line length, Signed-off-by presence)

Usage patterns:

# Check a patch file
scripts/checkpatch.pl --strict 0001-my-fix.patch

# Check the last N commits as patches
scripts/checkpatch.pl --strict -g HEAD~3..HEAD

# Check a source file directly (not a patch)
scripts/checkpatch.pl --file drivers/staging/foo/bar.c

The --strict flag enables additional checks that are recommendations, not hard requirements, but GKH's staging tree enforces them. Get to zero errors AND zero warnings before sending.

Common mistakes beginners make:

  • Forgetting the blank line between the commit summary and the body
  • Writing if(condition) instead of if (condition)
  • Using // comments (C99 style — allowed in the kernel since ~2020, but older maintainers still prefer /* */ in C files)
  • Including unrelated style changes in a functional patch (make them separate commits)
/* A commit message that passes checkpatch format checks */

/*
 subject line: <subsystem>: <imperative verb>, under 72 chars

 Body explains WHY, not just WHAT. What was broken? What is the
 correct behavior? Reference any relevant specs or bug reports.

 Fixes: abcdef012345 ("previous commit that introduced the bug")
 Link: https://lore.kernel.org/...
 Reported-by: Some Reporter <reporter@example.com>
 Signed-off-by: Your Name <your@email.com>
*/

// Bad subject lines:
// "Fixed bug"
// "Various fixes to foo driver"
// "This patch changes the timeout value in the foo driver from 100 to 50"

// Good subject line:
// staging: greybus: replace deprecated kfree_skb() with dev_kfree_skb_any()

04get_maintainer.pl: Finding Who to Email

One of the most common beginner mistakes is sending a patch to the wrong list or forgetting to CC the right maintainer. scripts/get_maintainer.pl solves this automatically by parsing MAINTAINERS — a giant database file at the root of the tree that maps file paths and patterns to maintainer names, email addresses, and mailing lists.

Every entry in MAINTAINERS has fields like:

  • M: — maintainer email
  • R: — reviewer
  • L: — mailing list
  • S: — status (Maintained, Supported, Odd Fixes, Orphan, Obsolete)
  • F: — file pattern
  • T: — git tree

Run get_maintainer.pl on the file(s) you changed:

scripts/get_maintainer.pl drivers/staging/rtl8723bs/

The output gives you a ready-made --to and --cc list for git send-email. Always include everyone listed as (maintainer) or (list).

For staging drivers, the list is almost always linux-staging@lists.linux.dev and the maintainer is Greg Kroah-Hartman. For drivers outside staging, include the subsystem maintainer (e.g., linux-wireless@vger.kernel.org for wireless drivers) AND the driver's specific maintainer if one is listed.

Do NOT send to LKML only. The mailing list for your subsystem is where the action is.

$ scripts/get_maintainer.pl drivers/staging/media/atomisp/
Ana Rey <anarey@gmail.com> (maintainer:INTEL ATOMISP2 STAGING DRIVER)
Hans Verkuil <hverkuil@xs4all.nl> (maintainer:MEDIA INPUT INFRASTRUCTURE ...)
Mauro Carvalho Chehab <mchehab@kernel.org> (maintainer:MEDIA INPUT ...)
linux-staging@lists.linux.dev (open list:STAGING SUBSYSTEM)
linux-media@vger.kernel.org (open list:MEDIA INPUT INFRASTRUCTURE ...)
linux-kernel@vger.kernel.org (open list)

# Then in git send-email:
git send-email \
  --to='Ana Rey <anarey@gmail.com>' \
  --cc='Hans Verkuil <hverkuil@xs4all.nl>' \
  --cc='linux-staging@lists.linux.dev' \
  --cc='linux-media@vger.kernel.org' \
  0001-staging-atomisp-fix-foo.patch

05The Signed-off-by Line and the Developer Certificate of Origin

Every kernel patch must include a Signed-off-by: line with your real name and email. This is not optional, and it is not just formality. It means you have agreed to the Developer Certificate of Origin (DCO) — a legally meaningful statement that:

  1. You wrote the code yourself, OR
  2. You have the right to submit it under the license, AND
  3. You understand the contribution will be public and may be incorporated into the Linux kernel under the GPL

The DCO lives in Documentation/process/submitting-patches.rst. Read it once, carefully. Unlike a Contributor License Agreement (CLA), the DCO does not transfer copyright — you keep ownership, you just certify you have the right to contribute.

The git commit -s flag automatically appends Signed-off-by: Your Name <your@email.com> using your configured git identity. Make sure git config user.name and git config user.email are set to your real name and a real address you check — maintainers will email you there.

Other common trailer lines:

  • Reviewed-by: — a reviewer confirms they examined the patch
  • Tested-by: — someone tested it on real hardware
  • Reported-by: — credits the person who found the bug
  • Fixes: <sha1> ("commit message") — links to the commit that introduced the bug; this triggers the stable kernel backport process
  • Link: — URL to the mailing list discussion or bug report

All trailers go at the bottom of the commit message, after a blank line separator, in the correct order: Reported-by → Fixes → Link → Signed-off-by.

# Correct commit message structure with trailers

staging: wilc1000: remove redundant null check before kfree

kfree() already handles NULL pointers gracefully by doing nothing.
The explicit NULL check before calling kfree() is therefore dead code
and misleads the reader into thinking there is special-case behavior.

This pattern was flagged by the following coccinelle semantic patch:
  scripts/coccinelle/free/kfree.cocci

Fixes: 5c4efde12a00 ("wilc1000: add initial driver")
Signed-off-by: Ada Lovelace <ada@example.com>

06Syzbot: Automated Bug Reports Waiting for a Human Fix

syzbot (syzkaller bot) is Google's continuous fuzzing infrastructure for the Linux kernel. It runs syzkaller — a coverage-guided kernel fuzzer — 24/7 against the latest kernel trees, and when it finds a crash it automatically files a bug report at syzkaller.appspot.com and emails the relevant mailing lists.

Syzbot bug reports are a goldmine for contributors because:

  1. The bug is real and reproducible
  2. syzbot usually provides a C reproducer (a small program that triggers the crash)
  3. The crash log (KASAN report, UBSAN report, BUG_ON, etc.) points almost exactly to the problem
  4. The bug has not been fixed yet — it is waiting for someone like you

How to work a syzbot bug:

  1. Browse https://syzkaller.appspot.com/upstream for open bugs
  2. Filter by subsystem or driver you are familiar with
  3. Read the crash report — look for KASAN: use-after-free, general protection fault, WARNING: suspicious RCU usage, etc.
  4. Build a kernel with the required configs (syzbot shows you which .config fragments are needed)
  5. Run the provided C reproducer in a VM (QEMU) to confirm you can reproduce it
  6. Bisect if needed, then read the relevant code until you understand the bug
  7. Write and test a fix
  8. Reply to the syzbot thread on the mailing list with your patch, tagging Reported-by: syzbot+<hash>@syzkaller.appspotmail.com

KASAN (Kernel Address Sanitizer) and UBSAN (Undefined Behavior Sanitizer) are compile-time instrumentation passes that make latent memory bugs loud and obvious — enable them in your test kernel.

/* A toy example of the kind of use-after-free syzbot finds:
 * Object freed in one path, then accessed in another. */

// BAD: use-after-free
static void foo_remove(struct platform_device *pdev)
{
	struct foo_priv *priv = platform_get_drvdata(pdev);
	free_irq(priv->irq, priv);  // IRQ handler may still run!
	kfree(priv);                 // priv freed here
	// ... if IRQ fires now, handler reads freed priv -> UAF
}

// GOOD: disable IRQ before freeing
static void foo_remove(struct platform_device *pdev)
{
	struct foo_priv *priv = platform_get_drvdata(pdev);
	disable_irq(priv->irq);     // quiesce handler first
	free_irq(priv->irq, priv);  // now safe to free
	kfree(priv);
}

07Bugzilla and the kernel.org Bug Tracker

The Linux kernel has an official Bugzilla instance at bugzilla.kernel.org. Unlike syzbot which finds bugs automatically, Bugzilla contains bugs reported by end-users who hit real problems on real hardware. Many of these bugs have been open for a long time because no developer has stepped up to investigate them.

Bugzilla is a good source of first contributions because:

  • Bugs often come with detailed reproduction steps and dmesg output
  • Many involve driver issues that are isolated to one subsystem
  • The reporter is usually reachable for testing your fix

How to use Bugzilla as a contributor:

  1. Browse https://bugzilla.kernel.org → open bugs → filter by product/component
  2. Look for bugs that have reproduction steps but no 'patch submitted' comment
  3. Check whether the bug still exists on the latest linux-next (many old bugs are already fixed)
  4. If you find and fix it, mention the Bugzilla URL in your commit message: Link: https://bugzilla.kernel.org/show_bug.cgi?id=NNNNN
  5. After your patch is applied, add a comment to the bug pointing to the commit

Honest caveat: Bugzilla is less active than syzbot for the kernel community. Many maintainers prefer bug reports on their mailing list. But for device drivers and hardware-specific bugs, Bugzilla is still relevant and actively monitored by some subsystem maintainers.

08Reading the MAINTAINERS File and Tree Structure

The MAINTAINERS file is over 25,000 lines. You will never read it top to bottom — but you must understand its structure to navigate it.

Key status values that affect your contribution strategy:

  • Maintained: someone actively reviews patches, responds quickly
  • Supported: a company guarantees support; often fast reviews
  • Odd Fixes: occasionally maintained; expect slower responses
  • Orphan: no active maintainer — your patch may sit unapplied for months; consider CCing the relevant subsystem maintainer instead
  • Obsolete: abandoned hardware; patches may still be accepted for removal

For staging work specifically: check drivers/staging/<driver>/TODO. If it says 'Get rid of checkpatch warnings' and there are none left, the TODO is stale — the driver may be promotion-ready, which is a higher-impact (and harder) contribution.

The kernel tree structure you need to know:

  • drivers/ — device drivers (largest subdirectory)
  • arch/ — architecture-specific code (x86, arm, riscv, ...)
  • fs/ — file systems
  • mm/ — memory management
  • net/ — networking stack
  • kernel/ — core scheduler, locks, tracing
  • lib/ — generic kernel library routines
  • Documentation/ — documentation, process docs (process/submitting-patches.rst is your bible)
  • scripts/ — developer tools (checkpatch, get_maintainer, etc.)
  • tools/ — userspace tools that interact with the kernel (perf, bpftool, etc.)

For a first patch, stick to drivers/staging/. For a second patch, consider drivers/ or Documentation/.

# Quickly find orphaned staging drivers with TODOs
for dir in drivers/staging/*/; do
    if [ -f "$dir/TODO" ]; then
        # Find the maintainer status
        name=$(basename $dir)
        status=$(grep -A1 "STAGING - $name" MAINTAINERS 2>/dev/null | grep '^S:' | head -1)
        echo "$name: $status"
    fi
done

# Find all checkpatch warnings in a staging driver
scripts/checkpatch.pl --file --strict \
    drivers/staging/rtl8723bs/*.c | \
    grep -c 'WARNING\|ERROR'

09The Eudyptula Challenge Spirit: Incremental Real Tasks

The Eudyptula Challenge was a private, email-based kernel programming course created by Greg Kroah-Hartman (under a pseudonym) around 2014. It had 20 tasks, each building on the last, starting from 'compile hello world as a kernel module' and ending with 'submit a real patch to the kernel'. Tens of thousands enrolled; a few hundred finished. It is no longer accepting new submissions.

The spirit of the challenge — and its key lesson — was: learn by doing the real thing, not a simulation. Every task produced a real artifact (a module, a patch, a commit) using real tools (git, email, make). There were no multiple-choice questions.

The tasks taught in sequence:

  1. Write a kernel module that prints 'Hello World' to the kernel log
  2. Compile it for your running kernel, load/unload it
  3. Write a module that creates a /proc entry
  4. Add a character device
  5. Read/write through the device from userspace
  6. Learn about miscdevice and simplify
  7. Use debugfs instead of procfs
  8. Understand kernel coding style via checkpatch
  9. Work with wait queues
  10. ... up to submitting a real kernel patch

Even though the challenge itself is closed, you can reconstruct the exercises. The kernel module 'hello world' is a rite of passage every contributor should do — it teaches the build system, the symbol namespace, module licensing, and how printk works, all in under 20 lines of real kernel C.

// Task 1 of the Eudyptula spirit: a minimal kernel module
// Save as hello.c alongside a Kbuild/Makefile

#include <linux/module.h>
#include <linux/init.h>

static int __init hello_init(void)
{
	pr_info("Hello, kernel world!\n");
	return 0;
}

static void __exit hello_exit(void)
{
	pr_info("Goodbye, kernel world!\n");
}

module_init(hello_init);
module_exit(hello_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name <you@example.com>");
MODULE_DESCRIPTION("Eudyptula Task 1: Hello World");

10Scratch Your Own Itch: Finding Real Bugs Through Use

'Scratch your own itch' is the oldest and most sustainable source of contributions. If you use Linux daily and something annoys you — a driver that does not work on your hardware, a syscall that returns an undocumented error code, a /proc entry with wrong data — you have found your contribution.

The process:

  1. Reproduce the problem on the latest kernel (download from kernel.org, not your distro's patched version)
  2. Bisect with git bisect to find the commit that introduced it — this is the most valuable debugging skill in the kernel
  3. Read the code — find the relevant function, understand the intended behavior, understand why it is wrong
  4. Write a fix — often a one-to-five line change
  5. Test — does your fix work? Does make allyesconfig still build? Do the kselftest tests for your subsystem pass?
  6. Send — format with git format-patch, check with checkpatch, find maintainers, send

git bisect works on a kernel with ~1000 commits between releases. A typical bisect takes ~10 steps (log2(1000) ≈ 10), each of which means: compile, boot a VM, test one condition. With ccache and a fast machine, each step takes ~2 minutes. In 20-30 minutes you can pinpoint the exact commit that broke something.

Do not underestimate documentation bugs. If you read Documentation/ and find something wrong, outdated, or unclear, a documentation patch is a perfectly valid first contribution. Many maintainers love documentation fixes because they are rare.

# git bisect: finding which commit broke your audio driver
# Assume: works on v6.6, broken on v6.8

git bisect start
git bisect bad HEAD          # current HEAD is broken
git bisect good v6.6         # this tag was good

# git checks out the midpoint commit automatically
# Build and test, then mark it:
git bisect good   # or: git bisect bad

# Repeat ~10 times until git says:
# "abcdef1234 is the first bad commit"
git bisect reset  # return to HEAD when done

# Now read that commit with:
git show abcdef1234
# Find the bug, write the fix, reference the bad commit with:
# Fixes: abcdef1234 ("the exact commit subject")

11Surviving the Review Process

Kernel review culture is direct to the point of seeming harsh. A maintainer might reply with nothing but 'NAK' (not acknowledged — rejected) or a single terse criticism. This is not personal, and it is not a sign the community dislikes you. It is a sign that the maintainer has 500 patches in their queue and writes exactly the words needed to unblock the contributor.

Rules for surviving (and thriving in) review:

Do not argue unless you have a technical reason. If a maintainer says 'this is wrong', ask why if you genuinely do not understand, but do not defend a choice that is just a preference. The maintainer knows their subsystem better than you do at this stage.

Revise and resend as vN. When you resend, the subject becomes [PATCH v2]. The cover letter (for multi-patch series) or the patch body should include a --- section (below Signed-off-by, above the diff) listing what changed in each version:

---
Changes in v2:
- Replaced open-coded bit manipulation with BIT() macro
- Moved variable declaration to top of block per reviewer comment

This section appears in the email but is stripped by git am — it does not end up in the commit log.

Do not reuse the Message-Id of a previous patch in your resend. Use git format-patch freshly each time.

Wait patiently. Maintainers are volunteers too. If two weeks pass with no response, a polite single-line 'ping' is appropriate. Do not ping after 2 days.

Use lore.kernel.org to search if your patch has been discussed, applied, or queued. Type the subject line into the search box.

# Sending a v2 of a patch correctly

# After making changes based on review:
git add drivers/staging/foo/bar.c
git commit --amend   # amend the local commit (NOT a new commit)

# Format the v2 patch
git format-patch -1 HEAD \
    --subject-prefix='PATCH v2' \
    --add-header='In-Reply-To: <original-message-id@mail.example.com>'

# The In-Reply-To makes the v2 thread properly in email clients
# Find the original Message-Id from lore.kernel.org

# Send it:
git send-email --to=<maintainer> --cc=<list> 0001-*.patch

12Essential Tooling: b4, sparse, coccinelle, and kselftest

Beyond checkpatch and get_maintainer, a serious contributor builds a small but powerful toolbox:

b4 (pip install b4): a modern tool that replaces the manual parts of the workflow. It can retrieve patch series from lore.kernel.org, apply them to your tree, help you prepare and send cover letters, and handle the In-Reply-To threading automatically. b4 am <message-id> is invaluable for applying and testing someone else's patch series locally.

sparse (apt install sparse / pacman -S sparse): a semantic C checker written by Linus. It catches type system violations that gcc ignores — mixing __user pointers with kernel pointers, locking imbalances declared with __must_hold, incorrect endianness with __be32 vs __le32. Run with: make C=1 drivers/staging/foo/bar.o

coccinelle / spatch (apt install coccinelle): semantic patch tool. Given a .cocci file that describes a code transformation pattern, it applies it across the entire tree. The kernel ships ~200 ready-made semantic patches in scripts/coccinelle/. make coccicheck runs them. Finding and fixing a coccinelle-detected pattern across the tree is a classic beginner patch series.

kselftest: the kernel's built-in self-test framework. make -C tools/testing/selftests/<subsystem> run_tests runs the tests for that subsystem. If you are fixing a bug, writing a kselftest that proves it is fixed is the gold standard.

QEMU + KVM: You must test kernel changes. Boot a minimal VM: qemu-system-x86_64 -enable-kvm -m 1G -kernel arch/x86/boot/bzImage -initrd /path/to/initramfs -append 'console=ttyS0 nokaslr' -nographic. A script like virtme-ng (formerly virtme) makes this one command.

# Run sparse on one file:
make C=1 CF="-D__CHECK_ENDIAN__" drivers/staging/wlan-ng/hfa384x.o

# Run a specific coccinelle semantic patch:
spatch --sp-file scripts/coccinelle/api/devm_ioremap.cocci \
       --dir drivers/staging/foo/ \
       --in-place
# Review the diff, test, then commit as:
# staging: foo: use devm_ioremap_resource() (found by coccinelle)

# Install and run virtme-ng for fast kernel boot testing:
pip install virtme-ng
vng --build           # builds the kernel with a minimal config
vng --run             # boots it in QEMU, drops you into a shell

Key insights

  • ✦The kernel patch workflow is entirely email-based — there is no GitHub PR button. This is a feature, not a bug: every decision is documented in a public, permanent, searchable archive at lore.kernel.org.
  • ✦drivers/staging/ is explicitly designed as a beginner on-ramp. The TODO files are literal to-do lists written for you. Starting there is not settling for less — it is using the right tool for the job.
  • ✦One logical change per patch is a hard rule, not a preference. Mixing a style fix with a behavior change makes the patch harder to review, harder to revert, and harder to bisect.
  • ✦checkpatch.pl to zero errors AND zero warnings before sending is a minimum bar. A patch that fails checkpatch will almost always be rejected without further review.
  • ✦The Signed-off-by line is a legal statement (the DCO), not a formality. Always use your real name and a real email you check.
  • ✦Syzbot reports are ideal second-level contributions: the bug is proven real, the reproducer is provided, and the crash log points you almost to the line of code.
  • ✦git bisect is the single most powerful debugging technique for kernel regressions. Ten build-and-test cycles find any regression in a 1000-commit range.
  • ✦Terse review feedback is not hostility — it is efficiency. Treat 'NAK' as 'tell me why' and 'this is wrong' as 'now I know exactly what to fix'.

Common pitfalls

  • ⚠Sending a patch to linux-kernel@vger.kernel.org only, without CCing the subsystem list or the maintainer. The patch will be lost in LKML noise. Always run get_maintainer.pl.
  • ⚠Mixing multiple unrelated fixes in one patch. 'Cleaned up the foo driver' covering whitespace, a bug fix, and a refactor will be immediately asked to be split. The rule is: one logical change, one patch.
  • ⚠Forgetting --strict with checkpatch.pl. Without it you miss the extra checks that GKH's staging tree enforces. Run checkpatch --strict and reach zero warnings, not just zero errors.
  • ⚠Not rebasing onto the correct base tree. Your patch should be based on the maintainer's development tree (found in MAINTAINERS under T:), not on Linus's release tag. Check `scripts/get_maintainer.pl` output for the T: field.
  • ⚠Sending a v2 patch without indicating what changed between v1 and v2. Add a changelog section below the --- separator in your patch. Without it the reviewer must re-read the whole patch to see what changed.
  • ⚠Testing only in a real machine without KASAN/UBSAN. Your fix may introduce a subtle memory bug that only shows up under sanitizers. Always test with CONFIG_KASAN=y and CONFIG_UBSAN=y in a VM.
  • ⚠Giving up after a week of silence. Maintainers are volunteers with full-time jobs. Two to four weeks is a normal response time. Send a single polite ping after two weeks and wait another two.
  • ⚠Applying staging checkpatch fixes to a driver that is already being worked on by someone else. Check the mailing list archives for recent patches to your target driver before starting.

Practice

  1. 01
    easy

    Clone the mainline kernel tree and find a staging driver with at least 5 checkpatch --strict warnings. Run checkpatch --file on every .c file in that driver directory, collect the warnings into a list, then fix exactly ONE category of warning (e.g., all 'space prohibited before semicolon' warnings in that driver). Format the fix as a patch using git format-patch and run checkpatch on the resulting .patch file until it is clean. Do NOT send it yet — the goal is the clean patch file.

  2. 02
    easy

    Write and load a minimal kernel module (the Eudyptula Task 1 spirit). The module must: (1) print a greeting with pr_info on load, (2) print a farewell on unload, (3) have a correct MODULE_LICENSE("GPL"), (4) build cleanly against your running kernel using a proper Makefile with `obj-m := hello.o`. Load it with insmod, verify the output with dmesg, then unload it with rmmod. As a bonus, add a module parameter (module_param) that changes the greeting string.

  3. 03
    medium

    Use git bisect to find a commit that introduced a known regression. Set up a QEMU VM with the latest kernel, find an open syzbot bug at syzkaller.appspot.com that provides a C reproducer, download the reproducer, and confirm you can trigger the crash. (You do not need to fix it for this exercise — confirming reproduction and understanding the crash report is the goal.) Write a paragraph explaining: what memory error occurred, which kernel subsystem owns the crashing code, and what a correct fix might look like.

  4. 04
    medium

    Run `make coccicheck` (or `spatch --sp-file scripts/coccinelle/api/kfree_null.cocci --dir drivers/staging/FOO`) on a staging driver of your choice. If coccinelle finds matches, apply them, verify the result compiles (`make M=drivers/staging/FOO`), run checkpatch --strict on the diff, write a proper commit message with Signed-off-by, and format it as a patch. This is a complete, sendable patch — if it is clean, consider actually sending it.

  5. 05
    hard

    Complete the full contribution pipeline end-to-end: find a real issue in drivers/staging/ (checkpatch, sparse, coccinelle, or a genuine bug), write a fix, pass all checkers, format the patch, run get_maintainer.pl to find the correct TO/CC addresses, and **send the patch** to the staging mailing list. Keep a copy of any replies you receive. Whether it is accepted, rejected, or met with silence — document what happened and why, and if changes were requested, send a v2.

Resources

📄Submitting Patches: the Essential Guide
docsfree

The canonical, authoritative guide to kernel patch submission — read it cover to cover before sending anything.

📄Linux Kernel Coding Style
docsfree

The official style guide. checkpatch.pl enforces most of this mechanically, but you need to understand the reasoning behind each rule.

✎KernelNewbies: First Kernel Patch
articlefree

Step-by-step walkthrough specifically for the very first patch, with examples and common pitfalls. Read alongside this module.

🔧syzbot: Continuous Kernel Fuzzing Dashboard
toolfree

Browse open kernel bugs found by syzkaller fuzzing, with C reproducers and crash logs. A gold mine for second-level contributions.

🔧lore.kernel.org: Kernel Mailing List Archive
toolfree

The permanent, searchable archive of every kernel mailing list. Search your patch subject here to check if it was applied or discussed.

📕Linux Device Drivers, 3rd Edition
bookfree

Corbet, Rubini, Kroah-Hartman. Dated but foundational — the module programming chapters are still largely accurate and align perfectly with staging work.

📄The Linux Kernel Module Programming Guide
docsfree

Actively maintained (2023+) guide to writing kernel modules. Covers everything from hello world to character devices and proc entries.

✎Working with the Kernel Development Process (LWN)
articlefree

LWN's overview of the kernel development process — merge windows, -rc cycles, subsystem trees. Essential context for understanding patch timing.

🔧b4: A Tool for Kernel Patch Workflow
toolfree

Modern replacement for the manual git send-email + threading workflow. Highly recommended for patch preparation and series management.

🔧Bugzilla: Linux Kernel Bug Tracker
toolfree

User-reported kernel bugs, many with reproduction steps and dmesg output. Filter by component to find work in your area of interest.

Checkpoints — you can now…

  • ✓You can run scripts/checkpatch.pl --strict on a patch and reach zero errors and zero warnings, understanding the reason behind each check.
  • ✓You can run scripts/get_maintainer.pl on any file in the kernel tree and produce a correct --to and --cc list for git send-email.
  • ✓You can write a kernel commit message that includes a proper subject line, body explaining the why, and correct Signed-off-by / Fixes / Link trailers.
  • ✓You can write, build, load, and unload a minimal kernel module that uses pr_info and module_param.
  • ✓You can navigate drivers/staging/, read a TODO file, identify an actionable item, and produce a clean patch that addresses exactly one item.
  • ✓You can navigate the syzbot dashboard, read a KASAN crash report, identify the faulting subsystem and memory error type, and describe what a fix would need to address.
  • ✓You can use git bisect to narrow a regression down to a specific commit across a range of kernel releases.
  • ✓You understand the full patch lifecycle from local commit to linux-next to Linus's tree, and you know what to do when a reviewer asks for changes.