Desarrollar código de kernel para GKI

La imagen genérica del kernel (GKI) reduce la fragmentación del kernel al alinearse estrechamente con el kernel anterior de Linux. Sin embargo, existen razones válidas por las que algunos parches no se pueden aceptar en sentido ascendente y hay programas de productos que se deben cumplir, por lo que algunos parches se mantienen en las fuentes de Android Common Kernel (ACK) a partir de las cuales se crea el GKI.

Los desarrolladores deben enviar los cambios de código en sentido ascendente utilizando la Lista de correo del kernel de Linux (LKML) como primera opción, y enviar los cambios de código a la rama android-mainline ACK solo cuando haya una razón sólida por la cual el nivel ascendente no es viable. A continuación se enumeran ejemplos de razones válidas y cómo manejarlas.

  • El parche se envió a LKML, pero no se aceptó a tiempo para el lanzamiento de un producto. Para manejar este parche:

    • Proporcione evidencia de que el parche se envió a LKML y los comentarios recibidos para el parche, o un tiempo estimado en el que el parche se enviará aguas arriba.
    • Decida un curso de acción para colocar el parche en ACK, obtener su aprobación en sentido ascendente y luego sacarlo de ACK cuando la versión ascendente final se fusione con ACK.
  • El parche define EXPORT_SYMBOLS_GPL() para un módulo de proveedor, pero no se pudo enviar en sentido ascendente porque no hay módulos en el árbol que consuman ese símbolo. Para manejar este parche, proporcione detalles sobre por qué su módulo no se puede enviar en sentido ascendente y las alternativas que consideró antes de realizar esta solicitud.

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

  • Upstream no puede aceptar el parche porque... <inserte el motivo aquí> . Para manejar este parche, comuníquese con el equipo del kernel de Android y trabaje con nosotros en las opciones para refactorizar el parche para que pueda enviarse para su revisión y aceptarse en sentido ascendente.

Hay muchas más justificaciones potenciales. Cuando envíe su error o su parche, incluya una justificación válida y espere alguna iteración y discusión. Reconocemos que el ACK llevará algunos parches, especialmente en las primeras fases de GKI, mientras todos están aprendiendo a trabajar en sentido ascendente pero no pueden relajar los programas de productos para hacerlo. Espere que los requisitos de upstreaming se vuelvan más estrictos con el tiempo.

Requisitos del parche

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 en sentido ascendente o a ACK. El scripts/checkpatch.pl se ejecuta como parte de las pruebas de preenvío de Gerrit, así que ejecútelo con anticipación para asegurarse de que pase. Para ejecutar el script checkpatch con la misma configuración que la prueba de envío previo, use build/static_analysis/checkpatch_presubmit.sh desde el pago del repo .

parches ACK

Los parches enviados a ACK deben cumplir con los estándares de codificación del kernel de Linux y las pautas de contribución . Debe incluir una etiqueta Change-Id en el mensaje de confirmación; si envía el parche a varias sucursales (por ejemplo, android-mainline y android12-5.4 ), debe usar el mismo Change-Id para todas las instancias del parche.

Envíe parches a LKML primero para una revisión previa. Si el parche es:

  • Aceptado en sentido ascendente, se fusiona automáticamente en android-mainline .
  • No aceptado en sentido ascendente, envíelo a android-mainline con una referencia al envío ascendente o una explicación de por qué no se envió a LKML.

Una vez que se acepta un parche, ya sea en sentido ascendente o en android-mainline , se puede transferir al ACK adecuado basado en LTS (como android12-5.4 y android11-5.4 para parches que corrigen el código específico de Android). El envío a android-mainline permite realizar pruebas con nuevos candidatos de versión ascendente y garantiza que el parche se encuentre en el próximo ACK basado en LTS. Las excepciones incluyen casos en los que un parche ascendente se retroalimenta a android12-5.4 (porque es probable que el parche ya esté en android-mainline ).

parches aguas arriba

Como se especifica en las pautas de contribución , los parches ascendentes destinados a los núcleos ACK se clasifican en los siguientes grupos (enumerados en orden de probabilidad de aceptación).

  • UPSTREAM: - Es probable que los parches seleccionados de 'android-mainline' se acepten en ACK si hay un caso de uso razonable.
  • BACKPORT: - También es probable que se acepten los parches de upstream que no se eligen de manera limpia y necesitan modificaciones si hay un caso de uso razonable.
  • FROMGIT: - Los parches seleccionados de una rama de mantenimiento en preparación para enviar a la línea principal de Linux podrían aceptarse si hay una fecha límite próxima. Estos deberán estar justificados tanto en contenido como en horario.
  • 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 mantenimiento, a menos que la justificación sea lo suficientemente convincente como para que el parche se acepte, ya sea que llegue o no a Linux upstream (suponemos que no lo hará). Debe haber un problema asociado con los parches de FROMLIST para facilitar la discusión con el equipo del kernel de Android.

Parches específicos de Android

Si no puede realizar los cambios necesarios aguas arriba, puede intentar enviar parches fuera del árbol a ACK directamente. El envío de parches fuera del árbol requiere que cree un problema en TI que cite el parche y la justificación de por qué el parche no se puede enviar aguas arriba (consulte la lista anterior para ver ejemplos). Sin embargo, hay algunos casos en los que el código no se puede enviar aguas arriba. Estos casos se tratan de la siguiente manera y deben seguir las pautas de contribución para parches específicos de Android y estar etiquetados con el prefijo ANDROID: en el asunto.

Cambios en gki_defconfig

Todos los cambios de CONFIG en gki_defconfig deben aplicarse a las versiones arm64 y x86, a menos que CONFIG sea específico de la arquitectura. Para solicitar un cambio en una configuración de CONFIG , cree un problema en TI para discutir el cambio. Se rechaza cualquier cambio de CONFIG que afecte a la interfaz del módulo del kernel (KMI) después de congelarla. En los casos en que los socios solicitan configuraciones en conflicto para una sola configuración, resolvemos los conflictos a través de la discusión sobre los errores relacionados.

Código que no existe aguas arriba

Las modificaciones al código que ya es específico de Android no se pueden enviar en sentido ascendente. Por ejemplo, aunque el controlador de enlace se mantiene en sentido ascendente, las modificaciones a las funciones de herencia de prioridad del controlador de enlace no pueden enviarse en sentido ascendente porque son específicas de Android. Sea explícito en su error y corrija por qué el código no se puede enviar aguas arriba. Si es posible, divida los parches en partes que se puedan enviar en sentido ascendente y en partes específicas de Android que no se puedan enviar en sentido ascendente 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, las listas de símbolos de KMI, gki_defconfig , scripts de compilación o configuración, u otros scripts que no existen en sentido ascendente.

Módulos fuera del árbol

Upstream Linux desaconseja activamente el soporte para construir módulos fuera del árbol. Esta es una posición razonable dado que los mantenedores de Linux no garantizan la compatibilidad binaria o de fuente en el kernel y no quieren admitir código que no está en el árbol. Sin embargo, GKI ofrece garantías ABI para los módulos de proveedores, lo que garantiza que las interfaces KMI sean estables durante la vida útil admitida de un kernel. Por lo tanto, hay una clase de cambios para admitir módulos de proveedores que son aceptables para ACK pero no son aceptables para upstream.

Por ejemplo, considere un parche que agrega macros EXPORT_SYMBOL_GPL() donde los módulos que usan la exportación no están en el árbol fuente. Si bien debe intentar solicitar EXPORT_SYMBOL_GPL() en sentido ascendente y proporcionar un módulo que use el símbolo recién exportado, si existe una justificación válida de por qué el módulo no se envía en sentido ascendente, puede enviar el parche a ACK en su lugar. Debe incluir la justificación de por qué el módulo no se puede actualizar en el problema. (No solicite la variante sin GPL, EXPORT_SYMBOL() .

Configuraciones ocultas

Algunos módulos en á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 creación de módulos fuera del árbol, GKI incluye un mecanismo para habilitar configuraciones ocultas.

Para habilitar una configuración oculta, agregue una declaración de select en init/Kconfig.gki para que se seleccione automáticamente en función de la configuración del kernel CONFIG_GKI_HACKS_TO_FIX , que está habilitada en gki_defconfig . Use este mecanismo solo para configuraciones ocultas; si la configuración no está oculta, debe especificarse en gki_defconfig ya sea explícitamente o como una dependencia.

Gobernadores cargables

Para marcos de kernel (como cpufreq ) que admiten gobernadores cargables, puede anular el gobernador predeterminado (como el gobernador cpufreq de schedutil ). implementación específica del proveedor, cree un problema en TI y consulte con el equipo del kernel de Android .

Trabajaremos con usted y los mantenedores de aguas arriba para agregar el soporte necesario.

ganchos de proveedores

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

Los ganchos de proveedores vienen en dos variantes (normal y restringida) que se basan en puntos de seguimiento (no en eventos de seguimiento) a los que se pueden adjuntar módulos de proveedores. Por ejemplo, en lugar de agregar una nueva función sched_exit() para realizar una contabilidad al final de la tarea, los proveedores pueden agregar un gancho en do_exit() al que se puede adjuntar un módulo de proveedor para su procesamiento. Una implementación de ejemplo incluye los siguientes enlaces de proveedores.

  • Los ganchos de proveedores normales usan DECLARE_HOOK() para crear una función de punto de rastreo con el nombre trace_ name donde name es el identificador único para el rastreo. Por convención, los nombres de enlaces de proveedores normales comienzan con android_vh , por lo que el nombre del sched_exit() sería android_vh_sched_exit .
  • Los ganchos de proveedores restringidos son necesarios para casos como los ganchos del programador en los que se debe llamar a la función adjunta incluso si la CPU está fuera de línea o requiere un contexto no atómico. Los ganchos de proveedores restringidos no se pueden desconectar, por lo que los módulos que se conectan a un gancho restringido nunca se pueden descargar. Solo se permite un adjunto, por lo que cualquier otro intento de adjuntar falla con -EBUSY . Los nombres de enlaces de proveedores restringidos comienzan con android_rvh .

Para agregar un enlace de proveedor, presente un problema en TI y envíe parches (como con todos los parches específicos de Android, debe existir un problema y debe proporcionar una justificación). El soporte para ganchos de proveedores solo está en ACK, así que no envíe estos parches a Linux ascendente.

Agregar campos de proveedor a las estructuras

Puede asociar datos de proveedores con estructuras de datos clave agregando campos android_vendor_data usando las macros ANDROID_VENDOR_DATA() . Por ejemplo, para admitir funciones de valor agregado, agregue campos a las estructuras como se muestra en el siguiente ejemplo de código.

Para evitar posibles conflictos entre los campos que necesitan los proveedores y los campos que necesitan los OEM, los OEM nunca deben usar campos declarados mediante macros ANDROID_VENDOR_DATA() . En su lugar, los OEM deben usar ANDROID_OEM_DATA() para declarar los 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 */
}

Definir ganchos de proveedores

Agregue ganchos de proveedores al código del kernel como puntos de rastreo declarándolos usando DECLARE_HOOK() o DECLARE_RESTRICTED_HOOK() y luego agregándolos al código como un punto de rastreo. Por ejemplo, para agregar trace_android_vh_sched_exit() a la función del núcleo do_exit() existente:

#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 comprueba si hay algo adjunto. Sin embargo, si un módulo de proveedor registra un controlador mediante 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 enlace debe definirse en un archivo de encabezado en el directorio include/trace/hooks .

Por ejemplo, el siguiente código proporciona una declaración posible para trace_android_vh_sched_exit() en el archivo include/trace/hooks/sched.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 */
#include <linux/sched.h>

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>

NOTA : Las estructuras de datos que se utilizan dentro de la declaración de enlace deben definirse completamente para garantizar la estabilidad de ABI. De lo contrario, no es seguro eliminar la referencia de los punteros opacos o usar la estructura en contextos de tamaño. #include <linux/sched.h> en el ejemplo anterior hace que la definición de struct task_struct esté disponible y habilita el seguimiento de ABI.

Para crear instancias de las interfaces requeridas para el enlace del proveedor, agregue el archivo de encabezado con la declaración del enlace a drivers/android/vendor_hooks.c y exporte los símbolos. Por ejemplo, el siguiente código completa la declaración del android_vh_sched_exit() .

#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);

Adjuntar a ganchos de proveedores

Para usar enlaces de proveedores, el módulo de proveedores debe registrar un controlador para el enlace (normalmente se hace durante la inicialización del módulo). Por ejemplo, el código siguiente 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, NULL);
    ...
}

Características principales del kernel

Si ninguna de las técnicas anteriores le permite implementar una función de un módulo, entonces debe agregar la función como modificación específica de Android al kernel central. Cree un problema en el rastreador de problemas (TI) para iniciar la conversación.

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

  • Archivos de cabecera UAPI. Los cambios en los archivos de encabezado de UAPI deben ocurrir en sentido ascendente a menos que los cambios sean en interfaces específicas de Android. Utilice 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 agregue nuevos nodos sysfs al kernel GKI (dichas adiciones son válidas solo en módulos de proveedores). Los nodos sysfs utilizados por las bibliotecas SoC e independientes del dispositivo y el código Java que comprende el marco de trabajo de Android solo se pueden cambiar de manera compatible y deben cambiarse en sentido ascendente si no son nodos sysfs específicos de Android. Puede 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 espacio de usuario mediante SELinux. Depende del proveedor agregar las etiquetas SELinux apropiadas para permitir el acceso por parte del software del proveedor autorizado.
  • Nodos DebugFS. Los módulos de proveedores pueden definir nodos en debugfs solo para depuración (ya que debugfs no se monta durante el funcionamiento normal del dispositivo).