huawei-mrd-kernel/drivers/hwzerohung/watchpoint/hung_wp_lowmemorykiller.c

258 lines
6.7 KiB
C
Executable file

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/oom.h>
#include <linux/sched.h>
#include <linux/swap.h>
#include <linux/rcupdate.h>
#include <linux/notifier.h>
#include <linux/atomic.h>
#include <chipset_common/hwzrhung/zrhung.h>
#ifdef CONFIG_LOG_JANK
#include <huawei_platform/log/log_jank.h>
#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
}
}