diff options
| author | Peter Zijlstra <peterz@infradead.org> | 2011-06-22 05:20:09 -0400 |
|---|---|---|
| committer | Ingo Molnar <mingo@elte.hu> | 2011-06-22 05:39:34 -0400 |
| commit | 4f2a8d3cf5e0486fd547633fa86c5d130ae98cad (patch) | |
| tree | 4f60a635565fd63473e5c23f45745b3b502a1d52 /kernel | |
| parent | 0b5e1c5255e7ee8670e077e8224e5c2281229a5b (diff) | |
printk: Fix console_sem vs logbuf_lock unlock race
Fix up the fallout from commit 0b5e1c5255 ("printk: Release
console_sem after logbuf_lock").
The reason for unlocking the console_sem under the logbuf_lock
is that a concurrent printk() might fill up the buffer but fail
to acquire the console sem, resulting in a missed write to the
console until a subsequent console_sem acquire/release cycle.
Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: efault@gmx.de
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Andrew Morton <akpm@linux-foundation.org>
Link: http://lkml.kernel.org/r/1308734409.1022.14.camel@twins
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/printk.c | 20 |
1 files changed, 18 insertions, 2 deletions
diff --git a/kernel/printk.c b/kernel/printk.c index 751e7b84e9e3..37dff3429adb 100644 --- a/kernel/printk.c +++ b/kernel/printk.c | |||
| @@ -1244,7 +1244,7 @@ void console_unlock(void) | |||
| 1244 | { | 1244 | { |
| 1245 | unsigned long flags; | 1245 | unsigned long flags; |
| 1246 | unsigned _con_start, _log_end; | 1246 | unsigned _con_start, _log_end; |
| 1247 | unsigned wake_klogd = 0; | 1247 | unsigned wake_klogd = 0, retry = 0; |
| 1248 | 1248 | ||
| 1249 | if (console_suspended) { | 1249 | if (console_suspended) { |
| 1250 | up(&console_sem); | 1250 | up(&console_sem); |
| @@ -1253,6 +1253,7 @@ void console_unlock(void) | |||
| 1253 | 1253 | ||
| 1254 | console_may_schedule = 0; | 1254 | console_may_schedule = 0; |
| 1255 | 1255 | ||
| 1256 | again: | ||
| 1256 | for ( ; ; ) { | 1257 | for ( ; ; ) { |
| 1257 | spin_lock_irqsave(&logbuf_lock, flags); | 1258 | spin_lock_irqsave(&logbuf_lock, flags); |
| 1258 | wake_klogd |= log_start - log_end; | 1259 | wake_klogd |= log_start - log_end; |
| @@ -1273,8 +1274,23 @@ void console_unlock(void) | |||
| 1273 | if (unlikely(exclusive_console)) | 1274 | if (unlikely(exclusive_console)) |
| 1274 | exclusive_console = NULL; | 1275 | exclusive_console = NULL; |
| 1275 | 1276 | ||
| 1276 | spin_unlock_irqrestore(&logbuf_lock, flags); | 1277 | spin_unlock(&logbuf_lock); |
| 1278 | |||
| 1277 | up(&console_sem); | 1279 | up(&console_sem); |
| 1280 | |||
| 1281 | /* | ||
| 1282 | * Someone could have filled up the buffer again, so re-check if there's | ||
| 1283 | * something to flush. In case we cannot trylock the console_sem again, | ||
| 1284 | * there's a new owner and the console_unlock() from them will do the | ||
| 1285 | * flush, no worries. | ||
| 1286 | */ | ||
| 1287 | spin_lock(&logbuf_lock); | ||
| 1288 | if (con_start != log_end) | ||
| 1289 | retry = 1; | ||
| 1290 | spin_unlock_irqrestore(&logbuf_lock, flags); | ||
| 1291 | if (retry && console_trylock()) | ||
| 1292 | goto again; | ||
| 1293 | |||
| 1278 | if (wake_klogd) | 1294 | if (wake_klogd) |
| 1279 | wake_up_klogd(); | 1295 | wake_up_klogd(); |
| 1280 | } | 1296 | } |
