diff options
author | Stanislav Kinsbursky <skinsbursky@parallels.com> | 2013-01-04 18:34:56 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-01-04 19:11:45 -0500 |
commit | 3a665531a3b7c2ad2c87903b24646be6916340e4 (patch) | |
tree | 9f5186e3b9786216a74c092b6652d958abc8e2a9 | |
parent | 4a674f34ba04a002244edaf891b5da7fc1473ae8 (diff) |
selftests: IPC message queue copy feature test
This test can be used to check wheither kernel supports IPC message queue
copy and restore features (required by CRIU project).
Signed-off-by: Stanislav Kinsbursky <skinsbursky@parallels.com>
Cc: Serge Hallyn <serge.hallyn@canonical.com>
Cc: "Eric W. Biederman" <ebiederm@xmission.com>
Cc: Pavel Emelyanov <xemul@parallels.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Cc: Michael Kerrisk <mtk.manpages@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | include/linux/msg.h | 3 | ||||
-rw-r--r-- | ipc/compat.c | 3 | ||||
-rw-r--r-- | tools/testing/selftests/ipc/Makefile | 25 | ||||
-rw-r--r-- | tools/testing/selftests/ipc/msgque.c | 246 |
4 files changed, 275 insertions, 2 deletions
diff --git a/include/linux/msg.h b/include/linux/msg.h index fc5743a554e6..391af8d11cce 100644 --- a/include/linux/msg.h +++ b/include/linux/msg.h | |||
@@ -36,6 +36,7 @@ extern long do_msgsnd(int msqid, long mtype, void __user *mtext, | |||
36 | size_t msgsz, int msgflg); | 36 | size_t msgsz, int msgflg); |
37 | extern long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp, | 37 | extern long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp, |
38 | int msgflg, | 38 | int msgflg, |
39 | long (*msg_fill)(void __user *, struct msg_msg *, size_t)); | 39 | long (*msg_fill)(void __user *, struct msg_msg *, |
40 | size_t)); | ||
40 | 41 | ||
41 | #endif /* _LINUX_MSG_H */ | 42 | #endif /* _LINUX_MSG_H */ |
diff --git a/ipc/compat.c b/ipc/compat.c index eb3ea16d2d1d..2547f29dcd1b 100644 --- a/ipc/compat.c +++ b/ipc/compat.c | |||
@@ -365,7 +365,8 @@ long compat_sys_msgrcv(int first, int second, int msgtyp, int third, | |||
365 | uptr = compat_ptr(ipck.msgp); | 365 | uptr = compat_ptr(ipck.msgp); |
366 | msgtyp = ipck.msgtyp; | 366 | msgtyp = ipck.msgtyp; |
367 | } | 367 | } |
368 | return do_msgrcv(first, uptr, second, msgtyp, third, compat_do_msg_fill); | 368 | return do_msgrcv(first, uptr, second, msgtyp, third, |
369 | compat_do_msg_fill); | ||
369 | } | 370 | } |
370 | #else | 371 | #else |
371 | long compat_sys_semctl(int semid, int semnum, int cmd, int arg) | 372 | long compat_sys_semctl(int semid, int semnum, int cmd, int arg) |
diff --git a/tools/testing/selftests/ipc/Makefile b/tools/testing/selftests/ipc/Makefile new file mode 100644 index 000000000000..5386fd7c43ae --- /dev/null +++ b/tools/testing/selftests/ipc/Makefile | |||
@@ -0,0 +1,25 @@ | |||
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../../../../usr/include/ | ||
13 | |||
14 | all: | ||
15 | ifeq ($(ARCH),X86) | ||
16 | gcc $(CFLAGS) msgque.c -o msgque_test | ||
17 | else | ||
18 | echo "Not an x86 target, can't build msgque selftest" | ||
19 | endif | ||
20 | |||
21 | run_tests: all | ||
22 | ./msgque_test | ||
23 | |||
24 | clean: | ||
25 | rm -fr ./msgque_test | ||
diff --git a/tools/testing/selftests/ipc/msgque.c b/tools/testing/selftests/ipc/msgque.c new file mode 100644 index 000000000000..d66418237d21 --- /dev/null +++ b/tools/testing/selftests/ipc/msgque.c | |||
@@ -0,0 +1,246 @@ | |||
1 | #include <stdlib.h> | ||
2 | #include <stdio.h> | ||
3 | #include <string.h> | ||
4 | #include <errno.h> | ||
5 | #include <linux/msg.h> | ||
6 | #include <fcntl.h> | ||
7 | |||
8 | #define MAX_MSG_SIZE 32 | ||
9 | |||
10 | struct msg1 { | ||
11 | int msize; | ||
12 | long mtype; | ||
13 | char mtext[MAX_MSG_SIZE]; | ||
14 | }; | ||
15 | |||
16 | #define TEST_STRING "Test sysv5 msg" | ||
17 | #define MSG_TYPE 1 | ||
18 | |||
19 | #define ANOTHER_TEST_STRING "Yet another test sysv5 msg" | ||
20 | #define ANOTHER_MSG_TYPE 26538 | ||
21 | |||
22 | struct msgque_data { | ||
23 | key_t key; | ||
24 | int msq_id; | ||
25 | int qbytes; | ||
26 | int qnum; | ||
27 | int mode; | ||
28 | struct msg1 *messages; | ||
29 | }; | ||
30 | |||
31 | int restore_queue(struct msgque_data *msgque) | ||
32 | { | ||
33 | int fd, ret, id, i; | ||
34 | char buf[32]; | ||
35 | |||
36 | fd = open("/proc/sys/kernel/msg_next_id", O_WRONLY); | ||
37 | if (fd == -1) { | ||
38 | printf("Failed to open /proc/sys/kernel/msg_next_id\n"); | ||
39 | return -errno; | ||
40 | } | ||
41 | sprintf(buf, "%d", msgque->msq_id); | ||
42 | |||
43 | ret = write(fd, buf, strlen(buf)); | ||
44 | if (ret != strlen(buf)) { | ||
45 | printf("Failed to write to /proc/sys/kernel/msg_next_id\n"); | ||
46 | return -errno; | ||
47 | } | ||
48 | |||
49 | id = msgget(msgque->key, msgque->mode | IPC_CREAT | IPC_EXCL); | ||
50 | if (id == -1) { | ||
51 | printf("Failed to create queue\n"); | ||
52 | return -errno; | ||
53 | } | ||
54 | |||
55 | if (id != msgque->msq_id) { | ||
56 | printf("Restored queue has wrong id (%d instead of %d)\n", | ||
57 | id, msgque->msq_id); | ||
58 | ret = -EFAULT; | ||
59 | goto destroy; | ||
60 | } | ||
61 | |||
62 | for (i = 0; i < msgque->qnum; i++) { | ||
63 | if (msgsnd(msgque->msq_id, &msgque->messages[i].mtype, | ||
64 | msgque->messages[i].msize, IPC_NOWAIT) != 0) { | ||
65 | printf("msgsnd failed (%m)\n"); | ||
66 | ret = -errno; | ||
67 | goto destroy; | ||
68 | }; | ||
69 | } | ||
70 | return 0; | ||
71 | |||
72 | destroy: | ||
73 | if (msgctl(id, IPC_RMID, 0)) | ||
74 | printf("Failed to destroy queue: %d\n", -errno); | ||
75 | return ret; | ||
76 | } | ||
77 | |||
78 | int check_and_destroy_queue(struct msgque_data *msgque) | ||
79 | { | ||
80 | struct msg1 message; | ||
81 | int cnt = 0, ret; | ||
82 | |||
83 | while (1) { | ||
84 | ret = msgrcv(msgque->msq_id, &message.mtype, MAX_MSG_SIZE, | ||
85 | 0, IPC_NOWAIT); | ||
86 | if (ret < 0) { | ||
87 | if (errno == ENOMSG) | ||
88 | break; | ||
89 | printf("Failed to read IPC message: %m\n"); | ||
90 | ret = -errno; | ||
91 | goto err; | ||
92 | } | ||
93 | if (ret != msgque->messages[cnt].msize) { | ||
94 | printf("Wrong message size: %d (expected %d)\n", ret, | ||
95 | msgque->messages[cnt].msize); | ||
96 | ret = -EINVAL; | ||
97 | goto err; | ||
98 | } | ||
99 | if (message.mtype != msgque->messages[cnt].mtype) { | ||
100 | printf("Wrong message type\n"); | ||
101 | ret = -EINVAL; | ||
102 | goto err; | ||
103 | } | ||
104 | if (memcmp(message.mtext, msgque->messages[cnt].mtext, ret)) { | ||
105 | printf("Wrong message content\n"); | ||
106 | ret = -EINVAL; | ||
107 | goto err; | ||
108 | } | ||
109 | cnt++; | ||
110 | } | ||
111 | |||
112 | if (cnt != msgque->qnum) { | ||
113 | printf("Wrong message number\n"); | ||
114 | ret = -EINVAL; | ||
115 | goto err; | ||
116 | } | ||
117 | |||
118 | ret = 0; | ||
119 | err: | ||
120 | if (msgctl(msgque->msq_id, IPC_RMID, 0)) { | ||
121 | printf("Failed to destroy queue: %d\n", -errno); | ||
122 | return -errno; | ||
123 | } | ||
124 | return ret; | ||
125 | } | ||
126 | |||
127 | int dump_queue(struct msgque_data *msgque) | ||
128 | { | ||
129 | struct msqid64_ds ds; | ||
130 | int kern_id; | ||
131 | int i, ret; | ||
132 | |||
133 | for (kern_id = 0; kern_id < 256; kern_id++) { | ||
134 | ret = msgctl(kern_id, MSG_STAT, &ds); | ||
135 | if (ret < 0) { | ||
136 | if (errno == -EINVAL) | ||
137 | continue; | ||
138 | printf("Failed to get stats for IPC queue with id %d\n", | ||
139 | kern_id); | ||
140 | return -errno; | ||
141 | } | ||
142 | |||
143 | if (ret == msgque->msq_id) | ||
144 | break; | ||
145 | } | ||
146 | |||
147 | msgque->messages = malloc(sizeof(struct msg1) * ds.msg_qnum); | ||
148 | if (msgque->messages == NULL) { | ||
149 | printf("Failed to get stats for IPC queue\n"); | ||
150 | return -ENOMEM; | ||
151 | } | ||
152 | |||
153 | msgque->qnum = ds.msg_qnum; | ||
154 | msgque->mode = ds.msg_perm.mode; | ||
155 | msgque->qbytes = ds.msg_qbytes; | ||
156 | |||
157 | for (i = 0; i < msgque->qnum; i++) { | ||
158 | ret = msgrcv(msgque->msq_id, &msgque->messages[i].mtype, | ||
159 | MAX_MSG_SIZE, i, IPC_NOWAIT | MSG_COPY); | ||
160 | if (ret < 0) { | ||
161 | printf("Failed to copy IPC message: %m (%d)\n", errno); | ||
162 | return -errno; | ||
163 | } | ||
164 | msgque->messages[i].msize = ret; | ||
165 | } | ||
166 | return 0; | ||
167 | } | ||
168 | |||
169 | int fill_msgque(struct msgque_data *msgque) | ||
170 | { | ||
171 | struct msg1 msgbuf; | ||
172 | |||
173 | msgbuf.mtype = MSG_TYPE; | ||
174 | memcpy(msgbuf.mtext, TEST_STRING, sizeof(TEST_STRING)); | ||
175 | if (msgsnd(msgque->msq_id, &msgbuf.mtype, sizeof(TEST_STRING), | ||
176 | IPC_NOWAIT) != 0) { | ||
177 | printf("First message send failed (%m)\n"); | ||
178 | return -errno; | ||
179 | }; | ||
180 | |||
181 | msgbuf.mtype = ANOTHER_MSG_TYPE; | ||
182 | memcpy(msgbuf.mtext, ANOTHER_TEST_STRING, sizeof(ANOTHER_TEST_STRING)); | ||
183 | if (msgsnd(msgque->msq_id, &msgbuf.mtype, sizeof(ANOTHER_TEST_STRING), | ||
184 | IPC_NOWAIT) != 0) { | ||
185 | printf("Second message send failed (%m)\n"); | ||
186 | return -errno; | ||
187 | }; | ||
188 | return 0; | ||
189 | } | ||
190 | |||
191 | int main(int argc, char **argv) | ||
192 | { | ||
193 | int msg, pid, err; | ||
194 | struct msgque_data msgque; | ||
195 | |||
196 | msgque.key = ftok(argv[0], 822155650); | ||
197 | if (msgque.key == -1) { | ||
198 | printf("Can't make key\n"); | ||
199 | return -errno; | ||
200 | } | ||
201 | |||
202 | msgque.msq_id = msgget(msgque.key, IPC_CREAT | IPC_EXCL | 0666); | ||
203 | if (msgque.msq_id == -1) { | ||
204 | printf("Can't create queue\n"); | ||
205 | goto err_out; | ||
206 | } | ||
207 | |||
208 | err = fill_msgque(&msgque); | ||
209 | if (err) { | ||
210 | printf("Failed to fill queue\n"); | ||
211 | goto err_destroy; | ||
212 | } | ||
213 | |||
214 | err = dump_queue(&msgque); | ||
215 | if (err) { | ||
216 | printf("Failed to dump queue\n"); | ||
217 | goto err_destroy; | ||
218 | } | ||
219 | |||
220 | err = check_and_destroy_queue(&msgque); | ||
221 | if (err) { | ||
222 | printf("Failed to check and destroy queue\n"); | ||
223 | goto err_out; | ||
224 | } | ||
225 | |||
226 | err = restore_queue(&msgque); | ||
227 | if (err) { | ||
228 | printf("Failed to restore queue\n"); | ||
229 | goto err_destroy; | ||
230 | } | ||
231 | |||
232 | err = check_and_destroy_queue(&msgque); | ||
233 | if (err) { | ||
234 | printf("Failed to test queue\n"); | ||
235 | goto err_out; | ||
236 | } | ||
237 | return 0; | ||
238 | |||
239 | err_destroy: | ||
240 | if (msgctl(msgque.msq_id, IPC_RMID, 0)) { | ||
241 | printf("Failed to destroy queue: %d\n", -errno); | ||
242 | return -errno; | ||
243 | } | ||
244 | err_out: | ||
245 | return err; | ||
246 | } | ||