Implementar particiones dinámicas

La partición dinámica se implementa utilizando el módulo dm-linear device-mapper en el kernel de Linux. La super contiene metadatos que enumeran los nombres y rangos de bloques de cada partición dinámica dentro de super . Durante init de la primera etapa, estos metadatos se analizan y validan, y se crean dispositivos de bloques virtuales para representar cada partición dinámica.

Al aplicar una OTA, las particiones dinámicas se crean, cambian de tamaño o eliminan automáticamente según sea necesario. Para los dispositivos A/B, hay dos copias de los metadatos y los cambios se aplican solo a la copia que representa la ranura de destino.

Debido a que las particiones dinámicas se implementan en el espacio de usuario, las particiones que necesita el gestor de arranque no se pueden hacer dinámicas. Por ejemplo, el gestor de arranque lee boot , dtbo y vbmeta , por lo que deben permanecer como particiones físicas.

Cada partición dinámica puede pertenecer a un grupo de actualización . Estos grupos limitan el espacio máximo que pueden consumir las particiones de ese grupo. Por ejemplo, system y vendor pueden pertenecer a un grupo que restringe el tamaño total del system y vendor .

Implementar particiones dinámicas en nuevos dispositivos

Esta sección detalla cómo implementar particiones dinámicas en dispositivos nuevos que se inician con Android 10 y superior. Para actualizar dispositivos existentes, consulte Actualización de dispositivos Android .

Cambios de partición

Para dispositivos que se inician con Android 10, cree una partición llamada super . La super maneja las ranuras A/B internamente, por lo que los dispositivos A/B no necesitan particiones super_a y super_b separadas. Todas las particiones AOSP de solo lectura que no utiliza el gestor de arranque deben ser dinámicas y deben eliminarse de la tabla de particiones GUID (GPT). Las particiones específicas del proveedor no tienen que ser dinámicas y pueden colocarse en el GPT.

Para estimar el tamaño de super , agregue los tamaños de las particiones que se eliminan del GPT. Para dispositivos A/B, esto debe incluir el tamaño de ambas ranuras. La Figura 1 muestra una tabla de particiones de ejemplo antes y después de la conversión a particiones dinámicas.

Diseño de la tabla de particiones
Figura 1. Nuevo diseño de la tabla de particiones físicas al convertir a particiones dinámicas

Las particiones dinámicas admitidas son:

  • Sistema
  • Proveedor
  • Producto
  • Extensión del sistema
  • ODM

Para dispositivos que se inician con Android 10, la opción de línea de comando del kernel androidboot.super_partition debe estar vacía para que el comando sysprop ro.boot.super_partition esté vacío.

Alineación de particiones

El módulo asignador de dispositivos puede funcionar con menos eficiencia si la super no está alineada correctamente. La super DEBE estar alineada con el tamaño mínimo de solicitud de E/S determinado por la capa de bloque. De forma predeterminada, el sistema de compilación (a través de lpmake , que genera la imagen de la super ), asume que una alineación de 1 MiB es suficiente para cada partición dinámica. Sin embargo, los proveedores deben asegurarse de que la super esté alineada correctamente.

Puede determinar el tamaño mínimo de solicitud de un dispositivo de bloque inspeccionando sysfs . Por ejemplo:

# ls -l /dev/block/by-name/super
lrwxrwxrwx 1 root root 16 1970-04-05 01:41 /dev/block/by-name/super -> /dev/block/sda17
# cat /sys/block/sda/queue/minimum_io_size
786432

Puede verificar la alineación de super de manera similar:

# cat /sys/block/sda/sda17/alignment_offset

El desplazamiento de alineación DEBE ser 0.

Cambios en la configuración del dispositivo

Para habilitar la partición dinámica, agregue la siguiente marca en device.mk :

PRODUCT_USE_DYNAMIC_PARTITIONS := true

Cambios de configuración de la placa

Debes establecer el tamaño de la super :

BOARD_SUPER_PARTITION_SIZE := <size-in-bytes>

En dispositivos A/B, el sistema de compilación genera un error si el tamaño total de las imágenes de partición dinámica es más de la mitad del tamaño de la super .

Puede configurar la lista de particiones dinámicas de la siguiente manera. Para dispositivos que utilizan grupos de actualización, enumere los grupos en la variable BOARD_SUPER_PARTITION_GROUPS . Cada nombre de grupo tiene entonces una variable BOARD_ group _SIZE y BOARD_ group _PARTITION_LIST . Para dispositivos A/B, el tamaño máximo de un grupo debe cubrir solo una ranura, ya que los nombres de los grupos tienen sufijos de ranura internamente.

Aquí hay un dispositivo de ejemplo que coloca todas las particiones en un grupo llamado example_dynamic_partitions :

BOARD_SUPER_PARTITION_GROUPS := example_dynamic_partitions
BOARD_EXAMPLE_DYNAMIC_PARTITIONS_SIZE := 6442450944
BOARD_EXAMPLE_DYNAMIC_PARTITIONS_PARTITION_LIST := system vendor product

A continuación se muestra un dispositivo de ejemplo que coloca los servicios del sistema y del producto en group_foo , y vendor , product y odm en group_bar :

BOARD_SUPER_PARTITION_GROUPS := group_foo group_bar
BOARD_GROUP_FOO_SIZE := 4831838208
BOARD_GROUP_FOO_PARTITION_LIST := system product_services
BOARD_GROUP_BAR_SIZE := 1610612736
BOARD_GROUP_BAR_PARTITION_LIST := vendor product odm
  • Para dispositivos de lanzamiento Virtual A/B, la suma de los tamaños máximos de todos los grupos debe ser como máximo:
    BOARD_SUPER_PARTITION_SIZE - gastos generales
    Consulte Implementación de Virtual A/B .
  • Para dispositivos de lanzamiento A/B, la suma de los tamaños máximos de todos los grupos debe ser:
    BOARD_SUPER_PARTITION_SIZE / 2 - gastos generales
  • Para dispositivos que no son A/B y dispositivos A/B modernizados, la suma de los tamaños máximos de todos los grupos debe ser:
    BOARD_SUPER_PARTITION_SIZE - gastos generales
  • En el momento de la compilación, la suma de los tamaños de las imágenes de cada partición en un grupo de actualización no debe exceder el tamaño máximo del grupo.
  • Se requieren gastos generales en el cálculo para tener en cuenta los metadatos, las alineaciones, etc. Una sobrecarga razonable es de 4 MiB, pero puede elegir una sobrecarga mayor según lo necesite el dispositivo.

Tamaño de particiones dinámicas

Antes de las particiones dinámicas, los tamaños de las particiones se sobreasignaban para garantizar que tuvieran suficiente espacio para futuras actualizaciones. El tamaño real se tomó tal cual y la mayoría de las particiones de sólo lectura tenían cierta cantidad de espacio libre en su sistema de archivos. En las particiones dinámicas, ese espacio libre no se puede utilizar y podría usarse para hacer crecer las particiones durante una OTA. Es fundamental asegurarse de que las particiones no desperdicien espacio y se asignen al tamaño mínimo posible.

Para imágenes ext4 de solo lectura, el sistema de compilación asigna automáticamente el tamaño mínimo si no se especifica ningún tamaño de partición codificada. El sistema de compilación se ajusta a la imagen para que el sistema de archivos tenga el menor espacio posible sin utilizar. Esto garantiza que el dispositivo no desperdicie espacio que pueda usarse para OTA.

Además, las imágenes ext4 se pueden comprimir aún más habilitando la deduplicación a nivel de bloque. Para habilitar esto, use la siguiente configuración:

BOARD_EXT4_SHARE_DUP_BLOCKS := true

Si no es deseable la asignación automática del tamaño mínimo de una partición, existen dos formas de controlar el tamaño de la partición. Puede especificar una cantidad mínima de espacio libre con BOARD_ partition IMAGE_PARTITION_RESERVED_SIZE , o puede especificar BOARD_ partition IMAGE_PARTITION_SIZE para forzar las particiones dinámicas a un tamaño específico. Ninguno de estos se recomienda a menos que sea necesario.

Por ejemplo:

BOARD_PRODUCTIMAGE_PARTITION_RESERVED_SIZE := 52428800

Esto obliga al sistema de archivos en product.img a tener 50 MiB de espacio no utilizado.

Cambios del sistema como raíz

Los dispositivos que se inician con Android 10 no deben utilizar el sistema como raíz.

Los dispositivos con particiones dinámicas (ya sea que se inicien con particiones dinámicas o las actualicen) no deben usar el sistema como raíz. El kernel de Linux no puede interpretar la super y, por lo tanto, no puede montar system . system ahora está montado mediante init de primera etapa, que reside en el disco RAM.

No configure BOARD_BUILD_SYSTEM_ROOT_IMAGE . En Android 10, el indicador BOARD_BUILD_SYSTEM_ROOT_IMAGE solo se usa para diferenciar si el sistema está montado por el kernel o por el init de primera etapa en ramdisk.

Establecer BOARD_BUILD_SYSTEM_ROOT_IMAGE en true provoca un error de compilación cuando PRODUCT_USE_DYNAMIC_PARTITIONS también es true .

Cuando BOARD_USES_RECOVERY_AS_BOOT se establece en verdadero, la imagen de recuperación se crea como boot.img y contiene el disco ram de recuperación. Anteriormente, el gestor de arranque usaba el parámetro de línea de comando del kernel skip_initramfs para decidir en qué modo arrancar. Para dispositivos con Android 10, el gestor de arranque NO DEBE pasar skip_initramfs a la línea de comandos del kernel. En su lugar, el gestor de arranque debe pasar androidboot.force_normal_boot=1 para omitir la recuperación e iniciar Android normal. Los dispositivos que se inician con Android 12 o posterior deben usar bootconfig para pasar androidboot.force_normal_boot=1 .

Cambios de configuración de AVB

Cuando se utiliza Android Verified Boot 2.0 , si el dispositivo no utiliza descriptores de partición encadenados , no es necesario ningún cambio. Sin embargo, si se utilizan particiones encadenadas y una de las particiones verificadas es dinámica, entonces son necesarios cambios.

A continuación se muestra una configuración de ejemplo para un dispositivo que encadena vbmeta para el system y las particiones vendor .

BOARD_AVB_SYSTEM_KEY_PATH := external/avb/test/data/testkey_rsa2048.pem
BOARD_AVB_SYSTEM_ALGORITHM := SHA256_RSA2048
BOARD_AVB_SYSTEM_ROLLBACK_INDEX := $(PLATFORM_SECURITY_PATCH_TIMESTAMP)
BOARD_AVB_SYSTEM_ROLLBACK_INDEX_LOCATION := 1

BOARD_AVB_VENDOR_KEY_PATH := external/avb/test/data/testkey_rsa2048.pem
BOARD_AVB_VENDOR_ALGORITHM := SHA256_RSA2048
BOARD_AVB_VENDOR_ROLLBACK_INDEX := $(PLATFORM_SECURITY_PATCH_TIMESTAMP)
BOARD_AVB_VENDOR_ROLLBACK_INDEX_LOCATION := 1

Con esta configuración, el gestor de arranque espera encontrar un pie de página vbmeta al final del system y de las particiones vendor . Debido a que estas particiones ya no son visibles para el gestor de arranque (residen en super ), se necesitan dos cambios.

  • Agregue las particiones vbmeta_system y vbmeta_vendor a la tabla de particiones del dispositivo. Para dispositivos A/B, agregue vbmeta_system_a , vbmeta_system_b , vbmeta_vendor_a y vbmeta_vendor_b . Si agrega una o más de estas particiones, deben tener el mismo tamaño que la partición vbmeta .
  • Cambie el nombre de los indicadores de configuración agregando VBMETA_ y especifique a qué particiones se extiende el encadenamiento:
    BOARD_AVB_VBMETA_SYSTEM := system
    BOARD_AVB_VBMETA_SYSTEM_KEY_PATH := external/avb/test/data/testkey_rsa2048.pem
    BOARD_AVB_VBMETA_SYSTEM_ALGORITHM := SHA256_RSA2048
    BOARD_AVB_VBMETA_SYSTEM_ROLLBACK_INDEX := $(PLATFORM_SECURITY_PATCH_TIMESTAMP)
    BOARD_AVB_VBMETA_SYSTEM_ROLLBACK_INDEX_LOCATION := 1
    
    BOARD_AVB_VBMETA_VENDOR := vendor
    BOARD_AVB_VBMETA_VENDOR_KEY_PATH := external/avb/test/data/testkey_rsa2048.pem
    BOARD_AVB_VBMETA_VENDOR_ALGORITHM := SHA256_RSA2048
    BOARD_AVB_VBMETA_VENDOR_ROLLBACK_INDEX := $(PLATFORM_SECURITY_PATCH_TIMESTAMP)
    BOARD_AVB_VBMETA_VENDOR_ROLLBACK_INDEX_LOCATION := 1
    

Un dispositivo puede estar usando una, ambas o ninguna de estas particiones. Los cambios sólo son necesarios cuando se encadena a una partición lógica.

Cambios en el gestor de arranque AVB

Si el gestor de arranque tiene libavb integrado, incluya los siguientes parches:

Si utiliza particiones encadenadas, incluya un parche adicional:

  • 49936b4c0109411fdd38bd4ba3a32a01c40439a9 — "libavb: admite blobs vbmeta al comienzo de la partición".

Cambios en la línea de comando del kernel

Se debe agregar un nuevo parámetro, androidboot.boot_devices , a la línea de comando del kernel. Esto lo utiliza init para habilitar los enlaces /dev/block/by-name . Debería ser el componente de ruta del dispositivo al enlace simbólico de nombre subyacente creado por ueventd , es decir, /dev/block/platform/ device-path /by-name/ partition-name . Los dispositivos que se inician con Android 12 o posterior deben usar bootconfig para pasar androidboot.boot_devices a init .

Por ejemplo, si el enlace simbólico por nombre de la superpartición es /dev/block/platform/ soc/100000.ufshc /by-name/super , puede agregar el parámetro de línea de comando en el archivo BoardConfig.mk de la siguiente manera:

BOARD_KERNEL_CMDLINE += androidboot.boot_devices=soc/100000.ufshc
Puede agregar el parámetro bootconfig en el archivo BoardConfig.mk de la siguiente manera:
BOARD_BOOTCONFIG += androidboot.boot_devices=soc/100000.ufshc

cambios fstab

El árbol de dispositivos y las superposiciones del árbol de dispositivos no deben contener entradas fstab. Utilice un archivo fstab que será parte del disco ram.

Se deben realizar cambios en el archivo fstab para particiones lógicas:

  • El campo fs_mgr flags debe incluir el indicador logical y el indicador first_stage_mount , introducido en Android 10, que indica que se va a montar una partición en la primera etapa.
  • Una partición puede especificar avb= vbmeta partition name como un indicador fs_mgr y luego la partición vbmeta especificada se inicializa mediante init de primera etapa antes de intentar montar cualquier dispositivo.
  • El campo dev debe ser el nombre de la partición.

Las siguientes entradas de fstab configuran el sistema, el proveedor y el producto como particiones lógicas siguiendo las reglas anteriores.

#<dev>  <mnt_point> <type>  <mnt_flags options> <fs_mgr_flags>
system   /system     ext4    ro,barrier=1        wait,slotselect,avb=vbmeta,logical,first_stage_mount
vendor   /vendor     ext4    ro,barrier=1        wait,slotselect,avb,logical,first_stage_mount
product  /product    ext4    ro,barrier=1        wait,slotselect,avb,logical,first_stage_mount

Copie el archivo fstab en el disco RAM de la primera etapa.

Cambios en SELinux

El dispositivo de bloque de superpartición debe estar marcado con la etiqueta super_block_device . Por ejemplo, si el enlace simbólico por nombre de la superpartición es /dev/block/platform/ soc/100000.ufshc /by-name/super , agregue la siguiente línea a file_contexts :

/dev/block/platform/soc/10000\.ufshc/by-name/super   u:object_r:super_block_device:s0

arranque rápido

El gestor de arranque (o cualquier herramienta de actualización que no sea del espacio de usuario) no comprende las particiones dinámicas, por lo que no puede actualizarlas. Para solucionar esto, los dispositivos deben utilizar una implementación en el espacio de usuario del protocolo fastboot, llamada fastbootd.

Para obtener más información sobre cómo implementar fastbootd, consulte Mover Fastboot al espacio de usuario .

remontar adb

Para los desarrolladores que utilizan compilaciones eng o userdebug, adb remount es extremadamente útil para una iteración rápida. Las particiones dinámicas plantean un problema para adb remount porque ya no hay espacio libre dentro de cada sistema de archivos. Para solucionar esto, los dispositivos pueden habilitar superposiciones. Siempre que haya espacio libre dentro de la superpartición, adb remount crea automáticamente una partición dinámica temporal y utiliza superposiciones para las escrituras. La partición temporal se llama scratch , así que no utilice este nombre para otras particiones.

Para obtener más información sobre cómo habilitar overlayfs, consulte el archivo README de overlayfs en AOSP.

Actualizar dispositivos Android

Si actualiza un dispositivo a Android 10 y desea incluir compatibilidad con particiones dinámicas en la OTA, no necesita cambiar la tabla de particiones integrada. Se requiere alguna configuración adicional.

Cambios en la configuración del dispositivo

Para actualizar la partición dinámica, agregue las siguientes opciones en device.mk :

PRODUCT_USE_DYNAMIC_PARTITIONS := true
PRODUCT_RETROFIT_DYNAMIC_PARTITIONS := true

Cambios de configuración de la placa

Debes configurar las siguientes variables del tablero:

  • Establezca BOARD_SUPER_PARTITION_BLOCK_DEVICES en la lista de dispositivos de bloque utilizados para almacenar extensiones de particiones dinámicas. Esta es la lista de nombres de particiones físicas existentes en el dispositivo.
  • Establezca BOARD_SUPER_PARTITION_ partition _DEVICE_SIZE en los tamaños de cada dispositivo de bloque en BOARD_SUPER_PARTITION_BLOCK_DEVICES , respectivamente. Esta es la lista de tamaños de particiones físicas existentes en el dispositivo. Suele ser BOARD_ partition IMAGE_PARTITION_SIZE en configuraciones de placa existentes.
  • Desarmar BOARD_ partition IMAGE_PARTITION_SIZE existente para todas las particiones en BOARD_SUPER_PARTITION_BLOCK_DEVICES .
  • Establezca BOARD_SUPER_PARTITION_SIZE en la suma de BOARD_SUPER_PARTITION_ partition _DEVICE_SIZE .
  • Establezca BOARD_SUPER_PARTITION_METADATA_DEVICE en el dispositivo de bloque donde se almacenan los metadatos de la partición dinámica. Debe ser uno de BOARD_SUPER_PARTITION_BLOCK_DEVICES . Generalmente, esto está configurado en system .
  • Establezca BOARD_SUPER_PARTITION_GROUPS , BOARD_ group _SIZE y BOARD_ group _PARTITION_LIST , respectivamente. Consulte Cambios de configuración de la placa en dispositivos nuevos para obtener más detalles.

Por ejemplo, si el dispositivo ya tiene particiones de sistema y de proveedor y desea convertirlas en particiones dinámicas y agregar una nueva partición de producto durante la actualización, establezca esta configuración de placa:

BOARD_SUPER_PARTITION_BLOCK_DEVICES := system vendor
BOARD_SUPER_PARTITION_METADATA_DEVICE := system

# Rename BOARD_SYSTEMIMAGE_PARTITION_SIZE to BOARD_SUPER_PARTITION_SYSTEM_DEVICE_SIZE.
BOARD_SUPER_PARTITION_SYSTEM_DEVICE_SIZE := <size-in-bytes>

# Rename BOARD_VENDORIMAGE_PARTITION_SIZE to BOARD_SUPER_PARTITION_VENDOR_DEVICE_SIZE
BOARD_SUPER_PARTITION_VENDOR_DEVICE_SIZE := <size-in-bytes>

# This is BOARD_SUPER_PARTITION_SYSTEM_DEVICE_SIZE + BOARD_SUPER_PARTITION_VENDOR_DEVICE_SIZE
BOARD_SUPER_PARTITION_SIZE := <size-in-bytes>

# Configuration for dynamic partitions. For example:
BOARD_SUPER_PARTITION_GROUPS := group_foo
BOARD_GROUP_FOO_SIZE := <size-in-bytes>
BOARD_GROUP_FOO_PARTITION_LIST := system vendor product

Cambios en SELinux

Los dispositivos del bloque de superpartición deben estar marcados con el atributo super_block_device_type . Por ejemplo, si el dispositivo ya tiene particiones system y vendor , desea usarlas como dispositivos de bloque para almacenar extensiones de particiones dinámicas, y sus enlaces simbólicos por nombre están marcados como system_block_device :

/dev/block/platform/soc/10000\.ufshc/by-name/system   u:object_r:system_block_device:s0
/dev/block/platform/soc/10000\.ufshc/by-name/vendor   u:object_r:system_block_device:s0

Luego, agregue la siguiente línea a device.te :

typeattribute system_block_device super_block_device_type;

Para otras configuraciones, consulte Implementación de particiones dinámicas en dispositivos nuevos .

Para obtener más información sobre las actualizaciones de actualización, consulte OTA para dispositivos A/B sin particiones dinámicas .

Imágenes de fábrica

Para un dispositivo que se inicia con soporte para particiones dinámicas, evite utilizar el arranque rápido en el espacio de usuario para actualizar las imágenes de fábrica, ya que el arranque en el espacio de usuario es más lento que otros métodos de actualización.

Para solucionar este problema, make dist ahora crea una imagen super.img adicional que se puede actualizar directamente a la superpartición. Empaqueta automáticamente el contenido de las particiones lógicas, lo que significa que contiene system.img , vendor.img , etc., además de los metadatos de la super . Esta imagen se puede enviar directamente a la super sin necesidad de herramientas adicionales ni de usar fastbootd. Después de la compilación, super.img se coloca en ${ANDROID_PRODUCT_OUT} .

Para dispositivos A/B que se inician con particiones dinámicas, super.img contiene imágenes en la ranura A. Después de mostrar la superimagen directamente, marque la ranura A como de arranque antes de reiniciar el dispositivo.

Para dispositivos modernizados, make dist crea un conjunto de imágenes super_*.img que se pueden actualizar directamente a las particiones físicas correspondientes. Por ejemplo, make dist compilaciones super_system.img y super_vendor.img cuando BOARD_SUPER_PARTITION_BLOCK_DEVICES sea el proveedor del sistema. Estas imágenes se colocan en la carpeta OTA en target_files.zip .

Ajuste del dispositivo de almacenamiento del mapeador de dispositivos

La partición dinámica admite una serie de objetos asignadores de dispositivos no deterministas. Es posible que no todas se creen instancias como se esperaba, por lo que debe realizar un seguimiento de todos los montajes y actualizar las propiedades de Android de todas las particiones asociadas con sus dispositivos de almacenamiento subyacentes.

Un mecanismo dentro de init rastrea los montajes y actualiza asincrónicamente las propiedades de Android. No se garantiza que la cantidad de tiempo que esto lleva esté dentro de un período específico, por lo que debe proporcionar suficiente tiempo para que reaccionen todos on property . Las propiedades son dev.mnt.blk. <partition> donde <partition> es root , system , data o vendor , por ejemplo. Cada propiedad está asociada con el nombre del dispositivo de almacenamiento base, como se muestra en estos ejemplos:

taimen:/ % getprop | grep dev.mnt.blk
[dev.mnt.blk.data]: [sda]
[dev.mnt.blk.firmware]: [sde]
[dev.mnt.blk.metadata]: [sde]
[dev.mnt.blk.persist]: [sda]
[dev.mnt.blk.root]: [dm-0]
[dev.mnt.blk.vendor]: [dm-1]

blueline:/ $ getprop | grep dev.mnt.blk
[dev.mnt.blk.data]: [dm-4]
[dev.mnt.blk.metadata]: [sda]
[dev.mnt.blk.mnt.scratch]: [sda]
[dev.mnt.blk.mnt.vendor.persist]: [sdf]
[dev.mnt.blk.product]: [dm-2]
[dev.mnt.blk.root]: [dm-0]
[dev.mnt.blk.system_ext]: [dm-3]
[dev.mnt.blk.vendor]: [dm-1]
[dev.mnt.blk.vendor.firmware_mnt]: [sda]

El lenguaje init.rc permite expandir las propiedades de Android como parte de las reglas, y la plataforma puede ajustar los dispositivos de almacenamiento según sea necesario con comandos como estos:

write /sys/block/${dev.mnt.blk.root}/queue/read_ahead_kb 128
write /sys/block/${dev.mnt.blk.data}/queue/read_ahead_kb 128

Una vez que comienza el procesamiento del comando en la segunda etapa init , el epoll loop se activa y los valores comienzan a actualizarse. Sin embargo, debido a que los desencadenadores de propiedades no están activos hasta late- init , no se pueden usar en las etapas de arranque iniciales para manejar root , system o vendor . Puede esperar que el valor predeterminado read_ahead_kb del kernel sea suficiente hasta que los scripts init.rc puedan anularse en early-fs (cuando se inician varios demonios e instalaciones). Por lo tanto, Google recomienda utilizar la función on property , junto con una propiedad controlada por init.rc como sys.read_ahead_kb , para controlar el tiempo de las operaciones y evitar condiciones de carrera, como en estos ejemplos:

on property:dev.mnt.blk.root=* && property:sys.read_ahead_kb=*
    write /sys/block/${dev.mnt.blk.root}/queue/read_ahead_kb ${sys.read_ahead_kb:-2048}

on property:dev.mnt.blk.system=* && property:sys.read_ahead_kb=*
    write /sys/block/${dev.mnt.blk.system}/queue/read_ahead_kb ${sys.read_ahead_kb:-2048}

on property:dev.mnt.blk.vendor=* && property:sys.read_ahead_kb=*
    write /sys/block/${dev.mnt.blk.vendor}/queue/read_ahead_kb ${sys.read_ahead_kb:-2048}

on property:dev.mnt.blk.product=* && property:sys.read_ahead_kb=*
    write /sys/block/${dev.mnt.blk.system_ext}/queue/read_ahead_kb ${sys.read_ahead_kb:-2048}

on property:dev.mnt.blk.oem=* && property:sys.read_ahead_kb=*
    write /sys/block/${dev.mnt.blk.oem}/queue/read_ahead_kb ${sys.read_ahead_kb:-2048}

on property:dev.mnt.blk.data=* && property:sys.read_ahead_kb=*
    write /sys/block/${dev.mnt.blk.data}/queue/read_ahead_kb ${sys.read_ahead_kb:-2048}

on early-fs:
    setprop sys.read_ahead_kb ${ro.read_ahead_kb.boot:-2048}

on property:sys.boot_completed=1
   setprop sys.read_ahead_kb ${ro.read_ahead_kb.bootcomplete:-128}