.. | .. |
---|
1 | 1 | .. include:: ../disclaimer-ita.rst |
---|
2 | 2 | |
---|
| 3 | +.. c:namespace:: it_IT |
---|
| 4 | + |
---|
3 | 5 | :Original: :ref:`Documentation/kernel-hacking/locking.rst <kernel_hacking_lock>` |
---|
4 | 6 | :Translator: Federico Vaga <federico.vaga@vaga.pv.it> |
---|
5 | 7 | |
---|
.. | .. |
---|
159 | 161 | Se avete una struttura dati che verrà utilizzata solo dal contesto utente, |
---|
160 | 162 | allora, per proteggerla, potete utilizzare un semplice mutex |
---|
161 | 163 | (``include/linux/mutex.h``). Questo è il caso più semplice: inizializzate il |
---|
162 | | -mutex; invocate :c:func:`mutex_lock_interruptible()` per trattenerlo e |
---|
163 | | -:c:func:`mutex_unlock()` per rilasciarlo. C'è anche :c:func:`mutex_lock()` |
---|
| 164 | +mutex; invocate mutex_lock_interruptible() per trattenerlo e |
---|
| 165 | +mutex_unlock() per rilasciarlo. C'è anche mutex_lock() |
---|
164 | 166 | ma questa dovrebbe essere evitata perché non ritorna in caso di segnali. |
---|
165 | 167 | |
---|
166 | 168 | Per esempio: ``net/netfilter/nf_sockopt.c`` permette la registrazione |
---|
167 | | -di nuove chiamate per :c:func:`setsockopt()` e :c:func:`getsockopt()` |
---|
168 | | -usando la funzione :c:func:`nf_register_sockopt()`. La registrazione e |
---|
| 169 | +di nuove chiamate per setsockopt() e getsockopt() |
---|
| 170 | +usando la funzione nf_register_sockopt(). La registrazione e |
---|
169 | 171 | la rimozione vengono eseguite solamente quando il modulo viene caricato |
---|
170 | 172 | o scaricato (e durante l'avvio del sistema, qui non abbiamo concorrenza), |
---|
171 | 173 | e la lista delle funzioni registrate viene consultata solamente quando |
---|
172 | | -:c:func:`setsockopt()` o :c:func:`getsockopt()` sono sconosciute al sistema. |
---|
| 174 | +setsockopt() o getsockopt() sono sconosciute al sistema. |
---|
173 | 175 | In questo caso ``nf_sockopt_mutex`` è perfetto allo scopo, in particolar modo |
---|
174 | 176 | visto che setsockopt e getsockopt potrebbero dormire. |
---|
175 | 177 | |
---|
.. | .. |
---|
179 | 181 | Se un softirq condivide dati col contesto utente, avete due problemi. |
---|
180 | 182 | Primo, il contesto utente corrente potrebbe essere interroto da un softirq, |
---|
181 | 183 | e secondo, la sezione critica potrebbe essere eseguita da un altro |
---|
182 | | -processore. Questo è quando :c:func:`spin_lock_bh()` |
---|
| 184 | +processore. Questo è quando spin_lock_bh() |
---|
183 | 185 | (``include/linux/spinlock.h``) viene utilizzato. Questo disabilita i softirq |
---|
184 | | -sul processore e trattiene il *lock*. Invece, :c:func:`spin_unlock_bh()` fa |
---|
| 186 | +sul processore e trattiene il *lock*. Invece, spin_unlock_bh() fa |
---|
185 | 187 | l'opposto. (Il suffisso '_bh' è un residuo storico che fa riferimento al |
---|
186 | 188 | "Bottom Halves", il vecchio nome delle interruzioni software. In un mondo |
---|
187 | 189 | perfetto questa funzione si chiamerebbe 'spin_lock_softirq()'). |
---|
188 | 190 | |
---|
189 | | -Da notare che in questo caso potete utilizzare anche :c:func:`spin_lock_irq()` |
---|
190 | | -o :c:func:`spin_lock_irqsave()`, queste fermano anche le interruzioni hardware: |
---|
| 191 | +Da notare che in questo caso potete utilizzare anche spin_lock_irq() |
---|
| 192 | +o spin_lock_irqsave(), queste fermano anche le interruzioni hardware: |
---|
191 | 193 | vedere :ref:`Contesto di interruzione hardware <it_hardirq-context>`. |
---|
192 | 194 | |
---|
193 | 195 | Questo funziona alla perfezione anche sui sistemi monoprocessore: gli spinlock |
---|
194 | | -svaniscono e questa macro diventa semplicemente :c:func:`local_bh_disable()` |
---|
| 196 | +svaniscono e questa macro diventa semplicemente local_bh_disable() |
---|
195 | 197 | (``include/linux/interrupt.h``), la quale impedisce ai softirq d'essere |
---|
196 | 198 | eseguiti. |
---|
197 | 199 | |
---|
.. | .. |
---|
224 | 226 | ~~~~~~~~~~~~~~~~~~~~~~~~ |
---|
225 | 227 | |
---|
226 | 228 | Se un altro tasklet/timer vuole condividere dati col vostro tasklet o timer, |
---|
227 | | -allora avrete bisogno entrambe di :c:func:`spin_lock()` e |
---|
228 | | -:c:func:`spin_unlock()`. Qui :c:func:`spin_lock_bh()` è inutile, siete già |
---|
| 229 | +allora avrete bisogno entrambe di spin_lock() e |
---|
| 230 | +spin_unlock(). Qui spin_lock_bh() è inutile, siete già |
---|
229 | 231 | in un tasklet ed avete la garanzia che nessun altro verrà eseguito sullo |
---|
230 | 232 | stesso processore. |
---|
231 | 233 | |
---|
.. | .. |
---|
243 | 245 | fino a questo punto nell'uso dei softirq, probabilmente tenete alla scalabilità |
---|
244 | 246 | delle prestazioni abbastanza da giustificarne la complessità aggiuntiva. |
---|
245 | 247 | |
---|
246 | | -Dovete utilizzare :c:func:`spin_lock()` e :c:func:`spin_unlock()` per |
---|
| 248 | +Dovete utilizzare spin_lock() e spin_unlock() per |
---|
247 | 249 | proteggere i dati condivisi. |
---|
248 | 250 | |
---|
249 | 251 | Diversi Softirqs |
---|
250 | 252 | ~~~~~~~~~~~~~~~~ |
---|
251 | 253 | |
---|
252 | | -Dovete utilizzare :c:func:`spin_lock()` e :c:func:`spin_unlock()` per |
---|
| 254 | +Dovete utilizzare spin_lock() e spin_unlock() per |
---|
253 | 255 | proteggere i dati condivisi, che siano timer, tasklet, diversi softirq o |
---|
254 | 256 | lo stesso o altri softirq: uno qualsiasi di essi potrebbe essere in esecuzione |
---|
255 | 257 | su un diverso processore. |
---|
.. | .. |
---|
270 | 272 | avrete due preoccupazioni. Primo, il softirq può essere interrotto da |
---|
271 | 273 | un'interruzione hardware, e secondo, la sezione critica potrebbe essere |
---|
272 | 274 | eseguita da un'interruzione hardware su un processore diverso. Questo è il caso |
---|
273 | | -dove :c:func:`spin_lock_irq()` viene utilizzato. Disabilita le interruzioni |
---|
274 | | -sul processore che l'esegue, poi trattiene il lock. :c:func:`spin_unlock_irq()` |
---|
| 275 | +dove spin_lock_irq() viene utilizzato. Disabilita le interruzioni |
---|
| 276 | +sul processore che l'esegue, poi trattiene il lock. spin_unlock_irq() |
---|
275 | 277 | fa l'opposto. |
---|
276 | 278 | |
---|
277 | | -Il gestore d'interruzione hardware non usa :c:func:`spin_lock_irq()` perché |
---|
278 | | -i softirq non possono essere eseguiti quando il gestore d'interruzione hardware |
---|
279 | | -è in esecuzione: per questo si può usare :c:func:`spin_lock()`, che è un po' |
---|
| 279 | +Il gestore d'interruzione hardware non ha bisogno di usare spin_lock_irq() |
---|
| 280 | +perché i softirq non possono essere eseguiti quando il gestore d'interruzione |
---|
| 281 | +hardware è in esecuzione: per questo si può usare spin_lock(), che è un po' |
---|
280 | 282 | più veloce. L'unica eccezione è quando un altro gestore d'interruzioni |
---|
281 | | -hardware utilizza lo stesso *lock*: :c:func:`spin_lock_irq()` impedirà a questo |
---|
| 283 | +hardware utilizza lo stesso *lock*: spin_lock_irq() impedirà a questo |
---|
282 | 284 | secondo gestore di interrompere quello in esecuzione. |
---|
283 | 285 | |
---|
284 | 286 | Questo funziona alla perfezione anche sui sistemi monoprocessore: gli spinlock |
---|
285 | | -svaniscono e questa macro diventa semplicemente :c:func:`local_irq_disable()` |
---|
| 287 | +svaniscono e questa macro diventa semplicemente local_irq_disable() |
---|
286 | 288 | (``include/asm/smp.h``), la quale impedisce a softirq/tasklet/BH d'essere |
---|
287 | 289 | eseguiti. |
---|
288 | 290 | |
---|
289 | | -:c:func:`spin_lock_irqsave()` (``include/linux/spinlock.h``) è una variante che |
---|
| 291 | +spin_lock_irqsave() (``include/linux/spinlock.h``) è una variante che |
---|
290 | 292 | salva lo stato delle interruzioni in una variabile, questa verrà poi passata |
---|
291 | | -a :c:func:`spin_unlock_irqrestore()`. Questo significa che lo stesso codice |
---|
| 293 | +a spin_unlock_irqrestore(). Questo significa che lo stesso codice |
---|
292 | 294 | potrà essere utilizzato in un'interruzione hardware (dove le interruzioni sono |
---|
293 | 295 | già disabilitate) e in un softirq (dove la disabilitazione delle interruzioni |
---|
294 | 296 | è richiesta). |
---|
295 | 297 | |
---|
296 | 298 | Da notare che i softirq (e quindi tasklet e timer) sono eseguiti al ritorno |
---|
297 | | -da un'interruzione hardware, quindi :c:func:`spin_lock_irq()` interrompe |
---|
| 299 | +da un'interruzione hardware, quindi spin_lock_irq() interrompe |
---|
298 | 300 | anche questi. Tenuto conto di questo si può dire che |
---|
299 | | -:c:func:`spin_lock_irqsave()` è la funzione di sincronizzazione più generica |
---|
| 301 | +spin_lock_irqsave() è la funzione di sincronizzazione più generica |
---|
300 | 302 | e potente. |
---|
301 | 303 | |
---|
302 | 304 | Sincronizzazione fra due gestori d'interruzioni hardware |
---|
303 | 305 | -------------------------------------------------------- |
---|
304 | 306 | |
---|
305 | 307 | Condividere dati fra due gestori di interruzione hardware è molto raro, ma se |
---|
306 | | -succede, dovreste usare :c:func:`spin_lock_irqsave()`: è una specificità |
---|
| 308 | +succede, dovreste usare spin_lock_irqsave(): è una specificità |
---|
307 | 309 | dell'architettura il fatto che tutte le interruzioni vengano interrotte |
---|
308 | 310 | quando si eseguono di gestori di interruzioni. |
---|
309 | 311 | |
---|
.. | .. |
---|
317 | 319 | il mutex e dormire (``copy_from_user*(`` o ``kmalloc(x,GFP_KERNEL)``). |
---|
318 | 320 | |
---|
319 | 321 | - Altrimenti (== i dati possono essere manipolati da un'interruzione) usate |
---|
320 | | - :c:func:`spin_lock_irqsave()` e :c:func:`spin_unlock_irqrestore()`. |
---|
| 322 | + spin_lock_irqsave() e spin_unlock_irqrestore(). |
---|
321 | 323 | |
---|
322 | 324 | - Evitate di trattenere uno spinlock per più di 5 righe di codice incluse |
---|
323 | 325 | le chiamate a funzione (ad eccezione di quell per l'accesso come |
---|
324 | | - :c:func:`readb()`). |
---|
| 326 | + readb()). |
---|
325 | 327 | |
---|
326 | 328 | Tabella dei requisiti minimi |
---|
327 | 329 | ---------------------------- |
---|
.. | .. |
---|
334 | 336 | la sincronizzazione è necessaria). |
---|
335 | 337 | |
---|
336 | 338 | Ricordatevi il suggerimento qui sopra: potete sempre usare |
---|
337 | | -:c:func:`spin_lock_irqsave()`, che è un sovrainsieme di tutte le altre funzioni |
---|
| 339 | +spin_lock_irqsave(), che è un sovrainsieme di tutte le altre funzioni |
---|
338 | 340 | per spinlock. |
---|
339 | 341 | |
---|
340 | 342 | ============== ============= ============= ========= ========= ========= ========= ======= ======= ============== ============== |
---|
.. | .. |
---|
378 | 380 | trattenendo il *lock*. Potrete acquisire il *lock* più tardi se vi |
---|
379 | 381 | serve accedere ai dati protetti da questo *lock*. |
---|
380 | 382 | |
---|
381 | | -La funzione :c:func:`spin_trylock()` non ritenta di acquisire il *lock*, |
---|
| 383 | +La funzione spin_trylock() non ritenta di acquisire il *lock*, |
---|
382 | 384 | se ci riesce al primo colpo ritorna un valore diverso da zero, altrimenti |
---|
383 | 385 | se fallisce ritorna 0. Questa funzione può essere utilizzata in un qualunque |
---|
384 | | -contesto, ma come :c:func:`spin_lock()`: dovete disabilitare i contesti che |
---|
| 386 | +contesto, ma come spin_lock(): dovete disabilitare i contesti che |
---|
385 | 387 | potrebbero interrompervi e quindi trattenere lo spinlock. |
---|
386 | 388 | |
---|
387 | | -La funzione :c:func:`mutex_trylock()` invece di sospendere il vostro processo |
---|
| 389 | +La funzione mutex_trylock() invece di sospendere il vostro processo |
---|
388 | 390 | ritorna un valore diverso da zero se è possibile trattenere il lock al primo |
---|
389 | 391 | colpo, altrimenti se fallisce ritorna 0. Nonostante non dorma, questa funzione |
---|
390 | 392 | non può essere usata in modo sicuro in contesti di interruzione hardware o |
---|
.. | .. |
---|
468 | 470 | if ((obj = kmalloc(sizeof(*obj), GFP_KERNEL)) == NULL) |
---|
469 | 471 | return -ENOMEM; |
---|
470 | 472 | |
---|
471 | | - strlcpy(obj->name, name, sizeof(obj->name)); |
---|
| 473 | + strscpy(obj->name, name, sizeof(obj->name)); |
---|
472 | 474 | obj->id = id; |
---|
473 | 475 | obj->popularity = 0; |
---|
474 | 476 | |
---|
.. | .. |
---|
506 | 508 | caso è semplice dato che copiamo i dati dall'utente e non permettiamo |
---|
507 | 509 | mai loro di accedere direttamente agli oggetti. |
---|
508 | 510 | |
---|
509 | | -C'è una piccola ottimizzazione qui: nella funzione :c:func:`cache_add()` |
---|
| 511 | +C'è una piccola ottimizzazione qui: nella funzione cache_add() |
---|
510 | 512 | impostiamo i campi dell'oggetto prima di acquisire il *lock*. Questo è |
---|
511 | 513 | sicuro perché nessun altro potrà accedervi finché non lo inseriremo |
---|
512 | 514 | nella memoria. |
---|
.. | .. |
---|
514 | 516 | Accesso dal contesto utente |
---|
515 | 517 | --------------------------- |
---|
516 | 518 | |
---|
517 | | -Ora consideriamo il caso in cui :c:func:`cache_find()` può essere invocata |
---|
| 519 | +Ora consideriamo il caso in cui cache_find() può essere invocata |
---|
518 | 520 | dal contesto d'interruzione: sia hardware che software. Un esempio potrebbe |
---|
519 | 521 | essere un timer che elimina oggetti dalla memoria. |
---|
520 | 522 | |
---|
.. | .. |
---|
583 | 585 | return ret; |
---|
584 | 586 | } |
---|
585 | 587 | |
---|
586 | | -Da notare che :c:func:`spin_lock_irqsave()` disabiliterà le interruzioni |
---|
| 588 | +Da notare che spin_lock_irqsave() disabiliterà le interruzioni |
---|
587 | 589 | se erano attive, altrimenti non farà niente (quando siamo già in un contesto |
---|
588 | 590 | d'interruzione); dunque queste funzioni possono essere chiamante in |
---|
589 | 591 | sicurezza da qualsiasi contesto. |
---|
590 | 592 | |
---|
591 | | -Sfortunatamente, :c:func:`cache_add()` invoca :c:func:`kmalloc()` con |
---|
| 593 | +Sfortunatamente, cache_add() invoca kmalloc() con |
---|
592 | 594 | l'opzione ``GFP_KERNEL`` che è permessa solo in contesto utente. Ho supposto |
---|
593 | | -che :c:func:`cache_add()` venga chiamata dal contesto utente, altrimenti |
---|
594 | | -questa opzione deve diventare un parametro di :c:func:`cache_add()`. |
---|
| 595 | +che cache_add() venga chiamata dal contesto utente, altrimenti |
---|
| 596 | +questa opzione deve diventare un parametro di cache_add(). |
---|
595 | 597 | |
---|
596 | | -Exposing Objects Outside This File |
---|
597 | | ----------------------------------- |
---|
| 598 | +Esporre gli oggetti al di fuori del file |
---|
| 599 | +---------------------------------------- |
---|
598 | 600 | |
---|
599 | 601 | Se i vostri oggetti contengono più informazioni, potrebbe non essere |
---|
600 | 602 | sufficiente copiare i dati avanti e indietro: per esempio, altre parti del |
---|
.. | .. |
---|
610 | 612 | mantiene un puntatore ad un oggetto, presumibilmente si aspetta che questo |
---|
611 | 613 | puntatore rimanga valido. Sfortunatamente, questo è garantito solo mentre |
---|
612 | 614 | si trattiene il *lock*, altrimenti qualcuno potrebbe chiamare |
---|
613 | | -:c:func:`cache_delete()` o peggio, aggiungere un oggetto che riutilizza lo |
---|
| 615 | +cache_delete() o peggio, aggiungere un oggetto che riutilizza lo |
---|
614 | 616 | stesso indirizzo. |
---|
615 | 617 | |
---|
616 | 618 | Dato che c'è un solo *lock*, non potete trattenerlo a vita: altrimenti |
---|
.. | .. |
---|
678 | 680 | } |
---|
679 | 681 | |
---|
680 | 682 | @@ -63,6 +94,7 @@ |
---|
681 | | - strlcpy(obj->name, name, sizeof(obj->name)); |
---|
| 683 | + strscpy(obj->name, name, sizeof(obj->name)); |
---|
682 | 684 | obj->id = id; |
---|
683 | 685 | obj->popularity = 0; |
---|
684 | 686 | + obj->refcnt = 1; /* The cache holds a reference */ |
---|
.. | .. |
---|
710 | 712 | } |
---|
711 | 713 | |
---|
712 | 714 | Abbiamo incapsulato il contatore di riferimenti nelle tipiche funzioni |
---|
713 | | -di 'get' e 'put'. Ora possiamo ritornare l'oggetto da :c:func:`cache_find()` |
---|
| 715 | +di 'get' e 'put'. Ora possiamo ritornare l'oggetto da cache_find() |
---|
714 | 716 | col vantaggio che l'utente può dormire trattenendo l'oggetto (per esempio, |
---|
715 | | -:c:func:`copy_to_user()` per copiare il nome verso lo spazio utente). |
---|
| 717 | +copy_to_user() per copiare il nome verso lo spazio utente). |
---|
716 | 718 | |
---|
717 | 719 | Un altro punto da notare è che ho detto che il contatore dovrebbe incrementarsi |
---|
718 | 720 | per ogni puntatore ad un oggetto: quindi il contatore di riferimenti è 1 |
---|
.. | .. |
---|
727 | 729 | in ``include/asm/atomic.h``: queste sono garantite come atomiche su qualsiasi |
---|
728 | 730 | processore del sistema, quindi non sono necessari i *lock*. In questo caso è |
---|
729 | 731 | più semplice rispetto all'uso degli spinlock, benché l'uso degli spinlock |
---|
730 | | -sia più elegante per casi non banali. Le funzioni :c:func:`atomic_inc()` e |
---|
731 | | -:c:func:`atomic_dec_and_test()` vengono usate al posto dei tipici operatori di |
---|
| 732 | +sia più elegante per casi non banali. Le funzioni atomic_inc() e |
---|
| 733 | +atomic_dec_and_test() vengono usate al posto dei tipici operatori di |
---|
732 | 734 | incremento e decremento, e i *lock* non sono più necessari per proteggere il |
---|
733 | 735 | contatore stesso. |
---|
734 | 736 | |
---|
.. | .. |
---|
792 | 794 | } |
---|
793 | 795 | |
---|
794 | 796 | @@ -94,7 +76,7 @@ |
---|
795 | | - strlcpy(obj->name, name, sizeof(obj->name)); |
---|
| 797 | + strscpy(obj->name, name, sizeof(obj->name)); |
---|
796 | 798 | obj->id = id; |
---|
797 | 799 | obj->popularity = 0; |
---|
798 | 800 | - obj->refcnt = 1; /* The cache holds a reference */ |
---|
.. | .. |
---|
820 | 822 | - Si può togliere static da ``cache_lock`` e dire agli utenti che devono |
---|
821 | 823 | trattenere il *lock* prima di modificare il nome di un oggetto. |
---|
822 | 824 | |
---|
823 | | -- Si può fornire una funzione :c:func:`cache_obj_rename()` che prende il |
---|
| 825 | +- Si può fornire una funzione cache_obj_rename() che prende il |
---|
824 | 826 | *lock* e cambia il nome per conto del chiamante; si dirà poi agli utenti |
---|
825 | 827 | di usare questa funzione. |
---|
826 | 828 | |
---|
.. | .. |
---|
878 | 880 | protetto da ``cache_lock`` piuttosto che dal *lock* dell'oggetto; questo |
---|
879 | 881 | perché è logicamente parte dell'infrastruttura (come |
---|
880 | 882 | :c:type:`struct list_head <list_head>` nell'oggetto). In questo modo, |
---|
881 | | -in :c:func:`__cache_add()`, non ho bisogno di trattenere il *lock* di ogni |
---|
| 883 | +in __cache_add(), non ho bisogno di trattenere il *lock* di ogni |
---|
882 | 884 | oggetto mentre si cerca il meno popolare. |
---|
883 | 885 | |
---|
884 | 886 | Ho anche deciso che il campo id è immutabile, quindi non ho bisogno di |
---|
885 | | -trattenere il lock dell'oggetto quando si usa :c:func:`__cache_find()` |
---|
| 887 | +trattenere il lock dell'oggetto quando si usa __cache_find() |
---|
886 | 888 | per leggere questo campo; il *lock* dell'oggetto è usato solo dal chiamante |
---|
887 | 889 | che vuole leggere o scrivere il campo name. |
---|
888 | 890 | |
---|
.. | .. |
---|
907 | 909 | sveglio 5 notti a parlare da solo. |
---|
908 | 910 | |
---|
909 | 911 | Un caso un pochino più complesso; immaginate d'avere una spazio condiviso |
---|
910 | | -fra un softirq ed il contesto utente. Se usate :c:func:`spin_lock()` per |
---|
| 912 | +fra un softirq ed il contesto utente. Se usate spin_lock() per |
---|
911 | 913 | proteggerlo, il contesto utente potrebbe essere interrotto da un softirq |
---|
912 | 914 | mentre trattiene il lock, da qui il softirq rimarrà in attesa attiva provando |
---|
913 | 915 | ad acquisire il *lock* già trattenuto nel contesto utente. |
---|
.. | .. |
---|
1006 | 1008 | spin_unlock_bh(&list_lock); |
---|
1007 | 1009 | |
---|
1008 | 1010 | Primo o poi, questo esploderà su un sistema multiprocessore perché un |
---|
1009 | | -temporizzatore potrebbe essere già partiro prima di :c:func:`spin_lock_bh()`, |
---|
1010 | | -e prenderà il *lock* solo dopo :c:func:`spin_unlock_bh()`, e cercherà |
---|
| 1011 | +temporizzatore potrebbe essere già partiro prima di spin_lock_bh(), |
---|
| 1012 | +e prenderà il *lock* solo dopo spin_unlock_bh(), e cercherà |
---|
1011 | 1013 | di eliminare il suo oggetto (che però è già stato eliminato). |
---|
1012 | 1014 | |
---|
1013 | 1015 | Questo può essere evitato controllando il valore di ritorno di |
---|
1014 | | -:c:func:`del_timer()`: se ritorna 1, il temporizzatore è stato già |
---|
| 1016 | +del_timer(): se ritorna 1, il temporizzatore è stato già |
---|
1015 | 1017 | rimosso. Se 0, significa (in questo caso) che il temporizzatore è in |
---|
1016 | 1018 | esecuzione, quindi possiamo fare come segue:: |
---|
1017 | 1019 | |
---|
.. | .. |
---|
1032 | 1034 | spin_unlock_bh(&list_lock); |
---|
1033 | 1035 | |
---|
1034 | 1036 | Un altro problema è l'eliminazione dei temporizzatori che si riavviano |
---|
1035 | | -da soli (chiamando :c:func:`add_timer()` alla fine della loro esecuzione). |
---|
| 1037 | +da soli (chiamando add_timer() alla fine della loro esecuzione). |
---|
1036 | 1038 | Dato che questo è un problema abbastanza comune con una propensione |
---|
1037 | | -alle corse critiche, dovreste usare :c:func:`del_timer_sync()` |
---|
| 1039 | +alle corse critiche, dovreste usare del_timer_sync() |
---|
1038 | 1040 | (``include/linux/timer.h``) per gestire questo caso. Questa ritorna il |
---|
1039 | 1041 | numero di volte che il temporizzatore è stato interrotto prima che |
---|
1040 | 1042 | fosse in grado di fermarlo senza che si riavviasse. |
---|
.. | .. |
---|
1116 | 1118 | wmb(); |
---|
1117 | 1119 | list->next = new; |
---|
1118 | 1120 | |
---|
1119 | | -La funzione :c:func:`wmb()` è una barriera di sincronizzazione delle |
---|
| 1121 | +La funzione wmb() è una barriera di sincronizzazione delle |
---|
1120 | 1122 | scritture. Questa garantisce che la prima operazione (impostare l'elemento |
---|
1121 | 1123 | ``next`` del nuovo elemento) venga completata e vista da tutti i processori |
---|
1122 | 1124 | prima che venga eseguita la seconda operazione (che sarebbe quella di mettere |
---|
.. | .. |
---|
1127 | 1129 | il puntatore ``next`` deve puntare al resto della lista. |
---|
1128 | 1130 | |
---|
1129 | 1131 | Fortunatamente, c'è una funzione che fa questa operazione sulle liste |
---|
1130 | | -:c:type:`struct list_head <list_head>`: :c:func:`list_add_rcu()` |
---|
| 1132 | +:c:type:`struct list_head <list_head>`: list_add_rcu() |
---|
1131 | 1133 | (``include/linux/list.h``). |
---|
1132 | 1134 | |
---|
1133 | 1135 | Rimuovere un elemento dalla lista è anche più facile: sostituiamo il puntatore |
---|
.. | .. |
---|
1138 | 1140 | |
---|
1139 | 1141 | list->next = old->next; |
---|
1140 | 1142 | |
---|
1141 | | -La funzione :c:func:`list_del_rcu()` (``include/linux/list.h``) fa esattamente |
---|
| 1143 | +La funzione list_del_rcu() (``include/linux/list.h``) fa esattamente |
---|
1142 | 1144 | questo (la versione normale corrompe il vecchio oggetto, e non vogliamo che |
---|
1143 | 1145 | accada). |
---|
1144 | 1146 | |
---|
.. | .. |
---|
1146 | 1148 | attraverso il puntatore ``next`` il contenuto dell'elemento successivo |
---|
1147 | 1149 | troppo presto, ma non accorgersi che il contenuto caricato è sbagliato quando |
---|
1148 | 1150 | il puntatore ``next`` viene modificato alla loro spalle. Ancora una volta |
---|
1149 | | -c'è una funzione che viene in vostro aiuto :c:func:`list_for_each_entry_rcu()` |
---|
| 1151 | +c'è una funzione che viene in vostro aiuto list_for_each_entry_rcu() |
---|
1150 | 1152 | (``include/linux/list.h``). Ovviamente, gli scrittori possono usare |
---|
1151 | | -:c:func:`list_for_each_entry()` dato che non ci possono essere due scrittori |
---|
| 1153 | +list_for_each_entry() dato che non ci possono essere due scrittori |
---|
1152 | 1154 | in contemporanea. |
---|
1153 | 1155 | |
---|
1154 | 1156 | Il nostro ultimo dilemma è il seguente: quando possiamo realmente distruggere |
---|
.. | .. |
---|
1156 | 1158 | elemento proprio ora: se eliminiamo questo elemento ed il puntatore ``next`` |
---|
1157 | 1159 | cambia, il lettore salterà direttamente nella spazzatura e scoppierà. Dobbiamo |
---|
1158 | 1160 | aspettare finché tutti i lettori che stanno attraversando la lista abbiano |
---|
1159 | | -finito. Utilizziamo :c:func:`call_rcu()` per registrare una funzione di |
---|
| 1161 | +finito. Utilizziamo call_rcu() per registrare una funzione di |
---|
1160 | 1162 | richiamo che distrugga l'oggetto quando tutti i lettori correnti hanno |
---|
1161 | 1163 | terminato. In alternative, potrebbe essere usata la funzione |
---|
1162 | | -:c:func:`synchronize_rcu()` che blocca l'esecuzione finché tutti i lettori |
---|
| 1164 | +synchronize_rcu() che blocca l'esecuzione finché tutti i lettori |
---|
1163 | 1165 | non terminano di ispezionare la lista. |
---|
1164 | 1166 | |
---|
1165 | 1167 | Ma come fa l'RCU a sapere quando i lettori sono finiti? Il meccanismo è |
---|
1166 | 1168 | il seguente: innanzi tutto i lettori accedono alla lista solo fra la coppia |
---|
1167 | | -:c:func:`rcu_read_lock()`/:c:func:`rcu_read_unlock()` che disabilita la |
---|
| 1169 | +rcu_read_lock()/rcu_read_unlock() che disabilita la |
---|
1168 | 1170 | prelazione così che i lettori non vengano sospesi mentre stanno leggendo |
---|
1169 | 1171 | la lista. |
---|
1170 | 1172 | |
---|
.. | .. |
---|
1253 | 1255 | } |
---|
1254 | 1256 | |
---|
1255 | 1257 | Da notare che i lettori modificano il campo popularity nella funzione |
---|
1256 | | -:c:func:`__cache_find()`, e ora non trattiene alcun *lock*. Una soluzione |
---|
| 1258 | +__cache_find(), e ora non trattiene alcun *lock*. Una soluzione |
---|
1257 | 1259 | potrebbe essere quella di rendere la variabile ``atomic_t``, ma per l'uso |
---|
1258 | 1260 | che ne abbiamo fatto qui, non ci interessano queste corse critiche perché un |
---|
1259 | 1261 | risultato approssimativo è comunque accettabile, quindi non l'ho cambiato. |
---|
1260 | 1262 | |
---|
1261 | | -Il risultato è che la funzione :c:func:`cache_find()` non ha bisogno di alcuna |
---|
| 1263 | +Il risultato è che la funzione cache_find() non ha bisogno di alcuna |
---|
1262 | 1264 | sincronizzazione con le altre funzioni, quindi è veloce su un sistema |
---|
1263 | 1265 | multi-processore tanto quanto lo sarebbe su un sistema mono-processore. |
---|
1264 | 1266 | |
---|
.. | .. |
---|
1271 | 1273 | |
---|
1272 | 1274 | Ora, dato che il '*lock* di lettura' di un RCU non fa altro che disabilitare |
---|
1273 | 1275 | la prelazione, un chiamante che ha sempre la prelazione disabilitata fra le |
---|
1274 | | -chiamate :c:func:`cache_find()` e :c:func:`object_put()` non necessita |
---|
| 1276 | +chiamate cache_find() e object_put() non necessita |
---|
1275 | 1277 | di incrementare e decrementare il contatore di riferimenti. Potremmo |
---|
1276 | | -esporre la funzione :c:func:`__cache_find()` dichiarandola non-static, |
---|
| 1278 | +esporre la funzione __cache_find() dichiarandola non-static, |
---|
1277 | 1279 | e quel chiamante potrebbe usare direttamente questa funzione. |
---|
1278 | 1280 | |
---|
1279 | 1281 | Il beneficio qui sta nel fatto che il contatore di riferimenti no |
---|
.. | .. |
---|
1293 | 1295 | Se questo dovesse essere troppo lento (solitamente non lo è, ma se avete |
---|
1294 | 1296 | dimostrato che lo è devvero), potreste usare un contatore per ogni processore |
---|
1295 | 1297 | e quindi non sarebbe più necessaria la mutua esclusione. Vedere |
---|
1296 | | -:c:func:`DEFINE_PER_CPU()`, :c:func:`get_cpu_var()` e :c:func:`put_cpu_var()` |
---|
| 1298 | +DEFINE_PER_CPU(), get_cpu_var() e put_cpu_var() |
---|
1297 | 1299 | (``include/linux/percpu.h``). |
---|
1298 | 1300 | |
---|
1299 | | -Il tipo di dato ``local_t``, la funzione :c:func:`cpu_local_inc()` e tutte |
---|
| 1301 | +Il tipo di dato ``local_t``, la funzione cpu_local_inc() e tutte |
---|
1300 | 1302 | le altre funzioni associate, sono di particolare utilità per semplici contatori |
---|
1301 | 1303 | per-processore; su alcune architetture sono anche più efficienti |
---|
1302 | 1304 | (``include/asm/local.h``). |
---|
.. | .. |
---|
1324 | 1326 | enable_irq(irq); |
---|
1325 | 1327 | spin_unlock(&lock); |
---|
1326 | 1328 | |
---|
1327 | | -La funzione :c:func:`disable_irq()` impedisce al gestore d'interruzioni |
---|
| 1329 | +La funzione disable_irq() impedisce al gestore d'interruzioni |
---|
1328 | 1330 | d'essere eseguito (e aspetta che finisca nel caso fosse in esecuzione su |
---|
1329 | 1331 | un altro processore). Lo spinlock, invece, previene accessi simultanei. |
---|
1330 | 1332 | Naturalmente, questo è più lento della semplice chiamata |
---|
1331 | | -:c:func:`spin_lock_irq()`, quindi ha senso solo se questo genere di accesso |
---|
| 1333 | +spin_lock_irq(), quindi ha senso solo se questo genere di accesso |
---|
1332 | 1334 | è estremamente raro. |
---|
1333 | 1335 | |
---|
1334 | 1336 | .. _`it_sleeping-things`: |
---|
.. | .. |
---|
1336 | 1338 | Quali funzioni possono essere chiamate in modo sicuro dalle interruzioni? |
---|
1337 | 1339 | ========================================================================= |
---|
1338 | 1340 | |
---|
1339 | | -Molte funzioni del kernel dormono (in sostanza, chiamano ``schedule()``) |
---|
| 1341 | +Molte funzioni del kernel dormono (in sostanza, chiamano schedule()) |
---|
1340 | 1342 | direttamente od indirettamente: non potete chiamarle se trattenere uno |
---|
1341 | 1343 | spinlock o avete la prelazione disabilitata, mai. Questo significa che |
---|
1342 | 1344 | dovete necessariamente essere nel contesto utente: chiamarle da un |
---|
.. | .. |
---|
1354 | 1356 | |
---|
1355 | 1357 | - Accessi allo spazio utente: |
---|
1356 | 1358 | |
---|
1357 | | - - :c:func:`copy_from_user()` |
---|
| 1359 | + - copy_from_user() |
---|
1358 | 1360 | |
---|
1359 | | - - :c:func:`copy_to_user()` |
---|
| 1361 | + - copy_to_user() |
---|
1360 | 1362 | |
---|
1361 | | - - :c:func:`get_user()` |
---|
| 1363 | + - get_user() |
---|
1362 | 1364 | |
---|
1363 | | - - :c:func:`put_user()` |
---|
| 1365 | + - put_user() |
---|
1364 | 1366 | |
---|
1365 | | -- :c:func:`kmalloc(GFP_KERNEL) <kmalloc>` |
---|
| 1367 | +- kmalloc(GFP_KERNEL) <kmalloc>` |
---|
1366 | 1368 | |
---|
1367 | | -- :c:func:`mutex_lock_interruptible()` and |
---|
1368 | | - :c:func:`mutex_lock()` |
---|
| 1369 | +- mutex_lock_interruptible() and |
---|
| 1370 | + mutex_lock() |
---|
1369 | 1371 | |
---|
1370 | | - C'è anche :c:func:`mutex_trylock()` che però non dorme. |
---|
| 1372 | + C'è anche mutex_trylock() che però non dorme. |
---|
1371 | 1373 | Comunque, non deve essere usata in un contesto d'interruzione dato |
---|
1372 | 1374 | che la sua implementazione non è sicura in quel contesto. |
---|
1373 | | - Anche :c:func:`mutex_unlock()` non dorme mai. Non può comunque essere |
---|
| 1375 | + Anche mutex_unlock() non dorme mai. Non può comunque essere |
---|
1374 | 1376 | usata in un contesto d'interruzione perché un mutex deve essere rilasciato |
---|
1375 | 1377 | dallo stesso processo che l'ha acquisito. |
---|
1376 | 1378 | |
---|
.. | .. |
---|
1380 | 1382 | Alcune funzioni possono essere chiamate tranquillamente da qualsiasi |
---|
1381 | 1383 | contesto, o trattenendo un qualsiasi *lock*. |
---|
1382 | 1384 | |
---|
1383 | | -- :c:func:`printk()` |
---|
| 1385 | +- printk() |
---|
1384 | 1386 | |
---|
1385 | | -- :c:func:`kfree()` |
---|
| 1387 | +- kfree() |
---|
1386 | 1388 | |
---|
1387 | | -- :c:func:`add_timer()` e :c:func:`del_timer()` |
---|
| 1389 | +- add_timer() e del_timer() |
---|
1388 | 1390 | |
---|
1389 | 1391 | Riferimento per l'API dei Mutex |
---|
1390 | 1392 | =============================== |
---|
.. | .. |
---|
1398 | 1400 | Riferimento per l'API dei Futex |
---|
1399 | 1401 | =============================== |
---|
1400 | 1402 | |
---|
1401 | | -.. kernel-doc:: kernel/futex.c |
---|
| 1403 | +.. kernel-doc:: kernel/futex/core.c |
---|
1402 | 1404 | :internal: |
---|
1403 | 1405 | |
---|
1404 | 1406 | Approfondimenti |
---|
1405 | 1407 | =============== |
---|
1406 | 1408 | |
---|
1407 | | -- ``Documentation/locking/spinlocks.txt``: la guida di Linus Torvalds agli |
---|
| 1409 | +- ``Documentation/locking/spinlocks.rst``: la guida di Linus Torvalds agli |
---|
1408 | 1410 | spinlock del kernel. |
---|
1409 | 1411 | |
---|
1410 | 1412 | - Unix Systems for Modern Architectures: Symmetric Multiprocessing and |
---|
.. | .. |
---|
1444 | 1446 | bh |
---|
1445 | 1447 | Bottom Half: per ragioni storiche, le funzioni che contengono '_bh' nel |
---|
1446 | 1448 | loro nome ora si riferiscono a qualsiasi interruzione software; per esempio, |
---|
1447 | | - :c:func:`spin_lock_bh()` blocca qualsiasi interuzione software sul processore |
---|
| 1449 | + spin_lock_bh() blocca qualsiasi interuzione software sul processore |
---|
1448 | 1450 | corrente. I *Bottom Halves* sono deprecati, e probabilmente verranno |
---|
1449 | 1451 | sostituiti dai tasklet. In un dato momento potrà esserci solo un |
---|
1450 | 1452 | *bottom half* in esecuzione. |
---|
1451 | 1453 | |
---|
1452 | 1454 | contesto d'interruzione |
---|
1453 | 1455 | Non è il contesto utente: qui si processano le interruzioni hardware e |
---|
1454 | | - software. La macro :c:func:`in_interrupt()` ritorna vero. |
---|
| 1456 | + software. La macro in_interrupt() ritorna vero. |
---|
1455 | 1457 | |
---|
1456 | 1458 | contesto utente |
---|
1457 | 1459 | Il kernel che esegue qualcosa per conto di un particolare processo (per |
---|
.. | .. |
---|
1461 | 1463 | che hardware. |
---|
1462 | 1464 | |
---|
1463 | 1465 | interruzione hardware |
---|
1464 | | - Richiesta di interruzione hardware. :c:func:`in_irq()` ritorna vero in un |
---|
| 1466 | + Richiesta di interruzione hardware. in_irq() ritorna vero in un |
---|
1465 | 1467 | gestore d'interruzioni hardware. |
---|
1466 | 1468 | |
---|
1467 | 1469 | interruzione software / softirq |
---|
1468 | | - Gestore di interruzioni software: :c:func:`in_irq()` ritorna falso; |
---|
1469 | | - :c:func:`in_softirq()` ritorna vero. I tasklet e le softirq sono entrambi |
---|
| 1470 | + Gestore di interruzioni software: in_irq() ritorna falso; |
---|
| 1471 | + in_softirq() ritorna vero. I tasklet e le softirq sono entrambi |
---|
1470 | 1472 | considerati 'interruzioni software'. |
---|
1471 | 1473 | |
---|
1472 | 1474 | In soldoni, un softirq è uno delle 32 interruzioni software che possono |
---|