From: Michael Tremer Date: Mon, 8 Aug 2022 14:47:01 +0000 (+0000) Subject: cgroup: Add controllers X-Git-Tag: 0.9.28~582 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2901c3a70409658221c2ed8775c48c5abacefe46;p=pakfire.git cgroup: Add controllers This is really not working well so far. But I thought it would be better to commit this and work on it than creating another monster commit. Signed-off-by: Michael Tremer --- diff --git a/src/libpakfire/cgroup.c b/src/libpakfire/cgroup.c index 0dccef797..bad6476c2 100644 --- a/src/libpakfire/cgroup.c +++ b/src/libpakfire/cgroup.c @@ -31,10 +31,26 @@ #define ROOT "/sys/fs/cgroup" #define BUFFER_SIZE 64 * 1024 +enum pakfire_cgroup_controllers { + PAKFIRE_CGROUP_CONTROLLER_CPU = (1 << 0), + PAKFIRE_CGROUP_CONTROLLER_MEMORY = (1 << 1), + PAKFIRE_CGROUP_CONTROLLER_PIDS = (1 << 2), + PAKFIRE_CGROUP_CONTROLLER_IO = (1 << 3), +}; + +static const enum pakfire_cgroup_controllers pakfire_cgroup_accounting_controllers = + PAKFIRE_CGROUP_CONTROLLER_CPU | + PAKFIRE_CGROUP_CONTROLLER_MEMORY | + PAKFIRE_CGROUP_CONTROLLER_PIDS | + PAKFIRE_CGROUP_CONTROLLER_IO; + struct pakfire_cgroup { struct pakfire* pakfire; int nrefs; + // Flags + int flags; + // Store the path char path[PATH_MAX]; @@ -47,6 +63,10 @@ static int pakfire_cgroup_is_root(struct pakfire_cgroup* cgroup) { return !*cgroup->path; } +static int pakfire_cgroup_has_flag(struct pakfire_cgroup* cgroup, int flag) { + return cgroup->flags & flag; +} + static const char* pakfire_cgroup_name(struct pakfire_cgroup* cgroup) { if (pakfire_cgroup_is_root(cgroup)) return "(root)"; @@ -54,6 +74,76 @@ static const char* pakfire_cgroup_name(struct pakfire_cgroup* cgroup) { return cgroup->path; } +static const char* pakfire_cgroup_controller_name( + enum pakfire_cgroup_controllers controller) { + switch (controller) { + case PAKFIRE_CGROUP_CONTROLLER_CPU: + return "cpu"; + + case PAKFIRE_CGROUP_CONTROLLER_MEMORY: + return "memory"; + + case PAKFIRE_CGROUP_CONTROLLER_PIDS: + return "pids"; + + case PAKFIRE_CGROUP_CONTROLLER_IO: + return "io"; + } + + return NULL; +} + +static enum pakfire_cgroup_controllers pakfire_cgroup_find_controller_by_name( + const char* name) { + const char* n = NULL; + + // Walk through the bitmap + for (unsigned int i = 1; i; i <<= 1) { + n = pakfire_cgroup_controller_name(i); + if (!n) + break; + + // Match + if (strcmp(name, n) == 0) + return i; + } + + // Nothing found + return 0; +} + +static struct pakfire_cgroup* pakfire_cgroup_parent(struct pakfire_cgroup* cgroup) { + struct pakfire_cgroup* parent = NULL; + int r; + + // Cannot return parent for root group + if (pakfire_cgroup_is_root(cgroup)) + return NULL; + + // Determine the path of the parent + char* path = pakfire_dirname(cgroup->path); + if (!path) { + ERROR(cgroup->pakfire, "Could not determine path for parent cgroup: %m\n"); + return NULL; + } + + // dirname() returns . if no directory component could be found + if (strcmp(path, ".") == 0) + *path = '\0'; + + // Open the cgroup + r = pakfire_cgroup_open(&parent, cgroup->pakfire, path, 0); + if (r) { + ERROR(cgroup->pakfire, "Could not open parent cgroup: %m\n"); + parent = NULL; + } + + // Cleanup + free(path); + + return parent; +} + static void pakfire_cgroup_free(struct pakfire_cgroup* cgroup) { DEBUG(cgroup->pakfire, "Releasing cgroup %s at %p\n", pakfire_cgroup_name(cgroup), cgroup); @@ -132,14 +222,14 @@ ERROR: return fd; } -static int pakfire_cgroup_read(struct pakfire_cgroup* cgroup, const char* path, +static ssize_t pakfire_cgroup_read(struct pakfire_cgroup* cgroup, const char* path, char* buffer, size_t length) { - int r = -1; + ssize_t bytes_read = -1; // Check if this cgroup has been destroyed already if (!cgroup->fd) { ERROR(cgroup->pakfire, "Trying to read from destroyed cgroup\n"); - return r; + return -1; } // Open the file @@ -151,21 +241,22 @@ static int pakfire_cgroup_read(struct pakfire_cgroup* cgroup, const char* path, } // Read file content into buffer - ssize_t bytes_read = read(fd, buffer, length); - if (bytes_read <= 0) { + bytes_read = read(fd, buffer, length); + if (bytes_read < 0) { DEBUG(cgroup->pakfire, "Could not read from %s/%s: %m\n", pakfire_cgroup_name(cgroup), path); goto ERROR; } - // Return how many bytes we have read - r = bytes_read; + // Terminate the buffer + if (bytes_read < length) + buffer[bytes_read] = '\0'; ERROR: if (fd > 0) close(fd); - return r; + return bytes_read; } static int pakfire_cgroup_write(struct pakfire_cgroup* cgroup, @@ -206,36 +297,136 @@ static int pakfire_cgroup_write(struct pakfire_cgroup* cgroup, return r; } -static struct pakfire_cgroup* pakfire_cgroup_parent(struct pakfire_cgroup* cgroup) { - struct pakfire_cgroup* parent = NULL; +static int pakfire_cgroup_read_controllers( + struct pakfire_cgroup* cgroup, const char* name) { + char buffer[BUFFER_SIZE]; + char* p = NULL; int r; - // Cannot return parent for root group - if (pakfire_cgroup_is_root(cgroup)) - return NULL; + // Discovered controllers + int controllers = 0; - // Determine the path of the parent - char* path = pakfire_dirname(cgroup->path); - if (!path) { - ERROR(cgroup->pakfire, "Could not determine path for parent cgroup: %m\n"); - return NULL; + // Read cgroup.controllers file + ssize_t bytes_read = pakfire_cgroup_read(cgroup, name, buffer, sizeof(buffer)); + if (bytes_read < 0) + return -1; + + // If the file was empty, there is nothing more to do + if (bytes_read == 0) + return 0; + + char* token = strtok_r(buffer, " \n", &p); + + while (token) { + DEBUG(cgroup->pakfire, "Found controller '%s'\n", token); + + // Try finding this controller + int controller = pakfire_cgroup_find_controller_by_name(token); + if (controller) + controllers |= controller; + + // Move on to next token + token = strtok_r(NULL, " \n", &p); } - // dirname() returns . if no directory component could be found - if (strcmp(path, ".") == 0) - *path = '\0'; + // Return discovered controllers + return controllers; +} - // Open the cgroup - r = pakfire_cgroup_open(&parent, cgroup->pakfire, path); - if (r) { - ERROR(cgroup->pakfire, "Could not open parent cgroup: %m\n"); - parent = NULL; +/* + Returns a bitmap of all available controllers +*/ +static int pakfire_cgroup_available_controllers(struct pakfire_cgroup* cgroup) { + return pakfire_cgroup_read_controllers(cgroup, "cgroup.controllers"); +} + +/* + Returns a bitmap of all enabled controllers +*/ +static int pakfire_cgroup_enabled_controllers(struct pakfire_cgroup* cgroup) { + return pakfire_cgroup_read_controllers(cgroup, "cgroup.subtree_control"); +} + +/* + This function takes a bitmap of controllers that should be enabled. +*/ +static int pakfire_cgroup_enable_controllers(struct pakfire_cgroup* cgroup, + enum pakfire_cgroup_controllers controllers) { + struct pakfire_cgroup* parent = NULL; + int r = 1; + + // Find all enabled controllers + const int enabled_controllers = pakfire_cgroup_enabled_controllers(cgroup); + if (enabled_controllers < 0) { + ERROR(cgroup->pakfire, "Could not fetch enabled controllers: %m\n"); + goto ERROR; } - // Cleanup - free(path); + // Filter out anything that is already enabled + controllers = (controllers & ~enabled_controllers); - return parent; + // Exit if everything is already enabled + if (!controllers) { + DEBUG(cgroup->pakfire, "All controllers are already enabled\n"); + return 0; + } + + // Find all available controllers + const int available_controllers = pakfire_cgroup_available_controllers(cgroup); + if (available_controllers < 0) { + ERROR(cgroup->pakfire, "Could not fetch available controllers: %m\n"); + goto ERROR; + } + + // Are all controllers we need available, yet? + if (controllers & ~available_controllers) { + DEBUG(cgroup->pakfire, "Not all controllers are available, yet\n"); + + parent = pakfire_cgroup_parent(cgroup); + + // Enable everything we need on the parent group + if (parent) { + r = pakfire_cgroup_enable_controllers(parent, controllers); + if (r) + goto ERROR; + } + } + + // Determine how many iterations we will need + const int iterations = 1 << (sizeof(controllers) * 8 - __builtin_clz(controllers)); + + // Iterate over all known controllers + for (int controller = 1; controller < iterations; controller <<= 1) { + // Skip enabling this controller if not requested + if (!(controller & controllers)) + continue; + + // Fetch name + const char* name = pakfire_cgroup_controller_name(controller); + + DEBUG(cgroup->pakfire, "Enabling controller %s in cgroup %s\n", + name, pakfire_cgroup_name(cgroup)); + + // Try enabling the controller (this will succeed if it already is enabled) + r = pakfire_cgroup_write(cgroup, "cgroup.subtree_control", "+%s\n", name); + if (r) { + ERROR(cgroup->pakfire, "Could not enable controller %s in cgroup %s\n", + name, pakfire_cgroup_name(cgroup)); + goto ERROR; + } + } + +ERROR: + if (parent) + pakfire_cgroup_unref(parent); + + return r; +} + +static int pakfire_cgroup_enable_accounting(struct pakfire_cgroup* cgroup) { + // Enable all accounting controllers + return pakfire_cgroup_enable_controllers(cgroup, + pakfire_cgroup_accounting_controllers); } /* @@ -244,7 +435,7 @@ static struct pakfire_cgroup* pakfire_cgroup_parent(struct pakfire_cgroup* cgrou If the cgroup doesn't exist, it will be created including any parent cgroups. */ int pakfire_cgroup_open(struct pakfire_cgroup** cgroup, - struct pakfire* pakfire, const char* path) { + struct pakfire* pakfire, const char* path, int flags) { int r = 1; // Allocate the cgroup struct @@ -263,11 +454,21 @@ int pakfire_cgroup_open(struct pakfire_cgroup** cgroup, // Copy path pakfire_string_set(c->path, path); + // Copy flags + c->flags = flags; + // Open a file descriptor c->fd = __pakfire_cgroup_open(c); if (c->fd < 0) goto ERROR; + // Enable accounting if requested + if (pakfire_cgroup_has_flag(c, PAKFIRE_CGROUP_ENABLE_ACCOUNTING)) { + r = pakfire_cgroup_enable_accounting(c); + if (r) + goto ERROR; + } + *cgroup = c; return 0; diff --git a/src/libpakfire/include/pakfire/cgroup.h b/src/libpakfire/include/pakfire/cgroup.h index a9d3914b1..07a726d5a 100644 --- a/src/libpakfire/include/pakfire/cgroup.h +++ b/src/libpakfire/include/pakfire/cgroup.h @@ -27,12 +27,18 @@ struct pakfire_cgroup; +enum pakfire_cgroup_flags { + PAKFIRE_CGROUP_ENABLE_ACCOUNTING = (1 << 0), +}; + int pakfire_cgroup_open(struct pakfire_cgroup** cgroup, - struct pakfire* pakfire, const char* path); + struct pakfire* pakfire, const char* path, int flags); struct pakfire_cgroup* pakfire_cgroup_ref(struct pakfire_cgroup* cgroup); struct pakfire_cgroup* pakfire_cgroup_unref(struct pakfire_cgroup* cgroup); +int pakfire_cgroup_enable_default_controllers(struct pakfire_cgroup* cgroup); + int pakfire_cgroup_destroy(struct pakfire_cgroup* cgroup); int pakfire_cgroup_fd(struct pakfire_cgroup* cgroup); diff --git a/src/libpakfire/jail.c b/src/libpakfire/jail.c index ce56c5769..f03a4f2d0 100644 --- a/src/libpakfire/jail.c +++ b/src/libpakfire/jail.c @@ -1279,7 +1279,8 @@ static int __pakfire_jail_exec(struct pakfire_jail* jail, const char* argv[]) { #endif /* ENABLE_DEBUG */ // Setup a cgroup - r = pakfire_cgroup_open(&ctx.cgroup, jail->pakfire, "jail"); + r = pakfire_cgroup_open(&ctx.cgroup, jail->pakfire, "jail/test1", + PAKFIRE_CGROUP_ENABLE_ACCOUNTING); if (r) goto ERROR;