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

במסמך הזה מוסבר איך לזהות ולפתור בעיות שקשורות לנעילות של אוטובוס 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 הזו ומיישמת אמצעי להפחתת הסיכון, בדרך כלל על ידי הגבלת קצב של התהליך הפגום. על ידי הכרחת התהליך לישון, ליבת המערכת מונעת מאפליקציה אחת להעמיס על אפיק הזיכרון, וכך משמרת את ביצועי המערכת עבור שאר מופע המחשוב, על חשבון ביצועי האפליקציה הפגומה.

תסמינים

אם תהליך במערכת האורחת של 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)

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

  • ביקורת על מיפויים של זיכרון: בודקים את הקוד כדי לראות אם נעשה שימוש ב-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 ולכלול את כל המידע שאספתם במהלך פתרון הבעיה.