[PATCH] dm-stats: Avoid cumulative rounding in time conversion
From: Baek Kyu, Kim
Date: Sun Oct 25 2026 - 13:16:29 EST
dm_jiffies_to_msec64() converts a 22-bit chunk to milliseconds, rounds
it up, then multiplies that rounded value by the number of chunks. At
HZ=300, the chunk length is not an integral number of milliseconds, so
the rounding error grows with the accumulated time. For 4,194,305
jiffies, a stats print reports 13,981,018 ms instead of 13,981,017 ms.
Divide the full jiffies value by HZ and convert the remainder to
milliseconds once. This gives a single rounding of the total while
avoiding an overflowing multiplication of the full input. Preserve the
precise timestamp path, which already returns nanoseconds unchanged.
Signed-off-by: Baek Kyu, Kim <pocj8ur4in@xxxxxxxxx>
---
Tested under ARM64 QEMU/KVM with HZ=300 through the DM stats ioctl
path on a zero target, using injected time values. At 4,194,305
jiffies, the baseline printed 13,981,018 ms in all six time fields and
the patched kernel printed 13,981,017 ms. Precise timestamps and
stats_print_clear also passed.
drivers/md/dm-stats.c | 19 ++++++-------------
1 file changed, 6 insertions(+), 13 deletions(-)
diff --git a/drivers/md/dm-stats.c b/drivers/md/dm-stats.c
index beabbe3b39f3..9ec568d120a4 100644
--- a/drivers/md/dm-stats.c
+++ b/drivers/md/dm-stats.c
@@ -9,6 +9,7 @@
#include <linux/vmalloc.h>
#include <linux/mm.h>
#include <linux/module.h>
+#include <linux/math64.h>
#include <linux/device-mapper.h>
#include "dm-core.h"
@@ -831,23 +832,15 @@ static int dm_stats_clear(struct dm_stats *stats, int id)
*/
static unsigned long long dm_jiffies_to_msec64(struct dm_stat *s, unsigned long long j)
{
- unsigned long long result;
- unsigned int mult;
+ u64 result;
+ u32 remainder;
if (s->stat_flags & STAT_PRECISE_TIMESTAMPS)
return j;
- result = 0;
- if (j)
- result = jiffies_to_msecs(j & 0x3fffff);
- if (j >= 1 << 22) {
- mult = jiffies_to_msecs(1 << 22);
- result += (unsigned long long)mult * ((j >> 22) & 0x3fffff);
- }
- if (j >= 1ULL << 44)
- result += (unsigned long long)mult * (unsigned long long)(1 << 22) * (j >> 44);
-
- return result;
+ result = div_u64_rem(j, HZ, &remainder);
+ return result * MSEC_PER_SEC +
+ DIV_ROUND_UP_ULL((u64)remainder * MSEC_PER_SEC, HZ);
}
static int dm_stats_print(struct dm_stats *stats, int id,