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
233 lines
6.5 KiB
C
233 lines
6.5 KiB
C
// 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();
|
||
}
|