Commit | Line | Data |
---|---|---|
47508f2e JB |
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 |