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 *
26 */
27
28 #include "igt.h"
29 #include "igt_sysfs.h"
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <unistd.h>
34 #include <errno.h>
35 #include <time.h>
36
37
38 #define SLEEP_DURATION 3 /* in seconds */
39
40 #define RC6_ENABLED 1
41 #define RC6P_ENABLED 2
42 #define RC6PP_ENABLED 4
43
44 static int sysfs;
45
46 struct residencies {
47 int rc6;
48 int media_rc6;
49 int rc6p;
50 int rc6pp;
51 int duration;
52 };
53
get_rc6_enabled_mask(void)54 static unsigned long get_rc6_enabled_mask(void)
55 {
56 unsigned long enabled;
57
58 enabled = 0;
59 igt_sysfs_scanf(sysfs, "power/rc6_enable", "%lu", &enabled);
60 return enabled;
61 }
62
has_rc6_residency(const char * name)63 static bool has_rc6_residency(const char *name)
64 {
65 unsigned long residency;
66 char path[128];
67
68 sprintf(path, "power/%s_residency_ms", name);
69 return igt_sysfs_scanf(sysfs, path, "%lu", &residency) == 1;
70 }
71
read_rc6_residency(const char * name)72 static unsigned long read_rc6_residency(const char *name)
73 {
74 unsigned long residency;
75 char path[128];
76
77 residency = 0;
78 sprintf(path, "power/%s_residency_ms", name);
79 igt_assert(igt_sysfs_scanf(sysfs, path, "%lu", &residency) == 1);
80 return residency;
81 }
82
residency_accuracy(unsigned int diff,unsigned int duration,const char * name_of_rc6_residency)83 static void residency_accuracy(unsigned int diff,
84 unsigned int duration,
85 const char *name_of_rc6_residency)
86 {
87 double ratio;
88
89 ratio = (double)diff / duration;
90
91 igt_info("Residency in %s or deeper state: %u ms (sleep duration %u ms) (%.1f%% of expected duration)\n",
92 name_of_rc6_residency, diff, duration, 100*ratio);
93 igt_assert_f(ratio > 0.9 && ratio < 1.05,
94 "Sysfs RC6 residency counter is inaccurate.\n");
95 }
96
gettime_ms(void)97 static unsigned long gettime_ms(void)
98 {
99 struct timespec ts;
100
101 clock_gettime(CLOCK_MONOTONIC, &ts);
102
103 return ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
104 }
105
read_residencies(int devid,unsigned int mask,struct residencies * res)106 static void read_residencies(int devid, unsigned int mask,
107 struct residencies *res)
108 {
109 res->duration = gettime_ms();
110
111 if (mask & RC6_ENABLED)
112 res->rc6 = read_rc6_residency("rc6");
113
114 if ((mask & RC6_ENABLED) &&
115 (IS_VALLEYVIEW(devid) || IS_CHERRYVIEW(devid)))
116 res->media_rc6 = read_rc6_residency("media_rc6");
117
118 if (mask & RC6P_ENABLED)
119 res->rc6p = read_rc6_residency("rc6p");
120
121 if (mask & RC6PP_ENABLED)
122 res->rc6pp = read_rc6_residency("rc6pp");
123
124 res->duration += (gettime_ms() - res->duration) / 2;
125 }
126
measure_residencies(int devid,unsigned int mask,struct residencies * res)127 static void measure_residencies(int devid, unsigned int mask,
128 struct residencies *res)
129 {
130 struct residencies start = { };
131 struct residencies end = { };
132 int retry;
133
134 /*
135 * Retry in case of counter wrap-around. We simply re-run the
136 * measurement, since the valid counter range is different on
137 * different platforms and so fixing it up would be non-trivial.
138 */
139 read_residencies(devid, mask, &end);
140 igt_debug("time=%d: rc6=(%d, %d), rc6p=%d, rc6pp=%d\n",
141 end.duration, end.rc6, end.media_rc6, end.rc6p, end.rc6pp);
142 for (retry = 0; retry < 2; retry++) {
143 start = end;
144 sleep(SLEEP_DURATION);
145 read_residencies(devid, mask, &end);
146
147 igt_debug("time=%d: rc6=(%d, %d), rc6p=%d, rc6pp=%d\n",
148 end.duration,
149 end.rc6, end.media_rc6, end.rc6p, end.rc6pp);
150
151 if (end.rc6 >= start.rc6 &&
152 end.media_rc6 >= start.media_rc6 &&
153 end.rc6p >= start.rc6p &&
154 end.rc6pp >= start.rc6pp)
155 break;
156 }
157 igt_assert_f(retry < 2, "residency values are not consistent\n");
158
159 res->rc6 = end.rc6 - start.rc6;
160 res->rc6p = end.rc6p - start.rc6p;
161 res->rc6pp = end.rc6pp - start.rc6pp;
162 res->media_rc6 = end.media_rc6 - start.media_rc6;
163 res->duration = end.duration - start.duration;
164
165 /*
166 * For the purposes of this test case we want a given residency value
167 * to include the time spent in the corresponding RC state _and_ also
168 * the time spent in any enabled deeper states. So for example if any
169 * of RC6P or RC6PP is enabled we want the time spent in these states
170 * to be also included in the RC6 residency value. The kernel reported
171 * residency values are exclusive, so add up things here.
172 */
173 res->rc6p += res->rc6pp;
174 res->rc6 += res->rc6p;
175 }
176
wait_for_rc6(void)177 static bool wait_for_rc6(void)
178 {
179 struct timespec tv = {};
180 unsigned long start, now;
181
182 /* First wait for roughly an RC6 Evaluation Interval */
183 usleep(160 * 1000);
184
185 /* Then poll for RC6 to start ticking */
186 now = read_rc6_residency("rc6");
187 do {
188 start = now;
189 usleep(5000);
190 now = read_rc6_residency("rc6");
191 if (now - start > 1)
192 return true;
193 } while (!igt_seconds_elapsed(&tv));
194
195 return false;
196 }
197
198 igt_main
199 {
200 unsigned int rc6_enabled = 0;
201 unsigned int devid = 0;
202
203 igt_skip_on_simulation();
204
205 /* Use drm_open_driver to verify device existence */
206 igt_fixture {
207 int fd;
208
209 fd = drm_open_driver(DRIVER_INTEL);
210 devid = intel_get_drm_devid(fd);
211 sysfs = igt_sysfs_open(fd);
212
213 igt_require(has_rc6_residency("rc6"));
214
215 /* Make sure rc6 counters are running */
216 igt_drop_caches_set(fd, DROP_IDLE);
217 igt_require(wait_for_rc6());
218
219 close(fd);
220
221 rc6_enabled = get_rc6_enabled_mask();
222 igt_require(rc6_enabled & RC6_ENABLED);
223 }
224
225 igt_subtest("rc6-accuracy") {
226 struct residencies res;
227
228 measure_residencies(devid, rc6_enabled, &res);
229 residency_accuracy(res.rc6, res.duration, "rc6");
230 }
231
232 igt_subtest("media-rc6-accuracy") {
233 struct residencies res;
234
235 igt_require(IS_VALLEYVIEW(devid) || IS_CHERRYVIEW(devid));
236
237 measure_residencies(devid, rc6_enabled, &res);
238 residency_accuracy(res.media_rc6, res.duration, "media_rc6");
239 }
240 }
241