Module Name: src Committed By: riastradh Date: Thu Aug 23 01:10:21 UTC 2018
Modified Files: src/sys/external/bsd/drm2/dist/drm/nouveau: nouveau_fence.c nouveau_fence.h Log Message: Defer nouveau_fence_unref until spin unlock. - kfree while holding a spin lock is not a good idea. - Make sure we GC every time we might signal fences. PR kern/53441 XXX pullup-7 XXX pullup-8 To generate a diff of this commit: cvs rdiff -u -r1.6 -r1.7 \ src/sys/external/bsd/drm2/dist/drm/nouveau/nouveau_fence.c cvs rdiff -u -r1.4 -r1.5 \ src/sys/external/bsd/drm2/dist/drm/nouveau/nouveau_fence.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/external/bsd/drm2/dist/drm/nouveau/nouveau_fence.c diff -u src/sys/external/bsd/drm2/dist/drm/nouveau/nouveau_fence.c:1.6 src/sys/external/bsd/drm2/dist/drm/nouveau/nouveau_fence.c:1.7 --- src/sys/external/bsd/drm2/dist/drm/nouveau/nouveau_fence.c:1.6 Thu Aug 23 01:10:04 2018 +++ src/sys/external/bsd/drm2/dist/drm/nouveau/nouveau_fence.c Thu Aug 23 01:10:21 2018 @@ -1,4 +1,4 @@ -/* $NetBSD: nouveau_fence.c,v 1.6 2018/08/23 01:10:04 riastradh Exp $ */ +/* $NetBSD: nouveau_fence.c,v 1.7 2018/08/23 01:10:21 riastradh Exp $ */ /* * Copyright (C) 2007 Ben Skeggs. @@ -27,7 +27,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: nouveau_fence.c,v 1.6 2018/08/23 01:10:04 riastradh Exp $"); +__KERNEL_RCSID(0, "$NetBSD: nouveau_fence.c,v 1.7 2018/08/23 01:10:21 riastradh Exp $"); #include <sys/types.h> #include <sys/xcall.h> @@ -87,6 +87,43 @@ nouveau_fence_channel_acquire(struct nou } /* + * nouveau_fence_gc_grab(fctx, list) + * + * Move all of channel's done fences to list. + * + * Caller must hold channel's fence lock. + */ +static void +nouveau_fence_gc_grab(struct nouveau_fence_chan *fctx, struct list_head *list) +{ + struct list_head *node, *next; + + BUG_ON(!spin_is_locked(&fctx->lock)); + + list_for_each_safe(node, next, &fctx->done) { + list_move_tail(node, list); + } +} + +/* + * nouveau_fence_gc_free(list) + * + * Unreference all of the fences in the list. + * + * Caller MUST NOT hold the fences' channel's fence lock. + */ +static void +nouveau_fence_gc_free(struct list_head *list) +{ + struct nouveau_fence *fence, *next; + + list_for_each_entry_safe(fence, next, list, head) { + list_del(&fence->head); + nouveau_fence_unref(&fence); + } +} + +/* * nouveau_fence_channel_release(channel) * * Release the channel acquired with nouveau_fence_channel_acquire. @@ -114,7 +151,8 @@ nouveau_fence_channel_release(struct nou /* * nouveau_fence_signal(fence) * - * Schedule all the work for fence's completion, and mark it done. + * Schedule all the work for fence's completion, mark it done, and + * move it from the pending list to the done list. * * Caller must hold fence's channel's fence lock. */ @@ -136,7 +174,9 @@ nouveau_fence_signal(struct nouveau_fenc /* Note that the fence is done. */ fence->done = true; - list_del(&fence->head); + + /* Move it from the pending list to the done list. */ + list_move_tail(&fence->head, &fctx->done); } static void @@ -154,16 +194,22 @@ void nouveau_fence_context_del(struct nouveau_fence_chan *fctx) { struct nouveau_fence *fence, *fnext; + struct list_head done_list; int ret __diagused; + INIT_LIST_HEAD(&done_list); + /* Signal all the fences in fctx. */ spin_lock(&fctx->lock); list_for_each_entry_safe(fence, fnext, &fctx->pending, head) { nouveau_fence_signal(fence); - /* XXX Doesn't this leak fence? */ } + nouveau_fence_gc_grab(fctx, &done_list); spin_unlock(&fctx->lock); + /* Release any fences that we signalled. */ + nouveau_fence_gc_free(&done_list); + /* Wait for the workqueue to drain. */ flush_scheduled_work(); @@ -177,6 +223,11 @@ nouveau_fence_context_del(struct nouveau BUG_ON(ret); spin_unlock(&fctx->lock); + /* Make sure there are no more fences on the list. */ + BUG_ON(!list_empty(&fctx->done)); + BUG_ON(!list_empty(&fctx->flip)); + BUG_ON(!list_empty(&fctx->pending)); + /* Destroy the fence context. */ DRM_DESTROY_WAITQUEUE(&fctx->waitqueue); spin_lock_destroy(&fctx->lock); @@ -193,6 +244,7 @@ nouveau_fence_context_new(struct nouveau INIT_LIST_HEAD(&fctx->flip); INIT_LIST_HEAD(&fctx->pending); + INIT_LIST_HEAD(&fctx->done); spin_lock_init(&fctx->lock); DRM_INIT_WAITQUEUE(&fctx->waitqueue, "nvfnchan"); fctx->refcnt = 0; @@ -275,7 +327,6 @@ nouveau_fence_update(struct nouveau_chan if (fctx->read(chan) < fence->sequence) break; nouveau_fence_signal(fence); - nouveau_fence_unref(&fence); } BUG_ON(!spin_is_locked(&fctx->lock)); } @@ -349,18 +400,24 @@ nouveau_fence_done(struct nouveau_fence { struct nouveau_channel *chan; struct nouveau_fence_chan *fctx; + struct list_head done_list; bool done; if ((chan = nouveau_fence_channel_acquire(fence)) == NULL) return true; + INIT_LIST_HEAD(&done_list); + fctx = chan->fence; spin_lock(&fctx->lock); done = nouveau_fence_done_locked(fence, chan); + nouveau_fence_gc_grab(fctx, &done_list); spin_unlock(&fctx->lock); nouveau_fence_channel_release(chan); + nouveau_fence_gc_free(&done_list); + return done; } @@ -397,6 +454,7 @@ nouveau_fence_wait_uevent(struct nouveau struct nouveau_fifo *pfifo = nouveau_fifo(chan->drm->device); struct nouveau_fence_chan *fctx = chan->fence; struct nouveau_eventh *handler; + struct list_head done_list; int ret = 0; BUG_ON(fence->channel != chan); @@ -409,6 +467,8 @@ nouveau_fence_wait_uevent(struct nouveau nouveau_event_get(handler); + INIT_LIST_HEAD(&done_list); + if (fence->timeout) { unsigned long timeout = fence->timeout - jiffies; @@ -425,6 +485,7 @@ nouveau_fence_wait_uevent(struct nouveau timeout, nouveau_fence_done_locked(fence, chan)); } + nouveau_fence_gc_grab(fctx, &done_list); spin_unlock(&fctx->lock); } @@ -444,10 +505,14 @@ nouveau_fence_wait_uevent(struct nouveau &fctx->lock, nouveau_fence_done_locked(fence, chan)); } + nouveau_fence_gc_grab(fctx, &done_list); spin_unlock(&fctx->lock); } nouveau_event_ref(NULL, &handler); + + nouveau_fence_gc_free(&done_list); + if (unlikely(ret < 0)) return ret; Index: src/sys/external/bsd/drm2/dist/drm/nouveau/nouveau_fence.h diff -u src/sys/external/bsd/drm2/dist/drm/nouveau/nouveau_fence.h:1.4 src/sys/external/bsd/drm2/dist/drm/nouveau/nouveau_fence.h:1.5 --- src/sys/external/bsd/drm2/dist/drm/nouveau/nouveau_fence.h:1.4 Thu Aug 23 01:10:04 2018 +++ src/sys/external/bsd/drm2/dist/drm/nouveau/nouveau_fence.h Thu Aug 23 01:10:21 2018 @@ -36,6 +36,7 @@ int nouveau_fence_sync(struct nouveau_f struct nouveau_fence_chan { struct list_head pending; struct list_head flip; + struct list_head done; int (*emit)(struct nouveau_fence *); int (*sync)(struct nouveau_fence *, struct nouveau_channel *,