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