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