Commit 15689c3c authored by Ben Skeggs's avatar Ben Skeggs

drm/nouveau/core: split lock into list+exec and enable refcount locks

This fixes a reported locking inversion when interacting with the DRM
core's vblank routines.
Reviewed-by: default avatarMaarten Lankhorst <maarten.lankhorst@canonical.com>
Signed-off-by: default avatarBen Skeggs <bskeggs@redhat.com>
parent 51cb4b39
...@@ -23,27 +23,19 @@ ...@@ -23,27 +23,19 @@
#include <core/os.h> #include <core/os.h>
#include <core/event.h> #include <core/event.h>
static void
nouveau_event_put_locked(struct nouveau_event *event, int index,
struct nouveau_eventh *handler)
{
if (__test_and_clear_bit(NVKM_EVENT_ENABLE, &handler->flags)) {
if (!--event->index[index].refs) {
if (event->disable)
event->disable(event, index);
}
list_del(&handler->head);
}
}
void void
nouveau_event_put(struct nouveau_eventh *handler) nouveau_event_put(struct nouveau_eventh *handler)
{ {
struct nouveau_event *event = handler->event; struct nouveau_event *event = handler->event;
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&event->lock, flags); if (__test_and_clear_bit(NVKM_EVENT_ENABLE, &handler->flags)) {
nouveau_event_put_locked(handler->event, handler->index, handler); spin_lock_irqsave(&event->refs_lock, flags);
spin_unlock_irqrestore(&event->lock, flags); if (!--event->index[handler->index].refs) {
if (event->disable)
event->disable(event, handler->index);
}
spin_unlock_irqrestore(&event->refs_lock, flags);
}
} }
void void
...@@ -51,22 +43,25 @@ nouveau_event_get(struct nouveau_eventh *handler) ...@@ -51,22 +43,25 @@ nouveau_event_get(struct nouveau_eventh *handler)
{ {
struct nouveau_event *event = handler->event; struct nouveau_event *event = handler->event;
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&event->lock, flags);
if (!__test_and_set_bit(NVKM_EVENT_ENABLE, &handler->flags)) { if (!__test_and_set_bit(NVKM_EVENT_ENABLE, &handler->flags)) {
list_add(&handler->head, &event->index[handler->index].list); spin_lock_irqsave(&event->refs_lock, flags);
if (!event->index[handler->index].refs++) { if (!event->index[handler->index].refs++) {
if (event->enable) if (event->enable)
event->enable(event, handler->index); event->enable(event, handler->index);
} }
spin_unlock_irqrestore(&event->refs_lock, flags);
} }
spin_unlock_irqrestore(&event->lock, flags);
} }
static void static void
nouveau_event_fini(struct nouveau_eventh *handler) nouveau_event_fini(struct nouveau_eventh *handler)
{ {
struct nouveau_event *event = handler->event;
unsigned long flags;
nouveau_event_put(handler); nouveau_event_put(handler);
spin_lock_irqsave(&event->list_lock, flags);
list_del(&handler->head);
spin_unlock_irqrestore(&event->list_lock, flags);
} }
static int static int
...@@ -74,13 +69,20 @@ nouveau_event_init(struct nouveau_event *event, int index, ...@@ -74,13 +69,20 @@ nouveau_event_init(struct nouveau_event *event, int index,
int (*func)(void *, int), void *priv, int (*func)(void *, int), void *priv,
struct nouveau_eventh *handler) struct nouveau_eventh *handler)
{ {
unsigned long flags;
if (index >= event->index_nr) if (index >= event->index_nr)
return -EINVAL; return -EINVAL;
handler->event = event; handler->event = event;
handler->flags = 0; handler->flags = 0;
handler->index = index; handler->index = index;
handler->func = func; handler->func = func;
handler->priv = priv; handler->priv = priv;
spin_lock_irqsave(&event->list_lock, flags);
list_add_tail(&handler->head, &event->index[index].list);
spin_unlock_irqrestore(&event->list_lock, flags);
return 0; return 0;
} }
...@@ -116,19 +118,19 @@ nouveau_event_ref(struct nouveau_eventh *handler, struct nouveau_eventh **ref) ...@@ -116,19 +118,19 @@ nouveau_event_ref(struct nouveau_eventh *handler, struct nouveau_eventh **ref)
void void
nouveau_event_trigger(struct nouveau_event *event, int index) nouveau_event_trigger(struct nouveau_event *event, int index)
{ {
struct nouveau_eventh *handler, *temp; struct nouveau_eventh *handler;
unsigned long flags; unsigned long flags;
if (index >= event->index_nr) if (WARN_ON(index >= event->index_nr))
return; return;
spin_lock_irqsave(&event->lock, flags); spin_lock_irqsave(&event->list_lock, flags);
list_for_each_entry_safe(handler, temp, &event->index[index].list, head) { list_for_each_entry(handler, &event->index[index].list, head) {
if (handler->func(handler->priv, index) == NVKM_EVENT_DROP) { if (test_bit(NVKM_EVENT_ENABLE, &handler->flags) &&
nouveau_event_put_locked(event, index, handler); handler->func(handler->priv, index) == NVKM_EVENT_DROP)
} nouveau_event_put(handler);
} }
spin_unlock_irqrestore(&event->lock, flags); spin_unlock_irqrestore(&event->list_lock, flags);
} }
void void
...@@ -152,7 +154,8 @@ nouveau_event_create(int index_nr, struct nouveau_event **pevent) ...@@ -152,7 +154,8 @@ nouveau_event_create(int index_nr, struct nouveau_event **pevent)
if (!event) if (!event)
return -ENOMEM; return -ENOMEM;
spin_lock_init(&event->lock); spin_lock_init(&event->list_lock);
spin_lock_init(&event->refs_lock);
for (i = 0; i < index_nr; i++) for (i = 0; i < index_nr; i++)
INIT_LIST_HEAD(&event->index[i].list); INIT_LIST_HEAD(&event->index[i].list);
event->index_nr = index_nr; event->index_nr = index_nr;
......
...@@ -18,7 +18,8 @@ struct nouveau_eventh { ...@@ -18,7 +18,8 @@ struct nouveau_eventh {
}; };
struct nouveau_event { struct nouveau_event {
spinlock_t lock; spinlock_t list_lock;
spinlock_t refs_lock;
void *priv; void *priv;
void (*enable)(struct nouveau_event *, int index); void (*enable)(struct nouveau_event *, int index);
......
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