static int l_cgroup_set_value(lua_State *L) { struct u_cgroup_controller *uc = check_cgroup_controller(L, 1); const char *key = lua_tostring(L, 2); int rv; if(!uc) { lua_pushstring(L, "Not a valid cgroup"); lua_error (L); return 0; } if(lua_isstring(L, 3)) { rv = cgroup_set_value_string(uc->controller, key, lua_tostring(L, 3)); } else if(lua_isnumber(L, 3)) { rv = cgroup_set_value_int64(uc->controller, key, lua_tointeger(L, 3)); } else if(lua_isboolean(L, 3)) { rv = cgroup_set_value_bool(uc->controller, key, lua_toboolean(L, 3)); } else { lua_pushstring(L, "Not a valid type"); lua_error (L); } lua_pushinteger(L, rv); return 1; }
static int controller_apply_config(struct cgroup *ct, struct cgroup *parent, struct cgroup_controller *controller, const char *name) { int ret; if (!strcmp("cpuset", name)) { struct cgroup_controller *pcont = cgroup_get_controller(parent, name); if (!pcont) return 0; if ((ret = copy_string_from_parent(controller, pcont, "cpuset.cpus"))) return ret; if ((ret = copy_string_from_parent(controller, pcont, "cpuset.mems"))) return ret; } else if (!strcmp("memory", name)) { if ((ret = cgroup_set_value_string(controller, "memory.use_hierarchy", "1"))) return ret; /* * The kernel memory controller cannot flip states from * unlimited to limited if there are already tasks in it. * Therefore, we always have to run with *some* value of kmem * enabled. If we don't do it, we can't start unlimited and * then use the set command to set any beancounters. We write * The maximum amount minus two pages, which should effectively * mean "accounting turned on, but unlimited". This will fail * if the kmem controller is not present, but that is okay. */ cgroup_set_value_string(controller, "memory.kmem.limit_in_bytes", "9223372036854767712"); } else if (!strcmp("devices", name)) { if ((ret = cgroup_set_value_string(controller, "devices.deny", "a"))) return ret; } return 0; }
static int copy_string_from_parent(struct cgroup_controller *controller, struct cgroup_controller *pcont, const char *file) { char *ptr = NULL; int ret; ret = cgroup_get_value_string(pcont, file, &ptr); if (ret) goto out; ret = cgroup_set_value_string(controller, file, ptr); out: free(ptr); return ret; }
int container_apply_config(envid_t veid, enum conf_files c, void *_val) { struct cgroup *ct; char cgrp[CT_MAX_STR_SIZE]; struct cgroup_controller *mem, *cpu, *cpuset; int ret = -EINVAL; unsigned long *val = _val; veid_to_name(cgrp, veid); ct = cgroup_new_cgroup(cgrp); /* * We should really be doing some thing like: * * ret = cgroup_get_cgroup(ct); * * and then doing cgroup_get_controller. However, libcgroup has * a very nasty bug that make it sometimes fail. adding a controller * to a newly "created" cgroup structure and then setting the value * is a workaround that seems to work on various versions of the * library */ switch (c) { case MEMORY: if ((mem = cgroup_add_controller(ct, "memory"))) ret = cgroup_set_value_uint64(mem, MEMLIMIT, *val); break; case SWAP: /* Unlike kmem, this must always be greater than mem */ if ((mem = cgroup_add_controller(ct, "memory"))) { u_int64_t mval; if (!cgroup_get_value_uint64(mem, MEMLIMIT, &mval)) ret = cgroup_set_value_uint64(mem, SWAPLIMIT, mval + *val); } break; case KMEMORY: if ((mem = cgroup_add_controller(ct, "memory"))) ret = cgroup_set_value_uint64(mem, KMEMLIMIT, *val); break; case TCP: if ((mem = cgroup_add_controller(ct, "memory"))) ret = cgroup_set_value_uint64(mem, TCPLIMIT, *val); break; case CPULIMIT: { u_int64_t period; u_int64_t quota; if ((cpu = cgroup_add_controller(ct, "cpu")) == NULL) break; /* Should be 100000, but be safe. It may fail on some versions * of libcgroup, so if it fails, just assume the default */ ret = cgroup_get_value_uint64(cpu, "cpu.cfs_period_us", &period); if (ret) period = 100000; /* val will contain an integer percentage, like 223% */ quota = (period * (*val)) / 100; ret = cgroup_set_value_uint64(cpu, "cpu.cfs_quota_us", quota); break; } case CPUSHARES: if ((cpu = cgroup_add_controller(ct, "cpu")) == NULL) break; ret = cgroup_set_value_uint64(cpu, "cpu.shares", *val); break; case CPUMASK: { struct cgroup_controller *pcont; struct cgroup *parent; char *ptr = NULL; char cpusetstr[2 * CPUMASK_NBITS]; unsigned int i; if ((cpuset = cgroup_add_controller(ct, "cpuset")) == NULL) break; /* * Having all bits set is a bit different, bitmap_snprintf will * return a bad string. (From the PoV of the cpuset cgroup). We * actually need to copy the parent's mask in that case. */ for (i = 0; i < CPUMASK_NBYTES; i++) { if (val[i] != (~0UL)) { bitmap_snprintf(cpusetstr, CPUMASK_NBITS * 2, val, CPUMASK_NBITS); goto string_ok; } } parent = cgroup_new_cgroup(CT_BASE_STRING); cgroup_get_cgroup(parent); pcont = cgroup_get_controller(parent, "cpuset"); ret = cgroup_get_value_string(pcont, "cpuset.cpus", &ptr); if (ptr) { strncpy(cpusetstr, ptr, CPUMASK_NBITS *2); free(ptr); } cgroup_free(&parent); string_ok: ret = cgroup_set_value_string(cpuset, "cpuset.cpus", cpusetstr); break; } case DEVICES_DENY: { struct cgroup_controller *dev; if ((dev = cgroup_add_controller(ct, "devices")) == NULL) break; ret = cgroup_set_value_string(dev, "devices.deny", (char *)_val); break; } case DEVICES_ALLOW: { struct cgroup_controller *dev; if ((dev = cgroup_add_controller(ct, "devices")) == NULL) break; ret = cgroup_set_value_string(dev, "devices.allow", (char *)_val); break; } default: ret = -EINVAL; break; } if (ret) goto out; if ((ret = cgroup_modify_cgroup(ct))) logger(-1, 0, "Failed to set limits for %s (%s)", conf_names[c], cgroup_strerror(ret)); out: cgroup_free(&ct); return ret; }
int create_container(envid_t veid) { char cgrp[CT_MAX_STR_SIZE]; struct cgroup *ct, *parent; int ret; unsigned int i; const char *devices[] = { "c *:* m", /* everyone can mknod */ "b *:* m", /* block devices too */ "c 1:3 rmw", /* null */ "c 1:5 rmw", /* zero */ "c 1:7 rmw", /* full */ "c 1:8 rmw", /* random */ "c 1:9 rmw", /* urandom */ "c 5:2 rmw", /* ptmx */ "c 136:* rmw", /* various pts */ }; veid_to_name(cgrp, veid); ct = cgroup_new_cgroup(cgrp); parent = cgroup_new_cgroup("/"); ret = do_create_container(ct, parent); cgroup_free(&ct); cgroup_free(&parent); /* * FIXME: This is yet another hack required by libcgroup. At some point * in time, this MUST go away. * * Problem is that libcgroup works with buffered writes. If we write to * a cgroup file and want it to be seen in the filesystem, we need to * call cgroup_modify_cgroup(). * * However, all versions up to 0.38 will fail that operation for already * existent cgroups, due to a bug in the way they handle modifications * in the presence of read-only files (whether or not that specific file * was being modified). Because of that, we need to come up with a new * cgroup all the time, and free it afterwards. */ for (i = 0; i < ARRAY_SIZE(devices); i++) { struct cgroup_controller *dev; veid_to_name(cgrp, veid); ct = cgroup_new_cgroup(cgrp); if ((dev = cgroup_add_controller(ct, "devices"))) { cgroup_set_value_string(dev, "devices.allow", devices[i]); if ((ret = cgroup_modify_cgroup(ct))) { logger(-1, 0, "Failed to set device permissions for %s (%s)", devices[i], cgroup_strerror(ret)); } } else { logger(-1, 0, "Failed to attach device controller (%s)", cgroup_strerror(ret)); } cgroup_free(&ct); } return ret; }