Développer du code de kernel pour GKI

L'image de kernel générique (GKI) réduit la fragmentation du kernel en s'alignant étroitement sur le kernel Linux en amont. Cependant, il existe des raisons valables certains correctifs ne peuvent pas être acceptés en amont, et des calendriers de produit doit être respectée. Par conséquent, certains correctifs sont conservés dans le noyau commun (ACK) d'Android. sources à partir desquelles le GKI est créé.

Les développeurs doivent soumettre les modifications de code en amont à l'aide du publipostage du noyau Linux. List (LKML) comme premier choix, puis soumettez les modifications de code à l’ACK Branche android-mainline uniquement lorsqu'il existe une bonne raison pour laquelle les flux en amont ne sont pas viable. Vous trouverez ci-dessous des exemples de raisons valides et la manière de les gérer.

  • Le correctif a été envoyé à LKML, mais n'a pas été accepté à temps pour une version du produit. Pour gérer ce correctif:

    • Fournissez la preuve que le correctif a été envoyé au LKML et envoyez vos commentaires le correctif reçu, ou la durée estimée du correctif envoyé en amont.
    • Décidez du plan d'action pour envoyer le correctif dans ACK, faites-le approuver en amont, puis de le retirer de l'ACK lorsque la version finale en amont est fusionné en ACK.
  • Le correctif définit EXPORT_SYMBOLS_GPL() pour un module de fournisseur, mais n'a pas pu être envoyé en amont, car aucun module "in-tree" ne l'utilise . Pour gérer ce correctif, indiquez pourquoi votre module ne peut pas être soumis en amont ainsi que les alternatives envisagées requête.

  • Le correctif n'est pas assez générique pour l'amont, et il n'y a pas le temps de le refactoriser avant la sortie du produit. Pour gérer ce correctif, indiquez une date estimée à laquelle un correctif refactorisé sera envoyé en amont (le correctif ne sera pas accepté dans l'ACK sans plan d'envoi d'un correctif refactorisé en amont pour examen).

  • Impossible d'accepter le correctif en amont pour les raisons suivantes : <insérer le motif ici>. Pour gérer ce correctif, contactez l'équipe chargée du noyau Android et travailler avec nous sur les options permettant de refactoriser le correctif afin qu'il puisse être envoyé. pour examen et acceptation en amont.

Il existe bien d'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 au début les phases de GKI pendant que tout le monde apprend à travailler en amont, mais ne peut pas se détendre le calendrier du produit. Attendez-vous à ce que les exigences liées à l'augmentation rigoureux au fil du temps.

Exigences concernant les correctifs

Les correctifs doivent respecter les normes de codage du noyau Linux décrites dans les Arborescence source Linux, qu'ils soient envoyés en amont ou vers l'accusé de réception (ACK). Le script scripts/checkpatch.pl est exécuté dans le cadre des tests de présoumission 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 à l'ACK doivent respecter les normes de codage du noyau Linux et les consignes relatives aux contributions. Vous devez inclure une balise 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:

  • Accepté en amont, il est fusionné automatiquement dans android-mainline.
  • Refusé en amont, envoyez-le à android-mainline avec un une référence à l'envoi en amont ou une explication pour expliquer pourquoi envoyés au LKML.

Une fois qu'un correctif est accepté en amont ou dans android-mainline, il peut être rétroporté vers l'ACK basé sur LTS approprié (comme android12-5.4 et android11-5.4 pour les correctifs qui corrigent le code spécifique à Android). Envoi à android-mainline permet d'effectuer des tests avec de nouvelles versions candidates en amont et garantit que le correctif se trouve dans le prochain ACK basé sur LTS. à l'exception des cas où un correctif en amont est rétroporté vers android12-5.4 (car le correctif est figure probablement déjà dans android-mainline).

Correctifs en amont

Comme indiqué dans la contribution consignes, les correctifs en amont destinés aux noyaux ACK appartiennent aux groupes suivants (répertoriés par ordre de probabilité d'être acceptés).

  • UPSTREAM: : les correctifs sélectionnés dans "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 sont pas sélectionnés de manière claire et qui doivent être modifiés sont également susceptibles d'être acceptés si un cas d'utilisation raisonnable existe.
  • FROMGIT: : les correctifs sélectionnés à partir d'une branche de maintenance en préparation pour être envoyés à la branche principale de Linux peuvent être acceptés si une échéance est imminente. Ces raisons doivent être justifiées aussi bien pour le contenu que pour le calendrier.
  • FROMLIST: : les correctifs envoyés à LKML, mais qui n'ont pas encore été acceptés dans une branche de maintenance, sont peu susceptibles d'être acceptés, sauf si la justification est suffisamment convaincante pour que le correctif soit accepté, qu'il soit intégré ou non dans Linux en amont (nous supposons qu'il ne le sera pas). Il y doit être un problème associé aux correctifs FROMLIST pour faciliter la discussion avec l'équipe du noyau Android.

Correctifs spécifiques à Android

Si vous n'arrivez pas à apporter les modifications requises en amont, vous pouvez essayer d'envoyer des correctifs hors de l’arborescence à ACK directement. Pour envoyer des correctifs hors du dépôt, vous devez créer un problème dans l'IT qui cite le correctif et explique pourquoi il ne peut pas être envoyé en amont (voir la liste précédente pour obtenir des exemples). Toutefois, dans certains cas, le code ne peut pas être envoyé en amont. Ces cas sont couverts comme suit et doivent respecter les consignes de contribution pour les correctifs spécifiques à Android et être tagués avec le préfixe ANDROID: dans l'objet.

Modifications apportées à gki_defconfig

Toutes les modifications CONFIG apportées à gki_defconfig doivent être appliquées aux versions arm64 et x86, sauf si la CONFIG est spécifique à l'architecture. Pour demander une modification d'un paramètre CONFIG, créez un problème dans l'équipe IT afin de discuter de la modification. N'importe quelle valeur Modification de CONFIG qui affecte l'interface de module du noyau (KMI) après son figée est refusée. Lorsque des partenaires demandent des paramètres contradictoires pour une seule configuration, nous les résolvons 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 de liaison est géré en amont, les modifications apportées aux fonctionnalités d'héritage de priorité du pilote de liaison ne peuvent pas être envoyées en amont, car elles sont spécifiques à Android. Dans votre bug et votre correctif, indiquez clairement pourquoi le code ne peut pas être envoyé en amont. Si possible, divisez les correctifs en morceaux qui peuvent être envoyés en amont et des éléments spécifiques à Android qui ne peuvent pas être envoyés ; en amont afin de minimiser la quantité de code hors-arbre conservé dans ACK.

Les autres modifications de cette catégorie incluent les mises à jour des fichiers de représentation KMI, des listes de symboles KMI, de gki_defconfig, des scripts de compilation ou de configuration, ou d'autres scripts qui n'existent pas en amont.

Modules hors arborescence

Linux en amont déconseille activement la création de modules hors arborescence. C'est une position raisonnable étant donné que les responsables de Linux n'offrent aucune garantie à propos de la compatibilité binaire ou source dans le noyau et ne voulez pas prendre en charge le code qui ne figure 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 prise en charge d'un noyau. Par conséquent, il existe une classe de modifications pour prendre en charge les modules du fournisseur qui sont acceptables pour l'ACK, mais pas pour l'amont.

Prenons l'exemple d'un correctif qui ajoute des macros EXPORT_SYMBOL_GPL() où les modules qui utilisent l'exportation ne figurent pas dans l'arborescence source. Bien que vous deviez essayer pour demander EXPORT_SYMBOL_GPL() en amont et fournir un module qui utilise le récemment exporté, si la raison pour laquelle le module n'est pas envoyé en amont, vous pouvez envoyer le correctif à ACK à la place. Vous devez inclure la justification pour laquelle le module ne peut pas être intégré dans le problème. (Ne demandez pas la variante non-GPL, EXPORT_SYMBOL().)

Configurations masquées

Certains modules dans l'arborescence 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 compilation de modules hors du nœud, 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 est automatiquement sélectionné en fonction de la configuration du noyau CONFIG_GKI_HACKS_TO_FIX, qui est activé 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, soit explicitement, soit en tant que dépendance.

Gouverneurs pouvant être chargés

Pour les frameworks de noyau (tels que cpufreq) compatibles avec les gouverneurs chargeables, vous peut remplacer le gouverneur par défaut (tel que le gouverneur schedutil de cpufreq). Pour (par exemple, l'infrastructure thermique) qui ne sont pas compatibles avec les gouverneurs chargeables ou pilotes, mais nécessitant une implémentation spécifique au fournisseur, créez un problème au service informatique et consultez l'équipe du noyau Android.

Nous collaborerons avec vous et les mainteneurs en amont pour ajouter la prise en charge nécessaire.

Hooks du 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ée dans des modules et ne sera pas acceptée dans les noyaux en amont ou dans ACK. Pour activer les fonctionnalités à valeur ajoutée sur lesquelles les partenaires s'appuient avec un impact minimal sur le code du noyau principal, GKI accepte les hooks du 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 qui sont disponibles pour stocker des données spécifiques au fournisseur afin d'implémenter ces fonctionnalités.

Il existe deux variantes des accroches de fournisseurs (normales et limitées), basées sur Points de trace (et non les événements de trace) auxquels les modules du fournisseur peuvent s'associer. Par exemple : au lieu d'ajouter une nouvelle fonction sched_exit() pour effectuer une traçabilité au niveau de la tâche les fournisseurs peuvent ajouter un hook dans do_exit() qu'un module de fournisseur peut associer pour traitement. Un exemple d'implémentation inclut les hooks du fournisseur suivants.

  • Les hooks de fournisseur normaux utilisent DECLARE_HOOK() pour créer une fonction tracepoint nommée trace_name, où name est l'identifiant unique de traceur. Par convention, les noms de crochets de fournisseur normaux commencent par android_vh. Le nom du crochet sched_exit() est donc android_vh_sched_exit.
  • Des hooks de fournisseur restreints sont nécessaires dans les cas, par exemple, pour les hooks de planificateur. la fonction associée doit être appelée même si le processeur est hors connexion ou nécessite dans un contexte non atomique. Les hooks de fournisseurs restreints ne peuvent pas être dissociés. Par conséquent, les modules qui s'attachent à un hook restreint ne peuvent jamais être désinstallés. Les noms de crochets de fournisseurs restreints commencent par android_rvh.

Pour ajouter un crochet de fournisseur, envoyez une demande d'assistance IT 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 ne se fait qu'en ACK. N'envoyez donc pas ces des correctifs à Linux en amont.

Ajouter des champs de fournisseur aux structures

Vous pouvez associer des données sur les fournisseurs à des structures de données clés en ajoutant des champs android_vendor_data à l'aide des macros ANDROID_VENDOR_DATA(). Pour Par exemple, pour accepter des caractéristiques à valeur ajoutée, ajoutez des champs aux structures comme indiqué. dans l'exemple de code suivant.

Pour éviter les conflits potentiels entre les champs requis par les fournisseurs et les champs 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 des 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 les hooks de fournisseur

Ajoutez des hooks du fournisseur au code du kernel 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() au 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() ne vérifie initialement que si quelque chose est associé. Toutefois, si un module "provider" 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 concernant les verrous maintenus, l'état du RCS et d'autres facteurs. Le hook doit être défini dans un fichier d'en-tête du répertoire include/trace/hooks.

Par exemple, le code suivant fournit 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 de fournisseur, ajoutez le fichier d'en-tête avec la déclaration de hook vers drivers/android/vendor_hooks.c et exporter symboles. Par exemple, le code suivant complète la déclaration du android_vh_sched_exit() crochet.

#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 de hook doivent être entièrement définies afin de garantir la stabilité de l'ABI. Sinon, il est dangereux de désadresser les pointeurs opaques ou d'utiliser la struct dans des contextes de taille. L'instruction include 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 endommagent le KMI. Déclarez plutôt les types de manière indirecte.

Associer à des hooks de fournisseur

Pour utiliser des hooks de fournisseur, le module du fournisseur doit enregistrer un gestionnaire pour le hook. (généralement effectuée lors de l'initialisation du module). Par exemple, le code suivant affiche le gestionnaire du 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 des hooks de fournisseur à partir de fichiers d'en-tête, vous devrez peut-être mettre à jour le fichier d'en-tête du hook de fournisseur pour supprimer 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 pu être 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 le correctif équivalent au fichier d'en-tête de 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

Définir UNDEF_TRACE_INCLUDE_PATH indique à include/trace/define_trace.h de annulez la définition de TRACE_INCLUDE_PATH après avoir créé les points de trace.

Principales fonctionnalités 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 dans noyau. Créez un problème dans le suivi des problèmes (IT) pour lancer la conversation.

Interface de programmation d'application utilisateur (UAPI)

  • Fichiers d'en-tête UAPI Modifications apportées à Fichiers d'en-tête UAPI doivent avoir lieu en amont, sauf si les modifications concernent des interfaces spécifiques à Android. Utilisez des fichiers d'en-tête spécifiques au fournisseur pour définir des 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 (de tels ajouts ne sont valides que dans les modules de fournisseurs). nœuds sysfs utilisés par le SoC et des bibliothèques indépendantes de l'appareil et du code Java qui comprend le framework Android. ne peuvent être modifiés que d'une manière compatible et doivent être modifiés en amont si ce ne sont pas des nœuds sysfs spécifiques à Android. Vous pouvez créer des nœuds sysfs spécifiques au fournisseur à utiliser 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. C'est à vous pour ajouter les étiquettes SELinux appropriées afin d'autoriser l'accès le logiciel du fournisseur.
  • Nœuds DebugFS Les modules du fournisseur peuvent définir des nœuds dans debugfs uniquement à des fins de débogage (car debugfs n'est pas installé pendant le fonctionnement normal de l'appareil).