Commit c4c7044f authored by Ben Skeggs's avatar Ben Skeggs

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: default avatarBen Skeggs <bskeggs@redhat.com>
parent 780194b1
...@@ -1550,13 +1550,8 @@ void ...@@ -1550,13 +1550,8 @@ void
nouveau_bo_vma_del(struct nouveau_bo *nvbo, struct nouveau_vma *vma) nouveau_bo_vma_del(struct nouveau_bo *nvbo, struct nouveau_vma *vma)
{ {
if (vma->node) { if (vma->node) {
if (nvbo->bo.mem.mem_type != TTM_PL_SYSTEM) { if (nvbo->bo.mem.mem_type != TTM_PL_SYSTEM)
spin_lock(&nvbo->bo.bdev->fence_lock);
ttm_bo_wait(&nvbo->bo, false, false, false);
spin_unlock(&nvbo->bo.bdev->fence_lock);
nouveau_vm_unmap(vma); nouveau_vm_unmap(vma);
}
nouveau_vm_put(vma); nouveau_vm_put(vma);
list_del(&vma->head); list_del(&vma->head);
} }
......
...@@ -35,15 +35,34 @@ ...@@ -35,15 +35,34 @@
#include <engine/fifo.h> #include <engine/fifo.h>
struct fence_work {
struct work_struct base;
struct list_head head;
void (*func)(void *);
void *data;
};
static void
nouveau_fence_signal(struct nouveau_fence *fence)
{
struct fence_work *work, *temp;
list_for_each_entry_safe(work, temp, &fence->work, head) {
schedule_work(&work->base);
list_del(&work->head);
}
fence->channel = NULL;
list_del(&fence->head);
}
void void
nouveau_fence_context_del(struct nouveau_fence_chan *fctx) nouveau_fence_context_del(struct nouveau_fence_chan *fctx)
{ {
struct nouveau_fence *fence, *fnext; struct nouveau_fence *fence, *fnext;
spin_lock(&fctx->lock); spin_lock(&fctx->lock);
list_for_each_entry_safe(fence, fnext, &fctx->pending, head) { list_for_each_entry_safe(fence, fnext, &fctx->pending, head) {
fence->channel = NULL; nouveau_fence_signal(fence);
list_del(&fence->head);
nouveau_fence_unref(&fence);
} }
spin_unlock(&fctx->lock); spin_unlock(&fctx->lock);
} }
...@@ -56,6 +75,50 @@ nouveau_fence_context_new(struct nouveau_fence_chan *fctx) ...@@ -56,6 +75,50 @@ nouveau_fence_context_new(struct nouveau_fence_chan *fctx)
spin_lock_init(&fctx->lock); spin_lock_init(&fctx->lock);
} }
static void
nouveau_fence_work_handler(struct work_struct *kwork)
{
struct fence_work *work = container_of(kwork, typeof(*work), base);
work->func(work->data);
kfree(work);
}
void
nouveau_fence_work(struct nouveau_fence *fence,
void (*func)(void *), void *data)
{
struct nouveau_channel *chan = fence->channel;
struct nouveau_fence_chan *fctx;
struct fence_work *work = NULL;
if (nouveau_fence_done(fence)) {
func(data);
return;
}
fctx = chan->fence;
work = kmalloc(sizeof(*work), GFP_KERNEL);
if (!work) {
WARN_ON(nouveau_fence_wait(fence, false, false));
func(data);
return;
}
spin_lock(&fctx->lock);
if (!fence->channel) {
spin_unlock(&fctx->lock);
kfree(work);
func(data);
return;
}
INIT_WORK(&work->base, nouveau_fence_work_handler);
work->func = func;
work->data = data;
list_add(&work->head, &fence->work);
spin_unlock(&fctx->lock);
}
static void static void
nouveau_fence_update(struct nouveau_channel *chan) nouveau_fence_update(struct nouveau_channel *chan)
{ {
...@@ -67,8 +130,7 @@ nouveau_fence_update(struct nouveau_channel *chan) ...@@ -67,8 +130,7 @@ nouveau_fence_update(struct nouveau_channel *chan)
if (fctx->read(chan) < fence->sequence) if (fctx->read(chan) < fence->sequence)
break; break;
fence->channel = NULL; nouveau_fence_signal(fence);
list_del(&fence->head);
nouveau_fence_unref(&fence); nouveau_fence_unref(&fence);
} }
spin_unlock(&fctx->lock); spin_unlock(&fctx->lock);
...@@ -265,6 +327,7 @@ nouveau_fence_new(struct nouveau_channel *chan, bool sysmem, ...@@ -265,6 +327,7 @@ nouveau_fence_new(struct nouveau_channel *chan, bool sysmem,
if (!fence) if (!fence)
return -ENOMEM; return -ENOMEM;
INIT_LIST_HEAD(&fence->work);
fence->sysmem = sysmem; fence->sysmem = sysmem;
kref_init(&fence->kref); kref_init(&fence->kref);
......
...@@ -5,6 +5,7 @@ struct nouveau_drm; ...@@ -5,6 +5,7 @@ struct nouveau_drm;
struct nouveau_fence { struct nouveau_fence {
struct list_head head; struct list_head head;
struct list_head work;
struct kref kref; struct kref kref;
bool sysmem; bool sysmem;
...@@ -22,6 +23,7 @@ void nouveau_fence_unref(struct nouveau_fence **); ...@@ -22,6 +23,7 @@ void nouveau_fence_unref(struct nouveau_fence **);
int nouveau_fence_emit(struct nouveau_fence *, struct nouveau_channel *); int nouveau_fence_emit(struct nouveau_fence *, struct nouveau_channel *);
bool nouveau_fence_done(struct nouveau_fence *); bool nouveau_fence_done(struct nouveau_fence *);
void nouveau_fence_work(struct nouveau_fence *, void (*)(void *), void *);
int nouveau_fence_wait(struct nouveau_fence *, bool lazy, bool intr); int nouveau_fence_wait(struct nouveau_fence *, bool lazy, bool intr);
int nouveau_fence_sync(struct nouveau_fence *, struct nouveau_channel *); int nouveau_fence_sync(struct nouveau_fence *, struct nouveau_channel *);
......
...@@ -101,6 +101,41 @@ nouveau_gem_object_open(struct drm_gem_object *gem, struct drm_file *file_priv) ...@@ -101,6 +101,41 @@ nouveau_gem_object_open(struct drm_gem_object *gem, struct drm_file *file_priv)
return ret; return ret;
} }
static void
nouveau_gem_object_delete(void *data)
{
struct nouveau_vma *vma = data;
nouveau_vm_unmap(vma);
nouveau_vm_put(vma);
kfree(vma);
}
static void
nouveau_gem_object_unmap(struct nouveau_bo *nvbo, struct nouveau_vma *vma)
{
const bool mapped = nvbo->bo.mem.mem_type != TTM_PL_SYSTEM;
struct nouveau_fence *fence = NULL;
list_del(&vma->head);
if (mapped) {
spin_lock(&nvbo->bo.bdev->fence_lock);
if (nvbo->bo.sync_obj)
fence = nouveau_fence_ref(nvbo->bo.sync_obj);
spin_unlock(&nvbo->bo.bdev->fence_lock);
}
if (fence) {
nouveau_fence_work(fence, nouveau_gem_object_delete, vma);
} else {
if (mapped)
nouveau_vm_unmap(vma);
nouveau_vm_put(vma);
kfree(vma);
}
nouveau_fence_unref(&fence);
}
void void
nouveau_gem_object_close(struct drm_gem_object *gem, struct drm_file *file_priv) nouveau_gem_object_close(struct drm_gem_object *gem, struct drm_file *file_priv)
{ {
...@@ -118,10 +153,8 @@ nouveau_gem_object_close(struct drm_gem_object *gem, struct drm_file *file_priv) ...@@ -118,10 +153,8 @@ nouveau_gem_object_close(struct drm_gem_object *gem, struct drm_file *file_priv)
vma = nouveau_bo_vma_find(nvbo, cli->base.vm); vma = nouveau_bo_vma_find(nvbo, cli->base.vm);
if (vma) { if (vma) {
if (--vma->refcount == 0) { if (--vma->refcount == 0)
nouveau_bo_vma_del(nvbo, vma); nouveau_gem_object_unmap(nvbo, vma);
kfree(vma);
}
} }
ttm_bo_unreserve(&nvbo->bo); ttm_bo_unreserve(&nvbo->bo);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment