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