resourced-cpu-boosting: Add cpu boosting governor 32/297132/11
authorUnsung Lee <unsung.lee@samsung.com>
Thu, 10 Aug 2023 09:07:21 +0000 (18:07 +0900)
committerUnsung Lee <unsung.lee@samsung.com>
Wed, 30 Aug 2023 04:51:25 +0000 (13:51 +0900)
Add cpu boosting governor in resourced plugin backend to govern
boosted thread. This function is called when CPU PSIs are triggered in resourced.
That is, cpu contention is detected in the resourced.
cpu boosting governor figures out the reason of cpu contention, and
make a list actions to handle cpu contention in cpu boosting controller.

The rule of cpu boosting governor is like below:
  - Decrease cpu boosting level of all threads having CPU_BOOSTING_LEVEL_STRONG.
    (CPU_BOOSTING_LEVEL_STRONG -> CPU_BOOSTING_LEVEL_MEDIUM)
  - Decrease cpu boosting level of all threads having CPU_BOOSTING_LEVEL_MEDIUM
    when there is no thread having CPU_BOOSTING_LEVEL_STRONG.
    (CPU_BOOSTING_LEVEL_MEDIUM -> CPU_BOOSTING_LEVEL_WEAK)

A new function called cpu boosting governor is added:
  - int cpu_boosting_governor_govern_request
(const GHashTable *cpu_boosting_info_table,
 cpu_boosting_level_e cpu_boosting_level,
 GSList **cpu_boosting_controller_action);
    * This function receives cpu_boosting_info_table and cpu_boosting_level
      as inputs and gives cpu_boosting_controller_action as an output.
      If return value of this function is not 0,
      then cpu_boosting_controller_action is meaningless.

Change-Id: Ie86c123b6b0f78a2f64857877677b822eda45130
Signed-off-by: Unsung Lee <unsung.lee@samsung.com>
CMakeLists.txt
packaging/system-plugin-resourced-generic.spec
src/resourced-cpu-boosting/CMakeLists.txt [new file with mode: 0644]
src/resourced-cpu-boosting/resourced-cpu-boosting.c [new file with mode: 0644]

index a42663ee25dd2a169098fc9802bfc8a534d339bc..f53523449b647d37387a6140b163803a2cc7e714 100644 (file)
@@ -6,3 +6,4 @@ SET(PREFIX ${CMAKE_INSTALL_PREFIX})
 SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -Wall -Werror")
 
 ADD_SUBDIRECTORY(src/resourced-memory-lmk)
+ADD_SUBDIRECTORY(src/resourced-cpu-boosting)
index 9c3806b151be21d61f500624135c1f9a26d63e9e..0c12cfce1a44876e526c288468dd0a7951794680 100644 (file)
@@ -16,6 +16,7 @@ BuildRequires:  cmake
 BuildRequires:  pkgconfig(glib-2.0)
 BuildRequires:  pkgconfig(libsyscommon)
 BuildRequires:  pkgconfig(libsyscommon-plugin-api-resourced)
+BuildRequires:  pkgconfig(capi-system-resource)
 
 %description
 System plugin backend for resourced and generic profile
@@ -27,7 +28,8 @@ cp %{SOURCE1} .
 %build
 %cmake . -DPLUGIN_BACKEND_NAME=%{name} \
        -DPLUGIN_LIB_DIR=%{SYSTEM_PLUGIN_LIBDIR} \
-       -DPLUGIN_RESOURCED_MEMORY_LMK_ENABLE_DLOG=1
+       -DPLUGIN_RESOURCED_MEMORY_LMK_ENABLE_DLOG=1 \
+       -DPLUGIN_RESOURCED_CPU_BOOSTING_ENABLE_DLOG=1
 
 make %{?jobs:-j%jobs}
 
@@ -45,3 +47,4 @@ make %{?jobs:-j%jobs}
 %manifest %{name}.manifest
 %license LICENSE.Apache-2.0
 %{SYSTEM_PLUGIN_LIBDIR}/libplugin-backend-resourced-memory-lmk.so
+%{SYSTEM_PLUGIN_LIBDIR}/libplugin-backend-resourced-cpu-boosting.so
diff --git a/src/resourced-cpu-boosting/CMakeLists.txt b/src/resourced-cpu-boosting/CMakeLists.txt
new file mode 100644 (file)
index 0000000..7f1942b
--- /dev/null
@@ -0,0 +1,33 @@
+CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
+PROJECT(plugin-backend-resourced-cpu-boosting C)
+
+SET(PREFIX ${CMAKE_INSTALL_PREFIX})
+
+if (${PLUGIN_RESOURCED_CPU_BOOSTING_ENABLE_DLOG})
+       SET(PKG_MODULES
+               libsyscommon
+               libsyscommon-plugin-api-resourced
+               glib-2.0
+               capi-system-resource)
+       ADD_DEFINITIONS("-DENABLE_DLOG")
+       ADD_DEFINITIONS("-DLOG_TAG=\"SYSTEM_PLUGIN_RESOURCED_CPU_BOOSTING\"")
+else()
+       SET(PKG_MODULES
+               libsyscommon
+               libsyscommon-plugin-api-resourced
+               capi-system-resource)
+endif()
+
+INCLUDE(FindPkgConfig)
+pkg_check_modules(pkgs REQUIRED ${PKG_MODULES})
+FOREACH(flag ${pkgs_CFLAGS})
+       SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}")
+ENDFOREACH(flag)
+
+SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -fvisibility=hidden")
+SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS}")
+
+ADD_LIBRARY(${PROJECT_NAME} SHARED resourced-cpu-boosting.c)
+MESSAGE("pkgs_LDFLAGS: ${pkgs_LDFLAGS}")
+TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${pkgs_LDFLAGS})
+INSTALL(TARGETS ${PROJECT_NAME} DESTINATION ${PLUGIN_LIB_DIR} COMPONENT RuntimeLibraries)
diff --git a/src/resourced-cpu-boosting/resourced-cpu-boosting.c b/src/resourced-cpu-boosting/resourced-cpu-boosting.c
new file mode 100644 (file)
index 0000000..9657fe7
--- /dev/null
@@ -0,0 +1,329 @@
+/**
+ * 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,
+};