Add -p switch to "zpool get"
[zfs.git] / cmd / zpios / zpios_main.c
1 /*****************************************************************************\
2  *  ZPIOS is a heavily modified version of the original PIOS test code.
3  *  It is designed to have the test code running in the Linux kernel
4  *  against ZFS while still being flexibly controled from user space.
5  *
6  *  Copyright (C) 2008-2010 Lawrence Livermore National Security, LLC.
7  *  Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
8  *  Written by Brian Behlendorf <behlendorf1@llnl.gov>.
9  *  LLNL-CODE-403049
10  *
11  *  Original PIOS Test Code
12  *  Copyright (C) 2004 Cluster File Systems, Inc.
13  *  Written by Peter Braam <braam@clusterfs.com>
14  *             Atul Vidwansa <atul@clusterfs.com>
15  *             Milind Dumbare <milind@clusterfs.com>
16  *
17  *  This file is part of ZFS on Linux.
18  *  For details, see <http://zfsonlinux.org/>.
19  *
20  *  ZPIOS is free software; you can redistribute it and/or modify it
21  *  under the terms of the GNU General Public License as published by the
22  *  Free Software Foundation; either version 2 of the License, or (at your
23  *  option) any later version.
24  *
25  *  ZPIOS is distributed in the hope that it will be useful, but WITHOUT
26  *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
27  *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
28  *  for more details.
29  *
30  *  You should have received a copy of the GNU General Public License along
31  *  with ZPIOS.  If not, see <http://www.gnu.org/licenses/>.
32 \*****************************************************************************/
33
34 #include <stdlib.h>
35 #include <stdio.h>
36 #include <string.h>
37 #include <errno.h>
38 #include <getopt.h>
39 #include <assert.h>
40 #include <fcntl.h>
41 #include <unistd.h>
42 #include <sys/ioctl.h>
43 #include "zpios.h"
44
45 static const char short_opt[] = "t:l:h:e:n:i:j:k:o:m:q:r:c:a:b:g:s:A:B:C:"
46                                 "L:p:M:xP:R:G:I:N:T:VzOfHv?";
47 static const struct option long_opt[] = {
48         {"threadcount",         required_argument, 0, 't' },
49         {"threadcount_low",     required_argument, 0, 'l' },
50         {"threadcount_high",    required_argument, 0, 'h' },
51         {"threadcount_incr",    required_argument, 0, 'e' },
52         {"regioncount",         required_argument, 0, 'n' },
53         {"regioncount_low",     required_argument, 0, 'i' },
54         {"regioncount_high",    required_argument, 0, 'j' },
55         {"regioncount_incr",    required_argument, 0, 'k' },
56         {"offset",              required_argument, 0, 'o' },
57         {"offset_low",          required_argument, 0, 'm' },
58         {"offset_high",         required_argument, 0, 'q' },
59         {"offset_incr",         required_argument, 0, 'r' },
60         {"chunksize",           required_argument, 0, 'c' },
61         {"chunksize_low",       required_argument, 0, 'a' },
62         {"chunksize_high",      required_argument, 0, 'b' },
63         {"chunksize_incr",      required_argument, 0, 'g' },
64         {"regionsize",          required_argument, 0, 's' },
65         {"regionsize_low",      required_argument, 0, 'A' },
66         {"regionsize_high",     required_argument, 0, 'B' },
67         {"regionsize_incr",     required_argument, 0, 'C' },
68         {"load",                required_argument, 0, 'L' },
69         {"pool",                required_argument, 0, 'p' },
70         {"name",                required_argument, 0, 'M' },
71         {"cleanup",             no_argument,       0, 'x' },
72         {"prerun",              required_argument, 0, 'P' },
73         {"postrun",             required_argument, 0, 'R' },
74         {"log",                 required_argument, 0, 'G' },
75         {"regionnoise",         required_argument, 0, 'I' },
76         {"chunknoise",          required_argument, 0, 'N' },
77         {"threaddelay",         required_argument, 0, 'T' },
78         {"verify",              no_argument,       0, 'V' },
79         {"zerocopy",            no_argument,       0, 'z' },
80         {"nowait",              no_argument,       0, 'O' },
81         {"noprefetch",          no_argument,       0, 'f' },
82         {"human-readable",      no_argument,       0, 'H' },
83         {"verbose",             no_argument,       0, 'v' },
84         {"help",                no_argument,       0, '?' },
85         { 0,                    0,                 0,  0  },
86 };
87
88 static int zpiosctl_fd;                         /* Control file descriptor */
89 static char zpios_version[VERSION_SIZE];        /* Kernel version string */
90 static char *zpios_buffer = NULL;               /* Scratch space area */
91 static int zpios_buffer_size = 0;               /* Scratch space size */
92
93 static int
94 usage(void)
95 {
96         fprintf(stderr, "Usage: zpios\n");
97         fprintf(stderr,
98                 "       --threadcount       -t    =values\n"
99                 "       --threadcount_low   -l    =value\n"
100                 "       --threadcount_high  -h    =value\n"
101                 "       --threadcount_incr  -e    =value\n"
102                 "       --regioncount       -n    =values\n"
103                 "       --regioncount_low   -i    =value\n"
104                 "       --regioncount_high  -j    =value\n"
105                 "       --regioncount_incr  -k    =value\n"
106                 "       --offset            -o    =values\n"
107                 "       --offset_low        -m    =value\n"
108                 "       --offset_high       -q    =value\n"
109                 "       --offset_incr       -r    =value\n"
110                 "       --chunksize         -c    =values\n"
111                 "       --chunksize_low     -a    =value\n"
112                 "       --chunksize_high    -b    =value\n"
113                 "       --chunksize_incr    -g    =value\n"
114                 "       --regionsize        -s    =values\n"
115                 "       --regionsize_low    -A    =value\n"
116                 "       --regionsize_high   -B    =value\n"
117                 "       --regionsize_incr   -C    =value\n"
118                 "       --load              -L    =dmuio|ssf|fpp\n"
119                 "       --pool              -p    =pool name\n"
120                 "       --name              -M    =test name\n"
121                 "       --cleanup           -x\n"
122                 "       --prerun            -P    =pre-command\n"
123                 "       --postrun           -R    =post-command\n"
124                 "       --log               -G    =log directory\n"
125                 "       --regionnoise       -I    =shift\n"
126                 "       --chunknoise        -N    =bytes\n"
127                 "       --threaddelay       -T    =jiffies\n"
128                 "       --verify            -V\n"
129                 "       --zerocopy          -z\n"
130                 "       --nowait            -O\n"
131                 "       --noprefetch        -f\n"
132                 "       --human-readable    -H\n"
133                 "       --verbose           -v    =increase verbosity\n"
134                 "       --help              -?    =this help\n\n");
135
136         return 0;
137 }
138
139 static void args_fini(cmd_args_t *args)
140 {
141         assert(args != NULL);
142         free(args);
143 }
144
145 static cmd_args_t *
146 args_init(int argc, char **argv)
147 {
148         cmd_args_t *args;
149         uint32_t fl_th = 0;
150         uint32_t fl_rc = 0;
151         uint32_t fl_of = 0;
152         uint32_t fl_rs = 0;
153         uint32_t fl_cs = 0;
154         int c, rc;
155
156         if (argc == 1) {
157                 usage();
158                 return (cmd_args_t *)NULL;
159         }
160
161         /* Configure and populate the args structures */
162         args = malloc(sizeof(*args));
163         if (args == NULL)
164                 return NULL;
165
166         memset(args, 0, sizeof(*args));
167
168         while ((c=getopt_long(argc, argv, short_opt, long_opt, NULL)) != -1) {
169                 rc = 0;
170
171                 switch (c) {
172                 case 't': /* --thread count */
173                         rc = set_count(REGEX_NUMBERS, REGEX_NUMBERS_COMMA,
174                                        &args->T, optarg, &fl_th, "threadcount");
175                         break;
176                 case 'l': /* --threadcount_low */
177                         rc = set_lhi(REGEX_NUMBERS, &args->T, optarg,
178                                      FLAG_LOW, &fl_th, "threadcount_low");
179                         break;
180                 case 'h': /* --threadcount_high */
181                         rc = set_lhi(REGEX_NUMBERS, &args->T, optarg,
182                                      FLAG_HIGH, &fl_th, "threadcount_high");
183                         break;
184                 case 'e': /* --threadcount_inc */
185                         rc = set_lhi(REGEX_NUMBERS, &args->T, optarg,
186                                      FLAG_INCR, &fl_th, "threadcount_incr");
187                         break;
188                 case 'n': /* --regioncount */
189                         rc = set_count(REGEX_NUMBERS, REGEX_NUMBERS_COMMA,
190                                        &args->N, optarg, &fl_rc, "regioncount");
191                         break;
192                 case 'i': /* --regioncount_low */
193                         rc = set_lhi(REGEX_NUMBERS, &args->N, optarg,
194                                      FLAG_LOW, &fl_rc, "regioncount_low");
195                         break;
196                 case 'j': /* --regioncount_high */
197                         rc = set_lhi(REGEX_NUMBERS, &args->N, optarg,
198                                      FLAG_HIGH, &fl_rc, "regioncount_high");
199                         break;
200                 case 'k': /* --regioncount_inc */
201                         rc = set_lhi(REGEX_NUMBERS, &args->N, optarg,
202                                      FLAG_INCR, &fl_rc, "regioncount_incr");
203                         break;
204                 case 'o': /* --offset */
205                         rc = set_count(REGEX_SIZE, REGEX_SIZE_COMMA,
206                                        &args->O, optarg, &fl_of, "offset");
207                         break;
208                 case 'm': /* --offset_low */
209                         rc = set_lhi(REGEX_SIZE, &args->O, optarg,
210                                      FLAG_LOW, &fl_of, "offset_low");
211                         break;
212                 case 'q': /* --offset_high */
213                         rc = set_lhi(REGEX_SIZE, &args->O, optarg,
214                                      FLAG_HIGH, &fl_of, "offset_high");
215                         break;
216                 case 'r': /* --offset_inc */
217                         rc = set_lhi(REGEX_NUMBERS, &args->O, optarg,
218                                      FLAG_INCR, &fl_of, "offset_incr");
219                         break;
220                 case 'c': /* --chunksize */
221                         rc = set_count(REGEX_SIZE, REGEX_SIZE_COMMA,
222                                        &args->C, optarg, &fl_cs, "chunksize");
223                         break;
224                 case 'a': /* --chunksize_low */
225                         rc = set_lhi(REGEX_SIZE, &args->C, optarg,
226                                      FLAG_LOW, &fl_cs, "chunksize_low");
227                         break;
228                 case 'b': /* --chunksize_high */
229                         rc = set_lhi(REGEX_SIZE, &args->C, optarg,
230                                      FLAG_HIGH, &fl_cs, "chunksize_high");
231                         break;
232                 case 'g': /* --chunksize_inc */
233                         rc = set_lhi(REGEX_NUMBERS, &args->C, optarg,
234                                      FLAG_INCR, &fl_cs, "chunksize_incr");
235                         break;
236                 case 's': /* --regionsize */
237                         rc = set_count(REGEX_SIZE, REGEX_SIZE_COMMA,
238                                        &args->S, optarg, &fl_rs, "regionsize");
239                         break;
240                 case 'A': /* --regionsize_low */
241                         rc = set_lhi(REGEX_SIZE, &args->S, optarg,
242                                      FLAG_LOW, &fl_rs, "regionsize_low");
243                         break;
244                 case 'B': /* --regionsize_high */
245                         rc = set_lhi(REGEX_SIZE, &args->S, optarg,
246                                      FLAG_HIGH, &fl_rs, "regionsize_high");
247                         break;
248                 case 'C': /* --regionsize_inc */
249                         rc = set_lhi(REGEX_NUMBERS, &args->S, optarg,
250                                      FLAG_INCR, &fl_rs, "regionsize_incr");
251                         break;
252                 case 'L': /* --load */
253                         rc = set_load_params(args, optarg);
254                         break;
255                 case 'p': /* --pool */
256                         args->pool = optarg;
257                         break;
258                 case 'M':
259                         args->name = optarg;
260                         break;
261                 case 'x': /* --cleanup */
262                         args->flags |= DMU_REMOVE;
263                         break;
264                 case 'P': /* --prerun */
265                         strncpy(args->pre, optarg, ZPIOS_PATH_SIZE - 1);
266                         break;
267                 case 'R': /* --postrun */
268                         strncpy(args->post, optarg, ZPIOS_PATH_SIZE - 1);
269                         break;
270                 case 'G': /* --log */
271                         strncpy(args->log, optarg, ZPIOS_PATH_SIZE - 1);
272                         break;
273                 case 'I': /* --regionnoise */
274                         rc = set_noise(&args->regionnoise, optarg, "regionnoise");
275                         break;
276                 case 'N': /* --chunknoise */
277                         rc = set_noise(&args->chunknoise, optarg, "chunknoise");
278                         break;
279                 case 'T': /* --threaddelay */
280                         rc = set_noise(&args->thread_delay, optarg, "threaddelay");
281                         break;
282                 case 'V': /* --verify */
283                         args->flags |= DMU_VERIFY;
284                         break;
285                 case 'z': /* --zerocopy */
286                         args->flags |= (DMU_WRITE_ZC | DMU_READ_ZC);
287                         break;
288                 case 'O': /* --nowait */
289                         args->flags |= DMU_WRITE_NOWAIT;
290                         break;
291                 case 'f': /* --noprefetch */
292                         args->flags |= DMU_READ_NOPF;
293                         break;
294                 case 'H': /* --human-readable */
295                         args->human_readable = 1;
296                         break;
297                 case 'v': /* --verbose */
298                         args->verbose++;
299                         break;
300                 case '?':
301                         rc = 1;
302                         break;
303                 default:
304                         fprintf(stderr,"Unknown option '%s'\n",argv[optind-1]);
305                         rc = EINVAL;
306                         break;
307                 }
308
309                 if (rc) {
310                         usage();
311                         args_fini(args);
312                         return NULL;
313                 }
314         }
315
316         check_mutual_exclusive_command_lines(fl_th, "threadcount");
317         check_mutual_exclusive_command_lines(fl_rc, "regioncount");
318         check_mutual_exclusive_command_lines(fl_of, "offset");
319         check_mutual_exclusive_command_lines(fl_rs, "regionsize");
320         check_mutual_exclusive_command_lines(fl_cs, "chunksize");
321
322         if (args->pool == NULL) {
323                 fprintf(stderr, "Error: Pool not specificed\n");
324                 usage();
325                 args_fini(args);
326                 return NULL;
327         }
328
329         if ((args->flags & (DMU_WRITE_ZC | DMU_READ_ZC)) &&
330             (args->flags & DMU_VERIFY)) {
331                 fprintf(stderr, "Error, --zerocopy incompatible --verify, "
332                             "used for performance analysis only\n");
333                 usage();
334                 args_fini(args);
335                 return NULL;
336         }
337
338         return args;
339 }
340
341 static int
342 dev_clear(void)
343 {
344         zpios_cfg_t cfg;
345         int rc;
346
347         memset(&cfg, 0, sizeof(cfg));
348         cfg.cfg_magic = ZPIOS_CFG_MAGIC;
349         cfg.cfg_cmd   = ZPIOS_CFG_BUFFER_CLEAR;
350         cfg.cfg_arg1  = 0;
351
352         rc = ioctl(zpiosctl_fd, ZPIOS_CFG, &cfg);
353         if (rc)
354                 fprintf(stderr, "Ioctl() error %lu / %d: %d\n",
355                         (unsigned long) ZPIOS_CFG, cfg.cfg_cmd, errno);
356
357         lseek(zpiosctl_fd, 0, SEEK_SET);
358
359         return rc;
360 }
361
362 /* Passing a size of zero simply results in querying the current size */
363 static int
364 dev_size(int size)
365 {
366         zpios_cfg_t cfg;
367         int rc;
368
369         memset(&cfg, 0, sizeof(cfg));
370         cfg.cfg_magic = ZPIOS_CFG_MAGIC;
371         cfg.cfg_cmd   = ZPIOS_CFG_BUFFER_SIZE;
372         cfg.cfg_arg1  = size;
373
374         rc = ioctl(zpiosctl_fd, ZPIOS_CFG, &cfg);
375         if (rc) {
376                 fprintf(stderr, "Ioctl() error %lu / %d: %d\n",
377                         (unsigned long) ZPIOS_CFG, cfg.cfg_cmd, errno);
378                 return rc;
379         }
380
381         return cfg.cfg_rc1;
382 }
383
384 static void
385 dev_fini(void)
386 {
387         if (zpios_buffer)
388                 free(zpios_buffer);
389
390         if (zpiosctl_fd != -1) {
391                 if (close(zpiosctl_fd) == -1) {
392                         fprintf(stderr, "Unable to close %s: %d\n",
393                                 ZPIOS_DEV, errno);
394                 }
395         }
396 }
397
398 static int
399 dev_init(void)
400 {
401         int rc;
402
403         zpiosctl_fd = open(ZPIOS_DEV, O_RDONLY);
404         if (zpiosctl_fd == -1) {
405                 fprintf(stderr, "Unable to open %s: %d\n"
406                         "Is the zpios module loaded?\n", ZPIOS_DEV, errno);
407                 rc = errno;
408                 goto error;
409         }
410
411         if ((rc = dev_clear()))
412                 goto error;
413
414         if ((rc = dev_size(0)) < 0)
415                 goto error;
416
417         zpios_buffer_size = rc;
418         zpios_buffer = (char *)malloc(zpios_buffer_size);
419         if (zpios_buffer == NULL) {
420                 rc = ENOMEM;
421                 goto error;
422         }
423
424         memset(zpios_buffer, 0, zpios_buffer_size);
425         return 0;
426 error:
427         if (zpiosctl_fd != -1) {
428                 if (close(zpiosctl_fd) == -1) {
429                         fprintf(stderr, "Unable to close %s: %d\n",
430                                 ZPIOS_DEV, errno);
431                 }
432         }
433
434         return rc;
435 }
436
437 static int
438 get_next(uint64_t *val, range_repeat_t *range)
439 {
440         /* if low, incr, high is given */
441         if (range->val_count == 0) {
442                 *val = (range->val_low) +
443                        (range->val_low * range->next_val / 100);
444
445                 if (*val > range->val_high)
446                         return 0; /* No more values, limit exceeded */
447
448                 if (!range->next_val)
449                         range->next_val = range->val_inc_perc;
450                 else
451                         range->next_val = range->next_val+range->val_inc_perc;
452
453                 return 1; /* more values to come */
454
455         /* if only one val is given */
456         } else if (range->val_count == 1) {
457                 if (range->next_val)
458                         return 0; /* No more values, we only have one */
459
460                 *val = range->val[0];
461                 range->next_val = 1;
462                 return 1; /* more values to come */
463
464         /* if comma separated values are given */
465         } else if (range->val_count > 1) {
466                 if (range->next_val > range->val_count - 1)
467                         return 0; /* No more values, limit exceeded */
468
469                 *val = range->val[range->next_val];
470                 range->next_val++;
471                 return 1; /* more values to come */
472         }
473
474         return 0;
475 }
476
477 static int
478 run_one(cmd_args_t *args, uint32_t id, uint32_t T, uint32_t N,
479         uint64_t C, uint64_t S, uint64_t O)
480 {
481         zpios_cmd_t *cmd;
482         int rc, rc2, cmd_size;
483
484         dev_clear();
485
486         cmd_size = sizeof(zpios_cmd_t) + ((T + N + 1) * sizeof(zpios_stats_t));
487         cmd = (zpios_cmd_t *)malloc(cmd_size);
488         if (cmd == NULL)
489                 return ENOMEM;
490
491         memset(cmd, 0, cmd_size);
492         cmd->cmd_magic = ZPIOS_CMD_MAGIC;
493         strncpy(cmd->cmd_pool, args->pool, ZPIOS_NAME_SIZE - 1);
494         strncpy(cmd->cmd_pre, args->pre, ZPIOS_PATH_SIZE - 1);
495         strncpy(cmd->cmd_post, args->post, ZPIOS_PATH_SIZE - 1);
496         strncpy(cmd->cmd_log, args->log, ZPIOS_PATH_SIZE - 1);
497         cmd->cmd_id           = id;
498         cmd->cmd_chunk_size   = C;
499         cmd->cmd_thread_count = T;
500         cmd->cmd_region_count = N;
501         cmd->cmd_region_size  = S;
502         cmd->cmd_offset       = O;
503         cmd->cmd_region_noise = args->regionnoise;
504         cmd->cmd_chunk_noise  = args->chunknoise;
505         cmd->cmd_thread_delay = args->thread_delay;
506         cmd->cmd_flags        = args->flags;
507         cmd->cmd_data_size    = (T + N + 1) * sizeof(zpios_stats_t);
508
509         rc = ioctl(zpiosctl_fd, ZPIOS_CMD, cmd);
510         if (rc)
511                 args->rc = errno;
512
513         print_stats(args, cmd);
514
515         if (args->verbose) {
516                 rc2 = read(zpiosctl_fd, zpios_buffer, zpios_buffer_size - 1);
517                 if (rc2 < 0) {
518                         fprintf(stdout, "Error reading results: %d\n", rc2);
519                 } else if ((rc2 > 0) && (strlen(zpios_buffer) > 0)) {
520                         fprintf(stdout, "\n%s\n", zpios_buffer);
521                         fflush(stdout);
522                 }
523         }
524
525         free(cmd);
526
527         return rc;
528 }
529
530 static int
531 run_offsets(cmd_args_t *args)
532 {
533         int rc = 0;
534
535         while (rc == 0 && get_next(&args->current_O, &args->O)) {
536                 rc = run_one(args, args->current_id,
537                              args->current_T, args->current_N, args->current_C,
538                              args->current_S, args->current_O);
539                 args->current_id++;
540         }
541
542         args->O.next_val = 0;
543         return rc;
544 }
545
546 static int
547 run_region_counts(cmd_args_t *args)
548 {
549         int rc = 0;
550
551         while (rc == 0 && get_next((uint64_t *)&args->current_N, &args->N))
552                rc = run_offsets(args);
553
554         args->N.next_val = 0;
555         return rc;
556 }
557
558 static int
559 run_region_sizes(cmd_args_t *args)
560 {
561         int rc = 0;
562
563         while (rc == 0 && get_next(&args->current_S, &args->S)) {
564                 if (args->current_S < args->current_C) {
565                         fprintf(stderr, "Error: in any run chunksize can "
566                                 "not be smaller than regionsize.\n");
567                         return EINVAL;
568                 }
569
570                 rc = run_region_counts(args);
571         }
572
573         args->S.next_val = 0;
574         return rc;
575 }
576
577 static int
578 run_chunk_sizes(cmd_args_t *args)
579 {
580         int rc = 0;
581
582         while (rc == 0 && get_next(&args->current_C, &args->C)) {
583                rc = run_region_sizes(args);
584         }
585
586         args->C.next_val = 0;
587         return rc;
588 }
589
590 static int
591 run_thread_counts(cmd_args_t *args)
592 {
593         int rc = 0;
594
595         while (rc == 0 && get_next((uint64_t *)&args->current_T, &args->T))
596                 rc = run_chunk_sizes(args);
597
598         return rc;
599 }
600
601 int
602 main(int argc, char **argv)
603 {
604         cmd_args_t *args;
605         int rc = 0;
606
607         /* Argument init and parsing */
608         if ((args = args_init(argc, argv)) == NULL) {
609                 rc = -1;
610                 goto out;
611         }
612
613         /* Device specific init */
614         if ((rc = dev_init()))
615                 goto out;
616
617         /* Generic kernel version string */
618         if (args->verbose)
619                 fprintf(stdout, "%s", zpios_version);
620
621         print_stats_header(args);
622         rc = run_thread_counts(args);
623 out:
624         if (args != NULL)
625                 args_fini(args);
626
627         dev_fini();
628         return rc;
629 }