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