Android 12 supporta FUSE passthrough, che riduce al minimo il sovraccarico di FUSE per ottenere prestazioni paragonabili all'accesso diretto al file system inferiore. FUSE passthrough è supportato nei kernel android12-5.4, android12-5.10 e android-mainline (solo per i test), il che significa che il supporto per questa funzionalità dipende dal kernel utilizzato dal dispositivo e dalla versione di Android in esecuzione sul dispositivo:
I dispositivi che eseguono l'upgrade da Android 11 ad Android 12 non possono supportare FUSE passthrough perché i kernel di questi dispositivi sono bloccati e non possono passare a un kernel di cui è stato eseguito l'upgrade ufficiale con le modifiche di FUSE passthrough.
I dispositivi lanciati con Android 12 possono supportare FUSE passthrough quando utilizzano un kernel ufficiale. Per questi dispositivi, il codice del framework Android che implementa FUSE passthrough è incorporato nel modulo principale MediaProvider, di cui viene eseguito l'upgrade automatico. Anche i dispositivi che non implementano MediaProvider come modulo principale (ad esempio i dispositivi Android Go) possono accedere alle modifiche di MediaProvider perché sono condivise pubblicamente.
FUSE rispetto a SDCardFS
File system in Userspace (FUSE) è un meccanismo che consente al kernel (driver FUSE) di esternalizzare le operazioni eseguite su un file system FUSE a un programma userspace (daemon FUSE), che implementa le operazioni. Android 11 ha ritirato SDCardFS e ha reso FUSE la soluzione predefinita per l'emulazione dello spazio di archiviazione. Nell'ambito di questa modifica, Android ha implementato il proprio daemon FUSE per intercettare gli accessi ai file, applicare funzionalità di sicurezza e privacy aggiuntive e manipolare i file in fase di runtime.
Sebbene FUSE funzioni bene quando si tratta di informazioni memorizzabili nella cache, come pagine o attributi, introduce regressioni delle prestazioni quando si accede allo spazio di archiviazione esterno, che sono particolarmente visibili sui dispositivi di fascia media e bassa. Queste regressioni sono causate da una catena di componenti che collaborano all'implementazione del file system FUSE, nonché da più passaggi dallo spazio kernel allo spazio utente nelle comunicazioni tra il driver FUSE e il daemon FUSE (rispetto all'accesso diretto al file system inferiore, che è più snello e completamente implementato nel kernel).
Per attenuare queste regressioni, le app possono utilizzare
lo splicing per
ridurre la copia dei dati e utilizzare l'API
ContentProvider
per ottenere l'accesso diretto ai file del file system inferiore. Anche con queste e altre
ottimizzazioni, le operazioni di lettura
e scrittura potrebbero riscontrare una larghezza di banda ridotta quando si utilizza FUSE rispetto all'accesso diretto al file
system — soprattutto con le operazioni di lettura casuale, dove la memorizzazione nella cache o la lettura anticipata non possono essere d'aiuto. Inoltre, le app che accedono direttamente allo spazio di archiviazione tramite il percorso legacy /sdcard/ continuano a riscontrare cali di prestazioni notevoli, soprattutto quando eseguono operazioni con un utilizzo intensivo di I/O.
Richieste userspace SDcardFS
L'utilizzo di SDcardFS può velocizzare l'emulazione dello spazio di archiviazione e i controlli delle autorizzazioni di FUSE rimuovendo la chiamata allo spazio utente dal kernel. Le richieste userspace seguono il percorso: Userspace → VFS → sdcardfs → VFS → ext4 → Cache/spazio di archiviazione delle pagine.
Figura 1. Richieste userspace SDcardFS
Richieste userspace FUSE
Inizialmente, FUSE veniva utilizzato per abilitare l'emulazione dello spazio di archiviazione e per consentire alle app di utilizzare in modo trasparente la memoria interna o una scheda SD esterna. L'utilizzo di FUSE introduce un sovraccarico perché ogni richiesta userspace segue il percorso: Userspace → VFS → Driver FUSE → Daemon FUSE → VFS → ext4 → Cache/spazio di archiviazione delle pagine.
Figura 2. Richieste userspace FUSE
Richieste FUSE passthrough
La maggior parte delle autorizzazioni di accesso ai file viene controllata al momento dell'apertura del file, con controlli delle autorizzazioni aggiuntivi che si verificano durante la lettura e la scrittura del file. In alcuni casi, è possibile sapere al momento dell'apertura del file che l'app richiedente ha accesso completo al file richiesto, quindi il sistema non deve continuare a inoltrare le richieste di lettura e scrittura dal driver FUSE al daemon FUSE (in quanto ciò sposterebbe solo i dati da un luogo all'altro).
Con FUSE passthrough, il daemon FUSE che gestisce una richiesta di apertura può notificare al driver FUSE che l'operazione è consentita e che tutte le successive richieste di lettura e scrittura possono essere inoltrate direttamente al file system inferiore. In questo modo si evita il sovraccarico aggiuntivo di attesa della risposta del daemon FUSE userspace alle richieste del driver FUSE.
Di seguito è riportato un confronto tra le richieste FUSE e FUSE passthrough.
Figura 3. Richiesta FUSE rispetto a richiesta FUSE passthrough
Quando un'app esegue un accesso al file system FUSE, si verificano le seguenti operazioni:
Il driver FUSE gestisce e mette in coda la richiesta, quindi la presenta al daemon FUSE che gestisce il file system FUSE tramite un'istanza di connessione specifica sul file
/dev/fuse, che il daemon FUSE non può leggere.Quando il daemon FUSE riceve una richiesta di apertura di un file, decide se FUSE passthrough deve essere disponibile per quel particolare file. Se è disponibile, il daemon:
Notifica al driver FUSE questa richiesta.
Abilita FUSE passthrough per il file utilizzando l'ioctl
FUSE_DEV_IOC_PASSTHROUGH_OPEN, che deve essere eseguito sul descrittore del file/dev/fuseaperto.
L'ioctl riceve (come parametro) una struttura di dati che contiene:
Descrittore del file system inferiore che è la destinazione della funzionalità passthrough.
Identificatore univoco della richiesta FUSE attualmente gestita (deve essere aperta o creata e aperta).
Campi aggiuntivi che possono essere lasciati vuoti e sono destinati a implementazioni future.
Se l'ioctl ha esito positivo, il daemon FUSE completa la richiesta di apertura, il driver FUSE gestisce la risposta del daemon FUSE e al file FUSE all'interno del kernel viene aggiunto un riferimento al file system inferiore. Quando un'app richiede un'operazione di lettura/scrittura su un file FUSE, il driver FUSE verifica se è disponibile il riferimento a un file system inferiore.
Se è disponibile un riferimento, il driver crea una nuova richiesta VFS (Virtual File System) con gli stessi parametri che hanno come target il file system inferiore.
Se non è disponibile un riferimento, il driver inoltra la richiesta al daemon FUSE.
Le operazioni descritte sopra si verificano per le operazioni di lettura/scrittura e lettura-iter/scrittura-iter su file generici e per le operazioni di lettura/scrittura su file con mapping della memoria. FUSE passthrough per un determinato file esiste finché il file non viene chiuso.
Implementare FUSE passthrough
Per abilitare FUSE passthrough sui dispositivi con Android 12, aggiungi le seguenti righe al file $ANDROID_BUILD_TOP/device/…/device.mk del dispositivo di destinazione.
# Use FUSE passthrough
PRODUCT_PRODUCT_PROPERTIES += \
persist.sys.fuse.passthrough.enable=true
Per disattivare FUSE passthrough, ometti la modifica della configurazione sopra riportata o imposta persist.sys.fuse.passthrough.enable su false. Se in precedenza hai attivato FUSE passthrough, la disattivazione impedisce al dispositivo di utilizzare FUSE passthrough, ma il dispositivo rimane funzionante.
Per attivare/disattivare FUSE passthrough senza eseguire il flashing del dispositivo, modifica la proprietà di sistema utilizzando i comandi ADB. Di seguito è riportato un esempio.
adb rootadb shell setprop persist.sys.fuse.passthrough.enable {true,false}adb reboot
Per ulteriore assistenza, consulta l'implementazione di riferimento.
Convalidare FUSE passthrough
Per verificare che MediaProvider stia utilizzando FUSE passthrough, controlla logcat per i messaggi di debug. Ad esempio:
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...La voce FuseDaemon: Using FUSE passthrough nel log garantisce che FUSE passthrough sia in uso.
Android 12 CTS include CtsStorageTest, che include test che attivano FUSE passthrough. Per eseguire il test manualmente, utilizza atest come mostrato di seguito:
atest CtsStorageTest