#include "module.h"
#include "macro.h"
#include "resourced.h"
+#include "config-parser.h"
+#include "trace.h"
+#include "cgroup.h"
+
+#define CPU_SCHED_CONF_FILE RD_CONFIG_FILE(cpu-sched)
+#define CPU_SCHED_FG_NAME "foreground"
+#define MOUNTS_PATH "/proc/mounts"
+#define CPUSET_CGROUP "/sys/fs/cgroup/cpuset"
+
+
+struct core {
+ int id;
+ bool on;
+};
+
+struct coreset {
+ GSList *cores;
+ char *name;
+};
+
+struct cpu_sched {
+ struct coreset fg;
+ bool is_initalized;
+};
+
+static struct cpu_sched cs;
+
+/* check if cpuset subsystem is mounted at the right location */
+static bool cpu_sched_is_cpuset_mounted()
+{
+ FILE *f;
+ ssize_t r;
+ size_t len = 0;
+ char *buf = NULL;
+ char mountpoint[128], opts[128];
+ bool ret = false;
+
+ f = fopen(MOUNTS_PATH, "r");
+ if (NULL == f) {
+ _E("cpu-sched: could not open " MOUNTS_PATH);
+ return ret;
+ }
+
+ while ((r = getline(&buf, &len, f)) != -1) {
+ if (sscanf(buf, "%*s %127s %*s %127s %*d %*d", mountpoint, opts) != 2)
+ continue;
+
+ if (!strcmp(mountpoint, CPUSET_CGROUP) && NULL != strstr(opts, "cpuset")) {
+ ret = true;
+ break;
+ }
+ }
+
+ free(buf);
+ fclose(f);
+
+ return ret;
+}
+
+static int cpu_sched_init_cgroup_set(const struct coreset *set)
+{
+ int r;
+ char buf[512];
+
+ assert(set);
+ assert(set->name);
+
+ _D("cpu-sched: init cgroup set for %s", set->name);
+ r = cgroup_make_subdir(CPUSET_CGROUP, set->name, NULL);
+ if (r < 0) {
+ _E("failed to make cpuset cgroup (%s)", set->name);
+ return r;
+ }
+
+ r = snprintf(buf, sizeof buf, "%s/%s", CPUSET_CGROUP, set->name);
+ if (r < 0) {
+ _E("failed to setup memory nodes for cpuset (%s)", set->name);
+ return r;
+ }
+
+ /* don't force any memory nodes with this cpuset */
+ r = cgroup_write_node_uint32(buf, "cpuset.mems", 0);
+ ret_value_msg_if(r < 0, r,
+ "Failed to reset memory nodes for cpuset: %m");
+
+ return 0;
+}
+
+/* init cpuset's cgroups */
+static int cpu_sched_init_cgroup(struct cpu_sched *cs)
+{
+ int r;
+
+ _D("cpu-sched: init cgroup subsystem");
+ if (false == cpu_sched_is_cpuset_mounted()) {
+ r = cgroup_make_subdir(DEFAULT_CGROUP, "cpuset", NULL);
+ if (r < 0) {
+ _E("failed to make cpuset cgroup");
+ return r;
+ }
+
+ r = cgroup_mount_subsystem("cpuset", CPUSET_CGROUP, "cpuset");
+ if (r < 0) {
+ _E("failed to mount cpuset cgroup: %m");
+ return r;
+ }
+ }
+
+ return cpu_sched_init_cgroup_set(&cs->fg);
+}
+
+/* create single core struct representation */
+static int cpu_sched_new_core(struct coreset *set, int core_id)
+{
+ struct core *c;
+
+ assert(set);
+ assert(core_id >= 0);
+
+ _D("cpu-sched: new core %d for set %s", core_id, set->name);
+ c = (struct core *)calloc(1, sizeof *c);
+ if (NULL == c) {
+ _E("cpu-sched: could not allocate memory for core struct");
+ return RESOURCED_ERROR_FAIL;
+ }
+ c->id = core_id;
+ c->on = false;
+
+ set->cores = g_slist_append(set->cores, c);
+ return RESOURCED_ERROR_NONE;
+}
+
+/* free memory allocated in coreset structure */
+static void cpu_sched_free_cpuset(struct coreset *set)
+{
+ if (NULL != set->cores)
+ g_slist_free_full(set->cores, free);
+ set->cores = NULL;
+
+ free(set->name);
+ set->name = NULL;
+}
+
+/* parse config for specific coreset line */
+static int cpu_sched_parse_cpuset(struct coreset *set, char *value)
+{
+ char *ptr, *saveptr;
+ int min, max;
+ int r;
+
+ assert(set);
+ assert(value);
+ assert(!set->cores);
+ _D("cpu-sched: parse cpuset for %s (%s)", set->name, value);
+
+ ptr = strtok_r(value, ",", &saveptr);
+ while (ptr) {
+ if (NULL == strstr(ptr, "-")) { /* single value */
+ r = sscanf(ptr, "%d", &min);
+ if (r == 1 && min >= 0) {
+ if (RESOURCED_ERROR_NONE != cpu_sched_new_core(set, min))
+ goto parse_cpuset_fail;
+ } else {
+ _E("cpu-sched: error parsing cpuset (%s)", ptr);
+ goto parse_cpuset_fail;
+ }
+ } else { /* range */
+ r = sscanf(ptr, "%d-%d", &min, &max);
+ if (r == 2 && min >= 0 && max >= 0 && max > min) {
+ for (int i = min; i <= max; i++) {
+ if (RESOURCED_ERROR_NONE != cpu_sched_new_core(set, i))
+ goto parse_cpuset_fail;
+ }
+ } else {
+ _E("cpu-sched: error parsing cpuset (%s)", ptr);
+ goto parse_cpuset_fail;
+ }
+
+ }
+
+ ptr = strtok_r(NULL, ",", &saveptr);
+ }
+ return RESOURCED_ERROR_NONE;
+parse_cpuset_fail:
+ cpu_sched_free_cpuset(set);
+ return RESOURCED_ERROR_FAIL;
+}
+
+static int load_config(struct parse_result *result, void *user_data)
+{
+ int r;
+ struct cpu_sched *data = (struct cpu_sched *)user_data;
+
+ assert(data);
+
+ if (strcmp(result->name, CPU_SCHED_FG_NAME))
+ return RESOURCED_ERROR_NONE;
+
+ r = cpu_sched_parse_cpuset(&data->fg, result->value);
+
+ if (r < 0) {
+ _E("cpu-sched parse fg coreset: could not parse");
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ data->fg.name = strdup(CPU_SCHED_FG_NAME);
+ return RESOURCED_ERROR_NONE;
+}
+
+static int cpu_sched_parse_config(struct cpu_sched *data)
+{
+ _D("cpu-sched: parse config");
+ if (config_parse(CPU_SCHED_CONF_FILE, load_config, data) < 0)
+ return RESOURCED_ERROR_FAIL;
+
+ return RESOURCED_ERROR_NONE;
+}
static int cpu_sched_init(void *data)
{
+ int r;
_D("cpu-sched: init module");
+
+ if (cpu_sched_parse_config(&cs) != RESOURCED_ERROR_NONE) {
+ _E("cpu-sched: error parsing config");
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ r = cpu_sched_init_cgroup(&cs);
+ if (r < 0)
+ goto init_failed;
+
+ cs.is_initalized = true;
return RESOURCED_ERROR_NONE;
+
+init_failed:
+ cpu_sched_free_cpuset(&cs.fg);
+ cs.is_initalized = false;
+ return RESOURCED_ERROR_FAIL;
}
static int cpu_sched_finalize(void *data)
{
_D("cpu-sched: deinit module");
+ cpu_sched_free_cpuset(&cs.fg);
+ cs.is_initalized = false;
return RESOURCED_ERROR_NONE;
}