diff options
Diffstat (limited to 'libdrm')
| -rw-r--r-- | libdrm/intel/intel_bufmgr_fake.c | 115 | 
1 files changed, 84 insertions, 31 deletions
diff --git a/libdrm/intel/intel_bufmgr_fake.c b/libdrm/intel/intel_bufmgr_fake.c index 28c7f6b3..e26d46c1 100644 --- a/libdrm/intel/intel_bufmgr_fake.c +++ b/libdrm/intel/intel_bufmgr_fake.c @@ -166,7 +166,7 @@ typedef struct _bufmgr_fake {     /** Driver-supplied argument to driver callbacks */     void *driver_priv;     /* Pointer to kernel-updated sarea data for the last completed user irq */ -   volatile unsigned int *last_dispatch; +   volatile int *last_dispatch;     int fd; @@ -264,61 +264,114 @@ _fence_emit_internal(dri_bufmgr_fake *bufmgr_fake)        abort();     } -   /* The kernel implementation of IRQ_WAIT is broken for wraparound, and has -    * been since it was first introduced.  It only checks for -    * completed_seq >= seq, and thus returns success early for wrapped irq -    * values if the CPU wins a race. -    * -    * We have to do it up front at emit when we discover wrap, so that another -    * client can't race (after we drop the lock) to emit and wait and fail. -    */ -   if (seq == 0 || seq == 1) { -      drmCommandWriteRead(bufmgr_fake->fd, DRM_I915_FLUSH, &ie, sizeof(ie)); -   } -     DBG("emit 0x%08x\n", seq);     bufmgr_fake->last_fence = seq;     return bufmgr_fake->last_fence;  }  static void -_fence_wait_internal(dri_bufmgr_fake *bufmgr_fake, unsigned int cookie) +_fence_wait_internal(dri_bufmgr_fake *bufmgr_fake, int seq)  {     struct drm_i915_irq_wait iw; -   unsigned int last_dispatch; +   int hw_seq;     int ret; +   int kernel_lied;     if (bufmgr_fake->fence_wait != NULL) { -      bufmgr_fake->fence_wait(cookie, bufmgr_fake->fence_priv); +      bufmgr_fake->fence_wait(seq, bufmgr_fake->fence_priv);        return;     }     DBG("wait 0x%08x\n", iw.irq_seq); -   /* The kernel implementation of IRQ_WAIT is broken for wraparound, and has -    * been since it was first introduced.  It only checks for -    * completed_seq >= seq, and thus never returns for pre-wrapped irq values -    * if the GPU wins the race. +   iw.irq_seq = seq; + +   /* The kernel IRQ_WAIT implementation is all sorts of broken. +    * 1) It returns 1 to 0x7fffffff instead of using the full 32-bit unsigned +    *    range. +    * 2) It returns 0 if hw_seq >= seq, not seq - hw_seq < 0 on the 32-bit +    *    signed range. +    * 3) It waits if seq < hw_seq, not seq - hw_seq > 0 on the 32-bit +    *    signed range. +    * 4) It returns -EBUSY in 3 seconds even if the hardware is still +    *    successfully chewing through buffers. +    * +    * Assume that in userland we treat sequence numbers as ints, which makes +    * some of the comparisons convenient, since the sequence numbers are +    * all postive signed integers. +    * +    * From this we get several cases we need to handle.  Here's a timeline. +    * 0x2   0x7                                         0x7ffffff8   0x7ffffffd +    *   |    |                                                   |    | +    * ------------------------------------------------------------------- +    * +    * A) Normal wait for hw to catch up +    * hw_seq seq +    *   |    | +    * ------------------------------------------------------------------- +    * seq - hw_seq = 5.  If we call IRQ_WAIT, it will wait for hw to catch up. +    * +    * B) Normal wait for a sequence number that's already passed. +    * seq    hw_seq +    *   |    | +    * ------------------------------------------------------------------- +    * seq - hw_seq = -5.  If we call IRQ_WAIT, it returns 0 quickly.      * -    * So, check if it looks like a pre-wrapped value and just return success. +    * C) Hardware has already wrapped around ahead of us +    * hw_seq                                                         seq +    *   |                                                             | +    * ------------------------------------------------------------------- +    * seq - hw_seq = 0x80000000 - 5.  If we called IRQ_WAIT, it would wait +    * for hw_seq >= seq, which may never occur.  Thus, we want to catch this +    * in userland and return 0. +    * +    * D) We've wrapped around ahead of the hardware. +    * seq                                                           hw_seq +    *   |                                                             | +    * ------------------------------------------------------------------- +    * seq - hw_seq = -(0x80000000 - 5).  If we called IRQ_WAIT, it would return +    * 0 quickly because hw_seq >= seq, even though the hardware isn't caught up. +    * Thus, we need to catch this early return in userland and bother the +    * kernel until the hardware really does catch up. +    * +    * E) Hardware might wrap after we test in userland. +    *                                                         hw_seq  seq +    *                                                            |    | +    * ------------------------------------------------------------------- +    * seq - hw_seq = 5.  If we call IRQ_WAIT, it will likely see seq >= hw_seq +    * and wait.  However, suppose hw_seq wraps before we make it into the +    * kernel.  The kernel sees hw_seq >= seq and waits for 3 seconds then +    * returns -EBUSY.  This is case C).  We should catch this and then return +    * successfully.      */ -   if (*bufmgr_fake->last_dispatch - cookie > 0x4000000) -      return; +   do { +      /* Keep a copy of last_dispatch so that if the wait -EBUSYs because the +       * hardware didn't catch up in 3 seconds, we can see if it at least made +       * progress and retry. +       */ +      hw_seq = *bufmgr_fake->last_dispatch; -   iw.irq_seq = cookie; +      /* Catch case C */ +      if (seq - hw_seq > 0x40000000) +	 return; -   do { -      last_dispatch = *bufmgr_fake->last_dispatch;        ret = drmCommandWrite(bufmgr_fake->fd, DRM_I915_IRQ_WAIT,  			    &iw, sizeof(iw)); -   } while (ret == -EAGAIN || ret == -EINTR || -	    (ret == -EBUSY && last_dispatch != *bufmgr_fake->last_dispatch)); +      /* Catch case D */ +      kernel_lied = (ret == 0) && (seq - *bufmgr_fake->last_dispatch < +				   -0x40000000); + +      /* Catch case E */ +      if (ret == -EBUSY && (seq - *bufmgr_fake->last_dispatch > 0x40000000)) +	 ret = 0; +   } while (kernel_lied || ret == -EAGAIN || ret == -EINTR || +	    (ret == -EBUSY && hw_seq != *bufmgr_fake->last_dispatch));     if (ret != 0) {        drmMsg("%s:%d: Error %d waiting for fence.\n", __FILE__, __LINE__, ret);        abort();     } -   clear_fenced(bufmgr_fake, cookie); +   clear_fenced(bufmgr_fake, seq);  }  static int @@ -1312,7 +1365,7 @@ void intel_bufmgr_fake_set_last_dispatch(dri_bufmgr *bufmgr,  {     dri_bufmgr_fake *bufmgr_fake = (dri_bufmgr_fake *)bufmgr; -   bufmgr_fake->last_dispatch = last_dispatch; +   bufmgr_fake->last_dispatch = (volatile int *)last_dispatch;  }  dri_bufmgr * @@ -1349,7 +1402,7 @@ intel_bufmgr_fake_init(int fd,     bufmgr_fake->bufmgr.debug = 0;     bufmgr_fake->fd = fd; -   bufmgr_fake->last_dispatch = last_dispatch; +   bufmgr_fake->last_dispatch = (volatile int *)last_dispatch;     return &bufmgr_fake->bufmgr;  }  | 
