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