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