* [PATCH v2 1/2] taskstats: retain dead thread stats in TGID queries [not found] <cover.1776020234.git.cyyzero16@gmail.com> @ 2026-04-12 19:18 ` Yiyang Chen 2026-04-13 3:00 ` Balbir Singh 0 siblings, 1 reply; 2+ messages in thread From: Yiyang Chen @ 2026-04-12 19:18 UTC (permalink / raw) To: Balbir Singh, Yang Yang, Wang Yaxin Cc: linux-kernel, Oleg Nesterov, Dr . Thomas Orgis, Andrew Morton, Yiyang Chen, stable fill_stats_for_tgid() builds TGID stats from two sources: the cached aggregate in signal->stats and a scan of the live threads in the group. However, fill_tgid_exit() only accumulates delay accounting into signal->stats. This means that once a thread exits, TGID queries lose the fields that fill_stats_for_tgid() adds for live threads. This gap was introduced incrementally by two earlier changes that extended fill_stats_for_tgid() but did not make the corresponding update to fill_tgid_exit(): - commit 8c733420bdd5 ("taskstats: add e/u/stime for TGID command") added ac_etime, ac_utime, and ac_stime to the TGID query path. - commit b663a79c1915 ("taskstats: add context-switch counters") added nvcsw and nivcsw to the TGID query path. As a result, those fields were accounted for live threads in TGID queries, but were dropped from the cached TGID aggregate after thread exit. The final TGID exit notification emitted when group_dead is true also copies that cached aggregate, so it loses the same fields. Factor the per-task TGID accumulation into tgid_stats_add_task() and use it in both fill_stats_for_tgid() and fill_tgid_exit(). This keeps the cached aggregate used for dead threads aligned with the live-thread accumulation used by TGID queries. Fixes: 8c733420bdd5 ("taskstats: add e/u/stime for TGID command") Fixes: b663a79c1915 ("taskstats: add context-switch counters") Cc: stable@vger.kernel.org Signed-off-by: Yiyang Chen <cyyzero16@gmail.com> diff --git a/kernel/taskstats.c b/kernel/taskstats.c index 0cd680ccc7e5..a80be5d9f52b 100644 --- a/kernel/taskstats.c +++ b/kernel/taskstats.c @@ -210,13 +210,39 @@ static int fill_stats_for_pid(pid_t pid, struct taskstats *stats) return 0; } +static void tgid_stats_add_task(struct taskstats *stats, + struct task_struct *tsk, u64 now_ns) +{ + u64 delta, utime, stime; + + /* + * Each accounting subsystem calls its functions here to + * accumulate its per-task stats for tsk, into the per-tgid structure + * + * per-task-foo(tsk->signal->stats, tsk); + */ + delayacct_add_tsk(stats, tsk); + + /* calculate task elapsed time in nsec */ + delta = now_ns - tsk->start_time; + /* Convert to micro seconds */ + do_div(delta, NSEC_PER_USEC); + stats->ac_etime += delta; + + task_cputime(tsk, &utime, &stime); + stats->ac_utime += div_u64(utime, NSEC_PER_USEC); + stats->ac_stime += div_u64(stime, NSEC_PER_USEC); + + stats->nvcsw += tsk->nvcsw; + stats->nivcsw += tsk->nivcsw; +} + static int fill_stats_for_tgid(pid_t tgid, struct taskstats *stats) { struct task_struct *tsk, *first; unsigned long flags; int rc = -ESRCH; - u64 delta, utime, stime; - u64 start_time; + u64 now_ns; /* * Add additional stats from live tasks except zombie thread group @@ -233,30 +259,12 @@ static int fill_stats_for_tgid(pid_t tgid, struct taskstats *stats) else memset(stats, 0, sizeof(*stats)); - start_time = ktime_get_ns(); + now_ns = ktime_get_ns(); for_each_thread(first, tsk) { if (tsk->exit_state) continue; - /* - * Accounting subsystem can call its functions here to - * fill in relevant parts of struct taskstsats as follows - * - * per-task-foo(stats, tsk); - */ - delayacct_add_tsk(stats, tsk); - - /* calculate task elapsed time in nsec */ - delta = start_time - tsk->start_time; - /* Convert to micro seconds */ - do_div(delta, NSEC_PER_USEC); - stats->ac_etime += delta; - task_cputime(tsk, &utime, &stime); - stats->ac_utime += div_u64(utime, NSEC_PER_USEC); - stats->ac_stime += div_u64(stime, NSEC_PER_USEC); - - stats->nvcsw += tsk->nvcsw; - stats->nivcsw += tsk->nivcsw; + tgid_stats_add_task(stats, tsk, now_ns); } unlock_task_sighand(first, &flags); @@ -275,18 +283,14 @@ static int fill_stats_for_tgid(pid_t tgid, struct taskstats *stats) static void fill_tgid_exit(struct task_struct *tsk) { unsigned long flags; + u64 now_ns; spin_lock_irqsave(&tsk->sighand->siglock, flags); if (!tsk->signal->stats) goto ret; - /* - * Each accounting subsystem calls its functions here to - * accumalate its per-task stats for tsk, into the per-tgid structure - * - * per-task-foo(tsk->signal->stats, tsk); - */ - delayacct_add_tsk(tsk->signal->stats, tsk); + now_ns = ktime_get_ns(); + tgid_stats_add_task(tsk->signal->stats, tsk, now_ns); ret: spin_unlock_irqrestore(&tsk->sighand->siglock, flags); return; -- 2.43.0 ^ permalink raw reply related [flat|nested] 2+ messages in thread
* Re: [PATCH v2 1/2] taskstats: retain dead thread stats in TGID queries 2026-04-12 19:18 ` [PATCH v2 1/2] taskstats: retain dead thread stats in TGID queries Yiyang Chen @ 2026-04-13 3:00 ` Balbir Singh 0 siblings, 0 replies; 2+ messages in thread From: Balbir Singh @ 2026-04-13 3:00 UTC (permalink / raw) To: Yiyang Chen, Yang Yang, Wang Yaxin Cc: linux-kernel, Oleg Nesterov, Dr . Thomas Orgis, Andrew Morton, stable On 4/13/26 05:18, Yiyang Chen wrote: > fill_stats_for_tgid() builds TGID stats from two sources: the cached > aggregate in signal->stats and a scan of the live threads in the group. > > However, fill_tgid_exit() only accumulates delay accounting into > signal->stats. This means that once a thread exits, TGID queries lose > the fields that fill_stats_for_tgid() adds for live threads. > > This gap was introduced incrementally by two earlier changes that > extended fill_stats_for_tgid() but did not make the corresponding > update to fill_tgid_exit(): > > - commit 8c733420bdd5 ("taskstats: add e/u/stime for TGID command") > added ac_etime, ac_utime, and ac_stime to the TGID query path. > - commit b663a79c1915 ("taskstats: add context-switch counters") > added nvcsw and nivcsw to the TGID query path. > > As a result, those fields were accounted for live threads in TGID > queries, but were dropped from the cached TGID aggregate after thread > exit. The final TGID exit notification emitted when group_dead is true > also copies that cached aggregate, so it loses the same fields. > > Factor the per-task TGID accumulation into tgid_stats_add_task() and > use it in both fill_stats_for_tgid() and fill_tgid_exit(). This keeps > the cached aggregate used for dead threads aligned with the live-thread > accumulation used by TGID queries. > > Fixes: 8c733420bdd5 ("taskstats: add e/u/stime for TGID command") > Fixes: b663a79c1915 ("taskstats: add context-switch counters") > Cc: stable@vger.kernel.org > Signed-off-by: Yiyang Chen <cyyzero16@gmail.com> > > diff --git a/kernel/taskstats.c b/kernel/taskstats.c > index 0cd680ccc7e5..a80be5d9f52b 100644 > --- a/kernel/taskstats.c > +++ b/kernel/taskstats.c > @@ -210,13 +210,39 @@ static int fill_stats_for_pid(pid_t pid, struct taskstats *stats) > return 0; > } > > +static void tgid_stats_add_task(struct taskstats *stats, > + struct task_struct *tsk, u64 now_ns) > +{ > + u64 delta, utime, stime; > + > + /* > + * Each accounting subsystem calls its functions here to > + * accumulate its per-task stats for tsk, into the per-tgid structure > + * > + * per-task-foo(tsk->signal->stats, tsk); > + */ The comment should read per-task-foo(stats, tsk); > + delayacct_add_tsk(stats, tsk); > + > + /* calculate task elapsed time in nsec */ > + delta = now_ns - tsk->start_time; > + /* Convert to micro seconds */ > + do_div(delta, NSEC_PER_USEC); > + stats->ac_etime += delta; > + > + task_cputime(tsk, &utime, &stime); > + stats->ac_utime += div_u64(utime, NSEC_PER_USEC); > + stats->ac_stime += div_u64(stime, NSEC_PER_USEC); > + > + stats->nvcsw += tsk->nvcsw; > + stats->nivcsw += tsk->nivcsw; > +} > + > static int fill_stats_for_tgid(pid_t tgid, struct taskstats *stats) > { > struct task_struct *tsk, *first; > unsigned long flags; > int rc = -ESRCH; > - u64 delta, utime, stime; > - u64 start_time; > + u64 now_ns; > > /* > * Add additional stats from live tasks except zombie thread group > @@ -233,30 +259,12 @@ static int fill_stats_for_tgid(pid_t tgid, struct taskstats *stats) > else > memset(stats, 0, sizeof(*stats)); > > - start_time = ktime_get_ns(); > + now_ns = ktime_get_ns(); > for_each_thread(first, tsk) { > if (tsk->exit_state) > continue; > - /* > - * Accounting subsystem can call its functions here to > - * fill in relevant parts of struct taskstsats as follows > - * > - * per-task-foo(stats, tsk); > - */ > - delayacct_add_tsk(stats, tsk); > - > - /* calculate task elapsed time in nsec */ > - delta = start_time - tsk->start_time; > - /* Convert to micro seconds */ > - do_div(delta, NSEC_PER_USEC); > - stats->ac_etime += delta; > > - task_cputime(tsk, &utime, &stime); > - stats->ac_utime += div_u64(utime, NSEC_PER_USEC); > - stats->ac_stime += div_u64(stime, NSEC_PER_USEC); > - > - stats->nvcsw += tsk->nvcsw; > - stats->nivcsw += tsk->nivcsw; > + tgid_stats_add_task(stats, tsk, now_ns); > } > > unlock_task_sighand(first, &flags); > @@ -275,18 +283,14 @@ static int fill_stats_for_tgid(pid_t tgid, struct taskstats *stats) > static void fill_tgid_exit(struct task_struct *tsk) > { > unsigned long flags; > + u64 now_ns; > > spin_lock_irqsave(&tsk->sighand->siglock, flags); > if (!tsk->signal->stats) > goto ret; > > - /* > - * Each accounting subsystem calls its functions here to > - * accumalate its per-task stats for tsk, into the per-tgid structure > - * > - * per-task-foo(tsk->signal->stats, tsk); > - */ > - delayacct_add_tsk(tsk->signal->stats, tsk); > + now_ns = ktime_get_ns(); > + tgid_stats_add_task(tsk->signal->stats, tsk, now_ns); > ret: > spin_unlock_irqrestore(&tsk->sighand->siglock, flags); > return; Acked-by: Balbir Singh <balbirs@nvidia.com> ^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-04-13 3:00 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <cover.1776020234.git.cyyzero16@gmail.com>
2026-04-12 19:18 ` [PATCH v2 1/2] taskstats: retain dead thread stats in TGID queries Yiyang Chen
2026-04-13 3:00 ` Balbir Singh
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox