From 04deb9bf7ea16ce12146b7d2709a712dd7c2659d Mon Sep 17 00:00:00 2001 From: Unsung Lee Date: Fri, 17 Jun 2022 19:29:01 +0900 Subject: [PATCH] cpu-boosting: implement set and clear funcs Change-Id: Id75c4da4be7e809399d41bdda5a2ead6893ffefb Signed-off-by: Unsung Lee --- conf/optimizer.conf | 2 +- src/common/cpu-common.h | 28 +++ src/common/util.c | 13 +- src/common/util.h | 1 + src/resource-limiter/cpu/cpu-throttling.c | 3 +- src/resource-optimizer/cpu/cpu-boosting.c | 386 ++++++++++++++++++++++++++++-- 6 files changed, 405 insertions(+), 28 deletions(-) diff --git a/conf/optimizer.conf b/conf/optimizer.conf index af09070..4dd788f 100644 --- a/conf/optimizer.conf +++ b/conf/optimizer.conf @@ -35,7 +35,7 @@ FragLevel=800 [CpuBoostingLevelStrong] CpuSched=rr -CpuRTPriority=49 +CpuRTPriority=50 [CpuBoostingLevelMedium] CpuSched=rr diff --git a/src/common/cpu-common.h b/src/common/cpu-common.h index 67ec557..a8daf58 100644 --- a/src/common/cpu-common.h +++ b/src/common/cpu-common.h @@ -12,6 +12,7 @@ extern "C" { #define CPU_INIT_NICE 100 #define CPU_MAX_NICE 19 #define CPU_MIN_NICE -20 +#define CPU_DEFAULT_NICE 0 #define CPU_INIT_PRIO 0 #define CPU_MAX_PRIO 99 @@ -37,6 +38,33 @@ enum cpu_sched_type { CPU_SCHED_DEADLINE, }; +enum cpu_boosting_command { + CPU_BOOSTING_COMMAND_NONE = 0, + CPU_BOOSTING_COMMAND_SET, + CPU_BOOSTING_COMMAND_CLEAR, + CPU_BOOSTING_COMMAND_GET, +}; + +struct cpu_boosting_input { + enum cpu_boosting_command command; + cpu_boosting_level_e level; + int timeout_msec; + int *tid_list; + int tid_count; + guint *gsource_id; +}; + +struct cpu_boosting_worker { + pthread_t thread; + GAsyncQueue *queue; + int active; +}; + +struct cpu_boosting_output { + bool success; + cpu_boosting_level_info_t level; +}; + struct cpu_sched_info { enum cpu_sched_type cpu_sched_type; int cpu_nice; /* fixed cpu nice */ diff --git a/src/common/util.c b/src/common/util.c index 5d84a9b..d99d6d3 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -51,7 +51,7 @@ # define SYS_sched_getattr _NR_sched_getattr #endif -static int sched_setattr(pid_t pid, struct sched_attr *attr, unsigned int flags) +int sched_setattr(pid_t pid, struct sched_attr *attr, unsigned int flags) { int error; @@ -76,7 +76,7 @@ static int sched_setattr(pid_t pid, struct sched_attr *attr, unsigned int flags) } #ifndef SYS_sched_setattr - setpriority(PRIO_PGRP, pid, attr->sched_nice); + setpriority(PRIO_PROCESS, pid, attr->sched_nice); #endif break; case SCHED_FIFO: @@ -115,6 +115,7 @@ static int sched_setattr(pid_t pid, struct sched_attr *attr, unsigned int flags) int sched_setattr_of_all_tasks(pid_t pid, struct sched_attr *attr, unsigned int flag) { int r; + int fail_cnt = 0; pid_t tid; _cleanup_free_ char *buf = NULL; DIR *dir; @@ -145,15 +146,21 @@ int sched_setattr_of_all_tasks(pid_t pid, struct sched_attr *attr, unsigned int r = sched_setattr(tid, attr, flag); if (r < 0) { _E("Failed to change scheduler of tid (%d)", tid); + fail_cnt++; } } else { _E("Thread id (%d) should be larger than 0", tid); + fail_cnt++; } } closedir(dir); - return 0; + + if (fail_cnt > 0) + return fail_cnt; + else + return 0; } int sched_getattr(pid_t pid, struct sched_attr *attr, unsigned int flags) diff --git a/src/common/util.h b/src/common/util.h index 2e44dc6..8a66ee4 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -165,6 +165,7 @@ void strv_free_full(char ***strv); int resourced_restarted(void); +int sched_setattr(pid_t pid, struct sched_attr *attr, unsigned int flag); int sched_setattr_of_all_tasks(pid_t pid, struct sched_attr *attr, unsigned int flag); int sched_getattr(pid_t pid, struct sched_attr *attr, unsigned int flags); diff --git a/src/resource-limiter/cpu/cpu-throttling.c b/src/resource-limiter/cpu/cpu-throttling.c index f11eb8d..c8d117c 100644 --- a/src/resource-limiter/cpu/cpu-throttling.c +++ b/src/resource-limiter/cpu/cpu-throttling.c @@ -56,7 +56,6 @@ #define CPU_CONF_LAZY "LAZY_PREDEFINE" #define MAX_PREDEFINED_TASKS 10 #define CPU_TIMER_INTERVAL 30 -#define CPU_DEFAULT_PRI 0 #define CPU_BACKGROUND_PRI 1 #define CPU_CONTROL_PRI 10 @@ -227,7 +226,7 @@ static void load_cpu_throttling_config(void) /* Initialize type and nice of cpu throttling scheduler */ memset(&normal_attr, 0, sizeof(struct sched_attr)); normal_attr.size = sizeof(struct sched_attr); - normal_attr.sched_nice = CPU_DEFAULT_PRI; + normal_attr.sched_nice = CPU_DEFAULT_NICE; normal_attr.sched_policy = SCHED_OTHER; memset(&throttling_attr, 0, sizeof(struct sched_attr)); diff --git a/src/resource-optimizer/cpu/cpu-boosting.c b/src/resource-optimizer/cpu/cpu-boosting.c index 27d193e..a4dc5d2 100644 --- a/src/resource-optimizer/cpu/cpu-boosting.c +++ b/src/resource-optimizer/cpu/cpu-boosting.c @@ -1,4 +1,9 @@ #include +#include +#include +#include +#include + #include "module.h" #include "macro.h" #include "util.h" @@ -8,8 +13,112 @@ #include "cpu-common.h" #include "proc-common.h" -bool boosting_success[CPU_BOOSTING_LEVEL_END] = {false, }; -struct sched_attr boosting_attr[CPU_BOOSTING_LEVEL_END]; +static GHashTable *tid_table; + +struct cpu_boosting_worker worker; +bool cpu_boosting_success[CPU_BOOSTING_LEVEL_END] = {false, }; +struct sched_attr cpu_boosting_attr[CPU_BOOSTING_LEVEL_END]; + +static void cpu_boosting_destroy_request(struct cpu_boosting_input *input); + +#define CPU_BOOSTING_WORKER_IS_ACTIVE g_atomic_int_get(&worker.active) +#define CPU_BOOSTING_WORKER_ACTIVATE g_atomic_int_set(&worker.active, 1) +#define CPU_BOOSTING_WORKER_DEACTIVATE g_atomic_int_set(&worker.active, 0) + +#define CPU_BOOSTING_SET_REQUEST(_input, _command, _level, _timeout_msec) \ +{ \ + (_input)->command = _command; (_input)->level = _level; \ + (_input)->timeout_msec = _timeout_msec; \ +} + +#define CPU_BOOSTING_ENQUEUE_REQUEST(_input) \ + do { \ + if (CPU_BOOSTING_WORKER_IS_ACTIVE) \ + g_async_queue_push(worker.queue, _input); \ + else \ + cpu_boosting_destroy_request(_input); \ + } while (0) + +static void cpu_boosting_destroy_request(struct cpu_boosting_input *input) +{ + if (input == NULL) + return; + + if (input->tid_list) + free(input->tid_list); + + if (input->gsource_id) + g_free(input->gsource_id); + + g_slice_free(struct cpu_boosting_input, input); +} + +static struct cpu_boosting_input *cpu_boosting_new_request(resource_pid_t pid) +{ + struct cpu_boosting_input *input = g_slice_new0(struct cpu_boosting_input); + if (input == NULL) { + _E("[CPU-BOOSTING] Failed to allocate memory for cpu boosting input"); + return NULL; + } + +#define CPU_BOOSTING_TEMP_TID_LIST_SIZE 50 + if (pid.pid > 0) { + int r; + int tid; + int tid_list[CPU_BOOSTING_TEMP_TID_LIST_SIZE]; + int tid_count = 0; + _cleanup_free_ char *buf = NULL; + DIR *dir; + struct dirent *dirent; + r = asprintf(&buf, "/proc/%d/task", pid.pid); + if (r < 0) + goto dealloc_input; + + dir = opendir(buf); + if (!dir) + goto dealloc_input; + while ((dirent = readdir(dir))) { + const char *id = dirent->d_name; + if(!isdigit(*id)) + continue; + + tid = atoi(id); + if (tid > 0) { + if (tid_count < CPU_BOOSTING_TEMP_TID_LIST_SIZE) + tid_list[tid_count++] = tid; + else { + _E("[CPU_BOOSTING] Temp tid list size is smaller than the number of threads of pid = %d", pid.pid); + closedir(dir); + goto dealloc_input; + } + } + } + closedir(dir); + + input->tid_list = (int *)calloc(tid_count, sizeof(int)); + if (input->tid_list == NULL) + goto dealloc_input; + + input->tid_count = tid_count; + for (int i = 0; i < tid_count; i++) + input->tid_list[i] = tid_list[i]; + } + else { + input->tid_list = (int *)calloc(pid.tid_count, sizeof(int)); + if (input->tid_list == NULL) + goto dealloc_input; + + input->tid_count = pid.tid_count; + for (int i = 0; i < pid.tid_count; i++) + input->tid_list[i] = pid.tid[i]; + } + + return input; + +dealloc_input: + g_slice_free(struct cpu_boosting_input, input); + return NULL; +} static bool load_cpu_boosting_config(cpu_boosting_level_e level) { @@ -38,7 +147,7 @@ static bool load_cpu_boosting_config(cpu_boosting_level_e level) return false; } - memset(&boosting_attr[level], 0, sizeof(struct sched_attr)); + memset(&cpu_boosting_attr[level], 0, sizeof(struct sched_attr)); if (cpu_sched_type == CPU_SCHED_BATCH || cpu_sched_type == CPU_SCHED_OTHER) { if (cpu_nice < CPU_MIN_NICE || cpu_nice > CPU_MAX_NICE) { @@ -56,7 +165,7 @@ static bool load_cpu_boosting_config(cpu_boosting_level_e level) else cpu_policy = SCHED_OTHER; - boosting_attr[level].sched_nice = cpu_nice; + cpu_boosting_attr[level].sched_nice = cpu_nice; } else { if (cpu_rt_priority < CPU_MIN_PRIO || cpu_rt_priority > CPU_MAX_PRIO) { @@ -70,55 +179,288 @@ static bool load_cpu_boosting_config(cpu_boosting_level_e level) cpu_policy = SCHED_FIFO; } - boosting_attr[level].size = sizeof(struct sched_attr); - boosting_attr[level].sched_policy = cpu_policy; - boosting_attr[level].sched_priority = cpu_rt_priority; + cpu_boosting_attr[level].size = sizeof(struct sched_attr); + cpu_boosting_attr[level].sched_policy = cpu_policy; + cpu_boosting_attr[level].sched_priority = cpu_rt_priority; _I("[CPU-BOOSTING] Boosting (level = %d) policy = %s", level, - boosting_attr[level].sched_policy == SCHED_BATCH ? "SCHED_BATCH" : - boosting_attr[level].sched_policy == SCHED_OTHER ? "SCHED_OTHER" : - boosting_attr[level].sched_policy == SCHED_RR ? "SCHED_RR" : "SCHED_FIFO"); - _I("[CPU-BOOSTING] Boosting (level = %d) nice = %d", level, boosting_attr[level].sched_nice); - _I("[CPU-BOOSTING] Boosting (level = %d) priority = %d", level, boosting_attr[level].sched_priority); + cpu_boosting_attr[level].sched_policy == SCHED_BATCH ? "SCHED_BATCH" : + cpu_boosting_attr[level].sched_policy == SCHED_OTHER ? "SCHED_OTHER" : + cpu_boosting_attr[level].sched_policy == SCHED_RR ? "SCHED_RR" : "SCHED_FIFO"); + _I("[CPU-BOOSTING] Boosting (level = %d) nice = %d", level, cpu_boosting_attr[level].sched_nice); + _I("[CPU-BOOSTING] Boosting (level = %d) priority = %d", level, cpu_boosting_attr[level].sched_priority); return true; } static void load_cpu_boosting_configs(void) { - for (int i = CPU_BOOSTING_LEVEL_STRONG; i < CPU_BOOSTING_LEVEL_END; i++) { - boosting_success[i] = load_cpu_boosting_config(i); - if (!boosting_success[i]) - _I("[CPU-BOOSTING] Boosting (level = %d) is not supported", i); - free_cpu_boosting_conf(i); + int level = CPU_BOOSTING_LEVEL_NONE; + + /* Initialize type and nice of cpu throttling scheduler */ + memset(&cpu_boosting_attr[level], 0, sizeof(struct sched_attr)); + cpu_boosting_attr[level].size = sizeof(struct sched_attr); + cpu_boosting_attr[level].sched_nice = CPU_DEFAULT_NICE; + cpu_boosting_attr[level].sched_policy = SCHED_OTHER; + cpu_boosting_success[level] = true; + + for (level = CPU_BOOSTING_LEVEL_STRONG; level < CPU_BOOSTING_LEVEL_END; level++) { + cpu_boosting_success[level] = load_cpu_boosting_config(level); + if (!cpu_boosting_success[level]) + _I("[CPU-BOOSTING] Boosting (level = %d) is not supported", level); + free_cpu_boosting_conf(level); } } -static int cpu_boosting_enqueue(void *data) +/* Called by resourced main thread */ +static int cpu_boosting_enqueue_by_conf(void *data) { assert(data); struct proc_status *ps = (struct proc_status *)data; + struct cpu_boosting_input *input; + cpu_boosting_level_e cpu_boosting_level; + resource_pid_t pid; - if (ps->pci == NULL) + if (ps->pci == NULL) { + _E("[CPU-BOOSTING] Process conf should not be NULL"); + return RESOURCED_ERROR_FAIL; + } + + pid.pid = ps->pid; + if (pid.pid <= 0) { + _E("[CPU-BOOSTING] pid should be larger than 0"); + return RESOURCED_ERROR_FAIL; + } + cpu_boosting_level = ps->pci->cpu_boosting_level; + if (!cpu_boosting_success[cpu_boosting_level]) { + _I("[CPU-BOOSTING] cpu boosting (level = %d) is not supported", cpu_boosting_level); return RESOURCED_ERROR_NONE; + } - _I("[CPU-BOOSTING] pid = %d, level = %d", ps->pid, ps->pci->cpu_boosting_level); + input = cpu_boosting_new_request(pid); + if (input == NULL) { + _E("[CPU-BOOSTING] Failed to allocate cpu boosting input"); + return RESOURCED_ERROR_FAIL; + } + CPU_BOOSTING_SET_REQUEST(input, CPU_BOOSTING_COMMAND_SET, cpu_boosting_level, -1); + CPU_BOOSTING_ENQUEUE_REQUEST(input); return RESOURCED_ERROR_NONE; } +/* Called by the last calling thread which calls unref */ +static void cpu_boosting_input_destroy(gpointer data) +{ + struct cpu_boosting_input *input = (struct cpu_boosting_input *)data; + + cpu_boosting_destroy_request(input); +} + +static gboolean cpu_boosting_timeout(gpointer data) +{ + struct cpu_boosting_input *input = (struct cpu_boosting_input *)data; + + if (input == NULL) { + _E("[CPU-BOOSTING] input argument of cpu boosting should not be NULL"); + goto timer_out; + } + + int tid_count = input->tid_count; + int *tid_list = input->tid_list; + cpu_boosting_level_e cpu_boosting_level = CPU_BOOSTING_LEVEL_NONE; + + for (int i = 0; i < tid_count; i++) { + guint *gsource_id; + + gsource_id = (guint *)g_hash_table_lookup(tid_table, &tid_list[i]); + if (gsource_id == NULL || *gsource_id != *(input->gsource_id)) + continue; + + if (tid_list[i] > 0) { + sched_setattr(tid_list[i], &cpu_boosting_attr[cpu_boosting_level], 0); + } + else { + _E("[CPU-BOOSTING] Thread (id = %d) should be larger than 0", tid_list[i]); + } + } + + cpu_boosting_destroy_request(input); + +timer_out: + return G_SOURCE_REMOVE; +} + +static void cpu_boosting_set_or_clear(struct cpu_boosting_input *input, + struct cpu_boosting_output *output) +{ + bool need_to_clear = false; + int fail_cnt = 0; + int tid_count = input->tid_count; + int *tid_list = input->tid_list; + int timeout_msec = input->timeout_msec; + cpu_boosting_level_e cpu_boosting_level = input->level; + + for (int i = 0; i < tid_count; i++) { + if (tid_list[i] > 0) { + g_hash_table_remove(tid_table, (gpointer)&tid_list[i]); + if (sched_setattr(tid_list[i], &cpu_boosting_attr[cpu_boosting_level], 0) < 0) { + _E("[CPU-BOOSTING] Failed to boost cpu of (tid = %d) with (level = %d)", + tid_list[i], cpu_boosting_level); + fail_cnt++; + continue; + } + need_to_clear = true; + } + else { + _E("[CPU-BOOSTING] Thread (id = %d) should be larger than 0", tid_list[i]); + fail_cnt++; + } + } + + if (fail_cnt > 0) + output->success = false; + else + output->success = true; + + if (timeout_msec > 0 && need_to_clear) { + input->gsource_id = g_new(guint, 1); + *(input->gsource_id) = g_timeout_add(timeout_msec, cpu_boosting_timeout, input); + + for (int i = 0; i < tid_count; i++) { + if (tid_list[i] <= 0) + continue; + + g_hash_table_insert(tid_table, (gpointer)&tid_list[i], + (gpointer)input->gsource_id); + } + } + else + cpu_boosting_destroy_request(input); +} + +static void cpu_boosting_get(struct cpu_boosting_input *input, + struct cpu_boosting_output *output) +{ +} + +/* Cpu boosting thread's main code */ +static void *cpu_boosting_func(void *data) +{ + CPU_BOOSTING_WORKER_ACTIVATE; + + setpriority(PRIO_PROCESS, 0, CPU_MIN_NICE); + + g_async_queue_ref(worker.queue); + + while (1) { + struct cpu_boosting_input *input; + struct cpu_boosting_output output; + + if (!CPU_BOOSTING_WORKER_IS_ACTIVE) + break; + + input = g_async_queue_pop(worker.queue); + if (!input) { + _E("[CPU-BOOSTING] Cpu boosting input data structure should not be NULL"); + continue; + } + + /* + * Whether current boosting level is supported or not should be checked before + * calling CPU_BOOSTING_ENQUEUE_REQUEST. That is the cpu boosting thread ignores it + */ + switch (input->command) { + case CPU_BOOSTING_COMMAND_SET: + case CPU_BOOSTING_COMMAND_CLEAR: + cpu_boosting_set_or_clear(input, &output); + break; + case CPU_BOOSTING_COMMAND_GET: + cpu_boosting_get(input, &output); + break; + case CPU_BOOSTING_COMMAND_NONE: + default: + _E("[CPU-BOOSTING] Unknwon cpu boosting command"); + cpu_boosting_destroy_request(input); + } + + } + + g_async_queue_unref(worker.queue); + + pthread_exit(NULL); +} + +static int cpu_boosting_thread_activate(void) +{ + int ret; + + if (CPU_BOOSTING_WORKER_IS_ACTIVE) + return RESOURCED_ERROR_NONE; + + worker.queue = g_async_queue_new_full(cpu_boosting_input_destroy); + if (!worker.queue) { + _E("[CPU-BOOSTING] Failed to create request queue"); + return RESOURCED_ERROR_FAIL; + } + + ret = pthread_create(&worker.thread, NULL, + cpu_boosting_func, NULL); + if (ret == 0) { + pthread_detach(worker.thread); + ret = RESOURCED_ERROR_NONE; + } + else { + _E("[CPU-BOOSTING] Failed to create cpu boosting thread"); + ret = RESOURCED_ERROR_FAIL; + } + + return ret; +} + +/* Called by resourced main thread */ +static void cpu_boosting_drain_queue(void) +{ + struct cpu_boosting_input *input; + + g_async_queue_lock(worker.queue); + while ((input = g_async_queue_try_pop_unlocked(worker.queue))) { + cpu_boosting_destroy_request(input); + } + g_async_queue_unlock(worker.queue); +} + +/* Called by resourced main thread */ +static void cpu_boosting_thread_deactivate(void) +{ + if (!CPU_BOOSTING_WORKER_IS_ACTIVE) + return; + + CPU_BOOSTING_WORKER_DEACTIVATE; + cpu_boosting_drain_queue(); + + g_async_queue_unref(worker.queue); +} + +/* Called by resourced main thread */ static int cpu_boosting_init(void *data) { load_cpu_boosting_configs(); - register_notifier(RESOURCED_NOTIFIER_BOOSTING_RESOURCE, cpu_boosting_enqueue); + cpu_boosting_thread_activate(); + tid_table = g_hash_table_new_full(g_int_hash, g_int_equal, NULL, NULL); + g_assert(tid_table); + + register_notifier(RESOURCED_NOTIFIER_BOOSTING_RESOURCE, cpu_boosting_enqueue_by_conf); return RESOURCED_ERROR_NONE; } +/* Called by resourced main thread */ static int cpu_boosting_finalize(void *data) { - unregister_notifier(RESOURCED_NOTIFIER_BOOSTING_RESOURCE, cpu_boosting_enqueue); + cpu_boosting_thread_deactivate(); + + unregister_notifier(RESOURCED_NOTIFIER_BOOSTING_RESOURCE, cpu_boosting_enqueue_by_conf); return RESOURCED_ERROR_NONE; } -- 2.7.4