diff options
author | Doug Ledford <dledford@redhat.com> | 2012-05-31 19:26:37 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-05-31 20:49:31 -0400 |
commit | 7820b0715b6fb1378fab41b27fb7aa3950852cb7 (patch) | |
tree | f5720d49643717253c6c427fab45300ba4818bdf /tools | |
parent | 113289cc086f80f28acd06f160a7c6423cdd4191 (diff) |
tools/selftests: add mq_perf_tests
Add the mq_perf_tests tool I used when creating my mq performance patch.
Also add a local .gitignore to keep the binaries from showing up in git
status output.
[akpm@linux-foundation.org: checkpatch fixes]
Signed-off-by: Doug Ledford <dledford@redhat.com>
Cc: Stephen Rothwell <sfr@canb.auug.org.au>
Cc: Manfred Spraul <manfred@colorfullife.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Acked-by: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'tools')
-rw-r--r-- | tools/testing/selftests/mqueue/.gitignore | 2 | ||||
-rw-r--r-- | tools/testing/selftests/mqueue/Makefile | 4 | ||||
-rw-r--r-- | tools/testing/selftests/mqueue/mq_perf_tests.c | 741 |
3 files changed, 746 insertions, 1 deletions
diff --git a/tools/testing/selftests/mqueue/.gitignore b/tools/testing/selftests/mqueue/.gitignore new file mode 100644 index 000000000000..d8d42377205a --- /dev/null +++ b/tools/testing/selftests/mqueue/.gitignore | |||
@@ -0,0 +1,2 @@ | |||
1 | mq_open_tests | ||
2 | mq_perf_tests | ||
diff --git a/tools/testing/selftests/mqueue/Makefile b/tools/testing/selftests/mqueue/Makefile index bd74142a173f..54c0aad2b47c 100644 --- a/tools/testing/selftests/mqueue/Makefile +++ b/tools/testing/selftests/mqueue/Makefile | |||
@@ -1,8 +1,10 @@ | |||
1 | all: | 1 | all: |
2 | gcc -O2 -lrt mq_open_tests.c -o mq_open_tests | 2 | gcc -O2 -lrt mq_open_tests.c -o mq_open_tests |
3 | gcc -O2 -lrt -lpthread -lpopt -o mq_perf_tests mq_perf_tests.c | ||
3 | 4 | ||
4 | run_tests: | 5 | run_tests: |
5 | ./mq_open_tests /test1 | 6 | ./mq_open_tests /test1 |
7 | ./mq_perf_tests | ||
6 | 8 | ||
7 | clean: | 9 | clean: |
8 | rm -f mq_open_tests | 10 | rm -f mq_open_tests mq_perf_tests |
diff --git a/tools/testing/selftests/mqueue/mq_perf_tests.c b/tools/testing/selftests/mqueue/mq_perf_tests.c new file mode 100644 index 000000000000..2fadd4b97045 --- /dev/null +++ b/tools/testing/selftests/mqueue/mq_perf_tests.c | |||
@@ -0,0 +1,741 @@ | |||
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)"); | ||
299 | printf("\t\tmq_maxmsg:\t\t\t%d\n", result.mq_maxmsg); | ||
300 | printf("\t\tmq_msgsize:\t\t\t%d\n", result.mq_msgsize); | ||
301 | printf("\t\tmq_curmsgs:\t\t\t%d\n", result.mq_curmsgs); | ||
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); | ||
443 | printf("\t\tClock resolution:\t\t%d nsec%s\n", res.tv_nsec, | ||
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(); | ||
457 | printf("\t\tSend msg:\t\t\t%d.%ds total time\n", | ||
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; | ||
461 | printf("\t\t\t\t\t\t%d nsec/msg\n", nsec); | ||
462 | printf("\t\tRecv msg:\t\t\t%d.%ds total time\n", | ||
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; | ||
466 | printf("\t\t\t\t\t\t%d nsec/msg\n", nsec); | ||
467 | |||
468 | |||
469 | for (cur_test = test2; cur_test->desc != NULL; cur_test++) { | ||
470 | printf(cur_test->desc); | ||
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"); | ||
496 | printf("\t\tSend msg:\t\t\t%d.%ds total time\n", | ||
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; | ||
500 | printf("\t\t\t\t\t\t%d nsec/msg\n", nsec); | ||
501 | printf("\t\tRecv msg:\t\t\t%d.%ds total time\n", | ||
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; | ||
505 | printf("\t\t\t\t\t\t%d nsec/msg\n", nsec); | ||
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; | ||
539 | int i, cpu; | ||
540 | struct sigaction sa; | ||
541 | poptContext popt_context; | ||
542 | char rc; | ||
543 | void *retval; | ||
544 | |||
545 | main_thread = pthread_self(); | ||
546 | num_cpus_to_pin = 0; | ||
547 | |||
548 | if (sysconf(_SC_NPROCESSORS_ONLN) == -1) { | ||
549 | perror("sysconf(_SC_NPROCESSORS_ONLN)"); | ||
550 | exit(1); | ||
551 | } | ||
552 | cpus_online = min(MAX_CPUS, sysconf(_SC_NPROCESSORS_ONLN)); | ||
553 | cpu_set = CPU_ALLOC(cpus_online); | ||
554 | if (cpu_set == NULL) { | ||
555 | perror("CPU_ALLOC()"); | ||
556 | exit(1); | ||
557 | } | ||
558 | cpu_set_size = CPU_ALLOC_SIZE(cpus_online); | ||
559 | CPU_ZERO_S(cpu_set_size, cpu_set); | ||
560 | |||
561 | popt_context = poptGetContext(NULL, argc, (const char **)argv, | ||
562 | options, 0); | ||
563 | |||
564 | while ((rc = poptGetNextOpt(popt_context)) > 0) { | ||
565 | switch (rc) { | ||
566 | case 'c': | ||
567 | continuous_mode = 1; | ||
568 | option = cpu_option_string; | ||
569 | do { | ||
570 | next_option = strchr(option, ','); | ||
571 | if (next_option) | ||
572 | *next_option = '\0'; | ||
573 | cpu = atoi(option); | ||
574 | if (cpu >= cpus_online) | ||
575 | fprintf(stderr, "CPU %d exceeds " | ||
576 | "cpus online, ignoring.\n", | ||
577 | cpu); | ||
578 | else | ||
579 | cpus_to_pin[num_cpus_to_pin++] = cpu; | ||
580 | if (next_option) | ||
581 | option = ++next_option; | ||
582 | } while (next_option && num_cpus_to_pin < MAX_CPUS); | ||
583 | /* Double check that they didn't give us the same CPU | ||
584 | * more than once */ | ||
585 | for (cpu = 0; cpu < num_cpus_to_pin; cpu++) { | ||
586 | if (CPU_ISSET_S(cpus_to_pin[cpu], cpu_set_size, | ||
587 | cpu_set)) { | ||
588 | fprintf(stderr, "Any given CPU may " | ||
589 | "only be given once.\n"); | ||
590 | exit(1); | ||
591 | } else | ||
592 | CPU_SET_S(cpus_to_pin[cpu], | ||
593 | cpu_set_size, cpu_set); | ||
594 | } | ||
595 | break; | ||
596 | case 'p': | ||
597 | /* | ||
598 | * Although we can create a msg queue with a | ||
599 | * non-absolute path name, unlink will fail. So, | ||
600 | * if the name doesn't start with a /, add one | ||
601 | * when we save it. | ||
602 | */ | ||
603 | option = queue_path; | ||
604 | if (*option != '/') { | ||
605 | queue_path = malloc(strlen(option) + 2); | ||
606 | if (!queue_path) { | ||
607 | perror("malloc()"); | ||
608 | exit(1); | ||
609 | } | ||
610 | queue_path[0] = '/'; | ||
611 | queue_path[1] = 0; | ||
612 | strcat(queue_path, option); | ||
613 | free(option); | ||
614 | } | ||
615 | break; | ||
616 | } | ||
617 | } | ||
618 | |||
619 | if (continuous_mode && num_cpus_to_pin == 0) { | ||
620 | fprintf(stderr, "Must pass at least one CPU to continuous " | ||
621 | "mode.\n"); | ||
622 | poptPrintUsage(popt_context, stderr, 0); | ||
623 | exit(1); | ||
624 | } else if (!continuous_mode) { | ||
625 | num_cpus_to_pin = 1; | ||
626 | cpus_to_pin[0] = cpus_online - 1; | ||
627 | } | ||
628 | |||
629 | if (getuid() != 0) { | ||
630 | fprintf(stderr, "Not running as root, but almost all tests " | ||
631 | "require root in order to modify\nsystem settings. " | ||
632 | "Exiting.\n"); | ||
633 | exit(1); | ||
634 | } | ||
635 | |||
636 | max_msgs = fopen(MAX_MSGS, "r+"); | ||
637 | max_msgsize = fopen(MAX_MSGSIZE, "r+"); | ||
638 | if (!max_msgs) | ||
639 | shutdown(2, "Failed to open msg_max", __LINE__); | ||
640 | if (!max_msgsize) | ||
641 | shutdown(2, "Failed to open msgsize_max", __LINE__); | ||
642 | |||
643 | /* Load up the current system values for everything we can */ | ||
644 | getr(RLIMIT_MSGQUEUE, &saved_limits); | ||
645 | cur_limits = saved_limits; | ||
646 | saved_max_msgs = cur_max_msgs = get(max_msgs); | ||
647 | saved_max_msgsize = cur_max_msgsize = get(max_msgsize); | ||
648 | errno = 0; | ||
649 | cur_nice = getpriority(PRIO_PROCESS, 0); | ||
650 | if (errno) | ||
651 | shutdown(2, "getpriority()", __LINE__); | ||
652 | |||
653 | /* Tell the user our initial state */ | ||
654 | printf("\nInitial system state:\n"); | ||
655 | printf("\tUsing queue path:\t\t\t%s\n", queue_path); | ||
656 | printf("\tRLIMIT_MSGQUEUE(soft):\t\t\t%d\n", saved_limits.rlim_cur); | ||
657 | printf("\tRLIMIT_MSGQUEUE(hard):\t\t\t%d\n", saved_limits.rlim_max); | ||
658 | printf("\tMaximum Message Size:\t\t\t%d\n", saved_max_msgsize); | ||
659 | printf("\tMaximum Queue Size:\t\t\t%d\n", saved_max_msgs); | ||
660 | printf("\tNice value:\t\t\t\t%d\n", cur_nice); | ||
661 | printf("\n"); | ||
662 | |||
663 | increase_limits(); | ||
664 | |||
665 | printf("Adjusted system state for testing:\n"); | ||
666 | if (cur_limits.rlim_cur == RLIM_INFINITY) { | ||
667 | printf("\tRLIMIT_MSGQUEUE(soft):\t\t\t(unlimited)\n"); | ||
668 | printf("\tRLIMIT_MSGQUEUE(hard):\t\t\t(unlimited)\n"); | ||
669 | } else { | ||
670 | printf("\tRLIMIT_MSGQUEUE(soft):\t\t\t%d\n", | ||
671 | cur_limits.rlim_cur); | ||
672 | printf("\tRLIMIT_MSGQUEUE(hard):\t\t\t%d\n", | ||
673 | cur_limits.rlim_max); | ||
674 | } | ||
675 | printf("\tMaximum Message Size:\t\t\t%d\n", cur_max_msgsize); | ||
676 | printf("\tMaximum Queue Size:\t\t\t%d\n", cur_max_msgs); | ||
677 | printf("\tNice value:\t\t\t\t%d\n", cur_nice); | ||
678 | printf("\tContinuous mode:\t\t\t(%s)\n", continuous_mode ? | ||
679 | (continuous_mode_fake ? "fake mode" : "enabled") : | ||
680 | "disabled"); | ||
681 | printf("\tCPUs to pin:\t\t\t\t%d", cpus_to_pin[0]); | ||
682 | for (cpu = 1; cpu < num_cpus_to_pin; cpu++) | ||
683 | printf(",%d", cpus_to_pin[cpu]); | ||
684 | printf("\n"); | ||
685 | |||
686 | sa.sa_sigaction = sig_action_SIGUSR1; | ||
687 | sigemptyset(&sa.sa_mask); | ||
688 | sigaddset(&sa.sa_mask, SIGHUP); | ||
689 | sigaddset(&sa.sa_mask, SIGINT); | ||
690 | sigaddset(&sa.sa_mask, SIGQUIT); | ||
691 | sigaddset(&sa.sa_mask, SIGTERM); | ||
692 | sa.sa_flags = SA_SIGINFO; | ||
693 | if (sigaction(SIGUSR1, &sa, NULL) == -1) | ||
694 | shutdown(1, "sigaction(SIGUSR1)", __LINE__); | ||
695 | sa.sa_sigaction = sig_action; | ||
696 | if (sigaction(SIGHUP, &sa, NULL) == -1) | ||
697 | shutdown(1, "sigaction(SIGHUP)", __LINE__); | ||
698 | if (sigaction(SIGINT, &sa, NULL) == -1) | ||
699 | shutdown(1, "sigaction(SIGINT)", __LINE__); | ||
700 | if (sigaction(SIGQUIT, &sa, NULL) == -1) | ||
701 | shutdown(1, "sigaction(SIGQUIT)", __LINE__); | ||
702 | if (sigaction(SIGTERM, &sa, NULL) == -1) | ||
703 | shutdown(1, "sigaction(SIGTERM)", __LINE__); | ||
704 | |||
705 | if (!continuous_mode_fake) { | ||
706 | attr.mq_flags = O_NONBLOCK; | ||
707 | attr.mq_maxmsg = cur_max_msgs; | ||
708 | attr.mq_msgsize = MSG_SIZE; | ||
709 | open_queue(&attr); | ||
710 | } | ||
711 | for (i = 0; i < num_cpus_to_pin; i++) { | ||
712 | pthread_attr_t thread_attr; | ||
713 | void *thread_func; | ||
714 | |||
715 | if (continuous_mode_fake) | ||
716 | thread_func = &fake_cont_thread; | ||
717 | else if (continuous_mode) | ||
718 | thread_func = &cont_thread; | ||
719 | else | ||
720 | thread_func = &perf_test_thread; | ||
721 | |||
722 | CPU_ZERO_S(cpu_set_size, cpu_set); | ||
723 | CPU_SET_S(cpus_to_pin[i], cpu_set_size, cpu_set); | ||
724 | pthread_attr_init(&thread_attr); | ||
725 | pthread_attr_setaffinity_np(&thread_attr, cpu_set_size, | ||
726 | cpu_set); | ||
727 | if (pthread_create(&cpu_threads[i], &thread_attr, thread_func, | ||
728 | NULL)) | ||
729 | shutdown(1, "pthread_create()", __LINE__); | ||
730 | pthread_attr_destroy(&thread_attr); | ||
731 | } | ||
732 | |||
733 | if (!continuous_mode) { | ||
734 | pthread_join(cpu_threads[0], &retval); | ||
735 | shutdown((long)retval, "perf_test_thread()", __LINE__); | ||
736 | } else { | ||
737 | while (1) | ||
738 | sleep(1); | ||
739 | } | ||
740 | shutdown(0, "", 0); | ||
741 | } | ||