Skip to content

spindown-v25.patch: trailing newline in standby_disks prevents last disk from being recognized #8

Description

@d22b

The TrueNAS v25 spindown patch reads /run/middleware/standby_disks using:

file.read().split(',')

If the state file contains a trailing newline, for example:

sda,sdc,sde,sdf,sdg\n

the resulting list is:

['sda', 'sdc', 'sde', 'sdf', 'sdg\n']

Therefore the last disk is no longer matched correctly:

'sdg' in file.read().split(',')
# False

This causes the standby guard in DiskEntry.temp() to fail specifically for the last entry in standby_disks.

Observed behavior

On TrueNAS SCALE 25.10, five HDDs were configured with a 10-minute standby timer.

Four disks consistently entered standby while one remained active/idle.

Initially this appeared to be related to a specific HDD, /dev/sdX assignment, or SATA port. Multiple physical disk/port swaps showed that this was not the case: the affected physical disk changed.

Tracing with bpftrace finally showed recurring 512-byte device requests against the disk that would not enter standby.

Example:

14:58:08 BLOCK ... comm=python.d.plugin bytes=512 sector=0 nr_sector=0
14:59:53 BLOCK ... comm=IoThread        bytes=512 sector=0 nr_sector=0
15:03:08 BLOCK ... comm=python.d.plugin bytes=512 sector=0 nr_sector=0
15:04:53 BLOCK ... comm=IoThread        bytes=512 sector=0 nr_sector=0

The processes were identified as:

  • python.d.plugin — TrueNAS/Netdata disk-temperature collector
  • middlewared / IoThread — middleware disk-temperature polling

The Netdata temperature collector runs every 300 seconds.

Isolation of the device request

Individual DiskEntry properties were tested while tracing block:block_rq_issue.

The following produced no disk request:

DiskEntry.serial
DiskEntry.lunid
DiskEntry.identifier

Only:

DiskEntry.temp()

generated the 512-byte disk request.

Before the fix:

/run/middleware/standby_disks:
'sda,sdc,sde,sdf,sdg\n'

sdg present: False

DiskEntry(name="sdg", devpath="/dev/sdg").temp()
→ TempEntry(temp_c=41.0, crit=70.0)

This means the standby guard was not triggered and temp1_input was read through the drivetemp kernel driver.

That temperature read issues a command to the HDD and resets/restarts the ATA idle timer before the configured standby timeout can expire.

Root cause

Two locations in spindown-v25.patch parse the file without removing trailing whitespace:

file.read().split(',')

The final element therefore retains the newline.

This also explains why the problem appeared to move between disks after /dev/sdX assignments changed: it followed the final entry in standby_disks, not a specific physical HDD or SATA port.

Proposed fix

In middlewared/utils/disks_/disk_class.py:

- if self.name in file.read().split(','):
+ if self.name in file.read().strip().split(','):

And in middlewared/plugins/disk.py:

- disk_list = file.read().split(',')
+ disk_list = file.read().strip().split(',')

A more defensive alternative would be:

[x.strip() for x in file.read().split(',') if x.strip()]

although .strip().split(',') is sufficient for the observed state-file format.

Verification

After changing both reads to:

file.read().strip().split(',')

the same direct temperature test returned:

TempEntry(temp_c=None, crit=None)

and the block_rq_issue trace remained completely empty.

Finally, all five HDDs were tested simultaneously with a 10-minute ATA standby timer.

Result after approximately 11 minutes:

sdg    drive state is: standby
sde    drive state is: standby
sdf    drive state is: standby
sdc    drive state is: standby
sda    drive state is: standby

So the change reproducibly fixes the spindown failure.

Environment

  • TrueNAS SCALE 25.10
  • Native TrueNAS HDD standby: 10 minutes
  • TrueNAS-Tricks v25 spindown patch
  • Multiple SATA HDDs
  • Verified using bpftrace on block:block_rq_issue

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions