diff options
author | Huang Ying <ying.huang@intel.com> | 2009-02-12 07:39:34 -0500 |
---|---|---|
committer | H. Peter Anvin <hpa@linux.intel.com> | 2009-02-17 18:33:05 -0500 |
commit | ef41df4344ff952c79746d44a6126bd2cf7ed2bc (patch) | |
tree | a0756296c6f25e59a680bdc505233d611fd1f56e /arch/x86/kernel/cpu | |
parent | d6b75584a3eaab8cb2ab3e8cf90c5e57c1928a85 (diff) |
x86, mce: fix a race condition in mce_read()
Impact: bugfix
Considering the situation as follow:
before: mcelog.next == 1, mcelog.entry[0].finished = 1
+--------------------------------------------------------------------------
R W1 W2 W3
read mcelog.next (1)
mcelog.next++ (2)
(working on entry 1,
finished == 0)
mcelog.next = 0
mcelog.next++ (1)
(working on entry 0)
mcelog.next++ (2)
(working on entry 1)
<----------------- race ---------------->
(done on entry 1,
finished = 1)
(done on entry 1,
finished = 1)
To fix the race condition, a cmpxchg loop is added to mce_read() to
ensure no new MCE record can be added between mcelog.next reading and
mcelog.next = 0.
Signed-off-by: Huang Ying <ying.huang@intel.com>
Signed-off-by: Andi Kleen <ak@linux.intel.com>
Acked-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: H. Peter Anvin <hpa@zytor.com>
Diffstat (limited to 'arch/x86/kernel/cpu')
-rw-r--r-- | arch/x86/kernel/cpu/mcheck/mce_64.c | 41 |
1 files changed, 24 insertions, 17 deletions
diff --git a/arch/x86/kernel/cpu/mcheck/mce_64.c b/arch/x86/kernel/cpu/mcheck/mce_64.c index 1db94c0d5aaf..870d08deccf7 100644 --- a/arch/x86/kernel/cpu/mcheck/mce_64.c +++ b/arch/x86/kernel/cpu/mcheck/mce_64.c | |||
@@ -595,7 +595,7 @@ static ssize_t mce_read(struct file *filp, char __user *ubuf, size_t usize, | |||
595 | { | 595 | { |
596 | unsigned long *cpu_tsc; | 596 | unsigned long *cpu_tsc; |
597 | static DEFINE_MUTEX(mce_read_mutex); | 597 | static DEFINE_MUTEX(mce_read_mutex); |
598 | unsigned next; | 598 | unsigned prev, next; |
599 | char __user *buf = ubuf; | 599 | char __user *buf = ubuf; |
600 | int i, err; | 600 | int i, err; |
601 | 601 | ||
@@ -614,25 +614,32 @@ static ssize_t mce_read(struct file *filp, char __user *ubuf, size_t usize, | |||
614 | } | 614 | } |
615 | 615 | ||
616 | err = 0; | 616 | err = 0; |
617 | for (i = 0; i < next; i++) { | 617 | prev = 0; |
618 | unsigned long start = jiffies; | 618 | do { |
619 | 619 | for (i = prev; i < next; i++) { | |
620 | while (!mcelog.entry[i].finished) { | 620 | unsigned long start = jiffies; |
621 | if (time_after_eq(jiffies, start + 2)) { | 621 | |
622 | memset(mcelog.entry + i,0, sizeof(struct mce)); | 622 | while (!mcelog.entry[i].finished) { |
623 | goto timeout; | 623 | if (time_after_eq(jiffies, start + 2)) { |
624 | memset(mcelog.entry + i, 0, | ||
625 | sizeof(struct mce)); | ||
626 | goto timeout; | ||
627 | } | ||
628 | cpu_relax(); | ||
624 | } | 629 | } |
625 | cpu_relax(); | 630 | smp_rmb(); |
631 | err |= copy_to_user(buf, mcelog.entry + i, | ||
632 | sizeof(struct mce)); | ||
633 | buf += sizeof(struct mce); | ||
634 | timeout: | ||
635 | ; | ||
626 | } | 636 | } |
627 | smp_rmb(); | ||
628 | err |= copy_to_user(buf, mcelog.entry + i, sizeof(struct mce)); | ||
629 | buf += sizeof(struct mce); | ||
630 | timeout: | ||
631 | ; | ||
632 | } | ||
633 | 637 | ||
634 | memset(mcelog.entry, 0, next * sizeof(struct mce)); | 638 | memset(mcelog.entry + prev, 0, |
635 | mcelog.next = 0; | 639 | (next - prev) * sizeof(struct mce)); |
640 | prev = next; | ||
641 | next = cmpxchg(&mcelog.next, prev, 0); | ||
642 | } while (next != prev); | ||
636 | 643 | ||
637 | synchronize_sched(); | 644 | synchronize_sched(); |
638 | 645 | ||