Passante FUSIBILE

Android 12 supporta il passthrough FUSE, che riduce al minimo il sovraccarico di 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 per questa funzionalità dipende dal kernel utilizzato dal dispositivo e dalla versione di Android eseguita sul dispositivo:

  • I dispositivi che eseguono l'aggiornamento da Android 11 ad Android 12 non possono supportare il passthrough FUSE poiché i kernel di questi dispositivi sono congelati e non possono passare a un kernel che è stato ufficialmente aggiornato con le modifiche del passthrough FUSE.

  • I dispositivi avviati con Android 12 possono supportare il passthrough FUSE quando si utilizza un kernel ufficiale. Per tali dispositivi, il codice del framework Android che implementa il passthrough FUSE è incorporato nel modulo principale MediaProvider , che viene aggiornato automaticamente. Anche i dispositivi che non implementano MediaProvider come modulo principale (ad esempio i dispositivi Android Go) possono accedere alle modifiche di MediaProvider poiché sono condivise pubblicamente.

FUSE rispetto a SDCardFS

Il 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 in userspace (daemon FUSE), che implementa le operazioni. Android 11 ha deprecato SDCardFS e ha reso FUSE la soluzione predefinita per l'emulazione dello spazio di archiviazione. Come parte di questo cambiamento, Android ha implementato il proprio demone FUSE per intercettare gli accessi ai file, applicare funzionalità aggiuntive di sicurezza e privacy 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 a storage 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 molteplici passaggi dallo spazio kernel allo spazio utente nelle comunicazioni tra il driver FUSE e il demone FUSE (rispetto all'accesso diretto al file inferiore sistema 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 ottenere l'accesso diretto ai file del file system inferiore. Anche con queste e altre ottimizzazioni , le operazioni di lettura e scrittura potrebbero vedere una larghezza di banda ridotta quando si utilizza FUSE rispetto all'accesso diretto al file system inferiore, in particolare con operazioni di lettura casuale, dove nessuna memorizzazione nella cache o read-ahead può essere d'aiuto. Inoltre, le app che accedono direttamente allo spazio di archiviazione tramite il percorso legacy /sdcard/ continuano a riscontrare notevoli cali di prestazioni, soprattutto quando si eseguono operazioni con uso intensivo di I/O.

Richieste dello spazio utente SDcardFS

L'utilizzo di SDcardFS può velocizzare l'emulazione di archiviazione e i controlli delle autorizzazioni di FUSE rimuovendo la chiamata allo spazio utente dal kernel. Le richieste dello spazio utente seguono il percorso: Userspace → VFS → sdcardfs → VFS → ext4 → Page cache/Storage.

FUSE Passthrough SDcardFS

Figura 1. Richieste di spazio utente SDcardFS

FUSE richieste dello spazio utente

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'uso di FUSE introduce un certo sovraccarico perché ogni richiesta dello spazio utente segue il percorso: Spazio utente → VFS → Driver FUSE → Demone FUSE → VFS → ext4 → Cache/archiviazione della pagina.

FUSIBILE FUSIBILE passante

Figura 2. Richieste dello spazio utente FUSE

Richieste passthrough FUSE

La maggior parte delle autorizzazioni di accesso ai file vengono controllate all'apertura del file, con ulteriori controlli delle autorizzazioni che si verificano durante la lettura e la scrittura su quel file. In alcuni casi, è possibile sapere al momento dell'apertura del file che l'app richiedente ha pieno accesso al file richiesto, quindi il sistema non ha bisogno di continuare a inoltrare le richieste di lettura e scrittura dal driver FUSE al demone FUSE (poiché sposterebbe solo i dati da un luogo all'altro).

Con il passthrough FUSE, il demone 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. Ciò evita il sovraccarico aggiuntivo derivante dall'attesa che il demone FUSE dello spazio utente risponda alle richieste del driver FUSE.

Di seguito è riportato un confronto tra le richieste passthrough FUSE e FUSE.

Confronto passthrough FUSE

Figura 3. Richiesta FUSE e richiesta passthrough FUSE

Quando un'app esegue un accesso al file system FUSE, si verificano le seguenti operazioni:

  1. Il driver FUSE gestisce e accoda la richiesta, quindi la presenta al demone FUSE che gestisce il file system FUSE tramite un'istanza di connessione specifica sul file /dev/fuse , di cui il demone FUSE non può leggere.

  2. Quando il demone FUSE riceve una richiesta di apertura di un file, decide se il passthrough FUSE deve essere disponibile per quel particolare file. Se è disponibile, il demone:

    1. Notifica al driver FUSE questa richiesta.

    2. Abilita il passthrough FUSE per il file utilizzando l'ioctl FUSE_DEV_IOC_PASSTHROUGH_OPEN , che deve essere eseguito sul descrittore di file del /dev/fuse aperto.

  3. L'ioctl riceve (come parametro) una struttura dati che contiene quanto segue:

    • Descrittore di file del file system inferiore che è la destinazione della funzionalità passthrough.

    • Identificatore univoco della richiesta FUSE attualmente gestita (deve essere open o create-and-open).

    • Campi aggiuntivi che possono essere lasciati vuoti e sono destinati a implementazioni future.

  4. Se ioctl ha esito positivo, il demone FUSE completa la richiesta di apertura, il driver FUSE gestisce la risposta del demone FUSE e un riferimento al file system inferiore viene aggiunto al file FUSE all'interno del kernel. Quando un'app richiede un'operazione di lettura/scrittura su un file FUSE, il driver FUSE controlla se è disponibile il riferimento a un file system di livello inferiore.

    • Se è disponibile un riferimento, il driver crea una nuova richiesta VFS (Virtual File System) con gli stessi parametri destinati al file system di file inferiore.

    • Se un riferimento non è disponibile, il driver inoltra la richiesta al demone FUSE.

Le operazioni di cui sopra si verificano per operazioni di lettura/scrittura e iter di lettura/scrittura su file generici e 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 abilitare 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 disabilitare il passthrough FUSE, omettere la modifica di configurazione precedente o impostare persist.sys.fuse.passthrough.enable su false . Se in precedenza hai abilitato il passthrough FUSE, disabilitandolo impedisci al dispositivo di utilizzare il passthrough FUSE ma il dispositivo rimane funzionante.

Per abilitare/disabilitare il passthrough FUSE senza eseguire il flashing del dispositivo, modificare la proprietà del sistema utilizzando i comandi ADB. Di seguito è mostrato un esempio.

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

Per ulteriore assistenza, fare riferimento all'implementazione di riferimento .

Convalidare il passthrough FUSE

Per verificare che MediaProvider utilizzi il passthrough FUSE, controlla logcat per i messaggi di debug. Per 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...

FuseDaemon: Using FUSE passthrough nel registro garantisce che il passthrough FUSE sia in uso.

Android 12 CTS include CtsStorageTest , che include test che attivano il passthrough FUSE. Per eseguire il test manualmente, utilizzare atest come mostrato di seguito:

atest CtsStorageTest