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 de Linux ascendente. Sin embargo, existen razones válidas por las que algunos parches no se pueden aceptar en sentido ascendente y hay cronogramas de productos que deben cumplirse, por lo que algunos parches se mantienen en las fuentes del núcleo común de Android (ACK) a partir de las cuales se construye GKI.

Los desarrolladores deben enviar cambios de código ascendentes utilizando la Lista de correo del kernel de Linux (LKML) como primera opción, y enviar cambios de código a la rama ACK android-mainline solo cuando hay una razón sólida por la cual el 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 fue aceptado a tiempo para el lanzamiento del 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 cual se enviará el parche en sentido ascendente.
    • Decida un curso de acción para colocar el parche en ACK, lograr que se apruebe en sentido ascendente y luego retirarlo de ACK cuando la versión final ascendente se fusione en 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 el nivel superior y no hay tiempo para refactorizarlo antes del lanzamiento del producto. Para manejar este parche, proporcione un tiempo estimado en el cual 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).

  • El parche no puede ser aceptado por upstream 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 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 algunas iteraciones y discusiones. Reconocemos que el ACK incluirá algunos parches, especialmente en las primeras fases de GKI, mientras todos aprenden a trabajar en sentido ascendente pero no pueden relajar los cronogramas de productos para hacerlo. Espere que los requisitos de upstreaming se vuelvan más estrictos con el tiempo.

Requisitos de 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 en ACK. El script scripts/checkpatch.pl se ejecuta como parte de la prueba previa al enví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/kernel/static_analysis:checkpatch_presubmit . Para obtener más detalles, consulte 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 las pautas de contribución . Debe incluir una etiqueta Change-Id en el mensaje de confirmación; Si envía el parche a varias ramas (por ejemplo, android-mainline y android12-5.4 ), debe usar el mismo Change-Id para todas las instancias del parche.

Envíe los 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 realizar una copia de respaldo al ACK basado en LTS apropiado (como android12-5.4 y android11-5.4 para parches que corrigen código específico de Android). El envío a android-mainline permite realizar pruebas con nuevas versiones candidatas ascendentes y garantiza que el parche esté en el próximo ACK basado en LTS. Las excepciones incluyen casos en los que un parche ascendente se actualiza a android12-5.4 (porque es probable que el parche ya esté en android-mainline ).

Parches ascendentes

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

  • UPSTREAM: - Es probable que los parches seleccionados de 'android-mainline' se acepten en ACK si existe un caso de uso razonable.
  • BACKPORT: - También es probable que se acepten los parches anteriores que no se seleccionan limpiamente y necesitan modificaciones si existe un caso de uso razonable.
  • FROMGIT: - Los parches seleccionados de una rama de mantenimiento en preparación para enviarlos a la línea principal de Linux podrían aceptarse si hay una fecha límite próxima. Estos deberán estar justificados tanto por el contenido como por el cronograma.
  • FROMLIST: - Es poco probable que se acepten los parches que se han enviado a LKML pero que aún no han sido aceptados en una rama de mantenimiento, a menos que la justificación sea lo suficientemente convincente como para que el parche sea aceptado ya sea que llegue o no a Linux (asumimos que no será así). Debe haber un problema asociado con los parches 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 en sentido ascendente, 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 el motivo por el cual el parche no se puede enviar en sentido ascendente (consulte la lista anterior para ver ejemplos). Sin embargo, hay algunos casos en los que el código no se puede enviar en sentido ascendente. 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 CONFIG a gki_defconfig se deben aplicar a las versiones arm64 y x86 a menos que la CONFIG sea específica de la arquitectura. Para solicitar un cambio en una configuración CONFIG , cree un problema en TI para discutir el cambio. Cualquier cambio CONFIG que afecte a la interfaz del módulo kernel (KMI) después de su congelación se rechaza. En los casos en que los socios solicitan configuraciones conflictivas para una sola configuración, resolvemos los conflictos mediante discusiones sobre los errores relacionados.

Código que no existe en sentido ascendente

Las modificaciones al código que ya es específico de Android no se pueden enviar ascendentes. Por ejemplo, aunque el controlador de carpeta se mantiene en sentido ascendente, las modificaciones a las características de herencia prioritarias del controlador de carpeta no se pueden enviar en sentido ascendente porque son específicas de Android. Sea explícito en su error y solucione por qué el código no se puede enviar en sentido ascendente. Si es posible, divida los parches en partes que se puedan enviar en sentido ascendente y partes específicas de Android que no se puedan enviar en sentido ascendente para minimizar la cantidad de código fuera del árbol mantenido en ACK.

Otros cambios en esta categoría son actualizaciones de archivos de representación KMI, listas de símbolos 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 la creación de módulos fuera del árbol. Esta es una posición razonable dado que los mantenedores de Linux no ofrecen garantías sobre el código fuente en el kernel o la compatibilidad binaria 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, existe 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 de origen. Si bien debe intentar solicitar EXPORT_SYMBOL_GPL() en sentido ascendente y proporcionar un módulo que utilice el símbolo recién exportado, si hay 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 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 dar cabida 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 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 . Utilice este mecanismo sólo para configuraciones ocultas; Si la configuración no está oculta, se debe especificar en gki_defconfig ya sea explícitamente o como una dependencia.

Gobernadores cargables

Para los marcos del kernel (como cpufreq ) que admiten gobernadores cargables, puede anular el gobernador predeterminado (como el gobernador schedutil de cpufreq ). Para los marcos (como el marco térmico) que no admiten gobernadores o controladores cargables pero aún requieren un 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 upstream para agregar el soporte necesario.

Ganchos de proveedores

En versiones anteriores, se podían agregar modificaciones específicas del proveedor directamente en el núcleo principal. Esto no es posible con GKI 2.0 porque el código específico del producto debe implementarse en módulos y no será aceptado en los núcleos principales 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 central, GKI acepta enlaces 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 de proveedores que están disponibles para almacenar datos específicos del proveedor para implementar estas funciones.

Los ganchos de proveedor vienen en dos variantes (normal y restringido) que se basan en puntos de seguimiento (no en eventos de seguimiento) a los que se pueden conectar 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 enlace en do_exit() al que un módulo de proveedor puede adjuntar para su procesamiento. Una implementación de ejemplo incluye los siguientes enlaces de proveedores.

  • Los enlaces de proveedores normales utilizan 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 enlaces de proveedores normales comienzan con android_vh , por lo que el nombre del enlace sched_exit() sería android_vh_sched_exit .
  • Se necesitan enlaces de proveedores restringidos para casos como los enlaces 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 separar, por lo que los módulos que se conectan a un gancho restringido nunca se pueden descargar. 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 ocurre con todos los parches específicos de Android, debe existir un problema y usted debe proporcionar una justificación). El soporte para enlaces de proveedores solo está en ACK, así que no envíe estos parches a Linux.

Agregar campos de proveedores a 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 utilizar 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 enlaces de proveedor al código del kernel como puntos de seguimiento declarándolos usando DECLARE_HOOK() o DECLARE_RESTRICTED_HOOK() y luego agregándolos al código como punto de seguimiento. Por ejemplo, para agregar trace_android_vh_sched_exit() a la función del 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);
    ...
}

La función trace_android_vh_sched_exit() inicialmente verifica solo si hay algo adjunto. 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 manejador debe ser consciente del contexto con respecto a las cerraduras retenidas, el estado del 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 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 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 gancho 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 utilizan dentro de la declaración de enlace deben estar completamente definidas para garantizar la estabilidad de ABI. De lo contrario, no es seguro eliminar la referencia a los punteros opacos o utilizar la estructura en contextos de tamaño. La inclusión que proporciona la definición completa de dichas 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 rompan el KMI. En su lugar, declare los tipos.

Adjuntar a los ganchos del proveedor

Para utilizar enlaces de proveedor, el módulo de proveedor debe registrar un controlador para el enlace (normalmente 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);
    ...
}

Características principales del núcleo

Si ninguna de las técnicas anteriores le permite implementar una función desde un módulo, entonces debe agregar la función como modificación específica de Android al núcleo principal. 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 encabezado UAPI. Los cambios en los archivos de encabezado UAPI deben realizarse 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 (tales adiciones solo son válidas en módulos de proveedores). Los nodos sysfs utilizados por las bibliotecas independientes de dispositivos y SoC y el código Java que comprende el marco de trabajo de Android se pueden cambiar solo de manera compatible y se deben cambiar en sentido ascendente si no son nodos sysfs específicos de Android. Puede crear nodos sysfs específicos del proveedor para que los utilice el espacio de usuario del proveedor. De forma predeterminada, se deniega el acceso a los nodos sysfs por espacio de usuario utilizando SELinux. Depende del proveedor agregar las etiquetas SELinux apropiadas para permitir el acceso al software del proveedor autorizado.
  • Nodos de depuración FS. Los módulos del proveedor pueden definir nodos en debugfs solo para depuración (ya que debugfs no se monta durante el funcionamiento normal del dispositivo).