Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit dac79008 authored by Ben Skeggs's avatar Ben Skeggs
Browse files

drm/nouveau: add spinlock around ramht modifications

parent e05c5a31
Loading
Loading
Loading
Loading
+56 −20
Original line number Diff line number Diff line
@@ -27,13 +27,13 @@
#include "nouveau_drv.h"
#include "nouveau_ramht.h"

static uint32_t
nouveau_ramht_hash_handle(struct nouveau_channel *chan, uint32_t handle)
static u32
nouveau_ramht_hash_handle(struct nouveau_channel *chan, u32 handle)
{
	struct drm_device *dev = chan->dev;
	struct drm_nouveau_private *dev_priv = dev->dev_private;
	struct nouveau_ramht *ramht = chan->ramht;
	uint32_t hash = 0;
	u32 hash = 0;
	int i;

	NV_DEBUG(dev, "ch%d handle=0x%08x\n", chan->id, handle);
@@ -53,10 +53,10 @@ nouveau_ramht_hash_handle(struct nouveau_channel *chan, uint32_t handle)

static int
nouveau_ramht_entry_valid(struct drm_device *dev, struct nouveau_gpuobj *ramht,
			  uint32_t offset)
			  u32 offset)
{
	struct drm_nouveau_private *dev_priv = dev->dev_private;
	uint32_t ctx = nv_ro32(ramht, offset + 4);
	u32 ctx = nv_ro32(ramht, offset + 4);

	if (dev_priv->card_type < NV_40)
		return ((ctx & NV_RAMHT_CONTEXT_VALID) != 0);
@@ -72,7 +72,8 @@ nouveau_ramht_insert(struct nouveau_channel *chan, u32 handle,
	struct nouveau_instmem_engine *instmem = &dev_priv->engine.instmem;
	struct nouveau_ramht_entry *entry;
	struct nouveau_gpuobj *ramht = chan->ramht->gpuobj;
	uint32_t ctx, co, ho;
	unsigned long flags;
	u32 ctx, co, ho;

	if (nouveau_ramht_find(chan, handle))
		return -EEXIST;
@@ -83,7 +84,6 @@ nouveau_ramht_insert(struct nouveau_channel *chan, u32 handle,
	entry->channel = chan;
	entry->gpuobj = NULL;
	entry->handle = handle;
	list_add(&entry->head, &chan->ramht->entries);
	nouveau_gpuobj_ref(gpuobj, &entry->gpuobj);

	if (dev_priv->card_type < NV_40) {
@@ -105,6 +105,9 @@ nouveau_ramht_insert(struct nouveau_channel *chan, u32 handle,
		}
	}

	spin_lock_irqsave(&chan->ramht->lock, flags);
	list_add(&entry->head, &chan->ramht->entries);

	co = ho = nouveau_ramht_hash_handle(chan, handle);
	do {
		if (!nouveau_ramht_entry_valid(dev, ramht, co)) {
@@ -114,6 +117,7 @@ nouveau_ramht_insert(struct nouveau_channel *chan, u32 handle,
			nv_wo32(ramht, co + 0, handle);
			nv_wo32(ramht, co + 4, ctx);

			spin_unlock_irqrestore(&chan->ramht->lock, flags);
			instmem->flush(dev);
			return 0;
		}
@@ -127,12 +131,13 @@ nouveau_ramht_insert(struct nouveau_channel *chan, u32 handle,

	NV_ERROR(dev, "RAMHT space exhausted. ch=%d\n", chan->id);
	list_del(&entry->head);
	spin_unlock_irqrestore(&chan->ramht->lock, flags);
	kfree(entry);
	return -ENOMEM;
}

void
nouveau_ramht_remove(struct nouveau_channel *chan, u32 handle)
static void
nouveau_ramht_remove_locked(struct nouveau_channel *chan, u32 handle)
{
	struct drm_device *dev = chan->dev;
	struct drm_nouveau_private *dev_priv = dev->dev_private;
@@ -173,17 +178,35 @@ nouveau_ramht_remove(struct nouveau_channel *chan, u32 handle)
		 chan->id, handle);
}

void
nouveau_ramht_remove(struct nouveau_channel *chan, u32 handle)
{
	struct nouveau_ramht *ramht = chan->ramht;
	unsigned long flags;

	spin_lock_irqsave(&ramht->lock, flags);
	nouveau_ramht_remove_locked(chan, handle);
	spin_unlock_irqrestore(&ramht->lock, flags);
}

struct nouveau_gpuobj *
nouveau_ramht_find(struct nouveau_channel *chan, u32 handle)
{
	struct nouveau_ramht *ramht = chan->ramht;
	struct nouveau_ramht_entry *entry;
	struct nouveau_gpuobj *gpuobj = NULL;
	unsigned long flags;

	spin_lock_irqsave(&ramht->lock, flags);
	list_for_each_entry(entry, &chan->ramht->entries, head) {
		if (entry->channel == chan && entry->handle == handle)
			return entry->gpuobj;
		if (entry->channel == chan && entry->handle == handle) {
			gpuobj = entry->gpuobj;
			break;
		}
	}
	spin_unlock_irqrestore(&ramht->lock, flags);

	return NULL;
	return gpuobj;
}

int
@@ -197,36 +220,49 @@ nouveau_ramht_new(struct drm_device *dev, struct nouveau_gpuobj *gpuobj,
		return -ENOMEM;

	ramht->dev = dev;
	ramht->refcount = 1;
	kref_init(&ramht->refcount);
	ramht->bits = drm_order(gpuobj->size / 8);
	INIT_LIST_HEAD(&ramht->entries);
	spin_lock_init(&ramht->lock);
	nouveau_gpuobj_ref(gpuobj, &ramht->gpuobj);

	*pramht = ramht;
	return 0;
}

static void
nouveau_ramht_del(struct kref *ref)
{
	struct nouveau_ramht *ramht =
		container_of(ref, struct nouveau_ramht, refcount);

	nouveau_gpuobj_ref(NULL, &ramht->gpuobj);
	kfree(ramht);
}

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 *ramht;
	unsigned long flags;

	if (ref)
		ref->refcount++;
		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)
				nouveau_ramht_remove(chan, entry->handle);
		}
			if (entry->channel != chan)
				continue;

		if (--ramht->refcount == 0) {
			nouveau_gpuobj_ref(NULL, &ramht->gpuobj);
			kfree(ramht);
			nouveau_ramht_remove_locked(chan, entry->handle);
		}
		spin_unlock_irqrestore(&ramht->lock, flags);

		kref_put(&ramht->refcount, nouveau_ramht_del);
	}
	*ptr = ref;
}
+2 −1
Original line number Diff line number Diff line
@@ -34,7 +34,8 @@ struct nouveau_ramht_entry {

struct nouveau_ramht {
	struct drm_device *dev;
	int refcount;
	struct kref refcount;
	spinlock_t lock;
	struct nouveau_gpuobj *gpuobj;
	struct list_head entries;
	int bits;