Kernel Contribution
Chapter 19: Linux Kernel Development and Community Contribution
Submitting a patch to the Linux kernel is a technical milestone for many engineers. The kernel community is large and process-driven, yet welcoming to newcomers who follow established conventions. This chapter covers the full pipeline: acquiring sources, building the kernel, understanding coding standards, checking code with checkpatch.pl, generating patches with git format-patch, sending them to the LKML via git send-email, and earning maintainer acceptance.
1. Getting Started: Barriers and Opportunities
The fundamental difference between kernel and application development is the execution environment: kernel code runs in privileged mode with no standard library, no memory protection, and no floating-point arithmetic. A null pointer dereference does not produce a segfault — it triggers a kernel panic. This demands deep understanding of hardware and OS internals.
Despite the high bar, the kernel community offers many contributor-friendly entry points. You do not have to start by writing a new driver or implementing a new subsystem. Recommended beginner paths include:
- Documentation improvements — fix typos, add examples, translate docs — minimal risk, fast maintainer review
- drivers/staging cleanup — staging drivers have uneven quality with TODO comments everywhere — ideal for beginners
- checkpatch warning fixes — run checkpatch.pl across the tree and fix coding style warnings
- Coccinelle semantic patches — use Coccinelle scripts to automate bulk code transformations (e.g., merging kzalloc+memset into kcalloc)
- sparse static analysis fixes —
make C=1invokes sparse for type annotation checking — fix the findings
Why contribute: Your code runs on billions of devices worldwide. Kernel Newbies data shows most contributors who get their first patch accepted keep contributing. A commit record in Linus's tree is also a powerful resume entry.
2. Acquiring the Kernel Source
The Linux kernel has multiple official trees. Beginners should understand what each is for:
| Tree | URL | Purpose |
|---|---|---|
| mainline | kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git | Linus's mainline, releases rc versions |
| linux-next | kernel.org/pub/scm/linux/kernel/git/next/linux-next.git | Integration of all subsystem next branches; base your patches here |
| stable | kernel.org/pub/scm/linux/kernel/git/stable/linux.git | Stable release maintenance and backported critical fixes |
| Subsystem trees | Maintained by respective maintainers | e.g. net-next (networking), drm-next (GPU) |
# 克隆 linux-next(使用 --depth=1 节省时间,完整历史约 4GB)
git clone --depth=1 \
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git \
linux-next
cd linux-next
# 如果后续需要完整历史(用于 git log / git blame)
git fetch --unshallow
# 添加 stable 树作为远程仓库
git remote add stable \
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
# 查看 tag 列表(内核版本号)
git tag -l 'v6.*' | sort -V | tail -20
3. Building the Kernel
Install Build Dependencies
# Debian / Ubuntu
sudo apt-get install -y \
build-essential libelf-dev libssl-dev \
flex bison bc dwarves pahole \
libncurses-dev pkg-config
# Fedora / RHEL
sudo dnf install -y \
gcc make flex bison elfutils-libelf-devel \
openssl-devel ncurses-devel bc dwarves
Kernel Configuration
# 方式1:图形化 TUI 菜单(推荐新手)
make menuconfig
# 方式2:使用架构默认配置(适合快速验证补丁)
make defconfig
# 方式3:基于当前运行内核的配置(只编译已加载模块,速度最快)
make localmodconfig
# 之后把当前内核 config 复制过来
cp /boot/config-$(uname -r) .config
make olddefconfig # 对新增选项采用默认值
# 仅修改单个配置项(不打开菜单)
./scripts/config --enable CONFIG_DEBUG_INFO
./scripts/config --disable CONFIG_SECURITY_SELINUX
make olddefconfig
Build and Install
# 并行编译(nproc 输出 CPU 核心数,编译时间随机器而异,约10分钟到1小时)
make -j$(nproc)
# 用 ccache 加速二次编译(首次编译后效果明显)
sudo apt-get install -y ccache
CC="ccache gcc" make -j$(nproc)
# 安装内核模块
sudo make modules_install
# 安装内核镜像(同时更新 initramfs 和 GRUB)
sudo make install
# 或手动复制
sudo cp arch/x86/boot/bzImage /boot/vmlinuz-$(make kernelrelease)
sudo update-grub
# 验证新内核版本
cat include/generated/utsrelease.h
Note: Always test self-compiled kernels in a VM or spare machine — never replace the kernel on a production system. If the new kernel fails to boot, select the previous kernel in the GRUB menu to roll back.
4. Kernel Coding Standards
The kernel coding style document lives at Documentation/process/coding-style.rst and is mandatory reading for all contributors. Patches that violate these rules will be rejected by maintainers — often without explanation.
Indentation and Line Length
/* 内核使用 TAB 缩进,宽度为 8 个字符(非空格!) */
/* 行长限制从历史的 80 字符放宽到了 100 字符 */
/* 正确 */
int kernel_function(struct device *dev, unsigned long flags)
{
if (flags & SOME_FLAG) {
do_something(dev);
return 0;
}
return -EINVAL;
}
/* 错误:使用空格缩进 */
int bad_function(void) {
return 0; /* 这是空格,不是 TAB */
}
Brace Placement
/* 函数体的左花括号独占一行(K&R 风格的内核变体) */
int my_function(int arg)
{
/* 函数体 */
}
/* 控制语句(if/for/while)左花括号在同一行 */
if (condition) {
do_something();
} else {
do_other();
}
/* 单行 if 不要加花括号,除非 else 分支需要 */
if (err)
return err;
Naming Conventions
- Functions/variables:
lowercase_with_underscores - Macros/constants:
UPPERCASE_WITH_UNDERSCORES - Struct types:
struct my_struct(avoid typedef unless there is a specific reason) - Global variables: avoid strongly; if necessary, add a module prefix
Legitimate Use of goto
/* 内核中 goto 用于错误路径清理,是推荐做法 */
int probe_device(struct platform_device *pdev)
{
struct my_priv *priv;
int ret;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv) {
ret = -ENOMEM;
goto err_alloc;
}
ret = request_irq(priv->irq, my_irq_handler, 0, "mydev", priv);
if (ret)
goto err_irq;
ret = register_device(priv);
if (ret)
goto err_register;
return 0;
err_register:
free_irq(priv->irq, priv);
err_irq:
/* devm_kzalloc 自动释放,无需手动 free */
err_alloc:
return ret;
}
5. Finding Places to Contribute
drivers/staging
The drivers/staging/ directory contains drivers not yet meeting mainline quality standards. Each driver has a TODO file explicitly listing what needs to be fixed — the best entry point for beginners.
# 列出所有 staging 驱动及其 TODO 文件
ls drivers/staging/
cat drivers/staging/greybus/TODO
# 用 checkpatch 扫描某个 staging 驱动
scripts/checkpatch.pl --strict -f drivers/staging/greybus/*.c
# 搜索 coding style 问题(典型:用 printk 替代 dev_err)
grep -rn "printk(" drivers/staging/greybus/ | grep -v "KERN_"
Using sparse for Type Annotation Checks
# 安装 sparse
sudo apt-get install -y sparse
# 只编译目标驱动(不需要全量编译)
make C=1 drivers/staging/greybus/
# C=2 在所有文件上运行 sparse(包括已经编译过的)
make C=2 drivers/staging/greybus/
# 常见 sparse 警告:
# warning: incorrect type in argument
# warning: context imbalance in 'func' -- unexpected unlock
6. Preparing Your First Patch
Understand the Problem
Before touching any code, fully understand the problem. Use git log --oneline drivers/xxx/ to review change history, git blame to find the original authors, and read relevant kernel docs and comments. Confirm this is genuinely a bug or standards violation, not a misunderstanding of kernel APIs.
# 创建工作分支(分支名描述具体改动,便于 maintainer 识别)
git checkout -b staging/greybus/fix-coding-style
# 编辑文件,做最小化、聚焦的改动
# 一个 patch 只做一件事!不要把多个无关改动混在一起
vim drivers/staging/greybus/core.c
# 只编译目标模块验证改动不引入编译错误
make -j$(nproc) drivers/staging/greybus/
# sparse 检查
make C=1 drivers/staging/greybus/
Running checkpatch.pl
# 检查最新一个 commit(最常用方式)
scripts/checkpatch.pl --strict -g HEAD
# 检查一个 .patch 文件
scripts/checkpatch.pl --strict 0001-staging-greybus-fix-style.patch
# 检查某个源文件(不需要先 commit)
scripts/checkpatch.pl --strict -f drivers/staging/greybus/core.c
# 输出示例:
# WARNING: line over 100 characters
# ERROR: do not use assignment in if condition
# CHECK: 'foo' may be better written as 'bar'
# total: 0 errors, 1 warnings, 0 checks, 42 lines checked
Target: Before sending, aim for
total: 0 errors, 0 warnings. CHECK-level hints (blue) are advisory — fix them when reasonable but they are not mandatory.
Writing the Commit Message
git add drivers/staging/greybus/core.c
git commit
# commit message 格式(非常重要!):
# 第一行:subsystem: component: Short description (50字符以内)
# 空行
# 正文:解释为什么做这个改动(WHY),而不是做了什么(WHAT,代码已经说明)
# 空行(如有)
# 标签行(Signed-off-by 是强制要求)
staging: greybus: replace printk with dev_err for proper log context
Using raw printk() in device drivers loses the device context from
the log message, making it harder to correlate log entries with
specific devices in systems with multiple greybus instances.
Replace printk(KERN_ERR ...) calls with dev_err(dev, ...) which
automatically prepends the device name to the log output.
Signed-off-by: Your Name
Signed-off-by:
Signed-off-byis the Developer's Certificate of Origin (DCO) declaration, confirming you have the right to submit this code. Patches without it are rejected outright. Usegit commit -sto add it automatically.
7. Finding the Right Maintainer
The Linux kernel has hundreds of maintainers, each responsible for a specific subsystem. Finding the right maintainer and mailing list before sending is essential — otherwise your patch will be ignored.
# get_maintainer.pl 根据文件路径或 patch 内容查找 maintainer
# 输出格式:Name (role: maintainer/reviewer/...)
# 对最新的 commit 生成 patch 并查找 maintainer
git format-patch -1 HEAD --stdout | scripts/get_maintainer.pl
# 或直接指定文件
scripts/get_maintainer.pl --file drivers/staging/greybus/core.c
# 示例输出:
# Greg Kroah-Hartman (maintainer:STAGING SUBSYSTEM)
# Johan Hovold (reviewer:GREYBUS SUBSYSTEM)
# [email protected] (open list:STAGING SUBSYSTEM)
# [email protected] (open list:GREYBUS SUBSYSTEM)
The MAINTAINERS file in the kernel root is the maintainer database, containing M (maintainer), R (reviewer), L (mailing list), S (status), and F (file pattern) fields for each subsystem. Understanding this file helps you decide where to send your patch.
8. Sending the Patch
Generating .patch Files
# 生成最新 1 个 commit 的 patch(-1 表示最近 1 个)
git format-patch -1 HEAD
# 生成多个 commit 的 patch 系列(自动添加序号前缀)
git format-patch -3 HEAD # 最近 3 个 commit
# 加 cover letter(补丁系列介绍,--cover-letter 生成 0000-cover-letter.patch)
git format-patch -3 HEAD --cover-letter
# 指定输出目录
git format-patch -1 HEAD -o /tmp/patches/
# 生成的文件示例:
# 0001-staging-greybus-replace-printk-with-dev_err.patch
Configuring git send-email
# 安装 git send-email(可能需要单独安装)
sudo apt-get install -y git-email
# ~/.gitconfig 中配置 SMTP(以 Gmail 为例)
git config --global sendemail.smtpserver smtp.gmail.com
git config --global sendemail.smtpserverport 587
git config --global sendemail.smtpencryption tls
git config --global sendemail.smtpuser [email protected]
# SMTP 密码通过 git credential helper 管理或在命令行输入
# 以腾讯企业邮为例
git config --global sendemail.smtpserver smtp.exmail.qq.com
git config --global sendemail.smtpserverport 465
git config --global sendemail.smtpencryption ssl
Sending the Patch
# 先用 --dry-run 预览(不实际发送)
git send-email \
--to [email protected] \
--cc [email protected] \
--dry-run \
0001-staging-greybus-replace-printk-with-dev_err.patch
# 确认无误后去掉 --dry-run 发送
git send-email \
--to [email protected] \
--cc [email protected] \
0001-staging-greybus-replace-printk-with-dev_err.patch
# 发送 patch 系列(cover letter + 多个 patch)
git send-email \
--to [email protected] \
--cc [email protected] \
/tmp/patches/*.patch
Critical: Kernel patches must be sent as plain text with no HTML, and the mail client must not re-wrap lines. Thunderbird requires plain-text mode with format=flowed disabled. Gmail's web interface corrupts patch formatting — strongly prefer
git send-email.
9. LKML Etiquette
Writing a Cover Letter
A single patch usually does not need a cover letter, but a patch series (more than 2 patches) must include one explaining the overall intent. The cover letter subject line format is [PATCH 0/N] Subject; the body explains why you are making these changes, how you tested them, and any known limitations.
Responding to Review Feedback
- Reply within 48 hours to show you are engaged
- Address each comment individually — accept, decline with reasoning, or request further discussion
- Resend as
[PATCH v2]with a changelog in the cover letter - Do not be discouraged by critical feedback — rigorous review is why the kernel is high quality
Common Tag Meanings
| Tag | Meaning |
|---|---|
| Signed-off-by: | Author certifies right to submit (DCO); also used by maintainers when forwarding |
| Reviewed-by: | Reviewer confirms code is correct and recommends merging |
| Acked-by: | Relevant party (e.g., subsystem maintainer) does not object without deep review |
| Tested-by: | Tester verified the patch works on real hardware |
| Reported-by: | Credits the person who reported the bug |
| Fixes: | Points to the commit this patch fixes (format: Fixes: abcdef ("commit subject")) |
Tracking Patch Status with patchwork
patchwork.kernel.org automatically archives patches sent to mailing lists and tracks their status (New / Under Review / Accepted / Rejected / Deferred). After sending, search patchwork by your name or patch subject to confirm correct archival.
10. Kernel Newbies and Learning Resources
The kernel community provides excellent learning and support channels for newcomers:
- kernelnewbies.org — the most important newcomer portal, including a TODO list (beginner-friendly tasks), per-release change explanations, and IRC channels
- [email protected] — the main LKML list — very high volume (hundreds of emails per day). Use mail filters or read via the lore.kernel.org web archive
- lore.kernel.org — public archive and search interface for all kernel mailing lists, with threaded reading support
- #kernelnewbies (Libera.Chat IRC) — real-time Q&A channel where maintainers and experienced contributors answer questions
- Kernel Documentation —
Documentation/process/contains complete contribution guides (submitting-patches.rst, email-clients.rst, etc.) - Weekly kernel activity tracking — LWN.net publishes weekly kernel development roundups — the best way to follow the community
Action checklist: ① Clone linux-next, ② find a checkpatch warning in drivers/staging/, ③ fix it and verify with checkpatch, ④ use get_maintainer.pl to find recipients, ⑤ send the patch to the mailing list (even staging patches get real human review). Complete these five steps and you are officially a kernel contributor.
Previous
← Ch18: Mini Shell
Next
Ch20: Final Project →