--- /dev/null
+/**
+ * system-plugin-resourced-generic
+ *
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <dirent.h>
+#include <assert.h>
+#include <glib.h>
+
+#include <system/syscommon-plugin-common-interface.h>
+#include <system/syscommon-plugin-resourced-cpu-boosting-interface.h>
+
+#include <libsyscommon/log.h>
+
+#define EXPORT __attribute__ ((visibility("default")))
+
+/**
+ * This macro is already defined in resourced, so if common utils of resourced
+ * are moved to shared library such as libsyscommon,
+ * then remove this macro in this file.
+ */
+#define gslist_for_each_safe(head, elem, elem_next, node) \
+ for (elem = head, elem_next = g_slist_next(elem), node = NULL; \
+ elem && ((node = elem->data) != NULL); \
+ elem = elem_next, elem_next = g_slist_next(elem), node = NULL)
+
+static syscommon_plugin_backend_resourced_cpu_boosting_funcs g_cpu_boosting_funcs;
+
+#define CPU_BOOSTING_MAKE_REQUEST( \
+ _input, _command, _level, _timeout_msec, \
+ _pid, _flags, _remove_input) \
+{ \
+ (_input)->client_input.command = _command; \
+ (_input)->client_input.timeout_msec = _timeout_msec; \
+ (_input)->client_input.level = _level; \
+ (_input)->client_input.pid = _pid; \
+ (_input)->client_input.flags = _flags; \
+ (_input)->remove_input = _remove_input; \
+}
+
+#define WATCHDOG_CGROUP_PATH "/sys/fs/cgroup/watchdog"
+#define CGROUP_TASKS_NAME "tasks"
+#define PATH_BUF_MAX 256
+#define NAME_BUF_MAX 34
+
+static bool is_in_cgroup_tasks(pid_t tid, const char *path)
+{
+ char filename[PATH_BUF_MAX];
+ char threadid[NAME_BUF_MAX];
+ FILE *fp = NULL;
+
+ snprintf(filename, PATH_BUF_MAX, "%s/%s", path, CGROUP_TASKS_NAME);
+ fp = fopen(filename, "r");
+ if (!fp) {
+ _E("[CPU-BOOSTING-BACKEND] Failed to open %s", filename);
+ return false;
+ }
+
+ while (1) {
+ if (fgets(threadid, sizeof(threadid), fp) == NULL)
+ break;
+
+ if (tid == atoi(threadid)) {
+ fclose(fp);
+ return true;
+ }
+ }
+
+ fclose(fp);
+ return false;
+}
+
+static bool is_watchdog_process(pid_t tid)
+{
+ int ret;
+ DIR *dp = NULL;
+ struct dirent *dir;
+
+ dp = opendir(WATCHDOG_CGROUP_PATH);
+ if (!dp) {
+ _E("[CPU-BOOSTING-BACKEND] Failed to open dir %s", WATCHDOG_CGROUP_PATH);
+ return false;
+ }
+
+ while (1) {
+ char *path = NULL;
+
+ dir = readdir(dp);
+ if (!dir)
+ break;
+
+ if (dir->d_type != DT_DIR)
+ continue;
+
+ if (!strncmp(dir->d_name, ".", 2) || !strncmp(dir->d_name, "..", 3))
+ continue;
+
+ ret = asprintf(&path, WATCHDOG_CGROUP_PATH "/%s", dir->d_name);
+ if (ret < 0) {
+ _W("[CPU-BOOSTING-BACKEND] Failed to allocate memory");
+ continue;
+ }
+
+ if (is_in_cgroup_tasks(tid, path)) {
+ free(path);
+ closedir(dp);
+ return true;
+ }
+
+ free(path);
+ }
+
+ closedir(dp);
+
+ return false;
+}
+
+static void destroy_cpu_boosting_controller_action(
+ GSList **cpu_boosting_controller_action)
+{
+ struct syscommon_resourced_cpu_boosting_input *cpu_boosting_input;
+ GSList *next;
+ GSList *iter;
+
+ gslist_for_each_safe(*cpu_boosting_controller_action, iter, next,
+ cpu_boosting_input) {
+ assert(cpu_boosting_input);
+
+ *cpu_boosting_controller_action =
+ g_slist_remove(*cpu_boosting_controller_action, cpu_boosting_input);
+
+ if (cpu_boosting_input->client_input.pid.tid)
+ free(cpu_boosting_input->client_input.pid.tid);
+
+ if (cpu_boosting_input->gsource_id)
+ g_free(cpu_boosting_input->gsource_id);
+ g_slice_free(struct syscommon_resourced_cpu_boosting_input,
+ cpu_boosting_input);
+ }
+}
+
+static int cpu_boosting_governor_govern_request (
+ GHashTable *cpu_boosting_info_table,
+ cpu_boosting_level_e cpu_boosting_level,
+ GSList **cpu_boosting_controller_action)
+{
+ GHashTableIter cpu_boosting_info_iter;
+ resource_pid_t resource_pid;
+ guint **gsource_list = NULL;
+ pid_t **tid_list = NULL;
+ gpointer key;
+ gpointer value;
+ int hash_index = 0;
+ int hash_size;
+ int ret;
+
+ if (!cpu_boosting_controller_action) {
+ _E("Wrong cpu_boosting_controller_action");
+ return -EINVAL;
+ }
+
+ if (!cpu_boosting_info_table) {
+ _E("cpu boosting info table cannot be NULL");
+ ret = -EINVAL;
+ goto error;
+ }
+
+ hash_size = g_hash_table_size(cpu_boosting_info_table);
+ if (hash_size == 0)
+ return 0;
+
+ switch (cpu_boosting_level) {
+ case CPU_BOOSTING_LEVEL_STRONG:
+ case CPU_BOOSTING_LEVEL_MEDIUM:
+ break;
+ case CPU_BOOSTING_LEVEL_WEAK:
+ return 0;
+ case CPU_BOOSTING_LEVEL_NONE:
+ default:
+ _E("Unknown cpu boosting level");
+ ret = -EINVAL;
+ goto error;
+ }
+
+ tid_list = (pid_t **)calloc(hash_size, sizeof(pid_t *));
+ if (tid_list == NULL) {
+ _E("Failed to allocate memory");
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ gsource_list = (guint **)calloc(hash_size, sizeof(guint *));
+ if (gsource_list == NULL) {
+ _E("[CPU-BOOSTING] Failed to allocate memory");
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ for (hash_index = 0; hash_index < hash_size; hash_index++) {
+ tid_list[hash_index] = (pid_t *)calloc(1, sizeof(pid_t));
+ if (tid_list[hash_index] == NULL) {
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ gsource_list[hash_index] = g_new(guint, 1);
+ }
+
+ hash_index = 0;
+ g_hash_table_iter_init(&cpu_boosting_info_iter, cpu_boosting_info_table);
+ while (1) {
+ struct syscommon_resourced_cpu_boosting_input *cpu_boosting_input;
+ struct syscommon_resourced_cpu_boosting_info *cpu_boosting_info;
+ if (g_hash_table_iter_next(&cpu_boosting_info_iter, &key, &value) == FALSE)
+ break;
+
+ cpu_boosting_info =
+ (struct syscommon_resourced_cpu_boosting_info *)value;
+ pid_t tid = g_int_hash(key);
+
+ if (is_watchdog_process(tid))
+ continue;
+
+ if (tid <= 0) {
+ _W("Thread id should be larger than 0");
+ continue;
+ }
+
+ if (!cpu_boosting_info) {
+ _W("cpu boosting info cannot be null");
+ continue;
+ }
+
+ if (cpu_boosting_info->level != cpu_boosting_level) {
+ _W("cpu boosting info (level = %d) different from level = %d",
+ cpu_boosting_info->level, cpu_boosting_level);
+ continue;
+ }
+
+ cpu_boosting_input =
+ g_slice_new0(struct syscommon_resourced_cpu_boosting_input);
+
+ resource_pid.pid = 0;
+ resource_pid.tid_count = 1;
+ resource_pid.tid = tid_list[hash_index];
+ resource_pid.tid[0] = tid;
+
+ cpu_boosting_input->gsource_id = gsource_list[hash_index];
+ cpu_boosting_input->gsource_id[0] =
+ cpu_boosting_info->gsource_id;
+
+ hash_index++;
+
+ CPU_BOOSTING_MAKE_REQUEST(cpu_boosting_input, CPU_BOOSTING_COMMAND_SET,
+ cpu_boosting_level + 1, -1, resource_pid,
+ cpu_boosting_info->cpu_boosting_flags, true);
+
+ *cpu_boosting_controller_action = g_slist_prepend(
+ *cpu_boosting_controller_action, cpu_boosting_input);
+ }
+
+ for (int index = hash_index; index < hash_size; index++) {
+ free(tid_list[index]);
+ g_free(gsource_list[index]);
+ }
+
+ /**
+ * tid_list[index] and gsource_list[index] will be freed
+ * in cpu boosting controller.
+ */
+ free(tid_list);
+ free(gsource_list);
+
+ return 0;
+
+error:
+ for (int index = 0; index < hash_index; index++) {
+ free(tid_list[index]);
+ g_free(gsource_list[index]);
+ }
+
+ free(tid_list);
+ free(gsource_list);
+
+ destroy_cpu_boosting_controller_action(cpu_boosting_controller_action);
+
+ _E("[CPU-BOOSTING-BACKEND] Failed to govern");
+
+ return ret;
+}
+
+static int resourced_cpu_boosting_init(void **data)
+{
+ *data = (void *)&g_cpu_boosting_funcs;
+
+ return 0;
+}
+
+static int resourced_cpu_boosting_exit(void *data)
+{
+ return 0;
+}
+
+static syscommon_plugin_backend_resourced_cpu_boosting_funcs g_cpu_boosting_funcs = {
+ .cpu_boosting_governor_govern_request = cpu_boosting_governor_govern_request,
+};
+
+syscommon_plugin_backend EXPORT system_plugin_backend_resourced_cpu_boosting_data = {
+ .name = "resourced-cpu-boosting",
+ .vendor = "Samsung",
+ .abi_version = SYSCOMMON_PLUGIN_ABI_VERSION_TIZEN_8_0,
+ .init = resourced_cpu_boosting_init,
+ .exit = resourced_cpu_boosting_exit,
+};