I've changed the options, it was indeed confusing, sorry for that. 
Very good observation, Thank you!

________________________________________
From: Kok, Auke-jan H [[email protected]]
Sent: Thursday, July 14, 2011 12:41 AM
To: Patrascu, Cristian
Cc: [email protected]; Zaharia, Adrian; [email protected]
Subject: Re: [systemd-devel] [PATCH] systemd-cgroup-limits.patch

On Wed, Jul 13, 2011 at 12:35 AM, Patrascu, Cristian
<[email protected]> wrote:
> 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" ;

nitpick, but still

you define MemoryHardLimit to set limit_in_bytes
you define MemoryLimit to set soft_limit_in_bytes

that's confusing, since "MemoryLimit" does not set "limit_in_bytes".

Why not just 1:1 map this to cgroup naming instead:

MemoryLimit -> limit_in_bytes
MemorySoftLimit -> soft_limit_in_bytes

seems so trivial to do that instead...

Auke
From 8d47086086689645e0a02ca19dfcc56dc5f6c2e3 Mon Sep 17 00:00:00 2001
From: Cristian Patrascu <[email protected]>
Date: Wed, 6 Jul 2011 12:30:15 +0300
Subject: [PATCH] systemd cgroup 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..3a8d112 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=\"MemoryLimit\" type=\"x\" access=\"readwrite\"/>\n" \
+        "  <property name=\"MemorySoftLimit\" 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, "MemoryLimit",                   bus_property_append_int64,  "x",     &(context).cgroup_limits.memory_limit_in_bytes }, \
+        { interface, "MemorySoftLimit",               bus_property_append_int64,  "x",     &(context).cgroup_limits.memory_soft_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..4be271d 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_limit_in_bytes > 0LL)
+        cgroup_bonding_set_memory_limit_hard(cgroup_bondings, cgroup_limits->memory_limit_in_bytes);
+    if (cgroup_limits->memory_soft_limit_in_bytes > 0LL)
+         cgroup_bonding_set_memory_limit_soft(cgroup_bondings, cgroup_limits->memory_soft_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..dac2f29 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 "MemoryLimit" option - if set, searches
+     * the cgroup in "memory" controller and sets
+     * "memory.limit_in_bytes" with specified value. */
+    long long memory_limit_in_bytes;
+
+    /* For "MemorySoftLimit" option - if set, searches
+     * the cgroup in "memory" controller and sets
+     * "memory.soft_limit_in_bytes" with specified value. */
+    long long memory_soft_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..45b893a 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   }, \
+                { "MemoryLimit",            config_parse_int64,           0, &(context).cgroup_limits.memory_limit_in_bytes, section   }, \
+                { "MemorySoftLimit",        config_parse_int64,           0, &(context).cgroup_limits.memory_soft_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..3d83e9e 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_limit_in_bytes ,
+                        s->exec_context.cgroup_limits.memory_soft_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_limit_in_bytes ,
+                            &s->exec_context.cgroup_limits.memory_soft_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