aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMandeep Singh Baines <msb@google.com>2009-01-17 13:31:48 -0500
committerIngo Molnar <mingo@elte.hu>2009-01-18 13:20:17 -0500
commit603a148f434742fff08273207ffa44176cad13a1 (patch)
treee0c051efd07a7b51390e13169f4c78a29c676fc1
parentaf432eb1cc3178ec7109aca2283aafb1c12ccac1 (diff)
softlockup: fix potential race in hung_task when resetting timeout
Impact: fix potential false panic A potential race exists if sysctl_hung_task_timeout_secs is reset to 0 while inside check_hung_uniterruptible_tasks(). If check_task() is entered, a comparison with 0 will result in a false hung_task being detected. If sysctl_hung_task_panic is set, the system will panic. Signed-off-by: Mandeep Singh Baines <msb@google.com> Signed-off-by: Ingo Molnar <mingo@elte.hu>
-rw-r--r--kernel/hung_task.c24
1 files changed, 16 insertions, 8 deletions
diff --git a/kernel/hung_task.c b/kernel/hung_task.c
index ba5a77cad3bb..ba8ccd432963 100644
--- a/kernel/hung_task.c
+++ b/kernel/hung_task.c
@@ -72,7 +72,8 @@ static unsigned long get_timestamp(void)
72 return cpu_clock(this_cpu) >> 30LL; /* 2^30 ~= 10^9 */ 72 return cpu_clock(this_cpu) >> 30LL; /* 2^30 ~= 10^9 */
73} 73}
74 74
75static void check_hung_task(struct task_struct *t, unsigned long now) 75static void check_hung_task(struct task_struct *t, unsigned long now,
76 unsigned long timeout)
76{ 77{
77 unsigned long switch_count = t->nvcsw + t->nivcsw; 78 unsigned long switch_count = t->nvcsw + t->nivcsw;
78 79
@@ -84,8 +85,7 @@ static void check_hung_task(struct task_struct *t, unsigned long now)
84 t->last_switch_timestamp = now; 85 t->last_switch_timestamp = now;
85 return; 86 return;
86 } 87 }
87 if ((long)(now - t->last_switch_timestamp) < 88 if ((long)(now - t->last_switch_timestamp) < timeout)
88 sysctl_hung_task_timeout_secs)
89 return; 89 return;
90 if (!sysctl_hung_task_warnings) 90 if (!sysctl_hung_task_warnings)
91 return; 91 return;
@@ -96,8 +96,7 @@ static void check_hung_task(struct task_struct *t, unsigned long now)
96 * complain: 96 * complain:
97 */ 97 */
98 printk(KERN_ERR "INFO: task %s:%d blocked for more than " 98 printk(KERN_ERR "INFO: task %s:%d blocked for more than "
99 "%ld seconds.\n", t->comm, t->pid, 99 "%ld seconds.\n", t->comm, t->pid, timeout);
100 sysctl_hung_task_timeout_secs);
101 printk(KERN_ERR "\"echo 0 > /proc/sys/kernel/hung_task_timeout_secs\"" 100 printk(KERN_ERR "\"echo 0 > /proc/sys/kernel/hung_task_timeout_secs\""
102 " disables this message.\n"); 101 " disables this message.\n");
103 sched_show_task(t); 102 sched_show_task(t);
@@ -115,7 +114,7 @@ static void check_hung_task(struct task_struct *t, unsigned long now)
115 * a really long time (120 seconds). If that happens, print out 114 * a really long time (120 seconds). If that happens, print out
116 * a warning. 115 * a warning.
117 */ 116 */
118static void check_hung_uninterruptible_tasks(void) 117static void check_hung_uninterruptible_tasks(unsigned long timeout)
119{ 118{
120 int max_count = sysctl_hung_task_check_count; 119 int max_count = sysctl_hung_task_check_count;
121 unsigned long now = get_timestamp(); 120 unsigned long now = get_timestamp();
@@ -134,7 +133,7 @@ static void check_hung_uninterruptible_tasks(void)
134 goto unlock; 133 goto unlock;
135 /* use "==" to skip the TASK_KILLABLE tasks waiting on NFS */ 134 /* use "==" to skip the TASK_KILLABLE tasks waiting on NFS */
136 if (t->state == TASK_UNINTERRUPTIBLE) 135 if (t->state == TASK_UNINTERRUPTIBLE)
137 check_hung_task(t, now); 136 check_hung_task(t, now, timeout);
138 } while_each_thread(g, t); 137 } while_each_thread(g, t);
139 unlock: 138 unlock:
140 read_unlock(&tasklist_lock); 139 read_unlock(&tasklist_lock);
@@ -180,8 +179,17 @@ static int watchdog(void *dummy)
180 update_poll_jiffies(); 179 update_poll_jiffies();
181 180
182 for ( ; ; ) { 181 for ( ; ; ) {
182 unsigned long timeout;
183
183 while (schedule_timeout_interruptible(hung_task_poll_jiffies)); 184 while (schedule_timeout_interruptible(hung_task_poll_jiffies));
184 check_hung_uninterruptible_tasks(); 185
186 /*
187 * Need to cache timeout here to avoid timeout being set
188 * to 0 via sysctl while inside check_hung_*_tasks().
189 */
190 timeout = sysctl_hung_task_timeout_secs;
191 if (timeout)
192 check_hung_uninterruptible_tasks(timeout);
185 } 193 }
186 194
187 return 0; 195 return 0;