NVMe I/O Error Fehlermeldung unter Linux analysieren
Kommt es auf einem Linux System zu NMVe I/O Error Situationen, protokolliert der Kernel Fehlerinformationen welche mittels dmesg Kommando abgefragt werden können. In diesem Artikel zeigen wir, wie Sie eine NVMe I/O Error Fehlermeldung analysieren.
Beispielfehler I/O Error sct 0x0 sc 0x4 DNR

Auf einem Testsystem mit Linux-Kernel 6.8.0-55-generic #57-Ubuntu kommt es im Zuge von I/O Tests mit fio zu einem Abbruch des fio-Tests durch einen I/O Error. In der dmesg Ausgabe ist zu sehen:
Die erste Zeile enthält folgende Informationen:
[74942.261934] nvme0c0n1: I/O Cmd(0x1) @ LBA 218630656, 256 blocks, I/O Error (sct 0x0 / sc 0x4) DNR[74942.262623] I/O error, dev nvme0c0n1, sector 218630656 op 0x1:(WRITE) flags 0x2008800 phys_seg 17 prio class 0
Speziell interessant sind dabei folgende Teile der Meldung:
- I/O Error
- sct 0x0
- sc 0x4
- DNR
Die Bedeutung dieser Werte sind in der NVMe Express Base Specification[2] dokumentiert. In Kapitel 4.2.3 (Status Field Definition) der NVM Express® Base Specification Revision 2.3[1] werden die einzelnen Elemente des Status Field folgendermaßen beschrieben:
- SCT (3 bits) - Status Code Type - Gibt den Typ des Statuscodes an, den der Controller (der NVMe SSD) zurückgibt.
- SC (8 bits) - Status Code - (Details siehe weiter unten.)
- CRD (2 bits) - Command Retry Delay - Wenn das DNR-Bit im Statusfeld auf „1“ gesetzt ist, ist dieses Feld reserviert. (Weitere Details siehe NVM Express Base Specification.)
- M (1 bit) - More - Wenn dieses Bit auf „1“ gesetzt ist, gibt es weitere Statusinformationen zu diesem Befehl als Teil der Seite „Error Information log page“, die mit dem Befehl „Get Log Page“ abgerufen werden können. Wenn dieses Bit auf „0“ gesetzt ist, gibt es keine zusätzlichen Statusinformationen zu diesem Befehl.
- DNR (1 bit) - Do Not Retry - Wenn dieses Bit auf „1“ gesetzt ist, bedeutet dies, dass derselbe Befehl, wenn er erneut an einen Controller im NVM-Subsystem gesendet wird, voraussichtlich fehlschlagen wird. Wenn dieses Bit auf „0“ gesetzt ist, bedeutet dies, dass derselbe Befehl bei einer Wiederholung erfolgreich sein kann.
Status Code Type

Die NVM Express Base Specification definiert mehrere Status Code Types:
| Status Code Type (sct) | Definition |
|---|---|
| 0 | Generic Command Status |
| 1 | Command Specific Status |
| 2 | Media and Data Integrity Errors |
| 3 | Path Related Status |
| 4, 5, 6 | reserviert |
| 7 | Hersteller-spezifisch |
Im Beispiel oben (I/O Error (sct 0x0 / sc 0x4) DNR) lautet der Status Code Type 0 (sct 0x0).
Status Code
Abhänging vom jeweiligen Status Code Type, hat der zusätzlich mit übermittelte Status Code eine unterschiedliche Bedeutung.
Status Code (Generic Command Status)

Die Bedeutung des Status Codes bei einem Generic Command Status (sct = 0) ist in Kapitel 4.2.3.1 definiert. Hier dazu ein Auszug:
| Status Code | Definition |
|---|---|
| 0 | Successful Completion: Der Befehl wurde ohne Fehler ausgeführt. |
| 1 | Invalid Command Opcode: Ein reservierter codierter Wert oder ein nicht unterstützter Wert im Befehls-Opcode-Feld. |
| 2 | Invalid Field in Command: Ein reservierter codierter Wert oder ein nicht unterstützter Wert in einem definierten Feld (außer dem Opcode-Feld). |
| 3 | Command ID Conflict: Die Befehlskennung (Command ID) wird bereits verwendet. Hinweis: Wie viele Befehle nach einem Konflikt durchsucht werden, hängt von der jeweiligen Implementierung ab. |
| 4 | Data Transfer Error: Beim Übertragen der mit einem Befehl verbundenen Daten oder Metadaten ist ein Fehler aufgetreten. |
| ... |
Im Beispiel oben (I/O Error (sct 0x0 / sc 0x4) DNR) lautet der Status Code 4 (sc 0x4). Es handelt sich damit um einen Data Transfer Error.
Status Code (Command Specific Status)
Die Bedeutung des Status Codes bei einem Command Specific Status (sct = 1) ist in Kapitel 4.2.3.2 definiert. In diesem Beispiel gehen wir nicht näher darauf ein.
Interpretation der Statusinformation
Die Statusinformation "Data Transfer Error" deutet auf Probleme bei der Datenübertragung hin. Potentiell können beispielsweise Kabel, Steckverbindungen oder Backplane die Ursache sein.
Die Detail-Ausgabe von lspci zeigt, dass die betroffene NVMe SSD via PCIe 5.0 (32GT/s) angebunden ist:
# lspci -s 81:00.0 -vvv
81:00.0 Non-Volatile memory controller: KIOXIA Corporation Device 0013 (rev 01) (prog-if 02 [NVM Express])
Subsystem: KIOXIA Corporation Device 0045
Physical Slot: 1
[...]
Capabilities: [70] Express (v2) Endpoint, MSI 00
[...]
LnkCap: Port #0, Speed 32GT/s, Width x4, ASPM not supported
ClockPM- Surprise- LLActRep- BwNot- ASPMOptComp+
LnkCtl: ASPM Disabled; RCB 64 bytes, Disabled- CommClk+
ExtSynch- ClockPM- AutWidDis- BWInt- AutBWInt-
LnkSta: Speed 32GT/s, Width x4
TrErr- Train- SlotClk+ DLActive- BWMgmt- ABWMgmt-
[...]
LnkCap2: Supported Link Speeds: 2.5-32GT/s, Crosslink- Retimer+ 2Retimers+ DRS-
LnkCtl2: Target Link Speed: 32GT/s, EnterCompliance- SpeedDis-
Transmit Margin: Normal Operating Range, EnterModifiedCompliance- ComplianceSOS-
Compliance Preset/De-emphasis: -6dB de-emphasis, 0dB preshoot
In diesem Beispiel kommt jedoch eine Backplane mit Oculink Buchse zum Einsatz. Oculink unterstützt laut Spezifikation PCIe 4.0, jedoch nicht PCIe 5.0.
Um die Verbindung auf PCIe 4.0 Geschwindigkeit (16GT/s) zu limitieren, kann man entweder das Tool setpci direkt mit den entsprechenden Parametern verwenden, oder das Skript pci_set_speed.sh von Alex Forenchich nutzen:[3]
# ./pci_set_speed.sh 0000:81:00.0 4 Link capabilities: 007b7845 Max link speed: 5 Link status: 7045 Current link speed: 5 Configuring 0000:80:01.1... Original link control 2: 001e0005 Original link target speed: 5 New target link speed: 4 New link control 2: 001e0004 Triggering link retraining... Original link control: 70450040 New link control: 70450060 Link status: 7044 Current link speed: 4
Nach dem Ausführen des Skriptes ist die PCIe Link Geschwindigkeit reduziert (LnkSta: Speed 16GT/s (downgraded)):
root@xfuel:~# lspci -s 81:00.0 -vvv
81:00.0 Non-Volatile memory controller: KIOXIA Corporation Device 0013 (rev 01) (prog-if 02 [NVM Express])
Subsystem: KIOXIA Corporation Device 0045
Physical Slot: 1
[...]
Capabilities: [70] Express (v2) Endpoint, MSI 00
[...]
LnkCap: Port #0, Speed 32GT/s, Width x4, ASPM not supported
ClockPM- Surprise- LLActRep- BwNot- ASPMOptComp+
LnkCtl: ASPM Disabled; RCB 64 bytes, Disabled- CommClk+
ExtSynch- ClockPM- AutWidDis- BWInt- AutBWInt-
LnkSta: Speed 16GT/s (downgraded), Width x4
TrErr- Train- SlotClk+ DLActive- BWMgmt- ABWMgmt-
[...]
LnkCap2: Supported Link Speeds: 2.5-32GT/s, Crosslink- Retimer+ 2Retimers+ DRS-
LnkCtl2: Target Link Speed: 32GT/s, EnterCompliance- SpeedDis-
Transmit Margin: Normal Operating Range, EnterModifiedCompliance- ComplianceSOS-
Compliance Preset/De-emphasis: -6dB de-emphasis, 0dB preshoot
Folgende Tests mit fio zeigten keine Probleme mehr. Die Ursache für die Data Transfer Error war also die höhere (32GT/s) Übertragungsgeschwindigkeit von PCIe 5.0 über Komponenten (Backplane), die nur bis PCIe 4.0 freigegeben sind.
Skript zum Limitieren der PCIe Übertragungsrate
Hier ist zur Information der vollständige Code des Bash-Scripts von Alex Forenchich (Lizenz: [https://creativecommons.org/licenses/by-sa/4.0/ CC-BY-SA 4.0):<ref name=pcispeed>
#!/bin/bash
dev=$1
speed=$2
if [ -z "$dev" ]; then
echo "Error: no device specified"
exit 1
fi
if [ ! -e "/sys/bus/pci/devices/$dev" ]; then
dev="0000:$dev"
fi
if [ ! -e "/sys/bus/pci/devices/$dev" ]; then
echo "Error: device $dev not found"
exit 1
fi
pciec=$(setpci -s $dev CAP_EXP+02.W)
pt=$((("0x$pciec" & 0xF0) >> 4))
port=$(basename $(dirname $(readlink "/sys/bus/pci/devices/$dev")))
if (($pt == 0)) || (($pt == 1)) || (($pt == 5)); then
dev=$port
fi
lc=$(setpci -s $dev CAP_EXP+0c.L)
ls=$(setpci -s $dev CAP_EXP+12.W)
max_speed=$(("0x$lc" & 0xF))
echo "Link capabilities:" $lc
echo "Max link speed:" $max_speed
echo "Link status:" $ls
echo "Current link speed:" $(("0x$ls" & 0xF))
if [ -z "$speed" ]; then
speed=$max_speed
fi
if (($speed > $max_speed)); then
speed=$max_speed
fi
echo "Configuring $dev..."
lc2=$(setpci -s $dev CAP_EXP+30.L)
echo "Original link control 2:" $lc2
echo "Original link target speed:" $(("0x$lc2" & 0xF))
lc2n=$(printf "%08x" $((("0x$lc2" & 0xFFFFFFF0) | $speed)))
echo "New target link speed:" $speed
echo "New link control 2:" $lc2n
setpci -s $dev CAP_EXP+30.L=$lc2n
echo "Triggering link retraining..."
lc=$(setpci -s $dev CAP_EXP+10.L)
echo "Original link control:" $lc
lcn=$(printf "%08x" $(("0x$lc" | 0x20)))
echo "New link control:" $lcn
setpci -s $dev CAP_EXP+10.L=$lcn
sleep 0.1
ls=$(setpci -s $dev CAP_EXP+12.W)
echo "Link status:" $ls
echo "Current link speed:" $(("0x$ls" & 0xF))
Weitere Informationen
- H12SSL-NT - Work around missing PCIe Force Link Speed option (forums.servethehome.com, 15.01.2024)
Einzelnachweise
- ↑ 1,0 1,1 NVM Express® Base Specification Revision 2.3 (nvmexpress.org)
- ↑ NVM Express® Base Specification (nvmexpress.org)
- ↑ PCIe Set Speed (alexforencich.com)
|
Autor: Werner Fischer Werner Fischer arbeitet im Product Management Team von Thomas-Krenn. Er evaluiert dabei neueste Technologien und teilt sein Wissen in Fachartikeln, bei Konferenzen und im Thomas-Krenn Wiki. Bereits 2005 - ein Jahr nach seinem Abschluss des Studiums zu Computer- und Mediensicherheit an der FH Hagenberg - heuerte er beim bayerischen Server-Hersteller an. Als Öffi-Fan nutzt er gerne Bus & Bahn und genießt seinen morgendlichen Spaziergang ins Büro. |

