spin_lock_irqsave vs spin_lock_irq
-
23-09-2019 - |
Pregunta
En una máquina SMP debemos usar spin_lock_irqsave
y no spin_lock_irq
del contexto de interrupción.
¿Por qué queremos para salvar las banderas (que contienen el SI)?
¿Hay alguna otra rutina de interrupción que podrían interrumpir?
Solución
Soy nuevo en el núcleo pero por lo que deduzco de libro de Robert Love "del núcleo Linux Desarrollo", si las interrupciones ya están deshabilitados en el procesador antes de su código comienza bloqueo, cuando se llama spin_unlock_irq va a liberar el bloqueo en una errónea manera. Si guarda las banderas y liberarlo con las banderas, la spin_lock_irqsave función se acaba de volver de 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...
Otros consejos
spin_lock_irqsave
se utiliza básicamente para guardar el estado de interrupción antes de tomar el bloqueo de giro, esto es debido a bloqueo de bucle desactiva la interrupción, cuando el bloqueo se toma en el contexto de interrupciones, y vuelve a habilitar cuando, mientras que el desbloqueo. El estado de las interrupciones se guarda de manera que debe restablecer las interrupciones de nuevo.
Ejemplo:
- Digamos que x interrupción se ha desactivado antes de adquirir bloqueo de bucle
-
spin_lock_irq
desactivará la x de interrupción y tomar el la cerradura -
spin_unlock_irq
permitirá a la x de interrupción.
Así que en la tercera etapa anterior después de soltar el bloqueo tendremos x interrupción habilitada que era anteriormente desactivado antes de adquirir el bloqueo.
Por lo que sólo cuando esté seguro de que las interrupciones no se desactivan sólo entonces usted debe spin_lock_irq
de lo contrario se debe utilizar siempre spin_lock_irqsave
.
La necesidad de spin_lock_irqsave
además spin_lock_irq
es bastante similar a la razón se necesita local_irq_save(flags)
además local_irq_disable
. Aquí hay una buena explicación de este requisito tomado de Linux Kernel Desarrollo Segunda edición de Robert Love.
La rutina local_irq_disable () es peligroso si las interrupciones no eran Ya desactivado antes de su invocación. La llamada correspondiente a local_irq_enable () permite interrupciones incondicionalmente, a pesar de la hecho de que se pusieron en marcha para empezar. En su lugar, se necesita un mecanismo para restaurar las interrupciones a un estado anterior. Esta es una preocupación común debido a que una ruta de código dado en el kernel se puede llegar con y sin interrupciones permitido, dependiendo de la cadena de llamadas. Por ejemplo, imaginar 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 uno que no lo hace. Porque se está convirtiendo más duro que el núcleo crece en tamaño y complejidad a conocer todo el código caminos que conducen a una función, es mucho más seguro para guardar el estado de el sistema de interrupción antes de su desactivación. Entonces, cuando usted está listo para interrupciones volver a activar, sólo tiene que devolverlos a 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 el parámetro flags (que debe ser definido como un largo sin signo) es aparentemente pasa por valor. Este parámetro contiene datos arquitectura-específico que contiene el estado de la interrupción Los sistemas. Debido a que al menos una arquitectura soportada incorpora información de la pila en el valor (ejem, SPARC), las banderas no se puede pasar a otra función (en concreto, debe permanecer en la misma pila cuadro). Por esta razón, la llamada para guardar y restaurar la llamada a interrupciones deben ocurrir en la misma función.
Todas las funciones anteriores puede ser llamado desde ambos interrupción y proceso contexto.
Por qué código del núcleo / subproceso en ejecución en el contexto de interrupción no puede dormir? que une a Robert Amores artículo , leí esto:
algunos controladores de interrupción (conocido en Linux como el manejador de interrupciones rápidas) de ejecución con todas las interrupciones en el local de procesador desactivado. Esto se hace para garantizar que se ejecuta el gestor de interrupciones sin interrupción, lo más rápidamente posible. Más aún, toda interrupción manejadores se ejecutan con su actual línea de interrupción deshabilitado en todas procesadores. Esto asegura que dos controladores de interrupción para el mismo línea de interrupción no se ejecutan concurrentemente. También evita que el dispositivo escritores conductor de tener que manejar interrupciones recursivas, que complican la programación.
A continuación se muestra parte del código en el kernel de Linux 4.15.18, lo que demuestra que spiin_lock_irq () llamará __raw_spin_lock_irq (). Sin embargo, no va a guardar ninguna bandera como se puede ver a continuación parte del código, pero desactivar la interrupción.
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);
}
A continuación código muestra spin_lock_irqsave () lo que ahorra la etapa actual de la bandera y luego desactivar Preferencia.
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;
}
Esta pregunta se inicia desde la falsa aseveración:
On an SMP machine we must use spin_lock_irqsave and not spin_lock_irq from interrupt context.
Ninguno de estos deben utilizarse de interrupción
contexto, el SMP o en UP. Dicho esto, spin_lock_irqsave()
pueden usarse de contexto de interrupción, como siendo más universal
(Que puede ser utilizado en ambos de interrupción y normales contextos), pero
que se supone que usar spin_lock()
del contexto de interrupción,
y spin_lock_irq()
o spin_lock_irqsave()
a partir del contexto normal.
El uso de spin_lock_irq()
es casi siempre lo incorrecto
que hacer en el contexto de interrupciones, siendo esta SMP o UP. Es posible que funcione
porque la mayoría de controladores de interrupción IRQ correr con habilitadas a nivel local,
pero no se debe tratar de eso.