Risolvi i problemi relativi ai blocchi del bus della CPU

Questo documento spiega come identificare e risolvere i problemi relativi ai blocchi del bus della CPU nei sistemi operativi guest Linux. Vengono trattati i sintomi dei blocchi del bus della CPU, come diagnosticare questi problemi utilizzando i messaggi di log del kernel, come individuare il codice difettoso e come applicare mitigazioni o correzioni.

Panoramica

Un blocco del bus della CPU si verifica quando un processore deve asserire un segnale hardware LOCK# per acquisire l'accesso esclusivo al bus di memoria a livello di sistema. Questo problema si verifica in genere in una delle seguenti circostanze:

  • Un'istruzione atomica opera su una memoria non allineata che attraversa un limite di riga della cache (un blocco suddiviso).
  • Un'istruzione atomica opera sulla memoria designata come uncacheable (UC), ad esempio Memory-Mapped I/O (MMIO).

Poiché la CPU asserisce un blocco del bus globale, tutti gli altri processori e dispositivi nel sistema operativo guest devono attendere il completamento dell'operazione di memoria. Un tasso elevato di blocchi del bus potrebbe ridurre notevolmente le prestazioni della CPU.

Mentre i processori più vecchi non tracciavano i blocchi del bus, i moderni processori x86 come Intel Sapphire Rapids e versioni successive o AMD Zen 5 e versioni successive, includono una funzionalità hardware che rileva i blocchi del bus della CPU. Quando un'istruzione attiva un blocco del bus della CPU, quest'ultima genera un'eccezione di debug (#DB) immediatamente dopo il completamento dell'istruzione.

A partire dalla versione kernel 5.13 del kernel Linux su Intel e 6.13 su AMD, il kernel Linux intercetta questa eccezione #DB e applica una mitigazione, in genere limitando la velocità del processo difettoso. Forzando intenzionalmente il thread a sospendersi, il kernel impedisce a una singola applicazione di saturare il bus di memoria, preservando le prestazioni del sistema per il resto dell'istanza di computing a scapito delle prestazioni dell'applicazione difettosa.

Sintomi

Se un processo nel guest Linux attiva blocchi del bus della CPU, potresti riscontrare i seguenti sintomi:

  • Prestazioni delle applicazioni ridotte: i blocchi del bus della CPU potrebbero introdurre una latenza imprevista per le applicazioni.
  • Picchi di carico a livello di sistema: la reattività complessiva del sistema potrebbe diminuire.
  • Arresti anomali dell'applicazione: se configuri il kernel per gestire i blocchi split o bus in modo rigoroso (split_lock_detect=fatal), l'applicazione difettosa potrebbe arrestarsi in modo anomalo con un errore SIGBUS.

Identificare i blocchi del bus della CPU

Per identificare se la tua istanza di computing sta riscontrando blocchi del bus della CPU, svolgi una delle seguenti operazioni:

Esempio di traccia del blocco del bus della CPU

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

Per rilevare futuri blocchi del bus della CPU:

  1. Abilita il logging dell'output della porta seriale.
  2. Crea un criterio di avviso basato su log per il seguente log:

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

    Questa voce di log fornisce il nome del processo (<process_name>) e l'ID processo (<pid>) responsabile del blocco del bus della CPU, nonché l'indirizzo del puntatore dell'istruzione in cui si è verificato l'errore.

Risolvi i problemi relativi ai blocchi del bus della CPU

Se stai sviluppando o compilando l'applicazione difettosa, puoi utilizzare avvisi specifici del compilatore C o C++ per identificare variabili e strutture che potrebbero causare blocchi suddivisi.

Avvisi del compilatore

Se utilizzi GCC o Clang, compila il codice con i seguenti flag per identificare i problemi di allineamento:

  • -Wcast-align o -Wcast-align=strict: questi flag ti avvisano quando un cast di puntatore aumenta l'allineamento richiesto della destinazione. Il cast di un buffer char* generico in un uint64_t* e l'esecuzione di un'operazione atomica è una causa classica di blocchi suddivisi.
  • -Waddress-of-packed-member: questo flag ti avvisa quando prendi l'indirizzo di un membro di una struct compattata (ad esempio, utilizzando #pragma pack(1) o __attribute__((packed))). Poiché le struct compattate non tengono conto dell'allineamento naturale della memoria, qualsiasi operazione atomica su un membro di una struct compattata ha un'alta probabilità di superare un limite di riga della cache di 64 byte.

Rilevamento dei blocchi di memoria non memorizzabili nella cache (UC)

Se le operazioni atomiche sulla memoria non memorizzabile nella cache causano il blocco del bus della CPU, gli avvisi del compilatore non lo rilevano. Questo problema si verifica in genere quando interagisci con la memoria del dispositivo:

  • Controlla i mapping della memoria: esamina il codice per verificare l'utilizzo di mmap con flag come O_SYNC o l'accesso diretto a /dev/mem o /dev/uio.
  • Evita le operazioni atomiche su MMIO: non utilizzare operazioni atomiche come __sync_fetch_and_add o std::atomic su regioni di memoria mappate su registri del dispositivo o buffer di memoria non memorizzabili nella cache

Correzione dei blocchi del bus della CPU

Puoi risolvere i problemi di blocco del bus della CPU correggendo l'allineamento della memoria nel codice sorgente dell'applicazione.

  • Evita di utilizzare #pragma pack o __attribute__((packed)) su strutture che contengono variabili atomiche, mutex o spinlock.
  • Utilizza direttive di allineamento standard (come alignas(64) in C++11 o __attribute__((aligned(64))) in C) per forzare l'allineamento delle variabili molto utilizzate nelle operazioni atomiche ai limiti della riga della cache.
  • Assicurati che non ci siano avvisi relativi all'allineamento durante la compilazione.
  • Assicurati di utilizzare solo meccanismi di blocco standard (mutex, spinlock) o istruzioni atomiche su RAM standard memorizzabile nella cache, mai su memoria MMIO o UC.

Se la procedura per la risoluzione dei problemi non ha risolto il problema, allora contatta l'assistenza clienti Google Cloud e includi tutte le informazioni che hai raccolto durante la risoluzione dei problemi.