Commit d0fc63f7 authored by Stuart Bennett's avatar Stuart Bennett Committed by Ingo Molnar

x86 mmiotrace: fix remove_kmmio_fault_pages()

Impact: fix race+crash in mmiotrace

The list manipulation in remove_kmmio_fault_pages() was broken. If more
than one consecutive kmmio_fault_page was re-added during the grace
period between unregister_kmmio_probe() and remove_kmmio_fault_pages(),
the list manipulation failed to remove pages from the release list.

After a second grace period the pages get into rcu_free_kmmio_fault_pages()
and raise a BUG_ON() kernel crash.

The list manipulation is fixed to properly remove pages from the release
list.

This bug has been present from the very beginning of mmiotrace in the
mainline kernel. It was introduced in 0fd0e3da ("x86: mmiotrace full
patch, preview 1");

An urgent fix for Linus. Tested by Stuart (on 32-bit) and Pekka
(on amd and intel 64-bit systems, nouveau and nvidia proprietary).
Signed-off-by: default avatarStuart Bennett <stuart@freedesktop.org>
Signed-off-by: default avatarPekka Paalanen <pq@iki.fi>
LKML-Reference: <20090308202135.34933feb@daedalus.pq.iki.fi>
Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
parent 73bf1b62
...@@ -451,23 +451,24 @@ static void rcu_free_kmmio_fault_pages(struct rcu_head *head) ...@@ -451,23 +451,24 @@ static void rcu_free_kmmio_fault_pages(struct rcu_head *head)
static void remove_kmmio_fault_pages(struct rcu_head *head) static void remove_kmmio_fault_pages(struct rcu_head *head)
{ {
struct kmmio_delayed_release *dr = container_of( struct kmmio_delayed_release *dr =
head, container_of(head, struct kmmio_delayed_release, rcu);
struct kmmio_delayed_release,
rcu);
struct kmmio_fault_page *p = dr->release_list; struct kmmio_fault_page *p = dr->release_list;
struct kmmio_fault_page **prevp = &dr->release_list; struct kmmio_fault_page **prevp = &dr->release_list;
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&kmmio_lock, flags); spin_lock_irqsave(&kmmio_lock, flags);
while (p) { while (p) {
if (!p->count) if (!p->count) {
list_del_rcu(&p->list); list_del_rcu(&p->list);
else
*prevp = p->release_next;
prevp = &p->release_next; prevp = &p->release_next;
} else {
*prevp = p->release_next;
}
p = p->release_next; p = p->release_next;
} }
spin_unlock_irqrestore(&kmmio_lock, flags); spin_unlock_irqrestore(&kmmio_lock, flags);
/* This is the real RCU destroy call. */ /* This is the real RCU destroy call. */
call_rcu(&dr->rcu, rcu_free_kmmio_fault_pages); call_rcu(&dr->rcu, rcu_free_kmmio_fault_pages);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment