Android 12 est compatible avec le passthrough FUSE, qui minimise la surcharge FUSE pour obtenir des performances comparables à l'accès direct au système de fichiers inférieur. Le transfert FUSE est compatible avec les noyaux android12-5.4
, android12-5.10
et android-mainline
(pour les tests uniquement). Cela signifie que la compatibilité 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 transfert 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 du transfert FUSE.
Les appareils lancés avec Android 12 peuvent prendre en charge le transfert FUSE lorsqu'ils utilisent un kernel officiel. Pour ces appareils, le code du framework Android qui implémente la transmission 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 Mainline (par exemple, les appareils Android Go) peuvent également accéder aux modifications apportées à MediaProvider lorsqu'elles sont partagées publiquement.
Comparaison entre FUSE et SDCardFS
Filesystem in Userspace (FUSE) est un mécanisme qui permet au noyau (pilote FUSE) de sous-traiter les opérations effectuées sur un système de fichiers FUSE à un programme de l'espace utilisateur (démon FUSE), qui implémente 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 lorsqu'il s'agit d'informations mises en cache telles que des pages ou des 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 d'entrée de gamme. Ces régressions sont dues à une chaîne de composants coopérant dans l'implémentation du système de fichiers FUSE, ainsi qu'à de multiples commutations de l'espace du noyau vers 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 léger et entièrement implémenté dans le noyau).
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, les opérations de lecture et d'écriture peuvent voir leur bande passante 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éatoire, où aucune mise en cache ni lecture anticipée ne peuvent aider. De plus, les applications qui accèdent directement au stockage via l'ancien chemin d'accès /sdcard/
continuent de subir des baisses de performances notables, en particulier lors d'opérations intensives d'E/S.
Requêtes de l'espace utilisateur SDcardFS
L'utilisation de SDcardFS peut accélérer l'émulation du stockage et les vérifications des 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 suivant : espace utilisateur → VFS → sdcardfs → VFS → ext4 → cache de page/stockage.
Figure 1 : Requêtes de l'espace utilisateur SDcardFS
Requêtes FUSE userspace
FUSE était initialement utilisé pour permettre l'émulation du stockage et pour permettre aux applications d'utiliser de manière transparente le stockage interne ou une carte SD externe. L'utilisation de FUSE introduit une certaine surcharge, car chaque requête de l'espace utilisateur suit le chemin d'accès suivant : espace utilisateur → VFS → pilote FUSE → démon FUSE → VFS → ext4 → cache de pages/stockage.
Figure 2. Requêtes FUSE userspace
Requêtes FUSE directes
La plupart des autorisations d'accès aux fichiers sont vérifiées lors de l'ouverture des fichiers. Des vérifications d'autorisations supplémentaires ont lieu lors de la lecture et de l'écriture dans ces fichiers. Dans certains cas, il est possible de savoir au moment de l'ouverture du fichier que l'application qui en fait la demande y a un accès complet. Le système n'a donc pas besoin de continuer à transférer les requêtes de lecture et d'écriture du pilote FUSE au démon FUSE (car cela ne ferait que déplacer les données d'un endroit à un autre).
Avec le transfert FUSE, le démon FUSE qui gère une requête ouverte 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 la surcharge supplémentaire liée à l'attente de la réponse du démon FUSE de l'espace utilisateur aux requêtes du pilote FUSE.
Vous trouverez ci-dessous une comparaison des requêtes FUSE et FUSE passthrough.
Figure 3. Requête FUSE et requête FUSE directe
Lorsqu'une application effectue un accès au système de fichiers FUSE, les opérations suivantes se produisent :
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 ne peut pas lire.Lorsque le démon FUSE reçoit une demande d'ouverture d'un fichier, il décide si la transmission FUSE doit être disponible pour ce fichier spécifique. Si le démon est disponible :
Avertit le pilote FUSE de cette requête.
Active le transfert FUSE pour le fichier à l'aide de l'ioctl
FUSE_DEV_IOC_PASSTHROUGH_OPEN
, qui doit être effectué sur le descripteur de fichier du/dev/fuse
ouvert.
ioctl reçoit (en tant que paramètre) une structure de données contenant les éléments suivants :
Descripteur de fichier du fichier du système de fichiers inférieur qui est la cible de la fonctionnalité de transfert.
Identifiant unique de la requête FUSE en cours de traitement (doit être ouverte ou de type "create-and-open").
Champs supplémentaires qui peuvent être laissés vides et qui sont destinés aux futures implémentations.
Si l'ioctl réussit, 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 du 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 du système de fichiers inférieur est disponible.
Si une référence est disponible, le pilote crée une requête VFS (Virtual File System) avec les mêmes paramètres ciblant le fichier du 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-iter/écriture-iter sur les fichiers génériques, ainsi que pour les opérations de lecture/écriture sur les fichiers mappés en mémoire. Le transfert FUSE pour un fichier donné existe jusqu'à ce que ce fichier soit fermé.
Implémenter le passthrough FUSE
Pour activer le transfert FUSE sur les appareils exécutant 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 transfert 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 transfert FUSE, le fait de le désactiver empêche l'appareil de l'utiliser, mais l'appareil reste fonctionnel.
Pour activer/désactiver le transfert FUSE sans flasher l'appareil, modifiez la propriété système à l'aide des commandes ADB. Un exemple est présenté ci-dessous.
adb root
adb shell setprop persist.sys.fuse.passthrough.enable {true,false}
adb reboot
Pour obtenir de l'aide, consultez l'implémentation de référence.
Valider le transfert FUSE
Pour vérifier que MediaProvider utilise la transmission 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
dans le journal garantit que la transmission directe FUSE est utilisée.
Le CTS Android 12 inclut CtsStorageTest
, qui comprend des tests qui déclenchent le transfert FUSE. Pour exécuter le test manuellement, utilisez atest comme indiqué ci-dessous :
atest CtsStorageTest