Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8002db63 authored by Dave Airlie's avatar Dave Airlie
Browse files

qxl: convert qxl driver to proper use for reservations



The recent addition of lockdep support to reservations and their subsequent
use by TTM showed up a number of potential problems with the way qxl was using
TTM objects.

a) it was allocating objects, and reserving them later without validating
underneath the reservation, which meant in extreme conditions the objects could
be evicted before the reservation ever used them.

b) it was reserving objects straight after allocating them, but with no
ability to back off should the reservations fail. It now allocates the necessary
objects then does a complete reservation pass on them to avoid deadlocks.

c) it had two lists per release tracking objects, unnecessary complicating
the reservation process.

This patch removes the dual object tracking, adds reservations ticket support
to the release and fence object handling. It then ports the internal fb
drawing code and the userspace facing ioctl to use the new interfaces properly,
along with cleanup up the error path handling in some codepaths.

Signed-off-by: default avatarDave Airlie <airlied@redhat.com>
parent 4f49ec92
Loading
Loading
Loading
Loading
+17 −23
Original line number Diff line number Diff line
@@ -179,9 +179,10 @@ qxl_push_command_ring_release(struct qxl_device *qdev, struct qxl_release *relea
			      uint32_t type, bool interruptible)
{
	struct qxl_command cmd;
	struct qxl_bo_list *entry = list_first_entry(&release->bos, struct qxl_bo_list, tv.head);

	cmd.type = type;
	cmd.data = qxl_bo_physical_address(qdev, release->bos[0], release->release_offset);
	cmd.data = qxl_bo_physical_address(qdev, to_qxl_bo(entry->tv.bo), release->release_offset);

	return qxl_ring_push(qdev->command_ring, &cmd, interruptible);
}
@@ -191,9 +192,10 @@ qxl_push_cursor_ring_release(struct qxl_device *qdev, struct qxl_release *releas
			     uint32_t type, bool interruptible)
{
	struct qxl_command cmd;
	struct qxl_bo_list *entry = list_first_entry(&release->bos, struct qxl_bo_list, tv.head);

	cmd.type = type;
	cmd.data = qxl_bo_physical_address(qdev, release->bos[0], release->release_offset);
	cmd.data = qxl_bo_physical_address(qdev, to_qxl_bo(entry->tv.bo), release->release_offset);

	return qxl_ring_push(qdev->cursor_ring, &cmd, interruptible);
}
@@ -214,7 +216,6 @@ int qxl_garbage_collect(struct qxl_device *qdev)
	struct qxl_release *release;
	uint64_t id, next_id;
	int i = 0;
	int ret;
	union qxl_release_info *info;

	while (qxl_ring_pop(qdev->release_ring, &id)) {
@@ -224,17 +225,10 @@ int qxl_garbage_collect(struct qxl_device *qdev)
			if (release == NULL)
				break;

			ret = qxl_release_reserve(qdev, release, false);
			if (ret) {
				qxl_io_log(qdev, "failed to reserve release on garbage collect %lld\n", id);
				DRM_ERROR("failed to reserve release %lld\n", id);
			}

			info = qxl_release_map(qdev, release);
			next_id = info->next;
			qxl_release_unmap(qdev, release, info);

			qxl_release_unreserve(qdev, release);
			QXL_INFO(qdev, "popped %lld, next %lld\n", id,
				next_id);

@@ -259,7 +253,9 @@ int qxl_garbage_collect(struct qxl_device *qdev)
	return i;
}

int qxl_alloc_bo_reserved(struct qxl_device *qdev, unsigned long size,
int qxl_alloc_bo_reserved(struct qxl_device *qdev,
			  struct qxl_release *release,
			  unsigned long size,
			  struct qxl_bo **_bo)
{
	struct qxl_bo *bo;
@@ -271,15 +267,15 @@ int qxl_alloc_bo_reserved(struct qxl_device *qdev, unsigned long size,
		DRM_ERROR("failed to allocate VRAM BO\n");
		return ret;
	}
	ret = qxl_bo_reserve(bo, false);
	if (unlikely(ret != 0))
	ret = qxl_release_list_add(release, bo);
	if (ret)
		goto out_unref;

	*_bo = bo;
	return 0;
out_unref:
	qxl_bo_unref(&bo);
	return 0;
	return ret;
}

static int wait_for_io_cmd_user(struct qxl_device *qdev, uint8_t val, long port, bool intr)
@@ -503,6 +499,10 @@ int qxl_hw_surface_alloc(struct qxl_device *qdev,
	if (ret)
		return ret;

	ret = qxl_release_reserve_list(release, true);
	if (ret)
		return ret;

	cmd = (struct qxl_surface_cmd *)qxl_release_map(qdev, release);
	cmd->type = QXL_SURFACE_CMD_CREATE;
	cmd->u.surface_create.format = surf->surf.format;
@@ -524,14 +524,11 @@ int qxl_hw_surface_alloc(struct qxl_device *qdev,

	surf->surf_create = release;

	/* no need to add a release to the fence for this bo,
	/* 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_fence_releaseable(qdev, release);

	qxl_push_command_ring_release(qdev, release, QXL_CMD_SURFACE, false);

	qxl_release_unreserve(qdev, release);
	qxl_release_fence_buffer_objects(release);

	surf->hw_surf_alloc = true;
	spin_lock(&qdev->surf_id_idr_lock);
@@ -573,12 +570,9 @@ int qxl_hw_surface_dealloc(struct qxl_device *qdev,
	cmd->surface_id = id;
	qxl_release_unmap(qdev, release, &cmd->release_info);

	qxl_fence_releaseable(qdev, release);

	qxl_push_command_ring_release(qdev, release, QXL_CMD_SURFACE, false);

	qxl_release_unreserve(qdev, release);

	qxl_release_fence_buffer_objects(release);

	return 0;
}
+46 −24
Original line number Diff line number Diff line
@@ -179,7 +179,7 @@ static void qxl_crtc_destroy(struct drm_crtc *crtc)
	kfree(qxl_crtc);
}

static void
static int
qxl_hide_cursor(struct qxl_device *qdev)
{
	struct qxl_release *release;
@@ -188,14 +188,22 @@ qxl_hide_cursor(struct qxl_device *qdev)

	ret = qxl_alloc_release_reserved(qdev, sizeof(*cmd), QXL_RELEASE_CURSOR_CMD,
					 &release, NULL);
	if (ret)
		return ret;

	ret = qxl_release_reserve_list(release, true);
	if (ret) {
		qxl_release_free(qdev, release);
		return ret;
	}

	cmd = (struct qxl_cursor_cmd *)qxl_release_map(qdev, release);
	cmd->type = QXL_CURSOR_HIDE;
	qxl_release_unmap(qdev, release, &cmd->release_info);

	qxl_fence_releaseable(qdev, release);
	qxl_push_cursor_ring_release(qdev, release, QXL_CMD_CURSOR, false);
	qxl_release_unreserve(qdev, release);
	qxl_release_fence_buffer_objects(release);
	return 0;
}

static int qxl_crtc_cursor_set2(struct drm_crtc *crtc,
@@ -216,10 +224,8 @@ static int qxl_crtc_cursor_set2(struct drm_crtc *crtc,

	int size = 64*64*4;
	int ret = 0;
	if (!handle) {
		qxl_hide_cursor(qdev);
		return 0;
	}
	if (!handle)
		return qxl_hide_cursor(qdev);

	obj = drm_gem_object_lookup(crtc->dev, file_priv, handle);
	if (!obj) {
@@ -234,8 +240,9 @@ static int qxl_crtc_cursor_set2(struct drm_crtc *crtc,
		goto out_unref;

	ret = qxl_bo_pin(user_bo, QXL_GEM_DOMAIN_CPU, NULL);
	qxl_bo_unreserve(user_bo);
	if (ret)
		goto out_unreserve;
		goto out_unref;

	ret = qxl_bo_kmap(user_bo, &user_ptr);
	if (ret)
@@ -246,14 +253,20 @@ static int qxl_crtc_cursor_set2(struct drm_crtc *crtc,
					 &release, NULL);
	if (ret)
		goto out_kunmap;
	ret = qxl_alloc_bo_reserved(qdev, sizeof(struct qxl_cursor) + size,

	ret = qxl_alloc_bo_reserved(qdev, release, sizeof(struct qxl_cursor) + size,
			   &cursor_bo);
	if (ret)
		goto out_free_release;
	ret = qxl_bo_kmap(cursor_bo, (void **)&cursor);

	ret = qxl_release_reserve_list(release, false);
	if (ret)
		goto out_free_bo;

	ret = qxl_bo_kmap(cursor_bo, (void **)&cursor);
	if (ret)
		goto out_backoff;

	cursor->header.unique = 0;
	cursor->header.type = SPICE_CURSOR_TYPE_ALPHA;
	cursor->header.width = 64;
@@ -269,11 +282,7 @@ static int qxl_crtc_cursor_set2(struct drm_crtc *crtc,

	qxl_bo_kunmap(cursor_bo);

	/* finish with the userspace bo */
	qxl_bo_kunmap(user_bo);
	qxl_bo_unpin(user_bo);
	qxl_bo_unreserve(user_bo);
	drm_gem_object_unreference_unlocked(obj);

	cmd = (struct qxl_cursor_cmd *)qxl_release_map(qdev, release);
	cmd->type = QXL_CURSOR_SET;
@@ -281,30 +290,35 @@ static int qxl_crtc_cursor_set2(struct drm_crtc *crtc,
	cmd->u.set.position.y = qcrtc->cur_y;

	cmd->u.set.shape = qxl_bo_physical_address(qdev, cursor_bo, 0);
	qxl_release_add_res(qdev, release, cursor_bo);

	cmd->u.set.visible = 1;
	qxl_release_unmap(qdev, release, &cmd->release_info);

	qxl_fence_releaseable(qdev, release);
	qxl_push_cursor_ring_release(qdev, release, QXL_CMD_CURSOR, false);
	qxl_release_unreserve(qdev, release);
	qxl_release_fence_buffer_objects(release);

	/* finish with the userspace bo */
	ret = qxl_bo_reserve(user_bo, false);
	if (!ret) {
		qxl_bo_unpin(user_bo);
		qxl_bo_unreserve(user_bo);
	}
	drm_gem_object_unreference_unlocked(obj);

	qxl_bo_unreserve(cursor_bo);
	qxl_bo_unref(&cursor_bo);

	return ret;

out_backoff:
	qxl_release_backoff_reserve_list(release);
out_free_bo:
	qxl_bo_unref(&cursor_bo);
out_free_release:
	qxl_release_unreserve(qdev, release);
	qxl_release_free(qdev, release);
out_kunmap:
	qxl_bo_kunmap(user_bo);
out_unpin:
	qxl_bo_unpin(user_bo);
out_unreserve:
	qxl_bo_unreserve(user_bo);
out_unref:
	drm_gem_object_unreference_unlocked(obj);
	return ret;
@@ -322,6 +336,14 @@ static int qxl_crtc_cursor_move(struct drm_crtc *crtc,

	ret = qxl_alloc_release_reserved(qdev, sizeof(*cmd), QXL_RELEASE_CURSOR_CMD,
				   &release, NULL);
	if (ret)
		return ret;

	ret = qxl_release_reserve_list(release, true);
	if (ret) {
		qxl_release_free(qdev, release);
		return ret;
	}

	qcrtc->cur_x = x;
	qcrtc->cur_y = y;
@@ -332,9 +354,9 @@ static int qxl_crtc_cursor_move(struct drm_crtc *crtc,
	cmd->u.position.y = qcrtc->cur_y;
	qxl_release_unmap(qdev, release, &cmd->release_info);

	qxl_fence_releaseable(qdev, release);
	qxl_push_cursor_ring_release(qdev, release, QXL_CMD_CURSOR, false);
	qxl_release_unreserve(qdev, release);
	qxl_release_fence_buffer_objects(release);

	return 0;
}

+180 −83
Original line number Diff line number Diff line
@@ -23,25 +23,29 @@
#include "qxl_drv.h"
#include "qxl_object.h"

static int alloc_clips(struct qxl_device *qdev,
		       struct qxl_release *release,
		       unsigned num_clips,
		       struct qxl_bo **clips_bo)
{
	int size = sizeof(struct qxl_clip_rects) + sizeof(struct qxl_rect) * num_clips;

	return qxl_alloc_bo_reserved(qdev, release, size, clips_bo);
}

/* returns a pointer to the already allocated qxl_rect array inside
 * the qxl_clip_rects. This is *not* the same as the memory allocated
 * on the device, it is offset to qxl_clip_rects.chunk.data */
static struct qxl_rect *drawable_set_clipping(struct qxl_device *qdev,
					      struct qxl_drawable *drawable,
					      unsigned num_clips,
					      struct qxl_bo **clips_bo,
					      struct qxl_release *release)
					      struct qxl_bo *clips_bo)
{
	struct qxl_clip_rects *dev_clips;
	int ret;
	int size = sizeof(*dev_clips) + sizeof(struct qxl_rect) * num_clips;
	ret = qxl_alloc_bo_reserved(qdev, size, clips_bo);
	if (ret)
		return NULL;

	ret = qxl_bo_kmap(*clips_bo, (void **)&dev_clips);
	ret = qxl_bo_kmap(clips_bo, (void **)&dev_clips);
	if (ret) {
		qxl_bo_unref(clips_bo);
		return NULL;
	}
	dev_clips->num_rects = num_clips;
@@ -51,21 +55,35 @@ static struct qxl_rect *drawable_set_clipping(struct qxl_device *qdev,
	return (struct qxl_rect *)dev_clips->chunk.data;
}

static int
alloc_drawable(struct qxl_device *qdev, struct qxl_release **release)
{
	int ret;
	ret = qxl_alloc_release_reserved(qdev, sizeof(struct qxl_drawable),
					 QXL_RELEASE_DRAWABLE, release,
					 NULL);
	return ret;
}

static void
free_drawable(struct qxl_device *qdev, struct qxl_release *release)
{
	qxl_release_free(qdev, release);
}

/* release needs to be reserved at this point */
static int
make_drawable(struct qxl_device *qdev, int surface, uint8_t type,
	      const struct qxl_rect *rect,
	      struct qxl_release **release)
	      struct qxl_release *release)
{
	struct qxl_drawable *drawable;
	int i, ret;
	int i;

	ret = qxl_alloc_release_reserved(qdev, sizeof(*drawable),
					 QXL_RELEASE_DRAWABLE, release,
					 NULL);
	if (ret)
		return ret;
	drawable = (struct qxl_drawable *)qxl_release_map(qdev, release);
	if (!drawable)
		return -ENOMEM;

	drawable = (struct qxl_drawable *)qxl_release_map(qdev, *release);
	drawable->type = type;

	drawable->surface_id = surface;		/* Only primary for now */
@@ -91,14 +109,23 @@ make_drawable(struct qxl_device *qdev, int surface, uint8_t type,
		drawable->bbox = *rect;

	drawable->mm_time = qdev->rom->mm_clock;
	qxl_release_unmap(qdev, *release, &drawable->release_info);
	qxl_release_unmap(qdev, release, &drawable->release_info);
	return 0;
}

static int qxl_palette_create_1bit(struct qxl_bo **palette_bo,
static int alloc_palette_object(struct qxl_device *qdev,
				struct qxl_release *release,
				struct qxl_bo **palette_bo)
{
	return qxl_alloc_bo_reserved(qdev, release,
				     sizeof(struct qxl_palette) + sizeof(uint32_t) * 2,
				     palette_bo);
}

static int qxl_palette_create_1bit(struct qxl_bo *palette_bo,
				   struct qxl_release *release,
				   const struct qxl_fb_image *qxl_fb_image)
{
	struct qxl_device *qdev = qxl_fb_image->qdev;
	const struct fb_image *fb_image = &qxl_fb_image->fb_image;
	uint32_t visual = qxl_fb_image->visual;
	const uint32_t *pseudo_palette = qxl_fb_image->pseudo_palette;
@@ -108,12 +135,7 @@ static int qxl_palette_create_1bit(struct qxl_bo **palette_bo,
	static uint64_t unique; /* we make no attempt to actually set this
				 * correctly globaly, since that would require
				 * tracking all of our palettes. */

	ret = qxl_alloc_bo_reserved(qdev,
				    sizeof(struct qxl_palette) + sizeof(uint32_t) * 2,
				    palette_bo);

	ret = qxl_bo_kmap(*palette_bo, (void **)&pal);
	ret = qxl_bo_kmap(palette_bo, (void **)&pal);
	pal->num_ents = 2;
	pal->unique = unique++;
	if (visual == FB_VISUAL_TRUECOLOR || visual == FB_VISUAL_DIRECTCOLOR) {
@@ -126,7 +148,7 @@ static int qxl_palette_create_1bit(struct qxl_bo **palette_bo,
	}
	pal->ents[0] = bgcolor;
	pal->ents[1] = fgcolor;
	qxl_bo_kunmap(*palette_bo);
	qxl_bo_kunmap(palette_bo);
	return 0;
}

@@ -144,44 +166,63 @@ void qxl_draw_opaque_fb(const struct qxl_fb_image *qxl_fb_image,
	const char *src = fb_image->data;
	int depth = fb_image->depth;
	struct qxl_release *release;
	struct qxl_bo *image_bo;
	struct qxl_image *image;
	int ret;

	struct qxl_drm_image *dimage;
	struct qxl_bo *palette_bo = NULL;
	if (stride == 0)
		stride = depth * width / 8;

	ret = alloc_drawable(qdev, &release);
	if (ret)
		return;

	ret = qxl_image_alloc_objects(qdev, release,
				      &dimage,
				      height, stride);
	if (ret)
		goto out_free_drawable;

	if (depth == 1) {
		ret = alloc_palette_object(qdev, release, &palette_bo);
		if (ret)
			goto out_free_image;
	}

	/* do a reservation run over all the objects we just allocated */
	ret = qxl_release_reserve_list(release, true);
	if (ret)
		goto out_free_palette;

	rect.left = x;
	rect.right = x + width;
	rect.top = y;
	rect.bottom = y + height;

	ret = make_drawable(qdev, 0, QXL_DRAW_COPY, &rect, &release);
	if (ret)
		return;
	ret = make_drawable(qdev, 0, QXL_DRAW_COPY, &rect, release);
	if (ret) {
		qxl_release_backoff_reserve_list(release);
		goto out_free_palette;
	}

	ret = qxl_image_create(qdev, release, &image_bo,
	ret = qxl_image_init(qdev, release, dimage,
			     (const uint8_t *)src, 0, 0,
			     width, height, depth, stride);
	if (ret) {
		qxl_release_unreserve(qdev, release);
		qxl_release_backoff_reserve_list(release);
		qxl_release_free(qdev, release);
		return;
	}

	if (depth == 1) {
		struct qxl_bo *palette_bo;
		void *ptr;
		ret = qxl_palette_create_1bit(&palette_bo, qxl_fb_image);
		qxl_release_add_res(qdev, release, palette_bo);
		ret = qxl_palette_create_1bit(palette_bo, release, qxl_fb_image);

		ptr = qxl_bo_kmap_atomic_page(qdev, image_bo, 0);
		ptr = qxl_bo_kmap_atomic_page(qdev, dimage->bo, 0);
		image = ptr;
		image->u.bitmap.palette =
			qxl_bo_physical_address(qdev, palette_bo, 0);
		qxl_bo_kunmap_atomic_page(qdev, image_bo, ptr);
		qxl_bo_unreserve(palette_bo);
		qxl_bo_unref(&palette_bo);
		qxl_bo_kunmap_atomic_page(qdev, dimage->bo, ptr);
	}

	drawable = (struct qxl_drawable *)qxl_release_map(qdev, release);
@@ -199,16 +240,20 @@ void qxl_draw_opaque_fb(const struct qxl_fb_image *qxl_fb_image,
	drawable->u.copy.mask.bitmap = 0;

	drawable->u.copy.src_bitmap =
		qxl_bo_physical_address(qdev, image_bo, 0);
		qxl_bo_physical_address(qdev, dimage->bo, 0);
	qxl_release_unmap(qdev, release, &drawable->release_info);

	qxl_release_add_res(qdev, release, image_bo);
	qxl_bo_unreserve(image_bo);
	qxl_bo_unref(&image_bo);

	qxl_fence_releaseable(qdev, release);
	qxl_push_command_ring_release(qdev, release, QXL_CMD_DRAW, false);
	qxl_release_unreserve(qdev, release);
	qxl_release_fence_buffer_objects(release);

out_free_palette:
	if (palette_bo)
		qxl_bo_unref(&palette_bo);
out_free_image:
	qxl_image_free_objects(qdev, dimage);
out_free_drawable:
	if (ret)
		free_drawable(qdev, release);
}

/* push a draw command using the given clipping rectangles as
@@ -243,10 +288,14 @@ void qxl_draw_dirty_fb(struct qxl_device *qdev,
	int depth = qxl_fb->base.bits_per_pixel;
	uint8_t *surface_base;
	struct qxl_release *release;
	struct qxl_bo *image_bo;
	struct qxl_bo *clips_bo;
	struct qxl_drm_image *dimage;
	int ret;

	ret = alloc_drawable(qdev, &release);
	if (ret)
		return;

	left = clips->x1;
	right = clips->x2;
	top = clips->y1;
@@ -263,36 +312,52 @@ void qxl_draw_dirty_fb(struct qxl_device *qdev,

	width = right - left;
	height = bottom - top;

	ret = alloc_clips(qdev, release, num_clips, &clips_bo);
	if (ret)
		goto out_free_drawable;

	ret = qxl_image_alloc_objects(qdev, release,
				      &dimage,
				      height, stride);
	if (ret)
		goto out_free_clips;

	/* do a reservation run over all the objects we just allocated */
	ret = qxl_release_reserve_list(release, true);
	if (ret)
		goto out_free_image;

	drawable_rect.left = left;
	drawable_rect.right = right;
	drawable_rect.top = top;
	drawable_rect.bottom = bottom;

	ret = make_drawable(qdev, 0, QXL_DRAW_COPY, &drawable_rect,
			    &release);
			    release);
	if (ret)
		return;
		goto out_release_backoff;

	ret = qxl_bo_kmap(bo, (void **)&surface_base);
	if (ret)
		goto out_unref;
		goto out_release_backoff;


	ret = qxl_image_create(qdev, release, &image_bo, surface_base,
	ret = qxl_image_init(qdev, release, dimage, surface_base,
			     left, top, width, height, depth, stride);
	qxl_bo_kunmap(bo);
	if (ret)
		goto out_unref;
		goto out_release_backoff;

	rects = drawable_set_clipping(qdev, drawable, num_clips, clips_bo);
	if (!rects)
		goto out_release_backoff;

	rects = drawable_set_clipping(qdev, drawable, num_clips, &clips_bo, release);
	if (!rects) {
		qxl_bo_unref(&image_bo);
		goto out_unref;
	}
	drawable = (struct qxl_drawable *)qxl_release_map(qdev, release);

	drawable->clip.type = SPICE_CLIP_TYPE_RECTS;
	drawable->clip.data = qxl_bo_physical_address(qdev,
						      clips_bo, 0);
	qxl_release_add_res(qdev, release, clips_bo);

	drawable->u.copy.src_area.top = 0;
	drawable->u.copy.src_area.bottom = height;
@@ -306,11 +371,9 @@ void qxl_draw_dirty_fb(struct qxl_device *qdev,
	drawable->u.copy.mask.pos.y = 0;
	drawable->u.copy.mask.bitmap = 0;

	drawable->u.copy.src_bitmap = qxl_bo_physical_address(qdev, image_bo, 0);
	drawable->u.copy.src_bitmap = qxl_bo_physical_address(qdev, dimage->bo, 0);
	qxl_release_unmap(qdev, release, &drawable->release_info);
	qxl_release_add_res(qdev, release, image_bo);
	qxl_bo_unreserve(image_bo);
	qxl_bo_unref(&image_bo);

	clips_ptr = clips;
	for (i = 0; i < num_clips; i++, clips_ptr += inc) {
		rects[i].left   = clips_ptr->x1;
@@ -319,17 +382,22 @@ void qxl_draw_dirty_fb(struct qxl_device *qdev,
		rects[i].bottom = clips_ptr->y2;
	}
	qxl_bo_kunmap(clips_bo);
	qxl_bo_unreserve(clips_bo);
	qxl_bo_unref(&clips_bo);

	qxl_fence_releaseable(qdev, release);
	qxl_push_command_ring_release(qdev, release, QXL_CMD_DRAW, false);
	qxl_release_unreserve(qdev, release);
	return;
	qxl_release_fence_buffer_objects(release);

out_release_backoff:
	if (ret)
		qxl_release_backoff_reserve_list(release);
out_free_image:
	qxl_image_free_objects(qdev, dimage);
out_free_clips:
	qxl_bo_unref(&clips_bo);
out_free_drawable:
	/* only free drawable on error */
	if (ret)
		free_drawable(qdev, release);

out_unref:
	qxl_release_unreserve(qdev, release);
	qxl_release_free(qdev, release);
}

void qxl_draw_copyarea(struct qxl_device *qdev,
@@ -342,22 +410,36 @@ void qxl_draw_copyarea(struct qxl_device *qdev,
	struct qxl_release *release;
	int ret;

	ret = alloc_drawable(qdev, &release);
	if (ret)
		return;

	/* do a reservation run over all the objects we just allocated */
	ret = qxl_release_reserve_list(release, true);
	if (ret)
		goto out_free_release;

	rect.left = dx;
	rect.top = dy;
	rect.right = dx + width;
	rect.bottom = dy + height;
	ret = make_drawable(qdev, 0, QXL_COPY_BITS, &rect, &release);
	if (ret)
		return;
	ret = make_drawable(qdev, 0, QXL_COPY_BITS, &rect, release);
	if (ret) {
		qxl_release_backoff_reserve_list(release);
		goto out_free_release;
	}

	drawable = (struct qxl_drawable *)qxl_release_map(qdev, release);
	drawable->u.copy_bits.src_pos.x = sx;
	drawable->u.copy_bits.src_pos.y = sy;

	qxl_release_unmap(qdev, release, &drawable->release_info);
	qxl_fence_releaseable(qdev, release);

	qxl_push_command_ring_release(qdev, release, QXL_CMD_DRAW, false);
	qxl_release_unreserve(qdev, release);
	qxl_release_fence_buffer_objects(release);

out_free_release:
	if (ret)
		free_drawable(qdev, release);
}

void qxl_draw_fill(struct qxl_draw_fill *qxl_draw_fill_rec)
@@ -370,10 +452,21 @@ void qxl_draw_fill(struct qxl_draw_fill *qxl_draw_fill_rec)
	struct qxl_release *release;
	int ret;

	ret = make_drawable(qdev, 0, QXL_DRAW_FILL, &rect, &release);
	ret = alloc_drawable(qdev, &release);
	if (ret)
		return;

	/* do a reservation run over all the objects we just allocated */
	ret = qxl_release_reserve_list(release, true);
	if (ret)
		goto out_free_release;

	ret = make_drawable(qdev, 0, QXL_DRAW_FILL, &rect, release);
	if (ret) {
		qxl_release_backoff_reserve_list(release);
		goto out_free_release;
	}

	drawable = (struct qxl_drawable *)qxl_release_map(qdev, release);
	drawable->u.fill.brush.type = SPICE_BRUSH_TYPE_SOLID;
	drawable->u.fill.brush.u.color = color;
@@ -384,7 +477,11 @@ void qxl_draw_fill(struct qxl_draw_fill *qxl_draw_fill_rec)
	drawable->u.fill.mask.bitmap = 0;

	qxl_release_unmap(qdev, release, &drawable->release_info);
	qxl_fence_releaseable(qdev, release);

	qxl_push_command_ring_release(qdev, release, QXL_CMD_DRAW, false);
	qxl_release_unreserve(qdev, release);
	qxl_release_fence_buffer_objects(release);

out_free_release:
	if (ret)
		free_drawable(qdev, release);
}
+40 −35
Original line number Diff line number Diff line
@@ -42,6 +42,9 @@
#include <ttm/ttm_placement.h>
#include <ttm/ttm_module.h>

/* just for ttm_validate_buffer */
#include <ttm/ttm_execbuf_util.h>

#include <drm/qxl_drm.h>
#include "qxl_dev.h"

@@ -118,9 +121,9 @@ struct qxl_bo {
	uint32_t surface_id;
	struct qxl_fence fence; /* per bo fence  - list of releases */
	struct qxl_release *surf_create;
	atomic_t reserve_count;
};
#define gem_to_qxl_bo(gobj) container_of((gobj), struct qxl_bo, gem_base)
#define to_qxl_bo(tobj) container_of((tobj), struct qxl_bo, tbo)

struct qxl_gem {
	struct mutex		mutex;
@@ -128,12 +131,7 @@ struct qxl_gem {
};

struct qxl_bo_list {
	struct list_head lhead;
	struct qxl_bo *bo;
};

struct qxl_reloc_list {
	struct list_head bos;
	struct ttm_validate_buffer tv;
};

struct qxl_crtc {
@@ -195,10 +193,20 @@ enum {
struct qxl_release {
	int id;
	int type;
	int bo_count;
	uint32_t release_offset;
	uint32_t surface_release_id;
	struct qxl_bo *bos[QXL_MAX_RES];
	struct ww_acquire_ctx ticket;
	struct list_head bos;
};

struct qxl_drm_chunk {
	struct list_head head;
	struct qxl_bo *bo;
};

struct qxl_drm_image {
	struct qxl_bo *bo;
	struct list_head chunk_list;
};

struct qxl_fb_image {
@@ -434,12 +442,19 @@ int qxl_mmap(struct file *filp, struct vm_area_struct *vma);

/* qxl image */

int qxl_image_create(struct qxl_device *qdev,
int qxl_image_init(struct qxl_device *qdev,
		   struct qxl_release *release,
		     struct qxl_bo **image_bo,
		   struct qxl_drm_image *dimage,
		   const uint8_t *data,
		   int x, int y, int width, int height,
		   int depth, int stride);
int
qxl_image_alloc_objects(struct qxl_device *qdev,
			struct qxl_release *release,
			struct qxl_drm_image **image_ptr,
			int height, int stride);
void qxl_image_free_objects(struct qxl_device *qdev, struct qxl_drm_image *dimage);

void qxl_update_screen(struct qxl_device *qxl);

/* qxl io operations (qxl_cmd.c) */
@@ -460,20 +475,15 @@ int qxl_ring_push(struct qxl_ring *ring, const void *new_elt, bool interruptible
void qxl_io_flush_release(struct qxl_device *qdev);
void qxl_io_flush_surfaces(struct qxl_device *qdev);

int qxl_release_reserve(struct qxl_device *qdev,
			struct qxl_release *release, bool no_wait);
void qxl_release_unreserve(struct qxl_device *qdev,
			   struct qxl_release *release);
union qxl_release_info *qxl_release_map(struct qxl_device *qdev,
					struct qxl_release *release);
void qxl_release_unmap(struct qxl_device *qdev,
		       struct qxl_release *release,
		       union qxl_release_info *info);
/*
 * qxl_bo_add_resource.
 *
 */
void qxl_bo_add_resource(struct qxl_bo *main_bo, struct qxl_bo *resource);
int qxl_release_list_add(struct qxl_release *release, struct qxl_bo *bo);
int qxl_release_reserve_list(struct qxl_release *release, bool no_intr);
void qxl_release_backoff_reserve_list(struct qxl_release *release);
void qxl_release_fence_buffer_objects(struct qxl_release *release);

int qxl_alloc_surface_release_reserved(struct qxl_device *qdev,
				       enum qxl_surface_cmd_type surface_cmd_type,
@@ -482,15 +492,16 @@ int qxl_alloc_surface_release_reserved(struct qxl_device *qdev,
int qxl_alloc_release_reserved(struct qxl_device *qdev, unsigned long size,
			       int type, struct qxl_release **release,
			       struct qxl_bo **rbo);
int qxl_fence_releaseable(struct qxl_device *qdev,
			  struct qxl_release *release);

int
qxl_push_command_ring_release(struct qxl_device *qdev, struct qxl_release *release,
			      uint32_t type, bool interruptible);
int
qxl_push_cursor_ring_release(struct qxl_device *qdev, struct qxl_release *release,
			     uint32_t type, bool interruptible);
int qxl_alloc_bo_reserved(struct qxl_device *qdev, unsigned long size,
int qxl_alloc_bo_reserved(struct qxl_device *qdev,
			  struct qxl_release *release,
			  unsigned long size,
			  struct qxl_bo **_bo);
/* qxl drawing commands */

@@ -511,15 +522,9 @@ void qxl_draw_copyarea(struct qxl_device *qdev,
		       u32 sx, u32 sy,
		       u32 dx, u32 dy);

uint64_t
qxl_release_alloc(struct qxl_device *qdev, int type,
		  struct qxl_release **ret);

void qxl_release_free(struct qxl_device *qdev,
		      struct qxl_release *release);
void qxl_release_add_res(struct qxl_device *qdev,
			 struct qxl_release *release,
			 struct qxl_bo *bo);

/* used by qxl_debugfs_release */
struct qxl_release *qxl_release_from_id_locked(struct qxl_device *qdev,
						   uint64_t id);
@@ -562,7 +567,7 @@ void qxl_surface_evict(struct qxl_device *qdev, struct qxl_bo *surf, bool freein
int qxl_update_surface(struct qxl_device *qdev, struct qxl_bo *surf);

/* qxl_fence.c */
int qxl_fence_add_release(struct qxl_fence *qfence, uint32_t rel_id);
void qxl_fence_add_release_locked(struct qxl_fence *qfence, uint32_t rel_id);
int qxl_fence_remove_release(struct qxl_fence *qfence, uint32_t rel_id);
int qxl_fence_init(struct qxl_device *qdev, struct qxl_fence *qfence);
void qxl_fence_fini(struct qxl_fence *qfence);
+2 −8
Original line number Diff line number Diff line
@@ -49,17 +49,11 @@

   For some reason every so often qxl hw fails to release, things go wrong.
*/


int qxl_fence_add_release(struct qxl_fence *qfence, uint32_t rel_id)
/* must be called with the fence lock held */
void qxl_fence_add_release_locked(struct qxl_fence *qfence, uint32_t rel_id)
{
	struct qxl_bo *bo = container_of(qfence, struct qxl_bo, fence);

	spin_lock(&bo->tbo.bdev->fence_lock);
	radix_tree_insert(&qfence->tree, rel_id, qfence);
	qfence->num_active_releases++;
	spin_unlock(&bo->tbo.bdev->fence_lock);
	return 0;
}

int qxl_fence_remove_release(struct qxl_fence *qfence, uint32_t rel_id)
Loading