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 esempioMemory-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 erroreSIGBUS.
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:
- Se hai abilitato il logging dell'output della porta seriale per l'istanza di computing, esamina l'output della porta seriale per una traccia di blocco del bus della CPU.
- Esamina i log del sistema operativo dell'istanza di calcolo (
/var/log/messages) per una traccia di blocco del bus della CPU.
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:
- Abilita il logging dell'output della porta seriale.
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-aligno-Wcast-align=strict: questi flag ti avvisano quando un cast di puntatore aumenta l'allineamento richiesto della destinazione. Il cast di un bufferchar*generico a unuint64_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
mmapcon flag comeO_SYNCo l'accesso diretto a/dev/memo/dev/uio. - Evita le operazioni atomiche su MMIO: non utilizzare operazioni atomiche come
__sync_fetch_and_addostd::atomicsu 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 packo__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.