1 // SPDX-License-Identifier: GPL-2.0 2 /* Copyright (c) 2026 Tejun Heo <tj@kernel.org> */ 3 #define _GNU_SOURCE 4 #include <sched.h> 5 #include <stdio.h> 6 #include <stdlib.h> 7 #include <time.h> 8 #include <unistd.h> 9 #include <sys/wait.h> 10 #include <bpf/bpf.h> 11 #include <scx/common.h> 12 #include "enable_cmask.bpf.skel.h" 13 #include "scx_test.h" 14 15 #define SCHED_EXT 7 16 #define NR_CHILDREN 8 17 #define MAX_CPUS 1024 18 19 static int cpus[MAX_CPUS]; 20 static int nr_cpus; 21 22 static void spin_ms(int ms) 23 { 24 struct timespec start, now; 25 26 clock_gettime(CLOCK_MONOTONIC, &start); 27 do { 28 clock_gettime(CLOCK_MONOTONIC, &now); 29 } while ((now.tv_sec - start.tv_sec) * 1000 + 30 (now.tv_nsec - start.tv_nsec) / 1000000 < ms); 31 } 32 33 static int pin(pid_t pid, int idx) 34 { 35 cpu_set_t set; 36 37 CPU_ZERO(&set); 38 CPU_SET(cpus[idx % nr_cpus], &set); 39 return sched_setaffinity(pid, sizeof(set), &set); 40 } 41 42 /* 43 * Pin, switch to SCHED_EXT for a class-switch enable, fork a grandchild that 44 * inherits the policy for a fork-path enable, then change affinity a few times 45 * while running for set_cmask() on live tasks. 46 */ 47 static int child(int idx) 48 { 49 struct sched_param param = {}; 50 int i, status; 51 pid_t pid; 52 53 if (pin(0, idx) || sched_setscheduler(0, SCHED_EXT, ¶m)) 54 return 1; 55 56 pid = fork(); 57 if (pid < 0) 58 return 1; 59 if (!pid) { 60 spin_ms(20); 61 return 0; 62 } 63 64 for (i = 1; i <= 4; i++) { 65 if (pin(0, idx + i)) 66 return 1; 67 spin_ms(5); 68 } 69 70 return waitpid(pid, &status, 0) == pid && !status ? 0 : 1; 71 } 72 73 static enum scx_test_status run(void *ctx) 74 { 75 struct enable_cmask *skel; 76 struct bpf_link *link; 77 pid_t pids[NR_CHILDREN]; 78 cpu_set_t set; 79 int i, status, failed = 0; 80 81 if (!__COMPAT_struct_has_field("scx_enable_args", "cmask_arena_addr")) 82 return SCX_TEST_SKIP; 83 84 SCX_FAIL_IF(sched_getaffinity(0, sizeof(set), &set), "Failed to read affinity"); 85 for (i = 0; i < MAX_CPUS && i < CPU_SETSIZE; i++) 86 if (CPU_ISSET(i, &set)) 87 cpus[nr_cpus++] = i; 88 if (nr_cpus < 2) 89 return SCX_TEST_SKIP; 90 91 skel = enable_cmask__open(); 92 SCX_FAIL_IF(!skel, "Failed to open"); 93 SCX_ENUM_INIT(skel); 94 SCX_FAIL_IF(enable_cmask__load(skel), "Failed to load skel"); 95 96 link = bpf_map__attach_struct_ops(skel->maps.enable_cmask_ops); 97 SCX_FAIL_IF(!link, "Failed to attach struct_ops"); 98 99 for (i = 0; i < NR_CHILDREN; i++) { 100 pids[i] = fork(); 101 SCX_FAIL_IF(pids[i] < 0, "Failed to fork"); 102 if (!pids[i]) 103 exit(child(i)); 104 } 105 106 /* affinity changes from the outside race with the children's own */ 107 for (i = 0; i < NR_CHILDREN; i++) 108 pin(pids[i], i + NR_CHILDREN); 109 110 for (i = 0; i < NR_CHILDREN; i++) { 111 if (waitpid(pids[i], &status, 0) != pids[i] || status) 112 failed++; 113 } 114 115 bpf_link__destroy(link); 116 117 SCX_EQ(skel->data->uei.kind, EXIT_KIND(SCX_EXIT_UNREG)); 118 SCX_EQ(failed, 0); 119 SCX_GE(skel->bss->nr_enable, 2 * NR_CHILDREN); 120 SCX_EQ(skel->bss->nr_initial_set_cmask, skel->bss->nr_enable); 121 SCX_GT(skel->bss->nr_set_cmask, skel->bss->nr_initial_set_cmask); 122 SCX_GE(skel->bss->nr_set_weight, skel->bss->nr_enable); 123 printf("enable=%lu initial_set_cmask=%lu set_cmask=%lu set_weight=%lu\n", 124 (unsigned long)skel->bss->nr_enable, 125 (unsigned long)skel->bss->nr_initial_set_cmask, 126 (unsigned long)skel->bss->nr_set_cmask, 127 (unsigned long)skel->bss->nr_set_weight); 128 129 enable_cmask__destroy(skel); 130 return SCX_TEST_PASS; 131 } 132 133 struct scx_test enable_cmask = { 134 .name = "enable_cmask", 135 .description = "Check the cid-form ops.enable() cmask and the set_cmask() after it", 136 .run = run, 137 }; 138 REGISTER_SCX_TEST(&enable_cmask) 139