Lab 7: Your First Patch (Milestone 8)
Background
Everything so far was rehearsal. This lab sends a real patch, to a real mailing list, read by real people who will read your name for the first time.
It is deliberately small. The point is not the change — it is that the whole pipeline works: correct recipients, correct base, correct format, uncorrupted transport, and a commit message that answers the question before it is asked. Getting a one-line fix merged proves the machinery. The interesting patches come after, and they come much more easily once the machinery is boring.
Start this in week 9, not week 18. The community's clock runs in weeks and it does not start until you press send.
Why This Lab Matters
- Your first patch is your first impression, and it is archived forever on
lore. - Every mechanical failure mode — wrong list, mangled patch, wrong tree, missing sign-off — costs a round trip, and a round trip costs a week.
- Once the mechanics are automatic, the only remaining variable is the quality of your idea, which is where you want to be spending attention.
Prerequisites
- All five contribution chapters read.
Documentation/process/submitting-patches.rstread end to end. Not skimmed.git send-emailconfigured, and the round-trip test passed.b4installed.- A real name and a working email address in
git config.
Predict First
- How long until you get a reply? Give a number and a confidence.
- What is the single most likely mechanical thing to go wrong with your first send?
- If your patch is applied, how long until it is in a tagged release?
- If nobody replies for two weeks, what will you do — precisely?
- What fraction of first patches from newcomers get a "please split this" or "wrong tree" reply?
Write all five down. You will check them against reality in the validation step.
Step-by-Step Tasks
Step 1: Find something real
This is the hard part of the lab, and the constraint that makes it hard is deliberate:
The change must be real. Not a whitespace fix. Not a
checkpatchcleanup in code you have not read. Not a typo in a comment nobody reads. Something that, if a user or a developer hit it, would have cost them time.
Good first patches, in rough order of value:
| Kind | How to find one |
|---|---|
| A documentation error you can prove | Read Documentation/ for a subsystem you now know, and check each claim against the code. Docs drift; you will find something. |
| A stale or wrong comment | git log -S the code near a comment. If the code changed and the comment did not, that is a real defect. |
| A wrong error message or errno | A path that returns -EINVAL where the code clearly means -ENOMEM, or a dev_err naming the wrong thing |
A missing MODULE_DESCRIPTION | Recent kernels warn about these; there is a real, finite list |
| Dead code you can prove is dead | #ifdef on a symbol that no longer exists; a function with no callers |
A checkpatch finding in code you were reading anyway | Only if you understand the code |
cd ~/kernel/linux
# Documentation that references something that no longer exists:
rg -o -N '`\w+\(\)`' Documentation/driver-api/ | sort -u | head -40
# ...then check a few: does that function still exist?
rg -n "\bsome_function\b" --type c | head
# Comments referring to a function that was renamed:
git log --oneline -S'old_function_name' | head
# Missing module descriptions (a real, finite list):
make O=../build allmodconfig >/dev/null 2>&1
make O=../build -j"$(nproc)" 2>&1 | grep -i "missing MODULE_DESCRIPTION" | head
# What has recently been accepted in your area — calibrate on this:
git log --oneline --since="3 months ago" -- <your subsystem> | head -30
Warning: Do not go looking for
TODOandFIXMEcomments to fix. They are almost always there because the fix is hard, contentious, or blocked on something, and a newcomer's patch "fixing" one usually reveals they did not know why it was there. Find a defect, not a marker.
Step 2: Verify it is actually a bug
Before writing anything:
# 1. Is it already fixed in a tree you are not looking at?
git fetch linux-next --tags
git log --oneline linux-next/master --since="3 months ago" -- <the file>
# 2. Has someone already sent a patch for it?
# Search lore.kernel.org for the file name and the function name.
# 3. Was it done deliberately? The commit message will say.
git log --format='%h %s%n%n%b' -3 -- <the file>
git blame -L '/the_line/,+1' <the file>
Finding that it is already fixed is a success: you spent ten minutes instead of a week and a public round trip.
Step 3: Target it
./scripts/get_maintainer.pl --scm --status -f <the file>
# Is the subsystem alive?
git log --oneline --since="6 months ago" -- <the dir> | wc -l
# Is there a profile with extra rules?
grep -B 5 -A 20 "<the dir>" MAINTAINERS | grep -E '^[PQTLS]:'
ls Documentation/process/maintainer-*.rst
# Base on the tree the T: line names.
git remote add <subsys> <the T: URL>
git fetch <subsys>
git branch -r | grep <subsys> # branch names vary: main, for-next, ...
git switch -c my-first-fix <subsys>/<branch>
Step 4: Write it
One logical change. Nothing else in the diff — no whitespace, no unrelated tidying.
$EDITOR <the file>
git add -p # stage ONLY the intended hunks
git diff --cached # read every line of it
Step 5: Write the message
This is where a first patch is usually won or lost.
git commit -s # -s adds Signed-off-by
subsys: fix the specific thing
Describe what is wrong TODAY: under what conditions, and what a reader
would observe. Someone who has never seen this code should be able to
tell whether they are affected.
Then what you changed and why this is the right fix.
Then how you know: what you built, what you ran, on what.
Fixes: abc123def456 ("subsys: the commit that introduced it")
Signed-off-by: Your Real Name <you@example.com>
# Get the subject prefix from the file's own history — do not invent one.
git log --oneline -20 -- <the file>
# If it is a bug fix, find the commit that introduced it:
git log -S'the broken thing' --oneline -- <the file>
git log -1 --pretty=fixes <that sha> # emits a correctly formatted Fixes: line
Step 6: Check it
./scripts/checkpatch.pl --strict -g HEAD~1..HEAD
make O=../build -j"$(nproc)" # it builds
make O=../build C=2 W=1 <the/dir>/ # sparse + extra warnings
# If it touches code that can be built as a module:
make O=../build-all allmodconfig && make O=../build-all -j"$(nproc)"
And, if it is a behavioral change rather than documentation: boot it.
PROFILE=paranoid ~/kernel-labs/scripts/build-kernel.sh
~/kernel-labs/scripts/run-qemu.sh
Step 7: Produce and self-review
git format-patch -1 --base=auto
cat 0001-*.patch # READ IT. All of it. Out loud.
Read it as a stranger would. Does the subject say what it does? Does the message explain why? Is there anything in the diff you cannot justify?
Step 8: The self-send
Non-negotiable, even though you already did the round-trip test once — because this is the actual patch.
git send-email --to=you@example.com --suppress-cc=all 0001-*.patch
# fetch the raw message, then:
git am /tmp/received.eml && git show --stat HEAD
Step 9: Send it
./scripts/get_maintainer.pl 0001-*.patch # decide, do not just pipe
git send-email --dry-run \
--to='Maintainer Name <maint@example.com>' \
--cc='subsystem-list@vger.kernel.org' \
--cc='linux-kernel@vger.kernel.org' \
0001-*.patch
# Read the dry-run output. Then, once:
git send-email --to=... --cc=... 0001-*.patch
Check the cycle position first:
git fetch origin --tags && git describe --tags origin/master
Step 10: Record and wait
# It should appear within minutes:
# https://lore.kernel.org/all/?q=<your subject>
# Save the message-ID. That is the permanent identity of your patch.
# And the Patchwork entry, if the subsystem has one (the Q: line):
# https://patchwork.kernel.org/project/<project>/list/?submitter=<you>
Then wait. Two weeks before a single ping. Meanwhile, do Lab 9 — reviewing someone else's patch is the best possible use of the waiting time, and it is how you become a name people recognize.
Implementation Requirements / Deliverables
- A real change: you can state, in one sentence, who it helps and how.
-
Verified not already fixed in
linux-nextand not already posted onlore. -
Based on the tree the
MAINTAINERST:line names, on the right branch. - The subsystem's profile document read, if it has one.
- One logical change; nothing else in the diff.
- Commit message: problem, fix, testing — in that order, imperative mood, wrapped at ~72.
-
Signed-off-by:present, and you have read the DCO text it certifies. -
Fixes:present if it is a bug fix, in the exact format. -
checkpatch --strict -gclean. -
Builds;
C=2 W=1clean for the files touched;allmodconfigif applicable. -
The self-send round trip performed on this exact patch, and
git amapplied it. -
Sent with
git send-email, plain text, correctTo:andCc:. - Sent at a sensible point in the cycle.
-
The
loremessage-ID recorded. - Your five predictions recorded, with results filled in as they arrive.
Expected Output
$ ./scripts/checkpatch.pl --strict -g HEAD~1..HEAD
total: 0 errors, 0 warnings, 0 checks, 14 lines checked
Commit abc1234def56 ("subsys: fix the specific thing") has no obvious style problems
and is ready for submission.
$ git send-email --dry-run --to=... --cc=... 0001-*.patch
Dry-OK. Log says:
Server: smtp.example.com
MAIL FROM:<you@example.com>
RCPT TO:<maint@example.com>
RCPT TO:<subsystem-list@vger.kernel.org>
From: Your Real Name <you@example.com>
Subject: [PATCH] subsys: fix the specific thing
Within minutes, on lore.kernel.org:
[PATCH] subsys: fix the specific thing
Your Real Name Thu, 12 Jun 2026 14:02:11 +0000
And a reply, in anything from an hour to three weeks:
On Thu, Jun 12, 2026 at 02:02:11PM +0000, Your Real Name wrote:
> Fixes: abc123def456 ("subsys: the original")
Applied to subsys/for-next, thanks.
Debugging Steps
It never appeared on lore
It did not arrive. Your SMTP failed silently, or a gateway dropped it. Re-run the self-send test and
read the git send-email output rather than assuming it worked.
checkpatch complains about lines you did not touch
You used -f <file> instead of -g <commits>. Only your changes are your responsibility.
"Does not apply"
Wrong base. --base=auto in format-patch records what you built on; check it against the branch
the maintainer named, and rebase.
"Please split this patch"
Two logical changes. Go back to patch craft and apply the two questions.
"This should go to "
get_maintainer.pl under-included, which happens when a change has cross-subsystem implications.
Resend to the right people, briefly apologising, with a Link: to the original.
A reply says the patch is corrupted / does not apply for them
Your mail path mangled it. You skipped the self-send test, or you tested a different patch. Fix the path, then resend as v2 with a note.
A robot mails you about an architecture you have never used
That is the 0-day bot doing its job. Read the report — it usually names the exact config — fix it, and send v2 with a brief thanks. This is completely normal and not a failure.
Two weeks of silence
Work the table in Review and Etiquette, in order, before doing anything.
Experiment
CLAIM. The mechanical failure modes of a first patch are predictable, and you can measure how common they are rather than guessing.
METHOD. Before you send, go to lore.kernel.org and find ten first-time contributors'
patches in any subsystem. (First-timers are identifiable: no prior posts, and often a reply
explaining a mechanical rule.) For each, record:
| # | Outcome |
|---|---|
| Applied with no comment | |
| Applied after a technical revision | |
| Asked to split | |
| Wrong tree / wrong recipients | |
| Format problem (HTML, attachment, corrupted) | |
Style/checkpatch comment | |
| NAK on the merits | |
| No reply at all |
PREDICTION. Fill in the counts you expect before you look.
RESULT. Then compare with your prediction, and — the actual point — check your own patch against every category that appeared. Most people discover that the mechanical failures outnumber the technical ones, which is exactly why this lab is mostly mechanics.
Test
The test for this lab is external and slow, so make the parts you control checkable:
cat > ~/kernel-labs/scripts/presend.sh <<'EOF'
#!/usr/bin/env bash
# Everything checkable about a patch, before it leaves your machine.
set -euo pipefail
KTREE="${KTREE:-$HOME/kernel/linux}"
RANGE="${1:-HEAD~1..HEAD}"
cd "$KTREE"
rc=0
echo "== checkpatch"
./scripts/checkpatch.pl --strict -g "$RANGE" || rc=1
echo "== every commit builds"
git rebase --exec "make -s -j$(nproc) O=../build" "${RANGE%%..*}" || rc=1
echo "== sign-off present on every commit"
for c in $(git rev-list "$RANGE"); do
git show -s --format='%b' "$c" | grep -q '^Signed-off-by:' || {
echo " MISSING Signed-off-by on $c"; rc=1; }
done
echo "== recipients"
git format-patch "$RANGE" -o /tmp/presend >/dev/null
./scripts/get_maintainer.pl /tmp/presend/*.patch
echo "== base-commit recorded"
grep -q '^base-commit:' /tmp/presend/*.patch || { echo " no base-commit (use --base=auto)"; rc=1; }
[ $rc -eq 0 ] && echo "PRESEND OK" || echo "PRESEND FOUND PROBLEMS"
exit $rc
EOF
chmod +x ~/kernel-labs/scripts/presend.sh
~/kernel-labs/scripts/presend.sh
Verify it can fail: remove your Signed-off-by and re-run. If it still passes, the script is
wrong.
Challenge Extensions
-
Do it with
b4instead.b4 prep --new,b4 prep --edit-cover,b4 send. Compare the workflow with rawgit send-emailand decide which you will use going forward. -
Find the same bug in three other places. Most defects are patterns. A Coccinelle script that finds yours tree-wide (
make coccicheck MODE=report COCCI=...) turns one patch into a series and is a genuinely respected kind of contribution. -
Follow your patch all the way. Every week, check: is it in the maintainer's tree? In
linux-next? In a tagged release? Record the dates. That timeline is the answer to prediction 3, and having measured it once removes the anxiety permanently. -
Read the applied version. When it lands, diff what was applied against what you sent (
git range-diffor by hand). Maintainers often adjust the subject, the message, or whitespace. Every difference is a lesson about what they wanted. -
Send a second one immediately. The first patch is about mechanics; the second is where you find out whether the mechanics are actually automatic. Do not wait for the first to land.
-
Volunteer for an
Orphan. Find aMAINTAINERSentry withS: Orphanin an area you understand, fix something in it, and offer to take it on. This is how a surprising number of maintainers started.
Validation / Self-check
- Why must a first patch be a real change rather than a whitespace or
checkpatchfix? - Why is hunting for
TODO/FIXMEcomments bad advice? - Name three checks to run before writing a line, to avoid duplicating work.
- How do you find the right tree and branch, and what happens if you base on
masterinstead? - What are the three parts of the commit message body, in order?
- Where do you get the subject-line prefix?
- What exactly does the self-send test prove, and why do it on this specific patch even though you already tested your setup?
- Why
checkpatch -grather than-f? - You get a mail from the kernel test robot about a build failure on an architecture you do not have. What is the correct response, and what does ignoring it signal?
- Your patch has had no reply in fifteen days. What, precisely, do you do — in order?
- How long, realistically, from sending to a tagged release? Compare with your prediction.
- Of the eight outcomes in the experiment, which were most common, and which of them could you have prevented mechanically?
Next: Lab 8 — A Patch Series, where one change becomes several and bisectability becomes a hard requirement.