public inbox for stable@vger.kernel.org
 help / color / mirror / Atom feed
* [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