diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml
index aea7116e29..4f37c22b75 100644
--- a/man/systemd.exec.xml
+++ b/man/systemd.exec.xml
@@ -1140,10 +1140,11 @@ CapabilityBoundingSet=~CAP_B CAP_C
IOSchedulingClass=
- Sets the I/O scheduling class for executed processes. Takes an integer between 0 and 3 or one
- of the strings , , or
- . If the empty string is assigned to this option, all prior assignments to both
- IOSchedulingClass= and IOSchedulingPriority= have no effect. See
+ Sets the I/O scheduling class for executed processes. Takes one of the strings
+ , or . The kernel's
+ default scheduling class is at a priority of 4. If the empty string is
+ assigned to this option, all prior assignments to both IOSchedulingClass= and
+ IOSchedulingPriority= have no effect. See
ioprio_set2 for
details.
@@ -1156,7 +1157,8 @@ CapabilityBoundingSet=~CAP_B CAP_C
bandwidth is made available to the unit's processes, larger values mean less bandwidth. The available
priorities depend on the selected I/O scheduling class (see above). If the empty string is assigned
to this option, all prior assignments to both IOSchedulingClass= and
- IOSchedulingPriority= have no effect. See
+ IOSchedulingPriority= have no effect. For the kernel's default scheduling class
+ () this defaults to 4. See
ioprio_set2 for
details.
diff --git a/src/basic/ioprio-util.c b/src/basic/ioprio-util.c
new file mode 100644
index 0000000000..b63650b80b
--- /dev/null
+++ b/src/basic/ioprio-util.c
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "ioprio-util.h"
+#include "parse-util.h"
+#include "string-table.h"
+
+int ioprio_parse_priority(const char *s, int *ret) {
+ int i, r;
+
+ assert(s);
+ assert(ret);
+
+ r = safe_atoi(s, &i);
+ if (r < 0)
+ return r;
+
+ if (!ioprio_priority_is_valid(i))
+ return -EINVAL;
+
+ *ret = i;
+ return 0;
+}
+
+static const char *const ioprio_class_table[] = {
+ [IOPRIO_CLASS_NONE] = "none",
+ [IOPRIO_CLASS_RT] = "realtime",
+ [IOPRIO_CLASS_BE] = "best-effort",
+ [IOPRIO_CLASS_IDLE] = "idle",
+};
+
+DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ioprio_class, int, IOPRIO_N_CLASSES);
diff --git a/src/basic/ioprio-util.h b/src/basic/ioprio-util.h
new file mode 100644
index 0000000000..b8c9b7dd59
--- /dev/null
+++ b/src/basic/ioprio-util.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "macro.h"
+#include "missing_ioprio.h"
+
+int ioprio_class_to_string_alloc(int i, char **s);
+int ioprio_class_from_string(const char *s);
+
+static inline bool ioprio_class_is_valid(int i) {
+ return IN_SET(i, IOPRIO_CLASS_NONE, IOPRIO_CLASS_RT, IOPRIO_CLASS_BE, IOPRIO_CLASS_IDLE);
+}
+
+static inline bool ioprio_priority_is_valid(int i) {
+ return i >= 0 && i < IOPRIO_BE_NR;
+}
+
+int ioprio_parse_priority(const char *s, int *ret);
+
+/* IOPRIO_CLASS_NONE with any prio value is another way to say IOPRIO_CLASS_BE with level 4. Encode that in a
+ * proper macro. */
+#define IOPRIO_DEFAULT_CLASS_AND_PRIO ioprio_prio_value(IOPRIO_CLASS_BE, 4)
+
+static inline int ioprio_normalize(int v) {
+ /* Converts IOPRIO_CLASS_NONE to what it actually means */
+ return ioprio_prio_class(v) == IOPRIO_CLASS_NONE ? IOPRIO_DEFAULT_CLASS_AND_PRIO : v;
+}
diff --git a/src/basic/meson.build b/src/basic/meson.build
index f0d5a1d2ea..bc90100c34 100644
--- a/src/basic/meson.build
+++ b/src/basic/meson.build
@@ -82,6 +82,8 @@ basic_sources = files('''
inotify-util.h
io-util.c
io-util.h
+ ioprio-util.c
+ ioprio-util.h
limits-util.c
limits-util.h
linux/btrfs.h
diff --git a/src/basic/process-util.c b/src/basic/process-util.c
index bea53ccb7d..1dd2414bb2 100644
--- a/src/basic/process-util.c
+++ b/src/basic/process-util.c
@@ -1132,23 +1132,6 @@ int pid_compare_func(const pid_t *a, const pid_t *b) {
return CMP(*a, *b);
}
-int ioprio_parse_priority(const char *s, int *ret) {
- int i, r;
-
- assert(s);
- assert(ret);
-
- r = safe_atoi(s, &i);
- if (r < 0)
- return r;
-
- if (!ioprio_priority_is_valid(i))
- return -EINVAL;
-
- *ret = i;
- return 0;
-}
-
/* The cached PID, possible values:
*
* == UNSET [0] → cache not initialized yet
@@ -1627,14 +1610,6 @@ _noreturn_ void freeze(void) {
pause();
}
-static const char *const ioprio_class_table[] = {
- [IOPRIO_CLASS_NONE] = "none",
- [IOPRIO_CLASS_RT] = "realtime",
- [IOPRIO_CLASS_BE] = "best-effort",
- [IOPRIO_CLASS_IDLE] = "idle",
-};
-
-DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ioprio_class, int, IOPRIO_N_CLASSES);
static const char *const sigchld_code_table[] = {
[CLD_EXITED] = "exited",
diff --git a/src/basic/process-util.h b/src/basic/process-util.h
index 25dea35ce6..c312e05d9c 100644
--- a/src/basic/process-util.h
+++ b/src/basic/process-util.h
@@ -14,7 +14,6 @@
#include "alloc-util.h"
#include "format-util.h"
#include "macro.h"
-#include "missing_ioprio.h"
#include "time-util.h"
#define procfs_file_alloca(pid, field) \
@@ -97,9 +96,6 @@ const char *personality_to_string(unsigned long);
int safe_personality(unsigned long p);
int opinionated_personality(unsigned long *ret);
-int ioprio_class_to_string_alloc(int i, char **s);
-int ioprio_class_from_string(const char *s);
-
const char *sigchld_code_to_string(int i) _const_;
int sigchld_code_from_string(const char *s) _pure_;
@@ -130,20 +126,10 @@ static inline bool sched_priority_is_valid(int i) {
return i >= 0 && i <= sched_get_priority_max(SCHED_RR);
}
-static inline bool ioprio_class_is_valid(int i) {
- return IN_SET(i, IOPRIO_CLASS_NONE, IOPRIO_CLASS_RT, IOPRIO_CLASS_BE, IOPRIO_CLASS_IDLE);
-}
-
-static inline bool ioprio_priority_is_valid(int i) {
- return i >= 0 && i < IOPRIO_BE_NR;
-}
-
static inline bool pid_is_valid(pid_t p) {
return p > 0;
}
-int ioprio_parse_priority(const char *s, int *ret);
-
pid_t getpid_cached(void);
void reset_cached_pid(void);
diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c
index afb2d8f10f..5c499e5d06 100644
--- a/src/core/dbus-execute.c
+++ b/src/core/dbus-execute.c
@@ -24,6 +24,7 @@
#include "fileio.h"
#include "hexdecoct.h"
#include "io-util.h"
+#include "ioprio-util.h"
#include "journal-file.h"
#include "missing_ioprio.h"
#include "mountpoint-util.h"
@@ -2810,7 +2811,7 @@ int bus_exec_context_set_transient_property(
if (r < 0)
return r;
- c->ioprio = ioprio_prio_value(q, ioprio_prio_data(c->ioprio));
+ c->ioprio = ioprio_normalize(ioprio_prio_value(q, ioprio_prio_data(c->ioprio)));
c->ioprio_set = true;
unit_write_settingf(u, flags, name, "IOSchedulingClass=%s", s);
@@ -2829,7 +2830,7 @@ int bus_exec_context_set_transient_property(
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid IO scheduling priority: %i", p);
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- c->ioprio = ioprio_prio_value(ioprio_prio_class(c->ioprio), p);
+ c->ioprio = ioprio_normalize(ioprio_prio_value(ioprio_prio_class(c->ioprio), p));
c->ioprio_set = true;
unit_write_settingf(u, flags, name, "IOSchedulingPriority=%i", p);
diff --git a/src/core/execute.c b/src/core/execute.c
index 39c5031065..e3b9134c3d 100644
--- a/src/core/execute.c
+++ b/src/core/execute.c
@@ -63,6 +63,7 @@
#include "glob-util.h"
#include "hexdecoct.h"
#include "io-util.h"
+#include "ioprio-util.h"
#include "label.h"
#include "log.h"
#include "macro.h"
@@ -5068,7 +5069,7 @@ void exec_context_init(ExecContext *c) {
assert(c);
c->umask = 0022;
- c->ioprio = ioprio_prio_value(IOPRIO_CLASS_BE, 0);
+ c->ioprio = IOPRIO_DEFAULT_CLASS_AND_PRIO;
c->cpu_sched_policy = SCHED_OTHER;
c->syslog_priority = LOG_DAEMON|LOG_INFO;
c->syslog_level_prefix = true;
@@ -6020,9 +6021,9 @@ int exec_context_get_effective_ioprio(const ExecContext *c) {
p = ioprio_get(IOPRIO_WHO_PROCESS, 0);
if (p < 0)
- return ioprio_prio_value(IOPRIO_CLASS_BE, 4);
+ return IOPRIO_DEFAULT_CLASS_AND_PRIO;
- return p;
+ return ioprio_normalize(p);
}
bool exec_context_get_effective_mount_apivfs(const ExecContext *c) {
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index 6efcba3265..92a52819e2 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -40,6 +40,7 @@
#include "fs-util.h"
#include "hexdecoct.h"
#include "io-util.h"
+#include "ioprio-util.h"
#include "ip-protocol-list.h"
#include "journal-file.h"
#include "limits-util.h"
@@ -1416,7 +1417,7 @@ int config_parse_exec_io_class(const char *unit,
if (isempty(rvalue)) {
c->ioprio_set = false;
- c->ioprio = ioprio_prio_value(IOPRIO_CLASS_BE, 0);
+ c->ioprio = IOPRIO_DEFAULT_CLASS_AND_PRIO;
return 0;
}
@@ -1426,7 +1427,7 @@ int config_parse_exec_io_class(const char *unit,
return 0;
}
- c->ioprio = ioprio_prio_value(x, ioprio_prio_data(c->ioprio));
+ c->ioprio = ioprio_normalize(ioprio_prio_value(x, ioprio_prio_data(c->ioprio)));
c->ioprio_set = true;
return 0;
@@ -1453,7 +1454,7 @@ int config_parse_exec_io_priority(const char *unit,
if (isempty(rvalue)) {
c->ioprio_set = false;
- c->ioprio = ioprio_prio_value(IOPRIO_CLASS_BE, 0);
+ c->ioprio = IOPRIO_DEFAULT_CLASS_AND_PRIO;
return 0;
}
@@ -1463,7 +1464,7 @@ int config_parse_exec_io_priority(const char *unit,
return 0;
}
- c->ioprio = ioprio_prio_value(ioprio_prio_class(c->ioprio), i);
+ c->ioprio = ioprio_normalize(ioprio_prio_value(ioprio_prio_class(c->ioprio), i));
c->ioprio_set = true;
return 0;
diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c
index b8fd85a191..c58394e63e 100644
--- a/src/shared/bus-unit-util.c
+++ b/src/shared/bus-unit-util.c
@@ -19,6 +19,7 @@
#include "hexdecoct.h"
#include "hostname-util.h"
#include "in-addr-util.h"
+#include "ioprio-util.h"
#include "ip-protocol-list.h"
#include "libmount-util.h"
#include "locale-util.h"
diff --git a/src/test/test-process-util.c b/src/test/test-process-util.c
index b87a24b342..cc0f870e57 100644
--- a/src/test/test-process-util.c
+++ b/src/test/test-process-util.c
@@ -19,6 +19,7 @@
#include "errno-list.h"
#include "errno-util.h"
#include "fd-util.h"
+#include "ioprio-util.h"
#include "log.h"
#include "macro.h"
#include "missing_sched.h"
@@ -725,32 +726,38 @@ static void test_pid_to_ptr(void) {
assert_se(PTR_TO_PID(PID_TO_PTR(INT32_MIN)) == INT32_MIN);
}
-static void test_ioprio_class_from_to_string_one(const char *val, int expected) {
+static void test_ioprio_class_from_to_string_one(const char *val, int expected, int normalized) {
assert_se(ioprio_class_from_string(val) == expected);
if (expected >= 0) {
_cleanup_free_ char *s = NULL;
unsigned ret;
+ int combined;
assert_se(ioprio_class_to_string_alloc(expected, &s) == 0);
- /* We sometimes get a class number and sometimes a number back */
+ /* We sometimes get a class number and sometimes a name back */
assert_se(streq(s, val) ||
safe_atou(val, &ret) == 0);
+
+ /* Make sure normalization works, i.e. NONE → BE gets normalized */
+ combined = ioprio_normalize(ioprio_prio_value(expected, 0));
+ assert_se(ioprio_prio_class(combined) == normalized);
+ assert_se(expected != IOPRIO_CLASS_NONE || ioprio_prio_data(combined) == 4);
}
}
static void test_ioprio_class_from_to_string(void) {
log_info("/* %s */", __func__);
- test_ioprio_class_from_to_string_one("none", IOPRIO_CLASS_NONE);
- test_ioprio_class_from_to_string_one("realtime", IOPRIO_CLASS_RT);
- test_ioprio_class_from_to_string_one("best-effort", IOPRIO_CLASS_BE);
- test_ioprio_class_from_to_string_one("idle", IOPRIO_CLASS_IDLE);
- test_ioprio_class_from_to_string_one("0", 0);
- test_ioprio_class_from_to_string_one("1", 1);
- test_ioprio_class_from_to_string_one("7", 7);
- test_ioprio_class_from_to_string_one("8", 8);
- test_ioprio_class_from_to_string_one("9", -EINVAL);
- test_ioprio_class_from_to_string_one("-1", -EINVAL);
+ test_ioprio_class_from_to_string_one("none", IOPRIO_CLASS_NONE, IOPRIO_CLASS_BE);
+ test_ioprio_class_from_to_string_one("realtime", IOPRIO_CLASS_RT, IOPRIO_CLASS_RT);
+ test_ioprio_class_from_to_string_one("best-effort", IOPRIO_CLASS_BE, IOPRIO_CLASS_BE);
+ test_ioprio_class_from_to_string_one("idle", IOPRIO_CLASS_IDLE, IOPRIO_CLASS_IDLE);
+ test_ioprio_class_from_to_string_one("0", IOPRIO_CLASS_NONE, IOPRIO_CLASS_BE);
+ test_ioprio_class_from_to_string_one("1", 1, 1);
+ test_ioprio_class_from_to_string_one("7", 7, 7);
+ test_ioprio_class_from_to_string_one("8", 8, 8);
+ test_ioprio_class_from_to_string_one("9", -EINVAL, -EINVAL);
+ test_ioprio_class_from_to_string_one("-1", -EINVAL, -EINVAL);
}
static void test_setpriority_closest(void) {
diff --git a/test/test-execute/exec-ioschedulingclass-none.service b/test/test-execute/exec-ioschedulingclass-none.service
index efe3182261..0494d45897 100644
--- a/test/test-execute/exec-ioschedulingclass-none.service
+++ b/test/test-execute/exec-ioschedulingclass-none.service
@@ -3,6 +3,7 @@
Description=Test for IOSchedulingClass=none
[Service]
-ExecStart=/bin/sh -x -c 'c=$$(LC_ALL=C ionice); test "$${c%%:*}" = "none"'
+# Old kernels might report "none" here, new kernels "best-effort".
+ExecStart=/bin/sh -x -c 'c=$$(LC_ALL=C ionice); test "$${c%%:*}" = "none" -o "$${c%%:*}" = "best-effort"'
Type=oneshot
IOSchedulingClass=none