Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

kselftest/alsa: Run PCM tests for multiple cards in parallel

With each test taking 4 seconds the runtime of pcm-test can add up. Since
generally each card in the system is physically independent and will be
unaffected by what's going on with other cards we can mitigate this by
testing each card in parallel. Make a list of cards as we enumerate the
system and then start a thread for each, then join the threads to ensure
they have all finished. The threads each run the same tests we currently
run for each PCM on the card before exiting.

The list of PCMs is kept global since it helps with global operations
like working out our planned number of tests and identifying missing PCMs
and it seemed neater to check for PCMs on the right card in the card
thread than make every PCM loop iterate over cards as well.

We don't run per-PCM tests in parallel since in embedded systems it can
be the case that resources are shared between the PCMs and operations on
one PCM on a card may constrain what can be done on another PCM on the same
card leading to potentially unstable results.

We use a mutex to ensure that the reporting of results is serialised and we
don't have issues with anything like the current test number, we could do
this in the kselftest framework but it seems like this might cause problems
for other tests that are doing lower level testing and building in
constrained environments such as nolibc so this seems more sensible.

Note that the ordering of the tests can't be guaranteed as things stand,
this does not seem like a major problem since the numbering of tests often
changes as test programs are changed so results parsers are expected to
rely on the test name rather than the test numbers. We also now prefix the
machine generated test name when printing the description of the test since
this is logged before streaming starts.

On my two card desktop system this reduces the overall runtime by a
third.

Signed-off-by: Mark Brown <broonie@kernel.org>
Link: https://lore.kernel.org/r/20230203-alsa-pcm-test-card-thread-v1-1-59941640ebba@kernel.org
Signed-off-by: Takashi Iwai <tiwai@suse.de>

authored by

Mark Brown and committed by
Takashi Iwai
69218b59 0d9eb7ed

+74 -8
+2
tools/testing/selftests/alsa/Makefile
··· 8 8 endif 9 9 CFLAGS += -L$(OUTPUT) -Wl,-rpath=./ 10 10 11 + LDLIBS+=-lpthread 12 + 11 13 OVERRIDE_TARGETS = 1 12 14 13 15 TEST_GEN_PROGS := mixer-test pcm-test
+72 -8
tools/testing/selftests/alsa/pcm-test.c
··· 15 15 #include <stdbool.h> 16 16 #include <errno.h> 17 17 #include <assert.h> 18 + #include <pthread.h> 18 19 19 20 #include "../kselftest.h" 20 21 #include "alsa-local.h" 21 22 22 23 typedef struct timespec timestamp_t; 24 + 25 + struct card_data { 26 + int card; 27 + pthread_t thread; 28 + struct card_data *next; 29 + }; 30 + 31 + struct card_data *card_list = NULL; 23 32 24 33 struct pcm_data { 25 34 snd_pcm_t *handle; ··· 44 35 45 36 int num_missing = 0; 46 37 struct pcm_data *pcm_missing = NULL; 38 + 39 + snd_config_t *default_pcm_config; 40 + 41 + /* Lock while reporting results since kselftest doesn't */ 42 + pthread_mutex_t results_lock = PTHREAD_MUTEX_INITIALIZER; 47 43 48 44 enum test_class { 49 45 TEST_CLASS_DEFAULT, ··· 155 141 snd_ctl_t *handle; 156 142 snd_pcm_info_t *pcm_info; 157 143 snd_config_t *config, *card_config, *pcm_config; 144 + struct card_data *card_data; 158 145 159 146 snd_pcm_info_alloca(&pcm_info); 160 147 ··· 176 161 } 177 162 178 163 card_config = conf_by_card(card); 164 + 165 + card_data = calloc(1, sizeof(*card_data)); 166 + if (!card_data) 167 + ksft_exit_fail_msg("Out of memory\n"); 168 + card_data->card = card; 169 + card_data->next = card_list; 170 + card_list = card_data; 179 171 180 172 dev = -1; 181 173 while (1) { ··· 268 246 bool skip = true; 269 247 const char *desc; 270 248 271 - desc = conf_get_string(pcm_cfg, "description", NULL, NULL); 272 - if (desc) 273 - ksft_print_msg("%s\n", desc); 274 - 275 249 switch (class) { 276 250 case TEST_CLASS_DEFAULT: 277 251 test_class_name = "default"; ··· 279 261 ksft_exit_fail_msg("Unknown test class %d\n", class); 280 262 break; 281 263 } 264 + 265 + desc = conf_get_string(pcm_cfg, "description", NULL, NULL); 266 + if (desc) 267 + ksft_print_msg("%s.%s.%d.%d.%d.%s - %s\n", 268 + test_class_name, test_name, 269 + data->card, data->device, data->subdevice, 270 + snd_pcm_stream_name(data->stream), 271 + desc); 272 + 282 273 283 274 snd_pcm_hw_params_alloca(&hw_params); 284 275 snd_pcm_sw_params_alloca(&sw_params); ··· 470 443 msg[0] = '\0'; 471 444 pass = true; 472 445 __close: 446 + pthread_mutex_lock(&results_lock); 447 + 473 448 switch (class) { 474 449 case TEST_CLASS_SYSTEM: 475 450 test_class_name = "system"; ··· 500 471 data->card, data->device, data->subdevice, 501 472 snd_pcm_stream_name(data->stream), 502 473 msg[0] ? " " : "", msg); 474 + 475 + pthread_mutex_unlock(&results_lock); 476 + 503 477 free(samples); 504 478 if (handle) 505 479 snd_pcm_close(handle); ··· 534 502 } 535 503 } 536 504 505 + void *card_thread(void *data) 506 + { 507 + struct card_data *card = data; 508 + struct pcm_data *pcm; 509 + 510 + for (pcm = pcm_list; pcm != NULL; pcm = pcm->next) { 511 + if (pcm->card != card->card) 512 + continue; 513 + 514 + run_time_tests(pcm, TEST_CLASS_DEFAULT, default_pcm_config); 515 + run_time_tests(pcm, TEST_CLASS_SYSTEM, pcm->pcm_config); 516 + } 517 + 518 + return 0; 519 + } 520 + 537 521 int main(void) 538 522 { 523 + struct card_data *card; 539 524 struct pcm_data *pcm; 540 - snd_config_t *global_config, *default_pcm_config, *cfg, *pcm_cfg; 525 + snd_config_t *global_config, *cfg, *pcm_cfg; 541 526 int num_pcm_tests = 0, num_tests, num_std_pcm_tests; 527 + int ret; 528 + void *thread_ret; 542 529 543 530 ksft_print_header(); 544 531 ··· 591 540 snd_pcm_stream_name(pcm->stream)); 592 541 } 593 542 594 - for (pcm = pcm_list; pcm != NULL; pcm = pcm->next) { 595 - run_time_tests(pcm, TEST_CLASS_DEFAULT, default_pcm_config); 596 - run_time_tests(pcm, TEST_CLASS_SYSTEM, pcm->pcm_config); 543 + for (card = card_list; card != NULL; card = card->next) { 544 + ret = pthread_create(&card->thread, NULL, card_thread, card); 545 + if (ret != 0) { 546 + ksft_exit_fail_msg("Failed to create card %d thread: %d (%s)\n", 547 + card->card, ret, 548 + strerror(errno)); 549 + } 550 + } 551 + 552 + for (card = card_list; card != NULL; card = card->next) { 553 + ret = pthread_join(card->thread, &thread_ret); 554 + if (ret != 0) { 555 + ksft_exit_fail_msg("Failed to join card %d thread: %d (%s)\n", 556 + card->card, ret, 557 + strerror(errno)); 558 + } 597 559 } 598 560 599 561 snd_config_delete(global_config);