]>
Commit | Line | Data |
---|---|---|
f64122c1 DA |
1 | /* |
2 | * Copyright 2013 Red Hat Inc. | |
3 | * | |
4 | * Permission is hereby granted, free of charge, to any person obtaining a | |
5 | * copy of this software and associated documentation files (the "Software"), | |
6 | * to deal in the Software without restriction, including without limitation | |
7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
8 | * and/or sell copies of the Software, and to permit persons to whom the | |
9 | * Software is furnished to do so, subject to the following conditions: | |
10 | * | |
11 | * The above copyright notice and this permission notice shall be included in | |
12 | * all copies or substantial portions of the Software. | |
13 | * | |
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
17 | * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
18 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
19 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
20 | * OTHER DEALINGS IN THE SOFTWARE. | |
21 | * | |
22 | * Authors: Dave Airlie | |
23 | * Alon Levy | |
24 | */ | |
25 | ||
26 | /* QXL cmd/ring handling */ | |
27 | ||
e9eafcb5 SR |
28 | #include <drm/drm_util.h> |
29 | ||
f64122c1 DA |
30 | #include "qxl_drv.h" |
31 | #include "qxl_object.h" | |
32 | ||
33 | static int qxl_reap_surface_id(struct qxl_device *qdev, int max_to_reap); | |
34 | ||
35 | struct ring { | |
36 | struct qxl_ring_header header; | |
37 | uint8_t elements[0]; | |
38 | }; | |
39 | ||
40 | struct qxl_ring { | |
41 | struct ring *ring; | |
42 | int element_size; | |
43 | int n_elements; | |
44 | int prod_notify; | |
45 | wait_queue_head_t *push_event; | |
46 | spinlock_t lock; | |
47 | }; | |
48 | ||
49 | void qxl_ring_free(struct qxl_ring *ring) | |
50 | { | |
51 | kfree(ring); | |
52 | } | |
53 | ||
1e209117 DA |
54 | void qxl_ring_init_hdr(struct qxl_ring *ring) |
55 | { | |
56 | ring->ring->header.notify_on_prod = ring->n_elements; | |
57 | } | |
58 | ||
f64122c1 DA |
59 | struct qxl_ring * |
60 | qxl_ring_create(struct qxl_ring_header *header, | |
61 | int element_size, | |
62 | int n_elements, | |
63 | int prod_notify, | |
64 | bool set_prod_notify, | |
65 | wait_queue_head_t *push_event) | |
66 | { | |
67 | struct qxl_ring *ring; | |
68 | ||
69 | ring = kmalloc(sizeof(*ring), GFP_KERNEL); | |
70 | if (!ring) | |
71 | return NULL; | |
72 | ||
73 | ring->ring = (struct ring *)header; | |
74 | ring->element_size = element_size; | |
75 | ring->n_elements = n_elements; | |
76 | ring->prod_notify = prod_notify; | |
77 | ring->push_event = push_event; | |
78 | if (set_prod_notify) | |
1e209117 | 79 | qxl_ring_init_hdr(ring); |
f64122c1 DA |
80 | spin_lock_init(&ring->lock); |
81 | return ring; | |
82 | } | |
83 | ||
84 | static int qxl_check_header(struct qxl_ring *ring) | |
85 | { | |
86 | int ret; | |
87 | struct qxl_ring_header *header = &(ring->ring->header); | |
88 | unsigned long flags; | |
408799eb | 89 | |
f64122c1 DA |
90 | spin_lock_irqsave(&ring->lock, flags); |
91 | ret = header->prod - header->cons < header->num_items; | |
92 | if (ret == 0) | |
93 | header->notify_on_cons = header->cons + 1; | |
94 | spin_unlock_irqrestore(&ring->lock, flags); | |
95 | return ret; | |
96 | } | |
97 | ||
1e209117 | 98 | int qxl_check_idle(struct qxl_ring *ring) |
f64122c1 DA |
99 | { |
100 | int ret; | |
101 | struct qxl_ring_header *header = &(ring->ring->header); | |
102 | unsigned long flags; | |
408799eb | 103 | |
f64122c1 DA |
104 | spin_lock_irqsave(&ring->lock, flags); |
105 | ret = header->prod == header->cons; | |
106 | spin_unlock_irqrestore(&ring->lock, flags); | |
107 | return ret; | |
108 | } | |
109 | ||
110 | int qxl_ring_push(struct qxl_ring *ring, | |
111 | const void *new_elt, bool interruptible) | |
112 | { | |
113 | struct qxl_ring_header *header = &(ring->ring->header); | |
114 | uint8_t *elt; | |
115 | int idx, ret; | |
116 | unsigned long flags; | |
408799eb | 117 | |
f64122c1 DA |
118 | spin_lock_irqsave(&ring->lock, flags); |
119 | if (header->prod - header->cons == header->num_items) { | |
120 | header->notify_on_cons = header->cons + 1; | |
121 | mb(); | |
122 | spin_unlock_irqrestore(&ring->lock, flags); | |
123 | if (!drm_can_sleep()) { | |
124 | while (!qxl_check_header(ring)) | |
125 | udelay(1); | |
126 | } else { | |
127 | if (interruptible) { | |
128 | ret = wait_event_interruptible(*ring->push_event, | |
129 | qxl_check_header(ring)); | |
130 | if (ret) | |
131 | return ret; | |
132 | } else { | |
133 | wait_event(*ring->push_event, | |
134 | qxl_check_header(ring)); | |
135 | } | |
136 | ||
137 | } | |
138 | spin_lock_irqsave(&ring->lock, flags); | |
139 | } | |
140 | ||
141 | idx = header->prod & (ring->n_elements - 1); | |
142 | elt = ring->ring->elements + idx * ring->element_size; | |
143 | ||
144 | memcpy((void *)elt, new_elt, ring->element_size); | |
145 | ||
146 | header->prod++; | |
147 | ||
148 | mb(); | |
149 | ||
150 | if (header->prod == header->notify_on_prod) | |
151 | outb(0, ring->prod_notify); | |
152 | ||
153 | spin_unlock_irqrestore(&ring->lock, flags); | |
154 | return 0; | |
155 | } | |
156 | ||
6d01f1f5 DA |
157 | static bool qxl_ring_pop(struct qxl_ring *ring, |
158 | void *element) | |
f64122c1 DA |
159 | { |
160 | volatile struct qxl_ring_header *header = &(ring->ring->header); | |
161 | volatile uint8_t *ring_elt; | |
162 | int idx; | |
163 | unsigned long flags; | |
408799eb | 164 | |
f64122c1 DA |
165 | spin_lock_irqsave(&ring->lock, flags); |
166 | if (header->cons == header->prod) { | |
167 | header->notify_on_prod = header->cons + 1; | |
168 | spin_unlock_irqrestore(&ring->lock, flags); | |
169 | return false; | |
170 | } | |
171 | ||
172 | idx = header->cons & (ring->n_elements - 1); | |
173 | ring_elt = ring->ring->elements + idx * ring->element_size; | |
174 | ||
175 | memcpy(element, (void *)ring_elt, ring->element_size); | |
176 | ||
177 | header->cons++; | |
178 | ||
179 | spin_unlock_irqrestore(&ring->lock, flags); | |
180 | return true; | |
181 | } | |
182 | ||
f64122c1 DA |
183 | int |
184 | qxl_push_command_ring_release(struct qxl_device *qdev, struct qxl_release *release, | |
185 | uint32_t type, bool interruptible) | |
186 | { | |
187 | struct qxl_command cmd; | |
188 | ||
189 | cmd.type = type; | |
7a31805b | 190 | cmd.data = qxl_bo_physical_address(qdev, release->release_bo, release->release_offset); |
f64122c1 DA |
191 | |
192 | return qxl_ring_push(qdev->command_ring, &cmd, interruptible); | |
193 | } | |
194 | ||
195 | int | |
196 | qxl_push_cursor_ring_release(struct qxl_device *qdev, struct qxl_release *release, | |
197 | uint32_t type, bool interruptible) | |
198 | { | |
199 | struct qxl_command cmd; | |
200 | ||
201 | cmd.type = type; | |
7a31805b | 202 | cmd.data = qxl_bo_physical_address(qdev, release->release_bo, release->release_offset); |
f64122c1 DA |
203 | |
204 | return qxl_ring_push(qdev->cursor_ring, &cmd, interruptible); | |
205 | } | |
206 | ||
207 | bool qxl_queue_garbage_collect(struct qxl_device *qdev, bool flush) | |
208 | { | |
209 | if (!qxl_check_idle(qdev->release_ring)) { | |
7b2d16f5 | 210 | schedule_work(&qdev->gc_work); |
f64122c1 DA |
211 | if (flush) |
212 | flush_work(&qdev->gc_work); | |
213 | return true; | |
214 | } | |
215 | return false; | |
216 | } | |
217 | ||
218 | int qxl_garbage_collect(struct qxl_device *qdev) | |
219 | { | |
220 | struct qxl_release *release; | |
221 | uint64_t id, next_id; | |
222 | int i = 0; | |
f64122c1 DA |
223 | union qxl_release_info *info; |
224 | ||
225 | while (qxl_ring_pop(qdev->release_ring, &id)) { | |
56cbcb6c | 226 | DRM_DEBUG_DRIVER("popped %lld\n", id); |
f64122c1 DA |
227 | while (id) { |
228 | release = qxl_release_from_id_locked(qdev, id); | |
229 | if (release == NULL) | |
230 | break; | |
231 | ||
f64122c1 DA |
232 | info = qxl_release_map(qdev, release); |
233 | next_id = info->next; | |
234 | qxl_release_unmap(qdev, release, info); | |
235 | ||
56cbcb6c GH |
236 | DRM_DEBUG_DRIVER("popped %lld, next %lld\n", id, |
237 | next_id); | |
f64122c1 DA |
238 | |
239 | switch (release->type) { | |
240 | case QXL_RELEASE_DRAWABLE: | |
241 | case QXL_RELEASE_SURFACE_CMD: | |
242 | case QXL_RELEASE_CURSOR_CMD: | |
243 | break; | |
244 | default: | |
245 | DRM_ERROR("unexpected release type\n"); | |
246 | break; | |
247 | } | |
248 | id = next_id; | |
249 | ||
250 | qxl_release_free(qdev, release); | |
251 | ++i; | |
252 | } | |
253 | } | |
254 | ||
56cbcb6c | 255 | DRM_DEBUG_DRIVER("%d\n", i); |
f64122c1 DA |
256 | |
257 | return i; | |
258 | } | |
259 | ||
8002db63 DA |
260 | int qxl_alloc_bo_reserved(struct qxl_device *qdev, |
261 | struct qxl_release *release, | |
262 | unsigned long size, | |
f64122c1 DA |
263 | struct qxl_bo **_bo) |
264 | { | |
265 | struct qxl_bo *bo; | |
266 | int ret; | |
267 | ||
268 | ret = qxl_bo_create(qdev, size, false /* not kernel - device */, | |
4f49ec92 | 269 | false, QXL_GEM_DOMAIN_VRAM, NULL, &bo); |
f64122c1 DA |
270 | if (ret) { |
271 | DRM_ERROR("failed to allocate VRAM BO\n"); | |
272 | return ret; | |
273 | } | |
8002db63 DA |
274 | ret = qxl_release_list_add(release, bo); |
275 | if (ret) | |
f64122c1 DA |
276 | goto out_unref; |
277 | ||
278 | *_bo = bo; | |
279 | return 0; | |
280 | out_unref: | |
281 | qxl_bo_unref(&bo); | |
8002db63 | 282 | return ret; |
f64122c1 DA |
283 | } |
284 | ||
a6ac1bc3 | 285 | static int wait_for_io_cmd_user(struct qxl_device *qdev, uint8_t val, long port, bool intr) |
f64122c1 DA |
286 | { |
287 | int irq_num; | |
288 | long addr = qdev->io_base + port; | |
289 | int ret; | |
290 | ||
291 | mutex_lock(&qdev->async_io_mutex); | |
292 | irq_num = atomic_read(&qdev->irq_received_io_cmd); | |
f64122c1 | 293 | if (qdev->last_sent_io_cmd > irq_num) { |
a6ac1bc3 DA |
294 | if (intr) |
295 | ret = wait_event_interruptible_timeout(qdev->io_cmd_event, | |
296 | atomic_read(&qdev->irq_received_io_cmd) > irq_num, 5*HZ); | |
297 | else | |
298 | ret = wait_event_timeout(qdev->io_cmd_event, | |
299 | atomic_read(&qdev->irq_received_io_cmd) > irq_num, 5*HZ); | |
300 | /* 0 is timeout, just bail the "hw" has gone away */ | |
301 | if (ret <= 0) | |
f64122c1 DA |
302 | goto out; |
303 | irq_num = atomic_read(&qdev->irq_received_io_cmd); | |
304 | } | |
305 | outb(val, addr); | |
306 | qdev->last_sent_io_cmd = irq_num + 1; | |
a6ac1bc3 DA |
307 | if (intr) |
308 | ret = wait_event_interruptible_timeout(qdev->io_cmd_event, | |
309 | atomic_read(&qdev->irq_received_io_cmd) > irq_num, 5*HZ); | |
310 | else | |
311 | ret = wait_event_timeout(qdev->io_cmd_event, | |
312 | atomic_read(&qdev->irq_received_io_cmd) > irq_num, 5*HZ); | |
f64122c1 | 313 | out: |
a6ac1bc3 DA |
314 | if (ret > 0) |
315 | ret = 0; | |
f64122c1 DA |
316 | mutex_unlock(&qdev->async_io_mutex); |
317 | return ret; | |
318 | } | |
319 | ||
320 | static void wait_for_io_cmd(struct qxl_device *qdev, uint8_t val, long port) | |
321 | { | |
322 | int ret; | |
323 | ||
324 | restart: | |
a6ac1bc3 | 325 | ret = wait_for_io_cmd_user(qdev, val, port, false); |
f64122c1 DA |
326 | if (ret == -ERESTARTSYS) |
327 | goto restart; | |
328 | } | |
329 | ||
330 | int qxl_io_update_area(struct qxl_device *qdev, struct qxl_bo *surf, | |
331 | const struct qxl_rect *area) | |
332 | { | |
333 | int surface_id; | |
334 | uint32_t surface_width, surface_height; | |
335 | int ret; | |
336 | ||
337 | if (!surf->hw_surf_alloc) | |
338 | DRM_ERROR("got io update area with no hw surface\n"); | |
339 | ||
340 | if (surf->is_primary) | |
341 | surface_id = 0; | |
342 | else | |
343 | surface_id = surf->surface_id; | |
344 | surface_width = surf->surf.width; | |
345 | surface_height = surf->surf.height; | |
346 | ||
347 | if (area->left < 0 || area->top < 0 || | |
735581a0 | 348 | area->right > surface_width || area->bottom > surface_height) |
f64122c1 | 349 | return -EINVAL; |
735581a0 | 350 | |
f64122c1 DA |
351 | mutex_lock(&qdev->update_area_mutex); |
352 | qdev->ram_header->update_area = *area; | |
353 | qdev->ram_header->update_surface = surface_id; | |
a6ac1bc3 | 354 | ret = wait_for_io_cmd_user(qdev, 0, QXL_IO_UPDATE_AREA_ASYNC, true); |
f64122c1 DA |
355 | mutex_unlock(&qdev->update_area_mutex); |
356 | return ret; | |
357 | } | |
358 | ||
359 | void qxl_io_notify_oom(struct qxl_device *qdev) | |
360 | { | |
361 | outb(0, qdev->io_base + QXL_IO_NOTIFY_OOM); | |
362 | } | |
363 | ||
364 | void qxl_io_flush_release(struct qxl_device *qdev) | |
365 | { | |
366 | outb(0, qdev->io_base + QXL_IO_FLUSH_RELEASE); | |
367 | } | |
368 | ||
369 | void qxl_io_flush_surfaces(struct qxl_device *qdev) | |
370 | { | |
371 | wait_for_io_cmd(qdev, 0, QXL_IO_FLUSH_SURFACES_ASYNC); | |
372 | } | |
373 | ||
f64122c1 DA |
374 | void qxl_io_destroy_primary(struct qxl_device *qdev) |
375 | { | |
376 | wait_for_io_cmd(qdev, 0, QXL_IO_DESTROY_PRIMARY_ASYNC); | |
4979904c GH |
377 | qdev->primary_bo->is_primary = false; |
378 | drm_gem_object_put_unlocked(&qdev->primary_bo->gem_base); | |
16620544 | 379 | qdev->primary_bo = NULL; |
f64122c1 DA |
380 | } |
381 | ||
fb7ebc01 | 382 | void qxl_io_create_primary(struct qxl_device *qdev, struct qxl_bo *bo) |
f64122c1 DA |
383 | { |
384 | struct qxl_surface_create *create; | |
385 | ||
16620544 GH |
386 | if (WARN_ON(qdev->primary_bo)) |
387 | return; | |
388 | ||
56cbcb6c | 389 | DRM_DEBUG_DRIVER("qdev %p, ram_header %p\n", qdev, qdev->ram_header); |
f64122c1 DA |
390 | create = &qdev->ram_header->create_surface; |
391 | create->format = bo->surf.format; | |
07f8d9bd DA |
392 | create->width = bo->surf.width; |
393 | create->height = bo->surf.height; | |
f64122c1 | 394 | create->stride = bo->surf.stride; |
4979904c | 395 | create->mem = qxl_bo_physical_address(qdev, bo, 0); |
f64122c1 | 396 | |
56cbcb6c | 397 | DRM_DEBUG_DRIVER("mem = %llx, from %p\n", create->mem, bo->kptr); |
f64122c1 DA |
398 | |
399 | create->flags = QXL_SURF_FLAG_KEEP_DATA; | |
400 | create->type = QXL_SURF_TYPE_PRIMARY; | |
401 | ||
402 | wait_for_io_cmd(qdev, 0, QXL_IO_CREATE_PRIMARY_ASYNC); | |
16620544 | 403 | qdev->primary_bo = bo; |
4979904c GH |
404 | qdev->primary_bo->is_primary = true; |
405 | drm_gem_object_get(&qdev->primary_bo->gem_base); | |
f64122c1 DA |
406 | } |
407 | ||
408 | void qxl_io_memslot_add(struct qxl_device *qdev, uint8_t id) | |
409 | { | |
56cbcb6c | 410 | DRM_DEBUG_DRIVER("qxl_memslot_add %d\n", id); |
f64122c1 DA |
411 | wait_for_io_cmd(qdev, id, QXL_IO_MEMSLOT_ADD_ASYNC); |
412 | } | |
413 | ||
f64122c1 DA |
414 | void qxl_io_reset(struct qxl_device *qdev) |
415 | { | |
416 | outb(0, qdev->io_base + QXL_IO_RESET); | |
417 | } | |
418 | ||
419 | void qxl_io_monitors_config(struct qxl_device *qdev) | |
420 | { | |
f64122c1 DA |
421 | wait_for_io_cmd(qdev, 0, QXL_IO_MONITORS_CONFIG_ASYNC); |
422 | } | |
423 | ||
424 | int qxl_surface_id_alloc(struct qxl_device *qdev, | |
425 | struct qxl_bo *surf) | |
426 | { | |
307b9c02 | 427 | uint32_t handle; |
f64122c1 DA |
428 | int idr_ret; |
429 | int count = 0; | |
430 | again: | |
307b9c02 | 431 | idr_preload(GFP_ATOMIC); |
f64122c1 | 432 | spin_lock(&qdev->surf_id_idr_lock); |
307b9c02 | 433 | idr_ret = idr_alloc(&qdev->surf_id_idr, NULL, 1, 0, GFP_NOWAIT); |
f64122c1 | 434 | spin_unlock(&qdev->surf_id_idr_lock); |
307b9c02 DA |
435 | idr_preload_end(); |
436 | if (idr_ret < 0) | |
437 | return idr_ret; | |
438 | handle = idr_ret; | |
f64122c1 DA |
439 | |
440 | if (handle >= qdev->rom->n_surfaces) { | |
441 | count++; | |
442 | spin_lock(&qdev->surf_id_idr_lock); | |
443 | idr_remove(&qdev->surf_id_idr, handle); | |
444 | spin_unlock(&qdev->surf_id_idr_lock); | |
445 | qxl_reap_surface_id(qdev, 2); | |
446 | goto again; | |
447 | } | |
448 | surf->surface_id = handle; | |
449 | ||
450 | spin_lock(&qdev->surf_id_idr_lock); | |
451 | qdev->last_alloced_surf_id = handle; | |
452 | spin_unlock(&qdev->surf_id_idr_lock); | |
f64122c1 DA |
453 | return 0; |
454 | } | |
455 | ||
456 | void qxl_surface_id_dealloc(struct qxl_device *qdev, | |
457 | uint32_t surface_id) | |
458 | { | |
459 | spin_lock(&qdev->surf_id_idr_lock); | |
460 | idr_remove(&qdev->surf_id_idr, surface_id); | |
461 | spin_unlock(&qdev->surf_id_idr_lock); | |
462 | } | |
463 | ||
464 | int qxl_hw_surface_alloc(struct qxl_device *qdev, | |
edd38a1e | 465 | struct qxl_bo *surf) |
f64122c1 DA |
466 | { |
467 | struct qxl_surface_cmd *cmd; | |
468 | struct qxl_release *release; | |
469 | int ret; | |
470 | ||
471 | if (surf->hw_surf_alloc) | |
472 | return 0; | |
473 | ||
474 | ret = qxl_alloc_surface_release_reserved(qdev, QXL_SURFACE_CMD_CREATE, | |
475 | NULL, | |
476 | &release); | |
477 | if (ret) | |
478 | return ret; | |
479 | ||
8002db63 DA |
480 | ret = qxl_release_reserve_list(release, true); |
481 | if (ret) | |
482 | return ret; | |
483 | ||
f64122c1 DA |
484 | cmd = (struct qxl_surface_cmd *)qxl_release_map(qdev, release); |
485 | cmd->type = QXL_SURFACE_CMD_CREATE; | |
2fa19535 | 486 | cmd->flags = QXL_SURF_FLAG_KEEP_DATA; |
f64122c1 DA |
487 | cmd->u.surface_create.format = surf->surf.format; |
488 | cmd->u.surface_create.width = surf->surf.width; | |
489 | cmd->u.surface_create.height = surf->surf.height; | |
490 | cmd->u.surface_create.stride = surf->surf.stride; | |
edd38a1e | 491 | cmd->u.surface_create.data = qxl_bo_physical_address(qdev, surf, 0); |
f64122c1 DA |
492 | cmd->surface_id = surf->surface_id; |
493 | qxl_release_unmap(qdev, release, &cmd->release_info); | |
494 | ||
495 | surf->surf_create = release; | |
496 | ||
8002db63 | 497 | /* no need to add a release to the fence for this surface bo, |
f64122c1 DA |
498 | since it is only released when we ask to destroy the surface |
499 | and it would never signal otherwise */ | |
f64122c1 | 500 | qxl_push_command_ring_release(qdev, release, QXL_CMD_SURFACE, false); |
8002db63 | 501 | qxl_release_fence_buffer_objects(release); |
f64122c1 DA |
502 | |
503 | surf->hw_surf_alloc = true; | |
504 | spin_lock(&qdev->surf_id_idr_lock); | |
505 | idr_replace(&qdev->surf_id_idr, surf, surf->surface_id); | |
506 | spin_unlock(&qdev->surf_id_idr_lock); | |
507 | return 0; | |
508 | } | |
509 | ||
510 | int qxl_hw_surface_dealloc(struct qxl_device *qdev, | |
511 | struct qxl_bo *surf) | |
512 | { | |
513 | struct qxl_surface_cmd *cmd; | |
514 | struct qxl_release *release; | |
515 | int ret; | |
516 | int id; | |
517 | ||
518 | if (!surf->hw_surf_alloc) | |
519 | return 0; | |
520 | ||
521 | ret = qxl_alloc_surface_release_reserved(qdev, QXL_SURFACE_CMD_DESTROY, | |
522 | surf->surf_create, | |
523 | &release); | |
524 | if (ret) | |
525 | return ret; | |
526 | ||
527 | surf->surf_create = NULL; | |
528 | /* remove the surface from the idr, but not the surface id yet */ | |
529 | spin_lock(&qdev->surf_id_idr_lock); | |
530 | idr_replace(&qdev->surf_id_idr, NULL, surf->surface_id); | |
531 | spin_unlock(&qdev->surf_id_idr_lock); | |
532 | surf->hw_surf_alloc = false; | |
533 | ||
534 | id = surf->surface_id; | |
535 | surf->surface_id = 0; | |
536 | ||
537 | release->surface_release_id = id; | |
538 | cmd = (struct qxl_surface_cmd *)qxl_release_map(qdev, release); | |
539 | cmd->type = QXL_SURFACE_CMD_DESTROY; | |
540 | cmd->surface_id = id; | |
541 | qxl_release_unmap(qdev, release, &cmd->release_info); | |
542 | ||
f64122c1 DA |
543 | qxl_push_command_ring_release(qdev, release, QXL_CMD_SURFACE, false); |
544 | ||
8002db63 | 545 | qxl_release_fence_buffer_objects(release); |
f64122c1 DA |
546 | |
547 | return 0; | |
548 | } | |
549 | ||
e4a76442 | 550 | static int qxl_update_surface(struct qxl_device *qdev, struct qxl_bo *surf) |
f64122c1 DA |
551 | { |
552 | struct qxl_rect rect; | |
553 | int ret; | |
554 | ||
555 | /* if we are evicting, we need to make sure the surface is up | |
556 | to date */ | |
557 | rect.left = 0; | |
558 | rect.right = surf->surf.width; | |
559 | rect.top = 0; | |
560 | rect.bottom = surf->surf.height; | |
561 | retry: | |
562 | ret = qxl_io_update_area(qdev, surf, &rect); | |
563 | if (ret == -ERESTARTSYS) | |
564 | goto retry; | |
565 | return ret; | |
566 | } | |
567 | ||
6d01f1f5 | 568 | static void qxl_surface_evict_locked(struct qxl_device *qdev, struct qxl_bo *surf, bool do_update_area) |
f64122c1 DA |
569 | { |
570 | /* no need to update area if we are just freeing the surface normally */ | |
571 | if (do_update_area) | |
572 | qxl_update_surface(qdev, surf); | |
573 | ||
574 | /* nuke the surface id at the hw */ | |
575 | qxl_hw_surface_dealloc(qdev, surf); | |
576 | } | |
577 | ||
578 | void qxl_surface_evict(struct qxl_device *qdev, struct qxl_bo *surf, bool do_update_area) | |
579 | { | |
580 | mutex_lock(&qdev->surf_evict_mutex); | |
581 | qxl_surface_evict_locked(qdev, surf, do_update_area); | |
582 | mutex_unlock(&qdev->surf_evict_mutex); | |
583 | } | |
584 | ||
585 | static int qxl_reap_surf(struct qxl_device *qdev, struct qxl_bo *surf, bool stall) | |
586 | { | |
587 | int ret; | |
588 | ||
589 | ret = qxl_bo_reserve(surf, false); | |
c610c713 FZ |
590 | if (ret) |
591 | return ret; | |
f64122c1 | 592 | |
f64122c1 DA |
593 | if (stall) |
594 | mutex_unlock(&qdev->surf_evict_mutex); | |
595 | ||
8aa6d4fc | 596 | ret = ttm_bo_wait(&surf->tbo, true, !stall); |
f64122c1 DA |
597 | |
598 | if (stall) | |
599 | mutex_lock(&qdev->surf_evict_mutex); | |
c610c713 | 600 | if (ret) { |
f64122c1 | 601 | qxl_bo_unreserve(surf); |
c610c713 | 602 | return ret; |
f64122c1 DA |
603 | } |
604 | ||
605 | qxl_surface_evict_locked(qdev, surf, true); | |
606 | qxl_bo_unreserve(surf); | |
607 | return 0; | |
608 | } | |
609 | ||
610 | static int qxl_reap_surface_id(struct qxl_device *qdev, int max_to_reap) | |
611 | { | |
612 | int num_reaped = 0; | |
613 | int i, ret; | |
614 | bool stall = false; | |
615 | int start = 0; | |
616 | ||
617 | mutex_lock(&qdev->surf_evict_mutex); | |
618 | again: | |
619 | ||
620 | spin_lock(&qdev->surf_id_idr_lock); | |
621 | start = qdev->last_alloced_surf_id + 1; | |
622 | spin_unlock(&qdev->surf_id_idr_lock); | |
623 | ||
624 | for (i = start; i < start + qdev->rom->n_surfaces; i++) { | |
625 | void *objptr; | |
626 | int surfid = i % qdev->rom->n_surfaces; | |
627 | ||
628 | /* this avoids the case where the objects is in the | |
629 | idr but has been evicted half way - its makes | |
630 | the idr lookup atomic with the eviction */ | |
631 | spin_lock(&qdev->surf_id_idr_lock); | |
632 | objptr = idr_find(&qdev->surf_id_idr, surfid); | |
633 | spin_unlock(&qdev->surf_id_idr_lock); | |
634 | ||
635 | if (!objptr) | |
636 | continue; | |
637 | ||
638 | ret = qxl_reap_surf(qdev, objptr, stall); | |
639 | if (ret == 0) | |
640 | num_reaped++; | |
641 | if (num_reaped >= max_to_reap) | |
642 | break; | |
643 | } | |
644 | if (num_reaped == 0 && stall == false) { | |
645 | stall = true; | |
646 | goto again; | |
647 | } | |
648 | ||
649 | mutex_unlock(&qdev->surf_evict_mutex); | |
650 | if (num_reaped) { | |
651 | usleep_range(500, 1000); | |
652 | qxl_queue_garbage_collect(qdev, true); | |
653 | } | |
654 | ||
655 | return 0; | |
656 | } |