18c2ecf20Sopenharmony_ci.. include:: ../disclaimer-ita.rst
28c2ecf20Sopenharmony_ci
38c2ecf20Sopenharmony_ci.. c:namespace:: it_IT
48c2ecf20Sopenharmony_ci
58c2ecf20Sopenharmony_ci:Original: :ref:`Documentation/kernel-hacking/locking.rst <kernel_hacking_lock>`
68c2ecf20Sopenharmony_ci:Translator: Federico Vaga <federico.vaga@vaga.pv.it>
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci.. _it_kernel_hacking_lock:
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci==========================================
118c2ecf20Sopenharmony_ciL'inaffidabile guida alla sincronizzazione
128c2ecf20Sopenharmony_ci==========================================
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci:Author: Rusty Russell
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ciIntroduzione
178c2ecf20Sopenharmony_ci============
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ciBenvenuto, alla notevole ed inaffidabile guida ai problemi di sincronizzazione
208c2ecf20Sopenharmony_ci(locking) nel kernel. Questo documento descrive il sistema di sincronizzazione
218c2ecf20Sopenharmony_cinel kernel Linux 2.6.
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ciDato il largo utilizzo del multi-threading e della prelazione nel kernel
248c2ecf20Sopenharmony_ciLinux, chiunque voglia dilettarsi col kernel deve conoscere i concetti
258c2ecf20Sopenharmony_cifondamentali della concorrenza e della sincronizzazione nei sistemi
268c2ecf20Sopenharmony_cimulti-processore.
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ciIl problema con la concorrenza
298c2ecf20Sopenharmony_ci==============================
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci(Saltatelo se sapete già cos'è una corsa critica).
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ciIn un normale programma, potete incrementare un contatore nel seguente modo:
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci::
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci          contatore++;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ciQuesto è quello che vi aspettereste che accada sempre:
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci.. table:: Risultati attesi
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci  +------------------------------------+------------------------------------+
458c2ecf20Sopenharmony_ci  | Istanza 1                          | Istanza 2                          |
468c2ecf20Sopenharmony_ci  +====================================+====================================+
478c2ecf20Sopenharmony_ci  | leggi contatore (5)                |                                    |
488c2ecf20Sopenharmony_ci  +------------------------------------+------------------------------------+
498c2ecf20Sopenharmony_ci  | aggiungi 1 (6)                     |                                    |
508c2ecf20Sopenharmony_ci  +------------------------------------+------------------------------------+
518c2ecf20Sopenharmony_ci  | scrivi contatore (6)               |                                    |
528c2ecf20Sopenharmony_ci  +------------------------------------+------------------------------------+
538c2ecf20Sopenharmony_ci  |                                    | leggi contatore (6)                |
548c2ecf20Sopenharmony_ci  +------------------------------------+------------------------------------+
558c2ecf20Sopenharmony_ci  |                                    | aggiungi 1 (7)                     |
568c2ecf20Sopenharmony_ci  +------------------------------------+------------------------------------+
578c2ecf20Sopenharmony_ci  |                                    | scrivi contatore (7)               |
588c2ecf20Sopenharmony_ci  +------------------------------------+------------------------------------+
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ciQuesto è quello che potrebbe succedere in realtà:
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci.. table:: Possibile risultato
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci  +------------------------------------+------------------------------------+
658c2ecf20Sopenharmony_ci  | Istanza 1                          | Istanza 2                          |
668c2ecf20Sopenharmony_ci  +====================================+====================================+
678c2ecf20Sopenharmony_ci  | leggi contatore (5)                |                                    |
688c2ecf20Sopenharmony_ci  +------------------------------------+------------------------------------+
698c2ecf20Sopenharmony_ci  |                                    | leggi contatore (5)                |
708c2ecf20Sopenharmony_ci  +------------------------------------+------------------------------------+
718c2ecf20Sopenharmony_ci  | aggiungi 1 (6)                     |                                    |
728c2ecf20Sopenharmony_ci  +------------------------------------+------------------------------------+
738c2ecf20Sopenharmony_ci  |                                    | aggiungi 1 (6)                     |
748c2ecf20Sopenharmony_ci  +------------------------------------+------------------------------------+
758c2ecf20Sopenharmony_ci  | scrivi contatore (6)               |                                    |
768c2ecf20Sopenharmony_ci  +------------------------------------+------------------------------------+
778c2ecf20Sopenharmony_ci  |                                    | scrivi contatore (6)               |
788c2ecf20Sopenharmony_ci  +------------------------------------+------------------------------------+
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ciCorse critiche e sezioni critiche
828c2ecf20Sopenharmony_ci---------------------------------
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ciQuesta sovrapposizione, ovvero quando un risultato dipende dal tempo che
858c2ecf20Sopenharmony_ciintercorre fra processi diversi, è chiamata corsa critica. La porzione
868c2ecf20Sopenharmony_cidi codice che contiene questo problema è chiamata sezione critica.
878c2ecf20Sopenharmony_ciIn particolar modo da quando Linux ha incominciato a girare su
888c2ecf20Sopenharmony_cimacchine multi-processore, le sezioni critiche sono diventate uno dei
898c2ecf20Sopenharmony_cimaggiori problemi di progettazione ed implementazione del kernel.
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ciLa prelazione può sortire gli stessi effetti, anche se c'è una sola CPU:
928c2ecf20Sopenharmony_ciinterrompendo un processo nella sua sezione critica otterremo comunque
938c2ecf20Sopenharmony_cila stessa corsa critica. In questo caso, il thread che si avvicenda
948c2ecf20Sopenharmony_cinell'esecuzione potrebbe eseguire anch'esso la sezione critica.
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ciLa soluzione è quella di riconoscere quando avvengono questi accessi
978c2ecf20Sopenharmony_cisimultanei, ed utilizzare i *lock* per accertarsi che solo un'istanza
988c2ecf20Sopenharmony_ciper volta possa entrare nella sezione critica. Il kernel offre delle buone
998c2ecf20Sopenharmony_cifunzioni a questo scopo. E poi ci sono quelle meno buone, ma farò finta
1008c2ecf20Sopenharmony_ciche non esistano.
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ciSincronizzazione nel kernel Linux
1038c2ecf20Sopenharmony_ci=================================
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ciSe posso darvi un suggerimento: non dormite mai con qualcuno più pazzo di
1068c2ecf20Sopenharmony_civoi. Ma se dovessi darvi un suggerimento sulla sincronizzazione:
1078c2ecf20Sopenharmony_ci**mantenetela semplice**.
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ciSiate riluttanti nell'introduzione di nuovi *lock*.
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ciAbbastanza strano, quest'ultimo è l'esatto opposto del mio suggerimento
1128c2ecf20Sopenharmony_cisu quando **avete** dormito con qualcuno più pazzo di voi. E dovreste
1138c2ecf20Sopenharmony_cipensare a prendervi un cane bello grande.
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ciI due principali tipi di *lock* nel kernel: spinlock e mutex
1168c2ecf20Sopenharmony_ci------------------------------------------------------------
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ciCi sono due tipi principali di *lock* nel kernel. Il tipo fondamentale è lo
1198c2ecf20Sopenharmony_cispinlock (``include/asm/spinlock.h``), un semplice *lock* che può essere
1208c2ecf20Sopenharmony_citrattenuto solo da un processo: se non si può trattenere lo spinlock, allora
1218c2ecf20Sopenharmony_cirimane in attesa attiva (in inglese *spinning*) finché non ci riesce.
1228c2ecf20Sopenharmony_ciGli spinlock sono molto piccoli e rapidi, possono essere utilizzati ovunque.
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ciIl secondo tipo è il mutex (``include/linux/mutex.h``): è come uno spinlock,
1258c2ecf20Sopenharmony_cima potreste bloccarvi trattenendolo. Se non potete trattenere un mutex
1268c2ecf20Sopenharmony_ciil vostro processo si auto-sospenderà; verrà riattivato quando il mutex
1278c2ecf20Sopenharmony_civerrà rilasciato. Questo significa che il processore potrà occuparsi d'altro
1288c2ecf20Sopenharmony_cimentre il vostro processo è in attesa. Esistono molti casi in cui non potete
1298c2ecf20Sopenharmony_cipermettervi di sospendere un processo (vedere
1308c2ecf20Sopenharmony_ci:ref:`Quali funzioni possono essere chiamate in modo sicuro dalle interruzioni? <it_sleeping-things>`)
1318c2ecf20Sopenharmony_cie quindi dovrete utilizzare gli spinlock.
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ciNessuno di questi *lock* è ricorsivo: vedere
1348c2ecf20Sopenharmony_ci:ref:`Stallo: semplice ed avanzato <it_deadlock>`
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ciI *lock* e i kernel per sistemi monoprocessore
1378c2ecf20Sopenharmony_ci----------------------------------------------
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ciPer i kernel compilati senza ``CONFIG_SMP`` e senza ``CONFIG_PREEMPT``
1408c2ecf20Sopenharmony_cigli spinlock non esistono. Questa è un'ottima scelta di progettazione:
1418c2ecf20Sopenharmony_ciquando nessun altro processo può essere eseguito in simultanea, allora
1428c2ecf20Sopenharmony_cinon c'è la necessità di avere un *lock*.
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ciSe il kernel è compilato senza ``CONFIG_SMP`` ma con ``CONFIG_PREEMPT``,
1458c2ecf20Sopenharmony_ciallora gli spinlock disabilitano la prelazione; questo è sufficiente a
1468c2ecf20Sopenharmony_ciprevenire le corse critiche. Nella maggior parte dei casi, possiamo considerare
1478c2ecf20Sopenharmony_cila prelazione equivalente ad un sistema multi-processore senza preoccuparci
1488c2ecf20Sopenharmony_cidi trattarla indipendentemente.
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ciDovreste verificare sempre la sincronizzazione con le opzioni ``CONFIG_SMP`` e
1518c2ecf20Sopenharmony_ci``CONFIG_PREEMPT`` abilitate, anche quando non avete un sistema
1528c2ecf20Sopenharmony_cimulti-processore, questo vi permetterà di identificare alcuni problemi
1538c2ecf20Sopenharmony_cidi sincronizzazione.
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ciCome vedremo di seguito, i mutex continuano ad esistere perché sono necessari
1568c2ecf20Sopenharmony_ciper la sincronizzazione fra processi in contesto utente.
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ciSincronizzazione in contesto utente
1598c2ecf20Sopenharmony_ci-----------------------------------
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ciSe avete una struttura dati che verrà utilizzata solo dal contesto utente,
1628c2ecf20Sopenharmony_ciallora, per proteggerla, potete utilizzare un semplice mutex
1638c2ecf20Sopenharmony_ci(``include/linux/mutex.h``). Questo è il caso più semplice: inizializzate il
1648c2ecf20Sopenharmony_cimutex; invocate mutex_lock_interruptible() per trattenerlo e
1658c2ecf20Sopenharmony_cimutex_unlock() per rilasciarlo. C'è anche mutex_lock()
1668c2ecf20Sopenharmony_cima questa dovrebbe essere evitata perché non ritorna in caso di segnali.
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ciPer esempio: ``net/netfilter/nf_sockopt.c`` permette la registrazione
1698c2ecf20Sopenharmony_cidi nuove chiamate per setsockopt() e getsockopt()
1708c2ecf20Sopenharmony_ciusando la funzione nf_register_sockopt(). La registrazione e
1718c2ecf20Sopenharmony_cila rimozione vengono eseguite solamente quando il modulo viene caricato
1728c2ecf20Sopenharmony_cio scaricato (e durante l'avvio del sistema, qui non abbiamo concorrenza),
1738c2ecf20Sopenharmony_cie la lista delle funzioni registrate viene consultata solamente quando
1748c2ecf20Sopenharmony_cisetsockopt() o getsockopt() sono sconosciute al sistema.
1758c2ecf20Sopenharmony_ciIn questo caso ``nf_sockopt_mutex`` è perfetto allo scopo, in particolar modo
1768c2ecf20Sopenharmony_civisto che setsockopt e getsockopt potrebbero dormire.
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ciSincronizzazione fra il contesto utente e i softirq
1798c2ecf20Sopenharmony_ci---------------------------------------------------
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ciSe un softirq condivide dati col contesto utente, avete due problemi.
1828c2ecf20Sopenharmony_ciPrimo, il contesto utente corrente potrebbe essere interroto da un softirq,
1838c2ecf20Sopenharmony_cie secondo, la sezione critica potrebbe essere eseguita da un altro
1848c2ecf20Sopenharmony_ciprocessore. Questo è quando spin_lock_bh()
1858c2ecf20Sopenharmony_ci(``include/linux/spinlock.h``) viene utilizzato. Questo disabilita i softirq
1868c2ecf20Sopenharmony_cisul processore e trattiene il *lock*. Invece, spin_unlock_bh() fa
1878c2ecf20Sopenharmony_cil'opposto. (Il suffisso '_bh' è un residuo storico che fa riferimento al
1888c2ecf20Sopenharmony_ci"Bottom Halves", il vecchio nome delle interruzioni software. In un mondo
1898c2ecf20Sopenharmony_ciperfetto questa funzione si chiamerebbe 'spin_lock_softirq()').
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ciDa notare che in questo caso potete utilizzare anche spin_lock_irq()
1928c2ecf20Sopenharmony_cio spin_lock_irqsave(), queste fermano anche le interruzioni hardware:
1938c2ecf20Sopenharmony_civedere :ref:`Contesto di interruzione hardware <it_hardirq-context>`.
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ciQuesto funziona alla perfezione anche sui sistemi monoprocessore: gli spinlock
1968c2ecf20Sopenharmony_cisvaniscono e questa macro diventa semplicemente local_bh_disable()
1978c2ecf20Sopenharmony_ci(``include/linux/interrupt.h``), la quale impedisce ai softirq d'essere
1988c2ecf20Sopenharmony_cieseguiti.
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ciSincronizzazione fra contesto utente e i tasklet
2018c2ecf20Sopenharmony_ci------------------------------------------------
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ciQuesto caso è uguale al precedente, un tasklet viene eseguito da un softirq.
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ciSincronizzazione fra contesto utente e i timer
2068c2ecf20Sopenharmony_ci----------------------------------------------
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ciAnche questo caso è uguale al precedente, un timer viene eseguito da un
2098c2ecf20Sopenharmony_cisoftirq.
2108c2ecf20Sopenharmony_ciDal punto di vista della sincronizzazione, tasklet e timer sono identici.
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ciSincronizzazione fra tasklet e timer
2138c2ecf20Sopenharmony_ci------------------------------------
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ciQualche volta un tasklet od un timer potrebbero condividere i dati con
2168c2ecf20Sopenharmony_ciun altro tasklet o timer
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ciLo stesso tasklet/timer
2198c2ecf20Sopenharmony_ci~~~~~~~~~~~~~~~~~~~~~~~
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ciDato che un tasklet non viene mai eseguito contemporaneamente su due
2228c2ecf20Sopenharmony_ciprocessori, non dovete preoccuparvi che sia rientrante (ovvero eseguito
2238c2ecf20Sopenharmony_cipiù volte in contemporanea), perfino su sistemi multi-processore.
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ciDifferenti tasklet/timer
2268c2ecf20Sopenharmony_ci~~~~~~~~~~~~~~~~~~~~~~~~
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ciSe un altro tasklet/timer vuole condividere dati col vostro tasklet o timer,
2298c2ecf20Sopenharmony_ciallora avrete bisogno entrambe di spin_lock() e
2308c2ecf20Sopenharmony_cispin_unlock(). Qui spin_lock_bh() è inutile, siete già
2318c2ecf20Sopenharmony_ciin un tasklet ed avete la garanzia che nessun altro verrà eseguito sullo
2328c2ecf20Sopenharmony_cistesso processore.
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ciSincronizzazione fra softirq
2358c2ecf20Sopenharmony_ci----------------------------
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ciSpesso un softirq potrebbe condividere dati con se stesso o un tasklet/timer.
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ciLo stesso softirq
2408c2ecf20Sopenharmony_ci~~~~~~~~~~~~~~~~~
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ciLo stesso softirq può essere eseguito su un diverso processore: allo scopo
2438c2ecf20Sopenharmony_cidi migliorare le prestazioni potete utilizzare dati riservati ad ogni
2448c2ecf20Sopenharmony_ciprocessore (vedere :ref:`Dati per processore <it_per-cpu>`). Se siete arrivati
2458c2ecf20Sopenharmony_cifino a questo punto nell'uso dei softirq, probabilmente tenete alla scalabilità
2468c2ecf20Sopenharmony_cidelle prestazioni abbastanza da giustificarne la complessità aggiuntiva.
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ciDovete utilizzare spin_lock() e spin_unlock() per
2498c2ecf20Sopenharmony_ciproteggere i dati condivisi.
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ciDiversi Softirqs
2528c2ecf20Sopenharmony_ci~~~~~~~~~~~~~~~~
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ciDovete utilizzare spin_lock() e spin_unlock() per
2558c2ecf20Sopenharmony_ciproteggere i dati condivisi, che siano timer, tasklet, diversi softirq o
2568c2ecf20Sopenharmony_cilo stesso o altri softirq: uno qualsiasi di essi potrebbe essere in esecuzione
2578c2ecf20Sopenharmony_cisu un diverso processore.
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci.. _`it_hardirq-context`:
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ciContesto di interruzione hardware
2628c2ecf20Sopenharmony_ci=================================
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ciSolitamente le interruzioni hardware comunicano con un tasklet o un softirq.
2658c2ecf20Sopenharmony_ciSpesso questo si traduce nel mettere in coda qualcosa da fare che verrà
2668c2ecf20Sopenharmony_cipreso in carico da un softirq.
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ciSincronizzazione fra interruzioni hardware e softirq/tasklet
2698c2ecf20Sopenharmony_ci------------------------------------------------------------
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ciSe un gestore di interruzioni hardware condivide dati con un softirq, allora
2728c2ecf20Sopenharmony_ciavrete due preoccupazioni. Primo, il softirq può essere interrotto da
2738c2ecf20Sopenharmony_ciun'interruzione hardware, e secondo, la sezione critica potrebbe essere
2748c2ecf20Sopenharmony_cieseguita da un'interruzione hardware su un processore diverso. Questo è il caso
2758c2ecf20Sopenharmony_cidove spin_lock_irq() viene utilizzato. Disabilita le interruzioni
2768c2ecf20Sopenharmony_cisul processore che l'esegue, poi trattiene il lock. spin_unlock_irq()
2778c2ecf20Sopenharmony_cifa l'opposto.
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ciIl gestore d'interruzione hardware non ha bisogno di usare spin_lock_irq()
2808c2ecf20Sopenharmony_ciperché i softirq non possono essere eseguiti quando il gestore d'interruzione
2818c2ecf20Sopenharmony_cihardware è in esecuzione: per questo si può usare spin_lock(), che è un po'
2828c2ecf20Sopenharmony_cipiù veloce. L'unica eccezione è quando un altro gestore d'interruzioni
2838c2ecf20Sopenharmony_cihardware utilizza lo stesso *lock*: spin_lock_irq() impedirà a questo
2848c2ecf20Sopenharmony_cisecondo gestore di interrompere quello in esecuzione.
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ciQuesto funziona alla perfezione anche sui sistemi monoprocessore: gli spinlock
2878c2ecf20Sopenharmony_cisvaniscono e questa macro diventa semplicemente local_irq_disable()
2888c2ecf20Sopenharmony_ci(``include/asm/smp.h``), la quale impedisce a softirq/tasklet/BH d'essere
2898c2ecf20Sopenharmony_cieseguiti.
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_cispin_lock_irqsave() (``include/linux/spinlock.h``) è una variante che
2928c2ecf20Sopenharmony_cisalva lo stato delle interruzioni in una variabile, questa verrà poi passata
2938c2ecf20Sopenharmony_cia spin_unlock_irqrestore(). Questo significa che lo stesso codice
2948c2ecf20Sopenharmony_cipotrà essere utilizzato in un'interruzione hardware (dove le interruzioni sono
2958c2ecf20Sopenharmony_cigià disabilitate) e in un softirq (dove la disabilitazione delle interruzioni
2968c2ecf20Sopenharmony_ciè richiesta).
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ciDa notare che i softirq (e quindi tasklet e timer) sono eseguiti al ritorno
2998c2ecf20Sopenharmony_cida un'interruzione hardware, quindi spin_lock_irq() interrompe
3008c2ecf20Sopenharmony_cianche questi. Tenuto conto di questo si può dire che
3018c2ecf20Sopenharmony_cispin_lock_irqsave() è la funzione di sincronizzazione più generica
3028c2ecf20Sopenharmony_cie potente.
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ciSincronizzazione fra due gestori d'interruzioni hardware
3058c2ecf20Sopenharmony_ci--------------------------------------------------------
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ciCondividere dati fra due gestori di interruzione hardware è molto raro, ma se
3088c2ecf20Sopenharmony_cisuccede, dovreste usare spin_lock_irqsave(): è una specificità
3098c2ecf20Sopenharmony_cidell'architettura il fatto che tutte le interruzioni vengano interrotte
3108c2ecf20Sopenharmony_ciquando si eseguono di gestori di interruzioni.
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ciBigino della sincronizzazione
3138c2ecf20Sopenharmony_ci=============================
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ciPete Zaitcev ci offre il seguente riassunto:
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci-  Se siete in un contesto utente (una qualsiasi chiamata di sistema)
3188c2ecf20Sopenharmony_ci   e volete sincronizzarvi con altri processi, usate i mutex. Potete trattenere
3198c2ecf20Sopenharmony_ci   il mutex e dormire (``copy_from_user*(`` o ``kmalloc(x,GFP_KERNEL)``).
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci-  Altrimenti (== i dati possono essere manipolati da un'interruzione) usate
3228c2ecf20Sopenharmony_ci   spin_lock_irqsave() e spin_unlock_irqrestore().
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci-  Evitate di trattenere uno spinlock per più di 5 righe di codice incluse
3258c2ecf20Sopenharmony_ci   le chiamate a funzione (ad eccezione di quell per l'accesso come
3268c2ecf20Sopenharmony_ci   readb()).
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ciTabella dei requisiti minimi
3298c2ecf20Sopenharmony_ci----------------------------
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ciLa tabella seguente illustra i requisiti **minimi** per la sincronizzazione fra
3328c2ecf20Sopenharmony_cidiversi contesti. In alcuni casi, lo stesso contesto può essere eseguito solo
3338c2ecf20Sopenharmony_cida un processore per volta, quindi non ci sono requisiti per la
3348c2ecf20Sopenharmony_cisincronizzazione (per esempio, un thread può essere eseguito solo su un
3358c2ecf20Sopenharmony_ciprocessore alla volta, ma se deve condividere dati con un altro thread, allora
3368c2ecf20Sopenharmony_cila sincronizzazione è necessaria).
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ciRicordatevi il suggerimento qui sopra: potete sempre usare
3398c2ecf20Sopenharmony_cispin_lock_irqsave(), che è un sovrainsieme di tutte le altre funzioni
3408c2ecf20Sopenharmony_ciper spinlock.
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci============== ============= ============= ========= ========= ========= ========= ======= ======= ============== ==============
3438c2ecf20Sopenharmony_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
3448c2ecf20Sopenharmony_ci============== ============= ============= ========= ========= ========= ========= ======= ======= ============== ==============
3458c2ecf20Sopenharmony_ciIRQ Handler A  None
3468c2ecf20Sopenharmony_ciIRQ Handler B  SLIS          None
3478c2ecf20Sopenharmony_ciSoftirq A      SLI           SLI           SL
3488c2ecf20Sopenharmony_ciSoftirq B      SLI           SLI           SL        SL
3498c2ecf20Sopenharmony_ciTasklet A      SLI           SLI           SL        SL        None
3508c2ecf20Sopenharmony_ciTasklet B      SLI           SLI           SL        SL        SL        None
3518c2ecf20Sopenharmony_ciTimer A        SLI           SLI           SL        SL        SL        SL        None
3528c2ecf20Sopenharmony_ciTimer B        SLI           SLI           SL        SL        SL        SL        SL      None
3538c2ecf20Sopenharmony_ciUser Context A SLI           SLI           SLBH      SLBH      SLBH      SLBH      SLBH    SLBH    None
3548c2ecf20Sopenharmony_ciUser Context B SLI           SLI           SLBH      SLBH      SLBH      SLBH      SLBH    SLBH    MLI            None
3558c2ecf20Sopenharmony_ci============== ============= ============= ========= ========= ========= ========= ======= ======= ============== ==============
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ciTable: Tabella dei requisiti per la sincronizzazione
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci+--------+----------------------------+
3608c2ecf20Sopenharmony_ci| SLIS   | spin_lock_irqsave          |
3618c2ecf20Sopenharmony_ci+--------+----------------------------+
3628c2ecf20Sopenharmony_ci| SLI    | spin_lock_irq              |
3638c2ecf20Sopenharmony_ci+--------+----------------------------+
3648c2ecf20Sopenharmony_ci| SL     | spin_lock                  |
3658c2ecf20Sopenharmony_ci+--------+----------------------------+
3668c2ecf20Sopenharmony_ci| SLBH   | spin_lock_bh               |
3678c2ecf20Sopenharmony_ci+--------+----------------------------+
3688c2ecf20Sopenharmony_ci| MLI    | mutex_lock_interruptible   |
3698c2ecf20Sopenharmony_ci+--------+----------------------------+
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ciTable: Legenda per la tabella dei requisiti per la sincronizzazione
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ciLe funzioni *trylock*
3748c2ecf20Sopenharmony_ci=====================
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ciCi sono funzioni che provano a trattenere un *lock* solo una volta e
3778c2ecf20Sopenharmony_ciritornano immediatamente comunicato il successo od il fallimento
3788c2ecf20Sopenharmony_cidell'operazione. Posso essere usate quando non serve accedere ai dati
3798c2ecf20Sopenharmony_ciprotetti dal *lock* quando qualche altro thread lo sta già facendo
3808c2ecf20Sopenharmony_citrattenendo il *lock*. Potrete acquisire il *lock* più tardi se vi
3818c2ecf20Sopenharmony_ciserve accedere ai dati protetti da questo *lock*.
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ciLa funzione spin_trylock() non ritenta di acquisire il *lock*,
3848c2ecf20Sopenharmony_cise ci riesce al primo colpo ritorna un valore diverso da zero, altrimenti
3858c2ecf20Sopenharmony_cise fallisce ritorna 0. Questa funzione può essere utilizzata in un qualunque
3868c2ecf20Sopenharmony_cicontesto, ma come spin_lock(): dovete disabilitare i contesti che
3878c2ecf20Sopenharmony_cipotrebbero interrompervi e quindi trattenere lo spinlock.
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ciLa funzione mutex_trylock() invece di sospendere il vostro processo
3908c2ecf20Sopenharmony_ciritorna un valore diverso da zero se è possibile trattenere il lock al primo
3918c2ecf20Sopenharmony_cicolpo, altrimenti se fallisce ritorna 0. Nonostante non dorma, questa funzione
3928c2ecf20Sopenharmony_cinon può essere usata in modo sicuro in contesti di interruzione hardware o
3938c2ecf20Sopenharmony_cisoftware.
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ciEsempi più comuni
3968c2ecf20Sopenharmony_ci=================
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ciGuardiamo un semplice esempio: una memoria che associa nomi a numeri.
3998c2ecf20Sopenharmony_ciLa memoria tiene traccia di quanto spesso viene utilizzato ogni oggetto;
4008c2ecf20Sopenharmony_ciquando è piena, l'oggetto meno usato viene eliminato.
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ciTutto in contesto utente
4038c2ecf20Sopenharmony_ci------------------------
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ciNel primo esempio, supponiamo che tutte le operazioni avvengano in contesto
4068c2ecf20Sopenharmony_ciutente (in soldoni, da una chiamata di sistema), quindi possiamo dormire.
4078c2ecf20Sopenharmony_ciQuesto significa che possiamo usare i mutex per proteggere la nostra memoria
4088c2ecf20Sopenharmony_cie tutti gli oggetti che contiene. Ecco il codice::
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci    #include <linux/list.h>
4118c2ecf20Sopenharmony_ci    #include <linux/slab.h>
4128c2ecf20Sopenharmony_ci    #include <linux/string.h>
4138c2ecf20Sopenharmony_ci    #include <linux/mutex.h>
4148c2ecf20Sopenharmony_ci    #include <asm/errno.h>
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci    struct object
4178c2ecf20Sopenharmony_ci    {
4188c2ecf20Sopenharmony_ci            struct list_head list;
4198c2ecf20Sopenharmony_ci            int id;
4208c2ecf20Sopenharmony_ci            char name[32];
4218c2ecf20Sopenharmony_ci            int popularity;
4228c2ecf20Sopenharmony_ci    };
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci    /* Protects the cache, cache_num, and the objects within it */
4258c2ecf20Sopenharmony_ci    static DEFINE_MUTEX(cache_lock);
4268c2ecf20Sopenharmony_ci    static LIST_HEAD(cache);
4278c2ecf20Sopenharmony_ci    static unsigned int cache_num = 0;
4288c2ecf20Sopenharmony_ci    #define MAX_CACHE_SIZE 10
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci    /* Must be holding cache_lock */
4318c2ecf20Sopenharmony_ci    static struct object *__cache_find(int id)
4328c2ecf20Sopenharmony_ci    {
4338c2ecf20Sopenharmony_ci            struct object *i;
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci            list_for_each_entry(i, &cache, list)
4368c2ecf20Sopenharmony_ci                    if (i->id == id) {
4378c2ecf20Sopenharmony_ci                            i->popularity++;
4388c2ecf20Sopenharmony_ci                            return i;
4398c2ecf20Sopenharmony_ci                    }
4408c2ecf20Sopenharmony_ci            return NULL;
4418c2ecf20Sopenharmony_ci    }
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci    /* Must be holding cache_lock */
4448c2ecf20Sopenharmony_ci    static void __cache_delete(struct object *obj)
4458c2ecf20Sopenharmony_ci    {
4468c2ecf20Sopenharmony_ci            BUG_ON(!obj);
4478c2ecf20Sopenharmony_ci            list_del(&obj->list);
4488c2ecf20Sopenharmony_ci            kfree(obj);
4498c2ecf20Sopenharmony_ci            cache_num--;
4508c2ecf20Sopenharmony_ci    }
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci    /* Must be holding cache_lock */
4538c2ecf20Sopenharmony_ci    static void __cache_add(struct object *obj)
4548c2ecf20Sopenharmony_ci    {
4558c2ecf20Sopenharmony_ci            list_add(&obj->list, &cache);
4568c2ecf20Sopenharmony_ci            if (++cache_num > MAX_CACHE_SIZE) {
4578c2ecf20Sopenharmony_ci                    struct object *i, *outcast = NULL;
4588c2ecf20Sopenharmony_ci                    list_for_each_entry(i, &cache, list) {
4598c2ecf20Sopenharmony_ci                            if (!outcast || i->popularity < outcast->popularity)
4608c2ecf20Sopenharmony_ci                                    outcast = i;
4618c2ecf20Sopenharmony_ci                    }
4628c2ecf20Sopenharmony_ci                    __cache_delete(outcast);
4638c2ecf20Sopenharmony_ci            }
4648c2ecf20Sopenharmony_ci    }
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci    int cache_add(int id, const char *name)
4678c2ecf20Sopenharmony_ci    {
4688c2ecf20Sopenharmony_ci            struct object *obj;
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci            if ((obj = kmalloc(sizeof(*obj), GFP_KERNEL)) == NULL)
4718c2ecf20Sopenharmony_ci                    return -ENOMEM;
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci            strscpy(obj->name, name, sizeof(obj->name));
4748c2ecf20Sopenharmony_ci            obj->id = id;
4758c2ecf20Sopenharmony_ci            obj->popularity = 0;
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci            mutex_lock(&cache_lock);
4788c2ecf20Sopenharmony_ci            __cache_add(obj);
4798c2ecf20Sopenharmony_ci            mutex_unlock(&cache_lock);
4808c2ecf20Sopenharmony_ci            return 0;
4818c2ecf20Sopenharmony_ci    }
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci    void cache_delete(int id)
4848c2ecf20Sopenharmony_ci    {
4858c2ecf20Sopenharmony_ci            mutex_lock(&cache_lock);
4868c2ecf20Sopenharmony_ci            __cache_delete(__cache_find(id));
4878c2ecf20Sopenharmony_ci            mutex_unlock(&cache_lock);
4888c2ecf20Sopenharmony_ci    }
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci    int cache_find(int id, char *name)
4918c2ecf20Sopenharmony_ci    {
4928c2ecf20Sopenharmony_ci            struct object *obj;
4938c2ecf20Sopenharmony_ci            int ret = -ENOENT;
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci            mutex_lock(&cache_lock);
4968c2ecf20Sopenharmony_ci            obj = __cache_find(id);
4978c2ecf20Sopenharmony_ci            if (obj) {
4988c2ecf20Sopenharmony_ci                    ret = 0;
4998c2ecf20Sopenharmony_ci                    strcpy(name, obj->name);
5008c2ecf20Sopenharmony_ci            }
5018c2ecf20Sopenharmony_ci            mutex_unlock(&cache_lock);
5028c2ecf20Sopenharmony_ci            return ret;
5038c2ecf20Sopenharmony_ci    }
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ciDa notare che ci assicuriamo sempre di trattenere cache_lock quando
5068c2ecf20Sopenharmony_ciaggiungiamo, rimuoviamo od ispezioniamo la memoria: sia la struttura
5078c2ecf20Sopenharmony_cidella memoria che il suo contenuto sono protetti dal *lock*. Questo
5088c2ecf20Sopenharmony_cicaso è semplice dato che copiamo i dati dall'utente e non permettiamo
5098c2ecf20Sopenharmony_cimai loro di accedere direttamente agli oggetti.
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ciC'è una piccola ottimizzazione qui: nella funzione cache_add()
5128c2ecf20Sopenharmony_ciimpostiamo i campi dell'oggetto prima di acquisire il *lock*. Questo è
5138c2ecf20Sopenharmony_cisicuro perché nessun altro potrà accedervi finché non lo inseriremo
5148c2ecf20Sopenharmony_cinella memoria.
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ciAccesso dal contesto utente
5178c2ecf20Sopenharmony_ci---------------------------
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ciOra consideriamo il caso in cui cache_find() può essere invocata
5208c2ecf20Sopenharmony_cidal contesto d'interruzione: sia hardware che software. Un esempio potrebbe
5218c2ecf20Sopenharmony_ciessere un timer che elimina oggetti dalla memoria.
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ciQui di seguito troverete la modifica nel formato *patch*: le righe ``-``
5248c2ecf20Sopenharmony_cisono quelle rimosse, mentre quelle ``+`` sono quelle aggiunte.
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci::
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci    --- cache.c.usercontext 2003-12-09 13:58:54.000000000 +1100
5298c2ecf20Sopenharmony_ci    +++ cache.c.interrupt   2003-12-09 14:07:49.000000000 +1100
5308c2ecf20Sopenharmony_ci    @@ -12,7 +12,7 @@
5318c2ecf20Sopenharmony_ci             int popularity;
5328c2ecf20Sopenharmony_ci     };
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci    -static DEFINE_MUTEX(cache_lock);
5358c2ecf20Sopenharmony_ci    +static DEFINE_SPINLOCK(cache_lock);
5368c2ecf20Sopenharmony_ci     static LIST_HEAD(cache);
5378c2ecf20Sopenharmony_ci     static unsigned int cache_num = 0;
5388c2ecf20Sopenharmony_ci     #define MAX_CACHE_SIZE 10
5398c2ecf20Sopenharmony_ci    @@ -55,6 +55,7 @@
5408c2ecf20Sopenharmony_ci     int cache_add(int id, const char *name)
5418c2ecf20Sopenharmony_ci     {
5428c2ecf20Sopenharmony_ci             struct object *obj;
5438c2ecf20Sopenharmony_ci    +        unsigned long flags;
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci             if ((obj = kmalloc(sizeof(*obj), GFP_KERNEL)) == NULL)
5468c2ecf20Sopenharmony_ci                     return -ENOMEM;
5478c2ecf20Sopenharmony_ci    @@ -63,30 +64,33 @@
5488c2ecf20Sopenharmony_ci             obj->id = id;
5498c2ecf20Sopenharmony_ci             obj->popularity = 0;
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci    -        mutex_lock(&cache_lock);
5528c2ecf20Sopenharmony_ci    +        spin_lock_irqsave(&cache_lock, flags);
5538c2ecf20Sopenharmony_ci             __cache_add(obj);
5548c2ecf20Sopenharmony_ci    -        mutex_unlock(&cache_lock);
5558c2ecf20Sopenharmony_ci    +        spin_unlock_irqrestore(&cache_lock, flags);
5568c2ecf20Sopenharmony_ci             return 0;
5578c2ecf20Sopenharmony_ci     }
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci     void cache_delete(int id)
5608c2ecf20Sopenharmony_ci     {
5618c2ecf20Sopenharmony_ci    -        mutex_lock(&cache_lock);
5628c2ecf20Sopenharmony_ci    +        unsigned long flags;
5638c2ecf20Sopenharmony_ci    +
5648c2ecf20Sopenharmony_ci    +        spin_lock_irqsave(&cache_lock, flags);
5658c2ecf20Sopenharmony_ci             __cache_delete(__cache_find(id));
5668c2ecf20Sopenharmony_ci    -        mutex_unlock(&cache_lock);
5678c2ecf20Sopenharmony_ci    +        spin_unlock_irqrestore(&cache_lock, flags);
5688c2ecf20Sopenharmony_ci     }
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci     int cache_find(int id, char *name)
5718c2ecf20Sopenharmony_ci     {
5728c2ecf20Sopenharmony_ci             struct object *obj;
5738c2ecf20Sopenharmony_ci             int ret = -ENOENT;
5748c2ecf20Sopenharmony_ci    +        unsigned long flags;
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci    -        mutex_lock(&cache_lock);
5778c2ecf20Sopenharmony_ci    +        spin_lock_irqsave(&cache_lock, flags);
5788c2ecf20Sopenharmony_ci             obj = __cache_find(id);
5798c2ecf20Sopenharmony_ci             if (obj) {
5808c2ecf20Sopenharmony_ci                     ret = 0;
5818c2ecf20Sopenharmony_ci                     strcpy(name, obj->name);
5828c2ecf20Sopenharmony_ci             }
5838c2ecf20Sopenharmony_ci    -        mutex_unlock(&cache_lock);
5848c2ecf20Sopenharmony_ci    +        spin_unlock_irqrestore(&cache_lock, flags);
5858c2ecf20Sopenharmony_ci             return ret;
5868c2ecf20Sopenharmony_ci     }
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ciDa notare che spin_lock_irqsave() disabiliterà le interruzioni
5898c2ecf20Sopenharmony_cise erano attive, altrimenti non farà niente (quando siamo già in un contesto
5908c2ecf20Sopenharmony_cid'interruzione); dunque queste funzioni possono essere chiamante in
5918c2ecf20Sopenharmony_cisicurezza da qualsiasi contesto.
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ciSfortunatamente, cache_add() invoca kmalloc() con
5948c2ecf20Sopenharmony_cil'opzione ``GFP_KERNEL`` che è permessa solo in contesto utente. Ho supposto
5958c2ecf20Sopenharmony_ciche cache_add() venga chiamata dal contesto utente, altrimenti
5968c2ecf20Sopenharmony_ciquesta opzione deve diventare un parametro di cache_add().
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ciEsporre gli oggetti al di fuori del file
5998c2ecf20Sopenharmony_ci----------------------------------------
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ciSe i vostri oggetti contengono più informazioni, potrebbe non essere
6028c2ecf20Sopenharmony_cisufficiente copiare i dati avanti e indietro: per esempio, altre parti del
6038c2ecf20Sopenharmony_cicodice potrebbero avere un puntatore a questi oggetti piuttosto che cercarli
6048c2ecf20Sopenharmony_ciogni volta. Questo introduce due problemi.
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ciIl primo problema è che utilizziamo ``cache_lock`` per proteggere gli oggetti:
6078c2ecf20Sopenharmony_cidobbiamo renderlo dinamico così che il resto del codice possa usarlo. Questo
6088c2ecf20Sopenharmony_cirende la sincronizzazione più complicata dato che non avviene più in un unico
6098c2ecf20Sopenharmony_ciposto.
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ciIl secondo problema è il problema del ciclo di vita: se un'altra struttura
6128c2ecf20Sopenharmony_cimantiene un puntatore ad un oggetto, presumibilmente si aspetta che questo
6138c2ecf20Sopenharmony_cipuntatore rimanga valido. Sfortunatamente, questo è garantito solo mentre
6148c2ecf20Sopenharmony_cisi trattiene il *lock*, altrimenti qualcuno potrebbe chiamare
6158c2ecf20Sopenharmony_cicache_delete() o peggio, aggiungere un oggetto che riutilizza lo
6168c2ecf20Sopenharmony_cistesso indirizzo.
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ciDato che c'è un solo *lock*, non potete trattenerlo a vita: altrimenti
6198c2ecf20Sopenharmony_cinessun altro potrà eseguire il proprio lavoro.
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ciLa soluzione a questo problema è l'uso di un contatore di riferimenti:
6228c2ecf20Sopenharmony_cichiunque punti ad un oggetto deve incrementare il contatore, e decrementarlo
6238c2ecf20Sopenharmony_ciquando il puntatore non viene più usato. Quando il contatore raggiunge lo zero
6248c2ecf20Sopenharmony_cisignifica che non è più usato e l'oggetto può essere rimosso.
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_ciEcco il codice::
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci    --- cache.c.interrupt   2003-12-09 14:25:43.000000000 +1100
6298c2ecf20Sopenharmony_ci    +++ cache.c.refcnt  2003-12-09 14:33:05.000000000 +1100
6308c2ecf20Sopenharmony_ci    @@ -7,6 +7,7 @@
6318c2ecf20Sopenharmony_ci     struct object
6328c2ecf20Sopenharmony_ci     {
6338c2ecf20Sopenharmony_ci             struct list_head list;
6348c2ecf20Sopenharmony_ci    +        unsigned int refcnt;
6358c2ecf20Sopenharmony_ci             int id;
6368c2ecf20Sopenharmony_ci             char name[32];
6378c2ecf20Sopenharmony_ci             int popularity;
6388c2ecf20Sopenharmony_ci    @@ -17,6 +18,35 @@
6398c2ecf20Sopenharmony_ci     static unsigned int cache_num = 0;
6408c2ecf20Sopenharmony_ci     #define MAX_CACHE_SIZE 10
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci    +static void __object_put(struct object *obj)
6438c2ecf20Sopenharmony_ci    +{
6448c2ecf20Sopenharmony_ci    +        if (--obj->refcnt == 0)
6458c2ecf20Sopenharmony_ci    +                kfree(obj);
6468c2ecf20Sopenharmony_ci    +}
6478c2ecf20Sopenharmony_ci    +
6488c2ecf20Sopenharmony_ci    +static void __object_get(struct object *obj)
6498c2ecf20Sopenharmony_ci    +{
6508c2ecf20Sopenharmony_ci    +        obj->refcnt++;
6518c2ecf20Sopenharmony_ci    +}
6528c2ecf20Sopenharmony_ci    +
6538c2ecf20Sopenharmony_ci    +void object_put(struct object *obj)
6548c2ecf20Sopenharmony_ci    +{
6558c2ecf20Sopenharmony_ci    +        unsigned long flags;
6568c2ecf20Sopenharmony_ci    +
6578c2ecf20Sopenharmony_ci    +        spin_lock_irqsave(&cache_lock, flags);
6588c2ecf20Sopenharmony_ci    +        __object_put(obj);
6598c2ecf20Sopenharmony_ci    +        spin_unlock_irqrestore(&cache_lock, flags);
6608c2ecf20Sopenharmony_ci    +}
6618c2ecf20Sopenharmony_ci    +
6628c2ecf20Sopenharmony_ci    +void object_get(struct object *obj)
6638c2ecf20Sopenharmony_ci    +{
6648c2ecf20Sopenharmony_ci    +        unsigned long flags;
6658c2ecf20Sopenharmony_ci    +
6668c2ecf20Sopenharmony_ci    +        spin_lock_irqsave(&cache_lock, flags);
6678c2ecf20Sopenharmony_ci    +        __object_get(obj);
6688c2ecf20Sopenharmony_ci    +        spin_unlock_irqrestore(&cache_lock, flags);
6698c2ecf20Sopenharmony_ci    +}
6708c2ecf20Sopenharmony_ci    +
6718c2ecf20Sopenharmony_ci     /* Must be holding cache_lock */
6728c2ecf20Sopenharmony_ci     static struct object *__cache_find(int id)
6738c2ecf20Sopenharmony_ci     {
6748c2ecf20Sopenharmony_ci    @@ -35,6 +65,7 @@
6758c2ecf20Sopenharmony_ci     {
6768c2ecf20Sopenharmony_ci             BUG_ON(!obj);
6778c2ecf20Sopenharmony_ci             list_del(&obj->list);
6788c2ecf20Sopenharmony_ci    +        __object_put(obj);
6798c2ecf20Sopenharmony_ci             cache_num--;
6808c2ecf20Sopenharmony_ci     }
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci    @@ -63,6 +94,7 @@
6838c2ecf20Sopenharmony_ci             strscpy(obj->name, name, sizeof(obj->name));
6848c2ecf20Sopenharmony_ci             obj->id = id;
6858c2ecf20Sopenharmony_ci             obj->popularity = 0;
6868c2ecf20Sopenharmony_ci    +        obj->refcnt = 1; /* The cache holds a reference */
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci             spin_lock_irqsave(&cache_lock, flags);
6898c2ecf20Sopenharmony_ci             __cache_add(obj);
6908c2ecf20Sopenharmony_ci    @@ -79,18 +111,15 @@
6918c2ecf20Sopenharmony_ci             spin_unlock_irqrestore(&cache_lock, flags);
6928c2ecf20Sopenharmony_ci     }
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_ci    -int cache_find(int id, char *name)
6958c2ecf20Sopenharmony_ci    +struct object *cache_find(int id)
6968c2ecf20Sopenharmony_ci     {
6978c2ecf20Sopenharmony_ci             struct object *obj;
6988c2ecf20Sopenharmony_ci    -        int ret = -ENOENT;
6998c2ecf20Sopenharmony_ci             unsigned long flags;
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ci             spin_lock_irqsave(&cache_lock, flags);
7028c2ecf20Sopenharmony_ci             obj = __cache_find(id);
7038c2ecf20Sopenharmony_ci    -        if (obj) {
7048c2ecf20Sopenharmony_ci    -                ret = 0;
7058c2ecf20Sopenharmony_ci    -                strcpy(name, obj->name);
7068c2ecf20Sopenharmony_ci    -        }
7078c2ecf20Sopenharmony_ci    +        if (obj)
7088c2ecf20Sopenharmony_ci    +                __object_get(obj);
7098c2ecf20Sopenharmony_ci             spin_unlock_irqrestore(&cache_lock, flags);
7108c2ecf20Sopenharmony_ci    -        return ret;
7118c2ecf20Sopenharmony_ci    +        return obj;
7128c2ecf20Sopenharmony_ci     }
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ciAbbiamo incapsulato il contatore di riferimenti nelle tipiche funzioni
7158c2ecf20Sopenharmony_cidi 'get' e 'put'. Ora possiamo ritornare l'oggetto da cache_find()
7168c2ecf20Sopenharmony_cicol vantaggio che l'utente può dormire trattenendo l'oggetto (per esempio,
7178c2ecf20Sopenharmony_cicopy_to_user() per copiare il nome verso lo spazio utente).
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ciUn altro punto da notare è che ho detto che il contatore dovrebbe incrementarsi
7208c2ecf20Sopenharmony_ciper ogni puntatore ad un oggetto: quindi il contatore di riferimenti è 1
7218c2ecf20Sopenharmony_ciquando l'oggetto viene inserito nella memoria. In altre versione il framework
7228c2ecf20Sopenharmony_cinon trattiene un riferimento per se, ma diventa più complicato.
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ciUsare operazioni atomiche per il contatore di riferimenti
7258c2ecf20Sopenharmony_ci~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ciIn sostanza, :c:type:`atomic_t` viene usato come contatore di riferimenti.
7288c2ecf20Sopenharmony_ciCi sono un certo numbero di operazioni atomiche definite
7298c2ecf20Sopenharmony_ciin ``include/asm/atomic.h``: queste sono garantite come atomiche su qualsiasi
7308c2ecf20Sopenharmony_ciprocessore del sistema, quindi non sono necessari i *lock*. In questo caso è
7318c2ecf20Sopenharmony_cipiù semplice rispetto all'uso degli spinlock, benché l'uso degli spinlock
7328c2ecf20Sopenharmony_cisia più elegante per casi non banali. Le funzioni atomic_inc() e
7338c2ecf20Sopenharmony_ciatomic_dec_and_test() vengono usate al posto dei tipici operatori di
7348c2ecf20Sopenharmony_ciincremento e decremento, e i *lock* non sono più necessari per proteggere il
7358c2ecf20Sopenharmony_cicontatore stesso.
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ci::
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_ci    --- cache.c.refcnt  2003-12-09 15:00:35.000000000 +1100
7408c2ecf20Sopenharmony_ci    +++ cache.c.refcnt-atomic   2003-12-11 15:49:42.000000000 +1100
7418c2ecf20Sopenharmony_ci    @@ -7,7 +7,7 @@
7428c2ecf20Sopenharmony_ci     struct object
7438c2ecf20Sopenharmony_ci     {
7448c2ecf20Sopenharmony_ci             struct list_head list;
7458c2ecf20Sopenharmony_ci    -        unsigned int refcnt;
7468c2ecf20Sopenharmony_ci    +        atomic_t refcnt;
7478c2ecf20Sopenharmony_ci             int id;
7488c2ecf20Sopenharmony_ci             char name[32];
7498c2ecf20Sopenharmony_ci             int popularity;
7508c2ecf20Sopenharmony_ci    @@ -18,33 +18,15 @@
7518c2ecf20Sopenharmony_ci     static unsigned int cache_num = 0;
7528c2ecf20Sopenharmony_ci     #define MAX_CACHE_SIZE 10
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci    -static void __object_put(struct object *obj)
7558c2ecf20Sopenharmony_ci    -{
7568c2ecf20Sopenharmony_ci    -        if (--obj->refcnt == 0)
7578c2ecf20Sopenharmony_ci    -                kfree(obj);
7588c2ecf20Sopenharmony_ci    -}
7598c2ecf20Sopenharmony_ci    -
7608c2ecf20Sopenharmony_ci    -static void __object_get(struct object *obj)
7618c2ecf20Sopenharmony_ci    -{
7628c2ecf20Sopenharmony_ci    -        obj->refcnt++;
7638c2ecf20Sopenharmony_ci    -}
7648c2ecf20Sopenharmony_ci    -
7658c2ecf20Sopenharmony_ci     void object_put(struct object *obj)
7668c2ecf20Sopenharmony_ci     {
7678c2ecf20Sopenharmony_ci    -        unsigned long flags;
7688c2ecf20Sopenharmony_ci    -
7698c2ecf20Sopenharmony_ci    -        spin_lock_irqsave(&cache_lock, flags);
7708c2ecf20Sopenharmony_ci    -        __object_put(obj);
7718c2ecf20Sopenharmony_ci    -        spin_unlock_irqrestore(&cache_lock, flags);
7728c2ecf20Sopenharmony_ci    +        if (atomic_dec_and_test(&obj->refcnt))
7738c2ecf20Sopenharmony_ci    +                kfree(obj);
7748c2ecf20Sopenharmony_ci     }
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci     void object_get(struct object *obj)
7778c2ecf20Sopenharmony_ci     {
7788c2ecf20Sopenharmony_ci    -        unsigned long flags;
7798c2ecf20Sopenharmony_ci    -
7808c2ecf20Sopenharmony_ci    -        spin_lock_irqsave(&cache_lock, flags);
7818c2ecf20Sopenharmony_ci    -        __object_get(obj);
7828c2ecf20Sopenharmony_ci    -        spin_unlock_irqrestore(&cache_lock, flags);
7838c2ecf20Sopenharmony_ci    +        atomic_inc(&obj->refcnt);
7848c2ecf20Sopenharmony_ci     }
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_ci     /* Must be holding cache_lock */
7878c2ecf20Sopenharmony_ci    @@ -65,7 +47,7 @@
7888c2ecf20Sopenharmony_ci     {
7898c2ecf20Sopenharmony_ci             BUG_ON(!obj);
7908c2ecf20Sopenharmony_ci             list_del(&obj->list);
7918c2ecf20Sopenharmony_ci    -        __object_put(obj);
7928c2ecf20Sopenharmony_ci    +        object_put(obj);
7938c2ecf20Sopenharmony_ci             cache_num--;
7948c2ecf20Sopenharmony_ci     }
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_ci    @@ -94,7 +76,7 @@
7978c2ecf20Sopenharmony_ci             strscpy(obj->name, name, sizeof(obj->name));
7988c2ecf20Sopenharmony_ci             obj->id = id;
7998c2ecf20Sopenharmony_ci             obj->popularity = 0;
8008c2ecf20Sopenharmony_ci    -        obj->refcnt = 1; /* The cache holds a reference */
8018c2ecf20Sopenharmony_ci    +        atomic_set(&obj->refcnt, 1); /* The cache holds a reference */
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_ci             spin_lock_irqsave(&cache_lock, flags);
8048c2ecf20Sopenharmony_ci             __cache_add(obj);
8058c2ecf20Sopenharmony_ci    @@ -119,7 +101,7 @@
8068c2ecf20Sopenharmony_ci             spin_lock_irqsave(&cache_lock, flags);
8078c2ecf20Sopenharmony_ci             obj = __cache_find(id);
8088c2ecf20Sopenharmony_ci             if (obj)
8098c2ecf20Sopenharmony_ci    -                __object_get(obj);
8108c2ecf20Sopenharmony_ci    +                object_get(obj);
8118c2ecf20Sopenharmony_ci             spin_unlock_irqrestore(&cache_lock, flags);
8128c2ecf20Sopenharmony_ci             return obj;
8138c2ecf20Sopenharmony_ci     }
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_ciProteggere l'oggetto stesso
8168c2ecf20Sopenharmony_ci---------------------------
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_ciIn questo esempio, assumiamo che gli oggetti (ad eccezione del contatore
8198c2ecf20Sopenharmony_cidi riferimenti) non cambino mai dopo la loro creazione. Se vogliamo permettere
8208c2ecf20Sopenharmony_cial nome di cambiare abbiamo tre possibilità:
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_ci-  Si può togliere static da ``cache_lock`` e dire agli utenti che devono
8238c2ecf20Sopenharmony_ci   trattenere il *lock* prima di modificare il nome di un oggetto.
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_ci-  Si può fornire una funzione cache_obj_rename() che prende il
8268c2ecf20Sopenharmony_ci   *lock* e cambia il nome per conto del chiamante; si dirà poi agli utenti
8278c2ecf20Sopenharmony_ci   di usare questa funzione.
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_ci-  Si può decidere che ``cache_lock`` protegge solo la memoria stessa, ed
8308c2ecf20Sopenharmony_ci   un altro *lock* è necessario per la protezione del nome.
8318c2ecf20Sopenharmony_ci
8328c2ecf20Sopenharmony_ciTeoricamente, possiamo avere un *lock* per ogni campo e per ogni oggetto.
8338c2ecf20Sopenharmony_ciIn pratica, le varianti più comuni sono:
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_ci-  un *lock* che protegge l'infrastruttura (la lista ``cache`` di questo
8368c2ecf20Sopenharmony_ci   esempio) e gli oggetti. Questo è quello che abbiamo fatto finora.
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_ci-  un *lock* che protegge l'infrastruttura (inclusi i puntatori alla lista
8398c2ecf20Sopenharmony_ci   negli oggetti), e un *lock* nell'oggetto per proteggere il resto
8408c2ecf20Sopenharmony_ci   dell'oggetto stesso.
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_ci-  *lock* multipli per proteggere l'infrastruttura (per esempio un *lock*
8438c2ecf20Sopenharmony_ci   per ogni lista), possibilmente con un *lock* per oggetto.
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_ciQui di seguito un'implementazione con "un lock per oggetto":
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_ci::
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_ci    --- cache.c.refcnt-atomic   2003-12-11 15:50:54.000000000 +1100
8508c2ecf20Sopenharmony_ci    +++ cache.c.perobjectlock   2003-12-11 17:15:03.000000000 +1100
8518c2ecf20Sopenharmony_ci    @@ -6,11 +6,17 @@
8528c2ecf20Sopenharmony_ci
8538c2ecf20Sopenharmony_ci     struct object
8548c2ecf20Sopenharmony_ci     {
8558c2ecf20Sopenharmony_ci    +        /* These two protected by cache_lock. */
8568c2ecf20Sopenharmony_ci             struct list_head list;
8578c2ecf20Sopenharmony_ci    +        int popularity;
8588c2ecf20Sopenharmony_ci    +
8598c2ecf20Sopenharmony_ci             atomic_t refcnt;
8608c2ecf20Sopenharmony_ci    +
8618c2ecf20Sopenharmony_ci    +        /* Doesn't change once created. */
8628c2ecf20Sopenharmony_ci             int id;
8638c2ecf20Sopenharmony_ci    +
8648c2ecf20Sopenharmony_ci    +        spinlock_t lock; /* Protects the name */
8658c2ecf20Sopenharmony_ci             char name[32];
8668c2ecf20Sopenharmony_ci    -        int popularity;
8678c2ecf20Sopenharmony_ci     };
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci     static DEFINE_SPINLOCK(cache_lock);
8708c2ecf20Sopenharmony_ci    @@ -77,6 +84,7 @@
8718c2ecf20Sopenharmony_ci             obj->id = id;
8728c2ecf20Sopenharmony_ci             obj->popularity = 0;
8738c2ecf20Sopenharmony_ci             atomic_set(&obj->refcnt, 1); /* The cache holds a reference */
8748c2ecf20Sopenharmony_ci    +        spin_lock_init(&obj->lock);
8758c2ecf20Sopenharmony_ci
8768c2ecf20Sopenharmony_ci             spin_lock_irqsave(&cache_lock, flags);
8778c2ecf20Sopenharmony_ci             __cache_add(obj);
8788c2ecf20Sopenharmony_ci
8798c2ecf20Sopenharmony_ciDa notare che ho deciso che il contatore di popolarità dovesse essere
8808c2ecf20Sopenharmony_ciprotetto da ``cache_lock`` piuttosto che dal *lock* dell'oggetto; questo
8818c2ecf20Sopenharmony_ciperché è logicamente parte dell'infrastruttura (come
8828c2ecf20Sopenharmony_ci:c:type:`struct list_head <list_head>` nell'oggetto). In questo modo,
8838c2ecf20Sopenharmony_ciin __cache_add(), non ho bisogno di trattenere il *lock* di ogni
8848c2ecf20Sopenharmony_cioggetto mentre si cerca il meno popolare.
8858c2ecf20Sopenharmony_ci
8868c2ecf20Sopenharmony_ciHo anche deciso che il campo id è immutabile, quindi non ho bisogno di
8878c2ecf20Sopenharmony_citrattenere il lock dell'oggetto quando si usa __cache_find()
8888c2ecf20Sopenharmony_ciper leggere questo campo; il *lock* dell'oggetto è usato solo dal chiamante
8898c2ecf20Sopenharmony_ciche vuole leggere o scrivere il campo name.
8908c2ecf20Sopenharmony_ci
8918c2ecf20Sopenharmony_ciInoltre, da notare che ho aggiunto un commento che descrive i dati che sono
8928c2ecf20Sopenharmony_ciprotetti dal *lock*. Questo è estremamente importante in quanto descrive il
8938c2ecf20Sopenharmony_cicomportamento del codice, che altrimenti sarebbe di difficile comprensione
8948c2ecf20Sopenharmony_cileggendo solamente il codice. E come dice Alan Cox: “Lock data, not code”.
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_ciProblemi comuni
8978c2ecf20Sopenharmony_ci===============
8988c2ecf20Sopenharmony_ci
8998c2ecf20Sopenharmony_ci.. _`it_deadlock`:
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_ciStallo: semplice ed avanzato
9028c2ecf20Sopenharmony_ci----------------------------
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_ciEsiste un tipo di  baco dove un pezzo di codice tenta di trattenere uno
9058c2ecf20Sopenharmony_cispinlock due volte: questo rimarrà in attesa attiva per sempre aspettando che
9068c2ecf20Sopenharmony_ciil *lock* venga rilasciato (in Linux spinlocks, rwlocks e mutex non sono
9078c2ecf20Sopenharmony_ciricorsivi).
9088c2ecf20Sopenharmony_ciQuesto è facile da diagnosticare: non è uno di quei problemi che ti tengono
9098c2ecf20Sopenharmony_cisveglio 5 notti a parlare da solo.
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_ciUn caso un pochino più complesso; immaginate d'avere una spazio condiviso
9128c2ecf20Sopenharmony_cifra un softirq ed il contesto utente. Se usate spin_lock() per
9138c2ecf20Sopenharmony_ciproteggerlo, il contesto utente potrebbe essere interrotto da un softirq
9148c2ecf20Sopenharmony_cimentre trattiene il lock, da qui il softirq rimarrà in attesa attiva provando
9158c2ecf20Sopenharmony_ciad acquisire il *lock* già trattenuto nel contesto utente.
9168c2ecf20Sopenharmony_ci
9178c2ecf20Sopenharmony_ciQuesti casi sono chiamati stalli (*deadlock*), e come mostrato qui sopra,
9188c2ecf20Sopenharmony_cipuò succedere anche con un solo processore (Ma non sui sistemi
9198c2ecf20Sopenharmony_cimonoprocessore perché gli spinlock spariscano quando il kernel è compilato
9208c2ecf20Sopenharmony_cicon ``CONFIG_SMP``\ =n. Nonostante ciò, nel secondo caso avrete comunque
9218c2ecf20Sopenharmony_ciuna corruzione dei dati).
9228c2ecf20Sopenharmony_ci
9238c2ecf20Sopenharmony_ciQuesti casi sono facili da diagnosticare; sui sistemi multi-processore
9248c2ecf20Sopenharmony_ciil supervisione (*watchdog*) o l'opzione di compilazione ``DEBUG_SPINLOCK``
9258c2ecf20Sopenharmony_ci(``include/linux/spinlock.h``) permettono di scovare immediatamente quando
9268c2ecf20Sopenharmony_cisuccedono.
9278c2ecf20Sopenharmony_ci
9288c2ecf20Sopenharmony_ciEsiste un caso più complesso che è conosciuto come l'abbraccio della morte;
9298c2ecf20Sopenharmony_ciquesto coinvolge due o più *lock*. Diciamo che avete un vettore di hash in cui
9308c2ecf20Sopenharmony_ciogni elemento è uno spinlock a cui è associata una lista di elementi con lo
9318c2ecf20Sopenharmony_cistesso hash. In un gestore di interruzioni software, dovete modificare un
9328c2ecf20Sopenharmony_cioggetto e spostarlo su un altro hash; quindi dovrete trattenete lo spinlock
9338c2ecf20Sopenharmony_cidel vecchio hash e di quello nuovo, quindi rimuovere l'oggetto dal vecchio ed
9348c2ecf20Sopenharmony_ciinserirlo nel nuovo.
9358c2ecf20Sopenharmony_ci
9368c2ecf20Sopenharmony_ciQui abbiamo due problemi. Primo, se il vostro codice prova a spostare un
9378c2ecf20Sopenharmony_cioggetto all'interno della stessa lista, otterrete uno stallo visto che
9388c2ecf20Sopenharmony_citenterà di trattenere lo stesso *lock* due volte. Secondo, se la stessa
9398c2ecf20Sopenharmony_ciinterruzione software su un altro processore sta tentando di spostare
9408c2ecf20Sopenharmony_ciun altro oggetto nella direzione opposta, potrebbe accadere quanto segue:
9418c2ecf20Sopenharmony_ci
9428c2ecf20Sopenharmony_ci+---------------------------------+---------------------------------+
9438c2ecf20Sopenharmony_ci| CPU 1                           | CPU 2                           |
9448c2ecf20Sopenharmony_ci+=================================+=================================+
9458c2ecf20Sopenharmony_ci| Trattiene *lock* A -> OK        | Trattiene *lock* B -> OK        |
9468c2ecf20Sopenharmony_ci+---------------------------------+---------------------------------+
9478c2ecf20Sopenharmony_ci| Trattiene *lock* B -> attesa    | Trattiene *lock* A -> attesa    |
9488c2ecf20Sopenharmony_ci+---------------------------------+---------------------------------+
9498c2ecf20Sopenharmony_ci
9508c2ecf20Sopenharmony_ciTable: Conseguenze
9518c2ecf20Sopenharmony_ci
9528c2ecf20Sopenharmony_ciEntrambe i processori rimarranno in attesa attiva sul *lock* per sempre,
9538c2ecf20Sopenharmony_ciaspettando che l'altro lo rilasci. Sembra e puzza come un blocco totale.
9548c2ecf20Sopenharmony_ci
9558c2ecf20Sopenharmony_ciPrevenire gli stalli
9568c2ecf20Sopenharmony_ci--------------------
9578c2ecf20Sopenharmony_ci
9588c2ecf20Sopenharmony_ciI libri di testo vi diranno che se trattenete i *lock* sempre nello stesso
9598c2ecf20Sopenharmony_ciordine non avrete mai un simile stallo. La pratica vi dirà che questo
9608c2ecf20Sopenharmony_ciapproccio non funziona all'ingrandirsi del sistema: quando creo un nuovo
9618c2ecf20Sopenharmony_ci*lock* non ne capisco abbastanza del kernel per dire in quale dei 5000 *lock*
9628c2ecf20Sopenharmony_cisi incastrerà.
9638c2ecf20Sopenharmony_ci
9648c2ecf20Sopenharmony_ciI *lock* migliori sono quelli incapsulati: non vengono esposti nei file di
9658c2ecf20Sopenharmony_ciintestazione, e non vengono mai trattenuti fuori dallo stesso file. Potete
9668c2ecf20Sopenharmony_cirileggere questo codice e vedere che non ci sarà mai uno stallo perché
9678c2ecf20Sopenharmony_cinon tenterà mai di trattenere un altro *lock* quando lo ha già.
9688c2ecf20Sopenharmony_ciLe persone che usano il vostro codice non devono nemmeno sapere che voi
9698c2ecf20Sopenharmony_cistate usando dei *lock*.
9708c2ecf20Sopenharmony_ci
9718c2ecf20Sopenharmony_ciUn classico problema deriva dall'uso di *callback* e di *hook*: se li
9728c2ecf20Sopenharmony_cichiamate mentre trattenete un *lock*, rischiate uno stallo o un abbraccio
9738c2ecf20Sopenharmony_cidella morte (chi lo sa cosa farà una *callback*?).
9748c2ecf20Sopenharmony_ci
9758c2ecf20Sopenharmony_ciOssessiva prevenzione degli stalli
9768c2ecf20Sopenharmony_ci~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
9778c2ecf20Sopenharmony_ci
9788c2ecf20Sopenharmony_ciGli stalli sono un problema, ma non così terribile come la corruzione dei dati.
9798c2ecf20Sopenharmony_ciUn pezzo di codice trattiene un *lock* di lettura, cerca in una lista,
9808c2ecf20Sopenharmony_cifallisce nel trovare quello che vuole, quindi rilascia il *lock* di lettura,
9818c2ecf20Sopenharmony_citrattiene un *lock* di scrittura ed inserisce un oggetto; questo genere di
9828c2ecf20Sopenharmony_cicodice presenta una corsa critica.
9838c2ecf20Sopenharmony_ci
9848c2ecf20Sopenharmony_ciSe non riuscite a capire il perché, per favore state alla larga dal mio
9858c2ecf20Sopenharmony_cicodice.
9868c2ecf20Sopenharmony_ci
9878c2ecf20Sopenharmony_cicorsa fra temporizzatori: un passatempo del kernel
9888c2ecf20Sopenharmony_ci--------------------------------------------------
9898c2ecf20Sopenharmony_ci
9908c2ecf20Sopenharmony_ciI temporizzatori potrebbero avere dei problemi con le corse critiche.
9918c2ecf20Sopenharmony_ciConsiderate una collezione di oggetti (liste, hash, eccetera) dove ogni oggetto
9928c2ecf20Sopenharmony_ciha un temporizzatore che sta per distruggerlo.
9938c2ecf20Sopenharmony_ci
9948c2ecf20Sopenharmony_ciSe volete eliminare l'intera collezione (diciamo quando rimuovete un modulo),
9958c2ecf20Sopenharmony_cipotreste fare come segue::
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_ci            /* THIS CODE BAD BAD BAD BAD: IF IT WAS ANY WORSE IT WOULD USE
9988c2ecf20Sopenharmony_ci               HUNGARIAN NOTATION */
9998c2ecf20Sopenharmony_ci            spin_lock_bh(&list_lock);
10008c2ecf20Sopenharmony_ci
10018c2ecf20Sopenharmony_ci            while (list) {
10028c2ecf20Sopenharmony_ci                    struct foo *next = list->next;
10038c2ecf20Sopenharmony_ci                    del_timer(&list->timer);
10048c2ecf20Sopenharmony_ci                    kfree(list);
10058c2ecf20Sopenharmony_ci                    list = next;
10068c2ecf20Sopenharmony_ci            }
10078c2ecf20Sopenharmony_ci
10088c2ecf20Sopenharmony_ci            spin_unlock_bh(&list_lock);
10098c2ecf20Sopenharmony_ci
10108c2ecf20Sopenharmony_ciPrimo o poi, questo esploderà su un sistema multiprocessore perché un
10118c2ecf20Sopenharmony_citemporizzatore potrebbe essere già partiro prima di spin_lock_bh(),
10128c2ecf20Sopenharmony_cie prenderà il *lock* solo dopo spin_unlock_bh(), e cercherà
10138c2ecf20Sopenharmony_cidi eliminare il suo oggetto (che però è già stato eliminato).
10148c2ecf20Sopenharmony_ci
10158c2ecf20Sopenharmony_ciQuesto può essere evitato controllando il valore di ritorno di
10168c2ecf20Sopenharmony_cidel_timer(): se ritorna 1, il temporizzatore è stato già
10178c2ecf20Sopenharmony_cirimosso. Se 0, significa (in questo caso) che il temporizzatore è in
10188c2ecf20Sopenharmony_ciesecuzione, quindi possiamo fare come segue::
10198c2ecf20Sopenharmony_ci
10208c2ecf20Sopenharmony_ci            retry:
10218c2ecf20Sopenharmony_ci                    spin_lock_bh(&list_lock);
10228c2ecf20Sopenharmony_ci
10238c2ecf20Sopenharmony_ci                    while (list) {
10248c2ecf20Sopenharmony_ci                            struct foo *next = list->next;
10258c2ecf20Sopenharmony_ci                            if (!del_timer(&list->timer)) {
10268c2ecf20Sopenharmony_ci                                    /* Give timer a chance to delete this */
10278c2ecf20Sopenharmony_ci                                    spin_unlock_bh(&list_lock);
10288c2ecf20Sopenharmony_ci                                    goto retry;
10298c2ecf20Sopenharmony_ci                            }
10308c2ecf20Sopenharmony_ci                            kfree(list);
10318c2ecf20Sopenharmony_ci                            list = next;
10328c2ecf20Sopenharmony_ci                    }
10338c2ecf20Sopenharmony_ci
10348c2ecf20Sopenharmony_ci                    spin_unlock_bh(&list_lock);
10358c2ecf20Sopenharmony_ci
10368c2ecf20Sopenharmony_ciUn altro problema è l'eliminazione dei temporizzatori che si riavviano
10378c2ecf20Sopenharmony_cida soli (chiamando add_timer() alla fine della loro esecuzione).
10388c2ecf20Sopenharmony_ciDato che questo è un problema abbastanza comune con una propensione
10398c2ecf20Sopenharmony_cialle corse critiche, dovreste usare del_timer_sync()
10408c2ecf20Sopenharmony_ci(``include/linux/timer.h``) per gestire questo caso. Questa ritorna il
10418c2ecf20Sopenharmony_cinumero di volte che il temporizzatore è stato interrotto prima che
10428c2ecf20Sopenharmony_cifosse in grado di fermarlo senza che si riavviasse.
10438c2ecf20Sopenharmony_ci
10448c2ecf20Sopenharmony_ciVelocità della sincronizzazione
10458c2ecf20Sopenharmony_ci===============================
10468c2ecf20Sopenharmony_ci
10478c2ecf20Sopenharmony_ciCi sono tre cose importanti da tenere in considerazione quando si valuta
10488c2ecf20Sopenharmony_cila velocità d'esecuzione di un pezzo di codice che necessita di
10498c2ecf20Sopenharmony_cisincronizzazione. La prima è la concorrenza: quante cose rimangono in attesa
10508c2ecf20Sopenharmony_cimentre qualcuno trattiene un *lock*. La seconda è il tempo necessario per
10518c2ecf20Sopenharmony_ciacquisire (senza contese) e rilasciare un *lock*. La terza è di usare meno
10528c2ecf20Sopenharmony_ci*lock* o di più furbi. Immagino che i *lock* vengano usati regolarmente,
10538c2ecf20Sopenharmony_cialtrimenti, non sareste interessati all'efficienza.
10548c2ecf20Sopenharmony_ci
10558c2ecf20Sopenharmony_ciLa concorrenza dipende da quanto a lungo un *lock* è trattenuto: dovreste
10568c2ecf20Sopenharmony_citrattenere un *lock* solo il tempo minimo necessario ma non un istante in più.
10578c2ecf20Sopenharmony_ciNella memoria dell'esempio precedente, creiamo gli oggetti senza trattenere
10588c2ecf20Sopenharmony_ciil *lock*, poi acquisiamo il *lock* quando siamo pronti per inserirlo nella
10598c2ecf20Sopenharmony_cilista.
10608c2ecf20Sopenharmony_ci
10618c2ecf20Sopenharmony_ciIl tempo di acquisizione di un *lock* dipende da quanto danno fa
10628c2ecf20Sopenharmony_cil'operazione sulla *pipeline* (ovvero stalli della *pipeline*) e quant'è
10638c2ecf20Sopenharmony_ciprobabile che il processore corrente sia stato anche l'ultimo ad acquisire
10648c2ecf20Sopenharmony_ciil *lock* (in pratica, il *lock* è nella memoria cache del processore
10658c2ecf20Sopenharmony_cicorrente?): su sistemi multi-processore questa probabilità precipita
10668c2ecf20Sopenharmony_cirapidamente. Consideriamo un processore Intel Pentium III a 700Mhz: questo
10678c2ecf20Sopenharmony_ciesegue un'istruzione in 0.7ns, un incremento atomico richiede 58ns, acquisire
10688c2ecf20Sopenharmony_ciun *lock* che è nella memoria cache del processore richiede 160ns, e un
10698c2ecf20Sopenharmony_citrasferimento dalla memoria cache di un altro processore richiede altri
10708c2ecf20Sopenharmony_ci170/360ns (Leggetevi l'articolo di Paul McKenney's `Linux Journal RCU
10718c2ecf20Sopenharmony_ciarticle <http://www.linuxjournal.com/article.php?sid=6993>`__).
10728c2ecf20Sopenharmony_ci
10738c2ecf20Sopenharmony_ciQuesti due obiettivi sono in conflitto: trattenere un *lock* per il minor
10748c2ecf20Sopenharmony_citempo possibile potrebbe richiedere la divisione in più *lock* per diverse
10758c2ecf20Sopenharmony_ciparti (come nel nostro ultimo esempio con un *lock* per ogni oggetto),
10768c2ecf20Sopenharmony_cima questo aumenta il numero di acquisizioni di *lock*, ed il risultato
10778c2ecf20Sopenharmony_cispesso è che tutto è più lento che con un singolo *lock*. Questo è un altro
10788c2ecf20Sopenharmony_ciargomento in favore della semplicità quando si parla di sincronizzazione.
10798c2ecf20Sopenharmony_ci
10808c2ecf20Sopenharmony_ciIl terzo punto è discusso di seguito: ci sono alcune tecniche per ridurre
10818c2ecf20Sopenharmony_ciil numero di sincronizzazioni che devono essere fatte.
10828c2ecf20Sopenharmony_ci
10838c2ecf20Sopenharmony_ciRead/Write Lock Variants
10848c2ecf20Sopenharmony_ci------------------------
10858c2ecf20Sopenharmony_ci
10868c2ecf20Sopenharmony_ciSia gli spinlock che i mutex hanno una variante per la lettura/scrittura
10878c2ecf20Sopenharmony_ci(read/write): ``rwlock_t`` e :c:type:`struct rw_semaphore <rw_semaphore>`.
10888c2ecf20Sopenharmony_ciQueste dividono gli utenti in due categorie: i lettori e gli scrittori.
10898c2ecf20Sopenharmony_ciSe state solo leggendo i dati, potete acquisire il *lock* di lettura, ma
10908c2ecf20Sopenharmony_ciper scrivere avrete bisogno del *lock* di scrittura. Molti possono trattenere
10918c2ecf20Sopenharmony_ciil *lock* di lettura, ma solo uno scrittore alla volta può trattenere
10928c2ecf20Sopenharmony_ciquello di scrittura.
10938c2ecf20Sopenharmony_ci
10948c2ecf20Sopenharmony_ciSe il vostro codice si divide chiaramente in codice per lettori e codice
10958c2ecf20Sopenharmony_ciper scrittori (come nel nostro esempio), e il *lock* dei lettori viene
10968c2ecf20Sopenharmony_citrattenuto per molto tempo, allora l'uso di questo tipo di *lock* può aiutare.
10978c2ecf20Sopenharmony_ciQuesti sono leggermente più lenti rispetto alla loro versione normale, quindi
10988c2ecf20Sopenharmony_cinella pratica l'uso di ``rwlock_t`` non ne vale la pena.
10998c2ecf20Sopenharmony_ci
11008c2ecf20Sopenharmony_ciEvitare i *lock*: Read Copy Update
11018c2ecf20Sopenharmony_ci--------------------------------------------
11028c2ecf20Sopenharmony_ci
11038c2ecf20Sopenharmony_ciEsiste un metodo di sincronizzazione per letture e scritture detto
11048c2ecf20Sopenharmony_ciRead Copy Update. Con l'uso della tecnica RCU, i lettori possono scordarsi
11058c2ecf20Sopenharmony_cicompletamente di trattenere i *lock*; dato che nel nostro esempio ci
11068c2ecf20Sopenharmony_ciaspettiamo d'avere più lettore che scrittori (altrimenti questa memoria
11078c2ecf20Sopenharmony_cisarebbe uno spreco) possiamo dire che questo meccanismo permette
11088c2ecf20Sopenharmony_ciun'ottimizzazione.
11098c2ecf20Sopenharmony_ci
11108c2ecf20Sopenharmony_ciCome facciamo a sbarazzarci dei *lock* di lettura? Sbarazzarsi dei *lock* di
11118c2ecf20Sopenharmony_cilettura significa che uno scrittore potrebbe cambiare la lista sotto al naso
11128c2ecf20Sopenharmony_cidei lettori. Questo è abbastanza semplice: possiamo leggere una lista
11138c2ecf20Sopenharmony_ciconcatenata se lo scrittore aggiunge elementi alla fine e con certe
11148c2ecf20Sopenharmony_ciprecauzioni. Per esempio, aggiungendo ``new`` ad una lista concatenata
11158c2ecf20Sopenharmony_cichiamata ``list``::
11168c2ecf20Sopenharmony_ci
11178c2ecf20Sopenharmony_ci            new->next = list->next;
11188c2ecf20Sopenharmony_ci            wmb();
11198c2ecf20Sopenharmony_ci            list->next = new;
11208c2ecf20Sopenharmony_ci
11218c2ecf20Sopenharmony_ciLa funzione wmb() è una barriera di sincronizzazione delle
11228c2ecf20Sopenharmony_ciscritture. Questa garantisce che la prima operazione (impostare l'elemento
11238c2ecf20Sopenharmony_ci``next`` del nuovo elemento) venga completata e vista da tutti i processori
11248c2ecf20Sopenharmony_ciprima che venga eseguita la seconda operazione (che sarebbe quella di mettere
11258c2ecf20Sopenharmony_ciil nuovo elemento nella lista). Questo è importante perché i moderni
11268c2ecf20Sopenharmony_cicompilatori ed i moderni processori possono, entrambe, riordinare le istruzioni
11278c2ecf20Sopenharmony_cise non vengono istruiti altrimenti: vogliamo che i lettori non vedano
11288c2ecf20Sopenharmony_cicompletamente il nuovo elemento; oppure che lo vedano correttamente e quindi
11298c2ecf20Sopenharmony_ciil puntatore ``next`` deve puntare al resto della lista.
11308c2ecf20Sopenharmony_ci
11318c2ecf20Sopenharmony_ciFortunatamente, c'è una funzione che fa questa operazione sulle liste
11328c2ecf20Sopenharmony_ci:c:type:`struct list_head <list_head>`: list_add_rcu()
11338c2ecf20Sopenharmony_ci(``include/linux/list.h``).
11348c2ecf20Sopenharmony_ci
11358c2ecf20Sopenharmony_ciRimuovere un elemento dalla lista è anche più facile: sostituiamo il puntatore
11368c2ecf20Sopenharmony_cial vecchio elemento con quello del suo successore, e i lettori vedranno
11378c2ecf20Sopenharmony_cil'elemento o lo salteranno.
11388c2ecf20Sopenharmony_ci
11398c2ecf20Sopenharmony_ci::
11408c2ecf20Sopenharmony_ci
11418c2ecf20Sopenharmony_ci            list->next = old->next;
11428c2ecf20Sopenharmony_ci
11438c2ecf20Sopenharmony_ciLa funzione list_del_rcu() (``include/linux/list.h``) fa esattamente
11448c2ecf20Sopenharmony_ciquesto (la versione normale corrompe il vecchio oggetto, e non vogliamo che
11458c2ecf20Sopenharmony_ciaccada).
11468c2ecf20Sopenharmony_ci
11478c2ecf20Sopenharmony_ciAnche i lettori devono stare attenti: alcuni processori potrebbero leggere
11488c2ecf20Sopenharmony_ciattraverso il puntatore ``next`` il contenuto dell'elemento successivo
11498c2ecf20Sopenharmony_citroppo presto, ma non accorgersi che il contenuto caricato è sbagliato quando
11508c2ecf20Sopenharmony_ciil puntatore ``next`` viene modificato alla loro spalle. Ancora una volta
11518c2ecf20Sopenharmony_cic'è una funzione che viene in vostro aiuto list_for_each_entry_rcu()
11528c2ecf20Sopenharmony_ci(``include/linux/list.h``). Ovviamente, gli scrittori possono usare
11538c2ecf20Sopenharmony_cilist_for_each_entry() dato che non ci possono essere due scrittori
11548c2ecf20Sopenharmony_ciin contemporanea.
11558c2ecf20Sopenharmony_ci
11568c2ecf20Sopenharmony_ciIl nostro ultimo dilemma è il seguente: quando possiamo realmente distruggere
11578c2ecf20Sopenharmony_cil'elemento rimosso? Ricordate, un lettore potrebbe aver avuto accesso a questo
11588c2ecf20Sopenharmony_cielemento proprio ora: se eliminiamo questo elemento ed il puntatore ``next``
11598c2ecf20Sopenharmony_cicambia, il lettore salterà direttamente nella spazzatura e scoppierà. Dobbiamo
11608c2ecf20Sopenharmony_ciaspettare finché tutti i lettori che stanno attraversando la lista abbiano
11618c2ecf20Sopenharmony_cifinito. Utilizziamo call_rcu() per registrare una funzione di
11628c2ecf20Sopenharmony_cirichiamo che distrugga l'oggetto quando tutti i lettori correnti hanno
11638c2ecf20Sopenharmony_citerminato. In alternative, potrebbe essere usata la funzione
11648c2ecf20Sopenharmony_cisynchronize_rcu() che blocca l'esecuzione finché tutti i lettori
11658c2ecf20Sopenharmony_cinon terminano di ispezionare la lista.
11668c2ecf20Sopenharmony_ci
11678c2ecf20Sopenharmony_ciMa come fa l'RCU a sapere quando i lettori sono finiti? Il meccanismo è
11688c2ecf20Sopenharmony_ciil seguente: innanzi tutto i lettori accedono alla lista solo fra la coppia
11698c2ecf20Sopenharmony_circu_read_lock()/rcu_read_unlock() che disabilita la
11708c2ecf20Sopenharmony_ciprelazione così che i lettori non vengano sospesi mentre stanno leggendo
11718c2ecf20Sopenharmony_cila lista.
11728c2ecf20Sopenharmony_ci
11738c2ecf20Sopenharmony_ciPoi, l'RCU aspetta finché tutti i processori non abbiano dormito almeno
11748c2ecf20Sopenharmony_ciuna volta; a questo punto, dato che i lettori non possono dormire, possiamo
11758c2ecf20Sopenharmony_cidedurre che un qualsiasi lettore che abbia consultato la lista durante la
11768c2ecf20Sopenharmony_cirimozione abbia già terminato, quindi la *callback* viene eseguita. Il vero
11778c2ecf20Sopenharmony_cicodice RCU è un po' più ottimizzato di così, ma questa è l'idea di fondo.
11788c2ecf20Sopenharmony_ci
11798c2ecf20Sopenharmony_ci::
11808c2ecf20Sopenharmony_ci
11818c2ecf20Sopenharmony_ci    --- cache.c.perobjectlock   2003-12-11 17:15:03.000000000 +1100
11828c2ecf20Sopenharmony_ci    +++ cache.c.rcupdate    2003-12-11 17:55:14.000000000 +1100
11838c2ecf20Sopenharmony_ci    @@ -1,15 +1,18 @@
11848c2ecf20Sopenharmony_ci     #include <linux/list.h>
11858c2ecf20Sopenharmony_ci     #include <linux/slab.h>
11868c2ecf20Sopenharmony_ci     #include <linux/string.h>
11878c2ecf20Sopenharmony_ci    +#include <linux/rcupdate.h>
11888c2ecf20Sopenharmony_ci     #include <linux/mutex.h>
11898c2ecf20Sopenharmony_ci     #include <asm/errno.h>
11908c2ecf20Sopenharmony_ci
11918c2ecf20Sopenharmony_ci     struct object
11928c2ecf20Sopenharmony_ci     {
11938c2ecf20Sopenharmony_ci    -        /* These two protected by cache_lock. */
11948c2ecf20Sopenharmony_ci    +        /* This is protected by RCU */
11958c2ecf20Sopenharmony_ci             struct list_head list;
11968c2ecf20Sopenharmony_ci             int popularity;
11978c2ecf20Sopenharmony_ci
11988c2ecf20Sopenharmony_ci    +        struct rcu_head rcu;
11998c2ecf20Sopenharmony_ci    +
12008c2ecf20Sopenharmony_ci             atomic_t refcnt;
12018c2ecf20Sopenharmony_ci
12028c2ecf20Sopenharmony_ci             /* Doesn't change once created. */
12038c2ecf20Sopenharmony_ci    @@ -40,7 +43,7 @@
12048c2ecf20Sopenharmony_ci     {
12058c2ecf20Sopenharmony_ci             struct object *i;
12068c2ecf20Sopenharmony_ci
12078c2ecf20Sopenharmony_ci    -        list_for_each_entry(i, &cache, list) {
12088c2ecf20Sopenharmony_ci    +        list_for_each_entry_rcu(i, &cache, list) {
12098c2ecf20Sopenharmony_ci                     if (i->id == id) {
12108c2ecf20Sopenharmony_ci                             i->popularity++;
12118c2ecf20Sopenharmony_ci                             return i;
12128c2ecf20Sopenharmony_ci    @@ -49,19 +52,25 @@
12138c2ecf20Sopenharmony_ci             return NULL;
12148c2ecf20Sopenharmony_ci     }
12158c2ecf20Sopenharmony_ci
12168c2ecf20Sopenharmony_ci    +/* Final discard done once we know no readers are looking. */
12178c2ecf20Sopenharmony_ci    +static void cache_delete_rcu(void *arg)
12188c2ecf20Sopenharmony_ci    +{
12198c2ecf20Sopenharmony_ci    +        object_put(arg);
12208c2ecf20Sopenharmony_ci    +}
12218c2ecf20Sopenharmony_ci    +
12228c2ecf20Sopenharmony_ci     /* Must be holding cache_lock */
12238c2ecf20Sopenharmony_ci     static void __cache_delete(struct object *obj)
12248c2ecf20Sopenharmony_ci     {
12258c2ecf20Sopenharmony_ci             BUG_ON(!obj);
12268c2ecf20Sopenharmony_ci    -        list_del(&obj->list);
12278c2ecf20Sopenharmony_ci    -        object_put(obj);
12288c2ecf20Sopenharmony_ci    +        list_del_rcu(&obj->list);
12298c2ecf20Sopenharmony_ci             cache_num--;
12308c2ecf20Sopenharmony_ci    +        call_rcu(&obj->rcu, cache_delete_rcu);
12318c2ecf20Sopenharmony_ci     }
12328c2ecf20Sopenharmony_ci
12338c2ecf20Sopenharmony_ci     /* Must be holding cache_lock */
12348c2ecf20Sopenharmony_ci     static void __cache_add(struct object *obj)
12358c2ecf20Sopenharmony_ci     {
12368c2ecf20Sopenharmony_ci    -        list_add(&obj->list, &cache);
12378c2ecf20Sopenharmony_ci    +        list_add_rcu(&obj->list, &cache);
12388c2ecf20Sopenharmony_ci             if (++cache_num > MAX_CACHE_SIZE) {
12398c2ecf20Sopenharmony_ci                     struct object *i, *outcast = NULL;
12408c2ecf20Sopenharmony_ci                     list_for_each_entry(i, &cache, list) {
12418c2ecf20Sopenharmony_ci    @@ -104,12 +114,11 @@
12428c2ecf20Sopenharmony_ci     struct object *cache_find(int id)
12438c2ecf20Sopenharmony_ci     {
12448c2ecf20Sopenharmony_ci             struct object *obj;
12458c2ecf20Sopenharmony_ci    -        unsigned long flags;
12468c2ecf20Sopenharmony_ci
12478c2ecf20Sopenharmony_ci    -        spin_lock_irqsave(&cache_lock, flags);
12488c2ecf20Sopenharmony_ci    +        rcu_read_lock();
12498c2ecf20Sopenharmony_ci             obj = __cache_find(id);
12508c2ecf20Sopenharmony_ci             if (obj)
12518c2ecf20Sopenharmony_ci                     object_get(obj);
12528c2ecf20Sopenharmony_ci    -        spin_unlock_irqrestore(&cache_lock, flags);
12538c2ecf20Sopenharmony_ci    +        rcu_read_unlock();
12548c2ecf20Sopenharmony_ci             return obj;
12558c2ecf20Sopenharmony_ci     }
12568c2ecf20Sopenharmony_ci
12578c2ecf20Sopenharmony_ciDa notare che i lettori modificano il campo popularity nella funzione
12588c2ecf20Sopenharmony_ci__cache_find(), e ora non trattiene alcun *lock*. Una soluzione
12598c2ecf20Sopenharmony_cipotrebbe essere quella di rendere la variabile ``atomic_t``, ma per l'uso
12608c2ecf20Sopenharmony_ciche ne abbiamo fatto qui, non ci interessano queste corse critiche perché un
12618c2ecf20Sopenharmony_cirisultato approssimativo è comunque accettabile, quindi non l'ho cambiato.
12628c2ecf20Sopenharmony_ci
12638c2ecf20Sopenharmony_ciIl risultato è che la funzione cache_find() non ha bisogno di alcuna
12648c2ecf20Sopenharmony_cisincronizzazione con le altre funzioni, quindi è veloce su un sistema
12658c2ecf20Sopenharmony_cimulti-processore tanto quanto lo sarebbe su un sistema mono-processore.
12668c2ecf20Sopenharmony_ci
12678c2ecf20Sopenharmony_ciEsiste un'ulteriore ottimizzazione possibile: vi ricordate il codice originale
12688c2ecf20Sopenharmony_cidella nostra memoria dove non c'erano contatori di riferimenti e il chiamante
12698c2ecf20Sopenharmony_cisemplicemente tratteneva il *lock* prima di accedere ad un oggetto? Questo è
12708c2ecf20Sopenharmony_ciancora possibile: se trattenete un *lock* nessuno potrà cancellare l'oggetto,
12718c2ecf20Sopenharmony_ciquindi non avete bisogno di incrementare e decrementare il contatore di
12728c2ecf20Sopenharmony_ciriferimenti.
12738c2ecf20Sopenharmony_ci
12748c2ecf20Sopenharmony_ciOra, dato che il '*lock* di lettura' di un RCU non fa altro che disabilitare
12758c2ecf20Sopenharmony_cila prelazione, un chiamante che ha sempre la prelazione disabilitata fra le
12768c2ecf20Sopenharmony_cichiamate cache_find() e object_put() non necessita
12778c2ecf20Sopenharmony_cidi incrementare e decrementare il contatore di riferimenti. Potremmo
12788c2ecf20Sopenharmony_ciesporre la funzione __cache_find() dichiarandola non-static,
12798c2ecf20Sopenharmony_cie quel chiamante potrebbe usare direttamente questa funzione.
12808c2ecf20Sopenharmony_ci
12818c2ecf20Sopenharmony_ciIl beneficio qui sta nel fatto che il contatore di riferimenti no
12828c2ecf20Sopenharmony_civiene scritto: l'oggetto non viene alterato in alcun modo e quindi diventa
12838c2ecf20Sopenharmony_cimolto più veloce su sistemi molti-processore grazie alla loro memoria cache.
12848c2ecf20Sopenharmony_ci
12858c2ecf20Sopenharmony_ci.. _`it_per-cpu`:
12868c2ecf20Sopenharmony_ci
12878c2ecf20Sopenharmony_ciDati per processore
12888c2ecf20Sopenharmony_ci-------------------
12898c2ecf20Sopenharmony_ci
12908c2ecf20Sopenharmony_ciUn'altra tecnica comunemente usata per evitare la sincronizzazione è quella
12918c2ecf20Sopenharmony_cidi duplicare le informazioni per ogni processore. Per esempio, se volete
12928c2ecf20Sopenharmony_ciavere un contatore di qualcosa, potreste utilizzare uno spinlock ed un
12938c2ecf20Sopenharmony_cisingolo contatore. Facile e pulito.
12948c2ecf20Sopenharmony_ci
12958c2ecf20Sopenharmony_ciSe questo dovesse essere troppo lento (solitamente non lo è, ma se avete
12968c2ecf20Sopenharmony_cidimostrato che lo è devvero), potreste usare un contatore per ogni processore
12978c2ecf20Sopenharmony_cie quindi non sarebbe più necessaria la mutua esclusione. Vedere
12988c2ecf20Sopenharmony_ciDEFINE_PER_CPU(), get_cpu_var() e put_cpu_var()
12998c2ecf20Sopenharmony_ci(``include/linux/percpu.h``).
13008c2ecf20Sopenharmony_ci
13018c2ecf20Sopenharmony_ciIl tipo di dato ``local_t``, la funzione cpu_local_inc() e tutte
13028c2ecf20Sopenharmony_cile altre funzioni associate, sono di particolare utilità per semplici contatori
13038c2ecf20Sopenharmony_ciper-processore; su alcune architetture sono anche più efficienti
13048c2ecf20Sopenharmony_ci(``include/asm/local.h``).
13058c2ecf20Sopenharmony_ci
13068c2ecf20Sopenharmony_ciDa notare che non esiste un modo facile ed affidabile per ottenere il valore
13078c2ecf20Sopenharmony_cidi un simile contatore senza introdurre altri *lock*. In alcuni casi questo
13088c2ecf20Sopenharmony_cinon è un problema.
13098c2ecf20Sopenharmony_ci
13108c2ecf20Sopenharmony_ciDati che sono usati prevalentemente dai gestori d'interruzioni
13118c2ecf20Sopenharmony_ci--------------------------------------------------------------
13128c2ecf20Sopenharmony_ci
13138c2ecf20Sopenharmony_ciSe i dati vengono utilizzati sempre dallo stesso gestore d'interruzioni,
13148c2ecf20Sopenharmony_ciallora i *lock* non vi servono per niente: il kernel già vi garantisce che
13158c2ecf20Sopenharmony_ciil gestore d'interruzione non verrà eseguito in contemporanea su diversi
13168c2ecf20Sopenharmony_ciprocessori.
13178c2ecf20Sopenharmony_ci
13188c2ecf20Sopenharmony_ciManfred Spraul fa notare che potreste comunque comportarvi così anche
13198c2ecf20Sopenharmony_cise i dati vengono occasionalmente utilizzati da un contesto utente o
13208c2ecf20Sopenharmony_cida un'interruzione software. Il gestore d'interruzione non utilizza alcun
13218c2ecf20Sopenharmony_ci*lock*, e tutti gli altri accessi verranno fatti così::
13228c2ecf20Sopenharmony_ci
13238c2ecf20Sopenharmony_ci        spin_lock(&lock);
13248c2ecf20Sopenharmony_ci        disable_irq(irq);
13258c2ecf20Sopenharmony_ci        ...
13268c2ecf20Sopenharmony_ci        enable_irq(irq);
13278c2ecf20Sopenharmony_ci        spin_unlock(&lock);
13288c2ecf20Sopenharmony_ci
13298c2ecf20Sopenharmony_ciLa funzione disable_irq() impedisce al gestore d'interruzioni
13308c2ecf20Sopenharmony_cid'essere eseguito (e aspetta che finisca nel caso fosse in esecuzione su
13318c2ecf20Sopenharmony_ciun altro processore). Lo spinlock, invece, previene accessi simultanei.
13328c2ecf20Sopenharmony_ciNaturalmente, questo è più lento della semplice chiamata
13338c2ecf20Sopenharmony_cispin_lock_irq(), quindi ha senso solo se questo genere di accesso
13348c2ecf20Sopenharmony_ciè estremamente raro.
13358c2ecf20Sopenharmony_ci
13368c2ecf20Sopenharmony_ci.. _`it_sleeping-things`:
13378c2ecf20Sopenharmony_ci
13388c2ecf20Sopenharmony_ciQuali funzioni possono essere chiamate in modo sicuro dalle interruzioni?
13398c2ecf20Sopenharmony_ci=========================================================================
13408c2ecf20Sopenharmony_ci
13418c2ecf20Sopenharmony_ciMolte funzioni del kernel dormono (in sostanza, chiamano schedule())
13428c2ecf20Sopenharmony_cidirettamente od indirettamente: non potete chiamarle se trattenere uno
13438c2ecf20Sopenharmony_cispinlock o avete la prelazione disabilitata, mai. Questo significa che
13448c2ecf20Sopenharmony_cidovete necessariamente essere nel contesto utente: chiamarle da un
13458c2ecf20Sopenharmony_cicontesto d'interruzione è illegale.
13468c2ecf20Sopenharmony_ci
13478c2ecf20Sopenharmony_ciAlcune funzioni che dormono
13488c2ecf20Sopenharmony_ci---------------------------
13498c2ecf20Sopenharmony_ci
13508c2ecf20Sopenharmony_ciLe più comuni sono elencate qui di seguito, ma solitamente dovete leggere
13518c2ecf20Sopenharmony_ciil codice per scoprire se altre chiamate sono sicure. Se chiunque altro
13528c2ecf20Sopenharmony_cile chiami dorme, allora dovreste poter dormire anche voi. In particolar
13538c2ecf20Sopenharmony_cimodo, le funzioni di registrazione e deregistrazione solitamente si
13548c2ecf20Sopenharmony_ciaspettano d'essere chiamante da un contesto utente e quindi che possono
13558c2ecf20Sopenharmony_cidormire.
13568c2ecf20Sopenharmony_ci
13578c2ecf20Sopenharmony_ci-  Accessi allo spazio utente:
13588c2ecf20Sopenharmony_ci
13598c2ecf20Sopenharmony_ci   -  copy_from_user()
13608c2ecf20Sopenharmony_ci
13618c2ecf20Sopenharmony_ci   -  copy_to_user()
13628c2ecf20Sopenharmony_ci
13638c2ecf20Sopenharmony_ci   -  get_user()
13648c2ecf20Sopenharmony_ci
13658c2ecf20Sopenharmony_ci   -  put_user()
13668c2ecf20Sopenharmony_ci
13678c2ecf20Sopenharmony_ci-  kmalloc(GFP_KERNEL) <kmalloc>`
13688c2ecf20Sopenharmony_ci
13698c2ecf20Sopenharmony_ci-  mutex_lock_interruptible() and
13708c2ecf20Sopenharmony_ci   mutex_lock()
13718c2ecf20Sopenharmony_ci
13728c2ecf20Sopenharmony_ci   C'è anche mutex_trylock() che però non dorme.
13738c2ecf20Sopenharmony_ci   Comunque, non deve essere usata in un contesto d'interruzione dato
13748c2ecf20Sopenharmony_ci   che la sua implementazione non è sicura in quel contesto.
13758c2ecf20Sopenharmony_ci   Anche mutex_unlock() non dorme mai. Non può comunque essere
13768c2ecf20Sopenharmony_ci   usata in un contesto d'interruzione perché un mutex deve essere rilasciato
13778c2ecf20Sopenharmony_ci   dallo stesso processo che l'ha acquisito.
13788c2ecf20Sopenharmony_ci
13798c2ecf20Sopenharmony_ciAlcune funzioni che non dormono
13808c2ecf20Sopenharmony_ci-------------------------------
13818c2ecf20Sopenharmony_ci
13828c2ecf20Sopenharmony_ciAlcune funzioni possono essere chiamate tranquillamente da qualsiasi
13838c2ecf20Sopenharmony_cicontesto, o trattenendo un qualsiasi *lock*.
13848c2ecf20Sopenharmony_ci
13858c2ecf20Sopenharmony_ci-  printk()
13868c2ecf20Sopenharmony_ci
13878c2ecf20Sopenharmony_ci-  kfree()
13888c2ecf20Sopenharmony_ci
13898c2ecf20Sopenharmony_ci-  add_timer() e del_timer()
13908c2ecf20Sopenharmony_ci
13918c2ecf20Sopenharmony_ciRiferimento per l'API dei Mutex
13928c2ecf20Sopenharmony_ci===============================
13938c2ecf20Sopenharmony_ci
13948c2ecf20Sopenharmony_ci.. kernel-doc:: include/linux/mutex.h
13958c2ecf20Sopenharmony_ci   :internal:
13968c2ecf20Sopenharmony_ci
13978c2ecf20Sopenharmony_ci.. kernel-doc:: kernel/locking/mutex.c
13988c2ecf20Sopenharmony_ci   :export:
13998c2ecf20Sopenharmony_ci
14008c2ecf20Sopenharmony_ciRiferimento per l'API dei Futex
14018c2ecf20Sopenharmony_ci===============================
14028c2ecf20Sopenharmony_ci
14038c2ecf20Sopenharmony_ci.. kernel-doc:: kernel/futex/core.c
14048c2ecf20Sopenharmony_ci   :internal:
14058c2ecf20Sopenharmony_ci
14068c2ecf20Sopenharmony_ciApprofondimenti
14078c2ecf20Sopenharmony_ci===============
14088c2ecf20Sopenharmony_ci
14098c2ecf20Sopenharmony_ci-  ``Documentation/locking/spinlocks.rst``: la guida di Linus Torvalds agli
14108c2ecf20Sopenharmony_ci   spinlock del kernel.
14118c2ecf20Sopenharmony_ci
14128c2ecf20Sopenharmony_ci-  Unix Systems for Modern Architectures: Symmetric Multiprocessing and
14138c2ecf20Sopenharmony_ci   Caching for Kernel Programmers.
14148c2ecf20Sopenharmony_ci
14158c2ecf20Sopenharmony_ci   L'introduzione alla sincronizzazione a livello di kernel di Curt Schimmel
14168c2ecf20Sopenharmony_ci   è davvero ottima (non è scritta per Linux, ma approssimativamente si adatta
14178c2ecf20Sopenharmony_ci   a tutte le situazioni). Il libro è costoso, ma vale ogni singolo spicciolo
14188c2ecf20Sopenharmony_ci   per capire la sincronizzazione nei sistemi multi-processore.
14198c2ecf20Sopenharmony_ci   [ISBN: 0201633388]
14208c2ecf20Sopenharmony_ci
14218c2ecf20Sopenharmony_ciRingraziamenti
14228c2ecf20Sopenharmony_ci==============
14238c2ecf20Sopenharmony_ci
14248c2ecf20Sopenharmony_ciGrazie a Telsa Gwynne per aver formattato questa guida in DocBook, averla
14258c2ecf20Sopenharmony_cipulita e aggiunto un po' di stile.
14268c2ecf20Sopenharmony_ci
14278c2ecf20Sopenharmony_ciGrazie a Martin Pool, Philipp Rumpf, Stephen Rothwell, Paul Mackerras,
14288c2ecf20Sopenharmony_ciRuedi Aschwanden, Alan Cox, Manfred Spraul, Tim Waugh, Pete Zaitcev,
14298c2ecf20Sopenharmony_ciJames Morris, Robert Love, Paul McKenney, John Ashby per aver revisionato,
14308c2ecf20Sopenharmony_cicorretto, maledetto e commentato.
14318c2ecf20Sopenharmony_ci
14328c2ecf20Sopenharmony_ciGrazie alla congrega per non aver avuto alcuna influenza su questo documento.
14338c2ecf20Sopenharmony_ci
14348c2ecf20Sopenharmony_ciGlossario
14358c2ecf20Sopenharmony_ci=========
14368c2ecf20Sopenharmony_ci
14378c2ecf20Sopenharmony_ciprelazione
14388c2ecf20Sopenharmony_ci  Prima del kernel 2.5, o quando ``CONFIG_PREEMPT`` non è impostato, i processi
14398c2ecf20Sopenharmony_ci  in contesto utente non si avvicendano nell'esecuzione (in pratica, il
14408c2ecf20Sopenharmony_ci  processo userà il processore fino al proprio termine, a meno che non ci siano
14418c2ecf20Sopenharmony_ci  delle interruzioni). Con l'aggiunta di ``CONFIG_PREEMPT`` nella versione
14428c2ecf20Sopenharmony_ci  2.5.4 questo è cambiato: quando si è in contesto utente, processi con una
14438c2ecf20Sopenharmony_ci  priorità maggiore possono subentrare nell'esecuzione: gli spinlock furono
14448c2ecf20Sopenharmony_ci  cambiati per disabilitare la prelazioni, anche su sistemi monoprocessore.
14458c2ecf20Sopenharmony_ci
14468c2ecf20Sopenharmony_cibh
14478c2ecf20Sopenharmony_ci  Bottom Half: per ragioni storiche, le funzioni che contengono '_bh' nel
14488c2ecf20Sopenharmony_ci  loro nome ora si riferiscono a qualsiasi interruzione software; per esempio,
14498c2ecf20Sopenharmony_ci  spin_lock_bh() blocca qualsiasi interuzione software sul processore
14508c2ecf20Sopenharmony_ci  corrente. I *Bottom Halves* sono deprecati, e probabilmente verranno
14518c2ecf20Sopenharmony_ci  sostituiti dai tasklet. In un dato momento potrà esserci solo un
14528c2ecf20Sopenharmony_ci  *bottom half* in esecuzione.
14538c2ecf20Sopenharmony_ci
14548c2ecf20Sopenharmony_cicontesto d'interruzione
14558c2ecf20Sopenharmony_ci  Non è il contesto utente: qui si processano le interruzioni hardware e
14568c2ecf20Sopenharmony_ci  software. La macro in_interrupt() ritorna vero.
14578c2ecf20Sopenharmony_ci
14588c2ecf20Sopenharmony_cicontesto utente
14598c2ecf20Sopenharmony_ci  Il kernel che esegue qualcosa per conto di un particolare processo (per
14608c2ecf20Sopenharmony_ci  esempio una chiamata di sistema) o di un thread del kernel. Potete
14618c2ecf20Sopenharmony_ci  identificare il processo con la macro ``current``. Da non confondere
14628c2ecf20Sopenharmony_ci  con lo spazio utente. Può essere interrotto sia da interruzioni software
14638c2ecf20Sopenharmony_ci  che hardware.
14648c2ecf20Sopenharmony_ci
14658c2ecf20Sopenharmony_ciinterruzione hardware
14668c2ecf20Sopenharmony_ci  Richiesta di interruzione hardware. in_irq() ritorna vero in un
14678c2ecf20Sopenharmony_ci  gestore d'interruzioni hardware.
14688c2ecf20Sopenharmony_ci
14698c2ecf20Sopenharmony_ciinterruzione software / softirq
14708c2ecf20Sopenharmony_ci  Gestore di interruzioni software: in_irq() ritorna falso;
14718c2ecf20Sopenharmony_ci  in_softirq() ritorna vero. I tasklet e le softirq sono entrambi
14728c2ecf20Sopenharmony_ci  considerati 'interruzioni software'.
14738c2ecf20Sopenharmony_ci
14748c2ecf20Sopenharmony_ci  In soldoni, un softirq è uno delle 32 interruzioni software che possono
14758c2ecf20Sopenharmony_ci  essere eseguite su più processori in contemporanea. A volte si usa per
14768c2ecf20Sopenharmony_ci  riferirsi anche ai tasklet (in pratica tutte le interruzioni software).
14778c2ecf20Sopenharmony_ci
14788c2ecf20Sopenharmony_cimonoprocessore / UP
14798c2ecf20Sopenharmony_ci  (Uni-Processor) un solo processore, ovvero non è SMP. (``CONFIG_SMP=n``).
14808c2ecf20Sopenharmony_ci
14818c2ecf20Sopenharmony_cimulti-processore / SMP
14828c2ecf20Sopenharmony_ci  (Symmetric Multi-Processor) kernel compilati per sistemi multi-processore
14838c2ecf20Sopenharmony_ci  (``CONFIG_SMP=y``).
14848c2ecf20Sopenharmony_ci
14858c2ecf20Sopenharmony_cispazio utente
14868c2ecf20Sopenharmony_ci  Un processo che esegue il proprio codice fuori dal kernel.
14878c2ecf20Sopenharmony_ci
14888c2ecf20Sopenharmony_citasklet
14898c2ecf20Sopenharmony_ci  Un'interruzione software registrabile dinamicamente che ha la garanzia
14908c2ecf20Sopenharmony_ci  d'essere eseguita solo su un processore alla volta.
14918c2ecf20Sopenharmony_ci
14928c2ecf20Sopenharmony_citimer
14938c2ecf20Sopenharmony_ci  Un'interruzione software registrabile dinamicamente che viene eseguita
14948c2ecf20Sopenharmony_ci  (circa) in un determinato momento. Quando è in esecuzione è come un tasklet
14958c2ecf20Sopenharmony_ci  (infatti, sono chiamati da ``TIMER_SOFTIRQ``).
1496