mirror of
https://github.com/torvalds/linux.git
synced 2024-12-14 07:02:23 +00:00
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: Francisco Jerez <currojerez@riseup.net> Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
This commit is contained in:
parent
cbab95db84
commit
7bb94d26ad
@ -153,26 +153,42 @@ nouveau_ramht_insert(struct nouveau_channel *chan, u32 handle,
|
||||
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
|
||||
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_nouveau_private *dev_priv = dev->dev_private;
|
||||
struct nouveau_instmem_engine *instmem = &dev_priv->engine.instmem;
|
||||
struct nouveau_gpuobj *ramht = chan->ramht->gpuobj;
|
||||
struct nouveau_ramht_entry *entry, *tmp;
|
||||
unsigned long flags;
|
||||
u32 co, ho;
|
||||
|
||||
list_for_each_entry_safe(entry, tmp, &chan->ramht->entries, head) {
|
||||
if (entry->channel != chan || entry->handle != handle)
|
||||
continue;
|
||||
|
||||
nouveau_gpuobj_ref(NULL, &entry->gpuobj);
|
||||
list_del(&entry->head);
|
||||
kfree(entry);
|
||||
break;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&chan->ramht->lock, flags);
|
||||
co = ho = nouveau_ramht_hash_handle(chan, handle);
|
||||
do {
|
||||
if (nouveau_ramht_entry_valid(dev, ramht, co) &&
|
||||
@ -184,7 +200,7 @@ nouveau_ramht_remove_locked(struct nouveau_channel *chan, u32 handle)
|
||||
nv_wo32(ramht, co + 0, 0x00000000);
|
||||
nv_wo32(ramht, co + 4, 0x00000000);
|
||||
instmem->flush(dev);
|
||||
return;
|
||||
goto out;
|
||||
}
|
||||
|
||||
co += 8;
|
||||
@ -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",
|
||||
chan->id, handle);
|
||||
out:
|
||||
spin_unlock_irqrestore(&chan->ramht->lock, flags);
|
||||
}
|
||||
|
||||
void
|
||||
nouveau_ramht_remove(struct nouveau_channel *chan, u32 handle)
|
||||
{
|
||||
struct nouveau_ramht *ramht = chan->ramht;
|
||||
unsigned long flags;
|
||||
struct nouveau_ramht_entry *entry;
|
||||
|
||||
spin_lock_irqsave(&ramht->lock, flags);
|
||||
nouveau_ramht_remove_locked(chan, handle);
|
||||
spin_unlock_irqrestore(&ramht->lock, flags);
|
||||
entry = nouveau_ramht_remove_entry(chan, handle);
|
||||
if (!entry)
|
||||
return;
|
||||
|
||||
nouveau_ramht_remove_hash(chan, entry->handle);
|
||||
nouveau_gpuobj_ref(NULL, &entry->gpuobj);
|
||||
kfree(entry);
|
||||
}
|
||||
|
||||
struct nouveau_gpuobj *
|
||||
@ -265,23 +286,19 @@ void
|
||||
nouveau_ramht_ref(struct nouveau_ramht *ref, struct nouveau_ramht **ptr,
|
||||
struct nouveau_channel *chan)
|
||||
{
|
||||
struct nouveau_ramht_entry *entry, *tmp;
|
||||
struct nouveau_ramht_entry *entry;
|
||||
struct nouveau_ramht *ramht;
|
||||
unsigned long flags;
|
||||
|
||||
if (ref)
|
||||
kref_get(&ref->refcount);
|
||||
|
||||
ramht = *ptr;
|
||||
if (ramht) {
|
||||
spin_lock_irqsave(&ramht->lock, flags);
|
||||
list_for_each_entry_safe(entry, tmp, &ramht->entries, head) {
|
||||
if (entry->channel != chan)
|
||||
continue;
|
||||
|
||||
nouveau_ramht_remove_locked(chan, entry->handle);
|
||||
while ((entry = nouveau_ramht_remove_entry(chan, 0))) {
|
||||
nouveau_ramht_remove_hash(chan, entry->handle);
|
||||
nouveau_gpuobj_ref(NULL, &entry->gpuobj);
|
||||
kfree(entry);
|
||||
}
|
||||
spin_unlock_irqrestore(&ramht->lock, flags);
|
||||
|
||||
kref_put(&ramht->refcount, nouveau_ramht_del);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user