Commit 7bb94d26 authored by Francisco Jerez's avatar Francisco Jerez Committed by Ben Skeggs

drm/nouveau: Avoid lock dependency between ramht and ramin spinlocks.

The ramht code called some gpuobj functions with the HARDIRQ-safe
RAMHT spinlock held, this could potentially lead to a dead lock
because ramin_lock is HARDIRQ-unsafe.
Signed-off-by: default avatarFrancisco Jerez <currojerez@riseup.net>
Signed-off-by: default avatarBen Skeggs <bskeggs@redhat.com>
parent cbab95db
...@@ -153,26 +153,42 @@ nouveau_ramht_insert(struct nouveau_channel *chan, u32 handle, ...@@ -153,26 +153,42 @@ nouveau_ramht_insert(struct nouveau_channel *chan, u32 handle,
return -ENOMEM; return -ENOMEM;
} }
static struct nouveau_ramht_entry *
nouveau_ramht_remove_entry(struct nouveau_channel *chan, u32 handle)
{
struct nouveau_ramht *ramht = chan ? chan->ramht : NULL;
struct nouveau_ramht_entry *entry;
unsigned long flags;
if (!ramht)
return NULL;
spin_lock_irqsave(&ramht->lock, flags);
list_for_each_entry(entry, &ramht->entries, head) {
if (entry->channel == chan &&
(!handle || entry->handle == handle)) {
list_del(&entry->head);
spin_unlock_irqrestore(&ramht->lock, flags);
return entry;
}
}
spin_unlock_irqrestore(&ramht->lock, flags);
return NULL;
}
static void static void
nouveau_ramht_remove_locked(struct nouveau_channel *chan, u32 handle) nouveau_ramht_remove_hash(struct nouveau_channel *chan, u32 handle)
{ {
struct drm_device *dev = chan->dev; struct drm_device *dev = chan->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private; struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_instmem_engine *instmem = &dev_priv->engine.instmem; struct nouveau_instmem_engine *instmem = &dev_priv->engine.instmem;
struct nouveau_gpuobj *ramht = chan->ramht->gpuobj; struct nouveau_gpuobj *ramht = chan->ramht->gpuobj;
struct nouveau_ramht_entry *entry, *tmp; unsigned long flags;
u32 co, ho; u32 co, ho;
list_for_each_entry_safe(entry, tmp, &chan->ramht->entries, head) { spin_lock_irqsave(&chan->ramht->lock, flags);
if (entry->channel != chan || entry->handle != handle)
continue;
nouveau_gpuobj_ref(NULL, &entry->gpuobj);
list_del(&entry->head);
kfree(entry);
break;
}
co = ho = nouveau_ramht_hash_handle(chan, handle); co = ho = nouveau_ramht_hash_handle(chan, handle);
do { do {
if (nouveau_ramht_entry_valid(dev, ramht, co) && if (nouveau_ramht_entry_valid(dev, ramht, co) &&
...@@ -184,7 +200,7 @@ nouveau_ramht_remove_locked(struct nouveau_channel *chan, u32 handle) ...@@ -184,7 +200,7 @@ nouveau_ramht_remove_locked(struct nouveau_channel *chan, u32 handle)
nv_wo32(ramht, co + 0, 0x00000000); nv_wo32(ramht, co + 0, 0x00000000);
nv_wo32(ramht, co + 4, 0x00000000); nv_wo32(ramht, co + 4, 0x00000000);
instmem->flush(dev); instmem->flush(dev);
return; goto out;
} }
co += 8; co += 8;
...@@ -194,17 +210,22 @@ nouveau_ramht_remove_locked(struct nouveau_channel *chan, u32 handle) ...@@ -194,17 +210,22 @@ nouveau_ramht_remove_locked(struct nouveau_channel *chan, u32 handle)
NV_ERROR(dev, "RAMHT entry not found. ch=%d, handle=0x%08x\n", NV_ERROR(dev, "RAMHT entry not found. ch=%d, handle=0x%08x\n",
chan->id, handle); chan->id, handle);
out:
spin_unlock_irqrestore(&chan->ramht->lock, flags);
} }
void void
nouveau_ramht_remove(struct nouveau_channel *chan, u32 handle) nouveau_ramht_remove(struct nouveau_channel *chan, u32 handle)
{ {
struct nouveau_ramht *ramht = chan->ramht; struct nouveau_ramht_entry *entry;
unsigned long flags;
spin_lock_irqsave(&ramht->lock, flags); entry = nouveau_ramht_remove_entry(chan, handle);
nouveau_ramht_remove_locked(chan, handle); if (!entry)
spin_unlock_irqrestore(&ramht->lock, flags); return;
nouveau_ramht_remove_hash(chan, entry->handle);
nouveau_gpuobj_ref(NULL, &entry->gpuobj);
kfree(entry);
} }
struct nouveau_gpuobj * struct nouveau_gpuobj *
...@@ -265,23 +286,19 @@ void ...@@ -265,23 +286,19 @@ void
nouveau_ramht_ref(struct nouveau_ramht *ref, struct nouveau_ramht **ptr, nouveau_ramht_ref(struct nouveau_ramht *ref, struct nouveau_ramht **ptr,
struct nouveau_channel *chan) struct nouveau_channel *chan)
{ {
struct nouveau_ramht_entry *entry, *tmp; struct nouveau_ramht_entry *entry;
struct nouveau_ramht *ramht; struct nouveau_ramht *ramht;
unsigned long flags;
if (ref) if (ref)
kref_get(&ref->refcount); kref_get(&ref->refcount);
ramht = *ptr; ramht = *ptr;
if (ramht) { if (ramht) {
spin_lock_irqsave(&ramht->lock, flags); while ((entry = nouveau_ramht_remove_entry(chan, 0))) {
list_for_each_entry_safe(entry, tmp, &ramht->entries, head) { nouveau_ramht_remove_hash(chan, entry->handle);
if (entry->channel != chan) nouveau_gpuobj_ref(NULL, &entry->gpuobj);
continue; kfree(entry);
nouveau_ramht_remove_locked(chan, entry->handle);
} }
spin_unlock_irqrestore(&ramht->lock, flags);
kref_put(&ramht->refcount, nouveau_ramht_del); kref_put(&ramht->refcount, nouveau_ramht_del);
} }
......
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