Entwickeln Sie Kernel-Code für GKI

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

Entwickler müssen Codeänderungen vorab über die Linux Kernel Mailing List (LKML) als erste Wahl einreichen und Codeänderungen nur dann an den ACK-Zweig android-mainline übermitteln, wenn es einen triftigen Grund gibt, warum der Upstream nicht realisierbar ist. Nachfolgend sind Beispiele für triftige Gründe und deren Umgang aufgeführt.

  • Der Patch wurde an LKML übermittelt, aber nicht rechtzeitig für eine Produktveröffentlichung akzeptiert. Um mit diesem Patch umzugehen:

    • Geben Sie den Nachweis an, dass der Patch an LKML übermittelt wurde, und geben Sie an, dass Kommentare zum Patch eingegangen sind, oder geben Sie einen geschätzten Zeitpunkt an, bis zu dem der Patch vorab eingereicht wird.
    • Entscheiden Sie sich für eine Vorgehensweise, um den Patch in ACK zu landen, ihn von den Upstreams 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 Anbietermodul, konnte aber nicht an die Originalautoren übermittelt werden, da es keine In-Tree-Module gibt, die dieses Symbol verbrauchen. Um diesen Patch zu bearbeiten, geben Sie bitte Einzelheiten dazu an, warum Ihr Modul nicht vorab eingereicht werden kann und welche Alternativen Sie in Betracht gezogen haben, bevor Sie diese Anfrage gestellt haben.

  • Der Patch ist nicht generisch genug für den Upstream und es bleibt keine Zeit, ihn vor einer Produktveröffentlichung umzugestalten. Um diesen Patch zu bearbeiten, geben Sie eine geschätzte Zeit an, bis zu der ein überarbeiteter Patch im Upstream eingereicht wird (der Patch wird im ACK nicht akzeptiert, ohne dass geplant ist, einen überarbeiteten Patch im Upstream zur Überprüfung einzureichen).

  • Der Patch kann vom Upstream nicht akzeptiert werden, weil... <Grund hier einfügen> . Um diesen Patch zu bearbeiten, 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 von den Upstreams akzeptiert werden kann.

Es gibt noch viele weitere mögliche Rechtfertigungen. Wenn Sie Ihren Fehler oder Ihren Patch einreichen, geben Sie eine gültige Begründung an und erwarten Sie eine Wiederholung und Diskussion. Wir sind uns bewusst, dass das ACK einige Patches bereitstellen wird, insbesondere in den frühen Phasen von GKI, während jeder lernt, wie man im Upstream arbeitet, aber die Produktzeitpläne dafür nicht lockern kann. Erwarten Sie, dass die Upstreaming-Anforderungen mit der Zeit strenger werden.

Patch-Anforderungen

Patches müssen den im Linux-Quellbaum beschriebenen Linux-Kernel-Codierungsstandards entsprechen, unabhängig davon, ob sie im Upstream oder an ACK übermittelt werden. Das Skript scripts/checkpatch.pl wird im Rahmen des Presubmit-Tests von Gerrit ausgeführt. Führen Sie es daher im Voraus aus, um sicherzustellen, dass es erfolgreich ist. Um das Checkpatch-Skript mit derselben Konfiguration wie die Presubmit-Tests auszuführen, verwenden Sie //build/kernel/static_analysis:checkpatch_presubmit . Einzelheiten finden Sie unter build/kernel/kleaf/docs/checkpatch.md .

ACK-Patches

An ACK übermittelte Patches müssen den Codierungsstandards des Linux-Kernels 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 für alle Instanzen des Patches dieselbe Change-Id verwenden.

Senden Sie Patches zunächst zur Upstream-Überprüfung an LKML. Wenn der Patch:

  • Von den Upstreams akzeptiert, wird es automatisch in android-mainline eingefügt.
  • Nicht vom Upstream akzeptiert. Senden Sie es an android-mainline mit einem Verweis auf die Upstream-Einreichung 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 zum entsprechenden LTS-basierten 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 umfassen Fälle, in denen ein Upstream-Patch auf android12-5.4 zurückportiert wird (da sich der Patch wahrscheinlich bereits in android-mainline befindet).

Upstream-Patches

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

  • UPSTREAM: – Patches, die von „android-mainline“ ausgewählt wurden, werden wahrscheinlich in ACK akzeptiert, wenn es einen vernünftigen Anwendungsfall gibt.
  • BACKPORT: – Patches von Upstream-Anbietern, die nicht sauber ausgewählt werden können und geändert werden müssen, werden wahrscheinlich auch akzeptiert, wenn es einen sinnvollen Anwendungsfall gibt.
  • FROMGIT: – Patches, die von einem Maintainer-Zweig ausgewählt wurden, um sie auf die Übermittlung an die Linux-Hauptlinie vorzubereiten, werden möglicherweise akzeptiert, wenn eine Frist ansteht. Diese müssen sowohl inhaltlich als auch zeitlich begründet sein.
  • FROMLIST: - Patches, die an LKML übermittelt, aber noch nicht von einem 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 im Upstream-Linux landet oder nicht (wir gehen davon aus). dass das nicht der Fall sein wird). Es muss ein Problem im Zusammenhang mit FROMLIST Patches vorliegen, um die Diskussion mit dem Android-Kernel-Team zu erleichtern.

Android-spezifische Patches

Wenn Sie die erforderlichen Änderungen nicht vorab erreichen können, können Sie versuchen, Out-of-Tree-Patches direkt an ACK zu übermitteln. Für die Übermittlung von Patches außerhalb des Verzeichnisbaums ist es erforderlich, dass Sie in der IT ein Problem erstellen, in dem der Patch und die Begründung genannt werden, warum der Patch nicht vorab eingereicht werden kann (Beispiele finden Sie in der vorherigen Liste). Es gibt jedoch einige Fälle, in denen der Code nicht vorab übermittelt werden kann. Diese Fälle werden wie folgt abgedeckt und müssen den Beitragsrichtlinien für Android-spezifische Patches entsprechen und im Betreff mit dem Präfix ANDROID: 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 einer CONFIG Einstellung anzufordern, erstellen Sie ein Problem in der IT, um die Änderung zu besprechen. Jede CONFIG Änderung, die sich nach dem Einfrieren auf die Kernel Module Interface (KMI) auswirkt, wird abgelehnt. In Fällen, in denen Partner widersprüchliche Einstellungen für eine einzelne Konfiguration anfordern, lösen wir Konflikte durch Diskussion der zugehörigen Fehler.

Code, der im Upstream nicht existiert

Änderungen am Code, der bereits Android-spezifisch ist, können nicht an den Upstream gesendet werden. Auch wenn beispielsweise der Binder-Treiber im Upstream verwaltet wird, können Änderungen an den Prioritätsvererbungsfunktionen des Binder-Treibers nicht im Upstream gesendet werden, da sie Android-spezifisch sind. Geben Sie in Ihrem Fehler und Patch explizit an, warum der Code nicht an den Upstream gesendet werden kann. Wenn möglich, teilen Sie die Patches in Teile auf, die im Upstream eingereicht werden können, und Android-spezifische Teile, die nicht im Upstream eingereicht werden können, um die Menge an Out-of-Tree-Code zu minimieren, der in ACK verwaltet wird.

Weitere Änderungen in dieser Kategorie sind Aktualisierungen von KMI-Darstellungsdateien, KMI-Symbollisten, gki_defconfig , Build-Skripten oder -Konfigurationen oder anderen Skripten, die im Original nicht vorhanden sind.

Out-of-Tree-Module

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

Betrachten Sie beispielsweise einen Patch, der EXPORT_SYMBOL_GPL() -Makros hinzufügt, wenn sich die Module, die den Export verwenden, nicht im Quellbaum befinden. Während Sie versuchen müssen, EXPORT_SYMBOL_GPL() im Upstream anzufordern und ein Modul bereitzustellen, das das neu exportierte Symbol verwendet, können Sie stattdessen den Patch an ACK senden, wenn es eine gültige Begründung dafür gibt, warum das Modul nicht im Upstream eingereicht wird. Sie müssen in der Ausgabe eine Begründung dafür angeben, warum das Modul nicht vorgestreamt werden kann. (Fordern Sie nicht die Nicht-GPL-Variante EXPORT_SYMBOL() an.)

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 die Erstellung von Out-of-Tree-Modulen zu ermöglichen, enthält GKI einen Mechanismus zur Aktivierung versteckter Konfigurationen.

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

Beladbare Gouverneure

Für Kernel-Frameworks (z. B. cpufreq ), die ladbare Gouverneure unterstützen, können Sie den Standard-Governor überschreiben (z. B. den schedutil -Governor von cpufreq ). Für Frameworks (z. B. das Thermal Framework), die keine ladbaren Gouverneure oder Treiber unterstützen, aber dennoch einen erfordern Herstellerspezifische Implementierung, Erstellen eines Problems in der IT und Rücksprache mit dem Android-Kernel-Team .

Wir arbeiten mit Ihnen und den Upstream-Betreuern zusammen, um die erforderliche Unterstützung bereitzustellen.

Anbieter-Hooks

In früheren Versionen konnten Sie herstellerspezifische Änderungen direkt in den Kernel einfügen. Dies ist mit GKI 2.0 nicht möglich, da produktspezifischer Code in Modulen implementiert werden muss und weder in den Upstream-Kernkerneln noch in ACK akzeptiert wird. Um Mehrwertfunktionen, auf die sich Partner verlassen, mit minimalen Auswirkungen auf den Kernel-Code zu ermöglichen, akzeptiert GKI Hersteller-Hooks, die den Aufruf von Modulen aus dem Kernel-Code ermöglichen. Darüber hinaus können wichtige Datenstrukturen mit Anbieterdatenfeldern aufgefüllt werden, die zum Speichern anbieterspezifischer Daten zur Implementierung dieser Funktionen zur Verfügung stehen.

Anbieter-Hooks gibt es in zwei Varianten (normal und eingeschränkt), die auf Tracepoints (nicht Trace-Ereignissen) basieren, an die Anbietermodule angehängt werden können. Anstatt beispielsweise eine neue sched_exit() Funktion hinzuzufügen, um beim Beenden der Aufgabe eine Abrechnung durchzuführen, können Anbieter einen Hook in do_exit() hinzufügen, an den ein Anbietermodul zur Verarbeitung angehängt werden kann. Eine Beispielimplementierung umfasst die folgenden Hersteller-Hooks.

  • Normale Hersteller-Hooks verwenden DECLARE_HOOK() , um eine Tracepoint-Funktion mit dem Namen trace_ name zu erstellen, wobei name die eindeutige Kennung für den Trace ist. Konventionell beginnen normale Hersteller-Hook-Namen mit android_vh , daher wäre der Name für sched_exit() -Hook android_vh_sched_exit .
  • Eingeschränkte Hersteller-Hooks sind für Fälle wie Scheduler-Hooks erforderlich, bei denen die angehängte Funktion auch dann aufgerufen werden muss, wenn die CPU offline ist oder einen nichtatomaren Kontext erfordert. Eingeschränkte Hersteller-Hooks können nicht getrennt werden, sodass Module, die an einen eingeschränkten Hook angehängt sind, niemals entladen werden können. Eingeschränkte Anbieter-Hook-Namen beginnen mit android_rvh .

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

Fügen Sie Lieferantenfelder zu Strukturen hinzu

Sie können Lieferantendaten mit wichtigen Datenstrukturen verknüpfen, indem Sie android_vendor_data Felder mithilfe der ANDROID_VENDOR_DATA() -Makros hinzufügen. Um beispielsweise Mehrwertfunktionen zu unterstützen, hängen Sie Felder an Strukturen an, wie im folgenden Codebeispiel gezeigt.

Um potenzielle Konflikte zwischen von Anbietern benötigten Feldern und von OEMs benötigten Feldern 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 */
}

Definieren Sie Anbieter-Hooks

Fügen Sie Hersteller-Hooks als Tracepoints zum Kernel-Code hinzu, indem Sie sie mit DECLARE_HOOK() oder DECLARE_RESTRICTED_HOOK() deklarieren und sie dann als Tracepoint zum Code hinzufügen. Um beispielsweise trace_android_vh_sched_exit() zur vorhandenen Kernelfunktion 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 Anbietermodul einen Handler mit register_trace_android_vh_sched_exit() registriert, wird die registrierte Funktion aufgerufen. Der Handler muss den Kontext im Hinblick auf gehaltene Sperren, den RCS-Status 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/exit.h an.

/* 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 Vendor-Hook erforderlichen Schnittstellen zu instanziieren, fügen Sie die Header-Datei mit der Hook-Deklaration zu drivers/android/vendor_hooks.c hinzu und exportieren Sie die Symbole. Der folgende Code vervollständigt beispielsweise die Deklaration des android_vh_sched_exit() -Hooks.

#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 sein, um die 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 im Abschnitt #ifndef __GENKSYMS__ von drivers/android/vendor_hooks.c enthalten sein. Die Header-Dateien in include/trace/hooks sollten nicht die Kernel-Header-Datei mit den Typdefinitionen enthalten, um CRC-Änderungen zu vermeiden, die den KMI beschädigen. Deklarieren Sie stattdessen die Typen weiter.

An den Haken des Anbieters befestigen

Um Hersteller-Hooks zu verwenden, muss das Anbietermodul einen Handler für den Hook registrieren (normalerweise während der Modulinitialisierung). Der folgende Code zeigt beispielsweise den Modul-Handler 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);
    ...
}

Kernfunktionen des Kernels

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 im Issue Tracker (IT) ein Problem, um die Konversation zu starten.

Benutzeranwendungsprogrammierschnittstelle (UAPI)

  • UAPI-Header-Dateien. Änderungen an UAPI-Headerdateien müssen im Upstream erfolgen, es sei denn, die Änderungen beziehen sich auf Android-spezifische Schnittstellen. Verwenden Sie herstellerspezifische 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 Anbietermodulen gültig). Sysfs-Knoten, die von den SoC- und geräteunabhängigen Bibliotheken verwendet werden, und Java-Code, aus dem das Android-Framework besteht, 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 Anbieters verwendet werden. Standardmäßig wird der Zugriff auf sysfs-Knoten durch den Userspace mit SELinux verweigert. Es ist Sache des Anbieters, die entsprechenden SELinux-Kennzeichnungen hinzuzufügen, um den Zugriff durch autorisierte Anbietersoftware zu ermöglichen.
  • DebugFS-Knoten. Anbietermodule können Knoten in debugfs nur zum Debuggen definieren (da debugfs während des normalen Betriebs des Geräts nicht gemountet wird).