spin_lock_irqsave vs spin_lock_irq
-
23-09-2019 - |
Question
Sur une machine SMP, nous devons utiliser spin_lock_irqsave
et non spin_lock_irq
de contexte d'interruption.
Pourquoi voudrions-nous sauver les drapeaux (qui contiennent le IF)?
Y at-il une autre routine d'interruption qui pourrait nous interrompre?
La solution
Je suis nouveau au noyau, mais de ce que je déduis du livre de Robert Love « Linux Kernel Development », si les interruptions sont déjà désactivées sur le processeur avant votre code commence verrouillage, lorsque vous appelez spin_unlock_irq vous libérer le verrou dans une erreur manière. Si vous enregistrez les drapeaux et relâchez les drapeaux, la fonction spin_lock_irqsave va simplement retourner l'interruption à son état précédent.
Exemple avec 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
Exemple avec spin_lock_irq
(sans 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...
Autres conseils
spin_lock_irqsave
est essentiellement utilisé pour enregistrer l'état d'interruption avant de prendre le verrou de rotation, c'est parce que spin lock désactive l'interruption, lorsque le verrou est pris dans le contexte d'interruption, et rétablit quand tout déverrouillage. L'état d'interruption est enregistrée afin qu'il devrait rétablir les interruptions à nouveau.
Exemple:
- permet de dire d'interruption x a été désactivé avant verrouillage de spin a été acquis
-
spin_lock_irq
désactive le x d'interruption et prendre le verrou -
spin_unlock_irq
permettra au x d'interruption.
Ainsi, dans la 3ème étape ci-dessus après avoir relâché le verrou d'interruption, nous aurons x permis qui a été précédemment désactivé avant que le verrou a été acquis.
Alors que lorsque vous êtes sûr que les interruptions ne sont pas désactivés seulement alors vous devriez spin_lock_irq
sinon vous devriez toujours utiliser spin_lock_irqsave
.
La nécessité d'spin_lock_irqsave
en plus spin_lock_irq
est tout à fait similaire à la raison local_irq_save(flags)
est nécessaire en plus local_irq_disable
. Voici une bonne explication de cette exigence tirée de Linux Development noyau deuxième édition par Robert Amour.
La routine local_irq_disable () est dangereux si les interruptions étaient déjà désactivé avant son invocation. L'appel correspondant à local_irq_enable () permet inconditionnellement les interruptions, malgré la fait qu'ils étaient en route pour commencer. Au lieu de cela, il faut un mécanisme pour restaurer les interruptions à un état précédent. Ceci est une préoccupation commune car peut être atteint un chemin de code donné dans le noyau avec et sans interruption activée, en fonction de la chaîne d'appel. Par exemple, imaginez l'extrait de code précédent fait partie d'une fonction plus grande. Imaginez que cette fonction est appelée par deux autres fonctions, celle qui désactive les interruptions et qui ne fonctionne pas. Parce qu'il est de plus en plus difficile que le noyau se développe dans la taille et la complexité de connaître tout le code chemins menant à une fonction, il est beaucoup plus sûr pour sauver l'état de le système d'interruption avant de la neutraliser. Ensuite, lorsque vous êtes prêt à interruptions réactiver, vous les restaurer simplement leur état d'origine:
unsigned long flags;
local_irq_save(flags); /* interrupts are now disabled */ /* ... */
local_irq_restore(flags); /* interrupts are restored to their previous
state */
Notez que ces méthodes sont mises en œuvre au moins en partie sous forme de macros, de sorte le paramètre flags (qui doit être défini comme un long non signé) est apparemment passé par valeur. Ce paramètre contient des données spécifiques à l'architecture contenant l'état de l'interruption systèmes. Parce que au moins une architecture supportée intègre empiler des informations sur la valeur (hum, SPARC), les drapeaux ne peuvent pas être transmises à une autre fonction (en particulier, il doit rester sur la même pile Cadre). Pour cette raison, l'appel d'enregistrer et de l'appel à restaurer les interruptions doivent avoir lieu dans la même fonction.
Toutes les fonctions précédentes peuvent être appelées à la fois d'interruption et contexte de processus.
Lecture Pourquoi le code du noyau / thread d'exécution dans un contexte d'interruption ne peut pas dormir? qui relie à Robert Loves article , je lis ceci:
certains des gestionnaires d'interruption (connus Linux rapide des gestionnaires d'interruption) course avec toutes les interruptions sur local processeur désactivé. Cela se fait à veiller à ce que le gestionnaire d'interruption exécute sans interruption, aussi rapidement que possible. Plus encore, tout d'interruption L'exécution des gestionnaires avec leur courant interruption désactivé en ligne tous processeurs. Cela garantit que deux les gestionnaires d'interruption pour le même ligne d'interruption ne fonctionne pas en même temps. Il empêche également dispositif les auteurs de pilotes d'avoir à gérer les interruptions récursives, ce qui complique la programmation.
Ci-dessous fait partie du code dans le noyau Linux 4.15.18, ce qui montre que spiin_lock_irq () appellera __raw_spin_lock_irq (). Cependant, il ne sauvera pas les drapeaux que vous pouvez voir ci-dessous une partie du code, mais désactiver l'interruption.
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);
}
Ci-dessous le code montre spin_lock_irqsave () qui permet de gagner l'étape actuelle du drapeau, puis désactiver préempter.
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;
}
Cette question commence par l'affirmation fausse:
On an SMP machine we must use spin_lock_irqsave and not spin_lock_irq from interrupt context.
Aucun de ceux-ci devraient être utilisés d'interruption
contexte, le lait écrémé en poudre ou UP. Cela dit, spin_lock_irqsave()
peut être utilisé à partir du contexte d'interruption, comme étant plus universel
(Il peut être utilisé dans les deux contextes d'interruption et normal), mais
vous êtes censé utiliser spin_lock()
de contexte d'interruption,
et spin_lock_irq()
ou spin_lock_irqsave()
du contexte normal.
L'utilisation de spin_lock_irq()
est presque toujours la mauvaise chose
faire dans un contexte d'interruption, en étant SMP ou UP. Il peut travailler
parce que la plupart des gestionnaires d'interruption avec exécuter localement IRQ est activée,
mais vous ne devriez pas essayer.