[lxc-devel] [PATCH RFC] Allow a few basic limits to be set at creation / runtime
Dwight Engen
dwight.engen at oracle.com
Tue May 27 19:37:19 UTC 2014
Currently, a user has to read kernel/Documentation/cgroups/* to know what
is available and then apply these using lxc-cgroups to set runtime limits,
or hand edit the configuration file after creating a container to set them
permanently.
This change covers the most common use cases (cpu, memory) by allowing the
user to specify them to lxc-create, and introduces a new command lxc-ctl
which allows setting the limits at run-time and/or saving them to the
containers' config file with the --save option. This functionality is
analogous to that found in vzctl, or Solaris zonecfg.
Introduce config_cgroup_find to find an already set cgroup item, and
use it in config_cgroup() to replace a value instead of appending
another entry. This means that (in for example lxc-clone) only the last
duplicate entry will now be written out.
Signed-off-by: Dwight Engen <dwight.engen at oracle.com>
---
.gitignore | 1 +
src/lxc/Makefile.am | 2 +
src/lxc/arguments.h | 10 ++-
src/lxc/confile.c | 175 ++++++++++++++++++++++++++++++++++++++++++---------
src/lxc/confile.h | 18 ++++++
src/lxc/lxc_create.c | 19 +++++-
src/lxc/lxc_ctl.c | 133 +++++++++++++++++++++++++++++++++++++++
7 files changed, 326 insertions(+), 32 deletions(-)
create mode 100644 src/lxc/lxc_ctl.c
diff --git a/.gitignore b/.gitignore
index 8145f81..b90a0e2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -52,6 +52,7 @@ src/lxc/lxc-clone
src/lxc/lxc-console
src/lxc/lxc-config
src/lxc/lxc-create
+src/lxc/lxc-ctl
src/lxc/lxc-destroy
src/lxc/lxc-execute
src/lxc/lxc-freeze
diff --git a/src/lxc/Makefile.am b/src/lxc/Makefile.am
index ab8a46e..d48ebf6 100644
--- a/src/lxc/Makefile.am
+++ b/src/lxc/Makefile.am
@@ -186,6 +186,7 @@ bin_PROGRAMS = \
lxc-config \
lxc-console \
lxc-create \
+ lxc-ctl \
lxc-destroy \
lxc-execute \
lxc-freeze \
@@ -229,6 +230,7 @@ lxc_unfreeze_SOURCES = lxc_unfreeze.c
lxc_unshare_SOURCES = lxc_unshare.c
lxc_wait_SOURCES = lxc_wait.c
lxc_create_SOURCES = lxc_create.c
+lxc_ctl_SOURCES = lxc_ctl.c
lxc_snapshot_SOURCES = lxc_snapshot.c
lxc_usernsexec_SOURCES = lxc_usernsexec.c
lxc_user_nic_SOURCES = lxc_user_nic.c network.c network.h
diff --git a/src/lxc/arguments.h b/src/lxc/arguments.h
index cc85f86..0e963dd 100644
--- a/src/lxc/arguments.h
+++ b/src/lxc/arguments.h
@@ -88,6 +88,9 @@ struct lxc_arguments {
char *lvname, *vgname, *thinpool;
char *zfsroot, *lowerdir, *dir;
+ /* lxc-create/lxc-ctl */
+ char *mem, *cpulimit, *cpuset;
+
/* auto-start */
int all;
int ignore_auto;
@@ -114,8 +117,11 @@ struct lxc_arguments {
{0, 0, 0, 0}
/* option keys for long only options */
-#define OPT_USAGE 0x1000
-#define OPT_VERSION OPT_USAGE-1
+#define OPT_USAGE 0x1000
+#define OPT_VERSION OPT_USAGE-1
+#define OPT_MEM OPT_USAGE-2
+#define OPT_CPULIMIT OPT_USAGE-3
+#define OPT_CPUSET OPT_USAGE-4
extern int lxc_arguments_parse(struct lxc_arguments *args,
int argc, char *const argv[]);
diff --git a/src/lxc/confile.c b/src/lxc/confile.c
index ac05b75..d65789e 100644
--- a/src/lxc/confile.c
+++ b/src/lxc/confile.c
@@ -37,6 +37,7 @@
#include <netinet/in.h>
#include <net/if.h>
+#include "arguments.h"
#include "parse.h"
#include "config.h"
#include "confile.h"
@@ -45,6 +46,7 @@
#include "conf.h"
#include "network.h"
#include "lxcseccomp.h"
+#include "lxccontainer.h"
#if HAVE_SYS_PERSONALITY_H
#include <sys/personality.h>
@@ -1212,13 +1214,27 @@ static int config_stopsignal(const char *key, const char *value,
return 0;
}
+static struct lxc_list *config_cgroup_find(struct lxc_conf *lxc_conf,
+ const char *subkey)
+{
+ struct lxc_list *it;
+
+ lxc_list_for_each(it, &lxc_conf->cgroup) {
+ struct lxc_cgroup *cg = it->elem;
+ if (strcmp(cg->subsystem, subkey) == 0)
+ return it;
+ }
+ return NULL;
+}
+
static int config_cgroup(const char *key, const char *value,
struct lxc_conf *lxc_conf)
{
char *token = "lxc.cgroup.";
- char *subkey;
+ char *subkey, *subsystem, *val;
struct lxc_list *cglist = NULL;
struct lxc_cgroup *cgelem = NULL;
+ bool add = false;
if (!value || strlen(value) == 0)
return lxc_clear_cgroups(lxc_conf, key);
@@ -1236,41 +1252,44 @@ static int config_cgroup(const char *key, const char *value,
subkey += strlen(token);
- cglist = malloc(sizeof(*cglist));
- if (!cglist)
- goto out;
-
- cgelem = malloc(sizeof(*cgelem));
- if (!cgelem)
- goto out;
- memset(cgelem, 0, sizeof(*cgelem));
+ subsystem = strdup(subkey);
+ if (!subsystem)
+ return -1;
+ val = strdup(value);
+ if (!val)
+ goto err1;
- cgelem->subsystem = strdup(subkey);
- cgelem->value = strdup(value);
+ cglist = config_cgroup_find(lxc_conf, subkey);
+ if (cglist) {
+ cgelem = cglist->elem;
+ free(cgelem->subsystem);
+ free(cgelem->value);
+ } else {
+ cglist = malloc(sizeof(*cglist));
+ if (!cglist)
+ goto err2;
- if (!cgelem->subsystem || !cgelem->value)
- goto out;
+ cgelem = malloc(sizeof(*cgelem));
+ if (!cgelem)
+ goto err3;
+ memset(cgelem, 0, sizeof(*cgelem));
+ add = true;
+ }
+ cgelem->subsystem = subsystem;
+ cgelem->value = val;
cglist->elem = cgelem;
- lxc_list_add_tail(&lxc_conf->cgroup, cglist);
-
+ if (add)
+ lxc_list_add_tail(&lxc_conf->cgroup, cglist);
return 0;
-out:
- if (cglist)
- free(cglist);
-
- if (cgelem) {
- if (cgelem->subsystem)
- free(cgelem->subsystem);
-
- if (cgelem->value)
- free(cgelem->value);
-
- free(cgelem);
- }
-
+err3:
+ free(cglist);
+err2:
+ free(val);
+err1:
+ free(subsystem);
return -1;
}
@@ -2402,3 +2421,101 @@ void write_config(FILE *fout, struct lxc_conf *c)
lxc_list_for_each(it, &c->groups)
fprintf(fout, "lxc.group = %s\n", (char *)it->elem);
}
+
+static const struct lxc_ctl lxc_ctl_init[] = {
+ {false, "mem", "memory.limit_in_bytes", NULL},
+ {false, "cpulimit", "cpu.shares", NULL},
+ {false, "cpuset", "cpuset.cpus", NULL}
+};
+
+#define LXC_CTL_CNT (sizeof(lxc_ctl_init)/sizeof(lxc_ctl_init[0]))
+
+struct lxc_ctl *lxc_ctl_new(struct lxc_arguments *args)
+{
+ struct lxc_ctl *ctl;
+ ctl = malloc(sizeof(lxc_ctl_init));
+ if (ctl)
+ memcpy(ctl, lxc_ctl_init, sizeof(lxc_ctl_init));
+ if (args->mem)
+ lxc_ctl_set_value(ctl, "mem", args->mem);
+ if (args->cpuset)
+ lxc_ctl_set_value(ctl, "cpuset", args->cpuset);
+ if (args->cpulimit) {
+ char buf[11];
+ sprintf(buf, "%d", atoi(args->cpulimit) * 1024 / 100);
+ lxc_ctl_set_value(ctl, "cpulimit", buf);
+ }
+
+ return ctl;
+};
+
+void lxc_ctl_free(struct lxc_ctl *lxc_ctl)
+{
+ int i;
+
+ for (i = 0; i < LXC_CTL_CNT; i++) {
+ if (lxc_ctl[i].value)
+ free(lxc_ctl[i].value);
+ }
+ free(lxc_ctl);
+}
+
+void lxc_ctl_set_value(struct lxc_ctl *lxc_ctl, const char *ctl,
+ const char *value)
+{
+ int i;
+
+ for (i = 0; i < LXC_CTL_CNT; i++) {
+ if (strcmp(lxc_ctl[i].ctl, ctl))
+ continue;
+ lxc_ctl[i].set = 1;
+ if (lxc_ctl[i].value)
+ free(lxc_ctl[i].value);
+ lxc_ctl[i].value = strdup(value);
+ break;
+ }
+}
+
+bool lxc_ctl_set_running(struct lxc_ctl *lxc_ctl, struct lxc_container *c)
+{
+ int i;
+
+ for (i = 0; i < LXC_CTL_CNT; i++) {
+ if (!lxc_ctl[i].set)
+ continue;
+
+ if (!c->set_cgroup_item(c, lxc_ctl[i].cgitem, lxc_ctl[i].value)) {
+ ERROR("failed setting %s to %s",
+ lxc_ctl[i].cgitem, lxc_ctl[i].value);
+ return false;
+ }
+ }
+ return true;
+}
+
+bool lxc_ctl_set_config(struct lxc_ctl *lxc_ctl, struct lxc_container *c)
+{
+ char *key;
+ int i, rc;
+ bool did_set = false;
+
+ for (i = 0; i < LXC_CTL_CNT; i++) {
+ if (!lxc_ctl[i].set)
+ continue;
+
+ rc = asprintf(&key, "lxc.cgroup.%s", lxc_ctl[i].cgitem);
+ if (rc < 0)
+ return false;
+ if (config_cgroup(key, lxc_ctl[i].value, c->lxc_conf) != 0) {
+ ERROR("failed setting cfg %s to %s",
+ lxc_ctl[i].cgitem, lxc_ctl[i].value);
+ return false;
+ }
+ free(key);
+ did_set = true;
+ }
+
+ if (did_set && !c->save_config(c, NULL))
+ return false;
+ return true;
+}
diff --git a/src/lxc/confile.h b/src/lxc/confile.h
index 171614a..4e32ede 100644
--- a/src/lxc/confile.h
+++ b/src/lxc/confile.h
@@ -25,10 +25,13 @@
#define __LXC_CONFILE_H
#include <stdio.h>
+#include <stdbool.h>
#include <lxc/attach_options.h>
struct lxc_conf;
struct lxc_list;
+struct lxc_container;
+struct lxc_arguments;
typedef int (*config_cb)(const char *, const char *, struct lxc_conf *);
struct lxc_config_t {
@@ -52,4 +55,19 @@ extern int lxc_fill_elevated_privileges(char *flaglist, int *flags);
extern int lxc_get_config_item(struct lxc_conf *c, const char *key, char *retv, int inlen);
extern int lxc_clear_config_item(struct lxc_conf *c, const char *key);
extern void write_config(FILE *fout, struct lxc_conf *c);
+
+struct lxc_ctl
+{
+ bool set;
+ const char *ctl;
+ const char *cgitem;
+ char *value;
+};
+
+extern struct lxc_ctl *lxc_ctl_new(struct lxc_arguments *args);
+extern void lxc_ctl_free(struct lxc_ctl *lxc_ctl);
+extern void lxc_ctl_set_value(struct lxc_ctl *lxc_ctl, const char *ctl, const char *value);
+extern bool lxc_ctl_set_running(struct lxc_ctl *lxc_ctl, struct lxc_container *c);
+extern bool lxc_ctl_set_config(struct lxc_ctl *lxc_ctl, struct lxc_container *c);
+
#endif
diff --git a/src/lxc/lxc_create.c b/src/lxc/lxc_create.c
index 2cc866a..b8bf312 100644
--- a/src/lxc/lxc_create.c
+++ b/src/lxc/lxc_create.c
@@ -32,6 +32,7 @@
#include "bdev.h"
#include "arguments.h"
#include "utils.h"
+#include "confile.h"
lxc_log_define(lxc_create_ui, lxc);
@@ -81,6 +82,9 @@ static int my_parser(struct lxc_arguments* args, int c, char* arg)
case '4': args->fssize = get_fssize(arg); break;
case '5': args->zfsroot = arg; break;
case '6': args->dir = arg; break;
+ case OPT_CPULIMIT: args->cpulimit = arg; break;
+ case OPT_CPUSET: args->cpuset = arg; break;
+ case OPT_MEM: args->mem = arg; break;
}
return 0;
}
@@ -96,6 +100,9 @@ static const struct option my_longopts[] = {
{"fssize", required_argument, 0, '4'},
{"zfsroot", required_argument, 0, '5'},
{"dir", required_argument, 0, '6'},
+ {"cpulimit", required_argument, 0, OPT_CPULIMIT},
+ {"cpuset", required_argument, 0, OPT_CPUSET},
+ {"mem", required_argument, 0, OPT_MEM},
LXC_COMMON_OPTIONS
};
@@ -154,7 +161,10 @@ Options :\n\
(Default: 1G, default unit: M)\n\
--dir=DIR Place rootfs directory under DIR\n\
--zfsroot=PATH Create zfs under given zfsroot\n\
- (Default: tank/lxc)\n",
+ (Default: tank/lxc)\n\
+ --cpulimit=NUM Percentage of cpu allowed\n\
+ --cpuset=NUM,NUM Which cpus to run on\n\
+ --mem=BYTES Amount of memory (may be suffixed with K,M,G)\n",
.options = my_longopts,
.parser = my_parser,
.checker = NULL,
@@ -190,6 +200,7 @@ int main(int argc, char *argv[])
{
struct lxc_container *c;
struct bdev_specs spec;
+ struct lxc_ctl *ctl;
int flags = 0;
if (lxc_arguments_parse(&my_args, argc, argv))
@@ -221,6 +232,10 @@ int main(int argc, char *argv[])
if (strcmp(my_args.bdevtype, "none") == 0)
my_args.bdevtype = "dir";
+ ctl = lxc_ctl_new(&my_args);
+ if (!ctl)
+ exit(1);
+
if (geteuid()) {
if (mkdir_p(my_args.lxcpath[0], 0755)) {
exit(1);
@@ -276,6 +291,8 @@ int main(int argc, char *argv[])
my_args.bdevtype = NULL;
if (my_args.quiet)
flags = LXC_CREATE_QUIET;
+
+ lxc_ctl_set_config(ctl, c);
if (!c->create(c, my_args.template, my_args.bdevtype, &spec, flags, &argv[optind])) {
ERROR("Error creating container %s", c->name);
lxc_container_put(c);
diff --git a/src/lxc/lxc_ctl.c b/src/lxc/lxc_ctl.c
new file mode 100644
index 0000000..efe82f9
--- /dev/null
+++ b/src/lxc/lxc_ctl.c
@@ -0,0 +1,133 @@
+/*
+ * lxc: linux Container library
+ *
+ * Authors:
+ * Copyright © 2014 Oracle
+ * Dwight Engen <dwight.engen at oracle.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdbool.h>
+#include <sys/types.h>
+
+#include <lxc/lxccontainer.h>
+
+#include "lxc.h"
+#include "log.h"
+#include "utils.h"
+#include "arguments.h"
+#include "confile.h"
+
+lxc_log_define(lxc_ctl_ui, lxc);
+
+static bool save = false;
+
+#define OPT_SAVE OPT_USAGE+1
+
+static int my_parser(struct lxc_arguments *args, int c, char *arg)
+{
+ switch (c) {
+ case OPT_SAVE: save = 1; break;
+ case OPT_CPULIMIT: args->cpulimit = arg; break;
+ case OPT_CPUSET: args->cpuset = arg; break;
+ case OPT_MEM: args->mem = arg; break;
+ }
+ return 0;
+}
+
+static const struct option my_longopts[] = {
+ {"save", no_argument, 0, OPT_SAVE},
+ {"cpulimit", required_argument, 0, OPT_CPULIMIT},
+ {"cpuset", required_argument, 0, OPT_CPUSET},
+ {"mem", required_argument, 0, OPT_MEM},
+ LXC_COMMON_OPTIONS,
+};
+
+static struct lxc_arguments my_args = {
+ .progname = "lxc-ctl",
+ .help = "\
+--name=NAME\n\
+\n\
+lxc-ctl set basic container limits\n\
+\n\
+Options :\n\
+ -n, --name=NAME NAME for name of the container\n\
+ --save Save to config file\n\
+ --cpulimit=NUM Set percentage of cpu allowed\n\
+ --cpuset=NUM,NUM Set which cpus to run on\n\
+ --mem=BYTES Set amount of memory (may be suffixed with K,M,G)",
+ .name = NULL,
+ .options = my_longopts,
+ .parser = my_parser,
+ .checker = NULL,
+};
+
+int main(int argc, char *argv[])
+{
+ struct lxc_container *c;
+ struct lxc_ctl *ctl;
+ int ret = EXIT_FAILURE;
+
+ if (lxc_arguments_parse(&my_args, argc, argv))
+ goto err1;
+
+ if (!my_args.log_file)
+ my_args.log_file = "none";
+
+ if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority,
+ my_args.progname, my_args.quiet, my_args.lxcpath[0]))
+ goto err1;
+ lxc_log_options_no_override();
+
+ c = lxc_container_new(my_args.name, my_args.lxcpath[0]);
+ if (!c)
+ goto err1;
+
+ if (!c->may_control(c)) {
+ ERROR("Insufficent privileges to control %s:%s", my_args.lxcpath[0], my_args.name);
+ goto err2;
+ }
+
+ ctl = lxc_ctl_new(&my_args);
+ if (!ctl)
+ goto err2;
+
+ if (save && !lxc_ctl_set_config(ctl, c)) {
+ ERROR("failed to save config %s:%s", my_args.lxcpath[0], my_args.name);
+ goto err3;
+ }
+
+ if (c->is_running(c)) {
+ if (!lxc_ctl_set_running(ctl, c))
+ goto err3;
+ } else {
+ if (!save) {
+ ERROR("%s:%s is not running", my_args.lxcpath[0], my_args.name);
+ goto err3;
+ }
+ }
+
+ ret = EXIT_SUCCESS;
+
+err3:
+ lxc_ctl_free(ctl);
+err2:
+ lxc_container_put(c);
+err1:
+ return ret;
+}
--
1.9.0
More information about the lxc-devel
mailing list