Whamcloud - gitweb
b=16488
[fs/lustre-release.git] / lustre / tests / write_append_truncate.c
1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=8:tabstop=8:
3  *
4  * GPL HEADER START
5  *
6  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License version 2 only,
10  * as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * General Public License version 2 for more details (a copy is included
16  * in the LICENSE file that accompanied this code).
17  *
18  * You should have received a copy of the GNU General Public License
19  * version 2 along with this program; If not, see
20  * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
21  *
22  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
23  * CA 95054 USA or visit www.sun.com if you need additional information or
24  * have any questions.
25  *
26  * GPL HEADER END
27  */
28 /*
29  * Copyright  2008 Sun Microsystems, Inc. All rights reserved
30  * Use is subject to license terms.
31  */
32 /*
33  * This file is part of Lustre, http://www.lustre.org/
34  * Lustre is a trademark of Sun Microsystems, Inc.
35  *
36  * lustre/tests/write_append_truncate.c
37  *
38  * Each loop does 3 things:
39  *   - truncate file to zero (not via ftruncate though, to test O_APPEND)
40  *   - append a "chunk" of data (should be at file offset 0 after truncate)
41  *   - on each of two threads either append or truncate-up the file
42  *
43  * If the truncate happened first, we should have a hole in the file.
44  * If the append happened first, we should have truncated the file down.
45  *
46  * We pick the CHUNK_SIZE_MAX and APPEND_SIZE_MAX so that we cross a stripe.
47  *
48  * compile: mpicc -g -Wall -o write_append_truncate write_append_truncate.c
49  * run:     mpirun -np 2 -machlist <hostlist file> write_append_truncate <file>
50  *  or:     pdsh -w <two hosts> write_append_truncate <file>
51  *  or:     prun -n 2 [-N 2] write_append_truncate <file>
52  */
53
54 #include <stdlib.h>
55 #include <stdio.h>
56 #include <stdarg.h>
57 #include <sys/types.h>
58 #include <sys/stat.h>
59 #include <fcntl.h>
60 #include <errno.h>
61 #include <string.h>
62 #include <unistd.h>
63 #include "mpi.h"
64
65 #define DEFAULT_ITER     50000
66
67 #define CHUNK_SIZE_MAX   123456
68 #define CHUNK_CHAR   'C'
69
70 #define APPEND_SIZE_MAX  123456
71 #define APPEND_CHAR  'A'
72
73 #define TRUNC_SIZE_MAX   (CHUNK_SIZE_MAX+APPEND_SIZE_MAX)
74
75 #define HOSTNAME_SIZE 50
76
77 void usage(char *prog)
78 {
79         printf("usage: %s <filename> [nloops]\n", prog);
80         printf("%s must be run with at least 2 processes\n", prog);
81
82         MPI_Finalize();
83         exit(1);
84 }
85
86 /* Print process rank, loop count, message, and exit (i.e. a fatal error) */
87 void rprintf(int rank, int loop, const char *fmt, ...)
88 {
89         va_list       ap;
90
91         printf("rank %d, loop %d: ", rank, loop);
92
93         va_start(ap, fmt);
94
95         vprintf(fmt, ap);
96
97         MPI_Abort(MPI_COMM_WORLD, -1);
98 }
99
100 int main(int argc, char *argv[])
101 {
102         int n, nloops = 0, fd;
103         int rank, size, ret;
104         int chunk_size, append_size, trunc_offset;
105         char append_buf[APPEND_SIZE_MAX];
106         char chunk_buf[CHUNK_SIZE_MAX];
107         char read_buf[TRUNC_SIZE_MAX+APPEND_SIZE_MAX];
108         char trunc_buf[TRUNC_SIZE_MAX];
109         int done;
110         int error;
111         char hostname[HOSTNAME_SIZE];
112         char *fname, *prog;
113
114         error = MPI_Init(&argc, &argv);
115         if (error != MPI_SUCCESS)
116                 rprintf(-1, -1, "MPI_Init failed: %d\n", error);
117
118         prog = strrchr(argv[0], '/');
119         if (prog == NULL)
120                 prog = argv[0];
121         else
122                 prog++;
123
124         if (argc < 2 || argc > 3)
125                 usage(prog);
126
127         error = MPI_Comm_rank(MPI_COMM_WORLD, &rank);
128         if (error != MPI_SUCCESS)
129                 rprintf(-1, -1, "MPI_Comm_rank failed: %d\n", error);
130
131         error = MPI_Comm_size(MPI_COMM_WORLD, &size);
132         if (error != MPI_SUCCESS)
133                 rprintf(rank, -1, "MPI_Comm_size failed: %d\n", error);
134
135         if (size < 2)
136                 rprintf(rank, -1, "%s: must run with at least 2 processes\n",
137                         prog);
138
139         memset(append_buf, APPEND_CHAR, APPEND_SIZE_MAX);
140         memset(chunk_buf, CHUNK_CHAR, CHUNK_SIZE_MAX);
141         memset(trunc_buf, 0, TRUNC_SIZE_MAX);
142
143         if (gethostname(hostname, HOSTNAME_SIZE) < 0)
144                 rprintf(rank, -1, "gethostname failed: %s\n", strerror(errno));
145
146         fname = argv[1];
147
148         if (argc == 3)
149                 nloops = strtoul(argv[2], NULL, 0);
150         if (nloops == 0)
151                 nloops = DEFAULT_ITER;
152
153         if (rank == 0) {
154                 fd = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
155                 if (fd < 0)
156                         rprintf(0, -1, "create %s failed: %s\n", fname,
157                                 strerror(errno));
158                 printf("using %s\n", fname);
159         }
160         error = MPI_Barrier(MPI_COMM_WORLD);
161         if (error != MPI_SUCCESS)
162                 rprintf(rank, -1, "prep MPI_Barrier failed: %d\n", error);
163
164         fd = open(fname, O_RDWR | O_APPEND);
165         if (fd < 0)
166                 rprintf(rank, -1, "open %s failed: %s\n",fname,strerror(errno));
167
168         for (n = 0; n < nloops; n++) {
169                 /* reset the environment */
170                 chunk_size = (rand()%(CHUNK_SIZE_MAX-1))+1;
171                 append_size = (rand()%(APPEND_SIZE_MAX-1))+1;
172                 trunc_offset = chunk_size + rand()%append_size;
173                 if (rank == 0) {
174                         if (n % 1000 == 0)
175                                 printf("loop %5d: chunk %6d/%#07x, "
176                                        "append %6d/%#07x, trunc @ %6d/%#07x\n",
177                                        n, chunk_size, chunk_size, append_size,
178                                        append_size, trunc_offset, trunc_offset);
179
180                         ret = truncate(fname, (off_t)0);
181                         if (ret < 0)
182                                 rprintf(0, n, "truncate @ 0: %s\n",
183                                         strerror(errno));
184                         done = 0;
185                         do {
186                                 ret = write(fd, chunk_buf+done,chunk_size-done);
187                                 if (ret <= 0) {
188                                         rprintf(0, n, "chunk @ %d: %s\n",
189                                                 done, strerror(errno));
190                                         break;
191                                 }
192                                 done += ret;
193                         } while (done != chunk_size);
194                 }
195
196                 error = MPI_Barrier(MPI_COMM_WORLD);
197                 if (error != MPI_SUCCESS)
198                         rprintf(rank, n, "start MPI_Barrier: %d\n",error);
199
200                 /* Do the race */
201                 if (rank == n % size) {
202                         //
203                         done = 0;
204                         do {
205                                 ret = write(fd, append_buf + done,
206                                             append_size - done);
207                                 if (ret < 0) {
208                                         rprintf(rank, n,
209                                                 "loop %d: append @ %u: %s\n",
210                                                 done, strerror(errno));
211                                         break;
212                                 }
213                                 done += ret;
214                         } while (done != append_size);
215                 } else if (rank == (n + 1) % size) {
216                         ret = truncate(fname, (off_t)trunc_offset);
217                         if (ret != 0)
218                                 rprintf(rank, n, "truncate @ %u: %s\n",
219                                         trunc_offset, strerror(errno) );
220                 }
221
222                 error = MPI_Barrier(MPI_COMM_WORLD);
223                 if (error != MPI_SUCCESS)
224                         rprintf(rank, n, "end MPI_Barrier: %d\n", error);
225
226                 error = 0;
227
228                 /* Check the result */
229                 if (rank == 0) {
230                         struct stat st;
231                         if (stat(fname, &st) < 0)
232                                 rprintf(0, n, "loop %d: stat %s: %s\n",
233                                         fname, strerror(errno));
234
235                         if (lseek(fd, (off_t)0, SEEK_SET) != 0)
236                                 rprintf(0, n, "lseek fname 0: %s\n", fname,
237                                         strerror(errno));
238
239                         done = 0;
240                         do {
241                                 ret = read(fd, read_buf+done, st.st_size-done);
242                                 if (ret < 0) {
243                                         rprintf(0, n, "read @ %u: %s\n",
244                                                done, strerror(errno));
245                                 }
246                                 done += ret;
247                         } while (done != st.st_size);
248
249                         if (memcmp(read_buf, chunk_buf, chunk_size)) {
250                                 printf("loop %d: base chunk bad"
251                                        " [0-%d]/[0-%#x] != %c\n", n,
252                                        chunk_size - 1, chunk_size - 1,
253                                        CHUNK_CHAR);
254                                 error = 1;
255                         }
256
257                         if (st.st_size == trunc_offset) {
258                                 /* Check case 1: first append then truncate */
259                                 error = memcmp(read_buf+chunk_size, append_buf,
260                                                trunc_offset - chunk_size);
261                                 if (error) {
262                                         printf("loop %d: trunc-after-append bad"
263                                                " [%d-%d]/[%#x-%#x] != %c\n",
264                                                n, chunk_size, trunc_offset - 1,
265                                                chunk_size, trunc_offset - 1,
266                                                APPEND_CHAR);
267                                 }
268                         } else {
269                                 /* Check case 2: first truncate then append */
270                                 if (memcmp(read_buf+chunk_size, trunc_buf,
271                                            trunc_offset-chunk_size)) {
272                                         printf("loop %d: append-after-TRUNC bad"
273                                                " [%d-%d]/[%#x-%#x] != 0\n",
274                                                n, chunk_size, trunc_offset - 1,
275                                                chunk_size, trunc_offset - 1);
276                                         error = 1;
277                                 } else if (memcmp(read_buf+trunc_offset,
278                                                   append_buf, append_size)) {
279                                         printf("loop %d: APPEND-after-trunc bad"
280                                                " [%d-%d]/[%#x-%#x] != %c\n",
281                                                n, trunc_offset, append_size - 1,
282                                                trunc_offset, append_size - 1,
283                                                APPEND_CHAR);
284                                         error = 1;
285                                 }
286                         }
287                 }
288                 ret = MPI_Bcast(&error, 1, MPI_INT, 0, MPI_COMM_WORLD);
289                 if (ret != MPI_SUCCESS)
290                         rprintf(rank, n, "MPI_Bcast: %d\n");
291
292                 if (error == 1) {
293                         if (rank == 0) {
294                                 char command[4096];
295
296                                 printf("loop %5d: chunk %6d/%#07x, "
297                                        "append %6d/%#07x, trunc @ %6d/%#07x\n",
298                                        n, chunk_size, chunk_size, append_size,
299                                        append_size, trunc_offset, trunc_offset);
300
301                                 sprintf(command, "od -Ax -a %s", fname);
302                                 system(command);
303                         }
304                         rprintf(rank, n, "on machine %s with pid %d\n",
305                                 hostname, (int)getpid());
306                 }
307         }
308
309         printf("rank %d, loop %d: finished\n", rank, n);
310         close(fd);
311
312         if (rank == 0) {
313                 error = unlink(fname);
314                 if (error < 0)
315                         rprintf(0, n, "unlink %s failed: %s\n",
316                                 fname, strerror(errno));
317         }
318
319         MPI_Finalize();
320         return 0;
321 }