最近有一個向 Linux 內核提交 Patch (補丁) 的機會。歷經卡在 SMTP 配置,被老闆退回數次,被 Maintainer 建議修改後,終於被接納進了 subtree……(Update:進入主線內核了!)
讓自己的代碼名留千古的機會❌
涉及的驅動可能被刪除以避免側信道攻擊 ✅
什麼是內核 Patch#
眾所周知,Linux 內核是世界上最大,最複雜,也是最重要的軟件之一。其開發和維護依賴全球數以萬計的開發者共同努力。
向 Linux 社區貢獻代碼的最基本方式是通過郵件的方式向內核維護者提交補丁(而非 “現代化” 地在代碼托管平台發起 Pull Request)
A patch is a small text document containing a delta of changes between two different versions of a source tree. FROM kernel.org.
補丁文件按照一定的格式介紹了代碼變動的內容,以及進行這些修改的原因。經內核維護者審查通過後,這個補丁將集中合併進內核主線,或進入某個特殊的分支。
配置準備#
本文基於 Ubuntu 介紹發送 Patch 的基本配置。
- git
- git send-email (
apt install git-email
,git-core
is also needed) - diff
- perl
其中,git 用於版本控制與代碼提交,git send-email 用於發送攜帶補丁的郵件,diff 用於比較文件差異,perl 作為 perl 解釋器執行提交補丁所需的腳本。
為配置 smtp 發送郵件,需要在 git 配置文件.gitconfig
中添加郵件相關的配置,以下為一個使用 Gmail 作為發送郵箱的例子:
[user]
name = YOURNAME
# 此處的 email 應與發送郵件的郵箱一致
email = [email protected]
[sendemail]
from = YOURNAME <[email protected]>
smtpencryption = ssl
# 也可使用 tls 作為加密方式,端口應改為 578
smtpserver = smtp.gmail.com
smtpuser = YOURNAME
smtpserverport = 465
smtpAuth = LOGIN # 非必須
chainreplyto = false # 非必須
# tocmd 和 cccmd 是對獲取內核郵件列表指令的簡化,如果發送給自己以進行測試,只需註釋即可
tocmd = "`pwd`/scripts/get_maintainer.pl --nogit --norolestats --nol"
cccmd = "`pwd`/scripts/get_maintainer.pl --nogit --norolestats --nom"
進行修改#
想提交內核補丁,當然需要先獲取一份內核代碼!從 kernel.org 等處可以獲取內核源碼倉庫,git clone
一份到本地你喜歡的地方,並新建一個準備進行修改工作的分支。
git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
git branch -b fix/blablabla
注意,為了避免出現衝突,需要保持本地代碼與主線一致。開始修改前git pull
是個好習慣。
描述改動#
進行commit
時,簡單描述一下你所進行的改動,commit
標題格式可以參考其他補丁,一般為A: B: blablabla
的形式。使用git commit -s
自動添加Signed-off-by: YOUNAME <[email protected]>
,否則你應在 patch 文件中添加該行信息。
git format-patch -1
git format-patch
將在當前目錄下生成一份包含標準郵件格式(但不一定是標準補丁格式)的.patch
文件。這裡的-1
表示該補丁文件中僅應包含一個commit
。
在Subject:
與Signed-off-by
行之間,需要對該補丁進行詳細描述。我提交的補丁中描述了如何發現問題(Smatch 提供的錯誤信息),錯誤原因(ioremap
分配的內存沒有取消映射)和修改方式(改為devm_ioremap
以保證在設備移除時取消內存映射)
別忘了測試!你可以選擇全量編譯整個內核,或在你修改的模塊裡僅編譯你修改的部分(比如一個驅動)
發送,以及等待#
格式檢查:使用內核源碼中內置的腳本:
$PATH_TO_KERNEL/scripts/checkpatch.pl YOUR_PATCH.patch
一個常見的錯誤是:
WARNING: Possible unwrapped commit description (prefer a maximum 75 chars per line)
這是因為在 Linux 內核的COMMIT_MSG
和 Patch 需要能夠在限長 80 字符的終端機正常顯示,因此每行不能超過 80 字(believe it or not,內核中充滿神奇的古老規定)按報錯信息修改即可。
還記得我們在.gitconfig
中添加的tocmd
和cccmd
嗎?
tocmd = "`pwd`/scripts/get_maintainer.pl --nogit --norolestats --nol"
cccmd = "`pwd`/scripts/get_maintainer.pl --nogit --norolestats --nom"
tocmd
行將自動獲取收件人,即修改部分的內核維護者的郵件。同理,cccmd
行獲取抄送人郵件列表。這樣可以簡化使用命令行發送郵件時的 “CLI 心智負擔”。
git send-email YOUR_PATCH.patch
沒錯,最終的發送指令就這麼簡單!
如果想先發給自己,或者其他有經驗的人為你 Review 一下,避免不合理的 Patch 引起內核維護者的怒火呢?你可以將tocmd
和cccmd
註釋掉,並在發送時添加--to
和--cc
選項,指定發送 / 抄送人即可。
git send-email --to [email protected] --cc [email protected] YOUR_PATCH.patch
正是發送後,你需要的就只有…… 耐心地等待。Well,內核維護者們非常勤奮,但沒有人能保證及時審查和回覆所有郵件,對吧?
假如幾個星期後你的補丁郵件依然無人問津(這並不罕見),你可以發送 PING 或 RESEND 來提醒維護者。
git fromat-patch -1 --subject-prefix='PING'
# or
git format-patch -1 --subject-prefix='RESEND'
修改,v2,v3,v4……#
經過漫長的等待,一封Re: PATCH
叩響了你郵箱的大門 —— 迎接你的也許是一句簡單的 “Applied, thanks”,也許是滿滿的牢騷和怒火(當然,大多數時候是禮貌而友好的)
請注意,新的 PATCH,即PATCH v2
,應該和最初一樣,在原汁原味的 HEAD 分支上進行改動,而不是在你的 Commit 的基礎上提交新的代碼。
按意見修改後,你就可以發出PATCH v2
了。記得在Reviewed by
之後添加 Changelog 信息,即你進行了哪些修改。比如:
v2 -> v3: Directly deleted 'base' and its related code based on master.
v1 -> v2: Directly deleted 'base' and its related code based on PATCH v1.
新郵件的主題也需要進行修改:
git format-patch -1 --subject-prefix='PATCH v2'
發送新補丁的過程和第一次一樣,注意要發送的文件別選錯了。
也許修改補丁的過程要重複很多次,也許你還需要發送 v3,v4,但請不要氣餒!
接收之後#
在你的補丁被維護者接納之後,它會首先被合併進該模塊的子樹(可以在git.kernel.org找到),然後和其他補丁一起集中提交給 Linus,最後進入主線內核。大功告成!
其他需要注意的#
- 只回覆郵件,不發送補丁,也可以使用
git send-email
,具體需要的指令可以在https://lore.kernel.org/lkml/你要回覆的郵件後找到。 - 本文沒有涉及一次發送多個 PATCH 的情況。你可以在這裡找到更多資料。