cpu-boosting: Check whether cpu stall handler is turned on
[platform/core/system/resourced.git] / src / resource-optimizer / cpu / cpu-boosting.c
1 #include <string.h>
2 #include <errno.h>
3 #include <stdio.h>
4 #include <ctype.h>
5 #include <pthread.h>
6 #include <sys/un.h>
7 #include <sys/time.h>
8 #include <sys/resource.h>
9 #include <sys/types.h>
10 #include <sys/socket.h>
11
12 #include <systemd/sd-daemon.h>
13
14 #include <system/syscommon-plugin-resourced-cpu-boosting.h>
15
16 #include "module.h"
17 #include "macro.h"
18 #include "util.h"
19 #include "trace.h"
20 #include "notifier.h"
21 #include "resourced.h"
22 #include "cpu-common.h"
23 #include "fd-handler.h"
24 #include "proc-common.h"
25 /* key = tid, value = cpu_boosting info per thread */
26 static GHashTable *g_cpu_boosting_info_table[CPU_BOOSTING_LEVEL_END] = { NULL, };
27 static GHashTable *dest_table;  /* key = destiation process name, value = cpu_boosting_input */
28 /* Key = tid, value = cpu_contention_handle_data */
29 static GHashTable *g_cpu_contention_handle_data_table;
30
31 pthread_t cpu_boosting_thread;
32 static struct sched_attr cpu_boosting_attr[CPU_BOOSTING_LEVEL_END];
33 static bool cpu_boosting_success[CPU_BOOSTING_LEVEL_END] = {false, };
34
35 GMainLoop *cpu_boosting_loop;
36 GMainContext *cpu_boosting_context;
37
38 static guint g_latest_timer_id;
39
40 #define CPU_CONTENTION_HANDLE_TIMEOUT_MSEC 500
41 #define SOCK_PATH "/run/.resourced.socket"
42
43 /**
44  * Information to restore CPU boosting level
45  * before dealing with CPU contention
46  */
47 struct cpu_contention_handle_data {
48         pid_t tid;
49         guint timer_id;
50         cpu_boosting_level_e origin_cpu_boosting_level;
51         cpu_boosting_level_e current_cpu_boosting_level;
52 };
53
54 #define CPU_BOOSTING_SET_REQUEST(_input, _command, _level,                      \
55                 _timeout_msec, _pid, _flags)                                    \
56 {                                                                               \
57         (_input)->client_input.command = _command;                              \
58         (_input)->client_input.timeout_msec = _timeout_msec;                    \
59         (_input)->client_input.level = _level;                                  \
60         (_input)->client_input.pid = _pid;                                      \
61         (_input)->client_input.flags = _flags;                                  \
62 }
63
64 static void cpu_boosting_handle_command(
65                 struct syscommon_resourced_cpu_boosting_input *input);
66
67 static void free_cpu_boosting_info(gpointer data)
68 {
69         struct syscommon_resourced_cpu_boosting_info *cpu_boosting_info =
70                 (struct syscommon_resourced_cpu_boosting_info *)data;
71
72         assert(cpu_boosting_info);
73
74         cpu_boosting_info->ref_cnt--;
75         if (cpu_boosting_info->ref_cnt == 0)
76                 free(cpu_boosting_info);
77 }
78
79 static void free_cpu_contention_handle_data(gpointer data)
80 {
81         struct cpu_contention_handle_data *cpu_contention_handle_data =
82                 (struct cpu_contention_handle_data *)data;
83
84         assert(cpu_contention_handle_data);
85
86         g_slice_free(struct cpu_contention_handle_data,
87                         cpu_contention_handle_data);
88 }
89
90 static void remove_cpu_boosting_info_in_tables(int *tid)
91 {
92         gboolean ret_removed;
93         for (cpu_boosting_level_e cpu_boosting_level = CPU_BOOSTING_LEVEL_STRONG;
94                         cpu_boosting_level < CPU_BOOSTING_LEVEL_END; cpu_boosting_level++) {
95                 ret_removed = g_hash_table_remove(
96                                 g_cpu_boosting_info_table[cpu_boosting_level], tid);
97                 if (ret_removed)
98                         return;
99         }
100 }
101
102 static void find_cpu_boosting_info_in_tables(
103                 struct syscommon_resourced_cpu_boosting_info **cpu_boosting_info, int *tid)
104 {
105         assert(cpu_boosting_info);
106
107         for (cpu_boosting_level_e cpu_boosting_level = CPU_BOOSTING_LEVEL_STRONG;
108                         cpu_boosting_level < CPU_BOOSTING_LEVEL_END; cpu_boosting_level++) {
109                 *cpu_boosting_info = g_hash_table_lookup(
110                                 g_cpu_boosting_info_table[cpu_boosting_level], tid);
111                 if (*cpu_boosting_info)
112                         return;
113         }
114 }
115
116 static int restore_cpu_boosting_level(void)
117 {
118         GHashTableIter cpu_contention_handle_data_iter;
119         guint **gsource_list = NULL;
120         pid_t **tid_list = NULL;
121         int hash_size;
122         gpointer tid;
123         gpointer value;
124         int hash_index = 0;
125         int ret;
126
127         hash_size = g_hash_table_size(g_cpu_contention_handle_data_table);
128         if (hash_size == 0) {
129                 _E("[CPU-BOOSTING] No thread to restore cpu boosting level");
130                 return RESOURCED_ERROR_FAIL;
131         }
132
133         /**
134          * Secure memory first before running main loop.
135          * If the current memory is not enough, then do not restore now
136          * and wait for interval time.
137          */
138         tid_list = (pid_t **)calloc(hash_size, sizeof(pid_t *));
139         if (tid_list == NULL) {
140                 _E("[CPU-BOOSTING] Failed to allocate memory");
141                 return RESOURCED_ERROR_OUT_OF_MEMORY;
142         }
143
144         gsource_list = (guint **)calloc(hash_size, sizeof(guint *));
145         if (gsource_list == NULL) {
146                 _E("[CPU-BOOSTING] Failed to allocate memory");
147                 goto free_mem;
148         }
149
150         for (hash_index = 0; hash_index < hash_size; hash_index++) {
151                 tid_list[hash_index] = (pid_t *)calloc(1, sizeof(pid_t));
152                 if (tid_list[hash_index] == NULL)
153                         goto free_mem;
154
155                 gsource_list[hash_index] = g_new(guint, 1);
156         }
157
158         hash_index = 0;
159         g_hash_table_iter_init(&cpu_contention_handle_data_iter,
160                         g_cpu_contention_handle_data_table);
161         while (1) {
162                 struct syscommon_resourced_cpu_boosting_input *cpu_boosting_input;
163                 struct syscommon_resourced_cpu_boosting_info *cpu_boosting_info;
164                 struct cpu_contention_handle_data *cpu_contention_handle_data;
165                 cpu_boosting_level_e current_cpu_boosting_level;
166                 cpu_boosting_level_e origin_cpu_boosting_level;
167                 resource_pid_t resource_pid;
168
169                 ret = g_hash_table_iter_next(&cpu_contention_handle_data_iter,
170                                 &tid, &value);
171                 if (ret == 0)
172                         break;
173
174                 cpu_contention_handle_data =
175                         (struct cpu_contention_handle_data *)value;
176                 current_cpu_boosting_level =
177                         cpu_contention_handle_data->current_cpu_boosting_level;
178                 origin_cpu_boosting_level =
179                         cpu_contention_handle_data->origin_cpu_boosting_level;
180
181                 /**
182                  * Cpu boosting can be already finished
183                  * by clearing cpu boosting requets from the source or timeout.
184                  * Therefore, first check whether the corresponding thread
185                  * cpu boosting is still valid.
186                  */
187                 cpu_boosting_info = g_hash_table_lookup(
188                                 g_cpu_boosting_info_table[current_cpu_boosting_level], tid);
189                 if (cpu_boosting_info == NULL)
190                         continue;
191
192                 cpu_boosting_input =
193                         g_slice_new0(struct syscommon_resourced_cpu_boosting_input);
194
195                 resource_pid.pid = 0;
196                 resource_pid.tid_count = 1;
197                 resource_pid.tid = tid_list[hash_index];
198                 resource_pid.tid[0] = g_int_hash(tid);
199
200                 cpu_boosting_input->remove_input = true;
201                 cpu_boosting_input->gsource_id = gsource_list[hash_index];
202                 cpu_boosting_input->gsource_id[0] =
203                         cpu_boosting_info->gsource_id;
204
205                 hash_index++;
206
207                 CPU_BOOSTING_SET_REQUEST(cpu_boosting_input,
208                                 CPU_BOOSTING_COMMAND_SET, origin_cpu_boosting_level,
209                                 -1, resource_pid, cpu_boosting_info->cpu_boosting_flags);
210
211                 /**
212                  * tid_list[index] and gsource_list[index] will be freed
213                  * after calling this function.
214                  */
215                 cpu_boosting_handle_command(cpu_boosting_input);
216         }
217
218         for (int index = hash_index; index < hash_size; index++) {
219                 free(tid_list[index]);
220                 g_free(gsource_list[index]);
221         }
222
223         free(tid_list);
224         g_free(gsource_list);
225         g_hash_table_remove_all(g_cpu_contention_handle_data_table);
226
227         return RESOURCED_ERROR_NONE;
228
229 free_mem:
230         for (int index = 0; index < hash_index; index++) {
231                 free(tid_list[index]);
232                 g_free(gsource_list[index]);
233         }
234
235         free(tid_list);
236         free(gsource_list);
237
238         return RESOURCED_ERROR_OUT_OF_MEMORY;
239 }
240
241 static gboolean cpu_contention_handle_timeout(gpointer data)
242 {
243         guint *timer_id =(guint *)data;
244         int ret = RESOURCED_ERROR_NONE;
245
246         if (timer_id == NULL) {
247                 _E("[CPU-BOOSTING] Timer id cannot be NULL");
248                 return G_SOURCE_REMOVE;
249         }
250
251         if (syscommon_plugin_resourced_is_cpu_contention_alleviated
252                         (*timer_id, g_latest_timer_id))
253                 ret = restore_cpu_boosting_level();
254
255         if (ret == RESOURCED_ERROR_OUT_OF_MEMORY)
256                 return G_SOURCE_CONTINUE;
257
258         g_free(timer_id);
259
260         return G_SOURCE_REMOVE;
261 }
262
263 static void register_cpu_contention_handle_data(
264         struct syscommon_resourced_cpu_boosting_input *input,
265         cpu_boosting_level_e origin_cpu_boosting_level,
266         guint timer_id)
267 {
268         struct cpu_contention_handle_data *cpu_contention_handle_data = NULL;
269
270         assert(input);
271
272         /**
273          * Register information to restore cpu boosting level
274          * before dealing with CPU contention.
275          */
276         for (int i = 0; i < input->client_input.pid.tid_count; i++) {
277                 cpu_contention_handle_data = (struct cpu_contention_handle_data *)
278                         g_hash_table_lookup(g_cpu_contention_handle_data_table,
279                                         &input->client_input.pid.tid[i]);
280                 if (cpu_contention_handle_data) {
281                         cpu_contention_handle_data->current_cpu_boosting_level =
282                                 input->client_input.level;
283                         cpu_contention_handle_data->timer_id = timer_id;
284                         continue;
285                 }
286
287                 cpu_contention_handle_data = (struct cpu_contention_handle_data *)
288                         g_slice_new0(struct cpu_contention_handle_data);
289                 cpu_contention_handle_data->tid = input->client_input.pid.tid[i];
290                 cpu_contention_handle_data->origin_cpu_boosting_level =
291                         origin_cpu_boosting_level;
292                 cpu_contention_handle_data->current_cpu_boosting_level =
293                         input->client_input.level;
294                 cpu_contention_handle_data->timer_id = timer_id;
295
296                 g_hash_table_insert(g_cpu_contention_handle_data_table,
297                                 &cpu_contention_handle_data->tid, cpu_contention_handle_data);
298         }
299 }
300
301 static gboolean cpu_boosting_governor_govern_request (gpointer user_data)
302 {
303         struct syscommon_resourced_cpu_boosting_input *input = NULL;
304         cpu_boosting_level_e cpu_boosting_level;
305         guint *timer_id;
306         GSList *action_list = NULL;
307         GSource *source;
308         GSList *iter;
309         GSList *next;
310         bool is_cpu_busy;
311         int ret;
312
313         /**
314          * Search cpu boosted thread list from CPU_BOOSTING_LEVEL_STRONG to
315          * CPU_BOOSTING_LEVEL_WEAK. If the governor cannot find out victims,
316          * then just drop the event and wait for the next event from the monitor.
317          */
318         for (cpu_boosting_level = CPU_BOOSTING_LEVEL_STRONG;
319                         cpu_boosting_level < CPU_BOOSTING_LEVEL_END; cpu_boosting_level++) {
320                 ret = syscommon_plugin_resourced_cpu_boosting_governor_govern_request(
321                                 g_cpu_boosting_info_table[cpu_boosting_level],
322                                 cpu_boosting_level, &action_list, &is_cpu_busy);
323                 if (ret < 0)
324                         return G_SOURCE_REMOVE;
325
326                 if (!is_cpu_busy)
327                         break;
328
329                 if (action_list != NULL)
330                         break;
331         }
332
333         /**
334          * action_list is NULL when there is not boosted thread to decrease
335          * cpu boosting level.
336          * In addition, g_cpu_contention_handle_data_table size is 0 when
337          * there is no boosted threads yet with decreased cpu boosting level.
338          * (resourced should check whehter no more stall event during
339          * CPU_CONTENTION_HANDLE_TIMEOUT_MSEC.)
340          *
341          * In conclusion, additional timer is useless to set when action_list is
342          * empty and no decreased boosted thread.
343          */
344         if (action_list == NULL &&
345                         g_hash_table_size(g_cpu_contention_handle_data_table) == 0)
346                 return G_SOURCE_REMOVE;
347
348         /**
349          * Set timer to check whether cpu contention is alleviated or not
350          * after CPU_CONTENTION_HANDLE_TIMEOUT_MSEC.
351          */
352         timer_id = g_new(guint, 1);
353         source = g_timeout_source_new(CPU_CONTENTION_HANDLE_TIMEOUT_MSEC);
354         g_source_set_callback(source, cpu_contention_handle_timeout,
355                         timer_id, NULL);
356         g_latest_timer_id = *timer_id =
357                 g_source_attach(source, cpu_boosting_context);
358         g_source_unref(source);
359
360         gslist_for_each_safe(action_list, iter, next, input) {
361                 assert(input);
362
363                 register_cpu_contention_handle_data(input, cpu_boosting_level,
364                                 *timer_id);
365
366                 action_list = g_slist_remove(action_list, input);
367
368                 /**
369                  * Control cpu boosting level of victims according to
370                  * the governor policy.
371                  */
372                 cpu_boosting_handle_command(input);
373         }
374
375         return G_SOURCE_REMOVE;
376 }
377
378 void cpu_boosting_wakeup_cpu_boosting_thread(void)
379 {
380         /**
381          * This function is called by a monitor thread,
382          * so, the monitor should wakeup the cpu boosting main thread
383          * to call governor.
384          */
385         if (cpu_boosting_is_enabled_of_cpu_stall_handler())
386                 g_main_context_invoke(cpu_boosting_context,
387                                 cpu_boosting_governor_govern_request, NULL);
388         else
389                 _I("[CPU-BOOSTING] CPU stall handler is turned off");
390 }
391
392 static cpu_boosting_level_e cpu_boosting_level_search(struct sched_attr attr)
393 {
394         attr.sched_policy = attr.sched_policy & ~SCHED_RESET_ON_FORK;
395
396         for (int level = CPU_BOOSTING_LEVEL_STRONG; level < CPU_BOOSTING_LEVEL_END; level++) {
397                 if (cpu_boosting_attr[level].sched_policy == SCHED_RR ||
398                         cpu_boosting_attr[level].sched_policy == SCHED_FIFO) {
399                         if (cpu_boosting_attr[level].sched_priority == attr.sched_priority)
400                                 return level;
401                 }
402                 else if (cpu_boosting_attr[level].sched_nice == attr.sched_nice)
403                         return level;
404         }
405
406         return CPU_BOOSTING_LEVEL_NONE;
407 }
408
409 static int cpu_boosting_get_tids(pid_t pid, resource_pid_t *resource_pid)
410 {
411         int ret;
412         int tid;
413         DIR *dir;
414         pid_t *tid_list = NULL;
415         int alloc_count = 0;
416         int tid_count = 0;
417         struct dirent *dirent;
418         _cleanup_free_ char *buf = NULL;
419
420         if (pid <= 0) {
421                 _E("[CPU-BOOSTING] Wrong pid = %d", pid);
422                 return RESOURCED_ERROR_FAIL;
423         }
424
425         ret = asprintf(&buf, "/proc/%d/task", pid);
426         if (ret < 0) {
427                 _E("[CPU-BOOSTING] Failed to allocate buf (dir = %s)", buf);
428                 return RESOURCED_ERROR_OUT_OF_MEMORY;
429         }
430
431         dir = opendir(buf);
432         if (!dir) {
433                 _E("[CPU-BOOSTING] Failed to open (dir = %s)", buf);
434                 return RESOURCED_ERROR_FAIL;
435         }
436
437         while ((dirent = readdir(dir))) {
438                 const char *id = dirent->d_name;
439                 if(!isdigit(*id))
440                         continue;
441
442                 tid = atoi(id);
443                 if (tid > 0) {
444                         if (alloc_count == tid_count) {
445                                 alloc_count += 15;
446                                 tid_list = (pid_t *)realloc(tid_list, alloc_count * sizeof(pid_t));
447                                 if (tid_list == NULL) {
448                                         _E("[CPU-BOOSTING] Failed to allocate memory");
449                                         closedir(dir);
450                                         resource_pid->tid = NULL;
451                                         resource_pid->tid_count = 0;
452                                         return RESOURCED_ERROR_OUT_OF_MEMORY;
453                                 }
454                         }
455                         tid_list[tid_count++] = tid;
456                 }
457                 else
458                         _E("[CPU-BOOSTING] Thread (id = %d) should be larger than 0", tid);
459         }
460         closedir(dir);
461
462         resource_pid->tid = tid_list;
463         resource_pid->tid_count = tid_count;
464
465         return RESOURCED_ERROR_NONE;
466 }
467
468 static void cpu_boosting_destroy_request(gpointer data)
469 {
470         struct syscommon_resourced_cpu_boosting_input *input =
471                 (struct syscommon_resourced_cpu_boosting_input *)data;
472
473         if (input == NULL)
474                 return;
475
476         if (input->client_input.pid.tid)
477                 free(input->client_input.pid.tid);
478
479         if (input->client_input.dest) {
480                 free((void *)input->client_input.dest);
481         }
482
483         if (input->gsource_id) {
484                 g_free(input->gsource_id);
485         }
486
487         g_slice_free(struct syscommon_resourced_cpu_boosting_input, input);
488 }
489
490 static struct syscommon_resourced_cpu_boosting_input *
491 cpu_boosting_new_request(void)
492 {
493         struct syscommon_resourced_cpu_boosting_input *input =
494                 g_slice_new0(struct syscommon_resourced_cpu_boosting_input);
495         if (input == NULL) {
496                 _E("[CPU-BOOSTING] Failed to allocate memory for cpu boosting input");
497                 return NULL;
498         }
499
500         return input;
501 }
502
503 static bool load_cpu_boosting_config(cpu_boosting_level_e level)
504 {
505         int cpu_nice, cpu_rt_priority, cpu_policy;
506
507         enum cpu_sched_type cpu_sched_type;
508
509         struct cpu_boosting_conf *cpu_boosting_conf = get_cpu_boosting_conf(level);
510         if (cpu_boosting_conf == NULL) {
511                 _E("[CPU-BOOSTING] Boosting (level = %d) configuration structure should not be NULL",
512                                 level);
513                 return false;
514         }
515
516         if (!cpu_boosting_conf->enable) {
517                 _D("[CPU-BOOSTING] Cpu boosting is disabled");
518                 return false;
519         }
520
521         cpu_nice = cpu_boosting_conf->cpu_sched_info.cpu_nice;
522         cpu_rt_priority = cpu_boosting_conf->cpu_sched_info.cpu_rt_priority;
523         cpu_sched_type = cpu_boosting_conf->cpu_sched_info.cpu_sched_type;
524
525         if (cpu_sched_type < CPU_SCHED_BATCH || cpu_sched_type > CPU_SCHED_RR) {
526                 _E("[CPU-BOOSTING] Cpu scheduler type is unknown or idle");
527                 return false;
528         }
529
530         memset(&cpu_boosting_attr[level], 0, sizeof(struct sched_attr));
531
532         if (cpu_sched_type == CPU_SCHED_BATCH || cpu_sched_type == CPU_SCHED_OTHER) {
533                 if (cpu_nice < CPU_MIN_NICE || cpu_nice > CPU_MAX_NICE) {
534                         _E("[CPU-BOOSTING] Cpu nice (= %d) is out of scope", cpu_nice);
535                         return false;
536                 }
537
538                 if (cpu_rt_priority != CPU_INIT_PRIO) {
539                         _E("[CPU-BOOSTING] Cpu rt priority (= %d) is out of scope", cpu_rt_priority);
540                         return false;
541                 }
542
543                 if (cpu_sched_type == CPU_SCHED_BATCH)
544                         cpu_policy = SCHED_BATCH;
545                 else
546                         cpu_policy = SCHED_OTHER;
547
548                 cpu_boosting_attr[level].sched_nice = cpu_nice;
549         }
550         else {
551                 if (cpu_rt_priority < CPU_MIN_PRIO || cpu_rt_priority > CPU_MAX_PRIO) {
552                         _E("[CPU-BOOSTING] Cpu rt priority (= %d) is out of scope", cpu_rt_priority);
553                         return false;
554                 }
555
556                 if (cpu_sched_type == CPU_SCHED_RR)
557                         cpu_policy = SCHED_RR;
558                 else
559                         cpu_policy = SCHED_FIFO;
560         }
561
562         cpu_boosting_attr[level].size = sizeof(struct sched_attr);
563         cpu_boosting_attr[level].sched_policy = cpu_policy;
564         cpu_boosting_attr[level].sched_priority = cpu_rt_priority;
565
566         _I("[CPU-BOOSTING] Boosting (level = %d) policy = %s", level,
567                 cpu_boosting_attr[level].sched_policy == SCHED_BATCH ? "SCHED_BATCH" :
568                         cpu_boosting_attr[level].sched_policy == SCHED_OTHER ? "SCHED_OTHER" :
569                         cpu_boosting_attr[level].sched_policy == SCHED_RR ? "SCHED_RR" : "SCHED_FIFO");
570         _I("[CPU-BOOSTING] Boosting (level = %d) nice = %d", level, cpu_boosting_attr[level].sched_nice);
571         _I("[CPU-BOOSTING] Boosting (level = %d) priority = %d", level, cpu_boosting_attr[level].sched_priority);
572
573         return true;
574 }
575
576 static void load_cpu_boosting_configs(void)
577 {
578         int level = CPU_BOOSTING_LEVEL_NONE;
579
580         /* Initialize type and nice of cpu throttling scheduler */
581         memset(&cpu_boosting_attr[level], 0, sizeof(struct sched_attr));
582         cpu_boosting_attr[level].size = sizeof(struct sched_attr);
583         cpu_boosting_attr[level].sched_nice = CPU_DEFAULT_NICE;
584         cpu_boosting_attr[level].sched_policy = SCHED_OTHER;
585         cpu_boosting_success[level] = true;
586
587         for (level = CPU_BOOSTING_LEVEL_STRONG; level < CPU_BOOSTING_LEVEL_END; level++) {
588                 cpu_boosting_success[level] = load_cpu_boosting_config(level);
589                 if (!cpu_boosting_success[level])
590                         _I("[CPU-BOOSTING] Boosting (level = %d) is not supported", level);
591                 free_cpu_boosting_conf(level);
592         }
593 }
594
595 static void cpu_boosting_send_reply(int sock, cpu_boosting_output_t output)
596 {
597         int byte;
598
599         if (sock < 0) {
600                 _E("[CPU-BOOSTING] socket cannot be negative");
601                 goto remove_output;
602         }
603
604         if (output.success) {
605                 /* Send a header first */
606                 byte = send(sock, (const void *)&output, sizeof(output), 0);
607                 if (byte != sizeof(output)) {
608                         _E("[CPU-BOOSTING] Server output size is %u, but sent size is %d",
609                                         (unsigned int)sizeof(output), byte);
610                         goto remove_output;
611                 }
612
613                 /* Check body size */
614                 byte = send(sock, (const void *)output.level.tid_level,
615                                 output.level.tid_count * sizeof(int), 0);
616                 if (byte != output.level.tid_count * sizeof(int)) {
617                         _E("[CPU-BOOSTING] Server output size is %u, but sent size is %d",
618                                         output.level.tid_count * (unsigned int)sizeof(int), byte);
619                         goto remove_output;
620                 }
621         }
622         else {
623                 /* Send a header */
624                 byte = send(sock, (const void *)&output.success, sizeof(output.success), 0);
625                 if (byte != sizeof(output.success)) {
626                         _E("[CPU-BOOSTING] Server output size is %u, but sent size is %d",
627                                         (unsigned int)sizeof(output.success), byte);
628                         goto remove_output;
629                 }
630         }
631
632 remove_output:
633         if (output.level.tid_level)
634                 free(output.level.tid_level);
635 }
636
637 static int cpu_boosting_enqueue_by_socket(int sock,
638                 struct syscommon_resourced_cpu_boosting_input **input_p)
639 {
640         int ret = RESOURCED_ERROR_NONE;
641         int byte;
642
643         struct syscommon_resourced_cpu_boosting_input *input =
644                 cpu_boosting_new_request();
645         if (input == NULL) {
646                 ret = RESOURCED_ERROR_OUT_OF_MEMORY;
647                 goto destroy_input;
648         }
649         else
650                 *input_p = input;
651
652         /* Get a header from the client */
653         byte = recv(sock, (void *)&input->client_input, sizeof(input->client_input), 0);
654         if (byte != sizeof(input->client_input)) {
655                 ret = RESOURCED_ERROR_FAIL;
656                 if (byte < 0) {
657                         _E("[CPU-BOOSTING] error is based on %m");
658                 }
659                 else if (byte > 0) {
660                         _E("[CPU-BOOSTING] client input size is %u, but received size is %d",
661                                         (unsigned int)sizeof(input->client_input), byte);
662                         input->client_input.pid.tid = NULL;
663                         input->client_input.dest = NULL;
664                 }
665
666                 goto destroy_input;
667         }
668         input->client_input.pid.tid = NULL;
669         input->client_input.dest = NULL;
670
671         if (input->client_input.command == CPU_BOOSTING_COMMAND_CLEAR ||
672                 input->client_input.command == CPU_BOOSTING_COMMAND_SET) {
673                 if (input->client_input.level > CPU_BOOSTING_LEVEL_WEAK) {
674                         ret = RESOURCED_ERROR_FAIL;
675                         _E("[CPU-BOOSTING] cpu boosting (level = %d) is out of scope",
676                                         input->client_input.level);
677                         goto destroy_input;
678                 }
679
680                 if (!cpu_boosting_success[input->client_input.level]) {
681                         ret = RESOURCED_ERROR_FAIL;
682                         _I("[CPU-BOOSTING] cpu boosting (level = %d) is not supported",
683                                         input->client_input.level);
684                         goto destroy_input;
685                 }
686         }
687
688         /* Check body size */
689         if (input->client_input.body_len > 0 &&
690             input->client_input.pid.tid_count > 0) {
691                 pid_t *tid_list;
692                 int body_len = input->client_input.body_len;
693
694                 tid_list = (pid_t *)calloc(1, body_len);
695                 if (tid_list == NULL) {
696                         _E("[CPU-BOOSTING] Failed to allocate memory");
697                         ret = RESOURCED_ERROR_OUT_OF_MEMORY;
698                         goto destroy_input;
699                 }
700                 else
701                         input->client_input.pid.tid = tid_list;
702
703                 /* Get a body from the client */
704                 byte = recv(sock, (void *)tid_list, body_len, 0);
705                 if (byte != body_len) {
706                         ret = RESOURCED_ERROR_FAIL;
707                         if (byte < 0) {
708                                 _E("[CPU-BOOSTING] error is based on %m");
709                         }
710                         else if (byte > 0)
711                                 _E("[CPU-BOOSTING] client input size is %d, but received size is %d",
712                                                 body_len, byte);
713
714                         goto destroy_input;
715                 }
716         }
717         else if (input->client_input.body_len == 0) {
718                 switch (input->client_input.command) {
719                         case CPU_BOOSTING_COMMAND_SET:
720                         case CPU_BOOSTING_COMMAND_CLEAR:
721                         case CPU_BOOSTING_COMMAND_GET:
722                         case CPU_BOOSTING_COMMAND_REGISTER_DESTINATION:
723                                 ret = cpu_boosting_get_tids(input->client_input.pid.pid, &input->client_input.pid);
724                                 if (ret != RESOURCED_ERROR_NONE)
725                                         goto destroy_input;
726
727                                 break;
728                         default:
729                                 //do nothing
730                                 break;
731                 }
732         }
733         else {
734                 _E("[CPU-BOOSTING] Unknown header format");
735                 ret = RESOURCED_ERROR_FAIL;
736                 goto destroy_input;
737         }
738
739         if (input->client_input.command == CPU_BOOSTING_COMMAND_GET)
740                 input->sock = sock;     /* For a reply */
741         else if (input->client_input.command == CPU_BOOSTING_COMMAND_REGISTER_DESTINATION ||
742                          input->client_input.command == CPU_BOOSTING_COMMAND_UNREGISTER_DESTINATION ||
743                          input->client_input.command == CPU_BOOSTING_COMMAND_SET_INHERITANCE ||
744                          input->client_input.command == CPU_BOOSTING_COMMAND_CLEAR_INHERITANCE) {
745                 char *dest;
746
747                 if (input->client_input.dest_len <= 0 ||
748                         input->client_input.dest_len >= MAX_PATH_LENGTH) {
749                         _E("[CPU-BOOSTING] Wrong destination name");
750                         ret = RESOURCED_ERROR_FAIL;
751                         goto destroy_input;
752                 }
753
754                 dest = (char *)calloc(input->client_input.dest_len + 1, sizeof(char));
755                 if (dest == NULL) {
756                         _E("[CPU-BOOSTING] Failed to allocate memory");
757                         ret = RESOURCED_ERROR_OUT_OF_MEMORY;
758                         goto destroy_input;
759                 }
760                 else
761                         input->client_input.dest = dest;
762
763                 byte = recv(sock, (void *)dest, input->client_input.dest_len, 0);
764                 if (byte != input->client_input.dest_len) {
765                         ret = RESOURCED_ERROR_FAIL;
766                         if (byte < 0) {
767                                 _E("[CPU-BOOSTING] error is based on %m");
768                         }
769                         else if (byte > 0)
770                                 _E("[CPU-BOOSTING] client input size is %d, but received size is %d",
771                                                 input->client_input.dest_len, byte);
772
773                         goto destroy_input;
774                 }
775         }
776
777         return RESOURCED_ERROR_NONE;
778
779 destroy_input:
780         cpu_boosting_destroy_request(input);
781
782         return ret;
783 }
784
785 /* Called by resourced main thread */
786 static int cpu_boosting_enqueue_by_conf(void *data,
787                 struct syscommon_resourced_cpu_boosting_input **input)
788 {
789         assert(data);
790
791         int ret;
792         struct proc_status *ps = (struct proc_status *)data;
793         cpu_boosting_level_e cpu_boosting_level;
794         resource_pid_t pid;
795
796         if (ps->pci == NULL) {
797                 _E("[CPU-BOOSTING] Process conf should not be NULL");
798                 return RESOURCED_ERROR_FAIL;
799         }
800
801         if (ps->pid <= 0) {
802                 _E("[CPU-BOOSTING] pid should be larger than 0");
803                 return RESOURCED_ERROR_FAIL;
804         }
805
806         cpu_boosting_level = ps->pci->cpu_boosting_level;
807         if (!cpu_boosting_success[cpu_boosting_level]) {
808                 _I("[CPU-BOOSTING] cpu boosting (level = %d) is not supported", cpu_boosting_level);
809                 return RESOURCED_ERROR_NONE;
810         }
811
812         ret = cpu_boosting_get_tids(ps->pid, &pid);
813         if (ret != RESOURCED_ERROR_NONE)
814                 return ret;
815
816         *input = cpu_boosting_new_request();
817         if (*input == NULL)
818                 return RESOURCED_ERROR_FAIL;
819
820         CPU_BOOSTING_SET_REQUEST(*input, CPU_BOOSTING_COMMAND_SET,
821                         cpu_boosting_level, -1, pid, 0);
822
823         return RESOURCED_ERROR_NONE;
824 }
825
826 static gboolean cpu_boosting_timeout(gpointer data)
827 {
828         int ret;
829         struct syscommon_resourced_cpu_boosting_input *input =
830                 (struct syscommon_resourced_cpu_boosting_input *)data;
831
832         if (input == NULL) {
833                 _E("[CPU-BOOSTING] input argument of cpu boosting should not be NULL");
834                 goto timer_out;
835         }
836
837         int tid_count = input->client_input.pid.tid_count;
838         int *tid_list = input->client_input.pid.tid;
839
840         for (int i = 0; i < tid_count; i++) {
841                 struct syscommon_resourced_cpu_boosting_info *cpu_boosting_info = NULL;
842
843                 if (tid_list[i] <= 0) {
844                         _W("[CPU-BOOSTING] Thread (id = %d) should be larger than 0", tid_list[i]);
845                         continue;
846                 }
847
848                 find_cpu_boosting_info_in_tables(&cpu_boosting_info, &tid_list[i]);
849
850                 /**
851                  * cpu_boosting_info is NULL when clearing boosting before timeout.
852                  * cpu_boosting_info->gsource_id != *(input->gsource_id)
853                  * when setting cpu boosting again before timeout.
854                  */
855                 if (cpu_boosting_info == NULL || input->gsource_id == NULL
856                                 || cpu_boosting_info->gsource_id != *(input->gsource_id))
857                         continue;
858
859                 switch (cpu_boosting_info->level) {
860                 case CPU_BOOSTING_LEVEL_STRONG:
861                 case CPU_BOOSTING_LEVEL_MEDIUM:
862                 case CPU_BOOSTING_LEVEL_WEAK:
863                         g_hash_table_remove(
864                                         g_cpu_boosting_info_table[cpu_boosting_info->level],
865                                         &tid_list[i]);
866                         break;
867                 case CPU_BOOSTING_LEVEL_NONE:
868                 default:
869                         _E("[CPU-BOOSTING] Unknown cpu boosting level (SIGABT)");
870                         assert(0);
871                 }
872
873                 ret = sched_setattr(tid_list[i], &cpu_boosting_attr[CPU_BOOSTING_LEVEL_NONE], 0);
874                 if (ret != RESOURCED_ERROR_NONE)
875                         _E("[CPU-BOOSTING] Failed to clear boost cpu of (tid = %d)", tid_list[i]);
876         }
877
878         if (input->remove_input)
879                 cpu_boosting_destroy_request(input);
880
881 timer_out:
882         return G_SOURCE_REMOVE;
883 }
884
885 static void cpu_boosting_find_and_insert_info(pid_t tid,
886                 cpu_boosting_flag_e cpu_boosting_flags, guint *timer_id,
887                 cpu_boosting_level_e cpu_boosting_level)
888 {
889         struct syscommon_resourced_cpu_boosting_info *cpu_boosting_info = NULL;
890
891         switch (cpu_boosting_level) {
892         case CPU_BOOSTING_LEVEL_STRONG:
893         case CPU_BOOSTING_LEVEL_MEDIUM:
894         case CPU_BOOSTING_LEVEL_WEAK:
895                 break;
896         case CPU_BOOSTING_LEVEL_NONE:
897         default:
898                 _E("[CPU-BOOSTING] Cannot boosting unknown or none level");
899                 return;
900         }
901
902         find_cpu_boosting_info_in_tables(&cpu_boosting_info, &tid);
903         if (cpu_boosting_info == NULL) {
904                 cpu_boosting_info = (struct syscommon_resourced_cpu_boosting_info *)
905                         calloc(1, sizeof (struct syscommon_resourced_cpu_boosting_info));
906                 if (cpu_boosting_info == NULL) {
907                         _E("[CPU-BOOSTING] Failed to allocate memory");
908                         return;
909                 }
910
911                 cpu_boosting_info->tid = tid;
912                 cpu_boosting_info->ref_cnt = 1;
913
914                 g_hash_table_insert(g_cpu_boosting_info_table[cpu_boosting_level],
915                                 &cpu_boosting_info->tid, cpu_boosting_info);
916         } else if (cpu_boosting_info->level != cpu_boosting_level) {
917                 /**
918                  * Do not free cpu_boosting_info in
919                  * g_cpu_boosting_info_table[cpu_boosting_info->level].
920                  * This is because the corresponding cpu_boosting_info
921                  * will be reused as a value in another table
922                  * (g_cpu_boosting_info_table[cpu_boosting_level]
923                  */
924                 cpu_boosting_info->ref_cnt++;
925                 g_hash_table_remove(
926                                 g_cpu_boosting_info_table[cpu_boosting_info->level],
927                                 &cpu_boosting_info->tid);
928                 g_hash_table_insert(g_cpu_boosting_info_table[cpu_boosting_level],
929                                 &cpu_boosting_info->tid, cpu_boosting_info);
930         }
931
932         cpu_boosting_info->level = cpu_boosting_level;
933
934         /**
935          * Register cpu_boosting_flags to handle cpu contention and
936          * restore cpu boosting level after solving cpu contention.
937          * For example, if resourced received CPU_BOOSTING_COMMAND_SET command
938          * with CPU_BOOSTING_RESET_ON_FORK, then turn on
939          * CPU_BOOSTING_RESET_ON_FORK flag
940          * when decreasing and restoring cpu boosting level.
941          */
942         cpu_boosting_info->cpu_boosting_flags = cpu_boosting_flags;
943
944         if (timer_id)
945                 cpu_boosting_info->gsource_id = *timer_id;
946         else
947                 cpu_boosting_info->gsource_id = 0;
948 }
949
950 static void
951 cpu_boosting_set(struct syscommon_resourced_cpu_boosting_input *input)
952 {
953         guint id = 0;
954         int fail_cnt = 0;
955         int success_cnt = 0;
956         struct sched_attr attr;
957         int tid_count = input->client_input.pid.tid_count;
958         int *tid_list = input->client_input.pid.tid;
959         int timeout_msec = input->client_input.timeout_msec;
960         cpu_boosting_level_e cpu_boosting_level = input->client_input.level;
961         cpu_boosting_flag_e cpu_boosting_flags = input->client_input.flags;
962
963         switch (cpu_boosting_level) {
964         case CPU_BOOSTING_LEVEL_STRONG:
965         case CPU_BOOSTING_LEVEL_MEDIUM:
966         case CPU_BOOSTING_LEVEL_WEAK:
967                 break;
968         case CPU_BOOSTING_LEVEL_NONE:
969                 return;
970         default:
971                 _E("[CPU-BOOSTING] Unknown boosting level");
972                 return;
973         }
974
975         attr = cpu_boosting_attr[cpu_boosting_level];
976         if (cpu_boosting_flags & CPU_BOOSTING_RESET_ON_FORK) {
977                 _I("[CPU-BOOSTING] Turn on SCHED_RESET_ON_FORK flag");
978                 attr.sched_policy |= SCHED_RESET_ON_FORK;
979         }
980
981         for (int i = 0; i < tid_count; i++) {
982                 if (tid_list[i] <= 0) {
983                         _W("[CPU-BOOSTING] Thread (id = %d) should be larger than 0", tid_list[i]);
984                         fail_cnt++;
985                         continue;
986                 }
987
988                 if (sched_setattr(tid_list[i], &attr, 0) < 0) {
989                         _W("[CPU-BOOSTING] Failed to boost cpu of (tid = %d) with (level = %d)",
990                                         tid_list[i], cpu_boosting_level);
991                         tid_list[i] = 0;
992                         fail_cnt++;
993                         continue;
994                 }
995                 success_cnt++;
996         }
997
998         if (fail_cnt > 0)
999                 _W("[CPU-BOOSTING] Boosting success ratio = %d/%d", success_cnt, fail_cnt + success_cnt);
1000
1001         if (success_cnt > 0) {
1002                 /**
1003                  * If timeout_msec is negative, then do not set timer.
1004                  * This is because, cpu boosting should be continued until
1005                  * clearing cpu boosting.
1006                  * When the governor requests CPU_BOOSTING_COMMAND_SET command,
1007                  * input->gsource_id is not NULL. Therefore, new timer is always
1008                  * skipped during handling cpu contention.
1009                  */
1010                 if (timeout_msec > 0 && input->gsource_id == NULL) {
1011                         GSource *source;
1012
1013                         input->gsource_id = g_new(guint, 1);
1014
1015                         source = g_timeout_source_new(timeout_msec);
1016                         g_source_set_callback(source, cpu_boosting_timeout, input, NULL);
1017                         *(input->gsource_id) = g_source_attach(source, cpu_boosting_context);
1018                         g_source_unref(source);
1019
1020                         id = *(input->gsource_id);
1021                 }
1022
1023                 for (int i = 0; i < tid_count; i++) {
1024                         if (tid_list[i] <= 0)
1025                                 continue;
1026
1027                         cpu_boosting_find_and_insert_info(tid_list[i], cpu_boosting_flags,
1028                                         input->gsource_id, cpu_boosting_level);
1029                 }
1030         }
1031
1032         if (id == 0 && input->remove_input)
1033                 cpu_boosting_destroy_request(input);
1034 }
1035
1036 static void cpu_boosting_clear(struct syscommon_resourced_cpu_boosting_input *input)
1037 {
1038         int ret;
1039         int fail_cnt = 0;
1040         int success_cnt = 0;
1041         int tid_count = input->client_input.pid.tid_count;
1042         int *tid_list = input->client_input.pid.tid;
1043
1044         for (int i = 0; i < tid_count; i++) {
1045                 if (tid_list[i] <= 0) {
1046                         _W("[CPU-BOOSTING] Thread (id = %d) should be larger than 0",
1047                                         tid_list[i]);
1048                         continue;
1049                 }
1050
1051                 remove_cpu_boosting_info_in_tables(&tid_list[i]);
1052
1053                 ret = sched_setattr(tid_list[i],
1054                                 &cpu_boosting_attr[CPU_BOOSTING_LEVEL_NONE], 0);
1055                 if (ret != RESOURCED_ERROR_NONE) {
1056                         _E("[CPU-BOOSTING] Failed to clear boost cpu of (tid = %d)", tid_list[i]);
1057                         fail_cnt++;
1058                         continue;
1059                 }
1060                 success_cnt++;
1061         }
1062
1063         if (fail_cnt > 0)
1064                 _W("[CPU-BOOSTING] Boosting success ratio = %d/%d", success_cnt, fail_cnt + success_cnt);
1065
1066         if (input->remove_input)
1067                 cpu_boosting_destroy_request(input);
1068 }
1069
1070 static void cpu_boosting_get(struct syscommon_resourced_cpu_boosting_input *input)
1071 {
1072         int fail_cnt = 0;
1073         int success_cnt = 0;
1074         struct sched_attr attr;
1075         cpu_boosting_output_t output;
1076         int tid_count = input->client_input.pid.tid_count;
1077         int *tid_list = input->client_input.pid.tid;
1078
1079         memset(&output, 0, sizeof (output));
1080         output.level.tid_level = (int *)calloc(tid_count, sizeof (int));
1081         if (output.level.tid_level == NULL) {
1082                 _E("[CPU-BOOSTING] Failed to allocate memory");
1083                 fail_cnt = 1;
1084                 goto output_update;
1085         }
1086
1087         for (int i = 0; i < tid_count; i++) {
1088                 struct syscommon_resourced_cpu_boosting_info *cpu_boosting_info = NULL;
1089
1090                 if (tid_list[i] <= 0) {
1091                         fail_cnt++;
1092                         _W("[CPU-BOOSTING] Thread (id = %d) should be larger than 0", tid_list[i]);
1093                         continue;
1094                 }
1095
1096                 find_cpu_boosting_info_in_tables(&cpu_boosting_info, &tid_list[i]);
1097                 if (cpu_boosting_info == NULL) {
1098                         if (sched_getattr(tid_list[i], &attr, 0) < 0) {
1099                                 fail_cnt++;
1100                                 continue;
1101                         }
1102
1103                         output.level.tid_level[success_cnt] = cpu_boosting_level_search(attr);
1104                         success_cnt++;
1105                 } else {
1106                         output.level.tid_level[success_cnt] = cpu_boosting_info->level;
1107                         success_cnt++;
1108                 }
1109         }
1110         output.level.tid_count = success_cnt;
1111
1112 output_update:
1113         if (fail_cnt > 0) {
1114                 _W("[CPU-BOOSTING] Get boosting success ratio = %d/%d", success_cnt, fail_cnt + success_cnt);
1115                 output.success = false;
1116         }
1117         else
1118                 output.success = true;
1119
1120         cpu_boosting_send_reply(input->sock, output);
1121         cpu_boosting_destroy_request(input);
1122 }
1123
1124 static void
1125 cpu_boosting_set_inheritance(struct syscommon_resourced_cpu_boosting_input *input)
1126 {
1127         struct syscommon_resourced_cpu_boosting_info *cpu_boosting_info = NULL;
1128         struct syscommon_resourced_cpu_boosting_input *input_dest;
1129         pid_t source_tid;
1130         struct sched_attr attr;
1131         cpu_boosting_level_e boosting_level;
1132
1133
1134         source_tid = input->client_input.pid.pid;
1135         if (source_tid <= 0) {
1136                 _E("[CPU-BOOSTING] source tid should be larger than 0");
1137                 goto destroy_input;
1138         }
1139
1140         input_dest = g_hash_table_lookup(dest_table, input->client_input.dest);
1141         if (input_dest == NULL) {
1142                 _E("[CPU-BOOSTING] %s is not registered before", input->client_input.dest);
1143                 goto destroy_input;
1144         }
1145
1146         find_cpu_boosting_info_in_tables(&cpu_boosting_info, &source_tid);
1147
1148         /**
1149          * This source thread is not boosted by resourced.
1150          * Therefore, resourced needs to figure out cpu priority or nice value through the kernel.
1151          */
1152         if (cpu_boosting_info == NULL) {
1153                 if (sched_getattr(source_tid, &attr, 0) < 0) {
1154                         _E("[CPU-BOOSTING] Failed to get boost cpu of (tid = %d)", source_tid);
1155                         goto destroy_input;
1156                 }
1157
1158                 boosting_level = cpu_boosting_level_search(attr);
1159         } else
1160                 boosting_level = cpu_boosting_info->level;
1161
1162 #ifdef  CONFIG_DEBUG_CPU_BOOSTING
1163         int tid_count = input_dest->client_input.pid.tid_count;
1164         int *tid_list = input_dest->client_input.pid.tid;
1165
1166         for (int i = 0; i < tid_count; i++) {
1167                 if (tid_list[i] > 0)
1168                         _D("[CPU-BOOSTING] tid = %d", tid_list[i]);
1169                 else
1170                         _E("[CPU-BOOSTING] Thread (id = %d) should be larger than 0", tid_list[i]);
1171         }
1172         _D("[CPU-BOOSTING] %s is boost set", input_dest->client_input.dest);
1173 #endif
1174         if (input_dest->gsource_id)
1175                 g_free(g_steal_pointer(&input_dest->gsource_id));
1176
1177         input_dest->client_input.timeout_msec = input->client_input.timeout_msec;
1178         input_dest->client_input.level = boosting_level;
1179         cpu_boosting_set(input_dest);
1180
1181 destroy_input:
1182         cpu_boosting_destroy_request(input);
1183 }
1184
1185 static void
1186 cpu_boosting_clear_inheritance(struct syscommon_resourced_cpu_boosting_input *input)
1187 {
1188         struct syscommon_resourced_cpu_boosting_input *input_dest =
1189                 g_hash_table_lookup(dest_table, input->client_input.dest);
1190         if (input_dest == NULL) {
1191                 _E("[CPU-BOOSTING] %s is not registered before", input->client_input.dest);
1192                 goto destroy_input;
1193         }
1194
1195 #ifdef  CONFIG_DEBUG_CPU_BOOSTING
1196         int tid_count = input_dest->client_input.pid.tid_count;
1197         int *tid_list = input_dest->client_input.pid.tid;
1198
1199         for (int i = 0; i < tid_count; i++) {
1200                 if (tid_list[i] > 0)
1201                         _D("[CPU-BOOSTING] tid = %d", tid_list[i]);
1202                 else
1203                         _E("[CPU-BOOSTING] Thread (id = %d) should be larger than 0", tid_list[i]);
1204         }
1205         _D("[CPU-BOOSTING] %s is boost cleared", input_dest->client_input.dest);
1206 #endif
1207
1208         cpu_boosting_clear(input_dest);
1209
1210 destroy_input:
1211         cpu_boosting_destroy_request(input);
1212 }
1213
1214 static void cpu_boosting_register_destination(
1215                 struct syscommon_resourced_cpu_boosting_input *input)
1216 {
1217         if (input->client_input.dest == NULL) {
1218                 _E("[CPU-BOOSTING] Boosting target name cannot be NULL");
1219                 return;
1220         }
1221
1222 #ifdef  CONFIG_DEBUG_CPU_BOOSTING
1223         int tid_count = input->client_input.pid.tid_count;
1224         int *tid_list = input->client_input.pid.tid;
1225
1226         for (int i = 0; i < tid_count; i++) {
1227                 if (tid_list[i] > 0)
1228                         _D("[CPU-BOOSTING] tid = %d", tid_list[i]);
1229                 else
1230                         _E("[CPU-BOOSTING] Thread (id = %d) should be larger than 0", tid_list[i]);
1231         }
1232         _D("[CPU-BOOSTING] %s is inserted", input->client_input.dest);
1233 #endif
1234
1235         input->remove_input = false;
1236         g_hash_table_insert(dest_table, (gpointer)input->client_input.dest, input);
1237 }
1238
1239 static void cpu_boosting_unregister_destination(
1240                 struct syscommon_resourced_cpu_boosting_input *input)
1241 {
1242         if (input->client_input.dest == NULL) {
1243                 _E("[CPU-BOOSTING] Boosting target name cannot be NULL");
1244                 return;
1245         }
1246
1247 #ifdef  CONFIG_DEBUG_CPU_BOOSTING
1248         struct syscommon_resourced_cpu_boosting_input *input_dest =
1249                 g_hash_table_lookup(dest_table, input->client_input.dest);
1250         if (input_dest == NULL) {
1251                 _E("[CPU-BOOSTING] %s does not registered before", input->client_input.dest);
1252                 return;
1253         }
1254
1255         int tid_count = input_dest->client_input.pid.tid_count;
1256         int *tid_list = input_dest->client_input.pid.tid;
1257
1258         for (int i = 0; i < tid_count; i++) {
1259                 if (tid_list[i] > 0)
1260                         _D("[CPU-BOOSTING] tid = %d", tid_list[i]);
1261                 else
1262                         _E("[CPU-BOOSTING] Thread (id = %d) should be larger than 0", tid_list[i]);
1263         }
1264 #endif
1265
1266 #ifdef  CONFIG_DEBUG_CPU_BOOSTING
1267         gboolean ret = g_hash_table_remove(dest_table, input->client_input.dest);
1268         _D("[CPU-BOOSTING] %s is %sremoved", input->client_input.dest,
1269                         ret ? "" : "already ");
1270 #else
1271         g_hash_table_remove(dest_table, input->client_input.dest);
1272 #endif
1273         cpu_boosting_destroy_request(input);
1274 }
1275
1276 static int cpu_boosting_init_socket(void)
1277 {
1278         /* Create a server socket */
1279         int ret;
1280         int sock = -1;
1281         socklen_t size;
1282         struct sockaddr_un sockaddr = {0, };
1283         int n = sd_listen_fds(0);
1284         if (n > 0) {
1285                 for (int fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) {
1286                         if (sd_is_socket_unix(fd, SOCK_STREAM, 1, SOCK_PATH, 0) > 0) {
1287                                 sock = fd;
1288                                 break;
1289                         }
1290                 }
1291         }
1292
1293         if (sock < 0) {
1294                 sock = socket(AF_UNIX, SOCK_STREAM, 0);
1295                 if (sock < 0) {
1296                         _E("[CPU-BOOSTING] Failed to allocate a socket");
1297                         goto return_sock;
1298                 }
1299
1300                 sockaddr.sun_family = AF_UNIX;
1301                 strncpy(sockaddr.sun_path, (const char *)SOCK_PATH, strlen(SOCK_PATH)+1);
1302                 size = sizeof(sockaddr);
1303                 ret = unlink(SOCK_PATH);
1304                 if (ret < 0)
1305                         _W("[CPU-BOOSTING] Failed to unlink because of %m");
1306
1307                 ret = bind(sock, (struct sockaddr *)&sockaddr, size);
1308                 if (ret < 0) {
1309                         _E("[CPU-BOOSTING] Failed to bind socket");
1310                         goto close_sock;
1311                 }
1312
1313                 ret = listen(sock, SOMAXCONN);
1314                 if (ret < 0) {
1315                         _E("[CPU-BOOSTING] Failed to listen socket");
1316                         goto close_sock;
1317                 }
1318         }
1319
1320 return_sock:
1321         return sock;
1322
1323 close_sock:
1324         close(sock);
1325         return 0;
1326 }
1327
1328 static void
1329 cpu_boosting_handle_command(struct syscommon_resourced_cpu_boosting_input *input)
1330 {
1331         if (!input) {
1332                 _E("[CPU-BOOSTING] Cpu boosting input data structure should not be NULL");
1333                 return;
1334         }
1335
1336         /*
1337          * Whether current boosting level is supported or not should be checked before
1338          * calling CPU_BOOSTING_ENQUEUE_REQUEST. That is the cpu boosting thread ignores it
1339          */
1340         switch (input->client_input.command) {
1341                 case CPU_BOOSTING_COMMAND_SET:
1342                         input->remove_input = true;
1343                         cpu_boosting_set(input);
1344                         break;
1345                 case CPU_BOOSTING_COMMAND_CLEAR:
1346                         input->remove_input = true;
1347                         cpu_boosting_clear(input);
1348                         break;
1349                 case CPU_BOOSTING_COMMAND_GET:
1350                         cpu_boosting_get(input);
1351                         break;
1352                 case CPU_BOOSTING_COMMAND_SET_INHERITANCE:
1353                         cpu_boosting_set_inheritance(input);
1354                         break;
1355                 case CPU_BOOSTING_COMMAND_CLEAR_INHERITANCE:
1356                         cpu_boosting_clear_inheritance(input);
1357                         break;
1358                 case CPU_BOOSTING_COMMAND_REGISTER_DESTINATION:
1359                         cpu_boosting_register_destination(input);
1360                         break;
1361                 case CPU_BOOSTING_COMMAND_UNREGISTER_DESTINATION:
1362                         cpu_boosting_unregister_destination(input);
1363                         break;
1364                 case CPU_BOOSTING_COMMAND_NONE:
1365                 default:
1366                         _E("[CPU-BOOSTING] Unknown cpu boosting (command = %d)", input->client_input.command);
1367                         cpu_boosting_destroy_request(input);
1368         }
1369 }
1370
1371 static int cpu_boosting_recv_from_conf(void *data)
1372 {
1373         int ret;
1374         struct syscommon_resourced_cpu_boosting_input *input = NULL;
1375
1376         if ((ret = cpu_boosting_enqueue_by_conf(data, &input)) < 0 ||
1377                         input == NULL)
1378                 return ret;
1379
1380         if (input->client_input.pid.tid_count == 0) {
1381                 _E("[CPU-BOOSTING] The number of tids should be larger than 0");
1382                 cpu_boosting_destroy_request(input);
1383                 return RESOURCED_ERROR_FAIL;
1384         }
1385
1386         cpu_boosting_handle_command(input);
1387
1388         return RESOURCED_ERROR_NONE;
1389 }
1390
1391 /* Client asks send() to the resourced, so call recv() */
1392 static bool cpu_boosting_recv_from_client(int fd, void *data)
1393 {
1394         int client_sock = fd;
1395         struct syscommon_resourced_cpu_boosting_input *input = NULL;
1396
1397         if (client_sock <= 0) {
1398                 _E("[CPU-BOOSTING] client socket should be larger than 0");
1399                 goto remove_handler;
1400         }
1401
1402         if (cpu_boosting_enqueue_by_socket(client_sock, &input) < 0 ||
1403                         input == NULL) {
1404                 goto remove_handler;
1405         }
1406
1407         cpu_boosting_handle_command(input);
1408
1409         return G_SOURCE_CONTINUE;
1410
1411 remove_handler:
1412         if (client_sock > 0)
1413                 close(client_sock);
1414
1415         return G_SOURCE_REMOVE;
1416 }
1417
1418 /* Client asks connect() to the resourced */
1419 static bool cpu_boosting_connect_from_client(int fd, void *data)
1420 {
1421         int ret;
1422         int master_sock = fd;
1423
1424         if (master_sock <= 0) {
1425                 _E("[CPU-BOOSTING] master socket should be larger than 0");
1426                 goto remove_handler;
1427         }
1428
1429         int new_sock = accept(master_sock, NULL, NULL);
1430         if (new_sock < 0) {
1431                 _E("[CPU-BOOSTING] Failed to allocate a new socket for the client (%m)");
1432                 goto remove_handler;
1433         }
1434
1435         /* Add an event handler to receive data from the client */
1436         ret = add_fd_read_handler(cpu_boosting_context, new_sock, cpu_boosting_recv_from_client,
1437                         NULL, NULL, NULL);
1438         if (ret < 0) {
1439                 _E("[CPU-BOOSTING] Failed to add event handler of (sock = %d)", new_sock);
1440                 close(new_sock);
1441         }
1442
1443         return G_SOURCE_CONTINUE;
1444
1445 remove_handler:
1446         if (master_sock > 0)
1447                 close(master_sock);
1448
1449         return G_SOURCE_REMOVE;
1450 }
1451
1452 static int cpu_boosting_init_server(void)
1453 {
1454         int ret;
1455         static fd_handler_h handler;
1456         int sock = cpu_boosting_init_socket();
1457         if (sock > 0) {
1458                 ret = add_fd_read_handler(cpu_boosting_context, sock,
1459                                 cpu_boosting_connect_from_client, NULL, NULL, &handler);
1460                 if (ret < 0) {
1461                         _E("[CPU-BOOSTING] Failed to add event handler of (sock = %d)", sock);
1462                         close(sock);
1463                         return RESOURCED_ERROR_FAIL;
1464                 }
1465         }
1466         else
1467                 return RESOURCED_ERROR_FAIL;
1468
1469         return RESOURCED_ERROR_NONE;
1470 }
1471
1472 /* Cpu boosting thread's main code */
1473 static void *cpu_boosting_func(void *data)
1474 {
1475         /* Boost cpu boosting thread itself */
1476         setpriority(PRIO_PROCESS, 0, CPU_MIN_NICE);
1477
1478         cpu_boosting_context = g_main_context_new();
1479         g_main_context_push_thread_default(cpu_boosting_context);
1480         cpu_boosting_loop = g_main_loop_new(cpu_boosting_context, FALSE);
1481
1482         /* For the socket-based client */
1483         if (cpu_boosting_init_server() < 0) {
1484                 _E("[CPU-BOOSTING] Failed to initialize server environment");
1485                 goto thread_exit;
1486         }
1487         else
1488                 _D("[CPU-BOOSTING] Success to initialize server environment");
1489
1490         g_main_loop_run(cpu_boosting_loop);
1491
1492 thread_exit:
1493         g_main_loop_unref(cpu_boosting_loop);
1494         g_main_context_unref(cpu_boosting_context);
1495         pthread_exit(NULL);
1496 }
1497
1498 static int cpu_boosting_thread_activate(void)
1499 {
1500         int ret = pthread_create(&cpu_boosting_thread, NULL,
1501                         cpu_boosting_func, NULL);
1502         if (ret == 0) {
1503                 pthread_detach(cpu_boosting_thread);
1504                 ret = RESOURCED_ERROR_NONE;
1505         }
1506         else {
1507                 _E("[CPU-BOOSTING] Failed to create cpu boosting thread");
1508                 ret = RESOURCED_ERROR_FAIL;
1509         }
1510
1511         return ret;
1512 }
1513
1514 static void cpu_boosting_thread_deactivate(void)
1515 {
1516         g_main_loop_quit(cpu_boosting_loop);
1517 }
1518
1519 /* Called by resourced main thread */
1520 static int cpu_boosting_init(void *data)
1521 {
1522         int ret;
1523
1524         load_cpu_boosting_configs();
1525
1526         ret = cpu_boosting_thread_activate();
1527         if (ret != RESOURCED_ERROR_NONE)
1528                 return ret;
1529
1530         for (int level = CPU_BOOSTING_LEVEL_STRONG; level < CPU_BOOSTING_LEVEL_END; level++) {
1531                 g_cpu_boosting_info_table[level] = g_hash_table_new_full(
1532                                 g_int_hash, g_int_equal, NULL, free_cpu_boosting_info);
1533                 g_assert(g_cpu_boosting_info_table[level]);
1534         }
1535
1536         dest_table = g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
1537                         cpu_boosting_destroy_request);
1538         g_assert(dest_table);
1539
1540         g_cpu_contention_handle_data_table =
1541                 g_hash_table_new_full(g_int_hash, g_int_equal, NULL,
1542                                 free_cpu_contention_handle_data);
1543         g_assert(g_cpu_contention_handle_data_table);
1544
1545         /* For the conf-based client */
1546         register_notifier(RESOURCED_NOTIFIER_BOOSTING_RESOURCE, cpu_boosting_recv_from_conf);
1547
1548         return RESOURCED_ERROR_NONE;
1549 }
1550
1551 /* Called by resourced main thread */
1552 static int cpu_boosting_finalize(void *data)
1553 {
1554         cpu_boosting_thread_deactivate();
1555         for (int level = CPU_BOOSTING_LEVEL_STRONG; level < CPU_BOOSTING_LEVEL_END; level++)
1556                 g_hash_table_destroy(g_cpu_boosting_info_table[level]);
1557         g_hash_table_destroy(dest_table);
1558         g_hash_table_destroy(g_cpu_contention_handle_data_table);
1559
1560         unregister_notifier(RESOURCED_NOTIFIER_BOOSTING_RESOURCE, cpu_boosting_recv_from_conf);
1561         return RESOURCED_ERROR_NONE;
1562 }
1563
1564 static struct module_ops cpu_boosting_modules_ops = {
1565         .priority = MODULE_PRIORITY_EARLY,
1566         .name = "cpu-boosting",
1567         .init = cpu_boosting_init,
1568         .exit = cpu_boosting_finalize,
1569 };
1570
1571 MODULE_REGISTER(&cpu_boosting_modules_ops)