O Google tem o compromisso de promover a igualdade racial para as comunidades negras. Saiba como.
Esta página foi traduzida pela API Cloud Translation.
Switch to English

Implementando Partições Dinâmicas

O particionamento dinâmico é implementado usando o módulo dm-linear device-mapper no kernel do Linux. O super partição contém metadados listando os nomes e intervalos de blocos de cada partição dinâmica dentro super . Durante a init primeiro estágio, esses metadados são analisados ​​e validados, e dispositivos de bloco virtual são criados para representar cada partição dinâmica.

Ao aplicar um OTA, as partições dinâmicas são criadas, redimensionadas ou excluídas automaticamente conforme necessário. Para dispositivos A / B, há duas cópias dos metadados e as alterações são aplicadas apenas à cópia que representa o slot de destino.

Como as partições dinâmicas são implementadas no espaço do usuário, as partições necessárias para o bootloader não podem ser tornadas dinâmicas. Por exemplo, boot , dtbo e vbmeta são lidos pelo bootloader e, portanto, devem permanecer como partições físicas.

Cada partição dinâmica pode pertencer a um grupo de atualização . Esses grupos limitam o espaço máximo que as partições desse grupo podem consumir. Por exemplo, system e vendor podem pertencer a um grupo que restringe o tamanho total do system e vendor .

Implementando partições dinâmicas em novos dispositivos

Esta seção detalha como implementar partições dinâmicas em novos dispositivos lançando com Android 10 e superior. Para atualizar dispositivos existentes, consulte Atualizando dispositivos Android .

Alterações de particionamento

Para dispositivos lançando com Android 10, crie uma partição chamada super . Os super alças de partições A / ranhuras B internamente, de modo A / B dispositivos não precisam separado super_a e super_b partições. Todas as partições AOSP somente leitura que não são usadas pelo bootloader devem ser dinâmicas e devem ser removidas da GUID Partition Table (GPT). As partições específicas do fornecedor não precisam ser dinâmicas e podem ser colocadas no GPT.

Para estimar o tamanho de super , adicione os tamanhos das partições que estão sendo excluídas do GPT. Para dispositivos A / B, isso deve incluir o tamanho de ambos os slots. A Figura 1 mostra um exemplo de tabela de partição antes e depois da conversão para partições dinâmicas.

Layout da tabela de partição
Figura 1. Novo layout de tabela de partição física ao converter para partições dinâmicas

As partições dinâmicas com suporte são:

  • Sistema
  • Fornecedor
  • produtos
  • System Ext
  • ODM

Para dispositivos que androidboot.super_partition com Android 10, a opção de linha de comando do kernel androidboot.super_partition deve estar vazia para que o comando sysprop ro.boot.super_partition esteja vazio.

Alinhamento de partição

O módulo mapeador de dispositivos pode operar com menos eficiência se a super não estiver alinhada corretamente. A super partição tem de ser alinhada com o tamanho mínimo de pedido de I / O, tal como determinado pela camada de bloqueio. Por defeito, o sistema de construção (via lpmake , que gera o super imagem partição), que assume uma MIB alinhamento 1 é suficiente para cada partição dinâmica. No entanto, os fornecedores devem garantir que o super partição está alinhada corretamente.

Você pode determinar o tamanho mínimo da solicitação de um dispositivo de bloco inspecionando o sysfs . Por exemplo:

# 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

Você pode verificar a super alinhamento de partição de forma semelhante:

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

O deslocamento do alinhamento DEVE ser 0.

Mudanças na configuração do dispositivo

Para habilitar o particionamento dinâmico, adicione o seguinte sinalizador em device.mk :

PRODUCT_USE_DYNAMIC_PARTITIONS := true

Mudanças na configuração da placa

Você é obrigado a definir o tamanho do super partição:

BOARD_SUPER_PARTITION_SIZE := <size-in-bytes>

Em dispositivos A / B, o sistema de compilação gera um erro se o tamanho total de imagens de partições dinâmicas é mais do que metade do super tamanho da partição.

Você pode configurar a lista de partições dinâmicas da seguinte maneira. Para dispositivos que usam grupos de atualização, liste os grupos na variável BOARD_SUPER_PARTITION_GROUPS . Cada nome de grupo tem uma BOARD_ group _SIZE e BOARD_ group _PARTITION_LIST variável BOARD_ group _PARTITION_LIST . Para dispositivos A / B, o tamanho máximo de um grupo deve abranger apenas um slot, pois os nomes dos grupos têm sufixos de slot internamente.

Aqui está um exemplo de dispositivo que coloca todas as partições em um grupo chamado 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

Aqui está um exemplo de dispositivo que coloca serviços de sistema e produto em group_foo , e vendor , product e odm em 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 lançamento Virtual A / B, a soma dos tamanhos máximos de todos os grupos deve ser no máximo:
    BOARD_SUPER_PARTITION_SIZE - sobrecarga
    Consulte Implementando A / B Virtual .
  • Para dispositivos de lançamento A / B, a soma dos tamanhos máximos de todos os grupos deve ser:
    BOARD_SUPER_PARTITION_SIZE / 2 - sobrecarga
  • Para dispositivos não A / B e dispositivos A / B adaptados, a soma dos tamanhos máximos de todos os grupos deve ser:
    BOARD_SUPER_PARTITION_SIZE - sobrecarga
  • No momento da construção, a soma dos tamanhos das imagens de cada partição em um grupo de atualização não deve exceder o tamanho máximo do grupo.
  • A sobrecarga é necessária na computação para contabilizar metadados, alinhamentos e assim por diante. Uma sobrecarga razoável é de 4 MiB, mas você pode escolher uma sobrecarga maior conforme necessário para o dispositivo.

Dimensionando partições dinâmicas

Antes das partições dinâmicas, os tamanhos das partições eram superalocados para garantir que tivessem espaço suficiente para atualizações futuras. O tamanho real foi considerado como está e a maioria das partições somente leitura tinha alguma quantidade de espaço livre em seu sistema de arquivos. Em partições dinâmicas, esse espaço livre é inutilizável e pode ser usado para aumentar partições durante uma OTA. É fundamental garantir que as partições não estejam desperdiçando espaço e sejam alocadas com o tamanho mínimo possível.

Para imagens ext4 somente leitura, o sistema de compilação aloca automaticamente o tamanho mínimo se nenhum tamanho de partição codificado for especificado. O sistema de construção se ajusta à imagem para que o sistema de arquivos tenha o mínimo de espaço não utilizado possível. Isso garante que o dispositivo não desperdice espaço que pode ser usado para OTAs.

Além disso, as imagens ext4 podem ser compactadas ainda mais, permitindo a desduplicação em nível de bloco. Para habilitar isso, use a seguinte configuração:

BOARD_EXT4_SHARE_DUP_BLOCKS := true

Se a alocação automática de um tamanho mínimo de partição for indesejável, há duas maneiras de controlar o tamanho da partição. Você pode especificar uma quantidade mínima de espaço livre com a BOARD_ partition IMAGE_PARTITION_RESERVED_SIZE ou pode especificar a BOARD_ partition IMAGE_PARTITION_SIZE para forçar as partições dinâmicas a um tamanho específico. Nenhum deles é recomendado, a menos que necessário.

Por exemplo:

BOARD_PRODUCTIMAGE_PARTITION_RESERVED_SIZE := 52428800

Isso força o sistema de arquivos em product.img a ter 50 MiB de espaço não utilizado.

Mudanças de sistema como raiz

Os dispositivos que iniciam com o Android 10 não devem usar o sistema como raiz.

Dispositivos com partições dinâmicas (sejam eles iniciados ou adaptados a partições dinâmicas) não devem usar o sistema como root. O kernel do Linux não pode interpretar a super partição e, portanto, não pode montar o próprio system . system agora é montado pelo init primeiro estágio, que reside no ramdisk.

Não defina BOARD_BUILD_SYSTEM_ROOT_IMAGE . No Android 10, o sinalizador BOARD_BUILD_SYSTEM_ROOT_IMAGE é usado apenas para diferenciar se o sistema é montado pelo kernel ou pelo init primeiro estágio no ramdisk.

Definir BOARD_BUILD_SYSTEM_ROOT_IMAGE como true causa um erro de compilação quando PRODUCT_USES_DYNAMIC_PARTITIONS também é true .

Quando BOARD_USES_RECOVERY_AS_BOOT é definido como verdadeiro, a imagem de recuperação é construída como boot.img, contendo o ramdisk da recuperação. Anteriormente, o bootloader usava o parâmetro de linha de comando do kernel skip_initramfs para decidir em qual modo inicializar. Para dispositivos Android 10, o bootloader NÃO DEVE passar skip_initramfs para a linha de comando do kernel. Em vez disso, o bootloader deve passar androidboot.force_normal_boot=1 para pular a recuperação e inicializar o Android normal.

Alterações de configuração AVB

Ao usar Android Verified Boot 2.0 , se o dispositivo não estiver usando descritores de partição em cadeia , nenhuma alteração será necessária. Se estiver usando partições encadeadas, no entanto, e uma das partições verificadas for dinâmica, as alterações serão necessárias.

Aqui está um exemplo de configuração para um dispositivo que encadeia vbmeta para as partições do system e do 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

Com esta configuração, o bootloader espera encontrar um rodapé vbmeta no final das partições do system e do vendor . Como essas partições não são mais visíveis para o carregador de boot (elas residem em super ), duas mudanças são necessárias.

  • Adicionar vbmeta_system e vbmeta_vendor partições a tabela de partição do dispositivo. Para dispositivos A / B, adicione vbmeta_system_a , vbmeta_system_b , vbmeta_vendor_a e vbmeta_vendor_b . Se adicionar uma ou mais dessas partições, elas devem ter o mesmo tamanho que a partição vbmeta .
  • Renomeie os sinalizadores de configuração adicionando VBMETA_ e especifique para quais partições o encadeamento se estende:
    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
    

Um dispositivo pode estar usando uma, ambas ou nenhuma dessas partições. As alterações são necessárias apenas ao encadear a uma partição lógica.

Alterações do carregador de inicialização AVB

Se o carregador de inicialização incorporou libavb , inclua os seguintes patches:

Se estiver usando partições encadeadas, inclua um patch adicional:

  • 49936b4c0109411fdd38bd4ba3a32a01c40439a9 - "libavb: suporta blobs vbmeta no início da partição."

Mudanças na linha de comando do kernel

Um novo parâmetro, androidboot.boot_devices , deve ser adicionado à linha de comando do kernel. Isso é usado pelo init para habilitar os links simbólicos /dev/block/by-name . Deve ser o componente do caminho do dispositivo para o link simbólico subjacente por nome criado por ueventd , ou seja, /dev/block/platform/ device-path /by-name/ partition-name .

Por exemplo, se o link simbólico da /dev/block/platform/ soc/100000.ufshc /by-name/super por nome for /dev/block/platform/ soc/100000.ufshc /by-name/super , você pode adicionar o parâmetro de linha de comando no arquivo BoardConfig.mk da seguinte maneira:

BOARD_KERNEL_CMDLINE += androidboot.boot_devices=soc/100000.ufshc

mudanças fstab

A árvore de dispositivos e as sobreposições da árvore de dispositivos não devem conter entradas fstab. Use um arquivo fstab que fará parte do ramdisk.

As alterações devem ser feitas no arquivo fstab para partições lógicas:

  • O campo de sinalizadores fs_mgr deve incluir o sinalizador logical e o sinalizador first_stage_mount , introduzido no Android 10, que indica que uma partição deve ser montada no primeiro estágio.
  • Uma partição pode especificar o avb= vbmeta partition name como um sinalizador fs_mgr e, em seguida, a partição vbmeta especificada é inicializada pelo init primeiro estágio antes de tentar montar qualquer dispositivo.
  • O campo dev deve ser o nome da partição.

As seguintes entradas de fstab definem sistema, fornecedor e produto como partições lógicas seguindo as regras acima.

#<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 o arquivo fstab para o ramdisk do primeiro estágio.

Mudanças SELinux

O dispositivo de bloco de super_block_device deve ser marcado com o rótulo super_block_device . Por exemplo, se o link simbólico da /dev/block/platform/ soc/100000.ufshc /by-name/super for /dev/block/platform/ soc/100000.ufshc /by-name/super , adicione a seguinte linha a file_contexts :

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

fastbootd

O bootloader (ou qualquer ferramenta de flashing que não seja do espaço do usuário) não entende partições dinâmicas, então não pode atualizá-las. Para resolver isso, os dispositivos devem usar uma implementação de espaço do usuário do protocolo fastboot, chamada fastbootd.

Para obter mais informações sobre como implementar o fastbootd, consulte Movendo o Fastboot para o espaço do usuário .

adb remount

Para desenvolvedores que usam builds eng ou userdebug, adb remount é extremamente útil para iteração rápida. As partições dinâmicas representam um problema para a adb remount porque não há mais espaço livre em cada sistema de arquivos. Para resolver isso, os dispositivos podem habilitar overlayfs. Contanto que haja espaço livre na adb remount , o adb remount cria automaticamente uma partição dinâmica temporária e usa overlayfs para gravações. A partição temporária é chamada de scratch , portanto, não use esse nome para outras partições.

Para obter mais informações sobre como habilitar overlayfs, consulte o README de overlayfs no AOSP.

Atualizando dispositivos Android

Se você atualizar um dispositivo para Android 10 e quiser incluir suporte a partições dinâmicas no OTA, não será necessário alterar a tabela de partição integrada. É necessária alguma configuração extra.

Mudanças na configuração do dispositivo

Para adaptar o particionamento dinâmico, adicione os seguintes sinalizadores em device.mk :

PRODUCT_USE_DYNAMIC_PARTITIONS := true
PRODUCT_RETROFIT_DYNAMIC_PARTITIONS := true

Mudanças na configuração da placa

Você deve definir as seguintes variáveis ​​da placa:

  • Defina BOARD_SUPER_PARTITION_BLOCK_DEVICES para a lista de dispositivos de bloco usados ​​para armazenar extensões de partições dinâmicas. Esta é a lista de nomes de partições físicas existentes no dispositivo.
  • Defina BOARD_SUPER_PARTITION_ partition _DEVICE_SIZE para os tamanhos de cada dispositivo de bloco em BOARD_SUPER_PARTITION_BLOCK_DEVICES , respectivamente. Esta é a lista de tamanhos de partições físicas existentes no dispositivo. Normalmente é a BOARD_ partition IMAGE_PARTITION_SIZE nas configurações de placa existentes.
  • BOARD_ partition IMAGE_PARTITION_SIZE existente de BOARD_ partition IMAGE_PARTITION_SIZE para todas as partições em BOARD_SUPER_PARTITION_BLOCK_DEVICES .
  • Defina BOARD_SUPER_PARTITION_SIZE para a soma de BOARD_SUPER_PARTITION_ partition _DEVICE_SIZE .
  • Defina BOARD_SUPER_PARTITION_METADATA_DEVICE para o dispositivo de bloco onde os metadados da partição dinâmica estão armazenados. Deve ser um dos BOARD_SUPER_PARTITION_BLOCK_DEVICES . Normalmente, isso é definido como system .
  • Defina BOARD_SUPER_PARTITION_GROUPS , BOARD_ group _SIZE e BOARD_ group _PARTITION_LIST , respectivamente. Consulte as alterações de configuração da placa em novos dispositivos para obter detalhes.

Por exemplo, se o dispositivo já tiver partições de sistema e de fornecedor e você quiser convertê-las em partições dinâmicas e adicionar uma nova partição de produto durante a atualização, defina esta configuração 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

Mudanças SELinux

Os dispositivos de bloco de super_block_device_type devem ser marcados com o atributo super_block_device_type . Por exemplo, se o dispositivo já tiver partições de system e de vendor , você deseja usá-los como dispositivos de bloco para armazenar extensões de partições dinâmicas e seus links simbólicos por nome são 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

Em seguida, adicione a seguinte linha a device.te :

typeattribute system_block_device super_block_device_type;

Para outras configurações, consulte Implementando partições dinâmicas em novos dispositivos .

Para obter mais informações sobre atualizações de retrofit, consulte OTA para dispositivos A / B sem partições dinâmicas .

Imagens de fábrica

Para a inicialização de um dispositivo com suporte a partições dinâmicas, evite usar o fastboot do espaço do usuário para imagens de fábrica do flash, pois a inicialização do espaço do usuário é mais lenta do que outros métodos de flash.

Para resolver isso, make dist agora cria uma imagem super.img adicional que pode ser super.img diretamente para a super.img . É feixes automaticamente o conteúdo de partições lógicas, o que significa que contém system.img , vendor.img , e assim por diante, além do super metadados partição. Esta imagem pode ser brilhou diretamente para o super partição sem qualquer ferramenta adicional ou usar fastbootd. Após a construção, super.img é colocado em ${ANDROID_PRODUCT_OUT} .

Para dispositivos A / B que são iniciados com partições dinâmicas, super.img contém imagens no slot A. Depois de fazer o flash da superimagem diretamente, marque o slot A como inicializável antes de reiniciar o dispositivo.

Para dispositivos de retrofit, make dist cria um conjunto de imagens super_*.img que podem ser transferidas diretamente para as partições físicas correspondentes. Por exemplo, make dist builds super_system.img e super_vendor.img quando BOARD_SUPER_PARTITION_BLOCK_DEVICES for o fornecedor do sistema. Essas imagens são colocadas na pasta OTA em target_files.zip .

Ajuste do dispositivo de armazenamento do mapeador de dispositivos

O particionamento dinâmico acomoda vários objetos mapeadores de dispositivos não determinísticos. Eles podem não se instanciar conforme o esperado, portanto, você deve rastrear todas as montagens e atualizar as propriedades do Android de todas as partições associadas com seus dispositivos de armazenamento subjacentes.

Um mecanismo dentro do init rastreia as montagens e atualiza de forma assíncrona as propriedades do Android. A quantidade de tempo que isso leva não é garantida dentro de um período específico, então você deve fornecer tempo suficiente para que todos on property acionadores de on property reajam. As propriedades são dev.mnt.blk. <partition> onde <partition> é root , system , data ou vendor , por exemplo. Cada propriedade está associada ao nome do dispositivo de armazenamento básico, conforme mostrado nestes exemplos:

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]

A linguagem init.rc permite que as propriedades do Android sejam expandidas como parte das regras, e os dispositivos de armazenamento podem ser ajustados pela plataforma conforme necessário com comandos como estes:

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

Depois que o processamento do comando começa no init segundo estágio, o epoll loop torna-se ativo e os valores começam a ser atualizados. No entanto, como os disparadores de propriedade não estão ativos até a init tardia, eles não podem ser usados ​​nos estágios iniciais de inicialização para lidar com o root , o system ou o vendor . Você pode esperar que o read_ahead_kb padrão do kernel seja suficiente até que os scripts init.rc possam sobrescrever no early-fs (quando vários daemons e recursos init.rc iniciados). Portanto, o Google recomenda que você use o on property característica, juntamente com um init.rc propriedade -controlado como sys.read_ahead_kb , para lidar com o tempo as operações e evitar condições de corrida, como nestes exemplos:

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}