diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-08-07 13:11:27 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-08-07 13:11:27 +0000 |
commit | 34996e42f82bfd60bc2c191e5cae3c6ab233ec6c (patch) | |
tree | 62db60558cbf089714b48daeabca82bf2b20b20e /Documentation/translations | |
parent | Adding debian version 6.8.12-1. (diff) | |
download | linux-34996e42f82bfd60bc2c191e5cae3c6ab233ec6c.tar.xz linux-34996e42f82bfd60bc2c191e5cae3c6ab233ec6c.zip |
Merging upstream version 6.9.7.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'Documentation/translations')
31 files changed, 2743 insertions, 98 deletions
diff --git a/Documentation/translations/it_IT/RCU/index.rst b/Documentation/translations/it_IT/RCU/index.rst new file mode 100644 index 0000000000..22adf1d587 --- /dev/null +++ b/Documentation/translations/it_IT/RCU/index.rst @@ -0,0 +1,19 @@ +.. SPDX-License-Identifier: GPL-2.0 + +.. _it_rcu_concepts: + +=============== +Concetti su RCU +=============== + +.. toctree:: + :maxdepth: 3 + + torture + +.. only:: subproject and html + + Indici + ====== + + * :ref:`genindex` diff --git a/Documentation/translations/it_IT/RCU/torture.rst b/Documentation/translations/it_IT/RCU/torture.rst new file mode 100644 index 0000000000..189f7c6cae --- /dev/null +++ b/Documentation/translations/it_IT/RCU/torture.rst @@ -0,0 +1,369 @@ +.. SPDX-License-Identifier: GPL-2.0 + +.. include:: ../disclaimer-ita.rst + +============================================= +Le operazioni RCU per le verifiche *torture* +============================================= + +CONFIG_RCU_TORTURE_TEST +======================= + +L'opzione CONFIG_RCU_TORTURE_TEST è disponibile per tutte le implementazione di +RCU. L'opzione creerà un modulo rcutorture che potrete caricare per avviare le +verifiche. La verifica userà printk() per riportare lo stato, dunque potrete +visualizzarlo con dmesg (magari usate grep per filtrare "torture"). Le verifiche +inizieranno al caricamento, e si fermeranno alla sua rimozione. + +I parametri di modulo hanno tutti il prefisso "rcutortute.", vedere +Documentation/admin-guide/kernel-parameters.txt. + +Rapporto +======== + +Il rapporto sulle verifiche si presenta nel seguente modo:: + + rcu-torture:--- Start of test: nreaders=16 nfakewriters=4 stat_interval=30 verbose=0 test_no_idle_hz=1 shuffle_interval=3 stutter=5 irqreader=1 fqs_duration=0 fqs_holdoff=0 fqs_stutter=3 test_boost=1/0 test_boost_interval=7 test_boost_duration=4 + rcu-torture: rtc: (null) ver: 155441 tfle: 0 rta: 155441 rtaf: 8884 rtf: 155440 rtmbe: 0 rtbe: 0 rtbke: 0 rtbre: 0 rtbf: 0 rtb: 0 nt: 3055767 + rcu-torture: Reader Pipe: 727860534 34213 0 0 0 0 0 0 0 0 0 + rcu-torture: Reader Batch: 727877838 17003 0 0 0 0 0 0 0 0 0 + rcu-torture: Free-Block Circulation: 155440 155440 155440 155440 155440 155440 155440 155440 155440 155440 0 + rcu-torture:--- End of test: SUCCESS: nreaders=16 nfakewriters=4 stat_interval=30 verbose=0 test_no_idle_hz=1 shuffle_interval=3 stutter=5 irqreader=1 fqs_duration=0 fqs_holdoff=0 fqs_stutter=3 test_boost=1/0 test_boost_interval=7 test_boost_duration=4 + +Sulla maggior parte dei sistemi questo rapporto si produce col comando "dmesg | +grep torture:". Su configurazioni più esoteriche potrebbe essere necessario +usare altri comandi per visualizzare i messaggi di printk(). La funzione +printk() usa KERN_ALERT, dunque i messaggi dovrebbero essere ben visibili. ;-) + +La prima e l'ultima riga mostrano i parametri di module di rcutorture, e solo +sull'ultima riga abbiamo il risultato finale delle verifiche effettuate che può +essere "SUCCESS" (successo) or "FAILURE" (insuccesso). + +Le voci sono le seguenti: + +* "rtc": L'indirizzo in esadecimale della struttura attualmente visibile dai + lettori. + +* "ver": Il numero di volte dall'avvio che il processo scrittore di RCU ha + cambiato la struttura visible ai lettori. + +* "tfle": se non è zero, indica la lista di strutture "torture freelist" da + mettere in "rtc" è vuota. Questa condizione è importante perché potrebbe + illuderti che RCU stia funzionando mentre invece non è il caso. :-/ + +* "rta": numero di strutture allocate dalla lista "torture freelist". + +* "rtaf": il numero di allocazioni fallite dalla lista "torture freelist" a + causa del fatto che fosse vuota. Non è inusuale che sia diverso da zero, ma è + un brutto segno se questo numero rappresenta una frazione troppo alta di + "rta". + +* "rtf": il numero di rilasci nella lista "torture freelist" + +* "rtmbe": Un valore diverso da zero indica che rcutorture crede che + rcu_assign_pointer() e rcu_dereference() non funzionino correttamente. Il + valore dovrebbe essere zero. + +* "rtbe": un valore diverso da zero indica che le funzioni della famiglia + rcu_barrier() non funzionano correttamente. + +* "rtbke": rcutorture è stato capace di creare dei kthread real-time per forzare + l'inversione di priorità di RCU. Il valore dovrebbe essere zero. + +* "rtbre": sebbene rcutorture sia riuscito a creare dei kthread capaci di + forzare l'inversione di priorità, non è riuscito però ad impostarne la + priorità real-time al livello 1. Il valore dovrebbe essere zero. + +* "rtbf": Il numero di volte che è fallita la promozione della priorità per + risolvere un'inversione. + +* "rtb": Il numero di volte che rcutorture ha provato a forzare l'inversione di + priorità. Il valore dovrebbe essere diverso da zero Se state verificando la + promozione della priorità col parametro "test_bootst". + +* "nt": il numero di volte che rcutorture ha eseguito codice lato lettura + all'interno di un gestore di *timer*. Questo valore dovrebbe essere diverso da + zero se avete specificato il parametro "irqreader". + +* "Reader Pipe": un istogramma dell'età delle strutture viste dai lettori. RCU + non funziona correttamente se una qualunque voce, dalla terza in poi, ha un + valore diverso da zero. Se dovesse succedere, rcutorture stampa la stringa + "!!!" per renderlo ben visibile. L'età di una struttura appena creata è zero, + diventerà uno quando sparisce dalla visibilità di un lettore, e incrementata + successivamente per ogni periodo di grazia; infine rilasciata dopo essere + passata per (RCU_TORTURE_PIPE_LEN-2) periodi di grazia. + + L'istantanea qui sopra è stata presa da una corretta implementazione di RCU. + Se volete vedere come appare quando non funziona, sbizzarritevi nel romperla. + ;-) + +* "Reader Batch": un istogramma di età di strutture viste dai lettori, ma + conteggiata in termini di lotti piuttosto che periodi. Anche qui dalla terza + voce in poi devono essere zero. La ragione d'esistere di questo rapporto è che + a volte è più facile scatenare un terzo valore diverso da zero qui piuttosto + che nella lista "Reader Pipe". + +* "Free-Block Circulation": il numero di strutture *torture* che hanno raggiunto + un certo punto nella catena. Il primo numero dovrebbe corrispondere + strettamente al numero di strutture allocate; il secondo conta quelle rimosse + dalla vista dei lettori. Ad eccezione dell'ultimo valore, gli altri + corrispondono al numero di passaggi attraverso il periodo di grazia. L'ultimo + valore dovrebbe essere zero, perché viene incrementato solo se il contatore + della struttura torture viene in un qualche modo incrementato oltre il + normale. + +Una diversa implementazione di RCU potrebbe fornire informazioni aggiuntive. Per +esempio, *Tree SRCU* fornisce anche la seguente riga:: + + srcud-torture: Tree SRCU per-CPU(idx=0): 0(35,-21) 1(-4,24) 2(1,1) 3(-26,20) 4(28,-47) 5(-9,4) 6(-10,14) 7(-14,11) T(1,6) + +Questa riga mostra lo stato dei contatori per processore, in questo caso per +*Tree SRCU*, usando un'allocazione dinamica di srcu_struct (dunque "srcud-" +piuttosto che "srcu-"). I numeri fra parentesi sono i valori del "vecchio" +contatore e di quello "corrente" per ogni processore. Il valore "idx" mappa +questi due valori nell'array, ed è utile per il *debug*. La "T" finale contiene +il valore totale dei contatori. + +Uso su specifici kernel +======================= + +A volte può essere utile eseguire RCU torture su un kernel già compilato, ad +esempio quando lo si sta per mettere in proeduzione. In questo caso, il kernel +dev'essere compilato con CONFIG_RCU_TORTURE_TEST=m, cosicché le verifiche possano +essere avviate usano modprobe e terminate con rmmod. + +Per esempio, potreste usare questo script:: + + #!/bin/sh + + modprobe rcutorture + sleep 3600 + rmmod rcutorture + dmesg | grep torture: + +Potete controllare il rapporto verificando manualmente la presenza del marcatore +di errore "!!!". Ovviamente, siete liberi di scriverne uno più elaborato che +identifichi automaticamente gli errori. Il comando "rmmod" forza la stampa di +"SUCCESS" (successo), "FAILURE" (fallimento), o "RCU_HOTPLUG". I primi due sono +autoesplicativi; invece, l'ultimo indica che non son stati trovati problemi in +RCU, tuttavia ci sono stati problemi con CPU-hotplug. + + +Uso sul kernel di riferimento +============================= + +Quando si usa rcutorture per verificare modifiche ad RCU stesso, spesso è +necessario compilare un certo numero di kernel usando configurazioni diverse e +con parametri d'avvio diversi. In questi casi, usare modprobe ed rmmod potrebbe +richiedere molto tempo ed il processo essere suscettibile ad errori. + +Dunque, viene messo a disposizione il programma +tools/testing/selftests/rcutorture/bin/kvm.sh per le architetture x86, arm64 e +powerpc. Di base, eseguirà la serie di verifiche elencate in +tools/testing/selftests/rcutorture/configs/rcu/CFLIST. Ognuna di queste verrà +eseguita per 30 minuti in una macchina virtuale con uno spazio utente minimale +fornito da un initrd generato automaticamente. Al completamento, gli artefatti +prodotti e i messaggi vengono analizzati alla ricerca di errori, ed i risultati +delle esecuzioni riassunti in un rapporto. + +Su grandi sistemi, le verifiche di rcutorture posso essere velocizzare passano a +kvm.sh l'argomento --cpus. Per esempio, su un sistema a 64 processori, "--cpus +43" userà fino a 43 processori per eseguire contemporaneamente le verifiche. Su +un kernel v5.4 per eseguire tutti gli scenari in due serie, riduce il tempo +d'esecuzione da otto ore a un'ora (senza contare il tempo per compilare sedici +kernel). L'argomento "--dryrun sched" non eseguirà verifiche, piuttosto vi +informerà su come queste verranno organizzate in serie. Questo può essere utile +per capire quanti processori riservare per le verifiche in --cpus. + +Non serve eseguire tutti gli scenari di verifica per ogni modifica. Per esempio, +per una modifica a Tree SRCU potete eseguire gli scenari SRCU-N e SRCU-P. Per +farlo usate l'argomento --configs di kvm.sh in questo modo: "--configs 'SRCU-N +SRCU-P'". Su grandi sistemi si possono eseguire più copie degli stessi scenari, +per esempio, un hardware che permette di eseguire 448 thread, può eseguire 5 +istanze complete contemporaneamente. Per farlo:: + + kvm.sh --cpus 448 --configs '5*CFLIST' + +Oppure, lo stesso sistema, può eseguire contemporaneamente 56 istanze dello +scenario su otto processori:: + + kvm.sh --cpus 448 --configs '56*TREE04' + +O ancora 28 istanze per ogni scenario su otto processori:: + + kvm.sh --cpus 448 --configs '28*TREE03 28*TREE04' + +Ovviamente, ogni esecuzione utilizzerà della memoria. Potete limitarne l'uso con +l'argomento --memory, che di base assume il valore 512M. Per poter usare valori +piccoli dovrete disabilitare le verifiche *callback-flooding* usando il +parametro --bootargs che vedremo in seguito. + +A volte è utile avere informazioni aggiuntive di debug, in questo caso potete +usare il parametro --kconfig, per esempio, ``--kconfig +'CONFIG_RCU_EQS_DEBUG=y'``. In aggiunta, ci sono i parametri --gdb, --kasan, and +kcsan. Da notare che --gdb vi limiterà all'uso di un solo scenario per +esecuzione di kvm.sh e richiede di avere anche un'altra finestra aperta dalla +quale eseguire ``gdb`` come viene spiegato dal programma. + +Potete passare anche i parametri d'avvio del kernel, per esempio, per +controllare i parametri del modulo rcutorture. Per esempio, per verificare +modifiche del codice RCU CPU stall-warning, usate ``bootargs +'rcutorture.stall_cpu=30``. Il programma riporterà un fallimento, ossia il +risultato della verifica. Come visto in precedenza, ridurre la memoria richiede +la disabilitazione delle verifiche *callback-flooding*:: + + kvm.sh --cpus 448 --configs '56*TREE04' --memory 128M \ + --bootargs 'rcutorture.fwd_progress=0' + +A volte tutto quello che serve è una serie completa di compilazioni del kernel. +Questo si ottiene col parametro --buildonly. + +Il parametro --duration sovrascrive quello di base di 30 minuti. Per esempio, +con ``--duration 2d`` l'esecuzione sarà di due giorni, ``--duraction 5min`` di +cinque minuti, e ``--duration 45s`` di 45 secondi. L'ultimo può essere utile per +scovare rari errori nella sequenza d'avvio. + +Infine, il parametro --trust-make permette ad ogni nuova compilazione del kernel +di riutilizzare tutto il possibile da quelle precedenti. Da notare che senza il +parametro --trust-make, i vostri file di *tag* potrebbero essere distrutti. + +Ci sono altri parametri più misteriosi che sono documentati nel codice sorgente +dello programma kvm.sh. + +Se un'esecuzione contiene degli errori, il loro numero durante la compilazione e +all'esecuzione verranno elencati alla fine fra i risultati di kvm.sh (che vi +consigliamo caldamente di reindirizzare verso un file). I file prodotti dalla +compilazione ed i risultati stampati vengono salvati, usando un riferimento +temporale, nelle cartella tools/testing/selftests/rcutorture/res. Una cartella +di queste cartelle può essere fornita a kvm-find-errors.sh per estrarne gli +errori. Per esempio:: + + tools/testing/selftests/rcutorture/bin/kvm-find-errors.sh \ + tools/testing/selftests/rcutorture/res/2020.01.20-15.54.23 + +Tuttavia, molto spesso è più conveniente aprire i file direttamente. I file +riguardanti tutti gli scenari di un'esecuzione di trovano nella cartella +principale (2020.01.20-15.54.23 nell'esempio precedente), mentre quelli +specifici per scenario si trovano in sotto cartelle che prendono il nome dello +scenario stesso (per esempio, "TREE04"). Se un dato scenario viene eseguito più +di una volta (come abbiamo visto con "--configs '56*TREE04'"), allora dalla +seconda esecuzione in poi le sottocartelle includeranno un numero di +progressione, per esempio "TREE04.2", "TREE04.3", e via dicendo. + +Il file solitamente più usato nella cartella principale è testid.txt. Se la +verifica viene eseguita in un repositorio git, allora questo file conterrà il +*commit* sul quale si basano le verifiche, mentre tutte le modifiche non +registrare verranno mostrate in formato diff. + +I file solitamente più usati nelle cartelle di scenario sono: + +.config + Questo file contiene le opzioni di Kconfig + +Make.out + Questo file contiene il risultato di compilazione per uno specifico scenario + +console.log + Questo file contiene il risultato d'esecuzione per uno specifico scenario. + Questo file può essere esaminato una volta che il kernel è stato avviato, + ma potrebbe non esistere se l'avvia non è fallito. + +vmlinux + Questo file contiene il kernel, e potrebbe essere utile da esaminare con + programmi come pbjdump e gdb + +Ci sono altri file, ma vengono usati meno. Molti sono utili all'analisi di +rcutorture stesso o dei suoi programmi. + +Nel kernel v5.4, su un sistema a 12 processori, un'esecuzione senza errori +usando gli scenari di base produce il seguente risultato:: + + SRCU-N ------- 804233 GPs (148.932/s) [srcu: g10008272 f0x0 ] + SRCU-P ------- 202320 GPs (37.4667/s) [srcud: g1809476 f0x0 ] + SRCU-t ------- 1122086 GPs (207.794/s) [srcu: g0 f0x0 ] + SRCU-u ------- 1111285 GPs (205.794/s) [srcud: g1 f0x0 ] + TASKS01 ------- 19666 GPs (3.64185/s) [tasks: g0 f0x0 ] + TASKS02 ------- 20541 GPs (3.80389/s) [tasks: g0 f0x0 ] + TASKS03 ------- 19416 GPs (3.59556/s) [tasks: g0 f0x0 ] + TINY01 ------- 836134 GPs (154.84/s) [rcu: g0 f0x0 ] n_max_cbs: 34198 + TINY02 ------- 850371 GPs (157.476/s) [rcu: g0 f0x0 ] n_max_cbs: 2631 + TREE01 ------- 162625 GPs (30.1157/s) [rcu: g1124169 f0x0 ] + TREE02 ------- 333003 GPs (61.6672/s) [rcu: g2647753 f0x0 ] n_max_cbs: 35844 + TREE03 ------- 306623 GPs (56.782/s) [rcu: g2975325 f0x0 ] n_max_cbs: 1496497 + CPU count limited from 16 to 12 + TREE04 ------- 246149 GPs (45.5831/s) [rcu: g1695737 f0x0 ] n_max_cbs: 434961 + TREE05 ------- 314603 GPs (58.2598/s) [rcu: g2257741 f0x2 ] n_max_cbs: 193997 + TREE07 ------- 167347 GPs (30.9902/s) [rcu: g1079021 f0x0 ] n_max_cbs: 478732 + CPU count limited from 16 to 12 + TREE09 ------- 752238 GPs (139.303/s) [rcu: g13075057 f0x0 ] n_max_cbs: 99011 + +Ripetizioni +=========== + +Immaginate di essere alla caccia di un raro problema che si verifica all'avvio. +Potreste usare kvm.sh, tuttavia questo ricompilerebbe il kernel ad ogni +esecuzione. Se avete bisogno di (diciamo) 1000 esecuzioni per essere sicuri di +aver risolto il problema, allora queste inutili ricompilazioni possono diventare +estremamente fastidiose. + +Per questo motivo esiste kvm-again.sh. + +Immaginate che un'esecuzione precedente di kvm.sh abbia lasciato i suoi +artefatti nella cartella:: + + tools/testing/selftests/rcutorture/res/2022.11.03-11.26.28 + +Questa esecuzione può essere rieseguita senza ricompilazioni:: + + kvm-again.sh tools/testing/selftests/rcutorture/res/2022.11.03-11.26.28 + +Alcuni dei parametri originali di kvm.sh possono essere sovrascritti, in +particolare --duration e --bootargs. Per esempio:: + + kvm-again.sh tools/testing/selftests/rcutorture/res/2022.11.03-11.26.28 \ + --duration 45s + +rieseguirebbe il test precedente, ma solo per 45 secondi, e quindi aiutando a +trovare quel raro problema all'avvio sopracitato. + +Esecuzioni distribuite +====================== + +Sebbene kvm.sh sia utile, le sue verifiche sono limitate ad un singolo sistema. +Non è poi così difficile usare un qualsiasi ambiente di sviluppo per eseguire +(diciamo) 5 istanze di kvm.sh su altrettanti sistemi, ma questo avvierebbe +inutili ricompilazioni del kernel. In aggiunta, il processo di distribuzione +degli scenari di verifica per rcutorture sui sistemi disponibili richiede +scrupolo perché soggetto ad errori. + +Per questo esiste kvm-remote.sh. + +Se il seguente comando funziona:: + + ssh system0 date + +e funziona anche per system1, system2, system3, system4, e system5, e tutti +questi sistemi hanno 64 CPU, allora potere eseguire:: + + kvm-remote.sh "system0 system1 system2 system3 system4 system5" \ + --cpus 64 --duration 8h --configs "5*CFLIST" + +Questo compilerà lo scenario di base sul sistema locale, poi lo distribuirà agli +altri cinque sistemi elencati fra i parametri, ed eseguirà ogni scenario per +otto ore. Alla fine delle esecuzioni, i risultati verranno raccolti, registrati, +e stampati. La maggior parte dei parametri di kvm.sh possono essere usati con +kvm-remote.sh, tuttavia la lista dei sistemi deve venire sempre per prima. + +L'argomento di kvm.sh ``--dryrun scenarios`` può essere utile per scoprire +quanti scenari potrebbero essere eseguiti in gruppo di sistemi. + +Potete rieseguire anche una precedente esecuzione remota come abbiamo già fatto +per kvm.sh:: + + kvm-remote.sh "system0 system1 system2 system3 system4 system5" \ + tools/testing/selftests/rcutorture/res/2022.11.03-11.26.28-remote \ + --duration 24h + +In questo caso, la maggior parte dei parametri di kvm-again.sh possono essere +usati dopo il percorso alla cartella contenente gli artefatti dell'esecuzione da +ripetere. diff --git a/Documentation/translations/it_IT/core-api/index.rst b/Documentation/translations/it_IT/core-api/index.rst index cc4c4328ad..dad20402d1 100644 --- a/Documentation/translations/it_IT/core-api/index.rst +++ b/Documentation/translations/it_IT/core-api/index.rst @@ -10,6 +10,18 @@ Utilità di base symbol-namespaces +Primitive di sincronizzazione +============================= + +Come Linux impedisce che tutto si verifichi contemporaneamente. Consultate +Documentation/translations/it_IT/locking/index.rst per maggiorni informazioni +sul tema. + +.. toctree:: + :maxdepth: 1 + + ../RCU/index + .. only:: subproject and html Indices diff --git a/Documentation/translations/it_IT/i2c/i2c-protocol.rst b/Documentation/translations/it_IT/i2c/i2c-protocol.rst new file mode 100644 index 0000000000..ba7c8cd8f5 --- /dev/null +++ b/Documentation/translations/it_IT/i2c/i2c-protocol.rst @@ -0,0 +1,99 @@ +================= +Il protocollo I2C +================= + +Questo documento è una panoramica delle transazioni di base I2C e delle API +del kernel per eseguirli. + +Spiegazione dei simboli +======================= + +=============== =========================================================== +S Condizione di avvio +P Condizione di stop +Rd/Wr (1 bit) Bit di lettura/scrittura. Rd vale 1, Wr vale 0. +A, NA (1 bit) Bit di riconoscimento (ACK) e di non riconoscimento (NACK). +Addr (7 bit) Indirizzo I2C a 7 bit. Nota che questo può essere espanso + per ottenere un indirizzo I2C a 10 bit. +Dati (8 bit) Un byte di dati. + +[..] Fra parentesi quadre i dati inviati da dispositivi I2C, + anziché dal master. +=============== =========================================================== + + +Transazione semplice di invio +============================= + +Implementato da i2c_master_send():: + + S Addr Wr [A] Dati [A] Dati [A] ... [A] Dati [A] P + + +Transazione semplice di ricezione +================================= + +Implementato da i2c_master_recv():: + + S Addr Rd [A] [Dati] A [Dati] A ... A [Dati] NA P + + +Transazioni combinate +===================== + +Implementato da i2c_transfer(). + +Sono come le transazioni di cui sopra, ma invece di uno condizione di stop P +viene inviata una condizione di inizio S e la transazione continua. +Un esempio di lettura di un byte, seguita da una scrittura di un byte:: + + S Addr Rd [A] [Dati] NA S Addr Wr [A] Dati [A] P + + +Transazioni modificate +====================== + +Le seguenti modifiche al protocollo I2C possono essere generate +impostando questi flag per i messaggi I2C. Ad eccezione di I2C_M_NOSTART, sono +di solito necessari solo per risolvere problemi di un dispositivo: + +I2C_M_IGNORE_NAK: + Normalmente il messaggio viene interrotto immediatamente se il dispositivo + risponde con [NA]. Impostando questo flag, si considera qualsiasi [NA] come + [A] e tutto il messaggio viene inviato. + Questi messaggi potrebbero comunque non riuscire a raggiungere il timeout + SCL basso->alto. + +I2C_M_NO_RD_ACK: + In un messaggio di lettura, il bit A/NA del master viene saltato. + +I2C_M_NOSTART: + In una transazione combinata, potrebbe non essere generato alcun + "S Addr Wr/Rd [A]". + Ad esempio, impostando I2C_M_NOSTART sul secondo messaggio parziale + genera qualcosa del tipo:: + + S Addr Rd [A] [Dati] NA Dati [A] P + + Se si imposta il flag I2C_M_NOSTART per il primo messaggio parziale, + non viene generato Addr, ma si genera la condizione di avvio S. + Questo probabilmente confonderà tutti gli altri dispositivi sul bus, quindi + meglio non usarlo. + + Questo viene spesso utilizzato per raccogliere le trasmissioni da più + buffer di dati presenti nella memoria di sistema in qualcosa che appare + come un singolo trasferimento verso il dispositivo I2C. Inoltre, alcuni + dispositivi particolari lo utilizzano anche tra i cambi di direzione. + +I2C_M_REV_DIR_ADDR: + Questo inverte il flag Rd/Wr. Cioè, se si vuole scrivere, ma si ha bisogno + di emettere una Rd invece di una Wr, o viceversa, si può impostare questo + flag. + Per esempio:: + + S Addr Rd [A] Dati [A] Dati [A] ... [A] Dati [A] P + +I2C_M_STOP: + Forza una condizione di stop (P) dopo il messaggio. Alcuni protocolli + simili a I2C come SCCB lo richiedono. Normalmente, non si vuole essere + interrotti tra i messaggi di un trasferimento. diff --git a/Documentation/translations/it_IT/i2c/index.rst b/Documentation/translations/it_IT/i2c/index.rst new file mode 100644 index 0000000000..14fbe3d782 --- /dev/null +++ b/Documentation/translations/it_IT/i2c/index.rst @@ -0,0 +1,46 @@ +.. SPDX-License-Identifier: GPL-2.0 + +========================= +Il sottosistema I2C/SMBus +========================= + +Introduzione +============ + +.. toctree:: + :maxdepth: 1 + + summary + i2c-protocol + +Scrivere un device driver +========================= + +.. toctree:: + :maxdepth: 1 + +Debugging +========= + +.. toctree:: + :maxdepth: 1 + +Slave I2C +========= + +.. toctree:: + :maxdepth: 1 + + +Argomenti avanzati +================== + +.. toctree:: + :maxdepth: 1 + +.. only:: subproject and html + + Indici + ====== + + * :ref:`genindex` diff --git a/Documentation/translations/it_IT/i2c/summary.rst b/Documentation/translations/it_IT/i2c/summary.rst new file mode 100644 index 0000000000..1535e13a32 --- /dev/null +++ b/Documentation/translations/it_IT/i2c/summary.rst @@ -0,0 +1,64 @@ +========================== +Introduzione a I2C e SMBus +========================== + +I²C (letteralmente "I al quadrato C" e scritto I2C nella documentazione del +kernel) è un protocollo sviluppato da Philips. É un protocollo lento a 2 fili +(a velocità variabile, al massimo 400KHz), con un'estensione per le velocità +elevate (3.4 MHz). Questo protocollo offre un bus a basso costo per collegare +dispositivi di vario genere a cui si accede sporadicamente e utilizzando +poca banda. Alcuni sistemi usano varianti che non rispettano i requisiti +originali, per cui non sono indicati come I2C, ma hanno nomi diversi, per +esempio TWI (Interfaccia a due fili), IIC. + +L'ultima specifica ufficiale I2C è la `"Specifica I2C-bus e manuale utente" +(UM10204) <https://www.nxp.com/webapp/Download?colCode=UM10204>`_ +pubblicata da NXP Semiconductors. Tuttavia, è necessario effettuare il login +al sito per accedere al PDF. Una versione precedente della specifica +(revisione 6) è archiviata +`qui <https://web.archive.org/web/20210813122132/ +https://www.nxp.com/docs/en/user-guide/UM10204.pdf>`_. + +SMBus (Bus per la gestione del sistema) si basa sul protocollo I2C ed è +principalmente un sottoinsieme di protocolli e segnali I2C. Molti dispositivi +I2C funzioneranno su SMBus, ma alcuni protocolli SMBus aggiungono semantica +oltre quanto richiesto da I2C. Le moderne schede madri dei PC si affidano a +SMBus. I più comuni dispositivi collegati tramite SMBus sono moduli RAM +configurati utilizzando EEPROM I2C, e circuiti integrati di monitoraggio +hardware. + +Poiché SMBus è principalmente un sottoinsieme del bus I2C, +possiamo farne uso su molti sistemi I2C. Ci sono però sistemi che non +soddisfano i vincoli elettrici sia di SMBus che di I2C; e altri che non possono +implementare tutta la semantica o messaggi comuni del protocollo SMBus. + + +Terminologia +============ + +Utilizzando la terminologia della documentazione ufficiale, il bus I2C connette +uno o più circuiti integrati *master* e uno o più circuiti integrati *slave*. + +.. kernel-figure:: ../../../i2c/i2c_bus.svg + :alt: Un semplice bus I2C con un master e 3 slave + + Un semplice Bus I2C + +Un circuito integrato **master** è un nodo che inizia le comunicazioni con gli +slave. Nell'implementazione del kernel Linux è chiamato **adattatore** o bus. I +driver degli adattatori si trovano nella sottocartella ``drivers/i2c/busses/``. + +Un **algoritmo** contiene codice generico che può essere utilizzato per +implementare una intera classe di adattatori I2C. Ciascun driver dell' +adattatore specifico dipende da un driver dell'algoritmo nella sottocartella +``drivers/i2c/algos/`` o include la propria implementazione. + +Un circuito integrato **slave** è un nodo che risponde alle comunicazioni +quando indirizzato dal master. In Linux è chiamato **client** (dispositivo). I +driver dei dispositivi sono contenuti in una cartella specifica per la +funzionalità che forniscono, ad esempio ``drivers/media/gpio/`` per espansori +GPIO e ``drivers/media/i2c/`` per circuiti integrati relativi ai video. + +Per la configurazione di esempio in figura, avrai bisogno di un driver per il +tuo adattatore I2C e driver per i tuoi dispositivi I2C (solitamente un driver +per ciascuno dispositivo). diff --git a/Documentation/translations/it_IT/index.rst b/Documentation/translations/it_IT/index.rst index b95dfa1ded..70ccd23b2c 100644 --- a/Documentation/translations/it_IT/index.rst +++ b/Documentation/translations/it_IT/index.rst @@ -91,6 +91,8 @@ interfacciarsi con il resto del kernel. :maxdepth: 1 core-api/index + Sincronizzazione nel kernel <locking/index> + subsystem-apis Strumenti e processi per lo sviluppo ==================================== diff --git a/Documentation/translations/it_IT/locking/index.rst b/Documentation/translations/it_IT/locking/index.rst new file mode 100644 index 0000000000..19963d33e8 --- /dev/null +++ b/Documentation/translations/it_IT/locking/index.rst @@ -0,0 +1,20 @@ +.. SPDX-License-Identifier: GPL-2.0 + +================ +Sincronizzazione +================ + +.. toctree:: + :maxdepth: 1 + + locktypes + lockdep-design + lockstat + locktorture + +.. only:: subproject and html + + Indici + ====== + + * :ref:`genindex` diff --git a/Documentation/translations/it_IT/locking/lockdep-design.rst b/Documentation/translations/it_IT/locking/lockdep-design.rst new file mode 100644 index 0000000000..9ed00d8cf2 --- /dev/null +++ b/Documentation/translations/it_IT/locking/lockdep-design.rst @@ -0,0 +1,678 @@ +.. SPDX-License-Identifier: GPL-2.0 + +.. include:: ../disclaimer-ita.rst + +Validatore di sincronizzazione durante l'esecuzione +=================================================== + +Classi di blocchi +----------------- + +L'oggetto su cui il validatore lavora è una "classe" di blocchi. + +Una classe di blocchi è un gruppo di blocchi che seguono le stesse regole di +sincronizzazione, anche quando i blocchi potrebbero avere più istanze (anche +decine di migliaia). Per esempio un blocco nella struttura inode è una classe, +mentre ogni inode sarà un'istanza di questa classe di blocco. + +Il validatore traccia lo "stato d'uso" di una classe di blocchi e le sue +dipendenze con altre classi. L'uso di un blocco indica come quel blocco viene +usato rispetto al suo contesto d'interruzione, mentre le dipendenze di un blocco +possono essere interpretate come il loro ordine; per esempio L1 -> L2 suggerisce +che un processo cerca di acquisire L2 mentre già trattiene L1. Dal punto di +vista di lockdep, i due blocchi (L1 ed L2) non sono per forza correlati: quella +dipendenza indica solamente l'ordine in cui sono successe le cose. Il validatore +verifica permanentemente la correttezza dell'uso dei blocchi e delle loro +dipendenze, altrimenti ritornerà un errore. + +Il comportamento di una classe di blocchi viene costruito dall'insieme delle sue +istanze. Una classe di blocco viene registrata alla creazione della sua prima +istanza, mentre tutte le successive istanze verranno mappate; dunque, il loro +uso e le loro dipendenze contribuiranno a costruire quello della classe. Una +classe di blocco non sparisce quando sparisce una sua istanza, ma può essere +rimossa quando il suo spazio in memoria viene reclamato. Per esempio, questo +succede quando si rimuove un modulo, o quando una *workqueue* viene eliminata. + +Stato +----- + +Il validatore traccia l'uso cronologico delle classi di blocchi e ne divide +l'uso in categorie (4 USI * n STATI + 1). + +I quattro USI possono essere: + +- 'sempre trattenuto nel contesto <STATO>' +- 'sempre trattenuto come blocco di lettura nel contesto <STATO>' +- 'sempre trattenuto con <STATO> abilitato' +- 'sempre trattenuto come blocco di lettura con <STATO> abilitato' + +gli `n` STATI sono codificati in kernel/locking/lockdep_states.h, ad oggi +includono: + +- hardirq +- softirq + +infine l'ultima categoria è: + +- 'sempre trattenuto' [ == !unused ] + +Quando vengono violate le regole di sincronizzazione, questi bit di utilizzo +vengono presentati nei messaggi di errore di sincronizzazione, fra parentesi +graffe, per un totale di `2 * n` (`n`: bit STATO). Un esempio inventato:: + + modprobe/2287 is trying to acquire lock: + (&sio_locks[i].lock){-.-.}, at: [<c02867fd>] mutex_lock+0x21/0x24 + + but task is already holding lock: + (&sio_locks[i].lock){-.-.}, at: [<c02867fd>] mutex_lock+0x21/0x24 + +Per un dato blocco, da sinistra verso destra, la posizione del bit indica l'uso +del blocco e di un eventuale blocco di lettura, per ognuno degli `n` STATI elencati +precedentemente. Il carattere mostrato per ogni bit indica: + + === =========================================================================== + '.' acquisito con interruzioni disabilitate fuori da un contesto d'interruzione + '-' acquisito in contesto d'interruzione + '+' acquisito con interruzioni abilitate + '?' acquisito in contesto d'interruzione con interruzioni abilitate + === =========================================================================== + +Il seguente esempio mostra i bit:: + + (&sio_locks[i].lock){-.-.}, at: [<c02867fd>] mutex_lock+0x21/0x24 + |||| + ||| \-> softirq disabilitati e fuori da un contesto di softirq + || \--> acquisito in un contesto di softirq + | \---> hardirq disabilitati e fuori da un contesto di hardirq + \----> acquisito in un contesto di hardirq + +Per un dato STATO, che il blocco sia mai stato acquisito in quel contesto di +STATO, o che lo STATO sia abilitato, ci lascia coi quattro possibili scenari +mostrati nella seguente tabella. Il carattere associato al bit indica con +esattezza in quale scenario ci si trova al momento del rapporto. + + +---------------+---------------+------------------+ + | | irq abilitati | irq disabilitati | + +---------------+---------------+------------------+ + | sempre in irq | '?' | '-' | + +---------------+---------------+------------------+ + | mai in irq | '+' | '.' | + +---------------+---------------+------------------+ + +Il carattere '-' suggerisce che le interruzioni sono disabilitate perché +altrimenti verrebbe mostrato il carattere '?'. Una deduzione simile può essere +fatta anche per '+' + +I blocchi inutilizzati (ad esempio i mutex) non possono essere fra le cause di +un errore. + +Regole dello stato per un blocco singolo +---------------------------------------- + +Avere un blocco sicuro in interruzioni (*irq-safe*) significa che è sempre stato +usato in un contesto d'interruzione, mentre un blocco insicuro in interruzioni +(*irq-unsafe*) significa che è sempre stato acquisito con le interruzioni +abilitate. + +Una classe softirq insicura è automaticamente insicura anche per hardirq. I +seguenti stati sono mutualmente esclusivi: solo una può essere vero quando viene +usata una classe di blocco:: + + <hardirq-safe> o <hardirq-unsafe> + <softirq-safe> o <softirq-unsafe> + +Questo perché se un blocco può essere usato in un contesto di interruzioni +(sicuro in interruzioni), allora non può mai essere acquisito con le +interruzioni abilitate (insicuro in interruzioni). Altrimenti potrebbe +verificarsi uno stallo. Per esempio, questo blocco viene acquisito, ma prima di +essere rilasciato il contesto d'esecuzione viene interrotto nuovamente, e quindi +si tenterà di acquisirlo nuovamente. Questo porterà ad uno stallo, in +particolare uno stallo ricorsivo. + +Il validatore rileva e riporta gli usi di blocchi che violano queste regole per +blocchi singoli. + +Regole per le dipendenze di blocchi multipli +-------------------------------------------- + +La stessa classe di blocco non deve essere acquisita due volte, questo perché +potrebbe portare ad uno blocco ricorsivo e dunque ad uno stallo. + +Inoltre, due blocchi non possono essere trattenuti in ordine inverso:: + + <L1> -> <L2> + <L2> -> <L1> + +perché porterebbe ad uno stallo - chiamato stallo da blocco inverso - in cui si +cerca di trattenere i due blocchi in un ciclo in cui entrambe i contesti +aspettano per sempre che l'altro termini. Il validatore è in grado di trovare +queste dipendenze cicliche di qualsiasi complessità, ovvero nel mezzo ci +potrebbero essere altre sequenze di blocchi. Il validatore troverà se questi +blocchi possono essere acquisiti circolarmente. + +In aggiunta, le seguenti sequenze di blocco nei contesti indicati non sono +permesse, indipendentemente da quale che sia la classe di blocco:: + + <hardirq-safe> -> <hardirq-unsafe> + <softirq-safe> -> <softirq-unsafe> + +La prima regola deriva dal fatto che un blocco sicuro in interruzioni può essere +trattenuto in un contesto d'interruzione che, per definizione, ha la possibilità +di interrompere un blocco insicuro in interruzioni; questo porterebbe ad uno +stallo da blocco inverso. La seconda, analogamente, ci dice che un blocco sicuro +in interruzioni software potrebbe essere trattenuto in un contesto di +interruzione software, dunque potrebbe interrompere un blocco insicuro in +interruzioni software. + +Le suddette regole vengono applicate per qualsiasi sequenza di blocchi: quando +si acquisiscono nuovi blocchi, il validatore verifica se vi è una violazione +delle regole fra il nuovo blocco e quelli già trattenuti. + +Quando una classe di blocco cambia stato, applicheremo le seguenti regole: + +- se viene trovato un nuovo blocco sicuro in interruzioni, verificheremo se + abbia mai trattenuto dei blocchi insicuri in interruzioni. + +- se viene trovato un nuovo blocco sicuro in interruzioni software, + verificheremo se abbia trattenuto dei blocchi insicuri in interruzioni + software. + +- se viene trovato un nuovo blocco insicuro in interruzioni, verificheremo se + abbia trattenuto dei blocchi sicuri in interruzioni. + +- se viene trovato un nuovo blocco insicuro in interruzioni software, + verificheremo se abbia trattenuto dei blocchi sicuri in interruzioni + software. + +(Di nuovo, questi controlli vengono fatti perché un contesto d'interruzione +potrebbe interrompere l'esecuzione di qualsiasi blocco insicuro portando ad uno +stallo; questo anche se lo stallo non si verifica in pratica) + +Eccezione: dipendenze annidate sui dati portano a blocchi annidati +------------------------------------------------------------------ + +Ci sono alcuni casi in cui il kernel Linux acquisisce più volte la stessa +istanza di una classe di blocco. Solitamente, questo succede quando esiste una +gerarchia fra oggetti dello stesso tipo. In questi casi viene ereditato +implicitamente l'ordine fra i due oggetti (definito dalle proprietà di questa +gerarchia), ed il kernel tratterrà i blocchi in questo ordine prefissato per +ognuno degli oggetti. + +Un esempio di questa gerarchia di oggetti che producono "blocchi annidati" sono +i *block-dev* che rappresentano l'intero disco e quelli che rappresentano una +sua partizione; la partizione è una parte del disco intero, e l'ordine dei +blocchi sarà corretto fintantoche uno acquisisce il blocco del disco intero e +poi quello della partizione. Il validatore non rileva automaticamente questo +ordine implicito, perché queste regole di sincronizzazione non sono statiche. + +Per istruire il validatore riguardo a questo uso corretto dei blocchi sono stati +introdotte nuove primitive per specificare i "livelli di annidamento". Per +esempio, per i blocchi a mutua esclusione dei *block-dev* si avrebbe una +chiamata simile a:: + + enum bdev_bd_mutex_lock_class + { + BD_MUTEX_NORMAL, + BD_MUTEX_WHOLE, + BD_MUTEX_PARTITION + }; + + mutex_lock_nested(&bdev->bd_contains->bd_mutex, BD_MUTEX_PARTITION); + +In questo caso la sincronizzazione viene fatta su un *block-dev* sapendo che si +tratta di una partizione. + +Ai fini della validazione, il validatore lo considererà con una - sotto - classe +di blocco separata. + +Nota: Prestate estrema attenzione che la vostra gerarchia sia corretta quando si +vogliono usare le primitive _nested(); altrimenti potreste avere sia falsi +positivi che falsi negativi. + +Annotazioni +----------- + +Si possono utilizzare due costrutti per verificare ed annotare se certi blocchi +devono essere trattenuti: lockdep_assert_held*(&lock) e +lockdep_*pin_lock(&lock). + +Come suggerito dal nome, la famiglia di macro lockdep_assert_held* asseriscono +che un dato blocco in un dato momento deve essere trattenuto (altrimenti, verrà +generato un WARN()). Queste vengono usate abbondantemente nel kernel, per +esempio in kernel/sched/core.c:: + + void update_rq_clock(struct rq *rq) + { + s64 delta; + + lockdep_assert_held(&rq->lock); + [...] + } + +dove aver trattenuto rq->lock è necessario per aggiornare in sicurezza il clock +rq. + +L'altra famiglia di macro è lockdep_*pin_lock(), che a dire il vero viene usata +solo per rq->lock ATM. Se per caso un blocco non viene trattenuto, queste +genereranno un WARN(). Questo si rivela particolarmente utile quando si deve +verificare la correttezza di codice con *callback*, dove livelli superiori +potrebbero assumere che un blocco rimanga trattenuto, ma livelli inferiori +potrebbero invece pensare che il blocco possa essere rilasciato e poi +riacquisito (involontariamente si apre una sezione critica). lockdep_pin_lock() +restituisce 'struct pin_cookie' che viene usato da lockdep_unpin_lock() per +verificare che nessuno abbia manomesso il blocco. Per esempio in +kernel/sched/sched.h abbiamo:: + + static inline void rq_pin_lock(struct rq *rq, struct rq_flags *rf) + { + rf->cookie = lockdep_pin_lock(&rq->lock); + [...] + } + + static inline void rq_unpin_lock(struct rq *rq, struct rq_flags *rf) + { + [...] + lockdep_unpin_lock(&rq->lock, rf->cookie); + } + +I commenti riguardo alla sincronizzazione possano fornire informazioni utili, +tuttavia sono le verifiche in esecuzione effettuate da queste macro ad essere +vitali per scovare problemi di sincronizzazione, ed inoltre forniscono lo stesso +livello di informazioni quando si ispeziona il codice. Nel dubbio, preferite +queste annotazioni! + +Dimostrazione di correttezza al 100% +------------------------------------ + +Il validatore verifica la proprietà di chiusura in senso matematico. Ovvero, per +ogni sequenza di sincronizzazione di un singolo processo che si verifichi almeno +una volta nel kernel, il validatore dimostrerà con una certezza del 100% che +nessuna combinazione e tempistica di queste sequenze possa causare uno stallo in +una qualsiasi classe di blocco. [1]_ + +In pratica, per dimostrare l'esistenza di uno stallo non servono complessi +scenari di sincronizzazione multi-processore e multi-processo. Il validatore può +dimostrare la correttezza basandosi sulla sola sequenza di sincronizzazione +apparsa almeno una volta (in qualunque momento, in qualunque processo o +contesto). Uno scenario complesso che avrebbe bisogno di 3 processori e una +sfortunata presenza di processi, interruzioni, e pessimo tempismo, può essere +riprodotto su un sistema a singolo processore. + +Questo riduce drasticamente la complessità del controllo di qualità della +sincronizzazione nel kernel: quello che deve essere fatto è di innescare nel +kernel quante più possibili "semplici" sequenze di sincronizzazione, almeno una +volta, allo scopo di dimostrarne la correttezza. Questo al posto di innescare +una verifica per ogni possibile combinazione di sincronizzazione fra processori, +e differenti scenari con hardirq e softirq e annidamenti vari (nella pratica, +impossibile da fare) + +.. [1] + + assumendo che il validatore sia corretto al 100%, e che nessun altra parte + del sistema possa corromperne lo stato. Assumiamo anche che tutti i percorsi + MNI/SMM [potrebbero interrompere anche percorsi dove le interruzioni sono + disabilitate] sono corretti e non interferiscono con il validatore. Inoltre, + assumiamo che un hash a 64-bit sia unico per ogni sequenza di + sincronizzazione nel sistema. Infine, la ricorsione dei blocchi non deve + essere maggiore di 20. + +Prestazione +----------- + +Le regole sopracitate hanno bisogno di una quantità **enorme** di verifiche +durante l'esecuzione. Il sistema sarebbe diventato praticamente inutilizzabile +per la sua lentezza se le avessimo fatte davvero per ogni blocco trattenuto e +per ogni abilitazione delle interruzioni. La complessità della verifica è +O(N^2), quindi avremmo dovuto fare decine di migliaia di verifiche per ogni +evento, il tutto per poche centinaia di classi. + +Il problema è stato risolto facendo una singola verifica per ogni 'scenario di +sincronizzazione' (una sequenza unica di blocchi trattenuti uno dopo l'altro). +Per farlo, viene mantenuta una pila dei blocchi trattenuti, e viene calcolato un +hash a 64-bit unico per ogni sequenza. Quando la sequenza viene verificata per +la prima volta, l'hash viene inserito in una tabella hash. La tabella potrà +essere verificata senza bisogno di blocchi. Se la sequenza dovesse ripetersi, la +tabella ci dirà che non è necessario verificarla nuovamente. + +Risoluzione dei problemi +------------------------ + +Il massimo numero di classi di blocco che il validatore può tracciare è: +MAX_LOCKDEP_KEYS. Oltrepassare questo limite indurrà lokdep a generare il +seguente avviso:: + + (DEBUG_LOCKS_WARN_ON(id >= MAX_LOCKDEP_KEYS)) + +Di base questo valore è 8191, e un classico sistema da ufficio ha meno di 1000 +classi, dunque questo avviso è solitamente la conseguenza di un problema di +perdita delle classi di blocco o d'inizializzazione dei blocchi. Di seguito una +descrizione dei due problemi: + +1. caricare e rimuovere continuamente i moduli mentre il validatore è in + esecuzione porterà ad una perdita di classi di blocco. Il problema è che ogni + caricamento crea un nuovo insieme di classi di blocco per tutti i blocchi di + quel modulo. Tuttavia, la rimozione del modulo non rimuove le vecchie classi + (vedi dopo perché non le riusiamo). Dunque, il continuo caricamento e + rimozione di un modulo non fa altro che aumentare il contatore di classi fino + a raggiungere, eventualmente, il limite. + +2. Usare array con un gran numero di blocchi che non vengono esplicitamente + inizializzati. Per esempio, una tabella hash con 8192 *bucket* dove ognuno ha + il proprio spinlock_t consumerà 8192 classi di blocco a meno che non vengano + esplicitamente inizializzati in esecuzione usando spin_lock_init() invece + dell'inizializzazione durante la compilazione con __SPIN_LOCK_UNLOCKED(). + Sbagliare questa inizializzazione garantisce un esaurimento di classi di + blocco. Viceversa, un ciclo che invoca spin_lock_init() su tutti i blocchi li + mapperebbe tutti alla stessa classe di blocco. + + La morale della favola è che dovete sempre inizializzare esplicitamente i + vostri blocchi. + +Qualcuno potrebbe argomentare che il validatore debba permettere il riuso di +classi di blocco. Tuttavia, se siete tentati dall'argomento, prima revisionate +il codice e pensate alla modifiche necessarie, e tenendo a mente che le classi +di blocco da rimuovere probabilmente sono legate al grafo delle dipendenze. Più +facile a dirsi che a farsi. + +Ovviamente, se non esaurite le classi di blocco, la prossima cosa da fare è +quella di trovare le classi non funzionanti. Per prima cosa, il seguente comando +ritorna il numero di classi attualmente in uso assieme al valore massimo:: + + grep "lock-classes" /proc/lockdep_stats + +Questo comando produce il seguente messaggio:: + + lock-classes: 748 [max: 8191] + +Se il numero di assegnazioni (748 qui sopra) aumenta continuamente nel tempo, +allora c'è probabilmente un problema da qualche parte. Il seguente comando può +essere utilizzato per identificare le classi di blocchi problematiche:: + + grep "BD" /proc/lockdep + +Eseguite il comando e salvatene l'output, quindi confrontatelo con l'output di +un'esecuzione successiva per identificare eventuali problemi. Questo stesso +output può anche aiutarti a trovare situazioni in cui l'inizializzazione del +blocco è stata omessa. + +Lettura ricorsiva dei blocchi +----------------------------- + +Il resto di questo documento vuole dimostrare che certi cicli equivalgono ad una +possibilità di stallo. + +Ci sono tre tipi di bloccatori: gli scrittori (bloccatori esclusivi, come +spin_lock() o write_lock()), lettori non ricorsivi (bloccatori condivisi, come +down_read()), e lettori ricorsivi (bloccatori condivisi ricorsivi, come +rcu_read_lock()). D'ora in poi, per questi tipi di bloccatori, useremo la +seguente notazione: + + W o E: per gli scrittori (bloccatori esclusivi) (W dall'inglese per + *Writer*, ed E per *Exclusive*). + + r: per i lettori non ricorsivi (r dall'inglese per *reader*). + + R: per i lettori ricorsivi (R dall'inglese per *Reader*). + + S: per qualsiasi lettore (non ricorsivi + ricorsivi), dato che entrambe + sono bloccatori condivisi (S dall'inglese per *Shared*). + + N: per gli scrittori ed i lettori non ricorsivi, dato che entrambe sono + non ricorsivi. + +Ovviamente, N equivale a "r o W" ed S a "r o R". + +Come suggerisce il nome, i lettori ricorsivi sono dei bloccatori a cui è +permesso di acquisire la stessa istanza di blocco anche all'interno della +sezione critica di un altro lettore. In altre parole, permette di annidare la +stessa istanza di blocco nelle sezioni critiche dei lettori. + +Dall'altro canto, lo stesso comportamento indurrebbe un lettore non ricorsivo ad +auto infliggersi uno stallo. + +La differenza fra questi due tipi di lettori esiste perché: quelli ricorsivi +vengono bloccati solo dal trattenimento di un blocco di scrittura, mentre quelli +non ricorsivi possono essere bloccati dall'attesa di un blocco di scrittura. +Consideriamo il seguente esempio:: + + TASK A: TASK B: + + read_lock(X); + write_lock(X); + read_lock_2(X); + +L'attività A acquisisce il blocco di lettura X (non importa se di tipo ricorsivo +o meno) usando read_lock(). Quando l'attività B tenterà di acquisire il blocco +X, si fermerà e rimarrà in attesa che venga rilasciato. Ora se read_lock_2() è +un tipo lettore ricorsivo, l'attività A continuerà perché gli scrittori in +attesa non possono bloccare lettori ricorsivi, e non avremo alcuno stallo. +Tuttavia, se read_lock_2() è un lettore non ricorsivo, allora verrà bloccato +dall'attività B e si causerà uno stallo. + +Condizioni bloccanti per lettori/scrittori su uno stesso blocco +--------------------------------------------------------------- +Essenzialmente ci sono quattro condizioni bloccanti: + +1. Uno scrittore blocca un altro scrittore. +2. Un lettore blocca uno scrittore. +3. Uno scrittore blocca sia i lettori ricorsivi che non ricorsivi. +4. Un lettore (ricorsivo o meno) non blocca altri lettori ricorsivi ma potrebbe + bloccare quelli non ricorsivi (perché potrebbero esistere degli scrittori in + attesa). + +Di seguito le tabella delle condizioni bloccanti, Y (*Yes*) significa che il +tipo in riga blocca quello in colonna, mentre N l'opposto. + + +---+---+---+---+ + | | W | r | R | + +---+---+---+---+ + | W | Y | Y | Y | + +---+---+---+---+ + | r | Y | Y | N | + +---+---+---+---+ + | R | Y | Y | N | + +---+---+---+---+ + + (W: scrittori, r: lettori non ricorsivi, R: lettori ricorsivi) + +Al contrario dei blocchi per lettori non ricorsivi, quelli ricorsivi vengono +trattenuti da chi trattiene il blocco di scrittura piuttosto che da chi ne +attende il rilascio. Per esempio:: + + TASK A: TASK B: + + read_lock(X); + + write_lock(X); + + read_lock(X); + +non produce uno stallo per i lettori ricorsivi, in quanto il processo B rimane +in attesta del blocco X, mentre il secondo read_lock() non ha bisogno di +aspettare perché si tratta di un lettore ricorsivo. Tuttavia, se read_lock() +fosse un lettore non ricorsivo, questo codice produrrebbe uno stallo. + +Da notare che in funzione dell'operazione di blocco usate per l'acquisizione (in +particolare il valore del parametro 'read' in lock_acquire()), un blocco può +essere di scrittura (blocco esclusivo), di lettura non ricorsivo (blocco +condiviso e non ricorsivo), o di lettura ricorsivo (blocco condiviso e +ricorsivo). In altre parole, per un'istanza di blocco esistono tre tipi di +acquisizione che dipendono dalla funzione di acquisizione usata: esclusiva, di +lettura non ricorsiva, e di lettura ricorsiva. + +In breve, chiamiamo "non ricorsivi" blocchi di scrittura e quelli di lettura non +ricorsiva, mentre "ricorsivi" i blocchi di lettura ricorsivi. + +I blocchi ricorsivi non si bloccano a vicenda, mentre quelli non ricorsivi sì +(anche in lettura). Un blocco di lettura non ricorsivi può bloccare uno +ricorsivo, e viceversa. + +Il seguente esempio mostra uno stallo con blocchi ricorsivi:: + + TASK A: TASK B: + + read_lock(X); + read_lock(Y); + write_lock(Y); + write_lock(X); + +Il processo A attende che il processo B esegua read_unlock() so Y, mentre il +processo B attende che A esegua read_unlock() su X. + +Tipi di dipendenze e percorsi forti +----------------------------------- +Le dipendenze fra blocchi tracciano l'ordine con cui una coppia di blocchi viene +acquisita, e perché vi sono 3 tipi di bloccatori, allora avremo 9 tipi di +dipendenze. Tuttavia, vi mostreremo che 4 sono sufficienti per individuare gli +stalli. + +Per ogni dipendenza fra blocchi avremo:: + + L1 -> L2 + +Questo significa che lockdep ha visto acquisire L1 prima di L2 nello stesso +contesto di esecuzione. Per quanto riguarda l'individuazione degli stalli, ci +interessa sapere se possiamo rimanere bloccati da L2 mentre L1 viene trattenuto. +In altre parole, vogliamo sapere se esiste un bloccatore L3 che viene bloccato +da L1 e un L2 che viene bloccato da L3. Dunque, siamo interessati a (1) quello +che L1 blocca e (2) quello che blocca L2. Di conseguenza, possiamo combinare +lettori ricorsivi e non per L1 (perché bloccano gli stessi tipi) e possiamo +combinare scrittori e lettori non ricorsivi per L2 (perché vengono bloccati +dagli stessi tipi). + +Con questa semplificazione, possiamo dedurre che ci sono 4 tipi di rami nel +grafo delle dipendenze di lockdep: + +1) -(ER)->: + dipendenza da scrittore esclusivo a lettore ricorsivo. "X -(ER)-> Y" + significa X -> Y, dove X è uno scrittore e Y un lettore ricorsivo. + +2) -(EN)->: + dipendenza da scrittore esclusivo a bloccatore non ricorsivo. + "X -(EN)->" significa X-> Y, dove X è uno scrittore e Y può essere + o uno scrittore o un lettore non ricorsivo. + +3) -(SR)->: + dipendenza da lettore condiviso a lettore ricorsivo. "X -(SR)->" + significa X -> Y, dove X è un lettore (ricorsivo o meno) e Y è un + lettore ricorsivo. + +4) -(SN)->: + dipendenza da lettore condiviso a bloccatore non ricorsivo. + "X -(SN)-> Y" significa X -> Y , dove X è un lettore (ricorsivo + o meno) e Y può essere o uno scrittore o un lettore non ricorsivo. + +Da notare che presi due blocchi, questi potrebbero avere più dipendenza fra di +loro. Per esempio:: + + TASK A: + + read_lock(X); + write_lock(Y); + ... + + TASK B: + + write_lock(X); + write_lock(Y); + +Nel grafo delle dipendenze avremo sia X -(SN)-> Y che X -(EN)-> Y. + +Usiamo -(xN)-> per rappresentare i rami sia per -(EN)-> che -(SN)->, allo stesso +modo -(Ex)->, -(xR)-> e -(Sx)-> + +Un "percorso" in un grafo è una serie di nodi e degli archi che li congiungono. +Definiamo un percorso "forte", come il percorso che non ha archi (dipendenze) di +tipo -(xR)-> e -(Sx)->. In altre parole, un percorso "forte" è un percorso da un +blocco ad un altro attraverso le varie dipendenze, e se sul percorso abbiamo X +-> Y -> Z (dove X, Y, e Z sono blocchi), e da X a Y si ha una dipendenza -(SR)-> +o -(ER)->, allora fra Y e Z non deve esserci una dipendenza -(SN)-> o -(SR)->. + +Nella prossima sezione vedremo perché definiamo questo percorso "forte". + +Identificazione di stalli da lettura ricorsiva +---------------------------------------------- +Ora vogliamo dimostrare altre due cose: + +Lemma 1: + +Se esiste un percorso chiuso forte (ciclo forte), allora esiste anche una +combinazione di sequenze di blocchi che causa uno stallo. In altre parole, +l'esistenza di un ciclo forte è sufficiente alla scoperta di uno stallo. + +Lemma 2: + +Se non esiste un percorso chiuso forte (ciclo forte), allora non esiste una +combinazione di sequenze di blocchi che causino uno stallo. In altre parole, i +cicli forti sono necessari alla rilevazione degli stallo. + +Con questi due lemmi possiamo facilmente affermare che un percorso chiuso forte +è sia sufficiente che necessario per avere gli stalli, dunque averli equivale +alla possibilità di imbattersi concretamente in uno stallo. Un percorso chiuso +forte significa che può causare stalli, per questo lo definiamo "forte", ma ci +sono anche cicli di dipendenze che non causeranno stalli. + +Dimostrazione di sufficienza (lemma 1): + +Immaginiamo d'avere un ciclo forte:: + + L1 -> L2 ... -> Ln -> L1 + +Questo significa che abbiamo le seguenti dipendenze:: + + L1 -> L2 + L2 -> L3 + ... + Ln-1 -> Ln + Ln -> L1 + +Ora possiamo costruire una combinazione di sequenze di blocchi che causano lo +stallo. + +Per prima cosa facciamo sì che un processo/processore prenda L1 in L1 -> L2, poi +un altro prende L2 in L2 -> L3, e così via. Alla fine, tutti i Lx in Lx -> Lx+1 +saranno trattenuti da processi/processori diversi. + +Poi visto che abbiamo L1 -> L2, chi trattiene L1 vorrà acquisire L2 in L1 -> L2, +ma prima dovrà attendere che venga rilasciato da chi lo trattiene. Questo perché +L2 è già trattenuto da un altro processo/processore, ed in più L1 -> L2 e L2 -> +L3 non sono -(xR)-> né -(Sx)-> (la definizione di forte). Questo significa che L2 +in L1 -> L2 non è un bloccatore non ricorsivo (bloccabile da chiunque), e L2 in +L2 -> L3 non è uno scrittore (che blocca chiunque). + +In aggiunta, possiamo trarre una simile conclusione per chi sta trattenendo L2: +deve aspettare che L3 venga rilasciato, e così via. Ora possiamo dimostrare che +chi trattiene Lx deve aspettare che Lx+1 venga rilasciato. Notiamo che Ln+1 è +L1, dunque si è creato un ciclo dal quale non possiamo uscire, quindi si ha uno +stallo. + +Dimostrazione della necessità (lemma 2): + +Questo lemma equivale a dire che: se siamo in uno scenario di stallo, allora +deve esiste un ciclo forte nel grafo delle dipendenze. + +Secondo Wikipedia[1], se c'è uno stallo, allora deve esserci un ciclo di attese, +ovvero ci sono N processi/processori dove P1 aspetta un blocco trattenuto da P2, +e P2 ne aspetta uno trattenuto da P3, ... e Pn attende che il blocco P1 venga +rilasciato. Chiamiamo Lx il blocco che attende Px, quindi P1 aspetta L1 e +trattiene Ln. Quindi avremo Ln -> L1 nel grafo delle dipendenze. Similarmente, +nel grafo delle dipendenze avremo L1 -> L2, L2 -> L3, ..., Ln-1 -> Ln, il che +significa che abbiamo un ciclo:: + + Ln -> L1 -> L2 -> ... -> Ln + +, ed ora dimostriamo d'avere un ciclo forte. + +Per un blocco Lx, il processo Px contribuisce alla dipendenza Lx-1 -> Lx e Px+1 +contribuisce a quella Lx -> Lx+1. Visto che Px aspetta che Px+1 rilasci Lx, sarà +impossibile che Lx in Px+1 sia un lettore e che Lx in Px sia un lettore +ricorsivo. Questo perché i lettori (ricorsivi o meno) non bloccano lettori +ricorsivi. Dunque, Lx-1 -> Lx e Lx -> Lx+1 non possono essere una coppia di +-(xR)-> -(Sx)->. Questo è vero per ogni ciclo, dunque, questo è un ciclo forte. + +Riferimenti +----------- + +[1]: https://it.wikipedia.org/wiki/Stallo_(informatica) + +[2]: Shibu, K. (2009). Intro To Embedded Systems (1st ed.). Tata McGraw-Hill diff --git a/Documentation/translations/it_IT/locking/lockstat.rst b/Documentation/translations/it_IT/locking/lockstat.rst new file mode 100644 index 0000000000..77972d971d --- /dev/null +++ b/Documentation/translations/it_IT/locking/lockstat.rst @@ -0,0 +1,230 @@ +.. SPDX-License-Identifier: GPL-2.0 + +.. include:: ../disclaimer-ita.rst + +======================= +Statistiche sui blocchi +======================= + +Cosa +==== + +Come suggerisce il nome, fornisce statistiche sui blocchi. + + +Perché +====== + +Perché, tanto per fare un esempio, le contese sui blocchi possono influenzare +significativamente le prestazioni. + +Come +==== + +*Lockdep* ha punti di collegamento nelle funzioni di blocco e inoltre +mappa le istanze di blocco con le relative classi. Partiamo da questo punto +(vedere Documentation/translations/it_IT/locking/lockdep-design.rst). +Il grafico sottostante mostra la relazione che intercorre fra le +funzioni di blocco e i vari punti di collegamenti che ci sono al loro +interno:: + + __acquire + | + lock _____ + | \ + | __contended + | | + | <wait> + | _______/ + |/ + | + __acquired + | + . + <hold> + . + | + __release + | + unlock + + lock, unlock - le classiche funzioni di blocco + __* - i punti di collegamento + <> - stati + +Grazie a questi punti di collegamento possiamo fornire le seguenti statistiche: + +con-bounces + - numero di contese su un blocco che riguarda dati di un processore + +contentions + - numero di acquisizioni di blocchi che hanno dovuto attendere + +wait time + min + - tempo minimo (diverso da zero) che sia mai stato speso in attesa di + un blocco + + max + - tempo massimo che sia mai stato speso in attesa di un blocco + + total + - tempo totale speso in attesa di un blocco + + avg + - tempo medio speso in attesa di un blocco + +acq-bounces + - numero di acquisizioni di blocco che riguardavano i dati su un processore + +acquisitions + - numero di volte che un blocco è stato ottenuto + +hold time + min + - tempo minimo (diverso da zero) che sia mai stato speso trattenendo un blocco + + max + - tempo massimo che sia mai stato speso trattenendo un blocco + + total + - tempo totale di trattenimento di un blocco + + avg + - tempo medio di trattenimento di un blocco + +Questi numeri vengono raccolti per classe di blocco, e per ogni stato di +lettura/scrittura (quando applicabile). + +Inoltre, questa raccolta di statistiche tiene traccia di 4 punti di contesa +per classe di blocco. Un punto di contesa è una chiamata che ha dovuto +aspettare l'acquisizione di un blocco. + +Configurazione +-------------- + +Le statistiche sui blocchi si abilitano usando l'opzione di configurazione +CONFIG_LOCK_STAT. + +Uso +--- + +Abilitare la raccolta di statistiche:: + + # echo 1 >/proc/sys/kernel/lock_stat + +Disabilitare la raccolta di statistiche:: + + # echo 0 >/proc/sys/kernel/lock_stat + +Per vedere le statistiche correnti sui blocchi:: + + ( i numeri di riga non fanno parte dell'output del comando, ma sono stati + aggiunti ai fini di questa spiegazione ) + + # less /proc/lock_stat + + 01 lock_stat version 0.4 + 02----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + 03 class name con-bounces contentions waittime-min waittime-max waittime-total waittime-avg acq-bounces acquisitions holdtime-min holdtime-max holdtime-total holdtime-avg + 04----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + 05 + 06 &mm->mmap_sem-W: 46 84 0.26 939.10 16371.53 194.90 47291 2922365 0.16 2220301.69 17464026916.32 5975.99 + 07 &mm->mmap_sem-R: 37 100 1.31 299502.61 325629.52 3256.30 212344 34316685 0.10 7744.91 95016910.20 2.77 + 08 --------------- + 09 &mm->mmap_sem 1 [<ffffffff811502a7>] khugepaged_scan_mm_slot+0x57/0x280 + 10 &mm->mmap_sem 96 [<ffffffff815351c4>] __do_page_fault+0x1d4/0x510 + 11 &mm->mmap_sem 34 [<ffffffff81113d77>] vm_mmap_pgoff+0x87/0xd0 + 12 &mm->mmap_sem 17 [<ffffffff81127e71>] vm_munmap+0x41/0x80 + 13 --------------- + 14 &mm->mmap_sem 1 [<ffffffff81046fda>] dup_mmap+0x2a/0x3f0 + 15 &mm->mmap_sem 60 [<ffffffff81129e29>] SyS_mprotect+0xe9/0x250 + 16 &mm->mmap_sem 41 [<ffffffff815351c4>] __do_page_fault+0x1d4/0x510 + 17 &mm->mmap_sem 68 [<ffffffff81113d77>] vm_mmap_pgoff+0x87/0xd0 + 18 + 19............................................................................................................................................................................................................................. + 20 + 21 unix_table_lock: 110 112 0.21 49.24 163.91 1.46 21094 66312 0.12 624.42 31589.81 0.48 + 22 --------------- + 23 unix_table_lock 45 [<ffffffff8150ad8e>] unix_create1+0x16e/0x1b0 + 24 unix_table_lock 47 [<ffffffff8150b111>] unix_release_sock+0x31/0x250 + 25 unix_table_lock 15 [<ffffffff8150ca37>] unix_find_other+0x117/0x230 + 26 unix_table_lock 5 [<ffffffff8150a09f>] unix_autobind+0x11f/0x1b0 + 27 --------------- + 28 unix_table_lock 39 [<ffffffff8150b111>] unix_release_sock+0x31/0x250 + 29 unix_table_lock 49 [<ffffffff8150ad8e>] unix_create1+0x16e/0x1b0 + 30 unix_table_lock 20 [<ffffffff8150ca37>] unix_find_other+0x117/0x230 + 31 unix_table_lock 4 [<ffffffff8150a09f>] unix_autobind+0x11f/0x1b0 + +Questo estratto mostra le statistiche delle prime due classi di +blocco. La riga 01 mostra la versione dell'output - la versione +cambierà ogni volta che cambia il formato. Le righe dalla 02 alla 04 +rappresentano l'intestazione con la descrizione delle colonne. Le +statistiche sono mostrate nelle righe dalla 05 alla 18 e dalla 20 +alla 31. Queste statistiche sono divise in due parti: le statistiche, +seguite dai punti di contesa (righe 08 e 13) separati da un divisore. + +Le righe dalla 09 alla 12 mostrano i primi quattro punti di contesa +registrati (il codice che tenta di acquisire un blocco) e le righe +dalla 14 alla 17 mostrano i primi quattro punti contesi registrati +(ovvero codice che ha acquisito un blocco). È possibile che nelle +statistiche manchi il valore *max con-bounces*. + +Il primo blocco (righe dalla 05 alla 18) è di tipo lettura/scrittura e quindi +mostra due righe prima del divisore. I punti di contesa non corrispondono alla +descrizione delle colonne nell'intestazione; essi hanno due colonne: *punti di +contesa* e *[<IP>] simboli*. Il secondo gruppo di punti di contesa sono i punti +con cui si contende il blocco. + +La parte interna del tempo è espressa in us (microsecondi). + +Quando si ha a che fare con blocchi annidati si potrebbero vedere le +sottoclassi di blocco:: + + 32........................................................................................................................................................................................................................... + 33 + 34 &rq->lock: 13128 13128 0.43 190.53 103881.26 7.91 97454 3453404 0.00 401.11 13224683.11 3.82 + 35 --------- + 36 &rq->lock 645 [<ffffffff8103bfc4>] task_rq_lock+0x43/0x75 + 37 &rq->lock 297 [<ffffffff8104ba65>] try_to_wake_up+0x127/0x25a + 38 &rq->lock 360 [<ffffffff8103c4c5>] select_task_rq_fair+0x1f0/0x74a + 39 &rq->lock 428 [<ffffffff81045f98>] scheduler_tick+0x46/0x1fb + 40 --------- + 41 &rq->lock 77 [<ffffffff8103bfc4>] task_rq_lock+0x43/0x75 + 42 &rq->lock 174 [<ffffffff8104ba65>] try_to_wake_up+0x127/0x25a + 43 &rq->lock 4715 [<ffffffff8103ed4b>] double_rq_lock+0x42/0x54 + 44 &rq->lock 893 [<ffffffff81340524>] schedule+0x157/0x7b8 + 45 + 46........................................................................................................................................................................................................................... + 47 + 48 &rq->lock/1: 1526 11488 0.33 388.73 136294.31 11.86 21461 38404 0.00 37.93 109388.53 2.84 + 49 ----------- + 50 &rq->lock/1 11526 [<ffffffff8103ed58>] double_rq_lock+0x4f/0x54 + 51 ----------- + 52 &rq->lock/1 5645 [<ffffffff8103ed4b>] double_rq_lock+0x42/0x54 + 53 &rq->lock/1 1224 [<ffffffff81340524>] schedule+0x157/0x7b8 + 54 &rq->lock/1 4336 [<ffffffff8103ed58>] double_rq_lock+0x4f/0x54 + 55 &rq->lock/1 181 [<ffffffff8104ba65>] try_to_wake_up+0x127/0x25a + +La riga 48 mostra le statistiche per la seconda sottoclasse (/1) della +classe *&irq->lock* (le sottoclassi partono da 0); in questo caso, +come suggerito dalla riga 50, ``double_rq_lock`` tenta di acquisire un blocco +annidato di due spinlock. + +Per vedere i blocco più contesi:: + + # grep : /proc/lock_stat | head + clockevents_lock: 2926159 2947636 0.15 46882.81 1784540466.34 605.41 3381345 3879161 0.00 2260.97 53178395.68 13.71 + tick_broadcast_lock: 346460 346717 0.18 2257.43 39364622.71 113.54 3642919 4242696 0.00 2263.79 49173646.60 11.59 + &mapping->i_mmap_mutex: 203896 203899 3.36 645530.05 31767507988.39 155800.21 3361776 8893984 0.17 2254.15 14110121.02 1.59 + &rq->lock: 135014 136909 0.18 606.09 842160.68 6.15 1540728 10436146 0.00 728.72 17606683.41 1.69 + &(&zone->lru_lock)->rlock: 93000 94934 0.16 59.18 188253.78 1.98 1199912 3809894 0.15 391.40 3559518.81 0.93 + tasklist_lock-W: 40667 41130 0.23 1189.42 428980.51 10.43 270278 510106 0.16 653.51 3939674.91 7.72 + tasklist_lock-R: 21298 21305 0.20 1310.05 215511.12 10.12 186204 241258 0.14 1162.33 1179779.23 4.89 + rcu_node_1: 47656 49022 0.16 635.41 193616.41 3.95 844888 1865423 0.00 764.26 1656226.96 0.89 + &(&dentry->d_lockref.lock)->rlock: 39791 40179 0.15 1302.08 88851.96 2.21 2790851 12527025 0.10 1910.75 3379714.27 0.27 + rcu_node_0: 29203 30064 0.16 786.55 1555573.00 51.74 88963 244254 0.00 398.87 428872.51 1.76 + +Per cancellare le statistiche:: + + # echo 0 > /proc/lock_stat diff --git a/Documentation/translations/it_IT/locking/locktorture.rst b/Documentation/translations/it_IT/locking/locktorture.rst new file mode 100644 index 0000000000..87a0dbeaca --- /dev/null +++ b/Documentation/translations/it_IT/locking/locktorture.rst @@ -0,0 +1,181 @@ +.. SPDX-License-Identifier: GPL-2.0 + +.. include:: ../disclaimer-ita.rst + +============================================ +Funzionamento del test *Kernel Lock Torture* +============================================ + +CONFIG_LOCK_TORTURE_TEST +======================== + +L'opzione di configurazione CONFIG_LOCK_TORTURE_TEST fornisce un +modulo kernel che esegue delle verifiche che *torturano* le primitive di +sincronizzazione del kernel. Se dovesse servire, il modulo kernel, +'locktorture', può essere generato successivamente su un kernel che +volete verificare. Periodicamente le verifiche stampano messaggi tramite +``printk()`` e che quindi possono essere letti tramite ``dmesg`` (magari +filtrate l'output con ``grep "torture"``). La verifica inizia quando +il modulo viene caricato e termina quando viene rimosso. Questo +programma si basa sulle modalità di verifica di RCU tramite rcutorture. + +Questa verifica consiste nella creazione di un certo numero di thread +del kernel che acquisiscono un blocco e lo trattengono per una certa +quantità di tempo così da simulare diversi comportamenti nelle sezioni +critiche. La quantità di contese su un blocco può essere simulata +allargando la sezione critica e/o creando più thread. + + +Parametri del modulo +==================== + +Questo modulo ha i seguenti parametri: + + +Specifici di locktorture +------------------------ + +nwriters_stress + Numero di thread del kernel che stresseranno l'acquisizione + esclusiva dei blocchi (scrittori). Il valore di base è il + doppio del numero di processori attivi presenti. + +nreaders_stress + Numero di thread del kernel che stresseranno l'acquisizione + condivisa dei blocchi (lettori). Il valore di base è lo stesso + di nwriters_stress. Se l'utente non ha specificato + nwriters_stress, allora entrambe i valori corrisponderanno + al numero di processori attivi presenti. + +torture_type + Tipo di blocco da verificare. Di base, solo gli spinlock + verranno verificati. Questo modulo può verificare anche + i seguenti tipi di blocchi: + + - "lock_busted": + Simula un'incorretta implementazione del + blocco. + + - "spin_lock": + coppie di spin_lock() e spin_unlock(). + + - "spin_lock_irq": + coppie di spin_lock_irq() e spin_unlock_irq(). + + - "rw_lock": + coppie di rwlock read/write lock() e unlock(). + + - "rw_lock_irq": + copie di rwlock read/write lock_irq() e + unlock_irq(). + + - "mutex_lock": + coppie di mutex_lock() e mutex_unlock(). + + - "rtmutex_lock": + coppie di rtmutex_lock() e rtmutex_unlock(). + Il kernel deve avere CONFIG_RT_MUTEXES=y. + + - "rwsem_lock": + coppie di semafori read/write down() e up(). + + +Generici dell'ambiente di sviluppo 'torture' (RCU + locking) +------------------------------------------------------------ + +shutdown_secs + Numero di secondi prima che la verifica termini e il sistema + venga spento. Il valore di base è zero, il che disabilita + la possibilità di terminare e spegnere. Questa funzionalità + può essere utile per verifiche automatizzate. + +onoff_interval + Numero di secondi fra ogni tentativo di esecuzione di + un'operazione casuale di CPU-hotplug. Di base è zero, il + che disabilita la funzionalità di CPU-hotplug. Nei kernel + con CONFIG_HOTPLUG_CPU=n, locktorture si rifiuterà, senza + dirlo, di effettuare una qualsiasi operazione di + CPU-hotplug indipendentemente dal valore specificato in + onoff_interval. + +onoff_holdoff + Numero di secondi da aspettare prima di iniziare le + operazioni di CPU-hotplug. Normalmente questo verrebbe + usato solamente quando locktorture è compilato come parte + integrante del kernel ed eseguito automaticamente all'avvio, + in questo caso è utile perché permette di non confondere + l'avvio con i processori che vanno e vengono. Questo + parametro è utile sono se CONFIG_HOTPLUG_CPU è abilitato. + +stat_interval + Numero di secondi fra una stampa (printk()) delle + statistiche e l'altra. Di base, locktorture riporta le + statistiche ogni 60 secondi. Impostando l'intervallo a 0 + ha l'effetto di stampare le statistiche -solo- quando il + modulo viene rimosso. + +stutter + Durata della verifica prima di effettuare una pausa di + eguale durata. Di base "stutter=5", quindi si eseguono + verifiche e pause di (circa) cinque secondi. + L'impostazione di "stutter=0" fa si che la verifica + venga eseguita continuamente senza fermarsi. + +shuffle_interval + Il numero di secondi per cui un thread debba mantenere + l'affinità con un sottoinsieme di processori, di base è + 3 secondi. Viene usato assieme a test_no_idle_hz. + +verbose + Abilita le stampe di debug, via printk(). Di base è + abilitato. Queste informazioni aggiuntive sono per la + maggior parte relative ad errori di alto livello e resoconti + da parte dell'struttura 'torture'. + + +Statistiche +=========== + +Le statistiche vengono stampate secondo il seguente formato:: + + spin_lock-torture: Writes: Total: 93746064 Max/Min: 0/0 Fail: 0 + (A) (B) (C) (D) (E) + + (A): tipo di lock sotto verifica -- parametro torture_type. + + (B): Numero di acquisizione del blocco in scrittura. Se si ha a che fare + con una primitiva di lettura/scrittura apparirà di seguito anche una + seconda voce "Reads" + + (C): Numero di volte che il blocco è stato acquisito + + (D): Numero minimo e massimo di volte che un thread ha fallito + nell'acquisire il blocco + + (E): valori true/false nel caso di errori durante l'acquisizione del blocco. + Questo dovrebbe dare un riscontro positivo -solo- se c'è un baco + nell'implementazione delle primitive di sincronizzazione. Altrimenti un + blocco non dovrebbe mai fallire (per esempio, spin_lock()). + Ovviamente lo stesso si applica per (C). Un semplice esempio è il tipo + "lock_busted". + +Uso +=== + +Il seguente script può essere utilizzato per verificare i blocchi:: + + #!/bin/sh + + modprobe locktorture + sleep 3600 + rmmod locktorture + dmesg | grep torture: + +L'output può essere manualmente ispezionato cercando il marcatore d'errore +"!!!". Ovviamente potreste voler creare degli script più elaborati che +verificano automaticamente la presenza di errori. Il comando "rmmod" forza la +stampa (usando printk()) di "SUCCESS", "FAILURE", oppure "RCU_HOTPLUG". I primi +due si piegano da soli, mentre l'ultimo indica che non stati trovati problemi di +sincronizzazione, tuttavia ne sono stati trovati in CPU-hotplug. + +Consultate anche: Documentation/translations/it_IT/RCU/torture.rst diff --git a/Documentation/translations/it_IT/locking/locktypes.rst b/Documentation/translations/it_IT/locking/locktypes.rst new file mode 100644 index 0000000000..1c7056283b --- /dev/null +++ b/Documentation/translations/it_IT/locking/locktypes.rst @@ -0,0 +1,547 @@ +.. SPDX-License-Identifier: GPL-2.0 + +.. include:: ../disclaimer-ita.rst + +.. _it_kernel_hacking_locktypes: + +======================================== +Tipologie di blocco e le loro istruzioni +======================================== + +Introduzione +============ + +Il kernel fornisce un certo numero di primitive di blocco che possiamo dividere +in tre categorie: + + - blocchi ad attesa con sospensione + - blocchi locali per CPU + - blocchi ad attesa attiva + +Questo documento descrive questi tre tipi e fornisce istruzioni su come +annidarli, ed usarli su kernel PREEMPT_RT. + +Categorie di blocchi +==================== + +Blocchi ad attesa con sospensione +--------------------------------- + +I blocchi ad attesa con sospensione possono essere acquisiti solo in un contesti +dov'è possibile la prelazione. + +Diverse implementazioni permettono di usare try_lock() anche in altri contesti, +nonostante ciò è bene considerare anche la sicurezza dei corrispondenti +unlock(). Inoltre, vanno prese in considerazione anche le varianti di *debug* +di queste primitive. Insomma, non usate i blocchi ad attesa con sospensioni in +altri contesti a meno che proprio non vi siano alternative. + +In questa categoria troviamo: + + - mutex + - rt_mutex + - semaphore + - rw_semaphore + - ww_mutex + - percpu_rw_semaphore + +Nei kernel con PREEMPT_RT, i seguenti blocchi sono convertiti in blocchi ad +attesa con sospensione: + + - local_lock + - spinlock_t + - rwlock_t + +Blocchi locali per CPU +---------------------- + + - local_lock + +Su kernel non-PREEMPT_RT, le funzioni local_lock gestiscono le primitive di +disabilitazione di prelazione ed interruzioni. Al contrario di altri meccanismi, +la disabilitazione della prelazione o delle interruzioni sono puri meccanismi +per il controllo della concorrenza su una CPU e quindi non sono adatti per la +gestione della concorrenza inter-CPU. + +Blocchi ad attesa attiva +------------------------ + + - raw_spinlcok_t + - bit spinlocks + + Nei kernel non-PREEMPT_RT, i seguenti blocchi sono ad attesa attiva: + + - spinlock_t + - rwlock_t + +Implicitamente, i blocchi ad attesa attiva disabilitano la prelazione e le +funzioni lock/unlock hanno anche dei suffissi per gestire il livello di +protezione: + + =================== ========================================================================= + _bh() disabilita / abilita *bottom halves* (interruzioni software) + _irq() disabilita / abilita le interruzioni + _irqsave/restore() salva e disabilita le interruzioni / ripristina ed attiva le interruzioni + =================== ========================================================================= + +Semantica del proprietario +========================== + +Eccetto i semafori, i sopracitati tipi di blocchi hanno tutti una semantica +molto stringente riguardo al proprietario di un blocco: + + Il contesto (attività) che ha acquisito il blocco deve rilasciarlo + +I semafori rw_semaphores hanno un'interfaccia speciale che permette anche ai non +proprietari del blocco di rilasciarlo per i lettori. + +rtmutex +======= + +I blocchi a mutua esclusione RT (*rtmutex*) sono un sistema a mutua esclusione +con supporto all'ereditarietà della priorità (PI). + +Questo meccanismo ha delle limitazioni sui kernel non-PREEMPT_RT dovuti alla +prelazione e alle sezioni con interruzioni disabilitate. + +Chiaramente, questo meccanismo non può avvalersi della prelazione su una sezione +dove la prelazione o le interruzioni sono disabilitate; anche sui kernel +PREEMPT_RT. Tuttavia, i kernel PREEMPT_RT eseguono la maggior parte delle +sezioni in contesti dov'è possibile la prelazione, specialmente in contesti +d'interruzione (anche software). Questa conversione permette a spinlock_t e +rwlock_t di essere implementati usando rtmutex. + +semaphore +========= + +La primitiva semaphore implementa un semaforo con contatore. + +I semafori vengono spesso utilizzati per la serializzazione e l'attesa, ma per +nuovi casi d'uso si dovrebbero usare meccanismi diversi, come mutex e +completion. + +semaphore e PREEMPT_RT +---------------------- + +I kernel PREEMPT_RT non cambiano l'implementazione di semaphore perché non hanno +un concetto di proprietario, dunque impediscono a PREEMPT_RT d'avere +l'ereditarietà della priorità sui semafori. Un proprietario sconosciuto non può +ottenere una priorità superiore. Di consequenza, bloccarsi sui semafori porta +all'inversione di priorità. + + +rw_semaphore +============ + +Il blocco rw_semaphore è un meccanismo che permette più lettori ma un solo scrittore. + +Sui kernel non-PREEMPT_RT l'implementazione è imparziale, quindi previene +l'inedia dei processi scrittori. + +Questi blocchi hanno una semantica molto stringente riguardo il proprietario, ma +offre anche interfacce speciali che permettono ai processi non proprietari di +rilasciare un processo lettore. Queste interfacce funzionano indipendentemente +dalla configurazione del kernel. + +rw_semaphore e PREEMPT_RT +------------------------- + +I kernel PREEMPT_RT sostituiscono i rw_semaphore con un'implementazione basata +su rt_mutex, e questo ne modifica l'imparzialità: + + Dato che uno scrittore rw_semaphore non può assicurare la propria priorità ai + suoi lettori, un lettore con priorità più bassa che ha subito la prelazione + continuerà a trattenere il blocco, quindi porta all'inedia anche gli scrittori + con priorità più alta. Per contro, dato che i lettori possono garantire la + propria priorità agli scrittori, uno scrittore a bassa priorità che subisce la + prelazione vedrà la propria priorità alzata finché non rilascerà il blocco, e + questo preverrà l'inedia dei processi lettori a causa di uno scrittore. + + +local_lock +========== + +I local_lock forniscono nomi agli ambiti di visibilità delle sezioni critiche +protette tramite la disattivazione della prelazione o delle interruzioni. + +Sui kernel non-PREEMPT_RT le operazioni local_lock si traducono +nell'abilitazione o disabilitazione della prelazione o le interruzioni. + + =============================== ====================== + local_lock(&llock) preempt_disable() + local_unlock(&llock) preempt_enable() + local_lock_irq(&llock) local_irq_disable() + local_unlock_irq(&llock) local_irq_enable() + local_lock_irqsave(&llock) local_irq_save() + local_unlock_irqrestore(&llock) local_irq_restore() + =============================== ====================== + +Gli ambiti di visibilità con nome hanno due vantaggi rispetto alle primitive di +base: + + - Il nome del blocco permette di fare un'analisi statica, ed è anche chiaro su + cosa si applichi la protezione cosa che invece non si può fare con le + classiche primitive in quanto sono opache e senza alcun ambito di + visibilità. + + - Se viene abilitato lockdep, allora local_lock ottiene un lockmap che + permette di verificare la bontà della protezione. Per esempio, questo può + identificare i casi dove una funzione usa preempt_disable() come meccanismo + di protezione in un contesto d'interruzione (anche software). A parte + questo, lockdep_assert_held(&llock) funziona come tutte le altre primitive + di sincronizzazione. + +local_lock e PREEMPT_RT +------------------------- + +I kernel PREEMPT_RT sostituiscono local_lock con uno spinlock_t per CPU, quindi +ne cambia la semantica: + + - Tutte le modifiche a spinlock_t si applicano anche a local_lock + +L'uso di local_lock +------------------- + +I local_lock dovrebbero essere usati su kernel non-PREEMPT_RT quando la +disabilitazione della prelazione o delle interruzioni è il modo più adeguato per +gestire l'accesso concorrente a strutture dati per CPU. + +Questo meccanismo non è adatto alla protezione da prelazione o interruzione su +kernel PREEMPT_RT dato che verrà convertito in spinlock_t. + + +raw_spinlock_t e spinlock_t +=========================== + +raw_spinlock_t +-------------- + +I blocco raw_spinlock_t è un blocco ad attesa attiva su tutti i tipi di kernel, +incluso quello PREEMPT_RT. Usate raw_spinlock_t solo in sezioni critiche nel +cuore del codice, nella gestione delle interruzioni di basso livello, e in posti +dove è necessario disabilitare la prelazione o le interruzioni. Per esempio, per +accedere in modo sicuro lo stato dell'hardware. A volte, i raw_spinlock_t +possono essere usati quando la sezione critica è minuscola, per evitare gli +eccessi di un rtmutex. + +spinlock_t +---------- + +Il significato di spinlock_t cambia in base allo stato di PREEMPT_RT. + +Sui kernel non-PREEMPT_RT, spinlock_t si traduce in un raw_spinlock_t ed ha +esattamente lo stesso significato. + +spinlock_t e PREEMPT_RT +----------------------- + +Sui kernel PREEMPT_RT, spinlock_t ha un'implementazione dedicata che si basa +sull'uso di rt_mutex. Questo ne modifica il significato: + + - La prelazione non viene disabilitata. + + - I suffissi relativi alla interruzioni (_irq, _irqsave / _irqrestore) per le + operazioni spin_lock / spin_unlock non hanno alcun effetto sullo stato delle + interruzioni della CPU. + + - I suffissi relativi alle interruzioni software (_bh()) disabilitano i + relativi gestori d'interruzione. + + I kernel non-PREEMPT_RT disabilitano la prelazione per ottenere lo stesso effetto. + + I kernel PREEMPT_RT usano un blocco per CPU per la serializzazione, il che + permette di tenere attiva la prelazione. Il blocco disabilita i gestori + d'interruzione software e previene la rientranza vista la prelazione attiva. + +A parte quanto appena discusso, i kernel PREEMPT_RT preservano il significato +di tutti gli altri aspetti di spinlock_t: + + - Le attività che trattengono un blocco spinlock_t non migrano su altri + processori. Disabilitando la prelazione, i kernel non-PREEMPT_RT evitano la + migrazione. Invece, i kernel PREEMPT_RT disabilitano la migrazione per + assicurarsi che i puntatori a variabili per CPU rimangano validi anche + quando un'attività subisce la prelazione. + + - Lo stato di un'attività si mantiene durante le acquisizioni del blocco al + fine di garantire che le regole basate sullo stato delle attività si possano + applicare a tutte le configurazioni del kernel. I kernel non-PREEMPT_RT + lasciano lo stato immutato. Tuttavia, la funzionalità PREEMPT_RT deve + cambiare lo stato se l'attività si blocca durante l'acquisizione. Dunque, + salva lo stato attuale prima di bloccarsi ed il rispettivo risveglio lo + ripristinerà come nell'esempio seguente:: + + task->state = TASK_INTERRUPTIBLE + lock() + block() + task->saved_state = task->state + task->state = TASK_UNINTERRUPTIBLE + schedule() + lock wakeup + task->state = task->saved_state + + Altri tipi di risvegli avrebbero impostato direttamente lo stato a RUNNING, + ma in questo caso non avrebbe funzionato perché l'attività deve rimanere + bloccata fintanto che il blocco viene trattenuto. Quindi, lo stato salvato + viene messo a RUNNING quando il risveglio di un non-blocco cerca di + risvegliare un'attività bloccata in attesa del rilascio di uno spinlock. Poi, + quando viene completata l'acquisizione del blocco, il suo risveglio + ripristinerà lo stato salvato, in questo caso a RUNNING:: + + task->state = TASK_INTERRUPTIBLE + lock() + block() + task->saved_state = task->state + task->state = TASK_UNINTERRUPTIBLE + schedule() + non lock wakeup + task->saved_state = TASK_RUNNING + + lock wakeup + task->state = task->saved_state + + Questo garantisce che il vero risveglio non venga perso. + +rwlock_t +======== + +Il blocco rwlock_t è un meccanismo che permette più lettori ma un solo scrittore. + +Sui kernel non-PREEMPT_RT questo è un blocco ad attesa e per i suoi suffissi si +applicano le stesse regole per spinlock_t. La sua implementazione è imparziale, +quindi previene l'inedia dei processi scrittori. + +rwlock_t e PREEMPT_RT +--------------------- + +Sui kernel PREEMPT_RT rwlock_t ha un'implementazione dedicata che si basa +sull'uso di rt_mutex. Questo ne modifica il significato: + + - Tutte le modifiche fatte a spinlock_t si applicano anche a rwlock_t. + + - Dato che uno scrittore rw_semaphore non può assicurare la propria priorità ai + suoi lettori, un lettore con priorità più bassa che ha subito la prelazione + continuerà a trattenere il blocco, quindi porta all'inedia anche gli + scrittori con priorità più alta. Per contro, dato che i lettori possono + garantire la propria priorità agli scrittori, uno scrittore a bassa priorità + che subisce la prelazione vedrà la propria priorità alzata finché non + rilascerà il blocco, e questo preverrà l'inedia dei processi lettori a causa + di uno scrittore. + + +Precisazioni su PREEMPT_RT +========================== + +local_lock su RT +---------------- + +Sui kernel PREEMPT_RT Ci sono alcune implicazioni dovute alla conversione di +local_lock in un spinlock_t. Per esempio, su un kernel non-PREEMPT_RT il +seguente codice funzionerà come ci si aspetta:: + + local_lock_irq(&local_lock); + raw_spin_lock(&lock); + +ed è equivalente a:: + + raw_spin_lock_irq(&lock); + +Ma su un kernel PREEMPT_RT questo codice non funzionerà perché local_lock_irq() +si traduce in uno spinlock_t per CPU che non disabilita né le interruzioni né la +prelazione. Il seguente codice funzionerà su entrambe i kernel con o senza +PREEMPT_RT:: + + local_lock_irq(&local_lock); + spin_lock(&lock); + +Un altro dettaglio da tenere a mente con local_lock è che ognuno di loro ha un +ambito di protezione ben preciso. Dunque, la seguente sostituzione è errate:: + + + func1() + { + local_irq_save(flags); -> local_lock_irqsave(&local_lock_1, flags); + func3(); + local_irq_restore(flags); -> local_unlock_irqrestore(&local_lock_1, flags); + } + + func2() + { + local_irq_save(flags); -> local_lock_irqsave(&local_lock_2, flags); + func3(); + local_irq_restore(flags); -> local_unlock_irqrestore(&local_lock_2, flags); + } + + func3() + { + lockdep_assert_irqs_disabled(); + access_protected_data(); + } + +Questo funziona correttamente su un kernel non-PREEMPT_RT, ma su un kernel +PREEMPT_RT local_lock_1 e local_lock_2 sono distinti e non possono serializzare +i chiamanti di func3(). L'*assert* di lockdep verrà attivato su un kernel +PREEMPT_RT perché local_lock_irqsave() non disabilita le interruzione a casa +della specifica semantica di spinlock_t in PREEMPT_RT. La corretta sostituzione +è:: + + func1() + { + local_irq_save(flags); -> local_lock_irqsave(&local_lock, flags); + func3(); + local_irq_restore(flags); -> local_unlock_irqrestore(&local_lock, flags); + } + + func2() + { + local_irq_save(flags); -> local_lock_irqsave(&local_lock, flags); + func3(); + local_irq_restore(flags); -> local_unlock_irqrestore(&local_lock, flags); + } + + func3() + { + lockdep_assert_held(&local_lock); + access_protected_data(); + } + +spinlock_t e rwlock_t +--------------------- + +Ci sono alcune conseguenze di cui tener conto dal cambiamento di semantica di +spinlock_t e rwlock_t sui kernel PREEMPT_RT. Per esempio, sui kernel non +PREEMPT_RT il seguente codice funziona come ci si aspetta:: + + local_irq_disable(); + spin_lock(&lock); + +ed è equivalente a:: + + spin_lock_irq(&lock); + +Lo stesso vale per rwlock_t e le varianti con _irqsave(). + +Sui kernel PREEMPT_RT questo codice non funzionerà perché gli rtmutex richiedono +un contesto con la possibilità di prelazione. Al suo posto, usate +spin_lock_irq() o spin_lock_irqsave() e le loro controparti per il rilascio. I +kernel PREEMPT_RT offrono un meccanismo local_lock per i casi in cui la +disabilitazione delle interruzioni ed acquisizione di un blocco devono rimanere +separati. Acquisire un local_lock àncora un processo ad una CPU permettendo cose +come un'acquisizione di un blocco con interruzioni disabilitate per singola CPU. + +Il tipico scenario è quando si vuole proteggere una variabile di processore nel +contesto di un thread:: + + + struct foo *p = get_cpu_ptr(&var1); + + spin_lock(&p->lock); + p->count += this_cpu_read(var2); + +Questo codice è corretto su un kernel non-PREEMPT_RT, ma non lo è su un +PREEMPT_RT. La modifica della semantica di spinlock_t su PREEMPT_RT non permette +di acquisire p->lock perché, implicitamente, get_cpu_ptr() disabilita la +prelazione. La seguente sostituzione funzionerà su entrambe i kernel:: + + struct foo *p; + + migrate_disable(); + p = this_cpu_ptr(&var1); + spin_lock(&p->lock); + p->count += this_cpu_read(var2); + +La funzione migrate_disable() assicura che il processo venga tenuto sulla CPU +corrente, e di conseguenza garantisce che gli accessi per-CPU alle variabili var1 e +var2 rimangano sulla stessa CPU fintanto che il processo rimane prelabile. + +La sostituzione con migrate_disable() non funzionerà nel seguente scenario:: + + func() + { + struct foo *p; + + migrate_disable(); + p = this_cpu_ptr(&var1); + p->val = func2(); + +Questo non funziona perché migrate_disable() non protegge dal ritorno da un +processo che aveva avuto il diritto di prelazione. Una sostituzione più adatta +per questo caso è:: + + func() + { + struct foo *p; + + local_lock(&foo_lock); + p = this_cpu_ptr(&var1); + p->val = func2(); + +Su un kernel non-PREEMPT_RT, questo codice protegge dal rientro disabilitando la +prelazione. Su un kernel PREEMPT_RT si ottiene lo stesso risultato acquisendo lo +spinlock di CPU. + +raw_spinlock_t su RT +-------------------- + +Acquisire un raw_spinlock_t disabilita la prelazione e possibilmente anche le +interruzioni, quindi la sezione critica deve evitare di acquisire uno spinlock_t +o rwlock_t. Per esempio, la sezione critica non deve fare allocazioni di +memoria. Su un kernel non-PREEMPT_RT il seguente codice funziona perfettamente:: + + raw_spin_lock(&lock); + p = kmalloc(sizeof(*p), GFP_ATOMIC); + +Ma lo stesso codice non funziona su un kernel PREEMPT_RT perché l'allocatore di +memoria può essere oggetto di prelazione e quindi non può essere chiamato in un +contesto atomico. Tuttavia, si può chiamare l'allocatore di memoria quando si +trattiene un blocco *non-raw* perché non disabilitano la prelazione sui kernel +PREEMPT_RT:: + + spin_lock(&lock); + p = kmalloc(sizeof(*p), GFP_ATOMIC); + + +bit spinlocks +------------- + +I kernel PREEMPT_RT non possono sostituire i bit spinlock perché un singolo bit +è troppo piccolo per farci stare un rtmutex. Dunque, la semantica dei bit +spinlock è mantenuta anche sui kernel PREEMPT_RT. Quindi, le precisazioni fatte +per raw_spinlock_t valgono anche qui. + +In PREEMPT_RT, alcuni bit spinlock sono sostituiti con normali spinlock_t usando +condizioni di preprocessore in base a dove vengono usati. Per contro, questo non +serve quando si sostituiscono gli spinlock_t. Invece, le condizioni poste sui +file d'intestazione e sul cuore dell'implementazione della sincronizzazione +permettono al compilatore di effettuare la sostituzione in modo trasparente. + + +Regole d'annidamento dei tipi di blocchi +======================================== + +Le regole principali sono: + + - I tipi di blocco appartenenti alla stessa categoria possono essere annidati + liberamente a patto che si rispetti l'ordine di blocco al fine di evitare + stalli. + + - I blocchi con sospensione non possono essere annidati in blocchi del tipo + CPU locale o ad attesa attiva + + - I blocchi ad attesa attiva e su CPU locale possono essere annidati nei + blocchi ad attesa con sospensione. + + - I blocchi ad attesa attiva possono essere annidati in qualsiasi altro tipo. + +Queste limitazioni si applicano ad entrambe i kernel con o senza PREEMPT_RT. + +Il fatto che un kernel PREEMPT_RT cambi i blocchi spinlock_t e rwlock_t dal tipo +ad attesa attiva a quello con sospensione, e che sostituisca local_lock con uno +spinlock_t per CPU, significa che non possono essere acquisiti quando si è in un +blocco raw_spinlock. Ne consegue il seguente ordine d'annidamento: + + 1) blocchi ad attesa con sospensione + 2) spinlock_t, rwlock_t, local_lock + 3) raw_spinlock_t e bit spinlocks + +Se queste regole verranno violate, allora lockdep se ne accorgerà e questo sia +con o senza PREEMPT_RT. diff --git a/Documentation/translations/it_IT/networking/netdev-FAQ.rst b/Documentation/translations/it_IT/networking/netdev-FAQ.rst deleted file mode 100644 index 8a1e049585..0000000000 --- a/Documentation/translations/it_IT/networking/netdev-FAQ.rst +++ /dev/null @@ -1,13 +0,0 @@ -.. include:: ../disclaimer-ita.rst - -:Original: :ref:`Documentation/process/maintainer-netdev.rst <netdev-FAQ>` - -.. _it_netdev-FAQ: - -========== -netdev FAQ -========== - -.. warning:: - - TODO ancora da tradurre diff --git a/Documentation/translations/it_IT/process/coding-style.rst b/Documentation/translations/it_IT/process/coding-style.rst index 5f244e16f5..284a75ac19 100644 --- a/Documentation/translations/it_IT/process/coding-style.rst +++ b/Documentation/translations/it_IT/process/coding-style.rst @@ -575,9 +575,9 @@ due parti ``err_free_bar:`` e ``err_free_foo:``: .. code-block:: c - err_free_bar: + err_free_bar: kfree(foo->bar); - err_free_foo: + err_free_foo: kfree(foo); return ret; @@ -671,7 +671,7 @@ segue nel vostro file .emacs: (c-offsets-alist . ( (arglist-close . c-lineup-arglist-tabs-only) (arglist-cont-nonempty . - (c-lineup-gcc-asm-reg c-lineup-arglist-tabs-only)) + (c-lineup-gcc-asm-reg c-lineup-arglist-tabs-only)) (arglist-intro . +) (brace-list-intro . +) (c . c-lineup-C-comments) diff --git a/Documentation/translations/it_IT/subsystem-apis.rst b/Documentation/translations/it_IT/subsystem-apis.rst new file mode 100644 index 0000000000..d179af60c2 --- /dev/null +++ b/Documentation/translations/it_IT/subsystem-apis.rst @@ -0,0 +1,47 @@ +.. SPDX-License-Identifier: GPL-2.0 + +========================================== +Documentazione dei sottosistemi del kernel +========================================== + +In questa parte della documentazione si entra nel dettaglio di come funzionano +i sottosistemi specifici del kernel dal punto di vista di uno sviluppatore del +kernel. Molte delle informazioni qui contenute provengono direttamente dai +sorgenti del kernel, con aggiunte di materiale dove è necessario (anche se +talora *non* è stato aggiunto tutto ciò che era necessario). + +Sottosistemi principali +----------------------- + +.. toctree:: + :maxdepth: 1 + + core-api/index + +Interfacce uomo-macchina +------------------------ + +.. toctree:: + :maxdepth: 1 + + +Interfacce di rete +------------------ + +.. toctree:: + :maxdepth: 1 + +Interfacce per l'archiviazione +------------------------------ + +.. toctree:: + :maxdepth: 1 + + +Interfacce varie +---------------- + +.. toctree:: + :maxdepth: 1 + + i2c/index diff --git a/Documentation/translations/ja_JP/index.rst b/Documentation/translations/ja_JP/index.rst index 43b9fb7246..0b476b429e 100644 --- a/Documentation/translations/ja_JP/index.rst +++ b/Documentation/translations/ja_JP/index.rst @@ -11,7 +11,7 @@ .. toctree:: :maxdepth: 1 - howto + process/howto .. raw:: latex diff --git a/Documentation/translations/ja_JP/howto.rst b/Documentation/translations/ja_JP/process/howto.rst index 8d856ebe87..8d856ebe87 100644 --- a/Documentation/translations/ja_JP/howto.rst +++ b/Documentation/translations/ja_JP/process/howto.rst diff --git a/Documentation/translations/sp_SP/process/coding-style.rst b/Documentation/translations/sp_SP/process/coding-style.rst index a0261ba5b9..a372747643 100644 --- a/Documentation/translations/sp_SP/process/coding-style.rst +++ b/Documentation/translations/sp_SP/process/coding-style.rst @@ -604,9 +604,9 @@ Normalmente la solución para esto es dividirlo en dos etiquetas de error .. code-block:: c - err_free_bar: + err_free_bar: kfree(foo->bar); - err_free_foo: + err_free_foo: kfree(foo); return ret; @@ -698,7 +698,7 @@ sanos. Para hacer esto último, puede pegar lo siguiente en su archivo (c-offsets-alist . ( (arglist-close . c-lineup-arglist-tabs-only) (arglist-cont-nonempty . - (c-lineup-gcc-asm-reg c-lineup-arglist-tabs-only)) + (c-lineup-gcc-asm-reg c-lineup-arglist-tabs-only)) (arglist-intro . +) (brace-list-intro . +) (c . c-lineup-C-comments) diff --git a/Documentation/translations/sp_SP/process/embargoed-hardware-issues.rst b/Documentation/translations/sp_SP/process/embargoed-hardware-issues.rst index c261b428b3..7d4d694967 100644 --- a/Documentation/translations/sp_SP/process/embargoed-hardware-issues.rst +++ b/Documentation/translations/sp_SP/process/embargoed-hardware-issues.rst @@ -273,7 +273,7 @@ revelada involucrada. La lista de embajadores actuales: IBM Power Anton Blanchard <anton@linux.ibm.com> IBM Z Christian Borntraeger <borntraeger@de.ibm.com> Intel Tony Luck <tony.luck@intel.com> - Qualcomm Trilok Soni <tsoni@codeaurora.org> + Qualcomm Trilok Soni <quic_tsoni@quicinc.com> Samsung Javier González <javier.gonz@samsung.com> Microsoft James Morris <jamorris@linux.microsoft.com> diff --git a/Documentation/translations/sp_SP/process/researcher-guidelines.rst b/Documentation/translations/sp_SP/process/researcher-guidelines.rst index 462b3290b7..deccc908a6 100644 --- a/Documentation/translations/sp_SP/process/researcher-guidelines.rst +++ b/Documentation/translations/sp_SP/process/researcher-guidelines.rst @@ -147,4 +147,4 @@ Si no se puede encontrar a nadie para revisar internamente los parches y necesit ayuda para encontrar a esa persona, o si tiene alguna otra pregunta relacionada con este documento y las expectativas de la comunidad de desarrolladores, por favor contacte con la lista de correo privada Technical Advisory Board: -<tech-board@lists.linux-foundation.org>. +<tech-board@groups.linuxfoundation.org>. diff --git a/Documentation/translations/zh_CN/admin-guide/mm/damon/usage.rst b/Documentation/translations/zh_CN/admin-guide/mm/damon/usage.rst index 17b9949d9b..da2745464e 100644 --- a/Documentation/translations/zh_CN/admin-guide/mm/damon/usage.rst +++ b/Documentation/translations/zh_CN/admin-guide/mm/damon/usage.rst @@ -344,7 +344,7 @@ debugfs接口 :ref:`sysfs接口<sysfs_interface>`。 DAMON导出了八个文件, ``attrs``, ``target_ids``, ``init_regions``, -``schemes``, ``monitor_on``, ``kdamond_pid``, ``mk_contexts`` 和 +``schemes``, ``monitor_on_DEPRECATED``, ``kdamond_pid``, ``mk_contexts`` 和 ``rm_contexts`` under its debugfs directory, ``<debugfs>/damon/``. @@ -521,15 +521,15 @@ DAMON导出了八个文件, ``attrs``, ``target_ids``, ``init_regions``, 开关 ---- -除非你明确地启动监测,否则如上所述的文件设置不会产生效果。你可以通过写入和读取 ``monitor_on`` +除非你明确地启动监测,否则如上所述的文件设置不会产生效果。你可以通过写入和读取 ``monitor_on_DEPRECATED`` 文件来启动、停止和检查监测的当前状态。写入 ``on`` 该文件可以启动对有属性的目标的监测。写入 ``off`` 该文件则停止这些目标。如果每个目标进程被终止,DAMON也会停止。下面的示例命令开启、关 闭和检查DAMON的状态:: # cd <debugfs>/damon - # echo on > monitor_on - # echo off > monitor_on - # cat monitor_on + # echo on > monitor_on_DEPRECATED + # echo off > monitor_on_DEPRECATED + # cat monitor_on_DEPRECATED off 请注意,当监测开启时,你不能写到上述的debugfs文件。如果你在DAMON运行时写到这些文件,将会返 @@ -543,11 +543,11 @@ DAMON通过一个叫做kdamond的内核线程来进行请求监测。你可以 得该线程的 ``pid`` 。当监测被 ``关闭`` 时,读取该文件不会返回任何信息:: # cd <debugfs>/damon - # cat monitor_on + # cat monitor_on_DEPRECATED off # cat kdamond_pid none - # echo on > monitor_on + # echo on > monitor_on_DEPRECATED # cat kdamond_pid 18594 @@ -574,7 +574,7 @@ DAMON通过一个叫做kdamond的内核线程来进行请求监测。你可以 # ls foo # ls: cannot access 'foo': No such file or directory -注意, ``mk_contexts`` 、 ``rm_contexts`` 和 ``monitor_on`` 文件只在根目录下。 +注意, ``mk_contexts`` 、 ``rm_contexts`` 和 ``monitor_on_DEPRECATED`` 文件只在根目录下。 监测结果的监测点 @@ -583,9 +583,9 @@ DAMON通过一个叫做kdamond的内核线程来进行请求监测。你可以 DAMON通过一个tracepoint ``damon:damon_aggregated`` 提供监测结果. 当监测开启时,你可 以记录追踪点事件,并使用追踪点支持工具如perf显示结果。比如说:: - # echo on > monitor_on + # echo on > monitor_on_DEPRECATED # perf record -e damon:damon_aggregated & # sleep 5 # kill 9 $(pidof perf) - # echo off > monitor_on + # echo off > monitor_on_DEPRECATED # perf script diff --git a/Documentation/translations/zh_CN/core-api/workqueue.rst b/Documentation/translations/zh_CN/core-api/workqueue.rst index 7fac6f75d0..fe0ff5a127 100644 --- a/Documentation/translations/zh_CN/core-api/workqueue.rst +++ b/Documentation/translations/zh_CN/core-api/workqueue.rst @@ -7,12 +7,13 @@ 司延腾 Yanteng Si <siyanteng@loongson.cn> 周彬彬 Binbin Zhou <zhoubinbin@loongson.cn> + 陈兴友 Xingyou Chen <rockrush@rockwork.org> .. _cn_workqueue.rst: -========================= -并发管理的工作队列 (cmwq) -========================= +======== +工作队列 +======== :日期: September, 2010 :作者: Tejun Heo <tj@kernel.org> @@ -22,7 +23,7 @@ 简介 ==== -在很多情况下,需要一个异步进程的执行环境,工作队列(wq)API是这种情况下 +在很多情况下,需要一个异步的程序执行环境,工作队列(wq)API是这种情况下 最常用的机制。 当需要这样一个异步执行上下文时,一个描述将要执行的函数的工作项(work, @@ -34,8 +35,8 @@ 队列时,工作者又开始执行。 -为什么要cmwq? -============= +为什么要有并发管理工作队列? +=========================== 在最初的wq实现中,多线程(MT)wq在每个CPU上有一个工作者线程,而单线程 (ST)wq在全系统有一个工作者线程。一个MT wq需要保持与CPU数量相同的工 @@ -73,9 +74,11 @@ 向该函数的工作项,并在工作队列中排队等待该工作项。(就是挂到workqueue 队列里面去) -特定目的线程,称为工作线程(工作者),一个接一个地执行队列中的功能。 -如果没有工作项排队,工作者线程就会闲置。这些工作者线程被管理在所谓 -的工作者池中。 +工作项可以在线程或BH(软中断)上下文中执行。 + +对于由线程执行的工作队列,被称为(内核)工作者([k]worker)的特殊 +线程会依次执行其中的函数。如果没有工作项排队,工作者线程就会闲置。 +这些工作者线程被管理在所谓的工作者池中。 cmwq设计区分了面向用户的工作队列,子系统和驱动程序在上面排队工作, 以及管理工作者池和处理排队工作项的后端机制。 @@ -84,6 +87,10 @@ cmwq设计区分了面向用户的工作队列,子系统和驱动程序在上 优先级的工作项,还有一些额外的工作者池,用于服务未绑定工作队列的工 作项目——这些后备池的数量是动态的。 +BH工作队列使用相同的结构。然而,由于同一时间只可能有一个执行上下文, +不需要担心并发问题。每个CPU上的BH工作者池只包含一个用于表示BH执行 +上下文的虚拟工作者。BH工作队列可以被看作软中断的便捷接口。 + 当他们认为合适的时候,子系统和驱动程序可以通过特殊的 ``workqueue API`` 函数创建和排队工作项。他们可以通过在工作队列上 设置标志来影响工作项执行方式的某些方面,他们把工作项放在那里。这些 @@ -95,9 +102,9 @@ cmwq设计区分了面向用户的工作队列,子系统和驱动程序在上 否则一个绑定的工作队列的工作项将被排在与发起线程运行的CPU相关的普 通或高级工作工作者池的工作项列表中。 -对于任何工作者池的实施,管理并发水平(有多少执行上下文处于活动状 -态)是一个重要问题。最低水平是为了节省资源,而饱和水平是指系统被 -充分使用。 +对于任何线程池的实施,管理并发水平(有多少执行上下文处于活动状 +态)是一个重要问题。cmwq试图将并发保持在一个尽可能低且充足的 +水平。最低水平是为了节省资源,而充足是为了使系统能被充分使用。 每个与实际CPU绑定的worker-pool通过钩住调度器来实现并发管理。每当 一个活动的工作者被唤醒或睡眠时,工作者池就会得到通知,并跟踪当前可 @@ -140,6 +147,17 @@ workqueue将自动创建与属性相匹配的后备工作者池。调节并发 ``flags`` --------- +``WQ_BH`` + BH工作队列可以被看作软中断的便捷接口。它总是每个CPU一份, + 其中的各个工作项也会按在队列中的顺序,被所属CPU在软中断 + 上下文中执行。 + + BH工作队列的 ``max_active`` 值必须为0,且只能单独或和 + ``WQ_HIGHPRI`` 标志组合使用。 + + BH工作项不可以睡眠。像延迟排队、冲洗、取消等所有其他特性 + 都是支持的。 + ``WQ_UNBOUND`` 排队到非绑定wq的工作项由特殊的工作者池提供服务,这些工作者不 绑定在任何特定的CPU上。这使得wq表现得像一个简单的执行环境提 @@ -184,25 +202,21 @@ workqueue将自动创建与属性相匹配的后备工作者池。调节并发 -------------- ``@max_active`` 决定了每个CPU可以分配给wq的工作项的最大执行上 -下文数量。例如,如果 ``@max_active为16`` ,每个CPU最多可以同 -时执行16个wq的工作项。 +下文数量。例如,如果 ``@max_active`` 为16 ,每个CPU最多可以同 +时执行16个wq的工作项。它总是每CPU属性,即便对于未绑定 wq。 -目前,对于一个绑定的wq, ``@max_active`` 的最大限制是512,当指 -定为0时使用的默认值是256。对于非绑定的wq,其限制是512和 -4 * ``num_possible_cpus()`` 中的较高值。这些值被选得足够高,所 -以它们不是限制性因素,同时会在失控情况下提供保护。 +``@max_active`` 的最大限制是512,当指定为0时使用的默认值是256。 +这些值被选得足够高,所以它们不是限制性因素,同时会在失控情况下提供 +保护。 一个wq的活动工作项的数量通常由wq的用户来调节,更具体地说,是由用 户在同一时间可以排列多少个工作项来调节。除非有特定的需求来控制活动 工作项的数量,否则建议指定 为"0"。 -一些用户依赖于ST wq的严格执行顺序。 ``@max_active`` 为1和 ``WQ_UNBOUND`` -的组合用来实现这种行为。这种wq上的工作项目总是被排到未绑定的工作池 -中,并且在任何时候都只有一个工作项目处于活动状态,从而实现与ST wq相 -同的排序属性。 - -在目前的实现中,上述配置只保证了特定NUMA节点内的ST行为。相反, -``alloc_ordered_workqueue()`` 应该被用来实现全系统的ST行为。 +一些用户依赖于任意时刻最多只有一个工作项被执行,且各工作项被按队列中 +顺序处理带来的严格执行顺序。``@max_active`` 为1和 ``WQ_UNBOUND`` +的组合曾被用来实现这种行为,现在不用了。请使用 +``alloc_ordered_workqueue()`` 。 执行场景示例 @@ -285,7 +299,7 @@ And with cmwq with ``@max_active`` >= 3, :: * 除非有特殊需要,建议使用0作为@max_active。在大多数使用情 况下,并发水平通常保持在默认限制之下。 -* 一个wq作为前进进度保证(WQ_MEM_RECLAIM,冲洗(flush)和工 +* 一个wq作为前进进度保证,``WQ_MEM_RECLAIM`` ,冲洗(flush)和工 作项属性的域。不涉及内存回收的工作项,不需要作为工作项组的一 部分被刷新,也不需要任何特殊属性,可以使用系统中的一个wq。使 用专用wq和系统wq在执行特性上没有区别。 @@ -294,6 +308,337 @@ And with cmwq with ``@max_active`` >= 3, :: 益的,因为wq操作和工作项执行中的定位水平提高了。 +亲和性作用域 +============ + +一个非绑定工作队列根据其亲和性作用域来对CPU进行分组以提高缓存 +局部性。比如如果一个工作队列使用默认的“cache”亲和性作用域, +它将根据最后一级缓存的边界来分组处理器。这个工作队列上的工作项 +将被分配给一个与发起CPU共用最后级缓存的处理器上的工作者。根据 +``affinity_strict`` 的设置,工作者在启动后可能被允许移出 +所在作用域,也可能不被允许。 + +工作队列目前支持以下亲和性作用域。 + +``default`` + 使用模块参数 ``workqueue.default_affinity_scope`` 指定 + 的作用域,该参数总是会被设为以下作用域中的一个。 + +``cpu`` + CPU不被分组。一个CPU上发起的工作项会被同一CPU上的工作者执行。 + 这使非绑定工作队列表现得像是不含并发管理的每CPU工作队列。 + +``smt`` + CPU被按SMT边界分组。这通常意味着每个物理CPU核上的各逻辑CPU会 + 被分进同一组。 + +``cache`` + CPU被按缓存边界分组。采用哪个缓存边界由架构代码决定。很多情况 + 下会使用L3。这是默认的亲和性作用域。 + +``numa`` + CPU被按NUMA边界分组。 + +``system`` + 所有CPU被放在同一组。工作队列不尝试在临近发起CPU的CPU上运行 + 工作项。 + +默认的亲和性作用域可以被模块参数 ``workqueue.default_affinity_scope`` +修改,特定工作队列的亲和性作用域可以通过 ``apply_workqueue_attrs()`` +被更改。 + +如果设置了 ``WQ_SYSFS`` ,工作队列会在它的 ``/sys/devices/virtual/workqueue/WQ_NAME/`` +目录中有以下亲和性作用域相关的接口文件。 + +``affinity_scope`` + 读操作以查看当前的亲和性作用域。写操作用于更改设置。 + + 当前作用域是默认值时,当前生效的作用域也可以被从这个文件中 + 读到(小括号内),例如 ``default (cache)`` 。 + +``affinity_strict`` + 默认值0表明亲和性作用域不是严格的。当一个工作项开始执行时, + 工作队列尽量尝试使工作者处于亲和性作用域内,称为遣返。启动后, + 调度器可以自由地将工作者调度到系统中任意它认为合适的地方去。 + 这使得在保留使用其他CPU(如果必需且有可用)能力的同时, + 还能从作用域局部性上获益。 + + 如果设置为1,作用域内的所有工作者将被保证总是处于作用域内。 + 这在跨亲和性作用域会导致如功耗、负载隔离等方面的潜在影响时 + 会有用。严格的NUMA作用域也可用于和旧版内核中工作队列的行为 + 保持一致。 + + +亲和性作用域与性能 +================== + +如果非绑定工作队列的行为对绝大多数使用场景来说都是最优的, +不需要更多调节,就完美了。很不幸,在当前内核中,重度使用 +工作队列时,需要在局部性和利用率间显式地作一个明显的权衡。 + +更高的局部性带来更高效率,也就是相同数量的CPU周期内可以做 +更多工作。然而,如果发起者没能将工作项充分地分散在亲和性 +作用域间,更高的局部性也可能带来更低的整体系统利用率。以下 +dm-crypt 的性能测试清楚地阐明了这一取舍。 + +测试运行在一个12核24线程、4个L3缓存的处理器(AMD Ryzen +9 3900x)上。为保持一致性,关闭CPU超频。 ``/dev/dm-0`` +是NVME SSD(三星 990 PRO)上创建,用 ``cryptsetup`` +以默认配置打开的一个 dm-crypt 设备。 + + +场景 1: 机器上遍布着有充足的发起者和工作量 +------------------------------------------ + +使用命令::: + + $ fio --filename=/dev/dm-0 --direct=1 --rw=randrw --bs=32k --ioengine=libaio \ + --iodepth=64 --runtime=60 --numjobs=24 --time_based --group_reporting \ + --name=iops-test-job --verify=sha512 + +这里有24个发起者,每个同时发起64个IO。 ``--verify=sha512`` +使得 ``fio`` 每次生成和读回内容受发起者和 ``kcryptd`` +间的执行局部性影响。下面是基于不同 ``kcryptd`` 的亲和性 +作用域设置,各经过五次测试得到的读取带宽和CPU利用率数据。 + +.. list-table:: + :widths: 16 20 20 + :header-rows: 1 + + * - 亲和性 + - 带宽 (MiBps) + - CPU利用率(%) + + * - system + - 1159.40 ±1.34 + - 99.31 ±0.02 + + * - cache + - 1166.40 ±0.89 + - 99.34 ±0.01 + + * - cache (strict) + - 1166.00 ±0.71 + - 99.35 ±0.01 + +在系统中分布着足够多发起者的情况下,不论严格与否,“cache” +没有表现得更差。三种配置均使整个机器达到饱和,但由于提高了 +局部性,缓存相关的两种有0.6%的(带宽)提升。 + + +场景 2: 更少发起者,足以达到饱和的工作量 +---------------------------------------- + +使用命令::: + + $ fio --filename=/dev/dm-0 --direct=1 --rw=randrw --bs=32k \ + --ioengine=libaio --iodepth=64 --runtime=60 --numjobs=8 \ + --time_based --group_reporting --name=iops-test-job --verify=sha512 + +与上一个场景唯一的区别是 ``--numjobs=8``。 发起者数量 +减少为三分之一,但仍然有足以使系统达到饱和的工作总量。 + +.. list-table:: + :widths: 16 20 20 + :header-rows: 1 + + * - 亲和性 + - 带宽 (MiBps) + - CPU利用率(%) + + * - system + - 1155.40 ±0.89 + - 97.41 ±0.05 + + * - cache + - 1154.40 ±1.14 + - 96.15 ±0.09 + + * - cache (strict) + - 1112.00 ±4.64 + - 93.26 ±0.35 + +这里有超过使系统达到饱和所需的工作量。“system”和“cache” +都接近但并未使机器完全饱和。“cache”消耗更少的CPU但更高的 +效率使其得到和“system”相同的带宽。 + +八个发起者盘桓在四个L3缓存作用域间仍然允许“cache (strict)” +几乎使机器饱和,但缺少对工作的保持(不移到空闲处理器上) +开始带来3.7%的带宽损失。 + + +场景 3: 更少发起者,不充足的工作量 +---------------------------------- + +使用命令::: + + $ fio --filename=/dev/dm-0 --direct=1 --rw=randrw --bs=32k \ + --ioengine=libaio --iodepth=64 --runtime=60 --numjobs=4 \ + --time_based --group_reporting --name=iops-test-job --verify=sha512 + +再次,唯一的区别是 ``--numjobs=4``。由于发起者减少到四个, +现在没有足以使系统饱和的工作量,带宽变得依赖于完成时延。 + +.. list-table:: + :widths: 16 20 20 + :header-rows: 1 + + * - 亲和性 + - 带宽 (MiBps) + - CPU利用率(%) + + * - system + - 993.60 ±1.82 + - 75.49 ±0.06 + + * - cache + - 973.40 ±1.52 + - 74.90 ±0.07 + + * - cache (strict) + - 828.20 ±4.49 + - 66.84 ±0.29 + +现在,局部性和利用率间的权衡更清晰了。“cache”展示出相比 +“system”2%的带宽损失,而“cache (strict)”跌到20%。 + + +结论和建议 +---------- + +在以上试验中,虽然一致并且也明显,但“cache”亲和性作用域 +相比“system”的性能优势并不大。然而,这影响是依赖于作用域 +间距离的,在更复杂的处理器拓扑下可能有更明显的影响。 + +虽然这些情形下缺少工作保持是有坏处的,但比“cache (strict)” +好多了,而且最大化工作队列利用率的需求也并不常见。因此, +“cache”是非绑定池的默认亲和性作用域。 + +* 由于不存在一个适用于大多数场景的选择,对于可能需要消耗 + 大量CPU的工作队列,建议通过 ``apply_workqueue_attrs()`` + 进行(专门)配置,并考虑是否启用 ``WQ_SYSFS``。 + +* 设置了严格“cpu”亲和性作用域的非绑定工作队列,它的行为与 + ``WQ_CPU_INTENSIVE`` 每CPU工作队列一样。后者没有真正 + 优势,而前者提供了大幅度的灵活性。 + +* 亲和性作用域是从Linux v6.5起引入的。为了模拟旧版行为, + 可以使用严格的“numa”亲和性作用域。 + +* 不严格的亲和性作用域中,缺少工作保持大概缘于调度器。内核 + 为什么没能维护好大多数场景下的工作保持,把事情作对,还没有 + 理论上的解释。因此,未来调度器的改进可能会使我们不再需要 + 这些调节项。 + + +检查配置 +======== + +使用 tools/workqueue/wq_dump.py(drgn脚本) 来检查未 +绑定CPU的亲和性配置,工作者池,以及工作队列如何映射到池上: :: + + $ tools/workqueue/wq_dump.py + Affinity Scopes + =============== + wq_unbound_cpumask=0000000f + + CPU + nr_pods 4 + pod_cpus [0]=00000001 [1]=00000002 [2]=00000004 [3]=00000008 + pod_node [0]=0 [1]=0 [2]=1 [3]=1 + cpu_pod [0]=0 [1]=1 [2]=2 [3]=3 + + SMT + nr_pods 4 + pod_cpus [0]=00000001 [1]=00000002 [2]=00000004 [3]=00000008 + pod_node [0]=0 [1]=0 [2]=1 [3]=1 + cpu_pod [0]=0 [1]=1 [2]=2 [3]=3 + + CACHE (default) + nr_pods 2 + pod_cpus [0]=00000003 [1]=0000000c + pod_node [0]=0 [1]=1 + cpu_pod [0]=0 [1]=0 [2]=1 [3]=1 + + NUMA + nr_pods 2 + pod_cpus [0]=00000003 [1]=0000000c + pod_node [0]=0 [1]=1 + cpu_pod [0]=0 [1]=0 [2]=1 [3]=1 + + SYSTEM + nr_pods 1 + pod_cpus [0]=0000000f + pod_node [0]=-1 + cpu_pod [0]=0 [1]=0 [2]=0 [3]=0 + + Worker Pools + ============ + pool[00] ref= 1 nice= 0 idle/workers= 4/ 4 cpu= 0 + pool[01] ref= 1 nice=-20 idle/workers= 2/ 2 cpu= 0 + pool[02] ref= 1 nice= 0 idle/workers= 4/ 4 cpu= 1 + pool[03] ref= 1 nice=-20 idle/workers= 2/ 2 cpu= 1 + pool[04] ref= 1 nice= 0 idle/workers= 4/ 4 cpu= 2 + pool[05] ref= 1 nice=-20 idle/workers= 2/ 2 cpu= 2 + pool[06] ref= 1 nice= 0 idle/workers= 3/ 3 cpu= 3 + pool[07] ref= 1 nice=-20 idle/workers= 2/ 2 cpu= 3 + pool[08] ref=42 nice= 0 idle/workers= 6/ 6 cpus=0000000f + pool[09] ref=28 nice= 0 idle/workers= 3/ 3 cpus=00000003 + pool[10] ref=28 nice= 0 idle/workers= 17/ 17 cpus=0000000c + pool[11] ref= 1 nice=-20 idle/workers= 1/ 1 cpus=0000000f + pool[12] ref= 2 nice=-20 idle/workers= 1/ 1 cpus=00000003 + pool[13] ref= 2 nice=-20 idle/workers= 1/ 1 cpus=0000000c + + Workqueue CPU -> pool + ===================== + [ workqueue \ CPU 0 1 2 3 dfl] + events percpu 0 2 4 6 + events_highpri percpu 1 3 5 7 + events_long percpu 0 2 4 6 + events_unbound unbound 9 9 10 10 8 + events_freezable percpu 0 2 4 6 + events_power_efficient percpu 0 2 4 6 + events_freezable_power_ percpu 0 2 4 6 + rcu_gp percpu 0 2 4 6 + rcu_par_gp percpu 0 2 4 6 + slub_flushwq percpu 0 2 4 6 + netns ordered 8 8 8 8 8 + ... + +参见命令的帮助消息以获取更多信息。 + + +监视 +==== + +使用 tools/workqueue/wq_monitor.py 来监视工作队列的运行: :: + + $ tools/workqueue/wq_monitor.py events + total infl CPUtime CPUhog CMW/RPR mayday rescued + events 18545 0 6.1 0 5 - - + events_highpri 8 0 0.0 0 0 - - + events_long 3 0 0.0 0 0 - - + events_unbound 38306 0 0.1 - 7 - - + events_freezable 0 0 0.0 0 0 - - + events_power_efficient 29598 0 0.2 0 0 - - + events_freezable_power_ 10 0 0.0 0 0 - - + sock_diag_events 0 0 0.0 0 0 - - + + total infl CPUtime CPUhog CMW/RPR mayday rescued + events 18548 0 6.1 0 5 - - + events_highpri 8 0 0.0 0 0 - - + events_long 3 0 0.0 0 0 - - + events_unbound 38322 0 0.1 - 7 - - + events_freezable 0 0 0.0 0 0 - - + events_power_efficient 29603 0 0.2 0 0 - - + events_freezable_power_ 10 0 0.0 0 0 - - + sock_diag_events 0 0 0.0 0 0 - - + + ... + +参见命令的帮助消息以获取更多信息。 + + 调试 ==== @@ -330,7 +675,6 @@ And with cmwq with ``@max_active`` >= 3, :: 工作队列保证,如果在工作项排队后满足以下条件,则工作项不能重入: - 1. 工作函数没有被改变。 2. 没有人将该工作项排到另一个工作队列中。 3. 该工作项尚未被重新启动。 diff --git a/Documentation/translations/zh_CN/dev-tools/kasan.rst b/Documentation/translations/zh_CN/dev-tools/kasan.rst index 8fdb20c966..2b1e8f7490 100644 --- a/Documentation/translations/zh_CN/dev-tools/kasan.rst +++ b/Documentation/translations/zh_CN/dev-tools/kasan.rst @@ -137,7 +137,7 @@ KASAN受到通用 ``panic_on_warn`` 命令行参数的影响。当它被启用 典型的KASAN报告如下所示:: ================================================================== - BUG: KASAN: slab-out-of-bounds in kmalloc_oob_right+0xa8/0xbc [test_kasan] + BUG: KASAN: slab-out-of-bounds in kmalloc_oob_right+0xa8/0xbc [kasan_test] Write of size 1 at addr ffff8801f44ec37b by task insmod/2760 CPU: 1 PID: 2760 Comm: insmod Not tainted 4.19.0-rc3+ #698 @@ -147,8 +147,8 @@ KASAN受到通用 ``panic_on_warn`` 命令行参数的影响。当它被启用 print_address_description+0x73/0x280 kasan_report+0x144/0x187 __asan_report_store1_noabort+0x17/0x20 - kmalloc_oob_right+0xa8/0xbc [test_kasan] - kmalloc_tests_init+0x16/0x700 [test_kasan] + kmalloc_oob_right+0xa8/0xbc [kasan_test] + kmalloc_tests_init+0x16/0x700 [kasan_test] do_one_initcall+0xa5/0x3ae do_init_module+0x1b6/0x547 load_module+0x75df/0x8070 @@ -168,8 +168,8 @@ KASAN受到通用 ``panic_on_warn`` 命令行参数的影响。当它被启用 save_stack+0x43/0xd0 kasan_kmalloc+0xa7/0xd0 kmem_cache_alloc_trace+0xe1/0x1b0 - kmalloc_oob_right+0x56/0xbc [test_kasan] - kmalloc_tests_init+0x16/0x700 [test_kasan] + kmalloc_oob_right+0x56/0xbc [kasan_test] + kmalloc_tests_init+0x16/0x700 [kasan_test] do_one_initcall+0xa5/0x3ae do_init_module+0x1b6/0x547 load_module+0x75df/0x8070 @@ -421,15 +421,15 @@ KASAN连接到vmap基础架构以懒清理未使用的影子内存。 当由于 ``kmalloc`` 失败而导致测试失败时:: - # kmalloc_large_oob_right: ASSERTION FAILED at lib/test_kasan.c:163 + # kmalloc_large_oob_right: ASSERTION FAILED at mm/kasan/kasan_test.c:245 Expected ptr is not null, but is - not ok 4 - kmalloc_large_oob_right + not ok 5 - kmalloc_large_oob_right 当由于缺少KASAN报告而导致测试失败时:: - # kmalloc_double_kzfree: EXPECTATION FAILED at lib/test_kasan.c:974 + # kmalloc_double_kzfree: EXPECTATION FAILED at mm/kasan/kasan_test.c:709 KASAN failure expected in "kfree_sensitive(ptr)", but none occurred - not ok 44 - kmalloc_double_kzfree + not ok 28 - kmalloc_double_kzfree 最后打印所有KASAN测试的累积状态。成功:: @@ -445,7 +445,7 @@ KASAN连接到vmap基础架构以懒清理未使用的影子内存。 1. 可加载模块 启用 ``CONFIG_KUNIT`` 后,KASAN-KUnit测试可以构建为可加载模块,并通过使用 - ``insmod`` 或 ``modprobe`` 加载 ``test_kasan.ko`` 来运行。 + ``insmod`` 或 ``modprobe`` 加载 ``kasan_test.ko`` 来运行。 2. 内置 diff --git a/Documentation/translations/zh_CN/power/opp.rst b/Documentation/translations/zh_CN/power/opp.rst index 8d6e3f6f62..7470fa2d4c 100644 --- a/Documentation/translations/zh_CN/power/opp.rst +++ b/Documentation/translations/zh_CN/power/opp.rst @@ -274,7 +274,7 @@ dev_pm_opp_get_opp_count { /* 做一些事情 */ num_available = dev_pm_opp_get_opp_count(dev); - speeds = kzalloc(sizeof(u32) * num_available, GFP_KERNEL); + speeds = kcalloc(num_available, sizeof(u32), GFP_KERNEL); /* 按升序填充表 */ freq = 0; while (!IS_ERR(opp = dev_pm_opp_find_freq_ceil(dev, &freq))) { diff --git a/Documentation/translations/zh_CN/process/coding-style.rst b/Documentation/translations/zh_CN/process/coding-style.rst index fa28ef0a7f..3bc2810b15 100644 --- a/Documentation/translations/zh_CN/process/coding-style.rst +++ b/Documentation/translations/zh_CN/process/coding-style.rst @@ -523,9 +523,9 @@ Linux 里这是提倡的做法,因为这样可以很简单的给读者提供 .. code-block:: c - err_free_bar: + err_free_bar: kfree(foo->bar); - err_free_foo: + err_free_foo: kfree(foo); return ret; diff --git a/Documentation/translations/zh_CN/process/embargoed-hardware-issues.rst b/Documentation/translations/zh_CN/process/embargoed-hardware-issues.rst index cf5f1fca3d..c90ecb5578 100644 --- a/Documentation/translations/zh_CN/process/embargoed-hardware-issues.rst +++ b/Documentation/translations/zh_CN/process/embargoed-hardware-issues.rst @@ -177,7 +177,7 @@ CVE分配 AMD Tom Lendacky <thomas.lendacky@amd.com> IBM Intel Tony Luck <tony.luck@intel.com> - Qualcomm Trilok Soni <tsoni@codeaurora.org> + Qualcomm Trilok Soni <quic_tsoni@quicinc.com> Microsoft Sasha Levin <sashal@kernel.org> VMware diff --git a/Documentation/translations/zh_CN/userspace-api/accelerators/ocxl.rst b/Documentation/translations/zh_CN/userspace-api/accelerators/ocxl.rst index 845b932bf9..aefad87e90 100644 --- a/Documentation/translations/zh_CN/userspace-api/accelerators/ocxl.rst +++ b/Documentation/translations/zh_CN/userspace-api/accelerators/ocxl.rst @@ -53,7 +53,7 @@ OpenCAPI定义了一个在物理链路层上实现的数据链路层(TL)和 Processor:处理器 Memory:内存 - Accelerated Function Unit:加速函数单元 + Accelerated Function Unit:加速功能单元 @@ -97,7 +97,7 @@ OpenCAPI拥有AFU向主机进程发送中断的可能性。它通过定义在传 ======== 驱动为每个在物理设备上发现的AFU创建一个字符设备。一个物理设备可能拥有多个 -函数,一个函数可以拥有多个AFU。不过编写这篇文档之时,只对导出一个AFU的设备 +功能,一个功能可以拥有多个AFU。不过编写这篇文档之时,只对导出一个AFU的设备 测试过。 字符设备可以在 /dev/ocxl/ 中被找到,其命名为: diff --git a/Documentation/translations/zh_TW/admin-guide/mm/damon/usage.rst b/Documentation/translations/zh_TW/admin-guide/mm/damon/usage.rst index 6dee719a32..7464279f9b 100644 --- a/Documentation/translations/zh_TW/admin-guide/mm/damon/usage.rst +++ b/Documentation/translations/zh_TW/admin-guide/mm/damon/usage.rst @@ -344,7 +344,7 @@ debugfs接口 :ref:`sysfs接口<sysfs_interface>`。 DAMON導出了八個文件, ``attrs``, ``target_ids``, ``init_regions``, -``schemes``, ``monitor_on``, ``kdamond_pid``, ``mk_contexts`` 和 +``schemes``, ``monitor_on_DEPRECATED``, ``kdamond_pid``, ``mk_contexts`` 和 ``rm_contexts`` under its debugfs directory, ``<debugfs>/damon/``. @@ -521,15 +521,15 @@ DAMON導出了八個文件, ``attrs``, ``target_ids``, ``init_regions``, 開關 ---- -除非你明確地啓動監測,否則如上所述的文件設置不會產生效果。你可以通過寫入和讀取 ``monitor_on`` +除非你明確地啓動監測,否則如上所述的文件設置不會產生效果。你可以通過寫入和讀取 ``monitor_on_DEPRECATED`` 文件來啓動、停止和檢查監測的當前狀態。寫入 ``on`` 該文件可以啓動對有屬性的目標的監測。寫入 ``off`` 該文件則停止這些目標。如果每個目標進程被終止,DAMON也會停止。下面的示例命令開啓、關 閉和檢查DAMON的狀態:: # cd <debugfs>/damon - # echo on > monitor_on - # echo off > monitor_on - # cat monitor_on + # echo on > monitor_on_DEPRECATED + # echo off > monitor_on_DEPRECATED + # cat monitor_on_DEPRECATED off 請注意,當監測開啓時,你不能寫到上述的debugfs文件。如果你在DAMON運行時寫到這些文件,將會返 @@ -543,11 +543,11 @@ DAMON通過一個叫做kdamond的內核線程來進行請求監測。你可以 得該線程的 ``pid`` 。當監測被 ``關閉`` 時,讀取該文件不會返回任何信息:: # cd <debugfs>/damon - # cat monitor_on + # cat monitor_on_DEPRECATED off # cat kdamond_pid none - # echo on > monitor_on + # echo on > monitor_on_DEPRECATED # cat kdamond_pid 18594 @@ -574,7 +574,7 @@ DAMON通過一個叫做kdamond的內核線程來進行請求監測。你可以 # ls foo # ls: cannot access 'foo': No such file or directory -注意, ``mk_contexts`` 、 ``rm_contexts`` 和 ``monitor_on`` 文件只在根目錄下。 +注意, ``mk_contexts`` 、 ``rm_contexts`` 和 ``monitor_on_DEPRECATED`` 文件只在根目錄下。 監測結果的監測點 @@ -583,10 +583,10 @@ DAMON通過一個叫做kdamond的內核線程來進行請求監測。你可以 DAMON通過一個tracepoint ``damon:damon_aggregated`` 提供監測結果. 當監測開啓時,你可 以記錄追蹤點事件,並使用追蹤點支持工具如perf顯示結果。比如說:: - # echo on > monitor_on + # echo on > monitor_on_DEPRECATED # perf record -e damon:damon_aggregated & # sleep 5 # kill 9 $(pidof perf) - # echo off > monitor_on + # echo off > monitor_on_DEPRECATED # perf script diff --git a/Documentation/translations/zh_TW/dev-tools/kasan.rst b/Documentation/translations/zh_TW/dev-tools/kasan.rst index 979eb84bc5..ed342e67d8 100644 --- a/Documentation/translations/zh_TW/dev-tools/kasan.rst +++ b/Documentation/translations/zh_TW/dev-tools/kasan.rst @@ -137,7 +137,7 @@ KASAN受到通用 ``panic_on_warn`` 命令行參數的影響。當它被啓用 典型的KASAN報告如下所示:: ================================================================== - BUG: KASAN: slab-out-of-bounds in kmalloc_oob_right+0xa8/0xbc [test_kasan] + BUG: KASAN: slab-out-of-bounds in kmalloc_oob_right+0xa8/0xbc [kasan_test] Write of size 1 at addr ffff8801f44ec37b by task insmod/2760 CPU: 1 PID: 2760 Comm: insmod Not tainted 4.19.0-rc3+ #698 @@ -147,8 +147,8 @@ KASAN受到通用 ``panic_on_warn`` 命令行參數的影響。當它被啓用 print_address_description+0x73/0x280 kasan_report+0x144/0x187 __asan_report_store1_noabort+0x17/0x20 - kmalloc_oob_right+0xa8/0xbc [test_kasan] - kmalloc_tests_init+0x16/0x700 [test_kasan] + kmalloc_oob_right+0xa8/0xbc [kasan_test] + kmalloc_tests_init+0x16/0x700 [kasan_test] do_one_initcall+0xa5/0x3ae do_init_module+0x1b6/0x547 load_module+0x75df/0x8070 @@ -168,8 +168,8 @@ KASAN受到通用 ``panic_on_warn`` 命令行參數的影響。當它被啓用 save_stack+0x43/0xd0 kasan_kmalloc+0xa7/0xd0 kmem_cache_alloc_trace+0xe1/0x1b0 - kmalloc_oob_right+0x56/0xbc [test_kasan] - kmalloc_tests_init+0x16/0x700 [test_kasan] + kmalloc_oob_right+0x56/0xbc [kasan_test] + kmalloc_tests_init+0x16/0x700 [kasan_test] do_one_initcall+0xa5/0x3ae do_init_module+0x1b6/0x547 load_module+0x75df/0x8070 @@ -421,15 +421,15 @@ KASAN連接到vmap基礎架構以懶清理未使用的影子內存。 當由於 ``kmalloc`` 失敗而導致測試失敗時:: - # kmalloc_large_oob_right: ASSERTION FAILED at lib/test_kasan.c:163 + # kmalloc_large_oob_right: ASSERTION FAILED at mm/kasan/kasan_test.c:245 Expected ptr is not null, but is - not ok 4 - kmalloc_large_oob_right + not ok 5 - kmalloc_large_oob_right 當由於缺少KASAN報告而導致測試失敗時:: - # kmalloc_double_kzfree: EXPECTATION FAILED at lib/test_kasan.c:974 + # kmalloc_double_kzfree: EXPECTATION FAILED at mm/kasan/kasan_test.c:709 KASAN failure expected in "kfree_sensitive(ptr)", but none occurred - not ok 44 - kmalloc_double_kzfree + not ok 28 - kmalloc_double_kzfree 最後打印所有KASAN測試的累積狀態。成功:: @@ -445,7 +445,7 @@ KASAN連接到vmap基礎架構以懶清理未使用的影子內存。 1. 可加載模塊 啓用 ``CONFIG_KUNIT`` 後,KASAN-KUnit測試可以構建爲可加載模塊,並通過使用 - ``insmod`` 或 ``modprobe`` 加載 ``test_kasan.ko`` 來運行。 + ``insmod`` 或 ``modprobe`` 加載 ``kasan_test.ko`` 來運行。 2. 內置 diff --git a/Documentation/translations/zh_TW/process/coding-style.rst b/Documentation/translations/zh_TW/process/coding-style.rst index f11dbb65ca..c7ac504f6f 100644 --- a/Documentation/translations/zh_TW/process/coding-style.rst +++ b/Documentation/translations/zh_TW/process/coding-style.rst @@ -526,9 +526,9 @@ Linux 裏這是提倡的做法,因爲這樣可以很簡單的給讀者提供 .. code-block:: c - err_free_bar: + err_free_bar: kfree(foo->bar); - err_free_foo: + err_free_foo: kfree(foo); return ret; diff --git a/Documentation/translations/zh_TW/process/embargoed-hardware-issues.rst b/Documentation/translations/zh_TW/process/embargoed-hardware-issues.rst index 3cce7db2ab..93d21fd889 100644 --- a/Documentation/translations/zh_TW/process/embargoed-hardware-issues.rst +++ b/Documentation/translations/zh_TW/process/embargoed-hardware-issues.rst @@ -180,7 +180,7 @@ CVE分配 AMD Tom Lendacky <thomas.lendacky@amd.com> IBM Intel Tony Luck <tony.luck@intel.com> - Qualcomm Trilok Soni <tsoni@codeaurora.org> + Qualcomm Trilok Soni <quic_tsoni@quicinc.com> Microsoft Sasha Levin <sashal@kernel.org> VMware |