xref: /aosp_15_r20/external/igt-gpu-tools/tests/kms_busy.c (revision d83cc019efdc2edc6c4b16e9034a3ceb8d35d77c)
1 /*
2  * Copyright © 2016 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 #include "igt.h"
25 
26 #include <sys/poll.h>
27 #include <signal.h>
28 #include <time.h>
29 
30 IGT_TEST_DESCRIPTION("Basic check of KMS ABI with busy framebuffers.");
31 
32 static igt_output_t *
set_fb_on_crtc(igt_display_t * dpy,int pipe,struct igt_fb * fb)33 set_fb_on_crtc(igt_display_t *dpy, int pipe, struct igt_fb *fb)
34 {
35 	drmModeModeInfoPtr mode;
36 	igt_plane_t *primary;
37 	igt_output_t *output;
38 
39 	output = igt_get_single_output_for_pipe(dpy, pipe);
40 
41 	igt_output_set_pipe(output, pipe);
42 	mode = igt_output_get_mode(output);
43 
44 	igt_create_pattern_fb(dpy->drm_fd, mode->hdisplay, mode->vdisplay,
45 			      DRM_FORMAT_XRGB8888,
46 			      LOCAL_I915_FORMAT_MOD_X_TILED, fb);
47 
48 	primary = igt_output_get_plane_type(output, DRM_PLANE_TYPE_PRIMARY);
49 	igt_plane_set_fb(primary, fb);
50 
51 	return output;
52 }
53 
do_cleanup_display(igt_display_t * dpy)54 static void do_cleanup_display(igt_display_t *dpy)
55 {
56 	enum pipe pipe;
57 	igt_output_t *output;
58 	igt_plane_t *plane;
59 
60 	for_each_pipe(dpy, pipe)
61 		for_each_plane_on_pipe(dpy, pipe, plane)
62 			igt_plane_set_fb(plane, NULL);
63 
64 	for_each_connected_output(dpy, output)
65 		igt_output_set_pipe(output, PIPE_NONE);
66 
67 	igt_display_commit2(dpy, dpy->is_atomic ? COMMIT_ATOMIC : COMMIT_LEGACY);
68 }
69 
flip_to_fb(igt_display_t * dpy,int pipe,igt_output_t * output,struct igt_fb * fb,unsigned ring,const char * name,bool modeset)70 static void flip_to_fb(igt_display_t *dpy, int pipe,
71 		       igt_output_t *output,
72 		       struct igt_fb *fb, unsigned ring,
73 		       const char *name, bool modeset)
74 {
75 	struct pollfd pfd = { .fd = dpy->drm_fd, .events = POLLIN };
76 	const int timeout = modeset ? 8500 : 100;
77 	struct drm_event_vblank ev;
78 
79 	igt_spin_t *t = igt_spin_new(dpy->drm_fd,
80 				     .engine = ring,
81 				     .dependency = fb->gem_handle);
82 
83 	if (modeset) {
84 		/*
85 		 * We want to check that a modeset actually waits for the
86 		 * spin batch to complete, but we keep a bigger timeout for
87 		 * disable than required for flipping.
88 		 *
89 		 * As a result, the GPU reset code may kick in, which we neuter
90 		 * here to be sure there's no premature completion.
91 		 */
92 		igt_set_module_param_int("enable_hangcheck", 0);
93 	}
94 
95 	igt_fork(child, 1) {
96 		igt_assert(gem_bo_busy(dpy->drm_fd, fb->gem_handle));
97 		if (!modeset)
98 			do_or_die(drmModePageFlip(dpy->drm_fd,
99 						  dpy->pipes[pipe].crtc_id, fb->fb_id,
100 						  DRM_MODE_PAGE_FLIP_EVENT, fb));
101 		else {
102 			igt_plane_set_fb(igt_output_get_plane_type(output, DRM_PLANE_TYPE_PRIMARY), fb);
103 			igt_output_set_pipe(output, PIPE_NONE);
104 			igt_display_commit_atomic(dpy,
105 						  DRM_MODE_ATOMIC_NONBLOCK |
106 						  DRM_MODE_PAGE_FLIP_EVENT |
107 						  DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
108 		}
109 
110 		igt_assert_f(poll(&pfd, 1, timeout) == 0,
111 			     "flip completed whilst %s was busy [%d]\n",
112 			     name, gem_bo_busy(dpy->drm_fd, fb->gem_handle));
113 		igt_assert(gem_bo_busy(dpy->drm_fd, fb->gem_handle));
114 	}
115 
116 	igt_waitchildren_timeout(5 * timeout,
117 				 "flip blocked waiting for busy bo\n");
118 	igt_spin_end(t);
119 
120 	igt_assert(read(dpy->drm_fd, &ev, sizeof(ev)) == sizeof(ev));
121 	igt_assert(poll(&pfd, 1, 0) == 0);
122 
123 	if (modeset) {
124 		gem_quiescent_gpu(dpy->drm_fd);
125 		igt_set_module_param_int("enable_hangcheck", 1);
126 
127 		/* Clear old mode blob. */
128 		igt_pipe_refresh(dpy, pipe, true);
129 
130 		igt_output_set_pipe(output, pipe);
131 		igt_display_commit2(dpy, COMMIT_ATOMIC);
132 	}
133 
134 	igt_spin_free(dpy->drm_fd, t);
135 }
136 
test_flip(igt_display_t * dpy,unsigned ring,int pipe,bool modeset)137 static void test_flip(igt_display_t *dpy, unsigned ring, int pipe, bool modeset)
138 {
139 	struct igt_fb fb[2];
140 	int warmup[] = { 0, 1, 0, -1 };
141 	igt_output_t *output;
142 
143 	if (modeset)
144 		igt_require(dpy->is_atomic);
145 
146 	output = set_fb_on_crtc(dpy, pipe, &fb[0]);
147 	igt_display_commit2(dpy, COMMIT_LEGACY);
148 
149 	igt_create_pattern_fb(dpy->drm_fd,
150 			      fb[0].width, fb[0].height,
151 			      DRM_FORMAT_XRGB8888,
152 			      LOCAL_I915_FORMAT_MOD_X_TILED,
153 			      &fb[1]);
154 
155 	/* Bind both fb to the display (such that they are ready for future
156 	 * flips without stalling for the bind) leaving fb[0] as bound.
157 	 */
158 	for (int i = 0; warmup[i] != -1; i++) {
159 		struct drm_event_vblank ev;
160 
161 		do_or_die(drmModePageFlip(dpy->drm_fd,
162 					  dpy->pipes[pipe].crtc_id,
163 					  fb[warmup[i]].fb_id,
164 					  DRM_MODE_PAGE_FLIP_EVENT,
165 					  &fb[warmup[i]]));
166 		igt_assert(read(dpy->drm_fd, &ev, sizeof(ev)) == sizeof(ev));
167 	}
168 
169 	/* Make the frontbuffer busy and try to flip to itself */
170 	flip_to_fb(dpy, pipe, output, &fb[0], ring, "fb[0]", modeset);
171 
172 	/* Repeat for flip to second buffer */
173 	flip_to_fb(dpy, pipe, output, &fb[1], ring, "fb[1]", modeset);
174 
175 	do_cleanup_display(dpy);
176 	igt_remove_fb(dpy->drm_fd, &fb[1]);
177 	igt_remove_fb(dpy->drm_fd, &fb[0]);
178 }
179 
test_atomic_commit_hang(igt_display_t * dpy,igt_plane_t * primary,struct igt_fb * busy_fb,unsigned ring)180 static void test_atomic_commit_hang(igt_display_t *dpy, igt_plane_t *primary,
181 				    struct igt_fb *busy_fb, unsigned ring)
182 {
183 	igt_spin_t *t = igt_spin_new(dpy->drm_fd,
184 				     .engine = ring,
185 				     .dependency = busy_fb->gem_handle);
186 	struct pollfd pfd = { .fd = dpy->drm_fd, .events = POLLIN };
187 	unsigned flags = 0;
188 	struct drm_event_vblank ev;
189 
190 	flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
191 	flags |= DRM_MODE_ATOMIC_NONBLOCK;
192 	flags |= DRM_MODE_PAGE_FLIP_EVENT;
193 
194 	igt_display_commit_atomic(dpy, flags, NULL);
195 
196 	igt_fork(child, 1) {
197 		/*
198 		 * bit of a hack, just set atomic commit to NULL fb to make sure
199 		 * that we don't wait for the new update to complete.
200 		 */
201 		igt_plane_set_fb(primary, NULL);
202 		igt_display_commit_atomic(dpy, 0, NULL);
203 
204 		igt_assert_f(poll(&pfd, 1, 1) > 0,
205 			    "nonblocking update completed whilst fb[%d] was still busy [%d]\n",
206 			    busy_fb->fb_id, gem_bo_busy(dpy->drm_fd, busy_fb->gem_handle));
207 	}
208 
209 	igt_waitchildren();
210 
211 	igt_assert(read(dpy->drm_fd, &ev, sizeof(ev)) == sizeof(ev));
212 
213 	igt_spin_end(t);
214 }
215 
test_hang(igt_display_t * dpy,unsigned ring,enum pipe pipe,bool modeset,bool hang_newfb)216 static void test_hang(igt_display_t *dpy, unsigned ring,
217 		      enum pipe pipe, bool modeset, bool hang_newfb)
218 {
219 	struct igt_fb fb[2];
220 	igt_output_t *output;
221 	igt_plane_t *primary;
222 
223 	output = set_fb_on_crtc(dpy, pipe, &fb[0]);
224 	igt_display_commit2(dpy, COMMIT_ATOMIC);
225 	primary = igt_output_get_plane_type(output, DRM_PLANE_TYPE_PRIMARY);
226 
227 	igt_create_pattern_fb(dpy->drm_fd,
228 			      fb[0].width, fb[0].height,
229 			      DRM_FORMAT_XRGB8888,
230 			      LOCAL_I915_FORMAT_MOD_X_TILED,
231 			      &fb[1]);
232 
233 	if (modeset) {
234 		/* Test modeset disable with hang */
235 		igt_output_set_pipe(output, PIPE_NONE);
236 		igt_plane_set_fb(primary, &fb[1]);
237 		test_atomic_commit_hang(dpy, primary, &fb[hang_newfb], ring);
238 
239 		/* Test modeset enable with hang */
240 		igt_plane_set_fb(primary, &fb[0]);
241 		igt_output_set_pipe(output, pipe);
242 		test_atomic_commit_hang(dpy, primary, &fb[!hang_newfb], ring);
243 	} else {
244 		/*
245 		 * Test what happens with a single hanging pageflip.
246 		 * This always completes early, because we have some
247 		 * timeouts taking care of it.
248 		 */
249 		igt_plane_set_fb(primary, &fb[1]);
250 		test_atomic_commit_hang(dpy, primary, &fb[hang_newfb], ring);
251 	}
252 
253 	do_cleanup_display(dpy);
254 	igt_remove_fb(dpy->drm_fd, &fb[1]);
255 	igt_remove_fb(dpy->drm_fd, &fb[0]);
256 }
257 
test_pageflip_modeset_hang(igt_display_t * dpy,unsigned ring,enum pipe pipe)258 static void test_pageflip_modeset_hang(igt_display_t *dpy,
259 				       unsigned ring, enum pipe pipe)
260 {
261 	struct igt_fb fb;
262 	struct drm_event_vblank ev;
263 	igt_output_t *output;
264 	igt_plane_t *primary;
265 	igt_spin_t *t;
266 
267 	output = set_fb_on_crtc(dpy, pipe, &fb);
268 	primary = igt_output_get_plane_type(output, DRM_PLANE_TYPE_PRIMARY);
269 
270 	igt_display_commit2(dpy, dpy->is_atomic ? COMMIT_ATOMIC : COMMIT_LEGACY);
271 
272 	t = igt_spin_new(dpy->drm_fd,
273 			 .engine = ring,
274 			 .dependency = fb.gem_handle);
275 
276 	do_or_die(drmModePageFlip(dpy->drm_fd, dpy->pipes[pipe].crtc_id, fb.fb_id, DRM_MODE_PAGE_FLIP_EVENT, &fb));
277 
278 	/* Kill crtc with hung fb */
279 	igt_plane_set_fb(primary, NULL);
280 	igt_output_set_pipe(output, PIPE_NONE);
281 	igt_display_commit2(dpy, dpy->is_atomic ? COMMIT_ATOMIC : COMMIT_LEGACY);
282 
283 	igt_assert(read(dpy->drm_fd, &ev, sizeof(ev)) == sizeof(ev));
284 
285 	igt_spin_end(t);
286 
287 	igt_remove_fb(dpy->drm_fd, &fb);
288 }
289 
290 igt_main
291 {
292 	igt_display_t display = { .drm_fd = -1, .n_pipes = IGT_MAX_PIPES };
293 	/* we only test on render */
294 	const struct intel_execution_engine *e = &intel_execution_engines[1];
295 	enum pipe n;
296 
297 	igt_skip_on_simulation();
298 
299 	igt_fixture {
300 		int fd = drm_open_driver_master(DRIVER_INTEL);
301 
302 		igt_require_gem(fd);
303 		gem_require_mmap_wc(fd);
304 
305 		kmstest_set_vt_graphics_mode();
306 		igt_display_require(&display, fd);
307 	}
308 
309 	/* XXX Extend to cover atomic rendering tests to all planes + legacy */
310 
for_each_pipe_static(n)311 	for_each_pipe_static(n) igt_subtest_group {
312 		igt_hang_t hang;
313 
314 		errno = 0;
315 
316 		igt_fixture {
317 			igt_display_require_output_on_pipe(&display, n);
318 		}
319 
320 		igt_subtest_f("basic-flip-%s",
321 			kmstest_pipe_name(n)) {
322 			igt_require(gem_has_ring(display.drm_fd,
323 						e->exec_id | e->flags));
324 
325 			test_flip(&display, e->exec_id | e->flags, n, false);
326 		}
327 		igt_subtest_f("basic-modeset-%s",
328 			kmstest_pipe_name(n)) {
329 			igt_require(gem_has_ring(display.drm_fd,
330 						e->exec_id | e->flags));
331 
332 			test_flip(&display, e->exec_id | e->flags, n, true);
333 		}
334 
335 		igt_fixture {
336 			igt_require(gem_has_ring(display.drm_fd,
337 						e->exec_id | e->flags));
338 
339 			hang = igt_allow_hang(display.drm_fd, 0, 0);
340 		}
341 
342 		igt_subtest_f("extended-pageflip-modeset-hang-oldfb-%s-%s",
343 				e->name, kmstest_pipe_name(n)) {
344 			igt_require(gem_has_ring(display.drm_fd,
345 						e->exec_id | e->flags));
346 
347 			test_pageflip_modeset_hang(&display, e->exec_id | e->flags, n);
348 		}
349 
350 		igt_fixture
351 			igt_require(display.is_atomic);
352 
353 		igt_subtest_f("extended-pageflip-hang-oldfb-%s-%s",
354 				e->name, kmstest_pipe_name(n))
355 			test_hang(&display, e->exec_id | e->flags, n, false, false);
356 
357 		igt_subtest_f("extended-pageflip-hang-newfb-%s-%s",
358 				e->name, kmstest_pipe_name(n))
359 			test_hang(&display, e->exec_id | e->flags, n, false, true);
360 
361 		igt_subtest_f("extended-modeset-hang-oldfb-%s-%s",
362 				e->name, kmstest_pipe_name(n))
363 			test_hang(&display, e->exec_id | e->flags, n, true, false);
364 
365 		igt_subtest_f("extended-modeset-hang-newfb-%s-%s",
366 				e->name, kmstest_pipe_name(n))
367 			test_hang(&display, e->exec_id | e->flags, n, true, true);
368 
369 		igt_subtest_f("extended-modeset-hang-oldfb-with-reset-%s-%s",
370 				e->name, kmstest_pipe_name(n)) {
371 			igt_set_module_param_int("force_reset_modeset_test", 1);
372 
373 			test_hang(&display, e->exec_id | e->flags, n, true, false);
374 
375 			igt_set_module_param_int("force_reset_modeset_test", 0);
376 		}
377 
378 		igt_subtest_f("extended-modeset-hang-newfb-with-reset-%s-%s",
379 				e->name, kmstest_pipe_name(n)) {
380 			igt_set_module_param_int("force_reset_modeset_test", 1);
381 
382 			test_hang(&display, e->exec_id | e->flags, n, true, true);
383 
384 			igt_set_module_param_int("force_reset_modeset_test", 0);
385 		}
386 
387 		igt_fixture {
388 			igt_disallow_hang(display.drm_fd, hang);
389 		}
390 	}
391 
392 	igt_fixture {
393 		igt_display_fini(&display);
394 	}
395 }
396