* [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
* Re: [PATCH v2 0/1] coresched: Manage core scheduling cookies for tasks
From: Phil Auld @ 2024-03-27 14:18 UTC (permalink / raw)
To: Thijs Raymakers; +Cc: kzak, util-linux
In-Reply-To: <20240327132137.GA361020@lorien.usersys.redhat.com>
On Wed, Mar 27, 2024 at 09:21:46AM -0400 Phil Auld wrote:
>
> Hi Thijs,
>
> On Wed, Mar 27, 2024 at 01:43:20PM +0100 Thijs Raymakers wrote:
> > Hi Phil,
> >
> > >> In general, `prctl` does indeed return EINVAL if the operation is not
> > >> recognized, or not supported on the system. The `PR_SCHED_CORE`
> > >> operation itself only returns EINVAL if it is called with
> > >> - an invalid operation
> > >> - an invalid type
> > >> - a negative PID
> > >> - an invalid cookie store address (for PR_SCHED_CORE_GET)
> > >> Assuming that all these cases are prevented by the util, we could
> > >> interpret a EINVAL as a sign that PR_SCHED_CORE is not supported on
> > >> the system.
> > > Fair enough. Could say something like "got EINVAL. Does your kernel
> > > support CONFIG_SCHED_CORE?".
> >
> > I've added this as a warning in case a prctl call returns EINVAL. I
> > haven't tested it on a kernel with CONFIG_SCHED_CORE=n yet but I will do
> > that later.
>
> Thanks. I'll refresh and try it again. My build machine does not have
> CONFIG_CORE_SCHED so that's easy for me to test :)
>
After fixing the missing #defines I tested this. I think this looks
good but I'd do the error line first then the CONFIG_SCHED_CORE message.
i.e:
$ ./coresched -n ls
coresched: Does your kernel support CONFIG_SCHED_CORE?
coresched: Failed to create cookie for PID 3747542: Invalid argument
should be
$ ./coresched -n ls
coresched: Failed to create cookie for PID 3747542: Invalid argument
coresched: Does your kernel support CONFIG_SCHED_CORE?
It's a little harder to clear the cookie with coresched that coreset
but otherwise I think this version is really good. It's covering all
my use cases and seems to fit my expectations pretty well.
Thanks for listening to my suggestions.
Cheers,
Phil
> >
> > > There is also
> > > err_exclusive_options(c, longopts, excl, excl_st);
> > >
> > > in the optuils.h code which can handle the tests for mutually exclusive
> > > arguments.
> > >
> > > Thomas pointed me to that. It works nicely and can remove some of the extra
> > > checks (once you get it setup). And the error then looks the same as other
> > > util-linux progs.
> >
> > Good suggestion, I replaced some of the earlier mutually exclusive
> > checks with a call to err_exclusive_options.
> >
> > > You might consider "errtryhelp(EXIT_FAILURE);" in usage failures. A lot of
> > > the progs (incl taskset which is my model for this) do that. Then you only
> > > do the full usage when given -h/--help.
> >
> > This does indeed feel a slightly nicer to use. I've added this in case a
> > of a usage failure. In case no options are given, it defaults to
> > printing the --help page. This matches the behavior of some other utils like
> > uclampset, and saves some time.
> >
> >
> > Op 26-03-2024 om 21:16 schreef Phil Auld:
> > > So "-n/--new" creates a new cookie and so does "". Just one on an existing task
> > > and one on the exec'd task. Seems inconsistent, no?
> >
> > Hmmm, I didn't intially see this as inconsistent behavior but I get why
> > you say this. I've modified this now to have -n/--new deal both with
> > assigning cookies to existing processes and to spawn new processes.
> >
> >
> > Op 26-03-2024 om 21:17 schreef Phil Auld:
> > > Btw, did you try coreset to see if it does what you need?
> >
> > I did take a look at it. Thanks for adding the functionality of copying
> > a cookie from/to an existing PID. The commands take a bit of getting
> > used to for me, so I guess it is just a matter of preference of what feels
> > natural to use.
>
> Yeah, fair enough. I wrote it so it makes sense to me ;)
>
> > Some things that weren't directly clear to me:
> > - Both `coreset ls` and `coreset -n ls` spawn a new program. If I
> > understood it correctly, the former spawns the program without
> > a cookie, and the latter spawns it with a cookie. I'm not entirely
> > sure what the point of the former would be, as the cookie would either
> > be 0 or equal to the cookie of the `coreset` process itself since the
> > cookie in inherited across execs. It is not necessary to run the
> > provided program in that case.
>
> It's more informational. Since "get" is essentially the default, coreset ls
> would run ls and report its cookie. Not totally useful but more for
> consistency and it tells you that your shell does or does not have a
> cookie set.
>
> > - Not entirely sure what the difference between --dest and --to is.
> > The names of these options are similar in meaning and do almost the
> > same thing.
>
> That's the one I added to cover your use case. Dest is specifically only
> used with --copy. It makes copy into a pull_then_push rather than a
> pull_then_exec.
>
> --to is just a push. It will push the current task's cookie to
> <pid>. This is also useful to clear a tasks cookie if your current shell does
> not have a cookie set.
>
> With your coresched tool I think you have to pick a random pid that you
> know does not have a cookie and use that to clear a different pid's cookie.
>
> > - It is not clear in what cases --scope is ignored and in which
> > cases it is necessary.
>
> Fair enough. Also I have not yet cribbed your string version of scope.
>
> > - What does `coreset -c -s 0 -d 2 -p 1 ls` do? Does it first copy the
> > cookie from PID 1 to PID 2, and then run `ls` with the same or
> > different cookie? Or the other way around?
> >
>
> It should copy pid 1's cookie to pid 2 and report that it's ignoring the
> extra input. It would not run ls in this case.
>
> -d is the destination pid in the case of copy. So --pid is copy from (in
> both exec and --dest cases).
>
> -s 0 is redundant since that's the default in coreset.
>
> > Now I do see what you meant earlier with imitating the behavior of the
> > `taskset` command. I don't think this is strictly necessary since it is
> > a different utility program, but I can understand that it can be useful
> > for people if the command behaves similar to a program that they already
> > know and have certain expectations of.
> >
> > I've modified the options of coresched to be more in line with these
> > expectations, while also addressing the things I mentioned above.
> >
> > Usage:
> > coresched [-p PID]
> > coresched --new [-t <TYPE>] -p <PID>
> > coresched --new [-t <TYPE>] -- PROGRAM [ARGS...]
> > coresched --copy -p <PID> [-t <TYPE>] -d <PID>
> > coresched --copy -p <PID> [-t <TYPE>] -- PROGRAM [ARGS...]
> >
> > This follows the taskset's behavior (i.e. perform the same operation
> > on either an existing PID or on a new program) and taskset's arguments
> > (-p for PID instead of the -s I had earlier) more closely than before.
> >
>
> I think I like the above, nice.
>
> > I've attached an interdiff of the util compared to the previous version
> > that I sent yesterday. I think that this version encompases both our use
> > cases quite nicely.
>
> Let me give it a try. I may or may not have time to get to it today.
> Other priorities arise. We'll see.
>
> I do still prefer coreset as a name but I won't die on that hill :)
>
>
> Cheers,
> Phil
>
> >
> > Thijs
> >
> > Thijs Raymakers (1):
> > coresched: Manage core scheduling cookies for tasks
> >
> > .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 | 363 ++++++++++++++++++++++++++++++++++++
> > 8 files changed, 412 insertions(+), 6 deletions(-)
> > create mode 100644 bash-completion/coresched
> > create mode 100644 schedutils/coresched.1.adoc
> > create mode 100644 schedutils/coresched.c
> >
> > Interdiff against v1:
> > diff --git a/schedutils/coresched.c b/schedutils/coresched.c
> > index 17d775f2d..537281fdb 100644
> > --- a/schedutils/coresched.c
> > +++ b/schedutils/coresched.c
> > @@ -12,10 +12,12 @@
> > #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, even if the
> > @@ -26,32 +28,34 @@
> > #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_EXEC = 0,
> > - SCHED_CORE_CMD_GET = 1,
> > - SCHED_CORE_CMD_CREATE = 2,
> > - SCHED_CORE_CMD_COPY = 4,
> > + SCHED_CORE_CMD_GET,
> > + SCHED_CORE_CMD_NEW,
> > + SCHED_CORE_CMD_COPY,
> > } core_sched_cmd_t;
> >
> > struct args {
> > - pid_t from_pid;
> > - pid_t to_pid;
> > + pid_t pid;
> > + pid_t dest;
> > core_sched_type_t type;
> > core_sched_cmd_t cmd;
> > int exec_argv_offset;
> > };
> >
> > -unsigned long core_sched_get_cookie(struct args *args);
> > -void core_sched_create_cookie(struct args *args);
> > +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(struct args *args);
> > +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);
> > @@ -59,22 +63,31 @@ void parse_arguments(int argc, char **argv, struct args *args);
> > void set_pid_or_err(pid_t *dest, pid_t src, const char *err_msg);
> > static void __attribute__((__noreturn__)) usage(void);
> >
> > -unsigned long core_sched_get_cookie(struct args *args)
> > +#define bad_usage(FMT...) \
> > + warnx(FMT); \
> > + errtryhelp(EINVAL);
> > +
> > +#define check_coresched_in_kernel(errno) \
> > + if (errno == EINVAL) { \
> > + warnx("Does your kernel support CONFIG_SCHED_CORE?"); \
> > + }
> > +
> > +cookie_t core_sched_get_cookie(pid_t pid)
> > {
> > - unsigned long cookie = 0;
> > - if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_GET, args->from_pid,
> > + cookie_t cookie = 0;
> > + if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_GET, pid,
> > PR_SCHED_CORE_SCOPE_THREAD, &cookie)) {
> > - err(errno, "Failed to get cookie from PID %d", args->from_pid);
> > + check_coresched_in_kernel(errno);
> > + err(errno, "Failed to get cookie from PID %d", pid);
> > }
> > return cookie;
> > }
> >
> > -void core_sched_create_cookie(struct args *args)
> > +void core_sched_create_cookie(pid_t pid, core_sched_type_t type)
> > {
> > - if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_CREATE, args->to_pid, args->type,
> > - 0)) {
> > - err(errno, "Failed to create cookie for PID %d",
> > - args->from_pid);
> > + 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);
> > }
> > }
> >
> > @@ -82,6 +95,7 @@ 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);
> > }
> > }
> > @@ -89,14 +103,18 @@ void core_sched_pull_cookie(pid_t 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);
> > }
> > }
> >
> > -void core_sched_copy_cookie(struct args *args)
> > +void core_sched_copy_cookie(pid_t from, pid_t to, core_sched_type_t to_type)
> > {
> > - core_sched_pull_cookie(args->from_pid);
> > - core_sched_push_cookie(args->to_pid, args->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)
> > @@ -111,11 +129,15 @@ void core_sched_exec_with_cookie(struct args *args, char **argv)
> > // If a source PID is provided, try to copy the cookie from
> > // that PID. Otherwise, create a brand new cookie with the
> > // provided type.
> > - if (args->from_pid) {
> > - core_sched_pull_cookie(args->from_pid);
> > + if (args->pid) {
> > + core_sched_pull_cookie(args->pid);
> > + core_sched_get_and_print_cookie(args->pid);
> > } else {
> > - args->to_pid = getpid();
> > - core_sched_create_cookie(args);
> > + 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)) {
> > @@ -123,6 +145,13 @@ void core_sched_exec_with_cookie(struct args *args, char **argv)
> > }
> > }
> >
> > +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)) {
> > @@ -133,156 +162,96 @@ core_sched_type_t parse_core_sched_type(char *str)
> > return PR_SCHED_CORE_SCOPE_PROCESS_GROUP;
> > }
> >
> > - errx(EINVAL, "'%s' is an invalid option. Must be one of pid/tgid/pgid",
> > - str);
> > + 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 --get <PID>\n"), program_invocation_short_name);
> > - fprintf(stdout, _(" %s --new <PID> [-t <TYPE>]\n"),
> > + 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 --copy -s <PID> -d <PID> [-t <TYPE>]\n"),
> > + fprintf(stdout, _(" %s --new [-t <TYPE>] -- PROGRAM [ARGS...]\n"),
> > program_invocation_short_name);
> > - fprintf(stdout, _(" %s [-s <PID>] -- PROGRAM ARGS... \n"),
> > + 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(_(" -g, --get <PID> get the core scheduling cookie of a PID"),
> > + fputsln(_(" -n, --new assign a new core scheduling cookie to an existing PID or\n"
> > + " execute a program with a new cookie."),
> > stdout);
> > - fputsln(_(" -n, --new <PID> assign a new core scheduling cookie to PID"),
> > + 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(_(" -c, --copy copy the core scheduling cookie from PID to\n"
> > - " another PID, requires the --source and --dest option"),
> > + 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(_(" -s, --source <PID> where to copy the core scheduling cookie from."),
> > - stdout);
> > - fputsln(_(" -d, --dest <PID> where to copy the core scheduling cookie to."),
> > + 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 of the destination PID, or the type of\n"
> > - " the PID when a new core scheduling cookie\n"
> > - " is created. Can be one of the following:\n"
> > - " pid, tgid or pgid. Defaults to tgid."),
> > + 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(
> > - 25)); /* char offset to align option descriptions */
> > + 20)); /* char offset to align option descriptions */
> > fprintf(stdout, USAGE_MAN_TAIL("coresched(1)"));
> > exit(EXIT_SUCCESS);
> > }
> >
> > -bool verify_arguments(struct args *args)
> > -{
> > - // Check if the value of args->cmd is a power of 2
> > - // In that case, only a single function option was set.
> > - if (args->cmd & (args->cmd - 1)) {
> > - errx(EINVAL,
> > - "Cannot do more than one function at a time. See %s --help",
> > - program_invocation_short_name);
> > - }
> > -
> > - switch (args->cmd) {
> > - case SCHED_CORE_CMD_GET:
> > - if (args->to_pid) {
> > - errx(EINVAL,
> > - "Cannot use -d/--dest with this -g/--get. See %s --help",
> > - program_invocation_short_name);
> > - }
> > - break;
> > - case SCHED_CORE_CMD_CREATE:
> > - if (args->from_pid) {
> > - errx(EINVAL,
> > - "Cannot use -s/--source with this -n/--new. See %s --help",
> > - program_invocation_short_name);
> > - }
> > - break;
> > - case SCHED_CORE_CMD_COPY:
> > - if (!args->from_pid) {
> > - errx(EINVAL,
> > - "-s/--source PID is required when copying");
> > - }
> > - if (!args->to_pid) {
> > - errx(EINVAL, "-d/--dest PID is required when copying");
> > - }
> > - break;
> > - case SCHED_CORE_CMD_EXEC:
> > - if (args->to_pid) {
> > - errx(EINVAL,
> > - "Cannot use -d/--dest when spawning a program. See %s --help",
> > - program_invocation_short_name);
> > - }
> > - break;
> > - }
> > - return true;
> > -}
> > -
> > -void set_pid_or_err(pid_t *dest, pid_t src, const char *err_msg)
> > -{
> > - if (*dest) {
> > - errx(EINVAL, "Ambigious usage: %s", err_msg);
> > - } else {
> > - *dest = src;
> > - }
> > -}
> > -
> > -static const char *ERR_MSG_MULTIPLE_SOURCE_PIDS =
> > - "Multiple source PIDs defined";
> > void parse_arguments(int argc, char **argv, struct args *args)
> > {
> > int c;
> > - pid_t tmp;
> >
> > static const struct option longopts[] = {
> > - { "get", required_argument, NULL, 'g' },
> > - { "new", required_argument, NULL, 'n' },
> > + { "new", no_argument, NULL, 'n' },
> > { "copy", no_argument, NULL, 'c' },
> > - { "source", required_argument, NULL, 's' },
> > - { "destination", required_argument, NULL, 'd' },
> > + { "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 }
> > + };
> >
> > - while ((c = getopt_long(argc, argv, "g:n:cs:d:t:Vh", longopts, NULL)) !=
> > - -1)
> > + 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 'g':
> > - args->cmd |= SCHED_CORE_CMD_GET;
> > - tmp = strtopid_or_err(
> > - optarg, "Failed to parse PID for -g/--get");
> > - set_pid_or_err(&args->from_pid, tmp,
> > - ERR_MSG_MULTIPLE_SOURCE_PIDS);
> > - break;
> > case 'n':
> > - args->cmd |= SCHED_CORE_CMD_CREATE;
> > - tmp = strtopid_or_err(
> > - optarg, "Failed to parse PID for -n/--new");
> > - set_pid_or_err(&args->to_pid, tmp,
> > - ERR_MSG_MULTIPLE_SOURCE_PIDS);
> > + args->cmd = SCHED_CORE_CMD_NEW;
> > break;
> > case 'c':
> > - args->cmd |= SCHED_CORE_CMD_COPY;
> > + args->cmd = SCHED_CORE_CMD_COPY;
> > break;
> > - case 's':
> > - tmp = strtopid_or_err(
> > - optarg, "Failed to parse PID for -s/--source");
> > - set_pid_or_err(&args->from_pid, tmp,
> > - ERR_MSG_MULTIPLE_SOURCE_PIDS);
> > + case 'p':
> > + args->pid = strtopid_or_err(
> > + optarg, "Failed to parse PID for -p/--pid");
> > break;
> > case 'd':
> > - tmp = strtopid_or_err(
> > + args->dest = strtopid_or_err(
> > optarg, "Failed to parse PID for -d/--dest");
> > - set_pid_or_err(&args->to_pid, tmp,
> > - "Multiple destination PIDs defined");
> > break;
> > case 't':
> > args->type = parse_core_sched_type(optarg);
> > @@ -294,57 +263,98 @@ void parse_arguments(int argc, char **argv, struct args *args)
> > 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) {
> > - if (args->cmd == SCHED_CORE_CMD_EXEC) {
> > - args->exec_argv_offset = optind;
> > - } else {
> > - // -g, -n or -c AND a program to run is provided
> > - errx(EINVAL, "bad usage, see %s --help",
> > - program_invocation_short_name);
> > + 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;
> > }
> > - } else if (argc == optind && args->from_pid) {
> > - // Neither a function (-g, -n, or -c), nor a program to
> > - // run is given
> > - args->cmd = SCHED_CORE_CMD_GET;
> > }
> >
> > - verify_arguments(args);
> > + 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 arguments = { 0 };
> > - arguments.type = PR_SCHED_CORE_SCOPE_THREAD_GROUP;
> > + 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, &arguments);
> > + parse_arguments(argc, argv, &args);
> > +
> > + cookie_t cookie = 0;
> >
> > - unsigned long cookie = 0;
> > - switch (arguments.cmd) {
> > + switch (args.cmd) {
> > case SCHED_CORE_CMD_GET:
> > - cookie = core_sched_get_cookie(&arguments);
> > - if (cookie) {
> > - printf("core scheduling cookie of pid %d is 0x%lx\n",
> > - arguments.from_pid, cookie);
> > + 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 {
> > - printf("pid %d doesn't have a core scheduling cookie\n",
> > - arguments.from_pid);
> > - exit(1);
> > + usage();
> > + exit(0);
> > }
> > break;
> > - case SCHED_CORE_CMD_CREATE:
> > - core_sched_create_cookie(&arguments);
> > + 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:
> > - core_sched_copy_cookie(&arguments);
> > - break;
> > - case SCHED_CORE_CMD_EXEC:
> > - core_sched_exec_with_cookie(&arguments, argv);
> > + if (args.dest) {
> > + core_sched_copy_cookie(args.pid, args.dest, args.type);
> > + } else {
> > + core_sched_exec_with_cookie(&args, argv);
> > + }
> > break;
> > default:
> > usage();
> > --
> > 2.44.0
> >
>
> --
--
^ permalink raw reply
* Re: [PATCH v2 1/1] coresched: Manage core scheduling cookies for tasks
From: Phil Auld @ 2024-03-27 14:09 UTC (permalink / raw)
To: Thijs Raymakers; +Cc: kzak, util-linux
In-Reply-To: <20240327124323.27029-2-thijs@raymakers.nl>
Hi Thijs,
On Wed, Mar 27, 2024 at 01:43:21PM +0100 Thijs Raymakers wrote:
> Signed-off-by: Thijs Raymakers <thijs@raymakers.nl>
> Thanks: Phil Auld for the valuable feedback
> ---
> .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 | 363 ++++++++++++++++++++++++++++++++++++
> 8 files changed, 412 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..537281fdb
> --- /dev/null
> +++ b/schedutils/coresched.c
> @@ -0,0 +1,363 @@
> +/**
> + * 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"
> +
I think you need to do a similar #ifdef game
as below, with all the PR_SCHED_CORE* macros as well.
On a system with an older prctl.h this just fails to build.
There is no reason it needs to though since these
are just numbers.
Alternatively the configure system needs to check and disable
the program but that does not seem necessary to me.
Cheers,
Phil
> +// These definitions might not be defined, even if the
> +// prctl interface accepts them.
> +#ifndef PR_SCHED_CORE_SCOPE_THREAD
> +#define PR_SCHED_CORE_SCOPE_THREAD 0
> +#endif
> +#ifndef PR_SCHED_CORE_SCOPE_THREAD_GROUP
> +#define PR_SCHED_CORE_SCOPE_THREAD_GROUP 1
> +#endif
> +
> +#ifndef PR_SCHED_CORE_SCOPE_PROCESS_GROUP
> +#define PR_SCHED_CORE_SCOPE_PROCESS_GROUP 2
> +#endif
> +
> +typedef int core_sched_type_t;
> +typedef 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_coresched_in_kernel(errno) \
> + if (errno == EINVAL) { \
> + warnx("Does your kernel support CONFIG_SCHED_CORE?"); \
> + }
> +
> +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);
> + }
> + 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_coresched_in_kernel(errno);
> + err(errno, "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_coresched_in_kernel(errno);
> + err(errno, "Failed to pull cookie from PID %d", from);
> + }
> +}
> +
> +void core_sched_push_cookie(pid_t to, core_sched_type_t type)
> +{
> + if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_SHARE_TO, to, type, 0)) {
> + check_coresched_in_kernel(errno);
> + err(errno, "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
* Re: [PATCH v2 0/1] coresched: Manage core scheduling cookies for tasks
From: Phil Auld @ 2024-03-27 13:21 UTC (permalink / raw)
To: Thijs Raymakers; +Cc: kzak, util-linux
In-Reply-To: <20240327124323.27029-1-thijs@raymakers.nl>
Hi Thijs,
On Wed, Mar 27, 2024 at 01:43:20PM +0100 Thijs Raymakers wrote:
> Hi Phil,
>
> Op 26-03-2024 om 19:13 schreef Phil Auld:
> > On Tue, Mar 26, 2024 at 06:49:07PM +0100 Thijs Raymakers wrote:
> >> Op 26-03-2024 om 15:41 schreef Phil Auld:
> >>> On Sat, Mar 16, 2024 at 06:10:03PM +0100, Thijs Raymakers wrote:
> >>>> - How should the program behave if the prctl core scheduling API is not
> >>>> available? It has been in Linus' tree since november 2021
> >>>> (commit a41b74451b35f7a6529689760eb8c05241feecbc) but it can be
> >>>> disabled with CONFIG_SCHED_CORE=n
> >>> This one is tricky. If I recall the error you get is ambiguous in this case
> >>> (EINVAL, which can be returned for other reasons). It would be nice to be
> >>> able to report that specifically.
> >> In general, `prctl` does indeed return EINVAL if the operation is not
> >> recognized, or not supported on the system. The `PR_SCHED_CORE`
> >> operation itself only returns EINVAL if it is called with
> >> - an invalid operation
> >> - an invalid type
> >> - a negative PID
> >> - an invalid cookie store address (for PR_SCHED_CORE_GET)
> >> Assuming that all these cases are prevented by the util, we could
> >> interpret a EINVAL as a sign that PR_SCHED_CORE is not supported on
> >> the system.
> > Fair enough. Could say something like "got EINVAL. Does your kernel
> > support CONFIG_SCHED_CORE?".
>
> I've added this as a warning in case a prctl call returns EINVAL. I
> haven't tested it on a kernel with CONFIG_SCHED_CORE=n yet but I will do
> that later.
Thanks. I'll refresh and try it again. My build machine does not have
CONFIG_CORE_SCHED so that's easy for me to test :)
>
> > There is also
> > err_exclusive_options(c, longopts, excl, excl_st);
> >
> > in the optuils.h code which can handle the tests for mutually exclusive
> > arguments.
> >
> > Thomas pointed me to that. It works nicely and can remove some of the extra
> > checks (once you get it setup). And the error then looks the same as other
> > util-linux progs.
>
> Good suggestion, I replaced some of the earlier mutually exclusive
> checks with a call to err_exclusive_options.
>
> > You might consider "errtryhelp(EXIT_FAILURE);" in usage failures. A lot of
> > the progs (incl taskset which is my model for this) do that. Then you only
> > do the full usage when given -h/--help.
>
> This does indeed feel a slightly nicer to use. I've added this in case a
> of a usage failure. In case no options are given, it defaults to
> printing the --help page. This matches the behavior of some other utils like
> uclampset, and saves some time.
>
>
> Op 26-03-2024 om 21:16 schreef Phil Auld:
> > So "-n/--new" creates a new cookie and so does "". Just one on an existing task
> > and one on the exec'd task. Seems inconsistent, no?
>
> Hmmm, I didn't intially see this as inconsistent behavior but I get why
> you say this. I've modified this now to have -n/--new deal both with
> assigning cookies to existing processes and to spawn new processes.
>
>
> Op 26-03-2024 om 21:17 schreef Phil Auld:
> > Btw, did you try coreset to see if it does what you need?
>
> I did take a look at it. Thanks for adding the functionality of copying
> a cookie from/to an existing PID. The commands take a bit of getting
> used to for me, so I guess it is just a matter of preference of what feels
> natural to use.
Yeah, fair enough. I wrote it so it makes sense to me ;)
> Some things that weren't directly clear to me:
> - Both `coreset ls` and `coreset -n ls` spawn a new program. If I
> understood it correctly, the former spawns the program without
> a cookie, and the latter spawns it with a cookie. I'm not entirely
> sure what the point of the former would be, as the cookie would either
> be 0 or equal to the cookie of the `coreset` process itself since the
> cookie in inherited across execs. It is not necessary to run the
> provided program in that case.
It's more informational. Since "get" is essentially the default, coreset ls
would run ls and report its cookie. Not totally useful but more for
consistency and it tells you that your shell does or does not have a
cookie set.
> - Not entirely sure what the difference between --dest and --to is.
> The names of these options are similar in meaning and do almost the
> same thing.
That's the one I added to cover your use case. Dest is specifically only
used with --copy. It makes copy into a pull_then_push rather than a
pull_then_exec.
--to is just a push. It will push the current task's cookie to
<pid>. This is also useful to clear a tasks cookie if your current shell does
not have a cookie set.
With your coresched tool I think you have to pick a random pid that you
know does not have a cookie and use that to clear a different pid's cookie.
> - It is not clear in what cases --scope is ignored and in which
> cases it is necessary.
Fair enough. Also I have not yet cribbed your string version of scope.
> - What does `coreset -c -s 0 -d 2 -p 1 ls` do? Does it first copy the
> cookie from PID 1 to PID 2, and then run `ls` with the same or
> different cookie? Or the other way around?
>
It should copy pid 1's cookie to pid 2 and report that it's ignoring the
extra input. It would not run ls in this case.
-d is the destination pid in the case of copy. So --pid is copy from (in
both exec and --dest cases).
-s 0 is redundant since that's the default in coreset.
> Now I do see what you meant earlier with imitating the behavior of the
> `taskset` command. I don't think this is strictly necessary since it is
> a different utility program, but I can understand that it can be useful
> for people if the command behaves similar to a program that they already
> know and have certain expectations of.
>
> I've modified the options of coresched to be more in line with these
> expectations, while also addressing the things I mentioned above.
>
> Usage:
> coresched [-p PID]
> coresched --new [-t <TYPE>] -p <PID>
> coresched --new [-t <TYPE>] -- PROGRAM [ARGS...]
> coresched --copy -p <PID> [-t <TYPE>] -d <PID>
> coresched --copy -p <PID> [-t <TYPE>] -- PROGRAM [ARGS...]
>
> This follows the taskset's behavior (i.e. perform the same operation
> on either an existing PID or on a new program) and taskset's arguments
> (-p for PID instead of the -s I had earlier) more closely than before.
>
I think I like the above, nice.
> I've attached an interdiff of the util compared to the previous version
> that I sent yesterday. I think that this version encompases both our use
> cases quite nicely.
Let me give it a try. I may or may not have time to get to it today.
Other priorities arise. We'll see.
I do still prefer coreset as a name but I won't die on that hill :)
Cheers,
Phil
>
> Thijs
>
> Thijs Raymakers (1):
> coresched: Manage core scheduling cookies for tasks
>
> .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 | 363 ++++++++++++++++++++++++++++++++++++
> 8 files changed, 412 insertions(+), 6 deletions(-)
> create mode 100644 bash-completion/coresched
> create mode 100644 schedutils/coresched.1.adoc
> create mode 100644 schedutils/coresched.c
>
> Interdiff against v1:
> diff --git a/schedutils/coresched.c b/schedutils/coresched.c
> index 17d775f2d..537281fdb 100644
> --- a/schedutils/coresched.c
> +++ b/schedutils/coresched.c
> @@ -12,10 +12,12 @@
> #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, even if the
> @@ -26,32 +28,34 @@
> #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_EXEC = 0,
> - SCHED_CORE_CMD_GET = 1,
> - SCHED_CORE_CMD_CREATE = 2,
> - SCHED_CORE_CMD_COPY = 4,
> + SCHED_CORE_CMD_GET,
> + SCHED_CORE_CMD_NEW,
> + SCHED_CORE_CMD_COPY,
> } core_sched_cmd_t;
>
> struct args {
> - pid_t from_pid;
> - pid_t to_pid;
> + pid_t pid;
> + pid_t dest;
> core_sched_type_t type;
> core_sched_cmd_t cmd;
> int exec_argv_offset;
> };
>
> -unsigned long core_sched_get_cookie(struct args *args);
> -void core_sched_create_cookie(struct args *args);
> +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(struct args *args);
> +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);
> @@ -59,22 +63,31 @@ void parse_arguments(int argc, char **argv, struct args *args);
> void set_pid_or_err(pid_t *dest, pid_t src, const char *err_msg);
> static void __attribute__((__noreturn__)) usage(void);
>
> -unsigned long core_sched_get_cookie(struct args *args)
> +#define bad_usage(FMT...) \
> + warnx(FMT); \
> + errtryhelp(EINVAL);
> +
> +#define check_coresched_in_kernel(errno) \
> + if (errno == EINVAL) { \
> + warnx("Does your kernel support CONFIG_SCHED_CORE?"); \
> + }
> +
> +cookie_t core_sched_get_cookie(pid_t pid)
> {
> - unsigned long cookie = 0;
> - if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_GET, args->from_pid,
> + cookie_t cookie = 0;
> + if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_GET, pid,
> PR_SCHED_CORE_SCOPE_THREAD, &cookie)) {
> - err(errno, "Failed to get cookie from PID %d", args->from_pid);
> + check_coresched_in_kernel(errno);
> + err(errno, "Failed to get cookie from PID %d", pid);
> }
> return cookie;
> }
>
> -void core_sched_create_cookie(struct args *args)
> +void core_sched_create_cookie(pid_t pid, core_sched_type_t type)
> {
> - if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_CREATE, args->to_pid, args->type,
> - 0)) {
> - err(errno, "Failed to create cookie for PID %d",
> - args->from_pid);
> + 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);
> }
> }
>
> @@ -82,6 +95,7 @@ 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);
> }
> }
> @@ -89,14 +103,18 @@ void core_sched_pull_cookie(pid_t 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);
> }
> }
>
> -void core_sched_copy_cookie(struct args *args)
> +void core_sched_copy_cookie(pid_t from, pid_t to, core_sched_type_t to_type)
> {
> - core_sched_pull_cookie(args->from_pid);
> - core_sched_push_cookie(args->to_pid, args->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)
> @@ -111,11 +129,15 @@ void core_sched_exec_with_cookie(struct args *args, char **argv)
> // If a source PID is provided, try to copy the cookie from
> // that PID. Otherwise, create a brand new cookie with the
> // provided type.
> - if (args->from_pid) {
> - core_sched_pull_cookie(args->from_pid);
> + if (args->pid) {
> + core_sched_pull_cookie(args->pid);
> + core_sched_get_and_print_cookie(args->pid);
> } else {
> - args->to_pid = getpid();
> - core_sched_create_cookie(args);
> + 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)) {
> @@ -123,6 +145,13 @@ void core_sched_exec_with_cookie(struct args *args, char **argv)
> }
> }
>
> +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)) {
> @@ -133,156 +162,96 @@ core_sched_type_t parse_core_sched_type(char *str)
> return PR_SCHED_CORE_SCOPE_PROCESS_GROUP;
> }
>
> - errx(EINVAL, "'%s' is an invalid option. Must be one of pid/tgid/pgid",
> - str);
> + 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 --get <PID>\n"), program_invocation_short_name);
> - fprintf(stdout, _(" %s --new <PID> [-t <TYPE>]\n"),
> + 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 --copy -s <PID> -d <PID> [-t <TYPE>]\n"),
> + fprintf(stdout, _(" %s --new [-t <TYPE>] -- PROGRAM [ARGS...]\n"),
> program_invocation_short_name);
> - fprintf(stdout, _(" %s [-s <PID>] -- PROGRAM ARGS... \n"),
> + 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(_(" -g, --get <PID> get the core scheduling cookie of a PID"),
> + fputsln(_(" -n, --new assign a new core scheduling cookie to an existing PID or\n"
> + " execute a program with a new cookie."),
> stdout);
> - fputsln(_(" -n, --new <PID> assign a new core scheduling cookie to PID"),
> + 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(_(" -c, --copy copy the core scheduling cookie from PID to\n"
> - " another PID, requires the --source and --dest option"),
> + 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(_(" -s, --source <PID> where to copy the core scheduling cookie from."),
> - stdout);
> - fputsln(_(" -d, --dest <PID> where to copy the core scheduling cookie to."),
> + 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 of the destination PID, or the type of\n"
> - " the PID when a new core scheduling cookie\n"
> - " is created. Can be one of the following:\n"
> - " pid, tgid or pgid. Defaults to tgid."),
> + 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(
> - 25)); /* char offset to align option descriptions */
> + 20)); /* char offset to align option descriptions */
> fprintf(stdout, USAGE_MAN_TAIL("coresched(1)"));
> exit(EXIT_SUCCESS);
> }
>
> -bool verify_arguments(struct args *args)
> -{
> - // Check if the value of args->cmd is a power of 2
> - // In that case, only a single function option was set.
> - if (args->cmd & (args->cmd - 1)) {
> - errx(EINVAL,
> - "Cannot do more than one function at a time. See %s --help",
> - program_invocation_short_name);
> - }
> -
> - switch (args->cmd) {
> - case SCHED_CORE_CMD_GET:
> - if (args->to_pid) {
> - errx(EINVAL,
> - "Cannot use -d/--dest with this -g/--get. See %s --help",
> - program_invocation_short_name);
> - }
> - break;
> - case SCHED_CORE_CMD_CREATE:
> - if (args->from_pid) {
> - errx(EINVAL,
> - "Cannot use -s/--source with this -n/--new. See %s --help",
> - program_invocation_short_name);
> - }
> - break;
> - case SCHED_CORE_CMD_COPY:
> - if (!args->from_pid) {
> - errx(EINVAL,
> - "-s/--source PID is required when copying");
> - }
> - if (!args->to_pid) {
> - errx(EINVAL, "-d/--dest PID is required when copying");
> - }
> - break;
> - case SCHED_CORE_CMD_EXEC:
> - if (args->to_pid) {
> - errx(EINVAL,
> - "Cannot use -d/--dest when spawning a program. See %s --help",
> - program_invocation_short_name);
> - }
> - break;
> - }
> - return true;
> -}
> -
> -void set_pid_or_err(pid_t *dest, pid_t src, const char *err_msg)
> -{
> - if (*dest) {
> - errx(EINVAL, "Ambigious usage: %s", err_msg);
> - } else {
> - *dest = src;
> - }
> -}
> -
> -static const char *ERR_MSG_MULTIPLE_SOURCE_PIDS =
> - "Multiple source PIDs defined";
> void parse_arguments(int argc, char **argv, struct args *args)
> {
> int c;
> - pid_t tmp;
>
> static const struct option longopts[] = {
> - { "get", required_argument, NULL, 'g' },
> - { "new", required_argument, NULL, 'n' },
> + { "new", no_argument, NULL, 'n' },
> { "copy", no_argument, NULL, 'c' },
> - { "source", required_argument, NULL, 's' },
> - { "destination", required_argument, NULL, 'd' },
> + { "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 }
> + };
>
> - while ((c = getopt_long(argc, argv, "g:n:cs:d:t:Vh", longopts, NULL)) !=
> - -1)
> + 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 'g':
> - args->cmd |= SCHED_CORE_CMD_GET;
> - tmp = strtopid_or_err(
> - optarg, "Failed to parse PID for -g/--get");
> - set_pid_or_err(&args->from_pid, tmp,
> - ERR_MSG_MULTIPLE_SOURCE_PIDS);
> - break;
> case 'n':
> - args->cmd |= SCHED_CORE_CMD_CREATE;
> - tmp = strtopid_or_err(
> - optarg, "Failed to parse PID for -n/--new");
> - set_pid_or_err(&args->to_pid, tmp,
> - ERR_MSG_MULTIPLE_SOURCE_PIDS);
> + args->cmd = SCHED_CORE_CMD_NEW;
> break;
> case 'c':
> - args->cmd |= SCHED_CORE_CMD_COPY;
> + args->cmd = SCHED_CORE_CMD_COPY;
> break;
> - case 's':
> - tmp = strtopid_or_err(
> - optarg, "Failed to parse PID for -s/--source");
> - set_pid_or_err(&args->from_pid, tmp,
> - ERR_MSG_MULTIPLE_SOURCE_PIDS);
> + case 'p':
> + args->pid = strtopid_or_err(
> + optarg, "Failed to parse PID for -p/--pid");
> break;
> case 'd':
> - tmp = strtopid_or_err(
> + args->dest = strtopid_or_err(
> optarg, "Failed to parse PID for -d/--dest");
> - set_pid_or_err(&args->to_pid, tmp,
> - "Multiple destination PIDs defined");
> break;
> case 't':
> args->type = parse_core_sched_type(optarg);
> @@ -294,57 +263,98 @@ void parse_arguments(int argc, char **argv, struct args *args)
> 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) {
> - if (args->cmd == SCHED_CORE_CMD_EXEC) {
> - args->exec_argv_offset = optind;
> - } else {
> - // -g, -n or -c AND a program to run is provided
> - errx(EINVAL, "bad usage, see %s --help",
> - program_invocation_short_name);
> + 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;
> }
> - } else if (argc == optind && args->from_pid) {
> - // Neither a function (-g, -n, or -c), nor a program to
> - // run is given
> - args->cmd = SCHED_CORE_CMD_GET;
> }
>
> - verify_arguments(args);
> + 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 arguments = { 0 };
> - arguments.type = PR_SCHED_CORE_SCOPE_THREAD_GROUP;
> + 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, &arguments);
> + parse_arguments(argc, argv, &args);
> +
> + cookie_t cookie = 0;
>
> - unsigned long cookie = 0;
> - switch (arguments.cmd) {
> + switch (args.cmd) {
> case SCHED_CORE_CMD_GET:
> - cookie = core_sched_get_cookie(&arguments);
> - if (cookie) {
> - printf("core scheduling cookie of pid %d is 0x%lx\n",
> - arguments.from_pid, cookie);
> + 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 {
> - printf("pid %d doesn't have a core scheduling cookie\n",
> - arguments.from_pid);
> - exit(1);
> + usage();
> + exit(0);
> }
> break;
> - case SCHED_CORE_CMD_CREATE:
> - core_sched_create_cookie(&arguments);
> + 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:
> - core_sched_copy_cookie(&arguments);
> - break;
> - case SCHED_CORE_CMD_EXEC:
> - core_sched_exec_with_cookie(&arguments, argv);
> + if (args.dest) {
> + core_sched_copy_cookie(args.pid, args.dest, args.type);
> + } else {
> + core_sched_exec_with_cookie(&args, argv);
> + }
> break;
> default:
> usage();
> --
> 2.44.0
>
--
^ permalink raw reply
* [PATCH v2 1/1] coresched: Manage core scheduling cookies for tasks
From: Thijs Raymakers @ 2024-03-27 12:43 UTC (permalink / raw)
To: pauld; +Cc: kzak, util-linux, Thijs Raymakers
In-Reply-To: <20240326201722.gf315070@lorien.usersys.redhat.com>
Signed-off-by: Thijs Raymakers <thijs@raymakers.nl>
Thanks: Phil Auld for the valuable feedback
---
.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 | 363 ++++++++++++++++++++++++++++++++++++
8 files changed, 412 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..537281fdb
--- /dev/null
+++ b/schedutils/coresched.c
@@ -0,0 +1,363 @@
+/**
+ * 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, even if the
+// prctl interface accepts them.
+#ifndef PR_SCHED_CORE_SCOPE_THREAD
+#define PR_SCHED_CORE_SCOPE_THREAD 0
+#endif
+#ifndef PR_SCHED_CORE_SCOPE_THREAD_GROUP
+#define PR_SCHED_CORE_SCOPE_THREAD_GROUP 1
+#endif
+
+#ifndef PR_SCHED_CORE_SCOPE_PROCESS_GROUP
+#define PR_SCHED_CORE_SCOPE_PROCESS_GROUP 2
+#endif
+
+typedef int core_sched_type_t;
+typedef 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_coresched_in_kernel(errno) \
+ if (errno == EINVAL) { \
+ warnx("Does your kernel support CONFIG_SCHED_CORE?"); \
+ }
+
+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);
+ }
+ 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_coresched_in_kernel(errno);
+ err(errno, "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_coresched_in_kernel(errno);
+ err(errno, "Failed to pull cookie from PID %d", from);
+ }
+}
+
+void core_sched_push_cookie(pid_t to, core_sched_type_t type)
+{
+ if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_SHARE_TO, to, type, 0)) {
+ check_coresched_in_kernel(errno);
+ err(errno, "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
* [PATCH v2 0/1] coresched: Manage core scheduling cookies for tasks
From: Thijs Raymakers @ 2024-03-27 12:43 UTC (permalink / raw)
To: pauld; +Cc: kzak, util-linux, Thijs Raymakers
In-Reply-To: <20240326201722.gf315070@lorien.usersys.redhat.com>
Hi Phil,
Op 26-03-2024 om 19:13 schreef Phil Auld:
> On Tue, Mar 26, 2024 at 06:49:07PM +0100 Thijs Raymakers wrote:
>> Op 26-03-2024 om 15:41 schreef Phil Auld:
>>> On Sat, Mar 16, 2024 at 06:10:03PM +0100, Thijs Raymakers wrote:
>>>> - How should the program behave if the prctl core scheduling API is not
>>>> available? It has been in Linus' tree since november 2021
>>>> (commit a41b74451b35f7a6529689760eb8c05241feecbc) but it can be
>>>> disabled with CONFIG_SCHED_CORE=n
>>> This one is tricky. If I recall the error you get is ambiguous in this case
>>> (EINVAL, which can be returned for other reasons). It would be nice to be
>>> able to report that specifically.
>> In general, `prctl` does indeed return EINVAL if the operation is not
>> recognized, or not supported on the system. The `PR_SCHED_CORE`
>> operation itself only returns EINVAL if it is called with
>> - an invalid operation
>> - an invalid type
>> - a negative PID
>> - an invalid cookie store address (for PR_SCHED_CORE_GET)
>> Assuming that all these cases are prevented by the util, we could
>> interpret a EINVAL as a sign that PR_SCHED_CORE is not supported on
>> the system.
> Fair enough. Could say something like "got EINVAL. Does your kernel
> support CONFIG_SCHED_CORE?".
I've added this as a warning in case a prctl call returns EINVAL. I
haven't tested it on a kernel with CONFIG_SCHED_CORE=n yet but I will do
that later.
> There is also
> err_exclusive_options(c, longopts, excl, excl_st);
>
> in the optuils.h code which can handle the tests for mutually exclusive
> arguments.
>
> Thomas pointed me to that. It works nicely and can remove some of the extra
> checks (once you get it setup). And the error then looks the same as other
> util-linux progs.
Good suggestion, I replaced some of the earlier mutually exclusive
checks with a call to err_exclusive_options.
> You might consider "errtryhelp(EXIT_FAILURE);" in usage failures. A lot of
> the progs (incl taskset which is my model for this) do that. Then you only
> do the full usage when given -h/--help.
This does indeed feel a slightly nicer to use. I've added this in case a
of a usage failure. In case no options are given, it defaults to
printing the --help page. This matches the behavior of some other utils like
uclampset, and saves some time.
Op 26-03-2024 om 21:16 schreef Phil Auld:
> So "-n/--new" creates a new cookie and so does "". Just one on an existing task
> and one on the exec'd task. Seems inconsistent, no?
Hmmm, I didn't intially see this as inconsistent behavior but I get why
you say this. I've modified this now to have -n/--new deal both with
assigning cookies to existing processes and to spawn new processes.
Op 26-03-2024 om 21:17 schreef Phil Auld:
> Btw, did you try coreset to see if it does what you need?
I did take a look at it. Thanks for adding the functionality of copying
a cookie from/to an existing PID. The commands take a bit of getting
used to for me, so I guess it is just a matter of preference of what feels
natural to use.
Some things that weren't directly clear to me:
- Both `coreset ls` and `coreset -n ls` spawn a new program. If I
understood it correctly, the former spawns the program without
a cookie, and the latter spawns it with a cookie. I'm not entirely
sure what the point of the former would be, as the cookie would either
be 0 or equal to the cookie of the `coreset` process itself since the
cookie in inherited across execs. It is not necessary to run the
provided program in that case.
- Not entirely sure what the difference between --dest and --to is.
The names of these options are similar in meaning and do almost the
same thing.
- It is not clear in what cases --scope is ignored and in which
cases it is necessary.
- What does `coreset -c -s 0 -d 2 -p 1 ls` do? Does it first copy the
cookie from PID 1 to PID 2, and then run `ls` with the same or
different cookie? Or the other way around?
Now I do see what you meant earlier with imitating the behavior of the
`taskset` command. I don't think this is strictly necessary since it is
a different utility program, but I can understand that it can be useful
for people if the command behaves similar to a program that they already
know and have certain expectations of.
I've modified the options of coresched to be more in line with these
expectations, while also addressing the things I mentioned above.
Usage:
coresched [-p PID]
coresched --new [-t <TYPE>] -p <PID>
coresched --new [-t <TYPE>] -- PROGRAM [ARGS...]
coresched --copy -p <PID> [-t <TYPE>] -d <PID>
coresched --copy -p <PID> [-t <TYPE>] -- PROGRAM [ARGS...]
This follows the taskset's behavior (i.e. perform the same operation
on either an existing PID or on a new program) and taskset's arguments
(-p for PID instead of the -s I had earlier) more closely than before.
I've attached an interdiff of the util compared to the previous version
that I sent yesterday. I think that this version encompases both our use
cases quite nicely.
Thijs
Thijs Raymakers (1):
coresched: Manage core scheduling cookies for tasks
.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 | 363 ++++++++++++++++++++++++++++++++++++
8 files changed, 412 insertions(+), 6 deletions(-)
create mode 100644 bash-completion/coresched
create mode 100644 schedutils/coresched.1.adoc
create mode 100644 schedutils/coresched.c
Interdiff against v1:
diff --git a/schedutils/coresched.c b/schedutils/coresched.c
index 17d775f2d..537281fdb 100644
--- a/schedutils/coresched.c
+++ b/schedutils/coresched.c
@@ -12,10 +12,12 @@
#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, even if the
@@ -26,32 +28,34 @@
#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_EXEC = 0,
- SCHED_CORE_CMD_GET = 1,
- SCHED_CORE_CMD_CREATE = 2,
- SCHED_CORE_CMD_COPY = 4,
+ SCHED_CORE_CMD_GET,
+ SCHED_CORE_CMD_NEW,
+ SCHED_CORE_CMD_COPY,
} core_sched_cmd_t;
struct args {
- pid_t from_pid;
- pid_t to_pid;
+ pid_t pid;
+ pid_t dest;
core_sched_type_t type;
core_sched_cmd_t cmd;
int exec_argv_offset;
};
-unsigned long core_sched_get_cookie(struct args *args);
-void core_sched_create_cookie(struct args *args);
+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(struct args *args);
+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);
@@ -59,22 +63,31 @@ void parse_arguments(int argc, char **argv, struct args *args);
void set_pid_or_err(pid_t *dest, pid_t src, const char *err_msg);
static void __attribute__((__noreturn__)) usage(void);
-unsigned long core_sched_get_cookie(struct args *args)
+#define bad_usage(FMT...) \
+ warnx(FMT); \
+ errtryhelp(EINVAL);
+
+#define check_coresched_in_kernel(errno) \
+ if (errno == EINVAL) { \
+ warnx("Does your kernel support CONFIG_SCHED_CORE?"); \
+ }
+
+cookie_t core_sched_get_cookie(pid_t pid)
{
- unsigned long cookie = 0;
- if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_GET, args->from_pid,
+ cookie_t cookie = 0;
+ if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_GET, pid,
PR_SCHED_CORE_SCOPE_THREAD, &cookie)) {
- err(errno, "Failed to get cookie from PID %d", args->from_pid);
+ check_coresched_in_kernel(errno);
+ err(errno, "Failed to get cookie from PID %d", pid);
}
return cookie;
}
-void core_sched_create_cookie(struct args *args)
+void core_sched_create_cookie(pid_t pid, core_sched_type_t type)
{
- if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_CREATE, args->to_pid, args->type,
- 0)) {
- err(errno, "Failed to create cookie for PID %d",
- args->from_pid);
+ 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);
}
}
@@ -82,6 +95,7 @@ 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);
}
}
@@ -89,14 +103,18 @@ void core_sched_pull_cookie(pid_t 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);
}
}
-void core_sched_copy_cookie(struct args *args)
+void core_sched_copy_cookie(pid_t from, pid_t to, core_sched_type_t to_type)
{
- core_sched_pull_cookie(args->from_pid);
- core_sched_push_cookie(args->to_pid, args->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)
@@ -111,11 +129,15 @@ void core_sched_exec_with_cookie(struct args *args, char **argv)
// If a source PID is provided, try to copy the cookie from
// that PID. Otherwise, create a brand new cookie with the
// provided type.
- if (args->from_pid) {
- core_sched_pull_cookie(args->from_pid);
+ if (args->pid) {
+ core_sched_pull_cookie(args->pid);
+ core_sched_get_and_print_cookie(args->pid);
} else {
- args->to_pid = getpid();
- core_sched_create_cookie(args);
+ 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)) {
@@ -123,6 +145,13 @@ void core_sched_exec_with_cookie(struct args *args, char **argv)
}
}
+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)) {
@@ -133,156 +162,96 @@ core_sched_type_t parse_core_sched_type(char *str)
return PR_SCHED_CORE_SCOPE_PROCESS_GROUP;
}
- errx(EINVAL, "'%s' is an invalid option. Must be one of pid/tgid/pgid",
- str);
+ 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 --get <PID>\n"), program_invocation_short_name);
- fprintf(stdout, _(" %s --new <PID> [-t <TYPE>]\n"),
+ 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 --copy -s <PID> -d <PID> [-t <TYPE>]\n"),
+ fprintf(stdout, _(" %s --new [-t <TYPE>] -- PROGRAM [ARGS...]\n"),
program_invocation_short_name);
- fprintf(stdout, _(" %s [-s <PID>] -- PROGRAM ARGS... \n"),
+ 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(_(" -g, --get <PID> get the core scheduling cookie of a PID"),
+ fputsln(_(" -n, --new assign a new core scheduling cookie to an existing PID or\n"
+ " execute a program with a new cookie."),
stdout);
- fputsln(_(" -n, --new <PID> assign a new core scheduling cookie to PID"),
+ 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(_(" -c, --copy copy the core scheduling cookie from PID to\n"
- " another PID, requires the --source and --dest option"),
+ 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(_(" -s, --source <PID> where to copy the core scheduling cookie from."),
- stdout);
- fputsln(_(" -d, --dest <PID> where to copy the core scheduling cookie to."),
+ 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 of the destination PID, or the type of\n"
- " the PID when a new core scheduling cookie\n"
- " is created. Can be one of the following:\n"
- " pid, tgid or pgid. Defaults to tgid."),
+ 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(
- 25)); /* char offset to align option descriptions */
+ 20)); /* char offset to align option descriptions */
fprintf(stdout, USAGE_MAN_TAIL("coresched(1)"));
exit(EXIT_SUCCESS);
}
-bool verify_arguments(struct args *args)
-{
- // Check if the value of args->cmd is a power of 2
- // In that case, only a single function option was set.
- if (args->cmd & (args->cmd - 1)) {
- errx(EINVAL,
- "Cannot do more than one function at a time. See %s --help",
- program_invocation_short_name);
- }
-
- switch (args->cmd) {
- case SCHED_CORE_CMD_GET:
- if (args->to_pid) {
- errx(EINVAL,
- "Cannot use -d/--dest with this -g/--get. See %s --help",
- program_invocation_short_name);
- }
- break;
- case SCHED_CORE_CMD_CREATE:
- if (args->from_pid) {
- errx(EINVAL,
- "Cannot use -s/--source with this -n/--new. See %s --help",
- program_invocation_short_name);
- }
- break;
- case SCHED_CORE_CMD_COPY:
- if (!args->from_pid) {
- errx(EINVAL,
- "-s/--source PID is required when copying");
- }
- if (!args->to_pid) {
- errx(EINVAL, "-d/--dest PID is required when copying");
- }
- break;
- case SCHED_CORE_CMD_EXEC:
- if (args->to_pid) {
- errx(EINVAL,
- "Cannot use -d/--dest when spawning a program. See %s --help",
- program_invocation_short_name);
- }
- break;
- }
- return true;
-}
-
-void set_pid_or_err(pid_t *dest, pid_t src, const char *err_msg)
-{
- if (*dest) {
- errx(EINVAL, "Ambigious usage: %s", err_msg);
- } else {
- *dest = src;
- }
-}
-
-static const char *ERR_MSG_MULTIPLE_SOURCE_PIDS =
- "Multiple source PIDs defined";
void parse_arguments(int argc, char **argv, struct args *args)
{
int c;
- pid_t tmp;
static const struct option longopts[] = {
- { "get", required_argument, NULL, 'g' },
- { "new", required_argument, NULL, 'n' },
+ { "new", no_argument, NULL, 'n' },
{ "copy", no_argument, NULL, 'c' },
- { "source", required_argument, NULL, 's' },
- { "destination", required_argument, NULL, 'd' },
+ { "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 }
+ };
- while ((c = getopt_long(argc, argv, "g:n:cs:d:t:Vh", longopts, NULL)) !=
- -1)
+ 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 'g':
- args->cmd |= SCHED_CORE_CMD_GET;
- tmp = strtopid_or_err(
- optarg, "Failed to parse PID for -g/--get");
- set_pid_or_err(&args->from_pid, tmp,
- ERR_MSG_MULTIPLE_SOURCE_PIDS);
- break;
case 'n':
- args->cmd |= SCHED_CORE_CMD_CREATE;
- tmp = strtopid_or_err(
- optarg, "Failed to parse PID for -n/--new");
- set_pid_or_err(&args->to_pid, tmp,
- ERR_MSG_MULTIPLE_SOURCE_PIDS);
+ args->cmd = SCHED_CORE_CMD_NEW;
break;
case 'c':
- args->cmd |= SCHED_CORE_CMD_COPY;
+ args->cmd = SCHED_CORE_CMD_COPY;
break;
- case 's':
- tmp = strtopid_or_err(
- optarg, "Failed to parse PID for -s/--source");
- set_pid_or_err(&args->from_pid, tmp,
- ERR_MSG_MULTIPLE_SOURCE_PIDS);
+ case 'p':
+ args->pid = strtopid_or_err(
+ optarg, "Failed to parse PID for -p/--pid");
break;
case 'd':
- tmp = strtopid_or_err(
+ args->dest = strtopid_or_err(
optarg, "Failed to parse PID for -d/--dest");
- set_pid_or_err(&args->to_pid, tmp,
- "Multiple destination PIDs defined");
break;
case 't':
args->type = parse_core_sched_type(optarg);
@@ -294,57 +263,98 @@ void parse_arguments(int argc, char **argv, struct args *args)
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) {
- if (args->cmd == SCHED_CORE_CMD_EXEC) {
- args->exec_argv_offset = optind;
- } else {
- // -g, -n or -c AND a program to run is provided
- errx(EINVAL, "bad usage, see %s --help",
- program_invocation_short_name);
+ 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;
}
- } else if (argc == optind && args->from_pid) {
- // Neither a function (-g, -n, or -c), nor a program to
- // run is given
- args->cmd = SCHED_CORE_CMD_GET;
}
- verify_arguments(args);
+ 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 arguments = { 0 };
- arguments.type = PR_SCHED_CORE_SCOPE_THREAD_GROUP;
+ 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, &arguments);
+ parse_arguments(argc, argv, &args);
+
+ cookie_t cookie = 0;
- unsigned long cookie = 0;
- switch (arguments.cmd) {
+ switch (args.cmd) {
case SCHED_CORE_CMD_GET:
- cookie = core_sched_get_cookie(&arguments);
- if (cookie) {
- printf("core scheduling cookie of pid %d is 0x%lx\n",
- arguments.from_pid, cookie);
+ 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 {
- printf("pid %d doesn't have a core scheduling cookie\n",
- arguments.from_pid);
- exit(1);
+ usage();
+ exit(0);
}
break;
- case SCHED_CORE_CMD_CREATE:
- core_sched_create_cookie(&arguments);
+ 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:
- core_sched_copy_cookie(&arguments);
- break;
- case SCHED_CORE_CMD_EXEC:
- core_sched_exec_with_cookie(&arguments, argv);
+ if (args.dest) {
+ core_sched_copy_cookie(args.pid, args.dest, args.type);
+ } else {
+ core_sched_exec_with_cookie(&args, argv);
+ }
break;
default:
usage();
--
2.44.0
^ permalink raw reply related
* Re: Mistakes in messages of util-linux-2.40-rc2
From: Karel Zak @ 2024-03-27 7:52 UTC (permalink / raw)
To: Benno Schulenberg; +Cc: util-linux, Petr Pisar
In-Reply-To: <257b46c7-dbe9-414e-a707-fc543d1e7849@telfort.nl>
Hi Benno and Petr,
On Tue, Mar 26, 2024 at 04:48:09PM +0100, Benno Schulenberg wrote:
>
> Op 26-03-2024 om 12:28 schreef Petr Pisar:
> > while translating util-linux-2.40-rc2 I notice a few messages which are, in my
> > opinion, incorrect: [ ...]
>
> A patch for most of these issues is coming up.
>
> Karel, would there be time for an rc3? It wouldn't need to delay
> things much: giving translators three or four days to update their
> translations for these small changes should be enough.
I need to release v2.40 today, it's already scheduled due to the end of
embargo for one CVE.
I guess we will have v2.40.1 very soon ;-)
Karel
--
Karel Zak <kzak@redhat.com>
http://karelzak.blogspot.com
^ permalink raw reply
* Re: [PATCH 0/1] coresched: Manage core scheduling cookies for tasks
From: Phil Auld @ 2024-03-26 20:17 UTC (permalink / raw)
To: Thijs Raymakers; +Cc: kzak, util-linux
In-Reply-To: <20240326201618.GE315070@lorien.usersys.redhat.com>
On Tue, Mar 26, 2024 at 04:16:23PM -0400 Phil Auld wrote:
> On Tue, Mar 26, 2024 at 08:26:23PM +0100 Thijs Raymakers wrote:
> > Op 26-03-2024 om 20:09 schreef Phil Auld:
> > > I tried this version out and it doesn't work for me. One of the
> > > basic use cases of a wrapper like this is to run a command with
> > > a new cookie (say starting a container or something).
> > >
> > > Coresched requires a pid to do that:
> > > # ./coresched -n ls
> > > coresched: Failed to parse PID for -n/--new: 'ls'
> > >
> > > With my coreset utility it does work and gives some information about what
> > > it did:
> > >
> > > # coreset -n ls
> > > pid 20860's current cookie: 0x0
> > > pid 20860's new cookie: 0xa9fcfbf1
> > > ABOUT-NLS chrt configure ionice libsmartcols Makefile.in
> > > ...
> > >
> > > Did I miss something? I think this will be one of the primary use cases for
> > > this utility.
> >
> > Hi Phil,
> >
> > The following command works
> >
> > # ./coresched ls
> > chrt.1 chrt.1.deps coresched.1.adoc coresched.o ionice.1.adoc
> > ...
>
> Hhm, I'd prefer the no argument case to be a noop.
>
> But okay, I was missing something :) This is where some output might
> have helped though ...
>
> So "-n/--new" creates a new cookie and so does "". Just one on an existing task
> and one on the exec'd task. Seems inconsistent, no?
>
> >
> > By default, it will spawn the provided program with a new core
> > scheduling cookie.
> >
>
> See above about default. But maybe that's just my taste.
>
> Since you have expplicit commands to do things why not just make --get be
> the default no command one? So coresched -p 1234 just reports the cookie
> of pid 1234 or corsched ls just reports (in this case the meaningless) cookie
> of the exec'd ls command.
>
> The use the -n and friends as an action operation to create/copy etc.
>
>
> > This command
> > # ./coresched -n 123
> >
> > is to assign a new core scheduling cookie to PID 123. The commands don't
> > show what the current and/or new cookie is, because that is of limited
> > utility in my opinion (and --get also does this).
>
> Yes, true. But it's sometimes nice to see that the command you ran did
> something,
>
>
> But anyway, that's more of my 2 cents. I'll try it again with new
> knowledge.
>
Btw, did you try coreset to see if it does what you need?
Cheers,
Phil
>
> Cheers,
> Phil
>
>
>
> > You can only use it to check if two programs have the same cookie.
> > An error message will be printed and the exit code will be set if it
> > doesn't succeed. Otherwise,
> > if the program succeeds then it won't write anything to stderr/stdout.
> >
> > Thijs
> >
>
> --
--
^ permalink raw reply
* Re: [PATCH 0/1] coresched: Manage core scheduling cookies for tasks
From: Phil Auld @ 2024-03-26 20:16 UTC (permalink / raw)
To: Thijs Raymakers; +Cc: kzak, util-linux
In-Reply-To: <c43ab992-9fb0-4b73-a542-71e4e8a86cf5@raymakers.nl>
On Tue, Mar 26, 2024 at 08:26:23PM +0100 Thijs Raymakers wrote:
> Op 26-03-2024 om 20:09 schreef Phil Auld:
> > I tried this version out and it doesn't work for me. One of the
> > basic use cases of a wrapper like this is to run a command with
> > a new cookie (say starting a container or something).
> >
> > Coresched requires a pid to do that:
> > # ./coresched -n ls
> > coresched: Failed to parse PID for -n/--new: 'ls'
> >
> > With my coreset utility it does work and gives some information about what
> > it did:
> >
> > # coreset -n ls
> > pid 20860's current cookie: 0x0
> > pid 20860's new cookie: 0xa9fcfbf1
> > ABOUT-NLS chrt configure ionice libsmartcols Makefile.in
> > ...
> >
> > Did I miss something? I think this will be one of the primary use cases for
> > this utility.
>
> Hi Phil,
>
> The following command works
>
> # ./coresched ls
> chrt.1 chrt.1.deps coresched.1.adoc coresched.o ionice.1.adoc
> ...
Hhm, I'd prefer the no argument case to be a noop.
But okay, I was missing something :) This is where some output might
have helped though ...
So "-n/--new" creates a new cookie and so does "". Just one on an existing task
and one on the exec'd task. Seems inconsistent, no?
>
> By default, it will spawn the provided program with a new core
> scheduling cookie.
>
See above about default. But maybe that's just my taste.
Since you have expplicit commands to do things why not just make --get be
the default no command one? So coresched -p 1234 just reports the cookie
of pid 1234 or corsched ls just reports (in this case the meaningless) cookie
of the exec'd ls command.
The use the -n and friends as an action operation to create/copy etc.
> This command
> # ./coresched -n 123
>
> is to assign a new core scheduling cookie to PID 123. The commands don't
> show what the current and/or new cookie is, because that is of limited
> utility in my opinion (and --get also does this).
Yes, true. But it's sometimes nice to see that the command you ran did
something,
But anyway, that's more of my 2 cents. I'll try it again with new
knowledge.
Cheers,
Phil
> You can only use it to check if two programs have the same cookie.
> An error message will be printed and the exit code will be set if it
> doesn't succeed. Otherwise,
> if the program succeeds then it won't write anything to stderr/stdout.
>
> Thijs
>
--
^ permalink raw reply
* Re: [PATCH 0/1] coresched: Manage core scheduling cookies for tasks
From: Thijs Raymakers @ 2024-03-26 19:26 UTC (permalink / raw)
To: Phil Auld; +Cc: kzak, util-linux
In-Reply-To: <20240326190910.GD315070@lorien.usersys.redhat.com>
Op 26-03-2024 om 20:09 schreef Phil Auld:
> I tried this version out and it doesn't work for me. One of the
> basic use cases of a wrapper like this is to run a command with
> a new cookie (say starting a container or something).
>
> Coresched requires a pid to do that:
> # ./coresched -n ls
> coresched: Failed to parse PID for -n/--new: 'ls'
>
> With my coreset utility it does work and gives some information about what
> it did:
>
> # coreset -n ls
> pid 20860's current cookie: 0x0
> pid 20860's new cookie: 0xa9fcfbf1
> ABOUT-NLS chrt configure ionice libsmartcols Makefile.in
> ...
>
> Did I miss something? I think this will be one of the primary use cases for
> this utility.
Hi Phil,
The following command works
# ./coresched ls
chrt.1 chrt.1.deps coresched.1.adoc coresched.o ionice.1.adoc
...
By default, it will spawn the provided program with a new core
scheduling cookie.
This command
# ./coresched -n 123
is to assign a new core scheduling cookie to PID 123. The commands don't
show what the current and/or new cookie is, because that is of limited
utility in my opinion (and --get also does this). You can only use it to
check if two programs have the same cookie. An error message will be
printed and the exit code will be set if it doesn't succeed. Otherwise,
if the program succeeds then it won't write anything to stderr/stdout.
Thijs
^ permalink raw reply
* Re: [PATCH 0/1] coresched: Manage core scheduling cookies for tasks
From: Phil Auld @ 2024-03-26 19:09 UTC (permalink / raw)
To: Thijs Raymakers; +Cc: kzak, util-linux
In-Reply-To: <20240326174909.117426-1-thijs@raymakers.nl>
Hi Thijs,
On Tue, Mar 26, 2024 at 06:49:07PM +0100 Thijs Raymakers wrote:
> Hi Phil,
>
> Thank you for your comments. I've attached an interdiff to this email
> with all the changes that I've made in response.
>
I tried this version out and it doesn't work for me. One of the
basic use cases of a wrapper like this is to run a command with
a new cookie (say starting a container or something).
Coresched requires a pid to do that:
# ./coresched -n ls
coresched: Failed to parse PID for -n/--new: 'ls'
With my coreset utility it does work and gives some information about what
it did:
# coreset -n ls
pid 20860's current cookie: 0x0
pid 20860's new cookie: 0xa9fcfbf1
ABOUT-NLS chrt configure ionice libsmartcols Makefile.in
...
Did I miss something? I think this will be one of the primary use cases for
this utility.
Cheers,
Phil
> Phil Auld wrote on 26-03-2024 at 15:41:
> > The exec should not fork first.
>
> You're right, this is not necessary. I've removed it.
>
> > And the error handling I think is just not right. I believe prctl()
> > returns -1 on error and sets errno. This code will report EPERM for all
> > prtcl errors. While EPERM is likely I don't think it's the only error.
>
> Good catch! I assumed that the return values of the `prctl()` call
> matched the return values of `sched_core_share_pid` in
> `kernel/sched/core_sched.c`. I've updated the error handling code to
> just use errno instead.
>
> > On Sat, Mar 16, 2024 at 06:10:03PM +0100, Thijs Raymakers wrote:
> >> - How should the program behave if the prctl core scheduling API is not
> >> available? It has been in Linus' tree since november 2021
> >> (commit a41b74451b35f7a6529689760eb8c05241feecbc) but it can be
> >> disabled with CONFIG_SCHED_CORE=n
> >
> > This one is tricky. If I recall the error you get is ambiguous in this case
> > (EINVAL, which can be returned for other reasons). It would be nice to be
> > able to report that specifically.
>
> In general, `prctl` does indeed return EINVAL if the operation is not
> recognized, or not supported on the system. The `PR_SCHED_CORE`
> operation itself only returns EINVAL if it is called with
> - an invalid operation
> - an invalid type
> - a negative PID
> - an invalid cookie store address (for PR_SCHED_CORE_GET)
> Assuming that all these cases are prevented by the util, we could
> interpret a EINVAL as a sign that PR_SCHED_CORE is not supported on
> the system.
>
> >> - I've currently licensed it under the EUPL v1.2, which is easier to
> >> enforce in my jurisdiction than the GPL. It is GPL compatible so it
> >> shouldn't be an issue, but if anybody has any remarks on this, please
> >> let me know.
> >
> > The util-linux package is GPL so personally I think it would be good to just
> > make any new addition to that package also GPL.
>
> As far as I understand it, util-linux is a collection of different works
> with different licenses (see the README.licensing file). Not every
> program included in util-linux has to be GPL.
>
> >> +typedef enum {
> >> + SCHED_CORE_SCOPE_PID = PR_SCHED_CORE_SCOPE_THREAD,
> >> + SCHED_CORE_SCOPE_TGID = PR_SCHED_CORE_SCOPE_THREAD_GROUP,
> >> + SCHED_CORE_SCOPE_PGID = PR_SCHED_CORE_SCOPE_PROCESS_GROUP,
> >> +} core_sched_type_t;
> >> +
> >
> > Is this needed? You've made the names a little shorter I guess.
> > Also some versions of prctl.h don't have the PR_SCHED_CORE_SCOPE* defines
> > even when they have the base PR_SCHED_CORE macros.
>
> I've changed to to just use the PR_SCHED_CORE_SCOPE* definitions
> instead.
>
> >> +void core_sched_create_cookie(struct args *args)
> >> +{
> >> + int prctl_errno = prctl(PR_SCHED_CORE, PR_SCHED_CORE_CREATE,
> >> + args->from_pid, args->type, 0);
> >
> > We are creating a new cookie on from_pid, seems odd.
>
> I agree, I changed to to `args->to_pid` because that is more logical.
>
> >> + fprintf(stdout, _(" %s --get <PID>\n"), program_invocation_short_name);
> >> + fprintf(stdout, _(" %s --new <PID> [-t <TYPE>]\n"),
> >> + program_invocation_short_name);
> >> + fprintf(stdout, _(" %s --copy -s <PID> -d <PID> [-t <TYPE>]\n"),
> >> + program_invocation_short_name);
> >> + fprintf(stdout, _(" %s --exec [-s <PID>] -- PROGRAM ARGS... \n"),
> >> + program_invocation_short_name);
> >
> > There are 4 different arguments that take a pid when there are really
> > only at most 2 needed.
>
> I chose this to keep the amount of options that you have to provide as
> small possible. I thought that `coresched -n 123` would be easier to use
> than `coresched -n -p 123`. I guess the amount of different arguments
> that can take PIDs can be reduced to two, but it would make the commands
> a bit longer.
>
> > "Get" could just be the default with no arguments given.
>
> I've removed the explicit --exec and made it the default when a program
> name is provided. If no program name is provided, it defaults to --get.
>
> >> + enum {
> >> + OPT_GET = 'g',
> >> + OPT_NEW = 'n',
> >> + OPT_COPY = 'c',
> >> + OPT_EXEC = 'e',
> >> + OPT_SRC = 's',
> >> + OPT_DEST = 'd',
> >> + OPT_TYPE = 't',
> >> + OPT_VERSION = 'V',
> >> + OPT_HELP = 'h'
> >> + };
> >
> > This seems over-engineered. You have to specificy the characters
> > in the getopt_long call anyway so this just complicates the code for
> > no reason it seems to me.
>
> Fair point, I've removed this. I initially added this to make it easier
> to experiment a bit with different values for the options, but that is
> no longer necessary.
>
> Thanks!
>
> - Thijs
>
> Thijs Raymakers (1):
> coresched: Manage core scheduling cookies for tasks
>
> .gitignore | 1 +
> bash-completion/coresched | 0
> configure.ac | 12 +-
> meson.build | 16 +-
> meson_options.txt | 2 +-
> schedutils/Makemodule.am | 8 +
> schedutils/coresched.1.adoc | 16 ++
> schedutils/coresched.c | 353 ++++++++++++++++++++++++++++++++++++
> 8 files changed, 402 insertions(+), 6 deletions(-)
> create mode 100644 bash-completion/coresched
> create mode 100644 schedutils/coresched.1.adoc
> create mode 100644 schedutils/coresched.c
>
> Interdiff:
> diff --git a/schedutils/coresched.c b/schedutils/coresched.c
> index 4be8f9fda..17d775f2d 100644
> --- a/schedutils/coresched.c
> +++ b/schedutils/coresched.c
> @@ -18,18 +18,24 @@
> #include "nls.h"
> #include "strutils.h"
>
> +// These definitions might not be defined, even if the
> +// prctl interface accepts them.
> +#ifndef PR_SCHED_CORE_SCOPE_THREAD
> +#define PR_SCHED_CORE_SCOPE_THREAD 0
> +#endif
> +#ifndef PR_SCHED_CORE_SCOPE_THREAD_GROUP
> +#define PR_SCHED_CORE_SCOPE_THREAD_GROUP 1
> +#endif
> +#ifndef PR_SCHED_CORE_SCOPE_PROCESS_GROUP
> +#define PR_SCHED_CORE_SCOPE_PROCESS_GROUP 2
> +#endif
> +
> +typedef int core_sched_type_t;
> typedef enum {
> - SCHED_CORE_SCOPE_PID = PR_SCHED_CORE_SCOPE_THREAD,
> - SCHED_CORE_SCOPE_TGID = PR_SCHED_CORE_SCOPE_THREAD_GROUP,
> - SCHED_CORE_SCOPE_PGID = PR_SCHED_CORE_SCOPE_PROCESS_GROUP,
> -} core_sched_type_t;
> -
> -typedef enum {
> - SCHED_CORE_CMD_NONE = 0,
> + SCHED_CORE_CMD_EXEC = 0,
> SCHED_CORE_CMD_GET = 1,
> SCHED_CORE_CMD_CREATE = 2,
> SCHED_CORE_CMD_COPY = 4,
> - SCHED_CORE_CMD_EXEC = 8,
> } core_sched_cmd_t;
>
> struct args {
> @@ -50,44 +56,40 @@ void core_sched_exec_with_cookie(struct args *args, char **argv);
> core_sched_type_t parse_core_sched_type(char *str);
> bool verify_arguments(struct args *args);
> void parse_arguments(int argc, char **argv, struct args *args);
> +void set_pid_or_err(pid_t *dest, pid_t src, const char *err_msg);
> +static void __attribute__((__noreturn__)) usage(void);
>
> unsigned long core_sched_get_cookie(struct args *args)
> {
> unsigned long cookie = 0;
> - int prctl_errno = prctl(PR_SCHED_CORE, PR_SCHED_CORE_GET,
> - args->from_pid, SCHED_CORE_SCOPE_PID, &cookie);
> - if (prctl_errno) {
> - errx(-prctl_errno, "Failed to get cookie from PID %d",
> - args->from_pid);
> + if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_GET, args->from_pid,
> + PR_SCHED_CORE_SCOPE_THREAD, &cookie)) {
> + err(errno, "Failed to get cookie from PID %d", args->from_pid);
> }
> return cookie;
> }
>
> void core_sched_create_cookie(struct args *args)
> {
> - int prctl_errno = prctl(PR_SCHED_CORE, PR_SCHED_CORE_CREATE,
> - args->from_pid, args->type, 0);
> - if (prctl_errno) {
> - errx(-prctl_errno, "Failed to create cookie for PID %d",
> - args->from_pid);
> + if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_CREATE, args->to_pid, args->type,
> + 0)) {
> + err(errno, "Failed to create cookie for PID %d",
> + args->from_pid);
> }
> }
>
> void core_sched_pull_cookie(pid_t from)
> {
> - int prctl_errno = prctl(PR_SCHED_CORE, PR_SCHED_CORE_SHARE_FROM, from,
> - SCHED_CORE_SCOPE_PID, 0);
> - if (prctl_errno) {
> - errx(-prctl_errno, "Failed to pull cookie from PID %d", from);
> + if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_SHARE_FROM, from,
> + PR_SCHED_CORE_SCOPE_THREAD, 0)) {
> + err(errno, "Failed to pull cookie from PID %d", from);
> }
> }
>
> void core_sched_push_cookie(pid_t to, core_sched_type_t type)
> {
> - int prctl_errno =
> - prctl(PR_SCHED_CORE, PR_SCHED_CORE_SHARE_TO, to, type, 0);
> - if (prctl_errno) {
> - errx(-prctl_errno, "Failed to push cookie to PID %d", to);
> + if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_SHARE_TO, to, type, 0)) {
> + err(errno, "Failed to push cookie to PID %d", to);
> }
> }
>
> @@ -100,46 +102,35 @@ void core_sched_copy_cookie(struct args *args)
> void core_sched_exec_with_cookie(struct args *args, char **argv)
> {
> if (!args->exec_argv_offset) {
> - errx(EINVAL, "when --exec is provided, a program name "
> - "has to be given.");
> + usage();
> }
>
> // Move the argument list to the first argument of the program
> argv = &argv[args->exec_argv_offset];
>
> - pid_t pid = fork();
> - if (pid == -1) {
> - errx(errno, "Failed to spawn new process");
> + // If a source PID is provided, try to copy the cookie from
> + // that PID. Otherwise, create a brand new cookie with the
> + // provided type.
> + if (args->from_pid) {
> + core_sched_pull_cookie(args->from_pid);
> + } else {
> + args->to_pid = getpid();
> + core_sched_create_cookie(args);
> }
>
> - if (!pid) {
> - // If a source PID is provided, try to copy the cookie from
> - // that PID. Otherwise, create a brand new cookie with the
> - // provided type.
> - if (args->from_pid) {
> - core_sched_pull_cookie(args->from_pid);
> - } else {
> - args->from_pid = getpid();
> - core_sched_create_cookie(args);
> - }
> - if (execvp(argv[0], argv)) {
> - errexec(argv[0]);
> - }
> - } else {
> - int status = 0;
> - waitpid(pid, &status, 0);
> - exit(status);
> + if (execvp(argv[0], argv)) {
> + errexec(argv[0]);
> }
> }
>
> core_sched_type_t parse_core_sched_type(char *str)
> {
> if (!strncmp(str, "pid\0", 4)) {
> - return SCHED_CORE_SCOPE_PID;
> + return PR_SCHED_CORE_SCOPE_THREAD;
> } else if (!strncmp(str, "tgid\0", 5)) {
> - return SCHED_CORE_SCOPE_TGID;
> + return PR_SCHED_CORE_SCOPE_THREAD_GROUP;
> } else if (!strncmp(str, "pgid\0", 5)) {
> - return SCHED_CORE_SCOPE_PGID;
> + return PR_SCHED_CORE_SCOPE_PROCESS_GROUP;
> }
>
> errx(EINVAL, "'%s' is an invalid option. Must be one of pid/tgid/pgid",
> @@ -155,7 +146,7 @@ static void __attribute__((__noreturn__)) usage(void)
> program_invocation_short_name);
> fprintf(stdout, _(" %s --copy -s <PID> -d <PID> [-t <TYPE>]\n"),
> program_invocation_short_name);
> - fprintf(stdout, _(" %s --exec [-s <PID>] -- PROGRAM ARGS... \n"),
> + fprintf(stdout, _(" %s [-s <PID>] -- PROGRAM ARGS... \n"),
> program_invocation_short_name);
>
> fputs(USAGE_SEPARATOR, stdout);
> @@ -169,9 +160,6 @@ static void __attribute__((__noreturn__)) usage(void)
> fputsln(_(" -c, --copy copy the core scheduling cookie from PID to\n"
> " another PID, requires the --source and --dest option"),
> stdout);
> - fputsln(_(" -e, --exec execute a program with a new core scheduling\n"
> - " cookie."),
> - stdout);
>
> fputs(USAGE_OPTIONS, stdout);
> fputsln(_(" -s, --source <PID> where to copy the core scheduling cookie from."),
> @@ -193,116 +181,141 @@ static void __attribute__((__noreturn__)) usage(void)
>
> bool verify_arguments(struct args *args)
> {
> - if (args->cmd == SCHED_CORE_CMD_NONE) {
> - usage();
> - }
> -
> // Check if the value of args->cmd is a power of 2
> // In that case, only a single function option was set.
> - if (!(args->cmd && !(args->cmd & (args->cmd - 1)))) {
> - errx(EINVAL, "Cannot do more than one function at a time.");
> - }
> -
> - if (args->from_pid < 0) {
> - errx(EINVAL, "source PID cannot be negative");
> + if (args->cmd & (args->cmd - 1)) {
> + errx(EINVAL,
> + "Cannot do more than one function at a time. See %s --help",
> + program_invocation_short_name);
> }
>
> - if (args->to_pid < 0) {
> - errx(EINVAL, "destination PID cannot be negative");
> - }
> -
> - if (args->from_pid == 0 && args->cmd == SCHED_CORE_CMD_COPY) {
> - errx(EINVAL, "valid argument to --source is required");
> - }
> -
> - if (args->to_pid == 0 && args->cmd == SCHED_CORE_CMD_COPY) {
> - errx(EINVAL, "valid argument to --dest is required");
> + switch (args->cmd) {
> + case SCHED_CORE_CMD_GET:
> + if (args->to_pid) {
> + errx(EINVAL,
> + "Cannot use -d/--dest with this -g/--get. See %s --help",
> + program_invocation_short_name);
> + }
> + break;
> + case SCHED_CORE_CMD_CREATE:
> + if (args->from_pid) {
> + errx(EINVAL,
> + "Cannot use -s/--source with this -n/--new. See %s --help",
> + program_invocation_short_name);
> + }
> + break;
> + case SCHED_CORE_CMD_COPY:
> + if (!args->from_pid) {
> + errx(EINVAL,
> + "-s/--source PID is required when copying");
> + }
> + if (!args->to_pid) {
> + errx(EINVAL, "-d/--dest PID is required when copying");
> + }
> + break;
> + case SCHED_CORE_CMD_EXEC:
> + if (args->to_pid) {
> + errx(EINVAL,
> + "Cannot use -d/--dest when spawning a program. See %s --help",
> + program_invocation_short_name);
> + }
> + break;
> }
> + return true;
> +}
>
> - if (args->from_pid == 0 && args->cmd != SCHED_CORE_CMD_EXEC) {
> - errx(EINVAL, "PID cannot be zero");
> +void set_pid_or_err(pid_t *dest, pid_t src, const char *err_msg)
> +{
> + if (*dest) {
> + errx(EINVAL, "Ambigious usage: %s", err_msg);
> + } else {
> + *dest = src;
> }
> -
> - return true;
> }
>
> +static const char *ERR_MSG_MULTIPLE_SOURCE_PIDS =
> + "Multiple source PIDs defined";
> void parse_arguments(int argc, char **argv, struct args *args)
> {
> int c;
> -
> - enum {
> - OPT_GET = 'g',
> - OPT_NEW = 'n',
> - OPT_COPY = 'c',
> - OPT_EXEC = 'e',
> - OPT_SRC = 's',
> - OPT_DEST = 'd',
> - OPT_TYPE = 't',
> - OPT_VERSION = 'V',
> - OPT_HELP = 'h'
> - };
> + pid_t tmp;
>
> static const struct option longopts[] = {
> - { "get", required_argument, NULL, OPT_GET },
> - { "new", required_argument, NULL, OPT_NEW },
> - { "copy", no_argument, NULL, OPT_COPY },
> - { "exec", no_argument, NULL, OPT_EXEC },
> - { "source", required_argument, NULL, OPT_SRC },
> - { "destination", required_argument, NULL, OPT_DEST },
> - { "type", required_argument, NULL, OPT_TYPE },
> - { "version", no_argument, NULL, OPT_VERSION },
> - { "help", no_argument, NULL, OPT_HELP },
> + { "get", required_argument, NULL, 'g' },
> + { "new", required_argument, NULL, 'n' },
> + { "copy", no_argument, NULL, 'c' },
> + { "source", required_argument, NULL, 's' },
> + { "destination", required_argument, NULL, 'd' },
> + { "type", required_argument, NULL, 't' },
> + { "version", no_argument, NULL, 'V' },
> + { "help", no_argument, NULL, 'h' },
> { NULL, 0, NULL, 0 }
> };
>
> - while ((c = getopt_long(argc, argv, "g:n:ces:d:t:Vh", longopts,
> - NULL)) != -1)
> + while ((c = getopt_long(argc, argv, "g:n:cs:d:t:Vh", longopts, NULL)) !=
> + -1)
> switch (c) {
> - case OPT_GET:
> + case 'g':
> args->cmd |= SCHED_CORE_CMD_GET;
> - args->from_pid = strtos32_or_err(
> - optarg, "Failed to parse PID for --get");
> + tmp = strtopid_or_err(
> + optarg, "Failed to parse PID for -g/--get");
> + set_pid_or_err(&args->from_pid, tmp,
> + ERR_MSG_MULTIPLE_SOURCE_PIDS);
> break;
> - case OPT_NEW:
> + case 'n':
> args->cmd |= SCHED_CORE_CMD_CREATE;
> - args->from_pid = strtos32_or_err(
> - optarg, "Failed to parse PID for --new");
> + tmp = strtopid_or_err(
> + optarg, "Failed to parse PID for -n/--new");
> + set_pid_or_err(&args->to_pid, tmp,
> + ERR_MSG_MULTIPLE_SOURCE_PIDS);
> break;
> - case OPT_COPY:
> + case 'c':
> args->cmd |= SCHED_CORE_CMD_COPY;
> break;
> - case OPT_EXEC:
> - args->cmd |= SCHED_CORE_CMD_EXEC;
> - break;
> - case OPT_SRC:
> - args->from_pid = strtos32_or_err(
> - optarg, "Failed to parse PID for --source");
> + case 's':
> + tmp = strtopid_or_err(
> + optarg, "Failed to parse PID for -s/--source");
> + set_pid_or_err(&args->from_pid, tmp,
> + ERR_MSG_MULTIPLE_SOURCE_PIDS);
> break;
> - case OPT_DEST:
> - args->to_pid = strtos32_or_err(
> - optarg, "Failed to parse PID for --dest");
> + case 'd':
> + tmp = strtopid_or_err(
> + optarg, "Failed to parse PID for -d/--dest");
> + set_pid_or_err(&args->to_pid, tmp,
> + "Multiple destination PIDs defined");
> break;
> - case OPT_TYPE:
> + case 't':
> args->type = parse_core_sched_type(optarg);
> break;
> - case OPT_VERSION:
> + case 'V':
> print_version(EXIT_SUCCESS);
> - case OPT_HELP:
> + case 'h':
> usage();
> default:
> errtryhelp(EXIT_FAILURE);
> }
>
> if (argc > optind) {
> - args->exec_argv_offset = optind;
> + if (args->cmd == SCHED_CORE_CMD_EXEC) {
> + args->exec_argv_offset = optind;
> + } else {
> + // -g, -n or -c AND a program to run is provided
> + errx(EINVAL, "bad usage, see %s --help",
> + program_invocation_short_name);
> + }
> + } else if (argc == optind && args->from_pid) {
> + // Neither a function (-g, -n, or -c), nor a program to
> + // run is given
> + args->cmd = SCHED_CORE_CMD_GET;
> }
> +
> verify_arguments(args);
> }
>
> int main(int argc, char **argv)
> {
> struct args arguments = { 0 };
> - arguments.type = SCHED_CORE_SCOPE_TGID;
> + arguments.type = PR_SCHED_CORE_SCOPE_THREAD_GROUP;
>
> setlocale(LC_ALL, "");
> bindtextdomain(PACKAGE, LOCALEDIR);
> --
> 2.44.0
>
--
^ permalink raw reply
* Re: [PATCH 0/1] coresched: Manage core scheduling cookies for tasks
From: Phil Auld @ 2024-03-26 18:13 UTC (permalink / raw)
To: Thijs Raymakers; +Cc: kzak, util-linux
In-Reply-To: <20240326174909.117426-1-thijs@raymakers.nl>
Hi Thijs,
On Tue, Mar 26, 2024 at 06:49:07PM +0100 Thijs Raymakers wrote:
> Hi Phil,
>
> Thank you for your comments. I've attached an interdiff to this email
> with all the changes that I've made in response.
Fwiw, I've updated my version with the ability to handle your pull/push
use case if you want to give that a try and see if it works for you.
https://github.com/util-linux/util-linux/pull/2839
I still find it a little closer to taskset and thus maybe more
intuitive for people. But I'll try to give your version a try on
my setup and see if my fingers/muscle memory can do it :)
>
> Phil Auld wrote on 26-03-2024 at 15:41:
> > The exec should not fork first.
>
> You're right, this is not necessary. I've removed it.
>
> > And the error handling I think is just not right. I believe prctl()
> > returns -1 on error and sets errno. This code will report EPERM for all
> > prtcl errors. While EPERM is likely I don't think it's the only error.
>
> Good catch! I assumed that the return values of the `prctl()` call
> matched the return values of `sched_core_share_pid` in
> `kernel/sched/core_sched.c`. I've updated the error handling code to
> just use errno instead.
>
> > On Sat, Mar 16, 2024 at 06:10:03PM +0100, Thijs Raymakers wrote:
> >> - How should the program behave if the prctl core scheduling API is not
> >> available? It has been in Linus' tree since november 2021
> >> (commit a41b74451b35f7a6529689760eb8c05241feecbc) but it can be
> >> disabled with CONFIG_SCHED_CORE=n
> >
> > This one is tricky. If I recall the error you get is ambiguous in this case
> > (EINVAL, which can be returned for other reasons). It would be nice to be
> > able to report that specifically.
>
> In general, `prctl` does indeed return EINVAL if the operation is not
> recognized, or not supported on the system. The `PR_SCHED_CORE`
> operation itself only returns EINVAL if it is called with
> - an invalid operation
> - an invalid type
> - a negative PID
> - an invalid cookie store address (for PR_SCHED_CORE_GET)
> Assuming that all these cases are prevented by the util, we could
> interpret a EINVAL as a sign that PR_SCHED_CORE is not supported on
> the system.
>
Fair enough. Could say something like "got EINVAL. Does your kernel
support CONFIG_SCHED_CORE?".
> >> - I've currently licensed it under the EUPL v1.2, which is easier to
> >> enforce in my jurisdiction than the GPL. It is GPL compatible so it
> >> shouldn't be an issue, but if anybody has any remarks on this, please
> >> let me know.
> >
> > The util-linux package is GPL so personally I think it would be good to just
> > make any new addition to that package also GPL.
>
> As far as I understand it, util-linux is a collection of different works
> with different licenses (see the README.licensing file). Not every
> program included in util-linux has to be GPL.
>
> >> +typedef enum {
> >> + SCHED_CORE_SCOPE_PID = PR_SCHED_CORE_SCOPE_THREAD,
> >> + SCHED_CORE_SCOPE_TGID = PR_SCHED_CORE_SCOPE_THREAD_GROUP,
> >> + SCHED_CORE_SCOPE_PGID = PR_SCHED_CORE_SCOPE_PROCESS_GROUP,
> >> +} core_sched_type_t;
> >> +
> >
> > Is this needed? You've made the names a little shorter I guess.
> > Also some versions of prctl.h don't have the PR_SCHED_CORE_SCOPE* defines
> > even when they have the base PR_SCHED_CORE macros.
>
> I've changed to to just use the PR_SCHED_CORE_SCOPE* definitions
> instead.
>
> >> +void core_sched_create_cookie(struct args *args)
> >> +{
> >> + int prctl_errno = prctl(PR_SCHED_CORE, PR_SCHED_CORE_CREATE,
> >> + args->from_pid, args->type, 0);
> >
> > We are creating a new cookie on from_pid, seems odd.
>
> I agree, I changed to to `args->to_pid` because that is more logical.
>
> >> + fprintf(stdout, _(" %s --get <PID>\n"), program_invocation_short_name);
> >> + fprintf(stdout, _(" %s --new <PID> [-t <TYPE>]\n"),
> >> + program_invocation_short_name);
> >> + fprintf(stdout, _(" %s --copy -s <PID> -d <PID> [-t <TYPE>]\n"),
> >> + program_invocation_short_name);
> >> + fprintf(stdout, _(" %s --exec [-s <PID>] -- PROGRAM ARGS... \n"),
> >> + program_invocation_short_name);
> >
> > There are 4 different arguments that take a pid when there are really
> > only at most 2 needed.
>
> I chose this to keep the amount of options that you have to provide as
> small possible. I thought that `coresched -n 123` would be easier to use
> than `coresched -n -p 123`. I guess the amount of different arguments
> that can take PIDs can be reduced to two, but it would make the commands
> a bit longer.
>
> > "Get" could just be the default with no arguments given.
>
> I've removed the explicit --exec and made it the default when a program
> name is provided. If no program name is provided, it defaults to --get.
>
> >> + enum {
> >> + OPT_GET = 'g',
> >> + OPT_NEW = 'n',
> >> + OPT_COPY = 'c',
> >> + OPT_EXEC = 'e',
> >> + OPT_SRC = 's',
> >> + OPT_DEST = 'd',
> >> + OPT_TYPE = 't',
> >> + OPT_VERSION = 'V',
> >> + OPT_HELP = 'h'
> >> + };
> >
> > This seems over-engineered. You have to specificy the characters
> > in the getopt_long call anyway so this just complicates the code for
> > no reason it seems to me.
>
> Fair point, I've removed this. I initially added this to make it easier
> to experiment a bit with different values for the options, but that is
> no longer necessary.
>
I do see that in some other util-linux programs but it doesn't make sense
there either, to me :)
There is also
err_exclusive_options(c, longopts, excl, excl_st);
in the optuils.h code which can handle the tests for mutually exclusive
arguments.
Thomas pointed me to that. It works nicely and can remove some of the extra
checks (once you get it setup). And the error then looks the same as other
util-linux progs.
You might consider "errtryhelp(EXIT_FAILURE);" in usage failures. A lot of
the progs (incl taskset which is my model for this) do that. Then you only
do the full usage when given -h/--help.
Cheers,
Phil
> Thanks!
>
> - Thijs
>
> Thijs Raymakers (1):
> coresched: Manage core scheduling cookies for tasks
>
> .gitignore | 1 +
> bash-completion/coresched | 0
> configure.ac | 12 +-
> meson.build | 16 +-
> meson_options.txt | 2 +-
> schedutils/Makemodule.am | 8 +
> schedutils/coresched.1.adoc | 16 ++
> schedutils/coresched.c | 353 ++++++++++++++++++++++++++++++++++++
> 8 files changed, 402 insertions(+), 6 deletions(-)
> create mode 100644 bash-completion/coresched
> create mode 100644 schedutils/coresched.1.adoc
> create mode 100644 schedutils/coresched.c
>
> Interdiff:
> diff --git a/schedutils/coresched.c b/schedutils/coresched.c
> index 4be8f9fda..17d775f2d 100644
> --- a/schedutils/coresched.c
> +++ b/schedutils/coresched.c
> @@ -18,18 +18,24 @@
> #include "nls.h"
> #include "strutils.h"
>
> +// These definitions might not be defined, even if the
> +// prctl interface accepts them.
> +#ifndef PR_SCHED_CORE_SCOPE_THREAD
> +#define PR_SCHED_CORE_SCOPE_THREAD 0
> +#endif
> +#ifndef PR_SCHED_CORE_SCOPE_THREAD_GROUP
> +#define PR_SCHED_CORE_SCOPE_THREAD_GROUP 1
> +#endif
> +#ifndef PR_SCHED_CORE_SCOPE_PROCESS_GROUP
> +#define PR_SCHED_CORE_SCOPE_PROCESS_GROUP 2
> +#endif
> +
> +typedef int core_sched_type_t;
> typedef enum {
> - SCHED_CORE_SCOPE_PID = PR_SCHED_CORE_SCOPE_THREAD,
> - SCHED_CORE_SCOPE_TGID = PR_SCHED_CORE_SCOPE_THREAD_GROUP,
> - SCHED_CORE_SCOPE_PGID = PR_SCHED_CORE_SCOPE_PROCESS_GROUP,
> -} core_sched_type_t;
> -
> -typedef enum {
> - SCHED_CORE_CMD_NONE = 0,
> + SCHED_CORE_CMD_EXEC = 0,
> SCHED_CORE_CMD_GET = 1,
> SCHED_CORE_CMD_CREATE = 2,
> SCHED_CORE_CMD_COPY = 4,
> - SCHED_CORE_CMD_EXEC = 8,
> } core_sched_cmd_t;
>
> struct args {
> @@ -50,44 +56,40 @@ void core_sched_exec_with_cookie(struct args *args, char **argv);
> core_sched_type_t parse_core_sched_type(char *str);
> bool verify_arguments(struct args *args);
> void parse_arguments(int argc, char **argv, struct args *args);
> +void set_pid_or_err(pid_t *dest, pid_t src, const char *err_msg);
> +static void __attribute__((__noreturn__)) usage(void);
>
> unsigned long core_sched_get_cookie(struct args *args)
> {
> unsigned long cookie = 0;
> - int prctl_errno = prctl(PR_SCHED_CORE, PR_SCHED_CORE_GET,
> - args->from_pid, SCHED_CORE_SCOPE_PID, &cookie);
> - if (prctl_errno) {
> - errx(-prctl_errno, "Failed to get cookie from PID %d",
> - args->from_pid);
> + if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_GET, args->from_pid,
> + PR_SCHED_CORE_SCOPE_THREAD, &cookie)) {
> + err(errno, "Failed to get cookie from PID %d", args->from_pid);
> }
> return cookie;
> }
>
> void core_sched_create_cookie(struct args *args)
> {
> - int prctl_errno = prctl(PR_SCHED_CORE, PR_SCHED_CORE_CREATE,
> - args->from_pid, args->type, 0);
> - if (prctl_errno) {
> - errx(-prctl_errno, "Failed to create cookie for PID %d",
> - args->from_pid);
> + if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_CREATE, args->to_pid, args->type,
> + 0)) {
> + err(errno, "Failed to create cookie for PID %d",
> + args->from_pid);
> }
> }
>
> void core_sched_pull_cookie(pid_t from)
> {
> - int prctl_errno = prctl(PR_SCHED_CORE, PR_SCHED_CORE_SHARE_FROM, from,
> - SCHED_CORE_SCOPE_PID, 0);
> - if (prctl_errno) {
> - errx(-prctl_errno, "Failed to pull cookie from PID %d", from);
> + if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_SHARE_FROM, from,
> + PR_SCHED_CORE_SCOPE_THREAD, 0)) {
> + err(errno, "Failed to pull cookie from PID %d", from);
> }
> }
>
> void core_sched_push_cookie(pid_t to, core_sched_type_t type)
> {
> - int prctl_errno =
> - prctl(PR_SCHED_CORE, PR_SCHED_CORE_SHARE_TO, to, type, 0);
> - if (prctl_errno) {
> - errx(-prctl_errno, "Failed to push cookie to PID %d", to);
> + if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_SHARE_TO, to, type, 0)) {
> + err(errno, "Failed to push cookie to PID %d", to);
> }
> }
>
> @@ -100,46 +102,35 @@ void core_sched_copy_cookie(struct args *args)
> void core_sched_exec_with_cookie(struct args *args, char **argv)
> {
> if (!args->exec_argv_offset) {
> - errx(EINVAL, "when --exec is provided, a program name "
> - "has to be given.");
> + usage();
> }
>
> // Move the argument list to the first argument of the program
> argv = &argv[args->exec_argv_offset];
>
> - pid_t pid = fork();
> - if (pid == -1) {
> - errx(errno, "Failed to spawn new process");
> + // If a source PID is provided, try to copy the cookie from
> + // that PID. Otherwise, create a brand new cookie with the
> + // provided type.
> + if (args->from_pid) {
> + core_sched_pull_cookie(args->from_pid);
> + } else {
> + args->to_pid = getpid();
> + core_sched_create_cookie(args);
> }
>
> - if (!pid) {
> - // If a source PID is provided, try to copy the cookie from
> - // that PID. Otherwise, create a brand new cookie with the
> - // provided type.
> - if (args->from_pid) {
> - core_sched_pull_cookie(args->from_pid);
> - } else {
> - args->from_pid = getpid();
> - core_sched_create_cookie(args);
> - }
> - if (execvp(argv[0], argv)) {
> - errexec(argv[0]);
> - }
> - } else {
> - int status = 0;
> - waitpid(pid, &status, 0);
> - exit(status);
> + if (execvp(argv[0], argv)) {
> + errexec(argv[0]);
> }
> }
>
> core_sched_type_t parse_core_sched_type(char *str)
> {
> if (!strncmp(str, "pid\0", 4)) {
> - return SCHED_CORE_SCOPE_PID;
> + return PR_SCHED_CORE_SCOPE_THREAD;
> } else if (!strncmp(str, "tgid\0", 5)) {
> - return SCHED_CORE_SCOPE_TGID;
> + return PR_SCHED_CORE_SCOPE_THREAD_GROUP;
> } else if (!strncmp(str, "pgid\0", 5)) {
> - return SCHED_CORE_SCOPE_PGID;
> + return PR_SCHED_CORE_SCOPE_PROCESS_GROUP;
> }
>
> errx(EINVAL, "'%s' is an invalid option. Must be one of pid/tgid/pgid",
> @@ -155,7 +146,7 @@ static void __attribute__((__noreturn__)) usage(void)
> program_invocation_short_name);
> fprintf(stdout, _(" %s --copy -s <PID> -d <PID> [-t <TYPE>]\n"),
> program_invocation_short_name);
> - fprintf(stdout, _(" %s --exec [-s <PID>] -- PROGRAM ARGS... \n"),
> + fprintf(stdout, _(" %s [-s <PID>] -- PROGRAM ARGS... \n"),
> program_invocation_short_name);
>
> fputs(USAGE_SEPARATOR, stdout);
> @@ -169,9 +160,6 @@ static void __attribute__((__noreturn__)) usage(void)
> fputsln(_(" -c, --copy copy the core scheduling cookie from PID to\n"
> " another PID, requires the --source and --dest option"),
> stdout);
> - fputsln(_(" -e, --exec execute a program with a new core scheduling\n"
> - " cookie."),
> - stdout);
>
> fputs(USAGE_OPTIONS, stdout);
> fputsln(_(" -s, --source <PID> where to copy the core scheduling cookie from."),
> @@ -193,116 +181,141 @@ static void __attribute__((__noreturn__)) usage(void)
>
> bool verify_arguments(struct args *args)
> {
> - if (args->cmd == SCHED_CORE_CMD_NONE) {
> - usage();
> - }
> -
> // Check if the value of args->cmd is a power of 2
> // In that case, only a single function option was set.
> - if (!(args->cmd && !(args->cmd & (args->cmd - 1)))) {
> - errx(EINVAL, "Cannot do more than one function at a time.");
> - }
> -
> - if (args->from_pid < 0) {
> - errx(EINVAL, "source PID cannot be negative");
> + if (args->cmd & (args->cmd - 1)) {
> + errx(EINVAL,
> + "Cannot do more than one function at a time. See %s --help",
> + program_invocation_short_name);
> }
>
> - if (args->to_pid < 0) {
> - errx(EINVAL, "destination PID cannot be negative");
> - }
> -
> - if (args->from_pid == 0 && args->cmd == SCHED_CORE_CMD_COPY) {
> - errx(EINVAL, "valid argument to --source is required");
> - }
> -
> - if (args->to_pid == 0 && args->cmd == SCHED_CORE_CMD_COPY) {
> - errx(EINVAL, "valid argument to --dest is required");
> + switch (args->cmd) {
> + case SCHED_CORE_CMD_GET:
> + if (args->to_pid) {
> + errx(EINVAL,
> + "Cannot use -d/--dest with this -g/--get. See %s --help",
> + program_invocation_short_name);
> + }
> + break;
> + case SCHED_CORE_CMD_CREATE:
> + if (args->from_pid) {
> + errx(EINVAL,
> + "Cannot use -s/--source with this -n/--new. See %s --help",
> + program_invocation_short_name);
> + }
> + break;
> + case SCHED_CORE_CMD_COPY:
> + if (!args->from_pid) {
> + errx(EINVAL,
> + "-s/--source PID is required when copying");
> + }
> + if (!args->to_pid) {
> + errx(EINVAL, "-d/--dest PID is required when copying");
> + }
> + break;
> + case SCHED_CORE_CMD_EXEC:
> + if (args->to_pid) {
> + errx(EINVAL,
> + "Cannot use -d/--dest when spawning a program. See %s --help",
> + program_invocation_short_name);
> + }
> + break;
> }
> + return true;
> +}
>
> - if (args->from_pid == 0 && args->cmd != SCHED_CORE_CMD_EXEC) {
> - errx(EINVAL, "PID cannot be zero");
> +void set_pid_or_err(pid_t *dest, pid_t src, const char *err_msg)
> +{
> + if (*dest) {
> + errx(EINVAL, "Ambigious usage: %s", err_msg);
> + } else {
> + *dest = src;
> }
> -
> - return true;
> }
>
> +static const char *ERR_MSG_MULTIPLE_SOURCE_PIDS =
> + "Multiple source PIDs defined";
> void parse_arguments(int argc, char **argv, struct args *args)
> {
> int c;
> -
> - enum {
> - OPT_GET = 'g',
> - OPT_NEW = 'n',
> - OPT_COPY = 'c',
> - OPT_EXEC = 'e',
> - OPT_SRC = 's',
> - OPT_DEST = 'd',
> - OPT_TYPE = 't',
> - OPT_VERSION = 'V',
> - OPT_HELP = 'h'
> - };
> + pid_t tmp;
>
> static const struct option longopts[] = {
> - { "get", required_argument, NULL, OPT_GET },
> - { "new", required_argument, NULL, OPT_NEW },
> - { "copy", no_argument, NULL, OPT_COPY },
> - { "exec", no_argument, NULL, OPT_EXEC },
> - { "source", required_argument, NULL, OPT_SRC },
> - { "destination", required_argument, NULL, OPT_DEST },
> - { "type", required_argument, NULL, OPT_TYPE },
> - { "version", no_argument, NULL, OPT_VERSION },
> - { "help", no_argument, NULL, OPT_HELP },
> + { "get", required_argument, NULL, 'g' },
> + { "new", required_argument, NULL, 'n' },
> + { "copy", no_argument, NULL, 'c' },
> + { "source", required_argument, NULL, 's' },
> + { "destination", required_argument, NULL, 'd' },
> + { "type", required_argument, NULL, 't' },
> + { "version", no_argument, NULL, 'V' },
> + { "help", no_argument, NULL, 'h' },
> { NULL, 0, NULL, 0 }
> };
>
> - while ((c = getopt_long(argc, argv, "g:n:ces:d:t:Vh", longopts,
> - NULL)) != -1)
> + while ((c = getopt_long(argc, argv, "g:n:cs:d:t:Vh", longopts, NULL)) !=
> + -1)
> switch (c) {
> - case OPT_GET:
> + case 'g':
> args->cmd |= SCHED_CORE_CMD_GET;
> - args->from_pid = strtos32_or_err(
> - optarg, "Failed to parse PID for --get");
> + tmp = strtopid_or_err(
> + optarg, "Failed to parse PID for -g/--get");
> + set_pid_or_err(&args->from_pid, tmp,
> + ERR_MSG_MULTIPLE_SOURCE_PIDS);
> break;
> - case OPT_NEW:
> + case 'n':
> args->cmd |= SCHED_CORE_CMD_CREATE;
> - args->from_pid = strtos32_or_err(
> - optarg, "Failed to parse PID for --new");
> + tmp = strtopid_or_err(
> + optarg, "Failed to parse PID for -n/--new");
> + set_pid_or_err(&args->to_pid, tmp,
> + ERR_MSG_MULTIPLE_SOURCE_PIDS);
> break;
> - case OPT_COPY:
> + case 'c':
> args->cmd |= SCHED_CORE_CMD_COPY;
> break;
> - case OPT_EXEC:
> - args->cmd |= SCHED_CORE_CMD_EXEC;
> - break;
> - case OPT_SRC:
> - args->from_pid = strtos32_or_err(
> - optarg, "Failed to parse PID for --source");
> + case 's':
> + tmp = strtopid_or_err(
> + optarg, "Failed to parse PID for -s/--source");
> + set_pid_or_err(&args->from_pid, tmp,
> + ERR_MSG_MULTIPLE_SOURCE_PIDS);
> break;
> - case OPT_DEST:
> - args->to_pid = strtos32_or_err(
> - optarg, "Failed to parse PID for --dest");
> + case 'd':
> + tmp = strtopid_or_err(
> + optarg, "Failed to parse PID for -d/--dest");
> + set_pid_or_err(&args->to_pid, tmp,
> + "Multiple destination PIDs defined");
> break;
> - case OPT_TYPE:
> + case 't':
> args->type = parse_core_sched_type(optarg);
> break;
> - case OPT_VERSION:
> + case 'V':
> print_version(EXIT_SUCCESS);
> - case OPT_HELP:
> + case 'h':
> usage();
> default:
> errtryhelp(EXIT_FAILURE);
> }
>
> if (argc > optind) {
> - args->exec_argv_offset = optind;
> + if (args->cmd == SCHED_CORE_CMD_EXEC) {
> + args->exec_argv_offset = optind;
> + } else {
> + // -g, -n or -c AND a program to run is provided
> + errx(EINVAL, "bad usage, see %s --help",
> + program_invocation_short_name);
> + }
> + } else if (argc == optind && args->from_pid) {
> + // Neither a function (-g, -n, or -c), nor a program to
> + // run is given
> + args->cmd = SCHED_CORE_CMD_GET;
> }
> +
> verify_arguments(args);
> }
>
> int main(int argc, char **argv)
> {
> struct args arguments = { 0 };
> - arguments.type = SCHED_CORE_SCOPE_TGID;
> + arguments.type = PR_SCHED_CORE_SCOPE_THREAD_GROUP;
>
> setlocale(LC_ALL, "");
> bindtextdomain(PACKAGE, LOCALEDIR);
> --
> 2.44.0
>
--
^ permalink raw reply
* [PATCH 1/1] coresched: Manage core scheduling cookies for tasks
From: Thijs Raymakers @ 2024-03-26 17:49 UTC (permalink / raw)
To: pauld; +Cc: kzak, util-linux, Thijs Raymakers
In-Reply-To: <20240326144129.GA315070@lorien.usersys.redhat.com>
Signed-off-by: Thijs Raymakers <thijs@raymakers.nl>
---
.gitignore | 1 +
bash-completion/coresched | 0
configure.ac | 12 +-
meson.build | 16 +-
meson_options.txt | 2 +-
schedutils/Makemodule.am | 8 +
schedutils/coresched.1.adoc | 16 ++
schedutils/coresched.c | 353 ++++++++++++++++++++++++++++++++++++
8 files changed, 402 insertions(+), 6 deletions(-)
create mode 100644 bash-completion/coresched
create mode 100644 schedutils/coresched.1.adoc
create mode 100644 schedutils/coresched.c
diff --git a/.gitignore b/.gitignore
index 6ecbfa7fe..316f3cdcc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -94,6 +94,7 @@ ylwrap
/colcrt
/colrm
/column
+/coresched
/ctrlaltdel
/delpart
/dmesg
diff --git a/bash-completion/coresched b/bash-completion/coresched
new file mode 100644
index 000000000..e69de29bb
diff --git a/configure.ac b/configure.ac
index ab7c98636..3a189a075 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2500,9 +2500,9 @@ UL_REQUIRES_HAVE([setterm], [ncursesw, ncurses], [ncursesw or ncurses library])
AM_CONDITIONAL([BUILD_SETTERM], [test "x$build_setterm" = xyes])
# build_schedutils= is just configure-only variable to control
-# ionice, taskset and chrt
+# ionice, taskset, coresched and chrt
AC_ARG_ENABLE([schedutils],
- AS_HELP_STRING([--disable-schedutils], [do not build chrt, ionice, taskset]),
+ AS_HELP_STRING([--disable-schedutils], [do not build chrt, ionice, taskset, coresched]),
[], [UL_DEFAULT_ENABLE([schedutils], [check])]
)
@@ -2545,6 +2545,14 @@ UL_REQUIRES_SYSCALL_CHECK([taskset],
AM_CONDITIONAL([BUILD_TASKSET], [test "x$build_taskset" = xyes])
+UL_ENABLE_ALIAS([coresched], [schedutils])
+UL_BUILD_INIT([coresched])
+UL_REQUIRES_SYSCALL_CHECK([coresched],
+ [UL_CHECK_SYSCALL([prctl])],
+ [prctl])
+AM_CONDITIONAL([BUILD_CORESCHED], [test "x$build_coresched" = xyes])
+
+
have_schedsetter=no
AS_IF([test "x$ac_cv_func_sched_setscheduler" = xyes], [have_schedsetter=yes],
[test "x$ac_cv_func_sched_setattr" = xyes], [have_schedsetter=yes])
diff --git a/meson.build b/meson.build
index 9600ce49f..9a2c04e8f 100644
--- a/meson.build
+++ b/meson.build
@@ -3111,13 +3111,23 @@ exe4 = executable(
install : opt,
build_by_default : opt)
+exe5 = executable(
+ 'coresched',
+ 'schedutils/coresched.c',
+ include_directories : includes,
+ link_with : lib_common,
+ install_dir : usrbin_exec_dir,
+ install : opt,
+ build_by_default : opt)
+
if opt and not is_disabler(exe)
- exes += [exe, exe2, exe3, exe4]
+ exes += [exe, exe2, exe3, exe4, exe5]
manadocs += ['schedutils/chrt.1.adoc',
'schedutils/ionice.1.adoc',
'schedutils/taskset.1.adoc',
- 'schedutils/uclampset.1.adoc']
- bashcompletions += ['chrt', 'ionice', 'taskset', 'uclampset']
+ 'schedutils/uclampset.1.adoc',
+ 'schedutils/coresched.1.adoc']
+ bashcompletions += ['chrt', 'ionice', 'taskset', 'uclampset', 'coresched']
endif
############################################################
diff --git a/meson_options.txt b/meson_options.txt
index 7b8cf3f35..3405c1b73 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -162,7 +162,7 @@ option('build-pipesz', type : 'feature',
option('build-setterm', type : 'feature',
description : 'build setterm')
option('build-schedutils', type : 'feature',
- description : 'build chrt, ionice, taskset')
+ description : 'build chrt, ionice, taskset, coresched')
option('build-wall', type : 'feature',
description : 'build wall')
option('build-write', type : 'feature',
diff --git a/schedutils/Makemodule.am b/schedutils/Makemodule.am
index 1040da85f..0cb655401 100644
--- a/schedutils/Makemodule.am
+++ b/schedutils/Makemodule.am
@@ -29,3 +29,11 @@ dist_noinst_DATA += schedutils/uclampset.1.adoc
uclampset_SOURCES = schedutils/uclampset.c schedutils/sched_attr.h
uclampset_LDADD = $(LDADD) libcommon.la
endif
+
+if BUILD_CORESCHED
+usrbin_exec_PROGRAMS += coresched
+MANPAGES += schedutils/coresched.1
+dist_noinst_DATA += schedutils/coresched.1.adoc
+coresched_SOURCES = schedutils/coresched.c
+coresched_LDADD = $(LDADD) libcommon.la
+endif
diff --git a/schedutils/coresched.1.adoc b/schedutils/coresched.1.adoc
new file mode 100644
index 000000000..60a21cd01
--- /dev/null
+++ b/schedutils/coresched.1.adoc
@@ -0,0 +1,16 @@
+//po4a: entry man manual
+////
+coresched(1) manpage
+////
+= coresched(1)
+:doctype: manpage
+:man manual: User Commands
+:man source: util-linux {release-version}
+:page-layout: base
+:command: coresched
+:colon: :
+:copyright: ©
+
+== NAME
+
+coresched - manage core scheduling cookies for tasks
diff --git a/schedutils/coresched.c b/schedutils/coresched.c
new file mode 100644
index 000000000..17d775f2d
--- /dev/null
+++ b/schedutils/coresched.c
@@ -0,0 +1,353 @@
+/**
+ * SPDX-License-Identifier: EUPL-1.2
+ *
+ * coresched.c - manage core scheduling cookies for tasks
+ *
+ * Copyright (C) 2024 Thijs Raymakers
+ * Licensed under the EUPL v1.2
+ */
+
+#include <getopt.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <sys/prctl.h>
+#include <sys/wait.h>
+
+#include "c.h"
+#include "closestream.h"
+#include "nls.h"
+#include "strutils.h"
+
+// These definitions might not be defined, even if the
+// prctl interface accepts them.
+#ifndef PR_SCHED_CORE_SCOPE_THREAD
+#define PR_SCHED_CORE_SCOPE_THREAD 0
+#endif
+#ifndef PR_SCHED_CORE_SCOPE_THREAD_GROUP
+#define PR_SCHED_CORE_SCOPE_THREAD_GROUP 1
+#endif
+#ifndef PR_SCHED_CORE_SCOPE_PROCESS_GROUP
+#define PR_SCHED_CORE_SCOPE_PROCESS_GROUP 2
+#endif
+
+typedef int core_sched_type_t;
+typedef enum {
+ SCHED_CORE_CMD_EXEC = 0,
+ SCHED_CORE_CMD_GET = 1,
+ SCHED_CORE_CMD_CREATE = 2,
+ SCHED_CORE_CMD_COPY = 4,
+} core_sched_cmd_t;
+
+struct args {
+ pid_t from_pid;
+ pid_t to_pid;
+ core_sched_type_t type;
+ core_sched_cmd_t cmd;
+ int exec_argv_offset;
+};
+
+unsigned long core_sched_get_cookie(struct args *args);
+void core_sched_create_cookie(struct args *args);
+void core_sched_pull_cookie(pid_t from);
+void core_sched_push_cookie(pid_t to, core_sched_type_t type);
+void core_sched_copy_cookie(struct args *args);
+void core_sched_exec_with_cookie(struct args *args, char **argv);
+
+core_sched_type_t parse_core_sched_type(char *str);
+bool verify_arguments(struct args *args);
+void parse_arguments(int argc, char **argv, struct args *args);
+void set_pid_or_err(pid_t *dest, pid_t src, const char *err_msg);
+static void __attribute__((__noreturn__)) usage(void);
+
+unsigned long core_sched_get_cookie(struct args *args)
+{
+ unsigned long cookie = 0;
+ if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_GET, args->from_pid,
+ PR_SCHED_CORE_SCOPE_THREAD, &cookie)) {
+ err(errno, "Failed to get cookie from PID %d", args->from_pid);
+ }
+ return cookie;
+}
+
+void core_sched_create_cookie(struct args *args)
+{
+ if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_CREATE, args->to_pid, args->type,
+ 0)) {
+ err(errno, "Failed to create cookie for PID %d",
+ args->from_pid);
+ }
+}
+
+void core_sched_pull_cookie(pid_t from)
+{
+ if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_SHARE_FROM, from,
+ PR_SCHED_CORE_SCOPE_THREAD, 0)) {
+ err(errno, "Failed to pull cookie from PID %d", from);
+ }
+}
+
+void core_sched_push_cookie(pid_t to, core_sched_type_t type)
+{
+ if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_SHARE_TO, to, type, 0)) {
+ err(errno, "Failed to push cookie to PID %d", to);
+ }
+}
+
+void core_sched_copy_cookie(struct args *args)
+{
+ core_sched_pull_cookie(args->from_pid);
+ core_sched_push_cookie(args->to_pid, args->type);
+}
+
+void core_sched_exec_with_cookie(struct args *args, char **argv)
+{
+ if (!args->exec_argv_offset) {
+ usage();
+ }
+
+ // Move the argument list to the first argument of the program
+ argv = &argv[args->exec_argv_offset];
+
+ // If a source PID is provided, try to copy the cookie from
+ // that PID. Otherwise, create a brand new cookie with the
+ // provided type.
+ if (args->from_pid) {
+ core_sched_pull_cookie(args->from_pid);
+ } else {
+ args->to_pid = getpid();
+ core_sched_create_cookie(args);
+ }
+
+ if (execvp(argv[0], argv)) {
+ errexec(argv[0]);
+ }
+}
+
+core_sched_type_t parse_core_sched_type(char *str)
+{
+ if (!strncmp(str, "pid\0", 4)) {
+ return PR_SCHED_CORE_SCOPE_THREAD;
+ } else if (!strncmp(str, "tgid\0", 5)) {
+ return PR_SCHED_CORE_SCOPE_THREAD_GROUP;
+ } else if (!strncmp(str, "pgid\0", 5)) {
+ return PR_SCHED_CORE_SCOPE_PROCESS_GROUP;
+ }
+
+ errx(EINVAL, "'%s' is an invalid option. Must be one of pid/tgid/pgid",
+ str);
+ __builtin_unreachable();
+}
+
+static void __attribute__((__noreturn__)) usage(void)
+{
+ fputs(USAGE_HEADER, stdout);
+ fprintf(stdout, _(" %s --get <PID>\n"), program_invocation_short_name);
+ fprintf(stdout, _(" %s --new <PID> [-t <TYPE>]\n"),
+ program_invocation_short_name);
+ fprintf(stdout, _(" %s --copy -s <PID> -d <PID> [-t <TYPE>]\n"),
+ program_invocation_short_name);
+ fprintf(stdout, _(" %s [-s <PID>] -- PROGRAM ARGS... \n"),
+ program_invocation_short_name);
+
+ fputs(USAGE_SEPARATOR, stdout);
+ fputsln(_("Manage core scheduling cookies for tasks."), stdout);
+
+ fputs(USAGE_FUNCTIONS, stdout);
+ fputsln(_(" -g, --get <PID> get the core scheduling cookie of a PID"),
+ stdout);
+ fputsln(_(" -n, --new <PID> assign a new core scheduling cookie to PID"),
+ stdout);
+ fputsln(_(" -c, --copy copy the core scheduling cookie from PID to\n"
+ " another PID, requires the --source and --dest option"),
+ stdout);
+
+ fputs(USAGE_OPTIONS, stdout);
+ fputsln(_(" -s, --source <PID> where to copy the core scheduling cookie from."),
+ stdout);
+ fputsln(_(" -d, --dest <PID> where to copy the core scheduling cookie to."),
+ stdout);
+ fputsln(_(" -t, --type type of the destination PID, or the type of\n"
+ " the PID when a new core scheduling cookie\n"
+ " is created. Can be one of the following:\n"
+ " pid, tgid or pgid. Defaults to tgid."),
+ stdout);
+ fputs(USAGE_SEPARATOR, stdout);
+ fprintf(stdout,
+ USAGE_HELP_OPTIONS(
+ 25)); /* char offset to align option descriptions */
+ fprintf(stdout, USAGE_MAN_TAIL("coresched(1)"));
+ exit(EXIT_SUCCESS);
+}
+
+bool verify_arguments(struct args *args)
+{
+ // Check if the value of args->cmd is a power of 2
+ // In that case, only a single function option was set.
+ if (args->cmd & (args->cmd - 1)) {
+ errx(EINVAL,
+ "Cannot do more than one function at a time. See %s --help",
+ program_invocation_short_name);
+ }
+
+ switch (args->cmd) {
+ case SCHED_CORE_CMD_GET:
+ if (args->to_pid) {
+ errx(EINVAL,
+ "Cannot use -d/--dest with this -g/--get. See %s --help",
+ program_invocation_short_name);
+ }
+ break;
+ case SCHED_CORE_CMD_CREATE:
+ if (args->from_pid) {
+ errx(EINVAL,
+ "Cannot use -s/--source with this -n/--new. See %s --help",
+ program_invocation_short_name);
+ }
+ break;
+ case SCHED_CORE_CMD_COPY:
+ if (!args->from_pid) {
+ errx(EINVAL,
+ "-s/--source PID is required when copying");
+ }
+ if (!args->to_pid) {
+ errx(EINVAL, "-d/--dest PID is required when copying");
+ }
+ break;
+ case SCHED_CORE_CMD_EXEC:
+ if (args->to_pid) {
+ errx(EINVAL,
+ "Cannot use -d/--dest when spawning a program. See %s --help",
+ program_invocation_short_name);
+ }
+ break;
+ }
+ return true;
+}
+
+void set_pid_or_err(pid_t *dest, pid_t src, const char *err_msg)
+{
+ if (*dest) {
+ errx(EINVAL, "Ambigious usage: %s", err_msg);
+ } else {
+ *dest = src;
+ }
+}
+
+static const char *ERR_MSG_MULTIPLE_SOURCE_PIDS =
+ "Multiple source PIDs defined";
+void parse_arguments(int argc, char **argv, struct args *args)
+{
+ int c;
+ pid_t tmp;
+
+ static const struct option longopts[] = {
+ { "get", required_argument, NULL, 'g' },
+ { "new", required_argument, NULL, 'n' },
+ { "copy", no_argument, NULL, 'c' },
+ { "source", required_argument, NULL, 's' },
+ { "destination", required_argument, NULL, 'd' },
+ { "type", required_argument, NULL, 't' },
+ { "version", no_argument, NULL, 'V' },
+ { "help", no_argument, NULL, 'h' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ while ((c = getopt_long(argc, argv, "g:n:cs:d:t:Vh", longopts, NULL)) !=
+ -1)
+ switch (c) {
+ case 'g':
+ args->cmd |= SCHED_CORE_CMD_GET;
+ tmp = strtopid_or_err(
+ optarg, "Failed to parse PID for -g/--get");
+ set_pid_or_err(&args->from_pid, tmp,
+ ERR_MSG_MULTIPLE_SOURCE_PIDS);
+ break;
+ case 'n':
+ args->cmd |= SCHED_CORE_CMD_CREATE;
+ tmp = strtopid_or_err(
+ optarg, "Failed to parse PID for -n/--new");
+ set_pid_or_err(&args->to_pid, tmp,
+ ERR_MSG_MULTIPLE_SOURCE_PIDS);
+ break;
+ case 'c':
+ args->cmd |= SCHED_CORE_CMD_COPY;
+ break;
+ case 's':
+ tmp = strtopid_or_err(
+ optarg, "Failed to parse PID for -s/--source");
+ set_pid_or_err(&args->from_pid, tmp,
+ ERR_MSG_MULTIPLE_SOURCE_PIDS);
+ break;
+ case 'd':
+ tmp = strtopid_or_err(
+ optarg, "Failed to parse PID for -d/--dest");
+ set_pid_or_err(&args->to_pid, tmp,
+ "Multiple destination PIDs defined");
+ break;
+ case 't':
+ args->type = parse_core_sched_type(optarg);
+ break;
+ case 'V':
+ print_version(EXIT_SUCCESS);
+ case 'h':
+ usage();
+ default:
+ errtryhelp(EXIT_FAILURE);
+ }
+
+ if (argc > optind) {
+ if (args->cmd == SCHED_CORE_CMD_EXEC) {
+ args->exec_argv_offset = optind;
+ } else {
+ // -g, -n or -c AND a program to run is provided
+ errx(EINVAL, "bad usage, see %s --help",
+ program_invocation_short_name);
+ }
+ } else if (argc == optind && args->from_pid) {
+ // Neither a function (-g, -n, or -c), nor a program to
+ // run is given
+ args->cmd = SCHED_CORE_CMD_GET;
+ }
+
+ verify_arguments(args);
+}
+
+int main(int argc, char **argv)
+{
+ struct args arguments = { 0 };
+ arguments.type = PR_SCHED_CORE_SCOPE_THREAD_GROUP;
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ close_stdout_atexit();
+
+ parse_arguments(argc, argv, &arguments);
+
+ unsigned long cookie = 0;
+ switch (arguments.cmd) {
+ case SCHED_CORE_CMD_GET:
+ cookie = core_sched_get_cookie(&arguments);
+ if (cookie) {
+ printf("core scheduling cookie of pid %d is 0x%lx\n",
+ arguments.from_pid, cookie);
+ } else {
+ printf("pid %d doesn't have a core scheduling cookie\n",
+ arguments.from_pid);
+ exit(1);
+ }
+ break;
+ case SCHED_CORE_CMD_CREATE:
+ core_sched_create_cookie(&arguments);
+ break;
+ case SCHED_CORE_CMD_COPY:
+ core_sched_copy_cookie(&arguments);
+ break;
+ case SCHED_CORE_CMD_EXEC:
+ core_sched_exec_with_cookie(&arguments, argv);
+ break;
+ default:
+ usage();
+ exit(1);
+ }
+}
--
2.44.0
^ permalink raw reply related
* [PATCH 0/1] coresched: Manage core scheduling cookies for tasks
From: Thijs Raymakers @ 2024-03-26 17:49 UTC (permalink / raw)
To: pauld; +Cc: kzak, util-linux, Thijs Raymakers
In-Reply-To: <20240326144129.GA315070@lorien.usersys.redhat.com>
Hi Phil,
Thank you for your comments. I've attached an interdiff to this email
with all the changes that I've made in response.
Phil Auld wrote on 26-03-2024 at 15:41:
> The exec should not fork first.
You're right, this is not necessary. I've removed it.
> And the error handling I think is just not right. I believe prctl()
> returns -1 on error and sets errno. This code will report EPERM for all
> prtcl errors. While EPERM is likely I don't think it's the only error.
Good catch! I assumed that the return values of the `prctl()` call
matched the return values of `sched_core_share_pid` in
`kernel/sched/core_sched.c`. I've updated the error handling code to
just use errno instead.
> On Sat, Mar 16, 2024 at 06:10:03PM +0100, Thijs Raymakers wrote:
>> - How should the program behave if the prctl core scheduling API is not
>> available? It has been in Linus' tree since november 2021
>> (commit a41b74451b35f7a6529689760eb8c05241feecbc) but it can be
>> disabled with CONFIG_SCHED_CORE=n
>
> This one is tricky. If I recall the error you get is ambiguous in this case
> (EINVAL, which can be returned for other reasons). It would be nice to be
> able to report that specifically.
In general, `prctl` does indeed return EINVAL if the operation is not
recognized, or not supported on the system. The `PR_SCHED_CORE`
operation itself only returns EINVAL if it is called with
- an invalid operation
- an invalid type
- a negative PID
- an invalid cookie store address (for PR_SCHED_CORE_GET)
Assuming that all these cases are prevented by the util, we could
interpret a EINVAL as a sign that PR_SCHED_CORE is not supported on
the system.
>> - I've currently licensed it under the EUPL v1.2, which is easier to
>> enforce in my jurisdiction than the GPL. It is GPL compatible so it
>> shouldn't be an issue, but if anybody has any remarks on this, please
>> let me know.
>
> The util-linux package is GPL so personally I think it would be good to just
> make any new addition to that package also GPL.
As far as I understand it, util-linux is a collection of different works
with different licenses (see the README.licensing file). Not every
program included in util-linux has to be GPL.
>> +typedef enum {
>> + SCHED_CORE_SCOPE_PID = PR_SCHED_CORE_SCOPE_THREAD,
>> + SCHED_CORE_SCOPE_TGID = PR_SCHED_CORE_SCOPE_THREAD_GROUP,
>> + SCHED_CORE_SCOPE_PGID = PR_SCHED_CORE_SCOPE_PROCESS_GROUP,
>> +} core_sched_type_t;
>> +
>
> Is this needed? You've made the names a little shorter I guess.
> Also some versions of prctl.h don't have the PR_SCHED_CORE_SCOPE* defines
> even when they have the base PR_SCHED_CORE macros.
I've changed to to just use the PR_SCHED_CORE_SCOPE* definitions
instead.
>> +void core_sched_create_cookie(struct args *args)
>> +{
>> + int prctl_errno = prctl(PR_SCHED_CORE, PR_SCHED_CORE_CREATE,
>> + args->from_pid, args->type, 0);
>
> We are creating a new cookie on from_pid, seems odd.
I agree, I changed to to `args->to_pid` because that is more logical.
>> + fprintf(stdout, _(" %s --get <PID>\n"), program_invocation_short_name);
>> + fprintf(stdout, _(" %s --new <PID> [-t <TYPE>]\n"),
>> + program_invocation_short_name);
>> + fprintf(stdout, _(" %s --copy -s <PID> -d <PID> [-t <TYPE>]\n"),
>> + program_invocation_short_name);
>> + fprintf(stdout, _(" %s --exec [-s <PID>] -- PROGRAM ARGS... \n"),
>> + program_invocation_short_name);
>
> There are 4 different arguments that take a pid when there are really
> only at most 2 needed.
I chose this to keep the amount of options that you have to provide as
small possible. I thought that `coresched -n 123` would be easier to use
than `coresched -n -p 123`. I guess the amount of different arguments
that can take PIDs can be reduced to two, but it would make the commands
a bit longer.
> "Get" could just be the default with no arguments given.
I've removed the explicit --exec and made it the default when a program
name is provided. If no program name is provided, it defaults to --get.
>> + enum {
>> + OPT_GET = 'g',
>> + OPT_NEW = 'n',
>> + OPT_COPY = 'c',
>> + OPT_EXEC = 'e',
>> + OPT_SRC = 's',
>> + OPT_DEST = 'd',
>> + OPT_TYPE = 't',
>> + OPT_VERSION = 'V',
>> + OPT_HELP = 'h'
>> + };
>
> This seems over-engineered. You have to specificy the characters
> in the getopt_long call anyway so this just complicates the code for
> no reason it seems to me.
Fair point, I've removed this. I initially added this to make it easier
to experiment a bit with different values for the options, but that is
no longer necessary.
Thanks!
- Thijs
Thijs Raymakers (1):
coresched: Manage core scheduling cookies for tasks
.gitignore | 1 +
bash-completion/coresched | 0
configure.ac | 12 +-
meson.build | 16 +-
meson_options.txt | 2 +-
schedutils/Makemodule.am | 8 +
schedutils/coresched.1.adoc | 16 ++
schedutils/coresched.c | 353 ++++++++++++++++++++++++++++++++++++
8 files changed, 402 insertions(+), 6 deletions(-)
create mode 100644 bash-completion/coresched
create mode 100644 schedutils/coresched.1.adoc
create mode 100644 schedutils/coresched.c
Interdiff:
diff --git a/schedutils/coresched.c b/schedutils/coresched.c
index 4be8f9fda..17d775f2d 100644
--- a/schedutils/coresched.c
+++ b/schedutils/coresched.c
@@ -18,18 +18,24 @@
#include "nls.h"
#include "strutils.h"
+// These definitions might not be defined, even if the
+// prctl interface accepts them.
+#ifndef PR_SCHED_CORE_SCOPE_THREAD
+#define PR_SCHED_CORE_SCOPE_THREAD 0
+#endif
+#ifndef PR_SCHED_CORE_SCOPE_THREAD_GROUP
+#define PR_SCHED_CORE_SCOPE_THREAD_GROUP 1
+#endif
+#ifndef PR_SCHED_CORE_SCOPE_PROCESS_GROUP
+#define PR_SCHED_CORE_SCOPE_PROCESS_GROUP 2
+#endif
+
+typedef int core_sched_type_t;
typedef enum {
- SCHED_CORE_SCOPE_PID = PR_SCHED_CORE_SCOPE_THREAD,
- SCHED_CORE_SCOPE_TGID = PR_SCHED_CORE_SCOPE_THREAD_GROUP,
- SCHED_CORE_SCOPE_PGID = PR_SCHED_CORE_SCOPE_PROCESS_GROUP,
-} core_sched_type_t;
-
-typedef enum {
- SCHED_CORE_CMD_NONE = 0,
+ SCHED_CORE_CMD_EXEC = 0,
SCHED_CORE_CMD_GET = 1,
SCHED_CORE_CMD_CREATE = 2,
SCHED_CORE_CMD_COPY = 4,
- SCHED_CORE_CMD_EXEC = 8,
} core_sched_cmd_t;
struct args {
@@ -50,44 +56,40 @@ void core_sched_exec_with_cookie(struct args *args, char **argv);
core_sched_type_t parse_core_sched_type(char *str);
bool verify_arguments(struct args *args);
void parse_arguments(int argc, char **argv, struct args *args);
+void set_pid_or_err(pid_t *dest, pid_t src, const char *err_msg);
+static void __attribute__((__noreturn__)) usage(void);
unsigned long core_sched_get_cookie(struct args *args)
{
unsigned long cookie = 0;
- int prctl_errno = prctl(PR_SCHED_CORE, PR_SCHED_CORE_GET,
- args->from_pid, SCHED_CORE_SCOPE_PID, &cookie);
- if (prctl_errno) {
- errx(-prctl_errno, "Failed to get cookie from PID %d",
- args->from_pid);
+ if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_GET, args->from_pid,
+ PR_SCHED_CORE_SCOPE_THREAD, &cookie)) {
+ err(errno, "Failed to get cookie from PID %d", args->from_pid);
}
return cookie;
}
void core_sched_create_cookie(struct args *args)
{
- int prctl_errno = prctl(PR_SCHED_CORE, PR_SCHED_CORE_CREATE,
- args->from_pid, args->type, 0);
- if (prctl_errno) {
- errx(-prctl_errno, "Failed to create cookie for PID %d",
- args->from_pid);
+ if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_CREATE, args->to_pid, args->type,
+ 0)) {
+ err(errno, "Failed to create cookie for PID %d",
+ args->from_pid);
}
}
void core_sched_pull_cookie(pid_t from)
{
- int prctl_errno = prctl(PR_SCHED_CORE, PR_SCHED_CORE_SHARE_FROM, from,
- SCHED_CORE_SCOPE_PID, 0);
- if (prctl_errno) {
- errx(-prctl_errno, "Failed to pull cookie from PID %d", from);
+ if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_SHARE_FROM, from,
+ PR_SCHED_CORE_SCOPE_THREAD, 0)) {
+ err(errno, "Failed to pull cookie from PID %d", from);
}
}
void core_sched_push_cookie(pid_t to, core_sched_type_t type)
{
- int prctl_errno =
- prctl(PR_SCHED_CORE, PR_SCHED_CORE_SHARE_TO, to, type, 0);
- if (prctl_errno) {
- errx(-prctl_errno, "Failed to push cookie to PID %d", to);
+ if (prctl(PR_SCHED_CORE, PR_SCHED_CORE_SHARE_TO, to, type, 0)) {
+ err(errno, "Failed to push cookie to PID %d", to);
}
}
@@ -100,46 +102,35 @@ void core_sched_copy_cookie(struct args *args)
void core_sched_exec_with_cookie(struct args *args, char **argv)
{
if (!args->exec_argv_offset) {
- errx(EINVAL, "when --exec is provided, a program name "
- "has to be given.");
+ usage();
}
// Move the argument list to the first argument of the program
argv = &argv[args->exec_argv_offset];
- pid_t pid = fork();
- if (pid == -1) {
- errx(errno, "Failed to spawn new process");
+ // If a source PID is provided, try to copy the cookie from
+ // that PID. Otherwise, create a brand new cookie with the
+ // provided type.
+ if (args->from_pid) {
+ core_sched_pull_cookie(args->from_pid);
+ } else {
+ args->to_pid = getpid();
+ core_sched_create_cookie(args);
}
- if (!pid) {
- // If a source PID is provided, try to copy the cookie from
- // that PID. Otherwise, create a brand new cookie with the
- // provided type.
- if (args->from_pid) {
- core_sched_pull_cookie(args->from_pid);
- } else {
- args->from_pid = getpid();
- core_sched_create_cookie(args);
- }
- if (execvp(argv[0], argv)) {
- errexec(argv[0]);
- }
- } else {
- int status = 0;
- waitpid(pid, &status, 0);
- exit(status);
+ if (execvp(argv[0], argv)) {
+ errexec(argv[0]);
}
}
core_sched_type_t parse_core_sched_type(char *str)
{
if (!strncmp(str, "pid\0", 4)) {
- return SCHED_CORE_SCOPE_PID;
+ return PR_SCHED_CORE_SCOPE_THREAD;
} else if (!strncmp(str, "tgid\0", 5)) {
- return SCHED_CORE_SCOPE_TGID;
+ return PR_SCHED_CORE_SCOPE_THREAD_GROUP;
} else if (!strncmp(str, "pgid\0", 5)) {
- return SCHED_CORE_SCOPE_PGID;
+ return PR_SCHED_CORE_SCOPE_PROCESS_GROUP;
}
errx(EINVAL, "'%s' is an invalid option. Must be one of pid/tgid/pgid",
@@ -155,7 +146,7 @@ static void __attribute__((__noreturn__)) usage(void)
program_invocation_short_name);
fprintf(stdout, _(" %s --copy -s <PID> -d <PID> [-t <TYPE>]\n"),
program_invocation_short_name);
- fprintf(stdout, _(" %s --exec [-s <PID>] -- PROGRAM ARGS... \n"),
+ fprintf(stdout, _(" %s [-s <PID>] -- PROGRAM ARGS... \n"),
program_invocation_short_name);
fputs(USAGE_SEPARATOR, stdout);
@@ -169,9 +160,6 @@ static void __attribute__((__noreturn__)) usage(void)
fputsln(_(" -c, --copy copy the core scheduling cookie from PID to\n"
" another PID, requires the --source and --dest option"),
stdout);
- fputsln(_(" -e, --exec execute a program with a new core scheduling\n"
- " cookie."),
- stdout);
fputs(USAGE_OPTIONS, stdout);
fputsln(_(" -s, --source <PID> where to copy the core scheduling cookie from."),
@@ -193,116 +181,141 @@ static void __attribute__((__noreturn__)) usage(void)
bool verify_arguments(struct args *args)
{
- if (args->cmd == SCHED_CORE_CMD_NONE) {
- usage();
- }
-
// Check if the value of args->cmd is a power of 2
// In that case, only a single function option was set.
- if (!(args->cmd && !(args->cmd & (args->cmd - 1)))) {
- errx(EINVAL, "Cannot do more than one function at a time.");
- }
-
- if (args->from_pid < 0) {
- errx(EINVAL, "source PID cannot be negative");
+ if (args->cmd & (args->cmd - 1)) {
+ errx(EINVAL,
+ "Cannot do more than one function at a time. See %s --help",
+ program_invocation_short_name);
}
- if (args->to_pid < 0) {
- errx(EINVAL, "destination PID cannot be negative");
- }
-
- if (args->from_pid == 0 && args->cmd == SCHED_CORE_CMD_COPY) {
- errx(EINVAL, "valid argument to --source is required");
- }
-
- if (args->to_pid == 0 && args->cmd == SCHED_CORE_CMD_COPY) {
- errx(EINVAL, "valid argument to --dest is required");
+ switch (args->cmd) {
+ case SCHED_CORE_CMD_GET:
+ if (args->to_pid) {
+ errx(EINVAL,
+ "Cannot use -d/--dest with this -g/--get. See %s --help",
+ program_invocation_short_name);
+ }
+ break;
+ case SCHED_CORE_CMD_CREATE:
+ if (args->from_pid) {
+ errx(EINVAL,
+ "Cannot use -s/--source with this -n/--new. See %s --help",
+ program_invocation_short_name);
+ }
+ break;
+ case SCHED_CORE_CMD_COPY:
+ if (!args->from_pid) {
+ errx(EINVAL,
+ "-s/--source PID is required when copying");
+ }
+ if (!args->to_pid) {
+ errx(EINVAL, "-d/--dest PID is required when copying");
+ }
+ break;
+ case SCHED_CORE_CMD_EXEC:
+ if (args->to_pid) {
+ errx(EINVAL,
+ "Cannot use -d/--dest when spawning a program. See %s --help",
+ program_invocation_short_name);
+ }
+ break;
}
+ return true;
+}
- if (args->from_pid == 0 && args->cmd != SCHED_CORE_CMD_EXEC) {
- errx(EINVAL, "PID cannot be zero");
+void set_pid_or_err(pid_t *dest, pid_t src, const char *err_msg)
+{
+ if (*dest) {
+ errx(EINVAL, "Ambigious usage: %s", err_msg);
+ } else {
+ *dest = src;
}
-
- return true;
}
+static const char *ERR_MSG_MULTIPLE_SOURCE_PIDS =
+ "Multiple source PIDs defined";
void parse_arguments(int argc, char **argv, struct args *args)
{
int c;
-
- enum {
- OPT_GET = 'g',
- OPT_NEW = 'n',
- OPT_COPY = 'c',
- OPT_EXEC = 'e',
- OPT_SRC = 's',
- OPT_DEST = 'd',
- OPT_TYPE = 't',
- OPT_VERSION = 'V',
- OPT_HELP = 'h'
- };
+ pid_t tmp;
static const struct option longopts[] = {
- { "get", required_argument, NULL, OPT_GET },
- { "new", required_argument, NULL, OPT_NEW },
- { "copy", no_argument, NULL, OPT_COPY },
- { "exec", no_argument, NULL, OPT_EXEC },
- { "source", required_argument, NULL, OPT_SRC },
- { "destination", required_argument, NULL, OPT_DEST },
- { "type", required_argument, NULL, OPT_TYPE },
- { "version", no_argument, NULL, OPT_VERSION },
- { "help", no_argument, NULL, OPT_HELP },
+ { "get", required_argument, NULL, 'g' },
+ { "new", required_argument, NULL, 'n' },
+ { "copy", no_argument, NULL, 'c' },
+ { "source", required_argument, NULL, 's' },
+ { "destination", required_argument, NULL, 'd' },
+ { "type", required_argument, NULL, 't' },
+ { "version", no_argument, NULL, 'V' },
+ { "help", no_argument, NULL, 'h' },
{ NULL, 0, NULL, 0 }
};
- while ((c = getopt_long(argc, argv, "g:n:ces:d:t:Vh", longopts,
- NULL)) != -1)
+ while ((c = getopt_long(argc, argv, "g:n:cs:d:t:Vh", longopts, NULL)) !=
+ -1)
switch (c) {
- case OPT_GET:
+ case 'g':
args->cmd |= SCHED_CORE_CMD_GET;
- args->from_pid = strtos32_or_err(
- optarg, "Failed to parse PID for --get");
+ tmp = strtopid_or_err(
+ optarg, "Failed to parse PID for -g/--get");
+ set_pid_or_err(&args->from_pid, tmp,
+ ERR_MSG_MULTIPLE_SOURCE_PIDS);
break;
- case OPT_NEW:
+ case 'n':
args->cmd |= SCHED_CORE_CMD_CREATE;
- args->from_pid = strtos32_or_err(
- optarg, "Failed to parse PID for --new");
+ tmp = strtopid_or_err(
+ optarg, "Failed to parse PID for -n/--new");
+ set_pid_or_err(&args->to_pid, tmp,
+ ERR_MSG_MULTIPLE_SOURCE_PIDS);
break;
- case OPT_COPY:
+ case 'c':
args->cmd |= SCHED_CORE_CMD_COPY;
break;
- case OPT_EXEC:
- args->cmd |= SCHED_CORE_CMD_EXEC;
- break;
- case OPT_SRC:
- args->from_pid = strtos32_or_err(
- optarg, "Failed to parse PID for --source");
+ case 's':
+ tmp = strtopid_or_err(
+ optarg, "Failed to parse PID for -s/--source");
+ set_pid_or_err(&args->from_pid, tmp,
+ ERR_MSG_MULTIPLE_SOURCE_PIDS);
break;
- case OPT_DEST:
- args->to_pid = strtos32_or_err(
- optarg, "Failed to parse PID for --dest");
+ case 'd':
+ tmp = strtopid_or_err(
+ optarg, "Failed to parse PID for -d/--dest");
+ set_pid_or_err(&args->to_pid, tmp,
+ "Multiple destination PIDs defined");
break;
- case OPT_TYPE:
+ case 't':
args->type = parse_core_sched_type(optarg);
break;
- case OPT_VERSION:
+ case 'V':
print_version(EXIT_SUCCESS);
- case OPT_HELP:
+ case 'h':
usage();
default:
errtryhelp(EXIT_FAILURE);
}
if (argc > optind) {
- args->exec_argv_offset = optind;
+ if (args->cmd == SCHED_CORE_CMD_EXEC) {
+ args->exec_argv_offset = optind;
+ } else {
+ // -g, -n or -c AND a program to run is provided
+ errx(EINVAL, "bad usage, see %s --help",
+ program_invocation_short_name);
+ }
+ } else if (argc == optind && args->from_pid) {
+ // Neither a function (-g, -n, or -c), nor a program to
+ // run is given
+ args->cmd = SCHED_CORE_CMD_GET;
}
+
verify_arguments(args);
}
int main(int argc, char **argv)
{
struct args arguments = { 0 };
- arguments.type = SCHED_CORE_SCOPE_TGID;
+ arguments.type = PR_SCHED_CORE_SCOPE_THREAD_GROUP;
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
--
2.44.0
^ permalink raw reply related
* Re: Mistakes in messages of util-linux-2.40-rc2
From: Petr Pisar @ 2024-03-26 16:35 UTC (permalink / raw)
To: util-linux; +Cc: util-linux
In-Reply-To: <257b46c7-dbe9-414e-a707-fc543d1e7849@telfort.nl>
[-- Attachment #1: Type: text/plain, Size: 1398 bytes --]
V Tue, Mar 26, 2024 at 04:48:09PM +0100, Benno Schulenberg napsal(a):
> >> #: misc-utils/lsblk.c:236
> >> msgid "write same max bytes, use <number> if --bytes is given"
> >
> > There are multiple messages ending with "use <number> if --bytes is given".
> >
> > I found the ", use" clause clumsy. Especially the reference to "<number>".
> > I know it refers to an argument of the --bytes option, but it can be difficult
> > for users to realize it. [...]
>
> If I read things correctly, the --bytes option takes no argument.
> So to me the msgid makes zero sense. :/
>
You are right.
> > I propose rephrasing it as "write-same max bytes, respect --bytes format".
> > Or "write same max bytes, respects --bytes format".
>
> What does "same" refer to?
Same bytes.
The column simply prints a content of "write_same_max_bytes" file from sysfs.
That file is documented in Linux as:
Some devices support a write same operation in which a
single data block can be written to a range of several
contiguous blocks on storage. This can be used to wipe areas on
disk or to initialize drives in a RAID configuration.
write_same_max_bytes indicates how many bytes can be written in
a single write same command. If write_same_max_bytes is 0, write
same is not supported by the device.
So write-same is a name of the operation.
-- Petr
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply
* [PATCH 2/2] lastlog2: begin descriptions of options with a lowercase letter
From: Benno Schulenberg @ 2024-03-26 15:48 UTC (permalink / raw)
To: util-linux
In-Reply-To: <20240326154822.8959-1-bensberg@telfort.nl>
These descriptions are not full sentences and do not end with a period,
so they should not start with a capital either.
---
misc-utils/lastlog2.c | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/misc-utils/lastlog2.c b/misc-utils/lastlog2.c
index 2b8365b40..7e511b036 100644
--- a/misc-utils/lastlog2.c
+++ b/misc-utils/lastlog2.c
@@ -108,15 +108,15 @@ static void __attribute__((__noreturn__)) usage(void)
fprintf(output, _(" %s [options]\n"), program_invocation_short_name);
fputs(USAGE_OPTIONS, output);
- fputs(_(" -b, --before DAYS Print only records older than DAYS\n"), output);
- fputs(_(" -C, --clear Clear record of a user (requires -u)\n"), output);
- fputs(_(" -d, --database FILE Use FILE as lastlog2 database\n"), output);
- fputs(_(" -i, --import FILE Import data from old lastlog file\n"), output);
- fputs(_(" -r, --rename NEWNAME Rename existing user to NEWNAME (requires -u)\n"), output);
- fputs(_(" -s, --service Display PAM service\n"), output);
- fputs(_(" -S, --set Set lastlog record to current time (requires -u)\n"), output);
- fputs(_(" -t, --time DAYS Print only lastlog records more recent than DAYS\n"), output);
- fputs(_(" -u, --user LOGIN Print lastlog record of the specified LOGIN\n"), output);
+ fputs(_(" -b, --before DAYS print only records older than DAYS\n"), output);
+ fputs(_(" -C, --clear clear record of a user (requires -u)\n"), output);
+ fputs(_(" -d, --database FILE use FILE as lastlog2 database\n"), output);
+ fputs(_(" -i, --import FILE import data from old lastlog file\n"), output);
+ fputs(_(" -r, --rename NEWNAME rename existing user to NEWNAME (requires -u)\n"), output);
+ fputs(_(" -s, --service display PAM service\n"), output);
+ fputs(_(" -S, --set set lastlog record to current time (requires -u)\n"), output);
+ fputs(_(" -t, --time DAYS print only lastlog records more recent than DAYS\n"), output);
+ fputs(_(" -u, --user LOGIN print lastlog record of the specified LOGIN\n"), output);
fputs(USAGE_SEPARATOR, output);
fprintf(output, USAGE_HELP_OPTIONS(25));
--
2.42.1
^ permalink raw reply related
* [PATCH 1/2] textual: fix some typos and inconsistencies in usage and error messages
From: Benno Schulenberg @ 2024-03-26 15:48 UTC (permalink / raw)
To: util-linux; +Cc: Petr Pisar
Reported-by: Petr Pisar <petr.pisar@atlas.cz>
Signed-off-by: Benno Schulenberg <bensberg@telfort.nl>
---
login-utils/lslogins.c | 2 +-
misc-utils/lastlog2.c | 2 +-
misc-utils/lsfd.c | 2 +-
misc-utils/lslocks.c | 2 +-
misc-utils/waitpid.c | 2 +-
sys-utils/irqtop.c | 4 ++--
sys-utils/setpriv.c | 2 +-
term-utils/agetty.c | 4 ++--
8 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/login-utils/lslogins.c b/login-utils/lslogins.c
index c6958a464..d13d3d720 100644
--- a/login-utils/lslogins.c
+++ b/login-utils/lslogins.c
@@ -1527,7 +1527,7 @@ static void __attribute__((__noreturn__)) usage(void)
fputs(_(" --notruncate don't truncate output\n"), out);
fputs(_(" -o, --output[=<list>] define the columns to output\n"), out);
fputs(_(" --output-all output all columns\n"), out);
- fputs(_(" -p, --pwd display information related to login by password.\n"), out);
+ fputs(_(" -p, --pwd display information related to login by password\n"), out);
fputs(_(" -r, --raw display in raw mode\n"), out);
fputs(_(" -s, --system-accs display system accounts\n"), out);
fputs(_(" --time-format=<type> display dates in short, full or iso format\n"), out);
diff --git a/misc-utils/lastlog2.c b/misc-utils/lastlog2.c
index 4029f5e48..2b8365b40 100644
--- a/misc-utils/lastlog2.c
+++ b/misc-utils/lastlog2.c
@@ -114,7 +114,7 @@ static void __attribute__((__noreturn__)) usage(void)
fputs(_(" -i, --import FILE Import data from old lastlog file\n"), output);
fputs(_(" -r, --rename NEWNAME Rename existing user to NEWNAME (requires -u)\n"), output);
fputs(_(" -s, --service Display PAM service\n"), output);
- fputs(_(" -S, --set ySet lastlog record to current time (requires -u)\n"), output);
+ fputs(_(" -S, --set Set lastlog record to current time (requires -u)\n"), output);
fputs(_(" -t, --time DAYS Print only lastlog records more recent than DAYS\n"), output);
fputs(_(" -u, --user LOGIN Print lastlog record of the specified LOGIN\n"), output);
diff --git a/misc-utils/lsfd.c b/misc-utils/lsfd.c
index d4d2a99bc..41d0dd282 100644
--- a/misc-utils/lsfd.c
+++ b/misc-utils/lsfd.c
@@ -422,7 +422,7 @@ static const struct colinfo infos[] = {
N_("user of the process") },
[COL_XMODE] = { "XMODE",
0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
- N_("extended version of MDOE (rwxD[Ll]m)") },
+ N_("extended version of MODE (rwxD[Ll]m)") },
};
static const int default_columns[] = {
diff --git a/misc-utils/lslocks.c b/misc-utils/lslocks.c
index 3d70b047f..946f724e6 100644
--- a/misc-utils/lslocks.c
+++ b/misc-utils/lslocks.c
@@ -89,7 +89,7 @@ static struct colinfo infos[] = {
[COL_END] = { "END", 10, SCOLS_FL_RIGHT, N_("ending offset of the lock")},
[COL_PATH] = { "PATH", 0, SCOLS_FL_TRUNC, N_("path of the locked file")},
[COL_BLOCKER] = { "BLOCKER", 0, SCOLS_FL_RIGHT, N_("PID of the process blocking the lock") },
- [COL_HOLDERS] = { "HOLDERS", 0, SCOLS_FL_WRAP, N_("HOLDERS of the lock") },
+ [COL_HOLDERS] = { "HOLDERS", 0, SCOLS_FL_WRAP, N_("holders of the lock") },
};
static int columns[ARRAY_SIZE(infos) * 2];
diff --git a/misc-utils/waitpid.c b/misc-utils/waitpid.c
index 0a4e03d16..4d77e1df4 100644
--- a/misc-utils/waitpid.c
+++ b/misc-utils/waitpid.c
@@ -236,7 +236,7 @@ int main(int argc, char **argv)
if (count && count > n_pids)
errx(EXIT_FAILURE,
- _("can't want for %zu of %zu PIDs"), count, n_pids);
+ _("can't wait for %zu of %zu PIDs"), count, n_pids);
pid_t *pids = parse_pids(argc - pid_idx, argv + pid_idx);
diff --git a/sys-utils/irqtop.c b/sys-utils/irqtop.c
index 7baa47baf..11a11fb50 100644
--- a/sys-utils/irqtop.c
+++ b/sys-utils/irqtop.c
@@ -177,7 +177,7 @@ static int event_loop(struct irqtop_ctl *ctl, struct irq_output *out)
efd = epoll_create1(0);
if ((tfd = timerfd_create(CLOCK_MONOTONIC, 0)) < 0)
- err(EXIT_FAILURE, _("cannot not create timerfd"));
+ err(EXIT_FAILURE, _("cannot create timerfd"));
if (timerfd_settime(tfd, 0, &ctl->timer, NULL) != 0)
err(EXIT_FAILURE, _("cannot set timerfd"));
@@ -197,7 +197,7 @@ static int event_loop(struct irqtop_ctl *ctl, struct irq_output *out)
sigaddset(&sigmask, SIGQUIT);
if ((sfd = signalfd(-1, &sigmask, SFD_CLOEXEC)) < 0)
- err(EXIT_FAILURE, _("cannot not create signalfd"));
+ err(EXIT_FAILURE, _("cannot create signalfd"));
ev.events = EPOLLIN;
ev.data.fd = sfd;
diff --git a/sys-utils/setpriv.c b/sys-utils/setpriv.c
index 890fa2427..4b0543101 100644
--- a/sys-utils/setpriv.c
+++ b/sys-utils/setpriv.c
@@ -135,7 +135,7 @@ static void __attribute__((__noreturn__)) usage(void)
fputs(_(" --bounding-set <caps> set capability bounding set\n"), out);
fputs(_(" --ruid <uid|user> set real uid\n"), out);
fputs(_(" --euid <uid|user> set effective uid\n"), out);
- fputs(_(" --rgid <gid|user> set real gid\n"), out);
+ fputs(_(" --rgid <gid|group> set real gid\n"), out);
fputs(_(" --egid <gid|group> set effective gid\n"), out);
fputs(_(" --reuid <uid|user> set real and effective uid\n"), out);
fputs(_(" --regid <gid|group> set real and effective gid\n"), out);
diff --git a/term-utils/agetty.c b/term-utils/agetty.c
index 0fc6f1514..4532e7454 100644
--- a/term-utils/agetty.c
+++ b/term-utils/agetty.c
@@ -2463,12 +2463,12 @@ static void __attribute__((__noreturn__)) usage(void)
fputs(_(" -H, --host <hostname> specify login host\n"), out);
fputs(_(" -i, --noissue do not display issue file\n"), out);
fputs(_(" -I, --init-string <string> set init string\n"), out);
- fputs(_(" -J --noclear do not clear the screen before prompt\n"), out);
+ fputs(_(" -J, --noclear do not clear the screen before prompt\n"), out);
fputs(_(" -l, --login-program <file> specify login program\n"), out);
fputs(_(" -L, --local-line[=<mode>] control the local line flag\n"), out);
fputs(_(" -m, --extract-baud extract baud rate during connect\n"), out);
fputs(_(" -n, --skip-login do not prompt for login\n"), out);
- fputs(_(" -N --nonewline do not print a newline before issue\n"), out);
+ fputs(_(" -N, --nonewline do not print a newline before issue\n"), out);
fputs(_(" -o, --login-options <opts> options that are passed to login\n"), out);
fputs(_(" -p, --login-pause wait for any key before the login\n"), out);
fputs(_(" -r, --chroot <dir> change root to the directory\n"), out);
--
2.42.1
^ permalink raw reply related
* Re: Mistakes in messages of util-linux-2.40-rc2
From: Benno Schulenberg @ 2024-03-26 15:48 UTC (permalink / raw)
To: util-linux; +Cc: Petr Pisar
In-Reply-To: <ZgKxUSbGN-R8Rn-w@album.bayer.uni.cx>
[-- Attachment #1.1: Type: text/plain, Size: 1526 bytes --]
Op 26-03-2024 om 12:28 schreef Petr Pisar:
> while translating util-linux-2.40-rc2 I notice a few messages which are, in my
> opinion, incorrect: [ ...]
A patch for most of these issues is coming up.
Karel, would there be time for an rc3? It wouldn't need to delay
things much: giving translators three or four days to update their
translations for these small changes should be enough.
>> #: misc-utils/lsblk.c:236
>> msgid "write same max bytes, use <number> if --bytes is given"
>
> There are multiple messages ending with "use <number> if --bytes is given".
>
> I found the ", use" clause clumsy. Especially the reference to "<number>".
> I know it refers to an argument of the --bytes option, but it can be difficult
> for users to realize it. [...]
If I read things correctly, the --bytes option takes no argument.
So to me the msgid makes zero sense. :/
> I propose rephrasing it as "write-same max bytes, respect --bytes format".
> Or "write same max bytes, respects --bytes format".
What does "same" refer to? "respects the --bytes option" would be a
good wording.
(As I don't understand what these descriptions are saying, I have not
touched them in my patch.)
>> #: sys-utils/chcpu.c:208
>> #, c-format
>> msgid "CPU %u configure failed"
>
> "configure" should be "configuration".
Hm. I think that this "configure" (and "deconfigure" further down)
refers to the --configure (and --deconfigure) "command". So my patch
doesn't change those.
Benno
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 840 bytes --]
^ permalink raw reply
* Re: [PATCH RFC] coresched: Manage core scheduling cookies for tasks
From: Phil Auld @ 2024-03-26 14:45 UTC (permalink / raw)
To: Thijs Raymakers; +Cc: Karel Zak, util-linux
In-Reply-To: <20240326144129.GA315070@lorien.usersys.redhat.com>
On Tue, Mar 26, 2024 at 10:41:50AM -0400 Phil Auld wrote:
> On Sun, Mar 24, 2024 at 09:07:45PM +0100 Karel Zak wrote:
> > On Sat, Mar 16, 2024 at 06:10:03PM +0100, Thijs Raymakers wrote:
> > > I'm looking for any comments on coresched, a program that allows you to
> > > manage core scheduling cookies for tasks.
> >
> > https://github.com/util-linux/util-linux/pull/2839
> >
> > Guys, you should sync your work and merge it together :-)
> >
> > CC: Phil Auld
> >
>
> After looking at this some more I'd like to try to add your
> pull/push use case to my code instead.
>
I should add that I like your short words for the scope and
may also use those...
Cheers,
Phil
> There are some issues I'm having with the arguments in this one.
> I think it's a little counter-intuitive.
>
> The exec should not fork first.
>
> And the error handling I think is just not right. I believe prctl()
> returns -1 on error and sets errno. This code will report EPERM for all
> prtcl errors. While EPERM is likely I don't think it's the only error.
>
> I put a few other comments inline as well.
>
>
> Cheers,
> Phil
>
> > Karel
> >
> > >
> > > === What is Core Scheduling ===
> > > Core Scheduling can be used to ensure that certain tasks will never be
> > > scheduled on the same physical core. This can be a useful, alternative,
> > > mitigation to hardware vulnerabilities like L1tf or MDS.
> > > The full software mitigation for these vulnerabilities would be to disable
> > > SMT/Hyper-Threading. However, this can be prohibitively expensive and
> > > therefore often not done in practice.
> > > With Core Scheduling you can mitigate in these issues in some scenarios,
> > > while keeping SMT enabled.
> > >
> > > Core Scheduling works by adding a random "cookie" to a process. Only
> > > processes with the same core scheduling cookie are allowed to run on
> > > sibling cores. Tasks that trust each other can be given the same
> > > cookie and untrusted tasks are given a different cookie.
> > > This is important when running VMs that don't trust each other, as
> > > it prevents a guest VM to leak data from another guest VM with L1tf or MDS.
> > >
> > > === Motivation ===
> > > The kernel exposes a prctl uapi to manage core scheduling cookies (see
> > > https://www.kernel.org/doc/html/latest/admin-guide/hw-vuln/core-scheduling.html)
> > > Last week, I wanted to use core scheduling on some programs. Adding the
> > > prctl calls and recompiling felt a bit inconvenient, so I looked for a
> > > program like taskset that could do the job without having to modify the
> > > target program. I couldn't find any, and so I wrote a small program that
> > > does this. Hopefully it saves the next person some time :)
> > >
> > > === RFC ===
> > > I'm looking forward to any comments that you might have on the patch!
> > > Please note that I haven't written the manpage and the bash completion
> > > script yet. I first wanted to get some feedback on the program before I
> > > start documenting it in more detail.
> > >
> > > I'm particularly curious about your thoughts on the following things:
> > >
> > > - General comments about interacting with the program: Do the options
> > > make sense? Are there any necessary functions missing? Are the error
> > > messages helpful? Is the output too verbose/not verbose enough?
> > > - How should the program behave if the prctl core scheduling API is not
> > > available? It has been in Linus' tree since november 2021
> > > (commit a41b74451b35f7a6529689760eb8c05241feecbc) but it can be
> > > disabled with CONFIG_SCHED_CORE=n
>
>
> This one is tricky. If I recall the error you get is ambiguous in this case
> (EINVAL, which can be returned for other reasons). It would be nice to be
> able to report that specifically.
>
>
> > > - Most of the options require the user to have the CAP_SYS_PTRACE
> > > capability. Should the program notify the user that the capability
> > > is missing when the prctl call returns -EPREM, or does a mention in the
> > > man page suffice?
>
> Reporting the correct error message should be enough.
>
>
> > > - I've currently licensed it under the EUPL v1.2, which is easier to
> > > enforce in my jurisdiction than the GPL. It is GPL compatible so it
> > > shouldn't be an issue, but if anybody has any remarks on this, please
> > > let me know.
>
> The util-linux package is GPL so personally I think it would be good to just
> make any new addition to that package also GPL.
>
>
> > >
> > > Thanks for taking the time!
> > >
> > > Best regards,
> > > Thijs Raymakers
> > >
> > >
> > > Signed-off-by: Thijs Raymakers <thijs@raymakers.nl>
> > >
> > > ---
> > > .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 | 340 ++++++++++++++++++++++++++++++++++++
> > > 8 files changed, 389 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 f7baab7a2..8244c43a9 100644
> > > --- a/meson.build
> > > +++ b/meson.build
> > > @@ -3107,13 +3107,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..4be8f9fda
> > > --- /dev/null
> > > +++ b/schedutils/coresched.c
> > > @@ -0,0 +1,340 @@
> > > +/**
> > > + * SPDX-License-Identifier: EUPL-1.2
> > > + *
> > > + * coresched.c - manage core scheduling cookies for tasks
> > > + *
> > > + * Copyright (C) 2024 Thijs Raymakers
> > > + * Licensed under the EUPL v1.2
> > > + */
> > > +
> > > +#include <getopt.h>
> > > +#include <stdbool.h>
> > > +#include <stdio.h>
> > > +#include <sys/prctl.h>
> > > +#include <sys/wait.h>
> > > +
> > > +#include "c.h"
> > > +#include "closestream.h"
> > > +#include "nls.h"
> > > +#include "strutils.h"
> > > +
> > > +typedef enum {
> > > + SCHED_CORE_SCOPE_PID = PR_SCHED_CORE_SCOPE_THREAD,
> > > + SCHED_CORE_SCOPE_TGID = PR_SCHED_CORE_SCOPE_THREAD_GROUP,
> > > + SCHED_CORE_SCOPE_PGID = PR_SCHED_CORE_SCOPE_PROCESS_GROUP,
> > > +} core_sched_type_t;
> > > +
>
> Is this needed? You've made the names a little shorter I guess.
> Also some versions of prctl.h don't have the PR_SCHED_CORE_SCOPE* defines
> even when they have the base PR_SCHED_CORE macros.
>
>
> > > +typedef enum {
> > > + SCHED_CORE_CMD_NONE = 0,
> > > + SCHED_CORE_CMD_GET = 1,
> > > + SCHED_CORE_CMD_CREATE = 2,
> > > + SCHED_CORE_CMD_COPY = 4,
> > > + SCHED_CORE_CMD_EXEC = 8,
> > > +} core_sched_cmd_t;
> > > +
> > > +struct args {
> > > + pid_t from_pid;
> > > + pid_t to_pid;
> > > + core_sched_type_t type;
> > > + core_sched_cmd_t cmd;
> > > + int exec_argv_offset;
> > > +};
> > > +
> > > +unsigned long core_sched_get_cookie(struct args *args);
> > > +void core_sched_create_cookie(struct args *args);
> > > +void core_sched_pull_cookie(pid_t from);
> > > +void core_sched_push_cookie(pid_t to, core_sched_type_t type);
> > > +void core_sched_copy_cookie(struct args *args);
> > > +void core_sched_exec_with_cookie(struct args *args, char **argv);
> > > +
> > > +core_sched_type_t parse_core_sched_type(char *str);
> > > +bool verify_arguments(struct args *args);
> > > +void parse_arguments(int argc, char **argv, struct args *args);
> > > +
> > > +unsigned long core_sched_get_cookie(struct args *args)
> > > +{
> > > + unsigned long cookie = 0;
> > > + int prctl_errno = prctl(PR_SCHED_CORE, PR_SCHED_CORE_GET,
> > > + args->from_pid, SCHED_CORE_SCOPE_PID, &cookie);
> > > + if (prctl_errno) {
> > > + errx(-prctl_errno, "Failed to get cookie from PID %d",
> > > + args->from_pid);
> > > + }
>
> prctl_errno will be 0 on success or -1 on failure. This will report EPERM for
> every error.
>
>
> > > + return cookie;
> > > +}
> > > +
> > > +void core_sched_create_cookie(struct args *args)
> > > +{
> > > + int prctl_errno = prctl(PR_SCHED_CORE, PR_SCHED_CORE_CREATE,
> > > + args->from_pid, args->type, 0);
>
> We are creating a new cookie on from_pid, seems odd.
>
>
> > > + if (prctl_errno) {
> > > + errx(-prctl_errno, "Failed to create cookie for PID %d",
> > > + args->from_pid);
> > > + }
> > > +}
> > > +
> > > +void core_sched_pull_cookie(pid_t from)
> > > +{
> > > + int prctl_errno = prctl(PR_SCHED_CORE, PR_SCHED_CORE_SHARE_FROM, from,
> > > + SCHED_CORE_SCOPE_PID, 0);
> > > + if (prctl_errno) {
> > > + errx(-prctl_errno, "Failed to pull cookie from PID %d", from);
> > > + }
> > > +}
> > > +
> > > +void core_sched_push_cookie(pid_t to, core_sched_type_t type)
> > > +{
> > > + int prctl_errno =
> > > + prctl(PR_SCHED_CORE, PR_SCHED_CORE_SHARE_TO, to, type, 0);
> > > + if (prctl_errno) {
> > > + errx(-prctl_errno, "Failed to push cookie to PID %d", to);
> > > + }
> > > +}
> > > +
> > > +void core_sched_copy_cookie(struct args *args)
> > > +{
> > > + core_sched_pull_cookie(args->from_pid);
> > > + core_sched_push_cookie(args->to_pid, args->type);
> > > +}
> > > +
> > > +void core_sched_exec_with_cookie(struct args *args, char **argv)
> > > +{
> > > + if (!args->exec_argv_offset) {
> > > + errx(EINVAL, "when --exec is provided, a program name "
> > > + "has to be given.");
> > > + }
> > > +
> > > + // Move the argument list to the first argument of the program
> > > + argv = &argv[args->exec_argv_offset];
> > > +
> > > + pid_t pid = fork();
> > > + if (pid == -1) {
> > > + errx(errno, "Failed to spawn new process");
> > > + }
>
> Don't fork here. Just exec to the new program, as the other wrapper
> programs like taskset do.
>
>
> > > +
> > > + if (!pid) {
> > > + // If a source PID is provided, try to copy the cookie from
> > > + // that PID. Otherwise, create a brand new cookie with the
> > > + // provided type.
> > > + if (args->from_pid) {
> > > + core_sched_pull_cookie(args->from_pid);
> > > + } else {
> > > + args->from_pid = getpid();
> > > + core_sched_create_cookie(args);
> > > + }
> > > + if (execvp(argv[0], argv)) {
> > > + errexec(argv[0]);
> > > + }
> > > + } else {
> > > + int status = 0;
> > > + waitpid(pid, &status, 0);
> > > + exit(status);
> > > + }
> > > +}
> > > +
> > > +core_sched_type_t parse_core_sched_type(char *str)
> > > +{
> > > + if (!strncmp(str, "pid\0", 4)) {
> > > + return SCHED_CORE_SCOPE_PID;
> > > + } else if (!strncmp(str, "tgid\0", 5)) {
> > > + return SCHED_CORE_SCOPE_TGID;
> > > + } else if (!strncmp(str, "pgid\0", 5)) {
> > > + return SCHED_CORE_SCOPE_PGID;
> > > + }
> > > +
> > > + errx(EINVAL, "'%s' is an invalid option. Must be one of pid/tgid/pgid",
> > > + str);
> > > + __builtin_unreachable();
> > > +}
> > > +
> > > +static void __attribute__((__noreturn__)) usage(void)
> > > +{
> > > + fputs(USAGE_HEADER, stdout);
> > > + fprintf(stdout, _(" %s --get <PID>\n"), program_invocation_short_name);
> > > + fprintf(stdout, _(" %s --new <PID> [-t <TYPE>]\n"),
> > > + program_invocation_short_name);
> > > + fprintf(stdout, _(" %s --copy -s <PID> -d <PID> [-t <TYPE>]\n"),
> > > + program_invocation_short_name);
> > > + fprintf(stdout, _(" %s --exec [-s <PID>] -- PROGRAM ARGS... \n"),
> > > + program_invocation_short_name);
>
> There are 4 different arguments that take a pid when there are really
> only at most 2 needed.
>
> "Get" could just be the default with no arguments given.
>
>
> > > +
> > > + fputs(USAGE_SEPARATOR, stdout);
> > > + fputsln(_("Manage core scheduling cookies for tasks."), stdout);
> > > +
> > > + fputs(USAGE_FUNCTIONS, stdout);
> > > + fputsln(_(" -g, --get <PID> get the core scheduling cookie of a PID"),
> > > + stdout);
> > > + fputsln(_(" -n, --new <PID> assign a new core scheduling cookie to PID"),
> > > + stdout);
> > > + fputsln(_(" -c, --copy copy the core scheduling cookie from PID to\n"
> > > + " another PID, requires the --source and --dest option"),
> > > + stdout);
> > > + fputsln(_(" -e, --exec execute a program with a new core scheduling\n"
> > > + " cookie."),
> > > + stdout);
> > > +
> > > + fputs(USAGE_OPTIONS, stdout);
> > > + fputsln(_(" -s, --source <PID> where to copy the core scheduling cookie from."),
> > > + stdout);
> > > + fputsln(_(" -d, --dest <PID> where to copy the core scheduling cookie to."),
> > > + stdout);
> > > + fputsln(_(" -t, --type type of the destination PID, or the type of\n"
> > > + " the PID when a new core scheduling cookie\n"
> > > + " is created. Can be one of the following:\n"
> > > + " pid, tgid or pgid. Defaults to tgid."),
> > > + stdout);
> > > + fputs(USAGE_SEPARATOR, stdout);
> > > + fprintf(stdout,
> > > + USAGE_HELP_OPTIONS(
> > > + 25)); /* char offset to align option descriptions */
> > > + fprintf(stdout, USAGE_MAN_TAIL("coresched(1)"));
> > > + exit(EXIT_SUCCESS);
> > > +}
> > > +
> > > +bool verify_arguments(struct args *args)
> > > +{
> > > + if (args->cmd == SCHED_CORE_CMD_NONE) {
> > > + usage();
> > > + }
> > > +
> > > + // Check if the value of args->cmd is a power of 2
> > > + // In that case, only a single function option was set.
> > > + if (!(args->cmd && !(args->cmd & (args->cmd - 1)))) {
> > > + errx(EINVAL, "Cannot do more than one function at a time.");
> > > + }
> > > +
> > > + if (args->from_pid < 0) {
> > > + errx(EINVAL, "source PID cannot be negative");
> > > + }
> > > +
> > > + if (args->to_pid < 0) {
> > > + errx(EINVAL, "destination PID cannot be negative");
> > > + }
> > > +
> > > + if (args->from_pid == 0 && args->cmd == SCHED_CORE_CMD_COPY) {
> > > + errx(EINVAL, "valid argument to --source is required");
> > > + }
> > > +
> > > + if (args->to_pid == 0 && args->cmd == SCHED_CORE_CMD_COPY) {
> > > + errx(EINVAL, "valid argument to --dest is required");
> > > + }
> > > +
> > > + if (args->from_pid == 0 && args->cmd != SCHED_CORE_CMD_EXEC) {
> > > + errx(EINVAL, "PID cannot be zero");
> > > + }
> > > +
> > > + return true;
> > > +}
> > > +
> > > +void parse_arguments(int argc, char **argv, struct args *args)
> > > +{
> > > + int c;
> > > +
> > > + enum {
> > > + OPT_GET = 'g',
> > > + OPT_NEW = 'n',
> > > + OPT_COPY = 'c',
> > > + OPT_EXEC = 'e',
> > > + OPT_SRC = 's',
> > > + OPT_DEST = 'd',
> > > + OPT_TYPE = 't',
> > > + OPT_VERSION = 'V',
> > > + OPT_HELP = 'h'
> > > + };
>
> This seems over-engineered. You have to specificy the characters
> in the getopt_long call anyway so this just complicates the code for
> no reason it seems to me.
>
> > > +
> > > + static const struct option longopts[] = {
> > > + { "get", required_argument, NULL, OPT_GET },
> > > + { "new", required_argument, NULL, OPT_NEW },
> > > + { "copy", no_argument, NULL, OPT_COPY },
> > > + { "exec", no_argument, NULL, OPT_EXEC },
> > > + { "source", required_argument, NULL, OPT_SRC },
> > > + { "destination", required_argument, NULL, OPT_DEST },
> > > + { "type", required_argument, NULL, OPT_TYPE },
> > > + { "version", no_argument, NULL, OPT_VERSION },
> > > + { "help", no_argument, NULL, OPT_HELP },
> > > + { NULL, 0, NULL, 0 }
> > > + };
> > > +
> > > + while ((c = getopt_long(argc, argv, "g:n:ces:d:t:Vh", longopts,
> > > + NULL)) != -1)
> > > + switch (c) {
> > > + case OPT_GET:
> > > + args->cmd |= SCHED_CORE_CMD_GET;
> > > + args->from_pid = strtos32_or_err(
> > > + optarg, "Failed to parse PID for --get");
> > > + break;
> > > + case OPT_NEW:
> > > + args->cmd |= SCHED_CORE_CMD_CREATE;
> > > + args->from_pid = strtos32_or_err(
> > > + optarg, "Failed to parse PID for --new");
> > > + break;
> > > + case OPT_COPY:
> > > + args->cmd |= SCHED_CORE_CMD_COPY;
> > > + break;
> > > + case OPT_EXEC:
> > > + args->cmd |= SCHED_CORE_CMD_EXEC;
> > > + break;
> > > + case OPT_SRC:
> > > + args->from_pid = strtos32_or_err(
> > > + optarg, "Failed to parse PID for --source");
> > > + break;
> > > + case OPT_DEST:
> > > + args->to_pid = strtos32_or_err(
> > > + optarg, "Failed to parse PID for --dest");
> > > + break;
> > > + case OPT_TYPE:
> > > + args->type = parse_core_sched_type(optarg);
> > > + break;
> > > + case OPT_VERSION:
> > > + print_version(EXIT_SUCCESS);
> > > + case OPT_HELP:
> > > + usage();
> > > + default:
> > > + errtryhelp(EXIT_FAILURE);
> > > + }
> > > +
> > > + if (argc > optind) {
> > > + args->exec_argv_offset = optind;
> > > + }
> > > + verify_arguments(args);
> > > +}
> > > +
> > > +int main(int argc, char **argv)
> > > +{
> > > + struct args arguments = { 0 };
> > > + arguments.type = SCHED_CORE_SCOPE_TGID;
> > > +
> > > + setlocale(LC_ALL, "");
> > > + bindtextdomain(PACKAGE, LOCALEDIR);
> > > + textdomain(PACKAGE);
> > > + close_stdout_atexit();
> > > +
> > > + parse_arguments(argc, argv, &arguments);
> > > +
> > > + unsigned long cookie = 0;
> > > + switch (arguments.cmd) {
> > > + case SCHED_CORE_CMD_GET:
> > > + cookie = core_sched_get_cookie(&arguments);
> > > + if (cookie) {
> > > + printf("core scheduling cookie of pid %d is 0x%lx\n",
> > > + arguments.from_pid, cookie);
> > > + } else {
> > > + printf("pid %d doesn't have a core scheduling cookie\n",
> > > + arguments.from_pid);
> > > + exit(1);
> > > + }
> > > + break;
> > > + case SCHED_CORE_CMD_CREATE:
> > > + core_sched_create_cookie(&arguments);
> > > + break;
> > > + case SCHED_CORE_CMD_COPY:
> > > + core_sched_copy_cookie(&arguments);
> > > + break;
> > > + case SCHED_CORE_CMD_EXEC:
> > > + core_sched_exec_with_cookie(&arguments, argv);
> > > + break;
> > > + default:
> > > + usage();
> > > + exit(1);
> > > + }
> > > +}
> > > --
> > > 2.44.0
> > >
> > >
> >
> > --
> > Karel Zak <kzak@redhat.com>
> > http://karelzak.blogspot.com
> >
>
> --
--
^ permalink raw reply
* Re: [PATCH RFC] coresched: Manage core scheduling cookies for tasks
From: Phil Auld @ 2024-03-26 14:41 UTC (permalink / raw)
To: Thijs Raymakers; +Cc: Karel Zak, util-linux
In-Reply-To: <20240324200745.yn5krdvi7brdrqga@ws.net.home>
On Sun, Mar 24, 2024 at 09:07:45PM +0100 Karel Zak wrote:
> On Sat, Mar 16, 2024 at 06:10:03PM +0100, Thijs Raymakers wrote:
> > I'm looking for any comments on coresched, a program that allows you to
> > manage core scheduling cookies for tasks.
>
> https://github.com/util-linux/util-linux/pull/2839
>
> Guys, you should sync your work and merge it together :-)
>
> CC: Phil Auld
>
After looking at this some more I'd like to try to add your
pull/push use case to my code instead.
There are some issues I'm having with the arguments in this one.
I think it's a little counter-intuitive.
The exec should not fork first.
And the error handling I think is just not right. I believe prctl()
returns -1 on error and sets errno. This code will report EPERM for all
prtcl errors. While EPERM is likely I don't think it's the only error.
I put a few other comments inline as well.
Cheers,
Phil
> Karel
>
> >
> > === What is Core Scheduling ===
> > Core Scheduling can be used to ensure that certain tasks will never be
> > scheduled on the same physical core. This can be a useful, alternative,
> > mitigation to hardware vulnerabilities like L1tf or MDS.
> > The full software mitigation for these vulnerabilities would be to disable
> > SMT/Hyper-Threading. However, this can be prohibitively expensive and
> > therefore often not done in practice.
> > With Core Scheduling you can mitigate in these issues in some scenarios,
> > while keeping SMT enabled.
> >
> > Core Scheduling works by adding a random "cookie" to a process. Only
> > processes with the same core scheduling cookie are allowed to run on
> > sibling cores. Tasks that trust each other can be given the same
> > cookie and untrusted tasks are given a different cookie.
> > This is important when running VMs that don't trust each other, as
> > it prevents a guest VM to leak data from another guest VM with L1tf or MDS.
> >
> > === Motivation ===
> > The kernel exposes a prctl uapi to manage core scheduling cookies (see
> > https://www.kernel.org/doc/html/latest/admin-guide/hw-vuln/core-scheduling.html)
> > Last week, I wanted to use core scheduling on some programs. Adding the
> > prctl calls and recompiling felt a bit inconvenient, so I looked for a
> > program like taskset that could do the job without having to modify the
> > target program. I couldn't find any, and so I wrote a small program that
> > does this. Hopefully it saves the next person some time :)
> >
> > === RFC ===
> > I'm looking forward to any comments that you might have on the patch!
> > Please note that I haven't written the manpage and the bash completion
> > script yet. I first wanted to get some feedback on the program before I
> > start documenting it in more detail.
> >
> > I'm particularly curious about your thoughts on the following things:
> >
> > - General comments about interacting with the program: Do the options
> > make sense? Are there any necessary functions missing? Are the error
> > messages helpful? Is the output too verbose/not verbose enough?
> > - How should the program behave if the prctl core scheduling API is not
> > available? It has been in Linus' tree since november 2021
> > (commit a41b74451b35f7a6529689760eb8c05241feecbc) but it can be
> > disabled with CONFIG_SCHED_CORE=n
This one is tricky. If I recall the error you get is ambiguous in this case
(EINVAL, which can be returned for other reasons). It would be nice to be
able to report that specifically.
> > - Most of the options require the user to have the CAP_SYS_PTRACE
> > capability. Should the program notify the user that the capability
> > is missing when the prctl call returns -EPREM, or does a mention in the
> > man page suffice?
Reporting the correct error message should be enough.
> > - I've currently licensed it under the EUPL v1.2, which is easier to
> > enforce in my jurisdiction than the GPL. It is GPL compatible so it
> > shouldn't be an issue, but if anybody has any remarks on this, please
> > let me know.
The util-linux package is GPL so personally I think it would be good to just
make any new addition to that package also GPL.
> >
> > Thanks for taking the time!
> >
> > Best regards,
> > Thijs Raymakers
> >
> >
> > Signed-off-by: Thijs Raymakers <thijs@raymakers.nl>
> >
> > ---
> > .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 | 340 ++++++++++++++++++++++++++++++++++++
> > 8 files changed, 389 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 f7baab7a2..8244c43a9 100644
> > --- a/meson.build
> > +++ b/meson.build
> > @@ -3107,13 +3107,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..4be8f9fda
> > --- /dev/null
> > +++ b/schedutils/coresched.c
> > @@ -0,0 +1,340 @@
> > +/**
> > + * SPDX-License-Identifier: EUPL-1.2
> > + *
> > + * coresched.c - manage core scheduling cookies for tasks
> > + *
> > + * Copyright (C) 2024 Thijs Raymakers
> > + * Licensed under the EUPL v1.2
> > + */
> > +
> > +#include <getopt.h>
> > +#include <stdbool.h>
> > +#include <stdio.h>
> > +#include <sys/prctl.h>
> > +#include <sys/wait.h>
> > +
> > +#include "c.h"
> > +#include "closestream.h"
> > +#include "nls.h"
> > +#include "strutils.h"
> > +
> > +typedef enum {
> > + SCHED_CORE_SCOPE_PID = PR_SCHED_CORE_SCOPE_THREAD,
> > + SCHED_CORE_SCOPE_TGID = PR_SCHED_CORE_SCOPE_THREAD_GROUP,
> > + SCHED_CORE_SCOPE_PGID = PR_SCHED_CORE_SCOPE_PROCESS_GROUP,
> > +} core_sched_type_t;
> > +
Is this needed? You've made the names a little shorter I guess.
Also some versions of prctl.h don't have the PR_SCHED_CORE_SCOPE* defines
even when they have the base PR_SCHED_CORE macros.
> > +typedef enum {
> > + SCHED_CORE_CMD_NONE = 0,
> > + SCHED_CORE_CMD_GET = 1,
> > + SCHED_CORE_CMD_CREATE = 2,
> > + SCHED_CORE_CMD_COPY = 4,
> > + SCHED_CORE_CMD_EXEC = 8,
> > +} core_sched_cmd_t;
> > +
> > +struct args {
> > + pid_t from_pid;
> > + pid_t to_pid;
> > + core_sched_type_t type;
> > + core_sched_cmd_t cmd;
> > + int exec_argv_offset;
> > +};
> > +
> > +unsigned long core_sched_get_cookie(struct args *args);
> > +void core_sched_create_cookie(struct args *args);
> > +void core_sched_pull_cookie(pid_t from);
> > +void core_sched_push_cookie(pid_t to, core_sched_type_t type);
> > +void core_sched_copy_cookie(struct args *args);
> > +void core_sched_exec_with_cookie(struct args *args, char **argv);
> > +
> > +core_sched_type_t parse_core_sched_type(char *str);
> > +bool verify_arguments(struct args *args);
> > +void parse_arguments(int argc, char **argv, struct args *args);
> > +
> > +unsigned long core_sched_get_cookie(struct args *args)
> > +{
> > + unsigned long cookie = 0;
> > + int prctl_errno = prctl(PR_SCHED_CORE, PR_SCHED_CORE_GET,
> > + args->from_pid, SCHED_CORE_SCOPE_PID, &cookie);
> > + if (prctl_errno) {
> > + errx(-prctl_errno, "Failed to get cookie from PID %d",
> > + args->from_pid);
> > + }
prctl_errno will be 0 on success or -1 on failure. This will report EPERM for
every error.
> > + return cookie;
> > +}
> > +
> > +void core_sched_create_cookie(struct args *args)
> > +{
> > + int prctl_errno = prctl(PR_SCHED_CORE, PR_SCHED_CORE_CREATE,
> > + args->from_pid, args->type, 0);
We are creating a new cookie on from_pid, seems odd.
> > + if (prctl_errno) {
> > + errx(-prctl_errno, "Failed to create cookie for PID %d",
> > + args->from_pid);
> > + }
> > +}
> > +
> > +void core_sched_pull_cookie(pid_t from)
> > +{
> > + int prctl_errno = prctl(PR_SCHED_CORE, PR_SCHED_CORE_SHARE_FROM, from,
> > + SCHED_CORE_SCOPE_PID, 0);
> > + if (prctl_errno) {
> > + errx(-prctl_errno, "Failed to pull cookie from PID %d", from);
> > + }
> > +}
> > +
> > +void core_sched_push_cookie(pid_t to, core_sched_type_t type)
> > +{
> > + int prctl_errno =
> > + prctl(PR_SCHED_CORE, PR_SCHED_CORE_SHARE_TO, to, type, 0);
> > + if (prctl_errno) {
> > + errx(-prctl_errno, "Failed to push cookie to PID %d", to);
> > + }
> > +}
> > +
> > +void core_sched_copy_cookie(struct args *args)
> > +{
> > + core_sched_pull_cookie(args->from_pid);
> > + core_sched_push_cookie(args->to_pid, args->type);
> > +}
> > +
> > +void core_sched_exec_with_cookie(struct args *args, char **argv)
> > +{
> > + if (!args->exec_argv_offset) {
> > + errx(EINVAL, "when --exec is provided, a program name "
> > + "has to be given.");
> > + }
> > +
> > + // Move the argument list to the first argument of the program
> > + argv = &argv[args->exec_argv_offset];
> > +
> > + pid_t pid = fork();
> > + if (pid == -1) {
> > + errx(errno, "Failed to spawn new process");
> > + }
Don't fork here. Just exec to the new program, as the other wrapper
programs like taskset do.
> > +
> > + if (!pid) {
> > + // If a source PID is provided, try to copy the cookie from
> > + // that PID. Otherwise, create a brand new cookie with the
> > + // provided type.
> > + if (args->from_pid) {
> > + core_sched_pull_cookie(args->from_pid);
> > + } else {
> > + args->from_pid = getpid();
> > + core_sched_create_cookie(args);
> > + }
> > + if (execvp(argv[0], argv)) {
> > + errexec(argv[0]);
> > + }
> > + } else {
> > + int status = 0;
> > + waitpid(pid, &status, 0);
> > + exit(status);
> > + }
> > +}
> > +
> > +core_sched_type_t parse_core_sched_type(char *str)
> > +{
> > + if (!strncmp(str, "pid\0", 4)) {
> > + return SCHED_CORE_SCOPE_PID;
> > + } else if (!strncmp(str, "tgid\0", 5)) {
> > + return SCHED_CORE_SCOPE_TGID;
> > + } else if (!strncmp(str, "pgid\0", 5)) {
> > + return SCHED_CORE_SCOPE_PGID;
> > + }
> > +
> > + errx(EINVAL, "'%s' is an invalid option. Must be one of pid/tgid/pgid",
> > + str);
> > + __builtin_unreachable();
> > +}
> > +
> > +static void __attribute__((__noreturn__)) usage(void)
> > +{
> > + fputs(USAGE_HEADER, stdout);
> > + fprintf(stdout, _(" %s --get <PID>\n"), program_invocation_short_name);
> > + fprintf(stdout, _(" %s --new <PID> [-t <TYPE>]\n"),
> > + program_invocation_short_name);
> > + fprintf(stdout, _(" %s --copy -s <PID> -d <PID> [-t <TYPE>]\n"),
> > + program_invocation_short_name);
> > + fprintf(stdout, _(" %s --exec [-s <PID>] -- PROGRAM ARGS... \n"),
> > + program_invocation_short_name);
There are 4 different arguments that take a pid when there are really
only at most 2 needed.
"Get" could just be the default with no arguments given.
> > +
> > + fputs(USAGE_SEPARATOR, stdout);
> > + fputsln(_("Manage core scheduling cookies for tasks."), stdout);
> > +
> > + fputs(USAGE_FUNCTIONS, stdout);
> > + fputsln(_(" -g, --get <PID> get the core scheduling cookie of a PID"),
> > + stdout);
> > + fputsln(_(" -n, --new <PID> assign a new core scheduling cookie to PID"),
> > + stdout);
> > + fputsln(_(" -c, --copy copy the core scheduling cookie from PID to\n"
> > + " another PID, requires the --source and --dest option"),
> > + stdout);
> > + fputsln(_(" -e, --exec execute a program with a new core scheduling\n"
> > + " cookie."),
> > + stdout);
> > +
> > + fputs(USAGE_OPTIONS, stdout);
> > + fputsln(_(" -s, --source <PID> where to copy the core scheduling cookie from."),
> > + stdout);
> > + fputsln(_(" -d, --dest <PID> where to copy the core scheduling cookie to."),
> > + stdout);
> > + fputsln(_(" -t, --type type of the destination PID, or the type of\n"
> > + " the PID when a new core scheduling cookie\n"
> > + " is created. Can be one of the following:\n"
> > + " pid, tgid or pgid. Defaults to tgid."),
> > + stdout);
> > + fputs(USAGE_SEPARATOR, stdout);
> > + fprintf(stdout,
> > + USAGE_HELP_OPTIONS(
> > + 25)); /* char offset to align option descriptions */
> > + fprintf(stdout, USAGE_MAN_TAIL("coresched(1)"));
> > + exit(EXIT_SUCCESS);
> > +}
> > +
> > +bool verify_arguments(struct args *args)
> > +{
> > + if (args->cmd == SCHED_CORE_CMD_NONE) {
> > + usage();
> > + }
> > +
> > + // Check if the value of args->cmd is a power of 2
> > + // In that case, only a single function option was set.
> > + if (!(args->cmd && !(args->cmd & (args->cmd - 1)))) {
> > + errx(EINVAL, "Cannot do more than one function at a time.");
> > + }
> > +
> > + if (args->from_pid < 0) {
> > + errx(EINVAL, "source PID cannot be negative");
> > + }
> > +
> > + if (args->to_pid < 0) {
> > + errx(EINVAL, "destination PID cannot be negative");
> > + }
> > +
> > + if (args->from_pid == 0 && args->cmd == SCHED_CORE_CMD_COPY) {
> > + errx(EINVAL, "valid argument to --source is required");
> > + }
> > +
> > + if (args->to_pid == 0 && args->cmd == SCHED_CORE_CMD_COPY) {
> > + errx(EINVAL, "valid argument to --dest is required");
> > + }
> > +
> > + if (args->from_pid == 0 && args->cmd != SCHED_CORE_CMD_EXEC) {
> > + errx(EINVAL, "PID cannot be zero");
> > + }
> > +
> > + return true;
> > +}
> > +
> > +void parse_arguments(int argc, char **argv, struct args *args)
> > +{
> > + int c;
> > +
> > + enum {
> > + OPT_GET = 'g',
> > + OPT_NEW = 'n',
> > + OPT_COPY = 'c',
> > + OPT_EXEC = 'e',
> > + OPT_SRC = 's',
> > + OPT_DEST = 'd',
> > + OPT_TYPE = 't',
> > + OPT_VERSION = 'V',
> > + OPT_HELP = 'h'
> > + };
This seems over-engineered. You have to specificy the characters
in the getopt_long call anyway so this just complicates the code for
no reason it seems to me.
> > +
> > + static const struct option longopts[] = {
> > + { "get", required_argument, NULL, OPT_GET },
> > + { "new", required_argument, NULL, OPT_NEW },
> > + { "copy", no_argument, NULL, OPT_COPY },
> > + { "exec", no_argument, NULL, OPT_EXEC },
> > + { "source", required_argument, NULL, OPT_SRC },
> > + { "destination", required_argument, NULL, OPT_DEST },
> > + { "type", required_argument, NULL, OPT_TYPE },
> > + { "version", no_argument, NULL, OPT_VERSION },
> > + { "help", no_argument, NULL, OPT_HELP },
> > + { NULL, 0, NULL, 0 }
> > + };
> > +
> > + while ((c = getopt_long(argc, argv, "g:n:ces:d:t:Vh", longopts,
> > + NULL)) != -1)
> > + switch (c) {
> > + case OPT_GET:
> > + args->cmd |= SCHED_CORE_CMD_GET;
> > + args->from_pid = strtos32_or_err(
> > + optarg, "Failed to parse PID for --get");
> > + break;
> > + case OPT_NEW:
> > + args->cmd |= SCHED_CORE_CMD_CREATE;
> > + args->from_pid = strtos32_or_err(
> > + optarg, "Failed to parse PID for --new");
> > + break;
> > + case OPT_COPY:
> > + args->cmd |= SCHED_CORE_CMD_COPY;
> > + break;
> > + case OPT_EXEC:
> > + args->cmd |= SCHED_CORE_CMD_EXEC;
> > + break;
> > + case OPT_SRC:
> > + args->from_pid = strtos32_or_err(
> > + optarg, "Failed to parse PID for --source");
> > + break;
> > + case OPT_DEST:
> > + args->to_pid = strtos32_or_err(
> > + optarg, "Failed to parse PID for --dest");
> > + break;
> > + case OPT_TYPE:
> > + args->type = parse_core_sched_type(optarg);
> > + break;
> > + case OPT_VERSION:
> > + print_version(EXIT_SUCCESS);
> > + case OPT_HELP:
> > + usage();
> > + default:
> > + errtryhelp(EXIT_FAILURE);
> > + }
> > +
> > + if (argc > optind) {
> > + args->exec_argv_offset = optind;
> > + }
> > + verify_arguments(args);
> > +}
> > +
> > +int main(int argc, char **argv)
> > +{
> > + struct args arguments = { 0 };
> > + arguments.type = SCHED_CORE_SCOPE_TGID;
> > +
> > + setlocale(LC_ALL, "");
> > + bindtextdomain(PACKAGE, LOCALEDIR);
> > + textdomain(PACKAGE);
> > + close_stdout_atexit();
> > +
> > + parse_arguments(argc, argv, &arguments);
> > +
> > + unsigned long cookie = 0;
> > + switch (arguments.cmd) {
> > + case SCHED_CORE_CMD_GET:
> > + cookie = core_sched_get_cookie(&arguments);
> > + if (cookie) {
> > + printf("core scheduling cookie of pid %d is 0x%lx\n",
> > + arguments.from_pid, cookie);
> > + } else {
> > + printf("pid %d doesn't have a core scheduling cookie\n",
> > + arguments.from_pid);
> > + exit(1);
> > + }
> > + break;
> > + case SCHED_CORE_CMD_CREATE:
> > + core_sched_create_cookie(&arguments);
> > + break;
> > + case SCHED_CORE_CMD_COPY:
> > + core_sched_copy_cookie(&arguments);
> > + break;
> > + case SCHED_CORE_CMD_EXEC:
> > + core_sched_exec_with_cookie(&arguments, argv);
> > + break;
> > + default:
> > + usage();
> > + exit(1);
> > + }
> > +}
> > --
> > 2.44.0
> >
> >
>
> --
> Karel Zak <kzak@redhat.com>
> http://karelzak.blogspot.com
>
--
^ permalink raw reply
* Mistakes in messages of util-linux-2.40-rc2
From: Petr Pisar @ 2024-03-26 11:28 UTC (permalink / raw)
To: util-linux
[-- Attachment #1: Type: text/plain, Size: 2041 bytes --]
Hello,
while translating util-linux-2.40-rc2 I notice a few messages which are, in my
opinion, incorrect:
> #: login-utils/lslogins.c:1461
> msgid " -p, --pwd display information related to login by password.\n"
Other surrounding messages do end with the full stop charater. I recommend
removing it.
> #: misc-utils/lastlog2.c:117
> msgid " -S, --set ySet lastlog record to current time (requires -u)\n"
"ySet" is a typo.
> #: misc-utils/lsblk.c:236
> msgid "write same max bytes, use <number> if --bytes is given"
There are multiple messages ending with "use <number> if --bytes is given".
I found the ", use" clause clumsy. Especillay the reference to "<number>".
I know it refers to an argument of the --bytes option, but it can be difficult
for users to realize it. Another issue I have is the same verb form of "write"
and "use". It takes time to realize "write" is a compound attribute of
"bytes", not a predicate.
I propose rephrasing it as "write-same max bytes, respect --bytes format".
Or "write same max bytes, respects --bytes format".
> #: misc-utils/lsfd.c:405
> msgid "extended version of MDOE (rwxD[Ll]m)"
"MDOE" should be "MODE".
> #: misc-utils/lslocks.c:92
> msgid "HOLDERS of the lock"
"HOLDERS" should be "holders". It's not a replacement word.
> #: misc-utils/waitpid.c:239
> #, c-format
> msgid "can't want for %zu of %zu PIDs"
"want" should be "wait".
> #: sys-utils/chcpu.c:208
> #, c-format
> msgid "CPU %u configure failed"
"configure" should be "configuration".
> #: sys-utils/irqtop.c:180
> msgid "cannot not create timerfd"
Duplicate "not".
> #: sys-utils/setpriv.c:135
> msgid " --rgid <gid|user> set real gid\n"
"user" should be "group".
> #: term-utils/agetty.c:2466
> msgid " -J --noclear do not clear the screen before prompt\n"
There is missing a comma between the alternations of the option.
> #: term-utils/agetty.c:2471
> msgid " -N --nonewline do not print a newline before issue\n"
The same problem.
-- Petr
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply
* Re: [PATCH] rev: Check for wchar conversion errors
From: Karel Zak @ 2024-03-25 18:52 UTC (permalink / raw)
To: Tim Hallmann; +Cc: util-linux
In-Reply-To: <20240324191430.319771-1-tim@t8w.de>
On Sun, Mar 24, 2024 at 08:14:30PM +0100, Tim Hallmann wrote:
> text-utils/rev.c | 14 ++++++++------
> 1 file changed, 8 insertions(+), 6 deletions(-)
Looks good, I've created PR
https://github.com/util-linux/util-linux/pull/2854 to verify the
change against our tests.
Karel
--
Karel Zak <kzak@redhat.com>
http://karelzak.blogspot.com
^ permalink raw reply
* Re: [PATCH RFC] coresched: Manage core scheduling cookies for tasks
From: Karel Zak @ 2024-03-24 20:07 UTC (permalink / raw)
To: Thijs Raymakers, Phil Auld; +Cc: util-linux
In-Reply-To: <20240316171238.177897-1-thijs@raymakers.nl>
On Sat, Mar 16, 2024 at 06:10:03PM +0100, Thijs Raymakers wrote:
> I'm looking for any comments on coresched, a program that allows you to
> manage core scheduling cookies for tasks.
https://github.com/util-linux/util-linux/pull/2839
Guys, you should sync your work and merge it together :-)
CC: Phil Auld
Karel
>
> === What is Core Scheduling ===
> Core Scheduling can be used to ensure that certain tasks will never be
> scheduled on the same physical core. This can be a useful, alternative,
> mitigation to hardware vulnerabilities like L1tf or MDS.
> The full software mitigation for these vulnerabilities would be to disable
> SMT/Hyper-Threading. However, this can be prohibitively expensive and
> therefore often not done in practice.
> With Core Scheduling you can mitigate in these issues in some scenarios,
> while keeping SMT enabled.
>
> Core Scheduling works by adding a random "cookie" to a process. Only
> processes with the same core scheduling cookie are allowed to run on
> sibling cores. Tasks that trust each other can be given the same
> cookie and untrusted tasks are given a different cookie.
> This is important when running VMs that don't trust each other, as
> it prevents a guest VM to leak data from another guest VM with L1tf or MDS.
>
> === Motivation ===
> The kernel exposes a prctl uapi to manage core scheduling cookies (see
> https://www.kernel.org/doc/html/latest/admin-guide/hw-vuln/core-scheduling.html)
> Last week, I wanted to use core scheduling on some programs. Adding the
> prctl calls and recompiling felt a bit inconvenient, so I looked for a
> program like taskset that could do the job without having to modify the
> target program. I couldn't find any, and so I wrote a small program that
> does this. Hopefully it saves the next person some time :)
>
> === RFC ===
> I'm looking forward to any comments that you might have on the patch!
> Please note that I haven't written the manpage and the bash completion
> script yet. I first wanted to get some feedback on the program before I
> start documenting it in more detail.
>
> I'm particularly curious about your thoughts on the following things:
>
> - General comments about interacting with the program: Do the options
> make sense? Are there any necessary functions missing? Are the error
> messages helpful? Is the output too verbose/not verbose enough?
> - How should the program behave if the prctl core scheduling API is not
> available? It has been in Linus' tree since november 2021
> (commit a41b74451b35f7a6529689760eb8c05241feecbc) but it can be
> disabled with CONFIG_SCHED_CORE=n
> - Most of the options require the user to have the CAP_SYS_PTRACE
> capability. Should the program notify the user that the capability
> is missing when the prctl call returns -EPREM, or does a mention in the
> man page suffice?
> - I've currently licensed it under the EUPL v1.2, which is easier to
> enforce in my jurisdiction than the GPL. It is GPL compatible so it
> shouldn't be an issue, but if anybody has any remarks on this, please
> let me know.
>
> Thanks for taking the time!
>
> Best regards,
> Thijs Raymakers
>
>
> Signed-off-by: Thijs Raymakers <thijs@raymakers.nl>
>
> ---
> .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 | 340 ++++++++++++++++++++++++++++++++++++
> 8 files changed, 389 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 f7baab7a2..8244c43a9 100644
> --- a/meson.build
> +++ b/meson.build
> @@ -3107,13 +3107,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..4be8f9fda
> --- /dev/null
> +++ b/schedutils/coresched.c
> @@ -0,0 +1,340 @@
> +/**
> + * SPDX-License-Identifier: EUPL-1.2
> + *
> + * coresched.c - manage core scheduling cookies for tasks
> + *
> + * Copyright (C) 2024 Thijs Raymakers
> + * Licensed under the EUPL v1.2
> + */
> +
> +#include <getopt.h>
> +#include <stdbool.h>
> +#include <stdio.h>
> +#include <sys/prctl.h>
> +#include <sys/wait.h>
> +
> +#include "c.h"
> +#include "closestream.h"
> +#include "nls.h"
> +#include "strutils.h"
> +
> +typedef enum {
> + SCHED_CORE_SCOPE_PID = PR_SCHED_CORE_SCOPE_THREAD,
> + SCHED_CORE_SCOPE_TGID = PR_SCHED_CORE_SCOPE_THREAD_GROUP,
> + SCHED_CORE_SCOPE_PGID = PR_SCHED_CORE_SCOPE_PROCESS_GROUP,
> +} core_sched_type_t;
> +
> +typedef enum {
> + SCHED_CORE_CMD_NONE = 0,
> + SCHED_CORE_CMD_GET = 1,
> + SCHED_CORE_CMD_CREATE = 2,
> + SCHED_CORE_CMD_COPY = 4,
> + SCHED_CORE_CMD_EXEC = 8,
> +} core_sched_cmd_t;
> +
> +struct args {
> + pid_t from_pid;
> + pid_t to_pid;
> + core_sched_type_t type;
> + core_sched_cmd_t cmd;
> + int exec_argv_offset;
> +};
> +
> +unsigned long core_sched_get_cookie(struct args *args);
> +void core_sched_create_cookie(struct args *args);
> +void core_sched_pull_cookie(pid_t from);
> +void core_sched_push_cookie(pid_t to, core_sched_type_t type);
> +void core_sched_copy_cookie(struct args *args);
> +void core_sched_exec_with_cookie(struct args *args, char **argv);
> +
> +core_sched_type_t parse_core_sched_type(char *str);
> +bool verify_arguments(struct args *args);
> +void parse_arguments(int argc, char **argv, struct args *args);
> +
> +unsigned long core_sched_get_cookie(struct args *args)
> +{
> + unsigned long cookie = 0;
> + int prctl_errno = prctl(PR_SCHED_CORE, PR_SCHED_CORE_GET,
> + args->from_pid, SCHED_CORE_SCOPE_PID, &cookie);
> + if (prctl_errno) {
> + errx(-prctl_errno, "Failed to get cookie from PID %d",
> + args->from_pid);
> + }
> + return cookie;
> +}
> +
> +void core_sched_create_cookie(struct args *args)
> +{
> + int prctl_errno = prctl(PR_SCHED_CORE, PR_SCHED_CORE_CREATE,
> + args->from_pid, args->type, 0);
> + if (prctl_errno) {
> + errx(-prctl_errno, "Failed to create cookie for PID %d",
> + args->from_pid);
> + }
> +}
> +
> +void core_sched_pull_cookie(pid_t from)
> +{
> + int prctl_errno = prctl(PR_SCHED_CORE, PR_SCHED_CORE_SHARE_FROM, from,
> + SCHED_CORE_SCOPE_PID, 0);
> + if (prctl_errno) {
> + errx(-prctl_errno, "Failed to pull cookie from PID %d", from);
> + }
> +}
> +
> +void core_sched_push_cookie(pid_t to, core_sched_type_t type)
> +{
> + int prctl_errno =
> + prctl(PR_SCHED_CORE, PR_SCHED_CORE_SHARE_TO, to, type, 0);
> + if (prctl_errno) {
> + errx(-prctl_errno, "Failed to push cookie to PID %d", to);
> + }
> +}
> +
> +void core_sched_copy_cookie(struct args *args)
> +{
> + core_sched_pull_cookie(args->from_pid);
> + core_sched_push_cookie(args->to_pid, args->type);
> +}
> +
> +void core_sched_exec_with_cookie(struct args *args, char **argv)
> +{
> + if (!args->exec_argv_offset) {
> + errx(EINVAL, "when --exec is provided, a program name "
> + "has to be given.");
> + }
> +
> + // Move the argument list to the first argument of the program
> + argv = &argv[args->exec_argv_offset];
> +
> + pid_t pid = fork();
> + if (pid == -1) {
> + errx(errno, "Failed to spawn new process");
> + }
> +
> + if (!pid) {
> + // If a source PID is provided, try to copy the cookie from
> + // that PID. Otherwise, create a brand new cookie with the
> + // provided type.
> + if (args->from_pid) {
> + core_sched_pull_cookie(args->from_pid);
> + } else {
> + args->from_pid = getpid();
> + core_sched_create_cookie(args);
> + }
> + if (execvp(argv[0], argv)) {
> + errexec(argv[0]);
> + }
> + } else {
> + int status = 0;
> + waitpid(pid, &status, 0);
> + exit(status);
> + }
> +}
> +
> +core_sched_type_t parse_core_sched_type(char *str)
> +{
> + if (!strncmp(str, "pid\0", 4)) {
> + return SCHED_CORE_SCOPE_PID;
> + } else if (!strncmp(str, "tgid\0", 5)) {
> + return SCHED_CORE_SCOPE_TGID;
> + } else if (!strncmp(str, "pgid\0", 5)) {
> + return SCHED_CORE_SCOPE_PGID;
> + }
> +
> + errx(EINVAL, "'%s' is an invalid option. Must be one of pid/tgid/pgid",
> + str);
> + __builtin_unreachable();
> +}
> +
> +static void __attribute__((__noreturn__)) usage(void)
> +{
> + fputs(USAGE_HEADER, stdout);
> + fprintf(stdout, _(" %s --get <PID>\n"), program_invocation_short_name);
> + fprintf(stdout, _(" %s --new <PID> [-t <TYPE>]\n"),
> + program_invocation_short_name);
> + fprintf(stdout, _(" %s --copy -s <PID> -d <PID> [-t <TYPE>]\n"),
> + program_invocation_short_name);
> + fprintf(stdout, _(" %s --exec [-s <PID>] -- PROGRAM ARGS... \n"),
> + program_invocation_short_name);
> +
> + fputs(USAGE_SEPARATOR, stdout);
> + fputsln(_("Manage core scheduling cookies for tasks."), stdout);
> +
> + fputs(USAGE_FUNCTIONS, stdout);
> + fputsln(_(" -g, --get <PID> get the core scheduling cookie of a PID"),
> + stdout);
> + fputsln(_(" -n, --new <PID> assign a new core scheduling cookie to PID"),
> + stdout);
> + fputsln(_(" -c, --copy copy the core scheduling cookie from PID to\n"
> + " another PID, requires the --source and --dest option"),
> + stdout);
> + fputsln(_(" -e, --exec execute a program with a new core scheduling\n"
> + " cookie."),
> + stdout);
> +
> + fputs(USAGE_OPTIONS, stdout);
> + fputsln(_(" -s, --source <PID> where to copy the core scheduling cookie from."),
> + stdout);
> + fputsln(_(" -d, --dest <PID> where to copy the core scheduling cookie to."),
> + stdout);
> + fputsln(_(" -t, --type type of the destination PID, or the type of\n"
> + " the PID when a new core scheduling cookie\n"
> + " is created. Can be one of the following:\n"
> + " pid, tgid or pgid. Defaults to tgid."),
> + stdout);
> + fputs(USAGE_SEPARATOR, stdout);
> + fprintf(stdout,
> + USAGE_HELP_OPTIONS(
> + 25)); /* char offset to align option descriptions */
> + fprintf(stdout, USAGE_MAN_TAIL("coresched(1)"));
> + exit(EXIT_SUCCESS);
> +}
> +
> +bool verify_arguments(struct args *args)
> +{
> + if (args->cmd == SCHED_CORE_CMD_NONE) {
> + usage();
> + }
> +
> + // Check if the value of args->cmd is a power of 2
> + // In that case, only a single function option was set.
> + if (!(args->cmd && !(args->cmd & (args->cmd - 1)))) {
> + errx(EINVAL, "Cannot do more than one function at a time.");
> + }
> +
> + if (args->from_pid < 0) {
> + errx(EINVAL, "source PID cannot be negative");
> + }
> +
> + if (args->to_pid < 0) {
> + errx(EINVAL, "destination PID cannot be negative");
> + }
> +
> + if (args->from_pid == 0 && args->cmd == SCHED_CORE_CMD_COPY) {
> + errx(EINVAL, "valid argument to --source is required");
> + }
> +
> + if (args->to_pid == 0 && args->cmd == SCHED_CORE_CMD_COPY) {
> + errx(EINVAL, "valid argument to --dest is required");
> + }
> +
> + if (args->from_pid == 0 && args->cmd != SCHED_CORE_CMD_EXEC) {
> + errx(EINVAL, "PID cannot be zero");
> + }
> +
> + return true;
> +}
> +
> +void parse_arguments(int argc, char **argv, struct args *args)
> +{
> + int c;
> +
> + enum {
> + OPT_GET = 'g',
> + OPT_NEW = 'n',
> + OPT_COPY = 'c',
> + OPT_EXEC = 'e',
> + OPT_SRC = 's',
> + OPT_DEST = 'd',
> + OPT_TYPE = 't',
> + OPT_VERSION = 'V',
> + OPT_HELP = 'h'
> + };
> +
> + static const struct option longopts[] = {
> + { "get", required_argument, NULL, OPT_GET },
> + { "new", required_argument, NULL, OPT_NEW },
> + { "copy", no_argument, NULL, OPT_COPY },
> + { "exec", no_argument, NULL, OPT_EXEC },
> + { "source", required_argument, NULL, OPT_SRC },
> + { "destination", required_argument, NULL, OPT_DEST },
> + { "type", required_argument, NULL, OPT_TYPE },
> + { "version", no_argument, NULL, OPT_VERSION },
> + { "help", no_argument, NULL, OPT_HELP },
> + { NULL, 0, NULL, 0 }
> + };
> +
> + while ((c = getopt_long(argc, argv, "g:n:ces:d:t:Vh", longopts,
> + NULL)) != -1)
> + switch (c) {
> + case OPT_GET:
> + args->cmd |= SCHED_CORE_CMD_GET;
> + args->from_pid = strtos32_or_err(
> + optarg, "Failed to parse PID for --get");
> + break;
> + case OPT_NEW:
> + args->cmd |= SCHED_CORE_CMD_CREATE;
> + args->from_pid = strtos32_or_err(
> + optarg, "Failed to parse PID for --new");
> + break;
> + case OPT_COPY:
> + args->cmd |= SCHED_CORE_CMD_COPY;
> + break;
> + case OPT_EXEC:
> + args->cmd |= SCHED_CORE_CMD_EXEC;
> + break;
> + case OPT_SRC:
> + args->from_pid = strtos32_or_err(
> + optarg, "Failed to parse PID for --source");
> + break;
> + case OPT_DEST:
> + args->to_pid = strtos32_or_err(
> + optarg, "Failed to parse PID for --dest");
> + break;
> + case OPT_TYPE:
> + args->type = parse_core_sched_type(optarg);
> + break;
> + case OPT_VERSION:
> + print_version(EXIT_SUCCESS);
> + case OPT_HELP:
> + usage();
> + default:
> + errtryhelp(EXIT_FAILURE);
> + }
> +
> + if (argc > optind) {
> + args->exec_argv_offset = optind;
> + }
> + verify_arguments(args);
> +}
> +
> +int main(int argc, char **argv)
> +{
> + struct args arguments = { 0 };
> + arguments.type = SCHED_CORE_SCOPE_TGID;
> +
> + setlocale(LC_ALL, "");
> + bindtextdomain(PACKAGE, LOCALEDIR);
> + textdomain(PACKAGE);
> + close_stdout_atexit();
> +
> + parse_arguments(argc, argv, &arguments);
> +
> + unsigned long cookie = 0;
> + switch (arguments.cmd) {
> + case SCHED_CORE_CMD_GET:
> + cookie = core_sched_get_cookie(&arguments);
> + if (cookie) {
> + printf("core scheduling cookie of pid %d is 0x%lx\n",
> + arguments.from_pid, cookie);
> + } else {
> + printf("pid %d doesn't have a core scheduling cookie\n",
> + arguments.from_pid);
> + exit(1);
> + }
> + break;
> + case SCHED_CORE_CMD_CREATE:
> + core_sched_create_cookie(&arguments);
> + break;
> + case SCHED_CORE_CMD_COPY:
> + core_sched_copy_cookie(&arguments);
> + break;
> + case SCHED_CORE_CMD_EXEC:
> + core_sched_exec_with_cookie(&arguments, argv);
> + break;
> + default:
> + usage();
> + exit(1);
> + }
> +}
> --
> 2.44.0
>
>
--
Karel Zak <kzak@redhat.com>
http://karelzak.blogspot.com
^ permalink raw reply
* [PATCH] rev: Check for wchar conversion errors
From: Tim Hallmann @ 2024-03-24 19:14 UTC (permalink / raw)
To: util-linux
Commit c9cc84621ca98ef85499e83ca56f05f12055f193 introduced a regression
where only the actual EOF is handled, not other error conditions
returning WEOF. This leads to an infinite loop upon encountering
conversion errors. For example (using LC_CTYPE="en_US.UTF-8"):
$ printf '\x80' | rev
Signed-off-by: Tim Hallmann <tim@t8w.de>
---
text-utils/rev.c | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)
diff --git a/text-utils/rev.c b/text-utils/rev.c
index 81331719d..4b731890d 100644
--- a/text-utils/rev.c
+++ b/text-utils/rev.c
@@ -173,8 +173,6 @@ int main(int argc, char *argv[])
line = 0;
while (!feof(fp)) {
len = read_line(sep, buf, bufsiz, fp);
- if (len == 0)
- continue;
/* This is my hack from setpwnam.c -janl */
while (len == bufsiz && !feof(fp)) {
@@ -187,14 +185,18 @@ int main(int argc, char *argv[])
/* And fill the rest of the buffer */
len += read_line(sep, &buf[len], bufsiz/2, fp);
}
+ if (ferror(fp)) {
+ warn("%s: %ju", filename, line);
+ rval = EXIT_FAILURE;
+ break;
+ }
+ if (len == 0)
+ continue;
+
reverse_str(buf, buf[len - 1] == sep ? len - 1 : len);
write_line(buf, len, stdout);
line++;
}
- if (ferror(fp)) {
- warn("%s: %ju", filename, line);
- rval = EXIT_FAILURE;
- }
if (fp != stdin)
fclose(fp);
} while(*argv);
--
2.44.0
^ permalink raw reply related
* Re: bug#69532: mv's new -x option should be made orthogonal to -t/-T/default
From: Bernhard Voelker @ 2024-03-23 10:24 UTC (permalink / raw)
To: Karel Zak
Cc: Paul Eggert, Pádraig Brady, 69532, util-linux,
Dominique Martinet, Petr Malat, Rob Landley
In-Reply-To: <20240322102257.ovv5kpnm4zsgtf7n@ws.net.home>
On 3/22/24 11:22, Karel Zak wrote:
> On Wed, Mar 20, 2024 at 11:53:05PM +0100, Bernhard Voelker wrote:>> On userland OTOH, we have broader choice.
>> Karel did his choice in util-linux for exch(1), and coreutils could expose
>> the same functionality.
>>
>> For other feature requests, we were much more reluctant in coreutils ... for
>> good reasons: feature bloat, maintainability, etc.
>>
>> So I'm asking myself what is different this time?
>> - The feature already exists -> util-linux.
>
> Note that we can move exch(1) from util-linux to coreutils if, at the
> end of the brainstorming session, the conclusion will be that mv(1) is
> a bad choice :-)
I'd be for that as well, because ...
>> I'm currently only 20:80 for adding it to mv(1).
>
> I think the functionality will be lost in the mv(1) for many users.
... that's a fair point.
The code for the functionality is in copy.c, so - as with mv.c/cp.c/install.c -
we could have a exch.c using just that part, and thus expose a clearer interface
to the users.
Have a nice day,
Berny
^ 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