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