huawei-mrd-kernel/kernel/huawei_hung_task.c

1649 lines
50 KiB
C

/*
* Detect Hung Task
*
* kernel/hisi_hung_task.c - kernel thread for detecting tasks stuck in D state
*
*/
#include <linux/mm.h>
#include <linux/cpu.h>
#include <linux/nmi.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/freezer.h>
#include <linux/kthread.h>
#include <linux/lockdep.h>
#include <linux/export.h>
#include <linux/sysctl.h>
#include <linux/utsname.h>
#include <trace/events/sched.h>
#include <linux/timer.h>
#include <linux/timex.h>
#include <linux/rtc.h>
#include <linux/slab.h>
#include <linux/version.h>
/* no sched/debug.h before kernel4.14 in which sched_show_task is declared */
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0))
#else
#include <linux/sched/debug.h>
#endif
#include <huawei_platform/log/log_jank.h>
#include <linux/ptrace.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/proc_fs.h>
#include <linux/errno.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/syscalls.h>
#include <asm/traps.h>
#include <linux/suspend.h>
#ifdef CONFIG_HISI_BB
#include <linux/hisi/rdr_hisi_ap_hook.h>
#endif
#ifdef CONFIG_TZDRIVER
#include <chipset_common/security/itrustee.h>
#endif
#ifdef CONFIG_HW_ZEROHUNG
#include <chipset_common/hwzrhung/zrhung.h>
#endif
/****************************MICRO DEFINITION*********************************/
#define RWSEM_READER_OWNED ((struct task_struct *)1UL)
#define ENABLE_SHOW_LEN 8
#define WHITELIST_STORE_LEN 200
#define IGNORELIST_LENGTH 100
#define CONCERNLIST_LENGTH 11
#define WHITE_LIST 1
#define BLACK_LIST 2
#define HT_ENABLE 1
#define HT_DISABLE 0
#define JANK_TASK_MAXNUM 8
#define HEARTBEAT_TIME 3
#define MAX_LOOP_NUM (CONFIG_DEFAULT_HUNG_TASK_TIMEOUT/\
HEARTBEAT_TIME)
#define ONE_MINUTE (60/HEARTBEAT_TIME)
#define ONE_AND_HALF_MINUTE (90/HEARTBEAT_TIME)
#define TWO_MINUTES (120/HEARTBEAT_TIME)
#define THREE_MINUTES (180/HEARTBEAT_TIME)
#define TWENTY_SECONDS (21/HEARTBEAT_TIME)
#define THIRTY_SECONDS (30/HEARTBEAT_TIME)
#define HUNG_ONE_HOUR (3600/HEARTBEAT_TIME)
#define HUNG_TEN_MINUTES (600/HEARTBEAT_TIME)
#define HUNGTASK_REPORT_TIMECOST TWENTY_SECONDS
#define HUNGTASK_DUMP_IN_PANIC_LOOSE 3
#define HUNGTASK_DUMP_IN_PANIC_STRICT 2
#define MAX_DUMP_TIMES 10
#define REFRESH_INTERVAL THREE_MINUTES
#define FLAG_NONE 0
#define FLAG_DUMP_WHITE 1
#define FLAG_DUMP_APP 2
#define FLAG_DUMP_NOSCHEDULE 4
#define FLAG_DUMP_JANK 8
#define FLAG_PANIC 16
#define FLAG_PF_FROZEN 64
#define TASK_TYPE_IGNORE 0
#define TASK_TYPE_WHITE 1
#define TASK_TYPE_APP 2 /*android java process, and should
not be TASK_TYPE_WHITE at the same time*/
#define TASK_TYPE_JANK 4 /*can combined with TASK_TYPE_APP or
TASK_TYPE_WHITE at the same time*/
#define TASK_TYPE_KERNEL 8 /*it's kernel task*/
#define TASK_TYPE_NATIVE 16 /*it's native task*/
#define TASK_TYPE_NOSCHEDULE 32 /*it's android watchdog task*/
#define TASK_TYPE_FROZEN 64 /*task is FROZEN */
#define PID_INIT 1 /*PID for init process,always 1*/
#define PID_KTHREAD 2 /*PID for kernel kthreadd, always 2*/
/****************************JANK REPORT CONTROLLERS******************/
#define JANK_REPORT_LIMIT_PERDAY 5
#define JANK_REPORT_TRESHOLD 1
/********************DEFAULT DUMP OR PANIC TIME SETTINGS**************/
#define DEFAULT_SHUT_THIS_DOWN 0
#define DEFAULT_WHITE_DUMP_CNT MAX_LOOP_NUM
#define DEFAULT_WHITE_PANIC_CNT MAX_LOOP_NUM
#define DEFAULT_APP_DUMP_CNT MAX_LOOP_NUM
#define DEFAULT_APP_PANIC_CNT DEFAULT_SHUT_THIS_DOWN
#define DEFAULT_NOSCHEDULE_DUMP_CNT (ONE_MINUTE - HUNGTASK_REPORT_TIMECOST)
#define DEFAULT_NOSCHEDULE_PANIC_CNT TWO_MINUTES
#define DEFAULT_JANK_DUMP_CNT JANK_REPORT_TRESHOLD
#define DEFAULT_OTHER_LOG_CNT MAX_LOOP_NUM
#define HUNG_TASK_RECORDTIME_CNT 4
#define HUNG_TASK_UPLOAD_ONCE 1
#define IGN_STATE_INIT 1
#define IGN_STATE_FIRST 2
#define IGN_STATE_DONE 3
#define IGNORELIST_EMPTY 0
#define IGNORELIST_NORMAL 1
#define IGNORELIST_FULL 2
#define TOPAPP_HUNG_INIT 0
#define TOPAPP_HUNG_FOUND 1
#define TOPAPP_HUNG_RECORDED 2
#define WATCHDOG_THREAD_NAME "watchdog"
/*
* Limit number of tasks checked in a batch.
* This value controls the preemptibility of khungtaskd since preemption
* is disabled during the critical section. It also controls the size of
* the RCU grace period. So it needs to be upper-bound.
*/
#define HUNG_TASK_BATCHING 1024
#define TIME_REFRESH_PIDS 20
#define HASH_ERROR -1
#define HASH_FIND 0
#define HASH_INSERT 1
#define HASH_CONTINUE 0
#define HASH_END 1
#define HASH_FIRST 47
#define HASH_SECOND 23
#define HASH_THIRD 17
/****************************ZRHUNG CONTROLLERS***********************/
#define HUNGTASK_EVENT_WHITELIST 1
#define HUNGTASK_EVENT_LAZYWATCHDOG 2
#define HUNGTASK_EVENT_TOPAPP 4
#define REPORT_MSGLENGTH 100
#define ZRHUNG_CONFIG_LENGTH 10
#define MAX_ZRHUNG_CMD_BUF_SIZE 20
/****************************ZRHUNG CONFIGS***************************/
#define ZRHUNG_HUNGTASK_ENABLE 49
/****************************STRUCTURE DEFINITION*********************/
struct task_item {
struct rb_node node;
pid_t pid;
pid_t tgid;
char name[TASK_COMM_LEN + 1];
unsigned long switchCount;
unsigned int task_type;
int dump_wa;
int panic_wa;
int dump_jank;
int time_in_D_state;
bool isDonewa;
bool isDonejank;
bool isReportjank;
};
struct hungtask_concernlist_table {
pid_t pid;
int end;
};
struct hungtask_concernlist_tmptable {
pid_t pid;
char name[TASK_COMM_LEN + 1];
};
struct hungtask_ignorelist_table {
pid_t pid;
int end;
int exist;
};
struct task_hung_upload {
char name[TASK_COMM_LEN + 1];
pid_t pid;
pid_t tgid;
int flag;
int duration;
};
/************************GLOBAL VARIABLE DEFINITION**************************/
static struct rb_root list_tasks = RB_ROOT; /*pid*/
static struct hungtask_concernlist_table whitelist[CONCERNLIST_LENGTH];
static struct hungtask_concernlist_tmptable whitetmplist[CONCERNLIST_LENGTH];
static struct hungtask_concernlist_table janklist[CONCERNLIST_LENGTH];
static struct hungtask_ignorelist_table ignorelist[IGNORELIST_LENGTH];
static bool whitelistEmpty = true;
static bool janklistEmpty = true;
/*the number of tasks checked*/
static int __read_mostly sysctl_hung_task_check_count = PID_MAX_LIMIT;
/*zero means infinite timeout - no checking done*/
static unsigned long __read_mostly huawei_hung_task_timeout_secs = CONFIG_DEFAULT_HUNG_TASK_TIMEOUT;
extern unsigned long __read_mostly sysctl_hung_task_timeout_secs;
/*static int __read_mostly sysctl_hung_task_warnings = MAX_DUMP_TIMES;*/
static int did_panic = 0; /*if not 0, then check no more*/
/*static struct task_struct *watchdog_task;*/
/*should we panic (and reboot, if panic_timeout= is set)
when a hung task is detected:*/
extern unsigned int __read_mostly sysctl_hung_task_panic;
/*control the switch on/off at "/sys/kernel/hubngtask/enable"*/
static unsigned int hungtask_enable = HT_DISABLE;
static unsigned int hungtask_watchdog_nosched_enable = 0;
static unsigned int hungtask_watchdog_nosched_firstkick = 0;
static unsigned int hungtask_watchdog_nosched_status = 1;
static unsigned int hungtask_watchdog_nosched_vmrebooting = 0;
/*used for white list D state detect*/
static unsigned int whitelist_type = WHITE_LIST;
static int whitelist_dump_cnt = DEFAULT_WHITE_DUMP_CNT;
static int whitelist_panic_cnt = DEFAULT_WHITE_PANIC_CNT;
/*used for jank D state dectect*/
static int jankproc_pids[JANK_TASK_MAXNUM];
static int jankproc_pids_size;
static int topapp_pid;
//static int last_topapp_pid;
static int found_in_topapp = TOPAPP_HUNG_INIT;
static int topapp_hung_times = 0;
static int janklist_dump_cnt = DEFAULT_JANK_DUMP_CNT;
static int jank_report_limit;
/*used for app D state detect*/
static int zygote64_pid;
static int zygote_pid;
static int maple_zygote64_pid;
static bool vmboot_flag = false;
static int systemserver_pid;
static int last_systemserver_pid;
static int watchdog_pid;
static int applist_dump_cnt = DEFAULT_APP_DUMP_CNT;
static int applist_panic_cnt = DEFAULT_APP_PANIC_CNT;
/*used for S state task that not be schedule for long time*/
static int noschedule_dump_cnt = DEFAULT_NOSCHEDULE_DUMP_CNT;
static int noschedule_panic_cnt = DEFAULT_NOSCHEDULE_PANIC_CNT;
static int topapp_hung_cnt = DEFAULT_WHITE_DUMP_CNT;
static int other_log_cnt = DEFAULT_OTHER_LOG_CNT;
static int watchdog_nosched_cnt = 0;
/*indicate ignorelist is generated or not, the list is used
to avoid too much D state task printing during dump*/
static int ignore_state = IGN_STATE_INIT;
static int ignorelist_state = IGNORELIST_EMPTY;
/*how many heartbeat happend after power on*/
static unsigned long cur_heartbeat;
static int hung_task_dump_and_upload = 0;
static int time_since_upload = 0;
static int hung_task_must_panic = 0;
#ifdef CONFIG_HW_ZEROHUNG
static int report_zrhung_id;
static int zrhung_WPhungtask_enable;
#endif
static struct task_hung_upload upload_hungtask;
static bool in_suspend = false;
static struct rtc_time jank_last_tm = {0};
static struct rtc_time jank_tm = {0};
enum hash_turn {
HASH_TURN_END = 0,
HASH_TURN_FIRST = 1,
HASH_TURN_SECOND,
HASH_TURN_THIRD,
HASH_TURN_FOURTH,
HASH_TURN_FIFTH
};
/****************************FUNCTION DEFINITION*************************/
static struct task_item *find_task(pid_t pid, struct rb_root *root);
static bool rcu_lock_break(struct task_struct *g, struct task_struct *t);
extern void sysrq_sched_debug_show(void);
#if defined(CONFIG_HW_CGROUP_INFO) && defined(CONFIG_HW_ZEROHUNG)
extern int freezer_info_show_messages(void);
#else
int freezer_info_show_messages(void) {return 0;}
#endif
#ifdef CONFIG_HW_ZEROHUNG
extern int hwhungtask_get_backlight(void);
#endif
static void hungtask_print_all_ignorelist(void)
{
pr_err("hungtask: the ignorelist is full which is abnormal for hungtask!\n");
}
static int ignorelist_hash_locate(pid_t pid, int goal, struct hungtask_ignorelist_table *whichlist)
{
int ret = HASH_ERROR;
int mid = 0, turn = HASH_TURN_FIRST, end = 0;
int valueExpect = 0, valueDisappoint = 0;
if (pid <= 0 || !hungtask_enable)
return HASH_ERROR;
valueExpect = (goal == HASH_FIND) ? pid : 0;
valueDisappoint = (goal == HASH_INSERT) ? pid : 0;
ret = pid % HASH_FIRST;
mid = pid / HASH_FIRST;
while (turn) {
if (whichlist[ret].pid == valueExpect) {
whichlist[ret].pid = pid;
if (valueDisappoint)
whichlist[ret].end = HASH_END;
return ret;
} else if (whichlist[ret].pid == valueDisappoint ||
(valueExpect && whichlist[ret].end == HASH_END)) {
return HASH_ERROR;
} else if (valueDisappoint)
whichlist[ret].end = HASH_CONTINUE;
turn++;
if(turn == HASH_TURN_SECOND)
ret = HASH_FIRST + (pid - mid) % HASH_SECOND;
else if (turn == HASH_TURN_THIRD)
ret = HASH_FIRST + HASH_SECOND + pid % HASH_THIRD;
else if (turn == HASH_TURN_FOURTH)
ret = HASH_FIRST + HASH_SECOND + HASH_THIRD;
else if (turn == HASH_TURN_FIFTH) {
ret++;
turn = HASH_TURN_FOURTH;
if (ret == IGNORELIST_LENGTH) {
ret = 0;
end++;
}
if(end == HASH_TURN_SECOND) {
if (HASH_INSERT == goal) {
pr_err("hungtask: No room to store another hung task!\n");
ignorelist_state = IGNORELIST_FULL;
}
turn = HASH_TURN_END;
}
}
}
return HASH_ERROR;
}
static int hunglist_hash_locate(pid_t pid, int goal, struct hungtask_concernlist_table *whichlist)
{
int ret = HASH_ERROR, turn = HASH_TURN_FIRST;
int valueExpect = 0, valueDisappoint = 0;
if (pid <= 0)
return HASH_ERROR;
valueExpect = (goal == HASH_FIND) ? pid : 0;
valueDisappoint = (goal == HASH_INSERT) ? pid : 0;
ret = pid % CONCERNLIST_LENGTH;
while (turn) {
if (whichlist[ret].pid == valueExpect) {
whichlist[ret].pid = pid;
if (valueDisappoint)
whichlist[ret].end = HASH_END;
return ret;
} else if (whichlist[ret].pid == valueDisappoint ||
(valueExpect && whichlist[ret].end == HASH_END)) {
return HASH_ERROR;
} else if (valueDisappoint)
whichlist[ret].end = HASH_CONTINUE;
ret++;
if (ret == CONCERNLIST_LENGTH) {
ret = 0;
turn++;
}
if (turn == HASH_TURN_THIRD)
turn = 0;
}
pr_err("hungtask: No room to store another list item.\n");
return HASH_ERROR;
}
static void hash_lists_init(void)
{
memset(whitelist, 0, sizeof(whitelist));
memset(whitetmplist, 0, sizeof(whitetmplist));
memset(janklist, 0, sizeof(janklist));
memset(ignorelist, 0, sizeof(ignorelist));
}
static void empty_whitelist(void)
{
memset(whitelist, 0 , sizeof(whitelist));
}
static void empty_janklist(void)
{
memset(janklist, 0 , sizeof(janklist));
}
static pid_t get_pid_by_name(const char *name)
{
int max_count = sysctl_hung_task_check_count;
int batch_count = HUNG_TASK_BATCHING;
struct task_struct *g, *t;
int pid = 0;
rcu_read_lock();
do_each_thread(g, t) {
if (!max_count--)
goto unlock_f;
if (!--batch_count) {
batch_count = HUNG_TASK_BATCHING;
if (!rcu_lock_break(g, t))
goto unlock_f;
}
if (!strncmp(t->comm, name, TASK_COMM_LEN)) {
/*the function is used to match whitelist, janklist, for system_server,
some thread has the same name as main thread, so we use tgid*/
pid = t->tgid;
goto unlock_f;
}
} while_each_thread(g, t);
unlock_f:
rcu_read_unlock();
return pid;
}
static int get_task_type(pid_t pid, pid_t tgid, struct task_struct *parent)
{
unsigned int flag = TASK_TYPE_IGNORE;
/*check tgid of it's parent as PPID*/
if (parent) {
pid_t ppid = parent->tgid;
if (PID_KTHREAD == ppid) {
flag |= TASK_TYPE_KERNEL;
} else if ((ppid == zygote_pid || ppid == zygote64_pid
|| ppid == maple_zygote64_pid) && tgid != systemserver_pid) {
flag |= TASK_TYPE_APP;
} else if (ppid == PID_INIT) {
flag |= TASK_TYPE_NATIVE;
}
}
if (!whitelistEmpty && hunglist_hash_locate(tgid, HASH_FIND, whitelist) != HASH_ERROR) {
flag |= TASK_TYPE_WHITE;
/*pr_err("hungtask: Task: %d in whitelist found in D state\n", pid);*/
}
if (!janklistEmpty && hunglist_hash_locate(tgid, HASH_FIND, janklist) != HASH_ERROR)
flag |= TASK_TYPE_JANK;
return flag;
}
static void refresh_zygote_pids(void)
{
int max_count = sysctl_hung_task_check_count;
int batch_count = HUNG_TASK_BATCHING;
struct task_struct *g, *t;
#ifdef CONFIG_HW_DIE_CATCH
unsigned short vipthread_flag = 0;
#endif
hungtask_watchdog_nosched_vmrebooting = 1;
rcu_read_lock();
do_each_thread(g, t) {
if (!max_count--)
goto unlock_f;
if (!--batch_count) {
batch_count = HUNG_TASK_BATCHING;
if (!rcu_lock_break(g, t))
goto unlock_f;
}
#ifdef CONFIG_HW_DIE_CATCH
if (t->signal != NULL) {
vipthread_flag = t->signal->unexpected_die_catch_flags;
} else {
vipthread_flag = 0;
}
#endif
if (!strncmp(t->comm, "main", TASK_COMM_LEN)) {
#ifdef CONFIG_HW_DIE_CATCH
if (vipthread_flag == 0)
continue;
#endif
if (t->tgid < systemserver_pid && vmboot_flag) {
if (zygote64_pid == 0) {
zygote64_pid = t->tgid;
pr_err("hungtask: zygote64 pid-%d.\n", zygote64_pid);
} else {
zygote_pid = t->tgid;
pr_err("hungtask: zygote pid-%d.\n", zygote_pid);
vmboot_flag = false;
}
}
} else if (!strncmp(t->comm, "system_server", TASK_COMM_LEN)) {
systemserver_pid = t->tgid;
if (systemserver_pid != last_systemserver_pid) {
vmboot_flag = true;
last_systemserver_pid = systemserver_pid;
}
if (t->pid == t->tgid) {
maple_zygote64_pid = t->real_parent->tgid;
pr_err("hungtask: maple_zygote64 pid-%d, system_server pid-%d.\n", maple_zygote64_pid, systemserver_pid);
}
} else if (!strncmp(t->comm, "watchdog", TASK_COMM_LEN)) {
if (systemserver_pid && systemserver_pid == t->tgid) {
hungtask_watchdog_nosched_vmrebooting = 0;
watchdog_pid = t->pid;
pr_err("hungtask: watchdog pid-%d.\n", watchdog_pid);
}
}
} while_each_thread(g, t);
unlock_f:
rcu_read_unlock();
}
static int refresh_pids(void)
{
int i = 0, ret = 0, hash_index = 0;
empty_whitelist();
for (i = 0; i < CONCERNLIST_LENGTH; i++) {
if(strlen(whitetmplist[i].name) > 0) {
pr_err("hungtask: whitetmplist[%d].name %s.\n", i, whitetmplist[i].name);
whitetmplist[i].pid = get_pid_by_name(whitetmplist[i].name);
hash_index = hunglist_hash_locate(whitetmplist[i].pid, HASH_INSERT, whitelist);
if (hash_index != HASH_ERROR)
pr_err("hungtask: whitelist member%d--%s-%d.\n",i ,
whitetmplist[i].name, whitelist[hash_index].pid);
}
}
/*refresh janklist*/
empty_janklist();
if (topapp_pid) {
hunglist_hash_locate(topapp_pid, HASH_INSERT, janklist);
}
for (i = 0; i < jankproc_pids_size; i++) {
hash_index = hunglist_hash_locate(jankproc_pids[i], HASH_INSERT, janklist);
if (hash_index > 0 && janklist[hash_index].pid > 0)
janklistEmpty = false;
}
/*refresh zygote_pid and zygote64_pid*/
refresh_zygote_pids();
return ret;
}
static struct task_item *find_task(pid_t pid, struct rb_root *root)
{
struct rb_node **p = &root->rb_node;
struct task_item *cur = NULL;
while (*p) {
struct rb_node *parent = NULL;
parent = *p;
cur = rb_entry(parent, struct task_item, node);
if (!cur)
return NULL;
if (pid < cur->pid)
p = &(*p)->rb_left;
else if (pid > cur->pid)
p = &(*p)->rb_right;
else
return cur;
}
return NULL;
}
static bool insert_task(struct task_item *item, struct rb_root *root)
{
struct rb_node **p = &root->rb_node;
struct rb_node *parent = NULL;
struct task_item *cur = NULL;
while (*p) {
parent = *p;
cur = rb_entry(parent, struct task_item, node);
if (!cur)
return false;
if (item->pid < cur->pid)
p = &(*p)->rb_left;
else if (item->pid > cur->pid)
p = &(*p)->rb_right;
else {
pr_info("hungtask: insert failed due to already"
" exist pid=%d,tgid=%d,name=%s,type=%d\n",
item->pid, item->tgid, item->name,
item->task_type);
return false;
}
}
rb_link_node(&item->node, parent, p);
rb_insert_color(&item->node, root);
/*if (item->task_type & (TASK_TYPE_WHITE | TASK_TYPE_JANK)) {
pr_info("hungtask: insert success pid=%d,tgid=%d,name=%s,"
"type=%d\n", item->pid, item->tgid, item->name, item->task_type);
}*/
return true;
}
void show_state_filter_ext(unsigned long state_filter)
{
struct task_struct *g, *p;
struct task_item *taskitem;
int exist_frozen = 0;
unsigned long long last_arrival, last_queued;
#if BITS_PER_LONG == 32
printk(KERN_INFO " task PC stack pid father\n");
#else
printk(KERN_INFO
" task PC stack pid father\n");
#endif
rcu_read_lock();
for_each_process_thread(g, p) {
/*
* reset the NMI-timeout, listing all files on a slow
* console might take a lot of time:
*/
touch_nmi_watchdog();
if (((p->state == TASK_RUNNING) || (p->state & state_filter))
&& (ignorelist_hash_locate(p->pid, HASH_FIND, ignorelist) == HASH_ERROR)) {
#ifdef CONFIG_SCHED_INFO
last_arrival = p->sched_info.last_arrival;
last_queued = p->sched_info.last_queued;
#else
last_arrival = 0;
last_queued = 0;
#endif /* CONFIG_SCHED_INFO */
taskitem = find_task(p->pid, &list_tasks);
if (unlikely(p->flags & PF_FROZEN)) {
exist_frozen++;
if (taskitem) {
pr_err("hungtask:name=%s,PID=%d,tgid=%d,tgname=%s FROZEN for %ds,"
"type=%d,SP=0x%08lx,la:%llu/lq:%llu.\n",
p->comm, p->pid, p->tgid, p->group_leader->comm,
taskitem->time_in_D_state * HEARTBEAT_TIME,
taskitem->task_type, p->thread.cpu_context.sp,
last_arrival, last_queued);
} else {
pr_err("hungtask:name=%s,PID=%d,tgid=%d,tgname=%s a new FROZEN task"
",SP=0x%08lx,la:%llu/lq:%llu.\n",
p->comm, p->pid, p->tgid, p->group_leader->comm,
p->thread.cpu_context.sp,
last_arrival, last_queued);
}
} else {
if (taskitem) {
pr_err("hungtask:name=%s,PID=%d,tgid=%d,tgname=%s,type=%d blocked %ds,"
"SP=0x%08lx,la:%llu/lq:%llu.\n", taskitem->name,
taskitem->pid, p->tgid, p->group_leader->comm, taskitem->task_type,
taskitem->time_in_D_state * HEARTBEAT_TIME,
p->thread.cpu_context.sp,
last_arrival, last_queued);
} else {
pr_err("hungtask:name=%s,PID=%d,tgid=%d,tgname=%s,SP=0x%08lx,la:%llu/lq:%llu.\n",
p->comm, p->pid, p->tgid,
p->group_leader->comm, p->thread.cpu_context.sp,
last_arrival, last_queued);
}
sched_show_task(p);
}
}
}
touch_all_softlockup_watchdogs();
#ifdef CONFIG_SCHED_DEBUG
#endif
rcu_read_unlock();
/*
* Only show locks if all tasks are dumped:
*/
if (TASK_UNINTERRUPTIBLE == state_filter || !state_filter)
debug_show_all_locks();
if (exist_frozen)
freezer_info_show_messages();
exist_frozen = 0;
}
void hwhungtask_show_state_filter(unsigned long state_filter)
{
show_state_filter_ext(state_filter);
}
EXPORT_SYMBOL(hwhungtask_show_state_filter);
static void jank_print_task_wchan(struct task_struct *task)
{
unsigned long wchan = 0;
char symname[KSYM_NAME_LEN] = {0};
char report_jank_text[REPORT_MSGLENGTH] = {0};
wchan = get_wchan(task);
if (lookup_symbol_name(wchan, symname) < 0) {
if (!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS))
return;
pr_err("hungtask: Task %s pid:%d tgid: %d wchan:[<%08lx>].\n",
task->comm, task->pid, task->tgid, wchan);
} else {
pr_err("hungtask: Task %s pid:%d tgid: %d wchan:[<%08lx>]-%s.\n",
task->comm, task->pid, task->tgid, wchan, symname);
}
snprintf(report_jank_text, sizeof(report_jank_text),
"hungtask-janklist Blocked task %s pid:%d tgid:%d tgname:%s wchan %s.\n",
task->comm, task->pid, task->tgid, task->group_leader->comm, symname);
#ifdef CONFIG_HW_ZEROHUNG
zrhung_send_event(ZRHUNG_WP_HTSK_WARNING, NULL, report_jank_text);
#endif
}
static void do_dump_task(struct task_struct *task)
{
sched_show_task(task);
debug_show_held_locks(task);
}
static void do_dump(struct task_struct *task, int flag, int time_in_D_state)
{
pr_err("hungtask: do_dump, flag=%d\n", flag);
rcu_read_lock();
if (!pid_alive(task)) {
rcu_read_unlock();
return;
}
#ifdef CONFIG_HUAWEI_PRINTK_CTRL
printk_level_setup(LOGLEVEL_DEBUG);
#endif
if (flag & (FLAG_DUMP_WHITE | FLAG_DUMP_APP)) {
int cnt = 0;
trace_sched_process_hang(task);
/*if (!sysctl_hung_task_warnings)
return;
if (sysctl_hung_task_warnings > 0)
sysctl_hung_task_warnings--;*/
cnt = time_in_D_state;
pr_err("INFO: task %s:%d tgid:%d blocked more than %d seconds"
" in %s.\n", task->comm, task->pid, task->tgid,
(HEARTBEAT_TIME * cnt),
(flag & FLAG_DUMP_WHITE) ? "whitelist" : "applist");
/*should this RDR hook bind with dump or panic---TBD ??*/
#ifdef CONFIG_HISI_BB
hung_task_hook((void *)task,
(u32) sysctl_hung_task_timeout_secs);
#endif
pr_err(" %s %s %.*s\n",
print_tainted(), init_utsname()->release,
(int)strcspn(init_utsname()->version, " "),
init_utsname()->version);
pr_err("\"echo 0 > /proc/sys/kernel/hung_task_timeout_secs\""
" disables this message.\n");
do_dump_task(task);
#ifdef CONFIG_RWSEM_SPIN_ON_OWNER
if (task->mm && task->mm->mmap_sem.owner &&
task->mm->mmap_sem.owner != RWSEM_READER_OWNED) {
pr_err("dump the semaphore owner task\n");
do_dump_task(task->mm->mmap_sem.owner);
}
#endif
touch_nmi_watchdog();
if (flag & FLAG_DUMP_WHITE && (!hung_task_dump_and_upload)) {
hung_task_dump_and_upload++;
upload_hungtask.pid = task->pid;
upload_hungtask.tgid = task->tgid;
upload_hungtask.duration = time_in_D_state;
memset(upload_hungtask.name, 0,sizeof(upload_hungtask.name));
strncpy(upload_hungtask.name, task->comm, sizeof(task->comm));
upload_hungtask.flag = flag;
if (task->flags & PF_FROZEN)
upload_hungtask.flag |= FLAG_PF_FROZEN;
}
} else if (flag & FLAG_DUMP_JANK) {
do_dump_task(task);
}
#ifdef CONFIG_HUAWEI_PRINTK_CTRL
printk_level_setup(sysctl_printk_level);
#endif
rcu_read_unlock();
}
static void do_panic(void)
{
if (sysctl_hung_task_panic) {
trigger_all_cpu_backtrace();
panic("hungtask: blocked tasks");
}
}
static void create_taskitem(struct task_item *taskitem, struct task_struct *task)
{
taskitem->pid = task->pid;
taskitem->tgid = task->tgid;
memset(taskitem->name, 0, sizeof(taskitem->name));
strncpy(taskitem->name, task->comm, sizeof(task->comm));
taskitem->switchCount = task->nvcsw + task->nivcsw;
taskitem->dump_wa = 0; /*whitelist or applist task dump times*/
taskitem->panic_wa = 0; /*whitelist or applist task panic times*/
taskitem->dump_jank = 0; /*janklist task dump times*/
taskitem->time_in_D_state = -1;/*D time_cnt is 1 less than dump_wa or dump_jank*/
taskitem->isDonewa = true; /*if task in white or app dealed */
taskitem->isDonejank = true; /*if task in jank dealed */
taskitem->isReportjank = false;
}
static bool refresh_task(struct task_item *taskitem, struct task_struct *task)
{
bool is_called = false;
if (taskitem->switchCount != (task->nvcsw + task->nivcsw)) {
taskitem->switchCount = task->nvcsw + task->nivcsw;
is_called = true;
return is_called;
}
if (taskitem->task_type & TASK_TYPE_WHITE) {
taskitem->isDonewa = false;
taskitem->dump_wa++;
taskitem->panic_wa++;
} else if (taskitem->task_type & TASK_TYPE_APP) {
taskitem->isDonewa = false;
taskitem->dump_wa++;
taskitem->panic_wa++;
}
if (taskitem->task_type & TASK_TYPE_JANK) {
taskitem->isDonejank = false;
taskitem->dump_jank++;
} else if (!(taskitem->task_type & (TASK_TYPE_WHITE | TASK_TYPE_APP))) {
taskitem->dump_wa++;
taskitem->isDonewa = false;
}
taskitem->time_in_D_state++;
if (task->flags & PF_FROZEN) {
taskitem->task_type |= TASK_TYPE_FROZEN;
}
return is_called;
}
static void generate_ignorelist(struct task_struct *t)
{
int hashIndex = 0;
if (IGN_STATE_INIT == ignore_state && cur_heartbeat > ONE_MINUTE) {
ignorelist_hash_locate(t->pid, HASH_INSERT, ignorelist);
} else if (IGN_STATE_FIRST == ignore_state &&
cur_heartbeat <= ONE_AND_HALF_MINUTE) {
hashIndex = ignorelist_hash_locate(t->pid, HASH_FIND, ignorelist);
if (hashIndex != HASH_ERROR)
ignorelist[hashIndex].exist++;
}
}
static void remove_list_tasks(struct task_item *item)
{
/*if(item->task_type & (TASK_TYPE_WHITE | TASK_TYPE_JANK)) {
pr_info("hungtask: remove from list_tasks pid=%d,tgid=%d,"
"name=%s\n", item->pid, item->tgid, item->name);
}*/
rb_erase(&item->node, &list_tasks);
kfree(item);
}
static void shrink_list_tasks(void)
{
bool found = false;
do {
struct rb_node *n;
struct task_item *item = NULL;
found = false;
for (n = rb_first(&list_tasks); n != NULL; n = rb_next(n)) {
item = rb_entry(n, struct task_item, node);
if (!item)
continue;
if (item->isDonewa && item->isDonejank) {
found = true;
break;
}
}
if (found)
remove_list_tasks(item);
} while (found);
}
static void check_parameters(void)
{
if (whitelist_dump_cnt < 0 || whitelist_dump_cnt > DEFAULT_WHITE_DUMP_CNT)
whitelist_dump_cnt = DEFAULT_WHITE_DUMP_CNT;
if (whitelist_panic_cnt <= 0 || whitelist_panic_cnt > DEFAULT_WHITE_PANIC_CNT)
whitelist_panic_cnt = DEFAULT_WHITE_PANIC_CNT;
if (applist_dump_cnt < 0 || applist_dump_cnt > MAX_LOOP_NUM)
applist_dump_cnt = DEFAULT_APP_DUMP_CNT;
if (applist_panic_cnt != DEFAULT_APP_PANIC_CNT)
applist_panic_cnt = DEFAULT_APP_PANIC_CNT;
if (noschedule_panic_cnt < 0 /*DEFAULT_NOSCHEDULE_PANIC_CNT*/ ||
noschedule_panic_cnt > MAX_LOOP_NUM) {
noschedule_panic_cnt = DEFAULT_NOSCHEDULE_PANIC_CNT;
}
if (janklist_dump_cnt < 0 || janklist_dump_cnt > MAX_LOOP_NUM)
janklist_dump_cnt = DEFAULT_JANK_DUMP_CNT;
}
#ifdef CONFIG_HW_ZEROHUNG
static void hungtask_report_zrhung(int event)
{
bool report_load = false;
char report_buf_tag[REPORT_MSGLENGTH] = {0};
char report_buf_tag_cmd[REPORT_MSGLENGTH + 2] = {0};
char report_buf_text[REPORT_MSGLENGTH] = {0};
char report_name[TASK_COMM_LEN + 1] = {0};
char cmd[MAX_ZRHUNG_CMD_BUF_SIZE] = {0};
int report_pid = 0, report_hungtime = 0, report_tasktype = 0;
if (zrhung_WPhungtask_enable != ZRHUNG_HUNGTASK_ENABLE)
return;
if (!event)
return;
if (event & HUNGTASK_EVENT_WHITELIST) {
snprintf(report_buf_tag, sizeof(report_buf_tag), "hungtask_whitelist_%d", report_zrhung_id);
strncpy(report_name, upload_hungtask.name, TASK_COMM_LEN);
report_pid = upload_hungtask.pid;
report_tasktype = TASK_TYPE_WHITE;
report_hungtime = whitelist_dump_cnt * HEARTBEAT_TIME;
report_load = true;
} else if (event & HUNGTASK_EVENT_LAZYWATCHDOG) {
snprintf(report_buf_tag, sizeof(report_buf_tag), "hungtask_lazydog_%d", report_zrhung_id);
snprintf(report_name, TASK_COMM_LEN, "vmwatchdog");
report_pid = watchdog_pid;
report_tasktype = TASK_TYPE_NOSCHEDULE;
report_hungtime = watchdog_nosched_cnt * HEARTBEAT_TIME;
upload_hungtask.flag = FLAG_DUMP_NOSCHEDULE;
report_load = true;
} else if (event & HUNGTASK_EVENT_TOPAPP) {
snprintf(report_buf_tag, sizeof(report_buf_tag), "hungtask_topapp_%d", report_zrhung_id);
strncpy(report_name, upload_hungtask.name, TASK_COMM_LEN);
report_pid = upload_hungtask.pid;
report_tasktype = TASK_TYPE_APP;
report_hungtime = topapp_hung_cnt * HEARTBEAT_TIME;
if (upload_hungtask.duration < TWO_MINUTES || (0 == upload_hungtask.duration % HUNG_ONE_HOUR))
report_load = true;
} else
pr_err("hungtask: No such event report to zerohung!");
pr_err("hungtask: %s start.\n", report_buf_tag);
if (event & HUNGTASK_EVENT_WHITELIST)
pr_err("hungtask: HUNGTASK_EVENT_WHITELIST happens , report to zrhung!\n");
if (event & HUNGTASK_EVENT_LAZYWATCHDOG) {
pr_err("hungtask: HUNGTASK_EVENT_LAZYWATCHDOG happens in this cycle!\n");
snprintf(cmd, MAX_ZRHUNG_CMD_BUF_SIZE, "P=%d", systemserver_pid);
zrhung_send_event(ZRHUNG_WP_HUNGTASK, cmd, "hungtask: vmreboot watchdog not scheduled for 39s.");
}
if (event & HUNGTASK_EVENT_TOPAPP)
pr_err("hungtask: HUNGTASK_EVENT_TOPAPP happens in this cycle!\n");
if (upload_hungtask.flag & FLAG_PF_FROZEN) {
snprintf(report_buf_text, sizeof(report_buf_text),
"Task %s(%s) pid %d type %d blocked %ds.",
report_name, "FROZEN", report_pid,
report_tasktype, report_hungtime);
} else
snprintf(report_buf_text, sizeof(report_buf_text),
"Task %s pid %d type %d blocked %ds.",
report_name, report_pid, report_tasktype, report_hungtime);
if (report_load) {
show_state_filter_ext(TASK_UNINTERRUPTIBLE);
pr_err("hungtask: %s end.\n", report_buf_tag);
snprintf(report_buf_tag_cmd, sizeof(report_buf_tag_cmd), "R=%s", report_buf_tag);
zrhung_send_event(ZRHUNG_WP_HUNGTASK, report_buf_tag_cmd, report_buf_text);
report_zrhung_id++;
}
}
#endif
static int hungtask_check_topapp(void)
{
if (found_in_topapp == TOPAPP_HUNG_INIT)
topapp_hung_times = 0;
if (topapp_hung_times > topapp_hung_cnt) {
topapp_hung_times = 0;
#ifdef CONFIG_HW_ZEROHUNG
if (hwhungtask_get_backlight() > 0)
return HUNGTASK_EVENT_TOPAPP;
#endif
}
return 0;
}
static int watchdog_nosched_check(void)
{
int ret = 0;
#ifdef CONFIG_HW_ZEROHUNG
char cmd[MAX_ZRHUNG_CMD_BUF_SIZE] = {0};
#endif
if (!hungtask_watchdog_nosched_enable || !hungtask_watchdog_nosched_firstkick)
return ret;
if(watchdog_nosched_cnt >= noschedule_dump_cnt) {
refresh_zygote_pids();
if (hungtask_watchdog_nosched_vmrebooting) {
pr_err("hungtask: watchdog nowhere probably because vmrebooting.\n");
watchdog_nosched_cnt = 0;
hungtask_watchdog_nosched_vmrebooting = 0;
return ret;
}
if (watchdog_nosched_cnt == noschedule_dump_cnt) {
pr_err("hungtask: vmreboot watchdog not scheduled for more"
"than %d seconds, notify zerohung.\n",
noschedule_dump_cnt * HEARTBEAT_TIME);
watchdog_nosched_cnt++;
return HUNGTASK_EVENT_LAZYWATCHDOG;
}
if(watchdog_nosched_cnt > noschedule_panic_cnt) {
pr_err("hungtask: vmreboot watchdog not scheduled for more"
"than %d seconds.\n", noschedule_panic_cnt * HEARTBEAT_TIME);
#ifdef CONFIG_HW_ZEROHUNG
snprintf(cmd, MAX_ZRHUNG_CMD_BUF_SIZE, "S,P=%d", systemserver_pid);
zrhung_send_event(ZRHUNG_WP_HUNGTASK, cmd, "hungtask: vmreboot watchdog not scheduled for 120s.");
#endif
watchdog_nosched_cnt = 0;
pr_err("hungtask: print all running cpu stack and"
" D state stack due to systemserver watchdog"
" thread not schedule\n");
show_state_filter_ext(TASK_UNINTERRUPTIBLE);
do_panic();
}
}
if (hungtask_watchdog_nosched_status) {
watchdog_nosched_cnt = 0;
hungtask_watchdog_nosched_status = 0;
}
watchdog_nosched_cnt++;
return ret;
}
static void post_process(void)
{
struct rb_node *n;
int i = 0;
int hungevent = 0;
#ifdef CONFIG_HW_ZEROHUNG
int err;
char config_str[ZRHUNG_CONFIG_LENGTH] = {0};
#endif
if (hung_task_dump_and_upload == HUNG_TASK_UPLOAD_ONCE) {
hungevent |= HUNGTASK_EVENT_WHITELIST;
hung_task_dump_and_upload++;
}
if (hung_task_dump_and_upload > 0) {
time_since_upload++;
if (time_since_upload > (whitelist_panic_cnt - whitelist_dump_cnt)) {
hung_task_dump_and_upload = 0;
time_since_upload = 0;
}
}
if (hung_task_must_panic) {
show_state_filter_ext(TASK_UNINTERRUPTIBLE);
hung_task_must_panic = 0;
pr_err("hungtask: The whitelist task %s:%d blocked more than %ds is causing panic.\n",
upload_hungtask.name, upload_hungtask.pid,
whitelist_panic_cnt * HEARTBEAT_TIME);
do_panic();
}
if (IGN_STATE_INIT == ignore_state && cur_heartbeat > ONE_MINUTE) {
ignore_state = IGN_STATE_FIRST;
} else if (IGN_STATE_FIRST == ignore_state && cur_heartbeat <=
ONE_AND_HALF_MINUTE) {
for (i = 0; i < IGNORELIST_LENGTH; i++) {
if(ignorelist[i].exist == 0) {
ignorelist[i].pid = 0;
} else
ignorelist[i].exist = 0;
}
} else if (ignore_state == IGN_STATE_FIRST && cur_heartbeat > ONE_AND_HALF_MINUTE) {
ignore_state = IGN_STATE_DONE;
}
shrink_list_tasks();
for (n = rb_first(&list_tasks); n != NULL; n = rb_next(n)) {
struct task_item *item = rb_entry(n, struct task_item, node);
item->isDonewa = true;
item->isDonejank = true;
}
hungevent |= hungtask_check_topapp();
hungevent |= watchdog_nosched_check();
#ifdef CONFIG_HW_ZEROHUNG
if (hungevent) {
err = zrhung_get_config(ZRHUNG_WP_HUNGTASK, config_str, sizeof(config_str));
if (err)
pr_err("hungtask: get zrhung config failed.\n");
else
zrhung_WPhungtask_enable = (int)config_str[0];
hungtask_report_zrhung(hungevent);
}
#endif
}
/*
* To avoid extending the RCU grace period for an unbounded amount of time,
* periodically exit the critical section and enter a new one.
*
* For preemptible RCU it is sufficient to call rcu_read_unlock in order
* to exit the grace period. For classic RCU, a reschedule is required.
*/
static bool rcu_lock_break(struct task_struct *g, struct task_struct *t)
{
bool can_cont = false;
get_task_struct(g);
get_task_struct(t);
rcu_read_unlock();
cond_resched();
rcu_read_lock();
can_cont = pid_alive(g) && pid_alive(t);
put_task_struct(t);
put_task_struct(g);
return can_cont;
}
static int dump_task_wa(struct task_item *item, int dump_cnt,
struct task_struct *task, int flag)
{
int ret = 0;
if (item->time_in_D_state > TWO_MINUTES &&
(item->time_in_D_state % TWO_MINUTES != 0))
return ret;
if (item->time_in_D_state > HUNG_TEN_MINUTES &&
(item->time_in_D_state % HUNG_TEN_MINUTES != 0))
return ret;
if (item->time_in_D_state > HUNG_ONE_HOUR &&
(item->time_in_D_state % HUNG_ONE_HOUR != 0))
return ret;
if (dump_cnt && item->dump_wa > dump_cnt) {
item->dump_wa = 1;
if (!hung_task_dump_and_upload) {
if (task->flags & PF_FROZEN) {
#ifndef CONFIG_FINAL_RELEASE
pr_err("hungtask: Task %s:%d tgid:%d type:%d is FROZEN for %ds.\n",
item->name, item->pid, item->tgid, item->task_type,
item->time_in_D_state * HEARTBEAT_TIME);
#endif
} else {
pr_err("hungtask: Ready to dump a task %s.\n", item->name);
do_dump(task, flag, item->time_in_D_state);
ret++;
}
}
}
return ret;
}
static void check_topapp_hung(struct task_item *item)
{
if (item->pid == topapp_pid) {
if (found_in_topapp == TOPAPP_HUNG_INIT) {
found_in_topapp = TOPAPP_HUNG_FOUND;
if (!hung_task_dump_and_upload) {
upload_hungtask.pid = item->pid;
upload_hungtask.tgid = item->tgid;
upload_hungtask.duration = item->time_in_D_state;
memset(upload_hungtask.name, 0,sizeof(upload_hungtask.name));
strncpy(upload_hungtask.name, item->name, sizeof(item->name));
if (item->task_type & TASK_TYPE_FROZEN)
upload_hungtask.flag |= FLAG_PF_FROZEN;
}
}
if (found_in_topapp == TOPAPP_HUNG_FOUND) {
found_in_topapp = TOPAPP_HUNG_RECORDED;
topapp_hung_times++;
}
}
}
static void deal_task(struct task_item *item, struct task_struct *task, bool is_called)
{
int any_dumped_num = 0;
if (is_called) {
item->dump_wa = 1;
item->panic_wa = 1;
item->dump_jank = 1;
item->time_in_D_state = 0;
return;
}
if (item->task_type & TASK_TYPE_WHITE) {
any_dumped_num =
dump_task_wa(item, whitelist_dump_cnt, task, FLAG_DUMP_WHITE);
} else if (item->task_type & TASK_TYPE_APP) {
any_dumped_num =
dump_task_wa(item, applist_dump_cnt, task, FLAG_DUMP_APP);
}
if (item->task_type & TASK_TYPE_JANK && janklist_dump_cnt) {
if (item->dump_jank > janklist_dump_cnt
&& false == item->isReportjank
&& !hung_task_dump_and_upload) {
LOG_JANK_D(JLID_KERNEL_HUNG_TASK,
"#ARG1:<%s>#ARG2:<%d>", item->name,
(item->dump_jank - 1) * HEARTBEAT_TIME);
item->isReportjank = true;
if (jank_report_limit < JANK_REPORT_LIMIT_PERDAY) {
jank_print_task_wchan(task);
do_dump(task, FLAG_DUMP_JANK, item->time_in_D_state);
jank_report_limit++;
}
}
} else if (!(item->task_type & (TASK_TYPE_WHITE | TASK_TYPE_APP))) {
if (item->dump_wa > other_log_cnt && item->time_in_D_state < HUNG_ONE_HOUR) {
#ifndef CONFIG_FINAL_RELEASE
pr_err("hungtask: Unconcerned task %s:%d blocked more than %d seconds\n",
item->name, item->pid, item->time_in_D_state * HEARTBEAT_TIME);
#endif
item->dump_wa = 1;
any_dumped_num++;
}
}
#ifdef CONFIG_TZDRIVER
if (any_dumped_num && is_tee_hungtask(task)) {
pr_info("hungtask: related to teeos was detected, dump status of teeos\n");
wakeup_tc_siq();
}
#endif
if (!is_called && (item->task_type & TASK_TYPE_WHITE)) {
if(whitelist_panic_cnt && item->panic_wa > whitelist_panic_cnt) {
pr_err("hungtask: A whitelist task %s is about to cause panic.\n", item->name);
item->panic_wa = 0;
hung_task_must_panic++;
} else
item->isDonewa = false;
}
check_topapp_hung(item);
if (item->isDonewa && item->isDonejank)
remove_list_tasks(item);
}
static bool check_conditions(struct task_struct *task, unsigned int task_type)
{
bool needNoCheck = true;
if (task->flags & PF_FROZEN && in_suspend) {
return needNoCheck;
}
if (task_type & TASK_TYPE_WHITE && (whitelist_dump_cnt ||
whitelist_panic_cnt))
needNoCheck = false;
else if (task_type & TASK_TYPE_APP && (applist_dump_cnt ||
applist_panic_cnt))
needNoCheck = false;
if (task_type & TASK_TYPE_JANK && janklist_dump_cnt)
needNoCheck = false;
else if (!(task_type & (TASK_TYPE_WHITE | TASK_TYPE_APP)) && ignore_state == IGN_STATE_DONE)
needNoCheck = false;
return needNoCheck;
}
static void get_localtime(struct rtc_time *tm)
{
struct timex txc;
do_gettimeofday(&(txc.time));
txc.time.tv_sec -= (int)(sys_tz.tz_minuteswest * 60);
rtc_time_to_tm(txc.time.tv_sec,tm);
}
static void jank_refresh_limit_check(void)
{
get_localtime(&jank_tm);
if((jank_tm.tm_year != jank_last_tm.tm_year)
||(jank_tm.tm_mon != jank_last_tm.tm_mon)
|| (jank_tm.tm_mday != jank_last_tm.tm_mday)){
jank_report_limit = 0;
}
memcpy(&jank_last_tm, &jank_tm, sizeof(struct rtc_time));
}
void check_hung_tasks_proposal(unsigned long timeout)
{
int max_count = sysctl_hung_task_check_count;
int batch_count = HUNG_TASK_BATCHING;
struct task_struct *g, *t;
static int do_refresh = 0;
if(!hungtask_enable)
return;
if (test_taint(TAINT_DIE) || did_panic) {
pr_err("hungtask: heartbeart=%ld, it's going to panic, "
"ignore this heartbeart\n", cur_heartbeat);
return;
}
cur_heartbeat++;
if((cur_heartbeat % REFRESH_INTERVAL) == 0) {
pr_info("hungtask: The huawei hungtask detect is running.\n");
if (unlikely(IGNORELIST_FULL == ignorelist_state)) {
hungtask_print_all_ignorelist();
}
do_refresh = 1;
} else
do_refresh = 0;
if(do_refresh || cur_heartbeat < TIME_REFRESH_PIDS) {
refresh_pids();
check_parameters();
}
found_in_topapp = TOPAPP_HUNG_INIT;
rcu_read_lock();
for_each_process_thread(g, t) {
bool is_called = false;
if (!max_count--)
goto unlock;
if (!--batch_count) {
batch_count = HUNG_TASK_BATCHING;
if (!rcu_lock_break(g, t))
goto unlock;
}
if (t->state == TASK_UNINTERRUPTIBLE) {
unsigned int task_type = TASK_TYPE_IGNORE;
unsigned long switch_count = t->nvcsw + t->nivcsw;
struct task_item *taskitem;
int hashIndex = HASH_ERROR;
/*
* When a freshly created task is scheduled once,
*changes its state to TASK_UNINTERRUPTIBLE without
*having ever been switched out once, it musn't
*be checked.
*/
if (unlikely(!switch_count)) {
pr_info("hungtask: heartbeart=%ld, switch_count"
" is zero, ignore this heartbeart\n", cur_heartbeat);
continue;
}
if (ignore_state == IGN_STATE_DONE) {
hashIndex = ignorelist_hash_locate(t->pid, HASH_FIND, ignorelist);
if (hashIndex != HASH_ERROR)
continue;
} else
generate_ignorelist(t);
taskitem = find_task(t->pid, &list_tasks);
if (taskitem) {
if (check_conditions(t, taskitem->task_type))
continue;
is_called = refresh_task(taskitem, t);
} else {
task_type = get_task_type(t->pid,
t->tgid, t->real_parent);
if (check_conditions(t, task_type))
continue;
taskitem = kmalloc(sizeof(*taskitem),
GFP_ATOMIC);
if (!taskitem) {
pr_err("hungtask: kmalloc failed");
continue;
}
memset(taskitem, 0, sizeof(*taskitem));
taskitem->task_type = task_type;
create_taskitem(taskitem, t);
is_called = refresh_task(taskitem, t);
insert_task(taskitem, &list_tasks);
}
deal_task(taskitem, t, is_called);
}
}
unlock:
rcu_read_unlock();
post_process();
if (do_refresh)
jank_refresh_limit_check();
}
static ssize_t watchdog_nosched_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
{
if (hungtask_watchdog_nosched_status)
return snprintf(buf, ENABLE_SHOW_LEN, "kick\n");
if (hungtask_watchdog_nosched_enable)
return snprintf(buf, ENABLE_SHOW_LEN, "on\n");
else
return snprintf(buf, ENABLE_SHOW_LEN, "off\n");
}
static ssize_t watchdog_nosched_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t count)
{
char tmp[6];
size_t len = 0;
char *p;
if ((count < 2) || (count > (sizeof(tmp) - 1))) {
pr_err("hungtask: string too long or too short.\n");
return -EINVAL;
}
if (!buf)
return -EINVAL;
p = memchr(buf, '\n', count);
len = p ? (size_t) (p - buf) : count;
memset(tmp, 0, sizeof(tmp));
strncpy(tmp, buf, len);
if (strncmp(tmp, "on", strlen(tmp)) == 0) {
hungtask_watchdog_nosched_enable = 1;
pr_err("hungtask: watchdog_nosched_enable is set to enable.\n");
} else if (unlikely(strncmp(tmp, "off", strlen(tmp)) == 0)) {
hungtask_watchdog_nosched_enable = 0;
pr_err("hungtask: watchdog_nosched_enable is set to disable.\n");
} else if (likely(strncmp(tmp, "kick", strlen(tmp)) == 0)) {
hungtask_watchdog_nosched_firstkick = 1;
hungtask_watchdog_nosched_status = 1;
/*pr_err("hungtask: watchdog_nosched_enable is kick.\n");*/
} else
pr_err("hungtask: only accept on off or kick !\n");
return (ssize_t) count;
}
static ssize_t enable_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
{
if (hungtask_enable)
return snprintf(buf, ENABLE_SHOW_LEN, "on\n");
else
return snprintf(buf, ENABLE_SHOW_LEN, "off\n");
}
static ssize_t enable_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t count)
{
char tmp[6];
size_t len = 0;
char *p;
if ((count < 2) || (count > (sizeof(tmp) - 1))) {
pr_err("hungtask: string too long or too short.\n");
return -EINVAL;
}
if (!buf)
return -EINVAL;
p = memchr(buf, '\n', count);
len = p ? (size_t) (p - buf) : count;
memset(tmp, 0, sizeof(tmp));
strncpy(tmp, buf, len);
if (strncmp(tmp, "on", strlen(tmp)) == 0) {
hungtask_enable = HT_ENABLE;
pr_info("hungtask: hungtask_enable is set to enable.\n");
} else if (strncmp(tmp, "off", strlen(tmp)) == 0) {
hungtask_enable = HT_DISABLE;
pr_info("hungtask: hungtask_enable is set to disable.\n");
} else
pr_err("hungtask: only accept on or off !\n");
return (ssize_t) count;
}
static ssize_t monitorlist_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
char *start = buf;
char all_buf[WHITELIST_STORE_LEN - 20];
int i = 0;
unsigned long len = 0;
memset(all_buf, 0, sizeof(all_buf));
for (i = 0; i < CONCERNLIST_LENGTH; i++) {
if (whitetmplist[i].pid > 0) {
len += snprintf(all_buf + len, sizeof(all_buf) - len, "%s-%d,",
whitetmplist[i].name, whitetmplist[i].pid);
if (!(len < sizeof(all_buf))) {
len = sizeof(all_buf) - 1;
break;
}
}
}
if (len > 0)
all_buf[len] = 0;
/*pr_info("hungtask: show all_buf=%s\n", all_buf);*/
if (whitelist_type == WHITE_LIST)
buf += snprintf(buf, WHITELIST_STORE_LEN, "whitelist: [%s]\n",
all_buf);
else if (whitelist_type == BLACK_LIST)
buf += snprintf(buf, WHITELIST_STORE_LEN, "blacklist: [%s]\n",
all_buf);
else
buf += snprintf(buf, WHITELIST_STORE_LEN, "\n");
return buf - start;
}
/*
* monitorlist_store - Called when 'write/echo' method is
* used on entry '/sys/kernel/hungtask/monitorlist'.
*/
static ssize_t monitorlist_store(struct kobject *kobj,
struct kobj_attribute *attr, const char *buf,
size_t n)
{
size_t len = 0;
char *p;
char all_buf[WHITELIST_STORE_LEN];
char *cur = all_buf;
int index = 0;
if ((n < 2) || (n > (sizeof(all_buf) - 1))) {
pr_err("hungtask: whitelist input string too long or too short.\n");
return -EINVAL;
}
if (!buf)
return -EINVAL;
/*input format:
*write /sys/kernel/hungtask/monitorlist "whitelist,
*system_server,surfaceflinger"*/
p = memchr(buf, '\n', n);
len = p ? (size_t) (p - buf) : n; /*exclude the '\n'*/
memset(all_buf, 0, sizeof(all_buf));
strncpy(all_buf, buf,
len > WHITELIST_STORE_LEN ? WHITELIST_STORE_LEN : len);
p = strsep(&cur, ",");
if (!cur) {
pr_err("hungtask: input string is not correct!\n");
return -EINVAL;
}
if (!strncmp(p, "whitelist", n)) {
whitelist_type = WHITE_LIST;
} else{
if (!strncmp(p, "blacklist", n))
/*whitelist_type = BLACK_LIST;*/
pr_err("hungtask: blacklist is not support!\n");
else
pr_err("hungtask: wrong list type is set!\n");
return -EINVAL;
}
if (!strlen(cur)) {
pr_err("hungtask: at least one process need to be set!\n");
return -EINVAL;
}
pr_err("hungtask: whitelist is %s.\n", cur);
hash_lists_init();
/*generate the new whitelist*/
for (;;) {
char *token;
token = strsep(&cur, ",");
if (token && strlen(token)) {
strncpy(whitetmplist[index].name, token, TASK_COMM_LEN);
if(strlen(whitetmplist[index].name) > 0)
whitelistEmpty = false;
index++;
if (index >= CONCERNLIST_LENGTH)
break;
}
if (!cur)
break;
}
/*check again in case user input "whitelist,,,,,,"*/
if (whitelistEmpty) {
pr_err("hungtask: at least one process need to be set!\n");
return -EINVAL;
}
return (ssize_t) n;
}
static struct notifier_block pm_event = {0};
static int pm_sr_event(struct notifier_block *this,
unsigned long event, void *ptr)
{
switch (event) {
case PM_SUSPEND_PREPARE:
in_suspend = true;
break;
case PM_POST_SUSPEND:
in_suspend = false;
break;
default:
return NOTIFY_DONE;
}
return NOTIFY_OK;
}
/*used for sysctl at "/proc/sys/kernel/hung_task_timeout_secs"*/
void fetch_task_timeout_secs(unsigned long new_sysctl_hung_task_timeout_secs)
{
if (new_sysctl_hung_task_timeout_secs > CONFIG_DEFAULT_HUNG_TASK_TIMEOUT
|| (new_sysctl_hung_task_timeout_secs % HEARTBEAT_TIME))
return;
huawei_hung_task_timeout_secs = new_sysctl_hung_task_timeout_secs;
/*if user change panic timeout value, we sync it to dump value
defaultly, user can set it diffrently*/
whitelist_panic_cnt = (int)(huawei_hung_task_timeout_secs / HEARTBEAT_TIME);
if (whitelist_panic_cnt > THIRTY_SECONDS) {
whitelist_dump_cnt = whitelist_panic_cnt / HUNGTASK_DUMP_IN_PANIC_LOOSE;
} else {
whitelist_dump_cnt = whitelist_panic_cnt / HUNGTASK_DUMP_IN_PANIC_STRICT;
}
applist_dump_cnt = whitelist_dump_cnt;
topapp_hung_cnt = whitelist_dump_cnt;
}
void fetch_hung_task_panic(int new_did_panic)
{
did_panic = new_did_panic;
}
/*used as main thread of khungtaskd*/
static struct kobj_attribute watchdog_nosched_attribute = {
.attr = {
.name = "vm_heart",
.mode = 0640,
},
.show = watchdog_nosched_show,
.store = watchdog_nosched_store,
};
static struct kobj_attribute timeout_attribute = {
.attr = {
.name = "enable",
.mode = 0640,
},
.show = enable_show,
.store = enable_store,
};
static struct kobj_attribute monitorlist_attr = {
.attr = {
.name = "monitorlist",
.mode = 0640,
},
.show = monitorlist_show,
.store = monitorlist_store,
};
static struct attribute *attrs[] = {
&timeout_attribute.attr,
&monitorlist_attr.attr,
&watchdog_nosched_attribute.attr,
NULL
};
static struct attribute_group hungtask_attr_group = {
.attrs = attrs,
};
struct kobject *hungtask_kobj;
int create_sysfs_hungtask(void)
{
int retval = 0;
while (kernel_kobj == NULL)
msleep(1000);
/*Create kobject named "hungtask" located at /sys/kernel/huangtask */
hungtask_kobj = kobject_create_and_add("hungtask", kernel_kobj);
if (!hungtask_kobj)
return -ENOMEM;
retval = sysfs_create_group(hungtask_kobj, &hungtask_attr_group);
if (retval)
kobject_put(hungtask_kobj);
pm_event.notifier_call = pm_sr_event;
pm_event.priority = -1;
if (register_pm_notifier(&pm_event))
{
pr_err("hungtask: register pm notifier failed\n");
return -EFAULT;
}
return retval;
}
int get_fg_pid(void)
{
return topapp_pid;
}
EXPORT_SYMBOL(get_fg_pid);
/*all parameters located under "/sys/module/huawei_hung_task/parameters/"*/
module_param_array_named(jankproc_pids, jankproc_pids, int,
&jankproc_pids_size, (S_IRUGO | S_IWUSR));
MODULE_PARM_DESC(jankproc_pids, "jankproc_pids state");
module_param_named(topapp_pid, topapp_pid, int, (S_IRUGO | S_IWUSR));