[lxc-devel] [PATCH RFC] Allow a few basic limits to be set at creation / runtime
Serge Hallyn
serge.hallyn at ubuntu.com
Wed May 28 14:51:55 UTC 2014
Quoting Dwight Engen (dwight.engen at oracle.com):
> 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>
would lxc-cgctl or lxc-limit be a better name, or do you intend to generalize
it to other container controls later?
Acked-by: Serge E. Hallyn <serge.hallyn at ubuntu.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
>
> _______________________________________________
> lxc-devel mailing list
> lxc-devel at lists.linuxcontainers.org
> http://lists.linuxcontainers.org/listinfo/lxc-devel
More information about the lxc-devel
mailing list