Cómo desarrollar código de kernel para GKI

La imagen genérica del kernel (GKI) reduce la fragmentación del kernel a través de una estrecha alineación con el kernel de Linux ascendente. Sin embargo, hay razones válidas por las que algunos parches no se pueden aceptar en sentido ascendente, y hay programaciones de productos que para que algunos parches se mantengan en el kernel común (ACK) de Android. fuentes de las que se compiló el GKI.

Los desarrolladores deben enviar los cambios de código ascendentes usando el Kernel Mailing de Linux List (LKML) como primera opción y enviar cambios de código al ACK La rama android-mainline solo cuando existe un motivo sólido por el que el upstream no está viables. A continuación, se incluyen ejemplos de motivos válidos y cómo manejarlos.

  • El parche se envió a LKML, pero no se aceptó a tiempo para un producto. lanzamiento. Para controlar este parche, haz lo siguiente:

    • Proporciona pruebas de que el parche se envió a la LKML y comentarios recibidos para el parche o una hora estimada a la que enviados de manera ascendente.
    • Decide el procedimiento para implementar el parche en ACK y obtener su aprobación. ascendente y, luego, quitarlo de ACK cuando la versión ascendente se combina en ACK.
  • El parche define EXPORT_SYMBOLS_GPL() para un módulo de proveedor, pero no se pudo se enviarán de manera ascendente porque no hay módulos en el árbol que consuman ese . Para administrar este parche, proporciona detalles sobre por qué no se puede usar tu módulo que se enviaron en sentido upstream y las alternativas que consideraste para cada solicitud.

  • El parche no es lo suficientemente genérico para las versiones upstream y no hay tiempo y refactorizarlo antes del lanzamiento de un producto. Para controlar este parche, proporciona un tiempo estimado en el que se envía un parche refactorizado en sentido ascendente (el parche no se aceptará en ACK sin un plan para enviar un mensaje un parche ascendente para su revisión).

  • En sentido ascendente, no se puede aceptar el parche porque... <insertar motivo aquí>. Para administrar este parche, comunícate con el equipo de kernel de Android y trabajaremos con nosotros en opciones para refactorizar el parche a fin de que pueda enviarse para su revisión y aceptación ascendente.

Existen muchas otras justificaciones posibles. Cuando envías un informe de errores o tu incluye una justificación válida y espera algo de iteración y debate. Reconocemos que el ACK tiene algunos parches, especialmente en las primeras fases de GKI mientras todo el mundo está aprendiendo a trabajar de forma ascendente, pero no se puede relajar cronogramas de productos para hacerlo. Los requisitos ascendentes serán cada vez más 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 descritos en el Árbol de fuentes de Linux, ya sea que se envíen ascendentes o a ACK. El scripts/checkpatch.pl Este comando se ejecuta como parte de las pruebas de envío previo de Gerrit, así que ejecútalo con anticipación para asegurarte de que para asegurarse de que se apruebe. Para ejecutar la secuencia de comandos checkpatch con la misma configuración que la prueba previa al envío, usa //build/kernel/static_analysis:checkpatch_presubmit. Para obtener más información, consulta build/kernel/kleaf/docs/checkpatch.md

Parches 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 un Change-Id etiqueta en el mensaje de confirmación; si envías el parche a varias ramas (para ejemplo, android-mainline y android12-5.4), debes usar el mismo Change-Id para todas las instancias del parche.

Primero, envía parches al LKML para una revisión upstream. A continuación, se describen los estados de los parches:

  • Aceptado en sentido upstream, se combina automáticamente en android-mainline.
  • No se acepta el upstream, envíalo a android-mainline con un referencia al envío upstream o una explicación de por qué no fue enviados a LKML.

Después de que se acepta un parche, ya sea de manera ascendente o en android-mainline, se puede portabilidad a versiones anteriores al ACK adecuado basado en LTS (como android12-5.4 y android11-5.4 para los parches que corrigen el código específico de Android). Envío a android-mainline permite realizar pruebas con nuevas versiones candidatas a versiones ascendentes, y garantiza que el parche esté en el próximo ACK basado en LTS. Las excepciones incluyen los casos en la que se brinda portabilidad a versiones anteriores de un parche upstream a android12-5.4 (porque el parche probablemente ya estén en android-mainline).

Parches ascendentes

Como se especifica en la contribución lineamientos, los parches upstream destinados a kernels ACK se encuentran en los siguientes grupos (enumerados en orden de probabilidad de ser aceptado).

  • UPSTREAM:: Es probable que se apliquen los parches seleccionados de "android-mainline" acepta en ACK si existe un caso de uso razonable.
  • BACKPORT:: Parches del flujo ascendente que no se seleccionan de forma correcta y necesitan también es probable que se acepten las modificaciones para determinar si este es el caso.
  • FROMGIT:: Parches seleccionados especialmente de una rama de encargado de mantenimiento en preparación para enviar a la línea principal de Linux si hay una próxima fecha límite. Estas condiciones deben justificarse tanto en el contenido como en el cronograma.
  • FROMLIST:: Parches que se enviaron a LKML, pero no se enviaron pero es poco probable que lo hagan, a menos que La justificación es lo suficientemente convincente para que se acepte el parche. si llega o no a Linux ascendente (suponemos que no lo hará). Hay Debe ser un problema asociado con los parches de FROMLIST para facilitar el debate. con el equipo de kernel de Android.

Parches específicos de Android

Si no puedes conseguir los cambios necesarios en sentido ascendente, intenta enviar de parches fuera del árbol para confirmar directamente los cambios. Enviar parches fuera del árbol requiere que crees un problema en TI que cite el parche y la justificación el parche no se puede enviar de manera 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 abarcan de la siguiente manera y deben seguir la contribución lineamientos para parches específicos de Android y se etiqueta con el prefijo ANDROID: en el sujeto.

Cambios en gki_defconfig

Todos los cambios de CONFIG en gki_defconfig se deben aplicar a arm64 y Versiones x86, a menos que CONFIG sea específico de una arquitectura Cómo solicitar un cambio a un parámetro de configuración CONFIG, crea un problema en el equipo de TI para analizar el cambio. Cualquiera Cambio de CONFIG que afecta a la interfaz de módulo de kernel (KMI) después de congelado se rechaza. En los casos en que los socios soliciten un conflicto para una sola configuración, resuelvo los conflictos mediante un debate sobre los errores relacionados.

Código que no existe en sentido ascendente

Las modificaciones en un 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 en sentido ascendente, las modificaciones Las funciones de herencia prioritaria del controlador de Binder no se pueden enviar de manera ascendente. porque son específicos de Android. Sé explícito en el error y parchea por qué el código no se puede enviar de forma ascendente. De ser posible, divide los parches en partes Se pueden enviar partes específicas de Android y upstream que no se pueden enviar. para minimizar la cantidad de código fuera del árbol que se mantiene en ACK.

Otros cambios en esta categoría son las actualizaciones de los archivos de representación de KMI, listas de símbolos, gki_defconfig, secuencias de comandos de compilación o configuración, y otras secuencias de comandos que no existen en sentido ascendente.

Módulos fuera del árbol

Upstream Linux desaconseja activamente la compatibilidad con la compilación de módulos fuera de árbol. Esta es una posición razonable, ya que los encargados de mantenimiento de Linux no garantizan acerca de la compatibilidad con objetos binarios o fuentes del kernel y no queremos admitir código que no está en el árbol. Sin embargo, GKI garantiza las ABI módulos de proveedores, lo que garantiza que las interfaces de KMI sean estables para las la vida útil de un kernel. Por lo tanto, hay una clase de cambios para admitir módulos que son aceptables para ACK, pero no para ascendentes.

Por ejemplo, considera un parche que agregue macros EXPORT_SYMBOL_GPL(), en el que el Los módulos que usan la exportación no están en el árbol de fuentes. Aunque debes intentar solicitar EXPORT_SYMBOL_GPL() ascendente y proporcionar un módulo que use el exportado recientemente, si hay una justificación válida que indique por qué el módulo no se envía de forma ascendente, puedes enviar el parche a ACK en su lugar. Tú deberás incluir la justificación de por qué el módulo no puede subirse en el problema. (no solicites la variante que no es de GPL, EXPORT_SYMBOL()).

Parámetros de configuración ocultos

Algunos módulos de árbol seleccionan automáticamente las configuraciones ocultas que no se pueden especificar en gki_defconfig. Por ejemplo, se seleccionó CONFIG_SND_SOC_TOPOLOGY automáticamente cuando se configura CONFIG_SND_SOC_SOF=y. Para adaptarse a en la compilación de módulos fuera de árbol, GKI incluye un mecanismo para habilitar configuraciones ocultas.

Para habilitar una configuración oculta, agrega una sentencia select en init/Kconfig.gki para que se selecciona automáticamente en función de la configuración del kernel CONFIG_GKI_HACKS_TO_FIX que está habilitado 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.

Reguladores cargables

Para los frameworks de kernel (como cpufreq) que admiten reguladores que se pueden cargar, puedes puede anular el regulador predeterminado (como el regulador schedutil de cpufreq. Para de seguridad (como el térmico) que no admiten controladores cargables o controladores, pero aun así requieren una implementación específica del proveedor, crea un problema al equipo de TI y consulta al equipo de kernels de Android.

Trabajaremos contigo y con los encargados de mantenimiento upstream para agregar la asistencia necesaria.

Hooks del proveedor

En versiones anteriores, podías agregar modificaciones específicas del proveedor directamente en la kernel principal. Esto no es posible con GKI 2.0 porque el código específico del producto debe implementarse en módulos y no se aceptarán en los kernels principales ascendentes o en ACK. Habilitar funciones con valor agregado en las que confían los socios con un impacto mínimo en el código del kernel principal, GKI acepta hooks del proveedor que permiten que se invoquen módulos. a partir del código principal del kernel. Además, las estructuras de datos clave pueden rellenarse con campos de datos del proveedor que están disponibles para almacenar datos específicos del proveedor para implementar estas funciones.

Los hooks de proveedor se presentan en dos variantes (normal y restringida) que se basan en puntos de seguimiento (no eventos de seguimiento) a los que pueden adjuntarse los módulos del proveedor. Por ejemplo: en lugar de agregar una nueva función sched_exit() para hacer una contabilidad en una tarea los proveedores pueden agregar un gancho en do_exit() al que se puede adjuntar un módulo de proveedores para su procesamiento. Una implementación de ejemplo incluye los siguientes hooks del proveedor.

  • Los hooks normales del proveedor usan DECLARE_HOOK() para crear una función de punto de seguimiento con el nombre trace_name, donde name es el identificador único del seguimiento. Por convención, los nombres de hook del proveedor normales comienzan con android_vh, por lo que el nombre del hook sched_exit() sería android_vh_sched_exit.
  • Los hooks restringidos del proveedor son necesarios para casos como hooks de 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 restringidos del proveedor no se pueden separar, por lo que los módulos que se adjunten a un hook restringido nunca podrán descargarse. Restringida Los nombres de hook del proveedor comienzan con android_rvh.

Para agregar un hook del proveedor, informa un problema en TI y envía parches (como sucede con todos los parches específicos de Android, debe existir un problema y debes proporcionar justificación). La compatibilidad con hooks de proveedor solo es ACK, así que no los envíes. o parches para seguridad de Linux.

Cómo agregar campos de proveedor a las estructuras

Puedes asociar datos de proveedores con estructuras de datos clave agregando Campos android_vendor_data que usan las macros ANDROID_VENDOR_DATA(). Para Por ejemplo, para admitir atributos con 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 campos necesarios, los OEM nunca deben usar campos declarados con Macros de ANDROID_VENDOR_DATA(). En su lugar, los OEM 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 hooks del proveedor

Agregar hooks del proveedor al código del kernel como puntos de seguimiento declarándolos mediante DECLARE_HOOK() o DECLARE_RESTRICTED_HOOK() y, luego, agregarlos al código como un punto de seguimiento. Por ejemplo, para agregar trace_android_vh_sched_exit() al función de kernel do_exit() existente:

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

Inicialmente, la función trace_android_vh_sched_exit() solo verifica si hay algo está conectado. Sin embargo, si un módulo de proveedor registra un controlador usando register_trace_android_vh_sched_exit(), se llama a la función registrada. El el controlador debe conocer el contexto con respecto a los bloqueos retenidos, el estado de RCS y con otros factores. El hook debe definirse en un archivo de encabezado en el 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>

Agrega el archivo de encabezado para crear una instancia de las interfaces requeridas para el hook del proveedor. con la declaración de hook a drivers/android/vendor_hooks.c y exporta el 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 de hook deben completamente definida para garantizar la estabilidad de la ABI. De lo contrario, no es seguro deshagan las referencias de los punteros opacos o usen el struct en contextos de gran tamaño. La etiqueta de inclusión que brinda la definición completa de esas estructuras, Sección #ifndef __GENKSYMS__ de drivers/android/vendor_hooks.c. El encabezado Los archivos en include/trace/hooks no deben incluir el archivo de encabezado de kernel con el definiciones de tipo para evitar cambios de CRC que dañen el KMI En su lugar, hacia adelante declarar los tipos.

Adjuntar a hooks del proveedor

A fin de usar hooks de proveedor, el módulo de 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);
    ...
}

Usar hooks del proveedor de los archivos de encabezado

Para usar hooks de proveedor de archivos de encabezado, es posible que debas actualizar el hook de proveedor de encabezado para anular la definición de TRACE_INCLUDE_PATH y evitar errores de compilación que indiquen 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 hook del proveedor. archivo de encabezado 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

Definir UNDEF_TRACE_INCLUDE_PATH le indica a include/trace/define_trace.h que anula 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 modificación específica de Android al módulo kernel. Crea una entrada en la Herramienta de seguimiento de errores (TI) para iniciar la conversación.

Interfaz de programación de aplicaciones de usuario (UAPI)

  • Archivos de encabezado de UAPI. Cambios en Archivos de encabezado de UAPI deben producirse en sentido ascendente, a menos que los cambios se realicen en interfaces específicas de Android. Usa archivos de encabezado específicos del proveedor para definir interfaces entre los módulos y el código del espacio de usuario del proveedor.
  • nodos sysfs. No agregues nuevos nodos sysfs al kernel de GKI (estas adiciones solo son válidos en los módulos de proveedores). nodos sysfs utilizados por el SoC- Se pueden usar bibliotecas independientes del dispositivo y código Java que comprende el framework de Android. solo en formas compatibles y debe cambiarse en sentido ascendente si no son nodos sysfs específicos de Android. Puedes crear nodos sysfs específicos del proveedor que usará el espacio de usuario del proveedor. De forma predeterminada, se deniega el acceso a los nodos de sysfs por parte del espacio de usuario a través de SELinux. Depende de la que el proveedor agregue las etiquetas de SELinux adecuadas para permitir el acceso software del proveedor de servicios en la nube.
  • Nodos de DebugFS. Los módulos de proveedores pueden definir nodos en debugfs para solo para depuración (ya que debugfs no se activa durante el funcionamiento normal de la dispositivo).