O Android 12 oferece suporte ao FUSE de passagem, que minimiza
a sobrecarga do FUSE para alcançar um desempenho comparável ao acesso direto ao sistema de arquivos
inferior. A passagem do FUSE tem suporte nos kernels android12-5.4
,
android12-5.10
e android-mainline
(somente para teste). Isso significa
que o suporte a esse recurso depende do kernel usado pelo dispositivo e da
versão do Android em que o dispositivo está executando:
Os dispositivos que fizeram upgrade do Android 11 para o 12 não podem oferecer suporte à passagem do FUSE, já que os kernels desses dispositivos estão congelados e não podem ser movidos para um kernel que foi oficialmente atualizado com as mudanças de passagem do FUSE.
Os dispositivos lançados com o Android 12 podem oferecer suporte ao encaminhamento do FUSE ao usar um kernel oficial. Para esses dispositivos, o código do framework Android que implementa o passthrough FUSE é incorporado ao módulo principal MediaProvider, que é atualizado automaticamente. Dispositivos que não implementam o MediaProvider como um módulo principal (por exemplo, dispositivos Android Go) também podem acessar as mudanças do MediaProvider conforme são compartilhadas publicamente.
FUSE x SDCardFS
O sistema de arquivos no espaço do usuário (FUSE, na sigla em inglês) é um mecanismo que permite que as operações realizadas em um sistema de arquivos FUSE sejam terceirizadas pelo kernel (driver FUSE) para um programa de espaço do usuário (demônio FUSE), que implementa as operações. O Android 11 desativou o SDCardFS e tornou o FUSE a solução padrão para emulação de armazenamento. Como parte dessa mudança, o Android implementou o próprio daemon FUSE para interceptar acessos a arquivos, aplicar recursos extras de segurança e privacidade e manipular arquivos no momento da execução.
Embora o FUSE tenha um bom desempenho ao lidar com informações em cache, como páginas ou atributos, ele introduz regressões de desempenho ao acessar o armazenamento externo, que são especialmente visíveis em dispositivos de nível médio e baixo. Essas regressões são causadas por uma cadeia de componentes que cooperam na implementação do sistema de arquivos FUSE, bem como várias mudanças do espaço do kernel para o espaço do usuário nas comunicações entre o driver FUSE e o daemon FUSE (em comparação com o acesso direto ao sistema de arquivos inferior, que é mais simples e completamente implementado no kernel).
Para atenuar essas regressões, os apps podem usar
junção para
reduzir a cópia de dados e usar a API ContentProvider
para ter acesso direto a arquivos de sistemas de arquivos mais baixos. Mesmo com essas e outras
otimizações, as operações de leitura
e gravação podem ter uma largura de banda reduzida ao usar o FUSE em comparação
com o acesso direto ao sistema de arquivos
inferior, especialmente com operações de leitura
aleatórias, em que nenhum armazenamento em cache ou leitura antecipada pode ajudar. E os apps que acessam
diretamente o armazenamento pelo caminho /sdcard/
legados continuam apresentando
quedas de desempenho perceptíveis, especialmente ao realizar operações
intensivas de E/S.
Solicitações de espaço do usuário do SDcardFS
O uso do SDcardFS pode acelerar a emulação de armazenamento e as verificações de permissão do FUSE removendo a chamada de espaço do usuário do kernel. As solicitações de espaço do usuário seguem este caminho: espaço do usuário → VFS → sdcardfs → VFS → ext4 → cache/armazenamento da página.
Figura 1. Solicitações de espaço do usuário do SDcardFS
Solicitações do espaço do usuário do FUSE
O FUSE foi usado inicialmente para ativar a emulação de armazenamento e permitir que os apps usassem o armazenamento interno ou um cartão SD externo de forma transparente. O uso do FUSE introduz alguma sobrecarga porque cada solicitação do espaço do usuário segue o caminho: espaço do usuário → VFS → driver FUSE → daemon FUSE → VFS → ext4 → armazenamento/cache de página.
Figura 2. Solicitações do espaço do usuário do FUSE
Solicitações de passagem do FUSE
A maioria das permissões de acesso a arquivos é verificada no momento da abertura do arquivo, com outras verificações de permissões ocorrendo durante a leitura e gravação nesse arquivo. Em alguns casos, é possível saber no momento da abertura do arquivo que o app solicitante tem acesso total ao arquivo solicitado. Assim, o sistema não precisa continuar encaminhando as solicitações de leitura e gravação do driver FUSE para o daemon FUSE, já que isso apenas move dados de um lugar para outro.
Com o FUSE passthrough, o daemon FUSE que processa uma solicitação aberta pode notificar o driver FUSE de que a operação é permitida e que todas as solicitações de leitura e gravação posteriores podem ser encaminhadas diretamente para o sistema de arquivos de nível inferior. Isso evita a sobrecarga extra de esperar que o daemon FUSE do espaço do usuário responda às solicitações do driver FUSE.
Confira abaixo uma comparação entre as solicitações de FUSE e FUSE passthrough.
Figura 3. Solicitação FUSE x solicitação de passagem FUSE
Quando um app realiza um acesso ao sistema de arquivos FUSE, as seguintes operações ocorrem:
O driver FUSE processa e enfileira a solicitação, depois a apresenta ao demônio FUSE que processa esse sistema de arquivos FUSE por uma instância de conexão específica no arquivo
/dev/fuse
, que o demônio FUSE bloqueia para leitura.Quando o daemon do FUSE recebe uma solicitação para abrir um arquivo, ele decide se a passagem do FUSE deve estar disponível para esse arquivo específico. Se estiver disponível, o daemon:
Notifica o driver FUSE sobre essa solicitação.
Ativa o encaminhamento FUSE para o arquivo usando o ioctl
FUSE_DEV_IOC_PASSTHROUGH_OPEN
, que precisa ser executado no descritor de arquivo do/dev/fuse
aberto.
O ioctl recebe (como parâmetro) uma estrutura de dados que contém o seguinte:
Descriptor de arquivo do arquivo de sistema de arquivos inferior que é o destino do recurso de transferência.
Identificador exclusivo da solicitação FUSE que está sendo processada (precisa estar aberta ou criar e abrir).
Campos extras que podem ser deixados em branco e são destinados a implementações futuras.
Se o ioctl for bem-sucedido, o daemon do FUSE concluirá a solicitação aberta, o driver do FUSE processará a resposta do daemon do FUSE e uma referência ao arquivo do sistema de arquivos inferior será adicionada ao arquivo FUSE dentro do kernel. Quando um app solicita uma operação de leitura/gravação em um arquivo FUSE, o driver do FUSE verifica se a referência a um arquivo de sistema de arquivos anterior está disponível.
Se uma referência estiver disponível, o driver criará uma nova solicitação de sistema de arquivos virtual (VFS, na sigla em inglês) com os mesmos parâmetros direcionados ao arquivo de sistema de arquivos inferior.
Se uma referência não estiver disponível, o driver vai encaminhar a solicitação para o daemon FUSE.
As operações acima ocorrem para leitura/gravação e leitura-iter/gravação-iter em arquivos genéricos e operações de leitura/gravação em arquivos mapeados em memória. O encaminhamento FUSE para um arquivo específico existe até que ele seja fechado.
Implementar a visualização externa do FUSE
Para ativar o encaminhamento FUSE em dispositivos com o Android
12, adicione as linhas abaixo ao
arquivo $ANDROID_BUILD_TOP/device/…/device.mk
do dispositivo de destino.
# Use FUSE passthrough
PRODUCT_PRODUCT_PROPERTIES += \
persist.sys.fuse.passthrough.enable=true
Para desativar o encaminhamento FUSE, omita a mudança de configuração acima ou defina
persist.sys.fuse.passthrough.enable
como false
. Se você tiver ativado anteriormente
o passthrough do FUSE, a desativação impedirá que o dispositivo use o passthrough do FUSE,
mas ele continuará funcional.
Para ativar/desativar o passthrough do FUSE sem atualizar o dispositivo, mude a propriedade do sistema usando comandos ADB. Veja um exemplo abaixo.
adb root
adb shell setprop persist.sys.fuse.passthrough.enable {true,false}
adb reboot
Para mais ajuda, consulte a implementação de referência.
Validar o passthrough do FUSE
Para validar se o MediaProvider está usando o FUSE passthrough, verifique logcat
para
mensagens de depuração. Exemplo:
adb logcat FuseDaemon:V \*:S
--------- beginning of main
03-02 12:09:57.833 3499 3773 I FuseDaemon: Using FUSE passthrough
03-02 12:09:57.833 3499 3773 I FuseDaemon: Starting fuse...
A entrada FuseDaemon: Using FUSE passthrough
no registro garante que a passagem do FUSE esteja em uso.
O CTS do Android 12 inclui CtsStorageTest
, que
inclui testes que acionam a passagem do FUSE. Para executar o teste manualmente, use
atest, conforme mostrado abaixo:
atest CtsStorageTest