*/
#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>
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,
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(¤t, 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",
.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,
+ }
},
};