548 lines
19 KiB
ReStructuredText
548 lines
19 KiB
ReStructuredText
.. SPDX-License-Identifier: GPL-2.0
|
|
|
|
.. include:: ../disclaimer-ita.rst
|
|
|
|
.. _it_kernel_hacking_locktypes:
|
|
|
|
========================================
|
|
Tipologie di blocco e le loro istruzioni
|
|
========================================
|
|
|
|
Introduzione
|
|
============
|
|
|
|
Il kernel fornisce un certo numero di primitive di blocco che possiamo dividere
|
|
in tre categorie:
|
|
|
|
- blocchi ad attesa con sospensione
|
|
- blocchi locali per CPU
|
|
- blocchi ad attesa attiva
|
|
|
|
Questo documento descrive questi tre tipi e fornisce istruzioni su come
|
|
annidarli, ed usarli su kernel PREEMPT_RT.
|
|
|
|
Categorie di blocchi
|
|
====================
|
|
|
|
Blocchi ad attesa con sospensione
|
|
---------------------------------
|
|
|
|
I blocchi ad attesa con sospensione possono essere acquisiti solo in un contesti
|
|
dov'è possibile la prelazione.
|
|
|
|
Diverse implementazioni permettono di usare try_lock() anche in altri contesti,
|
|
nonostante ciò è bene considerare anche la sicurezza dei corrispondenti
|
|
unlock(). Inoltre, vanno prese in considerazione anche le varianti di *debug*
|
|
di queste primitive. Insomma, non usate i blocchi ad attesa con sospensioni in
|
|
altri contesti a meno che proprio non vi siano alternative.
|
|
|
|
In questa categoria troviamo:
|
|
|
|
- mutex
|
|
- rt_mutex
|
|
- semaphore
|
|
- rw_semaphore
|
|
- ww_mutex
|
|
- percpu_rw_semaphore
|
|
|
|
Nei kernel con PREEMPT_RT, i seguenti blocchi sono convertiti in blocchi ad
|
|
attesa con sospensione:
|
|
|
|
- local_lock
|
|
- spinlock_t
|
|
- rwlock_t
|
|
|
|
Blocchi locali per CPU
|
|
----------------------
|
|
|
|
- local_lock
|
|
|
|
Su kernel non-PREEMPT_RT, le funzioni local_lock gestiscono le primitive di
|
|
disabilitazione di prelazione ed interruzioni. Al contrario di altri meccanismi,
|
|
la disabilitazione della prelazione o delle interruzioni sono puri meccanismi
|
|
per il controllo della concorrenza su una CPU e quindi non sono adatti per la
|
|
gestione della concorrenza inter-CPU.
|
|
|
|
Blocchi ad attesa attiva
|
|
------------------------
|
|
|
|
- raw_spinlcok_t
|
|
- bit spinlocks
|
|
|
|
Nei kernel non-PREEMPT_RT, i seguenti blocchi sono ad attesa attiva:
|
|
|
|
- spinlock_t
|
|
- rwlock_t
|
|
|
|
Implicitamente, i blocchi ad attesa attiva disabilitano la prelazione e le
|
|
funzioni lock/unlock hanno anche dei suffissi per gestire il livello di
|
|
protezione:
|
|
|
|
=================== =========================================================================
|
|
_bh() disabilita / abilita *bottom halves* (interruzioni software)
|
|
_irq() disabilita / abilita le interruzioni
|
|
_irqsave/restore() salva e disabilita le interruzioni / ripristina ed attiva le interruzioni
|
|
=================== =========================================================================
|
|
|
|
Semantica del proprietario
|
|
==========================
|
|
|
|
Eccetto i semafori, i sopracitati tipi di blocchi hanno tutti una semantica
|
|
molto stringente riguardo al proprietario di un blocco:
|
|
|
|
Il contesto (attività) che ha acquisito il blocco deve rilasciarlo
|
|
|
|
I semafori rw_semaphores hanno un'interfaccia speciale che permette anche ai non
|
|
proprietari del blocco di rilasciarlo per i lettori.
|
|
|
|
rtmutex
|
|
=======
|
|
|
|
I blocchi a mutua esclusione RT (*rtmutex*) sono un sistema a mutua esclusione
|
|
con supporto all'ereditarietà della priorità (PI).
|
|
|
|
Questo meccanismo ha delle limitazioni sui kernel non-PREEMPT_RT dovuti alla
|
|
prelazione e alle sezioni con interruzioni disabilitate.
|
|
|
|
Chiaramente, questo meccanismo non può avvalersi della prelazione su una sezione
|
|
dove la prelazione o le interruzioni sono disabilitate; anche sui kernel
|
|
PREEMPT_RT. Tuttavia, i kernel PREEMPT_RT eseguono la maggior parte delle
|
|
sezioni in contesti dov'è possibile la prelazione, specialmente in contesti
|
|
d'interruzione (anche software). Questa conversione permette a spinlock_t e
|
|
rwlock_t di essere implementati usando rtmutex.
|
|
|
|
semaphore
|
|
=========
|
|
|
|
La primitiva semaphore implementa un semaforo con contatore.
|
|
|
|
I semafori vengono spesso utilizzati per la serializzazione e l'attesa, ma per
|
|
nuovi casi d'uso si dovrebbero usare meccanismi diversi, come mutex e
|
|
completion.
|
|
|
|
semaphore e PREEMPT_RT
|
|
----------------------
|
|
|
|
I kernel PREEMPT_RT non cambiano l'implementazione di semaphore perché non hanno
|
|
un concetto di proprietario, dunque impediscono a PREEMPT_RT d'avere
|
|
l'ereditarietà della priorità sui semafori. Un proprietario sconosciuto non può
|
|
ottenere una priorità superiore. Di consequenza, bloccarsi sui semafori porta
|
|
all'inversione di priorità.
|
|
|
|
|
|
rw_semaphore
|
|
============
|
|
|
|
Il blocco rw_semaphore è un meccanismo che permette più lettori ma un solo scrittore.
|
|
|
|
Sui kernel non-PREEMPT_RT l'implementazione è imparziale, quindi previene
|
|
l'inedia dei processi scrittori.
|
|
|
|
Questi blocchi hanno una semantica molto stringente riguardo il proprietario, ma
|
|
offre anche interfacce speciali che permettono ai processi non proprietari di
|
|
rilasciare un processo lettore. Queste interfacce funzionano indipendentemente
|
|
dalla configurazione del kernel.
|
|
|
|
rw_semaphore e PREEMPT_RT
|
|
-------------------------
|
|
|
|
I kernel PREEMPT_RT sostituiscono i rw_semaphore con un'implementazione basata
|
|
su rt_mutex, e questo ne modifica l'imparzialità:
|
|
|
|
Dato che uno scrittore rw_semaphore non può assicurare la propria priorità ai
|
|
suoi lettori, un lettore con priorità più bassa che ha subito la prelazione
|
|
continuerà a trattenere il blocco, quindi porta all'inedia anche gli scrittori
|
|
con priorità più alta. Per contro, dato che i lettori possono garantire la
|
|
propria priorità agli scrittori, uno scrittore a bassa priorità che subisce la
|
|
prelazione vedrà la propria priorità alzata finché non rilascerà il blocco, e
|
|
questo preverrà l'inedia dei processi lettori a causa di uno scrittore.
|
|
|
|
|
|
local_lock
|
|
==========
|
|
|
|
I local_lock forniscono nomi agli ambiti di visibilità delle sezioni critiche
|
|
protette tramite la disattivazione della prelazione o delle interruzioni.
|
|
|
|
Sui kernel non-PREEMPT_RT le operazioni local_lock si traducono
|
|
nell'abilitazione o disabilitazione della prelazione o le interruzioni.
|
|
|
|
=============================== ======================
|
|
local_lock(&llock) preempt_disable()
|
|
local_unlock(&llock) preempt_enable()
|
|
local_lock_irq(&llock) local_irq_disable()
|
|
local_unlock_irq(&llock) local_irq_enable()
|
|
local_lock_irqsave(&llock) local_irq_save()
|
|
local_unlock_irqrestore(&llock) local_irq_restore()
|
|
=============================== ======================
|
|
|
|
Gli ambiti di visibilità con nome hanno due vantaggi rispetto alle primitive di
|
|
base:
|
|
|
|
- Il nome del blocco permette di fare un'analisi statica, ed è anche chiaro su
|
|
cosa si applichi la protezione cosa che invece non si può fare con le
|
|
classiche primitive in quanto sono opache e senza alcun ambito di
|
|
visibilità.
|
|
|
|
- Se viene abilitato lockdep, allora local_lock ottiene un lockmap che
|
|
permette di verificare la bontà della protezione. Per esempio, questo può
|
|
identificare i casi dove una funzione usa preempt_disable() come meccanismo
|
|
di protezione in un contesto d'interruzione (anche software). A parte
|
|
questo, lockdep_assert_held(&llock) funziona come tutte le altre primitive
|
|
di sincronizzazione.
|
|
|
|
local_lock e PREEMPT_RT
|
|
-------------------------
|
|
|
|
I kernel PREEMPT_RT sostituiscono local_lock con uno spinlock_t per CPU, quindi
|
|
ne cambia la semantica:
|
|
|
|
- Tutte le modifiche a spinlock_t si applicano anche a local_lock
|
|
|
|
L'uso di local_lock
|
|
-------------------
|
|
|
|
I local_lock dovrebbero essere usati su kernel non-PREEMPT_RT quando la
|
|
disabilitazione della prelazione o delle interruzioni è il modo più adeguato per
|
|
gestire l'accesso concorrente a strutture dati per CPU.
|
|
|
|
Questo meccanismo non è adatto alla protezione da prelazione o interruzione su
|
|
kernel PREEMPT_RT dato che verrà convertito in spinlock_t.
|
|
|
|
|
|
raw_spinlock_t e spinlock_t
|
|
===========================
|
|
|
|
raw_spinlock_t
|
|
--------------
|
|
|
|
I blocco raw_spinlock_t è un blocco ad attesa attiva su tutti i tipi di kernel,
|
|
incluso quello PREEMPT_RT. Usate raw_spinlock_t solo in sezioni critiche nel
|
|
cuore del codice, nella gestione delle interruzioni di basso livello, e in posti
|
|
dove è necessario disabilitare la prelazione o le interruzioni. Per esempio, per
|
|
accedere in modo sicuro lo stato dell'hardware. A volte, i raw_spinlock_t
|
|
possono essere usati quando la sezione critica è minuscola, per evitare gli
|
|
eccessi di un rtmutex.
|
|
|
|
spinlock_t
|
|
----------
|
|
|
|
Il significato di spinlock_t cambia in base allo stato di PREEMPT_RT.
|
|
|
|
Sui kernel non-PREEMPT_RT, spinlock_t si traduce in un raw_spinlock_t ed ha
|
|
esattamente lo stesso significato.
|
|
|
|
spinlock_t e PREEMPT_RT
|
|
-----------------------
|
|
|
|
Sui kernel PREEMPT_RT, spinlock_t ha un'implementazione dedicata che si basa
|
|
sull'uso di rt_mutex. Questo ne modifica il significato:
|
|
|
|
- La prelazione non viene disabilitata.
|
|
|
|
- I suffissi relativi alla interruzioni (_irq, _irqsave / _irqrestore) per le
|
|
operazioni spin_lock / spin_unlock non hanno alcun effetto sullo stato delle
|
|
interruzioni della CPU.
|
|
|
|
- I suffissi relativi alle interruzioni software (_bh()) disabilitano i
|
|
relativi gestori d'interruzione.
|
|
|
|
I kernel non-PREEMPT_RT disabilitano la prelazione per ottenere lo stesso effetto.
|
|
|
|
I kernel PREEMPT_RT usano un blocco per CPU per la serializzazione, il che
|
|
permette di tenere attiva la prelazione. Il blocco disabilita i gestori
|
|
d'interruzione software e previene la rientranza vista la prelazione attiva.
|
|
|
|
A parte quanto appena discusso, i kernel PREEMPT_RT preservano il significato
|
|
di tutti gli altri aspetti di spinlock_t:
|
|
|
|
- Le attività che trattengono un blocco spinlock_t non migrano su altri
|
|
processori. Disabilitando la prelazione, i kernel non-PREEMPT_RT evitano la
|
|
migrazione. Invece, i kernel PREEMPT_RT disabilitano la migrazione per
|
|
assicurarsi che i puntatori a variabili per CPU rimangano validi anche
|
|
quando un'attività subisce la prelazione.
|
|
|
|
- Lo stato di un'attività si mantiene durante le acquisizioni del blocco al
|
|
fine di garantire che le regole basate sullo stato delle attività si possano
|
|
applicare a tutte le configurazioni del kernel. I kernel non-PREEMPT_RT
|
|
lasciano lo stato immutato. Tuttavia, la funzionalità PREEMPT_RT deve
|
|
cambiare lo stato se l'attività si blocca durante l'acquisizione. Dunque,
|
|
salva lo stato attuale prima di bloccarsi ed il rispettivo risveglio lo
|
|
ripristinerà come nell'esempio seguente::
|
|
|
|
task->state = TASK_INTERRUPTIBLE
|
|
lock()
|
|
block()
|
|
task->saved_state = task->state
|
|
task->state = TASK_UNINTERRUPTIBLE
|
|
schedule()
|
|
lock wakeup
|
|
task->state = task->saved_state
|
|
|
|
Altri tipi di risvegli avrebbero impostato direttamente lo stato a RUNNING,
|
|
ma in questo caso non avrebbe funzionato perché l'attività deve rimanere
|
|
bloccata fintanto che il blocco viene trattenuto. Quindi, lo stato salvato
|
|
viene messo a RUNNING quando il risveglio di un non-blocco cerca di
|
|
risvegliare un'attività bloccata in attesa del rilascio di uno spinlock. Poi,
|
|
quando viene completata l'acquisizione del blocco, il suo risveglio
|
|
ripristinerà lo stato salvato, in questo caso a RUNNING::
|
|
|
|
task->state = TASK_INTERRUPTIBLE
|
|
lock()
|
|
block()
|
|
task->saved_state = task->state
|
|
task->state = TASK_UNINTERRUPTIBLE
|
|
schedule()
|
|
non lock wakeup
|
|
task->saved_state = TASK_RUNNING
|
|
|
|
lock wakeup
|
|
task->state = task->saved_state
|
|
|
|
Questo garantisce che il vero risveglio non venga perso.
|
|
|
|
rwlock_t
|
|
========
|
|
|
|
Il blocco rwlock_t è un meccanismo che permette più lettori ma un solo scrittore.
|
|
|
|
Sui kernel non-PREEMPT_RT questo è un blocco ad attesa e per i suoi suffissi si
|
|
applicano le stesse regole per spinlock_t. La sua implementazione è imparziale,
|
|
quindi previene l'inedia dei processi scrittori.
|
|
|
|
rwlock_t e PREEMPT_RT
|
|
---------------------
|
|
|
|
Sui kernel PREEMPT_RT rwlock_t ha un'implementazione dedicata che si basa
|
|
sull'uso di rt_mutex. Questo ne modifica il significato:
|
|
|
|
- Tutte le modifiche fatte a spinlock_t si applicano anche a rwlock_t.
|
|
|
|
- Dato che uno scrittore rw_semaphore non può assicurare la propria priorità ai
|
|
suoi lettori, un lettore con priorità più bassa che ha subito la prelazione
|
|
continuerà a trattenere il blocco, quindi porta all'inedia anche gli
|
|
scrittori con priorità più alta. Per contro, dato che i lettori possono
|
|
garantire la propria priorità agli scrittori, uno scrittore a bassa priorità
|
|
che subisce la prelazione vedrà la propria priorità alzata finché non
|
|
rilascerà il blocco, e questo preverrà l'inedia dei processi lettori a causa
|
|
di uno scrittore.
|
|
|
|
|
|
Precisazioni su PREEMPT_RT
|
|
==========================
|
|
|
|
local_lock su RT
|
|
----------------
|
|
|
|
Sui kernel PREEMPT_RT Ci sono alcune implicazioni dovute alla conversione di
|
|
local_lock in un spinlock_t. Per esempio, su un kernel non-PREEMPT_RT il
|
|
seguente codice funzionerà come ci si aspetta::
|
|
|
|
local_lock_irq(&local_lock);
|
|
raw_spin_lock(&lock);
|
|
|
|
ed è equivalente a::
|
|
|
|
raw_spin_lock_irq(&lock);
|
|
|
|
Ma su un kernel PREEMPT_RT questo codice non funzionerà perché local_lock_irq()
|
|
si traduce in uno spinlock_t per CPU che non disabilita né le interruzioni né la
|
|
prelazione. Il seguente codice funzionerà su entrambe i kernel con o senza
|
|
PREEMPT_RT::
|
|
|
|
local_lock_irq(&local_lock);
|
|
spin_lock(&lock);
|
|
|
|
Un altro dettaglio da tenere a mente con local_lock è che ognuno di loro ha un
|
|
ambito di protezione ben preciso. Dunque, la seguente sostituzione è errate::
|
|
|
|
|
|
func1()
|
|
{
|
|
local_irq_save(flags); -> local_lock_irqsave(&local_lock_1, flags);
|
|
func3();
|
|
local_irq_restore(flags); -> local_unlock_irqrestore(&local_lock_1, flags);
|
|
}
|
|
|
|
func2()
|
|
{
|
|
local_irq_save(flags); -> local_lock_irqsave(&local_lock_2, flags);
|
|
func3();
|
|
local_irq_restore(flags); -> local_unlock_irqrestore(&local_lock_2, flags);
|
|
}
|
|
|
|
func3()
|
|
{
|
|
lockdep_assert_irqs_disabled();
|
|
access_protected_data();
|
|
}
|
|
|
|
Questo funziona correttamente su un kernel non-PREEMPT_RT, ma su un kernel
|
|
PREEMPT_RT local_lock_1 e local_lock_2 sono distinti e non possono serializzare
|
|
i chiamanti di func3(). L'*assert* di lockdep verrà attivato su un kernel
|
|
PREEMPT_RT perché local_lock_irqsave() non disabilita le interruzione a casa
|
|
della specifica semantica di spinlock_t in PREEMPT_RT. La corretta sostituzione
|
|
è::
|
|
|
|
func1()
|
|
{
|
|
local_irq_save(flags); -> local_lock_irqsave(&local_lock, flags);
|
|
func3();
|
|
local_irq_restore(flags); -> local_unlock_irqrestore(&local_lock, flags);
|
|
}
|
|
|
|
func2()
|
|
{
|
|
local_irq_save(flags); -> local_lock_irqsave(&local_lock, flags);
|
|
func3();
|
|
local_irq_restore(flags); -> local_unlock_irqrestore(&local_lock, flags);
|
|
}
|
|
|
|
func3()
|
|
{
|
|
lockdep_assert_held(&local_lock);
|
|
access_protected_data();
|
|
}
|
|
|
|
spinlock_t e rwlock_t
|
|
---------------------
|
|
|
|
Ci sono alcune conseguenze di cui tener conto dal cambiamento di semantica di
|
|
spinlock_t e rwlock_t sui kernel PREEMPT_RT. Per esempio, sui kernel non
|
|
PREEMPT_RT il seguente codice funziona come ci si aspetta::
|
|
|
|
local_irq_disable();
|
|
spin_lock(&lock);
|
|
|
|
ed è equivalente a::
|
|
|
|
spin_lock_irq(&lock);
|
|
|
|
Lo stesso vale per rwlock_t e le varianti con _irqsave().
|
|
|
|
Sui kernel PREEMPT_RT questo codice non funzionerà perché gli rtmutex richiedono
|
|
un contesto con la possibilità di prelazione. Al suo posto, usate
|
|
spin_lock_irq() o spin_lock_irqsave() e le loro controparti per il rilascio. I
|
|
kernel PREEMPT_RT offrono un meccanismo local_lock per i casi in cui la
|
|
disabilitazione delle interruzioni ed acquisizione di un blocco devono rimanere
|
|
separati. Acquisire un local_lock àncora un processo ad una CPU permettendo cose
|
|
come un'acquisizione di un blocco con interruzioni disabilitate per singola CPU.
|
|
|
|
Il tipico scenario è quando si vuole proteggere una variabile di processore nel
|
|
contesto di un thread::
|
|
|
|
|
|
struct foo *p = get_cpu_ptr(&var1);
|
|
|
|
spin_lock(&p->lock);
|
|
p->count += this_cpu_read(var2);
|
|
|
|
Questo codice è corretto su un kernel non-PREEMPT_RT, ma non lo è su un
|
|
PREEMPT_RT. La modifica della semantica di spinlock_t su PREEMPT_RT non permette
|
|
di acquisire p->lock perché, implicitamente, get_cpu_ptr() disabilita la
|
|
prelazione. La seguente sostituzione funzionerà su entrambe i kernel::
|
|
|
|
struct foo *p;
|
|
|
|
migrate_disable();
|
|
p = this_cpu_ptr(&var1);
|
|
spin_lock(&p->lock);
|
|
p->count += this_cpu_read(var2);
|
|
|
|
La funzione migrate_disable() assicura che il processo venga tenuto sulla CPU
|
|
corrente, e di conseguenza garantisce che gli accessi per-CPU alle variabili var1 e
|
|
var2 rimangano sulla stessa CPU fintanto che il processo rimane prelabile.
|
|
|
|
La sostituzione con migrate_disable() non funzionerà nel seguente scenario::
|
|
|
|
func()
|
|
{
|
|
struct foo *p;
|
|
|
|
migrate_disable();
|
|
p = this_cpu_ptr(&var1);
|
|
p->val = func2();
|
|
|
|
Questo non funziona perché migrate_disable() non protegge dal ritorno da un
|
|
processo che aveva avuto il diritto di prelazione. Una sostituzione più adatta
|
|
per questo caso è::
|
|
|
|
func()
|
|
{
|
|
struct foo *p;
|
|
|
|
local_lock(&foo_lock);
|
|
p = this_cpu_ptr(&var1);
|
|
p->val = func2();
|
|
|
|
Su un kernel non-PREEMPT_RT, questo codice protegge dal rientro disabilitando la
|
|
prelazione. Su un kernel PREEMPT_RT si ottiene lo stesso risultato acquisendo lo
|
|
spinlock di CPU.
|
|
|
|
raw_spinlock_t su RT
|
|
--------------------
|
|
|
|
Acquisire un raw_spinlock_t disabilita la prelazione e possibilmente anche le
|
|
interruzioni, quindi la sezione critica deve evitare di acquisire uno spinlock_t
|
|
o rwlock_t. Per esempio, la sezione critica non deve fare allocazioni di
|
|
memoria. Su un kernel non-PREEMPT_RT il seguente codice funziona perfettamente::
|
|
|
|
raw_spin_lock(&lock);
|
|
p = kmalloc(sizeof(*p), GFP_ATOMIC);
|
|
|
|
Ma lo stesso codice non funziona su un kernel PREEMPT_RT perché l'allocatore di
|
|
memoria può essere oggetto di prelazione e quindi non può essere chiamato in un
|
|
contesto atomico. Tuttavia, si può chiamare l'allocatore di memoria quando si
|
|
trattiene un blocco *non-raw* perché non disabilitano la prelazione sui kernel
|
|
PREEMPT_RT::
|
|
|
|
spin_lock(&lock);
|
|
p = kmalloc(sizeof(*p), GFP_ATOMIC);
|
|
|
|
|
|
bit spinlocks
|
|
-------------
|
|
|
|
I kernel PREEMPT_RT non possono sostituire i bit spinlock perché un singolo bit
|
|
è troppo piccolo per farci stare un rtmutex. Dunque, la semantica dei bit
|
|
spinlock è mantenuta anche sui kernel PREEMPT_RT. Quindi, le precisazioni fatte
|
|
per raw_spinlock_t valgono anche qui.
|
|
|
|
In PREEMPT_RT, alcuni bit spinlock sono sostituiti con normali spinlock_t usando
|
|
condizioni di preprocessore in base a dove vengono usati. Per contro, questo non
|
|
serve quando si sostituiscono gli spinlock_t. Invece, le condizioni poste sui
|
|
file d'intestazione e sul cuore dell'implementazione della sincronizzazione
|
|
permettono al compilatore di effettuare la sostituzione in modo trasparente.
|
|
|
|
|
|
Regole d'annidamento dei tipi di blocchi
|
|
========================================
|
|
|
|
Le regole principali sono:
|
|
|
|
- I tipi di blocco appartenenti alla stessa categoria possono essere annidati
|
|
liberamente a patto che si rispetti l'ordine di blocco al fine di evitare
|
|
stalli.
|
|
|
|
- I blocchi con sospensione non possono essere annidati in blocchi del tipo
|
|
CPU locale o ad attesa attiva
|
|
|
|
- I blocchi ad attesa attiva e su CPU locale possono essere annidati nei
|
|
blocchi ad attesa con sospensione.
|
|
|
|
- I blocchi ad attesa attiva possono essere annidati in qualsiasi altro tipo.
|
|
|
|
Queste limitazioni si applicano ad entrambe i kernel con o senza PREEMPT_RT.
|
|
|
|
Il fatto che un kernel PREEMPT_RT cambi i blocchi spinlock_t e rwlock_t dal tipo
|
|
ad attesa attiva a quello con sospensione, e che sostituisca local_lock con uno
|
|
spinlock_t per CPU, significa che non possono essere acquisiti quando si è in un
|
|
blocco raw_spinlock. Ne consegue il seguente ordine d'annidamento:
|
|
|
|
1) blocchi ad attesa con sospensione
|
|
2) spinlock_t, rwlock_t, local_lock
|
|
3) raw_spinlock_t e bit spinlocks
|
|
|
|
Se queste regole verranno violate, allora lockdep se ne accorgerà e questo sia
|
|
con o senza PREEMPT_RT.
|