Résoudre les problèmes de blocage du bus du processeur

Ce document explique comment identifier et résoudre les problèmes de verrouillage du bus du processeur dans les systèmes d'exploitation invités Linux. Il aborde les symptômes des verrouillages du bus du processeur, explique comment diagnostiquer ces problèmes à l'aide des messages du journal du noyau, comment localiser le code défectueux et comment appliquer des mesures d'atténuation ou des correctifs.

Présentation

Un verrouillage du bus du processeur se produit lorsqu'un processeur doit affirmer un signal matériel LOCK# pour obtenir un accès exclusif au bus de mémoire à l'échelle du système. Ce problème se produit généralement dans l'un des cas suivants :

  • Une instruction atomique fonctionne sur une mémoire non alignée qui franchit une limite de ligne de cache (un verrouillage fractionné).
  • Une instruction atomique fonctionne sur la mémoire désignée comme uncacheable (UC), telle que Memory-Mapped I/O (MMIO).

Étant donné que le processeur affirme un verrouillage global du bus, tous les autres processeurs et appareils du système d'exploitation invité doivent attendre la fin de l'opération de mémoire. Un taux élevé de verrouillages de bus peut dégrader considérablement les performances du processeur.

Alors que les processeurs plus anciens ne suivaient pas les verrouillages de bus, les processeurs x86 modernes tels qu'Intel Sapphire Rapids et les versions ultérieures, ou AMD Zen 5 et les versions ultérieures, incluent une fonctionnalité matérielle qui détecte les verrouillages de bus du processeur. Lorsqu'une instruction déclenche un verrouillage du bus du processeur, celui-ci émet une exception de débogage (#DB) immédiatement après l'exécution de l'instruction.

À partir de la version 5.13 du noyau Linux sur Intel et de la version 6.13 sur AMD, le noyau Linux intercepte cette exception #DB et applique une atténuation, généralement en limitant le taux du processus défectueux. En forçant intentionnellement le thread à se mettre en veille, le noyau empêche une seule application de saturer le bus de mémoire, ce qui préserve les performances du système pour le reste de l'instance de calcul au détriment des performances de l'application défectueuse.

Symptômes

Si un processus dans votre invité Linux déclenche des verrouillages de bus CPU, vous pouvez rencontrer les symptômes suivants :

  • Dégradation des performances des applications : les verrouillages de bus de processeur peuvent entraîner une latence inattendue pour les applications.
  • Pics de charge à l'échelle du système : la réactivité globale du système peut diminuer.
  • Plantages inattendus d'applications : si vous configurez le noyau pour qu'il gère strictement les verrous fractionnés ou de bus (split_lock_detect=fatal), l'application défectueuse peut planter avec une erreur SIGBUS.

Identifier les verrouillages du bus du processeur

Pour déterminer si votre instance de calcul est confrontée à des blocages de bus de processeur, employez l'une de ces méthodes :

Exemple de trace de verrouillage du bus du processeur

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

Pour détecter les futurs blocages de bus CPU, procédez comme suit :

  1. Activez la journalisation en sortie du port série.
  2. Créez une règle d'alerte basée sur les journaux pour le journal suivant :

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

    Cette entrée de journal vous indique le nom du processus (<process_name>) et l'ID du processus (<pid>) responsable du verrouillage du bus du processeur, ainsi que l'adresse du pointeur d'instruction où l'erreur s'est produite.

Résoudre les problèmes de blocage du bus du processeur

Si vous développez ou compilez l'application défectueuse, vous pouvez utiliser des avertissements de compilateur C ou C++ spécifiques pour identifier les variables et les structures susceptibles de provoquer des verrous fractionnés.

Avertissements du compilateur

Si vous utilisez GCC ou Clang, compilez votre code avec les indicateurs suivants pour identifier les problèmes d'alignement :

  • -Wcast-align ou -Wcast-align=strict : ces indicateurs vous avertissent lorsqu'un cast de pointeur augmente l'alignement requis de la cible. Le fait de caster un tampon char* générique en uint64_t* et d'effectuer une opération atomique sur celui-ci est une cause classique de verrouillage fractionné.
  • -Waddress-of-packed-member : cet indicateur vous avertit lorsque vous récupérez l'adresse d'un membre de structure compressée (par exemple, à l'aide de #pragma pack(1) ou __attribute__((packed))). Étant donné que les structures compressées ne tiennent pas compte de l'alignement naturel de la mémoire, toute opération atomique sur un membre d'une structure compressée a une forte probabilité de franchir une limite de ligne de cache de 64 octets.

Détecter les verrous de mémoire non cachables (UC)

Si les opérations atomiques sur la mémoire non cachable entraînent le verrouillage du bus du processeur, les avertissements du compilateur ne le détecteront pas. Ce problème se produit généralement lorsque vous interagissez avec la mémoire de l'appareil :

  • Auditez les mappages de mémoire : examinez votre code pour détecter les utilisations de mmap avec des indicateurs tels que O_SYNC ou l'accès direct à /dev/mem ou /dev/uio.
  • Évitez les opérations atomiques sur MMIO : n'utilisez pas d'opérations atomiques telles que __sync_fetch_and_add ou std::atomic sur les régions de mémoire mappées sur les registres de périphériques ou les tampons de mémoire non cachables.

Résoudre les blocages de bus du processeur

Pour résoudre les problèmes de verrouillage du bus du processeur, corrigez l'alignement de la mémoire dans le code source de l'application.

  • Évitez d'utiliser #pragma pack ou __attribute__((packed)) sur les structures qui contiennent des variables atomiques, des mutex ou des spinlocks.
  • Utilisez des directives d'alignement standards (comme alignas(64) en C++11 ou __attribute__((aligned(64))) en C) pour forcer les variables fortement utilisées dans les opérations atomiques à s'aligner sur les limites des lignes de cache.
  • Assurez-vous qu'aucun avertissement lié à l'alignement ne s'affiche lors de la compilation.
  • Assurez-vous de n'utiliser que des mécanismes de verrouillage standards (mutex, spinlocks) ou des instructions atomiques sur de la RAM standard et cachable, jamais sur de la mémoire MMIO ou UC.

Si les étapes de dépannage n'ont pas permis de résoudre le problème, contactez Cloud Customer Care en incluant toutes les informations que vous avez recueillies lors de votre tentative de dépannage.