[PATCH RFC 061/111] staging: etnaviv: ensure that we retire all pending events

Lucas Stach l.stach at pengutronix.de
Thu Apr 2 08:30:03 PDT 2015


From: Russell King <rmk+kernel at arm.linux.org.uk>

If we queue up multiple buffers, each with their own event, where the
first buffer takes a while to execute, but subsequent buffers do not,
we can end up receiving multiple events simultaneously.  (eg, 0, 1, 2).

In this case, we only look at event 2, which updates the last fence,
and then free event 2, leaving events 0 and 1 still allocated.  If this
is allowed to continue, eventually we consume all events, and we have
no further way to progress.

However, we have to bear in mind that we could end up with events in
other orders.  For example, we could have three buffers committed at
different times:

- buffer 0 is committed, getting event 0.
- buffer 1 is committed, getting event 1.
- buffer 0 completes, signalling event 0.
- we process event 0, and freeing it.
- buffer 2 is committed, is small, getting event 0.
- buffer 1 completes, signalling event 1.
- buffer 2 completes, signalling event 0 as well.
- we process both event 0 and event 1.  We must note that the fence from
  event 0 completed, and must not overwrite it with the fence from event 1.

Signed-off-by: Russell King <rmk+kernel at arm.linux.org.uk>
---
 drivers/staging/etnaviv/etnaviv_gpu.c | 31 +++++++++++++++++++++++++------
 1 file changed, 25 insertions(+), 6 deletions(-)

diff --git a/drivers/staging/etnaviv/etnaviv_gpu.c b/drivers/staging/etnaviv/etnaviv_gpu.c
index cd308976dec9..4cd84740eac8 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.c
+++ b/drivers/staging/etnaviv/etnaviv_gpu.c
@@ -891,20 +891,39 @@ static irqreturn_t irq_handler(int irq, void *data)
 	u32 intr = gpu_read(gpu, VIVS_HI_INTR_ACKNOWLEDGE);
 
 	if (intr != 0) {
+		int event;
+
 		dev_dbg(gpu->dev, "intr 0x%08x\n", intr);
 
-		if (intr & VIVS_HI_INTR_ACKNOWLEDGE_AXI_BUS_ERROR)
+		if (intr & VIVS_HI_INTR_ACKNOWLEDGE_AXI_BUS_ERROR) {
 			dev_err(gpu->dev, "AXI bus error\n");
-		else {
-			uint8_t event = __fls(intr);
+			intr &= ~VIVS_HI_INTR_ACKNOWLEDGE_AXI_BUS_ERROR;
+		}
+
+		while ((event = ffs(intr)) != 0) {
+			event -= 1;
+
+			intr &= ~(1 << event);
 
 			dev_dbg(gpu->dev, "event %u\n", event);
-			gpu->retired_fence = gpu->event[event].fence;
-			gpu->last_ring_pos = gpu->event[event].ring_pos;
+			/*
+			 * Events can be processed out of order.  Eg,
+			 * - allocate and queue event 0
+			 * - allocate event 1
+			 * - event 0 completes, we process it
+			 * - allocate and queue event 0
+			 * - event 1 and event 0 complete
+			 * we can end up processing event 0 first, then 1.
+			 */
+			if (fence_after(gpu->event[event].fence, gpu->retired_fence)) {
+				gpu->retired_fence = gpu->event[event].fence;
+				gpu->last_ring_pos = gpu->event[event].ring_pos;
+			}
 			event_free(gpu, event);
-			etnaviv_gpu_retire(gpu);
 		}
 
+		etnaviv_gpu_retire(gpu);
+
 		ret = IRQ_HANDLED;
 	}
 
-- 
2.1.4



More information about the dri-devel mailing list