123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * Copyright (c) 2021, The Linux Foundation. All rights reserved.
- * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
- */
- #include <linux/init.h>
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/syscore_ops.h>
- #include <linux/sched/walt.h>
- #include <trace/hooks/hung_task.h>
- #define DEFAULT_MAX_IOWAIT_TASK 5
- enum {
- TASK_DEFAULT = 0,
- TASK_IN_WHITELIST,
- TASK_IN_BLACKLIST,
- };
- enum {
- HUNG_TASK_MODE_WHITELIST = 0,
- HUNG_TASK_MODE_BLACKLIST,
- };
- /**
- * struct hung_task_enh_data
- * @read_pid: PID of the process will be read
- * @global_detect_mode: The global detect mode switch, 0-Whitelist/1-Blacklist
- * @curr_iowait_task_cnt: Count the number of iowait processes in one scan cycle
- * @max_iowait_task_cnt: Max number of iowait processes
- * @curr_iowait_timeout_cnt: Count how many times the iowait condition is met
- * @max_iowait_timeout_cnt: Max times of the iowait condition is met
- * @ctl_table_hdr: hung_task_enh ctl table header
- */
- struct hung_task_enh_data {
- int read_pid;
- int global_detect_mode;
- int curr_iowait_task_cnt;
- int max_iowait_task_cnt;
- int curr_iowait_timeout_cnt;
- int max_iowait_timeout_cnt;
- struct ctl_table_header *ctl_table_hdr;
- };
- static struct hung_task_enh_data hung_task_enh;
- void qcom_before_check_tasks(void *ignore, struct task_struct *t, unsigned long timeout,
- bool *need_check)
- {
- struct walt_task_struct *wts = (struct walt_task_struct *) t->android_vendor_data1;
- if ((hung_task_enh.global_detect_mode == HUNG_TASK_MODE_WHITELIST &&
- wts->hung_detect_status != TASK_IN_WHITELIST) ||
- (hung_task_enh.global_detect_mode == HUNG_TASK_MODE_BLACKLIST &&
- wts->hung_detect_status == TASK_IN_BLACKLIST)) {
- *need_check = false;
- return;
- }
- *need_check = true;
- if (unlikely(t->in_iowait) && (t->__state == TASK_UNINTERRUPTIBLE ||
- t->__state == TASK_STOPPED || t->__state == TASK_TRACED) &&
- t->last_switch_time != 0 &&
- time_is_before_jiffies(t->last_switch_time + timeout * HZ) &&
- (t->mm != NULL && t == t->group_leader))
- hung_task_enh.curr_iowait_task_cnt++;
- }
- void qcom_check_tasks_done(void *ignore, void *extra)
- {
- if (hung_task_enh.curr_iowait_task_cnt >= hung_task_enh.max_iowait_task_cnt)
- hung_task_enh.curr_iowait_timeout_cnt++;
- else
- hung_task_enh.curr_iowait_timeout_cnt = 0;
- if (hung_task_enh.max_iowait_timeout_cnt != 0 &&
- hung_task_enh.curr_iowait_timeout_cnt >= hung_task_enh.max_iowait_timeout_cnt)
- panic("Detect IO wait too long time for multiple tasks!\n");
- hung_task_enh.curr_iowait_task_cnt = 0;
- }
- static DEFINE_MUTEX(readpid_mutex);
- static int read_pid_handler(struct ctl_table *table, int write,
- void __user *buffer, size_t *lenp, loff_t *ppos)
- {
- int ret;
- mutex_lock(&readpid_mutex);
- ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
- mutex_unlock(&readpid_mutex);
- return ret;
- }
- static int hung_task_handler(struct ctl_table *table, int write,
- void __user *buffer, size_t *lenp, loff_t *ppos)
- {
- int ret;
- struct task_struct *task;
- struct walt_task_struct *wts;
- int pid_and_val[2] = {-1, -1};
- struct ctl_table tmp = {
- .data = &pid_and_val,
- .maxlen = sizeof(pid_and_val),
- .mode = table->mode,
- };
- mutex_lock(&readpid_mutex);
- if (!write) {
- task = get_pid_task(find_vpid(hung_task_enh.read_pid),
- PIDTYPE_PID);
- if (!task) {
- ret = -ENOENT;
- goto unlock_mutex;
- }
- wts = (struct walt_task_struct *) task->android_vendor_data1;
- pid_and_val[0] = hung_task_enh.read_pid;
- pid_and_val[1] = wts->hung_detect_status;
- ret = proc_dointvec(&tmp, write, buffer, lenp, ppos);
- goto put_task;
- }
- ret = proc_dointvec(&tmp, write, buffer, lenp, ppos);
- if (ret)
- goto unlock_mutex;
- if (pid_and_val[0] <= 0 || pid_and_val[1] < 0 || pid_and_val[1] > 1) {
- ret = -ENOENT;
- goto unlock_mutex;
- }
- task = get_pid_task(find_vpid(pid_and_val[0]), PIDTYPE_PID);
- if (!task) {
- ret = -ENOENT;
- goto unlock_mutex;
- }
- wts = (struct walt_task_struct *) task->android_vendor_data1;
- if (pid_and_val[1] == 1) {
- if (hung_task_enh.global_detect_mode == HUNG_TASK_MODE_WHITELIST)
- wts->hung_detect_status = TASK_IN_WHITELIST;
- else
- wts->hung_detect_status = TASK_IN_BLACKLIST;
- } else {
- wts->hung_detect_status = TASK_DEFAULT;
- }
- put_task:
- put_task_struct(task);
- unlock_mutex:
- mutex_unlock(&readpid_mutex);
- return ret;
- }
- struct ctl_table hung_task_table[] = {
- {
- .procname = "global_detect_mode",
- .data = &hung_task_enh.global_detect_mode,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec_minmax,
- .extra1 = SYSCTL_ZERO,
- .extra2 = SYSCTL_ONE,
- },
- {
- .procname = "per_task_detect_mode",
- .data = (int *) 0,
- .maxlen = sizeof(unsigned int) * 2,
- .mode = 0644,
- .proc_handler = hung_task_handler,
- },
- {
- .procname = "max_iowait_timeout_cnt",
- .data = &hung_task_enh.max_iowait_timeout_cnt,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec_minmax,
- .extra1 = SYSCTL_ZERO,
- .extra2 = SYSCTL_INT_MAX,
- },
- {
- .procname = "max_iowait_task_cnt",
- .data = &hung_task_enh.max_iowait_task_cnt,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec_minmax,
- .extra1 = SYSCTL_ONE,
- .extra2 = SYSCTL_INT_MAX,
- },
- {
- .procname = "read_pid",
- .data = &hung_task_enh.read_pid,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = read_pid_handler,
- .extra1 = SYSCTL_ONE,
- .extra2 = SYSCTL_INT_MAX,
- },
- { }
- };
- struct ctl_table hung_task_base_table[] = {
- {
- .procname = "hung_task_enh",
- .mode = 0555,
- .child = hung_task_table,
- },
- { }
- };
- static int __init hung_task_enh_init(void)
- {
- int ret;
- hung_task_enh.max_iowait_task_cnt = DEFAULT_MAX_IOWAIT_TASK;
- ret = register_trace_android_vh_check_uninterrupt_tasks(
- qcom_before_check_tasks, NULL);
- if (ret)
- return ret;
- ret = register_trace_android_vh_check_uninterrupt_tasks_done(
- qcom_check_tasks_done, NULL);
- if (ret) {
- unregister_trace_android_vh_check_uninterrupt_tasks(
- qcom_before_check_tasks, NULL);
- return ret;
- }
- hung_task_enh.ctl_table_hdr = register_sysctl_table(
- hung_task_base_table);
- if (!hung_task_enh.ctl_table_hdr) {
- unregister_trace_android_vh_check_uninterrupt_tasks(
- qcom_before_check_tasks, NULL);
- unregister_trace_android_vh_check_uninterrupt_tasks_done(
- qcom_check_tasks_done, NULL);
- return -ENOMEM;
- }
- return ret;
- }
- late_initcall(hung_task_enh_init);
- static void __exit hung_task_enh_exit(void)
- {
- unregister_sysctl_table(hung_task_enh.ctl_table_hdr);
- unregister_trace_android_vh_check_uninterrupt_tasks(
- qcom_before_check_tasks, NULL);
- unregister_trace_android_vh_check_uninterrupt_tasks_done(
- qcom_check_tasks_done, NULL);
- }
- module_exit(hung_task_enh_exit);
- MODULE_DESCRIPTION("QCOM Hung Task Enhancement");
- MODULE_LICENSE("GPL");
|