2
* Author: Colin King <colin.king@canonical.com>
4
* Copyright (C) 2012 Canonical, Ltd.
6
* This program is free software; you can redistribute it and/or
7
* modify it under the terms of the GNU General Public License
8
* as published by the Free Software Foundation; either version 2
9
* of the License, or (at your option) any later version.
11
* This program is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with this program; if not, write to the Free Software
18
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
26
#include <sys/types.h>
28
#include <sys/select.h>
34
#define TEST_PASSED (0)
35
#define TEST_FAILED (1)
36
#define TEST_ERROR (2)
38
#define MAX_FILES (16) /* number of files to create per iteration */
39
#define THREADS_PER_CPU (8) /* number of child processes per CPU */
41
#define TIMEOUT (30) /* system hang timeout in seconds */
43
#define TEST_DURATION (60) /* duration of test (seconds) */
44
#define MIN_DURATION (1) /* minimum test duration (seconds) */
51
#define DIE_SIGINT (1) /* Kill test threads because of SIGINT */
52
#define DIE_COMPLETE (2) /* Kill test threads because end of test duration */
54
static volatile int die = 0;
56
static char *options[] = {
64
* Create many threads that try and create create/truncate/unlink aces
68
* Run creat/tuncate/unlink as a child and detect any timeouts. This
69
* is a little heavy handed, but allows us to detect kernel
70
* hangs on the syscalls if we lock up.
73
int hang_check(int option, const char *filename)
86
if (pipe(pipefd) < 0) {
87
fprintf(stderr, "pipe error\n");
94
fprintf(stderr, "failed to fork child\n");
103
fd = creat(filename, 0700);
110
* We want to try and force a lock up -
111
* we don't care about the return since
112
* the file may not exit, but we assign
113
* ret to keep gcc happy
115
ret = truncate(filename, 64 * 1024);
116
ret = truncate(filename, 128 * 1024);
117
ret = truncate(filename, 64 * 1024);
118
ret = truncate(filename, 0);
119
ret = truncate(filename, 64 * 1024);
129
if (write(pipefd[1], "EXIT", 4) < 0)
130
fprintf(stderr, "pipe write failed\n");
141
FD_SET(pipefd[0], &readfds);
143
/* parent, sleep until we get a message from the child */
144
ret = select(pipefd[0]+1, &readfds, NULL, NULL, &tv);
149
/* Timed out on select, no signal from child! */
150
fprintf(stderr, "Timed out after %d seconds doing %s - possible eCryptfs hang\n",
151
TIMEOUT, options[option > TRUNCATE ? UNKNOWN : option]);
152
/* Vainly attempt to kill child */
156
if (errno != EINTR) {
157
fprintf(stderr, "Unexpected return from select(): %d %s\n", errno, strerror(errno));
158
waitpid(pid, &status, 0);
163
* We got sent a signal from controlling process to
164
* tell us to stop, so return TEST_PASSED since we have
165
* not detected any failures from our child
167
waitpid(pid, &status, 0);
171
/* Child completed the required operation and wrote down the pipe, lets reap */
172
waitpid(pid, &status, 0);
178
int test_files(const char *path, const int max_files)
182
size_t len = strlen(path) + 32;
183
int ret = TEST_PASSED;
185
if ((filename = malloc(len)) == NULL) {
186
fprintf(stderr, "failed to malloc filename\n");
191
for (i = 0; i < max_files; i++) {
192
snprintf(filename, len, "%s/%d", path, i);
193
if ((ret = hang_check(CREAT, filename)) != TEST_PASSED) {
201
for (i = 0; i < max_files; i++) {
202
snprintf(filename, len, "%s/%d", path, i);
203
if ((ret = hang_check(TRUNCATE, filename)) != TEST_PASSED) {
212
for (i = 0; i < max_files; i++) {
213
snprintf(filename, len, "%s/%d", path, i);
214
if ((ret = hang_check(UNLINK, filename)) != TEST_PASSED) {
226
if (die & DIE_SIGINT)
227
ret = TEST_ERROR; /* Got aborted */
232
void sigint_handler(int dummy)
237
void sigusr1_handler(int dummy)
242
int test_exercise(const char *path, const int max_files, const int duration)
245
long threads = sysconf(_SC_NPROCESSORS_CONF) * THREADS_PER_CPU;
247
int ret = TEST_PASSED;
249
if ((pids = calloc(threads, sizeof(pid_t))) == NULL) {
250
fprintf(stderr, "failed to calloc pids\n");
254
/* Go forth and multiply.. */
255
for (i = 0; i < threads; i++) {
261
fprintf(stderr, "failed to fork child %d of %ld\n", i+1, threads);
264
exit(test_files(path, max_files));
273
for (i = 0; i < threads; i++)
274
kill(pids[i], SIGUSR1);
276
for (i = 0; i < threads; i++) {
278
waitpid(pids[i], &status, 0);
280
if (WEXITSTATUS(status) != TEST_PASSED)
281
ret = WEXITSTATUS(status);
289
void show_usage(char *name)
291
fprintf(stderr, "Syntax: %s [-d duration] pathname\n", name);
292
fprintf(stderr, "\t-d duration of test (in seconds)\n");
296
int main(int argc, char **argv)
299
int duration = TEST_DURATION;
301
while ((opt = getopt(argc, argv, "d:")) != -1) {
304
duration = atoi(optarg);
315
if (duration < MIN_DURATION) {
317
"Test duration must be %d or more seconds long.\n",
322
if (access(argv[optind], R_OK | W_OK) < 0) {
323
fprintf(stderr, "Cannot access %s\n", argv[1]);
327
signal(SIGINT, sigint_handler);
328
signal(SIGUSR1, sigusr1_handler);
330
exit(test_exercise(argv[optind], MAX_FILES, duration));