Android 12 supporta il passthrough FUSE, che riduce al minimo
l'overhead FUSE per ottenere prestazioni paragonabili all'accesso diretto al file system
inferiore. Il passthrough FUSE è supportato nei kernel android12-5.4
,
android12-5.10
e android-mainline
(solo test), il che significa
che il supporto di 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 il passthrough FUSE 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 del passthrough FUSE.
I dispositivi lanciati con Android 12 possono supportare il passthrough FUSE quando utilizzano un kernel ufficiale. Per questi dispositivi, il codice del framework Android che implementa il passthrough FUSE è incorporato nel modulo mainline MediaProvider, di cui viene eseguito l'upgrade automatico. I dispositivi che non implementano MediaProvider come modulo mainline (ad esempio i dispositivi Android Go) possono anche accedere alle modifiche di MediaProvider man mano che vengono condivise pubblicamente.
FUSE e SDCardFS
File system in Userspace (FUSE) è un meccanismo che consente di esternalizzare le operazioni eseguite su un file system FUSE dal kernel (driver 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 gestisce informazioni memorizzabili nella cache, come pagine o attributi, introduce regressioni delle prestazioni quando accede a spazio di archiviazione esterno, che sono particolarmente visibili nei dispositivi di fascia media e bassa. Queste regressioni sono causate da una catena di componenti che cooperano nell'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, più snello e completamente implementato nel kernel).
Per mitigare queste regressioni, le app possono utilizzare
lo splicing per
ridurre la copia dei dati e utilizzare l'API ContentProvider
per accedere direttamente ai file del file system di livello inferiore. Anche con queste e altre
ottimizzazioni, le operazioni di lettura
e scrittura potrebbero subire una riduzione della larghezza di banda quando si utilizza FUSE rispetto
all'accesso diretto al file
system inferiore — soprattutto con operazioni di lettura
casuali, in cui nessuna memorizzazione nella cache o lettura anticipata può essere d'aiuto. Inoltre, le app che accedono direttamente
allo spazio di archiviazione tramite il percorso /sdcard/
legacy continuano a registrare
cali di prestazioni notevoli, soprattutto quando eseguono operazioni
a uso intensivo di I/O.
Richieste di spazio utente SDcardFS
L'utilizzo di SDcardFS può accelerare l'emulazione dell'archiviazione e i controlli delle autorizzazioni di FUSE rimuovendo la chiamata allo spazio utente dal kernel. Le richieste dello spazio utente seguono il percorso: spazio utente → VFS → sdcardfs → VFS → ext4 → cache di pagine/archiviazione.
Figura 1. Richieste di spazio utente SDcardFS
Richieste dello spazio utente FUSE
FUSE è stato inizialmente utilizzato per abilitare l'emulazione dell'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 dello spazio utente segue il percorso: Spazio utente → VFS → Driver FUSE → Daemon FUSE → VFS → ext4 → Cache di pagine/Archiviazione.
Figura 2. Richieste dello spazio utente FUSE
Richieste passthrough FUSE
La maggior parte delle autorizzazioni di accesso ai file vengono controllate al momento dell'apertura del file, con ulteriori controlli delle autorizzazioni che si verificano durante la lettura e la scrittura nel 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 il passthrough FUSE, il daemon FUSE che gestisce una richiesta aperta 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 dovuto all'attesa della risposta del daemon FUSE dello spazio utente alle richieste del driver FUSE.
Di seguito è riportato un confronto tra le richieste FUSE e FUSE passthrough.
Figura 3. Richiesta FUSE e richiesta passthrough FUSE
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 il passthrough FUSE deve essere disponibile per quel particolare file. Se è disponibile, il daemon:
Notifica al driver FUSE questa richiesta.
Consente il passthrough FUSE per il file utilizzando l'ioctl
FUSE_DEV_IOC_PASSTHROUGH_OPEN
, che deve essere eseguito sul descrittore di file di/dev/fuse
aperto.
ioctl riceve (come parametro) una struttura di dati che contiene quanto segue:
Descrittore del file del file system sottostante che è la destinazione della funzionalità passthrough.
Identificatore univoco della richiesta FUSE attualmente in gestione (deve essere aperta o di tipo create-and-open).
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 sottostante. Quando un'app richiede un'operazione di lettura/scrittura su un file FUSE, il driver FUSE verifica se il riferimento a un file del file system sottostante è disponibile.
Se è disponibile un riferimento, il driver crea una nuova richiesta Virtual File System (VFS) con gli stessi parametri che hanno come target il file system inferiore.
Se un riferimento non è disponibile, il driver inoltra la richiesta al daemon FUSE.
Le operazioni precedenti si verificano per la lettura/scrittura e la lettura/scrittura iterativa su file generici e per le operazioni di lettura/scrittura su file mappati in memoria. Il passthrough FUSE per un determinato file esiste finché il file non viene chiuso.
Implementare il passthrough FUSE
Per attivare il passthrough FUSE 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 il passthrough FUSE, ometti la modifica alla configurazione precedente o imposta
persist.sys.fuse.passthrough.enable
su false
. Se in precedenza hai attivato
il passthrough FUSE, la disattivazione impedisce al dispositivo di utilizzarlo,
ma il dispositivo rimane funzionante.
Per attivare/disattivare il passthrough FUSE senza eseguire il flashing del dispositivo, modifica la proprietà di sistema utilizzando i comandi ADB. Di seguito è riportato un esempio.
adb root
adb shell setprop persist.sys.fuse.passthrough.enable {true,false}
adb reboot
Per ulteriore assistenza, consulta l'implementazione di riferimento.
Convalida del passthrough FUSE
Per verificare che MediaProvider utilizzi il passthrough FUSE, 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 sia in uso il passthrough FUSE.
Il CTS di Android 12 include CtsStorageTest
, che
include test che attivano il passthrough FUSE. Per eseguire il test manualmente, utilizza
atest come mostrato di seguito:
atest CtsStorageTest