GKIのカーネルコードを開発する

Generic Kernel Image(GKI)は、アップストリームのLinuxカーネルと緊密に連携することにより、カーネルの断片化を減らします。ただし、一部のパッチをアップストリームで受け入れることができない正当な理由があり、満たす必要のある製品スケジュールがあるため、一部のパッチは、GKIが構築されているAndroid Common Kernel(ACK)ソースで維持されます。

開発者は、最初の選択肢としてLinuxカーネルメーリングリスト(LKML)を使用してコード変更をアップストリームに送信し、アップストリームが実行可能でない強い理由がある場合にのみ、コード変更をandroid-mainlineブランチに送信する必要があります。正当な理由の例とその処理方法を以下に示します。

  • パッチはLKMLに提出されましたが、製品のリリースに間に合うように受け入れられませんでした。このパッチを処理するには:

    • パッチがLKMLに送信されたことの証拠と、パッチに対して受け取ったコメント、またはパッチがアップストリームに送信される予定の時間を提供します。
    • パッチをACKに配置し、アップストリームで承認され、最終的なアップストリームバージョンがACKにマージされたときに、ACKからパッチを削除するための一連のアクションを決定します。
  • パッチはベンダーモジュールのEXPORT_SYMBOLS_GPL()を定義しますが、そのシンボルを消費するツリー内モジュールがないため、アップストリームに送信できませんでした。このパッチを処理するには、モジュールをアップストリームに送信できない理由と、このリクエストを行う前に検討した代替案の詳細を提供してください。

  • パッチはアップストリームに対して十分に一般的ではなく、製品リリースの前にリファクタリングする時間はありません。このパッチを処理するには、リファクタリングされたパッチがアップストリームに送信される推定時間を指定します(レビューのためにリファクタリングされたパッチをアップストリームに送信する計画がない限り、パッチはACKで受け入れられません)。

  • パッチはアップストリームで受け入れることができません... <ここに理由を挿入> 。このパッチを処理するには、Androidカーネルチームに連絡し、パッチをリファクタリングして、レビューのために送信してアップストリームで受け入れることができるようにするオプションについて協力してください。

より多くの潜在的な正当化があります。バグまたはパッチを提出するときは、有効な理由を含め、繰り返しと議論を期待してください。 ACKにはいくつかのパッチが適用されることを認識しています。特に、GKIの初期段階では、全員がアップストリームでの作業方法を学習していますが、製品のスケジュールを緩和することはできません。アップストリームの要件は、時間の経過とともにより厳しくなると予想されます。

パッチ要件

パッチは、アップストリームに送信されるかACKに送信されるかにかかわらず、 Linuxソースツリーに記述されているLinuxカーネルコーディング標準に準拠している必要があります。 scripts/checkpatch.plスクリプトは、Gerritの事前送信テストの一部として実行されるため、事前に実行して、合格することを確認してください。送信前テストと同じ構成でチェックパッチスクリプトを実行するには、リポジトリチェックアウトからbuild/static_analysis/checkpatch_presubmit.sh repoを使用します。

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:に送信されたが、メンテナブランチにまだ受け入れられていないパッチは、パッチがアップストリームLinuxにあるかどうかに関係なく、パッチが受け入れられるほど正当な理由がない限り、受け入れられる可能性は低いです(そうではないこと)。 Androidカーネルチームとの話し合いを容易にするために、 FROMLISTパッチに関連する問題が存在する必要があります。

Android固有のパッチ

必要な変更をアップストリームに配置できない場合は、ツリー外のパッチをACKに直接送信してみてください。ツリー外のパッチを送信するには、パッチを引用する問題と、パッチをアップストリームに送信できない理由をITで作成する必要があります(例については、前のリストを参照してください)。ただし、コードをアップストリームに送信できない場合がいくつかあります。これらのケースは次のようにカバーされており、Android固有のパッチの投稿ガイドラインに従い、件名にANDROID:プレフィックスを付ける必要があります。

gki_defconfigへの変更

CONFIGがアーキテクチャ固有でない限り、gki_defconfigに対するすべてのCONFIGの変更は、 gki_defconfigバージョンと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.gkiselectステートメントを追加して、 gki_defconfigで有効になっているCONFIG_GKI_HACKS_TO_FIXカーネル構成に基づいて自動的に選択されるようにします。このメカニズムは、非表示の構成にのみ使用してください。構成が非表示になっていない場合は、 gki_defconfigで明示的にまたは依存関係として指定する必要があります。

ロード可能なガバナー

ロード可能なガバナーをサポートするカーネルフレームワーク( cpufreqなど)の場合、デフォルトのガバナー( cpufreqschedutilガバナーなど)をオーバーライドできます。ロード可能なガバナーまたはドライバーをサポートしないフレームワーク(サーマルフレームワークなど)の場合は、ベンダー固有の実装、ITで問題を作成し、 Androidカーネルチームに相談してください。

私たちはあなたと上流のメンテナと協力して、必要なサポートを追加します。

ベンダーフック

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

ベンダーフックには、ベンダーモジュールが接続できるトレースポイント(トレースイベントではない)に基づく2つのバリエーション(通常と制限付き)があります。たとえば、タスクの終了時にアカウンティングを行うために新しいsched_exit()関数を追加する代わりに、ベンダーは、ベンダーモジュールが処理のためにアタッチできるdo_exit()にフックを追加できます。実装例には、次のベンダーフックが含まれます。

  • 通常のベンダーフックは、 DECLARE_HOOK()を使用して、 trace_ name名前のトレースポイント関数を作成します。ここで、 nameはトレースの一意の識別子です。慣例により、通常のベンダーフック名はandroid_vhで始まるため、 sched_exit()フックの名前はandroid_vh_sched_exitになります。
  • 制限されたベンダーフックは、CPUがオフラインであるか、非アトミックコンテキストを必要とする場合でも、アタッチされた関数を呼び出す必要があるスケジューラフックなどの場合に必要です。制限付きベンダーフックは切り離せないため、制限付きフックに接続しているモジュールをアンロードすることはできません。アタッチは1つしか許可されないため、他のアタッチの試行は-EBUSYで失敗します。制限されたベンダーフック名は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/sched.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 */
#include <linux/sched.h>

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>

:ABIの安定性を保証するには、フック宣言内で使用されるデータ構造を完全に定義する必要があります。そうしないと、不透明なポインターを逆参照したり、サイズ設定されたコンテキストで構造体を使用したりすることは安全ではありません。上記の例の#include <linux/sched.h>は、 struct task_structの定義を使用可能にし、ABI追跡を有効にします。

ベンダーフックに必要なインターフェースをインスタンス化するには、フック宣言を含むヘッダーファイルをdrivers/android/vendor_hooks.cに追加し、シンボルをエクスポートします。たとえば、次のコードはandroid_vh_sched_exit()フックの宣言を完了します。

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

ベンダーフックに接続する

ベンダーフックを使用するには、ベンダーモジュールはフックのハンドラーを登録する必要があります(通常はモジュールの初期化中に行われます)。たとえば、次のコードは、 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, NULL);
    ...
}

コアカーネル機能

これまでの手法のいずれでもモジュールから機能を実装できない場合は、その機能をAndroid固有の変更としてコアカーネルに追加する必要があります。課題トラッカー(IT)で課題を作成して、会話を開始します。

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

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