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