resource: system: Add MEMORY_PRESSURE_LEVEL attribute 16/287916/1
authorDongwoo Lee <dwoo08.lee@samsung.com>
Thu, 12 Jan 2023 12:39:12 +0000 (21:39 +0900)
committerDongwoo Lee <dwoo08.lee@samsung.com>
Tue, 7 Feb 2023 17:14:04 +0000 (09:14 -0800)
This adds the new attribute for representing pressure level of memory
as follows:
- Name: SYSTEM_ATTR_MEMORY_PRESSURE_LEVEL
- Type: SYSCOMMON_RESMAN_DATA_TYPE_INT
- Description:
    this attribute represents the current stall level for memory,
    which means how long delays memory allocation due to memory
    leakage. The level ranges between 0 and 4 (higher is worse).

Change-Id: Ibdce4a983384743e2316967744ac843a6ebb15e2
Signed-off-by: Dongwoo Lee <dwoo08.lee@samsung.com>
lib/resource-monitor/resource-monitor.h
src/resource/resource-system.c
tools/resource-monitor/resource-monitor.c

index 6e0e249..e65bb9f 100644 (file)
@@ -118,6 +118,7 @@ extern "C" {
 #define SYSTEM_ATTR_PER_CPU_SYS_UTIL           BIT(5)  /* SYSCOMMON_RESMAN_DATA_TYPE_ARRAY(DOUBLE) */
 #define SYSTEM_ATTR_POSSIBLE_CPU               BIT(6)  /* SYSCOMMON_RESMAN_DATA_TYPE_INT */
 #define SYSTEM_ATTR_ONLINE_CPU                 BIT(7)  /* SYSCOMMON_RESMAN_DATA_TYPE_INT */
+#define SYSTEM_ATTR_MEMORY_PRESSURE_LEVEL      BIT(8)  /* SYSCOMMON_RESMAN_DATA_TYPE_INT */
 
 /* Process Resource */
 #define PROCESS_ATTR_CPU_UTIL                  BIT(0)  /* SYSCOMMON_RESMAN_DATA_TYPE_DOUBLE */
index 4f90c71..7940663 100644 (file)
  */
 
 #include <glib.h>
+#include <unistd.h>
+#include <fcntl.h>
 
 #include <hal/hal-power.h>
 
 #include <util/common.h>
 #include <util/log.h>
 #include <util/kernel.h>
+#include <util/thread.h>
 
 #include <libsyscommon/resource-manager.h>
 #include <libsyscommon/resource-type.h>
+#include <libsyscommon/resource-listener.h>
 
 #include <resource-monitor/resource-monitor.h>
 
@@ -40,6 +44,8 @@ struct system_resource_data {
        int num_online_cpus;
        struct cpu_stat *prev_cpus;
        struct cpu_stat *curr_cpus;
+       int memory_pressure_level;
+       u_int64_t memory_pressure_last_update;
 };
 
 static double __calculate_cpu_util(int64_t id, struct cpu_stat *prev,
@@ -183,6 +189,201 @@ static int system_get_cpu_num(int resource_id,
        return 0;
 }
 
+#define PSI_MEMORY_GLOBAL               "/proc/pressure/memory"
+#define PSI_TYPE_WARN                   "some"
+#define PSI_TYPE_CRITICAL               "full"
+#define PSI_EVENT_DEBOUNCE_TIME                2000 /* msec */
+#define PSI_EVENT_RESOLVE_CHECK_RATE   3000 /* msec */
+
+enum {
+       MEM_LEVEL_HIGH,
+       MEM_LEVEL_MEDIUM,
+       MEM_LEVEL_LOW,
+       MEM_LEVEL_CRITICAL,
+       MEM_LEVEL_OOM,
+       MEM_LEVEL_MAX,
+};
+
+struct psi_level_desc {
+       char *type;
+       int stall;
+       int window;
+       u_int32_t level;
+};
+
+static struct thread *psi_timer;
+static int listener_handles[MEM_LEVEL_MAX];
+static int psi_fds[MEM_LEVEL_MAX];
+static const struct psi_level_desc psi_levels[MEM_LEVEL_MAX] = {
+       [MEM_LEVEL_MEDIUM] = {
+               .type = PSI_TYPE_WARN,
+               .stall = 70000,
+               .window = 1000000,
+               .level = MEM_LEVEL_MEDIUM,
+       },
+       [MEM_LEVEL_LOW] = {
+               .type = PSI_TYPE_WARN,
+               .stall = 150000,
+               .window = 1000000,
+               .level = MEM_LEVEL_LOW,
+       },
+       [MEM_LEVEL_CRITICAL] = {
+               .type = PSI_TYPE_CRITICAL,
+               .stall = 70000,
+               .window = 1000000,
+               .level = MEM_LEVEL_CRITICAL,
+       },
+       [MEM_LEVEL_OOM] = {
+               .type = PSI_TYPE_CRITICAL,
+               .stall = 150000,
+               .window = 1000000,
+               .level = MEM_LEVEL_OOM,
+       }
+};
+
+static int system_get_memory_pressure_level(int resource_id,
+                                           const struct syscommon_resman_resource_attribute *attr,
+                                           void *data)
+{
+       struct system_resource_data *sysdata;
+       int *pressure_level = (int *)data;
+
+       if (resource_id < 0 || !attr || !data)
+               return -EINVAL;
+
+       sysdata = syscommon_resman_get_resource_privdata(resource_id);
+       if (!sysdata)
+               return -EINVAL;
+
+       *pressure_level = sysdata->memory_pressure_level;
+
+       return 0;
+}
+
+static void system_handle_psi_listener(int resource_id,
+                                      const struct syscommon_resman_resource_attribute *attr,
+                                      void *data, int listener_type)
+{
+       struct system_resource_data *sysdata;
+       struct timeval current;
+       u_int64_t current_msec;
+       int memory_pressure_level;
+
+       if (resource_id < 0 || !attr || !data)
+               return;
+
+       sysdata = syscommon_resman_get_resource_privdata(resource_id);
+       if (!sysdata)
+               return;
+
+       memory_pressure_level = *(int *)data;
+
+       /* If event occurs in debounce time, lower level event is ignored. */
+       gettimeofday(&current, NULL);
+       current_msec = current.tv_sec * 1000 + current.tv_usec / 1000;
+       if (current_msec - sysdata->memory_pressure_last_update < PSI_EVENT_DEBOUNCE_TIME
+           && memory_pressure_level <= sysdata->memory_pressure_level)
+               return;
+
+       sysdata->memory_pressure_level = memory_pressure_level;
+       sysdata->memory_pressure_last_update = current_msec;
+}
+
+static int
+register_memory_pressure_listener(const struct psi_level_desc *desc, int resource_id,
+                                 const struct syscommon_resman_resource_attribute *attr)
+{
+       char desc_str[BUFF_MAX];
+       int ret, fd;
+
+       fd = open(PSI_MEMORY_GLOBAL, O_RDWR | O_NONBLOCK);
+       if (fd < 0) {
+               _E("failed to open psi node: %s %d", PSI_MEMORY_GLOBAL, errno);
+               return -errno;
+       }
+
+       ret = snprintf(desc_str, BUFF_MAX, "%s %d %d", desc->type, desc->stall, desc->window);
+       if (ret >= BUFF_MAX) {
+               _E("psi event description overflows");
+               ret = -EINVAL;
+               goto err_close_fd;
+       }
+
+       ret = write(fd, desc_str, strlen(desc_str) + 1);
+       if (ret < 0) {
+               _E("failed to write psi description: %s", desc_str);
+               ret = -errno;
+               goto err_close_fd;
+       }
+
+       ret = syscommon_resman_register_epoll_listener(resource_id, attr, fd, (void *)&desc->level);
+       if (ret < 0) {
+               _E("failed to register epoll event");
+               goto err_close_fd;
+       }
+
+       listener_handles[desc->level] = ret;
+       psi_fds[desc->level] = fd;
+
+       return 0;
+
+err_close_fd:
+       close(fd);
+
+       return ret;
+}
+
+static int system_check_memory_pressure_resolved(void *data, void **result)
+{
+       struct syscommon_resman_resource_attribute dummy_attr;
+       int mem_level_high = MEM_LEVEL_HIGH;
+       int resource_id = (int)data;
+
+       system_handle_psi_listener(resource_id, &dummy_attr,
+                       &mem_level_high, SYSCOMMON_RESMAN_LISTENER_TYPE_EPOLL);
+
+       return THREAD_RETURN_CONTINUE;
+}
+
+static int system_init_psi_listener(int resource_id,
+                                   const struct syscommon_resman_resource_attribute *attr)
+{
+       int i, ret;
+
+       ret = create_timer_thread(&psi_timer, PSI_EVENT_RESOLVE_CHECK_RATE,
+                       system_check_memory_pressure_resolved, (void *)resource_id);
+       if (ret < 0)
+               return ret;
+
+       for (i = MEM_LEVEL_MEDIUM; i < MEM_LEVEL_MAX; i++) {
+               ret = register_memory_pressure_listener(&psi_levels[i], resource_id, attr);
+               if (ret < 0)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static void system_exit_psi_listener(int resource_id,
+                                    const struct syscommon_resman_resource_attribute *attr)
+{
+       int i;
+
+       for (i = MEM_LEVEL_MEDIUM; i < MEM_LEVEL_MAX; i++) {
+               if (listener_handles[i]) {
+                       syscommon_resman_unregister_epoll_listener(listener_handles[i]);
+                       listener_handles[i] = 0;
+               }
+
+               if (psi_fds[i]) {
+                       close(psi_fds[i]);
+                       psi_fds[i] = 0;
+               }
+       }
+
+       destroy_thread(psi_timer);
+}
+
 static const struct syscommon_resman_resource_attribute system_attrs[] = {
        {
                .name   = "SYSTEM_ATTR_CPU_UTIL",
@@ -248,6 +449,19 @@ static const struct syscommon_resman_resource_attribute system_attrs[] = {
                .ops    = {
                        .get = system_get_cpu_num,
                }
+       }, {
+               .name   = "SYSTEM_ATTR_MEMORY_PRESSURE_LEVEL",
+               .id     = SYSTEM_ATTR_MEMORY_PRESSURE_LEVEL,
+               .type   = SYSCOMMON_RESMAN_DATA_TYPE_INT,
+               .flag   = SYSCOMMON_RESMAN_RESOURCE_ATTR_FLAG_PUBLIC,
+               .ops    = {
+                       .get = system_get_memory_pressure_level,
+               },
+               .listener_ops = {
+                       .init = system_init_psi_listener,
+                       .exit = system_exit_psi_listener,
+                       .action = system_handle_psi_listener,
+               }
        },
 };
 
index 815fd4d..3b0d6c8 100644 (file)
@@ -118,6 +118,8 @@ static struct syscommon_resman_resource_attr_data system_attrs[] = {
        { .id = SYSTEM_ATTR_PER_CPU_SYS_UTIL,           .type = SYSCOMMON_RESMAN_DATA_TYPE_ARRAY,       .name = "SYSTEM_ATTR_PER_CPU_SYS_UTIL",                 .unit = "%",            .desc = "Per-CPU utilization on system",        .array_type = SYSCOMMON_RESMAN_DATA_TYPE_DOUBLE, },
        { .id = SYSTEM_ATTR_POSSIBLE_CPU,               .type = SYSCOMMON_RESMAN_DATA_TYPE_INT,         .name = "SYSTEM_ATTR_POSSIBLE_CPU",                     .unit = "ea",           .desc = "Number of possible CPU", },
        { .id = SYSTEM_ATTR_ONLINE_CPU,                 .type = SYSCOMMON_RESMAN_DATA_TYPE_INT,         .name = "SYSTEM_ATTR_ONLINE_CPU",                       .unit = "ea",           .desc = "Number of online CPU", },
+       { .id = SYSTEM_ATTR_MEMORY_PRESSURE_LEVEL,      .type = SYSCOMMON_RESMAN_DATA_TYPE_INT,         .name = "MEMORY_ATTR_PRESSURE_LEVEL",                   .unit = "lv",           .desc = "Level of memory pressure", },
+
 };
 
 struct syscommon_resman_resource_attr_data process_attrs[] = {