linux-kernel spinlock

linux kernel - spin_lock_irqsave vs spin_lock_irq



linux-kernel spinlock (5)

Esta pregunta comienza con la afirmación falsa: On an SMP machine we must use spin_lock_irqsave and not spin_lock_irq from interrupt context.

Ninguno de estos debe usarse desde el contexto de interrupción, en SMP o en UP. Dicho esto, spin_lock_irqsave() puede usarse desde un contexto de interrupción, ya que es más universal (puede usarse tanto en contextos de interrupción como normales), pero se supone que spin_lock() usar spin_lock() del contexto de interrupción, y spin_lock_irq() o spin_lock_irqsave() del contexto normal. El uso de spin_lock_irq() casi siempre es incorrecto en el contexto de interrupción, ya sea SMP o UP. Puede funcionar porque la mayoría de los controladores de interrupción se ejecutan con IRQ habilitadas localmente, pero no debe intentarlo.

En una máquina SMP debemos usar spin_lock_irqsave y no spin_lock_irq del contexto de interrupción.

¿Por qué querríamos guardar las banderas (que contienen el SI)?

¿Hay otra rutina de interrupción que podría interrumpirnos?


La necesidad de spin_lock_irqsave además de spin_lock_irq es bastante similar a la razón por la que se necesita local_irq_save(flags) además de local_irq_disable . Aquí hay una buena explicación de este requisito tomado de Linux Kernel Development Second Edition por Robert Love.

La rutina local_irq_disable () es peligrosa si las interrupciones ya estaban deshabilitadas antes de su invocación. La llamada correspondiente a local_irq_enable () habilita incondicionalmente las interrupciones, a pesar del hecho de que para empezar estaban apagadas. En su lugar, se necesita un mecanismo para restaurar las interrupciones a un estado anterior. Esta es una preocupación común porque una ruta de código dada en el kernel se puede alcanzar con o sin interrupciones habilitadas, dependiendo de la cadena de llamadas. Por ejemplo, imagine que el fragmento de código anterior es parte de una función más grande. Imagine que esta función es llamada por otras dos funciones, una que deshabilita las interrupciones y otra que no. Debido a que se está volviendo más difícil a medida que el núcleo crece en tamaño y complejidad para conocer todas las rutas de código que conducen a una función, es mucho más seguro guardar el estado del sistema de interrupción antes de deshabilitarlo. Luego, cuando esté listo para volver a habilitar las interrupciones, simplemente restaure su estado original:

unsigned long flags; local_irq_save(flags); /* interrupts are now disabled */ /* ... */ local_irq_restore(flags); /* interrupts are restored to their previous state */

Tenga en cuenta que estos métodos se implementan al menos en parte como macros, por lo que el parámetro flags (que debe definirse como un largo sin signo) aparentemente se pasa por valor. Este parámetro contiene datos específicos de la arquitectura que contienen el estado de los sistemas de interrupción. Debido a que al menos una arquitectura admitida incorpora información de pila en el valor (ejem, SPARC), las banderas no se pueden pasar a otra función (específicamente, debe permanecer en el mismo marco de pila). Por este motivo, la llamada a guardar y la llamada a restaurar interrupciones deben ocurrir en la misma función.

Todas las funciones anteriores pueden llamarse desde el contexto de la interrupción y del proceso.


Lectura ¿Por qué el código / subproceso del kernel que se ejecuta en un contexto de interrupción no puede dormir? que enlaza con el article Robert Loves, leí esto:

algunos controladores de interrupción (conocidos en Linux como controladores de interrupción rápida) se ejecutan con todas las interrupciones en el procesador local deshabilitado. Esto se hace para garantizar que el controlador de interrupciones se ejecute sin interrupción lo más rápido posible. Más aún, todos los controladores de interrupción se ejecutan con su línea de interrupción actual desactivada en todos los procesadores. Esto garantiza que dos controladores de interrupción para la misma línea de interrupción no se ejecuten simultáneamente. También evita que los escritores de controladores de dispositivo tengan que manejar interrupciones recursivas, lo que complica la programación.


Soy nuevo en el kernel pero, según se desprende del libro "Desarrollo del kernel de Linux" de Robert Love, si las interrupciones ya están deshabilitadas en el procesador antes de que su código comience a bloquearse, cuando llame a spin_unlock_irq, liberará el bloqueo de manera errónea. Si guarda las banderas y las suelta con las banderas, la función spin_lock_irqsave simplemente devolverá la interrupción a su estado anterior.

Ejemplo 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

Ejemplo con spin_lock_irq (sin 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...


spin_lock_irqsave se usa básicamente para guardar el estado de interrupción antes de tomar el bloqueo de giro, esto se debe a que el bloqueo de giro desactiva la interrupción, cuando el bloqueo se toma en contexto de interrupción, y lo vuelve a habilitar mientras se está desbloqueando. El estado de interrupción se guarda para que vuelva a restablecer las interrupciones.

Ejemplo:

  1. Digamos que la interrupción x se desactivó antes de que se adquiriera el bloqueo de giro
  2. spin_lock_irq deshabilitará la interrupción x y tomará el bloqueo
  3. spin_unlock_irq habilitará la interrupción x.

Por lo tanto, en el tercer paso anterior, después de liberar el bloqueo, tendremos habilitada la interrupción x, que antes se desactivó antes de que se adquiriera el bloqueo.

Entonces, solo cuando esté seguro de que las interrupciones no están deshabilitadas solo entonces debe spin_lock_irq contrario, siempre debe usar spin_lock_irqsave .