#include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_LOG_JANK #include #endif #include "hung_wp_lowmemorykiller.h" static char lmkwp_logbuf[MAX_WP_BUFSIZE]; static lmkwp_main_t lmk_wp; static int config_retry = 3; static inline void lmkwp_config_show(lmkwp_config_t *this, const char *tag) { LMKWP_WARN("%s: is_ready(%d) enabled(%d) threshold(%u) period(%u) silence(%u) debuggable(%d)\n", tag, this->is_ready, this->enabled, this->threshold, jiffies_to_msecs(this->period), jiffies_to_msecs(this->silence), this->debuggable); } static inline u8 lmkwp_config_is_ready(void) { return lmk_wp.config.is_ready; } static inline u8 lmkwp_config_is_enabled(void) { return lmk_wp.config.enabled; } static inline u8 lmkwp_config_is_debuggable(void) { return lmk_wp.config.debuggable; } static inline u64 lmkwp_config_get_period(void) { return lmk_wp.config.period; } static inline unsigned int lmkwp_config_get_threshold(void) { return lmk_wp.config.threshold; } static int lmkwp_parse_config(char *str, uint32_t len, lmkwp_config_t *cfg_ptr) { lmkwp_config_t config = {0}; int ret = 0; int i_enable = 0; int i_debuggable = 0; if (str == NULL || len == 0 || cfg_ptr == NULL) return -1; ret = sscanf(str, " %1d,%llu,%u,%llu,%1d", &i_enable, /* the space is to skipped spaces in str*/ &config.period, &config.threshold, &config.silence, &i_debuggable); if (ret != 5) { LMKWP_WARN("parse config failed parameters(%d)\n", ret); return -1; } if (cfg_ptr) { config.enabled = (i_enable!=0)?1:0; config.debuggable = (i_debuggable!=0)?1:0; cfg_ptr->debuggable = config.debuggable; cfg_ptr->enabled = config.enabled; cfg_ptr->period = msecs_to_jiffies(config.period); cfg_ptr->threshold = config.threshold; cfg_ptr->silence = msecs_to_jiffies(config.silence); lmkwp_config_show(cfg_ptr, "config_parser"); } return 0; } static u8 lmkwp_config_load(void) { char config_str[128] = {0}; int ret = 0; lmkwp_config_t *config = &lmk_wp.config; ret = zrhung_get_config(ZRHUNG_WP_LMKD, config_str, sizeof(config_str)); if (ret > 0) { LMKWP_WARN("zrhung config not ready, wait...\n"); config->is_ready = false; } else if (ret < 0) { LMKWP_WARN("zrhung lmkwp is not enabled\n"); config->is_ready = true; config->enabled = false; } else { LMKWP_INFO("config string(%s)\n", config_str); if (lmkwp_parse_config(config_str, sizeof(config_str), config) != 0) config->enabled = false; /* if config is false, disable */ config->is_ready = true; } lmkwp_config_show(config, "config_load"); return config->is_ready; } static void lmkwp_event_ctor(lmkwp_event_t *this, struct task_struct *selected, struct shrink_control *sc, long cache_size, long cache_limit, short adj, long free) { this->stamp = get_jiffies_64(); memcpy(&this->sc, sc, sizeof(struct shrink_control)); /* copy selected process info */ this->selected_pid = selected->pid; this->selected_tgid = selected->tgid; memcpy(this->selected_comm, selected->comm, TASK_COMM_LEN); /* copy current process info */ this->cur_pid = current->pid; this->cur_tgid = current->tgid; memcpy(this->cur_comm, current->comm, TASK_COMM_LEN); /* current memory info */ this->cache_size = cache_size; this->cache_limit = cache_limit; this->adj = adj; this->free = free; } static int lmkwp_event_format(lmkwp_event_t *this, char *buf, size_t len) { return scnprintf(buf, len, "Killing '%s'(%d), tgid=%d, adj %hd\n" \ " to free memory on behalf of '%s' (%d) because\n" \ " cache %ldkB is below limit %ldkB\n" \ " Free memory is %ldkB above reserved (0x%x)\n", this->selected_comm, this->selected_pid, this->selected_tgid, this->adj, this->cur_comm, this->cur_pid, this->cache_size, this->cache_limit, this->free, this->sc.gfp_mask); } static size_t lmkwp_format_evenets(unsigned int pos) { unsigned int s = pos; size_t total_len = sizeof(lmkwp_logbuf); size_t written_len = 0; for (; s != lmk_wp.free; s = (s+1)%MAX_EVENTS) { size_t w_len = lmkwp_event_format(&lmk_wp.events[s], lmkwp_logbuf+written_len, total_len-written_len); written_len += w_len; } return written_len; } static bool lmkwp_check_threshold(unsigned int *pos) { lmkwp_config_t *config = &lmk_wp.config; lmkwp_event_t *events = lmk_wp.events; unsigned int last = lmk_wp.free; unsigned int new = lmk_wp.free == 0 ? (MAX_EVENTS - 1) : (lmk_wp.free - 1); unsigned int distance = last >= config->threshold?last-config->threshold:MAX_EVENTS+last-config->threshold; bool need_report = false; if (events[distance].stamp == 0) return need_report; if (events[new].stamp > events[distance].stamp + lmkwp_config_get_period()) { LMKWP_INFO("events[%d, %llu] to events [%d, %llu] not to upload", distance, events[distance].stamp, new, events[new].stamp); return need_report; } LMKWP_INFO("events[%d, %llu] to events [%d, %llu] need to upload", distance, events[distance].stamp, new, events[new].stamp); need_report = true; if (pos) *pos = distance; return need_report; } static lmkwp_event_t *lmkwp_get_slot(void) { lmkwp_event_t *ptr = lmk_wp.events+lmk_wp.free; lmk_wp.free++; lmk_wp.free = lmk_wp.free%MAX_EVENTS; return ptr; } int lmkwp_init(void) { LMKWP_INFO("lmkwp init sucess\n"); memset(&lmk_wp, 0, sizeof(lmkwp_main_t)); /*lmkwp_config_load(); */ return 0; } void lmkwp_report(struct task_struct *selected, struct shrink_control *sc, long cache_size, long cache_limit, short adj, long free) { unsigned int pos = 0; lmkwp_event_t *cur_event = NULL; /* check if need to load configure */ if (!lmkwp_config_is_ready()) { if (config_retry > 0) { lmkwp_config_load(); config_retry--; } else { return; } } /* check if lmkwp is enabled */ if (!lmkwp_config_is_enabled()) { LMKWP_INFO("report: lmkwp disabled ,exit\n"); return; } /* get free slot event */ cur_event = lmkwp_get_slot(); lmkwp_event_ctor(cur_event, selected, sc, cache_size, cache_limit, adj, free); if (cur_event->stamp < lmk_wp.last_report_stamp + lmk_wp.config.silence) { LMKWP_INFO("report: curr(%llu) less then last report(%llu)+silence(%llu)\n", cur_event->stamp, lmk_wp.last_report_stamp, lmk_wp.config.silence); return; } /* check if need to report events */ if (true == lmkwp_check_threshold(&pos)) { lmkwp_format_evenets(pos); //LMKWP_INFO("%s", lmkwp_logbuf); lmk_wp.last_report_stamp = cur_event->stamp; zrhung_send_event(ZRHUNG_WP_LMKD, "K,S", lmkwp_logbuf); #ifdef CONFIG_LOG_JANK LOG_JANK_D(349,"#ARG1:<%s>#ARG2:<%d>", current->comm, lmkwp_config_get_threshold()); #endif } }