DF-5017 / fix.diff
--- a/sys/dev/drm/linux_fence.c +++ b/sys/dev/drm/linux_fence.c @@ -174,8 +174,7 @@ { struct default_wait_cb *cb; long ret = timeout; - unsigned long end; - int i, err; + int i; if (timeout == 0) { for (i = 0; i < count; i++) { @@ -203,14 +202,42 @@ } } - end = jiffies + timeout; - for (ret = timeout; ret > 0; ret = MAX(0, end - jiffies)) { - if (dma_fence_test_signaled_any(fences, count, idx)) - break; - err = tsleep(current, intr ? PCATCH : 0, "dfwat", ret); - if (err == EINTR || err == ERESTART) { - ret = -ERESTARTSYS; - break; + /* + * Use the schedule_timeout() shim: its kt_spin + task-state latch + * closes the check-to-sleep wakeup race, it clamps the tick count + * (no long->int truncation of the sleep timeout), and it returns + * non-negative remaining time (no unsigned end-jiffies wrap). + */ + if (timeout == MAX_SCHEDULE_TIMEOUT) { + for (;;) { + if (dma_fence_test_signaled_any(fences, count, idx)) { + ret = 1; + break; + } + __set_current_state(intr ? TASK_INTERRUPTIBLE : + TASK_UNINTERRUPTIBLE); + schedule_timeout(MAX_SCHEDULE_TIMEOUT); + __set_current_state(TASK_RUNNING); + if (intr && signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } + } + } else { + unsigned long end = jiffies + timeout; + + for (ret = timeout; ret > 0; + ret = MAX(0L, (long)(end - jiffies))) { + if (dma_fence_test_signaled_any(fences, count, idx)) + break; + __set_current_state(intr ? TASK_INTERRUPTIBLE : + TASK_UNINTERRUPTIBLE); + schedule_timeout(ret); + __set_current_state(TASK_RUNNING); + if (intr && signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } } } |