summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorChris Wilson <chris@chris-wilson.co.uk>2015-02-11 09:48:00 +0000
committerChris Wilson <chris@chris-wilson.co.uk>2015-02-11 09:51:43 +0000
commit426a3afcc6ec96b8d4bb93262557bd036e00718c (patch)
tree8bf7ca1d64f98e3328442178ea40774d52c0c50b /test
parent0b7a6666f82b4fa07f9c9d9a9c1819efc363b31b (diff)
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@chris-wilson.co.uk>
Diffstat (limited to 'test')
-rw-r--r--test/present-test.c139
1 files changed, 133 insertions, 6 deletions
diff --git a/test/present-test.c b/test/present-test.c
index dc399c39..e90ef477 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);