Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/nouveau: delay busy bo vma removal until fence signals

As opposed to an explicit wait. Allows userspace to not stall waiting
on buffer deletion.

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>

+108 -15
+1 -6
drivers/gpu/drm/nouveau/nouveau_bo.c
··· 1550 1550 nouveau_bo_vma_del(struct nouveau_bo *nvbo, struct nouveau_vma *vma) 1551 1551 { 1552 1552 if (vma->node) { 1553 - if (nvbo->bo.mem.mem_type != TTM_PL_SYSTEM) { 1554 - spin_lock(&nvbo->bo.bdev->fence_lock); 1555 - ttm_bo_wait(&nvbo->bo, false, false, false); 1556 - spin_unlock(&nvbo->bo.bdev->fence_lock); 1553 + if (nvbo->bo.mem.mem_type != TTM_PL_SYSTEM) 1557 1554 nouveau_vm_unmap(vma); 1558 - } 1559 - 1560 1555 nouveau_vm_put(vma); 1561 1556 list_del(&vma->head); 1562 1557 }
+68 -5
drivers/gpu/drm/nouveau/nouveau_fence.c
··· 35 35 36 36 #include <engine/fifo.h> 37 37 38 + struct fence_work { 39 + struct work_struct base; 40 + struct list_head head; 41 + void (*func)(void *); 42 + void *data; 43 + }; 44 + 45 + static void 46 + nouveau_fence_signal(struct nouveau_fence *fence) 47 + { 48 + struct fence_work *work, *temp; 49 + 50 + list_for_each_entry_safe(work, temp, &fence->work, head) { 51 + schedule_work(&work->base); 52 + list_del(&work->head); 53 + } 54 + 55 + fence->channel = NULL; 56 + list_del(&fence->head); 57 + } 58 + 38 59 void 39 60 nouveau_fence_context_del(struct nouveau_fence_chan *fctx) 40 61 { 41 62 struct nouveau_fence *fence, *fnext; 42 63 spin_lock(&fctx->lock); 43 64 list_for_each_entry_safe(fence, fnext, &fctx->pending, head) { 44 - fence->channel = NULL; 45 - list_del(&fence->head); 46 - nouveau_fence_unref(&fence); 65 + nouveau_fence_signal(fence); 47 66 } 48 67 spin_unlock(&fctx->lock); 49 68 } ··· 76 57 } 77 58 78 59 static void 60 + nouveau_fence_work_handler(struct work_struct *kwork) 61 + { 62 + struct fence_work *work = container_of(kwork, typeof(*work), base); 63 + work->func(work->data); 64 + kfree(work); 65 + } 66 + 67 + void 68 + nouveau_fence_work(struct nouveau_fence *fence, 69 + void (*func)(void *), void *data) 70 + { 71 + struct nouveau_channel *chan = fence->channel; 72 + struct nouveau_fence_chan *fctx; 73 + struct fence_work *work = NULL; 74 + 75 + if (nouveau_fence_done(fence)) { 76 + func(data); 77 + return; 78 + } 79 + 80 + fctx = chan->fence; 81 + work = kmalloc(sizeof(*work), GFP_KERNEL); 82 + if (!work) { 83 + WARN_ON(nouveau_fence_wait(fence, false, false)); 84 + func(data); 85 + return; 86 + } 87 + 88 + spin_lock(&fctx->lock); 89 + if (!fence->channel) { 90 + spin_unlock(&fctx->lock); 91 + kfree(work); 92 + func(data); 93 + return; 94 + } 95 + 96 + INIT_WORK(&work->base, nouveau_fence_work_handler); 97 + work->func = func; 98 + work->data = data; 99 + list_add(&work->head, &fence->work); 100 + spin_unlock(&fctx->lock); 101 + } 102 + 103 + static void 79 104 nouveau_fence_update(struct nouveau_channel *chan) 80 105 { 81 106 struct nouveau_fence_chan *fctx = chan->fence; ··· 130 67 if (fctx->read(chan) < fence->sequence) 131 68 break; 132 69 133 - fence->channel = NULL; 134 - list_del(&fence->head); 70 + nouveau_fence_signal(fence); 135 71 nouveau_fence_unref(&fence); 136 72 } 137 73 spin_unlock(&fctx->lock); ··· 327 265 if (!fence) 328 266 return -ENOMEM; 329 267 268 + INIT_LIST_HEAD(&fence->work); 330 269 fence->sysmem = sysmem; 331 270 kref_init(&fence->kref); 332 271
+2
drivers/gpu/drm/nouveau/nouveau_fence.h
··· 5 5 6 6 struct nouveau_fence { 7 7 struct list_head head; 8 + struct list_head work; 8 9 struct kref kref; 9 10 10 11 bool sysmem; ··· 23 22 24 23 int nouveau_fence_emit(struct nouveau_fence *, struct nouveau_channel *); 25 24 bool nouveau_fence_done(struct nouveau_fence *); 25 + void nouveau_fence_work(struct nouveau_fence *, void (*)(void *), void *); 26 26 int nouveau_fence_wait(struct nouveau_fence *, bool lazy, bool intr); 27 27 int nouveau_fence_sync(struct nouveau_fence *, struct nouveau_channel *); 28 28
+37 -4
drivers/gpu/drm/nouveau/nouveau_gem.c
··· 101 101 return ret; 102 102 } 103 103 104 + static void 105 + nouveau_gem_object_delete(void *data) 106 + { 107 + struct nouveau_vma *vma = data; 108 + nouveau_vm_unmap(vma); 109 + nouveau_vm_put(vma); 110 + kfree(vma); 111 + } 112 + 113 + static void 114 + nouveau_gem_object_unmap(struct nouveau_bo *nvbo, struct nouveau_vma *vma) 115 + { 116 + const bool mapped = nvbo->bo.mem.mem_type != TTM_PL_SYSTEM; 117 + struct nouveau_fence *fence = NULL; 118 + 119 + list_del(&vma->head); 120 + 121 + if (mapped) { 122 + spin_lock(&nvbo->bo.bdev->fence_lock); 123 + if (nvbo->bo.sync_obj) 124 + fence = nouveau_fence_ref(nvbo->bo.sync_obj); 125 + spin_unlock(&nvbo->bo.bdev->fence_lock); 126 + } 127 + 128 + if (fence) { 129 + nouveau_fence_work(fence, nouveau_gem_object_delete, vma); 130 + } else { 131 + if (mapped) 132 + nouveau_vm_unmap(vma); 133 + nouveau_vm_put(vma); 134 + kfree(vma); 135 + } 136 + nouveau_fence_unref(&fence); 137 + } 138 + 104 139 void 105 140 nouveau_gem_object_close(struct drm_gem_object *gem, struct drm_file *file_priv) 106 141 { ··· 153 118 154 119 vma = nouveau_bo_vma_find(nvbo, cli->base.vm); 155 120 if (vma) { 156 - if (--vma->refcount == 0) { 157 - nouveau_bo_vma_del(nvbo, vma); 158 - kfree(vma); 159 - } 121 + if (--vma->refcount == 0) 122 + nouveau_gem_object_unmap(nvbo, vma); 160 123 } 161 124 ttm_bo_unreserve(&nvbo->bo); 162 125 }