La imagen genérica del kernel (GKI) reduce la fragmentación del kernel alineándose estrechamente con el kernel de Linux ascendente. Sin embargo, existen motivos válidos por los que no se pueden aceptar algunos parches ascendentes, y hay cronogramas de productos que se deben cumplir, por lo que algunos parches se mantienen en las fuentes del kernel común de Android (ACK) desde las que se compila la GKI.
Los desarrolladores deben enviar los cambios de código ascendentes con la lista de distribución del kernel de Linux (LKML) como primera opción y enviar los cambios de código a la rama android-mainline del ACK solo cuando haya un motivo sólido por el que la versión ascendente no sea viable. A continuación, se muestran ejemplos de motivos válidos y cómo controlarlos.
El parche se envió a LKML, pero no se aceptó a tiempo para el lanzamiento de un producto. Para controlar este parche, haz lo siguiente:
- Proporciona pruebas de que el parche se envió a LKML y los comentarios recibidos para el parche, o una hora estimada en la que se enviará el parche ascendente.
- Decide un curso de acción para colocar el parche en ACK, obtener su aprobación ascendente y, luego, quitarlo de ACK cuando la versión ascendente final se combine en ACK.
El parche define
EXPORT_SYMBOLS_GPL()para un módulo del proveedor, pero no se pudo enviar de forma ascendente porque no hay módulos en el árbol que consuman ese símbolo. Para controlar este parche, proporciona detalles sobre por qué no se puede enviar tu módulo de forma ascendente y las alternativas que consideraste antes de realizar esta solicitud.El parche no es lo suficientemente genérico para la versión ascendente y no hay tiempo para refactorizarlo antes del lanzamiento de un producto. Para controlar este parche, proporciona una hora estimada en la que se enviará un parche refactorizado de forma ascendente (el parche no se aceptará en ACK sin un plan para enviar un parche refactorizado de forma ascendente para su revisión).
La versión ascendente no puede aceptar el parche porque… <insert reason here>. Para controlar este parche, comunícate con el equipo del kernel de Android y trabaja con nosotros en las opciones para refactorizar el parche de modo que se pueda enviar para su revisión y aceptación ascendente.
Hay muchas más justificaciones posibles. Cuando envíes tu error o tu parche, incluye una justificación válida y espera algunas iteraciones y debates. Reconocemos que el ACK incluye algunos parches, en especial en las primeras fases de la GKI, mientras todos aprenden a trabajar de forma ascendente, pero no pueden relajar los cronogramas de los productos para hacerlo. Espera que los requisitos ascendentes se vuelvan más estrictos con el tiempo.
Requisitos de los parches
Los parches deben cumplir con los estándares de codificación del kernel de Linux que se describen en el
árbol de origen de Linux,
ya sea que se envíen de forma ascendente o a ACK. La secuencia de comandos scripts/checkpatch.pl se ejecuta como parte de las pruebas previas a la confirmación de Gerrit, por lo que debes ejecutarla con anticipación para asegurarte de que pase. Para ejecutar la secuencia de comandos checkpatch con la misma configuración que las pruebas previas a la confirmación, usa //build/kernel/static_analysis:checkpatch_presubmit.
Para obtener más detalles, consulta
build/kernel/kleaf/docs/checkpatch.md.
Parches de ACK
Los parches enviados a ACK deben cumplir con los estándares de codificación del kernel de Linux y
los lineamientos de contribución.
Debes incluir una Change-Id
etiqueta en el mensaje de confirmación. Si envías el parche a varias ramas (por
ejemplo, android-mainline y android12-5.4), debes usar el mismo
Change-Id para todas las instancias del parche.
Primero, envía los parches a LKML para una revisión ascendente. Si el parche es:
- Se acepta de forma ascendente y se combina automáticamente en
android-mainline. - No se acepta de forma ascendente, envíalo a
android-mainlinecon una referencia al envío ascendente o una explicación de por qué no se envió a LKML.
Después de que se acepta un parche de forma ascendente o en android-mainline, se puede transferir a la versión anterior del ACK basada en LTS adecuada (como android12-5.4 y android11-5.4 para los parches que corrigen el código específico de Android). El envío a android-mainline permite realizar pruebas con los nuevos candidatos a lanzamiento ascendentes y garantiza que el parche esté en el próximo ACK basado en LTS. Entre las excepciones, se incluyen los casos en los que un parche ascendente se transfiere a la versión anterior de android12-5.4 (porque es probable que el parche ya esté en android-mainline).
Parches ascendentes
Como se especifica en los lineamientos de contribución, los parches ascendentes destinados a los kernels de ACK se dividen en los siguientes grupos (ordenados según la probabilidad de que se acepten).
UPSTREAM:- Es probable que los parches seleccionados de "android-mainline" se acepten en ACK si hay un caso de uso razonable.BACKPORT:- Es probable que también se acepten los parches de la versión ascendente que no se seleccionan de forma limpia y que necesitan modificaciones si hay un caso de uso razonable.FROMGIT:- Es posible que se acepten los parches seleccionados de una rama de mantenedor en preparación para el envío a la línea principal de Linux si hay una fecha límite próxima. Estos deben justificarse tanto por el contenido como por el cronograma.FROMLIST:- Es poco probable que se acepten los parches que se enviaron a LKML, pero que aún no se aceptaron en una rama de mantenedor, a menos que la justificación sea lo suficientemente convincente como para que se acepte el parche, ya sea que llegue o no a la versión ascendente de Linux (suponemos que no lo hará). Debe haber un problema asociado con los parchesFROMLISTpara facilitar el debate con el equipo del kernel de Android.
Parches específicos de Android
Si no puedes colocar los cambios necesarios de forma ascendente, puedes intentar enviar parches externos directamente a ACK. Para enviar parches externos, debes crear un problema en el IT que cite el parche y el motivo por el que no se puede enviar de forma ascendente (consulta la lista anterior para ver ejemplos).
Sin embargo, hay algunos casos en los que el código no se puede enviar de forma ascendente. Estos
casos se tratan de la siguiente manera y deben seguir los lineamientos
de contribución
para los parches específicos de Android y etiquetarse con el prefijo ANDROID: en el
asunto.
Cambios en gki_defconfig
Todos los cambios de CONFIG en gki_defconfig se deben aplicar a las versiones arm64 y x86, a menos que CONFIG sea específico de la arquitectura. Para solicitar un cambio en la configuración de CONFIG, crea un problema en el IT para analizar el cambio. Se rechaza cualquier cambio de CONFIG que afecte a la interfaz del módulo del kernel (KMI) después de que se congela. En los casos en que los socios solicitan parámetros de configuración en conflicto para una sola configuración, resolvemos los conflictos a través de un debate sobre los errores relacionados.
Código que no existe de forma ascendente
Las modificaciones al código que ya es específico de Android no se pueden enviar de forma ascendente. Por ejemplo, aunque el controlador de Binder se mantiene de forma ascendente, las modificaciones en las funciones de herencia de prioridad del controlador de Binder no se pueden enviar de forma ascendente porque son específicas de Android. Sé explícito en tu error y parche sobre por qué no se puede enviar el código de forma ascendente. Si es posible, divide los parches en partes que se puedan enviar de forma ascendente y partes específicas de Android que no se puedan enviar de forma ascendente para minimizar la cantidad de código externo que se mantiene en ACK.
Otros cambios en esta categoría son las actualizaciones de los archivos de representación de KMI, las listas de símbolos de KMI, gki_defconfig, las secuencias de comandos de compilación o la configuración, o bien otras secuencias de comandos que no existen de forma ascendente.
Módulos externos
La versión ascendente de Linux desaconseja activamente la compatibilidad con la compilación de módulos externos. Esta es una posición razonable, ya que los mantenedores de Linux no garantizan la compatibilidad binaria o de origen en el kernel y no quieren admitir código que no esté en el árbol. Sin embargo, la GKI sí garantiza la ABI para los módulos del proveedor, lo que garantiza que las interfaces de KMI sean estables durante la vida útil admitida de un kernel. Por lo tanto, hay una clase de cambios para admitir módulos del proveedor que son aceptables para ACK, pero no para la versión ascendente.
Por ejemplo, considera un parche que agrega macros EXPORT_SYMBOL_GPL() en los que los módulos que usan la exportación no están en el árbol de origen. Si bien debes intentar solicitar EXPORT_SYMBOL_GPL() de forma ascendente y proporcionar un módulo que use el símbolo recién exportado, si hay una justificación válida por la que no se envía el módulo de forma ascendente, puedes enviar el parche a ACK. Debes incluir la justificación de por qué no se puede enviar el módulo de forma ascendente en el problema. (No solicites la variante que no es GPL, EXPORT_SYMBOL().)
Configuraciones ocultas
Algunos módulos en el árbol seleccionan automáticamente configuraciones ocultas que no se pueden especificar en gki_defconfig. Por ejemplo, CONFIG_SND_SOC_TOPOLOGY se selecciona automáticamente cuando se configura CONFIG_SND_SOC_SOF=y. Para adaptarse a la compilación de módulos externos, la GKI incluye un mecanismo para habilitar configuraciones ocultas.
Para habilitar una configuración oculta, agrega una instrucción select en init/Kconfig.gki para que se seleccione automáticamente según la configuración del kernel CONFIG_GKI_HACKS_TO_FIX, que está habilitada en gki_defconfig. Usa este mecanismo solo para configuraciones ocultas. Si la configuración no está oculta, se debe especificar en gki_defconfig de forma explícita o como una dependencia.
Gobernadores cargables
Para los frameworks del kernel (como cpufreq) que admiten gobernadores cargables, puedes anular el gobernador predeterminado (como el gobernador schedutil de cpufreq). Para los
frameworks (como el framework térmico) que no admiten gobernadores
ni controladores cargables, pero que aún requieren una implementación específica del proveedor, crea un problema
en el IT y consulta con el equipo del kernel de Android.
Trabajaremos contigo y con los mantenedores ascendentes para agregar la compatibilidad necesaria.
Hooks del proveedor
En versiones anteriores, podías agregar modificaciones específicas del proveedor directamente al kernel principal. Esto no es posible con la GKI 2.0 porque el código específico del producto se debe implementar en módulos y no se aceptará en los kernels principales ascendentes ni en ACK. Para habilitar funciones de valor agregado en las que confían los socios con un impacto mínimo en el código del kernel principal, la GKI acepta hooks del proveedor que permiten invocar módulos desde el código del kernel principal. Además, las estructuras de datos clave se pueden rellenar con campos de datos del proveedor que están disponibles para almacenar datos específicos del proveedor para implementar estas funciones.
Los hooks del proveedor vienen en dos variantes (normal y restringida) que se basan en puntos de seguimiento (no eventos de seguimiento) a los que se pueden adjuntar los módulos del proveedor. Por ejemplo, en lugar de agregar una nueva función sched_exit() para realizar una contabilidad al salir de la tarea, los proveedores pueden agregar un hook en do_exit() al que se pueda adjuntar un módulo del proveedor para el procesamiento. Una implementación de ejemplo incluye los siguientes hooks del proveedor.
- Los hooks del proveedor normales usan
DECLARE_HOOK()para crear una función de punto de seguimiento con el nombretrace_nameen la quenamees el identificador único del seguimiento. Por convención, los nombres de los hooks del proveedor normales comienzan conandroid_vh, por lo que el nombre del hooksched_exit()seríaandroid_vh_sched_exit. - Se necesitan hooks del proveedor restringidos para casos como los hooks del programador en los que se debe llamar a la función adjunta, incluso si la CPU está sin conexión o requiere un contexto no atómico. Los hooks del proveedor restringidos no se pueden separar, por lo que los módulos que se adjuntan a un hook restringido nunca se pueden descargar. Los nombres de los hooks del proveedor restringidos comienzan con
android_rvh.
Para agregar un hook del proveedor, envía un problema en el IT y envía parches (como con todos los parches específicos de Android, debe existir un problema y debes proporcionar una justificación). La compatibilidad con los hooks del proveedor solo está en ACK, por lo que no envíes estos parches a la versión ascendente de Linux.
Agrega campos del proveedor a las estructuras
Puedes asociar datos del proveedor con estructuras de datos clave agregando campos android_vendor_data con las macros ANDROID_VENDOR_DATA(). Por ejemplo, para admitir funciones de valor agregado, agrega campos a las estructuras como se muestra en la siguiente muestra de código.
Para evitar posibles conflictos entre los campos que necesitan los proveedores y los que necesitan los OEMs, los OEMs nunca deben usar campos declarados con macros ANDROID_VENDOR_DATA(). En su lugar, los OEMs deben usar ANDROID_OEM_DATA() para declarar campos 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 */
}
Define los hooks del proveedor
Agrega hooks del proveedor al código del kernel como puntos de seguimiento declarándolos con DECLARE_HOOK() o DECLARE_RESTRICTED_HOOK() y, luego, agregándolos al código como un punto de seguimiento. Por ejemplo, para agregar trace_android_vh_sched_exit() a la función del kernel do_exit() existente, haz lo siguiente:
#include <trace/hooks/exit.h>
void do_exit(long code)
{
struct task_struct *tsk = current;
...
trace_android_vh_sched_exit(tsk);
...
}
La función trace_android_vh_sched_exit() inicialmente solo verifica si hay algo adjunto. Sin embargo, si un módulo del proveedor registra un controlador con register_trace_android_vh_sched_exit(), se llama a la función registrada. El controlador debe conocer el contexto con respecto a los bloqueos retenidos, el estado de RCS y otros factores. El hook se debe definir en un archivo de encabezado en el directorio include/trace/hooks.
Por ejemplo, el siguiente código proporciona una posible declaración para trace_android_vh_sched_exit() en el archivo 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>
Para crear instancias de las interfaces necesarias para el hook del proveedor, agrega el archivo de encabezado con la declaración del hook a drivers/android/vendor_hooks.c y exporta los símbolos. Por ejemplo, el siguiente código completa la declaración del 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: Las estructuras de datos que se usan en la declaración del hook deben definirse
por completo para garantizar la estabilidad de la ABI. De lo contrario, no es seguro anular la referencia de los punteros opacos ni usar la estructura en contextos dimensionados. La inclusión que proporciona la definición completa de esas estructuras de datos debe ir dentro de la sección #ifndef __GENKSYMS__ de drivers/android/vendor_hooks.c. Los archivos de encabezado en include/trace/hooks no deben incluir el archivo de encabezado del kernel con las definiciones de tipo para evitar cambios de CRC que interrumpan la KMI. En su lugar, declara los tipos de forma directa.
Adjunta a los hooks del proveedor
Para usar hooks del proveedor, el módulo del proveedor debe registrar un controlador para el hook (por lo general, se hace durante la inicialización del módulo). Por ejemplo, el siguiente código muestra el controlador del módulo foo.ko para 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);
...
}
Usa hooks del proveedor desde archivos de encabezado
Para usar hooks del proveedor desde archivos de encabezado, es posible que debas actualizar el archivo de encabezado del hook del proveedor para anular la definición de TRACE_INCLUDE_PATH y evitar errores de compilación que indiquen que no se pudo encontrar un archivo de encabezado de punto de seguimiento. Por ejemplo:
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.
Para corregir este tipo de error de compilación, aplica la corrección equivalente al archivo de encabezado del hook del proveedor que incluyes. Para obtener más información, 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 definición de UNDEF_TRACE_INCLUDE_PATH le indica a include/trace/define_trace.h que
anule la definición de TRACE_INCLUDE_PATH después de crear los puntos de seguimiento.
Funciones principales del kernel
Si ninguna de las técnicas anteriores te permite implementar una función desde un módulo, debes agregar la función como una modificación específica de Android al kernel principal. Crea un problema en el servicio de seguimiento de problemas (IT) para iniciar la conversación.
Interfaz de programación de aplicaciones del usuario (UAPI)
- Archivos de encabezado de UAPI. Los cambios en los archivos de encabezado de UAPI deben ocurrir de forma ascendente, a menos que los cambios sean en interfaces específicas de Android. Usa archivos de encabezado específicos del proveedor para definir interfaces entre los módulos del proveedor y el código del espacio de usuario del proveedor.
- Nodos sysfs. No agregues nodos sysfs nuevos al kernel de GKI (esas adiciones solo son válidas en los módulos del proveedor). Los nodos sysfs que usan las bibliotecas y el código Java independientes del SoC y del dispositivo que componen el framework de Android solo se pueden cambiar de maneras compatibles y se deben cambiar de forma ascendente si no son nodos sysfs específicos de Android. Puedes crear nodos sysfs específicos del proveedor para que los use el espacio de usuario del proveedor. De forma predeterminada, se deniega el acceso a los nodos sysfs por parte del espacio de usuario con SELinux. Depende del proveedor agregar las etiquetas SELinux adecuadas para permitir el acceso al software del proveedor autorizado.
- Nodos DebugFS. Los módulos del proveedor pueden definir nodos en
debugfssolo para la depuración (ya quedebugfsno se activa durante el funcionamiento normal del dispositivo).