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:
Родитель
6c42966768
Коммит
ff9e5279b1
|
@ -275,9 +275,18 @@ nouveau_channel_free(struct nouveau_channel *chan)
|
||||||
*/
|
*/
|
||||||
nouveau_fence_fini(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);
|
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);
|
pgraph->fifo_access(dev, false);
|
||||||
if (pgraph->channel(dev) == chan)
|
if (pgraph->channel(dev) == chan)
|
||||||
pgraph->unload_context(dev);
|
pgraph->unload_context(dev);
|
||||||
|
@ -293,6 +302,8 @@ nouveau_channel_free(struct nouveau_channel *chan)
|
||||||
|
|
||||||
pfifo->reassign(dev, true);
|
pfifo->reassign(dev, true);
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
|
||||||
|
|
||||||
/* Release the channel's resources */
|
/* Release the channel's resources */
|
||||||
nouveau_gpuobj_ref_del(dev, &chan->pushbuf);
|
nouveau_gpuobj_ref_del(dev, &chan->pushbuf);
|
||||||
if (chan->pushbuf_bo) {
|
if (chan->pushbuf_bo) {
|
||||||
|
|
|
@ -533,6 +533,9 @@ struct drm_nouveau_private {
|
||||||
struct nouveau_engine engine;
|
struct nouveau_engine engine;
|
||||||
struct nouveau_channel *channel;
|
struct nouveau_channel *channel;
|
||||||
|
|
||||||
|
/* For PFIFO and PGRAPH. */
|
||||||
|
spinlock_t context_switch_lock;
|
||||||
|
|
||||||
/* RAMIN configuration, RAMFC, RAMHT and RAMRO offsets */
|
/* RAMIN configuration, RAMFC, RAMHT and RAMRO offsets */
|
||||||
struct nouveau_gpuobj *ramht;
|
struct nouveau_gpuobj *ramht;
|
||||||
uint32_t ramin_rsvd_vram;
|
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_device *dev = (struct drm_device *)arg;
|
||||||
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
||||||
uint32_t status, fbdev_flags = 0;
|
uint32_t status, fbdev_flags = 0;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
status = nv_rd32(dev, NV03_PMC_INTR_0);
|
status = nv_rd32(dev, NV03_PMC_INTR_0);
|
||||||
if (!status)
|
if (!status)
|
||||||
return IRQ_NONE;
|
return IRQ_NONE;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
|
||||||
|
|
||||||
if (dev_priv->fbdev_info) {
|
if (dev_priv->fbdev_info) {
|
||||||
fbdev_flags = dev_priv->fbdev_info->flags;
|
fbdev_flags = dev_priv->fbdev_info->flags;
|
||||||
dev_priv->fbdev_info->flags |= FBINFO_HWACCEL_DISABLED;
|
dev_priv->fbdev_info->flags |= FBINFO_HWACCEL_DISABLED;
|
||||||
|
@ -733,5 +736,7 @@ nouveau_irq_handler(DRM_IRQ_ARGS)
|
||||||
if (dev_priv->fbdev_info)
|
if (dev_priv->fbdev_info)
|
||||||
dev_priv->fbdev_info->flags = fbdev_flags;
|
dev_priv->fbdev_info->flags = fbdev_flags;
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
|
@ -391,6 +391,7 @@ nouveau_card_init(struct drm_device *dev)
|
||||||
goto out;
|
goto out;
|
||||||
engine = &dev_priv->engine;
|
engine = &dev_priv->engine;
|
||||||
dev_priv->init_state = NOUVEAU_CARD_INIT_FAILED;
|
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 */
|
/* Parse BIOS tables / Run init tables if card not POSTed */
|
||||||
if (drm_core_check_feature(dev, DRIVER_MODESET)) {
|
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_device *dev = chan->dev;
|
||||||
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
||||||
|
unsigned long flags;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = nouveau_gpuobj_new_fake(dev, NV04_RAMFC(chan->id), ~0,
|
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)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
|
||||||
|
|
||||||
/* Setup initial state */
|
/* Setup initial state */
|
||||||
dev_priv->engine.instmem.prepare_access(dev, true);
|
dev_priv->engine.instmem.prepare_access(dev, true);
|
||||||
RAMFC_WR(DMA_PUT, chan->pushbuf_base);
|
RAMFC_WR(DMA_PUT, chan->pushbuf_base);
|
||||||
|
@ -144,6 +147,8 @@ nv04_fifo_create_context(struct nouveau_channel *chan)
|
||||||
/* enable the fifo dma operation */
|
/* enable the fifo dma operation */
|
||||||
nv_wr32(dev, NV04_PFIFO_MODE,
|
nv_wr32(dev, NV04_PFIFO_MODE,
|
||||||
nv_rd32(dev, NV04_PFIFO_MODE) | (1 << chan->id));
|
nv_rd32(dev, NV04_PFIFO_MODE) | (1 << chan->id));
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,7 @@ nv40_fifo_create_context(struct nouveau_channel *chan)
|
||||||
struct drm_device *dev = chan->dev;
|
struct drm_device *dev = chan->dev;
|
||||||
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
||||||
uint32_t fc = NV40_RAMFC(chan->id);
|
uint32_t fc = NV40_RAMFC(chan->id);
|
||||||
|
unsigned long flags;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = nouveau_gpuobj_new_fake(dev, NV40_RAMFC(chan->id), ~0,
|
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)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
|
||||||
|
|
||||||
dev_priv->engine.instmem.prepare_access(dev, true);
|
dev_priv->engine.instmem.prepare_access(dev, true);
|
||||||
nv_wi32(dev, fc + 0, chan->pushbuf_base);
|
nv_wi32(dev, fc + 0, chan->pushbuf_base);
|
||||||
nv_wi32(dev, fc + 4, 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 */
|
/* enable the fifo dma operation */
|
||||||
nv_wr32(dev, NV04_PFIFO_MODE,
|
nv_wr32(dev, NV04_PFIFO_MODE,
|
||||||
nv_rd32(dev, NV04_PFIFO_MODE) | (1 << chan->id));
|
nv_rd32(dev, NV04_PFIFO_MODE) | (1 << chan->id));
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -243,6 +243,7 @@ nv50_fifo_create_context(struct nouveau_channel *chan)
|
||||||
struct drm_device *dev = chan->dev;
|
struct drm_device *dev = chan->dev;
|
||||||
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
||||||
struct nouveau_gpuobj *ramfc = NULL;
|
struct nouveau_gpuobj *ramfc = NULL;
|
||||||
|
unsigned long flags;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
NV_DEBUG(dev, "ch%d\n", chan->id);
|
NV_DEBUG(dev, "ch%d\n", chan->id);
|
||||||
|
@ -278,6 +279,8 @@ nv50_fifo_create_context(struct nouveau_channel *chan)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
|
||||||
|
|
||||||
dev_priv->engine.instmem.prepare_access(dev, true);
|
dev_priv->engine.instmem.prepare_access(dev, true);
|
||||||
|
|
||||||
nv_wo32(dev, ramfc, 0x08/4, chan->pushbuf_base);
|
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);
|
ret = nv50_fifo_channel_enable(dev, chan->id, false);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
NV_ERROR(dev, "error enabling ch%d: %d\n", chan->id, 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);
|
nouveau_gpuobj_ref_del(dev, &chan->ramfc);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче