aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm
diff options
context:
space:
mode:
authorBen Skeggs <bskeggs@redhat.com>2010-07-22 19:06:52 -0400
committerBen Skeggs <bskeggs@redhat.com>2010-08-16 21:58:56 -0400
commit415e6186f17136075f7cc825ba3835d005773637 (patch)
treef6f837734b05a7006cab9b6badcda8590e1e4d24 /drivers/gpu/drm
parent56dfc58ea094e7a8607786f4762c65b09cd85738 (diff)
drm/nouveau: fix race condition when under memory pressure
When VRAM is running out it's possible that the client's push buffers get evicted to main memory. When they're validated back in, the GPU may be used for the copy back to VRAM, but the existing synchronisation code only deals with inter-channel sync, not sync between PFIFO and PGRAPH on the same channel. This leads to PFIFO fetching from command buffers that haven't quite been copied by PGRAPH yet. This patch marks push buffers as so, and forces any GPU-assisted buffer moves to be done on a different channel, which triggers the correct synchronisation to happen before we submit them. Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
Diffstat (limited to 'drivers/gpu/drm')
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_bo.c15
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_drv.h1
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_gem.c36
3 files changed, 42 insertions, 10 deletions
diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c
index 84f85183d041..f6f44779d82f 100644
--- a/drivers/gpu/drm/nouveau/nouveau_bo.c
+++ b/drivers/gpu/drm/nouveau/nouveau_bo.c
@@ -36,6 +36,21 @@
36#include <linux/log2.h> 36#include <linux/log2.h>
37#include <linux/slab.h> 37#include <linux/slab.h>
38 38
39int
40nouveau_bo_sync_gpu(struct nouveau_bo *nvbo, struct nouveau_channel *chan)
41{
42 struct nouveau_fence *prev_fence = nvbo->bo.sync_obj;
43 int ret;
44
45 if (!prev_fence || nouveau_fence_channel(prev_fence) == chan)
46 return 0;
47
48 spin_lock(&nvbo->bo.lock);
49 ret = ttm_bo_wait(&nvbo->bo, false, false, false);
50 spin_unlock(&nvbo->bo.lock);
51 return ret;
52}
53
39static void 54static void
40nouveau_bo_del_ttm(struct ttm_buffer_object *bo) 55nouveau_bo_del_ttm(struct ttm_buffer_object *bo)
41{ 56{
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h
index e424bf74d706..1e093a069b7b 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drv.h
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.h
@@ -1165,6 +1165,7 @@ extern u16 nouveau_bo_rd16(struct nouveau_bo *nvbo, unsigned index);
1165extern void nouveau_bo_wr16(struct nouveau_bo *nvbo, unsigned index, u16 val); 1165extern void nouveau_bo_wr16(struct nouveau_bo *nvbo, unsigned index, u16 val);
1166extern u32 nouveau_bo_rd32(struct nouveau_bo *nvbo, unsigned index); 1166extern u32 nouveau_bo_rd32(struct nouveau_bo *nvbo, unsigned index);
1167extern void nouveau_bo_wr32(struct nouveau_bo *nvbo, unsigned index, u32 val); 1167extern void nouveau_bo_wr32(struct nouveau_bo *nvbo, unsigned index, u32 val);
1168extern int nouveau_bo_sync_gpu(struct nouveau_bo *, struct nouveau_channel *);
1168 1169
1169/* nouveau_fence.c */ 1170/* nouveau_fence.c */
1170struct nouveau_fence; 1171struct nouveau_fence;
diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c
index 547f2c24c1e7..a915dcdd9a49 100644
--- a/drivers/gpu/drm/nouveau/nouveau_gem.c
+++ b/drivers/gpu/drm/nouveau/nouveau_gem.c
@@ -361,16 +361,11 @@ validate_list(struct nouveau_channel *chan, struct list_head *list,
361 361
362 list_for_each_entry(nvbo, list, entry) { 362 list_for_each_entry(nvbo, list, entry) {
363 struct drm_nouveau_gem_pushbuf_bo *b = &pbbo[nvbo->pbbo_index]; 363 struct drm_nouveau_gem_pushbuf_bo *b = &pbbo[nvbo->pbbo_index];
364 struct nouveau_fence *prev_fence = nvbo->bo.sync_obj;
365 364
366 if (prev_fence && nouveau_fence_channel(prev_fence) != chan) { 365 ret = nouveau_bo_sync_gpu(nvbo, chan);
367 spin_lock(&nvbo->bo.lock); 366 if (unlikely(ret)) {
368 ret = ttm_bo_wait(&nvbo->bo, false, false, false); 367 NV_ERROR(dev, "fail pre-validate sync\n");
369 spin_unlock(&nvbo->bo.lock); 368 return ret;
370 if (unlikely(ret)) {
371 NV_ERROR(dev, "fail wait other chan\n");
372 return ret;
373 }
374 } 369 }
375 370
376 ret = nouveau_gem_set_domain(nvbo->gem, b->read_domains, 371 ret = nouveau_gem_set_domain(nvbo->gem, b->read_domains,
@@ -381,7 +376,7 @@ validate_list(struct nouveau_channel *chan, struct list_head *list,
381 return ret; 376 return ret;
382 } 377 }
383 378
384 nvbo->channel = chan; 379 nvbo->channel = (b->read_domains & (1 << 31)) ? NULL : chan;
385 ret = ttm_bo_validate(&nvbo->bo, &nvbo->placement, 380 ret = ttm_bo_validate(&nvbo->bo, &nvbo->placement,
386 false, false, false); 381 false, false, false);
387 nvbo->channel = NULL; 382 nvbo->channel = NULL;
@@ -390,6 +385,12 @@ validate_list(struct nouveau_channel *chan, struct list_head *list,
390 return ret; 385 return ret;
391 } 386 }
392 387
388 ret = nouveau_bo_sync_gpu(nvbo, chan);
389 if (unlikely(ret)) {
390 NV_ERROR(dev, "fail post-validate sync\n");
391 return ret;
392 }
393
393 if (nvbo->bo.offset == b->presumed.offset && 394 if (nvbo->bo.offset == b->presumed.offset &&
394 ((nvbo->bo.mem.mem_type == TTM_PL_VRAM && 395 ((nvbo->bo.mem.mem_type == TTM_PL_VRAM &&
395 b->presumed.domain & NOUVEAU_GEM_DOMAIN_VRAM) || 396 b->presumed.domain & NOUVEAU_GEM_DOMAIN_VRAM) ||
@@ -615,6 +616,21 @@ nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data,
615 616
616 mutex_lock(&dev->struct_mutex); 617 mutex_lock(&dev->struct_mutex);
617 618
619 /* Mark push buffers as being used on PFIFO, the validation code
620 * will then make sure that if the pushbuf bo moves, that they
621 * happen on the kernel channel, which will in turn cause a sync
622 * to happen before we try and submit the push buffer.
623 */
624 for (i = 0; i < req->nr_push; i++) {
625 if (push[i].bo_index >= req->nr_buffers) {
626 NV_ERROR(dev, "push %d buffer not in list\n", i);
627 ret = -EINVAL;
628 goto out;
629 }
630
631 bo[push[i].bo_index].read_domains |= (1 << 31);
632 }
633
618 /* Validate buffer list */ 634 /* Validate buffer list */
619 ret = nouveau_gem_pushbuf_validate(chan, file_priv, bo, req->buffers, 635 ret = nouveau_gem_pushbuf_validate(chan, file_priv, bo, req->buffers,
620 req->nr_buffers, &op, &do_reloc); 636 req->nr_buffers, &op, &do_reloc);