Skip to content

Commit 3cb3f0b

Browse files
rchatreshuahkh
authored andcommitted
selftests/resctrl: Ensure measurements skip initialization of default benchmark
The CMT, MBA, and MBM tests rely on the resctrl_val() wrapper to start and run a benchmark while providing test specific flows via callbacks to do test specific configuration and measurements. At a high level, the resctrl_val() flow is: a) Start by fork()ing a child process that installs a signal handler for SIGUSR1 that, on receipt of SIGUSR1, will start running a benchmark. b) Assign the child process created in (a) to the resctrl control and monitoring group that dictates the memory and cache allocations with which the process can run and will contain all resctrl monitoring data of that process. c) Once parent and child are considered "ready" (determined via a message over a pipe) the parent signals the child (via SIGUSR1) to start the benchmark, waits one second for the benchmark to run, and then starts collecting monitoring data for the tests, potentially also changing allocation configuration depending on the various test callbacks. A problem with the above flow is the "black box" view of the benchmark that is combined with an arbitrarily chosen "wait one second" before measurements start. No matter what the benchmark does, it is given one second to initialize before measurements start. The default benchmark "fill_buf" consists of two parts, first it prepares a buffer (allocate, initialize, then flush), then it reads from the buffer (in unpredictable ways) until terminated. Depending on the system and the size of the buffer, the first "prepare" part may not be complete by the time the one second delay expires. Test measurements may thus start before the work needing to be measured runs. Split the default benchmark into its "prepare" and "runtime" parts and simplify the resctrl_val() wrapper while doing so. This same split cannot be done for the user provided benchmark (without a user interface change), so the current behavior is maintained for user provided benchmark. Assign the test itself to the control and monitoring group and run the "prepare" part of the benchmark in this context, ensuring it runs with required cache and memory bandwidth allocations. With the benchmark preparation complete it is only needed to fork() the "runtime" part of the benchmark (or entire user provided benchmark). Keep the "wait one second" delay before measurements start. For the default "fill_buf" benchmark this time now covers only the "runtime" portion that needs to be measured. For the user provided benchmark this delay maintains current behavior. Signed-off-by: Reinette Chatre <[email protected]> Reviewed-by: Ilpo Järvinen <[email protected]> Signed-off-by: Shuah Khan <[email protected]>
1 parent e958c21 commit 3cb3f0b

File tree

3 files changed

+50
-161
lines changed

3 files changed

+50
-161
lines changed

tools/testing/selftests/resctrl/fill_buf.c

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -129,18 +129,3 @@ unsigned char *alloc_buffer(size_t buf_size, bool memflush)
129129

130130
return buf;
131131
}
132-
133-
int run_fill_buf(size_t buf_size, bool memflush)
134-
{
135-
unsigned char *buf;
136-
137-
buf = alloc_buffer(buf_size, memflush);
138-
if (!buf)
139-
return -1;
140-
141-
fill_cache_read(buf, buf_size, false);
142-
143-
free(buf);
144-
145-
return 0;
146-
}

tools/testing/selftests/resctrl/resctrl.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,6 @@ int perf_event_open(struct perf_event_attr *hw_event, pid_t pid, int cpu,
169169
unsigned char *alloc_buffer(size_t buf_size, bool memflush);
170170
void mem_flush(unsigned char *buf, size_t buf_size);
171171
void fill_cache_read(unsigned char *buf, size_t buf_size, bool once);
172-
int run_fill_buf(size_t buf_size, bool memflush);
173172
int initialize_read_mem_bw_imc(void);
174173
int measure_read_mem_bw(const struct user_params *uparams,
175174
struct resctrl_val_param *param, pid_t bm_pid);

tools/testing/selftests/resctrl/resctrl_val.c

Lines changed: 50 additions & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -373,7 +373,7 @@ static int get_mem_bw_resctrl(FILE *fp, unsigned long *mbm_total)
373373
return 0;
374374
}
375375

376-
static pid_t bm_pid, ppid;
376+
static pid_t bm_pid;
377377

378378
void ctrlc_handler(int signum, siginfo_t *info, void *ptr)
379379
{
@@ -431,13 +431,6 @@ void signal_handler_unregister(void)
431431
}
432432
}
433433

434-
static void parent_exit(pid_t ppid)
435-
{
436-
kill(ppid, SIGKILL);
437-
umount_resctrlfs();
438-
exit(EXIT_FAILURE);
439-
}
440-
441434
/*
442435
* print_results_bw: the memory bandwidth results are stored in a file
443436
* @filename: file that stores the results
@@ -535,52 +528,6 @@ int measure_read_mem_bw(const struct user_params *uparams,
535528
return ret;
536529
}
537530

538-
struct benchmark_info {
539-
const struct user_params *uparams;
540-
struct resctrl_val_param *param;
541-
};
542-
543-
/*
544-
* run_benchmark - Run a specified benchmark or fill_buf (default benchmark)
545-
* in specified signal. Direct benchmark stdio to /dev/null.
546-
* @signum: signal number
547-
* @info: signal info
548-
* @ucontext: user context in signal handling
549-
*/
550-
static void run_benchmark(int signum, siginfo_t *info, void *ucontext)
551-
{
552-
struct benchmark_info *benchmark_info = info->si_ptr;
553-
const struct user_params *uparams = benchmark_info->uparams;
554-
struct resctrl_val_param *param = benchmark_info->param;
555-
FILE *fp;
556-
int ret;
557-
558-
/*
559-
* Direct stdio of child to /dev/null, so that only parent writes to
560-
* stdio (console)
561-
*/
562-
fp = freopen("/dev/null", "w", stdout);
563-
if (!fp) {
564-
ksft_perror("Unable to direct benchmark status to /dev/null");
565-
parent_exit(ppid);
566-
}
567-
568-
if (param->fill_buf) {
569-
if (run_fill_buf(param->fill_buf->buf_size,
570-
param->fill_buf->memflush))
571-
fprintf(stderr, "Error in running fill buffer\n");
572-
} else if (uparams->benchmark_cmd[0]) {
573-
/* Execute specified benchmark */
574-
ret = execvp(uparams->benchmark_cmd[0], (char **)uparams->benchmark_cmd);
575-
if (ret)
576-
ksft_perror("execvp");
577-
}
578-
579-
fclose(stdout);
580-
ksft_print_msg("Unable to run specified benchmark\n");
581-
parent_exit(ppid);
582-
}
583-
584531
/*
585532
* resctrl_val: execute benchmark and measure memory bandwidth on
586533
* the benchmark
@@ -594,12 +541,11 @@ int resctrl_val(const struct resctrl_test *test,
594541
const struct user_params *uparams,
595542
struct resctrl_val_param *param)
596543
{
597-
struct benchmark_info benchmark_info;
598-
struct sigaction sigact;
599-
int ret = 0, pipefd[2];
600-
char pipe_message = 0;
601-
union sigval value;
544+
unsigned char *buf = NULL;
545+
cpu_set_t old_affinity;
602546
int domain_id;
547+
int ret = 0;
548+
pid_t ppid;
603549

604550
if (strcmp(param->filename, "") == 0)
605551
sprintf(param->filename, "stdio");
@@ -610,108 +556,65 @@ int resctrl_val(const struct resctrl_test *test,
610556
return ret;
611557
}
612558

613-
benchmark_info.uparams = uparams;
614-
benchmark_info.param = param;
615-
616-
/*
617-
* If benchmark wasn't successfully started by child, then child should
618-
* kill parent, so save parent's pid
619-
*/
620559
ppid = getpid();
621560

622-
if (pipe(pipefd)) {
623-
ksft_perror("Unable to create pipe");
561+
/* Taskset test to specified CPU. */
562+
ret = taskset_benchmark(ppid, uparams->cpu, &old_affinity);
563+
if (ret)
564+
return ret;
565+
566+
/* Write test to specified control & monitoring group in resctrl FS. */
567+
ret = write_bm_pid_to_resctrl(ppid, param->ctrlgrp, param->mongrp);
568+
if (ret)
569+
goto reset_affinity;
624570

625-
return -1;
571+
if (param->init) {
572+
ret = param->init(param, domain_id);
573+
if (ret)
574+
goto reset_affinity;
626575
}
627576

628577
/*
629-
* Fork to start benchmark, save child's pid so that it can be killed
630-
* when needed
578+
* If not running user provided benchmark, run the default
579+
* "fill_buf". First phase of "fill_buf" is to prepare the
580+
* buffer that the benchmark will operate on. No measurements
581+
* are needed during this phase and prepared memory will be
582+
* passed to next part of benchmark via copy-on-write thus
583+
* no impact on the benchmark that relies on reading from
584+
* memory only.
631585
*/
586+
if (param->fill_buf) {
587+
buf = alloc_buffer(param->fill_buf->buf_size,
588+
param->fill_buf->memflush);
589+
if (!buf) {
590+
ret = -ENOMEM;
591+
goto reset_affinity;
592+
}
593+
}
594+
632595
fflush(stdout);
633596
bm_pid = fork();
634597
if (bm_pid == -1) {
598+
ret = -errno;
635599
ksft_perror("Unable to fork");
636-
637-
return -1;
600+
goto free_buf;
638601
}
639602

603+
/*
604+
* What needs to be measured runs in separate process until
605+
* terminated.
606+
*/
640607
if (bm_pid == 0) {
641-
/*
642-
* Mask all signals except SIGUSR1, parent uses SIGUSR1 to
643-
* start benchmark
644-
*/
645-
sigfillset(&sigact.sa_mask);
646-
sigdelset(&sigact.sa_mask, SIGUSR1);
647-
648-
sigact.sa_sigaction = run_benchmark;
649-
sigact.sa_flags = SA_SIGINFO;
650-
651-
/* Register for "SIGUSR1" signal from parent */
652-
if (sigaction(SIGUSR1, &sigact, NULL)) {
653-
ksft_perror("Can't register child for signal");
654-
parent_exit(ppid);
655-
}
656-
657-
/* Tell parent that child is ready */
658-
close(pipefd[0]);
659-
pipe_message = 1;
660-
if (write(pipefd[1], &pipe_message, sizeof(pipe_message)) <
661-
sizeof(pipe_message)) {
662-
ksft_perror("Failed signaling parent process");
663-
close(pipefd[1]);
664-
return -1;
665-
}
666-
close(pipefd[1]);
667-
668-
/* Suspend child until delivery of "SIGUSR1" from parent */
669-
sigsuspend(&sigact.sa_mask);
670-
671-
ksft_perror("Child is done");
672-
parent_exit(ppid);
608+
if (param->fill_buf)
609+
fill_cache_read(buf, param->fill_buf->buf_size, false);
610+
else if (uparams->benchmark_cmd[0])
611+
execvp(uparams->benchmark_cmd[0], (char **)uparams->benchmark_cmd);
612+
exit(EXIT_SUCCESS);
673613
}
674614

675615
ksft_print_msg("Benchmark PID: %d\n", (int)bm_pid);
676616

677-
value.sival_ptr = (void *)&benchmark_info;
678-
679-
/* Taskset benchmark to specified cpu */
680-
ret = taskset_benchmark(bm_pid, uparams->cpu, NULL);
681-
if (ret)
682-
goto out;
683-
684-
/* Write benchmark to specified control&monitoring grp in resctrl FS */
685-
ret = write_bm_pid_to_resctrl(bm_pid, param->ctrlgrp, param->mongrp);
686-
if (ret)
687-
goto out;
688-
689-
if (param->init) {
690-
ret = param->init(param, domain_id);
691-
if (ret)
692-
goto out;
693-
}
694-
695-
/* Parent waits for child to be ready. */
696-
close(pipefd[1]);
697-
while (pipe_message != 1) {
698-
if (read(pipefd[0], &pipe_message, sizeof(pipe_message)) <
699-
sizeof(pipe_message)) {
700-
ksft_perror("Failed reading message from child process");
701-
close(pipefd[0]);
702-
goto out;
703-
}
704-
}
705-
close(pipefd[0]);
706-
707-
/* Signal child to start benchmark */
708-
if (sigqueue(bm_pid, SIGUSR1, value) == -1) {
709-
ksft_perror("sigqueue SIGUSR1 to child");
710-
ret = -1;
711-
goto out;
712-
}
713-
714-
/* Give benchmark enough time to fully run */
617+
/* Give benchmark enough time to fully run. */
715618
sleep(1);
716619

717620
/* Test runs until the callback setup() tells the test to stop. */
@@ -729,8 +632,10 @@ int resctrl_val(const struct resctrl_test *test,
729632
break;
730633
}
731634

732-
out:
733635
kill(bm_pid, SIGKILL);
734-
636+
free_buf:
637+
free(buf);
638+
reset_affinity:
639+
taskset_restore(ppid, &old_affinity);
735640
return ret;
736641
}

0 commit comments

Comments
 (0)