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