1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 *
25 * Portions Copyright 2008 Denis Cheng
26 */
27
28 #include <fcntl.h>
29 #include <pthread.h>
30 #include <errno.h>
31 #include <math.h>
32 #include <libgen.h>
33 #include <sys/mman.h>
34 #include <sys/shm.h>
35
36 #include "filebench.h"
37 #include "fileset.h"
38 #include "gamma_dist.h"
39 #include "utils.h"
40 #include "fsplug.h"
41
42 static int filecreate_done;
43
44 /*
45 * File sets, of type fileset_t, are entities which contain
46 * information about collections of files and subdirectories in Filebench.
47 * The fileset, once populated, consists of a tree of fileset entries of
48 * type filesetentry_t which specify files and directories. The fileset
49 * is rooted in a directory specified by fileset_path, and once the populated
50 * fileset has been created, has a tree of directories and files
51 * corresponding to the fileset's filesetentry tree.
52 *
53 * Fileset entities are allocated by fileset_define() which is called from
54 * parser_gram.y: parser_fileset_define(). The filesetentry tree corrseponding
55 * to the eventual directory and file tree to be instantiated on the storage
56 * medium is built by fileset_populate(), which is This routine is called
57 * from fileset_createset(), which is in turn called by fileset_createset().
58 * After calling fileset_populate(), fileset_createset() will call
59 * fileset_create() to pre-allocate designated files and directories.
60 *
61 * Fileset_createset() is called from parser_gram.y: parser_create_fileset()
62 * when a "create fileset" or "run" command is encountered. When the
63 * "create fileset" command is used, it is generally paired with
64 * a "create processes" command, and must appear first, in order to
65 * instantiate all the files in the fileset before trying to use them.
66 */
67
68 /* maximum parallel allocation control */
69 #define MAX_PARALLOC_THREADS 32
70
71 /*
72 * returns pointer to file or fileset
73 * string, as appropriate
74 */
75 static char *
fileset_entity_name(fileset_t * fileset)76 fileset_entity_name(fileset_t *fileset)
77 {
78 if (fileset->fs_attrs & FILESET_IS_FILE)
79 return ("file");
80 else
81 return ("fileset");
82 }
83
84 /*
85 * Removes the last file or directory name from a pathname.
86 * Basically removes characters from the end of the path by
87 * setting them to \0 until a forward slash '/' is
88 * encountered. It also removes the forward slash.
89 */
90 static char *
trunc_dirname(char * dir)91 trunc_dirname(char *dir)
92 {
93 char *s = dir + strlen(dir);
94
95 while (s != dir) {
96 int c = *s;
97
98 *s = 0;
99 if (c == '/')
100 break;
101 s--;
102 }
103 return (dir);
104 }
105
106 /*
107 * Creates a path string from the filesetentry_t "*entry"
108 * and all of its parent's path names. The resulting path
109 * is a concatination of all the individual parent paths.
110 * Allocates memory for the path string and returns a
111 * pointer to it.
112 */
113 char *
fileset_resolvepath(filesetentry_t * entry)114 fileset_resolvepath(filesetentry_t *entry)
115 {
116 filesetentry_t *fsep = entry;
117 char path[MAXPATHLEN];
118 char pathtmp[MAXPATHLEN];
119 char *s;
120
121 path[0] = '\0';
122 while (fsep->fse_parent) {
123 (void) strcpy(pathtmp, "/");
124 (void) fb_strlcat(pathtmp, fsep->fse_path, MAXPATHLEN);
125 (void) fb_strlcat(pathtmp, path, MAXPATHLEN);
126 (void) fb_strlcpy(path, pathtmp, MAXPATHLEN);
127 fsep = fsep->fse_parent;
128 }
129
130 s = malloc(strlen(path) + 1);
131 (void) fb_strlcpy(s, path, MAXPATHLEN);
132 return (s);
133 }
134
135 /*
136 * Creates multiple nested directories as required by the
137 * supplied path. Starts at the end of the path, creating
138 * a list of directories to mkdir, up to the root of the
139 * path, then mkdirs them one at a time from the root on down.
140 */
141 static int
fileset_mkdir(char * path,int mode)142 fileset_mkdir(char *path, int mode)
143 {
144 char *p;
145 char *dirs[65536];
146 int i = 0;
147
148 if ((p = strdup(path)) == NULL)
149 goto null_str;
150
151 /*
152 * Fill an array of subdirectory path names until either we
153 * reach the root or encounter an already existing subdirectory
154 */
155 /* CONSTCOND */
156 while (1) {
157 struct stat64 sb;
158
159 if (stat64(p, &sb) == 0)
160 break;
161 if (strlen(p) < 3)
162 break;
163 if ((dirs[i] = strdup(p)) == NULL) {
164 free(p);
165 goto null_str;
166 }
167
168 (void) trunc_dirname(p);
169 i++;
170 }
171
172 /* Make the directories, from closest to root downwards. */
173 for (--i; i >= 0; i--) {
174 (void) FB_MKDIR(dirs[i], mode);
175 free(dirs[i]);
176 }
177
178 free(p);
179 return (FILEBENCH_OK);
180
181 null_str:
182 /* clean up */
183 for (--i; i >= 0; i--)
184 free(dirs[i]);
185
186 filebench_log(LOG_ERROR,
187 "Failed to create directory path %s: Out of memory", path);
188 return (FILEBENCH_ERROR);
189 }
190
191 /*
192 * creates the subdirectory tree for a fileset.
193 */
194 static int
fileset_create_subdirs(fileset_t * fileset,char * filesetpath)195 fileset_create_subdirs(fileset_t *fileset, char *filesetpath)
196 {
197 filesetentry_t *direntry;
198 char full_path[MAXPATHLEN];
199 char *part_path;
200
201 /* walk the subdirectory list, enstanciating subdirs */
202 direntry = fileset->fs_dirlist;
203 while (direntry) {
204 (void) fb_strlcpy(full_path, filesetpath, MAXPATHLEN);
205 part_path = fileset_resolvepath(direntry);
206 (void) fb_strlcat(full_path, part_path, MAXPATHLEN);
207 free(part_path);
208
209 /* now create this portion of the subdirectory tree */
210 if (fileset_mkdir(full_path, 0755) == FILEBENCH_ERROR)
211 return (FILEBENCH_ERROR);
212
213 direntry = direntry->fse_nextoftype;
214 }
215 return (FILEBENCH_OK);
216 }
217
218 /*
219 * move filesetentry between exist tree and non-exist tree, source_tree
220 * to destination tree.
221 */
222 static void
fileset_move_entry(avl_tree_t * src_tree,avl_tree_t * dst_tree,filesetentry_t * entry)223 fileset_move_entry(avl_tree_t *src_tree, avl_tree_t *dst_tree,
224 filesetentry_t *entry)
225 {
226 avl_remove(src_tree, entry);
227 avl_add(dst_tree, entry);
228 }
229
230 /*
231 * given a fileset entry, determines if the associated leaf directory
232 * needs to be made or not, and if so does the mkdir.
233 */
234 static int
fileset_alloc_leafdir(filesetentry_t * entry)235 fileset_alloc_leafdir(filesetentry_t *entry)
236 {
237 fileset_t *fileset;
238 char path[MAXPATHLEN];
239 struct stat64 sb;
240 char *pathtmp;
241
242 fileset = entry->fse_fileset;
243 (void) fb_strlcpy(path, avd_get_str(fileset->fs_path), MAXPATHLEN);
244 (void) fb_strlcat(path, "/", MAXPATHLEN);
245 (void) fb_strlcat(path, avd_get_str(fileset->fs_name), MAXPATHLEN);
246 pathtmp = fileset_resolvepath(entry);
247 (void) fb_strlcat(path, pathtmp, MAXPATHLEN);
248 free(pathtmp);
249
250 filebench_log(LOG_DEBUG_IMPL, "Populated %s", entry->fse_path);
251
252 /* see if not reusing and this directory does not exist */
253 if (!((entry->fse_flags & FSE_REUSING) && (stat64(path, &sb) == 0))) {
254
255 /* No file or not reusing, so create */
256 if (FB_MKDIR(path, 0755) < 0) {
257 filebench_log(LOG_ERROR,
258 "Failed to pre-allocate leaf directory %s: %s",
259 path, strerror(errno));
260 fileset_unbusy(entry, TRUE, FALSE, 0);
261 return (FILEBENCH_ERROR);
262 }
263 }
264
265 /* unbusy the allocated entry */
266 fileset_unbusy(entry, TRUE, TRUE, 0);
267 return (FILEBENCH_OK);
268 }
269
270 /*
271 * given a fileset entry, determines if the associated file
272 * needs to be allocated or not, and if so does the allocation.
273 */
274 static int
fileset_alloc_file(filesetentry_t * entry)275 fileset_alloc_file(filesetentry_t *entry)
276 {
277 fileset_t *fileset;
278 char path[MAXPATHLEN];
279 char *buf;
280 struct stat64 sb;
281 char *pathtmp;
282 off64_t seek;
283 fb_fdesc_t fdesc;
284 int trust_tree;
285 int fs_readonly;
286
287 fileset = entry->fse_fileset;
288 (void) fb_strlcpy(path, avd_get_str(fileset->fs_path), MAXPATHLEN);
289 (void) fb_strlcat(path, "/", MAXPATHLEN);
290 (void) fb_strlcat(path, avd_get_str(fileset->fs_name), MAXPATHLEN);
291 pathtmp = fileset_resolvepath(entry);
292 (void) fb_strlcat(path, pathtmp, MAXPATHLEN);
293 free(pathtmp);
294
295 filebench_log(LOG_DEBUG_IMPL, "Populated %s", entry->fse_path);
296
297 /* see if fileset is readonly */
298 fs_readonly = avd_get_bool(fileset->fs_readonly) == TRUE;
299
300 /* see if reusing and this file exists */
301 trust_tree = avd_get_bool(fileset->fs_trust_tree);
302 if ((entry->fse_flags & FSE_REUSING) && (trust_tree ||
303 (FB_STAT(path, &sb) == 0))) {
304 if (FB_OPEN(&fdesc, path, fs_readonly ? O_RDONLY : O_RDWR, 0) == FILEBENCH_ERROR) {
305 filebench_log(LOG_INFO,
306 "Attempted but failed to Re-use file %s",
307 path);
308 fileset_unbusy(entry, TRUE, FALSE, 0);
309 return (FILEBENCH_ERROR);
310 }
311
312 if (trust_tree || (sb.st_size == (off64_t)entry->fse_size)) {
313 filebench_log(LOG_DEBUG_IMPL,
314 "Reusing file %s", path);
315
316 (void) FB_CLOSE(&fdesc);
317
318 /* unbusy the allocated entry */
319 fileset_unbusy(entry, TRUE, TRUE, 0);
320 return (FILEBENCH_OK);
321
322 } else if (sb.st_size > (off64_t)entry->fse_size) {
323 /* reuse, but too large */
324 filebench_log(LOG_DEBUG_IMPL,
325 "Truncating & re-using file %s", path);
326
327 (void) FB_FTRUNC(&fdesc, (off64_t)entry->fse_size);
328
329 (void) FB_CLOSE(&fdesc);
330
331 /* unbusy the allocated entry */
332 fileset_unbusy(entry, TRUE, TRUE, 0);
333 return (FILEBENCH_OK);
334 }
335 } else {
336
337 /* No file or not reusing, so create */
338 if (FB_OPEN(&fdesc, path, O_RDWR | O_CREAT, 0644) ==
339 FILEBENCH_ERROR) {
340 filebench_log(LOG_ERROR,
341 "Failed to pre-allocate file %s: %s",
342 path, strerror(errno));
343
344 /* unbusy the unallocated entry */
345 fileset_unbusy(entry, TRUE, FALSE, 0);
346 return (FILEBENCH_ERROR);
347 }
348 }
349
350 if ((buf = (char *)malloc(FILE_ALLOC_BLOCK)) == NULL) {
351 /* unbusy the unallocated entry */
352 fileset_unbusy(entry, TRUE, FALSE, 0);
353 return (FILEBENCH_ERROR);
354 }
355
356 for (seek = 0; seek < entry->fse_size; ) {
357 off64_t wsize;
358 int ret = 0;
359
360 /*
361 * Write FILE_ALLOC_BLOCK's worth,
362 * except on last write
363 */
364 wsize = MIN(entry->fse_size - seek, FILE_ALLOC_BLOCK);
365
366 ret = FB_WRITE(&fdesc, buf, wsize);
367 if (ret != wsize) {
368 filebench_log(LOG_ERROR,
369 "Failed to pre-allocate file %s: %s",
370 path, strerror(errno));
371 (void) FB_CLOSE(&fdesc);
372 free(buf);
373 fileset_unbusy(entry, TRUE, FALSE, 0);
374 return (FILEBENCH_ERROR);
375 }
376 seek += wsize;
377 }
378
379 (void) FB_CLOSE(&fdesc);
380
381 free(buf);
382
383 /* unbusy the allocated entry */
384 fileset_unbusy(entry, TRUE, TRUE, 0);
385
386 filebench_log(LOG_DEBUG_IMPL,
387 "Pre-allocated file %s size %llu",
388 path, (u_longlong_t)entry->fse_size);
389
390 return (FILEBENCH_OK);
391 }
392
393 /*
394 * given a fileset entry, determines if the associated file
395 * needs to be allocated or not, and if so does the allocation.
396 * Sets shm_fsparalloc_count to -1 on error.
397 */
398 static void *
fileset_alloc_thread(filesetentry_t * entry)399 fileset_alloc_thread(filesetentry_t *entry)
400 {
401 if (fileset_alloc_file(entry) == FILEBENCH_ERROR) {
402 (void) pthread_mutex_lock(&filebench_shm->shm_fsparalloc_lock);
403 filebench_shm->shm_fsparalloc_count = -1;
404 } else {
405 (void) pthread_mutex_lock(&filebench_shm->shm_fsparalloc_lock);
406 filebench_shm->shm_fsparalloc_count--;
407 }
408
409 (void) pthread_cond_signal(&filebench_shm->shm_fsparalloc_cv);
410 (void) pthread_mutex_unlock(&filebench_shm->shm_fsparalloc_lock);
411
412 pthread_exit(NULL);
413 return (NULL);
414 }
415
416
417 /*
418 * First creates the parent directories of the file using
419 * fileset_mkdir(). Then Optionally sets the O_DSYNC flag
420 * and opens the file with open64(). It unlocks the fileset
421 * entry lock, sets the DIRECTIO_ON or DIRECTIO_OFF flags
422 * as requested, and returns the file descriptor integer
423 * for the opened file in the supplied filebench file descriptor.
424 * Returns FILEBENCH_ERROR on error, and FILEBENCH_OK on success.
425 */
426 int
fileset_openfile(fb_fdesc_t * fdesc,fileset_t * fileset,filesetentry_t * entry,int flag,int filemode,int attrs)427 fileset_openfile(fb_fdesc_t *fdesc, fileset_t *fileset,
428 filesetentry_t *entry, int flag, int filemode, int attrs)
429 {
430 char path[MAXPATHLEN];
431 char dir[MAXPATHLEN];
432 char *pathtmp;
433 struct stat64 sb;
434 int open_attrs = 0;
435
436 (void) fb_strlcpy(path, avd_get_str(fileset->fs_path), MAXPATHLEN);
437 (void) fb_strlcat(path, "/", MAXPATHLEN);
438 (void) fb_strlcat(path, avd_get_str(fileset->fs_name), MAXPATHLEN);
439 pathtmp = fileset_resolvepath(entry);
440 (void) fb_strlcat(path, pathtmp, MAXPATHLEN);
441 (void) fb_strlcpy(dir, path, MAXPATHLEN);
442 free(pathtmp);
443 (void) trunc_dirname(dir);
444
445 /* If we are going to create a file, create the parent dirs */
446 if ((flag & O_CREAT) && (stat64(dir, &sb) != 0)) {
447 if (fileset_mkdir(dir, 0755) == FILEBENCH_ERROR)
448 return (FILEBENCH_ERROR);
449 }
450
451 if (attrs & FLOW_ATTR_DSYNC)
452 open_attrs |= O_SYNC;
453
454 #ifdef HAVE_O_DIRECT
455 if (attrs & FLOW_ATTR_DIRECTIO)
456 open_attrs |= O_DIRECT;
457 #endif /* HAVE_O_DIRECT */
458
459 if (FB_OPEN(fdesc, path, flag | open_attrs, filemode)
460 == FILEBENCH_ERROR) {
461 filebench_log(LOG_ERROR,
462 "Failed to open file %d, %s, with status %x: %s",
463 entry->fse_index, path, entry->fse_flags, strerror(errno));
464
465 fileset_unbusy(entry, FALSE, FALSE, 0);
466 return (FILEBENCH_ERROR);
467 }
468
469 #ifdef HAVE_DIRECTIO
470 if (attrs & FLOW_ATTR_DIRECTIO)
471 (void)directio(fdesc->fd_num, DIRECTIO_ON);
472 #endif /* HAVE_DIRECTIO */
473
474 #ifdef HAVE_NOCACHE_FCNTL
475 if (attrs & FLOW_ATTR_DIRECTIO)
476 (void)fcntl(fdesc->fd_num, F_NOCACHE, 1);
477 #endif /* HAVE_NOCACHE_FCNTL */
478
479 /* Disable read ahead with the help of fadvise, if asked for */
480 if (attrs & FLOW_ATTR_FADV_RANDOM) {
481 #ifdef HAVE_FADVISE
482 if (posix_fadvise(fdesc->fd_num, 0, 0, POSIX_FADV_RANDOM)
483 != FILEBENCH_OK) {
484 filebench_log(LOG_ERROR,
485 "Failed to disable read ahead for file %s, with status %s",
486 path, strerror(errno));
487 fileset_unbusy(entry, FALSE, FALSE, 0);
488 return (FILEBENCH_ERROR);
489 }
490 filebench_log(LOG_INFO, "** Read ahead disabled **");
491 #else
492 filebench_log(LOG_INFO, "** Read ahead was NOT disabled: not supported on this platform! **");
493 #endif
494 }
495
496
497 if (flag & O_CREAT)
498 fileset_unbusy(entry, TRUE, TRUE, 1);
499 else
500 fileset_unbusy(entry, FALSE, FALSE, 1);
501
502 return (FILEBENCH_OK);
503 }
504
505 /*
506 * removes all filesetentries from their respective btrees, and puts them
507 * on the free list. The supplied argument indicates which free list to
508 * use.
509 */
510 static void
fileset_pickreset(fileset_t * fileset,int entry_type)511 fileset_pickreset(fileset_t *fileset, int entry_type)
512 {
513 filesetentry_t *entry;
514
515 switch (entry_type & FILESET_PICKMASK) {
516 case FILESET_PICKFILE:
517 entry = (filesetentry_t *)avl_first(&fileset->fs_noex_files);
518
519 /* make sure non-existing files are marked free */
520 while (entry) {
521 entry->fse_flags |= FSE_FREE;
522 entry->fse_open_cnt = 0;
523 fileset_move_entry(&fileset->fs_noex_files,
524 &fileset->fs_free_files, entry);
525 entry = AVL_NEXT(&fileset->fs_noex_files, entry);
526 }
527
528 /* free up any existing files */
529 entry = (filesetentry_t *)avl_first(&fileset->fs_exist_files);
530
531 while (entry) {
532 entry->fse_flags |= FSE_FREE;
533 entry->fse_open_cnt = 0;
534 fileset_move_entry(&fileset->fs_exist_files,
535 &fileset->fs_free_files, entry);
536
537 entry = AVL_NEXT(&fileset->fs_exist_files, entry);
538 }
539
540 break;
541
542 case FILESET_PICKDIR:
543 /* nothing to reset, as all (sub)dirs always exist */
544 break;
545
546 case FILESET_PICKLEAFDIR:
547 entry = (filesetentry_t *)
548 avl_first(&fileset->fs_noex_leaf_dirs);
549
550 /* make sure non-existing leaf dirs are marked free */
551 while (entry) {
552 entry->fse_flags |= FSE_FREE;
553 entry->fse_open_cnt = 0;
554 fileset_move_entry(&fileset->fs_noex_leaf_dirs,
555 &fileset->fs_free_leaf_dirs, entry);
556 entry = AVL_NEXT(&fileset->fs_noex_leaf_dirs, entry);
557 }
558
559 /* free up any existing leaf dirs */
560 entry = (filesetentry_t *)
561 avl_first(&fileset->fs_exist_leaf_dirs);
562
563 while (entry) {
564 entry->fse_flags |= FSE_FREE;
565 entry->fse_open_cnt = 0;
566 fileset_move_entry(&fileset->fs_exist_leaf_dirs,
567 &fileset->fs_free_leaf_dirs, entry);
568
569 entry = AVL_NEXT(&fileset->fs_exist_leaf_dirs, entry);
570 }
571
572 break;
573 }
574 }
575
576 /*
577 * find a filesetentry from the fileset using the supplied index
578 */
579 static filesetentry_t *
fileset_find_entry(avl_tree_t * atp,uint_t index)580 fileset_find_entry(avl_tree_t *atp, uint_t index)
581 {
582 avl_index_t found_loc;
583 filesetentry_t desired_fse, *found_fse;
584
585 /* find the file with the desired index, if it is in the tree */
586 desired_fse.fse_index = index;
587 found_fse = avl_find(atp, (void *)(&desired_fse), &found_loc);
588 if (found_fse != NULL)
589 return (found_fse);
590
591 /* if requested node not found, find next higher node */
592 found_fse = avl_nearest(atp, found_loc, AVL_AFTER);
593 if (found_fse != NULL)
594 return (found_fse);
595
596 /* might have hit the end, return lowest available index node */
597 found_fse = avl_first(atp);
598 return (found_fse);
599 }
600
601 /*
602 * Selects a fileset entry from a fileset. If the
603 * FILESET_PICKLEAFDIR flag is set it will pick a leaf directory entry,
604 * if the FILESET_PICKDIR flag is set it will pick a non leaf directory
605 * entry, otherwise a file entry. The FILESET_PICKUNIQUE
606 * flag will take an entry off of one of the free (unused)
607 * lists (file or directory), otherwise the entry will be
608 * picked off of one of the rotor lists (file or directory).
609 * The FILESET_PICKEXISTS will insure that only extant
610 * (FSE_EXISTS) state files are selected, while
611 * FILESET_PICKNOEXIST insures that only non extant
612 * (not FSE_EXISTS) state files are selected.
613 * Note that the selected fileset entry (file) is returned
614 * with its FSE_BUSY flag (in fse_flags) set.
615 */
616 filesetentry_t *
fileset_pick(fileset_t * fileset,int flags,int tid,int index)617 fileset_pick(fileset_t *fileset, int flags, int tid, int index)
618 {
619 filesetentry_t *entry = NULL;
620 filesetentry_t *start_point;
621 avl_tree_t *atp = NULL;
622 fbint_t max_entries = 0;
623
624 (void) ipc_mutex_lock(&fileset->fs_pick_lock);
625
626 /* see if we have to wait for available files or directories */
627 switch (flags & FILESET_PICKMASK) {
628 case FILESET_PICKFILE:
629
630 filebench_log(LOG_DEBUG_SCRIPT, "Picking file");
631
632 if (fileset->fs_filelist == NULL)
633 goto empty;
634
635 while (fileset->fs_idle_files == 0) {
636 (void) pthread_cond_wait(&fileset->fs_idle_files_cv,
637 &fileset->fs_pick_lock);
638 }
639
640 max_entries = fileset->fs_constentries;
641 if (flags & FILESET_PICKUNIQUE) {
642 filebench_log(LOG_DEBUG_SCRIPT, "Picking unique");
643 atp = &fileset->fs_free_files;
644 } else if (flags & FILESET_PICKNOEXIST) {
645 filebench_log(LOG_DEBUG_SCRIPT, "Picking not existing");
646 atp = &fileset->fs_noex_files;
647 } else {
648 filebench_log(LOG_DEBUG_SCRIPT, "Picking existing");
649 atp = &fileset->fs_exist_files;
650 }
651 break;
652
653 case FILESET_PICKDIR:
654
655 filebench_log(LOG_DEBUG_SCRIPT, "Picking directory");
656
657 if (fileset->fs_dirlist == NULL)
658 goto empty;
659
660 while (fileset->fs_idle_dirs == 0) {
661 (void) pthread_cond_wait(&fileset->fs_idle_dirs_cv,
662 &fileset->fs_pick_lock);
663 }
664
665 max_entries = 1;
666 atp = &fileset->fs_dirs;
667 break;
668
669 case FILESET_PICKLEAFDIR:
670
671 filebench_log(LOG_DEBUG_SCRIPT, "Picking leaf directory");
672
673 if (fileset->fs_leafdirlist == NULL)
674 goto empty;
675
676 while (fileset->fs_idle_leafdirs == 0) {
677 (void) pthread_cond_wait(&fileset->fs_idle_leafdirs_cv,
678 &fileset->fs_pick_lock);
679 }
680
681 max_entries = fileset->fs_constleafdirs;
682 if (flags & FILESET_PICKUNIQUE) {
683 atp = &fileset->fs_free_leaf_dirs;
684 } else if (flags & FILESET_PICKNOEXIST) {
685 atp = &fileset->fs_noex_leaf_dirs;
686 } else {
687 atp = &fileset->fs_exist_leaf_dirs;
688 }
689 break;
690 }
691
692 /* see if asking for impossible */
693 if (avl_is_empty(atp)) {
694 filebench_log(LOG_DEBUG_SCRIPT, "atp is empty");
695 goto empty;
696 }
697
698 if (flags & FILESET_PICKUNIQUE) {
699 uint64_t index64;
700
701 /*
702 * pick at random from free list in order to
703 * distribute initially allocated files more
704 * randomly on storage media. Use uniform
705 * random number generator to select index
706 * if it is not supplied with pick call.
707 */
708 if (index) {
709 index64 = index;
710 } else {
711 fb_random64(&index64, max_entries, 0, NULL);
712 }
713
714 entry = fileset_find_entry(atp, (int)index64);
715
716 if (entry == NULL)
717 goto empty;
718
719 } else if (flags & FILESET_PICKBYINDEX) {
720 /* pick by supplied index */
721 entry = fileset_find_entry(atp, index);
722
723 } else {
724 /* pick in rotation */
725 switch (flags & FILESET_PICKMASK) {
726 case FILESET_PICKFILE:
727 if (flags & FILESET_PICKNOEXIST) {
728 entry = fileset_find_entry(atp,
729 fileset->fs_file_nerotor);
730 fileset->fs_file_nerotor =
731 entry->fse_index + 1;
732 } else {
733 entry = fileset_find_entry(atp,
734 fileset->fs_file_exrotor[tid]);
735 fileset->fs_file_exrotor[tid] =
736 entry->fse_index + 1;
737 }
738 break;
739
740 case FILESET_PICKDIR:
741 entry = fileset_find_entry(atp, fileset->fs_dirrotor);
742 fileset->fs_dirrotor = entry->fse_index + 1;
743 break;
744
745 case FILESET_PICKLEAFDIR:
746 if (flags & FILESET_PICKNOEXIST) {
747 entry = fileset_find_entry(atp,
748 fileset->fs_leafdir_nerotor);
749 fileset->fs_leafdir_nerotor =
750 entry->fse_index + 1;
751 } else {
752 entry = fileset_find_entry(atp,
753 fileset->fs_leafdir_exrotor);
754 fileset->fs_leafdir_exrotor =
755 entry->fse_index + 1;
756 }
757 break;
758 }
759 }
760
761 if (entry == NULL)
762 goto empty;
763
764 /* see if entry in use */
765 start_point = entry;
766 while (entry->fse_flags & FSE_BUSY) {
767
768 /* it is, so try next */
769 entry = AVL_NEXT(atp, entry);
770 if (entry == NULL)
771 entry = avl_first(atp);
772
773 /* see if we have wrapped around */
774 if ((entry == NULL) || (entry == start_point)) {
775 filebench_log(LOG_DEBUG_SCRIPT,
776 "All %d files are busy", avl_numnodes(atp));
777 goto empty;
778 }
779
780 }
781
782 /* update file or directory idle counts */
783 switch (flags & FILESET_PICKMASK) {
784 case FILESET_PICKFILE:
785 fileset->fs_idle_files--;
786 break;
787 case FILESET_PICKDIR:
788 fileset->fs_idle_dirs--;
789 break;
790 case FILESET_PICKLEAFDIR:
791 fileset->fs_idle_leafdirs--;
792 break;
793 }
794
795 /* Indicate that file or directory is now busy */
796 entry->fse_flags |= FSE_BUSY;
797
798 (void) ipc_mutex_unlock(&fileset->fs_pick_lock);
799 filebench_log(LOG_DEBUG_SCRIPT, "Picked file %s", entry->fse_path);
800 return (entry);
801
802 empty:
803 filebench_log(LOG_DEBUG_SCRIPT, "No file found");
804 (void) ipc_mutex_unlock(&fileset->fs_pick_lock);
805 return (NULL);
806 }
807
808 /*
809 * Removes a filesetentry from the "FSE_BUSY" state, signaling any threads
810 * that are waiting for a NOT BUSY filesetentry. Also sets whether it is
811 * existant or not, or leaves that designation alone.
812 */
813 void
fileset_unbusy(filesetentry_t * entry,int update_exist,int new_exist_val,int open_cnt_incr)814 fileset_unbusy(filesetentry_t *entry, int update_exist,
815 int new_exist_val, int open_cnt_incr)
816 {
817 fileset_t *fileset = NULL;
818
819 if (entry)
820 fileset = entry->fse_fileset;
821
822 if (fileset == NULL) {
823 filebench_log(LOG_ERROR, "fileset_unbusy: NO FILESET!");
824 return;
825 }
826
827 (void) ipc_mutex_lock(&fileset->fs_pick_lock);
828
829 /* modify FSE_EXIST flag and actual dirs/files count, if requested */
830 if (update_exist) {
831 if (new_exist_val == TRUE) {
832 if (entry->fse_flags & FSE_FREE) {
833
834 /* asked to set and it was free */
835 entry->fse_flags |= FSE_EXISTS;
836 entry->fse_flags &= (~FSE_FREE);
837 switch (entry->fse_flags & FSE_TYPE_MASK) {
838 case FSE_TYPE_FILE:
839 fileset_move_entry(
840 &fileset->fs_free_files,
841 &fileset->fs_exist_files, entry);
842 break;
843
844 case FSE_TYPE_DIR:
845 break;
846
847 case FSE_TYPE_LEAFDIR:
848 fileset_move_entry(
849 &fileset->fs_free_leaf_dirs,
850 &fileset->fs_exist_leaf_dirs,
851 entry);
852 break;
853 }
854
855 } else if (!(entry->fse_flags & FSE_EXISTS)) {
856
857 /* asked to set, and it was clear */
858 entry->fse_flags |= FSE_EXISTS;
859 switch (entry->fse_flags & FSE_TYPE_MASK) {
860 case FSE_TYPE_FILE:
861 fileset_move_entry(
862 &fileset->fs_noex_files,
863 &fileset->fs_exist_files, entry);
864 break;
865 case FSE_TYPE_DIR:
866 break;
867 case FSE_TYPE_LEAFDIR:
868 fileset_move_entry(
869 &fileset->fs_noex_leaf_dirs,
870 &fileset->fs_exist_leaf_dirs,
871 entry);
872 break;
873 }
874 }
875 } else {
876 if (entry->fse_flags & FSE_FREE) {
877 /* asked to clear, and it was free */
878 entry->fse_flags &= (~(FSE_FREE | FSE_EXISTS));
879 switch (entry->fse_flags & FSE_TYPE_MASK) {
880 case FSE_TYPE_FILE:
881 fileset_move_entry(
882 &fileset->fs_free_files,
883 &fileset->fs_noex_files, entry);
884 break;
885
886 case FSE_TYPE_DIR:
887 break;
888
889 case FSE_TYPE_LEAFDIR:
890 fileset_move_entry(
891 &fileset->fs_free_leaf_dirs,
892 &fileset->fs_noex_leaf_dirs,
893 entry);
894 break;
895 }
896 } else if (entry->fse_flags & FSE_EXISTS) {
897
898 /* asked to clear, and it was set */
899 entry->fse_flags &= (~FSE_EXISTS);
900 switch (entry->fse_flags & FSE_TYPE_MASK) {
901 case FSE_TYPE_FILE:
902 fileset_move_entry(
903 &fileset->fs_exist_files,
904 &fileset->fs_noex_files, entry);
905 break;
906 case FSE_TYPE_DIR:
907 break;
908 case FSE_TYPE_LEAFDIR:
909 fileset_move_entry(
910 &fileset->fs_exist_leaf_dirs,
911 &fileset->fs_noex_leaf_dirs,
912 entry);
913 break;
914 }
915 }
916 }
917 }
918
919 /* update open count */
920 entry->fse_open_cnt += open_cnt_incr;
921
922 /* increment idle count, clear FSE_BUSY and signal IF it was busy */
923 if (entry->fse_flags & FSE_BUSY) {
924
925 /* unbusy it */
926 entry->fse_flags &= (~FSE_BUSY);
927
928 /* release any threads waiting for unbusy */
929 if (entry->fse_flags & FSE_THRD_WAITNG) {
930 entry->fse_flags &= (~FSE_THRD_WAITNG);
931 (void) pthread_cond_broadcast(
932 &fileset->fs_thrd_wait_cv);
933 }
934
935 /* increment idle count and signal waiting threads */
936 switch (entry->fse_flags & FSE_TYPE_MASK) {
937 case FSE_TYPE_FILE:
938 fileset->fs_idle_files++;
939 if (fileset->fs_idle_files >= 1) {
940 (void) pthread_cond_signal(
941 &fileset->fs_idle_files_cv);
942 }
943 break;
944
945 case FSE_TYPE_DIR:
946 fileset->fs_idle_dirs++;
947 if (fileset->fs_idle_dirs >= 1) {
948 (void) pthread_cond_signal(
949 &fileset->fs_idle_dirs_cv);
950 }
951 break;
952
953 case FSE_TYPE_LEAFDIR:
954 fileset->fs_idle_leafdirs++;
955 if (fileset->fs_idle_leafdirs >= 1) {
956 (void) pthread_cond_signal(
957 &fileset->fs_idle_leafdirs_cv);
958 }
959 break;
960 }
961 }
962
963 (void) ipc_mutex_unlock(&fileset->fs_pick_lock);
964 }
965
966 /*
967 * Given a fileset "fileset", create the associated files as specified in the
968 * attributes of the fileset. The fileset is rooted in a directory whose
969 * pathname is in fileset_path. If the directory exists, meaning that there is
970 * already a fileset, and the fileset_reuse attribute is false, then remove it
971 * and all its contained files and subdirectories. Next, the routine creates a
972 * root directory for the fileset. All the file type filesetentries are cycled
973 * through creating as needed their containing subdirectory trees in the
974 * filesystem and creating actual files for fileset_preallocpercent of them.
975 * The created files are filled with fse_size bytes of unitialized data. The
976 * routine returns FILEBENCH_ERROR on errors, FILEBENCH_OK on success.
977 */
978 static int
fileset_create(fileset_t * fileset)979 fileset_create(fileset_t *fileset)
980 {
981 filesetentry_t *entry;
982 char path[MAXPATHLEN];
983 struct stat64 sb;
984 hrtime_t start = gethrtime();
985 char *fileset_path;
986 char *fileset_name;
987 int randno;
988 int preallocated = 0;
989 int reusing;
990 uint64_t preallocpercent;
991
992 fileset_path = avd_get_str(fileset->fs_path);
993 if (!fileset_path) {
994 filebench_log(LOG_ERROR, "%s path not set",
995 fileset_entity_name(fileset));
996 return FILEBENCH_ERROR;
997 }
998
999 fileset_name = avd_get_str(fileset->fs_name);
1000 if (!fileset_name) {
1001 filebench_log(LOG_ERROR, "%s name not set",
1002 fileset_entity_name(fileset));
1003 return FILEBENCH_ERROR;
1004 }
1005
1006 /* treat raw device as special case */
1007 if (fileset->fs_attrs & FILESET_IS_RAW_DEV)
1008 return FILEBENCH_OK;
1009
1010 /* XXX Add check to see if there is enough space */
1011
1012 /* set up path to fileset */
1013 (void) fb_strlcpy(path, fileset_path, MAXPATHLEN);
1014 (void) fb_strlcat(path, "/", MAXPATHLEN);
1015 (void) fb_strlcat(path, fileset_name, MAXPATHLEN);
1016
1017 /* if reusing and trusting to exist, just blindly reuse */
1018 if (avd_get_bool(fileset->fs_trust_tree)) {
1019 reusing = 1;
1020 /* if exists and resusing, then don't create new */
1021 } else if (((stat64(path, &sb) == 0)&& (strlen(path) > 3) &&
1022 (strlen(avd_get_str(fileset->fs_path)) > 2)) &&
1023 avd_get_bool(fileset->fs_reuse)) {
1024 reusing = 1;
1025 } else {
1026 reusing = 0;
1027 }
1028
1029 if (!reusing) {
1030 /* Remove existing */
1031 filebench_log(LOG_INFO,
1032 "Removing %s tree (if exists)", fileset_name);
1033
1034 FB_RECUR_RM(path);
1035 } else {
1036 /* we are re-using */
1037 filebench_log(LOG_INFO, "Reusing existing %s tree",
1038 fileset_name);
1039 }
1040
1041 /* make the filesets directory tree unless in reuse mode */
1042 if (!reusing) {
1043 filebench_log(LOG_INFO,
1044 "Pre-allocating directories in %s tree", fileset_name);
1045
1046 (void) FB_MKDIR(path, 0755);
1047
1048 if (fileset_create_subdirs(fileset, path) == FILEBENCH_ERROR)
1049 return (FILEBENCH_ERROR);
1050 }
1051
1052 start = gethrtime();
1053
1054 filebench_log(LOG_INFO,
1055 "Pre-allocating files in %s tree", fileset_name);
1056
1057 if (AVD_IS_BOOL(fileset->fs_preallocpercent)) {
1058 if (avd_get_bool(fileset->fs_preallocpercent))
1059 preallocpercent = 100;
1060 else
1061 preallocpercent = 0;
1062 } else
1063 preallocpercent = avd_get_int(fileset->fs_preallocpercent);
1064
1065 randno = ((RAND_MAX * (100 - preallocpercent)) / 100);
1066
1067 /* alloc any files, as required */
1068 fileset_pickreset(fileset, FILESET_PICKFILE);
1069 while ((entry = fileset_pick(fileset,
1070 FILESET_PICKFREE | FILESET_PICKFILE, 0, 0))) {
1071 pthread_t tid;
1072 int newrand;
1073
1074 newrand = rand();
1075
1076 if (randno && newrand <= randno) {
1077 /* unbusy the unallocated entry */
1078 fileset_unbusy(entry, TRUE, FALSE, 0);
1079 continue;
1080 }
1081
1082 preallocated++;
1083
1084 if (reusing)
1085 entry->fse_flags |= FSE_REUSING;
1086 else
1087 entry->fse_flags &= (~FSE_REUSING);
1088
1089 /* fire off allocation threads for each file if paralloc set */
1090 if (avd_get_bool(fileset->fs_paralloc)) {
1091
1092 /* limit total number of simultaneous allocations */
1093 (void) pthread_mutex_lock(
1094 &filebench_shm->shm_fsparalloc_lock);
1095 while (filebench_shm->shm_fsparalloc_count
1096 >= MAX_PARALLOC_THREADS) {
1097 (void) pthread_cond_wait(
1098 &filebench_shm->shm_fsparalloc_cv,
1099 &filebench_shm->shm_fsparalloc_lock);
1100 }
1101
1102 /* quit if any allocation thread reports an error */
1103 if (filebench_shm->shm_fsparalloc_count < 0) {
1104 (void) pthread_mutex_unlock(
1105 &filebench_shm->shm_fsparalloc_lock);
1106 return (FILEBENCH_ERROR);
1107 }
1108
1109 filebench_shm->shm_fsparalloc_count++;
1110 (void) pthread_mutex_unlock(
1111 &filebench_shm->shm_fsparalloc_lock);
1112
1113 /*
1114 * Fire off a detached allocation thread per file.
1115 * The thread will self destruct when it finishes
1116 * writing pre-allocation data to the file.
1117 */
1118 if (pthread_create(&tid, NULL,
1119 (void *(*)(void*))fileset_alloc_thread,
1120 entry) == 0) {
1121 /*
1122 * A thread was created; detach it so it can
1123 * fully quit when finished.
1124 */
1125 (void) pthread_detach(tid);
1126 } else {
1127 filebench_log(LOG_ERROR,
1128 "File prealloc thread create failed");
1129 filebench_shutdown(1);
1130 }
1131
1132 } else {
1133 if (fileset_alloc_file(entry) == FILEBENCH_ERROR)
1134 return FILEBENCH_ERROR;
1135 }
1136 }
1137
1138 /* alloc any leaf directories, as required */
1139 fileset_pickreset(fileset, FILESET_PICKLEAFDIR);
1140 while ((entry = fileset_pick(fileset,
1141 FILESET_PICKFREE | FILESET_PICKLEAFDIR, 0, 0))) {
1142
1143 if (rand() < randno) {
1144 /* unbusy the unallocated entry */
1145 fileset_unbusy(entry, TRUE, FALSE, 0);
1146 continue;
1147 }
1148
1149 preallocated++;
1150
1151 if (reusing)
1152 entry->fse_flags |= FSE_REUSING;
1153 else
1154 entry->fse_flags &= (~FSE_REUSING);
1155
1156 if (fileset_alloc_leafdir(entry) == FILEBENCH_ERROR)
1157 return (FILEBENCH_ERROR);
1158 }
1159
1160 filebench_log(LOG_VERBOSE,
1161 "Pre-allocated %d of %llu files in %s in %llu seconds",
1162 preallocated,
1163 (u_longlong_t)fileset->fs_constentries,
1164 fileset_name,
1165 (u_longlong_t)(((gethrtime() - start) / 1000000000) + 1));
1166
1167 return (FILEBENCH_OK);
1168 }
1169
1170 /*
1171 * Removes all files and directories associated with a fileset
1172 * from the storage subsystem.
1173 */
1174 static void
fileset_delete_storage(fileset_t * fileset)1175 fileset_delete_storage(fileset_t *fileset)
1176 {
1177 char path[MAXPATHLEN];
1178 char *fileset_path;
1179 char *fileset_name;
1180
1181 if ((fileset_path = avd_get_str(fileset->fs_path)) == NULL)
1182 return;
1183
1184 if ((fileset_name = avd_get_str(fileset->fs_name)) == NULL)
1185 return;
1186
1187 /* treat raw device as special case */
1188 if (fileset->fs_attrs & FILESET_IS_RAW_DEV)
1189 return;
1190
1191 /* set up path to file */
1192 (void) fb_strlcpy(path, fileset_path, MAXPATHLEN);
1193 (void) fb_strlcat(path, "/", MAXPATHLEN);
1194 (void) fb_strlcat(path, fileset_name, MAXPATHLEN);
1195
1196 /* now delete any files and directories on the disk */
1197 FB_RECUR_RM(path);
1198 }
1199
1200 /*
1201 * Removes the fileset entity and all of its filesetentry entities.
1202 */
1203 static void
fileset_delete_fileset(fileset_t * fileset)1204 fileset_delete_fileset(fileset_t *fileset)
1205 {
1206 filesetentry_t *entry, *next_entry;
1207
1208 /* run down the file list, removing and freeing each filesetentry */
1209 for (entry = fileset->fs_filelist; entry; entry = next_entry) {
1210
1211 /* free the entry */
1212 next_entry = entry->fse_next;
1213
1214 /* return it to the pool */
1215 switch (entry->fse_flags & FSE_TYPE_MASK) {
1216 case FSE_TYPE_FILE:
1217 case FSE_TYPE_LEAFDIR:
1218 case FSE_TYPE_DIR:
1219 ipc_free(FILEBENCH_FILESETENTRY, (void *)entry);
1220 break;
1221 default:
1222 filebench_log(LOG_ERROR,
1223 "Unallocated filesetentry found on list");
1224 break;
1225 }
1226 }
1227
1228 ipc_free(FILEBENCH_FILESET, (void *)fileset);
1229 }
1230
1231 void
fileset_delete_all_filesets(void)1232 fileset_delete_all_filesets(void)
1233 {
1234 fileset_t *fileset, *next_fileset;
1235
1236 for (fileset = filebench_shm->shm_filesetlist;
1237 fileset; fileset = next_fileset) {
1238 next_fileset = fileset->fs_next;
1239 fileset_delete_storage(fileset);
1240 fileset_delete_fileset(fileset);
1241 }
1242
1243 filebench_shm->shm_filesetlist = NULL;
1244 }
1245 /*
1246 * Adds an entry to the fileset's file list. Single threaded so
1247 * no locking needed.
1248 */
1249 static void
fileset_insfilelist(fileset_t * fileset,filesetentry_t * entry)1250 fileset_insfilelist(fileset_t *fileset, filesetentry_t *entry)
1251 {
1252 entry->fse_flags = FSE_TYPE_FILE | FSE_FREE;
1253 avl_add(&fileset->fs_free_files, entry);
1254
1255 if (fileset->fs_filelist == NULL) {
1256 fileset->fs_filelist = entry;
1257 entry->fse_nextoftype = NULL;
1258 } else {
1259 entry->fse_nextoftype = fileset->fs_filelist;
1260 fileset->fs_filelist = entry;
1261 }
1262 }
1263
1264 /*
1265 * Adds an entry to the fileset's directory list. Single
1266 * threaded so no locking needed.
1267 */
1268 static void
fileset_insdirlist(fileset_t * fileset,filesetentry_t * entry)1269 fileset_insdirlist(fileset_t *fileset, filesetentry_t *entry)
1270 {
1271 entry->fse_flags = FSE_TYPE_DIR | FSE_EXISTS;
1272 avl_add(&fileset->fs_dirs, entry);
1273
1274 if (fileset->fs_dirlist == NULL) {
1275 fileset->fs_dirlist = entry;
1276 entry->fse_nextoftype = NULL;
1277 } else {
1278 entry->fse_nextoftype = fileset->fs_dirlist;
1279 fileset->fs_dirlist = entry;
1280 }
1281 }
1282
1283 /*
1284 * Adds an entry to the fileset's leaf directory list. Single
1285 * threaded so no locking needed.
1286 */
1287 static void
fileset_insleafdirlist(fileset_t * fileset,filesetentry_t * entry)1288 fileset_insleafdirlist(fileset_t *fileset, filesetentry_t *entry)
1289 {
1290 entry->fse_flags = FSE_TYPE_LEAFDIR | FSE_FREE;
1291 avl_add(&fileset->fs_free_leaf_dirs, entry);
1292
1293 if (fileset->fs_leafdirlist == NULL) {
1294 fileset->fs_leafdirlist = entry;
1295 entry->fse_nextoftype = NULL;
1296 } else {
1297 entry->fse_nextoftype = fileset->fs_leafdirlist;
1298 fileset->fs_leafdirlist = entry;
1299 }
1300 }
1301
1302 /*
1303 * Compares two fileset entries to determine their relative order
1304 */
1305 static int
fileset_entry_compare(const void * node_1,const void * node_2)1306 fileset_entry_compare(const void *node_1, const void *node_2)
1307 {
1308 if (((filesetentry_t *)node_1)->fse_index <
1309 ((filesetentry_t *)node_2)->fse_index)
1310 return (-1);
1311
1312 if (((filesetentry_t *)node_1)->fse_index ==
1313 ((filesetentry_t *)node_2)->fse_index)
1314 return (0);
1315
1316 return (1);
1317 }
1318
1319 /*
1320 * Obtains a filesetentry entity for a file to be placed in a
1321 * (sub)directory of a fileset. The size of the file may be
1322 * specified by fileset_meansize, or calculated from a gamma
1323 * distribution of parameter fileset_sizegamma and of mean size
1324 * fileset_meansize. The filesetentry entity is placed on the file
1325 * list in the specified parent filesetentry entity, which may
1326 * be a directory filesetentry, or the root filesetentry in the
1327 * fileset. It is also placed on the fileset's list of all
1328 * contained files. Returns FILEBENCH_OK if successful or FILEBENCH_ERROR
1329 * if ipc memory for the path string cannot be allocated.
1330 */
1331 static int
fileset_populate_file(fileset_t * fileset,filesetentry_t * parent,int serial)1332 fileset_populate_file(fileset_t *fileset, filesetentry_t *parent, int serial)
1333 {
1334 char tmpname[16];
1335 filesetentry_t *entry;
1336 uint_t index;
1337
1338 if ((entry = (filesetentry_t *)ipc_malloc(FILEBENCH_FILESETENTRY))
1339 == NULL) {
1340 filebench_log(LOG_ERROR,
1341 "fileset_populate_file: Can't malloc filesetentry");
1342 return (FILEBENCH_ERROR);
1343 }
1344
1345 /* Another currently idle file */
1346 (void) ipc_mutex_lock(&fileset->fs_pick_lock);
1347 index = fileset->fs_idle_files++;
1348 (void) ipc_mutex_unlock(&fileset->fs_pick_lock);
1349
1350 entry->fse_index = index;
1351 entry->fse_parent = parent;
1352 entry->fse_fileset = fileset;
1353 fileset_insfilelist(fileset, entry);
1354
1355 (void) snprintf(tmpname, sizeof (tmpname), "%08d", serial);
1356 if ((entry->fse_path = (char *)ipc_pathalloc(tmpname)) == NULL) {
1357 filebench_log(LOG_ERROR,
1358 "fileset_populate_file: Can't alloc path string");
1359 return (FILEBENCH_ERROR);
1360 }
1361
1362 entry->fse_size = (off64_t)avd_get_int(fileset->fs_size);
1363 fileset->fs_bytes += entry->fse_size;
1364
1365 fileset->fs_realfiles++;
1366 return (FILEBENCH_OK);
1367 }
1368
1369 /*
1370 * Obtaines a filesetentry entity for a leaf directory to be placed in a
1371 * (sub)directory of a fileset. The leaf directory will always be empty so
1372 * it can be created and deleted (mkdir, rmdir) at will. The filesetentry
1373 * entity is placed on the leaf directory list in the specified parent
1374 * filesetentry entity, which may be a (sub) directory filesetentry, or
1375 * the root filesetentry in the fileset. It is also placed on the fileset's
1376 * list of all contained leaf directories. Returns FILEBENCH_OK if successful
1377 * or FILEBENCH_ERROR if ipc memory cannot be allocated.
1378 */
1379 static int
fileset_populate_leafdir(fileset_t * fileset,filesetentry_t * parent,int serial)1380 fileset_populate_leafdir(fileset_t *fileset, filesetentry_t *parent, int serial)
1381 {
1382 char tmpname[16];
1383 filesetentry_t *entry;
1384 uint_t index;
1385
1386 if ((entry = (filesetentry_t *)ipc_malloc(FILEBENCH_FILESETENTRY))
1387 == NULL) {
1388 filebench_log(LOG_ERROR,
1389 "fileset_populate_file: Can't malloc filesetentry");
1390 return (FILEBENCH_ERROR);
1391 }
1392
1393 /* Another currently idle leaf directory */
1394 (void) ipc_mutex_lock(&fileset->fs_pick_lock);
1395 index = fileset->fs_idle_leafdirs++;
1396 (void) ipc_mutex_unlock(&fileset->fs_pick_lock);
1397
1398 entry->fse_index = index;
1399 entry->fse_parent = parent;
1400 entry->fse_fileset = fileset;
1401 fileset_insleafdirlist(fileset, entry);
1402
1403 (void) snprintf(tmpname, sizeof (tmpname), "%08d", serial);
1404 if ((entry->fse_path = (char *)ipc_pathalloc(tmpname)) == NULL) {
1405 filebench_log(LOG_ERROR,
1406 "fileset_populate_file: Can't alloc path string");
1407 return (FILEBENCH_ERROR);
1408 }
1409
1410 fileset->fs_realleafdirs++;
1411 return (FILEBENCH_OK);
1412 }
1413
1414 /*
1415 * Creates a directory node in a fileset, by obtaining a filesetentry entity
1416 * for the node and initializing it according to parameters of the fileset. It
1417 * determines a directory tree depth and directory width, optionally using a
1418 * gamma distribution. If its calculated depth is less then its actual depth in
1419 * the directory tree, it becomes a leaf node and files itself with "width"
1420 * number of file type filesetentries, otherwise it files itself with "width"
1421 * number of directory type filesetentries, using recursive calls to
1422 * fileset_populate_subdir. The end result of the initial call to this routine
1423 * is a tree of directories of random width and varying depth with sufficient
1424 * leaf directories to contain all required files. Returns FILEBENCH_OK on
1425 * success. Returns FILEBENCH_ERROR if ipc path string memory cannot be
1426 * allocated and returns the error code (currently also FILEBENCH_ERROR) from
1427 * calls to fileset_populate_file or recursive calls to
1428 * fileset_populate_subdir.
1429 */
1430 static int
fileset_populate_subdir(fileset_t * fileset,filesetentry_t * parent,int serial,double depth)1431 fileset_populate_subdir(fileset_t *fileset, filesetentry_t *parent,
1432 int serial, double depth)
1433 {
1434 double randepth, drand, ranwidth;
1435 int isleaf = 0;
1436 char tmpname[16];
1437 filesetentry_t *entry;
1438 int i;
1439 uint_t index;
1440
1441 depth += 1;
1442
1443 /* Create dir node */
1444 entry = (filesetentry_t *)ipc_malloc(FILEBENCH_FILESETENTRY);
1445 if (!entry) {
1446 filebench_log(LOG_ERROR,
1447 "fileset_populate_subdir: Can't malloc filesetentry");
1448 return (FILEBENCH_ERROR);
1449 }
1450
1451 /* another idle directory */
1452 (void) ipc_mutex_lock(&fileset->fs_pick_lock);
1453 index = fileset->fs_idle_dirs++;
1454 (void) ipc_mutex_unlock(&fileset->fs_pick_lock);
1455
1456 (void) snprintf(tmpname, sizeof (tmpname), "%08d", serial);
1457 if ((entry->fse_path = (char *)ipc_pathalloc(tmpname)) == NULL) {
1458 filebench_log(LOG_ERROR,
1459 "fileset_populate_subdir: Can't alloc path string");
1460 return (FILEBENCH_ERROR);
1461 }
1462
1463 entry->fse_index = index;
1464 entry->fse_parent = parent;
1465 entry->fse_fileset = fileset;
1466 fileset_insdirlist(fileset, entry);
1467
1468 if (fileset->fs_dirdepthrv) {
1469 randepth = (int)avd_get_int(fileset->fs_dirdepthrv);
1470 } else {
1471 double gamma;
1472
1473 gamma = avd_get_int(fileset->fs_dirgamma) / 1000.0;
1474 if (gamma > 0) {
1475 drand = gamma_dist_knuth(gamma,
1476 fileset->fs_meandepth / gamma);
1477 randepth = (int)drand;
1478 } else {
1479 randepth = (int)fileset->fs_meandepth;
1480 }
1481 }
1482
1483 if (fileset->fs_meanwidth == -1) {
1484 ranwidth = avd_get_dbl(fileset->fs_dirwidth);
1485 } else {
1486 double gamma;
1487
1488 gamma = avd_get_int(fileset->fs_dirgamma) / 1000.0;
1489 if (gamma > 0) {
1490 drand = gamma_dist_knuth(gamma,
1491 fileset->fs_meanwidth / gamma);
1492 ranwidth = drand;
1493 } else {
1494 ranwidth = fileset->fs_meanwidth;
1495 }
1496 }
1497
1498 if (randepth == 0)
1499 randepth = 1;
1500 if (ranwidth == 0)
1501 ranwidth = 1;
1502 if (depth >= randepth)
1503 isleaf = 1;
1504
1505 /*
1506 * Create directory of random width filled with files according
1507 * to distribution, or if root directory, continue until #files required
1508 */
1509 for (i = 1; ((parent == NULL) || (i < ranwidth + 1)) &&
1510 (fileset->fs_realfiles < fileset->fs_constentries);
1511 i++) {
1512 int ret = 0;
1513
1514 if (parent && isleaf)
1515 ret = fileset_populate_file(fileset, entry, i);
1516 else
1517 ret = fileset_populate_subdir(fileset, entry, i, depth);
1518
1519 if (ret != 0)
1520 return (ret);
1521 }
1522
1523 /*
1524 * Create directory of random width filled with leaf directories
1525 * according to distribution, or if root directory, continue until
1526 * the number of leaf directories required has been generated.
1527 */
1528 for (i = 1; ((parent == NULL) || (i < ranwidth + 1)) &&
1529 (fileset->fs_realleafdirs < fileset->fs_constleafdirs);
1530 i++) {
1531 int ret = 0;
1532
1533 if (parent && isleaf)
1534 ret = fileset_populate_leafdir(fileset, entry, i);
1535 else
1536 ret = fileset_populate_subdir(fileset, entry, i, depth);
1537
1538 if (ret != 0)
1539 return (ret);
1540 }
1541
1542 return (FILEBENCH_OK);
1543 }
1544
1545 /*
1546 * Populates a fileset with files and subdirectory entries. Uses the supplied
1547 * fileset_dirwidth and fileset_entries (number of files) to calculate the
1548 * required fileset_meandepth (of subdirectories) and initialize the
1549 * fileset_meanwidth and fileset_meansize variables. Then calls
1550 * fileset_populate_subdir() to do the recursive subdirectory entry creation
1551 * and leaf file entry creation. All of the above is skipped if the fileset has
1552 * already been populated. Returns 0 on success, or an error code from the call
1553 * to fileset_populate_subdir if that call fails.
1554 */
1555 static int
fileset_populate(fileset_t * fileset)1556 fileset_populate(fileset_t *fileset)
1557 {
1558 fbint_t entries = avd_get_int(fileset->fs_entries);
1559 fbint_t leafdirs = avd_get_int(fileset->fs_leafdirs);
1560 int meandirwidth = 0;
1561 int ret;
1562
1563 /* Skip if already populated */
1564 if (fileset->fs_bytes > 0)
1565 goto exists;
1566
1567 /* check for raw device */
1568 if (fileset->fs_attrs & FILESET_IS_RAW_DEV)
1569 return (FILEBENCH_OK);
1570
1571 /*
1572 * save value of entries and leaf dirs obtained for later
1573 * in case it was random
1574 */
1575 fileset->fs_constentries = entries;
1576 fileset->fs_constleafdirs = leafdirs;
1577
1578 /* initialize idle files and directories condition variables */
1579 (void) pthread_cond_init(&fileset->fs_idle_files_cv, ipc_condattr());
1580 (void) pthread_cond_init(&fileset->fs_idle_dirs_cv, ipc_condattr());
1581 (void) pthread_cond_init(&fileset->fs_idle_leafdirs_cv, ipc_condattr());
1582
1583 /* no files or dirs idle (or busy) yet */
1584 fileset->fs_idle_files = 0;
1585 fileset->fs_idle_dirs = 0;
1586 fileset->fs_idle_leafdirs = 0;
1587
1588 /* initialize locks and other condition variables */
1589 (void) pthread_mutex_init(&fileset->fs_pick_lock,
1590 ipc_mutexattr(IPC_MUTEX_NORMAL));
1591 (void) pthread_mutex_init(&fileset->fs_histo_lock,
1592 ipc_mutexattr(IPC_MUTEX_NORMAL));
1593 (void) pthread_cond_init(&fileset->fs_thrd_wait_cv, ipc_condattr());
1594
1595 /* Initialize avl btrees */
1596 avl_create(&(fileset->fs_free_files), fileset_entry_compare,
1597 sizeof (filesetentry_t), FSE_OFFSETOF(fse_link));
1598 avl_create(&(fileset->fs_noex_files), fileset_entry_compare,
1599 sizeof (filesetentry_t), FSE_OFFSETOF(fse_link));
1600 avl_create(&(fileset->fs_exist_files), fileset_entry_compare,
1601 sizeof (filesetentry_t), FSE_OFFSETOF(fse_link));
1602 avl_create(&(fileset->fs_free_leaf_dirs), fileset_entry_compare,
1603 sizeof (filesetentry_t), FSE_OFFSETOF(fse_link));
1604 avl_create(&(fileset->fs_noex_leaf_dirs), fileset_entry_compare,
1605 sizeof (filesetentry_t), FSE_OFFSETOF(fse_link));
1606 avl_create(&(fileset->fs_exist_leaf_dirs), fileset_entry_compare,
1607 sizeof (filesetentry_t), FSE_OFFSETOF(fse_link));
1608 avl_create(&(fileset->fs_dirs), fileset_entry_compare,
1609 sizeof (filesetentry_t), FSE_OFFSETOF(fse_link));
1610
1611 /* is dirwidth a random variable? */
1612 if (AVD_IS_RANDOM(fileset->fs_dirwidth)) {
1613 meandirwidth =
1614 (int)fileset->fs_dirwidth->avd_val.randptr->rnd_dbl_mean;
1615 fileset->fs_meanwidth = -1;
1616 } else {
1617 meandirwidth = (int)avd_get_int(fileset->fs_dirwidth);
1618 fileset->fs_meanwidth = (double)meandirwidth;
1619 }
1620
1621 /*
1622 * Input params are:
1623 * # of files
1624 * ave # of files per dir
1625 * max size of dir
1626 * # ave size of file
1627 * max size of file
1628 */
1629 fileset->fs_meandepth = log(entries+leafdirs) / log(meandirwidth);
1630
1631 /* Has a random variable been supplied for dirdepth? */
1632 if (fileset->fs_dirdepthrv) {
1633 /* yes, so set the random variable's mean value to meandepth */
1634 fileset->fs_dirdepthrv->avd_val.randptr->rnd_dbl_mean =
1635 fileset->fs_meandepth;
1636 }
1637
1638 if ((ret = fileset_populate_subdir(fileset, NULL, 1, 0)) != 0)
1639 return (ret);
1640
1641 exists:
1642 if (fileset->fs_attrs & FILESET_IS_FILE) {
1643 filebench_log(LOG_VERBOSE, "File %s: %.3lfMB",
1644 avd_get_str(fileset->fs_name),
1645 (double)fileset->fs_bytes / 1024UL / 1024UL);
1646 } else {
1647 filebench_log(LOG_INFO, "%s populated: %llu files, "
1648 "avg. dir. width = %d, avg. dir. depth = %.1lf, "
1649 "%llu leafdirs, %.3lfMB total size",
1650 avd_get_str(fileset->fs_name), entries,
1651 meandirwidth, fileset->fs_meandepth,
1652 leafdirs,
1653 (double)fileset->fs_bytes / 1024UL / 1024UL);
1654 }
1655
1656 return FILEBENCH_OK;
1657 }
1658
1659 /*
1660 * Allocates a fileset instance, initializes fileset_dirgamma and
1661 * fileset_sizegamma default values, and sets the fileset name to the
1662 * supplied name string. Puts the allocated fileset on the
1663 * master fileset list and returns a pointer to it.
1664 *
1665 * This routine implements the 'define fileset' calls found in a .f
1666 * workload, such as in the following example:
1667 * define fileset name=drew4ever, entries=$nfiles
1668 */
1669 fileset_t *
fileset_define(avd_t name,avd_t path)1670 fileset_define(avd_t name, avd_t path)
1671 {
1672 fileset_t *fileset;
1673
1674 fileset = (fileset_t *)ipc_malloc(FILEBENCH_FILESET);
1675 if (!fileset) {
1676 filebench_log(LOG_ERROR, "can't allocate fileset %s",
1677 avd_get_str(name));
1678 return NULL;
1679 }
1680
1681 filebench_log(LOG_DEBUG_IMPL,
1682 "defining file[set] %s", avd_get_str(name));
1683
1684 fileset->fs_name = name;
1685 fileset->fs_path = path;
1686
1687 /* Add fileset to global list */
1688 (void)ipc_mutex_lock(&filebench_shm->shm_fileset_lock);
1689
1690 if (filebench_shm->shm_filesetlist == NULL) {
1691 filebench_shm->shm_filesetlist = fileset;
1692 fileset->fs_next = NULL;
1693 } else {
1694 fileset->fs_next = filebench_shm->shm_filesetlist;
1695 filebench_shm->shm_filesetlist = fileset;
1696 }
1697
1698 (void)ipc_mutex_unlock(&filebench_shm->shm_fileset_lock);
1699
1700 return fileset;
1701 }
1702
1703 /*
1704 * checks to see if the path/name pair points to a raw device. If
1705 * so it sets the raw device flag (FILESET_IS_RAW_DEV) and returns 1.
1706 * If RAW is not defined, or it is not a raw device, it clears the
1707 * raw device flag and returns 0.
1708 */
1709 int
fileset_checkraw(fileset_t * fileset)1710 fileset_checkraw(fileset_t *fileset)
1711 {
1712 char path[MAXPATHLEN];
1713 struct stat64 sb;
1714 char *pathname;
1715 char *setname;
1716
1717 fileset->fs_attrs &= (~FILESET_IS_RAW_DEV);
1718
1719 if ((pathname = avd_get_str(fileset->fs_path)) == NULL) {
1720 filebench_log(LOG_ERROR, "%s path not set",
1721 fileset_entity_name(fileset));
1722 filebench_shutdown(1);
1723 }
1724
1725 if ((setname = avd_get_str(fileset->fs_name)) == NULL) {
1726 filebench_log(LOG_ERROR, "%s name not set",
1727 fileset_entity_name(fileset));
1728 filebench_shutdown(1);
1729 }
1730
1731 (void) fb_strlcpy(path, pathname, MAXPATHLEN);
1732 (void) fb_strlcat(path, "/", MAXPATHLEN);
1733 (void) fb_strlcat(path, setname, MAXPATHLEN);
1734 if ((stat64(path, &sb) == 0) &&
1735 ((sb.st_mode & S_IFMT) == S_IFBLK)) {
1736 fileset->fs_attrs |= FILESET_IS_RAW_DEV;
1737 if (!(fileset->fs_attrs & FILESET_IS_FILE)) {
1738 filebench_log(LOG_ERROR,
1739 "WARNING Fileset %s/%s Cannot be RAW device",
1740 avd_get_str(fileset->fs_path),
1741 avd_get_str(fileset->fs_name));
1742 filebench_shutdown(1);
1743 }
1744 return 1;
1745 }
1746
1747 return 0;
1748 }
1749
1750 /*
1751 * Calls fileset_populate() and fileset_create() for all filesets on the
1752 * fileset list. Returns when any of fileset_populate() or fileset_create()
1753 * fail.
1754 */
1755 int
fileset_createsets()1756 fileset_createsets()
1757 {
1758 fileset_t *list;
1759 int ret = 0;
1760
1761 if (filecreate_done) {
1762 /* XXX: what if user defines a fileset after create?
1763 * IMHO, we should have filecreate_done flag per fileset */
1764 filebench_log(LOG_INFO,
1765 "Attempting to create fileset more than once, ignoring");
1766 return 0;
1767 }
1768
1769 filecreate_done = 1;
1770
1771 /* Set up for possible parallel pre-allocation */
1772 filebench_shm->shm_fsparalloc_count = 0;
1773 (void) pthread_cond_init(&filebench_shm->shm_fsparalloc_cv,
1774 ipc_condattr());
1775
1776 filebench_log(LOG_INFO, "Populating and pre-allocating filesets");
1777
1778 list = filebench_shm->shm_filesetlist;
1779 while (list) {
1780 /* Verify fileset parameters are valid */
1781 if ((avd_get_int(list->fs_entries) == 0) &&
1782 (avd_get_int(list->fs_leafdirs) == 0)) {
1783 filebench_log(LOG_ERROR, "Fileset has no files or leafdirs");
1784 }
1785
1786 if (list->fs_dirdepthrv && !AVD_IS_RANDOM(list->fs_dirdepthrv)) {
1787 filebench_log(LOG_ERROR,
1788 "Define fileset: dirdepthrv must be random var");
1789 filebench_shutdown(1);
1790 }
1791
1792 if (AVD_IS_RANDOM(list->fs_dirgamma)) {
1793 filebench_log(LOG_ERROR,
1794 "Define fileset: dirgamma attr cannot be random");
1795 filebench_shutdown(1);
1796 }
1797
1798 /* check for raw files */
1799 if (fileset_checkraw(list)) {
1800 filebench_log(LOG_INFO,
1801 "File %s/%s is a RAW device",
1802 avd_get_str(list->fs_path),
1803 avd_get_str(list->fs_name));
1804 list = list->fs_next;
1805 continue;
1806 }
1807
1808 ret = fileset_populate(list);
1809 if (ret)
1810 return ret;
1811
1812 ret = fileset_create(list);
1813 if (ret)
1814 return ret;
1815
1816 list = list->fs_next;
1817 }
1818
1819 /* wait for allocation threads to finish */
1820 filebench_log(LOG_INFO, "Waiting for pre-allocation to finish "
1821 "(in case of a parallel pre-allocation)");
1822
1823 (void) pthread_mutex_lock(&filebench_shm->shm_fsparalloc_lock);
1824 while (filebench_shm->shm_fsparalloc_count > 0)
1825 (void) pthread_cond_wait(
1826 &filebench_shm->shm_fsparalloc_cv,
1827 &filebench_shm->shm_fsparalloc_lock);
1828 (void) pthread_mutex_unlock(&filebench_shm->shm_fsparalloc_lock);
1829
1830 filebench_log(LOG_INFO,
1831 "Population and pre-allocation of filesets completed");
1832
1833 if (filebench_shm->shm_fsparalloc_count < 0)
1834 return (FILEBENCH_ERROR);
1835
1836 return 0;
1837 }
1838
1839 /*
1840 * Searches through the master fileset list for the named fileset.
1841 * If found, returns pointer to same, otherwise returns NULL.
1842 */
1843 fileset_t *
fileset_find(char * name)1844 fileset_find(char *name)
1845 {
1846 fileset_t *fileset = filebench_shm->shm_filesetlist;
1847
1848 (void) ipc_mutex_lock(&filebench_shm->shm_fileset_lock);
1849
1850 while (fileset) {
1851 if (strcmp(name, avd_get_str(fileset->fs_name)) == 0) {
1852 (void) ipc_mutex_unlock(
1853 &filebench_shm->shm_fileset_lock);
1854 return (fileset);
1855 }
1856 fileset = fileset->fs_next;
1857 }
1858 (void) ipc_mutex_unlock(&filebench_shm->shm_fileset_lock);
1859
1860 return (NULL);
1861 }
1862
1863 /*
1864 * Iterates over all the file sets in the filesetlist,
1865 * executing the supplied command "*cmd()" on them. Also
1866 * indicates to the executed command if it is the first
1867 * time the command has been executed since the current
1868 * call to fileset_iter.
1869 */
1870 int
fileset_iter(int (* cmd)(fileset_t * fileset,int first))1871 fileset_iter(int (*cmd)(fileset_t *fileset, int first))
1872 {
1873 fileset_t *fileset = filebench_shm->shm_filesetlist;
1874 int count = 0;
1875
1876 (void) ipc_mutex_lock(&filebench_shm->shm_fileset_lock);
1877
1878 while (fileset) {
1879 if (cmd(fileset, count == 0) == FILEBENCH_ERROR) {
1880 (void) ipc_mutex_unlock(
1881 &filebench_shm->shm_fileset_lock);
1882 return (FILEBENCH_ERROR);
1883 }
1884 fileset = fileset->fs_next;
1885 count++;
1886 }
1887
1888 (void) ipc_mutex_unlock(&filebench_shm->shm_fileset_lock);
1889 return (FILEBENCH_OK);
1890 }
1891
1892 /*
1893 * Prints information to the filebench log about the file
1894 * object. Also prints a header on the first call.
1895 */
1896 int
fileset_print(fileset_t * fileset,int first)1897 fileset_print(fileset_t *fileset, int first)
1898 {
1899 int pathlength;
1900 char *fileset_path;
1901 char *fileset_name;
1902 static char pad[] = " "; /* 30 spaces */
1903
1904 if ((fileset_path = avd_get_str(fileset->fs_path)) == NULL) {
1905 filebench_log(LOG_ERROR, "%s path not set",
1906 fileset_entity_name(fileset));
1907 return (FILEBENCH_ERROR);
1908 }
1909
1910 if ((fileset_name = avd_get_str(fileset->fs_name)) == NULL) {
1911 filebench_log(LOG_ERROR, "%s name not set",
1912 fileset_entity_name(fileset));
1913 return (FILEBENCH_ERROR);
1914 }
1915
1916 pathlength = strlen(fileset_path) + strlen(fileset_name);
1917
1918 if (pathlength > 29)
1919 pathlength = 29;
1920
1921 if (first) {
1922 filebench_log(LOG_INFO, "File or Fileset name%20s%12s%10s",
1923 "file size",
1924 "dir width",
1925 "entries");
1926 }
1927
1928 if (fileset->fs_attrs & FILESET_IS_FILE) {
1929 if (fileset->fs_attrs & FILESET_IS_RAW_DEV) {
1930 filebench_log(LOG_INFO,
1931 "%s/%s%s (Raw Device)",
1932 fileset_path, fileset_name, &pad[pathlength]);
1933 } else {
1934 filebench_log(LOG_INFO,
1935 "%s/%s%s%9llu (Single File)",
1936 fileset_path, fileset_name, &pad[pathlength],
1937 (u_longlong_t)avd_get_int(fileset->fs_size));
1938 }
1939 } else {
1940 filebench_log(LOG_INFO, "%s/%s%s%9llu%12llu%10llu",
1941 fileset_path, fileset_name,
1942 &pad[pathlength],
1943 (u_longlong_t)avd_get_int(fileset->fs_size),
1944 (u_longlong_t)avd_get_int(fileset->fs_dirwidth),
1945 (u_longlong_t)fileset->fs_constentries);
1946 }
1947 return (FILEBENCH_OK);
1948 }
1949