shared/blockdev-list: add flag to gather read&write stats

This commit is contained in:
Zbigniew Jędrzejewski-Szmek
2026-08-24 21:24:00 +02:00
parent ad997d0630
commit 47df51ea40
2 changed files with 37 additions and 6 deletions
+28 -2
View File
@@ -285,6 +285,30 @@ int blockdev_list_one(
log_device_debug_errno(dev, r, "Failed to acquire diskseq of device '%s', ignoring: %m", node);
}
uint64_t read_bytes, write_bytes;
bool bytes_good = false;
if (FLAGS_SET(flags, BLOCKDEV_LIST_RW_STATS)) {
const char *v;
r = sd_device_get_sysattr_value(dev, "stat", &v);
if (r < 0)
log_device_full_errno(dev, ERRNO_IS_NEG_DEVICE_ABSENT(r) ? LOG_DEBUG : LOG_WARNING,
r, "%s: failed to read 'stat' attribute, ignoring: %m", node);
else {
/* Fields 3 and 7: the number of sectors read and written. The "stat" attribute
* always counts in 512 byte sectors, regardless of the device's logical block size.
* The skipped fields are consumed with %*s, since their values may exceed any fixed
* integer width. */
if (sscanf(v, "%*s %*s %"SCNu64" %*s %*s %*s %"SCNu64, &read_bytes, &write_bytes) < 2)
log_device_warning(dev, "%s: failed to parse 'stat' attribute.", node);
else if (!MUL_SAFE(&read_bytes, read_bytes, UINT64_C(512)) ||
!MUL_SAFE(&write_bytes, write_bytes, UINT64_C(512)))
log_device_debug(dev, "%s: sector counters out of range, ignoring.", node);
else
bytes_good = true;
}
}
_cleanup_free_ char *m = strdup(node);
if (!m)
return log_oom();
@@ -292,11 +316,13 @@ int blockdev_list_one(
*ret = (BlockDevice) {
.node = TAKE_PTR(m),
.symlinks = TAKE_PTR(list),
.diskseq = diskseq,
.size = size,
.model = TAKE_PTR(model),
.vendor = TAKE_PTR(vendor),
.subsystem = TAKE_PTR(subsystem),
.diskseq = diskseq,
.size = size,
.read_bytes = bytes_good ? read_bytes : UINT64_MAX,
.write_bytes = bytes_good ? write_bytes : UINT64_MAX,
.read_only = ro,
};
+9 -4
View File
@@ -15,6 +15,7 @@ typedef enum BlockDevListFlags {
BLOCKDEV_LIST_REQUIRE_LUKS = 1 << 8, /* Only consider block devices with LUKS superblocks */
BLOCKDEV_LIST_METADATA = 1 << 9, /* Fill in model, vendor, subsystem, read_only */
BLOCKDEV_LIST_DISKSEQ = 1 << 10, /* Fill in the diskseq field */
BLOCKDEV_LIST_RW_STATS = 1 << 11, /* Fill in read and write measurements */
} BlockDevListFlags;
/* The "dynamic" filters – ones the kernel can flip at runtime via sysattrs on a live device.
@@ -31,13 +32,17 @@ typedef struct BlockDevice {
char *subsystem;
uint64_t diskseq;
uint64_t size; /* in bytes */
uint64_t read_bytes;
uint64_t write_bytes;
int read_only;
} BlockDevice;
#define BLOCK_DEVICE_NULL (BlockDevice) { \
.diskseq = UINT64_MAX, \
.size = UINT64_MAX, \
.read_only = -1, \
#define BLOCK_DEVICE_NULL (BlockDevice) { \
.diskseq = UINT64_MAX, \
.size = UINT64_MAX, \
.read_bytes = UINT64_MAX, \
.write_bytes = UINT64_MAX, \
.read_only = -1, \
}
/* Return values of blockdev_list_one(). Not its own type – returned as int. */