diff options
author | Chris Wilson <chris@chris-wilson.co.uk> | 2015-02-11 09:48:00 +0000 |
---|---|---|
committer | Chris Wilson <chris@chris-wilson.co.uk> | 2015-02-11 09:51:43 +0000 |
commit | 426a3afcc6ec96b8d4bb93262557bd036e00718c (patch) | |
tree | 8bf7ca1d64f98e3328442178ea40774d52c0c50b /test | |
parent | 0b7a6666f82b4fa07f9c9d9a9c1819efc363b31b (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.c | 139 |
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); |