Kernel-Code für GKI entwickeln

Mit Sammlungen den Überblick behalten Sie können Inhalte basierend auf Ihren Einstellungen speichern und kategorisieren.

Das Generic Kernel Image (GKI) reduziert die Kernel-Fragmentierung, indem es sich eng an den Upstream-Linux-Kernel anpasst. Es gibt jedoch triftige Gründe, warum einige Patches nicht vom Upstream akzeptiert werden können, und es gibt Produktzeitpläne, die eingehalten werden müssen, sodass einige Patches in den Quellen des Android Common Kernel (ACK) verwaltet werden, aus denen die GKI erstellt wird.

Entwickler müssen Codeänderungen an den Upstream senden, indem sie die Linux Kernel Mailing List (LKML) als erste Wahl verwenden, und Codeänderungen nur dann an den ACK android-mainline Zweig senden, wenn es einen triftigen Grund gibt, warum Upstream nicht realisierbar ist. Beispiele für triftige Gründe und deren Handhabung sind im Folgenden aufgeführt.

  • Der Patch wurde bei LKML eingereicht, aber nicht rechtzeitig für eine Produktfreigabe akzeptiert. So handhaben Sie diesen Patch:

    • Stellen Sie Nachweise bereit, dass der Patch an LKML übermittelt wurde, und geben Sie Kommentare zum Patch oder eine geschätzte Zeit an, bis zu der der Patch an den Upstream übermittelt wird.
    • Entscheiden Sie sich für eine Vorgehensweise, um den Patch in ACK zu landen, ihn vom Upstream genehmigen zu lassen und ihn dann aus ACK zu entfernen, wenn die endgültige Upstream-Version in ACK zusammengeführt wird.
  • Der Patch definiert EXPORT_SYMBOLS_GPL() für ein Herstellermodul, konnte aber nicht stromaufwärts eingereicht werden, da es keine In-Tree-Module gibt, die dieses Symbol verbrauchen. Um diesen Patch zu handhaben, geben Sie Details darüber an, warum Ihr Modul nicht an den Upstream gesendet werden kann, und geben Sie die Alternativen an, die Sie in Betracht gezogen haben, bevor Sie diese Anfrage gestellt haben.

  • Der Patch ist nicht generisch genug für Upstream und es ist keine Zeit, ihn vor einer Produktveröffentlichung zu überarbeiten. Um diesen Patch zu handhaben, geben Sie eine geschätzte Zeit an, bis zu der ein umgestalteter Patch an den Upstream gesendet wird (der Patch wird nicht in ACK akzeptiert, wenn nicht geplant ist, einen umgestalteten Patch an den Upstream zur Überprüfung zu senden).

  • Der Patch kann von den Originalautoren nicht akzeptiert werden, weil... <Grund hier einfügen> . Um diesen Patch zu handhaben, wenden Sie sich an das Android-Kernel-Team und arbeiten Sie mit uns an Optionen zur Umgestaltung des Patches, damit er zur Überprüfung eingereicht und vom Upstream akzeptiert werden kann.

Es gibt noch viel mehr mögliche Rechtfertigungen. Wenn Sie Ihren Fehler oder Patch einreichen, fügen Sie eine gültige Begründung bei und erwarten Sie einige Iterationen und Diskussionen. Wir sind uns bewusst, dass das ACK einige Patches enthalten wird, insbesondere in den frühen Phasen von GKI, während alle lernen, wie man vorgelagert arbeitet, aber die Produktpläne nicht lockern können, um dies zu tun. Erwarten Sie, dass die Upstreaming-Anforderungen im Laufe der Zeit strenger werden.

Patch-Anforderungen

Patches müssen den im Linux- Quellbaum beschriebenen Linux-Kernel-Codierungsstandards entsprechen, unabhängig davon, ob sie Upstream oder ACK übermittelt werden. Das Skript scripts/checkpatch.pl wird als Teil des Presubmit-Tests von Gerrit ausgeführt, führen Sie es also im Voraus aus, um sicherzustellen, dass es erfolgreich ist. Um das Checkpatch-Skript mit derselben Konfiguration wie beim Presubmit-Test auszuführen, verwenden build/static_analysis/checkpatch_presubmit.sh aus dem repo -Checkout.

ACK-Patches

An ACK übermittelte Patches müssen den Linux - Kernel - Codierungsstandards und den Beitragsrichtlinien entsprechen . Sie müssen ein Change-Id Tag in die Commit-Nachricht einfügen; Wenn Sie den Patch an mehrere Zweige senden (z. B. android-mainline und android12-5.4 ), müssen Sie dieselbe Change-Id für alle Instanzen des Patches verwenden.

Senden Sie Patches zuerst an LKML für eine vorgelagerte Überprüfung. Wenn der Patch ist:

  • Upstream akzeptiert, wird es automatisch in android-mainline zusammengeführt.
  • Upstream nicht akzeptiert, senden Sie es an android-mainline mit einem Verweis auf die Upstream-Übermittlung oder einer Erklärung, warum es nicht an LKML übermittelt wurde.

Nachdem ein Patch entweder im Upstream oder in android-mainline akzeptiert wurde, kann er auf das entsprechende LTS-basierte ACK zurückportiert werden (z. B. android12-5.4 und android11-5.4 für Patches, die Android-spezifischen Code reparieren). Die Übermittlung an android-mainline ermöglicht das Testen mit neuen Upstream-Release-Kandidaten und garantiert, dass der Patch im nächsten LTS-basierten ACK enthalten ist. Ausnahmen sind Fälle, in denen ein Upstream-Patch auf android12-5.4 wird (da der Patch wahrscheinlich bereits in android-mainline enthalten ist).

Upstream-Patches

Wie in den Beitragsrichtlinien angegeben, fallen Upstream-Patches, die für ACK-Kernel bestimmt sind, in die folgenden Gruppen (aufgelistet in der Reihenfolge ihrer Akzeptanzwahrscheinlichkeit).

  • UPSTREAM: - Patches, die von "Android-Mainline" herausgepickt wurden, werden wahrscheinlich in ACK akzeptiert, wenn es einen vernünftigen Anwendungsfall gibt.
  • BACKPORT: - Patches von Upstream, die nicht sauber picken und modifiziert werden müssen, werden wahrscheinlich auch akzeptiert, wenn es einen vernünftigen Anwendungsfall gibt.
  • FROMGIT: - Patches, die von einem Betreuer-Zweig in Vorbereitung auf die Übermittlung an die Linux-Hauptlinie herausgepickt wurden, könnten akzeptiert werden, wenn es eine bevorstehende Frist gibt. Diese sind sowohl inhaltlich als auch zeitlich zu begründen.
  • FROMLIST: - Patches, die an LKML übermittelt, aber noch nicht in einen Betreuerzweig akzeptiert wurden, werden wahrscheinlich nicht akzeptiert, es sei denn, die Begründung ist überzeugend genug, dass der Patch akzeptiert würde, unabhängig davon, ob er in Upstream-Linux landet oder nicht (wir nehmen an dass es nicht geht). Es muss ein Problem im Zusammenhang mit FROMLIST Patches geben, um die Diskussion mit dem Android-Kernel-Team zu erleichtern.

Android-spezifische Patches

Wenn Sie die erforderlichen Änderungen im Upstream nicht landen können, können Sie versuchen, Out-of-Tree-Patches direkt an ACK zu senden. Das Einreichen von Out-of-Tree-Patches erfordert, dass Sie ein Problem in der IT erstellen, das den Patch und eine Begründung dafür nennt, warum der Patch nicht an den Upstream gesendet werden kann (Beispiele finden Sie in der vorherigen Liste). Es gibt jedoch einige Fälle, in denen der Code nicht im Upstream eingereicht werden kann. Diese Fälle werden wie folgt behandelt und müssen den Beitragsrichtlinien für Android-spezifische Patches entsprechen und mit dem Präfix ANDROID: im Betreff gekennzeichnet sein.

Änderungen an gki_defconfig

Alle CONFIG Änderungen an gki_defconfig müssen sowohl auf die arm64- als auch auf die x86-Version angewendet werden, es sei denn, die CONFIG ist architekturspezifisch. Um eine Änderung an einer CONFIG Einstellung anzufordern, erstellen Sie ein Problem in der IT, um die Änderung zu besprechen. Jede CONFIG Änderung, die sich auf das Kernel Module Interface (KMI) nach dem Einfrieren auswirkt, wird abgelehnt. In Fällen, in denen Partner widersprüchliche Einstellungen für eine einzelne Konfiguration anfordern, lösen wir Konflikte durch Diskussionen über die zugehörigen Fehler.

Code, der im Upstream nicht existiert

Änderungen an Code, der bereits Android-spezifisch ist, können nicht in den Upstream gesendet werden. Obwohl der Binder-Treiber zum Beispiel Upstream beibehalten wird, können Änderungen an Prioritätsvererbungsfeatures des Binder-Treibers nicht Upstream gesendet werden, da sie Android-spezifisch sind. Geben Sie in Ihrem Fehler und Patch explizit an, warum der Code nicht in den Upstream gesendet werden kann. Teilen Sie die Patches nach Möglichkeit in Teile auf, die an Upstream gesendet werden können, und Android-spezifische Teile, die nicht an Upstream gesendet werden können, um die Menge an Out-of-Tree-Code zu minimieren, der in ACK verwaltet wird.

Andere Änderungen in dieser Kategorie sind Aktualisierungen von KMI-Darstellungsdateien, KMI-Symbollisten, gki_defconfig , Erstellungsskripten oder Konfigurationen oder anderen Skripts, die nicht im Upstream vorhanden sind.

Out-of-Tree-Module

Upstream-Linux rät aktiv von der Unterstützung für das Erstellen von Out-of-Tree-Modulen ab. Dies ist eine vernünftige Position, wenn man bedenkt, dass Linux-Betreuer keine Garantien für Kernel-Quell- oder Binärkompatibilität geben und keinen Code unterstützen wollen, der nicht im Baum vorhanden ist. Die GKI gibt jedoch ABI-Garantien für Anbietermodule und stellt sicher, dass KMI-Schnittstellen für die unterstützte Lebensdauer eines Kernels stabil sind. Daher gibt es eine Klasse von Änderungen zur Unterstützung von Herstellermodulen, die für ACK akzeptabel sind, aber für Upstream nicht akzeptabel sind.

Stellen Sie sich zum Beispiel einen Patch vor, der EXPORT_SYMBOL_GPL() Makros hinzufügt, wenn die Module, die den Export verwenden, nicht im Quellbaum vorhanden sind. Sie müssen zwar versuchen, EXPORT_SYMBOL_GPL() stromaufwärts anzufordern und ein Modul bereitzustellen, das das neu exportierte Symbol verwendet, aber wenn es eine gültige Begründung dafür gibt, warum das Modul nicht stromaufwärts gesendet wird, können Sie den Patch stattdessen an ACK senden. Sie müssen in der Ausgabe die Begründung angeben, warum das Modul nicht hochgeladen werden kann. (Fordern Sie nicht die Nicht-GPL-Variante EXPORT_SYMBOL() .)

Versteckte Konfigurationen

Einige In-Tree-Module wählen automatisch versteckte Konfigurationen aus, die nicht in gki_defconfig angegeben werden können. Beispielsweise wird CONFIG_SND_SOC_TOPOLOGY automatisch ausgewählt, wenn CONFIG_SND_SOC_SOF=y konfiguriert ist. Um das Erstellen von Modulen außerhalb des Baums zu ermöglichen, enthält GKI einen Mechanismus zum Aktivieren versteckter Konfigurationen.

Um eine versteckte Konfiguration zu aktivieren, fügen Sie eine select -Anweisung in init/Kconfig.gki , damit sie automatisch basierend auf der Kernel-Konfiguration CONFIG_GKI_HACKS_TO_FIX ausgewählt wird, die in gki_defconfig . Verwenden Sie diesen Mechanismus nur für versteckte Konfigurationen; Wenn die Konfiguration nicht ausgeblendet ist, muss sie entweder explizit oder als Abhängigkeit in gki_defconfig angegeben werden.

Ladbare Regler

Für Kernel-Frameworks (wie cpufreq ), die ladbare Governors unterstützen, können Sie den Standard-Governor (wie den cpufreq Governor von cpufreq ) schedutil . Für Frameworks (wie das thermische Framework), die keine ladbaren Governors oder Treiber unterstützen, aber dennoch einen Herstellerspezifische Implementierung, erstellen Sie ein Problem in der IT und wenden Sie sich an das Android-Kernel-Team .

Wir arbeiten mit Ihnen und den Upstream-Maintainern zusammen, um die erforderliche Unterstützung hinzuzufügen.

Anbieterhaken

In früheren Versionen konnten Sie herstellerspezifische Modifikationen direkt in den Kernel einfügen. Dies ist mit GKI 2.0 nicht möglich, da produktspezifischer Code in Modulen implementiert werden muss und nicht in den Core-Kernels Upstream oder in ACK akzeptiert wird. Um Mehrwertfunktionen, auf die sich Partner verlassen, mit minimaler Auswirkung auf den Kernel-Code zu ermöglichen, akzeptiert GKI Anbieter-Hooks, die es ermöglichen, Module aus dem Kernel-Code aufzurufen. Zusätzlich können Schlüsseldatenstrukturen mit Lieferantendatenfeldern aufgefüllt werden, die verfügbar sind, um anbieterspezifische Daten zu speichern, um diese Funktionen zu implementieren.

Anbieter-Hooks gibt es in zwei Varianten (normal und eingeschränkt), die auf Ablaufverfolgungspunkten (keine Ablaufverfolgungsereignisse) basieren, an die Anbietermodule angehängt werden können. Anstatt beispielsweise eine neue sched_exit() Funktion hinzuzufügen, um eine Abrechnung beim Task-Exit durchzuführen, können Anbieter einen Hook in do_exit() hinzufügen, an den ein Anbietermodul zur Verarbeitung angehängt werden kann. Eine Beispielimplementierung enthält die folgenden Anbieter-Hooks.

  • Normale Anbieter-Hooks verwenden DECLARE_HOOK() , um eine Tracepoint-Funktion mit dem Namen trace_ name zu erstellen, wobei name der eindeutige Bezeichner für den Trace ist. Per Konvention beginnen normale Anbieter-Hook-Namen mit android_vh , also wäre der Name für den sched_exit() android_vh_sched_exit .
  • Eingeschränkte Vendor-Hooks werden für Fälle wie Scheduler-Hooks benötigt, in denen die angehängte Funktion aufgerufen werden muss, selbst wenn die CPU offline ist oder einen nicht atomaren Kontext erfordert. Eingeschränkte Anbieter-Hooks können nicht getrennt werden, sodass Module, die an einen eingeschränkten Hook angehängt sind, niemals entladen werden können. Es ist nur ein Anhang zulässig, daher schlagen alle anderen Anhängeversuche mit -EBUSY . Eingeschränkte Anbieter-Hook-Namen beginnen mit android_rvh .

Um einen Anbieter-Hook hinzuzufügen, melden Sie ein Problem bei der IT und reichen Sie Patches ein (wie bei allen Android-spezifischen Patches muss ein Problem vorhanden sein und Sie müssen eine Begründung angeben). Unterstützung für Anbieter-Hooks gibt es nur in ACK, also senden Sie diese Patches nicht an Upstream-Linux.

Lieferantenfelder zu Strukturen hinzufügen

Sie können Anbieterdaten Schlüsseldatenstrukturen zuordnen, indem android_vendor_data Felder mithilfe der ANDROID_VENDOR_DATA() Makros hinzufügen. Um beispielsweise Mehrwertfeatures zu unterstützen, hängen Sie Felder an Strukturen an, wie im folgenden Codebeispiel gezeigt.

Um potenzielle Konflikte zwischen Feldern, die von Anbietern benötigt werden, und Feldern, die von OEMs benötigt werden, zu vermeiden, dürfen OEMs niemals Felder verwenden, die mit ANDROID_VENDOR_DATA() Makros deklariert wurden. Stattdessen müssen OEMs ANDROID_OEM_DATA() verwenden, um android_oem_data Felder zu deklarieren.

#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 */
}

Vendor-Hooks definieren

Fügen Sie Hersteller-Hooks als Ablaufverfolgungspunkte zum Kernel-Code hinzu, indem Sie sie mit DECLARE_HOOK() oder DECLARE_RESTRICTED_HOOK() und sie dann als Ablaufverfolgungspunkt zum Code hinzufügen. Um beispielsweise trace_android_vh_sched_exit() zur vorhandenen Kernel-Funktion do_exit() () hinzuzufügen:

#include <trace/hooks/exit.h>
void do_exit(long code)
{
    struct task_struct *tsk = current;
    ...
    trace_android_vh_sched_exit(tsk);
    ...
}

Die Funktion trace_android_vh_sched_exit() prüft zunächst nur, ob etwas angehängt ist. Wenn jedoch ein Herstellermodul einen Handler mit register_trace_android_vh_sched_exit() registriert, wird die registrierte Funktion aufgerufen. Der Handler muss den Kontext in Bezug auf gehaltene Sperren, den RCS-Zustand und andere Faktoren kennen. Der Hook muss in einer Header-Datei im Verzeichnis include/trace/hooks definiert werden.

Der folgende Code gibt beispielsweise eine mögliche Deklaration für trace_android_vh_sched_exit() in der Datei include/trace/hooks/sched.h .

/* 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>

Um die für den Hersteller-Hook erforderlichen Schnittstellen zu instanziieren, fügen Sie die Header-Datei mit der Hook-Deklaration zu drivers/android/vendor_hooks.c und exportieren Sie die Symbole. Der folgende Code vervollständigt beispielsweise die Deklaration des 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);

HINWEIS : Datenstrukturen, die innerhalb der Hook-Deklaration verwendet werden, müssen vollständig definiert werden, um ABI-Stabilität zu gewährleisten. Andernfalls ist es unsicher, die undurchsichtigen Zeiger zu dereferenzieren oder die Struktur in Größenkontexten zu verwenden. Das Include, das die vollständige Definition solcher Datenstrukturen bereitstellt, sollte in den Abschnitt #ifndef __GENKSYMS__ von drivers/android/vendor_hooks.c . Die Header-Dateien in include/trace/hooks sollten die Kernel-Header-Datei nicht mit den Typdefinitionen enthalten, um CRC-Änderungen zu vermeiden, die das KMI beschädigen. Deklarieren Sie stattdessen die Typen weiter.

An Händlerhaken befestigen

Um Vendor-Hooks zu verwenden, muss das Vendor-Modul einen Handler für den Hook registrieren (normalerweise während der Modulinitialisierung). Der folgende Code zeigt beispielsweise den Handler des Moduls foo.ko für trace_android_vh_sched_exit() .

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

Core-Kernel-Funktionen

Wenn Sie mit keiner der vorherigen Techniken eine Funktion aus einem Modul implementieren können, müssen Sie die Funktion als Android-spezifische Modifikation zum Kernel hinzufügen. Erstellen Sie ein Problem im Issue Tracker (IT), um das Gespräch zu beginnen.

Programmierschnittstelle für Benutzeranwendungen (UAPI)

  • UAPI-Header-Dateien. Änderungen an UAPI-Headerdateien müssen Upstream erfolgen, es sei denn, die Änderungen beziehen sich auf Android-spezifische Schnittstellen. Verwenden Sie anbieterspezifische Header-Dateien, um Schnittstellen zwischen Anbietermodulen und Anbieter-Userspace-Code zu definieren.
  • sysfs-Knoten. Fügen Sie dem GKI-Kernel keine neuen sysfs-Knoten hinzu (solche Ergänzungen sind nur in Herstellermodulen gültig). sysfs-Knoten, die von den SoC- und geräteunabhängigen Bibliotheken verwendet werden, und Java-Code, der das Android-Framework umfasst, können nur auf kompatible Weise geändert werden und müssen im Upstream geändert werden, wenn es sich nicht um Android-spezifische sysfs-Knoten handelt. Sie können herstellerspezifische sysfs-Knoten erstellen, die vom Benutzerbereich des Herstellers verwendet werden. Standardmäßig wird der Zugriff auf sysfs-Knoten durch den Benutzerbereich unter Verwendung von SELinux verweigert. Es ist Sache des Anbieters, die entsprechenden SELinux-Kennzeichnungen hinzuzufügen, um den Zugriff durch autorisierte Anbietersoftware zu ermöglichen.
  • DebugFS-Knoten. Herstellermodule können Knoten in debugfs nur zum Debuggen definieren (da debugfs während des normalen Betriebs des Geräts nicht gemountet wird).