L'immagine del kernel generico (GKI) riduce la frammentazione del kernel allineandosi strettamente al kernel Linux upstream. Tuttavia, esistono motivi validi per cui alcune patch non possono essere accettate a monte e ci sono pianificazioni dei prodotti che devono essere rispettate, pertanto alcune patch vengono mantenute nelle sorgenti del kernel comune di Android (ACK) da cui viene compilato il GKI.
Gli sviluppatori devono inviare le modifiche al codice in upstream utilizzando come prima scelta la mailing list Linux Kernel (LKML) e inviare le modifiche al codice al ramo ACKandroid-mainline
solo quando esiste un motivo valido per cui l'upstream non è fattibile. Di seguito sono riportati esempi di motivi validi e come gestirli.
La patch è stata inviata a LKML, ma non è stata accettata in tempo per una release del prodotto. Per gestire questo patch:
- Fornisci la prova che la patch è stata inviata a LKML e i commenti ricevuti per la patch oppure una stima del tempo entro cui la patch verrà inviata a monte.
- Decidi un'azione per inserire la patch in ACK, ottenerne l'approvazione in upstream e poi rimuoverla da ACK quando la versione finale di upstream viene unita ad ACK.
La patch definisce
EXPORT_SYMBOLS_GPL()
per un modulo del fornitore, ma non è stato possibile inviarla a monte perché non esistono moduli in-tree che utilizzano questo simbolo. Per gestire questa patch, fornisci i dettagli sul motivo per cui il modulo non può essere inviato in upstream 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 eseguire il refactoring prima del rilascio di un prodotto. Per gestire questa patch, fornisci un tempo stimato entro il quale una patch sottoposta a refactoring viene inviata a monte (la patch non sarà accettata in ACK senza un piano di invio di una patch sottoposta a refactoring a monte per la revisione).
La patch non può essere accettata dall'upstream perché... <insert reason here>. Per gestire questa patch, contatta il team del kernel di Android e collabora con noi per trovare le opzioni per eseguire il refactoring della patch in modo che possa essere inviata per la revisione e accettata in upstream.
Esistono molte altre potenziali giustificazioni. Quando invii il bug o la patch, includi una motivazione valida e preparati a qualche iterazione e discussione. Siamo consapevoli che l'ACK è dotato di alcune patch, soprattutto nelle prime fasi di GKI, mentre tutti stanno imparando a lavorare a monte, ma non possiamo allentare le pianificazioni dei prodotti per farlo. Ci aspettiamo che i requisiti upstream diventino più rigidi nel tempo.
Requisiti delle patch
Le patch devono essere conformi agli standard di codifica del kernel Linux descritti nella
struttura delle origini di Linux,
indipendentemente dal fatto che vengano inviate upstream o ACK. Lo script scripts/checkpatch.pl
viene eseguito nell'ambito dei test di pre-invio di Gerrit, quindi eseguilo in anticipo per assicurarti che superi i test. Per eseguire lo script checkpatch con la stessa configurazione dei test pre-invio, utilizza //build/kernel/static_analysis:checkpatch_presubmit
.
Per maggiori dettagli, consulta
build/kernel/kleaf/docs/checkpatch.md.
Patch ACK
Le patch inviate a ACK devono essere conformi agli standard di programmazione dei kernel Linux e alle linee guida per i contributi.
Devi includere un tag Change-Id
nel messaggio del commit. Se invii la patch a più branch (ad esempio android-mainline
e android12-5.4
), devi utilizzare lo stessoChange-Id
per tutte le istanze della patch.
Invia prima le patch a LKML per una revisione a monte. Se il patch è:
- Accettato a monte, viene unito automaticamente a
android-mainline
. - Non accettato a monte, invialo a
android-mainline
con un riferimento all'invio upstream o una spiegazione sul motivo per cui non è stato inviato a LKML.
Dopo che una patch è stata accettata in upstream o in android-mainline
, può essere eseguita la procedura di backporting nell'ACK basato su LTS appropriato (ad esempio android12-5.4
e
android11-5.4
per le patch che correggono il codice specifico di Android). L'invio a android-mainline
consente di eseguire test con nuovi candidati di release upstream e garantisce che la patch sarà nel successivo ACK basato su LTS. Le eccezioni includono i casi in cui una patch upstream viene sottoposta a backport a android12-5.4
(in quanto è probabile che sia già presente in android-mainline
).
Patch upstream
Come specificato nelle linee guida per i contributi, le patch upstream destinate ai kernel ACK rientrano nei seguenti gruppi (elencati in ordine di probabilità di accettazione).
UPSTREAM:
: è probabile che le patch scelte da "android-mainline" vengano accettate in ACK se esiste un caso d'uso ragionevole.BACKPORT:
- Anche le patch a monte che non vengono selezionate in modo accurato e che non richiedono una modifica saranno probabilmente accettate se esiste un caso d'uso ragionevole.FROMGIT:
: le patch selezionate da un ramo di manutenzione in preparazione all'invio alla linea principale Linux potrebbero essere accettate in caso di scadenza imminente. Queste devono essere giustificate sia per i contenuti che per la programmazione.FROMLIST:
. È improbabile che le patch inviate a LKML ma che non sono ancora state accettate in un ramo del manutentore, a meno che la giustificazione non sia abbastanza convincente da far sì che la patch venga accettata indipendentemente dal fatto che venga raggiunta o meno in un upstream Linux (supponiamo che non lo sarà). Deve esserci un problema associato alle patchFROMLIST
per facilitare la discussione con il team del kernel di Android.
Patch specifiche per Android
Se non riesci a ottenere le modifiche richieste a monte, puoi tentare di inviare patch out-of-tree direttamente a ACK. L'invio di patch out-of-tree richiede la creazione di un problema nel reparto IT che menzioni la patch e il motivo per cui non può essere inviata a monte (vedi l'elenco precedente per alcuni esempi).
Tuttavia, in alcuni casi il codice non può essere inviato a monte. Questi casi sono trattati come segue, 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 a CONFIG
in gki_defconfig
devono essere applicate sia alle versioni arm64 sia a quelle x86, a meno che CONFIG
non sia specifico per l'architettura. Per richiedere una modifica
a un'impostazione di CONFIG
, crea un problema nell'IT per discutere della modifica. Qualsiasi modifica a CONFIG
che interessa l'interfaccia del modulo del kernel (KMI) dopo il suo blocco viene rifiutata. Nei casi in cui i partner richiedano impostazioni in conflitto per una singola configurazione, risolviamo i conflitti esaminando i 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 binder viene gestito a monte, le modifiche alle funzionalità di eredità della priorità del driver binder non possono essere inviate a monte perché sono specifiche di Android. Spiega esplicitamente il motivo per cui il codice non può essere inviato a monte nel bug e nella patch. Se possibile, suddividi le patch in parti che possono essere inviate in upstream e parti specifiche per Android che non possono essere inviate in upstream per ridurre al minimo la quantità di codice out-of-tree gestito in ACK.
Altre modifiche in questa categoria sono gli aggiornamenti ai file di rappresentazione KMI, agli elenchi di simboli KMI, a gki_defconfig
, agli script di compilazione o alla configurazione o ad altri script che non esistono a monte.
Moduli esterni alla struttura
L'upstream Linux scoraggia attivamente il supporto per la compilazione di moduli out-of-tree. Si tratta di una posizione ragionevole, dato che i manutentori di Linux non forniscono garanzie sulla compatibilità binaria o del codice in-kernel e non vogliono supportare il codice che non è presente nell'albero. Tuttavia, il GKI fornisce garanzie ABI per i moduli del fornitore, garantendo che le interfacce KMI siano stabili per l'intera vita utile supportata di un kernel. Pertanto, esiste una classe di modifiche per supportare i moduli del fornitore che sono accettabili per ACK, ma non per l'upstream.
Ad esempio, prendi in considerazione una patch che aggiunge macro EXPORT_SYMBOL_GPL()
se i moduli che utilizzano l'esportazione non sono presenti nella struttura di origine. Devi tentare di richiedere EXPORT_SYMBOL_GPL()
in upstream e fornire un modulo che utilizzi il simbolo appena esportato. Tuttavia, se esiste una giustificazione valida per cui il modulo non viene inviato in upstream, puoi inviare la patch ad ACK. Devi includere nel problema la motivazione per cui non è possibile eseguire l'upstream del modulo. Non richiedere la variante non GPL, EXPORT_SYMBOL()
.
Configurazioni nascoste
Alcuni moduli in-tree selezionano automaticamente le configurazioni nascoste che non possono essere specificate in gki_defconfig
. Ad esempio, CONFIG_SND_SOC_TOPOLOGY
viene selezionato automaticamente quando è configurato CONFIG_SND_SOC_SOF=y
. Per supportare la compilazione di moduli out-of-tree, GKI include un meccanismo per abilitare le configurazioni nascoste.
Per attivare 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
, attivata in gki_defconfig
. Utilizza questo meccanismo solo per le configurazioni nascoste. Se la configurazione non è nascosta, deve essere specificata in gki_defconfig
in modo esplicito o come dipendenza.
Governatori caricabili
Per i framework del kernel (come cpufreq
) che supportano i gestori caricabili, puoi eseguire l'override del gestore predefinito (ad esempio il gestore schedutil
di cpufreq
). Per
i framework (come il framework termico) che non supportano regolatori o driver caricabili,
ma richiedono comunque un'implementazione specifica del fornitore, crea un problema
nel team IT e consulta il team kernel Android.
Collaboreremo con te e con i manutentori a monte per aggiungere il supporto necessario.
Hook del fornitore
Nelle versioni precedenti, era possibile aggiungere modifiche specifiche del fornitore direttamente nel kernel di base. Questo non è possibile con GKI 2.0 perché il codice specifico del prodotto deve essere implementato nei moduli e non verrà accettato nei kernel di base a monte o in ACK. Per abilitare funzionalità a valore aggiunto su cui i partner fanno affidamento con un impatto minimo sul codice 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 completate con campi di dati del fornitore disponibili per memorizzare i dati specifici del fornitore per implementare queste funzionalità.
Gli hook del fornitore sono disponibili in due varianti (normale e con limitazioni) che si basano su
tracepoint (non eventi di traccia) a cui i moduli del fornitore possono essere collegati. Ad esempio,
invece di aggiungere una nuova funzione sched_exit()
per eseguire la contabilità all'uscita della task, 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 del fornitore normali utilizzano
DECLARE_HOOK()
per creare una funzione tracepoint con il nometrace_name
, dovename
è l'identificatore univoco della traccia. Per convenzione, i nomi dei normali hook del fornitore iniziano conandroid_vh
, quindi il nome dell'hooksched_exit()
saràandroid_vh_sched_exit
. - Gli hook del fornitore con limitazioni 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 nonatomic. Gli hook dei fornitori con limitazioni non possono essere scollegati, pertanto i moduli
che si attaccano a un hook con limitazioni non possono mai essere scaricati. I nomi degli hook dei fornitori con limitazioni iniziano con
android_rvh
.
Per aggiungere un hook del fornitore, segnala un problema nell'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 gli hook dei fornitori è disponibile solo in ACK, quindi non inviare queste patch a Linux upstream.
Aggiungere campi dei fornitori alle strutture
Puoi associare i dati dei fornitori alle strutture di dati chiave aggiungendo
i campi android_vendor_data
con le macro ANDROID_VENDOR_DATA()
. Ad esempio, per supportare le funzionalità con valore aggiunto, aggiungi i campi alle strutture come mostrato nell'esempio di codice seguente.
Per evitare potenziali conflitti tra i campi necessari per i fornitori e i campi necessari per gli 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 hook del fornitore
Aggiungi gli hook del fornitore al codice del kernel come tracepoint dichiarandoli utilizzando
DECLARE_HOOK()
o DECLARE_RESTRICTED_HOOK()
e poi aggiungendoli al codice come
un tracepoint. Ad esempio, per aggiungere trace_android_vh_sched_exit()
alla
funzione 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 è collegato qualcosa. Tuttavia, se un modulo del fornitore registra un gestore utilizzando register_trace_android_vh_sched_exit()
, viene chiamata la funzione registrata. L'handle deve essere a conoscenza del contesto per quanto riguarda le chiavi bloccate, lo stato RCS e altri fattori. L'hook deve essere definito in un file di intestazione nella directory include/trace/hooks
.
Ad esempio, il seguente codice 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 creare istanze delle interfacce richieste per il 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 seguente codice completa la dichiarazione dell'hookandroid_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 di dati utilizzate all'interno della dichiarazione dell'hook devono essere completamente definite per garantire la stabilità dell'ABI. In caso contrario, non è sicuro eseguire il dereferenziamento dei puntatori opachi o utilizzare la struttura in contesti con dimensioni. L'istruzione include
che fornisce la definizione completa di queste strutture di dati deve trovarsi all'interno della
sezione #ifndef __GENKSYMS__
di drivers/android/vendor_hooks.c
. I file di intestazione in include/trace/hooks
non devono includere il file di intestazione del kernel con le definizioni di tipo per evitare modifiche al CRC che compromettono il KMI. Dichiara invece i tipi.
Collega gli hook del fornitore
Per utilizzare gli hook del fornitore, il modulo del fornitore deve registrare un gestore per l'hook (in genere viene eseguito durante l'inizializzazione del modulo). Ad esempio, il codice seguente mostra l'handler 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);
...
}
Utilizzare i hook del fornitore dai file intestazione
Per utilizzare gli hook del fornitore dai file di intestazione, potresti dover aggiornare il file di intestazione dell'hook del fornitore in modo che non definisca TRACE_INCLUDE_PATH
per evitare errori di build che indicano che non è stato possibile trovare un file di intestazione del punto di traccia. Ad esempio,
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.
Per correggere questo tipo di errore di compilazione, applica la correzione equivalente al file di intestazione dell'hook del fornitore che stai includendo. Per ulteriori informazioni, consulta 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 definizione di UNDEF_TRACE_INCLUDE_PATH
indica a include/trace/define_trace.h
di eliminare la definizione di TRACE_INCLUDE_PATH
dopo aver creato i punti traccia.
Funzionalità di base del kernel
Se nessuna delle tecniche precedenti ti consente di implementare una funzionalità da un modulo, devi aggiungerla come modifica specifica per Android al kernel di base. Crea un problema in Issue Tracker (IT) per avviare la conversazione.
Interfaccia di programmazione di un'applicazione utente (UAPI)
- File di intestazione UAPI. Le modifiche ai file di intestazione UAPI devono avvenire a monte, a meno che non riguardino interfacce specifiche di Android. Utilizza file di intestazione specifici del fornitore per definire le interfacce tra i moduli e il codice dello spazio utente del fornitore.
- Nodi sysfs. Non aggiungere nuovi nodi sysfs al kernel GKI (queste aggiunte sono valide solo nei moduli del fornitore). I nodi sysfs utilizzati dalle librerie SoC e indipendenti dal dispositivo e dal codice Java che compongono il framework Android possono essere modificati solo in modi compatibili e devono essere modificati in upstream se non sono nodi sysfs specifici per Android. Puoi creare nodi sysfs specifici del fornitore da utilizzare dallo spazio utente del fornitore. Per impostazione predefinita, l'accesso ai nodi sysfs da parte dello spazio utente è negato tramite SELinux. Spetta al fornitore aggiungere le etichette SELinux appropriate per consentire l'accesso da parte del software del fornitore autorizzato.
- Nodi DebugFS. I moduli del fornitore possono definire i nodi in
debugfs
solo per il debugging (poichédebugfs
non è montato durante il normale funzionamento del dispositivo).