Salta al contenuto principale

lspci: i dispositivi collegati al bus PCI

Inviato da tuxsa il

Una scheda di rete che non va, una scheda video che usa il driver sbagliato, un disco veloce che non compare: prima di tutto bisogna sapere che cosa c'è dentro la macchina. lspci risponde: elenca i dispositivi collegati al bus PCI e, con un'opzione, dice quale driver li sta gestendo. È il compagno naturale di lscpu (il processore) e di lsmod (i moduli del kernel).

Cos'è

Il bus PCI (e il suo successore PCI Express) è il collegamento interno attraverso cui il processore parla con schede di rete, schede video, controller dei dischi, dischi NVMe e molti dispositivi integrati nella scheda madre. lspci (list PCI) fa parte del pacchetto pciutils (su Debian e Ubuntu: sudo apt install pciutils, vedi l'articolo su apt). Legge i dati che il kernel (vedi uname) raccoglie in /sys/bus/pci/ e li traduce in nomi leggibili, usando un archivio di identificativi (pci.ids).

Cosa fa

Elenca ogni dispositivo PCI con il suo indirizzo (bus:dispositivo.funzione), la classe (scheda di rete, controller di archiviazione, scheda video...), il costruttore e il modello. Con le opzioni giuste mostra anche gli identificativi numerici, il driver in uso, la struttura ad albero e i dettagli tecnici. Non modifica niente.

Come si usa

$ lspci [opzioni]

Gli esempi dal "sandbox" sono output reali di una macchina virtuale di prova (11 dispositivi virtuali di tipo virtio, con lspci 3.10.0); quelli con web01, il server degli articoli precedenti, sono illustrativi, perché una macchina fisica ha dispositivi diversi.

Esempio 1: l'elenco dei dispositivi

$ lspci
00:00.0 Host bridge: Intel Corporation Device 0d57
00:01.0 Unassigned class [ffff]: Red Hat, Inc. Virtio 1.0 memory balloon (rev 01)
00:02.0 Mass storage controller: Red Hat, Inc. Virtio 1.0 block device (rev 01)
00:03.0 Mass storage controller: Red Hat, Inc. Virtio 1.0 block device (rev 01)
...
00:08.0 Ethernet controller: Red Hat, Inc. Virtio 1.0 network device (rev 01)
00:09.0 Unassigned class [ffff]: Red Hat, Inc. Virtio 1.0 socket (rev 01)
00:0a.0 Unassigned class [ffff]: Red Hat, Inc. Virtio 1.0 RNG (rev 01)
$ lspci | wc -l
11

Ogni riga comincia con l'indirizzo del dispositivo, nella forma bus:dispositivo.funzione (qui 00:08.0), seguito dalla classe (Ethernet controller, Mass storage controller, VGA compatible controller...), dal costruttore e dal modello. Se l'archivio dei nomi non conosce un dispositivo, mostra solo Device e il codice (Device 0d57), e se non conosce la classe scrive Unassigned class. Per cercare un tipo di dispositivo si usa grep (vedi l'articolo relativo): lspci | grep -i -E 'ethernet|network|vga|nvme'. Il numero di righe (qui 11) è il numero dei dispositivi; su un server fisico sono di solito decine, perché ci sono anche i bridge e i controller integrati nel chipset.

Esempio 2: quale driver gestisce un dispositivo

$ lspci -k -s 00:08.0
00:08.0 Ethernet controller: Red Hat, Inc. Virtio 1.0 network device (rev 01)
        Subsystem: Red Hat, Inc. Virtio 1.0 network device
        Kernel driver in use: virtio-pci
$ lspci -k -s 03:00.0     # esempio su web01
03:00.0 Ethernet controller: Realtek RTL8111/8168 PCI Express Gigabit
        Subsystem: ASUSTeK Computer Inc. Device 859e
        Kernel driver in use: r8169
        Kernel modules: r8169

Con -k (kernel) si vede il driver in uso: è l'opzione che si usa più spesso. Con -s (slot) si limita l'output a un solo dispositivo, dato il suo indirizzo (si può scrivere anche -s 00: per tutto un bus). Le righe da leggere: Kernel driver in use è il driver che sta gestendo il dispositivo adesso; Kernel modules sono i moduli del kernel che potrebbero gestirlo (vedi l'articolo su lsmod e modinfo). Se Kernel driver in use manca, il dispositivo non ha un driver attivo, ed è il primo indizio quando "la scheda non funziona". Nel sandbox il primo comando mostra solo virtio-pci e non c'è la riga dei moduli, perché il kernel di prova non ha moduli. In più lspci scrive sullo stream di errore Unable to load libkmod resources, per lo stesso motivo: su un sistema normale non compare. Il secondo blocco è illustrativo.

Esempio 3: codici numerici

$ lspci -nn -s 00:00.0
00:00.0 Host bridge [0600]: Intel Corporation Device [8086:0d57]
$ lspci -nn -s 00:08.0 | fold -s -w 70
00:08.0 Ethernet controller [0200]: Red Hat, Inc. Virtio 1.0 network
device [1af4:1041] (rev 01)
$ lspci -n -s 00:08.0
00:08.0 0200: 1af4:1041 (rev 01)
$ lspci -d 1af4: | wc -l
10
$ lspci -d ::0200
00:08.0 Ethernet controller: Red Hat, Inc. Virtio 1.0 network device (rev 01)
$ lspci -D | head -n 1
0000:00:00.0 Host bridge: Intel Corporation Device 0d57

Con -nn accanto ai nomi compaiono i codici numerici tra parentesi quadre: la classe (0200 = scheda di rete) e la coppia costruttore:modello (1af4:1041). Sono utili quando il nome non è noto, o per cercare informazioni precise (un driver, un errore, un bollettino) senza ambiguità. Con -n si hanno solo i numeri. Con -d si filtra: -d 1af4: tutti i dispositivi di un costruttore, -d ::0200 per classe. Con -D si mostra anche il "dominio" davanti all'indirizzo (0000:), come lo scrive il kernel nei suoi percorsi. Nel secondo esempio, fold -s -w 70 spezza la riga lunga a 70 caratteri, solo per farla stare nella pagina: l'output reale è su una riga sola.

Esempio 4: i dettagli di un dispositivo

$ lspci -v -s 00:08.0
00:08.0 Ethernet controller: Red Hat, Inc. Virtio 1.0 network device (rev 01)
        Subsystem: Red Hat, Inc. Virtio 1.0 network device
        Flags: bus master, fast devsel, latency 0
        Memory at 4000380000 (64-bit, non-prefetchable) [size=512K]
        Capabilities: [98] MSI-X: Enable+ Count=3 Masked-
        Kernel driver in use: virtio-pci
$ sudo lspci -vv -s 03:00.0 | grep -E 'LnkCap|LnkSta'     # esempio su web01
                LnkCap:        Port #0, Speed 2.5GT/s, Width x1, ASPM L0s L1
                LnkSta:        Speed 2.5GT/s, Width x1
$ lspci -vv -s 00:08.0 | grep -c 'access denied'     # senza privilegi
1

-v (verbose) aggiunge i dettagli: flag, aree di memoria, funzioni supportate (Capabilities); -vv e -vvv ne aggiungono ancora, fino alla velocità e alla larghezza del collegamento PCI Express (LnkCap è il massimo possibile, LnkSta è quello in uso: se sono diversi, per esempio un disco veloce collegato a velocità ridotta, il dispositivo non rende quanto potrebbe). Il primo blocco è reale (ho ridotto le righe Capabilities iniziali). Il secondo è illustrativo. Il terzo l'ho provato con un utente senza privilegi: alcune informazioni, come le Capabilities, compaiono solo con sudo, e senza compare <access denied>.

Esempio 5: la struttura ad albero

$ lspci -t
-[0000:00]-+-00.0
           +-01.0
           +-02.0
           ...
           +-08.0
           +-09.0
           \-0a.0
$ lspci -tv | head -n 4
-[0000:00]-+-00.0  Intel Corporation Device 0d57
           +-01.0  Red Hat, Inc. Virtio 1.0 memory balloon
           +-02.0  Red Hat, Inc. Virtio 1.0 block device
           +-03.0  Red Hat, Inc. Virtio 1.0 block device

Con -t (tree) i dispositivi sono disegnati ad albero, che mostra come sono collegati tra loro: dal bus principale (00) ai bridge e ai bus secondari. Con -tv compaiono anche i nomi. Su un server fisico l'albero è più profondo (per esempio una scheda di rete sotto un bridge PCI Express): serve a capire "dietro a cosa" sta un dispositivo, o a trovare quelli che dividono lo stesso collegamento. Nel sandbox, tutto è collegato direttamente al bus 00.

Esempio 6: una forma adatta agli script

$ lspci -vmm -s 00:08.0
Slot:        00:08.0
Class:        Ethernet controller
Vendor:        Red Hat, Inc.
Device:        Virtio 1.0 network device
SVendor:        Red Hat, Inc.
SDevice:        Virtio 1.0 network device
Rev:        01
ProgIf:        00
$ lspci -mm -s 00:08.0
00:08.0 "Ethernet controller" "Red Hat, Inc." "Virtio 1.0 network device" -r01 -p00
$ cat /sys/bus/pci/devices/0000:00:08.0/vendor
0x1af4
$ readlink /sys/bus/pci/devices/0000:00:08.0/driver
../../../bus/pci/drivers/virtio-pci

Per l'elaborazione automatica l'output normale non è adatto, perché cambia forma da riga a riga. Con -vmm si ha un blocco Campo: valore per ogni dispositivo (campi separati da tabulazioni, un blocco separato dall'altro da una riga vuota), con -mm una riga per dispositivo con i campi tra virgolette. Le informazioni arrivano comunque dal kernel, in /sys/bus/pci/devices/: ogni dispositivo ha una cartella con file semplici (vendor, device, class, il collegamento driver), e si leggono anche senza lspci (utile su un sistema minimale, o in un container di cui si ha il /sys). Nell'ultimo esempio readlink mostra il driver in uso. Il codice del costruttore, scritto in esadecimale (0x1af4), è lo stesso di -nn.

Esempio 7: uso negli script

#!/bin/bash
# Elenca i dispositivi PCI senza un driver in uso (esclusi i bridge)
ESCLUSI='Host bridge|PCI bridge|ISA bridge'
SENZA=$(lspci -k 2>/dev/null | awk '
    /^[0-9a-f]/ { if (dev != "" && !drv) print dev; dev = $0; drv = 0; next }
    /Kernel driver in use/ { drv = 1 }
    END { if (dev != "" && !drv) print dev }' | grep -v -E "$ESCLUSI")
if [ -n "$SENZA" ]; then
    echo "Dispositivi senza driver:" >&2
    echo "$SENZA" >&2
    exit 1
fi
echo "Tutti i dispositivi hanno un driver"

Lo script legge l'output di lspci -k con awk (vedi l'articolo relativo): una riga che inizia con una cifra o una lettera esadecimale è l'inizio di un nuovo dispositivo, e se il precedente non aveva la riga Kernel driver in use lo stampa. I bridge, che non sempre hanno un driver, sono esclusi con grep -v. Se trova qualcosa termina con l'errore (exit 1: è comodo per cron, vedi l'articolo su crontab). L'ho provato nel sandbox, dove risponde "Tutti i dispositivi hanno un driver", e con un lspci finto che contiene una scheda video senza driver, dove l'ha segnalata e ha terminato con codice 1. Un dispositivo senza driver non è sempre un problema (può essere inutilizzato, o gestito da un programma in user space): è un'indicazione da valutare.

Opzioni utili

  • lspci: elenco; -k: con driver e moduli; -v, -vv, -vvv: più dettagli; -s indirizzo: un solo dispositivo.
  • -n: solo codici numerici; -nn: nomi e codici; -d costruttore:modello o -d ::classe: filtra; -D: indirizzo con il dominio.
  • -t: albero dei collegamenti; -tv: albero con i nomi.
  • -m, -mm, -vmm: output per i programmi (-mm è la forma consigliata).
  • -x: lo spazio di configurazione in esadecimale (per chi sa leggerlo); -xxx: in modo esteso (solo amministratore).
  • update-pciids: aggiorna l'archivio dei nomi dei dispositivi (richiede la rete).
  • lsusb, lsblk, lsmod, dmesg, ip: i dispositivi USB, i dischi, i moduli, i messaggi del kernel, le interfacce di rete.

Attenzione a

  • Mostra solo i dispositivi PCI: non elenca le periferiche USB (si usa lsusb, del pacchetto usbutils), i dischi SATA (che sono dietro un controller: vedi lsblk), né le interfacce di rete (vedi ip). Un disco NVMe invece compare, perché è un dispositivo PCI Express.
  • Un dispositivo elencato non è un dispositivo funzionante: lspci mostra che l'hardware è presente e il kernel lo ha visto. Se funziona lo dice il driver: guarda Kernel driver in use e i messaggi di dmesg (sudo dmesg | grep -i firmware è una prova utile per i driver che chiedono un file di firmware).
  • Nei container e nelle macchine virtuali i dati sono quelli che l'ambiente espone: in un container si vedono di solito i dispositivi della macchina ospite (o niente); in una macchina virtuale i dispositivi sono virtuali, come nel sandbox (virtio), e i driver in uso possono essere un "generico" come virtio-pci.
  • I nomi dipendono dall'archivio pci.ids: un dispositivo recente può comparire come Device 0d57 finché l'archivio non viene aggiornato (con update-pciids, che ha bisogno della rete, o con l'aggiornamento del pacchetto). I codici di -nn restano il riferimento sicuro.
  • Il messaggio Unable to load libkmod resources compare quando lspci -k non trova l'elenco dei moduli del kernel in uso (è il caso del sandbox, che non ha moduli). Su un sistema normale non si vede; se lo vedi, di solito manca la cartella /lib/modules/ del kernel in esecuzione, ad esempio dopo un aggiornamento senza riavvio.
  • Alcuni dettagli richiedono sudo: -vv e simili mostrano il campo Capabilities solo all'amministratore (altrimenti <access denied>); per le informazioni di base non serve.
  • Non si modifica niente con lspci, ma setpci sì: lo stesso pacchetto contiene setpci, che scrive nei registri di un dispositivo. Un valore sbagliato può bloccare l'hardware o l'intero sistema: va usato solo se si sa esattamente cosa si sta facendo.
  • Gli indirizzi cambiano: l'indirizzo di un dispositivo (03:00.0) dipende dalla posizione fisica e può cambiare dopo l'aggiunta di una scheda o un aggiornamento del BIOS. Per riconoscere un dispositivo in modo stabile, meglio i codici costruttore:modello.

Riepilogo

ComandoCosa fa
lspciElenca i dispositivi PCI
lspci | grep -i ethernetCerca un tipo di dispositivo
lspci -kMostra anche il driver in uso e i moduli
lspci -k -s 00:08.0Driver di un solo dispositivo
lspci -nnNomi e codici numerici
lspci -d 1af4:Filtra per costruttore (o per classe: -d ::0200)
sudo lspci -vv -s 00:08.0Dettagli completi di un dispositivo
lspci -t, lspci -tvAlbero dei collegamenti
lspci -vmmOutput a campi, per gli script
cat /sys/bus/pci/devices/*/vendorDati grezzi dal kernel
sudo update-pciidsAggiorna l'archivio dei nomi