FUSE Passthrough

Android 12 unterstützt FUSE Passthrough, wodurch der FUSE-Aufwand minimiert wird, um eine Leistung zu erzielen, die mit dem direkten Zugriff auf das niedrigere Dateisystem vergleichbar ist. FUSE Passthrough wird in den Kerneln android12-5.4, android12-5.10 und android-mainline (nur Test) unterstützt. Die Unterstützung für diese Funktion hängt also vom Kernel ab, der vom Gerät verwendet wird, und von der Android-Version, die auf dem Gerät ausgeführt wird:

  • Geräte, die von Android 11 auf Android 12 aktualisiert werden, können FUSE Passthrough nicht unterstützen, da die Kernel für diese Geräte eingefroren sind und sie nicht zu einem Kernel verschoben werden können, der mit den FUSE-Passthrough-Änderungen offiziell aktualisiert wurde.

  • Geräte, die mit Android 12 ausgeliefert werden, können FUSE-Passthrough unterstützen, wenn ein offizieller Kernel verwendet wird. Bei solchen Geräten ist der Android-Framework-Code, der FUSE-Passthrough implementiert, in das MediaProvider-Hauptmodul eingebettet, das automatisch aktualisiert wird. Geräte, auf denen MediaProvider nicht als Mainstream-Modul implementiert ist (z. B. Android Go-Geräte), können ebenfalls auf MediaProvider-Änderungen zugreifen, da sie öffentlich freigegeben werden.

FUSE im Vergleich zu SDCardFS

Das Dateisystem im Userspace (FUSE) ist ein Mechanismus, mit dem in einem FUSE-Dateisystem ausgeführte Vorgänge vom Kernel (FUSE-Treiber) an ein Userspace-Programm (FUSE-Daemon) ausgelagert werden, in dem die Vorgänge implementiert werden. In Android 11 wurde SDCardFS eingestellt und FUSE zur Standardlösung für die Speicheremulation gemacht. Im Rahmen dieser Änderung hat Android seinen eigenen FUSE-Daemon implementiert, um Dateizugriffe abzufangen, zusätzliche Sicherheits- und Datenschutzfunktionen durchzusetzen und Dateien zur Laufzeit zu manipulieren.

FUSE funktioniert zwar gut bei der Verarbeitung von im Cache speicherbaren Informationen wie Seiten oder Attributen, führt aber zu Leistungsabfällen beim Zugriff auf externen Speicher, die besonders auf Mid- und Low-End-Geräten sichtbar sind. Diese Rückschritte werden durch eine Kette von Komponenten verursacht, die bei der Implementierung des FUSE-Dateisystems zusammenarbeiten, sowie durch mehrere Wechsel vom Kernel- zum Userspace bei der Kommunikation zwischen dem FUSE-Treiber und dem FUSE-Daemon (im Vergleich zum direkten Zugriff auf das untergeordnete Dateisystem, das schlanker und vollständig im Kernel implementiert ist).

Um diese Rückschritte zu vermeiden, können Apps Splicing verwenden, um das Kopieren von Daten zu reduzieren, und die ContentProvider API, um direkten Zugriff auf Dateien des untergeordneten Dateisystems zu erhalten. Selbst bei diesen und anderen Optimierungen kann die Bandbreite bei Lese- und Schreibvorgängen bei Verwendung von FUSE im Vergleich zum direkten Zugriff auf das untergeordnete Dateisystem reduziert sein. –  insbesondere bei zufälligen Lesevorgängen, bei denen kein Caching oder Vorablesen helfen kann. Bei Apps, die direkt über den alten Pfad /sdcard/ auf den Speicher zugreifen, treten weiterhin spürbare Leistungseinbußen auf, insbesondere bei speicherintensiven Vorgängen.

SDcardFS-Nutzerraumanfragen

Mit SDcardFS können die Speicheremulation und Berechtigungsprüfungen von FUSE beschleunigt werden, da der Aufruf des Nutzerbereichs aus dem Kernel entfernt wird. Userspace-Anfragen folgen dem Pfad: Userspace → VFS → sdcardfs → VFS → ext4 → Auslagerung/Speicher.

FUSE Passthrough SDcardFS

Abbildung 1. SDcardFS-Userspace-Anfragen

FUSE-Userspace-Anfragen

Ursprünglich wurde FUSE verwendet, um die Speicheremulation zu ermöglichen und Apps die transparente Nutzung des internen Speichers oder einer externen SD-Karte zu ermöglichen. Die Verwendung von FUSE führt zu einem gewissen Aufwand, da jede Userspace-Anfrage dem Pfad folgt: Userspace → VFS → FUSE-Treiber → FUSE-Daemon → VFS → ext4 → Seiten-Cache/Speicher.

FUSE-Passthrough

Abbildung 2. FUSE-Userspace-Anfragen

FUSE-Weiterleitungsanfragen

Die meisten Dateizugriffsberechtigungen werden beim Öffnen der Datei geprüft. Zusätzliche Berechtigungsprüfungen erfolgen beim Lesen aus und Schreiben in diese Datei. In einigen Fällen ist es beim Öffnen der Datei möglich, zu erkennen, dass die anfragende App vollen Zugriff auf die angeforderte Datei hat. Das System muss dann die Lese- und Schreibanfragen nicht weiter vom FUSE-Treiber an den FUSE-Daemon weiterleiten, da dadurch nur Daten von einem Ort an einen anderen verschoben würden.

Bei der FUSE-Durchleitung kann der FUSE-Daemon, der eine offene Anfrage verarbeitet, den FUSE-Treiber darüber informieren, dass der Vorgang zulässig ist und dass alle nachfolgenden Lese- und Schreibanfragen direkt an das untergeordnete Dateisystem weitergeleitet werden können. Dadurch wird der zusätzliche Aufwand vermieden, der entsteht, wenn der FUSE-Daemon auf die FUSE-Treiberanfragen antwortet.

Unten ist ein Vergleich von FUSE- und FUSE-Passthrough-Anfragen zu sehen.

FUSE-Passthrough-Vergleich

Abbildung 3 FUSE-Anfrage und FUSE-Weiterleitungsanfrage

Wenn eine App auf das FUSE-Dateisystem zugreift, werden die folgenden Vorgänge ausgeführt:

  1. Der FUSE-Treiber verarbeitet und stellt die Anfrage in die Warteschlange, bevor er sie an den FUSE-Daemon weiterleitet, der das FUSE-Dateisystem über eine bestimmte Verbindungsinstanz in der Datei /dev/fuse verwaltet. Der FUSE-Daemon ist jedoch vom Lesen der Datei ausgeschlossen.

  2. Wenn der FUSE-Daemon eine Anfrage zum Öffnen einer Datei erhält, entscheidet er, ob für diese bestimmte Datei FUSE Passthrough verfügbar ist. Wenn er verfügbar ist, führt der Daemon folgende Aktionen aus:

    1. Der FUSE-Treiber wird über diese Anfrage benachrichtigt.

    2. Aktiviert FUSE-Passthrough für die Datei mit dem ioctl FUSE_DEV_IOC_PASSTHROUGH_OPEN, das für den Dateideskriptor des geöffneten /dev/fuse ausgeführt werden muss.

  3. ioctl empfängt (als Parameter) eine Datenstruktur, die Folgendes enthält:

    • Dateideskriptor der Datei des untergeordneten Dateisystems, die das Ziel der Passthrough-Funktion ist.

    • Eindeutige Kennung der FUSE-Anfrage, die gerade verarbeitet wird (muss „open“ oder „create-and-open“ sein).

    • Zusätzliche Felder, die leer bleiben können und für zukünftige Implementierungen vorgesehen sind.

  4. Wenn der ioctl erfolgreich ist, schließt der FUSE-Daemon die Öffnungsanfrage ab, der FUSE-Treiber verarbeitet die Antwort des FUSE-Daemons und der FUSE-Datei wird im Kernel ein Verweis auf die untergeordnete Dateisystemdatei hinzugefügt. Wenn eine App einen Lese-/Schreibvorgang für eine FUSE-Datei anfordert, prüft der FUSE-Treiber, ob der Verweis auf eine untergeordnete Datei des Dateisystems verfügbar ist.

    • Wenn eine Referenz verfügbar ist, erstellt der Treiber eine neue VFS-Anfrage (Virtual File System) mit denselben Parametern, die auf die untergeordnete Dateisystemdatei ausgerichtet ist.

    • Wenn keine Referenz verfügbar ist, leitet der Treiber die Anfrage an den FUSE-Daemon weiter.

Die oben genannten Vorgänge treten bei Lese-/Schreib- und Lese-/Schreib-Iterationen auf generischen Dateien und Lese-/Schreibvorgängen auf speicherabgebildeten Dateien auf. Der FUSE-Passthrough für eine bestimmte Datei ist verfügbar, bis diese Datei geschlossen wird.

FUSE-Passthrough implementieren

Wenn Sie den FUSE-Passthrough auf Geräten mit Android 12 aktivieren möchten, fügen Sie der Datei $ANDROID_BUILD_TOP/device/…/device.mk des Zielgeräts die folgenden Zeilen hinzu.

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

Wenn Sie die FUSE-Passthrough-Funktion deaktivieren möchten, lassen Sie die oben genannte Konfigurationsänderung aus oder setzen Sie persist.sys.fuse.passthrough.enable auf false. Wenn Sie FUSE Passthrough zuvor aktiviert haben, wird das Gerät dadurch daran gehindert, FUSE Passthrough zu verwenden. Das Gerät bleibt jedoch funktionsfähig.

Wenn Sie die FUSE-Passthrough-Funktion aktivieren oder deaktivieren möchten, ohne das Gerät zu flashen, ändern Sie die Systemeigenschaft mit ADB-Befehlen. Hier ein Beispiel:

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

Weitere Informationen finden Sie in der Referenzimplementierung.

FUSE-Passthrough prüfen

Prüfe in logcat, ob Debug-Nachrichten für die Verwendung von FUSE-Passthrough angezeigt werden. Beispiel:

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

Der Eintrag FuseDaemon: Using FUSE passthrough im Protokoll gibt an, dass die FUSE-Passthrough-Funktion verwendet wird.

Die Android 12 CTS enthält CtsStorageTest, die Tests enthält, die FUSE-Passthrough auslösen. Wenn Sie den Test manuell ausführen möchten, verwenden Sie den Befehl „atest“, wie unten dargestellt:

atest CtsStorageTest