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 * Authors: Paulo Zanoni <[email protected]>
24 *
25 */
26
27 #include <sys/time.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <fcntl.h>
31 #include <inttypes.h>
32 #include <unistd.h>
33 #include <signal.h>
34 #include <time.h>
35 #include <getopt.h>
36 #include "igt.h"
37
38 #define IA32_TIME_STAMP_COUNTER 0x10
39
40 #define MSR_PKG_CST_CONFIG_CONTROL 0xE2
41 #define PKG_CST_LIMIT_MASK 0x7
42 #define PKG_CST_LIMIT_C0 0x0
43 #define PKG_CST_LIMIT_C2 0x1
44 #define PKG_CST_LIMIT_C3 0x2
45 #define PKG_CST_LIMIT_C6 0x3
46 #define PKG_CST_LIMIT_C7 0x4
47 #define PKG_CST_LIMIT_C7s 0x5
48 #define PKG_CST_NO_LIMIT 0x7
49
50 #define MSR_PKG_C2_RESIDENCY 0x60D
51 #define MSR_PKG_C3_RESIDENCY 0x3F8
52 #define MSR_PKG_C6_RESIDENCY 0x3F9
53 #define MSR_PKG_C7_RESIDENCY 0x3FA
54 #define MSR_PKG_C8_RESIDENCY 0x630
55 #define MSR_PKG_C9_RESIDENCY 0x631
56 #define MSR_PKG_C10_RESIDENCY 0x632
57
58 #define NUM_PC_STATES 7
59
60 const char *res_msr_names[] = {
61 "PC2", "PC3", "PC6", "PC7", "PC8", "PC9", "PC10"
62 };
63
64 const uint32_t res_msr_addrs[] = {
65 MSR_PKG_C2_RESIDENCY,
66 MSR_PKG_C3_RESIDENCY,
67 MSR_PKG_C6_RESIDENCY,
68 MSR_PKG_C7_RESIDENCY,
69 MSR_PKG_C8_RESIDENCY,
70 MSR_PKG_C9_RESIDENCY,
71 MSR_PKG_C10_RESIDENCY,
72 };
73
74 int msr_fd;
75
76 uint32_t deepest_pc_state;
77 uint64_t idle_res;
78
79 #define MAX_CONNECTORS 32
80 #define MAX_PLANES 32
81 struct {
82 int fd;
83 drmModeResPtr res;
84 drmModeConnectorPtr connectors[MAX_CONNECTORS];
85 drm_intel_bufmgr *bufmgr;
86 } drm;
87
88 struct {
89 uint32_t crtc_id;
90 uint32_t connector_id;
91 drmModeModeInfoPtr mode;
92 } modeset;
93
94 int vblank_interval_us;
95 struct igt_fb fbs[2], cursor, *front_fb, *back_fb;
96
97 struct {
98 int draw_size;
99 bool do_page_flip;
100 bool do_draw;
101 bool do_draw_and_flip;
102 int res_warm_time;
103 int res_calc_time;
104 int loop_inc;
105 char *test_name;
106 } opts = {
107 .draw_size = 0,
108 .do_page_flip = true,
109 .do_draw = true,
110 .do_draw_and_flip = true,
111 .res_warm_time = 1,
112 .res_calc_time = 4,
113 .loop_inc = 2,
114 .test_name = NULL,
115 };
116
msr_read(uint32_t addr)117 static uint64_t msr_read(uint32_t addr)
118 {
119 int rc;
120 uint64_t ret;
121
122 rc = pread(msr_fd, &ret, sizeof(uint64_t), addr);
123 igt_assert(rc == sizeof(ret));
124
125 return ret;
126 }
127
setup_msr(void)128 static void setup_msr(void)
129 {
130 #if 0
131 uint64_t control;
132 const char *limit;
133 #endif
134
135 /* Make sure our Kernel supports MSR and the module is loaded. */
136 igt_assert(system("modprobe -q msr > /dev/null 2>&1") != -1);
137
138 msr_fd = open("/dev/cpu/0/msr", O_RDONLY);
139 igt_assert_f(msr_fd >= 0,
140 "Can't open /dev/cpu/0/msr.\n");
141
142 #if 0
143 /* FIXME: why is this code not printing the truth? */
144 control = msr_read(MSR_PKG_CST_CONFIG_CONTROL);
145 printf("Control: 0x016%" PRIx64 "\n", control);
146 switch (control & PKG_CST_LIMIT_MASK) {
147 case PKG_CST_LIMIT_C0:
148 limit = "C0";
149 break;
150 case PKG_CST_LIMIT_C2:
151 limit = "C2";
152 break;
153 case PKG_CST_LIMIT_C3:
154 limit = "C3";
155 break;
156 case PKG_CST_LIMIT_C6:
157 limit = "C6";
158 break;
159 case PKG_CST_LIMIT_C7:
160 limit = "C7";
161 break;
162 case PKG_CST_LIMIT_C7s:
163 limit = "C7s";
164 break;
165 case PKG_CST_NO_LIMIT:
166 limit = "no limit";
167 break;
168 default:
169 limit = "unknown";
170 break;
171 }
172 printf("Package C state limit: %s\n", limit);
173 #endif
174 }
175
teardown_msr(void)176 static void teardown_msr(void)
177 {
178 close(msr_fd);
179 }
180
setup_drm(void)181 static void setup_drm(void)
182 {
183 int i;
184
185 drm.fd = drm_open_driver_master(DRIVER_INTEL);
186
187 drm.res = drmModeGetResources(drm.fd);
188 igt_assert(drm.res->count_connectors <= MAX_CONNECTORS);
189
190 for (i = 0; i < drm.res->count_connectors; i++)
191 drm.connectors[i] = drmModeGetConnector(drm.fd,
192 drm.res->connectors[i]);
193
194 drm.bufmgr = drm_intel_bufmgr_gem_init(drm.fd, 4096);
195 igt_assert(drm.bufmgr);
196 drm_intel_bufmgr_gem_enable_reuse(drm.bufmgr);
197 }
198
teardown_drm(void)199 static void teardown_drm(void)
200 {
201 int i;
202
203 drm_intel_bufmgr_destroy(drm.bufmgr);
204
205 for (i = 0; i < drm.res->count_connectors; i++)
206 drmModeFreeConnector(drm.connectors[i]);
207
208 drmModeFreeResources(drm.res);
209 close(drm.fd);
210 }
211
draw_rect(struct igt_fb * fb,enum igt_draw_method method,uint32_t color)212 static void draw_rect(struct igt_fb *fb, enum igt_draw_method method,
213 uint32_t color)
214 {
215 drmModeClip clip;
216 int rc;
217
218 switch (opts.draw_size) {
219 case 0:
220 clip.x1 = fb->width / 2 - 32;
221 clip.x2 = fb->width / 2 + 32;
222 clip.y1 = fb->height / 2 - 32;
223 clip.y2 = fb->height / 2 + 32;
224 break;
225 case 1:
226 clip.x1 = fb->width / 4;
227 clip.x2 = fb->width / 4 + fb->width / 2;
228 clip.y1 = fb->height / 4;
229 clip.y2 = fb->height / 4 + fb->height / 2;
230 break;
231 case 2:
232 clip.x1 = 0;
233 clip.x2 = fb->width;
234 clip.y1 = 0;
235 clip.y2 = fb->height;
236 break;
237 default:
238 igt_assert(false);
239 }
240
241 igt_draw_rect_fb(drm.fd, drm.bufmgr, NULL, fb, method, clip.x1, clip.y1,
242 clip.x2 - clip.x1, clip.y2 - clip.y1, color);
243
244 if (method == IGT_DRAW_MMAP_WC) {
245 rc = drmModeDirtyFB(drm.fd, fb->fb_id, &clip, 1);
246 igt_assert(rc == 0 || rc == -ENOSYS);
247 }
248 }
249
setup_modeset(void)250 static void setup_modeset(void)
251 {
252 int i;
253 drmModeConnectorPtr connector;
254
255 for (i = 0; i < drm.res->count_connectors; i++) {
256 connector = drm.connectors[i];
257
258 if (connector->connection == DRM_MODE_CONNECTED &&
259 connector->count_modes > 0)
260 break;
261 }
262 igt_assert(i < drm.res->count_connectors);
263
264 modeset.connector_id = connector->connector_id;
265 modeset.mode = &connector->modes[0];
266 modeset.crtc_id = kmstest_find_crtc_for_connector(drm.fd, drm.res,
267 connector, 0);
268
269 for (i = 0; i < 2; i++) {
270 igt_create_fb(drm.fd, modeset.mode->hdisplay,
271 modeset.mode->vdisplay, DRM_FORMAT_XRGB8888,
272 LOCAL_I915_FORMAT_MOD_X_TILED, &fbs[i]);
273 igt_draw_fill_fb(drm.fd, &fbs[i], 0x80);
274 }
275 draw_rect(&fbs[1], IGT_DRAW_BLT, 0x800000);
276
277 igt_create_fb(drm.fd, 64, 64, DRM_FORMAT_ARGB8888,
278 LOCAL_DRM_FORMAT_MOD_NONE, &cursor);
279 igt_draw_fill_fb(drm.fd, &cursor, 0xFF008000);
280 }
281
teardown_modeset(void)282 static void teardown_modeset(void)
283 {
284 igt_remove_fb(drm.fd, &fbs[0]);
285 igt_remove_fb(drm.fd, &fbs[1]);
286 igt_remove_fb(drm.fd, &cursor);
287 }
288
setup_vblank_interval(void)289 static void setup_vblank_interval(void)
290 {
291 uint64_t vrefresh, interval;
292
293 vrefresh = ((uint64_t) modeset.mode->clock * 1000 * 1000) /
294 (modeset.mode->htotal * modeset.mode->vtotal);
295 interval = 1000000000 / vrefresh;
296
297 vblank_interval_us = interval;
298
299 printf("Interval between vblanks:\t%dus\n", vblank_interval_us);
300 }
301
302 bool alarm_received;
alarm_handler(int signal)303 static void alarm_handler(int signal)
304 {
305 alarm_received = true;
306 }
307
setup_alarm(void)308 static void setup_alarm(void)
309 {
310 struct sigaction sa;
311
312 sa.sa_handler = alarm_handler;
313 sigemptyset(&sa.sa_mask);
314 sa.sa_flags = 0;
315 sigaction(SIGALRM, &sa, NULL);
316 }
317
set_alarm(time_t sec,suseconds_t usec)318 static void set_alarm(time_t sec, suseconds_t usec)
319 {
320 struct itimerval timerval = {{0, 0}, {sec, usec}};
321
322 alarm_received = false;
323 igt_assert(setitimer(ITIMER_REAL, &timerval, NULL) == 0);
324 }
325
unset_mode(void)326 static void unset_mode(void)
327 {
328 int rc;
329
330 kmstest_unset_all_crtcs(drm.fd, drm.res);
331 rc = drmModeSetCursor(drm.fd, modeset.crtc_id, 0, 0, 0);
332 igt_assert(rc == 0);
333 }
334
set_mode(void)335 static void set_mode(void)
336 {
337 int rc;
338
339 front_fb = &fbs[0];
340 back_fb = &fbs[1];
341 rc = drmModeSetCrtc(drm.fd, modeset.crtc_id, front_fb->fb_id, 0, 0,
342 &modeset.connector_id, 1, modeset.mode);
343 igt_assert(rc == 0);
344
345 /* TODO: it seems we need a cursor in order to reach PC7 on BDW. Why? */
346 rc = drmModeMoveCursor(drm.fd, modeset.crtc_id, 0, 0);
347 igt_assert(rc == 0);
348
349 rc = drmModeSetCursor(drm.fd, modeset.crtc_id, cursor.gem_handle,
350 cursor.width, cursor.height);
351 igt_assert(rc == 0);
352 }
353
wait_vblanks(int n_vblanks)354 static void wait_vblanks(int n_vblanks)
355 {
356 drmVBlank vblank;
357
358 if (!n_vblanks)
359 return;
360
361 vblank.request.type = DRM_VBLANK_RELATIVE;
362 vblank.request.sequence = n_vblanks;
363 vblank.request.signal = 0;
364 drmWaitVBlank(drm.fd, &vblank);
365 }
366
page_flip(void)367 static void page_flip(void)
368 {
369 struct igt_fb *tmp_fb;
370 int rc;
371
372 rc = drmModePageFlip(drm.fd, modeset.crtc_id, back_fb->fb_id, 0, NULL);
373 igt_assert(rc == 0);
374
375 tmp_fb = front_fb;
376 front_fb = back_fb;
377 back_fb = tmp_fb;
378 }
379
wait_until_idle(void)380 static void wait_until_idle(void)
381 {
382 uint64_t tsc, pc, res;
383
384 do {
385 set_alarm(0, 500 * 1000);
386
387 tsc = msr_read(IA32_TIME_STAMP_COUNTER);
388 pc = msr_read(deepest_pc_state);
389
390 while (!alarm_received)
391 pause();
392
393 pc = msr_read(deepest_pc_state) - pc;
394 tsc = msr_read(IA32_TIME_STAMP_COUNTER) - tsc;
395
396 res = pc * 100 / tsc;
397
398 /*printf("res:%02"PRIu64"\n", res);*/
399 } while (res < idle_res && idle_res - res > 3);
400
401 if (res > idle_res && res - idle_res > 3)
402 fprintf(stderr, "The calculated idle residency may be too low "
403 "(got %02"PRIu64"%%)\n", res);
404 }
405
do_measurement(void (* callback)(void * ptr),void * ptr)406 static uint64_t do_measurement(void (*callback)(void *ptr), void *ptr)
407 {
408 uint64_t tsc, pc;
409
410 wait_until_idle();
411
412 set_alarm(opts.res_warm_time, 0);
413 callback(ptr);
414
415 set_alarm(opts.res_calc_time, 0);
416
417 tsc = msr_read(IA32_TIME_STAMP_COUNTER);
418 pc = msr_read(deepest_pc_state);
419
420 callback(ptr);
421
422 pc = msr_read(deepest_pc_state) - pc;
423 tsc = msr_read(IA32_TIME_STAMP_COUNTER) - tsc;
424
425 return pc * 100 / tsc;
426 }
427
setup_idle(void)428 static void setup_idle(void)
429 {
430 uint64_t tsc, pc[NUM_PC_STATES], res, best_res;
431 int pc_i, best_pc_i = 0, retries, consecutive_not_best;
432
433 for (retries = 0; ; retries++) {
434
435 set_alarm(opts.res_warm_time, 0);
436 while (!alarm_received)
437 pause();
438
439 set_alarm(opts.res_calc_time, 0);
440
441 tsc = msr_read(IA32_TIME_STAMP_COUNTER);
442 for (pc_i = best_pc_i; pc_i < NUM_PC_STATES; pc_i++)
443 pc[pc_i] = msr_read(res_msr_addrs[pc_i]);
444
445 while (!alarm_received)
446 pause();
447
448 for (pc_i = best_pc_i; pc_i < NUM_PC_STATES; pc_i++)
449 pc[pc_i] = msr_read(res_msr_addrs[pc_i]) - pc[pc_i];
450 tsc = msr_read(IA32_TIME_STAMP_COUNTER) - tsc;
451
452 for (pc_i = NUM_PC_STATES -1; pc_i >= best_pc_i; pc_i--)
453 if (pc[pc_i] != 0)
454 break;
455 igt_require_f(pc_i >= 0, "We're not reaching any PC states!\n");
456
457 res = pc[pc_i] * 100 / tsc;
458
459 if (retries == 0 || pc_i > best_pc_i || res > best_res) {
460 best_pc_i = pc_i;
461 best_res = res;
462 consecutive_not_best = 0;
463 } else {
464 consecutive_not_best++;
465 if (consecutive_not_best > 2)
466 break;
467 }
468 }
469
470 deepest_pc_state = res_msr_addrs[best_pc_i];
471 idle_res = best_res;
472
473 printf("Stable idle residency retries:\t%d\n", retries);
474 printf("Deepest PC state reached when idle:\t%s\n",
475 res_msr_names[best_pc_i]);
476 printf("Idle residency for this state:\t%02"PRIu64"%%\n", idle_res);
477 }
478
print_result(int ops,int vblanks,uint64_t res)479 static void print_result(int ops, int vblanks, uint64_t res)
480 {
481 printf("- %02d ops every %02d vblanks:\t%02"PRIu64"%%\n",
482 ops, vblanks, res);
483 fflush(stdout);
484 }
485
486 struct page_flip_data {
487 int n_vblanks;
488 };
489
page_flip_cb(void * ptr)490 static void page_flip_cb(void *ptr)
491 {
492 struct page_flip_data *data = ptr;
493
494 while (!alarm_received) {
495 page_flip();
496 wait_vblanks(data->n_vblanks);
497 }
498 }
499
page_flip_test(void)500 static void page_flip_test(void)
501 {
502 struct page_flip_data data;
503 int n_vblanks;
504 uint64_t res;
505
506 printf("\nPage flip test:\n");
507
508 for (n_vblanks = 1; n_vblanks <= 64; n_vblanks *= opts.loop_inc) {
509 data.n_vblanks = n_vblanks;
510 res = do_measurement(page_flip_cb, &data);
511 print_result(1, n_vblanks, res);
512 }
513 }
514
515 struct draw_data {
516 enum igt_draw_method method;
517 int n_vblanks;
518 int ops_per_vblank;
519 };
520
draw_cb(void * ptr)521 static void draw_cb(void *ptr)
522 {
523 struct draw_data *data = ptr;
524 struct timespec req;
525 int i, ops;
526
527 req.tv_sec = 0;
528 req.tv_nsec = vblank_interval_us * 1000 / data->ops_per_vblank;
529
530 for (i = 0; !alarm_received; i++) {
531 for (ops = 0; ops < data->ops_per_vblank; ops++) {
532 draw_rect(front_fb, data->method, i << 8);
533
534 /* The code that stops the callbacks relies on SIGALRM,
535 * so we have to use nanosleep since it doesn't use
536 * signals. */
537 if (data->ops_per_vblank > 1)
538 nanosleep(&req, NULL);
539 }
540
541 if (data->n_vblanks)
542 wait_vblanks(data->n_vblanks);
543 }
544 }
545
draw_test(void)546 static void draw_test(void)
547 {
548 struct draw_data data;
549 enum igt_draw_method method;
550 int i;
551 uint64_t res;
552
553 for (method = 0; method < IGT_DRAW_METHOD_COUNT; method++) {
554 data.method = method;
555
556 printf("\nDraw %s test:\n",
557 igt_draw_get_method_name(method));
558
559 data.n_vblanks = 0;
560 for (i = 32; i >= 2; i /= opts.loop_inc) {
561 data.ops_per_vblank = i;
562 res = do_measurement(draw_cb, &data);
563 print_result(i, 1, res);
564 }
565
566 data.ops_per_vblank = 1;
567 for (i = 1; i <= 64; i *= opts.loop_inc) {
568 data.n_vblanks = i ;
569 res = do_measurement(draw_cb, &data);
570 print_result(1, i, res);
571 }
572 }
573 }
574
draw_and_flip_cb(void * ptr)575 static void draw_and_flip_cb(void *ptr)
576 {
577 struct draw_data *data = ptr;
578 int i, ops;
579
580 for (i = 0; !alarm_received; i++) {
581 for (ops = 0; ops < data->ops_per_vblank; ops++)
582 draw_rect(back_fb, data->method, i << 8);
583
584 page_flip();
585 wait_vblanks(1);
586 }
587 }
588
draw_and_flip_test(void)589 static void draw_and_flip_test(void)
590 {
591 struct draw_data data;
592 enum igt_draw_method method;
593 int i;
594 uint64_t res;
595
596 for (method = 0; method < IGT_DRAW_METHOD_COUNT; method++) {
597 data.method = method;
598
599 /* Doing everything consumes too much time! */
600 if (method != IGT_DRAW_MMAP_CPU && method != IGT_DRAW_BLT)
601 continue;
602
603 printf("\nDraw and flip %s test:\n",
604 igt_draw_get_method_name(method));
605
606 for (i = 16; i >= 1; i /= opts.loop_inc) {
607 data.ops_per_vblank = 1;
608 res = do_measurement(draw_and_flip_cb, &data);
609 print_result(i, 1, res);
610 }
611 }
612 }
613
parse_opts(int argc,char * argv[])614 static void parse_opts(int argc, char *argv[])
615 {
616 int opt;
617 char short_opts[] = "d:lrbw:c:i:fsn:";
618 struct option long_opts[] = {
619 { "draw-size", required_argument, NULL, 'd'},
620 { "no-flip", no_argument, NULL, 'l'},
621 { "no-draw", no_argument, NULL, 'r'},
622 { "no-draw-and-flip", no_argument, NULL, 'b'},
623 { "warm-time", required_argument, NULL, 'w'},
624 { "calc-time", required_argument, NULL, 'c'},
625 { "loop-increment", required_argument, NULL, 'i'},
626 { "fast", no_argument, NULL, 'f'},
627 { "slow", no_argument, NULL, 's'},
628 { "name", required_argument, NULL, 'n'},
629 { 0 },
630 };
631
632 while (1) {
633 opt = getopt_long(argc, argv, short_opts, long_opts, NULL);
634
635 switch (opt) {
636 case 'd':
637 if (strcmp(optarg, "s") == 0)
638 opts.draw_size = 0;
639 else if (strcmp(optarg, "m") == 0)
640 opts.draw_size = 1;
641 else if (strcmp(optarg, "l") == 0)
642 opts.draw_size = 2;
643 else
644 igt_assert(false);
645 break;
646 case 'l':
647 opts.do_page_flip = false;
648 break;
649 case 'r':
650 opts.do_draw = false;
651 break;
652 case 'b':
653 opts.do_draw_and_flip = false;
654 break;
655 case 'w':
656 opts.res_warm_time = atoi(optarg);
657 break;
658 case 'c':
659 opts.res_calc_time = atoi(optarg);
660 break;
661 case 'i':
662 opts.loop_inc = atoi(optarg);
663 break;
664 case 'f':
665 opts.res_warm_time = 1;
666 opts.res_calc_time = 2;
667 opts.loop_inc = 4;
668 break;
669 case 's':
670 opts.res_warm_time = 2;
671 opts.res_calc_time = 6;
672 opts.loop_inc = 2;
673 break;
674 case 'n':
675 opts.test_name = optarg;
676 break;
677 case -1:
678 return;
679 default:
680 igt_assert(false);
681 }
682 }
683 }
684
main(int argc,char * argv[])685 int main(int argc, char *argv[])
686 {
687 parse_opts(argc, argv);
688
689 setup_msr();
690 setup_drm();
691 setup_modeset();
692 setup_vblank_interval();
693 setup_alarm();
694
695 printf("Test name:\t%s\n", opts.test_name);
696
697 unset_mode();
698 set_mode();
699
700 setup_idle();
701
702 if (opts.do_page_flip)
703 page_flip_test();
704
705 if (opts.do_draw)
706 draw_test();
707
708 if (opts.do_draw_and_flip)
709 draw_and_flip_test();
710
711 teardown_modeset();
712 teardown_drm();
713 teardown_msr();
714 return 0;
715 }
716