Whamcloud - gitweb
LU-17705 ptlrpc: replace synchronize_rcu() with rcu_barrier()
[fs/lustre-release.git] / lustre / tests / writemany.c
1 /*
2  * GPL HEADER START
3  *
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 only,
8  * as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License version 2 for more details (a copy is included
14  * in the LICENSE file that accompanied this code).
15  *
16  * You should have received a copy of the GNU General Public License
17  * version 2 along with this program; If not, see
18  * http://www.gnu.org/licenses/gpl-2.0.html
19  *
20  * GPL HEADER END
21  */
22 /*
23  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
24  * Use is subject to license terms.
25  */
26 /*
27  * This file is part of Lustre, http://www.lustre.org/
28  */
29
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <unistd.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <signal.h>
36 #include <fcntl.h>
37 #include <errno.h>
38 #include <string.h>
39 #include <sys/wait.h>
40 #include <time.h>
41 #include <sys/time.h>
42
43 char cmdname[512];
44 int o_abort;
45 int o_quiet;
46
47 static void usage(char *name)
48 {
49         fprintf(stderr, "usage: %s [opts] <dirname> <seconds> <threads>\n",
50                 name);
51         fprintf(stderr, "  -q quiet\n");
52         fprintf(stderr, "  -a abort other children on first err\n");
53         exit(1);
54 }
55
56 struct kid_list_t {
57         pid_t kid;
58         struct kid_list_t *next;
59 };
60
61 struct kid_list_t *head;
62
63 static int push_kid(pid_t kid)
64 {
65         struct kid_list_t *new;
66
67         new = (struct kid_list_t *)malloc(sizeof(struct kid_list_t));
68         if (!new)
69                 return 1;
70
71         new->kid = kid;
72         new->next = head;
73         head = new;
74         return 0;
75 }
76
77 static void kill_kids(void)
78 {
79         while (head) {
80                 kill(head->kid, SIGTERM);
81                 head = head->next;
82         }
83 }
84
85 static int usr1_received;
86 static void usr1_handler(int unused)
87 {
88         usr1_received = 1;
89         kill_kids();
90 }
91
92 static int wait_for_threads(int live_threads)
93 {
94         int rc = 0;
95
96         while (live_threads > 0) {
97                 int status;
98                 pid_t ret;
99
100                 ret = waitpid(0, &status, 0);
101                 if (ret == 0)
102                         continue;
103
104                 if (ret < 0) {
105                         fprintf(stderr, "%s: error: wait - %s\n",
106                                 cmdname, strerror(errno));
107                         if (!rc)
108                                 rc = errno;
109                 } else {
110                         /*
111                          * This is a hack.  We _should_ be able to use
112                          * WIFEXITED(status) to see if there was an
113                          * error, but it appears to be broken and it
114                          * always returns 1 (OK).  See wait(2).
115                          */
116                         int err = WEXITSTATUS(status);
117
118                         if (err)
119                                 fprintf(stderr,
120                                         "%s: error: PID %d had rc=%d\n",
121                                         cmdname, ret, err);
122                         /* Record first error */
123                         if (!rc)
124                                 rc = err;
125
126                         /* Give up on first error */
127                         if (rc && o_abort) {
128                                 kill_kids();
129                                 break;
130                         }
131
132                         live_threads--;
133                 }
134         }
135         if (!o_quiet)
136                 printf("%s done, rc = %d\n", cmdname, rc);
137         return rc;
138 }
139
140 static void print_err(char *op, char *filename, struct timeval *time, int err)
141 {
142         fprintf(stderr, "%s: %d.%.06d error: %s(%s): %s\n",
143                 cmdname, (int)(time->tv_sec), (int)(time->tv_usec), op,
144                 filename, strerror(errno));
145 }
146
147 static int run_one_child(char *file, int thread, int seconds)
148 {
149         struct timeval start, cur;
150         double diff;
151         char filename[1024];
152         char buf[1024];
153         int fd, rc = 0, rand, maxrand, len;
154         long nfiles = 0, nbytes = 0;
155
156         if (!o_quiet)
157                 printf("%s: running thread #%d\n", cmdname, thread);
158
159         srandom(thread);
160         /*
161          * Higher thread numbers will produce bigger random files.
162          * Thread 1 will produce only 0-len files.
163          */
164         maxrand = 1; rand = thread;
165         while (--rand)
166                 maxrand *= 10;
167
168         gettimeofday(&start, NULL);
169         cur = start;
170
171         while (!rc) {
172                 if (usr1_received)
173                         break;
174
175                 gettimeofday(&cur, NULL);
176                 if (seconds) {
177                         if (cur.tv_sec > (start.tv_sec + seconds))
178                                 break;
179                 }
180
181                 snprintf(filename, sizeof(filename), "%s-%d-%ld",
182                          file, thread, nfiles);
183
184                 fd = open(filename, O_RDWR | O_CREAT, 0666);
185                 if (fd < 0) {
186                         print_err("open", filename, &cur, errno);
187                         rc = errno;
188                         break;
189                 }
190
191                 sprintf(buf, "%s %010ld %.19s.%012d\n", cmdname,
192                         nfiles++, ctime(&cur.tv_sec), (int)cur.tv_usec);
193                 len = strlen(buf);
194
195                 rand = random() % maxrand;
196                 while (rand-- > 0) {
197                         if (write(fd, buf, len) != len) {
198                                 print_err("write", filename, &cur, errno);
199                                 rc = errno;
200                                 break;
201                         }
202                         nbytes += len;
203                 }
204
205                 if (close(fd) < 0) {
206                         print_err("close", filename, &cur, errno);
207                         rc = errno;
208                         break;
209                 }
210                 if (unlink(filename) < 0) {
211                         print_err("unlink", filename, &cur, errno);
212                         if (errno == ENOENT) {
213                                 printf("Ignoring known bug 6082\n");
214                         } else {
215                                 rc = errno;
216                                 break;
217                         }
218                 }
219         }
220
221         diff = difftime(cur.tv_sec, start.tv_sec);
222         if (!o_quiet)
223                 printf("%s: %7ld files, %4ld MB in %.2fs (%7.2f files/s, %5.2f MB/s): rc = %d\n",
224                        cmdname, nfiles, nbytes >> 20, diff,
225                        diff == 0 ? (double)0 : (double)nfiles / diff,
226                        diff == 0 ? (double)0 : (double)nbytes / 1024 / 1024 /
227                        diff, rc);
228
229         return rc;
230 }
231
232 int main(int argc, char *argv[])
233 {
234         unsigned long duration;
235         int threads = 0;
236         char *end;
237         char *directory;
238         int i = 1, rc = 0;
239
240         snprintf(cmdname, sizeof(cmdname), "%s", argv[0]);
241
242         while ((i < argc) && (argv[i][0] == '-')) {
243                 switch (argv[i][1]) {
244                 case 'q':
245                         o_quiet++;
246                         break;
247                 case 'a':
248                         o_abort++;
249                         break;
250                 }
251                 i++;
252         }
253
254         if ((argc - i) < 3)
255                 usage(argv[0]);
256
257         directory = argv[i];
258         duration = strtoul(argv[++i], &end, 0);
259         if (*end) {
260                 fprintf(stderr, "%s: error: bad number of seconds '%s'\n",
261                         cmdname, argv[i]);
262                 exit(2);
263         }
264
265         threads = strtoul(argv[++i], &end, 0);
266         if (*end) {
267                 fprintf(stderr, "%s: error: bad thread count '%s'\n",
268                         cmdname, argv[i]);
269                 exit(2);
270         }
271
272         signal(SIGUSR1, usr1_handler);
273
274         for (i = 1; i <= threads; i++) {
275                 rc = fork();
276                 if (rc < 0) {
277                         if (!o_quiet)
278                                 fprintf(stderr, "%s: error: #%d - %s\n",
279                                         cmdname, i, strerror(rc = errno));
280                         return rc;
281                 }
282                 if (rc == 0) {
283                         /* children */
284                         snprintf(cmdname, sizeof(cmdname), "%s-%d", argv[0], i);
285                         return run_one_child(directory, i, duration);
286                 }
287                 /* parent */
288                 rc = push_kid(rc);
289                 if (rc != 0) {
290                         kill_kids();
291                         exit(3);
292                 }
293         }
294         /* parent process */
295         if (!o_quiet)
296                 printf("%s will run for %ld minutes\n", cmdname, duration / 60);
297         return wait_for_threads(threads);
298 }