]>
Commit | Line | Data |
---|---|---|
a08ee875 | 1 | /* |
302ef151 BB |
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 | |
cae5b340 | 4 | * against ZFS while still being flexibly controlled from user space. |
302ef151 BB |
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. | |
92db59ca | 18 | * For details, see <http://zfsonlinux.org/>. |
302ef151 BB |
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/>. | |
cae5b340 AX |
32 | * |
33 | * Copyright (c) 2015, Intel Corporation. | |
a08ee875 | 34 | */ |
302ef151 BB |
35 | |
36 | #include <stdlib.h> | |
37 | #include <stdio.h> | |
38 | #include <string.h> | |
39 | #include <errno.h> | |
40 | #include <assert.h> | |
41 | #include <regex.h> | |
42 | #include "zpios.h" | |
43 | ||
44 | /* extracts an unsigned int (64) and K,M,G,T from the string */ | |
45 | /* and returns a 64 bit value converted to the proper units */ | |
46 | static int | |
47 | kmgt_to_uint64(const char *str, uint64_t *val) | |
48 | { | |
49 | char *endptr; | |
50 | int rc = 0; | |
51 | ||
52 | *val = strtoll(str, &endptr, 0); | |
53 | if ((str == endptr) && (*val == 0)) | |
a08ee875 | 54 | return (EINVAL); |
302ef151 BB |
55 | |
56 | switch (endptr[0]) { | |
57 | case 'k': case 'K': | |
58 | *val = (*val) << 10; | |
59 | break; | |
60 | case 'm': case 'M': | |
61 | *val = (*val) << 20; | |
62 | break; | |
63 | case 'g': case 'G': | |
64 | *val = (*val) << 30; | |
65 | break; | |
66 | case 't': case 'T': | |
67 | *val = (*val) << 40; | |
68 | break; | |
69 | case '\0': | |
70 | break; | |
71 | default: | |
72 | rc = EINVAL; | |
73 | } | |
74 | ||
a08ee875 | 75 | return (rc); |
302ef151 BB |
76 | } |
77 | ||
78 | static char * | |
79 | uint64_to_kmgt(char *str, uint64_t val) | |
80 | { | |
81 | char postfix[] = "kmgt"; | |
82 | int i = -1; | |
83 | ||
84 | while ((val >= KB) && (i < 4)) { | |
85 | val = (val >> 10); | |
86 | i++; | |
87 | } | |
88 | ||
89 | if (i >= 4) | |
a08ee875 | 90 | (void) snprintf(str, KMGT_SIZE-1, "inf"); |
302ef151 | 91 | else |
a08ee875 LG |
92 | (void) snprintf(str, KMGT_SIZE-1, "%lu%c", (unsigned long)val, |
93 | (i == -1) ? '\0' : postfix[i]); | |
302ef151 | 94 | |
a08ee875 | 95 | return (str); |
302ef151 BB |
96 | } |
97 | ||
98 | static char * | |
99 | kmgt_per_sec(char *str, uint64_t v, double t) | |
100 | { | |
101 | char postfix[] = "kmgt"; | |
102 | double val = ((double)v) / t; | |
103 | int i = -1; | |
104 | ||
105 | while ((val >= (double)KB) && (i < 4)) { | |
106 | val /= (double)KB; | |
107 | i++; | |
108 | } | |
109 | ||
110 | if (i >= 4) | |
a08ee875 | 111 | (void) snprintf(str, KMGT_SIZE-1, "inf"); |
302ef151 | 112 | else |
a08ee875 LG |
113 | (void) snprintf(str, KMGT_SIZE-1, "%.2f%c", val, |
114 | (i == -1) ? '\0' : postfix[i]); | |
302ef151 | 115 | |
a08ee875 | 116 | return (str); |
302ef151 BB |
117 | } |
118 | ||
119 | static char * | |
120 | print_flags(char *str, uint32_t flags) | |
121 | { | |
122 | str[0] = (flags & DMU_WRITE) ? 'w' : '-'; | |
123 | str[1] = (flags & DMU_READ) ? 'r' : '-'; | |
124 | str[2] = (flags & DMU_VERIFY) ? 'v' : '-'; | |
125 | str[3] = (flags & DMU_REMOVE) ? 'c' : '-'; | |
126 | str[4] = (flags & DMU_FPP) ? 'p' : 's'; | |
127 | str[5] = (flags & (DMU_WRITE_ZC | DMU_READ_ZC)) ? 'z' : '-'; | |
128 | str[6] = (flags & DMU_WRITE_NOWAIT) ? 'O' : '-'; | |
129 | str[7] = '\0'; | |
130 | ||
a08ee875 | 131 | return (str); |
302ef151 BB |
132 | } |
133 | ||
134 | static int | |
135 | regex_match(const char *string, char *pattern) | |
136 | { | |
137 | regex_t re = { 0 }; | |
138 | int rc; | |
139 | ||
140 | rc = regcomp(&re, pattern, REG_EXTENDED | REG_NOSUB | REG_ICASE); | |
141 | if (rc) { | |
142 | fprintf(stderr, "Error: Couldn't do regcomp, %d\n", rc); | |
a08ee875 | 143 | return (rc); |
302ef151 BB |
144 | } |
145 | ||
cae5b340 | 146 | rc = regexec(&re, string, (size_t)0, NULL, 0); |
302ef151 BB |
147 | regfree(&re); |
148 | ||
a08ee875 | 149 | return (rc); |
302ef151 BB |
150 | } |
151 | ||
152 | /* fills the pios_range_repeat structure of comma separated values */ | |
153 | static int | |
154 | split_string(const char *optarg, char *pattern, range_repeat_t *range) | |
155 | { | |
156 | const char comma[] = ","; | |
157 | char *cp, *token[32]; | |
158 | int rc, i = 0; | |
159 | ||
160 | if ((rc = regex_match(optarg, pattern))) | |
a08ee875 | 161 | return (rc); |
302ef151 BB |
162 | |
163 | cp = strdup(optarg); | |
164 | if (cp == NULL) | |
a08ee875 | 165 | return (ENOMEM); |
302ef151 BB |
166 | |
167 | do { | |
a08ee875 LG |
168 | /* |
169 | * STRTOK(3) Each subsequent call, with a null pointer as the | |
302ef151 BB |
170 | * value of the * first argument, starts searching from the |
171 | * saved pointer and behaves as described above. | |
172 | */ | |
cae5b340 AX |
173 | if (i == 0) { |
174 | token[i] = strtok(cp, comma); | |
175 | } else { | |
176 | token[i] = strtok(NULL, comma); | |
177 | } | |
302ef151 BB |
178 | } while ((token[i++] != NULL) && (i < 32)); |
179 | ||
180 | range->val_count = i - 1; | |
181 | ||
182 | for (i = 0; i < range->val_count; i++) | |
183 | kmgt_to_uint64(token[i], &range->val[i]); | |
184 | ||
185 | free(cp); | |
a08ee875 | 186 | return (0); |
302ef151 BB |
187 | } |
188 | ||
189 | int | |
190 | set_count(char *pattern1, char *pattern2, range_repeat_t *range, | |
a08ee875 | 191 | char *optarg, uint32_t *flags, char *arg) |
302ef151 | 192 | { |
cae5b340 AX |
193 | uint64_t count = range->val_count; |
194 | ||
302ef151 BB |
195 | if (flags) |
196 | *flags |= FLAG_SET; | |
197 | ||
198 | range->next_val = 0; | |
199 | ||
200 | if (regex_match(optarg, pattern1) == 0) { | |
201 | kmgt_to_uint64(optarg, &range->val[0]); | |
202 | range->val_count = 1; | |
203 | } else if (split_string(optarg, pattern2, range) < 0) { | |
204 | fprintf(stderr, "Error: Incorrect pattern for %s, '%s'\n", | |
a08ee875 LG |
205 | arg, optarg); |
206 | return (EINVAL); | |
cae5b340 AX |
207 | } else if (count == range->val_count) { |
208 | fprintf(stderr, "Error: input ignored for %s, '%s'\n", | |
209 | arg, optarg); | |
302ef151 BB |
210 | } |
211 | ||
a08ee875 | 212 | return (0); |
302ef151 BB |
213 | } |
214 | ||
a08ee875 LG |
215 | /* |
216 | * Validates the value with regular expression and sets low, high, incr | |
217 | * according to value at which flag will be set. Sets the flag after. | |
218 | */ | |
302ef151 BB |
219 | int |
220 | set_lhi(char *pattern, range_repeat_t *range, char *optarg, | |
a08ee875 | 221 | int flag, uint32_t *flag_thread, char *arg) |
302ef151 BB |
222 | { |
223 | int rc; | |
224 | ||
225 | if ((rc = regex_match(optarg, pattern))) { | |
226 | fprintf(stderr, "Error: Wrong pattern in %s, '%s'\n", | |
cae5b340 | 227 | arg, optarg); |
a08ee875 | 228 | return (rc); |
302ef151 BB |
229 | } |
230 | ||
231 | switch (flag) { | |
232 | case FLAG_LOW: | |
233 | kmgt_to_uint64(optarg, &range->val_low); | |
234 | break; | |
235 | case FLAG_HIGH: | |
236 | kmgt_to_uint64(optarg, &range->val_high); | |
237 | break; | |
238 | case FLAG_INCR: | |
239 | kmgt_to_uint64(optarg, &range->val_inc_perc); | |
240 | break; | |
241 | default: | |
242 | assert(0); | |
243 | } | |
244 | ||
245 | *flag_thread |= flag; | |
246 | ||
a08ee875 | 247 | return (0); |
302ef151 BB |
248 | } |
249 | ||
250 | int | |
251 | set_noise(uint64_t *noise, char *optarg, char *arg) | |
252 | { | |
253 | if (regex_match(optarg, REGEX_NUMBERS) == 0) { | |
254 | kmgt_to_uint64(optarg, noise); | |
255 | } else { | |
256 | fprintf(stderr, "Error: Incorrect pattern for %s\n", arg); | |
a08ee875 | 257 | return (EINVAL); |
302ef151 BB |
258 | } |
259 | ||
a08ee875 | 260 | return (0); |
302ef151 BB |
261 | } |
262 | ||
263 | int | |
264 | set_load_params(cmd_args_t *args, char *optarg) | |
265 | { | |
cae5b340 | 266 | char *param, *search, *searchdup, comma[] = ","; |
302ef151 BB |
267 | int rc = 0; |
268 | ||
269 | search = strdup(optarg); | |
270 | if (search == NULL) | |
a08ee875 | 271 | return (ENOMEM); |
cae5b340 | 272 | searchdup = search; |
302ef151 BB |
273 | |
274 | while ((param = strtok(search, comma)) != NULL) { | |
275 | search = NULL; | |
276 | ||
277 | if (strcmp("fpp", param) == 0) { | |
278 | args->flags |= DMU_FPP; /* File Per Process/Thread */ | |
279 | } else if (strcmp("ssf", param) == 0) { | |
280 | args->flags &= ~DMU_FPP; /* Single Shared File */ | |
281 | } else if (strcmp("dmuio", param) == 0) { | |
282 | args->io_type |= DMU_IO; | |
283 | args->flags |= (DMU_WRITE | DMU_READ); | |
284 | } else { | |
285 | fprintf(stderr, "Invalid load: %s\n", param); | |
286 | rc = EINVAL; | |
287 | } | |
288 | } | |
289 | ||
cae5b340 | 290 | free(searchdup); |
302ef151 | 291 | |
a08ee875 | 292 | return (rc); |
302ef151 BB |
293 | } |
294 | ||
295 | ||
a08ee875 LG |
296 | /* |
297 | * Checks the low, high, increment values against the single value for | |
302ef151 | 298 | * mutual exclusion, for e.g threadcount is mutually exclusive to |
a08ee875 LG |
299 | * threadcount_low, ..._high, ..._incr |
300 | */ | |
302ef151 BB |
301 | int |
302 | check_mutual_exclusive_command_lines(uint32_t flag, char *arg) | |
303 | { | |
304 | if ((flag & FLAG_SET) && (flag & (FLAG_LOW | FLAG_HIGH | FLAG_INCR))) { | |
305 | fprintf(stderr, "Error: --%s can not be given with --%s_low, " | |
a08ee875 LG |
306 | "--%s_high or --%s_incr.\n", arg, arg, arg, arg); |
307 | return (0); | |
302ef151 BB |
308 | } |
309 | ||
a08ee875 | 310 | if ((flag & (FLAG_LOW | FLAG_HIGH | FLAG_INCR)) && !(flag & FLAG_SET)) { |
302ef151 BB |
311 | if (flag != (FLAG_LOW | FLAG_HIGH | FLAG_INCR)) { |
312 | fprintf(stderr, "Error: One or more values missing " | |
a08ee875 LG |
313 | "from --%s_low, --%s_high, --%s_incr.\n", |
314 | arg, arg, arg); | |
315 | return (0); | |
302ef151 BB |
316 | } |
317 | } | |
318 | ||
a08ee875 | 319 | return (1); |
302ef151 BB |
320 | } |
321 | ||
322 | void | |
323 | print_stats_header(cmd_args_t *args) | |
324 | { | |
325 | if (args->verbose) { | |
a08ee875 LG |
326 | printf( |
327 | "status name id\tth-cnt\trg-cnt\trg-sz\t" | |
cae5b340 | 328 | "ch-sz\toffset\trg-no\tch-no\tth-dly\tflags\tblksz\ttime\t" |
a08ee875 LG |
329 | "cr-time\trm-time\twr-time\trd-time\twr-data\twr-ch\t" |
330 | "wr-bw\trd-data\trd-ch\trd-bw\n"); | |
331 | printf( | |
cae5b340 AX |
332 | "-------------------------------------------------" |
333 | "-------------------------------------------------" | |
334 | "-------------------------------------------------" | |
335 | "--------------------------------------------------\n"); | |
302ef151 | 336 | } else { |
a08ee875 LG |
337 | printf( |
338 | "status name id\t" | |
339 | "wr-data\twr-ch\twr-bw\t" | |
340 | "rd-data\trd-ch\trd-bw\n"); | |
341 | printf( | |
342 | "-----------------------------------------" | |
343 | "--------------------------------------\n"); | |
302ef151 BB |
344 | } |
345 | } | |
346 | ||
347 | static void | |
348 | print_stats_human_readable(cmd_args_t *args, zpios_cmd_t *cmd) | |
349 | { | |
350 | zpios_stats_t *summary_stats; | |
351 | double t_time, wr_time, rd_time, cr_time, rm_time; | |
352 | char str[KMGT_SIZE]; | |
353 | ||
354 | if (args->rc) | |
355 | printf("FAIL: %3d ", args->rc); | |
356 | else | |
357 | printf("PASS: "); | |
358 | ||
359 | printf("%-12s", args->name ? args->name : ZPIOS_NAME); | |
a08ee875 | 360 | printf("%2u\t", cmd->cmd_id); |
302ef151 BB |
361 | |
362 | if (args->verbose) { | |
a08ee875 LG |
363 | printf("%u\t", cmd->cmd_thread_count); |
364 | printf("%u\t", cmd->cmd_region_count); | |
365 | printf("%s\t", uint64_to_kmgt(str, cmd->cmd_region_size)); | |
366 | printf("%s\t", uint64_to_kmgt(str, cmd->cmd_chunk_size)); | |
367 | printf("%s\t", uint64_to_kmgt(str, cmd->cmd_offset)); | |
368 | printf("%s\t", uint64_to_kmgt(str, cmd->cmd_region_noise)); | |
369 | printf("%s\t", uint64_to_kmgt(str, cmd->cmd_chunk_noise)); | |
370 | printf("%s\t", uint64_to_kmgt(str, cmd->cmd_thread_delay)); | |
302ef151 | 371 | printf("%s\t", print_flags(str, cmd->cmd_flags)); |
cae5b340 | 372 | printf("%s\t", uint64_to_kmgt(str, cmd->cmd_block_size)); |
302ef151 BB |
373 | } |
374 | ||
375 | if (args->rc) { | |
376 | printf("\n"); | |
377 | return; | |
378 | } | |
379 | ||
380 | summary_stats = (zpios_stats_t *)cmd->cmd_data_str; | |
381 | t_time = zpios_timespec_to_double(summary_stats->total_time.delta); | |
382 | wr_time = zpios_timespec_to_double(summary_stats->wr_time.delta); | |
383 | rd_time = zpios_timespec_to_double(summary_stats->rd_time.delta); | |
384 | cr_time = zpios_timespec_to_double(summary_stats->cr_time.delta); | |
385 | rm_time = zpios_timespec_to_double(summary_stats->rm_time.delta); | |
386 | ||
387 | if (args->verbose) { | |
388 | printf("%.2f\t", t_time); | |
389 | printf("%.3f\t", cr_time); | |
390 | printf("%.3f\t", rm_time); | |
391 | printf("%.2f\t", wr_time); | |
392 | printf("%.2f\t", rd_time); | |
393 | } | |
394 | ||
a08ee875 LG |
395 | printf("%s\t", uint64_to_kmgt(str, summary_stats->wr_data)); |
396 | printf("%s\t", uint64_to_kmgt(str, summary_stats->wr_chunks)); | |
302ef151 BB |
397 | printf("%s\t", kmgt_per_sec(str, summary_stats->wr_data, wr_time)); |
398 | ||
a08ee875 LG |
399 | printf("%s\t", uint64_to_kmgt(str, summary_stats->rd_data)); |
400 | printf("%s\t", uint64_to_kmgt(str, summary_stats->rd_chunks)); | |
302ef151 BB |
401 | printf("%s\n", kmgt_per_sec(str, summary_stats->rd_data, rd_time)); |
402 | fflush(stdout); | |
403 | } | |
404 | ||
405 | static void | |
406 | print_stats_table(cmd_args_t *args, zpios_cmd_t *cmd) | |
407 | { | |
408 | zpios_stats_t *summary_stats; | |
409 | double wr_time, rd_time; | |
410 | ||
411 | if (args->rc) | |
412 | printf("FAIL: %3d ", args->rc); | |
413 | else | |
414 | printf("PASS: "); | |
415 | ||
416 | printf("%-12s", args->name ? args->name : ZPIOS_NAME); | |
a08ee875 | 417 | printf("%2u\t", cmd->cmd_id); |
302ef151 BB |
418 | |
419 | if (args->verbose) { | |
a08ee875 LG |
420 | printf("%u\t", cmd->cmd_thread_count); |
421 | printf("%u\t", cmd->cmd_region_count); | |
422 | printf("%llu\t", (long long unsigned)cmd->cmd_region_size); | |
423 | printf("%llu\t", (long long unsigned)cmd->cmd_chunk_size); | |
424 | printf("%llu\t", (long long unsigned)cmd->cmd_offset); | |
425 | printf("%u\t", cmd->cmd_region_noise); | |
426 | printf("%u\t", cmd->cmd_chunk_noise); | |
427 | printf("%u\t", cmd->cmd_thread_delay); | |
302ef151 | 428 | printf("0x%x\t", cmd->cmd_flags); |
cae5b340 | 429 | printf("%u\t", cmd->cmd_block_size); |
302ef151 BB |
430 | } |
431 | ||
432 | if (args->rc) { | |
433 | printf("\n"); | |
434 | return; | |
435 | } | |
436 | ||
437 | summary_stats = (zpios_stats_t *)cmd->cmd_data_str; | |
438 | wr_time = zpios_timespec_to_double(summary_stats->wr_time.delta); | |
439 | rd_time = zpios_timespec_to_double(summary_stats->rd_time.delta); | |
440 | ||
441 | if (args->verbose) { | |
442 | printf("%ld.%02ld\t", | |
a08ee875 LG |
443 | (long)summary_stats->total_time.delta.ts_sec, |
444 | (long)summary_stats->total_time.delta.ts_nsec); | |
302ef151 | 445 | printf("%ld.%02ld\t", |
a08ee875 LG |
446 | (long)summary_stats->cr_time.delta.ts_sec, |
447 | (long)summary_stats->cr_time.delta.ts_nsec); | |
302ef151 | 448 | printf("%ld.%02ld\t", |
a08ee875 LG |
449 | (long)summary_stats->rm_time.delta.ts_sec, |
450 | (long)summary_stats->rm_time.delta.ts_nsec); | |
302ef151 | 451 | printf("%ld.%02ld\t", |
a08ee875 LG |
452 | (long)summary_stats->wr_time.delta.ts_sec, |
453 | (long)summary_stats->wr_time.delta.ts_nsec); | |
302ef151 | 454 | printf("%ld.%02ld\t", |
a08ee875 LG |
455 | (long)summary_stats->rd_time.delta.ts_sec, |
456 | (long)summary_stats->rd_time.delta.ts_nsec); | |
302ef151 BB |
457 | } |
458 | ||
a08ee875 LG |
459 | printf("%lld\t", (long long unsigned)summary_stats->wr_data); |
460 | printf("%lld\t", (long long unsigned)summary_stats->wr_chunks); | |
302ef151 BB |
461 | printf("%.4f\t", (double)summary_stats->wr_data / wr_time); |
462 | ||
a08ee875 LG |
463 | printf("%lld\t", (long long unsigned)summary_stats->rd_data); |
464 | printf("%lld\t", (long long unsigned)summary_stats->rd_chunks); | |
302ef151 BB |
465 | printf("%.4f\n", (double)summary_stats->rd_data / rd_time); |
466 | fflush(stdout); | |
467 | } | |
468 | ||
469 | void | |
470 | print_stats(cmd_args_t *args, zpios_cmd_t *cmd) | |
471 | { | |
472 | if (args->human_readable) | |
473 | print_stats_human_readable(args, cmd); | |
474 | else | |
475 | print_stats_table(args, cmd); | |
476 | } |