From: Ben Skeggs Date: Wed, 2 Oct 2013 23:59:16 +0000 (+1000) Subject: drm/nouveau/core: split lock into list+exec and enable refcount locks X-Git-Url: https://git.karo-electronics.de/?a=commitdiff_plain;h=15689c3c819d1d659cff464f427f398758a683de;p=linux-beck.git 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 Signed-off-by: Ben Skeggs --- diff --git a/drivers/gpu/drm/nouveau/core/core/event.c b/drivers/gpu/drm/nouveau/core/core/event.c index 313afcc65e0f..3f3c76581a9e 100644 --- a/drivers/gpu/drm/nouveau/core/core/event.c +++ b/drivers/gpu/drm/nouveau/core/core/event.c @@ -23,27 +23,19 @@ #include #include -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 b649c75d7198..5d539ebff3ed 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);