פתרון בעיות שקשורות לנעילות של אוטובוס המעבד

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

סקירה כללית

נעילת אפיק CPU מתרחשת כשמעבד צריך לאשר אות חומרה LOCK# כדי לקבל גישה בלעדית לאפיק הזיכרון של המערכת. הבעיה הזו מתרחשת בדרך כלל באחד מהמקרים הבאים:

  • הוראה אטומית פועלת על זיכרון לא מיושר שחוצה גבול של שורת מטמון (נעילה מפוצלת).
  • הוראה אטומית פועלת בזיכרון שמסומן כ-uncacheable (UC), כמו Memory-Mapped I/O (MMIO).

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

מעבדים ישנים יותר לא עקבו אחרי נעילות של אפיקי נתונים, אבל מעבדי x86 מודרניים, כמו Intel Sapphire Rapids ואילך, או AMD Zen 5 ואילך, כוללים תכונת חומרה שמזהה נעילות של אפיקי נתונים של מעבד. כשפקודה מפעילה נעילה של אפיק CPU, המעבד מנפיק חריגת ניפוי באגים (#DB) מיד אחרי שהפקודה מסתיימת.

החל מגרסת ליבת Linux‏ 5.13 ב-Intel ו-6.13 ב-AMD, ליבת Linux מיירטת את חריגת #DB הזו ומיישמת אמצעי להפחתת הסיכון, בדרך כלל על ידי הגבלת קצב התהליך הפגום. על ידי הכרחת התהליך לישון, ליבת המערכת מונעת מאפליקציה יחידה להעמיס על אפיק הזיכרון, וכך שומרת על ביצועי המערכת עבור שאר מופע ה-Compute, על חשבון ביצועי האפליקציה הפגומה.

תסמינים

אם תהליך במערכת האורחת של Linux מפעיל נעילות של אוטובוס המעבד, יכול להיות שתיתקלו בתסמינים הבאים:

  • ביצועי אפליקציה פחות טובים: נעילות של אוטובוס המעבד עלולות לגרום לחביון לא צפוי באפליקציות.
  • עליות פתאומיות בעומס על המערכת: יכול להיות שהיענות המערכת הכוללת תרד.
  • קריסות לא צפויות של אפליקציות: אם מגדירים את ליבת המערכת לטפל בפיצול או בנעילות של אפיק הנתונים באופן קפדני (split_lock_detect=fatal), יכול להיות שהאפליקציה הפגומה תקרוס עם שגיאה SIGBUS.

זיהוי נעילות של אוטובוס המעבד

כדי לזהות אם מופעלים נעילות של אפיק ה-CPU במופע החישוב, מבצעים אחת מהפעולות הבאות:

דוגמה למעקב אחר נעילת אפיק נתונים של מעבד

x86/split lock detection: #DB: <process_name>/<pid> took a bus_lock trap at address: 0x<address>

כדי לזהות נעילות של אוטובוס CPU בעתיד:

  1. הפעלת רישום ביומן של פלט היציאה הטורית
  2. יצירת מדיניות התראות שמבוססת על יומן עבור היומן הבא:

    resource.type="gce_instance" log_id("serialconsole.googleapis.com/serial_port_1_output") textPayload=~"took a bus_lock trap"

    רשומת היומן הזו כוללת את שם התהליך (<process_name>) ואת מזהה התהליך (<pid>) שאחראי לנעילת אפיק ה-CPU, וגם את כתובת מצביע ההוראה שבה התרחשה התקלה.

פתרון בעיות שקשורות לנעילות של אוטובוס המעבד

אם אתם מפתחים או מהדרים את האפליקציה הפגומה, אתם יכולים להשתמש באזהרות ספציפיות של מהדר C או C++‎ כדי לזהות משתנים ומבנים שעשויים לגרום לנעילות מפוצלות.

אזהרות של קומפיילר

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

  • -Wcast-align או -Wcast-align=strict: הדגלים האלה מציגים אזהרה כששינוי של מצביע מגדיל את היישור הנדרש של היעד. העברה של מאגר זמני גנרי char* ל-uint64_t* וביצוע פעולה אטומית עליו היא סיבה קלאסית לפיצול נעילות.
  • -Waddress-of-packed-member: הדגל הזה מציג אזהרה כשמבצעים פעולה על כתובת של חבר במבנה דחוס (לדוגמה, באמצעות #pragma pack(1) או __attribute__((packed))). מכיוון שמבנים דחוסים מתעלמים מיישור טבעי של הזיכרון, לכל פעולה אטומית על חבר במבנה דחוס יש סיכוי גבוה לחצות גבול של שורת מטמון של 64 בייט.

איתור נעילות זיכרון שלא ניתן לשמור במטמון (UC)

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

  • בודקים את מיפוי הזיכרון: בודקים את הקוד כדי לראות אם נעשה שימוש ב-mmap עם פלאגים כמו O_SYNC או גישה ישירה ל-/dev/mem או ל-/dev/uio.
  • לא מומלץ להשתמש בפעולות אטומיות ב-MMIO: אל תשתמשו בפעולות אטומיות כמו __sync_fetch_and_add או std::atomic באזורי זיכרון שממופים לרישום של מכשירים או למאגרי זיכרון שלא ניתן לשמור במטמון.

תיקון נעילות של אוטובוסים במעבד

כדי לפתור בעיות של נעילת אוטובוס CPU, צריך לתקן את יישור הזיכרון בקוד המקור של האפליקציה.

  • מומלץ להימנע משימוש ב-#pragma pack או ב-__attribute__((packed)) במבנים שמכילים משתנים אטומיים, mutex או spinlock.
  • כדי לאלץ משתנים שנמצאים בשימוש נרחב בפעולות אטומיות להתיישר לגבולות של שורות מטמון, משתמשים בהנחיות יישור רגילות (כמו alignas(64) ב-C++11 או __attribute__((aligned(64))) ב-C).
  • מוודאים שאין אזהרות שקשורות ליישור במהלך ההידור.
  • חשוב להקפיד להשתמש רק במנגנוני נעילה רגילים (mutexes, spinlocks) או בהוראות אטומיות ב-RAM רגיל שניתן לשמור במטמון, ולא בזיכרון MMIO או UC.

אם הבעיה לא נפתרה בעזרת השלבים לפתרון בעיות, פנו אל Cloud Customer Care וצרפו את כל המידע שאספתם במהלך פתרון הבעיות.