סקירה כללית על בדיקת A/B וירטואלית

בדיקת A/B וירטואלית היא מנגנון העדכון הראשי של Android. בדיקות A/B וירטואליות מבוססות על עדכוני A/B מדור קודם (ראו עדכוני מערכת A/B) ועל עדכונים שאינם A/B, שהוצאו משימוש בגרסה 15 כדי לצמצם את שטח האחסון שנדרש לעדכונים.

למעשה, לניסוי A/B וירטואלי אין מקום נוסף למחיצות דינמיות. אפשר לקרוא מידע נוסף במאמר מחיצות דינמיות. במקום זאת, השינוי נכתב בתמונה מיידית (snapshot) ולאחר מכן מתמזג עם המחיצה הבסיסית אחרי שמוודאים שההפעלה בוצעה בהצלחה. בבדיקה הווירטואלית של A/B נעשה שימוש בפורמט snapshot ספציפי ל-Android. כדאי לעיין במאמר פורמט COW לקובצי snapshot דחוסים, שמאפשר לדחוס קובצי snapshot ולצמצם את השימוש במקום בדיסק. ב-OTA מלא, גודל קובץ ה-snapshot מצטמצם בכ-45% לאחר דחיסה, וב-OTA מצטמצם בכ-55%.

ב-Android 12 יש אפשרות לדחיסה וירטואלית של בדיקות A/B כדי לדחוס מחיצות של קובצי snapshot. בניסוי A/B וירטואלי אפשר:

  • עדכוני A/B וירטואליים מתבצעים ללא הפרעה (העדכון מתבצע לגמרי ברקע בזמן שהמכשיר פועל), כמו עדכוני A/B. עדכוני A/B וירטואליים מקצרים את הזמן שבו המכשיר במצב אופליין ולא ניתן לשימוש.
  • אפשר לבטל עדכוני A/B וירטואליים. אם מערכת ההפעלה החדשה לא תצליח להפעיל את המכשיר, הוא יחזור לגרסה הקודמת באופן אוטומטי.
  • עדכוני A/B וירטואליים משתמשים במינימום מקום נוסף על ידי שכפול רק של המחיצות שבהן bootloader משתמש. מחיצות אחרות שניתן לעדכן נשמרות בקובץ snapshot.

רקע וטרמינולוגיה

בקטע הזה מוסבר מהי המונחולוגיה ומתוארת הטכנולוגיה שתומכת בבדיקות A/B וירטואליות. במהלך התקנה דרך OTA, נתוני מערכת ההפעלה החדשים נכתבים בחריץ החדש שלהם למחיצות פיזיות, או במכשיר COW ספציפי ל-Android. אחרי הפעלה מחדש של המכשיר, נתוני המחיצות הדינמיות מוזגו חזרה למכשיר הבסיסי באמצעות השימוש ב-dm-user וב-snapuserd daemon. התהליך הזה מתרחש כולו במרחב המשתמש.

Device-mapper

Device-mapper הוא שכבת בלוקים וירטואלית של Linux שמשמשת לעיתים קרובות ב-Android. במחיצות דינמיות, מחיצות כמו /system הן מקבץ של מכשירים בשכבות:

  • בחלק התחתון של הסטאק נמצא המחיצה הפיזית super (לדוגמה, /dev/block/by-name/super).
  • במרכז מופיע מכשיר dm-linear, שמציין אילו בלוקים במחיצה העל-עליונה יוצרים את המחיצה הדינמית הנתונה. הערך הזה מופיע כ-/dev/block/mapper/system_[a|b] במכשיר A/B או כ-/dev/block/mapper/system במכשיר שאינו A/B.
  • בחלק העליון מופיע מכשיר dm-verity שנוצר למחיצות מאומתות. המכשיר הזה מאמת שהחסימות במכשיר dm-linear חתומות בצורה נכונה. הוא מופיע בתור /dev/block/mapper/system-verity והוא המקור של נקודת הטעינה /system.

איך נראה הסטאק מתחת לנקודת הטעינה /system מוצג באיור 1.

יצירת מחיצות מתחת למערכת

איור 1. סטאק מתחת לנקודת הטעינה ‎ /system

קובצי snapshot דחוסים

ב-Android מגרסה 12 ואילך, בגלל שדרישות האחסון במחיצה /data יכולות להיות גבוהות, אפשר להפעיל קובצי snapshot דחוסים ב-build כדי לעמוד בדרישות האחסון הגבוהות יותר של המחיצה /data.

קובצי snapshot וירטואליים דחוסים של A/B מבוססים על הרכיבים הבאים, שזמינים ב-Android מגרסה 12 ואילך:

  • dm-user, מודול ליבה שדומה ל-FUSE ומאפשר למרחב המשתמש להטמיע התקני בלוקים.
  • snapuserd, דימון של מרחב המשתמש להטמעת פורמט חדש של קובץ snapshot.

הרכיבים האלה מאפשרים את הדחיסה. השינויים האחרים שנדרשו כדי להטמיע את היכולות של קובצי snapshot דחוסים מפורטים בקטעים הבאים: פורמט COW לתמונות מצב דחוסות, dm-user ו-snapuserd.

פורמט COW עבור קובצי snapshot דחוסים

ב-Android 12 ואילך, קובצי snapshot דחוסים משתמשים בפורמט COW ספציפי ל-Android. פורמט COW מכיל מטא-נתונים על ה-OTA, ויש לו מאגרים נפרדים שמכילים פעולות COW ונתונים חדשים של מערכת ההפעלה. בהשוואה לפורמט של קובץ snapshot של הליבה, שמאפשר רק פעולות replace (החלפת בלוק X בתמונה הבסיסית בתוכן של בלוק Y בקובץ ה-snapshot), הפורמט COW של קובצי snapshot דחוסים של Android הוא פורמט גמיש יותר ותומך בפעולות הבאות:

  • העתקה: צריך להחליף את הבלוק X במכשיר הבסיס בבלוק Y במכשיר הבסיס.
  • החלפה: צריך להחליף את בלוק X במכשיר הבסיס בתוכן של בלוק Y ב-snapshot. כל אחד מהבלוקים האלה עבר דחיסת gz.
  • אפס: צריך להחליף את הבלוק X במכשיר הבסיס בכל אפסים.
  • XOR: מכשיר ה-COW מאחסן ביטים דחוסים של XOR בין בלוק X לבין בלוק Y. (התכונה זמינה ב-Android מגרסה 13 ואילך).

עדכונים מלאים דרך OTA מורכבים רק מפעולות replace ו-zero. בעדכונים מצטברים של OTA יכולות להיות גם פעולות העתקה.

הפריסה המלאה של קובץ snapshot בדיסק נראית כך:

פורמט פרה

איור 2. פורמט COW של Android בדיסק

dm-user

מודול הליבה dm-user מאפשר ל-userspace להטמיע מכשירי בלוק של device-mapper. רשומה בטבלה dm-user יוצרת מכשיר 'אחר' בקטע /dev/dm-user/<control-name>. תהליך userspace יכול לבצע סקירה של המכשיר כדי לקבל מהליבה בקשות קריאה וכתיבה. לכל בקשה יש מאגר משויך שמרחב המשתמש מאכלס (לקריאה) או מעביר (לכתיבה).

מודול הליבה dm-user מספק ממשק חדש של הליבה שגלוי למשתמשים, ולא נכלל בבסיס הקוד של kernel.org ב-upstream. עד אז, Google שומרת לעצמה את הזכות לשנות את הממשק של dm-user ב-Android.

snapuserd

הרכיב של מרחב המשתמש snapuserd ל-dm-user מטמיע דחיסת A/B וירטואלית. Snapuserd הוא דימון במרחב המשתמש שאחראי על הכתיבה והקריאה במכשירי Android COW. כל הקלט והפלט של קובץ ה-snapshot חייבים לעבור דרך השירות הזה. במהלך התקנה OTA, נתוני מערכת הפעלה חדשים נכתבים ב-snapshot על ידי snapuserd (עם דחיסה). כאן מתבצע גם הניתוח של המטא-נתונים והפריסה של נתוני הבלוק החדשים.

דחיסת XOR

במכשירים שמופעלת בהם מערכת ההפעלה Android מגרסה 13 ואילך, תכונת דחיסת XOR מופעלת כברירת מחדל ומאפשרת לשמור בייטים דחוסים ב-XOR בין בלוקים ישנים לבלוקים חדשים ב-snapshots של מרחב המשתמש. כשרק כמה בייטים בבלוק משתנים בעדכון A/B וירטואלי, סכימה של אחסון דחוס XOR משתמשת בפחות מקום מאשר סכימה ברירת המחדל של אחסון, כי בתמונות המצב לא מאוחסנים 4,000 בייטים מלאים. הפחתת הגודל של קובץ snapshot אפשרית כי נתוני XOR מכילים הרבה אפסים, וקל יותר לדחוס אותם מאשר נתוני בלוק גולמיים. במכשירי Pixel, דחיסת XOR מפחיתה את גודל קובץ ה-snapshot ב-25% עד 40%.

במכשירים שמשודרגים ל-Android מגרסה 13 ואילך, צריך להפעיל את דחיסת XOR. פרטים נוספים זמינים במאמר דחיסת XOR.

מיזוג קובצי snapshot

במכשירים שמריצים Android מגרסה 13 ואילך, תהליכי תמונת המצב והמיזוג של תמונת המצב ב-Virtual A/B compression מתבצעים על ידי הרכיב snapuserd במרחב המשתמש. במכשירים שמשודרגים ל-Android מגרסה 13 ואילך, צריך להפעיל את התכונה הזו. מידע נוסף זמין במאמר מיזוג של Userspace.

בהמשך מתואר תהליך דחיסת ה-A/B הווירטואלי:

  1. המסגרת מחברת את המחיצה /system למכשיר dm-verity, שממוקם מעל מכשיר dm-user. המשמעות היא שכל פעולת קלט/פלט ממערכת הקבצים ברמה הבסיסית (root) מנותבת אל dm-user.
  2. dm-user מפנה את הקלט/פלט לדימון snapuserd במרחב המשתמש, שמטפל בבקשת הקלט/פלט.
  3. בסיום פעולת המיזוג, המסגרת מרכזת את dm-verity מעל dm-linear (system_base) ומסירה את dm-user.

תהליך דחיסה וירטואלי של A/B

איור 3. תהליך דחיסת A/B וירטואלי

תהליך המיזוג של קובצי snapshot יכול להיפסק. אם המכשיר יופעל מחדש במהלך תהליך המיזוג, תהליך המיזוג ימשיך אחרי ההפעלה מחדש.

מעברים של Init

כשמפעילים את המכונה באמצעות קובצי snapshot דחוסים, השלב הראשון של init צריך להפעיל את snapuserd כדי לטעון את המחיצות. הבעיה היא: כשsepolicy נטען ומופעל, snapuserd מועבר להקשר הלא נכון ובקשות הקריאה שלו נכשלות, עם דחיות של selinux.

כדי לטפל בבעיה הזו, snapuserd עובר מעבר במקביל ל-init, באופן הבא:

  1. בשלב הראשון, init מפעיל את snapuserd מה-ramdisk ושומר בו מתאר קובץ פתוח במשתנה סביבה.
  2. בשלב הראשון, init מעביר את מערכת הקבצים ברמה הבסיסית למחיצה של המערכת, ואז מפעיל את העותק של init במערכת.
  3. עותק המערכת של init קורא את מדיניות האבטחה המשולבת למחרוזת.
  4. Init מפעיל את mlock() בכל הדפים שמגובים ב-ext4. לאחר מכן, הוא משבית את כל הטבלאות של device-mapper למכשירי snapshot, ומפסיק את snapuserd. לאחר מכן אסור לקרוא מהמחיצות, כי הפעולה הזו גורמת לנעילה מרובת משתמשים (deadlock).
  5. באמצעות מתאר הפתוח של העותק של snapuserd ב-ramdisk, init מפעיל מחדש את הדימון עם ההקשר הנכון של selinux. טבלאות Device-mapper למכשירי snapshot מופעלות מחדש.
  6. Init מפעיל את munlockall() – אפשר לבצע שוב פעולות IO.

שימוש במרחב המשותף

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

ההשפעה של הגודל ללא בדיקת A/B A/B בדיקת A/B וירטואלית בדיקת A/B וירטואלית (דחוסה)
Original Factory Image 4.5GB סופר (קובץ אימג' בנפח 3.8GB + 700MB שמורים)1 9GB סופר (3.8GB + 700MB שמורים, לשני משבצות) 4.5GB סופר (קובץ אימג' בנפח 3.8GB + 700M שמורים) 4.5GB סופר (קובץ אימג' בנפח 3.8GB + 700M שמורים)
מחיצות סטטיות אחרות /cache ללא ללא ללא
נפח אחסון נוסף במהלך עדכון OTA (השטח מוחזר אחרי החלת העדכון) 1.4GB ב-/data 0 3.8GB2 ב-/data 2.1GB‏2 ב-/data
נפח האחסון הכולל הנדרש כדי להחיל עדכון OTA 5.9GB‏3 (נתונים ו-Super) 9GB (סופר) 8.3GB‏3 (סופר ונתונים) 6.6GB‏3 (מהיר ונתונים)

1מצביע על פריסה משוערת על סמך מיפוי Pixel.

2ההנחה היא שתמונת המערכת החדשה באותו גודל כמו התמונה המקורית.

3דרישת המקום היא זמנית עד להפעלה מחדש.

Android 11 Virtual A/B

Android 11 של Virtual A/B כתב למחיצה דינמית באמצעות הפורמט Kernel COW. בסופו של דבר הפורמט הזה הוצא משימוש כי פורמט ה-COW של הליבה לא תומך בדחיסה.

Android 12 Virtual A/B

ב-Android 12 יש תמיכה בדחיסה בפורמט COW ספציפי ל-Android. בגרסה הזו של Virtual A/B נדרשה תרגום של ה-COW הספציפי ל-Android לפורמט Kernel COW. בסופו של דבר, הפורמט הזה הוחלף ב-Android 13, שבו הוסרה התלות בפורמט COW של הליבה וגם ב-dm-snapshot.

כדי להטמיע בדיקת A/B וירטואלית או להשתמש ביכולות של קובצי snapshot דחוסים, קראו את המאמר הטמעת בדיקת A/B וירטואלית