diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-05-31 21:10:18 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-05-31 21:10:18 -0400 |
commit | 08615d7d85e5aa02c05bf6c4dde87d940e7f85f6 (patch) | |
tree | 18906149d313d25914160aca21cedf54b3a7e818 /tools | |
parent | 9fdadb2cbaf4b482dfd6086e8bd3d2db071a1702 (diff) | |
parent | 0a4dd35c67b144d8ef9432120105f1aab9293ee9 (diff) |
Merge branch 'akpm' (Andrew's patch-bomb)
Merge misc patches from Andrew Morton:
- the "misc" tree - stuff from all over the map
- checkpatch updates
- fatfs
- kmod changes
- procfs
- cpumask
- UML
- kexec
- mqueue
- rapidio
- pidns
- some checkpoint-restore feature work. Reluctantly. Most of it
delayed a release. I'm still rather worried that we don't have a
clear roadmap to completion for this work.
* emailed from Andrew Morton <akpm@linux-foundation.org>: (78 patches)
kconfig: update compression algorithm info
c/r: prctl: add ability to set new mm_struct::exe_file
c/r: prctl: extend PR_SET_MM to set up more mm_struct entries
c/r: procfs: add arg_start/end, env_start/end and exit_code members to /proc/$pid/stat
syscalls, x86: add __NR_kcmp syscall
fs, proc: introduce /proc/<pid>/task/<tid>/children entry
sysctl: make kernel.ns_last_pid control dependent on CHECKPOINT_RESTORE
aio/vfs: cleanup of rw_copy_check_uvector() and compat_rw_copy_check_uvector()
eventfd: change int to __u64 in eventfd_signal()
fs/nls: add Apple NLS
pidns: make killed children autoreap
pidns: use task_active_pid_ns in do_notify_parent
rapidio/tsi721: add DMA engine support
rapidio: add DMA engine support for RIO data transfers
ipc/mqueue: add rbtree node caching support
tools/selftests: add mq_perf_tests
ipc/mqueue: strengthen checks on mqueue creation
ipc/mqueue: correct mq_attr_ok test
ipc/mqueue: improve performance of send/recv
selftests: add mq_open_tests
...
Diffstat (limited to 'tools')
-rw-r--r-- | tools/testing/selftests/Makefile | 2 | ||||
-rw-r--r-- | tools/testing/selftests/kcmp/Makefile | 29 | ||||
-rw-r--r-- | tools/testing/selftests/kcmp/kcmp_test.c | 94 | ||||
-rw-r--r-- | tools/testing/selftests/mqueue/.gitignore | 2 | ||||
-rw-r--r-- | tools/testing/selftests/mqueue/Makefile | 10 | ||||
-rw-r--r-- | tools/testing/selftests/mqueue/mq_open_tests.c | 492 | ||||
-rw-r--r-- | tools/testing/selftests/mqueue/mq_perf_tests.c | 741 |
7 files changed, 1369 insertions, 1 deletions
diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 28bc57ee757c..a4162e15c25f 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile | |||
@@ -1,4 +1,4 @@ | |||
1 | TARGETS = breakpoints vm | 1 | TARGETS = breakpoints kcmp mqueue vm |
2 | 2 | ||
3 | all: | 3 | all: |
4 | for TARGET in $(TARGETS); do \ | 4 | for TARGET in $(TARGETS); do \ |
diff --git a/tools/testing/selftests/kcmp/Makefile b/tools/testing/selftests/kcmp/Makefile new file mode 100644 index 000000000000..dc79b86ea65c --- /dev/null +++ b/tools/testing/selftests/kcmp/Makefile | |||
@@ -0,0 +1,29 @@ | |||
1 | uname_M := $(shell uname -m 2>/dev/null || echo not) | ||
2 | ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/i386/) | ||
3 | ifeq ($(ARCH),i386) | ||
4 | ARCH := X86 | ||
5 | CFLAGS := -DCONFIG_X86_32 -D__i386__ | ||
6 | endif | ||
7 | ifeq ($(ARCH),x86_64) | ||
8 | ARCH := X86 | ||
9 | CFLAGS := -DCONFIG_X86_64 -D__x86_64__ | ||
10 | endif | ||
11 | |||
12 | CFLAGS += -I../../../../arch/x86/include/generated/ | ||
13 | CFLAGS += -I../../../../include/ | ||
14 | CFLAGS += -I../../../../usr/include/ | ||
15 | CFLAGS += -I../../../../arch/x86/include/ | ||
16 | |||
17 | all: | ||
18 | ifeq ($(ARCH),X86) | ||
19 | gcc $(CFLAGS) kcmp_test.c -o run_test | ||
20 | else | ||
21 | echo "Not an x86 target, can't build kcmp selftest" | ||
22 | endif | ||
23 | |||
24 | run-tests: all | ||
25 | ./kcmp_test | ||
26 | |||
27 | clean: | ||
28 | rm -fr ./run_test | ||
29 | rm -fr ./test-file | ||
diff --git a/tools/testing/selftests/kcmp/kcmp_test.c b/tools/testing/selftests/kcmp/kcmp_test.c new file mode 100644 index 000000000000..358cc6bfa35d --- /dev/null +++ b/tools/testing/selftests/kcmp/kcmp_test.c | |||
@@ -0,0 +1,94 @@ | |||
1 | #define _GNU_SOURCE | ||
2 | |||
3 | #include <stdio.h> | ||
4 | #include <stdlib.h> | ||
5 | #include <signal.h> | ||
6 | #include <limits.h> | ||
7 | #include <unistd.h> | ||
8 | #include <errno.h> | ||
9 | #include <string.h> | ||
10 | #include <fcntl.h> | ||
11 | |||
12 | #include <linux/unistd.h> | ||
13 | #include <linux/kcmp.h> | ||
14 | |||
15 | #include <sys/syscall.h> | ||
16 | #include <sys/types.h> | ||
17 | #include <sys/stat.h> | ||
18 | #include <sys/wait.h> | ||
19 | |||
20 | static long sys_kcmp(int pid1, int pid2, int type, int fd1, int fd2) | ||
21 | { | ||
22 | return syscall(__NR_kcmp, pid1, pid2, type, fd1, fd2); | ||
23 | } | ||
24 | |||
25 | int main(int argc, char **argv) | ||
26 | { | ||
27 | const char kpath[] = "kcmp-test-file"; | ||
28 | int pid1, pid2; | ||
29 | int fd1, fd2; | ||
30 | int status; | ||
31 | |||
32 | fd1 = open(kpath, O_RDWR | O_CREAT | O_TRUNC, 0644); | ||
33 | pid1 = getpid(); | ||
34 | |||
35 | if (fd1 < 0) { | ||
36 | perror("Can't create file"); | ||
37 | exit(1); | ||
38 | } | ||
39 | |||
40 | pid2 = fork(); | ||
41 | if (pid2 < 0) { | ||
42 | perror("fork failed"); | ||
43 | exit(1); | ||
44 | } | ||
45 | |||
46 | if (!pid2) { | ||
47 | int pid2 = getpid(); | ||
48 | int ret; | ||
49 | |||
50 | fd2 = open(kpath, O_RDWR, 0644); | ||
51 | if (fd2 < 0) { | ||
52 | perror("Can't open file"); | ||
53 | exit(1); | ||
54 | } | ||
55 | |||
56 | /* An example of output and arguments */ | ||
57 | printf("pid1: %6d pid2: %6d FD: %2ld FILES: %2ld VM: %2ld " | ||
58 | "FS: %2ld SIGHAND: %2ld IO: %2ld SYSVSEM: %2ld " | ||
59 | "INV: %2ld\n", | ||
60 | pid1, pid2, | ||
61 | sys_kcmp(pid1, pid2, KCMP_FILE, fd1, fd2), | ||
62 | sys_kcmp(pid1, pid2, KCMP_FILES, 0, 0), | ||
63 | sys_kcmp(pid1, pid2, KCMP_VM, 0, 0), | ||
64 | sys_kcmp(pid1, pid2, KCMP_FS, 0, 0), | ||
65 | sys_kcmp(pid1, pid2, KCMP_SIGHAND, 0, 0), | ||
66 | sys_kcmp(pid1, pid2, KCMP_IO, 0, 0), | ||
67 | sys_kcmp(pid1, pid2, KCMP_SYSVSEM, 0, 0), | ||
68 | |||
69 | /* This one should fail */ | ||
70 | sys_kcmp(pid1, pid2, KCMP_TYPES + 1, 0, 0)); | ||
71 | |||
72 | /* This one should return same fd */ | ||
73 | ret = sys_kcmp(pid1, pid2, KCMP_FILE, fd1, fd1); | ||
74 | if (ret) { | ||
75 | printf("FAIL: 0 expected but %d returned\n", ret); | ||
76 | ret = -1; | ||
77 | } else | ||
78 | printf("PASS: 0 returned as expected\n"); | ||
79 | |||
80 | /* Compare with self */ | ||
81 | ret = sys_kcmp(pid1, pid1, KCMP_VM, 0, 0); | ||
82 | if (ret) { | ||
83 | printf("FAIL: 0 expected but %li returned\n", ret); | ||
84 | ret = -1; | ||
85 | } else | ||
86 | printf("PASS: 0 returned as expected\n"); | ||
87 | |||
88 | exit(ret); | ||
89 | } | ||
90 | |||
91 | waitpid(pid2, &status, P_ALL); | ||
92 | |||
93 | return 0; | ||
94 | } | ||
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 new file mode 100644 index 000000000000..54c0aad2b47c --- /dev/null +++ b/tools/testing/selftests/mqueue/Makefile | |||
@@ -0,0 +1,10 @@ | |||
1 | all: | ||
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 | ||
4 | |||
5 | run_tests: | ||
6 | ./mq_open_tests /test1 | ||
7 | ./mq_perf_tests | ||
8 | |||
9 | clean: | ||
10 | rm -f mq_open_tests mq_perf_tests | ||
diff --git a/tools/testing/selftests/mqueue/mq_open_tests.c b/tools/testing/selftests/mqueue/mq_open_tests.c new file mode 100644 index 000000000000..711cc2923047 --- /dev/null +++ b/tools/testing/selftests/mqueue/mq_open_tests.c | |||
@@ -0,0 +1,492 @@ | |||
1 | /* | ||
2 | * This application is Copyright 2012 Red Hat, Inc. | ||
3 | * Doug Ledford <dledford@redhat.com> | ||
4 | * | ||
5 | * mq_open_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_open_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_open_tests.c | ||
17 | * Tests the various situations that should either succeed or fail to | ||
18 | * open a posix message queue and then reports whether or not they | ||
19 | * did as they were supposed to. | ||
20 | * | ||
21 | */ | ||
22 | #include <stdio.h> | ||
23 | #include <stdlib.h> | ||
24 | #include <unistd.h> | ||
25 | #include <fcntl.h> | ||
26 | #include <string.h> | ||
27 | #include <limits.h> | ||
28 | #include <errno.h> | ||
29 | #include <sys/types.h> | ||
30 | #include <sys/time.h> | ||
31 | #include <sys/resource.h> | ||
32 | #include <sys/stat.h> | ||
33 | #include <mqueue.h> | ||
34 | |||
35 | static char *usage = | ||
36 | "Usage:\n" | ||
37 | " %s path\n" | ||
38 | "\n" | ||
39 | " path Path name of the message queue to create\n" | ||
40 | "\n" | ||
41 | " Note: this program must be run as root in order to enable all tests\n" | ||
42 | "\n"; | ||
43 | |||
44 | char *DEF_MSGS = "/proc/sys/fs/mqueue/msg_default"; | ||
45 | char *DEF_MSGSIZE = "/proc/sys/fs/mqueue/msgsize_default"; | ||
46 | char *MAX_MSGS = "/proc/sys/fs/mqueue/msg_max"; | ||
47 | char *MAX_MSGSIZE = "/proc/sys/fs/mqueue/msgsize_max"; | ||
48 | |||
49 | int default_settings; | ||
50 | struct rlimit saved_limits, cur_limits; | ||
51 | int saved_def_msgs, saved_def_msgsize, saved_max_msgs, saved_max_msgsize; | ||
52 | int cur_def_msgs, cur_def_msgsize, cur_max_msgs, cur_max_msgsize; | ||
53 | FILE *def_msgs, *def_msgsize, *max_msgs, *max_msgsize; | ||
54 | char *queue_path; | ||
55 | mqd_t queue = -1; | ||
56 | |||
57 | static inline void __set(FILE *stream, int value, char *err_msg); | ||
58 | void shutdown(int exit_val, char *err_cause, int line_no); | ||
59 | static inline int get(FILE *stream); | ||
60 | static inline void set(FILE *stream, int value); | ||
61 | static inline void getr(int type, struct rlimit *rlim); | ||
62 | static inline void setr(int type, struct rlimit *rlim); | ||
63 | void validate_current_settings(); | ||
64 | static inline void test_queue(struct mq_attr *attr, struct mq_attr *result); | ||
65 | static inline int test_queue_fail(struct mq_attr *attr, struct mq_attr *result); | ||
66 | |||
67 | static inline void __set(FILE *stream, int value, char *err_msg) | ||
68 | { | ||
69 | rewind(stream); | ||
70 | if (fprintf(stream, "%d", value) < 0) | ||
71 | perror(err_msg); | ||
72 | } | ||
73 | |||
74 | |||
75 | void shutdown(int exit_val, char *err_cause, int line_no) | ||
76 | { | ||
77 | static int in_shutdown = 0; | ||
78 | |||
79 | /* In case we get called recursively by a set() call below */ | ||
80 | if (in_shutdown++) | ||
81 | return; | ||
82 | |||
83 | seteuid(0); | ||
84 | |||
85 | if (queue != -1) | ||
86 | if (mq_close(queue)) | ||
87 | perror("mq_close() during shutdown"); | ||
88 | if (queue_path) | ||
89 | /* | ||
90 | * Be silent if this fails, if we cleaned up already it's | ||
91 | * expected to fail | ||
92 | */ | ||
93 | mq_unlink(queue_path); | ||
94 | if (default_settings) { | ||
95 | if (saved_def_msgs) | ||
96 | __set(def_msgs, saved_def_msgs, | ||
97 | "failed to restore saved_def_msgs"); | ||
98 | if (saved_def_msgsize) | ||
99 | __set(def_msgsize, saved_def_msgsize, | ||
100 | "failed to restore saved_def_msgsize"); | ||
101 | } | ||
102 | if (saved_max_msgs) | ||
103 | __set(max_msgs, saved_max_msgs, | ||
104 | "failed to restore saved_max_msgs"); | ||
105 | if (saved_max_msgsize) | ||
106 | __set(max_msgsize, saved_max_msgsize, | ||
107 | "failed to restore saved_max_msgsize"); | ||
108 | if (exit_val) | ||
109 | error(exit_val, errno, "%s at %d", err_cause, line_no); | ||
110 | exit(0); | ||
111 | } | ||
112 | |||
113 | static inline int get(FILE *stream) | ||
114 | { | ||
115 | int value; | ||
116 | rewind(stream); | ||
117 | if (fscanf(stream, "%d", &value) != 1) | ||
118 | shutdown(4, "Error reading /proc entry", __LINE__ - 1); | ||
119 | return value; | ||
120 | } | ||
121 | |||
122 | static inline void set(FILE *stream, int value) | ||
123 | { | ||
124 | int new_value; | ||
125 | |||
126 | rewind(stream); | ||
127 | if (fprintf(stream, "%d", value) < 0) | ||
128 | return shutdown(5, "Failed writing to /proc file", | ||
129 | __LINE__ - 1); | ||
130 | new_value = get(stream); | ||
131 | if (new_value != value) | ||
132 | return shutdown(5, "We didn't get what we wrote to /proc back", | ||
133 | __LINE__ - 1); | ||
134 | } | ||
135 | |||
136 | static inline void getr(int type, struct rlimit *rlim) | ||
137 | { | ||
138 | if (getrlimit(type, rlim)) | ||
139 | shutdown(6, "getrlimit()", __LINE__ - 1); | ||
140 | } | ||
141 | |||
142 | static inline void setr(int type, struct rlimit *rlim) | ||
143 | { | ||
144 | if (setrlimit(type, rlim)) | ||
145 | shutdown(7, "setrlimit()", __LINE__ - 1); | ||
146 | } | ||
147 | |||
148 | void validate_current_settings() | ||
149 | { | ||
150 | int rlim_needed; | ||
151 | |||
152 | if (cur_limits.rlim_cur < 4096) { | ||
153 | printf("Current rlimit value for POSIX message queue bytes is " | ||
154 | "unreasonably low,\nincreasing.\n\n"); | ||
155 | cur_limits.rlim_cur = 8192; | ||
156 | cur_limits.rlim_max = 16384; | ||
157 | setr(RLIMIT_MSGQUEUE, &cur_limits); | ||
158 | } | ||
159 | |||
160 | if (default_settings) { | ||
161 | rlim_needed = (cur_def_msgs + 1) * (cur_def_msgsize + 1 + | ||
162 | 2 * sizeof(void *)); | ||
163 | if (rlim_needed > cur_limits.rlim_cur) { | ||
164 | printf("Temporarily lowering default queue parameters " | ||
165 | "to something that will work\n" | ||
166 | "with the current rlimit values.\n\n"); | ||
167 | set(def_msgs, 10); | ||
168 | cur_def_msgs = 10; | ||
169 | set(def_msgsize, 128); | ||
170 | cur_def_msgsize = 128; | ||
171 | } | ||
172 | } else { | ||
173 | rlim_needed = (cur_max_msgs + 1) * (cur_max_msgsize + 1 + | ||
174 | 2 * sizeof(void *)); | ||
175 | if (rlim_needed > cur_limits.rlim_cur) { | ||
176 | printf("Temporarily lowering maximum queue parameters " | ||
177 | "to something that will work\n" | ||
178 | "with the current rlimit values in case this is " | ||
179 | "a kernel that ties the default\n" | ||
180 | "queue parameters to the maximum queue " | ||
181 | "parameters.\n\n"); | ||
182 | set(max_msgs, 10); | ||
183 | cur_max_msgs = 10; | ||
184 | set(max_msgsize, 128); | ||
185 | cur_max_msgsize = 128; | ||
186 | } | ||
187 | } | ||
188 | } | ||
189 | |||
190 | /* | ||
191 | * test_queue - Test opening a queue, shutdown if we fail. This should | ||
192 | * only be called in situations that should never fail. We clean up | ||
193 | * after ourselves and return the queue attributes in *result. | ||
194 | */ | ||
195 | static inline void test_queue(struct mq_attr *attr, struct mq_attr *result) | ||
196 | { | ||
197 | int flags = O_RDWR | O_EXCL | O_CREAT; | ||
198 | int perms = DEFFILEMODE; | ||
199 | |||
200 | if ((queue = mq_open(queue_path, flags, perms, attr)) == -1) | ||
201 | shutdown(1, "mq_open()", __LINE__); | ||
202 | if (mq_getattr(queue, result)) | ||
203 | shutdown(1, "mq_getattr()", __LINE__); | ||
204 | if (mq_close(queue)) | ||
205 | shutdown(1, "mq_close()", __LINE__); | ||
206 | queue = -1; | ||
207 | if (mq_unlink(queue_path)) | ||
208 | shutdown(1, "mq_unlink()", __LINE__); | ||
209 | } | ||
210 | |||
211 | /* | ||
212 | * Same as test_queue above, but failure is not fatal. | ||
213 | * Returns: | ||
214 | * 0 - Failed to create a queue | ||
215 | * 1 - Created a queue, attributes in *result | ||
216 | */ | ||
217 | static inline int test_queue_fail(struct mq_attr *attr, struct mq_attr *result) | ||
218 | { | ||
219 | int flags = O_RDWR | O_EXCL | O_CREAT; | ||
220 | int perms = DEFFILEMODE; | ||
221 | |||
222 | if ((queue = mq_open(queue_path, flags, perms, attr)) == -1) | ||
223 | return 0; | ||
224 | if (mq_getattr(queue, result)) | ||
225 | shutdown(1, "mq_getattr()", __LINE__); | ||
226 | if (mq_close(queue)) | ||
227 | shutdown(1, "mq_close()", __LINE__); | ||
228 | queue = -1; | ||
229 | if (mq_unlink(queue_path)) | ||
230 | shutdown(1, "mq_unlink()", __LINE__); | ||
231 | return 1; | ||
232 | } | ||
233 | |||
234 | int main(int argc, char *argv[]) | ||
235 | { | ||
236 | struct mq_attr attr, result; | ||
237 | |||
238 | if (argc != 2) { | ||
239 | fprintf(stderr, "Must pass a valid queue name\n\n"); | ||
240 | fprintf(stderr, usage, argv[0]); | ||
241 | exit(1); | ||
242 | } | ||
243 | |||
244 | /* | ||
245 | * Although we can create a msg queue with a non-absolute path name, | ||
246 | * unlink will fail. So, if the name doesn't start with a /, add one | ||
247 | * when we save it. | ||
248 | */ | ||
249 | if (*argv[1] == '/') | ||
250 | queue_path = strdup(argv[1]); | ||
251 | else { | ||
252 | queue_path = malloc(strlen(argv[1]) + 2); | ||
253 | if (!queue_path) { | ||
254 | perror("malloc()"); | ||
255 | exit(1); | ||
256 | } | ||
257 | queue_path[0] = '/'; | ||
258 | queue_path[1] = 0; | ||
259 | strcat(queue_path, argv[1]); | ||
260 | } | ||
261 | |||
262 | if (getuid() != 0) { | ||
263 | fprintf(stderr, "Not running as root, but almost all tests " | ||
264 | "require root in order to modify\nsystem settings. " | ||
265 | "Exiting.\n"); | ||
266 | exit(1); | ||
267 | } | ||
268 | |||
269 | /* Find out what files there are for us to make tweaks in */ | ||
270 | def_msgs = fopen(DEF_MSGS, "r+"); | ||
271 | def_msgsize = fopen(DEF_MSGSIZE, "r+"); | ||
272 | max_msgs = fopen(MAX_MSGS, "r+"); | ||
273 | max_msgsize = fopen(MAX_MSGSIZE, "r+"); | ||
274 | |||
275 | if (!max_msgs) | ||
276 | shutdown(2, "Failed to open msg_max", __LINE__); | ||
277 | if (!max_msgsize) | ||
278 | shutdown(2, "Failed to open msgsize_max", __LINE__); | ||
279 | if (def_msgs || def_msgsize) | ||
280 | default_settings = 1; | ||
281 | |||
282 | /* Load up the current system values for everything we can */ | ||
283 | getr(RLIMIT_MSGQUEUE, &saved_limits); | ||
284 | cur_limits = saved_limits; | ||
285 | if (default_settings) { | ||
286 | saved_def_msgs = cur_def_msgs = get(def_msgs); | ||
287 | saved_def_msgsize = cur_def_msgsize = get(def_msgsize); | ||
288 | } | ||
289 | saved_max_msgs = cur_max_msgs = get(max_msgs); | ||
290 | saved_max_msgsize = cur_max_msgsize = get(max_msgsize); | ||
291 | |||
292 | /* Tell the user our initial state */ | ||
293 | printf("\nInitial system state:\n"); | ||
294 | printf("\tUsing queue path:\t\t%s\n", queue_path); | ||
295 | printf("\tRLIMIT_MSGQUEUE(soft):\t\t%d\n", saved_limits.rlim_cur); | ||
296 | printf("\tRLIMIT_MSGQUEUE(hard):\t\t%d\n", saved_limits.rlim_max); | ||
297 | printf("\tMaximum Message Size:\t\t%d\n", saved_max_msgsize); | ||
298 | printf("\tMaximum Queue Size:\t\t%d\n", saved_max_msgs); | ||
299 | if (default_settings) { | ||
300 | printf("\tDefault Message Size:\t\t%d\n", saved_def_msgsize); | ||
301 | printf("\tDefault Queue Size:\t\t%d\n", saved_def_msgs); | ||
302 | } else { | ||
303 | printf("\tDefault Message Size:\t\tNot Supported\n"); | ||
304 | printf("\tDefault Queue Size:\t\tNot Supported\n"); | ||
305 | } | ||
306 | printf("\n"); | ||
307 | |||
308 | validate_current_settings(); | ||
309 | |||
310 | printf("Adjusted system state for testing:\n"); | ||
311 | printf("\tRLIMIT_MSGQUEUE(soft):\t\t%d\n", cur_limits.rlim_cur); | ||
312 | printf("\tRLIMIT_MSGQUEUE(hard):\t\t%d\n", cur_limits.rlim_max); | ||
313 | printf("\tMaximum Message Size:\t\t%d\n", cur_max_msgsize); | ||
314 | printf("\tMaximum Queue Size:\t\t%d\n", cur_max_msgs); | ||
315 | if (default_settings) { | ||
316 | printf("\tDefault Message Size:\t\t%d\n", cur_def_msgsize); | ||
317 | printf("\tDefault Queue Size:\t\t%d\n", cur_def_msgs); | ||
318 | } | ||
319 | |||
320 | printf("\n\nTest series 1, behavior when no attr struct " | ||
321 | "passed to mq_open:\n"); | ||
322 | if (!default_settings) { | ||
323 | test_queue(NULL, &result); | ||
324 | printf("Given sane system settings, mq_open without an attr " | ||
325 | "struct succeeds:\tPASS\n"); | ||
326 | if (result.mq_maxmsg != cur_max_msgs || | ||
327 | result.mq_msgsize != cur_max_msgsize) { | ||
328 | printf("Kernel does not support setting the default " | ||
329 | "mq attributes,\nbut also doesn't tie the " | ||
330 | "defaults to the maximums:\t\t\tPASS\n"); | ||
331 | } else { | ||
332 | set(max_msgs, ++cur_max_msgs); | ||
333 | set(max_msgsize, ++cur_max_msgsize); | ||
334 | test_queue(NULL, &result); | ||
335 | if (result.mq_maxmsg == cur_max_msgs && | ||
336 | result.mq_msgsize == cur_max_msgsize) | ||
337 | printf("Kernel does not support setting the " | ||
338 | "default mq attributes and\n" | ||
339 | "also ties system wide defaults to " | ||
340 | "the system wide maximums:\t\t" | ||
341 | "FAIL\n"); | ||
342 | else | ||
343 | printf("Kernel does not support setting the " | ||
344 | "default mq attributes,\n" | ||
345 | "but also doesn't tie the defaults to " | ||
346 | "the maximums:\t\t\tPASS\n"); | ||
347 | } | ||
348 | } else { | ||
349 | printf("Kernel supports setting defaults separately from " | ||
350 | "maximums:\t\tPASS\n"); | ||
351 | /* | ||
352 | * While we are here, go ahead and test that the kernel | ||
353 | * properly follows the default settings | ||
354 | */ | ||
355 | test_queue(NULL, &result); | ||
356 | printf("Given sane values, mq_open without an attr struct " | ||
357 | "succeeds:\t\tPASS\n"); | ||
358 | if (result.mq_maxmsg != cur_def_msgs || | ||
359 | result.mq_msgsize != cur_def_msgsize) | ||
360 | printf("Kernel supports setting defaults, but does " | ||
361 | "not actually honor them:\tFAIL\n\n"); | ||
362 | else { | ||
363 | set(def_msgs, ++cur_def_msgs); | ||
364 | set(def_msgsize, ++cur_def_msgsize); | ||
365 | /* In case max was the same as the default */ | ||
366 | set(max_msgs, ++cur_max_msgs); | ||
367 | set(max_msgsize, ++cur_max_msgsize); | ||
368 | test_queue(NULL, &result); | ||
369 | if (result.mq_maxmsg != cur_def_msgs || | ||
370 | result.mq_msgsize != cur_def_msgsize) | ||
371 | printf("Kernel supports setting defaults, but " | ||
372 | "does not actually honor them:\t" | ||
373 | "FAIL\n"); | ||
374 | else | ||
375 | printf("Kernel properly honors default setting " | ||
376 | "knobs:\t\t\t\tPASS\n"); | ||
377 | } | ||
378 | set(def_msgs, cur_max_msgs + 1); | ||
379 | cur_def_msgs = cur_max_msgs + 1; | ||
380 | set(def_msgsize, cur_max_msgsize + 1); | ||
381 | cur_def_msgsize = cur_max_msgsize + 1; | ||
382 | if (cur_def_msgs * (cur_def_msgsize + 2 * sizeof(void *)) >= | ||
383 | cur_limits.rlim_cur) { | ||
384 | cur_limits.rlim_cur = (cur_def_msgs + 2) * | ||
385 | (cur_def_msgsize + 2 * sizeof(void *)); | ||
386 | cur_limits.rlim_max = 2 * cur_limits.rlim_cur; | ||
387 | setr(RLIMIT_MSGQUEUE, &cur_limits); | ||
388 | } | ||
389 | if (test_queue_fail(NULL, &result)) { | ||
390 | if (result.mq_maxmsg == cur_max_msgs && | ||
391 | result.mq_msgsize == cur_max_msgsize) | ||
392 | printf("Kernel properly limits default values " | ||
393 | "to lesser of default/max:\t\tPASS\n"); | ||
394 | else | ||
395 | printf("Kernel does not properly set default " | ||
396 | "queue parameters when\ndefaults > " | ||
397 | "max:\t\t\t\t\t\t\t\tFAIL\n"); | ||
398 | } else | ||
399 | printf("Kernel fails to open mq because defaults are " | ||
400 | "greater than maximums:\tFAIL\n"); | ||
401 | set(def_msgs, --cur_def_msgs); | ||
402 | set(def_msgsize, --cur_def_msgsize); | ||
403 | cur_limits.rlim_cur = cur_limits.rlim_max = cur_def_msgs * | ||
404 | cur_def_msgsize; | ||
405 | setr(RLIMIT_MSGQUEUE, &cur_limits); | ||
406 | if (test_queue_fail(NULL, &result)) | ||
407 | printf("Kernel creates queue even though defaults " | ||
408 | "would exceed\nrlimit setting:" | ||
409 | "\t\t\t\t\t\t\t\tFAIL\n"); | ||
410 | else | ||
411 | printf("Kernel properly fails to create queue when " | ||
412 | "defaults would\nexceed rlimit:" | ||
413 | "\t\t\t\t\t\t\t\tPASS\n"); | ||
414 | } | ||
415 | |||
416 | /* | ||
417 | * Test #2 - open with an attr struct that exceeds rlimit | ||
418 | */ | ||
419 | printf("\n\nTest series 2, behavior when attr struct is " | ||
420 | "passed to mq_open:\n"); | ||
421 | cur_max_msgs = 32; | ||
422 | cur_max_msgsize = cur_limits.rlim_max >> 4; | ||
423 | set(max_msgs, cur_max_msgs); | ||
424 | set(max_msgsize, cur_max_msgsize); | ||
425 | attr.mq_maxmsg = cur_max_msgs; | ||
426 | attr.mq_msgsize = cur_max_msgsize; | ||
427 | if (test_queue_fail(&attr, &result)) | ||
428 | printf("Queue open in excess of rlimit max when euid = 0 " | ||
429 | "succeeded:\t\tFAIL\n"); | ||
430 | else | ||
431 | printf("Queue open in excess of rlimit max when euid = 0 " | ||
432 | "failed:\t\tPASS\n"); | ||
433 | attr.mq_maxmsg = cur_max_msgs + 1; | ||
434 | attr.mq_msgsize = 10; | ||
435 | if (test_queue_fail(&attr, &result)) | ||
436 | printf("Queue open with mq_maxmsg > limit when euid = 0 " | ||
437 | "succeeded:\t\tPASS\n"); | ||
438 | else | ||
439 | printf("Queue open with mq_maxmsg > limit when euid = 0 " | ||
440 | "failed:\t\tFAIL\n"); | ||
441 | attr.mq_maxmsg = 1; | ||
442 | attr.mq_msgsize = cur_max_msgsize + 1; | ||
443 | if (test_queue_fail(&attr, &result)) | ||
444 | printf("Queue open with mq_msgsize > limit when euid = 0 " | ||
445 | "succeeded:\t\tPASS\n"); | ||
446 | else | ||
447 | printf("Queue open with mq_msgsize > limit when euid = 0 " | ||
448 | "failed:\t\tFAIL\n"); | ||
449 | attr.mq_maxmsg = 65536; | ||
450 | attr.mq_msgsize = 65536; | ||
451 | if (test_queue_fail(&attr, &result)) | ||
452 | printf("Queue open with total size > 2GB when euid = 0 " | ||
453 | "succeeded:\t\tFAIL\n"); | ||
454 | else | ||
455 | printf("Queue open with total size > 2GB when euid = 0 " | ||
456 | "failed:\t\t\tPASS\n"); | ||
457 | seteuid(99); | ||
458 | attr.mq_maxmsg = cur_max_msgs; | ||
459 | attr.mq_msgsize = cur_max_msgsize; | ||
460 | if (test_queue_fail(&attr, &result)) | ||
461 | printf("Queue open in excess of rlimit max when euid = 99 " | ||
462 | "succeeded:\t\tFAIL\n"); | ||
463 | else | ||
464 | printf("Queue open in excess of rlimit max when euid = 99 " | ||
465 | "failed:\t\tPASS\n"); | ||
466 | attr.mq_maxmsg = cur_max_msgs + 1; | ||
467 | attr.mq_msgsize = 10; | ||
468 | if (test_queue_fail(&attr, &result)) | ||
469 | printf("Queue open with mq_maxmsg > limit when euid = 99 " | ||
470 | "succeeded:\t\tFAIL\n"); | ||
471 | else | ||
472 | printf("Queue open with mq_maxmsg > limit when euid = 99 " | ||
473 | "failed:\t\tPASS\n"); | ||
474 | attr.mq_maxmsg = 1; | ||
475 | attr.mq_msgsize = cur_max_msgsize + 1; | ||
476 | if (test_queue_fail(&attr, &result)) | ||
477 | printf("Queue open with mq_msgsize > limit when euid = 99 " | ||
478 | "succeeded:\t\tFAIL\n"); | ||
479 | else | ||
480 | printf("Queue open with mq_msgsize > limit when euid = 99 " | ||
481 | "failed:\t\tPASS\n"); | ||
482 | attr.mq_maxmsg = 65536; | ||
483 | attr.mq_msgsize = 65536; | ||
484 | if (test_queue_fail(&attr, &result)) | ||
485 | printf("Queue open with total size > 2GB when euid = 99 " | ||
486 | "succeeded:\t\tFAIL\n"); | ||
487 | else | ||
488 | printf("Queue open with total size > 2GB when euid = 99 " | ||
489 | "failed:\t\t\tPASS\n"); | ||
490 | |||
491 | shutdown(0,"",0); | ||
492 | } | ||
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 | } | ||