1 /* 2 * This application is Copyright 2012 Red Hat, Inc. 3 * Doug Ledford <dledford@redhat.com> 4 * 5 * mq_perf_tests is free software: you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation, version 3. 8 * 9 * mq_perf_tests is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * For the full text of the license, see <http://www.gnu.org/licenses/>. 15 * 16 * mq_perf_tests.c 17 * Tests various types of message queue workloads, concentrating on those 18 * situations that invole large message sizes, large message queue depths, 19 * or both, and reports back useful metrics about kernel message queue 20 * performance. 21 * 22 */ 23 #define _GNU_SOURCE 24 #include <stdio.h> 25 #include <stdlib.h> 26 #include <unistd.h> 27 #include <fcntl.h> 28 #include <string.h> 29 #include <limits.h> 30 #include <errno.h> 31 #include <signal.h> 32 #include <pthread.h> 33 #include <sched.h> 34 #include <sys/types.h> 35 #include <sys/time.h> 36 #include <sys/resource.h> 37 #include <sys/stat.h> 38 #include <sys/param.h> 39 #include <mqueue.h> 40 #include <popt.h> 41 #include <error.h> 42 43 #include "../kselftest.h" 44 45 static char *usage = 46 "Usage:\n" 47 " %s [-c #[,#..] -f] path\n" 48 "\n" 49 " -c # Skip most tests and go straight to a high queue depth test\n" 50 " and then run that test continuously (useful for running at\n" 51 " the same time as some other workload to see how much the\n" 52 " cache thrashing caused by adding messages to a very deep\n" 53 " queue impacts the performance of other programs). The number\n" 54 " indicates which CPU core we should bind the process to during\n" 55 " the run. If you have more than one physical CPU, then you\n" 56 " will need one copy per physical CPU package, and you should\n" 57 " specify the CPU cores to pin ourself to via a comma separated\n" 58 " list of CPU values.\n" 59 " -f Only usable with continuous mode. Pin ourself to the CPUs\n" 60 " as requested, then instead of looping doing a high mq\n" 61 " workload, just busy loop. This will allow us to lock up a\n" 62 " single CPU just like we normally would, but without actually\n" 63 " thrashing the CPU cache. This is to make it easier to get\n" 64 " comparable numbers from some other workload running on the\n" 65 " other CPUs. One set of numbers with # CPUs locked up running\n" 66 " an mq workload, and another set of numbers with those same\n" 67 " CPUs locked away from the test workload, but not doing\n" 68 " anything to trash the cache like the mq workload might.\n" 69 " path Path name of the message queue to create\n" 70 "\n" 71 " Note: this program must be run as root in order to enable all tests\n" 72 "\n"; 73 74 char *MAX_MSGS = "/proc/sys/fs/mqueue/msg_max"; 75 char *MAX_MSGSIZE = "/proc/sys/fs/mqueue/msgsize_max"; 76 77 #define MAX_CPUS 64 78 char *cpu_option_string; 79 int cpus_to_pin[MAX_CPUS]; 80 int num_cpus_to_pin; 81 pthread_t cpu_threads[MAX_CPUS]; 82 pthread_t main_thread; 83 cpu_set_t *cpu_set; 84 int cpu_set_size; 85 int cpus_online; 86 87 #define MSG_SIZE 16 88 #define TEST1_LOOPS 10000000 89 #define TEST2_LOOPS 100000 90 int continuous_mode; 91 int continuous_mode_fake; 92 93 struct rlimit saved_limits, cur_limits; 94 int saved_max_msgs, saved_max_msgsize; 95 int cur_max_msgs, cur_max_msgsize; 96 FILE *max_msgs, *max_msgsize; 97 int cur_nice; 98 char *queue_path = "/mq_perf_tests"; 99 mqd_t queue = -1; 100 struct mq_attr result; 101 int mq_prio_max; 102 103 const struct poptOption options[] = { 104 { 105 .longName = "continuous", 106 .shortName = 'c', 107 .argInfo = POPT_ARG_STRING, 108 .arg = &cpu_option_string, 109 .val = 'c', 110 .descrip = "Run continuous tests at a high queue depth in " 111 "order to test the effects of cache thrashing on " 112 "other tasks on the system. This test is intended " 113 "to be run on one core of each physical CPU while " 114 "some other CPU intensive task is run on all the other " 115 "cores of that same physical CPU and the other task " 116 "is timed. It is assumed that the process of adding " 117 "messages to the message queue in a tight loop will " 118 "impact that other task to some degree. Once the " 119 "tests are performed in this way, you should then " 120 "re-run the tests using fake mode in order to check " 121 "the difference in time required to perform the CPU " 122 "intensive task", 123 .argDescrip = "cpu[,cpu]", 124 }, 125 { 126 .longName = "fake", 127 .shortName = 'f', 128 .argInfo = POPT_ARG_NONE, 129 .arg = &continuous_mode_fake, 130 .val = 0, 131 .descrip = "Tie up the CPUs that we would normally tie up in" 132 "continuous mode, but don't actually do any mq stuff, " 133 "just keep the CPU busy so it can't be used to process " 134 "system level tasks as this would free up resources on " 135 "the other CPU cores and skew the comparison between " 136 "the no-mqueue work and mqueue work tests", 137 .argDescrip = NULL, 138 }, 139 { 140 .longName = "path", 141 .shortName = 'p', 142 .argInfo = POPT_ARG_STRING | POPT_ARGFLAG_SHOW_DEFAULT, 143 .arg = &queue_path, 144 .val = 'p', 145 .descrip = "The name of the path to use in the mqueue " 146 "filesystem for our tests", 147 .argDescrip = "pathname", 148 }, 149 POPT_AUTOHELP 150 POPT_TABLEEND 151 }; 152 153 static inline void __set(FILE *stream, int value, char *err_msg); 154 void shutdown(int exit_val, char *err_cause, int line_no); 155 void sig_action_SIGUSR1(int signum, siginfo_t *info, void *context); 156 void sig_action(int signum, siginfo_t *info, void *context); 157 static inline int get(FILE *stream); 158 static inline void set(FILE *stream, int value); 159 static inline int try_set(FILE *stream, int value); 160 static inline void getr(int type, struct rlimit *rlim); 161 static inline void setr(int type, struct rlimit *rlim); 162 static inline void open_queue(struct mq_attr *attr); 163 void increase_limits(void); 164 165 static inline void __set(FILE *stream, int value, char *err_msg) 166 { 167 rewind(stream); 168 if (fprintf(stream, "%d", value) < 0) 169 perror(err_msg); 170 } 171 172 173 void shutdown(int exit_val, char *err_cause, int line_no) 174 { 175 static int in_shutdown = 0; 176 int errno_at_shutdown = errno; 177 int i; 178 179 /* In case we get called by multiple threads or from an sighandler */ 180 if (in_shutdown++) 181 return; 182 183 /* Free the cpu_set allocated using CPU_ALLOC in main function */ 184 CPU_FREE(cpu_set); 185 186 for (i = 0; i < num_cpus_to_pin; i++) 187 if (cpu_threads[i]) { 188 pthread_kill(cpu_threads[i], SIGUSR1); 189 pthread_join(cpu_threads[i], NULL); 190 } 191 192 if (queue != -1) 193 if (mq_close(queue)) 194 perror("mq_close() during shutdown"); 195 if (queue_path) 196 /* 197 * Be silent if this fails, if we cleaned up already it's 198 * expected to fail 199 */ 200 mq_unlink(queue_path); 201 if (saved_max_msgs) 202 __set(max_msgs, saved_max_msgs, 203 "failed to restore saved_max_msgs"); 204 if (saved_max_msgsize) 205 __set(max_msgsize, saved_max_msgsize, 206 "failed to restore saved_max_msgsize"); 207 if (exit_val) 208 error(exit_val, errno_at_shutdown, "%s at %d", 209 err_cause, line_no); 210 exit(0); 211 } 212 213 void sig_action_SIGUSR1(int signum, siginfo_t *info, void *context) 214 { 215 if (pthread_self() != main_thread) 216 pthread_exit(0); 217 else { 218 fprintf(stderr, "Caught signal %d in SIGUSR1 handler, " 219 "exiting\n", signum); 220 shutdown(0, "", 0); 221 fprintf(stderr, "\n\nReturned from shutdown?!?!\n\n"); 222 exit(0); 223 } 224 } 225 226 void sig_action(int signum, siginfo_t *info, void *context) 227 { 228 if (pthread_self() != main_thread) 229 pthread_kill(main_thread, signum); 230 else { 231 fprintf(stderr, "Caught signal %d, exiting\n", signum); 232 shutdown(0, "", 0); 233 fprintf(stderr, "\n\nReturned from shutdown?!?!\n\n"); 234 exit(0); 235 } 236 } 237 238 static inline int get(FILE *stream) 239 { 240 int value; 241 rewind(stream); 242 if (fscanf(stream, "%d", &value) != 1) 243 shutdown(4, "Error reading /proc entry", __LINE__); 244 return value; 245 } 246 247 static inline void set(FILE *stream, int value) 248 { 249 int new_value; 250 251 rewind(stream); 252 if (fprintf(stream, "%d", value) < 0) 253 return shutdown(5, "Failed writing to /proc file", __LINE__); 254 new_value = get(stream); 255 if (new_value != value) 256 return shutdown(5, "We didn't get what we wrote to /proc back", 257 __LINE__); 258 } 259 260 static inline int try_set(FILE *stream, int value) 261 { 262 int new_value; 263 264 rewind(stream); 265 fprintf(stream, "%d", value); 266 new_value = get(stream); 267 return new_value == value; 268 } 269 270 static inline void getr(int type, struct rlimit *rlim) 271 { 272 if (getrlimit(type, rlim)) 273 shutdown(6, "getrlimit()", __LINE__); 274 } 275 276 static inline void setr(int type, struct rlimit *rlim) 277 { 278 if (setrlimit(type, rlim)) 279 shutdown(7, "setrlimit()", __LINE__); 280 } 281 282 /** 283 * open_queue - open the global queue for testing 284 * @attr - An attr struct specifying the desired queue traits 285 * @result - An attr struct that lists the actual traits the queue has 286 * 287 * This open is not allowed to fail, failure will result in an orderly 288 * shutdown of the program. The global queue_path is used to set what 289 * queue to open, the queue descriptor is saved in the global queue 290 * variable. 291 */ 292 static inline void open_queue(struct mq_attr *attr) 293 { 294 int flags = O_RDWR | O_EXCL | O_CREAT | O_NONBLOCK; 295 int perms = DEFFILEMODE; 296 297 queue = mq_open(queue_path, flags, perms, attr); 298 if (queue == -1) 299 shutdown(1, "mq_open()", __LINE__); 300 if (mq_getattr(queue, &result)) 301 shutdown(1, "mq_getattr()", __LINE__); 302 printf("\n\tQueue %s created:\n", queue_path); 303 printf("\t\tmq_flags:\t\t\t%s\n", result.mq_flags & O_NONBLOCK ? 304 "O_NONBLOCK" : "(null)"); 305 printf("\t\tmq_maxmsg:\t\t\t%lu\n", result.mq_maxmsg); 306 printf("\t\tmq_msgsize:\t\t\t%lu\n", result.mq_msgsize); 307 printf("\t\tmq_curmsgs:\t\t\t%lu\n", result.mq_curmsgs); 308 } 309 310 void *fake_cont_thread(void *arg) 311 { 312 int i; 313 314 for (i = 0; i < num_cpus_to_pin; i++) 315 if (cpu_threads[i] == pthread_self()) 316 break; 317 printf("\tStarted fake continuous mode thread %d on CPU %d\n", i, 318 cpus_to_pin[i]); 319 while (1) 320 ; 321 } 322 323 void *cont_thread(void *arg) 324 { 325 char buff[MSG_SIZE]; 326 int i; 327 unsigned int priority; 328 329 for (i = 0; i < num_cpus_to_pin; i++) 330 if (cpu_threads[i] == pthread_self()) 331 break; 332 printf("\tStarted continuous mode thread %d on CPU %d\n", i, 333 cpus_to_pin[i]); 334 while (1) { 335 while (mq_send(queue, buff, sizeof(buff), 0) == 0) 336 ; 337 mq_receive(queue, buff, sizeof(buff), &priority); 338 } 339 } 340 341 #define drain_queue() \ 342 while (mq_receive(queue, buff, MSG_SIZE, &prio_in) == MSG_SIZE) 343 344 #define do_untimed_send() \ 345 do { \ 346 if (mq_send(queue, buff, MSG_SIZE, prio_out)) \ 347 shutdown(3, "Test send failure", __LINE__); \ 348 } while (0) 349 350 #define do_send_recv() \ 351 do { \ 352 clock_gettime(clock, &start); \ 353 if (mq_send(queue, buff, MSG_SIZE, prio_out)) \ 354 shutdown(3, "Test send failure", __LINE__); \ 355 clock_gettime(clock, &middle); \ 356 if (mq_receive(queue, buff, MSG_SIZE, &prio_in) != MSG_SIZE) \ 357 shutdown(3, "Test receive failure", __LINE__); \ 358 clock_gettime(clock, &end); \ 359 nsec = ((middle.tv_sec - start.tv_sec) * 1000000000) + \ 360 (middle.tv_nsec - start.tv_nsec); \ 361 send_total.tv_nsec += nsec; \ 362 if (send_total.tv_nsec >= 1000000000) { \ 363 send_total.tv_sec++; \ 364 send_total.tv_nsec -= 1000000000; \ 365 } \ 366 nsec = ((end.tv_sec - middle.tv_sec) * 1000000000) + \ 367 (end.tv_nsec - middle.tv_nsec); \ 368 recv_total.tv_nsec += nsec; \ 369 if (recv_total.tv_nsec >= 1000000000) { \ 370 recv_total.tv_sec++; \ 371 recv_total.tv_nsec -= 1000000000; \ 372 } \ 373 } while (0) 374 375 struct test { 376 char *desc; 377 void (*func)(int *); 378 }; 379 380 void const_prio(int *prio) 381 { 382 return; 383 } 384 385 void inc_prio(int *prio) 386 { 387 if (++*prio == mq_prio_max) 388 *prio = 0; 389 } 390 391 void dec_prio(int *prio) 392 { 393 if (--*prio < 0) 394 *prio = mq_prio_max - 1; 395 } 396 397 void random_prio(int *prio) 398 { 399 *prio = random() % mq_prio_max; 400 } 401 402 struct test test2[] = { 403 {"\n\tTest #2a: Time send/recv message, queue full, constant prio\n", 404 const_prio}, 405 {"\n\tTest #2b: Time send/recv message, queue full, increasing prio\n", 406 inc_prio}, 407 {"\n\tTest #2c: Time send/recv message, queue full, decreasing prio\n", 408 dec_prio}, 409 {"\n\tTest #2d: Time send/recv message, queue full, random prio\n", 410 random_prio}, 411 {NULL, NULL} 412 }; 413 414 /** 415 * Tests to perform (all done with MSG_SIZE messages): 416 * 417 * 1) Time to add/remove message with 0 messages on queue 418 * 1a) with constant prio 419 * 2) Time to add/remove message when queue close to capacity: 420 * 2a) with constant prio 421 * 2b) with increasing prio 422 * 2c) with decreasing prio 423 * 2d) with random prio 424 * 3) Test limits of priorities honored (double check _SC_MQ_PRIO_MAX) 425 */ 426 void *perf_test_thread(void *arg) 427 { 428 char buff[MSG_SIZE]; 429 int prio_out; 430 unsigned int prio_in; 431 int i; 432 clockid_t clock; 433 pthread_t *t; 434 struct timespec res, start, middle, end, send_total, recv_total; 435 unsigned long long nsec; 436 struct test *cur_test; 437 438 t = &cpu_threads[0]; 439 printf("\n\tStarted mqueue performance test thread on CPU %d\n", 440 cpus_to_pin[0]); 441 mq_prio_max = sysconf(_SC_MQ_PRIO_MAX); 442 if (mq_prio_max == -1) 443 shutdown(2, "sysconf(_SC_MQ_PRIO_MAX)", __LINE__); 444 if (pthread_getcpuclockid(cpu_threads[0], &clock) != 0) 445 shutdown(2, "pthread_getcpuclockid", __LINE__); 446 447 if (clock_getres(clock, &res)) 448 shutdown(2, "clock_getres()", __LINE__); 449 450 printf("\t\tMax priorities:\t\t\t%d\n", mq_prio_max); 451 printf("\t\tClock resolution:\t\t%lu nsec%s\n", res.tv_nsec, 452 res.tv_nsec > 1 ? "s" : ""); 453 454 455 456 printf("\n\tTest #1: Time send/recv message, queue empty\n"); 457 printf("\t\t(%d iterations)\n", TEST1_LOOPS); 458 prio_out = 0; 459 send_total.tv_sec = 0; 460 send_total.tv_nsec = 0; 461 recv_total.tv_sec = 0; 462 recv_total.tv_nsec = 0; 463 for (i = 0; i < TEST1_LOOPS; i++) 464 do_send_recv(); 465 printf("\t\tSend msg:\t\t\t%ld.%lus total time\n", 466 send_total.tv_sec, send_total.tv_nsec); 467 nsec = ((unsigned long long)send_total.tv_sec * 1000000000 + 468 send_total.tv_nsec) / TEST1_LOOPS; 469 printf("\t\t\t\t\t\t%lld nsec/msg\n", nsec); 470 printf("\t\tRecv msg:\t\t\t%ld.%lus total time\n", 471 recv_total.tv_sec, recv_total.tv_nsec); 472 nsec = ((unsigned long long)recv_total.tv_sec * 1000000000 + 473 recv_total.tv_nsec) / TEST1_LOOPS; 474 printf("\t\t\t\t\t\t%lld nsec/msg\n", nsec); 475 476 477 for (cur_test = test2; cur_test->desc != NULL; cur_test++) { 478 printf("%s:\n", cur_test->desc); 479 printf("\t\t(%d iterations)\n", TEST2_LOOPS); 480 prio_out = 0; 481 send_total.tv_sec = 0; 482 send_total.tv_nsec = 0; 483 recv_total.tv_sec = 0; 484 recv_total.tv_nsec = 0; 485 printf("\t\tFilling queue..."); 486 fflush(stdout); 487 clock_gettime(clock, &start); 488 for (i = 0; i < result.mq_maxmsg - 1; i++) { 489 do_untimed_send(); 490 cur_test->func(&prio_out); 491 } 492 clock_gettime(clock, &end); 493 nsec = ((unsigned long long)(end.tv_sec - start.tv_sec) * 494 1000000000) + (end.tv_nsec - start.tv_nsec); 495 printf("done.\t\t%lld.%llds\n", nsec / 1000000000, 496 nsec % 1000000000); 497 printf("\t\tTesting..."); 498 fflush(stdout); 499 for (i = 0; i < TEST2_LOOPS; i++) { 500 do_send_recv(); 501 cur_test->func(&prio_out); 502 } 503 printf("done.\n"); 504 printf("\t\tSend msg:\t\t\t%ld.%lus total time\n", 505 send_total.tv_sec, send_total.tv_nsec); 506 nsec = ((unsigned long long)send_total.tv_sec * 1000000000 + 507 send_total.tv_nsec) / TEST2_LOOPS; 508 printf("\t\t\t\t\t\t%lld nsec/msg\n", nsec); 509 printf("\t\tRecv msg:\t\t\t%ld.%lus total time\n", 510 recv_total.tv_sec, recv_total.tv_nsec); 511 nsec = ((unsigned long long)recv_total.tv_sec * 1000000000 + 512 recv_total.tv_nsec) / TEST2_LOOPS; 513 printf("\t\t\t\t\t\t%lld nsec/msg\n", nsec); 514 printf("\t\tDraining queue..."); 515 fflush(stdout); 516 clock_gettime(clock, &start); 517 drain_queue(); 518 clock_gettime(clock, &end); 519 nsec = ((unsigned long long)(end.tv_sec - start.tv_sec) * 520 1000000000) + (end.tv_nsec - start.tv_nsec); 521 printf("done.\t\t%lld.%llds\n", nsec / 1000000000, 522 nsec % 1000000000); 523 } 524 return 0; 525 } 526 527 void increase_limits(void) 528 { 529 cur_limits.rlim_cur = RLIM_INFINITY; 530 cur_limits.rlim_max = RLIM_INFINITY; 531 setr(RLIMIT_MSGQUEUE, &cur_limits); 532 while (try_set(max_msgs, cur_max_msgs += 10)) 533 ; 534 cur_max_msgs = get(max_msgs); 535 while (try_set(max_msgsize, cur_max_msgsize += 1024)) 536 ; 537 cur_max_msgsize = get(max_msgsize); 538 if (setpriority(PRIO_PROCESS, 0, -20) != 0) 539 shutdown(2, "setpriority()", __LINE__); 540 cur_nice = -20; 541 } 542 543 int main(int argc, char *argv[]) 544 { 545 struct mq_attr attr; 546 char *option, *next_option; 547 int i, cpu, rc; 548 struct sigaction sa; 549 poptContext popt_context; 550 void *retval; 551 552 main_thread = pthread_self(); 553 num_cpus_to_pin = 0; 554 555 if (sysconf(_SC_NPROCESSORS_ONLN) == -1) { 556 perror("sysconf(_SC_NPROCESSORS_ONLN)"); 557 exit(1); 558 } 559 560 if (getuid() != 0) 561 ksft_exit_skip("Not running as root, but almost all tests " 562 "require root in order to modify\nsystem settings. " 563 "Exiting.\n"); 564 565 cpus_online = MIN(MAX_CPUS, sysconf(_SC_NPROCESSORS_ONLN)); 566 cpu_set = CPU_ALLOC(cpus_online); 567 if (cpu_set == NULL) { 568 perror("CPU_ALLOC()"); 569 exit(1); 570 } 571 cpu_set_size = CPU_ALLOC_SIZE(cpus_online); 572 CPU_ZERO_S(cpu_set_size, cpu_set); 573 574 popt_context = poptGetContext(NULL, argc, (const char **)argv, 575 options, 0); 576 577 while ((rc = poptGetNextOpt(popt_context)) > 0) { 578 switch (rc) { 579 case 'c': 580 continuous_mode = 1; 581 option = cpu_option_string; 582 do { 583 next_option = strchr(option, ','); 584 if (next_option) 585 *next_option = '\0'; 586 cpu = atoi(option); 587 if (cpu >= cpus_online) 588 fprintf(stderr, "CPU %d exceeds " 589 "cpus online, ignoring.\n", 590 cpu); 591 else 592 cpus_to_pin[num_cpus_to_pin++] = cpu; 593 if (next_option) 594 option = ++next_option; 595 } while (next_option && num_cpus_to_pin < MAX_CPUS); 596 /* Double check that they didn't give us the same CPU 597 * more than once */ 598 for (cpu = 0; cpu < num_cpus_to_pin; cpu++) { 599 if (CPU_ISSET_S(cpus_to_pin[cpu], cpu_set_size, 600 cpu_set)) { 601 fprintf(stderr, "Any given CPU may " 602 "only be given once.\n"); 603 goto err_code; 604 } else 605 CPU_SET_S(cpus_to_pin[cpu], 606 cpu_set_size, cpu_set); 607 } 608 break; 609 case 'p': 610 /* 611 * Although we can create a msg queue with a 612 * non-absolute path name, unlink will fail. So, 613 * if the name doesn't start with a /, add one 614 * when we save it. 615 */ 616 option = queue_path; 617 if (*option != '/') { 618 queue_path = malloc(strlen(option) + 2); 619 if (!queue_path) { 620 perror("malloc()"); 621 goto err_code; 622 } 623 queue_path[0] = '/'; 624 queue_path[1] = 0; 625 strcat(queue_path, option); 626 free(option); 627 } 628 break; 629 } 630 } 631 632 if (continuous_mode && num_cpus_to_pin == 0) { 633 fprintf(stderr, "Must pass at least one CPU to continuous " 634 "mode.\n"); 635 poptPrintUsage(popt_context, stderr, 0); 636 goto err_code; 637 } else if (!continuous_mode) { 638 num_cpus_to_pin = 1; 639 cpus_to_pin[0] = cpus_online - 1; 640 } 641 642 max_msgs = fopen(MAX_MSGS, "r+"); 643 max_msgsize = fopen(MAX_MSGSIZE, "r+"); 644 if (!max_msgs) 645 shutdown(2, "Failed to open msg_max", __LINE__); 646 if (!max_msgsize) 647 shutdown(2, "Failed to open msgsize_max", __LINE__); 648 649 /* Load up the current system values for everything we can */ 650 getr(RLIMIT_MSGQUEUE, &saved_limits); 651 cur_limits = saved_limits; 652 saved_max_msgs = cur_max_msgs = get(max_msgs); 653 saved_max_msgsize = cur_max_msgsize = get(max_msgsize); 654 errno = 0; 655 cur_nice = getpriority(PRIO_PROCESS, 0); 656 if (errno) 657 shutdown(2, "getpriority()", __LINE__); 658 659 /* Tell the user our initial state */ 660 printf("\nInitial system state:\n"); 661 printf("\tUsing queue path:\t\t\t%s\n", queue_path); 662 printf("\tRLIMIT_MSGQUEUE(soft):\t\t\t%ld\n", 663 (long) saved_limits.rlim_cur); 664 printf("\tRLIMIT_MSGQUEUE(hard):\t\t\t%ld\n", 665 (long) saved_limits.rlim_max); 666 printf("\tMaximum Message Size:\t\t\t%d\n", saved_max_msgsize); 667 printf("\tMaximum Queue Size:\t\t\t%d\n", saved_max_msgs); 668 printf("\tNice value:\t\t\t\t%d\n", cur_nice); 669 printf("\n"); 670 671 increase_limits(); 672 673 printf("Adjusted system state for testing:\n"); 674 if (cur_limits.rlim_cur == RLIM_INFINITY) { 675 printf("\tRLIMIT_MSGQUEUE(soft):\t\t\t(unlimited)\n"); 676 printf("\tRLIMIT_MSGQUEUE(hard):\t\t\t(unlimited)\n"); 677 } else { 678 printf("\tRLIMIT_MSGQUEUE(soft):\t\t\t%ld\n", 679 (long) cur_limits.rlim_cur); 680 printf("\tRLIMIT_MSGQUEUE(hard):\t\t\t%ld\n", 681 (long) cur_limits.rlim_max); 682 } 683 printf("\tMaximum Message Size:\t\t\t%d\n", cur_max_msgsize); 684 printf("\tMaximum Queue Size:\t\t\t%d\n", cur_max_msgs); 685 printf("\tNice value:\t\t\t\t%d\n", cur_nice); 686 printf("\tContinuous mode:\t\t\t(%s)\n", continuous_mode ? 687 (continuous_mode_fake ? "fake mode" : "enabled") : 688 "disabled"); 689 printf("\tCPUs to pin:\t\t\t\t%d", cpus_to_pin[0]); 690 for (cpu = 1; cpu < num_cpus_to_pin; cpu++) 691 printf(",%d", cpus_to_pin[cpu]); 692 printf("\n"); 693 694 sa.sa_sigaction = sig_action_SIGUSR1; 695 sigemptyset(&sa.sa_mask); 696 sigaddset(&sa.sa_mask, SIGHUP); 697 sigaddset(&sa.sa_mask, SIGINT); 698 sigaddset(&sa.sa_mask, SIGQUIT); 699 sigaddset(&sa.sa_mask, SIGTERM); 700 sa.sa_flags = SA_SIGINFO; 701 if (sigaction(SIGUSR1, &sa, NULL) == -1) 702 shutdown(1, "sigaction(SIGUSR1)", __LINE__); 703 sa.sa_sigaction = sig_action; 704 if (sigaction(SIGHUP, &sa, NULL) == -1) 705 shutdown(1, "sigaction(SIGHUP)", __LINE__); 706 if (sigaction(SIGINT, &sa, NULL) == -1) 707 shutdown(1, "sigaction(SIGINT)", __LINE__); 708 if (sigaction(SIGQUIT, &sa, NULL) == -1) 709 shutdown(1, "sigaction(SIGQUIT)", __LINE__); 710 if (sigaction(SIGTERM, &sa, NULL) == -1) 711 shutdown(1, "sigaction(SIGTERM)", __LINE__); 712 713 if (!continuous_mode_fake) { 714 attr.mq_flags = O_NONBLOCK; 715 attr.mq_maxmsg = cur_max_msgs; 716 attr.mq_msgsize = MSG_SIZE; 717 open_queue(&attr); 718 } 719 for (i = 0; i < num_cpus_to_pin; i++) { 720 pthread_attr_t thread_attr; 721 void *thread_func; 722 723 if (continuous_mode_fake) 724 thread_func = &fake_cont_thread; 725 else if (continuous_mode) 726 thread_func = &cont_thread; 727 else 728 thread_func = &perf_test_thread; 729 730 CPU_ZERO_S(cpu_set_size, cpu_set); 731 CPU_SET_S(cpus_to_pin[i], cpu_set_size, cpu_set); 732 pthread_attr_init(&thread_attr); 733 pthread_attr_setaffinity_np(&thread_attr, cpu_set_size, 734 cpu_set); 735 if (pthread_create(&cpu_threads[i], &thread_attr, thread_func, 736 NULL)) 737 shutdown(1, "pthread_create()", __LINE__); 738 pthread_attr_destroy(&thread_attr); 739 } 740 741 if (!continuous_mode) { 742 pthread_join(cpu_threads[0], &retval); 743 shutdown((long)retval, "perf_test_thread()", __LINE__); 744 } else { 745 while (1) 746 sleep(1); 747 } 748 shutdown(0, "", 0); 749 750 err_code: 751 CPU_FREE(cpu_set); 752 exit(1); 753 754 } 755