GKI のカーネルコードの開発

汎用カーネル イメージ(GKI)は、アップストリームの Linux カーネルとの連携を密にすることでカーネルの断片化を軽減します。しかし、一部のパッチは正当な理由によりアップストリームで受理されない場合があります。製品スケジュールとの兼ね合いもあるため、一部のパッチは GKI のビルドに使用する Android 共通カーネル(ACK)ソースで管理されます。

デベロッパーがコード変更を提出する際は、Linux カーネル メーリング リスト(LKML)を使用することを第一の選択肢とすべきです。ACK android-mainline ブランチへの提出は、明確な理由によりアップストリームが不都合である場合に限定してください。正当な理由の例と取り扱い方法を以下に示します。

  • パッチを LKML に提出しましたが、その受理が製品リリースに間に合いませんでした。このパッチは次のように取り扱います。

    • パッチを LKML に提出したことを示す証拠と、パッチに対して受け取ったコメント、またはパッチがアップストリームに提出される予定期日を提示します。
    • パッチを ACK に組み込み、アップストリームで承認され、最終的なアップストリーム バージョンが ACK にマージされたら ACK から取り除くことにします。
  • ベンダー モジュール向けの EXPORT_SYMBOLS_GPL() を定義するパッチですが、そのシンボルを使用するツリー内モジュールがないため、アップストリームに提出できませんでした。このパッチについては、モジュールをアップストリームに提出できない理由と、このリクエストを行う前に検討した代替手段について詳しく説明します。

  • アップストリーム向けとしては汎用性が低いパッチで、製品リリース前にリファクタリングする時間もありません。このパッチについては、リファクタリングされたパッチがアップストリームに提出される予定期日を提示します(リファクタリングされたパッチがアップストリームに提出されてレビューを受ける予定がなければ、ACK に受理されません)。

  • パッチがアップストリームで受理できません。その理由は、<ここに理由を挿入>です。このパッチについては、Android カーネルチームに連絡し、アップストリームに提出して、レビュー用として受理されるよう、協力してパッチのリファクタリングを行います。

正当な理由は他にも多数あります。バグやパッチを提出する際は、正当な理由を記載し、イテレーションとディスカッションを想定してください。特に GKI の初期段階では、誰もがアップストリームでの作業方法を学んでいる最中でありながら、製品スケジュールを延ばすことはできないため、パッチが ACK に収容されることになると認識しています。アップストリームの要件は時間とともにより厳しくなると想定しておいてください。

パッチの要件

パッチは、提出先がアップストリームと ACK のどちらの場合でも、Linux ソースツリーに記載されている Linux カーネル コーディング基準に準拠している必要があります。scripts/checkpatch.pl スクリプトは、Gerrit presubmit テストの一環として実行されるため、前もって実行して合格することを確認してください。presubmit テストと同じ設定で 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 にマージされます。
  • アップストリームに受理されなかった場合、アップストリームへの提出の参照か、LKML に提出しなかった理由の説明を添えて android-mainline に送信します。

アップストリームまたは 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 に組み込まれるかどうかにかかわらず、そのパッチを受理する説得力のある正当な理由がない限り、受理される可能性は低くなります(受理されないことの方が多くなります)。Android カーネルチームとの議論を促進するために、FROMLIST パッチに関連付けられたイシューが必要です。

Android 固有のパッチ

必要な変更をアップストリームに組み込めない場合、ツリー外のパッチを ACK に直接提出してみてください。ツリー外のパッチを提出するには、パッチとそのパッチをアップストリームに提出できない理由を引用するイシューを IT で作成する必要があります(例については前述のリストをご覧ください)。しかし、アップストリームにコードを提出できない場合もあります。そのような場合については、以下で説明しますが、Android 固有のパッチに関するコントリビューション ガイドラインに沿って、件名の先頭に「ANDROID:」を付ける必要があります。

gki_defconfig の変更

gki_defconfig に対する CONFIG の変更はすべて、CONFIG がアーキテクチャ固有である場合を除き、arm64 と x86 の両方のバージョンに適用する必要があります。CONFIG 設定の変更をリクエストするには、変更について話し合うために IT でイシューを作成してください。フリーズ後のカーネル モジュール インターフェース(KMI)に影響する CONFIG の変更は却下されます。パートナーが 1 つの構成で競合する設定をリクエストする場合は、関連するバグについての議論を通じて競合を解決します。

アップストリームに存在しないコード

すでに Android 固有となっているコードの変更は、アップストリームに提出できません。たとえば、バインダ ドライバがアップストリームでメンテナンスされていても、バインダ ドライバの優先度継承機能の変更は Android 固有のものであるため、アップストリームに提出できません。コードをアップストリームに提出できない理由をバグとパッチに明示してください。可能であれば、ACK でメンテナンスされるツリー外のコードの量を最小限に抑えるために、アップストリームで送信できるパッチと、アップストリームで送信できない Android 固有の部分に分割します。

このカテゴリにおけるその他の変更には、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.gkiselect ステートメントを追加します。これにより、gki_defconfig で有効にした CONFIG_GKI_HACKS_TO_FIX カーネル設定に基づいて、隠し設定が自動的に選択されます。このメカニズムは隠し設定にのみ使用します。隠し設定になっていない設定は、gki_defconfig で、明示的にまたは依存関係として指定する必要があります。

ローダブル ガバナー

ローダブル ガバナーをサポートするカーネル フレームワーク(cpufreq など)では、デフォルトのガバナー(cpufreqschedutil ガバナーなど)をオーバーライドできます。ローダブル ガバナーやローダブル ドライバをサポートしていないものの、ベンダー固有の実装が必要なフレームワーク(サーマル フレームワークなど)については、IT でイシューを作成し、Android カーネルチームにご相談ください。

当チームが皆さんやアップストリームの管理者と協力して必要なサポートを追加します。

ベンダーフック

過去のリリースでは、ベンダー固有の変更をコアカーネルに直接追加できましたが、GKI 2.0 ではできなくなりました。プロダクト固有のコードはモジュール内で実装する必要があり、アップストリームのコアカーネルや ACK では受理されないためです。パートナーがコアカーネル コードへの影響を最小限に抑えながら付加価値機能を利用できるよう、GKI ではコアカーネル コードからモジュールを呼び出すことが可能なベンダーフックを使用できます。さらに、こうした機能に使用するベンダー固有のデータを保存できるベンダー データ フィールドを主要なデータ構造に埋め込むことが可能です。

ベンダーフックには、ベンダー モジュールをアタッチできるトレースポイント(トレース イベントではない)によって、2 つの種類があります(通常のベンダーフックと制限付きのベンダーフック)。たとえば、ベンダーは、タスクの終了時にアカウンティングを行う新しい 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__ セクション内に含める必要があります。KMI の互換性を損なうような CRC の変更を避けるため、include/trace/hooks のヘッダー ファイルには、型定義を含むカーネル ヘッダー ファイルを含めないでください。代わりに、型を前方宣言します。

ベンダーフックにアタッチする

ベンダーフックを使用するには、ベンダー モジュールでフックのハンドラを登録する必要があります(通常、モジュールの初期化中に行います)。次のコードは、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 固有の変更として、その機能をコアカーネルに追加する必要があります。Issue Tracker(IT)でイシューを作成し、検討事項とします。

ユーザー アプリケーション プログラミング インターフェース(UAPI)

  • UAPI ヘッダー ファイルUAPI ヘッダー ファイルに対する変更は、Android 固有のインターフェースに対する変更である場合を除き、アップストリームで行う必要があります。ベンダー固有のヘッダー ファイルを使用して、ベンダー モジュールとベンダーのユーザー空間コードとの間のインターフェースを定義してください。
  • sysfs ノード。GKI カーネルに新しい sysfs ノードを追加しないでください(このような追加はベンダー モジュールでのみ有効です)。SoC やデバイスに依存しないライブラリで使用される sysfs ノード、および Android フレームワークを構成する Java コードで使用される sysfs ノードは、互換性のある変更のみ可能であり、Android 固有の sysfs ノードでない場合はアップストリームで変更する必要があります。ベンダーのユーザー空間で使用するベンダー固有の sysfs ノードは作成可能です。デフォルトで、ユーザー空間からの sysfs ノードへのアクセスは、SELinux を使って拒否されます。適切な SELinux ラベルを追加して、承認済みのベンダー ソフトウェアからのアクセスを許可するかどうかの判断は、ベンダーに任せられています。
  • DebugFS ノード。ベンダー モジュールでは、デバッグ時に限り、debugfs にノードを定義できます(デバイスの通常動作中は debugfs がマウントされないため)。