cpu-boosting: implement set and clear funcs 23/276523/4
authorUnsung Lee <unsung.lee@samsung.com>
Fri, 17 Jun 2022 10:29:01 +0000 (19:29 +0900)
committerUnsung Lee <unsung.lee@samsung.com>
Mon, 20 Jun 2022 04:58:25 +0000 (13:58 +0900)
Change-Id: Id75c4da4be7e809399d41bdda5a2ead6893ffefb
Signed-off-by: Unsung Lee <unsung.lee@samsung.com>
conf/optimizer.conf
src/common/cpu-common.h
src/common/util.c
src/common/util.h
src/resource-limiter/cpu/cpu-throttling.c
src/resource-optimizer/cpu/cpu-boosting.c

index af09070..4dd788f 100644 (file)
@@ -35,7 +35,7 @@ FragLevel=800
 
 [CpuBoostingLevelStrong]
 CpuSched=rr
-CpuRTPriority=49
+CpuRTPriority=50
 
 [CpuBoostingLevelMedium]
 CpuSched=rr
index 67ec557..a8daf58 100644 (file)
@@ -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 */
index 5d84a9b..d99d6d3 100644 (file)
@@ -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)
index 2e44dc6..8a66ee4 100644 (file)
@@ -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);
 
index f11eb8d..c8d117c 100644 (file)
@@ -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));
index 27d193e..a4dc5d2 100644 (file)
@@ -1,4 +1,9 @@
 #include <stdio.h>
+#include <ctype.h>
+#include <pthread.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
 #include "module.h"
 #include "macro.h"
 #include "util.h"
 #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;
 }