diff --git a/Makefile b/Makefile index ce133e6..3007778 100644 --- a/Makefile +++ b/Makefile @@ -643,6 +643,7 @@ NORMALIZE_USES_SPC =\ modules/memnotify.c\ modules/memnotify.h\ modules/mempressure.c\ + modules/mempressure.h\ modules/packagekit.c\ modules/powersavemode.h\ modules/proximity.c\ diff --git a/builtin-gconf.c b/builtin-gconf.c index a5df1d0..d19cb5f 100644 --- a/builtin-gconf.c +++ b/builtin-gconf.c @@ -43,6 +43,7 @@ #include "event-input.h" #include "modules/memnotify.h" +#include "modules/mempressure.h" #include "modules/display.h" #include "modules/proximity.h" #include "modules/powersavemode.h" @@ -1873,6 +1874,31 @@ static const setting_t gconf_defaults[] = .type = "i", .def = G_STRINGIFY(MCE_DEFAULT_MEMNOTIFY_CRITICAL_ACTIVE) }, + { + .key = MCE_SETTING_MEMPRESSURE_WINDOW, + .type = "i", + .def = G_STRINGIFY(MCE_DEFAULT_MEMPRESSURE_WINDOW) + }, + { + .key = MCE_SETTING_MEMPRESSURE_WARNING_STALL, + .type = "i", + .def = G_STRINGIFY(MCE_DEFAULT_MEMPRESSURE_WARNING_STALL) + }, + { + .key = MCE_SETTING_MEMPRESSURE_WARNING_TYPE, + .type = "s", + .def = MCE_DEFAULT_MEMPRESSURE_WARNING_TYPE + }, + { + .key = MCE_SETTING_MEMPRESSURE_CRITICAL_STALL, + .type = "i", + .def = G_STRINGIFY(MCE_DEFAULT_MEMPRESSURE_CRITICAL_STALL) + }, + { + .key = MCE_SETTING_MEMPRESSURE_CRITICAL_TYPE, + .type = "s", + .def = MCE_DEFAULT_MEMPRESSURE_CRITICAL_TYPE + }, { .key = MCE_SETTING_TK_EXCEPT_LEN_CALL_IN, .type = "i", diff --git a/modules/mempressure.c b/modules/mempressure.c index 5488b8b..9c5b395 100644 --- a/modules/mempressure.c +++ b/modules/mempressure.c @@ -19,7 +19,7 @@ * License along with mce. If not, see . */ -#include "memnotify.h" +#include "mempressure.h" #include "../mce.h" #include "../mce-log.h" @@ -31,34 +31,12 @@ #include #include #include +#include #include +#include -/* Paths to relevant cgroup data/control files */ -#define CGROUP_MEMORY_DIRECTORY "/sys/fs/cgroup/memory" -#define CGROUP_DATA_PATH CGROUP_MEMORY_DIRECTORY "/memory.usage_in_bytes" -#define CGROUP_CTRL_PATH CGROUP_MEMORY_DIRECTORY "/cgroup.event_control" - -/* RAM page size in bytes - * - * Configuration is defined in terms of (memnotify style) page counts. - * - * Conversion to/from byte counts used in cgroups interface are done via: - * - mempressure_bytes_to_pages() - * - mempressure_pages_to_bytes() - */ -#define PAGE_SIZE ((unsigned long)sysconf(_SC_PAGESIZE)) - -/* ========================================================================= * - * Types - * ========================================================================= */ - -/** Structure for holding for /dev/mempressure compatible limit data */ -typedef struct -{ - /** Estimate of number of non-discardable RAM pages */ - gint mnl_used; -} mempressure_limit_t; +#define PSI_MEMORY_PATH "/proc/pressure/memory" /* ========================================================================= * * Prototypes @@ -68,38 +46,18 @@ typedef struct * UTILITY * ------------------------------------------------------------------------- */ -static int mempressure_bytes_to_pages(uint64_t bytes); -static uint64_t mempressure_pages_to_bytes(int pages); static guint mempressure_iowatch_add (int fd, bool close_on_unref, GIOCondition cnd, GIOFunc io_cb, gpointer aptr); +static bool mempressure_streq (const char *s1, const char *s2); /* ------------------------------------------------------------------------- * - * MEMPRESSURE_LIMIT - * ------------------------------------------------------------------------- */ - -static void mempressure_limit_clear (mempressure_limit_t *self); -static bool mempressure_limit_is_valid(const mempressure_limit_t *self); -static int mempressure_limit_repr (const mempressure_limit_t *self, char *data, size_t size); -static bool mempressure_limit_parse (mempressure_limit_t *self, const char *data); -static bool mempressure_limit_exceeded(const mempressure_limit_t *self, const mempressure_limit_t *status); - -/* ------------------------------------------------------------------------- * - * MEMPRESSURE_STATUS + * MEMPRESSURE_PSI * ------------------------------------------------------------------------- */ -static memnotify_level_t mempressure_status_evaluate_level(void); -static bool mempressure_status_update_level (void); -static void mempressure_status_show_triggers (void); - -/* ------------------------------------------------------------------------- * - * MEMPRESSURE_CGROUP - * ------------------------------------------------------------------------- */ +static bool mempressure_psi_is_available (void); +static gboolean mempressure_psi_event_cb (GIOChannel *chn, GIOCondition cnd, gpointer aptr); +static void mempressure_psi_quit (void); +static bool mempressure_psi_init (void); -static bool mempressure_cgroup_is_available (void); -static gboolean mempressure_cgroup_event_cb (GIOChannel *chn, GIOCondition cnd, gpointer aptr); -static void mempressure_cgroup_quit (void); -static bool mempressure_cgroup_init (void); -static void mempressure_cgroup_update_thresholds(void); -static bool mempressure_cgroup_update_status (void); /* ------------------------------------------------------------------------- * * MEMPRESSURE_SETTING @@ -127,22 +85,6 @@ void g_module_unload (GModule *module); * UTILITY * ========================================================================= */ -/* Convert kernel reported byte count to page count used in configuration - */ -static int mempressure_bytes_to_pages(uint64_t bytes) -{ - return (int)(bytes / PAGE_SIZE); -} - -/* Convert configuration page count to bytes for use in kernel interface - */ -static uint64_t mempressure_pages_to_bytes(int pages) -{ - if( pages < 0 ) - pages = 0; - return PAGE_SIZE * (uint64_t)pages; -} - /** Add a glib I/O notification for a file descriptor */ static guint @@ -170,350 +112,258 @@ mempressure_iowatch_add(int fd, bool close_on_unref, return wid; } -/* ========================================================================= * - * MEMPRESSURE_LIMIT - * ========================================================================= */ - -/** Reset limit object values - */ -static void -mempressure_limit_clear(mempressure_limit_t *self) -{ - self->mnl_used = 0; -} - -/** Limit validity predicate - */ -static bool -mempressure_limit_is_valid(const mempressure_limit_t *self) -{ - return self->mnl_used > 0; -} - -/** Convert limit object values to /dev/mempressure compatible ascii form - */ -static int -mempressure_limit_repr(const mempressure_limit_t *self, char *data, size_t size) -{ - int res = snprintf(data, size, "used %d", self->mnl_used); - return res; -} - -/** Parse limit object from /sys/fs/cgroup/memory/memory.usage_in_bytes format - */ -static bool -mempressure_limit_parse(mempressure_limit_t *self, const char *data) -{ - char *end = 0; - uint64_t val = strtoull(data, &end, 10); - bool res = end > data && *end == 0; - - if( !res ) - mce_log(LL_ERR, "parse error: '%s' is not a number", data); - else - self->mnl_used = mempressure_bytes_to_pages(val); - - return res; -} - -/** Check if limit object values are exceeded by given state data +/* Null tolerant string equality predicate + * + * @param s1 string + * @param s2 string + * + * @return true if both s1 and s2 are null or same string, false otherwise */ -static bool -mempressure_limit_exceeded(const mempressure_limit_t *self, - const mempressure_limit_t *status) +static bool mempressure_streq(const char *s1, const char *s2) { - return (mempressure_limit_is_valid(self) && - self->mnl_used <= status->mnl_used); + return (s1 && s2) ? !strcmp(s1, s2) : (s1 == s2); } /* ========================================================================= * * MEMPRESSURE_STATUS * ========================================================================= */ -/** Configuration limits for normal/warning/critical levels */ -static mempressure_limit_t mempressure_limit[] = -{ - [MEMNOTIFY_LEVEL_NORMAL] = { - .mnl_used = 0, - }, - [MEMNOTIFY_LEVEL_WARNING] = { - // values come from config - disabled by default - .mnl_used = 0, - }, - [MEMNOTIFY_LEVEL_CRITICAL] = { - // values come from config - disabled by default - .mnl_used = 0, - }, -}; - -/** Cached status read from kernel device */ -static mempressure_limit_t mempressure_status = -{ - .mnl_used = 0, -}; +/** Configuration */ +static gint mempressure_window = MCE_DEFAULT_MEMPRESSURE_WINDOW; +static gint mempressure_warning_stall = MCE_DEFAULT_MEMPRESSURE_WARNING_STALL; +static char* mempressure_warning_type = NULL; +static gint mempressure_critical_stall = MCE_DEFAULT_MEMPRESSURE_CRITICAL_STALL; +static char* mempressure_critical_type = NULL; /** Cached memory use level */ static memnotify_level_t mempressure_level = MEMNOTIFY_LEVEL_UNKNOWN; -/** Check current memory status against triggering levels - */ -static memnotify_level_t -mempressure_status_evaluate_level(void) -{ - memnotify_level_t res = MEMNOTIFY_LEVEL_UNKNOWN; - - if( mempressure_limit_is_valid(&mempressure_status) ) { - res = MEMNOTIFY_LEVEL_NORMAL; - - for( memnotify_level_t lev = MEMNOTIFY_LEVEL_NORMAL + 1; - lev < G_N_ELEMENTS(mempressure_limit); ++lev ) { - if( mempressure_limit_exceeded(mempressure_limit+lev, &mempressure_status) ) - res = lev; - } - } - - return res; -} +/* ========================================================================= * + * MEMPRESSURE_PSI + * ========================================================================= */ -/** Re-evaluate memory use level and broadcast changes via datapipe +/** Probe if the required PSI sysfs files are present */ static bool -mempressure_status_update_level(void) +mempressure_psi_is_available(void) { - memnotify_level_t prev = mempressure_level; - mempressure_level = mempressure_status_evaluate_level(); - - if( mempressure_level == prev ) - goto EXIT; + return access(PSI_MEMORY_PATH, R_OK) == 0; +} - mce_log(LL_WARN, "mempressure_level: %s -> %s", - memnotify_level_repr(prev), - memnotify_level_repr(mempressure_level)); +static int mempressure_psi_warning_fd = -1; +static int mempressure_psi_critical_fd = -1; +static guint mempressure_psi_warn_event_id = 0; +static guint mempressure_psi_crit_event_id = 0; +static guint mempressure_psi_warn_timeout = 0; +static guint mempressure_psi_crit_timeout = 0; - datapipe_exec_full(&memnotify_level_pipe, - GINT_TO_POINTER(mempressure_level)); +static void +mempressure_psi_update_level(void) +{ + memnotify_level_t prev = mempressure_level; -EXIT: + if ( mempressure_psi_crit_timeout != 0 ) { + mempressure_level = MEMNOTIFY_LEVEL_CRITICAL; + } else if ( mempressure_psi_warn_timeout != 0 ) { + mempressure_level = MEMNOTIFY_LEVEL_WARNING; + } else { + mempressure_level = MEMNOTIFY_LEVEL_NORMAL; + } + if ( prev != mempressure_level ) { + mce_log(LL_INFO, "mempressure_level: %s -> %s", + memnotify_level_repr(prev), + memnotify_level_repr(mempressure_level)); - return mempressure_level != MEMNOTIFY_LEVEL_UNKNOWN; + datapipe_exec_full(&memnotify_level_pipe, + GINT_TO_POINTER(mempressure_level)); + } } -/** Log current memory level configuration for debugging purposes - */ -static void -mempressure_status_show_triggers(void) +static gboolean +mempressure_psi_timeout_cb(gpointer user_data) { - if( mce_log_p(LL_DEBUG) ) { - for( size_t i = 0; i < G_N_ELEMENTS(mempressure_limit); ++i ) { - char tmp[256]; - mempressure_limit_repr(mempressure_limit+i, tmp, sizeof tmp); - mce_log(LL_DEBUG, "%s: %s", memnotify_level_repr(i), tmp); - } + if( user_data == NULL ) { + mce_log(LL_ERR, "null timeout argument"); + goto EXIT; } -} - -/* ========================================================================= * - * MEMPRESSURE_CGROUP - * ========================================================================= */ - -/** File descriptor for CGROUP_DATA_PATH */ -static int mempressure_cgroup_data_fd = -1; - -/** File descriptor for CGROUP_CTRL_PATH */ -static int mempressure_cgroup_ctrl_fd = -1; -/** Eventfd for receiving notifications about threshold crossings */ -static int mempressure_cgroup_event_fd = -1; + int fd = *((int*)user_data); + if ( fd == mempressure_psi_warning_fd ) { + mce_log(LL_DEBUG, "PSI warning event timeout"); + mempressure_psi_warn_timeout = 0; + } else if ( fd == mempressure_psi_critical_fd ) { + mce_log(LL_DEBUG, "PSI critical event timeout"); + mempressure_psi_crit_timeout = 0; + } else { + mce_log(LL_CRIT, "unknown fd in timeout callback"); + goto EXIT; + } -/** I/O watch for mempressure_cgroup_event_fd */ -static guint mempressure_cgroup_event_id = 0; + mempressure_psi_update_level(); -/** Probe if the required cgroup sysfs files are present - */ -static bool -mempressure_cgroup_is_available(void) -{ - return (access(CGROUP_DATA_PATH, R_OK) == 0 && - access(CGROUP_CTRL_PATH, W_OK) == 0); +EXIT: + return G_SOURCE_REMOVE; } -/** Input watch callback for cgroup memory threshold crossings +/** Input watch callback for PSI events */ static gboolean -mempressure_cgroup_event_cb(GIOChannel *chn, GIOCondition cnd, gpointer aptr) +mempressure_psi_event_cb(GIOChannel *chn, GIOCondition cnd, gpointer aptr) { - (void) chn; - (void) aptr; + (void)chn; + (void)aptr; gboolean ret = G_SOURCE_REMOVE; - if( !mempressure_cgroup_event_id ) - goto EXIT; - - if( mempressure_cgroup_event_fd == -1 ) - goto EXIT; - - if( mempressure_cgroup_data_fd == -1 ) - goto EXIT; - - mce_log(LL_DEBUG, "eventfd iowatch notify"); - - if( cnd & ~G_IO_IN ) { + if( cnd & ~G_IO_PRI ) { mce_log(LL_ERR, "unexpected input watch condition"); goto EXIT; } - - uint64_t count = 0; - ssize_t rc = read(mempressure_cgroup_event_fd, &count, sizeof count); - - if( rc == 0 ) { - mce_log(LL_ERR, "eventfd eof"); + if( aptr == NULL ) { + mce_log(LL_ERR, "null watch argument"); goto EXIT; } - if( rc == -1 ) { - if( errno == EINTR || errno == EAGAIN ) - ret = G_SOURCE_CONTINUE; - else - mce_log(LL_ERR, "eventfd error: %m"); + int fd = *((int*)aptr); + + if (fd != mempressure_psi_warning_fd && fd != mempressure_psi_critical_fd) { + mce_log(LL_CRIT, "unknown fd in iowatch callback"); goto EXIT; } - if( mempressure_cgroup_update_status() ) - ret = G_SOURCE_CONTINUE; - - /* Update level anyway -> if we disable iowatch due to - * read/parse errors, the level gets reset to 'unknown'. - */ - mempressure_status_update_level(); + if (fd == mempressure_psi_warning_fd) { + mce_log(LL_DEBUG, "warning PSI event"); + if (mempressure_psi_warn_timeout != 0) { + g_source_remove(mempressure_psi_warn_timeout); + } + mempressure_psi_warn_timeout = g_timeout_add((mempressure_window / 1000) * 2, + mempressure_psi_timeout_cb, aptr); + } else { + assert(fd == mempressure_psi_critical_fd); + mce_log(LL_DEBUG, "critical PSI event"); + if (mempressure_psi_crit_timeout != 0) { + g_source_remove(mempressure_psi_crit_timeout); + } + mempressure_psi_crit_timeout = g_timeout_add((mempressure_window / 1000) * 2, + mempressure_psi_timeout_cb, aptr); + } + mempressure_psi_update_level(); + ret = G_SOURCE_CONTINUE; EXIT: - if( ret == G_SOURCE_REMOVE && mempressure_cgroup_event_id ) { - mempressure_cgroup_event_id = 0; + if( ret == G_SOURCE_REMOVE && + (mempressure_psi_warn_event_id || mempressure_psi_crit_event_id) ){ + mempressure_psi_warn_event_id = 0; + mempressure_psi_crit_event_id = 0; mce_log(LL_CRIT, "disabling eventfd iowatch"); } return ret; } -/** Stop cgroup memory tracking - */ static void -mempressure_cgroup_quit(void) +mempressure_psi_quit(void) { - if( mempressure_cgroup_event_id ) { - mce_log(LL_DEBUG, "remove eventfd iowatch"); - g_source_remove(mempressure_cgroup_event_id), - mempressure_cgroup_event_id = 0; + if ( mempressure_psi_warn_timeout != 0 ) { + g_source_remove(mempressure_psi_warn_timeout); } - - if( mempressure_cgroup_event_fd != -1 ) { - mce_log(LL_DEBUG, "close eventfd"); - close(mempressure_cgroup_event_fd), - mempressure_cgroup_event_fd = -1; + if ( mempressure_psi_crit_timeout != 0 ) { + g_source_remove(mempressure_psi_crit_timeout); } - - if( mempressure_cgroup_ctrl_fd != -1 ) { - mce_log(LL_DEBUG, "close %s", CGROUP_CTRL_PATH); - close(mempressure_cgroup_ctrl_fd), - mempressure_cgroup_ctrl_fd = -1; + if( mempressure_psi_warn_event_id ) { + mce_log(LL_DEBUG, "remove warnong eventfd iowatch"); + g_source_remove(mempressure_psi_warn_event_id), + mempressure_psi_warn_event_id = 0; } - - if( mempressure_cgroup_data_fd != -1 ) { - mce_log(LL_DEBUG, "close %s", CGROUP_DATA_PATH); - close(mempressure_cgroup_data_fd), - mempressure_cgroup_data_fd = -1; + if( mempressure_psi_crit_event_id ) { + mce_log(LL_DEBUG, "remove critical eventfd iowatch"); + g_source_remove(mempressure_psi_crit_event_id), + mempressure_psi_crit_event_id = 0; + } + if ( mempressure_psi_warning_fd ) { + if ( close( mempressure_psi_warning_fd ) < 0) { + mce_log(LL_ERR, "close failed"); + } + mempressure_psi_warning_fd = 0; + } + if ( mempressure_psi_critical_fd ) { + if ( close( mempressure_psi_critical_fd ) < 0) { + mce_log(LL_ERR, "close failed"); + } + mempressure_psi_critical_fd = 0; } } -/** Start cgroup memory tracking - */ static bool -mempressure_cgroup_init(void) +mempressure_psi_init(void) { bool res = false; - /* Check threshold configuration */ - for( int i = MEMNOTIFY_LEVEL_WARNING; i <= MEMNOTIFY_LEVEL_CRITICAL; ++i ) { - if( !mempressure_limit_is_valid(&mempressure_limit[i]) ) { - mce_log(LL_WARN, "mempressure '%s' threshold is not defined", - memnotify_level_repr(i)); - goto EXIT; - } - } - /* Get file descriptors */ - mce_log(LL_DEBUG, "create eventfd"); - if( (mempressure_cgroup_event_fd = eventfd(0, 0)) == -1 ) { - mce_log(LL_ERR, "create eventfd: %m"); + mce_log(LL_DEBUG, "open %s for warning threshold", PSI_MEMORY_PATH); + if( (mempressure_psi_warning_fd = open(PSI_MEMORY_PATH, O_RDWR | O_NONBLOCK)) == -1 ) { + mce_log(LL_ERR, "%s: open: %m", PSI_MEMORY_PATH); goto EXIT; } - mce_log(LL_DEBUG, "open %s", CGROUP_DATA_PATH); - if( (mempressure_cgroup_data_fd = open(CGROUP_DATA_PATH, O_RDONLY)) == -1 ) { - mce_log(LL_ERR, "%s: open: %m", CGROUP_DATA_PATH); + mce_log(LL_DEBUG, "open %s for critical threshold", PSI_MEMORY_PATH); + if( (mempressure_psi_critical_fd = open(PSI_MEMORY_PATH, O_RDWR | O_NONBLOCK)) == -1 ) { + mce_log(LL_ERR, "%s: open: %m", PSI_MEMORY_PATH); goto EXIT; } - mce_log(LL_DEBUG, "open %s", CGROUP_CTRL_PATH); - if( (mempressure_cgroup_ctrl_fd = open(CGROUP_CTRL_PATH, O_WRONLY)) == -1 ) { - mce_log(LL_ERR, "%s: open: %m", CGROUP_CTRL_PATH); + // setup thresholds + char buf[1024]; + snprintf(buf, sizeof buf, + "%s %d %d", + mempressure_warning_type, mempressure_warning_stall, mempressure_window); + + mce_log(LL_DEBUG, "warning threshold: %s", buf); + + if (write(mempressure_psi_warning_fd, buf, strlen(buf) + 1) < 0) { + mce_log(LL_ERR, "%s: write: %m", PSI_MEMORY_PATH); goto EXIT; } - /* Program notification limits */ - for( int i = MEMNOTIFY_LEVEL_WARNING; i <= MEMNOTIFY_LEVEL_CRITICAL; ++i ) { - int pages = mempressure_limit[i].mnl_used; - uint64_t bytes = mempressure_pages_to_bytes(pages); + snprintf(buf, sizeof buf, + "%s %d %d", + mempressure_critical_type, mempressure_critical_stall, mempressure_window); - mce_log(LL_DEBUG, "mempressure %s threshold %" PRIu64 "", - memnotify_level_repr(i), bytes); + mce_log(LL_DEBUG, "critical threshold: %s", buf); - char data[256]; - snprintf(data, sizeof data, "%d %d %" PRIu64 "\n", - mempressure_cgroup_event_fd, mempressure_cgroup_data_fd, bytes); - if( write(mempressure_cgroup_ctrl_fd, data, strlen(data)) == -1 ) { - mce_log(LL_ERR, "%s: write: %m", CGROUP_CTRL_PATH); - goto EXIT; - } + if (write(mempressure_psi_critical_fd, buf, strlen(buf) + 1) < 0) { + mce_log(LL_ERR, "%s: write: %m", PSI_MEMORY_PATH); + goto EXIT; } - /* Control fd is not needed after threshold setup is done */ - mce_log(LL_DEBUG, "close %s", CGROUP_CTRL_PATH); - close(mempressure_cgroup_ctrl_fd), - mempressure_cgroup_ctrl_fd = -1; - /* Setup notification iowatch */ - mce_log(LL_DEBUG, "add eventfd iowatch"); - mempressure_cgroup_event_id = - mempressure_iowatch_add(mempressure_cgroup_event_fd, false, G_IO_IN, - mempressure_cgroup_event_cb, NULL); - if( !mempressure_cgroup_event_id ) { - mce_log(LL_ERR, "failed to add eventfd iowatch"); + mce_log(LL_DEBUG, "add warning fd iowatch"); + mempressure_psi_warn_event_id = + mempressure_iowatch_add(mempressure_psi_warning_fd, false, G_IO_PRI, + mempressure_psi_event_cb, &mempressure_psi_warning_fd); + if( !mempressure_psi_warn_event_id ) { + mce_log(LL_ERR, "failed to add warning fd iowatch"); goto EXIT; } - /* Evaluate and publish current state */ - if( !mempressure_cgroup_update_status() ) + mce_log(LL_DEBUG, "add critical fd iowatch"); + mempressure_psi_crit_event_id = + mempressure_iowatch_add(mempressure_psi_critical_fd, false, G_IO_PRI, + mempressure_psi_event_cb, &mempressure_psi_critical_fd); + if( !mempressure_psi_crit_event_id ) { + mce_log(LL_ERR, "failed to add critical fd iowatch"); goto EXIT; - - if( !mempressure_status_update_level() ) - goto EXIT; - - /* Initialization was successfully completed and we have broadcast - * a valid pressure level on memnotify_level_pipe - which should - * act as a signal for furthre to be loaded alternate memory pressure - * plugins to remain inactive. - */ + } res = true; + mempressure_level = MEMNOTIFY_LEVEL_NORMAL; EXIT: // all or nothing if( !res ) - mempressure_cgroup_quit(); + mempressure_psi_quit(); return res; } @@ -521,70 +371,34 @@ mempressure_cgroup_init(void) /** Set kernel side triggering levels and update current status */ static void -mempressure_cgroup_update_thresholds(void) +mempressure_psi_update_thresholds(void) { /* TODO: Is there some way to remove trigger thresholds? * * Meanwhile do a full reinitialization to get rid of old thresholds. */ - mempressure_cgroup_quit(); - mempressure_cgroup_init(); -} - -/** Read current memory use status from kernel side - */ -static bool -mempressure_cgroup_update_status(void) -{ - bool res = false; - - if( mempressure_cgroup_data_fd == -1 ) { - mce_log(LL_ERR, "data file not opened"); - goto EXIT; - } - - if( lseek(mempressure_cgroup_data_fd, 0, SEEK_SET) == -1 ) { - mce_log(LL_ERR, "failed to rewind data file: %m"); - goto EXIT; - } - - errno = 0; - - char tmp[256]; - int done = read(mempressure_cgroup_data_fd, tmp, sizeof tmp - 1); - if( done <= 0 ) { - mce_log(LL_ERR, "failed to read data file: %m"); - goto EXIT; - } - - tmp[done] = 0; - tmp[strcspn(tmp, "\n")] = 0; - - mce_log(LL_DEBUG, "status from data file: %s", tmp); - - if( !mempressure_limit_parse(&mempressure_status, tmp) ) { - mce_log(LL_ERR, "failed to parse status"); - goto EXIT; - } - - res = true; - -EXIT: - if( !res ) - mempressure_limit_clear(&mempressure_status); - - return res; + mempressure_psi_quit(); + mempressure_psi_init(); } /* ========================================================================= * * MEMPRESSURE_SETTING * ========================================================================= */ -/** GConf notification id for mempressure.warning.used level */ -static guint mempressure_setting_warning_used_id = 0; +/** GConf notification id for mempressure.window */ +static guint mempressure_setting_window_id = 0; + +/** GConf notification id for mempressure.warning.stall time */ +static guint mempressure_setting_warning_stall_id = 0; + +/** GConf notification id for mempressure.warning.type */ +static guint mempressure_setting_warning_type_id = 0; -/** GConf notification id for mempressure.critical.used level */ -static guint mempressure_setting_critical_used_id = 0; +/** GConf notification id for mempressure.critical.stall time */ +static guint mempressure_setting_critical_stall_id = 0; + +/** GConf notification id for mempressure.critical.type */ +static guint mempressure_setting_critical_type_id = 0; /** GConf callback for mempressure related settings * @@ -605,23 +419,47 @@ mempressure_setting_cb(GConfClient *const gcc, const guint id, if (gcv == NULL) { mce_log(LL_WARN, "GConf Key `%s' has been unset", gconf_entry_get_key(entry)); - } - else if( id == mempressure_setting_warning_used_id ) { - gint old = mempressure_limit[MEMNOTIFY_LEVEL_WARNING].mnl_used; + } else if ( id == mempressure_setting_window_id ) { + gint old = mempressure_window; gint val = gconf_value_get_int(gcv); if( old != val ) { - mce_log(LL_DEBUG, "mempressure.warning.used: %d -> %d", old, val); - mempressure_limit[MEMNOTIFY_LEVEL_WARNING].mnl_used = val; - mempressure_cgroup_update_thresholds(); + mce_log(LL_DEBUG, "mempressure.window: %d -> %d", old, val); + mempressure_window = val; + mempressure_psi_update_thresholds(); } - } - else if( id == mempressure_setting_critical_used_id ) { - gint old = mempressure_limit[MEMNOTIFY_LEVEL_CRITICAL].mnl_used; + } else if ( id == mempressure_setting_warning_stall_id ) { + gint old = mempressure_warning_stall; + gint val = gconf_value_get_int(gcv); + if( old != val ) { + mce_log(LL_DEBUG, "mempressure.warning.stall: %d -> %d", old, val); + mempressure_warning_stall = val; + mempressure_psi_update_thresholds(); + } + } else if ( id == mempressure_setting_warning_type_id ) { + char* old = mempressure_warning_type; + const char* val = gconf_value_get_string(gcv); + if( !mempressure_streq(old, val) ) { + mce_log(LL_DEBUG, "mempressure.warning.type: %s -> %s", old, val); + g_free(mempressure_warning_type); + mempressure_warning_type = g_strdup(val); + mempressure_psi_update_thresholds(); + } + } else if ( id == mempressure_setting_critical_stall_id ) { + gint old = mempressure_critical_stall; gint val = gconf_value_get_int(gcv); if( old != val ) { - mce_log(LL_DEBUG, "mempressure.critical.used: %d -> %d", old, val); - mempressure_limit[MEMNOTIFY_LEVEL_CRITICAL].mnl_used = val; - mempressure_cgroup_update_thresholds(); + mce_log(LL_DEBUG, "mempressure.critical.stall: %d -> %d", old, val); + mempressure_critical_stall = val; + mempressure_psi_update_thresholds(); + } + } else if ( id == mempressure_setting_critical_type_id ) { + char* old = mempressure_critical_type; + const char* val = gconf_value_get_string(gcv); + if( !mempressure_streq(old, val) ) { + mce_log(LL_DEBUG, "mempressure.critical.type: %s -> %s", old, val); + g_free(mempressure_critical_type); + mempressure_critical_type = g_strdup(val); + mempressure_psi_update_thresholds(); } } else { @@ -635,36 +473,79 @@ mempressure_setting_cb(GConfClient *const gcc, const guint id, */ static void mempressure_setting_init(void) { - /* mempressure.warning.used level */ - mce_setting_notifier_add(MCE_SETTING_MEMNOTIFY_WARNING_PATH, - MCE_SETTING_MEMNOTIFY_WARNING_USED, + mce_setting_notifier_add(MCE_SETTING_MEMPRESSURE_PATH, + MCE_SETTING_MEMPRESSURE_WINDOW, mempressure_setting_cb, - &mempressure_setting_warning_used_id); + &mempressure_setting_window_id); - mce_setting_get_int(MCE_SETTING_MEMNOTIFY_WARNING_USED, - &mempressure_limit[MEMNOTIFY_LEVEL_WARNING].mnl_used); + mce_setting_get_int(MCE_SETTING_MEMPRESSURE_WINDOW, + &mempressure_window); - /* mempressure.critical.used level */ - mce_setting_notifier_add(MCE_SETTING_MEMNOTIFY_CRITICAL_PATH, - MCE_SETTING_MEMNOTIFY_CRITICAL_USED, + mce_setting_notifier_add(MCE_SETTING_MEMPRESSURE_WARNING_PATH, + MCE_SETTING_MEMPRESSURE_WARNING_STALL, mempressure_setting_cb, - &mempressure_setting_critical_used_id); + &mempressure_setting_warning_stall_id); - mce_setting_get_int(MCE_SETTING_MEMNOTIFY_CRITICAL_USED, - &mempressure_limit[MEMNOTIFY_LEVEL_CRITICAL].mnl_used); + mce_setting_get_int(MCE_SETTING_MEMPRESSURE_WARNING_STALL, + &mempressure_warning_stall); - mempressure_status_show_triggers(); + mce_setting_notifier_add(MCE_SETTING_MEMPRESSURE_WARNING_PATH, + MCE_SETTING_MEMPRESSURE_WARNING_TYPE, + mempressure_setting_cb, + &mempressure_setting_warning_type_id); + + mce_setting_get_string(MCE_SETTING_MEMPRESSURE_WARNING_TYPE, + &mempressure_warning_type); + + if ( mempressure_warning_type == NULL ) { + mempressure_warning_type = g_strdup(MCE_DEFAULT_MEMPRESSURE_WARNING_TYPE); + } + + mce_setting_notifier_add(MCE_SETTING_MEMPRESSURE_CRITICAL_PATH, + MCE_SETTING_MEMPRESSURE_CRITICAL_STALL, + mempressure_setting_cb, + &mempressure_setting_critical_stall_id); + + mce_setting_get_int(MCE_SETTING_MEMPRESSURE_CRITICAL_STALL, + &mempressure_critical_stall); + + mce_setting_notifier_add(MCE_SETTING_MEMPRESSURE_CRITICAL_PATH, + MCE_SETTING_MEMPRESSURE_CRITICAL_TYPE, + mempressure_setting_cb, + &mempressure_setting_critical_type_id); + + mce_setting_get_string(MCE_SETTING_MEMPRESSURE_CRITICAL_TYPE, + &mempressure_critical_type); + + if ( mempressure_critical_type == NULL ) { + mempressure_critical_type = g_strdup(MCE_DEFAULT_MEMPRESSURE_CRITICAL_TYPE); + } } /** Stop tracking setting changes */ static void mempressure_setting_quit(void) { - mce_setting_notifier_remove(mempressure_setting_warning_used_id), - mempressure_setting_warning_used_id = 0; + mce_setting_notifier_remove(mempressure_setting_window_id), + mempressure_setting_window_id = 0; + + mce_setting_notifier_remove(mempressure_setting_warning_stall_id), + mempressure_setting_warning_stall_id = 0; + + mce_setting_notifier_remove(mempressure_setting_warning_type_id), + mempressure_setting_warning_type_id = 0; + + g_free(mempressure_warning_type); + mempressure_warning_type = NULL; + + mce_setting_notifier_remove(mempressure_setting_critical_stall_id), + mempressure_setting_critical_stall_id = 0; + + mce_setting_notifier_remove(mempressure_setting_critical_type_id), + mempressure_setting_critical_type_id = 0; - mce_setting_notifier_remove(mempressure_setting_critical_used_id), - mempressure_setting_critical_used_id = 0; + g_free(mempressure_critical_type); + mempressure_critical_type = NULL; } /* ========================================================================= * @@ -674,7 +555,7 @@ static void mempressure_setting_quit(void) static void mempressure_plugin_quit(void) { - mempressure_cgroup_quit(); + mempressure_psi_quit(); mempressure_setting_quit(); } @@ -685,7 +566,7 @@ mempressure_plugin_init(void) mempressure_setting_init(); - if( !mempressure_cgroup_init() ) + if ( !mempressure_psi_init() ) goto EXIT; success = true; @@ -721,8 +602,8 @@ G_MODULE_EXPORT const gchar *g_module_check_init(GModule *module) } /* Check if required sysfs files are present */ - if( !mempressure_cgroup_is_available() ) { - mce_log(LL_WARN, "mempressure cgroup interface not available"); + if( !mempressure_psi_is_available() ) { + mce_log(LL_WARN, "mempressure psi interface not available"); goto EXIT; } diff --git a/modules/mempressure.h b/modules/mempressure.h new file mode 100644 index 0000000..3272dbf --- /dev/null +++ b/modules/mempressure.h @@ -0,0 +1,58 @@ +/** + * @file mempressure.h + * Memory use tracking and notification plugin for the Mode Control Entity + *

+ * Copyright (C) 2014-2019 Jolla Ltd. + *

+ * @author Simo Piiroinen + * + * mce is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * mce is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with mce. If not, see . + */ + +#ifndef MEMPRESSURE_H_ +# define MEMPRESSURE_H_ + +/* ========================================================================= * + * Settings + * ========================================================================= */ + +/** Prefix for mempressure setting keys */ +# define MCE_SETTING_MEMPRESSURE_PATH "/system/osso/dsm/mempressure" + +/** PSI tracking window [us] */ +# define MCE_SETTING_MEMPRESSURE_WINDOW MCE_SETTING_MEMPRESSURE_PATH"/window" +# define MCE_DEFAULT_MEMPRESSURE_WINDOW 1000000 + +/** Memnotify warning level configuration */ +# define MCE_SETTING_MEMPRESSURE_WARNING_PATH MCE_SETTING_MEMPRESSURE_PATH"/warning" + +/** Warning threshold stall stall time [us] */ +# define MCE_SETTING_MEMPRESSURE_WARNING_STALL MCE_SETTING_MEMPRESSURE_PATH"/warning/stall" +# define MCE_DEFAULT_MEMPRESSURE_WARNING_STALL 100000 + +/** Warning threshold type (some or full) */ +# define MCE_SETTING_MEMPRESSURE_WARNING_TYPE MCE_SETTING_MEMPRESSURE_PATH"/warning/type" +# define MCE_DEFAULT_MEMPRESSURE_WARNING_TYPE "some" + +/** Memnotify critical level configuration */ +# define MCE_SETTING_MEMPRESSURE_CRITICAL_PATH MCE_SETTING_MEMPRESSURE_PATH"/critical" + +/** Critical threshold for stall time [us] */ +# define MCE_SETTING_MEMPRESSURE_CRITICAL_STALL MCE_SETTING_MEMPRESSURE_PATH"/critical/stall" +# define MCE_DEFAULT_MEMPRESSURE_CRITICAL_STALL 150000 + +/** Critica threshold type (some or full) */ +# define MCE_SETTING_MEMPRESSURE_CRITICAL_TYPE MCE_SETTING_MEMPRESSURE_PATH"/critical/type" +# define MCE_DEFAULT_MEMPRESSURE_CRITICAL_TYPE "full" + +#endif /* MEMPRESSURE_H_ */