為 GKI 開發核心程式碼

通用核心映像檔 (GKI) 會與上游 Linux 核心緊密配合,藉此減少核心分散現象。不過,有些修補程式無法在上游接受,這有其合理原因,而且必須遵守產品時程,因此有些修補程式會在 Android 通用核心 (ACK) 來源中維護,而 GKI 就是從中建構。

開發人員必須以 Linux Kernel Mailing List (LKML) 為首選,將程式碼變更提交至上游,並且只有在有充分理由認為上游無法運作時,才將程式碼變更提交至 ACK android-mainline 分支。有效原因和處理方式示例如下:

  • 修補程式已提交至 LKML,但未在產品發布時接受。如要處理這個修補程式:

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

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

  • 修補程式無法接受上游,因為... <插入原因>。如要處理這個修補程式,請與 Android 核心團隊聯絡,並與我們合作,選擇重構修補程式的選項,以便提交供上游審查及接受。

還有許多其他可能的理由。提交錯誤或修補程式時,請附上有效的理由,並預期會進行一些重複作業和討論。我們瞭解 ACK 會提供一些修補程式,尤其是在 GKI 的初期階段,因為大家都在學習如何進行上游工作,但無法放寬產品時間表。請注意,上游要求會隨著時間而變得更嚴格。

修補需求

無論是提交至上游還是 ACK,修補程式都必須符合 Linux 來源樹狀結構中所述的 Linux kernel 程式碼標準。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: - 如果有合理的用途,來自上游的修補程式即使無法精準挑選,且需要修改,也可能會獲得核准。
  • FROMGIT: - 如果有即將到來的截止日期,我們可能會接受從維護者分支中挑選出來的修補程式,以便提交至 Linux 主線。內容和播出時間表都必須符合規定。
  • FROMLIST: - 已提交至 LKML 但尚未納入維護者分支的修補程式,不太可能會被接受,除非有充分理由,不論修補程式是否會納入 Linux 上游,都會接受修補程式 (我們假設不會)。必須與 FROMLIST 修補程式相關聯的問題,才能與 Android 核心團隊進行討論。

Android 專屬修補程式

如果無法在上游完成必要的變更,您可以嘗試直接將樹外修補程提交至 ACK。如要提交樹狀結構外修補程式,您必須在 IT 中建立問題,並在其中引用修補程式,以及為何無法將修補程式提交至上游的理由 (請參閱前述清單中的範例)。不過,在某些情況下,程式碼無法提交至上游。這些情況如下所述,且必須遵循 Android 專屬修補程式的貢獻規範,並在主旨中加上 ANDROID: 前置字元。

gki_defconfig 的變更

除非 CONFIG 是特定架構,否則 gki_defconfig 的所有 CONFIG 變更都必須套用至 arm64 和 x86 版本。如要要求變更 CONFIG 設定,請在 IT 中建立問題,以便討論變更。任何在凍結後影響核心模組介面 (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 陳述式,以便系統根據 gki_defconfig 中啟用的 CONFIG_GKI_HACKS_TO_FIX 核心設定自動選取。請僅針對隱藏的設定使用此機制;如果設定未隱藏,則必須在 gki_defconfig 中明確指定,或設為依附元件。

可載入的調節器

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

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

供應商掛鉤

在先前的版本中,您可以直接將供應商專屬修改內容新增至核心核心。但在 GKI 2.0 中無法實現這項功能,因為產品專屬程式碼必須在模組中實作,且不會在核心核心或 ACK 中接受。為在對核心核心程式碼影響降到最低的情況下,啟用合作夥伴所需的加值功能,GKI 會接受供應商掛鉤,讓您可從核心核心程式碼叫用模組。此外,您也可以在主要資料結構中填入供應商資料欄位,用於儲存供應商專屬資料,以便實作這些功能。

供應商掛鉤有兩種變化版本 (一般和受限),這些版本是根據供應商模組可附加的追蹤點 (而非追蹤事件) 而定。舉例來說,供應商可以選擇在 do_exit() 中新增鉤子,讓供應商模組可附加至該鉤子進行處理,而非新增新的 sched_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 欄位,將供應商資料與主要資料結構建立關聯。舉例來說,如要支援附加價值功能,請在結構體中附加欄位,如以下程式碼範例所示。

為避免供應商所需的欄位與原始設備製造商 (OEM) 所需的欄位之間發生衝突,OEM 不得使用 ANDROID_VENDOR_DATA() 巨集宣告的欄位。相反地,原始設備製造商必須使用 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 穩定性,您必須完整定義在掛鉤宣告中使用的資料結構。否則,在大小限制的情況下,解參照不透明指標或使用結構體是不安全的。提供這類資料結構完整定義的 include 應放在 drivers/android/vendor_hooks.c#ifndef __GENKSYMS__ 部分。include/trace/hooks 中的標頭檔案不應包含含有類別定義的核心標頭檔案,以免 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 節點。請勿在 GKI 核心中新增 sysfs 節點 (這類新增項目僅適用於供應商模組)。由 SoC 和裝置無關的程式庫,以及組成 Android 架構的 Java 程式碼所使用的 sysfs 節點,只能以相容的方式變更,且如果不是 Android 專屬的 sysfs 節點,則必須在上游變更。您可以建立供應商專用的 sysfs 節點,供供應商使用者空間使用。根據預設,使用者空間會透過 SELinux 拒絕存取 sysfs 節點。供應商必須自行新增適當的 SELinux 標籤,以便授權供應商軟體存取。
  • DebugFS 節點。供應商模組只能在 debugfs 中定義節點,用於偵錯 (因為 debugfs 不會在裝置的正常運作期間掛載)。