Skip to content

Commit 7298f08

Browse files
suryasaimadhuKAGA-KOKO
authored andcommitted
x86/mcelog: Get rid of RCU remnants
Jeremy reported a suspicious RCU usage warning in mcelog. /dev/mcelog is called in process context now as part of the notifier chain and doesn't need any of the fancy RCU and lockless accesses which it did in atomic context. Axe it all in favor of a simple mutex synchronization which cures the problem reported. Fixes: 5de97c9 ("x86/mce: Factor out and deprecate the /dev/mcelog driver") Reported-by: Jeremy Cline <[email protected]> Signed-off-by: Borislav Petkov <[email protected]> Signed-off-by: Thomas Gleixner <[email protected]> Reviewed-and-tested-by: Tony Luck <[email protected]> Cc: Andi Kleen <[email protected]> Cc: [email protected] Cc: Laura Abbott <[email protected]> Cc: [email protected] Link: https://lkml.kernel.org/r/[email protected] Link: https://bugzilla.redhat.com/show_bug.cgi?id=1498969
1 parent 287683d commit 7298f08

File tree

1 file changed

+27
-94
lines changed

1 file changed

+27
-94
lines changed

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

Lines changed: 27 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,6 @@ static DEFINE_MUTEX(mce_chrdev_read_mutex);
2424
static char mce_helper[128];
2525
static char *mce_helper_argv[2] = { mce_helper, NULL };
2626

27-
#define mce_log_get_idx_check(p) \
28-
({ \
29-
RCU_LOCKDEP_WARN(!rcu_read_lock_sched_held() && \
30-
!lockdep_is_held(&mce_chrdev_read_mutex), \
31-
"suspicious mce_log_get_idx_check() usage"); \
32-
smp_load_acquire(&(p)); \
33-
})
34-
3527
/*
3628
* Lockless MCE logging infrastructure.
3729
* 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,
5345
void *data)
5446
{
5547
struct mce *mce = (struct mce *)data;
56-
unsigned int next, entry;
57-
58-
wmb();
59-
for (;;) {
60-
entry = mce_log_get_idx_check(mcelog.next);
61-
for (;;) {
62-
63-
/*
64-
* When the buffer fills up discard new entries.
65-
* Assume that the earlier errors are the more
66-
* interesting ones:
67-
*/
68-
if (entry >= MCE_LOG_LEN) {
69-
set_bit(MCE_OVERFLOW,
70-
(unsigned long *)&mcelog.flags);
71-
return NOTIFY_OK;
72-
}
73-
/* Old left over entry. Skip: */
74-
if (mcelog.entry[entry].finished) {
75-
entry++;
76-
continue;
77-
}
78-
break;
79-
}
80-
smp_rmb();
81-
next = entry + 1;
82-
if (cmpxchg(&mcelog.next, entry, next) == entry)
83-
break;
48+
unsigned int entry;
49+
50+
mutex_lock(&mce_chrdev_read_mutex);
51+
52+
entry = mcelog.next;
53+
54+
/*
55+
* When the buffer fills up discard new entries. Assume that the
56+
* earlier errors are the more interesting ones:
57+
*/
58+
if (entry >= MCE_LOG_LEN) {
59+
set_bit(MCE_OVERFLOW, (unsigned long *)&mcelog.flags);
60+
goto unlock;
8461
}
62+
63+
mcelog.next = entry + 1;
64+
8565
memcpy(mcelog.entry + entry, mce, sizeof(struct mce));
86-
wmb();
8766
mcelog.entry[entry].finished = 1;
88-
wmb();
8967

9068
/* wake processes polling /dev/mcelog */
9169
wake_up_interruptible(&mce_chrdev_wait);
9270

71+
unlock:
72+
mutex_unlock(&mce_chrdev_read_mutex);
73+
9374
return NOTIFY_OK;
9475
}
9576

@@ -177,13 +158,6 @@ static int mce_chrdev_release(struct inode *inode, struct file *file)
177158
return 0;
178159
}
179160

180-
static void collect_tscs(void *data)
181-
{
182-
unsigned long *cpu_tsc = (unsigned long *)data;
183-
184-
cpu_tsc[smp_processor_id()] = rdtsc();
185-
}
186-
187161
static int mce_apei_read_done;
188162

189163
/* 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,
231205
size_t usize, loff_t *off)
232206
{
233207
char __user *buf = ubuf;
234-
unsigned long *cpu_tsc;
235-
unsigned prev, next;
208+
unsigned next;
236209
int i, err;
237210

238-
cpu_tsc = kmalloc(nr_cpu_ids * sizeof(long), GFP_KERNEL);
239-
if (!cpu_tsc)
240-
return -ENOMEM;
241-
242211
mutex_lock(&mce_chrdev_read_mutex);
243212

244213
if (!mce_apei_read_done) {
@@ -247,65 +216,29 @@ static ssize_t mce_chrdev_read(struct file *filp, char __user *ubuf,
247216
goto out;
248217
}
249218

250-
next = mce_log_get_idx_check(mcelog.next);
251-
252219
/* Only supports full reads right now */
253220
err = -EINVAL;
254221
if (*off != 0 || usize < MCE_LOG_LEN*sizeof(struct mce))
255222
goto out;
256223

224+
next = mcelog.next;
257225
err = 0;
258-
prev = 0;
259-
do {
260-
for (i = prev; i < next; i++) {
261-
unsigned long start = jiffies;
262-
struct mce *m = &mcelog.entry[i];
263-
264-
while (!m->finished) {
265-
if (time_after_eq(jiffies, start + 2)) {
266-
memset(m, 0, sizeof(*m));
267-
goto timeout;
268-
}
269-
cpu_relax();
270-
}
271-
smp_rmb();
272-
err |= copy_to_user(buf, m, sizeof(*m));
273-
buf += sizeof(*m);
274-
timeout:
275-
;
276-
}
277-
278-
memset(mcelog.entry + prev, 0,
279-
(next - prev) * sizeof(struct mce));
280-
prev = next;
281-
next = cmpxchg(&mcelog.next, prev, 0);
282-
} while (next != prev);
283-
284-
synchronize_sched();
285226

286-
/*
287-
* Collect entries that were still getting written before the
288-
* synchronize.
289-
*/
290-
on_each_cpu(collect_tscs, cpu_tsc, 1);
291-
292-
for (i = next; i < MCE_LOG_LEN; i++) {
227+
for (i = 0; i < next; i++) {
293228
struct mce *m = &mcelog.entry[i];
294229

295-
if (m->finished && m->tsc < cpu_tsc[m->cpu]) {
296-
err |= copy_to_user(buf, m, sizeof(*m));
297-
smp_rmb();
298-
buf += sizeof(*m);
299-
memset(m, 0, sizeof(*m));
300-
}
230+
err |= copy_to_user(buf, m, sizeof(*m));
231+
buf += sizeof(*m);
301232
}
302233

234+
memset(mcelog.entry, 0, next * sizeof(struct mce));
235+
mcelog.next = 0;
236+
303237
if (err)
304238
err = -EFAULT;
305239

306240
out:
307241
mutex_unlock(&mce_chrdev_read_mutex);
308-
kfree(cpu_tsc);
309242

310243
return err ? err : buf - ubuf;
311244
}

0 commit comments

Comments
 (0)