it-swarm.com.ru

spin_lock_irqsave против spin_lock_irq

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

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

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

22
cojocar

Я новичок в ядре, но из того, что я почерпнул из книги Роберта Лава "Разработка ядра 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...
20
Hagai

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

Пример:

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

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

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

35
pastum

Потребность в spin_lock_irqsave помимо spin_lock_irq очень похожа на причину local_irq_save(flags), кроме local_irq_disable. Вот хорошее объяснение этого требования, взятое у Роберта Лава из Linux Kernel Development Second Edition.

Подпрограмма 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) - казалось бы, передается по значению. Этот параметр содержит специфичные для архитектуры данные, содержащие состояние прерывания системы. Потому что по крайней мере одна поддерживаемая архитектура включает в себя складывать информацию в значение (ahem, SPARC), флаги не могут быть переданы другой функции (в частности, она должна оставаться в том же стеке кадр). По этой причине вызов сохранить и призыв восстановить прерывания должны происходить в одной и той же функции.

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

4
4pie0

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

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

2
Ian Vaughan

Этот вопрос начинается с ложного утверждения: 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() из прерывания context, и spin_lock_irq() или spin_lock_irqsave() из обычного контекста . Использование spin_lock_irq() почти всегда неправильно делать в контексте прерывания, будь то SMP или UP. Это может работать , Потому что большинство обработчиков прерываний работают с локально включенными IRQ, , Но вы не должны пытаться это делать.

0
stsp