1 /*
2  * Copyright (C) 2016-2021 Canonical, Ltd.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program 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  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * This code is a complete clean re-write of the stress tool by
19  * Colin Ian King <colin.king@canonical.com> and attempts to be
20  * backwardly compatible with the stress tool by Amos Waterland
21  * <apw@rossby.metr.ou.edu> but has more stress tests and more
22  * functionality.
23  *
24  */
25 #include "stress-ng.h"
26 
27 struct list_entry;
28 
29 typedef void (*stress_list_func)(const stress_args_t *args,
30 				 const size_t n,
31 				 struct list_entry *data);
32 
33 typedef struct {
34 	const char              *name;  /* human readable form of stressor */
35 	const stress_list_func   func;	/* the list method function */
36 } stress_list_method_info_t;
37 
38 static const stress_list_method_info_t list_methods[];
39 
40 static const stress_help_t help[] = {
41 	{ NULL,	"list N",	 "start N workers that exercise list structures" },
42 	{ NULL,	"list-ops N",	 "stop after N bogo list operations" },
43 	{ NULL,	"list-method M", "select tlistmethod, all, circleq, insque, list, slist, stailq, tailq" },
44 	{ NULL,	"list-size N",	 "N is the number of items in the list" },
45 	{ NULL,	NULL,		 NULL }
46 };
47 
48 #if defined(HAVE_SYS_QUEUE_H)
49 
50 static volatile bool do_jmp = true;
51 static sigjmp_buf jmp_env;
52 
53 /*
54  *  Check if macros are defined from sys/queue.h
55  *  before attempting to use them.
56  */
57 #if defined(CIRCLEQ_ENTRY) && 		\
58     defined(CIRCLEQ_HEAD) &&		\
59     defined(CIRCLEQ_INIT) &&		\
60     defined(CIRCLEQ_INSERT_TAIL) &&	\
61     defined(CIRCLEQ_FOREACH) &&		\
62     defined(CIRCLEQ_FIRST) &&		\
63     defined(CIRCLEQ_REMOVE)
64 #define HAVE_SYS_QUEUE_CIRCLEQ
65 #endif
66 
67 #if defined(LIST_ENTRY) &&		\
68     defined(LIST_HEAD) &&		\
69     defined(LIST_INIT) &&		\
70     defined(LIST_INSERT_HEAD) &&	\
71     defined(LIST_FOREACH) &&		\
72     defined(LIST_EMPTY) &&		\
73     defined(LIST_FIRST) &&		\
74     defined(LIST_REMOVE)
75 #define HAVE_SYS_QUEUE_LIST
76 #endif
77 
78 #if defined(SLIST_ENTRY) &&		\
79     defined(SLIST_HEAD) &&		\
80     defined(SLIST_INIT) &&		\
81     defined(SLIST_INSERT_HEAD) &&	\
82     defined(SLIST_FOREACH) &&		\
83     defined(SLIST_EMPTY) &&		\
84     defined(SLIST_REMOVE_HEAD)
85 #define HAVE_SYS_QUEUE_SLIST
86 #endif
87 
88 #if defined(STAILQ_ENTRY) &&		\
89     defined(STAILQ_HEAD) &&		\
90     defined(STAILQ_INIT) &&		\
91     defined(STAILQ_INSERT_TAIL) &&	\
92     defined(STAILQ_FOREACH) &&		\
93     defined(STAILQ_FIRST) &&		\
94     defined(STAILQ_REMOVE)
95 #define HAVE_SYS_QUEUE_STAILQ
96 #endif
97 
98 #if defined(TAILQ_ENTRY) &&		\
99     defined(TAILQ_HEAD) &&		\
100     defined(TAILQ_INIT) &&		\
101     defined(TAILQ_INSERT_TAIL) &&	\
102     defined(TAILQ_FOREACH) &&		\
103     defined(TAILQ_FIRST) &&		\
104     defined(TAILQ_REMOVE)
105 #define HAVE_SYS_QUEUE_TAILQ
106 #endif
107 
108 struct list_entry {
109 	uint64_t value;
110 	union {
111 #if defined(HAVE_SYS_QUEUE_CIRCLEQ)
112 		CIRCLEQ_ENTRY(list_entry) circleq_entries;
113 #endif
114 #if defined(HAVE_SYS_QUEUE_LIST)
115 		LIST_ENTRY(list_entry) list_entries;
116 #endif
117 #if defined(HAVE_SYS_QUEUE_SLIST)
118 		SLIST_ENTRY(list_entry) slist_entries;
119 #endif
120 #if defined(HAVE_SYS_QUEUE_STAILQ)
121 		STAILQ_ENTRY(list_entry) stailq_entries;
122 #endif
123 #if defined(HAVE_SYS_QUEUE_TAILQ)
124 		TAILQ_ENTRY(list_entry) tailq_entries;
125 #endif
126 		struct list_entry *next;
127 	} u;
128 };
129 
130 #if defined(HAVE_SYS_QUEUE_CIRCLEQ)
131 CIRCLEQ_HEAD(circleqhead, list_entry);
132 #endif
133 #if defined(HAVE_SYS_QUEUE_LIST)
134 LIST_HEAD(listhead, list_entry);
135 #endif
136 #if defined(HAVE_SYS_QUEUE_SLIST)
137 SLIST_HEAD(slisthead, list_entry);
138 #endif
139 #if defined(HAVE_SYS_QUEUE_STAILQ)
140 STAILQ_HEAD(stailhead, list_entry);
141 #endif
142 #if defined(HAVE_SYS_QUEUE_TAILQ)
143 TAILQ_HEAD(tailhead, list_entry);
144 #endif
145 
146 #endif
147 
148 /*
149  *  stress_set_list_size()
150  *	set list size
151  */
stress_set_list_size(const char * opt)152 static int stress_set_list_size(const char *opt)
153 {
154 	uint64_t list_size;
155 
156 	list_size = stress_get_uint64(opt);
157 	stress_check_range("list-size", list_size,
158 		MIN_LIST_SIZE, MAX_LIST_SIZE);
159 	return stress_set_setting("list-size", TYPE_ID_UINT64, &list_size);
160 }
161 
162 #if defined(HAVE_SYS_QUEUE_H)
163 
164 /*
165  *  stress_list_handler()
166  *	SIGALRM generic handler
167  */
stress_list_handler(int signum)168 static void MLOCKED_TEXT stress_list_handler(int signum)
169 {
170 	(void)signum;
171 
172 	if (do_jmp) {
173 		do_jmp = false;
174 		siglongjmp(jmp_env, 1);		/* Ugly, bounce back */
175 	}
176 }
177 
stress_list_slistt(const stress_args_t * args,const size_t n,struct list_entry * data)178 static void OPTIMIZE3 stress_list_slistt(
179 	const stress_args_t *args,
180 	const size_t n,
181 	struct list_entry *data)
182 {
183 	size_t i;
184 	register struct list_entry *entry, *head, *tail;
185 	bool found = false;
186 
187 	entry = data;
188 	head = entry;
189 	tail = entry;
190 	entry++;
191 	for (i = 1; i < n; i++, entry++) {
192 		tail->u.next = entry;
193 		tail = entry;
194 	}
195 
196 	for (entry = head, i = 0; i < n; i++, entry++) {
197 		struct list_entry *find;
198 
199 		for (find = head; find; find = find->u.next) {
200 			if (find == entry) {
201 				found = true;
202 				break;
203 			}
204 		}
205 
206 		if (!found)
207 			pr_err("%s: slistt entry #%zd not found\n",
208 				args->name, i);
209 	}
210 	while (head) {
211 		register struct list_entry *next = head->u.next;
212 
213 		head->u.next = NULL;
214 		head = next;
215 	}
216 }
217 
218 #if defined(HAVE_SYS_QUEUE_LIST)
stress_list_list(const stress_args_t * args,const size_t n,struct list_entry * data)219 static void stress_list_list(
220 	const stress_args_t *args,
221 	const size_t n,
222 	struct list_entry *data)
223 {
224 	size_t i;
225 	struct list_entry *entry;
226 	struct listhead head;
227 	bool found = false;
228 
229 	LIST_INIT(&head);
230 
231 	for (entry = data, i = 0; i < n; i++, entry++) {
232 		LIST_INSERT_HEAD(&head, entry, u.list_entries);
233 	}
234 
235 	for (entry = data, i = 0; i < n; i++, entry++) {
236 		struct list_entry *find;
237 
238 		LIST_FOREACH(find, &head, u.list_entries) {
239 			if (find == entry) {
240 				found = true;
241 				break;
242 			}
243 		}
244 
245 		if (!found)
246 			pr_err("%s: list entry #%zd not found\n",
247 				args->name, i);
248 	}
249 	while (!LIST_EMPTY(&head)) {
250 		entry = LIST_FIRST(&head);
251 		LIST_REMOVE(entry, u.list_entries);
252 	}
253 	LIST_INIT(&head);
254 }
255 #endif
256 
257 #if defined(HAVE_SYS_QUEUE_SLIST)
stress_list_slist(const stress_args_t * args,const size_t n,struct list_entry * data)258 static void stress_list_slist(
259 	const stress_args_t *args,
260 	const size_t n,
261 	struct list_entry *data)
262 {
263 	size_t i;
264 	struct list_entry *entry;
265 	struct slisthead head;
266 	bool found = false;
267 
268 	SLIST_INIT(&head);
269 
270 	for (entry = data, i = 0; i < n; i++, entry++) {
271 		SLIST_INSERT_HEAD(&head, entry, u.slist_entries);
272 	}
273 
274 	for (entry = data, i = 0; i < n; i++, entry++) {
275 		struct list_entry *find;
276 
277 		SLIST_FOREACH(find, &head, u.slist_entries) {
278 			if (find == entry) {
279 				found = true;
280 				break;
281 			}
282 		}
283 
284 		if (!found)
285 			pr_err("%s: slist entry #%zd not found\n",
286 				args->name, i);
287 	}
288 	while (!SLIST_EMPTY(&head)) {
289 		SLIST_REMOVE_HEAD(&head, u.slist_entries);
290 	}
291 	SLIST_INIT(&head);
292 }
293 #endif
294 
295 #if defined(HAVE_SYS_QUEUE_CIRCLEQ)
stress_list_circleq(const stress_args_t * args,const size_t n,struct list_entry * data)296 static void stress_list_circleq(
297 	const stress_args_t *args,
298 	const size_t n,
299 	struct list_entry *data)
300 {
301 	size_t i;
302 	struct list_entry *entry;
303 	struct circleqhead head;
304 	bool found = false;
305 
306 	CIRCLEQ_INIT(&head);
307 
308 	for (entry = data, i = 0; i < n; i++, entry++) {
309 		CIRCLEQ_INSERT_TAIL(&head, entry, u.circleq_entries);
310 	}
311 
312 	for (entry = data, i = 0; i < n; i++, entry++) {
313 		struct list_entry *find;
314 
315 		CIRCLEQ_FOREACH(find, &head, u.circleq_entries) {
316 			if (find == entry) {
317 				found = true;
318 				break;
319 			}
320 		}
321 
322 		if (!found)
323 			pr_err("%s: circleq entry #%zd not found\n",
324 				args->name, i);
325 	}
326 	while ((entry = CIRCLEQ_FIRST(&head)) != (struct list_entry *)&head) {
327 		CIRCLEQ_REMOVE(&head, entry, u.circleq_entries);
328 	}
329 	CIRCLEQ_INIT(&head);
330 }
331 #endif
332 
333 #if defined(HAVE_SYS_QUEUE_STAILQ)
stress_list_stailq(const stress_args_t * args,const size_t n,struct list_entry * data)334 static void stress_list_stailq(
335 	const stress_args_t *args,
336 	const size_t n,
337 	struct list_entry *data)
338 {
339 	size_t i;
340 	struct list_entry *entry;
341 	struct stailhead head;
342 	bool found = false;
343 
344 	STAILQ_INIT(&head);
345 
346 	for (entry = data, i = 0; i < n; i++, entry++) {
347 		STAILQ_INSERT_TAIL(&head, entry, u.stailq_entries);
348 	}
349 
350 	for (entry = data, i = 0; i < n; i++, entry++) {
351 		struct list_entry *find;
352 
353 		STAILQ_FOREACH(find, &head, u.stailq_entries) {
354 			if (find == entry) {
355 				found = true;
356 				break;
357 			}
358 		}
359 
360 		if (!found)
361 			pr_err("%s: stailq entry #%zd not found\n",
362 				args->name, i);
363 	}
364 	while ((entry = STAILQ_FIRST(&head)) != NULL) {
365 		STAILQ_REMOVE(&head, entry, list_entry, u.stailq_entries);
366 	}
367 	STAILQ_INIT(&head);
368 }
369 #endif
370 
371 #if defined(HAVE_SYS_QUEUE_TAILQ)
stress_list_tailq(const stress_args_t * args,const size_t n,struct list_entry * data)372 static void stress_list_tailq(
373 	const stress_args_t *args,
374 	const size_t n,
375 	struct list_entry *data)
376 {
377 	size_t i;
378 	struct list_entry *entry;
379 	struct tailhead head;
380 	bool found = false;
381 
382 	TAILQ_INIT(&head);
383 
384 	for (entry = data, i = 0; i < n; i++, entry++) {
385 		TAILQ_INSERT_TAIL(&head, entry, u.tailq_entries);
386 	}
387 
388 	for (entry = data, i = 0; i < n; i++, entry++) {
389 		struct list_entry *find;
390 
391 		TAILQ_FOREACH(find, &head, u.tailq_entries) {
392 			if (find == entry) {
393 				found = true;
394 				break;
395 			}
396 		}
397 
398 		if (!found)
399 			pr_err("%s: tailq entry #%zd not found\n",
400 				args->name, i);
401 	}
402 	while ((entry = TAILQ_FIRST(&head)) != NULL) {
403 		TAILQ_REMOVE(&head, entry, u.tailq_entries);
404 	}
405 	TAILQ_INIT(&head);
406 }
407 #endif
408 
stress_list_all(const stress_args_t * args,const size_t n,struct list_entry * data)409 static void stress_list_all(
410 	const stress_args_t *args,
411 	const size_t n,
412 	struct list_entry *data)
413 {
414 	(void)args;
415 	(void)n;
416 	(void)data;
417 #if defined(HAVE_SYS_QUEUE_CIRCLEQ)
418 	stress_list_circleq(args, n, data);
419 #endif
420 #if defined(HAVE_SYS_QUEUE_LIST)
421 	stress_list_list(args, n, data);
422 #endif
423 #if defined(HAVE_SYS_QUEUE_SLIST)
424 	stress_list_slist(args, n, data);
425 #endif
426 	stress_list_slistt(args, n, data);
427 #if defined(HAVE_SYS_QUEUE_STAILQ)
428 	stress_list_stailq(args, n, data);
429 #endif
430 #if defined(HAVE_SYS_QUEUE_TAILQ)
431 	stress_list_tailq(args, n, data);
432 #endif
433 }
434 #endif
435 
436 /*
437  * Table of list stress methods
438  */
439 static const stress_list_method_info_t list_methods[] = {
440 #if defined(HAVE_SYS_QUEUE_H)
441 	{ "all",	stress_list_all },
442 #if defined(HAVE_SYS_QUEUE_CIRCLEQ)
443 	{ "circleq",	stress_list_circleq },
444 #endif
445 #if defined(HAVE_SYS_QUEUE_LIST)
446 	{ "list",	stress_list_list },
447 #endif
448 #if defined(HAVE_SYS_QUEUE_SLIST)
449 	{ "slist",	stress_list_slist },
450 #endif
451 	{ "slistt",	stress_list_slistt },
452 #if defined(HAVE_SYS_QUEUE_STAILQ)
453 	{ "stailq",	stress_list_stailq },
454 #endif
455 #if defined(HAVE_SYS_QUEUE_TAILQ)
456 	{ "tailq",	stress_list_tailq },
457 #endif
458 #endif
459 	{ NULL,		NULL },
460 };
461 
462 /*
463  *  stress_set_list_method()
464  *	set the default funccal stress method
465  */
stress_set_list_method(const char * name)466 static int stress_set_list_method(const char *name)
467 {
468 	stress_list_method_info_t const *info;
469 
470 	for (info = list_methods; info->func; info++) {
471 		if (!strcmp(info->name, name)) {
472 			stress_set_setting("list-method", TYPE_ID_UINTPTR_T, &info);
473 			return 0;
474 		}
475 	}
476 
477 	(void)fprintf(stderr, "list-method must be one of:");
478 	for (info = list_methods; info->func; info++) {
479 		(void)fprintf(stderr, " %s", info->name);
480 	}
481 	(void)fprintf(stderr, "\n");
482 
483 	return -1;
484 }
485 
486 static const stress_opt_set_func_t opt_set_funcs[] = {
487 	{ OPT_list_method,	stress_set_list_method },
488 	{ OPT_list_size,	stress_set_list_size },
489 	{ 0,			NULL }
490 };
491 
492 #if defined(HAVE_SYS_QUEUE_H)
493 /*
494  *  Rotate right a 64 bit value, compiler
495  *  optimizes this down to a rotate and store
496  */
ror64(const uint64_t val)497 static inline uint64_t ror64(const uint64_t val)
498 {
499 	register uint64_t tmp = val;
500 	register const uint64_t bit0 = (tmp & 1) << 63;
501 
502 	tmp >>= 1;
503 	return (tmp | bit0);
504 }
505 
506 /*
507  *  stress_list()
508  *	stress list
509  */
stress_list(const stress_args_t * args)510 static int stress_list(const stress_args_t *args)
511 {
512 	uint64_t v, list_size = DEFAULT_LIST_SIZE;
513 	struct list_entry *entries, *entry;
514 	size_t n, i, bit;
515 	struct sigaction old_action;
516 	int ret;
517 	stress_list_method_info_t const *info = &list_methods[0];
518 
519 	(void)stress_get_setting("list-method", &info);
520 
521 	if (!stress_get_setting("list-size", &list_size)) {
522 		if (g_opt_flags & OPT_FLAGS_MAXIMIZE)
523 			list_size = MAX_LIST_SIZE;
524 		if (g_opt_flags & OPT_FLAGS_MINIMIZE)
525 			list_size = MIN_LIST_SIZE;
526 	}
527 	n = (size_t)list_size;
528 
529 	entries = calloc(n, sizeof(*entries));
530 	if (!entries) {
531 		pr_fail("%s: malloc failed, out of memory\n", args->name);
532 		return EXIT_NO_RESOURCE;
533 	}
534 
535 	if (stress_sighandler(args->name, SIGALRM, stress_list_handler, &old_action) < 0) {
536 		free(entries);
537 		return EXIT_FAILURE;
538 	}
539 
540 	ret = sigsetjmp(jmp_env, 1);
541 	if (ret) {
542 		/*
543 		 * We return here if SIGALRM jmp'd back
544 		 */
545 		(void)stress_sigrestore(args->name, SIGALRM, &old_action);
546 		goto tidy;
547 	}
548 
549 	v = 0;
550 	for (entry = entries, i = 0, bit = 0; i < n; i++, entry++) {
551 		if (!bit) {
552 			v = stress_mwc64();
553 			bit = 1;
554 		} else {
555 			v ^= bit;
556 			bit <<= 1;
557 		}
558 		entry->value = v;
559 		v = ror64(v);
560 	}
561 
562 	stress_set_proc_state(args->name, STRESS_STATE_RUN);
563 
564 	do {
565 		uint64_t rnd;
566 
567 		info->func(args, n, entries);
568 
569 		rnd = stress_mwc64();
570 		for (entry = entries, i = 0; i < n; i++, entry++)
571 			entry->value = ror64(entry->value ^ rnd);
572 
573 		inc_counter(args);
574 	} while (keep_stressing(args));
575 
576 	do_jmp = false;
577 	(void)stress_sigrestore(args->name, SIGALRM, &old_action);
578 tidy:
579 	stress_set_proc_state(args->name, STRESS_STATE_DEINIT);
580 	free(entries);
581 
582 	return EXIT_SUCCESS;
583 }
584 
585 stressor_info_t stress_list_info = {
586 	.stressor = stress_list,
587 	.class = CLASS_CPU_CACHE | CLASS_CPU | CLASS_MEMORY,
588 	.opt_set_funcs = opt_set_funcs,
589 	.help = help
590 };
591 #else
592 stressor_info_t stress_list_info = {
593 	.stressor = stress_not_implemented,
594 	.class = CLASS_CPU_CACHE | CLASS_CPU | CLASS_MEMORY,
595 	.opt_set_funcs = opt_set_funcs,
596 	.help = help
597 };
598 #endif
599