Loading...
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
45static 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
74char *MAX_MSGS = "/proc/sys/fs/mqueue/msg_max";
75char *MAX_MSGSIZE = "/proc/sys/fs/mqueue/msgsize_max";
76
77#define MAX_CPUS 64
78char *cpu_option_string;
79int cpus_to_pin[MAX_CPUS];
80int num_cpus_to_pin;
81pthread_t cpu_threads[MAX_CPUS];
82pthread_t main_thread;
83cpu_set_t *cpu_set;
84int cpu_set_size;
85int cpus_online;
86
87#define MSG_SIZE 16
88#define TEST1_LOOPS 10000000
89#define TEST2_LOOPS 100000
90int continuous_mode;
91int continuous_mode_fake;
92
93struct rlimit saved_limits, cur_limits;
94int saved_max_msgs, saved_max_msgsize;
95int cur_max_msgs, cur_max_msgsize;
96FILE *max_msgs, *max_msgsize;
97int cur_nice;
98char *queue_path = "/mq_perf_tests";
99mqd_t queue = -1;
100struct mq_attr result;
101int mq_prio_max;
102
103const 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
153static inline void __set(FILE *stream, int value, char *err_msg);
154void shutdown(int exit_val, char *err_cause, int line_no);
155void sig_action_SIGUSR1(int signum, siginfo_t *info, void *context);
156void sig_action(int signum, siginfo_t *info, void *context);
157static inline int get(FILE *stream);
158static inline void set(FILE *stream, int value);
159static inline int try_set(FILE *stream, int value);
160static inline void getr(int type, struct rlimit *rlim);
161static inline void setr(int type, struct rlimit *rlim);
162static inline void open_queue(struct mq_attr *attr);
163void increase_limits(void);
164
165static 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
173void 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
213void 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
226void 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
238static 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
247static 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
260static 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
270static inline void getr(int type, struct rlimit *rlim)
271{
272 if (getrlimit(type, rlim))
273 shutdown(6, "getrlimit()", __LINE__);
274}
275
276static 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 */
292static 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
310void *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
323void *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
375struct test {
376 char *desc;
377 void (*func)(int *);
378};
379
380void const_prio(int *prio)
381{
382 return;
383}
384
385void inc_prio(int *prio)
386{
387 if (++*prio == mq_prio_max)
388 *prio = 0;
389}
390
391void dec_prio(int *prio)
392{
393 if (--*prio < 0)
394 *prio = mq_prio_max - 1;
395}
396
397void random_prio(int *prio)
398{
399 *prio = random() % mq_prio_max;
400}
401
402struct 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 */
426void *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
527void 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
543int 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
750err_code:
751 CPU_FREE(cpu_set);
752 exit(1);
753
754}
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
45static 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
74char *MAX_MSGS = "/proc/sys/fs/mqueue/msg_max";
75char *MAX_MSGSIZE = "/proc/sys/fs/mqueue/msgsize_max";
76
77#define MAX_CPUS 64
78char *cpu_option_string;
79int cpus_to_pin[MAX_CPUS];
80int num_cpus_to_pin;
81pthread_t cpu_threads[MAX_CPUS];
82pthread_t main_thread;
83cpu_set_t *cpu_set;
84int cpu_set_size;
85int cpus_online;
86
87#define MSG_SIZE 16
88#define TEST1_LOOPS 10000000
89#define TEST2_LOOPS 100000
90int continuous_mode;
91int continuous_mode_fake;
92
93struct rlimit saved_limits, cur_limits;
94int saved_max_msgs, saved_max_msgsize;
95int cur_max_msgs, cur_max_msgsize;
96FILE *max_msgs, *max_msgsize;
97int cur_nice;
98char *queue_path = "/mq_perf_tests";
99mqd_t queue = -1;
100struct mq_attr result;
101int mq_prio_max;
102
103const 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
153static inline void __set(FILE *stream, int value, char *err_msg);
154void shutdown(int exit_val, char *err_cause, int line_no);
155void sig_action_SIGUSR1(int signum, siginfo_t *info, void *context);
156void sig_action(int signum, siginfo_t *info, void *context);
157static inline int get(FILE *stream);
158static inline void set(FILE *stream, int value);
159static inline int try_set(FILE *stream, int value);
160static inline void getr(int type, struct rlimit *rlim);
161static inline void setr(int type, struct rlimit *rlim);
162static inline void open_queue(struct mq_attr *attr);
163void increase_limits(void);
164
165static 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
173void 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
213void 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
226void 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
238static 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
247static 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
260static 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
270static inline void getr(int type, struct rlimit *rlim)
271{
272 if (getrlimit(type, rlim))
273 shutdown(6, "getrlimit()", __LINE__);
274}
275
276static 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 */
292static 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
310void *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
323void *cont_thread(void *arg)
324{
325 char buff[MSG_SIZE];
326 int i, priority;
327
328 for (i = 0; i < num_cpus_to_pin; i++)
329 if (cpu_threads[i] == pthread_self())
330 break;
331 printf("\tStarted continuous mode thread %d on CPU %d\n", i,
332 cpus_to_pin[i]);
333 while (1) {
334 while (mq_send(queue, buff, sizeof(buff), 0) == 0)
335 ;
336 mq_receive(queue, buff, sizeof(buff), &priority);
337 }
338}
339
340#define drain_queue() \
341 while (mq_receive(queue, buff, MSG_SIZE, &prio_in) == MSG_SIZE)
342
343#define do_untimed_send() \
344 do { \
345 if (mq_send(queue, buff, MSG_SIZE, prio_out)) \
346 shutdown(3, "Test send failure", __LINE__); \
347 } while (0)
348
349#define do_send_recv() \
350 do { \
351 clock_gettime(clock, &start); \
352 if (mq_send(queue, buff, MSG_SIZE, prio_out)) \
353 shutdown(3, "Test send failure", __LINE__); \
354 clock_gettime(clock, &middle); \
355 if (mq_receive(queue, buff, MSG_SIZE, &prio_in) != MSG_SIZE) \
356 shutdown(3, "Test receive failure", __LINE__); \
357 clock_gettime(clock, &end); \
358 nsec = ((middle.tv_sec - start.tv_sec) * 1000000000) + \
359 (middle.tv_nsec - start.tv_nsec); \
360 send_total.tv_nsec += nsec; \
361 if (send_total.tv_nsec >= 1000000000) { \
362 send_total.tv_sec++; \
363 send_total.tv_nsec -= 1000000000; \
364 } \
365 nsec = ((end.tv_sec - middle.tv_sec) * 1000000000) + \
366 (end.tv_nsec - middle.tv_nsec); \
367 recv_total.tv_nsec += nsec; \
368 if (recv_total.tv_nsec >= 1000000000) { \
369 recv_total.tv_sec++; \
370 recv_total.tv_nsec -= 1000000000; \
371 } \
372 } while (0)
373
374struct test {
375 char *desc;
376 void (*func)(int *);
377};
378
379void const_prio(int *prio)
380{
381 return;
382}
383
384void inc_prio(int *prio)
385{
386 if (++*prio == mq_prio_max)
387 *prio = 0;
388}
389
390void dec_prio(int *prio)
391{
392 if (--*prio < 0)
393 *prio = mq_prio_max - 1;
394}
395
396void random_prio(int *prio)
397{
398 *prio = random() % mq_prio_max;
399}
400
401struct test test2[] = {
402 {"\n\tTest #2a: Time send/recv message, queue full, constant prio\n",
403 const_prio},
404 {"\n\tTest #2b: Time send/recv message, queue full, increasing prio\n",
405 inc_prio},
406 {"\n\tTest #2c: Time send/recv message, queue full, decreasing prio\n",
407 dec_prio},
408 {"\n\tTest #2d: Time send/recv message, queue full, random prio\n",
409 random_prio},
410 {NULL, NULL}
411};
412
413/**
414 * Tests to perform (all done with MSG_SIZE messages):
415 *
416 * 1) Time to add/remove message with 0 messages on queue
417 * 1a) with constant prio
418 * 2) Time to add/remove message when queue close to capacity:
419 * 2a) with constant prio
420 * 2b) with increasing prio
421 * 2c) with decreasing prio
422 * 2d) with random prio
423 * 3) Test limits of priorities honored (double check _SC_MQ_PRIO_MAX)
424 */
425void *perf_test_thread(void *arg)
426{
427 char buff[MSG_SIZE];
428 int prio_out, prio_in;
429 int i;
430 clockid_t clock;
431 pthread_t *t;
432 struct timespec res, start, middle, end, send_total, recv_total;
433 unsigned long long nsec;
434 struct test *cur_test;
435
436 t = &cpu_threads[0];
437 printf("\n\tStarted mqueue performance test thread on CPU %d\n",
438 cpus_to_pin[0]);
439 mq_prio_max = sysconf(_SC_MQ_PRIO_MAX);
440 if (mq_prio_max == -1)
441 shutdown(2, "sysconf(_SC_MQ_PRIO_MAX)", __LINE__);
442 if (pthread_getcpuclockid(cpu_threads[0], &clock) != 0)
443 shutdown(2, "pthread_getcpuclockid", __LINE__);
444
445 if (clock_getres(clock, &res))
446 shutdown(2, "clock_getres()", __LINE__);
447
448 printf("\t\tMax priorities:\t\t\t%d\n", mq_prio_max);
449 printf("\t\tClock resolution:\t\t%lu nsec%s\n", res.tv_nsec,
450 res.tv_nsec > 1 ? "s" : "");
451
452
453
454 printf("\n\tTest #1: Time send/recv message, queue empty\n");
455 printf("\t\t(%d iterations)\n", TEST1_LOOPS);
456 prio_out = 0;
457 send_total.tv_sec = 0;
458 send_total.tv_nsec = 0;
459 recv_total.tv_sec = 0;
460 recv_total.tv_nsec = 0;
461 for (i = 0; i < TEST1_LOOPS; i++)
462 do_send_recv();
463 printf("\t\tSend msg:\t\t\t%ld.%lus total time\n",
464 send_total.tv_sec, send_total.tv_nsec);
465 nsec = ((unsigned long long)send_total.tv_sec * 1000000000 +
466 send_total.tv_nsec) / TEST1_LOOPS;
467 printf("\t\t\t\t\t\t%lld nsec/msg\n", nsec);
468 printf("\t\tRecv msg:\t\t\t%ld.%lus total time\n",
469 recv_total.tv_sec, recv_total.tv_nsec);
470 nsec = ((unsigned long long)recv_total.tv_sec * 1000000000 +
471 recv_total.tv_nsec) / TEST1_LOOPS;
472 printf("\t\t\t\t\t\t%lld nsec/msg\n", nsec);
473
474
475 for (cur_test = test2; cur_test->desc != NULL; cur_test++) {
476 printf("%s:\n", cur_test->desc);
477 printf("\t\t(%d iterations)\n", TEST2_LOOPS);
478 prio_out = 0;
479 send_total.tv_sec = 0;
480 send_total.tv_nsec = 0;
481 recv_total.tv_sec = 0;
482 recv_total.tv_nsec = 0;
483 printf("\t\tFilling queue...");
484 fflush(stdout);
485 clock_gettime(clock, &start);
486 for (i = 0; i < result.mq_maxmsg - 1; i++) {
487 do_untimed_send();
488 cur_test->func(&prio_out);
489 }
490 clock_gettime(clock, &end);
491 nsec = ((unsigned long long)(end.tv_sec - start.tv_sec) *
492 1000000000) + (end.tv_nsec - start.tv_nsec);
493 printf("done.\t\t%lld.%llds\n", nsec / 1000000000,
494 nsec % 1000000000);
495 printf("\t\tTesting...");
496 fflush(stdout);
497 for (i = 0; i < TEST2_LOOPS; i++) {
498 do_send_recv();
499 cur_test->func(&prio_out);
500 }
501 printf("done.\n");
502 printf("\t\tSend msg:\t\t\t%ld.%lus total time\n",
503 send_total.tv_sec, send_total.tv_nsec);
504 nsec = ((unsigned long long)send_total.tv_sec * 1000000000 +
505 send_total.tv_nsec) / TEST2_LOOPS;
506 printf("\t\t\t\t\t\t%lld nsec/msg\n", nsec);
507 printf("\t\tRecv msg:\t\t\t%ld.%lus total time\n",
508 recv_total.tv_sec, recv_total.tv_nsec);
509 nsec = ((unsigned long long)recv_total.tv_sec * 1000000000 +
510 recv_total.tv_nsec) / TEST2_LOOPS;
511 printf("\t\t\t\t\t\t%lld nsec/msg\n", nsec);
512 printf("\t\tDraining queue...");
513 fflush(stdout);
514 clock_gettime(clock, &start);
515 drain_queue();
516 clock_gettime(clock, &end);
517 nsec = ((unsigned long long)(end.tv_sec - start.tv_sec) *
518 1000000000) + (end.tv_nsec - start.tv_nsec);
519 printf("done.\t\t%lld.%llds\n", nsec / 1000000000,
520 nsec % 1000000000);
521 }
522 return 0;
523}
524
525void increase_limits(void)
526{
527 cur_limits.rlim_cur = RLIM_INFINITY;
528 cur_limits.rlim_max = RLIM_INFINITY;
529 setr(RLIMIT_MSGQUEUE, &cur_limits);
530 while (try_set(max_msgs, cur_max_msgs += 10))
531 ;
532 cur_max_msgs = get(max_msgs);
533 while (try_set(max_msgsize, cur_max_msgsize += 1024))
534 ;
535 cur_max_msgsize = get(max_msgsize);
536 if (setpriority(PRIO_PROCESS, 0, -20) != 0)
537 shutdown(2, "setpriority()", __LINE__);
538 cur_nice = -20;
539}
540
541int main(int argc, char *argv[])
542{
543 struct mq_attr attr;
544 char *option, *next_option;
545 int i, cpu, rc;
546 struct sigaction sa;
547 poptContext popt_context;
548 void *retval;
549
550 main_thread = pthread_self();
551 num_cpus_to_pin = 0;
552
553 if (sysconf(_SC_NPROCESSORS_ONLN) == -1) {
554 perror("sysconf(_SC_NPROCESSORS_ONLN)");
555 exit(1);
556 }
557
558 if (getuid() != 0)
559 ksft_exit_skip("Not running as root, but almost all tests "
560 "require root in order to modify\nsystem settings. "
561 "Exiting.\n");
562
563 cpus_online = MIN(MAX_CPUS, sysconf(_SC_NPROCESSORS_ONLN));
564 cpu_set = CPU_ALLOC(cpus_online);
565 if (cpu_set == NULL) {
566 perror("CPU_ALLOC()");
567 exit(1);
568 }
569 cpu_set_size = CPU_ALLOC_SIZE(cpus_online);
570 CPU_ZERO_S(cpu_set_size, cpu_set);
571
572 popt_context = poptGetContext(NULL, argc, (const char **)argv,
573 options, 0);
574
575 while ((rc = poptGetNextOpt(popt_context)) > 0) {
576 switch (rc) {
577 case 'c':
578 continuous_mode = 1;
579 option = cpu_option_string;
580 do {
581 next_option = strchr(option, ',');
582 if (next_option)
583 *next_option = '\0';
584 cpu = atoi(option);
585 if (cpu >= cpus_online)
586 fprintf(stderr, "CPU %d exceeds "
587 "cpus online, ignoring.\n",
588 cpu);
589 else
590 cpus_to_pin[num_cpus_to_pin++] = cpu;
591 if (next_option)
592 option = ++next_option;
593 } while (next_option && num_cpus_to_pin < MAX_CPUS);
594 /* Double check that they didn't give us the same CPU
595 * more than once */
596 for (cpu = 0; cpu < num_cpus_to_pin; cpu++) {
597 if (CPU_ISSET_S(cpus_to_pin[cpu], cpu_set_size,
598 cpu_set)) {
599 fprintf(stderr, "Any given CPU may "
600 "only be given once.\n");
601 goto err_code;
602 } else
603 CPU_SET_S(cpus_to_pin[cpu],
604 cpu_set_size, cpu_set);
605 }
606 break;
607 case 'p':
608 /*
609 * Although we can create a msg queue with a
610 * non-absolute path name, unlink will fail. So,
611 * if the name doesn't start with a /, add one
612 * when we save it.
613 */
614 option = queue_path;
615 if (*option != '/') {
616 queue_path = malloc(strlen(option) + 2);
617 if (!queue_path) {
618 perror("malloc()");
619 goto err_code;
620 }
621 queue_path[0] = '/';
622 queue_path[1] = 0;
623 strcat(queue_path, option);
624 free(option);
625 }
626 break;
627 }
628 }
629
630 if (continuous_mode && num_cpus_to_pin == 0) {
631 fprintf(stderr, "Must pass at least one CPU to continuous "
632 "mode.\n");
633 poptPrintUsage(popt_context, stderr, 0);
634 goto err_code;
635 } else if (!continuous_mode) {
636 num_cpus_to_pin = 1;
637 cpus_to_pin[0] = cpus_online - 1;
638 }
639
640 max_msgs = fopen(MAX_MSGS, "r+");
641 max_msgsize = fopen(MAX_MSGSIZE, "r+");
642 if (!max_msgs)
643 shutdown(2, "Failed to open msg_max", __LINE__);
644 if (!max_msgsize)
645 shutdown(2, "Failed to open msgsize_max", __LINE__);
646
647 /* Load up the current system values for everything we can */
648 getr(RLIMIT_MSGQUEUE, &saved_limits);
649 cur_limits = saved_limits;
650 saved_max_msgs = cur_max_msgs = get(max_msgs);
651 saved_max_msgsize = cur_max_msgsize = get(max_msgsize);
652 errno = 0;
653 cur_nice = getpriority(PRIO_PROCESS, 0);
654 if (errno)
655 shutdown(2, "getpriority()", __LINE__);
656
657 /* Tell the user our initial state */
658 printf("\nInitial system state:\n");
659 printf("\tUsing queue path:\t\t\t%s\n", queue_path);
660 printf("\tRLIMIT_MSGQUEUE(soft):\t\t\t%ld\n",
661 (long) saved_limits.rlim_cur);
662 printf("\tRLIMIT_MSGQUEUE(hard):\t\t\t%ld\n",
663 (long) saved_limits.rlim_max);
664 printf("\tMaximum Message Size:\t\t\t%d\n", saved_max_msgsize);
665 printf("\tMaximum Queue Size:\t\t\t%d\n", saved_max_msgs);
666 printf("\tNice value:\t\t\t\t%d\n", cur_nice);
667 printf("\n");
668
669 increase_limits();
670
671 printf("Adjusted system state for testing:\n");
672 if (cur_limits.rlim_cur == RLIM_INFINITY) {
673 printf("\tRLIMIT_MSGQUEUE(soft):\t\t\t(unlimited)\n");
674 printf("\tRLIMIT_MSGQUEUE(hard):\t\t\t(unlimited)\n");
675 } else {
676 printf("\tRLIMIT_MSGQUEUE(soft):\t\t\t%ld\n",
677 (long) cur_limits.rlim_cur);
678 printf("\tRLIMIT_MSGQUEUE(hard):\t\t\t%ld\n",
679 (long) cur_limits.rlim_max);
680 }
681 printf("\tMaximum Message Size:\t\t\t%d\n", cur_max_msgsize);
682 printf("\tMaximum Queue Size:\t\t\t%d\n", cur_max_msgs);
683 printf("\tNice value:\t\t\t\t%d\n", cur_nice);
684 printf("\tContinuous mode:\t\t\t(%s)\n", continuous_mode ?
685 (continuous_mode_fake ? "fake mode" : "enabled") :
686 "disabled");
687 printf("\tCPUs to pin:\t\t\t\t%d", cpus_to_pin[0]);
688 for (cpu = 1; cpu < num_cpus_to_pin; cpu++)
689 printf(",%d", cpus_to_pin[cpu]);
690 printf("\n");
691
692 sa.sa_sigaction = sig_action_SIGUSR1;
693 sigemptyset(&sa.sa_mask);
694 sigaddset(&sa.sa_mask, SIGHUP);
695 sigaddset(&sa.sa_mask, SIGINT);
696 sigaddset(&sa.sa_mask, SIGQUIT);
697 sigaddset(&sa.sa_mask, SIGTERM);
698 sa.sa_flags = SA_SIGINFO;
699 if (sigaction(SIGUSR1, &sa, NULL) == -1)
700 shutdown(1, "sigaction(SIGUSR1)", __LINE__);
701 sa.sa_sigaction = sig_action;
702 if (sigaction(SIGHUP, &sa, NULL) == -1)
703 shutdown(1, "sigaction(SIGHUP)", __LINE__);
704 if (sigaction(SIGINT, &sa, NULL) == -1)
705 shutdown(1, "sigaction(SIGINT)", __LINE__);
706 if (sigaction(SIGQUIT, &sa, NULL) == -1)
707 shutdown(1, "sigaction(SIGQUIT)", __LINE__);
708 if (sigaction(SIGTERM, &sa, NULL) == -1)
709 shutdown(1, "sigaction(SIGTERM)", __LINE__);
710
711 if (!continuous_mode_fake) {
712 attr.mq_flags = O_NONBLOCK;
713 attr.mq_maxmsg = cur_max_msgs;
714 attr.mq_msgsize = MSG_SIZE;
715 open_queue(&attr);
716 }
717 for (i = 0; i < num_cpus_to_pin; i++) {
718 pthread_attr_t thread_attr;
719 void *thread_func;
720
721 if (continuous_mode_fake)
722 thread_func = &fake_cont_thread;
723 else if (continuous_mode)
724 thread_func = &cont_thread;
725 else
726 thread_func = &perf_test_thread;
727
728 CPU_ZERO_S(cpu_set_size, cpu_set);
729 CPU_SET_S(cpus_to_pin[i], cpu_set_size, cpu_set);
730 pthread_attr_init(&thread_attr);
731 pthread_attr_setaffinity_np(&thread_attr, cpu_set_size,
732 cpu_set);
733 if (pthread_create(&cpu_threads[i], &thread_attr, thread_func,
734 NULL))
735 shutdown(1, "pthread_create()", __LINE__);
736 pthread_attr_destroy(&thread_attr);
737 }
738
739 if (!continuous_mode) {
740 pthread_join(cpu_threads[0], &retval);
741 shutdown((long)retval, "perf_test_thread()", __LINE__);
742 } else {
743 while (1)
744 sleep(1);
745 }
746 shutdown(0, "", 0);
747
748err_code:
749 CPU_FREE(cpu_set);
750 exit(1);
751
752}