xref: /qemu/target/i386/kvm/vmsr_energy.c (revision b103cc6e74ac92f070a0e004bd84334e845c20b5)
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     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 
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 
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 
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 */
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 */
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 */
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 */
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 */
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     return;
288 }
289 
290 /* Read QEMU stat task folder to retrieve all QEMU threads ID */
291 pid_t *vmsr_get_thread_ids(pid_t pid, unsigned int *num_threads)
292 {
293     g_autofree char *task_path = g_strdup_printf("%d/task", pid);
294     g_autofree char *path = g_build_filename("/proc", task_path, NULL);
295 
296     DIR *dir = opendir(path);
297     if (dir == NULL) {
298         error_report("Error opening /proc/qemu/task");
299         return NULL;
300     }
301 
302     pid_t *thread_ids = NULL;
303     unsigned int thread_count = 0;
304 
305     g_autofree struct dirent *ent = NULL;
306     while ((ent = readdir(dir)) != NULL) {
307         if (ent->d_name[0] == '.') {
308             continue;
309         }
310         pid_t tid = atoi(ent->d_name);
311         if (pid != tid) {
312             thread_ids = g_renew(pid_t, thread_ids, (thread_count + 1));
313             thread_ids[thread_count] = tid;
314             thread_count++;
315         }
316     }
317 
318     closedir(dir);
319 
320     *num_threads = thread_count;
321     return thread_ids;
322 }
323 
324 void vmsr_delta_ticks(vmsr_thread_stat *thd_stat, int i)
325 {
326     thd_stat[i].delta_ticks = (thd_stat[i].utime[1] + thd_stat[i].stime[1])
327                             - (thd_stat[i].utime[0] + thd_stat[i].stime[0]);
328 }
329 
330 double vmsr_get_ratio(uint64_t e_delta,
331                       unsigned long long delta_ticks,
332                       unsigned int maxticks)
333 {
334     return (e_delta / 100.0) * ((100.0 / maxticks) * delta_ticks);
335 }
336 
337 void vmsr_init_topo_info(X86CPUTopoInfo *topo_info,
338                            const MachineState *ms)
339 {
340     topo_info->dies_per_pkg = ms->smp.dies;
341     topo_info->modules_per_die = ms->smp.modules;
342     topo_info->cores_per_module = ms->smp.cores;
343     topo_info->threads_per_core = ms->smp.threads;
344 }
345 
346