Passthrough FUSE

Android 12 est compatible avec le passthrough FUSE, qui réduit les frais généraux FUSE pour obtenir des performances comparables à celles de l'accès direct au système de fichiers inférieur. Le passthrough FUSE est compatible avec les noyaux android12-5.4, android12-5.10 et android-mainline (tests uniquement), ce qui signifie que la prise en charge de cette fonctionnalité dépend du noyau utilisé par l'appareil et de la version d'Android qu'il exécute:

  • Les appareils qui passent d'Android 11 à Android 12 ne peuvent pas prendre en charge le passthrough FUSE, car les noyaux de ces appareils sont figés et ne peuvent pas passer à un noyau qui a été officiellement mis à niveau avec les modifications de passthrough FUSE.

  • Les appareils lancés avec Android 12 peuvent prendre en charge le passthrough FUSE lorsqu'ils utilisent un kernel officiel. Pour ces appareils, le code du framework Android qui implémente le passthrough FUSE est intégré au module principal MediaProvider, qui est automatiquement mis à niveau. Les appareils qui n'implémentent pas MediaProvider en tant que module principal (par exemple, les appareils Android Go) peuvent également accéder aux modifications apportées à MediaProvider, car elles sont partagées publiquement.

FUSE par rapport à SDCardFS

Le système de fichiers dans l'espace utilisateur (FUSE) est un mécanisme qui permet aux opérations effectuées sur un système de fichiers FUSE d'être externalisées par le noyau (pilote FUSE) à un programme d'espace utilisateur (démon FUSE), qui met en œuvre les opérations. Android 11 a abandonné SDCardFS et a fait de FUSE la solution par défaut pour l'émulation de stockage. Dans le cadre de ce changement, Android a implémenté son propre démon FUSE pour intercepter les accès aux fichiers, appliquer des fonctionnalités de sécurité et de confidentialité supplémentaires, et manipuler les fichiers au moment de l'exécution.

Bien que FUSE fonctionne bien avec les informations en cache, telles que les pages ou les attributs, il introduit des régressions de performances lors de l'accès au stockage externe, qui sont particulièrement visibles sur les appareils de milieu et de bas de gamme. Ces régressions sont causées par une chaîne de composants qui coopèrent dans l'implémentation du système de fichiers FUSE, ainsi que par plusieurs commutateurs de l'espace kernel à l'espace utilisateur dans les communications entre le pilote FUSE et le démon FUSE (par rapport à l'accès direct au système de fichiers inférieur, qui est plus simple et entièrement implémenté dans le kernel).

Pour atténuer ces régressions, les applications peuvent utiliser l'épissage pour réduire la copie de données et utiliser l'API ContentProvider pour accéder directement aux fichiers du système de fichiers inférieur. Même avec ces autres optimisations, la bande passante des opérations de lecture et d'écriture peut être réduite lors de l'utilisation de FUSE par rapport à l'accès direct au système de fichiers inférieur — en particulier avec les opérations de lecture aléatoires, où aucune mise en cache ni lecture anticipée ne peut aider. De plus, les applications qui accèdent directement à l'espace de stockage via l'ancien chemin /sdcard/ continuent de constater des baisses de performances notables, en particulier lors d'opérations nécessitant beaucoup d'E/S.

Requêtes d'espace utilisateur SDcardFS

L'utilisation de SDcardFS peut accélérer l'émulation de stockage et les vérifications d'autorisations de FUSE en supprimant l'appel de l'espace utilisateur du noyau. Les requêtes de l'espace utilisateur suivent le chemin d'accès: espace utilisateur → VFS → sdcardfs → VFS → ext4 → cache de page/stockage.

FUSE Passthrough SDcardFS

Figure 1 : Requêtes d'espace utilisateur SDcardFS

Requêtes de l'espace utilisateur FUSE

FUSE était initialement utilisé pour permettre l'émulation de stockage et pour permettre aux applications d'utiliser de manière transparente le stockage interne ou une carte SD externe. L'utilisation de FUSE entraîne une surcharge, car chaque requête d'espace utilisateur suit le chemin suivant : espace utilisateur → VFS → pilote FUSE → démon FUSE → VFS → ext4 → cache de page/stockage.

FUSE Passthrough

Figure 2. Requêtes d'espace utilisateur FUSE

Requêtes de passthrough FUSE

La plupart des autorisations d'accès aux fichiers sont vérifiées au moment de l'ouverture du fichier, et des vérifications d'autorisations supplémentaires sont effectuées lors de la lecture et de l'écriture dans ce fichier. Dans certains cas, il est possible de savoir, au moment de l'ouverture du fichier, que l'application à l'origine de la demande dispose d'un accès complet au fichier demandé. Le système n'a donc pas besoin de continuer à transférer les requêtes de lecture et d'écriture du pilote FUSE au daemon FUSE (car cela ne ferait que déplacer les données d'un endroit à un autre).

Avec le passthrough FUSE, le démon FUSE qui gère une requête d'ouverture peut informer le pilote FUSE que l'opération est autorisée et que toutes les requêtes de lecture et d'écriture ultérieures peuvent être directement transférées au système de fichiers inférieur. Cela évite les frais supplémentaires d'attente du démon FUSE de l'espace utilisateur pour répondre aux requêtes du pilote FUSE.

Vous trouverez ci-dessous une comparaison des requêtes de passthrough FUSE et FUSE.

Comparaison du passthrough FUSE

Figure 3. Requête FUSE par rapport à la requête de passthrough FUSE

Lorsqu'une application effectue un accès au système de fichiers FUSE, les opérations suivantes se produisent:

  1. Le pilote FUSE gère et met en file d'attente la requête, puis la présente au démon FUSE qui gère ce système de fichiers FUSE via une instance de connexion spécifique sur le fichier /dev/fuse, que le démon FUSE est empêché de lire.

  2. Lorsque le démon FUSE reçoit une requête d'ouverture d'un fichier, il décide si le passthrough FUSE doit être disponible pour ce fichier particulier. Si elle est disponible, le démon:

    1. Informe le pilote FUSE de cette requête.

    2. Active le passthrough FUSE pour le fichier à l'aide de l'ioctl FUSE_DEV_IOC_PASSTHROUGH_OPEN, qui doit être effectué sur le descripteur de fichier de l'/dev/fuse ouvert.

  3. L'ioctl reçoit (en tant que paramètre) une structure de données contenant les éléments suivants:

    • Descripteur de fichier du fichier de système de fichiers inférieur qui est la cible de la fonctionnalité de passthrough.

    • Identifiant unique de la requête FUSE en cours de traitement (il doit être ouvert ou créé et ouvert).

    • Champs supplémentaires qui peuvent être laissés vides et qui sont destinés à de futures implémentations.

  4. Si l'ioctl aboutit, le démon FUSE termine la requête d'ouverture, le pilote FUSE gère la réponse du démon FUSE, et une référence au fichier système de fichiers inférieur est ajoutée au fichier FUSE dans le noyau. Lorsqu'une application demande une opération de lecture/écriture sur un fichier FUSE, le pilote FUSE vérifie si la référence à un fichier de système de fichiers inférieur est disponible.

    • Si une référence est disponible, le pilote crée une nouvelle requête de système de fichiers virtuels (VFS) avec les mêmes paramètres ciblant le fichier de système de fichiers inférieur.

    • Si aucune référence n'est disponible, le pilote transmet la requête au démon FUSE.

Les opérations ci-dessus se produisent pour les opérations de lecture/écriture et de lecture/écriture sur des fichiers génériques et les opérations de lecture/écriture sur des fichiers mappés en mémoire. Le passthrough FUSE pour un fichier donné existe jusqu'à ce que ce fichier soit fermé.

Implémenter le passthrough FUSE

Pour activer le passthrough FUSE sur les appareils équipés d'Android 12, ajoutez les lignes suivantes au fichier $ANDROID_BUILD_TOP/device/…/device.mk de l'appareil cible.

# Use FUSE passthrough
PRODUCT_PRODUCT_PROPERTIES += \
    persist.sys.fuse.passthrough.enable=true

Pour désactiver le passthrough FUSE, omettez la modification de configuration ci-dessus ou définissez persist.sys.fuse.passthrough.enable sur false. Si vous avez déjà activé le passthrough FUSE, sa désactivation empêche l'appareil de l'utiliser, mais il reste fonctionnel.

Pour activer/désactiver le passthrough FUSE sans flasher l'appareil, modifiez la propriété système à l'aide de commandes ADB. Vous trouverez un exemple ci-dessous.

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

Pour obtenir de l'aide supplémentaire, consultez l'implémentation de référence.

Valider le passthrough FUSE

Pour vérifier que MediaProvider utilise le passthrough FUSE, recherchez les messages de débogage dans logcat. Exemple :

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...

L'entrée FuseDaemon: Using FUSE passthrough du journal garantit que le passthrough FUSE est utilisé.

Le CTS Android 12 inclut CtsStorageTest, qui inclut des tests qui déclenchent le passthrough FUSE. Pour exécuter le test manuellement, utilisez un test comme indiqué ci-dessous:

atest CtsStorageTest