xref: /aosp_15_r20/external/ltp/testcases/kernel/syscalls/ipc/msgstress/msgstress01.c (revision 49cdfc7efb34551c7342be41a7384b9c40d7cab7)
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