]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
core: serialize/deserialize IP accounting across daemon reload/reexec
authorLennart Poettering <lennart@poettering.net>
Thu, 7 Sep 2017 12:07:13 +0000 (14:07 +0200)
committerLennart Poettering <lennart@poettering.net>
Fri, 22 Sep 2017 13:24:55 +0000 (15:24 +0200)
Make sure the current IP accounting counters aren't lost during
reload/reexec.

Note that we destroy all BPF file objects during a reload: the BPF
programs, the access and the accounting maps. The former two need to be
regenerated anyway with the newly loaded configuration data, but the
latter one needs to survive reloads/reexec. In this implementation I
opted to only save/restore the accounting map content instead of the map
itself. While this opens a (theoretic) window where IP traffic is still
accounted to the old map after we read it out, and we thus miss a few
bytes this has the benefit that we can alter the map layout between
versions should the need arise.

src/core/cgroup.c
src/core/unit.c
src/core/unit.h

index af611e7e7bc610b6098b5ad641edf8d0c2564dc9..c6667b39c78c4a4652b615b74b89c20e538dcaf6 100644 (file)
@@ -2224,6 +2224,7 @@ int unit_get_ip_accounting(
                 CGroupIPAccountingMetric metric,
                 uint64_t *ret) {
 
+        uint64_t value;
         int fd, r;
 
         assert(u);
@@ -2239,9 +2240,17 @@ int unit_get_ip_accounting(
                 return -ENODATA;
 
         if (IN_SET(metric, CGROUP_IP_INGRESS_BYTES, CGROUP_IP_EGRESS_BYTES))
-                r = bpf_firewall_read_accounting(fd, ret, NULL);
+                r = bpf_firewall_read_accounting(fd, &value, NULL);
         else
-                r = bpf_firewall_read_accounting(fd, NULL, ret);
+                r = bpf_firewall_read_accounting(fd, NULL, &value);
+        if (r < 0)
+                return r;
+
+        /* Add in additional metrics from a previous runtime. Note that when reexecing/reloading the daemon we compile
+         * all BPF programs and maps anew, but serialize the old counters. When deserializing we store them in the
+         * ip_accounting_extra[] field, and add them in here transparently. */
+
+        *ret = value + u->ip_accounting_extra[metric];
 
         return r;
 }
@@ -2275,6 +2284,8 @@ int unit_reset_ip_accounting(Unit *u) {
         if (u->ip_accounting_egress_map_fd >= 0)
                 q = bpf_firewall_reset_accounting(u->ip_accounting_egress_map_fd);
 
+        zero(u->ip_accounting_extra);
+
         return r < 0 ? r : q;
 }
 
index bb40baf2363ed2e867fb5ba4ceceb0ff24260f2a..5ed57644551740c6e535112d2cb752a39da02e25 100644 (file)
@@ -2770,7 +2770,15 @@ static int unit_serialize_cgroup_mask(FILE *f, const char *key, CGroupMask mask)
         return r;
 }
 
+static const char *ip_accounting_metric_field[_CGROUP_IP_ACCOUNTING_METRIC_MAX] = {
+        [CGROUP_IP_INGRESS_BYTES] = "ip-accounting-ingress-bytes",
+        [CGROUP_IP_INGRESS_PACKETS] = "ip-accounting-ingress-packets",
+        [CGROUP_IP_EGRESS_BYTES] = "ip-accounting-egress-bytes",
+        [CGROUP_IP_EGRESS_PACKETS] = "ip-accounting-egress-packets",
+};
+
 int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) {
+        CGroupIPAccountingMetric m;
         int r;
 
         assert(u);
@@ -2831,6 +2839,14 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) {
 
         bus_track_serialize(u->bus_track, f, "ref");
 
+        for (m = 0; m < _CGROUP_IP_ACCOUNTING_METRIC_MAX; m++) {
+                uint64_t v;
+
+                r = unit_get_ip_accounting(u, m, &v);
+                if (r >= 0)
+                        unit_serialize_item_format(u, f, ip_accounting_metric_field[m], "%" PRIu64, v);
+        }
+
         if (serialize_jobs) {
                 if (u->job) {
                         fprintf(f, "job\n");
@@ -2937,6 +2953,7 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
 
         for (;;) {
                 char line[LINE_MAX], *l, *v;
+                CGroupIPAccountingMetric m;
                 size_t k;
 
                 if (!fgets(line, sizeof(line), f)) {
@@ -3147,6 +3164,21 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
                         continue;
                 }
 
+                /* Check if this is an IP accounting metric serialization field */
+                for (m = 0; m < _CGROUP_IP_ACCOUNTING_METRIC_MAX; m++)
+                        if (streq(l, ip_accounting_metric_field[m]))
+                                break;
+                if (m < _CGROUP_IP_ACCOUNTING_METRIC_MAX) {
+                        uint64_t c;
+
+                        r = safe_atou64(v, &c);
+                        if (r < 0)
+                                log_unit_debug(u, "Failed to parse IP accounting value %s, ignoring.", v);
+                        else
+                                u->ip_accounting_extra[m] = c;
+                        continue;
+                }
+
                 if (unit_can_serialize(u)) {
                         if (rt) {
                                 r = exec_runtime_deserialize_item(u, rt, l, v, fds);
index a707e5259dee201b14f09432f9355a9a3fa06c2c..2759bd07a28a6d994ccd2fcdfc0ab794a4c59fe5 100644 (file)
@@ -34,6 +34,7 @@ typedef struct UnitStatusMessageFormats UnitStatusMessageFormats;
 #include "install.h"
 #include "list.h"
 #include "unit-name.h"
+#include "cgroup.h"
 
 typedef enum KillOperation {
         KILL_TERMINATE,
@@ -224,6 +225,8 @@ struct Unit {
         BPFProgram *ip_bpf_ingress;
         BPFProgram *ip_bpf_egress;
 
+        uint64_t ip_accounting_extra[_CGROUP_IP_ACCOUNTING_METRIC_MAX];
+
         /* How to start OnFailure units */
         JobMode on_failure_job_mode;