pkg/nvme: add NVMe package
This adds a NVMe package for performing various low-level operations on
NVMe devices. Only the most important (to us) calls are implemented as
NVMe has a vast API surface.
Change-Id: I532894c3c2eb780309993a1688226c92c91cdedf
Reviewed-on: https://review.monogon.dev/c/monogon/+/999
Reviewed-by: Mateusz Zalega <mateusz@monogon.tech>
Tested-by: Jenkins CI
diff --git a/metropolis/pkg/nvme/selftest.go b/metropolis/pkg/nvme/selftest.go
new file mode 100644
index 0000000..8f46995
--- /dev/null
+++ b/metropolis/pkg/nvme/selftest.go
@@ -0,0 +1,96 @@
+package nvme
+
+import (
+ "bytes"
+ "encoding/binary"
+)
+
+type SelfTestOp uint8
+
+const (
+ SelfTestNone SelfTestOp = 0x0
+ SelfTestShort SelfTestOp = 0x1
+ SelfTestExtended SelfTestOp = 0x2
+ SelfTestAbort SelfTestOp = 0xF
+)
+
+func (d *Device) StartSelfTest(ns uint32, action SelfTestOp) error {
+ return d.RawCommand(&Command{
+ Opcode: 0x14,
+ NamespaceID: ns,
+ CDW10: uint32(action & 0xF),
+ })
+}
+
+// Figure 99
+type selfTestResult struct {
+ SelfTestStatus uint8
+ SegmentNumber uint8
+ ValidDiagnosticInformation uint8
+ _ byte
+ PowerOnHours uint64
+ NamespaceID uint32
+ FailingLBA uint64
+ StatusCodeType uint8
+ StatusCode uint8
+ VendorSpecific [2]byte
+}
+
+// Figure 98
+type selfTestLogPage struct {
+ CurrentSelfTestOp uint8
+ CurrentSelfTestCompletion uint8
+ _ [2]byte
+ SelfTestResults [20]selfTestResult
+}
+
+type SelfTestResult struct {
+ // Op contains the self test type
+ Op SelfTestOp
+ Result uint8
+ SegmentNumber uint8
+ PowerOnHours uint64
+ NamespaceID uint32
+ FailingLBA uint64
+ Error Error
+}
+
+type SelfTestResults struct {
+ // CurrentOp contains the currently in-progress self test type (or
+ // SelfTestTypeNone if no self test is in progress).
+ CurrentOp SelfTestOp
+ // CurrentCompletion contains the progress from 0 to 1 of the currently
+ // in-progress self-test. Only valid if CurrentOp is not SelfTestTypeNone.
+ CurrentSelfTestCompletion float32
+ // PastResults contains a list of up to 20 previous self test results,
+ // sorted from the most recent to the oldest.
+ PastResults []SelfTestResult
+}
+
+func (d *Device) GetSelfTestResults(ns uint32) (*SelfTestResults, error) {
+ var buf [564]byte
+ if err := d.GetLogPage(ns, 0x06, 0, 0, buf[:]); err != nil {
+ return nil, err
+ }
+ var page selfTestLogPage
+ binary.Read(bytes.NewReader(buf[:]), binary.LittleEndian, &page)
+ var res SelfTestResults
+ res.CurrentOp = SelfTestOp(page.CurrentSelfTestOp & 0xF)
+ res.CurrentSelfTestCompletion = float32(page.CurrentSelfTestCompletion&0x7F) / 100.
+ for _, r := range page.SelfTestResults {
+ var t SelfTestResult
+ t.Op = SelfTestOp((r.SelfTestStatus >> 4) & 0xF)
+ t.Result = r.SelfTestStatus & 0xF
+ if t.Result == 0xF {
+ continue
+ }
+ t.SegmentNumber = r.SegmentNumber
+ t.PowerOnHours = r.PowerOnHours
+ t.NamespaceID = r.NamespaceID
+ t.FailingLBA = r.FailingLBA
+ t.Error.StatusCode = r.StatusCode
+ t.Error.StatusCodeType = r.StatusCodeType
+ res.PastResults = append(res.PastResults, t)
+ }
+ return &res, nil
+}