העברה ללא שינוי (passthrough) ב-FUSE

‫Android 12 תומך ב-FUSE passthrough, שמצמצם את התקורה של FUSE כדי להשיג ביצועים שדומים לגישה ישירה למערכת הקבצים התחתונה. העברת FUSE נתמכת בקרנלים android12-5.4,‏ android12-5.10 ו-android-mainline (בדיקה בלבד), כלומר התמיכה בתכונה הזו תלויה בקרנל שבו המכשיר משתמש ובגרסת Android שפועלת במכשיר:

  • מכשירים שמשדרגים מ-Android 11 ל-Android 12 לא יכולים לתמוך ב-FUSE passthrough, כי ליבות המערכת של המכשירים האלה קפואות ואי אפשר להעביר אותן לליבה ששודרגה באופן רשמי עם השינויים של FUSE passthrough.

  • מכשירים שמופעלים עם Android 12 יכולים לתמוך ב-FUSE passthrough כשמשתמשים בליבה רשמית. במכשירים כאלה, קוד המסגרת של Android שמטמיע את FUSE passthrough מוטמע במודול הראשי MediaProvider, שעובר שדרוג אוטומטי. מכשירים שלא מטמיעים את MediaProvider כמודול ראשי (לדוגמה, מכשירי Android Go), יכולים גם לגשת לשינויים ב-MediaProvider כי הם משותפים באופן ציבורי.

FUSE לעומת SDCardFS

File system in Userspace (FUSE) הוא מנגנון שמאפשר לליבה (kernel) (מנהל ההתקן של FUSE) להעביר לתוכנה במרחב המשתמש (FUSE daemon) פעולות שמתבצעות במערכת קבצים של FUSE, והתוכנה הזו מבצעת את הפעולות. ב-Android 11,‏ SDCardFS הוצא משימוש ו-FUSE הפך לפתרון ברירת המחדל לאמולציית אחסון. במסגרת השינוי הזה, מערכת Android הטמיעה דמון FUSE משלה כדי ליירט גישות לקבצים, לאכוף תכונות אבטחה ופרטיות נוספות ולשנות קבצים בזמן ריצה.

‫FUSE פועל היטב כשמדובר במידע שאפשר לשמור במטמון, כמו דפים או מאפיינים, אבל הוא גורם לירידה בביצועים כשניגשים לאחסון חיצוני, והירידה הזו בביצועים בולטת במיוחד במכשירים בינוניים ובמכשירים ברמת כניסה. הנסיגות האלה נגרמות כתוצאה משרשרת של רכיבים שפועלים יחד בהטמעה של מערכת הקבצים FUSE, וגם כתוצאה ממעברים רבים ממרחב הליבה למרחב המשתמש בתקשורת בין מנהל ההתקן FUSE לבין שד FUSE (לעומת גישה ישירה למערכת הקבצים התחתונה שהיא רזה יותר ומוטמעת לחלוטין בליבה).

כדי לצמצם את הרגרסיות האלה, אפליקציות יכולות להשתמש בשחבור כדי לצמצם את העתקת הנתונים, ולהשתמש ב-ContentProvider API כדי לקבל גישה ישירה לקבצים נמוכים יותר במערכת הקבצים. גם עם האופטימיזציות האלה ואופטימיזציות אחרות, יכול להיות שרוחב הפס של פעולות קריאה וכתיבה יצטמצם כשמשתמשים ב-FUSE בהשוואה לגישה ישירה למערכת הקבצים התחתונה – במיוחד בפעולות קריאה אקראיות, שבהן לא ניתן להשתמש במטמון או בקריאה מראש. בנוסף, אפליקציות שניגשות ישירות לאחסון דרך הנתיב מדור קודם /sdcard/ ימשיכו לחוות ירידות משמעותיות בביצועים, במיוחד כשמבצעים פעולות שדורשות הרבה קלט/פלט.

בקשות במרחב המשתמשים של SDcardFS

שימוש ב-SDcardFS יכול להאיץ את האמולציה של האחסון ואת בדיקות ההרשאות של FUSE על ידי הסרת הקריאה למרחב המשתמש מהליבה. בקשות במרחב המשתמשים עוברות בנתיב הבא: מרחב המשתמשים ← VFS ← sdcardfs ← VFS ← ext4 ← מטמון דפים/אחסון.

FUSE Passthrough SDcardFS

איור 1. בקשות במרחב המשתמשים של SDcardFS

בקשות של מרחב משתמש FUSE

בתחילה, FUSE שימש להפעלת אמולציית אחסון ולאפשר לאפליקציות להשתמש באופן שקוף באחסון הפנימי או בכרטיס SD חיצוני. השימוש ב-FUSE מוסיף תקורה מסוימת, כי כל בקשה במרחב המשתמשים עוברת בנתיב הבא: מרחב המשתמשים ← VFS ← מנהל התקן של FUSE ← דמון FUSE ← VFS ← ext4 ← מטמון דפים/אחסון.

FUSE Passthrough FUSE

איור 2. בקשות של מרחב משתמש FUSE

בקשות FUSE passthrough

רוב ההרשאות לגישה לקובץ נבדקות בזמן פתיחת הקובץ, ובדיקות הרשאות נוספות מתבצעות כשקוראים את הקובץ וכשכותבים בו. במקרים מסוימים, אפשר לדעת בזמן פתיחת הקובץ שלאפליקציה ששולחת את הבקשה יש גישה מלאה לקובץ המבוקש, ולכן המערכת לא צריכה להמשיך להעביר את בקשות הקריאה והכתיבה ממנהל ההתקן של FUSE אל דמון FUSE (כי זה רק יעביר נתונים ממקום אחד למקום אחר).

ב-FUSE passthrough, דמון FUSE שמטפל בבקשה פתוחה יכול להודיע למנהל ההתקן של FUSE שהפעולה מותרת ושאפשר להעביר ישירות למערכת הקבצים התחתונה את כל בקשות הקריאה והכתיבה הבאות. כך נמנעים מהתקורה הנוספת של המתנה לתגובה של דימון FUSE במרחב המשתמשים לבקשות של מנהל ההתקן FUSE.

בהמשך מוצגת השוואה בין בקשות FUSE לבין בקשות FUSE passthrough.

השוואה בין העברות FUSE

איור 3. בקשת FUSE לעומת בקשת FUSE passthrough

כשמבצעים גישה למערכת קבצים של FUSE דרך אפליקציה, מתבצעות הפעולות הבאות:

  1. מנהל ההתקן של FUSE מטפל בבקשה ומכניס אותה לתור, ואז מציג אותה לדמון של FUSE שמטפל במערכת הקבצים של FUSE דרך מופע חיבור ספציפי בקובץ /dev/fuse, שהדמון של FUSE חסום מקריאה שלו.

  2. כשדמון FUSE מקבל בקשה לפתוח קובץ, הוא מחליט אם צריך להפעיל את FUSE passthrough עבור הקובץ הספציפי הזה. אם הוא זמין, הדמון:

    1. הודעה על הבקשה הזו נשלחת ל-FUSE driver.

    2. ההגדרה מאפשרת העברה של קובץ באמצעות FUSE באמצעות FUSE_DEV_IOC_PASSTHROUGH_OPEN ioctl, שצריך לבצע על מתאר הקובץ של /dev/fuse שנפתח.

  3. ה-ioctl מקבל (כפרמטר) מבנה נתונים שמכיל את הפרטים הבאים:

    • מתאר הקובץ של הקובץ במערכת הקבצים התחתונה, שמוגדר כיעד לתכונת ההעברה.

    • מזהה ייחודי של בקשת FUSE שמטופלת כרגע (צריך להיות פתוח או ליצור ולפתוח).

    • שדות נוספים שאפשר להשאיר ריקים ומיועדים ליישומים עתידיים.

  4. אם ioctl מצליח, דמון FUSE משלים את בקשת הפתיחה, מנהל ההתקן של FUSE מטפל בתשובה של דמון FUSE, והפניה לקובץ במערכת הקבצים התחתונה מתווספת לקובץ FUSE בתוך ליבת המערכת. כשאפליקציה מבקשת פעולת קריאה או כתיבה בקובץ FUSE, מנהל ההתקן של FUSE בודק אם ההפניה לקובץ במערכת קבצים נמוכה יותר זמינה.

    • אם יש הפניה זמינה, מנהל ההתקן יוצר בקשה חדשה של מערכת קבצים וירטואלית (VFS) עם אותם פרמטרים שמטרגטים את הקובץ של מערכת הקבצים התחתונה.

    • אם אין הפניה זמינה, הנהג מעביר את הבקשה לדמון FUSE.

הפעולות שלמעלה מתרחשות בקריאה/כתיבה ובקריאה-איטרציה/כתיבה-איטרציה בקבצים גנריים, ובפעולות קריאה/כתיבה בקבצים שמופים לזיכרון. העברת FUSE לקובץ מסוים קיימת עד שהקובץ נסגר.

הטמעה של העברה ב-FUSE

כדי להפעיל העברה של FUSE במכשירים עם Android 12, מוסיפים את השורות הבאות לקובץ $ANDROID_BUILD_TOP/device/…/device.mk של מכשיר היעד.

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

כדי להשבית את העברת FUSE, משמיטים את שינוי ההגדרה שלמעלה או מגדירים את persist.sys.fuse.passthrough.enable ל-false. אם הפעלתם בעבר את FUSE passthrough, השבתתו תמנע מהמכשיר להשתמש ב-FUSE passthrough, אבל המכשיר ימשיך לפעול.

כדי להפעיל או להשבית את FUSE passthrough בלי להפעיל את המכשיר, משנים את מאפיין המערכת באמצעות פקודות ADB. דוגמה:

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

לקבלת עזרה נוספת, אפשר לעיין ביישום ההפניה.

אימות של העברת FUSE

כדי לוודא ש-MediaProvider משתמש ב-FUSE passthrough, בודקים את 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 passthrough.

‫Android 12 CTS כולל את CtsStorageTest, שכולל בדיקות שמפעילות העברה של FUSE. כדי להריץ את הבדיקה באופן ידני, משתמשים ב-atest כמו בדוגמה הבאה:

atest CtsStorageTest