La Generic Kernel Image (GKI) riduce la frammentazione del kernel allineandosi strettamente con il kernel Linux upstream. Tuttavia, ci sono valide ragioni per cui alcune patch non possono essere accettate a monte e ci sono programmi di prodotto che devono essere rispettati, quindi alcune patch vengono mantenute nelle origini Android Common Kernel (ACK) da cui è stato creato il GKI.
Gli sviluppatori devono inviare le modifiche al codice a monte utilizzando la Linux Kernel Mailing List (LKML) come prima scelta e inviare le modifiche al codice al ramo ACK android-mainline
solo quando esiste una valida ragione per cui l'upstream non è praticabile. Esempi di motivi validi e come gestirli sono elencati di seguito.
La patch è stata inviata a LKML, ma non è stata accettata in tempo per il rilascio del prodotto. Per gestire questa patch:
- Fornisci la prova che la patch è stata inviata a LKML e i commenti ricevuti per la patch o un tempo stimato entro il quale la patch verrà inviata a monte.
- Decidi una linea d'azione per far atterrare 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 fornitore, ma non può essere inviata a monte perché non ci sono moduli in-tree che consumano quel simbolo. Per gestire questa patch, fornisci i dettagli sul motivo per cui il tuo modulo non può essere inviato a monte e le alternative che hai preso in considerazione prima di effettuare questa richiesta.La patch non è abbastanza generica per l'upstream e non c'è tempo per il refactoring prima del rilascio di un prodotto. Per gestire questa patch, fornire un tempo stimato entro il quale una patch con refactoring verrà inviata a monte (la patch non sarà accettata in ACK senza un piano per inviare una patch con refactoring a monte 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 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, specialmente nelle prime fasi di GKI mentre tutti stanno imparando a lavorare a monte ma non possono allentare i programmi del prodotto per farlo. Aspettatevi che i requisiti di upstreaming diventino più rigorosi nel tempo.
Requisiti della patch
Le patch devono essere conformi agli standard di codifica del kernel Linux descritti nell'albero dei sorgenti di Linux , indipendentemente dal fatto che vengano inviate a monte o all'ACK. Lo script scripts/checkpatch.pl
viene eseguito come parte del test di preinvio di Gerrit, quindi eseguilo in anticipo per assicurarti che venga superato. Per eseguire lo script checkpatch con la stessa configurazione del test di preinvio, utilizzare build/static_analysis/checkpatch_presubmit.sh
dal checkout del repo
.
Patch ACK
Le patch inviate ad ACK devono essere conformi agli standard di codifica del kernel Linux e alle linee guida per la contribuzione . Devi 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 a monte. 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 a monte o in android-mainline
, può essere trasferita all'ACK basato su LTS appropriato (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 alla versione upstream e garantisce che la patch sia nel prossimo ACK basato su LTS. Le eccezioni includono i casi in cui viene eseguito il backport di una patch upstream su android12-5.4
(poiché è probabile che la patch sia già in android-mainline
).
Patch a monte
Come specificato nelle linee guida sui contributi , le patch upstream destinate ai kernel ACK rientrano nei seguenti gruppi (elencati in ordine di probabilità di essere accettati).
-
UPSTREAM:
- È probabile che le patch selezionate da "android-mainline" vengano accettate in ACK se esiste un caso d'uso ragionevole. -
BACKPORT:
- È probabile che vengano accettate anche le patch dall'upstream che non selezionano correttamente e richiedono modifiche 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 il contenuto che per il programma. -
FROMLIST:
- È improbabile che le patch che sono state inviate a LKML ma non siano state ancora accettate in un ramo manutentore vengano accettate, a meno che la giustificazione non sia abbastanza convincente da accettare la patch indipendentemente dal fatto che arrivi o meno in Linux a monte (supponiamo che non lo farà). Deve esserci un problema associato alle patchFROMLIST
per facilitare la discussione con il team del kernel Android.
Patch specifiche per Android
Se non riesci a far atterrare le modifiche richieste a monte, puoi tentare di inviare le patch out-of-tree direttamente ad ACK. L'invio di patch out-of-tree richiede la creazione di un problema nell'IT che citi la patch e il motivo 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 coperti come segue e devono seguire le linee guida per il contributo per le patch specifiche di Android ed essere contrassegnati con ANDROID:
prefisso nell'oggetto.
Modifiche a gki_defconfig
Tutte le modifiche CONFIG
a gki_defconfig
devono essere applicate a 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 influisca sull'interfaccia del modulo kernel (KMI) dopo che è stata bloccata viene rifiutata. Nei casi in cui i partner richiedano impostazioni contrastanti per una singola configurazione, risolviamo i conflitti discutendo i relativi bug.
Codice che non esiste a monte
Le modifiche al codice che sono già specifiche 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à prioritaria del driver del raccoglitore non possono essere inviate a monte perché sono specifiche di Android. Sii esplicito nel tuo bug e correggi perché il codice non può essere inviato a monte. Se possibile, dividi le patch in parti che possono essere inviate a monte e parti specifiche di Android che non possono essere inviate a monte per ridurre al minimo la quantità di codice out-of-tree mantenuto in ACK.
Altre modifiche in questa categoria sono aggiornamenti ai file di rappresentazione KMI, elenchi di simboli KMI, gki_defconfig
, script di compilazione o configurazione o altri script che non esistono a monte.
Moduli fuori dall'albero
Linux a monte 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 sulla compatibilità dei sorgenti o binari nel kernel e non vogliono supportare il codice che non è 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 l'upstream.
Ad esempio, considera una patch che aggiunge le macro EXPORT_SYMBOL_GPL()
in cui i moduli che utilizzano l'esportazione non sono nell'albero dei sorgenti. Mentre devi tentare di richiedere EXPORT_SYMBOL_GPL()
a monte e fornire un modulo che utilizza il simbolo appena esportato, se c'è una giustificazione valida per cui il modulo non viene inviato a monte, puoi invece inviare la patch a ACK. È necessario includere la giustificazione del motivo per cui il modulo non può essere sottoposto a upstream nel problema. (Non richiedere la variante non GPL, EXPORT_SYMBOL()
.)
Configurazioni nascoste
Alcuni moduli in-tree 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
. Usa questo meccanismo solo per le 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 i governatori caricabili, è possibile eseguire l'override del governor predefinito (come il governatore schedutil
di cpufreq
. Per i framework (come il framework termico) che non supportano i governatori o i driver caricabili ma richiedono comunque un implementazione specifica del fornitore, creare un problema nell'IT e consultare il team del kernel Android .
Lavoreremo con te e con i manutentori a monte per aggiungere il supporto necessario.
Ganci del venditore
Nelle versioni precedenti, era possibile aggiungere modifiche specifiche del fornitore direttamente nel kernel principale. Questo non è possibile con GKI 2.0 perché il codice specifico del prodotto deve essere implementato nei moduli e non sarà accettato nei kernel core a monte o in ACK. Per abilitare funzionalità a valore aggiunto su cui i partner fanno affidamento con un impatto minimo sul codice del kernel principale, GKI accetta hook del fornitore che consentono di richiamare i moduli dal codice del kernel principale. Inoltre, le strutture di dati chiave possono essere riempite con campi di dati del fornitore che sono disponibili per archiviare dati specifici del fornitore per implementare queste funzionalità.
Gli hook del fornitore sono disponibili in due varianti (normale e limitato) che si basano su tracepoint (non eventi di traccia) a cui i moduli del fornitore possono collegarsi. Ad esempio, invece di aggiungere una nuova funzione sched_exit()
per eseguire una contabilità all'uscita dall'attività, i fornitori possono aggiungere un hook in do_exit()
a cui un modulo fornitore può collegarsi per l'elaborazione. Un'implementazione di esempio include i seguenti vendor hook.
- I normali vendor hook utilizzano
DECLARE_HOOK()
per creare una funzione tracepoint con il nometrace_ name
dovename
è l'identificatore univoco per la traccia. Per convenzione, i normali nomi di hook del fornitore iniziano conandroid_vh
, quindi il nome per l'hooksched_exit()
sarebbeandroid_vh_sched_exit
. - Gli hook del fornitore limitati sono necessari per casi come gli hook dello scheduler in cui la funzione collegata deve essere chiamata anche se la CPU è offline o richiede un contesto non atomico. Gli hook del fornitore con restrizioni non possono essere scollegati, quindi i moduli che si collegano a un hook con restrizioni non possono mai essere scaricati. È consentito un solo allegato, quindi qualsiasi altro tentativo di collegamento fallisce con
-EBUSY
. I nomi degli hook del fornitore limitati iniziano conandroid_rvh
.
Per aggiungere un vendor hook, segnala un problema all'IT e invia le patch (come per tutte le patch specifiche per Android, deve esistere un problema e devi fornire una giustificazione). Il supporto per i vendor hook è solo in ACK, quindi non inviare queste patch a Linux upstream.
Aggiungere campi fornitore alle strutture
Puoi associare i dati del fornitore a strutture di dati chiave aggiungendo i campi android_vendor_data
utilizzando le macro ANDROID_VENDOR_DATA()
. Ad esempio, per supportare funzionalità a valore aggiunto, aggiungi i campi alle strutture come mostrato nell'esempio di codice seguente.
Per evitare potenziali conflitti tra i campi richiesti dai fornitori e i campi richiesti dagli OEM, gli OEM non devono mai utilizzare i campi dichiarati utilizzando le macro ANDROID_VENDOR_DATA()
. Invece, gli OEM devono 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 gli hook del fornitore
Aggiungere vendor hook 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()
controlla inizialmente solo se qualcosa è collegato. Tuttavia, se un modulo fornitore registra un gestore utilizzando register_trace_android_vh_sched_exit()
, viene chiamata la funzione registrata. Il conduttore deve essere a conoscenza del contesto per quanto riguarda i blocchi bloccati, 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 all'interno della 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 di 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 avanti dichiarare i tipi.
Attaccare ai ganci del fornitore
Per utilizzare gli hook del fornitore, il modulo del fornitore deve registrare un gestore per l'hook (tipicamente fatto 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 consente di implementare una funzionalità da un modulo, è necessario aggiungere la funzionalità come modifica specifica di Android al kernel principale. Crea un problema nel tracker dei problemi (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 il 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 che verranno utilizzati dallo spazio utente del fornitore. Per impostazione predefinita, l'accesso ai nodi sysfs da parte dello spazio utente è negato utilizzando SELinux. Spetta al fornitore aggiungere le etichette SELinux appropriate per consentire l'accesso al software del fornitore autorizzato.
- Eseguire il debug dei nodi FS. I moduli del fornitore possono definire nodi in
debugfs
solo per il debug (poichédebugfs
non è montato durante il normale funzionamento del dispositivo).