Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Зачем нужен барьер памяти (DMB) перед LDREX
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
bublik
Зачем нужен барьер памяти (DMB) перед LDREX

Здравствуйте, к сожалению не смог найти ответ на вопрос: зачем нужен барьер памяти (DMB) перед LDREX.

Суть вопроса:

В архитектурах ARMv6 и ARMv7 вместо операции SWP нужно использовать пару: LDREX/STREX.

Вот так например будет выглядеть атомарная операция сложения:

Код
void atomic_add(int *obj; int val)

.try:
    Ldrex    r2,  [r0]                
    add       r2,  r2, r1                                      
    strex     r3,  r2, [r0]                                    
    teq        r3,  #0                                          
    bne     .try                                                                            
    bx      lr


Все прекрасно работает и т.п.

Но это если функция атомарного сложения у нас void. Если же мы сделаем ее, чтобы она возвращала результат:

Код
int atomic_add(int *obj; int val)
.try:
    Ldrex    r2,  [r0]                            
    mov      r12, r2                                          
    add       r2,  r2, r1                                      
    strex     r3,  r2, [r0]                                    
    teq        r3,  #0                                          
    bne     .try      
    DMB                           //барьер                                
    mov     r0,  r12                                        
    bx      lr


Добавляется барьер (DMB) – это сделано для того, что если мы будем использовать данную функцию для реализации примитива синхронизации, мы при входе в критическую секцию знали (была дана гарантия), что те данные которые мы защищаем (работаем в критической секции) были в валидном состоянии (все операции по сохранению были завершены) подробнее в Barrier_Litmus_Tests_and_Cookbook_A08.pdf (http://infocenter.arm.com/help/topic/com.arm.doc.genc007826/Barrier_Litmus_Tests_and_Cookbook_A08.pdf)

Но мне не понятно, зачем gcc 4.9.2 для атомарных операций (С11) типа atomic_fetch_add
Вставляет dmb, до ldrex:

Код
int atomic_fetch_add(int *obj; int val)
     DMB    SY                     // я не понимаю зачем нужен этот барьер
.try:
    Ldrex    r2,  [r0]                            
    mov      r12, r2                                          
    add       r2,  r2, r1                                      
    strex     r3,  r2, [r0]                                    
    teq        r3,  #0                                          
    bne     .try      
    DMB    SY                    //барьер                                
    mov     r0,  r12                                        
    bx      lr


Аналогично сделано для атомарных операций в ядре Linux: /arch/arm/include/asm/atomic.h

Нашел коммит, который добавляет все это дело: https://git.kernel.org/cgit/linux/kernel/gi...0e59aa68af3b43a

Автор говорит, что это требуется для операций, которые возвращают результат (с нижним DMB – понятно) но верхний ?

Зачем нужный DMB до LDREX ?

Спасибо.
Golikov A.
может чтобы операнды на всякий пожарный подтянуть? Мало ли откуда они идут, может из какой-то функции без барьеров?
bublik
Цитата
Страница 17, раздел 7.1
- да Ldrex в себе не содержат явных барьеров, и получается что Golikov A. прав для того, чтобы подтянуть операнды...

Но тогда в ядре атомарные функции без return (. которые не возвращают результат) тоже должны иметь барьер до Ldrex. ведь мы им тоже передаем переменные, А их там нет. и код прекрасно работает.

Но как только функция должна вернуть результат, аж два барьера....

Еще раз последний барьер нужен, если нас приспичит реализовать свой примитив синхронизации, а согласно: arrier_Litmus_Tests_and_Cookbook_A08.pdf
п 7.2.1 - мы должны сделать барьер (чтобы при входе в критическую секцию - быть уверенными, что те данные которые мы защищаем при помощи нашей блокировки были в валидном состоянии.

согласно п .7.2.2 - барьер нужен до сброса блокировки ( я так понимаю это требование из: http://infocenter.arm.com/help/index.jsp?t...qs/ka14041.html )

и так как мы можем гипотетически использовать данную атомарную функцию в своем примитиве синхронизации мы должны выполнить пункт 7.2.2 - Но так как у нас функция одна (мы не делим ее на lock, unlock) мы имеем верхний барьер.

по идее он должен быти типа таким:
Код
int atomic_fetch_add(int *obj; int val)

.try:
    Ldrex    r2,  [r0]                            
    mov      r12, r2                                          
    add       r2,  r2, r1  
    DMB    SY                     //Согласно п 7.2.2  до сохранения (требования для unlock)                                
    strex     r3,  r2, [r0]                                    
    teq        r3,  #0                                          
    bne     .try      
    DMB    SY                    //барьер согласно п 7.2.1 (lock)                              
    mov     r0,  r12                                        
    bx      lr



Но его засунули на вверх для большей производительности Но это мне так кажется.... Я не знаю точного ответа.. wacko.gif

Убедите меня что я прав, или скажите где я НЕ прав.

Спасибо
aaarrr
Цитата(bublik @ Feb 15 2015, 17:08) *
Убедите меня что я прав, или скажите где я НЕ прав.

Очень похоже, что правы:
- atomic_add() и atomic_sub() не имеют барьеров, т.к. не могут использоваться как средства синхронизации
- прочие обернуты барьерами как гипотетические элементы синхронизации

Цитата(bublik @ Feb 15 2015, 17:08) *
Но его засунули на вверх для большей производительности Но это мне так кажется.... Я не знаю точного ответа.. wacko.gif

Странно было бы его ставить теле цикла ldrex-strex.

P.S. Сорри, предыдущий пост удалил, т.к. осознал, что не совсем понял вопрос.
bublik
Спасибо wink.gif
kimstik
А по мне это похоже на обход эрраты ARM 782772
Там предлагают DMB перед LDREX ставить
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.