FUSEパススルー

Android 12はFUSEパススルーをサポートしており、FUSEオーバーヘッドを最小限に抑えて、下位のファイルシステムへの直接アクセスに匹敵するパフォーマンスを実現します。 FUSEパススルーは、 android12-5.4android12-5.10 、およびandroid-mainline (テストのみ)カーネルでサポートされています。つまり、この機能のサポートは、デバイスで使用されているカーネルと、デバイスが実行しているAndroidのバージョンによって異なります。

  • Android11からAndroid12にアップグレードするデバイスは、FUSEパススルーをサポートできません。これらのデバイスのカーネルはフリーズしており、FUSEパススルーの変更で正式にアップグレードされたカーネルに移動できないためです。

  • Android 12で起動するデバイスは、公式カーネルを使用するときにFUSEパススルーをサポートできます。このようなデバイスの場合、FUSEパススルーを実装するAndroidフレームワークコードがMediaProviderメインラインモジュールに埋め込まれ、自動的にアップグレードされます。 MediaProviderをメインラインモジュールとして実装していないデバイス(Android Goデバイスなど)も、パブリックに共有されているため、MediaProviderの変更にアクセスできます。

FUSEとSDCardFS

Userspaceのファイルシステム(FUSE)は、FUSEファイルシステムで実行される操作を、カーネル(FUSEドライバー)が操作を実装するユーザースペースプログラム(FUSEデーモン)にアウトソーシングできるようにするメカニズムです。 Android 11はSDCardFSを廃止し、FUSEをストレージエミュレーションのデフォルトソリューションにしました。この変更の一環として、Androidは独自のFUSEデーモンを実装して、ファイルアクセスを傍受し、追加のセキュリティおよびプライバシー機能を適用し、実行時にファイルを操作しました。

FUSEは、ページや属性などのキャッシュ可能な情報を処理する場合に優れたパフォーマンスを発揮しますが、特にミッドエンドおよびローエンドのデバイスで表示される外部ストレージにアクセスする場合にパフォーマンスの低下をもたらします。これらのリグレッションは、FUSEファイルシステムの実装で連携するコンポーネントのチェーン、およびFUSEドライバーとFUSEデーモン間の通信におけるカーネルスペースからユーザースペースへの複数のスイッチによって引き起こされます(下位ファイルへの直接アクセスと比較して)よりスリムで、カーネルに完全に実装されているシステム)。

これらのリグレッションを軽減するために、アプリはスプライシングを使用してデータコピーを減らし、 ContentProviderAPIを使用して下位のファイルシステムファイルに直接アクセスできます。これらやその他の最適化を行っても、FUSEを使用すると、下位のファイルシステムに直接アクセスする場合と比較して、読み取りおよび書き込み操作の帯域幅が減少する可能性があります。特に、キャッシュや先読みが役に立たないランダム読み取り操作の場合はそうです。また、レガシー/sdcard/パスを介してストレージに直接アクセスするアプリでは、特にIOを多用する操作を実行する場合に、パフォーマンスが著しく低下し続けます。

SDcardFSユーザースペースリクエスト

SDcardFSを使用すると、カーネルからユーザースペースの呼び出しを削除することで、FUSEのストレージエミュレーションとアクセス許可のチェックを高速化できます。ユーザースペースリクエストは、ユーザースペース→VFS→sdcardfs→VFS→ext4→ページキャッシュ/ストレージのパスに従います。

FUSEパススルーSDcardFS

1.SDcardFSユーザースペースリクエスト

FUSEユーザースペースリクエスト

FUSEは当初、ストレージエミュレーションを有効にし、アプリが内部ストレージまたは外部SDカードのいずれかを透過的に使用できるようにするために使用されていました。 FUSEを使用すると、各ユーザースペースリクエストが次のパスに従うため、オーバーヘッドが発生します:ユーザースペース→VFS→FUSEドライバー→FUSEデーモン→VFS→ext4→ページキャッシュ/ストレージ。

FUSEパススルーFUSE

2.FUSEユーザースペースリクエスト

FUSEパススルーリクエスト{#fuse-passthrough-requests}

ほとんどのファイルアクセス許可はファイルのオープン時にチェックされ、そのファイルの読み取りおよび書き込み時に追加の許可チェックが行われます。場合によっては、ファイルのオープン時に、要求元のアプリが要求されたファイルに完全にアクセスできることを知ることができるため、システムはFUSEドライバーからFUSEデーモンに要求の読み取りと書き込みを転送し続ける必要はありません(データをある場所から別の場所に移動するだけです)。

FUSEパススルーを使用すると、オープン要求を処理するFUSEデーモンは、操作が許可されていること、および後続のすべての読み取りおよび書き込み要求を下位のファイルシステムに直接転送できることをFUSEドライバーに通知できます。これにより、ユーザースペースのFUSEデーモンがFUSEドライバーの要求に応答するのを待つという余分なオーバーヘッドが回避されます。

FUSEパススルー要求とFUSEパススルー要求の比較を以下に示します。

FUSEパススルーの比較

3.FUSEリクエストとFUSEパススルーリクエスト

アプリがFUSEファイルシステムアクセスを実行すると、次の操作が発生します。

  1. FUSEドライバーは、要求を処理してエンキューし、FUSEデーモンが読み取ることをブロックされている/dev/fuseファイル上の特定の接続インスタンスを介してそのFUSEファイルシステムを処理するFUSEデーモンに要求を提示します。

  2. FUSEデーモンは、ファイルを開く要求を受信すると、その特定のファイルでFUSEパススルーを使用できるかどうかを決定します。使用可能な場合、デーモンは次のようになります。

    1. この要求についてFUSEドライバーに通知します。

    2. FUSE_DEV_IOC_PASSTHROUGH_OPEN ioctlを使用して、ファイルのFUSEパススルーを有効にします。これは、開いた/dev/fuseのファイル記述子で実行する必要があります。

  3. ioctlは、以下を含むデータ構造を(パラメーターとして)受け取ります。

    • パススルー機能のターゲットである下位ファイルシステムファイルのファイル記述子。

    • 現在処理されているFUSE要求の一意の識別子(開いているか、作成して開く必要があります)。

    • 空のままにしておくことができ、将来の実装のために使用される追加のフィールド。

  4. ioctlが成功すると、FUSEデーモンがオープン要求を完了し、FUSEドライバーがFUSEデーモンの応答を処理し、カーネル内のFUSEファイルに下位のファイルシステムファイルへの参照が追加されます。アプリがFUSEファイルの読み取り/書き込み操作を要求すると、FUSEドライバーは下位のファイルシステムファイルへの参照が使用可能かどうかを確認します。

    • 参照が利用可能な場合、ドライバーは、下位​​のファイルシステムファイルを対象とする同じパラメーターを使用して、新しい仮想ファイルシステム(VFS)要求を作成します。

    • 参照が利用できない場合、ドライバーは要求をFUSEデーモンに転送します。

上記の操作は、汎用ファイルの読み取り/書き込みおよび読み取り-iter /書き込み-iter、およびメモリマップトファイルの読み取り/書き込み操作に対して発生します。特定のファイルのFUSEパススルーは、そのファイルが閉じられるまで存在します。

FUSEパススルーの実装

Android 12を実行しているデバイスでFUSEパススルーを有効にするには、ターゲットデバイスの$ANDROID_BUILD_TOP/device/…/device.mkファイルに次の行を追加します。

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

FUSEパススルーを無効にするには、上記の構成変更を省略するか、 persist.sys.fuse.passthrough.enablefalseに設定します。以前にFUSEパススルーを有効にしたことがある場合、それを無効にすると、デバイスはFUSEパススルーを使用できなくなりますが、デバイスは機能し続けます。

デバイスをフラッシュせずにFUSEパススルーを有効/無効にするには、ADBコマンドを使用してシステムプロパティを変更します。以下に例を示します。

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

追加のヘルプについては、リファレンス実装を参照してください。

FUSEパススルーの検証

MediaProviderがFUSEパススルーを使用していることを検証するには、 logcatでデバッグメッセージを確認してください。例えば:

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すると、FUSEパススルーが使用されていることが保証されます。

Android 12 CTSには、FUSEパススルーをトリガーするテストを含むCtsStorageTestが含まれています。テストを手動で実行するには、以下に示すようにatestを使用します。

atest CtsStorageTest