FUSE-Passthrough

Android 12 unterstützt FUSE-Passthrough, wodurch der FUSE-Overhead minimiert wird, um eine Leistung zu erreichen, die mit dem direkten Zugriff auf das untere Dateisystem vergleichbar ist. FUSE-Passthrough wird in den Kerneln android12-5.4 , android12-5.10 und android-mainline (nur zum Testen) unterstützt, was bedeutet, dass die Unterstützung dieser Funktion vom vom Gerät verwendeten Kernel und der Android-Version abhängt, die auf dem Gerät ausgeführt wird:

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

  • Geräte, die mit Android 12 gestartet werden, können FUSE-Passthrough unterstützen, wenn sie einen offiziellen Kernel verwenden. Für solche Geräte ist der Android-Framework-Code, der FUSE-Passthrough implementiert, in das MediaProvider- Hauptmodul eingebettet, das automatisch aktualisiert wird. Geräte, die MediaProvider nicht als Hauptmodul implementieren (z. B. Android Go-Geräte), können auch auf MediaProvider-Änderungen zugreifen, wenn diese öffentlich freigegeben werden.

FUSE versus SDCardFS

Dateisystem im Userspace (FUSE) ist ein Mechanismus, der es ermöglicht, auf einem FUSE-Dateisystem ausgeführte Operationen vom Kernel (FUSE-Treiber) an ein Userspace-Programm (FUSE-Daemon) auszulagern, das die Operationen implementiert. Android 11 hat SDCardFS veraltet und FUSE zur Standardlösung für die Speicheremulation gemacht. Als Teil dieser Änderung implementierte Android seinen eigenen FUSE-Daemon, um Dateizugriffe abzufangen, zusätzliche Sicherheits- und Datenschutzfunktionen durchzusetzen und Dateien zur Laufzeit zu manipulieren.

Während FUSE beim Umgang mit zwischenspeicherbaren Informationen wie Seiten oder Attributen eine gute Leistung erbringt, führt es beim Zugriff auf externen Speicher zu Leistungseinbußen, die besonders bei Geräten der mittleren und unteren Preisklasse sichtbar sind. Diese Regressionen werden durch eine Kette von Komponenten verursacht, die bei der Implementierung des FUSE-Dateisystems zusammenarbeiten, sowie durch mehrere Wechsel vom Kernel-Space zum User-Space bei der Kommunikation zwischen dem FUSE-Treiber und dem FUSE-Daemon (im Vergleich zum direkten Zugriff auf die untergeordnete Datei). schlankeres und vollständig im Kernel implementiertes System).

Um diese Regressionen zu mildern, können Apps Splicing verwenden, um das Kopieren von Daten zu reduzieren, und die ContentProvider-API verwenden, um direkten Zugriff auf niedrigere Dateisystemdateien zu erhalten. Selbst mit diesen und anderen Optimierungen kann es bei Lese- und Schreibvorgängen bei der Verwendung von FUSE zu einer geringeren Bandbreite im Vergleich zum direkten Zugriff auf das untere Dateisystem kommen – insbesondere bei zufälligen Lesevorgängen, bei denen kein Caching oder Vorauslesen helfen kann. Und bei Apps, die direkt über den alten /sdcard/ -Pfad auf den Speicher zugreifen, kommt es weiterhin zu spürbaren Leistungseinbußen, insbesondere bei der Ausführung von E/A-intensiven Vorgängen.

SDcardFS-Benutzerbereichsanfragen

Die Verwendung von SDcardFS kann die Speicheremulation und Berechtigungsprüfungen von FUSE beschleunigen, indem der User-Space-Aufruf aus dem Kernel entfernt wird. Userspace-Anfragen folgen dem Pfad: Userspace → VFS → sdcardfs → VFS → ext4 → Seitencache/Speicher.

FUSE Passthrough SDcardFS

Abbildung 1. SDcardFS-Benutzerbereichsanforderungen

FUSE-Userspace-Anfragen

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

SICHERUNG Passthrough-SICHERUNG

Abbildung 2. FUSE-Userspace-Anfragen

FUSE-Passthrough-Anfragen

Die meisten Dateizugriffsberechtigungen werden beim Öffnen der Datei überprüft. Beim Lesen aus und Schreiben in die Datei werden zusätzliche Berechtigungsprüfungen durchgeführt. In manchen Fällen ist es möglich, beim Öffnen der Datei zu wissen, dass die anfordernde App vollen Zugriff auf die angeforderte Datei hat, sodass das System die Lese- und Schreibanforderungen vom FUSE-Treiber nicht weiter an den FUSE-Daemon weiterleiten muss (so). würde nur Daten von einem Ort zum anderen verschieben).

Mit FUSE Passthrough kann der FUSE-Daemon, der eine Öffnungsanforderung verarbeitet, den FUSE-Treiber darüber informieren, dass der Vorgang zulässig ist und dass alle nachfolgenden Lese- und Schreibanforderungen direkt an das untergeordnete Dateisystem weitergeleitet werden können. Dadurch wird der zusätzliche Aufwand vermieden, der durch das Warten auf die Antwort des FUSE-Daemons im Benutzerbereich auf die FUSE-Treiberanforderungen entsteht.

Ein Vergleich von FUSE- und FUSE-Passthrough-Anfragen ist unten dargestellt.

FUSE Passthrough-Vergleich

Abbildung 3. FUSE-Anfrage im Vergleich zur FUSE-Passthrough-Anfrage

Wenn eine App einen FUSE-Dateisystemzugriff durchführt, werden die folgenden Vorgänge ausgeführt:

  1. Der FUSE-Treiber verarbeitet und stellt die Anforderung in die Warteschlange und präsentiert sie dann dem FUSE-Daemon, der das FUSE-Dateisystem über eine bestimmte Verbindungsinstanz in der /dev/fuse verarbeitet, deren Lesen der FUSE-Daemon blockiert.

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

    1. Benachrichtigt den FUSE-Treiber über diese Anfrage.

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

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

    • Dateideskriptor der unteren Dateisystemdatei, die das Ziel für die Passthrough-Funktion ist.

    • Eindeutiger Bezeichner der FUSE-Anfrage, die gerade bearbeitet wird (muss offen oder „Erstellen und Öffnen“ sein).

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

  4. Wenn ioctl erfolgreich ist, schließt der FUSE-Daemon die Öffnungsanforderung ab, der FUSE-Treiber verarbeitet die Antwort des FUSE-Daemons und ein Verweis auf die untergeordnete Dateisystemdatei wird zur FUSE-Datei im Kernel hinzugefügt. Wenn eine App einen Lese-/Schreibvorgang für eine FUSE-Datei anfordert, prüft der FUSE-Treiber, ob der Verweis auf eine niedrigere Dateisystemdatei 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 abzielt.

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

Die oben genannten Vorgänge werden für Lese-/Schreibvorgänge und Lese-Iter/Schreib-Iter für generische Dateien und Lese-/Schreibvorgänge für speicherzugeordnete Dateien ausgeführt. Der FUSE-Passthrough für eine bestimmte Datei bleibt bestehen, bis diese Datei geschlossen wird.

Implementieren Sie FUSE-Passthrough

Um FUSE-Passthrough auf Geräten mit Android 12 zu aktivieren, fügen Sie die folgenden Zeilen zur Datei $ANDROID_BUILD_TOP/device/…/device.mk des Zielgeräts hinzu.

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

Um FUSE-Passthrough zu deaktivieren, lassen Sie die obige Konfigurationsänderung weg oder setzen Sie persist.sys.fuse.passthrough.enable auf false . Wenn Sie FUSE-Passthrough zuvor aktiviert haben, verhindert die Deaktivierung, dass das Gerät FUSE-Passthrough verwendet, das Gerät bleibt jedoch funktionsfähig.

Um FUSE-Passthrough zu aktivieren/deaktivieren, ohne das Gerät zu flashen, ändern Sie die Systemeigenschaft mithilfe von ADB-Befehlen. Ein Beispiel ist unten dargestellt.

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

Weitere Hilfe finden Sie in der Referenzimplementierung .

Validieren Sie den FUSE-Passthrough

Um zu überprüfen, ob MediaProvider FUSE-Passthrough verwendet, überprüfen Sie logcat auf Debugmeldungen. Zum 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 stellt sicher, dass FUSE passthrough verwendet wird.

Das Android 12 CTS enthält CtsStorageTest , das Tests umfasst, die FUSE-Passthrough auslösen. Um den Test manuell auszuführen, verwenden Sie atest wie unten gezeigt:

atest CtsStorageTest