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