/* * Copyright 2013 Red Hat Inc. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * Authors: Dave Airlie * Alon Levy
*/
/* QXL cmd/ring handling */
#include <linux/delay.h>
#include <drm/drm_util.h>
#include"qxl_drv.h" #include"qxl_object.h"
staticint qxl_reap_surface_id(struct qxl_device *qdev, int max_to_reap);
struct ring { struct qxl_ring_header header;
uint8_t elements[];
};
struct qxl_ring { struct ring *ring; int element_size; int n_elements; int prod_notify;
wait_queue_head_t *push_event;
spinlock_t lock;
};
bool qxl_queue_garbage_collect(struct qxl_device *qdev, bool flush)
{ if (!qxl_check_idle(qdev->release_ring)) {
schedule_work(&qdev->gc_work); if (flush)
flush_work(&qdev->gc_work); returntrue;
} returnfalse;
}
int qxl_garbage_collect(struct qxl_device *qdev)
{ struct qxl_release *release;
uint64_t id, next_id; int i = 0; union qxl_release_info *info;
while (qxl_ring_pop(qdev->release_ring, &id)) {
DRM_DEBUG_DRIVER("popped %lld\n", id); while (id) {
release = qxl_release_from_id_locked(qdev, id); if (release == NULL) break;
info = qxl_release_map(qdev, release);
next_id = info->next;
qxl_release_unmap(qdev, release, info);
DRM_DEBUG_DRIVER("popped %lld, next %lld\n", id,
next_id);
switch (release->type) { case QXL_RELEASE_DRAWABLE: case QXL_RELEASE_SURFACE_CMD: case QXL_RELEASE_CURSOR_CMD: break; default:
DRM_ERROR("unexpected release type\n"); break;
}
id = next_id;
/* no need to add a release to the fence for this surface bo, since it is only released when we ask to destroy the surface
and it would never signal otherwise */
qxl_release_fence_buffer_objects(release);
qxl_push_command_ring_release(qdev, release, QXL_CMD_SURFACE, false);
int qxl_hw_surface_dealloc(struct qxl_device *qdev, struct qxl_bo *surf)
{ struct qxl_surface_cmd *cmd; struct qxl_release *release; int ret; int id;
if (!surf->hw_surf_alloc) return 0;
ret = qxl_alloc_surface_release_reserved(qdev, QXL_SURFACE_CMD_DESTROY,
surf->surf_create,
&release); if (ret) return ret;
surf->surf_create = NULL; /* remove the surface from the idr, but not the surface id yet */
spin_lock(&qdev->surf_id_idr_lock);
idr_replace(&qdev->surf_id_idr, NULL, surf->surface_id);
spin_unlock(&qdev->surf_id_idr_lock);
surf->hw_surf_alloc = false;
/* if we are evicting, we need to make sure the surface is up
to date */
rect.left = 0;
rect.right = surf->surf.width;
rect.top = 0;
rect.bottom = surf->surf.height;
retry:
ret = qxl_io_update_area(qdev, surf, &rect); if (ret == -ERESTARTSYS) goto retry; return ret;
}
staticvoid qxl_surface_evict_locked(struct qxl_device *qdev, struct qxl_bo *surf, bool do_update_area)
{ /* no need to update area if we are just freeing the surface normally */ if (do_update_area)
qxl_update_surface(qdev, surf);
/* nuke the surface id at the hw */
qxl_hw_surface_dealloc(qdev, surf);
}
if (stall) {
ret = dma_resv_wait_timeout(surf->tbo.base.resv,
DMA_RESV_USAGE_BOOKKEEP, true,
15 * HZ); if (ret > 0)
ret = 0; elseif (ret == 0)
ret = -EBUSY;
} else {
ret = dma_resv_test_signaled(surf->tbo.base.resv,
DMA_RESV_USAGE_BOOKKEEP);
ret = ret ? -EBUSY : 0;
}
if (stall)
mutex_lock(&qdev->surf_evict_mutex); if (ret) {
qxl_bo_unreserve(surf); return ret;
}
for (i = start; i < start + qdev->rom->n_surfaces; i++) { void *objptr; int surfid = i % qdev->rom->n_surfaces;
/* this avoids the case where the objects is in the idr but has been evicted half way - its makes
the idr lookup atomic with the eviction */
spin_lock(&qdev->surf_id_idr_lock);
objptr = idr_find(&qdev->surf_id_idr, surfid);
spin_unlock(&qdev->surf_id_idr_lock);
if (!objptr) continue;
ret = qxl_reap_surf(qdev, objptr, stall); if (ret == 0)
num_reaped++; if (num_reaped >= max_to_reap) break;
} if (num_reaped == 0 && stall == false) {
stall = true; goto again;
}
mutex_unlock(&qdev->surf_evict_mutex); if (num_reaped) {
usleep_range(500, 1000);
qxl_queue_garbage_collect(qdev, true);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.