為 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 腳本,請使用//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(例如用於修復 Android 特定程式碼的補丁的android12-5.4android11-5.4 )。提交到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_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是追蹤的唯一識別碼。按照慣例,正常的供應商鉤子名稱以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 穩定性,需要完整定義鉤子聲明中使用的資料結構。否則,取消引用不透明指標或在大小上下文中使用該結構是不安全的。提供此類資料結構的完整定義的包含內容應位於drivers/android/vendor_hooks.c#ifndef __GENKSYMS__部分內。 include/trace/hooks中的頭文件不應包含具有類型定義的核心頭文件,以避免破壞 KMI 的 CRC 變更。而是轉發聲明類型。

附加到供應商掛鉤

要使用供應商掛鉤,供應商模組需要為掛鉤註冊一個處理程序(通常在模組初始化期間完成)。例如,以下程式碼顯示了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);
    ...
}

核心內核特性

如果前面的技術都無法讓您從模組實現某個功能,那麼您必須將該功能作為 Android 特定的修改添加到核心核心中。在問題追蹤器 (IT) 中建立問題以開始對話。

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

  • UAPI 頭檔。UAPI 頭檔的變更必須發生在上游,除非更改是針對 Android 特定的介面。使用供應商特定的頭檔來定義供應商模組和供應商使用者空間程式碼之間的介面。
  • sysfs 節點。不要向 GKI 核心新增新的 sysfs 節點(此類新增僅在供應商模組中有效)。與 SoC 和裝置無關的函式庫以及構成 Android 框架的 Java 程式碼所使用的 sysfs 節點只能以相容的方式進行更改,而且如果它們不是特定於 Android 的 sysfs 節點,則必須在上游進行更改。您可以建立供應商特定的 sysfs 節點以供供應商使用者空間使用。預設情況下,使用 SELinux 會拒絕使用者空間對 sysfs 節點的存取。由供應商添加適當的 SELinux 標籤以允許授權供應商軟體進行存取。
  • DebugFS 節點。供應商模組可以在debugfs中定義節點,僅用於調試(因為在設備正常操作期間不會安裝debugfs )。