From: Russell King <rmk+kernel@xxxxxxxxxxxxxxxx> 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@xxxxxxxxxxxxxxxx> --- 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 _______________________________________________ dri-devel mailing list dri-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/dri-devel