• Aucun résultat trouvé

Exemple d’utilisation d’un verrou d’exclusion mutuelle

ANNEXE II ARTICLE DE CONFÉRENCE SUR LA MÉTHODE VPI

Extrait 5.1 Exemple d’utilisation d’un verrou d’exclusion mutuelle

atomiques sont nécessaires, car la plupart des modifications de variables se font à l’aide de séquences lecture/modification/écriture (« read/modify/write » ou RMW en anglais). Les pro- cesseurs PowerPC ne sont pas dotés d’instructions atomiques réalisant des opérations RMW de manière ininterruptible. On peut considérer les opérations atomiques comme des « micro- verrous » autour d’une modification simple de variable en mémoire. Puisque les primitives de synchronisation et l’accès aux données partagées reposent souvent sur l’utilisation d’opéra- tions atomiques, nous devons en implémenter un ensemble suffisant. Par exemple, l’opération atomique nommée XMAtomicIncReturn dans XtratuM-PPC permet d’incrémenter de ma- nière atomique une variable et il est garanti que cette incrémentation sera unique et perceptible immédiatement par les autres processeurs, peu importe leur hiérarchie mémoire et l’état de leurs caches.

Nous avons implémenté les opérations atomiques autour du squelette fourni initialement dans XtratuM. La version LEON de XtratuM contient en effet un API d’opérations atomiques, mais ces dernières sont réalisées avec des mécanismes non atomiques puisque la véritable atomicité de ces opérations n’était pas nécessaire dans un contexte monocoeur. En analysant le code source du noyau, nous avons déterminé que plusieurs de ces opérations n’étaient même

pas utilisées. Cela nous a permis de redéfinir certaines des opérations de manière opportuniste sans affecter la compatibilité avec le reste de la base de code existante.

L’implémentation d’opérations atomiques sur une architecture donnée est habituellement une tâche difficile étant donné la complexité des interrelations entre le modèle de consistance faible pour la mémoire comme celui du PowerPC et l’effet des mécanismes de cohérence des caches sur le partage de données. Ces problèmes sont déjà bien connus et présentés dans les références déjà mentionnées à la section 2.7 de la revue de littérature. Dans le cas du PowerPC, deux mécanismes doivent être appliqués pour réaliser des opérations atomiques : les barrières et les instructions de lecture et écriture conditionnelles.

Les instructions barrières2 permettent d’ordonner les instructions d’écriture et de lecture en mémoire, qui autrement auraient pû être exécutées dans un ordre différent de celui spécifié dans le code en raison des caractéristiques de traitement non ordonné du modèle de consis- tance mémoire faible de l’architecture PowerPC. Elles permettent aussi d’assurer que toutes les instructions émises ont exécuté leurs effets secondaires au point barrière. La barrière de lecture/écriture sync a aussi l’effet de synchroniser la visibilité des changements entre les caches des différents coeurs selon le protocole de cohérence des caches. Cette instruction est particulièrement importante, car c’est le seul moyen d’assurer qu’une valeur écrite en mémoire est visible à tous les autres coeurs qui pourraient vouloir y accéder.

Les instructions de lecture et écriture conditionnelles ont la même sémantique que le mé- canisme classique d’atomicité par parties « LL/SC » (« Load Linked, Store Conditional ») originaire sur l’architecture MIPS [59, p. 257]. L’instruction PowerPC lwarx (« Load word and reserve indexed ») permet d’effectuer une lecture avec réservation d’un bloc de mémoire. Toutes les opérations qui modifient la valeur chargée doivent s’être exécutées avant l’exécu- 2. Les instructions sync, isync et eieio sont respectivement les barrières de lecture/écriture, d’exécution et d’entrée/sorties sur le PowerPC e600.

153

tion de l’instruction stwcx. (« Store word conditional indexed, recording condition »)3 qui réécrit en mémoire la valeur modifiée. L’instruction stwcx. échoue et enregistre un bit de condition si un autre coeur tente une écriture sur le bloc réservé. Le bit de condition peut être vérifié pour réessayer l’opération en boucle jusqu’au succès de l’écriture. L’utilisation judi- cieuse de ces deux instructions permet d’émuler n’importe quelle opération atomique. Cette catégorie d’instructions est implémentée avec de la logique additionnelle dans le module de cohérence mémoire et dans les caches sur l’architecture e600. Les caches doivent donc être activées et configurées en mode « write-back » pour permettre leur utilisation.

Nous avons basé notre implémentation des opérations atomiques sur celle du port PowerPC du noyau Linux. Cette implémentation est disponible en code source libre et elle est utilisée depuis plusieurs années, en plus d’avoir été révisée par une multitude d’experts. Le fichier original du noyau Linux (version 2.6.33.2) qui contient le code des opérations atomiques est arch/powerpc/include/asm/atomic.h. Nous avons adapté ces routines pour res- pecter l’API original de XtratuM et pour remplacer plusieurs dépendances envers le noyau Linux. Avant d’arrêter notre choix sur l’implémentation Linux, nous avons évalué l’implé- mentation de U-Boot, mais notre analyse a démontré que celle-ci n’était pas suffisante pour un environnement multicoeur en raison des patrons de barrières employés.

L’extrait 5.2 présente l’implémentation de l’opération atomique d’incrémentation XMAto- micIncReturn. On remarque dans l’extrait que l’incrémentation (instruction addic) est entourée d’une boucle d’atomicité. La boucle d’atomicité débute avec une lecture (instruction lwarx) et se termine par un bloc d’écriture conditionnelle (instructions stwcx. et bne-) qui force un nouvel essai de l’opération si l’écriture de la valeur modifiée échoue. Avant l’opé- ration atomique, on remarque une barrière mémoire (la macro SMP_LWSYNC, équivalente à syncdans notre cas) qui force l’exécution complète de toutes les opérations mémoires précé- 3. Il est important de noter que le point final (« . ») dans « stwcx. » fait partie de la syntaxe assembleur de l’instruction. Lorsque nous référerons à cette instruction, le point sera inclus. Il ne s’agit donc pas d’une erreur typographique.

dant l’opération atomique. Après l’opération atomique, on retrouve une barrière d’exécution (la macro SMP_ISYNC, équivalente à isync dans notre cas) qui force l’exécution complète de toutes les instructions dans les queues du processeur, incluant le stwcx. final, avant de continuer l’exécution du reste du flux d’instructions.

/**

* Atomically increases the counter and returns the new value *

* @param p_atomic - pointer to an opaque atomic counter * @return value of counter after atomic increase

*/

static __inline__ xm_s32_t XMAtomicIncReturn(xmAtomic_t *

p_atomic) { xm_s32_t tmp; __asm__ __volatile__( SMP_LWSYNC "1: lwarx %0,0,%1\n\ addic %0,%0,1\n\ stwcx. %0,0,%1 \n\ bne- 1b" SMP_ISYNC : "=&r" (tmp) : "r" (&p_atomic->counter) : "cc", "xer", "memory"); return tmp; }