Este documento explica como identificar e resolver problemas de bloqueios de barramento de CPU em sistemas operacionais convidados do Linux. Ele aborda os sintomas de bloqueios de barramento de CPU, como diagnosticar esses problemas usando mensagens de registro do kernel, como localizar o código com falha e como aplicar mitigações ou correções.
Visão geral
Um bloqueio de barramento de CPU ocorre quando um processador precisa declarar um sinal de hardware LOCK# para adquirir acesso exclusivo ao barramento de memória de todo o sistema. Esse problema geralmente acontece em uma das seguintes circunstâncias:
- Uma instrução atômica opera em memória não alinhada que cruza um limite de linha de cache (um bloqueio dividido).
- Uma instrução atômica opera em memória designada como
uncacheable(UC), comoMemory-Mapped I/O(MMIO).
Como a CPU declara um bloqueio de barramento global, todos os outros processadores e dispositivos no sistema operacional convidado precisam esperar enquanto a operação de memória é concluída. Uma alta taxa de bloqueios de barramento pode degradar muito o desempenho da CPU.
Embora os processadores mais antigos não rastreassem bloqueios de barramento, os processadores x86 modernos, como Intel Sapphire Rapids e versões mais recentes ou AMD Zen 5 e versões mais recentes, incluem um recurso de hardware que detecta bloqueios de barramento de CPU. Quando uma instrução aciona um bloqueio de barramento de CPU, a CPU emite uma exceção de depuração (#DB) imediatamente após a conclusão da instrução.
A partir da versão 5.13 do kernel do Linux no Intel e da versão 6.13 no AMD, o kernel do Linux intercepta essa exceção #DB e aplica uma mitigação, normalmente limitando a taxa do processo com falha. Ao forçar intencionalmente a suspensão da linha de execução, o kernel impede que um único aplicativo sature o barramento de memória, preservando o desempenho do sistema para o restante da instância de computação ao custo do desempenho do aplicativo com falha.
Sintomas
Se um processo no convidado do Linux estiver acionando bloqueios de barramento de CPU, você poderá ter os seguintes sintomas:
- Desempenho do aplicativo degradado: os bloqueios de barramento de CPU podem introduzir latência não antecipada para aplicativos.
- Picos de carga em todo o sistema: a capacidade de resposta geral do sistema pode diminuir.
- Falhas inesperadas do aplicativo: se você configurar o kernel para lidar com bloqueios divididos ou de barramento estritamente (
split_lock_detect=fatal), o aplicativo com falha poderá falhar com um erroSIGBUS.
Identificar bloqueios de barramento de CPU
Para identificar se a instância de computação está sofrendo bloqueios de barramento de CPU, faça uma das seguintes ações:
- Se você ativou a geração de registros de saída da porta serial para a instância de computação, analise a saída da porta serial para encontrar um rastreamento de bloqueio de barramento de CPU.
- Analise os registros do sistema operacional da instância de computação (
/var/log/messages) para encontrar um rastreamento de bloqueio de barramento de CPU.
Exemplo de rastreamento de bloqueio de barramento de CPU
x86/split lock detection: #DB: <process_name>/<pid> took a bus_lock trap at
address: 0x<address>
Para detectar futuros bloqueios de barramento de CPU, faça o seguinte:
- Ative a geração de registros de saída da porta serial.
Crie uma política de alertas com base em registros para o seguinte registro:
resource.type="gce_instance" log_id("serialconsole.googleapis.com/serial_port_1_output") textPayload=~"took a bus_lock trap"Essa entrada de registro fornece o nome do processo (
<process_name>) e o ID do processo (<pid>) responsável pelo bloqueio de barramento de CPU, bem como o endereço do ponteiro de instrução em que a falha ocorreu.
Resolver problemas de bloqueios de barramento de CPU
Se você estiver desenvolvendo ou compilando o aplicativo com falha, poderá usar avisos específicos do compilador C ou C++ para identificar variáveis e estruturas que possam causar bloqueios divididos.
Avisos do compilador
Se você usar GCC ou Clang, compile o código com as seguintes flags para ajudar a identificar problemas de alinhamento:
-Wcast-alignou-Wcast-align=strict: essas flags avisam quando uma conversão de ponteiro aumenta o alinhamento necessário do destino. A conversão de um bufferchar*genérico em umuint64_t*e a execução de uma operação atômica nele é uma causa clássica de bloqueios divididos.-Waddress-of-packed-member: essa flag avisa quando você usa o endereço de um membro de struct compactado (por exemplo, usando#pragma pack(1)ou__attribute__((packed))). Como as estruturas compactadas ignoram o alinhamento de memória natural, qualquer operação atômica em um membro de uma struct compactada tem uma alta probabilidade de cruzar um limite de linha de cache de 64 bytes.
Como detectar bloqueios de memória não armazenáveis em cache (UC)
Se as operações atômicas em memória não armazenável em cache causarem o bloqueio de barramento de CPU, os avisos do compilador não vão detectá-lo. Esse problema geralmente acontece ao interagir com a memória do dispositivo:
- Auditar mapeamentos de memória: analise seu código para usos de
mmapcom flags comoO_SYNCou acesso direto a/dev/memou/dev/uio. - Evite operações atômicas em MMIO: não use operações atômicas como
__sync_fetch_and_addoustd::atomicem regiões de memória mapeadas para registros de dispositivos ou buffers de memória não armazenáveis em cache.
Como corrigir bloqueios de barramento de CPU
É possível corrigir problemas de bloqueio de barramento de CPU corrigindo o alinhamento de memória no código-fonte do aplicativo.
- Evite usar
#pragma packou__attribute__((packed))em estruturas que contenham variáveis atômicas, mutexes ou spinlocks. - Use diretivas de alinhamento padrão (como
alignas(64)em C++11 ou__attribute__((aligned(64)))em C) para forçar variáveis muito usadas em operações atômicas a se alinharem aos limites da linha de cache. - Verifique se não há avisos relacionados ao alinhamento durante a compilação.
- Use apenas mecanismos de bloqueio padrão (mutexes, spinlocks) ou instruções atômicas em RAM padrão armazenável em cache, nunca em memória MMIO ou UC.
Se as etapas de solução de problemas não resolverem o problema, então entre em contato com o Cloud Customer Care e inclua todas as informações coletadas durante a solução de problemas.