#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)
{
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) {
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) {
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;
}