[PATCH v2] delayacct: Preserve delay extrema when aggregating taskstats
From: Hungyu Lin
Date: Thu Oct 22 2026 - 12:17:26 EST
delayacct_add_tsk() adds each task's statistics to the accumulated TGID
statistics, but overwrites the delay maxima, minima and peak timestamps.
Consequently, the last task determines these fields, and a task with no
recorded delay can erase an earlier task's peak despite a nonzero total.
For example, a four-thread process has a worker writing copy-on-write
pages and a last-created thread with no WPCOPY events. With the group
stopped, getdelays -d -t 49 reports the following before and after the
fix (WPCOPY excerpt, spacing compacted and timestamp column omitted):
Before:
WPCOPY count delay total delay average delay max delay min
4103 5163661 0.001ms 0.000000ms 0.000000ms
After:
WPCOPY count delay total delay average delay max delay min
4101 4680983 0.001ms 0.049292ms 0.000208ms
The timings vary between runs. Before the fix, the zero extrema disagree
with the individual worker's nonzero values. After the fix, the group
extrema match the largest maximum and smallest nonzero minimum across
the individual threads.
Keep the largest maximum and its associated timestamp, and combine the
minima with min_not_zero() since zero denotes an unrecorded delay. Apply
this to CPU delay and the seven delay types handled by UPDATE_DELAY().
Single-task queries still start with zeroed statistics and retain the
task's values.
Fixes: 658eb5ab916d ("delayacct: add delay max to record delay peak")
Fixes: f65c64f311ee ("delayacct: add delay min to record delay peak")
Cc: stable@xxxxxxxxxxxxxxx
Reviewed-by: Bradley Morgan <brads@xxxxxxxxxxxxxx>
Signed-off-by: Hungyu Lin <dennylin0707@xxxxxxxxx>
---
Changes in v2:
- Add a getdelays -d -t before/after example to the commit message, as
requested by Andrew Morton.
- Add Bradley Morgan's Reviewed-by.
- No code changes.
kernel/delayacct.c | 23 +++++++++++++++--------
1 file changed, 15 insertions(+), 8 deletions(-)
diff --git a/kernel/delayacct.c b/kernel/delayacct.c
index 479e860aff70..ea1424524654 100644
--- a/kernel/delayacct.c
+++ b/kernel/delayacct.c
@@ -12,14 +12,18 @@
#include <linux/taskstats.h>
#include <linux/sysctl.h>
#include <linux/delayacct.h>
+#include <linux/minmax.h>
#include <linux/module.h>
#define UPDATE_DELAY(type) \
do { \
- d->type##_delay_max = tsk->delays->type##_delay_max; \
- d->type##_delay_min = tsk->delays->type##_delay_min; \
- d->type##_delay_max_ts.tv_sec = tsk->delays->type##_delay_max_ts.tv_sec; \
- d->type##_delay_max_ts.tv_nsec = tsk->delays->type##_delay_max_ts.tv_nsec; \
+ if (tsk->delays->type##_delay_max > d->type##_delay_max) { \
+ d->type##_delay_max = tsk->delays->type##_delay_max; \
+ d->type##_delay_max_ts.tv_sec = tsk->delays->type##_delay_max_ts.tv_sec; \
+ d->type##_delay_max_ts.tv_nsec = tsk->delays->type##_delay_max_ts.tv_nsec; \
+ } \
+ d->type##_delay_min = min_not_zero(d->type##_delay_min, \
+ tsk->delays->type##_delay_min); \
tmp = d->type##_delay_total + tsk->delays->type##_delay; \
d->type##_delay_total = (tmp < d->type##_delay_total) ? 0 : tmp; \
d->type##_count += tsk->delays->type##_count; \
@@ -174,10 +178,13 @@ int delayacct_add_tsk(struct taskstats *d, struct task_struct *tsk)
d->cpu_count += t1;
- d->cpu_delay_max = tsk->sched_info.max_run_delay;
- d->cpu_delay_min = tsk->sched_info.min_run_delay;
- d->cpu_delay_max_ts.tv_sec = tsk->sched_info.max_run_delay_ts.tv_sec;
- d->cpu_delay_max_ts.tv_nsec = tsk->sched_info.max_run_delay_ts.tv_nsec;
+ if (tsk->sched_info.max_run_delay > d->cpu_delay_max) {
+ d->cpu_delay_max = tsk->sched_info.max_run_delay;
+ d->cpu_delay_max_ts.tv_sec = tsk->sched_info.max_run_delay_ts.tv_sec;
+ d->cpu_delay_max_ts.tv_nsec = tsk->sched_info.max_run_delay_ts.tv_nsec;
+ }
+ d->cpu_delay_min = min_not_zero(d->cpu_delay_min,
+ tsk->sched_info.min_run_delay);
tmp = (s64)d->cpu_delay_total + t2;
d->cpu_delay_total = (tmp < (s64)d->cpu_delay_total) ? 0 : tmp;
tmp = (s64)d->cpu_run_virtual_total + t3;
--
2.43.0