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 upstream e sono previste pianificazioni dei prodotti che devono essere rispettate, pertanto alcune patch vengono mantenute nelle origini del kernel comune Android (ACK) da cui viene creato il GKI.
Gli sviluppatori devono inviare le modifiche al codice upstream utilizzando la mailing list del kernel Linux (LKML) come prima scelta e inviare le modifiche al codice al ramo android-mainline di ACK solo quando esiste un motivo valido per cui 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 la release di un 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 viene inviata upstream.
- Decidi una linea di condotta per inserire la patch in ACK, ottenerne l'approvazione upstream e poi rimuoverla da ACK quando la versione upstream finale viene unita in ACK.
La patch definisce
EXPORT_SYMBOLS_GPL()per un modulo fornitore, ma non è stato possibile inviarla upstream perché non esistono moduli in-tree che utilizzano questo simbolo. Per gestire questa patch, fornisci dettagli sul motivo per cui il modulo non può essere inviato upstream e sulle alternative che hai preso in considerazione prima di effettuare questa richiesta.La patch non è sufficientemente generica per upstream e non c'è tempo per eseguirne il refactoring prima della release di un prodotto. Per gestire questa patch, fornisci un tempo stimato entro il quale una patch di cui è stato eseguito il refactoring viene inviata upstream (la patch non verrà accettata in ACK senza un piano per inviare una patch di cui è stato eseguito il refactoring upstream per la revisione).
La patch non può essere accettata da upstream perché... <inserisci il motivo qui>. Per gestire questa patch, contatta il team del kernel Android e collabora con noi per trovare opzioni per eseguire il refactoring della patch in modo che possa essere inviata per la revisione e accettata upstream.
Esistono molte altre potenziali giustificazioni. Quando invii il bug o la patch, includi una giustificazione valida e prevedi alcune iterazioni e discussioni. Riconosciamo che ACK include alcune patch, soprattutto nelle prime fasi di GKI, quando tutti stanno imparando a lavorare upstream, ma non è possibile posticipare le pianificazioni dei prodotti per farlo. Prevediamo che i requisiti di upstreaming diventeranno più rigorosi nel tempo.
Requisiti delle patch
Le patch devono essere conformi agli standard di codifica del kernel Linux descritti nell'
albero delle origini Linux,
indipendentemente dal fatto che vengano inviate upstream o ad ACK. Lo script scripts/checkpatch.pl viene eseguito nell'ambito dei test di pre-invio di Gerrit, quindi eseguilo in anticipo per assicurarti che venga superato. Per eseguire lo script checkpatch con la stessa configurazione dei test di pre-invio, utilizza //build/kernel/static_analysis:checkpatch_presubmit.
Per maggiori dettagli, consulta
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.
Devi includere un Change-Id
tag 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:
- Viene accettata upstream, viene unita automaticamente in
android-mainline. - Non viene accettata upstream, inviala a
android-mainlinecon un riferimento all'invio upstream o una spiegazione del motivo per cui non è stata inviata a LKML.
Dopo che una patch viene accettata upstream o in android-mainline, può essere eseguito il backport all'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 le nuove release candidate upstream e garantisce che la patch sia nell'ACK basato su LTS successivo. Le eccezioni includono i casi in cui viene eseguito il backport di una patch upstream a android12-5.4 (perché è probabile che la patch sia già 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 selezionate da 'android-mainline` vengano accettate in ACK se esiste un caso d'uso ragionevole.BACKPORT:- È probabile che vengano accettate anche le patch upstream che non vengono selezionate in modo pulito e che richiedono modifiche, se esiste un caso d'uso ragionevole.FROMGIT:- Le patch selezionate da un ramo di manutenzione in preparazione per l'invio a Linux mainline potrebbero essere accettate se è prevista una scadenza imminente. Queste devono essere giustificate sia per i contenuti sia per la pianificazione.FROMLIST:- È improbabile che le patch inviate a LKML ma non ancora accettate in un ramo di manutenzione vengano accettate, a meno che la giustificazione non sia sufficientemente convincente da far accettare la patch indipendentemente dal fatto che venga inserita o meno in Linux upstream (supponiamo che non lo sarà). Deve essere associato un problema alle patchFROMLISTper facilitare la discussione con il team del kernel Android.
Patch specifiche di Android
Se non riesci a inserire le modifiche richieste upstream, puoi provare a inviare direttamente le patch out-of-tree ad ACK. L'invio di patch out-of-tree richiede la creazione di un problema in IT che citi la patch e la motivazione per cui la patch non può essere inviata upstream (vedi l'elenco precedente per esempi).
Tuttavia, esistono alcuni casi in cui il codice non può essere inviato upstream. Questi
casi sono trattati come segue e devono seguire le linee guida
per i contributi
per le patch specifiche di Android e devono essere contrassegnati con il prefetto ANDROID: nell'
oggetto.
Modifiche a gki_defconfig
Tutte le modifiche CONFIG a gki_defconfig devono essere applicate sia alle versioni arm64 sia a quelle x86, a meno che CONFIG non sia specifico dell'architettura. Per richiedere una modifica a un'impostazione CONFIG, crea un problema in IT per discuterne. Qualsiasi modifica CONFIG che influisce sull'interfaccia del modulo del kernel (KMI) dopo il blocco viene rifiutata. Nei casi in cui i partner richiedono impostazioni in conflitto per una singola configurazione, risolviamo i conflitti tramite la discussione sui bug correlati.
Codice che non esiste upstream
Le modifiche al codice già specifico di Android non possono essere inviate upstream. Ad esempio, anche se il driver binder viene gestito upstream, le modifiche alle funzionalità di ereditarietà della priorità del driver binder non possono essere inviate upstream perché sono specifiche di Android. Indica chiaramente nel bug e nella patch il motivo per cui il codice non può essere inviato upstream. Se possibile, dividi le patch in parti che possono essere inviate upstream e parti specifiche di Android che non possono essere inviate 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 build o alla configurazione o ad altri script che non esistono upstream.
Moduli out-of-tree
Linux upstream scoraggia attivamente il supporto per la creazione di moduli out-of-tree. Si tratta di una posizione ragionevole, dato che i manutentori di Linux non forniscono garanzie sulla compatibilità binaria o delle origini in-kernel e non vogliono supportare codice che non si trova nell'albero. Tuttavia, GKI fornisce garanzie ABI per i moduli 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 fornitore che sono accettabili per ACK, ma non per upstream.
Ad esempio, considera una patch che aggiunge macro EXPORT_SYMBOL_GPL() in cui i moduli che utilizzano l'esportazione non si trovano nell'albero delle origini. Anche se devi provare a richiedere EXPORT_SYMBOL_GPL() upstream e fornire un modulo che utilizza il simbolo appena esportato, se esiste una giustificazione valida per cui il modulo non viene inviato upstream, puoi inviare la patch ad ACK. Devi includere la giustificazione del motivo per cui il modulo non può essere inviato upstream nel problema. (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 consentire la creazione di moduli out-of-tree, GKI include un meccanismo per attivare 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, che è 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.
Governor caricabili
Per i framework del kernel (ad esempio, cpufreq) che supportano i governor caricabili, puoi sostituire il governor predefinito (ad esempio, il governor schedutil di cpufreq). Per
i framework (ad esempio, il framework termico) che non supportano i governor
o i driver caricabili, ma richiedono comunque un'implementazione specifica del fornitore, crea un problema
in IT e consulta il team del kernel Android.
Collaboreremo con te e con i manutentori upstream per aggiungere il supporto necessario.
Hook del fornitore
Nelle release precedenti, potevi 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 verrà accettato nei kernel principali upstream o in ACK. Per abilitare le funzionalità a valore aggiunto su cui si basano i partner con un impatto minimo sul codice del kernel principale, GKI accetta gli 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 disponibili per archiviare dati specifici del fornitore per implementare queste funzionalità.
Gli hook del fornitore sono disponibili in due varianti (normale e con limitazioni) basate su tracepoint (non eventi di traccia) a cui i moduli fornitore possono essere collegati. Ad esempio, anziché 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 fornitore può essere collegato 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_namedovenameè l'identificatore univoco della traccia. Per convenzione, i nomi degli hook del fornitore normali iniziano conandroid_vh, quindi il nome dell'hooksched_exit()sarebbeandroid_vh_sched_exit. - Gli hook del fornitore con limitazioni sono necessari in 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 limitazioni non possono essere scollegati, quindi i moduli collegati a un hook con limitazioni non possono mai essere scaricati. I nomi degli hook del fornitore con limitazioni iniziano con
android_rvh.
Per aggiungere un hook del fornitore, crea un problema in IT e invia le patch (come per tutte le patch specifiche di Android, deve esistere un problema e devi fornire una giustificazione). Il supporto per gli hook del fornitore è disponibile solo in ACK, quindi non inviare queste patch a Linux upstream.
Aggiungere campi del fornitore alle strutture
Puoi associare i dati del fornitore alle strutture di dati chiave aggiungendo i campi android_vendor_data utilizzando le macro ANDROID_VENDOR_DATA(). Ad esempio, per supportare le funzionalità a valore aggiunto, aggiungi i campi alle strutture come mostrato nel seguente esempio di codice.
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 gli 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 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 è collegato qualcosa. Tuttavia, se un modulo fornitore registra un gestore utilizzando register_trace_android_vh_sched_exit(), viene chiamata la funzione registrata. Il gestore deve essere a conoscenza del contesto in relazione ai blocchi mantenuti, allo stato RCS e ad 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 un'istanza delle 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 seguente codice 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 di dati utilizzate nella dichiarazione dell'hook devono essere
completamente definite per garantire la stabilità ABI. In caso contrario, non è sicuro dereferenziare i puntatori opachi o utilizzare la struct in contesti dimensionati. L'include che fornisce la definizione completa di queste strutture di dati deve essere inserito nella 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 dei tipi per evitare modifiche CRC che interrompono KMI. Dichiara invece i tipi in anticipo.
Collegarsi agli hook del fornitore
Per utilizzare gli hook del fornitore, il modulo fornitore deve registrare un gestore per l'hook (in genere durante l'inizializzazione del modulo). Ad esempio, il seguente codice 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);
...
}
Utilizzare gli hook del fornitore dai file di intestazione
Per utilizzare gli hook del fornitore dai file di intestazione, potrebbe essere necessario aggiornare il file di intestazione dell'hook del fornitore per annullare la definizione di 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 build, 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
annullare la definizione di TRACE_INCLUDE_PATH dopo la creazione dei punti di traccia.
Funzionalità del kernel principale
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 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 upstream, a meno che non riguardino interfacce specifiche di Android. Utilizza i file di intestazione specifici del fornitore per definire le interfacce tra i moduli fornitore e il codice userspace del fornitore.
- Nodi sysfs. Non aggiungere nuovi nodi sysfs al kernel GKI (queste aggiunte sono valide solo nei moduli fornitore). I nodi sysfs utilizzati dalle librerie e dal codice Java indipendenti da SoC e dispositivo che compongono il framework Android possono essere modificati solo in modo compatibile e devono essere modificati upstream se non sono nodi sysfs specifici di Android. Puoi creare nodi sysfs specifici del fornitore da utilizzare da userspace del fornitore. Per impostazione predefinita, l'accesso ai nodi sysfs da userspace viene negato utilizzando SELinux. È responsabilità del fornitore aggiungere le etichette SELinux appropriate per consentire l'accesso al software del fornitore autorizzato.
- Nodi DebugFS. I moduli fornitore possono definire i nodi in
debugfssolo per il debug (poichédebugfsnon è montato durante il normale funzionamento del dispositivo).