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 #if defined(HAVE_LIB_PTHREAD)
28 
29 typedef struct {
30 	const stress_args_t *args;
31 	uint64_t counter;
32 	uint64_t sleep_max;
33 	pthread_t pthread;
34 } stress_ctxt_t;
35 
36 static volatile bool thread_terminate;
37 static sigset_t set;
38 #endif
39 
40 static const stress_help_t help[] = {
41 	{ NULL,	"sleep N",	"start N workers performing various duration sleeps" },
42 	{ NULL,	"sleep-ops N",	"stop after N bogo sleep operations" },
43 	{ NULL,	"sleep-max P",	"create P threads at a time by each worker" },
44 	{ NULL,	NULL,		NULL }
45 };
46 
stress_set_sleep_max(const char * opt)47 static int stress_set_sleep_max(const char *opt)
48 {
49 	uint64_t sleep_max;
50 
51 	sleep_max = stress_get_uint64(opt);
52 	stress_check_range("sleep-max", sleep_max,
53 		MIN_SLEEP, MAX_SLEEP);
54 	return stress_set_setting("sleep-max", TYPE_ID_UINT64, &sleep_max);
55 }
56 
57 static const stress_opt_set_func_t opt_set_funcs[] = {
58 	{ OPT_sleep_max,	stress_set_sleep_max },
59 	{ 0,			NULL }
60 };
61 
62 #if defined(HAVE_LIB_PTHREAD)
63 
stress_sigalrm_handler(int signum)64 static void MLOCKED_TEXT stress_sigalrm_handler(int signum)
65 {
66 	(void)signum;
67 
68 	thread_terminate = true;
69 }
70 
71 /*
72  *  stress_pthread_func()
73  *	pthread that performs different ranges of sleeps
74  */
stress_pthread_func(void * c)75 static void *stress_pthread_func(void *c)
76 {
77 	static void *nowt = NULL;
78 	stress_ctxt_t *ctxt = (stress_ctxt_t *)c;
79 	const stress_args_t *args = ctxt->args;
80 	const uint64_t max_ops =
81 		args->max_ops ? (args->max_ops / ctxt->sleep_max) + 1 : 0;
82 
83 	while (keep_stressing(args) &&
84 	       !thread_terminate &&
85 	       (!max_ops || (ctxt->counter < max_ops))) {
86 		struct timespec tv;
87 #if defined(HAVE_SYS_SELECT_H)
88 		struct timeval timeout;
89 #endif
90 
91 		tv.tv_sec = 0;
92 		tv.tv_nsec = 1;
93 		if (nanosleep(&tv, NULL) < 0)
94 			break;
95 		tv.tv_sec = 0;
96 		tv.tv_nsec = 10;
97 		if (nanosleep(&tv, NULL) < 0)
98 			break;
99 		tv.tv_sec = 0;
100 		tv.tv_nsec = 100;
101 		if (nanosleep(&tv, NULL) < 0)
102 			break;
103 		if (shim_usleep(1) < 0)
104 			break;
105 		if (shim_usleep(10) < 0)
106 			break;
107 		if (shim_usleep(100) < 0)
108 			break;
109 		if (shim_usleep(1000) < 0)
110 			break;
111 		if (shim_usleep(10000) < 0)
112 			break;
113 
114 #if defined(HAVE_SYS_SELECT_H)
115 		timeout.tv_sec = 0;
116 		timeout.tv_usec = 10;
117 		if (select(0, NULL, NULL, NULL, &timeout) < 0)
118 			break;
119 		timeout.tv_sec = 0;
120 		timeout.tv_usec = 100;
121 		if (select(0, NULL, NULL, NULL, &timeout) < 0)
122 			break;
123 		timeout.tv_sec = 0;
124 		timeout.tv_usec = 1000;
125 		if (select(0, NULL, NULL, NULL, &timeout) < 0)
126 			break;
127 		timeout.tv_sec = 0;
128 		timeout.tv_usec = 10000;
129 		if (select(0, NULL, NULL, NULL, &timeout) < 0)
130 			break;
131 #endif
132 
133 		ctxt->counter++;
134 	}
135 	return &nowt;
136 }
137 
138 /*
139  *  stress_sleep()
140  *	stress by many sleeping threads
141  */
stress_sleep(const stress_args_t * args)142 static int stress_sleep(const stress_args_t *args)
143 {
144 	uint64_t i, n, limited = 0;
145 	uint64_t sleep_max = DEFAULT_SLEEP;
146 	static stress_ctxt_t ctxts[MAX_SLEEP];
147 	int ret = EXIT_SUCCESS;
148 
149 	if (!stress_get_setting("sleep-max", &sleep_max)) {
150 		if (g_opt_flags & OPT_FLAGS_MAXIMIZE)
151 			sleep_max = MAX_SLEEP;
152 		if (g_opt_flags & OPT_FLAGS_MINIMIZE)
153 			sleep_max = MIN_SLEEP;
154 	}
155 
156 	if (stress_sighandler(args->name, SIGALRM, stress_sigalrm_handler, NULL) < 0)
157 		return EXIT_FAILURE;
158 
159 	(void)memset(ctxts, 0, sizeof(ctxts));
160 	(void)sigfillset(&set);
161 
162 	stress_set_proc_state(args->name, STRESS_STATE_RUN);
163 
164 	for (n = 0; n < sleep_max; n++) {
165 		ctxts[n].args = args;
166 		ctxts[n].sleep_max = sleep_max;
167 		ret = pthread_create(&ctxts[n].pthread, NULL,
168 			stress_pthread_func, &ctxts[n]);
169 		if (ret) {
170 			/* Out of resources, don't try any more */
171 			if (ret == EAGAIN) {
172 				limited++;
173 				break;
174 			}
175 			/* Something really unexpected */
176 			pr_fail("%s: pthread create failed, errno=%d (%s)\n",
177 				args->name, ret, strerror(ret));
178 			ret = EXIT_NO_RESOURCE;
179 			goto tidy;
180 		}
181 		/* Timed out? abort! */
182 		if (!keep_stressing_flag())
183 			goto tidy;
184 	}
185 
186 	do {
187 		set_counter(args, 0);
188 		(void)shim_usleep_interruptible(10000);
189 		for (i = 0; i < n; i++)
190 			add_counter(args, ctxts[i].counter);
191 	}  while (!thread_terminate && keep_stressing(args));
192 
193 	ret = EXIT_SUCCESS;
194 tidy:
195 	stress_set_proc_state(args->name, STRESS_STATE_DEINIT);
196 
197 	thread_terminate = true;
198 	for (i = 0; i < n; i++) {
199 		ret = pthread_join(ctxts[i].pthread, NULL);
200 		(void)ret;
201 		/*
202 		if (ret)
203 			pr_dbg("%s: pthread join, ret=%d\n", args->name, ret);
204 		*/
205 	}
206 
207 	if (limited) {
208 		pr_inf("%s: %.2f%% of iterations could not reach "
209 			"requested %" PRIu64 " threads (instance %"
210 			PRIu32 ")\n",
211 			args->name,
212 			100.0 * (double)limited / (double)sleep_max,
213 			sleep_max, args->instance);
214 	}
215 
216 	return ret;
217 }
218 
219 stressor_info_t stress_sleep_info = {
220 	.stressor = stress_sleep,
221 	.class = CLASS_INTERRUPT | CLASS_SCHEDULER | CLASS_OS,
222 	.opt_set_funcs = opt_set_funcs,
223 	.help = help
224 };
225 #else
226 stressor_info_t stress_sleep_info = {
227 	.stressor = stress_not_implemented,
228 	.class = CLASS_INTERRUPT | CLASS_SCHEDULER | CLASS_OS,
229 	.opt_set_funcs = opt_set_funcs,
230 	.help = help
231 };
232 #endif
233