#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];
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)";
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);
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
}
// 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,
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);
}
/*
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
// 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;