150 likes | 170 Views
Лекция 2 : Синхронизация. Механизм Read Copy Update (RCU) Барьеры Примитив CMPXCHG Спинлоки Устройство SLT. Аппаратура. CPU 0. MAU. DAM. Link to CPU 1. Кэш L2. MLT. Кэш L1. VLIW e3m – 300 MHz, 2 cpus, SMP e3s – 500 MHz, 4-16 cpus, NUMA. ALU. TLB. RF. SLT. TLU.
E N D
Лекция 2: Синхронизация • Механизм Read Copy Update (RCU) • Барьеры • Примитив CMPXCHG • Спинлоки • Устройство SLT
Аппаратура CPU 0 MAU DAM Link to CPU 1 Кэш L2 MLT Кэш L1 VLIW e3m – 300 MHz, 2 cpus, SMP e3s – 500 MHz, 4-16 cpus, NUMA ALU TLB RF SLT TLU AAU(APB) TIRs Cellar (подвал)
Аппаратура CPU 0 MAU DAM Link to CPU 1 Кэш L2 MLT Кэш L1 VLIW e3m – 300 MHz, 2 cpus, SMP e3s – 500 MHz, 4-16 cpus, NUMA ALU TLB RF SLT TLU AAU(APB) TIRs Cellar (подвал)
Механизм RCU struct page { … struct list_head lru; … }; struct list_head { struct list_head *next, *prev; }; #define INIT_LIST_HEAD (ptr) do { \ (ptr)->next = (ptr); (ptr)->prev = (ptr); \ } while (0); container_of() page page page head prev next prev next prev next prev next #define list_entry(ptr, type, member) container_of(ptr, type, member)
Механизм RCU struct page { … struct list_head lru; … }; struct list_head { struct list_head *next, *prev; }; #define INIT_LIST_HEAD (ptr) do { \ (ptr)->next = (ptr); (ptr)->prev = (ptr); \ } while (0); container_of() page page page head prev next prev next prev next prev next #define list_entry(ptr, type, member) container_of(ptr, type, member)
Механизм RCU #define list_for_each_entry (pos, head, member) \ for (pos = list_entry((head)->next), typeof(*pos), member); \ &pos->member != (head); \ pos = list_entry(pos->member.next, typeof (*pos), member)) CPU0 CPU1 2 1 void __list_del ( struct list_head *prev, struct list_head *next) { next->prev = prev; prev->next = next; } void __list_add (struct list_head *new, struct list_head *prev, struct list_head *next) { next->prev = new; new->next = next; new->prev = prev; prev->next = new; } Race: 1) list_for_each_entry 2) __list_add
Механизм RCU Использование атомарности операции выравненной записи (IA64, E2K) CPU0 CPU1 2 1 Pointer to start of list Entry Entry void __list_add_rcu (struct list_head *new, struct list_head *prev, struct list_head *next) { new->next = next; new->prev = prev; smp_wmb(); next->prev = new; prev->next = new; } New Entry void __list_del ( struct list_head *prev, struct list_head *next) { next->prev = prev; prev->next = next; }
Барьеры include/asm-e2k/e2k_api.h: #define smp_mb() E2K_WAIT(_all_c) #define smp_rmb() E2K_WAIT(_ld_c) #define smp_wmb() E2K_WAIT(_st_c) include/asm-ia64/system.h: #define smp_mb() ia64_mf() #define smp_rmb() smp_mb() #define smp_wmb() smp_mb() • All • Read • Write #define _ld_c 0x8 #define _st_c 0x4 #define _all_c 0x1 #define E2K_WAIT(num) \ asm volatile (“wait ma_c=%0, fl_c=%1, ld_c=%2, st_c=%3, all_e=%4, all_c=%5” : : “i” (((num) & 0x3f) >> 5), … : “memory”); include/asm-ia64/gcc_intrin.h: #define ia64_mf() \ asm volatile (“mf”:::“memory”)
Барьеры include/asm-e2k/e2k_api.h: #define smp_mb() E2K_WAIT(_all_c) #define smp_rmb() E2K_WAIT(_ld_c) #define smp_wmb() E2K_WAIT(_st_c) include/asm-ia64/system.h: #define smp_mb() ia64_mf() #define smp_rmb() smp_mb() #define smp_wmb() smp_mb() • All • Read • Write #define _ld_c 0x8 #define _st_c 0x4 #define _all_c 0x1 #define E2K_WAIT(num) \ asm volatile (“wait ma_c=%0, fl_c=%1, ld_c=%2, st_c=%3, all_e=%4, all_c=%5” : : “i” (((num) & 0x3f) >> 5), … : “memory”); include/asm-ia64/gcc_intrin.h: #define ia64_mf() \ asm volatile (“mf”:::“memory”) Все это хорошо когда есть много читателей и только один писатель. Атомарности RCU и барьеров недостаточно если писателей больше чем один.
Примитив CMPXCHG CMPXCHG – Compare and Exchange. Примитив предназначен для обеспечения атомарности доступа к области памяти (atomic state transition): 1) Писатели пытаются получить эксклюзивный доступ к области памяти 2) Только один писатель получает эксклюзивный доступ 3) Писатель, получивший доступ, меняет содержимое области памяти 4) После изменения содержимого памяти писатель отдает право экслюзивного доступа другим писателям CMPXCHG – примитив нижнего уровня, реализован на ассемблере, обращается напрямую к аппаратному устройству, обеспечивающему переключение состояний строки кэша shared->exclusive. В архитектуре E2K такое устройство называется SLT.
Примитив CMPXCHG #define _E2K_READ_MAS(addr, mas, type, size_letter, chan_letter) \ ({ \ register type res; \ asm volatile(“ld” #size_letter “,” #chan_letter “ 0x0, [%1] %2, %0“) \ : “=r” (res) \ : “r” ((_e2k_ptr_t) (addr)), \ “i” (mas)); \ res; \ }) #define _E2K_WRITE_MAS(addr, mas, type, size_letter, chan_letter) \ ({ \ asm volatile(“st” #size_letter “,” #chan_letter “ 0x0, [%0] %2, %1“) \ : \ : “r” ((_e2k_ptr_t) (addr)), \ “r” ((type) (val)), \ “i” (mas)); \ })
Примитив CMPXCHG, спинлоки static inline unsigned long __cmpxchg_w (unsigned long old, unsigned long new new, volatile void * ptr) { unsigned long prev; prev = E2K_ATOMIC_LBRACKET_W(ptr, MAS_WAIT_LOCK, 3); if (prev == old) E2K_ATOMIC_RBRACKET_W(ptr, new, MAS_WAIT_UNLOCK, 3b); /* доступ получен */ else E2K_ATOMIC_RBRACKET_W(ptr, prev, MAS_WAIT_UNLOCK, 3b); /* занято */ wmb(); return (prev); } исполнение Critical Section accessing data protected by Spinlock Atomic operation lockval 0->1 Write Barrier lockval = 0 Wait while lockval != 0 Exclusive (CMPXCHG) Shared
Спинлоки /* * Decrement the use count and release all resources for an mm. */ void mmput(struct mm_struct *mm) { if (atomic_dec_and_test(&mm->mm_users)) { exit_aio(mm); exit_mmap(mm); if(!list_empty(&mm->mmlist)) { spin_lock(&mmlist_lock); list_del(&mm->mmlist); spin_unlock(&mmlist_lock); } put_swap_token(mm); mmdrop(mm); } } Спинлок = CMPXCHG + запрет прерываний Вопрос: Зачем нужны спинлоки на однопроцессорной машине?
Ссылки • http://os.lab.sun.mcst.ru/techpapers/gelato2005.pdf • http://www.lab.sun.mcst.ru/honey/elbrus_s/project_docs/hw/mu/SLTopi.html