Optimización del tiempo de inicio

En esta página, se proporcionan sugerencias para mejorar el tiempo de inicio.

Quita los símbolos de depuración de los módulos

De manera similar a cómo se quitan los símbolos de depuración del kernel en una instancia asegúrate de quitar también los símbolos de depuración de los módulos. Quita la depuración de los módulos ayuda al inicio mediante la reducción de lo siguiente:

  • El tiempo que lleva leer los objetos binarios desde Flash.
  • El tiempo que lleva descomprimir el disco RAM.
  • El tiempo que lleva cargar los módulos.

Si quitas el símbolo de depuración de los módulos, es posible que ahorres varios segundos durante el inicio.

La eliminación de símbolos está habilitada de forma predeterminada en la compilación de la plataforma de Android, pero para habilitarlos explícitamente, establece BOARD_DO_NOT_STRIP_VENDOR_RAMDISK_MODULES en la configuración específica de tu dispositivo en device/vendor/device.

Cómo usar la compresión LZ4 para kernel y ramdisk

Gzip genera un resultado comprimido más pequeño en comparación con LZ4, pero se descomprime más rápido que Gzip. Para el kernel y los módulos, el almacenamiento absoluto la reducción de tamaño con Gzip no es tan significativa en comparación con tiempo de descompresión de LZ4.

Se agregó compatibilidad con la compresión de ramdisk LZ4 a la plataforma de Android. compila mediante BOARD_RAMDISK_USE_LZ4. Puedes configurar esta opción en tu la configuración específica del dispositivo. La compresión del kernel se puede configurar con la defconfig.

Si cambias a LZ4, el tiempo de inicio debería ser de 500 ms a 1,000 ms más rápido.

Cómo evitar registros excesivos en tus controladores

En ARM64 y ARM32, las llamadas a funciones que están a más de una distancia específica de el sitio de la llamada necesita que una tabla de salto (llamada tabla de vinculación de procedimientos o PLT) se y se puede codificar la dirección de salto completa. Como los módulos se cargan de forma dinámica, estas tablas de salto deben arreglarse durante la carga del módulo. Las llamadas que necesitan reubicación se denominan entradas de reubicación con sumas explícitos. (o RELA, para abreviar) en formato ELF.

El kernel de Linux optimiza el tamaño de la memoria (como el acierto de caché). (optimización) cuando se asigna el PLT. Con este flujo ascendente, confirmación, el esquema de optimización tiene una complejidad O(N^2), donde N es la cantidad de RELA de tipo R_AARCH64_JUMP26 o R_AARCH64_CALL26 Es por eso que tener menos RELA de estos tipos es útil para reducir el tiempo de carga del módulo.

Un patrón de codificación común que aumenta la cantidad de R_AARCH64_CALL26 o R_AARCH64_JUMP26 RELA son registros excesivos en un controlador. Cada llamada a printk() o cualquier otro esquema de registro suele agregar un CALL26/JUMP26 entrada de RELA. En el texto de confirmación del flujo de confirmación, Ten en cuenta que incluso con la optimización, los seis módulos tardan alrededor de 250 ms ya que esos seis módulos fueron los seis módulos principales con la mayor cantidad de registros.

Reducir el registro puede ahorrar entre 100 y 300 ms en los tiempos de inicio de lo excesivo que es el registro existente.

Habilitar el sondeo asíncrono de forma selectiva

Cuando se carga un módulo, si el dispositivo que admite ya se se completa a partir de la DT (devicetree) y se agrega al núcleo del controlador, luego, el dispositivo el sondeo se realiza en el contexto de la llamada a module_init(). Cuando se realiza un sondeo de dispositivo en el contexto de module_init(), el módulo no puede terminar de cargarse hasta que el sondeo se complete. Como la carga de módulos suele estar serializada, un dispositivo que lleva un tiempo relativamente largo sondear ralentiza el tiempo de inicio.

Para evitar tiempos de inicio más lentos, habilita el sondeo asíncrono para los módulos que tarden mientras sondean sus dispositivos. Habilita el sondeo asíncrono para todos los módulos podría no ser beneficioso, ya que el tiempo que lleva bifurcar un hilo y comenzar puede ser tan alto como el tiempo que demore en sondear el dispositivo.

Dispositivos que se conectan a través de un bus lento, como I2C, dispositivos que no la carga del firmware en la función de sondeo y los dispositivos que hacen mucho hardware la inicialización puede generar un problema de tiempo. La mejor manera de identificar cuándo es recopilar el tiempo de sondeo de cada conductor y clasificarlo.

Para habilitar el sondeo asíncrono para un módulo, no es suficiente solamente configura PROBE_PREFER_ASYNCHRONOUS en el código del controlador. Para los módulos, también debes agregar module_name.async_probe=1 en la línea de comandos del kernel o pasa async_probe=1 como parámetro del módulo cuando se carga el módulo con modprobe o insmod.

Si habilitas el sondeo asíncrono puede ahorrar alrededor de 100 ms a 500 ms en los tiempos de inicio según el hardware o los controladores.

Sondea el controlador de CPUfreq lo antes posible

Cuanto antes investigue el controlador de CPUfreq, antes podrás escalar la CPU frecuencia al máximo (o un máximo limitado térmicamente) durante el inicio. El más rápido será la CPU, más rápido será el inicio. Este lineamiento también se aplica a devfreq. controladores que controlan la DRAM, la memoria y la frecuencia de interconexión.

Con los módulos, el orden de la carga puede depender del nivel de initcall y el orden de compilación o vinculación de los controladores. Usa un alias MODULE_SOFTDEP() para hacer Asegúrate de que el controlador cpufreq esté entre los primeros módulos que se cargarán.

Además de cargar el módulo con antelación, debes asegurarte de que para sondear el controlador de CPUfreq. Por ejemplo, si necesitas un reloj o una manija de regulador para controlar la frecuencia de tu CPU, asegúrate antes de verificar que se sondean primero. O es posible que necesites controladores térmicos para cargar antes que el controlador de CPUfreq si es posible que las CPU se en caliente durante el inicio. Así que, haga lo que pueda para asegurarse de que la CPUfreq los controladores de devfreq sondeos lo antes posible.

El ahorro por sondear el controlador de CPUfreq de manera anticipada puede ser desde muy pequeño hasta muy grandes, dependiendo de qué tan temprano puedas hacer el sondeo y con qué frecuencia el bootloader deja las CPU en su interior.

Mueve módulos a la partición init, provider o provider_dlkm de la segunda etapa.

Debido a que el proceso init de la primera etapa es serializado, no hay muchos oportunidades para paralelizar el proceso de inicio. Si no se necesita un módulo para init de primera etapa para finalizar, coloca el módulo a init de segunda etapa colocándolo en la partición del proveedor o vendor_dlkm.

El inicio de la primera etapa no requiere el sondeo de varios dispositivos para llegar a la segunda etapa init. Solo se requieren las capacidades de almacenamiento flash y de la consola para un flujo de inicio normal.

Carga los siguientes controladores esenciales:

  • watchdog
  • reset
  • cpufreq

Para el modo fastbootd de recuperación y espacio del usuario, el init de la primera etapa requiere más dispositivos para sondear (como USB) y mostrar. Guarda una copia de estos módulos en el ramdisk de primera etapa y en la partición vendor_dlkm o del proveedor. Esto les permite cargarse en el inicio de la primera etapa para la recuperación o en el flujo de inicio de fastbootd. Sin embargo, No cargues los módulos del modo de recuperación en el inicio de la primera etapa durante el inicio normal. de tu flujo de trabajo. Los módulos de modo de recuperación pueden aplazarse al init de la segunda etapa para disminuir la tiempo de inicio. Todos los demás módulos que no sean necesarios en el init de la primera etapa deben se movió a la partición del proveedor o vendor_dlkm.

Dada una lista de dispositivos de hoja (por ejemplo, el modelo UFS o el número de serie), dev needs.sh secuencia de comandos encuentra todos los controladores, dispositivos y módulos necesarios para las dependencias o proveedores (por ejemplo, relojes, reguladores o gpio) para sondear.

Mover los módulos a init de segunda etapa disminuye los tiempos de inicio en las siguientes situaciones: maneras:

  • Reducción del tamaño del ramdisk
    • Esto produce lecturas más rápidas cuando el bootloader carga el disco RAM. (paso de inicio serializado).
    • Esto produce velocidades de descompresión más rápidas cuando el kernel descomprime la ramdisk (paso de inicio serializado)
  • El init de la segunda etapa funciona en paralelo, lo que oculta el tiempo de carga del módulo y el trabajo se hace en el init de la segunda etapa.

Mover módulos a la segunda etapa puede ahorrar entre 500 y 1,000 ms en los tiempos de inicio, según de cuántos módulos puedes pasar a la init de la segunda etapa.

Logística de la carga del módulo

Las últimas configuraciones de la placa de funciones de compilación de Android que controlan qué los módulos se copian en cada etapa y qué módulos se cargan. Esta sección se centra en el siguiente subconjunto:

  • BOARD_VENDOR_RAMDISK_KERNEL_MODULES Esta lista de módulos se copiará en el ramdisk.
  • BOARD_VENDOR_RAMDISK_KERNEL_MODULES_LOAD Esta lista de módulos para cargar en el init de la primera etapa.
  • BOARD_VENDOR_RAMDISK_RECOVERY_KERNEL_MODULES_LOAD Esta lista de módulos para se cargará cuando se seleccione la recuperación o se seleccione fastbootd del disco RAM.
  • BOARD_VENDOR_KERNEL_MODULES Esta lista de módulos se copiará en proveedor o vendor_dlkm en el directorio /vendor/lib/modules/.
  • BOARD_VENDOR_KERNEL_MODULES_LOAD Esta lista de módulos que se cargarán en init de segunda etapa.

Los módulos de inicio y recuperación de ramdisk también se deben copiar en el proveedor vendor_dlkm de partición en /vendor/lib/modules. Copiar estos módulos en el la partición de proveedor garantiza que los módulos no sean invisibles durante el inicio de la segunda etapa, que es útil para depurar y recopilar modinfo para los informes de errores.

La duplicación debería costar un espacio mínimo en el proveedor o vendor_dlkm. partición siempre y cuando se minimice el conjunto de módulos de inicio. Asegúrate de que la configuración del proveedor El archivo modules.list tiene una lista filtrada de módulos en /vendor/lib/modules. La lista filtrada garantiza que los tiempos de inicio no se vean afectados por la carga de los módulos (lo que es un proceso costoso).

Asegúrate de que los módulos del modo de recuperación se carguen como un grupo. Cargando módulos de Modo de recuperación puede hacerse en modo de recuperación o al comienzo de la segunda etapa init en cada flujo de inicio.

Puedes usar los archivos Board.Config.mk del dispositivo para realizar estas acciones como se ve en el siguiente ejemplo:

# All kernel modules
KERNEL_MODULES := $(wildcard $(KERNEL_MODULE_DIR)/*.ko)
KERNEL_MODULES_LOAD := $(strip $(shell cat $(KERNEL_MODULE_DIR)/modules.load)

# First stage ramdisk modules
BOOT_KERNEL_MODULES_FILTER := $(foreach m,$(BOOT_KERNEL_MODULES),%/$(m))

# Recovery ramdisk modules
RECOVERY_KERNEL_MODULES_FILTER := $(foreach m,$(RECOVERY_KERNEL_MODULES),%/$(m))
BOARD_VENDOR_RAMDISK_KERNEL_MODULES += \
     $(filter $(BOOT_KERNEL_MODULES_FILTER) \
                $(RECOVERY_KERNEL_MODULES_FILTER),$(KERNEL_MODULES))

# ALL modules land in /vendor/lib/modules so they could be rmmod/insmod'd,
# and modules.list actually limits us to the ones we intend to load.
BOARD_VENDOR_KERNEL_MODULES := $(KERNEL_MODULES)
# To limit /vendor/lib/modules to just the ones loaded, use:
# BOARD_VENDOR_KERNEL_MODULES := $(filter-out \
#     $(BOOT_KERNEL_MODULES_FILTER),$(KERNEL_MODULES))

# Group set of /vendor/lib/modules loading order to recovery modules first,
# then remainder, subtracting both recovery and boot modules which are loaded
# already.
BOARD_VENDOR_KERNEL_MODULES_LOAD := \
        $(filter-out $(BOOT_KERNEL_MODULES_FILTER), \
        $(filter $(RECOVERY_KERNEL_MODULES_FILTER),$(KERNEL_MODULES_LOAD)))
BOARD_VENDOR_KERNEL_MODULES_LOAD += \
        $(filter-out $(BOOT_KERNEL_MODULES_FILTER) \
            $(RECOVERY_KERNEL_MODULES_FILTER),$(KERNEL_MODULES_LOAD))

# NB: Load order governed by modules.load and not by $(BOOT_KERNEL_MODULES)
BOARD_VENDOR_RAMDISK_KERNEL_MODULES_LOAD := \
        $(filter $(BOOT_KERNEL_MODULES_FILTER),$(KERNEL_MODULES_LOAD))

# Group set of /vendor/lib/modules loading order to boot modules first,
# then the remainder of recovery modules.
BOARD_VENDOR_RAMDISK_RECOVERY_KERNEL_MODULES_LOAD := \
    $(filter $(BOOT_KERNEL_MODULES_FILTER),$(KERNEL_MODULES_LOAD))
BOARD_VENDOR_RAMDISK_RECOVERY_KERNEL_MODULES_LOAD += \
    $(filter-out $(BOOT_KERNEL_MODULES_FILTER), \
    $(filter $(RECOVERY_KERNEL_MODULES_FILTER),$(KERNEL_MODULES_LOAD)))

En este ejemplo, se muestra un subconjunto más fácil de administrar de BOOT_KERNEL_MODULES y RECOVERY_KERNEL_MODULES se especificará de forma local en la configuración de la placa archivos. La secuencia de comandos anterior encuentra y completa cada uno de los módulos de subconjunto del módulos de kernel disponibles seleccionados, dejando los demás módulos por segundo init de la etapa.

Para init de segunda etapa, recomendamos ejecutar la carga del módulo como servicio para no bloquea el flujo de inicio. Usa una secuencia de comandos de shell para administrar la carga del módulo para que otros aspectos logísticos, como el manejo y la mitigación de errores, o la carga de módulos del proyecto, se pueden informar (o ignorar) si es necesario.

Puedes ignorar un error de carga del módulo de depuración que no está presente en las compilaciones de usuario. Para ignorar esta falla, establece la propiedad vendor.device.modules.ready en Activa etapas posteriores del flujo de inicio de secuencias de comandos de init rc para continuar con el lanzamiento en la pantalla. Consulta la siguiente secuencia de comandos de ejemplo si tienes el siguiente código en /vendor/etc/init.insmod.sh:

#!/vendor/bin/sh
. . .
if [ $# -eq 1 ]; then
  cfg_file=$1
else
  # Set property even if there is no insmod config
  # to unblock early-boot trigger
  setprop vendor.common.modules.ready
  setprop vendor.device.modules.ready
  exit 1
fi

if [ -f $cfg_file ]; then
  while IFS="|" read -r action arg
  do
    case $action in
      "insmod") insmod $arg ;;
      "setprop") setprop $arg 1 ;;
      "enable") echo 1 > $arg ;;
      "modprobe") modprobe -a -d /vendor/lib/modules $arg ;;
     . . .
    esac
  done < $cfg_file
fi

En el archivo rc de hardware, el servicio one shot se podría especificar con lo siguiente:

service insmod-sh /vendor/etc/init.insmod.sh /vendor/etc/init.insmod.<hw>.cfg
    class main
    user root
    group root system
    Disabled
    oneshot

Se pueden realizar optimizaciones adicionales después de que los módulos pasen del primer módulo a la segunda etapa. Puedes usar la función de lista de entidades bloqueadas de modprobe para dividir el segundo para incluir la carga diferida de módulos de módulos no esenciales. Se puede aplazar la carga de módulos que usa exclusivamente una HAL específica para cargar los módulos solo cuando se inicia la HAL.

Para mejorar los tiempos de inicio aparente, puedes elegir módulos en la sección de carga de módulos propicio para la carga después del lanzamiento en la pantalla. Por ejemplo, puedes cargar de forma explícita tarde los módulos para decodificador de video o Wi-Fi después de que se haya borrado el flujo de inicio de inicio (sys.boot_complete) como el indicador de propiedad de Android). Asegúrate de que las HALs de carga tardía Los módulos se bloquean lo suficiente cuando los controladores del kernel no están presentes.

Como alternativa, puedes usar el comando wait<file>[<timeout>] de init en el archivo de inicio La secuencia de comandos de Flow rc espera a que las entradas seleccionadas de sysfs muestren que los módulos del controlador completaron las operaciones de sondeo. Un ejemplo de esto es esperar a que mostrar el controlador para completar la carga en segundo plano de la recuperación o fastbootd antes de presentar los gráficos del menú.

Inicializa la frecuencia de la CPU en un valor razonable en el bootloader

Es posible que no todos los SoC o productos puedan iniciar la CPU con la frecuencia más alta. debido a problemas térmicos o de alimentación durante las pruebas del bucle de inicio. Sin embargo, asegúrate de que el bootloader establece la frecuencia de todas las CPU en línea en un nivel tan alto como seguro para un SoC o producto. Esto es muy importante porque, con una visión kernel modular, la descompresión de ramdisk init se realiza antes de que el controlador de cargas. Por lo tanto, si la CPU se deja en el extremo inferior de su frecuencia, por el bootloader, el tiempo de descompresión del ramdisk puede tardar más kernel compilado de forma estática (después de ajustar la diferencia de tamaño del ramdisk) ya que la frecuencia de la CPU sería muy baja cuando se realizara un trabajo intensivo. (descompresión). Lo mismo se aplica a la memoria y la frecuencia de interconexión.

Inicializa la frecuencia de la CPU grande en el bootloader

Antes de cargar el controlador CPUfreq, el kernel no reconoce la las frecuencias de CPU y no ajusta la capacidad de programación de la CPU para su uso frecuencia. El kernel puede migrar subprocesos a la CPU grande si la carga se es lo suficientemente alta en la CPU pequeña.

Asegúrese de que las CPU grandes tengan, al menos, el mismo rendimiento que las CPU pequeñas para el la frecuencia con la que el bootloader los deja. Por ejemplo, si la CPU grande es 2 veces más eficaz que una CPU pequeña para la misma frecuencia, pero bootloader establece la frecuencia de la CPU pequeña en 1.5 GHz y la frecuencia de la a 300 MHz, el rendimiento del inicio disminuirá si el kernel empieza mueve un subproceso a la CPU grande. En este ejemplo, si es seguro iniciar a 750 MHz, deberías hacerlo incluso si no planeas usarlo explícitamente.

Los controladores no deberían cargar el firmware en el inicio de la primera etapa.

Puede haber algunos casos inevitables en los que primero se deba cargar el firmware. init de la etapa. Pero, en general, los conductores no deberían cargar ningún firmware en la primera etapa especialmente en el contexto de sondeo del dispositivo. Cargando firmware en el inicio de la primera etapa hace que todo el proceso de inicio se detenga si el firmware no está disponible en el en el disco RAM de primera etapa. Incluso si el firmware está presente en la primera etapa ramdisk, aún causa un retraso innecesario.