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
stress_try_kill(const stress_args_t * args,const pid_t pid,const char * path)27 static void stress_try_kill(
28 const stress_args_t *args,
29 const pid_t pid,
30 const char *path)
31 {
32 int i;
33
34 for (i = 0; i < 10; i++) {
35 int ret, status;
36
37 ret = kill(pid, SIGKILL);
38 (void)ret;
39 ret = waitpid(pid, &status, WNOHANG);
40 (void)ret;
41 if ((kill(pid, 0) < 0) && (errno == ESRCH))
42 return;
43 (void)shim_usleep(100000);
44 }
45 pr_dbg("%s: can't kill pid %" PRIdMAX " opening %s\n",
46 args->name, (intmax_t)pid, path);
47 }
48
49 /*
50 * Try to open a file, return 0 if can open it, non-zero
51 * if it cannot be opened within timeout nanoseconds.
52 */
stress_try_open(const stress_args_t * args,const char * path,const int flags,const unsigned long timeout_ns)53 int stress_try_open(
54 const stress_args_t *args,
55 const char *path,
56 const int flags,
57 const unsigned long timeout_ns)
58 {
59 pid_t pid;
60 int ret, status = 0;
61 struct stat statbuf;
62 const int retries = 20;
63 unsigned long sleep_ns = timeout_ns / retries;
64 int i;
65
66 (void)args;
67
68 /* Don't try to open if file can't be stat'd */
69 if (stat(path, &statbuf) < 0)
70 return -1;
71
72 pid = fork();
73 if (pid < 0)
74 return STRESS_TRY_OPEN_FORK_FAIL;
75 if (pid == 0) {
76 int fd;
77
78 (void)alarm(1);
79 fd = open(path, flags);
80 if (fd < 0) {
81 /* blocked or out of memory, don't give up */
82 if ((errno == EBUSY) ||
83 (errno == ENOMEM))
84 _exit(STRESS_TRY_AGAIN);
85 _exit(STRESS_TRY_OPEN_FAIL);
86 }
87 _exit(STRESS_TRY_OPEN_OK);
88 }
89
90 for (i = 0; i < retries; i++) {
91 /*
92 * Child may block on open forever if the driver
93 * is broken, so use WNOHANG wait to poll rather
94 * than wait forever on a locked up process
95 */
96 ret = waitpid(pid, &status, WNOHANG);
97 if (ret < 0) {
98 /*
99 * EINTR or something else, treat as failed anyhow
100 * and forcibly kill child and re-wait. The child
101 * may be zombified by will get reaped by init
102 */
103 stress_try_kill(args, pid, path);
104
105 return STRESS_TRY_OPEN_WAIT_FAIL;
106 }
107 /* Has pid gone? */
108 if ((kill(pid, 0) < 0) && (errno == ESRCH))
109 goto done;
110
111 /* Sleep and retry */
112 (void)shim_nanosleep_uint64(sleep_ns);
113 }
114
115 /* Give up, force kill */
116 stress_try_kill(args, pid, path);
117 done:
118 /* Seems like we can open the device successfully */
119 if (WIFEXITED(status))
120 return WEXITSTATUS(status);
121
122 return STRESS_TRY_OPEN_EXIT_FAIL;
123 }
124
125 #if defined(HAVE_LIB_RT) && \
126 defined(HAVE_TIMER_CREATE) && \
127 defined(HAVE_TIMER_DELETE) && \
128 defined(HAVE_TIMER_GETOVERRUN) && \
129 defined(HAVE_TIMER_SETTIME)
130
stress_timer_handler(int sig)131 static void MLOCKED_TEXT stress_timer_handler(int sig)
132 {
133 (void)sig;
134 }
135
136 /*
137 * Try to open a file, return 0 if can open it, non-zero
138 * if it cannot be opened within timeout nanoseconds.
139 */
stress_open_timeout(const char * name,const char * path,const int flags,const unsigned long timeout_ns)140 int stress_open_timeout(
141 const char *name,
142 const char *path,
143 const int flags,
144 const unsigned long timeout_ns)
145 {
146 int ret, t_ret, tmp;
147 struct sigevent sev;
148 timer_t timerid;
149 struct itimerspec timer;
150
151 /*
152 * If a handler can't be installed then
153 * we can't test, so just return 0 and try
154 * it anyhow.
155 */
156 ret = stress_sighandler(name, SIGRTMIN, stress_timer_handler, NULL);
157 if (ret < 0)
158 return open(path, flags);
159
160 /*
161 * Enable a timer to interrupt log open waits
162 */
163 (void)memset(&sev, 0, sizeof(sev));
164 sev.sigev_notify = SIGEV_SIGNAL;
165 sev.sigev_signo = SIGRTMIN;
166 sev.sigev_value.sival_ptr = &timerid;
167
168 t_ret = timer_create(CLOCK_REALTIME, &sev, &timerid);
169 if (!t_ret) {
170 timer.it_value.tv_sec = timeout_ns / STRESS_NANOSECOND;
171 timer.it_value.tv_nsec = timeout_ns % STRESS_NANOSECOND;
172 timer.it_interval.tv_sec = timer.it_value.tv_sec;
173 timer.it_interval.tv_nsec = timer.it_value.tv_nsec;
174 t_ret = timer_settime(timerid, 0, &timer, NULL);
175 }
176 ret = open(path, flags);
177 tmp = errno;
178 if (!t_ret)
179 (void)timer_delete(timerid);
180
181 errno = tmp;
182 return ret;
183 }
184 #else
stress_open_timeout(const char * name,const char * path,const int flags,const unsigned long timeout_ns)185 int stress_open_timeout(
186 const char *name,
187 const char *path,
188 const int flags,
189 const unsigned long timeout_ns)
190 {
191 (void)name;
192 (void)timeout_ns;
193
194 return open(path, flags);
195 }
196 #endif
197