mirror of
https://github.com/systemd/systemd.git
synced 2024-11-30 22:03:41 +08:00
Merge pull request #18659 from poettering/permyriadification
use scaling relative to UINT32_MAX instead of percentages/permyriads wherever we can
This commit is contained in:
commit
5be5d1f2a1
@ -48,36 +48,38 @@
|
||||
|
||||
<variablelist class='config-directives'>
|
||||
<varlistentry>
|
||||
<term><varname>SwapUsedLimitPercent=</varname></term>
|
||||
<term><varname>SwapUsedLimit=</varname></term>
|
||||
|
||||
<listitem><para>Sets the limit for swap usage on the system before <command>systemd-oomd</command> will
|
||||
take action. If the percentage of swap used on the system is more than what is defined here,
|
||||
<command>systemd-oomd</command> will act on eligible descendant cgroups, starting from the ones with the
|
||||
highest swap usage to the lowest swap usage. Which cgroups are monitored and what
|
||||
action gets taken depends on what the unit has configured for <varname>ManagedOOMSwap=</varname>.
|
||||
Takes a percentage value between 0% and 100%, inclusive. Defaults to 90%.</para></listitem>
|
||||
<listitem><para>Sets the limit for swap usage on the system before <command>systemd-oomd</command>
|
||||
will take action. If the fraction of swap used on the system is more than what is defined here,
|
||||
<command>systemd-oomd</command> will act on eligible descendant control groups, starting from the
|
||||
ones with the highest swap usage to the lowest swap usage. Which control groups are monitored and
|
||||
what action gets taken depends on what the unit has configured for
|
||||
<varname>ManagedOOMSwap=</varname>. Takes a value specified in percent (when suffixed with "%"),
|
||||
permille ("‰") or permyriad ("‱"), between 0% and 100%, inclusive. Defaults to 90%.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>DefaultMemoryPressureLimit=</varname></term>
|
||||
|
||||
<listitem><para>Sets the limit for memory pressure on the unit's cgroup before <command>systemd-oomd</command>
|
||||
will take action. A unit can override this value with <varname>ManagedOOMMemoryPressureLimit=</varname>.
|
||||
The memory pressure for this property represents the fraction of time in a 10 second window in which all tasks
|
||||
in the cgroup were delayed. For each monitored cgroup, if the memory pressure on that cgroup exceeds the
|
||||
limit set for longer than the duration set by <varname>DefaultMemoryPressureDurationSec=</varname>,
|
||||
<command>systemd-oomd</command> will act on eligible descendant cgroups,
|
||||
starting from the ones with the most reclaim activity to the least reclaim activity. Which cgroups are
|
||||
monitored and what action gets taken depends on what the unit has configured for
|
||||
<varname>ManagedOOMMemoryPressure=</varname>. Takes a percentage value between 0% and 100%, inclusive.
|
||||
Defaults to 60%.</para></listitem>
|
||||
<listitem><para>Sets the limit for memory pressure on the unit's control group before
|
||||
<command>systemd-oomd</command> will take action. A unit can override this value with
|
||||
<varname>ManagedOOMMemoryPressureLimit=</varname>. The memory pressure for this property represents
|
||||
the fraction of time in a 10 second window in which all tasks in the control group were delayed. For
|
||||
each monitored control group, if the memory pressure on that control group exceeds the limit set for
|
||||
longer than the duration set by <varname>DefaultMemoryPressureDurationSec=</varname>,
|
||||
<command>systemd-oomd</command> will act on eligible descendant control groups, starting from the
|
||||
ones with the most reclaim activity to the least reclaim activity. Which control groups are monitored
|
||||
and what action gets taken depends on what the unit has configured for
|
||||
<varname>ManagedOOMMemoryPressure=</varname>. Takes a fraction specified in the same way as
|
||||
<varname>SwapUsedLimit=</varname> above. Defaults to 60%.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>DefaultMemoryPressureDurationSec=</varname></term>
|
||||
|
||||
<listitem><para>Sets the amount of time a unit's cgroup needs to have exceeded memory pressure limits before
|
||||
<command>systemd-oomd</command> will take action. Memory pressure limits are defined by
|
||||
<listitem><para>Sets the amount of time a unit's control group needs to have exceeded memory pressure
|
||||
limits before <command>systemd-oomd</command> will take action. Memory pressure limits are defined by
|
||||
<varname>DefaultMemoryPressureLimit=</varname> and <varname>ManagedOOMMemoryPressureLimit=</varname>.
|
||||
Defaults to 30 seconds when this property is unset or set to 0.</para></listitem>
|
||||
</varlistentry>
|
||||
|
@ -2469,7 +2469,7 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly s ManagedOOMMemoryPressure = '...';
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly u ManagedOOMMemoryPressureLimitPermyriad = ...;
|
||||
readonly u ManagedOOMMemoryPressureLimit = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly s ManagedOOMPreference = '...';
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
@ -2994,7 +2994,7 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
|
||||
|
||||
<!--property ManagedOOMMemoryPressure is not documented!-->
|
||||
|
||||
<!--property ManagedOOMMemoryPressureLimitPermyriad is not documented!-->
|
||||
<!--property ManagedOOMMemoryPressureLimit is not documented!-->
|
||||
|
||||
<!--property ManagedOOMPreference is not documented!-->
|
||||
|
||||
@ -3560,7 +3560,7 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="ManagedOOMMemoryPressure"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="ManagedOOMMemoryPressureLimitPermyriad"/>
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="ManagedOOMMemoryPressureLimit"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="ManagedOOMPreference"/>
|
||||
|
||||
@ -4229,7 +4229,7 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly s ManagedOOMMemoryPressure = '...';
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly u ManagedOOMMemoryPressureLimitPermyriad = ...;
|
||||
readonly u ManagedOOMMemoryPressureLimit = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly s ManagedOOMPreference = '...';
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
@ -4782,7 +4782,7 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
|
||||
|
||||
<!--property ManagedOOMMemoryPressure is not documented!-->
|
||||
|
||||
<!--property ManagedOOMMemoryPressureLimitPermyriad is not documented!-->
|
||||
<!--property ManagedOOMMemoryPressureLimit is not documented!-->
|
||||
|
||||
<!--property ManagedOOMPreference is not documented!-->
|
||||
|
||||
@ -5346,7 +5346,7 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="ManagedOOMMemoryPressure"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="ManagedOOMMemoryPressureLimitPermyriad"/>
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="ManagedOOMMemoryPressureLimit"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="ManagedOOMPreference"/>
|
||||
|
||||
@ -5928,7 +5928,7 @@ node /org/freedesktop/systemd1/unit/home_2emount {
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly s ManagedOOMMemoryPressure = '...';
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly u ManagedOOMMemoryPressureLimitPermyriad = ...;
|
||||
readonly u ManagedOOMMemoryPressureLimit = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly s ManagedOOMPreference = '...';
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
@ -6409,7 +6409,7 @@ node /org/freedesktop/systemd1/unit/home_2emount {
|
||||
|
||||
<!--property ManagedOOMMemoryPressure is not documented!-->
|
||||
|
||||
<!--property ManagedOOMMemoryPressureLimitPermyriad is not documented!-->
|
||||
<!--property ManagedOOMMemoryPressureLimit is not documented!-->
|
||||
|
||||
<!--property ManagedOOMPreference is not documented!-->
|
||||
|
||||
@ -6891,7 +6891,7 @@ node /org/freedesktop/systemd1/unit/home_2emount {
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="ManagedOOMMemoryPressure"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="ManagedOOMMemoryPressureLimitPermyriad"/>
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="ManagedOOMMemoryPressureLimit"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="ManagedOOMPreference"/>
|
||||
|
||||
@ -7594,7 +7594,7 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly s ManagedOOMMemoryPressure = '...';
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly u ManagedOOMMemoryPressureLimitPermyriad = ...;
|
||||
readonly u ManagedOOMMemoryPressureLimit = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly s ManagedOOMPreference = '...';
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
@ -8061,7 +8061,7 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
|
||||
|
||||
<!--property ManagedOOMMemoryPressure is not documented!-->
|
||||
|
||||
<!--property ManagedOOMMemoryPressureLimitPermyriad is not documented!-->
|
||||
<!--property ManagedOOMMemoryPressureLimit is not documented!-->
|
||||
|
||||
<!--property ManagedOOMPreference is not documented!-->
|
||||
|
||||
@ -8529,7 +8529,7 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="ManagedOOMMemoryPressure"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="ManagedOOMMemoryPressureLimitPermyriad"/>
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="ManagedOOMMemoryPressureLimit"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="ManagedOOMPreference"/>
|
||||
|
||||
@ -9085,7 +9085,7 @@ node /org/freedesktop/systemd1/unit/system_2eslice {
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly s ManagedOOMMemoryPressure = '...';
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly u ManagedOOMMemoryPressureLimitPermyriad = ...;
|
||||
readonly u ManagedOOMMemoryPressureLimit = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly s ManagedOOMPreference = '...';
|
||||
};
|
||||
@ -9222,7 +9222,7 @@ node /org/freedesktop/systemd1/unit/system_2eslice {
|
||||
|
||||
<!--property ManagedOOMMemoryPressure is not documented!-->
|
||||
|
||||
<!--property ManagedOOMMemoryPressureLimitPermyriad is not documented!-->
|
||||
<!--property ManagedOOMMemoryPressureLimit is not documented!-->
|
||||
|
||||
<!--property ManagedOOMPreference is not documented!-->
|
||||
|
||||
@ -9364,7 +9364,7 @@ node /org/freedesktop/systemd1/unit/system_2eslice {
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="ManagedOOMMemoryPressure"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="ManagedOOMMemoryPressureLimitPermyriad"/>
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="ManagedOOMMemoryPressureLimit"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="ManagedOOMPreference"/>
|
||||
|
||||
@ -9526,7 +9526,7 @@ node /org/freedesktop/systemd1/unit/session_2d1_2escope {
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly s ManagedOOMMemoryPressure = '...';
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly u ManagedOOMMemoryPressureLimitPermyriad = ...;
|
||||
readonly u ManagedOOMMemoryPressureLimit = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly s ManagedOOMPreference = '...';
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
@ -9679,7 +9679,7 @@ node /org/freedesktop/systemd1/unit/session_2d1_2escope {
|
||||
|
||||
<!--property ManagedOOMMemoryPressure is not documented!-->
|
||||
|
||||
<!--property ManagedOOMMemoryPressureLimitPermyriad is not documented!-->
|
||||
<!--property ManagedOOMMemoryPressureLimit is not documented!-->
|
||||
|
||||
<!--property ManagedOOMPreference is not documented!-->
|
||||
|
||||
@ -9847,7 +9847,7 @@ node /org/freedesktop/systemd1/unit/session_2d1_2escope {
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="ManagedOOMMemoryPressure"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="ManagedOOMMemoryPressureLimitPermyriad"/>
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="ManagedOOMMemoryPressureLimit"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="ManagedOOMPreference"/>
|
||||
|
||||
|
@ -77,7 +77,13 @@ uint64_t physical_memory(void) {
|
||||
}
|
||||
|
||||
uint64_t physical_memory_scale(uint64_t v, uint64_t max) {
|
||||
uint64_t p, m, ps, r;
|
||||
uint64_t p, m, ps;
|
||||
|
||||
/* Shortcut two special cases */
|
||||
if (v == 0)
|
||||
return 0;
|
||||
if (v == max)
|
||||
return physical_memory();
|
||||
|
||||
assert(max > 0);
|
||||
|
||||
@ -90,17 +96,16 @@ uint64_t physical_memory_scale(uint64_t v, uint64_t max) {
|
||||
p = physical_memory() / ps;
|
||||
assert(p > 0);
|
||||
|
||||
m = p * v;
|
||||
if (m / p != v)
|
||||
if (v > UINT64_MAX / p)
|
||||
return UINT64_MAX;
|
||||
|
||||
m = p * v;
|
||||
m /= max;
|
||||
|
||||
r = m * ps;
|
||||
if (r / ps != m)
|
||||
if (m > UINT64_MAX / ps)
|
||||
return UINT64_MAX;
|
||||
|
||||
return r;
|
||||
return m * ps;
|
||||
}
|
||||
|
||||
uint64_t system_tasks_max(void) {
|
||||
@ -138,6 +143,12 @@ uint64_t system_tasks_max(void) {
|
||||
uint64_t system_tasks_max_scale(uint64_t v, uint64_t max) {
|
||||
uint64_t t, m;
|
||||
|
||||
/* Shortcut two special cases */
|
||||
if (v == 0)
|
||||
return 0;
|
||||
if (v == max)
|
||||
return system_tasks_max();
|
||||
|
||||
assert(max > 0);
|
||||
|
||||
/* Multiply the system's task value by the fraction v/max. Hence, if max==100 this calculates percentages
|
||||
@ -146,9 +157,9 @@ uint64_t system_tasks_max_scale(uint64_t v, uint64_t max) {
|
||||
t = system_tasks_max();
|
||||
assert(t > 0);
|
||||
|
||||
m = t * v;
|
||||
if (m / t != v) /* overflow? */
|
||||
if (v > UINT64_MAX / t) /* overflow? */
|
||||
return UINT64_MAX;
|
||||
|
||||
m = t * v;
|
||||
return m / max;
|
||||
}
|
||||
|
@ -176,6 +176,8 @@ basic_sources = files('''
|
||||
path-lookup.h
|
||||
path-util.c
|
||||
path-util.h
|
||||
percent-util.c
|
||||
percent-util.h
|
||||
prioq.c
|
||||
prioq.h
|
||||
proc-cmdline.c
|
||||
|
@ -627,146 +627,6 @@ int parse_fractional_part_u(const char **p, size_t digits, unsigned *res) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_parts_value_whole(const char *p, const char *symbol) {
|
||||
const char *pc, *n;
|
||||
int r, v;
|
||||
|
||||
pc = endswith(p, symbol);
|
||||
if (!pc)
|
||||
return -EINVAL;
|
||||
|
||||
n = strndupa(p, pc - p);
|
||||
r = safe_atoi(n, &v);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (v < 0)
|
||||
return -ERANGE;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
static int parse_parts_value_with_tenths_place(const char *p, const char *symbol) {
|
||||
const char *pc, *dot, *n;
|
||||
int r, q, v;
|
||||
|
||||
pc = endswith(p, symbol);
|
||||
if (!pc)
|
||||
return -EINVAL;
|
||||
|
||||
dot = memchr(p, '.', pc - p);
|
||||
if (dot) {
|
||||
if (dot + 2 != pc)
|
||||
return -EINVAL;
|
||||
if (dot[1] < '0' || dot[1] > '9')
|
||||
return -EINVAL;
|
||||
q = dot[1] - '0';
|
||||
n = strndupa(p, dot - p);
|
||||
} else {
|
||||
q = 0;
|
||||
n = strndupa(p, pc - p);
|
||||
}
|
||||
r = safe_atoi(n, &v);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (v < 0)
|
||||
return -ERANGE;
|
||||
if (v > (INT_MAX - q) / 10)
|
||||
return -ERANGE;
|
||||
|
||||
v = v * 10 + q;
|
||||
return v;
|
||||
}
|
||||
|
||||
static int parse_parts_value_with_hundredths_place(const char *p, const char *symbol) {
|
||||
const char *pc, *dot, *n;
|
||||
int r, q, v;
|
||||
|
||||
pc = endswith(p, symbol);
|
||||
if (!pc)
|
||||
return -EINVAL;
|
||||
|
||||
dot = memchr(p, '.', pc - p);
|
||||
if (dot) {
|
||||
if (dot + 3 != pc)
|
||||
return -EINVAL;
|
||||
if (dot[1] < '0' || dot[1] > '9' || dot[2] < '0' || dot[2] > '9')
|
||||
return -EINVAL;
|
||||
q = (dot[1] - '0') * 10 + (dot[2] - '0');
|
||||
n = strndupa(p, dot - p);
|
||||
} else {
|
||||
q = 0;
|
||||
n = strndupa(p, pc - p);
|
||||
}
|
||||
r = safe_atoi(n, &v);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (v < 0)
|
||||
return -ERANGE;
|
||||
if (v > (INT_MAX - q) / 100)
|
||||
return -ERANGE;
|
||||
|
||||
v = v * 100 + q;
|
||||
return v;
|
||||
}
|
||||
|
||||
int parse_percent_unbounded(const char *p) {
|
||||
return parse_parts_value_whole(p, "%");
|
||||
}
|
||||
|
||||
int parse_percent(const char *p) {
|
||||
int v;
|
||||
|
||||
v = parse_percent_unbounded(p);
|
||||
if (v > 100)
|
||||
return -ERANGE;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
int parse_permille_unbounded(const char *p) {
|
||||
const char *pm;
|
||||
|
||||
pm = endswith(p, "‰");
|
||||
if (pm)
|
||||
return parse_parts_value_whole(p, "‰");
|
||||
|
||||
return parse_parts_value_with_tenths_place(p, "%");
|
||||
}
|
||||
|
||||
int parse_permille(const char *p) {
|
||||
int v;
|
||||
|
||||
v = parse_permille_unbounded(p);
|
||||
if (v > 1000)
|
||||
return -ERANGE;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
int parse_permyriad_unbounded(const char *p) {
|
||||
const char *pm;
|
||||
|
||||
pm = endswith(p, "‱");
|
||||
if (pm)
|
||||
return parse_parts_value_whole(p, "‱");
|
||||
|
||||
pm = endswith(p, "‰");
|
||||
if (pm)
|
||||
return parse_parts_value_with_tenths_place(p, "‰");
|
||||
|
||||
return parse_parts_value_with_hundredths_place(p, "%");
|
||||
}
|
||||
|
||||
int parse_permyriad(const char *p) {
|
||||
int v;
|
||||
|
||||
v = parse_permyriad_unbounded(p);
|
||||
if (v > 10000)
|
||||
return -ERANGE;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
int parse_nice(const char *p, int *ret) {
|
||||
int n, r;
|
||||
|
||||
|
@ -127,15 +127,6 @@ int safe_atod(const char *s, double *ret_d);
|
||||
|
||||
int parse_fractional_part_u(const char **s, size_t digits, unsigned *res);
|
||||
|
||||
int parse_percent_unbounded(const char *p);
|
||||
int parse_percent(const char *p);
|
||||
|
||||
int parse_permille_unbounded(const char *p);
|
||||
int parse_permille(const char *p);
|
||||
|
||||
int parse_permyriad_unbounded(const char *p);
|
||||
int parse_permyriad(const char *p);
|
||||
|
||||
int parse_nice(const char *p, int *ret);
|
||||
|
||||
int parse_ip_port(const char *s, uint16_t *ret);
|
||||
|
157
src/basic/percent-util.c
Normal file
157
src/basic/percent-util.c
Normal file
@ -0,0 +1,157 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "percent-util.h"
|
||||
#include "string-util.h"
|
||||
#include "parse-util.h"
|
||||
|
||||
static int parse_parts_value_whole(const char *p, const char *symbol) {
|
||||
const char *pc, *n;
|
||||
int r, v;
|
||||
|
||||
pc = endswith(p, symbol);
|
||||
if (!pc)
|
||||
return -EINVAL;
|
||||
|
||||
n = strndupa(p, pc - p);
|
||||
r = safe_atoi(n, &v);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (v < 0)
|
||||
return -ERANGE;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
static int parse_parts_value_with_tenths_place(const char *p, const char *symbol) {
|
||||
const char *pc, *dot, *n;
|
||||
int r, q, v;
|
||||
|
||||
pc = endswith(p, symbol);
|
||||
if (!pc)
|
||||
return -EINVAL;
|
||||
|
||||
dot = memchr(p, '.', pc - p);
|
||||
if (dot) {
|
||||
if (dot + 2 != pc)
|
||||
return -EINVAL;
|
||||
if (dot[1] < '0' || dot[1] > '9')
|
||||
return -EINVAL;
|
||||
q = dot[1] - '0';
|
||||
n = strndupa(p, dot - p);
|
||||
} else {
|
||||
q = 0;
|
||||
n = strndupa(p, pc - p);
|
||||
}
|
||||
r = safe_atoi(n, &v);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (v < 0)
|
||||
return -ERANGE;
|
||||
if (v > (INT_MAX - q) / 10)
|
||||
return -ERANGE;
|
||||
|
||||
v = v * 10 + q;
|
||||
return v;
|
||||
}
|
||||
|
||||
static int parse_parts_value_with_hundredths_place(const char *p, const char *symbol) {
|
||||
const char *pc, *dot, *n;
|
||||
int r, q, v;
|
||||
|
||||
pc = endswith(p, symbol);
|
||||
if (!pc)
|
||||
return -EINVAL;
|
||||
|
||||
dot = memchr(p, '.', pc - p);
|
||||
if (dot) {
|
||||
if (dot + 3 == pc) {
|
||||
/* Support two places after the dot */
|
||||
|
||||
if (dot[1] < '0' || dot[1] > '9' || dot[2] < '0' || dot[2] > '9')
|
||||
return -EINVAL;
|
||||
q = (dot[1] - '0') * 10 + (dot[2] - '0');
|
||||
|
||||
} else if (dot + 2 == pc) {
|
||||
/* Support one place after the dot */
|
||||
|
||||
if (dot[1] < '0' || dot[1] > '9')
|
||||
return -EINVAL;
|
||||
q = (dot[1] - '0') * 10;
|
||||
} else
|
||||
/* We do not support zero or more than two places */
|
||||
return -EINVAL;
|
||||
|
||||
n = strndupa(p, dot - p);
|
||||
} else {
|
||||
q = 0;
|
||||
n = strndupa(p, pc - p);
|
||||
}
|
||||
r = safe_atoi(n, &v);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (v < 0)
|
||||
return -ERANGE;
|
||||
if (v > (INT_MAX - q) / 100)
|
||||
return -ERANGE;
|
||||
|
||||
v = v * 100 + q;
|
||||
return v;
|
||||
}
|
||||
|
||||
int parse_percent_unbounded(const char *p) {
|
||||
return parse_parts_value_whole(p, "%");
|
||||
}
|
||||
|
||||
int parse_percent(const char *p) {
|
||||
int v;
|
||||
|
||||
v = parse_percent_unbounded(p);
|
||||
if (v > 100)
|
||||
return -ERANGE;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
int parse_permille_unbounded(const char *p) {
|
||||
const char *pm;
|
||||
|
||||
pm = endswith(p, "‰");
|
||||
if (pm)
|
||||
return parse_parts_value_whole(p, "‰");
|
||||
|
||||
return parse_parts_value_with_tenths_place(p, "%");
|
||||
}
|
||||
|
||||
int parse_permille(const char *p) {
|
||||
int v;
|
||||
|
||||
v = parse_permille_unbounded(p);
|
||||
if (v > 1000)
|
||||
return -ERANGE;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
int parse_permyriad_unbounded(const char *p) {
|
||||
const char *pm;
|
||||
|
||||
pm = endswith(p, "‱");
|
||||
if (pm)
|
||||
return parse_parts_value_whole(p, "‱");
|
||||
|
||||
pm = endswith(p, "‰");
|
||||
if (pm)
|
||||
return parse_parts_value_with_tenths_place(p, "‰");
|
||||
|
||||
return parse_parts_value_with_hundredths_place(p, "%");
|
||||
}
|
||||
|
||||
int parse_permyriad(const char *p) {
|
||||
int v;
|
||||
|
||||
v = parse_permyriad_unbounded(p);
|
||||
if (v > 10000)
|
||||
return -ERANGE;
|
||||
|
||||
return v;
|
||||
}
|
64
src/basic/percent-util.h
Normal file
64
src/basic/percent-util.h
Normal file
@ -0,0 +1,64 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "macro.h"
|
||||
|
||||
int parse_percent_unbounded(const char *p);
|
||||
int parse_percent(const char *p);
|
||||
|
||||
int parse_permille_unbounded(const char *p);
|
||||
int parse_permille(const char *p);
|
||||
|
||||
int parse_permyriad_unbounded(const char *p);
|
||||
int parse_permyriad(const char *p);
|
||||
|
||||
/* Some macro-like helpers that convert a percent/permille/permyriad value (as parsed by parse_percent()) to
|
||||
* a value relative to 100% == 2^32-1. Rounds to closest. */
|
||||
static inline uint32_t UINT32_SCALE_FROM_PERCENT(int percent) {
|
||||
assert_cc(INT_MAX <= UINT32_MAX);
|
||||
return (uint32_t) (((uint64_t) percent * UINT32_MAX + 50) / 100U);
|
||||
}
|
||||
|
||||
static inline uint32_t UINT32_SCALE_FROM_PERMILLE(int permille) {
|
||||
return (uint32_t) (((uint64_t) permille * UINT32_MAX + 500) / 1000U);
|
||||
}
|
||||
|
||||
static inline uint32_t UINT32_SCALE_FROM_PERMYRIAD(int permyriad) {
|
||||
return (uint32_t) (((uint64_t) permyriad * UINT32_MAX + 5000) / 10000U);
|
||||
}
|
||||
|
||||
static inline int UINT32_SCALE_TO_PERCENT(uint32_t scale) {
|
||||
uint32_t u;
|
||||
|
||||
u = (uint32_t) ((((uint64_t) scale) * 100U + UINT32_MAX/2) / UINT32_MAX);
|
||||
if (u > INT_MAX)
|
||||
return -ERANGE;
|
||||
|
||||
return (int) u;
|
||||
}
|
||||
|
||||
static inline int UINT32_SCALE_TO_PERMILLE(uint32_t scale) {
|
||||
uint32_t u;
|
||||
|
||||
u = (uint32_t) ((((uint64_t) scale) * 1000U + UINT32_MAX/2) / UINT32_MAX);
|
||||
if (u > INT_MAX)
|
||||
return -ERANGE;
|
||||
|
||||
return (int) u;
|
||||
}
|
||||
|
||||
static inline int UINT32_SCALE_TO_PERMYRIAD(uint32_t scale) {
|
||||
uint32_t u;
|
||||
|
||||
u = (uint32_t) ((((uint64_t) scale) * 10000U + UINT32_MAX/2) / UINT32_MAX);
|
||||
if (u > INT_MAX)
|
||||
return -ERANGE;
|
||||
|
||||
return (int) u;
|
||||
}
|
||||
|
||||
#define PERMYRIAD_AS_PERCENT_FORMAT_STR "%i.%02i%%"
|
||||
#define PERMYRIAD_AS_PERCENT_FORMAT_VAL(x) ((x)/100), ((x)%100)
|
@ -21,6 +21,7 @@
|
||||
#include "nulstr-util.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
#include "percent-util.h"
|
||||
#include "process-util.h"
|
||||
#include "procfs-util.h"
|
||||
#include "special.h"
|
||||
@ -418,7 +419,7 @@ void cgroup_context_dump(Unit *u, FILE* f, const char *prefix) {
|
||||
"%sDelegate: %s\n"
|
||||
"%sManagedOOMSwap: %s\n"
|
||||
"%sManagedOOMMemoryPressure: %s\n"
|
||||
"%sManagedOOMMemoryPressureLimit: %" PRIu32 ".%02" PRIu32 "%%\n"
|
||||
"%sManagedOOMMemoryPressureLimit: " PERMYRIAD_AS_PERCENT_FORMAT_STR "\n"
|
||||
"%sManagedOOMPreference: %s%%\n",
|
||||
prefix, yes_no(c->cpu_accounting),
|
||||
prefix, yes_no(c->io_accounting),
|
||||
@ -452,7 +453,7 @@ void cgroup_context_dump(Unit *u, FILE* f, const char *prefix) {
|
||||
prefix, yes_no(c->delegate),
|
||||
prefix, managed_oom_mode_to_string(c->moom_swap),
|
||||
prefix, managed_oom_mode_to_string(c->moom_mem_pressure),
|
||||
prefix, c->moom_mem_pressure_limit_permyriad / 100, c->moom_mem_pressure_limit_permyriad % 100,
|
||||
prefix, PERMYRIAD_AS_PERCENT_FORMAT_VAL(UINT32_SCALE_TO_PERMYRIAD(c->moom_mem_pressure_limit)),
|
||||
prefix, managed_oom_preference_to_string(c->moom_preference));
|
||||
|
||||
if (c->delegate) {
|
||||
|
@ -163,7 +163,7 @@ struct CGroupContext {
|
||||
/* Settings for systemd-oomd */
|
||||
ManagedOOMMode moom_swap;
|
||||
ManagedOOMMode moom_mem_pressure;
|
||||
uint32_t moom_mem_pressure_limit_permyriad;
|
||||
uint32_t moom_mem_pressure_limit; /* Normalized to 2^32-1 == 100% */
|
||||
ManagedOOMPreference moom_preference;
|
||||
};
|
||||
|
||||
|
@ -83,7 +83,7 @@ static int build_managed_oom_json_array_element(Unit *u, const char *property, J
|
||||
JSON_BUILD_PAIR("mode", JSON_BUILD_STRING(mode)),
|
||||
JSON_BUILD_PAIR("path", JSON_BUILD_STRING(u->cgroup_path)),
|
||||
JSON_BUILD_PAIR("property", JSON_BUILD_STRING(property)),
|
||||
JSON_BUILD_PAIR_CONDITION(use_limit, "limit", JSON_BUILD_UNSIGNED(c->moom_mem_pressure_limit_permyriad))));
|
||||
JSON_BUILD_PAIR_CONDITION(use_limit, "limit", JSON_BUILD_UNSIGNED(c->moom_mem_pressure_limit))));
|
||||
}
|
||||
|
||||
int manager_varlink_send_managed_oom_update(Unit *u) {
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "fileio.h"
|
||||
#include "limits-util.h"
|
||||
#include "path-util.h"
|
||||
#include "percent-util.h"
|
||||
|
||||
BUS_DEFINE_PROPERTY_GET(bus_property_get_tasks_max, "t", TasksMax, tasks_max_resolve);
|
||||
|
||||
@ -395,7 +396,7 @@ const sd_bus_vtable bus_cgroup_vtable[] = {
|
||||
SD_BUS_PROPERTY("DisableControllers", "as", property_get_cgroup_mask, offsetof(CGroupContext, disable_controllers), 0),
|
||||
SD_BUS_PROPERTY("ManagedOOMSwap", "s", property_get_managed_oom_mode, offsetof(CGroupContext, moom_swap), 0),
|
||||
SD_BUS_PROPERTY("ManagedOOMMemoryPressure", "s", property_get_managed_oom_mode, offsetof(CGroupContext, moom_mem_pressure), 0),
|
||||
SD_BUS_PROPERTY("ManagedOOMMemoryPressureLimitPermyriad", "u", NULL, offsetof(CGroupContext, moom_mem_pressure_limit_permyriad), 0),
|
||||
SD_BUS_PROPERTY("ManagedOOMMemoryPressureLimit", "u", NULL, offsetof(CGroupContext, moom_mem_pressure_limit), 0),
|
||||
SD_BUS_PROPERTY("ManagedOOMPreference", "s", property_get_managed_oom_preference, offsetof(CGroupContext, moom_preference), 0),
|
||||
SD_BUS_VTABLE_END
|
||||
};
|
||||
@ -704,10 +705,10 @@ static int bus_cgroup_set_boolean(
|
||||
/* Prepare to chop off suffix */ \
|
||||
assert_se(endswith(name, "Scale")); \
|
||||
\
|
||||
uint32_t scaled = DIV_ROUND_UP((uint64_t) raw * 1000, (uint64_t) UINT32_MAX); \
|
||||
unit_write_settingf(u, flags, name, "%.*s=%" PRIu32 ".%" PRIu32 "%%", \
|
||||
int scaled = UINT32_SCALE_TO_PERMYRIAD(raw); \
|
||||
unit_write_settingf(u, flags, name, "%.*s=" PERMYRIAD_AS_PERCENT_FORMAT_STR, \
|
||||
(int)(strlen(name) - strlen("Scale")), name, \
|
||||
scaled / 10, scaled % 10); \
|
||||
PERMYRIAD_AS_PERCENT_FORMAT_VAL(scaled)); \
|
||||
} \
|
||||
\
|
||||
return 1; \
|
||||
@ -1698,7 +1699,7 @@ int bus_cgroup_set_property(
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (streq(name, "ManagedOOMMemoryPressureLimitPermyriad")) {
|
||||
if (streq(name, "ManagedOOMMemoryPressureLimit")) {
|
||||
uint32_t v;
|
||||
|
||||
if (!UNIT_VTABLE(u)->can_set_managed_oom)
|
||||
@ -1708,12 +1709,11 @@ int bus_cgroup_set_property(
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (v > 10000)
|
||||
return -ERANGE;
|
||||
|
||||
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
|
||||
c->moom_mem_pressure_limit_permyriad = v;
|
||||
unit_write_settingf(u, flags, name, "ManagedOOMMemoryPressureLimit=%" PRIu32 ".%02" PRIu32 "%%", v / 100, v % 100);
|
||||
c->moom_mem_pressure_limit = v;
|
||||
unit_write_settingf(u, flags, name,
|
||||
"ManagedOOMMemoryPressureLimit=" PERMYRIAD_AS_PERCENT_FORMAT_STR,
|
||||
PERMYRIAD_AS_PERCENT_FORMAT_VAL(UINT32_SCALE_TO_PERMYRIAD(v)));
|
||||
}
|
||||
|
||||
if (c->moom_mem_pressure == MANAGED_OOM_KILL)
|
||||
|
@ -229,7 +229,7 @@ $1.IPIngressFilterPath, config_parse_ip_filter_bpf_progs,
|
||||
$1.IPEgressFilterPath, config_parse_ip_filter_bpf_progs, 0, offsetof($1, cgroup_context.ip_filters_egress)
|
||||
$1.ManagedOOMSwap, config_parse_managed_oom_mode, 0, offsetof($1, cgroup_context.moom_swap)
|
||||
$1.ManagedOOMMemoryPressure, config_parse_managed_oom_mode, 0, offsetof($1, cgroup_context.moom_mem_pressure)
|
||||
$1.ManagedOOMMemoryPressureLimit, config_parse_managed_oom_mem_pressure_limit, 0, offsetof($1, cgroup_context.moom_mem_pressure_limit_permyriad)
|
||||
$1.ManagedOOMMemoryPressureLimit, config_parse_managed_oom_mem_pressure_limit, 0, offsetof($1, cgroup_context.moom_mem_pressure_limit)
|
||||
$1.ManagedOOMPreference, config_parse_managed_oom_preference, 0, offsetof($1, cgroup_context.moom_preference)
|
||||
$1.NetClass, config_parse_warn_compat, DISABLED_LEGACY, 0'
|
||||
)m4_dnl
|
||||
|
@ -46,6 +46,7 @@
|
||||
#include "nulstr-util.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
#include "percent-util.h"
|
||||
#include "process-util.h"
|
||||
#if HAVE_SECCOMP
|
||||
#include "seccomp-util.h"
|
||||
@ -3593,13 +3594,13 @@ int config_parse_cpu_quota(
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = parse_permille_unbounded(rvalue);
|
||||
r = parse_permyriad_unbounded(rvalue);
|
||||
if (r <= 0) {
|
||||
log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid CPU quota '%s', ignoring.", rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
c->cpu_quota_per_sec_usec = ((usec_t) r * USEC_PER_SEC) / 1000U;
|
||||
c->cpu_quota_per_sec_usec = ((usec_t) r * USEC_PER_SEC) / 10000U;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -3664,7 +3665,7 @@ int config_parse_memory_limit(
|
||||
bytes = CGROUP_LIMIT_MIN;
|
||||
else if (!isempty(rvalue) && !streq(rvalue, "infinity")) {
|
||||
|
||||
r = parse_permille(rvalue);
|
||||
r = parse_permyriad(rvalue);
|
||||
if (r < 0) {
|
||||
r = parse_size(rvalue, 1024, &bytes);
|
||||
if (r < 0) {
|
||||
@ -3672,7 +3673,7 @@ int config_parse_memory_limit(
|
||||
return 0;
|
||||
}
|
||||
} else
|
||||
bytes = physical_memory_scale(r, 1000U);
|
||||
bytes = physical_memory_scale(r, 10000U);
|
||||
|
||||
if (bytes >= UINT64_MAX ||
|
||||
(bytes <= 0 && !STR_IN_SET(lvalue, "MemorySwapMax", "MemoryLow", "MemoryMin", "DefaultMemoryLow", "DefaultMemoryMin"))) {
|
||||
@ -3734,9 +3735,9 @@ int config_parse_tasks_max(
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = parse_permille(rvalue);
|
||||
r = parse_permyriad(rvalue);
|
||||
if (r >= 0)
|
||||
*tasks_max = (TasksMax) { r, 1000U }; /* r‰ */
|
||||
*tasks_max = (TasksMax) { r, 10000U }; /* r‱ */
|
||||
else {
|
||||
r = safe_atou64(rvalue, &v);
|
||||
if (r < 0) {
|
||||
@ -3842,6 +3843,7 @@ int config_parse_managed_oom_mode(
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
ManagedOOMMode *mode = data, m;
|
||||
UnitType t;
|
||||
|
||||
@ -3861,6 +3863,7 @@ int config_parse_managed_oom_mode(
|
||||
log_syntax(unit, LOG_WARNING, filename, line, m, "Invalid syntax, ignoring: %s", rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
*mode = m;
|
||||
return 0;
|
||||
}
|
||||
@ -3876,6 +3879,7 @@ int config_parse_managed_oom_mem_pressure_limit(
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
uint32_t *limit = data;
|
||||
UnitType t;
|
||||
int r;
|
||||
@ -3897,7 +3901,8 @@ int config_parse_managed_oom_mem_pressure_limit(
|
||||
return 0;
|
||||
}
|
||||
|
||||
*limit = r;
|
||||
/* Normalize to 2^32-1 == 100% */
|
||||
*limit = UINT32_SCALE_FROM_PERMYRIAD(r);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1256,8 +1256,8 @@ static int bump_rlimit_memlock(struct rlimit *saved_rlimit) {
|
||||
* must be unsigned, hence this is a given, but let's make this clear here. */
|
||||
assert_cc(RLIM_INFINITY > 0);
|
||||
|
||||
mm = physical_memory() / 8; /* Let's scale how much we allow to be locked by the amount of physical
|
||||
* RAM. We allow an eighth to be locked by us, just to pick a value. */
|
||||
mm = physical_memory_scale(1, 8); /* Let's scale how much we allow to be locked by the amount of physical
|
||||
* RAM. We allow an eighth to be locked by us, just to pick a value. */
|
||||
|
||||
new_rlimit = (struct rlimit) {
|
||||
.rlim_cur = MAX3(HIGH_RLIMIT_MEMLOCK, saved_rlimit->rlim_cur, mm),
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "parse-argument.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
#include "percent-util.h"
|
||||
#include "pkcs11-util.h"
|
||||
#include "pretty-print.h"
|
||||
#include "process-util.h"
|
||||
@ -1567,7 +1568,7 @@ static int resize_home(int argc, char *argv[], void *userdata) {
|
||||
(void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
|
||||
|
||||
if (arg_disk_size_relative != UINT64_MAX ||
|
||||
(argc > 2 && parse_percent(argv[2]) >= 0))
|
||||
(argc > 2 && parse_permyriad(argv[2]) >= 0))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
|
||||
"Relative disk size specification currently not supported when resizing.");
|
||||
|
||||
@ -2653,7 +2654,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
break;
|
||||
}
|
||||
|
||||
r = parse_permille(optarg);
|
||||
r = parse_permyriad(optarg);
|
||||
if (r < 0) {
|
||||
r = parse_size(optarg, 1024, &arg_disk_size);
|
||||
if (r < 0)
|
||||
@ -2670,7 +2671,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
arg_disk_size_relative = UINT64_MAX;
|
||||
} else {
|
||||
/* Normalize to UINT32_MAX == 100% */
|
||||
arg_disk_size_relative = (uint64_t) r * UINT32_MAX / 1000U;
|
||||
arg_disk_size_relative = UINT32_SCALE_FROM_PERMYRIAD(r);
|
||||
|
||||
r = drop_from_identity("diskSize");
|
||||
if (r < 0)
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "mkdir.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
#include "percent-util.h"
|
||||
#include "process-util.h"
|
||||
#include "service-util.h"
|
||||
#include "signal-util.h"
|
||||
|
@ -19,11 +19,12 @@
|
||||
#include "label.h"
|
||||
#include "limits-util.h"
|
||||
#include "logind-dbus.h"
|
||||
#include "logind-user.h"
|
||||
#include "logind-user-dbus.h"
|
||||
#include "logind-user.h"
|
||||
#include "mkdir.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
#include "percent-util.h"
|
||||
#include "rm-rf.h"
|
||||
#include "serialize.h"
|
||||
#include "special.h"
|
||||
@ -907,9 +908,9 @@ int config_parse_tmpfs_size(
|
||||
assert(data);
|
||||
|
||||
/* First, try to parse as percentage */
|
||||
r = parse_permille(rvalue);
|
||||
if (r > 0 && r < 1000)
|
||||
*sz = physical_memory_scale(r, 1000U);
|
||||
r = parse_permyriad(rvalue);
|
||||
if (r > 0)
|
||||
*sz = physical_memory_scale(r, 10000U);
|
||||
else {
|
||||
uint64_t k;
|
||||
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include "pam-util.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
#include "percent-util.h"
|
||||
#include "process-util.h"
|
||||
#include "rlimit-util.h"
|
||||
#include "socket-util.h"
|
||||
@ -334,9 +335,9 @@ static int append_session_memory_max(pam_handle_t *handle, sd_bus_message *m, co
|
||||
return PAM_SUCCESS;
|
||||
}
|
||||
|
||||
r = parse_permille(limit);
|
||||
r = parse_permyriad(limit);
|
||||
if (r >= 0) {
|
||||
r = sd_bus_message_append(m, "(sv)", "MemoryMaxScale", "u", (uint32_t) (((uint64_t) r * UINT32_MAX) / 1000U));
|
||||
r = sd_bus_message_append(m, "(sv)", "MemoryMaxScale", "u", UINT32_SCALE_FROM_PERMYRIAD(r));
|
||||
if (r < 0)
|
||||
return pam_bus_log_create_error(handle, r);
|
||||
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "extract-word.h"
|
||||
#include "fileio.h"
|
||||
#include "parse-util.h"
|
||||
#include "percent-util.h"
|
||||
#include "tc-util.h"
|
||||
#include "time-util.h"
|
||||
|
||||
@ -57,17 +58,17 @@ int tc_time_to_tick(usec_t t, uint32_t *ret) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int parse_tc_percent(const char *s, uint32_t *percent) {
|
||||
int parse_tc_percent(const char *s, uint32_t *ret_fraction) {
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
assert(percent);
|
||||
assert(ret_fraction);
|
||||
|
||||
r = parse_permille(s);
|
||||
r = parse_permyriad(s);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*percent = (double) r / 1000 * UINT32_MAX;
|
||||
*ret_fraction = (double) r / 10000 * UINT32_MAX;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -10,12 +10,13 @@
|
||||
#include "oomd-manager-bus.h"
|
||||
#include "oomd-manager.h"
|
||||
#include "path-util.h"
|
||||
#include "percent-util.h"
|
||||
|
||||
typedef struct ManagedOOMReply {
|
||||
ManagedOOMMode mode;
|
||||
char *path;
|
||||
char *property;
|
||||
unsigned limit;
|
||||
uint32_t limit;
|
||||
} ManagedOOMReply;
|
||||
|
||||
static void managed_oom_reply_destroy(ManagedOOMReply *reply) {
|
||||
@ -52,10 +53,10 @@ static int process_managed_oom_reply(
|
||||
assert(m);
|
||||
|
||||
static const JsonDispatch dispatch_table[] = {
|
||||
{ "mode", JSON_VARIANT_STRING, managed_oom_mode, offsetof(ManagedOOMReply, mode), JSON_MANDATORY },
|
||||
{ "path", JSON_VARIANT_STRING, json_dispatch_string, offsetof(ManagedOOMReply, path), JSON_MANDATORY },
|
||||
{ "property", JSON_VARIANT_STRING, json_dispatch_string, offsetof(ManagedOOMReply, property), JSON_MANDATORY },
|
||||
{ "limit", JSON_VARIANT_UNSIGNED, json_dispatch_unsigned, offsetof(ManagedOOMReply, limit), 0 },
|
||||
{ "mode", JSON_VARIANT_STRING, managed_oom_mode, offsetof(ManagedOOMReply, mode), JSON_MANDATORY },
|
||||
{ "path", JSON_VARIANT_STRING, json_dispatch_string, offsetof(ManagedOOMReply, path), JSON_MANDATORY },
|
||||
{ "property", JSON_VARIANT_STRING, json_dispatch_string, offsetof(ManagedOOMReply, property), JSON_MANDATORY },
|
||||
{ "limit", JSON_VARIANT_UNSIGNED, json_dispatch_uint32, offsetof(ManagedOOMReply, limit), 0 },
|
||||
{},
|
||||
};
|
||||
|
||||
@ -86,7 +87,8 @@ static int process_managed_oom_reply(
|
||||
if (ret == -ENOMEM) {
|
||||
r = ret;
|
||||
goto finish;
|
||||
} else if (ret < 0)
|
||||
}
|
||||
if (ret < 0)
|
||||
continue;
|
||||
|
||||
monitor_hm = streq(reply.property, "ManagedOOMSwap") ?
|
||||
@ -99,14 +101,15 @@ static int process_managed_oom_reply(
|
||||
|
||||
limit = m->default_mem_pressure_limit;
|
||||
|
||||
if (streq(reply.property, "ManagedOOMMemoryPressure")) {
|
||||
if (reply.limit > 10000)
|
||||
if (streq(reply.property, "ManagedOOMMemoryPressure") && reply.limit > 0) {
|
||||
int permyriad = UINT32_SCALE_TO_PERMYRIAD(reply.limit);
|
||||
|
||||
ret = store_loadavg_fixed_point(
|
||||
(unsigned long) permyriad / 100,
|
||||
(unsigned long) permyriad % 100,
|
||||
&limit);
|
||||
if (ret < 0)
|
||||
continue;
|
||||
else if (reply.limit != 0) {
|
||||
ret = store_loadavg_fixed_point((unsigned long) reply.limit / 100, (unsigned long) reply.limit % 100, &limit);
|
||||
if (ret < 0)
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
ret = oomd_insert_cgroup_context(NULL, monitor_hm, empty_to_root(reply.path));
|
||||
@ -348,11 +351,11 @@ static int monitor_cgroup_contexts_handler(sd_event_source *s, uint64_t usec, vo
|
||||
}
|
||||
}
|
||||
|
||||
if (oomd_swap_free_below(&m->system_context, (100 - m->swap_used_limit))) {
|
||||
if (oomd_swap_free_below(&m->system_context, 10000 - m->swap_used_limit_permyriad)) {
|
||||
_cleanup_hashmap_free_ Hashmap *candidates = NULL;
|
||||
|
||||
log_notice("Swap used (%"PRIu64") / total (%"PRIu64") is more than %u%%",
|
||||
m->system_context.swap_used, m->system_context.swap_total, m->swap_used_limit);
|
||||
log_notice("Swap used (%"PRIu64") / total (%"PRIu64") is more than " PERMYRIAD_AS_PERCENT_FORMAT_STR,
|
||||
m->system_context.swap_used, m->system_context.swap_total, PERMYRIAD_AS_PERCENT_FORMAT_VAL(m->swap_used_limit_permyriad));
|
||||
|
||||
r = get_monitored_cgroup_contexts_candidates(m->monitored_swap_cgroup_contexts, &candidates);
|
||||
if (r == -ENOMEM)
|
||||
@ -478,7 +481,13 @@ static int manager_connect_bus(Manager *m) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int manager_start(Manager *m, bool dry_run, int swap_used_limit, int mem_pressure_limit_permyriad, usec_t mem_pressure_usec) {
|
||||
int manager_start(
|
||||
Manager *m,
|
||||
bool dry_run,
|
||||
int swap_used_limit_permyriad,
|
||||
int mem_pressure_limit_permyriad,
|
||||
usec_t mem_pressure_usec) {
|
||||
|
||||
unsigned long l, f;
|
||||
int r;
|
||||
|
||||
@ -486,10 +495,10 @@ int manager_start(Manager *m, bool dry_run, int swap_used_limit, int mem_pressur
|
||||
|
||||
m->dry_run = dry_run;
|
||||
|
||||
m->swap_used_limit = swap_used_limit != -1 ? swap_used_limit : DEFAULT_SWAP_USED_LIMIT;
|
||||
assert(m->swap_used_limit <= 100);
|
||||
m->swap_used_limit_permyriad = swap_used_limit_permyriad >= 0 ? swap_used_limit_permyriad : DEFAULT_SWAP_USED_LIMIT_PERCENT * 100;
|
||||
assert(m->swap_used_limit_permyriad <= 10000);
|
||||
|
||||
if (mem_pressure_limit_permyriad != -1) {
|
||||
if (mem_pressure_limit_permyriad >= 0) {
|
||||
assert(mem_pressure_limit_permyriad <= 10000);
|
||||
|
||||
l = mem_pressure_limit_permyriad / 100;
|
||||
@ -537,12 +546,12 @@ int manager_get_dump_string(Manager *m, char **ret) {
|
||||
|
||||
fprintf(f,
|
||||
"Dry Run: %s\n"
|
||||
"Swap Used Limit: %u%%\n"
|
||||
"Swap Used Limit: " PERMYRIAD_AS_PERCENT_FORMAT_STR "\n"
|
||||
"Default Memory Pressure Limit: %lu.%02lu%%\n"
|
||||
"Default Memory Pressure Duration: %s\n"
|
||||
"System Context:\n",
|
||||
yes_no(m->dry_run),
|
||||
m->swap_used_limit,
|
||||
PERMYRIAD_AS_PERCENT_FORMAT_VAL(m->swap_used_limit_permyriad),
|
||||
LOAD_INT(m->default_mem_pressure_limit), LOAD_FRAC(m->default_mem_pressure_limit),
|
||||
format_timespan(buf, sizeof(buf), m->default_mem_pressure_duration_usec, USEC_PER_SEC));
|
||||
oomd_dump_system_context(&m->system_context, f, "\t");
|
||||
|
@ -18,7 +18,7 @@
|
||||
* system.slice are assumed to be less latency sensitive. */
|
||||
#define DEFAULT_MEM_PRESSURE_DURATION_USEC (30 * USEC_PER_SEC)
|
||||
#define DEFAULT_MEM_PRESSURE_LIMIT_PERCENT 60
|
||||
#define DEFAULT_SWAP_USED_LIMIT 90
|
||||
#define DEFAULT_SWAP_USED_LIMIT_PERCENT 90
|
||||
|
||||
#define RECLAIM_DURATION_USEC (30 * USEC_PER_SEC)
|
||||
#define POST_ACTION_DELAY_USEC (15 * USEC_PER_SEC)
|
||||
@ -32,7 +32,7 @@ struct Manager {
|
||||
Hashmap *polkit_registry;
|
||||
|
||||
bool dry_run;
|
||||
unsigned swap_used_limit;
|
||||
int swap_used_limit_permyriad;
|
||||
loadavg_t default_mem_pressure_limit;
|
||||
usec_t default_mem_pressure_duration_usec;
|
||||
|
||||
@ -56,7 +56,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
|
||||
|
||||
int manager_new(Manager **ret);
|
||||
|
||||
int manager_start(Manager *m, bool dry_run, int swap_used_limit, int mem_pressure_limit_permyriad, usec_t mem_pressure_usec);
|
||||
int manager_start(Manager *m, bool dry_run, int swap_used_limit_permyriad, int mem_pressure_limit_permyriad, usec_t mem_pressure_usec);
|
||||
|
||||
int manager_get_dump_string(Manager *m, char **ret);
|
||||
|
||||
|
@ -134,13 +134,13 @@ bool oomd_memory_reclaim(Hashmap *h) {
|
||||
return pgscan_of > last_pgscan_of;
|
||||
}
|
||||
|
||||
bool oomd_swap_free_below(const OomdSystemContext *ctx, uint64_t threshold_percent) {
|
||||
bool oomd_swap_free_below(const OomdSystemContext *ctx, int threshold_permyriad) {
|
||||
uint64_t swap_threshold;
|
||||
|
||||
assert(ctx);
|
||||
assert(threshold_percent <= 100);
|
||||
assert(threshold_permyriad <= 10000);
|
||||
|
||||
swap_threshold = ctx->swap_total * threshold_percent / ((uint64_t) 100);
|
||||
swap_threshold = ctx->swap_total * threshold_permyriad / (uint64_t) 10000;
|
||||
return (ctx->swap_total - ctx->swap_used) < swap_threshold;
|
||||
}
|
||||
|
||||
|
@ -61,8 +61,8 @@ int oomd_pressure_above(Hashmap *h, usec_t duration, Set **ret);
|
||||
* current sum is higher than the last interval's sum (there was some reclaim activity). */
|
||||
bool oomd_memory_reclaim(Hashmap *h);
|
||||
|
||||
/* Returns true if the amount of swap free is below the percentage of swap specified by `threshold_percent`. */
|
||||
bool oomd_swap_free_below(const OomdSystemContext *ctx, uint64_t threshold_percent);
|
||||
/* Returns true if the amount of swap free is below the permyriad of swap specified by `threshold_permyriad`. */
|
||||
bool oomd_swap_free_below(const OomdSystemContext *ctx, int threshold_permyriad);
|
||||
|
||||
/* The compare functions will sort from largest to smallest, putting all the contexts with "avoid" at the end
|
||||
* (after the smallest values). */
|
||||
|
@ -17,13 +17,13 @@
|
||||
#include "signal-util.h"
|
||||
|
||||
static bool arg_dry_run = false;
|
||||
static int arg_swap_used_limit = -1;
|
||||
static int arg_swap_used_limit_permyriad = -1;
|
||||
static int arg_mem_pressure_limit_permyriad = -1;
|
||||
static usec_t arg_mem_pressure_usec = 0;
|
||||
|
||||
static int parse_config(void) {
|
||||
static const ConfigTableItem items[] = {
|
||||
{ "OOM", "SwapUsedLimitPercent", config_parse_percent, 0, &arg_swap_used_limit },
|
||||
{ "OOM", "SwapUsedLimit", config_parse_permyriad, 0, &arg_swap_used_limit_permyriad },
|
||||
{ "OOM", "DefaultMemoryPressureLimit", config_parse_permyriad, 0, &arg_mem_pressure_limit_permyriad },
|
||||
{ "OOM", "DefaultMemoryPressureDurationSec", config_parse_sec, 0, &arg_mem_pressure_usec },
|
||||
{}
|
||||
@ -159,7 +159,12 @@ static int run(int argc, char *argv[]) {
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to create manager: %m");
|
||||
|
||||
r = manager_start(m, arg_dry_run, arg_swap_used_limit, arg_mem_pressure_limit_permyriad, arg_mem_pressure_usec);
|
||||
r = manager_start(
|
||||
m,
|
||||
arg_dry_run,
|
||||
arg_swap_used_limit_permyriad,
|
||||
arg_mem_pressure_limit_permyriad,
|
||||
arg_mem_pressure_usec);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to start up daemon: %m");
|
||||
|
||||
|
@ -13,6 +13,6 @@
|
||||
# See oomd.conf(5) for details
|
||||
|
||||
[OOM]
|
||||
#SwapUsedLimitPercent=90%
|
||||
#SwapUsedLimit=90%
|
||||
#DefaultMemoryPressureLimit=60%
|
||||
#DefaultMemoryPressureDurationSec=30s
|
||||
|
@ -302,19 +302,19 @@ static void test_oomd_swap_free_below(void) {
|
||||
.swap_total = 20971512 * 1024U,
|
||||
.swap_used = 20971440 * 1024U,
|
||||
};
|
||||
assert_se(oomd_swap_free_below(&ctx, 20) == true);
|
||||
assert_se(oomd_swap_free_below(&ctx, 2000) == true);
|
||||
|
||||
ctx = (OomdSystemContext) {
|
||||
.swap_total = 20971512 * 1024U,
|
||||
.swap_used = 3310136 * 1024U,
|
||||
};
|
||||
assert_se(oomd_swap_free_below(&ctx, 20) == false);
|
||||
assert_se(oomd_swap_free_below(&ctx, 2000) == false);
|
||||
|
||||
ctx = (OomdSystemContext) {
|
||||
.swap_total = 0,
|
||||
.swap_used = 0,
|
||||
};
|
||||
assert_se(oomd_swap_free_below(&ctx, 20) == false);
|
||||
assert_se(oomd_swap_free_below(&ctx, 2000) == false);
|
||||
}
|
||||
|
||||
static void test_oomd_sort_cgroups(void) {
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include "numa-util.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
#include "percent-util.h"
|
||||
#include "process-util.h"
|
||||
#include "rlimit-util.h"
|
||||
#if HAVE_SECCOMP
|
||||
@ -440,15 +441,12 @@ static int bus_append_cgroup_property(sd_bus_message *m, const char *field, cons
|
||||
return bus_append_string(m, field, eq);
|
||||
|
||||
if (STR_IN_SET(field, "ManagedOOMMemoryPressureLimit")) {
|
||||
char *n;
|
||||
|
||||
r = parse_permyriad(eq);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse %s value: %s", field, eq);
|
||||
|
||||
n = strjoina(field, "Permyriad");
|
||||
|
||||
r = sd_bus_message_append(m, "(sv)", n, "u", (uint32_t) r);
|
||||
/* Pass around scaled to 2^32-1 == 100% */
|
||||
r = sd_bus_message_append(m, "(sv)", field, "u", UINT32_SCALE_FROM_PERMYRIAD(r));
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
@ -539,7 +537,7 @@ static int bus_append_cgroup_property(sd_bus_message *m, const char *field, cons
|
||||
return 1;
|
||||
}
|
||||
|
||||
r = parse_permille(eq);
|
||||
r = parse_permyriad(eq);
|
||||
if (r >= 0) {
|
||||
char *n;
|
||||
|
||||
@ -548,7 +546,7 @@ static int bus_append_cgroup_property(sd_bus_message *m, const char *field, cons
|
||||
* size can be determined server-side. */
|
||||
|
||||
n = strjoina(field, "Scale");
|
||||
r = sd_bus_message_append(m, "(sv)", n, "u", (uint32_t) (((uint64_t) r * UINT32_MAX) / 1000U));
|
||||
r = sd_bus_message_append(m, "(sv)", n, "u", UINT32_SCALE_FROM_PERMYRIAD(r));
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
@ -565,14 +563,14 @@ static int bus_append_cgroup_property(sd_bus_message *m, const char *field, cons
|
||||
if (isempty(eq))
|
||||
r = sd_bus_message_append(m, "(sv)", "CPUQuotaPerSecUSec", "t", USEC_INFINITY);
|
||||
else {
|
||||
r = parse_permille_unbounded(eq);
|
||||
r = parse_permyriad_unbounded(eq);
|
||||
if (r == 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ERANGE),
|
||||
"CPU quota too small.");
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "CPU quota '%s' invalid.", eq);
|
||||
|
||||
r = sd_bus_message_append(m, "(sv)", "CPUQuotaPerSecUSec", "t", (((uint64_t) r * USEC_PER_SEC) / 1000U));
|
||||
r = sd_bus_message_append(m, "(sv)", "CPUQuotaPerSecUSec", "t", (((uint64_t) r * USEC_PER_SEC) / 10000U));
|
||||
}
|
||||
|
||||
if (r < 0)
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "nulstr-util.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
#include "percent-util.h"
|
||||
#include "process-util.h"
|
||||
#include "rlimit-util.h"
|
||||
#include "sd-id128.h"
|
||||
|
@ -547,6 +547,8 @@ tests += [
|
||||
|
||||
[['src/test/test-bus-util.c']],
|
||||
|
||||
[['src/test/test-percent-util.c']],
|
||||
|
||||
[['src/test/test-sd-hwdb.c']],
|
||||
|
||||
[['src/test/test-sd-path.c']],
|
||||
|
@ -718,144 +718,6 @@ static void test_safe_atod(void) {
|
||||
assert_se(r == -EINVAL);
|
||||
}
|
||||
|
||||
static void test_parse_percent(void) {
|
||||
assert_se(parse_percent("") == -EINVAL);
|
||||
assert_se(parse_percent("foo") == -EINVAL);
|
||||
assert_se(parse_percent("0") == -EINVAL);
|
||||
assert_se(parse_percent("50") == -EINVAL);
|
||||
assert_se(parse_percent("100") == -EINVAL);
|
||||
assert_se(parse_percent("-1") == -EINVAL);
|
||||
assert_se(parse_percent("0%") == 0);
|
||||
assert_se(parse_percent("55%") == 55);
|
||||
assert_se(parse_percent("100%") == 100);
|
||||
assert_se(parse_percent("-7%") == -ERANGE);
|
||||
assert_se(parse_percent("107%") == -ERANGE);
|
||||
assert_se(parse_percent("%") == -EINVAL);
|
||||
assert_se(parse_percent("%%") == -EINVAL);
|
||||
assert_se(parse_percent("%1") == -EINVAL);
|
||||
assert_se(parse_percent("1%%") == -EINVAL);
|
||||
assert_se(parse_percent("3.2%") == -EINVAL);
|
||||
}
|
||||
|
||||
static void test_parse_percent_unbounded(void) {
|
||||
assert_se(parse_percent_unbounded("101%") == 101);
|
||||
assert_se(parse_percent_unbounded("400%") == 400);
|
||||
}
|
||||
|
||||
static void test_parse_permille(void) {
|
||||
assert_se(parse_permille("") == -EINVAL);
|
||||
assert_se(parse_permille("foo") == -EINVAL);
|
||||
assert_se(parse_permille("0") == -EINVAL);
|
||||
assert_se(parse_permille("50") == -EINVAL);
|
||||
assert_se(parse_permille("100") == -EINVAL);
|
||||
assert_se(parse_permille("-1") == -EINVAL);
|
||||
|
||||
assert_se(parse_permille("0‰") == 0);
|
||||
assert_se(parse_permille("555‰") == 555);
|
||||
assert_se(parse_permille("1000‰") == 1000);
|
||||
assert_se(parse_permille("-7‰") == -ERANGE);
|
||||
assert_se(parse_permille("1007‰") == -ERANGE);
|
||||
assert_se(parse_permille("‰") == -EINVAL);
|
||||
assert_se(parse_permille("‰‰") == -EINVAL);
|
||||
assert_se(parse_permille("‰1") == -EINVAL);
|
||||
assert_se(parse_permille("1‰‰") == -EINVAL);
|
||||
assert_se(parse_permille("3.2‰") == -EINVAL);
|
||||
|
||||
assert_se(parse_permille("0%") == 0);
|
||||
assert_se(parse_permille("55%") == 550);
|
||||
assert_se(parse_permille("55.5%") == 555);
|
||||
assert_se(parse_permille("100%") == 1000);
|
||||
assert_se(parse_permille("-7%") == -ERANGE);
|
||||
assert_se(parse_permille("107%") == -ERANGE);
|
||||
assert_se(parse_permille("%") == -EINVAL);
|
||||
assert_se(parse_permille("%%") == -EINVAL);
|
||||
assert_se(parse_permille("%1") == -EINVAL);
|
||||
assert_se(parse_permille("1%%") == -EINVAL);
|
||||
assert_se(parse_permille("3.21%") == -EINVAL);
|
||||
}
|
||||
|
||||
static void test_parse_permille_unbounded(void) {
|
||||
assert_se(parse_permille_unbounded("1001‰") == 1001);
|
||||
assert_se(parse_permille_unbounded("4000‰") == 4000);
|
||||
assert_se(parse_permille_unbounded("2147483647‰") == 2147483647);
|
||||
assert_se(parse_permille_unbounded("2147483648‰") == -ERANGE);
|
||||
assert_se(parse_permille_unbounded("4294967295‰") == -ERANGE);
|
||||
assert_se(parse_permille_unbounded("4294967296‰") == -ERANGE);
|
||||
|
||||
assert_se(parse_permille_unbounded("101%") == 1010);
|
||||
assert_se(parse_permille_unbounded("400%") == 4000);
|
||||
assert_se(parse_permille_unbounded("214748364.7%") == 2147483647);
|
||||
assert_se(parse_permille_unbounded("214748364.8%") == -ERANGE);
|
||||
assert_se(parse_permille_unbounded("429496729.5%") == -ERANGE);
|
||||
assert_se(parse_permille_unbounded("429496729.6%") == -ERANGE);
|
||||
}
|
||||
|
||||
static void test_parse_permyriad(void) {
|
||||
assert_se(parse_permyriad("") == -EINVAL);
|
||||
assert_se(parse_permyriad("foo") == -EINVAL);
|
||||
assert_se(parse_permyriad("0") == -EINVAL);
|
||||
assert_se(parse_permyriad("50") == -EINVAL);
|
||||
assert_se(parse_permyriad("100") == -EINVAL);
|
||||
assert_se(parse_permyriad("-1") == -EINVAL);
|
||||
|
||||
assert_se(parse_permyriad("0‱") == 0);
|
||||
assert_se(parse_permyriad("555‱") == 555);
|
||||
assert_se(parse_permyriad("1000‱") == 1000);
|
||||
assert_se(parse_permyriad("-7‱") == -ERANGE);
|
||||
assert_se(parse_permyriad("10007‱") == -ERANGE);
|
||||
assert_se(parse_permyriad("‱") == -EINVAL);
|
||||
assert_se(parse_permyriad("‱‱") == -EINVAL);
|
||||
assert_se(parse_permyriad("‱1") == -EINVAL);
|
||||
assert_se(parse_permyriad("1‱‱") == -EINVAL);
|
||||
assert_se(parse_permyriad("3.2‱") == -EINVAL);
|
||||
|
||||
assert_se(parse_permyriad("0‰") == 0);
|
||||
assert_se(parse_permyriad("555.5‰") == 5555);
|
||||
assert_se(parse_permyriad("1000.0‰") == 10000);
|
||||
assert_se(parse_permyriad("-7‰") == -ERANGE);
|
||||
assert_se(parse_permyriad("1007‰") == -ERANGE);
|
||||
assert_se(parse_permyriad("‰") == -EINVAL);
|
||||
assert_se(parse_permyriad("‰‰") == -EINVAL);
|
||||
assert_se(parse_permyriad("‰1") == -EINVAL);
|
||||
assert_se(parse_permyriad("1‰‰") == -EINVAL);
|
||||
assert_se(parse_permyriad("3.22‰") == -EINVAL);
|
||||
|
||||
assert_se(parse_permyriad("0%") == 0);
|
||||
assert_se(parse_permyriad("55%") == 5500);
|
||||
assert_se(parse_permyriad("55.53%") == 5553);
|
||||
assert_se(parse_permyriad("100%") == 10000);
|
||||
assert_se(parse_permyriad("-7%") == -ERANGE);
|
||||
assert_se(parse_permyriad("107%") == -ERANGE);
|
||||
assert_se(parse_permyriad("%") == -EINVAL);
|
||||
assert_se(parse_permyriad("%%") == -EINVAL);
|
||||
assert_se(parse_permyriad("%1") == -EINVAL);
|
||||
assert_se(parse_permyriad("1%%") == -EINVAL);
|
||||
assert_se(parse_permyriad("3.212%") == -EINVAL);
|
||||
}
|
||||
|
||||
static void test_parse_permyriad_unbounded(void) {
|
||||
assert_se(parse_permyriad_unbounded("1001‱") == 1001);
|
||||
assert_se(parse_permyriad_unbounded("4000‱") == 4000);
|
||||
assert_se(parse_permyriad_unbounded("2147483647‱") == 2147483647);
|
||||
assert_se(parse_permyriad_unbounded("2147483648‱") == -ERANGE);
|
||||
assert_se(parse_permyriad_unbounded("4294967295‱") == -ERANGE);
|
||||
assert_se(parse_permyriad_unbounded("4294967296‱") == -ERANGE);
|
||||
|
||||
assert_se(parse_permyriad_unbounded("101‰") == 1010);
|
||||
assert_se(parse_permyriad_unbounded("400‰") == 4000);
|
||||
assert_se(parse_permyriad_unbounded("214748364.7‰") == 2147483647);
|
||||
assert_se(parse_permyriad_unbounded("214748364.8‰") == -ERANGE);
|
||||
assert_se(parse_permyriad_unbounded("429496729.5‰") == -ERANGE);
|
||||
assert_se(parse_permyriad_unbounded("429496729.6‰") == -ERANGE);
|
||||
|
||||
assert_se(parse_permyriad_unbounded("99%") == 9900);
|
||||
assert_se(parse_permyriad_unbounded("40%") == 4000);
|
||||
assert_se(parse_permyriad_unbounded("21474836.47%") == 2147483647);
|
||||
assert_se(parse_permyriad_unbounded("21474836.48%") == -ERANGE);
|
||||
assert_se(parse_permyriad_unbounded("42949672.95%") == -ERANGE);
|
||||
assert_se(parse_permyriad_unbounded("42949672.96%") == -ERANGE);
|
||||
}
|
||||
|
||||
static void test_parse_nice(void) {
|
||||
int n;
|
||||
|
||||
@ -1049,12 +911,6 @@ int main(int argc, char *argv[]) {
|
||||
test_safe_atoi64();
|
||||
test_safe_atoux64();
|
||||
test_safe_atod();
|
||||
test_parse_percent();
|
||||
test_parse_percent_unbounded();
|
||||
test_parse_permille();
|
||||
test_parse_permille_unbounded();
|
||||
test_parse_permyriad();
|
||||
test_parse_permyriad_unbounded();
|
||||
test_parse_nice();
|
||||
test_parse_dev();
|
||||
test_parse_errno();
|
||||
|
211
src/test/test-percent-util.c
Normal file
211
src/test/test-percent-util.c
Normal file
@ -0,0 +1,211 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "percent-util.h"
|
||||
#include "tests.h"
|
||||
#include "time-util.h"
|
||||
|
||||
static void test_parse_percent(void) {
|
||||
assert_se(parse_percent("") == -EINVAL);
|
||||
assert_se(parse_percent("foo") == -EINVAL);
|
||||
assert_se(parse_percent("0") == -EINVAL);
|
||||
assert_se(parse_percent("0.1") == -EINVAL);
|
||||
assert_se(parse_percent("50") == -EINVAL);
|
||||
assert_se(parse_percent("100") == -EINVAL);
|
||||
assert_se(parse_percent("-1") == -EINVAL);
|
||||
assert_se(parse_percent("0%") == 0);
|
||||
assert_se(parse_percent("55%") == 55);
|
||||
assert_se(parse_percent("100%") == 100);
|
||||
assert_se(parse_percent("-7%") == -ERANGE);
|
||||
assert_se(parse_percent("107%") == -ERANGE);
|
||||
assert_se(parse_percent("%") == -EINVAL);
|
||||
assert_se(parse_percent("%%") == -EINVAL);
|
||||
assert_se(parse_percent("%1") == -EINVAL);
|
||||
assert_se(parse_percent("1%%") == -EINVAL);
|
||||
assert_se(parse_percent("3.2%") == -EINVAL);
|
||||
}
|
||||
|
||||
static void test_parse_percent_unbounded(void) {
|
||||
assert_se(parse_percent_unbounded("101%") == 101);
|
||||
assert_se(parse_percent_unbounded("400%") == 400);
|
||||
}
|
||||
|
||||
static void test_parse_permille(void) {
|
||||
assert_se(parse_permille("") == -EINVAL);
|
||||
assert_se(parse_permille("foo") == -EINVAL);
|
||||
assert_se(parse_permille("0") == -EINVAL);
|
||||
assert_se(parse_permille("50") == -EINVAL);
|
||||
assert_se(parse_permille("100") == -EINVAL);
|
||||
assert_se(parse_permille("-1") == -EINVAL);
|
||||
assert_se(parse_permille("0.1") == -EINVAL);
|
||||
assert_se(parse_permille("5%") == 50);
|
||||
assert_se(parse_permille("5.5%") == 55);
|
||||
assert_se(parse_permille("5.12%") == -EINVAL);
|
||||
|
||||
assert_se(parse_permille("0‰") == 0);
|
||||
assert_se(parse_permille("555‰") == 555);
|
||||
assert_se(parse_permille("1000‰") == 1000);
|
||||
assert_se(parse_permille("-7‰") == -ERANGE);
|
||||
assert_se(parse_permille("1007‰") == -ERANGE);
|
||||
assert_se(parse_permille("‰") == -EINVAL);
|
||||
assert_se(parse_permille("‰‰") == -EINVAL);
|
||||
assert_se(parse_permille("‰1") == -EINVAL);
|
||||
assert_se(parse_permille("1‰‰") == -EINVAL);
|
||||
assert_se(parse_permille("3.2‰") == -EINVAL);
|
||||
assert_se(parse_permille("0.1‰") == -EINVAL);
|
||||
|
||||
assert_se(parse_permille("0%") == 0);
|
||||
assert_se(parse_permille("55%") == 550);
|
||||
assert_se(parse_permille("55.5%") == 555);
|
||||
assert_se(parse_permille("100%") == 1000);
|
||||
assert_se(parse_permille("-7%") == -ERANGE);
|
||||
assert_se(parse_permille("107%") == -ERANGE);
|
||||
assert_se(parse_permille("%") == -EINVAL);
|
||||
assert_se(parse_permille("%%") == -EINVAL);
|
||||
assert_se(parse_permille("%1") == -EINVAL);
|
||||
assert_se(parse_permille("1%%") == -EINVAL);
|
||||
assert_se(parse_permille("3.21%") == -EINVAL);
|
||||
assert_se(parse_permille("0.1%") == 1);
|
||||
}
|
||||
|
||||
static void test_parse_permille_unbounded(void) {
|
||||
assert_se(parse_permille_unbounded("1001‰") == 1001);
|
||||
assert_se(parse_permille_unbounded("4000‰") == 4000);
|
||||
assert_se(parse_permille_unbounded("2147483647‰") == 2147483647);
|
||||
assert_se(parse_permille_unbounded("2147483648‰") == -ERANGE);
|
||||
assert_se(parse_permille_unbounded("4294967295‰") == -ERANGE);
|
||||
assert_se(parse_permille_unbounded("4294967296‰") == -ERANGE);
|
||||
|
||||
assert_se(parse_permille_unbounded("101%") == 1010);
|
||||
assert_se(parse_permille_unbounded("400%") == 4000);
|
||||
assert_se(parse_permille_unbounded("214748364.7%") == 2147483647);
|
||||
assert_se(parse_permille_unbounded("214748364.8%") == -ERANGE);
|
||||
assert_se(parse_permille_unbounded("429496729.5%") == -ERANGE);
|
||||
assert_se(parse_permille_unbounded("429496729.6%") == -ERANGE);
|
||||
}
|
||||
|
||||
static void test_parse_permyriad(void) {
|
||||
assert_se(parse_permyriad("") == -EINVAL);
|
||||
assert_se(parse_permyriad("foo") == -EINVAL);
|
||||
assert_se(parse_permyriad("0") == -EINVAL);
|
||||
assert_se(parse_permyriad("50") == -EINVAL);
|
||||
assert_se(parse_permyriad("100") == -EINVAL);
|
||||
assert_se(parse_permyriad("-1") == -EINVAL);
|
||||
|
||||
assert_se(parse_permyriad("0‱") == 0);
|
||||
assert_se(parse_permyriad("555‱") == 555);
|
||||
assert_se(parse_permyriad("1000‱") == 1000);
|
||||
assert_se(parse_permyriad("-7‱") == -ERANGE);
|
||||
assert_se(parse_permyriad("10007‱") == -ERANGE);
|
||||
assert_se(parse_permyriad("‱") == -EINVAL);
|
||||
assert_se(parse_permyriad("‱‱") == -EINVAL);
|
||||
assert_se(parse_permyriad("‱1") == -EINVAL);
|
||||
assert_se(parse_permyriad("1‱‱") == -EINVAL);
|
||||
assert_se(parse_permyriad("3.2‱") == -EINVAL);
|
||||
|
||||
assert_se(parse_permyriad("0‰") == 0);
|
||||
assert_se(parse_permyriad("555.5‰") == 5555);
|
||||
assert_se(parse_permyriad("1000.0‰") == 10000);
|
||||
assert_se(parse_permyriad("-7‰") == -ERANGE);
|
||||
assert_se(parse_permyriad("1007‰") == -ERANGE);
|
||||
assert_se(parse_permyriad("‰") == -EINVAL);
|
||||
assert_se(parse_permyriad("‰‰") == -EINVAL);
|
||||
assert_se(parse_permyriad("‰1") == -EINVAL);
|
||||
assert_se(parse_permyriad("1‰‰") == -EINVAL);
|
||||
assert_se(parse_permyriad("3.22‰") == -EINVAL);
|
||||
|
||||
assert_se(parse_permyriad("0%") == 0);
|
||||
assert_se(parse_permyriad("55%") == 5500);
|
||||
assert_se(parse_permyriad("55.5%") == 5550);
|
||||
assert_se(parse_permyriad("55.50%") == 5550);
|
||||
assert_se(parse_permyriad("55.53%") == 5553);
|
||||
assert_se(parse_permyriad("100%") == 10000);
|
||||
assert_se(parse_permyriad("-7%") == -ERANGE);
|
||||
assert_se(parse_permyriad("107%") == -ERANGE);
|
||||
assert_se(parse_permyriad("%") == -EINVAL);
|
||||
assert_se(parse_permyriad("%%") == -EINVAL);
|
||||
assert_se(parse_permyriad("%1") == -EINVAL);
|
||||
assert_se(parse_permyriad("1%%") == -EINVAL);
|
||||
assert_se(parse_permyriad("3.212%") == -EINVAL);
|
||||
}
|
||||
|
||||
static void test_parse_permyriad_unbounded(void) {
|
||||
assert_se(parse_permyriad_unbounded("1001‱") == 1001);
|
||||
assert_se(parse_permyriad_unbounded("4000‱") == 4000);
|
||||
assert_se(parse_permyriad_unbounded("2147483647‱") == 2147483647);
|
||||
assert_se(parse_permyriad_unbounded("2147483648‱") == -ERANGE);
|
||||
assert_se(parse_permyriad_unbounded("4294967295‱") == -ERANGE);
|
||||
assert_se(parse_permyriad_unbounded("4294967296‱") == -ERANGE);
|
||||
|
||||
assert_se(parse_permyriad_unbounded("101‰") == 1010);
|
||||
assert_se(parse_permyriad_unbounded("400‰") == 4000);
|
||||
assert_se(parse_permyriad_unbounded("214748364.7‰") == 2147483647);
|
||||
assert_se(parse_permyriad_unbounded("214748364.8‰") == -ERANGE);
|
||||
assert_se(parse_permyriad_unbounded("429496729.5‰") == -ERANGE);
|
||||
assert_se(parse_permyriad_unbounded("429496729.6‰") == -ERANGE);
|
||||
|
||||
assert_se(parse_permyriad_unbounded("99%") == 9900);
|
||||
assert_se(parse_permyriad_unbounded("40%") == 4000);
|
||||
assert_se(parse_permyriad_unbounded("21474836.47%") == 2147483647);
|
||||
assert_se(parse_permyriad_unbounded("21474836.48%") == -ERANGE);
|
||||
assert_se(parse_permyriad_unbounded("42949672.95%") == -ERANGE);
|
||||
assert_se(parse_permyriad_unbounded("42949672.96%") == -ERANGE);
|
||||
}
|
||||
|
||||
static void test_scale(void) {
|
||||
/* Check some fixed values */
|
||||
assert_se(UINT32_SCALE_FROM_PERCENT(0) == 0);
|
||||
assert_se(UINT32_SCALE_FROM_PERCENT(50) == UINT32_MAX/2+1);
|
||||
assert_se(UINT32_SCALE_FROM_PERCENT(100) == UINT32_MAX);
|
||||
|
||||
assert_se(UINT32_SCALE_FROM_PERMILLE(0) == 0);
|
||||
assert_se(UINT32_SCALE_FROM_PERMILLE(500) == UINT32_MAX/2+1);
|
||||
assert_se(UINT32_SCALE_FROM_PERMILLE(1000) == UINT32_MAX);
|
||||
|
||||
assert_se(UINT32_SCALE_FROM_PERMYRIAD(0) == 0);
|
||||
assert_se(UINT32_SCALE_FROM_PERMYRIAD(5000) == UINT32_MAX/2+1);
|
||||
assert_se(UINT32_SCALE_FROM_PERMYRIAD(10000) == UINT32_MAX);
|
||||
|
||||
/* Make sure there's no numeric noise on the 0%…100% scale when converting from percent and back. */
|
||||
for (int percent = 0; percent <= 100; percent++) {
|
||||
log_debug("%i%% → %" PRIu32 " → %i%%",
|
||||
percent,
|
||||
UINT32_SCALE_FROM_PERCENT(percent),
|
||||
UINT32_SCALE_TO_PERCENT(UINT32_SCALE_FROM_PERCENT(percent)));
|
||||
|
||||
assert_se(UINT32_SCALE_TO_PERCENT(UINT32_SCALE_FROM_PERCENT(percent)) == percent);
|
||||
}
|
||||
|
||||
/* Make sure there's no numeric noise on the 0‰…1000‰ scale when converting from permille and back. */
|
||||
for (int permille = 0; permille <= 1000; permille++) {
|
||||
log_debug("%i‰ → %" PRIu32 " → %i‰",
|
||||
permille,
|
||||
UINT32_SCALE_FROM_PERMILLE(permille),
|
||||
UINT32_SCALE_TO_PERMILLE(UINT32_SCALE_FROM_PERMILLE(permille)));
|
||||
|
||||
assert_se(UINT32_SCALE_TO_PERMILLE(UINT32_SCALE_FROM_PERMILLE(permille)) == permille);
|
||||
}
|
||||
|
||||
/* Make sure there's no numeric noise on the 0‱…10000‱ scale when converting from permyriad and back. */
|
||||
for (int permyriad = 0; permyriad <= 10000; permyriad++) {
|
||||
log_debug("%i‱ → %" PRIu32 " → %i‱",
|
||||
permyriad,
|
||||
UINT32_SCALE_FROM_PERMYRIAD(permyriad),
|
||||
UINT32_SCALE_TO_PERMYRIAD(UINT32_SCALE_FROM_PERMYRIAD(permyriad)));
|
||||
|
||||
assert_se(UINT32_SCALE_TO_PERMYRIAD(UINT32_SCALE_FROM_PERMYRIAD(permyriad)) == permyriad);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
test_setup_logging(LOG_DEBUG);
|
||||
|
||||
test_parse_percent();
|
||||
test_parse_percent_unbounded();
|
||||
test_parse_permille();
|
||||
test_parse_permille_unbounded();
|
||||
test_parse_permyriad();
|
||||
test_parse_permyriad_unbounded();
|
||||
test_scale();
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue
Block a user