xf86-video-intel: 2 commits - configure.ac src/sna/sna_accel.c src/sna/sna_present.c test/present-test.c

Chris Wilson ickle at kemper.freedesktop.org
Wed Feb 11 02:03:05 PST 2015


 configure.ac          |    2 
 src/sna/sna_accel.c   |   38 ++++++++++++-
 src/sna/sna_present.c |   70 ++++++++++++++++++++++---
 test/present-test.c   |  139 +++++++++++++++++++++++++++++++++++++++++++++++---
 4 files changed, 231 insertions(+), 18 deletions(-)

New commits:
commit 426a3afcc6ec96b8d4bb93262557bd036e00718c
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Wed Feb 11 09:48:00 2015 +0000

    sna/present: Prevent DoS from exhausting the kernel vblank queue
    
    The kernel caps the number of vblanks per client to 128. There are
    already reports from the wild that Present clients are running foul of
    that limit, so supplement it with a timer fallback.
    
    Testcase: present-test with future flips
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/configure.ac b/configure.ac
index afe162d..7476e2b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -270,7 +270,7 @@ if test "x$shm" = "xyes"; then
 	AC_DEFINE([HAVE_MIT_SHM], 1, [Define to 1 if MIT-SHM is available])
 fi
 
-PKG_CHECK_MODULES(X11_DRI3, [xcb-dri3 xcb-sync xcb-present x11-xcb xshmfence x11 xrender xext libdrm], [x11_dri3="yes"], [x11_dri3="no"])
+PKG_CHECK_MODULES(X11_DRI3, [xcb-dri3 xcb-sync xcb-xfixes xcb-present x11-xcb xshmfence x11 xrender xext libdrm], [x11_dri3="yes"], [x11_dri3="no"])
 AM_CONDITIONAL(X11_DRI3, test "x$x11_dri3" = "xyes" -a "x$shm" = "xyes")
 AM_CONDITIONAL(X11_SHM, test "x$shm" = "xyes")
 
diff --git a/src/sna/sna_present.c b/src/sna/sna_present.c
index c2a9c5d..25b4e8b 100644
--- a/src/sna/sna_present.c
+++ b/src/sna/sna_present.c
@@ -38,9 +38,10 @@
 static present_screen_info_rec present_info;
 
 struct sna_present_event {
-	uint64_t event_id;
 	xf86CrtcPtr crtc;
 	struct sna *sna;
+	uint64_t event_id;
+	uint64_t target_msc;
 };
 
 static void sna_present_unflip(ScreenPtr screen, uint64_t event_id);
@@ -77,6 +78,56 @@ static inline int sna_wait_vblank(struct sna *sna, union drm_wait_vblank *vbl, i
 	return drmIoctl(sna->kgem.fd, DRM_IOCTL_WAIT_VBLANK, vbl);
 }
 
+static uint32_t msc_to_delay(xf86CrtcPtr crtc, uint64_t msc)
+{
+	DisplayModePtr mode = &crtc->desiredMode;
+	return msc * mode->VTotal * mode->HTotal / mode->Clock;
+}
+
+static CARD32 sna_fake_vblank_handler(OsTimerPtr timer, CARD32 now, void *data)
+{
+	struct sna_present_event *info = data;
+	union drm_wait_vblank vbl;
+	uint64_t msc, ust;
+
+	DBG(("%s(now=%d)\n", __FUNCTION__, now));
+
+	VG_CLEAR(vbl);
+	vbl.request.type = DRM_VBLANK_RELATIVE;
+	vbl.request.sequence = 0;
+	if (sna_wait_vblank(info->sna, &vbl, sna_crtc_to_pipe(info->crtc)) == 0) {
+		ust = ust64(vbl.reply.tval_sec, vbl.reply.tval_usec);
+		msc = sna_crtc_record_vblank(info->crtc, &vbl);
+		if (msc < info->target_msc)
+			return msc_to_delay(info->crtc, msc - info->target_msc);
+	} else {
+		const struct ust_msc *swap = sna_crtc_last_swap(info->crtc);
+		ust = ust64(swap->tv_sec, swap->tv_usec);
+		msc = swap->msc;
+	}
+
+	present_event_notify(info->event_id, ust, msc);
+	free(info);
+	free(timer);
+	return 0;
+}
+
+static bool sna_fake_vblank(struct sna_present_event *event)
+{
+	uint64_t msc = sna_crtc_last_swap(event->crtc)->msc;
+	uint32_t delay;
+
+	if (msc < event->target_msc)
+		delay = msc_to_delay(event->crtc, event->target_msc - msc);
+	else
+		delay = 0;
+
+	DBG(("%s(target_msc=%lld, msc=%lld, delay=%ums)\n",
+	     __FUNCTION__, event->target_msc, msc, delay));
+
+	return TimerSet(NULL, 0, delay, sna_fake_vblank_handler, event);
+}
+
 static RRCrtcPtr
 sna_present_get_crtc(WindowPtr window)
 {
@@ -131,10 +182,11 @@ sna_present_vblank_handler(struct drm_event_vblank *event)
 {
 	struct sna_present_event *info = to_present_event(event->user_data);
 
-	DBG(("%s: pipe=%d tv=%d.%06d msc=%d, event %lld complete\n", __FUNCTION__,
+	DBG(("%s: pipe=%d tv=%d.%06d msc=%d, event %lld complete: target_msc=%lld\n", __FUNCTION__,
 	     sna_crtc_to_pipe(info->crtc),
 	     event->tv_sec, event->tv_usec, event->sequence,
-	     (long long)info->event_id));
+	     (long long)info->event_id,
+	     (long long)info->target_msc));
 	present_event_notify(info->event_id,
 			     ust64(event->tv_sec, event->tv_usec),
 			     sna_crtc_record_event(info->crtc, event));
@@ -156,9 +208,10 @@ sna_present_queue_vblank(RRCrtcPtr crtc, uint64_t event_id, uint64_t msc)
 	if (event == NULL)
 		return BadAlloc;
 
-	event->event_id = event_id;
 	event->crtc = crtc->devPrivate;
 	event->sna = sna;
+	event->target_msc = msc;
+	event->event_id = event_id;
 
 	VG_CLEAR(vbl);
 	vbl.request.type = DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT;
@@ -166,8 +219,10 @@ sna_present_queue_vblank(RRCrtcPtr crtc, uint64_t event_id, uint64_t msc)
 	vbl.request.signal = (uintptr_t)MARK_PRESENT(event);
 	if (sna_wait_vblank(sna, &vbl, sna_crtc_to_pipe(event->crtc))) {
 		DBG(("%s: vblank enqueue failed\n", __FUNCTION__));
-		free(event);
-		return BadMatch;
+		if (!sna_fake_vblank(event)) {
+			free(event);
+			return BadAlloc;
+		}
 	}
 
 	return Success;
@@ -360,9 +415,10 @@ page_flip(struct sna *sna,
 	if (event == NULL)
 		return FALSE;
 
-	event->event_id = event_id;
 	event->crtc = crtc ? crtc->devPrivate : NULL;
 	event->sna = sna;
+	event->event_id = event_id;
+	event->target_msc = 0;
 
 	if (!sna_page_flip(sna, bo, present_flip_handler, event)) {
 		DBG(("%s: pageflip failed\n", __FUNCTION__));
diff --git a/test/present-test.c b/test/present-test.c
index dc399c3..e90ef47 100644
--- a/test/present-test.c
+++ b/test/present-test.c
@@ -44,6 +44,7 @@
 #endif
 #include <xcb/xcb.h>
 #include <xcb/present.h>
+#include <xcb/xfixes.h>
 #include <xf86drm.h>
 #include <i915_drm.h>
 
@@ -170,6 +171,7 @@ static void teardown_msc(Display *dpy, void *q)
 }
 static int test_whole(Display *dpy)
 {
+	xcb_connection_t *c = XGetXCBConnection(dpy);
 	Pixmap pixmap;
 	struct dri3_fence fence;
 	Window root;
@@ -189,8 +191,7 @@ static int test_whole(Display *dpy)
 	xshmfence_reset(fence.addr);
 
 	pixmap = XCreatePixmap(dpy, root, width, height, depth);
-	xcb_present_pixmap(XGetXCBConnection(dpy),
-			   root, pixmap,
+	xcb_present_pixmap(c, root, pixmap,
 			   0, /* sbc */
 			   0, /* valid */
 			   0, /* update */
@@ -207,8 +208,7 @@ static int test_whole(Display *dpy)
 	XFreePixmap(dpy, pixmap);
 
 	pixmap = XCreatePixmap(dpy, root, width, height, depth);
-	xcb_present_pixmap(XGetXCBConnection(dpy),
-			   root, pixmap,
+	xcb_present_pixmap(c, root, pixmap,
 			   0, /* sbc */
 			   0, /* valid */
 			   0, /* update */
@@ -234,6 +234,118 @@ static int test_whole(Display *dpy)
 	return ret;
 }
 
+static int test_future(Display *dpy, void *Q)
+{
+#define N_VBLANKS 256 /* kernel event queue length: 128 vblanks */
+	xcb_connection_t *c = XGetXCBConnection(dpy);
+	Pixmap pixmap;
+	struct dri3_fence fence;
+	Window root;
+	xcb_xfixes_region_t region;
+	unsigned int width, height;
+	unsigned border, depth;
+	int x, y, ret = 1, n;
+	uint64_t target, final;
+
+	XGetGeometry(dpy, DefaultRootWindow(dpy),
+		     &root, &x, &y, &width, &height, &border, &depth);
+
+	if (dri3_create_fence(dpy, root, &fence))
+		return 0;
+
+	printf("Testing whole screen flips into the future: %dx%d\n", width, height);
+	_x_error_occurred = 0;
+
+	region = xcb_generate_id(c);
+	xcb_xfixes_create_region(c, region, 0, NULL);
+
+	target = check_msc(dpy, root, Q, 0);
+	pixmap = XCreatePixmap(dpy, root, width, height, depth);
+	xshmfence_reset(fence.addr);
+	for (n = N_VBLANKS; n--; )
+		xcb_present_pixmap(c, root, pixmap,
+				   n, /* sbc */
+				   0, /* valid */
+				   region, /* update */
+				   0, /* x_off */
+				   0, /* y_off */
+				   None,
+				   None, /* wait fence */
+				   None,
+				   XCB_PRESENT_OPTION_NONE,
+				   target + N_VBLANKS, /* target msc */
+				   1, /* divisor */
+				   0, /* remainder */
+				   0, NULL);
+	xcb_present_pixmap(c, root, pixmap,
+			   N_VBLANKS, /* sbc */
+			   region, /* valid */
+			   region, /* update */
+			   0, /* x_off */
+			   0, /* y_off */
+			   None,
+			   None, /* wait fence */
+			   fence.xid,
+			   XCB_PRESENT_OPTION_NONE,
+			   target, /* target msc */
+			   0, /* divisor */
+			   0, /* remainder */
+			   0, NULL);
+	xcb_flush(c);
+
+	XSync(dpy, True);
+	ret = !!xshmfence_await(fence.addr);
+
+	final = check_msc(dpy, root, Q, 0);
+	if (final < target) {
+		printf("First flip too early, MSC was %llu, expected %llu\n",
+		       (long long)final, (long long)target);
+		ret++;
+	} else if (final > target + 1) {
+		printf("First flip too late, MSC was %llu, expected %llu\n",
+		       (long long)final, (long long)target);
+		ret++;
+	}
+
+	xshmfence_reset(fence.addr);
+	xcb_present_pixmap(c, root, pixmap,
+			   N_VBLANKS, /* sbc */
+			   region, /* valid */
+			   region, /* update */
+			   0, /* x_off */
+			   0, /* y_off */
+			   None,
+			   None, /* wait fence */
+			   fence.xid,
+			   XCB_PRESENT_OPTION_NONE,
+			   target + N_VBLANKS, /* target msc */
+			   0, /* divisor */
+			   0, /* remainder */
+			   0, NULL);
+	xcb_flush(c);
+	ret = !!xshmfence_await(fence.addr);
+
+	final = check_msc(dpy, root, Q, 0);
+	if (final < target + N_VBLANKS) {
+		printf("Last flip too early, MSC was %llu, expected %llu\n",
+		       (long long)final, (long long)(target + N_VBLANKS));
+		ret++;
+	} else if (final > target + N_VBLANKS + 1) {
+		printf("Last flip too late, MSC was %llu, expected %llu\n",
+		       (long long)final, (long long)(target + N_VBLANKS));
+		ret++;
+	}
+
+	XFreePixmap(dpy, pixmap);
+	xcb_xfixes_destroy_region(c, region);
+	dri3_fence_free(dpy, &fence);
+
+	XSync(dpy, True);
+	ret += !!_x_error_occurred;
+
+	return ret;
+}
+
 static inline XRRScreenResources *_XRRGetScreenResourcesCurrent(Display *dpy, Window window)
 {
 	XRRScreenResources *res;
@@ -672,8 +784,18 @@ fail:
 static int has_present(Display *dpy)
 {
 	xcb_connection_t *c = XGetXCBConnection(dpy);
-	xcb_present_query_version_reply_t *reply;
 	xcb_generic_error_t *error = NULL;
+	void *reply;
+
+	reply = xcb_xfixes_query_version_reply(c,
+					       xcb_xfixes_query_version(c,
+									XCB_XFIXES_MAJOR_VERSION,
+									XCB_XFIXES_MINOR_VERSION),
+					       &error);
+	free(reply);
+	free(error);
+	if (reply == NULL)
+		return 0;
 
 	reply = xcb_present_query_version_reply(c,
 						xcb_present_query_version(c,
@@ -683,8 +805,10 @@ static int has_present(Display *dpy)
 
 	free(reply);
 	free(error);
+	if (reply == NULL)
+		return 0;
 
-	return reply != NULL;
+	return 1;
 }
 
 int main(void)
@@ -713,6 +837,9 @@ int main(void)
 	error += test_whole(dpy);
 	last_msc = check_msc(dpy, root, queue, last_msc);
 
+	error += test_future(dpy, queue);
+	last_msc = check_msc(dpy, root, queue, last_msc);
+
 	error += test_crtc(dpy, queue, last_msc);
 	last_msc = check_msc(dpy, root, queue, last_msc);
 
commit 0b7a6666f82b4fa07f9c9d9a9c1819efc363b31b
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Mon Jan 5 14:00:44 2015 +0000

    sna: Partially unroll conversion of rectangles to boxes for fills
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/sna/sna_accel.c b/src/sna/sna_accel.c
index 6c516ad..b98c94f 100644
--- a/src/sna/sna_accel.c
+++ b/src/sna/sna_accel.c
@@ -11811,14 +11811,29 @@ sna_poly_fill_rect_blt(DrawablePtr drawable,
 				if (nbox > ARRAY_SIZE(boxes))
 					nbox = ARRAY_SIZE(boxes);
 				n -= nbox;
-				do {
+				while (nbox >= 2) {
+					b[0].x1 = rect[0].x + dx;
+					b[0].y1 = rect[0].y + dy;
+					b[0].x2 = b[0].x1 + rect[0].width;
+					b[0].y2 = b[0].y1 + rect[0].height;
+
+					b[1].x1 = rect[1].x + dx;
+					b[1].y1 = rect[1].y + dy;
+					b[1].x2 = b[1].x1 + rect[1].width;
+					b[1].y2 = b[1].y1 + rect[1].height;
+
+					b += 2;
+					rect += 2;
+					nbox -= 2;
+				}
+				if (nbox) {
 					b->x1 = rect->x + dx;
 					b->y1 = rect->y + dy;
 					b->x2 = b->x1 + rect->width;
 					b->y2 = b->y1 + rect->height;
 					b++;
 					rect++;
-				} while (--nbox);
+				}
 				fill.boxes(sna, &fill, boxes, b-boxes);
 				b = boxes;
 			} while (n);
@@ -11828,14 +11843,29 @@ sna_poly_fill_rect_blt(DrawablePtr drawable,
 				if (nbox > ARRAY_SIZE(boxes))
 					nbox = ARRAY_SIZE(boxes);
 				n -= nbox;
-				do {
+				while (nbox >= 2) {
+					b[0].x1 = rect[0].x;
+					b[0].y1 = rect[0].y;
+					b[0].x2 = b[0].x1 + rect[0].width;
+					b[0].y2 = b[0].y1 + rect[0].height;
+
+					b[1].x1 = rect[1].x + dx;
+					b[1].y1 = rect[1].y + dy;
+					b[1].x2 = b[1].x1 + rect[1].width;
+					b[1].y2 = b[1].y1 + rect[1].height;
+
+					b += 2;
+					rect += 2;
+					nbox -= 2;
+				}
+				if (nbox) {
 					b->x1 = rect->x;
 					b->y1 = rect->y;
 					b->x2 = b->x1 + rect->width;
 					b->y2 = b->y1 + rect->height;
 					b++;
 					rect++;
-				} while (--nbox);
+				}
 				fill.boxes(sna, &fill, boxes, b-boxes);
 				b = boxes;
 			} while (n);


More information about the xorg-commit mailing list