diff options
author | Francisco Jerez <currojerez@riseup.net> | 2010-10-17 21:53:39 -0400 |
---|---|---|
committer | Ben Skeggs <bskeggs@redhat.com> | 2010-12-03 00:06:35 -0500 |
commit | 3945e47543863385b54d94c94b023ee7ca9df972 (patch) | |
tree | 209eb523c0e3a01069f8e18751b97373804a22d3 /drivers/gpu/drm/nouveau/nv04_fifo.c | |
parent | fcccab2e4eb8d579837481054cc2cb28eea0baef (diff) |
drm/nouveau: Refactor context destruction to avoid a lock ordering issue.
The destroy_context() engine hooks call gpuobj management functions to
release the channel resources, these functions use HARDIRQ-unsafe locks
whereas destroy_context() is called with the HARDIRQ-safe
context_switch_lock held, that's a lock ordering violation.
Push the engine-specific channel destruction logic into destroy_context()
and let the hardware-specific code lock and unlock when it's actually
needed. Change the engine destruction order to avoid a race in the small
gap between pgraph and pfifo context uninitialization.
Reported-by: Marcin Slusarz <marcin.slusarz@gmail.com>
Signed-off-by: Francisco Jerez <currojerez@riseup.net>
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
Diffstat (limited to 'drivers/gpu/drm/nouveau/nv04_fifo.c')
-rw-r--r-- | drivers/gpu/drm/nouveau/nv04_fifo.c | 21 |
1 files changed, 19 insertions, 2 deletions
diff --git a/drivers/gpu/drm/nouveau/nv04_fifo.c b/drivers/gpu/drm/nouveau/nv04_fifo.c index 25c439dcdfd9..4c0d3a8fca68 100644 --- a/drivers/gpu/drm/nouveau/nv04_fifo.c +++ b/drivers/gpu/drm/nouveau/nv04_fifo.c | |||
@@ -151,10 +151,27 @@ void | |||
151 | nv04_fifo_destroy_context(struct nouveau_channel *chan) | 151 | nv04_fifo_destroy_context(struct nouveau_channel *chan) |
152 | { | 152 | { |
153 | struct drm_device *dev = chan->dev; | 153 | struct drm_device *dev = chan->dev; |
154 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
155 | struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo; | ||
156 | unsigned long flags; | ||
154 | 157 | ||
155 | nv_wr32(dev, NV04_PFIFO_MODE, | 158 | spin_lock_irqsave(&dev_priv->context_switch_lock, flags); |
156 | nv_rd32(dev, NV04_PFIFO_MODE) & ~(1 << chan->id)); | 159 | pfifo->reassign(dev, false); |
160 | |||
161 | /* Unload the context if it's the currently active one */ | ||
162 | if (pfifo->channel_id(dev) == chan->id) { | ||
163 | pfifo->disable(dev); | ||
164 | pfifo->unload_context(dev); | ||
165 | pfifo->enable(dev); | ||
166 | } | ||
167 | |||
168 | /* Keep it from being rescheduled */ | ||
169 | nv_mask(dev, NV04_PFIFO_MODE, 1 << chan->id, 0); | ||
170 | |||
171 | pfifo->reassign(dev, true); | ||
172 | spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); | ||
157 | 173 | ||
174 | /* Free the channel resources */ | ||
158 | nouveau_gpuobj_ref(NULL, &chan->ramfc); | 175 | nouveau_gpuobj_ref(NULL, &chan->ramfc); |
159 | } | 176 | } |
160 | 177 | ||