1 /*
2 * Copyright © 2013 Intel Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 *
23 */
24
25 #include <sched.h>
26 #include <sys/poll.h>
27
28 #include "igt.h"
29 #include "igt_rand.h"
30 #include "igt_stats.h"
31
32 #if defined(__x86_64__) || defined(__i386__)
33 #define cpu_relax() __builtin_ia32_pause()
34 #else
35 #define cpu_relax() asm volatile("": : :"memory")
36 #endif
37
38 #ifndef DRM_CAP_CURSOR_WIDTH
39 #define DRM_CAP_CURSOR_WIDTH 0x8
40 #endif
41
42 #ifndef DRM_CAP_CURSOR_HEIGHT
43 #define DRM_CAP_CURSOR_HEIGHT 0x9
44 #endif
45
46 #define PAGE_SIZE 4096
47
48 IGT_TEST_DESCRIPTION("Stress legacy cursor ioctl");
49
50 igt_pipe_crc_t *pipe_crc;
51
stress(igt_display_t * display,enum pipe pipe,int num_children,unsigned mode,int timeout)52 static void stress(igt_display_t *display,
53 enum pipe pipe, int num_children, unsigned mode,
54 int timeout)
55 {
56 struct drm_mode_cursor arg;
57 uint64_t *results;
58 bool torture;
59 int n;
60 unsigned crtc_id[IGT_MAX_PIPES], num_crtcs;
61
62 torture = false;
63 if (num_children < 0) {
64 torture = true;
65 num_children = -num_children;
66 }
67
68 results = mmap(NULL, PAGE_SIZE, PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
69 igt_assert(results != MAP_FAILED);
70
71 memset(&arg, 0, sizeof(arg));
72 arg.flags = DRM_MODE_CURSOR_BO;
73 arg.crtc_id = 0;
74 arg.width = 64;
75 arg.height = 64;
76 arg.handle = kmstest_dumb_create(display->drm_fd, 64, 64, 32, NULL, NULL);
77
78 if (pipe < 0) {
79 num_crtcs = display->n_pipes;
80 for_each_pipe(display, n) {
81 arg.crtc_id = crtc_id[n] = display->pipes[n].crtc_id;
82 do_ioctl(display->drm_fd, DRM_IOCTL_MODE_CURSOR, &arg);
83 }
84 } else {
85 num_crtcs = 1;
86 arg.crtc_id = crtc_id[0] = display->pipes[pipe].crtc_id;
87 do_ioctl(display->drm_fd, DRM_IOCTL_MODE_CURSOR, &arg);
88 }
89
90 arg.flags = mode;
91 igt_fork(child, num_children) {
92 struct sched_param rt = {.sched_priority = 99 };
93 cpu_set_t allowed;
94 unsigned long count = 0;
95
96 sched_setscheduler(getpid(), SCHED_RR, &rt);
97
98 CPU_ZERO(&allowed);
99 CPU_SET(child, &allowed);
100 sched_setaffinity(getpid(), sizeof(cpu_set_t), &allowed);
101
102 hars_petruska_f54_1_random_perturb(child);
103 igt_until_timeout(timeout) {
104 arg.crtc_id = crtc_id[hars_petruska_f54_1_random_unsafe() % num_crtcs];
105 do_ioctl(display->drm_fd, DRM_IOCTL_MODE_CURSOR, &arg);
106 count++;
107 }
108
109 igt_debug("[%d] count=%lu\n", child, count);
110 results[child] = count;
111 }
112 if (torture) {
113 igt_fork(child, num_children) {
114 struct sched_param rt = {.sched_priority = 1 };
115 cpu_set_t allowed;
116 unsigned long long count = 0;
117
118 sched_setscheduler(getpid(), SCHED_RR, &rt);
119
120 CPU_ZERO(&allowed);
121 CPU_SET(child, &allowed);
122 sched_setaffinity(getpid(), sizeof(cpu_set_t), &allowed);
123 igt_until_timeout(timeout) {
124 count++;
125 cpu_relax();
126 }
127 igt_debug("[hog:%d] count=%llu\n", child, count);
128 }
129 }
130 igt_waitchildren();
131
132 if (num_children > 1) {
133 igt_stats_t stats;
134
135 igt_stats_init_with_size(&stats, num_children);
136 results[num_children] = 0;
137 for (int child = 0; child < num_children; child++) {
138 igt_stats_push(&stats, results[child]);
139 results[num_children] += results[child];
140 }
141 igt_info("Total updates %llu (median of %d processes is %.2f)\n",
142 (long long)results[num_children],
143 num_children,
144 igt_stats_get_median(&stats));
145 igt_stats_fini(&stats);
146 } else {
147 igt_info("Total updates %llu\n", (long long)results[0]);
148 }
149
150 gem_close(display->drm_fd, arg.handle);
151 munmap(results, PAGE_SIZE);
152 }
153
set_fb_on_crtc(igt_display_t * display,enum pipe pipe,struct igt_fb * fb_info)154 static igt_output_t *set_fb_on_crtc(igt_display_t *display, enum pipe pipe, struct igt_fb *fb_info)
155 {
156 igt_output_t *output;
157
158 for_each_valid_output_on_pipe(display, pipe, output) {
159 drmModeModeInfoPtr mode;
160 igt_plane_t *primary;
161
162 if (output->pending_pipe != PIPE_NONE)
163 continue;
164
165 igt_output_set_pipe(output, pipe);
166 mode = igt_output_get_mode(output);
167
168 igt_create_pattern_fb(display->drm_fd,
169 mode->hdisplay, mode->vdisplay,
170 DRM_FORMAT_XRGB8888, I915_TILING_NONE, fb_info);
171
172 primary = igt_output_get_plane_type(output, DRM_PLANE_TYPE_PRIMARY);
173 igt_plane_set_fb(primary, fb_info);
174
175 return output;
176 }
177
178 return NULL;
179 }
180
set_cursor_on_pipe(igt_display_t * display,enum pipe pipe,struct igt_fb * fb)181 static void set_cursor_on_pipe(igt_display_t *display, enum pipe pipe, struct igt_fb *fb)
182 {
183 igt_plane_t *plane, *cursor = NULL;
184
185 for_each_plane_on_pipe(display, pipe, plane) {
186 if (plane->type != DRM_PLANE_TYPE_CURSOR)
187 continue;
188
189 cursor = plane;
190 break;
191 }
192
193 igt_require(cursor);
194 igt_plane_set_fb(cursor, fb);
195 }
196
populate_cursor_args(igt_display_t * display,enum pipe pipe,struct drm_mode_cursor * arg,struct igt_fb * fb)197 static void populate_cursor_args(igt_display_t *display, enum pipe pipe,
198 struct drm_mode_cursor *arg, struct igt_fb *fb)
199 {
200 arg->crtc_id = display->pipes[pipe].crtc_id;
201 arg->flags = DRM_MODE_CURSOR_MOVE;
202 arg->x = 128;
203 arg->y = 128;
204 arg->width = fb->width;
205 arg->height = fb->height;
206 arg->handle = fb->gem_handle;
207 arg[1] = *arg;
208 }
209
find_connected_pipe(igt_display_t * display,bool second)210 static enum pipe find_connected_pipe(igt_display_t *display, bool second)
211 {
212 enum pipe pipe, first = PIPE_NONE;
213 igt_output_t *output;
214 igt_output_t *first_output = NULL;
215 bool found = false;
216
217 if (!second) {
218 igt_pipe_crc_free(pipe_crc);
219 pipe_crc = NULL;
220
221 /* Clear display, events will be eaten by commit.. */
222 igt_display_reset(display);
223 }
224
225 for_each_pipe_with_valid_output(display, pipe, output) {
226 if (first == pipe || output == first_output)
227 continue;
228
229 if (second) {
230 first = pipe;
231 first_output = output;
232 second = false;
233 continue;
234 }
235
236 found = true;
237 break;
238 }
239
240 if (first_output)
241 igt_require_f(found, "No second valid output found\n");
242 else
243 igt_require_f(found, "No valid outputs found\n");
244
245 return pipe;
246 }
247
flip_nonblocking(igt_display_t * display,enum pipe pipe_id,bool atomic,struct igt_fb * fb,void * data)248 static void flip_nonblocking(igt_display_t *display, enum pipe pipe_id, bool atomic, struct igt_fb *fb, void *data)
249 {
250 igt_pipe_t *pipe = &display->pipes[pipe_id];
251 igt_plane_t *primary = igt_pipe_get_plane_type(pipe, DRM_PLANE_TYPE_PRIMARY);
252 int ret;
253
254 igt_set_timeout(1, "Scheduling page flip\n");
255 if (!atomic) {
256 /* Schedule a nonblocking flip for the next vblank */
257 do {
258 ret = drmModePageFlip(display->drm_fd, pipe->crtc_id, fb->fb_id,
259 DRM_MODE_PAGE_FLIP_EVENT, data);
260 } while (ret == -EBUSY);
261 } else {
262 igt_plane_set_fb(primary, fb);
263 do {
264 ret = igt_display_try_commit_atomic(display, DRM_MODE_ATOMIC_NONBLOCK | DRM_MODE_PAGE_FLIP_EVENT, data);
265 } while (ret == -EBUSY);
266 }
267 igt_assert(!ret);
268 igt_reset_timeout();
269 }
270
271 enum flip_test {
272 flip_test_legacy = 0,
273 flip_test_varying_size,
274 flip_test_toggle_visibility,
275 flip_test_atomic,
276 flip_test_atomic_transitions,
277 flip_test_atomic_transitions_varying_size,
278 flip_test_last = flip_test_atomic_transitions_varying_size
279 };
280
cursor_slowpath(enum flip_test mode)281 static bool cursor_slowpath(enum flip_test mode)
282 {
283 /* cursor moving doesn't take slowpath, everything else does. */
284 if (mode == flip_test_legacy || mode == flip_test_atomic)
285 return false;
286
287 return true;
288 }
289
290 /*
291 * On platforms with two-stage watermark programming
292 * changing sprite visibility may require a extra vblank wait.
293 *
294 * Handle this here.
295 */
mode_requires_extra_vblank(enum flip_test mode)296 static bool mode_requires_extra_vblank(enum flip_test mode)
297 {
298 if (mode == flip_test_atomic_transitions ||
299 mode == flip_test_atomic_transitions_varying_size)
300 return true;
301
302 return false;
303 }
304
transition_nonblocking(igt_display_t * display,enum pipe pipe_id,struct igt_fb * prim_fb,struct igt_fb * argb_fb,bool hide_sprite)305 static void transition_nonblocking(igt_display_t *display, enum pipe pipe_id,
306 struct igt_fb *prim_fb, struct igt_fb *argb_fb,
307 bool hide_sprite)
308 {
309 igt_pipe_t *pipe = &display->pipes[pipe_id];
310 igt_plane_t *primary = igt_pipe_get_plane_type(pipe, DRM_PLANE_TYPE_PRIMARY);
311 igt_plane_t *sprite = igt_pipe_get_plane_type(pipe, DRM_PLANE_TYPE_OVERLAY);
312
313 if (hide_sprite) {
314 igt_plane_set_fb(primary, prim_fb);
315 igt_plane_set_fb(sprite, NULL);
316 } else {
317 int ret;
318
319 igt_plane_set_fb(primary, NULL);
320 igt_plane_set_fb(sprite, argb_fb);
321
322 ret = igt_display_try_commit_atomic(display, DRM_MODE_ATOMIC_NONBLOCK | DRM_MODE_PAGE_FLIP_EVENT, display);
323 if (!ret)
324 return;
325
326 igt_assert(ret == -EINVAL);
327
328 igt_plane_set_fb(sprite, prim_fb);
329 }
330 igt_display_commit_atomic(display, DRM_MODE_ATOMIC_NONBLOCK | DRM_MODE_PAGE_FLIP_EVENT, display);
331 }
332
prepare_flip_test(igt_display_t * display,enum flip_test mode,enum pipe flip_pipe,enum pipe cursor_pipe,struct drm_mode_cursor * arg,const struct igt_fb * prim_fb,struct igt_fb * argb_fb,struct igt_fb * cursor_fb2)333 static void prepare_flip_test(igt_display_t *display,
334 enum flip_test mode,
335 enum pipe flip_pipe,
336 enum pipe cursor_pipe,
337 struct drm_mode_cursor *arg,
338 const struct igt_fb *prim_fb,
339 struct igt_fb *argb_fb,
340 struct igt_fb *cursor_fb2)
341 {
342 argb_fb->gem_handle = 0;
343 cursor_fb2->gem_handle = 0;
344
345 if (mode == flip_test_varying_size ||
346 mode == flip_test_atomic_transitions_varying_size) {
347 uint64_t width, height;
348
349 do_or_die(drmGetCap(display->drm_fd, DRM_CAP_CURSOR_WIDTH, &width));
350 do_or_die(drmGetCap(display->drm_fd, DRM_CAP_CURSOR_HEIGHT, &height));
351
352 igt_skip_on(width <= 64 && height <= 64);
353 igt_create_color_fb(display->drm_fd, width, height,
354 DRM_FORMAT_ARGB8888, 0, 1., 0., .7, cursor_fb2);
355
356 arg[0].flags = arg[1].flags = DRM_MODE_CURSOR_BO;
357 arg[1].handle = cursor_fb2->gem_handle;
358 arg[1].width = width;
359 arg[1].height = height;
360 }
361
362 if (mode == flip_test_legacy ||
363 mode == flip_test_atomic) {
364 arg[1].x = 192;
365 arg[1].y = 192;
366 }
367
368 if (mode == flip_test_toggle_visibility) {
369 arg[0].flags = arg[1].flags = DRM_MODE_CURSOR_BO;
370 arg[1].handle = 0;
371 arg[1].width = arg[1].height = 0;
372 }
373
374 if (mode == flip_test_atomic_transitions ||
375 mode == flip_test_atomic_transitions_varying_size) {
376 igt_require(display->pipes[flip_pipe].n_planes > 1 &&
377 display->pipes[flip_pipe].planes[1].type != DRM_PLANE_TYPE_CURSOR);
378
379 igt_create_color_pattern_fb(display->drm_fd, prim_fb->width, prim_fb->height,
380 DRM_FORMAT_ARGB8888, 0, .1, .1, .1, argb_fb);
381 }
382 }
383
flip(igt_display_t * display,int cursor_pipe,int flip_pipe,int timeout,enum flip_test mode)384 static void flip(igt_display_t *display,
385 int cursor_pipe, int flip_pipe,
386 int timeout, enum flip_test mode)
387 {
388 struct drm_mode_cursor arg[2];
389 uint64_t *results;
390 struct igt_fb fb_info, fb_info2, argb_fb, cursor_fb, cursor_fb2;
391
392 results = mmap(NULL, PAGE_SIZE, PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
393 igt_assert(results != MAP_FAILED);
394
395 flip_pipe = find_connected_pipe(display, !!flip_pipe);
396 cursor_pipe = find_connected_pipe(display, !!cursor_pipe);
397
398 igt_info("Using pipe %s for page flip, pipe %s for cursor\n",
399 kmstest_pipe_name(flip_pipe), kmstest_pipe_name(cursor_pipe));
400
401 if (mode >= flip_test_atomic)
402 igt_require(display->is_atomic);
403
404 igt_require(set_fb_on_crtc(display, flip_pipe, &fb_info));
405 if (flip_pipe != cursor_pipe)
406 igt_require(set_fb_on_crtc(display, cursor_pipe, &fb_info2));
407
408 igt_create_color_fb(display->drm_fd, fb_info.width, fb_info.height, DRM_FORMAT_ARGB8888, 0, .5, .5, .5, &cursor_fb);
409
410 igt_create_color_fb(display->drm_fd, 64, 64, DRM_FORMAT_ARGB8888, 0, 1., 1., 1., &cursor_fb);
411 set_cursor_on_pipe(display, cursor_pipe, &cursor_fb);
412 populate_cursor_args(display, cursor_pipe, arg, &cursor_fb);
413
414 prepare_flip_test(display, mode, flip_pipe, cursor_pipe, arg, &fb_info, &argb_fb, &cursor_fb2);
415
416 igt_display_commit2(display, display->is_atomic ? COMMIT_ATOMIC : COMMIT_LEGACY);
417
418 igt_fork(child, 1) {
419 unsigned long count = 0;
420
421 igt_until_timeout(timeout) {
422 do_ioctl(display->drm_fd, DRM_IOCTL_MODE_CURSOR, &arg[(count & 64)/64]);
423 count++;
424 }
425
426 igt_debug("cursor count=%lu\n", count);
427 results[0] = count;
428 }
429 igt_fork(child, 1) {
430 unsigned long count = 0;
431
432 igt_until_timeout(timeout) {
433 char buf[128];
434
435 switch (mode) {
436 default:
437 flip_nonblocking(display, flip_pipe, mode >= flip_test_atomic, &fb_info, NULL);
438 break;
439 case flip_test_atomic_transitions:
440 case flip_test_atomic_transitions_varying_size:
441 transition_nonblocking(display, flip_pipe, &fb_info, &argb_fb, count & 1);
442 break;
443 }
444
445 while (read(display->drm_fd, buf, sizeof(buf)) < 0 &&
446 (errno == EINTR || errno == EAGAIN))
447 ;
448 count++;
449 }
450
451 igt_debug("flip count=%lu\n", count);
452 results[1] = count;
453 }
454 igt_waitchildren();
455
456 munmap(results, PAGE_SIZE);
457
458 igt_remove_fb(display->drm_fd, &fb_info);
459 if (flip_pipe != cursor_pipe)
460 igt_remove_fb(display->drm_fd, &fb_info2);
461 igt_remove_fb(display->drm_fd, &cursor_fb);
462 if (argb_fb.gem_handle)
463 igt_remove_fb(display->drm_fd, &argb_fb);
464 if (cursor_fb2.gem_handle)
465 igt_remove_fb(display->drm_fd, &cursor_fb2);
466 }
467
pipe_select(enum pipe pipe)468 static inline uint32_t pipe_select(enum pipe pipe)
469 {
470 if (pipe > 1)
471 return pipe << DRM_VBLANK_HIGH_CRTC_SHIFT;
472 else if (pipe > 0)
473 return DRM_VBLANK_SECONDARY;
474 else
475 return 0;
476 }
477
get_vblank(int fd,enum pipe pipe,unsigned flags)478 static unsigned get_vblank(int fd, enum pipe pipe, unsigned flags)
479 {
480 union drm_wait_vblank vbl;
481
482 memset(&vbl, 0, sizeof(vbl));
483 vbl.request.type = DRM_VBLANK_RELATIVE | pipe_select(pipe) | flags;
484 if (drmIoctl(fd, DRM_IOCTL_WAIT_VBLANK, &vbl))
485 return 0;
486
487 return vbl.reply.sequence;
488 }
489
490 enum basic_flip_cursor {
491 FLIP_BEFORE_CURSOR,
492 FLIP_AFTER_CURSOR
493 };
494
495 #define BASIC_BUSY 0x1
496
basic_flip_cursor(igt_display_t * display,enum flip_test mode,enum basic_flip_cursor order,unsigned flags)497 static void basic_flip_cursor(igt_display_t *display,
498 enum flip_test mode,
499 enum basic_flip_cursor order,
500 unsigned flags)
501 {
502 struct drm_mode_cursor arg[2];
503 struct drm_event_vblank vbl;
504 struct igt_fb fb_info, cursor_fb, cursor_fb2, argb_fb;
505 unsigned vblank_start;
506 enum pipe pipe = find_connected_pipe(display, false);
507 igt_spin_t *spin;
508 int i, miss1 = 0, miss2 = 0, delta;
509
510 if (mode >= flip_test_atomic)
511 igt_require(display->is_atomic);
512
513 igt_require(set_fb_on_crtc(display, pipe, &fb_info));
514
515 igt_create_color_fb(display->drm_fd, 64, 64, DRM_FORMAT_ARGB8888, 0, 1., 1., 1., &cursor_fb);
516 set_cursor_on_pipe(display, pipe, &cursor_fb);
517 populate_cursor_args(display, pipe, arg, &cursor_fb);
518
519 prepare_flip_test(display, mode, pipe, pipe, arg, &fb_info, &argb_fb, &cursor_fb2);
520
521 igt_display_commit2(display, display->is_atomic ? COMMIT_ATOMIC : COMMIT_LEGACY);
522
523 /* Quick sanity check that we can update a cursor in a single vblank */
524 vblank_start = get_vblank(display->drm_fd, pipe, DRM_VBLANK_NEXTONMISS);
525 igt_assert_eq(get_vblank(display->drm_fd, pipe, 0), vblank_start);
526 do_ioctl(display->drm_fd, DRM_IOCTL_MODE_CURSOR, &arg[0]);
527 igt_assert_eq(get_vblank(display->drm_fd, pipe, 0), vblank_start);
528
529 for (i = 0; i < 25; i++) {
530 bool miss;
531
532 /* Bind the cursor first to warm up */
533 do_ioctl(display->drm_fd, DRM_IOCTL_MODE_CURSOR, &arg[0]);
534
535 spin = NULL;
536 if (flags & BASIC_BUSY)
537 spin = igt_spin_new(display->drm_fd,
538 .dependency = fb_info.gem_handle);
539
540 /* Start with a synchronous query to align with the vblank */
541 vblank_start = get_vblank(display->drm_fd, pipe, DRM_VBLANK_NEXTONMISS);
542
543 switch (order) {
544 case FLIP_BEFORE_CURSOR:
545 switch (mode) {
546 default:
547 flip_nonblocking(display, pipe, mode >= flip_test_atomic, &fb_info, NULL);
548 break;
549 case flip_test_atomic_transitions:
550 case flip_test_atomic_transitions_varying_size:
551 transition_nonblocking(display, pipe, &fb_info, &argb_fb, 0);
552 break;
553 }
554
555 delta = get_vblank(display->drm_fd, pipe, 0) - vblank_start;
556 miss = delta != 0;
557
558 do_ioctl(display->drm_fd, DRM_IOCTL_MODE_CURSOR, &arg[0]);
559 break;
560
561 case FLIP_AFTER_CURSOR:
562 do_ioctl(display->drm_fd, DRM_IOCTL_MODE_CURSOR, &arg[0]);
563
564 delta = get_vblank(display->drm_fd, pipe, 0) - vblank_start;
565 miss = delta != 0;
566
567 switch (mode) {
568 default:
569 flip_nonblocking(display, pipe, mode >= flip_test_atomic, &fb_info, NULL);
570 break;
571 case flip_test_atomic_transitions:
572 case flip_test_atomic_transitions_varying_size:
573 transition_nonblocking(display, pipe, &fb_info, &argb_fb, 0);
574 break;
575 }
576 }
577
578 delta = get_vblank(display->drm_fd, pipe, 0) - vblank_start;
579
580 if (spin) {
581 struct pollfd pfd = { display->drm_fd, POLLIN };
582 igt_assert(poll(&pfd, 1, 0) == 0);
583 igt_spin_free(display->drm_fd, spin);
584 }
585
586 if (miss)
587 { /* compare nothing, already failed */ }
588 else if (!cursor_slowpath(mode))
589 miss = delta != 0;
590 else
591 miss = delta != 0 && delta != 1;
592
593 miss1 += miss;
594
595 igt_set_timeout(1, "Stuck page flip");
596 igt_ignore_warn(read(display->drm_fd, &vbl, sizeof(vbl)));
597 igt_reset_timeout();
598
599 if (miss1)
600 continue;
601
602 delta = get_vblank(display->drm_fd, pipe, 0) - vblank_start;
603
604 if (!mode_requires_extra_vblank(mode))
605 miss2 += delta != 1;
606 else
607 miss2 += delta != 1 && delta != 2;
608 }
609
610 igt_fail_on_f(miss1 > 2 || miss1 + miss2 > 5, "Failed to evade %i vblanks and missed %i page flips\n", miss1, miss2);
611 if (miss1 || miss2)
612 igt_info("Failed to evade %i vblanks and missed %i page flips\n", miss1, miss2);
613
614 igt_remove_fb(display->drm_fd, &fb_info);
615 igt_remove_fb(display->drm_fd, &cursor_fb);
616
617 if (argb_fb.gem_handle)
618 igt_remove_fb(display->drm_fd, &argb_fb);
619 if (cursor_fb2.gem_handle)
620 igt_remove_fb(display->drm_fd, &cursor_fb2);
621 }
622
623 static int
get_cursor_updates_per_vblank(igt_display_t * display,enum pipe pipe,struct drm_mode_cursor * arg)624 get_cursor_updates_per_vblank(igt_display_t *display, enum pipe pipe,
625 struct drm_mode_cursor *arg)
626 {
627 int target;
628
629 for (target = 65536; target; target /= 2) {
630 unsigned vblank_start = get_vblank(display->drm_fd, pipe, DRM_VBLANK_NEXTONMISS);
631
632 igt_assert_eq(get_vblank(display->drm_fd, pipe, 0), vblank_start);
633
634 for (int n = 0; n < target; n++)
635 do_ioctl(display->drm_fd, DRM_IOCTL_MODE_CURSOR, arg);
636 if (get_vblank(display->drm_fd, pipe, 0) == vblank_start)
637 break;
638 }
639
640 /*
641 * Divide by 4, to handle variations in amount of vblanks
642 * caused by cpufreq throttling.
643 */
644 target /= 4;
645 igt_require(target > 1);
646
647 igt_info("Using a target of %d cursor updates per quarter-vblank\n", target);
648
649 return target;
650 }
651
flip_vs_cursor(igt_display_t * display,enum flip_test mode,int nloops)652 static void flip_vs_cursor(igt_display_t *display, enum flip_test mode, int nloops)
653 {
654 struct drm_mode_cursor arg[2];
655 struct drm_event_vblank vbl;
656 struct igt_fb fb_info, cursor_fb, cursor_fb2, argb_fb;
657 unsigned vblank_start;
658 int target, cpu;
659 enum pipe pipe = find_connected_pipe(display, false);
660 volatile unsigned long *shared;
661 cpu_set_t mask, oldmask;
662
663 if (mode >= flip_test_atomic)
664 igt_require(display->is_atomic);
665
666 igt_require(set_fb_on_crtc(display, pipe, &fb_info));
667
668 igt_create_color_fb(display->drm_fd, 64, 64, DRM_FORMAT_ARGB8888, 0, 1., 1., 1., &cursor_fb);
669 set_cursor_on_pipe(display, pipe, &cursor_fb);
670 populate_cursor_args(display, pipe, arg, &cursor_fb);
671
672 prepare_flip_test(display, mode, pipe, pipe, arg, &fb_info, &argb_fb, &cursor_fb2);
673
674 igt_display_commit2(display, display->is_atomic ? COMMIT_ATOMIC : COMMIT_LEGACY);
675
676 if (nloops)
677 target = get_cursor_updates_per_vblank(display, pipe, &arg[0]);
678 else
679 target = 1;
680
681 vblank_start = get_vblank(display->drm_fd, pipe, DRM_VBLANK_NEXTONMISS);
682 igt_assert_eq(get_vblank(display->drm_fd, pipe, 0), vblank_start);
683 for (int n = 0; n < target; n++)
684 do_ioctl(display->drm_fd, DRM_IOCTL_MODE_CURSOR, &arg[0]);
685 igt_assert_eq(get_vblank(display->drm_fd, pipe, 0), vblank_start);
686
687 /*
688 * There are variations caused by using cpu frequency changing. To
689 * eliminate those we force this test to run on the same cpu as an
690 * idle thread that does a busy loop of sched_yield(); The effect is
691 * that we don't throttle the cpu to a lower frequency, and the
692 * variations caused by cpu speed changing are eliminated.
693 */
694 if (target > 1) {
695 shared = mmap(NULL, PAGE_SIZE, PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
696 igt_assert(shared != MAP_FAILED);
697
698 cpu = sched_getcpu();
699 igt_assert(cpu >= 0);
700
701 CPU_ZERO(&mask);
702 CPU_SET(cpu, &mask);
703 sched_getaffinity(0, sizeof(oldmask), &oldmask);
704 sched_setaffinity(0, sizeof(mask), &mask);
705
706 shared[0] = 0;
707
708 igt_fork(child, 1) {
709 struct sched_param parm = { .sched_priority = 0 };
710
711 igt_assert(sched_setscheduler(0, SCHED_IDLE, &parm) == 0);
712
713 while (!shared[0])
714 sched_yield();
715 }
716 }
717
718 do {
719 /* Bind the cursor first to warm up */
720 do_ioctl(display->drm_fd, DRM_IOCTL_MODE_CURSOR, &arg[nloops & 1]);
721
722 /* Start with a synchronous query to align with the vblank */
723 vblank_start = get_vblank(display->drm_fd, pipe, DRM_VBLANK_NEXTONMISS);
724 switch (mode) {
725 default:
726 flip_nonblocking(display, pipe, mode >= flip_test_atomic, &fb_info, NULL);
727 break;
728 case flip_test_atomic_transitions:
729 case flip_test_atomic_transitions_varying_size:
730 transition_nonblocking(display, pipe, &fb_info, &argb_fb, (nloops & 2) /2);
731 break;
732 }
733
734 /* The nonblocking flip should not have delayed us */
735 igt_assert_eq(get_vblank(display->drm_fd, pipe, 0), vblank_start);
736 for (int n = 0; n < target; n++)
737 do_ioctl(display->drm_fd, DRM_IOCTL_MODE_CURSOR, &arg[nloops & 1]);
738
739 /* Nor should it have delayed the following cursor update */
740 if (!cursor_slowpath(mode))
741 igt_assert_eq(get_vblank(display->drm_fd, pipe, 0), vblank_start);
742 else
743 igt_assert_lte(get_vblank(display->drm_fd, pipe, 0), vblank_start + 1);
744
745 igt_set_timeout(1, "Stuck page flip");
746 igt_ignore_warn(read(display->drm_fd, &vbl, sizeof(vbl)));
747
748 if (!mode_requires_extra_vblank(mode))
749 igt_assert_eq(get_vblank(display->drm_fd, pipe, 0), vblank_start + 1);
750 else
751 igt_assert_lte(get_vblank(display->drm_fd, pipe, 0), vblank_start + 2);
752
753 igt_reset_timeout();
754 } while (nloops--);
755
756 if (target > 1) {
757 shared[0] = 1;
758 igt_waitchildren();
759 munmap((void *)shared, PAGE_SIZE);
760 sched_setaffinity(0, sizeof(oldmask), &oldmask);
761 }
762
763 igt_remove_fb(display->drm_fd, &fb_info);
764 igt_remove_fb(display->drm_fd, &cursor_fb);
765
766 if (argb_fb.gem_handle)
767 igt_remove_fb(display->drm_fd, &argb_fb);
768 if (cursor_fb2.gem_handle)
769 igt_remove_fb(display->drm_fd, &cursor_fb2);
770 }
771
nonblocking_modeset_vs_cursor(igt_display_t * display,int loops)772 static void nonblocking_modeset_vs_cursor(igt_display_t *display, int loops)
773 {
774 struct igt_fb fb_info, cursor_fb;
775 igt_output_t *output;
776 enum pipe pipe = find_connected_pipe(display, false);
777 struct drm_mode_cursor arg[2];
778 igt_plane_t *cursor = NULL, *plane;
779
780 igt_require(display->is_atomic);
781 igt_require((output = set_fb_on_crtc(display, pipe, &fb_info)));
782 igt_create_color_fb(display->drm_fd, 64, 64, DRM_FORMAT_ARGB8888, 0, 1., 1., 1., &cursor_fb);
783 set_cursor_on_pipe(display, pipe, &cursor_fb);
784 populate_cursor_args(display, pipe, arg, &cursor_fb);
785 arg[0].flags |= DRM_MODE_CURSOR_BO;
786
787 for_each_plane_on_pipe(display, pipe, plane) {
788 if (plane->type != DRM_PLANE_TYPE_CURSOR)
789 continue;
790
791 cursor = plane;
792 break;
793 }
794
795 igt_skip_on(!cursor);
796
797 /*
798 * Start disabled. No way around it, since the first atomic
799 * commit may be unreliable with amount of events sent.
800 */
801 igt_output_set_pipe(output, PIPE_NONE);
802 igt_display_commit2(display, COMMIT_ATOMIC);
803
804 while (loops--) {
805 unsigned flags;
806 struct pollfd pfd = { display->drm_fd, POLLIN };
807 struct drm_event_vblank vbl;
808
809 flags = DRM_MODE_ATOMIC_ALLOW_MODESET;
810 flags |= DRM_MODE_ATOMIC_NONBLOCK;
811 flags |= DRM_MODE_PAGE_FLIP_EVENT;
812
813 /*
814 * Test that a cursor update after a nonblocking modeset
815 * works as intended. It should block until the modeset completes.
816 */
817
818 igt_output_set_pipe(output, pipe);
819 igt_plane_set_fb(cursor, NULL);
820 igt_display_commit_atomic(display, flags, NULL);
821
822 igt_assert_eq(0, poll(&pfd, 1, 0));
823 igt_assert_eq(0, pfd.revents);
824
825 do_ioctl(display->drm_fd, DRM_IOCTL_MODE_CURSOR, &arg[0]);
826
827 igt_assert_eq(1, poll(&pfd, 1, 0));
828 igt_assert_eq(POLLIN, pfd.revents);
829
830 igt_set_timeout(1, "Stuck page flip");
831 igt_ignore_warn(read(display->drm_fd, &vbl, sizeof(vbl)));
832 igt_reset_timeout();
833
834 igt_output_set_pipe(output, PIPE_NONE);
835 igt_display_commit_atomic(display, flags, NULL);
836
837 igt_assert_eq(0, poll(&pfd, 1, 0));
838 igt_assert_eq(0, pfd.revents);
839
840 /* Same for cursor on disabled crtc. */
841 do_ioctl(display->drm_fd, DRM_IOCTL_MODE_CURSOR, &arg[0]);
842
843 igt_assert_eq(1, poll(&pfd, 1, 0));
844 igt_assert_eq(POLLIN, pfd.revents);
845
846 igt_set_timeout(1, "Stuck page flip");
847 igt_ignore_warn(read(display->drm_fd, &vbl, sizeof(vbl)));
848 igt_reset_timeout();
849 }
850
851 igt_remove_fb(display->drm_fd, &fb_info);
852 igt_remove_fb(display->drm_fd, &cursor_fb);
853 }
854
two_screens_flip_vs_cursor(igt_display_t * display,int nloops,bool modeset,bool atomic)855 static void two_screens_flip_vs_cursor(igt_display_t *display, int nloops, bool modeset, bool atomic)
856 {
857 struct drm_mode_cursor arg1[2], arg2[2];
858 struct igt_fb fb_info, fb2_info, cursor_fb;
859 enum pipe pipe = find_connected_pipe(display, false);
860 enum pipe pipe2 = find_connected_pipe(display, true);
861 igt_output_t *output, *output2;
862 bool enabled = false;
863 volatile unsigned long *shared;
864 unsigned flags = 0, vblank_start;
865 struct drm_event_vblank vbl;
866 int ret;
867
868 if (modeset) {
869 uint64_t val;
870
871 igt_fail_on(!atomic);
872 igt_require(drmGetCap(display->drm_fd, DRM_CAP_CRTC_IN_VBLANK_EVENT, &val) == 0);
873 }
874
875 shared = mmap(NULL, PAGE_SIZE, PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
876 igt_assert(shared != MAP_FAILED);
877
878 igt_fail_on(modeset && !atomic);
879
880 if (atomic)
881 igt_require(display->is_atomic);
882
883 igt_require((output = set_fb_on_crtc(display, pipe, &fb_info)));
884 igt_require((output2 = set_fb_on_crtc(display, pipe2, &fb2_info)));
885
886 igt_create_color_fb(display->drm_fd, 64, 64, DRM_FORMAT_ARGB8888, 0, 1., 1., 1., &cursor_fb);
887 set_cursor_on_pipe(display, pipe, &cursor_fb);
888 populate_cursor_args(display, pipe, arg1, &cursor_fb);
889
890 arg1[1].x = arg1[1].y = 192;
891
892 set_cursor_on_pipe(display, pipe2, &cursor_fb);
893 populate_cursor_args(display, pipe2, arg2, &cursor_fb);
894
895 arg2[1].x = arg2[1].y = 192;
896
897
898 igt_display_commit2(display, display->is_atomic ? COMMIT_ATOMIC : COMMIT_LEGACY);
899
900 igt_fork(child, 2) {
901 struct drm_mode_cursor *arg = child ? arg2 : arg1;
902
903 while (!shared[0])
904 do_ioctl(display->drm_fd, DRM_IOCTL_MODE_CURSOR,
905 &arg[!shared[1]]);
906 }
907
908 if (modeset) {
909 igt_plane_t *plane = igt_output_get_plane_type(output, DRM_PLANE_TYPE_PRIMARY);
910
911 flags = DRM_MODE_ATOMIC_ALLOW_MODESET |
912 DRM_MODE_ATOMIC_NONBLOCK | DRM_MODE_PAGE_FLIP_EVENT;
913
914 /* Disable pipe2 */
915 igt_output_set_pipe(output2, PIPE_NONE);
916 igt_display_commit_atomic(display, flags, NULL);
917 enabled = false;
918
919 /*
920 * Try a page flip on crtc 1, if we succeed pump page flips and
921 * modesets interleaved, else do a single atomic commit with both.
922 */
923 vblank_start = get_vblank(display->drm_fd, pipe, DRM_VBLANK_NEXTONMISS);
924 igt_plane_set_fb(plane, &fb_info);
925 ret = igt_display_try_commit_atomic(display, flags, (void*)(ptrdiff_t)vblank_start);
926 igt_assert(!ret || ret == -EBUSY);
927
928 if (ret == -EBUSY) {
929 /* Force completion on both pipes, and generate event. */
930 igt_display_commit_atomic(display, flags, NULL);
931
932 while (nloops--) {
933 shared[1] = nloops & 1;
934
935 igt_set_timeout(35, "Stuck modeset");
936 igt_assert_eq(read(display->drm_fd, &vbl, sizeof(vbl)), sizeof(vbl));
937 igt_assert_eq(read(display->drm_fd, &vbl, sizeof(vbl)), sizeof(vbl));
938 igt_reset_timeout();
939
940 if (!nloops)
941 break;
942
943 /* Commit page flip and modeset simultaneously. */
944 igt_plane_set_fb(plane, &fb_info);
945 igt_output_set_pipe(output2, enabled ? PIPE_NONE : pipe2);
946 enabled = !enabled;
947
948 igt_set_timeout(5, "Scheduling modeset\n");
949 do {
950 ret = igt_display_try_commit_atomic(display, flags, NULL);
951 } while (ret == -EBUSY);
952 igt_assert(!ret);
953 igt_reset_timeout();
954 }
955
956 goto done;
957 }
958 } else {
959 vblank_start = get_vblank(display->drm_fd, pipe, DRM_VBLANK_NEXTONMISS);
960 flip_nonblocking(display, pipe, atomic, &fb_info, (void*)(ptrdiff_t)vblank_start);
961
962 vblank_start = get_vblank(display->drm_fd, pipe2, DRM_VBLANK_NEXTONMISS);
963 flip_nonblocking(display, pipe2, atomic, &fb2_info, (void*)(ptrdiff_t)vblank_start);
964 }
965
966 while (nloops) {
967 shared[1] = nloops & 1;
968
969 if (!modeset || nloops > 1)
970 igt_set_timeout(1, "Stuck page flip");
971 else
972 igt_set_timeout(35, "Stuck modeset");
973 igt_assert_eq(read(display->drm_fd, &vbl, sizeof(vbl)), sizeof(vbl));
974 igt_reset_timeout();
975
976 vblank_start = vbl.user_data;
977 if (!modeset)
978 igt_assert_eq(vbl.sequence, vblank_start + 1);
979
980 /* Do not requeue on the last 2 events. */
981 if (nloops <= 2) {
982 nloops--;
983 continue;
984 }
985
986 if (vbl.crtc_id == display->pipes[pipe].crtc_id) {
987 vblank_start = get_vblank(display->drm_fd, pipe, DRM_VBLANK_NEXTONMISS);
988 flip_nonblocking(display, pipe, atomic, &fb_info, (void*)(ptrdiff_t)vblank_start);
989 } else {
990 igt_assert(vbl.crtc_id == display->pipes[pipe2].crtc_id);
991
992 nloops--;
993
994 if (!modeset) {
995 vblank_start = get_vblank(display->drm_fd, pipe2, DRM_VBLANK_NEXTONMISS);
996 flip_nonblocking(display, pipe2, atomic, &fb2_info, (void*)(ptrdiff_t)vblank_start);
997 } else {
998 igt_output_set_pipe(output2, enabled ? PIPE_NONE : pipe2);
999
1000 igt_set_timeout(1, "Scheduling modeset\n");
1001 do {
1002 ret = igt_display_try_commit_atomic(display, flags, NULL);
1003 } while (ret == -EBUSY);
1004 igt_assert(!ret);
1005 igt_reset_timeout();
1006
1007 enabled = !enabled;
1008 }
1009 }
1010 }
1011
1012 done:
1013 shared[0] = 1;
1014 igt_waitchildren();
1015
1016 igt_remove_fb(display->drm_fd, &fb_info);
1017 igt_remove_fb(display->drm_fd, &fb2_info);
1018 igt_remove_fb(display->drm_fd, &cursor_fb);
1019 munmap((void *)shared, PAGE_SIZE);
1020 }
1021
cursor_vs_flip(igt_display_t * display,enum flip_test mode,int nloops)1022 static void cursor_vs_flip(igt_display_t *display, enum flip_test mode, int nloops)
1023 {
1024 struct drm_mode_cursor arg[2];
1025 struct drm_event_vblank vbl;
1026 struct igt_fb fb_info, cursor_fb, cursor_fb2, argb_fb;
1027 unsigned vblank_start, vblank_last;
1028 volatile unsigned long *shared;
1029 long target;
1030 enum pipe pipe = find_connected_pipe(display, false);
1031 igt_output_t *output;
1032 uint32_t vrefresh;
1033 int fail_count;
1034
1035 if (mode >= flip_test_atomic)
1036 igt_require(display->is_atomic);
1037
1038 shared = mmap(NULL, PAGE_SIZE, PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
1039 igt_assert(shared != MAP_FAILED);
1040
1041 igt_require((output = set_fb_on_crtc(display, pipe, &fb_info)));
1042 vrefresh = igt_output_get_mode(output)->vrefresh;
1043
1044 igt_create_color_fb(display->drm_fd, 64, 64, DRM_FORMAT_ARGB8888, 0, 1., 1., 1., &cursor_fb);
1045 set_cursor_on_pipe(display, pipe, &cursor_fb);
1046 populate_cursor_args(display, pipe, arg, &cursor_fb);
1047
1048 prepare_flip_test(display, mode, pipe, pipe, arg, &fb_info, &argb_fb, &cursor_fb2);
1049
1050 igt_display_commit2(display, display->is_atomic ? COMMIT_ATOMIC : COMMIT_LEGACY);
1051
1052 target = get_cursor_updates_per_vblank(display, pipe, &arg[0]);
1053
1054 fail_count = 0;
1055
1056 for (int i = 0; i < nloops; i++) {
1057 shared[0] = 0;
1058 igt_fork(child, 1) {
1059 unsigned long count = 0;
1060 while (!shared[0]) {
1061 do_ioctl(display->drm_fd, DRM_IOCTL_MODE_CURSOR, &arg[i & 1]);
1062 count++;
1063 }
1064 igt_debug("child: %lu cursor updates\n", count);
1065 shared[0] = count;
1066 }
1067
1068 switch (mode) {
1069 default:
1070 flip_nonblocking(display, pipe, mode >= flip_test_atomic, &fb_info, NULL);
1071 break;
1072 case flip_test_atomic_transitions:
1073 case flip_test_atomic_transitions_varying_size:
1074 transition_nonblocking(display, pipe, &fb_info, &argb_fb, (i & 2) >> 1);
1075 break;
1076 }
1077
1078 igt_assert_eq(read(display->drm_fd, &vbl, sizeof(vbl)), sizeof(vbl));
1079 vblank_start = vblank_last = vbl.sequence;
1080 for (int n = 0; n < vrefresh / 2; n++) {
1081 flip_nonblocking(display, pipe, mode >= flip_test_atomic, &fb_info, NULL);
1082
1083 igt_assert_eq(read(display->drm_fd, &vbl, sizeof(vbl)), sizeof(vbl));
1084 if (vbl.sequence != vblank_last + 1) {
1085 igt_info("page flip %d was delayed, missed %d frames\n",
1086 n, vbl.sequence - vblank_last - 1);
1087 }
1088 vblank_last = vbl.sequence;
1089 }
1090
1091 if (!cursor_slowpath(mode))
1092 igt_assert_lte(vbl.sequence, vblank_start + 5 * vrefresh / 8);
1093
1094 shared[0] = 1;
1095 igt_waitchildren();
1096 if (shared[0] <= vrefresh*target / 2) {
1097 fail_count++;
1098 igt_critical("completed %lu cursor updated in a period of %u flips, "
1099 "we expect to complete approximately %lu updates, "
1100 "with the threshold set at %lu\n",
1101 shared[0], vrefresh / 2,
1102 vrefresh*target, vrefresh*target / 2);
1103 }
1104 }
1105
1106 igt_assert_f(fail_count == 0,
1107 "Failed to meet cursor update expectations in %d out of %d iterations\n",
1108 fail_count, nloops);
1109
1110 igt_remove_fb(display->drm_fd, &fb_info);
1111 igt_remove_fb(display->drm_fd, &cursor_fb);
1112 munmap((void *)shared, PAGE_SIZE);
1113 if (argb_fb.gem_handle)
1114 igt_remove_fb(display->drm_fd, &argb_fb);
1115 if (cursor_fb2.gem_handle)
1116 igt_remove_fb(display->drm_fd, &cursor_fb2);
1117 }
1118
two_screens_cursor_vs_flip(igt_display_t * display,int nloops,bool atomic)1119 static void two_screens_cursor_vs_flip(igt_display_t *display, int nloops, bool atomic)
1120 {
1121 struct drm_mode_cursor arg[2][2];
1122 struct drm_event_vblank vbl;
1123 struct igt_fb fb_info[2], cursor_fb;
1124 volatile unsigned long *shared;
1125 int target[2];
1126 enum pipe pipe[2] = {
1127 find_connected_pipe(display, false),
1128 find_connected_pipe(display, true)
1129 };
1130 igt_output_t *outputs[2];
1131
1132 shared = mmap(NULL, PAGE_SIZE, PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
1133 igt_assert(shared != MAP_FAILED);
1134
1135 if (atomic)
1136 igt_require(display->is_atomic);
1137
1138 igt_require((outputs[0] = set_fb_on_crtc(display, pipe[0], &fb_info[0])));
1139 igt_require((outputs[1] = set_fb_on_crtc(display, pipe[1], &fb_info[1])));
1140
1141 igt_create_color_fb(display->drm_fd, 64, 64, DRM_FORMAT_ARGB8888, 0, 1., 1., 1., &cursor_fb);
1142
1143 set_cursor_on_pipe(display, pipe[0], &cursor_fb);
1144 populate_cursor_args(display, pipe[0], arg[0], &cursor_fb);
1145 arg[0][1].x = arg[0][1].y = 192;
1146
1147 set_cursor_on_pipe(display, pipe[1], &cursor_fb);
1148 populate_cursor_args(display, pipe[1], arg[1], &cursor_fb);
1149 arg[1][1].x = arg[1][1].y = 192;
1150
1151 igt_display_commit2(display, display->is_atomic ? COMMIT_ATOMIC : COMMIT_LEGACY);
1152
1153 target[0] = get_cursor_updates_per_vblank(display, pipe[0], &arg[0][0]);
1154 target[1] = get_cursor_updates_per_vblank(display, pipe[1], &arg[1][0]);
1155
1156 for (int i = 0; i < nloops; i++) {
1157 unsigned long vrefresh[2];
1158 unsigned vblank_start[2], vblank_last[2];
1159 int done[2] = {};
1160
1161 vrefresh[0] = igt_output_get_mode(outputs[0])->vrefresh;
1162 vrefresh[1] = igt_output_get_mode(outputs[1])->vrefresh;
1163
1164 shared[0] = 0;
1165 shared[1] = 0;
1166 igt_fork(child, 2) {
1167 unsigned long count = 0;
1168
1169 while (!shared[child]) {
1170 do_ioctl(display->drm_fd, DRM_IOCTL_MODE_CURSOR, &arg[child][(i >> child) & 1]);
1171 count++;
1172 }
1173 igt_debug("child %i: %lu cursor updates\n", child, count);
1174 shared[child] = count;
1175 }
1176
1177 flip_nonblocking(display, pipe[0], atomic, &fb_info[0], (void *)0UL);
1178 flip_nonblocking(display, pipe[1], atomic, &fb_info[1], (void *)1UL);
1179
1180 for (int n = 0; n < vrefresh[0] / 2 + vrefresh[1] / 2; n++) {
1181 unsigned long child;
1182
1183 igt_assert_eq(read(display->drm_fd, &vbl, sizeof(vbl)), sizeof(vbl));
1184 child = vbl.user_data;
1185
1186 if (!done[child]++)
1187 vblank_start[child] = vbl.sequence;
1188 else if (vbl.sequence != vblank_last[child] + 1)
1189 igt_info("page flip %d was delayed, missed %d frames\n",
1190 done[child], vbl.sequence - vblank_last[child] - 1);
1191
1192 vblank_last[child] = vbl.sequence;
1193
1194 if (done[child] < vrefresh[child] / 2) {
1195 flip_nonblocking(display, pipe[child], atomic, &fb_info[child], (void *)child);
1196 } else {
1197 igt_assert_lte(vbl.sequence, vblank_start[child] + 5 * vrefresh[child] / 8);
1198
1199 shared[child] = 1;
1200 }
1201 }
1202
1203 igt_assert_eq(done[0], vrefresh[0] / 2);
1204 igt_assert_eq(done[1], vrefresh[1] / 2);
1205
1206 igt_waitchildren();
1207 for (int child = 0; child < 2; child++)
1208 igt_assert_f(shared[child] > vrefresh[child]*target[child] / 2,
1209 "completed %lu cursor updated in a period of %lu flips, "
1210 "we expect to complete approximately %lu updates, "
1211 "with the threshold set at %lu\n",
1212 shared[child], vrefresh[child] / 2,
1213 vrefresh[child]*target[child], vrefresh[child]*target[child] / 2);
1214 }
1215
1216 igt_remove_fb(display->drm_fd, &fb_info[0]);
1217 igt_remove_fb(display->drm_fd, &fb_info[1]);
1218 igt_remove_fb(display->drm_fd, &cursor_fb);
1219 munmap((void *)shared, PAGE_SIZE);
1220 }
1221
flip_vs_cursor_crc(igt_display_t * display,bool atomic)1222 static void flip_vs_cursor_crc(igt_display_t *display, bool atomic)
1223 {
1224 struct drm_mode_cursor arg[2];
1225 struct drm_event_vblank vbl;
1226 struct igt_fb fb_info, cursor_fb;
1227 unsigned vblank_start;
1228 enum pipe pipe = find_connected_pipe(display, false);
1229 igt_crc_t crcs[3];
1230
1231 if (atomic)
1232 igt_require(display->is_atomic);
1233
1234 igt_require(set_fb_on_crtc(display, pipe, &fb_info));
1235
1236 igt_create_color_fb(display->drm_fd, 64, 64, DRM_FORMAT_ARGB8888, 0, 1., 1., 1., &cursor_fb);
1237 populate_cursor_args(display, pipe, arg, &cursor_fb);
1238
1239 igt_display_commit2(display, display->is_atomic ? COMMIT_ATOMIC : COMMIT_LEGACY);
1240
1241 pipe_crc = igt_pipe_crc_new(display->drm_fd, pipe, INTEL_PIPE_CRC_SOURCE_AUTO);
1242
1243 set_cursor_on_pipe(display, pipe, &cursor_fb);
1244 igt_display_commit2(display, COMMIT_UNIVERSAL);
1245
1246 /* Collect reference crcs, crcs[0] last. */
1247 do_ioctl(display->drm_fd, DRM_IOCTL_MODE_CURSOR, &arg[1]);
1248 igt_pipe_crc_collect_crc(pipe_crc, &crcs[1]);
1249
1250 do_ioctl(display->drm_fd, DRM_IOCTL_MODE_CURSOR, &arg[0]);
1251 igt_pipe_crc_collect_crc(pipe_crc, &crcs[0]);
1252
1253 /* Disable cursor, and immediately queue a flip. Check if resulting crc is correct. */
1254 for (int i = 1; i >= 0; i--) {
1255 vblank_start = get_vblank(display->drm_fd, pipe, DRM_VBLANK_NEXTONMISS);
1256
1257 flip_nonblocking(display, pipe, atomic, &fb_info, NULL);
1258 do_ioctl(display->drm_fd, DRM_IOCTL_MODE_CURSOR, &arg[i]);
1259
1260 igt_assert_eq(get_vblank(display->drm_fd, pipe, 0), vblank_start);
1261
1262 igt_set_timeout(1, "Stuck page flip");
1263 igt_ignore_warn(read(display->drm_fd, &vbl, sizeof(vbl)));
1264 igt_reset_timeout();
1265
1266 igt_assert_eq(get_vblank(display->drm_fd, pipe, 0), vblank_start + 1);
1267
1268 igt_pipe_crc_collect_crc(pipe_crc, &crcs[2]);
1269
1270 igt_assert_crc_equal(&crcs[i], &crcs[2]);
1271 }
1272
1273 igt_remove_fb(display->drm_fd, &fb_info);
1274 igt_remove_fb(display->drm_fd, &cursor_fb);
1275 }
1276
flip_vs_cursor_busy_crc(igt_display_t * display,bool atomic)1277 static void flip_vs_cursor_busy_crc(igt_display_t *display, bool atomic)
1278 {
1279 struct drm_mode_cursor arg[2];
1280 struct drm_event_vblank vbl;
1281 struct igt_fb fb_info[2], cursor_fb;
1282 unsigned vblank_start;
1283 enum pipe pipe = find_connected_pipe(display, false);
1284 igt_pipe_t *pipe_connected = &display->pipes[pipe];
1285 igt_plane_t *plane_primary = igt_pipe_get_plane_type(pipe_connected, DRM_PLANE_TYPE_PRIMARY);
1286 igt_crc_t crcs[2], test_crc;
1287
1288 if (atomic)
1289 igt_require(display->is_atomic);
1290
1291 igt_require(set_fb_on_crtc(display, pipe, &fb_info[0]));
1292 igt_create_color_pattern_fb(display->drm_fd, fb_info[0].width, fb_info[0].height,
1293 DRM_FORMAT_XRGB8888, LOCAL_I915_FORMAT_MOD_X_TILED, .1, .1, .1, &fb_info[1]);
1294
1295 igt_create_color_fb(display->drm_fd, 64, 64, DRM_FORMAT_ARGB8888, 0, 1., 1., 1., &cursor_fb);
1296 populate_cursor_args(display, pipe, arg, &cursor_fb);
1297
1298 igt_display_commit2(display, display->is_atomic ? COMMIT_ATOMIC : COMMIT_LEGACY);
1299
1300 pipe_crc = igt_pipe_crc_new(display->drm_fd, pipe, INTEL_PIPE_CRC_SOURCE_AUTO);
1301
1302 set_cursor_on_pipe(display, pipe, &cursor_fb);
1303 igt_display_commit2(display, COMMIT_UNIVERSAL);
1304
1305 /* Collect reference crcs, crc[0] last for the loop. */
1306 do_ioctl(display->drm_fd, DRM_IOCTL_MODE_CURSOR, &arg[1]);
1307 igt_pipe_crc_collect_crc(pipe_crc, &crcs[1]);
1308
1309 do_ioctl(display->drm_fd, DRM_IOCTL_MODE_CURSOR, &arg[0]);
1310 igt_pipe_crc_collect_crc(pipe_crc, &crcs[0]);
1311
1312 /*
1313 * Set fb 1 on primary at least once before flipping to force
1314 * setting the correct cache level, else we get a stall in the
1315 * page flip handler.
1316 */
1317 igt_plane_set_fb(plane_primary, &fb_info[1]);
1318 igt_display_commit2(display, COMMIT_UNIVERSAL);
1319
1320 igt_plane_set_fb(plane_primary, &fb_info[0]);
1321 igt_display_commit2(display, COMMIT_UNIVERSAL);
1322
1323 /*
1324 * We must enable CRC collecting here since this may force
1325 * a modeset, and this loop is timing sensitive.
1326 */
1327 igt_pipe_crc_start(pipe_crc);
1328
1329 /* Disable cursor, and immediately queue a flip. Check if resulting crc is correct. */
1330 for (int i = 1; i >= 0; i--) {
1331 igt_spin_t *spin;
1332
1333 spin = igt_spin_new(display->drm_fd,
1334 .dependency = fb_info[1].gem_handle);
1335
1336 vblank_start = get_vblank(display->drm_fd, pipe, DRM_VBLANK_NEXTONMISS);
1337
1338 flip_nonblocking(display, pipe, atomic, &fb_info[1], NULL);
1339 do_ioctl(display->drm_fd, DRM_IOCTL_MODE_CURSOR, &arg[i]);
1340
1341 igt_assert_eq(get_vblank(display->drm_fd, pipe, 0), vblank_start);
1342
1343 igt_pipe_crc_get_current(display->drm_fd, pipe_crc, &test_crc);
1344
1345 igt_spin_free(display->drm_fd, spin);
1346
1347 igt_set_timeout(1, "Stuck page flip");
1348 igt_ignore_warn(read(display->drm_fd, &vbl, sizeof(vbl)));
1349 igt_reset_timeout();
1350
1351 igt_assert_lte(vblank_start + 1, get_vblank(display->drm_fd, pipe, 0));
1352
1353 igt_plane_set_fb(plane_primary, &fb_info[0]);
1354 igt_display_commit2(display, COMMIT_UNIVERSAL);
1355
1356 igt_assert_crc_equal(&crcs[i], &test_crc);
1357 }
1358
1359 igt_remove_fb(display->drm_fd, &fb_info[1]);
1360 igt_remove_fb(display->drm_fd, &fb_info[0]);
1361 igt_remove_fb(display->drm_fd, &cursor_fb);
1362 }
1363
1364 igt_main
1365 {
1366 const int ncpus = sysconf(_SC_NPROCESSORS_ONLN);
1367 igt_display_t display = { .drm_fd = -1 };
1368 int i;
1369
1370 igt_fixture {
1371 display.drm_fd = drm_open_driver_master(DRIVER_ANY);
1372 kmstest_set_vt_graphics_mode();
1373
1374 igt_display_require(&display, display.drm_fd);
1375 }
1376
1377 igt_subtest_group {
1378 enum pipe n;
for_each_pipe_static(n)1379 for_each_pipe_static(n) {
1380 errno = 0;
1381
1382 igt_fixture {
1383 igt_skip_on(n >= display.n_pipes);
1384 }
1385
1386 igt_subtest_f("pipe-%s-single-bo", kmstest_pipe_name(n))
1387 stress(&display, n, 1, DRM_MODE_CURSOR_BO, 20);
1388 igt_subtest_f("pipe-%s-single-move", kmstest_pipe_name(n))
1389 stress(&display, n, 1, DRM_MODE_CURSOR_MOVE, 20);
1390
1391 igt_subtest_f("pipe-%s-forked-bo", kmstest_pipe_name(n))
1392 stress(&display, n, ncpus, DRM_MODE_CURSOR_BO, 20);
1393 igt_subtest_f("pipe-%s-forked-move", kmstest_pipe_name(n))
1394 stress(&display, n, ncpus, DRM_MODE_CURSOR_MOVE, 20);
1395
1396 igt_subtest_f("pipe-%s-torture-bo", kmstest_pipe_name(n))
1397 stress(&display, n, -ncpus, DRM_MODE_CURSOR_BO, 20);
1398 igt_subtest_f("pipe-%s-torture-move", kmstest_pipe_name(n))
1399 stress(&display, n, -ncpus, DRM_MODE_CURSOR_MOVE, 20);
1400 }
1401 }
1402
1403 igt_subtest("all-pipes-single-bo")
1404 stress(&display, -1, 1, DRM_MODE_CURSOR_BO, 20);
1405 igt_subtest("all-pipes-single-move")
1406 stress(&display, -1, 1, DRM_MODE_CURSOR_MOVE, 20);
1407
1408 igt_subtest("all-pipes-forked-bo")
1409 stress(&display, -1, ncpus, DRM_MODE_CURSOR_BO, 20);
1410 igt_subtest("all-pipes-forked-move")
1411 stress(&display, -1, ncpus, DRM_MODE_CURSOR_MOVE, 20);
1412
1413 igt_subtest("all-pipes-torture-bo")
1414 stress(&display, -1, -ncpus, DRM_MODE_CURSOR_BO, 20);
1415 igt_subtest("all-pipes-torture-move")
1416 stress(&display, -1, -ncpus, DRM_MODE_CURSOR_MOVE, 20);
1417
1418 igt_subtest("nonblocking-modeset-vs-cursor-atomic")
1419 nonblocking_modeset_vs_cursor(&display, 1);
1420
1421 igt_subtest("long-nonblocking-modeset-vs-cursor-atomic")
1422 nonblocking_modeset_vs_cursor(&display, 16);
1423
1424 igt_subtest("2x-flip-vs-cursor-legacy")
1425 two_screens_flip_vs_cursor(&display, 8, false, false);
1426
1427 igt_subtest("2x-flip-vs-cursor-atomic")
1428 two_screens_flip_vs_cursor(&display, 8, false, true);
1429
1430 igt_subtest("2x-cursor-vs-flip-legacy")
1431 two_screens_cursor_vs_flip(&display, 8, false);
1432
1433 igt_subtest("2x-long-flip-vs-cursor-legacy")
1434 two_screens_flip_vs_cursor(&display, 150, false, false);
1435
1436 igt_subtest("2x-long-flip-vs-cursor-atomic")
1437 two_screens_flip_vs_cursor(&display, 150, false, true);
1438
1439 igt_subtest("2x-long-cursor-vs-flip-legacy")
1440 two_screens_cursor_vs_flip(&display, 50, false);
1441
1442 igt_subtest("2x-nonblocking-modeset-vs-cursor-atomic")
1443 two_screens_flip_vs_cursor(&display, 4, true, true);
1444
1445 igt_subtest("2x-cursor-vs-flip-atomic")
1446 two_screens_cursor_vs_flip(&display, 8, true);
1447
1448 igt_subtest("2x-long-nonblocking-modeset-vs-cursor-atomic")
1449 two_screens_flip_vs_cursor(&display, 15, true, true);
1450
1451 igt_subtest("2x-long-cursor-vs-flip-atomic")
1452 two_screens_cursor_vs_flip(&display, 50, true);
1453
1454 igt_subtest("flip-vs-cursor-crc-legacy")
1455 flip_vs_cursor_crc(&display, false);
1456
1457 igt_subtest("flip-vs-cursor-crc-atomic")
1458 flip_vs_cursor_crc(&display, true);
1459
1460 igt_subtest("flip-vs-cursor-busy-crc-legacy")
1461 flip_vs_cursor_busy_crc(&display, false);
1462
1463 igt_subtest("flip-vs-cursor-busy-crc-atomic")
1464 flip_vs_cursor_busy_crc(&display, true);
1465
1466 for (i = 0; i <= flip_test_last; i++) {
1467 const char *modes[flip_test_last+1] = {
1468 "legacy",
1469 "varying-size",
1470 "toggle",
1471 "atomic",
1472 "atomic-transitions",
1473 "atomic-transitions-varying-size"
1474 };
1475 const char *prefix = "short-";
1476
1477 switch (i) {
1478 case flip_test_legacy:
1479 case flip_test_varying_size:
1480 case flip_test_atomic:
1481 prefix = "basic-";
1482 break;
1483 default: break;
1484 }
1485
1486 igt_subtest_f("%sflip-before-cursor-%s", prefix, modes[i])
1487 basic_flip_cursor(&display, i, FLIP_BEFORE_CURSOR, 0);
1488
1489 if (!cursor_slowpath(i)) {
1490 igt_subtest_f("%sbusy-flip-before-cursor-%s", prefix, modes[i]) {
1491 igt_require_gem(display.drm_fd);
1492 basic_flip_cursor(&display, i, FLIP_BEFORE_CURSOR,
1493 BASIC_BUSY);
1494 }
1495 }
1496
1497 igt_subtest_f("%sflip-after-cursor-%s", prefix, modes[i])
1498 basic_flip_cursor(&display, i, FLIP_AFTER_CURSOR, 0);
1499
1500 igt_subtest_f("flip-vs-cursor-%s", modes[i])
1501 flip_vs_cursor(&display, i, 150);
1502 igt_subtest_f("cursor-vs-flip-%s", modes[i])
1503 cursor_vs_flip(&display, i, 50);
1504
1505 igt_subtest_f("cursorA-vs-flipA-%s", modes[i])
1506 flip(&display, 0, 0, 10, i);
1507
1508 igt_subtest_f("cursorA-vs-flipB-%s", modes[i])
1509 flip(&display, 0, 1, 10, i);
1510
1511 igt_subtest_f("cursorB-vs-flipA-%s", modes[i])
1512 flip(&display, 1, 0, 10, i);
1513
1514 igt_subtest_f("cursorB-vs-flipB-%s", modes[i])
1515 flip(&display, 1, 1, 10, i);
1516 }
1517
1518 igt_fixture {
1519 igt_display_fini(&display);
1520 }
1521 }
1522