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.
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>.
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>
17 * This file is part of ZFS on Linux.
18 * For details, see <http://github.com/behlendorf/zfs/>.
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.
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
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 \*****************************************************************************/
42 #include <sys/ioctl.h>
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, '?' },
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 */
96 fprintf(stderr, "Usage: zpios\n");
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"
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"
132 " --human-readable -H\n"
133 " --verbose -v =increase verbosity\n"
134 " --help -? =this help\n\n");
139 static void args_fini(cmd_args_t *args)
141 assert(args != NULL);
146 args_init(int argc, char **argv)
158 return (cmd_args_t *)NULL;
161 /* Configure and populate the args structures */
162 args = malloc(sizeof(*args));
166 memset(args, 0, sizeof(*args));
168 while ((c=getopt_long(argc, argv, short_opt, long_opt, NULL)) != -1) {
172 case 't': /* --thread count */
173 rc = set_count(REGEX_NUMBERS, REGEX_NUMBERS_COMMA,
174 &args->T, optarg, &fl_th, "threadcount");
176 case 'l': /* --threadcount_low */
177 rc = set_lhi(REGEX_NUMBERS, &args->T, optarg,
178 FLAG_LOW, &fl_th, "threadcount_low");
180 case 'h': /* --threadcount_high */
181 rc = set_lhi(REGEX_NUMBERS, &args->T, optarg,
182 FLAG_HIGH, &fl_th, "threadcount_high");
184 case 'e': /* --threadcount_inc */
185 rc = set_lhi(REGEX_NUMBERS, &args->T, optarg,
186 FLAG_INCR, &fl_th, "threadcount_incr");
188 case 'n': /* --regioncount */
189 rc = set_count(REGEX_NUMBERS, REGEX_NUMBERS_COMMA,
190 &args->N, optarg, &fl_rc, "regioncount");
192 case 'i': /* --regioncount_low */
193 rc = set_lhi(REGEX_NUMBERS, &args->N, optarg,
194 FLAG_LOW, &fl_rc, "regioncount_low");
196 case 'j': /* --regioncount_high */
197 rc = set_lhi(REGEX_NUMBERS, &args->N, optarg,
198 FLAG_HIGH, &fl_rc, "regioncount_high");
200 case 'k': /* --regioncount_inc */
201 rc = set_lhi(REGEX_NUMBERS, &args->N, optarg,
202 FLAG_INCR, &fl_rc, "regioncount_incr");
204 case 'o': /* --offset */
205 rc = set_count(REGEX_SIZE, REGEX_SIZE_COMMA,
206 &args->O, optarg, &fl_of, "offset");
208 case 'm': /* --offset_low */
209 rc = set_lhi(REGEX_SIZE, &args->O, optarg,
210 FLAG_LOW, &fl_of, "offset_low");
212 case 'q': /* --offset_high */
213 rc = set_lhi(REGEX_SIZE, &args->O, optarg,
214 FLAG_HIGH, &fl_of, "offset_high");
216 case 'r': /* --offset_inc */
217 rc = set_lhi(REGEX_NUMBERS, &args->O, optarg,
218 FLAG_INCR, &fl_of, "offset_incr");
220 case 'c': /* --chunksize */
221 rc = set_count(REGEX_SIZE, REGEX_SIZE_COMMA,
222 &args->C, optarg, &fl_cs, "chunksize");
224 case 'a': /* --chunksize_low */
225 rc = set_lhi(REGEX_SIZE, &args->C, optarg,
226 FLAG_LOW, &fl_cs, "chunksize_low");
228 case 'b': /* --chunksize_high */
229 rc = set_lhi(REGEX_SIZE, &args->C, optarg,
230 FLAG_HIGH, &fl_cs, "chunksize_high");
232 case 'g': /* --chunksize_inc */
233 rc = set_lhi(REGEX_NUMBERS, &args->C, optarg,
234 FLAG_INCR, &fl_cs, "chunksize_incr");
236 case 's': /* --regionsize */
237 rc = set_count(REGEX_SIZE, REGEX_SIZE_COMMA,
238 &args->S, optarg, &fl_rs, "regionsize");
240 case 'A': /* --regionsize_low */
241 rc = set_lhi(REGEX_SIZE, &args->S, optarg,
242 FLAG_LOW, &fl_rs, "regionsize_low");
244 case 'B': /* --regionsize_high */
245 rc = set_lhi(REGEX_SIZE, &args->S, optarg,
246 FLAG_HIGH, &fl_rs, "regionsize_high");
248 case 'C': /* --regionsize_inc */
249 rc = set_lhi(REGEX_NUMBERS, &args->S, optarg,
250 FLAG_INCR, &fl_rs, "regionsize_incr");
252 case 'L': /* --load */
253 rc = set_load_params(args, optarg);
255 case 'p': /* --pool */
261 case 'x': /* --cleanup */
262 args->flags |= DMU_REMOVE;
264 case 'P': /* --prerun */
265 strncpy(args->pre, optarg, ZPIOS_PATH_SIZE - 1);
267 case 'R': /* --postrun */
268 strncpy(args->post, optarg, ZPIOS_PATH_SIZE - 1);
270 case 'G': /* --log */
271 strncpy(args->log, optarg, ZPIOS_PATH_SIZE - 1);
273 case 'I': /* --regionnoise */
274 rc = set_noise(&args->regionnoise, optarg, "regionnoise");
276 case 'N': /* --chunknoise */
277 rc = set_noise(&args->chunknoise, optarg, "chunknoise");
279 case 'T': /* --threaddelay */
280 rc = set_noise(&args->thread_delay, optarg, "threaddelay");
282 case 'V': /* --verify */
283 args->flags |= DMU_VERIFY;
285 case 'z': /* --zerocopy */
286 args->flags |= (DMU_WRITE_ZC | DMU_READ_ZC);
288 case 'O': /* --nowait */
289 args->flags |= DMU_WRITE_NOWAIT;
291 case 'f': /* --noprefetch */
292 args->flags |= DMU_READ_NOPF;
294 case 'H': /* --human-readable */
295 args->human_readable = 1;
297 case 'v': /* --verbose */
304 fprintf(stderr,"Unknown option '%s'\n",argv[optind-1]);
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");
322 if (args->pool == NULL) {
323 fprintf(stderr, "Error: Pool not specificed\n");
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");
347 memset(&cfg, 0, sizeof(cfg));
348 cfg.cfg_magic = ZPIOS_CFG_MAGIC;
349 cfg.cfg_cmd = ZPIOS_CFG_BUFFER_CLEAR;
352 rc = ioctl(zpiosctl_fd, ZPIOS_CFG, &cfg);
354 fprintf(stderr, "Ioctl() error %lu / %d: %d\n",
355 (unsigned long) ZPIOS_CFG, cfg.cfg_cmd, errno);
357 lseek(zpiosctl_fd, 0, SEEK_SET);
362 /* Passing a size of zero simply results in querying the current size */
369 memset(&cfg, 0, sizeof(cfg));
370 cfg.cfg_magic = ZPIOS_CFG_MAGIC;
371 cfg.cfg_cmd = ZPIOS_CFG_BUFFER_SIZE;
374 rc = ioctl(zpiosctl_fd, ZPIOS_CFG, &cfg);
376 fprintf(stderr, "Ioctl() error %lu / %d: %d\n",
377 (unsigned long) ZPIOS_CFG, cfg.cfg_cmd, errno);
390 if (zpiosctl_fd != -1) {
391 if (close(zpiosctl_fd) == -1) {
392 fprintf(stderr, "Unable to close %s: %d\n",
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);
411 if ((rc = dev_clear()))
414 if ((rc = dev_size(0)) < 0)
417 zpios_buffer_size = rc;
418 zpios_buffer = (char *)malloc(zpios_buffer_size);
419 if (zpios_buffer == NULL) {
424 memset(zpios_buffer, 0, zpios_buffer_size);
427 if (zpiosctl_fd != -1) {
428 if (close(zpiosctl_fd) == -1) {
429 fprintf(stderr, "Unable to close %s: %d\n",
438 get_next(uint64_t *val, range_repeat_t *range)
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);
445 if (*val > range->val_high)
446 return 0; /* No more values, limit exceeded */
448 if (!range->next_val)
449 range->next_val = range->val_inc_perc;
451 range->next_val = range->next_val+range->val_inc_perc;
453 return 1; /* more values to come */
455 /* if only one val is given */
456 } else if (range->val_count == 1) {
458 return 0; /* No more values, we only have one */
460 *val = range->val[0];
462 return 1; /* more values to come */
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 */
469 *val = range->val[range->next_val];
471 return 1; /* more values to come */
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)
482 int rc, rc2, cmd_size;
486 cmd_size = sizeof(zpios_cmd_t) + ((T + N + 1) * sizeof(zpios_stats_t));
487 cmd = (zpios_cmd_t *)malloc(cmd_size);
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);
498 cmd->cmd_chunk_size = C;
499 cmd->cmd_thread_count = T;
500 cmd->cmd_region_count = N;
501 cmd->cmd_region_size = S;
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);
509 rc = ioctl(zpiosctl_fd, ZPIOS_CMD, cmd);
513 print_stats(args, cmd);
516 rc2 = read(zpiosctl_fd, zpios_buffer, zpios_buffer_size - 1);
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);
531 run_offsets(cmd_args_t *args)
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);
542 args->O.next_val = 0;
547 run_region_counts(cmd_args_t *args)
551 while (rc == 0 && get_next((uint64_t *)&args->current_N, &args->N))
552 rc = run_offsets(args);
554 args->N.next_val = 0;
559 run_region_sizes(cmd_args_t *args)
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");
570 rc = run_region_counts(args);
573 args->S.next_val = 0;
578 run_chunk_sizes(cmd_args_t *args)
582 while (rc == 0 && get_next(&args->current_C, &args->C)) {
583 rc = run_region_sizes(args);
586 args->C.next_val = 0;
591 run_thread_counts(cmd_args_t *args)
595 while (rc == 0 && get_next((uint64_t *)&args->current_T, &args->T))
596 rc = run_chunk_sizes(args);
602 main(int argc, char **argv)
607 /* Argument init and parsing */
608 if ((args = args_init(argc, argv)) == NULL) {
613 /* Device specific init */
614 if ((rc = dev_init()))
617 /* Generic kernel version string */
619 fprintf(stdout, "%s", zpios_version);
621 print_stats_header(args);
622 rc = run_thread_counts(args);