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

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
Loading
Loading
Loading
Loading
+44 −27
Original line number Diff line number Diff line
@@ -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);
	}