Make the stat tag a disk level

This commit is contained in:
Andrea Mazzoleni 2025-12-26 11:12:22 +01:00
parent 672b881df3
commit 524733a6db
6 changed files with 67 additions and 140 deletions

View file

@ -558,8 +558,6 @@ static void state_info_log(devinfo_t* devinfo)
log_tag("attr:%s:%s:size:%" PRIu64 "\n", devinfo->file, devinfo->name, devinfo->info[INFO_SIZE]);
if (devinfo->info[INFO_ROTATION_RATE] != SMART_UNASSIGNED)
log_tag("attr:%s:%s:rotationrate:%" PRIu64 "\n", devinfo->file, devinfo->name, devinfo->info[INFO_ROTATION_RATE]);
if (devinfo->access_stat != 0)
log_tag("attr:%s:%s:stat:%" PRIu64 "\n", devinfo->file, devinfo->name, devinfo->access_stat);
}
static void state_smart_log(devinfo_t* devinfo, double afr)
@ -854,6 +852,23 @@ static void state_probe(struct snapraid_state* state, tommy_list* low)
printf("\n");
}
static void state_stat(struct snapraid_state* state, tommy_list* high)
{
tommy_node* i;
(void)state;
/* compute lengths for padding */
for (i = tommy_list_head(high); i != 0; i = i->next) {
devinfo_t* devinfo = i->data;
/* print disk access_stat (not splitted) */
if (devinfo->split == 0 && devinfo->access_stat != 0) {
log_tag("stat:%s:%" PRIu64 "\n", devinfo->name, devinfo->access_stat);
}
}
}
int devtest(tommy_list* high, tommy_list* low, int operation)
{
tommy_node* i;
@ -955,6 +970,7 @@ void state_device(struct snapraid_state* state, int operation, tommy_list* filte
/* for all parities */
for (j = 0; j < state->level; ++j) {
devinfo_t* entry;
devinfo_t* split;
unsigned s;
if (filterlist_disk != 0 && filter_path(filterlist_disk, 0, lev_config_name(j), 0) != 0)
@ -963,6 +979,12 @@ void state_device(struct snapraid_state* state, int operation, tommy_list* filte
for (s = 0; s < state->parity[j].split_mac; ++s) {
entry = calloc_nofail(1, sizeof(devinfo_t));
/* keep track of the first split */
if (s == 0)
split = entry;
else
entry->split = split;
entry->device = state->parity[j].split_map[s].device;
device_name_set(entry, lev_config_name(j), s);
pathcpy(entry->mount, sizeof(entry->mount), state->parity[j].split_map[s].path);
@ -1010,6 +1032,8 @@ void state_device(struct snapraid_state* state, int operation, tommy_list* filte
}
}
state_stat(state, &high);
if (operation == DEVICE_SMART)
state_smart(state, state->level + tommy_list_count(&state->disklist), &low);

View file

@ -518,9 +518,10 @@ struct devinfo_struct {
char wfile[PATH_MAX]; /**< File device in Windows format. Like \\.\PhysicalDriveX, or \\?\Volume{X}. */
#endif
struct devinfo_struct* parent; /**< Pointer at the parent if any. */
struct devinfo_struct* split; /**< Pointer at first split if any. */
uint64_t smart[SMART_COUNT]; /**< SMART attributes. */
uint64_t info[INFO_COUNT]; /**< Info attributes. */
uint64_t access_stat; /**< Access stat. */
uint64_t access_stat; /**< Access stat info. */
char serial[SMART_MAX]; /**< Serial number. */
char family[SMART_MAX]; /**< Family. */
char model[SMART_MAX]; /**< Model. */

View file

@ -1276,124 +1276,17 @@ static int devsmart(dev_t device, const char* name, const char* smartctl, uint64
/**
* Compute disk usage by aggregating access statistics.
*
* This function determines whether a disk has been used by summing read
* and write access counters from all related partitions and device holders.
* Disk-level statistics are not used directly, as they may include
* kernel-generated fake accesses that do not reflect real I/O activity.
*
* By walking the device hierarchy and accumulating per-device statistics,
* the function provides a reliable measure of actual disk usage between
* sampling points.
*/
#if HAVE_LINUX_DEVICE
static int devstat_recurse(dev_t device, uint64_t* count)
static int devstat(dev_t device, uint64_t* count)
{
char device_path[PATH_MAX];
char path[PATH_MAX];
char buf[512];
char* device_file;
int device_len;
DIR* d;
struct dirent* dd;
int holders = 0;
int partitions = 0;
int token;
int ret;
char* i;
/* get the device name */
if (devresolve(device, device_path, sizeof(device_path)) != 0)
return -1;
device_len = strlen(device_path);
if (device_len <= 5)
return -1;
device_file = device_path + 5; /* skip /dev/ */
device_len -= 5;
pathprint(path, sizeof(path), "/sys/dev/block/%u:%u", major(device), minor(device));
/* check if there are partitions */
d = opendir(path);
if (d == 0)
return -1;
while ((dd = readdir(d)) != 0) {
dev_t subdev;
if (dd->d_name[0] == '.')
continue;
if (strncmp(device_file, dd->d_name, device_len) != 0)
continue;
pathprint(path, sizeof(path), "/sys/dev/block/%u:%u/%s/dev", major(device), minor(device), dd->d_name);
subdev = devread(path);
if (!subdev) {
/* LCOV_EXCL_START */
closedir(d);
return -1;
/* LCOV_EXCL_STOP */
}
if (devstat_recurse(subdev, count) != 0) {
/* LCOV_EXCL_START */
closedir(d);
return -1;
/* LCOV_EXCL_STOP */
}
++partitions;
}
closedir(d);
/* stop if processed partitions */
if (partitions != 0)
return 0;
pathprint(path, sizeof(path), "/sys/dev/block/%u:%u/holders", major(device), minor(device));
/* check if there are holders */
d = opendir(path);
if (d == 0)
return -1;
while ((dd = readdir(d)) != 0) {
dev_t subdev;
if (dd->d_name[0] == '.')
continue;
/* for each slave, expand the full potential tree */
pathprint(path, sizeof(path), "/sys/dev/block/%u:%u/holders/%s/dev", major(device), minor(device), dd->d_name);
subdev = devread(path);
if (!subdev) {
/* LCOV_EXCL_START */
closedir(d);
return -1;
/* LCOV_EXCL_STOP */
}
if (devstat_recurse(subdev, count) != 0) {
/* LCOV_EXCL_START */
closedir(d);
return -1;
/* LCOV_EXCL_STOP */
}
++holders;
}
closedir(d);
/* stop if processed holders */
if (holders != 0)
return 0;
*count = 0;
pathprint(path, sizeof(path), "/sys/dev/block/%u:%u/stat", major(device), minor(device));
@ -1452,12 +1345,6 @@ static int devstat_recurse(dev_t device, uint64_t* count)
return 0;
}
static int devstat(dev_t device, uint64_t* count)
{
*count = 0;
return devstat_recurse(device, count);
}
#endif
/**
@ -1893,11 +1780,6 @@ static void* thread_smart(void* arg)
*/
devattr(devinfo->device, devinfo->info, devinfo->serial, devinfo->family, devinfo->model);
/*
* Retrieve access stat for the device
*/
devstat(devinfo->device, &devinfo->access_stat);
return 0;
#else
(void)arg;
@ -1927,11 +1809,6 @@ static void* thread_probe(void* arg)
*/
devattr(devinfo->device, devinfo->info, devinfo->serial, devinfo->family, devinfo->model);
/*
* Retrieve access stat for the device
*/
devstat(devinfo->device, &devinfo->access_stat);
return 0;
#else
(void)arg;
@ -2004,6 +1881,7 @@ int devquery(tommy_list* high, tommy_list* low, int operation, int others)
for (i = tommy_list_head(high); i != 0; i = i->next) {
devinfo_t* devinfo = i->data;
uint64_t device = devinfo->device;
uint64_t access_stat;
/* if the major is the null device, find the real one */
if (major(device) == 0) {
@ -2016,6 +1894,15 @@ int devquery(tommy_list* high, tommy_list* low, int operation, int others)
}
}
/* retrieve access stat for the high level device */
if (devstat(device, &access_stat) == 0) {
/* cumulate access stat in the first split */
if (devinfo->split)
devinfo->split->access_stat += access_stat;
else
devinfo->access_stat += access_stat;
}
/* get the device file */
if (devresolve(device, devinfo->file, sizeof(devinfo->file)) != 0) {
/* LCOV_EXCL_START */

View file

@ -1444,8 +1444,11 @@ attributes, but with the risk of spinning up all disks.
.PP
Note that a \`<disk_name>\` may be associated with more \`<device_file>\`,
in case the logical disk uses more physical disks. In such a case,
you\'ll see multiple tags for the same \`<disk_name>\` but with different
\`<device_file>\`.
you\'ll see multiple \`<attr>\` and \`<smart>\` tags for the same
\`<disk_name>\` but with different \`<device_file>\`. The exception is the
\`stat\` attrbute that is cumulative for all the devices part of the
disk.
.PP
In case of split parity, the parity name is followed by \`/N\` to indicate
the split index, where N starts from 0. The \`/0\` is always omitted.
.SS Attributes
@ -1597,8 +1600,10 @@ read.
Logs a successful spin\-down operation initiated via the
smartctl command.
.TP
.B attr:<device_file>:<disk_name>[/<split_index>]:stat:<access_number>
Logs the total number of read and write accesses done on the disk.
.B stat:<disk_name>:<access_number>
Logs the total number of read and write accesses done on the
disk. This is a cumulative value that include all the devices
and split of the disk.
Comparing this number at different times, you can detect if the
disk was used during that time.
.RS 4

View file

@ -926,8 +926,11 @@ Command Smart And Probe Tags
Note that a `<disk_name>` may be associated with more `<device_file>`,
in case the logical disk uses more physical disks. In such a case,
you'll see multiple tags for the same `<disk_name>` but with different
`<device_file>`.
you'll see multiple `<attr>` and `<smart>` tags for the same
`<disk_name>` but with different `<device_file>`. The exception is the
`stat` attrbute that is cumulative for all the devices part of the
disk.
In case of split parity, the parity name is followed by `/N` to indicate
the split index, where N starts from 0. The `/0` is always omitted.
@ -1020,8 +1023,10 @@ Command Smart And Probe Tags
Logs a successful spin-down operation initiated via the
smartctl command.
=attr:<device_file>:<disk_name>[/<split_index>]:stat:<access_number>
Logs the total number of read and write accesses done on the disk.
=stat:<disk_name>:<access_number>
Logs the total number of read and write accesses done on the
disk. This is a cumulative value that include all the devices
and split of the disk.
Comparing this number at different times, you can detect if the
disk was used during that time.

View file

@ -968,8 +968,11 @@ attributes, but with the risk of spinning up all disks.
Note that a `<disk_name>` may be associated with more `<device_file>`,
in case the logical disk uses more physical disks. In such a case,
you'll see multiple tags for the same `<disk_name>` but with different
`<device_file>`.
you'll see multiple `<attr>` and `<smart>` tags for the same
`<disk_name>` but with different `<device_file>`. The exception is the
`stat` attrbute that is cumulative for all the devices part of the
disk.
In case of split parity, the parity name is followed by `/N` to indicate
the split index, where N starts from 0. The `/0` is always omitted.
@ -1062,8 +1065,10 @@ Tags logging specific disk identification and attribute values.
Logs a successful spin-down operation initiated via the
smartctl command.
attr:<device_file>:<disk_name>[/<split_index>]:stat:<access_number>
Logs the total number of read and write accesses done on the disk.
stat:<disk_name>:<access_number>
Logs the total number of read and write accesses done on the
disk. This is a cumulative value that include all the devices
and split of the disk.
Comparing this number at different times, you can detect if the
disk was used during that time.