[Libct] [PATCH 7/7] VZ containers: implement some resources constraints
Alexander Burluka
aburluka at parallels.com
Wed Nov 12 04:40:28 PST 2014
Memory, cpu and blkio limits. Implementation + tests
---
src/cgroups.c | 6 +-
src/include/cgroups.h | 2 +
src/vz.c | 251 ++++++++++++++++++++++++++++++++++++++++-------
test/Makefile | 4 +-
test/vz_cgroup_blkio.c | 112 +++++++++++++++++++++
test/vz_cgroup_cpu.c | 78 +++++++++++++++
test/vz_cgroup_memory.c | 89 +++++++++++++++++
7 files changed, 501 insertions(+), 41 deletions(-)
create mode 100644 test/vz_cgroup_blkio.c
create mode 100644 test/vz_cgroup_cpu.c
create mode 100644 test/vz_cgroup_memory.c
diff --git a/src/cgroups.c b/src/cgroups.c
index d2d8d5b..4d96249 100644
--- a/src/cgroups.c
+++ b/src/cgroups.c
@@ -170,14 +170,14 @@ static struct cg_config *cg_config_alloc(enum ct_controller ctype, char *param,
return cg;
}
-static int config_controller(struct container *ct, enum ct_controller ctype,
+int config_controller(struct container *ct, enum ct_controller ctype,
char *param, char *value)
{
char path[PATH_MAX], *t;
int fd, ret;
t = cgroup_get_path(ctype, path, sizeof(path));
- snprintf(t, sizeof(path) - (t - path), "/%s/%s", ct->name, param);
+ snprintf(t, sizeof(path) - (t - path), "/%s/%s.%s", ct->name, cg_descs[ctype].name, param);
ret = fd = open(path, O_WRONLY);
if (fd >= 0) {
@@ -228,7 +228,7 @@ int local_config_controller(ct_handler_t h, enum ct_controller ctype,
return config_controller(ct, ctype, param, value) ? -LCTERR_CGCONFIG : 0;
}
-static int cgroup_create_one(struct container *ct, struct controller *ctl)
+int cgroup_create_one(struct container *ct, struct controller *ctl)
{
char path[PATH_MAX], *t;
diff --git a/src/include/cgroups.h b/src/include/cgroups.h
index 43639df..8963ffa 100644
--- a/src/include/cgroups.h
+++ b/src/include/cgroups.h
@@ -34,6 +34,7 @@ struct cg_config {
extern struct cg_desc cg_descs[];
+extern int cgroup_create_one(struct container *ct, struct controller *ctl);
extern int cgroups_create(struct container *ct);
extern int cgroups_attach(struct container *ct);
extern void cgroups_destroy(struct container *ct);
@@ -41,6 +42,7 @@ extern void cgroups_free(struct container *ct);
extern int local_add_controller(ct_handler_t h, enum ct_controller ctype);
extern int local_config_controller(ct_handler_t h, enum ct_controller ctype, char *param, char *value);
+extern int config_controller(struct container *ct, enum ct_controller ctype, char *param, char *value);
extern int try_mount_cg(struct container *ct);
diff --git a/src/vz.c b/src/vz.c
index 39f788c..2611bc7 100644
--- a/src/vz.c
+++ b/src/vz.c
@@ -23,6 +23,7 @@
#include <grp.h>
#include <limits.h>
#include <sched.h>
+#include <bc/beancounter.h>
#include "linux-kernel.h"
#include "vz.h"
@@ -43,6 +44,9 @@
#define LINUX_REBOOT_MAGIC1 0xfee1dead
#define LINUX_REBOOT_MAGIC2 672274793
#define LINUX_REBOOT_CMD_POWER_OFF 0x4321FEDC
+#define IOPRIO_CLASS_BE 2
+#define IOPRIO_CLASS_SHIFT 13
+#define IOPRIO_WHO_UBC 1000
typedef enum {
M_HALT,
@@ -156,6 +160,7 @@ static void vz_ct_destroy(ct_handler_t h)
{
struct container *ct = cth2ct(h);
+ cgroups_free(ct);
fs_free(ct);
net_release(ct);
@@ -648,6 +653,206 @@ static int vz_spawn_cb(ct_handler_t h, ct_process_desc_t p, int (*cb)(void *), v
return -1;
}
+static int ublimit_mem_syscall(unsigned int veid, int type, unsigned long value)
+{
+ unsigned long param[2] = {value, value};
+
+ return syscall(__NR_setublimit, veid, type, param);
+}
+
+static int vz_set_memory_param(struct container *ct, char *param, char *value)
+{
+ unsigned long ram = 0;
+ unsigned long swap = 0;
+ float overcommit = LONG_MAX;
+ float memory;
+ unsigned int veid = 0;;
+
+ if (parse_uint(ct->name, &veid) < 0) {
+ pr_err("Unable to parse container's ID");
+ return -1;
+ }
+
+ if (strcmp(param, "limit_in_bytes") == 0) {
+ if (parse_uint(value, (unsigned int *)&ram) < 0) {
+ pr_err("Unable to parse container's RAM");
+ return -1;
+ }
+ ram /= getpagesize();
+
+ if (ublimit_mem_syscall(veid, UB_PHYSPAGES, ram)) {
+ pr_perror("Unable to set UB_LOCKEDPAGES");
+ return -1;
+ }
+
+ if (ublimit_mem_syscall(veid, UB_SWAPPAGES, 0)) {
+ pr_perror("Unable to set UB_LOCKEDPAGES");
+ return -1;
+ }
+
+ if (ublimit_mem_syscall(veid, UB_LOCKEDPAGES, ram)) {
+ pr_perror("Unable to set UB_LOCKEDPAGES");
+ return -1;
+ }
+
+ if (ublimit_mem_syscall(veid, UB_OOMGUARPAGES, ram)) {
+ pr_perror("Unable to set UB_OOMGUARPAGES");
+ return -1;
+ }
+
+ if (ublimit_mem_syscall(veid, UB_VMGUARPAGES, ram + swap)) {
+ pr_perror("Unable to set UB_VMGUARPAGES");
+ return -1;
+ }
+
+ memory = (ram + swap) * overcommit;
+ if (memory > LONG_MAX)
+ memory = UINT_MAX;
+
+ if (ublimit_mem_syscall(veid, UB_PRIVVMPAGES, memory)) {
+ pr_perror("Unable to set UB_PRIVVMPAGES");
+ return -1;
+ }
+ return 0;
+ }
+
+ pr_err("Unsupported param for CTL_MEMORY: %s", param);
+ return -1;
+}
+
+static int vzctl2_set_ioprio(unsigned veid, int prio)
+{
+ int ret;
+
+ if (prio < 0)
+ return -LCTERR_BADARG;
+
+ pr_info("Set up ioprio: %d", prio);
+ ret = syscall(__NR_ioprio_set, IOPRIO_WHO_UBC, veid,
+ prio | IOPRIO_CLASS_BE << IOPRIO_CLASS_SHIFT);
+ if (ret) {
+ if (errno == ESRCH) {
+ pr_err("Container is not running");
+ return -LCTERR_BADCTSTATE;
+ }
+ else if (errno == EINVAL) {
+ pr_warn("ioprio feature is not supported"
+ " by the kernel: ioprio configuration is skippe");
+ return -LCTERR_OPNOTSUPP;
+ }
+ pr_perror("Unable to set ioprio");
+ return -1;
+ }
+ return 0;
+}
+
+static int vzctl2_set_iopslimit(unsigned veid, int limit)
+{
+ int ret;
+ struct iolimit_state io;
+
+ if (limit < 0)
+ return -LCTERR_BADARG;
+ io.id = veid;
+ io.speed = limit;
+ io.burst = limit * 3;
+ io.latency = 10*1000;
+ pr_info("Set up iopslimit: %d", limit);
+ ret = ioctl(get_vzctlfd(), VZCTL_SET_IOPSLIMIT, &io);
+ if (ret) {
+ if (errno == ESRCH) {
+ pr_err("Container is not running");
+ return -LCTERR_BADCTSTATE;
+ }
+ else if (errno == ENOTTY) {
+ pr_warn("iopslimit feature is not supported"
+ " by the kernel; iopslimit configuration is skipped");
+ return -LCTERR_OPNOTSUPP;
+ }
+ pr_perror("Unable to set iopslimit");
+ return -1;
+ }
+ return 0;
+}
+
+
+static int vz_set_io_param(struct container *ct, char *param, char *value)
+{
+ unsigned int veid = 0;
+
+ if (parse_uint(ct->name, &veid) < 0) {
+ pr_err("Unable to parse container's ID");
+ return -1;
+ }
+
+ if (strcmp(param, "weight") == 0) {
+ int prio = -1;
+ if (parse_int(value, &prio)) {
+ pr_err("Unable to parse priority from '%s'", value);
+ return -1;
+ }
+ return vzctl2_set_ioprio(veid, prio);
+ } else if (strcmp(param, "throttle.write_iops_device") == 0 ||
+ strcmp(param, "throttle.read_iops_device") == 0) {
+ int limit = -1;
+ if (parse_int(value, &limit)) {
+ pr_err("Unable to parse limit from '%s'", value);
+ return -1;
+ }
+ return vzctl2_set_iopslimit(veid, limit);
+ }
+
+ pr_err("Unsupported param for CTL_BLKIO: %s", param);
+ return -1;
+}
+
+static int vz_resources_create(struct container *ct)
+{
+ struct controller *ctl;
+ struct cg_config *cfg;
+ int ret = 0;
+
+ list_for_each_entry(ctl, &ct->cgroups, ct_l) {
+ switch (ctl->ctype) {
+ case CTL_MEMORY:
+ break;
+ case CTL_BLKIO:
+ break;
+ case CTL_CPUSET:
+ break;
+ default:
+ ret = cgroup_create_one(ct, ctl);
+ if (ret)
+ return -LCTERR_CGCREATE;
+ break;
+ }
+ }
+
+ list_for_each_entry(cfg, &ct->cg_configs, l) {
+ switch (cfg->ctype) {
+ case CTL_MEMORY:
+ ret = vz_set_memory_param(ct, cfg->param, cfg->value);
+ if (ret)
+ return -LCTERR_CGCONFIG;
+ break;
+ case CTL_BLKIO:
+ ret = vz_set_io_param(ct, cfg->param, cfg->value);
+ if (ret)
+ return -LCTERR_CGCONFIG;
+ break;
+ default:
+ ret = config_controller(ct, cfg->ctype, cfg->param, cfg->value);
+ if (ret) {
+ pr_err("local_config_controller failed %d", ret);
+ return -LCTERR_CGCONFIG;
+ }
+ break;
+ }
+ }
+
+ return 0;
+}
+
static int vz_spawn_execve(ct_handler_t h, ct_process_desc_t p, char *path, char **argv, char **env, int *fds)
{
int ret = -1;
@@ -727,6 +932,12 @@ static int vz_spawn_execve(ct_handler_t h, ct_process_desc_t p, char *path, char
goto err_net;
}
+ ret = vz_resources_create(ct);
+ if (ret) {
+ pr_err("vz_resourse_create");
+ goto err_res;
+ }
+
ret = net_start(ct);
if (ret) {
pr_err("Unable to start network");
@@ -748,6 +959,7 @@ static int vz_spawn_execve(ct_handler_t h, ct_process_desc_t p, char *path, char
err_wait:
net_stop(ct);
err_net:
+err_res:
proc_wake_close(child_wait, -1);
err_fork:
fs_umount(ct);
@@ -823,35 +1035,6 @@ static int wait_env_state(unsigned int veid, int state, unsigned int timeout)
return -1;
}
-static int vzctl2_set_iopslimit(unsigned veid, int limit)
-{
- int ret;
- struct iolimit_state io;
-
- if (limit < 0)
- return -LCTERR_BADARG;
- io.id = veid;
- io.speed = limit;
- io.burst = limit * 3;
- io.latency = 10*1000;
- pr_info("Set up iopslimit: %d", limit);
- ret = ioctl(get_vzctlfd(), VZCTL_SET_IOPSLIMIT, &io);
- if (ret) {
- if (errno == ESRCH) {
- pr_err("Container is not running");
- return -LCTERR_BADCTSTATE;
- }
- else if (errno == ENOTTY) {
- pr_warn("iopslimit feature is not supported"
- " by the kernel; iopslimit configuration is skipped");
- return -LCTERR_OPNOTSUPP;
- }
- pr_perror("Unable to set iopslimit");
- return -1;
- }
- return 0;
-}
-
static int real_env_stop(int stop_mode)
{
int fd;
@@ -1042,14 +1225,8 @@ static int vz_set_nsmask(ct_handler_t h, unsigned long nsmask)
return 0;
}
-static int vz_config_controller(ct_handler_t h, enum ct_controller ctype,
- char *param, char *value)
-{
- pr_perror("Controller configuration are not supported");
- return -LCTERR_CGCONFIG;
-}
-
static int vz_enter_execve(ct_handler_t h, ct_process_desc_t p, char *path, char **argv, char **env, int *fds)
+{
struct container *ct = NULL;
unsigned int veid = -1;
int pid, child_pid, ret = 0;
@@ -1140,7 +1317,7 @@ static const struct container_ops vz_ct_ops = {
.detach = vz_ct_destroy,
.set_nsmask = vz_set_nsmask,
.add_controller = local_add_controller,
- .config_controller = vz_config_controller,
+ .config_controller = local_config_controller,
.fs_set_root = local_fs_set_root,
.fs_set_private = local_fs_set_private,
.fs_add_mount = local_add_mount,
diff --git a/test/Makefile b/test/Makefile
index 4d07094..c9f10c3 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -4,13 +4,15 @@ TESTS = ct_create ct_enter ct_proc ct_root ct_root_enter \
ct_ext_mount ct_private_subdir_ns \
ct_cgroup_sub ct_service ct_kill_nons ct_pid_enter \
ct_userns ct_caps \
- vz_create_exec vz_net_veth vz_enter
+ vz_create_exec vz_net_veth vz_enter \
+ vz_cgroup_memory vz_cgroup_cpu vz_cgroup_blkio
PIGS = file_piggy
CC = gcc
CFLAGS = -I../src/include/uapi/ -g -Wall -Werror
ifneq ("$(wildcard /proc/vz)","")
+ CFLAGS += -I/usr/src/kernels/$(shell uname -r)/include/
CFLAGS += -DVZ
endif
LIBNLDIR = ../.shipped/libnl/lib/.libs
diff --git a/test/vz_cgroup_blkio.c b/test/vz_cgroup_blkio.c
new file mode 100644
index 0000000..cde1dd3
--- /dev/null
+++ b/test/vz_cgroup_blkio.c
@@ -0,0 +1,112 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <libct.h>
+#include <unistd.h>
+#include <sched.h>
+#include <fcntl.h>
+#include <linux/vziolimit.h>
+#include <sys/ioctl.h>
+
+#include "test.h"
+
+#define FS_ROOT "/"
+#define VZCTLDEV "/dev/vzctl"
+
+#define STR_HELPER(x) #x
+#define STR(x) STR_HELPER(x)
+
+#define CT_ID 1339
+#define CT_NAME STR(CT_ID)
+#define IOPSLIMIT 1
+#define IOPSLIMIT_STR STR(IOPSLIMIT)
+#define IOPRIOLIMIT 7
+#define IOPRIOLIMIT_STR STR(IOPRIOLIMIT)
+
+int is_iopslimit_correct(unsigned int expected_limit)
+{
+ int fd = -1;
+ int ret = 0;
+ struct iolimit_state io;
+
+ fd = open(VZCTLDEV, O_RDWR);
+ if (fd == -1) {
+ fprintf(stderr, "Unable to open %s!\n", VZCTLDEV);
+ goto err;
+ }
+
+ io.id = CT_ID;
+ if (ioctl(fd, VZCTL_GET_IOPSLIMIT, &io)) {
+ perror("ioctl");
+ goto err;
+ }
+ ret = (io.speed == expected_limit);
+
+err:
+ close(fd);
+ return ret;
+}
+
+int is_iopriolimit_correct(unsigned int expected_limit)
+{
+ int ret = 0;
+ FILE *f = NULL;
+ int limit;
+ char buf[1024];
+
+ f = fopen("/proc/bc/" CT_NAME "/ioprio", "r");
+ if (!f) {
+ fprintf(stderr, "Unable to open /proc/bc/" CT_NAME "/ioprio!\n");
+ goto err;
+ }
+
+ if (fscanf(f, "%s %d", buf, &limit) != 2) {
+ fprintf(stderr, "fscanf failed!\n");
+ goto err;
+ }
+ ret = (limit == expected_limit);
+err:
+ fclose(f);
+ return ret;
+}
+
+int main(int argc, char *argv[])
+{
+ libct_session_t s;
+ ct_handler_t ct;
+ ct_process_desc_t p;
+ char *sleep_a[] = { "sleep", "2", NULL};
+ int fds[] = {STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO};
+ int pid;
+
+ s = libct_session_open_local();
+ ct = libct_container_create(s, CT_NAME);
+ p = libct_process_desc_create(s);
+ libct_fs_set_root(ct, FS_ROOT);
+
+ libct_container_set_nsmask(ct,
+ CLONE_NEWNS |
+ CLONE_NEWUTS |
+ CLONE_NEWIPC |
+ CLONE_NEWNET |
+ CLONE_NEWPID);
+
+ libct_controller_add(ct, CTL_BLKIO);
+ libct_controller_configure(ct, CTL_BLKIO, "throttle.write_iops_device", IOPSLIMIT_STR);
+ libct_controller_configure(ct, CTL_BLKIO, "weight", IOPRIOLIMIT_STR);
+ if ((pid = libct_container_spawn_execvfds(ct, p, "/bin/sleep", sleep_a, fds)) <= 0)
+ goto err;
+
+ if (!is_iopslimit_correct(IOPSLIMIT))
+ goto err;
+ if (!is_iopriolimit_correct(IOPRIOLIMIT))
+ goto err;
+
+ libct_container_wait(ct);
+ libct_container_destroy(ct);
+
+ libct_session_close(s);
+
+ return pass("All is ok");;
+err:
+ return fail("Something wrong");
+}
diff --git a/test/vz_cgroup_cpu.c b/test/vz_cgroup_cpu.c
new file mode 100644
index 0000000..a322155
--- /dev/null
+++ b/test/vz_cgroup_cpu.c
@@ -0,0 +1,78 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <libct.h>
+#include <unistd.h>
+#include <string.h>
+#include <sched.h>
+
+#include "test.h"
+
+#define FS_ROOT "/"
+
+#define STR_HELPER(x) #x
+#define STR(x) STR_HELPER(x)
+
+#define CT_ID 1339
+#define CT_NAME STR(CT_ID)
+#define CPUS "0-1,3"
+
+int is_cpu_count_correct(const char *expected_cpus)
+{
+ int ret = 0;
+ FILE *f = NULL;
+ char buf[1024] = {'\0'};
+
+ f = fopen("/proc/vz/fairsched/" CT_NAME "/cpuset.cpus", "r");
+ if (!f) {
+ fprintf(stderr, "Unable to open /proc/vz/fairsched/" CT_NAME "/cpuset.cpus!\n");
+ goto err;
+ }
+
+ if (fscanf(f, "%s", buf) != 1) {
+ fprintf(stderr, "fscanf failed!\n");
+ goto err;
+ }
+ ret = !strcmp(buf, expected_cpus);
+err:
+ fclose(f);
+ return ret;
+}
+
+
+int main(int argc, char *argv[])
+{
+ libct_session_t s;
+ ct_handler_t ct;
+ ct_process_desc_t p;
+ char *sleep_a[] = { "sleep", "2", NULL};
+ int fds[] = {STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO};
+
+ s = libct_session_open_local();
+ ct = libct_container_create(s, CT_NAME);
+ p = libct_process_desc_create(s);
+ libct_fs_set_root(ct, FS_ROOT);
+
+ libct_container_set_nsmask(ct,
+ CLONE_NEWNS |
+ CLONE_NEWUTS |
+ CLONE_NEWIPC |
+ CLONE_NEWNET |
+ CLONE_NEWPID);
+
+ libct_controller_add(ct, CTL_CPUSET);
+ libct_controller_configure(ct, CTL_CPUSET, "cpus", CPUS);
+ if (libct_container_spawn_execvfds(ct, p, "/bin/sleep", sleep_a, fds) <= 0)
+ goto err;
+
+ if (!is_cpu_count_correct(CPUS))
+ goto err;
+
+ libct_container_wait(ct);
+ libct_container_destroy(ct);
+
+ libct_session_close(s);
+
+ return pass("All is ok");;
+err:
+ return fail("Something wrong");
+}
diff --git a/test/vz_cgroup_memory.c b/test/vz_cgroup_memory.c
new file mode 100644
index 0000000..190bdbc
--- /dev/null
+++ b/test/vz_cgroup_memory.c
@@ -0,0 +1,89 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libct.h>
+#include <unistd.h>
+#include <sched.h>
+
+#include "test.h"
+
+#define FS_ROOT "/"
+
+#define STR_HELPER(x) #x
+#define STR(x) STR_HELPER(x)
+
+#define MEMLIMIT 134217728
+#define MEMLIMIT_STR STR(MEMLIMIT)
+
+#define CT_ID 1339
+#define CT_NAME STR(CT_ID)
+
+int is_memory_correct(unsigned long expected_limit)
+{
+ int ret = 0;
+ FILE *f = NULL;
+ char buf[1024] = {'\0'};
+ unsigned long flds[5] = {0};
+ unsigned long limit = 0;
+
+ f = fopen("/proc/bc/" CT_NAME "/resources", "r");
+ if (!f) {
+ fprintf(stderr, "Unable to open /proc/bc/" CT_NAME "/resources!\n");
+ goto err;
+ }
+
+ while (fscanf(f, "%s %lu %lu %lu %lu %lu", buf, &flds[0], &flds[1], &flds[2], &flds[3], &flds[4]) == 6) {
+ if (strcmp(buf, "physpages") == 0 && flds[2] == flds[3]) {
+ limit = flds[2] * getpagesize();
+ break;
+ }
+ }
+ if (!limit) {
+ fprintf(stderr, "unable to read \n");
+ goto err;
+ }
+ ret = (limit == expected_limit);
+err:
+ fclose(f);
+ return ret;
+}
+
+
+int main(int argc, char *argv[])
+{
+ libct_session_t s;
+ ct_handler_t ct;
+ ct_process_desc_t p;
+ char *run_a[3] = { "sleep", "2", NULL};
+ int fds[] = {STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO};
+
+ s = libct_session_open_local();
+ ct = libct_container_create(s, CT_NAME);
+ p = libct_process_desc_create(s);
+ libct_fs_set_root(ct, FS_ROOT);
+
+ libct_container_set_nsmask(ct,
+ CLONE_NEWNS |
+ CLONE_NEWUTS |
+ CLONE_NEWIPC |
+ CLONE_NEWNET |
+ CLONE_NEWPID);
+
+ libct_controller_add(ct, CTL_MEMORY);
+ libct_controller_configure(ct, CTL_MEMORY, "limit_in_bytes", MEMLIMIT_STR);
+
+ if (libct_container_spawn_execvfds(ct, p, "/bin/sleep", run_a, fds) <= 0)
+ goto err;
+
+ if (!is_memory_correct(MEMLIMIT))
+ goto err;
+
+ libct_container_wait(ct);
+ libct_container_destroy(ct);
+
+ libct_session_close(s);
+
+ return pass("All is ok");;
+err:
+ return fail("Something wrong");
+}
--
1.7.1
More information about the Libct
mailing list