spin_lock_irqsave vs spin_lock_irq
-
23-09-2019 - |
Frage
Auf einer SMP-Maschine müssen wir spin_lock_irqsave
und nicht spin_lock_irq
von Interrupt-Kontext verwenden.
Warum sollten wir wollen die Flaggen speichern (die die IF enthalten)?
Gibt es eine andere Interrupt-Routine, die uns unterbrechen könnte?
Lösung
Ich bin neu in den Kernel, sondern von dem, was ich von Robert Love Buch „Linux Kernel Development“ sammeln, wenn Interrupts bereits auf dem Prozessor deaktiviert, bevor Sie den Code Sperre beginnt, wenn Sie anrufen spin_unlock_irq Sie die Sperre in einem fehlerhaften freigeben werden Weise. Wenn Sie die Fahnen speichern und lassen Sie ihn mit den Flaggen, wird die Funktion spin_lock_irqsave zurückkehren nur den Interrupt in seinen vorherigen Zustand.
Beispiel mit 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
Beispiel mit spin_lock_irq
(ohne 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...
Andere Tipps
spin_lock_irqsave
ist im Grunde verwendet, um den Unterbrechungszustand zu speichern, bevor Sie den Spin-Lock nimmt, ist dies, weil Spin-Lock des Interrupt deaktiviert, wenn das Schloss in Interrupt-Kontext genommen und erneut aktiviert es, wenn während Entriegelung. Der Interrupt-Zustand wird so gespeichert, dass sie die Interrupts wieder wieder einstellen sollten.
Beispiel:
- Lassen Sie uns sagen Interrupt x deaktiviert wurde, bevor Spin-Lock erworben wurde
-
spin_lock_irq
wird die Interrupt-x deaktivieren und nehmen Sie die die Sperre -
spin_unlock_irq
wird die Interrupt-x aktivieren.
So im dritten Schritt oben nach Lösen der Verriegelung werden wir Interrupt-x aktiviert, das war früher deaktiviert, bevor die Sperre übernommen wurde.
Also nur, wenn Sie sicher sind, dass Interrupts deaktiviert sind nicht nur dann sollten Sie spin_lock_irq
sonst sollten Sie immer spin_lock_irqsave
verwenden.
Die Notwendigkeit für spin_lock_irqsave
neben spin_lock_irq
ist ganz ähnlich wie der Grund local_irq_save(flags)
neben local_irq_disable
benötigt wird. Hier ist eine gute Erklärung für diese Anforderung genommen von Linux Kernel Entwicklung Second Edition von Robert Love.
Die local_irq_disable () Routine ist gefährlich, wenn Interrupts waren bereits vor ihrem Aufruf gesperrt. Der entsprechende Aufruf local_irq_enable () ermöglicht bedingungslos Interrupts, trotz der Tatsache, dass sie mit zu beginnen, waren aus. Stattdessen wird ein Mechanismus benötigt Unterbrechungen in einen früheren Zustand wiederherzustellen. Dies ist ein gemeinsames Anliegen weil ein bestimmter Codepfad im Kernel mit den beiden erreicht werden kann und ohne Unterbrechungen freigegeben, auf der Call-Kette abhängig. Beispielsweise, vorstellen, den vorherigen Code-Schnipsel Teil einer größeren Funktion ist. Stellen Sie sich vor, dass diese Funktion durch zwei weitere Funktionen aufgerufen wird, eine, die sperrt Interrupts und eines, das nicht der Fall ist. Weil es immer in Größe und Komplexität härter als der Kernel den gesamten Code zu wissen, wächst Wege zu einer Funktion führt, ist es viel sicherer, den Zustand zu speichern das Interrupt-System, bevor es deaktiviert wird. Dann, wenn Sie bereit sind, reaktivieren Interrupts Sie einfach wieder in ihren ursprünglichen Zustand:
unsigned long flags;
local_irq_save(flags); /* interrupts are now disabled */ /* ... */
local_irq_restore(flags); /* interrupts are restored to their previous
state */
Beachten Sie, dass diese Verfahren zumindest teilweise als Makros implementiert sind, so Der Parameter flags (der als unsigned long definiert werden müssen) ist scheinbar von Wert übergeben. Dieser Parameter enthält architekturspezifischen Daten den Zustand der Interrupt-enthaltende Systeme. Da mindestens eine unterstützte Architektur beinhaltet Stapelinformation in den Wert (ähem, SPARC), Flags können nicht weitergegeben werden an eine andere Funktion (es muss speziell bleiben, auf dem gleichen Stapel Rahmen). Aus diesem Grund, um den Anruf zu speichern, und der Anruf wiederherstellen Unterbrechungen in der gleichen Funktion auftreten müssen.
Alle bisherigen Funktionen können von beiden Interrupt aufgerufen werden und Prozesskontext.
Beim Lesen Warum Kernel-Code / Thread in Interrupt-Kontext ausführen kann nicht schlafen? die Links zu Robert Liebt Artikel , las ich diese:
einig Interrupt-Handler (bekannt in Linux als schnelle Interrupt-Handler) laufen mit allen Unterbrechungen auf dem lokalen Prozessor deaktiviert. Dies geschieht, um sicherzustellen, dass die Interrupt-Handler läuft ohne Unterbrechung, so schnell wie möglich. Um so mehr, alle Interrupt Handler laufen mit ihren aktuellen Interrupt-Leitung auf alle deaktiviert Prozessoren. Dadurch wird sichergestellt, dass zwei Interrupt-Handler für den gleichen Interrupt-Leitung läuft nicht gleichzeitig. Es verhindert auch, Gerät Treiber Autoren aus zu Griff rekursiven Unterbrechungen, die erschweren Programmierung.
Im Folgenden ist ein Teil des Codes in Linux-Kernel 4.15.18, die zeigt, dass spiin_lock_irq () rufen __raw_spin_lock_irq (). Es wird jedoch nicht speichern Sie alle Flaggen, wie Sie können unten Teil des Codes sehen, aber deaktivieren Sie die Unterbrechung.
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);
}
Im Folgenden Code zeigt spin_lock_irqsave (), die den aktuellen Stand der Flagge speichert und dann Präemptionsverhinderungsfunktion.
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;
}
Diese Frage geht von der falschen Behauptung:
On an SMP machine we must use spin_lock_irqsave and not spin_lock_irq from interrupt context.
Auch diese sollten von der Unterbrechung verwendet werden
Zusammenhang auf SMP oder auf UP. Das heißt, spin_lock_irqsave()
können von Interrupt-Kontext verwendet werden, als universeller sein
(Es kann in beiden Interrupt und normalen Kontexten verwendet werden), aber
Sie sollen spin_lock()
von Interrupt-Kontext verwenden,
und spin_lock_irq()
oder spin_lock_irqsave()
aus dem normalen Rahmen.
Die Verwendung von spin_lock_irq()
ist fast immer die falsche Sache
im Interrupt-Kontext, wobei dieses SMP oder UP zu tun. Es kann funktionieren
weil die meisten Interrupt-Handler mit IRQs lokal ausführen aktiviert ist,
aber Sie sollten nicht versuchen.