162306a36Sopenharmony_ci.. include:: ../disclaimer-ita.rst
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci.. c:namespace:: it_IT
462306a36Sopenharmony_ci
562306a36Sopenharmony_ci:Original: :ref:`Documentation/kernel-hacking/locking.rst <kernel_hacking_lock>`
662306a36Sopenharmony_ci:Translator: Federico Vaga <federico.vaga@vaga.pv.it>
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci.. _it_kernel_hacking_lock:
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci==========================================
1162306a36Sopenharmony_ciL'inaffidabile guida alla sincronizzazione
1262306a36Sopenharmony_ci==========================================
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci:Author: Rusty Russell
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ciIntroduzione
1762306a36Sopenharmony_ci============
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ciBenvenuto, alla notevole ed inaffidabile guida ai problemi di sincronizzazione
2062306a36Sopenharmony_ci(locking) nel kernel. Questo documento descrive il sistema di sincronizzazione
2162306a36Sopenharmony_cinel kernel Linux 2.6.
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ciDato il largo utilizzo del multi-threading e della prelazione nel kernel
2462306a36Sopenharmony_ciLinux, chiunque voglia dilettarsi col kernel deve conoscere i concetti
2562306a36Sopenharmony_cifondamentali della concorrenza e della sincronizzazione nei sistemi
2662306a36Sopenharmony_cimulti-processore.
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ciIl problema con la concorrenza
2962306a36Sopenharmony_ci==============================
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci(Saltatelo se sapete già cos'è una corsa critica).
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ciIn un normale programma, potete incrementare un contatore nel seguente modo:
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci::
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci          contatore++;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ciQuesto è quello che vi aspettereste che accada sempre:
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci.. table:: Risultati attesi
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci  +------------------------------------+------------------------------------+
4562306a36Sopenharmony_ci  | Istanza 1                          | Istanza 2                          |
4662306a36Sopenharmony_ci  +====================================+====================================+
4762306a36Sopenharmony_ci  | leggi contatore (5)                |                                    |
4862306a36Sopenharmony_ci  +------------------------------------+------------------------------------+
4962306a36Sopenharmony_ci  | aggiungi 1 (6)                     |                                    |
5062306a36Sopenharmony_ci  +------------------------------------+------------------------------------+
5162306a36Sopenharmony_ci  | scrivi contatore (6)               |                                    |
5262306a36Sopenharmony_ci  +------------------------------------+------------------------------------+
5362306a36Sopenharmony_ci  |                                    | leggi contatore (6)                |
5462306a36Sopenharmony_ci  +------------------------------------+------------------------------------+
5562306a36Sopenharmony_ci  |                                    | aggiungi 1 (7)                     |
5662306a36Sopenharmony_ci  +------------------------------------+------------------------------------+
5762306a36Sopenharmony_ci  |                                    | scrivi contatore (7)               |
5862306a36Sopenharmony_ci  +------------------------------------+------------------------------------+
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ciQuesto è quello che potrebbe succedere in realtà:
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci.. table:: Possibile risultato
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci  +------------------------------------+------------------------------------+
6562306a36Sopenharmony_ci  | Istanza 1                          | Istanza 2                          |
6662306a36Sopenharmony_ci  +====================================+====================================+
6762306a36Sopenharmony_ci  | leggi contatore (5)                |                                    |
6862306a36Sopenharmony_ci  +------------------------------------+------------------------------------+
6962306a36Sopenharmony_ci  |                                    | leggi contatore (5)                |
7062306a36Sopenharmony_ci  +------------------------------------+------------------------------------+
7162306a36Sopenharmony_ci  | aggiungi 1 (6)                     |                                    |
7262306a36Sopenharmony_ci  +------------------------------------+------------------------------------+
7362306a36Sopenharmony_ci  |                                    | aggiungi 1 (6)                     |
7462306a36Sopenharmony_ci  +------------------------------------+------------------------------------+
7562306a36Sopenharmony_ci  | scrivi contatore (6)               |                                    |
7662306a36Sopenharmony_ci  +------------------------------------+------------------------------------+
7762306a36Sopenharmony_ci  |                                    | scrivi contatore (6)               |
7862306a36Sopenharmony_ci  +------------------------------------+------------------------------------+
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ciCorse critiche e sezioni critiche
8262306a36Sopenharmony_ci---------------------------------
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ciQuesta sovrapposizione, ovvero quando un risultato dipende dal tempo che
8562306a36Sopenharmony_ciintercorre fra processi diversi, è chiamata corsa critica. La porzione
8662306a36Sopenharmony_cidi codice che contiene questo problema è chiamata sezione critica.
8762306a36Sopenharmony_ciIn particolar modo da quando Linux ha incominciato a girare su
8862306a36Sopenharmony_cimacchine multi-processore, le sezioni critiche sono diventate uno dei
8962306a36Sopenharmony_cimaggiori problemi di progettazione ed implementazione del kernel.
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ciLa prelazione può sortire gli stessi effetti, anche se c'è una sola CPU:
9262306a36Sopenharmony_ciinterrompendo un processo nella sua sezione critica otterremo comunque
9362306a36Sopenharmony_cila stessa corsa critica. In questo caso, il thread che si avvicenda
9462306a36Sopenharmony_cinell'esecuzione potrebbe eseguire anch'esso la sezione critica.
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ciLa soluzione è quella di riconoscere quando avvengono questi accessi
9762306a36Sopenharmony_cisimultanei, ed utilizzare i *lock* per accertarsi che solo un'istanza
9862306a36Sopenharmony_ciper volta possa entrare nella sezione critica. Il kernel offre delle buone
9962306a36Sopenharmony_cifunzioni a questo scopo. E poi ci sono quelle meno buone, ma farò finta
10062306a36Sopenharmony_ciche non esistano.
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ciSincronizzazione nel kernel Linux
10362306a36Sopenharmony_ci=================================
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ciSe dovessi darvi un suggerimento sulla sincronizzazione: **mantenetela
10662306a36Sopenharmony_cisemplice**.
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ciSiate riluttanti nell'introduzione di nuovi *lock*.
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ciI due principali tipi di *lock* nel kernel: spinlock e mutex
11162306a36Sopenharmony_ci------------------------------------------------------------
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ciCi sono due tipi principali di *lock* nel kernel. Il tipo fondamentale è lo
11462306a36Sopenharmony_cispinlock (``include/asm/spinlock.h``), un semplice *lock* che può essere
11562306a36Sopenharmony_citrattenuto solo da un processo: se non si può trattenere lo spinlock, allora
11662306a36Sopenharmony_cirimane in attesa attiva (in inglese *spinning*) finché non ci riesce.
11762306a36Sopenharmony_ciGli spinlock sono molto piccoli e rapidi, possono essere utilizzati ovunque.
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ciIl secondo tipo è il mutex (``include/linux/mutex.h``): è come uno spinlock,
12062306a36Sopenharmony_cima potreste bloccarvi trattenendolo. Se non potete trattenere un mutex
12162306a36Sopenharmony_ciil vostro processo si auto-sospenderà; verrà riattivato quando il mutex
12262306a36Sopenharmony_civerrà rilasciato. Questo significa che il processore potrà occuparsi d'altro
12362306a36Sopenharmony_cimentre il vostro processo è in attesa. Esistono molti casi in cui non potete
12462306a36Sopenharmony_cipermettervi di sospendere un processo (vedere
12562306a36Sopenharmony_ci`Quali funzioni possono essere chiamate in modo sicuro dalle interruzioni?`_)
12662306a36Sopenharmony_cie quindi dovrete utilizzare gli spinlock.
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ciNessuno di questi *lock* è ricorsivo: vedere
12962306a36Sopenharmony_ci`Stallo: semplice ed avanzato`_
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ciI *lock* e i kernel per sistemi monoprocessore
13262306a36Sopenharmony_ci----------------------------------------------
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ciPer i kernel compilati senza ``CONFIG_SMP`` e senza ``CONFIG_PREEMPT``
13562306a36Sopenharmony_cigli spinlock non esistono. Questa è un'ottima scelta di progettazione:
13662306a36Sopenharmony_ciquando nessun altro processo può essere eseguito in simultanea, allora
13762306a36Sopenharmony_cinon c'è la necessità di avere un *lock*.
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ciSe il kernel è compilato senza ``CONFIG_SMP`` ma con ``CONFIG_PREEMPT``,
14062306a36Sopenharmony_ciallora gli spinlock disabilitano la prelazione; questo è sufficiente a
14162306a36Sopenharmony_ciprevenire le corse critiche. Nella maggior parte dei casi, possiamo considerare
14262306a36Sopenharmony_cila prelazione equivalente ad un sistema multi-processore senza preoccuparci
14362306a36Sopenharmony_cidi trattarla indipendentemente.
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ciDovreste verificare sempre la sincronizzazione con le opzioni ``CONFIG_SMP`` e
14662306a36Sopenharmony_ci``CONFIG_PREEMPT`` abilitate, anche quando non avete un sistema
14762306a36Sopenharmony_cimulti-processore, questo vi permetterà di identificare alcuni problemi
14862306a36Sopenharmony_cidi sincronizzazione.
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ciCome vedremo di seguito, i mutex continuano ad esistere perché sono necessari
15162306a36Sopenharmony_ciper la sincronizzazione fra processi in contesto utente.
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ciSincronizzazione in contesto utente
15462306a36Sopenharmony_ci-----------------------------------
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ciSe avete una struttura dati che verrà utilizzata solo dal contesto utente,
15762306a36Sopenharmony_ciallora, per proteggerla, potete utilizzare un semplice mutex
15862306a36Sopenharmony_ci(``include/linux/mutex.h``). Questo è il caso più semplice: inizializzate il
15962306a36Sopenharmony_cimutex; invocate mutex_lock_interruptible() per trattenerlo e
16062306a36Sopenharmony_cimutex_unlock() per rilasciarlo. C'è anche mutex_lock()
16162306a36Sopenharmony_cima questa dovrebbe essere evitata perché non ritorna in caso di segnali.
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ciPer esempio: ``net/netfilter/nf_sockopt.c`` permette la registrazione
16462306a36Sopenharmony_cidi nuove chiamate per setsockopt() e getsockopt()
16562306a36Sopenharmony_ciusando la funzione nf_register_sockopt(). La registrazione e
16662306a36Sopenharmony_cila rimozione vengono eseguite solamente quando il modulo viene caricato
16762306a36Sopenharmony_cio scaricato (e durante l'avvio del sistema, qui non abbiamo concorrenza),
16862306a36Sopenharmony_cie la lista delle funzioni registrate viene consultata solamente quando
16962306a36Sopenharmony_cisetsockopt() o getsockopt() sono sconosciute al sistema.
17062306a36Sopenharmony_ciIn questo caso ``nf_sockopt_mutex`` è perfetto allo scopo, in particolar modo
17162306a36Sopenharmony_civisto che setsockopt e getsockopt potrebbero dormire.
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ciSincronizzazione fra il contesto utente e i softirq
17462306a36Sopenharmony_ci---------------------------------------------------
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ciSe un softirq condivide dati col contesto utente, avete due problemi.
17762306a36Sopenharmony_ciPrimo, il contesto utente corrente potrebbe essere interroto da un softirq,
17862306a36Sopenharmony_cie secondo, la sezione critica potrebbe essere eseguita da un altro
17962306a36Sopenharmony_ciprocessore. Questo è quando spin_lock_bh()
18062306a36Sopenharmony_ci(``include/linux/spinlock.h``) viene utilizzato. Questo disabilita i softirq
18162306a36Sopenharmony_cisul processore e trattiene il *lock*. Invece, spin_unlock_bh() fa
18262306a36Sopenharmony_cil'opposto. (Il suffisso '_bh' è un residuo storico che fa riferimento al
18362306a36Sopenharmony_ci"Bottom Halves", il vecchio nome delle interruzioni software. In un mondo
18462306a36Sopenharmony_ciperfetto questa funzione si chiamerebbe 'spin_lock_softirq()').
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ciDa notare che in questo caso potete utilizzare anche spin_lock_irq()
18762306a36Sopenharmony_cio spin_lock_irqsave(), queste fermano anche le interruzioni hardware:
18862306a36Sopenharmony_civedere `Contesto di interruzione hardware`_.
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ciQuesto funziona alla perfezione anche sui sistemi monoprocessore: gli spinlock
19162306a36Sopenharmony_cisvaniscono e questa macro diventa semplicemente local_bh_disable()
19262306a36Sopenharmony_ci(``include/linux/interrupt.h``), la quale impedisce ai softirq d'essere
19362306a36Sopenharmony_cieseguiti.
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ciSincronizzazione fra contesto utente e i tasklet
19662306a36Sopenharmony_ci------------------------------------------------
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ciQuesto caso è uguale al precedente, un tasklet viene eseguito da un softirq.
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ciSincronizzazione fra contesto utente e i timer
20162306a36Sopenharmony_ci----------------------------------------------
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ciAnche questo caso è uguale al precedente, un timer viene eseguito da un
20462306a36Sopenharmony_cisoftirq.
20562306a36Sopenharmony_ciDal punto di vista della sincronizzazione, tasklet e timer sono identici.
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ciSincronizzazione fra tasklet e timer
20862306a36Sopenharmony_ci------------------------------------
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ciQualche volta un tasklet od un timer potrebbero condividere i dati con
21162306a36Sopenharmony_ciun altro tasklet o timer
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ciLo stesso tasklet/timer
21462306a36Sopenharmony_ci~~~~~~~~~~~~~~~~~~~~~~~
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ciDato che un tasklet non viene mai eseguito contemporaneamente su due
21762306a36Sopenharmony_ciprocessori, non dovete preoccuparvi che sia rientrante (ovvero eseguito
21862306a36Sopenharmony_cipiù volte in contemporanea), perfino su sistemi multi-processore.
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ciDifferenti tasklet/timer
22162306a36Sopenharmony_ci~~~~~~~~~~~~~~~~~~~~~~~~
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ciSe un altro tasklet/timer vuole condividere dati col vostro tasklet o timer,
22462306a36Sopenharmony_ciallora avrete bisogno entrambe di spin_lock() e
22562306a36Sopenharmony_cispin_unlock(). Qui spin_lock_bh() è inutile, siete già
22662306a36Sopenharmony_ciin un tasklet ed avete la garanzia che nessun altro verrà eseguito sullo
22762306a36Sopenharmony_cistesso processore.
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ciSincronizzazione fra softirq
23062306a36Sopenharmony_ci----------------------------
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ciSpesso un softirq potrebbe condividere dati con se stesso o un tasklet/timer.
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ciLo stesso softirq
23562306a36Sopenharmony_ci~~~~~~~~~~~~~~~~~
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ciLo stesso softirq può essere eseguito su un diverso processore: allo scopo
23862306a36Sopenharmony_cidi migliorare le prestazioni potete utilizzare dati riservati ad ogni
23962306a36Sopenharmony_ciprocessore (vedere `Dati per processore`_). Se siete arrivati
24062306a36Sopenharmony_cifino a questo punto nell'uso dei softirq, probabilmente tenete alla scalabilità
24162306a36Sopenharmony_cidelle prestazioni abbastanza da giustificarne la complessità aggiuntiva.
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ciDovete utilizzare spin_lock() e spin_unlock() per
24462306a36Sopenharmony_ciproteggere i dati condivisi.
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ciDiversi Softirqs
24762306a36Sopenharmony_ci~~~~~~~~~~~~~~~~
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ciDovete utilizzare spin_lock() e spin_unlock() per
25062306a36Sopenharmony_ciproteggere i dati condivisi, che siano timer, tasklet, diversi softirq o
25162306a36Sopenharmony_cilo stesso o altri softirq: uno qualsiasi di essi potrebbe essere in esecuzione
25262306a36Sopenharmony_cisu un diverso processore.
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci.. _`it_hardirq-context`:
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ciContesto di interruzione hardware
25762306a36Sopenharmony_ci=================================
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ciSolitamente le interruzioni hardware comunicano con un tasklet o un softirq.
26062306a36Sopenharmony_ciSpesso questo si traduce nel mettere in coda qualcosa da fare che verrà
26162306a36Sopenharmony_cipreso in carico da un softirq.
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ciSincronizzazione fra interruzioni hardware e softirq/tasklet
26462306a36Sopenharmony_ci------------------------------------------------------------
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ciSe un gestore di interruzioni hardware condivide dati con un softirq, allora
26762306a36Sopenharmony_ciavrete due preoccupazioni. Primo, il softirq può essere interrotto da
26862306a36Sopenharmony_ciun'interruzione hardware, e secondo, la sezione critica potrebbe essere
26962306a36Sopenharmony_cieseguita da un'interruzione hardware su un processore diverso. Questo è il caso
27062306a36Sopenharmony_cidove spin_lock_irq() viene utilizzato. Disabilita le interruzioni
27162306a36Sopenharmony_cisul processore che l'esegue, poi trattiene il lock. spin_unlock_irq()
27262306a36Sopenharmony_cifa l'opposto.
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ciIl gestore d'interruzione hardware non ha bisogno di usare spin_lock_irq()
27562306a36Sopenharmony_ciperché i softirq non possono essere eseguiti quando il gestore d'interruzione
27662306a36Sopenharmony_cihardware è in esecuzione: per questo si può usare spin_lock(), che è un po'
27762306a36Sopenharmony_cipiù veloce. L'unica eccezione è quando un altro gestore d'interruzioni
27862306a36Sopenharmony_cihardware utilizza lo stesso *lock*: spin_lock_irq() impedirà a questo
27962306a36Sopenharmony_cisecondo gestore di interrompere quello in esecuzione.
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ciQuesto funziona alla perfezione anche sui sistemi monoprocessore: gli spinlock
28262306a36Sopenharmony_cisvaniscono e questa macro diventa semplicemente local_irq_disable()
28362306a36Sopenharmony_ci(``include/asm/smp.h``), la quale impedisce a softirq/tasklet/BH d'essere
28462306a36Sopenharmony_cieseguiti.
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_cispin_lock_irqsave() (``include/linux/spinlock.h``) è una variante che
28762306a36Sopenharmony_cisalva lo stato delle interruzioni in una variabile, questa verrà poi passata
28862306a36Sopenharmony_cia spin_unlock_irqrestore(). Questo significa che lo stesso codice
28962306a36Sopenharmony_cipotrà essere utilizzato in un'interruzione hardware (dove le interruzioni sono
29062306a36Sopenharmony_cigià disabilitate) e in un softirq (dove la disabilitazione delle interruzioni
29162306a36Sopenharmony_ciè richiesta).
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ciDa notare che i softirq (e quindi tasklet e timer) sono eseguiti al ritorno
29462306a36Sopenharmony_cida un'interruzione hardware, quindi spin_lock_irq() interrompe
29562306a36Sopenharmony_cianche questi. Tenuto conto di questo si può dire che
29662306a36Sopenharmony_cispin_lock_irqsave() è la funzione di sincronizzazione più generica
29762306a36Sopenharmony_cie potente.
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ciSincronizzazione fra due gestori d'interruzioni hardware
30062306a36Sopenharmony_ci--------------------------------------------------------
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ciCondividere dati fra due gestori di interruzione hardware è molto raro, ma se
30362306a36Sopenharmony_cisuccede, dovreste usare spin_lock_irqsave(): è una specificità
30462306a36Sopenharmony_cidell'architettura il fatto che tutte le interruzioni vengano interrotte
30562306a36Sopenharmony_ciquando si eseguono di gestori di interruzioni.
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ciBigino della sincronizzazione
30862306a36Sopenharmony_ci=============================
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ciPete Zaitcev ci offre il seguente riassunto:
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci-  Se siete in un contesto utente (una qualsiasi chiamata di sistema)
31362306a36Sopenharmony_ci   e volete sincronizzarvi con altri processi, usate i mutex. Potete trattenere
31462306a36Sopenharmony_ci   il mutex e dormire (``copy_from_user(`` o ``kmalloc(x,GFP_KERNEL)``).
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci-  Altrimenti (== i dati possono essere manipolati da un'interruzione) usate
31762306a36Sopenharmony_ci   spin_lock_irqsave() e spin_unlock_irqrestore().
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci-  Evitate di trattenere uno spinlock per più di 5 righe di codice incluse
32062306a36Sopenharmony_ci   le chiamate a funzione (ad eccezione di quell per l'accesso come
32162306a36Sopenharmony_ci   readb()).
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ciTabella dei requisiti minimi
32462306a36Sopenharmony_ci----------------------------
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ciLa tabella seguente illustra i requisiti **minimi** per la sincronizzazione fra
32762306a36Sopenharmony_cidiversi contesti. In alcuni casi, lo stesso contesto può essere eseguito solo
32862306a36Sopenharmony_cida un processore per volta, quindi non ci sono requisiti per la
32962306a36Sopenharmony_cisincronizzazione (per esempio, un thread può essere eseguito solo su un
33062306a36Sopenharmony_ciprocessore alla volta, ma se deve condividere dati con un altro thread, allora
33162306a36Sopenharmony_cila sincronizzazione è necessaria).
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ciRicordatevi il suggerimento qui sopra: potete sempre usare
33462306a36Sopenharmony_cispin_lock_irqsave(), che è un sovrainsieme di tutte le altre funzioni
33562306a36Sopenharmony_ciper spinlock.
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci============== ============= ============= ========= ========= ========= ========= ======= ======= ============== ==============
33862306a36Sopenharmony_ci.              IRQ Handler A IRQ Handler B Softirq A Softirq B Tasklet A Tasklet B Timer A Timer B User Context A User Context B
33962306a36Sopenharmony_ci============== ============= ============= ========= ========= ========= ========= ======= ======= ============== ==============
34062306a36Sopenharmony_ciIRQ Handler A  None
34162306a36Sopenharmony_ciIRQ Handler B  SLIS          None
34262306a36Sopenharmony_ciSoftirq A      SLI           SLI           SL
34362306a36Sopenharmony_ciSoftirq B      SLI           SLI           SL        SL
34462306a36Sopenharmony_ciTasklet A      SLI           SLI           SL        SL        None
34562306a36Sopenharmony_ciTasklet B      SLI           SLI           SL        SL        SL        None
34662306a36Sopenharmony_ciTimer A        SLI           SLI           SL        SL        SL        SL        None
34762306a36Sopenharmony_ciTimer B        SLI           SLI           SL        SL        SL        SL        SL      None
34862306a36Sopenharmony_ciUser Context A SLI           SLI           SLBH      SLBH      SLBH      SLBH      SLBH    SLBH    None
34962306a36Sopenharmony_ciUser Context B SLI           SLI           SLBH      SLBH      SLBH      SLBH      SLBH    SLBH    MLI            None
35062306a36Sopenharmony_ci============== ============= ============= ========= ========= ========= ========= ======= ======= ============== ==============
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ciTable: Tabella dei requisiti per la sincronizzazione
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci+--------+----------------------------+
35562306a36Sopenharmony_ci| SLIS   | spin_lock_irqsave          |
35662306a36Sopenharmony_ci+--------+----------------------------+
35762306a36Sopenharmony_ci| SLI    | spin_lock_irq              |
35862306a36Sopenharmony_ci+--------+----------------------------+
35962306a36Sopenharmony_ci| SL     | spin_lock                  |
36062306a36Sopenharmony_ci+--------+----------------------------+
36162306a36Sopenharmony_ci| SLBH   | spin_lock_bh               |
36262306a36Sopenharmony_ci+--------+----------------------------+
36362306a36Sopenharmony_ci| MLI    | mutex_lock_interruptible   |
36462306a36Sopenharmony_ci+--------+----------------------------+
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ciTable: Legenda per la tabella dei requisiti per la sincronizzazione
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ciLe funzioni *trylock*
36962306a36Sopenharmony_ci=====================
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ciCi sono funzioni che provano a trattenere un *lock* solo una volta e
37262306a36Sopenharmony_ciritornano immediatamente comunicato il successo od il fallimento
37362306a36Sopenharmony_cidell'operazione. Posso essere usate quando non serve accedere ai dati
37462306a36Sopenharmony_ciprotetti dal *lock* quando qualche altro thread lo sta già facendo
37562306a36Sopenharmony_citrattenendo il *lock*. Potrete acquisire il *lock* più tardi se vi
37662306a36Sopenharmony_ciserve accedere ai dati protetti da questo *lock*.
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ciLa funzione spin_trylock() non ritenta di acquisire il *lock*,
37962306a36Sopenharmony_cise ci riesce al primo colpo ritorna un valore diverso da zero, altrimenti
38062306a36Sopenharmony_cise fallisce ritorna 0. Questa funzione può essere utilizzata in un qualunque
38162306a36Sopenharmony_cicontesto, ma come spin_lock(): dovete disabilitare i contesti che
38262306a36Sopenharmony_cipotrebbero interrompervi e quindi trattenere lo spinlock.
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ciLa funzione mutex_trylock() invece di sospendere il vostro processo
38562306a36Sopenharmony_ciritorna un valore diverso da zero se è possibile trattenere il lock al primo
38662306a36Sopenharmony_cicolpo, altrimenti se fallisce ritorna 0. Nonostante non dorma, questa funzione
38762306a36Sopenharmony_cinon può essere usata in modo sicuro in contesti di interruzione hardware o
38862306a36Sopenharmony_cisoftware.
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ciEsempi più comuni
39162306a36Sopenharmony_ci=================
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ciGuardiamo un semplice esempio: una memoria che associa nomi a numeri.
39462306a36Sopenharmony_ciLa memoria tiene traccia di quanto spesso viene utilizzato ogni oggetto;
39562306a36Sopenharmony_ciquando è piena, l'oggetto meno usato viene eliminato.
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ciTutto in contesto utente
39862306a36Sopenharmony_ci------------------------
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ciNel primo esempio, supponiamo che tutte le operazioni avvengano in contesto
40162306a36Sopenharmony_ciutente (in soldoni, da una chiamata di sistema), quindi possiamo dormire.
40262306a36Sopenharmony_ciQuesto significa che possiamo usare i mutex per proteggere la nostra memoria
40362306a36Sopenharmony_cie tutti gli oggetti che contiene. Ecco il codice::
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci    #include <linux/list.h>
40662306a36Sopenharmony_ci    #include <linux/slab.h>
40762306a36Sopenharmony_ci    #include <linux/string.h>
40862306a36Sopenharmony_ci    #include <linux/mutex.h>
40962306a36Sopenharmony_ci    #include <asm/errno.h>
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci    struct object
41262306a36Sopenharmony_ci    {
41362306a36Sopenharmony_ci            struct list_head list;
41462306a36Sopenharmony_ci            int id;
41562306a36Sopenharmony_ci            char name[32];
41662306a36Sopenharmony_ci            int popularity;
41762306a36Sopenharmony_ci    };
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci    /* Protects the cache, cache_num, and the objects within it */
42062306a36Sopenharmony_ci    static DEFINE_MUTEX(cache_lock);
42162306a36Sopenharmony_ci    static LIST_HEAD(cache);
42262306a36Sopenharmony_ci    static unsigned int cache_num = 0;
42362306a36Sopenharmony_ci    #define MAX_CACHE_SIZE 10
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci    /* Must be holding cache_lock */
42662306a36Sopenharmony_ci    static struct object *__cache_find(int id)
42762306a36Sopenharmony_ci    {
42862306a36Sopenharmony_ci            struct object *i;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci            list_for_each_entry(i, &cache, list)
43162306a36Sopenharmony_ci                    if (i->id == id) {
43262306a36Sopenharmony_ci                            i->popularity++;
43362306a36Sopenharmony_ci                            return i;
43462306a36Sopenharmony_ci                    }
43562306a36Sopenharmony_ci            return NULL;
43662306a36Sopenharmony_ci    }
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci    /* Must be holding cache_lock */
43962306a36Sopenharmony_ci    static void __cache_delete(struct object *obj)
44062306a36Sopenharmony_ci    {
44162306a36Sopenharmony_ci            BUG_ON(!obj);
44262306a36Sopenharmony_ci            list_del(&obj->list);
44362306a36Sopenharmony_ci            kfree(obj);
44462306a36Sopenharmony_ci            cache_num--;
44562306a36Sopenharmony_ci    }
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci    /* Must be holding cache_lock */
44862306a36Sopenharmony_ci    static void __cache_add(struct object *obj)
44962306a36Sopenharmony_ci    {
45062306a36Sopenharmony_ci            list_add(&obj->list, &cache);
45162306a36Sopenharmony_ci            if (++cache_num > MAX_CACHE_SIZE) {
45262306a36Sopenharmony_ci                    struct object *i, *outcast = NULL;
45362306a36Sopenharmony_ci                    list_for_each_entry(i, &cache, list) {
45462306a36Sopenharmony_ci                            if (!outcast || i->popularity < outcast->popularity)
45562306a36Sopenharmony_ci                                    outcast = i;
45662306a36Sopenharmony_ci                    }
45762306a36Sopenharmony_ci                    __cache_delete(outcast);
45862306a36Sopenharmony_ci            }
45962306a36Sopenharmony_ci    }
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci    int cache_add(int id, const char *name)
46262306a36Sopenharmony_ci    {
46362306a36Sopenharmony_ci            struct object *obj;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci            if ((obj = kmalloc(sizeof(*obj), GFP_KERNEL)) == NULL)
46662306a36Sopenharmony_ci                    return -ENOMEM;
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci            strscpy(obj->name, name, sizeof(obj->name));
46962306a36Sopenharmony_ci            obj->id = id;
47062306a36Sopenharmony_ci            obj->popularity = 0;
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci            mutex_lock(&cache_lock);
47362306a36Sopenharmony_ci            __cache_add(obj);
47462306a36Sopenharmony_ci            mutex_unlock(&cache_lock);
47562306a36Sopenharmony_ci            return 0;
47662306a36Sopenharmony_ci    }
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci    void cache_delete(int id)
47962306a36Sopenharmony_ci    {
48062306a36Sopenharmony_ci            mutex_lock(&cache_lock);
48162306a36Sopenharmony_ci            __cache_delete(__cache_find(id));
48262306a36Sopenharmony_ci            mutex_unlock(&cache_lock);
48362306a36Sopenharmony_ci    }
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci    int cache_find(int id, char *name)
48662306a36Sopenharmony_ci    {
48762306a36Sopenharmony_ci            struct object *obj;
48862306a36Sopenharmony_ci            int ret = -ENOENT;
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci            mutex_lock(&cache_lock);
49162306a36Sopenharmony_ci            obj = __cache_find(id);
49262306a36Sopenharmony_ci            if (obj) {
49362306a36Sopenharmony_ci                    ret = 0;
49462306a36Sopenharmony_ci                    strcpy(name, obj->name);
49562306a36Sopenharmony_ci            }
49662306a36Sopenharmony_ci            mutex_unlock(&cache_lock);
49762306a36Sopenharmony_ci            return ret;
49862306a36Sopenharmony_ci    }
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ciDa notare che ci assicuriamo sempre di trattenere cache_lock quando
50162306a36Sopenharmony_ciaggiungiamo, rimuoviamo od ispezioniamo la memoria: sia la struttura
50262306a36Sopenharmony_cidella memoria che il suo contenuto sono protetti dal *lock*. Questo
50362306a36Sopenharmony_cicaso è semplice dato che copiamo i dati dall'utente e non permettiamo
50462306a36Sopenharmony_cimai loro di accedere direttamente agli oggetti.
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ciC'è una piccola ottimizzazione qui: nella funzione cache_add()
50762306a36Sopenharmony_ciimpostiamo i campi dell'oggetto prima di acquisire il *lock*. Questo è
50862306a36Sopenharmony_cisicuro perché nessun altro potrà accedervi finché non lo inseriremo
50962306a36Sopenharmony_cinella memoria.
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ciAccesso dal contesto utente
51262306a36Sopenharmony_ci---------------------------
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ciOra consideriamo il caso in cui cache_find() può essere invocata
51562306a36Sopenharmony_cidal contesto d'interruzione: sia hardware che software. Un esempio potrebbe
51662306a36Sopenharmony_ciessere un timer che elimina oggetti dalla memoria.
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ciQui di seguito troverete la modifica nel formato *patch*: le righe ``-``
51962306a36Sopenharmony_cisono quelle rimosse, mentre quelle ``+`` sono quelle aggiunte.
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci::
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci    --- cache.c.usercontext 2003-12-09 13:58:54.000000000 +1100
52462306a36Sopenharmony_ci    +++ cache.c.interrupt   2003-12-09 14:07:49.000000000 +1100
52562306a36Sopenharmony_ci    @@ -12,7 +12,7 @@
52662306a36Sopenharmony_ci             int popularity;
52762306a36Sopenharmony_ci     };
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci    -static DEFINE_MUTEX(cache_lock);
53062306a36Sopenharmony_ci    +static DEFINE_SPINLOCK(cache_lock);
53162306a36Sopenharmony_ci     static LIST_HEAD(cache);
53262306a36Sopenharmony_ci     static unsigned int cache_num = 0;
53362306a36Sopenharmony_ci     #define MAX_CACHE_SIZE 10
53462306a36Sopenharmony_ci    @@ -55,6 +55,7 @@
53562306a36Sopenharmony_ci     int cache_add(int id, const char *name)
53662306a36Sopenharmony_ci     {
53762306a36Sopenharmony_ci             struct object *obj;
53862306a36Sopenharmony_ci    +        unsigned long flags;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci             if ((obj = kmalloc(sizeof(*obj), GFP_KERNEL)) == NULL)
54162306a36Sopenharmony_ci                     return -ENOMEM;
54262306a36Sopenharmony_ci    @@ -63,30 +64,33 @@
54362306a36Sopenharmony_ci             obj->id = id;
54462306a36Sopenharmony_ci             obj->popularity = 0;
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci    -        mutex_lock(&cache_lock);
54762306a36Sopenharmony_ci    +        spin_lock_irqsave(&cache_lock, flags);
54862306a36Sopenharmony_ci             __cache_add(obj);
54962306a36Sopenharmony_ci    -        mutex_unlock(&cache_lock);
55062306a36Sopenharmony_ci    +        spin_unlock_irqrestore(&cache_lock, flags);
55162306a36Sopenharmony_ci             return 0;
55262306a36Sopenharmony_ci     }
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci     void cache_delete(int id)
55562306a36Sopenharmony_ci     {
55662306a36Sopenharmony_ci    -        mutex_lock(&cache_lock);
55762306a36Sopenharmony_ci    +        unsigned long flags;
55862306a36Sopenharmony_ci    +
55962306a36Sopenharmony_ci    +        spin_lock_irqsave(&cache_lock, flags);
56062306a36Sopenharmony_ci             __cache_delete(__cache_find(id));
56162306a36Sopenharmony_ci    -        mutex_unlock(&cache_lock);
56262306a36Sopenharmony_ci    +        spin_unlock_irqrestore(&cache_lock, flags);
56362306a36Sopenharmony_ci     }
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci     int cache_find(int id, char *name)
56662306a36Sopenharmony_ci     {
56762306a36Sopenharmony_ci             struct object *obj;
56862306a36Sopenharmony_ci             int ret = -ENOENT;
56962306a36Sopenharmony_ci    +        unsigned long flags;
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci    -        mutex_lock(&cache_lock);
57262306a36Sopenharmony_ci    +        spin_lock_irqsave(&cache_lock, flags);
57362306a36Sopenharmony_ci             obj = __cache_find(id);
57462306a36Sopenharmony_ci             if (obj) {
57562306a36Sopenharmony_ci                     ret = 0;
57662306a36Sopenharmony_ci                     strcpy(name, obj->name);
57762306a36Sopenharmony_ci             }
57862306a36Sopenharmony_ci    -        mutex_unlock(&cache_lock);
57962306a36Sopenharmony_ci    +        spin_unlock_irqrestore(&cache_lock, flags);
58062306a36Sopenharmony_ci             return ret;
58162306a36Sopenharmony_ci     }
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ciDa notare che spin_lock_irqsave() disabiliterà le interruzioni
58462306a36Sopenharmony_cise erano attive, altrimenti non farà niente (quando siamo già in un contesto
58562306a36Sopenharmony_cid'interruzione); dunque queste funzioni possono essere chiamante in
58662306a36Sopenharmony_cisicurezza da qualsiasi contesto.
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ciSfortunatamente, cache_add() invoca kmalloc() con
58962306a36Sopenharmony_cil'opzione ``GFP_KERNEL`` che è permessa solo in contesto utente. Ho supposto
59062306a36Sopenharmony_ciche cache_add() venga chiamata dal contesto utente, altrimenti
59162306a36Sopenharmony_ciquesta opzione deve diventare un parametro di cache_add().
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ciEsporre gli oggetti al di fuori del file
59462306a36Sopenharmony_ci----------------------------------------
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ciSe i vostri oggetti contengono più informazioni, potrebbe non essere
59762306a36Sopenharmony_cisufficiente copiare i dati avanti e indietro: per esempio, altre parti del
59862306a36Sopenharmony_cicodice potrebbero avere un puntatore a questi oggetti piuttosto che cercarli
59962306a36Sopenharmony_ciogni volta. Questo introduce due problemi.
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ciIl primo problema è che utilizziamo ``cache_lock`` per proteggere gli oggetti:
60262306a36Sopenharmony_cidobbiamo renderlo dinamico così che il resto del codice possa usarlo. Questo
60362306a36Sopenharmony_cirende la sincronizzazione più complicata dato che non avviene più in un unico
60462306a36Sopenharmony_ciposto.
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ciIl secondo problema è il problema del ciclo di vita: se un'altra struttura
60762306a36Sopenharmony_cimantiene un puntatore ad un oggetto, presumibilmente si aspetta che questo
60862306a36Sopenharmony_cipuntatore rimanga valido. Sfortunatamente, questo è garantito solo mentre
60962306a36Sopenharmony_cisi trattiene il *lock*, altrimenti qualcuno potrebbe chiamare
61062306a36Sopenharmony_cicache_delete() o peggio, aggiungere un oggetto che riutilizza lo
61162306a36Sopenharmony_cistesso indirizzo.
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ciDato che c'è un solo *lock*, non potete trattenerlo a vita: altrimenti
61462306a36Sopenharmony_cinessun altro potrà eseguire il proprio lavoro.
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ciLa soluzione a questo problema è l'uso di un contatore di riferimenti:
61762306a36Sopenharmony_cichiunque punti ad un oggetto deve incrementare il contatore, e decrementarlo
61862306a36Sopenharmony_ciquando il puntatore non viene più usato. Quando il contatore raggiunge lo zero
61962306a36Sopenharmony_cisignifica che non è più usato e l'oggetto può essere rimosso.
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ciEcco il codice::
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci    --- cache.c.interrupt   2003-12-09 14:25:43.000000000 +1100
62462306a36Sopenharmony_ci    +++ cache.c.refcnt  2003-12-09 14:33:05.000000000 +1100
62562306a36Sopenharmony_ci    @@ -7,6 +7,7 @@
62662306a36Sopenharmony_ci     struct object
62762306a36Sopenharmony_ci     {
62862306a36Sopenharmony_ci             struct list_head list;
62962306a36Sopenharmony_ci    +        unsigned int refcnt;
63062306a36Sopenharmony_ci             int id;
63162306a36Sopenharmony_ci             char name[32];
63262306a36Sopenharmony_ci             int popularity;
63362306a36Sopenharmony_ci    @@ -17,6 +18,35 @@
63462306a36Sopenharmony_ci     static unsigned int cache_num = 0;
63562306a36Sopenharmony_ci     #define MAX_CACHE_SIZE 10
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci    +static void __object_put(struct object *obj)
63862306a36Sopenharmony_ci    +{
63962306a36Sopenharmony_ci    +        if (--obj->refcnt == 0)
64062306a36Sopenharmony_ci    +                kfree(obj);
64162306a36Sopenharmony_ci    +}
64262306a36Sopenharmony_ci    +
64362306a36Sopenharmony_ci    +static void __object_get(struct object *obj)
64462306a36Sopenharmony_ci    +{
64562306a36Sopenharmony_ci    +        obj->refcnt++;
64662306a36Sopenharmony_ci    +}
64762306a36Sopenharmony_ci    +
64862306a36Sopenharmony_ci    +void object_put(struct object *obj)
64962306a36Sopenharmony_ci    +{
65062306a36Sopenharmony_ci    +        unsigned long flags;
65162306a36Sopenharmony_ci    +
65262306a36Sopenharmony_ci    +        spin_lock_irqsave(&cache_lock, flags);
65362306a36Sopenharmony_ci    +        __object_put(obj);
65462306a36Sopenharmony_ci    +        spin_unlock_irqrestore(&cache_lock, flags);
65562306a36Sopenharmony_ci    +}
65662306a36Sopenharmony_ci    +
65762306a36Sopenharmony_ci    +void object_get(struct object *obj)
65862306a36Sopenharmony_ci    +{
65962306a36Sopenharmony_ci    +        unsigned long flags;
66062306a36Sopenharmony_ci    +
66162306a36Sopenharmony_ci    +        spin_lock_irqsave(&cache_lock, flags);
66262306a36Sopenharmony_ci    +        __object_get(obj);
66362306a36Sopenharmony_ci    +        spin_unlock_irqrestore(&cache_lock, flags);
66462306a36Sopenharmony_ci    +}
66562306a36Sopenharmony_ci    +
66662306a36Sopenharmony_ci     /* Must be holding cache_lock */
66762306a36Sopenharmony_ci     static struct object *__cache_find(int id)
66862306a36Sopenharmony_ci     {
66962306a36Sopenharmony_ci    @@ -35,6 +65,7 @@
67062306a36Sopenharmony_ci     {
67162306a36Sopenharmony_ci             BUG_ON(!obj);
67262306a36Sopenharmony_ci             list_del(&obj->list);
67362306a36Sopenharmony_ci    +        __object_put(obj);
67462306a36Sopenharmony_ci             cache_num--;
67562306a36Sopenharmony_ci     }
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci    @@ -63,6 +94,7 @@
67862306a36Sopenharmony_ci             strscpy(obj->name, name, sizeof(obj->name));
67962306a36Sopenharmony_ci             obj->id = id;
68062306a36Sopenharmony_ci             obj->popularity = 0;
68162306a36Sopenharmony_ci    +        obj->refcnt = 1; /* The cache holds a reference */
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci             spin_lock_irqsave(&cache_lock, flags);
68462306a36Sopenharmony_ci             __cache_add(obj);
68562306a36Sopenharmony_ci    @@ -79,18 +111,15 @@
68662306a36Sopenharmony_ci             spin_unlock_irqrestore(&cache_lock, flags);
68762306a36Sopenharmony_ci     }
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci    -int cache_find(int id, char *name)
69062306a36Sopenharmony_ci    +struct object *cache_find(int id)
69162306a36Sopenharmony_ci     {
69262306a36Sopenharmony_ci             struct object *obj;
69362306a36Sopenharmony_ci    -        int ret = -ENOENT;
69462306a36Sopenharmony_ci             unsigned long flags;
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci             spin_lock_irqsave(&cache_lock, flags);
69762306a36Sopenharmony_ci             obj = __cache_find(id);
69862306a36Sopenharmony_ci    -        if (obj) {
69962306a36Sopenharmony_ci    -                ret = 0;
70062306a36Sopenharmony_ci    -                strcpy(name, obj->name);
70162306a36Sopenharmony_ci    -        }
70262306a36Sopenharmony_ci    +        if (obj)
70362306a36Sopenharmony_ci    +                __object_get(obj);
70462306a36Sopenharmony_ci             spin_unlock_irqrestore(&cache_lock, flags);
70562306a36Sopenharmony_ci    -        return ret;
70662306a36Sopenharmony_ci    +        return obj;
70762306a36Sopenharmony_ci     }
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ciAbbiamo incapsulato il contatore di riferimenti nelle tipiche funzioni
71062306a36Sopenharmony_cidi 'get' e 'put'. Ora possiamo ritornare l'oggetto da cache_find()
71162306a36Sopenharmony_cicol vantaggio che l'utente può dormire trattenendo l'oggetto (per esempio,
71262306a36Sopenharmony_cicopy_to_user() per copiare il nome verso lo spazio utente).
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ciUn altro punto da notare è che ho detto che il contatore dovrebbe incrementarsi
71562306a36Sopenharmony_ciper ogni puntatore ad un oggetto: quindi il contatore di riferimenti è 1
71662306a36Sopenharmony_ciquando l'oggetto viene inserito nella memoria. In altre versione il framework
71762306a36Sopenharmony_cinon trattiene un riferimento per se, ma diventa più complicato.
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ciUsare operazioni atomiche per il contatore di riferimenti
72062306a36Sopenharmony_ci~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ciIn sostanza, :c:type:`atomic_t` viene usato come contatore di riferimenti.
72362306a36Sopenharmony_ciCi sono un certo numbero di operazioni atomiche definite
72462306a36Sopenharmony_ciin ``include/asm/atomic.h``: queste sono garantite come atomiche su qualsiasi
72562306a36Sopenharmony_ciprocessore del sistema, quindi non sono necessari i *lock*. In questo caso è
72662306a36Sopenharmony_cipiù semplice rispetto all'uso degli spinlock, benché l'uso degli spinlock
72762306a36Sopenharmony_cisia più elegante per casi non banali. Le funzioni atomic_inc() e
72862306a36Sopenharmony_ciatomic_dec_and_test() vengono usate al posto dei tipici operatori di
72962306a36Sopenharmony_ciincremento e decremento, e i *lock* non sono più necessari per proteggere il
73062306a36Sopenharmony_cicontatore stesso.
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci::
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci    --- cache.c.refcnt  2003-12-09 15:00:35.000000000 +1100
73562306a36Sopenharmony_ci    +++ cache.c.refcnt-atomic   2003-12-11 15:49:42.000000000 +1100
73662306a36Sopenharmony_ci    @@ -7,7 +7,7 @@
73762306a36Sopenharmony_ci     struct object
73862306a36Sopenharmony_ci     {
73962306a36Sopenharmony_ci             struct list_head list;
74062306a36Sopenharmony_ci    -        unsigned int refcnt;
74162306a36Sopenharmony_ci    +        atomic_t refcnt;
74262306a36Sopenharmony_ci             int id;
74362306a36Sopenharmony_ci             char name[32];
74462306a36Sopenharmony_ci             int popularity;
74562306a36Sopenharmony_ci    @@ -18,33 +18,15 @@
74662306a36Sopenharmony_ci     static unsigned int cache_num = 0;
74762306a36Sopenharmony_ci     #define MAX_CACHE_SIZE 10
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci    -static void __object_put(struct object *obj)
75062306a36Sopenharmony_ci    -{
75162306a36Sopenharmony_ci    -        if (--obj->refcnt == 0)
75262306a36Sopenharmony_ci    -                kfree(obj);
75362306a36Sopenharmony_ci    -}
75462306a36Sopenharmony_ci    -
75562306a36Sopenharmony_ci    -static void __object_get(struct object *obj)
75662306a36Sopenharmony_ci    -{
75762306a36Sopenharmony_ci    -        obj->refcnt++;
75862306a36Sopenharmony_ci    -}
75962306a36Sopenharmony_ci    -
76062306a36Sopenharmony_ci     void object_put(struct object *obj)
76162306a36Sopenharmony_ci     {
76262306a36Sopenharmony_ci    -        unsigned long flags;
76362306a36Sopenharmony_ci    -
76462306a36Sopenharmony_ci    -        spin_lock_irqsave(&cache_lock, flags);
76562306a36Sopenharmony_ci    -        __object_put(obj);
76662306a36Sopenharmony_ci    -        spin_unlock_irqrestore(&cache_lock, flags);
76762306a36Sopenharmony_ci    +        if (atomic_dec_and_test(&obj->refcnt))
76862306a36Sopenharmony_ci    +                kfree(obj);
76962306a36Sopenharmony_ci     }
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci     void object_get(struct object *obj)
77262306a36Sopenharmony_ci     {
77362306a36Sopenharmony_ci    -        unsigned long flags;
77462306a36Sopenharmony_ci    -
77562306a36Sopenharmony_ci    -        spin_lock_irqsave(&cache_lock, flags);
77662306a36Sopenharmony_ci    -        __object_get(obj);
77762306a36Sopenharmony_ci    -        spin_unlock_irqrestore(&cache_lock, flags);
77862306a36Sopenharmony_ci    +        atomic_inc(&obj->refcnt);
77962306a36Sopenharmony_ci     }
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci     /* Must be holding cache_lock */
78262306a36Sopenharmony_ci    @@ -65,7 +47,7 @@
78362306a36Sopenharmony_ci     {
78462306a36Sopenharmony_ci             BUG_ON(!obj);
78562306a36Sopenharmony_ci             list_del(&obj->list);
78662306a36Sopenharmony_ci    -        __object_put(obj);
78762306a36Sopenharmony_ci    +        object_put(obj);
78862306a36Sopenharmony_ci             cache_num--;
78962306a36Sopenharmony_ci     }
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci    @@ -94,7 +76,7 @@
79262306a36Sopenharmony_ci             strscpy(obj->name, name, sizeof(obj->name));
79362306a36Sopenharmony_ci             obj->id = id;
79462306a36Sopenharmony_ci             obj->popularity = 0;
79562306a36Sopenharmony_ci    -        obj->refcnt = 1; /* The cache holds a reference */
79662306a36Sopenharmony_ci    +        atomic_set(&obj->refcnt, 1); /* The cache holds a reference */
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci             spin_lock_irqsave(&cache_lock, flags);
79962306a36Sopenharmony_ci             __cache_add(obj);
80062306a36Sopenharmony_ci    @@ -119,7 +101,7 @@
80162306a36Sopenharmony_ci             spin_lock_irqsave(&cache_lock, flags);
80262306a36Sopenharmony_ci             obj = __cache_find(id);
80362306a36Sopenharmony_ci             if (obj)
80462306a36Sopenharmony_ci    -                __object_get(obj);
80562306a36Sopenharmony_ci    +                object_get(obj);
80662306a36Sopenharmony_ci             spin_unlock_irqrestore(&cache_lock, flags);
80762306a36Sopenharmony_ci             return obj;
80862306a36Sopenharmony_ci     }
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ciProteggere l'oggetto stesso
81162306a36Sopenharmony_ci---------------------------
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ciIn questo esempio, assumiamo che gli oggetti (ad eccezione del contatore
81462306a36Sopenharmony_cidi riferimenti) non cambino mai dopo la loro creazione. Se vogliamo permettere
81562306a36Sopenharmony_cial nome di cambiare abbiamo tre possibilità:
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci-  Si può togliere static da ``cache_lock`` e dire agli utenti che devono
81862306a36Sopenharmony_ci   trattenere il *lock* prima di modificare il nome di un oggetto.
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci-  Si può fornire una funzione cache_obj_rename() che prende il
82162306a36Sopenharmony_ci   *lock* e cambia il nome per conto del chiamante; si dirà poi agli utenti
82262306a36Sopenharmony_ci   di usare questa funzione.
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci-  Si può decidere che ``cache_lock`` protegge solo la memoria stessa, ed
82562306a36Sopenharmony_ci   un altro *lock* è necessario per la protezione del nome.
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ciTeoricamente, possiamo avere un *lock* per ogni campo e per ogni oggetto.
82862306a36Sopenharmony_ciIn pratica, le varianti più comuni sono:
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci-  un *lock* che protegge l'infrastruttura (la lista ``cache`` di questo
83162306a36Sopenharmony_ci   esempio) e gli oggetti. Questo è quello che abbiamo fatto finora.
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci-  un *lock* che protegge l'infrastruttura (inclusi i puntatori alla lista
83462306a36Sopenharmony_ci   negli oggetti), e un *lock* nell'oggetto per proteggere il resto
83562306a36Sopenharmony_ci   dell'oggetto stesso.
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci-  *lock* multipli per proteggere l'infrastruttura (per esempio un *lock*
83862306a36Sopenharmony_ci   per ogni lista), possibilmente con un *lock* per oggetto.
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ciQui di seguito un'implementazione con "un lock per oggetto":
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci::
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci    --- cache.c.refcnt-atomic   2003-12-11 15:50:54.000000000 +1100
84562306a36Sopenharmony_ci    +++ cache.c.perobjectlock   2003-12-11 17:15:03.000000000 +1100
84662306a36Sopenharmony_ci    @@ -6,11 +6,17 @@
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci     struct object
84962306a36Sopenharmony_ci     {
85062306a36Sopenharmony_ci    +        /* These two protected by cache_lock. */
85162306a36Sopenharmony_ci             struct list_head list;
85262306a36Sopenharmony_ci    +        int popularity;
85362306a36Sopenharmony_ci    +
85462306a36Sopenharmony_ci             atomic_t refcnt;
85562306a36Sopenharmony_ci    +
85662306a36Sopenharmony_ci    +        /* Doesn't change once created. */
85762306a36Sopenharmony_ci             int id;
85862306a36Sopenharmony_ci    +
85962306a36Sopenharmony_ci    +        spinlock_t lock; /* Protects the name */
86062306a36Sopenharmony_ci             char name[32];
86162306a36Sopenharmony_ci    -        int popularity;
86262306a36Sopenharmony_ci     };
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci     static DEFINE_SPINLOCK(cache_lock);
86562306a36Sopenharmony_ci    @@ -77,6 +84,7 @@
86662306a36Sopenharmony_ci             obj->id = id;
86762306a36Sopenharmony_ci             obj->popularity = 0;
86862306a36Sopenharmony_ci             atomic_set(&obj->refcnt, 1); /* The cache holds a reference */
86962306a36Sopenharmony_ci    +        spin_lock_init(&obj->lock);
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci             spin_lock_irqsave(&cache_lock, flags);
87262306a36Sopenharmony_ci             __cache_add(obj);
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ciDa notare che ho deciso che il contatore di popolarità dovesse essere
87562306a36Sopenharmony_ciprotetto da ``cache_lock`` piuttosto che dal *lock* dell'oggetto; questo
87662306a36Sopenharmony_ciperché è logicamente parte dell'infrastruttura (come
87762306a36Sopenharmony_ci:c:type:`struct list_head <list_head>` nell'oggetto). In questo modo,
87862306a36Sopenharmony_ciin __cache_add(), non ho bisogno di trattenere il *lock* di ogni
87962306a36Sopenharmony_cioggetto mentre si cerca il meno popolare.
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ciHo anche deciso che il campo id è immutabile, quindi non ho bisogno di
88262306a36Sopenharmony_citrattenere il lock dell'oggetto quando si usa __cache_find()
88362306a36Sopenharmony_ciper leggere questo campo; il *lock* dell'oggetto è usato solo dal chiamante
88462306a36Sopenharmony_ciche vuole leggere o scrivere il campo name.
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ciInoltre, da notare che ho aggiunto un commento che descrive i dati che sono
88762306a36Sopenharmony_ciprotetti dal *lock*. Questo è estremamente importante in quanto descrive il
88862306a36Sopenharmony_cicomportamento del codice, che altrimenti sarebbe di difficile comprensione
88962306a36Sopenharmony_cileggendo solamente il codice. E come dice Alan Cox: “Lock data, not code”.
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ciProblemi comuni
89262306a36Sopenharmony_ci===============
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ciStallo: semplice ed avanzato
89562306a36Sopenharmony_ci----------------------------
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ciEsiste un tipo di  baco dove un pezzo di codice tenta di trattenere uno
89862306a36Sopenharmony_cispinlock due volte: questo rimarrà in attesa attiva per sempre aspettando che
89962306a36Sopenharmony_ciil *lock* venga rilasciato (in Linux spinlocks, rwlocks e mutex non sono
90062306a36Sopenharmony_ciricorsivi).
90162306a36Sopenharmony_ciQuesto è facile da diagnosticare: non è uno di quei problemi che ti tengono
90262306a36Sopenharmony_cisveglio 5 notti a parlare da solo.
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ciUn caso un pochino più complesso; immaginate d'avere una spazio condiviso
90562306a36Sopenharmony_cifra un softirq ed il contesto utente. Se usate spin_lock() per
90662306a36Sopenharmony_ciproteggerlo, il contesto utente potrebbe essere interrotto da un softirq
90762306a36Sopenharmony_cimentre trattiene il lock, da qui il softirq rimarrà in attesa attiva provando
90862306a36Sopenharmony_ciad acquisire il *lock* già trattenuto nel contesto utente.
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ciQuesti casi sono chiamati stalli (*deadlock*), e come mostrato qui sopra,
91162306a36Sopenharmony_cipuò succedere anche con un solo processore (Ma non sui sistemi
91262306a36Sopenharmony_cimonoprocessore perché gli spinlock spariscano quando il kernel è compilato
91362306a36Sopenharmony_cicon ``CONFIG_SMP``\ =n. Nonostante ciò, nel secondo caso avrete comunque
91462306a36Sopenharmony_ciuna corruzione dei dati).
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ciQuesti casi sono facili da diagnosticare; sui sistemi multi-processore
91762306a36Sopenharmony_ciil supervisione (*watchdog*) o l'opzione di compilazione ``DEBUG_SPINLOCK``
91862306a36Sopenharmony_ci(``include/linux/spinlock.h``) permettono di scovare immediatamente quando
91962306a36Sopenharmony_cisuccedono.
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ciEsiste un caso più complesso che è conosciuto come l'abbraccio della morte;
92262306a36Sopenharmony_ciquesto coinvolge due o più *lock*. Diciamo che avete un vettore di hash in cui
92362306a36Sopenharmony_ciogni elemento è uno spinlock a cui è associata una lista di elementi con lo
92462306a36Sopenharmony_cistesso hash. In un gestore di interruzioni software, dovete modificare un
92562306a36Sopenharmony_cioggetto e spostarlo su un altro hash; quindi dovrete trattenete lo spinlock
92662306a36Sopenharmony_cidel vecchio hash e di quello nuovo, quindi rimuovere l'oggetto dal vecchio ed
92762306a36Sopenharmony_ciinserirlo nel nuovo.
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ciQui abbiamo due problemi. Primo, se il vostro codice prova a spostare un
93062306a36Sopenharmony_cioggetto all'interno della stessa lista, otterrete uno stallo visto che
93162306a36Sopenharmony_citenterà di trattenere lo stesso *lock* due volte. Secondo, se la stessa
93262306a36Sopenharmony_ciinterruzione software su un altro processore sta tentando di spostare
93362306a36Sopenharmony_ciun altro oggetto nella direzione opposta, potrebbe accadere quanto segue:
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci+---------------------------------+---------------------------------+
93662306a36Sopenharmony_ci| CPU 1                           | CPU 2                           |
93762306a36Sopenharmony_ci+=================================+=================================+
93862306a36Sopenharmony_ci| Trattiene *lock* A -> OK        | Trattiene *lock* B -> OK        |
93962306a36Sopenharmony_ci+---------------------------------+---------------------------------+
94062306a36Sopenharmony_ci| Trattiene *lock* B -> attesa    | Trattiene *lock* A -> attesa    |
94162306a36Sopenharmony_ci+---------------------------------+---------------------------------+
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ciTable: Conseguenze
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ciEntrambe i processori rimarranno in attesa attiva sul *lock* per sempre,
94662306a36Sopenharmony_ciaspettando che l'altro lo rilasci. Sembra e puzza come un blocco totale.
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ciPrevenire gli stalli
94962306a36Sopenharmony_ci--------------------
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ciI libri di testo vi diranno che se trattenete i *lock* sempre nello stesso
95262306a36Sopenharmony_ciordine non avrete mai un simile stallo. La pratica vi dirà che questo
95362306a36Sopenharmony_ciapproccio non funziona all'ingrandirsi del sistema: quando creo un nuovo
95462306a36Sopenharmony_ci*lock* non ne capisco abbastanza del kernel per dire in quale dei 5000 *lock*
95562306a36Sopenharmony_cisi incastrerà.
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ciI *lock* migliori sono quelli incapsulati: non vengono esposti nei file di
95862306a36Sopenharmony_ciintestazione, e non vengono mai trattenuti fuori dallo stesso file. Potete
95962306a36Sopenharmony_cirileggere questo codice e vedere che non ci sarà mai uno stallo perché
96062306a36Sopenharmony_cinon tenterà mai di trattenere un altro *lock* quando lo ha già.
96162306a36Sopenharmony_ciLe persone che usano il vostro codice non devono nemmeno sapere che voi
96262306a36Sopenharmony_cistate usando dei *lock*.
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ciUn classico problema deriva dall'uso di *callback* e di *hook*: se li
96562306a36Sopenharmony_cichiamate mentre trattenete un *lock*, rischiate uno stallo o un abbraccio
96662306a36Sopenharmony_cidella morte (chi lo sa cosa farà una *callback*?).
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ciOssessiva prevenzione degli stalli
96962306a36Sopenharmony_ci~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ciGli stalli sono un problema, ma non così terribile come la corruzione dei dati.
97262306a36Sopenharmony_ciUn pezzo di codice trattiene un *lock* di lettura, cerca in una lista,
97362306a36Sopenharmony_cifallisce nel trovare quello che vuole, quindi rilascia il *lock* di lettura,
97462306a36Sopenharmony_citrattiene un *lock* di scrittura ed inserisce un oggetto; questo genere di
97562306a36Sopenharmony_cicodice presenta una corsa critica.
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_cicorsa fra temporizzatori: un passatempo del kernel
97862306a36Sopenharmony_ci--------------------------------------------------
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ciI temporizzatori potrebbero avere dei problemi con le corse critiche.
98162306a36Sopenharmony_ciConsiderate una collezione di oggetti (liste, hash, eccetera) dove ogni oggetto
98262306a36Sopenharmony_ciha un temporizzatore che sta per distruggerlo.
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ciSe volete eliminare l'intera collezione (diciamo quando rimuovete un modulo),
98562306a36Sopenharmony_cipotreste fare come segue::
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ci            /* THIS CODE BAD BAD BAD BAD: IF IT WAS ANY WORSE IT WOULD USE
98862306a36Sopenharmony_ci               HUNGARIAN NOTATION */
98962306a36Sopenharmony_ci            spin_lock_bh(&list_lock);
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci            while (list) {
99262306a36Sopenharmony_ci                    struct foo *next = list->next;
99362306a36Sopenharmony_ci                    timer_delete(&list->timer);
99462306a36Sopenharmony_ci                    kfree(list);
99562306a36Sopenharmony_ci                    list = next;
99662306a36Sopenharmony_ci            }
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci            spin_unlock_bh(&list_lock);
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ciPrimo o poi, questo esploderà su un sistema multiprocessore perché un
100162306a36Sopenharmony_citemporizzatore potrebbe essere già partiro prima di spin_lock_bh(),
100262306a36Sopenharmony_cie prenderà il *lock* solo dopo spin_unlock_bh(), e cercherà
100362306a36Sopenharmony_cidi eliminare il suo oggetto (che però è già stato eliminato).
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ciQuesto può essere evitato controllando il valore di ritorno di
100662306a36Sopenharmony_citimer_delete(): se ritorna 1, il temporizzatore è stato già
100762306a36Sopenharmony_cirimosso. Se 0, significa (in questo caso) che il temporizzatore è in
100862306a36Sopenharmony_ciesecuzione, quindi possiamo fare come segue::
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci            retry:
101162306a36Sopenharmony_ci                    spin_lock_bh(&list_lock);
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci                    while (list) {
101462306a36Sopenharmony_ci                            struct foo *next = list->next;
101562306a36Sopenharmony_ci                            if (!timer_delete(&list->timer)) {
101662306a36Sopenharmony_ci                                    /* Give timer a chance to delete this */
101762306a36Sopenharmony_ci                                    spin_unlock_bh(&list_lock);
101862306a36Sopenharmony_ci                                    goto retry;
101962306a36Sopenharmony_ci                            }
102062306a36Sopenharmony_ci                            kfree(list);
102162306a36Sopenharmony_ci                            list = next;
102262306a36Sopenharmony_ci                    }
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci                    spin_unlock_bh(&list_lock);
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ciUn altro problema è l'eliminazione dei temporizzatori che si riavviano
102762306a36Sopenharmony_cida soli (chiamando add_timer() alla fine della loro esecuzione).
102862306a36Sopenharmony_ciDato che questo è un problema abbastanza comune con una propensione
102962306a36Sopenharmony_cialle corse critiche, dovreste usare timer_delete_sync()
103062306a36Sopenharmony_ci(``include/linux/timer.h``) per gestire questo caso.
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ciPrima di rilasciare un temporizzatore dovreste chiamare la funzione
103362306a36Sopenharmony_citimer_shutdown() o timer_shutdown_sync() di modo che non venga più riarmato.
103462306a36Sopenharmony_ciOgni successivo tentativo di riarmare il temporizzatore verrà silenziosamente
103562306a36Sopenharmony_ciignorato.
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_ciVelocità della sincronizzazione
103862306a36Sopenharmony_ci===============================
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_ciCi sono tre cose importanti da tenere in considerazione quando si valuta
104162306a36Sopenharmony_cila velocità d'esecuzione di un pezzo di codice che necessita di
104262306a36Sopenharmony_cisincronizzazione. La prima è la concorrenza: quante cose rimangono in attesa
104362306a36Sopenharmony_cimentre qualcuno trattiene un *lock*. La seconda è il tempo necessario per
104462306a36Sopenharmony_ciacquisire (senza contese) e rilasciare un *lock*. La terza è di usare meno
104562306a36Sopenharmony_ci*lock* o di più furbi. Immagino che i *lock* vengano usati regolarmente,
104662306a36Sopenharmony_cialtrimenti, non sareste interessati all'efficienza.
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_ciLa concorrenza dipende da quanto a lungo un *lock* è trattenuto: dovreste
104962306a36Sopenharmony_citrattenere un *lock* solo il tempo minimo necessario ma non un istante in più.
105062306a36Sopenharmony_ciNella memoria dell'esempio precedente, creiamo gli oggetti senza trattenere
105162306a36Sopenharmony_ciil *lock*, poi acquisiamo il *lock* quando siamo pronti per inserirlo nella
105262306a36Sopenharmony_cilista.
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ciIl tempo di acquisizione di un *lock* dipende da quanto danno fa
105562306a36Sopenharmony_cil'operazione sulla *pipeline* (ovvero stalli della *pipeline*) e quant'è
105662306a36Sopenharmony_ciprobabile che il processore corrente sia stato anche l'ultimo ad acquisire
105762306a36Sopenharmony_ciil *lock* (in pratica, il *lock* è nella memoria cache del processore
105862306a36Sopenharmony_cicorrente?): su sistemi multi-processore questa probabilità precipita
105962306a36Sopenharmony_cirapidamente. Consideriamo un processore Intel Pentium III a 700Mhz: questo
106062306a36Sopenharmony_ciesegue un'istruzione in 0.7ns, un incremento atomico richiede 58ns, acquisire
106162306a36Sopenharmony_ciun *lock* che è nella memoria cache del processore richiede 160ns, e un
106262306a36Sopenharmony_citrasferimento dalla memoria cache di un altro processore richiede altri
106362306a36Sopenharmony_ci170/360ns (Leggetevi l'articolo di Paul McKenney's `Linux Journal RCU
106462306a36Sopenharmony_ciarticle <http://www.linuxjournal.com/article.php?sid=6993>`__).
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ciQuesti due obiettivi sono in conflitto: trattenere un *lock* per il minor
106762306a36Sopenharmony_citempo possibile potrebbe richiedere la divisione in più *lock* per diverse
106862306a36Sopenharmony_ciparti (come nel nostro ultimo esempio con un *lock* per ogni oggetto),
106962306a36Sopenharmony_cima questo aumenta il numero di acquisizioni di *lock*, ed il risultato
107062306a36Sopenharmony_cispesso è che tutto è più lento che con un singolo *lock*. Questo è un altro
107162306a36Sopenharmony_ciargomento in favore della semplicità quando si parla di sincronizzazione.
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ciIl terzo punto è discusso di seguito: ci sono alcune tecniche per ridurre
107462306a36Sopenharmony_ciil numero di sincronizzazioni che devono essere fatte.
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ciRead/Write Lock Variants
107762306a36Sopenharmony_ci------------------------
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ciSia gli spinlock che i mutex hanno una variante per la lettura/scrittura
108062306a36Sopenharmony_ci(read/write): ``rwlock_t`` e :c:type:`struct rw_semaphore <rw_semaphore>`.
108162306a36Sopenharmony_ciQueste dividono gli utenti in due categorie: i lettori e gli scrittori.
108262306a36Sopenharmony_ciSe state solo leggendo i dati, potete acquisire il *lock* di lettura, ma
108362306a36Sopenharmony_ciper scrivere avrete bisogno del *lock* di scrittura. Molti possono trattenere
108462306a36Sopenharmony_ciil *lock* di lettura, ma solo uno scrittore alla volta può trattenere
108562306a36Sopenharmony_ciquello di scrittura.
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_ciSe il vostro codice si divide chiaramente in codice per lettori e codice
108862306a36Sopenharmony_ciper scrittori (come nel nostro esempio), e il *lock* dei lettori viene
108962306a36Sopenharmony_citrattenuto per molto tempo, allora l'uso di questo tipo di *lock* può aiutare.
109062306a36Sopenharmony_ciQuesti sono leggermente più lenti rispetto alla loro versione normale, quindi
109162306a36Sopenharmony_cinella pratica l'uso di ``rwlock_t`` non ne vale la pena.
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_ciEvitare i *lock*: Read Copy Update
109462306a36Sopenharmony_ci--------------------------------------------
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ciEsiste un metodo di sincronizzazione per letture e scritture detto
109762306a36Sopenharmony_ciRead Copy Update. Con l'uso della tecnica RCU, i lettori possono scordarsi
109862306a36Sopenharmony_cicompletamente di trattenere i *lock*; dato che nel nostro esempio ci
109962306a36Sopenharmony_ciaspettiamo d'avere più lettore che scrittori (altrimenti questa memoria
110062306a36Sopenharmony_cisarebbe uno spreco) possiamo dire che questo meccanismo permette
110162306a36Sopenharmony_ciun'ottimizzazione.
110262306a36Sopenharmony_ci
110362306a36Sopenharmony_ciCome facciamo a sbarazzarci dei *lock* di lettura? Sbarazzarsi dei *lock* di
110462306a36Sopenharmony_cilettura significa che uno scrittore potrebbe cambiare la lista sotto al naso
110562306a36Sopenharmony_cidei lettori. Questo è abbastanza semplice: possiamo leggere una lista
110662306a36Sopenharmony_ciconcatenata se lo scrittore aggiunge elementi alla fine e con certe
110762306a36Sopenharmony_ciprecauzioni. Per esempio, aggiungendo ``new`` ad una lista concatenata
110862306a36Sopenharmony_cichiamata ``list``::
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ci            new->next = list->next;
111162306a36Sopenharmony_ci            wmb();
111262306a36Sopenharmony_ci            list->next = new;
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_ciLa funzione wmb() è una barriera di sincronizzazione delle
111562306a36Sopenharmony_ciscritture. Questa garantisce che la prima operazione (impostare l'elemento
111662306a36Sopenharmony_ci``next`` del nuovo elemento) venga completata e vista da tutti i processori
111762306a36Sopenharmony_ciprima che venga eseguita la seconda operazione (che sarebbe quella di mettere
111862306a36Sopenharmony_ciil nuovo elemento nella lista). Questo è importante perché i moderni
111962306a36Sopenharmony_cicompilatori ed i moderni processori possono, entrambe, riordinare le istruzioni
112062306a36Sopenharmony_cise non vengono istruiti altrimenti: vogliamo che i lettori non vedano
112162306a36Sopenharmony_cicompletamente il nuovo elemento; oppure che lo vedano correttamente e quindi
112262306a36Sopenharmony_ciil puntatore ``next`` deve puntare al resto della lista.
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ciFortunatamente, c'è una funzione che fa questa operazione sulle liste
112562306a36Sopenharmony_ci:c:type:`struct list_head <list_head>`: list_add_rcu()
112662306a36Sopenharmony_ci(``include/linux/list.h``).
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ciRimuovere un elemento dalla lista è anche più facile: sostituiamo il puntatore
112962306a36Sopenharmony_cial vecchio elemento con quello del suo successore, e i lettori vedranno
113062306a36Sopenharmony_cil'elemento o lo salteranno.
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_ci::
113362306a36Sopenharmony_ci
113462306a36Sopenharmony_ci            list->next = old->next;
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ciLa funzione list_del_rcu() (``include/linux/list.h``) fa esattamente
113762306a36Sopenharmony_ciquesto (la versione normale corrompe il vecchio oggetto, e non vogliamo che
113862306a36Sopenharmony_ciaccada).
113962306a36Sopenharmony_ci
114062306a36Sopenharmony_ciAnche i lettori devono stare attenti: alcuni processori potrebbero leggere
114162306a36Sopenharmony_ciattraverso il puntatore ``next`` il contenuto dell'elemento successivo
114262306a36Sopenharmony_citroppo presto, ma non accorgersi che il contenuto caricato è sbagliato quando
114362306a36Sopenharmony_ciil puntatore ``next`` viene modificato alla loro spalle. Ancora una volta
114462306a36Sopenharmony_cic'è una funzione che viene in vostro aiuto list_for_each_entry_rcu()
114562306a36Sopenharmony_ci(``include/linux/list.h``). Ovviamente, gli scrittori possono usare
114662306a36Sopenharmony_cilist_for_each_entry() dato che non ci possono essere due scrittori
114762306a36Sopenharmony_ciin contemporanea.
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_ciIl nostro ultimo dilemma è il seguente: quando possiamo realmente distruggere
115062306a36Sopenharmony_cil'elemento rimosso? Ricordate, un lettore potrebbe aver avuto accesso a questo
115162306a36Sopenharmony_cielemento proprio ora: se eliminiamo questo elemento ed il puntatore ``next``
115262306a36Sopenharmony_cicambia, il lettore salterà direttamente nella spazzatura e scoppierà. Dobbiamo
115362306a36Sopenharmony_ciaspettare finché tutti i lettori che stanno attraversando la lista abbiano
115462306a36Sopenharmony_cifinito. Utilizziamo call_rcu() per registrare una funzione di
115562306a36Sopenharmony_cirichiamo che distrugga l'oggetto quando tutti i lettori correnti hanno
115662306a36Sopenharmony_citerminato. In alternative, potrebbe essere usata la funzione
115762306a36Sopenharmony_cisynchronize_rcu() che blocca l'esecuzione finché tutti i lettori
115862306a36Sopenharmony_cinon terminano di ispezionare la lista.
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ciMa come fa l'RCU a sapere quando i lettori sono finiti? Il meccanismo è
116162306a36Sopenharmony_ciil seguente: innanzi tutto i lettori accedono alla lista solo fra la coppia
116262306a36Sopenharmony_circu_read_lock()/rcu_read_unlock() che disabilita la
116362306a36Sopenharmony_ciprelazione così che i lettori non vengano sospesi mentre stanno leggendo
116462306a36Sopenharmony_cila lista.
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_ciPoi, l'RCU aspetta finché tutti i processori non abbiano dormito almeno
116762306a36Sopenharmony_ciuna volta; a questo punto, dato che i lettori non possono dormire, possiamo
116862306a36Sopenharmony_cidedurre che un qualsiasi lettore che abbia consultato la lista durante la
116962306a36Sopenharmony_cirimozione abbia già terminato, quindi la *callback* viene eseguita. Il vero
117062306a36Sopenharmony_cicodice RCU è un po' più ottimizzato di così, ma questa è l'idea di fondo.
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_ci::
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_ci    --- cache.c.perobjectlock   2003-12-11 17:15:03.000000000 +1100
117562306a36Sopenharmony_ci    +++ cache.c.rcupdate    2003-12-11 17:55:14.000000000 +1100
117662306a36Sopenharmony_ci    @@ -1,15 +1,18 @@
117762306a36Sopenharmony_ci     #include <linux/list.h>
117862306a36Sopenharmony_ci     #include <linux/slab.h>
117962306a36Sopenharmony_ci     #include <linux/string.h>
118062306a36Sopenharmony_ci    +#include <linux/rcupdate.h>
118162306a36Sopenharmony_ci     #include <linux/mutex.h>
118262306a36Sopenharmony_ci     #include <asm/errno.h>
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci     struct object
118562306a36Sopenharmony_ci     {
118662306a36Sopenharmony_ci    -        /* These two protected by cache_lock. */
118762306a36Sopenharmony_ci    +        /* This is protected by RCU */
118862306a36Sopenharmony_ci             struct list_head list;
118962306a36Sopenharmony_ci             int popularity;
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_ci    +        struct rcu_head rcu;
119262306a36Sopenharmony_ci    +
119362306a36Sopenharmony_ci             atomic_t refcnt;
119462306a36Sopenharmony_ci
119562306a36Sopenharmony_ci             /* Doesn't change once created. */
119662306a36Sopenharmony_ci    @@ -40,7 +43,7 @@
119762306a36Sopenharmony_ci     {
119862306a36Sopenharmony_ci             struct object *i;
119962306a36Sopenharmony_ci
120062306a36Sopenharmony_ci    -        list_for_each_entry(i, &cache, list) {
120162306a36Sopenharmony_ci    +        list_for_each_entry_rcu(i, &cache, list) {
120262306a36Sopenharmony_ci                     if (i->id == id) {
120362306a36Sopenharmony_ci                             i->popularity++;
120462306a36Sopenharmony_ci                             return i;
120562306a36Sopenharmony_ci    @@ -49,19 +52,25 @@
120662306a36Sopenharmony_ci             return NULL;
120762306a36Sopenharmony_ci     }
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_ci    +/* Final discard done once we know no readers are looking. */
121062306a36Sopenharmony_ci    +static void cache_delete_rcu(void *arg)
121162306a36Sopenharmony_ci    +{
121262306a36Sopenharmony_ci    +        object_put(arg);
121362306a36Sopenharmony_ci    +}
121462306a36Sopenharmony_ci    +
121562306a36Sopenharmony_ci     /* Must be holding cache_lock */
121662306a36Sopenharmony_ci     static void __cache_delete(struct object *obj)
121762306a36Sopenharmony_ci     {
121862306a36Sopenharmony_ci             BUG_ON(!obj);
121962306a36Sopenharmony_ci    -        list_del(&obj->list);
122062306a36Sopenharmony_ci    -        object_put(obj);
122162306a36Sopenharmony_ci    +        list_del_rcu(&obj->list);
122262306a36Sopenharmony_ci             cache_num--;
122362306a36Sopenharmony_ci    +        call_rcu(&obj->rcu, cache_delete_rcu);
122462306a36Sopenharmony_ci     }
122562306a36Sopenharmony_ci
122662306a36Sopenharmony_ci     /* Must be holding cache_lock */
122762306a36Sopenharmony_ci     static void __cache_add(struct object *obj)
122862306a36Sopenharmony_ci     {
122962306a36Sopenharmony_ci    -        list_add(&obj->list, &cache);
123062306a36Sopenharmony_ci    +        list_add_rcu(&obj->list, &cache);
123162306a36Sopenharmony_ci             if (++cache_num > MAX_CACHE_SIZE) {
123262306a36Sopenharmony_ci                     struct object *i, *outcast = NULL;
123362306a36Sopenharmony_ci                     list_for_each_entry(i, &cache, list) {
123462306a36Sopenharmony_ci    @@ -104,12 +114,11 @@
123562306a36Sopenharmony_ci     struct object *cache_find(int id)
123662306a36Sopenharmony_ci     {
123762306a36Sopenharmony_ci             struct object *obj;
123862306a36Sopenharmony_ci    -        unsigned long flags;
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_ci    -        spin_lock_irqsave(&cache_lock, flags);
124162306a36Sopenharmony_ci    +        rcu_read_lock();
124262306a36Sopenharmony_ci             obj = __cache_find(id);
124362306a36Sopenharmony_ci             if (obj)
124462306a36Sopenharmony_ci                     object_get(obj);
124562306a36Sopenharmony_ci    -        spin_unlock_irqrestore(&cache_lock, flags);
124662306a36Sopenharmony_ci    +        rcu_read_unlock();
124762306a36Sopenharmony_ci             return obj;
124862306a36Sopenharmony_ci     }
124962306a36Sopenharmony_ci
125062306a36Sopenharmony_ciDa notare che i lettori modificano il campo popularity nella funzione
125162306a36Sopenharmony_ci__cache_find(), e ora non trattiene alcun *lock*. Una soluzione
125262306a36Sopenharmony_cipotrebbe essere quella di rendere la variabile ``atomic_t``, ma per l'uso
125362306a36Sopenharmony_ciche ne abbiamo fatto qui, non ci interessano queste corse critiche perché un
125462306a36Sopenharmony_cirisultato approssimativo è comunque accettabile, quindi non l'ho cambiato.
125562306a36Sopenharmony_ci
125662306a36Sopenharmony_ciIl risultato è che la funzione cache_find() non ha bisogno di alcuna
125762306a36Sopenharmony_cisincronizzazione con le altre funzioni, quindi è veloce su un sistema
125862306a36Sopenharmony_cimulti-processore tanto quanto lo sarebbe su un sistema mono-processore.
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_ciEsiste un'ulteriore ottimizzazione possibile: vi ricordate il codice originale
126162306a36Sopenharmony_cidella nostra memoria dove non c'erano contatori di riferimenti e il chiamante
126262306a36Sopenharmony_cisemplicemente tratteneva il *lock* prima di accedere ad un oggetto? Questo è
126362306a36Sopenharmony_ciancora possibile: se trattenete un *lock* nessuno potrà cancellare l'oggetto,
126462306a36Sopenharmony_ciquindi non avete bisogno di incrementare e decrementare il contatore di
126562306a36Sopenharmony_ciriferimenti.
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_ciOra, dato che il '*lock* di lettura' di un RCU non fa altro che disabilitare
126862306a36Sopenharmony_cila prelazione, un chiamante che ha sempre la prelazione disabilitata fra le
126962306a36Sopenharmony_cichiamate cache_find() e object_put() non necessita
127062306a36Sopenharmony_cidi incrementare e decrementare il contatore di riferimenti. Potremmo
127162306a36Sopenharmony_ciesporre la funzione __cache_find() dichiarandola non-static,
127262306a36Sopenharmony_cie quel chiamante potrebbe usare direttamente questa funzione.
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_ciIl beneficio qui sta nel fatto che il contatore di riferimenti no
127562306a36Sopenharmony_civiene scritto: l'oggetto non viene alterato in alcun modo e quindi diventa
127662306a36Sopenharmony_cimolto più veloce su sistemi molti-processore grazie alla loro memoria cache.
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_ci
127962306a36Sopenharmony_ciDati per processore
128062306a36Sopenharmony_ci-------------------
128162306a36Sopenharmony_ci
128262306a36Sopenharmony_ciUn'altra tecnica comunemente usata per evitare la sincronizzazione è quella
128362306a36Sopenharmony_cidi duplicare le informazioni per ogni processore. Per esempio, se volete
128462306a36Sopenharmony_ciavere un contatore di qualcosa, potreste utilizzare uno spinlock ed un
128562306a36Sopenharmony_cisingolo contatore. Facile e pulito.
128662306a36Sopenharmony_ci
128762306a36Sopenharmony_ciSe questo dovesse essere troppo lento (solitamente non lo è, ma se avete
128862306a36Sopenharmony_cidimostrato che lo è devvero), potreste usare un contatore per ogni processore
128962306a36Sopenharmony_cie quindi non sarebbe più necessaria la mutua esclusione. Vedere
129062306a36Sopenharmony_ciDEFINE_PER_CPU(), get_cpu_var() e put_cpu_var()
129162306a36Sopenharmony_ci(``include/linux/percpu.h``).
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_ciIl tipo di dato ``local_t``, la funzione cpu_local_inc() e tutte
129462306a36Sopenharmony_cile altre funzioni associate, sono di particolare utilità per semplici contatori
129562306a36Sopenharmony_ciper-processore; su alcune architetture sono anche più efficienti
129662306a36Sopenharmony_ci(``include/asm/local.h``).
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ciDa notare che non esiste un modo facile ed affidabile per ottenere il valore
129962306a36Sopenharmony_cidi un simile contatore senza introdurre altri *lock*. In alcuni casi questo
130062306a36Sopenharmony_cinon è un problema.
130162306a36Sopenharmony_ci
130262306a36Sopenharmony_ciDati che sono usati prevalentemente dai gestori d'interruzioni
130362306a36Sopenharmony_ci--------------------------------------------------------------
130462306a36Sopenharmony_ci
130562306a36Sopenharmony_ciSe i dati vengono utilizzati sempre dallo stesso gestore d'interruzioni,
130662306a36Sopenharmony_ciallora i *lock* non vi servono per niente: il kernel già vi garantisce che
130762306a36Sopenharmony_ciil gestore d'interruzione non verrà eseguito in contemporanea su diversi
130862306a36Sopenharmony_ciprocessori.
130962306a36Sopenharmony_ci
131062306a36Sopenharmony_ciManfred Spraul fa notare che potreste comunque comportarvi così anche
131162306a36Sopenharmony_cise i dati vengono occasionalmente utilizzati da un contesto utente o
131262306a36Sopenharmony_cida un'interruzione software. Il gestore d'interruzione non utilizza alcun
131362306a36Sopenharmony_ci*lock*, e tutti gli altri accessi verranno fatti così::
131462306a36Sopenharmony_ci
131562306a36Sopenharmony_ci        mutex_lock(&lock);
131662306a36Sopenharmony_ci        disable_irq(irq);
131762306a36Sopenharmony_ci        ...
131862306a36Sopenharmony_ci        enable_irq(irq);
131962306a36Sopenharmony_ci        mutex_unlock(&lock);
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_ciLa funzione disable_irq() impedisce al gestore d'interruzioni
132262306a36Sopenharmony_cid'essere eseguito (e aspetta che finisca nel caso fosse in esecuzione su
132362306a36Sopenharmony_ciun altro processore). Lo spinlock, invece, previene accessi simultanei.
132462306a36Sopenharmony_ciNaturalmente, questo è più lento della semplice chiamata
132562306a36Sopenharmony_cispin_lock_irq(), quindi ha senso solo se questo genere di accesso
132662306a36Sopenharmony_ciè estremamente raro.
132762306a36Sopenharmony_ci
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_ciQuali funzioni possono essere chiamate in modo sicuro dalle interruzioni?
133062306a36Sopenharmony_ci=========================================================================
133162306a36Sopenharmony_ci
133262306a36Sopenharmony_ciMolte funzioni del kernel dormono (in sostanza, chiamano schedule())
133362306a36Sopenharmony_cidirettamente od indirettamente: non potete chiamarle se trattenere uno
133462306a36Sopenharmony_cispinlock o avete la prelazione disabilitata, mai. Questo significa che
133562306a36Sopenharmony_cidovete necessariamente essere nel contesto utente: chiamarle da un
133662306a36Sopenharmony_cicontesto d'interruzione è illegale.
133762306a36Sopenharmony_ci
133862306a36Sopenharmony_ciAlcune funzioni che dormono
133962306a36Sopenharmony_ci---------------------------
134062306a36Sopenharmony_ci
134162306a36Sopenharmony_ciLe più comuni sono elencate qui di seguito, ma solitamente dovete leggere
134262306a36Sopenharmony_ciil codice per scoprire se altre chiamate sono sicure. Se chiunque altro
134362306a36Sopenharmony_cile chiami dorme, allora dovreste poter dormire anche voi. In particolar
134462306a36Sopenharmony_cimodo, le funzioni di registrazione e deregistrazione solitamente si
134562306a36Sopenharmony_ciaspettano d'essere chiamante da un contesto utente e quindi che possono
134662306a36Sopenharmony_cidormire.
134762306a36Sopenharmony_ci
134862306a36Sopenharmony_ci-  Accessi allo spazio utente:
134962306a36Sopenharmony_ci
135062306a36Sopenharmony_ci   -  copy_from_user()
135162306a36Sopenharmony_ci
135262306a36Sopenharmony_ci   -  copy_to_user()
135362306a36Sopenharmony_ci
135462306a36Sopenharmony_ci   -  get_user()
135562306a36Sopenharmony_ci
135662306a36Sopenharmony_ci   -  put_user()
135762306a36Sopenharmony_ci
135862306a36Sopenharmony_ci-  kmalloc(GFP_KERNEL) <kmalloc>`
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_ci-  mutex_lock_interruptible() and
136162306a36Sopenharmony_ci   mutex_lock()
136262306a36Sopenharmony_ci
136362306a36Sopenharmony_ci   C'è anche mutex_trylock() che però non dorme.
136462306a36Sopenharmony_ci   Comunque, non deve essere usata in un contesto d'interruzione dato
136562306a36Sopenharmony_ci   che la sua implementazione non è sicura in quel contesto.
136662306a36Sopenharmony_ci   Anche mutex_unlock() non dorme mai. Non può comunque essere
136762306a36Sopenharmony_ci   usata in un contesto d'interruzione perché un mutex deve essere rilasciato
136862306a36Sopenharmony_ci   dallo stesso processo che l'ha acquisito.
136962306a36Sopenharmony_ci
137062306a36Sopenharmony_ciAlcune funzioni che non dormono
137162306a36Sopenharmony_ci-------------------------------
137262306a36Sopenharmony_ci
137362306a36Sopenharmony_ciAlcune funzioni possono essere chiamate tranquillamente da qualsiasi
137462306a36Sopenharmony_cicontesto, o trattenendo un qualsiasi *lock*.
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_ci-  printk()
137762306a36Sopenharmony_ci
137862306a36Sopenharmony_ci-  kfree()
137962306a36Sopenharmony_ci
138062306a36Sopenharmony_ci-  add_timer() e timer_delete()
138162306a36Sopenharmony_ci
138262306a36Sopenharmony_ciRiferimento per l'API dei Mutex
138362306a36Sopenharmony_ci===============================
138462306a36Sopenharmony_ci
138562306a36Sopenharmony_ci.. kernel-doc:: include/linux/mutex.h
138662306a36Sopenharmony_ci   :internal:
138762306a36Sopenharmony_ci
138862306a36Sopenharmony_ci.. kernel-doc:: kernel/locking/mutex.c
138962306a36Sopenharmony_ci   :export:
139062306a36Sopenharmony_ci
139162306a36Sopenharmony_ciRiferimento per l'API dei Futex
139262306a36Sopenharmony_ci===============================
139362306a36Sopenharmony_ci
139462306a36Sopenharmony_ci.. kernel-doc:: kernel/futex/core.c
139562306a36Sopenharmony_ci   :internal:
139662306a36Sopenharmony_ci
139762306a36Sopenharmony_ci.. kernel-doc:: kernel/futex/futex.h
139862306a36Sopenharmony_ci   :internal:
139962306a36Sopenharmony_ci
140062306a36Sopenharmony_ci.. kernel-doc:: kernel/futex/pi.c
140162306a36Sopenharmony_ci   :internal:
140262306a36Sopenharmony_ci
140362306a36Sopenharmony_ci.. kernel-doc:: kernel/futex/requeue.c
140462306a36Sopenharmony_ci   :internal:
140562306a36Sopenharmony_ci
140662306a36Sopenharmony_ci.. kernel-doc:: kernel/futex/waitwake.c
140762306a36Sopenharmony_ci   :internal:
140862306a36Sopenharmony_ci
140962306a36Sopenharmony_ciApprofondimenti
141062306a36Sopenharmony_ci===============
141162306a36Sopenharmony_ci
141262306a36Sopenharmony_ci-  ``Documentation/locking/spinlocks.rst``: la guida di Linus Torvalds agli
141362306a36Sopenharmony_ci   spinlock del kernel.
141462306a36Sopenharmony_ci
141562306a36Sopenharmony_ci-  Unix Systems for Modern Architectures: Symmetric Multiprocessing and
141662306a36Sopenharmony_ci   Caching for Kernel Programmers.
141762306a36Sopenharmony_ci
141862306a36Sopenharmony_ci   L'introduzione alla sincronizzazione a livello di kernel di Curt Schimmel
141962306a36Sopenharmony_ci   è davvero ottima (non è scritta per Linux, ma approssimativamente si adatta
142062306a36Sopenharmony_ci   a tutte le situazioni). Il libro è costoso, ma vale ogni singolo spicciolo
142162306a36Sopenharmony_ci   per capire la sincronizzazione nei sistemi multi-processore.
142262306a36Sopenharmony_ci   [ISBN: 0201633388]
142362306a36Sopenharmony_ci
142462306a36Sopenharmony_ciRingraziamenti
142562306a36Sopenharmony_ci==============
142662306a36Sopenharmony_ci
142762306a36Sopenharmony_ciGrazie a Telsa Gwynne per aver formattato questa guida in DocBook, averla
142862306a36Sopenharmony_cipulita e aggiunto un po' di stile.
142962306a36Sopenharmony_ci
143062306a36Sopenharmony_ciGrazie a Martin Pool, Philipp Rumpf, Stephen Rothwell, Paul Mackerras,
143162306a36Sopenharmony_ciRuedi Aschwanden, Alan Cox, Manfred Spraul, Tim Waugh, Pete Zaitcev,
143262306a36Sopenharmony_ciJames Morris, Robert Love, Paul McKenney, John Ashby per aver revisionato,
143362306a36Sopenharmony_cicorretto, maledetto e commentato.
143462306a36Sopenharmony_ci
143562306a36Sopenharmony_ciGrazie alla congrega per non aver avuto alcuna influenza su questo documento.
143662306a36Sopenharmony_ci
143762306a36Sopenharmony_ciGlossario
143862306a36Sopenharmony_ci=========
143962306a36Sopenharmony_ci
144062306a36Sopenharmony_ciprelazione
144162306a36Sopenharmony_ci  Prima del kernel 2.5, o quando ``CONFIG_PREEMPT`` non è impostato, i processi
144262306a36Sopenharmony_ci  in contesto utente non si avvicendano nell'esecuzione (in pratica, il
144362306a36Sopenharmony_ci  processo userà il processore fino al proprio termine, a meno che non ci siano
144462306a36Sopenharmony_ci  delle interruzioni). Con l'aggiunta di ``CONFIG_PREEMPT`` nella versione
144562306a36Sopenharmony_ci  2.5.4 questo è cambiato: quando si è in contesto utente, processi con una
144662306a36Sopenharmony_ci  priorità maggiore possono subentrare nell'esecuzione: gli spinlock furono
144762306a36Sopenharmony_ci  cambiati per disabilitare la prelazioni, anche su sistemi monoprocessore.
144862306a36Sopenharmony_ci
144962306a36Sopenharmony_cibh
145062306a36Sopenharmony_ci  Bottom Half: per ragioni storiche, le funzioni che contengono '_bh' nel
145162306a36Sopenharmony_ci  loro nome ora si riferiscono a qualsiasi interruzione software; per esempio,
145262306a36Sopenharmony_ci  spin_lock_bh() blocca qualsiasi interuzione software sul processore
145362306a36Sopenharmony_ci  corrente. I *Bottom Halves* sono deprecati, e probabilmente verranno
145462306a36Sopenharmony_ci  sostituiti dai tasklet. In un dato momento potrà esserci solo un
145562306a36Sopenharmony_ci  *bottom half* in esecuzione.
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_cicontesto d'interruzione
145862306a36Sopenharmony_ci  Non è il contesto utente: qui si processano le interruzioni hardware e
145962306a36Sopenharmony_ci  software. La macro in_interrupt() ritorna vero.
146062306a36Sopenharmony_ci
146162306a36Sopenharmony_cicontesto utente
146262306a36Sopenharmony_ci  Il kernel che esegue qualcosa per conto di un particolare processo (per
146362306a36Sopenharmony_ci  esempio una chiamata di sistema) o di un thread del kernel. Potete
146462306a36Sopenharmony_ci  identificare il processo con la macro ``current``. Da non confondere
146562306a36Sopenharmony_ci  con lo spazio utente. Può essere interrotto sia da interruzioni software
146662306a36Sopenharmony_ci  che hardware.
146762306a36Sopenharmony_ci
146862306a36Sopenharmony_ciinterruzione hardware
146962306a36Sopenharmony_ci  Richiesta di interruzione hardware. in_hardirq() ritorna vero in un
147062306a36Sopenharmony_ci  gestore d'interruzioni hardware.
147162306a36Sopenharmony_ci
147262306a36Sopenharmony_ciinterruzione software / softirq
147362306a36Sopenharmony_ci  Gestore di interruzioni software: in_hardirq() ritorna falso;
147462306a36Sopenharmony_ci  in_softirq() ritorna vero. I tasklet e le softirq sono entrambi
147562306a36Sopenharmony_ci  considerati 'interruzioni software'.
147662306a36Sopenharmony_ci
147762306a36Sopenharmony_ci  In soldoni, un softirq è uno delle 32 interruzioni software che possono
147862306a36Sopenharmony_ci  essere eseguite su più processori in contemporanea. A volte si usa per
147962306a36Sopenharmony_ci  riferirsi anche ai tasklet (in pratica tutte le interruzioni software).
148062306a36Sopenharmony_ci
148162306a36Sopenharmony_cimonoprocessore / UP
148262306a36Sopenharmony_ci  (Uni-Processor) un solo processore, ovvero non è SMP. (``CONFIG_SMP=n``).
148362306a36Sopenharmony_ci
148462306a36Sopenharmony_cimulti-processore / SMP
148562306a36Sopenharmony_ci  (Symmetric Multi-Processor) kernel compilati per sistemi multi-processore
148662306a36Sopenharmony_ci  (``CONFIG_SMP=y``).
148762306a36Sopenharmony_ci
148862306a36Sopenharmony_cispazio utente
148962306a36Sopenharmony_ci  Un processo che esegue il proprio codice fuori dal kernel.
149062306a36Sopenharmony_ci
149162306a36Sopenharmony_citasklet
149262306a36Sopenharmony_ci  Un'interruzione software registrabile dinamicamente che ha la garanzia
149362306a36Sopenharmony_ci  d'essere eseguita solo su un processore alla volta.
149462306a36Sopenharmony_ci
149562306a36Sopenharmony_citimer
149662306a36Sopenharmony_ci  Un'interruzione software registrabile dinamicamente che viene eseguita
149762306a36Sopenharmony_ci  (circa) in un determinato momento. Quando è in esecuzione è come un tasklet
149862306a36Sopenharmony_ci  (infatti, sono chiamati da ``TIMER_SOFTIRQ``).
1499