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