Sviluppare il codice kernel per GKI

L'immagine generica del kernel (GKI) riduce la frammentazione del kernel allineandosi strettamente con il kernel Linux upstream. Tuttavia, esistono validi motivi per cui alcune patch non possono essere accettate a monte e ci sono pianificazioni del prodotto che devono essere rispettate, quindi alcune patch vengono mantenute nelle origini Android Common Kernel (ACK) da cui è creata la GKI.

Gli sviluppatori devono inviare modifiche al codice a monte utilizzando la Linux Kernel Mailing List (LKML) come prima scelta e inviare modifiche al codice al ramo ACK android-mainline solo quando esiste un motivo valido per cui l'upstream non è fattibile. Di seguito sono elencati esempi di motivi validi e come gestirli.

  • La patch è stata inviata a LKML, ma non è stata accettata in tempo per il rilascio del prodotto. Per gestire questa patch:

    • Fornire la prova che la patch è stata inviata a LKML e i commenti ricevuti per la patch, oppure un tempo stimato entro il quale la patch verrà inviata in upstream.
    • Decidere una linea d'azione per inserire la patch in ACK, farla approvare a monte e quindi rimuoverla da ACK quando la versione finale a monte viene unita in ACK.
  • La patch definisce EXPORT_SYMBOLS_GPL() per un modulo del fornitore, ma non è stato possibile inviarla a monte perché non sono presenti moduli nell'albero che utilizzano quel simbolo. Per gestire questa patch, fornisci dettagli sul motivo per cui il tuo modulo non può essere inviato in upstream e le alternative che hai considerato prima di effettuare questa richiesta.

  • La patch non è sufficientemente generica per l'upstream e non c'è tempo per rifattorizzarla prima del rilascio del prodotto. Per gestire questa patch, fornire un tempo stimato entro il quale una patch rifattorizzata verrà inviata in upstream (la patch non verrà accettata in ACK senza un piano per inviare una patch rifattorizzata in upstream per la revisione).

  • La patch non può essere accettata dall'upstream perché... <inserire il motivo qui> . Per gestire questa patch, contatta il team del kernel Android e collabora con noi sulle opzioni per effettuare il refactoring della patch in modo che possa essere inviata per la revisione e accettata a monte.

Ci sono molte altre potenziali giustificazioni. Quando invii il tuo bug o la tua patch, includi una giustificazione valida e aspettati qualche iterazione e discussione. Riconosciamo che l'ACK conterrà alcune patch, soprattutto nelle prime fasi di GKI mentre tutti stanno imparando come lavorare a monte ma non possono allentare i programmi dei prodotti per farlo. Prevediamo che i requisiti di upstreaming diventeranno più rigorosi nel tempo.

Requisiti della patch

Le patch devono essere conformi agli standard di codifica del kernel Linux descritti nell'albero dei sorgenti Linux , indipendentemente dal fatto che siano inviate upstream o tramite ACK. Lo script scripts/checkpatch.pl viene eseguito come parte del test di preinvio di Gerrit, quindi eseguilo in anticipo per assicurarti che passi. Per eseguire lo script checkpatch con la stessa configurazione del test di preinvio, utilizzare //build/kernel/static_analysis:checkpatch_presubmit . Per i dettagli, vedere build/kernel/kleaf/docs/checkpatch.md .

Patch ACK

Le patch inviate ad ACK devono essere conformi agli standard di codifica del kernel Linux e alle linee guida per i contributi . È necessario includere un tag Change-Id nel messaggio di commit; se invii la patch a più rami (ad esempio, android-mainline e android12-5.4 ), devi utilizzare lo stesso Change-Id per tutte le istanze della patch.

Invia prima le patch a LKML per una revisione upstream. Se la patch è:

  • Accettato a monte, viene unito automaticamente in android-mainline .
  • Non accettato a monte, invialo ad android-mainline con un riferimento all'invio a monte o una spiegazione del motivo per cui non è stato inviato a LKML.

Dopo che una patch è stata accettata in upstream o in android-mainline , è possibile eseguirne il backport sull'ACK appropriato basato su LTS (come android12-5.4 e android11-5.4 per le patch che correggono il codice specifico di Android). L'invio ad android-mainline consente il test con i nuovi candidati al rilascio upstream e garantisce che la patch sia nel successivo ACK basato su LTS. Le eccezioni includono i casi in cui viene eseguito il backport di una patch upstream su android12-5.4 (perché è probabile che la patch sia già in android-mainline ).

Patch a monte

Come specificato nelle linee guida per il contributo , le patch upstream destinate ai kernel ACK rientrano nei seguenti gruppi (elencati in ordine di probabilità di essere accettate).

  • UPSTREAM: - È probabile che le patch selezionate da "android-mainline" vengano accettate in ACK se esiste un caso d'uso ragionevole.
  • BACKPORT: - È probabile che anche le patch provenienti dall'upstream che non vengono selezionate in modo pulito e che necessitano di modifiche vengano accettate se esiste un caso d'uso ragionevole.
  • FROMGIT: - Le patch selezionate da un ramo del manutentore in preparazione per l'invio alla linea principale di Linux potrebbero essere accettate se c'è una scadenza imminente. Questi devono essere giustificati sia per quanto riguarda il contenuto che per la pianificazione.
  • FROMLIST: - Le patch che sono state inviate a LKML ma non sono state ancora accettate in un ramo del manutentore è improbabile che vengano accettate, a meno che la giustificazione non sia sufficientemente convincente da far sì che la patch venga accettata indipendentemente dal fatto che arrivi o meno nel Linux upstream (assumiamo che non lo farà). Deve esserci un problema associato alle patch FROMLIST per facilitare la discussione con il team del kernel Android.

Patch specifiche per Android

Se non riesci a eseguire le modifiche richieste a monte, puoi tentare di inviare direttamente le patch fuori albero a ACK. L'invio di patch fuori struttura richiede la creazione di un problema nel reparto IT che citi la patch e la motivazione per cui la patch non può essere inviata a monte (vedere l'elenco precedente per gli esempi). Tuttavia, ci sono alcuni casi in cui il codice non può essere inviato a monte. Questi casi sono trattati come segue e devono seguire le linee guida per i contributi per le patch specifiche di Android ed essere contrassegnati con il prefisso ANDROID: nell'oggetto.

Modifiche a gki_defconfig

Tutte le modifiche CONFIG a gki_defconfig devono essere applicate ad entrambe le versioni arm64 e x86 a meno che CONFIG non sia specifico dell'architettura. Per richiedere una modifica a un'impostazione CONFIG , creare un problema nell'IT per discutere la modifica. Qualsiasi modifica CONFIG che influisce sull'interfaccia del modulo kernel (KMI) dopo che è stata congelata viene rifiutata. Nei casi in cui i partner richiedono impostazioni in conflitto per una singola configurazione, risolviamo i conflitti attraverso la discussione sui bug correlati.

Codice che non esiste a monte

Le modifiche al codice già specifico per Android non possono essere inviate a monte. Ad esempio, anche se il driver del raccoglitore viene mantenuto a monte, le modifiche alle funzionalità di ereditarietà della priorità del driver del raccoglitore non possono essere inviate a monte perché sono specifiche di Android. Sii esplicito nel tuo bug e correggi il motivo per cui il codice non può essere inviato a monte. Se possibile, suddividi le patch in parti che possono essere inviate a monte e in parti specifiche di Android che non possono essere inviate a monte per ridurre al minimo la quantità di codice fuori albero mantenuto in ACK.

Altre modifiche in questa categoria sono aggiornamenti ai file di rappresentazione KMI, elenchi di simboli KMI, gki_defconfig , script di build o configurazione o altri script che non esistono a monte.

Moduli fuori albero

Linux upstream scoraggia attivamente il supporto per la creazione di moduli fuori dall'albero. Questa è una posizione ragionevole dato che i manutentori di Linux non danno garanzie sul sorgente in-kernel o sulla compatibilità binaria e non vogliono supportare codice che non sia nell'albero. Tuttavia, GKI fornisce garanzie ABI per i moduli del fornitore, assicurando che le interfacce KMI siano stabili per la durata supportata di un kernel. Pertanto, esiste una classe di modifiche per supportare i moduli del fornitore che sono accettabili per ACK ma non sono accettabili per upstream.

Ad esempio, considera una patch che aggiunge macro EXPORT_SYMBOL_GPL() dove i moduli che utilizzano l'esportazione non sono nell'albero dei sorgenti. Anche se devi tentare di richiedere EXPORT_SYMBOL_GPL() a monte e fornire un modulo che utilizzi il simbolo appena esportato, se esiste una giustificazione valida per cui il modulo non viene inviato a monte, puoi invece inviare la patch a ACK. È necessario includere nel problema la giustificazione del motivo per cui non è possibile eseguire l'upstream del modulo. (Non richiedere la variante non GPL, EXPORT_SYMBOL() .)

Configurazioni nascoste

Alcuni moduli nell'albero selezionano automaticamente configurazioni nascoste che non possono essere specificate in gki_defconfig . Ad esempio, CONFIG_SND_SOC_TOPOLOGY viene selezionato automaticamente quando CONFIG_SND_SOC_SOF=y è configurato. Per consentire la creazione di moduli fuori dall'albero, GKI include un meccanismo per abilitare le configurazioni nascoste.

Per abilitare una configurazione nascosta, aggiungi un'istruzione select in init/Kconfig.gki in modo che venga selezionata automaticamente in base alla configurazione del kernel CONFIG_GKI_HACKS_TO_FIX , che è abilitata in gki_defconfig . Utilizzare questo meccanismo solo per configurazioni nascoste; se la configurazione non è nascosta, deve essere specificata in gki_defconfig esplicitamente o come dipendenza.

Governatori caricabili

Per i framework del kernel (come cpufreq ) che supportano governatori caricabili, è possibile sovrascrivere il governatore predefinito (come il governatore schedutil di cpufreq . Per i framework (come il framework termico) che non supportano governatori o driver caricabili ma richiedono comunque un implementazione specifica del fornitore, crea un problema nell'IT e consulta il team del kernel Android .

Lavoreremo con te e con i manutentori originali per aggiungere il supporto necessario.

Ganci del venditore

Nelle versioni precedenti era possibile aggiungere modifiche specifiche del fornitore direttamente nel kernel principale. Ciò non è possibile con GKI 2.0 perché il codice specifico del prodotto deve essere implementato nei moduli e non sarà accettato nei kernel core upstream o in ACK. Per abilitare funzionalità a valore aggiunto su cui fanno affidamento i partner con un impatto minimo sul codice core del kernel, GKI accetta hook dei fornitori che consentono di richiamare i moduli dal codice core del kernel. Inoltre, le strutture dati chiave possono essere riempite con campi dati del fornitore disponibili per archiviare dati specifici del fornitore per implementare queste funzionalità.

Gli hook del fornitore sono disponibili in due varianti (normale e limitata) basate su tracepoint (non eventi di traccia) a cui possono collegarsi i moduli del fornitore. Ad esempio, invece di aggiungere una nuova funzione sched_exit() per eseguire una contabilità all'uscita dell'attività, i fornitori possono aggiungere un hook in do_exit() a cui un modulo del fornitore può collegarsi per l'elaborazione. Un'implementazione di esempio include i seguenti hook del fornitore.

  • Gli hook dei fornitori normali utilizzano DECLARE_HOOK() per creare una funzione tracepoint con il nome trace_ name dove name è l'identificatore univoco per la traccia. Per convenzione, i normali nomi degli hook dei fornitori iniziano con android_vh , quindi il nome dell'hook sched_exit() sarebbe android_vh_sched_exit .
  • Gli hook del fornitore limitato sono necessari per casi come gli hook dello scheduler in cui la funzione allegata deve essere chiamata anche se la CPU è offline o richiede un contesto non atomico. Gli hook del fornitore limitato non possono essere scollegati, quindi i moduli che si collegano a un hook limitato non potranno mai essere scaricati. I nomi degli hook dei fornitori limitati iniziano con android_rvh .

Per aggiungere un hook al fornitore, segnala un problema all'IT e invia le patch (come con tutte le patch specifiche di Android, deve esistere un problema e devi fornire una giustificazione). Il supporto per gli hook dei fornitori è solo in ACK, quindi non inviare queste patch a Linux upstream.

Aggiungi campi fornitore alle strutture

Puoi associare i dati del fornitore alle strutture dati chiave aggiungendo i campi android_vendor_data utilizzando le macro ANDROID_VENDOR_DATA() . Ad esempio, per supportare funzionalità a valore aggiunto, aggiungi campi alle strutture come mostrato nell'esempio di codice seguente.

Per evitare potenziali conflitti tra i campi necessari ai fornitori e i campi necessari agli OEM, gli OEM non devono mai utilizzare i campi dichiarati utilizzando le macro ANDROID_VENDOR_DATA() . Gli OEM devono invece utilizzare ANDROID_OEM_DATA() per dichiarare i campi 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 */
}

Definire i ganci del fornitore

Aggiungi gli hook del fornitore al codice del kernel come tracepoint dichiarandoli utilizzando DECLARE_HOOK() o DECLARE_RESTRICTED_HOOK() e quindi aggiungendoli al codice come tracepoint. Ad esempio, per aggiungere trace_android_vh_sched_exit() alla funzione del kernel do_exit() esistente:

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

La funzione trace_android_vh_sched_exit() inizialmente controlla solo se è allegato qualcosa. Tuttavia, se un modulo del fornitore registra un gestore utilizzando register_trace_android_vh_sched_exit() , viene chiamata la funzione registrata. L'handler deve essere consapevole del contesto per quanto riguarda i lock mantenuti, lo stato RCS e altri fattori. L'hook deve essere definito in un file di intestazione nella directory include/trace/hooks .

Ad esempio, il codice seguente fornisce una possibile dichiarazione per trace_android_vh_sched_exit() nel file 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>

Per istanziare le interfacce richieste per l'hook del fornitore, aggiungi il file di intestazione con la dichiarazione dell'hook a drivers/android/vendor_hooks.c ed esporta i simboli. Ad esempio, il codice seguente completa la dichiarazione dell'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);

NOTA : le strutture dati utilizzate nella dichiarazione hook devono essere completamente definite per garantire la stabilità ABI. Altrimenti non è sicuro dereferenziare i puntatori opachi o utilizzare la struttura in contesti dimensionati. L'inclusione che fornisce la definizione completa di tali strutture dati dovrebbe essere inserita nella sezione #ifndef __GENKSYMS__ di drivers/android/vendor_hooks.c . I file di intestazione in include/trace/hooks non dovrebbero includere il file di intestazione del kernel con le definizioni del tipo per evitare modifiche CRC che interrompono il KMI. Invece dichiara in avanti i tipi.

Attaccalo ai ganci del venditore

Per utilizzare gli hook del fornitore, il modulo del fornitore deve registrare un gestore per l'hook (in genere effettuato durante l'inizializzazione del modulo). Ad esempio, il codice seguente mostra il gestore del modulo foo.ko per 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);
    ...
}

Funzionalità principali del kernel

Se nessuna delle tecniche precedenti ti consente di implementare una funzionalità da un modulo, devi aggiungere la funzionalità come modifica specifica di Android al kernel principale. Crea un problema nell'issue tracker (IT) per avviare la conversazione.

Interfaccia di programmazione dell'applicazione utente (UAPI)

  • File di intestazione UAPI. Le modifiche ai file di intestazione UAPI devono essere apportate a monte, a meno che le modifiche non riguardino interfacce specifiche di Android. Utilizzare file di intestazione specifici del fornitore per definire le interfacce tra i moduli del fornitore e il codice dello spazio utente del fornitore.
  • nodi sysfs. Non aggiungere nuovi nodi sysfs al kernel GKI (tali aggiunte sono valide solo nei moduli del fornitore). I nodi sysfs utilizzati dalle librerie SoC e indipendenti dal dispositivo e dal codice Java che comprende il framework Android possono essere modificati solo in modi compatibili e devono essere modificati a monte se non sono nodi sysfs specifici di Android. È possibile creare nodi sysfs specifici del fornitore da utilizzare nello spazio utente del fornitore. Per impostazione predefinita, l'accesso ai nodi sysfs in base allo spazio utente è negato utilizzando SELinux. Spetta al fornitore aggiungere le etichette SELinux appropriate per consentire l'accesso al software del fornitore autorizzato.
  • Nodi DebugFS. I moduli del fornitore possono definire nodi in debugfs solo per il debug (poiché debugfs non viene montato durante il normale funzionamento del dispositivo).