為 GKI 開發核心程式碼

通用核心映像檔 (GKI) 與上游 Linux 核心密切配合,可減少核心片段化。不過,有些修補程式無法接受上游,這是合理的,而且必須遵守產品時間表,因此部分修補程式會保留在 Android Common Kernel (ACK) 來源中,GKI 就是從這些來源建構而來。

開發人員必須優先使用 Linux Kernel Mailing List (LKML) 向上游提交程式碼變更,只有在有充分理由認為上游不可行時,才向 ACK android-mainline 分支提交程式碼變更。以下列舉有效原因和處理方式。

  • 該修補程式已提交至 LKML,但未及時通過審核,因此無法在產品發布時採用。如何處理這項修補程式:

    • 請提供證據,證明修補程式已提交至 LKML,並收到修補程式的留言,或提供修補程式提交至上游的預估時間。
    • 決定在 ACK 中套用修補程式的行動方案,取得上游核准,然後在最終上游版本合併至 ACK 時,將修補程式從 ACK 中移除。
  • 修補程式為供應商模組定義 EXPORT_SYMBOLS_GPL(),但由於沒有使用該符號的樹狀結構內模組,因此無法向上游提交。如要處理這項修補程式,請提供詳細資料,說明您的模組為何無法向上游提交,以及您在提出這項要求前考慮的替代方案。

  • 修補程式不夠通用,無法上游,且在產品發布前沒有時間重構。如要處理這個修補程式,請提供預計將重構修補程式提交至上游的時間 (如果沒有將重構修補程式提交至上游以供審查的計畫,ACK 就不會接受該修補程式)。

  • 上游無法接受修補程式,因為... <insert reason here>。如要處理這個修補程式,請與 Android 核心團隊聯絡,並與我們合作重新設計修補程式,以便提交審查並在上游接受。

還有許多其他可能的理由。提交錯誤或修補程式時,請附上正當理由,並預期會經過一些反覆討論。我們瞭解 ACK 帶有一些修補程式,尤其是在 GKI 的早期階段,大家都在學習如何上游作業,但無法放寬產品時間表來執行作業。預期上游作業要求會隨著時間變得更加嚴格。

修補程式需求

無論是提交至上游或 ACK,修補程式都必須符合 Linux 來源樹狀結構所述的 Linux 核心編碼標準。scripts/checkpatch.pl 指令碼會做為 Gerrit 預先提交測試的一部分執行,因此請預先執行,確保指令碼通過測試。如要使用與預先提交測試相同的設定執行 checkpatch 指令碼,請使用 //build/kernel/static_analysis:checkpatch_presubmit。 詳情請參閱 build/kernel/kleaf/docs/checkpatch.md

ACK 修補程式

提交至 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: - 如果有合理的用途,上游的修補程式無法順利進行 Cherrypick,且需要修改,也可能獲得接受。
  • FROMGIT: - 如果有即將到來的期限,從維護人員分支版本中挑選的修補程式可能會獲准,以準備提交至 Linux 主線。內容和時間表都必須有正當理由。
  • FROMLIST: - 提交至 LKML 但尚未納入維護人員分支的修補程式不太可能獲得接受,除非理由充分,足以讓修補程式無論是否納入上游 Linux 都會獲得接受 (我們假設不會)。必須有與 FROMLIST 修補程式相關的問題,才能與 Android 核心團隊討論。

Android 專屬修補程式

如果無法在上游進行必要變更,您可以嘗試直接向 ACK 提交樹外修補程式。提交樹外修補程式時,您必須在 IT 中建立問題,並引用修補程式和理由,說明修補程式無法向上游提交的原因 (請參閱先前的範例清單)。不過,在某些情況下,程式碼無法向上游提交。這些情況的處理方式如下,且必須遵循 Android 專屬修補程式的貢獻內容指南,並在主旨中加上 ANDROID: 前置字元。

gki_defconfig 的變更

除非 CONFIG 是架構專屬,否則所有對 gki_defconfigCONFIG 變更都必須套用至 arm64 和 x86 版本。如要要求變更 CONFIG 設定,請在 IT 中建立問題,討論變更事宜。在 KMI 凍結後,任何會影響 KMI 的變更都會遭到拒絕。CONFIG如果合作夥伴為單一設定要求相互衝突的設定,我們會透過相關錯誤的討論解決衝突。

上游不存在的程式碼

已針對 Android 進行的程式碼修改無法傳送至上游。 舉例來說,即使繫結器驅動程式是在上游維護,但繫結器驅動程式優先順序繼承功能的修改內容無法傳送至上游,因為這些修改內容是 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_SOF=y,系統會自動選取 CONFIG_SND_SOC_TOPOLOGY。為配合樹外模組建構作業,GKI 內含可啟用隱藏設定的機制。

如要啟用隱藏設定,請在 init/Kconfig.gki 中新增 select 陳述式,以便根據 CONFIG_GKI_HACKS_TO_FIX 核心設定自動選取設定 (該設定已在 gki_defconfig 中啟用)。這個機制只能用於隱藏設定;如果設定未隱藏,則必須在 gki_defconfig 中明確指定或做為依附元件。

可載入的調控器

對於支援可載入調控器的核心架構 (例如 cpufreq),您可以覆寫預設調控器 (例如 cpufreqschedutil 調控器)。對於不支援可載入調控器或驅動程式,但仍需要供應商專屬實作的架構 (例如熱能架構),請在 IT 中建立問題,並諮詢 Android 核心團隊

我們會與您和上游維護人員合作,加入必要的支援。

供應商掛鉤

在過去的版本中,您可以直接在核心核心中新增供應商專屬的修改項目。GKI 2.0 無法做到這一點,因為產品專屬程式碼必須在模組中實作,且不會在核心上游或 ACK 中接受。為啟用合作夥伴依賴的加值功能,同時盡量減少對核心核心程式碼的影響,GKI 接受供應商掛鉤,允許從核心核心程式碼叫用模組。此外,主要資料結構可填入供應商資料欄位,用來儲存供應商專屬資料,以實作這些功能。

供應商掛鉤有兩種變體 (一般和受限),是根據供應商模組可附加的追蹤點 (而非追蹤事件) 而定。舉例來說,供應商不必新增 sched_exit() 函式來在工作結束時執行會計作業,而是可以在 do_exit() 中新增掛鉤,供供應商模組附加以進行處理。導入範例包含下列供應商掛鉤。

  • 一般供應商掛鉤會使用 DECLARE_HOOK() 建立名為 trace_name 的追蹤點函式,其中 name 是追蹤的專屬 ID。按照慣例,一般供應商掛鉤名稱會以 android_vh 開頭,因此 sched_exit() 掛鉤的名稱會是 android_vh_sched_exit
  • 在排程器掛鉤等情況下,即使 CPU 離線或需要非不可分割的內容,也必須呼叫附加函式,因此需要受限的供應商掛鉤。受限制的廠商掛鉤無法分離,因此附加至受限制掛鉤的模組永遠無法卸載。受限制的供應商掛鉤名稱開頭為 android_rvh

如要新增供應商掛鉤,請在 IT 中提交問題並提供修補程式 (與所有 Android 專屬修補程式一樣,必須存在問題,且您必須提供理由)。供應商掛鉤僅支援 ACK,因此請勿將這些修補程式傳送至上游 Linux。

在結構中新增供應商欄位

您可以透過 ANDROID_VENDOR_DATA() 巨集新增 android_vendor_data 欄位,將供應商資料與重要資料結構建立關聯。舉例來說,如要支援加值功能,請將欄位附加至結構,如下列程式碼範例所示。

為避免供應商所需欄位與原始設備製造商所需欄位之間發生潛在衝突,原始設備製造商絕不得使用以 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/exit.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;

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>

如要例項化供應商掛鉤所需的介面,請將含有掛鉤宣告的標頭檔案新增至 drivers/android/vendor_hooks.c,並匯出符號。舉例來說,下列程式碼會完成 android_vh_sched_exit() 鉤子的宣告。

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

#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);

注意:為確保 ABI 穩定性,必須完整定義在 Hook 宣告中使用的資料結構。否則,取消參照不透明指標或在大小環境中使用結構體是不安全的。提供這類資料結構完整定義的 include 應位於 drivers/android/vendor_hooks.c#ifndef __GENKSYMS__ 區段內。include/trace/hooks 中的標頭檔案不應包含具有型別定義的 Kernel 標頭檔案,以免 CRC 變更導致 KMI 損壞。請改為轉送宣告型別。

附加至供應商掛鉤

如要使用供應商掛鉤,供應商模組必須註冊掛鉤的處理常式 (通常是在模組初始化期間完成)。舉例來說,下列程式碼會顯示 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_handler, NULL);
    ...
}

使用標頭檔案中的供應商掛鉤

如要使用標頭檔案中的供應商掛鉤,您可能需要更新供應商掛鉤標頭檔案,取消定義 TRACE_INCLUDE_PATH,以免發生建構錯誤,指出系統找不到追蹤點標頭檔案。例如:

In file included from .../common/init/main.c:111:
In file included from .../common/include/trace/events/initcall.h:74:
.../common/include/trace/define_trace.h:95:10: fatal error: 'trace/hooks/initcall.h' file not found
   95 | #include TRACE_INCLUDE(TRACE_INCLUDE_FILE)
      |          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.../common/include/trace/define_trace.h:90:32: note: expanded from macro 'TRACE_INCLUDE'
   90 | # define TRACE_INCLUDE(system) __TRACE_INCLUDE(system)
      |                                ^~~~~~~~~~~~~~~~~~~~~~~
.../common/include/trace/define_trace.h:87:34: note: expanded from macro '__TRACE_INCLUDE'
   87 | # define __TRACE_INCLUDE(system) __stringify(TRACE_INCLUDE_PATH/system.h)
      |                                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.../common/include/linux/stringify.h:10:27: note: expanded from macro '__stringify'
   10 | #define __stringify(x...)       __stringify_1(x)
      |                                 ^~~~~~~~~~~~~~~~
.../common/include/linux/stringify.h:9:29: note: expanded from macro '__stringify_1'
    9 | #define __stringify_1(x...)     #x
      |                                 ^~
<scratch space>:14:1: note: expanded from here
   14 | "trace/hooks/initcall.h"
      | ^~~~~~~~~~~~~~~~~~~~~~~~
1 error generated.

如要修正這類建構錯誤,請對您要加入的供應商掛鉤標頭檔案套用同等修正。詳情請參閱 https://r.android.com/3066703

diff --git a/include/trace/hooks/mm.h b/include/trace/hooks/mm.h
index bc6de7e53d66..039926f7701d 100644
--- a/include/trace/hooks/mm.h
+++ b/include/trace/hooks/mm.h
@@ -2,7 +2,10 @@
 #undef TRACE_SYSTEM
 #define TRACE_SYSTEM mm

+#ifdef CREATE_TRACE_POINTS
 #define TRACE_INCLUDE_PATH trace/hooks
+#define UNDEF_TRACE_INCLUDE_PATH
+#endif

定義 UNDEF_TRACE_INCLUDE_PATH 會告知 include/trace/define_trace.h 在建立追蹤點後取消定義 TRACE_INCLUDE_PATH

核心功能

如果上述任何技術都無法讓您實作模組中的功能,則必須將該功能新增為核心核心的 Android 特定修改項目。在問題追蹤工具 (IT) 中建立問題,即可開始對話。

使用者應用程式設計介面 (UAPI)

  • UAPI 標頭檔案。除非變更的是 Android 專屬介面,否則對 UAPI 標頭檔案的變更必須發生在上游。使用廠商專屬標頭檔,定義廠商模組與廠商使用者空間程式碼之間的介面。
  • sysfs 節點。請勿將新的 sysfs 節點新增至 GKI 核心 (這類新增項目僅在供應商模組中有效)。如果 sysfs 節點並非 Android 專屬,則只能以相容的方式變更,且必須在上游變更。這些節點是由 SoC 和裝置無關的程式庫和 Java 程式碼所使用,而這些程式碼構成 Android 架構。您可以建立供應商專屬的 sysfs 節點,供供應商使用者空間使用。根據預設,系統會使用 SELinux 拒絕使用者空間存取 sysfs 節點。供應商必須新增適當的 SELinux 標籤,授權供應商軟體存取。
  • DebugFS 節點。供應商模組可以在 debugfs 中定義節點,但僅供偵錯 (因為裝置正常運作時不會掛接 debugfs)。