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