mirror of
https://github.com/systemd/systemd.git
synced 2026-11-06 05:17:24 +00:00
nspawn: accept negative OCI CPU quotas as unlimited
Treat any negative OCI CPU quota as unlimited, with or without a period. Keep requiring quota and period together for finite quotas. Cover negative quotas, missing quota or period, zero quota, and the first quota value that would overflow the conversion.
This commit is contained in:
1 parent
e48149040c
commit
b308d3ef58
2 files changed
+77
-8
No files matched your search
+36
-8
@@ -1178,6 +1178,7 @@ struct cpu_data {
|
||||
uint64_t weight;
|
||||
uint64_t quota;
|
||||
uint64_t period;
|
||||
bool quota_unlimited;
|
||||
CPUSet cpu_set;
|
||||
};
|
||||
|
||||
@@ -1195,13 +1196,40 @@ static int oci_cgroup_cpu_shares(const char *name, sd_json_variant *v, sd_json_d
|
||||
}
|
||||
|
||||
static int oci_cgroup_cpu_quota(const char *name, sd_json_variant *v, sd_json_dispatch_flags_t flags, void *userdata) {
|
||||
uint64_t k, *u = ASSERT_PTR(userdata);
|
||||
struct cpu_data *data = ASSERT_PTR(userdata);
|
||||
uint64_t k;
|
||||
int r;
|
||||
|
||||
k = sd_json_variant_unsigned(v);
|
||||
if (k <= 0 || k >= UINT64_MAX)
|
||||
return json_log(v, flags, SYNTHETIC_ERRNO(ERANGE), "period/quota value out of range.");
|
||||
if (sd_json_variant_is_negative(v)) {
|
||||
data->quota_unlimited = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
*u = k;
|
||||
r = sd_json_dispatch_uint64(name, v, flags, &k);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (k == 0 || k > UINT64_MAX / USEC_PER_SEC)
|
||||
return json_log(v, flags, SYNTHETIC_ERRNO(ERANGE), "quota value out of range.");
|
||||
|
||||
data->quota_unlimited = false;
|
||||
data->quota = k;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int oci_cgroup_cpu_period(const char *name, sd_json_variant *v, sd_json_dispatch_flags_t flags, void *userdata) {
|
||||
uint64_t *p = ASSERT_PTR(userdata);
|
||||
uint64_t k;
|
||||
int r;
|
||||
|
||||
r = sd_json_dispatch_uint64(name, v, flags, &k);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (k == 0 || k >= UINT64_MAX)
|
||||
return json_log(v, flags, SYNTHETIC_ERRNO(ERANGE), "period value out of range.");
|
||||
|
||||
*p = k;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1224,8 +1252,8 @@ static int oci_cgroup_cpu(const char *name, sd_json_variant *v, sd_json_dispatch
|
||||
|
||||
static const sd_json_dispatch_field table[] = {
|
||||
{ "shares", SD_JSON_VARIANT_UNSIGNED, oci_cgroup_cpu_shares, offsetof(struct cpu_data, weight), 0 },
|
||||
{ "quota", SD_JSON_VARIANT_UNSIGNED, oci_cgroup_cpu_quota, offsetof(struct cpu_data, quota), 0 },
|
||||
{ "period", SD_JSON_VARIANT_UNSIGNED, oci_cgroup_cpu_quota, offsetof(struct cpu_data, period), 0 },
|
||||
{ "quota", SD_JSON_VARIANT_INTEGER, oci_cgroup_cpu_quota, 0, 0 },
|
||||
{ "period", SD_JSON_VARIANT_UNSIGNED, oci_cgroup_cpu_period, offsetof(struct cpu_data, period), 0 },
|
||||
{ "realtimeRuntime", SD_JSON_VARIANT_UNSIGNED, oci_unsupported, 0, 0 },
|
||||
{ "realtimePeriod", SD_JSON_VARIANT_UNSIGNED, oci_unsupported, 0, 0 },
|
||||
{ "cpus", SD_JSON_VARIANT_STRING, oci_cgroup_cpu_cpus, 0, 0 },
|
||||
@@ -1273,7 +1301,7 @@ static int oci_cgroup_cpu(const char *name, sd_json_variant *v, sd_json_dispatch
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
} else if ((data.quota != UINT64_MAX) != (data.period != UINT64_MAX))
|
||||
} else if (!data.quota_unlimited && (data.quota != UINT64_MAX) != (data.period != UINT64_MAX))
|
||||
return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
|
||||
"CPU quota and period not used together.");
|
||||
|
||||
|
||||
@@ -63,6 +63,42 @@ cat >"$OCI/config.json" <<EOF
|
||||
EOF
|
||||
systemd-nspawn --oci-bundle="$OCI" bash -xec 'mountpoint /root'
|
||||
|
||||
# A negative CPU quota is treated as unlimited, both with and without a period
|
||||
cat >"$OCI/config.json" <<EOF
|
||||
{
|
||||
"ociVersion" : "1.0.0",
|
||||
"root" : {
|
||||
"path" : "rootfs"
|
||||
},
|
||||
"linux" : {
|
||||
"resources" : {
|
||||
"cpu" : {
|
||||
"quota" : -2,
|
||||
"period" : 100000
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
systemd-nspawn --oci-bundle="$OCI" bash -xec 'read -r quota _ </sys/fs/cgroup/cpu.max; [[ $quota == max ]]'
|
||||
|
||||
cat >"$OCI/config.json" <<EOF
|
||||
{
|
||||
"ociVersion" : "1.0.0",
|
||||
"root" : {
|
||||
"path" : "rootfs"
|
||||
},
|
||||
"linux" : {
|
||||
"resources" : {
|
||||
"cpu" : {
|
||||
"quota" : -1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
systemd-nspawn --oci-bundle="$OCI" bash -xec 'read -r quota _ </sys/fs/cgroup/cpu.max; [[ $quota == max ]]'
|
||||
|
||||
# And now for something a bit more involved
|
||||
# Notes:
|
||||
# - the hooks are parsed & processed, but never executed
|
||||
@@ -420,6 +456,11 @@ INVALID_SNIPPETS=(
|
||||
'"process" : { "capabilities" : { "bounding" : [ "FOO_BAR" ] } }'
|
||||
# Unsupported option (without JSON_PERMISSIVE)
|
||||
'"linux" : { "resources" : { "cpu" : { "realtimeRuntime" : 1 } } }'
|
||||
# Invalid CPU quota
|
||||
'"linux" : { "resources" : { "cpu" : { "quota" : 0, "period" : 100000 } } }'
|
||||
'"linux" : { "resources" : { "cpu" : { "quota" : 100000 } } }'
|
||||
'"linux" : { "resources" : { "cpu" : { "period" : 100000 } } }'
|
||||
'"linux" : { "resources" : { "cpu" : { "quota" : 18446744073710, "period" : 100000 } } }'
|
||||
# Invalid namespace
|
||||
'"linux" : { "namespaces" : [ { "type" : "foo" } ] }'
|
||||
# Namespace path for a non-network namespace
|
||||
|
||||
Reference in new issue
Block a user