Spin_lock_irqsave vs spin_lock_irq
На машине SMP мы должны использовать spin_lock_irqsave
, а не spin_lock_irq
из контекста прерывания.
Почему мы хотим сохранить флаги (которые содержат IF)?
Есть ли другая процедура прерывания, которая может прервать нас?
Ответы
Ответ 1
Я новичок в ядре, но из того, что я собираю из книги Роберта Люка "Linux Kernel Development", если прерывания уже отключены на процессоре до того, как ваш код начнет блокировку, когда вы вызываете 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...
Ответ 2
spin_lock_irqsave
в основном используется для сохранения состояния прерывания перед фиксацией блокировки, это связано с тем, что блокировка блокировки отключает прерывание, когда блокировка выполняется в контексте прерывания и повторно включает ее при разблокировке. Состояние прерывания сохраняется так, что оно должно снова восстановить прерывания.
Пример:
- Предположим, что прерывание x было отключено до того, как была зафиксирована блокировка спина.
-
spin_lock_irq
отключит прерывание x и возьмет блокировку
-
spin_unlock_irq
будет включать прерывание x.
Итак, на третьем шаге выше, после освобождения блокировки, мы получим прерывание x, которое было ранее отключено до того, как была получена блокировка.
Итак, только когда вы уверены, что прерывания не отключены, тогда вы должны spin_lock_irq
, иначе вы всегда должны использовать spin_lock_irqsave
.
Ответ 3
Чтение Почему код ядра/поток, выполняемый в контексте прерывания, не может спать?, который ссылается на Robert Loves article, я прочитал следующее:
некоторые обработчики прерываний (известные в Linux как быстрые обработчики прерываний) со всеми прерываниями на местном процессор отключен. Это делается для убедитесь, что выполняется обработчик прерываний без перерыва, так же быстро, как возможное. Более того, все прерывания обработчики работают с их текущими прерывание отключено на всех процессоры. Это гарантирует, что два обработчики прерываний для одного и того же строка прерывания не запускается одновременно. Он также предотвращает авторам драйверов от необходимости обращаться рекурсивных прерываний, которые усложняют программирование.
Ответ 4
Необходимость spin_lock_irqsave
, кроме spin_lock_irq
, очень похожа на причину local_irq_save(flags)
, кроме local_irq_disable
. Вот хорошее объяснение этого требования, взятого из Linux Kernel Development Second Edition от Robert Love.
Процедура local_irq_disable() опасна, если прерывания были уже отключен до его вызова. Соответствующий вызов local_irq_enable() безоговорочно разрешает прерывания, несмотря на факт, что они были для начала. Вместо этого необходим механизм для восстановления прерываний в предыдущем состоянии. Это общая проблема потому что заданный путь кода в ядре может быть достигнут как с помощью, так и с без прерываний, в зависимости от цепочки вызовов. Например, представьте, что предыдущий фрагмент кода является частью более крупной функции. Представьте себе, что эта функция вызывается двумя другими функциями: отключает прерывания, а другой - нет. Потому что он становится сложнее, когда ядро растет по размеру и сложности, чтобы знать весь код пути, ведущие к функции, гораздо безопаснее сохранять состояние перед прерыванием. Затем, когда вы будете готовы reenable interrupts, вы просто восстанавливаете их в исходное состояние:
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), флаги не могут быть переданы к другой функции (в частности, она должна оставаться в одном стеке Рамка). По этой причине вызов для сохранения и вызов для восстановления прерывания должны выполняться в одной и той же функции.
Все предыдущие функции можно вызывать как из прерывания, так и из процесса.