From 524733a6db10bfc0bb8fe1699e127ac04b1ce564 Mon Sep 17 00:00:00 2001 From: Andrea Mazzoleni Date: Fri, 26 Dec 2025 11:12:22 +0100 Subject: [PATCH] Make the stat tag a disk level --- cmdline/device.c | 28 ++++++++- cmdline/portable.h | 3 +- cmdline/unix.c | 137 ++++--------------------------------------- doc/snapraid_log.1 | 13 ++-- doc/snapraid_log.d | 13 ++-- doc/snapraid_log.txt | 13 ++-- 6 files changed, 67 insertions(+), 140 deletions(-) diff --git a/cmdline/device.c b/cmdline/device.c index 71fe3260..0f68623d 100644 --- a/cmdline/device.c +++ b/cmdline/device.c @@ -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); diff --git a/cmdline/portable.h b/cmdline/portable.h index bc941a0b..ab2d36c0 100644 --- a/cmdline/portable.h +++ b/cmdline/portable.h @@ -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. */ diff --git a/cmdline/unix.c b/cmdline/unix.c index a5517b96..32861927 100644 --- a/cmdline/unix.c +++ b/cmdline/unix.c @@ -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 */ diff --git a/doc/snapraid_log.1 b/doc/snapraid_log.1 index c2786f95..338adf34 100644 --- a/doc/snapraid_log.1 +++ b/doc/snapraid_log.1 @@ -1444,8 +1444,11 @@ attributes, but with the risk of spinning up all disks. .PP Note that a \`\` may be associated with more \`\`, in case the logical disk uses more physical disks. In such a case, -you\'ll see multiple tags for the same \`\` but with different -\`\`. +you\'ll see multiple \`\` and \`\` tags for the same +\`\` but with different \`\`. 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::[/]:stat: -Logs the total number of read and write accesses done on the disk. +.B stat:: +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 diff --git a/doc/snapraid_log.d b/doc/snapraid_log.d index 93253cc4..4cd9bc39 100644 --- a/doc/snapraid_log.d +++ b/doc/snapraid_log.d @@ -926,8 +926,11 @@ Command Smart And Probe Tags Note that a `` may be associated with more ``, in case the logical disk uses more physical disks. In such a case, - you'll see multiple tags for the same `` but with different - ``. + you'll see multiple `` and `` tags for the same + `` but with different ``. 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::[/]:stat: - Logs the total number of read and write accesses done on the disk. + =stat:: + 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. diff --git a/doc/snapraid_log.txt b/doc/snapraid_log.txt index fff76194..2c05bc77 100644 --- a/doc/snapraid_log.txt +++ b/doc/snapraid_log.txt @@ -968,8 +968,11 @@ attributes, but with the risk of spinning up all disks. Note that a `` may be associated with more ``, in case the logical disk uses more physical disks. In such a case, -you'll see multiple tags for the same `` but with different -``. +you'll see multiple `` and `` tags for the same +`` but with different ``. 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::[/]:stat: - Logs the total number of read and write accesses done on the disk. + stat:: + 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.