* Re: lsfd/error-eperm test fails on git master
From: Masatake YAMATO @ 2024-04-16 0:29 UTC (permalink / raw)
To: glaubitz; +Cc: util-linux
In-Reply-To: <31ccace2e5912ffc428e065cd66764088c625c4d.camel@physik.fu-berlin.de>
Hi,
From: John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de>
Subject: lsfd/error-eperm test fails on git master
Date: Mon, 15 Apr 2024 12:24:08 +0200
> Hello,
>
> lsfd/error-eperm fails for me on git master. I have reproduced the issue
> on 32-bit PowerPC, 64-bit SPARC, 64-bit s390x and x86_64.
>
> Is there a tentative fix for this failure?
Thank you for reporting.
This may be a bug in a build script.
> Thanks,
> Adrian
>
> (sid_powerpc-dchroot)glaubitz@perotto:~/util-linux-git/tests$ ./run.sh lsfd/error-eperm
>
> -------------------- util-linux regression tests --------------------
>
> For development purpose only.
> Don't execute on production system!
>
> kernel: 6.6.15-powerpc64
>
> options: --srcdir=/home/glaubitz/util-linux-git/tests/.. \
> --builddir=/home/glaubitz/util-linux-git/tests/..
>
> lsfd: fd opening a file cannot be stat(2)'ed ... FAILED (lsfd/error-eperm)
>
> ---------------------------------------------------------------------
> 1 tests of 1 FAILED
>
> lsfd/error-eperm
> ---------------------------------------------------------------------
> (sid_powerpc-dchroot)glaubitz@perotto:~/util-linux-git/tests$
>
> diff-{{{
> --- /home/glaubitz/util-linux-git/tests/expected/lsfd/error-eperm 2024-04-09 09:22:29.505017516 +0000
> +++ /home/glaubitz/util-linux-git/tests/output/lsfd/error-eperm 2024-04-15 08:37:38.326220593 +0000
> @@ -1,2 +1,2 @@
> -mem ERROR stat:EPERM
> +mem ERROR stat:unknown(1)
EPERM is defined as 1 in asm-generic/errno-base.h.
So `unknown(1)' should be decoded as EPERM.
lsns uses errnos.h generated at build-time for decoding the error numbers.
Can I see errnos.h at the top of the build directory ?
I guess the file is at ~/util-linux-git/errnos.h.
On my environment, the head of the file is:
UL_ERRNO("E2BIG", E2BIG)
UL_ERRNO("EACCES", EACCES)
UL_ERRNO("EADDRINUSE", EADDRINUSE)
UL_ERRNO("EADDRNOTAVAIL", EADDRNOTAVAIL)
UL_ERRNO("EADV", EADV)
UL_ERRNO("EAFNOSUPPORT", EAFNOSUPPORT)
...
Masatake YAMATO
> ASSOC,TYPE,SOURCE: 0
> }}}-diff
>
> libsmartcols: fromfile: [15] wrap-tree ... OK
> lsfd: fd opening a file cannot be stat(2)'ed ... FAILED (lsfd/error-eperm)
>
> --
> .''`. John Paul Adrian Glaubitz
> : :' : Debian Developer
> `. `' Physicist
> `- GPG: 62FF 8A75 84E0 2956 9546 0006 7426 3B37 F5B5 F913
>
^ permalink raw reply
* lsfd/error-eperm test fails on git master
From: John Paul Adrian Glaubitz @ 2024-04-15 10:24 UTC (permalink / raw)
To: util-linux
Hello,
lsfd/error-eperm fails for me on git master. I have reproduced the issue
on 32-bit PowerPC, 64-bit SPARC, 64-bit s390x and x86_64.
Is there a tentative fix for this failure?
Thanks,
Adrian
(sid_powerpc-dchroot)glaubitz@perotto:~/util-linux-git/tests$ ./run.sh lsfd/error-eperm
-------------------- util-linux regression tests --------------------
For development purpose only.
Don't execute on production system!
kernel: 6.6.15-powerpc64
options: --srcdir=/home/glaubitz/util-linux-git/tests/.. \
--builddir=/home/glaubitz/util-linux-git/tests/..
lsfd: fd opening a file cannot be stat(2)'ed ... FAILED (lsfd/error-eperm)
---------------------------------------------------------------------
1 tests of 1 FAILED
lsfd/error-eperm
---------------------------------------------------------------------
(sid_powerpc-dchroot)glaubitz@perotto:~/util-linux-git/tests$
diff-{{{
--- /home/glaubitz/util-linux-git/tests/expected/lsfd/error-eperm 2024-04-09 09:22:29.505017516 +0000
+++ /home/glaubitz/util-linux-git/tests/output/lsfd/error-eperm 2024-04-15 08:37:38.326220593 +0000
@@ -1,2 +1,2 @@
-mem ERROR stat:EPERM
+mem ERROR stat:unknown(1)
ASSOC,TYPE,SOURCE: 0
}}}-diff
libsmartcols: fromfile: [15] wrap-tree ... OK
lsfd: fd opening a file cannot be stat(2)'ed ... FAILED (lsfd/error-eperm)
--
.''`. John Paul Adrian Glaubitz
: :' : Debian Developer
`. `' Physicist
`- GPG: 62FF 8A75 84E0 2956 9546 0006 7426 3B37 F5B5 F913
^ permalink raw reply
* Re: [PATCH v8] coresched: Manage core scheduling cookies for tasks
From: Phil Auld @ 2024-04-11 13:46 UTC (permalink / raw)
To: Thijs Raymakers; +Cc: kzak, thomas, util-linux
In-Reply-To: <20240411110250.292512-1-thijs@raymakers.nl>
On Thu, Apr 11, 2024 at 01:02:49PM +0200 Thijs Raymakers wrote:
> Co-authored-by: Phil Auld <pauld@redhat.com>
> Signed-off-by: Phil Auld <pauld@redhat.com>
> Signed-off-by: Thijs Raymakers <thijs@raymakers.nl>
>
Hi Thijs.
> ---
>
> Hi Phil,
>
> That might have been a bit too drastic from my side. I just wanted to
> make sure that I didn't accidentally attribute something to you that you
> didn't fully support, since the arguments of this version differ
> significantly from what we've previously discussed. I've put you back.
Thanks! I support whatever we can get in that covers the use cases. If we ask
three engineers about commandline options we'll likely get 5 answers :)
What we have here looks good and works well.
A couple of minor man page comments...
In the man page let's change this sentence:
"Core scheduling allows you to define groups of tasks that are allowed to share a physical core."
to
"Core scheduling permits the definition of groups ..."
That takes out the "you" and reduces the number of times we say "allow" in that
paragraph.
Similarly in the PERMISSIONS section:
"When retrieving or modifying the core scheduling cookie of a process, you need to have PTRACE_MODE_READ_REALCREDS ptrace access to that process."
should be something like:
"Retrieving or modifying the core scheduling cookie of a process requires PTRACE_MODE_READ_REALCREDS ptrace access to that process."
Cheers,
Phil
>
> Thijs
>
> Interdiff against v7:
> diff --git a/schedutils/coresched.1.adoc b/schedutils/coresched.1.adoc
> index 00f2603e0..aa228b84f 100644
> --- a/schedutils/coresched.1.adoc
> +++ b/schedutils/coresched.1.adoc
> @@ -113,11 +113,12 @@ If a _command_ is being executed, the return value of *{command}* will be the re
> This can be enabled via the *CONFIG_SCHED_CORE* kernel config option.
>
> == AUTHORS
> -mailto:thijs@raymakers.nl[Thijs Raymakers]
> +mailto:thijs@raymakers.nl[Thijs Raymakers],
> +mailto:pauld@redhat.com[Phil Auld]
>
> == COPYRIGHT
>
> -Copyright {copyright} 2024 Thijs Raymakers. This is free software licensed under the EUPL.
> +Copyright {copyright} 2024 Thijs Raymakers and Phil Auld. This is free software licensed under the EUPL.
>
> == SEE ALSO
> *chrt*(1),
> diff --git a/schedutils/coresched.c b/schedutils/coresched.c
> index 7e3cc46d2..bb97cc020 100644
> --- a/schedutils/coresched.c
> +++ b/schedutils/coresched.c
> @@ -3,7 +3,7 @@
> *
> * coresched.c - manage core scheduling cookies for tasks
> *
> - * Copyright (C) 2024 Thijs Raymakers
> + * Copyright (C) 2024 Thijs Raymakers, Phil Auld
> * Licensed under the EUPL v1.2
> */
>
>
> .gitignore | 1 +
> bash-completion/coresched | 0
> configure.ac | 12 +-
> meson.build | 16 +-
> meson_options.txt | 2 +-
> schedutils/Makemodule.am | 8 +
> schedutils/coresched.1.adoc | 139 +++++++
> schedutils/coresched.c | 358 ++++++++++++++++++
> tests/commands.sh | 1 +
> .../coresched-copy-from-child-to-parent | 1 +
> ...coresched-copy-from-parent-to-nested-child | 1 +
> .../schedutils/coresched-get-cookie-own-pid | 1 +
> .../coresched-get-cookie-parent-pid | 1 +
> .../coresched-new-child-with-new-cookie | 1 +
> .../coresched-set-cookie-parent-pid.err | 1 +
> .../expected/schedutils/set-cookie-parent-pid | 1 +
> tests/ts/schedutils/coresched | 83 ++++
> 17 files changed, 621 insertions(+), 6 deletions(-)
> create mode 100644 bash-completion/coresched
> create mode 100644 schedutils/coresched.1.adoc
> create mode 100644 schedutils/coresched.c
> create mode 100644 tests/expected/schedutils/coresched-copy-from-child-to-parent
> create mode 100644 tests/expected/schedutils/coresched-copy-from-parent-to-nested-child
> create mode 100644 tests/expected/schedutils/coresched-get-cookie-own-pid
> create mode 100644 tests/expected/schedutils/coresched-get-cookie-parent-pid
> create mode 100644 tests/expected/schedutils/coresched-new-child-with-new-cookie
> create mode 100644 tests/expected/schedutils/coresched-set-cookie-parent-pid.err
> create mode 100644 tests/expected/schedutils/set-cookie-parent-pid
> create mode 100755 tests/ts/schedutils/coresched
>
> 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..a3a648753 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..aa228b84f
> --- /dev/null
> +++ b/schedutils/coresched.1.adoc
> @@ -0,0 +1,139 @@
> +//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
> +
> +== SYNOPSIS
> +
> +*{command}* [*get*] [*-s* _pid_]
> +
> +*{command}* *new* [*-t* _type_] *-d* _pid_
> +
> +*{command}* *new* [*-t* _type_] \-- _command_ [_argument_...]
> +
> +*{command}* *copy* [*-s* _pid_] [*-t* _type_] *-d* _pid_
> +
> +*{command}* *copy* [*-s* _pid_] [*-t* _type_] \-- _command_ [_argument_...]
> +
> +== DESCRIPTION
> +The *{command}* command is used to retrieve or modify the core scheduling cookies of a running process given its _pid_, or to spawn a new _command_ with core scheduling cookies.
> +
> +Core scheduling allows you to define groups of tasks that are allowed to share a physical core.
> +This is done by assigning a cookie to each task.
> +Only tasks have the same cookie are allowed to be scheduled on the same physical core.
> +
> +It is possible to either assign a new random cookie to a task, or copy a cookie from another task. It is not possible to choose the value of the cookie.
> +
> +== FUNCTIONS
> +*get*::
> +Retrieve the core scheduling cookie of the PID specified in *-s*.
> +If *-s* is omitted, it will get the cookie of the current *{command}* process.
> +
> +*new*::
> +Assign a new cookie to an existing PID specified in *-d*, or execute _command_ with a new cookie.
> +
> +*copy*::
> +Copy the cookie from an existing PID (*-s*) to another PID (*-d*), or execute _command_ with that cookie.
> +If *-s* is omitted, it will get the cookie of the current *{command}* process.
> +
> +If no function is specified, it will run the *get* function.
> +
> +== OPTIONS
> +*-s*, *--source* _PID_::
> +Which _PID_ to get the cookie from.
> +If this option is omitted, it will get the cookie from the current *{command}* process.
> +
> +*-d*, *--dest* _PID_::
> +Which _PID_ to modify the cookie of.
> +
> +*-t*, *--dest-type* _TYPE_::
> +The type of the PID whose cookie will be modified. This can be one of three values:
> +- *pid*, or process ID
> +- *tgid*, or thread group ID (default value)
> +- *pgid*, or process group ID
> +
> +*-v*, *--verbose*::
> +Show extra information when modifying cookies of tasks.
> +
> +*-h*, *--help*::
> +Display help text and exit.
> +
> +*-V*, *--version*::
> +Print version and exit.
> +
> +== EXAMPLES
> +Get the core scheduling cookie of the {command} task itself, usually inherited from its parent{colon}::
> +*{command} get*
> +
> +Get the core scheduling cookie of a task with PID _123_{colon}::
> +*{command} get -s* _123_
> +
> +Give a task with PID _123_ a new core scheduling cookie{colon}::
> +*{command} new -d* _123_
> +
> +Spawn a new task with a new core scheduling cookie{colon}::
> +*{command} new* \-- _command_ [_argument_...]
> +
> +Copy the cookie from the current {command} process another task with pid _456_{colon}::
> +*{command} copy -d* _456_
> +
> +Copy the cookie from a task with pid _123_ to another task with pid _456_{colon}::
> +*{command} copy -s* _123_ *-d* _456_
> +
> +Copy the cookie from a task with pid _123_ to a new task _command_{colon}::
> +*{command} copy -s* _123_ \-- _command_ [_argument_...]
> +
> +Copy the cookie from a task with pid _123_ to the process group ID _456_{colon}::
> +*{command} copy -s* _123_ *-t* _pgid_ *-d* _456_
> +
> +== PERMISSIONS
> +When retrieving or modifying the core scheduling cookie of a process, you need to have *PTRACE_MODE_READ_REALCREDS* ptrace access to that process.
> +See the section "Ptrace access mode checking" in *ptrace*(2) for more information.
> +
> +== RETURN VALUE
> +On success, *{command}* returns 0.
> +If *{command}* fails, it will print an error and return 1.
> +
> +If a _command_ is being executed, the return value of *{command}* will be the return value of _command_.
> +
> +== NOTES
> +*{command}* requires core scheduling support in the kernel.
> +This can be enabled via the *CONFIG_SCHED_CORE* kernel config option.
> +
> +== AUTHORS
> +mailto:thijs@raymakers.nl[Thijs Raymakers],
> +mailto:pauld@redhat.com[Phil Auld]
> +
> +== COPYRIGHT
> +
> +Copyright {copyright} 2024 Thijs Raymakers and Phil Auld. This is free software licensed under the EUPL.
> +
> +== SEE ALSO
> +*chrt*(1),
> +*nice*(1),
> +*renice*(1),
> +*taskset*(1),
> +*ptrace*(2),
> +*sched*(7)
> +
> +The Linux kernel source files _Documentation/admin-guide/hw-vuln/core-scheduling.rst_
> +
> +include::man-common/bugreports.adoc[]
> +
> +include::man-common/footer.adoc[]
> +
> +ifdef::translation[]
> +include::man-common/translation.adoc[]
> +endif::[]
> diff --git a/schedutils/coresched.c b/schedutils/coresched.c
> new file mode 100644
> index 000000000..bb97cc020
> --- /dev/null
> +++ b/schedutils/coresched.c
> @@ -0,0 +1,358 @@
> +/**
> + * SPDX-License-Identifier: EUPL-1.2
> + *
> + * coresched.c - manage core scheduling cookies for tasks
> + *
> + * Copyright (C) 2024 Thijs Raymakers, Phil Auld
> + * Licensed under the EUPL v1.2
> + */
> +
> +#include <getopt.h>
> +#include <stdbool.h>
> +#include <stdio.h>
> +#include <sys/prctl.h>
> +#include <unistd.h>
> +
> +#include "c.h"
> +#include "closestream.h"
> +#include "nls.h"
> +#include "strutils.h"
> +
> +// These definitions might not be defined in the header files, even if the
> +// prctl interface in the kernel accepts them as valid.
> +#ifndef PR_SCHED_CORE
> + #define PR_SCHED_CORE 62
> +#endif
> +#ifndef PR_SCHED_CORE_GET
> + #define PR_SCHED_CORE_GET 0
> +#endif
> +#ifndef PR_SCHED_CORE_CREATE
> + #define PR_SCHED_CORE_CREATE 1
> +#endif
> +#ifndef PR_SCHED_CORE_SHARE_TO
> + #define PR_SCHED_CORE_SHARE_TO 2
> +#endif
> +#ifndef PR_SCHED_CORE_SHARE_FROM
> + #define PR_SCHED_CORE_SHARE_FROM 3
> +#endif
> +#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 sched_core_scope;
> +typedef unsigned long sched_core_cookie;
> +typedef enum {
> + SCHED_CORE_CMD_GET,
> + SCHED_CORE_CMD_NEW,
> + SCHED_CORE_CMD_COPY,
> +} sched_core_cmd;
> +
> +struct args {
> + pid_t src;
> + pid_t dest;
> + sched_core_scope type;
> + sched_core_cmd cmd;
> + int exec_argv_offset;
> +};
> +
> +static bool sched_core_verbose = false;
> +
> +static void __attribute__((__noreturn__)) usage(void)
> +{
> + fputs(USAGE_HEADER, stdout);
> + fprintf(stdout, _(" %s [get] [--source <PID>]\n"),
> + program_invocation_short_name);
> + fprintf(stdout, _(" %s new [-t <TYPE>] --dest <PID>\n"),
> + program_invocation_short_name);
> + fprintf(stdout, _(" %s new [-t <TYPE>] -- PROGRAM [ARGS...]\n"),
> + program_invocation_short_name);
> + fprintf(stdout,
> + _(" %s copy [--source <PID>] [-t <TYPE>] --dest <PID>\n"),
> + program_invocation_short_name);
> + fprintf(stdout,
> + _(" %s copy [--source <PID>] [-t <TYPE>] -- PROGRAM [ARGS...]\n"),
> + program_invocation_short_name);
> +
> + fputs(USAGE_SEPARATOR, stdout);
> + fputsln(_("Manage core scheduling cookies for tasks."), stdout);
> +
> + fputs(USAGE_FUNCTIONS, stdout);
> + fputsln(_(" get retrieve the core scheduling cookie of a PID"),
> + stdout);
> + fputsln(_(" new assign a new core scheduling cookie to an existing\n"
> + " PID or execute a program with a new cookie"),
> + stdout);
> + fputsln(_(" copy copy the core scheduling cookie from an existing PID\n"
> + " to another PID, or execute a program with that\n"
> + " copied cookie"),
> + stdout);
> +
> + fputs(USAGE_OPTIONS, stdout);
> + fprintf(stdout,
> + _(" -s, --source <PID> which PID to get the cookie from\n"
> + " If omitted, it is the PID of %s itself\n"),
> + program_invocation_short_name);
> + fputsln(_(" -d, --dest <PID> which PID to modify the cookie of\n"),
> + stdout);
> + fputsln(_(" -t, --dest-type <TYPE> type of the destination PID, or the type of the PID\n"
> + " when a new core scheduling cookie is created.\n"
> + " Can be one of the following: pid, tgid or pgid.\n"
> + " The default is tgid."),
> + stdout);
> + fputs(USAGE_SEPARATOR, stdout);
> + fputsln(_(" -v, --verbose verbose"), stdout);
> + fprintf(stdout, USAGE_HELP_OPTIONS(20));
> + fprintf(stdout, USAGE_MAN_TAIL("coresched(1)"));
> + exit(EXIT_SUCCESS);
> +}
> +
> +#define bad_usage(FMT...) \
> + do { \
> + warnx(FMT); \
> + errtryhelp(EXIT_FAILURE); \
> + } while (0)
> +
> +static sched_core_cookie core_sched_get_cookie(pid_t pid)
> +{
> + sched_core_cookie cookie = 0;
> + if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_GET, pid,
> + PR_SCHED_CORE_SCOPE_THREAD, &cookie))
> + err(EXIT_FAILURE, _("Failed to get cookie from PID %d"), pid);
> + return cookie;
> +}
> +
> +static void core_sched_create_cookie(pid_t pid, sched_core_scope type)
> +{
> + if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_CREATE, pid, type, 0))
> + err(EXIT_FAILURE, _("Failed to create cookie for PID %d"), pid);
> +}
> +
> +static 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(EXIT_FAILURE, _("Failed to pull cookie from PID %d"), from);
> +}
> +
> +static void core_sched_push_cookie(pid_t to, sched_core_scope type)
> +{
> + if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_SHARE_TO, to, type, 0))
> + err(EXIT_FAILURE, _("Failed to push cookie to PID %d"), to);
> +}
> +
> +static void core_sched_copy_cookie(pid_t from, pid_t to,
> + sched_core_scope to_type)
> +{
> + core_sched_pull_cookie(from);
> + core_sched_push_cookie(to, to_type);
> +
> + if (sched_core_verbose) {
> + sched_core_cookie before = core_sched_get_cookie(from);
> + warnx(_("copied cookie 0x%lx from PID %d to PID %d"), before,
> + from, to);
> + }
> +}
> +
> +static void core_sched_get_and_print_cookie(pid_t pid)
> +{
> + if (sched_core_verbose) {
> + sched_core_cookie after = core_sched_get_cookie(pid);
> + warnx(_("set cookie of PID %d to 0x%lx"), pid, after);
> + }
> +}
> +
> +static void core_sched_exec_with_cookie(struct args *args, char **argv)
> +{
> + // 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->src) {
> + core_sched_pull_cookie(args->src);
> + core_sched_get_and_print_cookie(args->src);
> + } else {
> + pid_t pid = getpid();
> + core_sched_create_cookie(pid, args->type);
> + core_sched_get_and_print_cookie(pid);
> + }
> +
> + if (execvp(argv[0], argv))
> + errexec(argv[0]);
> +}
> +
> +// If PR_SCHED_CORE is not recognized, or not supported on this system,
> +// then prctl will set errno to EINVAL. Assuming all other operands of
> +// prctl are valid, we can use errno==EINVAL as a check to see whether
> +// core scheduling is available on this system.
> +static bool is_core_sched_supported(void)
> +{
> + sched_core_cookie cookie = 0;
> + if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_GET, getpid(),
> + PR_SCHED_CORE_SCOPE_THREAD, &cookie))
> + if (errno == EINVAL)
> + return false;
> +
> + return true;
> +}
> +
> +static sched_core_scope parse_core_sched_type(char *str)
> +{
> + if (!strcmp(str, "pid"))
> + return PR_SCHED_CORE_SCOPE_THREAD;
> + else if (!strcmp(str, "tgid"))
> + return PR_SCHED_CORE_SCOPE_THREAD_GROUP;
> + else if (!strcmp(str, "pgid"))
> + return PR_SCHED_CORE_SCOPE_PROCESS_GROUP;
> +
> + bad_usage(_("'%s' is an invalid option. Must be one of pid/tgid/pgid"),
> + str);
> +}
> +
> +static void parse_and_verify_arguments(int argc, char **argv, struct args *args)
> +{
> + int c;
> +
> + static const struct option longopts[] = {
> + { "source", required_argument, NULL, 's' },
> + { "dest", required_argument, NULL, 'd' },
> + { "dest-type", required_argument, NULL, 't' },
> + { "verbose", no_argument, NULL, 'v' },
> + { "version", no_argument, NULL, 'V' },
> + { "help", no_argument, NULL, 'h' },
> + { NULL, 0, NULL, 0 }
> + };
> +
> + while ((c = getopt_long(argc, argv, "s:d:t:vVh", longopts, NULL)) != -1)
> + switch (c) {
> + case 's':
> + args->src = strtopid_or_err(
> + optarg,
> + _("Failed to parse PID for -s/--source"));
> + break;
> + case 'd':
> + args->dest = strtopid_or_err(
> + optarg, _("Failed to parse PID for -d/--dest"));
> + break;
> + case 't':
> + args->type = parse_core_sched_type(optarg);
> + break;
> + case 'v':
> + sched_core_verbose = true;
> + break;
> + case 'V':
> + print_version(EXIT_SUCCESS);
> + case 'h':
> + usage();
> + default:
> + errtryhelp(EXIT_FAILURE);
> + }
> +
> + if (argc <= optind) {
> + args->cmd = SCHED_CORE_CMD_GET;
> + } else {
> + if (!strcmp(argv[optind], "get"))
> + args->cmd = SCHED_CORE_CMD_GET;
> + else if (!strcmp(argv[optind], "new"))
> + args->cmd = SCHED_CORE_CMD_NEW;
> + else if (!strcmp(argv[optind], "copy"))
> + args->cmd = SCHED_CORE_CMD_COPY;
> + else
> + bad_usage(_("Unknown function"));
> +
> + // Since we parsed an extra "option" outside of getopt_long, we have to
> + // increment optind manually.
> + ++optind;
> + }
> +
> + if (args->cmd == SCHED_CORE_CMD_GET && args->dest)
> + bad_usage(_("get does not accept the --dest option"));
> +
> + if (args->cmd == SCHED_CORE_CMD_NEW && args->src)
> + bad_usage(_("new does not accept the --source option"));
> +
> + // If the -s/--source option is not specified, it defaults to the PID
> + // of the current coresched process
> + if (args->cmd != SCHED_CORE_CMD_NEW && !args->src)
> + args->src = getpid();
> +
> + // More arguments have been passed, which means that the user wants to run
> + // another program with a core scheduling cookie.
> + if (argc > optind) {
> + switch (args->cmd) {
> + case SCHED_CORE_CMD_GET:
> + bad_usage(_("bad usage of the get function"));
> + break;
> + case SCHED_CORE_CMD_NEW:
> + if (args->dest)
> + bad_usage(_(
> + "new requires either a -d/--dest or a command"));
> + else
> + args->exec_argv_offset = optind;
> + break;
> + case SCHED_CORE_CMD_COPY:
> + if (args->dest)
> + bad_usage(_(
> + "copy requires either a -d/--dest or a command"));
> + else
> + args->exec_argv_offset = optind;
> + break;
> + }
> + } else {
> + if (args->cmd == SCHED_CORE_CMD_NEW && !args->dest)
> + bad_usage(_(
> + "new requires either a -d/--dest or a command"));
> + if (args->cmd == SCHED_CORE_CMD_COPY && !args->dest)
> + bad_usage(_(
> + "copy requires either a -d/--dest or a command"));
> + }
> +}
> +
> +int main(int argc, char **argv)
> +{
> + struct args args = { .type = PR_SCHED_CORE_SCOPE_THREAD_GROUP };
> +
> + setlocale(LC_ALL, "");
> + bindtextdomain(PACKAGE, LOCALEDIR);
> + textdomain(PACKAGE);
> + close_stdout_atexit();
> +
> + parse_and_verify_arguments(argc, argv, &args);
> +
> + if (!is_core_sched_supported())
> + errx(EXIT_FAILURE,
> + _("No support for core scheduling found. Does your kernel"
> + "support CONFIG_SCHED_CORE?"));
> +
> + sched_core_cookie cookie;
> +
> + switch (args.cmd) {
> + case SCHED_CORE_CMD_GET:
> + cookie = core_sched_get_cookie(args.src);
> + printf(_("cookie of pid %d is 0x%lx\n"), args.src, cookie);
> + break;
> + case SCHED_CORE_CMD_NEW:
> + if (args.exec_argv_offset) {
> + core_sched_exec_with_cookie(&args, argv);
> + } else {
> + core_sched_create_cookie(args.dest, args.type);
> + core_sched_get_and_print_cookie(args.dest);
> + }
> + break;
> + case SCHED_CORE_CMD_COPY:
> + if (args.exec_argv_offset)
> + core_sched_exec_with_cookie(&args, argv);
> + else
> + core_sched_copy_cookie(args.src, args.dest, args.type);
> + break;
> + default:
> + usage();
> + }
> +}
> diff --git a/tests/commands.sh b/tests/commands.sh
> index 5674c5ff0..9eef92ccb 100644
> --- a/tests/commands.sh
> +++ b/tests/commands.sh
> @@ -71,6 +71,7 @@ TS_CMD_COLCRT=${TS_CMD_COLCRT:-"${ts_commandsdir}colcrt"}
> TS_CMD_COLRM=${TS_CMD_COLRM:-"${ts_commandsdir}colrm"}
> TS_CMD_COL=${TS_CMD_COL:-"${ts_commandsdir}col"}
> TS_CMD_COLUMN=${TS_CMD_COLUMN:-"${ts_commandsdir}column"}
> +TS_CMD_CORESCHED=${TS_CMD_CORESCHED:-"${ts_commandsdir}coresched"}
> TS_CMD_ENOSYS=${TS_CMD_ENOSYS-"${ts_commandsdir}enosys"}
> TS_CMD_EJECT=${TS_CMD_EJECT-"${ts_commandsdir}eject"}
> TS_CMD_EXCH=${TS_CMD_EXCH-"${ts_commandsdir}exch"}
> diff --git a/tests/expected/schedutils/coresched-copy-from-child-to-parent b/tests/expected/schedutils/coresched-copy-from-child-to-parent
> new file mode 100644
> index 000000000..5b9c40052
> --- /dev/null
> +++ b/tests/expected/schedutils/coresched-copy-from-child-to-parent
> @@ -0,0 +1 @@
> +DIFFERENT_COOKIE
> diff --git a/tests/expected/schedutils/coresched-copy-from-parent-to-nested-child b/tests/expected/schedutils/coresched-copy-from-parent-to-nested-child
> new file mode 100644
> index 000000000..ecfc41142
> --- /dev/null
> +++ b/tests/expected/schedutils/coresched-copy-from-parent-to-nested-child
> @@ -0,0 +1 @@
> +SAME_COOKIE
> diff --git a/tests/expected/schedutils/coresched-get-cookie-own-pid b/tests/expected/schedutils/coresched-get-cookie-own-pid
> new file mode 100644
> index 000000000..84f182cbe
> --- /dev/null
> +++ b/tests/expected/schedutils/coresched-get-cookie-own-pid
> @@ -0,0 +1 @@
> +cookie of pid OWN_PID is PARENT_COOKIE
> diff --git a/tests/expected/schedutils/coresched-get-cookie-parent-pid b/tests/expected/schedutils/coresched-get-cookie-parent-pid
> new file mode 100644
> index 000000000..e183e0402
> --- /dev/null
> +++ b/tests/expected/schedutils/coresched-get-cookie-parent-pid
> @@ -0,0 +1 @@
> +cookie of pid PARENT_PID is PARENT_COOKIE
> diff --git a/tests/expected/schedutils/coresched-new-child-with-new-cookie b/tests/expected/schedutils/coresched-new-child-with-new-cookie
> new file mode 100644
> index 000000000..5b9c40052
> --- /dev/null
> +++ b/tests/expected/schedutils/coresched-new-child-with-new-cookie
> @@ -0,0 +1 @@
> +DIFFERENT_COOKIE
> diff --git a/tests/expected/schedutils/coresched-set-cookie-parent-pid.err b/tests/expected/schedutils/coresched-set-cookie-parent-pid.err
> new file mode 100644
> index 000000000..e7318ffc2
> --- /dev/null
> +++ b/tests/expected/schedutils/coresched-set-cookie-parent-pid.err
> @@ -0,0 +1 @@
> +coresched: set cookie of PID PARENT_PID to PARENT_COOKIE
> diff --git a/tests/expected/schedutils/set-cookie-parent-pid b/tests/expected/schedutils/set-cookie-parent-pid
> new file mode 100644
> index 000000000..e7318ffc2
> --- /dev/null
> +++ b/tests/expected/schedutils/set-cookie-parent-pid
> @@ -0,0 +1 @@
> +coresched: set cookie of PID PARENT_PID to PARENT_COOKIE
> diff --git a/tests/ts/schedutils/coresched b/tests/ts/schedutils/coresched
> new file mode 100755
> index 000000000..e34fa319f
> --- /dev/null
> +++ b/tests/ts/schedutils/coresched
> @@ -0,0 +1,83 @@
> +#!/bin/bash
> +# SPDX-License-Identifier: EUPL-1.2
> +#
> +# This file is part of util-linux
> +#
> +# Copyright (C) 2024 Thijs Raymakers
> +# Licensed under the EUPL v1.2
> +
> +TS_TOPDIR="${0%/*}/../.."
> +TS_DESC="coresched"
> +
> +. "$TS_TOPDIR"/functions.sh
> +ts_init "$*"
> +
> +ts_check_test_command "$TS_CMD_CORESCHED"
> +ts_check_prog "tee"
> +ts_check_prog "sed"
> +
> +# If there is no kernel support, skip the test suite
> +CORESCHED_TEST_KERNEL_SUPPORT_CMD=$($TS_CMD_CORESCHED 2>&1)
> +if [[ $CORESCHED_TEST_KERNEL_SUPPORT_CMD == *"CONFIG_SCHED_CORE"* ]]; then
> + ts_skip "Kernel has no CONFIG_SCHED_CORE support"
> +fi
> +
> +# The output of coresched contains PIDs and core scheduling cookies, both of which should be
> +# assumed to be random values as we have no control over them. The tests replace these values
> +# with sed before writing them to the output file, so it can match the expected output file.
> +# - The PID of this bash script is replaced with the placeholder `OWN_PID`
> +# - The core scheduling cookie of this bash script is replaced by `COOKIE`
> +# - Any other cookie is replaced by `DIFFERENT_COOKIE`
> +# The behavior of coresched does not depend on the exact values of these cookies, so using
> +# placeholder values does not change the behavior tests.
> +ts_init_subtest "set-cookie-parent-pid"
> +CORESCHED_OUTPUT=$( ($TS_CMD_CORESCHED -v new -d $$ \
> + | tee -a "$TS_OUTPUT") 3>&1 1>&2 2>&3 \
> + | sed "s/$$/PARENT_PID/g")
> +CORESCHED_PARENT_COOKIE=$(echo "$CORESCHED_OUTPUT" | sed 's/^.*\(0x.*$\)/\1/g')
> +if [ -z "$CORESCHED_PARENT_COOKIE" ]; then
> + ts_failed "empty value for CORESCHED_PARENT_COOKIE"
> +fi
> +CORESCHED_OUTPUT=$(echo "$CORESCHED_OUTPUT" \
> + | sed "s/$CORESCHED_PARENT_COOKIE/PARENT_COOKIE/g")
> +echo "$CORESCHED_OUTPUT" >> "$TS_ERRLOG"
> +ts_finalize_subtest
> +
> +ts_init_subtest "get-cookie-parent-pid"
> +$TS_CMD_CORESCHED get -s $$ 2>> "$TS_ERRLOG" \
> + | sed -e "s/$$/PARENT_PID/g" \
> + -e "s/$CORESCHED_PARENT_COOKIE/PARENT_COOKIE/g" >> "$TS_OUTPUT"
> +ts_finalize_subtest
> +
> +ts_init_subtest "get-cookie-own-pid"
> +$TS_CMD_CORESCHED get 2>> "$TS_ERRLOG" \
> + | sed -e "s/pid [0-9]\+/pid OWN_PID/g" \
> + -e "s/$CORESCHED_PARENT_COOKIE/PARENT_COOKIE/g" >> "$TS_OUTPUT"
> +ts_finalize_subtest
> +
> +ts_init_subtest "new-child-with-new-cookie"
> +$TS_CMD_CORESCHED new -- "$TS_CMD_CORESCHED" get 2>> "$TS_ERRLOG" \
> + | sed -e 's/^.*\(0x.*$\)/\1/g' \
> + -e "s/$CORESCHED_PARENT_COOKIE/SAME_COOKIE/g" \
> + -e "s/0x.*$/DIFFERENT_COOKIE/g" >> "$TS_OUTPUT"
> +ts_finalize_subtest
> +
> +ts_init_subtest "copy-from-parent-to-nested-child"
> +$TS_CMD_CORESCHED new -- /bin/bash -c \
> + "$TS_CMD_CORESCHED copy -s $$ -- $TS_CMD_CORESCHED get" \
> +2>> "$TS_ERRLOG" \
> + | sed -e 's/^.*\(0x.*$\)/\1/g' \
> + -e "s/$CORESCHED_PARENT_COOKIE/SAME_COOKIE/g" \
> + -e "s/0x.*$/DIFFERENT_COOKIE/g" >> "$TS_OUTPUT"
> +ts_finalize_subtest
> +
> +ts_init_subtest "copy-from-child-to-parent"
> +$TS_CMD_CORESCHED new -- /bin/bash -c \
> + "$TS_CMD_CORESCHED copy -s \$\$ -d $$"
> +$TS_CMD_CORESCHED get 2>> "$TS_ERRLOG" \
> + | sed -e 's/^.*\(0x.*$\)/\1/g' \
> + -e "s/$CORESCHED_PARENT_COOKIE/SAME_COOKIE/g" \
> + -e "s/0x.*$/DIFFERENT_COOKIE/g" >> "$TS_OUTPUT"
> +ts_finalize_subtest
> +
> +ts_finalize
> --
> 2.44.0
>
--
^ permalink raw reply
* [PATCH v8] coresched: Manage core scheduling cookies for tasks
From: Thijs Raymakers @ 2024-04-11 11:02 UTC (permalink / raw)
To: pauld; +Cc: kzak, thomas, util-linux
In-Reply-To: <20240410221539.GA408244@lorien.usersys.redhat.com>
Co-authored-by: Phil Auld <pauld@redhat.com>
Signed-off-by: Phil Auld <pauld@redhat.com>
Signed-off-by: Thijs Raymakers <thijs@raymakers.nl>
---
Hi Phil,
That might have been a bit too drastic from my side. I just wanted to
make sure that I didn't accidentally attribute something to you that you
didn't fully support, since the arguments of this version differ
significantly from what we've previously discussed. I've put you back.
Thijs
Interdiff against v7:
diff --git a/schedutils/coresched.1.adoc b/schedutils/coresched.1.adoc
index 00f2603e0..aa228b84f 100644
--- a/schedutils/coresched.1.adoc
+++ b/schedutils/coresched.1.adoc
@@ -113,11 +113,12 @@ If a _command_ is being executed, the return value of *{command}* will be the re
This can be enabled via the *CONFIG_SCHED_CORE* kernel config option.
== AUTHORS
-mailto:thijs@raymakers.nl[Thijs Raymakers]
+mailto:thijs@raymakers.nl[Thijs Raymakers],
+mailto:pauld@redhat.com[Phil Auld]
== COPYRIGHT
-Copyright {copyright} 2024 Thijs Raymakers. This is free software licensed under the EUPL.
+Copyright {copyright} 2024 Thijs Raymakers and Phil Auld. This is free software licensed under the EUPL.
== SEE ALSO
*chrt*(1),
diff --git a/schedutils/coresched.c b/schedutils/coresched.c
index 7e3cc46d2..bb97cc020 100644
--- a/schedutils/coresched.c
+++ b/schedutils/coresched.c
@@ -3,7 +3,7 @@
*
* coresched.c - manage core scheduling cookies for tasks
*
- * Copyright (C) 2024 Thijs Raymakers
+ * Copyright (C) 2024 Thijs Raymakers, Phil Auld
* Licensed under the EUPL v1.2
*/
.gitignore | 1 +
bash-completion/coresched | 0
configure.ac | 12 +-
meson.build | 16 +-
meson_options.txt | 2 +-
schedutils/Makemodule.am | 8 +
schedutils/coresched.1.adoc | 139 +++++++
schedutils/coresched.c | 358 ++++++++++++++++++
tests/commands.sh | 1 +
.../coresched-copy-from-child-to-parent | 1 +
...coresched-copy-from-parent-to-nested-child | 1 +
.../schedutils/coresched-get-cookie-own-pid | 1 +
.../coresched-get-cookie-parent-pid | 1 +
.../coresched-new-child-with-new-cookie | 1 +
.../coresched-set-cookie-parent-pid.err | 1 +
.../expected/schedutils/set-cookie-parent-pid | 1 +
tests/ts/schedutils/coresched | 83 ++++
17 files changed, 621 insertions(+), 6 deletions(-)
create mode 100644 bash-completion/coresched
create mode 100644 schedutils/coresched.1.adoc
create mode 100644 schedutils/coresched.c
create mode 100644 tests/expected/schedutils/coresched-copy-from-child-to-parent
create mode 100644 tests/expected/schedutils/coresched-copy-from-parent-to-nested-child
create mode 100644 tests/expected/schedutils/coresched-get-cookie-own-pid
create mode 100644 tests/expected/schedutils/coresched-get-cookie-parent-pid
create mode 100644 tests/expected/schedutils/coresched-new-child-with-new-cookie
create mode 100644 tests/expected/schedutils/coresched-set-cookie-parent-pid.err
create mode 100644 tests/expected/schedutils/set-cookie-parent-pid
create mode 100755 tests/ts/schedutils/coresched
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..a3a648753 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..aa228b84f
--- /dev/null
+++ b/schedutils/coresched.1.adoc
@@ -0,0 +1,139 @@
+//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
+
+== SYNOPSIS
+
+*{command}* [*get*] [*-s* _pid_]
+
+*{command}* *new* [*-t* _type_] *-d* _pid_
+
+*{command}* *new* [*-t* _type_] \-- _command_ [_argument_...]
+
+*{command}* *copy* [*-s* _pid_] [*-t* _type_] *-d* _pid_
+
+*{command}* *copy* [*-s* _pid_] [*-t* _type_] \-- _command_ [_argument_...]
+
+== DESCRIPTION
+The *{command}* command is used to retrieve or modify the core scheduling cookies of a running process given its _pid_, or to spawn a new _command_ with core scheduling cookies.
+
+Core scheduling allows you to define groups of tasks that are allowed to share a physical core.
+This is done by assigning a cookie to each task.
+Only tasks have the same cookie are allowed to be scheduled on the same physical core.
+
+It is possible to either assign a new random cookie to a task, or copy a cookie from another task. It is not possible to choose the value of the cookie.
+
+== FUNCTIONS
+*get*::
+Retrieve the core scheduling cookie of the PID specified in *-s*.
+If *-s* is omitted, it will get the cookie of the current *{command}* process.
+
+*new*::
+Assign a new cookie to an existing PID specified in *-d*, or execute _command_ with a new cookie.
+
+*copy*::
+Copy the cookie from an existing PID (*-s*) to another PID (*-d*), or execute _command_ with that cookie.
+If *-s* is omitted, it will get the cookie of the current *{command}* process.
+
+If no function is specified, it will run the *get* function.
+
+== OPTIONS
+*-s*, *--source* _PID_::
+Which _PID_ to get the cookie from.
+If this option is omitted, it will get the cookie from the current *{command}* process.
+
+*-d*, *--dest* _PID_::
+Which _PID_ to modify the cookie of.
+
+*-t*, *--dest-type* _TYPE_::
+The type of the PID whose cookie will be modified. This can be one of three values:
+- *pid*, or process ID
+- *tgid*, or thread group ID (default value)
+- *pgid*, or process group ID
+
+*-v*, *--verbose*::
+Show extra information when modifying cookies of tasks.
+
+*-h*, *--help*::
+Display help text and exit.
+
+*-V*, *--version*::
+Print version and exit.
+
+== EXAMPLES
+Get the core scheduling cookie of the {command} task itself, usually inherited from its parent{colon}::
+*{command} get*
+
+Get the core scheduling cookie of a task with PID _123_{colon}::
+*{command} get -s* _123_
+
+Give a task with PID _123_ a new core scheduling cookie{colon}::
+*{command} new -d* _123_
+
+Spawn a new task with a new core scheduling cookie{colon}::
+*{command} new* \-- _command_ [_argument_...]
+
+Copy the cookie from the current {command} process another task with pid _456_{colon}::
+*{command} copy -d* _456_
+
+Copy the cookie from a task with pid _123_ to another task with pid _456_{colon}::
+*{command} copy -s* _123_ *-d* _456_
+
+Copy the cookie from a task with pid _123_ to a new task _command_{colon}::
+*{command} copy -s* _123_ \-- _command_ [_argument_...]
+
+Copy the cookie from a task with pid _123_ to the process group ID _456_{colon}::
+*{command} copy -s* _123_ *-t* _pgid_ *-d* _456_
+
+== PERMISSIONS
+When retrieving or modifying the core scheduling cookie of a process, you need to have *PTRACE_MODE_READ_REALCREDS* ptrace access to that process.
+See the section "Ptrace access mode checking" in *ptrace*(2) for more information.
+
+== RETURN VALUE
+On success, *{command}* returns 0.
+If *{command}* fails, it will print an error and return 1.
+
+If a _command_ is being executed, the return value of *{command}* will be the return value of _command_.
+
+== NOTES
+*{command}* requires core scheduling support in the kernel.
+This can be enabled via the *CONFIG_SCHED_CORE* kernel config option.
+
+== AUTHORS
+mailto:thijs@raymakers.nl[Thijs Raymakers],
+mailto:pauld@redhat.com[Phil Auld]
+
+== COPYRIGHT
+
+Copyright {copyright} 2024 Thijs Raymakers and Phil Auld. This is free software licensed under the EUPL.
+
+== SEE ALSO
+*chrt*(1),
+*nice*(1),
+*renice*(1),
+*taskset*(1),
+*ptrace*(2),
+*sched*(7)
+
+The Linux kernel source files _Documentation/admin-guide/hw-vuln/core-scheduling.rst_
+
+include::man-common/bugreports.adoc[]
+
+include::man-common/footer.adoc[]
+
+ifdef::translation[]
+include::man-common/translation.adoc[]
+endif::[]
diff --git a/schedutils/coresched.c b/schedutils/coresched.c
new file mode 100644
index 000000000..bb97cc020
--- /dev/null
+++ b/schedutils/coresched.c
@@ -0,0 +1,358 @@
+/**
+ * SPDX-License-Identifier: EUPL-1.2
+ *
+ * coresched.c - manage core scheduling cookies for tasks
+ *
+ * Copyright (C) 2024 Thijs Raymakers, Phil Auld
+ * Licensed under the EUPL v1.2
+ */
+
+#include <getopt.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <sys/prctl.h>
+#include <unistd.h>
+
+#include "c.h"
+#include "closestream.h"
+#include "nls.h"
+#include "strutils.h"
+
+// These definitions might not be defined in the header files, even if the
+// prctl interface in the kernel accepts them as valid.
+#ifndef PR_SCHED_CORE
+ #define PR_SCHED_CORE 62
+#endif
+#ifndef PR_SCHED_CORE_GET
+ #define PR_SCHED_CORE_GET 0
+#endif
+#ifndef PR_SCHED_CORE_CREATE
+ #define PR_SCHED_CORE_CREATE 1
+#endif
+#ifndef PR_SCHED_CORE_SHARE_TO
+ #define PR_SCHED_CORE_SHARE_TO 2
+#endif
+#ifndef PR_SCHED_CORE_SHARE_FROM
+ #define PR_SCHED_CORE_SHARE_FROM 3
+#endif
+#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 sched_core_scope;
+typedef unsigned long sched_core_cookie;
+typedef enum {
+ SCHED_CORE_CMD_GET,
+ SCHED_CORE_CMD_NEW,
+ SCHED_CORE_CMD_COPY,
+} sched_core_cmd;
+
+struct args {
+ pid_t src;
+ pid_t dest;
+ sched_core_scope type;
+ sched_core_cmd cmd;
+ int exec_argv_offset;
+};
+
+static bool sched_core_verbose = false;
+
+static void __attribute__((__noreturn__)) usage(void)
+{
+ fputs(USAGE_HEADER, stdout);
+ fprintf(stdout, _(" %s [get] [--source <PID>]\n"),
+ program_invocation_short_name);
+ fprintf(stdout, _(" %s new [-t <TYPE>] --dest <PID>\n"),
+ program_invocation_short_name);
+ fprintf(stdout, _(" %s new [-t <TYPE>] -- PROGRAM [ARGS...]\n"),
+ program_invocation_short_name);
+ fprintf(stdout,
+ _(" %s copy [--source <PID>] [-t <TYPE>] --dest <PID>\n"),
+ program_invocation_short_name);
+ fprintf(stdout,
+ _(" %s copy [--source <PID>] [-t <TYPE>] -- PROGRAM [ARGS...]\n"),
+ program_invocation_short_name);
+
+ fputs(USAGE_SEPARATOR, stdout);
+ fputsln(_("Manage core scheduling cookies for tasks."), stdout);
+
+ fputs(USAGE_FUNCTIONS, stdout);
+ fputsln(_(" get retrieve the core scheduling cookie of a PID"),
+ stdout);
+ fputsln(_(" new assign a new core scheduling cookie to an existing\n"
+ " PID or execute a program with a new cookie"),
+ stdout);
+ fputsln(_(" copy copy the core scheduling cookie from an existing PID\n"
+ " to another PID, or execute a program with that\n"
+ " copied cookie"),
+ stdout);
+
+ fputs(USAGE_OPTIONS, stdout);
+ fprintf(stdout,
+ _(" -s, --source <PID> which PID to get the cookie from\n"
+ " If omitted, it is the PID of %s itself\n"),
+ program_invocation_short_name);
+ fputsln(_(" -d, --dest <PID> which PID to modify the cookie of\n"),
+ stdout);
+ fputsln(_(" -t, --dest-type <TYPE> type of the destination PID, or the type of the PID\n"
+ " when a new core scheduling cookie is created.\n"
+ " Can be one of the following: pid, tgid or pgid.\n"
+ " The default is tgid."),
+ stdout);
+ fputs(USAGE_SEPARATOR, stdout);
+ fputsln(_(" -v, --verbose verbose"), stdout);
+ fprintf(stdout, USAGE_HELP_OPTIONS(20));
+ fprintf(stdout, USAGE_MAN_TAIL("coresched(1)"));
+ exit(EXIT_SUCCESS);
+}
+
+#define bad_usage(FMT...) \
+ do { \
+ warnx(FMT); \
+ errtryhelp(EXIT_FAILURE); \
+ } while (0)
+
+static sched_core_cookie core_sched_get_cookie(pid_t pid)
+{
+ sched_core_cookie cookie = 0;
+ if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_GET, pid,
+ PR_SCHED_CORE_SCOPE_THREAD, &cookie))
+ err(EXIT_FAILURE, _("Failed to get cookie from PID %d"), pid);
+ return cookie;
+}
+
+static void core_sched_create_cookie(pid_t pid, sched_core_scope type)
+{
+ if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_CREATE, pid, type, 0))
+ err(EXIT_FAILURE, _("Failed to create cookie for PID %d"), pid);
+}
+
+static 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(EXIT_FAILURE, _("Failed to pull cookie from PID %d"), from);
+}
+
+static void core_sched_push_cookie(pid_t to, sched_core_scope type)
+{
+ if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_SHARE_TO, to, type, 0))
+ err(EXIT_FAILURE, _("Failed to push cookie to PID %d"), to);
+}
+
+static void core_sched_copy_cookie(pid_t from, pid_t to,
+ sched_core_scope to_type)
+{
+ core_sched_pull_cookie(from);
+ core_sched_push_cookie(to, to_type);
+
+ if (sched_core_verbose) {
+ sched_core_cookie before = core_sched_get_cookie(from);
+ warnx(_("copied cookie 0x%lx from PID %d to PID %d"), before,
+ from, to);
+ }
+}
+
+static void core_sched_get_and_print_cookie(pid_t pid)
+{
+ if (sched_core_verbose) {
+ sched_core_cookie after = core_sched_get_cookie(pid);
+ warnx(_("set cookie of PID %d to 0x%lx"), pid, after);
+ }
+}
+
+static void core_sched_exec_with_cookie(struct args *args, char **argv)
+{
+ // 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->src) {
+ core_sched_pull_cookie(args->src);
+ core_sched_get_and_print_cookie(args->src);
+ } else {
+ pid_t pid = getpid();
+ core_sched_create_cookie(pid, args->type);
+ core_sched_get_and_print_cookie(pid);
+ }
+
+ if (execvp(argv[0], argv))
+ errexec(argv[0]);
+}
+
+// If PR_SCHED_CORE is not recognized, or not supported on this system,
+// then prctl will set errno to EINVAL. Assuming all other operands of
+// prctl are valid, we can use errno==EINVAL as a check to see whether
+// core scheduling is available on this system.
+static bool is_core_sched_supported(void)
+{
+ sched_core_cookie cookie = 0;
+ if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_GET, getpid(),
+ PR_SCHED_CORE_SCOPE_THREAD, &cookie))
+ if (errno == EINVAL)
+ return false;
+
+ return true;
+}
+
+static sched_core_scope parse_core_sched_type(char *str)
+{
+ if (!strcmp(str, "pid"))
+ return PR_SCHED_CORE_SCOPE_THREAD;
+ else if (!strcmp(str, "tgid"))
+ return PR_SCHED_CORE_SCOPE_THREAD_GROUP;
+ else if (!strcmp(str, "pgid"))
+ return PR_SCHED_CORE_SCOPE_PROCESS_GROUP;
+
+ bad_usage(_("'%s' is an invalid option. Must be one of pid/tgid/pgid"),
+ str);
+}
+
+static void parse_and_verify_arguments(int argc, char **argv, struct args *args)
+{
+ int c;
+
+ static const struct option longopts[] = {
+ { "source", required_argument, NULL, 's' },
+ { "dest", required_argument, NULL, 'd' },
+ { "dest-type", required_argument, NULL, 't' },
+ { "verbose", no_argument, NULL, 'v' },
+ { "version", no_argument, NULL, 'V' },
+ { "help", no_argument, NULL, 'h' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ while ((c = getopt_long(argc, argv, "s:d:t:vVh", longopts, NULL)) != -1)
+ switch (c) {
+ case 's':
+ args->src = strtopid_or_err(
+ optarg,
+ _("Failed to parse PID for -s/--source"));
+ break;
+ case 'd':
+ args->dest = strtopid_or_err(
+ optarg, _("Failed to parse PID for -d/--dest"));
+ break;
+ case 't':
+ args->type = parse_core_sched_type(optarg);
+ break;
+ case 'v':
+ sched_core_verbose = true;
+ break;
+ case 'V':
+ print_version(EXIT_SUCCESS);
+ case 'h':
+ usage();
+ default:
+ errtryhelp(EXIT_FAILURE);
+ }
+
+ if (argc <= optind) {
+ args->cmd = SCHED_CORE_CMD_GET;
+ } else {
+ if (!strcmp(argv[optind], "get"))
+ args->cmd = SCHED_CORE_CMD_GET;
+ else if (!strcmp(argv[optind], "new"))
+ args->cmd = SCHED_CORE_CMD_NEW;
+ else if (!strcmp(argv[optind], "copy"))
+ args->cmd = SCHED_CORE_CMD_COPY;
+ else
+ bad_usage(_("Unknown function"));
+
+ // Since we parsed an extra "option" outside of getopt_long, we have to
+ // increment optind manually.
+ ++optind;
+ }
+
+ if (args->cmd == SCHED_CORE_CMD_GET && args->dest)
+ bad_usage(_("get does not accept the --dest option"));
+
+ if (args->cmd == SCHED_CORE_CMD_NEW && args->src)
+ bad_usage(_("new does not accept the --source option"));
+
+ // If the -s/--source option is not specified, it defaults to the PID
+ // of the current coresched process
+ if (args->cmd != SCHED_CORE_CMD_NEW && !args->src)
+ args->src = getpid();
+
+ // More arguments have been passed, which means that the user wants to run
+ // another program with a core scheduling cookie.
+ if (argc > optind) {
+ switch (args->cmd) {
+ case SCHED_CORE_CMD_GET:
+ bad_usage(_("bad usage of the get function"));
+ break;
+ case SCHED_CORE_CMD_NEW:
+ if (args->dest)
+ bad_usage(_(
+ "new requires either a -d/--dest or a command"));
+ else
+ args->exec_argv_offset = optind;
+ break;
+ case SCHED_CORE_CMD_COPY:
+ if (args->dest)
+ bad_usage(_(
+ "copy requires either a -d/--dest or a command"));
+ else
+ args->exec_argv_offset = optind;
+ break;
+ }
+ } else {
+ if (args->cmd == SCHED_CORE_CMD_NEW && !args->dest)
+ bad_usage(_(
+ "new requires either a -d/--dest or a command"));
+ if (args->cmd == SCHED_CORE_CMD_COPY && !args->dest)
+ bad_usage(_(
+ "copy requires either a -d/--dest or a command"));
+ }
+}
+
+int main(int argc, char **argv)
+{
+ struct args args = { .type = PR_SCHED_CORE_SCOPE_THREAD_GROUP };
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ close_stdout_atexit();
+
+ parse_and_verify_arguments(argc, argv, &args);
+
+ if (!is_core_sched_supported())
+ errx(EXIT_FAILURE,
+ _("No support for core scheduling found. Does your kernel"
+ "support CONFIG_SCHED_CORE?"));
+
+ sched_core_cookie cookie;
+
+ switch (args.cmd) {
+ case SCHED_CORE_CMD_GET:
+ cookie = core_sched_get_cookie(args.src);
+ printf(_("cookie of pid %d is 0x%lx\n"), args.src, cookie);
+ break;
+ case SCHED_CORE_CMD_NEW:
+ if (args.exec_argv_offset) {
+ core_sched_exec_with_cookie(&args, argv);
+ } else {
+ core_sched_create_cookie(args.dest, args.type);
+ core_sched_get_and_print_cookie(args.dest);
+ }
+ break;
+ case SCHED_CORE_CMD_COPY:
+ if (args.exec_argv_offset)
+ core_sched_exec_with_cookie(&args, argv);
+ else
+ core_sched_copy_cookie(args.src, args.dest, args.type);
+ break;
+ default:
+ usage();
+ }
+}
diff --git a/tests/commands.sh b/tests/commands.sh
index 5674c5ff0..9eef92ccb 100644
--- a/tests/commands.sh
+++ b/tests/commands.sh
@@ -71,6 +71,7 @@ TS_CMD_COLCRT=${TS_CMD_COLCRT:-"${ts_commandsdir}colcrt"}
TS_CMD_COLRM=${TS_CMD_COLRM:-"${ts_commandsdir}colrm"}
TS_CMD_COL=${TS_CMD_COL:-"${ts_commandsdir}col"}
TS_CMD_COLUMN=${TS_CMD_COLUMN:-"${ts_commandsdir}column"}
+TS_CMD_CORESCHED=${TS_CMD_CORESCHED:-"${ts_commandsdir}coresched"}
TS_CMD_ENOSYS=${TS_CMD_ENOSYS-"${ts_commandsdir}enosys"}
TS_CMD_EJECT=${TS_CMD_EJECT-"${ts_commandsdir}eject"}
TS_CMD_EXCH=${TS_CMD_EXCH-"${ts_commandsdir}exch"}
diff --git a/tests/expected/schedutils/coresched-copy-from-child-to-parent b/tests/expected/schedutils/coresched-copy-from-child-to-parent
new file mode 100644
index 000000000..5b9c40052
--- /dev/null
+++ b/tests/expected/schedutils/coresched-copy-from-child-to-parent
@@ -0,0 +1 @@
+DIFFERENT_COOKIE
diff --git a/tests/expected/schedutils/coresched-copy-from-parent-to-nested-child b/tests/expected/schedutils/coresched-copy-from-parent-to-nested-child
new file mode 100644
index 000000000..ecfc41142
--- /dev/null
+++ b/tests/expected/schedutils/coresched-copy-from-parent-to-nested-child
@@ -0,0 +1 @@
+SAME_COOKIE
diff --git a/tests/expected/schedutils/coresched-get-cookie-own-pid b/tests/expected/schedutils/coresched-get-cookie-own-pid
new file mode 100644
index 000000000..84f182cbe
--- /dev/null
+++ b/tests/expected/schedutils/coresched-get-cookie-own-pid
@@ -0,0 +1 @@
+cookie of pid OWN_PID is PARENT_COOKIE
diff --git a/tests/expected/schedutils/coresched-get-cookie-parent-pid b/tests/expected/schedutils/coresched-get-cookie-parent-pid
new file mode 100644
index 000000000..e183e0402
--- /dev/null
+++ b/tests/expected/schedutils/coresched-get-cookie-parent-pid
@@ -0,0 +1 @@
+cookie of pid PARENT_PID is PARENT_COOKIE
diff --git a/tests/expected/schedutils/coresched-new-child-with-new-cookie b/tests/expected/schedutils/coresched-new-child-with-new-cookie
new file mode 100644
index 000000000..5b9c40052
--- /dev/null
+++ b/tests/expected/schedutils/coresched-new-child-with-new-cookie
@@ -0,0 +1 @@
+DIFFERENT_COOKIE
diff --git a/tests/expected/schedutils/coresched-set-cookie-parent-pid.err b/tests/expected/schedutils/coresched-set-cookie-parent-pid.err
new file mode 100644
index 000000000..e7318ffc2
--- /dev/null
+++ b/tests/expected/schedutils/coresched-set-cookie-parent-pid.err
@@ -0,0 +1 @@
+coresched: set cookie of PID PARENT_PID to PARENT_COOKIE
diff --git a/tests/expected/schedutils/set-cookie-parent-pid b/tests/expected/schedutils/set-cookie-parent-pid
new file mode 100644
index 000000000..e7318ffc2
--- /dev/null
+++ b/tests/expected/schedutils/set-cookie-parent-pid
@@ -0,0 +1 @@
+coresched: set cookie of PID PARENT_PID to PARENT_COOKIE
diff --git a/tests/ts/schedutils/coresched b/tests/ts/schedutils/coresched
new file mode 100755
index 000000000..e34fa319f
--- /dev/null
+++ b/tests/ts/schedutils/coresched
@@ -0,0 +1,83 @@
+#!/bin/bash
+# SPDX-License-Identifier: EUPL-1.2
+#
+# This file is part of util-linux
+#
+# Copyright (C) 2024 Thijs Raymakers
+# Licensed under the EUPL v1.2
+
+TS_TOPDIR="${0%/*}/../.."
+TS_DESC="coresched"
+
+. "$TS_TOPDIR"/functions.sh
+ts_init "$*"
+
+ts_check_test_command "$TS_CMD_CORESCHED"
+ts_check_prog "tee"
+ts_check_prog "sed"
+
+# If there is no kernel support, skip the test suite
+CORESCHED_TEST_KERNEL_SUPPORT_CMD=$($TS_CMD_CORESCHED 2>&1)
+if [[ $CORESCHED_TEST_KERNEL_SUPPORT_CMD == *"CONFIG_SCHED_CORE"* ]]; then
+ ts_skip "Kernel has no CONFIG_SCHED_CORE support"
+fi
+
+# The output of coresched contains PIDs and core scheduling cookies, both of which should be
+# assumed to be random values as we have no control over them. The tests replace these values
+# with sed before writing them to the output file, so it can match the expected output file.
+# - The PID of this bash script is replaced with the placeholder `OWN_PID`
+# - The core scheduling cookie of this bash script is replaced by `COOKIE`
+# - Any other cookie is replaced by `DIFFERENT_COOKIE`
+# The behavior of coresched does not depend on the exact values of these cookies, so using
+# placeholder values does not change the behavior tests.
+ts_init_subtest "set-cookie-parent-pid"
+CORESCHED_OUTPUT=$( ($TS_CMD_CORESCHED -v new -d $$ \
+ | tee -a "$TS_OUTPUT") 3>&1 1>&2 2>&3 \
+ | sed "s/$$/PARENT_PID/g")
+CORESCHED_PARENT_COOKIE=$(echo "$CORESCHED_OUTPUT" | sed 's/^.*\(0x.*$\)/\1/g')
+if [ -z "$CORESCHED_PARENT_COOKIE" ]; then
+ ts_failed "empty value for CORESCHED_PARENT_COOKIE"
+fi
+CORESCHED_OUTPUT=$(echo "$CORESCHED_OUTPUT" \
+ | sed "s/$CORESCHED_PARENT_COOKIE/PARENT_COOKIE/g")
+echo "$CORESCHED_OUTPUT" >> "$TS_ERRLOG"
+ts_finalize_subtest
+
+ts_init_subtest "get-cookie-parent-pid"
+$TS_CMD_CORESCHED get -s $$ 2>> "$TS_ERRLOG" \
+ | sed -e "s/$$/PARENT_PID/g" \
+ -e "s/$CORESCHED_PARENT_COOKIE/PARENT_COOKIE/g" >> "$TS_OUTPUT"
+ts_finalize_subtest
+
+ts_init_subtest "get-cookie-own-pid"
+$TS_CMD_CORESCHED get 2>> "$TS_ERRLOG" \
+ | sed -e "s/pid [0-9]\+/pid OWN_PID/g" \
+ -e "s/$CORESCHED_PARENT_COOKIE/PARENT_COOKIE/g" >> "$TS_OUTPUT"
+ts_finalize_subtest
+
+ts_init_subtest "new-child-with-new-cookie"
+$TS_CMD_CORESCHED new -- "$TS_CMD_CORESCHED" get 2>> "$TS_ERRLOG" \
+ | sed -e 's/^.*\(0x.*$\)/\1/g' \
+ -e "s/$CORESCHED_PARENT_COOKIE/SAME_COOKIE/g" \
+ -e "s/0x.*$/DIFFERENT_COOKIE/g" >> "$TS_OUTPUT"
+ts_finalize_subtest
+
+ts_init_subtest "copy-from-parent-to-nested-child"
+$TS_CMD_CORESCHED new -- /bin/bash -c \
+ "$TS_CMD_CORESCHED copy -s $$ -- $TS_CMD_CORESCHED get" \
+2>> "$TS_ERRLOG" \
+ | sed -e 's/^.*\(0x.*$\)/\1/g' \
+ -e "s/$CORESCHED_PARENT_COOKIE/SAME_COOKIE/g" \
+ -e "s/0x.*$/DIFFERENT_COOKIE/g" >> "$TS_OUTPUT"
+ts_finalize_subtest
+
+ts_init_subtest "copy-from-child-to-parent"
+$TS_CMD_CORESCHED new -- /bin/bash -c \
+ "$TS_CMD_CORESCHED copy -s \$\$ -d $$"
+$TS_CMD_CORESCHED get 2>> "$TS_ERRLOG" \
+ | sed -e 's/^.*\(0x.*$\)/\1/g' \
+ -e "s/$CORESCHED_PARENT_COOKIE/SAME_COOKIE/g" \
+ -e "s/0x.*$/DIFFERENT_COOKIE/g" >> "$TS_OUTPUT"
+ts_finalize_subtest
+
+ts_finalize
--
2.44.0
^ permalink raw reply related
* Re: [PATCH v7] coresched: Manage core scheduling cookies for tasks
From: Phil Auld @ 2024-04-10 22:15 UTC (permalink / raw)
To: Thijs Raymakers; +Cc: kzak, thomas, util-linux
In-Reply-To: <20240410211142.260087-1-thijs@raymakers.nl>
Hi Thijs,
On Wed, Apr 10, 2024 at 11:11:41PM +0200 Thijs Raymakers wrote:
> Signed-off-by: Thijs Raymakers <thijs@raymakers.nl>
You took me out throughout. I'm not sure that was needed :)
The new version looks fine. I'm not responsible for merging so if
Karel prefers the new arguments that works for me.
I'll update and test it out tomorrow. But I think you can put me back
if that works for you.
Cheers,
Phil
> ---
>
> Hi Karel,
>
> Thanks for the review!
>
> Op 09-04-2024 om 3:44 p.m. schreef Karel Zak:
> > I find it a little confusing that the --pid is sometimes used as a
> > source and in other cases as a destination.
> >
> > coresched --new --pid 123 # pid is destination
> > coresched --copy --pid 123 --dest 456 # pid is source
> >
> > It seems --copy always requires a source PID (according to the man
> > page), why not require a PID as argument for --copy:
> >
> > coresched --copy 123 --pid 456
> >
> > in this way --pid will be always destination (for 'copy' and 'new'
> > functions) and you will not need extra --dest option at all.
> >
> > If you want to keep the basic functions (e.g. --copy) without
> > arguments, it would be better to have --source, --dest, and
> > --dest-type instead of using the ambiguous --pid.
> >
> > I can also imagine the basic "functions" without "--".
> >
> > coresched [get] [--dest] 123
> > coresched copy [--source] 123 [--dest] 456
> > coresched new [--dest] 456
> >
> > In my opinion, we do not have to strictly adhere to old taskset or
> > similar commands.
>
> The command line arguments are currently somewhat similar in meaning
> to the arguments of taskset, which Phil mentioned he preferred.
> taskset (and others) uses --pid to refer to the PID. This works
> fine if you operate on just a single PID.
>
> I agree that this version of coresched can be indeed confusing to
> use if --pid is overloaded to mean both "source" and
> "destination", depending on the context. coresched needs at most two
> PIDs and can thus not use --pid for everything.
>
> How those two options are called/used seems to be a tradeoff between
> the familiarity by taskset users vs consistency within coreset.
> Either you use --pid which is familiar to users because other programs
> define PIDs like that, but the semantics will be inconsistent and will
> change depending what function of coresched you use.
> Or you use --source and --dest, which is a bit more inconvenient to
> use because you cannot use the same option to refer to a PID across the
> different coresed functions, but you will have consistent behavior of
> each option.
>
> Since you mention that we don't strictly have to adhere to taskset, I
> do have a slight preference for --source/--dest instead of --pid/--dest.
> With the help of the usage and/or man page, this difference shouldn't be
> too cumbersome to users that are familiar with other tools. The benefit
> of internal consistency outweights the benefit of familiarity in my
> opinion.
>
> Version 7 of the patch changes the arguments from --pid/--dest/--type
> (-t/-d/-t) to --source/--dest/--dest-type (-s/-d/-t). It also has the
> functions without the "--", as well as the other suggestions that you
> made. I've also added two more tests.
>
> The commands now look something like this
>
> coresched [get] [--source <PID>]
> coresched new [-t <TYPE>] --dest <PID>
> coresched new [-t <TYPE>] -- PROGRAM [ARGS...]
> coresched copy [--source <PID>] [-t <TYPE>] --dest <PID>
> coresched copy [--source <PID>] [-t <TYPE>] -- PROGRAM [ARGS...]
>
> > It should be noted that command line arguments are crucial, as they
> > are difficult to modify after release.
>
> It is good that you mentioned that the arguments felt confusing to use,
> because that is something that became a bit invisible to me after using
> the util for some time. It is important to get the arguments right, and
> making sure that they are sensible and self-consistent. These new
> command line arguments work for me as well. Phil previously expressed a
> preference for the --pid style so would like to know what his opinion is.
>
> > pedantic note, use extra space within ifdef
> >
> > #ifndef PR_SCHED_CORE
> > # define PR_SCHED_CORE 62
> > #endif
>
> I've changed to
>
> #ifndef PR_SCHED_CORE
> #define PR_SCHED_CORE 62
> #endif
>
> which looks a bit nicer in my opinion than what clang-format does with
> the AfterHash option. (I've used IndentPPDirectives: BeforeHash instead
> of IndentPPDirectives: None, which is the default in the kernel
> clang-format file)
>
>
> CC: Phil Auld and Thomas Weißschuh
> I've removed your Signed-off-by/Reviewed-by tags since this version
> differs significantly from the version that you've previously reviewed.
> I did't want to just assume that you approve of this version as well.
> With your permission, I can add the tags back again.
>
>
> Thijs
>
> Interdiff against v6:
> diff --git a/schedutils/coresched.1.adoc b/schedutils/coresched.1.adoc
> index c83fcdf91..00f2603e0 100644
> --- a/schedutils/coresched.1.adoc
> +++ b/schedutils/coresched.1.adoc
> @@ -17,9 +17,15 @@ coresched - manage core scheduling cookies for tasks
>
> == SYNOPSIS
>
> -*{command}* [options] *-p* _pid_
> +*{command}* [*get*] [*-s* _pid_]
>
> -*{command}* [options] \-- _command_ [_argument_...]
> +*{command}* *new* [*-t* _type_] *-d* _pid_
> +
> +*{command}* *new* [*-t* _type_] \-- _command_ [_argument_...]
> +
> +*{command}* *copy* [*-s* _pid_] [*-t* _type_] *-d* _pid_
> +
> +*{command}* *copy* [*-s* _pid_] [*-t* _type_] \-- _command_ [_argument_...]
>
> == DESCRIPTION
> The *{command}* command is used to retrieve or modify the core scheduling cookies of a running process given its _pid_, or to spawn a new _command_ with core scheduling cookies.
> @@ -31,22 +37,28 @@ Only tasks have the same cookie are allowed to be scheduled on the same physical
> It is possible to either assign a new random cookie to a task, or copy a cookie from another task. It is not possible to choose the value of the cookie.
>
> == FUNCTIONS
> -*-n*, *--new*::
> -Assign a new cookie to an existing PID or to execute _command_ with a new cookie.
> +*get*::
> +Retrieve the core scheduling cookie of the PID specified in *-s*.
> +If *-s* is omitted, it will get the cookie of the current *{command}* process.
>
> -*-c*, *--copy*::
> -Copy the cookie from an existing PID to another PID, or execute _command_ with that copied cookie.
> +*new*::
> +Assign a new cookie to an existing PID specified in *-d*, or execute _command_ with a new cookie.
>
> -If no function is specified, it will print the cookie of the current task, or of the task specified with the *-p* option.
> +*copy*::
> +Copy the cookie from an existing PID (*-s*) to another PID (*-d*), or execute _command_ with that cookie.
> +If *-s* is omitted, it will get the cookie of the current *{command}* process.
> +
> +If no function is specified, it will run the *get* function.
>
> == OPTIONS
> -*-p*, *--pid* _PID_::
> -Operate on an existing PID and do not launch a new task.
> +*-s*, *--source* _PID_::
> +Which _PID_ to get the cookie from.
> +If this option is omitted, it will get the cookie from the current *{command}* process.
>
> *-d*, *--dest* _PID_::
> -When using *--copy*, specify a destination PID where you want to copy the cookie to.
> +Which _PID_ to modify the cookie of.
>
> -*-t*, *--type* _TYPE_::
> +*-t*, *--dest-type* _TYPE_::
> The type of the PID whose cookie will be modified. This can be one of three values:
> - *pid*, or process ID
> - *tgid*, or thread group ID (default value)
> @@ -63,25 +75,28 @@ Print version and exit.
>
> == EXAMPLES
> Get the core scheduling cookie of the {command} task itself, usually inherited from its parent{colon}::
> -*{command}*
> +*{command} get*
>
> Get the core scheduling cookie of a task with PID _123_{colon}::
> -*{command} -p* _123_
> +*{command} get -s* _123_
>
> Give a task with PID _123_ a new core scheduling cookie{colon}::
> -*{command} --new -p* _123_
> +*{command} new -d* _123_
>
> Spawn a new task with a new core scheduling cookie{colon}::
> -*{command} --new* \-- _command_ [_argument_...]
> +*{command} new* \-- _command_ [_argument_...]
> +
> +Copy the cookie from the current {command} process another task with pid _456_{colon}::
> +*{command} copy -d* _456_
>
> Copy the cookie from a task with pid _123_ to another task with pid _456_{colon}::
> -*{command} --copy -p* _123_ *-d* _456_
> +*{command} copy -s* _123_ *-d* _456_
>
> Copy the cookie from a task with pid _123_ to a new task _command_{colon}::
> -*{command} --copy -p* _123_ \-- _command_ [_argument_...]
> +*{command} copy -s* _123_ \-- _command_ [_argument_...]
>
> Copy the cookie from a task with pid _123_ to the process group ID _456_{colon}::
> -*{command} --copy -p* _123_ *-t* pgid *-d* _456_
> +*{command} copy -s* _123_ *-t* _pgid_ *-d* _456_
>
> == PERMISSIONS
> When retrieving or modifying the core scheduling cookie of a process, you need to have *PTRACE_MODE_READ_REALCREDS* ptrace access to that process.
> @@ -98,12 +113,11 @@ If a _command_ is being executed, the return value of *{command}* will be the re
> This can be enabled via the *CONFIG_SCHED_CORE* kernel config option.
>
> == AUTHORS
> -mailto:thijs@raymakers.nl[Thijs Raymakers],
> -mailto:pauld@redhat.com[Phil Auld]
> +mailto:thijs@raymakers.nl[Thijs Raymakers]
>
> == COPYRIGHT
>
> -Copyright {copyright} 2024 Thijs Raymakers and Phil Auld. This is free software licensed under the EUPL.
> +Copyright {copyright} 2024 Thijs Raymakers. This is free software licensed under the EUPL.
>
> == SEE ALSO
> *chrt*(1),
> diff --git a/schedutils/coresched.c b/schedutils/coresched.c
> index b6be1717d..7e3cc46d2 100644
> --- a/schedutils/coresched.c
> +++ b/schedutils/coresched.c
> @@ -3,7 +3,7 @@
> *
> * coresched.c - manage core scheduling cookies for tasks
> *
> - * Copyright (C) 2024 Thijs Raymakers, Phil Auld
> + * Copyright (C) 2024 Thijs Raymakers
> * Licensed under the EUPL v1.2
> */
>
> @@ -16,34 +16,33 @@
> #include "c.h"
> #include "closestream.h"
> #include "nls.h"
> -#include "optutils.h"
> #include "strutils.h"
>
> // These definitions might not be defined in the header files, even if the
> // prctl interface in the kernel accepts them as valid.
> #ifndef PR_SCHED_CORE
> -#define PR_SCHED_CORE 62
> + #define PR_SCHED_CORE 62
> #endif
> #ifndef PR_SCHED_CORE_GET
> -#define PR_SCHED_CORE_GET 0
> + #define PR_SCHED_CORE_GET 0
> #endif
> #ifndef PR_SCHED_CORE_CREATE
> -#define PR_SCHED_CORE_CREATE 1
> + #define PR_SCHED_CORE_CREATE 1
> #endif
> #ifndef PR_SCHED_CORE_SHARE_TO
> -#define PR_SCHED_CORE_SHARE_TO 2
> + #define PR_SCHED_CORE_SHARE_TO 2
> #endif
> #ifndef PR_SCHED_CORE_SHARE_FROM
> -#define PR_SCHED_CORE_SHARE_FROM 3
> + #define PR_SCHED_CORE_SHARE_FROM 3
> #endif
> #ifndef PR_SCHED_CORE_SCOPE_THREAD
> -#define PR_SCHED_CORE_SCOPE_THREAD 0
> + #define PR_SCHED_CORE_SCOPE_THREAD 0
> #endif
> #ifndef PR_SCHED_CORE_SCOPE_THREAD_GROUP
> -#define PR_SCHED_CORE_SCOPE_THREAD_GROUP 1
> + #define PR_SCHED_CORE_SCOPE_THREAD_GROUP 1
> #endif
> #ifndef PR_SCHED_CORE_SCOPE_PROCESS_GROUP
> -#define PR_SCHED_CORE_SCOPE_PROCESS_GROUP 2
> + #define PR_SCHED_CORE_SCOPE_PROCESS_GROUP 2
> #endif
>
> typedef int sched_core_scope;
> @@ -55,7 +54,7 @@ typedef enum {
> } sched_core_cmd;
>
> struct args {
> - pid_t pid;
> + pid_t src;
> pid_t dest;
> sched_core_scope type;
> sched_core_cmd cmd;
> @@ -67,40 +66,44 @@ static bool sched_core_verbose = false;
> static void __attribute__((__noreturn__)) usage(void)
> {
> fputs(USAGE_HEADER, stdout);
> - fprintf(stdout, _(" %s [-p PID]\n"), program_invocation_short_name);
> - fprintf(stdout, _(" %s --new [-t <TYPE>] -p <PID>\n"),
> + fprintf(stdout, _(" %s [get] [--source <PID>]\n"),
> program_invocation_short_name);
> - fprintf(stdout, _(" %s --new [-t <TYPE>] -- PROGRAM [ARGS...]\n"),
> + fprintf(stdout, _(" %s new [-t <TYPE>] --dest <PID>\n"),
> program_invocation_short_name);
> - fprintf(stdout, _(" %s --copy -p <PID> [-t <TYPE>] -d <PID>\n"),
> + fprintf(stdout, _(" %s new [-t <TYPE>] -- PROGRAM [ARGS...]\n"),
> program_invocation_short_name);
> fprintf(stdout,
> - _(" %s --copy -p <PID> [-t <TYPE>] -- PROGRAM [ARGS...]\n"),
> + _(" %s copy [--source <PID>] [-t <TYPE>] --dest <PID>\n"),
> + program_invocation_short_name);
> + fprintf(stdout,
> + _(" %s copy [--source <PID>] [-t <TYPE>] -- PROGRAM [ARGS...]\n"),
> program_invocation_short_name);
>
> fputs(USAGE_SEPARATOR, stdout);
> fputsln(_("Manage core scheduling cookies for tasks."), stdout);
>
> fputs(USAGE_FUNCTIONS, stdout);
> - fputsln(_(" -n, --new assign a new core scheduling cookie to an existing PID or\n"
> - " execute a program with a new cookie."),
> + fputsln(_(" get retrieve the core scheduling cookie of a PID"),
> stdout);
> - fputsln(_(" -c, --copy copy the core scheduling cookie from an existing PID to\n"
> - " either another PID, or copy it to a new program"),
> + fputsln(_(" new assign a new core scheduling cookie to an existing\n"
> + " PID or execute a program with a new cookie"),
> stdout);
> - fputsln(_("\n If no function is provided, it will retrieve and print the cookie from\n"
> - " the PID provided via --pid.\n"),
> + fputsln(_(" copy copy the core scheduling cookie from an existing PID\n"
> + " to another PID, or execute a program with that\n"
> + " copied cookie"),
> stdout);
>
> fputs(USAGE_OPTIONS, stdout);
> - fputsln(_(" -p, --pid <PID> operate on an existing PID"), stdout);
> - fputsln(_(" -d, --dest <PID> when copying a cookie from an existing PID, --dest is\n"
> - " the destination PID where to copy the cookie to."),
> + fprintf(stdout,
> + _(" -s, --source <PID> which PID to get the cookie from\n"
> + " If omitted, it is the PID of %s itself\n"),
> + program_invocation_short_name);
> + fputsln(_(" -d, --dest <PID> which PID to modify the cookie of\n"),
> stdout);
> - fputsln(_(" -t, --type <TYPE> type of the destination PID, or the type of the PID\n"
> - " when a new core scheduling cookie is created.\n"
> - " Can be one of the following: pid, tgid or pgid.\n"
> - " The default is tgid."),
> + fputsln(_(" -t, --dest-type <TYPE> type of the destination PID, or the type of the PID\n"
> + " when a new core scheduling cookie is created.\n"
> + " Can be one of the following: pid, tgid or pgid.\n"
> + " The default is tgid."),
> stdout);
> fputs(USAGE_SEPARATOR, stdout);
> fputsln(_(" -v, --verbose verbose"), stdout);
> @@ -151,9 +154,8 @@ static void core_sched_copy_cookie(pid_t from, pid_t to,
>
> if (sched_core_verbose) {
> sched_core_cookie before = core_sched_get_cookie(from);
> - fprintf(stderr,
> - _("%s: copied cookie 0x%lx from PID %d to PID %d\n"),
> - program_invocation_short_name, before, from, to);
> + warnx(_("copied cookie 0x%lx from PID %d to PID %d"), before,
> + from, to);
> }
> }
>
> @@ -161,25 +163,21 @@ static void core_sched_get_and_print_cookie(pid_t pid)
> {
> if (sched_core_verbose) {
> sched_core_cookie after = core_sched_get_cookie(pid);
> - fprintf(stderr, _("%s: set cookie of PID %d to 0x%lx\n"),
> - program_invocation_short_name, pid, after);
> + warnx(_("set cookie of PID %d to 0x%lx"), pid, after);
> }
> }
>
> static 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->pid) {
> - core_sched_pull_cookie(args->pid);
> - core_sched_get_and_print_cookie(args->pid);
> + if (args->src) {
> + core_sched_pull_cookie(args->src);
> + core_sched_get_and_print_cookie(args->src);
> } else {
> pid_t pid = getpid();
> core_sched_create_cookie(pid, args->type);
> @@ -218,42 +216,26 @@ static sched_core_scope parse_core_sched_type(char *str)
> str);
> }
>
> -static void parse_arguments(int argc, char **argv, struct args *args)
> +static void parse_and_verify_arguments(int argc, char **argv, struct args *args)
> {
> int c;
>
> static const struct option longopts[] = {
> - { "new", no_argument, NULL, 'n' },
> - { "copy", no_argument, NULL, 'c' },
> - { "pid", required_argument, NULL, 'p' },
> + { "source", required_argument, NULL, 's' },
> { "dest", required_argument, NULL, 'd' },
> - { "type", required_argument, NULL, 't' },
> + { "dest-type", required_argument, NULL, 't' },
> { "verbose", no_argument, NULL, 'v' },
> { "version", no_argument, NULL, 'V' },
> { "help", no_argument, NULL, 'h' },
> { NULL, 0, NULL, 0 }
> };
> - static const ul_excl_t excl[] = {
> - { 'c', 'n' }, // Cannot do both --new and --copy
> - { 'd', 'n' }, // Cannot have both --new and --dest
> - { 0 }
> - };
> -
> - int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
>
> - while ((c = getopt_long(argc, argv, "ncp:d:t:vVh", longopts, NULL)) !=
> - -1) {
> - err_exclusive_options(c, longopts, excl, excl_st);
> + while ((c = getopt_long(argc, argv, "s:d:t:vVh", longopts, NULL)) != -1)
> switch (c) {
> - case 'n':
> - args->cmd = SCHED_CORE_CMD_NEW;
> - break;
> - case 'c':
> - args->cmd = SCHED_CORE_CMD_COPY;
> - break;
> - case 'p':
> - args->pid = strtopid_or_err(
> - optarg, _("Failed to parse PID for -p/--pid"));
> + case 's':
> + args->src = strtopid_or_err(
> + optarg,
> + _("Failed to parse PID for -s/--source"));
> break;
> case 'd':
> args->dest = strtopid_or_err(
> @@ -272,58 +254,77 @@ static void parse_arguments(int argc, char **argv, struct args *args)
> default:
> errtryhelp(EXIT_FAILURE);
> }
> +
> + if (argc <= optind) {
> + args->cmd = SCHED_CORE_CMD_GET;
> + } else {
> + if (!strcmp(argv[optind], "get"))
> + args->cmd = SCHED_CORE_CMD_GET;
> + else if (!strcmp(argv[optind], "new"))
> + args->cmd = SCHED_CORE_CMD_NEW;
> + else if (!strcmp(argv[optind], "copy"))
> + args->cmd = SCHED_CORE_CMD_COPY;
> + else
> + bad_usage(_("Unknown function"));
> +
> + // Since we parsed an extra "option" outside of getopt_long, we have to
> + // increment optind manually.
> + ++optind;
> }
>
> - if (args->cmd == SCHED_CORE_CMD_COPY && !args->pid)
> - bad_usage(_("--copy: requires a -p/--pid"));
> + if (args->cmd == SCHED_CORE_CMD_GET && args->dest)
> + bad_usage(_("get does not accept the --dest option"));
> +
> + if (args->cmd == SCHED_CORE_CMD_NEW && args->src)
> + bad_usage(_("new does not accept the --source option"));
> +
> + // If the -s/--source option is not specified, it defaults to the PID
> + // of the current coresched process
> + if (args->cmd != SCHED_CORE_CMD_NEW && !args->src)
> + args->src = getpid();
>
> // More arguments have been passed, which means that the user wants to run
> // another program with a core scheduling cookie.
> if (argc > optind) {
> switch (args->cmd) {
> case SCHED_CORE_CMD_GET:
> - bad_usage(_("Unknown command"));
> + bad_usage(_("bad usage of the get function"));
> break;
> case SCHED_CORE_CMD_NEW:
> - if (args->pid)
> + if (args->dest)
> bad_usage(_(
> - "--new: cannot accept both a -p/--pid and a command"));
> + "new requires either a -d/--dest or a command"));
> else
> args->exec_argv_offset = optind;
> break;
> case SCHED_CORE_CMD_COPY:
> if (args->dest)
> bad_usage(_(
> - "--copy: cannot accept both a destination PID "
> - "-d/--dest and a command"));
> + "copy requires either a -d/--dest or a command"));
> else
> args->exec_argv_offset = optind;
> break;
> }
> - }
> -
> - if (argc <= optind) {
> - if (args->cmd == SCHED_CORE_CMD_NEW && !args->pid)
> + } else {
> + if (args->cmd == SCHED_CORE_CMD_NEW && !args->dest)
> bad_usage(_(
> - "--new: requires either a -p/--pid or a command"));
> + "new requires either a -d/--dest or a command"));
> if (args->cmd == SCHED_CORE_CMD_COPY && !args->dest)
> bad_usage(_(
> - "--copy: requires either a -d/--dest or a command"));
> + "copy requires either a -d/--dest or a command"));
> }
> }
>
> int main(int argc, char **argv)
> {
> - struct args args = { 0 };
> - args.cmd = SCHED_CORE_CMD_GET;
> - args.type = PR_SCHED_CORE_SCOPE_THREAD_GROUP;
> + struct args args = { .type = PR_SCHED_CORE_SCOPE_THREAD_GROUP };
>
> setlocale(LC_ALL, "");
> bindtextdomain(PACKAGE, LOCALEDIR);
> textdomain(PACKAGE);
> close_stdout_atexit();
>
> - parse_arguments(argc, argv, &args);
> + parse_and_verify_arguments(argc, argv, &args);
>
> if (!is_core_sched_supported())
> errx(EXIT_FAILURE,
> @@ -331,27 +332,25 @@ int main(int argc, char **argv)
> "support CONFIG_SCHED_CORE?"));
>
> sched_core_cookie cookie;
> - pid_t pid;
>
> switch (args.cmd) {
> case SCHED_CORE_CMD_GET:
> - pid = args.pid ? args.pid : getpid();
> - cookie = core_sched_get_cookie(pid);
> - printf(_("cookie of pid %d is 0x%lx\n"), pid, cookie);
> + cookie = core_sched_get_cookie(args.src);
> + printf(_("cookie of pid %d is 0x%lx\n"), args.src, cookie);
> break;
> case SCHED_CORE_CMD_NEW:
> - if (args.pid) {
> - core_sched_create_cookie(args.pid, args.type);
> - core_sched_get_and_print_cookie(args.pid);
> - } else {
> + if (args.exec_argv_offset) {
> core_sched_exec_with_cookie(&args, argv);
> + } else {
> + core_sched_create_cookie(args.dest, args.type);
> + core_sched_get_and_print_cookie(args.dest);
> }
> break;
> case SCHED_CORE_CMD_COPY:
> - if (args.dest)
> - core_sched_copy_cookie(args.pid, args.dest, args.type);
> - else
> + if (args.exec_argv_offset)
> core_sched_exec_with_cookie(&args, argv);
> + else
> + core_sched_copy_cookie(args.src, args.dest, args.type);
> break;
> default:
> usage();
> diff --git a/tests/expected/schedutils/coresched-change-cookie-of-parent b/tests/expected/schedutils/coresched-change-cookie-of-parent
> deleted file mode 100644
> index d08118c02..000000000
> --- a/tests/expected/schedutils/coresched-change-cookie-of-parent
> +++ /dev/null
> @@ -1 +0,0 @@
> -cookie of pid OWN_PID is DIFFERENT_COOKIE
> diff --git a/tests/expected/schedutils/coresched-spawn-child-with-new-cookie b/tests/expected/schedutils/coresched-copy-from-child-to-parent
> similarity index 100%
> rename from tests/expected/schedutils/coresched-spawn-child-with-new-cookie
> rename to tests/expected/schedutils/coresched-copy-from-child-to-parent
> diff --git a/tests/expected/schedutils/coresched-copy-from-parent-to-nested-child b/tests/expected/schedutils/coresched-copy-from-parent-to-nested-child
> new file mode 100644
> index 000000000..ecfc41142
> --- /dev/null
> +++ b/tests/expected/schedutils/coresched-copy-from-parent-to-nested-child
> @@ -0,0 +1 @@
> +SAME_COOKIE
> diff --git a/tests/expected/schedutils/coresched-get-cookie-own-pid b/tests/expected/schedutils/coresched-get-cookie-own-pid
> new file mode 100644
> index 000000000..84f182cbe
> --- /dev/null
> +++ b/tests/expected/schedutils/coresched-get-cookie-own-pid
> @@ -0,0 +1 @@
> +cookie of pid OWN_PID is PARENT_COOKIE
> diff --git a/tests/expected/schedutils/coresched-get-cookie-parent-pid b/tests/expected/schedutils/coresched-get-cookie-parent-pid
> new file mode 100644
> index 000000000..e183e0402
> --- /dev/null
> +++ b/tests/expected/schedutils/coresched-get-cookie-parent-pid
> @@ -0,0 +1 @@
> +cookie of pid PARENT_PID is PARENT_COOKIE
> diff --git a/tests/expected/schedutils/coresched-get-own-pid-with-cookie b/tests/expected/schedutils/coresched-get-own-pid-with-cookie
> deleted file mode 100644
> index 3d7f08040..000000000
> --- a/tests/expected/schedutils/coresched-get-own-pid-with-cookie
> +++ /dev/null
> @@ -1 +0,0 @@
> -cookie of pid OWN_PID is COOKIE
> diff --git a/tests/expected/schedutils/coresched-new-child-with-new-cookie b/tests/expected/schedutils/coresched-new-child-with-new-cookie
> new file mode 100644
> index 000000000..5b9c40052
> --- /dev/null
> +++ b/tests/expected/schedutils/coresched-new-child-with-new-cookie
> @@ -0,0 +1 @@
> +DIFFERENT_COOKIE
> diff --git a/tests/expected/schedutils/coresched-set-cookie-own-pid.err b/tests/expected/schedutils/coresched-set-cookie-own-pid.err
> deleted file mode 100644
> index 1816a4fbd..000000000
> --- a/tests/expected/schedutils/coresched-set-cookie-own-pid.err
> +++ /dev/null
> @@ -1 +0,0 @@
> -coresched: set cookie of PID OWN_PID to COOKIE
> diff --git a/tests/expected/schedutils/coresched-set-cookie-parent-pid.err b/tests/expected/schedutils/coresched-set-cookie-parent-pid.err
> new file mode 100644
> index 000000000..e7318ffc2
> --- /dev/null
> +++ b/tests/expected/schedutils/coresched-set-cookie-parent-pid.err
> @@ -0,0 +1 @@
> +coresched: set cookie of PID PARENT_PID to PARENT_COOKIE
> diff --git a/tests/expected/schedutils/set-cookie-parent-pid b/tests/expected/schedutils/set-cookie-parent-pid
> new file mode 100644
> index 000000000..e7318ffc2
> --- /dev/null
> +++ b/tests/expected/schedutils/set-cookie-parent-pid
> @@ -0,0 +1 @@
> +coresched: set cookie of PID PARENT_PID to PARENT_COOKIE
> diff --git a/tests/ts/schedutils/coresched b/tests/ts/schedutils/coresched
> index ed96d89de..e34fa319f 100755
> --- a/tests/ts/schedutils/coresched
> +++ b/tests/ts/schedutils/coresched
> @@ -13,8 +13,8 @@ TS_DESC="coresched"
> ts_init "$*"
>
> ts_check_test_command "$TS_CMD_CORESCHED"
> -ts_check_test_command "tee"
> -ts_check_test_command "sed"
> +ts_check_prog "tee"
> +ts_check_prog "sed"
>
> # If there is no kernel support, skip the test suite
> CORESCHED_TEST_KERNEL_SUPPORT_CMD=$($TS_CMD_CORESCHED 2>&1)
> @@ -30,33 +30,54 @@ fi
> # - Any other cookie is replaced by `DIFFERENT_COOKIE`
> # The behavior of coresched does not depend on the exact values of these cookies, so using
> # placeholder values does not change the behavior tests.
> -ts_init_subtest "set-cookie-own-pid"
> -CORESCHED_OUTPUT=$( ($TS_CMD_CORESCHED -v -n -p $$ | tee -a "$TS_OUTPUT") 3>&1 1>&2 2>&3 | sed "s/$$/OWN_PID/g")
> -CORESCHED_COOKIE=$(echo "$CORESCHED_OUTPUT" | sed 's/^.*\(0x.*$\)/\1/g')
> -if [ -z "$CORESCHED_COOKIE" ]; then
> - ts_failed "empty value for CORESCHED_COOKIE "
> +ts_init_subtest "set-cookie-parent-pid"
> +CORESCHED_OUTPUT=$( ($TS_CMD_CORESCHED -v new -d $$ \
> + | tee -a "$TS_OUTPUT") 3>&1 1>&2 2>&3 \
> + | sed "s/$$/PARENT_PID/g")
> +CORESCHED_PARENT_COOKIE=$(echo "$CORESCHED_OUTPUT" | sed 's/^.*\(0x.*$\)/\1/g')
> +if [ -z "$CORESCHED_PARENT_COOKIE" ]; then
> + ts_failed "empty value for CORESCHED_PARENT_COOKIE"
> fi
> -CORESCHED_OUTPUT=$(echo "$CORESCHED_OUTPUT" | sed "s/$CORESCHED_COOKIE/COOKIE/g")
> +CORESCHED_OUTPUT=$(echo "$CORESCHED_OUTPUT" \
> + | sed "s/$CORESCHED_PARENT_COOKIE/PARENT_COOKIE/g")
> echo "$CORESCHED_OUTPUT" >> "$TS_ERRLOG"
> ts_finalize_subtest
>
> -ts_init_subtest "get-own-pid-with-cookie"
> -$TS_CMD_CORESCHED -p $$ 2>> "$TS_ERRLOG" | sed "s/$$/OWN_PID/g" | sed "s/$CORESCHED_COOKIE/COOKIE/g" >> "$TS_OUTPUT"
> +ts_init_subtest "get-cookie-parent-pid"
> +$TS_CMD_CORESCHED get -s $$ 2>> "$TS_ERRLOG" \
> + | sed -e "s/$$/PARENT_PID/g" \
> + -e "s/$CORESCHED_PARENT_COOKIE/PARENT_COOKIE/g" >> "$TS_OUTPUT"
> ts_finalize_subtest
>
> -ts_init_subtest "spawn-child-with-new-cookie"
> -$TS_CMD_CORESCHED -n -- "$TS_CMD_CORESCHED" 2>> "$TS_ERRLOG" \
> - | sed 's/^.*\(0x.*$\)/\1/g' \
> - | sed "s/$CORESCHED_COOKIE/SAME_COOKIE/g" \
> - | sed "s/0x.*$/DIFFERENT_COOKIE/g" >> "$TS_OUTPUT"
> +ts_init_subtest "get-cookie-own-pid"
> +$TS_CMD_CORESCHED get 2>> "$TS_ERRLOG" \
> + | sed -e "s/pid [0-9]\+/pid OWN_PID/g" \
> + -e "s/$CORESCHED_PARENT_COOKIE/PARENT_COOKIE/g" >> "$TS_OUTPUT"
> ts_finalize_subtest
>
> -ts_init_subtest "change-cookie-of-parent"
> -$TS_CMD_CORESCHED -n -- /bin/bash -c "$TS_CMD_CORESCHED -c -p \$\$ -d $$"
> -$TS_CMD_CORESCHED -p $$ 2>> "$TS_ERRLOG" \
> - | sed "s/$$/OWN_PID/g" \
> - | sed "s/$CORESCHED_COOKIE/COOKIE/g" \
> - | sed "s/0x.*$/DIFFERENT_COOKIE/g" >> "$TS_OUTPUT"
> +ts_init_subtest "new-child-with-new-cookie"
> +$TS_CMD_CORESCHED new -- "$TS_CMD_CORESCHED" get 2>> "$TS_ERRLOG" \
> + | sed -e 's/^.*\(0x.*$\)/\1/g' \
> + -e "s/$CORESCHED_PARENT_COOKIE/SAME_COOKIE/g" \
> + -e "s/0x.*$/DIFFERENT_COOKIE/g" >> "$TS_OUTPUT"
> +ts_finalize_subtest
> +
> +ts_init_subtest "copy-from-parent-to-nested-child"
> +$TS_CMD_CORESCHED new -- /bin/bash -c \
> + "$TS_CMD_CORESCHED copy -s $$ -- $TS_CMD_CORESCHED get" \
> +2>> "$TS_ERRLOG" \
> + | sed -e 's/^.*\(0x.*$\)/\1/g' \
> + -e "s/$CORESCHED_PARENT_COOKIE/SAME_COOKIE/g" \
> + -e "s/0x.*$/DIFFERENT_COOKIE/g" >> "$TS_OUTPUT"
> +ts_finalize_subtest
> +
> +ts_init_subtest "copy-from-child-to-parent"
> +$TS_CMD_CORESCHED new -- /bin/bash -c \
> + "$TS_CMD_CORESCHED copy -s \$\$ -d $$"
> +$TS_CMD_CORESCHED get 2>> "$TS_ERRLOG" \
> + | sed -e 's/^.*\(0x.*$\)/\1/g' \
> + -e "s/$CORESCHED_PARENT_COOKIE/SAME_COOKIE/g" \
> + -e "s/0x.*$/DIFFERENT_COOKIE/g" >> "$TS_OUTPUT"
> ts_finalize_subtest
>
> ts_finalize
>
> .gitignore | 1 +
> bash-completion/coresched | 0
> configure.ac | 12 +-
> meson.build | 16 +-
> meson_options.txt | 2 +-
> schedutils/Makemodule.am | 8 +
> schedutils/coresched.1.adoc | 138 +++++++
> schedutils/coresched.c | 358 ++++++++++++++++++
> tests/commands.sh | 1 +
> .../coresched-copy-from-child-to-parent | 1 +
> ...coresched-copy-from-parent-to-nested-child | 1 +
> .../schedutils/coresched-get-cookie-own-pid | 1 +
> .../coresched-get-cookie-parent-pid | 1 +
> .../coresched-new-child-with-new-cookie | 1 +
> .../coresched-set-cookie-parent-pid.err | 1 +
> .../expected/schedutils/set-cookie-parent-pid | 1 +
> tests/ts/schedutils/coresched | 83 ++++
> 17 files changed, 620 insertions(+), 6 deletions(-)
> create mode 100644 bash-completion/coresched
> create mode 100644 schedutils/coresched.1.adoc
> create mode 100644 schedutils/coresched.c
> create mode 100644 tests/expected/schedutils/coresched-copy-from-child-to-parent
> create mode 100644 tests/expected/schedutils/coresched-copy-from-parent-to-nested-child
> create mode 100644 tests/expected/schedutils/coresched-get-cookie-own-pid
> create mode 100644 tests/expected/schedutils/coresched-get-cookie-parent-pid
> create mode 100644 tests/expected/schedutils/coresched-new-child-with-new-cookie
> create mode 100644 tests/expected/schedutils/coresched-set-cookie-parent-pid.err
> create mode 100644 tests/expected/schedutils/set-cookie-parent-pid
> create mode 100755 tests/ts/schedutils/coresched
>
> 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..a3a648753 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..00f2603e0
> --- /dev/null
> +++ b/schedutils/coresched.1.adoc
> @@ -0,0 +1,138 @@
> +//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
> +
> +== SYNOPSIS
> +
> +*{command}* [*get*] [*-s* _pid_]
> +
> +*{command}* *new* [*-t* _type_] *-d* _pid_
> +
> +*{command}* *new* [*-t* _type_] \-- _command_ [_argument_...]
> +
> +*{command}* *copy* [*-s* _pid_] [*-t* _type_] *-d* _pid_
> +
> +*{command}* *copy* [*-s* _pid_] [*-t* _type_] \-- _command_ [_argument_...]
> +
> +== DESCRIPTION
> +The *{command}* command is used to retrieve or modify the core scheduling cookies of a running process given its _pid_, or to spawn a new _command_ with core scheduling cookies.
> +
> +Core scheduling allows you to define groups of tasks that are allowed to share a physical core.
> +This is done by assigning a cookie to each task.
> +Only tasks have the same cookie are allowed to be scheduled on the same physical core.
> +
> +It is possible to either assign a new random cookie to a task, or copy a cookie from another task. It is not possible to choose the value of the cookie.
> +
> +== FUNCTIONS
> +*get*::
> +Retrieve the core scheduling cookie of the PID specified in *-s*.
> +If *-s* is omitted, it will get the cookie of the current *{command}* process.
> +
> +*new*::
> +Assign a new cookie to an existing PID specified in *-d*, or execute _command_ with a new cookie.
> +
> +*copy*::
> +Copy the cookie from an existing PID (*-s*) to another PID (*-d*), or execute _command_ with that cookie.
> +If *-s* is omitted, it will get the cookie of the current *{command}* process.
> +
> +If no function is specified, it will run the *get* function.
> +
> +== OPTIONS
> +*-s*, *--source* _PID_::
> +Which _PID_ to get the cookie from.
> +If this option is omitted, it will get the cookie from the current *{command}* process.
> +
> +*-d*, *--dest* _PID_::
> +Which _PID_ to modify the cookie of.
> +
> +*-t*, *--dest-type* _TYPE_::
> +The type of the PID whose cookie will be modified. This can be one of three values:
> +- *pid*, or process ID
> +- *tgid*, or thread group ID (default value)
> +- *pgid*, or process group ID
> +
> +*-v*, *--verbose*::
> +Show extra information when modifying cookies of tasks.
> +
> +*-h*, *--help*::
> +Display help text and exit.
> +
> +*-V*, *--version*::
> +Print version and exit.
> +
> +== EXAMPLES
> +Get the core scheduling cookie of the {command} task itself, usually inherited from its parent{colon}::
> +*{command} get*
> +
> +Get the core scheduling cookie of a task with PID _123_{colon}::
> +*{command} get -s* _123_
> +
> +Give a task with PID _123_ a new core scheduling cookie{colon}::
> +*{command} new -d* _123_
> +
> +Spawn a new task with a new core scheduling cookie{colon}::
> +*{command} new* \-- _command_ [_argument_...]
> +
> +Copy the cookie from the current {command} process another task with pid _456_{colon}::
> +*{command} copy -d* _456_
> +
> +Copy the cookie from a task with pid _123_ to another task with pid _456_{colon}::
> +*{command} copy -s* _123_ *-d* _456_
> +
> +Copy the cookie from a task with pid _123_ to a new task _command_{colon}::
> +*{command} copy -s* _123_ \-- _command_ [_argument_...]
> +
> +Copy the cookie from a task with pid _123_ to the process group ID _456_{colon}::
> +*{command} copy -s* _123_ *-t* _pgid_ *-d* _456_
> +
> +== PERMISSIONS
> +When retrieving or modifying the core scheduling cookie of a process, you need to have *PTRACE_MODE_READ_REALCREDS* ptrace access to that process.
> +See the section "Ptrace access mode checking" in *ptrace*(2) for more information.
> +
> +== RETURN VALUE
> +On success, *{command}* returns 0.
> +If *{command}* fails, it will print an error and return 1.
> +
> +If a _command_ is being executed, the return value of *{command}* will be the return value of _command_.
> +
> +== NOTES
> +*{command}* requires core scheduling support in the kernel.
> +This can be enabled via the *CONFIG_SCHED_CORE* kernel config option.
> +
> +== AUTHORS
> +mailto:thijs@raymakers.nl[Thijs Raymakers]
> +
> +== COPYRIGHT
> +
> +Copyright {copyright} 2024 Thijs Raymakers. This is free software licensed under the EUPL.
> +
> +== SEE ALSO
> +*chrt*(1),
> +*nice*(1),
> +*renice*(1),
> +*taskset*(1),
> +*ptrace*(2),
> +*sched*(7)
> +
> +The Linux kernel source files _Documentation/admin-guide/hw-vuln/core-scheduling.rst_
> +
> +include::man-common/bugreports.adoc[]
> +
> +include::man-common/footer.adoc[]
> +
> +ifdef::translation[]
> +include::man-common/translation.adoc[]
> +endif::[]
> diff --git a/schedutils/coresched.c b/schedutils/coresched.c
> new file mode 100644
> index 000000000..7e3cc46d2
> --- /dev/null
> +++ b/schedutils/coresched.c
> @@ -0,0 +1,358 @@
> +/**
> + * 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 <unistd.h>
> +
> +#include "c.h"
> +#include "closestream.h"
> +#include "nls.h"
> +#include "strutils.h"
> +
> +// These definitions might not be defined in the header files, even if the
> +// prctl interface in the kernel accepts them as valid.
> +#ifndef PR_SCHED_CORE
> + #define PR_SCHED_CORE 62
> +#endif
> +#ifndef PR_SCHED_CORE_GET
> + #define PR_SCHED_CORE_GET 0
> +#endif
> +#ifndef PR_SCHED_CORE_CREATE
> + #define PR_SCHED_CORE_CREATE 1
> +#endif
> +#ifndef PR_SCHED_CORE_SHARE_TO
> + #define PR_SCHED_CORE_SHARE_TO 2
> +#endif
> +#ifndef PR_SCHED_CORE_SHARE_FROM
> + #define PR_SCHED_CORE_SHARE_FROM 3
> +#endif
> +#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 sched_core_scope;
> +typedef unsigned long sched_core_cookie;
> +typedef enum {
> + SCHED_CORE_CMD_GET,
> + SCHED_CORE_CMD_NEW,
> + SCHED_CORE_CMD_COPY,
> +} sched_core_cmd;
> +
> +struct args {
> + pid_t src;
> + pid_t dest;
> + sched_core_scope type;
> + sched_core_cmd cmd;
> + int exec_argv_offset;
> +};
> +
> +static bool sched_core_verbose = false;
> +
> +static void __attribute__((__noreturn__)) usage(void)
> +{
> + fputs(USAGE_HEADER, stdout);
> + fprintf(stdout, _(" %s [get] [--source <PID>]\n"),
> + program_invocation_short_name);
> + fprintf(stdout, _(" %s new [-t <TYPE>] --dest <PID>\n"),
> + program_invocation_short_name);
> + fprintf(stdout, _(" %s new [-t <TYPE>] -- PROGRAM [ARGS...]\n"),
> + program_invocation_short_name);
> + fprintf(stdout,
> + _(" %s copy [--source <PID>] [-t <TYPE>] --dest <PID>\n"),
> + program_invocation_short_name);
> + fprintf(stdout,
> + _(" %s copy [--source <PID>] [-t <TYPE>] -- PROGRAM [ARGS...]\n"),
> + program_invocation_short_name);
> +
> + fputs(USAGE_SEPARATOR, stdout);
> + fputsln(_("Manage core scheduling cookies for tasks."), stdout);
> +
> + fputs(USAGE_FUNCTIONS, stdout);
> + fputsln(_(" get retrieve the core scheduling cookie of a PID"),
> + stdout);
> + fputsln(_(" new assign a new core scheduling cookie to an existing\n"
> + " PID or execute a program with a new cookie"),
> + stdout);
> + fputsln(_(" copy copy the core scheduling cookie from an existing PID\n"
> + " to another PID, or execute a program with that\n"
> + " copied cookie"),
> + stdout);
> +
> + fputs(USAGE_OPTIONS, stdout);
> + fprintf(stdout,
> + _(" -s, --source <PID> which PID to get the cookie from\n"
> + " If omitted, it is the PID of %s itself\n"),
> + program_invocation_short_name);
> + fputsln(_(" -d, --dest <PID> which PID to modify the cookie of\n"),
> + stdout);
> + fputsln(_(" -t, --dest-type <TYPE> type of the destination PID, or the type of the PID\n"
> + " when a new core scheduling cookie is created.\n"
> + " Can be one of the following: pid, tgid or pgid.\n"
> + " The default is tgid."),
> + stdout);
> + fputs(USAGE_SEPARATOR, stdout);
> + fputsln(_(" -v, --verbose verbose"), stdout);
> + fprintf(stdout, USAGE_HELP_OPTIONS(20));
> + fprintf(stdout, USAGE_MAN_TAIL("coresched(1)"));
> + exit(EXIT_SUCCESS);
> +}
> +
> +#define bad_usage(FMT...) \
> + do { \
> + warnx(FMT); \
> + errtryhelp(EXIT_FAILURE); \
> + } while (0)
> +
> +static sched_core_cookie core_sched_get_cookie(pid_t pid)
> +{
> + sched_core_cookie cookie = 0;
> + if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_GET, pid,
> + PR_SCHED_CORE_SCOPE_THREAD, &cookie))
> + err(EXIT_FAILURE, _("Failed to get cookie from PID %d"), pid);
> + return cookie;
> +}
> +
> +static void core_sched_create_cookie(pid_t pid, sched_core_scope type)
> +{
> + if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_CREATE, pid, type, 0))
> + err(EXIT_FAILURE, _("Failed to create cookie for PID %d"), pid);
> +}
> +
> +static 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(EXIT_FAILURE, _("Failed to pull cookie from PID %d"), from);
> +}
> +
> +static void core_sched_push_cookie(pid_t to, sched_core_scope type)
> +{
> + if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_SHARE_TO, to, type, 0))
> + err(EXIT_FAILURE, _("Failed to push cookie to PID %d"), to);
> +}
> +
> +static void core_sched_copy_cookie(pid_t from, pid_t to,
> + sched_core_scope to_type)
> +{
> + core_sched_pull_cookie(from);
> + core_sched_push_cookie(to, to_type);
> +
> + if (sched_core_verbose) {
> + sched_core_cookie before = core_sched_get_cookie(from);
> + warnx(_("copied cookie 0x%lx from PID %d to PID %d"), before,
> + from, to);
> + }
> +}
> +
> +static void core_sched_get_and_print_cookie(pid_t pid)
> +{
> + if (sched_core_verbose) {
> + sched_core_cookie after = core_sched_get_cookie(pid);
> + warnx(_("set cookie of PID %d to 0x%lx"), pid, after);
> + }
> +}
> +
> +static void core_sched_exec_with_cookie(struct args *args, char **argv)
> +{
> + // 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->src) {
> + core_sched_pull_cookie(args->src);
> + core_sched_get_and_print_cookie(args->src);
> + } else {
> + pid_t pid = getpid();
> + core_sched_create_cookie(pid, args->type);
> + core_sched_get_and_print_cookie(pid);
> + }
> +
> + if (execvp(argv[0], argv))
> + errexec(argv[0]);
> +}
> +
> +// If PR_SCHED_CORE is not recognized, or not supported on this system,
> +// then prctl will set errno to EINVAL. Assuming all other operands of
> +// prctl are valid, we can use errno==EINVAL as a check to see whether
> +// core scheduling is available on this system.
> +static bool is_core_sched_supported(void)
> +{
> + sched_core_cookie cookie = 0;
> + if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_GET, getpid(),
> + PR_SCHED_CORE_SCOPE_THREAD, &cookie))
> + if (errno == EINVAL)
> + return false;
> +
> + return true;
> +}
> +
> +static sched_core_scope parse_core_sched_type(char *str)
> +{
> + if (!strcmp(str, "pid"))
> + return PR_SCHED_CORE_SCOPE_THREAD;
> + else if (!strcmp(str, "tgid"))
> + return PR_SCHED_CORE_SCOPE_THREAD_GROUP;
> + else if (!strcmp(str, "pgid"))
> + return PR_SCHED_CORE_SCOPE_PROCESS_GROUP;
> +
> + bad_usage(_("'%s' is an invalid option. Must be one of pid/tgid/pgid"),
> + str);
> +}
> +
> +static void parse_and_verify_arguments(int argc, char **argv, struct args *args)
> +{
> + int c;
> +
> + static const struct option longopts[] = {
> + { "source", required_argument, NULL, 's' },
> + { "dest", required_argument, NULL, 'd' },
> + { "dest-type", required_argument, NULL, 't' },
> + { "verbose", no_argument, NULL, 'v' },
> + { "version", no_argument, NULL, 'V' },
> + { "help", no_argument, NULL, 'h' },
> + { NULL, 0, NULL, 0 }
> + };
> +
> + while ((c = getopt_long(argc, argv, "s:d:t:vVh", longopts, NULL)) != -1)
> + switch (c) {
> + case 's':
> + args->src = strtopid_or_err(
> + optarg,
> + _("Failed to parse PID for -s/--source"));
> + break;
> + case 'd':
> + args->dest = strtopid_or_err(
> + optarg, _("Failed to parse PID for -d/--dest"));
> + break;
> + case 't':
> + args->type = parse_core_sched_type(optarg);
> + break;
> + case 'v':
> + sched_core_verbose = true;
> + break;
> + case 'V':
> + print_version(EXIT_SUCCESS);
> + case 'h':
> + usage();
> + default:
> + errtryhelp(EXIT_FAILURE);
> + }
> +
> + if (argc <= optind) {
> + args->cmd = SCHED_CORE_CMD_GET;
> + } else {
> + if (!strcmp(argv[optind], "get"))
> + args->cmd = SCHED_CORE_CMD_GET;
> + else if (!strcmp(argv[optind], "new"))
> + args->cmd = SCHED_CORE_CMD_NEW;
> + else if (!strcmp(argv[optind], "copy"))
> + args->cmd = SCHED_CORE_CMD_COPY;
> + else
> + bad_usage(_("Unknown function"));
> +
> + // Since we parsed an extra "option" outside of getopt_long, we have to
> + // increment optind manually.
> + ++optind;
> + }
> +
> + if (args->cmd == SCHED_CORE_CMD_GET && args->dest)
> + bad_usage(_("get does not accept the --dest option"));
> +
> + if (args->cmd == SCHED_CORE_CMD_NEW && args->src)
> + bad_usage(_("new does not accept the --source option"));
> +
> + // If the -s/--source option is not specified, it defaults to the PID
> + // of the current coresched process
> + if (args->cmd != SCHED_CORE_CMD_NEW && !args->src)
> + args->src = getpid();
> +
> + // More arguments have been passed, which means that the user wants to run
> + // another program with a core scheduling cookie.
> + if (argc > optind) {
> + switch (args->cmd) {
> + case SCHED_CORE_CMD_GET:
> + bad_usage(_("bad usage of the get function"));
> + break;
> + case SCHED_CORE_CMD_NEW:
> + if (args->dest)
> + bad_usage(_(
> + "new requires either a -d/--dest or a command"));
> + else
> + args->exec_argv_offset = optind;
> + break;
> + case SCHED_CORE_CMD_COPY:
> + if (args->dest)
> + bad_usage(_(
> + "copy requires either a -d/--dest or a command"));
> + else
> + args->exec_argv_offset = optind;
> + break;
> + }
> + } else {
> + if (args->cmd == SCHED_CORE_CMD_NEW && !args->dest)
> + bad_usage(_(
> + "new requires either a -d/--dest or a command"));
> + if (args->cmd == SCHED_CORE_CMD_COPY && !args->dest)
> + bad_usage(_(
> + "copy requires either a -d/--dest or a command"));
> + }
> +}
> +
> +int main(int argc, char **argv)
> +{
> + struct args args = { .type = PR_SCHED_CORE_SCOPE_THREAD_GROUP };
> +
> + setlocale(LC_ALL, "");
> + bindtextdomain(PACKAGE, LOCALEDIR);
> + textdomain(PACKAGE);
> + close_stdout_atexit();
> +
> + parse_and_verify_arguments(argc, argv, &args);
> +
> + if (!is_core_sched_supported())
> + errx(EXIT_FAILURE,
> + _("No support for core scheduling found. Does your kernel"
> + "support CONFIG_SCHED_CORE?"));
> +
> + sched_core_cookie cookie;
> +
> + switch (args.cmd) {
> + case SCHED_CORE_CMD_GET:
> + cookie = core_sched_get_cookie(args.src);
> + printf(_("cookie of pid %d is 0x%lx\n"), args.src, cookie);
> + break;
> + case SCHED_CORE_CMD_NEW:
> + if (args.exec_argv_offset) {
> + core_sched_exec_with_cookie(&args, argv);
> + } else {
> + core_sched_create_cookie(args.dest, args.type);
> + core_sched_get_and_print_cookie(args.dest);
> + }
> + break;
> + case SCHED_CORE_CMD_COPY:
> + if (args.exec_argv_offset)
> + core_sched_exec_with_cookie(&args, argv);
> + else
> + core_sched_copy_cookie(args.src, args.dest, args.type);
> + break;
> + default:
> + usage();
> + }
> +}
> diff --git a/tests/commands.sh b/tests/commands.sh
> index 5674c5ff0..9eef92ccb 100644
> --- a/tests/commands.sh
> +++ b/tests/commands.sh
> @@ -71,6 +71,7 @@ TS_CMD_COLCRT=${TS_CMD_COLCRT:-"${ts_commandsdir}colcrt"}
> TS_CMD_COLRM=${TS_CMD_COLRM:-"${ts_commandsdir}colrm"}
> TS_CMD_COL=${TS_CMD_COL:-"${ts_commandsdir}col"}
> TS_CMD_COLUMN=${TS_CMD_COLUMN:-"${ts_commandsdir}column"}
> +TS_CMD_CORESCHED=${TS_CMD_CORESCHED:-"${ts_commandsdir}coresched"}
> TS_CMD_ENOSYS=${TS_CMD_ENOSYS-"${ts_commandsdir}enosys"}
> TS_CMD_EJECT=${TS_CMD_EJECT-"${ts_commandsdir}eject"}
> TS_CMD_EXCH=${TS_CMD_EXCH-"${ts_commandsdir}exch"}
> diff --git a/tests/expected/schedutils/coresched-copy-from-child-to-parent b/tests/expected/schedutils/coresched-copy-from-child-to-parent
> new file mode 100644
> index 000000000..5b9c40052
> --- /dev/null
> +++ b/tests/expected/schedutils/coresched-copy-from-child-to-parent
> @@ -0,0 +1 @@
> +DIFFERENT_COOKIE
> diff --git a/tests/expected/schedutils/coresched-copy-from-parent-to-nested-child b/tests/expected/schedutils/coresched-copy-from-parent-to-nested-child
> new file mode 100644
> index 000000000..ecfc41142
> --- /dev/null
> +++ b/tests/expected/schedutils/coresched-copy-from-parent-to-nested-child
> @@ -0,0 +1 @@
> +SAME_COOKIE
> diff --git a/tests/expected/schedutils/coresched-get-cookie-own-pid b/tests/expected/schedutils/coresched-get-cookie-own-pid
> new file mode 100644
> index 000000000..84f182cbe
> --- /dev/null
> +++ b/tests/expected/schedutils/coresched-get-cookie-own-pid
> @@ -0,0 +1 @@
> +cookie of pid OWN_PID is PARENT_COOKIE
> diff --git a/tests/expected/schedutils/coresched-get-cookie-parent-pid b/tests/expected/schedutils/coresched-get-cookie-parent-pid
> new file mode 100644
> index 000000000..e183e0402
> --- /dev/null
> +++ b/tests/expected/schedutils/coresched-get-cookie-parent-pid
> @@ -0,0 +1 @@
> +cookie of pid PARENT_PID is PARENT_COOKIE
> diff --git a/tests/expected/schedutils/coresched-new-child-with-new-cookie b/tests/expected/schedutils/coresched-new-child-with-new-cookie
> new file mode 100644
> index 000000000..5b9c40052
> --- /dev/null
> +++ b/tests/expected/schedutils/coresched-new-child-with-new-cookie
> @@ -0,0 +1 @@
> +DIFFERENT_COOKIE
> diff --git a/tests/expected/schedutils/coresched-set-cookie-parent-pid.err b/tests/expected/schedutils/coresched-set-cookie-parent-pid.err
> new file mode 100644
> index 000000000..e7318ffc2
> --- /dev/null
> +++ b/tests/expected/schedutils/coresched-set-cookie-parent-pid.err
> @@ -0,0 +1 @@
> +coresched: set cookie of PID PARENT_PID to PARENT_COOKIE
> diff --git a/tests/expected/schedutils/set-cookie-parent-pid b/tests/expected/schedutils/set-cookie-parent-pid
> new file mode 100644
> index 000000000..e7318ffc2
> --- /dev/null
> +++ b/tests/expected/schedutils/set-cookie-parent-pid
> @@ -0,0 +1 @@
> +coresched: set cookie of PID PARENT_PID to PARENT_COOKIE
> diff --git a/tests/ts/schedutils/coresched b/tests/ts/schedutils/coresched
> new file mode 100755
> index 000000000..e34fa319f
> --- /dev/null
> +++ b/tests/ts/schedutils/coresched
> @@ -0,0 +1,83 @@
> +#!/bin/bash
> +# SPDX-License-Identifier: EUPL-1.2
> +#
> +# This file is part of util-linux
> +#
> +# Copyright (C) 2024 Thijs Raymakers
> +# Licensed under the EUPL v1.2
> +
> +TS_TOPDIR="${0%/*}/../.."
> +TS_DESC="coresched"
> +
> +. "$TS_TOPDIR"/functions.sh
> +ts_init "$*"
> +
> +ts_check_test_command "$TS_CMD_CORESCHED"
> +ts_check_prog "tee"
> +ts_check_prog "sed"
> +
> +# If there is no kernel support, skip the test suite
> +CORESCHED_TEST_KERNEL_SUPPORT_CMD=$($TS_CMD_CORESCHED 2>&1)
> +if [[ $CORESCHED_TEST_KERNEL_SUPPORT_CMD == *"CONFIG_SCHED_CORE"* ]]; then
> + ts_skip "Kernel has no CONFIG_SCHED_CORE support"
> +fi
> +
> +# The output of coresched contains PIDs and core scheduling cookies, both of which should be
> +# assumed to be random values as we have no control over them. The tests replace these values
> +# with sed before writing them to the output file, so it can match the expected output file.
> +# - The PID of this bash script is replaced with the placeholder `OWN_PID`
> +# - The core scheduling cookie of this bash script is replaced by `COOKIE`
> +# - Any other cookie is replaced by `DIFFERENT_COOKIE`
> +# The behavior of coresched does not depend on the exact values of these cookies, so using
> +# placeholder values does not change the behavior tests.
> +ts_init_subtest "set-cookie-parent-pid"
> +CORESCHED_OUTPUT=$( ($TS_CMD_CORESCHED -v new -d $$ \
> + | tee -a "$TS_OUTPUT") 3>&1 1>&2 2>&3 \
> + | sed "s/$$/PARENT_PID/g")
> +CORESCHED_PARENT_COOKIE=$(echo "$CORESCHED_OUTPUT" | sed 's/^.*\(0x.*$\)/\1/g')
> +if [ -z "$CORESCHED_PARENT_COOKIE" ]; then
> + ts_failed "empty value for CORESCHED_PARENT_COOKIE"
> +fi
> +CORESCHED_OUTPUT=$(echo "$CORESCHED_OUTPUT" \
> + | sed "s/$CORESCHED_PARENT_COOKIE/PARENT_COOKIE/g")
> +echo "$CORESCHED_OUTPUT" >> "$TS_ERRLOG"
> +ts_finalize_subtest
> +
> +ts_init_subtest "get-cookie-parent-pid"
> +$TS_CMD_CORESCHED get -s $$ 2>> "$TS_ERRLOG" \
> + | sed -e "s/$$/PARENT_PID/g" \
> + -e "s/$CORESCHED_PARENT_COOKIE/PARENT_COOKIE/g" >> "$TS_OUTPUT"
> +ts_finalize_subtest
> +
> +ts_init_subtest "get-cookie-own-pid"
> +$TS_CMD_CORESCHED get 2>> "$TS_ERRLOG" \
> + | sed -e "s/pid [0-9]\+/pid OWN_PID/g" \
> + -e "s/$CORESCHED_PARENT_COOKIE/PARENT_COOKIE/g" >> "$TS_OUTPUT"
> +ts_finalize_subtest
> +
> +ts_init_subtest "new-child-with-new-cookie"
> +$TS_CMD_CORESCHED new -- "$TS_CMD_CORESCHED" get 2>> "$TS_ERRLOG" \
> + | sed -e 's/^.*\(0x.*$\)/\1/g' \
> + -e "s/$CORESCHED_PARENT_COOKIE/SAME_COOKIE/g" \
> + -e "s/0x.*$/DIFFERENT_COOKIE/g" >> "$TS_OUTPUT"
> +ts_finalize_subtest
> +
> +ts_init_subtest "copy-from-parent-to-nested-child"
> +$TS_CMD_CORESCHED new -- /bin/bash -c \
> + "$TS_CMD_CORESCHED copy -s $$ -- $TS_CMD_CORESCHED get" \
> +2>> "$TS_ERRLOG" \
> + | sed -e 's/^.*\(0x.*$\)/\1/g' \
> + -e "s/$CORESCHED_PARENT_COOKIE/SAME_COOKIE/g" \
> + -e "s/0x.*$/DIFFERENT_COOKIE/g" >> "$TS_OUTPUT"
> +ts_finalize_subtest
> +
> +ts_init_subtest "copy-from-child-to-parent"
> +$TS_CMD_CORESCHED new -- /bin/bash -c \
> + "$TS_CMD_CORESCHED copy -s \$\$ -d $$"
> +$TS_CMD_CORESCHED get 2>> "$TS_ERRLOG" \
> + | sed -e 's/^.*\(0x.*$\)/\1/g' \
> + -e "s/$CORESCHED_PARENT_COOKIE/SAME_COOKIE/g" \
> + -e "s/0x.*$/DIFFERENT_COOKIE/g" >> "$TS_OUTPUT"
> +ts_finalize_subtest
> +
> +ts_finalize
> --
> 2.44.0
>
--
^ permalink raw reply
* [PATCH v7] coresched: Manage core scheduling cookies for tasks
From: Thijs Raymakers @ 2024-04-10 21:11 UTC (permalink / raw)
To: kzak; +Cc: thomas, pauld, util-linux, Thijs Raymakers
In-Reply-To: <20240409134430.twndleg7rxnfsf4m@ws.net.home>
Signed-off-by: Thijs Raymakers <thijs@raymakers.nl>
---
Hi Karel,
Thanks for the review!
Op 09-04-2024 om 3:44 p.m. schreef Karel Zak:
> I find it a little confusing that the --pid is sometimes used as a
> source and in other cases as a destination.
>
> coresched --new --pid 123 # pid is destination
> coresched --copy --pid 123 --dest 456 # pid is source
>
> It seems --copy always requires a source PID (according to the man
> page), why not require a PID as argument for --copy:
>
> coresched --copy 123 --pid 456
>
> in this way --pid will be always destination (for 'copy' and 'new'
> functions) and you will not need extra --dest option at all.
>
> If you want to keep the basic functions (e.g. --copy) without
> arguments, it would be better to have --source, --dest, and
> --dest-type instead of using the ambiguous --pid.
>
> I can also imagine the basic "functions" without "--".
>
> coresched [get] [--dest] 123
> coresched copy [--source] 123 [--dest] 456
> coresched new [--dest] 456
>
> In my opinion, we do not have to strictly adhere to old taskset or
> similar commands.
The command line arguments are currently somewhat similar in meaning
to the arguments of taskset, which Phil mentioned he preferred.
taskset (and others) uses --pid to refer to the PID. This works
fine if you operate on just a single PID.
I agree that this version of coresched can be indeed confusing to
use if --pid is overloaded to mean both "source" and
"destination", depending on the context. coresched needs at most two
PIDs and can thus not use --pid for everything.
How those two options are called/used seems to be a tradeoff between
the familiarity by taskset users vs consistency within coreset.
Either you use --pid which is familiar to users because other programs
define PIDs like that, but the semantics will be inconsistent and will
change depending what function of coresched you use.
Or you use --source and --dest, which is a bit more inconvenient to
use because you cannot use the same option to refer to a PID across the
different coresed functions, but you will have consistent behavior of
each option.
Since you mention that we don't strictly have to adhere to taskset, I
do have a slight preference for --source/--dest instead of --pid/--dest.
With the help of the usage and/or man page, this difference shouldn't be
too cumbersome to users that are familiar with other tools. The benefit
of internal consistency outweights the benefit of familiarity in my
opinion.
Version 7 of the patch changes the arguments from --pid/--dest/--type
(-t/-d/-t) to --source/--dest/--dest-type (-s/-d/-t). It also has the
functions without the "--", as well as the other suggestions that you
made. I've also added two more tests.
The commands now look something like this
coresched [get] [--source <PID>]
coresched new [-t <TYPE>] --dest <PID>
coresched new [-t <TYPE>] -- PROGRAM [ARGS...]
coresched copy [--source <PID>] [-t <TYPE>] --dest <PID>
coresched copy [--source <PID>] [-t <TYPE>] -- PROGRAM [ARGS...]
> It should be noted that command line arguments are crucial, as they
> are difficult to modify after release.
It is good that you mentioned that the arguments felt confusing to use,
because that is something that became a bit invisible to me after using
the util for some time. It is important to get the arguments right, and
making sure that they are sensible and self-consistent. These new
command line arguments work for me as well. Phil previously expressed a
preference for the --pid style so would like to know what his opinion is.
> pedantic note, use extra space within ifdef
>
> #ifndef PR_SCHED_CORE
> # define PR_SCHED_CORE 62
> #endif
I've changed to
#ifndef PR_SCHED_CORE
#define PR_SCHED_CORE 62
#endif
which looks a bit nicer in my opinion than what clang-format does with
the AfterHash option. (I've used IndentPPDirectives: BeforeHash instead
of IndentPPDirectives: None, which is the default in the kernel
clang-format file)
CC: Phil Auld and Thomas Weißschuh
I've removed your Signed-off-by/Reviewed-by tags since this version
differs significantly from the version that you've previously reviewed.
I did't want to just assume that you approve of this version as well.
With your permission, I can add the tags back again.
Thijs
Interdiff against v6:
diff --git a/schedutils/coresched.1.adoc b/schedutils/coresched.1.adoc
index c83fcdf91..00f2603e0 100644
--- a/schedutils/coresched.1.adoc
+++ b/schedutils/coresched.1.adoc
@@ -17,9 +17,15 @@ coresched - manage core scheduling cookies for tasks
== SYNOPSIS
-*{command}* [options] *-p* _pid_
+*{command}* [*get*] [*-s* _pid_]
-*{command}* [options] \-- _command_ [_argument_...]
+*{command}* *new* [*-t* _type_] *-d* _pid_
+
+*{command}* *new* [*-t* _type_] \-- _command_ [_argument_...]
+
+*{command}* *copy* [*-s* _pid_] [*-t* _type_] *-d* _pid_
+
+*{command}* *copy* [*-s* _pid_] [*-t* _type_] \-- _command_ [_argument_...]
== DESCRIPTION
The *{command}* command is used to retrieve or modify the core scheduling cookies of a running process given its _pid_, or to spawn a new _command_ with core scheduling cookies.
@@ -31,22 +37,28 @@ Only tasks have the same cookie are allowed to be scheduled on the same physical
It is possible to either assign a new random cookie to a task, or copy a cookie from another task. It is not possible to choose the value of the cookie.
== FUNCTIONS
-*-n*, *--new*::
-Assign a new cookie to an existing PID or to execute _command_ with a new cookie.
+*get*::
+Retrieve the core scheduling cookie of the PID specified in *-s*.
+If *-s* is omitted, it will get the cookie of the current *{command}* process.
-*-c*, *--copy*::
-Copy the cookie from an existing PID to another PID, or execute _command_ with that copied cookie.
+*new*::
+Assign a new cookie to an existing PID specified in *-d*, or execute _command_ with a new cookie.
-If no function is specified, it will print the cookie of the current task, or of the task specified with the *-p* option.
+*copy*::
+Copy the cookie from an existing PID (*-s*) to another PID (*-d*), or execute _command_ with that cookie.
+If *-s* is omitted, it will get the cookie of the current *{command}* process.
+
+If no function is specified, it will run the *get* function.
== OPTIONS
-*-p*, *--pid* _PID_::
-Operate on an existing PID and do not launch a new task.
+*-s*, *--source* _PID_::
+Which _PID_ to get the cookie from.
+If this option is omitted, it will get the cookie from the current *{command}* process.
*-d*, *--dest* _PID_::
-When using *--copy*, specify a destination PID where you want to copy the cookie to.
+Which _PID_ to modify the cookie of.
-*-t*, *--type* _TYPE_::
+*-t*, *--dest-type* _TYPE_::
The type of the PID whose cookie will be modified. This can be one of three values:
- *pid*, or process ID
- *tgid*, or thread group ID (default value)
@@ -63,25 +75,28 @@ Print version and exit.
== EXAMPLES
Get the core scheduling cookie of the {command} task itself, usually inherited from its parent{colon}::
-*{command}*
+*{command} get*
Get the core scheduling cookie of a task with PID _123_{colon}::
-*{command} -p* _123_
+*{command} get -s* _123_
Give a task with PID _123_ a new core scheduling cookie{colon}::
-*{command} --new -p* _123_
+*{command} new -d* _123_
Spawn a new task with a new core scheduling cookie{colon}::
-*{command} --new* \-- _command_ [_argument_...]
+*{command} new* \-- _command_ [_argument_...]
+
+Copy the cookie from the current {command} process another task with pid _456_{colon}::
+*{command} copy -d* _456_
Copy the cookie from a task with pid _123_ to another task with pid _456_{colon}::
-*{command} --copy -p* _123_ *-d* _456_
+*{command} copy -s* _123_ *-d* _456_
Copy the cookie from a task with pid _123_ to a new task _command_{colon}::
-*{command} --copy -p* _123_ \-- _command_ [_argument_...]
+*{command} copy -s* _123_ \-- _command_ [_argument_...]
Copy the cookie from a task with pid _123_ to the process group ID _456_{colon}::
-*{command} --copy -p* _123_ *-t* pgid *-d* _456_
+*{command} copy -s* _123_ *-t* _pgid_ *-d* _456_
== PERMISSIONS
When retrieving or modifying the core scheduling cookie of a process, you need to have *PTRACE_MODE_READ_REALCREDS* ptrace access to that process.
@@ -98,12 +113,11 @@ If a _command_ is being executed, the return value of *{command}* will be the re
This can be enabled via the *CONFIG_SCHED_CORE* kernel config option.
== AUTHORS
-mailto:thijs@raymakers.nl[Thijs Raymakers],
-mailto:pauld@redhat.com[Phil Auld]
+mailto:thijs@raymakers.nl[Thijs Raymakers]
== COPYRIGHT
-Copyright {copyright} 2024 Thijs Raymakers and Phil Auld. This is free software licensed under the EUPL.
+Copyright {copyright} 2024 Thijs Raymakers. This is free software licensed under the EUPL.
== SEE ALSO
*chrt*(1),
diff --git a/schedutils/coresched.c b/schedutils/coresched.c
index b6be1717d..7e3cc46d2 100644
--- a/schedutils/coresched.c
+++ b/schedutils/coresched.c
@@ -3,7 +3,7 @@
*
* coresched.c - manage core scheduling cookies for tasks
*
- * Copyright (C) 2024 Thijs Raymakers, Phil Auld
+ * Copyright (C) 2024 Thijs Raymakers
* Licensed under the EUPL v1.2
*/
@@ -16,34 +16,33 @@
#include "c.h"
#include "closestream.h"
#include "nls.h"
-#include "optutils.h"
#include "strutils.h"
// These definitions might not be defined in the header files, even if the
// prctl interface in the kernel accepts them as valid.
#ifndef PR_SCHED_CORE
-#define PR_SCHED_CORE 62
+ #define PR_SCHED_CORE 62
#endif
#ifndef PR_SCHED_CORE_GET
-#define PR_SCHED_CORE_GET 0
+ #define PR_SCHED_CORE_GET 0
#endif
#ifndef PR_SCHED_CORE_CREATE
-#define PR_SCHED_CORE_CREATE 1
+ #define PR_SCHED_CORE_CREATE 1
#endif
#ifndef PR_SCHED_CORE_SHARE_TO
-#define PR_SCHED_CORE_SHARE_TO 2
+ #define PR_SCHED_CORE_SHARE_TO 2
#endif
#ifndef PR_SCHED_CORE_SHARE_FROM
-#define PR_SCHED_CORE_SHARE_FROM 3
+ #define PR_SCHED_CORE_SHARE_FROM 3
#endif
#ifndef PR_SCHED_CORE_SCOPE_THREAD
-#define PR_SCHED_CORE_SCOPE_THREAD 0
+ #define PR_SCHED_CORE_SCOPE_THREAD 0
#endif
#ifndef PR_SCHED_CORE_SCOPE_THREAD_GROUP
-#define PR_SCHED_CORE_SCOPE_THREAD_GROUP 1
+ #define PR_SCHED_CORE_SCOPE_THREAD_GROUP 1
#endif
#ifndef PR_SCHED_CORE_SCOPE_PROCESS_GROUP
-#define PR_SCHED_CORE_SCOPE_PROCESS_GROUP 2
+ #define PR_SCHED_CORE_SCOPE_PROCESS_GROUP 2
#endif
typedef int sched_core_scope;
@@ -55,7 +54,7 @@ typedef enum {
} sched_core_cmd;
struct args {
- pid_t pid;
+ pid_t src;
pid_t dest;
sched_core_scope type;
sched_core_cmd cmd;
@@ -67,40 +66,44 @@ static bool sched_core_verbose = false;
static void __attribute__((__noreturn__)) usage(void)
{
fputs(USAGE_HEADER, stdout);
- fprintf(stdout, _(" %s [-p PID]\n"), program_invocation_short_name);
- fprintf(stdout, _(" %s --new [-t <TYPE>] -p <PID>\n"),
+ fprintf(stdout, _(" %s [get] [--source <PID>]\n"),
program_invocation_short_name);
- fprintf(stdout, _(" %s --new [-t <TYPE>] -- PROGRAM [ARGS...]\n"),
+ fprintf(stdout, _(" %s new [-t <TYPE>] --dest <PID>\n"),
program_invocation_short_name);
- fprintf(stdout, _(" %s --copy -p <PID> [-t <TYPE>] -d <PID>\n"),
+ fprintf(stdout, _(" %s new [-t <TYPE>] -- PROGRAM [ARGS...]\n"),
program_invocation_short_name);
fprintf(stdout,
- _(" %s --copy -p <PID> [-t <TYPE>] -- PROGRAM [ARGS...]\n"),
+ _(" %s copy [--source <PID>] [-t <TYPE>] --dest <PID>\n"),
+ program_invocation_short_name);
+ fprintf(stdout,
+ _(" %s copy [--source <PID>] [-t <TYPE>] -- PROGRAM [ARGS...]\n"),
program_invocation_short_name);
fputs(USAGE_SEPARATOR, stdout);
fputsln(_("Manage core scheduling cookies for tasks."), stdout);
fputs(USAGE_FUNCTIONS, stdout);
- fputsln(_(" -n, --new assign a new core scheduling cookie to an existing PID or\n"
- " execute a program with a new cookie."),
+ fputsln(_(" get retrieve the core scheduling cookie of a PID"),
stdout);
- fputsln(_(" -c, --copy copy the core scheduling cookie from an existing PID to\n"
- " either another PID, or copy it to a new program"),
+ fputsln(_(" new assign a new core scheduling cookie to an existing\n"
+ " PID or execute a program with a new cookie"),
stdout);
- fputsln(_("\n If no function is provided, it will retrieve and print the cookie from\n"
- " the PID provided via --pid.\n"),
+ fputsln(_(" copy copy the core scheduling cookie from an existing PID\n"
+ " to another PID, or execute a program with that\n"
+ " copied cookie"),
stdout);
fputs(USAGE_OPTIONS, stdout);
- fputsln(_(" -p, --pid <PID> operate on an existing PID"), stdout);
- fputsln(_(" -d, --dest <PID> when copying a cookie from an existing PID, --dest is\n"
- " the destination PID where to copy the cookie to."),
+ fprintf(stdout,
+ _(" -s, --source <PID> which PID to get the cookie from\n"
+ " If omitted, it is the PID of %s itself\n"),
+ program_invocation_short_name);
+ fputsln(_(" -d, --dest <PID> which PID to modify the cookie of\n"),
stdout);
- fputsln(_(" -t, --type <TYPE> type of the destination PID, or the type of the PID\n"
- " when a new core scheduling cookie is created.\n"
- " Can be one of the following: pid, tgid or pgid.\n"
- " The default is tgid."),
+ fputsln(_(" -t, --dest-type <TYPE> type of the destination PID, or the type of the PID\n"
+ " when a new core scheduling cookie is created.\n"
+ " Can be one of the following: pid, tgid or pgid.\n"
+ " The default is tgid."),
stdout);
fputs(USAGE_SEPARATOR, stdout);
fputsln(_(" -v, --verbose verbose"), stdout);
@@ -151,9 +154,8 @@ static void core_sched_copy_cookie(pid_t from, pid_t to,
if (sched_core_verbose) {
sched_core_cookie before = core_sched_get_cookie(from);
- fprintf(stderr,
- _("%s: copied cookie 0x%lx from PID %d to PID %d\n"),
- program_invocation_short_name, before, from, to);
+ warnx(_("copied cookie 0x%lx from PID %d to PID %d"), before,
+ from, to);
}
}
@@ -161,25 +163,21 @@ static void core_sched_get_and_print_cookie(pid_t pid)
{
if (sched_core_verbose) {
sched_core_cookie after = core_sched_get_cookie(pid);
- fprintf(stderr, _("%s: set cookie of PID %d to 0x%lx\n"),
- program_invocation_short_name, pid, after);
+ warnx(_("set cookie of PID %d to 0x%lx"), pid, after);
}
}
static 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->pid) {
- core_sched_pull_cookie(args->pid);
- core_sched_get_and_print_cookie(args->pid);
+ if (args->src) {
+ core_sched_pull_cookie(args->src);
+ core_sched_get_and_print_cookie(args->src);
} else {
pid_t pid = getpid();
core_sched_create_cookie(pid, args->type);
@@ -218,42 +216,26 @@ static sched_core_scope parse_core_sched_type(char *str)
str);
}
-static void parse_arguments(int argc, char **argv, struct args *args)
+static void parse_and_verify_arguments(int argc, char **argv, struct args *args)
{
int c;
static const struct option longopts[] = {
- { "new", no_argument, NULL, 'n' },
- { "copy", no_argument, NULL, 'c' },
- { "pid", required_argument, NULL, 'p' },
+ { "source", required_argument, NULL, 's' },
{ "dest", required_argument, NULL, 'd' },
- { "type", required_argument, NULL, 't' },
+ { "dest-type", required_argument, NULL, 't' },
{ "verbose", no_argument, NULL, 'v' },
{ "version", no_argument, NULL, 'V' },
{ "help", no_argument, NULL, 'h' },
{ NULL, 0, NULL, 0 }
};
- static const ul_excl_t excl[] = {
- { 'c', 'n' }, // Cannot do both --new and --copy
- { 'd', 'n' }, // Cannot have both --new and --dest
- { 0 }
- };
-
- int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
- while ((c = getopt_long(argc, argv, "ncp:d:t:vVh", longopts, NULL)) !=
- -1) {
- err_exclusive_options(c, longopts, excl, excl_st);
+ while ((c = getopt_long(argc, argv, "s:d:t:vVh", longopts, NULL)) != -1)
switch (c) {
- case 'n':
- args->cmd = SCHED_CORE_CMD_NEW;
- break;
- case 'c':
- args->cmd = SCHED_CORE_CMD_COPY;
- break;
- case 'p':
- args->pid = strtopid_or_err(
- optarg, _("Failed to parse PID for -p/--pid"));
+ case 's':
+ args->src = strtopid_or_err(
+ optarg,
+ _("Failed to parse PID for -s/--source"));
break;
case 'd':
args->dest = strtopid_or_err(
@@ -272,58 +254,77 @@ static void parse_arguments(int argc, char **argv, struct args *args)
default:
errtryhelp(EXIT_FAILURE);
}
+
+ if (argc <= optind) {
+ args->cmd = SCHED_CORE_CMD_GET;
+ } else {
+ if (!strcmp(argv[optind], "get"))
+ args->cmd = SCHED_CORE_CMD_GET;
+ else if (!strcmp(argv[optind], "new"))
+ args->cmd = SCHED_CORE_CMD_NEW;
+ else if (!strcmp(argv[optind], "copy"))
+ args->cmd = SCHED_CORE_CMD_COPY;
+ else
+ bad_usage(_("Unknown function"));
+
+ // Since we parsed an extra "option" outside of getopt_long, we have to
+ // increment optind manually.
+ ++optind;
}
- if (args->cmd == SCHED_CORE_CMD_COPY && !args->pid)
- bad_usage(_("--copy: requires a -p/--pid"));
+ if (args->cmd == SCHED_CORE_CMD_GET && args->dest)
+ bad_usage(_("get does not accept the --dest option"));
+
+ if (args->cmd == SCHED_CORE_CMD_NEW && args->src)
+ bad_usage(_("new does not accept the --source option"));
+
+ // If the -s/--source option is not specified, it defaults to the PID
+ // of the current coresched process
+ if (args->cmd != SCHED_CORE_CMD_NEW && !args->src)
+ args->src = getpid();
// More arguments have been passed, which means that the user wants to run
// another program with a core scheduling cookie.
if (argc > optind) {
switch (args->cmd) {
case SCHED_CORE_CMD_GET:
- bad_usage(_("Unknown command"));
+ bad_usage(_("bad usage of the get function"));
break;
case SCHED_CORE_CMD_NEW:
- if (args->pid)
+ if (args->dest)
bad_usage(_(
- "--new: cannot accept both a -p/--pid and a command"));
+ "new requires either a -d/--dest or a command"));
else
args->exec_argv_offset = optind;
break;
case SCHED_CORE_CMD_COPY:
if (args->dest)
bad_usage(_(
- "--copy: cannot accept both a destination PID "
- "-d/--dest and a command"));
+ "copy requires either a -d/--dest or a command"));
else
args->exec_argv_offset = optind;
break;
}
- }
-
- if (argc <= optind) {
- if (args->cmd == SCHED_CORE_CMD_NEW && !args->pid)
+ } else {
+ if (args->cmd == SCHED_CORE_CMD_NEW && !args->dest)
bad_usage(_(
- "--new: requires either a -p/--pid or a command"));
+ "new requires either a -d/--dest or a command"));
if (args->cmd == SCHED_CORE_CMD_COPY && !args->dest)
bad_usage(_(
- "--copy: requires either a -d/--dest or a command"));
+ "copy requires either a -d/--dest or a command"));
}
}
int main(int argc, char **argv)
{
- struct args args = { 0 };
- args.cmd = SCHED_CORE_CMD_GET;
- args.type = PR_SCHED_CORE_SCOPE_THREAD_GROUP;
+ struct args args = { .type = PR_SCHED_CORE_SCOPE_THREAD_GROUP };
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
close_stdout_atexit();
- parse_arguments(argc, argv, &args);
+ parse_and_verify_arguments(argc, argv, &args);
if (!is_core_sched_supported())
errx(EXIT_FAILURE,
@@ -331,27 +332,25 @@ int main(int argc, char **argv)
"support CONFIG_SCHED_CORE?"));
sched_core_cookie cookie;
- pid_t pid;
switch (args.cmd) {
case SCHED_CORE_CMD_GET:
- pid = args.pid ? args.pid : getpid();
- cookie = core_sched_get_cookie(pid);
- printf(_("cookie of pid %d is 0x%lx\n"), pid, cookie);
+ cookie = core_sched_get_cookie(args.src);
+ printf(_("cookie of pid %d is 0x%lx\n"), args.src, cookie);
break;
case SCHED_CORE_CMD_NEW:
- if (args.pid) {
- core_sched_create_cookie(args.pid, args.type);
- core_sched_get_and_print_cookie(args.pid);
- } else {
+ if (args.exec_argv_offset) {
core_sched_exec_with_cookie(&args, argv);
+ } else {
+ core_sched_create_cookie(args.dest, args.type);
+ core_sched_get_and_print_cookie(args.dest);
}
break;
case SCHED_CORE_CMD_COPY:
- if (args.dest)
- core_sched_copy_cookie(args.pid, args.dest, args.type);
- else
+ if (args.exec_argv_offset)
core_sched_exec_with_cookie(&args, argv);
+ else
+ core_sched_copy_cookie(args.src, args.dest, args.type);
break;
default:
usage();
diff --git a/tests/expected/schedutils/coresched-change-cookie-of-parent b/tests/expected/schedutils/coresched-change-cookie-of-parent
deleted file mode 100644
index d08118c02..000000000
--- a/tests/expected/schedutils/coresched-change-cookie-of-parent
+++ /dev/null
@@ -1 +0,0 @@
-cookie of pid OWN_PID is DIFFERENT_COOKIE
diff --git a/tests/expected/schedutils/coresched-spawn-child-with-new-cookie b/tests/expected/schedutils/coresched-copy-from-child-to-parent
similarity index 100%
rename from tests/expected/schedutils/coresched-spawn-child-with-new-cookie
rename to tests/expected/schedutils/coresched-copy-from-child-to-parent
diff --git a/tests/expected/schedutils/coresched-copy-from-parent-to-nested-child b/tests/expected/schedutils/coresched-copy-from-parent-to-nested-child
new file mode 100644
index 000000000..ecfc41142
--- /dev/null
+++ b/tests/expected/schedutils/coresched-copy-from-parent-to-nested-child
@@ -0,0 +1 @@
+SAME_COOKIE
diff --git a/tests/expected/schedutils/coresched-get-cookie-own-pid b/tests/expected/schedutils/coresched-get-cookie-own-pid
new file mode 100644
index 000000000..84f182cbe
--- /dev/null
+++ b/tests/expected/schedutils/coresched-get-cookie-own-pid
@@ -0,0 +1 @@
+cookie of pid OWN_PID is PARENT_COOKIE
diff --git a/tests/expected/schedutils/coresched-get-cookie-parent-pid b/tests/expected/schedutils/coresched-get-cookie-parent-pid
new file mode 100644
index 000000000..e183e0402
--- /dev/null
+++ b/tests/expected/schedutils/coresched-get-cookie-parent-pid
@@ -0,0 +1 @@
+cookie of pid PARENT_PID is PARENT_COOKIE
diff --git a/tests/expected/schedutils/coresched-get-own-pid-with-cookie b/tests/expected/schedutils/coresched-get-own-pid-with-cookie
deleted file mode 100644
index 3d7f08040..000000000
--- a/tests/expected/schedutils/coresched-get-own-pid-with-cookie
+++ /dev/null
@@ -1 +0,0 @@
-cookie of pid OWN_PID is COOKIE
diff --git a/tests/expected/schedutils/coresched-new-child-with-new-cookie b/tests/expected/schedutils/coresched-new-child-with-new-cookie
new file mode 100644
index 000000000..5b9c40052
--- /dev/null
+++ b/tests/expected/schedutils/coresched-new-child-with-new-cookie
@@ -0,0 +1 @@
+DIFFERENT_COOKIE
diff --git a/tests/expected/schedutils/coresched-set-cookie-own-pid.err b/tests/expected/schedutils/coresched-set-cookie-own-pid.err
deleted file mode 100644
index 1816a4fbd..000000000
--- a/tests/expected/schedutils/coresched-set-cookie-own-pid.err
+++ /dev/null
@@ -1 +0,0 @@
-coresched: set cookie of PID OWN_PID to COOKIE
diff --git a/tests/expected/schedutils/coresched-set-cookie-parent-pid.err b/tests/expected/schedutils/coresched-set-cookie-parent-pid.err
new file mode 100644
index 000000000..e7318ffc2
--- /dev/null
+++ b/tests/expected/schedutils/coresched-set-cookie-parent-pid.err
@@ -0,0 +1 @@
+coresched: set cookie of PID PARENT_PID to PARENT_COOKIE
diff --git a/tests/expected/schedutils/set-cookie-parent-pid b/tests/expected/schedutils/set-cookie-parent-pid
new file mode 100644
index 000000000..e7318ffc2
--- /dev/null
+++ b/tests/expected/schedutils/set-cookie-parent-pid
@@ -0,0 +1 @@
+coresched: set cookie of PID PARENT_PID to PARENT_COOKIE
diff --git a/tests/ts/schedutils/coresched b/tests/ts/schedutils/coresched
index ed96d89de..e34fa319f 100755
--- a/tests/ts/schedutils/coresched
+++ b/tests/ts/schedutils/coresched
@@ -13,8 +13,8 @@ TS_DESC="coresched"
ts_init "$*"
ts_check_test_command "$TS_CMD_CORESCHED"
-ts_check_test_command "tee"
-ts_check_test_command "sed"
+ts_check_prog "tee"
+ts_check_prog "sed"
# If there is no kernel support, skip the test suite
CORESCHED_TEST_KERNEL_SUPPORT_CMD=$($TS_CMD_CORESCHED 2>&1)
@@ -30,33 +30,54 @@ fi
# - Any other cookie is replaced by `DIFFERENT_COOKIE`
# The behavior of coresched does not depend on the exact values of these cookies, so using
# placeholder values does not change the behavior tests.
-ts_init_subtest "set-cookie-own-pid"
-CORESCHED_OUTPUT=$( ($TS_CMD_CORESCHED -v -n -p $$ | tee -a "$TS_OUTPUT") 3>&1 1>&2 2>&3 | sed "s/$$/OWN_PID/g")
-CORESCHED_COOKIE=$(echo "$CORESCHED_OUTPUT" | sed 's/^.*\(0x.*$\)/\1/g')
-if [ -z "$CORESCHED_COOKIE" ]; then
- ts_failed "empty value for CORESCHED_COOKIE "
+ts_init_subtest "set-cookie-parent-pid"
+CORESCHED_OUTPUT=$( ($TS_CMD_CORESCHED -v new -d $$ \
+ | tee -a "$TS_OUTPUT") 3>&1 1>&2 2>&3 \
+ | sed "s/$$/PARENT_PID/g")
+CORESCHED_PARENT_COOKIE=$(echo "$CORESCHED_OUTPUT" | sed 's/^.*\(0x.*$\)/\1/g')
+if [ -z "$CORESCHED_PARENT_COOKIE" ]; then
+ ts_failed "empty value for CORESCHED_PARENT_COOKIE"
fi
-CORESCHED_OUTPUT=$(echo "$CORESCHED_OUTPUT" | sed "s/$CORESCHED_COOKIE/COOKIE/g")
+CORESCHED_OUTPUT=$(echo "$CORESCHED_OUTPUT" \
+ | sed "s/$CORESCHED_PARENT_COOKIE/PARENT_COOKIE/g")
echo "$CORESCHED_OUTPUT" >> "$TS_ERRLOG"
ts_finalize_subtest
-ts_init_subtest "get-own-pid-with-cookie"
-$TS_CMD_CORESCHED -p $$ 2>> "$TS_ERRLOG" | sed "s/$$/OWN_PID/g" | sed "s/$CORESCHED_COOKIE/COOKIE/g" >> "$TS_OUTPUT"
+ts_init_subtest "get-cookie-parent-pid"
+$TS_CMD_CORESCHED get -s $$ 2>> "$TS_ERRLOG" \
+ | sed -e "s/$$/PARENT_PID/g" \
+ -e "s/$CORESCHED_PARENT_COOKIE/PARENT_COOKIE/g" >> "$TS_OUTPUT"
ts_finalize_subtest
-ts_init_subtest "spawn-child-with-new-cookie"
-$TS_CMD_CORESCHED -n -- "$TS_CMD_CORESCHED" 2>> "$TS_ERRLOG" \
- | sed 's/^.*\(0x.*$\)/\1/g' \
- | sed "s/$CORESCHED_COOKIE/SAME_COOKIE/g" \
- | sed "s/0x.*$/DIFFERENT_COOKIE/g" >> "$TS_OUTPUT"
+ts_init_subtest "get-cookie-own-pid"
+$TS_CMD_CORESCHED get 2>> "$TS_ERRLOG" \
+ | sed -e "s/pid [0-9]\+/pid OWN_PID/g" \
+ -e "s/$CORESCHED_PARENT_COOKIE/PARENT_COOKIE/g" >> "$TS_OUTPUT"
ts_finalize_subtest
-ts_init_subtest "change-cookie-of-parent"
-$TS_CMD_CORESCHED -n -- /bin/bash -c "$TS_CMD_CORESCHED -c -p \$\$ -d $$"
-$TS_CMD_CORESCHED -p $$ 2>> "$TS_ERRLOG" \
- | sed "s/$$/OWN_PID/g" \
- | sed "s/$CORESCHED_COOKIE/COOKIE/g" \
- | sed "s/0x.*$/DIFFERENT_COOKIE/g" >> "$TS_OUTPUT"
+ts_init_subtest "new-child-with-new-cookie"
+$TS_CMD_CORESCHED new -- "$TS_CMD_CORESCHED" get 2>> "$TS_ERRLOG" \
+ | sed -e 's/^.*\(0x.*$\)/\1/g' \
+ -e "s/$CORESCHED_PARENT_COOKIE/SAME_COOKIE/g" \
+ -e "s/0x.*$/DIFFERENT_COOKIE/g" >> "$TS_OUTPUT"
+ts_finalize_subtest
+
+ts_init_subtest "copy-from-parent-to-nested-child"
+$TS_CMD_CORESCHED new -- /bin/bash -c \
+ "$TS_CMD_CORESCHED copy -s $$ -- $TS_CMD_CORESCHED get" \
+2>> "$TS_ERRLOG" \
+ | sed -e 's/^.*\(0x.*$\)/\1/g' \
+ -e "s/$CORESCHED_PARENT_COOKIE/SAME_COOKIE/g" \
+ -e "s/0x.*$/DIFFERENT_COOKIE/g" >> "$TS_OUTPUT"
+ts_finalize_subtest
+
+ts_init_subtest "copy-from-child-to-parent"
+$TS_CMD_CORESCHED new -- /bin/bash -c \
+ "$TS_CMD_CORESCHED copy -s \$\$ -d $$"
+$TS_CMD_CORESCHED get 2>> "$TS_ERRLOG" \
+ | sed -e 's/^.*\(0x.*$\)/\1/g' \
+ -e "s/$CORESCHED_PARENT_COOKIE/SAME_COOKIE/g" \
+ -e "s/0x.*$/DIFFERENT_COOKIE/g" >> "$TS_OUTPUT"
ts_finalize_subtest
ts_finalize
.gitignore | 1 +
bash-completion/coresched | 0
configure.ac | 12 +-
meson.build | 16 +-
meson_options.txt | 2 +-
schedutils/Makemodule.am | 8 +
schedutils/coresched.1.adoc | 138 +++++++
schedutils/coresched.c | 358 ++++++++++++++++++
tests/commands.sh | 1 +
.../coresched-copy-from-child-to-parent | 1 +
...coresched-copy-from-parent-to-nested-child | 1 +
.../schedutils/coresched-get-cookie-own-pid | 1 +
.../coresched-get-cookie-parent-pid | 1 +
.../coresched-new-child-with-new-cookie | 1 +
.../coresched-set-cookie-parent-pid.err | 1 +
.../expected/schedutils/set-cookie-parent-pid | 1 +
tests/ts/schedutils/coresched | 83 ++++
17 files changed, 620 insertions(+), 6 deletions(-)
create mode 100644 bash-completion/coresched
create mode 100644 schedutils/coresched.1.adoc
create mode 100644 schedutils/coresched.c
create mode 100644 tests/expected/schedutils/coresched-copy-from-child-to-parent
create mode 100644 tests/expected/schedutils/coresched-copy-from-parent-to-nested-child
create mode 100644 tests/expected/schedutils/coresched-get-cookie-own-pid
create mode 100644 tests/expected/schedutils/coresched-get-cookie-parent-pid
create mode 100644 tests/expected/schedutils/coresched-new-child-with-new-cookie
create mode 100644 tests/expected/schedutils/coresched-set-cookie-parent-pid.err
create mode 100644 tests/expected/schedutils/set-cookie-parent-pid
create mode 100755 tests/ts/schedutils/coresched
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..a3a648753 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..00f2603e0
--- /dev/null
+++ b/schedutils/coresched.1.adoc
@@ -0,0 +1,138 @@
+//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
+
+== SYNOPSIS
+
+*{command}* [*get*] [*-s* _pid_]
+
+*{command}* *new* [*-t* _type_] *-d* _pid_
+
+*{command}* *new* [*-t* _type_] \-- _command_ [_argument_...]
+
+*{command}* *copy* [*-s* _pid_] [*-t* _type_] *-d* _pid_
+
+*{command}* *copy* [*-s* _pid_] [*-t* _type_] \-- _command_ [_argument_...]
+
+== DESCRIPTION
+The *{command}* command is used to retrieve or modify the core scheduling cookies of a running process given its _pid_, or to spawn a new _command_ with core scheduling cookies.
+
+Core scheduling allows you to define groups of tasks that are allowed to share a physical core.
+This is done by assigning a cookie to each task.
+Only tasks have the same cookie are allowed to be scheduled on the same physical core.
+
+It is possible to either assign a new random cookie to a task, or copy a cookie from another task. It is not possible to choose the value of the cookie.
+
+== FUNCTIONS
+*get*::
+Retrieve the core scheduling cookie of the PID specified in *-s*.
+If *-s* is omitted, it will get the cookie of the current *{command}* process.
+
+*new*::
+Assign a new cookie to an existing PID specified in *-d*, or execute _command_ with a new cookie.
+
+*copy*::
+Copy the cookie from an existing PID (*-s*) to another PID (*-d*), or execute _command_ with that cookie.
+If *-s* is omitted, it will get the cookie of the current *{command}* process.
+
+If no function is specified, it will run the *get* function.
+
+== OPTIONS
+*-s*, *--source* _PID_::
+Which _PID_ to get the cookie from.
+If this option is omitted, it will get the cookie from the current *{command}* process.
+
+*-d*, *--dest* _PID_::
+Which _PID_ to modify the cookie of.
+
+*-t*, *--dest-type* _TYPE_::
+The type of the PID whose cookie will be modified. This can be one of three values:
+- *pid*, or process ID
+- *tgid*, or thread group ID (default value)
+- *pgid*, or process group ID
+
+*-v*, *--verbose*::
+Show extra information when modifying cookies of tasks.
+
+*-h*, *--help*::
+Display help text and exit.
+
+*-V*, *--version*::
+Print version and exit.
+
+== EXAMPLES
+Get the core scheduling cookie of the {command} task itself, usually inherited from its parent{colon}::
+*{command} get*
+
+Get the core scheduling cookie of a task with PID _123_{colon}::
+*{command} get -s* _123_
+
+Give a task with PID _123_ a new core scheduling cookie{colon}::
+*{command} new -d* _123_
+
+Spawn a new task with a new core scheduling cookie{colon}::
+*{command} new* \-- _command_ [_argument_...]
+
+Copy the cookie from the current {command} process another task with pid _456_{colon}::
+*{command} copy -d* _456_
+
+Copy the cookie from a task with pid _123_ to another task with pid _456_{colon}::
+*{command} copy -s* _123_ *-d* _456_
+
+Copy the cookie from a task with pid _123_ to a new task _command_{colon}::
+*{command} copy -s* _123_ \-- _command_ [_argument_...]
+
+Copy the cookie from a task with pid _123_ to the process group ID _456_{colon}::
+*{command} copy -s* _123_ *-t* _pgid_ *-d* _456_
+
+== PERMISSIONS
+When retrieving or modifying the core scheduling cookie of a process, you need to have *PTRACE_MODE_READ_REALCREDS* ptrace access to that process.
+See the section "Ptrace access mode checking" in *ptrace*(2) for more information.
+
+== RETURN VALUE
+On success, *{command}* returns 0.
+If *{command}* fails, it will print an error and return 1.
+
+If a _command_ is being executed, the return value of *{command}* will be the return value of _command_.
+
+== NOTES
+*{command}* requires core scheduling support in the kernel.
+This can be enabled via the *CONFIG_SCHED_CORE* kernel config option.
+
+== AUTHORS
+mailto:thijs@raymakers.nl[Thijs Raymakers]
+
+== COPYRIGHT
+
+Copyright {copyright} 2024 Thijs Raymakers. This is free software licensed under the EUPL.
+
+== SEE ALSO
+*chrt*(1),
+*nice*(1),
+*renice*(1),
+*taskset*(1),
+*ptrace*(2),
+*sched*(7)
+
+The Linux kernel source files _Documentation/admin-guide/hw-vuln/core-scheduling.rst_
+
+include::man-common/bugreports.adoc[]
+
+include::man-common/footer.adoc[]
+
+ifdef::translation[]
+include::man-common/translation.adoc[]
+endif::[]
diff --git a/schedutils/coresched.c b/schedutils/coresched.c
new file mode 100644
index 000000000..7e3cc46d2
--- /dev/null
+++ b/schedutils/coresched.c
@@ -0,0 +1,358 @@
+/**
+ * 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 <unistd.h>
+
+#include "c.h"
+#include "closestream.h"
+#include "nls.h"
+#include "strutils.h"
+
+// These definitions might not be defined in the header files, even if the
+// prctl interface in the kernel accepts them as valid.
+#ifndef PR_SCHED_CORE
+ #define PR_SCHED_CORE 62
+#endif
+#ifndef PR_SCHED_CORE_GET
+ #define PR_SCHED_CORE_GET 0
+#endif
+#ifndef PR_SCHED_CORE_CREATE
+ #define PR_SCHED_CORE_CREATE 1
+#endif
+#ifndef PR_SCHED_CORE_SHARE_TO
+ #define PR_SCHED_CORE_SHARE_TO 2
+#endif
+#ifndef PR_SCHED_CORE_SHARE_FROM
+ #define PR_SCHED_CORE_SHARE_FROM 3
+#endif
+#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 sched_core_scope;
+typedef unsigned long sched_core_cookie;
+typedef enum {
+ SCHED_CORE_CMD_GET,
+ SCHED_CORE_CMD_NEW,
+ SCHED_CORE_CMD_COPY,
+} sched_core_cmd;
+
+struct args {
+ pid_t src;
+ pid_t dest;
+ sched_core_scope type;
+ sched_core_cmd cmd;
+ int exec_argv_offset;
+};
+
+static bool sched_core_verbose = false;
+
+static void __attribute__((__noreturn__)) usage(void)
+{
+ fputs(USAGE_HEADER, stdout);
+ fprintf(stdout, _(" %s [get] [--source <PID>]\n"),
+ program_invocation_short_name);
+ fprintf(stdout, _(" %s new [-t <TYPE>] --dest <PID>\n"),
+ program_invocation_short_name);
+ fprintf(stdout, _(" %s new [-t <TYPE>] -- PROGRAM [ARGS...]\n"),
+ program_invocation_short_name);
+ fprintf(stdout,
+ _(" %s copy [--source <PID>] [-t <TYPE>] --dest <PID>\n"),
+ program_invocation_short_name);
+ fprintf(stdout,
+ _(" %s copy [--source <PID>] [-t <TYPE>] -- PROGRAM [ARGS...]\n"),
+ program_invocation_short_name);
+
+ fputs(USAGE_SEPARATOR, stdout);
+ fputsln(_("Manage core scheduling cookies for tasks."), stdout);
+
+ fputs(USAGE_FUNCTIONS, stdout);
+ fputsln(_(" get retrieve the core scheduling cookie of a PID"),
+ stdout);
+ fputsln(_(" new assign a new core scheduling cookie to an existing\n"
+ " PID or execute a program with a new cookie"),
+ stdout);
+ fputsln(_(" copy copy the core scheduling cookie from an existing PID\n"
+ " to another PID, or execute a program with that\n"
+ " copied cookie"),
+ stdout);
+
+ fputs(USAGE_OPTIONS, stdout);
+ fprintf(stdout,
+ _(" -s, --source <PID> which PID to get the cookie from\n"
+ " If omitted, it is the PID of %s itself\n"),
+ program_invocation_short_name);
+ fputsln(_(" -d, --dest <PID> which PID to modify the cookie of\n"),
+ stdout);
+ fputsln(_(" -t, --dest-type <TYPE> type of the destination PID, or the type of the PID\n"
+ " when a new core scheduling cookie is created.\n"
+ " Can be one of the following: pid, tgid or pgid.\n"
+ " The default is tgid."),
+ stdout);
+ fputs(USAGE_SEPARATOR, stdout);
+ fputsln(_(" -v, --verbose verbose"), stdout);
+ fprintf(stdout, USAGE_HELP_OPTIONS(20));
+ fprintf(stdout, USAGE_MAN_TAIL("coresched(1)"));
+ exit(EXIT_SUCCESS);
+}
+
+#define bad_usage(FMT...) \
+ do { \
+ warnx(FMT); \
+ errtryhelp(EXIT_FAILURE); \
+ } while (0)
+
+static sched_core_cookie core_sched_get_cookie(pid_t pid)
+{
+ sched_core_cookie cookie = 0;
+ if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_GET, pid,
+ PR_SCHED_CORE_SCOPE_THREAD, &cookie))
+ err(EXIT_FAILURE, _("Failed to get cookie from PID %d"), pid);
+ return cookie;
+}
+
+static void core_sched_create_cookie(pid_t pid, sched_core_scope type)
+{
+ if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_CREATE, pid, type, 0))
+ err(EXIT_FAILURE, _("Failed to create cookie for PID %d"), pid);
+}
+
+static 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(EXIT_FAILURE, _("Failed to pull cookie from PID %d"), from);
+}
+
+static void core_sched_push_cookie(pid_t to, sched_core_scope type)
+{
+ if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_SHARE_TO, to, type, 0))
+ err(EXIT_FAILURE, _("Failed to push cookie to PID %d"), to);
+}
+
+static void core_sched_copy_cookie(pid_t from, pid_t to,
+ sched_core_scope to_type)
+{
+ core_sched_pull_cookie(from);
+ core_sched_push_cookie(to, to_type);
+
+ if (sched_core_verbose) {
+ sched_core_cookie before = core_sched_get_cookie(from);
+ warnx(_("copied cookie 0x%lx from PID %d to PID %d"), before,
+ from, to);
+ }
+}
+
+static void core_sched_get_and_print_cookie(pid_t pid)
+{
+ if (sched_core_verbose) {
+ sched_core_cookie after = core_sched_get_cookie(pid);
+ warnx(_("set cookie of PID %d to 0x%lx"), pid, after);
+ }
+}
+
+static void core_sched_exec_with_cookie(struct args *args, char **argv)
+{
+ // 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->src) {
+ core_sched_pull_cookie(args->src);
+ core_sched_get_and_print_cookie(args->src);
+ } else {
+ pid_t pid = getpid();
+ core_sched_create_cookie(pid, args->type);
+ core_sched_get_and_print_cookie(pid);
+ }
+
+ if (execvp(argv[0], argv))
+ errexec(argv[0]);
+}
+
+// If PR_SCHED_CORE is not recognized, or not supported on this system,
+// then prctl will set errno to EINVAL. Assuming all other operands of
+// prctl are valid, we can use errno==EINVAL as a check to see whether
+// core scheduling is available on this system.
+static bool is_core_sched_supported(void)
+{
+ sched_core_cookie cookie = 0;
+ if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_GET, getpid(),
+ PR_SCHED_CORE_SCOPE_THREAD, &cookie))
+ if (errno == EINVAL)
+ return false;
+
+ return true;
+}
+
+static sched_core_scope parse_core_sched_type(char *str)
+{
+ if (!strcmp(str, "pid"))
+ return PR_SCHED_CORE_SCOPE_THREAD;
+ else if (!strcmp(str, "tgid"))
+ return PR_SCHED_CORE_SCOPE_THREAD_GROUP;
+ else if (!strcmp(str, "pgid"))
+ return PR_SCHED_CORE_SCOPE_PROCESS_GROUP;
+
+ bad_usage(_("'%s' is an invalid option. Must be one of pid/tgid/pgid"),
+ str);
+}
+
+static void parse_and_verify_arguments(int argc, char **argv, struct args *args)
+{
+ int c;
+
+ static const struct option longopts[] = {
+ { "source", required_argument, NULL, 's' },
+ { "dest", required_argument, NULL, 'd' },
+ { "dest-type", required_argument, NULL, 't' },
+ { "verbose", no_argument, NULL, 'v' },
+ { "version", no_argument, NULL, 'V' },
+ { "help", no_argument, NULL, 'h' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ while ((c = getopt_long(argc, argv, "s:d:t:vVh", longopts, NULL)) != -1)
+ switch (c) {
+ case 's':
+ args->src = strtopid_or_err(
+ optarg,
+ _("Failed to parse PID for -s/--source"));
+ break;
+ case 'd':
+ args->dest = strtopid_or_err(
+ optarg, _("Failed to parse PID for -d/--dest"));
+ break;
+ case 't':
+ args->type = parse_core_sched_type(optarg);
+ break;
+ case 'v':
+ sched_core_verbose = true;
+ break;
+ case 'V':
+ print_version(EXIT_SUCCESS);
+ case 'h':
+ usage();
+ default:
+ errtryhelp(EXIT_FAILURE);
+ }
+
+ if (argc <= optind) {
+ args->cmd = SCHED_CORE_CMD_GET;
+ } else {
+ if (!strcmp(argv[optind], "get"))
+ args->cmd = SCHED_CORE_CMD_GET;
+ else if (!strcmp(argv[optind], "new"))
+ args->cmd = SCHED_CORE_CMD_NEW;
+ else if (!strcmp(argv[optind], "copy"))
+ args->cmd = SCHED_CORE_CMD_COPY;
+ else
+ bad_usage(_("Unknown function"));
+
+ // Since we parsed an extra "option" outside of getopt_long, we have to
+ // increment optind manually.
+ ++optind;
+ }
+
+ if (args->cmd == SCHED_CORE_CMD_GET && args->dest)
+ bad_usage(_("get does not accept the --dest option"));
+
+ if (args->cmd == SCHED_CORE_CMD_NEW && args->src)
+ bad_usage(_("new does not accept the --source option"));
+
+ // If the -s/--source option is not specified, it defaults to the PID
+ // of the current coresched process
+ if (args->cmd != SCHED_CORE_CMD_NEW && !args->src)
+ args->src = getpid();
+
+ // More arguments have been passed, which means that the user wants to run
+ // another program with a core scheduling cookie.
+ if (argc > optind) {
+ switch (args->cmd) {
+ case SCHED_CORE_CMD_GET:
+ bad_usage(_("bad usage of the get function"));
+ break;
+ case SCHED_CORE_CMD_NEW:
+ if (args->dest)
+ bad_usage(_(
+ "new requires either a -d/--dest or a command"));
+ else
+ args->exec_argv_offset = optind;
+ break;
+ case SCHED_CORE_CMD_COPY:
+ if (args->dest)
+ bad_usage(_(
+ "copy requires either a -d/--dest or a command"));
+ else
+ args->exec_argv_offset = optind;
+ break;
+ }
+ } else {
+ if (args->cmd == SCHED_CORE_CMD_NEW && !args->dest)
+ bad_usage(_(
+ "new requires either a -d/--dest or a command"));
+ if (args->cmd == SCHED_CORE_CMD_COPY && !args->dest)
+ bad_usage(_(
+ "copy requires either a -d/--dest or a command"));
+ }
+}
+
+int main(int argc, char **argv)
+{
+ struct args args = { .type = PR_SCHED_CORE_SCOPE_THREAD_GROUP };
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ close_stdout_atexit();
+
+ parse_and_verify_arguments(argc, argv, &args);
+
+ if (!is_core_sched_supported())
+ errx(EXIT_FAILURE,
+ _("No support for core scheduling found. Does your kernel"
+ "support CONFIG_SCHED_CORE?"));
+
+ sched_core_cookie cookie;
+
+ switch (args.cmd) {
+ case SCHED_CORE_CMD_GET:
+ cookie = core_sched_get_cookie(args.src);
+ printf(_("cookie of pid %d is 0x%lx\n"), args.src, cookie);
+ break;
+ case SCHED_CORE_CMD_NEW:
+ if (args.exec_argv_offset) {
+ core_sched_exec_with_cookie(&args, argv);
+ } else {
+ core_sched_create_cookie(args.dest, args.type);
+ core_sched_get_and_print_cookie(args.dest);
+ }
+ break;
+ case SCHED_CORE_CMD_COPY:
+ if (args.exec_argv_offset)
+ core_sched_exec_with_cookie(&args, argv);
+ else
+ core_sched_copy_cookie(args.src, args.dest, args.type);
+ break;
+ default:
+ usage();
+ }
+}
diff --git a/tests/commands.sh b/tests/commands.sh
index 5674c5ff0..9eef92ccb 100644
--- a/tests/commands.sh
+++ b/tests/commands.sh
@@ -71,6 +71,7 @@ TS_CMD_COLCRT=${TS_CMD_COLCRT:-"${ts_commandsdir}colcrt"}
TS_CMD_COLRM=${TS_CMD_COLRM:-"${ts_commandsdir}colrm"}
TS_CMD_COL=${TS_CMD_COL:-"${ts_commandsdir}col"}
TS_CMD_COLUMN=${TS_CMD_COLUMN:-"${ts_commandsdir}column"}
+TS_CMD_CORESCHED=${TS_CMD_CORESCHED:-"${ts_commandsdir}coresched"}
TS_CMD_ENOSYS=${TS_CMD_ENOSYS-"${ts_commandsdir}enosys"}
TS_CMD_EJECT=${TS_CMD_EJECT-"${ts_commandsdir}eject"}
TS_CMD_EXCH=${TS_CMD_EXCH-"${ts_commandsdir}exch"}
diff --git a/tests/expected/schedutils/coresched-copy-from-child-to-parent b/tests/expected/schedutils/coresched-copy-from-child-to-parent
new file mode 100644
index 000000000..5b9c40052
--- /dev/null
+++ b/tests/expected/schedutils/coresched-copy-from-child-to-parent
@@ -0,0 +1 @@
+DIFFERENT_COOKIE
diff --git a/tests/expected/schedutils/coresched-copy-from-parent-to-nested-child b/tests/expected/schedutils/coresched-copy-from-parent-to-nested-child
new file mode 100644
index 000000000..ecfc41142
--- /dev/null
+++ b/tests/expected/schedutils/coresched-copy-from-parent-to-nested-child
@@ -0,0 +1 @@
+SAME_COOKIE
diff --git a/tests/expected/schedutils/coresched-get-cookie-own-pid b/tests/expected/schedutils/coresched-get-cookie-own-pid
new file mode 100644
index 000000000..84f182cbe
--- /dev/null
+++ b/tests/expected/schedutils/coresched-get-cookie-own-pid
@@ -0,0 +1 @@
+cookie of pid OWN_PID is PARENT_COOKIE
diff --git a/tests/expected/schedutils/coresched-get-cookie-parent-pid b/tests/expected/schedutils/coresched-get-cookie-parent-pid
new file mode 100644
index 000000000..e183e0402
--- /dev/null
+++ b/tests/expected/schedutils/coresched-get-cookie-parent-pid
@@ -0,0 +1 @@
+cookie of pid PARENT_PID is PARENT_COOKIE
diff --git a/tests/expected/schedutils/coresched-new-child-with-new-cookie b/tests/expected/schedutils/coresched-new-child-with-new-cookie
new file mode 100644
index 000000000..5b9c40052
--- /dev/null
+++ b/tests/expected/schedutils/coresched-new-child-with-new-cookie
@@ -0,0 +1 @@
+DIFFERENT_COOKIE
diff --git a/tests/expected/schedutils/coresched-set-cookie-parent-pid.err b/tests/expected/schedutils/coresched-set-cookie-parent-pid.err
new file mode 100644
index 000000000..e7318ffc2
--- /dev/null
+++ b/tests/expected/schedutils/coresched-set-cookie-parent-pid.err
@@ -0,0 +1 @@
+coresched: set cookie of PID PARENT_PID to PARENT_COOKIE
diff --git a/tests/expected/schedutils/set-cookie-parent-pid b/tests/expected/schedutils/set-cookie-parent-pid
new file mode 100644
index 000000000..e7318ffc2
--- /dev/null
+++ b/tests/expected/schedutils/set-cookie-parent-pid
@@ -0,0 +1 @@
+coresched: set cookie of PID PARENT_PID to PARENT_COOKIE
diff --git a/tests/ts/schedutils/coresched b/tests/ts/schedutils/coresched
new file mode 100755
index 000000000..e34fa319f
--- /dev/null
+++ b/tests/ts/schedutils/coresched
@@ -0,0 +1,83 @@
+#!/bin/bash
+# SPDX-License-Identifier: EUPL-1.2
+#
+# This file is part of util-linux
+#
+# Copyright (C) 2024 Thijs Raymakers
+# Licensed under the EUPL v1.2
+
+TS_TOPDIR="${0%/*}/../.."
+TS_DESC="coresched"
+
+. "$TS_TOPDIR"/functions.sh
+ts_init "$*"
+
+ts_check_test_command "$TS_CMD_CORESCHED"
+ts_check_prog "tee"
+ts_check_prog "sed"
+
+# If there is no kernel support, skip the test suite
+CORESCHED_TEST_KERNEL_SUPPORT_CMD=$($TS_CMD_CORESCHED 2>&1)
+if [[ $CORESCHED_TEST_KERNEL_SUPPORT_CMD == *"CONFIG_SCHED_CORE"* ]]; then
+ ts_skip "Kernel has no CONFIG_SCHED_CORE support"
+fi
+
+# The output of coresched contains PIDs and core scheduling cookies, both of which should be
+# assumed to be random values as we have no control over them. The tests replace these values
+# with sed before writing them to the output file, so it can match the expected output file.
+# - The PID of this bash script is replaced with the placeholder `OWN_PID`
+# - The core scheduling cookie of this bash script is replaced by `COOKIE`
+# - Any other cookie is replaced by `DIFFERENT_COOKIE`
+# The behavior of coresched does not depend on the exact values of these cookies, so using
+# placeholder values does not change the behavior tests.
+ts_init_subtest "set-cookie-parent-pid"
+CORESCHED_OUTPUT=$( ($TS_CMD_CORESCHED -v new -d $$ \
+ | tee -a "$TS_OUTPUT") 3>&1 1>&2 2>&3 \
+ | sed "s/$$/PARENT_PID/g")
+CORESCHED_PARENT_COOKIE=$(echo "$CORESCHED_OUTPUT" | sed 's/^.*\(0x.*$\)/\1/g')
+if [ -z "$CORESCHED_PARENT_COOKIE" ]; then
+ ts_failed "empty value for CORESCHED_PARENT_COOKIE"
+fi
+CORESCHED_OUTPUT=$(echo "$CORESCHED_OUTPUT" \
+ | sed "s/$CORESCHED_PARENT_COOKIE/PARENT_COOKIE/g")
+echo "$CORESCHED_OUTPUT" >> "$TS_ERRLOG"
+ts_finalize_subtest
+
+ts_init_subtest "get-cookie-parent-pid"
+$TS_CMD_CORESCHED get -s $$ 2>> "$TS_ERRLOG" \
+ | sed -e "s/$$/PARENT_PID/g" \
+ -e "s/$CORESCHED_PARENT_COOKIE/PARENT_COOKIE/g" >> "$TS_OUTPUT"
+ts_finalize_subtest
+
+ts_init_subtest "get-cookie-own-pid"
+$TS_CMD_CORESCHED get 2>> "$TS_ERRLOG" \
+ | sed -e "s/pid [0-9]\+/pid OWN_PID/g" \
+ -e "s/$CORESCHED_PARENT_COOKIE/PARENT_COOKIE/g" >> "$TS_OUTPUT"
+ts_finalize_subtest
+
+ts_init_subtest "new-child-with-new-cookie"
+$TS_CMD_CORESCHED new -- "$TS_CMD_CORESCHED" get 2>> "$TS_ERRLOG" \
+ | sed -e 's/^.*\(0x.*$\)/\1/g' \
+ -e "s/$CORESCHED_PARENT_COOKIE/SAME_COOKIE/g" \
+ -e "s/0x.*$/DIFFERENT_COOKIE/g" >> "$TS_OUTPUT"
+ts_finalize_subtest
+
+ts_init_subtest "copy-from-parent-to-nested-child"
+$TS_CMD_CORESCHED new -- /bin/bash -c \
+ "$TS_CMD_CORESCHED copy -s $$ -- $TS_CMD_CORESCHED get" \
+2>> "$TS_ERRLOG" \
+ | sed -e 's/^.*\(0x.*$\)/\1/g' \
+ -e "s/$CORESCHED_PARENT_COOKIE/SAME_COOKIE/g" \
+ -e "s/0x.*$/DIFFERENT_COOKIE/g" >> "$TS_OUTPUT"
+ts_finalize_subtest
+
+ts_init_subtest "copy-from-child-to-parent"
+$TS_CMD_CORESCHED new -- /bin/bash -c \
+ "$TS_CMD_CORESCHED copy -s \$\$ -d $$"
+$TS_CMD_CORESCHED get 2>> "$TS_ERRLOG" \
+ | sed -e 's/^.*\(0x.*$\)/\1/g' \
+ -e "s/$CORESCHED_PARENT_COOKIE/SAME_COOKIE/g" \
+ -e "s/0x.*$/DIFFERENT_COOKIE/g" >> "$TS_OUTPUT"
+ts_finalize_subtest
+
+ts_finalize
--
2.44.0
^ permalink raw reply related
* Re: [PATCH v6] coresched: Manage core scheduling cookies for tasks
From: Phil Auld @ 2024-04-09 14:12 UTC (permalink / raw)
To: Karel Zak; +Cc: Thijs Raymakers, thomas, util-linux
In-Reply-To: <20240409134430.twndleg7rxnfsf4m@ws.net.home>
On Tue, Apr 09, 2024 at 03:44:30PM +0200 Karel Zak wrote:
>
> Hi,
>
> sorry for delay in my review ...
>
> On Tue, Apr 09, 2024 at 01:55:32PM +0200, Thijs Raymakers wrote:
> > +== OPTIONS
> > +*-p*, *--pid* _PID_::
> > +Operate on an existing PID and do not launch a new task.
>
> "Nnot launch a new task" does not seem to be true, as you later
> provide an example:
>
> Copy the cookie from a task with pid _123_ to a new task _command_{colon}::
> *{command} --copy -p* _123_ \-- _command_ [_argument_...]
>
> > +*-d*, *--dest* _PID_::
> > +When using *--copy*, specify a destination PID where you want to copy the cookie to.
>
> I find it a little confusing that the --pid is sometimes used as a
> source and in other cases as a destination.
>
> coresched --new --pid 123 # pid is destination
> coresched --copy --pid 123 --dest 456 # pid is source
>
> It seems --copy always requires a source PID (according to the man
> page), why not require a PID as argument for --copy:
>
> coresched --copy 123 --pid 456
>
> in this way --pid will be always destination (for 'copy' and 'new'
> functions) and you will not need extra --dest option at all.
>
Having copy take the dest pid itself could work for me.
As an aside, I just noticed we lost the ability to push the current cookie
to the new task or pid. I suppose you can just do "--copy $$" and get that
though so probably that's good enough.
> If you want to keep the basic functions (e.g. --copy) without
> arguments, it would be better to have --source, --dest, and
> --dest-type instead of using the ambiguous --pid.
At one point we had three arguments that took a pid.
>
> I can also imagine the basic "functions" without "--".
>
> coresched [get] [--dest] 123
This is is really --source since we are getting the cookie from it.
> coresched copy [--source] 123 [--dest] 456
> coresched new [--dest] 456
>
And using --source for "get" you'd have to change it to --dest
after you check for a cookie before you do new.
> In my opinion, we do not have to strictly adhere to old taskset or
> similar commands.
We went around about that. My take is that the users of coresched
will be the same people who use taskset. The idea was to keep it
familiar.
>
> It should be noted that command line arguments are crucial, as they
> are difficult to modify after release.
Indeed. That's why we went around and around...
Cheers,
Phil
>
> > +#include <getopt.h>
> > +#include <stdbool.h>
> > +#include <stdio.h>
> > +#include <sys/prctl.h>
> > +#include <unistd.h>
> > +
> > +#include "c.h"
> > +#include "closestream.h"
> > +#include "nls.h"
> > +#include "optutils.h"
> > +#include "strutils.h"
> > +
> > +// These definitions might not be defined in the header files, even if the
> > +// prctl interface in the kernel accepts them as valid.
> > +#ifndef PR_SCHED_CORE
> > +#define PR_SCHED_CORE 62
> > +#endif
>
> pedantic note, use extra space within ifdef
>
> #ifndef PR_SCHED_CORE
> # define PR_SCHED_CORE 62
> #endif
>
> please :-)
>
> > +static void core_sched_push_cookie(pid_t to, sched_core_scope type)
> > +{
> > + if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_SHARE_TO, to, type, 0))
> > + err(EXIT_FAILURE, _("Failed to push cookie to PID %d"), to);
> > +}
> > +
> > +static void core_sched_copy_cookie(pid_t from, pid_t to,
> > + sched_core_scope to_type)
> > +{
> > + core_sched_pull_cookie(from);
> > + core_sched_push_cookie(to, to_type);
> > +
> > + if (sched_core_verbose) {
> > + sched_core_cookie before = core_sched_get_cookie(from);
> > + fprintf(stderr,
> > + _("%s: copied cookie 0x%lx from PID %d to PID %d\n"),
> > + program_invocation_short_name, before, from, to);
>
> Don't use fprintf(), use:
>
> warnx(_(copied cookie 0x%lx from PID %d to PID %d\n"), before, from, to);
>
> > +static void core_sched_get_and_print_cookie(pid_t pid)
> > +{
> > + if (sched_core_verbose) {
> > + sched_core_cookie after = core_sched_get_cookie(pid);
> > + fprintf(stderr, _("%s: set cookie of PID %d to 0x%lx\n"),
> > + program_invocation_short_name, pid, after);
> > + }
>
> warnx()
>
> > +int main(int argc, char **argv)
> > +{
> > + struct args args = { 0 };
> > + args.cmd = SCHED_CORE_CMD_GET;
> > + args.type = PR_SCHED_CORE_SCOPE_THREAD_GROUP;
>
> struct args args = {
> .cmd = SCHED_CORE_CMD_GET,
> .type = PR_SCHED_CORE_SCOPE_THREAD_GROUP
> };
>
> (the rest is set to zero by compiler).
>
> > +ts_check_test_command "$TS_CMD_CORESCHED"
> > +ts_check_test_command "tee"
> > +ts_check_test_command "sed"
>
> ts_check_test_command is for in-tree stuff, use
>
> ts_check_prog "tee"
> ts_check_prog "sed"
>
> for things in $PATH.
>
> > +ts_init_subtest "get-own-pid-with-cookie"
> > +$TS_CMD_CORESCHED -p $$ 2>> "$TS_ERRLOG" | sed "s/$$/OWN_PID/g" | sed "s/$CORESCHED_COOKIE/COOKIE/g" >> "$TS_OUTPUT"
>
> Let's save some forks, execute sed(1) only once:
>
> | sed -e "s/$$/OWN_PID/g" \
> -e "s/$CORESCHED_COOKIE/COOKIE/g" >> "$TS_OUTPUT"
>
> > +ts_finalize_subtest
> > +
> > +ts_init_subtest "spawn-child-with-new-cookie"
> > +$TS_CMD_CORESCHED -n -- "$TS_CMD_CORESCHED" 2>> "$TS_ERRLOG" \
> > + | sed 's/^.*\(0x.*$\)/\1/g' \
> > + | sed "s/$CORESCHED_COOKIE/SAME_COOKIE/g" \
> > + | sed "s/0x.*$/DIFFERENT_COOKIE/g" >> "$TS_OUTPUT"
> > +ts_finalize_subtest
>
> | sed -e ... -e ... -e
>
> > +ts_init_subtest "change-cookie-of-parent"
> > +$TS_CMD_CORESCHED -n -- /bin/bash -c "$TS_CMD_CORESCHED -c -p \$\$ -d $$"
> > +$TS_CMD_CORESCHED -p $$ 2>> "$TS_ERRLOG" \
> > + | sed "s/$$/OWN_PID/g" \
> > + | sed "s/$CORESCHED_COOKIE/COOKIE/g" \
> > + | sed "s/0x.*$/DIFFERENT_COOKIE/g" >> "$TS_OUTPUT"
> > +ts_finalize_subtest
>
> | sed -e ... -e ... -e
>
>
> Looks good, thanks for all the work!
>
> Karel
>
> --
> Karel Zak <kzak@redhat.com>
> http://karelzak.blogspot.com
>
--
^ permalink raw reply
* Re: [PATCH v6] coresched: Manage core scheduling cookies for tasks
From: Karel Zak @ 2024-04-09 13:44 UTC (permalink / raw)
To: Thijs Raymakers; +Cc: thomas, pauld, util-linux
In-Reply-To: <20240409115532.18762-1-thijs@raymakers.nl>
Hi,
sorry for delay in my review ...
On Tue, Apr 09, 2024 at 01:55:32PM +0200, Thijs Raymakers wrote:
> +== OPTIONS
> +*-p*, *--pid* _PID_::
> +Operate on an existing PID and do not launch a new task.
"Nnot launch a new task" does not seem to be true, as you later
provide an example:
Copy the cookie from a task with pid _123_ to a new task _command_{colon}::
*{command} --copy -p* _123_ \-- _command_ [_argument_...]
> +*-d*, *--dest* _PID_::
> +When using *--copy*, specify a destination PID where you want to copy the cookie to.
I find it a little confusing that the --pid is sometimes used as a
source and in other cases as a destination.
coresched --new --pid 123 # pid is destination
coresched --copy --pid 123 --dest 456 # pid is source
It seems --copy always requires a source PID (according to the man
page), why not require a PID as argument for --copy:
coresched --copy 123 --pid 456
in this way --pid will be always destination (for 'copy' and 'new'
functions) and you will not need extra --dest option at all.
If you want to keep the basic functions (e.g. --copy) without
arguments, it would be better to have --source, --dest, and
--dest-type instead of using the ambiguous --pid.
I can also imagine the basic "functions" without "--".
coresched [get] [--dest] 123
coresched copy [--source] 123 [--dest] 456
coresched new [--dest] 456
In my opinion, we do not have to strictly adhere to old taskset or
similar commands.
It should be noted that command line arguments are crucial, as they
are difficult to modify after release.
> +#include <getopt.h>
> +#include <stdbool.h>
> +#include <stdio.h>
> +#include <sys/prctl.h>
> +#include <unistd.h>
> +
> +#include "c.h"
> +#include "closestream.h"
> +#include "nls.h"
> +#include "optutils.h"
> +#include "strutils.h"
> +
> +// These definitions might not be defined in the header files, even if the
> +// prctl interface in the kernel accepts them as valid.
> +#ifndef PR_SCHED_CORE
> +#define PR_SCHED_CORE 62
> +#endif
pedantic note, use extra space within ifdef
#ifndef PR_SCHED_CORE
# define PR_SCHED_CORE 62
#endif
please :-)
> +static void core_sched_push_cookie(pid_t to, sched_core_scope type)
> +{
> + if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_SHARE_TO, to, type, 0))
> + err(EXIT_FAILURE, _("Failed to push cookie to PID %d"), to);
> +}
> +
> +static void core_sched_copy_cookie(pid_t from, pid_t to,
> + sched_core_scope to_type)
> +{
> + core_sched_pull_cookie(from);
> + core_sched_push_cookie(to, to_type);
> +
> + if (sched_core_verbose) {
> + sched_core_cookie before = core_sched_get_cookie(from);
> + fprintf(stderr,
> + _("%s: copied cookie 0x%lx from PID %d to PID %d\n"),
> + program_invocation_short_name, before, from, to);
Don't use fprintf(), use:
warnx(_(copied cookie 0x%lx from PID %d to PID %d\n"), before, from, to);
> +static void core_sched_get_and_print_cookie(pid_t pid)
> +{
> + if (sched_core_verbose) {
> + sched_core_cookie after = core_sched_get_cookie(pid);
> + fprintf(stderr, _("%s: set cookie of PID %d to 0x%lx\n"),
> + program_invocation_short_name, pid, after);
> + }
warnx()
> +int main(int argc, char **argv)
> +{
> + struct args args = { 0 };
> + args.cmd = SCHED_CORE_CMD_GET;
> + args.type = PR_SCHED_CORE_SCOPE_THREAD_GROUP;
struct args args = {
.cmd = SCHED_CORE_CMD_GET,
.type = PR_SCHED_CORE_SCOPE_THREAD_GROUP
};
(the rest is set to zero by compiler).
> +ts_check_test_command "$TS_CMD_CORESCHED"
> +ts_check_test_command "tee"
> +ts_check_test_command "sed"
ts_check_test_command is for in-tree stuff, use
ts_check_prog "tee"
ts_check_prog "sed"
for things in $PATH.
> +ts_init_subtest "get-own-pid-with-cookie"
> +$TS_CMD_CORESCHED -p $$ 2>> "$TS_ERRLOG" | sed "s/$$/OWN_PID/g" | sed "s/$CORESCHED_COOKIE/COOKIE/g" >> "$TS_OUTPUT"
Let's save some forks, execute sed(1) only once:
| sed -e "s/$$/OWN_PID/g" \
-e "s/$CORESCHED_COOKIE/COOKIE/g" >> "$TS_OUTPUT"
> +ts_finalize_subtest
> +
> +ts_init_subtest "spawn-child-with-new-cookie"
> +$TS_CMD_CORESCHED -n -- "$TS_CMD_CORESCHED" 2>> "$TS_ERRLOG" \
> + | sed 's/^.*\(0x.*$\)/\1/g' \
> + | sed "s/$CORESCHED_COOKIE/SAME_COOKIE/g" \
> + | sed "s/0x.*$/DIFFERENT_COOKIE/g" >> "$TS_OUTPUT"
> +ts_finalize_subtest
| sed -e ... -e ... -e
> +ts_init_subtest "change-cookie-of-parent"
> +$TS_CMD_CORESCHED -n -- /bin/bash -c "$TS_CMD_CORESCHED -c -p \$\$ -d $$"
> +$TS_CMD_CORESCHED -p $$ 2>> "$TS_ERRLOG" \
> + | sed "s/$$/OWN_PID/g" \
> + | sed "s/$CORESCHED_COOKIE/COOKIE/g" \
> + | sed "s/0x.*$/DIFFERENT_COOKIE/g" >> "$TS_OUTPUT"
> +ts_finalize_subtest
| sed -e ... -e ... -e
Looks good, thanks for all the work!
Karel
--
Karel Zak <kzak@redhat.com>
http://karelzak.blogspot.com
^ permalink raw reply
* [PATCH v6] coresched: Manage core scheduling cookies for tasks
From: Thijs Raymakers @ 2024-04-09 11:55 UTC (permalink / raw)
To: thomas, pauld; +Cc: kzak, util-linux, Thijs Raymakers
In-Reply-To: <763a3030-d1c1-4701-b8bb-6878e7b4e18e@t-8ch.de>
Co-authored-by: Phil Auld <pauld@redhat.com>
Signed-off-by: Phil Auld <pauld@redhat.com>
Signed-off-by: Thijs Raymakers <thijs@raymakers.nl>
Reviewed-by: Thomas Weißschuh <thomas@t-8ch.de>
---
Hi Thomas,
Op 09-04-2024 om 8:12 a.m. schreef Thomas Weißschuh:
> In the normal verbose prints program_invocation_short_name is still
> used. Is this intentional?
Yes, this is intentional. I should have mentioned this in the previous
email. If coresched is used to spawn a new program, then it should be
clear what part of stderr is printed by coresched and what part is
printed by the new process. I thought that this is best conveyed by
prefixing the message with program_invocation_short_name
With the prefix, this looks something like
$ coresched -v -n ls
coresched: set cookie of PID 17347 to 0x350cc35
ABOUT-NLS disk-utils meson_options.txt
aclocal.m4 Documentation misc-utils
...
Without the prefix
$ coresched -v -n ls
set cookie of PID 17347 to 0x350cc35
ABOUT-NLS disk-utils meson_options.txt
aclocal.m4 Documentation misc-utils
...
Yes, it is a bit redundant to prefix the message with
program_invocation_short_name, but since it only shows up in verbose
mode anyway I think that the extra bit of clarity is worth it.
Op 09-04-2024 om 8:12 a.m. schreef Thomas Weißschuh:
> On 2024-04-08 23:16:54+0200, Thijs Raymakers wrote:
>> + fprintf(stdout,
>> + USAGE_HELP_OPTIONS(
>> + 20)); /* char offset to align option descriptions */
> These are some very weird linebreaks.
> In my opinion you can drop the comment.
Ah this is a leftover from the boilerplate.c file. I've removed it.
Op 09-04-2024 om 8:12 a.m. schreef Thomas Weißschuh:
> On 2024-04-08 23:16:54+0200, Thijs Raymakers wrote:
>> +#include <sys/wait.h>
> Seems unused.
Indeed, this is no longer necessary. I've removed it.
Op 09-04-2024 om 8:12 a.m. schreef Thomas Weißschuh:
> On 2024-04-08 23:16:54+0200, Thijs Raymakers wrote:
>> + _("Does your kernel support CONFIG_SCHED_CORE?"));
> Could this message be extended to be clearer to the user?
Yes, I've changed it to your suggestion.
Thank you for your review, I've added your Reviewed-by tag to the patch!
Thijs
Interdiff against v5:
diff --git a/schedutils/coresched.c b/schedutils/coresched.c
index 03b50cd5e..b6be1717d 100644
--- a/schedutils/coresched.c
+++ b/schedutils/coresched.c
@@ -11,7 +11,6 @@
#include <stdbool.h>
#include <stdio.h>
#include <sys/prctl.h>
-#include <sys/wait.h>
#include <unistd.h>
#include "c.h"
@@ -105,9 +104,7 @@ static void __attribute__((__noreturn__)) usage(void)
stdout);
fputs(USAGE_SEPARATOR, stdout);
fputsln(_(" -v, --verbose verbose"), stdout);
- fprintf(stdout,
- USAGE_HELP_OPTIONS(
- 20)); /* char offset to align option descriptions */
+ fprintf(stdout, USAGE_HELP_OPTIONS(20));
fprintf(stdout, USAGE_MAN_TAIL("coresched(1)"));
exit(EXIT_SUCCESS);
}
@@ -330,7 +327,8 @@ int main(int argc, char **argv)
if (!is_core_sched_supported())
errx(EXIT_FAILURE,
- _("Does your kernel support CONFIG_SCHED_CORE?"));
+ _("No support for core scheduling found. Does your kernel"
+ "support CONFIG_SCHED_CORE?"));
sched_core_cookie cookie;
pid_t pid;
.gitignore | 1 +
bash-completion/coresched | 0
configure.ac | 12 +-
meson.build | 16 +-
meson_options.txt | 2 +-
schedutils/Makemodule.am | 8 +
schedutils/coresched.1.adoc | 124 ++++++
schedutils/coresched.c | 359 ++++++++++++++++++
tests/commands.sh | 1 +
.../coresched-change-cookie-of-parent | 1 +
.../coresched-get-own-pid-with-cookie | 1 +
.../coresched-set-cookie-own-pid.err | 1 +
.../coresched-spawn-child-with-new-cookie | 1 +
tests/ts/schedutils/coresched | 62 +++
14 files changed, 583 insertions(+), 6 deletions(-)
create mode 100644 bash-completion/coresched
create mode 100644 schedutils/coresched.1.adoc
create mode 100644 schedutils/coresched.c
create mode 100644 tests/expected/schedutils/coresched-change-cookie-of-parent
create mode 100644 tests/expected/schedutils/coresched-get-own-pid-with-cookie
create mode 100644 tests/expected/schedutils/coresched-set-cookie-own-pid.err
create mode 100644 tests/expected/schedutils/coresched-spawn-child-with-new-cookie
create mode 100755 tests/ts/schedutils/coresched
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..a3a648753 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..c83fcdf91
--- /dev/null
+++ b/schedutils/coresched.1.adoc
@@ -0,0 +1,124 @@
+//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
+
+== SYNOPSIS
+
+*{command}* [options] *-p* _pid_
+
+*{command}* [options] \-- _command_ [_argument_...]
+
+== DESCRIPTION
+The *{command}* command is used to retrieve or modify the core scheduling cookies of a running process given its _pid_, or to spawn a new _command_ with core scheduling cookies.
+
+Core scheduling allows you to define groups of tasks that are allowed to share a physical core.
+This is done by assigning a cookie to each task.
+Only tasks have the same cookie are allowed to be scheduled on the same physical core.
+
+It is possible to either assign a new random cookie to a task, or copy a cookie from another task. It is not possible to choose the value of the cookie.
+
+== FUNCTIONS
+*-n*, *--new*::
+Assign a new cookie to an existing PID or to execute _command_ with a new cookie.
+
+*-c*, *--copy*::
+Copy the cookie from an existing PID to another PID, or execute _command_ with that copied cookie.
+
+If no function is specified, it will print the cookie of the current task, or of the task specified with the *-p* option.
+
+== OPTIONS
+*-p*, *--pid* _PID_::
+Operate on an existing PID and do not launch a new task.
+
+*-d*, *--dest* _PID_::
+When using *--copy*, specify a destination PID where you want to copy the cookie to.
+
+*-t*, *--type* _TYPE_::
+The type of the PID whose cookie will be modified. This can be one of three values:
+- *pid*, or process ID
+- *tgid*, or thread group ID (default value)
+- *pgid*, or process group ID
+
+*-v*, *--verbose*::
+Show extra information when modifying cookies of tasks.
+
+*-h*, *--help*::
+Display help text and exit.
+
+*-V*, *--version*::
+Print version and exit.
+
+== EXAMPLES
+Get the core scheduling cookie of the {command} task itself, usually inherited from its parent{colon}::
+*{command}*
+
+Get the core scheduling cookie of a task with PID _123_{colon}::
+*{command} -p* _123_
+
+Give a task with PID _123_ a new core scheduling cookie{colon}::
+*{command} --new -p* _123_
+
+Spawn a new task with a new core scheduling cookie{colon}::
+*{command} --new* \-- _command_ [_argument_...]
+
+Copy the cookie from a task with pid _123_ to another task with pid _456_{colon}::
+*{command} --copy -p* _123_ *-d* _456_
+
+Copy the cookie from a task with pid _123_ to a new task _command_{colon}::
+*{command} --copy -p* _123_ \-- _command_ [_argument_...]
+
+Copy the cookie from a task with pid _123_ to the process group ID _456_{colon}::
+*{command} --copy -p* _123_ *-t* pgid *-d* _456_
+
+== PERMISSIONS
+When retrieving or modifying the core scheduling cookie of a process, you need to have *PTRACE_MODE_READ_REALCREDS* ptrace access to that process.
+See the section "Ptrace access mode checking" in *ptrace*(2) for more information.
+
+== RETURN VALUE
+On success, *{command}* returns 0.
+If *{command}* fails, it will print an error and return 1.
+
+If a _command_ is being executed, the return value of *{command}* will be the return value of _command_.
+
+== NOTES
+*{command}* requires core scheduling support in the kernel.
+This can be enabled via the *CONFIG_SCHED_CORE* kernel config option.
+
+== AUTHORS
+mailto:thijs@raymakers.nl[Thijs Raymakers],
+mailto:pauld@redhat.com[Phil Auld]
+
+== COPYRIGHT
+
+Copyright {copyright} 2024 Thijs Raymakers and Phil Auld. This is free software licensed under the EUPL.
+
+== SEE ALSO
+*chrt*(1),
+*nice*(1),
+*renice*(1),
+*taskset*(1),
+*ptrace*(2),
+*sched*(7)
+
+The Linux kernel source files _Documentation/admin-guide/hw-vuln/core-scheduling.rst_
+
+include::man-common/bugreports.adoc[]
+
+include::man-common/footer.adoc[]
+
+ifdef::translation[]
+include::man-common/translation.adoc[]
+endif::[]
diff --git a/schedutils/coresched.c b/schedutils/coresched.c
new file mode 100644
index 000000000..b6be1717d
--- /dev/null
+++ b/schedutils/coresched.c
@@ -0,0 +1,359 @@
+/**
+ * SPDX-License-Identifier: EUPL-1.2
+ *
+ * coresched.c - manage core scheduling cookies for tasks
+ *
+ * Copyright (C) 2024 Thijs Raymakers, Phil Auld
+ * Licensed under the EUPL v1.2
+ */
+
+#include <getopt.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <sys/prctl.h>
+#include <unistd.h>
+
+#include "c.h"
+#include "closestream.h"
+#include "nls.h"
+#include "optutils.h"
+#include "strutils.h"
+
+// These definitions might not be defined in the header files, even if the
+// prctl interface in the kernel accepts them as valid.
+#ifndef PR_SCHED_CORE
+#define PR_SCHED_CORE 62
+#endif
+#ifndef PR_SCHED_CORE_GET
+#define PR_SCHED_CORE_GET 0
+#endif
+#ifndef PR_SCHED_CORE_CREATE
+#define PR_SCHED_CORE_CREATE 1
+#endif
+#ifndef PR_SCHED_CORE_SHARE_TO
+#define PR_SCHED_CORE_SHARE_TO 2
+#endif
+#ifndef PR_SCHED_CORE_SHARE_FROM
+#define PR_SCHED_CORE_SHARE_FROM 3
+#endif
+#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 sched_core_scope;
+typedef unsigned long sched_core_cookie;
+typedef enum {
+ SCHED_CORE_CMD_GET,
+ SCHED_CORE_CMD_NEW,
+ SCHED_CORE_CMD_COPY,
+} sched_core_cmd;
+
+struct args {
+ pid_t pid;
+ pid_t dest;
+ sched_core_scope type;
+ sched_core_cmd cmd;
+ int exec_argv_offset;
+};
+
+static bool sched_core_verbose = false;
+
+static void __attribute__((__noreturn__)) usage(void)
+{
+ fputs(USAGE_HEADER, stdout);
+ fprintf(stdout, _(" %s [-p PID]\n"), program_invocation_short_name);
+ fprintf(stdout, _(" %s --new [-t <TYPE>] -p <PID>\n"),
+ program_invocation_short_name);
+ fprintf(stdout, _(" %s --new [-t <TYPE>] -- PROGRAM [ARGS...]\n"),
+ program_invocation_short_name);
+ fprintf(stdout, _(" %s --copy -p <PID> [-t <TYPE>] -d <PID>\n"),
+ program_invocation_short_name);
+ fprintf(stdout,
+ _(" %s --copy -p <PID> [-t <TYPE>] -- PROGRAM [ARGS...]\n"),
+ program_invocation_short_name);
+
+ fputs(USAGE_SEPARATOR, stdout);
+ fputsln(_("Manage core scheduling cookies for tasks."), stdout);
+
+ fputs(USAGE_FUNCTIONS, stdout);
+ fputsln(_(" -n, --new assign a new core scheduling cookie to an existing PID or\n"
+ " execute a program with a new cookie."),
+ stdout);
+ fputsln(_(" -c, --copy copy the core scheduling cookie from an existing PID to\n"
+ " either another PID, or copy it to a new program"),
+ stdout);
+ fputsln(_("\n If no function is provided, it will retrieve and print the cookie from\n"
+ " the PID provided via --pid.\n"),
+ stdout);
+
+ fputs(USAGE_OPTIONS, stdout);
+ fputsln(_(" -p, --pid <PID> operate on an existing PID"), stdout);
+ fputsln(_(" -d, --dest <PID> when copying a cookie from an existing PID, --dest is\n"
+ " the destination PID where to copy the cookie to."),
+ stdout);
+ fputsln(_(" -t, --type <TYPE> type of the destination PID, or the type of the PID\n"
+ " when a new core scheduling cookie is created.\n"
+ " Can be one of the following: pid, tgid or pgid.\n"
+ " The default is tgid."),
+ stdout);
+ fputs(USAGE_SEPARATOR, stdout);
+ fputsln(_(" -v, --verbose verbose"), stdout);
+ fprintf(stdout, USAGE_HELP_OPTIONS(20));
+ fprintf(stdout, USAGE_MAN_TAIL("coresched(1)"));
+ exit(EXIT_SUCCESS);
+}
+
+#define bad_usage(FMT...) \
+ do { \
+ warnx(FMT); \
+ errtryhelp(EXIT_FAILURE); \
+ } while (0)
+
+static sched_core_cookie core_sched_get_cookie(pid_t pid)
+{
+ sched_core_cookie cookie = 0;
+ if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_GET, pid,
+ PR_SCHED_CORE_SCOPE_THREAD, &cookie))
+ err(EXIT_FAILURE, _("Failed to get cookie from PID %d"), pid);
+ return cookie;
+}
+
+static void core_sched_create_cookie(pid_t pid, sched_core_scope type)
+{
+ if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_CREATE, pid, type, 0))
+ err(EXIT_FAILURE, _("Failed to create cookie for PID %d"), pid);
+}
+
+static 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(EXIT_FAILURE, _("Failed to pull cookie from PID %d"), from);
+}
+
+static void core_sched_push_cookie(pid_t to, sched_core_scope type)
+{
+ if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_SHARE_TO, to, type, 0))
+ err(EXIT_FAILURE, _("Failed to push cookie to PID %d"), to);
+}
+
+static void core_sched_copy_cookie(pid_t from, pid_t to,
+ sched_core_scope to_type)
+{
+ core_sched_pull_cookie(from);
+ core_sched_push_cookie(to, to_type);
+
+ if (sched_core_verbose) {
+ sched_core_cookie before = core_sched_get_cookie(from);
+ fprintf(stderr,
+ _("%s: copied cookie 0x%lx from PID %d to PID %d\n"),
+ program_invocation_short_name, before, from, to);
+ }
+}
+
+static void core_sched_get_and_print_cookie(pid_t pid)
+{
+ if (sched_core_verbose) {
+ sched_core_cookie after = core_sched_get_cookie(pid);
+ fprintf(stderr, _("%s: set cookie of PID %d to 0x%lx\n"),
+ program_invocation_short_name, pid, after);
+ }
+}
+
+static 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->pid) {
+ core_sched_pull_cookie(args->pid);
+ core_sched_get_and_print_cookie(args->pid);
+ } else {
+ pid_t pid = getpid();
+ core_sched_create_cookie(pid, args->type);
+ core_sched_get_and_print_cookie(pid);
+ }
+
+ if (execvp(argv[0], argv))
+ errexec(argv[0]);
+}
+
+// If PR_SCHED_CORE is not recognized, or not supported on this system,
+// then prctl will set errno to EINVAL. Assuming all other operands of
+// prctl are valid, we can use errno==EINVAL as a check to see whether
+// core scheduling is available on this system.
+static bool is_core_sched_supported(void)
+{
+ sched_core_cookie cookie = 0;
+ if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_GET, getpid(),
+ PR_SCHED_CORE_SCOPE_THREAD, &cookie))
+ if (errno == EINVAL)
+ return false;
+
+ return true;
+}
+
+static sched_core_scope parse_core_sched_type(char *str)
+{
+ if (!strcmp(str, "pid"))
+ return PR_SCHED_CORE_SCOPE_THREAD;
+ else if (!strcmp(str, "tgid"))
+ return PR_SCHED_CORE_SCOPE_THREAD_GROUP;
+ else if (!strcmp(str, "pgid"))
+ return PR_SCHED_CORE_SCOPE_PROCESS_GROUP;
+
+ bad_usage(_("'%s' is an invalid option. Must be one of pid/tgid/pgid"),
+ str);
+}
+
+static void parse_arguments(int argc, char **argv, struct args *args)
+{
+ int c;
+
+ static const struct option longopts[] = {
+ { "new", no_argument, NULL, 'n' },
+ { "copy", no_argument, NULL, 'c' },
+ { "pid", required_argument, NULL, 'p' },
+ { "dest", required_argument, NULL, 'd' },
+ { "type", required_argument, NULL, 't' },
+ { "verbose", no_argument, NULL, 'v' },
+ { "version", no_argument, NULL, 'V' },
+ { "help", no_argument, NULL, 'h' },
+ { NULL, 0, NULL, 0 }
+ };
+ static const ul_excl_t excl[] = {
+ { 'c', 'n' }, // Cannot do both --new and --copy
+ { 'd', 'n' }, // Cannot have both --new and --dest
+ { 0 }
+ };
+
+ int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
+
+ while ((c = getopt_long(argc, argv, "ncp:d:t:vVh", longopts, NULL)) !=
+ -1) {
+ err_exclusive_options(c, longopts, excl, excl_st);
+ switch (c) {
+ case 'n':
+ args->cmd = SCHED_CORE_CMD_NEW;
+ break;
+ case 'c':
+ args->cmd = SCHED_CORE_CMD_COPY;
+ break;
+ case 'p':
+ args->pid = strtopid_or_err(
+ optarg, _("Failed to parse PID for -p/--pid"));
+ break;
+ case 'd':
+ args->dest = strtopid_or_err(
+ optarg, _("Failed to parse PID for -d/--dest"));
+ break;
+ case 't':
+ args->type = parse_core_sched_type(optarg);
+ break;
+ case 'v':
+ sched_core_verbose = true;
+ break;
+ case 'V':
+ print_version(EXIT_SUCCESS);
+ case 'h':
+ usage();
+ default:
+ errtryhelp(EXIT_FAILURE);
+ }
+ }
+
+ if (args->cmd == SCHED_CORE_CMD_COPY && !args->pid)
+ bad_usage(_("--copy: requires a -p/--pid"));
+
+ // More arguments have been passed, which means that the user wants to run
+ // another program with a core scheduling cookie.
+ if (argc > optind) {
+ switch (args->cmd) {
+ case SCHED_CORE_CMD_GET:
+ bad_usage(_("Unknown command"));
+ break;
+ case SCHED_CORE_CMD_NEW:
+ if (args->pid)
+ bad_usage(_(
+ "--new: cannot accept both a -p/--pid and a command"));
+ else
+ args->exec_argv_offset = optind;
+ break;
+ case SCHED_CORE_CMD_COPY:
+ if (args->dest)
+ bad_usage(_(
+ "--copy: cannot accept both a destination PID "
+ "-d/--dest and a command"));
+ else
+ args->exec_argv_offset = optind;
+ break;
+ }
+ }
+
+ if (argc <= optind) {
+ if (args->cmd == SCHED_CORE_CMD_NEW && !args->pid)
+ bad_usage(_(
+ "--new: requires either a -p/--pid or a command"));
+ if (args->cmd == SCHED_CORE_CMD_COPY && !args->dest)
+ bad_usage(_(
+ "--copy: requires either a -d/--dest or a command"));
+ }
+}
+
+int main(int argc, char **argv)
+{
+ struct args args = { 0 };
+ args.cmd = SCHED_CORE_CMD_GET;
+ args.type = PR_SCHED_CORE_SCOPE_THREAD_GROUP;
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ close_stdout_atexit();
+
+ parse_arguments(argc, argv, &args);
+
+ if (!is_core_sched_supported())
+ errx(EXIT_FAILURE,
+ _("No support for core scheduling found. Does your kernel"
+ "support CONFIG_SCHED_CORE?"));
+
+ sched_core_cookie cookie;
+ pid_t pid;
+
+ switch (args.cmd) {
+ case SCHED_CORE_CMD_GET:
+ pid = args.pid ? args.pid : getpid();
+ cookie = core_sched_get_cookie(pid);
+ printf(_("cookie of pid %d is 0x%lx\n"), pid, cookie);
+ break;
+ case SCHED_CORE_CMD_NEW:
+ if (args.pid) {
+ core_sched_create_cookie(args.pid, args.type);
+ core_sched_get_and_print_cookie(args.pid);
+ } else {
+ core_sched_exec_with_cookie(&args, argv);
+ }
+ break;
+ case SCHED_CORE_CMD_COPY:
+ if (args.dest)
+ core_sched_copy_cookie(args.pid, args.dest, args.type);
+ else
+ core_sched_exec_with_cookie(&args, argv);
+ break;
+ default:
+ usage();
+ }
+}
diff --git a/tests/commands.sh b/tests/commands.sh
index 5674c5ff0..9eef92ccb 100644
--- a/tests/commands.sh
+++ b/tests/commands.sh
@@ -71,6 +71,7 @@ TS_CMD_COLCRT=${TS_CMD_COLCRT:-"${ts_commandsdir}colcrt"}
TS_CMD_COLRM=${TS_CMD_COLRM:-"${ts_commandsdir}colrm"}
TS_CMD_COL=${TS_CMD_COL:-"${ts_commandsdir}col"}
TS_CMD_COLUMN=${TS_CMD_COLUMN:-"${ts_commandsdir}column"}
+TS_CMD_CORESCHED=${TS_CMD_CORESCHED:-"${ts_commandsdir}coresched"}
TS_CMD_ENOSYS=${TS_CMD_ENOSYS-"${ts_commandsdir}enosys"}
TS_CMD_EJECT=${TS_CMD_EJECT-"${ts_commandsdir}eject"}
TS_CMD_EXCH=${TS_CMD_EXCH-"${ts_commandsdir}exch"}
diff --git a/tests/expected/schedutils/coresched-change-cookie-of-parent b/tests/expected/schedutils/coresched-change-cookie-of-parent
new file mode 100644
index 000000000..d08118c02
--- /dev/null
+++ b/tests/expected/schedutils/coresched-change-cookie-of-parent
@@ -0,0 +1 @@
+cookie of pid OWN_PID is DIFFERENT_COOKIE
diff --git a/tests/expected/schedutils/coresched-get-own-pid-with-cookie b/tests/expected/schedutils/coresched-get-own-pid-with-cookie
new file mode 100644
index 000000000..3d7f08040
--- /dev/null
+++ b/tests/expected/schedutils/coresched-get-own-pid-with-cookie
@@ -0,0 +1 @@
+cookie of pid OWN_PID is COOKIE
diff --git a/tests/expected/schedutils/coresched-set-cookie-own-pid.err b/tests/expected/schedutils/coresched-set-cookie-own-pid.err
new file mode 100644
index 000000000..1816a4fbd
--- /dev/null
+++ b/tests/expected/schedutils/coresched-set-cookie-own-pid.err
@@ -0,0 +1 @@
+coresched: set cookie of PID OWN_PID to COOKIE
diff --git a/tests/expected/schedutils/coresched-spawn-child-with-new-cookie b/tests/expected/schedutils/coresched-spawn-child-with-new-cookie
new file mode 100644
index 000000000..5b9c40052
--- /dev/null
+++ b/tests/expected/schedutils/coresched-spawn-child-with-new-cookie
@@ -0,0 +1 @@
+DIFFERENT_COOKIE
diff --git a/tests/ts/schedutils/coresched b/tests/ts/schedutils/coresched
new file mode 100755
index 000000000..ed96d89de
--- /dev/null
+++ b/tests/ts/schedutils/coresched
@@ -0,0 +1,62 @@
+#!/bin/bash
+# SPDX-License-Identifier: EUPL-1.2
+#
+# This file is part of util-linux
+#
+# Copyright (C) 2024 Thijs Raymakers
+# Licensed under the EUPL v1.2
+
+TS_TOPDIR="${0%/*}/../.."
+TS_DESC="coresched"
+
+. "$TS_TOPDIR"/functions.sh
+ts_init "$*"
+
+ts_check_test_command "$TS_CMD_CORESCHED"
+ts_check_test_command "tee"
+ts_check_test_command "sed"
+
+# If there is no kernel support, skip the test suite
+CORESCHED_TEST_KERNEL_SUPPORT_CMD=$($TS_CMD_CORESCHED 2>&1)
+if [[ $CORESCHED_TEST_KERNEL_SUPPORT_CMD == *"CONFIG_SCHED_CORE"* ]]; then
+ ts_skip "Kernel has no CONFIG_SCHED_CORE support"
+fi
+
+# The output of coresched contains PIDs and core scheduling cookies, both of which should be
+# assumed to be random values as we have no control over them. The tests replace these values
+# with sed before writing them to the output file, so it can match the expected output file.
+# - The PID of this bash script is replaced with the placeholder `OWN_PID`
+# - The core scheduling cookie of this bash script is replaced by `COOKIE`
+# - Any other cookie is replaced by `DIFFERENT_COOKIE`
+# The behavior of coresched does not depend on the exact values of these cookies, so using
+# placeholder values does not change the behavior tests.
+ts_init_subtest "set-cookie-own-pid"
+CORESCHED_OUTPUT=$( ($TS_CMD_CORESCHED -v -n -p $$ | tee -a "$TS_OUTPUT") 3>&1 1>&2 2>&3 | sed "s/$$/OWN_PID/g")
+CORESCHED_COOKIE=$(echo "$CORESCHED_OUTPUT" | sed 's/^.*\(0x.*$\)/\1/g')
+if [ -z "$CORESCHED_COOKIE" ]; then
+ ts_failed "empty value for CORESCHED_COOKIE "
+fi
+CORESCHED_OUTPUT=$(echo "$CORESCHED_OUTPUT" | sed "s/$CORESCHED_COOKIE/COOKIE/g")
+echo "$CORESCHED_OUTPUT" >> "$TS_ERRLOG"
+ts_finalize_subtest
+
+ts_init_subtest "get-own-pid-with-cookie"
+$TS_CMD_CORESCHED -p $$ 2>> "$TS_ERRLOG" | sed "s/$$/OWN_PID/g" | sed "s/$CORESCHED_COOKIE/COOKIE/g" >> "$TS_OUTPUT"
+ts_finalize_subtest
+
+ts_init_subtest "spawn-child-with-new-cookie"
+$TS_CMD_CORESCHED -n -- "$TS_CMD_CORESCHED" 2>> "$TS_ERRLOG" \
+ | sed 's/^.*\(0x.*$\)/\1/g' \
+ | sed "s/$CORESCHED_COOKIE/SAME_COOKIE/g" \
+ | sed "s/0x.*$/DIFFERENT_COOKIE/g" >> "$TS_OUTPUT"
+ts_finalize_subtest
+
+ts_init_subtest "change-cookie-of-parent"
+$TS_CMD_CORESCHED -n -- /bin/bash -c "$TS_CMD_CORESCHED -c -p \$\$ -d $$"
+$TS_CMD_CORESCHED -p $$ 2>> "$TS_ERRLOG" \
+ | sed "s/$$/OWN_PID/g" \
+ | sed "s/$CORESCHED_COOKIE/COOKIE/g" \
+ | sed "s/0x.*$/DIFFERENT_COOKIE/g" >> "$TS_OUTPUT"
+ts_finalize_subtest
+
+ts_finalize
--
2.44.0
^ permalink raw reply related
* Re: [PATCH] audit-arch.h: add defines for m68k, sh
From: Karel Zak @ 2024-04-09 11:49 UTC (permalink / raw)
To: Chris Hofstaedtler; +Cc: util-linux, John Paul Adrian Glaubitz
In-Reply-To: <20240409083459.346888-1-zeha@debian.org>
On Tue, Apr 09, 2024 at 10:34:59AM +0200, Chris Hofstaedtler wrote:
> include/audit-arch.h | 8 ++++++++
> 1 file changed, 8 insertions(+)
Applied, thanks.
--
Karel Zak <kzak@redhat.com>
http://karelzak.blogspot.com
^ permalink raw reply
* Re: logger/{errors,formats} tests fail on 32-bit PowerPC
From: John Paul Adrian Glaubitz @ 2024-04-09 9:42 UTC (permalink / raw)
To: Thomas Weißschuh; +Cc: util-linux
In-Reply-To: <95d76e15-e879-4f6d-a7e3-c44ee948511e@t-8ch.de>
Hi Thomas,
On Tue, 2024-04-09 at 11:15 +0200, Thomas Weißschuh wrote:
> Hi,
>
> On 2024-04-09 09:26:39+0200, John Paul Adrian Glaubitz wrote:
> > the following two tests are failing on 32-bit PowerPC with 2.40:
> >
> > ---------------------------------------------------------------------
> > 2 tests of 312 FAILED
> >
> > logger/errors
> > logger/formats
> > ---------------------------------------------------------------------
> >
> > The diffs seem to be related to timestamps:
>
> Thanks for the report.
>
> Could you test https://github.com/util-linux/util-linux/pull/2938 ?
> (The last commit is enough)
I can confirm that the two tests pass with the patches of that tree applied on
top of git master of util-linux.
Tested-by: John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de>
Thanks,
Adrian
--
.''`. John Paul Adrian Glaubitz
: :' : Debian Developer
`. `' Physicist
`- GPG: 62FF 8A75 84E0 2956 9546 0006 7426 3B37 F5B5 F913
^ permalink raw reply
* Re: logger/{errors,formats} tests fail on 32-bit PowerPC
From: Thomas Weißschuh @ 2024-04-09 9:15 UTC (permalink / raw)
To: John Paul Adrian Glaubitz; +Cc: util-linux
In-Reply-To: <afef1b770ad80d50660bb2c53a0a8330b88d1049.camel@physik.fu-berlin.de>
Hi,
On 2024-04-09 09:26:39+0200, John Paul Adrian Glaubitz wrote:
> the following two tests are failing on 32-bit PowerPC with 2.40:
>
> ---------------------------------------------------------------------
> 2 tests of 312 FAILED
>
> logger/errors
> logger/formats
> ---------------------------------------------------------------------
>
> The diffs seem to be related to timestamps:
Thanks for the report.
Could you test https://github.com/util-linux/util-linux/pull/2938 ?
(The last commit is enough)
> diff-{{{
> --- /<<PKGBUILDDIR>>/tests/expected/logger/errors-rfc5424_exceed_size.err 2024-01-31 10:02:15.794809649 +0000
> +++ /<<PKGBUILDDIR>>/tests/output/logger/errors-rfc5424_exceed_size.err 2024-04-08 15:12:24.205336645 +0000
> @@ -1 +1 @@
> -<13>1 2009-02-13T23:31:30.123456+00:00 test-hostname rfc5424_exceed_size - - [timeQuality tzKnown="1" isSynced="0"] abc
> +<13>1 2009-02-13T23:31:30.000000+00:00 test-hostname rfc5424_exceed_size - - [timeQuality tzKnown="1" isSynced="0"] abc
> }}}-diff
>
> Details can be found in the full build log in [1].
>
> Adrian
>
> > [1] https://buildd.debian.org/status/fetch.php?pkg=util-linux&arch=powerpc&ver=2.40-5&stamp=1712589234&raw=0
^ permalink raw reply
* Re: [PATCH] audit-arch.h: add defines for m68k, sh
From: Thomas Weißschuh @ 2024-04-09 9:14 UTC (permalink / raw)
To: Chris Hofstaedtler; +Cc: util-linux, John Paul Adrian Glaubitz
In-Reply-To: <20240409083459.346888-1-zeha@debian.org>
On 2024-04-09 10:34:59+0200, Chris Hofstaedtler wrote:
> John Paul Adrian Glaubitz points out that libseccomp needs to release 2.6.0,
> too.
For the record: util-linux does not use libseccomp.
> Signed-off-by: Chris Hofstaedtler <zeha@debian.org>
Reviewed-by: Thomas Weißschuh <thomas@t-8ch.de>
> ---
> include/audit-arch.h | 8 ++++++++
> 1 file changed, 8 insertions(+)
>
> diff --git a/include/audit-arch.h b/include/audit-arch.h
> index ade182417..9afc663cd 100644
> --- a/include/audit-arch.h
> +++ b/include/audit-arch.h
> @@ -35,6 +35,8 @@
> # endif
> #elif __powerpc__
> # define SECCOMP_ARCH_NATIVE AUDIT_ARCH_PPC
> +#elif __m68k__
> +# define SECCOMP_ARCH_NATIVE AUDIT_ARCH_M68K
> #elif __mips__
> # if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
> # define SECCOMP_ARCH_NATIVE AUDIT_ARCH_MIPS
> @@ -47,6 +49,12 @@
> # else
> # define SECCOMP_ARCH_NATIVE AUDIT_ARCH_ARCV2
> # endif
> +#elif __sh__
> +# if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
> +# define SECCOMP_ARCH_NATIVE AUDIT_ARCH_SH
> +# else
> +# define SECCOMP_ARCH_NATIVE AUDIT_ARCH_SHEL
> +# endif
> #elif __sparc__
> # if __SIZEOF_POINTER__ == 4
> # define SECCOMP_ARCH_NATIVE AUDIT_ARCH_SPARC
> --
> 2.39.2
>
>
^ permalink raw reply
* Re: [PATCH] audit-arch.h: add defines for m68k, sh
From: John Paul Adrian Glaubitz @ 2024-04-09 8:44 UTC (permalink / raw)
To: Chris Hofstaedtler, util-linux
In-Reply-To: <20240409083459.346888-1-zeha@debian.org>
On Tue, 2024-04-09 at 10:34 +0200, Chris Hofstaedtler wrote:
> John Paul Adrian Glaubitz points out that libseccomp needs to release 2.6.0,
> too.
Indeed ;-).
> Signed-off-by: Chris Hofstaedtler <zeha@debian.org>
> ---
> include/audit-arch.h | 8 ++++++++
> 1 file changed, 8 insertions(+)
>
> diff --git a/include/audit-arch.h b/include/audit-arch.h
> index ade182417..9afc663cd 100644
> --- a/include/audit-arch.h
> +++ b/include/audit-arch.h
> @@ -35,6 +35,8 @@
> # endif
> #elif __powerpc__
> # define SECCOMP_ARCH_NATIVE AUDIT_ARCH_PPC
> +#elif __m68k__
> +# define SECCOMP_ARCH_NATIVE AUDIT_ARCH_M68K
> #elif __mips__
> # if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
> # define SECCOMP_ARCH_NATIVE AUDIT_ARCH_MIPS
> @@ -47,6 +49,12 @@
> # else
> # define SECCOMP_ARCH_NATIVE AUDIT_ARCH_ARCV2
> # endif
> +#elif __sh__
> +# if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
> +# define SECCOMP_ARCH_NATIVE AUDIT_ARCH_SH
> +# else
> +# define SECCOMP_ARCH_NATIVE AUDIT_ARCH_SHEL
> +# endif
> #elif __sparc__
> # if __SIZEOF_POINTER__ == 4
> # define SECCOMP_ARCH_NATIVE AUDIT_ARCH_SPARC
Reviewed-by: John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de>
--
.''`. John Paul Adrian Glaubitz
: :' : Debian Developer
`. `' Physicist
`- GPG: 62FF 8A75 84E0 2956 9546 0006 7426 3B37 F5B5 F913
^ permalink raw reply
* [PATCH] audit-arch.h: add defines for m68k, sh
From: Chris Hofstaedtler @ 2024-04-09 8:34 UTC (permalink / raw)
To: util-linux; +Cc: John Paul Adrian Glaubitz, Chris Hofstaedtler
John Paul Adrian Glaubitz points out that libseccomp needs to release 2.6.0,
too.
Signed-off-by: Chris Hofstaedtler <zeha@debian.org>
---
include/audit-arch.h | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/include/audit-arch.h b/include/audit-arch.h
index ade182417..9afc663cd 100644
--- a/include/audit-arch.h
+++ b/include/audit-arch.h
@@ -35,6 +35,8 @@
# endif
#elif __powerpc__
# define SECCOMP_ARCH_NATIVE AUDIT_ARCH_PPC
+#elif __m68k__
+# define SECCOMP_ARCH_NATIVE AUDIT_ARCH_M68K
#elif __mips__
# if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
# define SECCOMP_ARCH_NATIVE AUDIT_ARCH_MIPS
@@ -47,6 +49,12 @@
# else
# define SECCOMP_ARCH_NATIVE AUDIT_ARCH_ARCV2
# endif
+#elif __sh__
+# if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+# define SECCOMP_ARCH_NATIVE AUDIT_ARCH_SH
+# else
+# define SECCOMP_ARCH_NATIVE AUDIT_ARCH_SHEL
+# endif
#elif __sparc__
# if __SIZEOF_POINTER__ == 4
# define SECCOMP_ARCH_NATIVE AUDIT_ARCH_SPARC
--
2.39.2
^ permalink raw reply related
* logger/{errors,formats} tests fail on 32-bit PowerPC
From: John Paul Adrian Glaubitz @ 2024-04-09 7:26 UTC (permalink / raw)
To: util-linux
Hello,
the following two tests are failing on 32-bit PowerPC with 2.40:
---------------------------------------------------------------------
2 tests of 312 FAILED
logger/errors
logger/formats
---------------------------------------------------------------------
The diffs seem to be related to timestamps:
diff-{{{
--- /<<PKGBUILDDIR>>/tests/expected/logger/errors-rfc5424_exceed_size.err 2024-01-31 10:02:15.794809649 +0000
+++ /<<PKGBUILDDIR>>/tests/output/logger/errors-rfc5424_exceed_size.err 2024-04-08 15:12:24.205336645 +0000
@@ -1 +1 @@
-<13>1 2009-02-13T23:31:30.123456+00:00 test-hostname rfc5424_exceed_size - - [timeQuality tzKnown="1" isSynced="0"] abc
+<13>1 2009-02-13T23:31:30.000000+00:00 test-hostname rfc5424_exceed_size - - [timeQuality tzKnown="1" isSynced="0"] abc
}}}-diff
Details can be found in the full build log in [1].
Adrian
> [1] https://buildd.debian.org/status/fetch.php?pkg=util-linux&arch=powerpc&ver=2.40-5&stamp=1712589234&raw=0
--
.''`. John Paul Adrian Glaubitz
: :' : Debian Developer
`. `' Physicist
`- GPG: 62FF 8A75 84E0 2956 9546 0006 7426 3B37 F5B5 F913
^ permalink raw reply
* Re: [PATCH v5] coresched: Manage core scheduling cookies for tasks
From: Thomas Weißschuh @ 2024-04-09 6:12 UTC (permalink / raw)
To: Thijs Raymakers; +Cc: pauld, kzak, util-linux
In-Reply-To: <20240408211655.124186-1-thijs@raymakers.nl>
Hi Thijs,
On 2024-04-08 23:16:54+0200, Thijs Raymakers wrote:
> Op 05-04-2024 om 4:14 p.m. schreef Phil Auld:
> > On Fri, Apr 05, 2024 at 08:26:08AM +0200 Thomas Weißschuh wrote:
> >> On 2024-04-05 00:03:57+0200, Thijs Raymakers wrote:
> >>> Therefore, I've changed to code to explicitly check for kernel support
> >>> beforehand. This change also removes the need for the `check_prctl`
> >>> macro.
> >>
> >> Thanks, would it make sense to expose this check via the CLI?
> >> That could then used by the tests to skip themselves if the system does
> >> not provide core scheduling on old kernels or qemu-user.
> >>
> >
> > I don't think so. It's checking by doing an operation so just do a "get"
> > if you really want to check first. An explicit check would just be that
> > anyway.
>
> I agree with Phil, I don't think it would be necessary. I've added the check
> to the tests that allows them to be skipped if core scheduling is not
> available (such as on older kernels like you mentioned). This check just
> runs `coresched`, which will do the check implicitly.
Sounds good to me.
[snip]
> diff --git a/schedutils/coresched.c b/schedutils/coresched.c
> new file mode 100644
> index 000000000..03b50cd5e
> --- /dev/null
> +++ b/schedutils/coresched.c
> @@ -0,0 +1,361 @@
> +/**
> + * SPDX-License-Identifier: EUPL-1.2
> + *
> + * coresched.c - manage core scheduling cookies for tasks
> + *
> + * Copyright (C) 2024 Thijs Raymakers, Phil Auld
> + * Licensed under the EUPL v1.2
> + */
> +
> +#include <getopt.h>
> +#include <stdbool.h>
> +#include <stdio.h>
> +#include <sys/prctl.h>
> +#include <sys/wait.h>
Seems unused.
> +#include <unistd.h>
> +
> +#include "c.h"
> +#include "closestream.h"
> +#include "nls.h"
> +#include "optutils.h"
> +#include "strutils.h"
> +
> +// These definitions might not be defined in the header files, even if the
> +// prctl interface in the kernel accepts them as valid.
> +#ifndef PR_SCHED_CORE
> +#define PR_SCHED_CORE 62
> +#endif
> +#ifndef PR_SCHED_CORE_GET
> +#define PR_SCHED_CORE_GET 0
> +#endif
> +#ifndef PR_SCHED_CORE_CREATE
> +#define PR_SCHED_CORE_CREATE 1
> +#endif
> +#ifndef PR_SCHED_CORE_SHARE_TO
> +#define PR_SCHED_CORE_SHARE_TO 2
> +#endif
> +#ifndef PR_SCHED_CORE_SHARE_FROM
> +#define PR_SCHED_CORE_SHARE_FROM 3
> +#endif
> +#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 sched_core_scope;
> +typedef unsigned long sched_core_cookie;
> +typedef enum {
> + SCHED_CORE_CMD_GET,
> + SCHED_CORE_CMD_NEW,
> + SCHED_CORE_CMD_COPY,
> +} sched_core_cmd;
> +
> +struct args {
> + pid_t pid;
> + pid_t dest;
> + sched_core_scope type;
> + sched_core_cmd cmd;
> + int exec_argv_offset;
> +};
> +
> +static bool sched_core_verbose = false;
> +
> +static void __attribute__((__noreturn__)) usage(void)
> +{
> + fputs(USAGE_HEADER, stdout);
> + fprintf(stdout, _(" %s [-p PID]\n"), program_invocation_short_name);
> + fprintf(stdout, _(" %s --new [-t <TYPE>] -p <PID>\n"),
> + program_invocation_short_name);
> + fprintf(stdout, _(" %s --new [-t <TYPE>] -- PROGRAM [ARGS...]\n"),
> + program_invocation_short_name);
> + fprintf(stdout, _(" %s --copy -p <PID> [-t <TYPE>] -d <PID>\n"),
> + program_invocation_short_name);
> + fprintf(stdout,
> + _(" %s --copy -p <PID> [-t <TYPE>] -- PROGRAM [ARGS...]\n"),
> + program_invocation_short_name);
> +
> + fputs(USAGE_SEPARATOR, stdout);
> + fputsln(_("Manage core scheduling cookies for tasks."), stdout);
> +
> + fputs(USAGE_FUNCTIONS, stdout);
> + fputsln(_(" -n, --new assign a new core scheduling cookie to an existing PID or\n"
> + " execute a program with a new cookie."),
> + stdout);
> + fputsln(_(" -c, --copy copy the core scheduling cookie from an existing PID to\n"
> + " either another PID, or copy it to a new program"),
> + stdout);
> + fputsln(_("\n If no function is provided, it will retrieve and print the cookie from\n"
> + " the PID provided via --pid.\n"),
> + stdout);
> +
> + fputs(USAGE_OPTIONS, stdout);
> + fputsln(_(" -p, --pid <PID> operate on an existing PID"), stdout);
> + fputsln(_(" -d, --dest <PID> when copying a cookie from an existing PID, --dest is\n"
> + " the destination PID where to copy the cookie to."),
> + stdout);
> + fputsln(_(" -t, --type <TYPE> type of the destination PID, or the type of the PID\n"
> + " when a new core scheduling cookie is created.\n"
> + " Can be one of the following: pid, tgid or pgid.\n"
> + " The default is tgid."),
> + stdout);
> + fputs(USAGE_SEPARATOR, stdout);
> + fputsln(_(" -v, --verbose verbose"), stdout);
> + fprintf(stdout,
> + USAGE_HELP_OPTIONS(
> + 20)); /* char offset to align option descriptions */
These are some very weird linebreaks.
In my opinion you can drop the comment.
> + fprintf(stdout, USAGE_MAN_TAIL("coresched(1)"));
> + exit(EXIT_SUCCESS);
> +}
> +
> +#define bad_usage(FMT...) \
> + do { \
> + warnx(FMT); \
> + errtryhelp(EXIT_FAILURE); \
> + } while (0)
> +
> +static sched_core_cookie core_sched_get_cookie(pid_t pid)
> +{
> + sched_core_cookie cookie = 0;
> + if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_GET, pid,
> + PR_SCHED_CORE_SCOPE_THREAD, &cookie))
> + err(EXIT_FAILURE, _("Failed to get cookie from PID %d"), pid);
> + return cookie;
> +}
> +
> +static void core_sched_create_cookie(pid_t pid, sched_core_scope type)
> +{
> + if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_CREATE, pid, type, 0))
> + err(EXIT_FAILURE, _("Failed to create cookie for PID %d"), pid);
> +}
> +
> +static 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(EXIT_FAILURE, _("Failed to pull cookie from PID %d"), from);
> +}
> +
> +static void core_sched_push_cookie(pid_t to, sched_core_scope type)
> +{
> + if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_SHARE_TO, to, type, 0))
> + err(EXIT_FAILURE, _("Failed to push cookie to PID %d"), to);
> +}
> +
> +static void core_sched_copy_cookie(pid_t from, pid_t to,
> + sched_core_scope to_type)
> +{
> + core_sched_pull_cookie(from);
> + core_sched_push_cookie(to, to_type);
> +
> + if (sched_core_verbose) {
> + sched_core_cookie before = core_sched_get_cookie(from);
> + fprintf(stderr,
> + _("%s: copied cookie 0x%lx from PID %d to PID %d\n"),
> + program_invocation_short_name, before, from, to);
In the normal verbose prints program_invocation_short_name is still
used. Is this intentional?
> + }
> +}
> +
> +static void core_sched_get_and_print_cookie(pid_t pid)
> +{
> + if (sched_core_verbose) {
> + sched_core_cookie after = core_sched_get_cookie(pid);
> + fprintf(stderr, _("%s: set cookie of PID %d to 0x%lx\n"),
> + program_invocation_short_name, pid, after);
> + }
> +}
> +
> +static 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->pid) {
> + core_sched_pull_cookie(args->pid);
> + core_sched_get_and_print_cookie(args->pid);
> + } else {
> + pid_t pid = getpid();
> + core_sched_create_cookie(pid, args->type);
> + core_sched_get_and_print_cookie(pid);
> + }
> +
> + if (execvp(argv[0], argv))
> + errexec(argv[0]);
> +}
> +
> +// If PR_SCHED_CORE is not recognized, or not supported on this system,
> +// then prctl will set errno to EINVAL. Assuming all other operands of
> +// prctl are valid, we can use errno==EINVAL as a check to see whether
> +// core scheduling is available on this system.
> +static bool is_core_sched_supported(void)
> +{
> + sched_core_cookie cookie = 0;
> + if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_GET, getpid(),
> + PR_SCHED_CORE_SCOPE_THREAD, &cookie))
> + if (errno == EINVAL)
> + return false;
> +
> + return true;
> +}
> +
> +static sched_core_scope parse_core_sched_type(char *str)
> +{
> + if (!strcmp(str, "pid"))
> + return PR_SCHED_CORE_SCOPE_THREAD;
> + else if (!strcmp(str, "tgid"))
> + return PR_SCHED_CORE_SCOPE_THREAD_GROUP;
> + else if (!strcmp(str, "pgid"))
> + return PR_SCHED_CORE_SCOPE_PROCESS_GROUP;
> +
> + bad_usage(_("'%s' is an invalid option. Must be one of pid/tgid/pgid"),
> + str);
> +}
> +
> +static void parse_arguments(int argc, char **argv, struct args *args)
> +{
> + int c;
> +
> + static const struct option longopts[] = {
> + { "new", no_argument, NULL, 'n' },
> + { "copy", no_argument, NULL, 'c' },
> + { "pid", required_argument, NULL, 'p' },
> + { "dest", required_argument, NULL, 'd' },
> + { "type", required_argument, NULL, 't' },
> + { "verbose", no_argument, NULL, 'v' },
> + { "version", no_argument, NULL, 'V' },
> + { "help", no_argument, NULL, 'h' },
> + { NULL, 0, NULL, 0 }
> + };
> + static const ul_excl_t excl[] = {
> + { 'c', 'n' }, // Cannot do both --new and --copy
> + { 'd', 'n' }, // Cannot have both --new and --dest
> + { 0 }
> + };
> +
> + int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
> +
> + while ((c = getopt_long(argc, argv, "ncp:d:t:vVh", longopts, NULL)) !=
> + -1) {
> + err_exclusive_options(c, longopts, excl, excl_st);
> + switch (c) {
> + case 'n':
> + args->cmd = SCHED_CORE_CMD_NEW;
> + break;
> + case 'c':
> + args->cmd = SCHED_CORE_CMD_COPY;
> + break;
> + case 'p':
> + args->pid = strtopid_or_err(
> + optarg, _("Failed to parse PID for -p/--pid"));
> + break;
> + case 'd':
> + args->dest = strtopid_or_err(
> + optarg, _("Failed to parse PID for -d/--dest"));
> + break;
> + case 't':
> + args->type = parse_core_sched_type(optarg);
> + break;
> + case 'v':
> + sched_core_verbose = true;
> + break;
> + case 'V':
> + print_version(EXIT_SUCCESS);
> + case 'h':
> + usage();
> + default:
> + errtryhelp(EXIT_FAILURE);
> + }
> + }
> +
> + if (args->cmd == SCHED_CORE_CMD_COPY && !args->pid)
> + bad_usage(_("--copy: requires a -p/--pid"));
> +
> + // More arguments have been passed, which means that the user wants to run
> + // another program with a core scheduling cookie.
> + if (argc > optind) {
> + switch (args->cmd) {
> + case SCHED_CORE_CMD_GET:
> + bad_usage(_("Unknown command"));
> + break;
> + case SCHED_CORE_CMD_NEW:
> + if (args->pid)
> + bad_usage(_(
> + "--new: cannot accept both a -p/--pid and a command"));
> + else
> + args->exec_argv_offset = optind;
> + break;
> + case SCHED_CORE_CMD_COPY:
> + if (args->dest)
> + bad_usage(_(
> + "--copy: cannot accept both a destination PID "
> + "-d/--dest and a command"));
> + else
> + args->exec_argv_offset = optind;
> + break;
> + }
> + }
> +
> + if (argc <= optind) {
> + if (args->cmd == SCHED_CORE_CMD_NEW && !args->pid)
> + bad_usage(_(
> + "--new: requires either a -p/--pid or a command"));
> + if (args->cmd == SCHED_CORE_CMD_COPY && !args->dest)
> + bad_usage(_(
> + "--copy: requires either a -d/--dest or a command"));
> + }
> +}
> +
> +int main(int argc, char **argv)
> +{
> + struct args args = { 0 };
> + args.cmd = SCHED_CORE_CMD_GET;
> + args.type = PR_SCHED_CORE_SCOPE_THREAD_GROUP;
> +
> + setlocale(LC_ALL, "");
> + bindtextdomain(PACKAGE, LOCALEDIR);
> + textdomain(PACKAGE);
> + close_stdout_atexit();
> +
> + parse_arguments(argc, argv, &args);
> +
> + if (!is_core_sched_supported())
> + errx(EXIT_FAILURE,
> + _("Does your kernel support CONFIG_SCHED_CORE?"));
Could this message be extended to be clearer to the user?
"No support for core scheduling found. Does your kernel support
CONFIG_SCHED_CORE?"
> +
> + sched_core_cookie cookie;
> + pid_t pid;
> +
> + switch (args.cmd) {
> + case SCHED_CORE_CMD_GET:
> + pid = args.pid ? args.pid : getpid();
> + cookie = core_sched_get_cookie(pid);
> + printf(_("cookie of pid %d is 0x%lx\n"), pid, cookie);
> + break;
> + case SCHED_CORE_CMD_NEW:
> + if (args.pid) {
> + core_sched_create_cookie(args.pid, args.type);
> + core_sched_get_and_print_cookie(args.pid);
> + } else {
> + core_sched_exec_with_cookie(&args, argv);
> + }
> + break;
> + case SCHED_CORE_CMD_COPY:
> + if (args.dest)
> + core_sched_copy_cookie(args.pid, args.dest, args.type);
> + else
> + core_sched_exec_with_cookie(&args, argv);
> + break;
> + default:
> + usage();
> + }
> +}
[snip]
Some more (non-functional) nitpicks.
With or without them fixed:
Reviewed-by: Thomas Weißschuh <thomas@t-8ch.de>
Thanks!
^ permalink raw reply
* [PATCH v5] coresched: Manage core scheduling cookies for tasks
From: Thijs Raymakers @ 2024-04-08 21:16 UTC (permalink / raw)
To: thomas, pauld; +Cc: kzak, util-linux, Thijs Raymakers
In-Reply-To: <20240405141407.GA224852@lorien.usersys.redhat.com>
Co-authored-by: Phil Auld <pauld@redhat.com>
Signed-off-by: Thijs Raymakers <thijs@raymakers.nl>
Signed-off-by: Phil Auld <pauld@redhat.com>
---
Hi Thomas and Phil,
Op 05-04-2024 om 4:14 p.m. schreef Phil Auld:
> On Fri, Apr 05, 2024 at 08:26:08AM +0200 Thomas Weißschuh wrote:
>> On 2024-04-05 00:03:57+0200, Thijs Raymakers wrote:
>>> Therefore, I've changed to code to explicitly check for kernel support
>>> beforehand. This change also removes the need for the `check_prctl`
>>> macro.
>>
>> Thanks, would it make sense to expose this check via the CLI?
>> That could then used by the tests to skip themselves if the system does
>> not provide core scheduling on old kernels or qemu-user.
>>
>
> I don't think so. It's checking by doing an operation so just do a "get"
> if you really want to check first. An explicit check would just be that
> anyway.
I agree with Phil, I don't think it would be necessary. I've added the check
to the tests that allows them to be skipped if core scheduling is not
available (such as on older kernels like you mentioned). This check just
runs `coresched`, which will do the check implicitly.
Op 05-04-2024 om 8:26 a.m. schreef Thomas Weißschuh:
> General codestyle is to leave out braces around single-statement blocks.
> (I mentioned this before, if you want to keep them please mention it)
Ah, I guess something went wrong there on my side. I have changed the braces
where applicable.
(as a sidenote, I personally try to avoid leaving out the braces around
single-statement blocks, since it makes it slightly easier to introduce
logic bugs like the infamous goto fail bug from 2014)
Op 05-04-2024 om 8:26 a.m. schreef Thomas Weißschuh:
> These can be plain strcmp().
Changed!
Op 05-04-2024 om 4:14 p.m. schreef Phil Auld:
> On Fri, Apr 05, 2024 at 08:26:08AM +0200 Thomas Weißschuh wrote:
>> On 2024-04-05 00:03:57+0200, Thijs Raymakers wrote:
>>> + if (!is_core_sched_supported()) {
>>> + errx(ENOTSUP, _("Does your kernel support CONFIG_SCHED_CORE?"));
>>
>> Afaik errnos are not guaranteed to have the same numeric value
>> everywhere. If you want to signal something special, use your own
>> constant (and document it in the manpage).
>
> I don't think we should make up an errno but use one that already exists.
>
> If not ENOPTSUP we can just leave it as EINVAL I suppose. That should be
> the same everywhere.
I don't think there is a reason to differentiate the error code
between "failing because the kernel doesn't support it" and "any other
fatal failure". I've changed it to just return EXIT_FAILURE (1) in case
there is no support, since all other errors do that as well. The
displayed error message can be used to differentiate the errors.
Op 05-04-2024 om 8:26 a.m. schreef Thomas Weißschuh:
> On 2024-04-05 00:03:57+0200, Thijs Raymakers wrote:
>> + if (cookie) {
>> + printf(_("%s: cookie of pid %d is 0x%lx\n"),
>> + program_invocation_short_name, args.pid,
>> + cookie);
> program_invocation_short_name seems unnecessary.
Fair point, I've removed it.
Op 05-04-2024 om 4:14 p.m. schreef Phil Auld:
>Op 05-04-2024 om 8:26 a.m. schreef Thomas Weißschuh:
>> On 2024-04-05 00:03:57+0200, Thijs Raymakers wrote:
>>> + } else {
>>> + errx(ENODATA,
>>> + _("pid %d doesn't have a core scheduling cookie"),
>>> + args.pid);
>>
>> Is this really a failure case?
>> I would make the print only on --verbose and otherwise succeed without
>> output.
>
> Agree. This is not an error.
I've merged the two cases into one. A core scheduling cookie of 0 is
indeed not an error, as it is still a valid value.
Op 05-04-2024 om 8:26 a.m. schreef Thomas Weißschuh:
> On 2024-04-05 00:03:57+0200, Thijs Raymakers wrote:
>> + }
>> + } else {
>> + usage();
> Why not print the current cookie here?
I initially wrote it like this to make it more obvious what the command
does on first use because it will just show the help page, and thus saves
you from typing "coresched -h".
However, I do realise that this design makes it more inconvenient
to get the current cookie. To get the cookie of the current process you
now need to do something like "coresched -p $$", which isn't very
intuitive to use.
This has been changed and "coresched" will now print the cookie
of the current process.
Op 05-04-2024 om 8:26 a.m. schreef Thomas Weißschuh:
> On 2024-04-05 00:03:57+0200, Thijs Raymakers wrote:
>> +$TS_CMD_CORESCHED -p $$ 3>&2 2>&1 1>&3 | sed "s/$$/OWN_PID/g" >> "$TS_OUTPUT"
>
> This test fails if the process starting it already has a cookie set.
> Also in that case the output is printed past the redirections.
Indeed, this test does fail if the process does already have a cookie.
Since there is no way to clear the cookie of the process, this test
doesn't make a lot of sense anyway. I've removed it.
Op 05-04-2024 om 8:26 a.m. schreef Thomas Weißschuh:
> On 2024-04-05 00:03:57+0200, Thijs Raymakers wrote:
>> +$TS_CMD_CORESCHED -n -- /bin/bash -c "$TS_CMD_CORESCHED -p \$\$" \
>
> When coresched without arguments prints the current value this bash
> trickery can be removed.
I've removed it from the spawn-child-with-new-cookie test.
I want to thank you both the comments, an interdiff with changes compared
to the the previous version is listed below.
Thijs
Interdiff against v4:
diff --git a/schedutils/coresched.1.adoc b/schedutils/coresched.1.adoc
index 34bc352a8..c83fcdf91 100644
--- a/schedutils/coresched.1.adoc
+++ b/schedutils/coresched.1.adoc
@@ -37,7 +37,7 @@ Assign a new cookie to an existing PID or to execute _command_ with a new cookie
*-c*, *--copy*::
Copy the cookie from an existing PID to another PID, or execute _command_ with that copied cookie.
-If no function is specified, it will retrieve and print the cookie of a task.
+If no function is specified, it will print the cookie of the current task, or of the task specified with the *-p* option.
== OPTIONS
*-p*, *--pid* _PID_::
@@ -62,6 +62,9 @@ Display help text and exit.
Print version and exit.
== EXAMPLES
+Get the core scheduling cookie of the {command} task itself, usually inherited from its parent{colon}::
+*{command}*
+
Get the core scheduling cookie of a task with PID _123_{colon}::
*{command} -p* _123_
@@ -87,7 +90,12 @@ See the section "Ptrace access mode checking" in *ptrace*(2) for more informatio
== RETURN VALUE
On success, *{command}* returns 0.
If *{command}* fails, it will print an error and return 1.
-If core scheduling is not supported with your kernel (e.g. because *CONFIG_SCHED_CORE*=n), then it will return 95 (*ENOTSUP*).
+
+If a _command_ is being executed, the return value of *{command}* will be the return value of _command_.
+
+== NOTES
+*{command}* requires core scheduling support in the kernel.
+This can be enabled via the *CONFIG_SCHED_CORE* kernel config option.
== AUTHORS
mailto:thijs@raymakers.nl[Thijs Raymakers],
diff --git a/schedutils/coresched.c b/schedutils/coresched.c
index 3d9392b1a..03b50cd5e 100644
--- a/schedutils/coresched.c
+++ b/schedutils/coresched.c
@@ -112,40 +112,38 @@ static void __attribute__((__noreturn__)) usage(void)
exit(EXIT_SUCCESS);
}
-#define bad_usage(FMT...) \
- warnx(FMT); \
- errtryhelp(EXIT_FAILURE);
+#define bad_usage(FMT...) \
+ do { \
+ warnx(FMT); \
+ errtryhelp(EXIT_FAILURE); \
+ } while (0)
static sched_core_cookie core_sched_get_cookie(pid_t pid)
{
sched_core_cookie cookie = 0;
if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_GET, pid,
- PR_SCHED_CORE_SCOPE_THREAD, &cookie)) {
+ PR_SCHED_CORE_SCOPE_THREAD, &cookie))
err(EXIT_FAILURE, _("Failed to get cookie from PID %d"), pid);
- }
return cookie;
}
static void core_sched_create_cookie(pid_t pid, sched_core_scope type)
{
- if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_CREATE, pid, type, 0)) {
+ if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_CREATE, pid, type, 0))
err(EXIT_FAILURE, _("Failed to create cookie for PID %d"), pid);
- }
}
static 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)) {
+ PR_SCHED_CORE_SCOPE_THREAD, 0))
err(EXIT_FAILURE, _("Failed to pull cookie from PID %d"), from);
- }
}
static void core_sched_push_cookie(pid_t to, sched_core_scope type)
{
- if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_SHARE_TO, to, type, 0)) {
+ if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_SHARE_TO, to, type, 0))
err(EXIT_FAILURE, _("Failed to push cookie to PID %d"), to);
- }
}
static void core_sched_copy_cookie(pid_t from, pid_t to,
@@ -191,9 +189,8 @@ static void core_sched_exec_with_cookie(struct args *args, char **argv)
core_sched_get_and_print_cookie(pid);
}
- if (execvp(argv[0], argv)) {
+ if (execvp(argv[0], argv))
errexec(argv[0]);
- }
}
// If PR_SCHED_CORE is not recognized, or not supported on this system,
@@ -204,21 +201,20 @@ static bool is_core_sched_supported(void)
{
sched_core_cookie cookie = 0;
if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_GET, getpid(),
- PR_SCHED_CORE_SCOPE_THREAD, &cookie)) {
- if (errno == EINVAL) {
+ PR_SCHED_CORE_SCOPE_THREAD, &cookie))
+ if (errno == EINVAL)
return false;
- }
- }
+
return true;
}
static sched_core_scope parse_core_sched_type(char *str)
{
- if (!strncmp(str, "pid", 4))
+ if (!strcmp(str, "pid"))
return PR_SCHED_CORE_SCOPE_THREAD;
- else if (!strncmp(str, "tgid", 5))
+ else if (!strcmp(str, "tgid"))
return PR_SCHED_CORE_SCOPE_THREAD_GROUP;
- else if (!strncmp(str, "pgid", 5))
+ else if (!strcmp(str, "pgid"))
return PR_SCHED_CORE_SCOPE_PROCESS_GROUP;
bad_usage(_("'%s' is an invalid option. Must be one of pid/tgid/pgid"),
@@ -281,9 +277,8 @@ static void parse_arguments(int argc, char **argv, struct args *args)
}
}
- if (args->cmd == SCHED_CORE_CMD_COPY && !args->pid) {
+ if (args->cmd == SCHED_CORE_CMD_COPY && !args->pid)
bad_usage(_("--copy: requires a -p/--pid"));
- }
// More arguments have been passed, which means that the user wants to run
// another program with a core scheduling cookie.
@@ -293,34 +288,30 @@ static void parse_arguments(int argc, char **argv, struct args *args)
bad_usage(_("Unknown command"));
break;
case SCHED_CORE_CMD_NEW:
- if (args->pid) {
+ if (args->pid)
bad_usage(_(
"--new: cannot accept both a -p/--pid and a command"));
- } else {
+ else
args->exec_argv_offset = optind;
- }
break;
case SCHED_CORE_CMD_COPY:
- if (args->dest) {
+ if (args->dest)
bad_usage(_(
"--copy: cannot accept both a destination PID "
- "-d/--dest and a command"))
- } else {
+ "-d/--dest and a command"));
+ else
args->exec_argv_offset = optind;
- }
break;
}
}
if (argc <= optind) {
- if (args->cmd == SCHED_CORE_CMD_NEW && !args->pid) {
+ if (args->cmd == SCHED_CORE_CMD_NEW && !args->pid)
bad_usage(_(
"--new: requires either a -p/--pid or a command"));
- }
- if (args->cmd == SCHED_CORE_CMD_COPY && !args->dest) {
+ if (args->cmd == SCHED_CORE_CMD_COPY && !args->dest)
bad_usage(_(
"--copy: requires either a -d/--dest or a command"));
- }
}
}
@@ -337,28 +328,18 @@ int main(int argc, char **argv)
parse_arguments(argc, argv, &args);
- if (!is_core_sched_supported()) {
- errx(ENOTSUP, _("Does your kernel support CONFIG_SCHED_CORE?"));
- }
+ if (!is_core_sched_supported())
+ errx(EXIT_FAILURE,
+ _("Does your kernel support CONFIG_SCHED_CORE?"));
sched_core_cookie cookie;
+ pid_t pid;
switch (args.cmd) {
case SCHED_CORE_CMD_GET:
- if (args.pid) {
- cookie = core_sched_get_cookie(args.pid);
- if (cookie) {
- printf(_("%s: cookie of pid %d is 0x%lx\n"),
- program_invocation_short_name, args.pid,
- cookie);
- } else {
- errx(ENODATA,
- _("pid %d doesn't have a core scheduling cookie"),
- args.pid);
- }
- } else {
- usage();
- }
+ pid = args.pid ? args.pid : getpid();
+ cookie = core_sched_get_cookie(pid);
+ printf(_("cookie of pid %d is 0x%lx\n"), pid, cookie);
break;
case SCHED_CORE_CMD_NEW:
if (args.pid) {
diff --git a/tests/expected/schedutils/coresched-change-cookie-of-parent b/tests/expected/schedutils/coresched-change-cookie-of-parent
index 97ab7ea23..d08118c02 100644
--- a/tests/expected/schedutils/coresched-change-cookie-of-parent
+++ b/tests/expected/schedutils/coresched-change-cookie-of-parent
@@ -1 +1 @@
-coresched: cookie of pid OWN_PID is DIFFERENT_COOKIE
+cookie of pid OWN_PID is DIFFERENT_COOKIE
diff --git a/tests/expected/schedutils/coresched-get-own-pid-no-cookie b/tests/expected/schedutils/coresched-get-own-pid-no-cookie
deleted file mode 100644
index 7f64a98b3..000000000
--- a/tests/expected/schedutils/coresched-get-own-pid-no-cookie
+++ /dev/null
@@ -1 +0,0 @@
-coresched: pid OWN_PID doesn't have a core scheduling cookie
diff --git a/tests/expected/schedutils/coresched-get-own-pid-with-cookie b/tests/expected/schedutils/coresched-get-own-pid-with-cookie
index 556e31a65..3d7f08040 100644
--- a/tests/expected/schedutils/coresched-get-own-pid-with-cookie
+++ b/tests/expected/schedutils/coresched-get-own-pid-with-cookie
@@ -1 +1 @@
-coresched: cookie of pid OWN_PID is COOKIE
+cookie of pid OWN_PID is COOKIE
diff --git a/tests/expected/schedutils/coresched-set-cookie-own-pid b/tests/expected/schedutils/coresched-set-cookie-own-pid.err
similarity index 100%
rename from tests/expected/schedutils/coresched-set-cookie-own-pid
rename to tests/expected/schedutils/coresched-set-cookie-own-pid.err
diff --git a/tests/ts/schedutils/coresched b/tests/ts/schedutils/coresched
index 5381a56f2..ed96d89de 100755
--- a/tests/ts/schedutils/coresched
+++ b/tests/ts/schedutils/coresched
@@ -13,6 +13,14 @@ TS_DESC="coresched"
ts_init "$*"
ts_check_test_command "$TS_CMD_CORESCHED"
+ts_check_test_command "tee"
+ts_check_test_command "sed"
+
+# If there is no kernel support, skip the test suite
+CORESCHED_TEST_KERNEL_SUPPORT_CMD=$($TS_CMD_CORESCHED 2>&1)
+if [[ $CORESCHED_TEST_KERNEL_SUPPORT_CMD == *"CONFIG_SCHED_CORE"* ]]; then
+ ts_skip "Kernel has no CONFIG_SCHED_CORE support"
+fi
# The output of coresched contains PIDs and core scheduling cookies, both of which should be
# assumed to be random values as we have no control over them. The tests replace these values
@@ -22,23 +30,22 @@ ts_check_test_command "$TS_CMD_CORESCHED"
# - Any other cookie is replaced by `DIFFERENT_COOKIE`
# The behavior of coresched does not depend on the exact values of these cookies, so using
# placeholder values does not change the behavior tests.
-ts_init_subtest "get-own-pid-no-cookie"
-$TS_CMD_CORESCHED -p $$ 3>&2 2>&1 1>&3 | sed "s/$$/OWN_PID/g" >> "$TS_OUTPUT"
-ts_finalize_subtest
-
ts_init_subtest "set-cookie-own-pid"
-CORESCHED_OUTPUT=$($TS_CMD_CORESCHED -v -n -p $$ 3>&2 2>&1 1>&3 | sed "s/$$/OWN_PID/g")
+CORESCHED_OUTPUT=$( ($TS_CMD_CORESCHED -v -n -p $$ | tee -a "$TS_OUTPUT") 3>&1 1>&2 2>&3 | sed "s/$$/OWN_PID/g")
CORESCHED_COOKIE=$(echo "$CORESCHED_OUTPUT" | sed 's/^.*\(0x.*$\)/\1/g')
+if [ -z "$CORESCHED_COOKIE" ]; then
+ ts_failed "empty value for CORESCHED_COOKIE "
+fi
CORESCHED_OUTPUT=$(echo "$CORESCHED_OUTPUT" | sed "s/$CORESCHED_COOKIE/COOKIE/g")
-echo "$CORESCHED_OUTPUT" >> "$TS_OUTPUT"
+echo "$CORESCHED_OUTPUT" >> "$TS_ERRLOG"
ts_finalize_subtest
ts_init_subtest "get-own-pid-with-cookie"
-$TS_CMD_CORESCHED -p $$ | sed "s/$$/OWN_PID/g" | sed "s/$CORESCHED_COOKIE/COOKIE/g" >> "$TS_OUTPUT"
+$TS_CMD_CORESCHED -p $$ 2>> "$TS_ERRLOG" | sed "s/$$/OWN_PID/g" | sed "s/$CORESCHED_COOKIE/COOKIE/g" >> "$TS_OUTPUT"
ts_finalize_subtest
ts_init_subtest "spawn-child-with-new-cookie"
-$TS_CMD_CORESCHED -n -- /bin/bash -c "$TS_CMD_CORESCHED -p \$\$" \
+$TS_CMD_CORESCHED -n -- "$TS_CMD_CORESCHED" 2>> "$TS_ERRLOG" \
| sed 's/^.*\(0x.*$\)/\1/g' \
| sed "s/$CORESCHED_COOKIE/SAME_COOKIE/g" \
| sed "s/0x.*$/DIFFERENT_COOKIE/g" >> "$TS_OUTPUT"
@@ -46,7 +53,7 @@ ts_finalize_subtest
ts_init_subtest "change-cookie-of-parent"
$TS_CMD_CORESCHED -n -- /bin/bash -c "$TS_CMD_CORESCHED -c -p \$\$ -d $$"
-$TS_CMD_CORESCHED -p $$ \
+$TS_CMD_CORESCHED -p $$ 2>> "$TS_ERRLOG" \
| sed "s/$$/OWN_PID/g" \
| sed "s/$CORESCHED_COOKIE/COOKIE/g" \
| sed "s/0x.*$/DIFFERENT_COOKIE/g" >> "$TS_OUTPUT"
.gitignore | 1 +
bash-completion/coresched | 0
configure.ac | 12 +-
meson.build | 16 +-
meson_options.txt | 2 +-
schedutils/Makemodule.am | 8 +
schedutils/coresched.1.adoc | 124 ++++++
schedutils/coresched.c | 361 ++++++++++++++++++
tests/commands.sh | 1 +
.../coresched-change-cookie-of-parent | 1 +
.../coresched-get-own-pid-with-cookie | 1 +
.../coresched-set-cookie-own-pid.err | 1 +
.../coresched-spawn-child-with-new-cookie | 1 +
tests/ts/schedutils/coresched | 62 +++
14 files changed, 585 insertions(+), 6 deletions(-)
create mode 100644 bash-completion/coresched
create mode 100644 schedutils/coresched.1.adoc
create mode 100644 schedutils/coresched.c
create mode 100644 tests/expected/schedutils/coresched-change-cookie-of-parent
create mode 100644 tests/expected/schedutils/coresched-get-own-pid-with-cookie
create mode 100644 tests/expected/schedutils/coresched-set-cookie-own-pid.err
create mode 100644 tests/expected/schedutils/coresched-spawn-child-with-new-cookie
create mode 100755 tests/ts/schedutils/coresched
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..a3a648753 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..c83fcdf91
--- /dev/null
+++ b/schedutils/coresched.1.adoc
@@ -0,0 +1,124 @@
+//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
+
+== SYNOPSIS
+
+*{command}* [options] *-p* _pid_
+
+*{command}* [options] \-- _command_ [_argument_...]
+
+== DESCRIPTION
+The *{command}* command is used to retrieve or modify the core scheduling cookies of a running process given its _pid_, or to spawn a new _command_ with core scheduling cookies.
+
+Core scheduling allows you to define groups of tasks that are allowed to share a physical core.
+This is done by assigning a cookie to each task.
+Only tasks have the same cookie are allowed to be scheduled on the same physical core.
+
+It is possible to either assign a new random cookie to a task, or copy a cookie from another task. It is not possible to choose the value of the cookie.
+
+== FUNCTIONS
+*-n*, *--new*::
+Assign a new cookie to an existing PID or to execute _command_ with a new cookie.
+
+*-c*, *--copy*::
+Copy the cookie from an existing PID to another PID, or execute _command_ with that copied cookie.
+
+If no function is specified, it will print the cookie of the current task, or of the task specified with the *-p* option.
+
+== OPTIONS
+*-p*, *--pid* _PID_::
+Operate on an existing PID and do not launch a new task.
+
+*-d*, *--dest* _PID_::
+When using *--copy*, specify a destination PID where you want to copy the cookie to.
+
+*-t*, *--type* _TYPE_::
+The type of the PID whose cookie will be modified. This can be one of three values:
+- *pid*, or process ID
+- *tgid*, or thread group ID (default value)
+- *pgid*, or process group ID
+
+*-v*, *--verbose*::
+Show extra information when modifying cookies of tasks.
+
+*-h*, *--help*::
+Display help text and exit.
+
+*-V*, *--version*::
+Print version and exit.
+
+== EXAMPLES
+Get the core scheduling cookie of the {command} task itself, usually inherited from its parent{colon}::
+*{command}*
+
+Get the core scheduling cookie of a task with PID _123_{colon}::
+*{command} -p* _123_
+
+Give a task with PID _123_ a new core scheduling cookie{colon}::
+*{command} --new -p* _123_
+
+Spawn a new task with a new core scheduling cookie{colon}::
+*{command} --new* \-- _command_ [_argument_...]
+
+Copy the cookie from a task with pid _123_ to another task with pid _456_{colon}::
+*{command} --copy -p* _123_ *-d* _456_
+
+Copy the cookie from a task with pid _123_ to a new task _command_{colon}::
+*{command} --copy -p* _123_ \-- _command_ [_argument_...]
+
+Copy the cookie from a task with pid _123_ to the process group ID _456_{colon}::
+*{command} --copy -p* _123_ *-t* pgid *-d* _456_
+
+== PERMISSIONS
+When retrieving or modifying the core scheduling cookie of a process, you need to have *PTRACE_MODE_READ_REALCREDS* ptrace access to that process.
+See the section "Ptrace access mode checking" in *ptrace*(2) for more information.
+
+== RETURN VALUE
+On success, *{command}* returns 0.
+If *{command}* fails, it will print an error and return 1.
+
+If a _command_ is being executed, the return value of *{command}* will be the return value of _command_.
+
+== NOTES
+*{command}* requires core scheduling support in the kernel.
+This can be enabled via the *CONFIG_SCHED_CORE* kernel config option.
+
+== AUTHORS
+mailto:thijs@raymakers.nl[Thijs Raymakers],
+mailto:pauld@redhat.com[Phil Auld]
+
+== COPYRIGHT
+
+Copyright {copyright} 2024 Thijs Raymakers and Phil Auld. This is free software licensed under the EUPL.
+
+== SEE ALSO
+*chrt*(1),
+*nice*(1),
+*renice*(1),
+*taskset*(1),
+*ptrace*(2),
+*sched*(7)
+
+The Linux kernel source files _Documentation/admin-guide/hw-vuln/core-scheduling.rst_
+
+include::man-common/bugreports.adoc[]
+
+include::man-common/footer.adoc[]
+
+ifdef::translation[]
+include::man-common/translation.adoc[]
+endif::[]
diff --git a/schedutils/coresched.c b/schedutils/coresched.c
new file mode 100644
index 000000000..03b50cd5e
--- /dev/null
+++ b/schedutils/coresched.c
@@ -0,0 +1,361 @@
+/**
+ * SPDX-License-Identifier: EUPL-1.2
+ *
+ * coresched.c - manage core scheduling cookies for tasks
+ *
+ * Copyright (C) 2024 Thijs Raymakers, Phil Auld
+ * Licensed under the EUPL v1.2
+ */
+
+#include <getopt.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <sys/prctl.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "c.h"
+#include "closestream.h"
+#include "nls.h"
+#include "optutils.h"
+#include "strutils.h"
+
+// These definitions might not be defined in the header files, even if the
+// prctl interface in the kernel accepts them as valid.
+#ifndef PR_SCHED_CORE
+#define PR_SCHED_CORE 62
+#endif
+#ifndef PR_SCHED_CORE_GET
+#define PR_SCHED_CORE_GET 0
+#endif
+#ifndef PR_SCHED_CORE_CREATE
+#define PR_SCHED_CORE_CREATE 1
+#endif
+#ifndef PR_SCHED_CORE_SHARE_TO
+#define PR_SCHED_CORE_SHARE_TO 2
+#endif
+#ifndef PR_SCHED_CORE_SHARE_FROM
+#define PR_SCHED_CORE_SHARE_FROM 3
+#endif
+#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 sched_core_scope;
+typedef unsigned long sched_core_cookie;
+typedef enum {
+ SCHED_CORE_CMD_GET,
+ SCHED_CORE_CMD_NEW,
+ SCHED_CORE_CMD_COPY,
+} sched_core_cmd;
+
+struct args {
+ pid_t pid;
+ pid_t dest;
+ sched_core_scope type;
+ sched_core_cmd cmd;
+ int exec_argv_offset;
+};
+
+static bool sched_core_verbose = false;
+
+static void __attribute__((__noreturn__)) usage(void)
+{
+ fputs(USAGE_HEADER, stdout);
+ fprintf(stdout, _(" %s [-p PID]\n"), program_invocation_short_name);
+ fprintf(stdout, _(" %s --new [-t <TYPE>] -p <PID>\n"),
+ program_invocation_short_name);
+ fprintf(stdout, _(" %s --new [-t <TYPE>] -- PROGRAM [ARGS...]\n"),
+ program_invocation_short_name);
+ fprintf(stdout, _(" %s --copy -p <PID> [-t <TYPE>] -d <PID>\n"),
+ program_invocation_short_name);
+ fprintf(stdout,
+ _(" %s --copy -p <PID> [-t <TYPE>] -- PROGRAM [ARGS...]\n"),
+ program_invocation_short_name);
+
+ fputs(USAGE_SEPARATOR, stdout);
+ fputsln(_("Manage core scheduling cookies for tasks."), stdout);
+
+ fputs(USAGE_FUNCTIONS, stdout);
+ fputsln(_(" -n, --new assign a new core scheduling cookie to an existing PID or\n"
+ " execute a program with a new cookie."),
+ stdout);
+ fputsln(_(" -c, --copy copy the core scheduling cookie from an existing PID to\n"
+ " either another PID, or copy it to a new program"),
+ stdout);
+ fputsln(_("\n If no function is provided, it will retrieve and print the cookie from\n"
+ " the PID provided via --pid.\n"),
+ stdout);
+
+ fputs(USAGE_OPTIONS, stdout);
+ fputsln(_(" -p, --pid <PID> operate on an existing PID"), stdout);
+ fputsln(_(" -d, --dest <PID> when copying a cookie from an existing PID, --dest is\n"
+ " the destination PID where to copy the cookie to."),
+ stdout);
+ fputsln(_(" -t, --type <TYPE> type of the destination PID, or the type of the PID\n"
+ " when a new core scheduling cookie is created.\n"
+ " Can be one of the following: pid, tgid or pgid.\n"
+ " The default is tgid."),
+ stdout);
+ fputs(USAGE_SEPARATOR, stdout);
+ fputsln(_(" -v, --verbose verbose"), stdout);
+ fprintf(stdout,
+ USAGE_HELP_OPTIONS(
+ 20)); /* char offset to align option descriptions */
+ fprintf(stdout, USAGE_MAN_TAIL("coresched(1)"));
+ exit(EXIT_SUCCESS);
+}
+
+#define bad_usage(FMT...) \
+ do { \
+ warnx(FMT); \
+ errtryhelp(EXIT_FAILURE); \
+ } while (0)
+
+static sched_core_cookie core_sched_get_cookie(pid_t pid)
+{
+ sched_core_cookie cookie = 0;
+ if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_GET, pid,
+ PR_SCHED_CORE_SCOPE_THREAD, &cookie))
+ err(EXIT_FAILURE, _("Failed to get cookie from PID %d"), pid);
+ return cookie;
+}
+
+static void core_sched_create_cookie(pid_t pid, sched_core_scope type)
+{
+ if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_CREATE, pid, type, 0))
+ err(EXIT_FAILURE, _("Failed to create cookie for PID %d"), pid);
+}
+
+static 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(EXIT_FAILURE, _("Failed to pull cookie from PID %d"), from);
+}
+
+static void core_sched_push_cookie(pid_t to, sched_core_scope type)
+{
+ if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_SHARE_TO, to, type, 0))
+ err(EXIT_FAILURE, _("Failed to push cookie to PID %d"), to);
+}
+
+static void core_sched_copy_cookie(pid_t from, pid_t to,
+ sched_core_scope to_type)
+{
+ core_sched_pull_cookie(from);
+ core_sched_push_cookie(to, to_type);
+
+ if (sched_core_verbose) {
+ sched_core_cookie before = core_sched_get_cookie(from);
+ fprintf(stderr,
+ _("%s: copied cookie 0x%lx from PID %d to PID %d\n"),
+ program_invocation_short_name, before, from, to);
+ }
+}
+
+static void core_sched_get_and_print_cookie(pid_t pid)
+{
+ if (sched_core_verbose) {
+ sched_core_cookie after = core_sched_get_cookie(pid);
+ fprintf(stderr, _("%s: set cookie of PID %d to 0x%lx\n"),
+ program_invocation_short_name, pid, after);
+ }
+}
+
+static 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->pid) {
+ core_sched_pull_cookie(args->pid);
+ core_sched_get_and_print_cookie(args->pid);
+ } else {
+ pid_t pid = getpid();
+ core_sched_create_cookie(pid, args->type);
+ core_sched_get_and_print_cookie(pid);
+ }
+
+ if (execvp(argv[0], argv))
+ errexec(argv[0]);
+}
+
+// If PR_SCHED_CORE is not recognized, or not supported on this system,
+// then prctl will set errno to EINVAL. Assuming all other operands of
+// prctl are valid, we can use errno==EINVAL as a check to see whether
+// core scheduling is available on this system.
+static bool is_core_sched_supported(void)
+{
+ sched_core_cookie cookie = 0;
+ if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_GET, getpid(),
+ PR_SCHED_CORE_SCOPE_THREAD, &cookie))
+ if (errno == EINVAL)
+ return false;
+
+ return true;
+}
+
+static sched_core_scope parse_core_sched_type(char *str)
+{
+ if (!strcmp(str, "pid"))
+ return PR_SCHED_CORE_SCOPE_THREAD;
+ else if (!strcmp(str, "tgid"))
+ return PR_SCHED_CORE_SCOPE_THREAD_GROUP;
+ else if (!strcmp(str, "pgid"))
+ return PR_SCHED_CORE_SCOPE_PROCESS_GROUP;
+
+ bad_usage(_("'%s' is an invalid option. Must be one of pid/tgid/pgid"),
+ str);
+}
+
+static void parse_arguments(int argc, char **argv, struct args *args)
+{
+ int c;
+
+ static const struct option longopts[] = {
+ { "new", no_argument, NULL, 'n' },
+ { "copy", no_argument, NULL, 'c' },
+ { "pid", required_argument, NULL, 'p' },
+ { "dest", required_argument, NULL, 'd' },
+ { "type", required_argument, NULL, 't' },
+ { "verbose", no_argument, NULL, 'v' },
+ { "version", no_argument, NULL, 'V' },
+ { "help", no_argument, NULL, 'h' },
+ { NULL, 0, NULL, 0 }
+ };
+ static const ul_excl_t excl[] = {
+ { 'c', 'n' }, // Cannot do both --new and --copy
+ { 'd', 'n' }, // Cannot have both --new and --dest
+ { 0 }
+ };
+
+ int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
+
+ while ((c = getopt_long(argc, argv, "ncp:d:t:vVh", longopts, NULL)) !=
+ -1) {
+ err_exclusive_options(c, longopts, excl, excl_st);
+ switch (c) {
+ case 'n':
+ args->cmd = SCHED_CORE_CMD_NEW;
+ break;
+ case 'c':
+ args->cmd = SCHED_CORE_CMD_COPY;
+ break;
+ case 'p':
+ args->pid = strtopid_or_err(
+ optarg, _("Failed to parse PID for -p/--pid"));
+ break;
+ case 'd':
+ args->dest = strtopid_or_err(
+ optarg, _("Failed to parse PID for -d/--dest"));
+ break;
+ case 't':
+ args->type = parse_core_sched_type(optarg);
+ break;
+ case 'v':
+ sched_core_verbose = true;
+ break;
+ case 'V':
+ print_version(EXIT_SUCCESS);
+ case 'h':
+ usage();
+ default:
+ errtryhelp(EXIT_FAILURE);
+ }
+ }
+
+ if (args->cmd == SCHED_CORE_CMD_COPY && !args->pid)
+ bad_usage(_("--copy: requires a -p/--pid"));
+
+ // More arguments have been passed, which means that the user wants to run
+ // another program with a core scheduling cookie.
+ if (argc > optind) {
+ switch (args->cmd) {
+ case SCHED_CORE_CMD_GET:
+ bad_usage(_("Unknown command"));
+ break;
+ case SCHED_CORE_CMD_NEW:
+ if (args->pid)
+ bad_usage(_(
+ "--new: cannot accept both a -p/--pid and a command"));
+ else
+ args->exec_argv_offset = optind;
+ break;
+ case SCHED_CORE_CMD_COPY:
+ if (args->dest)
+ bad_usage(_(
+ "--copy: cannot accept both a destination PID "
+ "-d/--dest and a command"));
+ else
+ args->exec_argv_offset = optind;
+ break;
+ }
+ }
+
+ if (argc <= optind) {
+ if (args->cmd == SCHED_CORE_CMD_NEW && !args->pid)
+ bad_usage(_(
+ "--new: requires either a -p/--pid or a command"));
+ if (args->cmd == SCHED_CORE_CMD_COPY && !args->dest)
+ bad_usage(_(
+ "--copy: requires either a -d/--dest or a command"));
+ }
+}
+
+int main(int argc, char **argv)
+{
+ struct args args = { 0 };
+ args.cmd = SCHED_CORE_CMD_GET;
+ args.type = PR_SCHED_CORE_SCOPE_THREAD_GROUP;
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ close_stdout_atexit();
+
+ parse_arguments(argc, argv, &args);
+
+ if (!is_core_sched_supported())
+ errx(EXIT_FAILURE,
+ _("Does your kernel support CONFIG_SCHED_CORE?"));
+
+ sched_core_cookie cookie;
+ pid_t pid;
+
+ switch (args.cmd) {
+ case SCHED_CORE_CMD_GET:
+ pid = args.pid ? args.pid : getpid();
+ cookie = core_sched_get_cookie(pid);
+ printf(_("cookie of pid %d is 0x%lx\n"), pid, cookie);
+ break;
+ case SCHED_CORE_CMD_NEW:
+ if (args.pid) {
+ core_sched_create_cookie(args.pid, args.type);
+ core_sched_get_and_print_cookie(args.pid);
+ } else {
+ core_sched_exec_with_cookie(&args, argv);
+ }
+ break;
+ case SCHED_CORE_CMD_COPY:
+ if (args.dest)
+ core_sched_copy_cookie(args.pid, args.dest, args.type);
+ else
+ core_sched_exec_with_cookie(&args, argv);
+ break;
+ default:
+ usage();
+ }
+}
diff --git a/tests/commands.sh b/tests/commands.sh
index 5674c5ff0..9eef92ccb 100644
--- a/tests/commands.sh
+++ b/tests/commands.sh
@@ -71,6 +71,7 @@ TS_CMD_COLCRT=${TS_CMD_COLCRT:-"${ts_commandsdir}colcrt"}
TS_CMD_COLRM=${TS_CMD_COLRM:-"${ts_commandsdir}colrm"}
TS_CMD_COL=${TS_CMD_COL:-"${ts_commandsdir}col"}
TS_CMD_COLUMN=${TS_CMD_COLUMN:-"${ts_commandsdir}column"}
+TS_CMD_CORESCHED=${TS_CMD_CORESCHED:-"${ts_commandsdir}coresched"}
TS_CMD_ENOSYS=${TS_CMD_ENOSYS-"${ts_commandsdir}enosys"}
TS_CMD_EJECT=${TS_CMD_EJECT-"${ts_commandsdir}eject"}
TS_CMD_EXCH=${TS_CMD_EXCH-"${ts_commandsdir}exch"}
diff --git a/tests/expected/schedutils/coresched-change-cookie-of-parent b/tests/expected/schedutils/coresched-change-cookie-of-parent
new file mode 100644
index 000000000..d08118c02
--- /dev/null
+++ b/tests/expected/schedutils/coresched-change-cookie-of-parent
@@ -0,0 +1 @@
+cookie of pid OWN_PID is DIFFERENT_COOKIE
diff --git a/tests/expected/schedutils/coresched-get-own-pid-with-cookie b/tests/expected/schedutils/coresched-get-own-pid-with-cookie
new file mode 100644
index 000000000..3d7f08040
--- /dev/null
+++ b/tests/expected/schedutils/coresched-get-own-pid-with-cookie
@@ -0,0 +1 @@
+cookie of pid OWN_PID is COOKIE
diff --git a/tests/expected/schedutils/coresched-set-cookie-own-pid.err b/tests/expected/schedutils/coresched-set-cookie-own-pid.err
new file mode 100644
index 000000000..1816a4fbd
--- /dev/null
+++ b/tests/expected/schedutils/coresched-set-cookie-own-pid.err
@@ -0,0 +1 @@
+coresched: set cookie of PID OWN_PID to COOKIE
diff --git a/tests/expected/schedutils/coresched-spawn-child-with-new-cookie b/tests/expected/schedutils/coresched-spawn-child-with-new-cookie
new file mode 100644
index 000000000..5b9c40052
--- /dev/null
+++ b/tests/expected/schedutils/coresched-spawn-child-with-new-cookie
@@ -0,0 +1 @@
+DIFFERENT_COOKIE
diff --git a/tests/ts/schedutils/coresched b/tests/ts/schedutils/coresched
new file mode 100755
index 000000000..ed96d89de
--- /dev/null
+++ b/tests/ts/schedutils/coresched
@@ -0,0 +1,62 @@
+#!/bin/bash
+# SPDX-License-Identifier: EUPL-1.2
+#
+# This file is part of util-linux
+#
+# Copyright (C) 2024 Thijs Raymakers
+# Licensed under the EUPL v1.2
+
+TS_TOPDIR="${0%/*}/../.."
+TS_DESC="coresched"
+
+. "$TS_TOPDIR"/functions.sh
+ts_init "$*"
+
+ts_check_test_command "$TS_CMD_CORESCHED"
+ts_check_test_command "tee"
+ts_check_test_command "sed"
+
+# If there is no kernel support, skip the test suite
+CORESCHED_TEST_KERNEL_SUPPORT_CMD=$($TS_CMD_CORESCHED 2>&1)
+if [[ $CORESCHED_TEST_KERNEL_SUPPORT_CMD == *"CONFIG_SCHED_CORE"* ]]; then
+ ts_skip "Kernel has no CONFIG_SCHED_CORE support"
+fi
+
+# The output of coresched contains PIDs and core scheduling cookies, both of which should be
+# assumed to be random values as we have no control over them. The tests replace these values
+# with sed before writing them to the output file, so it can match the expected output file.
+# - The PID of this bash script is replaced with the placeholder `OWN_PID`
+# - The core scheduling cookie of this bash script is replaced by `COOKIE`
+# - Any other cookie is replaced by `DIFFERENT_COOKIE`
+# The behavior of coresched does not depend on the exact values of these cookies, so using
+# placeholder values does not change the behavior tests.
+ts_init_subtest "set-cookie-own-pid"
+CORESCHED_OUTPUT=$( ($TS_CMD_CORESCHED -v -n -p $$ | tee -a "$TS_OUTPUT") 3>&1 1>&2 2>&3 | sed "s/$$/OWN_PID/g")
+CORESCHED_COOKIE=$(echo "$CORESCHED_OUTPUT" | sed 's/^.*\(0x.*$\)/\1/g')
+if [ -z "$CORESCHED_COOKIE" ]; then
+ ts_failed "empty value for CORESCHED_COOKIE "
+fi
+CORESCHED_OUTPUT=$(echo "$CORESCHED_OUTPUT" | sed "s/$CORESCHED_COOKIE/COOKIE/g")
+echo "$CORESCHED_OUTPUT" >> "$TS_ERRLOG"
+ts_finalize_subtest
+
+ts_init_subtest "get-own-pid-with-cookie"
+$TS_CMD_CORESCHED -p $$ 2>> "$TS_ERRLOG" | sed "s/$$/OWN_PID/g" | sed "s/$CORESCHED_COOKIE/COOKIE/g" >> "$TS_OUTPUT"
+ts_finalize_subtest
+
+ts_init_subtest "spawn-child-with-new-cookie"
+$TS_CMD_CORESCHED -n -- "$TS_CMD_CORESCHED" 2>> "$TS_ERRLOG" \
+ | sed 's/^.*\(0x.*$\)/\1/g' \
+ | sed "s/$CORESCHED_COOKIE/SAME_COOKIE/g" \
+ | sed "s/0x.*$/DIFFERENT_COOKIE/g" >> "$TS_OUTPUT"
+ts_finalize_subtest
+
+ts_init_subtest "change-cookie-of-parent"
+$TS_CMD_CORESCHED -n -- /bin/bash -c "$TS_CMD_CORESCHED -c -p \$\$ -d $$"
+$TS_CMD_CORESCHED -p $$ 2>> "$TS_ERRLOG" \
+ | sed "s/$$/OWN_PID/g" \
+ | sed "s/$CORESCHED_COOKIE/COOKIE/g" \
+ | sed "s/0x.*$/DIFFERENT_COOKIE/g" >> "$TS_OUTPUT"
+ts_finalize_subtest
+
+ts_finalize
--
2.44.0
^ permalink raw reply related
* Re: [PATCH v4] coresched: Manage core scheduling cookies for tasks
From: Phil Auld @ 2024-04-05 14:14 UTC (permalink / raw)
To: Thomas Weißschuh; +Cc: Thijs Raymakers, kzak, util-linux
In-Reply-To: <799c60ac-1ad5-4aca-8ca3-2408f1aefce2@t-8ch.de>
On Fri, Apr 05, 2024 at 08:26:08AM +0200 Thomas Weißschuh wrote:
> On 2024-04-05 00:03:57+0200, Thijs Raymakers wrote:
> > Co-authored-by: Phil Auld <pauld@redhat.com>
> > Signed-off-by: Thijs Raymakers <thijs@raymakers.nl>
> > Signed-off-by: Phil Auld <pauld@redhat.com>
> > ---
> >
> > Hi Thomas,
> >
> > Thanks for your comments. This patch addresses most of your remarks. It
> > also adds Phil Auld as a Co-author since we went back and forth on the
> > design quite a lot. His feedback has been valuable and his ideas have
> > been integrated in the util to accomodate for both our usecases.
>
> Thanks, some more comments below.
>
> > Op 01-04-2024 om 7:18 p.m. schreef Thomas Weißschuh:
> > > I guess you are aware, but the manpage is empty.
> >
> > I've added the manpage in this version of the patch.
> >
> > > Technically all types ending in _t are reserved for libc.
> > > And a conflict for cookie_t doesn't even sound that improbable.
> > > How about sched_core_scope, sched_core_cookie and enum sched_core_cmd?
> >
> > Good suggestion, the names of these types have been changed.
> >
> > > Would a single call to core_sched_get_cookie() not be a reliable check
> > > that can be done and reported once?
> > > ...
> > > To make clear that this works like err{,x}() maybe call it err_prctl().
> > > It's unfortunate that this has to be a macro.
> > > There are verrx() and verr() which could make this an inline function.
> > > We would need fallback implementations for it in include/c.h though,
> > > which doesn't look so hard.
> >
> > I Initially wrote it like this to avoid doing more prctl calls than
> > necessary. All the PR_SCHED_CORE* prctl calls indicate whether or not
> > the kernel supports core scheduling whenever they return with errno
> > EINVAL.
> >
> > However, if support is explicitly checked beforehand, then the error
> > handling becomes a bit simpler and doesn't rely on macros anymore.
> > Therefore, I've changed to code to explicitly check for kernel support
> > beforehand. This change also removes the need for the `check_prctl`
> > macro.
>
> Thanks, would it make sense to expose this check via the CLI?
> That could then used by the tests to skip themselves if the system does
> not provide core scheduling on old kernels or qemu-user.
>
I don't think so. It's checking by doing an operation so just do a "get"
if you really want to check first. An explicit check would just be that
anyway.
> > > It would be nicer if these helpers are closer named to the prctl
> > > operations.
> > > sched_core_share_from_thread() for example.
> >
> > I've followed the terminology of the kernel documentation of core
> > scheduling (Documentation/admin-guide/hw-vuln/core-scheduling.rst) here.
> > There, `PR_SCHED_CORE_SHARE_TO` is described as "push core_sched cookie
> > to pid." and `PR_SCHED_CORE_SHARE_FROM` is described as "pull core_sched
> > cookie from pid"
> >
> > I prefer that terminology because I find it a little less ambiguous than
> > SHARE_TO and SHARE_FROM.
>
> Ok.
>
> > Op 01-04-2024 om 7:18 p.m. schreef Thomas Weißschuh:
> > > On 2024-03-27 16:30:08+0100, Thijs Raymakers wrote:
> > >> + printf("%s: copied cookie 0x%lx from PID %d to PID %d\n",
> > >> + program_invocation_short_name, before, from, to);
> > > stderr and only with --verbose?
> >
> > This has been changed now. All operations, except getting a cookie,
> > will no longer print this type of information unless --verbose is passed.
> >
> > > Also, please some tests.
> >
> > This version adds 5 tests for coresched:
> > - getting the cookie of itself when no cookie is set
> > - setting the cookie of itself
> > - getting the cookie of itself when the cookie is set (must be equal to
> > the cookie that was set)
> > - spawn a child process with a new cookie (must be different than the
> > cookie of the parent process)
> > - spawn a child process with a new cookie and push that cookie to the
> > parent process (afterwards, the cookie of the parent must be different)
>
> Nice!
>
> > An interdiff between the previous version and this version is listed
> > below. It also address your other remarks not mentioned in this email.
> >
> > Only the bash completion script is missing, but I think that is better
> > suited for a follow-up patch.
>
> Sounds fair.
>
> > Thijs
> >
> > Interdiff against v3:
>
> [snip]
>
> > .gitignore | 1 +
> > bash-completion/coresched | 0
> > configure.ac | 12 +-
> > meson.build | 16 +-
> > meson_options.txt | 2 +-
> > schedutils/Makemodule.am | 8 +
> > schedutils/coresched.1.adoc | 116 ++++++
> > schedutils/coresched.c | 380 ++++++++++++++++++
> > tests/commands.sh | 1 +
> > .../coresched-change-cookie-of-parent | 1 +
> > .../coresched-get-own-pid-no-cookie | 1 +
> > .../coresched-get-own-pid-with-cookie | 1 +
> > .../schedutils/coresched-set-cookie-own-pid | 1 +
> > .../coresched-spawn-child-with-new-cookie | 1 +
> > tests/ts/schedutils/coresched | 55 +++
> > 15 files changed, 590 insertions(+), 6 deletions(-)
> > create mode 100644 bash-completion/coresched
> > create mode 100644 schedutils/coresched.1.adoc
> > create mode 100644 schedutils/coresched.c
> > create mode 100644 tests/expected/schedutils/coresched-change-cookie-of-parent
> > create mode 100644 tests/expected/schedutils/coresched-get-own-pid-no-cookie
> > create mode 100644 tests/expected/schedutils/coresched-get-own-pid-with-cookie
> > create mode 100644 tests/expected/schedutils/coresched-set-cookie-own-pid
> > create mode 100644 tests/expected/schedutils/coresched-spawn-child-with-new-cookie
> > create mode 100755 tests/ts/schedutils/coresched
>
> [snip]
>
> > diff --git a/schedutils/coresched.c b/schedutils/coresched.c
> > new file mode 100644
> > index 000000000..3d9392b1a
> > --- /dev/null
> > +++ b/schedutils/coresched.c
> > @@ -0,0 +1,380 @@
> > +/**
> > + * SPDX-License-Identifier: EUPL-1.2
> > + *
> > + * coresched.c - manage core scheduling cookies for tasks
> > + *
> > + * Copyright (C) 2024 Thijs Raymakers, Phil Auld
> > + * Licensed under the EUPL v1.2
> > + */
> > +
> > +#include <getopt.h>
> > +#include <stdbool.h>
> > +#include <stdio.h>
> > +#include <sys/prctl.h>
> > +#include <sys/wait.h>
> > +#include <unistd.h>
> > +
> > +#include "c.h"
> > +#include "closestream.h"
> > +#include "nls.h"
> > +#include "optutils.h"
> > +#include "strutils.h"
> > +
> > +// These definitions might not be defined in the header files, even if the
> > +// prctl interface in the kernel accepts them as valid.
> > +#ifndef PR_SCHED_CORE
> > +#define PR_SCHED_CORE 62
> > +#endif
> > +#ifndef PR_SCHED_CORE_GET
> > +#define PR_SCHED_CORE_GET 0
> > +#endif
> > +#ifndef PR_SCHED_CORE_CREATE
> > +#define PR_SCHED_CORE_CREATE 1
> > +#endif
> > +#ifndef PR_SCHED_CORE_SHARE_TO
> > +#define PR_SCHED_CORE_SHARE_TO 2
> > +#endif
> > +#ifndef PR_SCHED_CORE_SHARE_FROM
> > +#define PR_SCHED_CORE_SHARE_FROM 3
> > +#endif
> > +#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 sched_core_scope;
> > +typedef unsigned long sched_core_cookie;
> > +typedef enum {
> > + SCHED_CORE_CMD_GET,
> > + SCHED_CORE_CMD_NEW,
> > + SCHED_CORE_CMD_COPY,
> > +} sched_core_cmd;
> > +
> > +struct args {
> > + pid_t pid;
> > + pid_t dest;
> > + sched_core_scope type;
> > + sched_core_cmd cmd;
> > + int exec_argv_offset;
> > +};
> > +
> > +static bool sched_core_verbose = false;
> > +
> > +static void __attribute__((__noreturn__)) usage(void)
> > +{
> > + fputs(USAGE_HEADER, stdout);
> > + fprintf(stdout, _(" %s [-p PID]\n"), program_invocation_short_name);
> > + fprintf(stdout, _(" %s --new [-t <TYPE>] -p <PID>\n"),
> > + program_invocation_short_name);
> > + fprintf(stdout, _(" %s --new [-t <TYPE>] -- PROGRAM [ARGS...]\n"),
> > + program_invocation_short_name);
> > + fprintf(stdout, _(" %s --copy -p <PID> [-t <TYPE>] -d <PID>\n"),
> > + program_invocation_short_name);
> > + fprintf(stdout,
> > + _(" %s --copy -p <PID> [-t <TYPE>] -- PROGRAM [ARGS...]\n"),
> > + program_invocation_short_name);
> > +
> > + fputs(USAGE_SEPARATOR, stdout);
> > + fputsln(_("Manage core scheduling cookies for tasks."), stdout);
> > +
> > + fputs(USAGE_FUNCTIONS, stdout);
> > + fputsln(_(" -n, --new assign a new core scheduling cookie to an existing PID or\n"
> > + " execute a program with a new cookie."),
> > + stdout);
> > + fputsln(_(" -c, --copy copy the core scheduling cookie from an existing PID to\n"
> > + " either another PID, or copy it to a new program"),
> > + stdout);
> > + fputsln(_("\n If no function is provided, it will retrieve and print the cookie from\n"
> > + " the PID provided via --pid.\n"),
> > + stdout);
> > +
> > + fputs(USAGE_OPTIONS, stdout);
> > + fputsln(_(" -p, --pid <PID> operate on an existing PID"), stdout);
> > + fputsln(_(" -d, --dest <PID> when copying a cookie from an existing PID, --dest is\n"
> > + " the destination PID where to copy the cookie to."),
> > + stdout);
> > + fputsln(_(" -t, --type <TYPE> type of the destination PID, or the type of the PID\n"
> > + " when a new core scheduling cookie is created.\n"
> > + " Can be one of the following: pid, tgid or pgid.\n"
> > + " The default is tgid."),
> > + stdout);
> > + fputs(USAGE_SEPARATOR, stdout);
> > + fputsln(_(" -v, --verbose verbose"), stdout);
> > + fprintf(stdout,
> > + USAGE_HELP_OPTIONS(
> > + 20)); /* char offset to align option descriptions */
> > + fprintf(stdout, USAGE_MAN_TAIL("coresched(1)"));
> > + exit(EXIT_SUCCESS);
> > +}
> > +
> > +#define bad_usage(FMT...) \
> > + warnx(FMT); \
> > + errtryhelp(EXIT_FAILURE);
> > +
> > +static sched_core_cookie core_sched_get_cookie(pid_t pid)
> > +{
> > + sched_core_cookie cookie = 0;
> > + if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_GET, pid,
> > + PR_SCHED_CORE_SCOPE_THREAD, &cookie)) {
> > + err(EXIT_FAILURE, _("Failed to get cookie from PID %d"), pid);
> > + }
> > + return cookie;
> > +}
> > +
> > +static void core_sched_create_cookie(pid_t pid, sched_core_scope type)
> > +{
> > + if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_CREATE, pid, type, 0)) {
> > + err(EXIT_FAILURE, _("Failed to create cookie for PID %d"), pid);
> > + }
>
> General codestyle is to leave out braces around single-statement blocks.
> (I mentioned this before, if you want to keep them please mention it)
>
> > +}
> > +
> > +static 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(EXIT_FAILURE, _("Failed to pull cookie from PID %d"), from);
> > + }
> > +}
> > +
> > +static void core_sched_push_cookie(pid_t to, sched_core_scope type)
> > +{
> > + if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_SHARE_TO, to, type, 0)) {
> > + err(EXIT_FAILURE, _("Failed to push cookie to PID %d"), to);
> > + }
> > +}
> > +
> > +static void core_sched_copy_cookie(pid_t from, pid_t to,
> > + sched_core_scope to_type)
> > +{
> > + core_sched_pull_cookie(from);
> > + core_sched_push_cookie(to, to_type);
> > +
> > + if (sched_core_verbose) {
> > + sched_core_cookie before = core_sched_get_cookie(from);
> > + fprintf(stderr,
> > + _("%s: copied cookie 0x%lx from PID %d to PID %d\n"),
> > + program_invocation_short_name, before, from, to);
> > + }
> > +}
> > +
> > +static void core_sched_get_and_print_cookie(pid_t pid)
> > +{
> > + if (sched_core_verbose) {
> > + sched_core_cookie after = core_sched_get_cookie(pid);
> > + fprintf(stderr, _("%s: set cookie of PID %d to 0x%lx\n"),
> > + program_invocation_short_name, pid, after);
> > + }
> > +}
> > +
> > +static 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->pid) {
> > + core_sched_pull_cookie(args->pid);
> > + core_sched_get_and_print_cookie(args->pid);
> > + } else {
> > + pid_t pid = getpid();
> > + core_sched_create_cookie(pid, args->type);
> > + core_sched_get_and_print_cookie(pid);
> > + }
> > +
> > + if (execvp(argv[0], argv)) {
> > + errexec(argv[0]);
> > + }
> > +}
> > +
> > +// If PR_SCHED_CORE is not recognized, or not supported on this system,
> > +// then prctl will set errno to EINVAL. Assuming all other operands of
> > +// prctl are valid, we can use errno==EINVAL as a check to see whether
> > +// core scheduling is available on this system.
> > +static bool is_core_sched_supported(void)
> > +{
> > + sched_core_cookie cookie = 0;
> > + if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_GET, getpid(),
> > + PR_SCHED_CORE_SCOPE_THREAD, &cookie)) {
> > + if (errno == EINVAL) {
> > + return false;
> > + }
> > + }
> > + return true;
> > +}
> > +
> > +static sched_core_scope parse_core_sched_type(char *str)
> > +{
> > + if (!strncmp(str, "pid", 4))
>
> These can be plain strcmp().
>
> > + return PR_SCHED_CORE_SCOPE_THREAD;
> > + else if (!strncmp(str, "tgid", 5))
> > + return PR_SCHED_CORE_SCOPE_THREAD_GROUP;
> > + else if (!strncmp(str, "pgid", 5))
> > + return PR_SCHED_CORE_SCOPE_PROCESS_GROUP;
> > +
> > + bad_usage(_("'%s' is an invalid option. Must be one of pid/tgid/pgid"),
> > + str);
> > +}
> > +
> > +static void parse_arguments(int argc, char **argv, struct args *args)
> > +{
> > + int c;
> > +
> > + static const struct option longopts[] = {
> > + { "new", no_argument, NULL, 'n' },
> > + { "copy", no_argument, NULL, 'c' },
> > + { "pid", required_argument, NULL, 'p' },
> > + { "dest", required_argument, NULL, 'd' },
> > + { "type", required_argument, NULL, 't' },
> > + { "verbose", no_argument, NULL, 'v' },
> > + { "version", no_argument, NULL, 'V' },
> > + { "help", no_argument, NULL, 'h' },
> > + { NULL, 0, NULL, 0 }
> > + };
> > + static const ul_excl_t excl[] = {
> > + { 'c', 'n' }, // Cannot do both --new and --copy
> > + { 'd', 'n' }, // Cannot have both --new and --dest
> > + { 0 }
> > + };
> > +
> > + int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
> > +
> > + while ((c = getopt_long(argc, argv, "ncp:d:t:vVh", longopts, NULL)) !=
> > + -1) {
> > + err_exclusive_options(c, longopts, excl, excl_st);
> > + switch (c) {
> > + case 'n':
> > + args->cmd = SCHED_CORE_CMD_NEW;
> > + break;
> > + case 'c':
> > + args->cmd = SCHED_CORE_CMD_COPY;
> > + break;
> > + case 'p':
> > + args->pid = strtopid_or_err(
> > + optarg, _("Failed to parse PID for -p/--pid"));
> > + break;
> > + case 'd':
> > + args->dest = strtopid_or_err(
> > + optarg, _("Failed to parse PID for -d/--dest"));
> > + break;
> > + case 't':
> > + args->type = parse_core_sched_type(optarg);
> > + break;
> > + case 'v':
> > + sched_core_verbose = true;
> > + break;
> > + case 'V':
> > + print_version(EXIT_SUCCESS);
> > + case 'h':
> > + usage();
> > + default:
> > + errtryhelp(EXIT_FAILURE);
> > + }
> > + }
> > +
> > + if (args->cmd == SCHED_CORE_CMD_COPY && !args->pid) {
> > + bad_usage(_("--copy: requires a -p/--pid"));
> > + }
> > +
> > + // More arguments have been passed, which means that the user wants to run
> > + // another program with a core scheduling cookie.
> > + if (argc > optind) {
> > + switch (args->cmd) {
> > + case SCHED_CORE_CMD_GET:
> > + bad_usage(_("Unknown command"));
> > + break;
> > + case SCHED_CORE_CMD_NEW:
> > + if (args->pid) {
> > + bad_usage(_(
> > + "--new: cannot accept both a -p/--pid and a command"));
> > + } else {
> > + args->exec_argv_offset = optind;
> > + }
> > + break;
> > + case SCHED_CORE_CMD_COPY:
> > + if (args->dest) {
> > + bad_usage(_(
> > + "--copy: cannot accept both a destination PID "
> > + "-d/--dest and a command"))
> > + } else {
> > + args->exec_argv_offset = optind;
> > + }
> > + break;
> > + }
> > + }
> > +
> > + if (argc <= optind) {
> > + if (args->cmd == SCHED_CORE_CMD_NEW && !args->pid) {
> > + bad_usage(_(
> > + "--new: requires either a -p/--pid or a command"));
> > + }
> > + if (args->cmd == SCHED_CORE_CMD_COPY && !args->dest) {
> > + bad_usage(_(
> > + "--copy: requires either a -d/--dest or a command"));
> > + }
> > + }
> > +}
> > +
> > +int main(int argc, char **argv)
> > +{
> > + struct args args = { 0 };
> > + args.cmd = SCHED_CORE_CMD_GET;
> > + args.type = PR_SCHED_CORE_SCOPE_THREAD_GROUP;
> > +
> > + setlocale(LC_ALL, "");
> > + bindtextdomain(PACKAGE, LOCALEDIR);
> > + textdomain(PACKAGE);
> > + close_stdout_atexit();
> > +
> > + parse_arguments(argc, argv, &args);
> > +
> > + if (!is_core_sched_supported()) {
> > + errx(ENOTSUP, _("Does your kernel support CONFIG_SCHED_CORE?"));
>
> Afaik errnos are not guaranteed to have the same numeric value
> everywhere. If you want to signal something special, use your own
> constant (and document it in the manpage).
>
I don't think we should make up an errno but use one that already exists.
If not ENOPTSUP we can just leave it as EINVAL I suppose. That should be
the same everywhere.
> > + }
> > +
> > + sched_core_cookie cookie;
> > +
> > + switch (args.cmd) {
> > + case SCHED_CORE_CMD_GET:
> > + if (args.pid) {
> > + cookie = core_sched_get_cookie(args.pid);
> > + if (cookie) {
> > + printf(_("%s: cookie of pid %d is 0x%lx\n"),
> > + program_invocation_short_name, args.pid,
> > + cookie);
>
> program_invocation_short_name seems unnecessary.
>
> The pid itself also seems unnecessary as the users know which cookie
> they asked for.
>
> Ah, taskset works like this. I disagree :-)
>
Yes, trying to make it sort of obvious for taskset users since I think
they will mostly be the same people.
> > + } else {
> > + errx(ENODATA,
> > + _("pid %d doesn't have a core scheduling cookie"),
> > + args.pid);
>
> Is this really a failure case?
> I would make the print only on --verbose and otherwise succeed without
> output.
Agree. This is not an error.
Cheers,
Phil
>
> > + }
> > + } else {
> > + usage();
>
> Why not print the current cookie here?
>
> > + }
> > + break;
> > + case SCHED_CORE_CMD_NEW:
> > + if (args.pid) {
> > + core_sched_create_cookie(args.pid, args.type);
> > + core_sched_get_and_print_cookie(args.pid);
> > + } else {
> > + core_sched_exec_with_cookie(&args, argv);
> > + }
> > + break;
> > + case SCHED_CORE_CMD_COPY:
> > + if (args.dest)
> > + core_sched_copy_cookie(args.pid, args.dest, args.type);
> > + else
> > + core_sched_exec_with_cookie(&args, argv);
> > + break;
> > + default:
> > + usage();
> > + }
> > +}
> > diff --git a/tests/commands.sh b/tests/commands.sh
> > index 5674c5ff0..9eef92ccb 100644
> > --- a/tests/commands.sh
> > +++ b/tests/commands.sh
> > @@ -71,6 +71,7 @@ TS_CMD_COLCRT=${TS_CMD_COLCRT:-"${ts_commandsdir}colcrt"}
> > TS_CMD_COLRM=${TS_CMD_COLRM:-"${ts_commandsdir}colrm"}
> > TS_CMD_COL=${TS_CMD_COL:-"${ts_commandsdir}col"}
> > TS_CMD_COLUMN=${TS_CMD_COLUMN:-"${ts_commandsdir}column"}
> > +TS_CMD_CORESCHED=${TS_CMD_CORESCHED:-"${ts_commandsdir}coresched"}
> > TS_CMD_ENOSYS=${TS_CMD_ENOSYS-"${ts_commandsdir}enosys"}
> > TS_CMD_EJECT=${TS_CMD_EJECT-"${ts_commandsdir}eject"}
> > TS_CMD_EXCH=${TS_CMD_EXCH-"${ts_commandsdir}exch"}
> > diff --git a/tests/expected/schedutils/coresched-change-cookie-of-parent b/tests/expected/schedutils/coresched-change-cookie-of-parent
> > new file mode 100644
> > index 000000000..97ab7ea23
> > --- /dev/null
> > +++ b/tests/expected/schedutils/coresched-change-cookie-of-parent
> > @@ -0,0 +1 @@
> > +coresched: cookie of pid OWN_PID is DIFFERENT_COOKIE
> > diff --git a/tests/expected/schedutils/coresched-get-own-pid-no-cookie b/tests/expected/schedutils/coresched-get-own-pid-no-cookie
> > new file mode 100644
> > index 000000000..7f64a98b3
> > --- /dev/null
> > +++ b/tests/expected/schedutils/coresched-get-own-pid-no-cookie
> > @@ -0,0 +1 @@
> > +coresched: pid OWN_PID doesn't have a core scheduling cookie
> > diff --git a/tests/expected/schedutils/coresched-get-own-pid-with-cookie b/tests/expected/schedutils/coresched-get-own-pid-with-cookie
> > new file mode 100644
> > index 000000000..556e31a65
> > --- /dev/null
> > +++ b/tests/expected/schedutils/coresched-get-own-pid-with-cookie
> > @@ -0,0 +1 @@
> > +coresched: cookie of pid OWN_PID is COOKIE
> > diff --git a/tests/expected/schedutils/coresched-set-cookie-own-pid b/tests/expected/schedutils/coresched-set-cookie-own-pid
> > new file mode 100644
> > index 000000000..1816a4fbd
> > --- /dev/null
> > +++ b/tests/expected/schedutils/coresched-set-cookie-own-pid
> > @@ -0,0 +1 @@
> > +coresched: set cookie of PID OWN_PID to COOKIE
> > diff --git a/tests/expected/schedutils/coresched-spawn-child-with-new-cookie b/tests/expected/schedutils/coresched-spawn-child-with-new-cookie
> > new file mode 100644
> > index 000000000..5b9c40052
> > --- /dev/null
> > +++ b/tests/expected/schedutils/coresched-spawn-child-with-new-cookie
> > @@ -0,0 +1 @@
> > +DIFFERENT_COOKIE
> > diff --git a/tests/ts/schedutils/coresched b/tests/ts/schedutils/coresched
> > new file mode 100755
> > index 000000000..5381a56f2
> > --- /dev/null
> > +++ b/tests/ts/schedutils/coresched
> > @@ -0,0 +1,55 @@
> > +#!/bin/bash
> > +# SPDX-License-Identifier: EUPL-1.2
> > +#
> > +# This file is part of util-linux
> > +#
> > +# Copyright (C) 2024 Thijs Raymakers
> > +# Licensed under the EUPL v1.2
> > +
> > +TS_TOPDIR="${0%/*}/../.."
> > +TS_DESC="coresched"
> > +
> > +. "$TS_TOPDIR"/functions.sh
> > +ts_init "$*"
> > +
> > +ts_check_test_command "$TS_CMD_CORESCHED"
> > +
> > +# The output of coresched contains PIDs and core scheduling cookies, both of which should be
> > +# assumed to be random values as we have no control over them. The tests replace these values
> > +# with sed before writing them to the output file, so it can match the expected output file.
> > +# - The PID of this bash script is replaced with the placeholder `OWN_PID`
> > +# - The core scheduling cookie of this bash script is replaced by `COOKIE`
> > +# - Any other cookie is replaced by `DIFFERENT_COOKIE`
> > +# The behavior of coresched does not depend on the exact values of these cookies, so using
> > +# placeholder values does not change the behavior tests.
> > +ts_init_subtest "get-own-pid-no-cookie"
> > +$TS_CMD_CORESCHED -p $$ 3>&2 2>&1 1>&3 | sed "s/$$/OWN_PID/g" >> "$TS_OUTPUT"
>
> This test fails if the process starting it already has a cookie set.
> Also in that case the output is printed past the redirections.
>
> > +ts_finalize_subtest
> > +
> > +ts_init_subtest "set-cookie-own-pid"
> > +CORESCHED_OUTPUT=$($TS_CMD_CORESCHED -v -n -p $$ 3>&2 2>&1 1>&3 | sed "s/$$/OWN_PID/g")
> > +CORESCHED_COOKIE=$(echo "$CORESCHED_OUTPUT" | sed 's/^.*\(0x.*$\)/\1/g')
> > +CORESCHED_OUTPUT=$(echo "$CORESCHED_OUTPUT" | sed "s/$CORESCHED_COOKIE/COOKIE/g")
> > +echo "$CORESCHED_OUTPUT" >> "$TS_OUTPUT"
> > +ts_finalize_subtest
> > +
> > +ts_init_subtest "get-own-pid-with-cookie"
> > +$TS_CMD_CORESCHED -p $$ | sed "s/$$/OWN_PID/g" | sed "s/$CORESCHED_COOKIE/COOKIE/g" >> "$TS_OUTPUT"
> > +ts_finalize_subtest
> > +
> > +ts_init_subtest "spawn-child-with-new-cookie"
> > +$TS_CMD_CORESCHED -n -- /bin/bash -c "$TS_CMD_CORESCHED -p \$\$" \
>
> When coresched without arguments prints the current value this bash
> trickery can be removed.
>
> > + | sed 's/^.*\(0x.*$\)/\1/g' \
> > + | sed "s/$CORESCHED_COOKIE/SAME_COOKIE/g" \
> > + | sed "s/0x.*$/DIFFERENT_COOKIE/g" >> "$TS_OUTPUT"
> > +ts_finalize_subtest
> > +
> > +ts_init_subtest "change-cookie-of-parent"
> > +$TS_CMD_CORESCHED -n -- /bin/bash -c "$TS_CMD_CORESCHED -c -p \$\$ -d $$"
> > +$TS_CMD_CORESCHED -p $$ \
> > + | sed "s/$$/OWN_PID/g" \
> > + | sed "s/$CORESCHED_COOKIE/COOKIE/g" \
> > + | sed "s/0x.*$/DIFFERENT_COOKIE/g" >> "$TS_OUTPUT"
> > +ts_finalize_subtest
> > +
> > +ts_finalize
> > --
> > 2.44.0
> >
>
--
^ permalink raw reply
* Re: [PATCH v4] coresched: Manage core scheduling cookies for tasks
From: Thomas Weißschuh @ 2024-04-05 6:26 UTC (permalink / raw)
To: Thijs Raymakers; +Cc: pauld, kzak, util-linux
In-Reply-To: <20240404220358.104595-1-thijs@raymakers.nl>
On 2024-04-05 00:03:57+0200, Thijs Raymakers wrote:
> Co-authored-by: Phil Auld <pauld@redhat.com>
> Signed-off-by: Thijs Raymakers <thijs@raymakers.nl>
> Signed-off-by: Phil Auld <pauld@redhat.com>
> ---
>
> Hi Thomas,
>
> Thanks for your comments. This patch addresses most of your remarks. It
> also adds Phil Auld as a Co-author since we went back and forth on the
> design quite a lot. His feedback has been valuable and his ideas have
> been integrated in the util to accomodate for both our usecases.
Thanks, some more comments below.
> Op 01-04-2024 om 7:18 p.m. schreef Thomas Weißschuh:
> > I guess you are aware, but the manpage is empty.
>
> I've added the manpage in this version of the patch.
>
> > Technically all types ending in _t are reserved for libc.
> > And a conflict for cookie_t doesn't even sound that improbable.
> > How about sched_core_scope, sched_core_cookie and enum sched_core_cmd?
>
> Good suggestion, the names of these types have been changed.
>
> > Would a single call to core_sched_get_cookie() not be a reliable check
> > that can be done and reported once?
> > ...
> > To make clear that this works like err{,x}() maybe call it err_prctl().
> > It's unfortunate that this has to be a macro.
> > There are verrx() and verr() which could make this an inline function.
> > We would need fallback implementations for it in include/c.h though,
> > which doesn't look so hard.
>
> I Initially wrote it like this to avoid doing more prctl calls than
> necessary. All the PR_SCHED_CORE* prctl calls indicate whether or not
> the kernel supports core scheduling whenever they return with errno
> EINVAL.
>
> However, if support is explicitly checked beforehand, then the error
> handling becomes a bit simpler and doesn't rely on macros anymore.
> Therefore, I've changed to code to explicitly check for kernel support
> beforehand. This change also removes the need for the `check_prctl`
> macro.
Thanks, would it make sense to expose this check via the CLI?
That could then used by the tests to skip themselves if the system does
not provide core scheduling on old kernels or qemu-user.
> > It would be nicer if these helpers are closer named to the prctl
> > operations.
> > sched_core_share_from_thread() for example.
>
> I've followed the terminology of the kernel documentation of core
> scheduling (Documentation/admin-guide/hw-vuln/core-scheduling.rst) here.
> There, `PR_SCHED_CORE_SHARE_TO` is described as "push core_sched cookie
> to pid." and `PR_SCHED_CORE_SHARE_FROM` is described as "pull core_sched
> cookie from pid"
>
> I prefer that terminology because I find it a little less ambiguous than
> SHARE_TO and SHARE_FROM.
Ok.
> Op 01-04-2024 om 7:18 p.m. schreef Thomas Weißschuh:
> > On 2024-03-27 16:30:08+0100, Thijs Raymakers wrote:
> >> + printf("%s: copied cookie 0x%lx from PID %d to PID %d\n",
> >> + program_invocation_short_name, before, from, to);
> > stderr and only with --verbose?
>
> This has been changed now. All operations, except getting a cookie,
> will no longer print this type of information unless --verbose is passed.
>
> > Also, please some tests.
>
> This version adds 5 tests for coresched:
> - getting the cookie of itself when no cookie is set
> - setting the cookie of itself
> - getting the cookie of itself when the cookie is set (must be equal to
> the cookie that was set)
> - spawn a child process with a new cookie (must be different than the
> cookie of the parent process)
> - spawn a child process with a new cookie and push that cookie to the
> parent process (afterwards, the cookie of the parent must be different)
Nice!
> An interdiff between the previous version and this version is listed
> below. It also address your other remarks not mentioned in this email.
>
> Only the bash completion script is missing, but I think that is better
> suited for a follow-up patch.
Sounds fair.
> Thijs
>
> Interdiff against v3:
[snip]
> .gitignore | 1 +
> bash-completion/coresched | 0
> configure.ac | 12 +-
> meson.build | 16 +-
> meson_options.txt | 2 +-
> schedutils/Makemodule.am | 8 +
> schedutils/coresched.1.adoc | 116 ++++++
> schedutils/coresched.c | 380 ++++++++++++++++++
> tests/commands.sh | 1 +
> .../coresched-change-cookie-of-parent | 1 +
> .../coresched-get-own-pid-no-cookie | 1 +
> .../coresched-get-own-pid-with-cookie | 1 +
> .../schedutils/coresched-set-cookie-own-pid | 1 +
> .../coresched-spawn-child-with-new-cookie | 1 +
> tests/ts/schedutils/coresched | 55 +++
> 15 files changed, 590 insertions(+), 6 deletions(-)
> create mode 100644 bash-completion/coresched
> create mode 100644 schedutils/coresched.1.adoc
> create mode 100644 schedutils/coresched.c
> create mode 100644 tests/expected/schedutils/coresched-change-cookie-of-parent
> create mode 100644 tests/expected/schedutils/coresched-get-own-pid-no-cookie
> create mode 100644 tests/expected/schedutils/coresched-get-own-pid-with-cookie
> create mode 100644 tests/expected/schedutils/coresched-set-cookie-own-pid
> create mode 100644 tests/expected/schedutils/coresched-spawn-child-with-new-cookie
> create mode 100755 tests/ts/schedutils/coresched
[snip]
> diff --git a/schedutils/coresched.c b/schedutils/coresched.c
> new file mode 100644
> index 000000000..3d9392b1a
> --- /dev/null
> +++ b/schedutils/coresched.c
> @@ -0,0 +1,380 @@
> +/**
> + * SPDX-License-Identifier: EUPL-1.2
> + *
> + * coresched.c - manage core scheduling cookies for tasks
> + *
> + * Copyright (C) 2024 Thijs Raymakers, Phil Auld
> + * Licensed under the EUPL v1.2
> + */
> +
> +#include <getopt.h>
> +#include <stdbool.h>
> +#include <stdio.h>
> +#include <sys/prctl.h>
> +#include <sys/wait.h>
> +#include <unistd.h>
> +
> +#include "c.h"
> +#include "closestream.h"
> +#include "nls.h"
> +#include "optutils.h"
> +#include "strutils.h"
> +
> +// These definitions might not be defined in the header files, even if the
> +// prctl interface in the kernel accepts them as valid.
> +#ifndef PR_SCHED_CORE
> +#define PR_SCHED_CORE 62
> +#endif
> +#ifndef PR_SCHED_CORE_GET
> +#define PR_SCHED_CORE_GET 0
> +#endif
> +#ifndef PR_SCHED_CORE_CREATE
> +#define PR_SCHED_CORE_CREATE 1
> +#endif
> +#ifndef PR_SCHED_CORE_SHARE_TO
> +#define PR_SCHED_CORE_SHARE_TO 2
> +#endif
> +#ifndef PR_SCHED_CORE_SHARE_FROM
> +#define PR_SCHED_CORE_SHARE_FROM 3
> +#endif
> +#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 sched_core_scope;
> +typedef unsigned long sched_core_cookie;
> +typedef enum {
> + SCHED_CORE_CMD_GET,
> + SCHED_CORE_CMD_NEW,
> + SCHED_CORE_CMD_COPY,
> +} sched_core_cmd;
> +
> +struct args {
> + pid_t pid;
> + pid_t dest;
> + sched_core_scope type;
> + sched_core_cmd cmd;
> + int exec_argv_offset;
> +};
> +
> +static bool sched_core_verbose = false;
> +
> +static void __attribute__((__noreturn__)) usage(void)
> +{
> + fputs(USAGE_HEADER, stdout);
> + fprintf(stdout, _(" %s [-p PID]\n"), program_invocation_short_name);
> + fprintf(stdout, _(" %s --new [-t <TYPE>] -p <PID>\n"),
> + program_invocation_short_name);
> + fprintf(stdout, _(" %s --new [-t <TYPE>] -- PROGRAM [ARGS...]\n"),
> + program_invocation_short_name);
> + fprintf(stdout, _(" %s --copy -p <PID> [-t <TYPE>] -d <PID>\n"),
> + program_invocation_short_name);
> + fprintf(stdout,
> + _(" %s --copy -p <PID> [-t <TYPE>] -- PROGRAM [ARGS...]\n"),
> + program_invocation_short_name);
> +
> + fputs(USAGE_SEPARATOR, stdout);
> + fputsln(_("Manage core scheduling cookies for tasks."), stdout);
> +
> + fputs(USAGE_FUNCTIONS, stdout);
> + fputsln(_(" -n, --new assign a new core scheduling cookie to an existing PID or\n"
> + " execute a program with a new cookie."),
> + stdout);
> + fputsln(_(" -c, --copy copy the core scheduling cookie from an existing PID to\n"
> + " either another PID, or copy it to a new program"),
> + stdout);
> + fputsln(_("\n If no function is provided, it will retrieve and print the cookie from\n"
> + " the PID provided via --pid.\n"),
> + stdout);
> +
> + fputs(USAGE_OPTIONS, stdout);
> + fputsln(_(" -p, --pid <PID> operate on an existing PID"), stdout);
> + fputsln(_(" -d, --dest <PID> when copying a cookie from an existing PID, --dest is\n"
> + " the destination PID where to copy the cookie to."),
> + stdout);
> + fputsln(_(" -t, --type <TYPE> type of the destination PID, or the type of the PID\n"
> + " when a new core scheduling cookie is created.\n"
> + " Can be one of the following: pid, tgid or pgid.\n"
> + " The default is tgid."),
> + stdout);
> + fputs(USAGE_SEPARATOR, stdout);
> + fputsln(_(" -v, --verbose verbose"), stdout);
> + fprintf(stdout,
> + USAGE_HELP_OPTIONS(
> + 20)); /* char offset to align option descriptions */
> + fprintf(stdout, USAGE_MAN_TAIL("coresched(1)"));
> + exit(EXIT_SUCCESS);
> +}
> +
> +#define bad_usage(FMT...) \
> + warnx(FMT); \
> + errtryhelp(EXIT_FAILURE);
> +
> +static sched_core_cookie core_sched_get_cookie(pid_t pid)
> +{
> + sched_core_cookie cookie = 0;
> + if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_GET, pid,
> + PR_SCHED_CORE_SCOPE_THREAD, &cookie)) {
> + err(EXIT_FAILURE, _("Failed to get cookie from PID %d"), pid);
> + }
> + return cookie;
> +}
> +
> +static void core_sched_create_cookie(pid_t pid, sched_core_scope type)
> +{
> + if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_CREATE, pid, type, 0)) {
> + err(EXIT_FAILURE, _("Failed to create cookie for PID %d"), pid);
> + }
General codestyle is to leave out braces around single-statement blocks.
(I mentioned this before, if you want to keep them please mention it)
> +}
> +
> +static 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(EXIT_FAILURE, _("Failed to pull cookie from PID %d"), from);
> + }
> +}
> +
> +static void core_sched_push_cookie(pid_t to, sched_core_scope type)
> +{
> + if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_SHARE_TO, to, type, 0)) {
> + err(EXIT_FAILURE, _("Failed to push cookie to PID %d"), to);
> + }
> +}
> +
> +static void core_sched_copy_cookie(pid_t from, pid_t to,
> + sched_core_scope to_type)
> +{
> + core_sched_pull_cookie(from);
> + core_sched_push_cookie(to, to_type);
> +
> + if (sched_core_verbose) {
> + sched_core_cookie before = core_sched_get_cookie(from);
> + fprintf(stderr,
> + _("%s: copied cookie 0x%lx from PID %d to PID %d\n"),
> + program_invocation_short_name, before, from, to);
> + }
> +}
> +
> +static void core_sched_get_and_print_cookie(pid_t pid)
> +{
> + if (sched_core_verbose) {
> + sched_core_cookie after = core_sched_get_cookie(pid);
> + fprintf(stderr, _("%s: set cookie of PID %d to 0x%lx\n"),
> + program_invocation_short_name, pid, after);
> + }
> +}
> +
> +static 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->pid) {
> + core_sched_pull_cookie(args->pid);
> + core_sched_get_and_print_cookie(args->pid);
> + } else {
> + pid_t pid = getpid();
> + core_sched_create_cookie(pid, args->type);
> + core_sched_get_and_print_cookie(pid);
> + }
> +
> + if (execvp(argv[0], argv)) {
> + errexec(argv[0]);
> + }
> +}
> +
> +// If PR_SCHED_CORE is not recognized, or not supported on this system,
> +// then prctl will set errno to EINVAL. Assuming all other operands of
> +// prctl are valid, we can use errno==EINVAL as a check to see whether
> +// core scheduling is available on this system.
> +static bool is_core_sched_supported(void)
> +{
> + sched_core_cookie cookie = 0;
> + if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_GET, getpid(),
> + PR_SCHED_CORE_SCOPE_THREAD, &cookie)) {
> + if (errno == EINVAL) {
> + return false;
> + }
> + }
> + return true;
> +}
> +
> +static sched_core_scope parse_core_sched_type(char *str)
> +{
> + if (!strncmp(str, "pid", 4))
These can be plain strcmp().
> + return PR_SCHED_CORE_SCOPE_THREAD;
> + else if (!strncmp(str, "tgid", 5))
> + return PR_SCHED_CORE_SCOPE_THREAD_GROUP;
> + else if (!strncmp(str, "pgid", 5))
> + return PR_SCHED_CORE_SCOPE_PROCESS_GROUP;
> +
> + bad_usage(_("'%s' is an invalid option. Must be one of pid/tgid/pgid"),
> + str);
> +}
> +
> +static void parse_arguments(int argc, char **argv, struct args *args)
> +{
> + int c;
> +
> + static const struct option longopts[] = {
> + { "new", no_argument, NULL, 'n' },
> + { "copy", no_argument, NULL, 'c' },
> + { "pid", required_argument, NULL, 'p' },
> + { "dest", required_argument, NULL, 'd' },
> + { "type", required_argument, NULL, 't' },
> + { "verbose", no_argument, NULL, 'v' },
> + { "version", no_argument, NULL, 'V' },
> + { "help", no_argument, NULL, 'h' },
> + { NULL, 0, NULL, 0 }
> + };
> + static const ul_excl_t excl[] = {
> + { 'c', 'n' }, // Cannot do both --new and --copy
> + { 'd', 'n' }, // Cannot have both --new and --dest
> + { 0 }
> + };
> +
> + int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
> +
> + while ((c = getopt_long(argc, argv, "ncp:d:t:vVh", longopts, NULL)) !=
> + -1) {
> + err_exclusive_options(c, longopts, excl, excl_st);
> + switch (c) {
> + case 'n':
> + args->cmd = SCHED_CORE_CMD_NEW;
> + break;
> + case 'c':
> + args->cmd = SCHED_CORE_CMD_COPY;
> + break;
> + case 'p':
> + args->pid = strtopid_or_err(
> + optarg, _("Failed to parse PID for -p/--pid"));
> + break;
> + case 'd':
> + args->dest = strtopid_or_err(
> + optarg, _("Failed to parse PID for -d/--dest"));
> + break;
> + case 't':
> + args->type = parse_core_sched_type(optarg);
> + break;
> + case 'v':
> + sched_core_verbose = true;
> + break;
> + case 'V':
> + print_version(EXIT_SUCCESS);
> + case 'h':
> + usage();
> + default:
> + errtryhelp(EXIT_FAILURE);
> + }
> + }
> +
> + if (args->cmd == SCHED_CORE_CMD_COPY && !args->pid) {
> + bad_usage(_("--copy: requires a -p/--pid"));
> + }
> +
> + // More arguments have been passed, which means that the user wants to run
> + // another program with a core scheduling cookie.
> + if (argc > optind) {
> + switch (args->cmd) {
> + case SCHED_CORE_CMD_GET:
> + bad_usage(_("Unknown command"));
> + break;
> + case SCHED_CORE_CMD_NEW:
> + if (args->pid) {
> + bad_usage(_(
> + "--new: cannot accept both a -p/--pid and a command"));
> + } else {
> + args->exec_argv_offset = optind;
> + }
> + break;
> + case SCHED_CORE_CMD_COPY:
> + if (args->dest) {
> + bad_usage(_(
> + "--copy: cannot accept both a destination PID "
> + "-d/--dest and a command"))
> + } else {
> + args->exec_argv_offset = optind;
> + }
> + break;
> + }
> + }
> +
> + if (argc <= optind) {
> + if (args->cmd == SCHED_CORE_CMD_NEW && !args->pid) {
> + bad_usage(_(
> + "--new: requires either a -p/--pid or a command"));
> + }
> + if (args->cmd == SCHED_CORE_CMD_COPY && !args->dest) {
> + bad_usage(_(
> + "--copy: requires either a -d/--dest or a command"));
> + }
> + }
> +}
> +
> +int main(int argc, char **argv)
> +{
> + struct args args = { 0 };
> + args.cmd = SCHED_CORE_CMD_GET;
> + args.type = PR_SCHED_CORE_SCOPE_THREAD_GROUP;
> +
> + setlocale(LC_ALL, "");
> + bindtextdomain(PACKAGE, LOCALEDIR);
> + textdomain(PACKAGE);
> + close_stdout_atexit();
> +
> + parse_arguments(argc, argv, &args);
> +
> + if (!is_core_sched_supported()) {
> + errx(ENOTSUP, _("Does your kernel support CONFIG_SCHED_CORE?"));
Afaik errnos are not guaranteed to have the same numeric value
everywhere. If you want to signal something special, use your own
constant (and document it in the manpage).
> + }
> +
> + sched_core_cookie cookie;
> +
> + switch (args.cmd) {
> + case SCHED_CORE_CMD_GET:
> + if (args.pid) {
> + cookie = core_sched_get_cookie(args.pid);
> + if (cookie) {
> + printf(_("%s: cookie of pid %d is 0x%lx\n"),
> + program_invocation_short_name, args.pid,
> + cookie);
program_invocation_short_name seems unnecessary.
The pid itself also seems unnecessary as the users know which cookie
they asked for.
Ah, taskset works like this. I disagree :-)
> + } else {
> + errx(ENODATA,
> + _("pid %d doesn't have a core scheduling cookie"),
> + args.pid);
Is this really a failure case?
I would make the print only on --verbose and otherwise succeed without
output.
> + }
> + } else {
> + usage();
Why not print the current cookie here?
> + }
> + break;
> + case SCHED_CORE_CMD_NEW:
> + if (args.pid) {
> + core_sched_create_cookie(args.pid, args.type);
> + core_sched_get_and_print_cookie(args.pid);
> + } else {
> + core_sched_exec_with_cookie(&args, argv);
> + }
> + break;
> + case SCHED_CORE_CMD_COPY:
> + if (args.dest)
> + core_sched_copy_cookie(args.pid, args.dest, args.type);
> + else
> + core_sched_exec_with_cookie(&args, argv);
> + break;
> + default:
> + usage();
> + }
> +}
> diff --git a/tests/commands.sh b/tests/commands.sh
> index 5674c5ff0..9eef92ccb 100644
> --- a/tests/commands.sh
> +++ b/tests/commands.sh
> @@ -71,6 +71,7 @@ TS_CMD_COLCRT=${TS_CMD_COLCRT:-"${ts_commandsdir}colcrt"}
> TS_CMD_COLRM=${TS_CMD_COLRM:-"${ts_commandsdir}colrm"}
> TS_CMD_COL=${TS_CMD_COL:-"${ts_commandsdir}col"}
> TS_CMD_COLUMN=${TS_CMD_COLUMN:-"${ts_commandsdir}column"}
> +TS_CMD_CORESCHED=${TS_CMD_CORESCHED:-"${ts_commandsdir}coresched"}
> TS_CMD_ENOSYS=${TS_CMD_ENOSYS-"${ts_commandsdir}enosys"}
> TS_CMD_EJECT=${TS_CMD_EJECT-"${ts_commandsdir}eject"}
> TS_CMD_EXCH=${TS_CMD_EXCH-"${ts_commandsdir}exch"}
> diff --git a/tests/expected/schedutils/coresched-change-cookie-of-parent b/tests/expected/schedutils/coresched-change-cookie-of-parent
> new file mode 100644
> index 000000000..97ab7ea23
> --- /dev/null
> +++ b/tests/expected/schedutils/coresched-change-cookie-of-parent
> @@ -0,0 +1 @@
> +coresched: cookie of pid OWN_PID is DIFFERENT_COOKIE
> diff --git a/tests/expected/schedutils/coresched-get-own-pid-no-cookie b/tests/expected/schedutils/coresched-get-own-pid-no-cookie
> new file mode 100644
> index 000000000..7f64a98b3
> --- /dev/null
> +++ b/tests/expected/schedutils/coresched-get-own-pid-no-cookie
> @@ -0,0 +1 @@
> +coresched: pid OWN_PID doesn't have a core scheduling cookie
> diff --git a/tests/expected/schedutils/coresched-get-own-pid-with-cookie b/tests/expected/schedutils/coresched-get-own-pid-with-cookie
> new file mode 100644
> index 000000000..556e31a65
> --- /dev/null
> +++ b/tests/expected/schedutils/coresched-get-own-pid-with-cookie
> @@ -0,0 +1 @@
> +coresched: cookie of pid OWN_PID is COOKIE
> diff --git a/tests/expected/schedutils/coresched-set-cookie-own-pid b/tests/expected/schedutils/coresched-set-cookie-own-pid
> new file mode 100644
> index 000000000..1816a4fbd
> --- /dev/null
> +++ b/tests/expected/schedutils/coresched-set-cookie-own-pid
> @@ -0,0 +1 @@
> +coresched: set cookie of PID OWN_PID to COOKIE
> diff --git a/tests/expected/schedutils/coresched-spawn-child-with-new-cookie b/tests/expected/schedutils/coresched-spawn-child-with-new-cookie
> new file mode 100644
> index 000000000..5b9c40052
> --- /dev/null
> +++ b/tests/expected/schedutils/coresched-spawn-child-with-new-cookie
> @@ -0,0 +1 @@
> +DIFFERENT_COOKIE
> diff --git a/tests/ts/schedutils/coresched b/tests/ts/schedutils/coresched
> new file mode 100755
> index 000000000..5381a56f2
> --- /dev/null
> +++ b/tests/ts/schedutils/coresched
> @@ -0,0 +1,55 @@
> +#!/bin/bash
> +# SPDX-License-Identifier: EUPL-1.2
> +#
> +# This file is part of util-linux
> +#
> +# Copyright (C) 2024 Thijs Raymakers
> +# Licensed under the EUPL v1.2
> +
> +TS_TOPDIR="${0%/*}/../.."
> +TS_DESC="coresched"
> +
> +. "$TS_TOPDIR"/functions.sh
> +ts_init "$*"
> +
> +ts_check_test_command "$TS_CMD_CORESCHED"
> +
> +# The output of coresched contains PIDs and core scheduling cookies, both of which should be
> +# assumed to be random values as we have no control over them. The tests replace these values
> +# with sed before writing them to the output file, so it can match the expected output file.
> +# - The PID of this bash script is replaced with the placeholder `OWN_PID`
> +# - The core scheduling cookie of this bash script is replaced by `COOKIE`
> +# - Any other cookie is replaced by `DIFFERENT_COOKIE`
> +# The behavior of coresched does not depend on the exact values of these cookies, so using
> +# placeholder values does not change the behavior tests.
> +ts_init_subtest "get-own-pid-no-cookie"
> +$TS_CMD_CORESCHED -p $$ 3>&2 2>&1 1>&3 | sed "s/$$/OWN_PID/g" >> "$TS_OUTPUT"
This test fails if the process starting it already has a cookie set.
Also in that case the output is printed past the redirections.
> +ts_finalize_subtest
> +
> +ts_init_subtest "set-cookie-own-pid"
> +CORESCHED_OUTPUT=$($TS_CMD_CORESCHED -v -n -p $$ 3>&2 2>&1 1>&3 | sed "s/$$/OWN_PID/g")
> +CORESCHED_COOKIE=$(echo "$CORESCHED_OUTPUT" | sed 's/^.*\(0x.*$\)/\1/g')
> +CORESCHED_OUTPUT=$(echo "$CORESCHED_OUTPUT" | sed "s/$CORESCHED_COOKIE/COOKIE/g")
> +echo "$CORESCHED_OUTPUT" >> "$TS_OUTPUT"
> +ts_finalize_subtest
> +
> +ts_init_subtest "get-own-pid-with-cookie"
> +$TS_CMD_CORESCHED -p $$ | sed "s/$$/OWN_PID/g" | sed "s/$CORESCHED_COOKIE/COOKIE/g" >> "$TS_OUTPUT"
> +ts_finalize_subtest
> +
> +ts_init_subtest "spawn-child-with-new-cookie"
> +$TS_CMD_CORESCHED -n -- /bin/bash -c "$TS_CMD_CORESCHED -p \$\$" \
When coresched without arguments prints the current value this bash
trickery can be removed.
> + | sed 's/^.*\(0x.*$\)/\1/g' \
> + | sed "s/$CORESCHED_COOKIE/SAME_COOKIE/g" \
> + | sed "s/0x.*$/DIFFERENT_COOKIE/g" >> "$TS_OUTPUT"
> +ts_finalize_subtest
> +
> +ts_init_subtest "change-cookie-of-parent"
> +$TS_CMD_CORESCHED -n -- /bin/bash -c "$TS_CMD_CORESCHED -c -p \$\$ -d $$"
> +$TS_CMD_CORESCHED -p $$ \
> + | sed "s/$$/OWN_PID/g" \
> + | sed "s/$CORESCHED_COOKIE/COOKIE/g" \
> + | sed "s/0x.*$/DIFFERENT_COOKIE/g" >> "$TS_OUTPUT"
> +ts_finalize_subtest
> +
> +ts_finalize
> --
> 2.44.0
>
^ permalink raw reply
* [PATCH v4] coresched: Manage core scheduling cookies for tasks
From: Thijs Raymakers @ 2024-04-04 22:03 UTC (permalink / raw)
To: thomas; +Cc: pauld, kzak, util-linux, Thijs Raymakers
In-Reply-To: <cf9125a9-281f-4940-9c32-1085680d03de@t-8ch.de>
Co-authored-by: Phil Auld <pauld@redhat.com>
Signed-off-by: Thijs Raymakers <thijs@raymakers.nl>
Signed-off-by: Phil Auld <pauld@redhat.com>
---
Hi Thomas,
Thanks for your comments. This patch addresses most of your remarks. It
also adds Phil Auld as a Co-author since we went back and forth on the
design quite a lot. His feedback has been valuable and his ideas have
been integrated in the util to accomodate for both our usecases.
Op 01-04-2024 om 7:18 p.m. schreef Thomas Weißschuh:
> I guess you are aware, but the manpage is empty.
I've added the manpage in this version of the patch.
> Technically all types ending in _t are reserved for libc.
> And a conflict for cookie_t doesn't even sound that improbable.
> How about sched_core_scope, sched_core_cookie and enum sched_core_cmd?
Good suggestion, the names of these types have been changed.
> Would a single call to core_sched_get_cookie() not be a reliable check
> that can be done and reported once?
> ...
> To make clear that this works like err{,x}() maybe call it err_prctl().
> It's unfortunate that this has to be a macro.
> There are verrx() and verr() which could make this an inline function.
> We would need fallback implementations for it in include/c.h though,
> which doesn't look so hard.
I Initially wrote it like this to avoid doing more prctl calls than
necessary. All the PR_SCHED_CORE* prctl calls indicate whether or not
the kernel supports core scheduling whenever they return with errno
EINVAL.
However, if support is explicitly checked beforehand, then the error
handling becomes a bit simpler and doesn't rely on macros anymore.
Therefore, I've changed to code to explicitly check for kernel support
beforehand. This change also removes the need for the `check_prctl`
macro.
> It would be nicer if these helpers are closer named to the prctl
> operations.
> sched_core_share_from_thread() for example.
I've followed the terminology of the kernel documentation of core
scheduling (Documentation/admin-guide/hw-vuln/core-scheduling.rst) here.
There, `PR_SCHED_CORE_SHARE_TO` is described as "push core_sched cookie
to pid." and `PR_SCHED_CORE_SHARE_FROM` is described as "pull core_sched
cookie from pid"
I prefer that terminology because I find it a little less ambiguous than
SHARE_TO and SHARE_FROM.
Op 01-04-2024 om 7:18 p.m. schreef Thomas Weißschuh:
> On 2024-03-27 16:30:08+0100, Thijs Raymakers wrote:
>> + printf("%s: copied cookie 0x%lx from PID %d to PID %d\n",
>> + program_invocation_short_name, before, from, to);
> stderr and only with --verbose?
This has been changed now. All operations, except getting a cookie,
will no longer print this type of information unless --verbose is passed.
> Also, please some tests.
This version adds 5 tests for coresched:
- getting the cookie of itself when no cookie is set
- setting the cookie of itself
- getting the cookie of itself when the cookie is set (must be equal to
the cookie that was set)
- spawn a child process with a new cookie (must be different than the
cookie of the parent process)
- spawn a child process with a new cookie and push that cookie to the
parent process (afterwards, the cookie of the parent must be different)
An interdiff between the previous version and this version is listed
below. It also address your other remarks not mentioned in this email.
Only the bash completion script is missing, but I think that is better
suited for a follow-up patch.
Thijs
Interdiff against v3:
diff --git a/meson.build b/meson.build
index 9a2c04e8f..a3a648753 100644
--- a/meson.build
+++ b/meson.build
@@ -3125,8 +3125,8 @@ if opt and not is_disabler(exe)
manadocs += ['schedutils/chrt.1.adoc',
'schedutils/ionice.1.adoc',
'schedutils/taskset.1.adoc',
- 'schedutils/uclampset.1.adoc',
- 'schedutils/coresched.1.adoc']
+ 'schedutils/uclampset.1.adoc',
+ 'schedutils/coresched.1.adoc']
bashcompletions += ['chrt', 'ionice', 'taskset', 'uclampset', 'coresched']
endif
diff --git a/schedutils/coresched.1.adoc b/schedutils/coresched.1.adoc
index 60a21cd01..34bc352a8 100644
--- a/schedutils/coresched.1.adoc
+++ b/schedutils/coresched.1.adoc
@@ -14,3 +14,103 @@ coresched(1) manpage
== NAME
coresched - manage core scheduling cookies for tasks
+
+== SYNOPSIS
+
+*{command}* [options] *-p* _pid_
+
+*{command}* [options] \-- _command_ [_argument_...]
+
+== DESCRIPTION
+The *{command}* command is used to retrieve or modify the core scheduling cookies of a running process given its _pid_, or to spawn a new _command_ with core scheduling cookies.
+
+Core scheduling allows you to define groups of tasks that are allowed to share a physical core.
+This is done by assigning a cookie to each task.
+Only tasks have the same cookie are allowed to be scheduled on the same physical core.
+
+It is possible to either assign a new random cookie to a task, or copy a cookie from another task. It is not possible to choose the value of the cookie.
+
+== FUNCTIONS
+*-n*, *--new*::
+Assign a new cookie to an existing PID or to execute _command_ with a new cookie.
+
+*-c*, *--copy*::
+Copy the cookie from an existing PID to another PID, or execute _command_ with that copied cookie.
+
+If no function is specified, it will retrieve and print the cookie of a task.
+
+== OPTIONS
+*-p*, *--pid* _PID_::
+Operate on an existing PID and do not launch a new task.
+
+*-d*, *--dest* _PID_::
+When using *--copy*, specify a destination PID where you want to copy the cookie to.
+
+*-t*, *--type* _TYPE_::
+The type of the PID whose cookie will be modified. This can be one of three values:
+- *pid*, or process ID
+- *tgid*, or thread group ID (default value)
+- *pgid*, or process group ID
+
+*-v*, *--verbose*::
+Show extra information when modifying cookies of tasks.
+
+*-h*, *--help*::
+Display help text and exit.
+
+*-V*, *--version*::
+Print version and exit.
+
+== EXAMPLES
+Get the core scheduling cookie of a task with PID _123_{colon}::
+*{command} -p* _123_
+
+Give a task with PID _123_ a new core scheduling cookie{colon}::
+*{command} --new -p* _123_
+
+Spawn a new task with a new core scheduling cookie{colon}::
+*{command} --new* \-- _command_ [_argument_...]
+
+Copy the cookie from a task with pid _123_ to another task with pid _456_{colon}::
+*{command} --copy -p* _123_ *-d* _456_
+
+Copy the cookie from a task with pid _123_ to a new task _command_{colon}::
+*{command} --copy -p* _123_ \-- _command_ [_argument_...]
+
+Copy the cookie from a task with pid _123_ to the process group ID _456_{colon}::
+*{command} --copy -p* _123_ *-t* pgid *-d* _456_
+
+== PERMISSIONS
+When retrieving or modifying the core scheduling cookie of a process, you need to have *PTRACE_MODE_READ_REALCREDS* ptrace access to that process.
+See the section "Ptrace access mode checking" in *ptrace*(2) for more information.
+
+== RETURN VALUE
+On success, *{command}* returns 0.
+If *{command}* fails, it will print an error and return 1.
+If core scheduling is not supported with your kernel (e.g. because *CONFIG_SCHED_CORE*=n), then it will return 95 (*ENOTSUP*).
+
+== AUTHORS
+mailto:thijs@raymakers.nl[Thijs Raymakers],
+mailto:pauld@redhat.com[Phil Auld]
+
+== COPYRIGHT
+
+Copyright {copyright} 2024 Thijs Raymakers and Phil Auld. This is free software licensed under the EUPL.
+
+== SEE ALSO
+*chrt*(1),
+*nice*(1),
+*renice*(1),
+*taskset*(1),
+*ptrace*(2),
+*sched*(7)
+
+The Linux kernel source files _Documentation/admin-guide/hw-vuln/core-scheduling.rst_
+
+include::man-common/bugreports.adoc[]
+
+include::man-common/footer.adoc[]
+
+ifdef::translation[]
+include::man-common/translation.adoc[]
+endif::[]
diff --git a/schedutils/coresched.c b/schedutils/coresched.c
index 756e0a1a6..3d9392b1a 100644
--- a/schedutils/coresched.c
+++ b/schedutils/coresched.c
@@ -3,7 +3,7 @@
*
* coresched.c - manage core scheduling cookies for tasks
*
- * Copyright (C) 2024 Thijs Raymakers
+ * Copyright (C) 2024 Thijs Raymakers, Phil Auld
* Licensed under the EUPL v1.2
*/
@@ -47,94 +47,134 @@
#define PR_SCHED_CORE_SCOPE_PROCESS_GROUP 2
#endif
-typedef int core_sched_type_t;
-typedef unsigned long cookie_t;
+typedef int sched_core_scope;
+typedef unsigned long sched_core_cookie;
typedef enum {
SCHED_CORE_CMD_GET,
SCHED_CORE_CMD_NEW,
SCHED_CORE_CMD_COPY,
-} core_sched_cmd_t;
+} sched_core_cmd;
struct args {
pid_t pid;
pid_t dest;
- core_sched_type_t type;
- core_sched_cmd_t cmd;
+ sched_core_scope type;
+ sched_core_cmd cmd;
int exec_argv_offset;
};
-cookie_t core_sched_get_cookie(pid_t pid);
-void core_sched_create_cookie(pid_t pid, core_sched_type_t type);
-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(pid_t from, pid_t to, core_sched_type_t to_type);
-void core_sched_exec_with_cookie(struct args *args, char **argv);
-void core_sched_get_and_print_cookie(pid_t pid);
+static bool sched_core_verbose = false;
-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);
+static void __attribute__((__noreturn__)) usage(void)
+{
+ fputs(USAGE_HEADER, stdout);
+ fprintf(stdout, _(" %s [-p PID]\n"), program_invocation_short_name);
+ fprintf(stdout, _(" %s --new [-t <TYPE>] -p <PID>\n"),
+ program_invocation_short_name);
+ fprintf(stdout, _(" %s --new [-t <TYPE>] -- PROGRAM [ARGS...]\n"),
+ program_invocation_short_name);
+ fprintf(stdout, _(" %s --copy -p <PID> [-t <TYPE>] -d <PID>\n"),
+ program_invocation_short_name);
+ fprintf(stdout,
+ _(" %s --copy -p <PID> [-t <TYPE>] -- PROGRAM [ARGS...]\n"),
+ program_invocation_short_name);
+
+ fputs(USAGE_SEPARATOR, stdout);
+ fputsln(_("Manage core scheduling cookies for tasks."), stdout);
+
+ fputs(USAGE_FUNCTIONS, stdout);
+ fputsln(_(" -n, --new assign a new core scheduling cookie to an existing PID or\n"
+ " execute a program with a new cookie."),
+ stdout);
+ fputsln(_(" -c, --copy copy the core scheduling cookie from an existing PID to\n"
+ " either another PID, or copy it to a new program"),
+ stdout);
+ fputsln(_("\n If no function is provided, it will retrieve and print the cookie from\n"
+ " the PID provided via --pid.\n"),
+ stdout);
+
+ fputs(USAGE_OPTIONS, stdout);
+ fputsln(_(" -p, --pid <PID> operate on an existing PID"), stdout);
+ fputsln(_(" -d, --dest <PID> when copying a cookie from an existing PID, --dest is\n"
+ " the destination PID where to copy the cookie to."),
+ stdout);
+ fputsln(_(" -t, --type <TYPE> type of the destination PID, or the type of the PID\n"
+ " when a new core scheduling cookie is created.\n"
+ " Can be one of the following: pid, tgid or pgid.\n"
+ " The default is tgid."),
+ stdout);
+ fputs(USAGE_SEPARATOR, stdout);
+ fputsln(_(" -v, --verbose verbose"), stdout);
+ fprintf(stdout,
+ USAGE_HELP_OPTIONS(
+ 20)); /* char offset to align option descriptions */
+ fprintf(stdout, USAGE_MAN_TAIL("coresched(1)"));
+ exit(EXIT_SUCCESS);
+}
#define bad_usage(FMT...) \
warnx(FMT); \
- errtryhelp(EINVAL);
-
-#define check_prctl(FMT...) \
- if (errno == EINVAL) { \
- warn(FMT); \
- errx(errno, "Does your kernel support CONFIG_SCHED_CORE?"); \
- } else { \
- err(errno, FMT); \
- }
+ errtryhelp(EXIT_FAILURE);
-cookie_t core_sched_get_cookie(pid_t pid)
+static sched_core_cookie core_sched_get_cookie(pid_t pid)
{
- cookie_t cookie = 0;
+ sched_core_cookie cookie = 0;
if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_GET, pid,
PR_SCHED_CORE_SCOPE_THREAD, &cookie)) {
- check_prctl("Failed to get cookie from PID %d", pid);
+ err(EXIT_FAILURE, _("Failed to get cookie from PID %d"), pid);
}
return cookie;
}
-void core_sched_create_cookie(pid_t pid, core_sched_type_t type)
+static void core_sched_create_cookie(pid_t pid, sched_core_scope type)
{
if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_CREATE, pid, type, 0)) {
- check_prctl("Failed to create cookie for PID %d", pid);
+ err(EXIT_FAILURE, _("Failed to create cookie for PID %d"), pid);
}
}
-void core_sched_pull_cookie(pid_t from)
+static 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)) {
- check_prctl("Failed to pull cookie from PID %d", from);
+ err(EXIT_FAILURE, _("Failed to pull cookie from PID %d"), from);
}
}
-void core_sched_push_cookie(pid_t to, core_sched_type_t type)
+static void core_sched_push_cookie(pid_t to, sched_core_scope type)
{
if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_SHARE_TO, to, type, 0)) {
- check_prctl("Failed to push cookie to PID %d", to);
+ err(EXIT_FAILURE, _("Failed to push cookie to PID %d"), to);
}
}
-void core_sched_copy_cookie(pid_t from, pid_t to, core_sched_type_t to_type)
+static void core_sched_copy_cookie(pid_t from, pid_t to,
+ sched_core_scope to_type)
{
core_sched_pull_cookie(from);
- cookie_t before = core_sched_get_cookie(from);
core_sched_push_cookie(to, to_type);
- printf("%s: copied cookie 0x%lx from PID %d to PID %d\n",
- program_invocation_short_name, before, from, to);
+
+ if (sched_core_verbose) {
+ sched_core_cookie before = core_sched_get_cookie(from);
+ fprintf(stderr,
+ _("%s: copied cookie 0x%lx from PID %d to PID %d\n"),
+ program_invocation_short_name, before, from, to);
+ }
}
-void core_sched_exec_with_cookie(struct args *args, char **argv)
+static void core_sched_get_and_print_cookie(pid_t pid)
{
- if (!args->exec_argv_offset) {
- usage();
+ if (sched_core_verbose) {
+ sched_core_cookie after = core_sched_get_cookie(pid);
+ fprintf(stderr, _("%s: set cookie of PID %d to 0x%lx\n"),
+ program_invocation_short_name, pid, after);
}
+}
+
+static 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];
@@ -148,9 +188,7 @@ void core_sched_exec_with_cookie(struct args *args, char **argv)
} else {
pid_t pid = getpid();
core_sched_create_cookie(pid, args->type);
- cookie_t after = core_sched_get_cookie(pid);
- printf("%s: set cookie of PID %d to 0x%lx\n",
- program_invocation_short_name, pid, after);
+ core_sched_get_and_print_cookie(pid);
}
if (execvp(argv[0], argv)) {
@@ -158,75 +196,36 @@ void core_sched_exec_with_cookie(struct args *args, char **argv)
}
}
-void core_sched_get_and_print_cookie(pid_t pid)
+// If PR_SCHED_CORE is not recognized, or not supported on this system,
+// then prctl will set errno to EINVAL. Assuming all other operands of
+// prctl are valid, we can use errno==EINVAL as a check to see whether
+// core scheduling is available on this system.
+static bool is_core_sched_supported(void)
{
- cookie_t after = core_sched_get_cookie(pid);
- printf("%s: set cookie of PID %d to 0x%lx\n",
- program_invocation_short_name, pid, after);
+ sched_core_cookie cookie = 0;
+ if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_GET, getpid(),
+ PR_SCHED_CORE_SCOPE_THREAD, &cookie)) {
+ if (errno == EINVAL) {
+ return false;
+ }
+ }
+ return true;
}
-core_sched_type_t parse_core_sched_type(char *str)
+static sched_core_scope parse_core_sched_type(char *str)
{
- if (!strncmp(str, "pid\0", 4)) {
+ if (!strncmp(str, "pid", 4))
return PR_SCHED_CORE_SCOPE_THREAD;
- } else if (!strncmp(str, "tgid\0", 5)) {
+ else if (!strncmp(str, "tgid", 5))
return PR_SCHED_CORE_SCOPE_THREAD_GROUP;
- } else if (!strncmp(str, "pgid\0", 5)) {
+ else if (!strncmp(str, "pgid", 5))
return PR_SCHED_CORE_SCOPE_PROCESS_GROUP;
- }
- bad_usage("'%s' is an invalid option. Must be one of pid/tgid/pgid",
+ bad_usage(_("'%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 [-p PID]\n"), program_invocation_short_name);
- fprintf(stdout, _(" %s --new [-t <TYPE>] -p <PID>\n"),
- program_invocation_short_name);
- fprintf(stdout, _(" %s --new [-t <TYPE>] -- PROGRAM [ARGS...]\n"),
- program_invocation_short_name);
- fprintf(stdout, _(" %s --copy -p <PID> [-t <TYPE>] -d <PID>\n"),
- program_invocation_short_name);
- fprintf(stdout,
- _(" %s --copy -p <PID> [-t <TYPE>] -- PROGRAM [ARGS...]\n"),
- program_invocation_short_name);
-
- fputs(USAGE_SEPARATOR, stdout);
- fputsln(_("Manage core scheduling cookies for tasks."), stdout);
-
- fputs(USAGE_FUNCTIONS, stdout);
- fputsln(_(" -n, --new assign a new core scheduling cookie to an existing PID or\n"
- " execute a program with a new cookie."),
- stdout);
- fputsln(_(" -c, --copy copy the core scheduling cookie from an existing PID to\n"
- " either another PID, or copy it to a new program"),
- stdout);
- fputsln(_("\n If no function is provided, it will retrieve and print the cookie from\n"
- " the PID provided via --pid.\n"),
- stdout);
-
- fputs(USAGE_OPTIONS, stdout);
- fputsln(_(" -p, --pid <PID> operate on an existing PID"), stdout);
- fputsln(_(" -d, --dest <PID> when copying a cookie from an existing PID, --dest is\n"
- " the destination PID where to copy the cookie to."),
- stdout);
- fputsln(_(" -t, --type <TYPE> type of the destination PID, or the type of the PID\n"
- " when a new core scheduling cookie is created.\n"
- " Can be one of the following: pid, tgid or pgid.\n"
- " The default is tgid."),
- stdout);
- fputs(USAGE_SEPARATOR, stdout);
- fprintf(stdout,
- USAGE_HELP_OPTIONS(
- 20)); /* char offset to align option descriptions */
- fprintf(stdout, USAGE_MAN_TAIL("coresched(1)"));
- exit(EXIT_SUCCESS);
}
-void parse_arguments(int argc, char **argv, struct args *args)
+static void parse_arguments(int argc, char **argv, struct args *args)
{
int c;
@@ -236,6 +235,7 @@ void parse_arguments(int argc, char **argv, struct args *args)
{ "pid", required_argument, NULL, 'p' },
{ "dest", required_argument, NULL, 'd' },
{ "type", required_argument, NULL, 't' },
+ { "verbose", no_argument, NULL, 'v' },
{ "version", no_argument, NULL, 'V' },
{ "help", no_argument, NULL, 'h' },
{ NULL, 0, NULL, 0 }
@@ -248,7 +248,7 @@ void parse_arguments(int argc, char **argv, struct args *args)
int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
- while ((c = getopt_long(argc, argv, "ncp:d:t:Vh", longopts, NULL)) !=
+ while ((c = getopt_long(argc, argv, "ncp:d:t:vVh", longopts, NULL)) !=
-1) {
err_exclusive_options(c, longopts, excl, excl_st);
switch (c) {
@@ -260,15 +260,18 @@ void parse_arguments(int argc, char **argv, struct args *args)
break;
case 'p':
args->pid = strtopid_or_err(
- optarg, "Failed to parse PID for -p/--pid");
+ optarg, _("Failed to parse PID for -p/--pid"));
break;
case 'd':
args->dest = strtopid_or_err(
- optarg, "Failed to parse PID for -d/--dest");
+ optarg, _("Failed to parse PID for -d/--dest"));
break;
case 't':
args->type = parse_core_sched_type(optarg);
break;
+ case 'v':
+ sched_core_verbose = true;
+ break;
case 'V':
print_version(EXIT_SUCCESS);
case 'h':
@@ -279,7 +282,7 @@ void parse_arguments(int argc, char **argv, struct args *args)
}
if (args->cmd == SCHED_CORE_CMD_COPY && !args->pid) {
- bad_usage("--copy: requires a -p/--pid");
+ bad_usage(_("--copy: requires a -p/--pid"));
}
// More arguments have been passed, which means that the user wants to run
@@ -287,21 +290,21 @@ void parse_arguments(int argc, char **argv, struct args *args)
if (argc > optind) {
switch (args->cmd) {
case SCHED_CORE_CMD_GET:
- bad_usage("Unknown command");
+ bad_usage(_("Unknown command"));
break;
case SCHED_CORE_CMD_NEW:
if (args->pid) {
- bad_usage(
- "--new: cannot accept both a -p/--pid and a command");
+ bad_usage(_(
+ "--new: cannot accept both a -p/--pid and a command"));
} else {
args->exec_argv_offset = optind;
}
break;
case SCHED_CORE_CMD_COPY:
if (args->dest) {
- bad_usage(
+ bad_usage(_(
"--copy: cannot accept both a destination PID "
- "-d/--dest and a command")
+ "-d/--dest and a command"))
} else {
args->exec_argv_offset = optind;
}
@@ -311,12 +314,12 @@ void parse_arguments(int argc, char **argv, struct args *args)
if (argc <= optind) {
if (args->cmd == SCHED_CORE_CMD_NEW && !args->pid) {
- bad_usage(
- "--new: requires either a -p/--pid or a command");
+ bad_usage(_(
+ "--new: requires either a -p/--pid or a command"));
}
if (args->cmd == SCHED_CORE_CMD_COPY && !args->dest) {
- bad_usage(
- "--copy: requires either a -d/--dest or a command");
+ bad_usage(_(
+ "--copy: requires either a -d/--dest or a command"));
}
}
}
@@ -334,24 +337,27 @@ int main(int argc, char **argv)
parse_arguments(argc, argv, &args);
- cookie_t cookie = 0;
+ if (!is_core_sched_supported()) {
+ errx(ENOTSUP, _("Does your kernel support CONFIG_SCHED_CORE?"));
+ }
+
+ sched_core_cookie cookie;
switch (args.cmd) {
case SCHED_CORE_CMD_GET:
if (args.pid) {
cookie = core_sched_get_cookie(args.pid);
if (cookie) {
- printf("%s: cookie of pid %d is 0x%lx\n",
+ printf(_("%s: cookie of pid %d is 0x%lx\n"),
program_invocation_short_name, args.pid,
cookie);
} else {
errx(ENODATA,
- "pid %d doesn't have a core scheduling cookie",
+ _("pid %d doesn't have a core scheduling cookie"),
args.pid);
}
} else {
usage();
- exit(0);
}
break;
case SCHED_CORE_CMD_NEW:
@@ -363,14 +369,12 @@ int main(int argc, char **argv)
}
break;
case SCHED_CORE_CMD_COPY:
- if (args.dest) {
+ if (args.dest)
core_sched_copy_cookie(args.pid, args.dest, args.type);
- } else {
+ else
core_sched_exec_with_cookie(&args, argv);
- }
break;
default:
usage();
- exit(1);
}
}
diff --git a/tests/commands.sh b/tests/commands.sh
index 5674c5ff0..9eef92ccb 100644
--- a/tests/commands.sh
+++ b/tests/commands.sh
@@ -71,6 +71,7 @@ TS_CMD_COLCRT=${TS_CMD_COLCRT:-"${ts_commandsdir}colcrt"}
TS_CMD_COLRM=${TS_CMD_COLRM:-"${ts_commandsdir}colrm"}
TS_CMD_COL=${TS_CMD_COL:-"${ts_commandsdir}col"}
TS_CMD_COLUMN=${TS_CMD_COLUMN:-"${ts_commandsdir}column"}
+TS_CMD_CORESCHED=${TS_CMD_CORESCHED:-"${ts_commandsdir}coresched"}
TS_CMD_ENOSYS=${TS_CMD_ENOSYS-"${ts_commandsdir}enosys"}
TS_CMD_EJECT=${TS_CMD_EJECT-"${ts_commandsdir}eject"}
TS_CMD_EXCH=${TS_CMD_EXCH-"${ts_commandsdir}exch"}
diff --git a/tests/expected/schedutils/coresched-change-cookie-of-parent b/tests/expected/schedutils/coresched-change-cookie-of-parent
new file mode 100644
index 000000000..97ab7ea23
--- /dev/null
+++ b/tests/expected/schedutils/coresched-change-cookie-of-parent
@@ -0,0 +1 @@
+coresched: cookie of pid OWN_PID is DIFFERENT_COOKIE
diff --git a/tests/expected/schedutils/coresched-get-own-pid-no-cookie b/tests/expected/schedutils/coresched-get-own-pid-no-cookie
new file mode 100644
index 000000000..7f64a98b3
--- /dev/null
+++ b/tests/expected/schedutils/coresched-get-own-pid-no-cookie
@@ -0,0 +1 @@
+coresched: pid OWN_PID doesn't have a core scheduling cookie
diff --git a/tests/expected/schedutils/coresched-get-own-pid-with-cookie b/tests/expected/schedutils/coresched-get-own-pid-with-cookie
new file mode 100644
index 000000000..556e31a65
--- /dev/null
+++ b/tests/expected/schedutils/coresched-get-own-pid-with-cookie
@@ -0,0 +1 @@
+coresched: cookie of pid OWN_PID is COOKIE
diff --git a/tests/expected/schedutils/coresched-set-cookie-own-pid b/tests/expected/schedutils/coresched-set-cookie-own-pid
new file mode 100644
index 000000000..1816a4fbd
--- /dev/null
+++ b/tests/expected/schedutils/coresched-set-cookie-own-pid
@@ -0,0 +1 @@
+coresched: set cookie of PID OWN_PID to COOKIE
diff --git a/tests/expected/schedutils/coresched-spawn-child-with-new-cookie b/tests/expected/schedutils/coresched-spawn-child-with-new-cookie
new file mode 100644
index 000000000..5b9c40052
--- /dev/null
+++ b/tests/expected/schedutils/coresched-spawn-child-with-new-cookie
@@ -0,0 +1 @@
+DIFFERENT_COOKIE
diff --git a/tests/ts/schedutils/coresched b/tests/ts/schedutils/coresched
new file mode 100755
index 000000000..5381a56f2
--- /dev/null
+++ b/tests/ts/schedutils/coresched
@@ -0,0 +1,55 @@
+#!/bin/bash
+# SPDX-License-Identifier: EUPL-1.2
+#
+# This file is part of util-linux
+#
+# Copyright (C) 2024 Thijs Raymakers
+# Licensed under the EUPL v1.2
+
+TS_TOPDIR="${0%/*}/../.."
+TS_DESC="coresched"
+
+. "$TS_TOPDIR"/functions.sh
+ts_init "$*"
+
+ts_check_test_command "$TS_CMD_CORESCHED"
+
+# The output of coresched contains PIDs and core scheduling cookies, both of which should be
+# assumed to be random values as we have no control over them. The tests replace these values
+# with sed before writing them to the output file, so it can match the expected output file.
+# - The PID of this bash script is replaced with the placeholder `OWN_PID`
+# - The core scheduling cookie of this bash script is replaced by `COOKIE`
+# - Any other cookie is replaced by `DIFFERENT_COOKIE`
+# The behavior of coresched does not depend on the exact values of these cookies, so using
+# placeholder values does not change the behavior tests.
+ts_init_subtest "get-own-pid-no-cookie"
+$TS_CMD_CORESCHED -p $$ 3>&2 2>&1 1>&3 | sed "s/$$/OWN_PID/g" >> "$TS_OUTPUT"
+ts_finalize_subtest
+
+ts_init_subtest "set-cookie-own-pid"
+CORESCHED_OUTPUT=$($TS_CMD_CORESCHED -v -n -p $$ 3>&2 2>&1 1>&3 | sed "s/$$/OWN_PID/g")
+CORESCHED_COOKIE=$(echo "$CORESCHED_OUTPUT" | sed 's/^.*\(0x.*$\)/\1/g')
+CORESCHED_OUTPUT=$(echo "$CORESCHED_OUTPUT" | sed "s/$CORESCHED_COOKIE/COOKIE/g")
+echo "$CORESCHED_OUTPUT" >> "$TS_OUTPUT"
+ts_finalize_subtest
+
+ts_init_subtest "get-own-pid-with-cookie"
+$TS_CMD_CORESCHED -p $$ | sed "s/$$/OWN_PID/g" | sed "s/$CORESCHED_COOKIE/COOKIE/g" >> "$TS_OUTPUT"
+ts_finalize_subtest
+
+ts_init_subtest "spawn-child-with-new-cookie"
+$TS_CMD_CORESCHED -n -- /bin/bash -c "$TS_CMD_CORESCHED -p \$\$" \
+ | sed 's/^.*\(0x.*$\)/\1/g' \
+ | sed "s/$CORESCHED_COOKIE/SAME_COOKIE/g" \
+ | sed "s/0x.*$/DIFFERENT_COOKIE/g" >> "$TS_OUTPUT"
+ts_finalize_subtest
+
+ts_init_subtest "change-cookie-of-parent"
+$TS_CMD_CORESCHED -n -- /bin/bash -c "$TS_CMD_CORESCHED -c -p \$\$ -d $$"
+$TS_CMD_CORESCHED -p $$ \
+ | sed "s/$$/OWN_PID/g" \
+ | sed "s/$CORESCHED_COOKIE/COOKIE/g" \
+ | sed "s/0x.*$/DIFFERENT_COOKIE/g" >> "$TS_OUTPUT"
+ts_finalize_subtest
+
+ts_finalize
.gitignore | 1 +
bash-completion/coresched | 0
configure.ac | 12 +-
meson.build | 16 +-
meson_options.txt | 2 +-
schedutils/Makemodule.am | 8 +
schedutils/coresched.1.adoc | 116 ++++++
schedutils/coresched.c | 380 ++++++++++++++++++
tests/commands.sh | 1 +
.../coresched-change-cookie-of-parent | 1 +
.../coresched-get-own-pid-no-cookie | 1 +
.../coresched-get-own-pid-with-cookie | 1 +
.../schedutils/coresched-set-cookie-own-pid | 1 +
.../coresched-spawn-child-with-new-cookie | 1 +
tests/ts/schedutils/coresched | 55 +++
15 files changed, 590 insertions(+), 6 deletions(-)
create mode 100644 bash-completion/coresched
create mode 100644 schedutils/coresched.1.adoc
create mode 100644 schedutils/coresched.c
create mode 100644 tests/expected/schedutils/coresched-change-cookie-of-parent
create mode 100644 tests/expected/schedutils/coresched-get-own-pid-no-cookie
create mode 100644 tests/expected/schedutils/coresched-get-own-pid-with-cookie
create mode 100644 tests/expected/schedutils/coresched-set-cookie-own-pid
create mode 100644 tests/expected/schedutils/coresched-spawn-child-with-new-cookie
create mode 100755 tests/ts/schedutils/coresched
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..a3a648753 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..34bc352a8
--- /dev/null
+++ b/schedutils/coresched.1.adoc
@@ -0,0 +1,116 @@
+//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
+
+== SYNOPSIS
+
+*{command}* [options] *-p* _pid_
+
+*{command}* [options] \-- _command_ [_argument_...]
+
+== DESCRIPTION
+The *{command}* command is used to retrieve or modify the core scheduling cookies of a running process given its _pid_, or to spawn a new _command_ with core scheduling cookies.
+
+Core scheduling allows you to define groups of tasks that are allowed to share a physical core.
+This is done by assigning a cookie to each task.
+Only tasks have the same cookie are allowed to be scheduled on the same physical core.
+
+It is possible to either assign a new random cookie to a task, or copy a cookie from another task. It is not possible to choose the value of the cookie.
+
+== FUNCTIONS
+*-n*, *--new*::
+Assign a new cookie to an existing PID or to execute _command_ with a new cookie.
+
+*-c*, *--copy*::
+Copy the cookie from an existing PID to another PID, or execute _command_ with that copied cookie.
+
+If no function is specified, it will retrieve and print the cookie of a task.
+
+== OPTIONS
+*-p*, *--pid* _PID_::
+Operate on an existing PID and do not launch a new task.
+
+*-d*, *--dest* _PID_::
+When using *--copy*, specify a destination PID where you want to copy the cookie to.
+
+*-t*, *--type* _TYPE_::
+The type of the PID whose cookie will be modified. This can be one of three values:
+- *pid*, or process ID
+- *tgid*, or thread group ID (default value)
+- *pgid*, or process group ID
+
+*-v*, *--verbose*::
+Show extra information when modifying cookies of tasks.
+
+*-h*, *--help*::
+Display help text and exit.
+
+*-V*, *--version*::
+Print version and exit.
+
+== EXAMPLES
+Get the core scheduling cookie of a task with PID _123_{colon}::
+*{command} -p* _123_
+
+Give a task with PID _123_ a new core scheduling cookie{colon}::
+*{command} --new -p* _123_
+
+Spawn a new task with a new core scheduling cookie{colon}::
+*{command} --new* \-- _command_ [_argument_...]
+
+Copy the cookie from a task with pid _123_ to another task with pid _456_{colon}::
+*{command} --copy -p* _123_ *-d* _456_
+
+Copy the cookie from a task with pid _123_ to a new task _command_{colon}::
+*{command} --copy -p* _123_ \-- _command_ [_argument_...]
+
+Copy the cookie from a task with pid _123_ to the process group ID _456_{colon}::
+*{command} --copy -p* _123_ *-t* pgid *-d* _456_
+
+== PERMISSIONS
+When retrieving or modifying the core scheduling cookie of a process, you need to have *PTRACE_MODE_READ_REALCREDS* ptrace access to that process.
+See the section "Ptrace access mode checking" in *ptrace*(2) for more information.
+
+== RETURN VALUE
+On success, *{command}* returns 0.
+If *{command}* fails, it will print an error and return 1.
+If core scheduling is not supported with your kernel (e.g. because *CONFIG_SCHED_CORE*=n), then it will return 95 (*ENOTSUP*).
+
+== AUTHORS
+mailto:thijs@raymakers.nl[Thijs Raymakers],
+mailto:pauld@redhat.com[Phil Auld]
+
+== COPYRIGHT
+
+Copyright {copyright} 2024 Thijs Raymakers and Phil Auld. This is free software licensed under the EUPL.
+
+== SEE ALSO
+*chrt*(1),
+*nice*(1),
+*renice*(1),
+*taskset*(1),
+*ptrace*(2),
+*sched*(7)
+
+The Linux kernel source files _Documentation/admin-guide/hw-vuln/core-scheduling.rst_
+
+include::man-common/bugreports.adoc[]
+
+include::man-common/footer.adoc[]
+
+ifdef::translation[]
+include::man-common/translation.adoc[]
+endif::[]
diff --git a/schedutils/coresched.c b/schedutils/coresched.c
new file mode 100644
index 000000000..3d9392b1a
--- /dev/null
+++ b/schedutils/coresched.c
@@ -0,0 +1,380 @@
+/**
+ * SPDX-License-Identifier: EUPL-1.2
+ *
+ * coresched.c - manage core scheduling cookies for tasks
+ *
+ * Copyright (C) 2024 Thijs Raymakers, Phil Auld
+ * Licensed under the EUPL v1.2
+ */
+
+#include <getopt.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <sys/prctl.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "c.h"
+#include "closestream.h"
+#include "nls.h"
+#include "optutils.h"
+#include "strutils.h"
+
+// These definitions might not be defined in the header files, even if the
+// prctl interface in the kernel accepts them as valid.
+#ifndef PR_SCHED_CORE
+#define PR_SCHED_CORE 62
+#endif
+#ifndef PR_SCHED_CORE_GET
+#define PR_SCHED_CORE_GET 0
+#endif
+#ifndef PR_SCHED_CORE_CREATE
+#define PR_SCHED_CORE_CREATE 1
+#endif
+#ifndef PR_SCHED_CORE_SHARE_TO
+#define PR_SCHED_CORE_SHARE_TO 2
+#endif
+#ifndef PR_SCHED_CORE_SHARE_FROM
+#define PR_SCHED_CORE_SHARE_FROM 3
+#endif
+#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 sched_core_scope;
+typedef unsigned long sched_core_cookie;
+typedef enum {
+ SCHED_CORE_CMD_GET,
+ SCHED_CORE_CMD_NEW,
+ SCHED_CORE_CMD_COPY,
+} sched_core_cmd;
+
+struct args {
+ pid_t pid;
+ pid_t dest;
+ sched_core_scope type;
+ sched_core_cmd cmd;
+ int exec_argv_offset;
+};
+
+static bool sched_core_verbose = false;
+
+static void __attribute__((__noreturn__)) usage(void)
+{
+ fputs(USAGE_HEADER, stdout);
+ fprintf(stdout, _(" %s [-p PID]\n"), program_invocation_short_name);
+ fprintf(stdout, _(" %s --new [-t <TYPE>] -p <PID>\n"),
+ program_invocation_short_name);
+ fprintf(stdout, _(" %s --new [-t <TYPE>] -- PROGRAM [ARGS...]\n"),
+ program_invocation_short_name);
+ fprintf(stdout, _(" %s --copy -p <PID> [-t <TYPE>] -d <PID>\n"),
+ program_invocation_short_name);
+ fprintf(stdout,
+ _(" %s --copy -p <PID> [-t <TYPE>] -- PROGRAM [ARGS...]\n"),
+ program_invocation_short_name);
+
+ fputs(USAGE_SEPARATOR, stdout);
+ fputsln(_("Manage core scheduling cookies for tasks."), stdout);
+
+ fputs(USAGE_FUNCTIONS, stdout);
+ fputsln(_(" -n, --new assign a new core scheduling cookie to an existing PID or\n"
+ " execute a program with a new cookie."),
+ stdout);
+ fputsln(_(" -c, --copy copy the core scheduling cookie from an existing PID to\n"
+ " either another PID, or copy it to a new program"),
+ stdout);
+ fputsln(_("\n If no function is provided, it will retrieve and print the cookie from\n"
+ " the PID provided via --pid.\n"),
+ stdout);
+
+ fputs(USAGE_OPTIONS, stdout);
+ fputsln(_(" -p, --pid <PID> operate on an existing PID"), stdout);
+ fputsln(_(" -d, --dest <PID> when copying a cookie from an existing PID, --dest is\n"
+ " the destination PID where to copy the cookie to."),
+ stdout);
+ fputsln(_(" -t, --type <TYPE> type of the destination PID, or the type of the PID\n"
+ " when a new core scheduling cookie is created.\n"
+ " Can be one of the following: pid, tgid or pgid.\n"
+ " The default is tgid."),
+ stdout);
+ fputs(USAGE_SEPARATOR, stdout);
+ fputsln(_(" -v, --verbose verbose"), stdout);
+ fprintf(stdout,
+ USAGE_HELP_OPTIONS(
+ 20)); /* char offset to align option descriptions */
+ fprintf(stdout, USAGE_MAN_TAIL("coresched(1)"));
+ exit(EXIT_SUCCESS);
+}
+
+#define bad_usage(FMT...) \
+ warnx(FMT); \
+ errtryhelp(EXIT_FAILURE);
+
+static sched_core_cookie core_sched_get_cookie(pid_t pid)
+{
+ sched_core_cookie cookie = 0;
+ if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_GET, pid,
+ PR_SCHED_CORE_SCOPE_THREAD, &cookie)) {
+ err(EXIT_FAILURE, _("Failed to get cookie from PID %d"), pid);
+ }
+ return cookie;
+}
+
+static void core_sched_create_cookie(pid_t pid, sched_core_scope type)
+{
+ if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_CREATE, pid, type, 0)) {
+ err(EXIT_FAILURE, _("Failed to create cookie for PID %d"), pid);
+ }
+}
+
+static 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(EXIT_FAILURE, _("Failed to pull cookie from PID %d"), from);
+ }
+}
+
+static void core_sched_push_cookie(pid_t to, sched_core_scope type)
+{
+ if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_SHARE_TO, to, type, 0)) {
+ err(EXIT_FAILURE, _("Failed to push cookie to PID %d"), to);
+ }
+}
+
+static void core_sched_copy_cookie(pid_t from, pid_t to,
+ sched_core_scope to_type)
+{
+ core_sched_pull_cookie(from);
+ core_sched_push_cookie(to, to_type);
+
+ if (sched_core_verbose) {
+ sched_core_cookie before = core_sched_get_cookie(from);
+ fprintf(stderr,
+ _("%s: copied cookie 0x%lx from PID %d to PID %d\n"),
+ program_invocation_short_name, before, from, to);
+ }
+}
+
+static void core_sched_get_and_print_cookie(pid_t pid)
+{
+ if (sched_core_verbose) {
+ sched_core_cookie after = core_sched_get_cookie(pid);
+ fprintf(stderr, _("%s: set cookie of PID %d to 0x%lx\n"),
+ program_invocation_short_name, pid, after);
+ }
+}
+
+static 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->pid) {
+ core_sched_pull_cookie(args->pid);
+ core_sched_get_and_print_cookie(args->pid);
+ } else {
+ pid_t pid = getpid();
+ core_sched_create_cookie(pid, args->type);
+ core_sched_get_and_print_cookie(pid);
+ }
+
+ if (execvp(argv[0], argv)) {
+ errexec(argv[0]);
+ }
+}
+
+// If PR_SCHED_CORE is not recognized, or not supported on this system,
+// then prctl will set errno to EINVAL. Assuming all other operands of
+// prctl are valid, we can use errno==EINVAL as a check to see whether
+// core scheduling is available on this system.
+static bool is_core_sched_supported(void)
+{
+ sched_core_cookie cookie = 0;
+ if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_GET, getpid(),
+ PR_SCHED_CORE_SCOPE_THREAD, &cookie)) {
+ if (errno == EINVAL) {
+ return false;
+ }
+ }
+ return true;
+}
+
+static sched_core_scope parse_core_sched_type(char *str)
+{
+ if (!strncmp(str, "pid", 4))
+ return PR_SCHED_CORE_SCOPE_THREAD;
+ else if (!strncmp(str, "tgid", 5))
+ return PR_SCHED_CORE_SCOPE_THREAD_GROUP;
+ else if (!strncmp(str, "pgid", 5))
+ return PR_SCHED_CORE_SCOPE_PROCESS_GROUP;
+
+ bad_usage(_("'%s' is an invalid option. Must be one of pid/tgid/pgid"),
+ str);
+}
+
+static void parse_arguments(int argc, char **argv, struct args *args)
+{
+ int c;
+
+ static const struct option longopts[] = {
+ { "new", no_argument, NULL, 'n' },
+ { "copy", no_argument, NULL, 'c' },
+ { "pid", required_argument, NULL, 'p' },
+ { "dest", required_argument, NULL, 'd' },
+ { "type", required_argument, NULL, 't' },
+ { "verbose", no_argument, NULL, 'v' },
+ { "version", no_argument, NULL, 'V' },
+ { "help", no_argument, NULL, 'h' },
+ { NULL, 0, NULL, 0 }
+ };
+ static const ul_excl_t excl[] = {
+ { 'c', 'n' }, // Cannot do both --new and --copy
+ { 'd', 'n' }, // Cannot have both --new and --dest
+ { 0 }
+ };
+
+ int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
+
+ while ((c = getopt_long(argc, argv, "ncp:d:t:vVh", longopts, NULL)) !=
+ -1) {
+ err_exclusive_options(c, longopts, excl, excl_st);
+ switch (c) {
+ case 'n':
+ args->cmd = SCHED_CORE_CMD_NEW;
+ break;
+ case 'c':
+ args->cmd = SCHED_CORE_CMD_COPY;
+ break;
+ case 'p':
+ args->pid = strtopid_or_err(
+ optarg, _("Failed to parse PID for -p/--pid"));
+ break;
+ case 'd':
+ args->dest = strtopid_or_err(
+ optarg, _("Failed to parse PID for -d/--dest"));
+ break;
+ case 't':
+ args->type = parse_core_sched_type(optarg);
+ break;
+ case 'v':
+ sched_core_verbose = true;
+ break;
+ case 'V':
+ print_version(EXIT_SUCCESS);
+ case 'h':
+ usage();
+ default:
+ errtryhelp(EXIT_FAILURE);
+ }
+ }
+
+ if (args->cmd == SCHED_CORE_CMD_COPY && !args->pid) {
+ bad_usage(_("--copy: requires a -p/--pid"));
+ }
+
+ // More arguments have been passed, which means that the user wants to run
+ // another program with a core scheduling cookie.
+ if (argc > optind) {
+ switch (args->cmd) {
+ case SCHED_CORE_CMD_GET:
+ bad_usage(_("Unknown command"));
+ break;
+ case SCHED_CORE_CMD_NEW:
+ if (args->pid) {
+ bad_usage(_(
+ "--new: cannot accept both a -p/--pid and a command"));
+ } else {
+ args->exec_argv_offset = optind;
+ }
+ break;
+ case SCHED_CORE_CMD_COPY:
+ if (args->dest) {
+ bad_usage(_(
+ "--copy: cannot accept both a destination PID "
+ "-d/--dest and a command"))
+ } else {
+ args->exec_argv_offset = optind;
+ }
+ break;
+ }
+ }
+
+ if (argc <= optind) {
+ if (args->cmd == SCHED_CORE_CMD_NEW && !args->pid) {
+ bad_usage(_(
+ "--new: requires either a -p/--pid or a command"));
+ }
+ if (args->cmd == SCHED_CORE_CMD_COPY && !args->dest) {
+ bad_usage(_(
+ "--copy: requires either a -d/--dest or a command"));
+ }
+ }
+}
+
+int main(int argc, char **argv)
+{
+ struct args args = { 0 };
+ args.cmd = SCHED_CORE_CMD_GET;
+ args.type = PR_SCHED_CORE_SCOPE_THREAD_GROUP;
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ close_stdout_atexit();
+
+ parse_arguments(argc, argv, &args);
+
+ if (!is_core_sched_supported()) {
+ errx(ENOTSUP, _("Does your kernel support CONFIG_SCHED_CORE?"));
+ }
+
+ sched_core_cookie cookie;
+
+ switch (args.cmd) {
+ case SCHED_CORE_CMD_GET:
+ if (args.pid) {
+ cookie = core_sched_get_cookie(args.pid);
+ if (cookie) {
+ printf(_("%s: cookie of pid %d is 0x%lx\n"),
+ program_invocation_short_name, args.pid,
+ cookie);
+ } else {
+ errx(ENODATA,
+ _("pid %d doesn't have a core scheduling cookie"),
+ args.pid);
+ }
+ } else {
+ usage();
+ }
+ break;
+ case SCHED_CORE_CMD_NEW:
+ if (args.pid) {
+ core_sched_create_cookie(args.pid, args.type);
+ core_sched_get_and_print_cookie(args.pid);
+ } else {
+ core_sched_exec_with_cookie(&args, argv);
+ }
+ break;
+ case SCHED_CORE_CMD_COPY:
+ if (args.dest)
+ core_sched_copy_cookie(args.pid, args.dest, args.type);
+ else
+ core_sched_exec_with_cookie(&args, argv);
+ break;
+ default:
+ usage();
+ }
+}
diff --git a/tests/commands.sh b/tests/commands.sh
index 5674c5ff0..9eef92ccb 100644
--- a/tests/commands.sh
+++ b/tests/commands.sh
@@ -71,6 +71,7 @@ TS_CMD_COLCRT=${TS_CMD_COLCRT:-"${ts_commandsdir}colcrt"}
TS_CMD_COLRM=${TS_CMD_COLRM:-"${ts_commandsdir}colrm"}
TS_CMD_COL=${TS_CMD_COL:-"${ts_commandsdir}col"}
TS_CMD_COLUMN=${TS_CMD_COLUMN:-"${ts_commandsdir}column"}
+TS_CMD_CORESCHED=${TS_CMD_CORESCHED:-"${ts_commandsdir}coresched"}
TS_CMD_ENOSYS=${TS_CMD_ENOSYS-"${ts_commandsdir}enosys"}
TS_CMD_EJECT=${TS_CMD_EJECT-"${ts_commandsdir}eject"}
TS_CMD_EXCH=${TS_CMD_EXCH-"${ts_commandsdir}exch"}
diff --git a/tests/expected/schedutils/coresched-change-cookie-of-parent b/tests/expected/schedutils/coresched-change-cookie-of-parent
new file mode 100644
index 000000000..97ab7ea23
--- /dev/null
+++ b/tests/expected/schedutils/coresched-change-cookie-of-parent
@@ -0,0 +1 @@
+coresched: cookie of pid OWN_PID is DIFFERENT_COOKIE
diff --git a/tests/expected/schedutils/coresched-get-own-pid-no-cookie b/tests/expected/schedutils/coresched-get-own-pid-no-cookie
new file mode 100644
index 000000000..7f64a98b3
--- /dev/null
+++ b/tests/expected/schedutils/coresched-get-own-pid-no-cookie
@@ -0,0 +1 @@
+coresched: pid OWN_PID doesn't have a core scheduling cookie
diff --git a/tests/expected/schedutils/coresched-get-own-pid-with-cookie b/tests/expected/schedutils/coresched-get-own-pid-with-cookie
new file mode 100644
index 000000000..556e31a65
--- /dev/null
+++ b/tests/expected/schedutils/coresched-get-own-pid-with-cookie
@@ -0,0 +1 @@
+coresched: cookie of pid OWN_PID is COOKIE
diff --git a/tests/expected/schedutils/coresched-set-cookie-own-pid b/tests/expected/schedutils/coresched-set-cookie-own-pid
new file mode 100644
index 000000000..1816a4fbd
--- /dev/null
+++ b/tests/expected/schedutils/coresched-set-cookie-own-pid
@@ -0,0 +1 @@
+coresched: set cookie of PID OWN_PID to COOKIE
diff --git a/tests/expected/schedutils/coresched-spawn-child-with-new-cookie b/tests/expected/schedutils/coresched-spawn-child-with-new-cookie
new file mode 100644
index 000000000..5b9c40052
--- /dev/null
+++ b/tests/expected/schedutils/coresched-spawn-child-with-new-cookie
@@ -0,0 +1 @@
+DIFFERENT_COOKIE
diff --git a/tests/ts/schedutils/coresched b/tests/ts/schedutils/coresched
new file mode 100755
index 000000000..5381a56f2
--- /dev/null
+++ b/tests/ts/schedutils/coresched
@@ -0,0 +1,55 @@
+#!/bin/bash
+# SPDX-License-Identifier: EUPL-1.2
+#
+# This file is part of util-linux
+#
+# Copyright (C) 2024 Thijs Raymakers
+# Licensed under the EUPL v1.2
+
+TS_TOPDIR="${0%/*}/../.."
+TS_DESC="coresched"
+
+. "$TS_TOPDIR"/functions.sh
+ts_init "$*"
+
+ts_check_test_command "$TS_CMD_CORESCHED"
+
+# The output of coresched contains PIDs and core scheduling cookies, both of which should be
+# assumed to be random values as we have no control over them. The tests replace these values
+# with sed before writing them to the output file, so it can match the expected output file.
+# - The PID of this bash script is replaced with the placeholder `OWN_PID`
+# - The core scheduling cookie of this bash script is replaced by `COOKIE`
+# - Any other cookie is replaced by `DIFFERENT_COOKIE`
+# The behavior of coresched does not depend on the exact values of these cookies, so using
+# placeholder values does not change the behavior tests.
+ts_init_subtest "get-own-pid-no-cookie"
+$TS_CMD_CORESCHED -p $$ 3>&2 2>&1 1>&3 | sed "s/$$/OWN_PID/g" >> "$TS_OUTPUT"
+ts_finalize_subtest
+
+ts_init_subtest "set-cookie-own-pid"
+CORESCHED_OUTPUT=$($TS_CMD_CORESCHED -v -n -p $$ 3>&2 2>&1 1>&3 | sed "s/$$/OWN_PID/g")
+CORESCHED_COOKIE=$(echo "$CORESCHED_OUTPUT" | sed 's/^.*\(0x.*$\)/\1/g')
+CORESCHED_OUTPUT=$(echo "$CORESCHED_OUTPUT" | sed "s/$CORESCHED_COOKIE/COOKIE/g")
+echo "$CORESCHED_OUTPUT" >> "$TS_OUTPUT"
+ts_finalize_subtest
+
+ts_init_subtest "get-own-pid-with-cookie"
+$TS_CMD_CORESCHED -p $$ | sed "s/$$/OWN_PID/g" | sed "s/$CORESCHED_COOKIE/COOKIE/g" >> "$TS_OUTPUT"
+ts_finalize_subtest
+
+ts_init_subtest "spawn-child-with-new-cookie"
+$TS_CMD_CORESCHED -n -- /bin/bash -c "$TS_CMD_CORESCHED -p \$\$" \
+ | sed 's/^.*\(0x.*$\)/\1/g' \
+ | sed "s/$CORESCHED_COOKIE/SAME_COOKIE/g" \
+ | sed "s/0x.*$/DIFFERENT_COOKIE/g" >> "$TS_OUTPUT"
+ts_finalize_subtest
+
+ts_init_subtest "change-cookie-of-parent"
+$TS_CMD_CORESCHED -n -- /bin/bash -c "$TS_CMD_CORESCHED -c -p \$\$ -d $$"
+$TS_CMD_CORESCHED -p $$ \
+ | sed "s/$$/OWN_PID/g" \
+ | sed "s/$CORESCHED_COOKIE/COOKIE/g" \
+ | sed "s/0x.*$/DIFFERENT_COOKIE/g" >> "$TS_OUTPUT"
+ts_finalize_subtest
+
+ts_finalize
--
2.44.0
^ permalink raw reply related
* [ANNOUNCE] util-linux maintenance release v2.39.4
From: Karel Zak @ 2024-04-04 9:39 UTC (permalink / raw)
To: linux-kernel, linux-fsdevel, util-linux
The util-linux stable maintenance release v2.39.4 is available at
http://www.kernel.org/pub/linux/utils/util-linux/v2.39/
Feedback and bug reports, as always, are welcomed.
(Please note that the current stable release is v2.40.)
Karel
util-linux v2.39.4 Release Notes
================================
Security issues
---------------
This release fixes CVE-2024-28085. The wall command does not filter escape
sequences from command line arguments. The vulnerable code was introduced in
commit cdd3cc7fa4 (2013). Every version since has been vulnerable.
This allows unprivileged users to put arbitrary text on other users terminals,
if mesg is set to y and *wall is setgid*. Not all distros are affected (e.g.
CentOS, RHEL, Fedora are not; Ubuntu and Debian wall is both setgid and mesg is
set to y by default).
Changes between v2.39.3 and v2.39.4
-----------------------------------
build:
- only build test_enosys if an audit arch exists [Thomas Weißschuh]
dmesg:
- (tests) validate json output [Thomas Weißschuh]
- -r LOG_MAKEPRI needs fac << 3 [Edward Chron]
- correctly print all supported facility names [Thomas Weißschuh]
- only write one message to json [Thomas Weißschuh]
- open-code LOG_MAKEPRI [Thomas Weißschuh]
docs:
- update AUTHORS file [Karel Zak]
fadvise:
- (test) don't compare fincore page counts [Thomas Weißschuh]
- (test) dynamically calculate expected test values [Thomas Weißschuh]
- (test) test with 64k blocks [Thomas Weißschuh]
- (tests) factor out calls to "fincore" [Thomas Weißschuh]
github:
- add labeler [Karel Zak]
jsonwrt:
- add ul_jsonwrt_value_s_sized [Thomas Weißschuh]
libblkid:
- Check offset in LUKS2 header [Milan Broz]
- topology/ioctl correctly handle kernel types [Thomas Weißschuh]
libmount:
- don't initialize variable twice (#2714) [Thorsten Kukuk]
- make sure "option=" is used as string [Karel Zak]
libsmartcols:
- (tests) add test for continuous json output [Thomas Weißschuh]
- drop spourious newline in between streamed JSON objects [Thomas Weißschuh]
- flush correct stream [Thomas Weißschuh]
- only recognize closed object as final element [Thomas Weißschuh]
po:
- merge changes [Karel Zak]
po-man:
- merge changes [Karel Zak]
wall:
- fix calloc cal [-Werror=calloc-transposed-args] [Karel Zak]
- fix escape sequence Injection [CVE-2024-28085] [Karel Zak]
--
Karel Zak <kzak@redhat.com>
http://karelzak.blogspot.com
^ permalink raw reply
* Re: [PATCH v3] coresched: Manage core scheduling cookies for tasks
From: Thomas Weißschuh @ 2024-04-01 17:18 UTC (permalink / raw)
To: Thijs Raymakers; +Cc: pauld, kzak, util-linux
In-Reply-To: <20240327153010.59273-1-thijs@raymakers.nl>
On 2024-03-27 16:30:08+0100, Thijs Raymakers wrote:
> [..]
> .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 | 376 ++++++++++++++++++++++++++++++++++++
> 8 files changed, 425 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']
Wrong indentation?
> + 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
I guess you are aware, but the manpage is empty.
> diff --git a/schedutils/coresched.c b/schedutils/coresched.c
> new file mode 100644
> index 000000000..756e0a1a6
> --- /dev/null
> +++ b/schedutils/coresched.c
> @@ -0,0 +1,376 @@
> +/**
> + * 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 <unistd.h>
> +
> +#include "c.h"
> +#include "closestream.h"
> +#include "nls.h"
> +#include "optutils.h"
> +#include "strutils.h"
> +
> +// These definitions might not be defined in the header files, even if the
> +// prctl interface in the kernel accepts them as valid.
> +#ifndef PR_SCHED_CORE
> +#define PR_SCHED_CORE 62
> +#endif
> +#ifndef PR_SCHED_CORE_GET
> +#define PR_SCHED_CORE_GET 0
> +#endif
> +#ifndef PR_SCHED_CORE_CREATE
> +#define PR_SCHED_CORE_CREATE 1
> +#endif
> +#ifndef PR_SCHED_CORE_SHARE_TO
> +#define PR_SCHED_CORE_SHARE_TO 2
> +#endif
> +#ifndef PR_SCHED_CORE_SHARE_FROM
> +#define PR_SCHED_CORE_SHARE_FROM 3
> +#endif
> +#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 unsigned long cookie_t;
> +typedef enum {
> + SCHED_CORE_CMD_GET,
> + SCHED_CORE_CMD_NEW,
> + SCHED_CORE_CMD_COPY,
> +} core_sched_cmd_t;
Technically all types ending in _t are reserved for libc.
And a conflict for cookie_t doesn't even sound that improbable.
How about sched_core_scope, sched_core_cookie and enum sched_core_cmd?
> +
> +struct args {
> + pid_t pid;
> + pid_t dest;
> + core_sched_type_t type;
> + core_sched_cmd_t cmd;
> + int exec_argv_offset;
> +};
> +
> +cookie_t core_sched_get_cookie(pid_t pid);
> +void core_sched_create_cookie(pid_t pid, core_sched_type_t type);
> +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(pid_t from, pid_t to, core_sched_type_t to_type);
> +void core_sched_exec_with_cookie(struct args *args, char **argv);
> +void core_sched_get_and_print_cookie(pid_t pid);
> +
> +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);
Some of these forward declarations don't exist and some are unused.
Can we do without forward decls?
Also all functions can be static.
> +
> +#define bad_usage(FMT...) \
> + warnx(FMT); \
> + errtryhelp(EINVAL);
> +
> +#define check_prctl(FMT...) \
> + if (errno == EINVAL) { \
> + warn(FMT); \
> + errx(errno, "Does your kernel support CONFIG_SCHED_CORE?"); \
Would a single call to core_sched_get_cookie() not be a reliable check
that can be done and reported once?
> + } else { \
> + err(errno, FMT); \
> + }
I'd prefer to not exit with a random errno, but always with a fixed
one. Except one to signal that kernel support is missing.
To make clear that this works like err{,x}() maybe call it err_prctl().
It's unfortunate that this has to be a macro.
There are verrx() and verr() which could make this an inline function.
We would need fallback implementations for it in include/c.h though,
which doesn't look so hard.
> +
> +cookie_t core_sched_get_cookie(pid_t pid)
> +{
> + cookie_t cookie = 0;
> + if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_GET, pid,
> + PR_SCHED_CORE_SCOPE_THREAD, &cookie)) {
> + check_prctl("Failed to get cookie from PID %d", pid);
*All* user-facing strings need to go through _() for localizations.
> + }
> + return cookie;
> +}
> +
> +void core_sched_create_cookie(pid_t pid, core_sched_type_t type)
> +{
> + if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_CREATE, pid, type, 0)) {
> + check_prctl("Failed to create cookie for PID %d", pid);
> + }
> +}
> +
> +void core_sched_pull_cookie(pid_t from)
It would be nicer if these helpers are closer named to the prctl
operations.
sched_core_share_from_thread() for example.
> +{
> + if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_SHARE_FROM, from,
> + PR_SCHED_CORE_SCOPE_THREAD, 0)) {
> + check_prctl("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)) {
> + check_prctl("Failed to push cookie to PID %d", to);
> + }
> +}
> +
> +void core_sched_copy_cookie(pid_t from, pid_t to, core_sched_type_t to_type)
> +{
> + core_sched_pull_cookie(from);
> + cookie_t before = core_sched_get_cookie(from);
> + core_sched_push_cookie(to, to_type);
> + printf("%s: copied cookie 0x%lx from PID %d to PID %d\n",
> + program_invocation_short_name, before, from, to);
stderr and only with --verbose?
> +}
> +
> +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->pid) {
> + core_sched_pull_cookie(args->pid);
> + core_sched_get_and_print_cookie(args->pid);
> + } else {
> + pid_t pid = getpid();
> + core_sched_create_cookie(pid, args->type);
> + cookie_t after = core_sched_get_cookie(pid);
> + printf("%s: set cookie of PID %d to 0x%lx\n",
> + program_invocation_short_name, pid, after);
> + }
> +
> + if (execvp(argv[0], argv)) {
> + errexec(argv[0]);
> + }
> +}
> +
> +void core_sched_get_and_print_cookie(pid_t pid)
> +{
> + cookie_t after = core_sched_get_cookie(pid);
> + printf("%s: set cookie of PID %d to 0x%lx\n",
> + program_invocation_short_name, pid, after);
> +}
> +
> +core_sched_type_t parse_core_sched_type(char *str)
> +{
> + if (!strncmp(str, "pid\0", 4)) {
This additional null-byte doesn't seem to make a difference.
> + 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;
> + }
The codestyle avoids braces around single-statement blocks.
> +
> + bad_usage("'%s' is an invalid option. Must be one of pid/tgid/pgid",
> + str);
> + __builtin_unreachable();
Why the unreachable?
> +}
> +
> +static void __attribute__((__noreturn__)) usage(void)
> +{
> + fputs(USAGE_HEADER, stdout);
> + fprintf(stdout, _(" %s [-p PID]\n"), program_invocation_short_name);
> + fprintf(stdout, _(" %s --new [-t <TYPE>] -p <PID>\n"),
> + program_invocation_short_name);
> + fprintf(stdout, _(" %s --new [-t <TYPE>] -- PROGRAM [ARGS...]\n"),
> + program_invocation_short_name);
> + fprintf(stdout, _(" %s --copy -p <PID> [-t <TYPE>] -d <PID>\n"),
> + program_invocation_short_name);
> + fprintf(stdout,
> + _(" %s --copy -p <PID> [-t <TYPE>] -- PROGRAM [ARGS...]\n"),
> + program_invocation_short_name);
> +
> + fputs(USAGE_SEPARATOR, stdout);
> + fputsln(_("Manage core scheduling cookies for tasks."), stdout);
> +
> + fputs(USAGE_FUNCTIONS, stdout);
> + fputsln(_(" -n, --new assign a new core scheduling cookie to an existing PID or\n"
> + " execute a program with a new cookie."),
> + stdout);
> + fputsln(_(" -c, --copy copy the core scheduling cookie from an existing PID to\n"
> + " either another PID, or copy it to a new program"),
> + stdout);
> + fputsln(_("\n If no function is provided, it will retrieve and print the cookie from\n"
> + " the PID provided via --pid.\n"),
> + stdout);
> +
> + fputs(USAGE_OPTIONS, stdout);
> + fputsln(_(" -p, --pid <PID> operate on an existing PID"), stdout);
> + fputsln(_(" -d, --dest <PID> when copying a cookie from an existing PID, --dest is\n"
> + " the destination PID where to copy the cookie to."),
> + stdout);
> + fputsln(_(" -t, --type <TYPE> type of the destination PID, or the type of the PID\n"
> + " when a new core scheduling cookie is created.\n"
> + " Can be one of the following: pid, tgid or pgid.\n"
> + " The default is tgid."),
> + stdout);
> + fputs(USAGE_SEPARATOR, stdout);
> + fprintf(stdout,
> + USAGE_HELP_OPTIONS(
> + 20)); /* char offset to align option descriptions */
> + fprintf(stdout, USAGE_MAN_TAIL("coresched(1)"));
> + exit(EXIT_SUCCESS);
> +}
> +
> +void parse_arguments(int argc, char **argv, struct args *args)
> +{
> + int c;
> +
> + static const struct option longopts[] = {
> + { "new", no_argument, NULL, 'n' },
> + { "copy", no_argument, NULL, 'c' },
> + { "pid", required_argument, NULL, 'p' },
> + { "dest", required_argument, NULL, 'd' },
> + { "type", required_argument, NULL, 't' },
> + { "version", no_argument, NULL, 'V' },
> + { "help", no_argument, NULL, 'h' },
> + { NULL, 0, NULL, 0 }
> + };
> + static const ul_excl_t excl[] = {
> + { 'c', 'n' }, // Cannot do both --new and --copy
> + { 'd', 'n' }, // Cannot have both --new and --dest
> + { 0 }
> + };
> +
> + int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
> +
> + while ((c = getopt_long(argc, argv, "ncp:d:t:Vh", longopts, NULL)) !=
> + -1) {
> + err_exclusive_options(c, longopts, excl, excl_st);
> + switch (c) {
> + case 'n':
> + args->cmd = SCHED_CORE_CMD_NEW;
> + break;
> + case 'c':
> + args->cmd = SCHED_CORE_CMD_COPY;
> + break;
> + case 'p':
> + args->pid = strtopid_or_err(
> + optarg, "Failed to parse PID for -p/--pid");
> + break;
> + case 'd':
> + args->dest = strtopid_or_err(
> + optarg, "Failed to parse PID for -d/--dest");
> + 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 (args->cmd == SCHED_CORE_CMD_COPY && !args->pid) {
> + bad_usage("--copy: requires a -p/--pid");
> + }
> +
> + // More arguments have been passed, which means that the user wants to run
> + // another program with a core scheduling cookie.
> + if (argc > optind) {
> + switch (args->cmd) {
> + case SCHED_CORE_CMD_GET:
> + bad_usage("Unknown command");
> + break;
> + case SCHED_CORE_CMD_NEW:
> + if (args->pid) {
> + bad_usage(
> + "--new: cannot accept both a -p/--pid and a command");
> + } else {
> + args->exec_argv_offset = optind;
> + }
> + break;
> + case SCHED_CORE_CMD_COPY:
> + if (args->dest) {
> + bad_usage(
> + "--copy: cannot accept both a destination PID "
> + "-d/--dest and a command")
> + } else {
> + args->exec_argv_offset = optind;
> + }
> + break;
> + }
> + }
> +
> + if (argc <= optind) {
> + if (args->cmd == SCHED_CORE_CMD_NEW && !args->pid) {
> + bad_usage(
> + "--new: requires either a -p/--pid or a command");
> + }
> + if (args->cmd == SCHED_CORE_CMD_COPY && !args->dest) {
> + bad_usage(
> + "--copy: requires either a -d/--dest or a command");
> + }
> + }
> +}
> +
> +int main(int argc, char **argv)
> +{
> + struct args args = { 0 };
> + args.cmd = SCHED_CORE_CMD_GET;
> + args.type = PR_SCHED_CORE_SCOPE_THREAD_GROUP;
> +
> + setlocale(LC_ALL, "");
> + bindtextdomain(PACKAGE, LOCALEDIR);
> + textdomain(PACKAGE);
> + close_stdout_atexit();
> +
> + parse_arguments(argc, argv, &args);
> +
> + cookie_t cookie = 0;
The initial value is never used.
By not initializing it the compiler could help you avoid using it
accidentally.
> +
> + switch (args.cmd) {
> + case SCHED_CORE_CMD_GET:
> + if (args.pid) {
> + cookie = core_sched_get_cookie(args.pid);
> + if (cookie) {
> + printf("%s: cookie of pid %d is 0x%lx\n",
> + program_invocation_short_name, args.pid,
> + cookie);
> + } else {
> + errx(ENODATA,
> + "pid %d doesn't have a core scheduling cookie",
> + args.pid);
> + }
> + } else {
> + usage();
> + exit(0);
exit(1)?
> + }
> + break;
> + case SCHED_CORE_CMD_NEW:
> + if (args.pid) {
> + core_sched_create_cookie(args.pid, args.type);
> + core_sched_get_and_print_cookie(args.pid);
> + } else {
> + core_sched_exec_with_cookie(&args, argv);
> + }
> + break;
> + case SCHED_CORE_CMD_COPY:
> + if (args.dest) {
> + core_sched_copy_cookie(args.pid, args.dest, args.type);
> + } else {
> + core_sched_exec_with_cookie(&args, argv);
> + }
> + break;
> + default:
> + usage();
> + exit(1);
> + }
> +}
Also, please some tests.
^ permalink raw reply
* [PATCH v3] coresched: Manage core scheduling cookies for tasks
From: Thijs Raymakers @ 2024-03-27 15:30 UTC (permalink / raw)
To: pauld; +Cc: kzak, util-linux, Thijs Raymakers
In-Reply-To: <20240327141845.GE361020@lorien.usersys.redhat.com>
Signed-off-by: Thijs Raymakers <thijs@raymakers.nl>
Thanks: Phil Auld for the valuable feedback
---
Hi Phil,
Thank you for taking a look at it. I've added the missing definitions of
the PR_SCHED* constants so it should now compile on systems that don't
have these definitions available.
Op 27-03-2024 om 15:18 schreef Phil Auld:
> I think this looks good but I'd do the error line first then the
> CONFIG_SCHED_CORE message.
I agree that it looks better like that. I switched them around.
> It's a little harder to clear the cookie with coresched than coreset
Clearing the cookie of a task is not something that is explicitly supported
by the kernel. Unless there is an option like PR_SCHED_CORE_CLEAR, this
remains a bit tricky to do. One could do
$ coresched --copy -p $$ -d <PID>
to copy the cookie of current shell to a destination PID, but that
would only clear the cookie if the current shell has no cookie to begin
with. Properly clearing the cookie of a task without relying on the
existance of a cookieless task seems to require some changes to the kernel.
> but otherwise I think this version is really good. It's covering all
> my use cases and seems to fit my expectations pretty well.
That is great to hear! My main use case of copying cookies around is
also covered. Unless you or anybody else have any remaining remarks,
I think this patch is ready.
The next steps would be to write the man page and add some tests as
well. I don't know exactly what the policy surrounding this is. Do those
have to be included in the same patch, or can they come a bit later?
Thijs
Interdiff against v2:
diff --git a/schedutils/coresched.c b/schedutils/coresched.c
index 537281fdb..756e0a1a6 100644
--- a/schedutils/coresched.c
+++ b/schedutils/coresched.c
@@ -20,15 +20,29 @@
#include "optutils.h"
#include "strutils.h"
-// These definitions might not be defined, even if the
-// prctl interface accepts them.
+// These definitions might not be defined in the header files, even if the
+// prctl interface in the kernel accepts them as valid.
+#ifndef PR_SCHED_CORE
+#define PR_SCHED_CORE 62
+#endif
+#ifndef PR_SCHED_CORE_GET
+#define PR_SCHED_CORE_GET 0
+#endif
+#ifndef PR_SCHED_CORE_CREATE
+#define PR_SCHED_CORE_CREATE 1
+#endif
+#ifndef PR_SCHED_CORE_SHARE_TO
+#define PR_SCHED_CORE_SHARE_TO 2
+#endif
+#ifndef PR_SCHED_CORE_SHARE_FROM
+#define PR_SCHED_CORE_SHARE_FROM 3
+#endif
#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
@@ -67,9 +81,12 @@ static void __attribute__((__noreturn__)) usage(void);
warnx(FMT); \
errtryhelp(EINVAL);
-#define check_coresched_in_kernel(errno) \
- if (errno == EINVAL) { \
- warnx("Does your kernel support CONFIG_SCHED_CORE?"); \
+#define check_prctl(FMT...) \
+ if (errno == EINVAL) { \
+ warn(FMT); \
+ errx(errno, "Does your kernel support CONFIG_SCHED_CORE?"); \
+ } else { \
+ err(errno, FMT); \
}
cookie_t core_sched_get_cookie(pid_t pid)
@@ -77,8 +94,7 @@ cookie_t core_sched_get_cookie(pid_t pid)
cookie_t cookie = 0;
if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_GET, pid,
PR_SCHED_CORE_SCOPE_THREAD, &cookie)) {
- check_coresched_in_kernel(errno);
- err(errno, "Failed to get cookie from PID %d", pid);
+ check_prctl("Failed to get cookie from PID %d", pid);
}
return cookie;
}
@@ -86,8 +102,7 @@ cookie_t core_sched_get_cookie(pid_t pid)
void core_sched_create_cookie(pid_t pid, core_sched_type_t type)
{
if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_CREATE, pid, type, 0)) {
- check_coresched_in_kernel(errno);
- err(errno, "Failed to create cookie for PID %d", pid);
+ check_prctl("Failed to create cookie for PID %d", pid);
}
}
@@ -95,16 +110,14 @@ 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)) {
- check_coresched_in_kernel(errno);
- err(errno, "Failed to pull cookie from PID %d", from);
+ check_prctl("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)) {
- check_coresched_in_kernel(errno);
- err(errno, "Failed to push cookie to PID %d", to);
+ check_prctl("Failed to push cookie to PID %d", to);
}
}
.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 | 376 ++++++++++++++++++++++++++++++++++++
8 files changed, 425 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..756e0a1a6
--- /dev/null
+++ b/schedutils/coresched.c
@@ -0,0 +1,376 @@
+/**
+ * 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 <unistd.h>
+
+#include "c.h"
+#include "closestream.h"
+#include "nls.h"
+#include "optutils.h"
+#include "strutils.h"
+
+// These definitions might not be defined in the header files, even if the
+// prctl interface in the kernel accepts them as valid.
+#ifndef PR_SCHED_CORE
+#define PR_SCHED_CORE 62
+#endif
+#ifndef PR_SCHED_CORE_GET
+#define PR_SCHED_CORE_GET 0
+#endif
+#ifndef PR_SCHED_CORE_CREATE
+#define PR_SCHED_CORE_CREATE 1
+#endif
+#ifndef PR_SCHED_CORE_SHARE_TO
+#define PR_SCHED_CORE_SHARE_TO 2
+#endif
+#ifndef PR_SCHED_CORE_SHARE_FROM
+#define PR_SCHED_CORE_SHARE_FROM 3
+#endif
+#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 unsigned long cookie_t;
+typedef enum {
+ SCHED_CORE_CMD_GET,
+ SCHED_CORE_CMD_NEW,
+ SCHED_CORE_CMD_COPY,
+} core_sched_cmd_t;
+
+struct args {
+ pid_t pid;
+ pid_t dest;
+ core_sched_type_t type;
+ core_sched_cmd_t cmd;
+ int exec_argv_offset;
+};
+
+cookie_t core_sched_get_cookie(pid_t pid);
+void core_sched_create_cookie(pid_t pid, core_sched_type_t type);
+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(pid_t from, pid_t to, core_sched_type_t to_type);
+void core_sched_exec_with_cookie(struct args *args, char **argv);
+void core_sched_get_and_print_cookie(pid_t pid);
+
+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);
+
+#define bad_usage(FMT...) \
+ warnx(FMT); \
+ errtryhelp(EINVAL);
+
+#define check_prctl(FMT...) \
+ if (errno == EINVAL) { \
+ warn(FMT); \
+ errx(errno, "Does your kernel support CONFIG_SCHED_CORE?"); \
+ } else { \
+ err(errno, FMT); \
+ }
+
+cookie_t core_sched_get_cookie(pid_t pid)
+{
+ cookie_t cookie = 0;
+ if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_GET, pid,
+ PR_SCHED_CORE_SCOPE_THREAD, &cookie)) {
+ check_prctl("Failed to get cookie from PID %d", pid);
+ }
+ return cookie;
+}
+
+void core_sched_create_cookie(pid_t pid, core_sched_type_t type)
+{
+ if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_CREATE, pid, type, 0)) {
+ check_prctl("Failed to create cookie for PID %d", 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)) {
+ check_prctl("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)) {
+ check_prctl("Failed to push cookie to PID %d", to);
+ }
+}
+
+void core_sched_copy_cookie(pid_t from, pid_t to, core_sched_type_t to_type)
+{
+ core_sched_pull_cookie(from);
+ cookie_t before = core_sched_get_cookie(from);
+ core_sched_push_cookie(to, to_type);
+ printf("%s: copied cookie 0x%lx from PID %d to PID %d\n",
+ program_invocation_short_name, before, from, to);
+}
+
+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->pid) {
+ core_sched_pull_cookie(args->pid);
+ core_sched_get_and_print_cookie(args->pid);
+ } else {
+ pid_t pid = getpid();
+ core_sched_create_cookie(pid, args->type);
+ cookie_t after = core_sched_get_cookie(pid);
+ printf("%s: set cookie of PID %d to 0x%lx\n",
+ program_invocation_short_name, pid, after);
+ }
+
+ if (execvp(argv[0], argv)) {
+ errexec(argv[0]);
+ }
+}
+
+void core_sched_get_and_print_cookie(pid_t pid)
+{
+ cookie_t after = core_sched_get_cookie(pid);
+ printf("%s: set cookie of PID %d to 0x%lx\n",
+ program_invocation_short_name, pid, after);
+}
+
+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;
+ }
+
+ bad_usage("'%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 [-p PID]\n"), program_invocation_short_name);
+ fprintf(stdout, _(" %s --new [-t <TYPE>] -p <PID>\n"),
+ program_invocation_short_name);
+ fprintf(stdout, _(" %s --new [-t <TYPE>] -- PROGRAM [ARGS...]\n"),
+ program_invocation_short_name);
+ fprintf(stdout, _(" %s --copy -p <PID> [-t <TYPE>] -d <PID>\n"),
+ program_invocation_short_name);
+ fprintf(stdout,
+ _(" %s --copy -p <PID> [-t <TYPE>] -- PROGRAM [ARGS...]\n"),
+ program_invocation_short_name);
+
+ fputs(USAGE_SEPARATOR, stdout);
+ fputsln(_("Manage core scheduling cookies for tasks."), stdout);
+
+ fputs(USAGE_FUNCTIONS, stdout);
+ fputsln(_(" -n, --new assign a new core scheduling cookie to an existing PID or\n"
+ " execute a program with a new cookie."),
+ stdout);
+ fputsln(_(" -c, --copy copy the core scheduling cookie from an existing PID to\n"
+ " either another PID, or copy it to a new program"),
+ stdout);
+ fputsln(_("\n If no function is provided, it will retrieve and print the cookie from\n"
+ " the PID provided via --pid.\n"),
+ stdout);
+
+ fputs(USAGE_OPTIONS, stdout);
+ fputsln(_(" -p, --pid <PID> operate on an existing PID"), stdout);
+ fputsln(_(" -d, --dest <PID> when copying a cookie from an existing PID, --dest is\n"
+ " the destination PID where to copy the cookie to."),
+ stdout);
+ fputsln(_(" -t, --type <TYPE> type of the destination PID, or the type of the PID\n"
+ " when a new core scheduling cookie is created.\n"
+ " Can be one of the following: pid, tgid or pgid.\n"
+ " The default is tgid."),
+ stdout);
+ fputs(USAGE_SEPARATOR, stdout);
+ fprintf(stdout,
+ USAGE_HELP_OPTIONS(
+ 20)); /* char offset to align option descriptions */
+ fprintf(stdout, USAGE_MAN_TAIL("coresched(1)"));
+ exit(EXIT_SUCCESS);
+}
+
+void parse_arguments(int argc, char **argv, struct args *args)
+{
+ int c;
+
+ static const struct option longopts[] = {
+ { "new", no_argument, NULL, 'n' },
+ { "copy", no_argument, NULL, 'c' },
+ { "pid", required_argument, NULL, 'p' },
+ { "dest", required_argument, NULL, 'd' },
+ { "type", required_argument, NULL, 't' },
+ { "version", no_argument, NULL, 'V' },
+ { "help", no_argument, NULL, 'h' },
+ { NULL, 0, NULL, 0 }
+ };
+ static const ul_excl_t excl[] = {
+ { 'c', 'n' }, // Cannot do both --new and --copy
+ { 'd', 'n' }, // Cannot have both --new and --dest
+ { 0 }
+ };
+
+ int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
+
+ while ((c = getopt_long(argc, argv, "ncp:d:t:Vh", longopts, NULL)) !=
+ -1) {
+ err_exclusive_options(c, longopts, excl, excl_st);
+ switch (c) {
+ case 'n':
+ args->cmd = SCHED_CORE_CMD_NEW;
+ break;
+ case 'c':
+ args->cmd = SCHED_CORE_CMD_COPY;
+ break;
+ case 'p':
+ args->pid = strtopid_or_err(
+ optarg, "Failed to parse PID for -p/--pid");
+ break;
+ case 'd':
+ args->dest = strtopid_or_err(
+ optarg, "Failed to parse PID for -d/--dest");
+ 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 (args->cmd == SCHED_CORE_CMD_COPY && !args->pid) {
+ bad_usage("--copy: requires a -p/--pid");
+ }
+
+ // More arguments have been passed, which means that the user wants to run
+ // another program with a core scheduling cookie.
+ if (argc > optind) {
+ switch (args->cmd) {
+ case SCHED_CORE_CMD_GET:
+ bad_usage("Unknown command");
+ break;
+ case SCHED_CORE_CMD_NEW:
+ if (args->pid) {
+ bad_usage(
+ "--new: cannot accept both a -p/--pid and a command");
+ } else {
+ args->exec_argv_offset = optind;
+ }
+ break;
+ case SCHED_CORE_CMD_COPY:
+ if (args->dest) {
+ bad_usage(
+ "--copy: cannot accept both a destination PID "
+ "-d/--dest and a command")
+ } else {
+ args->exec_argv_offset = optind;
+ }
+ break;
+ }
+ }
+
+ if (argc <= optind) {
+ if (args->cmd == SCHED_CORE_CMD_NEW && !args->pid) {
+ bad_usage(
+ "--new: requires either a -p/--pid or a command");
+ }
+ if (args->cmd == SCHED_CORE_CMD_COPY && !args->dest) {
+ bad_usage(
+ "--copy: requires either a -d/--dest or a command");
+ }
+ }
+}
+
+int main(int argc, char **argv)
+{
+ struct args args = { 0 };
+ args.cmd = SCHED_CORE_CMD_GET;
+ args.type = PR_SCHED_CORE_SCOPE_THREAD_GROUP;
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ close_stdout_atexit();
+
+ parse_arguments(argc, argv, &args);
+
+ cookie_t cookie = 0;
+
+ switch (args.cmd) {
+ case SCHED_CORE_CMD_GET:
+ if (args.pid) {
+ cookie = core_sched_get_cookie(args.pid);
+ if (cookie) {
+ printf("%s: cookie of pid %d is 0x%lx\n",
+ program_invocation_short_name, args.pid,
+ cookie);
+ } else {
+ errx(ENODATA,
+ "pid %d doesn't have a core scheduling cookie",
+ args.pid);
+ }
+ } else {
+ usage();
+ exit(0);
+ }
+ break;
+ case SCHED_CORE_CMD_NEW:
+ if (args.pid) {
+ core_sched_create_cookie(args.pid, args.type);
+ core_sched_get_and_print_cookie(args.pid);
+ } else {
+ core_sched_exec_with_cookie(&args, argv);
+ }
+ break;
+ case SCHED_CORE_CMD_COPY:
+ if (args.dest) {
+ core_sched_copy_cookie(args.pid, args.dest, args.type);
+ } else {
+ core_sched_exec_with_cookie(&args, argv);
+ }
+ break;
+ default:
+ usage();
+ exit(1);
+ }
+}
--
2.44.0
^ permalink raw reply related
* [ANNOUNCE] util-linux v2.40
From: Karel Zak @ 2024-03-27 15:10 UTC (permalink / raw)
To: linux-kernel, linux-fsdevel, util-linux
The util-linux release v2.40 is available at
http://www.kernel.org/pub/linux/utils/util-linux/v2.40/
Feedback and bug reports, as always, are welcomed.
Karel
util-linux 2.40 Release Notes
=============================
Release highlights
------------------
libmount:
The libmount monitor has been enhanced to be more user-friendly for
userspace-specific mount options (e.g., GNOME gVFS).
The new mount kernel API can be enabled/disabled using the environment
variable LIBMOUNT_FORCE_MOUNT2={always, never, auto}.
libsmartcols:
The library now supports filtering expressions (refer to the scols-filter(5)
man page). Applications can utilize the filter before gathering all data
for output, reducing resource usage and improving performance. Currently,
this feature is employed in lsfd and lsblk. Example:
lsblk --filter 'NAME !~ "sd[ab]"'
The library now supports counters (based on filters). For instance:
lsfd --summary=only \
-C 'netlink sockets':'(NAME =~ "NETLINK:.*")' \
-C 'unix sockets':'(NAME =~ "UNIX:.*")'
VALUE COUNTER
57 netlink sockets
1552 unix sockets
* liblastlog2 and pam_lastlog2:
Introducing a new library, liblastlog2, to implement lastlog replacement
using a SQLite3 database in /var/lib/lastlog/lastlog2.db. This implementation
is Y2038 safe, and the database size is independent of UIDs.
A new command, lastlog2, is now available.
lastlog2 is utilized in pam_lastlog2.
* libuuid iImproved support for 64-bit time.
* A new command, lsclocks, is introduced to display system clocks (realtime,
monotonic, boottime, etc.).
* A new small command, enosys, is introduced to make syscalls fail with ENOSYS.
The command is based on prctl( SECCOMP_MODE_FILTER ) syscall.
* A new command, exch, is introduced to atomically exchanges paths between
two files. The command is based on renameat2( RENAME_EXCHANGE ) syscall.
* A new command, setpgid, is introduced to run a program in a new process group.
* login(1) now supports systemd service credentials. 'login.noauth' is now
supported.
Security issues
---------------
This release fixes CVE-2024-28085. The wall command does not filter escape
sequences from command line arguments. The vulnerable code was introduced in
commit cdd3cc7fa4 (2013). Every version since has been vulnerable.
This allows unprivileged users to put arbitrary text on other users terminals,
if mesg is set to y and *wall is setgid*. Not all distros are affected (e.g.
CentOS, RHEL, Fedora are not; Ubuntu and Debian wall is both setgid and mesg is
set to y by default).
Changes between v2.39 and v2.40
-------------------------------
AUTHORS:
- add tools contributed by myself [Thomas Weißschuh]
Add Phytium FTC862 cpu model. fix:
- #2486 [unknown]
Documentation:
- add basic smoketest for boilerplate.c [Thomas Weißschuh]
Fix typo:
- octen -> octet [zeyun chen]
agetty:
- Load autologin user from agetty.autologin credential [Daan De Meyer]
- include fileutils.h [Thomas Weißschuh]
- remove duplicate include [Karel Zak]
- use get_terminal_default_type() [Karel Zak]
- use sd_get_sessions() for number of users (#2088) [Thorsten Kukuk]
audit-arch:
- add support for alpha [Thomas Weißschuh]
autotools:
- add dependence on libsmartcols for lsclocks [Karel Zak]
- add missing dist_noinst_DATA [Karel Zak]
- check for flex in autogen.sh [Karel Zak]
- cleanup lastlog2 stuff [Karel Zak]
- fix AC_DEFINE_UNQUOTED() use [Karel Zak]
- fix AC_PROG_LEX use [Karel Zak]
- fix librtas check [Karel Zak]
- fix non-Linux build [Karel Zak, Samuel Thibault]
- fix typos [Karel Zak]
- use stamp file to build filter parser, improve portability [Karel Zak]
bash-completion:
- (fadvise) fix a typo [Masatake YAMATO]
- (lslocks) add --bytes option to the rules [Masatake YAMATO]
- add -T to last [Karel Zak]
- make sure that "lastb" actually completes [Eli Schwartz]
- update for mkswap [Karel Zak]
blkdev.h:
- avoid some unused argument warnings [Thomas Weißschuh]
- update location of SCSI device types [Christoph Anton Mitterer]
blkid:
- fix call to err_exclusive_options [Thomas Weißschuh]
blkpr:
- store return value of getopt_long in int [Thomas Weißschuh]
blkzone:
- don't take address of struct blk_zone [Thomas Weißschuh]
blockdev:
- add missing verbose output for --getsz [Christoph Anton Mitterer]
- add support for BLKGETZONESZ [Thomas Weißschuh]
- properly check for BLKGETZONESZ ioctl [Thomas Weißschuh]
build:
- use -std=c99 and -std=c++11 by default [Thomas Weißschuh]
build-sys:
- (tests) validate that time_t is 64bit [Thomas Weißschuh]
- add --disable-exch [Karel Zak]
- add --disable-waitpid [Frantisek Sumsal]
- add AX_COMPARE_VERSION [Thomas Weißschuh]
- add enosys and syscalls.h to gitignore [Enze Li]
- backport autoconf year2038 macros [Thomas Weißschuh]
- don't call pkg-config --static if unnecessary [Karel Zak]
- fail build for untracked files [Thomas Weißschuh]
- fix libmount/src/hooks.c use [Karel Zak]
- fix po-man clean [Karel Zak]
- fix typo in waitpid check [Thomas Weißschuh]
- improve checkadoc [Karel Zak]
- introduce localstatedir [Karel Zak]
- make sure everywhere is localstatedir [Karel Zak]
- only build col on glibc [Thomas Weißschuh]
- only pass --failure-level if supported [Thomas Weißschuh]
- rearrange gitignore in alphabetical order [Enze Li]
- release++ (v2.40-rc1) [Karel Zak]
- release++ (v2.40-rc2) [Karel Zak]
- try to always use 64bit time support on glibc [Thomas Weißschuh]
buildsys:
- warn on usage of VLAs [Thomas Weißschuh]
- warn on usage of alloca() [Thomas Weißschuh]
c.h:
- make err_nonsys available [Thomas Weißschuh]
cal:
- avoid out of bound write [Thomas Weißschuh]
- fix error message for bad -c argument [Jakub Wilk]
- fix long option name for -c [Jakub Wilk]
cfdisk:
- add hint about labels for bootable flag [Karel Zak]
- ask y/n before wipe [Karel Zak]
- fix menu behavior after writing changes [Karel Zak]
- properly handle out-of-order partitions during resize [Thomas Weißschuh]
chcpu(8):
- document limitations of -g [Stanislav Brabec]
chrt:
- (man) add note about --sched-period lower limit [Karel Zak]
- (tests) don't mark tests as known failed [Thomas Weißschuh]
- (tests) increase deadline test parameters [Thomas Weißschuh]
- allow option separator [Thomas Weißschuh]
chsh:
- use libeconf to read /etc/shells [Thorsten Kukuk]
ci:
- (codeql) ignore cpp/uncontrolled-process-operation [Thomas Weißschuh]
- add OpenWrt SDK based CI jobs [Thomas Weißschuh]
- also use GCC 13 for sanitizer builds [Thomas Weißschuh]
- build on old distro [Thomas Weißschuh]
- build with GCC 13/11 [Thomas Weißschuh]
- build with clang 17 [Thomas Weißschuh]
- cache openwrt sdk [Thomas Weißschuh]
- cancel running jobs on push [Frantisek Sumsal]
- collect coverage on _exit() as well [Frantisek Sumsal]
- consistently use gcc 13 during CI [Thomas Weißschuh]
- disable cpp/path-injection rule [Thomas Weißschuh]
- don't combine -Werror and -fsanitize [Thomas Weißschuh]
- enable -Werror for meson [Thomas Weißschuh]
- fix indentation [Frantisek Sumsal]
- hide coverage-related stuff begind --enable-coverage [Frantisek Sumsal]
- mark source directory as safe [Thomas Weißschuh]
- packit add flex [Karel Zak]
- prevent prompts during installation [Thomas Weißschuh]
- reduce aslr level to avoid issues with ASAN [Thomas Weißschuh]
- run full testsuite under musl libc [Thomas Weißschuh]
- tweak build dir's ACL when collecting coverage [Frantisek Sumsal]
- use clang 16 [Thomas Weißschuh]
column:
- fix -l [Karel Zak]
- fix memory leak [Thomas Weißschuh]
coverage.h:
- mark _exit as noreturn [Thomas Weißschuh]
ctrlaltdel:
- remove unnecessary uid check [JJ-Meng]
disk-utils:
- add SPDX and Copyright notices [Karel Zak]
dmesg:
- (tests) validate json output [Thomas Weißschuh]
- -r LOG_MAKEPRI needs fac << 3 [Edward Chron]
- Delete redundant pager setup [Karel Zak]
- add caller_id support [Edward Chron]
- add support for reserved and local facilities [Thomas Weißschuh]
- cleanup function names [Karel Zak]
- correctly print all supported facility names [Thomas Weißschuh]
- don't affect delta by --since [Karel Zak]
- error out instead of silently ignoring force_prefix [Thomas Weißschuh]
- fix FD leak [Karel Zak]
- fix delta calculation [Karel Zak]
- fix wrong size calculation [Karel Zak]
- make kmsg read() buffer big enough for kernel [anteater]
- man and coding style changes [Karel Zak]
- only write one message to json [Thomas Weißschuh]
- open-code LOG_MAKEPRI [Thomas Weißschuh]
- support for additional human readable timestamp [Rishabh Thukral]
- support reading kmsg format from file [Thomas Weißschuh]
- use symbolic defines for second conversions [Thomas Weißschuh]
docs:
- add BSD-2-Clause [Karel Zak]
- add SPDX to boilerplate.c [Karel Zak]
- add enosys to ReleaseNotes [Karel Zak]
- add exch to ReleaseNotes [Karel Zak]
- add hints about systemd [Karel Zak]
- add note about stable branches [Karel Zak]
- add setpgid do ReleaseNotes [Karel Zak]
- cleanup public domain license texts [Karel Zak]
- fix a typo [Masatake YAMATO]
- fix typos [Jakub Wilk]
- improve howto-pull-request [Karel Zak]
- move Copyright in boilerplate.c [Karel Zak]
- move GPL-2.0 license text to Docimentation directory [Karel Zak]
- remove duplicated author name in namei.1.adoc [Emanuele Torre]
- update AUTHORS file [Karel Zak]
- update v2.40-ReleaseNotes [Karel Zak]
- use HTTPS for GitHub clone URLs [Jakub Wilk]
- use proper XSPD identifier for GPL-2.0 [Karel Zak]
eject:
- (tests) don't write mount hint to terminal [Karel Zak]
enosys:
- add --list [Thomas Weißschuh]
- add bash completion [Thomas Weißschuh]
- add common arguments [Thomas Weißschuh]
- add helpers for 64 bit integer loading [Thomas Weißschuh]
- add manpage [Thomas Weißschuh]
- add support for MIPS, PowerPC and ARC [Thomas Weißschuh]
- add support for ioctl blocking [Thomas Weißschuh]
- add support for loongarch [Thomas Weißschuh]
- add support for sparc [Thomas Weißschuh]
- add test [Thomas Weißschuh]
- allow CPU speculation [Thomas Weißschuh]
- avoid warnings when no aliases are found [Thomas Weißschuh]
- build BPF dynamically [Thomas Weißschuh]
- don't require end-of-options marker [Thomas Weißschuh]
- don't validate that numbers are found from headers [Thomas Weißschuh]
- drop unnessecary load of ioctl number [Thomas Weißschuh]
- enable locale handling [Thomas Weißschuh]
- find syscalls at build time [Thomas Weißschuh]
- fix build on hppa [John David Anglin]
- fix native arch for s390x [Thomas Weißschuh]
- improve checks for EXIT_NOTSUPP [Thomas Weißschuh]
- include sys/syscall.h [Thomas Weißschuh]
- list syscall numbers [Thomas Weißschuh]
- make messages useful for users [Thomas Weißschuh]
- mark variable static [Thomas Weißschuh]
- move from tests/helpers/test_enosys.c [Thomas Weißschuh]
- only build if AUDIT_ARCH_NATIVE is defined [Thomas Weißschuh]
- optimize bytecode when execve is not blocked [Thomas Weißschuh]
- optimize bytecode when no ioctls are blocked [Thomas Weißschuh]
- properly block execve syscall [Thomas Weißschuh]
- provide a nicer build message for syscalls.h generation [Thomas Weißschuh]
- remove long jumps from BPF [Thomas Weißschuh]
- remove unneeded inline variable declaration [Thomas Weißschuh]
- split audit arch detection into dedicated header [Thomas Weißschuh]
- store blocked syscalls in list instead of array [Thomas Weißschuh]
- syscall numbers are "long" [Thomas Weißschuh]
- translate messages [Thomas Weißschuh]
- validate syscall architecture [Thomas Weißschuh]
exch:
- Add man page to po4a.cfg to make it translatable [Mario Blättermann]
- cosmetic changes [Karel Zak]
- fix typo [Karel Zak]
- new command exchaging two files atomically [Masatake YAMATO]
- properly terminate options array [Thomas Weißschuh]
- use NULL rather than zero [Karel Zak]
exec_shell:
- use xasprintf [Thomas Weißschuh]
fadvise:
- (test) don't compare fincore page counts [Thomas Weißschuh]
- (test) dynamically calculate expected test values [Thomas Weißschuh]
- (test) test with 64k blocks [Thomas Weißschuh]
- (tests) factor out calls to "fincore" [Thomas Weißschuh]
- Fix markup in man page [Mario Blättermann]
fallocate:
- fix the way to evaluate values returned from posix_fallocate [Masatake YAMATO]
fdisk:
- (man) fix typo, improve readability [Karel Zak]
- add support for partition resizing [Thomas Weißschuh]
- guard posix variable [Thomas Weißschuh]
- remove usage of VLA [Thomas Weißschuh]
fileeq:
- optimize size of ul_fileeq_method [Thomas Weißschuh]
fincore:
- (tests) adapt alternative testcases to new header format [Thomas Weißschuh]
- (tests) also use nosize error file [Thomas Weißschuh]
- (tests) fix double log output [Chris Hofstaedtler]
- add --output-all [Thomas Weißschuh]
- fix alignment of column listing in --help [Thomas Weißschuh]
- refactor output formatting [Thomas Weißschuh]
- report data from cachestat() [Thomas Weißschuh]
findmnt:
- add --list-columns [Karel Zak]
- add -I, --dfi options for imitating the output of df -i [Masatake YAMATO]
- add inode-related columns for implementing "df -i" like output [Masatake YAMATO]
- remove deleted option from manual [Chris Hofstaedtler]
- use zero to separate lines in multi-line cells [Karel Zak]
flock:
- initialize timevals [-Werror=maybe-uninitialized] [Karel Zak]
fsck:
- initialize timevals [-Werror=maybe-uninitialized] [Karel Zak]
fstab:
- Fix markup in man page [Mario Blättermann]
- add hint about systemd reload [Karel Zak]
github:
- add labeler [Karel Zak]
- check apt-cache in more robust way [Karel Zak]
- check apt-cache in more robust way (v2) [Masatake YAMATO]
- fix build with clang and in ubuntu build-root [Karel Zak]
gitignore:
- ignore exch [Thomas Weißschuh]
- ignore setpgid binary [Christian Göttsche]
hardlink:
- (man) add missing comma [Jakub Wilk]
- Fix markup in man page [Mario Blättermann]
- fix fiemap use [Karel Zak]
hexdump:
- Add missing section header in man page [Mario Blättermann]
- add '--one-byte-hex' format option [Tomasz Wojdat]
- add new format-strings test case [Tomasz Wojdat]
- check blocksize when display data [Karel Zak]
- use xasprintf to build string [Thomas Weißschuh]
hwclock:
- Improve set error in the face of jitter [Eric Badger]
- add --vl-read, --vl-clear documentation and bash-completion [Rasmus Villemoes]
- add support for RTC_VL_READ/RTC_VL_CLR ioctls [Rasmus Villemoes]
- handle failure of audit_log_user_message [Thomas Weißschuh]
- reuse error message [Karel Zak]
include:
- add DragonFlyBSD GPT partition types [Thomas Weißschuh]
- add U-Boot environment partition type [Thomas Weißschuh]
- add some more ChromeOS partition types [Thomas Weißschuh]
- define pidfd syscalls if needed [Markus Mayer]
include/audit-arch:
- add missing SPDX [Karel Zak]
include/bitops.h:
- Remove bswap* compatibility hack for FreeBSD [Daniel Engberg]
include/c.h:
- add helpers for unaligned structure access [Thomas Weißschuh]
- handle members of const struct [Thomas Weißschuh]
- implement reallocarray [Thomas Weißschuh]
include/crc64:
- add missing license header [Karel Zak]
include/strutils:
- add ul_strtold() [Karel Zak]
irqtop:
- fix numeric sorting [Valery Ushakov]
jsonwrt:
- add ul_jsonwrt_value_s_sized [Thomas Weißschuh]
last:
- Add -T option for tab-separated output [Trag Date]
- avoid out of bounds array access [biubiuzy]
last(1):
- Document -T option for tab-separated output [Trag Date]
lastlog:
- cleanup function definitions [Karel Zak]
- improve errors printing [Karel Zak]
lastlog2:
- Don't print space if Service column is not printed [Miika Alikirri]
- Fix various issues with meson [Fabian Vogt]
- convert check_user() to boolean-like macro [Karel Zak]
- improve coding style [Karel Zak]
- make longopts[] static-const [Karel Zak]
- rename tmpfiles [Christian Hesse]
ldattach:
- don't call exit() from signal handler [Thomas Weißschuh]
ldfd:
- delete unnecessary ';' [Masatake YAMATO]
lib:
- remove pager.c from libcommon [Karel Zak]
lib/ include/:
- cleanup licence headers [Karel Zak]
lib/buffer:
- make buffer usable for non-string data [Karel Zak]
lib/c_strtod:
- fix uselocale() fallback if strtod_l() is not available [Alan Coopersmith]
lib/caputils:
- fix integer handling issues [coverity scan] [Karel Zak]
lib/color-names:
- fix licence header [Karel Zak]
lib/colors:
- correct documentation of colors_add_scheme() [Thomas Weißschuh]
lib/cpuset:
- exit early from cpulist_parse [Thomas Weißschuh]
- make max variable const [Thomas Weißschuh]
lib/env:
- avoid underflow of read_all_alloc() return value [Thomas Weißschuh]
- fix function name remote_entry -> remove_entry [Thomas Weißschuh]
lib/idcache:
- always gracefully handle null cache [Thomas Weißschuh]
lib/jsonwrt:
- add support for float numbers [Karel Zak]
lib/loopdev:
- consistently return error values from loopcxt_find_unused() [Thomas Weißschuh]
- document function return values [Thomas Weißschuh]
lib/mbsalign:
- calculate size of decoded string [Karel Zak]
lib/mbsedit:
- remove usage of VLA [Thomas Weißschuh]
lib/pager:
- Allow PAGER commands with options [Dragan Simic]
- Apply pager-specific fixes only when needed [Dragan Simic]
lib/path:
- Set errno in case of fgets failure [Tobias Stoeckmann]
- fix possible out of boundary access [Tobias Stoeckmann]
- fix typos [Tobias Stoeckmann]
- remove ul_prefix_fopen [Tobias Stoeckmann]
- remove usage of VLA [Thomas Weißschuh]
- set errno in case of error [Tobias Stoeckmann]
lib/pty-session:
- Don't ignore SIGHUP. [Kuniyuki Iwashima]
- initialize timevals [-Werror=maybe-uninitialized] [Karel Zak]
lib/sha1:
- fix for old glibc [Karel Zak]
lib/shells:
- Plug econf memory leak [Tobias Stoeckmann]
- initialize free-able variables [Karel Zak]
- remove space after function name [Karel Zak]
lib/strutils:
- add strfappend and strvfappend [Masatake YAMATO]
- add ul_next_string() [Karel Zak]
- fix typo [Jakub Wilk]
lib/timeutils:
- (parse_timestamp_reference) report errors on overflow [Thomas Weißschuh]
- (tests) add test for formatting [Thomas Weißschuh]
- (tests) move to struct timespec [Thomas Weißschuh]
- constify some arguments [Thomas Weißschuh]
- don't use glibc strptime extension [Thomas Weißschuh]
- implement nanosecond formatting [Thomas Weißschuh]
- implement timespec formatting [Thomas Weißschuh]
- print error if timestamp can't be parsed [Thomas Weißschuh]
- test epoch timestamp [Thomas Weißschuh]
lib/ttyutils:
- add get_terminal_default_type() [Karel Zak]
libblkid:
- (adapted_raid) validate size in standard minsz predicate [Thomas Weißschuh]
- (bcache) also calculate checksum over journal buckets [Thomas Weißschuh]
- (bcache) extend superblock definition [Thomas Weißschuh]
- (bcache) report block size [Thomas Weißschuh]
- (bcache) report label [Thomas Weißschuh]
- (bcache) report version [Thomas Weißschuh]
- (bcachefs) adapt to major.minor version [Thomas Weißschuh]
- (bcachefs) add support for 2nd superblock at 2MiB [Thomas Weißschuh]
- (bcachefs) add support for sub-device labels [Thomas Weißschuh]
- (bcachefs) add support for superblock at end of disk [Thomas Weißschuh]
- (bcachefs) compare against offset from idmag [Thomas Weißschuh]
- (bcachefs) fix compiler warning [-Werror=sign-compare] [Karel Zak]
- (bcachefs) fix not detecting large superblocks [Colin Gillespie]
- (bcachefs) fix size validation [Thomas Weißschuh]
- (cramfs) use magic hint [Thomas Weißschuh]
- (ddf_raid) validate size in standard minsz predicate [Thomas Weißschuh]
- (dev) use strdup to duplicate string [Thomas Weißschuh]
- (drbd) avoid unaligned accesses [Thomas Weißschuh]
- (drbd) reduce false-positive [biubiuzy]
- (drbd) use magics [Thomas Weißschuh]
- (drbd) validate size in standard minsz predicate [Thomas Weißschuh]
- (drbd) validate zero padding [Thomas Weißschuh]
- (hfsplus) reduce false positive [Karel Zak]
- (highpoint_raid) validate size in standard minsz predicate [Thomas Weißschuh]
- (isw_raid) validate size in standard minsz predicate [Thomas Weißschuh]
- (jmicron_raid) avoid modifying shared buffer [Thomas Weißschuh]
- (jmicron_raid) use checksum APIs [Thomas Weißschuh]
- (jmicron_raid) validate size in standard minsz predicate [Thomas Weißschuh]
- (lsi_raid) validate size in standard minsz predicate [Thomas Weißschuh]
- (lvm2) read complete superblock [Thomas Weißschuh]
- (ntfs) validate that sector_size is a power of two [Thomas Weißschuh]
- (nvidia_raid) validate size in standard minsz predicate [Thomas Weißschuh]
- (probe) add magic hint [Thomas Weißschuh]
- (probe) allow superblock offset from end of device [Thomas Weißschuh]
- (probe) handle probe without chain gracefully [Thomas Weißschuh]
- (probe) read data in chunks [Thomas Weißschuh]
- (probe) remove chunking from blkid_probe_get_idmag() [Thomas Weißschuh]
- (probe) remove duplicate log [Thomas Weißschuh]
- (promise_raid) validate size in standard minsz predicate [Thomas Weißschuh]
- (silicon_raid) validate size in standard minsz predicate [Thomas Weißschuh]
- (stratis) remove usage of VLA [Thomas Weißschuh]
- (superblocks) add helper blkid32_to_cpu() [Thomas Weißschuh]
- (vfat) avoid modifying shared buffer [Thomas Weißschuh]
- (via_raid) validate size in standard minsz predicate [Thomas Weißschuh]
- (vxfs) add test files [Thomas Weißschuh]
- (vxfs) report endianness [Thomas Weißschuh]
- (vxfs) simplify prober [Thomas Weißschuh]
- (vxfs) use hex escape for magic [Thomas Weißschuh]
- (zonefs) avoid modifying shared buffer [Thomas Weißschuh]
- Check offset in LUKS2 header [Milan Broz]
- add remove_buffer helper [Thomas Weißschuh]
- avoid aligning out of probing area [Thomas Weißschuh]
- avoid memory leak of cachefile path [Thomas Weißschuh]
- avoid use of non-standard typeof() [Thomas Weißschuh]
- constify cached disk data [Thomas Weißschuh]
- constify return values of blkid_probe_get_sb [Thomas Weißschuh]
- exfat fix fail to find volume label [Yuezhang Mo]
- fix topology chain types mismatch [Karel Zak]
- improve portability [Samuel Thibault]
- introduce blkid_wipe_all [Thomas Weißschuh]
- introduce helper to get offset for idmag [Thomas Weißschuh]
- iso9660 Define all fields in iso_volume_descriptor according to ECMA-119 4th edition spec [Pali Rohár]
- iso9660 Implement full High Sierra CDROM format support [Pali Rohár]
- jfs - avoid undefined shift [Milan Broz]
- limit read buffer size [Thomas Weißschuh]
- make enum libblkid_endianness lowercase [Thomas Weißschuh]
- protect shared buffers against modifications [Thomas Weißschuh]
- prune unneeded buffers [Thomas Weißschuh]
- reset errno before calling probefuncs [Thomas Weißschuh]
libfdisk:
- (dos) remove usage of VLA [Thomas Weißschuh]
- (sgi) use strncpy over strcpy [Thomas Weißschuh]
- (sun) properly initialize partition data [Thomas Weißschuh]
- (tests) fix tests for removal of non-blockdev sync() [Thomas Weißschuh]
- add fdisk_partition_get_max_size [Thomas Weißschuh]
- add shortcut for Linux extended boot [Thomas Weißschuh]
- constify builtin fdisk_parttype [Thomas Weißschuh]
- fdisk_deassign_device only sync(2) blockdevs [наб]
- fix typo in debug message [Thomas Weißschuh]
- handle allocation failure in fdisk_new_partition [Thomas Weißschuh]
- reset errno before calling read() [Thomas Weißschuh]
- use new blkid_wipe_all helper [Thomas Weißschuh]
liblastlog2:
- fix leaks [Karel Zak]
- fix pkg-config inclidedir [Karel Zak]
libmount:
- (context) avoid dead store [Thomas Weißschuh]
- (optlist) correctly detect ro status [Thomas Weißschuh]
- (python) work around python 3.12 bug [Thomas Weißschuh]
- (tests) add helper for option list splitting [Thomas Weißschuh]
- (tests) don't require root for update tests [Thomas Weißschuh]
- (tests) fix --filesystems crash on invalid argument [Thomas Weißschuh]
- (tests) fix --filesystems test argument parsing [Thomas Weißschuh]
- (tests) split helper tests [Thomas Weißschuh]
- (utils) avoid dead store [Thomas Weißschuh]
- (utils) fix statx fallback [Thomas Weißschuh]
- (veritydev) use asprintf to build string [Thomas Weißschuh]
- Fix export of mnt_context_is_lazy and mnt_context_is_onlyonce [Matt Turner]
- Fix regression when mounting with atime [Filipe Manana]
- accept '\' as escape for options separator [Karel Zak]
- add helper to log mount messages as emitted by kernel [Thomas Weißschuh]
- add missing utab options after helper call [Karel Zak]
- add mnt_context_within_helper() wrapper [Karel Zak]
- add private mnt_optstr_get_missing() [Karel Zak]
- add sample to test fs and context relation [Karel Zak]
- add utab.act file [Karel Zak]
- always ignore user=<name> [Karel Zak]
- change syscall status macros to be functions [Thomas Weißschuh]
- check for availability of mount_setattr [Thomas Weißschuh]
- check for linux/mount.h [Markus Mayer]
- check for struct statx [Markus Mayer]
- cleanup --fake mode [Karel Zak]
- cleanup enosys returns from mount hoop [Karel Zak]
- cleanup locking in table update code [Karel Zak]
- don't assume errno after failed asprintf() [Karel Zak]
- don't call hooks after mount.<type> helper [Karel Zak]
- don't call mount.<type> helper with usernames [Karel Zak]
- don't hold write fd to mounted device [Jan Kara]
- don't initialize variable twice (#2714) [Thorsten Kukuk]
- don't pass option "defaults" to helper [Thomas Weißschuh]
- fix copy & past bug in lock initialization [Karel Zak]
- fix fsconfig value unescaping [Karel Zak]
- fix options prepend/insert and merging [Karel Zak]
- fix possible NULL dereference [coverity scan] [Karel Zak]
- fix statx() includes [Karel Zak]
- fix sync options between context and fs structs [Karel Zak]
- fix typo [Debarshi Ray]
- gracefully handle NULL path in mnt_resolve_target() [Thomas Weißschuh]
- guard against sysapi == NULL [Thomas Weißschuh]
- handle failure to apply flags as part of a mount operation [Debarshi Ray]
- ifdef statx() call [Karel Zak]
- ignore unwanted kernel events in monitor [Karel Zak]
- improve EPERM interpretation [Karel Zak]
- improve act file close [Karel Zak]
- improve mnt_table_next_child_fs() [Karel Zak]
- introduce /run/mount/utab.event [Karel Zak]
- introduce LIBMOUNT_FORCE_MOUNT2={always,never,auto} [Karel Zak]
- introduce reference counting for libmnt_lock [Karel Zak]
- make sure "option=" is used as string [Karel Zak]
- make.stx_mnt_id use more robust [Karel Zak]
- reduce utab.lock permissions [Karel Zak]
- report all kernel messages for fd-based mount API [Thomas Weißschuh]
- report failed syscall name [Karel Zak]
- report kernel message from new API [Karel Zak]
- report statx in features list [Karel Zak]
- test utab options after helper call [Thomas Weißschuh]
- update documentation for MNT_ERR_APPLYFLAGS [Debarshi Ray]
- use mount(2) for remount on Linux < 5.14 [Karel Zak]
- use some MS_* flags as superblock flags [Karel Zak]
libmount (python):
- simplify struct initialization [Thomas Weißschuh]
libsmartcols:
- (cell) consistently handle NULL argument [Thomas Weißschuh]
- (filter) Add on-demand data filler [Karel Zak]
- (filter) add ability to cast data [Karel Zak]
- (filter) add regular expression operators [Karel Zak]
- (filter) add upper case EQ,NE,LE,LT,GT and GE operators [Karel Zak]
- (filter) cleanup __filter_new_node() [Karel Zak]
- (filter) cleanup data types [Karel Zak]
- (filter) cleanup function arguments [Karel Zak]
- (filter) evaluate params [Karel Zak]
- (filter) fix dereferences and leaks [coverity scann] [Karel Zak]
- (filter) fix regex deallocation [Karel Zak]
- (filter) implement data basic operators [Karel Zak]
- (filter) implement logical operators [Karel Zak]
- (filter) improve holder status [Karel Zak]
- (filter) improve holder use [Karel Zak]
- (filter) improve scols_filter_assign_column() [Karel Zak]
- (filter) make holders API more generic [Karel Zak]
- (filter) move struct filter_expr [Karel Zak]
- (filter) move struct filter_param [Karel Zak]
- (filter) normalize param strings [Karel Zak]
- (filter) param data refactoring [Karel Zak]
- (filter) split code [Karel Zak]
- (filter) support empty values [Karel Zak]
- (filter) support period in identifier [Karel Zak]
- (filter) use also rpmatch() for boolean [Karel Zak]
- (man) fix typos [Masatake YAMATO]
- (sample) fix error message [Karel Zak]
- (samples) fix format truncation warning [Thomas Weißschuh]
- (samples) remove filter.c [Karel Zak]
- (samples/fromfile) properly handle return value from getline() [Thomas Weißschuh]
- (tests) add test for continuous json output [Thomas Weißschuh]
- Add --highlight option to filter sample [Karel Zak]
- Export internally used types to API [Karel Zak]
- accept '% -' in column name for filters [Karel Zak]
- accept also '/' in column name for filters [Karel Zak]
- accept apostrophe as quote for strings in filter [Karel Zak]
- accept no data for custom wrapping cells [Karel Zak]
- add --{export,raw,json} to wrap sample [Karel Zak]
- add API to join filter and columns [Karel Zak]
- add filter API docs [Karel Zak]
- add filter sample [Karel Zak]
- add filter support to 'fromfile' sample [Karel Zak]
- add new functions to API docs [Karel Zak]
- add parser header files [Karel Zak]
- add scols-filter.5 man page [Karel Zak]
- add scols_cell_refer_memory() [Karel Zak]
- add support for zero separated wrap data [Karel Zak]
- add table cursor [Karel Zak]
- add wrap-zero test [Karel Zak]
- always print vertical symbol [Karel Zak]
- build filter scanner and parser header files too [Karel Zak]
- cleanup datafunc() API [Karel Zak]
- don't directly access struct members [Karel Zak]
- don't include hidden headers in column width calculation [Thomas Weißschuh]
- drop spourious newline in between streamed JSON objects [Thomas Weißschuh]
- fix columns reduction [Karel Zak]
- fix filter param copying [Karel Zak]
- fix filter parser initialization [Karel Zak]
- fix memory leak on filter parser error [Karel Zak]
- fix typo in comment [Karel Zak]
- fix typo in parser tokens [Karel Zak]
- fix uninitialized local variable in sample [Karel Zak]
- flush correct stream [Thomas Weißschuh]
- free after error in filter sample [Karel Zak]
- handle nameless tables in export format [Thomas Weißschuh]
- implement filter based counters [Karel Zak]
- improve and fix scols_column_set_properties() [Karel Zak]
- improve cell data preparation for non-wrapping cases [Karel Zak]
- improve filter integration, use JSON to dump [Karel Zak]
- improve parser error messages [Karel Zak]
- introduce basic files for filter implementation [Karel Zak]
- introduce column type [Karel Zak]
- make calculation more robust [Karel Zak]
- make cell data printing more robust [Karel Zak]
- make sure counter is initialized [Karel Zak]
- multi-line cells refactoring [Karel Zak]
- only recognize closed object as final element [Thomas Weißschuh]
- prefer float in filter expression [Karel Zak]
- reset cell wrapping if all done [Karel Zak]
- search also by normalized column names (aka 'shellvar' name) [Karel Zak]
- support SCOLS_JSON_FLOAT in print API [Karel Zak]
- support \x?? for data by samples/fromfile.c [Karel Zak]
- update gitignore [Karel Zak]
libuuid:
- (test_uuid) make reading UUIDs from file more robust [Thomas Weißschuh]
- Add uuid_time64 for 64bit time_t on 32bit [Thorsten Kukuk]
- avoid truncate clocks.txt to improve performance [Goldwyn Rodrigues]
- fix uint64_t printf and scanf format [Karel Zak]
libuuid/src/gen_uuid.c:
- fix cs_min declaration [Fabrice Fontaine]
logger:
- initialize socket credentials contol union [Karel Zak]
- make sure path is terminated [coverity scan] [Karel Zak]
- use strncpy instead of strcpy [Thomas Weißschuh]
login:
- Initialize noauth from login.noauth credential [Daan De Meyer]
- Use pid_t for child_pid [Tobias Stoeckmann]
- access login.noauth file directly [Tobias Stoeckmann]
- document blank treatment in shell field [Tobias Stoeckmann]
- fix memory leak [coverity scan] [Karel Zak]
- ignore return of audit_log_acct_message [Thomas Weißschuh]
- move comment [Tobias Stoeckmann]
- prevent undefined ioctl and tcsetattr calls [Tobias Stoeckmann]
- simplify name creation [Tobias Stoeckmann]
- unify pw_shell script test [Tobias Stoeckmann]
- use correct terminal fd during setup [Tobias Stoeckmann]
- use xasprintf [Tobias Stoeckmann]
login-utils:
- Report crashes on reboot lines insted of overlapping uptimes [Troy Rollo]
- include libgen.h for basename API [Khem Raj]
loopdev:
- report lost loop devices [Junxiao Bi, Karel Zak]
losetup:
- add --loop-ref and REF column [Karel Zak]
- add MAJ a MIN for device and backing-file [Karel Zak]
- cleanup device node modes [Karel Zak]
- deduplicate find_unused() logic [Thomas Weißschuh]
- fix JSON MAJ MIN [Karel Zak]
- improve "sector boundary" warning [Karel Zak]
- make --output-all more usable [Karel Zak]
- report lost loop devices for finding free loop [Junxiao Bi]
lsblk:
- add --filter [Karel Zak]
- add --highlight [Karel Zak]
- add --list-columns [Karel Zak]
- add docs for filters and counters [Karel Zak]
- add hint that partition start is in sectors [Karel Zak]
- add scols counters support [Karel Zak]
- add separate MAJ and MIN columns [Karel Zak]
- always set column type [Karel Zak]
- define cell data-types, use raw data for SIZEs [Karel Zak]
- explain FSAVAIL in better way [Karel Zak]
- fix in-tree filtering [Karel Zak]
- ignore duplicate lines for counters [Karel Zak]
- improve --tree description [Karel Zak]
- make sure all line data are deallocated [Karel Zak]
- rename sortdata to rawdata [Karel Zak]
- report all unknown columns in filter [Karel Zak]
- split filter allocation and initialization [Karel Zak]
- support normalized column names on command line [Karel Zak]
- update after rebase [Karel Zak]
- use zero to separate lines in multi-line cells [Karel Zak]
lsclocks:
- Fix markup and typos in man page [Mario Blättermann]
- Fix markup in man page [Mario Blättermann]
- add --output-all [Thomas Weißschuh]
- add COL_TYPE [Thomas Weißschuh]
- add NS_OFFSET column [Thomas Weißschuh]
- add column RESOL for clock resolution [Thomas Weißschuh]
- add relative time [Thomas Weißschuh]
- add support for RTC [Thomas Weißschuh]
- add support for cpu clocks [Thomas Weißschuh]
- add support for dynamic clocks [Thomas Weißschuh]
- automatically discover dynamic clocks [Thomas Weißschuh]
- don't fail without dynamic clocks [Thomas Weißschuh]
- factor out path based clocks [Thomas Weißschuh]
- improve dynamic clocks docs and completion [Thomas Weißschuh]
- new util to interact with system clocks [Thomas Weißschuh]
- refer to correct lsclocks(1) manpage [Thomas Weißschuh]
- remove unused code [Karel Zak]
- rename column RESOLUTION to RESOL_RAW [Thomas Weißschuh]
- split out data function [Thomas Weißschuh]
- trim default columns [Thomas Weißschuh]
- use clock id from clock_getcpuclockid in add_cpu_clock [Alan Coopersmith]
lscpu:
- Even more Arm part numbers (early 2023) [Jeremy Linton]
- Use 4K buffer size instead of BUFSIZ [Khem Raj]
- add procfs-sysfs dump from VisionFive 2 [Jan Engelhardt]
- cure empty output of lscpu -b/-p [Jan Engelhardt]
- don't use NULL sharedmap [Karel Zak]
- fix caches separator for --parse=<list> [Karel Zak]
- initialize all variables (#2714) [Thorsten Kukuk]
- remove redundant include [Karel Zak]
- remove usage of VLA [Thomas Weißschuh]
- restructure op-mode printing [Thomas Weißschuh]
lscpu-cputype.c:
- assign value to multiple variables (ar->bit32 and ar->bit64) clang with -Wcomma will emit an warning of "misuse of comma operator". Since the value that will be assigned, is the same for both (bit32 and bit64), just assigning directly to both variables seems reasonable. [rilysh]
lsdf:
- (man page) revise text decoration [Masatake YAMATO]
- make the code for filling SOURCE, PARTITION, and MAJMIN reusable [Masatake YAMATO]
lsfd:
- (comment) fix a typo [Masatake YAMATO]
- (cosmetic) normalize whitespaces [Masatake YAMATO]
- (filter) accept floating point numbers in expressions [Masatake YAMATO]
- (filter) improve error message [Masatake YAMATO]
- (filter) reduce duplicated code in macro definitions [Masatake YAMATO]
- (filter) support floating point number used in columns [Masatake YAMATO]
- (filter) weakly support ARRAY_STRING and ARRAY_NUMBER json types [Masatake YAMATO]
- (man) add bps(8) and ss(8) to the "SEE ALSO" section [Masatake YAMATO]
- (man) document --list-columns as the way to list columns [Masatake YAMATO]
- (man) document the ENDPOINT column for UNIX socket [Masatake YAMATO]
- (man) fix the broken page output for the description of NAME column [Masatake YAMATO]
- (man) fix the form for the optional argument of --inet option [Masatake YAMATO]
- (man) refer to scols-filter(5) [Masatake YAMATO]
- (man) update the description of ENDPOINTS column of UNIX-Stream sockets [Masatake YAMATO]
- (man) write about SOCK.SHUTDOWN column [Masatake YAMATO]
- (man) write about XMODE.m and classical system calls for multiplexing [Masatake YAMATO]
- (refactor) introduce a content data type for char devices [Masatake YAMATO]
- (refactor) make the code comparing struct lock objects reusable [Masatake YAMATO]
- (refactor) make the code for traversing threads reusable [Masatake YAMATO]
- (refactor) make the way to handle character devices extensible [Masatake YAMATO]
- (refactor) move miscdev specific code to cdev_misc_ops [Masatake YAMATO]
- (refactor) rename a function, s/new_process/new_proc/g [Masatake YAMATO]
- (refactor) rename add_nodevs to read_mountinfo [Masatake YAMATO]
- (refactor) unify the invocations of sysfs_get_byteorder() [Masatake YAMATO]
- (test) add a case for testing a unix socket including newline characters in its path name [Masatake YAMATO]
- (tests) don't run mqueue test on byteorder mismatch [Thomas Weißschuh]
- (tests) fix process leak [Masatake YAMATO]
- (tests) fix typo [Thomas Weißschuh]
- Fix typos in man page [Mario Blättermann]
- add "nsfs" to the nodev_table to fill SOURCE column for nsfs files [Masatake YAMATO]
- add 'D' flag for representing deleted files to XMODE column [Masatake YAMATO]
- add 'm' flag representing "multiplexed by epoll_wait(2)" to XMODE column [Masatake YAMATO]
- add BPF-MAP.TYPE, BPF-MAP.TYPE.RAW, and BPF-MAP.ID columns [Masatake YAMATO]
- add BPF-PROG.TYPE, BPF-PROG.TYPE.RAW, and BPF-PROG.ID columns [Masatake YAMATO]
- add BPF.NAME column [Masatake YAMATO]
- add EVENTFD.ID column [Masatake YAMATO]
- add PTMX.TTY-INDEX column [Masatake YAMATO]
- add SOCK.SHUTDOWN column [Masatake YAMATO]
- add TUN.IFFACE, a column for interfaces behind tun devices [Masatake YAMATO]
- add a back pointer as a member of anon_eventfd_data [Masatake YAMATO]
- add a helper function for adding a nodev to the nodev_table [Masatake YAMATO]
- add a helper function, add_endpoint [Masatake YAMATO]
- add a helper function, init_endpoint [Masatake YAMATO]
- add a helper function, new_ipc [Masatake YAMATO]
- add a helper macro, foreach_endpoint [Masatake YAMATO]
- add a new type "mqueue", a type for POSIX Mqueue [Masatake YAMATO]
- add a whitespace [Masatake YAMATO]
- add attach_xinfo and get_ipc_class methods to cdev_ops [Masatake YAMATO]
- add comment listing functions names importing via #include [Masatake YAMATO]
- add const modifier [Thomas Weißschuh]
- add flags, [-lL], representing file lock/lease states to XMODE column [Masatake YAMATO]
- add tmpfs as source of sysvipc to the the nodev_table [Masatake YAMATO]
- add xstrfappend and xstrvfappend [Masatake YAMATO]
- adjust coding style [Masatake YAMATO]
- append SOCK.SHUTDOWN value to ENDPOINTS column of UNIX-STREAM sockets [Masatake YAMATO]
- assign a class to the file in new_file() [Masatake YAMATO]
- avoid passing NULL to qsort() [Thomas Weißschuh]
- avoid undefined behavior [Thomas Weißschuh]
- build lsfd even if kcmp.h is not available [Masatake YAMATO]
- cache the result of checking whether "XMODE" column is enabled or not [Masatake YAMATO]
- call xinfo backend method before calling socket generic method when filling columns [Masatake YAMATO]
- choose anon_ops declarative way [Masatake YAMATO]
- cleanup --list-columns [Karel Zak]
- collect the device number for mqueue fs in the initialization stage [Masatake YAMATO]
- delete redundant parentheses surrounding return value [Masatake YAMATO]
- don't capitalize the help strings for the columns [Masatake YAMATO]
- don't check the value returned from new_file() [Masatake YAMATO]
- don't list kernel threads unless --threads is given [Masatake YAMATO]
- fill ENDPOINTS column for eventfd [Masatake YAMATO]
- fill ENDPOINTS column for pty devices [Masatake YAMATO]
- fill ENDPOINTS column of POSIX Mqueue [Masatake YAMATO]
- fill ENDPOINTS column of unix socket using UNIX_DIAG_PEER information [Masatake YAMATO]
- fill NAME column of inotify files with the information about their monitoring targets [Masatake YAMATO]
- fix a misleading parameter name [Masatake YAMATO]
- fix a sentence in comment [Masatake YAMATO]
- fix memory leak in append_filter_expr() [Karel Zak]
- fix specifying wrong JSON typs when building the help message [Masatake YAMATO]
- fix wrong inconsistency in extracting cwd and root associations [Masatake YAMATO]
- include common headers in lsfd.h [Masatake YAMATO]
- include system header files first [Masatake YAMATO]
- initialize pagesize in an earlier stage [Masatake YAMATO]
- initialize the ipc table before loading lists of unix socket peers via netlink diag [Masatake YAMATO]
- introduce -H, --list-columns option for making help messages short [Masatake YAMATO]
- introduce XMODE column, extensible variant of MODE [Masatake YAMATO]
- keep filter-only columns hidden [Karel Zak]
- make the order of calling finalize_* and initialize_* consistent [Masatake YAMATO]
- make the sock_xinfo layer be able to prepare an ipc_class for a given socket [Masatake YAMATO]
- make the way to read /proc/$pid/mountinfo robust [Masatake YAMATO]
- mark XMODE.m on fds monitored by poll(2) and ppoll(2) [Masatake YAMATO]
- mark XMODE.m on fds monitored by select(2) and pselect6(2) [Masatake YAMATO]
- move a local variable to a narrower scope [Masatake YAMATO]
- print file descriptors targeted by eventpoll files [Masatake YAMATO]
- print the detail of the timer associated with a timerfd [Masatake YAMATO]
- print the masks specified in signalfds [Masatake YAMATO]
- re-fill unix socket paths with sockdiag netlink interface [Masatake YAMATO]
- read /proc/$pid/ns/mnt earlier [Masatake YAMATO]
- rearrange the aligment of the help messages [Masatake YAMATO]
- show default columns in the help message [Masatake YAMATO]
- switch to c99-conformant alignment specification [Thomas Weißschuh]
- update the help message for XMODE column [Masatake YAMATO]
- use ARRAY_STRING and ARRAY_NUMBER json types in some columns [Masatake YAMATO]
- use SCOLS_JSON_FLOAT [Karel Zak]
- use \n as the separator in EVENTPOLL.TFDS column [Masatake YAMATO]
- use \n as the separator in INOTIFY.INODES and INOTIFY.INODES.RAW columns [Masatake YAMATO]
- use filter and counters from libsmartcols [Karel Zak]
- use helper functions in column-list-table.h [Masatake YAMATO]
- use scols_table_get_column_by_name [Masatake YAMATO]
- use the specified output stream for printing help messages [Masatake YAMATO]
- use xstrdup instead of xasprintf(...\"%s\" [Masatake YAMATO]
- utilize /proc/tty/drivers for filling SOURCE column of tty devices [Masatake YAMATO]
- write more about nsfs in comment [Masatake YAMATO]
lsfd,test_mkfds:
- (cosmetic) remove whitespaces between functions and their arglists [Masatake YAMATO]
lsfd-filter:
- constify filter logic [Thomas Weißschuh]
lsfd.1.adoc:
- document BPF.NAME column [Masatake YAMATO]
- fix a typo [Masatake YAMATO]
- fix typos [Masatake YAMATO]
- revise type names for columns [Masatake YAMATO]
- update for signalfds [Masatake YAMATO]
- write about timerfd [Masatake YAMATO]
lsipc:
- fix semaphore USED counter [Karel Zak]
lslocks:
- (fix) set JSON type for COL_SIZE even when --bytes is specified [Masatake YAMATO]
- (man) add missing fields [Masatake YAMATO]
- (man) document LEASE type [Masatake YAMATO]
- (man) update the note about OFDLCK [Masatake YAMATO]
- (preparation) add a fd number to the lock struct when loading lock info from /proc/$pid/fdinfo/$fd [Masatake YAMATO]
- (refactor) add a helper function returning JSON type for a given column [Masatake YAMATO]
- (refactor) lift up the code destroying the lock list for future extension [Masatake YAMATO]
- (refactor) make the data structure for storing lock information replacable [Masatake YAMATO]
- (refactor) remove 'pid' global variable [Masatake YAMATO]
- (refactor) use a tree for storing lock information extracted from /proc/$pid/fdinfo/$fd [Masatake YAMATO]
- (test) add a case [Masatake YAMATO]
- (test) add a case for OFDLCK type locks [Masatake YAMATO]
- add -H option printing avaiable columns [Masatake YAMATO]
- add HOLDERS column [Masatake YAMATO]
- add a missing "break;" in a switch/case statement [Masatake YAMATO]
- cleanup --list-columns [Karel Zak]
- don't attempt to open /proc/-1/fd/ [Jakub Wilk]
- fix buffer overflow [Karel Zak]
- improve --list-columns [Karel Zak]
- refactor the code reading /proc/locks [Masatake YAMATO]
- rename functions for future extension [Masatake YAMATO]
- store list_add_tail when storing information extracted from /proc/$pid/fdinfo/$fd [Masatake YAMATO]
- use information extracted from "locks " column of /proc/$pid/fdinfo/* [Masatake YAMATO]
lslogins:
- (man) fix -y option formatting [Thomas Weißschuh]
- fix realloc() loop allocation size [Thomas Weißschuh]
m4:
- update pkg.m4 [Thomas Weißschuh]
man:
- Add enosys and lsclocks to po4a.cfg [Mario Blättermann]
meson:
- Only build blkzone and blkpr if the required linux header exists [Jordan Williams]
- Only build libmount python module if python was found [Fabian Vogt]
- add check for linux/mount.h [Thomas Weißschuh]
- add check for struct statx [Thomas Weißschuh]
- add conditionalization for test progs [Zbigniew Jędrzejewski-Szmek]
- add forgotten files to lists [Zbigniew Jędrzejewski-Szmek]
- add missing scols sample [Karel Zak]
- avoid future-deprecated feature [Karel Zak]
- avoid int operation with non-int [Thomas Weißschuh]
- build test_mount_optlist [Thomas Weißschuh]
- bump required version to 0.60.0 [Thomas Weißschuh]
- check for HAVE_STRUCT_STATX_STX_MNT_ID [Karel Zak]
- check for _NL_TIME_WEEK_1STDAY in langinfo.h [Christian Hesse]
- conditionalize waitpid [Zbigniew Jędrzejewski-Szmek]
- create dedicated config for pam_lastlog2 [Thomas Weißschuh]
- define _GNU_SOURCE when checking for SO_PASSCRED [Thomas Weißschuh]
- don't try to build test_ca without libcap-ng [Thomas Weißschuh]
- fix LIBBLKID_VERSION definition [Karel Zak]
- fix copy & past error [Karel Zak]
- fix disablement check [Zbigniew Jędrzejewski-Szmek]
- fix mismatch with handling of lib_dl dependency [Zbigniew Jędrzejewski-Szmek]
- implement HAVE_PTY [Zbigniew Jędrzejewski-Szmek]
- include bash-completion for newgrp [Christian Hesse]
- include bash-completion for write [Christian Hesse]
- install chfn setuid [Christian Hesse]
- install chsh setuid [Christian Hesse]
- install lastlog2.h library header file [Karel Zak]
- install mount setuid [Christian Hesse]
- install newgrp setuid [Christian Hesse]
- install su setuid [Christian Hesse]
- install symlink for vigr man page [Christian Hesse]
- install umount setuid [Christian Hesse]
- install wall executable with group 'tty' [Christian Hesse]
- install wall setgid [Christian Hesse]
- install write executable with group 'tty' [Christian Hesse]
- install write setgid [Christian Hesse]
- only build liblastlog when enabled [Thomas Weißschuh]
- properly handle gettext non-existence [Thomas Weißschuh]
- remove scols filter sample [Karel Zak]
- require 0.57 [Thomas Weißschuh]
- run compiler checks with -D_GNU_SOURCE when necessary [Thomas Weißschuh]
- run tests if with option program-tests [sewn]
- try to always use 64bit time support on glibc [Thomas Weißschuh]
- update for libsmartcols filter [Karel Zak]
- use a dependency object for liblastlog2 [Thomas Weißschuh]
- use a dependency object for sqlite3 [Thomas Weißschuh]
- use bison --defines=HEADER [Karel Zak]
- use meson features instead of bash [sewn]
misc:
- constify some fields [Thomas Weißschuh]
mkfs.minix:
- handle 64bit time on 32bit system [Thomas Weißschuh]
mkswap:
- (tests) don't overwrite logfiles [Thomas Weißschuh]
- (tests) validate existence of truncate command [Thomas Weißschuh]
- implement --file [Vicki Pfau]
- implement --offset [Thomas Weißschuh]
more:
- avoid out-of-bound access [Thomas Weißschuh]
- exit if POLLERR and POLLHUP on stdin is received [Goldwyn Rodrigues]
- exit if POLLHUP or POLLERR on stdin is received [Goldwyn Rodrigues]
- fix poll() use [Karel Zak]
- remove second check for EOF (#2714) [Thorsten Kukuk]
- remove usage of alloca() [Thomas Weißschuh]
mount:
- (tests) don't create /dev/nul [Thomas Weißschuh]
- (tests) explicitly use test fstab location [Thomas Weißschuh]
- (tests) reuse well-known per-test fstab location [Thomas Weißschuh]
- (tests) test mount helper with multiple filesystems [Thomas Weißschuh]
- Fix markup and typos in man page [Mario Blättermann]
- add --map-users and --map-groups convenience options [Chris Webb]
- improve code readability [Karel Zak]
nsenter:
- (man) add --keep-caps [Karel Zak]
- add missing free() [Karel Zak]
- add option `-c` to join the cgroup of target process [u2386]
- avoid NULL pointer dereference [coverity scan] [Karel Zak]
- fix possible NULL dereferece [coverity scan] [Karel Zak]
pg:
- use snprintf to build string [Thomas Weißschuh]
pipesz:
- avoid dead store [Thomas Weißschuh]
po:
- add ro.po (from translationproject.org) [Remus-Gabriel Chelu]
- merge changes [Karel Zak]
- update cs.po (from translationproject.org) [Petr Písař]
- update de.po (from translationproject.org) [Hermann Beckers]
- update de.po (from translationproject.org) [Mario Blättermann]
- update es.po (from translationproject.org) [Antonio Ceballos Roa]
- update fr.po (from translationproject.org) [Frédéric Marchal]
- update hr.po (from translationproject.org) [Božidar Putanec]
- update ja.po (from translationproject.org) [Takeshi Hamasaki]
- update ko.po (from translationproject.org) [Seong-ho Cho]
- update pl.po (from translationproject.org) [Jakub Bogusz]
- update ro.po (from translationproject.org) [Remus-Gabriel Chelu]
- update sr.po (from translationproject.org) [Мирослав Николић]
- update tr.po (from translationproject.org) [Emir SARI]
- update uk.po (from translationproject.org) [Yuri Chornoivan]
po-man:
- add ko.po (from translationproject.org) [Seong-ho Cho]
- add ro.po (from translationproject.org) [Remus-Gabriel Chelu]
- merge changes [Karel Zak]
- update de.po (from translationproject.org) [Mario Blättermann]
- update fr.po (from translationproject.org) [Frédéric Marchal]
- update ro.po (from translationproject.org) [Remus-Gabriel Chelu]
- update sr.po (from translationproject.org) [Мирослав Николић]
- update uk.po (from translationproject.org) [Yuri Chornoivan]
prlimit:
- (man) fix formatting [Jakub Wilk]
- reject trailing junk in limits without " " [Jakub Wilk]
procfs:
- add a helper function to access /proc/$pid/syscall [Masatake YAMATO]
readprofile:
- use xasprintf to build string [Thomas Weißschuh]
rename:
- properly handle directories with trailing slash [Thomas Weißschuh]
rev:
- Check for wchar conversion errors [Tim Hallmann]
runuser.1.adoc:
- Move -m|-p|--preserve-environment in order [Sebastian Pipping]
runuser|su:
- Start supporting option -T|--no-pty [Sebastian Pipping]
script-playutils:
- close filestream in case ignore_line() fails [Thomas Weißschuh]
scriptreplay:
- support ctrl+s and ctrl+g [Karel Zak]
setarch:
- add PER_LINUX_FDPIC fallback [Karel Zak]
- add riscv64/riscv32 support [Michal Biesek]
setpriv:
- add landlock support [Thomas Weißschuh]
- apply landlock without configuration [Thomas Weißschuh]
- fix group argument completion [Thomas Weißschuh]
setterm:
- Document behavior of redirection [Stanislav Brabec]
- avoid restoring flags from uninitialized memory [Chris Hofstaedtler]
sfdisk:
- Fix markup in man page [Mario Blättermann]
- add hint about duplicate UUIDs when use dump [Karel Zak]
sha1:
- properly wipe variables [Thomas Weißschuh]
strv:
- make strv_new_api static [Thomas Weißschuh]
su:
- (man) add hint about sessions [Karel Zak]
- (man) improve formatting [Karel Zak]
- fix use after free in run_shell [Tanish Yadav]
su, agetty:
- don't use program_invocation_short_name for openlog() [Karel Zak]
sulogin:
- relabel terminal according to SELinux policy [Christian Göttsche]
- use get_terminal_default_type() [Karel Zak]
swapon:
- (man) fix --priority description [Karel Zak]
- (tests) abort test on failing commands [Thomas Weißschuh]
sys-utils:
- cleanup license lines, add SPDX [Karel Zak]
- fix SELinux context example in mount.8 [Todd Zullinger]
- hwclock-rtc fix pointer usage [Karthikeyan Krishnasamy]
sys-utils/lscpu:
- Unblock SIGSEGV before vmware_bdoor [WanBingjiang]
- Use ul_path_scanf where possible [Tobias Stoeckmann]
term-utils:
- fix indentation [Karel Zak]
test:
- (lsfd column-xmode) add mising "wait" invocation [Masatake YAMATO]
- (lsfd) add a case for l and L flags in XMODE column [Masatake YAMATO]
- (lsfd) add a case for testing BPF-MAP.TYPE and BPF-MAP.TYPE.RAW columns [Masatake YAMATO]
- (lsfd) add a case for testing BPF-PROG.TYPE and BPF-PROG.TYPE.RAW columns [Masatake YAMATO]
- (lsfd) add a case for testing DELETED column [Masatake YAMATO]
- (lsfd) add a subcase for testing NAME column for a deleted file [Masatake YAMATO]
- (mkfds bpf-map) new factory [Masatake YAMATO]
- (mkfds bpf-prog) new factory [Masatake YAMATO]
- (mkfds make-regular-file) add a parameter for file locking [Masatake YAMATO]
- (mkfds make-regular-file) add a parameter for making the new file readable [Masatake YAMATO]
- (mkfds make-regular-file) add a parameter for writing some bytes [Masatake YAMATO]
- (mkfds make-regular-file) delete the created file when an error occurs [Masatake YAMATO]
- (mkfds make-regular-file) make 'fd' local variable reusable [Masatake YAMATO]
- (mkfds ro-regular-file) add a parameter for a read lease [Masatake YAMATO]
- (mkfds) add "make-regular-file" factory [Masatake YAMATO]
test_enosys:
- fix build on old kernels [Thomas Weißschuh]
test_mkfds:
- avoid "ignoring return value of ‘write’ declared with attribute ‘warn_unused_result’" [Masatake YAMATO]
test_uuidd:
- make pthread_t formatting more robust [Thomas Weißschuh]
tests:
- (cosmetic,lslocks) trim whitespaces at the end of line [Masatake YAMATO]
- (functions.sh) create variable for test fstab location [Thomas Weißschuh]
- (functions.sh) use per-test fstab file [Thomas Weißschuh]
- (lsfd column-xmode) do rm -f the file for testing before making it [Masatake YAMATO]
- (lsfd column-xmode) ignore "rwx" mappings [Masatake YAMATO]
- (lsfd column-xmode) skip some subtests if OFD locks are not available [Masatake YAMATO]
- (lsfd filter-floating-point-nums) use --raw output to make the case more robust [Masatake YAMATO]
- (lsfd mkfds-*) alter the L4 ports for avoiding the conflict with option-inet test case [Masatake YAMATO]
- (lsfd mkfds-bpf-map) chmod a+x [Masatake YAMATO]
- (lsfd mkfds-inotify) consider environments not having / as a mount point [Masatake YAMATO]
- (lsfd mkfds-inotify) use findmnt(1) instead of stat(1) to get bdev numbers [Masatake YAMATO]
- (lsfd mkfds-socketpair) make a case for testing DGRAM a subtest and add a subtest for STREAM [Masatake YAMATO]
- (lsfd mkfds-unix-dgram) don't depend on the number of whitespaces in the output [Masatake YAMATO]
- (lsfd option-inet) get child-processes' pids via fifo [Masatake YAMATO]
- (lsfd) add a case for testing ENDPOINTS column of UNIX-STREAM sockets [Masatake YAMATO]
- (lsfd) add a case for testing EVENTPOLL.TFDS column [Masatake YAMATO]
- (lsfd) add a case for testing INOTIFY.INODES.RAW column [Masatake YAMATO]
- (lsfd) add a case for testing SOCK.SHUTDOWN column [Masatake YAMATO]
- (lsfd) add a case for testing SOURCE column for SysV shmem mappings [Masatake YAMATO]
- (lsfd) add a case for testing signalfd related columns [Masatake YAMATO]
- (lsfd) add a case for testing timerfd related columns [Masatake YAMATO]
- (lsfd) add a case for verifying ENDPOINTS column output in JSON mode [Masatake YAMATO]
- (lsfd) add a case testing 'm' flag in XMODE column [Masatake YAMATO]
- (lsfd) add a case testing INOTIFY.INODES.RAW column on btrfs [Masatake YAMATO]
- (lsfd) add a case testing NAME, SOURCE, ENDPOINTS, and PTMX.TTY-INDEX columns of pts fds [Masatake YAMATO]
- (lsfd) add a case testing TUN.IFACE column [Masatake YAMATO]
- (lsfd) add a case testing XMODE.m for classical syscalls for multiplexing [Masatake YAMATO]
- (lsfd) add cases for POSIX Mqueue [Masatake YAMATO]
- (lsfd) add cases for eventfd [Masatake YAMATO]
- (lsfd) add lsfd_check_mkfds_factory as a help function [Masatake YAMATO]
- (lsfd) avoid race conditions (part 1) [Masatake YAMATO]
- (lsfd) don't run the unix-stream testcase including newlines in the path on qemu-user [Masatake YAMATO]
- (lsfd) extend the cases for testing BPF.NAME column [Masatake YAMATO]
- (lsfd) extend the mkfds-socketpair case to test ENDPOINTS with SOCK.SHUTDOWN info [Masatake YAMATO]
- (lsfd) fix typoes in an error name [Masatake YAMATO]
- (lsfd) show the entry for mqueue in /proc/self/mountinfo [Masatake YAMATO]
- (lsfd) skip mkfds-netns if SIOCGSKNS is not defined [Masatake YAMATO]
- (lsfd) skip some cases if NETLINK_SOCK_DIAG for AF_UNIX is not available [Masatake YAMATO]
- (lsfd-functions.bash,cosmetic) unify the style to define functions [Masatake YAMATO]
- (lsfd/filter) add a case for comparing floating point numbers [Masatake YAMATO]
- (lslcoks) insert a sleep between taking a lock and running lslocks [Masatake YAMATO]
- (lslocks) add cases testing HOLDERS column [Masatake YAMATO]
- (lslocks) add missing ts_finalize call [Masatake YAMATO]
- (mkfds) add / and /etc/fstab as the monitoring targets to inotify [Masatake YAMATO]
- (mkfds) add a factor for opening tun device [Masatake YAMATO]
- (mkfds) add a factory to make SysV shmem [Masatake YAMATO]
- (mkfds) add a factory to make a signalfd [Masatake YAMATO]
- (mkfds) add a factory to make a timerfd [Masatake YAMATO]
- (mkfds) add a factory to make an eventpoll fd [Masatake YAMATO]
- (mkfds) add eventfd factory [Masatake YAMATO]
- (mkfds) add mqueue factory [Masatake YAMATO]
- (mkfds) print a whitespace only when the running factory has "report" method [Masatake YAMATO]
- (mkfds) provide the way to declare the number of extra printing values [Masatake YAMATO]
- (refactor (test_mkfds, lsfd)) use TS_EXIT_NOTSUPP instead of EXIT_ENOSYS [Masatake YAMATO]
- (run.sh) detect builddir from working directory [Thomas Weißschuh]
- (test_mkfds inotify) add "dir" and "file" parameters [Masatake YAMATO]
- (test_mkfds make-regular-file) add a new parameter, "dupfd" [Masatake YAMATO]
- (test_mkfds mkfds-multiplexing) dump /proc/$pid/syscall for debugging [Masatake YAMATO]
- (test_mkfds mkfds-multiplexing) make the output of ts_skip_subtest visible [Masatake YAMATO]
- (test_mkfds pty) add a new factory [Masatake YAMATO]
- (test_mkfds sockdiag) new factory [Masatake YAMATO]
- (test_mkfds socketpair) add "halfclose" parameter [Masatake YAMATO]
- (test_mkfds {bpf-prog,bpf-map}) fix memory leaks [Masatake YAMATO]
- (test_mkfds) add --is-available option [Masatake YAMATO]
- (test_mkfds) add a new factory "multiplexing" [Masatake YAMATO]
- (test_mkfds) add missing PARAM_END marker [Masatake YAMATO]
- (test_mkfds) add poll multiplexer [Masatake YAMATO]
- (test_mkfds) add ppoll multiplexer [Masatake YAMATO]
- (test_mkfds) add pselect6 and select multiplexers [Masatake YAMATO]
- (test_mkfds) allow to add factory-made fds to the multiplexer as event source [Masatake YAMATO]
- (test_mkfds) include locale headers first to define _GNU_SOURCE [Masatake YAMATO]
- (test_mkfds) initialize a proper union member [Masatake YAMATO]
- (test_mkfds) monitor stdin by default [Masatake YAMATO]
- (test_mkfds) revise the usage of " __attribute__((__unused__))" [Masatake YAMATO]
- (test_mkfds) use SYS_bpf instead of __NR_bpf [Masatake YAMATO]
- (test_mkfds) use err() when a system call fails [Masatake YAMATO]
- (test_mkfds, refactor) make the function for waiting events plugable [Masatake YAMATO]
- add libsmartcols filter tests [Karel Zak]
- add missing file and improve options-missing test [Karel Zak]
- add omitted files [Karel Zak]
- add optlist tests [Karel Zak]
- add sysinfo to show sizeof(time_t) [Thomas Weißschuh]
- add ts_skip_capability [Masatake YAMATO]
- add ts_skip_docker [Thomas Weißschuh]
- add user and user=name mount test [Karel Zak]
- constify a sysinfo helpers struct [Thomas Weißschuh]
- don't keep bison messages in tests [Karel Zak]
- fix capability testing [Thomas Weißschuh]
- fix memory leak in scols fromfile [Karel Zak]
- fix subtests containing spaces in their name [Thomas Weißschuh]
- increase delay for waitpid test [Goldwyn Rodrigues]
- make mount/special more robust [Karel Zak]
- make ts_skip_capability accepts the output of older version of getpcaps [Masatake YAMATO]
- skip broken tests on docker [Thomas Weißschuh]
- update build tests [Karel Zak]
- update dmesg deltas [Karel Zak]
- update lsfd broken filter test [Karel Zak]
- use array keys in more robust way [Karel Zak]
- use scols_column_set_properties() in 'fromfile' sample [Karel Zak]
tests,autotools:
- add TESTS_COMPONENTS macro for specfying test components from make cmdline [Masatake YAMATO]
timeutils:
- add an inline funciton, is_timespecset() [Masatake YAMATO]
- add strtimespec_relative [Thomas Weißschuh]
tmpfiles:
- add and install for uuidd, generate /run/uuidd & /var/lib/libuuid [Christian Hesse]
- depend on systemd... [Christian Hesse]
tools:
- (asciidoctor) explicitly require extensions module [Thomas Weißschuh]
tools/all_syscalls:
- use pipefail [sewn]
- use sh and replace awk with grep & sed [sewn]
treewide:
- explicitly mark unused arguments [Thomas Weißschuh]
- fix newlines when using fputs [Thomas Weißschuh]
- use (x)reallocarray() when applicable [Thomas Weißschuh]
- use reallocarray to allocated memory that will be reallocated [Thomas Weißschuh]
ttyutils:
- improve get_terminal_default_type() code [Karel Zak]
uclampset:
- Remove validation logic [Qais Yousef]
- doc Add a reference to latest kernel documentation [Qais Yousef]
umount:
- handle bindmounts during --recursive [Thomas Weißschuh]
unshare:
- Add --map-users=all and --map-groups=all [Chris Webb]
- Move implementation of --keep-caps option to library function [David Gibson]
- Set uid and gid maps directly when run as root [Chris Webb]
- Support multiple ID ranges for user and group maps [Chris Webb]
- allow negative time offsets [Thomas Weißschuh]
- don't try to reset the disposition of SIGKILL [Chris Webb]
- fix error message for unexpected time offsets [Thomas Weißschuh]
- make sure map_range.next is initialized [coverity scan] [Karel Zak]
utmpdump:
- validate subsecond granularity [Thomas Weißschuh]
uuidd:
- Fix markup in man page (uuidd.8) [Mario Blättermann]
- add cont_clock persistence [Michael Trapp]
- enable cont-clock in service file [Karel Zak]
- improve man page for -cont-clock [Karel Zak]
uuidd.rc:
- create localstatedir in init script [Christian Hesse]
uuidgen:
- add option --count [Karel Zak]
- mark some options mutually exclusive [Karel Zak]
- use xmalloc instead of malloc (#2714) [Thorsten Kukuk]
verity:
- modernize example in manpage [Luca Boccassi]
- use <roothash>-verity as the device mapper name instead of libmnt_<image> [Luca Boccassi]
waitpid:
- only build when pidfd_open is available [Thomas Weißschuh]
- warn of "exited" only when --verbose is given [Masatake YAMATO]
wall:
- do not error for ttys that do not exist [Mike Gilbert]
- fix calloc cal [-Werror=calloc-transposed-args] [Karel Zak]
- fix escape sequence Injection [CVE-2024-28085] [Karel Zak]
- query logind for list of users with tty (#2088) [Thorsten Kukuk]
wdctl:
- properyl test timeout conditions [Thomas Weißschuh]
- use only sysfs if sufficient [Thomas Weißschuh]
wipefs:
- (man) fix typos [codefiles]
- (tests) add test for all detected signatures [Thomas Weißschuh]
- (tests) remove necessity of root permissions [Thomas Weißschuh]
- allow storage of backups in specific location [Thomas Weißschuh]
write:
- Add missing section header in man page [Mario Blättermann]
- query logind for list of users with tty (#2088) [Thorsten Kukuk]
xalloc.h:
- add new functions xstrappend, xstrputc, xstrvfappend, and xstrfappend [Masatake YAMATO]
zramctl:
- add hint about supported algorithms [Karel Zak]
--
Karel Zak <kzak@redhat.com>
http://karelzak.blogspot.com
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox