diff options
author | Heiko Carstens <heiko.carstens@de.ibm.com> | 2009-03-09 08:31:59 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-03-09 11:13:35 -0400 |
commit | 6d5b5acca9e566515ef3f1ed617e7295c4f94345 (patch) | |
tree | cad7d2487a95bacbf18ca06be3cef96d4dcf87c9 | |
parent | 7a203f3b089be4410fe065dd9927027eade94557 (diff) |
Fix fixpoint divide exception in acct_update_integrals
Frans Pop reported the crash below when running an s390 kernel under Hercules:
Kernel BUG at 000738b4 verbose debug info unavailable!
fixpoint divide exception: 0009 #1! SMP
Modules linked in: nfs lockd nfs_acl sunrpc ctcm fsm tape_34xx
cu3088 tape ccwgroup tape_class ext3 jbd mbcache dm_mirror dm_log dm_snapshot
dm_mod dasd_eckd_mod dasd_mod
CPU: 0 Not tainted 2.6.27.19 #13
Process awk (pid: 2069, task: 0f9ed9b8, ksp: 0f4f7d18)
Krnl PSW : 070c1000 800738b4 (acct_update_integrals+0x4c/0x118)
R:0 T:1 IO:1 EX:1 Key:0 M:1 W:0 P:0 AS:0 CC:1 PM:0
Krnl GPRS: 00000000 000007d0 7fffffff fffff830
00000000 ffffffff 00000002 0f9ed9b8
00000000 00008ca0 00000000 0f9ed9b8
0f9edda4 8007386e 0f4f7ec8 0f4f7e98
Krnl Code: 800738aa: a71807d0 lhi %r1,2000
800738ae: 8c200001 srdl %r2,1
800738b2: 1d21 dr %r2,%r1
>800738b4: 5810d10e l %r1,270(%r13)
800738b8: 1823 lr %r2,%r3
800738ba: 4130f060 la %r3,96(%r15)
800738be: 0de1 basr %r14,%r1
800738c0: 5800f060 l %r0,96(%r15)
Call Trace:
( <000000000004fdea>! blocking_notifier_call_chain+0x1e/0x2c)
<0000000000038502>! do_exit+0x106/0x7c0
<0000000000038c36>! do_group_exit+0x7a/0xb4
<0000000000038c8e>! SyS_exit_group+0x1e/0x30
<0000000000021c28>! sysc_do_restart+0x12/0x16
<0000000077e7e924>! 0x77e7e924
Reason for this is that cpu time accounting usually only happens from
interrupt context, but acct_update_integrals gets also called from
process context with interrupts enabled.
So in acct_update_integrals we may end up with the following scenario:
Between reading tsk->stime/tsk->utime and tsk->acct_timexpd an interrupt
happens which updates accouting values. This causes acct_timexpd to be
greater than the former stime + utime. The subsequent calculation of
dtime = cputime_sub(time, tsk->acct_timexpd);
will be negative and the division performed by
cputime_to_jiffies(dtime)
will generate an exception since the result won't fit into a 32 bit
register.
In order to fix this just always disable interrupts while accessing any
of the accounting values.
Reported by: Frans Pop <elendil@planet.nl>
Tested by: Frans Pop <elendil@planet.nl>
Cc: stable@kernel.org
Cc: Martin Schwidefsky <schwidefsky@de.ibm.com>
Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | kernel/tsacct.c | 6 |
1 files changed, 5 insertions, 1 deletions
diff --git a/kernel/tsacct.c b/kernel/tsacct.c index 43f891b05a4b..00d59d048edf 100644 --- a/kernel/tsacct.c +++ b/kernel/tsacct.c | |||
@@ -122,8 +122,10 @@ void acct_update_integrals(struct task_struct *tsk) | |||
122 | if (likely(tsk->mm)) { | 122 | if (likely(tsk->mm)) { |
123 | cputime_t time, dtime; | 123 | cputime_t time, dtime; |
124 | struct timeval value; | 124 | struct timeval value; |
125 | unsigned long flags; | ||
125 | u64 delta; | 126 | u64 delta; |
126 | 127 | ||
128 | local_irq_save(flags); | ||
127 | time = tsk->stime + tsk->utime; | 129 | time = tsk->stime + tsk->utime; |
128 | dtime = cputime_sub(time, tsk->acct_timexpd); | 130 | dtime = cputime_sub(time, tsk->acct_timexpd); |
129 | jiffies_to_timeval(cputime_to_jiffies(dtime), &value); | 131 | jiffies_to_timeval(cputime_to_jiffies(dtime), &value); |
@@ -131,10 +133,12 @@ void acct_update_integrals(struct task_struct *tsk) | |||
131 | delta = delta * USEC_PER_SEC + value.tv_usec; | 133 | delta = delta * USEC_PER_SEC + value.tv_usec; |
132 | 134 | ||
133 | if (delta == 0) | 135 | if (delta == 0) |
134 | return; | 136 | goto out; |
135 | tsk->acct_timexpd = time; | 137 | tsk->acct_timexpd = time; |
136 | tsk->acct_rss_mem1 += delta * get_mm_rss(tsk->mm); | 138 | tsk->acct_rss_mem1 += delta * get_mm_rss(tsk->mm); |
137 | tsk->acct_vm_mem1 += delta * tsk->mm->total_vm; | 139 | tsk->acct_vm_mem1 += delta * tsk->mm->total_vm; |
140 | out: | ||
141 | local_irq_restore(flags); | ||
138 | } | 142 | } |
139 | } | 143 | } |
140 | 144 | ||