HARD deadlock в модуле ядра

Вопросы программного кода и архитектуры Linux

Модератор: Olej

bose
Писатель
Сообщения: 107
Зарегистрирован: 23 фев 2012, 14:41
Откуда: Киев
Контактная информация:

HARD deadlock в модуле ядра

Непрочитанное сообщение bose » 04 окт 2012, 23:33

Буду краток. Случилось следующее - в драйвере PCI устройства ловлю hardlock. Описание ситуации:
1) Есть некий ресурс А, который в обработчике прерывания защищается спомощью spin_lock
2) К этому же ресурсу (А) имеет доступ код модуля работающий в котексте процесса и защищает его с помощью spin_lock_irqsave
3) Случается ситуация когда возникает прерывание на том же CPU что и взяло доступ к ресурсу А в контексте процесса (с помощью spin_lock_irqsave)

Не могу понять как такое может произойти ведь локальные прерывания заблокированы. Код детально изучен и с уверенность могу сказать: 1) что в контексте процесса для доступа к ресурсу А используется исключительно spin_lock_irqsave/spin_unlock_irqrestore; 2) прерывание возникает на том же процессоре что и взяло доступ к ресурсу с помощью spin_lock_irqsave

Есть некие предположения. Я использую вложенные (относительно spin_lock_irqsave(ресурс А)) спинлоки для доступа к другим ресурсам (скажем Б и В). Поскольку я не делаю доступ к этим ресурсам в контексте прерывания (я их использую в тасклетах) - доступ к ним я защищаю с помощью spin_lock_bh/spin_unlock_bh. Не может ли вызов spin_unlock_bh разрешить локальные прерывания?

Код: Выделить всё


func#1
  spin_lock_irqsave(А)
    func#2
      spin_lock_bh(Б)
      spin_unlock_bh(Б)
     !!! С этих пор прерывания разрешены !!!
    return
  spin_unlock_irqrestore(А)
return

Кто то может прояснить сложившуюся ситуацию?

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: HARD deadlock в модуле ядра

Непрочитанное сообщение Olej » 05 окт 2012, 01:09

bose писал(а): 3) Случается ситуация когда возникает прерывание на том же CPU что и взяло доступ к ресурсу А в контексте процесса (с помощью spin_lock_irqsave)
А сколько у вас вообще процессоров?
P.S. это я спросил на всякий случай, потому как без SMP все вызовы spin_*() просто исключаются из кода ядра.
bose писал(а): Не может ли вызов spin_unlock_bh разрешить локальные прерывания?

Код: Выделить всё

func#1
  spin_lock_irqsave(А)
    func#2
      spin_lock_bh(Б)
      spin_unlock_bh(Б)
     !!! С этих пор прерывания разрешены !!!
    return
  spin_unlock_irqrestore(А)
return
А почему он должен не разрешать локальные прерывания?
Запреты/разрешения прерываний не накапливающие, стековые операции: или разрешили - или запретили...
Мне так кажется, что хоть 10 раз запрети локальные прерывания - первое же разрешение их разрешит.
Нет?

bose
Писатель
Сообщения: 107
Зарегистрирован: 23 фев 2012, 14:41
Откуда: Киев
Контактная информация:

Re: HARD deadlock в модуле ядра

Непрочитанное сообщение bose » 05 окт 2012, 09:52

Olej писал(а): А сколько у вас вообще процессоров?
P.S. это я спросил на всякий случай, потому как без SMP все вызовы spin_*() просто исключаются из кода ядра.
4
Olej писал(а): А почему он должен не разрешать локальные прерывания?
Запреты/разрешения прерываний не накапливающие, стековые операции: или разрешили - или запретили...
Мне так кажется, что хоть 10 раз запрети локальные прерывания - первое же разрешение их разрешит.
Нет?
Наверное я неправильно объяснил...
spin_lock_irqsave/spin_unlock_irqrestore - запрещают-разрешают локальные прерывания ЯВНО (это заложено в семантику вызова, о чём говорит и документация)
spin_lock_bh/spin_unlock_bh - если это и делают, то происходит это неявно

Ну вот собсвенно чуть копнув вглубь что видно:

Код: Выделить всё

static inline void __raw_spin_unlock_irqrestore(raw_spinlock_t *lock,
                                            unsigned long flags)
{
        spin_release(&lock->dep_map, 1, _RET_IP_);
        do_raw_spin_unlock(lock);
        local_irq_restore(flags);          // << Здесь разрешаются локальные прерывания (если они до этого были разрешены)
        preempt_enable();                  // << Здесь мы можем покинуть атомарную (критическую секцию)
}
против

Код: Выделить всё

static inline void __raw_spin_unlock_bh(raw_spinlock_t *lock)
{
        spin_release(&lock->dep_map, 1, _RET_IP_);
        do_raw_spin_unlock(lock);
        preempt_enable_no_resched();     // << WTF? Почему можно вытеснять ( Почему нельзя перепланировать? )
        local_bh_enable_ip((unsigned long)__builtin_return_address(0)); // 2 x WTF?
}
Ну вот ещё чуть дальше (local_bh_enable_ip):

Код: Выделить всё

157 static inline void _local_bh_enable_ip(unsigned long ip)
158 {
159         WARN_ON_ONCE(in_irq() || irqs_disabled());
160 #ifdef CONFIG_TRACE_IRQFLAGS
161         local_irq_disable();
162 #endif
163         /*
164          * Are softirqs going to be turned on now:
165          */
166         if (softirq_count() == SOFTIRQ_DISABLE_OFFSET)
167                 trace_softirqs_on(ip);
168         /*
169          * Keep preemption disabled until we are done with
170          * softirq processing:
171          */
172         sub_preempt_count(SOFTIRQ_DISABLE_OFFSET - 1);
173 
174         if (unlikely(!in_interrupt() && local_softirq_pending()))
175                 do_softirq();
176 
177         dec_preempt_count();
178 #ifdef CONFIG_TRACE_IRQFLAGS 
179         local_irq_enable();                    // << Кто вас тут разрешал? (Из серии - "вы нас не ждали а мы припёрлись")
180 #endif
181         preempt_check_resched();
182 }
Ну собсвенно главный вопрос - кто (какая функция) ещё может неявно разрешать локальные прерывания?

Ответить

Вернуться в «Linux изнутри»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 3 гостя