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 <[email protected]> --- 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,
