| 1 | --- a/hw/xfree86/dri2/dri2.c |
| 2 | +++ b/hw/xfree86/dri2/dri2.c |
| 3 | @@ -1,4 +1,4 @@ |
| 4 | -/* |
| 5 | + /* |
| 6 | * Copyright © 2007, 2008 Red Hat, Inc. |
| 7 | * |
| 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 */ |
| 12 | Bool needInvalidate; |
| 13 | + Bool client_created; |
| 14 | int prime_id; |
| 15 | PixmapPtr prime_slave_pixmap; |
| 16 | PixmapPtr redirectpixmap; |
| 17 | @@ -114,6 +115,7 @@ typedef struct _DRI2Screen { |
| 18 | int fd; |
| 19 | unsigned int lastSequence; |
| 20 | int prime_id; |
| 21 | + Bool need_async_swapbuffers_reply; |
| 22 | |
| 23 | DRI2CreateBufferProcPtr CreateBuffer; |
| 24 | DRI2DestroyBufferProcPtr DestroyBuffer; |
| 25 | @@ -201,6 +203,7 @@ DRI2AllocateDrawable(DrawablePtr pDraw) |
| 26 | if (pPriv == NULL) |
| 27 | return NULL; |
| 28 | |
| 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 |
| 34 | int rc; |
| 35 | |
| 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; |
| 44 | + return Success; |
| 45 | + } |
| 46 | if (pPriv == NULL) |
| 47 | pPriv = DRI2AllocateDrawable(pDraw); |
| 48 | if (pPriv == NULL) |
| 49 | @@ -342,6 +354,11 @@ DRI2CreateDrawable2(ClientPtr client, Dr |
| 50 | |
| 51 | if (dri2_id_out) |
| 52 | *dri2_id_out = dri2_id; |
| 53 | + else { |
| 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; |
| 57 | + } |
| 58 | |
| 59 | return Success; |
| 60 | } |
| 61 | @@ -1071,7 +1088,11 @@ DRI2WaitSwap(ClientPtr client, DrawableP |
| 62 | return FALSE; |
| 63 | } |
| 64 | |
| 65 | - |
| 66 | +Bool |
| 67 | +DRI2NeedAsyncSwapBuffersReply(DrawablePtr pDraw) |
| 68 | +{ |
| 69 | + return DRI2GetScreen(pDraw->pScreen)->need_async_swapbuffers_reply; |
| 70 | +} |
| 71 | |
| 72 | int |
| 73 | DRI2SwapBuffers(ClientPtr client, DrawablePtr pDraw, CARD64 target_msc, |
| 74 | @@ -1516,6 +1537,12 @@ DRI2ScreenInit(ScreenPtr pScreen, DRI2In |
| 75 | if (!ds->driverNames) |
| 76 | goto err_out; |
| 77 | ds->driverNames[0] = info->driverName; |
| 78 | + |
| 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; |
| 84 | } |
| 85 | else { |
| 86 | ds->numDrivers = info->numDrivers; |
| 87 | --- a/hw/xfree86/dri2/dri2ext.c |
| 88 | +++ b/hw/xfree86/dri2/dri2ext.c |
| 89 | @@ -53,6 +53,14 @@ |
| 90 | |
| 91 | static int DRI2EventBase; |
| 92 | |
| 93 | +/* Data shared by ProcDRI2SwapBuffers and the asynchronous callback |
| 94 | + * DRI2SwapEvent */ |
| 95 | +struct SwapBuffersData { |
| 96 | + CARD64 swap_target; |
| 97 | + int refcnt; |
| 98 | + DrawablePtr pDrawable; |
| 99 | + CARD16 sequence; |
| 100 | +}; |
| 101 | |
| 102 | static Bool |
| 103 | validDrawable(ClientPtr client, XID drawable, Mask access_mode, |
| 104 | @@ -276,7 +284,6 @@ ProcDRI2GetBuffers(ClientPtr client) |
| 105 | |
| 106 | if (DRI2ThrottleClient(client, pDrawable)) |
| 107 | return Success; |
| 108 | - |
| 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; |
| 114 | } |
| 115 | |
| 116 | +static void swap_buffers_data_unref(struct SwapBuffersData *data) |
| 117 | +{ |
| 118 | + if (--data->refcnt == 0) |
| 119 | + free(data); |
| 120 | +} |
| 121 | + |
| 122 | +static void send_swap_buffers_reply(ClientPtr client, struct SwapBuffersData *data) |
| 123 | +{ |
| 124 | + xDRI2SwapBuffersReply rep = { |
| 125 | + .type = X_Reply, |
| 126 | + .length = 0, |
| 127 | + .sequenceNumber = data->sequence, |
| 128 | + }; |
| 129 | + |
| 130 | + load_swap_reply(&rep, data->swap_target); |
| 131 | + WriteToClient(client, sizeof(xDRI2SwapBuffersReply), &rep); |
| 132 | +} |
| 133 | + |
| 134 | static CARD64 |
| 135 | vals_to_card64(CARD32 lo, CARD32 hi) |
| 136 | { |
| 137 | @@ -365,11 +390,11 @@ static void |
| 138 | DRI2SwapEvent(ClientPtr client, void *data, int type, CARD64 ust, CARD64 msc, |
| 139 | CARD32 sbc) |
| 140 | { |
| 141 | - DrawablePtr pDrawable = data; |
| 142 | + struct SwapBuffersData *sbdata = data; |
| 143 | xDRI2BufferSwapComplete2 event = { |
| 144 | .type = DRI2EventBase + DRI2_BufferSwapComplete, |
| 145 | .event_type = type, |
| 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 |
| 152 | .sbc = sbc |
| 153 | }; |
| 154 | |
| 155 | + if (DRI2NeedAsyncSwapBuffersReply(sbdata->pDrawable)) { |
| 156 | + send_swap_buffers_reply(client, sbdata); |
| 157 | + AttendClient(client); |
| 158 | + } |
| 159 | + |
| 160 | + swap_buffers_data_unref(data); |
| 161 | WriteEventsToClient(client, 1, (xEvent *) &event); |
| 162 | } |
| 163 | |
| 164 | @@ -384,14 +415,11 @@ static int |
| 165 | ProcDRI2SwapBuffers(ClientPtr client) |
| 166 | { |
| 167 | REQUEST(xDRI2SwapBuffersReq); |
| 168 | - xDRI2SwapBuffersReply rep = { |
| 169 | - .type = X_Reply, |
| 170 | - .sequenceNumber = client->sequence, |
| 171 | - .length = 0 |
| 172 | - }; |
| 173 | DrawablePtr pDrawable; |
| 174 | - CARD64 target_msc, divisor, remainder, swap_target; |
| 175 | + CARD64 target_msc, divisor, remainder; |
| 176 | + struct SwapBuffersData *data; |
| 177 | int status; |
| 178 | + Bool need_async_swap_reply; |
| 179 | |
| 180 | REQUEST_SIZE_MATCH(xDRI2SwapBuffersReq); |
| 181 | |
| 182 | @@ -399,6 +427,8 @@ ProcDRI2SwapBuffers(ClientPtr client) |
| 183 | DixReadAccess | DixWriteAccess, &pDrawable, &status)) |
| 184 | return status; |
| 185 | |
| 186 | + need_async_swap_reply = DRI2NeedAsyncSwapBuffersReply(pDrawable); |
| 187 | + |
| 188 | /* |
| 189 | * Ensures an out of control client can't exhaust our swap queue, and |
| 190 | * also orders swaps. |
| 191 | @@ -406,18 +436,49 @@ ProcDRI2SwapBuffers(ClientPtr client) |
| 192 | if (DRI2ThrottleClient(client, pDrawable)) |
| 193 | return Success; |
| 194 | |
| 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 |
| 203 | + * |
| 204 | + * If we had freed data in step 3, we would have a use-after-free in |
| 205 | + * step 4. |
| 206 | + * |
| 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. */ |
| 210 | + data->refcnt = 2; |
| 211 | + data->pDrawable = pDrawable; |
| 212 | + data->sequence = client->sequence; |
| 213 | + |
| 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); |
| 217 | |
| 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); |
| 222 | + |
| 223 | status = DRI2SwapBuffers(client, pDrawable, target_msc, divisor, remainder, |
| 224 | - &swap_target, DRI2SwapEvent, pDrawable); |
| 225 | - if (status != Success) |
| 226 | + &data->swap_target, DRI2SwapEvent, data); |
| 227 | + |
| 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); |
| 233 | return BadDrawable; |
| 234 | + } |
| 235 | |
| 236 | - load_swap_reply(&rep, swap_target); |
| 237 | + if (!need_async_swap_reply) |
| 238 | + send_swap_buffers_reply(client, data); |
| 239 | |
| 240 | - WriteToClient(client, sizeof(xDRI2SwapBuffersReply), &rep); |
| 241 | + swap_buffers_data_unref(data); |
| 242 | |
| 243 | return Success; |
| 244 | } |
| 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 |
| 248 | CARD64 *value); |
| 249 | |
| 250 | extern _X_EXPORT DrawablePtr DRI2UpdatePrime(DrawablePtr pDraw, DRI2BufferPtr pDest); |
| 251 | + |
| 252 | +extern Bool DRI2NeedAsyncSwapBuffersReply(DrawablePtr pDraw); |
| 253 | #endif |