]> git.proxmox.com Git - mirror_zfs-debian.git/blob - cmd/zpios/zpios_util.c
Merge branch 'add_breaks_replaces_zfs_initramfs' into 'master'
[mirror_zfs-debian.git] / cmd / zpios / zpios_util.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 controlled 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 * Copyright (c) 2015, Intel Corporation.
34 */
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))
54 return (EINVAL);
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
75 return (rc);
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)
90 (void) snprintf(str, KMGT_SIZE-1, "inf");
91 else
92 (void) snprintf(str, KMGT_SIZE-1, "%lu%c", (unsigned long)val,
93 (i == -1) ? '\0' : postfix[i]);
94
95 return (str);
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)
111 (void) snprintf(str, KMGT_SIZE-1, "inf");
112 else
113 (void) snprintf(str, KMGT_SIZE-1, "%.2f%c", val,
114 (i == -1) ? '\0' : postfix[i]);
115
116 return (str);
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
131 return (str);
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);
143 return (rc);
144 }
145
146 rc = regexec(&re, string, (size_t)0, NULL, 0);
147 regfree(&re);
148
149 return (rc);
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)))
161 return (rc);
162
163 cp = strdup(optarg);
164 if (cp == NULL)
165 return (ENOMEM);
166
167 do {
168 /*
169 * STRTOK(3) Each subsequent call, with a null pointer as the
170 * value of the * first argument, starts searching from the
171 * saved pointer and behaves as described above.
172 */
173 if (i == 0) {
174 token[i] = strtok(cp, comma);
175 } else {
176 token[i] = strtok(NULL, comma);
177 }
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);
186 return (0);
187 }
188
189 int
190 set_count(char *pattern1, char *pattern2, range_repeat_t *range,
191 char *optarg, uint32_t *flags, char *arg)
192 {
193 uint64_t count = range->val_count;
194
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",
205 arg, optarg);
206 return (EINVAL);
207 } else if (count == range->val_count) {
208 fprintf(stderr, "Error: input ignored for %s, '%s'\n",
209 arg, optarg);
210 }
211
212 return (0);
213 }
214
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 */
219 int
220 set_lhi(char *pattern, range_repeat_t *range, char *optarg,
221 int flag, uint32_t *flag_thread, char *arg)
222 {
223 int rc;
224
225 if ((rc = regex_match(optarg, pattern))) {
226 fprintf(stderr, "Error: Wrong pattern in %s, '%s'\n",
227 arg, optarg);
228 return (rc);
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
247 return (0);
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);
257 return (EINVAL);
258 }
259
260 return (0);
261 }
262
263 int
264 set_load_params(cmd_args_t *args, char *optarg)
265 {
266 char *param, *search, *searchdup, comma[] = ",";
267 int rc = 0;
268
269 search = strdup(optarg);
270 if (search == NULL)
271 return (ENOMEM);
272 searchdup = search;
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
290 free(searchdup);
291
292 return (rc);
293 }
294
295
296 /*
297 * Checks the low, high, increment values against the single value for
298 * mutual exclusion, for e.g threadcount is mutually exclusive to
299 * threadcount_low, ..._high, ..._incr
300 */
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, "
306 "--%s_high or --%s_incr.\n", arg, arg, arg, arg);
307 return (0);
308 }
309
310 if ((flag & (FLAG_LOW | FLAG_HIGH | FLAG_INCR)) && !(flag & FLAG_SET)) {
311 if (flag != (FLAG_LOW | FLAG_HIGH | FLAG_INCR)) {
312 fprintf(stderr, "Error: One or more values missing "
313 "from --%s_low, --%s_high, --%s_incr.\n",
314 arg, arg, arg);
315 return (0);
316 }
317 }
318
319 return (1);
320 }
321
322 void
323 print_stats_header(cmd_args_t *args)
324 {
325 if (args->verbose) {
326 printf(
327 "status name id\tth-cnt\trg-cnt\trg-sz\t"
328 "ch-sz\toffset\trg-no\tch-no\tth-dly\tflags\tblksz\ttime\t"
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(
332 "-------------------------------------------------"
333 "-------------------------------------------------"
334 "-------------------------------------------------"
335 "--------------------------------------------------\n");
336 } else {
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");
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);
360 printf("%2u\t", cmd->cmd_id);
361
362 if (args->verbose) {
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));
371 printf("%s\t", print_flags(str, cmd->cmd_flags));
372 printf("%s\t", uint64_to_kmgt(str, cmd->cmd_block_size));
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
395 printf("%s\t", uint64_to_kmgt(str, summary_stats->wr_data));
396 printf("%s\t", uint64_to_kmgt(str, summary_stats->wr_chunks));
397 printf("%s\t", kmgt_per_sec(str, summary_stats->wr_data, wr_time));
398
399 printf("%s\t", uint64_to_kmgt(str, summary_stats->rd_data));
400 printf("%s\t", uint64_to_kmgt(str, summary_stats->rd_chunks));
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);
417 printf("%2u\t", cmd->cmd_id);
418
419 if (args->verbose) {
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);
428 printf("0x%x\t", cmd->cmd_flags);
429 printf("%u\t", cmd->cmd_block_size);
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",
443 (long)summary_stats->total_time.delta.ts_sec,
444 (long)summary_stats->total_time.delta.ts_nsec);
445 printf("%ld.%02ld\t",
446 (long)summary_stats->cr_time.delta.ts_sec,
447 (long)summary_stats->cr_time.delta.ts_nsec);
448 printf("%ld.%02ld\t",
449 (long)summary_stats->rm_time.delta.ts_sec,
450 (long)summary_stats->rm_time.delta.ts_nsec);
451 printf("%ld.%02ld\t",
452 (long)summary_stats->wr_time.delta.ts_sec,
453 (long)summary_stats->wr_time.delta.ts_nsec);
454 printf("%ld.%02ld\t",
455 (long)summary_stats->rd_time.delta.ts_sec,
456 (long)summary_stats->rd_time.delta.ts_nsec);
457 }
458
459 printf("%lld\t", (long long unsigned)summary_stats->wr_data);
460 printf("%lld\t", (long long unsigned)summary_stats->wr_chunks);
461 printf("%.4f\t", (double)summary_stats->wr_data / wr_time);
462
463 printf("%lld\t", (long long unsigned)summary_stats->rd_data);
464 printf("%lld\t", (long long unsigned)summary_stats->rd_chunks);
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 }