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. In genere, questo problema si verifica in una delle seguenti circostanze:

  • Un'istruzione atomica opera su una memoria non allineata che attraversa un limite della riga della cache (un blocco diviso).
  • 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. Una frequenza elevata di blocchi del bus potrebbe ridurre notevolmente le prestazioni della CPU.

Mentre i processori meno recenti non tenevano traccia dei 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, la CPU genera un'eccezione di debug (#DB) immediatamente dopo il completamento dell'istruzione.

A partire dalla versione del kernel Linux 5.13 su Intel e 6.13 su AMD, il kernel Linux intercetta questa eccezione #DB e applica una mitigazione, in genere limitando la frequenza del processo difettoso. Forzando intenzionalmente il thread a sospendere, 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 i blocchi del bus della CPU, potresti riscontrare i seguenti sintomi:

  • Prestazioni dell'applicazione 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 imprevisti dell'applicazione: se configuri il kernel per gestire rigorosamente i blocchi divisi o del bus (split_lock_detect=fatal), l'applicazione difettosa potrebbe arrestarsi in modo anomalo con un errore SIGBUS.

Identificare i blocchi del bus della CPU

Per verificare se l'istanza di calcolo sta riscontrando blocchi del bus della CPU, svolgi una delle seguenti operazioni:

Esempio di traccia di blocco del bus della CPU

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

Per rilevare i futuri blocchi del bus della CPU:

  1. Abilita il logging dell'output della porta seriale.
  2. Crea un criterio di avviso basato sui 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 del processo (<pid>) responsabile del blocco del bus della CPU, nonché l'indirizzo del puntatore di istruzione in cui si è verificato l'errore.

Risolvere i problemi relativi ai blocchi del bus della CPU

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

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 a un uint64_t* e l'esecuzione di un'operazione atomica su di esso è una causa classica di blocchi divisi.
  • -Waddress-of-packed-member: questo flag ti avvisa quando prendi l'indirizzo di un membro di una struttura compressa (ad esempio, utilizzando #pragma pack(1) o __attribute__((packed))). Poiché le strutture compresse ignorano l'allineamento naturale della memoria, qualsiasi operazione atomica su un membro di una struttura compressa ha un'alta probabilità di attraversare un limite della riga della cache di 64 byte.

Rilevare i 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 rileveranno. In genere, questo problema si verifica quando si interagisce 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 ai registri dei dispositivi o buffer di memoria non memorizzabili nella cache.

Risolvere i problemi relativi ai blocchi del bus della CPU

Puoi risolvere i problemi relativi ai blocchi 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 le direttive di allineamento standard (ad esempio alignas(64) in C++11 o __attribute__((aligned(64))) in C) per forzare l'allineamento delle variabili utilizzate di frequente nelle operazioni atomiche ai limiti della riga della cache.
  • Assicurati che non siano presenti avvisi relativi all'allineamento durante la compilazione.
  • Assicurati di utilizzare solo meccanismi di blocco standard (mutex, spinlock) o istruzioni atomiche su RAM standard memorizzabili nella cache, mai su memoria MMIO o UC.

Se i passaggi per la risoluzione dei problemi non hanno risolto il problema, allora contatta l'assistenza clienti Google Cloud e includi tutte le informazioni raccolte durante la risoluzione dei problemi.