@@ -36,6 +36,9 @@ void a5xx_flush(struct msm_gpu *gpu, struct msm_ringbuffer *ring,
OUT_RING(ring, upper_32_bits(shadowptr(a5xx_gpu, ring)));
}
+ if (unlikely(ring->overflow))
+ return;
+
spin_lock_irqsave(&ring->preempt_lock, flags);
/* Copy the shadow to the actual register */
@@ -68,6 +68,9 @@ static void a6xx_flush(struct msm_gpu *gpu, struct msm_ringbuffer *ring)
OUT_RING(ring, upper_32_bits(shadowptr(a6xx_gpu, ring)));
}
+ if (unlikely(ring->overflow))
+ return;
+
spin_lock_irqsave(&ring->preempt_lock, flags);
/* Copy the shadow to the actual register */
@@ -467,6 +467,9 @@ void adreno_flush(struct msm_gpu *gpu, struct msm_ringbuffer *ring, u32 reg)
{
uint32_t wptr;
+ if (unlikely(ring->overflow))
+ return;
+
/* Copy the shadow to the actual register */
ring->cur = ring->next;
@@ -788,12 +791,31 @@ static uint32_t ring_freewords(struct msm_ringbuffer *ring)
return (rptr + (size - 1) - wptr) % size;
}
+static bool space_avail(struct msm_ringbuffer *ring, uint32_t ndwords)
+{
+ if (ring_freewords(ring) >= ndwords)
+ return true;
+
+ /* We don't have a good way to know in general when the RPTR has
+ * advanced.. newer things that use CP_WHERE_AM_I to update the
+ * shadow rptr could possibly insert a packet to generate an irq.
+ * But that doesn't cover older GPUs. But if the ringbuffer is
+ * full, it could take a while before it is empty again, so just
+ * insert a blind sleep to avoid a busy loop.
+ */
+ msleep(1);
+
+ return false;
+}
+
void adreno_wait_ring(struct msm_ringbuffer *ring, uint32_t ndwords)
{
- if (spin_until(ring_freewords(ring) >= ndwords))
+ if (spin_until(space_avail(ring, ndwords))) {
DRM_DEV_ERROR(ring->gpu->dev->dev,
"timeout waiting for space in ringbuffer %d\n",
ring->id);
+ ring->overflow = true;
+ }
}
/* Get legacy powerlevels from qcom,gpu-pwrlevels and populate the opp table */
@@ -683,6 +683,9 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data,
submitid = atomic_inc_return(&ident) - 1;
ring = gpu->rb[queue->prio];
+
+ GEM_WARN_ON(ring->overflow);
+
trace_msm_gpu_submit(pid_nr(pid), ring->id, submitid,
args->nr_bos, args->nr_cmds);
@@ -829,7 +832,9 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data,
}
}
- msm_gpu_submit(gpu, submit);
+ ret = msm_gpu_submit(gpu, submit);
+ if (ret)
+ goto out;
args->fence = submit->fence->seqno;
@@ -787,7 +787,7 @@ void msm_gpu_retire(struct msm_gpu *gpu)
}
/* add bo's to gpu's ring, and kick gpu: */
-void msm_gpu_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit)
+int msm_gpu_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit)
{
struct drm_device *dev = gpu->dev;
struct msm_drm_private *priv = dev->dev_private;
@@ -834,9 +834,38 @@ void msm_gpu_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit)
spin_unlock(&ring->submit_lock);
gpu->funcs->submit(gpu, submit);
- priv->lastctx = submit->queue->ctx;
hangcheck_timer_reset(gpu);
+
+ if (unlikely(ring->overflow)) {
+ /*
+ * Reset the ptr back to before the submit, so the GPU
+ * doesn't see a partial submit:
+ */
+ ring->next = ring->cur;
+
+ /*
+ * Clear the overflow flag, hopefully the next submit on
+ * the ring actually fits
+ */
+ ring->overflow = false;
+
+ /*
+ * One might be tempted to remove the submit from the
+ * submits list, and drop it's reference (and drop the
+ * active reference for all the bos). But we can't
+ * really signal the fence attached to obj->resv without
+ * disturbing other fences on the timeline. So instead
+ * just leave it and let it retire normally when a
+ * later submit completes.
+ */
+
+ return -ENOSPC;
+ }
+
+ priv->lastctx = submit->queue->ctx;
+
+ return 0;
}
/*
@@ -302,7 +302,7 @@ int msm_gpu_perfcntr_sample(struct msm_gpu *gpu, uint32_t *activetime,
uint32_t *totaltime, uint32_t ncntrs, uint32_t *cntrs);
void msm_gpu_retire(struct msm_gpu *gpu);
-void msm_gpu_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit);
+int msm_gpu_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit);
int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev,
struct msm_gpu *gpu, const struct msm_gpu_funcs *funcs,
@@ -40,6 +40,8 @@ struct msm_ringbuffer {
struct drm_gem_object *bo;
uint32_t *start, *end, *cur, *next;
+ bool overflow;
+
/*
* List of in-flight submits on this ring. Protected by submit_lock.
*/
@@ -69,6 +71,9 @@ void msm_ringbuffer_destroy(struct msm_ringbuffer *ring);
static inline void
OUT_RING(struct msm_ringbuffer *ring, uint32_t data)
{
+ if (ring->overflow)
+ return;
+
/*
* ring->next points to the current command being written - it won't be
* committed as ring->cur until the flush