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