From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from DM1PR04CU001.outbound.protection.outlook.com (mail-centralusazon11010008.outbound.protection.outlook.com [52.101.61.8]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4BE9F14EC73 for ; Mon, 13 Apr 2026 03:04:08 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=52.101.61.8 ARC-Seal:i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776049449; cv=fail; b=RjyzQhV4d1DIeCMv7pRiPKdOfOt7rKjqIptGvON9Dycn51yaUaT4apt9JUIne4DXPyC0oMve1UbHRpHZJc9NZgxZrxomGrGpTSfAF/SVSMy4I7d9GCubt3OmmlgLb+KilhReuKNPjSSvpF1gaMlYX1gNcr1SBDjXPJwuQ5p/8Y0= ARC-Message-Signature:i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776049449; c=relaxed/simple; bh=/LvcPfkB6H2vvsfuPUhkeK0oO1XZitB1jl7Xj45SEe4=; h=Message-ID:Date:Subject:To:Cc:References:From:In-Reply-To: Content-Type:MIME-Version; b=i/qiyuiQCUhBGs1P5wjxYA4QmrnjSMUPnbhG74myxvNs+PeBVpzwsRzeGEa1RDETrojWieUrmJs75TO6Om4yHDZfeYoU6T0CsVBqB1i3Bol/+woFIIrO5RKgKQcJUgnQhkg/OC2FIiV/UsEVJqnJb0sgqKoqBr0pcEXegTWD710= ARC-Authentication-Results:i=2; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=nvidia.com; spf=fail smtp.mailfrom=nvidia.com; dkim=pass (2048-bit key) header.d=Nvidia.com header.i=@Nvidia.com header.b=VGGgmH+t; arc=fail smtp.client-ip=52.101.61.8 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=nvidia.com Authentication-Results: smtp.subspace.kernel.org; spf=fail smtp.mailfrom=nvidia.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=Nvidia.com header.i=@Nvidia.com header.b="VGGgmH+t" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=BiS5gdko35dIGZJTzz9FCdRXslgxiFMggYqblrnAZN7YkYOTVowjJgOrJWI0JZKfTlhVdhW9TidA6zKLSM4SUvIT8syNrdvG53hiVLoPA87GZD4tWQUwZNK05kUi6vczkL/SDij1hXjZVOdHaRZ/Vp2wi0nNtWSHK1mSltv2trJE/eLaM+JRNATVVn3grhsocQjENXxkynW4pU8oRcAVsJnrCpk8AcCPzgabnHyb3mXsmdluBHxKH02jlTanxfG5htVpPr1bXz4cmVe3S0swYngCibNdMXoqhj/6NAfZSjmeMKcC+Ix0DcLR4efNGZ3G4KnimKW9zFK9Fdc0+ZaJuA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=ofs4Gp32Vly+KA5DX0mws/INkKKlGnYlV3eD/dTMTEE=; b=SqRGofCfMAQD28iLoEAlNgLu6gKsRVLPTCRgysBuizAM94MfIJrOUD0W4Nn3zJV0hUnFgegAOTpm39gzRsZMDPMZQEnvTGiDrKhpCUEDv98MhrUgAXRnemH+8j4XmTBVJL2jh/ROi7ASSv1hK2efqHKLZDL29zWIxNcpYG4Ock5MJQxyq5piuWUKq0wiUAshNSjrVKHQ9XL7Rq3MVUUTBKuQf4JSAqH7loc2AerbLs5yg6Pk+kei7pz88hHdHUjtyi1zT1XAMAmb1U6E08Fld/im9RhS7cgg1aA5ctXMwnkPzMVAV+DdO9WKUnky2dJg7E1Y668ahIiWDntzB6EzLw== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=nvidia.com; dmarc=pass action=none header.from=nvidia.com; dkim=pass header.d=nvidia.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=Nvidia.com; s=selector2; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=ofs4Gp32Vly+KA5DX0mws/INkKKlGnYlV3eD/dTMTEE=; b=VGGgmH+tGUE4y+EQtNx5PwjBCLBNPBO7xoP+KAnbdz8Kvs40sG0FC0kSQ1iRfY6o7n3i01aVRAFZZJRs0CvqDy454BY1exIrMAXrwjsyfT64TFBc4JNLtei7XTtts5qp9AuYF1pIfZVL1b/yIUA0daWfoimM7sl6ZVHka6SZjB6siD2SxcAhPJaBqv4guL0dTPjRmxO8CE3eUdYgcIb8wtt2NZKiW7ESsT0yoyI3EPvDGbiU8PmrlbFVqQMhQKVkprg4jTX65C0XiWq7M7a+FRAmeXyAm+grEHrF0QpY+y7OTB/EwoBZGPNoZn+ieQSBB0v31wEqPjnQjUdpVJ+t9Q== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nvidia.com; Received: from PH8PR12MB7277.namprd12.prod.outlook.com (2603:10b6:510:223::13) by CY8PR12MB7540.namprd12.prod.outlook.com (2603:10b6:930:97::15) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9769.48; Mon, 13 Apr 2026 03:04:05 +0000 Received: from PH8PR12MB7277.namprd12.prod.outlook.com ([fe80::2920:e6d9:4461:e2b4]) by PH8PR12MB7277.namprd12.prod.outlook.com ([fe80::2920:e6d9:4461:e2b4%5]) with mapi id 15.20.9818.017; Mon, 13 Apr 2026 03:04:05 +0000 Message-ID: <6b521e88-8f8d-4c3f-894b-000c52d39750@nvidia.com> Date: Mon, 13 Apr 2026 13:04:00 +1000 User-Agent: Mozilla Thunderbird Subject: Re: [PATCH v2 2/2] selftests/acct: add taskstats TGID retention test To: Yiyang Chen , Yang Yang , Wang Yaxin Cc: linux-kernel@vger.kernel.org, Oleg Nesterov , "Dr . Thomas Orgis" , Andrew Morton References: Content-Language: en-US From: Balbir Singh In-Reply-To: Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 7bit X-ClientProxiedBy: BY5PR13CA0027.namprd13.prod.outlook.com (2603:10b6:a03:180::40) To PH8PR12MB7277.namprd12.prod.outlook.com (2603:10b6:510:223::13) Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: PH8PR12MB7277:EE_|CY8PR12MB7540:EE_ X-MS-Office365-Filtering-Correlation-Id: 91701009-8f69-49e6-ebb6-08de9909524d X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|366016|376014|1800799024|56012099003|22082099003|18002099003; X-Microsoft-Antispam-Message-Info: yJGZRs8xmuJlx0HjSDTin+BCHPw90J9rRImVnx05eFfep3N12Xj8ayna0QRjhiQoSC6OK6eXXIZYzZcP0lDyhhA2FsB5QI/SPIKDq4C7cFEqwLC+4+/zZtHe5+3BxxUlsucISGj2byFf1RvDqfyRZgMh0fc14cFBUeWoYTEUYUtOQ8LrDDA8VDG/nQBzwrwyKkw6PjOhvMEIzUhs2AU86Zm2caKhwGifJjvufCRpfKolMasNvJsI9xgmZrzFAJXTunzdG+xySFVyZSIrKiqfQVX1a/8enF4mMKoYdKelbSOi6cOuH22//wbyndCpKQYV63YZ6N2MInoeLxsEx9idn9klCLcqGP5zvvmugPDmzEfhDx7Ri+Kk17Z3L/3x66D8H6o5HO7XJV6N5Ev7LQVQ8IN4FturvGxaVk7VYCm18YJm9StYGrmXl1DcquHMgsYUfPHcpOa/b0DeRtA/0l8ZHBSVzgEtYYlNvkmfVcu/YIqR3fAAi/ARzI3BURio9eBoXr/JcONvrwlw2Hrm0LEqpQxkaN041VRWbVf9DXOcRfYS+BNRJr5jLQcQb7IHLbUpKOQlyFTbqVFzmhTzZZBQb5no9YbG5yeKRzAC4raPXRaLbJ7J5iD/YbR298ccsN9OkeZo014jMycHV4yv/s7oJXTvWygh7Q+/yNpCRuWaV2i9E3MzfwLFhylEQKqgkkgizSBarZzeiPXBjxB0uzcwWambhQksS9ImbJQoxH2sUMc= X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:PH8PR12MB7277.namprd12.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(366016)(376014)(1800799024)(56012099003)(22082099003)(18002099003);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?utf-8?B?bjJuaWJ5OGZSaW5iU0RTclJkR0lmYk5BbEQ3UjNIZmNNOVcyTXM0Y3hINk5Z?= =?utf-8?B?b0RXWU9acFpIUHR4SVRuV0J1ZzIraGhQdW9OSE9qOUJQNHFwY1JlanAxQTJh?= =?utf-8?B?OUFqMG5QWlByZU5rRGpXRXVBOURNa0hLZjlFR0IyNXRHbEVGRm0xOG95RnBP?= =?utf-8?B?TTQzQjM3cFNDd1JlY3hwQ2JNZ2YrSkJtZXVEUjN2QmprNEU3WE1qOFhHZVB2?= =?utf-8?B?QVhDQWpvaFZIaHk1UHE0UTJpQlE4QXlpWG5DOVF6T1ZsWjh4QTRHYXJNSG1D?= =?utf-8?B?TjZiREJaSHZrWVJZY3FvMWFSbTFkYjc0RzJNZHNhb1BvMTNjUEViTVlUbWV5?= =?utf-8?B?M2lvbk5GcjZ0Y3Z6TzBBSWUxdmlqYklwNmh1N0xFNWlhY0R5a05xMnpKVGRx?= =?utf-8?B?WVpFQVV4WTMwRkZJN3BtMGpkclo4TlpBZHM3Y0d1dUxxNk5GUWRpMGYwaXBD?= =?utf-8?B?NnZ6TmZPK2VucnFwSmxCWmQ0bVJzMnpEUE5XNEN6YW1tT2hHb1psQzhMdmx3?= =?utf-8?B?SGgvTm5LT0xwVUgzYS9IUSthaHRFVThjTS9CTU1VRkpBVFd4ZzY5UUFSN2Qz?= =?utf-8?B?VFVUTXVxQ2FEa0NlcCs2OTYwTHI4WWxEYTRlbU05Umx1QVhzRU9XRzhqTkcw?= =?utf-8?B?TlluNG9iSUVuSnJFL2ZmeWgxdUdHSjFTTklnbXVPUXZyR2JEMHgvQTlXWHFE?= =?utf-8?B?bnhVSGNQaXBINHFXQjJoV2pJM3BxWjJCekE4ZzVyMGRNNWhNaFRScGNvc0R1?= =?utf-8?B?cU15bDNweHJOWDV4K1dTcFlKb1J1TkFDZWZscy9DdnRwelR3OHBpdE9PWFRm?= =?utf-8?B?WldFRVp5UE1McVFLQ213TGhIaTRiNHcxYlJEQnEzVTlnQ2lZaS94azcrS24x?= =?utf-8?B?Yk5BUEw4eXkxRjhiOGZEdHIvUHN0Zy9nZGRMWFJ5aVRvR1g2b3Npb0hGakRl?= =?utf-8?B?MmhTKzE4THVyZGdSajI0TlBBbUxKQ01QdVRwbjU0cm9lR1RiTkFpMnZsRFgz?= =?utf-8?B?aTNFNFFobmpPb25OUGthc3dKWk5hQkZ5Z1padktTRHdlREdmNXJtOGdlYXNT?= =?utf-8?B?SWFZYTZ1aExlNUR5K0VaSHhVczhUUGNtWk85NGt4a3crZlhUUmdJT084NGZh?= =?utf-8?B?NnpzaTFndFFsc3dIZldIL0V2a09ESm5QMEZqTlhxN1NBcGJYWHJtZWtNNmhN?= =?utf-8?B?R1c3cHVMYnFCZmNUU0RBYnhmSjB6ckVSREYxM0NkZWQyNEdlK1hqMTdSRGFs?= =?utf-8?B?eThJbFlFREM2cXQrM1FhWWJLcGZEZWE4MDYrd1dLcVhLSVhLRlJMOEYxWkdo?= =?utf-8?B?eE1FL0t1cE1LQldSaGp3S215elJmYTFEVUROek14ckNPUy95Y0QrQTNpdXNG?= =?utf-8?B?KzFVbmI2WjlQZjd0Wkl3ay9JdzE3TFhRY0VIK0N5cmhWTkpyMFhCejdwaHIv?= =?utf-8?B?UnFMaVg2a0F5UFo2V096VGlGMXJKVk9nYTQ2Z1lXSkNNS0UvZkhiU3lGZzVu?= =?utf-8?B?TW05N3B0NUR3clNBMEdxcDVVdmNJcHVjbVNtWXZHUmZsYWVuUzJPMm1OSzIz?= =?utf-8?B?ZC9zN3AyLzM1OG1rdW9IbGNRVzk2VGRwLzNsT0xxUzFBWFFVSnNyeEJDS1pH?= =?utf-8?B?bXIxYTFtK3lkSzZxSHI4cUc1UEhlcW8yVk84VVg5MkZnblpIQVhBbCt4SFh3?= =?utf-8?B?cjhsaUVNcTgwVXV3aFZZcVM5aGtjYld5YjJXbGVTeGhlNk01S05lYWRyYWQ0?= =?utf-8?B?VytOamFZb3Jqb1VTWVVwN2xaV1BtN2lubFQvMFZWN0orNXBZZUhTTmxLeXFi?= =?utf-8?B?YXpxZXRrWEM5RUdsMXBaNkNhMlErL0JpQ3FPOU1CVlVJWDV6UFE2UGdXZ0Vq?= =?utf-8?B?S0hGUGJ1c3NZSW8vVkF1REx4cDMrZ3phWE10cU1OUkw5THVONUVDbHFjYlAx?= =?utf-8?B?WlY3MHg2ODR0R0Y4OHdJcG1wQm5SbGJWazgxQnZwd2hqT1ZSRHk0R2NWS0x3?= =?utf-8?B?QjRGWngyYU1TL1BYVHBOYjdUVzJXcVlYcWRGNlhaWFRBOWlUWjE3UXV0RDdZ?= =?utf-8?B?YUg3a3g5Nk95RWtKNjhhMk54MVhDcHd1cmhuSTBZazZhOTg2YnkzVHdiWWNr?= =?utf-8?B?NmpqTElocXNPM3BnbUtTalppYURpeW9BS1BYZFBaWU81OG0vN21tWTcydmRH?= =?utf-8?B?a0NvWWhIWjMwd1BuUXZ5YThYblJrbFZ1b1RHUFhxMnhUOXJzNlpKaHZmMnAy?= =?utf-8?B?UDN1MmhIQldNaVk0dllqdFI4VnNidi8rbUZYOGozZEd1MmxWeUR2Mk4xaHEw?= =?utf-8?B?QmdWMWxJU2xNalBLbkpzbVo4QnhCNWFlZzVYM01MNzduY1Q1Y3UxQT09?= X-OriginatorOrg: Nvidia.com X-MS-Exchange-CrossTenant-Network-Message-Id: 91701009-8f69-49e6-ebb6-08de9909524d X-MS-Exchange-CrossTenant-AuthSource: PH8PR12MB7277.namprd12.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 13 Apr 2026 03:04:04.9323 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 43083d15-7273-40c1-b7db-39efd9ccc17a X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: Nosev4FwvuZAPxi3H3tOIAzKoCAQE6ASz0s/UiNEl3kHk3kfPIBq/zl2lcdJXihUdEfGaUxMU5reJGvC9v5kQg== X-MS-Exchange-Transport-CrossTenantHeadersStamped: CY8PR12MB7540 On 4/13/26 05:18, Yiyang Chen wrote: > Add a kselftest for the taskstats TGID aggregation fix. > > The test creates a worker thread, snapshots TGID taskstats while the > worker is still alive, lets the worker exit, and then verifies that the > TGID CPU total does not regress after the thread has been reaped. > > The pass/fail check intentionally keys off ac_utime + ac_stime only, > which is the primary user-visible regression fixed by the taskstats > change and is less sensitive to scheduling noise than context-switch > counters. > > Signed-off-by: Yiyang Chen > > diff --git a/tools/testing/selftests/acct/.gitignore b/tools/testing/selftests/acct/.gitignore > index 7e78aac19038..9e9c61c5bfd6 100644 > --- a/tools/testing/selftests/acct/.gitignore > +++ b/tools/testing/selftests/acct/.gitignore > @@ -1,3 +1,4 @@ > acct_syscall > +taskstats_fill_stats_tgid > config > -process_log > \ No newline at end of file > +process_log > diff --git a/tools/testing/selftests/acct/Makefile b/tools/testing/selftests/acct/Makefile > index 7e025099cf65..083cab5ddb72 100644 > --- a/tools/testing/selftests/acct/Makefile > +++ b/tools/testing/selftests/acct/Makefile > @@ -1,5 +1,8 @@ > # SPDX-License-Identifier: GPL-2.0 > TEST_GEN_PROGS := acct_syscall > +TEST_GEN_PROGS += taskstats_fill_stats_tgid > + > CFLAGS += -Wall > +LDLIBS += -lpthread > > -include ../lib.mk > \ No newline at end of file > +include ../lib.mk > diff --git a/tools/testing/selftests/acct/taskstats_fill_stats_tgid.c b/tools/testing/selftests/acct/taskstats_fill_stats_tgid.c > new file mode 100644 > index 000000000000..d6cab4ae26f2 > --- /dev/null > +++ b/tools/testing/selftests/acct/taskstats_fill_stats_tgid.c > @@ -0,0 +1,375 @@ > +// SPDX-License-Identifier: GPL-2.0 > +#define _GNU_SOURCE > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include "kselftest.h" > + > +#ifndef NLA_ALIGN > +#define NLA_ALIGNTO 4 > +#define NLA_ALIGN(len) (((len) + NLA_ALIGNTO - 1) & ~(NLA_ALIGNTO - 1)) > +#define NLA_HDRLEN ((int)NLA_ALIGN(sizeof(struct nlattr))) > +#endif > + > +#define BUSY_NS (200ULL * 1000 * 1000) > + > +struct worker_ctx { > + pthread_mutex_t lock; > + pthread_cond_t cond; > + bool ready; > + bool release; > +}; > + > +static unsigned long busy_sink; > + > +static void *taskstats_nla_data(const struct nlattr *na) > +{ > + return (void *)((char *)na + NLA_HDRLEN); > +} > + > +static bool taskstats_nla_ok(const struct nlattr *na, int remaining) > +{ > + return remaining >= (int)sizeof(*na) && > + na->nla_len >= sizeof(*na) && > + na->nla_len <= remaining; > +} > + > +static struct nlattr *taskstats_nla_next(const struct nlattr *na, int *remaining) > +{ > + int aligned_len = NLA_ALIGN(na->nla_len); > + > + *remaining -= aligned_len; > + return (struct nlattr *)((char *)na + aligned_len); > +} > + > +static uint64_t timespec_diff_ns(const struct timespec *start, > + const struct timespec *end) > +{ > + return (uint64_t)(end->tv_sec - start->tv_sec) * 1000000000ULL + > + (uint64_t)(end->tv_nsec - start->tv_nsec); > +} > + > +static void burn_cpu_for_ns(uint64_t runtime_ns) > +{ > + struct timespec start, now; > + unsigned long acc = 0; > + > + if (clock_gettime(CLOCK_MONOTONIC, &start)) { > + perror("clock_gettime"); > + exit(EXIT_FAILURE); > + } > + > + do { > + for (int i = 0; i < 100000; i++) > + acc += i; > + if (clock_gettime(CLOCK_MONOTONIC, &now)) { > + perror("clock_gettime"); > + exit(EXIT_FAILURE); > + } > + } while (timespec_diff_ns(&start, &now) < runtime_ns); > + > + busy_sink = acc; > +} > + > +static int netlink_open(void) > +{ > + struct sockaddr_nl addr = { > + .nl_family = AF_NETLINK, > + .nl_pid = getpid(), > + }; > + int fd; > + > + fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); > + if (fd < 0) > + return -errno; > + > + if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { > + int err = -errno; > + > + close(fd); > + return err; > + } > + > + return fd; > +} > + > +static int send_request(int fd, void *buf, size_t len) > +{ > + struct sockaddr_nl addr = { > + .nl_family = AF_NETLINK, > + }; > + > + if (sendto(fd, buf, len, 0, (struct sockaddr *)&addr, sizeof(addr)) < 0) > + return -errno; > + > + return 0; > +} > + > +static int get_family_id(int fd, const char *name) > +{ > + struct { > + struct nlmsghdr nlh; > + struct genlmsghdr genl; > + char buf[256]; > + } req = { 0 }; > + char resp[8192]; > + struct nlmsghdr *nlh; > + struct genlmsghdr *genl; > + struct nlattr *na; > + int len; > + int rem; > + int ret; > + > + req.nlh.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); > + req.nlh.nlmsg_type = GENL_ID_CTRL; > + req.nlh.nlmsg_flags = NLM_F_REQUEST; > + req.nlh.nlmsg_seq = 1; > + req.nlh.nlmsg_pid = getpid(); > + > + req.genl.cmd = CTRL_CMD_GETFAMILY; > + req.genl.version = 1; > + > + na = (struct nlattr *)((char *)&req + NLMSG_ALIGN(req.nlh.nlmsg_len)); > + na->nla_type = CTRL_ATTR_FAMILY_NAME; > + na->nla_len = NLA_HDRLEN + strlen(name) + 1; > + memcpy(taskstats_nla_data(na), name, strlen(name) + 1); > + req.nlh.nlmsg_len = NLMSG_ALIGN(req.nlh.nlmsg_len) + NLA_ALIGN(na->nla_len); > + > + ret = send_request(fd, &req, req.nlh.nlmsg_len); > + if (ret) > + return ret; > + > + len = recv(fd, resp, sizeof(resp), 0); > + if (len < 0) > + return -errno; > + > + for (nlh = (struct nlmsghdr *)resp; NLMSG_OK(nlh, len); > + nlh = NLMSG_NEXT(nlh, len)) { > + if (nlh->nlmsg_type == NLMSG_ERROR) { > + struct nlmsgerr *err = NLMSG_DATA(nlh); > + > + return err->error ? err->error : -ENOENT; > + } > + > + genl = (struct genlmsghdr *)NLMSG_DATA(nlh); > + rem = nlh->nlmsg_len - NLMSG_HDRLEN - GENL_HDRLEN; > + na = (struct nlattr *)((char *)genl + GENL_HDRLEN); > + while (taskstats_nla_ok(na, rem)) { > + if (na->nla_type == CTRL_ATTR_FAMILY_ID) > + return *(uint16_t *)taskstats_nla_data(na); > + na = taskstats_nla_next(na, &rem); > + } > + } > + > + return -ENOENT; > +} > + > +static int get_taskstats(int fd, int family_id, uint16_t attr_type, uint32_t id, > + struct taskstats *stats) > +{ > + struct { > + struct nlmsghdr nlh; > + struct genlmsghdr genl; > + char buf[256]; > + } req = { 0 }; > + char resp[16384]; > + struct nlmsghdr *nlh; > + struct genlmsghdr *genl; > + struct nlattr *na; > + struct nlattr *nested; > + int len; > + int rem; > + int nrem; > + int ret; > + > + memset(stats, 0, sizeof(*stats)); > + > + req.nlh.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); > + req.nlh.nlmsg_type = family_id; > + req.nlh.nlmsg_flags = NLM_F_REQUEST; > + req.nlh.nlmsg_seq = 2; > + req.nlh.nlmsg_pid = getpid(); > + > + req.genl.cmd = TASKSTATS_CMD_GET; > + req.genl.version = 1; > + > + na = (struct nlattr *)((char *)&req + NLMSG_ALIGN(req.nlh.nlmsg_len)); > + na->nla_type = attr_type; > + na->nla_len = NLA_HDRLEN + sizeof(id); > + memcpy(taskstats_nla_data(na), &id, sizeof(id)); > + req.nlh.nlmsg_len = NLMSG_ALIGN(req.nlh.nlmsg_len) + NLA_ALIGN(na->nla_len); > + > + ret = send_request(fd, &req, req.nlh.nlmsg_len); > + if (ret) > + return ret; > + > + len = recv(fd, resp, sizeof(resp), 0); > + if (len < 0) > + return -errno; > + > + for (nlh = (struct nlmsghdr *)resp; NLMSG_OK(nlh, len); > + nlh = NLMSG_NEXT(nlh, len)) { > + if (nlh->nlmsg_type == NLMSG_ERROR) { > + struct nlmsgerr *err = NLMSG_DATA(nlh); > + > + return err->error ? err->error : -ENOENT; > + } > + > + genl = (struct genlmsghdr *)NLMSG_DATA(nlh); > + rem = nlh->nlmsg_len - NLMSG_HDRLEN - GENL_HDRLEN; > + na = (struct nlattr *)((char *)genl + GENL_HDRLEN); > + while (taskstats_nla_ok(na, rem)) { > + if (na->nla_type == TASKSTATS_TYPE_AGGR_PID || > + na->nla_type == TASKSTATS_TYPE_AGGR_TGID) { > + nested = (struct nlattr *)taskstats_nla_data(na); > + nrem = na->nla_len - NLA_HDRLEN; > + while (taskstats_nla_ok(nested, nrem)) { > + if (nested->nla_type == TASKSTATS_TYPE_STATS) { > + memcpy(stats, taskstats_nla_data(nested), > + sizeof(*stats)); > + return 0; > + } > + nested = taskstats_nla_next(nested, &nrem); > + } > + } > + na = taskstats_nla_next(na, &rem); > + } > + } > + > + return -ENOENT; > +} > + > +static uint64_t cpu_total(const struct taskstats *stats) > +{ > + return (uint64_t)stats->ac_utime + (uint64_t)stats->ac_stime; > +} > + > +static void print_stats(const char *label, const struct taskstats *stats) > +{ > + ksft_print_msg("%s: cpu_total=%llu nvcsw=%llu nivcsw=%llu\n", > + label, (unsigned long long)cpu_total(stats), > + (unsigned long long)stats->nvcsw, > + (unsigned long long)stats->nivcsw); > +} > + > +static void *worker_thread(void *arg) > +{ > + struct worker_ctx *ctx = arg; > + > + burn_cpu_for_ns(BUSY_NS); > + > + pthread_mutex_lock(&ctx->lock); > + ctx->ready = true; > + pthread_cond_broadcast(&ctx->cond); > + while (!ctx->release) > + pthread_cond_wait(&ctx->cond, &ctx->lock); > + pthread_mutex_unlock(&ctx->lock); > + > + return NULL; > +} > + > +int main(void) > +{ > + struct worker_ctx ctx = { > + .lock = PTHREAD_MUTEX_INITIALIZER, > + .cond = PTHREAD_COND_INITIALIZER, > + }; > + struct taskstats before, after; > + pthread_t thread; > + pid_t tgid = getpid(); > + int family_id; > + int fd; > + int ret; > + > + ksft_print_header(); > + ksft_set_plan(1); > + > + if (geteuid()) > + ksft_exit_skip("taskstats_fill_stats_tgid needs root\n"); > + > + fd = netlink_open(); > + if (fd < 0) > + ksft_exit_skip("failed to open generic netlink socket: %s\n", > + strerror(-fd)); > + > + family_id = get_family_id(fd, TASKSTATS_GENL_NAME); > + if (family_id < 0) > + ksft_exit_skip("taskstats generic netlink family unavailable: %s\n", > + strerror(-family_id)); > + > + /* Create worker thread that burns 200ms of CPU */ > + if (pthread_create(&thread, NULL, worker_thread, &ctx) != 0) > + ksft_exit_fail_msg("pthread_create failed: %s\n", strerror(errno)); > + > + /* Wait for worker to finish generating activity */ > + pthread_mutex_lock(&ctx.lock); > + while (!ctx.ready) > + pthread_cond_wait(&ctx.cond, &ctx.lock); > + pthread_mutex_unlock(&ctx.lock); > + > + /* > + * Snapshot A: TGID stats while worker is alive and sleeping. > + * Contains main thread + worker contributions. > + */ > + ret = get_taskstats(fd, family_id, TASKSTATS_CMD_ATTR_TGID, tgid, &before); > + if (ret) > + ksft_exit_fail_msg("TGID query before exit failed: %s\n", > + strerror(-ret)); > + > + /* Release worker so it can exit, then join (deterministic wait). > + * > + * Kernel exit path ordering guarantees: > + * do_exit() > + * taskstats_exit() -> fill_tgid_exit() (accumulates worker into signal->stats) > + * exit_notify() (releases the thread) > + * do_task_dead() -> __schedule() (wakes joiner) > + * > + * So pthread_join() returns only after fill_tgid_exit() has completed. > + */ > + pthread_mutex_lock(&ctx.lock); > + ctx.release = true; > + pthread_cond_broadcast(&ctx.cond); > + pthread_mutex_unlock(&ctx.lock); > + > + pthread_join(thread, NULL); > + > + /* > + * Snapshot B: TGID stats after worker has exited. > + * fill_stats_for_tgid() does: > + * memcpy(signal->stats) <- includes fill_tgid_exit accumulation > + * + scan live threads <- only main thread now > + */ > + ret = get_taskstats(fd, family_id, TASKSTATS_CMD_ATTR_TGID, tgid, &after); > + if (ret) > + ksft_exit_fail_msg("TGID query after exit failed: %s\n", > + strerror(-ret)); > + > + print_stats("TGID before worker exit", &before); > + print_stats("TGID after worker exit", &after); > + > + /* > + * The worker burned 200ms of CPU before the first snapshot. > + * If the kernel correctly retained its contribution via > + * fill_tgid_exit(), then the TGID CPU total after exit must be at > + * least as large as the TGID CPU total before exit. > + */ > + ksft_test_result(cpu_total(&after) >= cpu_total(&before), > + "TGID CPU stats should not regress after thread exit\n"); > + > + close(fd); > + ksft_finished(); > + return ksft_get_fail_cnt() ? KSFT_FAIL : KSFT_PASS; > +} Thanks, the test strategy looks good! I would like to refactor the infra/setup code for netlink and taskstats in the future, if new tests are added Acked-by: Balbir Singh