Вопрос

На SMP-машине мы должны использовать spin_lock_irqsave и не spin_lock_irq из контекста прерывания.

Зачем нам сохранять флаги (которые содержат IF)?

Есть ли другая процедура прерывания, которая могла бы прервать нас?

Это было полезно?

Решение

Я новичок в ядре, но, судя по книге Роберта Лава «Разработка ядра Linux», если прерывания уже отключены на процессоре до того, как ваш код начнет блокироваться, при вызове spin_unlock_irq вы ошибочно снимете блокировку.Если вы сохраните флаги и отпустите его вместе с флагами, функция spin_lock_irqsave просто вернет прерывание в предыдущее состояние.

Пример с spin_lock_irqsave

spinlock_t mLock = SPIN_LOCK_UNLOCK;
unsigned long flags;

spin_lock_irqsave(&mLock, flags); // save the state, if locked already it is saved in flags
// Critical section
spin_unlock_irqrestore(&mLock, flags); // return to the formally state specified in flags

Пример с spin_lock_irq(без irqsave):

spinlock_t mLock = SPIN_LOCK_UNLOCK;
unsigned long flags;

spin_lock_irq(&mLock); // Does not know if already locked
// Critical section
spin_unlock_irq(&mLock); // Could result in an error unlock...

Другие советы

spin_lock_irqsave в основном используется для сохранения состояния прерывания перед выполнением спин-блокировки, это связано с тем, что спин-блокировка отключает прерывание, когда блокировка выполняется в контексте прерывания, и повторно включает его при разблокировке.Состояние прерывания сохраняется, чтобы оно могло снова восстановить прерывания.

Пример:

  1. Допустим, прерывание x было отключено до того, как была получена блокировка вращения.
  2. spin_lock_irq отключит прерывание x и возьмет блокировку
  3. spin_unlock_irq включит прерывание x.

Таким образом, на третьем шаге выше после снятия блокировки у нас будет включено прерывание x, которое ранее было отключено до того, как была получена блокировка.

Поэтому только тогда, когда вы уверены, что прерывания не отключены, вам следует spin_lock_irq в противном случае вам всегда следует использовать spin_lock_irqsave.

Необходимость в spin_lock_irqsave кроме того spin_lock_irq это очень похоже на причину local_irq_save(flags) необходим, кроме того local_irq_disable.Вот хорошее объяснение этого требования, взятое из второго издания Robert Love по разработке ядра Linux.

Процедура local_irq_disable() опасна, если прерывания были уже отключены до ее вызова.Соответствующий вызов local_irq_enable() безоговорочно разрешает прерывания, несмотря на тот факт, что они были отключены с самого начала.Вместо этого необходим механизм для восстановления прерываний в предыдущее состояние.Это общая проблема поскольку заданный путь кода в ядре может быть достигнут как с включенными прерываниями, так и без них, в зависимости от цепочки вызовов.Например, представьте, что предыдущий фрагмент кода является частью более крупной функции.Представьте, что эта функция вызывается двумя другими функциями, одна из которых отключает прерывания, а другая - нет.Поскольку по мере роста размера и сложности ядра становится сложнее знать весь код путей, ведущих к функции, гораздо безопаснее сохранить состояние системы прерываний перед ее отключением.Затем, когда вы будете готовы к повторному включению прерываний, вы просто восстановите их в исходное состояние:

unsigned long flags;

local_irq_save(flags);    /* interrupts are now disabled */ /* ... */
local_irq_restore(flags); /* interrupts are restored to their previous
state */

Обратите внимание, что эти методы реализованы, по крайней мере частично, как макросы, поэтому параметр flags (который должен быть определен как unsigned long) по-видимому, передается по значению.Этот параметр содержит зависящие от архитектуры данные, содержащие состояние прерывания системы.Поскольку по крайней мере одна поддерживаемая архитектура включает информацию стека в значение (кхм, SPARC), флаги не могут быть переданы другой функции (в частности, она должна оставаться в том же стековом фрейме).По этой причине вызов сохранения и вызов восстановления прерывания должны выполняться в одной и той же функции.

Все предыдущие функции могут быть вызваны из обоих прерываний и процесс контексте.

Чтение Почему код ядра / поток, выполняющийся в контексте прерывания, не может перейти в режим ожидания? что связано с Робертом Лавсом Статья, Я читал это :

некоторые обработчики прерываний (известные в Linux как быстрые обработчики прерываний) выполняются со всеми прерываниями на локальном процессоре отключенными.Это сделано для того, чтобы гарантировать, что обработчик прерываний выполняется без прерываний, как можно быстрее .Более того, все обработчики прерываний выполняются со своей текущей отключенной линией прерывания на всех процессорах.Это гарантирует, что два обработчика прерываний для одной и той же строки прерывания не будут выполняться одновременно.Это также избавляет авторов драйверов устройств от необходимости обрабатывать рекурсивные прерывания, которые усложняют программирование.

Ниже приведена часть кода ядра Linux 4.15.18, которая показывает, что spiin_lock_irq() вызывает __raw_spin_lock_irq().Однако он не сохранит никаких флагов, как вы можете видеть ниже в части кода, но отключит прерывание.

  static inline void __raw_spin_lock_irq(raw_spinlock_t *lock)
    {
        local_irq_disable();
        preempt_disable();
        spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);
        LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock);
    }

Ниже приведен код spin_lock_irqsave(), который сохраняет текущий этап флага, а затем вытесняет его.

static inline unsigned long __raw_spin_lock_irqsave(raw_spinlock_t *lock)
{
    unsigned long flags;

    local_irq_save(flags);
    preempt_disable();
    spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);
    /*
     * On lockdep we dont want the hand-coded irq-enable of
     * do_raw_spin_lock_flags() code, because lockdep assumes
     * that interrupts are not re-enabled during lock-acquire:
     */
#ifdef CONFIG_LOCKDEP
    LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock);
#else
    do_raw_spin_lock_flags(lock, &flags);
#endif
    return flags;
}

Этот вопрос начинается с ложного утверждения: On an SMP machine we must use spin_lock_irqsave and not spin_lock_irq from interrupt context.

Ни один из них не должен использоваться из контекста прерывания , на SMP или на UP.Это сказало, spin_lock_irqsave() мочь может использоваться из контекста прерывания, как более универсальный (он может использоваться как в контексте прерывания, так и в обычном контексте), но предполагается, что вы должны использовать spin_lock() из контекста прерывания, и spin_lock_irq() или spin_lock_irqsave() из обычного контекста.Использование spin_lock_irq() это почти всегда неправильно делать в контексте прерывания, будь то SMP или UP.Это может сработать потому что большинство обработчиков прерываний выполняются с локально включенными IRQ, но вам не стоит это пробовать.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top