4. tip tree 手冊¶
4.1. 什麼是 tip tree?¶
tip tree 是幾個子系統和開發領域的集合。 tip tree 既是直接開發樹,也是幾個子維護者樹的聚合樹。 tip tree gitweb URL 是: https://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git
tip tree 包含以下子系統
x86 架構
x86 架構的開發在 tip tree 中進行,除了 x86 KVM 和 XEN 特定的部分,它們在相應的子系統中維護,並直接從那裡路由到主線。 在 x86 特定的 KVM 和 XEN 補丁上抄送給 x86 維護者仍然是一個好的做法。
除了整體 x86 維護者之外,一些 x86 子系統還有自己的維護者。 請在涉及 arch/x86 中檔案的補丁上抄送給整體 x86 維護者,即使它們沒有在 MAINTAINER 檔案中被呼叫。
請注意,
x86@kernel.org不是郵件列表。 它只是一個郵件別名,用於將郵件分發到 x86 頂級維護者團隊。 請始終抄送 Linux 核心郵件列表 (LKML)linux-kernel@vger.kernel.org,否則您的郵件最終只會出現在維護者的私人收件箱中。排程器
排程器開發在 -tip tree 中進行,位於 sched/core 分支中 - 偶爾也會有用於正在進行中的補丁集的子主題樹。
鎖定和原子操作
鎖定開發(包括原子操作和其他與鎖定相關的同步原語)在 -tip tree 中進行,位於 locking/core 分支中 - 偶爾也會有用於正在進行中的補丁集的子主題樹。
通用中斷子系統和中斷晶片驅動程式:
中斷核心開發發生在 irq/core 分支中
中斷晶片驅動程式開發也發生在 irq/core 分支中,但補丁通常在單獨的維護者樹中應用,然後聚合到 irq/core 中
時間,定時器,時間管理,NOHZ 和相關晶片驅動程式:
時間管理,clocksource 核心,NTP 和 alarmtimer 開發發生在 timers/core 分支中,但補丁通常在單獨的維護者樹中應用,然後聚合到 timers/core 中
clocksource/event 驅動程式開發發生在 timers/core 分支中,但補丁主要在單獨的維護者樹中應用,然後聚合到 timers/core 中
效能計數器核心,架構支援和工具:
perf 核心和架構支援開發發生在 perf/core 分支中
perf 工具開發發生在 perf tools 維護者樹中,並聚合到 tip tree 中。
CPU 熱插拔核心
RAS 核心
主要是在 tip ras/core 分支中收集 x86 特定的 RAS 補丁。
EFI 核心
EFI 開發在 efi git 樹中進行。 收集的補丁聚合在 tip efi/core 分支中。
RCU
RCU 開發發生在 linux-rcu 樹中。 結果更改聚合到 tip core/rcu 分支中。
各種核心程式碼元件:
debugobjects
objtool
隨機片段
4.2. 補丁提交注意事項¶
4.2.1. 選擇 tree/branch¶
通常,針對 tip tree master 分支的 head 進行開發是可以的,但對於單獨維護,擁有自己的 git 樹並且僅聚合到 tip tree 中的子系統,應該針對相關的子系統樹或分支進行開發。
針對主線的錯誤修復應該始終適用於主線核心樹。 與已經在 tip tree 中排隊的更改的潛在衝突由維護者處理。
4.2.2. 補丁主題¶
tip tree 補丁主題字首的首選格式是 “subsys/component:”,例如 “x86/apic:”,“x86/mm/fault:”,“sched/fair:”,“genirq/core:”。 請不要使用檔名或完整檔案路徑作為字首。 在大多數情況下,“git log path/to/file” 應該給你一個合理的提示。
主題行中簡明的補丁描述應以大寫字母開頭,並應以祈使語氣書寫。
4.2.3. 變更日誌¶
關於 提交補丁指南 中變更日誌的一般規則適用。
tip tree 維護者重視遵循這些規則,特別是以祈使語氣編寫變更日誌,而不是模仿程式碼或其執行的要求。 這不僅僅是維護者的突發奇想。 用抽象詞語編寫的變更日誌比用小說形式編寫的變更日誌更精確,也更不容易混淆。
將變更日誌分成幾個段落而不是將所有內容都集中到一個段落中也很有用。 一個好的結構是在單獨的段落中解釋上下文,問題和解決方案,並按此順序排列。
示例說明
示例 1
x86/intel_rdt/mbm: Fix MBM overflow handler during hot cpu When a CPU is dying, we cancel the worker and schedule a new worker on a different CPU on the same domain. But if the timer is already about to expire (say 0.99s) then we essentially double the interval. We modify the hot cpu handling to cancel the delayed work on the dying cpu and run the worker immediately on a different cpu in same domain. We do not flush the worker because the MBM overflow worker reschedules the worker on same CPU and scans the domain->cpu_mask to get the domain pointer.改進版本
x86/intel_rdt/mbm: Fix MBM overflow handler during CPU hotplug When a CPU is dying, the overflow worker is canceled and rescheduled on a different CPU in the same domain. But if the timer is already about to expire this essentially doubles the interval which might result in a non detected overflow. Cancel the overflow worker and reschedule it immediately on a different CPU in the same domain. The work could be flushed as well, but that would reschedule it on the same CPU.示例 2
time: POSIX CPU timers: Ensure that variable is initialized If cpu_timer_sample_group returns -EINVAL, it will not have written into *sample. Checking for cpu_timer_sample_group's return value precludes the potential use of an uninitialized value of now in the following block. Given an invalid clock_idx, the previous code could otherwise overwrite *oldval in an undefined manner. This is now prevented. We also exploit short-circuiting of && to sample the timer only if the result will actually be used to update *oldval.改進版本
posix-cpu-timers: Make set_process_cpu_timer() more robust Because the return value of cpu_timer_sample_group() is not checked, compilers and static checkers can legitimately warn about a potential use of the uninitialized variable 'now'. This is not a runtime issue as all call sites hand in valid clock ids. Also cpu_timer_sample_group() is invoked unconditionally even when the result is not used because *oldval is NULL. Make the invocation conditional and check the return value.示例 3
The entity can also be used for other purposes. Let's rename it to be more generic.改進版本
The entity can also be used for other purposes. Rename it to be more generic.
對於複雜的場景,特別是競爭條件和記憶體排序問題,使用表格描述並行性和事件的時間順序來描述場景是有價值的。 這是一個例子
CPU0 CPU1
free_irq(X) interrupt X
spin_lock(desc->lock)
wake irq thread()
spin_unlock(desc->lock)
spin_lock(desc->lock)
remove action()
shutdown_irq()
release_resources() thread_handler()
spin_unlock(desc->lock) access released resources.
^^^^^^^^^^^^^^^^^^^^^^^^^
synchronize_irq()
Lockdep 提供類似的有用輸出以描述可能的死鎖場景
CPU0 CPU1
rtmutex_lock(&rcu->rt_mutex)
spin_lock(&rcu->rt_mutex.wait_lock)
local_irq_disable()
spin_lock(&timer->it_lock)
spin_lock(&rcu->mutex.wait_lock)
--> Interrupt
spin_lock(&timer->it_lock)
4.2.4. 變更日誌中的函式引用¶
當在變更日誌中提到一個函式時,無論是在正文還是主題行中,請使用 “function_name()” 格式。 省略函式名後面的括號可能會產生歧義
Subject: subsys/component: Make reservation_count static
reservation_count is only used in reservation_stats. Make it static.
帶有括號的變體更精確
Subject: subsys/component: Make reservation_count() static
reservation_count() is only called from reservation_stats(). Make it
static.
4.2.5. 變更日誌中的回溯¶
參見 提交訊息中的回溯。
4.2.7. 文件連結¶
在變更日誌中提供文件連結對以後的除錯和分析非常有幫助。 不幸的是,URL 經常很快中斷,因為公司經常重組他們的網站。 非 “易失性” 例外包括 Intel SDM 和 AMD APM。
因此,對於 “易失性” 文件,請在核心 bugzilla https://bugzilla.kernel.org 中建立一個條目,並將這些文件的副本附加到 bugzilla 條目。 最後,在變更日誌中提供 bugzilla 條目的 URL。
4.2.8. 補丁重發或提醒¶
參見 不要氣餒 - 或不耐煩。
4.2.9. 合併視窗¶
請不要期望在合併視窗周圍或期間由 tip 維護者稽核或合併補丁。 在此期間,樹對所有內容都是關閉的,除了緊急修復。 一旦合併視窗關閉併發布了新的 -rc1 核心,它們就會重新開啟。
大型系列應在合併視窗開啟 **至少** 一週前以可合併狀態提交。 例外情況是錯誤修復和 *有時* 用於新硬體的小型獨立驅動程式或硬體啟用方面的最小侵入性補丁。
在合併視窗期間,維護者而是專注於關注上游更改,修復合併視窗 fallout,收集錯誤修復,並讓自己喘口氣。 請尊重這一點。
所謂的 _緊急_ 分支將在每個版本的穩定階段合併到主線中。
4.2.10. Git¶
tip 維護者接受來自維護者的 git pull 請求,這些維護者提供子系統更改以在 tip tree 中進行聚合。
通常不接受針對新補丁提交的 pull 請求,並且不能代替向郵件列表正確提交補丁。 主要原因是審查工作流程是基於電子郵件的。
如果您提交了一個更大的補丁系列,那麼在私有儲存庫中提供一個 git 分支會很有幫助,這使感興趣的人可以輕鬆地提取該系列以進行測試。 提供此內容的通常方法是在補丁系列的封面信中使用 git URL。
4.2.11. 測試¶
程式碼應在提交給 tip 維護者之前進行測試。 除少量更改外,所有內容都應在啟用全面(和重量級)核心除錯選項的情況下進行構建,啟動和測試。
這些除錯選項可以在 kernel/configs/x86_debug.config 中找到,可以透過執行以下命令將其新增到現有核心配置中
make x86_debug.config
其中一些選項是 x86 特定的,在其他架構上進行測試時可以省略。
4.3. 編碼風格注意事項¶
4.3.2. 記錄鎖定要求¶
記錄鎖定要求是一件好事,但註釋不一定是最佳選擇。 代替寫入
/* Caller must hold foo->lock */ void func(struct foo *foo) { ... }請使用
void func(struct foo *foo) { lockdep_assert_held(&foo->lock); ... }在 PROVE_LOCKING 核心中,如果呼叫者沒有持有鎖,lockdep_assert_held() 會發出警告。 註釋無法做到這一點。
4.3.3. 括號規則¶
只有在 “if”,“for”,“while” 等之後跟隨的語句確實是單行時,才應省略括號
if (foo)
do_something();
即使 C 不需要括號,以下也不被認為是單行語句
for (i = 0; i < end; i++)
if (foo[i])
do_something(foo[i]);
在外層迴圈周圍新增括號可以增強閱讀流程
for (i = 0; i < end; i++) {
if (foo[i])
do_something(foo[i]);
}
4.3.4. 變數宣告¶
函式開頭變數宣告的首選順序是反向冷杉樹順序
struct long_struct_name *descriptive_name;
unsigned long foo, bar;
unsigned int tmp;
int ret;
以上比反向排序更快解析
int ret;
unsigned int tmp;
unsigned long foo, bar;
struct long_struct_name *descriptive_name;
甚至比隨機排序更快
unsigned long foo, bar;
int ret;
struct long_struct_name *descriptive_name;
unsigned int tmp;
另外,請嘗試將相同型別的變數聚合到一行中。 浪費螢幕空間是沒有意義的
unsigned long a;
unsigned long b;
unsigned long c;
unsigned long d;
這樣做真的足夠了
unsigned long a, b, c, d;
另請避免在變數宣告中引入換行符
struct long_struct_name *descriptive_name = container_of(bar,
struct long_struct_name,
member);
struct foobar foo;
最好將初始化移動到宣告之後的單獨一行
struct long_struct_name *descriptive_name;
struct foobar foo;
descriptive_name = container_of(bar, struct long_struct_name, member);
4.3.5. 變數型別¶
對於旨在描述硬體或用作訪問硬體的函式引數的變數,請使用正確的 u8,u16,u32,u64 型別。 這些型別清楚地定義了位寬,並避免了截斷,擴充套件和 32/64 位混淆。
在 “unsigned long” 會被使用的情況下,對於 32 位核心來說,如果程式碼變得模稜兩可,也建議使用 u64。 雖然在這種情況下也可以使用 “unsigned long long”,但 u64 更短,並且清楚地表明無論目標 CPU 如何,操作都需要 64 位寬。
請使用 “unsigned int” 代替 “unsigned”。
4.3.6. 常量¶
請不要在程式碼或初始化程式中使用文字(十六進位制)十進位制數。 要麼使用具有描述性名稱的正確定義,要麼考慮使用列舉。
4.3.7. 結構宣告和初始化程式¶
結構宣告應以表格形式對齊結構成員名稱
struct bar_order {
unsigned int guest_id;
int ordered_item;
struct menu *menu;
};
請避免在宣告中記錄結構成員,因為這通常會導致格式奇怪的註釋,並且結構成員變得模糊
struct bar_order {
unsigned int guest_id; /* Unique guest id */
int ordered_item;
/* Pointer to a menu instance which contains all the drinks */
struct menu *menu;
};
相反,請考慮在結構宣告之前的註釋中使用 kernel-doc 格式,該格式更易於閱讀並且具有在核心文件中包含資訊的額外優勢,例如,如下所示
/**
* struct bar_order - Description of a bar order
* @guest_id: Unique guest id
* @ordered_item: The item number from the menu
* @menu: Pointer to the menu from which the item
* was ordered
*
* Supplementary information for using the struct.
*
* Note, that the struct member descriptors above are arranged
* in a tabular fashion.
*/
struct bar_order {
unsigned int guest_id;
int ordered_item;
struct menu *menu;
};
靜態結構初始化程式必須使用 C99 初始化程式,並且也應以表格形式對齊
static struct foo statfoo = {
.a = 0,
.plain_integer = CONSTANT_DEFINE_OR_ENUM,
.bar = &statbar,
};
請注意,雖然 C99 語法允許省略最後一個逗號,但我們建議在最後一行使用逗號,因為它使重新排序和新增新行更容易,並且使此類未來的補丁也更容易閱讀。
4.3.8. 換行符¶
將行長限制為 80 個字元會使深度縮排的程式碼難以閱讀。 考慮將程式碼分解為輔助函式,以避免過多的換行。
80 個字元的規則不是一個嚴格的規則,因此在換行時請使用常識。 特別是格式字串絕不應被拆分。
當拆分函式宣告或函式呼叫時,請將第二行中的第一個引數與第一行中的第一個引數對齊
static int long_function_name(struct foobar *barfoo, unsigned int id,
unsigned int offset)
{
if (!id) {
ret = longer_function_name(barfoo, DEFAULT_BARFOO_ID,
offset);
...
4.3.9. 名稱空間¶
函式/變數名稱空間提高了可讀性,並允許輕鬆進行 grepping。 這些名稱空間是全域性可見的函式和變數名稱(包括內聯)的字串字首。 這些字首應結合子系統和元件名稱,例如 “x86_comp_”,“sched_”,“irq_” 和 “mutex_”。
這也包括立即放入全域性可見的驅動程式模板中的靜態檔案範圍函式 - 對於這些符號來說,攜帶一個好的字首也很有用,以提高回溯的可讀性。
對於本地靜態函式和變數,可以省略名稱空間字首。 真正本地的函式,僅由其他本地函式呼叫,可以具有更短的描述性名稱 - 我們的主要關注點是 greppability 和回溯可讀性。
請注意,“xxx_vendor_” 和 “vendor_xxx_` 字首對於特定於供應商的檔案中的靜態函式沒有幫助。 畢竟,程式碼已經是特定於供應商的程式碼這一點已經很清楚了。 此外,供應商名稱僅應用於真正特定於供應商的功能。
與往常一樣,應用常識,併力求一致性和可讀性。
4.4. 提交通知¶
tip tree 由一個機器人監視是否有新的提交。 該機器人為每個新提交向專用的郵件列表 (linux-tip-commits@vger.kernel.org) 傳送電子郵件,並抄送所有在提交標籤之一中提到的人。 它使用標籤列表末尾的 Link 標籤中的電子郵件訊息 ID 來設定 In-Reply-To 電子郵件標頭,以便該訊息與補丁提交電子郵件正確地進行執行緒處理。
tip 維護者和子維護者嘗試在合併補丁時回覆提交者,但他們有時會忘記或不適合當時的 workflow。 雖然機器人訊息純粹是機械的,但它也暗示著 “謝謝!已應用。”。
4.3.1. 註釋風格¶
註釋中的句子以大寫字母開頭。
單行註釋
多行註釋
無尾部註釋(見下文)
評論重要的東西
函式文件註釋