Patch adds functionality to apply certain cgroups limits, such as shares of 
cpu, memory limit and blkio.
Unit files of type Service, Mount, Swap and Socket have new options (under 
corresponding [Service], [Mount], [Swap], [Socket] option group ):
- "MemoryHardLimit" accepts int64 value (considered bytes), puts the value in 
"memory" cgroup (if defined) in file "memory.limit_in_bytes" ;
- "MemoryLimit" accepts int64 value (considered bytes), puts the value in 
"memory" cgroup (if defined) in file "memory.soft_limit_in_bytes" ;
- "CPUShares" accepts in32 value (considered shares of cpu), puts the value in 
"cpu" cgroup (if defined - should be, by default systemd behaviour) in file 
"cpu.shares" ;
- "HddReadLimit" accepts string which specifies read limit for one or more 
physical disks. String should be in format: "<major>:<minor> <limit> [ ; 
<major>:<minor> <limit> ... ]". More disks limit can be specified with ";" . 
This option will put the value(s) in "blkio" control goup (if defined) in file 
"blkio.throttle.read_bps_device". Example: "8:0 2000000" limits to approx. 
2MB/s read for disk 8:0.
- "HddWriteLimit" accepts string which specifies write limit for one or more 
physical disks. String should be in format: "<major>:<minor> <limit> [ ; 
<major>:<minor> <limit> ... ]". More disks limits can be specified with ";". 
This option will put the value(s) in "blkio" control goup (if defined) in file 
"blkio.throttle.write_bps_device". Example: "8:0 2000000" limits to approx. 
2MB/s read for disk 8:0.

This patch is made for systemd-29 (branch v29 sha1: 
f9a61ef2c9e3b330d9e2e37977b3dd5758a4b853).

From eba4dcbcb63d2da139d3396efe401243946d7fdd Mon Sep 17 00:00:00 2001
From: Cristian Patrascu <[email protected]>
Date: Wed, 6 Jul 2011 12:30:15 +0300
Subject: [PATCH] systemd cgroups limits

---
 src/cgroup-util.c   |   19 ++++++
 src/cgroup-util.h   |    2 +
 src/cgroup.c        |  112 ++++++++++++++++++++++++++++++++++++
 src/cgroup.h        |    6 ++
 src/dbus-common.c   |   13 ++++
 src/dbus-common.h   |    1 +
 src/dbus-execute.c  |   24 ++++++++
 src/dbus-execute.h  |   15 ++++-
 src/execute.c       |  158 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 src/execute.h       |   44 ++++++++++++++
 src/load-fragment.c |   56 ++++++++++++++++++-
 src/service.c       |   34 +++++++++++
 src/systemctl.c     |    8 +++
 13 files changed, 487 insertions(+), 5 deletions(-)

diff --git a/src/cgroup-util.c b/src/cgroup-util.c
index 090573b..39acaf6 100644
--- a/src/cgroup-util.c
+++ b/src/cgroup-util.c
@@ -1001,3 +1001,22 @@ int cg_get_user_path(char **path) {
         *path = p;
         return 0;
 }
+
+/* Set a value in a cgroup controller file */
+int cg_set_value(const char * controller, const char * cg_path, const char * file, char * value) {
+    int r;
+    char * file_path;
+
+    if (cg_path == NULL || controller == NULL || file == NULL || value == NULL) {
+        r = -ENXIO;
+        return r;
+    }
+
+    r = cg_get_path(controller, cg_path, file, &file_path);
+    if (r < 0) return r;
+
+    r = write_one_line_file(file_path, value);
+    if (r < 0) return r;
+
+    return 0;
+}
diff --git a/src/cgroup-util.h b/src/cgroup-util.h
index d142af3..289e4da 100644
--- a/src/cgroup-util.h
+++ b/src/cgroup-util.h
@@ -69,4 +69,6 @@ int cg_is_empty_recursive(const char *controller, const char *path, bool ignore_
 
 int cg_get_user_path(char **path);
 
+int cg_set_value(const char * controller, const char * cg_path, const char * file, char * value);
+
 #endif
diff --git a/src/cgroup.c b/src/cgroup.c
index 0c6f20d..749641a 100644
--- a/src/cgroup.c
+++ b/src/cgroup.c
@@ -469,3 +469,115 @@ pid_t cgroup_bonding_search_main_pid_list(CGroupBonding *first) {
         return 0;
 
 }
+
+/* Sets memory.limit_in_bytes in memory controller of cgroup */
+int cgroup_bonding_set_memory_limit_hard(CGroupBonding *first, const long long in_mem_limit_bytes) {
+        CGroupBonding *b = NULL;
+        int r;
+        const char * mem_ctl = "memory";
+        const char * mem_limit_file_name = "memory.limit_in_bytes";
+        char mem_limit_val[64];
+
+        b = cgroup_bonding_find_list(first, mem_ctl);
+        if (NULL == b) {
+            r = -ENXIO;
+            return r;
+        }
+
+        snprintf(mem_limit_val, sizeof(mem_limit_val),"%lld\n", in_mem_limit_bytes);
+
+        r = cg_set_value(mem_ctl, b->path, mem_limit_file_name, mem_limit_val);
+        if (r < 0) return r;
+
+        return 0;
+}
+
+/* Sets memory.soft_limit_in_bytes in memory controller of cgroup */
+int cgroup_bonding_set_memory_limit_soft(CGroupBonding *first, const long long in_mem_limit_bytes) {
+        CGroupBonding *b = NULL;
+        int r;
+        const char * mem_ctl = "memory";
+        const char * mem_soft_limit_file_name = "memory.soft_limit_in_bytes";
+        char mem_soft_limit_val[64];
+
+        b = cgroup_bonding_find_list(first, mem_ctl);
+        if (NULL == b) {
+            r = -ENXIO;
+            return r;
+        }
+
+        snprintf(mem_soft_limit_val, sizeof(mem_soft_limit_val),"%lld\n", in_mem_limit_bytes);
+
+        r = cg_set_value(mem_ctl, b->path, mem_soft_limit_file_name, mem_soft_limit_val);
+        if (r < 0) return r;
+
+        return 0;
+}
+
+/* Sets blkio.throttle.read_bps_device in blkio controller of cgroup
+   limits a disk read bandwidth (bytes per second) */
+int cgroup_bonding_set_blkio_read_limit(CGroupBonding *first, const char * in_disk, const long long in_limit) {
+        CGroupBonding *b = NULL;
+        int r;
+        const char * ctl = "blkio";
+        const char * limit_file_name = "blkio.throttle.read_bps_device";
+        char limit_val[64];
+
+        b = cgroup_bonding_find_list(first, ctl);
+        if (NULL == b) {
+            r = -ENXIO;
+            return r;
+        }
+
+        snprintf(limit_val, sizeof(limit_val),"%s %lld\n", in_disk, in_limit);
+
+        r = cg_set_value(ctl, b->path, limit_file_name, limit_val);
+        if (r < 0) return r;
+
+        return 0;
+}
+
+/* Sets blkio.throttle.write_bps_device in blkio controller of cgroup
+   limits a disk write bandwidth (bytes per second) */
+int cgroup_bonding_set_blkio_write_limit(CGroupBonding *first, const char * in_disk, const long long in_limit) {
+        CGroupBonding *b = NULL;
+        int r;
+        const char * ctl = "blkio";
+        const char * limit_file_name = "blkio.throttle.write_bps_device";
+        char limit_val[64];
+
+        b = cgroup_bonding_find_list(first, ctl);
+        if (NULL == b) {
+            r = -ENXIO;
+            return r;
+        }
+
+        snprintf(limit_val, sizeof(limit_val),"%s %lld\n", in_disk, in_limit);
+
+        r = cg_set_value(ctl, b->path, limit_file_name, limit_val);
+        if (r < 0) return r;
+
+        return 0;
+}
+
+/* Sets cpu.shares in cpu controller of cgroup */
+int cgroup_bonding_set_cpu_shares(CGroupBonding *first, const int in_cpu_shares) {
+        CGroupBonding *b = NULL;
+        int r;
+        const char * cpu_ctl = "cpu";
+        const char * cpu_shares_file_name = "cpu.shares";
+        char cpu_shares_val[32];
+
+        b = cgroup_bonding_find_list(first, cpu_ctl);
+        if (NULL == b) {
+            r = -ENXIO;
+            return r;
+        }
+
+        snprintf(cpu_shares_val, sizeof(cpu_shares_val),"%d\n", in_cpu_shares);
+
+        r = cg_set_value(cpu_ctl, b->path, cpu_shares_file_name, cpu_shares_val);
+        if (r < 0) return r;
+
+        return 0;
+}
diff --git a/src/cgroup.h b/src/cgroup.h
index c6dff43..5ac7d8f 100644
--- a/src/cgroup.h
+++ b/src/cgroup.h
@@ -75,6 +75,12 @@ char *cgroup_bonding_to_string(CGroupBonding *b);
 pid_t cgroup_bonding_search_main_pid(CGroupBonding *b);
 pid_t cgroup_bonding_search_main_pid_list(CGroupBonding *b);
 
+int cgroup_bonding_set_memory_limit_hard(CGroupBonding *first, const long long in_mem_limit_bytes);
+int cgroup_bonding_set_memory_limit_soft(CGroupBonding *first, const long long in_mem_limit_bytes);
+int cgroup_bonding_set_cpu_shares(CGroupBonding *first, const int in_cpu_shares);
+int cgroup_bonding_set_blkio_read_limit(CGroupBonding *first, const char * in_disk, const long long in_limit);
+int cgroup_bonding_set_blkio_write_limit(CGroupBonding *first, const char * in_disk, const long long in_limit);
+
 #include "manager.h"
 
 int manager_setup_cgroup(Manager *m);
diff --git a/src/dbus-common.c b/src/dbus-common.c
index 5db077b..b4aebfe 100644
--- a/src/dbus-common.c
+++ b/src/dbus-common.c
@@ -542,6 +542,19 @@ int bus_property_append_int32(DBusMessageIter *i, const char *property, void *da
         return 0;
 }
 
+int bus_property_append_int64(DBusMessageIter *i, const char *property, void *data) {
+        assert(i);
+        assert(property);
+        assert(data);
+
+        assert_cc(sizeof(int64_t) == sizeof(long long));
+
+        if (!dbus_message_iter_append_basic(i, DBUS_TYPE_INT64, data))
+                return -ENOMEM;
+
+        return 0;
+}
+
 int bus_property_append_size(DBusMessageIter *i, const char *property, void *data) {
         uint64_t u;
 
diff --git a/src/dbus-common.h b/src/dbus-common.h
index a88cb13..7dc1f17 100644
--- a/src/dbus-common.h
+++ b/src/dbus-common.h
@@ -121,6 +121,7 @@ int bus_property_append_string(DBusMessageIter *i, const char *property, void *d
 int bus_property_append_strv(DBusMessageIter *i, const char *property, void *data);
 int bus_property_append_bool(DBusMessageIter *i, const char *property, void *data);
 int bus_property_append_int32(DBusMessageIter *i, const char *property, void *data);
+int bus_property_append_int64(DBusMessageIter *i, const char *property, void *data);
 int bus_property_append_uint32(DBusMessageIter *i, const char *property, void *data);
 int bus_property_append_uint64(DBusMessageIter *i, const char *property, void *data);
 int bus_property_append_size(DBusMessageIter *i, const char *property, void *data);
diff --git a/src/dbus-execute.c b/src/dbus-execute.c
index 6ceffc5..09b37a3 100644
--- a/src/dbus-execute.c
+++ b/src/dbus-execute.c
@@ -352,3 +352,27 @@ int bus_execute_append_command(DBusMessageIter *i, const char *property, void *d
 
         return 0;
 }
+
+int bus_execute_append_hdd_limit(DBusMessageIter *it, const char *property, const struct hdd_cgroup_limit *hdd_cg_limit) {
+    char ** str_list = NULL;
+    int i;
+
+    assert(hdd_cg_limit);
+    assert(it);
+    assert(property);
+
+    for (i=0; i<MAX_HDD_LIMIT_DISKS; i++) {
+        char tmp[25];
+
+        if (streq(hdd_cg_limit[i].disk, ""))
+            break;
+
+        sprintf(tmp, "%s %lld", hdd_cg_limit[i].disk, hdd_cg_limit[i].limit_bps);
+        str_list = strv_append(str_list, tmp);
+    }
+
+    if (str_list == NULL)
+        str_list = strv_append(str_list, "");
+
+    return bus_property_append_strv(it, property, str_list);
+}
diff --git a/src/dbus-execute.h b/src/dbus-execute.h
index 56c5bcd..999f5a7 100644
--- a/src/dbus-execute.h
+++ b/src/dbus-execute.h
@@ -91,7 +91,12 @@
         "  <property name=\"SameProcessGroup\" type=\"b\" access=\"read\"/>\n" \
         "  <property name=\"KillMode\" type=\"s\" access=\"read\"/>\n"  \
         "  <property name=\"KillSignal\" type=\"i\" access=\"read\"/>\n" \
-        "  <property name=\"UtmpIdentifier\" type=\"s\" access=\"read\"/>\n"
+        "  <property name=\"UtmpIdentifier\" type=\"s\" access=\"read\"/>\n" \
+        "  <property name=\"MemoryHardLimit\" type=\"x\" access=\"readwrite\"/>\n" \
+        "  <property name=\"MemoryLimit\" type=\"x\" access=\"readwrite\"/>\n" \
+        "  <property name=\"CPUShares\" type=\"i\" access=\"readwrite\"/>\n" \
+        "  <property name=\"HddReadLimit\" type=\"as\" access=\"readwrite\"/>\n" \
+        "  <property name=\"HddWriteLimit\" type=\"as\" access=\"readwrite\"/>\n"
 
 #define BUS_EXEC_COMMAND_INTERFACE(name)                             \
         "  <property name=\"" name "\" type=\"a(sasbttuii)\" access=\"read\"/>\n"
@@ -153,7 +158,12 @@
         { interface, "SameProcessGroup",              bus_property_append_bool,   "b",     &(context).same_pgrp                    }, \
         { interface, "KillMode",                      bus_execute_append_kill_mode, "s",   &(context).kill_mode                    }, \
         { interface, "KillSignal",                    bus_property_append_int,    "i",     &(context).kill_signal                  }, \
-        { interface, "UtmpIdentifier",                bus_property_append_string, "s",     (context).utmp_id                       }
+        { interface, "UtmpIdentifier",                bus_property_append_string, "s",     (context).utmp_id                       }, \
+        { interface, "MemoryHardLimit",               bus_property_append_int64,  "x",     &(context).cgroup_limits.memory_hard_limit_in_bytes }, \
+        { interface, "MemoryLimit",                   bus_property_append_int64,  "x",     &(context).cgroup_limits.memory_limit_in_bytes      }, \
+        { interface, "CPUShares",                     bus_property_append_int32,  "i",     &(context).cgroup_limits.cpu_shares                 }, \
+        { interface, "HddReadLimit",                  bus_execute_append_hdd_limit, "as",  (context).cgroup_limits.hdd_read_limit              }, \
+        { interface, "HddWriteLimit",                 bus_execute_append_hdd_limit, "as",  (context).cgroup_limits.hdd_write_limit             }
 
 #define BUS_EXEC_STATUS_PROPERTIES(interface, estatus, prefix)           \
         { interface, prefix "StartTimestamp",         bus_property_append_usec,   "t",     &(estatus).start_timestamp.realtime     }, \
@@ -182,5 +192,6 @@ int bus_execute_append_rlimits(DBusMessageIter *i, const char *property, void *d
 int bus_execute_append_command(DBusMessageIter *u, const char *property, void *data);
 int bus_execute_append_kill_mode(DBusMessageIter *i, const char *property, void *data);
 int bus_execute_append_env_files(DBusMessageIter *i, const char *property, void *data);
+int bus_execute_append_hdd_limit(DBusMessageIter *it, const char *property, const struct hdd_cgroup_limit *hdd_cg_limit);
 
 #endif
diff --git a/src/execute.c b/src/execute.c
index a62f9db..418a5ab 100644
--- a/src/execute.c
+++ b/src/execute.c
@@ -979,6 +979,156 @@ finish:
         return r;
 }
 
+/* Applies cgroup limits */
+static void exec_apply_cg_limits(const CGroupLimits *cgroup_limits, struct CGroupBonding * cgroup_bondings) {
+    int i;
+
+    assert(cgroup_limits);
+    assert(cgroup_bondings);
+
+    if (cgroup_limits->cpu_shares > 0)
+        cgroup_bonding_set_cpu_shares(cgroup_bondings, cgroup_limits->cpu_shares);
+    if (cgroup_limits->memory_hard_limit_in_bytes > 0LL)
+        cgroup_bonding_set_memory_limit_hard(cgroup_bondings, cgroup_limits->memory_hard_limit_in_bytes);
+    if (cgroup_limits->memory_limit_in_bytes > 0LL)
+    	 cgroup_bonding_set_memory_limit_soft(cgroup_bondings, cgroup_limits->memory_limit_in_bytes);
+
+    for (i = 0; i<MAX_HDD_LIMIT_DISKS; i++) {
+        if (streq(cgroup_limits->hdd_read_limit[i].disk,""))
+            break;
+
+        if (cgroup_limits->hdd_read_limit[i].limit_bps > 0LL)
+            cgroup_bonding_set_blkio_read_limit(cgroup_bondings, cgroup_limits->hdd_read_limit[i].disk, cgroup_limits->hdd_read_limit[i].limit_bps);
+    }
+
+    for (i = 0; i<MAX_HDD_LIMIT_DISKS; i++) {
+        if (streq(cgroup_limits->hdd_write_limit[i].disk,""))
+            break;
+
+        if (cgroup_limits->hdd_write_limit[i].limit_bps > 0LL)
+            cgroup_bonding_set_blkio_write_limit(cgroup_bondings, cgroup_limits->hdd_write_limit[i].disk, cgroup_limits->hdd_write_limit[i].limit_bps);
+    }
+}
+
+/* Serialize hdd cgroup limit to string */
+void exec_hdd_lim_serialize(const struct hdd_cgroup_limit * hdd_lim, char ** out_hdd_lim_ser) {
+    int i;
+
+    char * out_str = NULL;
+
+    assert(hdd_lim);
+
+    for (i=0; i<MAX_HDD_LIMIT_DISKS; i++) {
+        char tmp[30];
+
+        if (streq(hdd_lim[i].disk, ""))
+            break;
+
+        sprintf(tmp, "%s %lld ; ", hdd_lim[i].disk, hdd_lim[i].limit_bps);
+        out_str = strappend(out_str, tmp);
+    }
+
+    *out_hdd_lim_ser = out_str;
+}
+
+/* Sets a hdd limit value coresponding to given disk,
+ * returns -1 if error, 0 if set (new or different limit),
+ * 1 if not set (same limit) */
+static int exec_hdd_cgroup_limit_set_internal_value(struct hdd_cgroup_limit * hdd_lim, const char * disk, const long long limit) {
+        int first_empty = -1;
+        int i;
+
+        int valid_limit = (limit <= 0 ? 0 : limit);
+
+        for (i=0; i<MAX_HDD_LIMIT_DISKS; i++) {
+                if (streq(hdd_lim[i].disk, disk)) {
+                        if (hdd_lim[i].limit_bps != valid_limit) {
+                                hdd_lim[i].limit_bps = valid_limit;
+                                return 0;
+                        }
+                        return 1;
+                }
+
+                if (first_empty == -1 &&
+                        streq(hdd_lim[i].disk, "")) {
+                        first_empty = i;
+                }
+        }
+
+        if (first_empty > -1) {
+                strcpy(hdd_lim[first_empty].disk, disk);
+                hdd_lim[first_empty].limit_bps = valid_limit;
+                return 0;
+        }
+
+        return -1;
+}
+
+/* Parse and set hdd read/write limits from string (';' delimits another disk limit)
+   ex: "8:0 1500000 ; 7:0 2500000" */
+int exec_parse_hdd_cgroup_limit(const char * str_value, struct hdd_cgroup_limit * hdd_cg_limit) {
+        int r = 0;
+        char *tmp_str;
+        char *w;
+        char *disk;
+        long long disk_limit;
+        int k;
+        int i;
+
+        assert(str_value);
+        assert(hdd_cg_limit);
+
+        if (streq(str_value,strnull(NULL))) {
+            for (i=0; i<MAX_HDD_LIMIT_DISKS; i++) {
+                if (streq(hdd_cg_limit[i].disk, ""))
+                    break;
+
+                strcpy(hdd_cg_limit[i].disk, "");
+                hdd_cg_limit[i].limit_bps = 0;
+            }
+            return r;
+        }
+
+        tmp_str = strdup(str_value);
+
+        k = 0;
+        w = strtok(tmp_str, WHITESPACE);
+        while (w != NULL) {
+                /* Delimits another disk limit */
+                if (strcmp(w, ";") == 0) {
+                        w = strtok(NULL, WHITESPACE);
+                        continue;
+                }
+
+                /* Disk specifier (1st word) */
+                if (k % 2 == 0) {
+                        disk = w;
+                }
+                /* limit value for disk (2nd word) */
+                else {
+                        if (safe_atolli(w, &disk_limit) < 0) {
+                                r = -ENOMEM;
+                                goto free_res;
+                        }
+
+                        /* set the internal values */
+                        if ((r = exec_hdd_cgroup_limit_set_internal_value(hdd_cg_limit, disk, disk_limit)) < 0) {
+                                r = -ENOMEM;
+                                goto free_res;
+                        }
+                }
+
+                k++;
+                w = strtok(NULL, WHITESPACE);
+        }
+
+free_res:
+        if (tmp_str)
+                free(tmp_str);
+
+        return r;
+}
+
 int exec_spawn(ExecCommand *command,
                char **argv,
                const ExecContext *context,
@@ -1160,11 +1310,13 @@ int exec_spawn(ExecCommand *command,
                         goto fail_child;
                 }
 
-                if (cgroup_bondings)
+                if (cgroup_bondings) {
                         if (cgroup_bonding_install_list(cgroup_bondings, 0) < 0) {
                                 r = EXIT_CGROUP;
                                 goto fail_child;
                         }
+                        exec_apply_cg_limits(&context->cgroup_limits, cgroup_bondings);
+                }
 
                 if (context->oom_score_adjust_set) {
                         char t[16];
@@ -1436,8 +1588,10 @@ int exec_spawn(ExecCommand *command,
          * outside of the cgroup) and in the parent (so that we can be
          * sure that when we kill the cgroup the process will be
          * killed too). */
-        if (cgroup_bondings)
+        if (cgroup_bondings) {
                 cgroup_bonding_install_list(cgroup_bondings, pid);
+                exec_apply_cg_limits(&context->cgroup_limits, cgroup_bondings);
+        }
 
         log_debug("Forked %s as %lu", command->path, (unsigned long) pid);
 
diff --git a/src/execute.h b/src/execute.h
index 55bae24..bc62187 100644
--- a/src/execute.h
+++ b/src/execute.h
@@ -96,6 +96,44 @@ struct ExecCommand {
         bool ignore;
 };
 
+/* 10 physical disks sould be enough
+   (limit is per whole disk, not per partition!) */
+#define MAX_HDD_LIMIT_DISKS 10
+
+struct hdd_cgroup_limit {
+        /* The disk to be limited (r/w).
+           It is specified in format "<major>:<minor>"
+           ex.: "8:0" (6 chars should be enough) */
+        char disk[6];
+
+        /* Limit of disk read/write bytes per second */
+        long long limit_bps;
+};
+
+typedef struct _CGroupLimits {
+    /* For "MemoryHardLimit" option - if set, searches
+     * the cgroup in "memory" controller and sets
+     * "memory.limit_in_bytes" with specified value. */
+    long long memory_hard_limit_in_bytes;
+
+    /* For "MemoryLimit" option - if set, searches
+     * the cgroup in "memory" controller and sets
+     * "memory.soft_limit_in_bytes" with specified value. */
+    long long memory_limit_in_bytes;
+
+    /* For "CPUShares" option - if set, searches
+     * the cgroup in "cpu" controller and sets
+     * "cpu.shares" with specified value. */
+    int cpu_shares;
+
+    /* hdd read limit (bytes per sec.), for max. 10 disks */
+    struct hdd_cgroup_limit hdd_read_limit[MAX_HDD_LIMIT_DISKS];
+
+    /* hdd write limit (bytes per sec.), for max. 10 disks */
+    struct hdd_cgroup_limit hdd_write_limit[MAX_HDD_LIMIT_DISKS];
+
+} CGroupLimits;
+
 struct ExecContext {
         char **environment;
         char **environment_files;
@@ -127,6 +165,9 @@ struct ExecContext {
         bool tty_vhangup;
         bool tty_vt_disallocate;
 
+        /* Some usefull cgroup limits for controllers: cpu, memory, blkio */
+        CGroupLimits cgroup_limits;
+
         /* Since resolving these names might might involve socket
          * connections and we don't want to deadlock ourselves these
          * names are resolved on execution only and in the child
@@ -222,4 +263,7 @@ KillMode kill_mode_from_string(const char *s);
 const char *kill_who_to_string(KillWho k);
 KillWho kill_who_from_string(const char *s);
 
+int exec_parse_hdd_cgroup_limit(const char * str_value, struct hdd_cgroup_limit * hdd_cg_limit);
+void exec_hdd_lim_serialize(const struct hdd_cgroup_limit * hdd_lim, char ** out_hdd_lim_ser);
+
 #endif
diff --git a/src/load-fragment.c b/src/load-fragment.c
index 56eaed9..64eac4d 100644
--- a/src/load-fragment.c
+++ b/src/load-fragment.c
@@ -60,6 +60,55 @@ static int config_parse_warn_compat(
 }
 #endif
 
+static int config_parse_hdd_lim(
+                const char *filename,
+                unsigned line,
+                const char *section,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+        struct hdd_cgroup_limit * hdd_cg_limit = data;
+        int r = 0;
+
+        assert(rvalue);
+        assert(hdd_cg_limit);
+
+        r = exec_parse_hdd_cgroup_limit(rvalue, hdd_cg_limit);
+
+        if (r<0)
+                log_error("[%s:%u] Failed to parse cgroup hdd limit, ignoring: %s. ", filename, line, rvalue);
+
+        return 0;
+}
+
+static int config_parse_int64(
+                const char *filename,
+                unsigned line,
+                const char *section,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+        long long *mem_lim = data;
+        long long u;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        if (safe_atoi64(rvalue, &u) < 0) {
+                log_error("[%s:%u] Failed to parse int64 value, ignoring: %s", filename, line, rvalue);
+                return 0;
+        }
+
+        *mem_lim = u;
+        return 0;
+}
+
 static int config_parse_deps(
                 const char *filename,
                 unsigned line,
@@ -1908,7 +1957,12 @@ static int load_from_path(Unit *u, const char *path) {
                 { "KillMode",               config_parse_kill_mode,       0, &(context).kill_mode,                            section   }, \
                 { "KillSignal",             config_parse_kill_signal,     0, &(context).kill_signal,                          section   }, \
                 { "SendSIGKILL",            config_parse_bool,            0, &(context).send_sigkill,                         section   }, \
-                { "UtmpIdentifier",         config_parse_string_printf,   0, &(context).utmp_id,                              section   }
+                { "UtmpIdentifier",         config_parse_string_printf,   0, &(context).utmp_id,                              section   }, \
+                { "MemoryHardLimit",        config_parse_int64,           0, &(context).cgroup_limits.memory_hard_limit_in_bytes, section   }, \
+                { "MemoryLimit",            config_parse_int64,           0, &(context).cgroup_limits.memory_limit_in_bytes,      section   }, \
+                { "CPUShares",              config_parse_int,             0, &(context).cgroup_limits.cpu_shares,                 section   }, \
+                { "HddReadLimit",           config_parse_hdd_lim,         0, (context).cgroup_limits.hdd_read_limit,              section   }, \
+                { "HddWriteLimit",          config_parse_hdd_lim,         0, (context).cgroup_limits.hdd_write_limit,             section   }
 
         const ConfigItem items[] = {
                 { "Names",                  config_parse_names,           0, u,                                               "Unit"    },
diff --git a/src/service.c b/src/service.c
index d59c4cb..2e96857 100644
--- a/src/service.c
+++ b/src/service.c
@@ -2411,6 +2411,27 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) {
                 }
         }
 
+        /* only if service is running, serialize cgroup limits state */
+        if (s->state == SERVICE_RUNNING) {
+                char * hdd_limit_serialized = NULL;
+
+                unit_serialize_item_format(
+                        u, f, "cgroup-limits","%lld %lld %d",
+                        s->exec_context.cgroup_limits.memory_hard_limit_in_bytes ,
+                        s->exec_context.cgroup_limits.memory_limit_in_bytes ,
+                        s->exec_context.cgroup_limits.cpu_shares);
+
+                exec_hdd_lim_serialize(s->exec_context.cgroup_limits.hdd_read_limit, &hdd_limit_serialized);
+                unit_serialize_item_format(u, f, "hdd_read_limit", "%s", hdd_limit_serialized);
+                if (hdd_limit_serialized)
+                        free(hdd_limit_serialized);
+
+                exec_hdd_lim_serialize(s->exec_context.cgroup_limits.hdd_write_limit, &hdd_limit_serialized);
+                unit_serialize_item_format(u, f, "hdd_write_limit", "%s", hdd_limit_serialized);
+                if (hdd_limit_serialized)
+                        free(hdd_limit_serialized);
+        }
+
         return 0;
 }
 
@@ -2510,6 +2531,19 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
                 dual_timestamp_deserialize(value, &s->main_exec_status.start_timestamp);
         else if (streq(key, "main-exec-status-exit"))
                 dual_timestamp_deserialize(value, &s->main_exec_status.exit_timestamp);
+        else if (
+                    s->deserialized_state == SERVICE_RUNNING &&
+                    streq(key, "cgroup-limits") &&
+                    sscanf(
+                            value, "%lld %lld %d",
+                            &s->exec_context.cgroup_limits.memory_hard_limit_in_bytes ,
+                            &s->exec_context.cgroup_limits.memory_limit_in_bytes ,
+                            &s->exec_context.cgroup_limits.cpu_shares) != 3)
+                        log_error("Failed to parse cgroup limit '%s' for deserializing", value);
+        else if (s->deserialized_state == SERVICE_RUNNING && streq(key, "hdd_read_limit"))
+                exec_parse_hdd_cgroup_limit(value, s->exec_context.cgroup_limits.hdd_read_limit);
+        else if (s->deserialized_state == SERVICE_RUNNING && streq(key, "hdd_write_limit"))
+                exec_parse_hdd_cgroup_limit(value, s->exec_context.cgroup_limits.hdd_write_limit);
         else
                 log_debug("Unknown serialization key '%s'", key);
 
diff --git a/src/systemctl.c b/src/systemctl.c
index 08c7fab..11792cb 100644
--- a/src/systemctl.c
+++ b/src/systemctl.c
@@ -2316,6 +2316,14 @@ static int print_property(const char *name, DBusMessageIter *iter) {
                 return 0;
         }
 
+        case DBUS_TYPE_INT64: {
+                int64_t i;
+                dbus_message_iter_get_basic(iter, &i);
+
+                printf("%s=%lld\n", name, (long long) i);
+                return 0;
+        }
+
         case DBUS_TYPE_DOUBLE: {
                 double d;
                 dbus_message_iter_get_basic(iter, &d);
-- 
1.7.0.4

_______________________________________________
systemd-devel mailing list
[email protected]
http://lists.freedesktop.org/mailman/listinfo/systemd-devel

Reply via email to