From 4fb3cba5bc6b256b774e780f2bbf47b1dbcc0ce9 Mon Sep 17 00:00:00 2001 From: Dwight Engen Date: Wed, 5 Feb 2014 16:59:26 -0500 Subject: [PATCH] split cgroup handling into discrete backends - refactor cgroup into two backends, the classic cgfs driver and the new cgmanager. Instead of lxc_handler knowing about the internals of each, have it just store an opaque pointer to a struct that is private to each backend. - rename a couple of cgroup functions for consistency: those that are considered an API (ie. exported by lxc.h) begin with lxc_ and those that are not are just cgroup_* - made as many backend routines static as possible, only cg*_ops_init is exported - made a nrtasks op which is needed by the utmp code for monitoring container shutdown, currently only implemented for the cgfs backend Signed-off-by: Dwight Engen Signed-off-by: Serge Hallyn --- src/lxc/Makefile.am | 3 +- src/lxc/attach.c | 2 +- src/lxc/cgfs.c | 524 ++++++++++++++++++-------------------------- src/lxc/cgmanager.c | 272 ++++++++++++++--------- src/lxc/cgroup.c | 169 ++++++++++++++ src/lxc/cgroup.h | 207 ++--------------- src/lxc/commands.c | 11 +- src/lxc/conf.c | 12 +- src/lxc/freezer.c | 41 +++- src/lxc/lxc.h | 11 - src/lxc/lxcutmp.c | 2 +- src/lxc/start.h | 2 +- src/lxc/state.c | 2 + src/tests/cgpath.c | 28 +-- 14 files changed, 631 insertions(+), 655 deletions(-) create mode 100644 src/lxc/cgroup.c diff --git a/src/lxc/Makefile.am b/src/lxc/Makefile.am index b4aa924fd..19511a4ec 100644 --- a/src/lxc/Makefile.am +++ b/src/lxc/Makefile.am @@ -62,7 +62,8 @@ liblxc_so_SOURCES = \ freezer.c \ error.h error.c \ parse.c parse.h \ - cgfs.c cgroup.h \ + cgfs.c \ + cgroup.c cgroup.h \ lxc.h \ utils.c utils.h \ sync.c sync.h \ diff --git a/src/lxc/attach.c b/src/lxc/attach.c index 5c4adcddc..87826522d 100644 --- a/src/lxc/attach.c +++ b/src/lxc/attach.c @@ -699,7 +699,7 @@ int lxc_attach(const char* name, const char* lxcpath, lxc_attach_exec_t exec_fun /* attach to cgroup, if requested */ if (options->attach_flags & LXC_ATTACH_MOVE_TO_CGROUP) { - if (!lxc_cgroup_attach(name, lxcpath, pid)) + if (!cgroup_attach(name, lxcpath, pid)) goto cleanup_error; } diff --git a/src/lxc/cgfs.c b/src/lxc/cgfs.c index c23b784fa..f576ba151 100644 --- a/src/lxc/cgfs.c +++ b/src/lxc/cgfs.c @@ -55,7 +55,75 @@ #include #endif -lxc_log_define(lxc_cgroup, lxc); +struct cgroup_hierarchy; +struct cgroup_meta_data; +struct cgroup_mount_point; + +/* + * cgroup_meta_data: the metadata about the cgroup infrastructure on this + * host + */ +struct cgroup_meta_data { + ptrdiff_t ref; /* simple refcount */ + struct cgroup_hierarchy **hierarchies; + struct cgroup_mount_point **mount_points; + int maximum_hierarchy; +}; + +/* + * cgroup_hierarchy: describes a single cgroup hierarchy + * (may have multiple mount points) + */ +struct cgroup_hierarchy { + int index; + bool used; /* false if the hierarchy should be ignored by lxc */ + char **subsystems; + struct cgroup_mount_point *rw_absolute_mount_point; + struct cgroup_mount_point *ro_absolute_mount_point; + struct cgroup_mount_point **all_mount_points; + size_t all_mount_point_capacity; +}; + +/* + * cgroup_mount_point: a mount point to where a hierarchy + * is mounted to + */ +struct cgroup_mount_point { + struct cgroup_hierarchy *hierarchy; + char *mount_point; + char *mount_prefix; + bool read_only; + bool need_cpuset_init; +}; + +/* + * cgroup_process_info: describes the membership of a + * process to the different cgroup + * hierarchies + * + * Note this is the per-process info tracked by the cgfs_ops. + * This is not used with cgmanager. + */ +struct cgroup_process_info { + struct cgroup_process_info *next; + struct cgroup_meta_data *meta_ref; + struct cgroup_hierarchy *hierarchy; + char *cgroup_path; + char *cgroup_path_sub; + char **created_paths; + size_t created_paths_capacity; + size_t created_paths_count; + struct cgroup_mount_point *designated_mount_point; +}; + +struct cgfs_data { + char *name; + const char *cgroup_pattern; + struct cgroup_meta_data *meta; + struct cgroup_process_info *info; +}; + +lxc_log_define(lxc_cgfs, lxc); static struct cgroup_process_info *lxc_cgroup_process_info_getx(const char *proc_pid_cgroup_str, struct cgroup_meta_data *meta); static char **subsystems_from_mount_options(const char *mount_options, char **kernel_list); @@ -68,27 +136,22 @@ static char *cgroup_to_absolute_path(struct cgroup_mount_point *mp, const char * static struct cgroup_process_info *find_info_for_subsystem(struct cgroup_process_info *info, const char *subsystem); static int do_cgroup_get(const char *cgroup_path, const char *sub_filename, char *value, size_t len); static int do_cgroup_set(const char *cgroup_path, const char *sub_filename, const char *value); -static bool cgroup_devices_has_allow_or_deny(struct lxc_handler *h, char *v, bool for_allow); -static int do_setup_cgroup_limits(struct lxc_handler *h, struct lxc_list *cgroup_settings, bool do_devices); +static bool cgroup_devices_has_allow_or_deny(struct cgfs_data *d, char *v, bool for_allow); +static int do_setup_cgroup_limits(struct cgfs_data *d, struct lxc_list *cgroup_settings, bool do_devices); static int cgroup_recursive_task_count(const char *cgroup_path); static int count_lines(const char *fn); static int handle_cgroup_settings(struct cgroup_mount_point *mp, char *cgroup_path); static bool init_cpuset_if_needed(struct cgroup_mount_point *mp, const char *path); +static struct cgroup_meta_data *lxc_cgroup_load_meta2(const char **subsystem_whitelist); +static struct cgroup_meta_data *lxc_cgroup_get_meta(struct cgroup_meta_data *meta_data); +static struct cgroup_meta_data *lxc_cgroup_put_meta(struct cgroup_meta_data *meta_data); + +/* free process membership information */ +static void lxc_cgroup_process_info_free(struct cgroup_process_info *info); +static void lxc_cgroup_process_info_free_and_remove(struct cgroup_process_info *info); + static struct cgroup_ops cgfs_ops; -struct cgroup_ops *active_cg_ops = &cgfs_ops; -static void init_cg_ops(void); - -#ifdef HAVE_CGMANAGER -/* this needs to be mutexed for api use */ -extern bool cgmanager_initialized; -extern bool use_cgmanager; -extern bool lxc_init_cgmanager(void); -#else -static bool cgmanager_initialized = false; -static bool use_cgmanager = false; -static bool lxc_init_cgmanager(void) { return false; } -#endif static int cgroup_rmdir(char *dirname) { @@ -159,7 +222,7 @@ static int cgroup_rmdir(char *dirname) return failed ? -1 : 0; } -struct cgroup_meta_data *lxc_cgroup_load_meta() +static struct cgroup_meta_data *lxc_cgroup_load_meta() { const char *cgroup_use = NULL; char **cgroup_use_list = NULL; @@ -184,7 +247,7 @@ struct cgroup_meta_data *lxc_cgroup_load_meta() } /* Step 1: determine all kernel subsystems */ -bool find_cgroup_subsystems(char ***kernel_subsystems) +static bool find_cgroup_subsystems(char ***kernel_subsystems) { FILE *proc_cgroups; bool bret = false; @@ -470,7 +533,7 @@ out: return bret; } -struct cgroup_meta_data *lxc_cgroup_load_meta2(const char **subsystem_whitelist) +static struct cgroup_meta_data *lxc_cgroup_load_meta2(const char **subsystem_whitelist) { bool all_kernel_subsystems = true; bool all_named_subsystems = false; @@ -526,13 +589,13 @@ out_error: return NULL; } -struct cgroup_meta_data *lxc_cgroup_get_meta(struct cgroup_meta_data *meta_data) +static struct cgroup_meta_data *lxc_cgroup_get_meta(struct cgroup_meta_data *meta_data) { meta_data->ref++; return meta_data; } -struct cgroup_meta_data *lxc_cgroup_put_meta(struct cgroup_meta_data *meta_data) +static struct cgroup_meta_data *lxc_cgroup_put_meta(struct cgroup_meta_data *meta_data) { size_t i; if (!meta_data) @@ -549,7 +612,7 @@ struct cgroup_meta_data *lxc_cgroup_put_meta(struct cgroup_meta_data *meta_data) return NULL; } -struct cgroup_hierarchy *lxc_cgroup_find_hierarchy(struct cgroup_meta_data *meta_data, const char *subsystem) +static struct cgroup_hierarchy *lxc_cgroup_find_hierarchy(struct cgroup_meta_data *meta_data, const char *subsystem) { size_t i; for (i = 0; i <= meta_data->maximum_hierarchy; i++) { @@ -560,7 +623,7 @@ struct cgroup_hierarchy *lxc_cgroup_find_hierarchy(struct cgroup_meta_data *meta return NULL; } -struct cgroup_mount_point *lxc_cgroup_find_mount_point(struct cgroup_hierarchy *hierarchy, const char *group, bool should_be_writable) +static struct cgroup_mount_point *lxc_cgroup_find_mount_point(struct cgroup_hierarchy *hierarchy, const char *group, bool should_be_writable) { struct cgroup_mount_point **mps; struct cgroup_mount_point *current_result = NULL; @@ -600,7 +663,7 @@ struct cgroup_mount_point *lxc_cgroup_find_mount_point(struct cgroup_hierarchy * return current_result; } -char *lxc_cgroup_find_abs_path(const char *subsystem, const char *group, bool should_be_writable, const char *suffix) +static char *lxc_cgroup_find_abs_path(const char *subsystem, const char *group, bool should_be_writable, const char *suffix) { struct cgroup_meta_data *meta_data; struct cgroup_hierarchy *h; @@ -634,19 +697,19 @@ out_error: return NULL; } -struct cgroup_process_info *lxc_cgroup_process_info_get(pid_t pid, struct cgroup_meta_data *meta) +static struct cgroup_process_info *lxc_cgroup_process_info_get(pid_t pid, struct cgroup_meta_data *meta) { char pid_buf[32]; snprintf(pid_buf, 32, "/proc/%lu/cgroup", (unsigned long)pid); return lxc_cgroup_process_info_getx(pid_buf, meta); } -struct cgroup_process_info *lxc_cgroup_process_info_get_init(struct cgroup_meta_data *meta) +static struct cgroup_process_info *lxc_cgroup_process_info_get_init(struct cgroup_meta_data *meta) { return lxc_cgroup_process_info_get(1, meta); } -struct cgroup_process_info *lxc_cgroup_process_info_get_self(struct cgroup_meta_data *meta) +static struct cgroup_process_info *lxc_cgroup_process_info_get_self(struct cgroup_meta_data *meta) { struct cgroup_process_info *i; i = lxc_cgroup_process_info_getx("/proc/self/cgroup", meta); @@ -724,7 +787,7 @@ static char *cgroup_rename_nsgroup(const char *mountpath, const char *oldname, p } /* create a new cgroup */ -struct cgroup_process_info *lxc_cgroupfs_create(const char *name, const char *path_pattern, struct cgroup_meta_data *meta_data, const char *sub_pattern) +static struct cgroup_process_info *lxc_cgroupfs_create(const char *name, const char *path_pattern, struct cgroup_meta_data *meta_data, const char *sub_pattern) { char **cgroup_path_components = NULL; char **p = NULL; @@ -817,7 +880,7 @@ struct cgroup_process_info *lxc_cgroupfs_create(const char *name, const char *pa } goto find_name_on_this_level; - + cleanup_name_on_this_level: /* This is reached if we found a name clash. * In that case, remove the cgroup from all previous hierarchies @@ -836,7 +899,7 @@ struct cgroup_process_info *lxc_cgroupfs_create(const char *name, const char *pa current_component = current_subpath = NULL; /* try again with another suffix */ ++suffix; - + find_name_on_this_level: /* determine name of the path component we should create */ if (contains_name && suffix > 0) { @@ -937,7 +1000,7 @@ struct cgroup_process_info *lxc_cgroupfs_create(const char *name, const char *pa free(current_component); current_component = current_subpath = NULL; continue; - + cleanup_from_error: /* called if an error occured in the loop, so we * do some additional cleanup here @@ -985,7 +1048,7 @@ out_initial_error: return NULL; } -int lxc_cgroup_create_legacy(struct cgroup_process_info *base_info, const char *name, pid_t pid) +static int lxc_cgroup_create_legacy(struct cgroup_process_info *base_info, const char *name, pid_t pid) { struct cgroup_process_info *info_ptr; int r; @@ -1016,7 +1079,7 @@ int lxc_cgroup_create_legacy(struct cgroup_process_info *base_info, const char * } /* get the cgroup membership of a given container */ -struct cgroup_process_info *lxc_cgroup_get_container_info(const char *name, const char *lxcpath, struct cgroup_meta_data *meta_data) +static struct cgroup_process_info *lxc_cgroup_get_container_info(const char *name, const char *lxcpath, struct cgroup_meta_data *meta_data) { struct cgroup_process_info *result = NULL; int saved_errno = 0; @@ -1064,7 +1127,7 @@ out_error: } /* move a processs to the cgroups specified by the membership */ -int lxc_cgroupfs_enter(struct cgroup_process_info *info, pid_t pid, bool enter_sub) +static int lxc_cgroupfs_enter(struct cgroup_process_info *info, pid_t pid, bool enter_sub) { char pid_buf[32]; char *cgroup_tasks_fn; @@ -1148,9 +1211,8 @@ void lxc_cgroup_process_info_free_and_remove(struct cgroup_process_info *info) lxc_cgroup_process_info_free_and_remove(next); } -static char *lxc_cgroup_get_hierarchy_path_handler(const char *subsystem, struct lxc_handler *handler) +static char *lxc_cgroup_get_hierarchy_path_data(const char *subsystem, struct cgfs_data *d) { - struct cgfs_data *d = handler->cgroup_info->data; struct cgroup_process_info *info = d->info; info = find_info_for_subsystem(info, subsystem); if (!info) @@ -1158,14 +1220,8 @@ static char *lxc_cgroup_get_hierarchy_path_handler(const char *subsystem, struct return info->cgroup_path; } -char *lxc_cgroup_get_hierarchy_path(const char *subsystem, const char *name, const char *lxcpath) -{ - return lxc_cmd_get_cgroup_path(name, lxcpath, subsystem); -} - -char *lxc_cgroup_get_hierarchy_abs_path_handler(const char *subsystem, struct lxc_handler *handler) +static char *lxc_cgroup_get_hierarchy_abs_path_data(const char *subsystem, struct cgfs_data *d) { - struct cgfs_data *d = handler->cgroup_info->data; struct cgroup_process_info *info = d->info; struct cgroup_mount_point *mp = NULL; @@ -1182,7 +1238,7 @@ char *lxc_cgroup_get_hierarchy_abs_path_handler(const char *subsystem, struct lx return cgroup_to_absolute_path(mp, info->cgroup_path, NULL); } -char *lxc_cgroup_get_hierarchy_abs_path(const char *subsystem, const char *name, const char *lxcpath) +static char *lxc_cgroup_get_hierarchy_abs_path(const char *subsystem, const char *name, const char *lxcpath) { struct cgroup_meta_data *meta; struct cgroup_process_info *base_info, *info; @@ -1214,7 +1270,7 @@ out1: return result; } -int lxc_cgroup_set_handler(const char *filename, const char *value, struct lxc_handler *handler) +static int lxc_cgroup_set_data(const char *filename, const char *value, struct cgfs_data *d) { char *subsystem = NULL, *p, *path; int ret = -1; @@ -1224,7 +1280,7 @@ int lxc_cgroup_set_handler(const char *filename, const char *value, struct lxc_h if ((p = index(subsystem, '.')) != NULL) *p = '\0'; - path = lxc_cgroup_get_hierarchy_abs_path_handler(subsystem, handler); + path = lxc_cgroup_get_hierarchy_abs_path_data(subsystem, d); if (path) { ret = do_cgroup_set(path, filename, value); free(path); @@ -1232,25 +1288,7 @@ int lxc_cgroup_set_handler(const char *filename, const char *value, struct lxc_h return ret; } -int lxc_cgroup_get_handler(const char *filename, char *value, size_t len, struct lxc_handler *handler) -{ - char *subsystem = NULL, *p, *path; - int ret = -1; - - subsystem = alloca(strlen(filename) + 1); - strcpy(subsystem, filename); - if ((p = index(subsystem, '.')) != NULL) - *p = '\0'; - - path = lxc_cgroup_get_hierarchy_abs_path_handler(subsystem, handler); - if (path) { - ret = do_cgroup_get(path, filename, value, len); - free(path); - } - return ret; -} - -int lxc_cgroupfs_set(const char *filename, const char *value, const char *name, const char *lxcpath) +static int lxc_cgroupfs_set(const char *filename, const char *value, const char *name, const char *lxcpath) { char *subsystem = NULL, *p, *path; int ret = -1; @@ -1268,7 +1306,7 @@ int lxc_cgroupfs_set(const char *filename, const char *value, const char *name, return ret; } -int lxc_cgroupfs_get(const char *filename, char *value, size_t len, const char *name, const char *lxcpath) +static int lxc_cgroupfs_get(const char *filename, char *value, size_t len, const char *name, const char *lxcpath) { char *subsystem = NULL, *p, *path; int ret = -1; @@ -1286,48 +1324,7 @@ int lxc_cgroupfs_get(const char *filename, char *value, size_t len, const char * return ret; } -/* - * lxc_cgroup_path_get: Get the absolute pathname for a cgroup - * file for a running container. - * - * @filename : the file of interest (e.g. "freezer.state") or - * the subsystem name (e.g. "freezer") in which case - * the directory where the cgroup may be modified - * will be returned - * @name : name of container to connect to - * @lxcpath : the lxcpath in which the container is running - * - * This is the exported function, which determines cgpath from the - * lxc-start of the @name container running in @lxcpath. - * - * Returns path on success, NULL on error. The caller must free() - * the returned path. - */ -char *lxc_cgroup_path_get(const char *filename, const char *name, - const char *lxcpath) -{ - char *subsystem = NULL, *longer_file = NULL, *p, *group, *path; - - subsystem = alloca(strlen(filename) + 1); - strcpy(subsystem, filename); - if ((p = index(subsystem, '.')) != NULL) { - *p = '\0'; - longer_file = alloca(strlen(filename) + 2); - longer_file[0] = '/'; - strcpy(longer_file + 1, filename); - } - - group = lxc_cgroup_get_hierarchy_path(subsystem, name, lxcpath); - if (!group) - return NULL; - - path = lxc_cgroup_find_abs_path(subsystem, group, true, p ? longer_file : NULL); - free(group); - return path; -} - -static bool cgroupfs_mount_cgroup(const char *root, - struct lxc_cgroup_info *cgroup_info, int type) +static bool cgroupfs_mount_cgroup(void *hdata, const char *root, int type) { size_t bufsz = strlen(root) + sizeof("/sys/fs/cgroup"); char *path = NULL; @@ -1339,9 +1336,9 @@ static bool cgroupfs_mount_cgroup(const char *root, struct cgroup_process_info *info, *base_info; int r, saved_errno = 0; - init_cg_ops(); - - cgfs_d = cgroup_info->data; + cgfs_d = hdata; + if (!cgfs_d) + return false; base_info = cgfs_d->info; if (type < LXC_AUTO_CGROUP_RO || type > LXC_AUTO_CGROUP_FULL_MIXED) { @@ -1510,14 +1507,20 @@ out_error: return false; } -int lxc_cgroup_nrtasks_handler(struct lxc_handler *handler) +static int cgfs_nrtasks(void *hdata) { - struct cgfs_data *d = handler->cgroup_info->data; - struct cgroup_process_info *info = d->info; + struct cgfs_data *d = hdata; + struct cgroup_process_info *info; struct cgroup_mount_point *mp = NULL; char *abs_path = NULL; int ret; + if (!d) { + errno = ENOENT; + return -1; + } + + info = d->info; if (!info) { errno = ENOENT; return -1; @@ -1842,7 +1845,7 @@ static int do_cgroup_set(const char *cgroup_path, const char *sub_filename, return ret; } -static int do_setup_cgroup_limits(struct lxc_handler *h, +static int do_setup_cgroup_limits(struct cgfs_data *d, struct lxc_list *cgroup_settings, bool do_devices) { struct lxc_list *iterator; @@ -1857,14 +1860,14 @@ static int do_setup_cgroup_limits(struct lxc_handler *h, if (do_devices == !strncmp("devices", cg->subsystem, 7)) { if (strcmp(cg->subsystem, "devices.deny") == 0 && - cgroup_devices_has_allow_or_deny(h, cg->value, false)) + cgroup_devices_has_allow_or_deny(d, cg->value, false)) continue; if (strcmp(cg->subsystem, "devices.allow") == 0 && - cgroup_devices_has_allow_or_deny(h, cg->value, true)) + cgroup_devices_has_allow_or_deny(d, cg->value, true)) continue; - if (lxc_cgroup_set_handler(cg->subsystem, cg->value, h)) { + if (lxc_cgroup_set_data(cg->subsystem, cg->value, d)) { ERROR("Error setting %s to %s for %s\n", - cg->subsystem, cg->value, h->name); + cg->subsystem, cg->value, d->name); goto out; } } @@ -1878,7 +1881,7 @@ out: return ret; } -static bool cgroup_devices_has_allow_or_deny(struct lxc_handler *h, +static bool cgroup_devices_has_allow_or_deny(struct cgfs_data *d, char *v, bool for_allow) { char *path; @@ -1898,7 +1901,7 @@ static bool cgroup_devices_has_allow_or_deny(struct lxc_handler *h, if (!for_allow && strcmp(v, "a") != 0 && strcmp(v, "a *:* rwm") != 0) return false; - parts[0] = (const char *)lxc_cgroup_get_hierarchy_abs_path_handler("devices", h); + parts[0] = (const char *)lxc_cgroup_get_hierarchy_abs_path_data("devices", d); if (!parts[0]) return false; path = lxc_string_join("/", parts, false); @@ -2167,124 +2170,127 @@ static bool init_cpuset_if_needed(struct cgroup_mount_point *mp, do_init_cpuset_file(mp, path, "/cpuset.mems") ); } -extern void lxc_monitor_send_state(const char *name, lxc_state_t state, - const char *lxcpath); -int do_unfreeze(int freeze, const char *name, const char *lxcpath) +struct cgroup_ops *cgfs_ops_init(void) { - char v[100]; - const char *state = freeze ? "FROZEN" : "THAWED"; - - if (lxc_cgroup_set("freezer.state", state, name, lxcpath) < 0) { - ERROR("Failed to freeze %s:%s", lxcpath, name); - return -1; - } - while (1) { - if (lxc_cgroup_get("freezer.state", v, 100, name, lxcpath) < 0) { - ERROR("Failed to get new freezer state for %s:%s", lxcpath, name); - return -1; - } - if (v[strlen(v)-1] == '\n') - v[strlen(v)-1] = '\0'; - if (strncmp(v, state, strlen(state)) == 0) { - if (name) - lxc_monitor_send_state(name, freeze ? FROZEN : THAWED, lxcpath); - return 0; - } - sleep(1); - } + return &cgfs_ops; } -int freeze_unfreeze(const char *name, int freeze, const char *lxcpath) +static void *cgfs_init(const char *name) { - return do_unfreeze(freeze, name, lxcpath); -} + struct cgfs_data *d; -lxc_state_t freezer_state(const char *name, const char *lxcpath) -{ - char v[100]; - if (lxc_cgroup_get("freezer.state", v, 100, name, lxcpath) < 0) - return -1; + d = malloc(sizeof(*d)); + if (!d) + return NULL; - if (v[strlen(v)-1] == '\n') - v[strlen(v)-1] = '\0'; - return lxc_str2state(v); + memset(d, 0, sizeof(*d)); + d->name = strdup(name); + if (!d->name) + goto err1; + + /* if we are running as root, use system cgroup pattern, otherwise + * just create a cgroup under the current one. But also fall back to + * that if for some reason reading the configuration fails and no + * default value is available + */ + if (geteuid() == 0) + d->cgroup_pattern = lxc_global_config_value("lxc.cgroup.pattern"); + if (!d->cgroup_pattern) + d->cgroup_pattern = "%n"; + + d->meta = lxc_cgroup_load_meta(); + if (!d->meta) { + ERROR("cgroupfs failed to detect cgroup metadata"); + goto err2; + } + return d; + +err2: + free(d->name); +err1: + free(d); + return NULL; } -static void cgfs_destroy(struct lxc_handler *handler) +static void cgfs_destroy(void *hdata) { - struct cgfs_data *d = handler->cgroup_info->data; + struct cgfs_data *d = hdata; + if (!d) return; + if (d->name) + free(d->name); if (d->info) lxc_cgroup_process_info_free_and_remove(d->info); if (d->meta) lxc_cgroup_put_meta(d->meta); free(d); - handler->cgroup_info->data = NULL; } -static inline bool cgfs_init(struct lxc_handler *handler) +static inline bool cgfs_create(void *hdata) { - struct cgfs_data *d = malloc(sizeof(*d)); - if (!d) - return false; - d->info = NULL; - d->meta = lxc_cgroup_load_meta(); + struct cgfs_data *d = hdata; + struct cgroup_process_info *i; + struct cgroup_meta_data *md; - if (!d->meta) { - ERROR("cgroupfs failed to detect cgroup metadata"); - free(d); + if (!d) return false; - } - handler->cgroup_info->data = d; - return true; -} - -static inline bool cgfs_create(struct lxc_handler *handler) -{ - struct cgfs_data *d = handler->cgroup_info->data; - struct cgroup_process_info *i; - struct cgroup_meta_data *md = d->meta; - i = lxc_cgroupfs_create(handler->name, handler->cgroup_info->cgroup_pattern, md, NULL); + md = d->meta; + i = lxc_cgroupfs_create(d->name, d->cgroup_pattern, md, NULL); if (!i) return false; d->info = i; return true; } -static inline bool cgfs_enter(struct lxc_handler *handler) +static inline bool cgfs_enter(void *hdata, pid_t pid) { - struct cgfs_data *d = handler->cgroup_info->data; - struct cgroup_process_info *i = d->info; + struct cgfs_data *d = hdata; + struct cgroup_process_info *i; int ret; - - ret = lxc_cgroupfs_enter(i, handler->pid, false); + + if (!d) + return false; + i = d->info; + ret = lxc_cgroupfs_enter(i, pid, false); return ret == 0; } -static inline bool cgfs_create_legacy(struct lxc_handler *handler) +static inline bool cgfs_create_legacy(void *hdata, pid_t pid) { - struct cgfs_data *d = handler->cgroup_info->data; - struct cgroup_process_info *i = d->info; - if (lxc_cgroup_create_legacy(i, handler->name, handler->pid) < 0) { - ERROR("failed to create legacy ns cgroups for '%s'", handler->name); + struct cgfs_data *d = hdata; + struct cgroup_process_info *i; + + if (!d) + return false; + i = d->info; + if (lxc_cgroup_create_legacy(i, d->name, pid) < 0) { + ERROR("failed to create legacy ns cgroups for '%s'", d->name); return false; } return true; } -static char *cgfs_get_cgroup(struct lxc_handler *handler, const char *subsystem) +static const char *cgfs_get_cgroup(void *hdata, const char *subsystem) { - return lxc_cgroup_get_hierarchy_path_handler(subsystem, handler); + struct cgfs_data *d = hdata; + + if (!d) + return NULL; + return lxc_cgroup_get_hierarchy_path_data(subsystem, d); } -static bool cgfs_unfreeze_fromhandler(struct lxc_handler *handler) +static bool cgfs_unfreeze(void *hdata) { + struct cgfs_data *d = hdata; char *cgabspath, *cgrelpath; int ret; - cgrelpath = lxc_cgroup_get_hierarchy_path_handler("freezer", handler); + if (!d) + return false; + + cgrelpath = lxc_cgroup_get_hierarchy_path_data("freezer", d); cgabspath = lxc_cgroup_find_abs_path("freezer", cgrelpath, true, NULL); if (!cgabspath) return false; @@ -2294,12 +2300,17 @@ static bool cgfs_unfreeze_fromhandler(struct lxc_handler *handler) return ret == 0; } -bool cgroupfs_setup_limits(struct lxc_handler *h, bool with_devices) +static bool cgroupfs_setup_limits(void *hdata, struct lxc_list *cgroup_conf, + bool with_devices) { - return do_setup_cgroup_limits(h, &h->conf->cgroup, with_devices) == 0; + struct cgfs_data *d = hdata; + + if (!d) + return false; + return do_setup_cgroup_limits(d, cgroup_conf, with_devices) == 0; } -bool lxc_cgroupfs_attach(const char *name, const char *lxcpath, pid_t pid) +static bool lxc_cgroupfs_attach(const char *name, const char *lxcpath, pid_t pid) { struct cgroup_meta_data *meta_data; struct cgroup_process_info *container_info; @@ -2328,134 +2339,19 @@ bool lxc_cgroupfs_attach(const char *name, const char *lxcpath, pid_t pid) } static struct cgroup_ops cgfs_ops = { - .destroy = cgfs_destroy, .init = cgfs_init, + .destroy = cgfs_destroy, .create = cgfs_create, .enter = cgfs_enter, .create_legacy = cgfs_create_legacy, .get_cgroup = cgfs_get_cgroup, .get = lxc_cgroupfs_get, .set = lxc_cgroupfs_set, - .unfreeze_fromhandler = cgfs_unfreeze_fromhandler, + .unfreeze = cgfs_unfreeze, .setup_limits = cgroupfs_setup_limits, .name = "cgroupfs", .attach = lxc_cgroupfs_attach, .chown = NULL, .mount_cgroup = cgroupfs_mount_cgroup, + .nrtasks = cgfs_nrtasks, }; -static void init_cg_ops(void) -{ - if (!use_cgmanager) - return; - if (cgmanager_initialized) - return; - if (!lxc_init_cgmanager()) { - ERROR("Could not contact cgroup manager, falling back to cgroupfs"); - active_cg_ops = &cgfs_ops; - } -} - -/* - * These are the backend-independent cgroup handlers for container - * start and stop - */ - -/* Free all cgroup info held by the handler */ -void cgroup_destroy(struct lxc_handler *handler) -{ - if (!handler->cgroup_info) - return; - if (active_cg_ops) - active_cg_ops->destroy(handler); -} - -/* - * Allocate a lxc_cgroup_info for the active cgroup - * backend, and assign it to the handler - */ -bool cgroup_init(struct lxc_handler *handler) -{ - init_cg_ops(); - handler->cgroup_info = malloc(sizeof(struct lxc_cgroup_info)); - if (!handler->cgroup_info) - return false; - memset(handler->cgroup_info, 0, sizeof(struct lxc_cgroup_info)); - /* if we are running as root, use system cgroup pattern, otherwise - * just create a cgroup under the current one. But also fall back to - * that if for some reason reading the configuration fails and no - * default value is available - */ - if (geteuid() == 0) - handler->cgroup_info->cgroup_pattern = lxc_global_config_value("lxc.cgroup.pattern"); - if (!handler->cgroup_info->cgroup_pattern) - handler->cgroup_info->cgroup_pattern = "%n"; - - return active_cg_ops->init(handler); -} - -/* Create the container cgroups for all requested controllers */ -bool cgroup_create(struct lxc_handler *handler) -{ - return active_cg_ops->create(handler); -} - -/* - * Enter the container init into its new cgroups for all - * requested controllers */ -bool cgroup_enter(struct lxc_handler *handler) -{ - return active_cg_ops->enter(handler); -} - -bool cgroup_create_legacy(struct lxc_handler *handler) -{ - if (active_cg_ops->create_legacy) - return active_cg_ops->create_legacy(handler); - return true; -} - -char *cgroup_get_cgroup(struct lxc_handler *handler, const char *subsystem) -{ - return active_cg_ops->get_cgroup(handler, subsystem); -} - -int lxc_cgroup_set(const char *filename, const char *value, const char *name, const char *lxcpath) -{ - init_cg_ops(); - return active_cg_ops->set(filename, value, name, lxcpath); -} - -int lxc_cgroup_get(const char *filename, char *value, size_t len, const char *name, const char *lxcpath) -{ - init_cg_ops(); - return active_cg_ops->get(filename, value, len, name, lxcpath); -} - -bool lxc_unfreeze_fromhandler(struct lxc_handler *handler) -{ - return active_cg_ops->unfreeze_fromhandler(handler); -} - -bool cgroup_setup_limits(struct lxc_handler *handler, bool with_devices) -{ - return active_cg_ops->setup_limits(handler, with_devices); -} - -bool cgroup_chown(struct lxc_handler *handler) -{ - if (active_cg_ops->chown) - return active_cg_ops->chown(handler); - return true; -} - -bool lxc_cgroup_attach(const char *name, const char *lxcpath, pid_t pid) -{ - init_cg_ops(); - return active_cg_ops->attach(name, lxcpath, pid); -} - -bool lxc_setup_mount_cgroup(const char *root, - struct lxc_cgroup_info *cgroup_info, int type) -{ - return active_cg_ops->mount_cgroup(root, cgroup_info, type); -} diff --git a/src/lxc/cgmanager.c b/src/lxc/cgmanager.c index a3faac1e8..6798b6b84 100644 --- a/src/lxc/cgmanager.c +++ b/src/lxc/cgmanager.c @@ -58,24 +58,74 @@ lxc_log_define(lxc_cgmanager, lxc); #include #include #include -NihDBusProxy *cgroup_manager = NULL; -extern struct cgroup_ops *active_cg_ops; -bool cgmanager_initialized = false; -bool use_cgmanager = true; +struct cgm_data { + char *name; + char *cgroup_path; + const char *cgroup_pattern; +}; + +static NihDBusProxy *cgroup_manager = NULL; static struct cgroup_ops cgmanager_ops; static int nr_subsystems; static char **subsystems; -bool lxc_init_cgmanager(void); -static void cgmanager_disconnected(DBusConnection *connection) +#define CGMANAGER_DBUS_SOCK "unix:path=/sys/fs/cgroup/cgmanager/sock" +static void cgm_dbus_disconnected(DBusConnection *connection); +static bool cgm_dbus_connect(void) +{ + DBusError dbus_error; + DBusConnection *connection; + dbus_error_init(&dbus_error); + + connection = nih_dbus_connect(CGMANAGER_DBUS_SOCK, cgm_dbus_disconnected); + if (!connection) { + NihError *nerr; + nerr = nih_error_get(); + DEBUG("Unable to open cgmanager connection at %s: %s", CGMANAGER_DBUS_SOCK, + nerr->message); + nih_free(nerr); + dbus_error_free(&dbus_error); + return false; + } + dbus_connection_set_exit_on_disconnect(connection, FALSE); + dbus_error_free(&dbus_error); + cgroup_manager = nih_dbus_proxy_new(NULL, connection, + NULL /* p2p */, + "/org/linuxcontainers/cgmanager", NULL, NULL); + dbus_connection_unref(connection); + if (!cgroup_manager) { + NihError *nerr; + nerr = nih_error_get(); + ERROR("Error opening cgmanager proxy: %s", nerr->message); + nih_free(nerr); + return false; + } + + // force fd passing negotiation + if (cgmanager_ping_sync(NULL, cgroup_manager, 0) != 0) { + NihError *nerr; + nerr = nih_error_get(); + ERROR("Error pinging cgroup manager: %s", nerr->message); + nih_free(nerr); + } + return true; +} + +static void cgm_dbus_disconnect(void) +{ + nih_free(cgroup_manager); + cgroup_manager = NULL; +} + +static void cgm_dbus_disconnected(DBusConnection *connection) { WARN("Cgroup manager connection was terminated"); cgroup_manager = NULL; - cgmanager_initialized = false; - if (lxc_init_cgmanager()) { - cgmanager_initialized = true; + if (cgm_dbus_connect()) { INFO("New cgroup manager connection was opened"); + } else { + WARN("Cgroup manager unable to re-open connection"); } } @@ -115,47 +165,6 @@ static int send_creds(int sock, int rpid, int ruid, int rgid) return 0; } -#define CGMANAGER_DBUS_SOCK "unix:path=/sys/fs/cgroup/cgmanager/sock" -bool lxc_init_cgmanager(void) -{ - DBusError dbus_error; - DBusConnection *connection; - dbus_error_init(&dbus_error); - - connection = nih_dbus_connect(CGMANAGER_DBUS_SOCK, cgmanager_disconnected); - if (!connection) { - NihError *nerr; - nerr = nih_error_get(); - ERROR("Error opening cgmanager connection at %s: %s", CGMANAGER_DBUS_SOCK, - nerr->message); - nih_free(nerr); - dbus_error_free(&dbus_error); - return false; - } - dbus_connection_set_exit_on_disconnect(connection, FALSE); - dbus_error_free(&dbus_error); - cgroup_manager = nih_dbus_proxy_new(NULL, connection, - NULL /* p2p */, - "/org/linuxcontainers/cgmanager", NULL, NULL); - dbus_connection_unref(connection); - if (!cgroup_manager) { - NihError *nerr; - nerr = nih_error_get(); - ERROR("Error opening cgmanager proxy: %s", nerr->message); - nih_free(nerr); - return false; - } - active_cg_ops = &cgmanager_ops; - // force fd passing negotiation - if (cgmanager_ping_sync(NULL, cgroup_manager, 0) != 0) { - NihError *nerr; - nerr = nih_error_get(); - ERROR("Error pinging cgroup manager: %s", nerr->message); - nih_free(nerr); - } - return true; -} - static bool lxc_cgmanager_create(const char *controller, const char *cgroup_path, int32_t *existed) { if ( cgmanager_create_sync(NULL, cgroup_manager, controller, @@ -341,19 +350,49 @@ static void cgm_remove_cgroup(const char *controller, const char *path) INFO("cgroup removal attempt: %s:%s did not exist", controller, path); } -static void cgm_destroy(struct lxc_handler *handler) +static void *cgm_init(const char *name) { - char *cgroup_path = handler->cgroup_info->data; + struct cgm_data *d; + + d = malloc(sizeof(*d)); + if (!d) + return NULL; + + memset(d, 0, sizeof(*d)); + d->name = strdup(name); + if (!d->name) + goto err1; + + /* if we are running as root, use system cgroup pattern, otherwise + * just create a cgroup under the current one. But also fall back to + * that if for some reason reading the configuration fails and no + * default value is available + */ + if (geteuid() == 0) + d->cgroup_pattern = lxc_global_config_value("lxc.cgroup.pattern"); + if (!d->cgroup_pattern) + d->cgroup_pattern = "%n"; + return d; + +err1: + free(d); + return NULL; +} + +static void cgm_destroy(void *hdata) +{ + struct cgm_data *d = hdata; int i; - if (!cgroup_path) + if (!d) return; - for (i = 0; i < nr_subsystems; i++) - cgm_remove_cgroup(subsystems[i], cgroup_path); + cgm_remove_cgroup(subsystems[i], d->cgroup_path); - free(cgroup_path); - handler->cgroup_info->data = NULL; + free(d->name); + if (d->cgroup_path) + free(d->cgroup_path); + free(d); } /* @@ -366,19 +405,21 @@ static inline void cleanup_cgroups(char *path) cgm_remove_cgroup(subsystems[i], path); } -static inline bool cgm_create(struct lxc_handler *handler) +static inline bool cgm_create(void *hdata) { + struct cgm_data *d = hdata; int i, index=0, baselen, ret; int32_t existed; - char result[MAXPATHLEN], *tmp; - char *cgroup_path = handler->cgroup_info->data; + char result[MAXPATHLEN], *tmp, *cgroup_path; + if (!d) + return false; // XXX we should send a hint to the cgmanager that when these // cgroups become empty they should be deleted. Requires a cgmanager // extension memset(result, 0, MAXPATHLEN); - tmp = lxc_string_replace("%n", handler->name, handler->cgroup_info->cgroup_pattern); + tmp = lxc_string_replace("%n", d->name, d->cgroup_pattern); if (!tmp) return false; if (strlen(tmp) > MAXPATHLEN) @@ -415,7 +456,7 @@ again: cleanup_cgroups(tmp); return false; } - handler->cgroup_info->data = cgroup_path; + d->cgroup_path = cgroup_path; return true; next: cleanup_cgroups(tmp); @@ -454,19 +495,25 @@ static bool do_cgm_enter(pid_t pid, const char *cgroup_path) return true; } -static inline bool cgm_enter(struct lxc_handler *handler) +static inline bool cgm_enter(void *hdata, pid_t pid) { - char *cgroup_path = handler->cgroup_info->data; - return do_cgm_enter(handler->pid, cgroup_path); + struct cgm_data *d = hdata; + + if (!d || !d->cgroup_path) + return false; + return do_cgm_enter(pid, d->cgroup_path); } -static char *cgm_get_cgroup(struct lxc_handler *handler, const char *subsystem) +static const char *cgm_get_cgroup(void *hdata, const char *subsystem) { - char *cgroup_path = handler->cgroup_info->data; - return cgroup_path; + struct cgm_data *d = hdata; + + if (!d || !d->cgroup_path) + return NULL; + return d->cgroup_path; } -int cgm_get(const char *filename, char *value, size_t len, const char *name, const char *lxcpath) +static int cgm_get(const char *filename, char *value, size_t len, const char *name, const char *lxcpath) { char *result, *controller, *key, *cgroup; size_t newlen; @@ -531,7 +578,7 @@ static int cgm_do_set(const char *controller, const char *file, return ret; } -int cgm_set(const char *filename, const char *value, const char *name, const char *lxcpath) +static int cgm_set(const char *filename, const char *value, const char *name, const char *lxcpath) { char *controller, *key, *cgroup; int ret; @@ -555,10 +602,21 @@ int cgm_set(const char *filename, const char *value, const char *name, const cha return ret; } +static void free_subsystems(void) +{ + int i; + + for (i = 0; i < nr_subsystems; i++) + free(subsystems[i]); + free(subsystems); + subsystems = NULL; + nr_subsystems = 0; +} + static bool collect_subsytems(void) { char *line = NULL, *tab1; - size_t sz = 0, i; + size_t sz = 0; FILE *f; if (subsystems) // already initialized @@ -598,51 +656,62 @@ static bool collect_subsytems(void) out_free: fclose(f); - for (i = 0; i < nr_subsystems; i++) - free(subsystems[i]); - free(subsystems); - subsystems = NULL; - nr_subsystems = 0; + free_subsystems(); return false; } -static inline bool cgm_init(struct lxc_handler *handler) +struct cgroup_ops *cgm_ops_init(void) { if (!collect_subsytems()) - return false; - if (geteuid()) - return true; + return NULL; + if (!cgm_dbus_connect()) + goto err1; + // root; try to escape to root cgroup - return lxc_cgmanager_escape(); + if (geteuid() == 0 && !lxc_cgmanager_escape()) + goto err2; + + return &cgmanager_ops; + +err2: + cgm_dbus_disconnect(); +err1: + free_subsystems(); + return NULL; } -static bool cgm_unfreeze_fromhandler(struct lxc_handler *handler) +static bool cgm_unfreeze(void *hdata) { - char *cgroup_path = handler->cgroup_info->data; + struct cgm_data *d = hdata; - if (cgmanager_set_value_sync(NULL, cgroup_manager, "freezer", cgroup_path, + if (!d || !d->cgroup_path) + return false; + + if (cgmanager_set_value_sync(NULL, cgroup_manager, "freezer", d->cgroup_path, "freezer.state", "THAWED") != 0) { NihError *nerr; nerr = nih_error_get(); ERROR("call to cgmanager_set_value_sync failed: %s", nerr->message); nih_free(nerr); - ERROR("Error unfreezing %s", cgroup_path); + ERROR("Error unfreezing %s", d->cgroup_path); return false; } return true; } -static bool setup_limits(struct lxc_handler *h, bool do_devices) +static bool cgm_setup_limits(void *hdata, struct lxc_list *cgroup_settings, bool do_devices) { + struct cgm_data *d = hdata; struct lxc_list *iterator; struct lxc_cgroup *cg; bool ret = false; - struct lxc_list *cgroup_settings = &h->conf->cgroup; - char *cgroup_path = h->cgroup_info->data; if (lxc_list_empty(cgroup_settings)) return true; + if (!d || !d->cgroup_path) + return false; + lxc_list_for_each(iterator, cgroup_settings) { char controller[100], *p; cg = iterator->elem; @@ -654,10 +723,10 @@ static bool setup_limits(struct lxc_handler *h, bool do_devices) p = strchr(controller, '.'); if (p) *p = '\0'; - if (cgm_do_set(controller, cg->subsystem, cgroup_path + if (cgm_do_set(controller, cg->subsystem, d->cgroup_path , cg->value) < 0) { ERROR("Error setting %s to %s for %s\n", - cg->subsystem, cg->value, h->name); + cg->subsystem, cg->value, d->name); goto out; } @@ -670,20 +739,17 @@ out: return ret; } -static bool cgm_setup_limits(struct lxc_handler *handler, bool with_devices) -{ - return setup_limits(handler, with_devices); -} - -static bool cgm_chown(struct lxc_handler *handler) +static bool cgm_chown(void *hdata, struct lxc_conf *conf) { - char *cgroup_path = handler->cgroup_info->data; + struct cgm_data *d = hdata; int i; + if (!d || !d->cgroup_path) + return false; for (i = 0; i < nr_subsystems; i++) { - if (!chown_cgroup(subsystems[i], cgroup_path, handler->conf)) + if (!chown_cgroup(subsystems[i], d->cgroup_path, conf)) WARN("Failed to chown %s:%s to container root", - subsystems[i], cgroup_path); + subsystems[i], d->cgroup_path); } return true; } @@ -771,8 +837,7 @@ static bool cgm_bind_dir(const char *root, const char *dirname) */ #define CGMANAGER_LOWER_SOCK "/sys/fs/cgroup/cgmanager.lower" #define CGMANAGER_UPPER_SOCK "/sys/fs/cgroup/cgmanager" -static bool cgm_mount_cgroup(const char *root, - struct lxc_cgroup_info *cgroup_info, int type) +static bool cgm_mount_cgroup(void *hdata, const char *root, int type) { if (dir_exists(CGMANAGER_LOWER_SOCK)) return cgm_bind_dir(root, CGMANAGER_LOWER_SOCK); @@ -783,19 +848,20 @@ static bool cgm_mount_cgroup(const char *root, } static struct cgroup_ops cgmanager_ops = { - .destroy = cgm_destroy, .init = cgm_init, + .destroy = cgm_destroy, .create = cgm_create, .enter = cgm_enter, .create_legacy = NULL, .get_cgroup = cgm_get_cgroup, .get = cgm_get, .set = cgm_set, - .unfreeze_fromhandler = cgm_unfreeze_fromhandler, + .unfreeze = cgm_unfreeze, .setup_limits = cgm_setup_limits, .name = "cgmanager", .chown = cgm_chown, .attach = cgm_attach, .mount_cgroup = cgm_mount_cgroup, + .nrtasks = NULL, }; #endif diff --git a/src/lxc/cgroup.c b/src/lxc/cgroup.c new file mode 100644 index 000000000..b03f69de6 --- /dev/null +++ b/src/lxc/cgroup.c @@ -0,0 +1,169 @@ +/* + * lxc: linux Container library + * + * (C) Copyright IBM Corp. 2007, 2008 + * + * Authors: + * Daniel Lezcano + * + * 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 + */ + +#include "cgroup.h" +#include "conf.h" +#include "log.h" +#include "start.h" + +lxc_log_define(lxc_cgroup, lxc); + +static struct cgroup_ops *ops = NULL; + +extern struct cgroup_ops *cgfs_ops_init(void); +extern struct cgroup_ops *cgm_ops_init(void); + +__attribute__((constructor)) +void cgroup_ops_init(void) +{ + if (ops) { + INFO("cgroup driver %s", ops->name); + return; + } + + DEBUG("cgroup_init"); + #if HAVE_CGMANAGER + ops = cgm_ops_init(); + #endif + if (!ops) + ops = cgfs_ops_init(); + if (ops) + INFO("Initialized cgroup driver %s", ops->name); +} + +bool cgroup_init(struct lxc_handler *handler) +{ + if (handler->cgroup_data) { + ERROR("cgroup_init called on already inited handler"); + return true; + } + + if (ops) { + INFO("cgroup driver %s initing for %s", ops->name, handler->name); + handler->cgroup_data = ops->init(handler->name); + } + return handler->cgroup_data != NULL; +} + +void cgroup_destroy(struct lxc_handler *handler) +{ + if (ops) { + ops->destroy(handler->cgroup_data); + handler->cgroup_data = NULL; + } +} + +/* Create the container cgroups for all requested controllers */ +bool cgroup_create(struct lxc_handler *handler) +{ + if (ops) + return ops->create(handler->cgroup_data); + return false; +} + +/* + * Enter the container init into its new cgroups for all + * requested controllers + */ +bool cgroup_enter(struct lxc_handler *handler) +{ + if (ops) + return ops->enter(handler->cgroup_data, handler->pid); + return false; +} + +bool cgroup_create_legacy(struct lxc_handler *handler) +{ + if (ops && ops->create_legacy) + return ops->create_legacy(handler->cgroup_data, handler->pid); + return true; +} + +const char *cgroup_get_cgroup(struct lxc_handler *handler, const char *subsystem) +{ + if (ops) + return ops->get_cgroup(handler->cgroup_data, subsystem); + return NULL; +} + +bool cgroup_unfreeze(struct lxc_handler *handler) +{ + if (ops) + return ops->unfreeze(handler->cgroup_data); + return false; +} + +bool cgroup_setup_limits(struct lxc_handler *handler, bool with_devices) +{ + if (ops) + return ops->setup_limits(handler->cgroup_data, + &handler->conf->cgroup, with_devices); + return false; +} + +bool cgroup_chown(struct lxc_handler *handler) +{ + if (ops && ops->chown) + return ops->chown(handler->cgroup_data, handler->conf); + return true; +} + +bool cgroup_mount(const char *root, struct lxc_handler *handler, int type) +{ + if (ops) { + return ops->mount_cgroup(handler->cgroup_data, root, type); + } + return false; +} + +int cgroup_nrtasks(struct lxc_handler *handler) +{ + if (ops) { + if (ops->nrtasks) + return ops->nrtasks(handler->cgroup_data); + else + WARN("CGROUP driver %s doesn't implement nrtasks", ops->name); + } + return -1; +} + +bool cgroup_attach(const char *name, const char *lxcpath, pid_t pid) +{ + if (ops) + return ops->attach(name, lxcpath, pid); + return false; +} + +int lxc_cgroup_set(const char *filename, const char *value, const char *name, const char *lxcpath) +{ + if (ops) + return ops->set(filename, value, name, lxcpath); + return -1; +} + +int lxc_cgroup_get(const char *filename, char *value, size_t len, const char *name, const char *lxcpath) +{ + if (ops) + return ops->get(filename, value, len, name, lxcpath); + return -1; +} diff --git a/src/lxc/cgroup.h b/src/lxc/cgroup.h index da77473cb..6f9e5f645 100644 --- a/src/lxc/cgroup.h +++ b/src/lxc/cgroup.h @@ -20,195 +20,39 @@ * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef _ncgroup_h -#define _ncgroup_h -#include -#include -#include - -#include "state.h" - -struct cgroup_hierarchy; -struct cgroup_meta_data; -struct cgroup_mount_point; - -/* - * cgroup_meta_data: the metadata about the cgroup infrastructure on this - * host - */ -struct cgroup_meta_data { - ptrdiff_t ref; /* simple refcount */ - struct cgroup_hierarchy **hierarchies; - struct cgroup_mount_point **mount_points; - int maximum_hierarchy; -}; - -/* - * cgroup_hierarchy: describes a single cgroup hierarchy - * (may have multiple mount points) - */ -struct cgroup_hierarchy { - int index; - bool used; /* false if the hierarchy should be ignored by lxc */ - char **subsystems; - struct cgroup_mount_point *rw_absolute_mount_point; - struct cgroup_mount_point *ro_absolute_mount_point; - struct cgroup_mount_point **all_mount_points; - size_t all_mount_point_capacity; -}; - -/* - * cgroup_mount_point: a mount point to where a hierarchy - * is mounted to - */ -struct cgroup_mount_point { - struct cgroup_hierarchy *hierarchy; - char *mount_point; - char *mount_prefix; - bool read_only; - bool need_cpuset_init; -}; - -/* - * cgroup_process_info: describes the membership of a - * process to the different cgroup - * hierarchies - * - * Note this is the per-process info tracked by the cgfs_ops. - * This is not used with cgmanager. - */ -struct cgroup_process_info { - struct cgroup_process_info *next; - struct cgroup_meta_data *meta_ref; - struct cgroup_hierarchy *hierarchy; - char *cgroup_path; - char *cgroup_path_sub; - char **created_paths; - size_t created_paths_capacity; - size_t created_paths_count; - struct cgroup_mount_point *designated_mount_point; -}; - -/* meta data management: - * lxc_cgroup_load_meta loads the meta data (using subsystem - * whitelist from main lxc configuration) - * lxc_cgroup_load_meta2 does the same, but allows one to specify - * a custom whitelist - * lxc_cgroup_get_meta increments the refcount of a meta data - * object - * lxc_cgroup_put_meta decrements the refcount of a meta data - * object, potentially destroying it - */ -extern struct cgroup_meta_data *lxc_cgroup_load_meta(); -extern struct cgroup_meta_data *lxc_cgroup_load_meta2(const char **subsystem_whitelist); -extern struct cgroup_meta_data *lxc_cgroup_get_meta(struct cgroup_meta_data *meta_data); -extern struct cgroup_meta_data *lxc_cgroup_put_meta(struct cgroup_meta_data *meta_data); - -/* find the hierarchy corresponding to a given subsystem */ -extern struct cgroup_hierarchy *lxc_cgroup_find_hierarchy(struct cgroup_meta_data *meta_data, const char *subsystem); - -/* find a mount point for a given hierarchy that has access to the cgroup in 'cgroup' and (if wanted) is writable */ -extern struct cgroup_mount_point *lxc_cgroup_find_mount_point(struct cgroup_hierarchy *hierarchy, const char *group, bool should_be_writable); - -/* all-in-one: find a mount point for a given hierarchy that has access to the cgroup and return the correct path within */ -extern char *lxc_cgroup_find_abs_path(const char *subsystem, const char *group, bool should_be_writable, const char *suffix); -/* determine the cgroup membership of a given process */ -extern struct cgroup_process_info *lxc_cgroup_process_info_get(pid_t pid, struct cgroup_meta_data *meta); -extern struct cgroup_process_info *lxc_cgroup_process_info_get_init(struct cgroup_meta_data *meta); -extern struct cgroup_process_info *lxc_cgroup_process_info_get_self(struct cgroup_meta_data *meta); +#ifndef __lxc_cgroup_h +#define __lxc_cgroup_h -/* create a new cgroup */ -extern struct cgroup_process_info *lxc_cgroup_create(const char *name, const char *path_pattern, struct cgroup_meta_data *meta_data, const char *sub_pattern); -extern int lxc_cgroup_create_legacy(struct cgroup_process_info *base_info, const char *name, pid_t pid); - -/* get the cgroup membership of a given container */ -extern struct cgroup_process_info *lxc_cgroup_get_container_info(const char *name, const char *lxcpath, struct cgroup_meta_data *meta_data); - -/* move a processs to the cgroups specified by the membership TODO - deprecated, switch users to cgroup_enter() */ -extern int lxc_cgroupfs_enter(struct cgroup_process_info *info, pid_t pid, bool enter_sub); - -/* free process membership information */ -extern void lxc_cgroup_process_info_free(struct cgroup_process_info *info); -extern void lxc_cgroup_process_info_free_and_remove(struct cgroup_process_info *info); +#include +#include +#include struct lxc_handler; -extern char *lxc_cgroup_get_hierarchy_path(const char *subsystem, const char *name, const char *lxcpath); -extern char *lxc_cgroup_get_hierarchy_abs_path_handler(const char *subsystem, struct lxc_handler *handler); -extern char *lxc_cgroup_get_hierarchy_abs_path(const char *subsystem, const char *name, const char *lxcpath); -extern int lxc_cgroup_set_handler(const char *filename, const char *value, struct lxc_handler *handler); -extern int lxc_cgroup_get_handler(const char *filename, char *value, size_t len, struct lxc_handler *handler); - -/* - * lxc_cgroup_path_get: Get the absolute pathname for a cgroup - * file for a running container. - * - * @filename : the file of interest (e.g. "freezer.state") or - * the subsystem name (e.g. "freezer") in which case - * the directory where the cgroup may be modified - * will be returned - * @name : name of container to connect to - * @lxcpath : the lxcpath in which the container is running - * - * This is the exported function, which determines cgpath from the - * lxc-start of the @name container running in @lxcpath. - * - * Returns path on success, NULL on error. The caller must free() - * the returned path. - */ -extern char *lxc_cgroup_path_get(const char *filename, const char *name, - const char *lxcpath); - +struct lxc_conf; struct lxc_list; -extern int lxc_setup_cgroup_without_devices(struct lxc_handler *h, struct lxc_list *cgroup_settings); -extern int lxc_setup_cgroup_devices(struct lxc_handler *h, struct lxc_list *cgroup_settings); - -extern int lxc_cgroup_nrtasks_handler(struct lxc_handler *handler); -extern int do_unfreeze(int freeze, const char *name, const char *lxcpath); -extern int freeze_unfreeze(const char *name, int freeze, const char *lxcpath); -extern const char *lxc_state2str(lxc_state_t state); -extern lxc_state_t freezer_state(const char *name, const char *lxcpath); - -/* - * cgroup-related data for backend use in start/stop of a - * container. This is tacked to the lxc_handler. - */ -struct lxc_cgroup_info { - /* handlers to actually do the cgroup stuff */ - struct cgroup_ops *ops; - /* extra data for the cgroup_ops, i.e. mountpoints for fs backend */ - void *data; - const char *cgroup_pattern; -}; - -/* per-backend cgroup hooks */ struct cgroup_ops { - void (*destroy)(struct lxc_handler *handler); - bool (*init)(struct lxc_handler *handler); - bool (*create)(struct lxc_handler *handler); - bool (*enter)(struct lxc_handler *handler); - bool (*create_legacy)(struct lxc_handler *handler); - char *(*get_cgroup)(struct lxc_handler *handler, const char *subsystem); + const char *name; + + void *(*init)(const char *name); + void (*destroy)(void *hdata); + bool (*create)(void *hdata); + bool (*enter)(void *hdata, pid_t pid); + bool (*create_legacy)(void *hdata, pid_t pid); + const char *(*get_cgroup)(void *hdata, const char *subsystem); int (*set)(const char *filename, const char *value, const char *name, const char *lxcpath); int (*get)(const char *filename, char *value, size_t len, const char *name, const char *lxcpath); - bool (*unfreeze_fromhandler)(struct lxc_handler *handler); - bool (*setup_limits)(struct lxc_handler *handler, bool with_devices); - bool (*chown)(struct lxc_handler *handler); + bool (*unfreeze)(void *hdata); + bool (*setup_limits)(void *hdata, struct lxc_list *cgroup_conf, bool with_devices); + bool (*chown)(void *hdata, struct lxc_conf *conf); bool (*attach)(const char *name, const char *lxcpath, pid_t pid); - bool (*mount_cgroup)(const char *root, struct lxc_cgroup_info *info, - int type); - const char *name; -}; - -struct cgfs_data { - struct cgroup_meta_data *meta; - struct cgroup_process_info *info; + bool (*mount_cgroup)(void *hdata, const char *root, int type); + int (*nrtasks)(void *hdata); }; -/* - * backend-independent cgroup handlers - */ +extern bool cgroup_attach(const char *name, const char *lxcpath, pid_t pid); +extern bool cgroup_mount(const char *root, struct lxc_handler *handler, int type); extern void cgroup_destroy(struct lxc_handler *handler); extern bool cgroup_init(struct lxc_handler *handler); extern bool cgroup_create(struct lxc_handler *handler); @@ -217,11 +61,8 @@ extern bool cgroup_chown(struct lxc_handler *handler); extern bool cgroup_enter(struct lxc_handler *handler); extern void cgroup_cleanup(struct lxc_handler *handler); extern bool cgroup_create_legacy(struct lxc_handler *handler); -extern char *cgroup_get_cgroup(struct lxc_handler *handler, const char *subsystem); -extern bool lxc_cgroup_attach(const char *name, const char *lxcpath, pid_t pid); -extern bool lxc_setup_mount_cgroup(const char *root, struct lxc_cgroup_info *cgroup_info, int type); -extern bool lxc_unfreeze_fromhandler(struct lxc_handler *handler); -extern int lxc_cgroup_set(const char *filename, const char *value, const char *name, const char *lxcpath); -extern int lxc_cgroup_get(const char *filename, char *value, size_t len, const char *name, const char *lxcpath); +extern int cgroup_nrtasks(struct lxc_handler *handler); +extern const char *cgroup_get_cgroup(struct lxc_handler *handler, const char *subsystem); +extern bool cgroup_unfreeze(struct lxc_handler *handler); #endif diff --git a/src/lxc/commands.c b/src/lxc/commands.c index e9ab42f4a..6b46c2ca7 100644 --- a/src/lxc/commands.c +++ b/src/lxc/commands.c @@ -430,7 +430,7 @@ static int lxc_cmd_get_cgroup_callback(int fd, struct lxc_cmd_req *req, struct lxc_handler *handler) { struct lxc_cmd_rsp rsp; - char *path; + const char *path; if (req->datalen < 1) return -1; @@ -439,7 +439,7 @@ static int lxc_cmd_get_cgroup_callback(int fd, struct lxc_cmd_req *req, if (!path) return -1; rsp.datalen = strlen(path) + 1, - rsp.data = path; + rsp.data = (char *)path; rsp.ret = 0; return lxc_cmd_rsp_send(fd, &rsp); @@ -590,7 +590,12 @@ static int lxc_cmd_stop_callback(int fd, struct lxc_cmd_req *req, memset(&rsp, 0, sizeof(rsp)); rsp.ret = kill(handler->pid, stopsignal); if (!rsp.ret) { - if (lxc_unfreeze_fromhandler(handler)) + /* we can't just use lxc_unfreeze() since we are already in the + * context of handling the STOP cmd in lxc-start, and calling + * lxc_unfreeze() would do another cmd (GET_CGROUP) which would + * deadlock us + */ + if (cgroup_unfreeze(handler)) return 0; ERROR("Failed to unfreeze %s:%s", handler->lxcpath, handler->name); rsp.ret = -1; diff --git a/src/lxc/conf.c b/src/lxc/conf.c index 3e16cc822..81dcb4201 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -63,7 +63,6 @@ #include "utils.h" #include "conf.h" #include "log.h" -#include "lxc.h" /* for lxc_cgroup_set() */ #include "caps.h" /* for lxc_caps_last_cap() */ #include "bdev.h" #include "cgroup.h" @@ -670,7 +669,7 @@ int pin_rootfs(const char *rootfs) return fd; } -static int lxc_mount_auto_mounts(struct lxc_conf *conf, int flags, struct lxc_cgroup_info *cgroup_info) +static int lxc_mount_auto_mounts(struct lxc_conf *conf, int flags, struct lxc_handler *handler) { int r; size_t i; @@ -744,8 +743,8 @@ static int lxc_mount_auto_mounts(struct lxc_conf *conf, int flags, struct lxc_cg } if (flags & LXC_AUTO_CGROUP_MASK) { - if (!lxc_setup_mount_cgroup(conf->rootfs.mount, cgroup_info, - flags & LXC_AUTO_CGROUP_MASK)) { + if (!cgroup_mount(conf->rootfs.mount, handler, + flags & LXC_AUTO_CGROUP_MASK)) { SYSERROR("error mounting /sys/fs/cgroup"); return -1; } @@ -3500,7 +3499,6 @@ int lxc_setup(struct lxc_handler *handler) struct lxc_conf *lxc_conf = handler->conf; const char *lxcpath = handler->lxcpath; void *data = handler->data; - struct lxc_cgroup_info *cgroup_info = handler->cgroup_info; if (lxc_conf->inherit_ns_fd[LXC_NS_UTS] == -1) { if (setup_utsname(lxc_conf->utsname)) { @@ -3538,7 +3536,7 @@ int lxc_setup(struct lxc_handler *handler) /* do automatic mounts (mainly /proc and /sys), but exclude * those that need to wait until other stuff has finished */ - if (lxc_mount_auto_mounts(lxc_conf, lxc_conf->auto_mounts & ~LXC_AUTO_CGROUP_MASK, cgroup_info) < 0) { + if (lxc_mount_auto_mounts(lxc_conf, lxc_conf->auto_mounts & ~LXC_AUTO_CGROUP_MASK, handler) < 0) { ERROR("failed to setup the automatic mounts for '%s'", name); return -1; } @@ -3557,7 +3555,7 @@ int lxc_setup(struct lxc_handler *handler) * before, /sys could not have been mounted * (is either mounted automatically or via fstab entries) */ - if (lxc_mount_auto_mounts(lxc_conf, lxc_conf->auto_mounts & LXC_AUTO_CGROUP_MASK, cgroup_info) < 0) { + if (lxc_mount_auto_mounts(lxc_conf, lxc_conf->auto_mounts & LXC_AUTO_CGROUP_MASK, handler) < 0) { ERROR("failed to setup the automatic mounts for '%s'", name); return -1; } diff --git a/src/lxc/freezer.c b/src/lxc/freezer.c index 89c7fab4e..c79f13951 100644 --- a/src/lxc/freezer.c +++ b/src/lxc/freezer.c @@ -35,18 +35,53 @@ #include "state.h" #include "monitor.h" #include "log.h" -#include "cgroup.h" +#include "lxc.h" lxc_log_define(lxc_freezer, lxc); +lxc_state_t freezer_state(const char *name, const char *lxcpath) +{ + char v[100]; + if (lxc_cgroup_get("freezer.state", v, 100, name, lxcpath) < 0) + return -1; + + if (v[strlen(v)-1] == '\n') + v[strlen(v)-1] = '\0'; + return lxc_str2state(v); +} + +static int do_freeze_thaw(int freeze, const char *name, const char *lxcpath) +{ + char v[100]; + const char *state = freeze ? "FROZEN" : "THAWED"; + + if (lxc_cgroup_set("freezer.state", state, name, lxcpath) < 0) { + ERROR("Failed to freeze %s:%s", lxcpath, name); + return -1; + } + while (1) { + if (lxc_cgroup_get("freezer.state", v, 100, name, lxcpath) < 0) { + ERROR("Failed to get new freezer state for %s:%s", lxcpath, name); + return -1; + } + if (v[strlen(v)-1] == '\n') + v[strlen(v)-1] = '\0'; + if (strncmp(v, state, strlen(state)) == 0) { + if (name) + lxc_monitor_send_state(name, freeze ? FROZEN : THAWED, lxcpath); + return 0; + } + sleep(1); + } +} int lxc_freeze(const char *name, const char *lxcpath) { lxc_monitor_send_state(name, FREEZING, lxcpath); - return freeze_unfreeze(name, 1, lxcpath); + return do_freeze_thaw(1, name, lxcpath); } int lxc_unfreeze(const char *name, const char *lxcpath) { - return freeze_unfreeze(name, 0, lxcpath); + return do_freeze_thaw(0, name, lxcpath); } diff --git a/src/lxc/lxc.h b/src/lxc/lxc.h index 56f9fe251..877564053 100644 --- a/src/lxc/lxc.h +++ b/src/lxc/lxc.h @@ -129,17 +129,6 @@ extern int lxc_unfreeze(const char *name, const char *lxcpath); */ extern lxc_state_t lxc_state(const char *name, const char *lxcpath); -struct lxc_handler; -/* - * Set a specified value for a specified subsystem. The specified - * subsystem must be fully specified, eg. "cpu.shares" - * @filename : the cgroup attribute filename - * @value : the value to be set - * @handler : the lxc_handler structure of the container - * Returns 0 on success, < 0 otherwise - */ -extern int lxc_cgroup_set_handler(const char *filename, const char *value, struct lxc_handler *handler); - /* * Set a specified value for a specified subsystem. The specified * subsystem must be fully specified, eg. "cpu.shares" diff --git a/src/lxc/lxcutmp.c b/src/lxc/lxcutmp.c index 15dd71ccc..24cfa7589 100644 --- a/src/lxc/lxcutmp.c +++ b/src/lxc/lxcutmp.c @@ -291,7 +291,7 @@ static int utmp_get_ntasks(struct lxc_handler *handler) { int ntasks; - ntasks = lxc_cgroup_nrtasks_handler(handler); + ntasks = cgroup_nrtasks(handler); if (ntasks < 0) { ERROR("failed to get the number of tasks"); diff --git a/src/lxc/start.h b/src/lxc/start.h index c30c66170..63fa8b45b 100644 --- a/src/lxc/start.h +++ b/src/lxc/start.h @@ -70,7 +70,7 @@ struct lxc_handler { int sv[2]; int pinfd; const char *lxcpath; - struct lxc_cgroup_info *cgroup_info; + void *cgroup_data; }; extern struct lxc_handler *lxc_init(const char *name, struct lxc_conf *, const char *); diff --git a/src/lxc/state.c b/src/lxc/state.c index 81648e034..db833b074 100644 --- a/src/lxc/state.c +++ b/src/lxc/state.c @@ -69,6 +69,8 @@ lxc_state_t lxc_str2state(const char *state) lxc_state_t lxc_getstate(const char *name, const char *lxcpath) { + extern lxc_state_t freezer_state(const char *name, const char *lxcpath); + lxc_state_t state = freezer_state(name, lxcpath); if (state != FROZEN && state != FREEZING) state = lxc_cmd_get_state(name, lxcpath); diff --git a/src/tests/cgpath.c b/src/tests/cgpath.c index f0e2de84c..c8a09e84a 100644 --- a/src/tests/cgpath.c +++ b/src/tests/cgpath.c @@ -50,9 +50,7 @@ static int test_running_container(const char *lxcpath, int ret = -1; struct lxc_container *c = NULL; char *cgrelpath; - char *cgabspath; char relpath[PATH_MAX+1]; - char abspath[PATH_MAX+1]; char value[NAME_MAX], value_save[NAME_MAX]; sprintf(relpath, "%s/%s", group ? group : "lxc", name); @@ -109,32 +107,8 @@ static int test_running_container(const char *lxcpath, goto err3; } - cgabspath = lxc_cgroup_path_get("freezer", c->name, c->config_path); - if (!cgabspath) { - TSTERR("lxc_cgroup_path_get returned NULL"); - goto err3; - } - sprintf(abspath, "%s/%s/%s", "freezer", group ? group : "lxc", c->name); - if (!strstr(cgabspath, abspath)) { - TSTERR("lxc_cgroup_path_get %s not in %s", abspath, cgabspath); - goto err4; - } - - free(cgabspath); - cgabspath = lxc_cgroup_path_get("freezer.state", c->name, c->config_path); - if (!cgabspath) { - TSTERR("lxc_cgroup_path_get returned NULL"); - goto err3; - } - sprintf(abspath, "%s/%s/%s", "freezer", group ? group : "lxc", c->name); - if (!strstr(cgabspath, abspath)) { - TSTERR("lxc_cgroup_path_get %s not in %s", abspath, cgabspath); - goto err4; - } - ret = 0; -err4: - free(cgabspath); + err3: free(cgrelpath); err2: -- 2.39.5