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 | |
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')
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_channel.c | 24 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_drv.h | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_state.c | 8 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nv04_fifo.c | 21 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nv04_graph.c | 15 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nv10_fifo.c | 11 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nv10_graph.c | 15 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nv20_graph.c | 14 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nv40_fifo.c | 11 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nv40_graph.c | 16 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nv50_fifo.c | 17 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nv50_graph.c | 11 |
12 files changed, 116 insertions, 49 deletions
diff --git a/drivers/gpu/drm/nouveau/nouveau_channel.c b/drivers/gpu/drm/nouveau/nouveau_channel.c index 0e2414b0ae5..9a051fafa7c 100644 --- a/drivers/gpu/drm/nouveau/nouveau_channel.c +++ b/drivers/gpu/drm/nouveau/nouveau_channel.c | |||
@@ -313,32 +313,20 @@ nouveau_channel_put(struct nouveau_channel **pchan) | |||
313 | /* boot it off the hardware */ | 313 | /* boot it off the hardware */ |
314 | pfifo->reassign(dev, false); | 314 | pfifo->reassign(dev, false); |
315 | 315 | ||
316 | /* We want to give pgraph a chance to idle and get rid of all potential | 316 | /* We want to give pgraph a chance to idle and get rid of all |
317 | * errors. We need to do this before the lock, otherwise the irq handler | 317 | * potential errors. We need to do this without the context |
318 | * is unable to process them. | 318 | * switch lock held, otherwise the irq handler is unable to |
319 | * process them. | ||
319 | */ | 320 | */ |
320 | if (pgraph->channel(dev) == chan) | 321 | if (pgraph->channel(dev) == chan) |
321 | nouveau_wait_for_idle(dev); | 322 | nouveau_wait_for_idle(dev); |
322 | 323 | ||
323 | spin_lock_irqsave(&dev_priv->context_switch_lock, flags); | 324 | /* destroy the engine specific contexts */ |
324 | |||
325 | pgraph->fifo_access(dev, false); | ||
326 | if (pgraph->channel(dev) == chan) | ||
327 | pgraph->unload_context(dev); | ||
328 | pgraph->destroy_context(chan); | ||
329 | pgraph->fifo_access(dev, true); | ||
330 | |||
331 | if (pfifo->channel_id(dev) == chan->id) { | ||
332 | pfifo->disable(dev); | ||
333 | pfifo->unload_context(dev); | ||
334 | pfifo->enable(dev); | ||
335 | } | ||
336 | pfifo->destroy_context(chan); | 325 | pfifo->destroy_context(chan); |
326 | pgraph->destroy_context(chan); | ||
337 | 327 | ||
338 | pfifo->reassign(dev, true); | 328 | pfifo->reassign(dev, true); |
339 | 329 | ||
340 | spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); | ||
341 | |||
342 | /* aside from its resources, the channel should now be dead, | 330 | /* aside from its resources, the channel should now be dead, |
343 | * remove it from the channel list | 331 | * remove it from the channel list |
344 | */ | 332 | */ |
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 699d546623b..198dabebafb 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h | |||
@@ -998,14 +998,12 @@ extern int nv04_fifo_unload_context(struct drm_device *); | |||
998 | extern int nv10_fifo_init(struct drm_device *); | 998 | extern int nv10_fifo_init(struct drm_device *); |
999 | extern int nv10_fifo_channel_id(struct drm_device *); | 999 | extern int nv10_fifo_channel_id(struct drm_device *); |
1000 | extern int nv10_fifo_create_context(struct nouveau_channel *); | 1000 | extern int nv10_fifo_create_context(struct nouveau_channel *); |
1001 | extern void nv10_fifo_destroy_context(struct nouveau_channel *); | ||
1002 | extern int nv10_fifo_load_context(struct nouveau_channel *); | 1001 | extern int nv10_fifo_load_context(struct nouveau_channel *); |
1003 | extern int nv10_fifo_unload_context(struct drm_device *); | 1002 | extern int nv10_fifo_unload_context(struct drm_device *); |
1004 | 1003 | ||
1005 | /* nv40_fifo.c */ | 1004 | /* nv40_fifo.c */ |
1006 | extern int nv40_fifo_init(struct drm_device *); | 1005 | extern int nv40_fifo_init(struct drm_device *); |
1007 | extern int nv40_fifo_create_context(struct nouveau_channel *); | 1006 | extern int nv40_fifo_create_context(struct nouveau_channel *); |
1008 | extern void nv40_fifo_destroy_context(struct nouveau_channel *); | ||
1009 | extern int nv40_fifo_load_context(struct nouveau_channel *); | 1007 | extern int nv40_fifo_load_context(struct nouveau_channel *); |
1010 | extern int nv40_fifo_unload_context(struct drm_device *); | 1008 | extern int nv40_fifo_unload_context(struct drm_device *); |
1011 | 1009 | ||
diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c index 513c1063fb5..1a4ba6ccafb 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c | |||
@@ -137,7 +137,7 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) | |||
137 | engine->fifo.cache_pull = nv04_fifo_cache_pull; | 137 | engine->fifo.cache_pull = nv04_fifo_cache_pull; |
138 | engine->fifo.channel_id = nv10_fifo_channel_id; | 138 | engine->fifo.channel_id = nv10_fifo_channel_id; |
139 | engine->fifo.create_context = nv10_fifo_create_context; | 139 | engine->fifo.create_context = nv10_fifo_create_context; |
140 | engine->fifo.destroy_context = nv10_fifo_destroy_context; | 140 | engine->fifo.destroy_context = nv04_fifo_destroy_context; |
141 | engine->fifo.load_context = nv10_fifo_load_context; | 141 | engine->fifo.load_context = nv10_fifo_load_context; |
142 | engine->fifo.unload_context = nv10_fifo_unload_context; | 142 | engine->fifo.unload_context = nv10_fifo_unload_context; |
143 | engine->display.early_init = nv04_display_early_init; | 143 | engine->display.early_init = nv04_display_early_init; |
@@ -191,7 +191,7 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) | |||
191 | engine->fifo.cache_pull = nv04_fifo_cache_pull; | 191 | engine->fifo.cache_pull = nv04_fifo_cache_pull; |
192 | engine->fifo.channel_id = nv10_fifo_channel_id; | 192 | engine->fifo.channel_id = nv10_fifo_channel_id; |
193 | engine->fifo.create_context = nv10_fifo_create_context; | 193 | engine->fifo.create_context = nv10_fifo_create_context; |
194 | engine->fifo.destroy_context = nv10_fifo_destroy_context; | 194 | engine->fifo.destroy_context = nv04_fifo_destroy_context; |
195 | engine->fifo.load_context = nv10_fifo_load_context; | 195 | engine->fifo.load_context = nv10_fifo_load_context; |
196 | engine->fifo.unload_context = nv10_fifo_unload_context; | 196 | engine->fifo.unload_context = nv10_fifo_unload_context; |
197 | engine->display.early_init = nv04_display_early_init; | 197 | engine->display.early_init = nv04_display_early_init; |
@@ -245,7 +245,7 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) | |||
245 | engine->fifo.cache_pull = nv04_fifo_cache_pull; | 245 | engine->fifo.cache_pull = nv04_fifo_cache_pull; |
246 | engine->fifo.channel_id = nv10_fifo_channel_id; | 246 | engine->fifo.channel_id = nv10_fifo_channel_id; |
247 | engine->fifo.create_context = nv10_fifo_create_context; | 247 | engine->fifo.create_context = nv10_fifo_create_context; |
248 | engine->fifo.destroy_context = nv10_fifo_destroy_context; | 248 | engine->fifo.destroy_context = nv04_fifo_destroy_context; |
249 | engine->fifo.load_context = nv10_fifo_load_context; | 249 | engine->fifo.load_context = nv10_fifo_load_context; |
250 | engine->fifo.unload_context = nv10_fifo_unload_context; | 250 | engine->fifo.unload_context = nv10_fifo_unload_context; |
251 | engine->display.early_init = nv04_display_early_init; | 251 | engine->display.early_init = nv04_display_early_init; |
@@ -302,7 +302,7 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) | |||
302 | engine->fifo.cache_pull = nv04_fifo_cache_pull; | 302 | engine->fifo.cache_pull = nv04_fifo_cache_pull; |
303 | engine->fifo.channel_id = nv10_fifo_channel_id; | 303 | engine->fifo.channel_id = nv10_fifo_channel_id; |
304 | engine->fifo.create_context = nv40_fifo_create_context; | 304 | engine->fifo.create_context = nv40_fifo_create_context; |
305 | engine->fifo.destroy_context = nv40_fifo_destroy_context; | 305 | engine->fifo.destroy_context = nv04_fifo_destroy_context; |
306 | engine->fifo.load_context = nv40_fifo_load_context; | 306 | engine->fifo.load_context = nv40_fifo_load_context; |
307 | engine->fifo.unload_context = nv40_fifo_unload_context; | 307 | engine->fifo.unload_context = nv40_fifo_unload_context; |
308 | engine->display.early_init = nv04_display_early_init; | 308 | engine->display.early_init = nv04_display_early_init; |
diff --git a/drivers/gpu/drm/nouveau/nv04_fifo.c b/drivers/gpu/drm/nouveau/nv04_fifo.c index 25c439dcdfd..4c0d3a8fca6 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 | ||
diff --git a/drivers/gpu/drm/nouveau/nv04_graph.c b/drivers/gpu/drm/nouveau/nv04_graph.c index 98b9525c1eb..1e2ad394233 100644 --- a/drivers/gpu/drm/nouveau/nv04_graph.c +++ b/drivers/gpu/drm/nouveau/nv04_graph.c | |||
@@ -412,10 +412,25 @@ int nv04_graph_create_context(struct nouveau_channel *chan) | |||
412 | 412 | ||
413 | void nv04_graph_destroy_context(struct nouveau_channel *chan) | 413 | void nv04_graph_destroy_context(struct nouveau_channel *chan) |
414 | { | 414 | { |
415 | struct drm_device *dev = chan->dev; | ||
416 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
417 | struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph; | ||
415 | struct graph_state *pgraph_ctx = chan->pgraph_ctx; | 418 | struct graph_state *pgraph_ctx = chan->pgraph_ctx; |
419 | unsigned long flags; | ||
420 | |||
421 | spin_lock_irqsave(&dev_priv->context_switch_lock, flags); | ||
422 | pgraph->fifo_access(dev, false); | ||
423 | |||
424 | /* Unload the context if it's the currently active one */ | ||
425 | if (pgraph->channel(dev) == chan) | ||
426 | pgraph->unload_context(dev); | ||
416 | 427 | ||
428 | /* Free the context resources */ | ||
417 | kfree(pgraph_ctx); | 429 | kfree(pgraph_ctx); |
418 | chan->pgraph_ctx = NULL; | 430 | chan->pgraph_ctx = NULL; |
431 | |||
432 | pgraph->fifo_access(dev, true); | ||
433 | spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); | ||
419 | } | 434 | } |
420 | 435 | ||
421 | int nv04_graph_load_context(struct nouveau_channel *chan) | 436 | int nv04_graph_load_context(struct nouveau_channel *chan) |
diff --git a/drivers/gpu/drm/nouveau/nv10_fifo.c b/drivers/gpu/drm/nouveau/nv10_fifo.c index 39328fcce70..912556f2e33 100644 --- a/drivers/gpu/drm/nouveau/nv10_fifo.c +++ b/drivers/gpu/drm/nouveau/nv10_fifo.c | |||
@@ -73,17 +73,6 @@ nv10_fifo_create_context(struct nouveau_channel *chan) | |||
73 | return 0; | 73 | return 0; |
74 | } | 74 | } |
75 | 75 | ||
76 | void | ||
77 | nv10_fifo_destroy_context(struct nouveau_channel *chan) | ||
78 | { | ||
79 | struct drm_device *dev = chan->dev; | ||
80 | |||
81 | nv_wr32(dev, NV04_PFIFO_MODE, | ||
82 | nv_rd32(dev, NV04_PFIFO_MODE) & ~(1 << chan->id)); | ||
83 | |||
84 | nouveau_gpuobj_ref(NULL, &chan->ramfc); | ||
85 | } | ||
86 | |||
87 | static void | 76 | static void |
88 | nv10_fifo_do_load_context(struct drm_device *dev, int chid) | 77 | nv10_fifo_do_load_context(struct drm_device *dev, int chid) |
89 | { | 78 | { |
diff --git a/drivers/gpu/drm/nouveau/nv10_graph.c b/drivers/gpu/drm/nouveau/nv10_graph.c index cd931b57cf0..e3a87a64c16 100644 --- a/drivers/gpu/drm/nouveau/nv10_graph.c +++ b/drivers/gpu/drm/nouveau/nv10_graph.c | |||
@@ -875,10 +875,25 @@ int nv10_graph_create_context(struct nouveau_channel *chan) | |||
875 | 875 | ||
876 | void nv10_graph_destroy_context(struct nouveau_channel *chan) | 876 | void nv10_graph_destroy_context(struct nouveau_channel *chan) |
877 | { | 877 | { |
878 | struct drm_device *dev = chan->dev; | ||
879 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
880 | struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph; | ||
878 | struct graph_state *pgraph_ctx = chan->pgraph_ctx; | 881 | struct graph_state *pgraph_ctx = chan->pgraph_ctx; |
882 | unsigned long flags; | ||
883 | |||
884 | spin_lock_irqsave(&dev_priv->context_switch_lock, flags); | ||
885 | pgraph->fifo_access(dev, false); | ||
886 | |||
887 | /* Unload the context if it's the currently active one */ | ||
888 | if (pgraph->channel(dev) == chan) | ||
889 | pgraph->unload_context(dev); | ||
879 | 890 | ||
891 | /* Free the context resources */ | ||
880 | kfree(pgraph_ctx); | 892 | kfree(pgraph_ctx); |
881 | chan->pgraph_ctx = NULL; | 893 | chan->pgraph_ctx = NULL; |
894 | |||
895 | pgraph->fifo_access(dev, true); | ||
896 | spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); | ||
882 | } | 897 | } |
883 | 898 | ||
884 | void | 899 | void |
diff --git a/drivers/gpu/drm/nouveau/nv20_graph.c b/drivers/gpu/drm/nouveau/nv20_graph.c index 12ab9cd56ec..8a040201255 100644 --- a/drivers/gpu/drm/nouveau/nv20_graph.c +++ b/drivers/gpu/drm/nouveau/nv20_graph.c | |||
@@ -425,9 +425,21 @@ nv20_graph_destroy_context(struct nouveau_channel *chan) | |||
425 | struct drm_device *dev = chan->dev; | 425 | struct drm_device *dev = chan->dev; |
426 | struct drm_nouveau_private *dev_priv = dev->dev_private; | 426 | struct drm_nouveau_private *dev_priv = dev->dev_private; |
427 | struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph; | 427 | struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph; |
428 | unsigned long flags; | ||
428 | 429 | ||
429 | nouveau_gpuobj_ref(NULL, &chan->ramin_grctx); | 430 | spin_lock_irqsave(&dev_priv->context_switch_lock, flags); |
431 | pgraph->fifo_access(dev, false); | ||
432 | |||
433 | /* Unload the context if it's the currently active one */ | ||
434 | if (pgraph->channel(dev) == chan) | ||
435 | pgraph->unload_context(dev); | ||
436 | |||
437 | pgraph->fifo_access(dev, true); | ||
438 | spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); | ||
439 | |||
440 | /* Free the context resources */ | ||
430 | nv_wo32(pgraph->ctx_table, chan->id * 4, 0); | 441 | nv_wo32(pgraph->ctx_table, chan->id * 4, 0); |
442 | nouveau_gpuobj_ref(NULL, &chan->ramin_grctx); | ||
431 | } | 443 | } |
432 | 444 | ||
433 | int | 445 | int |
diff --git a/drivers/gpu/drm/nouveau/nv40_fifo.c b/drivers/gpu/drm/nouveau/nv40_fifo.c index 3c7be3dc8b8..311ac9ea5d5 100644 --- a/drivers/gpu/drm/nouveau/nv40_fifo.c +++ b/drivers/gpu/drm/nouveau/nv40_fifo.c | |||
@@ -70,17 +70,6 @@ nv40_fifo_create_context(struct nouveau_channel *chan) | |||
70 | return 0; | 70 | return 0; |
71 | } | 71 | } |
72 | 72 | ||
73 | void | ||
74 | nv40_fifo_destroy_context(struct nouveau_channel *chan) | ||
75 | { | ||
76 | struct drm_device *dev = chan->dev; | ||
77 | |||
78 | nv_wr32(dev, NV04_PFIFO_MODE, | ||
79 | nv_rd32(dev, NV04_PFIFO_MODE) & ~(1 << chan->id)); | ||
80 | |||
81 | nouveau_gpuobj_ref(NULL, &chan->ramfc); | ||
82 | } | ||
83 | |||
84 | static void | 73 | static void |
85 | nv40_fifo_do_load_context(struct drm_device *dev, int chid) | 74 | nv40_fifo_do_load_context(struct drm_device *dev, int chid) |
86 | { | 75 | { |
diff --git a/drivers/gpu/drm/nouveau/nv40_graph.c b/drivers/gpu/drm/nouveau/nv40_graph.c index e0b41a26447..70d97cde49d 100644 --- a/drivers/gpu/drm/nouveau/nv40_graph.c +++ b/drivers/gpu/drm/nouveau/nv40_graph.c | |||
@@ -79,6 +79,22 @@ nv40_graph_create_context(struct nouveau_channel *chan) | |||
79 | void | 79 | void |
80 | nv40_graph_destroy_context(struct nouveau_channel *chan) | 80 | nv40_graph_destroy_context(struct nouveau_channel *chan) |
81 | { | 81 | { |
82 | struct drm_device *dev = chan->dev; | ||
83 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
84 | struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph; | ||
85 | unsigned long flags; | ||
86 | |||
87 | spin_lock_irqsave(&dev_priv->context_switch_lock, flags); | ||
88 | pgraph->fifo_access(dev, false); | ||
89 | |||
90 | /* Unload the context if it's the currently active one */ | ||
91 | if (pgraph->channel(dev) == chan) | ||
92 | pgraph->unload_context(dev); | ||
93 | |||
94 | pgraph->fifo_access(dev, true); | ||
95 | spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); | ||
96 | |||
97 | /* Free the context resources */ | ||
82 | nouveau_gpuobj_ref(NULL, &chan->ramin_grctx); | 98 | nouveau_gpuobj_ref(NULL, &chan->ramin_grctx); |
83 | } | 99 | } |
84 | 100 | ||
diff --git a/drivers/gpu/drm/nouveau/nv50_fifo.c b/drivers/gpu/drm/nouveau/nv50_fifo.c index 815960fe4f4..d3295aae0c4 100644 --- a/drivers/gpu/drm/nouveau/nv50_fifo.c +++ b/drivers/gpu/drm/nouveau/nv50_fifo.c | |||
@@ -292,10 +292,23 @@ void | |||
292 | nv50_fifo_destroy_context(struct nouveau_channel *chan) | 292 | nv50_fifo_destroy_context(struct nouveau_channel *chan) |
293 | { | 293 | { |
294 | struct drm_device *dev = chan->dev; | 294 | struct drm_device *dev = chan->dev; |
295 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
296 | struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo; | ||
295 | struct nouveau_gpuobj *ramfc = NULL; | 297 | struct nouveau_gpuobj *ramfc = NULL; |
298 | unsigned long flags; | ||
296 | 299 | ||
297 | NV_DEBUG(dev, "ch%d\n", chan->id); | 300 | NV_DEBUG(dev, "ch%d\n", chan->id); |
298 | 301 | ||
302 | spin_lock_irqsave(&dev_priv->context_switch_lock, flags); | ||
303 | pfifo->reassign(dev, false); | ||
304 | |||
305 | /* Unload the context if it's the currently active one */ | ||
306 | if (pfifo->channel_id(dev) == chan->id) { | ||
307 | pfifo->disable(dev); | ||
308 | pfifo->unload_context(dev); | ||
309 | pfifo->enable(dev); | ||
310 | } | ||
311 | |||
299 | /* This will ensure the channel is seen as disabled. */ | 312 | /* This will ensure the channel is seen as disabled. */ |
300 | nouveau_gpuobj_ref(chan->ramfc, &ramfc); | 313 | nouveau_gpuobj_ref(chan->ramfc, &ramfc); |
301 | nouveau_gpuobj_ref(NULL, &chan->ramfc); | 314 | nouveau_gpuobj_ref(NULL, &chan->ramfc); |
@@ -306,6 +319,10 @@ nv50_fifo_destroy_context(struct nouveau_channel *chan) | |||
306 | nv50_fifo_channel_disable(dev, 127); | 319 | nv50_fifo_channel_disable(dev, 127); |
307 | nv50_fifo_playlist_update(dev); | 320 | nv50_fifo_playlist_update(dev); |
308 | 321 | ||
322 | pfifo->reassign(dev, true); | ||
323 | spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); | ||
324 | |||
325 | /* Free the channel resources */ | ||
309 | nouveau_gpuobj_ref(NULL, &ramfc); | 326 | nouveau_gpuobj_ref(NULL, &ramfc); |
310 | nouveau_gpuobj_ref(NULL, &chan->cache); | 327 | nouveau_gpuobj_ref(NULL, &chan->cache); |
311 | } | 328 | } |
diff --git a/drivers/gpu/drm/nouveau/nv50_graph.c b/drivers/gpu/drm/nouveau/nv50_graph.c index 24a3f848757..dcc9175fb79 100644 --- a/drivers/gpu/drm/nouveau/nv50_graph.c +++ b/drivers/gpu/drm/nouveau/nv50_graph.c | |||
@@ -242,17 +242,28 @@ nv50_graph_destroy_context(struct nouveau_channel *chan) | |||
242 | { | 242 | { |
243 | struct drm_device *dev = chan->dev; | 243 | struct drm_device *dev = chan->dev; |
244 | struct drm_nouveau_private *dev_priv = dev->dev_private; | 244 | struct drm_nouveau_private *dev_priv = dev->dev_private; |
245 | struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph; | ||
245 | int i, hdr = (dev_priv->chipset == 0x50) ? 0x200 : 0x20; | 246 | int i, hdr = (dev_priv->chipset == 0x50) ? 0x200 : 0x20; |
247 | unsigned long flags; | ||
246 | 248 | ||
247 | NV_DEBUG(dev, "ch%d\n", chan->id); | 249 | NV_DEBUG(dev, "ch%d\n", chan->id); |
248 | 250 | ||
249 | if (!chan->ramin) | 251 | if (!chan->ramin) |
250 | return; | 252 | return; |
251 | 253 | ||
254 | spin_lock_irqsave(&dev_priv->context_switch_lock, flags); | ||
255 | pgraph->fifo_access(dev, false); | ||
256 | |||
257 | if (pgraph->channel(dev) == chan) | ||
258 | pgraph->unload_context(dev); | ||
259 | |||
252 | for (i = hdr; i < hdr + 24; i += 4) | 260 | for (i = hdr; i < hdr + 24; i += 4) |
253 | nv_wo32(chan->ramin, i, 0); | 261 | nv_wo32(chan->ramin, i, 0); |
254 | dev_priv->engine.instmem.flush(dev); | 262 | dev_priv->engine.instmem.flush(dev); |
255 | 263 | ||
264 | pgraph->fifo_access(dev, true); | ||
265 | spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); | ||
266 | |||
256 | nouveau_gpuobj_ref(NULL, &chan->ramin_grctx); | 267 | nouveau_gpuobj_ref(NULL, &chan->ramin_grctx); |
257 | } | 268 | } |
258 | 269 | ||