Passagem FUSE

O Android 12 é compatível com a passagem FUSE, que minimiza a sobrecarga do FUSE para obter um desempenho comparável ao acesso direto ao sistema de arquivos inferior. A passagem do FUSE é compatível com os 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 podem oferecer suporte à passagem FUSE, pois os kernels desses dispositivos estão congelados e não podem ser movidos para um kernel que foi oficialmente atualizado com as alterações de passagem FUSE.

  • Os dispositivos lançados com o Android 12 podem oferecer suporte à passagem FUSE ao usar um kernel oficial. Para esses dispositivos, o código da estrutura do Android que implementa a passagem FUSE é incorporado ao módulo principal MediaProvider , que é atualizado automaticamente. Os dispositivos que não implementam o MediaProvider como um 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 de espaço do usuário (daemon FUSE), que implementa as operações. O Android 11 preteriu 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 que podem ser armazenadas em cache, como páginas ou atributos, ele apresenta regressões de desempenho ao acessar armazenamento externo que é especialmente visível 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 várias opções 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 mais enxuto e completamente implementado no kernel).

Para atenuar essas regressões, os aplicativos podem usar o splicing para reduzir a cópia de dados e usar a API ContentProvider para obter acesso direto aos arquivos do sistema de arquivos inferiores. 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 quando comparado ao acesso direto ao sistema de arquivos inferior — especialmente com operações de leitura aleatória, em que nenhum armazenamento em cache ou leitura antecipada pode ajudar. E os aplicativos que acessam diretamente o armazenamento por meio do caminho /sdcard/ legado continuam a sofrer quedas de desempenho perceptíveis, especialmente ao executar operações com uso intenso de E/S.

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 Userspace seguem o caminho: Userspace → VFS → sdcardfs → VFS → ext4 → Page cache/Storage.

FUSE Passthrough SDcardFS

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

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

O FUSE foi usado inicialmente para habilitar a emulação de armazenamento e permitir que os aplicativos usem 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: Userspace → VFS → FUSE driver → FUSE daemon → VFS → ext4 → Page cache/Storage.

FUSE Passagem FUSE

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

Solicitações de passagem FUSE {#fuse-passthrough-requests}

A maioria das permissões de acesso ao arquivo é verificada no momento da abertura do arquivo, com verificações de permissões adicionais ocorrendo ao ler e gravar nesse arquivo. Em alguns casos, é possível saber no momento da abertura do arquivo que o aplicativo solicitante tem acesso total ao arquivo solicitado, para que o sistema não precise continuar encaminhando as solicitações de leitura e gravação do driver FUSE para o daemon FUSE (como só 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 de leitura e gravação subsequentes 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 trata e enfileira a solicitação e, em seguida, a apresenta ao daemon FUSE que trata esse sistema de arquivos FUSE por meio de uma instância de conexão específica no arquivo /dev/fuse , que 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 esse arquivo específico. Se estiver disponível, o daemon:

    1. Notifica o driver FUSE sobre essa 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 do sistema de arquivos inferior que é o destino do recurso de passagem.

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

    • Campos extras que podem ser deixados em branco e destinam-se a implementações futuras.

  4. Se o ioctl for bem-sucedido, o daemon FUSE conclui a solicitação aberta, o driver FUSE manipula a resposta do daemon FUSE e uma referência ao arquivo do sistema de arquivos inferior é adicionada ao arquivo FUSE no 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 do Virtual File System (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/gravação em arquivos genéricos e operações de leitura/gravação em arquivos mapeados em memória. A passagem FUSE para um determinado arquivo existe até que esse arquivo seja fechado.

Implementando a passagem do FUSE

Para habilitar a passagem FUSE em dispositivos que executam o 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 FUSE, omita a alteração de configuração acima ou defina persist.sys.fuse.passthrough.enable como false . Se você ativou a passagem FUSE anteriormente, desativá-la impede que o dispositivo use a passagem FUSE, mas o dispositivo permanece funcional.

Para ativar/desativar a passagem do 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 .

Validando a passagem do FUSE

Para validar se o MediaProvider está usando a passagem FUSE, verifique o 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 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 do FUSE. Para executar o teste manualmente, use atest conforme mostrado abaixo:

atest CtsStorageTest