[PATCH 1/1] coresched: Manage core scheduling cookies for tasks

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Signed-off-by: Thijs Raymakers <thijs@xxxxxxxxxxxx>
---
 .gitignore                  |   1 +
 bash-completion/coresched   |   0
 configure.ac                |  12 +-
 meson.build                 |  16 +-
 meson_options.txt           |   2 +-
 schedutils/Makemodule.am    |   8 +
 schedutils/coresched.1.adoc |  16 ++
 schedutils/coresched.c      | 353 ++++++++++++++++++++++++++++++++++++
 8 files changed, 402 insertions(+), 6 deletions(-)
 create mode 100644 bash-completion/coresched
 create mode 100644 schedutils/coresched.1.adoc
 create mode 100644 schedutils/coresched.c

diff --git a/.gitignore b/.gitignore
index 6ecbfa7fe..316f3cdcc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -94,6 +94,7 @@ ylwrap
 /colcrt
 /colrm
 /column
+/coresched
 /ctrlaltdel
 /delpart
 /dmesg
diff --git a/bash-completion/coresched b/bash-completion/coresched
new file mode 100644
index 000000000..e69de29bb
diff --git a/configure.ac b/configure.ac
index ab7c98636..3a189a075 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2500,9 +2500,9 @@ UL_REQUIRES_HAVE([setterm], [ncursesw, ncurses], [ncursesw or ncurses library])
 AM_CONDITIONAL([BUILD_SETTERM], [test "x$build_setterm" = xyes])
 
 # build_schedutils= is just configure-only variable to control
-# ionice, taskset and chrt
+# ionice, taskset, coresched and chrt
 AC_ARG_ENABLE([schedutils],
-  AS_HELP_STRING([--disable-schedutils], [do not build chrt, ionice, taskset]),
+  AS_HELP_STRING([--disable-schedutils], [do not build chrt, ionice, taskset, coresched]),
   [], [UL_DEFAULT_ENABLE([schedutils], [check])]
 )
 
@@ -2545,6 +2545,14 @@ UL_REQUIRES_SYSCALL_CHECK([taskset],
 AM_CONDITIONAL([BUILD_TASKSET], [test "x$build_taskset" = xyes])
 
 
+UL_ENABLE_ALIAS([coresched], [schedutils])
+UL_BUILD_INIT([coresched])
+UL_REQUIRES_SYSCALL_CHECK([coresched],
+	[UL_CHECK_SYSCALL([prctl])],
+	[prctl])
+AM_CONDITIONAL([BUILD_CORESCHED], [test "x$build_coresched" = xyes])
+
+
 have_schedsetter=no
 AS_IF([test "x$ac_cv_func_sched_setscheduler" = xyes], [have_schedsetter=yes],
       [test "x$ac_cv_func_sched_setattr" = xyes], [have_schedsetter=yes])
diff --git a/meson.build b/meson.build
index 9600ce49f..9a2c04e8f 100644
--- a/meson.build
+++ b/meson.build
@@ -3111,13 +3111,23 @@ exe4 = executable(
   install : opt,
   build_by_default : opt)
 
+exe5 = executable(
+  'coresched',
+  'schedutils/coresched.c',
+  include_directories : includes,
+  link_with : lib_common,
+  install_dir : usrbin_exec_dir,
+  install : opt,
+  build_by_default : opt)
+
 if opt and not is_disabler(exe)
-  exes += [exe, exe2, exe3, exe4]
+  exes += [exe, exe2, exe3, exe4, exe5]
   manadocs += ['schedutils/chrt.1.adoc',
                'schedutils/ionice.1.adoc',
                'schedutils/taskset.1.adoc',
-	       'schedutils/uclampset.1.adoc']
-  bashcompletions += ['chrt', 'ionice', 'taskset', 'uclampset']
+	       'schedutils/uclampset.1.adoc',
+         'schedutils/coresched.1.adoc']
+  bashcompletions += ['chrt', 'ionice', 'taskset', 'uclampset', 'coresched']
 endif
 
 ############################################################
diff --git a/meson_options.txt b/meson_options.txt
index 7b8cf3f35..3405c1b73 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -162,7 +162,7 @@ option('build-pipesz', type : 'feature',
 option('build-setterm', type : 'feature',
        description : 'build setterm')
 option('build-schedutils', type : 'feature',
-       description : 'build chrt, ionice, taskset')
+       description : 'build chrt, ionice, taskset, coresched')
 option('build-wall', type : 'feature',
        description : 'build wall')
 option('build-write', type : 'feature',
diff --git a/schedutils/Makemodule.am b/schedutils/Makemodule.am
index 1040da85f..0cb655401 100644
--- a/schedutils/Makemodule.am
+++ b/schedutils/Makemodule.am
@@ -29,3 +29,11 @@ dist_noinst_DATA += schedutils/uclampset.1.adoc
 uclampset_SOURCES = schedutils/uclampset.c schedutils/sched_attr.h
 uclampset_LDADD = $(LDADD) libcommon.la
 endif
+
+if BUILD_CORESCHED
+usrbin_exec_PROGRAMS += coresched
+MANPAGES += schedutils/coresched.1
+dist_noinst_DATA += schedutils/coresched.1.adoc
+coresched_SOURCES = schedutils/coresched.c
+coresched_LDADD = $(LDADD) libcommon.la
+endif
diff --git a/schedutils/coresched.1.adoc b/schedutils/coresched.1.adoc
new file mode 100644
index 000000000..60a21cd01
--- /dev/null
+++ b/schedutils/coresched.1.adoc
@@ -0,0 +1,16 @@
+//po4a: entry man manual
+////
+coresched(1) manpage
+////
+= coresched(1)
+:doctype: manpage
+:man manual: User Commands
+:man source: util-linux {release-version}
+:page-layout: base
+:command: coresched
+:colon: :
+:copyright: ©
+
+== NAME
+
+coresched - manage core scheduling cookies for tasks
diff --git a/schedutils/coresched.c b/schedutils/coresched.c
new file mode 100644
index 000000000..17d775f2d
--- /dev/null
+++ b/schedutils/coresched.c
@@ -0,0 +1,353 @@
+/**
+ * SPDX-License-Identifier: EUPL-1.2
+ *
+ * coresched.c - manage core scheduling cookies for tasks
+ *
+ * Copyright (C) 2024 Thijs Raymakers
+ * Licensed under the EUPL v1.2
+ */
+
+#include <getopt.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <sys/prctl.h>
+#include <sys/wait.h>
+
+#include "c.h"
+#include "closestream.h"
+#include "nls.h"
+#include "strutils.h"
+
+// These definitions might not be defined, even if the
+// prctl interface accepts them.
+#ifndef PR_SCHED_CORE_SCOPE_THREAD
+#define PR_SCHED_CORE_SCOPE_THREAD 0
+#endif
+#ifndef PR_SCHED_CORE_SCOPE_THREAD_GROUP
+#define PR_SCHED_CORE_SCOPE_THREAD_GROUP 1
+#endif
+#ifndef PR_SCHED_CORE_SCOPE_PROCESS_GROUP
+#define PR_SCHED_CORE_SCOPE_PROCESS_GROUP 2
+#endif
+
+typedef int core_sched_type_t;
+typedef enum {
+	SCHED_CORE_CMD_EXEC = 0,
+	SCHED_CORE_CMD_GET = 1,
+	SCHED_CORE_CMD_CREATE = 2,
+	SCHED_CORE_CMD_COPY = 4,
+} core_sched_cmd_t;
+
+struct args {
+	pid_t from_pid;
+	pid_t to_pid;
+	core_sched_type_t type;
+	core_sched_cmd_t cmd;
+	int exec_argv_offset;
+};
+
+unsigned long core_sched_get_cookie(struct args *args);
+void core_sched_create_cookie(struct args *args);
+void core_sched_pull_cookie(pid_t from);
+void core_sched_push_cookie(pid_t to, core_sched_type_t type);
+void core_sched_copy_cookie(struct args *args);
+void core_sched_exec_with_cookie(struct args *args, char **argv);
+
+core_sched_type_t parse_core_sched_type(char *str);
+bool verify_arguments(struct args *args);
+void parse_arguments(int argc, char **argv, struct args *args);
+void set_pid_or_err(pid_t *dest, pid_t src, const char *err_msg);
+static void __attribute__((__noreturn__)) usage(void);
+
+unsigned long core_sched_get_cookie(struct args *args)
+{
+	unsigned long cookie = 0;
+	if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_GET, args->from_pid,
+		  PR_SCHED_CORE_SCOPE_THREAD, &cookie)) {
+		err(errno, "Failed to get cookie from PID %d", args->from_pid);
+	}
+	return cookie;
+}
+
+void core_sched_create_cookie(struct args *args)
+{
+	if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_CREATE, args->to_pid, args->type,
+		  0)) {
+		err(errno, "Failed to create cookie for PID %d",
+		    args->from_pid);
+	}
+}
+
+void core_sched_pull_cookie(pid_t from)
+{
+	if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_SHARE_FROM, from,
+		  PR_SCHED_CORE_SCOPE_THREAD, 0)) {
+		err(errno, "Failed to pull cookie from PID %d", from);
+	}
+}
+
+void core_sched_push_cookie(pid_t to, core_sched_type_t type)
+{
+	if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_SHARE_TO, to, type, 0)) {
+		err(errno, "Failed to push cookie to PID %d", to);
+	}
+}
+
+void core_sched_copy_cookie(struct args *args)
+{
+	core_sched_pull_cookie(args->from_pid);
+	core_sched_push_cookie(args->to_pid, args->type);
+}
+
+void core_sched_exec_with_cookie(struct args *args, char **argv)
+{
+	if (!args->exec_argv_offset) {
+		usage();
+	}
+
+	// Move the argument list to the first argument of the program
+	argv = &argv[args->exec_argv_offset];
+
+	// If a source PID is provided, try to copy the cookie from
+	// that PID. Otherwise, create a brand new cookie with the
+	// provided type.
+	if (args->from_pid) {
+		core_sched_pull_cookie(args->from_pid);
+	} else {
+		args->to_pid = getpid();
+		core_sched_create_cookie(args);
+	}
+
+	if (execvp(argv[0], argv)) {
+		errexec(argv[0]);
+	}
+}
+
+core_sched_type_t parse_core_sched_type(char *str)
+{
+	if (!strncmp(str, "pid\0", 4)) {
+		return PR_SCHED_CORE_SCOPE_THREAD;
+	} else if (!strncmp(str, "tgid\0", 5)) {
+		return PR_SCHED_CORE_SCOPE_THREAD_GROUP;
+	} else if (!strncmp(str, "pgid\0", 5)) {
+		return PR_SCHED_CORE_SCOPE_PROCESS_GROUP;
+	}
+
+	errx(EINVAL, "'%s' is an invalid option. Must be one of pid/tgid/pgid",
+	     str);
+	__builtin_unreachable();
+}
+
+static void __attribute__((__noreturn__)) usage(void)
+{
+	fputs(USAGE_HEADER, stdout);
+	fprintf(stdout, _(" %s --get <PID>\n"), program_invocation_short_name);
+	fprintf(stdout, _(" %s --new <PID> [-t <TYPE>]\n"),
+		program_invocation_short_name);
+	fprintf(stdout, _(" %s --copy -s <PID> -d <PID> [-t <TYPE>]\n"),
+		program_invocation_short_name);
+	fprintf(stdout, _(" %s [-s <PID>] -- PROGRAM ARGS... \n"),
+		program_invocation_short_name);
+
+	fputs(USAGE_SEPARATOR, stdout);
+	fputsln(_("Manage core scheduling cookies for tasks."), stdout);
+
+	fputs(USAGE_FUNCTIONS, stdout);
+	fputsln(_(" -g, --get <PID>         get the core scheduling cookie of a PID"),
+		stdout);
+	fputsln(_(" -n, --new <PID>         assign a new core scheduling cookie to PID"),
+		stdout);
+	fputsln(_(" -c, --copy              copy the core scheduling cookie from PID to\n"
+		  "                           another PID, requires the --source and --dest option"),
+		stdout);
+
+	fputs(USAGE_OPTIONS, stdout);
+	fputsln(_(" -s, --source <PID>      where to copy the core scheduling cookie from."),
+		stdout);
+	fputsln(_(" -d, --dest <PID>        where to copy the core scheduling cookie to."),
+		stdout);
+	fputsln(_(" -t, --type              type of the destination PID, or the type of\n"
+		  "                           the PID when a new core scheduling cookie\n"
+		  "                           is created. Can be one of the following:\n"
+		  "                           pid, tgid or pgid. Defaults to tgid."),
+		stdout);
+	fputs(USAGE_SEPARATOR, stdout);
+	fprintf(stdout,
+		USAGE_HELP_OPTIONS(
+			25)); /* char offset to align option descriptions */
+	fprintf(stdout, USAGE_MAN_TAIL("coresched(1)"));
+	exit(EXIT_SUCCESS);
+}
+
+bool verify_arguments(struct args *args)
+{
+	// Check if the value of args->cmd is a power of 2
+	// In that case, only a single function option was set.
+	if (args->cmd & (args->cmd - 1)) {
+		errx(EINVAL,
+		     "Cannot do more than one function at a time. See %s --help",
+		     program_invocation_short_name);
+	}
+
+	switch (args->cmd) {
+	case SCHED_CORE_CMD_GET:
+		if (args->to_pid) {
+			errx(EINVAL,
+			     "Cannot use -d/--dest with this -g/--get. See %s --help",
+			     program_invocation_short_name);
+		}
+		break;
+	case SCHED_CORE_CMD_CREATE:
+		if (args->from_pid) {
+			errx(EINVAL,
+			     "Cannot use -s/--source with this -n/--new. See %s --help",
+			     program_invocation_short_name);
+		}
+		break;
+	case SCHED_CORE_CMD_COPY:
+		if (!args->from_pid) {
+			errx(EINVAL,
+			     "-s/--source PID is required when copying");
+		}
+		if (!args->to_pid) {
+			errx(EINVAL, "-d/--dest PID is required when copying");
+		}
+		break;
+	case SCHED_CORE_CMD_EXEC:
+		if (args->to_pid) {
+			errx(EINVAL,
+			     "Cannot use -d/--dest when spawning a program. See %s --help",
+			     program_invocation_short_name);
+		}
+		break;
+	}
+	return true;
+}
+
+void set_pid_or_err(pid_t *dest, pid_t src, const char *err_msg)
+{
+	if (*dest) {
+		errx(EINVAL, "Ambigious usage: %s", err_msg);
+	} else {
+		*dest = src;
+	}
+}
+
+static const char *ERR_MSG_MULTIPLE_SOURCE_PIDS =
+	"Multiple source PIDs defined";
+void parse_arguments(int argc, char **argv, struct args *args)
+{
+	int c;
+	pid_t tmp;
+
+	static const struct option longopts[] = {
+		{ "get", required_argument, NULL, 'g' },
+		{ "new", required_argument, NULL, 'n' },
+		{ "copy", no_argument, NULL, 'c' },
+		{ "source", required_argument, NULL, 's' },
+		{ "destination", required_argument, NULL, 'd' },
+		{ "type", required_argument, NULL, 't' },
+		{ "version", no_argument, NULL, 'V' },
+		{ "help", no_argument, NULL, 'h' },
+		{ NULL, 0, NULL, 0 }
+	};
+
+	while ((c = getopt_long(argc, argv, "g:n:cs:d:t:Vh", longopts, NULL)) !=
+	       -1)
+		switch (c) {
+		case 'g':
+			args->cmd |= SCHED_CORE_CMD_GET;
+			tmp = strtopid_or_err(
+				optarg, "Failed to parse PID for -g/--get");
+			set_pid_or_err(&args->from_pid, tmp,
+				       ERR_MSG_MULTIPLE_SOURCE_PIDS);
+			break;
+		case 'n':
+			args->cmd |= SCHED_CORE_CMD_CREATE;
+			tmp = strtopid_or_err(
+				optarg, "Failed to parse PID for -n/--new");
+			set_pid_or_err(&args->to_pid, tmp,
+				       ERR_MSG_MULTIPLE_SOURCE_PIDS);
+			break;
+		case 'c':
+			args->cmd |= SCHED_CORE_CMD_COPY;
+			break;
+		case 's':
+			tmp = strtopid_or_err(
+				optarg, "Failed to parse PID for -s/--source");
+			set_pid_or_err(&args->from_pid, tmp,
+				       ERR_MSG_MULTIPLE_SOURCE_PIDS);
+			break;
+		case 'd':
+			tmp = strtopid_or_err(
+				optarg, "Failed to parse PID for -d/--dest");
+			set_pid_or_err(&args->to_pid, tmp,
+				       "Multiple destination PIDs defined");
+			break;
+		case 't':
+			args->type = parse_core_sched_type(optarg);
+			break;
+		case 'V':
+			print_version(EXIT_SUCCESS);
+		case 'h':
+			usage();
+		default:
+			errtryhelp(EXIT_FAILURE);
+		}
+
+	if (argc > optind) {
+		if (args->cmd == SCHED_CORE_CMD_EXEC) {
+			args->exec_argv_offset = optind;
+		} else {
+			// -g, -n or -c AND a program to run is provided
+			errx(EINVAL, "bad usage, see %s --help",
+			     program_invocation_short_name);
+		}
+	} else if (argc == optind && args->from_pid) {
+		// Neither a function (-g, -n, or -c), nor a program to
+		// run is given
+		args->cmd = SCHED_CORE_CMD_GET;
+	}
+
+	verify_arguments(args);
+}
+
+int main(int argc, char **argv)
+{
+	struct args arguments = { 0 };
+	arguments.type = PR_SCHED_CORE_SCOPE_THREAD_GROUP;
+
+	setlocale(LC_ALL, "");
+	bindtextdomain(PACKAGE, LOCALEDIR);
+	textdomain(PACKAGE);
+	close_stdout_atexit();
+
+	parse_arguments(argc, argv, &arguments);
+
+	unsigned long cookie = 0;
+	switch (arguments.cmd) {
+	case SCHED_CORE_CMD_GET:
+		cookie = core_sched_get_cookie(&arguments);
+		if (cookie) {
+			printf("core scheduling cookie of pid %d is 0x%lx\n",
+			       arguments.from_pid, cookie);
+		} else {
+			printf("pid %d doesn't have a core scheduling cookie\n",
+			       arguments.from_pid);
+			exit(1);
+		}
+		break;
+	case SCHED_CORE_CMD_CREATE:
+		core_sched_create_cookie(&arguments);
+		break;
+	case SCHED_CORE_CMD_COPY:
+		core_sched_copy_cookie(&arguments);
+		break;
+	case SCHED_CORE_CMD_EXEC:
+		core_sched_exec_with_cookie(&arguments, argv);
+		break;
+	default:
+		usage();
+		exit(1);
+	}
+}
-- 
2.44.0





[Index of Archives]     [Netdev]     [Ethernet Bridging]     [Linux Wireless]     [Kernel Newbies]     [Security]     [Linux for Hams]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux Admin]     [Samba]

  Powered by Linux