Cómo implementar particiones dinámicas

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

Cuando se aplica una OTA, las particiones dinámicas se crean automáticamente, se les cambia el tamaño o se borran según sea necesario. En el caso de 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.

Dado que las particiones dinámicas se implementan en el espacio de usuario, las particiones que necesita el bootloader no pueden ser dinámicas. Por ejemplo, el bootloader 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 actualizaciones. 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 de system y vendor.

Implementa particiones dinámicas en dispositivos nuevos

En esta sección, se explica cómo implementar particiones dinámicas en dispositivos nuevos que se lancen con Android 10 y versiones posteriores. Para actualizar dispositivos existentes, consulta Cómo actualizar dispositivos Android.

Cambios en las particiones

Para los dispositivos que se lanzan con Android 10, crea una partición llamada super. La partición super controla los ranuras A/B de forma interna, por lo que los dispositivos A/B no necesitan particiones super_a y super_b separadas. Todas las particiones del AOSP de solo lectura que no use el bootloader deben ser dinámicas y se deben quitar de la tabla de particiones GUID (GPT). Las particiones específicas del proveedor no tienen que ser dinámicas y se pueden colocar en el GPT.

Para estimar el tamaño de super, agrega los tamaños de las particiones que se borrarán del GPT. En el caso de los dispositivos A/B, debe incluir el tamaño de ambas ranuras. En la Figura 1, se muestra un ejemplo de tabla de particiones antes y después de convertir a particiones dinámicas.

Diseño de la tabla de particiones
Figura 1: Nuevo diseño de tabla de particiones físicas cuando se convierten en particiones dinámicas.

Las particiones dinámicas admitidas son las siguientes:

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

En el caso de los dispositivos que se inician con Android 10, la opción de línea de comandos 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

Es posible que el módulo device-mapper funcione de manera menos eficiente si la partición super no está alineada correctamente. La partición super DEBE alinearse con el tamaño mínimo de la solicitud de E/S según lo determine la capa de bloques. De forma predeterminada, el sistema de compilación (a través de lpmake, que genera la imagen de partición super) supone que una alineación de 1 MiB es suficiente para cada partición dinámica. Sin embargo, los proveedores deben asegurarse de que la partición super esté alineada de forma correcta.

Para determinar el tamaño de solicitud mínimo de un dispositivo de almacenamiento en bloques, puedes inspeccionar 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

Puedes verificar la alineación de la partición super de una 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 el particionamiento dinámico, agrega la siguiente marca en device.mk:

PRODUCT_USE_DYNAMIC_PARTITIONS := true

Cambios en la configuración de la pizarra

Debes establecer el tamaño de la partición super:

BOARD_SUPER_PARTITION_SIZE := <size-in-bytes>

En los dispositivos A/B, el sistema de compilación muestra 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 partición super.

Puedes configurar la lista de particiones dinámicas de la siguiente manera. Para los dispositivos que usan grupos de actualización, enumera los grupos en la variable BOARD_SUPER_PARTITION_GROUPS. Cada nombre de grupo tiene una variable BOARD_group_SIZE y BOARD_group_PARTITION_LIST. En el caso de los dispositivos A/B, el tamaño máximo de un grupo debe cubrir solo un espacio, ya que los nombres de los grupos tienen un sufijo de espacio de forma interna.

Este es un ejemplo de dispositivo 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

Este es un ejemplo de dispositivo 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 los dispositivos de lanzamiento de A/B virtual, la suma de los tamaños máximos de todos los grupos debe ser como máximo:
    BOARD_SUPER_PARTITION_SIZE - sobrecarga
    Consulta Implementación de A/B virtual.
  • Para los dispositivos de lanzamiento A/B, la suma de los tamaños máximos de todos los grupos debe ser:
    BOARD_SUPER_PARTITION_SIZE / 2 - sobrecarga
  • Para dispositivos que no son A/B y dispositivos A/B retroadaptados, la suma de los tamaños máximos de todos los grupos debe ser:
    BOARD_SUPER_PARTITION_SIZE - sobrecarga
  • En el tiempo de 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 requiere sobrecarga en el procesamiento para tener en cuenta los metadatos, las alineaciones, etcétera. Una sobrecarga razonable es de 4 MiB, pero puedes elegir una sobrecarga mayor según las necesidades del dispositivo.

Cómo cambiar el tamaño de las particiones dinámicas

Antes de las particiones dinámicas, los tamaños de las particiones se asignaban en exceso a fin de garantizar que hubieran espacio suficiente para actualizaciones futuras. El tamaño real se tomó tal como estaba, y la mayoría de las particiones de solo lectura tenían cierta cantidad de espacio libre en su sistema de archivos. En las particiones dinámicas, ese espacio libre es inutilizable y se puede usar para aumentar las particiones durante una actualización OTA. Es fundamental asegurarse de que las particiones no desperdicien espacio y se asignen al tamaño mínimo posible.

Para las imágenes ext4 de solo lectura, el sistema de compilación asigna automáticamente el tamaño mínimo si no se especifica un tamaño de partición codificado. El sistema de compilación se ajusta a la imagen para que el sistema de archivos tenga el menor espacio sin usar posible. Esto garantiza que el dispositivo no desperdicie espacio que se puede usar para las actualizaciones OTA.

Además, las imágenes ext4 se pueden comprimir aún más si se habilita la anulación de duplicación a nivel de bloque. Para habilitar esta opción, usa la siguiente configuración:

BOARD_EXT4_SHARE_DUP_BLOCKS := true

Si no deseas la asignación automática de un tamaño mínimo de partición, hay dos maneras de controlar el tamaño de la partición. Puedes especificar una cantidad mínima de espacio libre con BOARD_partitionIMAGE_PARTITION_RESERVED_SIZE, o bien puedes especificar BOARD_partitionIMAGE_PARTITION_SIZE para forzar particiones dinámicas a un tamaño específico. No se recomienda ninguna de estas opciones, 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 sin usar.

Cambios de System-as-root

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

Los dispositivos con particiones dinámicas (ya sea que se inicien con particiones dinámicas o se adapten a ellas) no deben usar system-as-root. El kernel de Linux no puede interpretar la partición super y, por lo tanto, no puede activar system. system ahora está activado por init de primera etapa, que reside en el disco RAM.

No configures BOARD_BUILD_SYSTEM_ROOT_IMAGE. En Android 10, la marca BOARD_BUILD_SYSTEM_ROOT_IMAGE solo se usa para diferenciar si el sistema está activado por el kernel o por el init de primera etapa en el disco RAM.

Configurar BOARD_BUILD_SYSTEM_ROOT_IMAGE como true provoca un error de compilación cuando PRODUCT_USE_DYNAMIC_PARTITIONS también es true.

Cuando BOARD_USES_RECOVERY_AS_BOOT se establece como verdadero, la imagen de recuperación se compila como boot.img, que contiene el ramdisk de la recuperación. Anteriormente, el bootloader usaba el parámetro de línea de comandos del kernel skip_initramfs para decidir en qué modo iniciar. En el caso de los dispositivos con Android 10, el bootloader NO DEBE pasar skip_initramfs a la línea de comandos del kernel. En su lugar, el bootloader debe pasar androidboot.force_normal_boot=1 para omitir la recuperación e iniciar Android con normalidad. Los dispositivos que se lanzan con Android 12 o versiones posteriores deben usar bootconfig para pasar androidboot.force_normal_boot=1.

Cambios en la configuración de AVB

Cuando se usa el inicio verificado de Android 2.0, si el dispositivo no usa descriptores de particiones encadenadas, no es necesario ningún cambio. Sin embargo, si se usan particiones en cadena y una de las particiones verificadas es dinámica, se deben realizar cambios.

A continuación, te mostramos una configuración de ejemplo para un dispositivo que encadena vbmeta para las particiones system y 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 bootloader espera encontrar un pie de página de vbmeta al final de las particiones system y vendor. Debido a que estas particiones ya no son visibles para el bootloader (residen en super), se necesitan dos cambios.

  • Agrega las particiones vbmeta_system y vbmeta_vendor a la tabla de particiones del dispositivo. Para dispositivos A/B, agrega vbmeta_system_a, vbmeta_system_b, vbmeta_vendor_a y vbmeta_vendor_b. Si agregas una o más de estas particiones, deben tener el mismo tamaño que la partición vbmeta.
  • Para cambiar el nombre de las marcas de configuración, agrega VBMETA_ y especifica 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 usar una, ambas o ninguna de estas particiones. Los cambios solo son necesarios cuando se encadenan a una partición lógica.

Cambios en el bootloader de AVB

Si el bootloader tiene incorporada libavb, incluye los siguientes parches:

Si usas particiones encadenadas, incluye un parche adicional:

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

Cambios en la línea de comandos del kernel

Se debe agregar un parámetro nuevo, androidboot.boot_devices, a la línea de comandos del kernel. init lo usa para habilitar los symlinks de /dev/block/by-name. Debe ser el componente de ruta de acceso del dispositivo al symlink subyacente por nombre que crea ueventd, es decir, /dev/block/platform/device-path/by-name/partition-name. Los dispositivos que se lanzan con Android 12 o versiones posteriores deben usar bootconfig para pasar androidboot.boot_devices a init.

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

BOARD_KERNEL_CMDLINE += androidboot.boot_devices=soc/100000.ufshc
Puedes 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 de fstab. Usa un archivo fstab que forme parte del ramdisk.

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

  • El campo de marcas fs_mgr debe incluir la marca logical y la marca first_stage_mount, que se introdujo en Android 10, que indica que se debe activar una partición en la primera etapa.
  • Una partición puede especificar avb=vbmeta partition name como una marca fs_mgr y, luego, la partición vbmeta especificada se inicializa en la primera etapa de init antes de intentar activar cualquier dispositivo.
  • El campo dev debe ser el nombre de la partición.

Las siguientes entradas fstab establecen el sistema, el proveedor y el producto como particiones lógicas según 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

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

Cambios de SELinux

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

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

fastbootd

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

Para obtener más información sobre cómo implementar fastbootd, consulta Cómo mover Fastboot al espacio del usuario.

adb remount

Para los desarrolladores que usan compilaciones eng o userdebug, adb remount es muy útil para la iteración rápida. Las particiones dinámicas representan un problema para adb remount porque ya no hay espacio libre dentro de cada sistema de archivos. Para solucionar este problema, los dispositivos pueden habilitar overlayfs. Siempre que haya espacio libre dentro de la superpartición, adb remount crea automáticamente una partición dinámica temporal y usa overlayfs para las operaciones de escritura. La partición temporal se llama scratch, por lo que no debes usar este nombre para otras particiones.

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

Cómo actualizar dispositivos Android

Si actualizas un dispositivo a Android 10 y quieres incluir compatibilidad con particiones dinámicas en la actualización inalámbrica, no es necesario que cambies la tabla de particiones integrada. Se requiere configuración adicional.

Cambios en la configuración del dispositivo

Para adaptar el particionamiento dinámico, agrega las siguientes marcas en device.mk:

PRODUCT_USE_DYNAMIC_PARTITIONS := true
PRODUCT_RETROFIT_DYNAMIC_PARTITIONS := true

Cambios en la configuración de la pizarra

Debes configurar las siguientes variables de la placa:

  • Establece BOARD_SUPER_PARTITION_BLOCK_DEVICES en la lista de dispositivos de almacenamiento en bloque que se usan para almacenar extensiones de particiones dinámicas. Esta es la lista de nombres de las particiones físicas existentes en el dispositivo.
  • Establece BOARD_SUPER_PARTITION_partition_DEVICE_SIZE en los tamaños de cada dispositivo de almacenamiento en bloque en BOARD_SUPER_PARTITION_BLOCK_DEVICES, respectivamente. Esta es la lista de los tamaños de las particiones físicas existentes en el dispositivo. Por lo general, es BOARD_partitionIMAGE_PARTITION_SIZE en los parámetros de configuración de la placa existentes.
  • Anula el BOARD_partitionIMAGE_PARTITION_SIZE existente para todas las particiones en BOARD_SUPER_PARTITION_BLOCK_DEVICES.
  • Establece BOARD_SUPER_PARTITION_SIZE en la suma de BOARD_SUPER_PARTITION_partition_DEVICE_SIZE.
  • Establece BOARD_SUPER_PARTITION_METADATA_DEVICE en el dispositivo de almacenamiento en bloques en el que se almacenan los metadatos de la partición dinámica. Debe ser uno de BOARD_SUPER_PARTITION_BLOCK_DEVICES. Por lo general, está configurado en system.
  • Establece BOARD_SUPER_PARTITION_GROUPS, BOARD_group_SIZE y BOARD_group_PARTITION_LIST, respectivamente. Consulta Cambios en la configuración de la placa en dispositivos nuevos para obtener más información.

Por ejemplo, si el dispositivo ya tiene particiones del sistema y del proveedor, y deseas convertirlas en particiones dinámicas y agregar una partición de producto nueva durante la actualización, establece esta configuración de la 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 de SELinux

Los dispositivos de bloque de superpartición deben marcarse con el atributo super_block_device_type. Por ejemplo, si el dispositivo ya tiene particiones system y vendor, quieres usarlas como dispositivos de almacenamiento en bloque para almacenar extensiones de particiones dinámicas, y sus symlinks 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, agrega la siguiente línea a device.te:

typeattribute system_block_device super_block_device_type;

Para otras configuraciones, consulta Cómo implementar particiones dinámicas en dispositivos nuevos.

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

Imágenes de fábrica

Para que un dispositivo se inicie con compatibilidad con particiones dinámicas, evita usar fastboot de espacio de usuario para escribir imágenes de fábrica, ya que el inicio en el espacio de usuario es más lento que otros métodos de escritura.

Para solucionar este problema, make dist ahora compila una imagen super.img adicional que se puede escribir directamente en la partición superior. Agrupa automáticamente el contenido de las particiones lógicas, lo que significa que contiene system.img, vendor.img, etcétera, además de los metadatos de la partición super. Esta imagen se puede escribir directamente en la memoria flash de la partición super sin ninguna herramienta adicional ni usar fastboot. Después de la compilación, super.img se coloca en ${ANDROID_PRODUCT_OUT}.

En el caso de los dispositivos A/B que se inician con particiones dinámicas, super.img contiene imágenes en el espacio A. Después de escribir directamente en la memoria flash la superimagen, marca la ranura A como que se puede iniciar antes de reiniciar el dispositivo.

En el caso de los dispositivos de reacondicionamiento, make dist compila un conjunto de imágenes super_*.img que se pueden escribir directamente en las particiones físicas correspondientes. Por ejemplo, make dist compila super_system.img y super_vendor.img cuando BOARD_SUPER_PARTITION_BLOCK_DEVICES es el proveedor del sistema. Estas imágenes se colocan en la carpeta OTA en target_files.zip.

Ajuste de dispositivo de almacenamiento del asignador de dispositivos

El particionamiento dinámico admite una serie de objetos de asignador de dispositivos no deterministas. Es posible que no se creen instancias de todos como se espera, por lo que debes hacer un seguimiento de todos los activaciones y actualizar las propiedades de Android de todas las particiones asociadas con sus dispositivos de almacenamiento subyacentes.

Un mecanismo dentro de init realiza un seguimiento de las activaciones y actualiza de forma asíncrona las propiedades de Android. No se garantiza que el tiempo que esto tome sea dentro de un período específico, por lo que debes proporcionar suficiente tiempo para que todos los activadores de on property reaccionen. 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 básico, 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 que las propiedades de Android se expandan como parte de las reglas, y la plataforma puede ajustar los dispositivos de almacenamiento según sea necesario con comandos como los siguientes:

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 se inicia el procesamiento de comandos en init de segunda etapa, epoll loop se activa y los valores comienzan a actualizarse. Sin embargo, debido a que los activadores de propiedades no están activos hasta finales de init, no se pueden usar en las etapas de inicio iniciales para controlar root, system o vendor. Es posible que el read_ahead_kb predeterminado del kernel sea suficiente hasta que las secuencias de comandos init.rc puedan anularse en early-fs (cuando se inician varios demonios y servicios). Por lo tanto, Google recomienda que uses 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}