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