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 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 29 #include <fcntl.h> 30 #include <pthread.h> 31 #include <errno.h> 32 #include <math.h> 33 #include <libgen.h> 34 #include <sys/mman.h> 35 #include "fileset.h" 36 #include "filebench.h" 37 #include "gamma_dist.h" 38 39 /* 40 * File sets, of type fileset_t, are entities which contain 41 * information about collections of files and subdirectories in Filebench. 42 * The fileset, once populated, consists of a tree of fileset entries of 43 * type filesetentry_t which specify files and directories. The fileset 44 * is rooted in a directory specified by fileset_path, and once the populated 45 * fileset has been created, has a tree of directories and files 46 * corresponding to the fileset's filesetentry tree. 47 */ 48 49 /* parallel allocation control */ 50 #define MAX_PARALLOC_THREADS 32 51 static pthread_mutex_t paralloc_lock = PTHREAD_MUTEX_INITIALIZER; 52 static pthread_cond_t paralloc_cv = PTHREAD_COND_INITIALIZER; 53 static int paralloc_count; 54 55 /* 56 * returns pointer to file or fileset 57 * string, as appropriate 58 */ 59 static char * 60 fileset_entity_name(fileset_t *fileset) 61 { 62 if (fileset->fs_attrs & FILESET_IS_FILE) 63 return ("file"); 64 else 65 return ("fileset"); 66 } 67 68 /* 69 * Removes the last file or directory name from a pathname. 70 * Basically removes characters from the end of the path by 71 * setting them to \0 until a forward slash '/' is 72 * encountered. It also removes the forward slash. 73 */ 74 static char * 75 trunc_dirname(char *dir) 76 { 77 char *s = dir + strlen(dir); 78 79 while (s != dir) { 80 int c = *s; 81 82 *s = 0; 83 if (c == '/') 84 break; 85 s--; 86 } 87 return (dir); 88 } 89 90 /* 91 * Prints a list of allowed options and how to specify them. 92 */ 93 void 94 fileset_usage(void) 95 { 96 (void) fprintf(stderr, 97 "define [file name=<name> | fileset name=<name>],path=<pathname>," 98 ",entries=<number>\n"); 99 (void) fprintf(stderr, 100 " [,filesize=[size]]\n"); 101 (void) fprintf(stderr, 102 " [,dirwidth=[width]]\n"); 103 (void) fprintf(stderr, 104 " [,dirdepthrv=$random_variable_name]\n"); 105 (void) fprintf(stderr, 106 " [,dirgamma=[100-10000]] " 107 "(Gamma * 1000)\n"); 108 (void) fprintf(stderr, 109 " [,sizegamma=[100-10000]] (Gamma * 1000)\n"); 110 (void) fprintf(stderr, 111 " [,prealloc=[percent]]\n"); 112 (void) fprintf(stderr, " [,paralloc]\n"); 113 (void) fprintf(stderr, " [,reuse]\n"); 114 (void) fprintf(stderr, "\n"); 115 } 116 117 /* 118 * Frees up memory mapped file region of supplied size. The 119 * file descriptor "fd" indicates which memory mapped file. 120 * If successful, returns 0. Otherwise returns -1 if "size" 121 * is zero, or -1 times the number of times msync() failed. 122 */ 123 static int 124 fileset_freemem(int fd, off64_t size) 125 { 126 off64_t left; 127 int ret = 0; 128 129 for (left = size; left > 0; left -= MMAP_SIZE) { 130 off64_t thismapsize; 131 caddr_t addr; 132 133 thismapsize = MIN(MMAP_SIZE, left); 134 addr = mmap64(0, thismapsize, PROT_READ|PROT_WRITE, 135 MAP_SHARED, fd, size - left); 136 ret += msync(addr, thismapsize, MS_INVALIDATE); 137 (void) munmap(addr, thismapsize); 138 } 139 return (ret); 140 } 141 142 /* 143 * Creates a path string from the filesetentry_t "*entry" 144 * and all of its parent's path names. The resulting path 145 * is a concatination of all the individual parent paths. 146 * Allocates memory for the path string and returns a 147 * pointer to it. 148 */ 149 char * 150 fileset_resolvepath(filesetentry_t *entry) 151 { 152 filesetentry_t *fsep = entry; 153 char path[MAXPATHLEN]; 154 char pathtmp[MAXPATHLEN]; 155 char *s; 156 157 *path = 0; 158 while (fsep->fse_parent) { 159 (void) strcpy(pathtmp, "/"); 160 (void) strcat(pathtmp, fsep->fse_path); 161 (void) strcat(pathtmp, path); 162 (void) strcpy(path, pathtmp); 163 fsep = fsep->fse_parent; 164 } 165 166 s = malloc(strlen(path) + 1); 167 (void) strcpy(s, path); 168 return (s); 169 } 170 171 /* 172 * Creates multiple nested directories as required by the 173 * supplied path. Starts at the end of the path, creating 174 * a list of directories to mkdir, up to the root of the 175 * path, then mkdirs them one at a time from the root on down. 176 */ 177 static int 178 fileset_mkdir(char *path, int mode) 179 { 180 char *p; 181 char *dirs[65536]; 182 int i = 0; 183 184 if ((p = strdup(path)) == NULL) 185 goto null_str; 186 187 /* 188 * Fill an array of subdirectory path names until either we 189 * reach the root or encounter an already existing subdirectory 190 */ 191 /* CONSTCOND */ 192 while (1) { 193 struct stat64 sb; 194 195 if (stat64(p, &sb) == 0) 196 break; 197 if (strlen(p) < 3) 198 break; 199 if ((dirs[i] = strdup(p)) == NULL) { 200 free(p); 201 goto null_str; 202 } 203 204 (void) trunc_dirname(p); 205 i++; 206 } 207 208 /* Make the directories, from closest to root downwards. */ 209 for (--i; i >= 0; i--) { 210 (void) mkdir(dirs[i], mode); 211 free(dirs[i]); 212 } 213 214 free(p); 215 return (0); 216 217 null_str: 218 /* clean up */ 219 for (--i; i >= 0; i--) 220 free(dirs[i]); 221 222 filebench_log(LOG_ERROR, 223 "Failed to create directory path %s: Out of memory", path); 224 225 return (-1); 226 } 227 228 /* 229 * creates the subdirectory tree for a fileset. 230 */ 231 static int 232 fileset_create_subdirs(fileset_t *fileset, char *filesetpath) 233 { 234 filesetentry_t *direntry; 235 char full_path[MAXPATHLEN]; 236 char *part_path; 237 238 /* walk the subdirectory list, enstanciating subdirs */ 239 direntry = fileset->fs_dirlist; 240 while (direntry) { 241 (void) strcpy(full_path, filesetpath); 242 part_path = fileset_resolvepath(direntry); 243 (void) strcat(full_path, part_path); 244 free(part_path); 245 246 /* now create this portion of the subdirectory tree */ 247 if (fileset_mkdir(full_path, 0755) == -1) 248 return (-1); 249 250 direntry = direntry->fse_dirnext; 251 } 252 return (0); 253 } 254 255 /* 256 * given a fileset entry, determines if the associated file 257 * needs to be allocated or not, and if so does the allocation. 258 */ 259 static int 260 fileset_alloc_file(filesetentry_t *entry) 261 { 262 char path[MAXPATHLEN]; 263 char *buf; 264 struct stat64 sb; 265 char *pathtmp; 266 off64_t seek; 267 int fd; 268 269 *path = 0; 270 (void) strcpy(path, avd_get_str(entry->fse_fileset->fs_path)); 271 (void) strcat(path, "/"); 272 (void) strcat(path, avd_get_str(entry->fse_fileset->fs_name)); 273 pathtmp = fileset_resolvepath(entry); 274 (void) strcat(path, pathtmp); 275 276 filebench_log(LOG_DEBUG_IMPL, "Populated %s", entry->fse_path); 277 278 /* see if reusing and this file exists */ 279 if ((entry->fse_flags & FSE_REUSING) && (stat64(path, &sb) == 0)) { 280 if ((fd = open64(path, O_RDWR)) < 0) { 281 filebench_log(LOG_INFO, 282 "Attempted but failed to Re-use file %s", 283 path); 284 return (-1); 285 } 286 287 if (sb.st_size == (off64_t)entry->fse_size) { 288 filebench_log(LOG_INFO, 289 "Re-using file %s", path); 290 291 if (!avd_get_bool(entry->fse_fileset->fs_cached)) 292 (void) fileset_freemem(fd, 293 entry->fse_size); 294 295 entry->fse_flags |= FSE_EXISTS; 296 (void) close(fd); 297 return (0); 298 299 } else if (sb.st_size > (off64_t)entry->fse_size) { 300 /* reuse, but too large */ 301 filebench_log(LOG_INFO, 302 "Truncating & re-using file %s", path); 303 304 (void) ftruncate64(fd, 305 (off64_t)entry->fse_size); 306 307 if (!avd_get_bool(entry->fse_fileset->fs_cached)) 308 (void) fileset_freemem(fd, 309 entry->fse_size); 310 311 entry->fse_flags |= FSE_EXISTS; 312 (void) close(fd); 313 return (0); 314 } 315 } else { 316 317 /* No file or not reusing, so create */ 318 if ((fd = open64(path, O_RDWR | O_CREAT, 0644)) < 0) { 319 filebench_log(LOG_ERROR, 320 "Failed to pre-allocate file %s: %s", 321 path, strerror(errno)); 322 323 return (-1); 324 } 325 } 326 327 if ((buf = (char *)malloc(FILE_ALLOC_BLOCK)) == NULL) 328 return (-1); 329 330 entry->fse_flags |= FSE_EXISTS; 331 332 for (seek = 0; seek < entry->fse_size; ) { 333 off64_t wsize; 334 int ret = 0; 335 336 /* 337 * Write FILE_ALLOC_BLOCK's worth, 338 * except on last write 339 */ 340 wsize = MIN(entry->fse_size - seek, FILE_ALLOC_BLOCK); 341 342 ret = write(fd, buf, wsize); 343 if (ret != wsize) { 344 filebench_log(LOG_ERROR, 345 "Failed to pre-allocate file %s: %s", 346 path, strerror(errno)); 347 (void) close(fd); 348 free(buf); 349 return (-1); 350 } 351 seek += wsize; 352 } 353 354 if (!avd_get_bool(entry->fse_fileset->fs_cached)) 355 (void) fileset_freemem(fd, entry->fse_size); 356 357 (void) close(fd); 358 359 free(buf); 360 361 filebench_log(LOG_DEBUG_IMPL, 362 "Pre-allocated file %s size %lld", path, entry->fse_size); 363 364 return (0); 365 } 366 367 /* 368 * given a fileset entry, determines if the associated file 369 * needs to be allocated or not, and if so does the allocation. 370 */ 371 static void * 372 fileset_alloc_thread(filesetentry_t *entry) 373 { 374 if (fileset_alloc_file(entry) == -1) { 375 (void) pthread_mutex_lock(¶lloc_lock); 376 paralloc_count = -1; 377 } else { 378 (void) pthread_mutex_lock(¶lloc_lock); 379 paralloc_count--; 380 } 381 382 (void) pthread_cond_signal(¶lloc_cv); 383 (void) pthread_mutex_unlock(¶lloc_lock); 384 385 pthread_exit(NULL); 386 return (NULL); 387 } 388 389 390 /* 391 * First creates the parent directories of the file using 392 * fileset_mkdir(). Then Optionally sets the O_DSYNC flag 393 * and opens the file with open64(). It unlocks the fileset 394 * entry lock, sets the DIRECTIO_ON or DIRECTIO_OFF flags 395 * as requested, and returns the file descriptor integer 396 * for the opened file. 397 */ 398 int 399 fileset_openfile(fileset_t *fileset, 400 filesetentry_t *entry, int flag, int mode, int attrs) 401 { 402 char path[MAXPATHLEN]; 403 char dir[MAXPATHLEN]; 404 char *pathtmp; 405 struct stat64 sb; 406 int fd; 407 int open_attrs = 0; 408 409 *path = 0; 410 (void) strcpy(path, avd_get_str(fileset->fs_path)); 411 (void) strcat(path, "/"); 412 (void) strcat(path, avd_get_str(fileset->fs_name)); 413 pathtmp = fileset_resolvepath(entry); 414 (void) strcat(path, pathtmp); 415 (void) strcpy(dir, path); 416 free(pathtmp); 417 (void) trunc_dirname(dir); 418 419 /* If we are going to create a file, create the parent dirs */ 420 if ((flag & O_CREAT) && (stat64(dir, &sb) != 0)) { 421 if (fileset_mkdir(dir, 0755) == -1) 422 return (-1); 423 } 424 425 if (flag & O_CREAT) 426 entry->fse_flags |= FSE_EXISTS; 427 428 if (attrs & FLOW_ATTR_DSYNC) { 429 #ifdef sun 430 open_attrs |= O_DSYNC; 431 #else 432 open_attrs |= O_FSYNC; 433 #endif 434 } 435 436 if ((fd = open64(path, flag | open_attrs, mode)) < 0) { 437 filebench_log(LOG_ERROR, 438 "Failed to open file %s: %s", 439 path, strerror(errno)); 440 (void) ipc_mutex_unlock(&entry->fse_lock); 441 return (-1); 442 } 443 (void) ipc_mutex_unlock(&entry->fse_lock); 444 445 #ifdef sun 446 if (attrs & FLOW_ATTR_DIRECTIO) 447 (void) directio(fd, DIRECTIO_ON); 448 else 449 (void) directio(fd, DIRECTIO_OFF); 450 #endif 451 452 return (fd); 453 } 454 455 456 /* 457 * Selects a fileset entry from a fileset. If the 458 * FILESET_PICKDIR flag is set it will pick a directory 459 * entry, otherwise a file entry. The FILESET_PICKRESET 460 * flag will cause it to reset the free list to the 461 * overall list (file or directory). The FILESET_PICKUNIQUE 462 * flag will take an entry off of one of the free (unused) 463 * lists (file or directory), otherwise the entry will be 464 * picked off of one of the rotor lists (file or directory). 465 * The FILESET_PICKEXISTS will insure that only extant 466 * (FSE_EXISTS) state files are selected, while 467 * FILESET_PICKNOEXIST insures that only non extant 468 * (not FSE_EXISTS) state files are selected. 469 */ 470 filesetentry_t * 471 fileset_pick(fileset_t *fileset, int flags, int tid) 472 { 473 filesetentry_t *entry = NULL; 474 filesetentry_t *first = NULL; 475 476 (void) ipc_mutex_lock(&filebench_shm->fileset_lock); 477 478 while (entry == NULL) { 479 480 if ((flags & FILESET_PICKDIR) && (flags & FILESET_PICKRESET)) { 481 entry = fileset->fs_dirlist; 482 while (entry) { 483 entry->fse_flags |= FSE_FREE; 484 entry = entry->fse_dirnext; 485 } 486 fileset->fs_dirfree = fileset->fs_dirlist; 487 } 488 489 if (!(flags & FILESET_PICKDIR) && (flags & FILESET_PICKRESET)) { 490 entry = fileset->fs_filelist; 491 while (entry) { 492 entry->fse_flags |= FSE_FREE; 493 entry = entry->fse_filenext; 494 } 495 fileset->fs_filefree = fileset->fs_filelist; 496 } 497 498 if (flags & FILESET_PICKUNIQUE) { 499 if (flags & FILESET_PICKDIR) { 500 entry = fileset->fs_dirfree; 501 if (entry == NULL) 502 goto empty; 503 fileset->fs_dirfree = entry->fse_dirnext; 504 } else { 505 entry = fileset->fs_filefree; 506 if (entry == NULL) 507 goto empty; 508 fileset->fs_filefree = entry->fse_filenext; 509 } 510 entry->fse_flags &= ~FSE_FREE; 511 } else { 512 if (flags & FILESET_PICKDIR) { 513 entry = fileset->fs_dirrotor; 514 if (entry == NULL) 515 fileset->fs_dirrotor = 516 entry = fileset->fs_dirlist; 517 fileset->fs_dirrotor = entry->fse_dirnext; 518 } else { 519 entry = fileset->fs_filerotor[tid]; 520 if (entry == NULL) 521 fileset->fs_filerotor[tid] = 522 entry = fileset->fs_filelist; 523 fileset->fs_filerotor[tid] = 524 entry->fse_filenext; 525 } 526 } 527 528 if (first == entry) 529 goto empty; 530 531 if (first == NULL) 532 first = entry; 533 534 /* Return locked entry */ 535 (void) ipc_mutex_lock(&entry->fse_lock); 536 537 /* If we ask for an existing file, go round again */ 538 if ((flags & FILESET_PICKEXISTS) && 539 !(entry->fse_flags & FSE_EXISTS)) { 540 (void) ipc_mutex_unlock(&entry->fse_lock); 541 entry = NULL; 542 } 543 544 /* If we ask for not an existing file, go round again */ 545 if ((flags & FILESET_PICKNOEXIST) && 546 (entry->fse_flags & FSE_EXISTS)) { 547 (void) ipc_mutex_unlock(&entry->fse_lock); 548 entry = NULL; 549 } 550 } 551 552 (void) ipc_mutex_unlock(&filebench_shm->fileset_lock); 553 filebench_log(LOG_DEBUG_SCRIPT, "Picked file %s", entry->fse_path); 554 return (entry); 555 556 empty: 557 (void) ipc_mutex_unlock(&filebench_shm->fileset_lock); 558 return (NULL); 559 } 560 561 /* 562 * Given a fileset "fileset", create the associated files as 563 * specified in the attributes of the fileset. The fileset is 564 * rooted in a directory whose pathname is in fileset_path. If the 565 * directory exists, meaning that there is already a fileset, 566 * and the fileset_reuse attribute is false, then remove it and all 567 * its contained files and subdirectories. Next, the routine 568 * creates a root directory for the fileset. All the file type 569 * filesetentries are cycled through creating as needed 570 * their containing subdirectory trees in the filesystem and 571 * creating actual files for fileset_preallocpercent of them. The 572 * created files are filled with fse_size bytes of unitialized 573 * data. The routine returns -1 on errors, 0 on success. 574 */ 575 static int 576 fileset_create(fileset_t *fileset) 577 { 578 filesetentry_t *entry; 579 char path[MAXPATHLEN]; 580 struct stat64 sb; 581 int pickflags = FILESET_PICKUNIQUE | FILESET_PICKRESET; 582 hrtime_t start = gethrtime(); 583 char *fileset_path; 584 char *fileset_name; 585 int randno; 586 int preallocated = 0; 587 int reusing = 0; 588 589 if ((fileset_path = avd_get_str(fileset->fs_path)) == NULL) { 590 filebench_log(LOG_ERROR, "%s path not set", 591 fileset_entity_name(fileset)); 592 return (-1); 593 } 594 595 if ((fileset_name = avd_get_str(fileset->fs_name)) == NULL) { 596 filebench_log(LOG_ERROR, "%s name not set", 597 fileset_entity_name(fileset)); 598 return (-1); 599 } 600 601 #ifdef HAVE_RAW_SUPPORT 602 /* treat raw device as special case */ 603 if (fileset->fs_attrs & FILESET_IS_RAW_DEV) 604 return (0); 605 #endif /* HAVE_RAW_SUPPORT */ 606 607 /* XXX Add check to see if there is enough space */ 608 609 /* Remove existing */ 610 (void) strcpy(path, fileset_path); 611 (void) strcat(path, "/"); 612 (void) strcat(path, fileset_name); 613 if ((stat64(path, &sb) == 0) && (strlen(path) > 3) && 614 (strlen(avd_get_str(fileset->fs_path)) > 2)) { 615 if (!avd_get_bool(fileset->fs_reuse)) { 616 char cmd[MAXPATHLEN]; 617 618 (void) snprintf(cmd, sizeof (cmd), "rm -rf %s", path); 619 (void) system(cmd); 620 filebench_log(LOG_VERBOSE, 621 "Removed any existing %s %s in %lld seconds", 622 fileset_entity_name(fileset), fileset_name, 623 ((gethrtime() - start) / 1000000000) + 1); 624 } else { 625 /* we are re-using */ 626 reusing = 1; 627 filebench_log(LOG_VERBOSE, 628 "Re-using %s %s on %s file system.", 629 fileset_entity_name(fileset), 630 fileset_name, sb.st_fstype); 631 } 632 } 633 (void) mkdir(path, 0755); 634 635 /* make the filesets directory tree */ 636 if (fileset_create_subdirs(fileset, path) == -1) 637 return (-1); 638 639 start = gethrtime(); 640 641 filebench_log(LOG_VERBOSE, "Creating %s %s...", 642 fileset_entity_name(fileset), fileset_name); 643 644 if (!avd_get_bool(fileset->fs_prealloc)) 645 goto exit; 646 647 randno = ((RAND_MAX * (100 648 - avd_get_int(fileset->fs_preallocpercent))) / 100); 649 650 while (entry = fileset_pick(fileset, pickflags, 0)) { 651 pthread_t tid; 652 653 pickflags = FILESET_PICKUNIQUE; 654 655 entry->fse_flags &= ~FSE_EXISTS; 656 657 /* entry doesn't need to be locked during initialization */ 658 (void) ipc_mutex_unlock(&entry->fse_lock); 659 660 if (rand() < randno) 661 continue; 662 663 preallocated++; 664 665 if (reusing) 666 entry->fse_flags |= FSE_REUSING; 667 else 668 entry->fse_flags &= (~FSE_REUSING); 669 670 if (avd_get_bool(fileset->fs_paralloc)) { 671 672 /* fire off a separate allocation thread */ 673 (void) pthread_mutex_lock(¶lloc_lock); 674 while (paralloc_count >= MAX_PARALLOC_THREADS) { 675 (void) pthread_cond_wait( 676 ¶lloc_cv, ¶lloc_lock); 677 } 678 679 if (paralloc_count < 0) { 680 (void) pthread_mutex_unlock(¶lloc_lock); 681 return (-1); 682 } 683 684 paralloc_count++; 685 (void) pthread_mutex_unlock(¶lloc_lock); 686 687 if (pthread_create(&tid, NULL, 688 (void *(*)(void*))fileset_alloc_thread, 689 entry) != 0) { 690 filebench_log(LOG_ERROR, 691 "File prealloc thread create failed"); 692 filebench_shutdown(1); 693 } 694 695 } else { 696 if (fileset_alloc_file(entry) == -1) 697 return (-1); 698 } 699 } 700 701 exit: 702 filebench_log(LOG_VERBOSE, 703 "Preallocated %d of %lld of %s %s in %lld seconds", 704 preallocated, 705 fileset->fs_constentries, 706 fileset_entity_name(fileset), fileset_name, 707 ((gethrtime() - start) / 1000000000) + 1); 708 709 return (0); 710 } 711 712 /* 713 * Adds an entry to the fileset's file list. Single threaded so 714 * no locking needed. 715 */ 716 static void 717 fileset_insfilelist(fileset_t *fileset, filesetentry_t *entry) 718 { 719 if (fileset->fs_filelist == NULL) { 720 fileset->fs_filelist = entry; 721 entry->fse_filenext = NULL; 722 } else { 723 entry->fse_filenext = fileset->fs_filelist; 724 fileset->fs_filelist = entry; 725 } 726 } 727 728 /* 729 * Adds an entry to the fileset's directory list. Single 730 * threaded so no locking needed. 731 */ 732 static void 733 fileset_insdirlist(fileset_t *fileset, filesetentry_t *entry) 734 { 735 if (fileset->fs_dirlist == NULL) { 736 fileset->fs_dirlist = entry; 737 entry->fse_dirnext = NULL; 738 } else { 739 entry->fse_dirnext = fileset->fs_dirlist; 740 fileset->fs_dirlist = entry; 741 } 742 } 743 744 /* 745 * Obtaines a filesetentry entity for a file to be placed in a 746 * (sub)directory of a fileset. The size of the file may be 747 * specified by fileset_meansize, or calculated from a gamma 748 * distribution of parameter fileset_sizegamma and of mean size 749 * fileset_meansize. The filesetentry entity is placed on the file 750 * list in the specified parent filesetentry entity, which may 751 * be a directory filesetentry, or the root filesetentry in the 752 * fileset. It is also placed on the fileset's list of all 753 * contained files. Returns 0 if successful or -1 if ipc memory 754 * for the path string cannot be allocated. 755 */ 756 static int 757 fileset_populate_file(fileset_t *fileset, filesetentry_t *parent, int serial) 758 { 759 char tmpname[16]; 760 filesetentry_t *entry; 761 double drand; 762 763 if ((entry = (filesetentry_t *)ipc_malloc(FILEBENCH_FILESETENTRY)) 764 == NULL) { 765 filebench_log(LOG_ERROR, 766 "fileset_populate_file: Can't malloc filesetentry"); 767 return (-1); 768 } 769 770 (void) pthread_mutex_init(&entry->fse_lock, ipc_mutexattr()); 771 entry->fse_parent = parent; 772 entry->fse_fileset = fileset; 773 entry->fse_flags |= FSE_FREE; 774 fileset_insfilelist(fileset, entry); 775 776 (void) snprintf(tmpname, sizeof (tmpname), "%08d", serial); 777 if ((entry->fse_path = (char *)ipc_pathalloc(tmpname)) == NULL) { 778 filebench_log(LOG_ERROR, 779 "fileset_populate_file: Can't alloc path string"); 780 return (-1); 781 } 782 783 /* see if random variable was supplied for file size */ 784 if (fileset->fs_meansize == -1) { 785 entry->fse_size = (off64_t)avd_get_int(fileset->fs_size); 786 } else { 787 double gamma; 788 789 gamma = avd_get_int(fileset->fs_sizegamma) / 1000.0; 790 if (gamma > 0) { 791 drand = gamma_dist_knuth(gamma, 792 fileset->fs_meansize / gamma); 793 entry->fse_size = (off64_t)drand; 794 } else { 795 entry->fse_size = (off64_t)fileset->fs_meansize; 796 } 797 } 798 799 fileset->fs_bytes += entry->fse_size; 800 801 fileset->fs_realfiles++; 802 return (0); 803 } 804 805 /* 806 * Creates a directory node in a fileset, by obtaining a 807 * filesetentry entity for the node and initializing it 808 * according to parameters of the fileset. It determines a 809 * directory tree depth and directory width, optionally using 810 * a gamma distribution. If its calculated depth is less then 811 * its actual depth in the directory tree, it becomes a leaf 812 * node and files itself with "width" number of file type 813 * filesetentries, otherwise it files itself with "width" 814 * number of directory type filesetentries, using recursive 815 * calls to fileset_populate_subdir. The end result of the 816 * initial call to this routine is a tree of directories of 817 * random width and varying depth with sufficient leaf 818 * directories to contain all required files. 819 * Returns 0 on success. Returns -1 if ipc path string memory 820 * cannot be allocated and returns an error code (currently 821 * also -1) from calls to fileset_populate_file or recursive 822 * calls to fileset_populate_subdir. 823 */ 824 static int 825 fileset_populate_subdir(fileset_t *fileset, filesetentry_t *parent, 826 int serial, double depth) 827 { 828 double randepth, drand, ranwidth; 829 int isleaf = 0; 830 char tmpname[16]; 831 filesetentry_t *entry; 832 int i; 833 834 depth += 1; 835 836 /* Create dir node */ 837 if ((entry = (filesetentry_t *)ipc_malloc(FILEBENCH_FILESETENTRY)) 838 == NULL) { 839 filebench_log(LOG_ERROR, 840 "fileset_populate_subdir: Can't malloc filesetentry"); 841 return (-1); 842 } 843 844 (void) pthread_mutex_init(&entry->fse_lock, ipc_mutexattr()); 845 846 (void) snprintf(tmpname, sizeof (tmpname), "%08d", serial); 847 if ((entry->fse_path = (char *)ipc_pathalloc(tmpname)) == NULL) { 848 filebench_log(LOG_ERROR, 849 "fileset_populate_subdir: Can't alloc path string"); 850 return (-1); 851 } 852 853 entry->fse_parent = parent; 854 entry->fse_flags |= FSE_DIR | FSE_FREE; 855 fileset_insdirlist(fileset, entry); 856 857 if (fileset->fs_dirdepthrv) { 858 randepth = (int)avd_get_int(fileset->fs_dirdepthrv); 859 } else { 860 double gamma; 861 862 gamma = avd_get_int(fileset->fs_dirgamma) / 1000.0; 863 if (gamma > 0) { 864 drand = gamma_dist_knuth(gamma, 865 fileset->fs_meandepth / gamma); 866 randepth = (int)drand; 867 } else { 868 randepth = (int)fileset->fs_meandepth; 869 } 870 } 871 872 if (fileset->fs_meanwidth == -1) { 873 ranwidth = avd_get_dbl(fileset->fs_dirwidth); 874 } else { 875 double gamma; 876 877 gamma = avd_get_int(fileset->fs_sizegamma) / 1000.0; 878 if (gamma > 0) { 879 drand = gamma_dist_knuth(gamma, 880 fileset->fs_meanwidth / gamma); 881 ranwidth = drand; 882 } else { 883 ranwidth = fileset->fs_meanwidth; 884 } 885 } 886 887 if (randepth == 0) 888 randepth = 1; 889 if (ranwidth == 0) 890 ranwidth = 1; 891 if (depth >= randepth) 892 isleaf = 1; 893 894 /* 895 * Create directory of random width according to distribution, or 896 * if root directory, continue until #files required 897 */ 898 for (i = 1; ((parent == NULL) || (i < ranwidth + 1)) && 899 (fileset->fs_realfiles < fileset->fs_constentries); 900 i++) { 901 int ret = 0; 902 903 if (parent && isleaf) 904 ret = fileset_populate_file(fileset, entry, i); 905 else 906 ret = fileset_populate_subdir(fileset, entry, i, depth); 907 908 if (ret != 0) 909 return (ret); 910 } 911 return (0); 912 } 913 914 /* 915 * Populates a fileset with files and subdirectory entries. Uses 916 * the supplied fileset_dirwidth and fileset_entries (number of files) to 917 * calculate the required fileset_meandepth (of subdirectories) and 918 * initialize the fileset_meanwidth and fileset_meansize variables. Then 919 * calls fileset_populate_subdir() to do the recursive 920 * subdirectory entry creation and leaf file entry creation. All 921 * of the above is skipped if the fileset has already been 922 * populated. Returns 0 on success, or an error code from the 923 * call to fileset_populate_subdir if that call fails. 924 */ 925 static int 926 fileset_populate(fileset_t *fileset) 927 { 928 int entries = (int)avd_get_int(fileset->fs_entries); 929 int meandirwidth; 930 int ret; 931 932 /* Skip if already populated */ 933 if (fileset->fs_bytes > 0) 934 goto exists; 935 936 #ifdef HAVE_RAW_SUPPORT 937 /* check for raw device */ 938 if (fileset->fs_attrs & FILESET_IS_RAW_DEV) 939 return (0); 940 #endif /* HAVE_RAW_SUPPORT */ 941 942 /* save value of entries obtained for later, in case it was random */ 943 fileset->fs_constentries = entries; 944 945 /* is dirwidth a random variable? */ 946 if (AVD_IS_RANDOM(fileset->fs_dirwidth)) { 947 meandirwidth = 948 (int)fileset->fs_dirwidth->avd_val.randptr->rnd_dbl_mean; 949 fileset->fs_meanwidth = -1; 950 } else { 951 meandirwidth = (int)avd_get_int(fileset->fs_dirwidth); 952 fileset->fs_meanwidth = (double)meandirwidth; 953 } 954 955 /* 956 * Input params are: 957 * # of files 958 * ave # of files per dir 959 * max size of dir 960 * # ave size of file 961 * max size of file 962 */ 963 fileset->fs_meandepth = log(entries) / log(meandirwidth); 964 965 /* Has a random variable been supplied for dirdepth? */ 966 if (fileset->fs_dirdepthrv) { 967 /* yes, so set the random variable's mean value to meandepth */ 968 fileset->fs_dirdepthrv->avd_val.randptr->rnd_dbl_mean = 969 fileset->fs_meandepth; 970 } 971 972 /* test for random size variable */ 973 if (AVD_IS_RANDOM(fileset->fs_size)) 974 fileset->fs_meansize = -1; 975 else 976 fileset->fs_meansize = avd_get_int(fileset->fs_size); 977 978 if ((ret = fileset_populate_subdir(fileset, NULL, 1, 0)) != 0) 979 return (ret); 980 981 982 exists: 983 if (fileset->fs_attrs & FILESET_IS_FILE) { 984 filebench_log(LOG_VERBOSE, "File %s: mbytes=%lld", 985 avd_get_str(fileset->fs_name), 986 fileset->fs_bytes / 1024UL / 1024UL); 987 } else { 988 filebench_log(LOG_VERBOSE, "Fileset %s: %lld files, " 989 "avg dir = %d, avg depth = %.1lf, mbytes=%lld", 990 avd_get_str(fileset->fs_name), entries, 991 meandirwidth, 992 fileset->fs_meandepth, 993 fileset->fs_bytes / 1024UL / 1024UL); 994 } 995 return (0); 996 } 997 998 /* 999 * Allocates a fileset instance, initializes fileset_dirgamma and 1000 * fileset_sizegamma default values, and sets the fileset name to the 1001 * supplied name string. Puts the allocated fileset on the 1002 * master fileset list and returns a pointer to it. 1003 */ 1004 fileset_t * 1005 fileset_define(avd_t name) 1006 { 1007 fileset_t *fileset; 1008 1009 if (name == NULL) 1010 return (NULL); 1011 1012 if ((fileset = (fileset_t *)ipc_malloc(FILEBENCH_FILESET)) == NULL) { 1013 filebench_log(LOG_ERROR, 1014 "fileset_define: Can't malloc fileset"); 1015 return (NULL); 1016 } 1017 1018 filebench_log(LOG_DEBUG_IMPL, 1019 "Defining file %s", avd_get_str(name)); 1020 1021 (void) ipc_mutex_lock(&filebench_shm->fileset_lock); 1022 1023 fileset->fs_dirgamma = avd_int_alloc(1500); 1024 fileset->fs_sizegamma = avd_int_alloc(1500); 1025 1026 /* Add fileset to global list */ 1027 if (filebench_shm->filesetlist == NULL) { 1028 filebench_shm->filesetlist = fileset; 1029 fileset->fs_next = NULL; 1030 } else { 1031 fileset->fs_next = filebench_shm->filesetlist; 1032 filebench_shm->filesetlist = fileset; 1033 } 1034 1035 (void) ipc_mutex_unlock(&filebench_shm->fileset_lock); 1036 1037 fileset->fs_name = name; 1038 1039 return (fileset); 1040 } 1041 1042 /* 1043 * If supplied with a pointer to a fileset and the fileset's 1044 * fileset_prealloc flag is set, calls fileset_populate() to populate 1045 * the fileset with filesetentries, then calls fileset_create() 1046 * to make actual directories and files for the filesetentries. 1047 * Otherwise, it applies fileset_populate() and fileset_create() 1048 * to all the filesets on the master fileset list. It always 1049 * returns zero (0) if one fileset is populated / created, 1050 * otherwise it returns the sum of returned values from 1051 * fileset_create() and fileset_populate(), which 1052 * will be a negative one (-1) times the number of 1053 * fileset_create() calls which failed. 1054 */ 1055 int 1056 fileset_createset(fileset_t *fileset) 1057 { 1058 fileset_t *list; 1059 int ret = 0; 1060 1061 /* set up for possible parallel allocate */ 1062 paralloc_count = 0; 1063 1064 if (fileset && avd_get_bool(fileset->fs_prealloc)) { 1065 1066 filebench_log(LOG_INFO, 1067 "creating/pre-allocating %s %s", 1068 fileset_entity_name(fileset), 1069 avd_get_str(fileset->fs_name)); 1070 1071 if ((ret = fileset_populate(fileset)) != 0) 1072 return (ret); 1073 1074 if ((ret = fileset_create(fileset)) != 0) 1075 return (ret); 1076 } else { 1077 1078 filebench_log(LOG_INFO, 1079 "Creating/pre-allocating files and filesets"); 1080 1081 list = filebench_shm->filesetlist; 1082 while (list) { 1083 if ((ret = fileset_populate(list)) != 0) 1084 return (ret); 1085 if ((ret = fileset_create(list)) != 0) 1086 return (ret); 1087 list = list->fs_next; 1088 } 1089 } 1090 1091 /* wait for allocation threads to finish */ 1092 filebench_log(LOG_INFO, 1093 "waiting for fileset pre-allocation to finish"); 1094 1095 (void) pthread_mutex_lock(¶lloc_lock); 1096 while (paralloc_count > 0) 1097 (void) pthread_cond_wait(¶lloc_cv, ¶lloc_lock); 1098 (void) pthread_mutex_unlock(¶lloc_lock); 1099 1100 if (paralloc_count < 0) 1101 return (-1); 1102 1103 return (0); 1104 } 1105 1106 /* 1107 * Searches through the master fileset list for the named fileset. 1108 * If found, returns pointer to same, otherwise returns NULL. 1109 */ 1110 fileset_t * 1111 fileset_find(char *name) 1112 { 1113 fileset_t *fileset = filebench_shm->filesetlist; 1114 1115 (void) ipc_mutex_lock(&filebench_shm->fileset_lock); 1116 1117 while (fileset) { 1118 if (strcmp(name, avd_get_str(fileset->fs_name)) == 0) { 1119 (void) ipc_mutex_unlock(&filebench_shm->fileset_lock); 1120 return (fileset); 1121 } 1122 fileset = fileset->fs_next; 1123 } 1124 (void) ipc_mutex_unlock(&filebench_shm->fileset_lock); 1125 1126 return (NULL); 1127 } 1128 1129 /* 1130 * Iterates over all the file sets in the filesetlist, 1131 * executing the supplied command "*cmd()" on them. Also 1132 * indicates to the executed command if it is the first 1133 * time the command has been executed since the current 1134 * call to fileset_iter. 1135 */ 1136 void 1137 fileset_iter(int (*cmd)(fileset_t *fileset, int first)) 1138 { 1139 fileset_t *fileset = filebench_shm->filesetlist; 1140 int count = 0; 1141 1142 (void) ipc_mutex_lock(&filebench_shm->fileset_lock); 1143 1144 while (fileset) { 1145 cmd(fileset, count == 0); 1146 fileset = fileset->fs_next; 1147 count++; 1148 } 1149 1150 (void) ipc_mutex_unlock(&filebench_shm->fileset_lock); 1151 } 1152 1153 /* 1154 * Prints information to the filebench log about the file 1155 * object. Also prints a header on the first call. 1156 */ 1157 int 1158 fileset_print(fileset_t *fileset, int first) 1159 { 1160 int pathlength; 1161 char *fileset_path; 1162 char *fileset_name; 1163 static char pad[] = " "; /* 30 spaces */ 1164 1165 if ((fileset_path = avd_get_str(fileset->fs_path)) == NULL) { 1166 filebench_log(LOG_ERROR, "%s path not set", 1167 fileset_entity_name(fileset)); 1168 return (-1); 1169 } 1170 1171 if ((fileset_name = avd_get_str(fileset->fs_name)) == NULL) { 1172 filebench_log(LOG_ERROR, "%s name not set", 1173 fileset_entity_name(fileset)); 1174 return (-1); 1175 } 1176 1177 pathlength = strlen(fileset_path) + strlen(fileset_name); 1178 1179 if (pathlength > 29) 1180 pathlength = 29; 1181 1182 if (first) { 1183 filebench_log(LOG_INFO, "File or Fileset name%20s%12s%10s", 1184 "file size", 1185 "dir width", 1186 "entries"); 1187 } 1188 1189 if (fileset->fs_attrs & FILESET_IS_FILE) { 1190 if (fileset->fs_attrs & FILESET_IS_RAW_DEV) { 1191 filebench_log(LOG_INFO, 1192 "%s/%s%s (Raw Device)", 1193 fileset_path, fileset_name, &pad[pathlength]); 1194 } else { 1195 filebench_log(LOG_INFO, 1196 "%s/%s%s%9lld (Single File)", 1197 fileset_path, fileset_name, &pad[pathlength], 1198 avd_get_int(fileset->fs_size)); 1199 } 1200 } else { 1201 filebench_log(LOG_INFO, "%s/%s%s%9lld%12lld%10lld", 1202 fileset_path, fileset_name, 1203 &pad[pathlength], 1204 avd_get_int(fileset->fs_size), 1205 avd_get_int(fileset->fs_dirwidth), 1206 fileset->fs_constentries); 1207 } 1208 return (0); 1209 } 1210 /* 1211 * checks to see if the path/name pair points to a raw device. If 1212 * so it sets the raw device flag (FILESET_IS_RAW_DEV) and returns 1. 1213 * If RAW is not defined, or it is not a raw device, it clears the 1214 * raw device flag and returns 0. 1215 */ 1216 int 1217 fileset_checkraw(fileset_t *fileset) 1218 { 1219 char path[MAXPATHLEN]; 1220 struct stat64 sb; 1221 1222 fileset->fs_attrs &= (~FILESET_IS_RAW_DEV); 1223 1224 #ifdef HAVE_RAW_SUPPORT 1225 /* check for raw device */ 1226 (void) strcpy(path, avd_get_str(fileset->fs_path)); 1227 (void) strcat(path, "/"); 1228 (void) strcat(path, avd_get_str(fileset->fs_name)); 1229 if ((stat64(path, &sb) == 0) && 1230 ((sb.st_mode & S_IFMT) == S_IFBLK) && sb.st_rdev) { 1231 fileset->fs_attrs |= FILESET_IS_RAW_DEV; 1232 return (1); 1233 } 1234 #endif /* HAVE_RAW_SUPPORT */ 1235 1236 return (0); 1237 } 1238