19f269900SChris Hyser // SPDX-License-Identifier: GPL-2.0-only
29f269900SChris Hyser /*
39f269900SChris Hyser * Use the core scheduling prctl() to test core scheduling cookies control.
49f269900SChris Hyser *
59f269900SChris Hyser * Copyright (c) 2021 Oracle and/or its affiliates.
69f269900SChris Hyser * Author: Chris Hyser <chris.hyser@oracle.com>
79f269900SChris Hyser *
89f269900SChris Hyser *
99f269900SChris Hyser * This library is free software; you can redistribute it and/or modify it
109f269900SChris Hyser * under the terms of version 2.1 of the GNU Lesser General Public License as
119f269900SChris Hyser * published by the Free Software Foundation.
129f269900SChris Hyser *
139f269900SChris Hyser * This library is distributed in the hope that it will be useful, but WITHOUT
149f269900SChris Hyser * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
159f269900SChris Hyser * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
169f269900SChris Hyser * for more details.
179f269900SChris Hyser *
189f269900SChris Hyser * You should have received a copy of the GNU Lesser General Public License
199f269900SChris Hyser * along with this library; if not, see <http://www.gnu.org/licenses>.
209f269900SChris Hyser */
219f269900SChris Hyser
229f269900SChris Hyser #define _GNU_SOURCE
239f269900SChris Hyser #include <sys/eventfd.h>
249f269900SChris Hyser #include <sys/wait.h>
259f269900SChris Hyser #include <sys/types.h>
269f269900SChris Hyser #include <sched.h>
279f269900SChris Hyser #include <sys/prctl.h>
289f269900SChris Hyser #include <unistd.h>
299f269900SChris Hyser #include <time.h>
30a7151a8eSShuah Khan #include <errno.h>
319f269900SChris Hyser #include <stdio.h>
329f269900SChris Hyser #include <stdlib.h>
339f269900SChris Hyser #include <string.h>
349f269900SChris Hyser
359f269900SChris Hyser #if __GLIBC_PREREQ(2, 30) == 0
369f269900SChris Hyser #include <sys/syscall.h>
gettid(void)379f269900SChris Hyser static pid_t gettid(void)
389f269900SChris Hyser {
399f269900SChris Hyser return syscall(SYS_gettid);
409f269900SChris Hyser }
419f269900SChris Hyser #endif
429f269900SChris Hyser
439f269900SChris Hyser #ifndef PR_SCHED_CORE
449f269900SChris Hyser #define PR_SCHED_CORE 62
459f269900SChris Hyser #define PR_SCHED_CORE_GET 0
469f269900SChris Hyser #define PR_SCHED_CORE_CREATE 1 /* create unique core_sched cookie */
479f269900SChris Hyser #define PR_SCHED_CORE_SHARE_TO 2 /* push core_sched cookie to pid */
489f269900SChris Hyser #define PR_SCHED_CORE_SHARE_FROM 3 /* pull core_sched cookie to pid */
499f269900SChris Hyser #define PR_SCHED_CORE_MAX 4
509f269900SChris Hyser #endif
519f269900SChris Hyser
529f269900SChris Hyser #define MAX_PROCESSES 128
539f269900SChris Hyser #define MAX_THREADS 128
549f269900SChris Hyser
559f269900SChris Hyser static const char USAGE[] = "cs_prctl_test [options]\n"
569f269900SChris Hyser " options:\n"
579f269900SChris Hyser " -P : number of processes to create.\n"
589f269900SChris Hyser " -T : number of threads per process to create.\n"
599f269900SChris Hyser " -d : delay time to keep tasks alive.\n"
609f269900SChris Hyser " -k : keep tasks alive until keypress.\n";
619f269900SChris Hyser
629f269900SChris Hyser enum pid_type {PIDTYPE_PID = 0, PIDTYPE_TGID, PIDTYPE_PGID};
639f269900SChris Hyser
649f269900SChris Hyser const int THREAD_CLONE_FLAGS = CLONE_THREAD | CLONE_SIGHAND | CLONE_FS | CLONE_VM | CLONE_FILES;
659f269900SChris Hyser
661c36432bSLi Zhijian struct child_args {
671c36432bSLi Zhijian int num_threads;
681c36432bSLi Zhijian int pfd[2];
691c36432bSLi Zhijian int cpid;
701c36432bSLi Zhijian int thr_tids[MAX_THREADS];
711c36432bSLi Zhijian };
721c36432bSLi Zhijian
731c36432bSLi Zhijian static struct child_args procs[MAX_PROCESSES];
741c36432bSLi Zhijian static int num_processes = 2;
75130a8387SAtul Kumar Pant static int need_cleanup;
761c36432bSLi Zhijian
_prctl(int option,unsigned long arg2,unsigned long arg3,unsigned long arg4,unsigned long arg5)779f269900SChris Hyser static int _prctl(int option, unsigned long arg2, unsigned long arg3, unsigned long arg4,
789f269900SChris Hyser unsigned long arg5)
799f269900SChris Hyser {
809f269900SChris Hyser int res;
819f269900SChris Hyser
829f269900SChris Hyser res = prctl(option, arg2, arg3, arg4, arg5);
839f269900SChris Hyser printf("%d = prctl(%d, %ld, %ld, %ld, %lx)\n", res, option, (long)arg2, (long)arg3,
849f269900SChris Hyser (long)arg4, arg5);
859f269900SChris Hyser return res;
869f269900SChris Hyser }
879f269900SChris Hyser
889f269900SChris Hyser #define STACK_SIZE (1024 * 1024)
899f269900SChris Hyser
909f269900SChris Hyser #define handle_error(msg) __handle_error(__FILE__, __LINE__, msg)
__handle_error(char * fn,int ln,char * msg)919f269900SChris Hyser static void __handle_error(char *fn, int ln, char *msg)
929f269900SChris Hyser {
931c36432bSLi Zhijian int pidx;
949f269900SChris Hyser printf("(%s:%d) - ", fn, ln);
959f269900SChris Hyser perror(msg);
961c36432bSLi Zhijian if (need_cleanup) {
971c36432bSLi Zhijian for (pidx = 0; pidx < num_processes; ++pidx)
981c36432bSLi Zhijian kill(procs[pidx].cpid, 15);
991c36432bSLi Zhijian need_cleanup = 0;
1001c36432bSLi Zhijian }
1019f269900SChris Hyser exit(EXIT_FAILURE);
1029f269900SChris Hyser }
1039f269900SChris Hyser
handle_usage(int rc,char * msg)1049f269900SChris Hyser static void handle_usage(int rc, char *msg)
1059f269900SChris Hyser {
1069f269900SChris Hyser puts(USAGE);
1079f269900SChris Hyser puts(msg);
1089f269900SChris Hyser putchar('\n');
1099f269900SChris Hyser exit(rc);
1109f269900SChris Hyser }
1119f269900SChris Hyser
get_cs_cookie(int pid)1129f269900SChris Hyser static unsigned long get_cs_cookie(int pid)
1139f269900SChris Hyser {
1149f269900SChris Hyser unsigned long long cookie;
1159f269900SChris Hyser int ret;
1169f269900SChris Hyser
1179f269900SChris Hyser ret = prctl(PR_SCHED_CORE, PR_SCHED_CORE_GET, pid, PIDTYPE_PID,
1189f269900SChris Hyser (unsigned long)&cookie);
1199f269900SChris Hyser if (ret) {
1209f269900SChris Hyser printf("Not a core sched system\n");
1219f269900SChris Hyser return -1UL;
1229f269900SChris Hyser }
1239f269900SChris Hyser
1249f269900SChris Hyser return cookie;
1259f269900SChris Hyser }
1269f269900SChris Hyser
child_func_thread(void * arg)1279f269900SChris Hyser static int child_func_thread(void __attribute__((unused))*arg)
1289f269900SChris Hyser {
1299f269900SChris Hyser while (1)
1309f269900SChris Hyser usleep(20000);
1319f269900SChris Hyser return 0;
1329f269900SChris Hyser }
1339f269900SChris Hyser
create_threads(int num_threads,int thr_tids[])1349f269900SChris Hyser static void create_threads(int num_threads, int thr_tids[])
1359f269900SChris Hyser {
1369f269900SChris Hyser void *child_stack;
1379f269900SChris Hyser pid_t tid;
1389f269900SChris Hyser int i;
1399f269900SChris Hyser
1409f269900SChris Hyser for (i = 0; i < num_threads; ++i) {
1419f269900SChris Hyser child_stack = malloc(STACK_SIZE);
1429f269900SChris Hyser if (!child_stack)
1439f269900SChris Hyser handle_error("child stack allocate");
1449f269900SChris Hyser
1459f269900SChris Hyser tid = clone(child_func_thread, child_stack + STACK_SIZE, THREAD_CLONE_FLAGS, NULL);
1469f269900SChris Hyser if (tid == -1)
1479f269900SChris Hyser handle_error("clone thread");
1489f269900SChris Hyser thr_tids[i] = tid;
1499f269900SChris Hyser }
1509f269900SChris Hyser }
1519f269900SChris Hyser
child_func_process(void * arg)1529f269900SChris Hyser static int child_func_process(void *arg)
1539f269900SChris Hyser {
1549f269900SChris Hyser struct child_args *ca = (struct child_args *)arg;
155a7151a8eSShuah Khan int ret;
1569f269900SChris Hyser
1579f269900SChris Hyser close(ca->pfd[0]);
1589f269900SChris Hyser
1599f269900SChris Hyser create_threads(ca->num_threads, ca->thr_tids);
1609f269900SChris Hyser
161a7151a8eSShuah Khan ret = write(ca->pfd[1], &ca->thr_tids, sizeof(int) * ca->num_threads);
162a7151a8eSShuah Khan if (ret == -1)
163a7151a8eSShuah Khan printf("write failed on pfd[%d] - error (%s)\n",
164a7151a8eSShuah Khan ca->pfd[1], strerror(errno));
165a7151a8eSShuah Khan
1669f269900SChris Hyser close(ca->pfd[1]);
1679f269900SChris Hyser
1689f269900SChris Hyser while (1)
1699f269900SChris Hyser usleep(20000);
1709f269900SChris Hyser return 0;
1719f269900SChris Hyser }
1729f269900SChris Hyser
1739f269900SChris Hyser static unsigned char child_func_process_stack[STACK_SIZE];
1749f269900SChris Hyser
create_processes(int num_processes,int num_threads,struct child_args proc[])1759f269900SChris Hyser void create_processes(int num_processes, int num_threads, struct child_args proc[])
1769f269900SChris Hyser {
1779f269900SChris Hyser pid_t cpid;
178a7151a8eSShuah Khan int i, ret;
1799f269900SChris Hyser
1809f269900SChris Hyser for (i = 0; i < num_processes; ++i) {
1819f269900SChris Hyser proc[i].num_threads = num_threads;
1829f269900SChris Hyser
1839f269900SChris Hyser if (pipe(proc[i].pfd) == -1)
1849f269900SChris Hyser handle_error("pipe() failed");
1859f269900SChris Hyser
1869f269900SChris Hyser cpid = clone(child_func_process, child_func_process_stack + STACK_SIZE,
1879f269900SChris Hyser SIGCHLD, &proc[i]);
1889f269900SChris Hyser proc[i].cpid = cpid;
1899f269900SChris Hyser close(proc[i].pfd[1]);
1909f269900SChris Hyser }
1919f269900SChris Hyser
1929f269900SChris Hyser for (i = 0; i < num_processes; ++i) {
193a7151a8eSShuah Khan ret = read(proc[i].pfd[0], &proc[i].thr_tids, sizeof(int) * proc[i].num_threads);
194a7151a8eSShuah Khan if (ret == -1)
195a7151a8eSShuah Khan printf("read failed on proc[%d].pfd[0] error (%s)\n",
196a7151a8eSShuah Khan i, strerror(errno));
1979f269900SChris Hyser close(proc[i].pfd[0]);
1989f269900SChris Hyser }
1999f269900SChris Hyser }
2009f269900SChris Hyser
disp_processes(int num_processes,struct child_args proc[])2019f269900SChris Hyser void disp_processes(int num_processes, struct child_args proc[])
2029f269900SChris Hyser {
2039f269900SChris Hyser int i, j;
2049f269900SChris Hyser
2059f269900SChris Hyser printf("tid=%d, / tgid=%d / pgid=%d: %lx\n", gettid(), getpid(), getpgid(0),
2069f269900SChris Hyser get_cs_cookie(getpid()));
2079f269900SChris Hyser
2089f269900SChris Hyser for (i = 0; i < num_processes; ++i) {
2099f269900SChris Hyser printf(" tid=%d, / tgid=%d / pgid=%d: %lx\n", proc[i].cpid, proc[i].cpid,
2109f269900SChris Hyser getpgid(proc[i].cpid), get_cs_cookie(proc[i].cpid));
2119f269900SChris Hyser for (j = 0; j < proc[i].num_threads; ++j) {
2129f269900SChris Hyser printf(" tid=%d, / tgid=%d / pgid=%d: %lx\n", proc[i].thr_tids[j],
2139f269900SChris Hyser proc[i].cpid, getpgid(0), get_cs_cookie(proc[i].thr_tids[j]));
2149f269900SChris Hyser }
2159f269900SChris Hyser }
2169f269900SChris Hyser puts("\n");
2179f269900SChris Hyser }
2189f269900SChris Hyser
2199f269900SChris Hyser static int errors;
2209f269900SChris Hyser
2219f269900SChris Hyser #define validate(v) _validate(__LINE__, v, #v)
_validate(int line,int val,char * msg)2229f269900SChris Hyser void _validate(int line, int val, char *msg)
2239f269900SChris Hyser {
2249f269900SChris Hyser if (!val) {
2259f269900SChris Hyser ++errors;
2269f269900SChris Hyser printf("(%d) FAILED: %s\n", line, msg);
2279f269900SChris Hyser } else {
2289f269900SChris Hyser printf("(%d) PASSED: %s\n", line, msg);
2299f269900SChris Hyser }
2309f269900SChris Hyser }
2319f269900SChris Hyser
main(int argc,char * argv[])2329f269900SChris Hyser int main(int argc, char *argv[])
2339f269900SChris Hyser {
2349f269900SChris Hyser int keypress = 0;
2359f269900SChris Hyser int num_threads = 3;
2369f269900SChris Hyser int delay = 0;
2379f269900SChris Hyser int res = 0;
2389f269900SChris Hyser int pidx;
2399f269900SChris Hyser int pid;
2409f269900SChris Hyser int opt;
2419f269900SChris Hyser
2429f269900SChris Hyser while ((opt = getopt(argc, argv, ":hkT:P:d:")) != -1) {
2439f269900SChris Hyser switch (opt) {
2449f269900SChris Hyser case 'P':
2459f269900SChris Hyser num_processes = (int)strtol(optarg, NULL, 10);
2469f269900SChris Hyser break;
2479f269900SChris Hyser case 'T':
2489f269900SChris Hyser num_threads = (int)strtoul(optarg, NULL, 10);
2499f269900SChris Hyser break;
2509f269900SChris Hyser case 'd':
2519f269900SChris Hyser delay = (int)strtol(optarg, NULL, 10);
2529f269900SChris Hyser break;
2539f269900SChris Hyser case 'k':
2549f269900SChris Hyser keypress = 1;
2559f269900SChris Hyser break;
2569f269900SChris Hyser case 'h':
2579f269900SChris Hyser printf(USAGE);
2589f269900SChris Hyser exit(EXIT_SUCCESS);
2599f269900SChris Hyser default:
2609f269900SChris Hyser handle_usage(20, "unknown option");
2619f269900SChris Hyser }
2629f269900SChris Hyser }
2639f269900SChris Hyser
2649f269900SChris Hyser if (num_processes < 1 || num_processes > MAX_PROCESSES)
2659f269900SChris Hyser handle_usage(1, "Bad processes value");
2669f269900SChris Hyser
2679f269900SChris Hyser if (num_threads < 1 || num_threads > MAX_THREADS)
2689f269900SChris Hyser handle_usage(2, "Bad thread value");
2699f269900SChris Hyser
2709f269900SChris Hyser if (keypress)
2719f269900SChris Hyser delay = -1;
2729f269900SChris Hyser
2739f269900SChris Hyser srand(time(NULL));
2749f269900SChris Hyser
2759f269900SChris Hyser /* put into separate process group */
2769f269900SChris Hyser if (setpgid(0, 0) != 0)
2779f269900SChris Hyser handle_error("process group");
2789f269900SChris Hyser
279*6f1a214dSColin Ian King printf("\n## Create a thread/process/process group hierarchy\n");
2809f269900SChris Hyser create_processes(num_processes, num_threads, procs);
2811c36432bSLi Zhijian need_cleanup = 1;
2829f269900SChris Hyser disp_processes(num_processes, procs);
2839f269900SChris Hyser validate(get_cs_cookie(0) == 0);
2849f269900SChris Hyser
2859f269900SChris Hyser printf("\n## Set a cookie on entire process group\n");
2869f269900SChris Hyser if (_prctl(PR_SCHED_CORE, PR_SCHED_CORE_CREATE, 0, PIDTYPE_PGID, 0) < 0)
2879f269900SChris Hyser handle_error("core_sched create failed -- PGID");
2889f269900SChris Hyser disp_processes(num_processes, procs);
2899f269900SChris Hyser
2909f269900SChris Hyser validate(get_cs_cookie(0) != 0);
2919f269900SChris Hyser
2929f269900SChris Hyser /* get a random process pid */
2939f269900SChris Hyser pidx = rand() % num_processes;
2949f269900SChris Hyser pid = procs[pidx].cpid;
2959f269900SChris Hyser
2969f269900SChris Hyser validate(get_cs_cookie(0) == get_cs_cookie(pid));
2979f269900SChris Hyser validate(get_cs_cookie(0) == get_cs_cookie(procs[pidx].thr_tids[0]));
2989f269900SChris Hyser
2999f269900SChris Hyser printf("\n## Set a new cookie on entire process/TGID [%d]\n", pid);
3009f269900SChris Hyser if (_prctl(PR_SCHED_CORE, PR_SCHED_CORE_CREATE, pid, PIDTYPE_TGID, 0) < 0)
3019f269900SChris Hyser handle_error("core_sched create failed -- TGID");
3029f269900SChris Hyser disp_processes(num_processes, procs);
3039f269900SChris Hyser
3049f269900SChris Hyser validate(get_cs_cookie(0) != get_cs_cookie(pid));
3059f269900SChris Hyser validate(get_cs_cookie(pid) != 0);
3069f269900SChris Hyser validate(get_cs_cookie(pid) == get_cs_cookie(procs[pidx].thr_tids[0]));
3079f269900SChris Hyser
3089f269900SChris Hyser printf("\n## Copy the cookie of current/PGID[%d], to pid [%d] as PIDTYPE_PID\n",
3099f269900SChris Hyser getpid(), pid);
3109f269900SChris Hyser if (_prctl(PR_SCHED_CORE, PR_SCHED_CORE_SHARE_TO, pid, PIDTYPE_PID, 0) < 0)
3119f269900SChris Hyser handle_error("core_sched share to itself failed -- PID");
3129f269900SChris Hyser disp_processes(num_processes, procs);
3139f269900SChris Hyser
3149f269900SChris Hyser validate(get_cs_cookie(0) == get_cs_cookie(pid));
3159f269900SChris Hyser validate(get_cs_cookie(pid) != 0);
3169f269900SChris Hyser validate(get_cs_cookie(pid) != get_cs_cookie(procs[pidx].thr_tids[0]));
3179f269900SChris Hyser
3189f269900SChris Hyser printf("\n## Copy cookie from a thread [%d] to current/PGID [%d] as PIDTYPE_PID\n",
3199f269900SChris Hyser procs[pidx].thr_tids[0], getpid());
3209f269900SChris Hyser if (_prctl(PR_SCHED_CORE, PR_SCHED_CORE_SHARE_FROM, procs[pidx].thr_tids[0],
3219f269900SChris Hyser PIDTYPE_PID, 0) < 0)
3229f269900SChris Hyser handle_error("core_sched share from thread failed -- PID");
3239f269900SChris Hyser disp_processes(num_processes, procs);
3249f269900SChris Hyser
3259f269900SChris Hyser validate(get_cs_cookie(0) == get_cs_cookie(procs[pidx].thr_tids[0]));
3269f269900SChris Hyser validate(get_cs_cookie(pid) != get_cs_cookie(procs[pidx].thr_tids[0]));
3279f269900SChris Hyser
3289f269900SChris Hyser printf("\n## Copy cookie from current [%d] to current as pidtype PGID\n", getpid());
3299f269900SChris Hyser if (_prctl(PR_SCHED_CORE, PR_SCHED_CORE_SHARE_TO, 0, PIDTYPE_PGID, 0) < 0)
3309f269900SChris Hyser handle_error("core_sched share to self failed -- PGID");
3319f269900SChris Hyser disp_processes(num_processes, procs);
3329f269900SChris Hyser
3339f269900SChris Hyser validate(get_cs_cookie(0) == get_cs_cookie(pid));
3349f269900SChris Hyser validate(get_cs_cookie(pid) != 0);
3359f269900SChris Hyser validate(get_cs_cookie(pid) == get_cs_cookie(procs[pidx].thr_tids[0]));
3369f269900SChris Hyser
33714f4cc63SIvan Orlov validate(_prctl(PR_SCHED_CORE, PR_SCHED_CORE_MAX, 0, PIDTYPE_PGID, 0) < 0
33814f4cc63SIvan Orlov && errno == EINVAL);
33914f4cc63SIvan Orlov
34014f4cc63SIvan Orlov validate(_prctl(PR_SCHED_CORE, PR_SCHED_CORE_SHARE_TO, 0, PIDTYPE_PGID, 1) < 0
34114f4cc63SIvan Orlov && errno == EINVAL);
34214f4cc63SIvan Orlov
3439f269900SChris Hyser if (errors) {
3449f269900SChris Hyser printf("TESTS FAILED. errors: %d\n", errors);
3459f269900SChris Hyser res = 10;
3469f269900SChris Hyser } else {
3479f269900SChris Hyser printf("SUCCESS !!!\n");
3489f269900SChris Hyser }
3499f269900SChris Hyser
3509f269900SChris Hyser if (keypress)
3519f269900SChris Hyser getchar();
3529f269900SChris Hyser else
3539f269900SChris Hyser sleep(delay);
3549f269900SChris Hyser
3559f269900SChris Hyser for (pidx = 0; pidx < num_processes; ++pidx)
3569f269900SChris Hyser kill(procs[pidx].cpid, 15);
3579f269900SChris Hyser
3589f269900SChris Hyser return res;
3599f269900SChris Hyser }
360