Module Name: src Committed By: riastradh Date: Sun Dec 19 12:34:16 UTC 2021
Modified Files: src/sys/external/bsd/drm2/linux: linux_dma_fence.c Log Message: drm: Rework timeout return code logic. To generate a diff of this commit: cvs rdiff -u -r1.31 -r1.32 src/sys/external/bsd/drm2/linux/linux_dma_fence.c 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/linux/linux_dma_fence.c diff -u src/sys/external/bsd/drm2/linux/linux_dma_fence.c:1.31 src/sys/external/bsd/drm2/linux/linux_dma_fence.c:1.32 --- src/sys/external/bsd/drm2/linux/linux_dma_fence.c:1.31 Sun Dec 19 12:34:05 2021 +++ src/sys/external/bsd/drm2/linux/linux_dma_fence.c Sun Dec 19 12:34:16 2021 @@ -1,4 +1,4 @@ -/* $NetBSD: linux_dma_fence.c,v 1.31 2021/12/19 12:34:05 riastradh Exp $ */ +/* $NetBSD: linux_dma_fence.c,v 1.32 2021/12/19 12:34:16 riastradh Exp $ */ /*- * Copyright (c) 2018 The NetBSD Foundation, Inc. @@ -30,7 +30,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: linux_dma_fence.c,v 1.31 2021/12/19 12:34:05 riastradh Exp $"); +__KERNEL_RCSID(0, "$NetBSD: linux_dma_fence.c,v 1.32 2021/12/19 12:34:16 riastradh Exp $"); #include <sys/atomic.h> #include <sys/condvar.h> @@ -723,8 +723,12 @@ dma_fence_wait_any_timeout(struct dma_fe int start, end; long ret = 0; + KASSERTMSG(timeout >= 0, "timeout %ld", timeout); + KASSERTMSG(timeout <= MAX_SCHEDULE_TIMEOUT, "timeout %ld", timeout); + /* Optimistically check whether any are signalled. */ for (i = 0; i < nfences; i++) { + KASSERT(dma_fence_referenced_p(fences[i])); if (dma_fence_is_signaled(fences[i])) { if (ip) *ip = i; @@ -741,10 +745,8 @@ dma_fence_wait_any_timeout(struct dma_fe /* Allocate an array of callback records. */ cb = kcalloc(nfences, sizeof(cb[0]), GFP_KERNEL); - if (cb == NULL) { - ret = -ENOMEM; - goto out0; - } + if (cb == NULL) + return -ENOMEM; /* Initialize a mutex and condvar for the common wait. */ mutex_init(&common.lock, MUTEX_DEFAULT, IPL_VM); @@ -766,7 +768,7 @@ dma_fence_wait_any_timeout(struct dma_fe if (ip) *ip = i; ret = MAX(1, timeout); - goto out1; + goto out; } } @@ -775,7 +777,8 @@ dma_fence_wait_any_timeout(struct dma_fe * callbacks to notify us when it is done. */ mutex_enter(&common.lock); - while (timeout > 0 && !common.done) { + while (!common.done) { + /* Wait for the time remaining. */ start = getticks(); if (intr) { if (timeout != MAX_SCHEDULE_TIMEOUT) { @@ -796,13 +799,42 @@ dma_fence_wait_any_timeout(struct dma_fe } } end = getticks(); + + /* Deduct from time remaining. If none left, time out. */ + if (timeout != MAX_SCHEDULE_TIMEOUT) { + timeout -= MIN(timeout, + (unsigned)end - (unsigned)start); + if (timeout == 0) + ret = -EWOULDBLOCK; + } + + /* If the wait failed, give up. */ if (ret) break; - timeout -= MIN(timeout, (unsigned)end - (unsigned)start); } mutex_exit(&common.lock); /* + * Massage the return code if nonzero: + * - if we were interrupted, return -ERESTARTSYS; + * - if we timed out, return 0. + * No other failure is possible. On success, ret=0 but we + * check again below to verify anyway. + */ + if (ret) { + KASSERTMSG((ret == -EINTR || ret == -ERESTART || + ret == -EWOULDBLOCK), "ret=%ld", ret); + if (ret == -EINTR || ret == -ERESTART) { + ret = -ERESTARTSYS; + } else if (ret == -EWOULDBLOCK) { + KASSERT(timeout != MAX_SCHEDULE_TIMEOUT); + ret = 0; /* timed out */ + } + } + + KASSERT(ret != -ERESTART); /* would be confused with time left */ + + /* * Test whether any of the fences has been signalled. If they * have, return success. */ @@ -811,28 +843,22 @@ dma_fence_wait_any_timeout(struct dma_fe if (ip) *ip = j; ret = MAX(1, timeout); - goto out1; + goto out; } } /* - * Massage the return code: if we were interrupted, return - * ERESTARTSYS; if cv_timedwait timed out, return 0; otherwise - * return the remaining time. + * If user passed MAX_SCHEDULE_TIMEOUT, we can't return 0 + * meaning timed out because we're supposed to wait forever. */ - if (ret == -EINTR || ret == -ERESTART) { - ret = -ERESTARTSYS; - } else if (ret == -EWOULDBLOCK) { - ret = 0; /* timed out */ - } - KASSERTMSG(ret == -ERESTARTSYS || ret >= 0, "ret=%ld", ret); + KASSERT(timeout == MAX_SCHEDULE_TIMEOUT ? ret != 0 : 1); -out1: while (i --> 0) +out: while (i --> 0) (void)dma_fence_remove_callback(fences[i], &cb[i].fcb); cv_destroy(&common.cv); mutex_destroy(&common.lock); kfree(cb); -out0: return ret; + return ret; } /* @@ -911,27 +937,26 @@ dma_fence_default_wait(struct dma_fence /* Optimistically try to skip the lock if it's already signalled. */ if (fence->flags & (1u << DMA_FENCE_FLAG_SIGNALED_BIT)) - return (timeout ? timeout : 1); + return MAX(1, timeout); /* Acquire the lock. */ spin_lock(fence->lock); /* Ensure signalling is enabled, or stop if already completed. */ if (dma_fence_ensure_signal_enabled(fence) != 0) { - spin_unlock(fence->lock); - return (timeout ? timeout : 1); + ret = MAX(1, timeout); + goto out; } /* If merely polling, stop here. */ if (timeout == 0) { - spin_unlock(fence->lock); - return 0; + ret = 0; + goto out; } /* Find out what our deadline is so we can handle spurious wakeup. */ if (timeout < MAX_SCHEDULE_TIMEOUT) { now = getticks(); - __insn_barrier(); starttime = now; deadline = starttime + timeout; } @@ -944,10 +969,13 @@ dma_fence_default_wait(struct dma_fence */ if (timeout < MAX_SCHEDULE_TIMEOUT) { now = getticks(); - __insn_barrier(); - if (deadline <= now) + if (deadline <= now) { + ret = -EWOULDBLOCK; break; + } } + + /* Wait for the time remaining. */ if (intr) { if (timeout < MAX_SCHEDULE_TIMEOUT) { ret = -cv_timedwait_sig(&fence->f_cv, lock, @@ -964,43 +992,43 @@ dma_fence_default_wait(struct dma_fence ret = 0; } } + /* If the wait failed, give up. */ - if (ret) { - if (ret == -ERESTART) - ret = -ERESTARTSYS; + if (ret) break; - } } - /* All done. Release the lock. */ - spin_unlock(fence->lock); - - /* If cv_timedwait gave up, return 0 meaning timeout. */ - if (ret == -EWOULDBLOCK) { - /* Only cv_timedwait and cv_timedwait_sig can return this. */ - KASSERT(timeout < MAX_SCHEDULE_TIMEOUT); - return 0; - } - - /* If there was a timeout and the deadline passed, return 0. */ - if (timeout < MAX_SCHEDULE_TIMEOUT) { - if (deadline <= now) - return 0; + /* + * Massage the return code if nonzero: + * - if we were interrupted, return -ERESTARTSYS; + * - if we timed out, return 0. + * No other failure is possible. On success, ret=0 but we + * check again below to verify anyway. + */ + if (ret) { + KASSERTMSG((ret == -EINTR || ret == -ERESTART || + ret == -EWOULDBLOCK), "ret=%ld", ret); + if (ret == -EINTR || ret == -ERESTART) { + ret = -ERESTARTSYS; + } else if (ret == -EWOULDBLOCK) { + KASSERT(timeout < MAX_SCHEDULE_TIMEOUT); + ret = 0; /* timed out */ + } } - /* If we were interrupted, return -ERESTARTSYS. */ - if (ret == -EINTR || ret == -ERESTART) - return -ERESTARTSYS; + KASSERT(ret != -ERESTART); /* would be confused with time left */ - /* If there was any other kind of error, fail. */ - if (ret) - return ret; + /* Check again in case it was signalled after a wait. */ + if (fence->flags & (1u << DMA_FENCE_FLAG_SIGNALED_BIT)) { + if (timeout < MAX_SCHEDULE_TIMEOUT) + ret = MAX(1, deadline - now); + else + ret = MAX_SCHEDULE_TIMEOUT; + } - /* - * Success! Return the number of ticks left, at least 1, or 1 - * if no timeout. - */ - return (timeout < MAX_SCHEDULE_TIMEOUT ? MIN(deadline - now, 1) : 1); +out: /* All done. Release the lock. */ + spin_unlock(fence->lock); + return ret; } /*