為 GKI 開發內核代碼

通用內核映像 (GKI) 通過與上游 Linux 內核緊密結合來減少內核碎片。然而,有些補丁不能被上游接受是有正當理由的,而且有一些必須滿足的產品時間表,所以有些補丁是在構建 GKI 的 Android 通用內核 (ACK) 源中維護的。

開發人員必須優先使用 Linux 內核郵件列表 (LKML) 向上游提交代碼更改,並且只有在有充分理由說明上游不可行時才將代碼更改提交到 ACK android-mainline分支。正當理由及處理方法舉例如下。

  • 該補丁已提交給 LKML,但沒有及時接受產品發布。要處理此補丁:

    • 提供補丁已提交給 LKML 的證據和收到的補丁評論,或補丁提交上游的估計時間。
    • 決定將補丁放入 ACK 中的行動方案,使其在上游獲得批准,然後在最終上游版本合併到 ACK 時將其從 ACK 中取出。
  • 該補丁為供應商模塊定義了EXPORT_SYMBOLS_GPL() ,但由於沒有使用該符號的樹內模塊,因此無法向上游提交。要處理此補丁,請詳細說明您的模塊無法提交到上游的原因以及您在提出此請求之前考慮的替代方案。

  • 該補丁對於上游來說不夠通用,並且沒有時間在產品發布之前對其進行重構。要處理此補丁,請提供重構補丁將在上游提交的估計時間(如果沒有計劃向上游提交重構補丁以供審核,則該補丁將不會在 ACK 中被接受)。

  • 補丁不能被上游接受,因為... <在此處插入原因> 。要處理此補丁,請聯繫 Android 內核團隊,並與我們一起研究重構補丁的選項,以便將其提交審核並在上游接受。

還有很多潛在的理由。當您提交您的錯誤或補丁時,請提供有效的理由並期待一些迭代和討論。我們認識到 ACK 將攜帶一些補丁,尤其是在 GKI 的早期階段,當時每個人都在學習如何在上游工作,但不能放鬆產品時間表。預計隨著時間的推移,上游要求會變得更加嚴格。

補丁要求

補丁必須符合Linux 源代碼樹中描述的 Linux 內核編碼標準,無論它們是提交給上游還是 ACK。 scripts/checkpatch.pl腳本作為 Gerrit 預提交測試的一部分運行,因此請提前運行以確保它通過。要使用與預提交測試相同的配置運行 checkpatch 腳本,請使用repo checkout 中的build/static_analysis/checkpatch_presubmit.sh

確認補丁

提交給 ACK 的補丁必須符合 Linux 內核編碼標準和貢獻指南。您必須在提交消息中包含Change-Id標籤;如果您將補丁提交到多個分支(例如android-mainlineandroid12-5.4 ),則必須對補丁的所有實例使用相同的Change-Id

首先向 LKML 提交補丁以供上游審查。如果補丁是:

  • 被上游接受,它會自動合併到android-mainline中。
  • 上游不接受,將其提交給android-mainline並附上上游提交的參考資料或解釋為什麼它沒有提交給 LKML。

在上游或android-mainline中接受補丁後,可以將其反向移植到適當的基於 LTS 的 ACK(例如android12-5.4android11-5.4用於修復 Android 特定代碼的補丁)。提交到android-mainline可以使用新的上游候選版本進行測試,並保證補丁在下一個基於 LTS 的 ACK 中。例外情況包括上游補丁被反向移植到android12-5.4的情況(因為補丁可能已經在android-mainline中)。

上游補丁

貢獻指南中所述,發往 ACK 內核的上游補丁分為以下幾組(按被接受的可能性順序列出)。

  • UPSTREAM: - 如果有合理的用例,從 'android-mainline' 中挑選的補丁很可能會被 ACK 接受。
  • BACKPORT: - 如果有合理的用例,來自上游的補丁不能乾淨地挑選並且需要修改也可能會被接受。
  • FROMGIT: - 如果有即將到來的截止日期,可能會接受從維護者分支中挑選出來的補丁,以準備提交到 Linux 主線。這些必須在內容和時間表上都得到證明。
  • FROMLIST: - 已提交給 LKML 但尚未被維護者分支接受但不太可能被接受的補丁,除非理由足夠令人信服,以至於無論補丁是否登陸上游 Linux 都會被接受(我們假設它不會)。必須存在與FROMLIST補丁相關的問題,以便與 Android 內核團隊討論。

特定於 Android 的補丁

如果您無法在上游獲得所需的更改,您可以嘗試直接向 ACK 提交樹外補丁。提交樹外補丁要求您在 IT 中創建一個問題,該問題引用補丁和無法向上游提交補丁的理由(請參閱前面的列表以獲取示例)。但是,有少數情況下無法向上游提交代碼。這些情況如下所述,並且必須遵循 Android 特定補丁的貢獻指南,並在主題中使用ANDROID:前綴進行標記。

對 gki_defconfig 的更改

gki_defconfig的所有CONFIG更改必須同時應用於 arm64 和 x86 版本,除非CONFIG是特定於體系結構的。要請求更改CONFIG設置,請在 IT 中創建問題以討論更改。任何在凍結後影響內核模塊接口 (KMI) 的CONFIG更改都會被拒絕。如果合作夥伴要求單個配置的設置衝突,我們會通過討論相關錯誤來解決衝突。

上游不存在的代碼

無法向上游發送已針對 Android 的代碼的修改。例如,即使 binder 驅動程序在上游維護,對 binder 驅動程序的優先級繼承特性的修改也不能發送到上游,因為它們是 Android 特定的。在您的錯誤中明確並修補無法向上游發送代碼的原因。如果可能的話,將補丁分成可以提交到上游的部分和不能提交到上游的特定於 Android 的部分,以最大限度地減少 ACK 中維護的樹外代碼的數量。

此類別中的其他更改包括對 KMI 表示文件、KMI 符號列表、 gki_defconfig 、構建腳本或配置或上游不存在的其他腳本的更新。

樹外模塊

上游 Linux 積極阻止對構建樹外模塊的支持。這是一個合理的立場,因為 Linux 維護人員不保證內核源代碼或二進制兼容性,也不希望支持不在樹中的代碼。但是,GKI確實為供應商模塊提供 ABI 保證,確保 KMI 接口在內核支持的生命週期內保持穩定。因此,有一類更改可以支持 ACK 可接受但上游不可接受的供應商模塊。

例如,考慮一個添加EXPORT_SYMBOL_GPL()宏的補丁,其中使用導出的模塊不在源樹中。雖然您必須嘗試向上游請求EXPORT_SYMBOL_GPL()並提供一個使用新導出符號的模塊,但如果有正當理由說明為什麼沒有向上游提交該模塊,您可以將補丁提交給 ACK。您需要說明為什麼模塊不能在問題中上游化的理由。 (不要請求非 GPL 變體EXPORT_SYMBOL() 。)

隱藏配置

一些樹內模塊會自動選擇gki_defconfig中無法指定的隱藏配置。例如,配置CONFIG_SND_SOC_TOPOLOGY CONFIG_SND_SOC_SOF=y時會自動選擇 CONFIG_SND_SOC_TOPOLOGY。為了適應樹外模塊的構建,GKI 包含了一種啟用隱藏配置的機制。

要啟用隱藏配置,請在init/Kconfig.gki中添加一條select語句,以便根據 gki_defconfig 中啟用的CONFIG_GKI_HACKS_TO_FIX內核配置自動選擇gki_defconfig 。僅將此機制用於隱藏配置;如果配置未隱藏,則必須在gki_defconfig中明確指定或作為依賴項指定。

可加載的調速器

對於支持可加載調控器的內核框架(例如cpufreq ),您可以覆蓋默認調控器(例如cpufreqschedutil器。對於不支持可加載調控器或驅動程序但仍需要供應商特定的實施,在 IT 中創建問題並諮詢Android 內核團隊

我們將與您和上游維護者一起添加必要的支持。

供應商掛鉤

在過去的版本中,您可以將特定於供應商的修改直接添加到核心內核中。這在 GKI 2.0 中是不可能的,因為特定於產品的代碼必須在模塊中實現,並且不會在上游的核心內核或 ACK 中被接受。為了在對核心內核代碼影響最小的情況下啟用合作夥伴所依賴的增值功能,GKI 接受允許從核心內核代碼調用模塊的供應商掛鉤。此外,關鍵數據結構可以填充供應商數據字段,這些字段可用於存儲供應商特定數據以實現這些功能。

供應商掛鉤有兩種變體(正常和受限),它們基於供應商模塊可以附加到的跟踪點(不是跟踪事件)。例如,供應商可以在do_exit()中添加一個供應商模塊可以附加到處理的鉤子,而不是添加一個新的sched_exit()函數來在任務退出時進行記帳。示例實現包括以下供應商掛鉤。

  • 普通的供應商掛鉤使用DECLARE_HOOK()創建一個名為trace_ name的跟踪點函數,其中name是跟踪的唯一標識符。按照慣例,正常的供應商掛鉤名稱以android_vh開頭,因此sched_exit()掛鉤的名稱將是android_vh_sched_exit
  • 對於調度程序掛鉤等情況,即使 CPU 離線或需要非原子上下文也必須調用附加函數,因此需要受限供應商掛鉤。受限制的供應商掛鉤無法分離,因此附加到受限制掛鉤的模塊永遠無法卸載。只允許一個附件,因此任何其他附加嘗試都會失敗,並-EBUSY 。受限制的供應商掛鉤名稱以android_rvh開頭。

要添加供應商掛鉤,請在 IT 中提交問題並提交補丁(與所有 Android 特定補丁一樣,問題必須存在並且您必須提供理由)。對供應商掛鉤的支持僅在 ACK 中,因此不要將這些補丁發送到上游 Linux。

將供應商字段添加到結構

您可以通過使用ANDROID_VENDOR_DATA()宏添加android_vendor_data字段來將供應商數據與關鍵數據結構相關聯。例如,要支持增值功能,請將字段附加到結構中,如以下代碼示例所示。

為避免供應商所需字段與 OEM 所需字段之間的潛在衝突,OEM 不得使用使用ANDROID_VENDOR_DATA()宏聲明的字段。相反,OEM 必須使用ANDROID_OEM_DATA()來聲明android_oem_data字段。

#include <linux/android_vendor.h>
...
struct important_kernel_data {
  [all the standard fields];
  /* Create vendor data for use by hook implementations. The
   * size of vendor data is based on vendor input. Vendor data
   * can be defined as single u64 fields like the following that
   * declares a single u64 field named "android_vendor_data1" :
   */
  ANDROID_VENDOR_DATA(1);

  /*
   * ...or an array can be declared. The following is equivalent to
   * u64 android_vendor_data2[20]:
   */
  ANDROID_VENDOR_DATA_ARRAY(2, 20);

  /*
   * SoC vendors must not use fields declared for OEMs and
   * OEMs must not use fields declared for SoC vendors.
   */
  ANDROID_OEM_DATA(1);

  /* no further fields */
}

定義供應商掛鉤

通過使用DECLARE_HOOK()DECLARE_RESTRICTED_HOOK()將供應商掛鉤作為跟踪點添加到內核代碼中,然後將它們作為跟踪點添加到代碼中。例如,將trace_android_vh_sched_exit()添加到現有的do_exit()內核函數中:

#include <trace/hooks/exit.h>
void do_exit(long code)
{
    struct task_struct *tsk = current;
    ...
    trace_android_vh_sched_exit(tsk);
    ...
}

trace_android_vh_sched_exit()函數最初僅檢查是否附加了某些內容。但是,如果供應商模塊使用register_trace_android_vh_sched_exit()註冊處理程序,則調用註冊的函數。處理程序必須了解有關持有鎖、RCS 狀態和其他因素的上下文。掛鉤必須在include/trace/hooks目錄的頭文件中定義。

例如,以下代碼在文件include/trace/hooks/sched.h中給出了可能的trace_android_vh_sched_exit()聲明。

/* SPDX-License-Identifier: GPL-2.0 */
#undef TRACE_SYSTEM
#define TRACE_SYSTEM sched
#define TRACE_INCLUDE_PATH trace/hooks

#if !defined(_TRACE_HOOK_SCHED_H) || defined(TRACE_HEADER_MULTI_READ)
#define _TRACE_HOOK_SCHED_H
#include <trace/hooks/vendor_hooks.h>
/*
 * Following tracepoints are not exported in tracefs and provide a
 * mechanism for vendor modules to hook and extend functionality
 */

/* struct task_struct */
#include <linux/sched.h>

DECLARE_HOOK(android_vh_sched_exit,
             TP_PROTO(struct task_struct *p),
             TP_ARGS(p));

#endif /* _TRACE_HOOK_SCHED_H */

/* This part must be outside protection */
#include <trace/define_trace.h>

注意:鉤子聲明中使用的數據結構需要完全定義以保證 ABI 穩定性。否則,取消引用不透明指針或在大小上下文中使用結構是不安全的。上例中的#include <linux/sched.h>使struct task_struct的定義可用並啟用ABI 跟踪。

要實例化供應商掛鉤所需的接口,請將帶有掛鉤聲明的頭文件添加到drivers/android/vendor_hooks.c並導出符號。例如,以下代碼完成了android_vh_sched_exit()鉤子的聲明。

#define CREATE_TRACE_POINTS
#include <trace/hooks/vendor_hooks.h>
#include <trace/hooks/exit.h>
/*
 * Export tracepoints that act as a bare tracehook (i.e. have no trace
 * event associated with them) to allow external modules to probe
 * them.
 */
EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_sched_exit);

附加到供應商掛鉤

要使用供應商掛鉤,供應商模塊需要為掛鉤註冊一個處理程序(通常在模塊初始化期間完成)。例如,以下代碼顯示了用於trace_android_vh_sched_exit()的模塊foo.ko處理程序。

#include <trace/hooks/sched.h>
...
static void foo_sched_exit_handler(void *data, struct task_struct *p)
{
    foo_do_exit_accounting(p);
}
...
static int foo_probe(..)
{
    ...
    rc = register_trace_android_vh_sched_exit(foo_sched_exit, NULL);
    ...
}

核心內核特性

如果之前的技術都不能讓您從模塊中實現功能,那麼您必須將該功能作為 Android 特定的修改添加到核心內核。在問題跟踪器 (IT) 中創建問題以開始對話。

用戶應用程序編程接口 (UAPI)

  • UAPI 頭文件。UAPI 頭文件的更改必須在上游發生,除非更改是針對特定於 Android 的接口。使用供應商特定的頭文件來定義供應商模塊和供應商用戶空間代碼之間的接口。
  • sysfs 節點。不要將新的 sysfs 節點添加到 GKI 內核(此類添加僅在供應商模塊中有效)。由與 SoC 和設備無關的庫和構成 Android 框架的 Java 代碼使用的 sysfs 節點只能以兼容的方式進行更改,如果它們不是特定於 Android 的 sysfs 節點,則必須在上游進行更改。您可以創建供應商特定的 sysfs 節點以供供應商用戶空間使用。默認情況下,使用 SELinux 拒絕用戶空間訪問 sysfs 節點。由供應商添加適當的 SELinux 標籤以允許授權供應商軟件訪問。
  • DebugFS 節點。供應商模塊可以在debugfs中定義節點僅用於調試(因為在設備正常運行期間未安裝debugfs )。