// SPDX-License-Identifier: GPL-2.0 /* Copyright (c) 2026 Tejun Heo */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "enable_cmask.bpf.skel.h" #include "scx_test.h" #define SCHED_EXT 7 #define NR_CHILDREN 8 #define MAX_CPUS 1024 static int cpus[MAX_CPUS]; static int nr_cpus; static void spin_ms(int ms) { struct timespec start, now; clock_gettime(CLOCK_MONOTONIC, &start); do { clock_gettime(CLOCK_MONOTONIC, &now); } while ((now.tv_sec - start.tv_sec) * 1000 + (now.tv_nsec - start.tv_nsec) / 1000000 < ms); } static int pin(pid_t pid, int idx) { cpu_set_t set; CPU_ZERO(&set); CPU_SET(cpus[idx % nr_cpus], &set); return sched_setaffinity(pid, sizeof(set), &set); } /* * Pin, switch to SCHED_EXT for a class-switch enable, fork a grandchild that * inherits the policy for a fork-path enable, then change affinity a few times * while running for set_cmask() on live tasks. */ static int child(int idx) { struct sched_param param = {}; int i, status; pid_t pid; if (pin(0, idx) || sched_setscheduler(0, SCHED_EXT, ¶m)) return 1; pid = fork(); if (pid < 0) return 1; if (!pid) { spin_ms(20); return 0; } for (i = 1; i <= 4; i++) { if (pin(0, idx + i)) return 1; spin_ms(5); } return waitpid(pid, &status, 0) == pid && !status ? 0 : 1; } static enum scx_test_status run(void *ctx) { struct enable_cmask *skel; struct bpf_link *link; pid_t pids[NR_CHILDREN]; cpu_set_t set; int i, status, failed = 0; if (!__COMPAT_struct_has_field("scx_enable_args", "cmask_arena_addr")) return SCX_TEST_SKIP; SCX_FAIL_IF(sched_getaffinity(0, sizeof(set), &set), "Failed to read affinity"); for (i = 0; i < MAX_CPUS && i < CPU_SETSIZE; i++) if (CPU_ISSET(i, &set)) cpus[nr_cpus++] = i; if (nr_cpus < 2) return SCX_TEST_SKIP; skel = enable_cmask__open(); SCX_FAIL_IF(!skel, "Failed to open"); SCX_ENUM_INIT(skel); SCX_FAIL_IF(enable_cmask__load(skel), "Failed to load skel"); link = bpf_map__attach_struct_ops(skel->maps.enable_cmask_ops); SCX_FAIL_IF(!link, "Failed to attach struct_ops"); for (i = 0; i < NR_CHILDREN; i++) { pids[i] = fork(); SCX_FAIL_IF(pids[i] < 0, "Failed to fork"); if (!pids[i]) exit(child(i)); } /* affinity changes from the outside race with the children's own */ for (i = 0; i < NR_CHILDREN; i++) pin(pids[i], i + NR_CHILDREN); for (i = 0; i < NR_CHILDREN; i++) { if (waitpid(pids[i], &status, 0) != pids[i] || status) failed++; } bpf_link__destroy(link); SCX_EQ(skel->data->uei.kind, EXIT_KIND(SCX_EXIT_UNREG)); SCX_EQ(failed, 0); SCX_GE(skel->bss->nr_enable, 2 * NR_CHILDREN); SCX_EQ(skel->bss->nr_initial_set_cmask, skel->bss->nr_enable); SCX_GT(skel->bss->nr_set_cmask, skel->bss->nr_initial_set_cmask); SCX_GE(skel->bss->nr_set_weight, skel->bss->nr_enable); printf("enable=%lu initial_set_cmask=%lu set_cmask=%lu set_weight=%lu\n", (unsigned long)skel->bss->nr_enable, (unsigned long)skel->bss->nr_initial_set_cmask, (unsigned long)skel->bss->nr_set_cmask, (unsigned long)skel->bss->nr_set_weight); enable_cmask__destroy(skel); return SCX_TEST_PASS; } struct scx_test enable_cmask = { .name = "enable_cmask", .description = "Check the cid-form ops.enable() cmask and the set_cmask() after it", .run = run, }; REGISTER_SCX_TEST(&enable_cmask)