| .. | .. |
|---|
| 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 |
|---|