summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorBen Skeggs <bskeggs@redhat.com>2013-10-02 23:59:16 (GMT)
committerBen Skeggs <bskeggs@redhat.com>2013-11-08 05:36:06 (GMT)
commit15689c3c819d1d659cff464f427f398758a683de (patch)
treee13de5a68558c7d2a3055a6ce632d0ac0e5d07bc /drivers
parent51cb4b392a307a8293b4f1f300ab803d7ad3b036 (diff)
downloadlinux-fsl-qoriq-15689c3c819d1d659cff464f427f398758a683de.tar.xz
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: Maarten Lankhorst <maarten.lankhorst@canonical.com> Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/gpu/drm/nouveau/core/core/event.c61
-rw-r--r--drivers/gpu/drm/nouveau/core/include/core/event.h3
2 files changed, 34 insertions, 30 deletions
diff --git a/drivers/gpu/drm/nouveau/core/core/event.c b/drivers/gpu/drm/nouveau/core/core/event.c
index 313afcc..3f3c765 100644
--- a/drivers/gpu/drm/nouveau/core/core/event.c
+++ b/drivers/gpu/drm/nouveau/core/core/event.c
@@ -23,27 +23,19 @@
#include <core/os.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
nouveau_event_put(struct nouveau_eventh *handler)
{
struct nouveau_event *event = handler->event;
unsigned long flags;
- spin_lock_irqsave(&event->lock, flags);
- nouveau_event_put_locked(handler->event, handler->index, handler);
- spin_unlock_irqrestore(&event->lock, flags);
+ if (__test_and_clear_bit(NVKM_EVENT_ENABLE, &handler->flags)) {
+ spin_lock_irqsave(&event->refs_lock, flags);
+ if (!--event->index[handler->index].refs) {
+ if (event->disable)
+ event->disable(event, handler->index);
+ }
+ spin_unlock_irqrestore(&event->refs_lock, flags);
+ }
}
void
@@ -51,22 +43,25 @@ nouveau_event_get(struct nouveau_eventh *handler)
{
struct nouveau_event *event = handler->event;
unsigned long flags;
-
- spin_lock_irqsave(&event->lock, 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->enable)
event->enable(event, handler->index);
}
+ spin_unlock_irqrestore(&event->refs_lock, flags);
}
- spin_unlock_irqrestore(&event->lock, flags);
}
static void
nouveau_event_fini(struct nouveau_eventh *handler)
{
+ struct nouveau_event *event = handler->event;
+ unsigned long flags;
nouveau_event_put(handler);
+ spin_lock_irqsave(&event->list_lock, flags);
+ list_del(&handler->head);
+ spin_unlock_irqrestore(&event->list_lock, flags);
}
static int
@@ -74,13 +69,20 @@ nouveau_event_init(struct nouveau_event *event, int index,
int (*func)(void *, int), void *priv,
struct nouveau_eventh *handler)
{
+ unsigned long flags;
+
if (index >= event->index_nr)
return -EINVAL;
+
handler->event = event;
handler->flags = 0;
handler->index = index;
handler->func = func;
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;
}
@@ -116,19 +118,19 @@ nouveau_event_ref(struct nouveau_eventh *handler, struct nouveau_eventh **ref)
void
nouveau_event_trigger(struct nouveau_event *event, int index)
{
- struct nouveau_eventh *handler, *temp;
+ struct nouveau_eventh *handler;
unsigned long flags;
- if (index >= event->index_nr)
+ if (WARN_ON(index >= event->index_nr))
return;
- spin_lock_irqsave(&event->lock, flags);
- list_for_each_entry_safe(handler, temp, &event->index[index].list, head) {
- if (handler->func(handler->priv, index) == NVKM_EVENT_DROP) {
- nouveau_event_put_locked(event, index, handler);
- }
+ spin_lock_irqsave(&event->list_lock, flags);
+ list_for_each_entry(handler, &event->index[index].list, head) {
+ if (test_bit(NVKM_EVENT_ENABLE, &handler->flags) &&
+ 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
@@ -152,7 +154,8 @@ nouveau_event_create(int index_nr, struct nouveau_event **pevent)
if (!event)
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++)
INIT_LIST_HEAD(&event->index[i].list);
event->index_nr = index_nr;
diff --git a/drivers/gpu/drm/nouveau/core/include/core/event.h b/drivers/gpu/drm/nouveau/core/include/core/event.h
index b649c75..5d539eb 100644
--- a/drivers/gpu/drm/nouveau/core/include/core/event.h
+++ b/drivers/gpu/drm/nouveau/core/include/core/event.h
@@ -18,7 +18,8 @@ struct nouveau_eventh {
};
struct nouveau_event {
- spinlock_t lock;
+ spinlock_t list_lock;
+ spinlock_t refs_lock;
void *priv;
void (*enable)(struct nouveau_event *, int index);