L'image générique du noyau (GKI, Generic Kernel Image) réduit la fragmentation du noyau en s'alignant étroitement sur le noyau Linux en amont. Toutefois, il existe des raisons valables pour lesquelles certains correctifs ne peuvent pas être acceptés en amont, et des calendriers de produits qui doivent être respectés. Par conséquent, certains correctifs sont conservés dans les sources du noyau commun Android (ACK) à partir desquelles le GKI est créé.
Les développeurs doivent envoyer les modifications de code en amont à l'aide de la liste de diffusion du noyau Linux (LKML) en premier lieu, et envoyer les modifications de code à la branche ACK android-mainline
uniquement lorsqu'il existe une raison valable pour laquelle l'envoi en amont n'est pas possible. Vous trouverez ci-dessous des exemples de motifs valides et la façon de les traiter.
Le correctif a été envoyé à LKML, mais n'a pas été accepté à temps pour la sortie du produit. Pour gérer ce correctif :
- Fournissez la preuve que le correctif a été envoyé à LKML et les commentaires reçus pour le correctif, ou une estimation du délai d'envoi du correctif en amont.
- Décidez d'une ligne de conduite pour intégrer le correctif dans ACK, le faire approuver en amont, puis le supprimer d'ACK lorsque la version finale en amont est fusionnée dans ACK.
Le correctif définit
EXPORT_SYMBOLS_GPL()
pour un module fournisseur, mais n'a pas pu être envoyé en amont, car aucun module intégré ne consomme ce symbole. Pour gérer ce correctif, expliquez pourquoi votre module ne peut pas être envoyé en amont et les alternatives que vous avez envisagées avant de faire cette demande.Le correctif n'est pas assez générique pour l'upstream et il n'y a pas le temps de le refactoriser avant la sortie d'un produit. Pour gérer ce correctif, indiquez une estimation du délai de soumission d'un correctif refactorisé en amont (le correctif ne sera pas accepté dans ACK sans plan de soumission d'un correctif refactorisé en amont pour examen).
Le correctif ne peut pas être accepté par le fournisseur en amont, car… <insérer la raison ici>. Pour gérer ce correctif, contactez l'équipe du noyau Android et trouvez avec nous des options pour le refactoriser afin qu'il puisse être envoyé pour examen et accepté en amont.
Il existe de nombreuses autres justifications potentielles. Lorsque vous envoyez votre bug ou votre correctif, incluez une justification valide et attendez-vous à des itérations et des discussions. Nous reconnaissons que l'ACK comporte des correctifs, en particulier dans les premières phases de GKI, lorsque tout le monde apprend à travailler en amont, mais ne peut pas relâcher les plannings des produits pour le faire. Attendez-vous à ce que les exigences d'upstreaming deviennent plus strictes au fil du temps.
Exigences concernant les correctifs
Les correctifs doivent être conformes aux normes de codage du noyau Linux décrites dans l'arborescence source Linux, qu'ils soient envoyés en amont ou à ACK. Le script scripts/checkpatch.pl
est exécuté dans le cadre des tests de pré-commit Gerrit. Exécutez-le à l'avance pour vous assurer qu'il réussit. Pour exécuter le script checkpatch avec la même configuration que les tests avant envoi, utilisez //build/kernel/static_analysis:checkpatch_presubmit
.
Pour en savoir plus, consultez build/kernel/kleaf/docs/checkpatch.md.
Correctifs ACK
Les correctifs envoyés à ACK doivent être conformes aux normes de codage du noyau Linux et aux consignes de contribution.
Vous devez inclure un tag Change-Id
dans le message de commit. Si vous envoyez le correctif à plusieurs branches (par exemple, android-mainline
et android12-5.4
), vous devez utiliser le même Change-Id
pour toutes les instances du correctif.
Envoyez d'abord les correctifs à LKML pour un examen en amont. Si le correctif est :
- Une fois acceptée en amont, elle est automatiquement fusionnée dans
android-mainline
. - Non accepté en amont, envoyez-le à
android-mainline
en faisant référence à l'envoi en amont ou en expliquant pourquoi il n'a pas été envoyé à LKML.
Une fois un correctif accepté en amont ou dans android-mainline
, il peut être rétroporté vers l'ACK basé sur la LTS appropriée (par exemple, android12-5.4
et android11-5.4
pour les correctifs qui corrigent le code spécifique à Android). L'envoi à android-mainline
permet de tester les nouvelles versions candidates en amont et garantit que le correctif se trouve dans le prochain ACK basé sur LTS. Les exceptions incluent les cas où un correctif en amont est rétroporté vers android12-5.4
(car le correctif est probablement déjà dans android-mainline
).
Correctifs en amont
Comme indiqué dans les Consignes de contribution, les correctifs en amont destinés aux noyaux ACK se répartissent dans les groupes suivants (listés par ordre de probabilité d'acceptation).
UPSTREAM:
: les correctifs sélectionnés à partir de "android-mainline" sont susceptibles d'être acceptés dans ACK s'il existe un cas d'utilisation raisonnable.BACKPORT:
: les correctifs en amont qui ne peuvent pas être sélectionnés proprement et qui nécessitent une modification sont également susceptibles d'être acceptés s'il existe un cas d'utilisation raisonnable.FROMGIT:
: les correctifs sélectionnés à partir d'une branche de gestionnaire en vue de leur envoi à la branche principale de Linux peuvent être acceptés si une échéance approche. Elles doivent être justifiées pour le contenu et la programmation.FROMLIST:
: les correctifs qui ont été envoyés à LKML, mais qui n'ont pas encore été acceptés dans une branche de responsable, ont peu de chances d'être acceptés, sauf si la justification est suffisamment convaincante pour que le correctif soit accepté, qu'il soit intégré ou non à Linux en amont (nous supposons qu'il ne le sera pas). Un problème doit être associé aux correctifsFROMLIST
pour faciliter la discussion avec l'équipe du noyau Android.
Correctifs spécifiques à Android
Si vous ne parvenez pas à appliquer les modifications requises en amont, vous pouvez essayer d'envoyer des correctifs hors arbre directement à ACK. Pour envoyer des correctifs hors arbre, vous devez créer un problème dans l'outil IT qui cite le correctif et explique pourquoi il ne peut pas être envoyé en amont (voir la liste précédente pour des exemples).
Toutefois, dans certains cas, le code ne peut pas être envoyé en amont. Ces cas sont traités comme suit et doivent respecter les consignes de contribution pour les correctifs spécifiques à Android. Ils doivent également être tagués avec le préfixe ANDROID:
dans l'objet.
Modifications apportées à gki_defconfig
Toutes les modifications apportées à CONFIG
pour gki_defconfig
doivent être appliquées aux versions arm64 et x86, sauf si CONFIG
est spécifique à une architecture. Pour demander une modification d'un paramètre CONFIG
, créez un problème dans l'équipe informatique pour en discuter. Toute modification CONFIG
qui affecte l'interface du module du noyau (KMI) après son gel est refusée. Lorsque des partenaires demandent des paramètres conflictuels pour une même configuration, nous résolvons les conflits en discutant des bugs associés.
Code qui n'existe pas en amont
Les modifications apportées à du code déjà spécifique à Android ne peuvent pas être envoyées en amont. Par exemple, même si le pilote Binder est géré en amont, les modifications apportées aux fonctionnalités d'héritage de priorité du pilote Binder ne peuvent pas être envoyées en amont, car elles sont spécifiques à Android. Soyez explicite dans votre bug et expliquez pourquoi le code ne peut pas être envoyé en amont. Si possible, divisez les correctifs en éléments pouvant être envoyés en amont et en éléments spécifiques à Android qui ne peuvent pas être envoyés en amont afin de minimiser la quantité de code hors arbre maintenue dans ACK.
Les autres modifications de cette catégorie concernent les fichiers de représentation KMI, les listes de symboles KMI, gki_defconfig
, les scripts ou la configuration de compilation, ou d'autres scripts qui n'existent pas en amont.
Modules hors arbre
Le noyau Linux en amont déconseille vivement la création de modules hors arbre. Il s'agit d'une position raisonnable étant donné que les responsables de la maintenance de Linux ne garantissent pas la compatibilité binaire ou des sources dans le noyau et ne souhaitent pas prendre en charge le code qui ne se trouve pas dans l'arborescence. Toutefois, le GKI fournit des garanties ABI pour les modules du fournisseur, ce qui garantit que les interfaces KMI sont stables pendant la durée de vie compatible d'un noyau. Par conséquent, il existe une catégorie de modifications pour prendre en charge les modules du fournisseur qui sont acceptables pour ACK, mais pas pour le flux en amont.
Par exemple, considérons un correctif qui ajoute des macros EXPORT_SYMBOL_GPL()
alors que les modules qui utilisent l'exportation ne se trouvent pas dans l'arborescence source. Bien que vous deviez essayer de demander EXPORT_SYMBOL_GPL()
en amont et de fournir un module qui utilise le symbole nouvellement exporté, si vous avez une justification valable pour expliquer pourquoi le module n'est pas envoyé en amont, vous pouvez envoyer le correctif à ACK à la place. Vous devez indiquer dans le problème pourquoi le module ne peut pas être transféré en amont. (Ne demandez pas la variante non GPL, EXPORT_SYMBOL()
.)
Configurations masquées
Certains modules intégrés sélectionnent automatiquement des configurations masquées qui ne peuvent pas être spécifiées dans gki_defconfig
. Par exemple, CONFIG_SND_SOC_TOPOLOGY
est sélectionné automatiquement lorsque CONFIG_SND_SOC_SOF=y
est configuré. Pour permettre la création de modules hors arbre, GKI inclut un mécanisme permettant d'activer les configurations masquées.
Pour activer une configuration masquée, ajoutez une instruction select
dans init/Kconfig.gki
afin qu'elle soit automatiquement sélectionnée en fonction de la configuration du noyau CONFIG_GKI_HACKS_TO_FIX
, qui est activée dans gki_defconfig
. N'utilisez ce mécanisme que pour les configurations masquées. Si la configuration n'est pas masquée, elle doit être spécifiée dans gki_defconfig
de manière explicite ou en tant que dépendance.
Gouverneurs pouvant être chargés
Pour les frameworks de noyau (tels que cpufreq
) qui acceptent les régulateurs chargeables, vous pouvez remplacer le régulateur par défaut (tel que le régulateur schedutil
de cpufreq
). Pour les frameworks (tels que le framework thermique) qui ne prennent pas en charge les régulateurs ou les pilotes chargeables, mais qui nécessitent tout de même une implémentation spécifique au fournisseur, créez un problème dans l'outil de suivi des problèmes et consultez l'équipe du noyau Android.
Nous collaborerons avec vous et les responsables en amont pour ajouter l'assistance nécessaire.
Hooks de fournisseur
Dans les versions précédentes, vous pouviez ajouter des modifications spécifiques au fournisseur directement dans le noyau principal. Cela n'est pas possible avec GKI 2.0, car le code spécifique au produit doit être implémenté dans des modules et ne sera pas accepté dans les noyaux principaux en amont ni dans ACK. Pour activer les fonctionnalités à valeur ajoutée sur lesquelles s'appuient les partenaires avec un impact minimal sur le code du noyau principal, GKI accepte les hooks de fournisseur qui permettent d'appeler des modules à partir du code du noyau principal. De plus, les structures de données clés peuvent être complétées par des champs de données du fournisseur disponibles pour stocker des données spécifiques au fournisseur afin d'implémenter ces fonctionnalités.
Les hooks de fournisseur se présentent sous deux variantes (normale et restreinte) basées sur des points de trace (et non des événements de trace) auxquels les modules de fournisseur peuvent s'associer. Par exemple, au lieu d'ajouter une nouvelle fonction sched_exit()
pour effectuer une comptabilité à la sortie de la tâche, les fournisseurs peuvent ajouter un crochet dans do_exit()
auquel un module fournisseur peut s'attacher pour le traitement. Un exemple d'implémentation inclut les hooks de fournisseur suivants.
- Les hooks de fournisseur normaux utilisent
DECLARE_HOOK()
pour créer une fonction de point de trace avec le nomtrace_name
, oùname
est l'identifiant unique de la trace. Par convention, les noms de hooks de fournisseur normaux commencent parandroid_vh
. Le nom du hooksched_exit()
serait doncandroid_vh_sched_exit
. - Des hooks de fournisseur restreints sont nécessaires dans des cas tels que les hooks de planificateur, où la fonction associée doit être appelée même si le processeur est hors connexion ou nécessite un contexte non atomique. Les hooks de fournisseur restreints ne peuvent pas être détachés. Les modules qui s'y attachent ne peuvent donc jamais être déchargés. Les noms de hooks de fournisseur restreints commencent par
android_rvh
.
Pour ajouter un crochet de fournisseur, signalez un problème dans l'outil de suivi des problèmes et envoyez des correctifs (comme pour tous les correctifs spécifiques à Android, un problème doit exister et vous devez fournir une justification). La prise en charge des hooks de fournisseur n'est disponible que dans ACK. N'envoyez donc pas ces correctifs à Linux en amont.
Ajouter des champs de fournisseur aux structures
Vous pouvez associer des données de fournisseur à des structures de données clés en ajoutant des champs android_vendor_data
à l'aide des macros ANDROID_VENDOR_DATA()
. Par exemple, pour prendre en charge les fonctionnalités à valeur ajoutée, ajoutez des champs aux structures comme indiqué dans l'exemple de code suivant.
Pour éviter tout conflit potentiel entre les champs requis par les fournisseurs et ceux requis par les OEM, les OEM ne doivent jamais utiliser de champs déclarés à l'aide de macros ANDROID_VENDOR_DATA()
. Les OEM doivent plutôt utiliser ANDROID_OEM_DATA()
pour déclarer les champs 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 */
}
Définir des hooks de fournisseur
Ajoutez des hooks de fournisseur au code du noyau en tant que points de trace en les déclarant à l'aide de DECLARE_HOOK()
ou DECLARE_RESTRICTED_HOOK()
, puis en les ajoutant au code en tant que point de trace. Par exemple, pour ajouter trace_android_vh_sched_exit()
à la fonction de noyau do_exit()
existante :
#include <trace/hooks/exit.h>
void do_exit(long code)
{
struct task_struct *tsk = current;
...
trace_android_vh_sched_exit(tsk);
...
}
La fonction trace_android_vh_sched_exit()
vérifie d'abord si quelque chose est joint. Toutefois, si un module fournisseur enregistre un gestionnaire à l'aide de register_trace_android_vh_sched_exit()
, la fonction enregistrée est appelée. Le gestionnaire doit connaître le contexte en ce qui concerne les verrous détenus, l'état RCS et d'autres facteurs. Le crochet doit être défini dans un fichier d'en-tête du répertoire include/trace/hooks
.
Par exemple, le code suivant donne une déclaration possible pour trace_android_vh_sched_exit()
dans le fichier include/trace/hooks/exit.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>
Pour instancier les interfaces requises pour le hook du fournisseur, ajoutez le fichier d'en-tête avec la déclaration du hook à drivers/android/vendor_hooks.c
et exportez les symboles. Par exemple, le code suivant complète la déclaration du hook 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);
REMARQUE : Les structures de données utilisées dans la déclaration du hook doivent être entièrement définies pour garantir la stabilité de l'ABI. Sinon, il n'est pas sûr de déréférencer les pointeurs opaques ni d'utiliser la struct dans des contextes dimensionnés. L'inclusion qui fournit la définition complète de ces structures de données doit se trouver dans la section #ifndef __GENKSYMS__
de drivers/android/vendor_hooks.c
. Les fichiers d'en-tête de include/trace/hooks
ne doivent pas inclure le fichier d'en-tête du noyau avec les définitions de type pour éviter les modifications de CRC qui interrompent l'interface KMI. Déclarez plutôt les types.
S'associer aux hooks du fournisseur
Pour utiliser les hooks de fournisseur, le module de fournisseur doit enregistrer un gestionnaire pour le hook (généralement lors de l'initialisation du module). Par exemple, le code suivant affiche le gestionnaire de module foo.ko
pour 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);
...
}
Utiliser des hooks de fournisseur à partir de fichiers d'en-tête
Pour utiliser les hooks de fournisseur à partir des fichiers d'en-tête, vous devrez peut-être mettre à jour le fichier d'en-tête des hooks de fournisseur pour annuler la définition de TRACE_INCLUDE_PATH
afin d'éviter les erreurs de compilation indiquant qu'un fichier d'en-tête de point de trace n'a pas été trouvé. Par exemple,
In file included from .../common/init/main.c:111:
In file included from .../common/include/trace/events/initcall.h:74:
.../common/include/trace/define_trace.h:95:10: fatal error: 'trace/hooks/initcall.h' file not found
95 | #include TRACE_INCLUDE(TRACE_INCLUDE_FILE)
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.../common/include/trace/define_trace.h:90:32: note: expanded from macro 'TRACE_INCLUDE'
90 | # define TRACE_INCLUDE(system) __TRACE_INCLUDE(system)
| ^~~~~~~~~~~~~~~~~~~~~~~
.../common/include/trace/define_trace.h:87:34: note: expanded from macro '__TRACE_INCLUDE'
87 | # define __TRACE_INCLUDE(system) __stringify(TRACE_INCLUDE_PATH/system.h)
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.../common/include/linux/stringify.h:10:27: note: expanded from macro '__stringify'
10 | #define __stringify(x...) __stringify_1(x)
| ^~~~~~~~~~~~~~~~
.../common/include/linux/stringify.h:9:29: note: expanded from macro '__stringify_1'
9 | #define __stringify_1(x...) #x
| ^~
<scratch space>:14:1: note: expanded from here
14 | "trace/hooks/initcall.h"
| ^~~~~~~~~~~~~~~~~~~~~~~~
1 error generated.
Pour corriger ce type d'erreur de compilation, appliquez la correction équivalente au fichier d'en-tête du hook du fournisseur que vous incluez. Pour en savoir plus, consultez https://r.android.com/3066703.
diff --git a/include/trace/hooks/mm.h b/include/trace/hooks/mm.h
index bc6de7e53d66..039926f7701d 100644
--- a/include/trace/hooks/mm.h
+++ b/include/trace/hooks/mm.h
@@ -2,7 +2,10 @@
#undef TRACE_SYSTEM
#define TRACE_SYSTEM mm
+#ifdef CREATE_TRACE_POINTS
#define TRACE_INCLUDE_PATH trace/hooks
+#define UNDEF_TRACE_INCLUDE_PATH
+#endif
La définition de UNDEF_TRACE_INCLUDE_PATH
indique à include/trace/define_trace.h
de supprimer la définition de TRACE_INCLUDE_PATH
après la création des points de trace.
Fonctionnalités principales du noyau
Si aucune des techniques précédentes ne vous permet d'implémenter une fonctionnalité à partir d'un module, vous devez ajouter la fonctionnalité en tant que modification spécifique à Android au noyau principal. Créez un problème dans l'outil de suivi des problèmes (IT) pour démarrer la conversation.
Interface de programmation d'application utilisateur (UAPI)
- Fichiers d'en-tête UAPI Les modifications apportées aux fichiers d'en-tête UAPI doivent être effectuées en amont, sauf si elles concernent des interfaces spécifiques à Android. Utilisez des fichiers d'en-tête spécifiques au fournisseur pour définir les interfaces entre les modules du fournisseur et le code de l'espace utilisateur du fournisseur.
- Nœuds sysfs. N'ajoutez pas de nouveaux nœuds sysfs au noyau GKI (ces ajouts ne sont valables que dans les modules du fournisseur). Les nœuds sysfs utilisés par les bibliothèques et le code Java indépendants du SoC et de l'appareil qui composent le framework Android ne peuvent être modifiés que de manière compatible et doivent être modifiés en amont s'il ne s'agit pas de nœuds sysfs spécifiques à Android. Vous pouvez créer des nœuds sysfs spécifiques au fournisseur qui seront utilisés par l'espace utilisateur du fournisseur. Par défaut, l'accès aux nœuds sysfs par l'espace utilisateur est refusé à l'aide de SELinux. Il incombe au fournisseur d'ajouter les libellés SELinux appropriés pour autoriser l'accès par le logiciel fournisseur autorisé.
- Nœuds DebugFS. Les modules du fournisseur peuvent définir des nœuds dans
debugfs
pour le débogage uniquement (cardebugfs
n'est pas monté pendant le fonctionnement normal de l'appareil).