| 1 | |
| 2 | * This application is Copyright 2012 Red Hat, Inc.
|
| 3 | * Doug Ledford <[email protected]>
|
| 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 |
|
| 180 | if (in_shutdown++)
|
| 181 | return;
|
| 182 |
|
| 183 |
|
| 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 | |
| 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 |
|
| 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 |
|
| 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 | }
|