mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-27 03:10:12 +00:00
drm/nouveau: protect channel create/destroy and irq handler with a spinlock
The nv50 pgraph handler (for example) could reenable pgraph fifo access and that would be bad when pgraph context is being unloaded (we need the guarantee a ctxprog isn't running). Signed-off-by: Maarten Maathuis <madman2003@gmail.com> Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
This commit is contained in:
parent
6c42966768
commit
ff9e5279b1
7 changed files with 36 additions and 1 deletions
|
@ -275,9 +275,18 @@ nouveau_channel_free(struct nouveau_channel *chan)
|
|||
*/
|
||||
nouveau_fence_fini(chan);
|
||||
|
||||
/* Ensure the channel is no longer active on the GPU */
|
||||
/* This will prevent pfifo from switching channels. */
|
||||
pfifo->reassign(dev, false);
|
||||
|
||||
/* We want to give pgraph a chance to idle and get rid of all potential
|
||||
* errors. We need to do this before the lock, otherwise the irq handler
|
||||
* is unable to process them.
|
||||
*/
|
||||
if (pgraph->channel(dev) == chan)
|
||||
nouveau_wait_for_idle(dev);
|
||||
|
||||
spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
|
||||
|
||||
pgraph->fifo_access(dev, false);
|
||||
if (pgraph->channel(dev) == chan)
|
||||
pgraph->unload_context(dev);
|
||||
|
@ -293,6 +302,8 @@ nouveau_channel_free(struct nouveau_channel *chan)
|
|||
|
||||
pfifo->reassign(dev, true);
|
||||
|
||||
spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
|
||||
|
||||
/* Release the channel's resources */
|
||||
nouveau_gpuobj_ref_del(dev, &chan->pushbuf);
|
||||
if (chan->pushbuf_bo) {
|
||||
|
|
|
@ -533,6 +533,9 @@ struct drm_nouveau_private {
|
|||
struct nouveau_engine engine;
|
||||
struct nouveau_channel *channel;
|
||||
|
||||
/* For PFIFO and PGRAPH. */
|
||||
spinlock_t context_switch_lock;
|
||||
|
||||
/* RAMIN configuration, RAMFC, RAMHT and RAMRO offsets */
|
||||
struct nouveau_gpuobj *ramht;
|
||||
uint32_t ramin_rsvd_vram;
|
||||
|
|
|
@ -691,11 +691,14 @@ nouveau_irq_handler(DRM_IRQ_ARGS)
|
|||
struct drm_device *dev = (struct drm_device *)arg;
|
||||
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
||||
uint32_t status, fbdev_flags = 0;
|
||||
unsigned long flags;
|
||||
|
||||
status = nv_rd32(dev, NV03_PMC_INTR_0);
|
||||
if (!status)
|
||||
return IRQ_NONE;
|
||||
|
||||
spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
|
||||
|
||||
if (dev_priv->fbdev_info) {
|
||||
fbdev_flags = dev_priv->fbdev_info->flags;
|
||||
dev_priv->fbdev_info->flags |= FBINFO_HWACCEL_DISABLED;
|
||||
|
@ -733,5 +736,7 @@ nouveau_irq_handler(DRM_IRQ_ARGS)
|
|||
if (dev_priv->fbdev_info)
|
||||
dev_priv->fbdev_info->flags = fbdev_flags;
|
||||
|
||||
spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
|
|
@ -391,6 +391,7 @@ nouveau_card_init(struct drm_device *dev)
|
|||
goto out;
|
||||
engine = &dev_priv->engine;
|
||||
dev_priv->init_state = NOUVEAU_CARD_INIT_FAILED;
|
||||
spin_lock_init(&dev_priv->context_switch_lock);
|
||||
|
||||
/* Parse BIOS tables / Run init tables if card not POSTed */
|
||||
if (drm_core_check_feature(dev, DRIVER_MODESET)) {
|
||||
|
|
|
@ -117,6 +117,7 @@ nv04_fifo_create_context(struct nouveau_channel *chan)
|
|||
{
|
||||
struct drm_device *dev = chan->dev;
|
||||
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
ret = nouveau_gpuobj_new_fake(dev, NV04_RAMFC(chan->id), ~0,
|
||||
|
@ -127,6 +128,8 @@ nv04_fifo_create_context(struct nouveau_channel *chan)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
|
||||
|
||||
/* Setup initial state */
|
||||
dev_priv->engine.instmem.prepare_access(dev, true);
|
||||
RAMFC_WR(DMA_PUT, chan->pushbuf_base);
|
||||
|
@ -144,6 +147,8 @@ nv04_fifo_create_context(struct nouveau_channel *chan)
|
|||
/* enable the fifo dma operation */
|
||||
nv_wr32(dev, NV04_PFIFO_MODE,
|
||||
nv_rd32(dev, NV04_PFIFO_MODE) | (1 << chan->id));
|
||||
|
||||
spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@ nv40_fifo_create_context(struct nouveau_channel *chan)
|
|||
struct drm_device *dev = chan->dev;
|
||||
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
||||
uint32_t fc = NV40_RAMFC(chan->id);
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
ret = nouveau_gpuobj_new_fake(dev, NV40_RAMFC(chan->id), ~0,
|
||||
|
@ -45,6 +46,8 @@ nv40_fifo_create_context(struct nouveau_channel *chan)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
|
||||
|
||||
dev_priv->engine.instmem.prepare_access(dev, true);
|
||||
nv_wi32(dev, fc + 0, chan->pushbuf_base);
|
||||
nv_wi32(dev, fc + 4, chan->pushbuf_base);
|
||||
|
@ -63,6 +66,8 @@ nv40_fifo_create_context(struct nouveau_channel *chan)
|
|||
/* enable the fifo dma operation */
|
||||
nv_wr32(dev, NV04_PFIFO_MODE,
|
||||
nv_rd32(dev, NV04_PFIFO_MODE) | (1 << chan->id));
|
||||
|
||||
spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -243,6 +243,7 @@ nv50_fifo_create_context(struct nouveau_channel *chan)
|
|||
struct drm_device *dev = chan->dev;
|
||||
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
||||
struct nouveau_gpuobj *ramfc = NULL;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
NV_DEBUG(dev, "ch%d\n", chan->id);
|
||||
|
@ -278,6 +279,8 @@ nv50_fifo_create_context(struct nouveau_channel *chan)
|
|||
return ret;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
|
||||
|
||||
dev_priv->engine.instmem.prepare_access(dev, true);
|
||||
|
||||
nv_wo32(dev, ramfc, 0x08/4, chan->pushbuf_base);
|
||||
|
@ -306,10 +309,12 @@ nv50_fifo_create_context(struct nouveau_channel *chan)
|
|||
ret = nv50_fifo_channel_enable(dev, chan->id, false);
|
||||
if (ret) {
|
||||
NV_ERROR(dev, "error enabling ch%d: %d\n", chan->id, ret);
|
||||
spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
|
||||
nouveau_gpuobj_ref_del(dev, &chan->ramfc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue