diff options
author | Ben Skeggs <bskeggs@redhat.com> | 2014-02-23 23:30:11 +1000 |
---|---|---|
committer | Ben Skeggs <bskeggs@redhat.com> | 2014-03-26 14:00:53 +1000 |
commit | 98d1e317e5155898b979b44b31414c7281565654 (patch) | |
tree | e3d53e9fc63555a7a5a165d492c367a5b2c20cd8 | |
parent | 448a4532a059470635b67e48340d6c374905db5f (diff) | |
download | op-kernel-dev-98d1e317e5155898b979b44b31414c7281565654.zip op-kernel-dev-98d1e317e5155898b979b44b31414c7281565654.tar.gz |
drm/nve0/fifo: attempt to recover engines from mmu faults
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
-rw-r--r-- | drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c | 89 |
1 files changed, 87 insertions, 2 deletions
diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c index 0271337..072998b 100644 --- a/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c +++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c @@ -65,6 +65,10 @@ struct nve0_fifo_engn { struct nve0_fifo_priv { struct nouveau_fifo base; + + struct work_struct fault; + u64 mask; + struct nve0_fifo_engn engine[FIFO_ENGINE_NR]; struct { struct nouveau_gpuobj *mem; @@ -393,6 +397,75 @@ nve0_fifo_cclass = { * PFIFO engine ******************************************************************************/ +static inline int +nve0_fifo_engidx(struct nve0_fifo_priv *priv, u32 engn) +{ + switch (engn) { + case NVDEV_ENGINE_GR : + case NVDEV_ENGINE_COPY2: engn = 0; break; + case NVDEV_ENGINE_BSP : engn = 1; break; + case NVDEV_ENGINE_PPP : engn = 2; break; + case NVDEV_ENGINE_VP : engn = 3; break; + case NVDEV_ENGINE_COPY0: engn = 4; break; + case NVDEV_ENGINE_COPY1: engn = 5; break; + case NVDEV_ENGINE_VENC : engn = 6; break; + default: + return -1; + } + + return engn; +} + +static void +nve0_fifo_recover_work(struct work_struct *work) +{ + struct nve0_fifo_priv *priv = container_of(work, typeof(*priv), fault); + struct nouveau_object *engine; + unsigned long flags; + u32 engn, engm = 0; + u64 mask, todo; + + spin_lock_irqsave(&priv->base.lock, flags); + mask = priv->mask; + priv->mask = 0ULL; + spin_unlock_irqrestore(&priv->base.lock, flags); + + for (todo = mask; engn = __ffs64(todo), todo; todo &= ~(1 << engn)) + engm |= 1 << nve0_fifo_engidx(priv, engn); + nv_mask(priv, 0x002630, engm, engm); + + for (todo = mask; engn = __ffs64(todo), todo; todo &= ~(1 << engn)) { + if ((engine = (void *)nouveau_engine(priv, engn))) { + nv_ofuncs(engine)->fini(engine, false); + WARN_ON(nv_ofuncs(engine)->init(engine)); + } + nve0_fifo_runlist_update(priv, nve0_fifo_engidx(priv, engn)); + } + + nv_wr32(priv, 0x00262c, engm); + nv_mask(priv, 0x002630, engm, 0x00000000); +} + +static void +nve0_fifo_recover(struct nve0_fifo_priv *priv, struct nouveau_engine *engine, + struct nve0_fifo_chan *chan) +{ + struct nouveau_object *engobj = nv_object(engine); + u32 chid = chan->base.chid; + unsigned long flags; + + nv_error(priv, "%s engine fault on channel %d, recovering...\n", + nv_subdev(engine)->name, chid); + + nv_mask(priv, 0x800004 + (chid * 0x08), 0x00000800, 0x00000800); + chan->state = KILLED; + + spin_lock_irqsave(&priv->base.lock, flags); + priv->mask |= 1ULL << nv_engidx(engobj); + spin_unlock_irqrestore(&priv->base.lock, flags); + schedule_work(&priv->fault); +} + static int nve0_fifo_swmthd(struct nve0_fifo_priv *priv, u32 chid, u32 mthd, u32 data) { @@ -572,8 +645,8 @@ nve0_fifo_intr_fault(struct nve0_fifo_priv *priv, int unit) u32 write = (stat & 0x00000080); u32 hub = (stat & 0x00000040); u32 reason = (stat & 0x0000000f); - struct nouveau_object *engctx = NULL; - struct nouveau_engine *engine; + struct nouveau_object *engctx = NULL, *object; + struct nouveau_engine *engine = NULL; const struct nouveau_enum *er, *eu, *ec; char erunk[6] = ""; char euunk[6] = ""; @@ -623,6 +696,16 @@ nve0_fifo_intr_fault(struct nve0_fifo_priv *priv, int unit) ec ? ec->name : ecunk, (u64)inst << 12, nouveau_client_name(engctx)); + object = engctx; + while (object) { + switch (nv_mclass(object)) { + case NVE0_CHANNEL_IND_CLASS: + nve0_fifo_recover(priv, engine, (void *)object); + break; + } + object = object->parent; + } + nouveau_engctx_put(engctx); } @@ -889,6 +972,8 @@ nve0_fifo_ctor(struct nouveau_object *parent, struct nouveau_object *engine, if (ret) return ret; + INIT_WORK(&priv->fault, nve0_fifo_recover_work); + for (i = 0; i < FIFO_ENGINE_NR; i++) { ret = nouveau_gpuobj_new(nv_object(priv), NULL, 0x8000, 0x1000, 0, &priv->engine[i].runlist[0]); |