From 18e29a19202f8d06e023f33d3948d6b07c24de82 Mon Sep 17 00:00:00 2001 From: Imran Khan Date: Mon, 18 Sep 2017 15:48:30 +0530 Subject: [PATCH] hung task: check specific tasks for long uninterruptible sleep state khungtask by default monitors all tasks for long unterruptible sleep. This change introduces a sysctl option, /proc/sys/kernel/ hung_task_selective_monitoring, to enable monitoring selected tasks. If this sysctl option is enabled then only the tasks with /proc/$PID/hang_detection_enabled set are to be monitored, otherwise all tasks are monitored as default case. Some tasks may intentionally moves to uninterruptable sleep state, which shouldn't leads to khungtask panics, as those are recoverable hungs. So to avoid false hung reports, add an option to select tasks to be monitored and report/panic them only. By default, enable the feature always to monitor selected tasks. Change-Id: I48cd8cfe73dbe2b577541fe9607190eac5556bb2 Signed-off-by: Imran Khan Signed-off-by: Lingutla Chandrasekhar [sramana: Resolved minor merge conflict] Signed-off-by: Srinivas Ramana --- fs/proc/base.c | 58 ++++++++++++++++++++++++++++++++++-- include/linux/sched.h | 2 ++ include/linux/sched/sysctl.h | 1 + kernel/hung_task.c | 14 ++++++++- kernel/sysctl.c | 10 +++++++ 5 files changed, 82 insertions(+), 3 deletions(-) diff --git a/fs/proc/base.c b/fs/proc/base.c index b011d7008f98..55cffe1c43f1 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -2644,7 +2644,7 @@ static struct dentry *proc_pident_instantiate(struct dentry *dentry, return d_splice_alias(inode, dentry); } -static struct dentry *proc_pident_lookup(struct inode *dir, +static struct dentry *proc_pident_lookup(struct inode *dir, struct dentry *dentry, const struct pid_entry *ents, unsigned int nents) @@ -2791,7 +2791,7 @@ static const struct pid_entry attr_dir_stuff[] = { static int proc_attr_dir_readdir(struct file *file, struct dir_context *ctx) { - return proc_pident_readdir(file, ctx, + return proc_pident_readdir(file, ctx, attr_dir_stuff, ARRAY_SIZE(attr_dir_stuff)); } @@ -2953,6 +2953,52 @@ static int proc_tgid_io_accounting(struct seq_file *m, struct pid_namespace *ns, } #endif /* CONFIG_TASK_IO_ACCOUNTING */ +#ifdef CONFIG_DETECT_HUNG_TASK +static ssize_t proc_hung_task_detection_enabled_read(struct file *file, + char __user *buf, size_t count, loff_t *ppos) +{ + struct task_struct *task = get_proc_task(file_inode(file)); + char buffer[PROC_NUMBUF]; + size_t len; + bool hang_detection_enabled; + + if (!task) + return -ESRCH; + hang_detection_enabled = task->hang_detection_enabled; + put_task_struct(task); + + len = snprintf(buffer, sizeof(buffer), "%d\n", hang_detection_enabled); + + return simple_read_from_buffer(buf, sizeof(buffer), ppos, buffer, len); +} + +static ssize_t proc_hung_task_detection_enabled_write(struct file *file, + const char __user *buf, size_t count, loff_t *ppos) +{ + struct task_struct *task; + bool hang_detection_enabled; + int rv; + + rv = kstrtobool_from_user(buf, count, &hang_detection_enabled); + if (rv < 0) + return rv; + + task = get_proc_task(file_inode(file)); + if (!task) + return -ESRCH; + task->hang_detection_enabled = hang_detection_enabled; + put_task_struct(task); + + return count; +} + +static const struct file_operations proc_hung_task_detection_enabled_operations = { + .read = proc_hung_task_detection_enabled_read, + .write = proc_hung_task_detection_enabled_write, + .llseek = generic_file_llseek, +}; +#endif + static ssize_t proc_sched_task_boost_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { @@ -3315,6 +3361,10 @@ static const struct pid_entry tgid_base_stuff[] = { #ifdef CONFIG_TASK_IO_ACCOUNTING ONE("io", S_IRUSR, proc_tgid_io_accounting), #endif +#ifdef CONFIG_DETECT_HUNG_TASK + REG("hang_detection_enabled", 0666, + proc_hung_task_detection_enabled_operations), +#endif #ifdef CONFIG_USER_NS REG("uid_map", S_IRUGO|S_IWUSR, proc_uid_map_operations), REG("gid_map", S_IRUGO|S_IWUSR, proc_gid_map_operations), @@ -3700,6 +3750,10 @@ static const struct pid_entry tid_base_stuff[] = { #ifdef CONFIG_TASK_IO_ACCOUNTING ONE("io", S_IRUSR, proc_tid_io_accounting), #endif +#ifdef CONFIG_DETECT_HUNG_TASK + REG("hang_detection_enabled", 0666, + proc_hung_task_detection_enabled_operations), +#endif #ifdef CONFIG_USER_NS REG("uid_map", S_IRUGO|S_IWUSR, proc_uid_map_operations), REG("gid_map", S_IRUGO|S_IWUSR, proc_gid_map_operations), diff --git a/include/linux/sched.h b/include/linux/sched.h index a2c7d1b66865..eceacc284038 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1022,8 +1022,10 @@ struct task_struct { struct sysv_shm sysvshm; #endif #ifdef CONFIG_DETECT_HUNG_TASK + /* hung task detection */ unsigned long last_switch_count; unsigned long last_switch_time; + bool hang_detection_enabled; #endif /* Filesystem information: */ struct fs_struct *fs; diff --git a/include/linux/sched/sysctl.h b/include/linux/sched/sysctl.h index 1ed29742cc4a..3ab6949f96b5 100644 --- a/include/linux/sched/sysctl.h +++ b/include/linux/sched/sysctl.h @@ -12,6 +12,7 @@ extern unsigned int sysctl_hung_task_panic; extern unsigned long sysctl_hung_task_timeout_secs; extern unsigned long sysctl_hung_task_check_interval_secs; extern int sysctl_hung_task_warnings; +extern int sysctl_hung_task_selective_monitoring; extern int proc_dohung_task_timeout_secs(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos); diff --git a/kernel/hung_task.c b/kernel/hung_task.c index 4a9191617076..11f10e0169d0 100644 --- a/kernel/hung_task.c +++ b/kernel/hung_task.c @@ -21,12 +21,21 @@ #include #include +#include /* * The number of tasks checked: */ int __read_mostly sysctl_hung_task_check_count = PID_MAX_LIMIT; +/* + * Selective monitoring of hung tasks. + * + * if set to 1, khungtaskd skips monitoring tasks, which has + * task_struct->hang_detection_enabled value not set, else monitors all tasks. + */ +int sysctl_hung_task_selective_monitoring = 1; + /* * Limit number of tasks checked in a batch. * @@ -193,7 +202,10 @@ static void check_hung_uninterruptible_tasks(unsigned long timeout) } /* use "==" to skip the TASK_KILLABLE tasks waiting on NFS */ if (t->state == TASK_UNINTERRUPTIBLE) - check_hung_task(t, timeout); + /* Check for selective monitoring */ + if (!sysctl_hung_task_selective_monitoring || + t->hang_detection_enabled) + check_hung_task(t, timeout); } unlock: rcu_read_unlock(); diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 13a406b36de7..8497914e5c39 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -1275,6 +1275,16 @@ static struct ctl_table kern_table[] = { .proc_handler = proc_dointvec_minmax, .extra1 = &neg_one, }, + { + .procname = "hung_task_selective_monitoring", + .data = &sysctl_hung_task_selective_monitoring, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec_minmax, + .extra1 = &zero, + .extra2 = &one, + }, + #endif #ifdef CONFIG_RT_MUTEXES {