sensors: take a SAS drive's standby, whose mode holds spaces
mesh/merge-gate pass: builds new: modules/sensors, sent nowhere; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery delivered

The third review found the asleep match took only one-word modes, so a SAS drive in STANDBY BY TIMER or
LOW POWER was read as a failed answer and lost its reading.
This commit is contained in:
jochen
2026-10-08 15:04:02 +02:00
parent 66a816c3ac
commit 7aba612c6c
3 changed files with 27 additions and 17 deletions
+8 -5
View File
@@ -49,13 +49,16 @@ another process, and go on whatever the drives do.
- `-n standby,2,3` makes smartctl stop with status 2 when the drive is in standby or asleep. The drive
reader then keeps the reading before, and since when the drive has been asleep. A drive counts as asleep
on status 2 with either an ATA power mode of STANDBY or SLEEP, or smartctl's own words "Device is in
<mode> mode, exit(2)" (a SAS drive, or one the operating system powered down). An open failure is
neither, whatever its words.
<mode> mode, exit(2)" (a SAS drive, whose modes hold spaces such as STANDBY BY TIMER or LOW POWER, or
one the operating system powered down). An open failure is neither, whatever its words.
- It stops with status 3 when the drive cannot say (a USB bridge that does not pass CHECK POWER MODE on).
Such a drive is not read, and is said as not read.
- smartctl is told the drive's kind (`-d nvme`, `-d sat` for an ATA drive or one behind USB, `-d scsi`)
so it does not probe. A drive of a kind the module cannot tell (virtio, MMC) is read with smartctl's own
detection.
- smartctl is told the drive's kind (`-d nvme`, `-d sat` for an ATA drive or any drive behind USB,
`-d scsi`) so it does not probe. A drive behind a USB-to-NVMe enclosure or a vendor's own bridge does not
answer to `sat`, and is said as not read. A drive of a kind the module cannot tell (virtio, MMC) is read
with smartctl's own detection.
- Known limits: a smartctl stuck in the kernel outlives the drive reader, and a restarted reader starts a
new read beside it; all drives are read at once, with no cap per controller or bridge.
- Reading SMART from an awake drive can restart its standby timer, so a drive read more often than its
timer is set for never spins down. No mesh tool reads a drive's standby timer, so whether the servers'
drives spin down is not known. A spinning drive is therefore read once an hour, timed from its last
+4 -1
View File
@@ -540,7 +540,10 @@ func asleep(s smartJSON) bool {
return false
}
var sleepWords = regexp.MustCompile(`^Device is in [A-Z_]+( \(OS\))? mode, exit\(2\)$`)
// sleepWords are smartctl's words when -n standby stopped it: an ATA mode (STANDBY, SLEEP), a SAS mode,
// which holds spaces (STANDBY BY TIMER, STANDBY_Y BY COMMAND, LOW POWER), or STANDBY (OS). An idle mode
// never stops smartctl under -n standby, so it never appears here.
var sleepWords = regexp.MustCompile(`^Device is in [A-Z_]+( [A-Z_]+)*( \(OS\))? mode, exit\(2\)$`)
func saysNoPowerMode(s smartJSON) bool {
for _, msg := range s.Smartctl.Messages {
@@ -103,18 +103,22 @@ func TestABoardChipsOwnSensorDoesNotStopItsFanAndACPUSensorDoes(t *testing.T) {
}
}
// D: a SAS drive in standby says so in words and no ATA power mode; status 2 with those words is asleep.
// D: a SAS drive in standby says so in words and no ATA power mode, and its modes hold spaces
// (scsiprint.cpp of smartmontools 7.5); status 2 with those words is asleep. An idle mode never stops
// smartctl under -n standby, so it never says so.
func TestASASDriveInStandbyIsAsleep(t *testing.T) {
f := newFake(t)
f.drive("sdm", "ST4000NM0023", "SEAGATE ", 100, true)
d := f.machine().BlockDrives()[0]
sas := `{"smartctl":{"messages":[{"string":"Device is in STANDBY mode, exit(2)","severity":"information"}],"exit_status":2},` +
`"device":{"name":"/dev/sdm","type":"scsi","protocol":"SCSI"}}`
f.answers[smartctlLine("sdm", "scsi")] = Ran{Stdout: sas, Status: 2}
before := DriveReading{ReadAt: f.now.Add(-time.Hour), Smartctl: []byte(ataPassed)}
got := f.machine().ReadDrive(d, &before)
if !got.Asleep || string(got.Smartctl) != ataPassed || got.AsleepSince == nil {
t.Errorf("a SAS drive in standby keeps its reading: %+v", got)
for _, mode := range []string{"STANDBY BY TIMER", "STANDBY BY COMMAND", "STANDBY_Y BY TIMER", "STANDBY_Y BY COMMAND", "LOW POWER", "SLEEP"} {
f := newFake(t)
f.drive("sdm", "ST4000NM0023", "SEAGATE ", 100, true)
d := f.machine().BlockDrives()[0]
sas := `{"smartctl":{"messages":[{"string":"Device is in ` + mode + ` mode, exit(2)","severity":"information"}],"exit_status":2},` +
`"device":{"name":"/dev/sdm","type":"scsi","protocol":"SCSI"}}`
f.answers[smartctlLine("sdm", "scsi")] = Ran{Stdout: sas, Status: 2}
before := DriveReading{ReadAt: f.now.Add(-time.Hour), Smartctl: []byte(ataPassed)}
got := f.machine().ReadDrive(d, &before)
if !got.Asleep || string(got.Smartctl) != ataPassed || got.AsleepSince == nil {
t.Errorf("a SAS drive in %s keeps its reading: %+v", mode, got)
}
}
}