drm/nouveau: add more fine-grained locking to channel list + structures

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
diff --git a/drivers/gpu/drm/nouveau/nouveau_irq.c b/drivers/gpu/drm/nouveau/nouveau_irq.c
index a4fa9e1..c5e37bc 100644
--- a/drivers/gpu/drm/nouveau/nouveau_irq.c
+++ b/drivers/gpu/drm/nouveau/nouveau_irq.c
@@ -113,15 +113,17 @@
 	struct drm_nouveau_private *dev_priv = dev->dev_private;
 	struct nouveau_channel *chan = NULL;
 	struct nouveau_gpuobj *obj;
+	unsigned long flags;
 	const int subc = (addr >> 13) & 0x7;
 	const int mthd = addr & 0x1ffc;
 	bool handled = false;
 	u32 engine;
 
+	spin_lock_irqsave(&dev_priv->channels.lock, flags);
 	if (likely(chid >= 0 && chid < dev_priv->engine.fifo.channels))
-		chan = dev_priv->fifos[chid];
+		chan = dev_priv->channels.ptr[chid];
 	if (unlikely(!chan))
-		return false;
+		goto out;
 
 	switch (mthd) {
 	case 0x0000: /* bind object to subchannel */
@@ -146,6 +148,8 @@
 		break;
 	}
 
+out:
+	spin_unlock_irqrestore(&dev_priv->channels.lock, flags);
 	return handled;
 }
 
@@ -398,6 +402,8 @@
 nouveau_graph_chid_from_grctx(struct drm_device *dev)
 {
 	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_channel *chan;
+	unsigned long flags;
 	uint32_t inst;
 	int i;
 
@@ -407,27 +413,29 @@
 	if (dev_priv->card_type < NV_50) {
 		inst = (nv_rd32(dev, 0x40032c) & 0xfffff) << 4;
 
+		spin_lock_irqsave(&dev_priv->channels.lock, flags);
 		for (i = 0; i < dev_priv->engine.fifo.channels; i++) {
-			struct nouveau_channel *chan = dev_priv->fifos[i];
-
+			chan = dev_priv->channels.ptr[i];
 			if (!chan || !chan->ramin_grctx)
 				continue;
 
 			if (inst == chan->ramin_grctx->pinst)
 				break;
 		}
+		spin_unlock_irqrestore(&dev_priv->channels.lock, flags);
 	} else {
 		inst = (nv_rd32(dev, 0x40032c) & 0xfffff) << 12;
 
+		spin_lock_irqsave(&dev_priv->channels.lock, flags);
 		for (i = 0; i < dev_priv->engine.fifo.channels; i++) {
-			struct nouveau_channel *chan = dev_priv->fifos[i];
-
+			chan = dev_priv->channels.ptr[i];
 			if (!chan || !chan->ramin)
 				continue;
 
 			if (inst == chan->ramin->vinst)
 				break;
 		}
+		spin_unlock_irqrestore(&dev_priv->channels.lock, flags);
 	}
 
 
@@ -449,7 +457,8 @@
 	else
 		channel = nouveau_graph_chid_from_grctx(dev);
 
-	if (channel >= engine->fifo.channels || !dev_priv->fifos[channel]) {
+	if (channel >= engine->fifo.channels ||
+	    !dev_priv->channels.ptr[channel]) {
 		NV_ERROR(dev, "AIII, invalid/inactive channel id %d\n", channel);
 		return -EINVAL;
 	}
@@ -532,14 +541,19 @@
 			   struct nouveau_pgraph_trap *trap)
 {
 	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	unsigned long flags;
+	int ret = -EINVAL;
 
-	if (trap->channel < 0 ||
-	    trap->channel >= dev_priv->engine.fifo.channels ||
-	    !dev_priv->fifos[trap->channel])
-		return -ENODEV;
+	spin_lock_irqsave(&dev_priv->channels.lock, flags);
+	if (trap->channel > 0 &&
+	    trap->channel < dev_priv->engine.fifo.channels &&
+	    dev_priv->channels.ptr[trap->channel]) {
+		ret = nouveau_call_method(dev_priv->channels.ptr[trap->channel],
+					  trap->class, trap->mthd, trap->data);
+	}
+	spin_unlock_irqrestore(&dev_priv->channels.lock, flags);
 
-	return nouveau_call_method(dev_priv->fifos[trap->channel],
-				   trap->class, trap->mthd, trap->data);
+	return ret;
 }
 
 static inline void