And update the comment explaining the locking. Also take memlock in want_daemonize.
Signed-off-by: Serge Hallyn <serge.hal...@ubuntu.com> --- src/lxc/lxccontainer.c | 153 +++++++++++++++++++++++++++++++++++++------------ 1 file changed, 115 insertions(+), 38 deletions(-) diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c index 9bc1caf..145ed7c 100644 --- a/src/lxc/lxccontainer.c +++ b/src/lxc/lxccontainer.c @@ -131,7 +131,12 @@ void remove_partial(struct lxc_container *c, int fd) char *path = alloca(len); int ret; + if (process_lock()) { + ERROR("Failed getting process lock"); + return; + } close(fd); + process_unlock(); ret = snprintf(path, len, "%s/%s/partial", c->config_path, c->name); if (ret < 0 || ret >= len) { ERROR("Error writing partial pathname"); @@ -145,11 +150,11 @@ void remove_partial(struct lxc_container *c, int fd) } /* LOCKING - * 1. c->privlock protects the struct lxc_container from multiple threads. - * 2. c->slock protects the on-disk container data + * 1. container_mem_lock(c) protects the struct lxc_container from multiple threads. + * 2. container_disk_lock(c) protects the on-disk container data - in particular the + * container configuration file. + * The container_disk_lock also takes the container_mem_lock. * 3. thread_mutex protects process data (ex: fd table) from multiple threads - * slock is an flock, which does not exclude threads. Therefore slock should - * always be wrapped inside privlock. * NOTHING mutexes two independent programs with their own struct * lxc_container for the same c->name, between API calls. For instance, * c->config_read(); c->start(); Between those calls, data on disk @@ -160,7 +165,7 @@ void remove_partial(struct lxc_container *c, int fd) * due to hung callers. So I prefer to keep the locks only within our own * functions, not across functions. * - * If you're going to fork while holding a lxccontainer, increment + * If you're going to clone while holding a lxccontainer, increment * c->numthreads (under privlock) before forking. When deleting, * decrement numthreads under privlock, then if it hits 0 you can delete. * Do not ever use a lxccontainer whose numthreads you did not bump. @@ -358,11 +363,14 @@ static pid_t lxcapi_init_pid(struct lxc_container *c) static bool load_config_locked(struct lxc_container *c, const char *fname) { + bool ret = false; if (!c->lxc_conf) c->lxc_conf = lxc_conf_init(); + process_lock(); if (c->lxc_conf && !lxc_config_read(fname, c->lxc_conf)) - return true; - return false; + ret = true; + process_unlock(); + return ret; } static bool lxcapi_load_config(struct lxc_container *c, const char *alt_file) @@ -406,7 +414,12 @@ static void lxcapi_want_daemonize(struct lxc_container *c) { if (!c) return; + if (!container_mem_lock(c)) { + ERROR("Error getting mem lock"); + return; + } c->daemonize = 1; + container_mem_unlock(c); } static bool lxcapi_wait(struct lxc_container *c, const char *state, int timeout) @@ -512,6 +525,7 @@ static bool lxcapi_start(struct lxc_container *c, int useinit, char * const argv process_unlock(); return ret; } + // In a forked task, no longer threaded process_unlock(); /* second fork to be reparented by init */ pid = fork(); @@ -1023,7 +1037,9 @@ char** lxcapi_get_ips(struct lxc_container *c, char* interface, char* family, in goto out; /* Save reference to old netns */ + process_lock(); old_netns = open("/proc/self/ns/net", O_RDONLY); + process_unlock(); if (old_netns < 0) { SYSERROR("failed to open /proc/self/ns/net"); goto out; @@ -1034,7 +1050,9 @@ char** lxcapi_get_ips(struct lxc_container *c, char* interface, char* family, in if (ret < 0 || ret >= MAXPATHLEN) goto out; + process_lock(); new_netns = open(new_netns_path, O_RDONLY); + process_unlock(); if (new_netns < 0) { SYSERROR("failed to open %s", new_netns_path); goto out; @@ -1097,10 +1115,12 @@ out: /* Switch back to original netns */ if (old_netns >= 0 && setns(old_netns, CLONE_NEWNET)) SYSERROR("failed to setns"); + process_lock(); if (new_netns >= 0) close(new_netns); if (old_netns >= 0) close(old_netns); + process_unlock(); /* Append NULL to the array */ if (count) { @@ -1194,11 +1214,15 @@ static bool lxcapi_save_config(struct lxc_container *c, const char *alt_file) if (lret) return false; + process_lock(); fout = fopen(alt_file, "w"); + process_unlock(); if (!fout) goto out; write_config(fout, c->lxc_conf); + process_lock(); fclose(fout); + process_unlock(); ret = true; out: @@ -1214,16 +1238,13 @@ static bool lxcapi_destroy(struct lxc_container *c) { struct bdev *r = NULL; bool ret = false; + int v; if (!c || !lxcapi_is_defined(c)) return false; - if (lxclock(c->privlock, 0)) + if (container_disk_lock(c)) return false; - if (lxclock(c->slock, 0)) { - lxcunlock(c->privlock); - return false; - } if (!is_stopped(c)) { // we should queue some sort of error - in c->error_string? @@ -1231,10 +1252,16 @@ static bool lxcapi_destroy(struct lxc_container *c) goto out; } - if (c->lxc_conf->rootfs.path && c->lxc_conf->rootfs.mount) + if (c->lxc_conf->rootfs.path && c->lxc_conf->rootfs.mount) { + process_lock(); r = bdev_init(c->lxc_conf->rootfs.path, c->lxc_conf->rootfs.mount, NULL); + process_unlock(); + } if (r) { - if (r->ops->destroy(r) < 0) { + process_lock(); + v = r->ops->destroy(r); + process_unlock(); + if (v < 0) { ERROR("Error destroying rootfs for %s", c->name); goto out; } @@ -1243,15 +1270,17 @@ static bool lxcapi_destroy(struct lxc_container *c) const char *p1 = lxcapi_get_config_path(c); char *path = alloca(strlen(p1) + strlen(c->name) + 2); sprintf(path, "%s/%s", p1, c->name); - if (lxc_rmdir_onedev(path) < 0) { + process_lock(); + v = lxc_rmdir_onedev(path); + process_unlock(); + if (v < 0) { ERROR("Error destroying container directory for %s", c->name); goto out; } ret = true; out: - lxcunlock(c->privlock); - lxcunlock(c->slock); + container_disk_unlock(c); return ret; } @@ -1374,23 +1403,17 @@ err: static bool lxcapi_set_cgroup_item(struct lxc_container *c, const char *subsys, const char *value) { int ret; - bool b = false; if (!c) return false; - if (container_mem_lock(c)) - return false; - if (is_stopped(c)) - goto err; + return false; + process_lock(); ret = lxc_cgroup_set(c->name, subsys, value, c->config_path); - if (!ret) - b = true; -err: - container_mem_unlock(c); - return b; + process_unlock(); + return ret == 0; } static int lxcapi_get_cgroup_item(struct lxc_container *c, const char *subsys, char *retv, int inlen) @@ -1400,32 +1423,41 @@ static int lxcapi_get_cgroup_item(struct lxc_container *c, const char *subsys, c if (!c || !c->lxc_conf) return -1; - if (container_mem_lock(c)) - return -1; - if (is_stopped(c)) - goto out; + return -1; + process_lock(); ret = lxc_cgroup_get(c->name, subsys, retv, inlen, c->config_path); + process_unlock(); -out: - container_mem_unlock(c); return ret; } const char *lxc_get_default_config_path(void) { - return default_lxc_path(); + const char *ret; + process_lock(); + ret = default_lxc_path(); + process_unlock(); + return ret; } const char *lxc_get_default_lvm_vg(void) { - return default_lvm_vg(); + const char *ret; + process_lock(); + ret = default_lvm_vg(); + process_unlock(); + return ret; } const char *lxc_get_default_zfs_root(void) { - return default_zfs_root(); + const char *ret; + process_lock(); + ret = default_zfs_root(); + process_unlock(); + return ret; } const char *lxc_get_version(void) @@ -1450,12 +1482,16 @@ static int copy_file(char *old, char *new) return -1; } + process_lock(); in = open(old, O_RDONLY); + process_unlock(); if (in < 0) { SYSERROR("opening original file %s", old); return -1; } + process_lock(); out = open(new, O_CREAT | O_EXCL | O_WRONLY, 0644); + process_unlock(); if (out < 0) { SYSERROR("opening new file %s", new); close(in); @@ -1476,8 +1512,10 @@ static int copy_file(char *old, char *new) goto err; } } + process_lock(); close(in); close(out); + process_unlock(); // we set mode, but not owner/group ret = chmod(new, sbuf.st_mode); @@ -1489,8 +1527,10 @@ static int copy_file(char *old, char *new) return 0; err: + process_lock(); close(in); close(out); + process_unlock(); return -1; } @@ -1547,48 +1587,67 @@ static int update_name_and_paths(const char *path, struct lxc_container *oldc, const char *p0, *p1, *p2, *end; const char *oldpath = oldc->get_config_path(oldc); const char *oldname = oldc->name; + int ret; + process_lock(); f = fopen(path, "r"); + process_unlock(); if (!f) { SYSERROR("opening old config"); return -1; } if (fseek(f, 0, SEEK_END) < 0) { SYSERROR("seeking to end of old config"); + process_lock(); fclose(f); + process_unlock(); return -1; } flen = ftell(f); if (flen < 0) { + process_lock(); fclose(f); + process_unlock(); SYSERROR("telling size of old config"); return -1; } if (fseek(f, 0, SEEK_SET) < 0) { + process_lock(); fclose(f); + process_unlock(); SYSERROR("rewinding old config"); return -1; } contents = malloc(flen+1); if (!contents) { SYSERROR("out of memory"); + process_lock(); fclose(f); + process_unlock(); return -1; } if (fread(contents, 1, flen, f) != flen) { free(contents); + process_lock(); fclose(f); + process_unlock(); SYSERROR("reading old config"); return -1; } contents[flen] = '\0'; - if (fclose(f) < 0) { + + process_lock(); + ret = fclose(f); + process_unlock(); + if (ret < 0) { free(contents); SYSERROR("closing old config"); return -1; } + process_lock(); f = fopen(path, "w"); + process_unlock(); if (!f) { SYSERROR("reopening config"); free(contents); @@ -1605,11 +1664,15 @@ static int update_name_and_paths(const char *path, struct lxc_container *oldc, if (fwrite(p0, 1, (end-p0), f) != (end-p0)) { SYSERROR("writing new config"); free(contents); + process_lock(); fclose(f); + process_unlock(); return -1; } free(contents); + process_lock(); fclose(f); + process_unlock(); // success return 0; } else { @@ -1618,7 +1681,9 @@ static int update_name_and_paths(const char *path, struct lxc_container *oldc, if (fwrite(p0, 1, (p-p0), f) != (p-p0)) { SYSERROR("writing new config"); free(contents); + process_lock(); fclose(f); + process_unlock(); return -1; } p0 = p; @@ -1626,7 +1691,9 @@ static int update_name_and_paths(const char *path, struct lxc_container *oldc, if (fwrite(new, 1, strlen(new), f) != strlen(new)) { SYSERROR("writing new name or path in new config"); free(contents); + process_lock(); fclose(f); + process_unlock(); return -1; } p0 += (p == p2) ? strlen(oldname) : strlen(oldpath); @@ -1671,13 +1738,19 @@ static int copyhooks(struct lxc_container *oldc, struct lxc_container *c) static void new_hwaddr(char *hwaddr) { - FILE *f = fopen("/dev/urandom", "r"); + FILE *f; + + process_lock(); + f = fopen("/dev/urandom", "r"); + process_unlock(); if (f) { unsigned int seed; int ret = fread(&seed, sizeof(seed), 1, f); if (ret != 1) seed = time(NULL); + process_lock(); fclose(f); + process_unlock(); srand(seed); } else srand(time(NULL)); @@ -1891,13 +1964,17 @@ struct lxc_container *lxcapi_clone(struct lxc_container *c, const char *newname, } // copy the configuration, tweak it as needed, + process_lock(); fout = fopen(newpath, "w"); + process_unlock(); if (!fout) { SYSERROR("open %s", newpath); goto out; } write_config(fout, c->lxc_conf); + process_lock(); fclose(fout); + process_unlock(); if (update_name_and_paths(newpath, c, n, l) < 0) { ERROR("Error updating name in cloned config"); -- 1.8.1.2 ------------------------------------------------------------------------------ Introducing AppDynamics Lite, a free troubleshooting tool for Java/.NET Get 100% visibility into your production application - at no cost. Code-level diagnostics for performance bottlenecks with <2% overhead Download for free and get started troubleshooting in minutes. http://p.sf.net/sfu/appdyn_d2d_ap1 _______________________________________________ Lxc-devel mailing list Lxc-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/lxc-devel