1 /*
2 * QEMU KVM support -- x86 virtual RAPL msr
3 *
4 * Copyright 2024 Red Hat, Inc. 2024
5 *
6 * Author:
7 * Anthony Harivel <aharivel@redhat.com>
8 *
9 * This work is licensed under the terms of the GNU GPL, version 2 or later.
10 * See the COPYING file in the top-level directory.
11 *
12 */
13
14 #include "qemu/osdep.h"
15 #include "qemu/error-report.h"
16 #include "vmsr_energy.h"
17 #include "io/channel.h"
18 #include "io/channel-socket.h"
19 #include "hw/boards.h"
20 #include "cpu.h"
21 #include "host-cpu.h"
22
vmsr_compute_default_paths(void)23 char *vmsr_compute_default_paths(void)
24 {
25 g_autofree char *state = qemu_get_local_state_dir();
26
27 return g_build_filename(state, "run", "qemu-vmsr-helper.sock", NULL);
28 }
29
is_host_cpu_intel(void)30 bool is_host_cpu_intel(void)
31 {
32 char vendor[CPUID_VENDOR_SZ + 1];
33
34 host_cpu_vendor_fms(vendor, NULL, NULL, NULL);
35
36 return g_str_equal(vendor, CPUID_VENDOR_INTEL);
37 }
38
is_rapl_enabled(void)39 int is_rapl_enabled(void)
40 {
41 const char *path = "/sys/class/powercap/intel-rapl/enabled";
42 FILE *file = fopen(path, "r");
43 int value = 0;
44
45 if (file != NULL) {
46 if (fscanf(file, "%d", &value) != 1) {
47 error_report("INTEL RAPL not enabled");
48 }
49 fclose(file);
50 } else {
51 error_report("Error opening %s", path);
52 }
53
54 return value;
55 }
56
vmsr_open_socket(const char * path)57 QIOChannelSocket *vmsr_open_socket(const char *path)
58 {
59 g_autofree char *socket_path = NULL;
60
61 socket_path = g_strdup(path);
62
63 SocketAddress saddr = {
64 .type = SOCKET_ADDRESS_TYPE_UNIX,
65 .u.q_unix.path = socket_path
66 };
67
68 QIOChannelSocket *sioc = qio_channel_socket_new();
69 Error *local_err = NULL;
70
71 qio_channel_set_name(QIO_CHANNEL(sioc), "vmsr-helper");
72 qio_channel_socket_connect_sync(sioc,
73 &saddr,
74 &local_err);
75 if (local_err) {
76 /* Close socket. */
77 qio_channel_close(QIO_CHANNEL(sioc), NULL);
78 object_unref(OBJECT(sioc));
79 sioc = NULL;
80 goto out;
81 }
82
83 qio_channel_set_delay(QIO_CHANNEL(sioc), false);
84 out:
85 return sioc;
86 }
87
vmsr_read_msr(uint32_t reg,uint32_t cpu_id,uint32_t tid,QIOChannelSocket * sioc)88 uint64_t vmsr_read_msr(uint32_t reg, uint32_t cpu_id, uint32_t tid,
89 QIOChannelSocket *sioc)
90 {
91 uint64_t data = 0;
92 int r = 0;
93 Error *local_err = NULL;
94 uint32_t buffer[3];
95 /*
96 * Send the required arguments:
97 * 1. RAPL MSR register to read
98 * 2. On which CPU ID
99 * 3. From which vCPU (Thread ID)
100 */
101 buffer[0] = reg;
102 buffer[1] = cpu_id;
103 buffer[2] = tid;
104
105 r = qio_channel_write_all(QIO_CHANNEL(sioc),
106 (char *)buffer, sizeof(buffer),
107 &local_err);
108 if (r < 0) {
109 goto out_close;
110 }
111
112 r = qio_channel_read(QIO_CHANNEL(sioc),
113 (char *)&data, sizeof(data),
114 &local_err);
115 if (r < 0) {
116 data = 0;
117 goto out_close;
118 }
119
120 out_close:
121 return data;
122 }
123
124 /* Retrieve the max number of physical package */
vmsr_get_max_physical_package(unsigned int max_cpus)125 unsigned int vmsr_get_max_physical_package(unsigned int max_cpus)
126 {
127 const char *dir = "/sys/devices/system/cpu/";
128 const char *topo_path = "topology/physical_package_id";
129 g_autofree int *uniquePackages = g_new0(int, max_cpus);
130 unsigned int packageCount = 0;
131 FILE *file = NULL;
132
133 for (int i = 0; i < max_cpus; i++) {
134 g_autofree char *filePath = NULL;
135 g_autofree char *cpuid = g_strdup_printf("cpu%d", i);
136
137 filePath = g_build_filename(dir, cpuid, topo_path, NULL);
138
139 file = fopen(filePath, "r");
140
141 if (file == NULL) {
142 error_report("Error opening physical_package_id file");
143 return 0;
144 }
145
146 char packageId[10];
147 if (fgets(packageId, sizeof(packageId), file) == NULL) {
148 packageCount = 0;
149 }
150
151 fclose(file);
152
153 int currentPackageId = atoi(packageId);
154
155 bool isUnique = true;
156 for (int j = 0; j < packageCount; j++) {
157 if (uniquePackages[j] == currentPackageId) {
158 isUnique = false;
159 break;
160 }
161 }
162
163 if (isUnique) {
164 uniquePackages[packageCount] = currentPackageId;
165 packageCount++;
166
167 if (packageCount >= max_cpus) {
168 break;
169 }
170 }
171 }
172
173 return (packageCount == 0) ? 1 : packageCount;
174 }
175
176 /* Retrieve the max number of physical cpu on the host */
vmsr_get_maxcpus(void)177 unsigned int vmsr_get_maxcpus(void)
178 {
179 GDir *dir;
180 const gchar *entry_name;
181 unsigned int cpu_count = 0;
182 const char *path = "/sys/devices/system/cpu/";
183
184 dir = g_dir_open(path, 0, NULL);
185 if (dir == NULL) {
186 error_report("Unable to open cpu directory");
187 return -1;
188 }
189
190 while ((entry_name = g_dir_read_name(dir)) != NULL) {
191 if (g_ascii_strncasecmp(entry_name, "cpu", 3) == 0 &&
192 isdigit(entry_name[3])) {
193 cpu_count++;
194 }
195 }
196
197 g_dir_close(dir);
198
199 return cpu_count;
200 }
201
202 /* Count the number of physical cpu on each packages */
vmsr_count_cpus_per_package(unsigned int * package_count,unsigned int max_pkgs)203 unsigned int vmsr_count_cpus_per_package(unsigned int *package_count,
204 unsigned int max_pkgs)
205 {
206 g_autofree char *file_contents = NULL;
207 g_autofree char *path = NULL;
208 g_autofree char *path_name = NULL;
209 gsize length;
210
211 /* Iterate over cpus and count cpus in each package */
212 for (int cpu_id = 0; ; cpu_id++) {
213 path_name = g_strdup_printf("/sys/devices/system/cpu/cpu%d/"
214 "topology/physical_package_id", cpu_id);
215
216 path = g_build_filename(path_name, NULL);
217
218 if (!g_file_get_contents(path, &file_contents, &length, NULL)) {
219 break; /* No more cpus */
220 }
221
222 /* Get the physical package ID for this CPU */
223 int package_id = atoi(file_contents);
224
225 /* Check if the package ID is within the known number of packages */
226 if (package_id >= 0 && package_id < max_pkgs) {
227 /* If yes, count the cpu for this package*/
228 package_count[package_id]++;
229 }
230 }
231
232 return 0;
233 }
234
235 /* Get the physical package id from a given cpu id */
vmsr_get_physical_package_id(int cpu_id)236 int vmsr_get_physical_package_id(int cpu_id)
237 {
238 g_autofree char *file_contents = NULL;
239 g_autofree char *file_path = NULL;
240 int package_id = -1;
241 gsize length;
242
243 file_path = g_strdup_printf("/sys/devices/system/cpu/cpu%d"
244 "/topology/physical_package_id", cpu_id);
245
246 if (!g_file_get_contents(file_path, &file_contents, &length, NULL)) {
247 goto out;
248 }
249
250 package_id = atoi(file_contents);
251
252 out:
253 return package_id;
254 }
255
256 /* Read the scheduled time for a given thread of a give pid */
vmsr_read_thread_stat(pid_t pid,unsigned int thread_id,unsigned long long * utime,unsigned long long * stime,unsigned int * cpu_id)257 void vmsr_read_thread_stat(pid_t pid,
258 unsigned int thread_id,
259 unsigned long long *utime,
260 unsigned long long *stime,
261 unsigned int *cpu_id)
262 {
263 g_autofree char *path = NULL;
264 g_autofree char *path_name = NULL;
265
266 path_name = g_strdup_printf("/proc/%u/task/%d/stat", pid, thread_id);
267
268 path = g_build_filename(path_name, NULL);
269
270 FILE *file = fopen(path, "r");
271 if (file == NULL) {
272 error_report("Error opening %s", path_name);
273 return;
274 }
275
276 if (fscanf(file, "%*d (%*[^)]) %*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u"
277 " %llu %llu %*d %*d %*d %*d %*d %*d %*u %*u %*d %*u %*u"
278 " %*u %*u %*u %*u %*u %*u %*u %*u %*u %*d %*u %*u %u",
279 utime, stime, cpu_id) != 3)
280 {
281 fclose(file);
282 error_report("Error fscanf did not report the right amount of items");
283 return;
284 }
285
286 fclose(file);
287 }
288
289 /* Read QEMU stat task folder to retrieve all QEMU threads ID */
vmsr_get_thread_ids(pid_t pid,unsigned int * num_threads)290 pid_t *vmsr_get_thread_ids(pid_t pid, unsigned int *num_threads)
291 {
292 g_autofree char *task_path = g_strdup_printf("%d/task", pid);
293 g_autofree char *path = g_build_filename("/proc", task_path, NULL);
294
295 DIR *dir = opendir(path);
296 if (dir == NULL) {
297 error_report("Error opening /proc/qemu/task");
298 return NULL;
299 }
300
301 pid_t *thread_ids = NULL;
302 unsigned int thread_count = 0;
303
304 g_autofree struct dirent *ent = NULL;
305 while ((ent = readdir(dir)) != NULL) {
306 if (ent->d_name[0] == '.') {
307 continue;
308 }
309 pid_t tid = atoi(ent->d_name);
310 if (pid != tid) {
311 thread_ids = g_renew(pid_t, thread_ids, (thread_count + 1));
312 thread_ids[thread_count] = tid;
313 thread_count++;
314 }
315 }
316
317 closedir(dir);
318
319 *num_threads = thread_count;
320 return thread_ids;
321 }
322
vmsr_delta_ticks(vmsr_thread_stat * thd_stat,int i)323 void vmsr_delta_ticks(vmsr_thread_stat *thd_stat, int i)
324 {
325 thd_stat[i].delta_ticks = (thd_stat[i].utime[1] + thd_stat[i].stime[1])
326 - (thd_stat[i].utime[0] + thd_stat[i].stime[0]);
327 }
328
vmsr_get_ratio(uint64_t e_delta,unsigned long long delta_ticks,unsigned int maxticks)329 double vmsr_get_ratio(uint64_t e_delta,
330 unsigned long long delta_ticks,
331 unsigned int maxticks)
332 {
333 return (e_delta / 100.0) * ((100.0 / maxticks) * delta_ticks);
334 }
335
vmsr_init_topo_info(X86CPUTopoInfo * topo_info,const MachineState * ms)336 void vmsr_init_topo_info(X86CPUTopoInfo *topo_info,
337 const MachineState *ms)
338 {
339 topo_info->dies_per_pkg = ms->smp.dies;
340 topo_info->modules_per_die = ms->smp.modules;
341 topo_info->cores_per_module = ms->smp.cores;
342 topo_info->threads_per_core = ms->smp.threads;
343 }
344
345