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?

Était-ce utile?

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:

  1. permet de dire d'interruption x a été désactivé avant verrouillage de spin a été acquis
  2. spin_lock_irq désactive le x d'interruption et prendre le verrou
  3. 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.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top