passagem FUSE

O Android 12 oferece suporte à passagem FUSE, que minimiza a sobrecarga do FUSE para obter desempenho comparável ao acesso direto ao sistema de arquivos inferior. A passagem FUSE é suportada nos kernels android12-5.4 , android12-5.10 e android-mainline (somente teste), o que significa que o suporte para esse recurso depende do kernel usado pelo dispositivo e da versão do Android que o dispositivo está executando:

  • Os dispositivos atualizados do Android 11 para o Android 12 não são compatíveis com a passagem FUSE, pois os kernels desses dispositivos estão congelados e eles não podem migrar para um kernel que foi oficialmente atualizado com as alterações da passagem FUSE.

  • Dispositivos lançados com Android 12 podem suportar passagem FUSE ao usar um kernel oficial. Para esses dispositivos, o código da estrutura Android que implementa a passagem FUSE é incorporado no módulo principal do MediaProvider , que é atualizado automaticamente. Os dispositivos que não implementam o MediaProvider como módulo principal (por exemplo, dispositivos Android Go) também podem acessar as alterações do MediaProvider à medida que são compartilhadas publicamente.

FUSE versus SDCardFS

O sistema de arquivos no espaço do usuário (FUSE) é um mecanismo que permite que as operações executadas em um sistema de arquivos FUSE sejam terceirizadas pelo kernel (driver FUSE) para um programa do espaço do usuário (daemon FUSE), que implementa as operações. O Android 11 descontinuou o SDCardFS e tornou o FUSE a solução padrão para emulação de armazenamento. Como parte dessa mudança, o Android implementou seu próprio daemon FUSE para interceptar acessos a arquivos, aplicar recursos extras de segurança e privacidade e manipular arquivos em tempo de execução.

Embora o FUSE tenha um bom desempenho ao lidar com informações armazenáveis ​​em cache, como páginas ou atributos, ele introduz regressões de desempenho ao acessar armazenamento externo que são especialmente visíveis em dispositivos de médio e baixo custo. Essas regressões são causadas por uma cadeia de componentes que cooperam na implementação do sistema de arquivos FUSE, bem como por múltiplas 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 arquivo inferior sistema que é mais enxuto e completamente implementado no kernel).

Para mitigar essas regressões, os aplicativos podem usar emenda para reduzir a cópia de dados e usar a API ContentProvider para obter acesso direto a arquivos inferiores do sistema de arquivos. Mesmo com essas e outras otimizações , as operações de leitura e gravação podem ter 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ória, onde nenhum cache ou leitura antecipada pode ajudar. E os aplicativos que acessam diretamente o armazenamento por meio do caminho legado /sdcard/ continuam apresentando quedas perceptíveis de desempenho, especialmente ao executar operações com uso intensivo de IO.

Solicitações de espaço de usuário 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 o caminho: Espaço do usuário → VFS → sdcardfs → VFS → ext4 → Cache/armazenamento de página.

FUSE Passthrough SDcardFS

Figura 1. Solicitações de espaço de usuário SDcardFS

Solicitações de espaço do usuário FUSE

O FUSE foi inicialmente usado para permitir a emulação de armazenamento e permitir que os aplicativos usassem de forma transparente o armazenamento interno ou um cartão SD externo. O uso do FUSE introduz alguma sobrecarga porque cada solicitação de espaço do usuário segue o caminho: Espaço do usuário → VFS → driver FUSE → daemon FUSE → VFS → ext4 → Cache/armazenamento de página.

FUSÍVEL Passagem FUSÍVEL

Figura 2. Solicitações de espaço do usuário FUSE

Solicitações de passagem FUSE

A maioria das permissões de acesso a arquivos são verificadas no momento da abertura do arquivo, com verificações de permissões adicionais ocorrendo durante a leitura e gravação nesse arquivo. Em alguns casos, é possível saber, no momento da abertura do arquivo, que o aplicativo solicitante tem acesso total ao arquivo solicitado, portanto, o sistema não precisa continuar encaminhando as solicitações de leitura e gravação do driver FUSE para o daemon FUSE (pois isso apenas moveria dados de um lugar para outro).

Com a passagem FUSE, o daemon FUSE que trata de uma solicitação aberta pode notificar o driver FUSE de que a operação é permitida e que todas as solicitações subsequentes de leitura e gravação podem ser encaminhadas diretamente para o sistema de arquivos 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.

Uma comparação das solicitações de passagem FUSE e FUSE é mostrada abaixo.

Comparação de passagem FUSE

Figura 3. Solicitação FUSE versus solicitação de passagem FUSE

Quando um aplicativo executa um acesso ao sistema de arquivos FUSE, ocorrem as seguintes operações:

  1. O driver FUSE manipula e enfileira a solicitação e, em seguida, apresenta-a ao daemon FUSE que manipula esse sistema de arquivos FUSE por meio de uma instância de conexão específica no arquivo /dev/fuse , cuja leitura o daemon FUSE está impedido de ler.

  2. Quando o daemon FUSE recebe uma solicitação para abrir um arquivo, ele decide se a passagem FUSE deve estar disponível para aquele arquivo específico. Se estiver disponível, o daemon:

    1. Notifica o driver FUSE sobre esta solicitação.

    2. Habilita a passagem FUSE para o arquivo usando o ioctl FUSE_DEV_IOC_PASSTHROUGH_OPEN , que deve ser executado no descritor de arquivo do /dev/fuse aberto.

  3. O ioctl recebe (como parâmetro) uma estrutura de dados que contém o seguinte:

    • Descritor de arquivo do arquivo inferior do sistema de arquivos que é o destino do recurso de passagem.

    • Identificador exclusivo da solicitação FUSE que está sendo tratada no momento (deve ser aberto ou criar e abrir).

    • Campos extras que podem ser deixados em branco e são destinados a implementações futuras.

  4. Se o ioctl for bem-sucedido, o daemon FUSE conclui a solicitação de abertura, o driver FUSE trata a resposta do daemon FUSE e uma referência ao arquivo inferior do sistema de arquivos é adicionada ao arquivo FUSE dentro do kernel. Quando um aplicativo solicita uma operação de leitura/gravação em um arquivo FUSE, o driver FUSE verifica se a referência a um arquivo do sistema de arquivos inferior está disponível.

    • Se uma referência estiver disponível, o driver criará uma nova solicitação de sistema de arquivos virtual (VFS) com os mesmos parâmetros direcionados ao arquivo do sistema de arquivos inferior.

    • Se uma referência não estiver disponível, o driver encaminha 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 na memória. A passagem FUSE para um determinado arquivo existe até que esse arquivo seja fechado.

Implementar passagem FUSE

Para ativar a passagem FUSE em dispositivos que executam Android 12, adicione as seguintes linhas 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 desabilitar a passagem do FUSE, omita a alteração de configuração acima ou defina persist.sys.fuse.passthrough.enable como false . Se você já habilitou a passagem FUSE, desativá-la evita que o dispositivo use a passagem FUSE, mas o dispositivo permanece funcional.

Para ativar/desativar a passagem FUSE sem atualizar o dispositivo, altere a propriedade do sistema usando comandos ADB. Um exemplo é mostrado abaixo.

adb root
adb shell setprop persist.sys.fuse.passthrough.enable {true,false}
adb reboot

Para obter ajuda adicional, consulte a implementação de referência .

Validar passagem FUSE

Para validar se o MediaProvider está usando passagem FUSE, verifique logcat para mensagens de depuração. Por 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 log garante que a passagem FUSE esteja em uso.

O Android 12 CTS inclui CtsStorageTest , que inclui testes que acionam a passagem FUSE. Para executar o teste manualmente, use atest conforme mostrado abaixo:

atest CtsStorageTest