開發 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 中建立一個問題討論變更。任何 CONFIG 變更,如果會影響凍結後的核心模組介面 (KMI),都會遭到拒絕。如果合作夥伴針對單一設定要求互相衝突的設定,我們會討論相關錯誤,解決衝突問題。

上游不存在的程式碼

您無法將已針對 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() 巨集宣告的欄位。原始設備製造商 (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 穩定性,您必須完整定義在掛鉤宣告中使用的資料結構。否則,在大小限制的情況下,解參照不透明指標或使用結構體是不安全的。提供此類資料結構完整定義的 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 節點。請勿將新的 sysfs 節點新增至 GKI 核心 (此類新增功能僅在供應商模組中有效)。由 SoC 和各裝置通用程式庫使用的 sysfs 節點和構成 Android 架構的 Java 程式碼只能透過相容方式變更,且若非 Android 專屬 sysf 節點,則必須變更上游。您可以建立供應商專用的 sysfs 節點,供供應商使用者空間使用。根據預設,透過 SELinux 存取 sysfs 節點時,使用者空間將遭到拒絕。供應商可自行新增適當的 SELinux 標籤,以便授權供應商軟體存取。
  • DebugFS 節點。供應商模組只能在 debugfs 中定義節點,以便進行偵錯 (因為 debugfs 不會在裝置正常作業期間掛接)。