xref: /linux/tools/testing/selftests/sched/cs_prctl_test.c (revision a1ff5a7d78a036d6c2178ee5acd6ba4946243800)
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