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:
committed by
Ben Skeggs
parent
cbab95db84
commit
7bb94d26ad
@@ -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)
|
|||||||
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)
|
|||||||
|
|
||||||
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
|
|||||||
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);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user