1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (c) International Business Machines Corp., 2002
4 * 06/30/2001 Port to Linux [email protected]
5 * 11/11/2002 Port to LTP [email protected]
6 * Copyright (C) 2024 SUSE LLC Andrea Cervesato <[email protected]>
7 */
8
9 /*\
10 * [Description]
11 *
12 * Stress test for SysV IPC. We send multiple messages at the same time,
13 * checking that we are not loosing any byte once we receive the messages
14 * from multiple children.
15 *
16 * The number of messages to send is determined by the free slots available
17 * in SysV IPC and the available number of children which can be spawned by
18 * the process. Each sender will spawn multiple messages at the same time and
19 * each receiver will read them one by one.
20 */
21
22 #include <stdlib.h>
23 #include "tst_safe_sysv_ipc.h"
24 #include "tst_safe_stdio.h"
25 #include "tst_test.h"
26
27 #define SYSVIPC_TOTAL "/proc/sys/kernel/msgmni"
28 #define SYSVIPC_USED "/proc/sysvipc/msg"
29 #define MSGTYPE 10
30 #define MAXNREPS 100000
31
32 struct sysv_msg {
33 long type;
34 struct {
35 char len;
36 char pbytes[99];
37 } data;
38 };
39
40 struct sysv_data {
41 int id;
42 struct sysv_msg msg;
43 };
44
45 static struct sysv_data *ipc_data;
46 static int ipc_data_len;
47
48 static char *str_num_messages;
49 static char *str_num_iterations;
50 static int num_messages = 1000;
51 static int num_iterations = MAXNREPS;
52 static volatile int *stop;
53 static volatile int *fail;
54 static int *finished;
55 static int *flags;
56
get_used_sysvipc(void)57 static int get_used_sysvipc(void)
58 {
59 FILE *fp;
60 int used = -1;
61 char buf[BUFSIZ];
62
63 fp = SAFE_FOPEN(SYSVIPC_USED, "r");
64
65 while (fgets(buf, BUFSIZ, fp) != NULL)
66 used++;
67
68 SAFE_FCLOSE(fp);
69
70 if (used < 0)
71 tst_brk(TBROK, "Can't read %s to get used sysvipc resource", SYSVIPC_USED);
72
73 return used;
74 }
75
reset_messages(void)76 static void reset_messages(void)
77 {
78 ipc_data_len = 0;
79 memset(ipc_data, 0, sizeof(struct sysv_data) * num_messages);
80
81 for (int i = 0; i < num_messages; i++)
82 ipc_data[i].id = -1;
83
84 *stop = 0;
85 *fail = 0;
86 *finished = 0;
87 }
88
create_message(const int id)89 static int create_message(const int id)
90 {
91 int pos = ipc_data_len;
92 struct sysv_data *buff = ipc_data + pos;
93
94 buff->id = id;
95 buff->msg.type = MSGTYPE;
96 buff->msg.data.len = (rand() % 99) + 1;
97
98 for (int i = 0; i < buff->msg.data.len; i++)
99 buff->msg.data.pbytes[i] = rand() % 255;
100
101 ipc_data_len++;
102
103 return pos;
104 }
105
writer(const int id,const int pos)106 static void writer(const int id, const int pos)
107 {
108 struct sysv_data *buff = &ipc_data[pos];
109 int iter = num_iterations;
110
111 while (--iter >= 0 && !(*stop)) {
112 int size = msgsnd(id, &buff->msg, 100, IPC_NOWAIT);
113
114 if (size < 0) {
115 if (errno == EAGAIN) {
116 usleep(10);
117 continue;
118 }
119
120 tst_brk(TBROK | TERRNO, "msgsnd() failed");
121 }
122 }
123
124 tst_atomic_inc(finished);
125 }
126
reader(const int id,const int pos)127 static void reader(const int id, const int pos)
128 {
129 int size;
130 int iter = num_iterations;
131 struct sysv_msg msg_recv;
132 struct sysv_data *buff = &ipc_data[pos];
133
134 while (--iter >= 0 && !(*stop)) {
135 memset(&msg_recv, 0, sizeof(struct sysv_msg));
136
137 size = msgrcv(id, &msg_recv, 100, MSGTYPE, IPC_NOWAIT);
138 if (size < 0) {
139 if (errno == ENOMSG) {
140 usleep(10);
141 continue;
142 }
143
144 tst_brk(TBROK | TERRNO, "msgrcv() failed");
145 }
146
147 if (msg_recv.type != buff->msg.type) {
148 tst_res(TFAIL, "Received the wrong message type");
149
150 *stop = 1;
151 *fail = 1;
152 return;
153 }
154
155 if (msg_recv.data.len != buff->msg.data.len) {
156 tst_res(TFAIL, "Received the wrong message data length");
157
158 *stop = 1;
159 *fail = 1;
160 return;
161 }
162
163 for (int i = 0; i < msg_recv.data.len; i++) {
164 if (msg_recv.data.pbytes[i] != buff->msg.data.pbytes[i]) {
165 tst_res(TFAIL, "Received wrong data at index %d: %x != %x", i,
166 msg_recv.data.pbytes[i],
167 buff->msg.data.pbytes[i]);
168
169 *stop = 1;
170 *fail = 1;
171 return;
172 }
173 }
174
175 tst_res(TDEBUG, "Received correct data");
176 tst_res(TDEBUG, "msg_recv.type = %ld", msg_recv.type);
177 tst_res(TDEBUG, "msg_recv.data.len = %d", msg_recv.data.len);
178 }
179
180 tst_atomic_inc(finished);
181 }
182
remove_queues(void)183 static void remove_queues(void)
184 {
185 for (int pos = 0; pos < num_messages; pos++) {
186 struct sysv_data *buff = &ipc_data[pos];
187
188 if (buff->id != -1)
189 SAFE_MSGCTL(buff->id, IPC_RMID, NULL);
190 }
191 }
192
run(void)193 static void run(void)
194 {
195 int id, pos;
196
197 reset_messages();
198
199 for (int i = 0; i < num_messages; i++) {
200 id = SAFE_MSGGET(IPC_PRIVATE, IPC_CREAT | 0600);
201 pos = create_message(id);
202
203 if (!SAFE_FORK()) {
204 writer(id, pos);
205 return;
206 }
207
208 if (!SAFE_FORK()) {
209 reader(id, pos);
210 return;
211 }
212
213 if (*stop)
214 break;
215
216 if (!tst_remaining_runtime()) {
217 tst_res(TWARN, "Out of runtime during forking...");
218 *stop = 1;
219 break;
220 }
221 }
222
223 if (!(*stop))
224 tst_res(TINFO, "All processes running");
225
226 for (;;) {
227 if (tst_atomic_load(finished) == 2 * num_messages)
228 break;
229
230 if (*stop)
231 break;
232
233 if (!tst_remaining_runtime()) {
234 tst_res(TINFO, "Out of runtime, stopping processes...");
235 *stop = 1;
236 break;
237 }
238
239 sleep(1);
240 }
241
242 tst_reap_children();
243 remove_queues();
244
245 if (!(*fail))
246 tst_res(TPASS, "Test passed. All messages have been received");
247 }
248
setup(void)249 static void setup(void)
250 {
251 int total_msg;
252 int avail_msg;
253 int free_msgs;
254 int free_pids;
255
256 srand(time(0));
257
258 SAFE_FILE_SCANF(SYSVIPC_TOTAL, "%i", &total_msg);
259
260 free_msgs = total_msg - get_used_sysvipc();
261
262 /* We remove 10% of free pids, just to be sure
263 * we won't saturate the sysyem with processes.
264 */
265 free_pids = tst_get_free_pids() / 2.1;
266
267 avail_msg = MIN(free_msgs, free_pids);
268 if (!avail_msg)
269 tst_brk(TCONF, "Unavailable messages slots");
270
271 tst_res(TINFO, "Available messages slots: %d", avail_msg);
272
273 if (tst_parse_int(str_num_messages, &num_messages, 1, avail_msg))
274 tst_brk(TBROK, "Invalid number of messages '%s'", str_num_messages);
275
276 if (tst_parse_int(str_num_iterations, &num_iterations, 1, MAXNREPS))
277 tst_brk(TBROK, "Invalid number of messages iterations '%s'", str_num_iterations);
278
279 ipc_data = SAFE_MMAP(
280 NULL,
281 sizeof(struct sysv_data) * num_messages,
282 PROT_READ | PROT_WRITE,
283 MAP_SHARED | MAP_ANONYMOUS,
284 -1, 0);
285
286 flags = SAFE_MMAP(
287 NULL,
288 sizeof(int) * 3,
289 PROT_READ | PROT_WRITE,
290 MAP_SHARED | MAP_ANONYMOUS,
291 -1, 0);
292
293 stop = &flags[0];
294 fail = &flags[1];
295 finished = &flags[2];
296 }
297
cleanup(void)298 static void cleanup(void)
299 {
300 if (!ipc_data)
301 return;
302
303 remove_queues();
304
305 SAFE_MUNMAP(ipc_data, sizeof(struct sysv_data) * num_messages);
306 SAFE_MUNMAP(flags, sizeof(int) * 3);
307 }
308
309 static struct tst_test test = {
310 .test_all = run,
311 .setup = setup,
312 .cleanup = cleanup,
313 .forks_child = 1,
314 .max_runtime = 180,
315 .options = (struct tst_option[]) {
316 {"n:", &str_num_messages, "Number of messages to send (default: 1000)"},
317 {"l:", &str_num_iterations, "Number iterations per message (default: "
318 TST_TO_STR(MAXNREPS) ")"},
319 {},
320 },
321 };
322