aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/kernel/cpu
diff options
context:
space:
mode:
authorHuang Ying <ying.huang@intel.com>2009-02-12 07:39:34 -0500
committerH. Peter Anvin <hpa@linux.intel.com>2009-02-17 18:33:05 -0500
commitef41df4344ff952c79746d44a6126bd2cf7ed2bc (patch)
treea0756296c6f25e59a680bdc505233d611fd1f56e /arch/x86/kernel/cpu
parentd6b75584a3eaab8cb2ab3e8cf90c5e57c1928a85 (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.c41
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);
634timeout:
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