From: Ben Skeggs Date: Wed, 1 Sep 2010 05:24:36 +0000 (+1000) Subject: drm/nouveau: add spinlock around ramht modifications X-Git-Url: http://drtracing.org/?a=commitdiff_plain;h=dac790080467eb12f1049ddca1c101eb0dcc9f0c;p=deliverable%2Flinux.git drm/nouveau: add spinlock around ramht modifications Reviewed-by: Francisco Jerez Signed-off-by: Ben Skeggs --- diff --git a/drivers/gpu/drm/nouveau/nouveau_ramht.c b/drivers/gpu/drm/nouveau/nouveau_ramht.c index ccbc8d69ea68..de34b6bb059f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_ramht.c +++ b/drivers/gpu/drm/nouveau/nouveau_ramht.c @@ -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; } diff --git a/drivers/gpu/drm/nouveau/nouveau_ramht.h b/drivers/gpu/drm/nouveau/nouveau_ramht.h index f37737a93642..b79cb5e1a8f1 100644 --- a/drivers/gpu/drm/nouveau/nouveau_ramht.h +++ b/drivers/gpu/drm/nouveau/nouveau_ramht.h @@ -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;