2017-11-18 17:09:20 +01:00
|
|
|
/* SPDX-License-Identifier: LGPL-2.1+ */
|
2013-06-27 04:14:27 +02:00
|
|
|
|
2017-09-05 11:16:35 +02:00
|
|
|
#include <arpa/inet.h>
|
|
|
|
|
|
|
|
#include "af-list.h"
|
2015-10-27 03:01:06 +01:00
|
|
|
#include "alloc-util.h"
|
2017-09-06 17:56:15 +02:00
|
|
|
#include "bpf-firewall.h"
|
2020-06-28 16:43:29 +02:00
|
|
|
#include "bus-get-properties.h"
|
2013-11-19 21:12:59 +01:00
|
|
|
#include "cgroup-util.h"
|
|
|
|
#include "cgroup.h"
|
2013-06-27 04:14:27 +02:00
|
|
|
#include "dbus-cgroup.h"
|
2018-01-02 18:30:16 +01:00
|
|
|
#include "dbus-util.h"
|
2019-07-11 15:42:14 +02:00
|
|
|
#include "errno-util.h"
|
2015-10-25 13:14:12 +01:00
|
|
|
#include "fd-util.h"
|
2015-10-26 18:05:03 +01:00
|
|
|
#include "fileio.h"
|
2019-03-13 11:35:47 +01:00
|
|
|
#include "limits-util.h"
|
2015-10-25 13:14:12 +01:00
|
|
|
#include "path-util.h"
|
2013-06-27 04:14:27 +02:00
|
|
|
|
2019-11-05 13:50:28 +01:00
|
|
|
BUS_DEFINE_PROPERTY_GET(bus_property_get_tasks_max, "t", TasksMax, tasks_max_resolve);
|
|
|
|
|
2013-11-19 21:12:59 +01:00
|
|
|
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_cgroup_device_policy, cgroup_device_policy, CGroupDevicePolicy);
|
|
|
|
|
cgroup: Add DisableControllers= directive to disable controller in subtree
Some controllers (like the CPU controller) have a performance cost that
is non-trivial on certain workloads. While this can be mitigated and
improved to an extent, there will for some controllers always be some
overheads associated with the benefits gained from the controller.
Inside Facebook, the fix applied has been to disable the CPU controller
forcibly with `cgroup_disable=cpu` on the kernel command line.
This presents a problem: to disable or reenable the controller, a reboot
is required, but this is quite cumbersome and slow to do for many
thousands of machines, especially machines where disabling/enabling a
stateful service on a machine is a matter of several minutes.
Currently systemd provides some configuration knobs for these in the
form of `[Default]CPUAccounting`, `[Default]MemoryAccounting`, and the
like. The limitation of these is that Default*Accounting is overrideable
by individual services, of which any one could decide to reenable a
controller within the hierarchy at any point just by using a controller
feature implicitly (eg. `CPUWeight`), even if the use of that CPU
feature could just be opportunistic. Since many services are provided by
the distribution, or by upstream teams at a particular organisation,
it's not a sustainable solution to simply try to find and remove
offending directives from these units.
This commit presents a more direct solution -- a DisableControllers=
directive that forcibly disallows a controller from being enabled within
a subtree.
2018-12-03 15:38:06 +01:00
|
|
|
static int property_get_cgroup_mask(
|
2017-11-09 15:29:34 +01:00
|
|
|
sd_bus *bus,
|
|
|
|
const char *path,
|
|
|
|
const char *interface,
|
|
|
|
const char *property,
|
|
|
|
sd_bus_message *reply,
|
|
|
|
void *userdata,
|
|
|
|
sd_bus_error *error) {
|
|
|
|
|
cgroup: Add DisableControllers= directive to disable controller in subtree
Some controllers (like the CPU controller) have a performance cost that
is non-trivial on certain workloads. While this can be mitigated and
improved to an extent, there will for some controllers always be some
overheads associated with the benefits gained from the controller.
Inside Facebook, the fix applied has been to disable the CPU controller
forcibly with `cgroup_disable=cpu` on the kernel command line.
This presents a problem: to disable or reenable the controller, a reboot
is required, but this is quite cumbersome and slow to do for many
thousands of machines, especially machines where disabling/enabling a
stateful service on a machine is a matter of several minutes.
Currently systemd provides some configuration knobs for these in the
form of `[Default]CPUAccounting`, `[Default]MemoryAccounting`, and the
like. The limitation of these is that Default*Accounting is overrideable
by individual services, of which any one could decide to reenable a
controller within the hierarchy at any point just by using a controller
feature implicitly (eg. `CPUWeight`), even if the use of that CPU
feature could just be opportunistic. Since many services are provided by
the distribution, or by upstream teams at a particular organisation,
it's not a sustainable solution to simply try to find and remove
offending directives from these units.
This commit presents a more direct solution -- a DisableControllers=
directive that forcibly disallows a controller from being enabled within
a subtree.
2018-12-03 15:38:06 +01:00
|
|
|
CGroupMask *mask = userdata;
|
|
|
|
CGroupController ctrl;
|
2017-11-09 15:29:34 +01:00
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(bus);
|
|
|
|
assert(reply);
|
|
|
|
|
|
|
|
r = sd_bus_message_open_container(reply, 'a', "s");
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
cgroup: Add DisableControllers= directive to disable controller in subtree
Some controllers (like the CPU controller) have a performance cost that
is non-trivial on certain workloads. While this can be mitigated and
improved to an extent, there will for some controllers always be some
overheads associated with the benefits gained from the controller.
Inside Facebook, the fix applied has been to disable the CPU controller
forcibly with `cgroup_disable=cpu` on the kernel command line.
This presents a problem: to disable or reenable the controller, a reboot
is required, but this is quite cumbersome and slow to do for many
thousands of machines, especially machines where disabling/enabling a
stateful service on a machine is a matter of several minutes.
Currently systemd provides some configuration knobs for these in the
form of `[Default]CPUAccounting`, `[Default]MemoryAccounting`, and the
like. The limitation of these is that Default*Accounting is overrideable
by individual services, of which any one could decide to reenable a
controller within the hierarchy at any point just by using a controller
feature implicitly (eg. `CPUWeight`), even if the use of that CPU
feature could just be opportunistic. Since many services are provided by
the distribution, or by upstream teams at a particular organisation,
it's not a sustainable solution to simply try to find and remove
offending directives from these units.
This commit presents a more direct solution -- a DisableControllers=
directive that forcibly disallows a controller from being enabled within
a subtree.
2018-12-03 15:38:06 +01:00
|
|
|
for (ctrl = 0; ctrl < _CGROUP_CONTROLLER_MAX; ctrl++) {
|
|
|
|
if ((*mask & CGROUP_CONTROLLER_TO_MASK(ctrl)) == 0)
|
2017-11-09 15:29:34 +01:00
|
|
|
continue;
|
|
|
|
|
cgroup: Add DisableControllers= directive to disable controller in subtree
Some controllers (like the CPU controller) have a performance cost that
is non-trivial on certain workloads. While this can be mitigated and
improved to an extent, there will for some controllers always be some
overheads associated with the benefits gained from the controller.
Inside Facebook, the fix applied has been to disable the CPU controller
forcibly with `cgroup_disable=cpu` on the kernel command line.
This presents a problem: to disable or reenable the controller, a reboot
is required, but this is quite cumbersome and slow to do for many
thousands of machines, especially machines where disabling/enabling a
stateful service on a machine is a matter of several minutes.
Currently systemd provides some configuration knobs for these in the
form of `[Default]CPUAccounting`, `[Default]MemoryAccounting`, and the
like. The limitation of these is that Default*Accounting is overrideable
by individual services, of which any one could decide to reenable a
controller within the hierarchy at any point just by using a controller
feature implicitly (eg. `CPUWeight`), even if the use of that CPU
feature could just be opportunistic. Since many services are provided by
the distribution, or by upstream teams at a particular organisation,
it's not a sustainable solution to simply try to find and remove
offending directives from these units.
This commit presents a more direct solution -- a DisableControllers=
directive that forcibly disallows a controller from being enabled within
a subtree.
2018-12-03 15:38:06 +01:00
|
|
|
r = sd_bus_message_append(reply, "s", cgroup_controller_to_string(ctrl));
|
2017-11-09 15:29:34 +01:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
return sd_bus_message_close_container(reply);
|
|
|
|
}
|
|
|
|
|
cgroup: Add DisableControllers= directive to disable controller in subtree
Some controllers (like the CPU controller) have a performance cost that
is non-trivial on certain workloads. While this can be mitigated and
improved to an extent, there will for some controllers always be some
overheads associated with the benefits gained from the controller.
Inside Facebook, the fix applied has been to disable the CPU controller
forcibly with `cgroup_disable=cpu` on the kernel command line.
This presents a problem: to disable or reenable the controller, a reboot
is required, but this is quite cumbersome and slow to do for many
thousands of machines, especially machines where disabling/enabling a
stateful service on a machine is a matter of several minutes.
Currently systemd provides some configuration knobs for these in the
form of `[Default]CPUAccounting`, `[Default]MemoryAccounting`, and the
like. The limitation of these is that Default*Accounting is overrideable
by individual services, of which any one could decide to reenable a
controller within the hierarchy at any point just by using a controller
feature implicitly (eg. `CPUWeight`), even if the use of that CPU
feature could just be opportunistic. Since many services are provided by
the distribution, or by upstream teams at a particular organisation,
it's not a sustainable solution to simply try to find and remove
offending directives from these units.
This commit presents a more direct solution -- a DisableControllers=
directive that forcibly disallows a controller from being enabled within
a subtree.
2018-12-03 15:38:06 +01:00
|
|
|
static int property_get_delegate_controllers(
|
|
|
|
sd_bus *bus,
|
|
|
|
const char *path,
|
|
|
|
const char *interface,
|
|
|
|
const char *property,
|
|
|
|
sd_bus_message *reply,
|
|
|
|
void *userdata,
|
|
|
|
sd_bus_error *error) {
|
|
|
|
|
|
|
|
CGroupContext *c = userdata;
|
|
|
|
|
|
|
|
assert(bus);
|
|
|
|
assert(reply);
|
|
|
|
assert(c);
|
|
|
|
|
|
|
|
if (!c->delegate)
|
|
|
|
return sd_bus_message_append(reply, "as", 0);
|
|
|
|
|
|
|
|
return property_get_cgroup_mask(bus, path, interface, property, reply, &c->delegate_controllers, error);
|
|
|
|
}
|
|
|
|
|
2019-07-29 17:50:05 +02:00
|
|
|
static int property_get_cpuset(
|
|
|
|
sd_bus *bus,
|
|
|
|
const char *path,
|
|
|
|
const char *interface,
|
|
|
|
const char *property,
|
|
|
|
sd_bus_message *reply,
|
|
|
|
void *userdata,
|
|
|
|
sd_bus_error *error) {
|
|
|
|
|
|
|
|
CPUSet *cpus = userdata;
|
|
|
|
_cleanup_free_ uint8_t *array = NULL;
|
|
|
|
size_t allocated;
|
|
|
|
|
|
|
|
assert(bus);
|
|
|
|
assert(reply);
|
|
|
|
assert(cpus);
|
|
|
|
|
|
|
|
(void) cpu_set_to_dbus(cpus, &array, &allocated);
|
|
|
|
return sd_bus_message_append_array(reply, 'y', array, allocated);
|
|
|
|
}
|
|
|
|
|
2016-05-05 22:42:55 +02:00
|
|
|
static int property_get_io_device_weight(
|
|
|
|
sd_bus *bus,
|
|
|
|
const char *path,
|
|
|
|
const char *interface,
|
|
|
|
const char *property,
|
|
|
|
sd_bus_message *reply,
|
|
|
|
void *userdata,
|
|
|
|
sd_bus_error *error) {
|
|
|
|
|
|
|
|
CGroupContext *c = userdata;
|
|
|
|
CGroupIODeviceWeight *w;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(bus);
|
|
|
|
assert(reply);
|
|
|
|
assert(c);
|
|
|
|
|
|
|
|
r = sd_bus_message_open_container(reply, 'a', "(st)");
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
LIST_FOREACH(device_weights, w, c->io_device_weights) {
|
|
|
|
r = sd_bus_message_append(reply, "(st)", w->path, w->weight);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
return sd_bus_message_close_container(reply);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int property_get_io_device_limits(
|
|
|
|
sd_bus *bus,
|
|
|
|
const char *path,
|
|
|
|
const char *interface,
|
|
|
|
const char *property,
|
|
|
|
sd_bus_message *reply,
|
|
|
|
void *userdata,
|
|
|
|
sd_bus_error *error) {
|
|
|
|
|
|
|
|
CGroupContext *c = userdata;
|
|
|
|
CGroupIODeviceLimit *l;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(bus);
|
|
|
|
assert(reply);
|
|
|
|
assert(c);
|
|
|
|
|
|
|
|
r = sd_bus_message_open_container(reply, 'a', "(st)");
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
LIST_FOREACH(device_limits, l, c->io_device_limits) {
|
2016-05-18 22:50:56 +02:00
|
|
|
CGroupIOLimitType type;
|
2016-05-05 22:42:55 +02:00
|
|
|
|
2016-05-18 22:50:56 +02:00
|
|
|
type = cgroup_io_limit_type_from_string(property);
|
|
|
|
if (type < 0 || l->limits[type] == cgroup_io_limit_defaults[type])
|
2016-05-05 22:42:55 +02:00
|
|
|
continue;
|
|
|
|
|
2016-05-18 22:50:56 +02:00
|
|
|
r = sd_bus_message_append(reply, "(st)", l->path, l->limits[type]);
|
2016-05-05 22:42:55 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
return sd_bus_message_close_container(reply);
|
|
|
|
}
|
|
|
|
|
2018-06-13 23:16:35 +02:00
|
|
|
static int property_get_io_device_latency(
|
|
|
|
sd_bus *bus,
|
|
|
|
const char *path,
|
|
|
|
const char *interface,
|
|
|
|
const char *property,
|
|
|
|
sd_bus_message *reply,
|
|
|
|
void *userdata,
|
|
|
|
sd_bus_error *error) {
|
|
|
|
|
|
|
|
CGroupContext *c = userdata;
|
|
|
|
CGroupIODeviceLatency *l;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(bus);
|
|
|
|
assert(reply);
|
|
|
|
assert(c);
|
|
|
|
|
|
|
|
r = sd_bus_message_open_container(reply, 'a', "(st)");
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
LIST_FOREACH(device_latencies, l, c->io_device_latencies) {
|
|
|
|
r = sd_bus_message_append(reply, "(st)", l->path, l->target_usec);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
return sd_bus_message_close_container(reply);
|
|
|
|
}
|
|
|
|
|
2013-11-19 21:12:59 +01:00
|
|
|
static int property_get_blockio_device_weight(
|
|
|
|
sd_bus *bus,
|
|
|
|
const char *path,
|
|
|
|
const char *interface,
|
|
|
|
const char *property,
|
|
|
|
sd_bus_message *reply,
|
2013-11-21 19:34:37 +01:00
|
|
|
void *userdata,
|
|
|
|
sd_bus_error *error) {
|
2013-06-27 04:14:27 +02:00
|
|
|
|
2013-11-19 21:12:59 +01:00
|
|
|
CGroupContext *c = userdata;
|
2013-06-27 04:14:27 +02:00
|
|
|
CGroupBlockIODeviceWeight *w;
|
2013-11-19 21:12:59 +01:00
|
|
|
int r;
|
2013-06-27 04:14:27 +02:00
|
|
|
|
2013-11-19 21:12:59 +01:00
|
|
|
assert(bus);
|
|
|
|
assert(reply);
|
2013-06-27 04:14:27 +02:00
|
|
|
assert(c);
|
|
|
|
|
2013-11-19 21:12:59 +01:00
|
|
|
r = sd_bus_message_open_container(reply, 'a', "(st)");
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2013-06-27 04:14:27 +02:00
|
|
|
|
|
|
|
LIST_FOREACH(device_weights, w, c->blockio_device_weights) {
|
2013-11-19 21:12:59 +01:00
|
|
|
r = sd_bus_message_append(reply, "(st)", w->path, w->weight);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2013-06-27 04:14:27 +02:00
|
|
|
}
|
|
|
|
|
2013-11-19 21:12:59 +01:00
|
|
|
return sd_bus_message_close_container(reply);
|
2013-06-27 04:14:27 +02:00
|
|
|
}
|
|
|
|
|
2013-11-19 21:12:59 +01:00
|
|
|
static int property_get_blockio_device_bandwidths(
|
|
|
|
sd_bus *bus,
|
|
|
|
const char *path,
|
|
|
|
const char *interface,
|
|
|
|
const char *property,
|
|
|
|
sd_bus_message *reply,
|
2013-11-21 19:34:37 +01:00
|
|
|
void *userdata,
|
|
|
|
sd_bus_error *error) {
|
2013-11-19 21:12:59 +01:00
|
|
|
|
|
|
|
CGroupContext *c = userdata;
|
2013-06-27 04:14:27 +02:00
|
|
|
CGroupBlockIODeviceBandwidth *b;
|
2013-11-19 21:12:59 +01:00
|
|
|
int r;
|
2013-06-27 04:14:27 +02:00
|
|
|
|
2013-11-19 21:12:59 +01:00
|
|
|
assert(bus);
|
|
|
|
assert(reply);
|
2013-06-27 04:14:27 +02:00
|
|
|
assert(c);
|
|
|
|
|
2013-11-19 21:12:59 +01:00
|
|
|
r = sd_bus_message_open_container(reply, 'a', "(st)");
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2013-06-27 04:14:27 +02:00
|
|
|
|
|
|
|
LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) {
|
2016-05-18 22:51:46 +02:00
|
|
|
uint64_t v;
|
2013-06-27 04:14:27 +02:00
|
|
|
|
2016-05-18 22:51:46 +02:00
|
|
|
if (streq(property, "BlockIOReadBandwidth"))
|
|
|
|
v = b->rbps;
|
|
|
|
else
|
|
|
|
v = b->wbps;
|
|
|
|
|
|
|
|
if (v == CGROUP_LIMIT_MAX)
|
2013-06-27 04:14:27 +02:00
|
|
|
continue;
|
|
|
|
|
2016-05-18 22:51:46 +02:00
|
|
|
r = sd_bus_message_append(reply, "(st)", b->path, v);
|
2013-11-19 21:12:59 +01:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2013-06-27 04:14:27 +02:00
|
|
|
}
|
|
|
|
|
2013-11-19 21:12:59 +01:00
|
|
|
return sd_bus_message_close_container(reply);
|
2013-06-27 04:14:27 +02:00
|
|
|
}
|
|
|
|
|
2013-11-19 21:12:59 +01:00
|
|
|
static int property_get_device_allow(
|
|
|
|
sd_bus *bus,
|
|
|
|
const char *path,
|
|
|
|
const char *interface,
|
|
|
|
const char *property,
|
|
|
|
sd_bus_message *reply,
|
2013-11-21 19:34:37 +01:00
|
|
|
void *userdata,
|
|
|
|
sd_bus_error *error) {
|
2013-11-19 21:12:59 +01:00
|
|
|
|
|
|
|
CGroupContext *c = userdata;
|
2013-06-27 04:14:27 +02:00
|
|
|
CGroupDeviceAllow *a;
|
2013-11-19 21:12:59 +01:00
|
|
|
int r;
|
2013-06-27 04:14:27 +02:00
|
|
|
|
2013-11-19 21:12:59 +01:00
|
|
|
assert(bus);
|
|
|
|
assert(reply);
|
2013-06-27 04:14:27 +02:00
|
|
|
assert(c);
|
|
|
|
|
2013-11-19 21:12:59 +01:00
|
|
|
r = sd_bus_message_open_container(reply, 'a', "(ss)");
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2013-06-27 04:14:27 +02:00
|
|
|
|
|
|
|
LIST_FOREACH(device_allow, a, c->device_allow) {
|
|
|
|
unsigned k = 0;
|
2013-11-19 21:12:59 +01:00
|
|
|
char rwm[4];
|
2013-06-27 04:14:27 +02:00
|
|
|
|
|
|
|
if (a->r)
|
2013-11-19 21:12:59 +01:00
|
|
|
rwm[k++] = 'r';
|
2013-06-27 04:14:27 +02:00
|
|
|
if (a->w)
|
2013-11-19 21:12:59 +01:00
|
|
|
rwm[k++] = 'w';
|
2013-06-27 04:14:27 +02:00
|
|
|
if (a->m)
|
2013-11-19 21:12:59 +01:00
|
|
|
rwm[k++] = 'm';
|
2013-06-27 04:14:27 +02:00
|
|
|
|
2013-11-19 21:12:59 +01:00
|
|
|
rwm[k] = 0;
|
2013-06-27 04:14:27 +02:00
|
|
|
|
2013-11-19 21:12:59 +01:00
|
|
|
r = sd_bus_message_append(reply, "(ss)", a->path, rwm);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2013-06-27 04:14:27 +02:00
|
|
|
}
|
|
|
|
|
2013-11-19 21:12:59 +01:00
|
|
|
return sd_bus_message_close_container(reply);
|
2013-06-27 04:14:27 +02:00
|
|
|
}
|
|
|
|
|
2017-09-05 11:16:35 +02:00
|
|
|
static int property_get_ip_address_access(
|
|
|
|
sd_bus *bus,
|
|
|
|
const char *path,
|
|
|
|
const char *interface,
|
|
|
|
const char *property,
|
|
|
|
sd_bus_message *reply,
|
|
|
|
void *userdata,
|
|
|
|
sd_bus_error *error) {
|
|
|
|
|
|
|
|
IPAddressAccessItem** items = userdata, *i;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
r = sd_bus_message_open_container(reply, 'a', "(iayu)");
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
LIST_FOREACH(items, i, *items) {
|
|
|
|
|
|
|
|
r = sd_bus_message_open_container(reply, 'r', "iayu");
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = sd_bus_message_append(reply, "i", i->family);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = sd_bus_message_append_array(reply, 'y', &i->address, FAMILY_ADDRESS_SIZE(i->family));
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = sd_bus_message_append(reply, "u", (uint32_t) i->prefixlen);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = sd_bus_message_close_container(reply);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
return sd_bus_message_close_container(reply);
|
|
|
|
}
|
|
|
|
|
2013-11-19 21:12:59 +01:00
|
|
|
const sd_bus_vtable bus_cgroup_vtable[] = {
|
|
|
|
SD_BUS_VTABLE_START(0),
|
2014-11-05 17:57:23 +01:00
|
|
|
SD_BUS_PROPERTY("Delegate", "b", bus_property_get_bool, offsetof(CGroupContext, delegate), 0),
|
2017-11-09 15:29:34 +01:00
|
|
|
SD_BUS_PROPERTY("DelegateControllers", "as", property_get_delegate_controllers, 0, 0),
|
2013-12-22 19:44:04 +01:00
|
|
|
SD_BUS_PROPERTY("CPUAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, cpu_accounting), 0),
|
2016-08-07 15:45:39 +02:00
|
|
|
SD_BUS_PROPERTY("CPUWeight", "t", NULL, offsetof(CGroupContext, cpu_weight), 0),
|
|
|
|
SD_BUS_PROPERTY("StartupCPUWeight", "t", NULL, offsetof(CGroupContext, startup_cpu_weight), 0),
|
2015-09-11 16:48:24 +02:00
|
|
|
SD_BUS_PROPERTY("CPUShares", "t", NULL, offsetof(CGroupContext, cpu_shares), 0),
|
|
|
|
SD_BUS_PROPERTY("StartupCPUShares", "t", NULL, offsetof(CGroupContext, startup_cpu_shares), 0),
|
2014-09-29 05:42:33 +02:00
|
|
|
SD_BUS_PROPERTY("CPUQuotaPerSecUSec", "t", bus_property_get_usec, offsetof(CGroupContext, cpu_quota_per_sec_usec), 0),
|
2018-11-02 17:21:57 +01:00
|
|
|
SD_BUS_PROPERTY("CPUQuotaPeriodUSec", "t", bus_property_get_usec, offsetof(CGroupContext, cpu_quota_period_usec), 0),
|
2019-07-29 17:50:05 +02:00
|
|
|
SD_BUS_PROPERTY("AllowedCPUs", "ay", property_get_cpuset, offsetof(CGroupContext, cpuset_cpus), 0),
|
|
|
|
SD_BUS_PROPERTY("AllowedMemoryNodes", "ay", property_get_cpuset, offsetof(CGroupContext, cpuset_mems), 0),
|
2016-05-05 22:42:55 +02:00
|
|
|
SD_BUS_PROPERTY("IOAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, io_accounting), 0),
|
|
|
|
SD_BUS_PROPERTY("IOWeight", "t", NULL, offsetof(CGroupContext, io_weight), 0),
|
|
|
|
SD_BUS_PROPERTY("StartupIOWeight", "t", NULL, offsetof(CGroupContext, startup_io_weight), 0),
|
|
|
|
SD_BUS_PROPERTY("IODeviceWeight", "a(st)", property_get_io_device_weight, 0, 0),
|
|
|
|
SD_BUS_PROPERTY("IOReadBandwidthMax", "a(st)", property_get_io_device_limits, 0, 0),
|
|
|
|
SD_BUS_PROPERTY("IOWriteBandwidthMax", "a(st)", property_get_io_device_limits, 0, 0),
|
2016-05-18 22:50:56 +02:00
|
|
|
SD_BUS_PROPERTY("IOReadIOPSMax", "a(st)", property_get_io_device_limits, 0, 0),
|
|
|
|
SD_BUS_PROPERTY("IOWriteIOPSMax", "a(st)", property_get_io_device_limits, 0, 0),
|
2018-06-13 23:16:35 +02:00
|
|
|
SD_BUS_PROPERTY("IODeviceLatencyTargetUSec", "a(st)", property_get_io_device_latency, 0, 0),
|
2013-12-22 19:44:04 +01:00
|
|
|
SD_BUS_PROPERTY("BlockIOAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, blockio_accounting), 0),
|
2015-09-11 16:48:24 +02:00
|
|
|
SD_BUS_PROPERTY("BlockIOWeight", "t", NULL, offsetof(CGroupContext, blockio_weight), 0),
|
|
|
|
SD_BUS_PROPERTY("StartupBlockIOWeight", "t", NULL, offsetof(CGroupContext, startup_blockio_weight), 0),
|
2013-12-22 19:44:04 +01:00
|
|
|
SD_BUS_PROPERTY("BlockIODeviceWeight", "a(st)", property_get_blockio_device_weight, 0, 0),
|
|
|
|
SD_BUS_PROPERTY("BlockIOReadBandwidth", "a(st)", property_get_blockio_device_bandwidths, 0, 0),
|
|
|
|
SD_BUS_PROPERTY("BlockIOWriteBandwidth", "a(st)", property_get_blockio_device_bandwidths, 0, 0),
|
|
|
|
SD_BUS_PROPERTY("MemoryAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, memory_accounting), 0),
|
cgroup: Implement default propagation of MemoryLow with DefaultMemoryLow
In cgroup v2 we have protection tunables -- currently MemoryLow and
MemoryMin (there will be more in future for other resources, too). The
design of these protection tunables requires not only intermediate
cgroups to propagate protections, but also the units at the leaf of that
resource's operation to accept it (by setting MemoryLow or MemoryMin).
This makes sense from an low-level API design perspective, but it's a
good idea to also have a higher-level abstraction that can, by default,
propagate these resources to children recursively. In this patch, this
happens by having descendants set memory.low to N if their ancestor has
DefaultMemoryLow=N -- assuming they don't set a separate MemoryLow
value.
Any affected unit can opt out of this propagation by manually setting
`MemoryLow` to some value in its unit configuration. A unit can also
stop further propagation by setting `DefaultMemoryLow=` with no
argument. This removes further propagation in the subtree, but has no
effect on the unit itself (for that, use `MemoryLow=0`).
Our use case in production is simplifying the configuration of machines
which heavily rely on memory protection tunables, but currently require
tweaking a huge number of unit files to make that a reality. This
directive makes that significantly less fragile, and decreases the risk
of misconfiguration.
After this patch is merged, I will implement DefaultMemoryMin= using the
same principles.
2019-03-28 13:50:50 +01:00
|
|
|
SD_BUS_PROPERTY("DefaultMemoryLow", "t", NULL, offsetof(CGroupContext, default_memory_low), 0),
|
2019-05-03 14:19:05 +02:00
|
|
|
SD_BUS_PROPERTY("DefaultMemoryMin", "t", NULL, offsetof(CGroupContext, default_memory_min), 0),
|
2018-06-09 02:33:14 +02:00
|
|
|
SD_BUS_PROPERTY("MemoryMin", "t", NULL, offsetof(CGroupContext, memory_min), 0),
|
2016-05-27 18:10:18 +02:00
|
|
|
SD_BUS_PROPERTY("MemoryLow", "t", NULL, offsetof(CGroupContext, memory_low), 0),
|
|
|
|
SD_BUS_PROPERTY("MemoryHigh", "t", NULL, offsetof(CGroupContext, memory_high), 0),
|
|
|
|
SD_BUS_PROPERTY("MemoryMax", "t", NULL, offsetof(CGroupContext, memory_max), 0),
|
2016-07-04 09:03:54 +02:00
|
|
|
SD_BUS_PROPERTY("MemorySwapMax", "t", NULL, offsetof(CGroupContext, memory_swap_max), 0),
|
2013-12-22 19:44:04 +01:00
|
|
|
SD_BUS_PROPERTY("MemoryLimit", "t", NULL, offsetof(CGroupContext, memory_limit), 0),
|
|
|
|
SD_BUS_PROPERTY("DevicePolicy", "s", property_get_cgroup_device_policy, offsetof(CGroupContext, device_policy), 0),
|
|
|
|
SD_BUS_PROPERTY("DeviceAllow", "a(ss)", property_get_device_allow, 0, 0),
|
2015-09-10 12:32:16 +02:00
|
|
|
SD_BUS_PROPERTY("TasksAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, tasks_accounting), 0),
|
2019-11-05 13:50:28 +01:00
|
|
|
SD_BUS_PROPERTY("TasksMax", "t", bus_property_get_tasks_max, offsetof(CGroupContext, tasks_max), 0),
|
2017-09-05 11:16:35 +02:00
|
|
|
SD_BUS_PROPERTY("IPAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, ip_accounting), 0),
|
|
|
|
SD_BUS_PROPERTY("IPAddressAllow", "a(iayu)", property_get_ip_address_access, offsetof(CGroupContext, ip_address_allow), 0),
|
|
|
|
SD_BUS_PROPERTY("IPAddressDeny", "a(iayu)", property_get_ip_address_access, offsetof(CGroupContext, ip_address_deny), 0),
|
2019-04-23 12:14:20 +02:00
|
|
|
SD_BUS_PROPERTY("IPIngressFilterPath", "as", NULL, offsetof(CGroupContext, ip_filters_ingress), 0),
|
|
|
|
SD_BUS_PROPERTY("IPEgressFilterPath", "as", NULL, offsetof(CGroupContext, ip_filters_egress), 0),
|
cgroup: Add DisableControllers= directive to disable controller in subtree
Some controllers (like the CPU controller) have a performance cost that
is non-trivial on certain workloads. While this can be mitigated and
improved to an extent, there will for some controllers always be some
overheads associated with the benefits gained from the controller.
Inside Facebook, the fix applied has been to disable the CPU controller
forcibly with `cgroup_disable=cpu` on the kernel command line.
This presents a problem: to disable or reenable the controller, a reboot
is required, but this is quite cumbersome and slow to do for many
thousands of machines, especially machines where disabling/enabling a
stateful service on a machine is a matter of several minutes.
Currently systemd provides some configuration knobs for these in the
form of `[Default]CPUAccounting`, `[Default]MemoryAccounting`, and the
like. The limitation of these is that Default*Accounting is overrideable
by individual services, of which any one could decide to reenable a
controller within the hierarchy at any point just by using a controller
feature implicitly (eg. `CPUWeight`), even if the use of that CPU
feature could just be opportunistic. Since many services are provided by
the distribution, or by upstream teams at a particular organisation,
it's not a sustainable solution to simply try to find and remove
offending directives from these units.
This commit presents a more direct solution -- a DisableControllers=
directive that forcibly disallows a controller from being enabled within
a subtree.
2018-12-03 15:38:06 +01:00
|
|
|
SD_BUS_PROPERTY("DisableControllers", "as", property_get_cgroup_mask, offsetof(CGroupContext, disable_controllers), 0),
|
2013-11-19 21:12:59 +01:00
|
|
|
SD_BUS_VTABLE_END
|
2013-06-27 04:14:27 +02:00
|
|
|
};
|
2013-06-27 21:14:56 +02:00
|
|
|
|
2014-11-05 17:57:23 +01:00
|
|
|
static int bus_cgroup_set_transient_property(
|
|
|
|
Unit *u,
|
|
|
|
CGroupContext *c,
|
|
|
|
const char *name,
|
|
|
|
sd_bus_message *message,
|
2017-11-22 15:03:51 +01:00
|
|
|
UnitWriteFlags flags,
|
2014-11-05 17:57:23 +01:00
|
|
|
sd_bus_error *error) {
|
|
|
|
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(u);
|
|
|
|
assert(c);
|
|
|
|
assert(name);
|
|
|
|
assert(message);
|
|
|
|
|
2017-11-22 15:03:51 +01:00
|
|
|
flags |= UNIT_PRIVATE;
|
|
|
|
|
2014-11-05 17:57:23 +01:00
|
|
|
if (streq(name, "Delegate")) {
|
|
|
|
int b;
|
|
|
|
|
2018-02-06 11:57:35 +01:00
|
|
|
if (!UNIT_VTABLE(u)->can_delegate)
|
|
|
|
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Delegation not available for unit type");
|
|
|
|
|
2014-11-05 17:57:23 +01:00
|
|
|
r = sd_bus_message_read(message, "b", &b);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2017-11-22 15:03:51 +01:00
|
|
|
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
|
2014-11-05 17:57:23 +01:00
|
|
|
c->delegate = b;
|
2017-11-09 15:29:34 +01:00
|
|
|
c->delegate_controllers = b ? _CGROUP_MASK_ALL : 0;
|
|
|
|
|
2017-11-22 15:03:51 +01:00
|
|
|
unit_write_settingf(u, flags, name, "Delegate=%s", yes_no(b));
|
2014-11-05 17:57:23 +01:00
|
|
|
}
|
|
|
|
|
2017-11-09 15:29:34 +01:00
|
|
|
return 1;
|
|
|
|
|
2019-04-17 09:42:55 +02:00
|
|
|
} else if (STR_IN_SET(name, "DelegateControllers", "DisableControllers")) {
|
2017-11-09 15:29:34 +01:00
|
|
|
CGroupMask mask = 0;
|
|
|
|
|
2019-04-17 09:42:55 +02:00
|
|
|
if (streq(name, "DelegateControllers") && !UNIT_VTABLE(u)->can_delegate)
|
2018-02-06 11:57:35 +01:00
|
|
|
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Delegation not available for unit type");
|
|
|
|
|
2017-11-09 15:29:34 +01:00
|
|
|
r = sd_bus_message_enter_container(message, 'a', "s");
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
CGroupController cc;
|
|
|
|
const char *t;
|
|
|
|
|
|
|
|
r = sd_bus_message_read(message, "s", &t);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
if (r == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
cc = cgroup_controller_from_string(t);
|
|
|
|
if (cc < 0)
|
2018-04-14 11:06:11 +02:00
|
|
|
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown cgroup controller '%s'", t);
|
2017-11-09 15:29:34 +01:00
|
|
|
|
|
|
|
mask |= CGROUP_CONTROLLER_TO_MASK(cc);
|
|
|
|
}
|
|
|
|
|
|
|
|
r = sd_bus_message_exit_container(message);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2017-11-22 15:03:51 +01:00
|
|
|
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
|
2017-11-09 15:29:34 +01:00
|
|
|
_cleanup_free_ char *t = NULL;
|
|
|
|
|
|
|
|
r = cg_mask_to_string(mask, &t);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2019-04-17 09:42:55 +02:00
|
|
|
if (streq(name, "DelegateControllers")) {
|
|
|
|
|
|
|
|
c->delegate = true;
|
|
|
|
if (mask == 0)
|
|
|
|
c->delegate_controllers = 0;
|
|
|
|
else
|
|
|
|
c->delegate_controllers |= mask;
|
|
|
|
|
|
|
|
unit_write_settingf(u, flags, name, "Delegate=%s", strempty(t));
|
2017-11-09 15:29:34 +01:00
|
|
|
|
2019-04-17 09:42:55 +02:00
|
|
|
} else if (streq(name, "DisableControllers")) {
|
|
|
|
|
|
|
|
if (mask == 0)
|
|
|
|
c->disable_controllers = 0;
|
|
|
|
else
|
|
|
|
c->disable_controllers |= mask;
|
|
|
|
|
|
|
|
unit_write_settingf(u, flags, name, "%s=%s", name, strempty(t));
|
|
|
|
}
|
2017-11-09 15:29:34 +01:00
|
|
|
}
|
|
|
|
|
2019-04-23 12:14:20 +02:00
|
|
|
return 1;
|
|
|
|
} else if (STR_IN_SET(name, "IPIngressFilterPath", "IPEgressFilterPath")) {
|
|
|
|
char ***filters;
|
|
|
|
size_t n = 0;
|
|
|
|
|
|
|
|
filters = streq(name, "IPIngressFilterPath") ? &c->ip_filters_ingress : &c->ip_filters_egress;
|
|
|
|
r = sd_bus_message_enter_container(message, 'a', "s");
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
const char *path;
|
|
|
|
|
|
|
|
r = sd_bus_message_read(message, "s", &path);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
if (r == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (!path_is_normalized(path) || !path_is_absolute(path))
|
|
|
|
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "%s= expects a normalized absolute path.", name);
|
|
|
|
|
|
|
|
if (!UNIT_WRITE_FLAGS_NOOP(flags) && !strv_contains(*filters, path)) {
|
|
|
|
r = strv_extend(filters, path);
|
|
|
|
if (r < 0)
|
|
|
|
return log_oom();
|
|
|
|
}
|
|
|
|
n++;
|
|
|
|
}
|
|
|
|
r = sd_bus_message_exit_container(message);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
|
|
|
|
_cleanup_free_ char *buf = NULL;
|
|
|
|
_cleanup_fclose_ FILE *f = NULL;
|
|
|
|
char **entry;
|
|
|
|
size_t size = 0;
|
|
|
|
|
|
|
|
if (n == 0)
|
|
|
|
*filters = strv_free(*filters);
|
|
|
|
|
|
|
|
unit_invalidate_cgroup_bpf(u);
|
|
|
|
f = open_memstream_unlocked(&buf, &size);
|
|
|
|
if (!f)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
fputs(name, f);
|
|
|
|
fputs("=\n", f);
|
|
|
|
|
|
|
|
STRV_FOREACH(entry, *filters)
|
|
|
|
fprintf(f, "%s=%s\n", name, *entry);
|
|
|
|
|
|
|
|
r = fflush_and_check(f);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
unit_write_setting(u, flags, name, buf);
|
|
|
|
|
|
|
|
if (*filters) {
|
|
|
|
r = bpf_firewall_supported();
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
if (r != BPF_FIREWALL_SUPPORTED_WITH_MULTI) {
|
|
|
|
static bool warned = false;
|
|
|
|
|
|
|
|
log_full(warned ? LOG_DEBUG : LOG_WARNING,
|
2019-10-03 15:11:41 +02:00
|
|
|
"Transient unit %s configures an IP firewall with BPF, but the local system does not support BPF/cgroup firewalling with multiple filters.\n"
|
2019-04-23 12:14:20 +02:00
|
|
|
"Starting this unit will fail! (This warning is only shown for the first started transient unit using IP firewalling.)", u->id);
|
|
|
|
warned = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-05 17:57:23 +01:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-01-02 18:30:16 +01:00
|
|
|
static int bus_cgroup_set_boolean(
|
|
|
|
Unit *u,
|
|
|
|
const char *name,
|
|
|
|
bool *p,
|
|
|
|
CGroupMask mask,
|
|
|
|
sd_bus_message *message,
|
|
|
|
UnitWriteFlags flags,
|
|
|
|
sd_bus_error *error) {
|
|
|
|
|
|
|
|
int b, r;
|
|
|
|
|
|
|
|
assert(p);
|
|
|
|
|
|
|
|
r = sd_bus_message_read(message, "b", &b);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
|
|
|
|
*p = b;
|
|
|
|
unit_invalidate_cgroup(u, mask);
|
|
|
|
unit_write_settingf(u, flags, name, "%s=%s", name, yes_no(b));
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2018-03-09 11:34:50 +01:00
|
|
|
#define BUS_DEFINE_SET_CGROUP_WEIGHT(function, mask, check, val) \
|
|
|
|
static int bus_cgroup_set_##function( \
|
|
|
|
Unit *u, \
|
|
|
|
const char *name, \
|
|
|
|
uint64_t *p, \
|
|
|
|
sd_bus_message *message, \
|
|
|
|
UnitWriteFlags flags, \
|
|
|
|
sd_bus_error *error) { \
|
|
|
|
\
|
|
|
|
uint64_t v; \
|
|
|
|
int r; \
|
|
|
|
\
|
|
|
|
assert(p); \
|
|
|
|
\
|
|
|
|
r = sd_bus_message_read(message, "t", &v); \
|
|
|
|
if (r < 0) \
|
|
|
|
return r; \
|
|
|
|
\
|
|
|
|
if (!check(v)) \
|
|
|
|
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, \
|
|
|
|
"Value specified in %s is out of range", name); \
|
|
|
|
\
|
|
|
|
if (!UNIT_WRITE_FLAGS_NOOP(flags)) { \
|
|
|
|
*p = v; \
|
2019-11-05 18:08:15 +01:00
|
|
|
unit_invalidate_cgroup(u, mask); \
|
2018-03-09 11:34:50 +01:00
|
|
|
\
|
|
|
|
if (v == (val)) \
|
|
|
|
unit_write_settingf(u, flags, name, \
|
|
|
|
"%s=", name); \
|
|
|
|
else \
|
|
|
|
unit_write_settingf(u, flags, name, \
|
|
|
|
"%s=%" PRIu64, name, v); \
|
|
|
|
} \
|
|
|
|
\
|
|
|
|
return 1; \
|
|
|
|
}
|
|
|
|
|
|
|
|
#define BUS_DEFINE_SET_CGROUP_LIMIT(function, mask, scale, minimum) \
|
|
|
|
static int bus_cgroup_set_##function( \
|
|
|
|
Unit *u, \
|
|
|
|
const char *name, \
|
|
|
|
uint64_t *p, \
|
|
|
|
sd_bus_message *message, \
|
|
|
|
UnitWriteFlags flags, \
|
|
|
|
sd_bus_error *error) { \
|
|
|
|
\
|
|
|
|
uint64_t v; \
|
|
|
|
int r; \
|
|
|
|
\
|
|
|
|
assert(p); \
|
|
|
|
\
|
|
|
|
r = sd_bus_message_read(message, "t", &v); \
|
|
|
|
if (r < 0) \
|
|
|
|
return r; \
|
|
|
|
\
|
|
|
|
if (v < minimum) \
|
|
|
|
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, \
|
|
|
|
"Value specified in %s is out of range", name); \
|
|
|
|
\
|
|
|
|
if (!UNIT_WRITE_FLAGS_NOOP(flags)) { \
|
|
|
|
*p = v; \
|
2019-11-05 18:08:15 +01:00
|
|
|
unit_invalidate_cgroup(u, mask); \
|
2018-03-09 11:34:50 +01:00
|
|
|
\
|
|
|
|
if (v == CGROUP_LIMIT_MAX) \
|
|
|
|
unit_write_settingf(u, flags, name, \
|
|
|
|
"%s=infinity", name); \
|
|
|
|
else \
|
|
|
|
unit_write_settingf(u, flags, name, \
|
|
|
|
"%s=%" PRIu64, name, v); \
|
|
|
|
} \
|
|
|
|
\
|
|
|
|
return 1; \
|
|
|
|
} \
|
|
|
|
static int bus_cgroup_set_##function##_scale( \
|
|
|
|
Unit *u, \
|
|
|
|
const char *name, \
|
|
|
|
uint64_t *p, \
|
|
|
|
sd_bus_message *message, \
|
|
|
|
UnitWriteFlags flags, \
|
|
|
|
sd_bus_error *error) { \
|
|
|
|
\
|
|
|
|
uint64_t v; \
|
|
|
|
uint32_t raw; \
|
|
|
|
int r; \
|
|
|
|
\
|
|
|
|
assert(p); \
|
|
|
|
\
|
|
|
|
r = sd_bus_message_read(message, "u", &raw); \
|
|
|
|
if (r < 0) \
|
|
|
|
return r; \
|
|
|
|
\
|
|
|
|
v = scale(raw, UINT32_MAX); \
|
|
|
|
if (v < minimum || v >= UINT64_MAX) \
|
|
|
|
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, \
|
|
|
|
"Value specified in %s is out of range", name); \
|
|
|
|
\
|
|
|
|
if (!UNIT_WRITE_FLAGS_NOOP(flags)) { \
|
|
|
|
*p = v; \
|
2019-11-05 18:08:15 +01:00
|
|
|
unit_invalidate_cgroup(u, mask); \
|
2018-03-09 11:34:50 +01:00
|
|
|
\
|
2019-11-05 18:15:46 +01:00
|
|
|
/* Prepare to chop off suffix */ \
|
|
|
|
assert_se(endswith(name, "Scale")); \
|
2018-03-09 11:34:50 +01:00
|
|
|
\
|
2019-11-05 20:18:11 +01:00
|
|
|
uint32_t scaled = DIV_ROUND_UP((uint64_t) raw * 1000, (uint64_t) UINT32_MAX); \
|
|
|
|
unit_write_settingf(u, flags, name, "%.*s=%" PRIu32 ".%" PRIu32 "%%", \
|
2019-11-05 18:15:46 +01:00
|
|
|
(int)(strlen(name) - strlen("Scale")), name, \
|
2019-11-05 20:18:11 +01:00
|
|
|
scaled / 10, scaled % 10); \
|
2018-03-09 11:34:50 +01:00
|
|
|
} \
|
|
|
|
\
|
|
|
|
return 1; \
|
|
|
|
}
|
|
|
|
|
2020-05-25 18:20:52 +02:00
|
|
|
DISABLE_WARNING_TYPE_LIMITS;
|
2018-03-09 11:34:50 +01:00
|
|
|
BUS_DEFINE_SET_CGROUP_WEIGHT(cpu_weight, CGROUP_MASK_CPU, CGROUP_WEIGHT_IS_OK, CGROUP_WEIGHT_INVALID);
|
|
|
|
BUS_DEFINE_SET_CGROUP_WEIGHT(cpu_shares, CGROUP_MASK_CPU, CGROUP_CPU_SHARES_IS_OK, CGROUP_CPU_SHARES_INVALID);
|
|
|
|
BUS_DEFINE_SET_CGROUP_WEIGHT(io_weight, CGROUP_MASK_IO, CGROUP_WEIGHT_IS_OK, CGROUP_WEIGHT_INVALID);
|
|
|
|
BUS_DEFINE_SET_CGROUP_WEIGHT(blockio_weight, CGROUP_MASK_BLKIO, CGROUP_BLKIO_WEIGHT_IS_OK, CGROUP_BLKIO_WEIGHT_INVALID);
|
|
|
|
BUS_DEFINE_SET_CGROUP_LIMIT(memory, CGROUP_MASK_MEMORY, physical_memory_scale, 1);
|
2020-06-23 11:35:39 +02:00
|
|
|
BUS_DEFINE_SET_CGROUP_LIMIT(memory_protection, CGROUP_MASK_MEMORY, physical_memory_scale, 0);
|
2018-03-09 11:34:50 +01:00
|
|
|
BUS_DEFINE_SET_CGROUP_LIMIT(swap, CGROUP_MASK_MEMORY, physical_memory_scale, 0);
|
2020-05-25 18:20:52 +02:00
|
|
|
REENABLE_WARNING;
|
2018-01-02 18:30:16 +01:00
|
|
|
|
2019-11-05 13:50:28 +01:00
|
|
|
static int bus_cgroup_set_tasks_max(
|
|
|
|
Unit *u,
|
|
|
|
const char *name,
|
|
|
|
TasksMax *p,
|
|
|
|
sd_bus_message *message,
|
|
|
|
UnitWriteFlags flags,
|
|
|
|
sd_bus_error *error) {
|
|
|
|
|
|
|
|
uint64_t v;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(p);
|
|
|
|
|
|
|
|
r = sd_bus_message_read(message, "t", &v);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
if (v < 1)
|
|
|
|
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
|
|
|
|
"Value specified in %s is out of range", name);
|
|
|
|
|
|
|
|
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
|
|
|
|
*p = (TasksMax) { .value = v, .scale = 0 }; /* When .scale==0, .value is the absolute value */
|
|
|
|
unit_invalidate_cgroup(u, CGROUP_MASK_PIDS);
|
|
|
|
|
|
|
|
if (v == CGROUP_LIMIT_MAX)
|
|
|
|
unit_write_settingf(u, flags, name,
|
|
|
|
"%s=infinity", name);
|
|
|
|
else
|
|
|
|
unit_write_settingf(u, flags, name,
|
|
|
|
"%s=%" PRIu64, name, v);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int bus_cgroup_set_tasks_max_scale(
|
|
|
|
Unit *u,
|
|
|
|
const char *name,
|
|
|
|
TasksMax *p,
|
|
|
|
sd_bus_message *message,
|
|
|
|
UnitWriteFlags flags,
|
|
|
|
sd_bus_error *error) {
|
|
|
|
|
|
|
|
uint32_t v;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(p);
|
|
|
|
|
|
|
|
r = sd_bus_message_read(message, "u", &v);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
if (v < 1 || v >= UINT32_MAX)
|
|
|
|
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
|
|
|
|
"Value specified in %s is out of range", name);
|
|
|
|
|
|
|
|
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
|
|
|
|
*p = (TasksMax) { v, UINT32_MAX }; /* .scale is not 0, so this is interpreted as v/UINT32_MAX. */
|
|
|
|
unit_invalidate_cgroup(u, CGROUP_MASK_PIDS);
|
|
|
|
|
2019-11-05 20:18:11 +01:00
|
|
|
uint32_t scaled = DIV_ROUND_UP((uint64_t) v * 100U, (uint64_t) UINT32_MAX);
|
|
|
|
unit_write_settingf(u, flags, name, "%s=%" PRIu32 ".%" PRIu32 "%%", "TasksMax",
|
|
|
|
scaled / 10, scaled % 10);
|
2019-11-05 13:50:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2013-06-27 21:14:56 +02:00
|
|
|
int bus_cgroup_set_property(
|
|
|
|
Unit *u,
|
|
|
|
CGroupContext *c,
|
|
|
|
const char *name,
|
2013-11-19 21:12:59 +01:00
|
|
|
sd_bus_message *message,
|
2017-11-22 15:03:51 +01:00
|
|
|
UnitWriteFlags flags,
|
2013-11-19 21:12:59 +01:00
|
|
|
sd_bus_error *error) {
|
|
|
|
|
2016-05-18 22:50:56 +02:00
|
|
|
CGroupIOLimitType iol_type;
|
2013-11-19 21:12:59 +01:00
|
|
|
int r;
|
2013-06-27 21:14:56 +02:00
|
|
|
|
|
|
|
assert(u);
|
|
|
|
assert(c);
|
2013-11-19 21:12:59 +01:00
|
|
|
assert(name);
|
|
|
|
assert(message);
|
2013-06-27 21:14:56 +02:00
|
|
|
|
2017-11-22 15:03:51 +01:00
|
|
|
flags |= UNIT_PRIVATE;
|
|
|
|
|
2018-01-02 18:30:16 +01:00
|
|
|
if (streq(name, "CPUAccounting"))
|
2018-11-17 12:19:07 +01:00
|
|
|
return bus_cgroup_set_boolean(u, name, &c->cpu_accounting, get_cpu_accounting_mask(), message, flags, error);
|
2013-06-27 21:14:56 +02:00
|
|
|
|
2018-01-02 18:30:16 +01:00
|
|
|
if (streq(name, "CPUWeight"))
|
|
|
|
return bus_cgroup_set_cpu_weight(u, name, &c->cpu_weight, message, flags, error);
|
2013-06-27 21:14:56 +02:00
|
|
|
|
2018-01-02 18:30:16 +01:00
|
|
|
if (streq(name, "StartupCPUWeight"))
|
|
|
|
return bus_cgroup_set_cpu_weight(u, name, &c->startup_cpu_weight, message, flags, error);
|
2013-06-27 21:50:35 +02:00
|
|
|
|
2018-01-02 18:30:16 +01:00
|
|
|
if (streq(name, "CPUShares"))
|
|
|
|
return bus_cgroup_set_cpu_shares(u, name, &c->cpu_shares, message, flags, error);
|
2013-06-27 21:50:35 +02:00
|
|
|
|
2018-01-02 18:30:16 +01:00
|
|
|
if (streq(name, "StartupCPUShares"))
|
|
|
|
return bus_cgroup_set_cpu_shares(u, name, &c->startup_cpu_shares, message, flags, error);
|
2016-08-07 15:45:39 +02:00
|
|
|
|
2018-01-02 18:30:16 +01:00
|
|
|
if (streq(name, "IOAccounting"))
|
|
|
|
return bus_cgroup_set_boolean(u, name, &c->io_accounting, CGROUP_MASK_IO, message, flags, error);
|
2016-08-07 15:45:39 +02:00
|
|
|
|
2018-01-02 18:30:16 +01:00
|
|
|
if (streq(name, "IOWeight"))
|
|
|
|
return bus_cgroup_set_io_weight(u, name, &c->io_weight, message, flags, error);
|
2016-08-07 15:45:39 +02:00
|
|
|
|
2018-01-02 18:30:16 +01:00
|
|
|
if (streq(name, "StartupIOWeight"))
|
|
|
|
return bus_cgroup_set_io_weight(u, name, &c->startup_io_weight, message, flags, error);
|
2016-08-07 15:45:39 +02:00
|
|
|
|
2018-01-02 18:30:16 +01:00
|
|
|
if (streq(name, "BlockIOAccounting"))
|
|
|
|
return bus_cgroup_set_boolean(u, name, &c->blockio_accounting, CGROUP_MASK_BLKIO, message, flags, error);
|
2016-08-07 15:45:39 +02:00
|
|
|
|
2018-01-02 18:30:16 +01:00
|
|
|
if (streq(name, "BlockIOWeight"))
|
|
|
|
return bus_cgroup_set_blockio_weight(u, name, &c->blockio_weight, message, flags, error);
|
2016-08-07 15:45:39 +02:00
|
|
|
|
2018-01-02 18:30:16 +01:00
|
|
|
if (streq(name, "StartupBlockIOWeight"))
|
|
|
|
return bus_cgroup_set_blockio_weight(u, name, &c->startup_blockio_weight, message, flags, error);
|
2016-08-07 15:45:39 +02:00
|
|
|
|
2018-01-02 18:30:16 +01:00
|
|
|
if (streq(name, "MemoryAccounting"))
|
|
|
|
return bus_cgroup_set_boolean(u, name, &c->memory_accounting, CGROUP_MASK_MEMORY, message, flags, error);
|
2013-06-27 21:50:35 +02:00
|
|
|
|
2020-06-23 11:35:39 +02:00
|
|
|
if (streq(name, "MemoryMin")) {
|
|
|
|
r = bus_cgroup_set_memory_protection(u, name, &c->memory_min, message, flags, error);
|
|
|
|
if (r > 0)
|
|
|
|
c->memory_min_set = true;
|
|
|
|
return r;
|
|
|
|
}
|
2018-06-09 02:33:14 +02:00
|
|
|
|
2020-06-23 11:35:39 +02:00
|
|
|
if (streq(name, "MemoryLow")) {
|
|
|
|
r = bus_cgroup_set_memory_protection(u, name, &c->memory_low, message, flags, error);
|
|
|
|
if (r > 0)
|
|
|
|
c->memory_low_set = true;
|
|
|
|
return r;
|
|
|
|
}
|
2013-06-27 21:50:35 +02:00
|
|
|
|
2020-06-23 11:35:39 +02:00
|
|
|
if (streq(name, "DefaultMemoryMin")) {
|
|
|
|
r = bus_cgroup_set_memory_protection(u, name, &c->default_memory_min, message, flags, error);
|
|
|
|
if (r > 0)
|
|
|
|
c->default_memory_min_set = true;
|
|
|
|
return r;
|
|
|
|
}
|
2019-04-16 19:44:05 +02:00
|
|
|
|
2020-06-23 11:35:39 +02:00
|
|
|
if (streq(name, "DefaultMemoryLow")) {
|
|
|
|
r = bus_cgroup_set_memory_protection(u, name, &c->default_memory_low, message, flags, error);
|
|
|
|
if (r > 0)
|
|
|
|
c->default_memory_low_set = true;
|
|
|
|
return r;
|
|
|
|
}
|
cgroup: Implement default propagation of MemoryLow with DefaultMemoryLow
In cgroup v2 we have protection tunables -- currently MemoryLow and
MemoryMin (there will be more in future for other resources, too). The
design of these protection tunables requires not only intermediate
cgroups to propagate protections, but also the units at the leaf of that
resource's operation to accept it (by setting MemoryLow or MemoryMin).
This makes sense from an low-level API design perspective, but it's a
good idea to also have a higher-level abstraction that can, by default,
propagate these resources to children recursively. In this patch, this
happens by having descendants set memory.low to N if their ancestor has
DefaultMemoryLow=N -- assuming they don't set a separate MemoryLow
value.
Any affected unit can opt out of this propagation by manually setting
`MemoryLow` to some value in its unit configuration. A unit can also
stop further propagation by setting `DefaultMemoryLow=` with no
argument. This removes further propagation in the subtree, but has no
effect on the unit itself (for that, use `MemoryLow=0`).
Our use case in production is simplifying the configuration of machines
which heavily rely on memory protection tunables, but currently require
tweaking a huge number of unit files to make that a reality. This
directive makes that significantly less fragile, and decreases the risk
of misconfiguration.
After this patch is merged, I will implement DefaultMemoryMin= using the
same principles.
2019-03-28 13:50:50 +01:00
|
|
|
|
2018-01-02 18:30:16 +01:00
|
|
|
if (streq(name, "MemoryHigh"))
|
|
|
|
return bus_cgroup_set_memory(u, name, &c->memory_high, message, flags, error);
|
2013-06-27 21:50:35 +02:00
|
|
|
|
2018-01-02 18:30:16 +01:00
|
|
|
if (streq(name, "MemorySwapMax"))
|
2018-03-09 11:34:50 +01:00
|
|
|
return bus_cgroup_set_swap(u, name, &c->memory_swap_max, message, flags, error);
|
2014-05-15 17:09:34 +02:00
|
|
|
|
2018-01-02 18:30:16 +01:00
|
|
|
if (streq(name, "MemoryMax"))
|
|
|
|
return bus_cgroup_set_memory(u, name, &c->memory_max, message, flags, error);
|
2015-09-11 16:48:24 +02:00
|
|
|
|
2018-01-02 18:30:16 +01:00
|
|
|
if (streq(name, "MemoryLimit"))
|
|
|
|
return bus_cgroup_set_memory(u, name, &c->memory_limit, message, flags, error);
|
2014-05-15 17:09:34 +02:00
|
|
|
|
2020-06-23 11:35:39 +02:00
|
|
|
if (streq(name, "MemoryMinScale")) {
|
|
|
|
r = bus_cgroup_set_memory_protection_scale(u, name, &c->memory_min, message, flags, error);
|
|
|
|
if (r > 0)
|
|
|
|
c->memory_min_set = true;
|
|
|
|
return r;
|
|
|
|
}
|
2018-06-09 02:33:14 +02:00
|
|
|
|
2020-06-23 11:35:39 +02:00
|
|
|
if (streq(name, "MemoryLowScale")) {
|
|
|
|
r = bus_cgroup_set_memory_protection_scale(u, name, &c->memory_low, message, flags, error);
|
|
|
|
if (r > 0)
|
|
|
|
c->memory_low_set = true;
|
|
|
|
return r;
|
|
|
|
}
|
cgroup: Implement default propagation of MemoryLow with DefaultMemoryLow
In cgroup v2 we have protection tunables -- currently MemoryLow and
MemoryMin (there will be more in future for other resources, too). The
design of these protection tunables requires not only intermediate
cgroups to propagate protections, but also the units at the leaf of that
resource's operation to accept it (by setting MemoryLow or MemoryMin).
This makes sense from an low-level API design perspective, but it's a
good idea to also have a higher-level abstraction that can, by default,
propagate these resources to children recursively. In this patch, this
happens by having descendants set memory.low to N if their ancestor has
DefaultMemoryLow=N -- assuming they don't set a separate MemoryLow
value.
Any affected unit can opt out of this propagation by manually setting
`MemoryLow` to some value in its unit configuration. A unit can also
stop further propagation by setting `DefaultMemoryLow=` with no
argument. This removes further propagation in the subtree, but has no
effect on the unit itself (for that, use `MemoryLow=0`).
Our use case in production is simplifying the configuration of machines
which heavily rely on memory protection tunables, but currently require
tweaking a huge number of unit files to make that a reality. This
directive makes that significantly less fragile, and decreases the risk
of misconfiguration.
After this patch is merged, I will implement DefaultMemoryMin= using the
same principles.
2019-03-28 13:50:50 +01:00
|
|
|
|
2020-06-23 11:35:39 +02:00
|
|
|
if (streq(name, "DefaultMemoryMinScale")) {
|
|
|
|
r = bus_cgroup_set_memory_protection_scale(u, name, &c->default_memory_min, message, flags, error);
|
|
|
|
if (r > 0)
|
|
|
|
c->default_memory_min_set = true;
|
|
|
|
return r;
|
|
|
|
}
|
2019-04-16 19:44:05 +02:00
|
|
|
|
2020-06-23 11:35:39 +02:00
|
|
|
if (streq(name, "DefaultMemoryLowScale")) {
|
|
|
|
r = bus_cgroup_set_memory_protection_scale(u, name, &c->default_memory_low, message, flags, error);
|
|
|
|
if (r > 0)
|
|
|
|
c->default_memory_low_set = true;
|
|
|
|
return r;
|
|
|
|
}
|
2018-01-02 18:30:16 +01:00
|
|
|
|
|
|
|
if (streq(name, "MemoryHighScale"))
|
|
|
|
return bus_cgroup_set_memory_scale(u, name, &c->memory_high, message, flags, error);
|
|
|
|
|
|
|
|
if (streq(name, "MemorySwapMaxScale"))
|
2018-03-09 11:34:50 +01:00
|
|
|
return bus_cgroup_set_swap_scale(u, name, &c->memory_swap_max, message, flags, error);
|
2018-01-02 18:30:16 +01:00
|
|
|
|
|
|
|
if (streq(name, "MemoryMaxScale"))
|
|
|
|
return bus_cgroup_set_memory_scale(u, name, &c->memory_max, message, flags, error);
|
2014-05-15 17:09:34 +02:00
|
|
|
|
2018-01-02 18:30:16 +01:00
|
|
|
if (streq(name, "MemoryLimitScale"))
|
|
|
|
return bus_cgroup_set_memory_scale(u, name, &c->memory_limit, message, flags, error);
|
|
|
|
|
2018-01-15 11:33:20 +01:00
|
|
|
if (streq(name, "TasksAccounting"))
|
2018-01-02 18:30:16 +01:00
|
|
|
return bus_cgroup_set_boolean(u, name, &c->tasks_accounting, CGROUP_MASK_PIDS, message, flags, error);
|
|
|
|
|
|
|
|
if (streq(name, "TasksMax"))
|
|
|
|
return bus_cgroup_set_tasks_max(u, name, &c->tasks_max, message, flags, error);
|
|
|
|
|
|
|
|
if (streq(name, "TasksMaxScale"))
|
|
|
|
return bus_cgroup_set_tasks_max_scale(u, name, &c->tasks_max, message, flags, error);
|
|
|
|
|
|
|
|
if (streq(name, "CPUQuotaPerSecUSec")) {
|
2014-04-25 13:27:25 +02:00
|
|
|
uint64_t u64;
|
|
|
|
|
|
|
|
r = sd_bus_message_read(message, "t", &u64);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
if (u64 <= 0)
|
2017-11-22 15:15:11 +01:00
|
|
|
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "CPUQuotaPerSecUSec= value out of range");
|
2014-04-25 13:27:25 +02:00
|
|
|
|
2017-11-22 15:03:51 +01:00
|
|
|
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
|
2014-04-25 13:27:25 +02:00
|
|
|
c->cpu_quota_per_sec_usec = u64;
|
2019-01-24 05:19:44 +01:00
|
|
|
u->warned_clamping_cpu_quota_period = false;
|
2015-09-11 18:21:53 +02:00
|
|
|
unit_invalidate_cgroup(u, CGROUP_MASK_CPU);
|
2017-11-22 15:03:51 +01:00
|
|
|
|
2017-06-18 11:18:41 +02:00
|
|
|
if (c->cpu_quota_per_sec_usec == USEC_INFINITY)
|
2017-11-22 15:03:51 +01:00
|
|
|
unit_write_setting(u, flags, "CPUQuota", "CPUQuota=");
|
2017-06-18 11:18:41 +02:00
|
|
|
else
|
2017-11-22 15:03:51 +01:00
|
|
|
/* config_parse_cpu_quota() requires an integer, so truncating division is used on
|
|
|
|
* purpose here. */
|
|
|
|
unit_write_settingf(u, flags, "CPUQuota",
|
|
|
|
"CPUQuota=%0.f%%",
|
|
|
|
(double) (c->cpu_quota_per_sec_usec / 10000));
|
2014-04-25 13:27:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
2018-11-02 17:21:57 +01:00
|
|
|
} else if (streq(name, "CPUQuotaPeriodUSec")) {
|
|
|
|
uint64_t u64;
|
|
|
|
|
|
|
|
r = sd_bus_message_read(message, "t", &u64);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
|
|
|
|
c->cpu_quota_period_usec = u64;
|
2019-01-24 05:19:44 +01:00
|
|
|
u->warned_clamping_cpu_quota_period = false;
|
2018-11-02 17:21:57 +01:00
|
|
|
unit_invalidate_cgroup(u, CGROUP_MASK_CPU);
|
|
|
|
if (c->cpu_quota_period_usec == USEC_INFINITY)
|
|
|
|
unit_write_setting(u, flags, "CPUQuotaPeriodSec", "CPUQuotaPeriodSec=");
|
|
|
|
else {
|
|
|
|
char v[FORMAT_TIMESPAN_MAX];
|
|
|
|
unit_write_settingf(u, flags, "CPUQuotaPeriodSec",
|
|
|
|
"CPUQuotaPeriodSec=%s",
|
|
|
|
format_timespan(v, sizeof(v), c->cpu_quota_period_usec, 1));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
2019-07-29 17:50:05 +02:00
|
|
|
} else if (STR_IN_SET(name, "AllowedCPUs", "AllowedMemoryNodes")) {
|
|
|
|
const void *a;
|
|
|
|
size_t n;
|
|
|
|
_cleanup_(cpu_set_reset) CPUSet new_set = {};
|
|
|
|
|
|
|
|
r = sd_bus_message_read_array(message, 'y', &a, &n);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = cpu_set_from_dbus(a, n, &new_set);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
|
|
|
|
_cleanup_free_ char *setstr = NULL;
|
|
|
|
CPUSet *set;
|
|
|
|
|
|
|
|
setstr = cpu_set_to_range_string(&new_set);
|
2019-11-01 10:22:03 +01:00
|
|
|
if (!setstr)
|
|
|
|
return -ENOMEM;
|
2019-07-29 17:50:05 +02:00
|
|
|
|
|
|
|
if (streq(name, "AllowedCPUs"))
|
|
|
|
set = &c->cpuset_cpus;
|
|
|
|
else
|
|
|
|
set = &c->cpuset_mems;
|
|
|
|
|
|
|
|
cpu_set_reset(set);
|
2019-11-01 10:22:03 +01:00
|
|
|
*set = new_set;
|
|
|
|
new_set = (CPUSet) {};
|
|
|
|
|
2019-07-29 17:50:05 +02:00
|
|
|
unit_invalidate_cgroup(u, CGROUP_MASK_CPUSET);
|
2019-11-01 10:22:03 +01:00
|
|
|
unit_write_settingf(u, flags, name, "%s=%s", name, setstr);
|
2019-07-29 17:50:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
2016-05-18 22:50:56 +02:00
|
|
|
} else if ((iol_type = cgroup_io_limit_type_from_string(name)) >= 0) {
|
2016-05-05 22:42:55 +02:00
|
|
|
const char *path;
|
|
|
|
unsigned n = 0;
|
|
|
|
uint64_t u64;
|
|
|
|
|
|
|
|
r = sd_bus_message_enter_container(message, 'a', "(st)");
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
while ((r = sd_bus_message_read(message, "(st)", &path, &u64)) > 0) {
|
|
|
|
|
2018-06-11 12:19:01 +02:00
|
|
|
if (!path_is_normalized(path))
|
|
|
|
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path '%s' specified in %s= is not normalized.", name, path);
|
2017-12-19 07:34:04 +01:00
|
|
|
|
2017-11-22 15:03:51 +01:00
|
|
|
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
|
2016-05-05 22:42:55 +02:00
|
|
|
CGroupIODeviceLimit *a = NULL, *b;
|
|
|
|
|
|
|
|
LIST_FOREACH(device_limits, b, c->io_device_limits) {
|
|
|
|
if (path_equal(path, b->path)) {
|
|
|
|
a = b;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!a) {
|
2016-05-18 22:50:56 +02:00
|
|
|
CGroupIOLimitType type;
|
|
|
|
|
2016-05-05 22:42:55 +02:00
|
|
|
a = new0(CGroupIODeviceLimit, 1);
|
|
|
|
if (!a)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
a->path = strdup(path);
|
|
|
|
if (!a->path) {
|
|
|
|
free(a);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2016-05-18 22:50:56 +02:00
|
|
|
for (type = 0; type < _CGROUP_IO_LIMIT_TYPE_MAX; type++)
|
|
|
|
a->limits[type] = cgroup_io_limit_defaults[type];
|
2016-05-05 22:42:55 +02:00
|
|
|
|
|
|
|
LIST_PREPEND(device_limits, c->io_device_limits, a);
|
|
|
|
}
|
|
|
|
|
2016-05-18 22:50:56 +02:00
|
|
|
a->limits[iol_type] = u64;
|
2016-05-05 22:42:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
n++;
|
|
|
|
}
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = sd_bus_message_exit_container(message);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2017-11-22 15:03:51 +01:00
|
|
|
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
|
2016-05-05 22:42:55 +02:00
|
|
|
CGroupIODeviceLimit *a;
|
|
|
|
_cleanup_free_ char *buf = NULL;
|
|
|
|
_cleanup_fclose_ FILE *f = NULL;
|
|
|
|
size_t size = 0;
|
|
|
|
|
|
|
|
if (n == 0) {
|
|
|
|
LIST_FOREACH(device_limits, a, c->io_device_limits)
|
2016-05-18 22:50:56 +02:00
|
|
|
a->limits[iol_type] = cgroup_io_limit_defaults[iol_type];
|
2016-05-05 22:42:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
unit_invalidate_cgroup(u, CGROUP_MASK_IO);
|
|
|
|
|
2019-04-04 11:46:44 +02:00
|
|
|
f = open_memstream_unlocked(&buf, &size);
|
2016-05-05 22:42:55 +02:00
|
|
|
if (!f)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2016-05-18 22:50:56 +02:00
|
|
|
fprintf(f, "%s=\n", name);
|
|
|
|
LIST_FOREACH(device_limits, a, c->io_device_limits)
|
|
|
|
if (a->limits[iol_type] != cgroup_io_limit_defaults[iol_type])
|
|
|
|
fprintf(f, "%s=%s %" PRIu64 "\n", name, a->path, a->limits[iol_type]);
|
2016-05-05 22:42:55 +02:00
|
|
|
|
|
|
|
r = fflush_and_check(f);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2017-11-22 15:03:51 +01:00
|
|
|
unit_write_setting(u, flags, name, buf);
|
2016-05-05 22:42:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
} else if (streq(name, "IODeviceWeight")) {
|
|
|
|
const char *path;
|
|
|
|
uint64_t weight;
|
|
|
|
unsigned n = 0;
|
|
|
|
|
|
|
|
r = sd_bus_message_enter_container(message, 'a', "(st)");
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
while ((r = sd_bus_message_read(message, "(st)", &path, &weight)) > 0) {
|
|
|
|
|
2018-06-11 12:19:01 +02:00
|
|
|
if (!path_is_normalized(path))
|
|
|
|
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path '%s' specified in %s= is not normalized.", name, path);
|
2017-12-19 07:34:04 +01:00
|
|
|
|
2016-05-05 22:42:55 +02:00
|
|
|
if (!CGROUP_WEIGHT_IS_OK(weight) || weight == CGROUP_WEIGHT_INVALID)
|
2017-11-22 15:15:11 +01:00
|
|
|
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "IODeviceWeight= value out of range");
|
2016-05-05 22:42:55 +02:00
|
|
|
|
2017-11-22 15:03:51 +01:00
|
|
|
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
|
2016-05-05 22:42:55 +02:00
|
|
|
CGroupIODeviceWeight *a = NULL, *b;
|
|
|
|
|
|
|
|
LIST_FOREACH(device_weights, b, c->io_device_weights) {
|
|
|
|
if (path_equal(b->path, path)) {
|
|
|
|
a = b;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!a) {
|
|
|
|
a = new0(CGroupIODeviceWeight, 1);
|
|
|
|
if (!a)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
a->path = strdup(path);
|
|
|
|
if (!a->path) {
|
|
|
|
free(a);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
2018-01-02 18:28:17 +01:00
|
|
|
LIST_PREPEND(device_weights, c->io_device_weights, a);
|
2016-05-05 22:42:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
a->weight = weight;
|
|
|
|
}
|
|
|
|
|
|
|
|
n++;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = sd_bus_message_exit_container(message);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2017-11-22 15:03:51 +01:00
|
|
|
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
|
2016-05-05 22:42:55 +02:00
|
|
|
_cleanup_free_ char *buf = NULL;
|
|
|
|
_cleanup_fclose_ FILE *f = NULL;
|
|
|
|
CGroupIODeviceWeight *a;
|
|
|
|
size_t size = 0;
|
|
|
|
|
|
|
|
if (n == 0) {
|
|
|
|
while (c->io_device_weights)
|
|
|
|
cgroup_context_free_io_device_weight(c, c->io_device_weights);
|
|
|
|
}
|
|
|
|
|
|
|
|
unit_invalidate_cgroup(u, CGROUP_MASK_IO);
|
|
|
|
|
2019-04-04 11:46:44 +02:00
|
|
|
f = open_memstream_unlocked(&buf, &size);
|
2016-05-05 22:42:55 +02:00
|
|
|
if (!f)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2017-12-11 19:50:30 +01:00
|
|
|
fputs("IODeviceWeight=\n", f);
|
2016-05-05 22:42:55 +02:00
|
|
|
LIST_FOREACH(device_weights, a, c->io_device_weights)
|
|
|
|
fprintf(f, "IODeviceWeight=%s %" PRIu64 "\n", a->path, a->weight);
|
|
|
|
|
|
|
|
r = fflush_and_check(f);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2017-11-22 15:03:51 +01:00
|
|
|
unit_write_setting(u, flags, name, buf);
|
2018-06-13 23:16:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
} else if (streq(name, "IODeviceLatencyTargetUSec")) {
|
|
|
|
const char *path;
|
|
|
|
uint64_t target;
|
|
|
|
unsigned n = 0;
|
|
|
|
|
|
|
|
r = sd_bus_message_enter_container(message, 'a', "(st)");
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
while ((r = sd_bus_message_read(message, "(st)", &path, &target)) > 0) {
|
|
|
|
|
|
|
|
if (!path_is_normalized(path))
|
|
|
|
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path '%s' specified in %s= is not normalized.", name, path);
|
|
|
|
|
|
|
|
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
|
|
|
|
CGroupIODeviceLatency *a = NULL, *b;
|
|
|
|
|
|
|
|
LIST_FOREACH(device_latencies, b, c->io_device_latencies) {
|
|
|
|
if (path_equal(b->path, path)) {
|
|
|
|
a = b;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!a) {
|
|
|
|
a = new0(CGroupIODeviceLatency, 1);
|
|
|
|
if (!a)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
a->path = strdup(path);
|
|
|
|
if (!a->path) {
|
|
|
|
free(a);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
LIST_PREPEND(device_latencies, c->io_device_latencies, a);
|
|
|
|
}
|
|
|
|
|
|
|
|
a->target_usec = target;
|
|
|
|
}
|
|
|
|
|
|
|
|
n++;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = sd_bus_message_exit_container(message);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
|
|
|
|
_cleanup_free_ char *buf = NULL;
|
|
|
|
_cleanup_fclose_ FILE *f = NULL;
|
|
|
|
char ts[FORMAT_TIMESPAN_MAX];
|
|
|
|
CGroupIODeviceLatency *a;
|
|
|
|
size_t size = 0;
|
|
|
|
|
|
|
|
if (n == 0) {
|
|
|
|
while (c->io_device_latencies)
|
|
|
|
cgroup_context_free_io_device_latency(c, c->io_device_latencies);
|
|
|
|
}
|
|
|
|
|
|
|
|
unit_invalidate_cgroup(u, CGROUP_MASK_IO);
|
|
|
|
|
2019-04-04 11:46:44 +02:00
|
|
|
f = open_memstream_unlocked(&buf, &size);
|
2018-06-13 23:16:35 +02:00
|
|
|
if (!f)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
fputs("IODeviceLatencyTargetSec=\n", f);
|
|
|
|
LIST_FOREACH(device_latencies, a, c->io_device_latencies)
|
|
|
|
fprintf(f, "IODeviceLatencyTargetSec=%s %s\n",
|
|
|
|
a->path, format_timespan(ts, sizeof(ts), a->target_usec, 1));
|
|
|
|
|
|
|
|
r = fflush_and_check(f);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
unit_write_setting(u, flags, name, buf);
|
2016-05-05 22:42:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
2016-06-08 20:04:22 +02:00
|
|
|
} else if (STR_IN_SET(name, "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) {
|
2013-11-19 21:12:59 +01:00
|
|
|
const char *path;
|
2013-09-10 17:23:07 +02:00
|
|
|
bool read = true;
|
2013-11-19 21:12:59 +01:00
|
|
|
unsigned n = 0;
|
|
|
|
uint64_t u64;
|
2013-09-10 17:23:07 +02:00
|
|
|
|
|
|
|
if (streq(name, "BlockIOWriteBandwidth"))
|
|
|
|
read = false;
|
|
|
|
|
2013-11-19 21:12:59 +01:00
|
|
|
r = sd_bus_message_enter_container(message, 'a', "(st)");
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2013-09-10 17:23:07 +02:00
|
|
|
|
2013-11-19 21:12:59 +01:00
|
|
|
while ((r = sd_bus_message_read(message, "(st)", &path, &u64)) > 0) {
|
2013-09-10 17:23:07 +02:00
|
|
|
|
2018-06-11 12:19:01 +02:00
|
|
|
if (!path_is_normalized(path))
|
|
|
|
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path '%s' specified in %s= is not normalized.", name, path);
|
2017-12-19 07:34:04 +01:00
|
|
|
|
2017-11-22 15:03:51 +01:00
|
|
|
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
|
2013-11-19 21:12:59 +01:00
|
|
|
CGroupBlockIODeviceBandwidth *a = NULL, *b;
|
2013-09-10 17:23:07 +02:00
|
|
|
|
|
|
|
LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) {
|
2016-05-18 22:51:46 +02:00
|
|
|
if (path_equal(path, b->path)) {
|
2013-09-10 17:23:07 +02:00
|
|
|
a = b;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-11-19 21:12:59 +01:00
|
|
|
if (!a) {
|
2013-09-10 17:23:07 +02:00
|
|
|
a = new0(CGroupBlockIODeviceBandwidth, 1);
|
|
|
|
if (!a)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2016-05-18 22:51:46 +02:00
|
|
|
a->rbps = CGROUP_LIMIT_MAX;
|
|
|
|
a->wbps = CGROUP_LIMIT_MAX;
|
2013-09-10 17:23:07 +02:00
|
|
|
a->path = strdup(path);
|
|
|
|
if (!a->path) {
|
|
|
|
free(a);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
2013-11-19 21:12:59 +01:00
|
|
|
|
|
|
|
LIST_PREPEND(device_bandwidths, c->blockio_device_bandwidths, a);
|
2013-09-10 17:23:07 +02:00
|
|
|
}
|
|
|
|
|
2016-05-18 22:51:46 +02:00
|
|
|
if (read)
|
|
|
|
a->rbps = u64;
|
|
|
|
else
|
|
|
|
a->wbps = u64;
|
2013-09-10 17:23:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
n++;
|
|
|
|
}
|
2013-11-19 21:12:59 +01:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2013-09-10 17:23:07 +02:00
|
|
|
|
2014-02-24 02:59:57 +01:00
|
|
|
r = sd_bus_message_exit_container(message);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2017-11-22 15:03:51 +01:00
|
|
|
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
|
2016-05-18 22:51:46 +02:00
|
|
|
CGroupBlockIODeviceBandwidth *a;
|
2013-09-10 17:23:07 +02:00
|
|
|
_cleanup_free_ char *buf = NULL;
|
|
|
|
_cleanup_fclose_ FILE *f = NULL;
|
|
|
|
size_t size = 0;
|
|
|
|
|
|
|
|
if (n == 0) {
|
2016-05-18 22:51:46 +02:00
|
|
|
LIST_FOREACH(device_bandwidths, a, c->blockio_device_bandwidths) {
|
|
|
|
if (read)
|
|
|
|
a->rbps = CGROUP_LIMIT_MAX;
|
|
|
|
else
|
|
|
|
a->wbps = CGROUP_LIMIT_MAX;
|
|
|
|
}
|
2013-09-10 17:23:07 +02:00
|
|
|
}
|
|
|
|
|
2015-09-11 18:21:53 +02:00
|
|
|
unit_invalidate_cgroup(u, CGROUP_MASK_BLKIO);
|
2014-04-25 13:26:37 +02:00
|
|
|
|
2019-04-04 11:46:44 +02:00
|
|
|
f = open_memstream_unlocked(&buf, &size);
|
2013-09-10 17:23:07 +02:00
|
|
|
if (!f)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2015-08-06 00:31:09 +02:00
|
|
|
if (read) {
|
2017-12-11 19:50:30 +01:00
|
|
|
fputs("BlockIOReadBandwidth=\n", f);
|
2015-08-06 00:31:09 +02:00
|
|
|
LIST_FOREACH(device_bandwidths, a, c->blockio_device_bandwidths)
|
2016-05-18 22:51:46 +02:00
|
|
|
if (a->rbps != CGROUP_LIMIT_MAX)
|
|
|
|
fprintf(f, "BlockIOReadBandwidth=%s %" PRIu64 "\n", a->path, a->rbps);
|
2013-09-10 17:23:07 +02:00
|
|
|
} else {
|
2017-12-11 19:50:30 +01:00
|
|
|
fputs("BlockIOWriteBandwidth=\n", f);
|
2013-09-10 17:23:07 +02:00
|
|
|
LIST_FOREACH(device_bandwidths, a, c->blockio_device_bandwidths)
|
2016-05-18 22:51:46 +02:00
|
|
|
if (a->wbps != CGROUP_LIMIT_MAX)
|
|
|
|
fprintf(f, "BlockIOWriteBandwidth=%s %" PRIu64 "\n", a->path, a->wbps);
|
2013-09-10 17:23:07 +02:00
|
|
|
}
|
|
|
|
|
2015-10-21 18:17:12 +02:00
|
|
|
r = fflush_and_check(f);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2017-11-22 15:03:51 +01:00
|
|
|
|
|
|
|
unit_write_setting(u, flags, name, buf);
|
2013-08-27 07:36:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
} else if (streq(name, "BlockIODeviceWeight")) {
|
2013-11-19 21:12:59 +01:00
|
|
|
const char *path;
|
2015-09-11 16:48:24 +02:00
|
|
|
uint64_t weight;
|
2013-08-27 07:36:53 +02:00
|
|
|
unsigned n = 0;
|
|
|
|
|
2013-11-19 21:12:59 +01:00
|
|
|
r = sd_bus_message_enter_container(message, 'a', "(st)");
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2013-08-27 07:36:53 +02:00
|
|
|
|
2015-09-11 16:48:24 +02:00
|
|
|
while ((r = sd_bus_message_read(message, "(st)", &path, &weight)) > 0) {
|
2013-08-27 07:36:53 +02:00
|
|
|
|
2018-06-11 12:19:01 +02:00
|
|
|
if (!path_is_normalized(path))
|
|
|
|
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path '%s' specified in %s= is not normalized.", name, path);
|
2017-12-19 07:34:04 +01:00
|
|
|
|
2015-09-11 16:48:24 +02:00
|
|
|
if (!CGROUP_BLKIO_WEIGHT_IS_OK(weight) || weight == CGROUP_BLKIO_WEIGHT_INVALID)
|
2017-11-22 15:15:11 +01:00
|
|
|
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "BlockIODeviceWeight= out of range");
|
2013-08-27 07:36:53 +02:00
|
|
|
|
2017-11-22 15:03:51 +01:00
|
|
|
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
|
2013-11-19 21:12:59 +01:00
|
|
|
CGroupBlockIODeviceWeight *a = NULL, *b;
|
2013-08-27 07:36:53 +02:00
|
|
|
|
|
|
|
LIST_FOREACH(device_weights, b, c->blockio_device_weights) {
|
|
|
|
if (path_equal(b->path, path)) {
|
|
|
|
a = b;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-11-19 21:12:59 +01:00
|
|
|
if (!a) {
|
2013-08-27 07:36:53 +02:00
|
|
|
a = new0(CGroupBlockIODeviceWeight, 1);
|
|
|
|
if (!a)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
a->path = strdup(path);
|
|
|
|
if (!a->path) {
|
|
|
|
free(a);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
2018-01-02 18:28:17 +01:00
|
|
|
LIST_PREPEND(device_weights, c->blockio_device_weights, a);
|
2013-08-27 07:36:53 +02:00
|
|
|
}
|
|
|
|
|
2015-09-11 16:48:24 +02:00
|
|
|
a->weight = weight;
|
2013-08-27 07:36:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
n++;
|
|
|
|
}
|
|
|
|
|
2014-02-24 02:59:57 +01:00
|
|
|
r = sd_bus_message_exit_container(message);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2017-11-22 15:03:51 +01:00
|
|
|
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
|
2013-08-27 07:36:53 +02:00
|
|
|
_cleanup_free_ char *buf = NULL;
|
|
|
|
_cleanup_fclose_ FILE *f = NULL;
|
|
|
|
CGroupBlockIODeviceWeight *a;
|
|
|
|
size_t size = 0;
|
|
|
|
|
|
|
|
if (n == 0) {
|
|
|
|
while (c->blockio_device_weights)
|
|
|
|
cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights);
|
|
|
|
}
|
|
|
|
|
2015-09-11 18:21:53 +02:00
|
|
|
unit_invalidate_cgroup(u, CGROUP_MASK_BLKIO);
|
2014-04-25 13:26:37 +02:00
|
|
|
|
2019-04-04 11:46:44 +02:00
|
|
|
f = open_memstream_unlocked(&buf, &size);
|
2013-08-27 07:36:53 +02:00
|
|
|
if (!f)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2017-12-11 19:50:30 +01:00
|
|
|
fputs("BlockIODeviceWeight=\n", f);
|
2013-08-27 07:36:53 +02:00
|
|
|
LIST_FOREACH(device_weights, a, c->blockio_device_weights)
|
2015-09-11 16:48:24 +02:00
|
|
|
fprintf(f, "BlockIODeviceWeight=%s %" PRIu64 "\n", a->path, a->weight);
|
2013-08-27 07:36:53 +02:00
|
|
|
|
2015-10-21 18:17:12 +02:00
|
|
|
r = fflush_and_check(f);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2017-11-22 15:03:51 +01:00
|
|
|
|
|
|
|
unit_write_setting(u, flags, name, buf);
|
2013-09-10 17:23:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
2013-06-27 23:13:17 +02:00
|
|
|
} else if (streq(name, "DevicePolicy")) {
|
|
|
|
const char *policy;
|
|
|
|
CGroupDevicePolicy p;
|
|
|
|
|
2013-11-19 21:12:59 +01:00
|
|
|
r = sd_bus_message_read(message, "s", &policy);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2013-06-27 23:13:17 +02:00
|
|
|
|
|
|
|
p = cgroup_device_policy_from_string(policy);
|
|
|
|
if (p < 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2017-11-22 15:03:51 +01:00
|
|
|
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
|
2013-06-27 23:13:17 +02:00
|
|
|
c->device_policy = p;
|
2015-09-11 18:21:53 +02:00
|
|
|
unit_invalidate_cgroup(u, CGROUP_MASK_DEVICES);
|
2017-11-22 15:03:51 +01:00
|
|
|
unit_write_settingf(u, flags, name, "DevicePolicy=%s", policy);
|
2013-06-27 23:13:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
} else if (streq(name, "DeviceAllow")) {
|
2013-11-19 21:12:59 +01:00
|
|
|
const char *path, *rwm;
|
2013-06-27 23:13:17 +02:00
|
|
|
unsigned n = 0;
|
|
|
|
|
2013-11-19 21:12:59 +01:00
|
|
|
r = sd_bus_message_enter_container(message, 'a', "(ss)");
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2013-06-27 23:13:17 +02:00
|
|
|
|
2013-11-19 21:12:59 +01:00
|
|
|
while ((r = sd_bus_message_read(message, "(ss)", &path, &rwm)) > 0) {
|
2013-06-27 23:13:17 +02:00
|
|
|
|
2018-06-11 12:22:58 +02:00
|
|
|
if (!valid_device_allow_pattern(path) || strpbrk(path, WHITESPACE))
|
|
|
|
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "DeviceAllow= requires device node or pattern");
|
2013-06-27 23:13:17 +02:00
|
|
|
|
|
|
|
if (isempty(rwm))
|
|
|
|
rwm = "rwm";
|
2018-06-11 12:22:58 +02:00
|
|
|
else if (!in_charset(rwm, "rwm"))
|
2017-11-22 15:15:11 +01:00
|
|
|
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "DeviceAllow= requires combination of rwm flags");
|
2013-06-27 23:13:17 +02:00
|
|
|
|
2017-11-22 15:03:51 +01:00
|
|
|
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
|
2013-11-19 21:12:59 +01:00
|
|
|
CGroupDeviceAllow *a = NULL, *b;
|
2013-08-28 03:49:11 +02:00
|
|
|
|
|
|
|
LIST_FOREACH(device_allow, b, c->device_allow) {
|
2013-09-10 18:21:10 +02:00
|
|
|
if (path_equal(b->path, path)) {
|
2013-08-28 03:49:11 +02:00
|
|
|
a = b;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-11-19 21:12:59 +01:00
|
|
|
if (!a) {
|
2013-08-28 03:49:11 +02:00
|
|
|
a = new0(CGroupDeviceAllow, 1);
|
|
|
|
if (!a)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
a->path = strdup(path);
|
|
|
|
if (!a->path) {
|
|
|
|
free(a);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
2013-11-19 21:12:59 +01:00
|
|
|
|
|
|
|
LIST_PREPEND(device_allow, c->device_allow, a);
|
2013-06-27 23:13:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
a->r = !!strchr(rwm, 'r');
|
|
|
|
a->w = !!strchr(rwm, 'w');
|
|
|
|
a->m = !!strchr(rwm, 'm');
|
|
|
|
}
|
|
|
|
|
2013-06-28 04:12:58 +02:00
|
|
|
n++;
|
2013-06-27 23:13:17 +02:00
|
|
|
}
|
2013-12-25 19:00:12 +01:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2013-06-27 23:13:17 +02:00
|
|
|
|
2014-02-24 02:59:57 +01:00
|
|
|
r = sd_bus_message_exit_container(message);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2017-11-22 15:03:51 +01:00
|
|
|
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
|
2013-06-27 23:13:17 +02:00
|
|
|
_cleanup_free_ char *buf = NULL;
|
|
|
|
_cleanup_fclose_ FILE *f = NULL;
|
|
|
|
CGroupDeviceAllow *a;
|
|
|
|
size_t size = 0;
|
|
|
|
|
|
|
|
if (n == 0) {
|
|
|
|
while (c->device_allow)
|
|
|
|
cgroup_context_free_device_allow(c, c->device_allow);
|
|
|
|
}
|
|
|
|
|
2015-09-11 18:21:53 +02:00
|
|
|
unit_invalidate_cgroup(u, CGROUP_MASK_DEVICES);
|
2014-04-25 13:26:37 +02:00
|
|
|
|
2019-04-04 11:46:44 +02:00
|
|
|
f = open_memstream_unlocked(&buf, &size);
|
2013-06-27 23:13:17 +02:00
|
|
|
if (!f)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2017-12-11 19:50:30 +01:00
|
|
|
fputs("DeviceAllow=\n", f);
|
2013-06-27 23:13:17 +02:00
|
|
|
LIST_FOREACH(device_allow, a, c->device_allow)
|
|
|
|
fprintf(f, "DeviceAllow=%s %s%s%s\n", a->path, a->r ? "r" : "", a->w ? "w" : "", a->m ? "m" : "");
|
|
|
|
|
2015-10-21 18:17:12 +02:00
|
|
|
r = fflush_and_check(f);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2017-11-22 15:03:51 +01:00
|
|
|
unit_write_setting(u, flags, name, buf);
|
2013-06-27 23:13:17 +02:00
|
|
|
}
|
|
|
|
|
2013-06-27 21:50:35 +02:00
|
|
|
return 1;
|
2014-11-05 17:57:23 +01:00
|
|
|
|
2017-09-05 11:16:35 +02:00
|
|
|
} else if (streq(name, "IPAccounting")) {
|
|
|
|
int b;
|
|
|
|
|
|
|
|
r = sd_bus_message_read(message, "b", &b);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2017-11-22 15:03:51 +01:00
|
|
|
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
|
2017-09-05 11:16:35 +02:00
|
|
|
c->ip_accounting = b;
|
|
|
|
|
|
|
|
unit_invalidate_cgroup_bpf(u);
|
2017-11-22 15:03:51 +01:00
|
|
|
unit_write_settingf(u, flags, name, "IPAccounting=%s", yes_no(b));
|
2017-09-05 11:16:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
} else if (STR_IN_SET(name, "IPAddressAllow", "IPAddressDeny")) {
|
|
|
|
IPAddressAccessItem **list;
|
|
|
|
size_t n = 0;
|
|
|
|
|
|
|
|
list = streq(name, "IPAddressAllow") ? &c->ip_address_allow : &c->ip_address_deny;
|
|
|
|
|
|
|
|
r = sd_bus_message_enter_container(message, 'a', "(iayu)");
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
const void *ap;
|
|
|
|
int32_t family;
|
|
|
|
uint32_t prefixlen;
|
|
|
|
size_t an;
|
|
|
|
|
|
|
|
r = sd_bus_message_enter_container(message, 'r', "iayu");
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
if (r == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
r = sd_bus_message_read(message, "i", &family);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
if (!IN_SET(family, AF_INET, AF_INET6))
|
2017-11-22 15:15:11 +01:00
|
|
|
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "%s= expects IPv4 or IPv6 addresses only.", name);
|
2017-09-05 11:16:35 +02:00
|
|
|
|
|
|
|
r = sd_bus_message_read_array(message, 'y', &ap, &an);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
if (an != FAMILY_ADDRESS_SIZE(family))
|
2017-11-22 15:15:11 +01:00
|
|
|
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "IP address has wrong size for family (%s, expected %zu, got %zu)",
|
2017-09-05 11:16:35 +02:00
|
|
|
af_to_name(family), FAMILY_ADDRESS_SIZE(family), an);
|
|
|
|
|
|
|
|
r = sd_bus_message_read(message, "u", &prefixlen);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
if (prefixlen > FAMILY_ADDRESS_SIZE(family)*8)
|
2017-11-22 15:15:11 +01:00
|
|
|
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Prefix length %" PRIu32 " too large for address family %s.", prefixlen, af_to_name(family));
|
2017-09-05 11:16:35 +02:00
|
|
|
|
2017-11-22 15:03:51 +01:00
|
|
|
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
|
2017-09-05 11:16:35 +02:00
|
|
|
IPAddressAccessItem *item;
|
|
|
|
|
|
|
|
item = new0(IPAddressAccessItem, 1);
|
|
|
|
if (!item)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
item->family = family;
|
|
|
|
item->prefixlen = prefixlen;
|
|
|
|
memcpy(&item->address, ap, an);
|
|
|
|
|
|
|
|
LIST_PREPEND(items, *list, item);
|
|
|
|
}
|
|
|
|
|
|
|
|
r = sd_bus_message_exit_container(message);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
n++;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = sd_bus_message_exit_container(message);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2017-09-05 17:41:34 +02:00
|
|
|
*list = ip_address_access_reduce(*list);
|
|
|
|
|
2017-11-22 15:03:51 +01:00
|
|
|
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
|
2017-09-05 11:16:35 +02:00
|
|
|
_cleanup_free_ char *buf = NULL;
|
|
|
|
_cleanup_fclose_ FILE *f = NULL;
|
|
|
|
IPAddressAccessItem *item;
|
|
|
|
size_t size = 0;
|
|
|
|
|
|
|
|
if (n == 0)
|
|
|
|
*list = ip_address_access_free_all(*list);
|
|
|
|
|
|
|
|
unit_invalidate_cgroup_bpf(u);
|
2019-04-04 11:46:44 +02:00
|
|
|
f = open_memstream_unlocked(&buf, &size);
|
2017-09-05 11:16:35 +02:00
|
|
|
if (!f)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2017-12-11 19:50:30 +01:00
|
|
|
fputs(name, f);
|
|
|
|
fputs("=\n", f);
|
2017-09-05 11:16:35 +02:00
|
|
|
|
|
|
|
LIST_FOREACH(items, item, *list) {
|
|
|
|
char buffer[CONST_MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN)];
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
if (!inet_ntop(item->family, &item->address, buffer, sizeof(buffer)))
|
2019-07-11 15:42:14 +02:00
|
|
|
return errno_or_else(EINVAL);
|
2017-09-05 11:16:35 +02:00
|
|
|
|
|
|
|
fprintf(f, "%s=%s/%u\n", name, buffer, item->prefixlen);
|
|
|
|
}
|
|
|
|
|
|
|
|
r = fflush_and_check(f);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2017-11-22 15:03:51 +01:00
|
|
|
|
|
|
|
unit_write_setting(u, flags, name, buf);
|
2017-09-05 11:16:35 +02:00
|
|
|
}
|
|
|
|
|
2015-09-10 12:32:16 +02:00
|
|
|
return 1;
|
2014-11-05 17:57:23 +01:00
|
|
|
}
|
|
|
|
|
2019-04-17 09:42:55 +02:00
|
|
|
if (streq(name, "DisableControllers") || (u->transient && u->load_state == UNIT_STUB))
|
2017-12-19 10:14:39 +01:00
|
|
|
return bus_cgroup_set_transient_property(u, c, name, message, flags, error);
|
2013-06-27 21:14:56 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|