spin_lock_irqsave vs spin_lock_irq
-
23-09-2019 - |
Domanda
Su una macchina SMP dobbiamo usare spin_lock_irqsave
e non spin_lock_irq
dall'interruzione contesto.
Perché vogliamo salvare il flag (che contengono il SE)?
C'è un'altra routine di interrupt che interrompono noi?
Soluzione
Sono nuovo al kernel, ma da quanto ho capito dal libro di Robert Love "Linux Kernel Development", se gli interrupt sono già disabilitati sul processore prima che il codice di inizio di blocco, quando si chiama spin_unlock_irq si rilasciare il blocco in un'erronea maniera. Se si salvano le bandiere e rilasciarlo con le bandiere, la funzione spin_lock_irqsave sarà solo tornare l'interrupt al suo stato precedente.
Esempio con 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
Esempio con spin_lock_irq
(senza 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...
Altri suggerimenti
spin_lock_irqsave
è fondamentalmente utilizzato per salvare l'interrupt è stato prima di prendere il blocco di rotazione, questo perché spin lock disabilita gli interrupt, quando il blocco viene preso in interrupt contesto, e ri-attivato quando mentre lo sblocco.L'interrupt è stato salvato, in modo che dovrebbe ripristinare la interrompe di nuovo.
Esempio:
- Diciamo interrupt x è stato disattivato prima di spin lock è stata acquisita
spin_lock_irq
disabilita gli interrupt x e prendere il bloccospin_unlock_irq
abilita l'interrupt x.
Così nel 3 ° gradino al di sopra dopo il rilascio del blocco avremo interrupt x abilitato che è stato precedentemente disattivato prima che il blocco è stato acquisito.
Quindi, solo quando si è sicuri che gli interrupt non vengono disattivati solo allora si dovrebbe spin_lock_irq
altrimenti si dovrebbe usare sempre spin_lock_irqsave
.
La necessità di spin_lock_irqsave
oltre spin_lock_irq
è molto simile alla ragione local_irq_save(flags)
è necessaria oltre local_irq_disable
. Ecco una buona spiegazione di questo requisito preso da Linux Kernel Development Seconda edizione da Robert Love.
Il local_irq_disable () di routine è pericoloso se fossero gli interrupt già disattivato prima della sua invocazione. La corrispondente chiamata a local_irq_enable () consente incondizionatamente interrupt, nonostante la fatto che erano fuori per cominciare. Invece, è necessario un meccanismo per ripristinare interrupt a uno stato precedente. Questa è una preoccupazione comune perché un determinato percorso di codice nel kernel può essere raggiunto sia con che senza interruzioni attivato, a seconda della catena di chiamate. Per esempio, immaginare il codice precedente è parte di una funzione più ampia. Immaginare che questa funzione è chiamato da altre due funzioni, una che disabilita interrupt e uno che non lo fa. Perché sta diventando più difficile come il kernel cresce in dimensioni e complessità di conoscere tutto il codice sentieri che portano fino a una funzione, è molto più sicuro per salvare lo stato di il sistema di interruzione prima di disabilitarlo. Poi, quando si è pronti per interrupt riattivare, è semplicemente di ripristinare lo stato originale:
unsigned long flags;
local_irq_save(flags); /* interrupts are now disabled */ /* ... */
local_irq_restore(flags); /* interrupts are restored to their previous
state */
Si noti che questi metodi sono implementati almeno in parte come macro, in modo il parametro flags (che deve essere definito come un unsigned long) è apparentemente passato per valore. Questo parametro contiene dati Architettura-specifici contenenti lo stato di interruzione sistemi. Poiché almeno un'architettura supportata incorpora informazioni di stack nel valore (ahem, SPARC), le bandiere non può essere passato ad un'altra funzione (in particolare, deve rimanere sulla stessa pila telaio). Per questo motivo, la chiamata per salvare e la chiamata per ripristinare interrupt devono avvenire nella stessa funzione.
Tutte le funzioni precedenti può essere chiamato da entrambi interruzione e processo di contesto.
Perché il codice del kernel / thread in esecuzione nel contesto di interrupt non riesce a dormire? che collega Robert Loves articolo , ho letto questo:
alcuni gestori di interrupt (noto in Linux come gestori di interrupt veloci) run con tutti gli interrupt sul locale processore disabilitata. Questo viene fatto per garantire che le piste gestore di interrupt senza interruzione, nel più breve tempo possibile. Più così, tutti interrupt gestori di correre con loro attuale linea di interrupt disattivato su tutti processori. Questo assicura che due gestori di interrupt per la stessa linea di interrupt non vengono eseguiti simultaneamente. Inoltre impedisce dispositivo scrittori del driver da dover manico interrupt ricorsive che complicano programmazione.
Di seguito è parte del codice del kernel Linux 4.15.18, il che dimostra che spiin_lock_irq () chiameranno __raw_spin_lock_irq (). Tuttavia, non sarà salvare tutti i flag, come si può vedere di seguito parte del codice, ma disattivare l'interrupt.
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);
}
Sotto codice spettacoli spin_lock_irqsave () che salva l'attuale fase di bandiera e quindi disattivare preempt.
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;
}
Questa domanda parte dalla falsa asserzione:
On an SMP machine we must use spin_lock_irqsave and not spin_lock_irq from interrupt context.
Nessuno di questi dovrebbe essere usato da interrupt
contesto, lo SMP o UP. Detto questo, spin_lock_irqsave()
possono essere utilizzato dal contesto di interrupt, come più universale
(Può essere utilizzato sia in ambito interrupt e normali), ma
si suppone di utilizzare spin_lock()
dal contesto di interrupt,
e spin_lock_irq()
o spin_lock_irqsave()
dal contesto normale.
L'uso di spin_lock_irq()
è quasi sempre la cosa sbagliata
fare in contesto di interrupt, essendo questo SMP o UP. Si può lavorare
perché la maggior parte gestori di interrupt correre con IRQ localmente abilitati,
ma non si deve provare che.