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 2007 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 fs_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 /* 50 * Removes the last file or directory name from a pathname. 51 * Basically removes characters from the end of the path by 52 * setting them to \0 until a forward slash '/' is 53 * encountered. It also removes the forward slash. 54 */ 55 static char * 56 trunc_dirname(char *dir) 57 { 58 char *s = dir + strlen(dir); 59 60 while (s != dir) { 61 int c = *s; 62 63 *s = 0; 64 if (c == '/') 65 break; 66 s--; 67 } 68 return (dir); 69 } 70 71 /* 72 * Prints a list of allowed options and how to specify them. 73 */ 74 void 75 fileset_usage(void) 76 { 77 (void) fprintf(stderr, "define fileset name=<name>,path=<pathname>," 78 "entries=<number>\n"); 79 (void) fprintf(stderr, " [,dirwidth=[width]\n"); 80 (void) fprintf(stderr, " [,dirgamma=[100-10000] " 81 "(Gamma * 1000)\n"); 82 (void) fprintf(stderr, 83 " [,sizegamma=[100-10000] (Gamma * 1000)\n"); 84 (void) fprintf(stderr, 85 " [,prealloc=[percent]]\n"); 86 (void) fprintf(stderr, " [,reuse]\n"); 87 (void) fprintf(stderr, "\n"); 88 } 89 90 /* 91 * Frees up memory mapped file region of supplied size. The 92 * file descriptor "fd" indicates which memory mapped file. 93 * If successful, returns 0. Otherwise returns -1 if "size" 94 * is zero, or -1 times the number of times msync() failed. 95 */ 96 static int 97 fileset_freemem(int fd, off64_t size) 98 { 99 off64_t left; 100 int ret = 0; 101 102 for (left = size; left > 0; left -= MMAP_SIZE) { 103 off64_t thismapsize; 104 caddr_t addr; 105 106 thismapsize = MIN(MMAP_SIZE, left); 107 addr = mmap64(0, thismapsize, PROT_READ|PROT_WRITE, 108 MAP_SHARED, fd, size - left); 109 ret += msync(addr, thismapsize, MS_INVALIDATE); 110 (void) munmap(addr, thismapsize); 111 } 112 return (ret); 113 } 114 115 /* 116 * Creates a path string from the filesetentry_t "*entry" 117 * and all of its parent's path names. The resulting path 118 * is a concatination of all the individual parent paths. 119 * Allocates memory for the path string and returns a 120 * pointer to it. 121 */ 122 char * 123 fileset_resolvepath(filesetentry_t *entry) 124 { 125 filesetentry_t *fsep = entry; 126 char path[MAXPATHLEN]; 127 char pathtmp[MAXPATHLEN]; 128 char *s; 129 130 *path = 0; 131 while (fsep->fse_parent) { 132 (void) strcpy(pathtmp, "/"); 133 (void) strcat(pathtmp, fsep->fse_path); 134 (void) strcat(pathtmp, path); 135 (void) strcpy(path, pathtmp); 136 fsep = fsep->fse_parent; 137 } 138 139 s = malloc(strlen(path) + 1); 140 (void) strcpy(s, path); 141 return (s); 142 } 143 144 /* 145 * Creates multiple nested directories as required by the 146 * supplied path. Starts at the end of the path, creating 147 * a list of directories to mkdir, up to the root of the 148 * path, then mkdirs them one at a time from the root on down. 149 */ 150 static int 151 fileset_mkdir(char *path, int mode) 152 { 153 char *p; 154 char *dirs[65536]; 155 int i = 0; 156 157 if ((p = strdup(path)) == NULL) 158 goto null_str; 159 160 /* 161 * Fill an array of subdirectory path names until either we 162 * reach the root or encounter an already existing subdirectory 163 */ 164 /* CONSTCOND */ 165 while (1) { 166 struct stat64 sb; 167 168 if (stat64(p, &sb) == 0) 169 break; 170 if (strlen(p) < 3) 171 break; 172 if ((dirs[i] = strdup(p)) == NULL) { 173 free(p); 174 goto null_str; 175 } 176 177 (void) trunc_dirname(p); 178 i++; 179 } 180 181 /* Make the directories, from closest to root downwards. */ 182 for (--i; i >= 0; i--) { 183 (void) mkdir(dirs[i], mode); 184 free(dirs[i]); 185 } 186 187 free(p); 188 return (0); 189 190 null_str: 191 /* clean up */ 192 for (--i; i >= 0; i--) 193 free(dirs[i]); 194 195 filebench_log(LOG_ERROR, 196 "Failed to create directory path %s: Out of memory", path); 197 198 return (-1); 199 } 200 201 202 /* 203 * First creates the parent directories of the file using 204 * fileset_mkdir(). Then Optionally sets the O_DSYNC flag 205 * and opens the file with open64(). It unlocks the fileset 206 * entry lock, sets the DIRECTIO_ON or DIRECTIO_OFF flags 207 * as requested, and returns the file descriptor integer 208 * for the opened file. 209 */ 210 int 211 fileset_openfile(fileset_t *fileset, 212 filesetentry_t *entry, int flag, int mode, int attrs) 213 { 214 char path[MAXPATHLEN]; 215 char dir[MAXPATHLEN]; 216 char *pathtmp; 217 struct stat64 sb; 218 int fd; 219 int open_attrs = 0; 220 221 *path = 0; 222 (void) strcpy(path, *fileset->fs_path); 223 (void) strcat(path, "/"); 224 (void) strcat(path, fileset->fs_name); 225 pathtmp = fileset_resolvepath(entry); 226 (void) strcat(path, pathtmp); 227 (void) strcpy(dir, path); 228 free(pathtmp); 229 (void) trunc_dirname(dir); 230 231 /* If we are going to create a file, create the parent dirs */ 232 if ((flag & O_CREAT) && (stat64(dir, &sb) != 0)) { 233 if (fileset_mkdir(dir, 0755) == -1) 234 return (-1); 235 } 236 237 if (flag & O_CREAT) 238 entry->fse_flags |= FSE_EXISTS; 239 240 if (attrs & FLOW_ATTR_DSYNC) { 241 #ifdef sun 242 open_attrs |= O_DSYNC; 243 #else 244 open_attrs |= O_FSYNC; 245 #endif 246 } 247 248 if ((fd = open64(path, flag | open_attrs, mode)) < 0) { 249 filebench_log(LOG_ERROR, 250 "Failed to open file %s: %s", 251 path, strerror(errno)); 252 (void) ipc_mutex_unlock(&entry->fse_lock); 253 return (-1); 254 } 255 (void) ipc_mutex_unlock(&entry->fse_lock); 256 257 #ifdef sun 258 if (attrs & FLOW_ATTR_DIRECTIO) 259 (void) directio(fd, DIRECTIO_ON); 260 else 261 (void) directio(fd, DIRECTIO_OFF); 262 #endif 263 264 return (fd); 265 } 266 267 268 /* 269 * Selects a fileset entry from a fileset. If the 270 * FILESET_PICKDIR flag is set it will pick a directory 271 * entry, otherwise a file entry. The FILESET_PICKRESET 272 * flag will cause it to reset the free list to the 273 * overall list (file or directory). The FILESET_PICKUNIQUE 274 * flag will take an entry off of one of the free (unused) 275 * lists (file or directory), otherwise the entry will be 276 * picked off of one of the rotor lists (file or directory). 277 * The FILESET_PICKEXISTS will insure that only extant 278 * (FSE_EXISTS) state files are selected, while 279 * FILESET_PICKNOEXIST insures that only non extant 280 * (not FSE_EXISTS) state files are selected. 281 */ 282 filesetentry_t * 283 fileset_pick(fileset_t *fileset, int flags, int tid) 284 { 285 filesetentry_t *entry = NULL; 286 filesetentry_t *first = NULL; 287 288 (void) ipc_mutex_lock(&filebench_shm->fileset_lock); 289 290 while (entry == NULL) { 291 292 if ((flags & FILESET_PICKDIR) && (flags & FILESET_PICKRESET)) { 293 entry = fileset->fs_dirlist; 294 while (entry) { 295 entry->fse_flags |= FSE_FREE; 296 entry = entry->fse_dirnext; 297 } 298 fileset->fs_dirfree = fileset->fs_dirlist; 299 } 300 301 if (!(flags & FILESET_PICKDIR) && (flags & FILESET_PICKRESET)) { 302 entry = fileset->fs_filelist; 303 while (entry) { 304 entry->fse_flags |= FSE_FREE; 305 entry = entry->fse_filenext; 306 } 307 fileset->fs_filefree = fileset->fs_filelist; 308 } 309 310 if (flags & FILESET_PICKUNIQUE) { 311 if (flags & FILESET_PICKDIR) { 312 entry = fileset->fs_dirfree; 313 if (entry == NULL) 314 goto empty; 315 fileset->fs_dirfree = entry->fse_dirnext; 316 } else { 317 entry = fileset->fs_filefree; 318 if (entry == NULL) 319 goto empty; 320 fileset->fs_filefree = entry->fse_filenext; 321 } 322 entry->fse_flags &= ~FSE_FREE; 323 } else { 324 if (flags & FILESET_PICKDIR) { 325 entry = fileset->fs_dirrotor; 326 if (entry == NULL) 327 fileset->fs_dirrotor = 328 entry = fileset->fs_dirlist; 329 fileset->fs_dirrotor = entry->fse_dirnext; 330 } else { 331 entry = fileset->fs_filerotor[tid]; 332 if (entry == NULL) 333 fileset->fs_filerotor[tid] = 334 entry = fileset->fs_filelist; 335 fileset->fs_filerotor[tid] = 336 entry->fse_filenext; 337 } 338 } 339 340 if (first == entry) 341 goto empty; 342 343 if (first == NULL) 344 first = entry; 345 346 /* Return locked entry */ 347 (void) ipc_mutex_lock(&entry->fse_lock); 348 349 /* If we ask for an existing file, go round again */ 350 if ((flags & FILESET_PICKEXISTS) && 351 !(entry->fse_flags & FSE_EXISTS)) { 352 (void) ipc_mutex_unlock(&entry->fse_lock); 353 entry = NULL; 354 } 355 356 /* If we ask for not an existing file, go round again */ 357 if ((flags & FILESET_PICKNOEXIST) && 358 (entry->fse_flags & FSE_EXISTS)) { 359 (void) ipc_mutex_unlock(&entry->fse_lock); 360 entry = NULL; 361 } 362 } 363 364 (void) ipc_mutex_unlock(&filebench_shm->fileset_lock); 365 filebench_log(LOG_DEBUG_SCRIPT, "Picked file %s", entry->fse_path); 366 return (entry); 367 368 empty: 369 (void) ipc_mutex_unlock(&filebench_shm->fileset_lock); 370 return (NULL); 371 } 372 373 /* 374 * Given a fileset "fileset", create the associated files as 375 * specified in the attributes of the fileset. The fileset is 376 * rooted in a directory whose pathname is in fs_path. If the 377 * directory exists, meaning that there is already a fileset, 378 * and the fs_reuse attribute is false, then remove it and all 379 * its contained files and subdirectories. Next, the routine 380 * creates a root directory for the fileset. All the file type 381 * filesetentries are cycled through creating as needed 382 * their containing subdirectory trees in the filesystem and 383 * creating actual files for fs_preallocpercent of them. The 384 * created files are filled with fse_size bytes of unitialized 385 * data. The routine returns -1 on errors, 0 on success. 386 */ 387 static int 388 fileset_create(fileset_t *fileset) 389 { 390 filesetentry_t *entry; 391 char path[MAXPATHLEN]; 392 char *buf; 393 struct stat64 sb; 394 int pickflags = FILESET_PICKUNIQUE | FILESET_PICKRESET; 395 hrtime_t start = gethrtime(); 396 int preallocated = 0; 397 int reusing = 0; 398 399 if ((buf = (char *)malloc(FILE_ALLOC_BLOCK)) == NULL) 400 return (-1); 401 402 if (*fileset->fs_path == NULL) { 403 filebench_log(LOG_ERROR, "Fileset path not set"); 404 return (-1); 405 } 406 407 /* XXX Add check to see if there is enough space */ 408 409 /* Remove existing */ 410 (void) strcpy(path, *fileset->fs_path); 411 (void) strcat(path, "/"); 412 (void) strcat(path, fileset->fs_name); 413 if ((stat64(path, &sb) == 0) && (strlen(path) > 3) && 414 (strlen(*fileset->fs_path) > 2)) { 415 if (!integer_isset(fileset->fs_reuse)) { 416 char cmd[MAXPATHLEN]; 417 418 (void) snprintf(cmd, sizeof (cmd), "rm -rf %s", path); 419 (void) system(cmd); 420 filebench_log(LOG_VERBOSE, 421 "Removed any existing fileset %s in %d seconds", 422 fileset->fs_name, 423 ((gethrtime() - start) / 1000000000) + 1); 424 } else { 425 /* we are re-using */ 426 reusing = 1; 427 filebench_log(LOG_VERBOSE, 428 "Re-using fileset %s on %s file system.", 429 fileset->fs_name, sb.st_fstype); 430 } 431 } 432 (void) mkdir(path, 0755); 433 434 start = gethrtime(); 435 436 filebench_log(LOG_VERBOSE, "Creating fileset %s...", 437 fileset->fs_name); 438 439 while (entry = fileset_pick(fileset, pickflags, 0)) { 440 char dir[MAXPATHLEN]; 441 char *pathtmp; 442 off64_t seek; 443 int fd; 444 int randno; 445 446 pickflags = FILESET_PICKUNIQUE; 447 448 entry->fse_flags &= ~FSE_EXISTS; 449 450 if (!integer_isset(fileset->fs_prealloc)) { 451 (void) ipc_mutex_unlock(&entry->fse_lock); 452 continue; 453 } 454 455 *path = 0; 456 (void) strcpy(path, *fileset->fs_path); 457 (void) strcat(path, "/"); 458 (void) strcat(path, fileset->fs_name); 459 pathtmp = fileset_resolvepath(entry); 460 (void) strcat(path, pathtmp); 461 (void) strcpy(dir, path); 462 free(pathtmp); 463 464 (void) trunc_dirname(dir); 465 466 if (stat64(dir, &sb) != 0) { 467 if (fileset_mkdir(dir, 0775) == -1) { 468 (void) ipc_mutex_unlock(&entry->fse_lock); 469 return (-1); 470 } 471 } 472 473 randno = ((RAND_MAX * (100 - *(fileset->fs_preallocpercent))) 474 / 100); 475 476 if (rand() < randno) { 477 (void) ipc_mutex_unlock(&entry->fse_lock); 478 continue; 479 } 480 481 preallocated++; 482 483 filebench_log(LOG_DEBUG_IMPL, "Populated %s", entry->fse_path); 484 485 /* see if reusing and this file exists */ 486 if (reusing && (stat64(path, &sb) == 0)) { 487 if ((fd = open64(path, O_RDWR)) < 0) { 488 filebench_log(LOG_INFO, 489 "Attempted but failed to Re-use file %s", 490 path); 491 (void) ipc_mutex_unlock(&entry->fse_lock); 492 return (-1); 493 } 494 495 if (sb.st_size == (off64_t)entry->fse_size) { 496 filebench_log(LOG_INFO, 497 "Re-using file %s", path); 498 499 if (!integer_isset(fileset->fs_cached)) 500 (void) fileset_freemem(fd, 501 entry->fse_size); 502 503 entry->fse_flags |= FSE_EXISTS; 504 (void) close(fd); 505 (void) ipc_mutex_unlock(&entry->fse_lock); 506 continue; 507 508 } else if (sb.st_size > (off64_t)entry->fse_size) { 509 /* reuse, but too large */ 510 filebench_log(LOG_INFO, 511 "Truncating & re-using file %s", path); 512 513 (void) ftruncate64(fd, 514 (off64_t)entry->fse_size); 515 516 if (!integer_isset(fileset->fs_cached)) 517 (void) fileset_freemem(fd, 518 entry->fse_size); 519 520 entry->fse_flags |= FSE_EXISTS; 521 (void) close(fd); 522 (void) ipc_mutex_unlock(&entry->fse_lock); 523 continue; 524 } 525 } else { 526 527 /* No file or not reusing, so create */ 528 if ((fd = open64(path, O_RDWR | O_CREAT, 0644)) < 0) { 529 filebench_log(LOG_ERROR, 530 "Failed to pre-allocate file %s: %s", 531 path, strerror(errno)); 532 533 return (-1); 534 } 535 } 536 537 entry->fse_flags |= FSE_EXISTS; 538 539 for (seek = 0; seek < entry->fse_size; ) { 540 off64_t wsize; 541 int ret = 0; 542 543 /* 544 * Write FILE_ALLOC_BLOCK's worth, 545 * except on last write 546 */ 547 wsize = MIN(entry->fse_size - seek, FILE_ALLOC_BLOCK); 548 549 ret = write(fd, buf, wsize); 550 if (ret != wsize) { 551 filebench_log(LOG_ERROR, 552 "Failed to pre-allocate file %s: %s", 553 path, strerror(errno)); 554 (void) close(fd); 555 return (-1); 556 } 557 seek += wsize; 558 } 559 560 if (!integer_isset(fileset->fs_cached)) 561 (void) fileset_freemem(fd, entry->fse_size); 562 563 (void) close(fd); 564 (void) ipc_mutex_unlock(&entry->fse_lock); 565 566 filebench_log(LOG_DEBUG_IMPL, 567 "Pre-allocated file %s size %lld", path, entry->fse_size); 568 } 569 filebench_log(LOG_VERBOSE, 570 "Preallocated %d of %lld of fileset %s in %d seconds", 571 preallocated, 572 *(fileset->fs_entries), 573 fileset->fs_name, 574 ((gethrtime() - start) / 1000000000) + 1); 575 576 free(buf); 577 return (0); 578 } 579 580 /* 581 * Adds an entry to the fileset's file list. Single threaded so 582 * no locking needed. 583 */ 584 static void 585 fileset_insfilelist(fileset_t *fileset, filesetentry_t *entry) 586 { 587 if (fileset->fs_filelist == NULL) { 588 fileset->fs_filelist = entry; 589 entry->fse_filenext = NULL; 590 } else { 591 entry->fse_filenext = fileset->fs_filelist; 592 fileset->fs_filelist = entry; 593 } 594 } 595 596 /* 597 * Adds an entry to the fileset's directory list. Single 598 * threaded so no locking needed. 599 */ 600 static void 601 fileset_insdirlist(fileset_t *fileset, filesetentry_t *entry) 602 { 603 if (fileset->fs_dirlist == NULL) { 604 fileset->fs_dirlist = entry; 605 entry->fse_dirnext = NULL; 606 } else { 607 entry->fse_dirnext = fileset->fs_dirlist; 608 fileset->fs_dirlist = entry; 609 } 610 } 611 612 /* 613 * Obtaines a filesetentry entity for a file to be placed in a 614 * (sub)directory of a fileset. The size of the file may be 615 * specified by fs_meansize, or calculated from a gamma 616 * distribution of parameter fs_sizegamma and of mean size 617 * fs_meansize. The filesetentry entity is placed on the file 618 * list in the specified parent filesetentry entity, which may 619 * be a directory filesetentry, or the root filesetentry in the 620 * fileset. It is also placed on the fileset's list of all 621 * contained files. Returns 0 if successful or -1 if ipc memory 622 * for the path string cannot be allocated. 623 */ 624 static int 625 fileset_populate_file(fileset_t *fileset, filesetentry_t *parent, int serial) 626 { 627 char tmpname[16]; 628 filesetentry_t *entry; 629 double drand; 630 double gamma; 631 632 if ((entry = (filesetentry_t *)ipc_malloc(FILEBENCH_FILESETENTRY)) 633 == NULL) { 634 filebench_log(LOG_ERROR, 635 "fileset_populate_file: Can't malloc filesetentry"); 636 return (-1); 637 } 638 639 (void) pthread_mutex_init(&entry->fse_lock, ipc_mutexattr()); 640 entry->fse_parent = parent; 641 entry->fse_fileset = fileset; 642 entry->fse_flags |= FSE_FREE; 643 fileset_insfilelist(fileset, entry); 644 645 (void) snprintf(tmpname, sizeof (tmpname), "%08d", serial); 646 if ((entry->fse_path = (char *)ipc_pathalloc(tmpname)) == NULL) { 647 filebench_log(LOG_ERROR, 648 "fileset_populate_file: Can't alloc path string"); 649 return (-1); 650 } 651 652 gamma = *(fileset->fs_sizegamma) / 1000.0; 653 654 if (gamma > 0) { 655 drand = gamma_dist_knuth(gamma, fileset->fs_meansize / gamma); 656 entry->fse_size = (off64_t)drand; 657 } else { 658 entry->fse_size = (off64_t)fileset->fs_meansize; 659 } 660 661 fileset->fs_bytes += entry->fse_size; 662 663 fileset->fs_realfiles++; 664 return (0); 665 } 666 667 /* 668 * Creates a directory node in a fileset, by obtaining a 669 * filesetentry entity for the node and initializing it 670 * according to parameters of the fileset. It determines a 671 * directory tree depth and directory width, optionally using 672 * a gamma distribution. If its calculated depth is less then 673 * its actual depth in the directory tree, it becomes a leaf 674 * node and files itself with "width" number of file type 675 * filesetentries, otherwise it files itself with "width" 676 * number of directory type filesetentries, using recursive 677 * calls to fileset_populate_subdir. The end result of the 678 * initial call to this routine is a tree of directories of 679 * random width and varying depth with sufficient leaf 680 * directories to contain all required files. 681 * Returns 0 on success. Returns -1 if ipc path string memory 682 * cannot be allocated and returns an error code (currently 683 * also -1) from calls to fileset_populate_file or recursive 684 * calls to fileset_populate_subdir. 685 */ 686 static int 687 fileset_populate_subdir(fileset_t *fileset, filesetentry_t *parent, 688 int serial, double depth) 689 { 690 double randepth, drand, ranwidth, gamma; 691 int isleaf = 0; 692 char tmpname[16]; 693 filesetentry_t *entry; 694 int i; 695 696 depth += 1; 697 698 /* Create dir node */ 699 if ((entry = (filesetentry_t *)ipc_malloc(FILEBENCH_FILESETENTRY)) 700 == NULL) { 701 filebench_log(LOG_ERROR, 702 "fileset_populate_subdir: Can't malloc filesetentry"); 703 return (-1); 704 } 705 706 (void) pthread_mutex_init(&entry->fse_lock, ipc_mutexattr()); 707 708 (void) snprintf(tmpname, sizeof (tmpname), "%08d", serial); 709 if ((entry->fse_path = (char *)ipc_pathalloc(tmpname)) == NULL) { 710 filebench_log(LOG_ERROR, 711 "fileset_populate_subdir: Can't alloc path string"); 712 return (-1); 713 } 714 715 entry->fse_parent = parent; 716 entry->fse_flags |= FSE_DIR | FSE_FREE; 717 fileset_insdirlist(fileset, entry); 718 719 gamma = *(fileset->fs_dirgamma) / 1000.0; 720 if (gamma > 0) { 721 drand = gamma_dist_knuth(gamma, fileset->fs_meandepth / gamma); 722 randepth = (int)drand; 723 } else { 724 randepth = (int)fileset->fs_meandepth; 725 } 726 727 gamma = *(fileset->fs_sizegamma) / 1000.0; 728 729 if (gamma > 0) { 730 drand = gamma_dist_knuth(gamma, fileset->fs_meanwidth / gamma); 731 ranwidth = drand; 732 } else { 733 ranwidth = fileset->fs_meanwidth; 734 } 735 736 if (randepth == 0) 737 randepth = 1; 738 if (ranwidth == 0) 739 ranwidth = 1; 740 if (depth >= randepth) 741 isleaf = 1; 742 743 /* 744 * Create directory of random width according to distribution, or 745 * if root directory, continue until #files required 746 */ 747 for (i = 1; 748 ((parent == NULL) || (i < ranwidth + 1)) && 749 (fileset->fs_realfiles < *(fileset->fs_entries)); i++) { 750 int ret = 0; 751 752 if (parent && isleaf) 753 ret = fileset_populate_file(fileset, entry, i); 754 else 755 ret = fileset_populate_subdir(fileset, entry, i, depth); 756 757 if (ret != 0) 758 return (ret); 759 } 760 return (0); 761 } 762 763 /* 764 * Populates a fileset with files and subdirectory entries. Uses 765 * the supplied fs_dirwidth and fs_entries (number of files) to 766 * calculate the required fs_meandepth (of subdirectories) and 767 * initialize the fs_meanwidth and fs_meansize variables. Then 768 * calls fileset_populate_subdir() to do the recursive 769 * subdirectory entry creation and leaf file entry creation. All 770 * of the above is skipped if the fileset has already been 771 * populated. Returns 0 on success, or an error code from the 772 * call to fileset_populate_subdir if that call fails. 773 */ 774 static int 775 fileset_populate(fileset_t *fileset) 776 { 777 int nfiles; 778 int meandirwidth = *(fileset->fs_dirwidth); 779 int ret; 780 781 /* Skip if already populated */ 782 if (fileset->fs_bytes > 0) 783 goto exists; 784 785 /* 786 * Input params are: 787 * # of files 788 * ave # of files per dir 789 * max size of dir 790 * # ave size of file 791 * max size of file 792 */ 793 nfiles = *(fileset->fs_entries); 794 fileset->fs_meandepth = log(nfiles) / log(meandirwidth); 795 fileset->fs_meanwidth = meandirwidth; 796 fileset->fs_meansize = *(fileset->fs_size); 797 798 if ((ret = fileset_populate_subdir(fileset, NULL, 1, 0)) != 0) 799 return (ret); 800 801 802 exists: 803 filebench_log(LOG_VERBOSE, "Fileset %s: %lld files, " 804 "avg dir = %.1lf, avg depth = %.1lf, mbytes=%lld", 805 fileset->fs_name, 806 *(fileset->fs_entries), 807 fileset->fs_meanwidth, 808 fileset->fs_meandepth, 809 fileset->fs_bytes / 1024UL / 1024UL); 810 811 return (0); 812 } 813 814 /* 815 * Allocates a fileset instance, initializes fs_dirgamma and 816 * fs_sizegamma default values, and sets the fileset name to the 817 * supplied name string. Puts the allocated fileset on the 818 * master fileset list and returns a pointer to it. 819 */ 820 fileset_t * 821 fileset_define(char *name) 822 { 823 fileset_t *fileset; 824 825 if (name == NULL) 826 return (NULL); 827 828 if ((fileset = (fileset_t *)ipc_malloc(FILEBENCH_FILESET)) == NULL) { 829 filebench_log(LOG_ERROR, 830 "fileset_define: Can't malloc fileset"); 831 return (NULL); 832 } 833 834 filebench_log(LOG_DEBUG_IMPL, "Defining file %s", name); 835 836 (void) ipc_mutex_lock(&filebench_shm->fileset_lock); 837 838 fileset->fs_dirgamma = integer_alloc(1500); 839 fileset->fs_sizegamma = integer_alloc(1500); 840 841 /* Add fileset to global list */ 842 if (filebench_shm->filesetlist == NULL) { 843 filebench_shm->filesetlist = fileset; 844 fileset->fs_next = NULL; 845 } else { 846 fileset->fs_next = filebench_shm->filesetlist; 847 filebench_shm->filesetlist = fileset; 848 } 849 850 (void) ipc_mutex_unlock(&filebench_shm->fileset_lock); 851 852 (void) strcpy(fileset->fs_name, name); 853 854 return (fileset); 855 } 856 857 /* 858 * If supplied with a pointer to a fileset and the fileset's 859 * fs_prealloc flag is set, calls fileset_populate() to populate 860 * the fileset with filesetentries, then calls fileset_create() 861 * to make actual directories and files for the filesetentries. 862 * Otherwise, it applies fileset_populate() and fileset_create() 863 * to all the filesets on the master fileset list. It always 864 * returns zero (0) if one fileset is populated / created, 865 * otherwise it returns the sum of returned values from 866 * fileset_create() and fileset_populate(), which 867 * will be a negative one (-1) times the number of 868 * fileset_create() calls which failed. 869 */ 870 int 871 fileset_createset(fileset_t *fileset) 872 { 873 fileset_t *list; 874 int ret = 0; 875 876 if (fileset && integer_isset(fileset->fs_prealloc)) { 877 if ((ret = fileset_populate(fileset)) != 0) 878 return (ret); 879 return (fileset_create(fileset)); 880 } 881 882 list = filebench_shm->filesetlist; 883 while (list) { 884 ret += fileset_populate(list); 885 ret += fileset_create(list); 886 list = list->fs_next; 887 } 888 889 return (ret); 890 } 891 892 /* 893 * Searches through the master fileset list for the named fileset. 894 * If found, returns pointer to same, otherwise returns NULL. 895 */ 896 fileset_t * 897 fileset_find(char *name) 898 { 899 fileset_t *fileset = filebench_shm->filesetlist; 900 901 (void) ipc_mutex_lock(&filebench_shm->fileset_lock); 902 903 while (fileset) { 904 if (strcmp(name, fileset->fs_name) == 0) { 905 (void) ipc_mutex_unlock(&filebench_shm->fileset_lock); 906 return (fileset); 907 } 908 fileset = fileset->fs_next; 909 } 910 (void) ipc_mutex_unlock(&filebench_shm->fileset_lock); 911 912 return (NULL); 913 } 914