1 /*
2  * Copyright © 2012 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  * Authors:
24  *    Ben Widawsky <ben@bwidawsk.net>
25  *    Jeff McGee <jeff.mcgee@intel.com>
26  *
27  */
28 
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <unistd.h>
33 #include <fcntl.h>
34 #include <signal.h>
35 #include <errno.h>
36 #include <time.h>
37 #include <sys/wait.h>
38 
39 #include "igt.h"
40 #include "igt_dummyload.h"
41 #include "igt_sysfs.h"
42 
43 IGT_TEST_DESCRIPTION("Render P-States tests - verify GPU frequency changes");
44 
45 static int drm_fd;
46 
47 enum {
48 	CUR,
49 	MIN,
50 	MAX,
51 	RP0,
52 	RP1,
53 	RPn,
54 	BOOST,
55 	NUMFREQ
56 };
57 
58 static int origfreqs[NUMFREQ];
59 
60 struct sysfs_file {
61 	const char *name;
62 	const char *mode;
63 	FILE *filp;
64 } sysfs_files[] = {
65 	{ "cur", "r", NULL },
66 	{ "min", "rb+", NULL },
67 	{ "max", "rb+", NULL },
68 	{ "RP0", "r", NULL },
69 	{ "RP1", "r", NULL },
70 	{ "RPn", "r", NULL },
71 	{ "boost", "rb+", NULL },
72 	{ NULL, NULL, NULL }
73 };
74 
readval(FILE * filp)75 static int readval(FILE *filp)
76 {
77 	int val;
78 	int scanned;
79 
80 	rewind(filp);
81 	scanned = fscanf(filp, "%d", &val);
82 	igt_assert_eq(scanned, 1);
83 
84 	return val;
85 }
86 
read_freqs(int * freqs)87 static void read_freqs(int *freqs)
88 {
89 	int i;
90 
91 	for (i = 0; i < NUMFREQ; i++)
92 		freqs[i] = readval(sysfs_files[i].filp);
93 }
94 
nsleep(unsigned long ns)95 static void nsleep(unsigned long ns)
96 {
97 	struct timespec ts;
98 	int ret;
99 
100 	ts.tv_sec = 0;
101 	ts.tv_nsec = ns;
102 	do {
103 		struct timespec rem;
104 
105 		ret = nanosleep(&ts, &rem);
106 		igt_assert(ret == 0 || errno == EINTR);
107 		ts = rem;
108 	} while (ret && errno == EINTR);
109 }
110 
wait_freq_settle(void)111 static void wait_freq_settle(void)
112 {
113 	int timeout = 10;
114 
115 	while (1) {
116 		int freqs[NUMFREQ];
117 
118 		read_freqs(freqs);
119 		if (freqs[CUR] >= freqs[MIN] && freqs[CUR] <= freqs[MAX])
120 			break;
121 		nsleep(1000000);
122 		if (!timeout--)
123 			break;
124 	}
125 }
126 
do_writeval(FILE * filp,int val,int lerrno,bool readback_check)127 static int do_writeval(FILE *filp, int val, int lerrno, bool readback_check)
128 {
129 	int ret, orig;
130 
131 	orig = readval(filp);
132 	rewind(filp);
133 	ret = fprintf(filp, "%d", val);
134 
135 	if (lerrno) {
136 		/* Expecting specific error */
137 		igt_assert(ret == EOF && errno == lerrno);
138 		if (readback_check)
139 			igt_assert_eq(readval(filp), orig);
140 	} else {
141 		/* Expecting no error */
142 		igt_assert_lt(0, ret);
143 		wait_freq_settle();
144 		if (readback_check)
145 			igt_assert_eq(readval(filp), val);
146 	}
147 
148 	return ret;
149 }
150 #define writeval(filp, val) do_writeval(filp, val, 0, true)
151 #define writeval_inval(filp, val) do_writeval(filp, val, EINVAL, true)
152 #define writeval_nocheck(filp, val) do_writeval(filp, val, 0, false)
153 
check_freq_constraints(const int * freqs)154 static void check_freq_constraints(const int *freqs)
155 {
156 	igt_assert_lte(freqs[MIN], freqs[MAX]);
157 	igt_assert_lte(freqs[CUR], freqs[MAX]);
158 	igt_assert_lte(freqs[RPn], freqs[CUR]);
159 	igt_assert_lte(freqs[RPn], freqs[MIN]);
160 	igt_assert_lte(freqs[MAX], freqs[RP0]);
161 	igt_assert_lte(freqs[RP1], freqs[RP0]);
162 	igt_assert_lte(freqs[RPn], freqs[RP1]);
163 	igt_assert_neq(freqs[RP0], 0);
164 	igt_assert_neq(freqs[RP1], 0);
165 }
166 
dump(const int * freqs)167 static void dump(const int *freqs)
168 {
169 	int i;
170 
171 	igt_debug("gt freq (MHz):");
172 	for (i = 0; i < NUMFREQ; i++)
173 		igt_debug("  %s=%d", sysfs_files[i].name, freqs[i]);
174 
175 	igt_debug("\n");
176 }
177 
178 enum load {
179 	LOW = 0,
180 	HIGH
181 };
182 
183 static struct load_helper {
184 	int link;
185 	enum load load;
186 	bool exit;
187 	bool signal;
188 	struct igt_helper_process igt_proc;
189 } lh;
190 
load_helper_signal_handler(int sig)191 static void load_helper_signal_handler(int sig)
192 {
193 	if (sig == SIGUSR2) {
194 		lh.load = !lh.load;
195 		lh.signal = true;
196 		igt_debug("Switching background load to %s\n", lh.load ? "high" : "low");
197 	} else
198 		lh.exit = true;
199 }
200 
load_helper_sync(void)201 static void load_helper_sync(void)
202 {
203 	bool dummy;
204 
205 	igt_assert_eq(read(lh.link, &dummy, sizeof(dummy)), sizeof(dummy));
206 }
207 
208 #define LOAD_HELPER_PAUSE_USEC 500
209 #define LOAD_HELPER_BO_SIZE (16*1024*1024)
load_helper_set_load(enum load load)210 static void load_helper_set_load(enum load load)
211 {
212 	igt_assert(lh.igt_proc.running);
213 
214 	if (lh.load == load)
215 		return;
216 
217 	lh.load = load;
218 	kill(lh.igt_proc.pid, SIGUSR2);
219 
220 	/* wait for load-helper to switch */
221 	load_helper_sync();
222 }
223 
load_helper_run(enum load load)224 static void load_helper_run(enum load load)
225 {
226 	int link[2];
227 
228 	/*
229 	 * FIXME fork helpers won't get cleaned up when started from within a
230 	 * subtest, so handle the case where it sticks around a bit too long.
231 	 */
232 	if (lh.igt_proc.running) {
233 		load_helper_set_load(load);
234 		return;
235 	}
236 
237 	igt_require_gem(drm_fd);
238 
239 	lh.exit = false;
240 	lh.load = load;
241 	lh.signal = true;
242 
243 	pipe(link);
244 	lh.link = link[1];
245 
246 	igt_fork_helper(&lh.igt_proc) {
247 		igt_spin_t *spin[2] = {};
248 		bool prev_load;
249 		uint32_t handle;
250 
251 		signal(SIGUSR1, load_helper_signal_handler);
252 		signal(SIGUSR2, load_helper_signal_handler);
253 
254 		igt_debug("Applying %s load...\n", lh.load ? "high" : "low");
255 
256 		prev_load = lh.load == HIGH;
257 		spin[0] = __igt_spin_new(drm_fd);
258 		if (prev_load)
259 			spin[1] = __igt_spin_new(drm_fd);
260 		prev_load = !prev_load; /* send the initial signal */
261 		while (!lh.exit) {
262 			bool high_load;
263 
264 			handle = spin[0]->handle;
265 			igt_spin_end(spin[0]);
266 			while (gem_bo_busy(drm_fd, handle))
267 				usleep(100);
268 
269 			igt_spin_free(drm_fd, spin[0]);
270 			usleep(100);
271 
272 			high_load = lh.load == HIGH;
273 			if (!high_load && spin[1]) {
274 				igt_spin_free(drm_fd, spin[1]);
275 				spin[1] = NULL;
276 			} else {
277 				spin[0] = spin[1];
278 			}
279 			spin[high_load] = __igt_spin_new(drm_fd);
280 
281 			if (lh.signal && high_load != prev_load) {
282 				write(lh.link, &lh.signal, sizeof(lh.signal));
283 				lh.signal = false;
284 			}
285 			prev_load = high_load;
286 		}
287 
288 		handle = spin[0]->handle;
289 		igt_spin_end(spin[0]);
290 
291 		if (spin[1]) {
292 			handle = spin[1]->handle;
293 			igt_spin_end(spin[1]);
294 		}
295 
296 		/* Wait for completion without boosting */
297 		usleep(1000);
298 		while (gem_bo_busy(drm_fd, handle))
299 			usleep(1000);
300 
301 		/*
302 		 * Idle/boost logic is tied with request retirement.
303 		 * Speed up detection of idle state and ensure deboost
304 		 * after removing load.
305 		 */
306 		igt_drop_caches_set(drm_fd, DROP_RETIRE);
307 
308 		igt_spin_free(drm_fd, spin[1]);
309 		igt_spin_free(drm_fd, spin[0]);
310 	}
311 
312 	close(lh.link);
313 	lh.link = link[0];
314 
315 	/* wait for our helper to complete its first round */
316 	load_helper_sync();
317 }
318 
load_helper_stop(void)319 static void load_helper_stop(void)
320 {
321 	kill(lh.igt_proc.pid, SIGUSR1);
322 	igt_assert(igt_wait_helper(&lh.igt_proc) == 0);
323 }
324 
do_load_gpu(void)325 static void do_load_gpu(void)
326 {
327 	load_helper_run(LOW);
328 	nsleep(10000000);
329 	load_helper_stop();
330 }
331 
332 /* Return a frequency rounded by HW to the nearest supported value */
get_hw_rounded_freq(int target)333 static int get_hw_rounded_freq(int target)
334 {
335 	int freqs[NUMFREQ];
336 	int old_freq;
337 	int idx;
338 	int ret;
339 
340 	read_freqs(freqs);
341 
342 	if (freqs[MIN] > target)
343 		idx = MIN;
344 	else
345 		idx = MAX;
346 
347 	old_freq = freqs[idx];
348 	writeval_nocheck(sysfs_files[idx].filp, target);
349 	read_freqs(freqs);
350 	ret = freqs[idx];
351 	writeval_nocheck(sysfs_files[idx].filp, old_freq);
352 
353 	return ret;
354 }
355 
356 /*
357  * Modify softlimit MIN and MAX freqs to valid and invalid levels. Depending
358  * on subtest run different check after each modification.
359  */
min_max_config(void (* check)(void),bool load_gpu)360 static void min_max_config(void (*check)(void), bool load_gpu)
361 {
362 	int fmid = (origfreqs[RPn] + origfreqs[RP0]) / 2;
363 
364 	/*
365 	 * hw (and so kernel) rounds to the nearest value supported by
366 	 * the given platform.
367 	 */
368 	fmid = get_hw_rounded_freq(fmid);
369 
370 	igt_debug("\nCheck original min and max...\n");
371 	if (load_gpu)
372 		do_load_gpu();
373 	check();
374 
375 	igt_debug("\nSet min=RPn and max=RP0...\n");
376 	writeval(sysfs_files[MIN].filp, origfreqs[RPn]);
377 	writeval(sysfs_files[MAX].filp, origfreqs[RP0]);
378 	if (load_gpu)
379 		do_load_gpu();
380 	check();
381 
382 	igt_debug("\nIncrease min to midpoint...\n");
383 	writeval(sysfs_files[MIN].filp, fmid);
384 	if (load_gpu)
385 		do_load_gpu();
386 	check();
387 
388 	igt_debug("\nIncrease min to RP0...\n");
389 	writeval(sysfs_files[MIN].filp, origfreqs[RP0]);
390 	if (load_gpu)
391 		do_load_gpu();
392 	check();
393 
394 	igt_debug("\nIncrease min above RP0 (invalid)...\n");
395 	writeval_inval(sysfs_files[MIN].filp, origfreqs[RP0] + 1000);
396 	check();
397 
398 	igt_debug("\nDecrease max to RPn (invalid)...\n");
399 	writeval_inval(sysfs_files[MAX].filp, origfreqs[RPn]);
400 	check();
401 
402 	igt_debug("\nDecrease min to midpoint...\n");
403 	writeval(sysfs_files[MIN].filp, fmid);
404 	if (load_gpu)
405 		do_load_gpu();
406 	check();
407 
408 	igt_debug("\nDecrease min to RPn...\n");
409 	writeval(sysfs_files[MIN].filp, origfreqs[RPn]);
410 	if (load_gpu)
411 		do_load_gpu();
412 	check();
413 
414 	igt_debug("\nDecrease min below RPn (invalid)...\n");
415 	writeval_inval(sysfs_files[MIN].filp, 0);
416 	check();
417 
418 	igt_debug("\nDecrease max to midpoint...\n");
419 	writeval(sysfs_files[MAX].filp, fmid);
420 	check();
421 
422 	igt_debug("\nDecrease max to RPn...\n");
423 	writeval(sysfs_files[MAX].filp, origfreqs[RPn]);
424 	check();
425 
426 	igt_debug("\nDecrease max below RPn (invalid)...\n");
427 	writeval_inval(sysfs_files[MAX].filp, 0);
428 	check();
429 
430 	igt_debug("\nIncrease min to RP0 (invalid)...\n");
431 	writeval_inval(sysfs_files[MIN].filp, origfreqs[RP0]);
432 	check();
433 
434 	igt_debug("\nIncrease max to midpoint...\n");
435 	writeval(sysfs_files[MAX].filp, fmid);
436 	check();
437 
438 	igt_debug("\nIncrease max to RP0...\n");
439 	writeval(sysfs_files[MAX].filp, origfreqs[RP0]);
440 	check();
441 
442 	igt_debug("\nIncrease max above RP0 (invalid)...\n");
443 	writeval_inval(sysfs_files[MAX].filp, origfreqs[RP0] + 1000);
444 	check();
445 
446 	writeval(sysfs_files[MIN].filp, origfreqs[MIN]);
447 	writeval(sysfs_files[MAX].filp, origfreqs[MAX]);
448 }
449 
basic_check(void)450 static void basic_check(void)
451 {
452 	int freqs[NUMFREQ];
453 
454 	read_freqs(freqs);
455 	dump(freqs);
456 	check_freq_constraints(freqs);
457 }
458 
459 #define IDLE_WAIT_TIMESTEP_MSEC 250
460 #define IDLE_WAIT_TIMEOUT_MSEC 2500
idle_check(void)461 static void idle_check(void)
462 {
463 	int freqs[NUMFREQ];
464 	int wait = 0;
465 
466 	/* Monitor frequencies until cur settles down to min, which should
467 	 * happen within the allotted time */
468 	do {
469 		read_freqs(freqs);
470 		dump(freqs);
471 		check_freq_constraints(freqs);
472 		if (freqs[CUR] == freqs[RPn])
473 			break;
474 		usleep(1000 * IDLE_WAIT_TIMESTEP_MSEC);
475 		wait += IDLE_WAIT_TIMESTEP_MSEC;
476 	} while (wait < IDLE_WAIT_TIMEOUT_MSEC);
477 
478 	igt_debugfs_dump(drm_fd, "i915_rps_boost_info");
479 	igt_assert_eq(freqs[CUR], freqs[RPn]);
480 	igt_debug("Required %d msec to reach cur=idle\n", wait);
481 }
482 
483 #define LOADED_WAIT_TIMESTEP_MSEC 100
484 #define LOADED_WAIT_TIMEOUT_MSEC 3000
loaded_check(void)485 static void loaded_check(void)
486 {
487 	int freqs[NUMFREQ];
488 	int wait = 0;
489 
490 	/* Monitor frequencies until cur increases to max, which should
491 	 * happen within the allotted time */
492 	do {
493 		read_freqs(freqs);
494 		dump(freqs);
495 		check_freq_constraints(freqs);
496 		if (freqs[CUR] >= freqs[MAX])
497 			break;
498 		usleep(1000 * LOADED_WAIT_TIMESTEP_MSEC);
499 		wait += LOADED_WAIT_TIMESTEP_MSEC;
500 	} while (wait < LOADED_WAIT_TIMEOUT_MSEC);
501 
502 	igt_debugfs_dump(drm_fd, "i915_rps_boost_info");
503 	igt_assert_lte(freqs[MAX], freqs[CUR]);
504 	igt_debug("Required %d msec to reach cur=max\n", wait);
505 }
506 
507 #define STABILIZE_WAIT_TIMESTEP_MSEC 250
508 #define STABILIZE_WAIT_TIMEOUT_MSEC 15000
stabilize_check(int * out)509 static void stabilize_check(int *out)
510 {
511 	int freqs[NUMFREQ];
512 	int wait = 0;
513 
514 	/* Monitor frequencies until HW will stabilize cur frequency.
515 	 * It should happen within allotted time */
516 	read_freqs(freqs);
517 	dump(freqs);
518 	usleep(1000 * STABILIZE_WAIT_TIMESTEP_MSEC);
519 	do {
520 		read_freqs(out);
521 		dump(out);
522 
523 		if (memcmp(freqs, out, sizeof(freqs)) == 0)
524 			break;
525 
526 		memcpy(freqs, out, sizeof(freqs));
527 		wait += STABILIZE_WAIT_TIMESTEP_MSEC;
528 	} while (wait < STABILIZE_WAIT_TIMEOUT_MSEC);
529 
530 	igt_debugfs_dump(drm_fd, "i915_rps_boost_info");
531 	igt_debug("Waited %d msec to stabilize cur\n", wait);
532 }
533 
resubmit_batch(int fd,uint32_t handle,int count)534 static void resubmit_batch(int fd, uint32_t handle, int count)
535 {
536 	struct drm_i915_gem_exec_object2 obj = {
537 		.handle = handle
538 	};
539 	struct drm_i915_gem_execbuffer2 eb = {
540 		.buffer_count = 1,
541 		.buffers_ptr = to_user_pointer(&obj),
542 	};
543 	while (count--)
544 		gem_execbuf(fd, &eb);
545 }
546 
boost_freq(int fd,int * boost_freqs)547 static void boost_freq(int fd, int *boost_freqs)
548 {
549 	int64_t timeout = 1;
550 	igt_spin_t *load;
551 
552 	load = igt_spin_new(fd);
553 	resubmit_batch(fd, load->handle, 16);
554 
555 	/* Waiting will grant us a boost to maximum */
556 	gem_wait(fd, load->handle, &timeout);
557 
558 	read_freqs(boost_freqs);
559 	dump(boost_freqs);
560 
561 	/* Avoid downlocking till boost request is pending */
562 	igt_spin_end(load);
563 	gem_sync(fd, load->handle);
564 	igt_spin_free(fd, load);
565 }
566 
waitboost(int fd,bool reset)567 static void waitboost(int fd, bool reset)
568 {
569 	int pre_freqs[NUMFREQ];
570 	int boost_freqs[NUMFREQ];
571 	int post_freqs[NUMFREQ];
572 	int fmid = (origfreqs[RPn] + origfreqs[RP0]) / 2;
573 	fmid = get_hw_rounded_freq(fmid);
574 
575 	load_helper_run(LOW);
576 
577 	igt_debug("Apply low load...\n");
578 	sleep(1);
579 	stabilize_check(pre_freqs);
580 
581 	if (reset) {
582 		igt_debug("Reset gpu...\n");
583 		igt_force_gpu_reset(fd);
584 		sleep(1);
585 	}
586 
587 	/* Set max freq to less than boost freq */
588 	writeval(sysfs_files[MAX].filp, fmid);
589 
590 	/* When we wait upon the GPU, we want to temporarily boost it
591 	 * to maximum.
592 	 */
593 	boost_freq(fd, boost_freqs);
594 
595 	/* Set max freq to original softmax */
596 	writeval(sysfs_files[MAX].filp, origfreqs[MAX]);
597 
598 	igt_debug("Apply low load again...\n");
599 	sleep(1);
600 	stabilize_check(post_freqs);
601 
602 	igt_debug("Removing load...\n");
603 	load_helper_stop();
604 	idle_check();
605 
606 	igt_assert_lt(pre_freqs[CUR], pre_freqs[MAX]);
607 	igt_assert_eq(boost_freqs[CUR], boost_freqs[BOOST]);
608 	igt_assert_lt(post_freqs[CUR], post_freqs[MAX]);
609 }
610 
pm_rps_exit_handler(int sig)611 static void pm_rps_exit_handler(int sig)
612 {
613 	if (origfreqs[MIN] > readval(sysfs_files[MAX].filp)) {
614 		writeval(sysfs_files[MAX].filp, origfreqs[MAX]);
615 		writeval(sysfs_files[MIN].filp, origfreqs[MIN]);
616 	} else {
617 		writeval(sysfs_files[MIN].filp, origfreqs[MIN]);
618 		writeval(sysfs_files[MAX].filp, origfreqs[MAX]);
619 	}
620 
621 	if (lh.igt_proc.running)
622 		load_helper_stop();
623 
624 	close(drm_fd);
625 }
626 
627 igt_main
628 {
629 	igt_skip_on_simulation();
630 
631 	igt_fixture {
632 		struct sysfs_file *sysfs_file = sysfs_files;
633 		char sysfs_path[80];
634 		int ret;
635 
636 		/* Use drm_open_driver to verify device existence */
637 		drm_fd = drm_open_driver(DRIVER_INTEL);
638 		igt_require_gem(drm_fd);
639 		igt_require(gem_can_store_dword(drm_fd, 0));
640 		igt_assert(igt_sysfs_path(drm_fd, sysfs_path,
641 					  sizeof(sysfs_path)));
642 
643 		do {
644 			int val = -1;
645 			char *path;
646 
647 			ret = asprintf(&path, "%s/gt_%s_freq_mhz",
648 				       sysfs_path, sysfs_file->name);
649 			igt_assert(ret != -1);
650 			sysfs_file->filp = fopen(path, sysfs_file->mode);
651 			igt_require(sysfs_file->filp);
652 			setbuf(sysfs_file->filp, NULL);
653 
654 			val = readval(sysfs_file->filp);
655 			igt_assert(val >= 0);
656 			sysfs_file++;
657 		} while (sysfs_file->name != NULL);
658 
659 		read_freqs(origfreqs);
660 
661 		igt_install_exit_handler(pm_rps_exit_handler);
662 	}
663 
664 	igt_subtest("basic-api")
665 		min_max_config(basic_check, false);
666 
667 	/* Verify the constraints, check if we can reach idle */
668 	igt_subtest("min-max-config-idle")
669 		min_max_config(idle_check, true);
670 
671 	/* Verify the constraints with high load, check if we can reach max */
672 	igt_subtest("min-max-config-loaded") {
673 		load_helper_run(HIGH);
674 		min_max_config(loaded_check, false);
675 		load_helper_stop();
676 	}
677 
678 	/* Checks if we achieve boost using gem_wait */
679 	igt_subtest("waitboost")
680 		waitboost(drm_fd, false);
681 
682 	/* Test boost frequency after GPU reset */
683 	igt_subtest("reset") {
684 		igt_hang_t hang = igt_allow_hang(drm_fd, 0, 0);
685 		waitboost(drm_fd, true);
686 		igt_disallow_hang(drm_fd, hang);
687 	}
688 }
689