Merge branch 'ras-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
authorLinus Torvalds <torvalds@linux-foundation.org>
Sun, 5 Nov 2017 20:12:51 +0000 (12:12 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sun, 5 Nov 2017 20:12:51 +0000 (12:12 -0800)
Pull RAS fix from Ingo Molnar:
 "Fix an RCU warning that triggers when /dev/mcelog is used"

* 'ras-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  x86/mcelog: Get rid of RCU remnants

arch/x86/kernel/cpu/mcheck/dev-mcelog.c

index 10cec43aac389790e591f76be31de5cda0780c1c..7f85b76f43bcc622aa537e3bc40a35860f316479 100644 (file)
@@ -24,14 +24,6 @@ static DEFINE_MUTEX(mce_chrdev_read_mutex);
 static char mce_helper[128];
 static char *mce_helper_argv[2] = { mce_helper, NULL };
 
-#define mce_log_get_idx_check(p) \
-({ \
-       RCU_LOCKDEP_WARN(!rcu_read_lock_sched_held() && \
-                        !lockdep_is_held(&mce_chrdev_read_mutex), \
-                        "suspicious mce_log_get_idx_check() usage"); \
-       smp_load_acquire(&(p)); \
-})
-
 /*
  * Lockless MCE logging infrastructure.
  * This avoids deadlocks on printk locks without having to break locks. Also
@@ -53,43 +45,32 @@ static int dev_mce_log(struct notifier_block *nb, unsigned long val,
                                void *data)
 {
        struct mce *mce = (struct mce *)data;
-       unsigned int next, entry;
-
-       wmb();
-       for (;;) {
-               entry = mce_log_get_idx_check(mcelog.next);
-               for (;;) {
-
-                       /*
-                        * When the buffer fills up discard new entries.
-                        * Assume that the earlier errors are the more
-                        * interesting ones:
-                        */
-                       if (entry >= MCE_LOG_LEN) {
-                               set_bit(MCE_OVERFLOW,
-                                       (unsigned long *)&mcelog.flags);
-                               return NOTIFY_OK;
-                       }
-                       /* Old left over entry. Skip: */
-                       if (mcelog.entry[entry].finished) {
-                               entry++;
-                               continue;
-                       }
-                       break;
-               }
-               smp_rmb();
-               next = entry + 1;
-               if (cmpxchg(&mcelog.next, entry, next) == entry)
-                       break;
+       unsigned int entry;
+
+       mutex_lock(&mce_chrdev_read_mutex);
+
+       entry = mcelog.next;
+
+       /*
+        * When the buffer fills up discard new entries. Assume that the
+        * earlier errors are the more interesting ones:
+        */
+       if (entry >= MCE_LOG_LEN) {
+               set_bit(MCE_OVERFLOW, (unsigned long *)&mcelog.flags);
+               goto unlock;
        }
+
+       mcelog.next = entry + 1;
+
        memcpy(mcelog.entry + entry, mce, sizeof(struct mce));
-       wmb();
        mcelog.entry[entry].finished = 1;
-       wmb();
 
        /* wake processes polling /dev/mcelog */
        wake_up_interruptible(&mce_chrdev_wait);
 
+unlock:
+       mutex_unlock(&mce_chrdev_read_mutex);
+
        return NOTIFY_OK;
 }
 
@@ -177,13 +158,6 @@ static int mce_chrdev_release(struct inode *inode, struct file *file)
        return 0;
 }
 
-static void collect_tscs(void *data)
-{
-       unsigned long *cpu_tsc = (unsigned long *)data;
-
-       cpu_tsc[smp_processor_id()] = rdtsc();
-}
-
 static int mce_apei_read_done;
 
 /* Collect MCE record of previous boot in persistent storage via APEI ERST. */
@@ -231,14 +205,9 @@ static ssize_t mce_chrdev_read(struct file *filp, char __user *ubuf,
                                size_t usize, loff_t *off)
 {
        char __user *buf = ubuf;
-       unsigned long *cpu_tsc;
-       unsigned prev, next;
+       unsigned next;
        int i, err;
 
-       cpu_tsc = kmalloc(nr_cpu_ids * sizeof(long), GFP_KERNEL);
-       if (!cpu_tsc)
-               return -ENOMEM;
-
        mutex_lock(&mce_chrdev_read_mutex);
 
        if (!mce_apei_read_done) {
@@ -247,65 +216,29 @@ static ssize_t mce_chrdev_read(struct file *filp, char __user *ubuf,
                        goto out;
        }
 
-       next = mce_log_get_idx_check(mcelog.next);
-
        /* Only supports full reads right now */
        err = -EINVAL;
        if (*off != 0 || usize < MCE_LOG_LEN*sizeof(struct mce))
                goto out;
 
+       next = mcelog.next;
        err = 0;
-       prev = 0;
-       do {
-               for (i = prev; i < next; i++) {
-                       unsigned long start = jiffies;
-                       struct mce *m = &mcelog.entry[i];
-
-                       while (!m->finished) {
-                               if (time_after_eq(jiffies, start + 2)) {
-                                       memset(m, 0, sizeof(*m));
-                                       goto timeout;
-                               }
-                               cpu_relax();
-                       }
-                       smp_rmb();
-                       err |= copy_to_user(buf, m, sizeof(*m));
-                       buf += sizeof(*m);
-timeout:
-                       ;
-               }
-
-               memset(mcelog.entry + prev, 0,
-                      (next - prev) * sizeof(struct mce));
-               prev = next;
-               next = cmpxchg(&mcelog.next, prev, 0);
-       } while (next != prev);
-
-       synchronize_sched();
 
-       /*
-        * Collect entries that were still getting written before the
-        * synchronize.
-        */
-       on_each_cpu(collect_tscs, cpu_tsc, 1);
-
-       for (i = next; i < MCE_LOG_LEN; i++) {
+       for (i = 0; i < next; i++) {
                struct mce *m = &mcelog.entry[i];
 
-               if (m->finished && m->tsc < cpu_tsc[m->cpu]) {
-                       err |= copy_to_user(buf, m, sizeof(*m));
-                       smp_rmb();
-                       buf += sizeof(*m);
-                       memset(m, 0, sizeof(*m));
-               }
+               err |= copy_to_user(buf, m, sizeof(*m));
+               buf += sizeof(*m);
        }
 
+       memset(mcelog.entry, 0, next * sizeof(struct mce));
+       mcelog.next = 0;
+
        if (err)
                err = -EFAULT;
 
 out:
        mutex_unlock(&mce_chrdev_read_mutex);
-       kfree(cpu_tsc);
 
        return err ? err : buf - ubuf;
 }