On 2026-06-19 00:54, Paul Eggert wrote:
from dd's point of view, it asked for the time and got the wrong time from the kernel.
On looking into this a bit more, my guess is that the underlying kernel and/or firmware bug is not likely to be fixed any time soon, so I installed the attached workaround to coreutils. Please give it a try.
From 2e7707c23e1a05d12dbe41676b3759ef4f74a812 Mon Sep 17 00:00:00 2001 From: Paul Eggert <[email protected]> Date: Sat, 20 Jun 2026 12:14:55 -0700 Subject: [PATCH] dd: work around Linux kernel CLOCK_MONOTONIC bug MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem reported by Sick Pigs (bug#81269). Ordinarily we’d say “fix the kernel or the hardware”, but this bug seems widespread and unlikely to be fixed any time soon, and the workaround is not a lot of trouble. * src/dd.c (real_start_time): New static var. (getrealxtime): New static function. (print_xfer_stats): Use the max of elapsed monotonic and elapsed real time. --- NEWS | 3 +++ src/dd.c | 26 ++++++++++++++++++++------ 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/NEWS b/NEWS index 4ffd690de..b639141a9 100644 --- a/NEWS +++ b/NEWS @@ -12,6 +12,9 @@ GNU coreutils NEWS -*- outline -*- will correctly match the last delimiter specified. [bug introduced with multi-byte support in coreutils-9.11] + 'dd conv=fsync' no longer outputs bogus stats if the monotonic clock freezes. + [This works around a too-common bug in the Linux kernel.] + 'head' and 'tail' now quote names in file headers when needed. [This bug was present in "the beginning".] diff --git a/src/dd.c b/src/dd.c index 26382a233..9512372f6 100644 --- a/src/dd.c +++ b/src/dd.c @@ -181,8 +181,8 @@ static intmax_t w_bytes = 0; /* Last-reported number of bytes written, or negative if never reported. */ static intmax_t reported_w_bytes = -1; -/* Time that dd started. */ -static xtime_t start_time; +/* Time that dd started, in both monotonic and real time. */ +static xtime_t start_time, real_start_time; /* Next time to report periodic progress. */ static xtime_t next_time; @@ -740,12 +740,20 @@ abbreviation_lacks_prefix (char const *message) return message[strlen (message) - 2] == ' '; } +static xtime_t +getrealxtime (void) +{ + struct timespec now = current_timespec (); + return xtime_make (now.tv_sec, now.tv_nsec); +} + /* Print transfer statistics. */ static void print_xfer_stats (xtime_t progress_time) { xtime_t now = progress_time ? progress_time : gethrxtime (); + xtime_t real_now = getrealxtime (); static char const slash_s[] = "/s"; char hbuf[3][LONGEST_HUMAN_READABLE + sizeof slash_s]; double delta_s; @@ -754,14 +762,19 @@ print_xfer_stats (xtime_t progress_time) char const *iec = human_readable (w_bytes, hbuf[1], human_opts | human_base_1024, 1, 1); - /* Use integer arithmetic to compute the transfer rate, - since that makes it easy to use SI abbreviations. */ + /* Use integer arithmetic to compute the transfer rate, since that + makes it easy to use SI abbreviations. Although we want the + elapsed monotonic time, too often the Linux kernel stops its + monotonic clocks during an fsync/fdatasync (coreutils bug#81269), + and perhaps other kernels have similar bugs. So take the maximum + of the elapsed monotonic and real times, even though this can + overestimate if the real clock was set during our run. */ char *bpsbuf = hbuf[2]; int bpsbufsize = sizeof hbuf[2]; - if (start_time < now) + xtime_t delta_xtime = MAX (now - start_time, real_now - real_start_time); + if (0 < delta_xtime) { double XTIME_PRECISIONe0 = XTIME_PRECISION; - xtime_t delta_xtime = now - start_time; delta_s = delta_xtime / XTIME_PRECISIONe0; bytes_per_second = human_readable (w_bytes, bpsbuf, human_opts, XTIME_PRECISION, delta_xtime); @@ -2537,6 +2550,7 @@ main (int argc, char **argv) } start_time = gethrxtime (); + real_start_time = getrealxtime (); next_time = start_time + XTIME_PRECISION; int copy_status = dd_copy (); -- 2.53.0
