1 /*
2  * Copyright (C) 2013-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 #define MAX_NANOSLEEP_THREADS	(8)
28 
29 #if defined(HAVE_LIB_PTHREAD) &&	\
30     defined(HAVE_NANOSLEEP)
31 
32 typedef struct {
33 	const stress_args_t *args;
34 	uint64_t counter;
35 	pthread_t pthread;
36 } stress_ctxt_t;
37 
38 static volatile bool thread_terminate;
39 static sigset_t set;
40 #endif
41 
42 static const stress_help_t help[] = {
43 	{ NULL,	"nanosleep N",		"start N workers performing short sleeps" },
44 	{ NULL,	"nanosleep-ops N",	"stop after N bogo sleep operations" },
45 	{ NULL,	NULL,		NULL }
46 };
47 
48 #if defined(HAVE_LIB_PTHREAD)
49 
stress_sigalrm_handler(int signum)50 static void MLOCKED_TEXT stress_sigalrm_handler(int signum)
51 {
52 	(void)signum;
53 
54 	thread_terminate = true;
55 }
56 
57 /*
58  *  stress_pthread_func()
59  *	pthread that performs different ranges of sleeps
60  */
stress_pthread_func(void * c)61 static void *stress_pthread_func(void *c)
62 {
63 	static void *nowt = NULL;
64 	stress_ctxt_t *ctxt = (stress_ctxt_t *)c;
65 	const stress_args_t *args = ctxt->args;
66 	const uint64_t max_ops =
67 		args->max_ops ? (args->max_ops / MAX_NANOSLEEP_THREADS) + 1 : 0;
68 
69 	while (keep_stressing(args) &&
70 	       !thread_terminate &&
71 	       (!max_ops || (ctxt->counter < max_ops))) {
72 		struct timespec tv;
73 		unsigned long i;
74 
75 		for (i = 1 << 18; i > 0; i >>=1) {
76 			tv.tv_sec = 0;
77 			tv.tv_nsec = (stress_mwc32() % i) + 8;
78 			if (nanosleep(&tv, NULL) < 0)
79 				break;
80 		}
81 		ctxt->counter++;
82 	}
83 	return &nowt;
84 }
85 
86 /*
87  *  stress_nanosleep()
88  *	stress nanosleep by many sleeping threads
89  */
stress_nanosleep(const stress_args_t * args)90 static int stress_nanosleep(const stress_args_t *args)
91 {
92 	uint64_t i, n, limited = 0;
93 	static stress_ctxt_t ctxts[MAX_NANOSLEEP_THREADS];
94 	int ret = EXIT_SUCCESS;
95 
96 	if (stress_sighandler(args->name, SIGALRM, stress_sigalrm_handler, NULL) < 0)
97 		return EXIT_FAILURE;
98 
99 	(void)memset(ctxts, 0, sizeof(ctxts));
100 	(void)sigfillset(&set);
101 
102 	for (n = 0; n < MAX_NANOSLEEP_THREADS; n++) {
103 		ctxts[n].args = args;
104 		ret = pthread_create(&ctxts[n].pthread, NULL,
105 			stress_pthread_func, &ctxts[n]);
106 		if (ret) {
107 			/* Out of resources, don't try any more */
108 			if (ret == EAGAIN) {
109 				limited++;
110 				break;
111 			}
112 			/* Something really unexpected */
113 			pr_fail("%s: pthread create failed, errno=%d (%s)\n",
114 				args->name, ret, strerror(ret));
115 			ret = EXIT_NO_RESOURCE;
116 			goto tidy;
117 		}
118 		/* Timed out? abort! */
119 		if (!keep_stressing_flag())
120 			goto tidy;
121 	}
122 
123 	stress_set_proc_state(args->name, STRESS_STATE_RUN);
124 
125 	do {
126 		set_counter(args, 0);
127 		(void)shim_usleep_interruptible(10000);
128 		for (i = 0; i < n; i++)
129 			add_counter(args, ctxts[i].counter);
130 	}  while (!thread_terminate && keep_stressing(args));
131 
132 	ret = EXIT_SUCCESS;
133 tidy:
134 	stress_set_proc_state(args->name, STRESS_STATE_DEINIT);
135 
136 	thread_terminate = true;
137 	for (i = 0; i < n; i++) {
138 		ret = pthread_join(ctxts[i].pthread, NULL);
139 		(void)ret;
140 		/*
141 		if (ret)
142 			pr_dbg("%s: pthread join, ret=%d\n", args->name, ret);
143 		*/
144 	}
145 
146 	if (limited) {
147 		pr_inf("%s: %.2f%% of iterations could not reach "
148 			"requested %d threads (instance %"
149 			PRIu32 ")\n",
150 			args->name,
151 			100.0 * (double)limited / (double)MAX_NANOSLEEP_THREADS,
152 			MAX_NANOSLEEP_THREADS, args->instance);
153 	}
154 
155 	return ret;
156 }
157 
158 stressor_info_t stress_nanosleep_info = {
159 	.stressor = stress_nanosleep,
160 	.class = CLASS_INTERRUPT | CLASS_SCHEDULER | CLASS_OS,
161 	.help = help
162 };
163 #else
164 stressor_info_t stress_nanosleep_info = {
165 	.stressor = stress_not_implemented,
166 	.class = CLASS_INTERRUPT | CLASS_SCHEDULER | CLASS_OS,
167 	.help = help
168 };
169 #endif
170