xref: /dpdk/examples/vm_power_manager/oob_monitor_x86.c (revision 200bc52e5aa0d72e70464c9cd22b55cf536ed13c)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2018 Intel Corporation
3  */
4 
5 #include <unistd.h>
6 #include <fcntl.h>
7 #include <rte_log.h>
8 
9 #include "oob_monitor.h"
10 #include "power_manager.h"
11 #include "channel_manager.h"
12 
13 static volatile unsigned run_loop = 1;
14 static uint64_t g_branches, g_branch_misses;
15 static int g_active;
16 
17 void branch_monitor_exit(void)
18 {
19 	run_loop = 0;
20 }
21 
22 /* Number of microseconds between each poll */
23 #define INTERVAL 100
24 #define PRINT_LOOP_COUNT (1000000/INTERVAL)
25 #define IA32_PERFEVTSEL0 0x186
26 #define IA32_PERFEVTSEL1 0x187
27 #define IA32_PERFCTR0 0xc1
28 #define IA32_PERFCTR1 0xc2
29 #define IA32_PERFEVT_BRANCH_HITS 0x05300c4
30 #define IA32_PERFEVT_BRANCH_MISS 0x05300c5
31 
32 static float
33 apply_policy(int core)
34 {
35 	struct core_info *ci;
36 	uint64_t counter = 0;
37 	uint64_t branches, branch_misses;
38 	uint64_t last_branches, last_branch_misses;
39 	int64_t hits_diff, miss_diff;
40 	float ratio;
41 	int ret;
42 
43 	g_active = 0;
44 	ci = get_core_info();
45 
46 	last_branches = ci->cd[core].last_branches;
47 	last_branch_misses = ci->cd[core].last_branch_misses;
48 
49 	ret = pread(ci->cd[core].msr_fd, &counter,
50 			sizeof(counter), IA32_PERFCTR0);
51 	if (ret < 0)
52 		RTE_LOG(ERR, POWER_MANAGER,
53 				"unable to read counter for core %u\n",
54 				core);
55 	branches = counter;
56 
57 	counter = 0;
58 	ret = pread(ci->cd[core].msr_fd, &counter,
59 			sizeof(counter), IA32_PERFCTR1);
60 	if (ret < 0)
61 		RTE_LOG(ERR, POWER_MANAGER,
62 				"unable to read counter for core %u\n",
63 				core);
64 	branch_misses = counter;
65 
66 
67 	ci->cd[core].last_branches = branches;
68 	ci->cd[core].last_branch_misses = branch_misses;
69 
70 	/*
71 	 * Intentional right shift to make MSB 0 to avoid
72 	 * possible signed overflow or truncation.
73 	 */
74 	branches >>= 1;
75 	last_branches >>= 1;
76 	hits_diff = (int64_t)branches - (int64_t)last_branches;
77 	if (hits_diff <= 0) {
78 		/* Likely a counter overflow condition, skip this round */
79 		return -1.0;
80 	}
81 
82 	/*
83 	 * Intentional right shift to make MSB 0 to avoid
84 	 * possible signed overflow or truncation.
85 	 */
86 	branch_misses >>= 1;
87 	last_branch_misses >>= 1;
88 	miss_diff = (int64_t)branch_misses - (int64_t)last_branch_misses;
89 	if (miss_diff <= 0) {
90 		/* Likely a counter overflow condition, skip this round */
91 		return -1.0;
92 	}
93 
94 	g_branches = hits_diff;
95 	g_branch_misses = miss_diff;
96 
97 	if (hits_diff < (INTERVAL*100)) {
98 		/* Likely no workload running on this core. Skip. */
99 		return -1.0;
100 	}
101 
102 	ratio = (float)miss_diff * (float)100 / (float)hits_diff;
103 
104 	if (ratio < ci->branch_ratio_threshold)
105 		power_manager_scale_core_min(core);
106 	else
107 		power_manager_scale_core_max(core);
108 
109 	g_active = 1;
110 	return ratio;
111 }
112 
113 int
114 add_core_to_monitor(int core)
115 {
116 	struct core_info *ci;
117 	char proc_file[UNIX_PATH_MAX];
118 	int ret;
119 
120 	ci = get_core_info();
121 
122 	if (core < ci->core_count) {
123 		long setup;
124 
125 		snprintf(proc_file, UNIX_PATH_MAX, "/dev/cpu/%d/msr", core);
126 		ci->cd[core].msr_fd = open(proc_file, O_RDWR | O_SYNC);
127 		if (ci->cd[core].msr_fd < 0) {
128 			RTE_LOG(ERR, POWER_MANAGER,
129 					"Error opening MSR file for core %d "
130 					"(is msr kernel module loaded?)\n",
131 					core);
132 			return -1;
133 		}
134 		/*
135 		 * Set up branch counters
136 		 */
137 		setup = IA32_PERFEVT_BRANCH_HITS;
138 		ret = pwrite(ci->cd[core].msr_fd, &setup,
139 				sizeof(setup), IA32_PERFEVTSEL0);
140 		if (ret < 0) {
141 			RTE_LOG(ERR, POWER_MANAGER,
142 					"unable to set counter for core %u\n",
143 					core);
144 			return ret;
145 		}
146 		setup = IA32_PERFEVT_BRANCH_MISS;
147 		ret = pwrite(ci->cd[core].msr_fd, &setup,
148 				sizeof(setup), IA32_PERFEVTSEL1);
149 		if (ret < 0) {
150 			RTE_LOG(ERR, POWER_MANAGER,
151 					"unable to set counter for core %u\n",
152 					core);
153 			return ret;
154 		}
155 		/*
156 		 * Close the file and re-open as read only so
157 		 * as not to hog the resource
158 		 */
159 		close(ci->cd[core].msr_fd);
160 		ci->cd[core].msr_fd = open(proc_file, O_RDONLY);
161 		if (ci->cd[core].msr_fd < 0) {
162 			RTE_LOG(ERR, POWER_MANAGER,
163 					"Error opening MSR file for core %d "
164 					"(is msr kernel module loaded?)\n",
165 					core);
166 			return -1;
167 		}
168 		ci->cd[core].oob_enabled = 1;
169 	}
170 	return 0;
171 }
172 
173 int
174 remove_core_from_monitor(int core)
175 {
176 	struct core_info *ci;
177 	char proc_file[UNIX_PATH_MAX];
178 	int ret;
179 
180 	ci = get_core_info();
181 
182 	if (ci->cd[core].oob_enabled) {
183 		long setup;
184 
185 		/*
186 		 * close the msr file, then reopen rw so we can
187 		 * disable the counters
188 		 */
189 		if (ci->cd[core].msr_fd != 0)
190 			close(ci->cd[core].msr_fd);
191 		snprintf(proc_file, UNIX_PATH_MAX, "/dev/cpu/%d/msr", core);
192 		ci->cd[core].msr_fd = open(proc_file, O_RDWR | O_SYNC);
193 		if (ci->cd[core].msr_fd < 0) {
194 			RTE_LOG(ERR, POWER_MANAGER,
195 					"Error opening MSR file for core %d "
196 					"(is msr kernel module loaded?)\n",
197 					core);
198 			return -1;
199 		}
200 		setup = 0x0; /* clear event */
201 		ret = pwrite(ci->cd[core].msr_fd, &setup,
202 				sizeof(setup), IA32_PERFEVTSEL0);
203 		if (ret < 0) {
204 			RTE_LOG(ERR, POWER_MANAGER,
205 					"unable to set counter for core %u\n",
206 					core);
207 			return ret;
208 		}
209 		setup = 0x0; /* clear event */
210 		ret = pwrite(ci->cd[core].msr_fd, &setup,
211 				sizeof(setup), IA32_PERFEVTSEL1);
212 		if (ret < 0) {
213 			RTE_LOG(ERR, POWER_MANAGER,
214 					"unable to set counter for core %u\n",
215 					core);
216 			return ret;
217 		}
218 
219 		close(ci->cd[core].msr_fd);
220 		ci->cd[core].msr_fd = 0;
221 		ci->cd[core].oob_enabled = 0;
222 	}
223 	return 0;
224 }
225 
226 int
227 branch_monitor_init(void)
228 {
229 	return 0;
230 }
231 
232 void
233 run_branch_monitor(void)
234 {
235 	struct core_info *ci;
236 	int print = 0;
237 	float ratio;
238 	int printed;
239 	int reads = 0;
240 
241 	ci = get_core_info();
242 
243 	while (run_loop) {
244 
245 		if (!run_loop)
246 			break;
247 		usleep(INTERVAL);
248 		int j;
249 		print++;
250 		printed = 0;
251 		for (j = 0; j < ci->core_count; j++) {
252 			if (ci->cd[j].oob_enabled) {
253 				ratio = apply_policy(j);
254 				if ((print > PRINT_LOOP_COUNT) && (g_active)) {
255 					printf("  %d: %.4f {%lu} {%d}", j,
256 							ratio, g_branches,
257 							reads);
258 					printed = 1;
259 					reads = 0;
260 				} else {
261 					reads++;
262 				}
263 			}
264 		}
265 		if (print > PRINT_LOOP_COUNT) {
266 			if (printed)
267 				printf("\n");
268 			print = 0;
269 		}
270 	}
271 }
272