xref: /openbmc/qemu/target/i386/kvm/vmsr_energy.c (revision 6e623af3)
10418f908SAnthony Harivel /*
20418f908SAnthony Harivel  * QEMU KVM support -- x86 virtual RAPL msr
30418f908SAnthony Harivel  *
40418f908SAnthony Harivel  * Copyright 2024 Red Hat, Inc. 2024
50418f908SAnthony Harivel  *
60418f908SAnthony Harivel  *  Author:
70418f908SAnthony Harivel  *      Anthony Harivel <aharivel@redhat.com>
80418f908SAnthony Harivel  *
90418f908SAnthony Harivel  * This work is licensed under the terms of the GNU GPL, version 2 or later.
100418f908SAnthony Harivel  * See the COPYING file in the top-level directory.
110418f908SAnthony Harivel  *
120418f908SAnthony Harivel  */
130418f908SAnthony Harivel 
140418f908SAnthony Harivel #include "qemu/osdep.h"
150418f908SAnthony Harivel #include "qemu/error-report.h"
160418f908SAnthony Harivel #include "vmsr_energy.h"
170418f908SAnthony Harivel #include "io/channel.h"
180418f908SAnthony Harivel #include "io/channel-socket.h"
190418f908SAnthony Harivel #include "hw/boards.h"
200418f908SAnthony Harivel #include "cpu.h"
210418f908SAnthony Harivel #include "host-cpu.h"
220418f908SAnthony Harivel 
vmsr_compute_default_paths(void)230418f908SAnthony Harivel char *vmsr_compute_default_paths(void)
240418f908SAnthony Harivel {
250418f908SAnthony Harivel     g_autofree char *state = qemu_get_local_state_dir();
260418f908SAnthony Harivel 
270418f908SAnthony Harivel     return g_build_filename(state, "run", "qemu-vmsr-helper.sock", NULL);
280418f908SAnthony Harivel }
290418f908SAnthony Harivel 
is_host_cpu_intel(void)300418f908SAnthony Harivel bool is_host_cpu_intel(void)
310418f908SAnthony Harivel {
320418f908SAnthony Harivel     int family, model, stepping;
330418f908SAnthony Harivel     char vendor[CPUID_VENDOR_SZ + 1];
340418f908SAnthony Harivel 
350418f908SAnthony Harivel     host_cpu_vendor_fms(vendor, &family, &model, &stepping);
360418f908SAnthony Harivel 
370418f908SAnthony Harivel     return strcmp(vendor, CPUID_VENDOR_INTEL);
380418f908SAnthony Harivel }
390418f908SAnthony Harivel 
is_rapl_enabled(void)400418f908SAnthony Harivel int is_rapl_enabled(void)
410418f908SAnthony Harivel {
420418f908SAnthony Harivel     const char *path = "/sys/class/powercap/intel-rapl/enabled";
430418f908SAnthony Harivel     FILE *file = fopen(path, "r");
440418f908SAnthony Harivel     int value = 0;
450418f908SAnthony Harivel 
460418f908SAnthony Harivel     if (file != NULL) {
470418f908SAnthony Harivel         if (fscanf(file, "%d", &value) != 1) {
480418f908SAnthony Harivel             error_report("INTEL RAPL not enabled");
490418f908SAnthony Harivel         }
500418f908SAnthony Harivel         fclose(file);
510418f908SAnthony Harivel     } else {
520418f908SAnthony Harivel         error_report("Error opening %s", path);
530418f908SAnthony Harivel     }
540418f908SAnthony Harivel 
550418f908SAnthony Harivel     return value;
560418f908SAnthony Harivel }
570418f908SAnthony Harivel 
vmsr_open_socket(const char * path)580418f908SAnthony Harivel QIOChannelSocket *vmsr_open_socket(const char *path)
590418f908SAnthony Harivel {
600418f908SAnthony Harivel     g_autofree char *socket_path = NULL;
610418f908SAnthony Harivel 
620418f908SAnthony Harivel     socket_path = g_strdup(path);
630418f908SAnthony Harivel 
640418f908SAnthony Harivel     SocketAddress saddr = {
650418f908SAnthony Harivel         .type = SOCKET_ADDRESS_TYPE_UNIX,
660418f908SAnthony Harivel         .u.q_unix.path = socket_path
670418f908SAnthony Harivel     };
680418f908SAnthony Harivel 
690418f908SAnthony Harivel     QIOChannelSocket *sioc = qio_channel_socket_new();
700418f908SAnthony Harivel     Error *local_err = NULL;
710418f908SAnthony Harivel 
720418f908SAnthony Harivel     qio_channel_set_name(QIO_CHANNEL(sioc), "vmsr-helper");
730418f908SAnthony Harivel     qio_channel_socket_connect_sync(sioc,
740418f908SAnthony Harivel                                     &saddr,
750418f908SAnthony Harivel                                     &local_err);
760418f908SAnthony Harivel     if (local_err) {
770418f908SAnthony Harivel         /* Close socket. */
780418f908SAnthony Harivel         qio_channel_close(QIO_CHANNEL(sioc), NULL);
790418f908SAnthony Harivel         object_unref(OBJECT(sioc));
800418f908SAnthony Harivel         sioc = NULL;
810418f908SAnthony Harivel         goto out;
820418f908SAnthony Harivel     }
830418f908SAnthony Harivel 
840418f908SAnthony Harivel     qio_channel_set_delay(QIO_CHANNEL(sioc), false);
850418f908SAnthony Harivel out:
860418f908SAnthony Harivel     return sioc;
870418f908SAnthony Harivel }
880418f908SAnthony Harivel 
vmsr_read_msr(uint32_t reg,uint32_t cpu_id,uint32_t tid,QIOChannelSocket * sioc)890418f908SAnthony Harivel uint64_t vmsr_read_msr(uint32_t reg, uint32_t cpu_id, uint32_t tid,
900418f908SAnthony Harivel                        QIOChannelSocket *sioc)
910418f908SAnthony Harivel {
920418f908SAnthony Harivel     uint64_t data = 0;
930418f908SAnthony Harivel     int r = 0;
940418f908SAnthony Harivel     Error *local_err = NULL;
950418f908SAnthony Harivel     uint32_t buffer[3];
960418f908SAnthony Harivel     /*
970418f908SAnthony Harivel      * Send the required arguments:
980418f908SAnthony Harivel      * 1. RAPL MSR register to read
990418f908SAnthony Harivel      * 2. On which CPU ID
1000418f908SAnthony Harivel      * 3. From which vCPU (Thread ID)
1010418f908SAnthony Harivel      */
1020418f908SAnthony Harivel     buffer[0] = reg;
1030418f908SAnthony Harivel     buffer[1] = cpu_id;
1040418f908SAnthony Harivel     buffer[2] = tid;
1050418f908SAnthony Harivel 
1060418f908SAnthony Harivel     r = qio_channel_write_all(QIO_CHANNEL(sioc),
1070418f908SAnthony Harivel                               (char *)buffer, sizeof(buffer),
1080418f908SAnthony Harivel                               &local_err);
1090418f908SAnthony Harivel     if (r < 0) {
1100418f908SAnthony Harivel         goto out_close;
1110418f908SAnthony Harivel     }
1120418f908SAnthony Harivel 
1130418f908SAnthony Harivel     r = qio_channel_read(QIO_CHANNEL(sioc),
1140418f908SAnthony Harivel                              (char *)&data, sizeof(data),
1150418f908SAnthony Harivel                              &local_err);
1160418f908SAnthony Harivel     if (r < 0) {
1170418f908SAnthony Harivel         data = 0;
1180418f908SAnthony Harivel         goto out_close;
1190418f908SAnthony Harivel     }
1200418f908SAnthony Harivel 
1210418f908SAnthony Harivel out_close:
1220418f908SAnthony Harivel    return data;
1230418f908SAnthony Harivel }
1240418f908SAnthony Harivel 
1250418f908SAnthony Harivel /* Retrieve the max number of physical package */
vmsr_get_max_physical_package(unsigned int max_cpus)1260418f908SAnthony Harivel unsigned int vmsr_get_max_physical_package(unsigned int max_cpus)
1270418f908SAnthony Harivel {
1280418f908SAnthony Harivel     const char *dir = "/sys/devices/system/cpu/";
1290418f908SAnthony Harivel     const char *topo_path = "topology/physical_package_id";
1300418f908SAnthony Harivel     g_autofree int *uniquePackages = g_new0(int, max_cpus);
1310418f908SAnthony Harivel     unsigned int packageCount = 0;
1320418f908SAnthony Harivel     FILE *file = NULL;
1330418f908SAnthony Harivel 
1340418f908SAnthony Harivel     for (int i = 0; i < max_cpus; i++) {
1350418f908SAnthony Harivel         g_autofree char *filePath = NULL;
1360418f908SAnthony Harivel         g_autofree char *cpuid = g_strdup_printf("cpu%d", i);
1370418f908SAnthony Harivel 
1380418f908SAnthony Harivel         filePath = g_build_filename(dir, cpuid, topo_path, NULL);
1390418f908SAnthony Harivel 
1400418f908SAnthony Harivel         file = fopen(filePath, "r");
1410418f908SAnthony Harivel 
1420418f908SAnthony Harivel         if (file == NULL) {
1430418f908SAnthony Harivel             error_report("Error opening physical_package_id file");
1440418f908SAnthony Harivel             return 0;
1450418f908SAnthony Harivel         }
1460418f908SAnthony Harivel 
1470418f908SAnthony Harivel         char packageId[10];
1480418f908SAnthony Harivel         if (fgets(packageId, sizeof(packageId), file) == NULL) {
1490418f908SAnthony Harivel             packageCount = 0;
1500418f908SAnthony Harivel         }
1510418f908SAnthony Harivel 
1520418f908SAnthony Harivel         fclose(file);
1530418f908SAnthony Harivel 
1540418f908SAnthony Harivel         int currentPackageId = atoi(packageId);
1550418f908SAnthony Harivel 
1560418f908SAnthony Harivel         bool isUnique = true;
1570418f908SAnthony Harivel         for (int j = 0; j < packageCount; j++) {
1580418f908SAnthony Harivel             if (uniquePackages[j] == currentPackageId) {
1590418f908SAnthony Harivel                 isUnique = false;
1600418f908SAnthony Harivel                 break;
1610418f908SAnthony Harivel             }
1620418f908SAnthony Harivel         }
1630418f908SAnthony Harivel 
1640418f908SAnthony Harivel         if (isUnique) {
1650418f908SAnthony Harivel             uniquePackages[packageCount] = currentPackageId;
1660418f908SAnthony Harivel             packageCount++;
1670418f908SAnthony Harivel 
1680418f908SAnthony Harivel             if (packageCount >= max_cpus) {
1690418f908SAnthony Harivel                 break;
1700418f908SAnthony Harivel             }
1710418f908SAnthony Harivel         }
1720418f908SAnthony Harivel     }
1730418f908SAnthony Harivel 
1740418f908SAnthony Harivel     return (packageCount == 0) ? 1 : packageCount;
1750418f908SAnthony Harivel }
1760418f908SAnthony Harivel 
1770418f908SAnthony Harivel /* Retrieve the max number of physical cpu on the host */
vmsr_get_maxcpus(void)1780418f908SAnthony Harivel unsigned int vmsr_get_maxcpus(void)
1790418f908SAnthony Harivel {
1800418f908SAnthony Harivel     GDir *dir;
1810418f908SAnthony Harivel     const gchar *entry_name;
1820418f908SAnthony Harivel     unsigned int cpu_count = 0;
1830418f908SAnthony Harivel     const char *path = "/sys/devices/system/cpu/";
1840418f908SAnthony Harivel 
1850418f908SAnthony Harivel     dir = g_dir_open(path, 0, NULL);
1860418f908SAnthony Harivel     if (dir == NULL) {
1870418f908SAnthony Harivel         error_report("Unable to open cpu directory");
1880418f908SAnthony Harivel         return -1;
1890418f908SAnthony Harivel     }
1900418f908SAnthony Harivel 
1910418f908SAnthony Harivel     while ((entry_name = g_dir_read_name(dir)) != NULL) {
1920418f908SAnthony Harivel         if (g_ascii_strncasecmp(entry_name, "cpu", 3) == 0 &&
1930418f908SAnthony Harivel             isdigit(entry_name[3])) {
1940418f908SAnthony Harivel             cpu_count++;
1950418f908SAnthony Harivel         }
1960418f908SAnthony Harivel     }
1970418f908SAnthony Harivel 
1980418f908SAnthony Harivel     g_dir_close(dir);
1990418f908SAnthony Harivel 
2000418f908SAnthony Harivel     return cpu_count;
2010418f908SAnthony Harivel }
2020418f908SAnthony Harivel 
2030418f908SAnthony Harivel /* Count the number of physical cpu on each packages */
vmsr_count_cpus_per_package(unsigned int * package_count,unsigned int max_pkgs)2040418f908SAnthony Harivel unsigned int vmsr_count_cpus_per_package(unsigned int *package_count,
2050418f908SAnthony Harivel                                          unsigned int max_pkgs)
2060418f908SAnthony Harivel {
2070418f908SAnthony Harivel     g_autofree char *file_contents = NULL;
2080418f908SAnthony Harivel     g_autofree char *path = NULL;
2090418f908SAnthony Harivel     g_autofree char *path_name = NULL;
2100418f908SAnthony Harivel     gsize length;
2110418f908SAnthony Harivel 
2120418f908SAnthony Harivel     /* Iterate over cpus and count cpus in each package */
2130418f908SAnthony Harivel     for (int cpu_id = 0; ; cpu_id++) {
2140418f908SAnthony Harivel         path_name = g_strdup_printf("/sys/devices/system/cpu/cpu%d/"
2150418f908SAnthony Harivel             "topology/physical_package_id", cpu_id);
2160418f908SAnthony Harivel 
2170418f908SAnthony Harivel         path = g_build_filename(path_name, NULL);
2180418f908SAnthony Harivel 
2190418f908SAnthony Harivel         if (!g_file_get_contents(path, &file_contents, &length, NULL)) {
2200418f908SAnthony Harivel             break; /* No more cpus */
2210418f908SAnthony Harivel         }
2220418f908SAnthony Harivel 
2230418f908SAnthony Harivel         /* Get the physical package ID for this CPU */
2240418f908SAnthony Harivel         int package_id = atoi(file_contents);
2250418f908SAnthony Harivel 
2260418f908SAnthony Harivel         /* Check if the package ID is within the known number of packages */
2270418f908SAnthony Harivel         if (package_id >= 0 && package_id < max_pkgs) {
2280418f908SAnthony Harivel             /* If yes, count the cpu for this package*/
2290418f908SAnthony Harivel             package_count[package_id]++;
2300418f908SAnthony Harivel         }
2310418f908SAnthony Harivel     }
2320418f908SAnthony Harivel 
2330418f908SAnthony Harivel     return 0;
2340418f908SAnthony Harivel }
2350418f908SAnthony Harivel 
2360418f908SAnthony Harivel /* Get the physical package id from a given cpu id */
vmsr_get_physical_package_id(int cpu_id)2370418f908SAnthony Harivel int vmsr_get_physical_package_id(int cpu_id)
2380418f908SAnthony Harivel {
2390418f908SAnthony Harivel     g_autofree char *file_contents = NULL;
2400418f908SAnthony Harivel     g_autofree char *file_path = NULL;
2410418f908SAnthony Harivel     int package_id = -1;
2420418f908SAnthony Harivel     gsize length;
2430418f908SAnthony Harivel 
2440418f908SAnthony Harivel     file_path = g_strdup_printf("/sys/devices/system/cpu/cpu%d"
2450418f908SAnthony Harivel         "/topology/physical_package_id", cpu_id);
2460418f908SAnthony Harivel 
2470418f908SAnthony Harivel     if (!g_file_get_contents(file_path, &file_contents, &length, NULL)) {
2480418f908SAnthony Harivel         goto out;
2490418f908SAnthony Harivel     }
2500418f908SAnthony Harivel 
2510418f908SAnthony Harivel     package_id = atoi(file_contents);
2520418f908SAnthony Harivel 
2530418f908SAnthony Harivel out:
2540418f908SAnthony Harivel     return package_id;
2550418f908SAnthony Harivel }
2560418f908SAnthony Harivel 
2570418f908SAnthony Harivel /* 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)2580418f908SAnthony Harivel void vmsr_read_thread_stat(pid_t pid,
2590418f908SAnthony Harivel                       unsigned int thread_id,
2600418f908SAnthony Harivel                       unsigned long long *utime,
2610418f908SAnthony Harivel                       unsigned long long *stime,
2620418f908SAnthony Harivel                       unsigned int *cpu_id)
2630418f908SAnthony Harivel {
2640418f908SAnthony Harivel     g_autofree char *path = NULL;
2650418f908SAnthony Harivel     g_autofree char *path_name = NULL;
2660418f908SAnthony Harivel 
2670418f908SAnthony Harivel     path_name = g_strdup_printf("/proc/%u/task/%d/stat", pid, thread_id);
2680418f908SAnthony Harivel 
2690418f908SAnthony Harivel     path = g_build_filename(path_name, NULL);
2700418f908SAnthony Harivel 
2710418f908SAnthony Harivel     FILE *file = fopen(path, "r");
2720418f908SAnthony Harivel     if (file == NULL) {
273*6e623af3SAnthony Harivel         error_report("Error opening %s", path_name);
2740418f908SAnthony Harivel         return;
2750418f908SAnthony Harivel     }
2760418f908SAnthony Harivel 
2770418f908SAnthony Harivel     if (fscanf(file, "%*d (%*[^)]) %*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u"
2780418f908SAnthony Harivel         " %llu %llu %*d %*d %*d %*d %*d %*d %*u %*u %*d %*u %*u"
2790418f908SAnthony Harivel         " %*u %*u %*u %*u %*u %*u %*u %*u %*u %*d %*u %*u %u",
2800418f908SAnthony Harivel            utime, stime, cpu_id) != 3)
2810418f908SAnthony Harivel     {
282*6e623af3SAnthony Harivel         fclose(file);
283*6e623af3SAnthony Harivel         error_report("Error fscanf did not report the right amount of items");
2840418f908SAnthony Harivel         return;
2850418f908SAnthony Harivel     }
2860418f908SAnthony Harivel 
2870418f908SAnthony Harivel     fclose(file);
2880418f908SAnthony Harivel     return;
2890418f908SAnthony Harivel }
2900418f908SAnthony Harivel 
2910418f908SAnthony Harivel /* Read QEMU stat task folder to retrieve all QEMU threads ID */
vmsr_get_thread_ids(pid_t pid,unsigned int * num_threads)2920418f908SAnthony Harivel pid_t *vmsr_get_thread_ids(pid_t pid, unsigned int *num_threads)
2930418f908SAnthony Harivel {
2940418f908SAnthony Harivel     g_autofree char *task_path = g_strdup_printf("%d/task", pid);
2950418f908SAnthony Harivel     g_autofree char *path = g_build_filename("/proc", task_path, NULL);
2960418f908SAnthony Harivel 
2970418f908SAnthony Harivel     DIR *dir = opendir(path);
2980418f908SAnthony Harivel     if (dir == NULL) {
2990418f908SAnthony Harivel         error_report("Error opening /proc/qemu/task");
3000418f908SAnthony Harivel         return NULL;
3010418f908SAnthony Harivel     }
3020418f908SAnthony Harivel 
3030418f908SAnthony Harivel     pid_t *thread_ids = NULL;
3040418f908SAnthony Harivel     unsigned int thread_count = 0;
3050418f908SAnthony Harivel 
3060418f908SAnthony Harivel     g_autofree struct dirent *ent = NULL;
3070418f908SAnthony Harivel     while ((ent = readdir(dir)) != NULL) {
3080418f908SAnthony Harivel         if (ent->d_name[0] == '.') {
3090418f908SAnthony Harivel             continue;
3100418f908SAnthony Harivel         }
3110418f908SAnthony Harivel         pid_t tid = atoi(ent->d_name);
3120418f908SAnthony Harivel         if (pid != tid) {
3130418f908SAnthony Harivel             thread_ids = g_renew(pid_t, thread_ids, (thread_count + 1));
3140418f908SAnthony Harivel             thread_ids[thread_count] = tid;
3150418f908SAnthony Harivel             thread_count++;
3160418f908SAnthony Harivel         }
3170418f908SAnthony Harivel     }
3180418f908SAnthony Harivel 
3190418f908SAnthony Harivel     closedir(dir);
3200418f908SAnthony Harivel 
3210418f908SAnthony Harivel     *num_threads = thread_count;
3220418f908SAnthony Harivel     return thread_ids;
3230418f908SAnthony Harivel }
3240418f908SAnthony Harivel 
vmsr_delta_ticks(vmsr_thread_stat * thd_stat,int i)3250418f908SAnthony Harivel void vmsr_delta_ticks(vmsr_thread_stat *thd_stat, int i)
3260418f908SAnthony Harivel {
3270418f908SAnthony Harivel     thd_stat[i].delta_ticks = (thd_stat[i].utime[1] + thd_stat[i].stime[1])
3280418f908SAnthony Harivel                             - (thd_stat[i].utime[0] + thd_stat[i].stime[0]);
3290418f908SAnthony Harivel }
3300418f908SAnthony Harivel 
vmsr_get_ratio(uint64_t e_delta,unsigned long long delta_ticks,unsigned int maxticks)3310418f908SAnthony Harivel double vmsr_get_ratio(uint64_t e_delta,
3320418f908SAnthony Harivel                       unsigned long long delta_ticks,
3330418f908SAnthony Harivel                       unsigned int maxticks)
3340418f908SAnthony Harivel {
3350418f908SAnthony Harivel     return (e_delta / 100.0) * ((100.0 / maxticks) * delta_ticks);
3360418f908SAnthony Harivel }
3370418f908SAnthony Harivel 
vmsr_init_topo_info(X86CPUTopoInfo * topo_info,const MachineState * ms)3380418f908SAnthony Harivel void vmsr_init_topo_info(X86CPUTopoInfo *topo_info,
3390418f908SAnthony Harivel                            const MachineState *ms)
3400418f908SAnthony Harivel {
3410418f908SAnthony Harivel     topo_info->dies_per_pkg = ms->smp.dies;
3420418f908SAnthony Harivel     topo_info->modules_per_die = ms->smp.modules;
3430418f908SAnthony Harivel     topo_info->cores_per_module = ms->smp.cores;
3440418f908SAnthony Harivel     topo_info->threads_per_core = ms->smp.threads;
3450418f908SAnthony Harivel }
3460418f908SAnthony Harivel 
347