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