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