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 312 fileset = entry->fse_fileset; 313 (void) fb_strlcpy(path, avd_get_str(fileset->fs_path), MAXPATHLEN); 314 (void) fb_strlcat(path, "/", MAXPATHLEN); 315 (void) fb_strlcat(path, avd_get_str(fileset->fs_name), MAXPATHLEN); 316 pathtmp = fileset_resolvepath(entry); 317 (void) fb_strlcat(path, pathtmp, MAXPATHLEN); 318 free(pathtmp); 319 320 filebench_log(LOG_DEBUG_IMPL, "Populated %s", entry->fse_path); 321 322 /* see if reusing and this file exists */ 323 if ((entry->fse_flags & FSE_REUSING) && (FB_STAT(path, &sb) == 0)) { 324 if (FB_OPEN(&fdesc, path, O_RDWR, 0) == FILEBENCH_ERROR) { 325 filebench_log(LOG_INFO, 326 "Attempted but failed to Re-use file %s", 327 path); 328 fileset_unbusy(entry, TRUE, FALSE, 0); 329 return (FILEBENCH_ERROR); 330 } 331 332 if (sb.st_size == (off64_t)entry->fse_size) { 333 filebench_log(LOG_DEBUG_IMPL, 334 "Re-using file %s", path); 335 336 if (!avd_get_bool(fileset->fs_cached)) 337 (void) FB_FREEMEM(&fdesc, entry->fse_size); 338 339 (void) FB_CLOSE(&fdesc); 340 341 /* unbusy the allocated entry */ 342 fileset_unbusy(entry, TRUE, TRUE, 0); 343 return (FILEBENCH_OK); 344 345 } else if (sb.st_size > (off64_t)entry->fse_size) { 346 /* reuse, but too large */ 347 filebench_log(LOG_DEBUG_IMPL, 348 "Truncating & re-using file %s", path); 349 350 (void) FB_FTRUNC(&fdesc, (off64_t)entry->fse_size); 351 352 if (!avd_get_bool(fileset->fs_cached)) 353 (void) FB_FREEMEM(&fdesc, entry->fse_size); 354 355 (void) FB_CLOSE(&fdesc); 356 357 /* unbusy the allocated entry */ 358 fileset_unbusy(entry, TRUE, TRUE, 0); 359 return (FILEBENCH_OK); 360 } 361 } else { 362 363 /* No file or not reusing, so create */ 364 if (FB_OPEN(&fdesc, path, O_RDWR | O_CREAT, 0644) == 365 FILEBENCH_ERROR) { 366 filebench_log(LOG_ERROR, 367 "Failed to pre-allocate file %s: %s", 368 path, strerror(errno)); 369 370 /* unbusy the unallocated entry */ 371 fileset_unbusy(entry, TRUE, FALSE, 0); 372 return (FILEBENCH_ERROR); 373 } 374 } 375 376 if ((buf = (char *)malloc(FILE_ALLOC_BLOCK)) == NULL) { 377 /* unbusy the unallocated entry */ 378 fileset_unbusy(entry, TRUE, FALSE, 0); 379 return (FILEBENCH_ERROR); 380 } 381 382 for (seek = 0; seek < entry->fse_size; ) { 383 off64_t wsize; 384 int ret = 0; 385 386 /* 387 * Write FILE_ALLOC_BLOCK's worth, 388 * except on last write 389 */ 390 wsize = MIN(entry->fse_size - seek, FILE_ALLOC_BLOCK); 391 392 ret = FB_WRITE(&fdesc, buf, wsize); 393 if (ret != wsize) { 394 filebench_log(LOG_ERROR, 395 "Failed to pre-allocate file %s: %s", 396 path, strerror(errno)); 397 (void) FB_CLOSE(&fdesc); 398 free(buf); 399 fileset_unbusy(entry, TRUE, FALSE, 0); 400 return (FILEBENCH_ERROR); 401 } 402 seek += wsize; 403 } 404 405 if (!avd_get_bool(fileset->fs_cached)) 406 (void) FB_FREEMEM(&fdesc, entry->fse_size); 407 408 (void) FB_CLOSE(&fdesc); 409 410 free(buf); 411 412 /* unbusy the allocated entry */ 413 fileset_unbusy(entry, TRUE, TRUE, 0); 414 415 filebench_log(LOG_DEBUG_IMPL, 416 "Pre-allocated file %s size %llu", 417 path, (u_longlong_t)entry->fse_size); 418 419 return (FILEBENCH_OK); 420 } 421 422 /* 423 * given a fileset entry, determines if the associated file 424 * needs to be allocated or not, and if so does the allocation. 425 * Sets shm_fsparalloc_count to -1 on error. 426 */ 427 static void * 428 fileset_alloc_thread(filesetentry_t *entry) 429 { 430 if (fileset_alloc_file(entry) == FILEBENCH_ERROR) { 431 (void) pthread_mutex_lock(&filebench_shm->shm_fsparalloc_lock); 432 filebench_shm->shm_fsparalloc_count = -1; 433 } else { 434 (void) pthread_mutex_lock(&filebench_shm->shm_fsparalloc_lock); 435 filebench_shm->shm_fsparalloc_count--; 436 } 437 438 (void) pthread_cond_signal(&filebench_shm->shm_fsparalloc_cv); 439 (void) pthread_mutex_unlock(&filebench_shm->shm_fsparalloc_lock); 440 441 pthread_exit(NULL); 442 return (NULL); 443 } 444 445 446 /* 447 * First creates the parent directories of the file using 448 * fileset_mkdir(). Then Optionally sets the O_DSYNC flag 449 * and opens the file with open64(). It unlocks the fileset 450 * entry lock, sets the DIRECTIO_ON or DIRECTIO_OFF flags 451 * as requested, and returns the file descriptor integer 452 * for the opened file in the supplied filebench file descriptor. 453 * Returns FILEBENCH_ERROR on error, and FILEBENCH_OK on success. 454 */ 455 int 456 fileset_openfile(fb_fdesc_t *fdesc, fileset_t *fileset, 457 filesetentry_t *entry, int flag, int filemode, int attrs) 458 { 459 char path[MAXPATHLEN]; 460 char dir[MAXPATHLEN]; 461 char *pathtmp; 462 struct stat64 sb; 463 int open_attrs = 0; 464 465 (void) fb_strlcpy(path, avd_get_str(fileset->fs_path), MAXPATHLEN); 466 (void) fb_strlcat(path, "/", MAXPATHLEN); 467 (void) fb_strlcat(path, avd_get_str(fileset->fs_name), MAXPATHLEN); 468 pathtmp = fileset_resolvepath(entry); 469 (void) fb_strlcat(path, pathtmp, MAXPATHLEN); 470 (void) fb_strlcpy(dir, path, MAXPATHLEN); 471 free(pathtmp); 472 (void) trunc_dirname(dir); 473 474 /* If we are going to create a file, create the parent dirs */ 475 if ((flag & O_CREAT) && (stat64(dir, &sb) != 0)) { 476 if (fileset_mkdir(dir, 0755) == FILEBENCH_ERROR) 477 return (FILEBENCH_ERROR); 478 } 479 480 if (attrs & FLOW_ATTR_DSYNC) { 481 #ifdef sun 482 open_attrs |= O_DSYNC; 483 #else 484 open_attrs |= O_FSYNC; 485 #endif 486 } 487 488 if (FB_OPEN(fdesc, path, flag | open_attrs, filemode) 489 == FILEBENCH_ERROR) { 490 filebench_log(LOG_ERROR, 491 "Failed to open file %d, %s, with status %x: %s", 492 entry->fse_index, path, entry->fse_flags, strerror(errno)); 493 494 fileset_unbusy(entry, FALSE, FALSE, 0); 495 return (FILEBENCH_ERROR); 496 } 497 498 if (flag & O_CREAT) 499 fileset_unbusy(entry, TRUE, TRUE, 1); 500 else 501 fileset_unbusy(entry, FALSE, FALSE, 1); 502 503 #ifdef sun 504 if (attrs & FLOW_ATTR_DIRECTIO) 505 (void) directio(fdesc->fd_num, DIRECTIO_ON); 506 else 507 (void) directio(fdesc->fd_num, DIRECTIO_OFF); 508 #endif 509 510 return (FILEBENCH_OK); 511 } 512 513 /* 514 * removes all filesetentries from their respective btrees, and puts them 515 * on the free list. The supplied argument indicates which free list to 516 * use. 517 */ 518 static void 519 fileset_pickreset(fileset_t *fileset, int entry_type) 520 { 521 filesetentry_t *entry; 522 523 switch (entry_type & FILESET_PICKMASK) { 524 case FILESET_PICKFILE: 525 entry = (filesetentry_t *)avl_first(&fileset->fs_noex_files); 526 527 /* make sure non-existing files are marked free */ 528 while (entry) { 529 entry->fse_flags |= FSE_FREE; 530 entry->fse_open_cnt = 0; 531 fileset_move_entry(&fileset->fs_noex_files, 532 &fileset->fs_free_files, entry); 533 entry = AVL_NEXT(&fileset->fs_noex_files, entry); 534 } 535 536 /* free up any existing files */ 537 entry = (filesetentry_t *)avl_first(&fileset->fs_exist_files); 538 539 while (entry) { 540 entry->fse_flags |= FSE_FREE; 541 entry->fse_open_cnt = 0; 542 fileset_move_entry(&fileset->fs_exist_files, 543 &fileset->fs_free_files, entry); 544 545 entry = AVL_NEXT(&fileset->fs_exist_files, entry); 546 } 547 548 break; 549 550 case FILESET_PICKDIR: 551 /* nothing to reset, as all (sub)dirs always exist */ 552 break; 553 554 case FILESET_PICKLEAFDIR: 555 entry = (filesetentry_t *) 556 avl_first(&fileset->fs_noex_leaf_dirs); 557 558 /* make sure non-existing leaf dirs are marked free */ 559 while (entry) { 560 entry->fse_flags |= FSE_FREE; 561 entry->fse_open_cnt = 0; 562 fileset_move_entry(&fileset->fs_noex_leaf_dirs, 563 &fileset->fs_free_leaf_dirs, entry); 564 entry = AVL_NEXT(&fileset->fs_noex_leaf_dirs, entry); 565 } 566 567 /* free up any existing leaf dirs */ 568 entry = (filesetentry_t *) 569 avl_first(&fileset->fs_exist_leaf_dirs); 570 571 while (entry) { 572 entry->fse_flags |= FSE_FREE; 573 entry->fse_open_cnt = 0; 574 fileset_move_entry(&fileset->fs_exist_leaf_dirs, 575 &fileset->fs_free_leaf_dirs, entry); 576 577 entry = AVL_NEXT(&fileset->fs_exist_leaf_dirs, entry); 578 } 579 580 break; 581 } 582 } 583 584 /* 585 * find a filesetentry from the fileset using the supplied index 586 */ 587 static filesetentry_t * 588 fileset_find_entry(avl_tree_t *atp, uint_t index) 589 { 590 avl_index_t found_loc; 591 filesetentry_t desired_fse, *found_fse; 592 593 /* find the file with the desired index, if it is in the tree */ 594 desired_fse.fse_index = index; 595 found_fse = avl_find(atp, (void *)(&desired_fse), &found_loc); 596 if (found_fse != NULL) 597 return (found_fse); 598 599 /* if requested node not found, find next higher node */ 600 found_fse = avl_nearest(atp, found_loc, AVL_AFTER); 601 if (found_fse != NULL) 602 return (found_fse); 603 604 /* might have hit the end, return lowest available index node */ 605 found_fse = avl_first(atp); 606 return (found_fse); 607 } 608 609 /* 610 * Selects a fileset entry from a fileset. If the 611 * FILESET_PICKLEAFDIR flag is set it will pick a leaf directory entry, 612 * if the FILESET_PICKDIR flag is set it will pick a non leaf directory 613 * entry, otherwise a file entry. The FILESET_PICKUNIQUE 614 * flag will take an entry off of one of the free (unused) 615 * lists (file or directory), otherwise the entry will be 616 * picked off of one of the rotor lists (file or directory). 617 * The FILESET_PICKEXISTS will insure that only extant 618 * (FSE_EXISTS) state files are selected, while 619 * FILESET_PICKNOEXIST insures that only non extant 620 * (not FSE_EXISTS) state files are selected. 621 * Note that the selected fileset entry (file) is returned 622 * with its FSE_BUSY flag (in fse_flags) set. 623 */ 624 filesetentry_t * 625 fileset_pick(fileset_t *fileset, int flags, int tid, int index) 626 { 627 filesetentry_t *entry = NULL; 628 filesetentry_t *start_point; 629 avl_tree_t *atp; 630 fbint_t max_entries; 631 632 (void) ipc_mutex_lock(&fileset->fs_pick_lock); 633 634 /* see if we have to wait for available files or directories */ 635 switch (flags & FILESET_PICKMASK) { 636 case FILESET_PICKFILE: 637 if (fileset->fs_filelist == NULL) 638 goto empty; 639 640 while (fileset->fs_idle_files == 0) { 641 (void) pthread_cond_wait(&fileset->fs_idle_files_cv, 642 &fileset->fs_pick_lock); 643 } 644 645 max_entries = fileset->fs_constentries; 646 if (flags & FILESET_PICKUNIQUE) { 647 atp = &fileset->fs_free_files; 648 } else if (flags & FILESET_PICKNOEXIST) { 649 atp = &fileset->fs_noex_files; 650 } else { 651 atp = &fileset->fs_exist_files; 652 } 653 break; 654 655 case FILESET_PICKDIR: 656 if (fileset->fs_dirlist == NULL) 657 goto empty; 658 659 while (fileset->fs_idle_dirs == 0) { 660 (void) pthread_cond_wait(&fileset->fs_idle_dirs_cv, 661 &fileset->fs_pick_lock); 662 } 663 664 max_entries = 1; 665 atp = &fileset->fs_dirs; 666 break; 667 668 case FILESET_PICKLEAFDIR: 669 if (fileset->fs_leafdirlist == NULL) 670 goto empty; 671 672 while (fileset->fs_idle_leafdirs == 0) { 673 (void) pthread_cond_wait(&fileset->fs_idle_leafdirs_cv, 674 &fileset->fs_pick_lock); 675 } 676 677 max_entries = fileset->fs_constleafdirs; 678 if (flags & FILESET_PICKUNIQUE) { 679 atp = &fileset->fs_free_leaf_dirs; 680 } else if (flags & FILESET_PICKNOEXIST) { 681 atp = &fileset->fs_noex_leaf_dirs; 682 } else { 683 atp = &fileset->fs_exist_leaf_dirs; 684 } 685 break; 686 } 687 688 /* see if asking for impossible */ 689 if (avl_is_empty(atp)) 690 goto empty; 691 692 if (flags & FILESET_PICKUNIQUE) { 693 uint64_t index64; 694 695 /* 696 * pick at random from free list in order to 697 * distribute initially allocated files more 698 * randomly on storage media. Use uniform 699 * random number generator to select index 700 * if it is not supplied with pick call. 701 */ 702 if (index) { 703 index64 = index; 704 } else { 705 if (filebench_randomno64(&index64, max_entries, 1, 706 NULL) == FILEBENCH_ERROR) 707 return (NULL); 708 } 709 710 entry = fileset_find_entry(atp, (int)index64); 711 712 if (entry == NULL) 713 goto empty; 714 715 } else if (flags & FILESET_PICKBYINDEX) { 716 /* pick by supplied index */ 717 entry = fileset_find_entry(atp, index); 718 719 } else { 720 /* pick in rotation */ 721 switch (flags & FILESET_PICKMASK) { 722 case FILESET_PICKFILE: 723 if (flags & FILESET_PICKNOEXIST) { 724 entry = fileset_find_entry(atp, 725 fileset->fs_file_nerotor); 726 fileset->fs_file_nerotor = 727 entry->fse_index + 1; 728 } else { 729 entry = fileset_find_entry(atp, 730 fileset->fs_file_exrotor[tid]); 731 fileset->fs_file_exrotor[tid] = 732 entry->fse_index + 1; 733 } 734 break; 735 736 case FILESET_PICKDIR: 737 entry = fileset_find_entry(atp, fileset->fs_dirrotor); 738 fileset->fs_dirrotor = entry->fse_index + 1; 739 break; 740 741 case FILESET_PICKLEAFDIR: 742 if (flags & FILESET_PICKNOEXIST) { 743 entry = fileset_find_entry(atp, 744 fileset->fs_leafdir_nerotor); 745 fileset->fs_leafdir_nerotor = 746 entry->fse_index + 1; 747 } else { 748 entry = fileset_find_entry(atp, 749 fileset->fs_leafdir_exrotor); 750 fileset->fs_leafdir_exrotor = 751 entry->fse_index + 1; 752 } 753 break; 754 } 755 } 756 757 if (entry == NULL) 758 goto empty; 759 760 /* see if entry in use */ 761 start_point = entry; 762 while (entry->fse_flags & FSE_BUSY) { 763 764 /* it is, so try next */ 765 entry = AVL_NEXT(atp, entry); 766 if (entry == NULL) 767 entry = avl_first(atp); 768 769 /* see if we have wrapped around */ 770 if ((entry == NULL) || (entry == start_point)) { 771 filebench_log(LOG_DEBUG_SCRIPT, 772 "All %d files are busy", avl_numnodes(atp)); 773 goto empty; 774 } 775 776 } 777 778 /* update file or directory idle counts */ 779 switch (flags & FILESET_PICKMASK) { 780 case FILESET_PICKFILE: 781 fileset->fs_idle_files--; 782 break; 783 case FILESET_PICKDIR: 784 fileset->fs_idle_dirs--; 785 break; 786 case FILESET_PICKLEAFDIR: 787 fileset->fs_idle_leafdirs--; 788 break; 789 } 790 791 /* Indicate that file or directory is now busy */ 792 entry->fse_flags |= FSE_BUSY; 793 794 (void) ipc_mutex_unlock(&fileset->fs_pick_lock); 795 filebench_log(LOG_DEBUG_SCRIPT, "Picked file %s", entry->fse_path); 796 return (entry); 797 798 empty: 799 filebench_log(LOG_DEBUG_SCRIPT, "No file found"); 800 (void) ipc_mutex_unlock(&fileset->fs_pick_lock); 801 return (NULL); 802 } 803 804 /* 805 * Removes a filesetentry from the "FSE_BUSY" state, signaling any threads 806 * that are waiting for a NOT BUSY filesetentry. Also sets whether it is 807 * existant or not, or leaves that designation alone. 808 */ 809 void 810 fileset_unbusy(filesetentry_t *entry, int update_exist, 811 int new_exist_val, int open_cnt_incr) 812 { 813 fileset_t *fileset = NULL; 814 815 if (entry) 816 fileset = entry->fse_fileset; 817 818 if (fileset == NULL) { 819 filebench_log(LOG_ERROR, "fileset_unbusy: NO FILESET!"); 820 return; 821 } 822 823 (void) ipc_mutex_lock(&fileset->fs_pick_lock); 824 825 /* modify FSE_EXIST flag and actual dirs/files count, if requested */ 826 if (update_exist) { 827 if (new_exist_val == TRUE) { 828 if (entry->fse_flags & FSE_FREE) { 829 830 /* asked to set and it was free */ 831 entry->fse_flags |= FSE_EXISTS; 832 entry->fse_flags &= (~FSE_FREE); 833 switch (entry->fse_flags & FSE_TYPE_MASK) { 834 case FSE_TYPE_FILE: 835 fileset_move_entry( 836 &fileset->fs_free_files, 837 &fileset->fs_exist_files, entry); 838 break; 839 840 case FSE_TYPE_DIR: 841 break; 842 843 case FSE_TYPE_LEAFDIR: 844 fileset_move_entry( 845 &fileset->fs_free_leaf_dirs, 846 &fileset->fs_exist_leaf_dirs, 847 entry); 848 break; 849 } 850 851 } else if (!(entry->fse_flags & FSE_EXISTS)) { 852 853 /* asked to set, and it was clear */ 854 entry->fse_flags |= FSE_EXISTS; 855 switch (entry->fse_flags & FSE_TYPE_MASK) { 856 case FSE_TYPE_FILE: 857 fileset_move_entry( 858 &fileset->fs_noex_files, 859 &fileset->fs_exist_files, entry); 860 break; 861 case FSE_TYPE_DIR: 862 break; 863 case FSE_TYPE_LEAFDIR: 864 fileset_move_entry( 865 &fileset->fs_noex_leaf_dirs, 866 &fileset->fs_exist_leaf_dirs, 867 entry); 868 break; 869 } 870 } 871 } else { 872 if (entry->fse_flags & FSE_FREE) { 873 /* asked to clear, and it was free */ 874 entry->fse_flags &= (~(FSE_FREE | FSE_EXISTS)); 875 switch (entry->fse_flags & FSE_TYPE_MASK) { 876 case FSE_TYPE_FILE: 877 fileset_move_entry( 878 &fileset->fs_free_files, 879 &fileset->fs_noex_files, entry); 880 break; 881 882 case FSE_TYPE_DIR: 883 break; 884 885 case FSE_TYPE_LEAFDIR: 886 fileset_move_entry( 887 &fileset->fs_free_leaf_dirs, 888 &fileset->fs_noex_leaf_dirs, 889 entry); 890 break; 891 } 892 } else if (entry->fse_flags & FSE_EXISTS) { 893 894 /* asked to clear, and it was set */ 895 entry->fse_flags &= (~FSE_EXISTS); 896 switch (entry->fse_flags & FSE_TYPE_MASK) { 897 case FSE_TYPE_FILE: 898 fileset_move_entry( 899 &fileset->fs_exist_files, 900 &fileset->fs_noex_files, entry); 901 break; 902 case FSE_TYPE_DIR: 903 break; 904 case FSE_TYPE_LEAFDIR: 905 fileset_move_entry( 906 &fileset->fs_exist_leaf_dirs, 907 &fileset->fs_noex_leaf_dirs, 908 entry); 909 break; 910 } 911 } 912 } 913 } 914 915 /* update open count */ 916 entry->fse_open_cnt += open_cnt_incr; 917 918 /* increment idle count, clear FSE_BUSY and signal IF it was busy */ 919 if (entry->fse_flags & FSE_BUSY) { 920 921 /* unbusy it */ 922 entry->fse_flags &= (~FSE_BUSY); 923 924 /* release any threads waiting for unbusy */ 925 if (entry->fse_flags & FSE_THRD_WAITNG) { 926 entry->fse_flags &= (~FSE_THRD_WAITNG); 927 (void) pthread_cond_broadcast( 928 &fileset->fs_thrd_wait_cv); 929 } 930 931 /* increment idle count and signal waiting threads */ 932 switch (entry->fse_flags & FSE_TYPE_MASK) { 933 case FSE_TYPE_FILE: 934 fileset->fs_idle_files++; 935 if (fileset->fs_idle_files == 1) { 936 (void) pthread_cond_signal( 937 &fileset->fs_idle_files_cv); 938 } 939 break; 940 941 case FSE_TYPE_DIR: 942 fileset->fs_idle_dirs++; 943 if (fileset->fs_idle_dirs == 1) { 944 (void) pthread_cond_signal( 945 &fileset->fs_idle_dirs_cv); 946 } 947 break; 948 949 case FSE_TYPE_LEAFDIR: 950 fileset->fs_idle_leafdirs++; 951 if (fileset->fs_idle_leafdirs == 1) { 952 (void) pthread_cond_signal( 953 &fileset->fs_idle_leafdirs_cv); 954 } 955 break; 956 } 957 } 958 959 (void) ipc_mutex_unlock(&fileset->fs_pick_lock); 960 } 961 962 /* 963 * Given a fileset "fileset", create the associated files as 964 * specified in the attributes of the fileset. The fileset is 965 * rooted in a directory whose pathname is in fileset_path. If the 966 * directory exists, meaning that there is already a fileset, 967 * and the fileset_reuse attribute is false, then remove it and all 968 * its contained files and subdirectories. Next, the routine 969 * creates a root directory for the fileset. All the file type 970 * filesetentries are cycled through creating as needed 971 * their containing subdirectory trees in the filesystem and 972 * creating actual files for fileset_preallocpercent of them. The 973 * created files are filled with fse_size bytes of unitialized 974 * data. The routine returns FILEBENCH_ERROR on errors, 975 * FILEBENCH_OK on success. 976 */ 977 static int 978 fileset_create(fileset_t *fileset) 979 { 980 filesetentry_t *entry; 981 char path[MAXPATHLEN]; 982 struct stat64 sb; 983 hrtime_t start = gethrtime(); 984 char *fileset_path; 985 char *fileset_name; 986 int randno; 987 int preallocated = 0; 988 int reusing; 989 990 if ((fileset_path = avd_get_str(fileset->fs_path)) == NULL) { 991 filebench_log(LOG_ERROR, "%s path not set", 992 fileset_entity_name(fileset)); 993 return (FILEBENCH_ERROR); 994 } 995 996 if ((fileset_name = avd_get_str(fileset->fs_name)) == NULL) { 997 filebench_log(LOG_ERROR, "%s name not set", 998 fileset_entity_name(fileset)); 999 return (FILEBENCH_ERROR); 1000 } 1001 1002 #ifdef HAVE_RAW_SUPPORT 1003 /* treat raw device as special case */ 1004 if (fileset->fs_attrs & FILESET_IS_RAW_DEV) 1005 return (FILEBENCH_OK); 1006 #endif /* HAVE_RAW_SUPPORT */ 1007 1008 /* XXX Add check to see if there is enough space */ 1009 1010 /* set up path to fileset */ 1011 (void) fb_strlcpy(path, fileset_path, MAXPATHLEN); 1012 (void) fb_strlcat(path, "/", MAXPATHLEN); 1013 (void) fb_strlcat(path, fileset_name, MAXPATHLEN); 1014 1015 /* if exists and resusing, then don't create new */ 1016 if (((stat64(path, &sb) == 0)&& (strlen(path) > 3) && 1017 (strlen(avd_get_str(fileset->fs_path)) > 2)) && 1018 avd_get_bool(fileset->fs_reuse)) { 1019 reusing = 1; 1020 } else { 1021 reusing = 0; 1022 } 1023 1024 if (!reusing) { 1025 char cmd[MAXPATHLEN]; 1026 1027 /* Remove existing */ 1028 (void) snprintf(cmd, sizeof (cmd), "rm -rf %s", path); 1029 (void) system(cmd); 1030 filebench_log(LOG_VERBOSE, 1031 "Removed any existing %s %s in %llu seconds", 1032 fileset_entity_name(fileset), fileset_name, 1033 (u_longlong_t)(((gethrtime() - start) / 1034 1000000000) + 1)); 1035 } else { 1036 /* we are re-using */ 1037 filebench_log(LOG_VERBOSE, "Re-using %s %s.", 1038 fileset_entity_name(fileset), fileset_name); 1039 } 1040 1041 /* make the filesets directory tree unless in reuse mode */ 1042 if (!reusing && (avd_get_bool(fileset->fs_prealloc))) { 1043 filebench_log(LOG_VERBOSE, 1044 "making tree for filset %s", path); 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_VERBOSE, "Creating %s %s...", 1055 fileset_entity_name(fileset), fileset_name); 1056 1057 randno = ((RAND_MAX * (100 1058 - avd_get_int(fileset->fs_preallocpercent))) / 100); 1059 1060 /* alloc any files, as required */ 1061 fileset_pickreset(fileset, FILESET_PICKFILE); 1062 while (entry = fileset_pick(fileset, 1063 FILESET_PICKFREE | FILESET_PICKFILE, 0, 0)) { 1064 pthread_t tid; 1065 int newrand; 1066 1067 newrand = rand(); 1068 1069 if (newrand < randno) { 1070 /* unbusy the unallocated entry */ 1071 fileset_unbusy(entry, TRUE, FALSE, 0); 1072 continue; 1073 } 1074 1075 preallocated++; 1076 1077 if (reusing) 1078 entry->fse_flags |= FSE_REUSING; 1079 else 1080 entry->fse_flags &= (~FSE_REUSING); 1081 1082 /* fire off allocation threads for each file if paralloc set */ 1083 if (avd_get_bool(fileset->fs_paralloc)) { 1084 1085 /* limit total number of simultaneous allocations */ 1086 (void) pthread_mutex_lock( 1087 &filebench_shm->shm_fsparalloc_lock); 1088 while (filebench_shm->shm_fsparalloc_count 1089 >= MAX_PARALLOC_THREADS) { 1090 (void) pthread_cond_wait( 1091 &filebench_shm->shm_fsparalloc_cv, 1092 &filebench_shm->shm_fsparalloc_lock); 1093 } 1094 1095 /* quit if any allocation thread reports an error */ 1096 if (filebench_shm->shm_fsparalloc_count < 0) { 1097 (void) pthread_mutex_unlock( 1098 &filebench_shm->shm_fsparalloc_lock); 1099 return (FILEBENCH_ERROR); 1100 } 1101 1102 filebench_shm->shm_fsparalloc_count++; 1103 (void) pthread_mutex_unlock( 1104 &filebench_shm->shm_fsparalloc_lock); 1105 1106 /* 1107 * Fire off a detached allocation thread per file. 1108 * The thread will self destruct when it finishes 1109 * writing pre-allocation data to the file. 1110 */ 1111 if (pthread_create(&tid, NULL, 1112 (void *(*)(void*))fileset_alloc_thread, 1113 entry) == 0) { 1114 /* 1115 * A thread was created; detach it so it can 1116 * fully quit when finished. 1117 */ 1118 (void) pthread_detach(tid); 1119 } else { 1120 filebench_log(LOG_ERROR, 1121 "File prealloc thread create failed"); 1122 filebench_shutdown(1); 1123 } 1124 1125 } else { 1126 if (fileset_alloc_file(entry) == FILEBENCH_ERROR) 1127 return (FILEBENCH_ERROR); 1128 } 1129 } 1130 1131 /* alloc any leaf directories, as required */ 1132 fileset_pickreset(fileset, FILESET_PICKLEAFDIR); 1133 while (entry = fileset_pick(fileset, 1134 FILESET_PICKFREE | FILESET_PICKLEAFDIR, 0, 0)) { 1135 1136 if (rand() < randno) { 1137 /* unbusy the unallocated entry */ 1138 fileset_unbusy(entry, TRUE, FALSE, 0); 1139 continue; 1140 } 1141 1142 preallocated++; 1143 1144 if (reusing) 1145 entry->fse_flags |= FSE_REUSING; 1146 else 1147 entry->fse_flags &= (~FSE_REUSING); 1148 1149 if (fileset_alloc_leafdir(entry) == FILEBENCH_ERROR) 1150 return (FILEBENCH_ERROR); 1151 } 1152 1153 exit: 1154 filebench_log(LOG_VERBOSE, 1155 "Preallocated %d of %llu of %s %s in %llu seconds", 1156 preallocated, 1157 (u_longlong_t)fileset->fs_constentries, 1158 fileset_entity_name(fileset), fileset_name, 1159 (u_longlong_t)(((gethrtime() - start) / 1000000000) + 1)); 1160 1161 return (FILEBENCH_OK); 1162 } 1163 1164 /* 1165 * Adds an entry to the fileset's file list. Single threaded so 1166 * no locking needed. 1167 */ 1168 static void 1169 fileset_insfilelist(fileset_t *fileset, filesetentry_t *entry) 1170 { 1171 entry->fse_flags = FSE_TYPE_FILE | FSE_FREE; 1172 avl_add(&fileset->fs_free_files, entry); 1173 1174 if (fileset->fs_filelist == NULL) { 1175 fileset->fs_filelist = entry; 1176 entry->fse_nextoftype = NULL; 1177 } else { 1178 entry->fse_nextoftype = fileset->fs_filelist; 1179 fileset->fs_filelist = entry; 1180 } 1181 } 1182 1183 /* 1184 * Adds an entry to the fileset's directory list. Single 1185 * threaded so no locking needed. 1186 */ 1187 static void 1188 fileset_insdirlist(fileset_t *fileset, filesetentry_t *entry) 1189 { 1190 entry->fse_flags = FSE_TYPE_DIR | FSE_EXISTS; 1191 avl_add(&fileset->fs_dirs, entry); 1192 1193 if (fileset->fs_dirlist == NULL) { 1194 fileset->fs_dirlist = entry; 1195 entry->fse_nextoftype = NULL; 1196 } else { 1197 entry->fse_nextoftype = fileset->fs_dirlist; 1198 fileset->fs_dirlist = entry; 1199 } 1200 } 1201 1202 /* 1203 * Adds an entry to the fileset's leaf directory list. Single 1204 * threaded so no locking needed. 1205 */ 1206 static void 1207 fileset_insleafdirlist(fileset_t *fileset, filesetentry_t *entry) 1208 { 1209 entry->fse_flags = FSE_TYPE_LEAFDIR | FSE_FREE; 1210 avl_add(&fileset->fs_free_leaf_dirs, entry); 1211 1212 if (fileset->fs_leafdirlist == NULL) { 1213 fileset->fs_leafdirlist = entry; 1214 entry->fse_nextoftype = NULL; 1215 } else { 1216 entry->fse_nextoftype = fileset->fs_leafdirlist; 1217 fileset->fs_leafdirlist = entry; 1218 } 1219 } 1220 1221 /* 1222 * Compares two fileset entries to determine their relative order 1223 */ 1224 static int 1225 fileset_entry_compare(const void *node_1, const void *node_2) 1226 { 1227 if (((filesetentry_t *)node_1)->fse_index < 1228 ((filesetentry_t *)node_2)->fse_index) 1229 return (-1); 1230 1231 if (((filesetentry_t *)node_1)->fse_index == 1232 ((filesetentry_t *)node_2)->fse_index) 1233 return (0); 1234 1235 return (1); 1236 } 1237 1238 /* 1239 * Obtains a filesetentry entity for a file to be placed in a 1240 * (sub)directory of a fileset. The size of the file may be 1241 * specified by fileset_meansize, or calculated from a gamma 1242 * distribution of parameter fileset_sizegamma and of mean size 1243 * fileset_meansize. The filesetentry entity is placed on the file 1244 * list in the specified parent filesetentry entity, which may 1245 * be a directory filesetentry, or the root filesetentry in the 1246 * fileset. It is also placed on the fileset's list of all 1247 * contained files. Returns FILEBENCH_OK if successful or FILEBENCH_ERROR 1248 * if ipc memory for the path string cannot be allocated. 1249 */ 1250 static int 1251 fileset_populate_file(fileset_t *fileset, filesetentry_t *parent, int serial) 1252 { 1253 char tmpname[16]; 1254 filesetentry_t *entry; 1255 double drand; 1256 uint_t index; 1257 1258 if ((entry = (filesetentry_t *)ipc_malloc(FILEBENCH_FILESETENTRY)) 1259 == NULL) { 1260 filebench_log(LOG_ERROR, 1261 "fileset_populate_file: Can't malloc filesetentry"); 1262 return (FILEBENCH_ERROR); 1263 } 1264 1265 /* Another currently idle file */ 1266 (void) ipc_mutex_lock(&fileset->fs_pick_lock); 1267 index = fileset->fs_idle_files++; 1268 (void) ipc_mutex_unlock(&fileset->fs_pick_lock); 1269 1270 entry->fse_index = index; 1271 entry->fse_parent = parent; 1272 entry->fse_fileset = fileset; 1273 fileset_insfilelist(fileset, entry); 1274 1275 (void) snprintf(tmpname, sizeof (tmpname), "%08d", serial); 1276 if ((entry->fse_path = (char *)ipc_pathalloc(tmpname)) == NULL) { 1277 filebench_log(LOG_ERROR, 1278 "fileset_populate_file: Can't alloc path string"); 1279 return (FILEBENCH_ERROR); 1280 } 1281 1282 /* see if random variable was supplied for file size */ 1283 if (fileset->fs_meansize == -1) { 1284 entry->fse_size = (off64_t)avd_get_int(fileset->fs_size); 1285 } else { 1286 double gamma; 1287 1288 gamma = avd_get_int(fileset->fs_sizegamma) / 1000.0; 1289 if (gamma > 0) { 1290 drand = gamma_dist_knuth(gamma, 1291 fileset->fs_meansize / gamma); 1292 entry->fse_size = (off64_t)drand; 1293 } else { 1294 entry->fse_size = (off64_t)fileset->fs_meansize; 1295 } 1296 } 1297 1298 fileset->fs_bytes += entry->fse_size; 1299 1300 fileset->fs_realfiles++; 1301 return (FILEBENCH_OK); 1302 } 1303 1304 /* 1305 * Obtaines a filesetentry entity for a leaf directory to be placed in a 1306 * (sub)directory of a fileset. The leaf directory will always be empty so 1307 * it can be created and deleted (mkdir, rmdir) at will. The filesetentry 1308 * entity is placed on the leaf directory list in the specified parent 1309 * filesetentry entity, which may be a (sub) directory filesetentry, or 1310 * the root filesetentry in the fileset. It is also placed on the fileset's 1311 * list of all contained leaf directories. Returns FILEBENCH_OK if successful 1312 * or FILEBENCH_ERROR if ipc memory cannot be allocated. 1313 */ 1314 static int 1315 fileset_populate_leafdir(fileset_t *fileset, filesetentry_t *parent, int serial) 1316 { 1317 char tmpname[16]; 1318 filesetentry_t *entry; 1319 uint_t index; 1320 1321 if ((entry = (filesetentry_t *)ipc_malloc(FILEBENCH_FILESETENTRY)) 1322 == NULL) { 1323 filebench_log(LOG_ERROR, 1324 "fileset_populate_file: Can't malloc filesetentry"); 1325 return (FILEBENCH_ERROR); 1326 } 1327 1328 /* Another currently idle leaf directory */ 1329 (void) ipc_mutex_lock(&fileset->fs_pick_lock); 1330 index = fileset->fs_idle_leafdirs++; 1331 (void) ipc_mutex_unlock(&fileset->fs_pick_lock); 1332 1333 entry->fse_index = index; 1334 entry->fse_parent = parent; 1335 entry->fse_fileset = fileset; 1336 fileset_insleafdirlist(fileset, entry); 1337 1338 (void) snprintf(tmpname, sizeof (tmpname), "%08d", serial); 1339 if ((entry->fse_path = (char *)ipc_pathalloc(tmpname)) == NULL) { 1340 filebench_log(LOG_ERROR, 1341 "fileset_populate_file: Can't alloc path string"); 1342 return (FILEBENCH_ERROR); 1343 } 1344 1345 fileset->fs_realleafdirs++; 1346 return (FILEBENCH_OK); 1347 } 1348 1349 /* 1350 * Creates a directory node in a fileset, by obtaining a 1351 * filesetentry entity for the node and initializing it 1352 * according to parameters of the fileset. It determines a 1353 * directory tree depth and directory width, optionally using 1354 * a gamma distribution. If its calculated depth is less then 1355 * its actual depth in the directory tree, it becomes a leaf 1356 * node and files itself with "width" number of file type 1357 * filesetentries, otherwise it files itself with "width" 1358 * number of directory type filesetentries, using recursive 1359 * calls to fileset_populate_subdir. The end result of the 1360 * initial call to this routine is a tree of directories of 1361 * random width and varying depth with sufficient leaf 1362 * directories to contain all required files. 1363 * Returns FILEBENCH_OK on success. Returns FILEBENCH_ERROR if ipc path 1364 * string memory cannot be allocated and returns the error code (currently 1365 * also FILEBENCH_ERROR) from calls to fileset_populate_file or recursive 1366 * calls to fileset_populate_subdir. 1367 */ 1368 static int 1369 fileset_populate_subdir(fileset_t *fileset, filesetentry_t *parent, 1370 int serial, double depth) 1371 { 1372 double randepth, drand, ranwidth; 1373 int isleaf = 0; 1374 char tmpname[16]; 1375 filesetentry_t *entry; 1376 int i; 1377 uint_t index; 1378 1379 depth += 1; 1380 1381 /* Create dir node */ 1382 if ((entry = (filesetentry_t *)ipc_malloc(FILEBENCH_FILESETENTRY)) 1383 == NULL) { 1384 filebench_log(LOG_ERROR, 1385 "fileset_populate_subdir: Can't malloc filesetentry"); 1386 return (FILEBENCH_ERROR); 1387 } 1388 1389 /* another idle directory */ 1390 (void) ipc_mutex_lock(&fileset->fs_pick_lock); 1391 index = fileset->fs_idle_dirs++; 1392 (void) ipc_mutex_unlock(&fileset->fs_pick_lock); 1393 1394 (void) snprintf(tmpname, sizeof (tmpname), "%08d", serial); 1395 if ((entry->fse_path = (char *)ipc_pathalloc(tmpname)) == NULL) { 1396 filebench_log(LOG_ERROR, 1397 "fileset_populate_subdir: Can't alloc path string"); 1398 return (FILEBENCH_ERROR); 1399 } 1400 1401 entry->fse_index = index; 1402 entry->fse_parent = parent; 1403 entry->fse_fileset = fileset; 1404 fileset_insdirlist(fileset, entry); 1405 1406 if (fileset->fs_dirdepthrv) { 1407 randepth = (int)avd_get_int(fileset->fs_dirdepthrv); 1408 } else { 1409 double gamma; 1410 1411 gamma = avd_get_int(fileset->fs_dirgamma) / 1000.0; 1412 if (gamma > 0) { 1413 drand = gamma_dist_knuth(gamma, 1414 fileset->fs_meandepth / gamma); 1415 randepth = (int)drand; 1416 } else { 1417 randepth = (int)fileset->fs_meandepth; 1418 } 1419 } 1420 1421 if (fileset->fs_meanwidth == -1) { 1422 ranwidth = avd_get_dbl(fileset->fs_dirwidth); 1423 } else { 1424 double gamma; 1425 1426 gamma = avd_get_int(fileset->fs_sizegamma) / 1000.0; 1427 if (gamma > 0) { 1428 drand = gamma_dist_knuth(gamma, 1429 fileset->fs_meanwidth / gamma); 1430 ranwidth = drand; 1431 } else { 1432 ranwidth = fileset->fs_meanwidth; 1433 } 1434 } 1435 1436 if (randepth == 0) 1437 randepth = 1; 1438 if (ranwidth == 0) 1439 ranwidth = 1; 1440 if (depth >= randepth) 1441 isleaf = 1; 1442 1443 /* 1444 * Create directory of random width filled with files according 1445 * to distribution, or if root directory, continue until #files required 1446 */ 1447 for (i = 1; ((parent == NULL) || (i < ranwidth + 1)) && 1448 (fileset->fs_realfiles < fileset->fs_constentries); 1449 i++) { 1450 int ret = 0; 1451 1452 if (parent && isleaf) 1453 ret = fileset_populate_file(fileset, entry, i); 1454 else 1455 ret = fileset_populate_subdir(fileset, entry, i, depth); 1456 1457 if (ret != 0) 1458 return (ret); 1459 } 1460 1461 /* 1462 * Create directory of random width filled with leaf directories 1463 * according to distribution, or if root directory, continue until 1464 * the number of leaf directories required has been generated. 1465 */ 1466 for (i = 1; ((parent == NULL) || (i < ranwidth + 1)) && 1467 (fileset->fs_realleafdirs < fileset->fs_constleafdirs); 1468 i++) { 1469 int ret = 0; 1470 1471 if (parent && isleaf) 1472 ret = fileset_populate_leafdir(fileset, entry, i); 1473 else 1474 ret = fileset_populate_subdir(fileset, entry, i, depth); 1475 1476 if (ret != 0) 1477 return (ret); 1478 } 1479 1480 return (FILEBENCH_OK); 1481 } 1482 1483 /* 1484 * Populates a fileset with files and subdirectory entries. Uses 1485 * the supplied fileset_dirwidth and fileset_entries (number of files) to 1486 * calculate the required fileset_meandepth (of subdirectories) and 1487 * initialize the fileset_meanwidth and fileset_meansize variables. Then 1488 * calls fileset_populate_subdir() to do the recursive 1489 * subdirectory entry creation and leaf file entry creation. All 1490 * of the above is skipped if the fileset has already been 1491 * populated. Returns 0 on success, or an error code from the 1492 * call to fileset_populate_subdir if that call fails. 1493 */ 1494 static int 1495 fileset_populate(fileset_t *fileset) 1496 { 1497 fbint_t entries = avd_get_int(fileset->fs_entries); 1498 fbint_t leafdirs = avd_get_int(fileset->fs_leafdirs); 1499 int meandirwidth; 1500 int ret; 1501 1502 /* Skip if already populated */ 1503 if (fileset->fs_bytes > 0) 1504 goto exists; 1505 1506 #ifdef HAVE_RAW_SUPPORT 1507 /* check for raw device */ 1508 if (fileset->fs_attrs & FILESET_IS_RAW_DEV) 1509 return (FILEBENCH_OK); 1510 #endif /* HAVE_RAW_SUPPORT */ 1511 1512 /* 1513 * save value of entries and leaf dirs obtained for later 1514 * in case it was random 1515 */ 1516 fileset->fs_constentries = entries; 1517 fileset->fs_constleafdirs = leafdirs; 1518 1519 /* initialize idle files and directories condition variables */ 1520 (void) pthread_cond_init(&fileset->fs_idle_files_cv, ipc_condattr()); 1521 (void) pthread_cond_init(&fileset->fs_idle_dirs_cv, ipc_condattr()); 1522 (void) pthread_cond_init(&fileset->fs_idle_leafdirs_cv, ipc_condattr()); 1523 1524 /* no files or dirs idle (or busy) yet */ 1525 fileset->fs_idle_files = 0; 1526 fileset->fs_idle_dirs = 0; 1527 fileset->fs_idle_leafdirs = 0; 1528 1529 /* initialize locks and other condition variables */ 1530 (void) pthread_mutex_init(&fileset->fs_pick_lock, 1531 ipc_mutexattr(IPC_MUTEX_NORMAL)); 1532 (void) pthread_mutex_init(&fileset->fs_histo_lock, 1533 ipc_mutexattr(IPC_MUTEX_NORMAL)); 1534 (void) pthread_cond_init(&fileset->fs_thrd_wait_cv, ipc_condattr()); 1535 1536 /* Initialize avl btrees */ 1537 avl_create(&(fileset->fs_free_files), fileset_entry_compare, 1538 sizeof (filesetentry_t), FSE_OFFSETOF(fse_link)); 1539 avl_create(&(fileset->fs_noex_files), fileset_entry_compare, 1540 sizeof (filesetentry_t), FSE_OFFSETOF(fse_link)); 1541 avl_create(&(fileset->fs_exist_files), fileset_entry_compare, 1542 sizeof (filesetentry_t), FSE_OFFSETOF(fse_link)); 1543 avl_create(&(fileset->fs_free_leaf_dirs), fileset_entry_compare, 1544 sizeof (filesetentry_t), FSE_OFFSETOF(fse_link)); 1545 avl_create(&(fileset->fs_noex_leaf_dirs), fileset_entry_compare, 1546 sizeof (filesetentry_t), FSE_OFFSETOF(fse_link)); 1547 avl_create(&(fileset->fs_exist_leaf_dirs), fileset_entry_compare, 1548 sizeof (filesetentry_t), FSE_OFFSETOF(fse_link)); 1549 avl_create(&(fileset->fs_dirs), fileset_entry_compare, 1550 sizeof (filesetentry_t), FSE_OFFSETOF(fse_link)); 1551 1552 /* is dirwidth a random variable? */ 1553 if (AVD_IS_RANDOM(fileset->fs_dirwidth)) { 1554 meandirwidth = 1555 (int)fileset->fs_dirwidth->avd_val.randptr->rnd_dbl_mean; 1556 fileset->fs_meanwidth = -1; 1557 } else { 1558 meandirwidth = (int)avd_get_int(fileset->fs_dirwidth); 1559 fileset->fs_meanwidth = (double)meandirwidth; 1560 } 1561 1562 /* 1563 * Input params are: 1564 * # of files 1565 * ave # of files per dir 1566 * max size of dir 1567 * # ave size of file 1568 * max size of file 1569 */ 1570 fileset->fs_meandepth = log(entries+leafdirs) / log(meandirwidth); 1571 1572 /* Has a random variable been supplied for dirdepth? */ 1573 if (fileset->fs_dirdepthrv) { 1574 /* yes, so set the random variable's mean value to meandepth */ 1575 fileset->fs_dirdepthrv->avd_val.randptr->rnd_dbl_mean = 1576 fileset->fs_meandepth; 1577 } 1578 1579 /* test for random size variable */ 1580 if (AVD_IS_RANDOM(fileset->fs_size)) 1581 fileset->fs_meansize = -1; 1582 else 1583 fileset->fs_meansize = avd_get_int(fileset->fs_size); 1584 1585 if ((ret = fileset_populate_subdir(fileset, NULL, 1, 0)) != 0) 1586 return (ret); 1587 1588 1589 exists: 1590 if (fileset->fs_attrs & FILESET_IS_FILE) { 1591 filebench_log(LOG_VERBOSE, "File %s: mbytes=%llu", 1592 avd_get_str(fileset->fs_name), 1593 (u_longlong_t)(fileset->fs_bytes / 1024UL / 1024UL)); 1594 } else { 1595 filebench_log(LOG_VERBOSE, "Fileset %s: %d files, %d leafdirs " 1596 "avg dir = %d, avg depth = %.1lf, mbytes=%llu", 1597 avd_get_str(fileset->fs_name), entries, leafdirs, 1598 meandirwidth, 1599 fileset->fs_meandepth, 1600 (u_longlong_t)(fileset->fs_bytes / 1024UL / 1024UL)); 1601 } 1602 1603 return (FILEBENCH_OK); 1604 } 1605 1606 /* 1607 * Allocates a fileset instance, initializes fileset_dirgamma and 1608 * fileset_sizegamma default values, and sets the fileset name to the 1609 * supplied name string. Puts the allocated fileset on the 1610 * master fileset list and returns a pointer to it. 1611 * 1612 * This routine implements the 'define fileset' calls found in a .f 1613 * workload, such as in the following example: 1614 * define fileset name=drew4ever, entries=$nfiles 1615 */ 1616 fileset_t * 1617 fileset_define(avd_t name) 1618 { 1619 fileset_t *fileset; 1620 1621 if (name == NULL) 1622 return (NULL); 1623 1624 if ((fileset = (fileset_t *)ipc_malloc(FILEBENCH_FILESET)) == NULL) { 1625 filebench_log(LOG_ERROR, 1626 "fileset_define: Can't malloc fileset"); 1627 return (NULL); 1628 } 1629 1630 filebench_log(LOG_DEBUG_IMPL, 1631 "Defining file %s", avd_get_str(name)); 1632 1633 (void) ipc_mutex_lock(&filebench_shm->shm_fileset_lock); 1634 1635 fileset->fs_dirgamma = avd_int_alloc(1500); 1636 fileset->fs_sizegamma = avd_int_alloc(1500); 1637 fileset->fs_histo_id = -1; 1638 1639 /* Add fileset to global list */ 1640 if (filebench_shm->shm_filesetlist == NULL) { 1641 filebench_shm->shm_filesetlist = fileset; 1642 fileset->fs_next = NULL; 1643 } else { 1644 fileset->fs_next = filebench_shm->shm_filesetlist; 1645 filebench_shm->shm_filesetlist = fileset; 1646 } 1647 1648 (void) ipc_mutex_unlock(&filebench_shm->shm_fileset_lock); 1649 1650 fileset->fs_name = name; 1651 1652 return (fileset); 1653 } 1654 1655 /* 1656 * If supplied with a pointer to a fileset and the fileset's 1657 * fileset_prealloc flag is set, calls fileset_populate() to populate 1658 * the fileset with filesetentries, then calls fileset_create() 1659 * to make actual directories and files for the filesetentries. 1660 * Otherwise, it applies fileset_populate() and fileset_create() 1661 * to all the filesets on the master fileset list. It always 1662 * returns zero (0) if one fileset is populated / created, 1663 * otherwise it returns the sum of returned values from 1664 * fileset_create() and fileset_populate(), which 1665 * will be a negative one (-1) times the number of 1666 * fileset_create() calls which failed. 1667 */ 1668 int 1669 fileset_createset(fileset_t *fileset) 1670 { 1671 fileset_t *list; 1672 int ret = 0; 1673 1674 /* set up for possible parallel allocate */ 1675 filebench_shm->shm_fsparalloc_count = 0; 1676 (void) pthread_cond_init( 1677 &filebench_shm->shm_fsparalloc_cv, 1678 ipc_condattr()); 1679 1680 if (fileset && avd_get_bool(fileset->fs_prealloc)) { 1681 1682 /* check for raw files */ 1683 if (fileset_checkraw(fileset)) { 1684 filebench_log(LOG_INFO, 1685 "file %s/%s is a RAW device", 1686 avd_get_str(fileset->fs_path), 1687 avd_get_str(fileset->fs_name)); 1688 return (FILEBENCH_OK); 1689 } 1690 1691 filebench_log(LOG_INFO, 1692 "creating/pre-allocating %s %s", 1693 fileset_entity_name(fileset), 1694 avd_get_str(fileset->fs_name)); 1695 1696 if ((ret = fileset_populate(fileset)) != FILEBENCH_OK) 1697 return (ret); 1698 1699 if ((ret = fileset_create(fileset)) != FILEBENCH_OK) 1700 return (ret); 1701 } else { 1702 1703 filebench_log(LOG_INFO, 1704 "Creating/pre-allocating files and filesets"); 1705 1706 list = filebench_shm->shm_filesetlist; 1707 while (list) { 1708 /* check for raw files */ 1709 if (fileset_checkraw(list)) { 1710 filebench_log(LOG_INFO, 1711 "file %s/%s is a RAW device", 1712 avd_get_str(list->fs_path), 1713 avd_get_str(list->fs_name)); 1714 list = list->fs_next; 1715 continue; 1716 } 1717 1718 if ((ret = fileset_populate(list)) != FILEBENCH_OK) 1719 return (ret); 1720 1721 if ((ret = fileset_create(list)) != FILEBENCH_OK) 1722 return (ret); 1723 1724 list = list->fs_next; 1725 } 1726 } 1727 1728 /* wait for allocation threads to finish */ 1729 filebench_log(LOG_INFO, 1730 "waiting for fileset pre-allocation to finish"); 1731 1732 (void) pthread_mutex_lock(&filebench_shm->shm_fsparalloc_lock); 1733 while (filebench_shm->shm_fsparalloc_count > 0) 1734 (void) pthread_cond_wait( 1735 &filebench_shm->shm_fsparalloc_cv, 1736 &filebench_shm->shm_fsparalloc_lock); 1737 (void) pthread_mutex_unlock(&filebench_shm->shm_fsparalloc_lock); 1738 1739 if (filebench_shm->shm_fsparalloc_count < 0) 1740 return (FILEBENCH_ERROR); 1741 1742 return (FILEBENCH_OK); 1743 } 1744 1745 /* 1746 * Searches through the master fileset list for the named fileset. 1747 * If found, returns pointer to same, otherwise returns NULL. 1748 */ 1749 fileset_t * 1750 fileset_find(char *name) 1751 { 1752 fileset_t *fileset = filebench_shm->shm_filesetlist; 1753 1754 (void) ipc_mutex_lock(&filebench_shm->shm_fileset_lock); 1755 1756 while (fileset) { 1757 if (strcmp(name, avd_get_str(fileset->fs_name)) == 0) { 1758 (void) ipc_mutex_unlock( 1759 &filebench_shm->shm_fileset_lock); 1760 return (fileset); 1761 } 1762 fileset = fileset->fs_next; 1763 } 1764 (void) ipc_mutex_unlock(&filebench_shm->shm_fileset_lock); 1765 1766 return (NULL); 1767 } 1768 1769 /* 1770 * Iterates over all the file sets in the filesetlist, 1771 * executing the supplied command "*cmd()" on them. Also 1772 * indicates to the executed command if it is the first 1773 * time the command has been executed since the current 1774 * call to fileset_iter. 1775 */ 1776 int 1777 fileset_iter(int (*cmd)(fileset_t *fileset, int first)) 1778 { 1779 fileset_t *fileset = filebench_shm->shm_filesetlist; 1780 int count = 0; 1781 1782 (void) ipc_mutex_lock(&filebench_shm->shm_fileset_lock); 1783 1784 while (fileset) { 1785 if (cmd(fileset, count == 0) == FILEBENCH_ERROR) { 1786 (void) ipc_mutex_unlock( 1787 &filebench_shm->shm_fileset_lock); 1788 return (FILEBENCH_ERROR); 1789 } 1790 fileset = fileset->fs_next; 1791 count++; 1792 } 1793 1794 (void) ipc_mutex_unlock(&filebench_shm->shm_fileset_lock); 1795 return (FILEBENCH_OK); 1796 } 1797 1798 /* 1799 * Prints information to the filebench log about the file 1800 * object. Also prints a header on the first call. 1801 */ 1802 int 1803 fileset_print(fileset_t *fileset, int first) 1804 { 1805 int pathlength; 1806 char *fileset_path; 1807 char *fileset_name; 1808 static char pad[] = " "; /* 30 spaces */ 1809 1810 if ((fileset_path = avd_get_str(fileset->fs_path)) == NULL) { 1811 filebench_log(LOG_ERROR, "%s path not set", 1812 fileset_entity_name(fileset)); 1813 return (FILEBENCH_ERROR); 1814 } 1815 1816 if ((fileset_name = avd_get_str(fileset->fs_name)) == NULL) { 1817 filebench_log(LOG_ERROR, "%s name not set", 1818 fileset_entity_name(fileset)); 1819 return (FILEBENCH_ERROR); 1820 } 1821 1822 pathlength = strlen(fileset_path) + strlen(fileset_name); 1823 1824 if (pathlength > 29) 1825 pathlength = 29; 1826 1827 if (first) { 1828 filebench_log(LOG_INFO, "File or Fileset name%20s%12s%10s", 1829 "file size", 1830 "dir width", 1831 "entries"); 1832 } 1833 1834 if (fileset->fs_attrs & FILESET_IS_FILE) { 1835 if (fileset->fs_attrs & FILESET_IS_RAW_DEV) { 1836 filebench_log(LOG_INFO, 1837 "%s/%s%s (Raw Device)", 1838 fileset_path, fileset_name, &pad[pathlength]); 1839 } else { 1840 filebench_log(LOG_INFO, 1841 "%s/%s%s%9llu (Single File)", 1842 fileset_path, fileset_name, &pad[pathlength], 1843 (u_longlong_t)avd_get_int(fileset->fs_size)); 1844 } 1845 } else { 1846 filebench_log(LOG_INFO, "%s/%s%s%9llu%12llu%10llu", 1847 fileset_path, fileset_name, 1848 &pad[pathlength], 1849 (u_longlong_t)avd_get_int(fileset->fs_size), 1850 (u_longlong_t)avd_get_int(fileset->fs_dirwidth), 1851 (u_longlong_t)fileset->fs_constentries); 1852 } 1853 return (FILEBENCH_OK); 1854 } 1855 1856 /* 1857 * checks to see if the path/name pair points to a raw device. If 1858 * so it sets the raw device flag (FILESET_IS_RAW_DEV) and returns 1. 1859 * If RAW is not defined, or it is not a raw device, it clears the 1860 * raw device flag and returns 0. 1861 */ 1862 int 1863 fileset_checkraw(fileset_t *fileset) 1864 { 1865 char path[MAXPATHLEN]; 1866 struct stat64 sb; 1867 char *pathname; 1868 char *setname; 1869 1870 fileset->fs_attrs &= (~FILESET_IS_RAW_DEV); 1871 1872 #ifdef HAVE_RAW_SUPPORT 1873 /* check for raw device */ 1874 if ((pathname = avd_get_str(fileset->fs_path)) == NULL) 1875 return (FILEBENCH_OK); 1876 1877 if ((setname = avd_get_str(fileset->fs_name)) == NULL) 1878 return (FILEBENCH_OK); 1879 1880 (void) fb_strlcpy(path, pathname, MAXPATHLEN); 1881 (void) fb_strlcat(path, "/", MAXPATHLEN); 1882 (void) fb_strlcat(path, setname, MAXPATHLEN); 1883 if ((stat64(path, &sb) == 0) && 1884 ((sb.st_mode & S_IFMT) == S_IFBLK) && sb.st_rdev) { 1885 fileset->fs_attrs |= FILESET_IS_RAW_DEV; 1886 if (!(fileset->fs_attrs & FILESET_IS_FILE)) { 1887 filebench_log(LOG_ERROR, 1888 "WARNING Fileset %s/%s Cannot be RAW device", 1889 avd_get_str(fileset->fs_path), 1890 avd_get_str(fileset->fs_name)); 1891 filebench_shutdown(1); 1892 } 1893 1894 return (1); 1895 } 1896 #endif /* HAVE_RAW_SUPPORT */ 1897 1898 return (FILEBENCH_OK); 1899 } 1900