diff options
author | Maarten Lankhorst <maarten.lankhorst@linux.intel.com> | 2017-01-26 11:35:50 +0100 |
---|---|---|
committer | Maarten Lankhorst <maarten.lankhorst@linux.intel.com> | 2017-01-26 11:58:24 +0100 |
commit | 24bfa3e89da0e3cbd7f752ac0bfe4c47d006ded7 (patch) | |
tree | 9422ffa1c85a59e337e4c65e8fcf7b79352aefc6 /tests/kms_busy.c | |
parent | 35afbb0e3bfda8fdb9bbb2b981af7c661ebdf440 (diff) |
kms_busy: Add extended tests that force a hang.
The intel driver adds a timeout to each atomic update of 10s for
the new fb.
Make sure this works as intended, and also test what happens when
hanging the old framebuffer, with and without modeset. The driver
waits indefinitely on the old framebuffer, but another blocking
update will wait at most 10 seconds for it.
Abuse this fact in a testcase. For added bonus also add some tests
that make sure this works on older gens that perform a display
reset during gpu reset.
Signed-off-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Diffstat (limited to 'tests/kms_busy.c')
-rw-r--r-- | tests/kms_busy.c | 204 |
1 files changed, 192 insertions, 12 deletions
diff --git a/tests/kms_busy.c b/tests/kms_busy.c index 1ae5d7fb..d2be7212 100644 --- a/tests/kms_busy.c +++ b/tests/kms_busy.c @@ -80,7 +80,7 @@ static void do_cleanup_display(igt_display_t *dpy) static void finish_fb_busy(igt_spin_t *spin, int msecs) { - struct timespec tv = { 0, msecs * 1000 * 1000 }; + struct timespec tv = { msecs / 1000, (msecs % 1000) * 1000000ULL }; nanosleep(&tv, NULL); igt_spin_batch_end(spin); } @@ -90,42 +90,64 @@ static void sighandler(int sig) } static void flip_to_fb(igt_display_t *dpy, int pipe, + igt_output_t *output, struct igt_fb *fb, unsigned ring, - const char *name) + const char *name, bool modeset) { struct pollfd pfd = { .fd = dpy->drm_fd, .events = POLLIN }; struct timespec tv = { 1, 0 }; struct drm_event_vblank ev; + igt_spin_t *t = igt_spin_batch_new(dpy->drm_fd, ring, fb->gem_handle); igt_fork(child, 1) { igt_assert(gem_bo_busy(dpy->drm_fd, fb->gem_handle)); - do_or_die(drmModePageFlip(dpy->drm_fd, - dpy->pipes[pipe].crtc_id, fb->fb_id, - DRM_MODE_PAGE_FLIP_EVENT, fb)); + if (!modeset) + do_or_die(drmModePageFlip(dpy->drm_fd, + dpy->pipes[pipe].crtc_id, fb->fb_id, + DRM_MODE_PAGE_FLIP_EVENT, fb)); + else { + igt_plane_set_fb(igt_output_get_plane(output, IGT_PLANE_PRIMARY), fb); + igt_output_set_pipe(output, PIPE_NONE); + igt_display_commit_atomic(dpy, + DRM_MODE_ATOMIC_NONBLOCK | + DRM_MODE_PAGE_FLIP_EVENT | + DRM_MODE_ATOMIC_ALLOW_MODESET, NULL); + } + kill(getppid(), SIGALRM); igt_assert(gem_bo_busy(dpy->drm_fd, fb->gem_handle)); - igt_assert_f(poll(&pfd, 1, TIMEOUT) == 0, + igt_assert_f(poll(&pfd, 1, modeset ? 5000 : TIMEOUT) == 0, "flip completed whilst %s was busy [%d]\n", name, gem_bo_busy(dpy->drm_fd, fb->gem_handle)); } igt_assert_f(nanosleep(&tv, NULL) == -1, "flip to %s blocked waiting for busy fb", name); - finish_fb_busy(t, 2*TIMEOUT); igt_waitchildren(); + finish_fb_busy(t, modeset ? 5000 : 2 * TIMEOUT); igt_assert(read(dpy->drm_fd, &ev, sizeof(ev)) == sizeof(ev)); igt_assert(poll(&pfd, 1, 0) == 0); + if (modeset) { + dpy->pipes[pipe].mode_blob = 0; + igt_output_set_pipe(output, pipe); + igt_display_commit2(dpy, COMMIT_ATOMIC); + } + igt_spin_batch_free(dpy->drm_fd, t); } -static void test_flip(igt_display_t *dpy, unsigned ring, int pipe) +static void test_flip(igt_display_t *dpy, unsigned ring, int pipe, bool modeset) { struct igt_fb fb[2]; int warmup[] = { 0, 1, 0, -1 }; + igt_output_t *output; + + if (modeset) + igt_require(dpy->is_atomic); signal(SIGALRM, sighandler); - igt_require(set_fb_on_crtc(dpy, pipe, &fb[0])); + igt_require((output = set_fb_on_crtc(dpy, pipe, &fb[0]))); igt_display_commit2(dpy, COMMIT_LEGACY); igt_create_pattern_fb(dpy->drm_fd, @@ -149,10 +171,10 @@ static void test_flip(igt_display_t *dpy, unsigned ring, int pipe) } /* Make the frontbuffer busy and try to flip to itself */ - flip_to_fb(dpy, pipe, &fb[0], ring, "fb[0]"); + flip_to_fb(dpy, pipe, output, &fb[0], ring, "fb[0]", modeset); /* Repeat for flip to second buffer */ - flip_to_fb(dpy, pipe, &fb[1], ring, "fb[1]"); + flip_to_fb(dpy, pipe, output, &fb[1], ring, "fb[1]", modeset); do_cleanup_display(dpy); igt_remove_fb(dpy->drm_fd, &fb[1]); @@ -161,6 +183,88 @@ static void test_flip(igt_display_t *dpy, unsigned ring, int pipe) signal(SIGALRM, SIG_DFL); } +static void test_atomic_commit_hang(igt_display_t *dpy, igt_plane_t *primary, + struct igt_fb *busy_fb, unsigned ring, + bool completes_early) +{ + igt_spin_t *t = igt_spin_batch_new(dpy->drm_fd, ring, busy_fb->gem_handle); + struct pollfd pfd = { .fd = dpy->drm_fd, .events = POLLIN }; + unsigned flags = 0; + struct drm_event_vblank ev; + + flags |= DRM_MODE_ATOMIC_ALLOW_MODESET; + flags |= DRM_MODE_ATOMIC_NONBLOCK; + flags |= DRM_MODE_PAGE_FLIP_EVENT; + + igt_display_commit_atomic(dpy, flags, NULL); + + igt_fork(child, 1) { + /* + * bit of a hack, just set atomic commit to NULL fb to make sure + * that we don't wait for the new update to complete. + */ + igt_plane_set_fb(primary, NULL); + igt_display_commit_atomic(dpy, 0, NULL); + + if (completes_early) + igt_assert(gem_bo_busy(dpy->drm_fd, busy_fb->gem_handle)); + else + igt_fail_on(gem_bo_busy(dpy->drm_fd, busy_fb->gem_handle)); + + igt_assert_f(poll(&pfd, 1, 1) > 0, + "nonblocking update completed whilst fb[%d] was still busy [%d]\n", + busy_fb->fb_id, gem_bo_busy(dpy->drm_fd, busy_fb->gem_handle)); + } + + igt_waitchildren(); + + igt_assert(read(dpy->drm_fd, &ev, sizeof(ev)) == sizeof(ev)); + + igt_spin_batch_end(t); +} + +static void test_hang(igt_display_t *dpy, unsigned ring, + enum pipe pipe, bool modeset, bool hang_newfb) +{ + struct igt_fb fb[2]; + igt_output_t *output; + igt_plane_t *primary; + + igt_require((output = set_fb_on_crtc(dpy, pipe, &fb[0]))); + igt_display_commit2(dpy, COMMIT_ATOMIC); + primary = igt_output_get_plane(output, IGT_PLANE_PRIMARY); + + igt_create_pattern_fb(dpy->drm_fd, + fb[0].width, fb[0].height, + DRM_FORMAT_XRGB8888, + LOCAL_I915_FORMAT_MOD_X_TILED, + &fb[1]); + + if (modeset) { + /* Test modeset disable with hang */ + igt_output_set_pipe(output, PIPE_NONE); + igt_plane_set_fb(primary, &fb[1]); + test_atomic_commit_hang(dpy, primary, &fb[hang_newfb], ring, hang_newfb); + + /* Test modeset enable with hang */ + igt_plane_set_fb(primary, &fb[0]); + igt_output_set_pipe(output, pipe); + test_atomic_commit_hang(dpy, primary, &fb[!hang_newfb], ring, hang_newfb); + } else { + /* + * Test what happens with a single hanging pageflip. + * This always completes early, because we have some + * timeouts taking care of it. + */ + igt_plane_set_fb(primary, &fb[1]); + test_atomic_commit_hang(dpy, primary, &fb[hang_newfb], ring, true); + } + + do_cleanup_display(dpy); + igt_remove_fb(dpy->drm_fd, &fb[1]); + igt_remove_fb(dpy->drm_fd, &fb[0]); +} + igt_main { igt_display_t display = { .drm_fd = -1, .n_pipes = I915_MAX_PIPES }; @@ -194,7 +298,83 @@ igt_main igt_require(gem_has_ring(display.drm_fd, e->exec_id | e->flags)); - test_flip(&display, e->exec_id | e->flags, n); + test_flip(&display, e->exec_id | e->flags, n, false); + } + igt_subtest_f("%smodeset-%s-%s", + e->exec_id == 0 ? "basic-" : "", + e->name, kmstest_pipe_name(n)) { + igt_require(gem_has_ring(display.drm_fd, + e->exec_id | e->flags)); + + test_flip(&display, e->exec_id | e->flags, n, true); + } + + igt_subtest_group { + igt_hang_t hang; + + igt_fixture { + igt_require(display.is_atomic); + + hang = igt_allow_hang(display.drm_fd, 0, 0); + } + + igt_subtest_f("extended-pageflip-hang-oldfb-%s-%s", + e->name, kmstest_pipe_name(n)) { + igt_require(gem_has_ring(display.drm_fd, + e->exec_id | e->flags)); + + test_hang(&display, e->exec_id | e->flags, n, false, false); + } + + igt_subtest_f("extended-pageflip-hang-newfb-%s-%s", + e->name, kmstest_pipe_name(n)) { + igt_require(gem_has_ring(display.drm_fd, + e->exec_id | e->flags)); + + test_hang(&display, e->exec_id | e->flags, n, false, true); + } + + igt_subtest_f("extended-modeset-hang-oldfb-%s-%s", + e->name, kmstest_pipe_name(n)) { + igt_require(gem_has_ring(display.drm_fd, + e->exec_id | e->flags)); + + test_hang(&display, e->exec_id | e->flags, n, true, false); + } + + igt_subtest_f("extended-modeset-hang-newfb-%s-%s", + e->name, kmstest_pipe_name(n)) { + igt_require(gem_has_ring(display.drm_fd, + e->exec_id | e->flags)); + + test_hang(&display, e->exec_id | e->flags, n, true, true); + } + + igt_subtest_f("extended-modeset-hang-oldfb-with-reset-%s-%s", + e->name, kmstest_pipe_name(n)) { + igt_require(gem_has_ring(display.drm_fd, + e->exec_id | e->flags)); + igt_set_module_param_int("force_reset_modeset_test", 1); + + test_hang(&display, e->exec_id | e->flags, n, true, false); + + igt_set_module_param_int("force_reset_modeset_test", 0); + } + + igt_subtest_f("extended-modeset-hang-newfb-with-reset-%s-%s", + e->name, kmstest_pipe_name(n)) { + igt_require(gem_has_ring(display.drm_fd, + e->exec_id | e->flags)); + igt_set_module_param_int("force_reset_modeset_test", 1); + + test_hang(&display, e->exec_id | e->flags, n, true, true); + + igt_set_module_param_int("force_reset_modeset_test", 0); + } + + igt_fixture { + igt_disallow_hang(display.drm_fd, hang); + } } } } |