1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Memory bandwidth monitoring and allocation library 4 * 5 * Copyright (C) 2018 Intel Corporation 6 * 7 * Authors: 8 * Sai Praneeth Prakhya <[email protected]>, 9 * Fenghua Yu <[email protected]> 10 */ 11 #include "resctrl.h" 12 13 #define UNCORE_IMC "uncore_imc" 14 #define READ_FILE_NAME "events/cas_count_read" 15 #define WRITE_FILE_NAME "events/cas_count_write" 16 #define DYN_PMU_PATH "/sys/bus/event_source/devices" 17 #define SCALE 0.00006103515625 18 #define MAX_IMCS 20 19 #define MAX_TOKENS 5 20 #define READ 0 21 #define WRITE 1 22 23 #define CON_MBM_LOCAL_BYTES_PATH \ 24 "%s/%s/mon_data/mon_L3_%02d/mbm_local_bytes" 25 26 struct membw_read_format { 27 __u64 value; /* The value of the event */ 28 __u64 time_enabled; /* if PERF_FORMAT_TOTAL_TIME_ENABLED */ 29 __u64 time_running; /* if PERF_FORMAT_TOTAL_TIME_RUNNING */ 30 __u64 id; /* if PERF_FORMAT_ID */ 31 }; 32 33 struct imc_counter_config { 34 __u32 type; 35 __u64 event; 36 __u64 umask; 37 struct perf_event_attr pe; 38 struct membw_read_format return_value; 39 int fd; 40 }; 41 42 static char mbm_total_path[1024]; 43 static int imcs; 44 static struct imc_counter_config imc_counters_config[MAX_IMCS][2]; 45 static const struct resctrl_test *current_test; 46 47 static void membw_initialize_perf_event_attr(int i, int j) 48 { 49 memset(&imc_counters_config[i][j].pe, 0, 50 sizeof(struct perf_event_attr)); 51 imc_counters_config[i][j].pe.type = imc_counters_config[i][j].type; 52 imc_counters_config[i][j].pe.size = sizeof(struct perf_event_attr); 53 imc_counters_config[i][j].pe.disabled = 1; 54 imc_counters_config[i][j].pe.inherit = 1; 55 imc_counters_config[i][j].pe.exclude_guest = 0; 56 imc_counters_config[i][j].pe.config = 57 imc_counters_config[i][j].umask << 8 | 58 imc_counters_config[i][j].event; 59 imc_counters_config[i][j].pe.sample_type = PERF_SAMPLE_IDENTIFIER; 60 imc_counters_config[i][j].pe.read_format = 61 PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_TOTAL_TIME_RUNNING; 62 } 63 64 static void membw_ioctl_perf_event_ioc_reset_enable(int i, int j) 65 { 66 ioctl(imc_counters_config[i][j].fd, PERF_EVENT_IOC_RESET, 0); 67 ioctl(imc_counters_config[i][j].fd, PERF_EVENT_IOC_ENABLE, 0); 68 } 69 70 static void membw_ioctl_perf_event_ioc_disable(int i, int j) 71 { 72 ioctl(imc_counters_config[i][j].fd, PERF_EVENT_IOC_DISABLE, 0); 73 } 74 75 /* 76 * get_event_and_umask: Parse config into event and umask 77 * @cas_count_cfg: Config 78 * @count: iMC number 79 * @op: Operation (read/write) 80 */ 81 static void get_event_and_umask(char *cas_count_cfg, int count, bool op) 82 { 83 char *token[MAX_TOKENS]; 84 int i = 0; 85 86 token[0] = strtok(cas_count_cfg, "=,"); 87 88 for (i = 1; i < MAX_TOKENS; i++) 89 token[i] = strtok(NULL, "=,"); 90 91 for (i = 0; i < MAX_TOKENS - 1; i++) { 92 if (!token[i]) 93 break; 94 if (strcmp(token[i], "event") == 0) { 95 if (op == READ) 96 imc_counters_config[count][READ].event = 97 strtol(token[i + 1], NULL, 16); 98 else 99 imc_counters_config[count][WRITE].event = 100 strtol(token[i + 1], NULL, 16); 101 } 102 if (strcmp(token[i], "umask") == 0) { 103 if (op == READ) 104 imc_counters_config[count][READ].umask = 105 strtol(token[i + 1], NULL, 16); 106 else 107 imc_counters_config[count][WRITE].umask = 108 strtol(token[i + 1], NULL, 16); 109 } 110 } 111 } 112 113 static int open_perf_event(int i, int cpu_no, int j) 114 { 115 imc_counters_config[i][j].fd = 116 perf_event_open(&imc_counters_config[i][j].pe, -1, cpu_no, -1, 117 PERF_FLAG_FD_CLOEXEC); 118 119 if (imc_counters_config[i][j].fd == -1) { 120 fprintf(stderr, "Error opening leader %llx\n", 121 imc_counters_config[i][j].pe.config); 122 123 return -1; 124 } 125 126 return 0; 127 } 128 129 /* Get type and config (read and write) of an iMC counter */ 130 static int read_from_imc_dir(char *imc_dir, int count) 131 { 132 char cas_count_cfg[1024], imc_counter_cfg[1024], imc_counter_type[1024]; 133 FILE *fp; 134 135 /* Get type of iMC counter */ 136 sprintf(imc_counter_type, "%s%s", imc_dir, "type"); 137 fp = fopen(imc_counter_type, "r"); 138 if (!fp) { 139 ksft_perror("Failed to open iMC counter type file"); 140 141 return -1; 142 } 143 if (fscanf(fp, "%u", &imc_counters_config[count][READ].type) <= 0) { 144 ksft_perror("Could not get iMC type"); 145 fclose(fp); 146 147 return -1; 148 } 149 fclose(fp); 150 151 imc_counters_config[count][WRITE].type = 152 imc_counters_config[count][READ].type; 153 154 /* Get read config */ 155 sprintf(imc_counter_cfg, "%s%s", imc_dir, READ_FILE_NAME); 156 fp = fopen(imc_counter_cfg, "r"); 157 if (!fp) { 158 ksft_perror("Failed to open iMC config file"); 159 160 return -1; 161 } 162 if (fscanf(fp, "%s", cas_count_cfg) <= 0) { 163 ksft_perror("Could not get iMC cas count read"); 164 fclose(fp); 165 166 return -1; 167 } 168 fclose(fp); 169 170 get_event_and_umask(cas_count_cfg, count, READ); 171 172 /* Get write config */ 173 sprintf(imc_counter_cfg, "%s%s", imc_dir, WRITE_FILE_NAME); 174 fp = fopen(imc_counter_cfg, "r"); 175 if (!fp) { 176 ksft_perror("Failed to open iMC config file"); 177 178 return -1; 179 } 180 if (fscanf(fp, "%s", cas_count_cfg) <= 0) { 181 ksft_perror("Could not get iMC cas count write"); 182 fclose(fp); 183 184 return -1; 185 } 186 fclose(fp); 187 188 get_event_and_umask(cas_count_cfg, count, WRITE); 189 190 return 0; 191 } 192 193 /* 194 * A system can have 'n' number of iMC (Integrated Memory Controller) 195 * counters, get that 'n'. For each iMC counter get it's type and config. 196 * Also, each counter has two configs, one for read and the other for write. 197 * A config again has two parts, event and umask. 198 * Enumerate all these details into an array of structures. 199 * 200 * Return: >= 0 on success. < 0 on failure. 201 */ 202 static int num_of_imcs(void) 203 { 204 char imc_dir[512], *temp; 205 unsigned int count = 0; 206 struct dirent *ep; 207 int ret; 208 DIR *dp; 209 210 dp = opendir(DYN_PMU_PATH); 211 if (dp) { 212 while ((ep = readdir(dp))) { 213 temp = strstr(ep->d_name, UNCORE_IMC); 214 if (!temp) 215 continue; 216 217 /* 218 * imc counters are named as "uncore_imc_<n>", hence 219 * increment the pointer to point to <n>. Note that 220 * sizeof(UNCORE_IMC) would count for null character as 221 * well and hence the last underscore character in 222 * uncore_imc'_' need not be counted. 223 */ 224 temp = temp + sizeof(UNCORE_IMC); 225 226 /* 227 * Some directories under "DYN_PMU_PATH" could have 228 * names like "uncore_imc_free_running", hence, check if 229 * first character is a numerical digit or not. 230 */ 231 if (temp[0] >= '0' && temp[0] <= '9') { 232 sprintf(imc_dir, "%s/%s/", DYN_PMU_PATH, 233 ep->d_name); 234 ret = read_from_imc_dir(imc_dir, count); 235 if (ret) { 236 closedir(dp); 237 238 return ret; 239 } 240 count++; 241 } 242 } 243 closedir(dp); 244 if (count == 0) { 245 ksft_print_msg("Unable to find iMC counters\n"); 246 247 return -1; 248 } 249 } else { 250 ksft_perror("Unable to open PMU directory"); 251 252 return -1; 253 } 254 255 return count; 256 } 257 258 int initialize_mem_bw_imc(void) 259 { 260 int imc, j; 261 262 imcs = num_of_imcs(); 263 if (imcs <= 0) 264 return imcs; 265 266 /* Initialize perf_event_attr structures for all iMC's */ 267 for (imc = 0; imc < imcs; imc++) { 268 for (j = 0; j < 2; j++) 269 membw_initialize_perf_event_attr(imc, j); 270 } 271 272 return 0; 273 } 274 275 static void perf_close_imc_mem_bw(void) 276 { 277 int mc; 278 279 for (mc = 0; mc < imcs; mc++) { 280 if (imc_counters_config[mc][READ].fd != -1) 281 close(imc_counters_config[mc][READ].fd); 282 if (imc_counters_config[mc][WRITE].fd != -1) 283 close(imc_counters_config[mc][WRITE].fd); 284 } 285 } 286 287 /* 288 * perf_open_imc_mem_bw - Open perf fds for IMCs 289 * @cpu_no: CPU number that the benchmark PID is bound to 290 * 291 * Return: = 0 on success. < 0 on failure. 292 */ 293 static int perf_open_imc_mem_bw(int cpu_no) 294 { 295 int imc, ret; 296 297 for (imc = 0; imc < imcs; imc++) { 298 imc_counters_config[imc][READ].fd = -1; 299 imc_counters_config[imc][WRITE].fd = -1; 300 } 301 302 for (imc = 0; imc < imcs; imc++) { 303 ret = open_perf_event(imc, cpu_no, READ); 304 if (ret) 305 goto close_fds; 306 ret = open_perf_event(imc, cpu_no, WRITE); 307 if (ret) 308 goto close_fds; 309 } 310 311 return 0; 312 313 close_fds: 314 perf_close_imc_mem_bw(); 315 return -1; 316 } 317 318 /* 319 * do_mem_bw_test - Perform memory bandwidth test 320 * 321 * Runs memory bandwidth test over one second period. Also, handles starting 322 * and stopping of the IMC perf counters around the test. 323 */ 324 static void do_imc_mem_bw_test(void) 325 { 326 int imc; 327 328 for (imc = 0; imc < imcs; imc++) { 329 membw_ioctl_perf_event_ioc_reset_enable(imc, READ); 330 membw_ioctl_perf_event_ioc_reset_enable(imc, WRITE); 331 } 332 333 sleep(1); 334 335 /* Stop counters after a second to get results (both read and write) */ 336 for (imc = 0; imc < imcs; imc++) { 337 membw_ioctl_perf_event_ioc_disable(imc, READ); 338 membw_ioctl_perf_event_ioc_disable(imc, WRITE); 339 } 340 } 341 342 /* 343 * get_mem_bw_imc - Memory bandwidth as reported by iMC counters 344 * @bw_report: Bandwidth report type (reads, writes) 345 * 346 * Memory bandwidth utilized by a process on a socket can be calculated 347 * using iMC counters. Perf events are used to read these counters. 348 * 349 * Return: = 0 on success. < 0 on failure. 350 */ 351 static int get_mem_bw_imc(const char *bw_report, float *bw_imc) 352 { 353 float reads, writes, of_mul_read, of_mul_write; 354 int imc; 355 356 /* Start all iMC counters to log values (both read and write) */ 357 reads = 0, writes = 0, of_mul_read = 1, of_mul_write = 1; 358 359 /* 360 * Get results which are stored in struct type imc_counter_config 361 * Take overflow into consideration before calculating total bandwidth. 362 */ 363 for (imc = 0; imc < imcs; imc++) { 364 struct imc_counter_config *r = 365 &imc_counters_config[imc][READ]; 366 struct imc_counter_config *w = 367 &imc_counters_config[imc][WRITE]; 368 369 if (read(r->fd, &r->return_value, 370 sizeof(struct membw_read_format)) == -1) { 371 ksft_perror("Couldn't get read bandwidth through iMC"); 372 return -1; 373 } 374 375 if (read(w->fd, &w->return_value, 376 sizeof(struct membw_read_format)) == -1) { 377 ksft_perror("Couldn't get write bandwidth through iMC"); 378 return -1; 379 } 380 381 __u64 r_time_enabled = r->return_value.time_enabled; 382 __u64 r_time_running = r->return_value.time_running; 383 384 if (r_time_enabled != r_time_running) 385 of_mul_read = (float)r_time_enabled / 386 (float)r_time_running; 387 388 __u64 w_time_enabled = w->return_value.time_enabled; 389 __u64 w_time_running = w->return_value.time_running; 390 391 if (w_time_enabled != w_time_running) 392 of_mul_write = (float)w_time_enabled / 393 (float)w_time_running; 394 reads += r->return_value.value * of_mul_read * SCALE; 395 writes += w->return_value.value * of_mul_write * SCALE; 396 } 397 398 if (strcmp(bw_report, "reads") == 0) { 399 *bw_imc = reads; 400 return 0; 401 } 402 403 if (strcmp(bw_report, "writes") == 0) { 404 *bw_imc = writes; 405 return 0; 406 } 407 408 *bw_imc = reads + writes; 409 return 0; 410 } 411 412 /* 413 * initialize_mem_bw_resctrl: Appropriately populate "mbm_total_path" 414 * @param: Parameters passed to resctrl_val() 415 * @domain_id: Domain ID (cache ID; for MB, L3 cache ID) 416 */ 417 void initialize_mem_bw_resctrl(const struct resctrl_val_param *param, 418 int domain_id) 419 { 420 sprintf(mbm_total_path, CON_MBM_LOCAL_BYTES_PATH, RESCTRL_PATH, 421 param->ctrlgrp, domain_id); 422 } 423 424 /* 425 * Open file to read MBM local bytes from resctrl FS 426 */ 427 static FILE *open_mem_bw_resctrl(const char *mbm_bw_file) 428 { 429 FILE *fp; 430 431 fp = fopen(mbm_bw_file, "r"); 432 if (!fp) 433 ksft_perror("Failed to open total memory bandwidth file"); 434 435 return fp; 436 } 437 438 /* 439 * Get MBM Local bytes as reported by resctrl FS 440 */ 441 static int get_mem_bw_resctrl(FILE *fp, unsigned long *mbm_total) 442 { 443 if (fscanf(fp, "%lu\n", mbm_total) <= 0) { 444 ksft_perror("Could not get MBM local bytes"); 445 return -1; 446 } 447 return 0; 448 } 449 450 static pid_t bm_pid, ppid; 451 452 void ctrlc_handler(int signum, siginfo_t *info, void *ptr) 453 { 454 /* Only kill child after bm_pid is set after fork() */ 455 if (bm_pid) 456 kill(bm_pid, SIGKILL); 457 umount_resctrlfs(); 458 if (current_test && current_test->cleanup) 459 current_test->cleanup(); 460 ksft_print_msg("Ending\n\n"); 461 462 exit(EXIT_SUCCESS); 463 } 464 465 /* 466 * Register CTRL-C handler for parent, as it has to kill 467 * child process before exiting. 468 */ 469 int signal_handler_register(const struct resctrl_test *test) 470 { 471 struct sigaction sigact = {}; 472 int ret = 0; 473 474 bm_pid = 0; 475 476 current_test = test; 477 sigact.sa_sigaction = ctrlc_handler; 478 sigemptyset(&sigact.sa_mask); 479 sigact.sa_flags = SA_SIGINFO; 480 if (sigaction(SIGINT, &sigact, NULL) || 481 sigaction(SIGTERM, &sigact, NULL) || 482 sigaction(SIGHUP, &sigact, NULL)) { 483 ksft_perror("sigaction"); 484 ret = -1; 485 } 486 return ret; 487 } 488 489 /* 490 * Reset signal handler to SIG_DFL. 491 * Non-Value return because the caller should keep 492 * the error code of other path even if sigaction fails. 493 */ 494 void signal_handler_unregister(void) 495 { 496 struct sigaction sigact = {}; 497 498 current_test = NULL; 499 sigact.sa_handler = SIG_DFL; 500 sigemptyset(&sigact.sa_mask); 501 if (sigaction(SIGINT, &sigact, NULL) || 502 sigaction(SIGTERM, &sigact, NULL) || 503 sigaction(SIGHUP, &sigact, NULL)) { 504 ksft_perror("sigaction"); 505 } 506 } 507 508 static void parent_exit(pid_t ppid) 509 { 510 kill(ppid, SIGKILL); 511 umount_resctrlfs(); 512 exit(EXIT_FAILURE); 513 } 514 515 /* 516 * print_results_bw: the memory bandwidth results are stored in a file 517 * @filename: file that stores the results 518 * @bm_pid: child pid that runs benchmark 519 * @bw_imc: perf imc counter value 520 * @bw_resc: memory bandwidth value 521 * 522 * Return: 0 on success, < 0 on error. 523 */ 524 static int print_results_bw(char *filename, pid_t bm_pid, float bw_imc, 525 unsigned long bw_resc) 526 { 527 unsigned long diff = fabs(bw_imc - bw_resc); 528 FILE *fp; 529 530 if (strcmp(filename, "stdio") == 0 || strcmp(filename, "stderr") == 0) { 531 printf("Pid: %d \t Mem_BW_iMC: %f \t ", (int)bm_pid, bw_imc); 532 printf("Mem_BW_resc: %lu \t Difference: %lu\n", bw_resc, diff); 533 } else { 534 fp = fopen(filename, "a"); 535 if (!fp) { 536 ksft_perror("Cannot open results file"); 537 538 return -1; 539 } 540 if (fprintf(fp, "Pid: %d \t Mem_BW_iMC: %f \t Mem_BW_resc: %lu \t Difference: %lu\n", 541 (int)bm_pid, bw_imc, bw_resc, diff) <= 0) { 542 ksft_print_msg("Could not log results\n"); 543 fclose(fp); 544 545 return -1; 546 } 547 fclose(fp); 548 } 549 550 return 0; 551 } 552 553 /* 554 * measure_mem_bw - Measures memory bandwidth numbers while benchmark runs 555 * @uparams: User supplied parameters 556 * @param: Parameters passed to resctrl_val() 557 * @bm_pid: PID that runs the benchmark 558 * @bw_report: Bandwidth report type (reads, writes) 559 * 560 * Measure memory bandwidth from resctrl and from another source which is 561 * perf imc value or could be something else if perf imc event is not 562 * available. Compare the two values to validate resctrl value. It takes 563 * 1 sec to measure the data. 564 */ 565 int measure_mem_bw(const struct user_params *uparams, 566 struct resctrl_val_param *param, pid_t bm_pid, 567 const char *bw_report) 568 { 569 unsigned long bw_resc, bw_resc_start, bw_resc_end; 570 FILE *mem_bw_fp; 571 float bw_imc; 572 int ret; 573 574 bw_report = get_bw_report_type(bw_report); 575 if (!bw_report) 576 return -1; 577 578 mem_bw_fp = open_mem_bw_resctrl(mbm_total_path); 579 if (!mem_bw_fp) 580 return -1; 581 582 ret = perf_open_imc_mem_bw(uparams->cpu); 583 if (ret < 0) 584 goto close_fp; 585 586 ret = get_mem_bw_resctrl(mem_bw_fp, &bw_resc_start); 587 if (ret < 0) 588 goto close_imc; 589 590 rewind(mem_bw_fp); 591 592 do_imc_mem_bw_test(); 593 594 ret = get_mem_bw_resctrl(mem_bw_fp, &bw_resc_end); 595 if (ret < 0) 596 goto close_imc; 597 598 ret = get_mem_bw_imc(bw_report, &bw_imc); 599 if (ret < 0) 600 goto close_imc; 601 602 perf_close_imc_mem_bw(); 603 fclose(mem_bw_fp); 604 605 bw_resc = (bw_resc_end - bw_resc_start) / MB; 606 607 return print_results_bw(param->filename, bm_pid, bw_imc, bw_resc); 608 609 close_imc: 610 perf_close_imc_mem_bw(); 611 close_fp: 612 fclose(mem_bw_fp); 613 return ret; 614 } 615 616 /* 617 * run_benchmark - Run a specified benchmark or fill_buf (default benchmark) 618 * in specified signal. Direct benchmark stdio to /dev/null. 619 * @signum: signal number 620 * @info: signal info 621 * @ucontext: user context in signal handling 622 */ 623 static void run_benchmark(int signum, siginfo_t *info, void *ucontext) 624 { 625 int operation, ret, memflush; 626 char **benchmark_cmd; 627 size_t span; 628 bool once; 629 FILE *fp; 630 631 benchmark_cmd = info->si_ptr; 632 633 /* 634 * Direct stdio of child to /dev/null, so that only parent writes to 635 * stdio (console) 636 */ 637 fp = freopen("/dev/null", "w", stdout); 638 if (!fp) { 639 ksft_perror("Unable to direct benchmark status to /dev/null"); 640 parent_exit(ppid); 641 } 642 643 if (strcmp(benchmark_cmd[0], "fill_buf") == 0) { 644 /* Execute default fill_buf benchmark */ 645 span = strtoul(benchmark_cmd[1], NULL, 10); 646 memflush = atoi(benchmark_cmd[2]); 647 operation = atoi(benchmark_cmd[3]); 648 if (!strcmp(benchmark_cmd[4], "true")) { 649 once = true; 650 } else if (!strcmp(benchmark_cmd[4], "false")) { 651 once = false; 652 } else { 653 ksft_print_msg("Invalid once parameter\n"); 654 parent_exit(ppid); 655 } 656 657 if (run_fill_buf(span, memflush, operation, once)) 658 fprintf(stderr, "Error in running fill buffer\n"); 659 } else { 660 /* Execute specified benchmark */ 661 ret = execvp(benchmark_cmd[0], benchmark_cmd); 662 if (ret) 663 ksft_perror("execvp"); 664 } 665 666 fclose(stdout); 667 ksft_print_msg("Unable to run specified benchmark\n"); 668 parent_exit(ppid); 669 } 670 671 /* 672 * resctrl_val: execute benchmark and measure memory bandwidth on 673 * the benchmark 674 * @test: test information structure 675 * @uparams: user supplied parameters 676 * @benchmark_cmd: benchmark command and its arguments 677 * @param: parameters passed to resctrl_val() 678 * 679 * Return: 0 when the test was run, < 0 on error. 680 */ 681 int resctrl_val(const struct resctrl_test *test, 682 const struct user_params *uparams, 683 const char * const *benchmark_cmd, 684 struct resctrl_val_param *param) 685 { 686 struct sigaction sigact; 687 int ret = 0, pipefd[2]; 688 char pipe_message = 0; 689 union sigval value; 690 int domain_id; 691 692 if (strcmp(param->filename, "") == 0) 693 sprintf(param->filename, "stdio"); 694 695 ret = get_domain_id(test->resource, uparams->cpu, &domain_id); 696 if (ret < 0) { 697 ksft_print_msg("Could not get domain ID\n"); 698 return ret; 699 } 700 701 /* 702 * If benchmark wasn't successfully started by child, then child should 703 * kill parent, so save parent's pid 704 */ 705 ppid = getpid(); 706 707 if (pipe(pipefd)) { 708 ksft_perror("Unable to create pipe"); 709 710 return -1; 711 } 712 713 /* 714 * Fork to start benchmark, save child's pid so that it can be killed 715 * when needed 716 */ 717 fflush(stdout); 718 bm_pid = fork(); 719 if (bm_pid == -1) { 720 ksft_perror("Unable to fork"); 721 722 return -1; 723 } 724 725 if (bm_pid == 0) { 726 /* 727 * Mask all signals except SIGUSR1, parent uses SIGUSR1 to 728 * start benchmark 729 */ 730 sigfillset(&sigact.sa_mask); 731 sigdelset(&sigact.sa_mask, SIGUSR1); 732 733 sigact.sa_sigaction = run_benchmark; 734 sigact.sa_flags = SA_SIGINFO; 735 736 /* Register for "SIGUSR1" signal from parent */ 737 if (sigaction(SIGUSR1, &sigact, NULL)) { 738 ksft_perror("Can't register child for signal"); 739 parent_exit(ppid); 740 } 741 742 /* Tell parent that child is ready */ 743 close(pipefd[0]); 744 pipe_message = 1; 745 if (write(pipefd[1], &pipe_message, sizeof(pipe_message)) < 746 sizeof(pipe_message)) { 747 ksft_perror("Failed signaling parent process"); 748 close(pipefd[1]); 749 return -1; 750 } 751 close(pipefd[1]); 752 753 /* Suspend child until delivery of "SIGUSR1" from parent */ 754 sigsuspend(&sigact.sa_mask); 755 756 ksft_perror("Child is done"); 757 parent_exit(ppid); 758 } 759 760 ksft_print_msg("Benchmark PID: %d\n", (int)bm_pid); 761 762 /* 763 * The cast removes constness but nothing mutates benchmark_cmd within 764 * the context of this process. At the receiving process, it becomes 765 * argv, which is mutable, on exec() but that's after fork() so it 766 * doesn't matter for the process running the tests. 767 */ 768 value.sival_ptr = (void *)benchmark_cmd; 769 770 /* Taskset benchmark to specified cpu */ 771 ret = taskset_benchmark(bm_pid, uparams->cpu, NULL); 772 if (ret) 773 goto out; 774 775 /* Write benchmark to specified control&monitoring grp in resctrl FS */ 776 ret = write_bm_pid_to_resctrl(bm_pid, param->ctrlgrp, param->mongrp); 777 if (ret) 778 goto out; 779 780 if (param->init) { 781 ret = param->init(param, domain_id); 782 if (ret) 783 goto out; 784 } 785 786 /* Parent waits for child to be ready. */ 787 close(pipefd[1]); 788 while (pipe_message != 1) { 789 if (read(pipefd[0], &pipe_message, sizeof(pipe_message)) < 790 sizeof(pipe_message)) { 791 ksft_perror("Failed reading message from child process"); 792 close(pipefd[0]); 793 goto out; 794 } 795 } 796 close(pipefd[0]); 797 798 /* Signal child to start benchmark */ 799 if (sigqueue(bm_pid, SIGUSR1, value) == -1) { 800 ksft_perror("sigqueue SIGUSR1 to child"); 801 ret = -1; 802 goto out; 803 } 804 805 /* Give benchmark enough time to fully run */ 806 sleep(1); 807 808 /* Test runs until the callback setup() tells the test to stop. */ 809 while (1) { 810 ret = param->setup(test, uparams, param); 811 if (ret == END_OF_TESTS) { 812 ret = 0; 813 break; 814 } 815 if (ret < 0) 816 break; 817 818 ret = param->measure(uparams, param, bm_pid); 819 if (ret) 820 break; 821 } 822 823 out: 824 kill(bm_pid, SIGKILL); 825 826 return ret; 827 } 828