Whamcloud - gitweb
LU-9771 flr: resync support and test tool
[fs/lustre-release.git] / lustre / tests / mirror_io.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.sun.com/software/products/lustre/docs/GPLv2.pdf
19  *
20  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
21  * CA 95054 USA or visit www.sun.com if you need additional information or
22  * have any questions.
23  *
24  * GPL HEADER END
25  */
26 /*
27  * Copyright (c) 2017, Intel Corporation. All rights reserved.
28  * Use is subject to license terms.
29  *
30  * lustre/tests/mirror_io.c
31  *
32  * Lustre mirror test tool.
33  *
34  * Author: Jinshan Xiong <jinshan.xiong@intel.com>
35  */
36
37 #include <stdio.h>
38 #include <unistd.h>
39 #include <fcntl.h>
40 #include <stdlib.h>
41 #include <errno.h>
42 #include <time.h>
43 #include <sys/types.h>
44 #include <sys/stat.h>
45 #include <sys/param.h>
46 #include <err.h>
47
48 #include <uapi/linux/lustre/lustre_idl.h>
49 #include <lustre/lustreapi.h>
50
51 #define syserr(exp, str, args...)                                       \
52 do {                                                                    \
53         if (exp)                                                        \
54                 errx(EXIT_FAILURE, "%d: "str, __LINE__, ##args);        \
55 } while (0)
56
57 #define syserrx(exp, str, args...)                                      \
58 do {                                                                    \
59         if (exp)                                                        \
60                 errx(EXIT_FAILURE, "%d: "str, __LINE__, ##args);        \
61 } while (0)
62
63 #define ARRAY_SIZE(a) ((sizeof(a)) / (sizeof((a)[0])))
64
65 static const char *progname;
66
67 static void usage(void);
68
69 static int open_file(const char *fname)
70 {
71         struct stat stbuf;
72         int fd;
73
74         if (stat(fname, &stbuf) < 0)
75                 err(1, "%s", fname);
76
77         if (!S_ISREG(stbuf.st_mode))
78                 errx(1, "%s: '%s' is not a regular file", progname, fname);
79
80         fd = open(fname, O_DIRECT | O_RDWR);
81         syserr(fd < 0, "open %s", fname);
82
83         return fd;
84 }
85
86 static size_t get_ids(int fd, unsigned int *ids)
87 {
88         struct llapi_layout *layout;
89         size_t count = 0;
90         int rc;
91
92         layout = llapi_layout_get_by_fd(fd, 0);
93         syserrx(layout == NULL, "layout is NULL");
94
95         rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_FIRST);
96         syserrx(rc < 0, "first component");
97
98         do {
99                 unsigned int id;
100
101                 rc = llapi_layout_mirror_id_get(layout, &id);
102                 syserrx(rc < 0, "id get");
103
104                 if (!count || ids[count - 1] != id)
105                         ids[count++] = id;
106
107                 rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_NEXT);
108                 syserrx(rc < 0, "move to next");
109         } while (rc == 0);
110
111         llapi_layout_free(layout);
112
113         return count;
114 }
115
116 static void check_id(int fd, unsigned int id)
117 {
118         unsigned int ids[LUSTRE_MIRROR_COUNT_MAX];
119         size_t count;
120         bool found = false;
121         int i;
122
123         count = get_ids(fd, ids);
124         for (i = 0; i < count; i++) {
125                 if (id == ids[i]) {
126                         found = true;
127                         break;
128                 }
129         }
130
131         syserr(!found, "cannot find the mirror id: %d", id);
132 }
133
134 static void mirror_dump(int argc, char *argv[])
135 {
136         const char *outfile = NULL;
137         int id = -1;
138         int fd;
139         int outfd;
140         int c;
141         const size_t buflen = 4 * 1024 * 1024;
142         void *buf;
143         off_t pos;
144
145         opterr = 0;
146         while ((c = getopt(argc, argv, "i:o:")) != -1) {
147                 switch (c) {
148                 case 'i':
149                         id = atol(optarg);
150                         break;
151
152                 case 'o':
153                         outfile = optarg;
154                         break;
155
156                 default:
157                         errx(1, "unknown option: '%s'", argv[optind - 1]);
158                 }
159         }
160
161         if (argc > optind + 1)
162                 errx(1, "too many files");
163         if (argc == optind)
164                 errx(1, "no file name given");
165
166         syserrx(id < 0, "mirror id is not set");
167
168         fd = open_file(argv[optind]);
169
170         check_id(fd, id);
171
172         if (outfile) {
173                 outfd = open(outfile, O_EXCL | O_WRONLY | O_CREAT, 0644);
174                 syserr(outfd < 0, "open %s", outfile);
175         } else {
176                 outfd = STDOUT_FILENO;
177         }
178
179         c = posix_memalign(&buf, sysconf(_SC_PAGESIZE), buflen);
180         syserr(c, "posix_memalign");
181
182         pos = 0;
183         while (1) {
184                 ssize_t bytes_read;
185                 ssize_t written;
186
187                 bytes_read = llapi_mirror_read(fd, id, buf, buflen, pos);
188                 if (!bytes_read)
189                         break;
190
191                 syserrx(bytes_read < 0, "mirror read");
192
193                 written = write(outfd, buf, bytes_read);
194                 syserrx(written < bytes_read, "short write");
195
196                 pos += bytes_read;
197         }
198
199         fsync(outfd);
200         close(outfd);
201
202         close(fd);
203
204         free(buf);
205 }
206
207 static size_t add_tids(unsigned int *ids, size_t count, char *arg)
208 {
209         while (*arg) {
210                 char *end;
211                 char *tmp;
212                 int id;
213                 int i;
214
215                 tmp = strchr(arg, ',');
216                 if (tmp)
217                         *tmp = 0;
218
219                 id = strtol(arg, &end, 10);
220                 syserrx(*end || id <= 0, "id string error: '%s'", arg);
221
222                 for (i = 0; i < count; i++)
223                         syserrx(id == ids[i], "duplicate id: %d", id);
224
225                 ids[count++] = (unsigned int)id;
226
227                 if (!tmp)
228                         break;
229
230                 arg = tmp + 1;
231         }
232
233         return count;
234 }
235
236 static void mirror_copy(int argc, char *argv[])
237 {
238         int id = -1;
239         int fd;
240         int c;
241         int i;
242
243         unsigned int ids[4096] = { 0 };
244         size_t count = 0;
245         ssize_t result;
246
247         opterr = 0;
248         while ((c = getopt(argc, argv, "i:t:")) != -1) {
249                 switch (c) {
250                 case 'i':
251                         id = atol(optarg);
252                         break;
253
254                 case 't':
255                         count = add_tids(ids, count, optarg);
256                         break;
257
258                 default:
259                         errx(1, "unknown option: '%s'", argv[optind - 1]);
260                 }
261         }
262
263         if (argc > optind + 1)
264                 errx(1, "too many files");
265         if (argc == optind)
266                 errx(1, "no file name given");
267
268         syserrx(id < 0, "mirror id is not set");
269
270         for (i = 0; i < count; i++)
271                 syserrx(id == ids[i], "src and dst have the same id");
272
273         fd = open_file(argv[optind]);
274
275         check_id(fd, id);
276
277         result = llapi_mirror_copy_many(fd, id, ids, count);
278         syserrx(result < 0, "copy error: %zd", result);
279
280         fprintf(stdout, "mirror copied successfully: ");
281         for (i = 0; i < result; i++)
282                 fprintf(stdout, "%d ", ids[i]);
283         fprintf(stdout, "\n");
284
285         close(fd);
286 }
287
288 /* XXX - does not work. Leave here as place holder */
289 static void mirror_ost_lv(int argc, char *argv[])
290 {
291         int id = -1;
292         int fd;
293         int c;
294         int rc;
295         __u32 layout_version;
296
297         opterr = 0;
298         while ((c = getopt(argc, argv, "i:")) != -1) {
299                 switch (c) {
300                 case 'i':
301                         id = atol(optarg);
302                         break;
303
304                 default:
305                         errx(1, "unknown option: '%s'", argv[optind - 1]);
306                 }
307         }
308
309         if (argc > optind + 1)
310                 errx(1, "too many files");
311         if (argc == optind)
312                 errx(1, "no file name given");
313
314         syserrx(id < 0, "mirror id is not set");
315
316         fd = open_file(argv[optind]);
317
318         check_id(fd, id);
319
320         rc = llapi_mirror_set(fd, id);
321         syserr(rc < 0, "set mirror id error");
322
323         rc = llapi_get_ost_layout_version(fd, &layout_version);
324         syserr(rc < 0, "get ostlayoutversion error");
325
326         llapi_mirror_clear(fd);
327         close(fd);
328
329         fprintf(stdout, "ostlayoutversion: %u\n", layout_version);
330 }
331
332 enum resync_errors {
333         AFTER_RESYNC_START      = 1 << 0,
334         INVALID_IDS             = 1 << 1,
335         ZERO_RESYNC_IDS         = 1 << 2,
336         DELAY_BEFORE_COPY       = 1 << 3,
337         OPEN_TEST_FILE          = 1 << 4,
338 };
339
340 static enum resync_errors resync_parse_error(const char *arg)
341 {
342         struct {
343                 const char *loc;
344                 enum resync_errors  error;
345         } cmds[] = {
346                 { "resync_start", AFTER_RESYNC_START },
347                 { "invalid_ids", INVALID_IDS },
348                 { "zero_resync_ids", ZERO_RESYNC_IDS },
349                 { "delay_before_copy", DELAY_BEFORE_COPY },
350                 { "open_test_file", OPEN_TEST_FILE },
351         };
352         int i;
353
354         for (i = 0; i < ARRAY_SIZE(cmds); i++)
355                 if (strcmp(arg, cmds[i].loc) == 0)
356                         return cmds[i].error;
357
358         syserr(1, "unknown error string: %s", arg);
359         return 0;
360 }
361
362 struct resync_comp {
363         uint64_t start;
364         uint64_t end;
365         uint32_t mirror_id;
366         uint32_t id;    /* component id */
367         bool synced;
368 };
369
370 /* find all stale components */
371 static size_t mirror_find_stale(struct llapi_layout *layout,
372                 struct resync_comp *comp, size_t max_count)
373 {
374         int idx = 0;
375         int rc;
376
377         rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_FIRST);
378         syserr(rc < 0, "llapi_layout_comp_move");
379
380         while (rc == 0) {
381                 uint32_t id;
382                 uint32_t mirror_id;
383                 uint32_t flags;
384                 uint64_t start, end;
385
386                 rc = llapi_layout_mirror_id_get(layout, &mirror_id);
387                 syserr(rc < 0, "llapi_layout_comp_id_get");
388
389                 rc = llapi_layout_comp_id_get(layout, &id);
390                 syserr(rc < 0, "llapi_layout_comp_id_get");
391
392                 rc = llapi_layout_comp_flags_get(layout, &flags);
393                 syserr(rc < 0, "llapi_layout_comp_flags_get");
394
395                 rc = llapi_layout_comp_extent_get(layout, &start, &end);
396                 syserr(rc < 0, "llapi_layout_comp_flags_get");
397
398                 if (flags & LCME_FL_STALE) {
399                         comp[idx].id = id;
400                         comp[idx].mirror_id = mirror_id;
401                         comp[idx].start = start;
402                         comp[idx].end = end;
403                         idx++;
404
405                         syserr(idx >= max_count, "array too small");
406                 }
407
408                 rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_NEXT);
409                 syserr(rc < 0, "llapi_layout_comp_move");
410         }
411
412         return idx;
413 }
414
415 /* locate @layout to a valid component covering file [file_start, file_end) */
416 static uint32_t mirror_find(struct llapi_layout *layout,
417                 uint64_t file_start, uint64_t file_end, uint64_t *endp)
418 {
419         uint32_t mirror_id = 0;
420         int rc;
421
422         rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_FIRST);
423         syserr(rc < 0, "llapi_layout_comp_move");
424
425         *endp = 0;
426         while (rc == 0) {
427                 uint64_t start, end;
428                 uint32_t flags, id, rid;
429
430                 llapi_layout_mirror_id_get(layout, &rid);
431                 syserr(rc < 0, "llapi_layout_mirror_id_get");
432
433                 rc = llapi_layout_comp_id_get(layout, &id);
434                 syserr(rc < 0, "llapi_layout_comp_id_get");
435
436                 rc = llapi_layout_comp_flags_get(layout, &flags);
437                 syserr(rc < 0, "llapi_layout_comp_flags_get");
438
439                 rc = llapi_layout_comp_extent_get(layout, &start, &end);
440                 syserr(rc < 0, "llapi_layout_comp_extent_get");
441
442                 if (!(flags & LCME_FL_STALE)) {
443                         if (file_start >= start && file_start < end) {
444                                 if (mirror_id == 0)
445                                         mirror_id = rid;
446                                 else if (mirror_id != rid || *endp != start)
447                                         break;
448
449                                 file_start = *endp = end;
450                                 if (end >= file_end)
451                                         break;
452                         }
453                 }
454
455                 rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_NEXT);
456                 syserr(rc < 0, "llapi_layout_comp_move");
457         }
458
459         return mirror_id;
460 }
461
462 static char *endstr(uint64_t end)
463 {
464         static char buf[32];
465
466         if (end == (uint64_t)-1)
467                 return "eof";
468
469         snprintf(buf, sizeof(buf), "%lx", end);
470         return buf;
471 }
472
473 static ssize_t mirror_resync_one(int fd, struct llapi_layout *layout,
474                                 uint32_t dst, uint64_t start, uint64_t end)
475 {
476         uint64_t mirror_end;
477         ssize_t result = 0;
478         size_t count;
479
480         if (end == OBD_OBJECT_EOF)
481                 count = OBD_OBJECT_EOF;
482         else
483                 count = end - start;
484
485         while (count > 0) {
486                 uint32_t src;
487                 size_t to_copy;
488                 ssize_t copied;
489
490                 src = mirror_find(layout, start, end, &mirror_end);
491                 syserr(!src, "could find component covering %lu\n", start);
492
493                 if (mirror_end == OBD_OBJECT_EOF)
494                         to_copy = count;
495                 else
496                         to_copy = MIN(count, mirror_end - start);
497
498                 copied = llapi_mirror_copy(fd, src, dst, start, to_copy);
499                 syserr(copied < 0, "llapi_mirror_copy returned %zd\n", copied);
500
501                 printf("src (%u) [%lx -> %s) -> dst (%u), copied %zd bytes\n",
502                         src, start, endstr(mirror_end), dst, copied);
503
504                 result += copied;
505                 if (copied < to_copy) /* end of file */
506                         break;
507
508                 if (count != OBD_OBJECT_EOF)
509                         count -= copied;
510                 start += copied;
511         }
512
513         return result;
514 }
515
516 static void mirror_resync(int argc, char *argv[])
517 {
518         const char *fname;
519         int error_inject = 0;
520         int fd;
521         int c;
522         int rc;
523         int delay = 2;
524         int idx;
525
526         struct llapi_layout *layout;
527         struct ll_ioc_lease *ioc;
528         struct resync_comp comp_array[1024] = { { 0 } };
529         size_t comp_size = 0;
530         uint32_t flr_state;
531
532         opterr = 0;
533         while ((c = getopt(argc, argv, "e:d:")) != -1) {
534                 switch (c) {
535                 case 'e':
536                         error_inject |= resync_parse_error(optarg);
537                         break;
538                 case 'd':
539                         delay = atol(optarg);
540                         break;
541                 default:
542                         errx(1, "unknown option: '%s'", argv[optind - 1]);
543                 }
544         }
545
546         if (argc > optind + 1)
547                 errx(1, "too many files");
548         if (argc == optind)
549                 errx(1, "no file name given");
550
551         fname = argv[optind];
552         fd = open_file(fname);
553
554         /* set the lease on the file */
555         ioc = calloc(sizeof(*ioc) + sizeof(__u32) * 4096, 1);
556         syserr(ioc == NULL, "no memory");
557
558         ioc->lil_mode = LL_LEASE_WRLCK;
559         ioc->lil_flags = LL_LEASE_RESYNC;
560         rc = llapi_lease_get_ext(fd, ioc);
561         syserr(rc < 0, "llapi_lease_get_ext resync");
562
563         if (error_inject & AFTER_RESYNC_START)
564                 syserrx(1, "hit by error injection");
565
566         layout = llapi_layout_get_by_fd(fd, 0);
567         syserr(layout == NULL, "llapi_layout_get_by_fd");
568
569         rc = llapi_layout_flags_get(layout, &flr_state);
570         syserr(rc, "llapi_layout_flags_get");
571
572         flr_state &= LCM_FL_FLR_MASK;
573         syserrx(flr_state != LCM_FL_WRITE_PENDING &&
574                 flr_state != LCM_FL_SYNC_PENDING,
575                 "file state error: %d", flr_state);
576
577         if (error_inject & DELAY_BEFORE_COPY)
578                 sleep(delay);
579
580         comp_size = mirror_find_stale(layout, comp_array,
581                                         ARRAY_SIZE(comp_array));
582
583         printf("%s: found %zd stale components\n", fname, comp_size);
584
585         idx = 0;
586         while (idx < comp_size) {
587                 ssize_t res;
588                 uint64_t end;
589                 uint32_t mirror_id;
590                 int i;
591
592                 rc = llapi_lease_check(fd);
593                 syserr(rc != LL_LEASE_WRLCK, "lost lease lock");
594
595                 mirror_id = comp_array[idx].mirror_id;
596                 end = comp_array[idx].end;
597
598                 printf("%s: resyncing mirror: %u, components: %u ",
599                         fname, mirror_id, comp_array[idx].id);
600
601                 for (i = idx + 1; i < comp_size; i++) {
602                         if (mirror_id != comp_array[i].mirror_id ||
603                             end != comp_array[i].start)
604                                 break;
605
606                         printf("%u ", comp_array[i].id);
607                         end = comp_array[i].end;
608                 }
609                 printf("\b\n");
610
611                 res = mirror_resync_one(fd, layout, mirror_id,
612                                          comp_array[idx].start, end);
613                 if (res > 0) {
614                         int j;
615
616                         printf("components synced: ");
617                         for (j = idx; j < i; j++) {
618                                 comp_array[j].synced = true;
619                                 printf("%u ", comp_array[j].id);
620                         }
621                         printf("\n");
622                 }
623
624                 syserrx(res < 0, "llapi_mirror_copy_many");
625
626                 idx = i;
627         }
628
629         /* prepare ioc for lease put */
630         ioc->lil_mode = LL_LEASE_UNLCK;
631         ioc->lil_flags = LL_LEASE_RESYNC_DONE;
632         ioc->lil_count = 0;
633         for (idx = 0; idx < comp_size; idx++) {
634                 if (comp_array[idx].synced) {
635                         ioc->lil_ids[ioc->lil_count] = comp_array[idx].id;
636                         ioc->lil_count++;
637                 }
638         }
639
640         if (error_inject & ZERO_RESYNC_IDS)
641                 ioc->lil_count = 0;
642
643         if (error_inject & INVALID_IDS && ioc->lil_count > 0)
644                 ioc->lil_ids[ioc->lil_count - 1] = 567; /* inject error */
645
646         llapi_layout_free(layout);
647
648         if (error_inject & OPEN_TEST_FILE) /* break lease */
649                 close(open(argv[optind], O_RDONLY));
650
651         rc = llapi_lease_get_ext(fd, ioc);
652         syserr(rc < 0, "llapi_lease_get_ext resync done");
653
654         syserr(rc == 0, "file busy");
655
656         close(fd);
657 }
658
659 static void usage_wrapper(int argc, char *argv[])
660 {
661         usage();
662 }
663
664 const struct subcommand {
665         const char *name;
666         void (*func)(int argc, char *argv[]);
667         const char *helper;
668 } cmds[] = {
669         { "dump", mirror_dump, "dump mirror: <-i id> [-o file] FILE" },
670         { "copy", mirror_copy, "copy mirror: <-i id> <-t id1,id2> FILE" },
671         { "data_version", mirror_ost_lv, "ost layout version: <-i id> FILE" },
672         { "resync", mirror_resync,
673           "resync mirrors: [-e error] [-d delay] FILE" },
674         { "help", usage_wrapper, "print helper message" },
675 };
676
677 static void usage(void)
678 {
679         int i;
680
681         fprintf(stdout, "%s <command> [OPTIONS] [<FILE>]\n", progname);
682         for (i = 0; i < ARRAY_SIZE(cmds); i++)
683                 fprintf(stdout, "\t%s - %s\n", cmds[i].name, cmds[i].helper);
684
685         exit(0);
686 }
687
688 int main(int argc, char *argv[])
689 {
690         bool found = false;
691         int i;
692
693         progname = basename(argv[0]);
694         if (argc < 3)
695                 usage();
696
697         for (i = 0; i < ARRAY_SIZE(cmds); i++) {
698                 if (strcmp(cmds[i].name, argv[1]))
699                         continue;
700
701                 found = true;
702                 cmds[i].func(argc - 1, argv + 1);
703                 break;
704         }
705
706         if (!found) {
707                 syserrx(1, "unknown subcommand: '%s'", argv[1]);
708                 exit(EXIT_FAILURE);
709         }
710         exit(EXIT_SUCCESS);
711 }