]> git.proxmox.com Git - mirror_zfs-debian.git/blame - cmd/zpios/zpios_main.c
Merge remote-tracking branch 'behlendorf/master' into upstream
[mirror_zfs-debian.git] / cmd / zpios / zpios_main.c
CommitLineData
302ef151
BB
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://github.com/behlendorf/zfs/>.
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
45static 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?";
47static 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
88static int zpiosctl_fd; /* Control file descriptor */
89static char zpios_version[VERSION_SIZE]; /* Kernel version string */
90static char *zpios_buffer = NULL; /* Scratch space area */
91static int zpios_buffer_size = 0; /* Scratch space size */
92
93static int
94usage(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
139static void args_fini(cmd_args_t *args)
140{
141 assert(args != NULL);
142 free(args);
143}
144
145static cmd_args_t *
146args_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
341static int
342dev_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 */
363static int
364dev_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
384static void
385dev_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
398static int
399dev_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;
426error:
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
437static int
438get_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
477static int
478run_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
530static int
531run_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
546static int
547run_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
558static int
559run_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
577static int
578run_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
590static int
591run_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
601int
602main(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);
623out:
624 if (args != NULL)
625 args_fini(args);
626
627 dev_fini();
628 return rc;
629}