mirror of
https://github.com/systemd/systemd.git
synced 2025-03-25 18:50:18 +03:00
Merge pull request #3481 from poettering/relative-memcg
various changes, most importantly regarding memory metrics
This commit is contained in:
commit
616aab6085
6
TODO
6
TODO
@ -33,7 +33,10 @@ Janitorial Clean-ups:
|
||||
|
||||
Features:
|
||||
|
||||
* use phyical_memory() to allow MemoryLimit= configuration based on available system memory
|
||||
* resolved: make sure when we get an ip address with ifindex suffix, we handle
|
||||
it nicely
|
||||
|
||||
* resolved: maybe add a switch to disable any local caching
|
||||
|
||||
* ProtectKernelLogs= (drops CAP_SYSLOG, add seccomp for syslog() syscall, and DeviceAllow to /dev/kmsg) in service files
|
||||
|
||||
@ -227,7 +230,6 @@ Features:
|
||||
- resolved should optionally register additional per-interface LLMNR
|
||||
names, so that for the container case we can establish the same name
|
||||
(maybe "host") for referencing the server, everywhere.
|
||||
- enable DNSSEC by default
|
||||
- allow clients to request DNSSEC for a single lookup even if DNSSEC is off (?)
|
||||
|
||||
* refcounting in sd-resolve is borked
|
||||
|
@ -92,16 +92,14 @@
|
||||
<refsect1>
|
||||
<title>Automatic Dependencies</title>
|
||||
|
||||
<para>Units with the <varname>Slice=</varname> setting set get
|
||||
automatic <varname>Requires=</varname> and
|
||||
<varname>After=</varname> dependencies on the specified slice
|
||||
unit.</para>
|
||||
<para>Units with the <varname>Slice=</varname> setting set automatically acquire <varname>Requires=</varname> and
|
||||
<varname>After=</varname> dependencies on the specified slice unit.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Unified and Legacy Control Group Hierarchies</title>
|
||||
|
||||
<para>Unified control group hierarchy is the new version of kernel control group interface. Depending on the
|
||||
<para>The unified control group hierarchy is the new version of kernel control group interface. Depending on the
|
||||
resource type, there are differences in resource control capabilities. Also, because of interface changes, some
|
||||
resource types have a separate set of options on the unified hierarchy.</para>
|
||||
|
||||
@ -117,8 +115,8 @@
|
||||
<varlistentry>
|
||||
<term><option>Memory</option></term>
|
||||
<listitem>
|
||||
<para><varname>MemoryMax</varname> replaces <varname>MemoryLimit</varname>. <varname>MemoryLow</varname>
|
||||
and <varname>MemoryHigh</varname> are effective only on unified hierarchy.</para>
|
||||
<para><varname>MemoryMax=</varname> replaces <varname>MemoryLimit=</varname>. <varname>MemoryLow=</varname>
|
||||
and <varname>MemoryHigh=</varname> are effective only on unified hierarchy.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
@ -228,9 +226,11 @@
|
||||
reclaimed as long as memory can be reclaimed from unprotected units.</para>
|
||||
|
||||
<para>Takes a memory size in bytes. If the value is suffixed with K, M, G or T, the specified memory size is
|
||||
parsed as Kilobytes, Megabytes, Gigabytes, or Terabytes (with the base 1024), respectively. This controls the
|
||||
<literal>memory.low</literal> control group attribute. For details about this control group attribute, see
|
||||
<ulink url="https://www.kernel.org/doc/Documentation/cgroup-v2.txt">cgroup-v2.txt</ulink>.</para>
|
||||
parsed as Kilobytes, Megabytes, Gigabytes, or Terabytes (with the base 1024), respectively. Alternatively, a
|
||||
percentage value may be specified, which is taken relative to the installed physical memory on the
|
||||
system. This controls the <literal>memory.low</literal> control group attribute. For details about this
|
||||
control group attribute, see <ulink
|
||||
url="https://www.kernel.org/doc/Documentation/cgroup-v2.txt">cgroup-v2.txt</ulink>.</para>
|
||||
|
||||
<para>Implies <literal>MemoryAccounting=true</literal>.</para>
|
||||
|
||||
@ -247,7 +247,9 @@
|
||||
aggressively in such cases. This is the main mechanism to control memory usage of a unit.</para>
|
||||
|
||||
<para>Takes a memory size in bytes. If the value is suffixed with K, M, G or T, the specified memory size is
|
||||
parsed as Kilobytes, Megabytes, Gigabytes, or Terabytes (with the base 1024), respectively. If assigned the
|
||||
parsed as Kilobytes, Megabytes, Gigabytes, or Terabytes (with the base 1024), respectively. Alternatively, a
|
||||
percentage value may be specified, which is taken relative to the installed physical memory on the
|
||||
system. If assigned the
|
||||
special value <literal>infinity</literal>, no memory limit is applied. This controls the
|
||||
<literal>memory.high</literal> control group attribute. For details about this control group attribute, see
|
||||
<ulink url="https://www.kernel.org/doc/Documentation/cgroup-v2.txt">cgroup-v2.txt</ulink>.</para>
|
||||
@ -268,8 +270,9 @@
|
||||
last line of defense.</para>
|
||||
|
||||
<para>Takes a memory size in bytes. If the value is suffixed with K, M, G or T, the specified memory size is
|
||||
parsed as Kilobytes, Megabytes, Gigabytes, or Terabytes (with the base 1024), respectively. If assigned the
|
||||
special value <literal>infinity</literal>, no memory limit is applied. This controls the
|
||||
parsed as Kilobytes, Megabytes, Gigabytes, or Terabytes (with the base 1024), respectively. Alternatively, a
|
||||
percentage value may be specified, which is taken relative to the installed physical memory on the system. If
|
||||
assigned the special value <literal>infinity</literal>, no memory limit is applied. This controls the
|
||||
<literal>memory.max</literal> control group attribute. For details about this control group attribute, see
|
||||
<ulink url="https://www.kernel.org/doc/Documentation/cgroup-v2.txt">cgroup-v2.txt</ulink>.</para>
|
||||
|
||||
@ -284,17 +287,14 @@
|
||||
<term><varname>MemoryLimit=<replaceable>bytes</replaceable></varname></term>
|
||||
|
||||
<listitem>
|
||||
<para>Specify the limit on maximum memory usage of the
|
||||
executed processes. The limit specifies how much process and
|
||||
kernel memory can be used by tasks in this unit. Takes a
|
||||
memory size in bytes. If the value is suffixed with K, M, G
|
||||
or T, the specified memory size is parsed as Kilobytes,
|
||||
Megabytes, Gigabytes, or Terabytes (with the base 1024),
|
||||
respectively. If assigned the special value
|
||||
<literal>infinity</literal>, no memory limit is applied. This
|
||||
controls the <literal>memory.limit_in_bytes</literal>
|
||||
control group attribute. For details about this control
|
||||
group attribute, see <ulink
|
||||
<para>Specify the limit on maximum memory usage of the executed processes. The limit specifies how much
|
||||
process and kernel memory can be used by tasks in this unit. Takes a memory size in bytes. If the value is
|
||||
suffixed with K, M, G or T, the specified memory size is parsed as Kilobytes, Megabytes, Gigabytes, or
|
||||
Terabytes (with the base 1024), respectively. Alternatively, a percentage value may be specified, which is
|
||||
taken relative to the installed physical memory on the system. If assigned the special value
|
||||
<literal>infinity</literal>, no memory limit is applied. This controls the
|
||||
<literal>memory.limit_in_bytes</literal> control group attribute. For details about this control group
|
||||
attribute, see <ulink
|
||||
url="https://www.kernel.org/doc/Documentation/cgroup-v1/memory.txt">memory.txt</ulink>.</para>
|
||||
|
||||
<para>Implies <literal>MemoryAccounting=true</literal>.</para>
|
||||
|
@ -532,3 +532,22 @@ int parse_fractional_part_u(const char **p, size_t digits, unsigned *res) {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int parse_percent(const char *p) {
|
||||
const char *pc, *n;
|
||||
unsigned v;
|
||||
int r;
|
||||
|
||||
pc = endswith(p, "%");
|
||||
if (!pc)
|
||||
return -EINVAL;
|
||||
|
||||
n = strndupa(p, pc - p);
|
||||
r = safe_atou(n, &v);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (v > 100)
|
||||
return -ERANGE;
|
||||
|
||||
return (int) v;
|
||||
}
|
||||
|
@ -105,3 +105,5 @@ static inline int safe_atozu(const char *s, size_t *ret_u) {
|
||||
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(const char *p);
|
||||
|
@ -36,6 +36,7 @@
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "build.h"
|
||||
#include "cgroup-util.h"
|
||||
#include "def.h"
|
||||
#include "dirent-util.h"
|
||||
#include "fd-util.h"
|
||||
@ -771,15 +772,64 @@ int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int
|
||||
}
|
||||
|
||||
uint64_t physical_memory(void) {
|
||||
long mem;
|
||||
_cleanup_free_ char *root = NULL, *value = NULL;
|
||||
uint64_t mem, lim;
|
||||
size_t ps;
|
||||
long sc;
|
||||
|
||||
/* We return this as uint64_t in case we are running as 32bit
|
||||
* process on a 64bit kernel with huge amounts of memory */
|
||||
/* We return this as uint64_t in case we are running as 32bit process on a 64bit kernel with huge amounts of
|
||||
* memory.
|
||||
*
|
||||
* In order to support containers nicely that have a configured memory limit we'll take the minimum of the
|
||||
* physically reported amount of memory and the limit configured for the root cgroup, if there is any. */
|
||||
|
||||
mem = sysconf(_SC_PHYS_PAGES);
|
||||
assert(mem > 0);
|
||||
sc = sysconf(_SC_PHYS_PAGES);
|
||||
assert(sc > 0);
|
||||
|
||||
return (uint64_t) mem * (uint64_t) page_size();
|
||||
ps = page_size();
|
||||
mem = (uint64_t) sc * (uint64_t) ps;
|
||||
|
||||
if (cg_get_root_path(&root) < 0)
|
||||
return mem;
|
||||
|
||||
if (cg_get_attribute("memory", root, "memory.limit_in_bytes", &value))
|
||||
return mem;
|
||||
|
||||
if (safe_atou64(value, &lim) < 0)
|
||||
return mem;
|
||||
|
||||
/* Make sure the limit is a multiple of our own page size */
|
||||
lim /= ps;
|
||||
lim *= ps;
|
||||
|
||||
return MIN(mem, lim);
|
||||
}
|
||||
|
||||
uint64_t physical_memory_scale(uint64_t v, uint64_t max) {
|
||||
uint64_t p, m, ps, r;
|
||||
|
||||
assert(max > 0);
|
||||
|
||||
/* Returns the physical memory size, multiplied by v divided by max. Returns UINT64_MAX on overflow. On success
|
||||
* the result is a multiple of the page size (rounds down). */
|
||||
|
||||
ps = page_size();
|
||||
assert(ps > 0);
|
||||
|
||||
p = physical_memory() / ps;
|
||||
assert(p > 0);
|
||||
|
||||
m = p * v;
|
||||
if (m / p != v)
|
||||
return UINT64_MAX;
|
||||
|
||||
m /= max;
|
||||
|
||||
r = m * ps;
|
||||
if (r / ps != m)
|
||||
return UINT64_MAX;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int update_reboot_parameter_and_warn(const char *param) {
|
||||
|
@ -184,6 +184,7 @@ int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int *
|
||||
int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd);
|
||||
|
||||
uint64_t physical_memory(void);
|
||||
uint64_t physical_memory_scale(uint64_t v, uint64_t max);
|
||||
|
||||
int update_reboot_parameter_and_warn(const char *param);
|
||||
|
||||
|
@ -641,7 +641,7 @@ int bus_cgroup_set_property(
|
||||
|
||||
return 1;
|
||||
|
||||
} else if (streq(name, "BlockIOReadBandwidth") || streq(name, "BlockIOWriteBandwidth")) {
|
||||
} else if (STR_IN_SET(name, "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) {
|
||||
const char *path;
|
||||
bool read = true;
|
||||
unsigned n = 0;
|
||||
@ -835,6 +835,8 @@ int bus_cgroup_set_property(
|
||||
r = sd_bus_message_read(message, "t", &v);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (v <= 0)
|
||||
return sd_bus_error_set_errnof(error, EINVAL, "%s= is too small", name);
|
||||
|
||||
if (mode != UNIT_CHECK) {
|
||||
if (streq(name, "MemoryLow"))
|
||||
@ -847,19 +849,53 @@ int bus_cgroup_set_property(
|
||||
unit_invalidate_cgroup(u, CGROUP_MASK_MEMORY);
|
||||
|
||||
if (v == CGROUP_LIMIT_MAX)
|
||||
unit_write_drop_in_private_format(u, mode, name, "%s=max", name);
|
||||
unit_write_drop_in_private_format(u, mode, name, "%s=infinity", name);
|
||||
else
|
||||
unit_write_drop_in_private_format(u, mode, name, "%s=%" PRIu64, name, v);
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
} else if (STR_IN_SET(name, "MemoryLowByPhysicalMemory", "MemoryHighByPhysicalMemory", "MemoryMaxByPhysicalMemory")) {
|
||||
uint32_t raw;
|
||||
uint64_t v;
|
||||
|
||||
r = sd_bus_message_read(message, "u", &raw);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
v = physical_memory_scale(raw, UINT32_MAX);
|
||||
if (v <= 0 || v == UINT64_MAX)
|
||||
return sd_bus_error_set_errnof(error, EINVAL, "%s= is out of range", name);
|
||||
|
||||
if (mode != UNIT_CHECK) {
|
||||
const char *e;
|
||||
|
||||
/* Chop off suffix */
|
||||
assert_se(e = endswith(name, "ByPhysicalMemory"));
|
||||
name = strndupa(name, e - name);
|
||||
|
||||
if (streq(name, "MemoryLow"))
|
||||
c->memory_low = v;
|
||||
else if (streq(name, "MemoryHigh"))
|
||||
c->memory_high = v;
|
||||
else
|
||||
c->memory_max = v;
|
||||
|
||||
unit_invalidate_cgroup(u, CGROUP_MASK_MEMORY);
|
||||
unit_write_drop_in_private_format(u, mode, name, "%s=%" PRIu32 "%%", name, (uint32_t) (DIV_ROUND_UP((uint64_t) raw * 100, (uint64_t) UINT32_MAX)));
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
} else if (streq(name, "MemoryLimit")) {
|
||||
uint64_t limit;
|
||||
|
||||
r = sd_bus_message_read(message, "t", &limit);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (limit <= 0)
|
||||
return sd_bus_error_set_errnof(error, EINVAL, "%s= is too small", name);
|
||||
|
||||
if (mode != UNIT_CHECK) {
|
||||
c->memory_limit = limit;
|
||||
@ -873,6 +909,26 @@ int bus_cgroup_set_property(
|
||||
|
||||
return 1;
|
||||
|
||||
} else if (streq(name, "MemoryLimitByPhysicalMemory")) {
|
||||
uint64_t limit;
|
||||
uint32_t raw;
|
||||
|
||||
r = sd_bus_message_read(message, "u", &raw);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
limit = physical_memory_scale(raw, UINT32_MAX);
|
||||
if (limit <= 0 || limit == UINT64_MAX)
|
||||
return sd_bus_error_set_errnof(error, EINVAL, "%s= is out of range", name);
|
||||
|
||||
if (mode != UNIT_CHECK) {
|
||||
c->memory_limit = limit;
|
||||
unit_invalidate_cgroup(u, CGROUP_MASK_MEMORY);
|
||||
unit_write_drop_in_private_format(u, mode, "MemoryLimit", "MemoryLimit=%" PRIu32 "%%", (uint32_t) (DIV_ROUND_UP((uint64_t) raw * 100, (uint64_t) UINT32_MAX)));
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
} else if (streq(name, "DevicePolicy")) {
|
||||
const char *policy;
|
||||
CGroupDevicePolicy p;
|
||||
|
@ -2774,7 +2774,7 @@ int config_parse_cpu_quota(
|
||||
void *userdata) {
|
||||
|
||||
CGroupContext *c = data;
|
||||
double percent;
|
||||
int r;
|
||||
|
||||
assert(filename);
|
||||
assert(lvalue);
|
||||
@ -2785,18 +2785,13 @@ int config_parse_cpu_quota(
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!endswith(rvalue, "%")) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, 0, "CPU quota '%s' not ending in '%%'. Ignoring.", rvalue);
|
||||
r = parse_percent(rvalue);
|
||||
if (r <= 0) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, r, "CPU quota '%s' invalid. Ignoring.", rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (sscanf(rvalue, "%lf%%", &percent) != 1 || percent <= 0) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, 0, "CPU quota '%s' invalid. Ignoring.", rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
c->cpu_quota_per_sec_usec = (usec_t) (percent * USEC_PER_SEC / 100);
|
||||
|
||||
c->cpu_quota_per_sec_usec = ((usec_t) r * USEC_PER_SEC) / 100U;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -2817,9 +2812,19 @@ int config_parse_memory_limit(
|
||||
int r;
|
||||
|
||||
if (!isempty(rvalue) && !streq(rvalue, "infinity")) {
|
||||
r = parse_size(rvalue, 1024, &bytes);
|
||||
if (r < 0 || bytes < 1) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, r, "Memory limit '%s' invalid. Ignoring.", rvalue);
|
||||
|
||||
r = parse_percent(rvalue);
|
||||
if (r < 0) {
|
||||
r = parse_size(rvalue, 1024, &bytes);
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, r, "Memory limit '%s' invalid. Ignoring.", rvalue);
|
||||
return 0;
|
||||
}
|
||||
} else
|
||||
bytes = physical_memory_scale(r, 100U);
|
||||
|
||||
if (bytes < 1) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, 0, "Memory limit '%s' too small. Ignoring.", rvalue);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -3375,7 +3375,7 @@ int unit_write_drop_in(Unit *u, UnitSetPropertiesMode mode, const char *name, co
|
||||
return -EINVAL;
|
||||
|
||||
wrapped = strjoina("# This is a drop-in unit file extension, created via \"systemctl set-property\"\n"
|
||||
"or an equivalent operation. Do not edit.\n",
|
||||
"# or an equivalent operation. Do not edit.\n",
|
||||
data,
|
||||
"\n");
|
||||
|
||||
|
@ -307,7 +307,7 @@ static int ndisc_recv(sd_event_source *s, int fd, uint32_t revents, void *userda
|
||||
if (cmsg->cmsg_level == SOL_SOCKET &&
|
||||
cmsg->cmsg_type == SO_TIMESTAMP &&
|
||||
cmsg->cmsg_len == CMSG_LEN(sizeof(struct timeval)))
|
||||
triple_timestamp_from_realtime(&rt->timestamp, timeval_load(CMSG_DATA(cmsg)));
|
||||
triple_timestamp_from_realtime(&rt->timestamp, timeval_load((struct timeval*) CMSG_DATA(cmsg)));
|
||||
}
|
||||
|
||||
if (!triple_timestamp_is_set(&rt->timestamp))
|
||||
|
@ -843,7 +843,6 @@ int config_parse_tmpfs_size(
|
||||
void *userdata) {
|
||||
|
||||
size_t *sz = data;
|
||||
const char *e;
|
||||
int r;
|
||||
|
||||
assert(filename);
|
||||
@ -851,29 +850,17 @@ int config_parse_tmpfs_size(
|
||||
assert(rvalue);
|
||||
assert(data);
|
||||
|
||||
e = endswith(rvalue, "%");
|
||||
if (e) {
|
||||
unsigned long ul;
|
||||
char *f;
|
||||
|
||||
errno = 0;
|
||||
ul = strtoul(rvalue, &f, 10);
|
||||
if (errno > 0 || f != e) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, errno, "Failed to parse percentage value, ignoring: %s", rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ul <= 0 || ul >= 100) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, 0, "Percentage value out of range, ignoring: %s", rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
*sz = PAGE_ALIGN((size_t) ((physical_memory() * (uint64_t) ul) / (uint64_t) 100));
|
||||
} else {
|
||||
/* First, try to parse as percentage */
|
||||
r = parse_percent(rvalue);
|
||||
if (r > 0 && r < 100)
|
||||
*sz = physical_memory_scale(r, 100U);
|
||||
else {
|
||||
uint64_t k;
|
||||
|
||||
/* If the passed argument was not a percentage, or out of range, parse as byte size */
|
||||
|
||||
r = parse_size(rvalue, 1024, &k);
|
||||
if (r < 0 || (uint64_t) (size_t) k != k) {
|
||||
if (r < 0 || k <= 0 || (uint64_t) (size_t) k != k) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ static void manager_reset_config(Manager *m) {
|
||||
m->idle_action_usec = 30 * USEC_PER_MINUTE;
|
||||
m->idle_action = HANDLE_IGNORE;
|
||||
|
||||
m->runtime_dir_size = PAGE_ALIGN((size_t) (physical_memory() / 10)); /* 10% */
|
||||
m->runtime_dir_size = physical_memory_scale(10U, 100U); /* 10% */
|
||||
m->user_tasks_max = 12288;
|
||||
m->sessions_max = 8192;
|
||||
m->inhibitors_max = 8192;
|
||||
|
@ -83,18 +83,14 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
|
||||
|
||||
if (isempty(eq))
|
||||
r = sd_bus_message_append(m, "sv", "CPUQuotaPerSecUSec", "t", USEC_INFINITY);
|
||||
else if (endswith(eq, "%")) {
|
||||
double percent;
|
||||
|
||||
if (sscanf(eq, "%lf%%", &percent) != 1 || percent <= 0) {
|
||||
log_error("CPU quota '%s' invalid.", eq);
|
||||
else {
|
||||
r = parse_percent(eq);
|
||||
if (r <= 0) {
|
||||
log_error_errno(r, "CPU quota '%s' invalid.", eq);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
r = sd_bus_message_append(m, "sv", "CPUQuotaPerSecUSec", "t", (usec_t) percent * USEC_PER_SEC / 100);
|
||||
} else {
|
||||
log_error("CPU quota needs to be in percent.");
|
||||
return -EINVAL;
|
||||
r = sd_bus_message_append(m, "sv", "CPUQuotaPerSecUSec", "t", (usec_t) r * USEC_PER_SEC / 100U);
|
||||
}
|
||||
|
||||
goto finish;
|
||||
@ -110,6 +106,7 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
|
||||
char *n;
|
||||
usec_t t;
|
||||
size_t l;
|
||||
|
||||
r = parse_sec(eq, &t);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse %s= parameter: %s", field, eq);
|
||||
@ -123,6 +120,34 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
|
||||
strcpy(mempcpy(n, field, l - 3), "USec");
|
||||
r = sd_bus_message_append(m, "sv", n, "t", t);
|
||||
goto finish;
|
||||
|
||||
} else if (STR_IN_SET(field, "MemoryLow", "MemoryHigh", "MemoryMax", "MemoryLimit")) {
|
||||
uint64_t bytes;
|
||||
|
||||
if (isempty(eq) || streq(eq, "infinity"))
|
||||
bytes = CGROUP_LIMIT_MAX;
|
||||
else {
|
||||
r = parse_percent(eq);
|
||||
if (r >= 0) {
|
||||
char *n;
|
||||
|
||||
/* When this is a percentage we'll convert this into a relative value in the range
|
||||
* 0…UINT32_MAX and pass it in the MemoryLowByPhysicalMemory property (and related
|
||||
* ones). This way the physical memory size can be determined server-side */
|
||||
|
||||
n = strjoina(field, "ByPhysicalMemory");
|
||||
r = sd_bus_message_append(m, "sv", n, "u", (uint32_t) (((uint64_t) UINT32_MAX * r) / 100U));
|
||||
goto finish;
|
||||
|
||||
} else {
|
||||
r = parse_size(eq, 1024, &bytes);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse bytes specification %s", assignment);
|
||||
}
|
||||
}
|
||||
|
||||
r = sd_bus_message_append(m, "sv", field, "t", bytes);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
|
||||
@ -166,21 +191,6 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
|
||||
|
||||
r = sd_bus_message_append(m, "v", "b", r);
|
||||
|
||||
} else if (STR_IN_SET(field, "MemoryLow", "MemoryHigh", "MemoryMax", "MemoryLimit")) {
|
||||
uint64_t bytes;
|
||||
|
||||
if (isempty(eq) || streq(eq, "infinity"))
|
||||
bytes = CGROUP_LIMIT_MAX;
|
||||
else {
|
||||
r = parse_size(eq, 1024, &bytes);
|
||||
if (r < 0) {
|
||||
log_error("Failed to parse bytes specification %s", assignment);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
r = sd_bus_message_append(m, "v", "t", bytes);
|
||||
|
||||
} else if (streq(field, "TasksMax")) {
|
||||
uint64_t n;
|
||||
|
||||
|
@ -475,6 +475,24 @@ static void test_safe_atod(void) {
|
||||
assert_se(*e == ',');
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
log_parse_environment();
|
||||
log_open();
|
||||
@ -488,6 +506,7 @@ int main(int argc, char *argv[]) {
|
||||
test_safe_atou16();
|
||||
test_safe_atoi16();
|
||||
test_safe_atod();
|
||||
test_parse_percent();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "def.h"
|
||||
#include "fileio.h"
|
||||
#include "fs-util.h"
|
||||
#include "parse-util.h"
|
||||
#include "raw-clone.h"
|
||||
#include "rm-rf.h"
|
||||
#include "string-util.h"
|
||||
@ -263,6 +264,53 @@ static void test_raw_clone(void) {
|
||||
}
|
||||
}
|
||||
|
||||
static void test_physical_memory(void) {
|
||||
uint64_t p;
|
||||
char buf[FORMAT_BYTES_MAX];
|
||||
|
||||
p = physical_memory();
|
||||
assert_se(p > 0);
|
||||
assert_se(p < UINT64_MAX);
|
||||
assert_se(p % page_size() == 0);
|
||||
|
||||
log_info("Memory: %s (%" PRIu64 ")", format_bytes(buf, sizeof(buf), p), p);
|
||||
}
|
||||
|
||||
static void test_physical_memory_scale(void) {
|
||||
uint64_t p;
|
||||
|
||||
p = physical_memory();
|
||||
|
||||
assert_se(physical_memory_scale(0, 100) == 0);
|
||||
assert_se(physical_memory_scale(100, 100) == p);
|
||||
|
||||
log_info("Memory original: %" PRIu64, physical_memory());
|
||||
log_info("Memory scaled by 50%%: %" PRIu64, physical_memory_scale(50, 100));
|
||||
log_info("Memory divided by 2: %" PRIu64, physical_memory() / 2);
|
||||
log_info("Page size: %zu", page_size());
|
||||
|
||||
/* There might be an uneven number of pages, hence permit these calculations to be half a page off... */
|
||||
assert_se(page_size()/2 + physical_memory_scale(50, 100) - p/2 <= page_size());
|
||||
assert_se(physical_memory_scale(200, 100) == p*2);
|
||||
|
||||
assert_se(physical_memory_scale(0, 1) == 0);
|
||||
assert_se(physical_memory_scale(1, 1) == p);
|
||||
assert_se(physical_memory_scale(2, 1) == p*2);
|
||||
|
||||
assert_se(physical_memory_scale(0, 2) == 0);
|
||||
|
||||
assert_se(page_size()/2 + physical_memory_scale(1, 2) - p/2 <= page_size());
|
||||
assert_se(physical_memory_scale(2, 2) == p);
|
||||
assert_se(physical_memory_scale(4, 2) == p*2);
|
||||
|
||||
assert_se(physical_memory_scale(0, UINT32_MAX) == 0);
|
||||
assert_se(physical_memory_scale(UINT32_MAX, UINT32_MAX) == p);
|
||||
|
||||
/* overflow */
|
||||
assert_se(physical_memory_scale(UINT64_MAX/4, UINT64_MAX) == UINT64_MAX);
|
||||
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
log_parse_environment();
|
||||
log_open();
|
||||
@ -277,6 +325,8 @@ int main(int argc, char *argv[]) {
|
||||
test_log2i();
|
||||
test_execute_directory();
|
||||
test_raw_clone();
|
||||
test_physical_memory();
|
||||
test_physical_memory_scale();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user