1 --- a/hw/xfree86/dri2/dri2.c
2 +++ b/hw/xfree86/dri2/dri2.c
6 * Copyright © 2007, 2008 Red Hat, Inc.
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 @@ -100,6 +100,7 @@ typedef struct _DRI2Drawable {
10 CARD64 last_swap_ust; /* ust at completion of most recent swap */
11 int swap_limit; /* for N-buffering */
13 + Bool client_created;
15 PixmapPtr prime_slave_pixmap;
16 PixmapPtr redirectpixmap;
17 @@ -114,6 +115,7 @@ typedef struct _DRI2Screen {
19 unsigned int lastSequence;
21 + Bool need_async_swapbuffers_reply;
23 DRI2CreateBufferProcPtr CreateBuffer;
24 DRI2DestroyBufferProcPtr DestroyBuffer;
25 @@ -201,6 +203,7 @@ DRI2AllocateDrawable(DrawablePtr pDraw)
29 + pPriv->client_created = FALSE;
30 pPriv->dri2_screen = ds;
31 pPriv->drawable = pDraw;
32 pPriv->width = pDraw->width;
33 @@ -328,6 +331,15 @@ DRI2CreateDrawable2(ClientPtr client, Dr
36 pPriv = DRI2GetDrawable(pDraw);
37 + if (pPriv && dri2_id_out == NULL && pPriv->client_created) {
38 + /* We already allocated a DRI2Drawable for this drawable, the client
39 + * has called into this function (so will receive invalidate events)
40 + * and now we're being called by the client again (we know this because
41 + * the caller doesn't care about the dri2_id). This means we don't need
42 + * to allocate another one, we have nothing else to do. */
43 + pPriv->prime_id = dri2_client->prime_id;
47 pPriv = DRI2AllocateDrawable(pDraw);
49 @@ -342,6 +354,11 @@ DRI2CreateDrawable2(ClientPtr client, Dr
52 *dri2_id_out = dri2_id;
54 + /* The client has called in for the first time, it will now receive
55 + * invalidate events. Record this for later. */
56 + pPriv->client_created = TRUE;
61 @@ -1071,7 +1088,11 @@ DRI2WaitSwap(ClientPtr client, DrawableP
67 +DRI2NeedAsyncSwapBuffersReply(DrawablePtr pDraw)
69 + return DRI2GetScreen(pDraw->pScreen)->need_async_swapbuffers_reply;
73 DRI2SwapBuffers(ClientPtr client, DrawablePtr pDraw, CARD64 target_msc,
74 @@ -1516,6 +1537,12 @@ DRI2ScreenInit(ScreenPtr pScreen, DRI2In
77 ds->driverNames[0] = info->driverName;
79 + /* Mali incorrectly assumes that a return from SwapBuffers means that
80 + * buffers have been swapped, it doesn't wait for the relevant event.
81 + * In this case, we work around this annoyance by delaying the
82 + * SwapBuffers reply until the swap has actually completed. */
83 + ds->need_async_swapbuffers_reply = strcmp(info->driverName, "armsoc") == 0;
86 ds->numDrivers = info->numDrivers;
87 --- a/hw/xfree86/dri2/dri2ext.c
88 +++ b/hw/xfree86/dri2/dri2ext.c
91 static int DRI2EventBase;
93 +/* Data shared by ProcDRI2SwapBuffers and the asynchronous callback
95 +struct SwapBuffersData {
98 + DrawablePtr pDrawable;
103 validDrawable(ClientPtr client, XID drawable, Mask access_mode,
104 @@ -276,7 +284,6 @@ ProcDRI2GetBuffers(ClientPtr client)
106 if (DRI2ThrottleClient(client, pDrawable))
109 attachments = (unsigned int *) &stuff[1];
110 buffers = DRI2GetBuffers(pDrawable, &width, &height,
111 attachments, stuff->count, &count);
112 @@ -355,6 +362,24 @@ load_swap_reply(xDRI2SwapBuffersReply *
113 rep->swap_lo = sbc & 0xffffffff;
116 +static void swap_buffers_data_unref(struct SwapBuffersData *data)
118 + if (--data->refcnt == 0)
122 +static void send_swap_buffers_reply(ClientPtr client, struct SwapBuffersData *data)
124 + xDRI2SwapBuffersReply rep = {
127 + .sequenceNumber = data->sequence,
130 + load_swap_reply(&rep, data->swap_target);
131 + WriteToClient(client, sizeof(xDRI2SwapBuffersReply), &rep);
135 vals_to_card64(CARD32 lo, CARD32 hi)
137 @@ -365,11 +390,11 @@ static void
138 DRI2SwapEvent(ClientPtr client, void *data, int type, CARD64 ust, CARD64 msc,
141 - DrawablePtr pDrawable = data;
142 + struct SwapBuffersData *sbdata = data;
143 xDRI2BufferSwapComplete2 event = {
144 .type = DRI2EventBase + DRI2_BufferSwapComplete,
146 - .drawable = pDrawable->id,
147 + .drawable = sbdata->pDrawable->id,
148 .ust_hi = (CARD64) ust >> 32,
149 .ust_lo = ust & 0xffffffff,
150 .msc_hi = (CARD64) msc >> 32,
151 @@ -377,6 +402,12 @@ DRI2SwapEvent(ClientPtr client, void *da
155 + if (DRI2NeedAsyncSwapBuffersReply(sbdata->pDrawable)) {
156 + send_swap_buffers_reply(client, sbdata);
157 + AttendClient(client);
160 + swap_buffers_data_unref(data);
161 WriteEventsToClient(client, 1, (xEvent *) &event);
164 @@ -384,14 +415,11 @@ static int
165 ProcDRI2SwapBuffers(ClientPtr client)
167 REQUEST(xDRI2SwapBuffersReq);
168 - xDRI2SwapBuffersReply rep = {
170 - .sequenceNumber = client->sequence,
173 DrawablePtr pDrawable;
174 - CARD64 target_msc, divisor, remainder, swap_target;
175 + CARD64 target_msc, divisor, remainder;
176 + struct SwapBuffersData *data;
178 + Bool need_async_swap_reply;
180 REQUEST_SIZE_MATCH(xDRI2SwapBuffersReq);
182 @@ -399,6 +427,8 @@ ProcDRI2SwapBuffers(ClientPtr client)
183 DixReadAccess | DixWriteAccess, &pDrawable, &status))
186 + need_async_swap_reply = DRI2NeedAsyncSwapBuffersReply(pDrawable);
189 * Ensures an out of control client can't exhaust our swap queue, and
191 @@ -406,18 +436,49 @@ ProcDRI2SwapBuffers(ClientPtr client)
192 if (DRI2ThrottleClient(client, pDrawable))
195 + data = malloc(sizeof(struct SwapBuffersData));
196 + /* DRI2SwapEvent() above might feel like the obvious place to free the
197 + * data struct we just allocated. But consider the following sequence of
198 + * events (and understand that swap_target is stored in data):
199 + * 1. DRI2SwapBuffers accesses swap_target
200 + * 2. DRI2SwapBuffers calls into ScheduleSwap
201 + * 3. ScheduleSwap blits and calls DRI2SwapEvent before returning
202 + * 4. Back in DRI2SwapBuffers, we access swap_target again
204 + * If we had freed data in step 3, we would have a use-after-free in
207 + * So, we set up a refcount of 2. One ref is owned by this function, and
208 + * one ref is owned by DRI2SwapEvent. We only free the data once both
209 + * functions are done. */
211 + data->pDrawable = pDrawable;
212 + data->sequence = client->sequence;
214 target_msc = vals_to_card64(stuff->target_msc_lo, stuff->target_msc_hi);
215 divisor = vals_to_card64(stuff->divisor_lo, stuff->divisor_hi);
216 remainder = vals_to_card64(stuff->remainder_lo, stuff->remainder_hi);
218 + /* If we're sending the reply later, we need to avoid processing any
219 + * more requests from this client until that point. */
220 + if (need_async_swap_reply)
221 + IgnoreClient(client);
223 status = DRI2SwapBuffers(client, pDrawable, target_msc, divisor, remainder,
224 - &swap_target, DRI2SwapEvent, pDrawable);
225 - if (status != Success)
226 + &data->swap_target, DRI2SwapEvent, data);
228 + if (status != Success) {
229 + swap_buffers_data_unref(data);
230 + swap_buffers_data_unref(data);
231 + if (need_async_swap_reply)
232 + AttendClient(client);
236 - load_swap_reply(&rep, swap_target);
237 + if (!need_async_swap_reply)
238 + send_swap_buffers_reply(client, data);
240 - WriteToClient(client, sizeof(xDRI2SwapBuffersReply), &rep);
241 + swap_buffers_data_unref(data);
245 --- a/hw/xfree86/dri2/dri2.h
246 +++ b/hw/xfree86/dri2/dri2.h
247 @@ -359,4 +359,6 @@ extern _X_EXPORT int DRI2GetParam(Client
250 extern _X_EXPORT DrawablePtr DRI2UpdatePrime(DrawablePtr pDraw, DRI2BufferPtr pDest);
252 +extern Bool DRI2NeedAsyncSwapBuffersReply(DrawablePtr pDraw);