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 {