15184Sek110237 /*
25184Sek110237  * CDDL HEADER START
35184Sek110237  *
45184Sek110237  * The contents of this file are subject to the terms of the
55184Sek110237  * Common Development and Distribution License (the "License").
65184Sek110237  * You may not use this file except in compliance with the License.
75184Sek110237  *
85184Sek110237  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
95184Sek110237  * or http://www.opensolaris.org/os/licensing.
105184Sek110237  * See the License for the specific language governing permissions
115184Sek110237  * and limitations under the License.
125184Sek110237  *
135184Sek110237  * When distributing Covered Code, include this CDDL HEADER in each
145184Sek110237  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
155184Sek110237  * If applicable, add the following below this CDDL HEADER, with the
165184Sek110237  * fields enclosed by brackets "[]" replaced with your own identifying
175184Sek110237  * information: Portions Copyright [yyyy] [name of copyright owner]
185184Sek110237  *
195184Sek110237  * CDDL HEADER END
205184Sek110237  */
215184Sek110237 /*
226212Saw148015  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
235184Sek110237  * Use is subject to license terms.
246613Sek110237  *
256613Sek110237  * Portions Copyright 2008 Denis Cheng
265184Sek110237  */
275184Sek110237 
285184Sek110237 #include <fcntl.h>
295184Sek110237 #include <pthread.h>
305184Sek110237 #include <errno.h>
315184Sek110237 #include <math.h>
325184Sek110237 #include <libgen.h>
335184Sek110237 #include <sys/mman.h>
346613Sek110237 
356613Sek110237 #include "filebench.h"
365184Sek110237 #include "fileset.h"
375184Sek110237 #include "gamma_dist.h"
385184Sek110237 
395184Sek110237 /*
405184Sek110237  * File sets, of type fileset_t, are entities which contain
415184Sek110237  * information about collections of files and subdirectories in Filebench.
425184Sek110237  * The fileset, once populated, consists of a tree of fileset entries of
435184Sek110237  * type filesetentry_t which specify files and directories.  The fileset
446212Saw148015  * is rooted in a directory specified by fileset_path, and once the populated
455184Sek110237  * fileset has been created, has a tree of directories and files
465184Sek110237  * corresponding to the fileset's filesetentry tree.
476701Saw148015  *
487556SAndrew.W.Wilson@sun.com  * Fileset entities are allocated by fileset_define() which is called from
497556SAndrew.W.Wilson@sun.com  * parser_gram.y: parser_fileset_define(). The filesetentry tree corrseponding
507556SAndrew.W.Wilson@sun.com  * to the eventual directory and file tree to be instantiated on the storage
517556SAndrew.W.Wilson@sun.com  * medium is built by fileset_populate(), which is called from
527556SAndrew.W.Wilson@sun.com  * fileset_createset(). After calling fileset_populate(), fileset_createset()
537556SAndrew.W.Wilson@sun.com  * will call fileset_create() to pre-allocate designated files and directories.
547556SAndrew.W.Wilson@sun.com  *
557556SAndrew.W.Wilson@sun.com  * Fileset_createset() is called from parser_gram.y: parser_create_fileset()
567556SAndrew.W.Wilson@sun.com  * when a "create fileset" or "run" command is encountered. When the
577556SAndrew.W.Wilson@sun.com  * "create fileset" command is used, it is generally paired with
586701Saw148015  * a "create processes" command, and must appear first, in order to
596701Saw148015  * instantiate all the files in the fileset before trying to use them.
605184Sek110237  */
615184Sek110237 
626305Saw148015 static int fileset_checkraw(fileset_t *fileset);
636305Saw148015 
647556SAndrew.W.Wilson@sun.com /* maximum parallel allocation control */
655673Saw148015 #define	MAX_PARALLOC_THREADS 32
665673Saw148015 
675673Saw148015 /*
685673Saw148015  * returns pointer to file or fileset
695673Saw148015  * string, as appropriate
705673Saw148015  */
715673Saw148015 static char *
725673Saw148015 fileset_entity_name(fileset_t *fileset)
735673Saw148015 {
745673Saw148015 	if (fileset->fs_attrs & FILESET_IS_FILE)
755673Saw148015 		return ("file");
765673Saw148015 	else
775673Saw148015 		return ("fileset");
785673Saw148015 }
795673Saw148015 
805184Sek110237 /*
815184Sek110237  * Removes the last file or directory name from a pathname.
825184Sek110237  * Basically removes characters from the end of the path by
835184Sek110237  * setting them to \0 until a forward slash '/' is
845184Sek110237  * encountered. It also removes the forward slash.
855184Sek110237  */
865184Sek110237 static char *
875184Sek110237 trunc_dirname(char *dir)
885184Sek110237 {
895184Sek110237 	char *s = dir + strlen(dir);
905184Sek110237 
915184Sek110237 	while (s != dir) {
925184Sek110237 		int c = *s;
935184Sek110237 
945184Sek110237 		*s = 0;
955184Sek110237 		if (c == '/')
965184Sek110237 			break;
975184Sek110237 		s--;
985184Sek110237 	}
995184Sek110237 	return (dir);
1005184Sek110237 }
1015184Sek110237 
1025184Sek110237 /*
1035184Sek110237  * Prints a list of allowed options and how to specify them.
1045184Sek110237  */
1055184Sek110237 void
1065184Sek110237 fileset_usage(void)
1075184Sek110237 {
1085673Saw148015 	(void) fprintf(stderr,
1095673Saw148015 	    "define [file name=<name> | fileset name=<name>],path=<pathname>,"
1105673Saw148015 	    ",entries=<number>\n");
1115673Saw148015 	(void) fprintf(stderr,
1126212Saw148015 	    "		        [,filesize=[size]]\n");
1136212Saw148015 	(void) fprintf(stderr,
1145673Saw148015 	    "		        [,dirwidth=[width]]\n");
1155673Saw148015 	(void) fprintf(stderr,
1166212Saw148015 	    "		        [,dirdepthrv=$random_variable_name]\n");
1176212Saw148015 	(void) fprintf(stderr,
1185673Saw148015 	    "		        [,dirgamma=[100-10000]] "
1195184Sek110237 	    "(Gamma * 1000)\n");
1205184Sek110237 	(void) fprintf(stderr,
1215673Saw148015 	    "		        [,sizegamma=[100-10000]] (Gamma * 1000)\n");
1225184Sek110237 	(void) fprintf(stderr,
1235184Sek110237 	    "		        [,prealloc=[percent]]\n");
1245673Saw148015 	(void) fprintf(stderr, "		        [,paralloc]\n");
1255184Sek110237 	(void) fprintf(stderr, "		        [,reuse]\n");
1265184Sek110237 	(void) fprintf(stderr, "\n");
1275184Sek110237 }
1285184Sek110237 
1295184Sek110237 /*
1305184Sek110237  * Frees up memory mapped file region of supplied size. The
1315184Sek110237  * file descriptor "fd" indicates which memory mapped file.
1327556SAndrew.W.Wilson@sun.com  * If successful, returns 0. Otherwise returns -1 times the number of
1337556SAndrew.W.Wilson@sun.com  * times msync() failed.
1345184Sek110237  */
1355184Sek110237 static int
1365184Sek110237 fileset_freemem(int fd, off64_t size)
1375184Sek110237 {
1385184Sek110237 	off64_t left;
1395184Sek110237 	int ret = 0;
1405184Sek110237 
1415184Sek110237 	for (left = size; left > 0; left -= MMAP_SIZE) {
1425184Sek110237 		off64_t thismapsize;
1435184Sek110237 		caddr_t addr;
1445184Sek110237 
1455184Sek110237 		thismapsize = MIN(MMAP_SIZE, left);
1465184Sek110237 		addr = mmap64(0, thismapsize, PROT_READ|PROT_WRITE,
1475184Sek110237 		    MAP_SHARED, fd, size - left);
1485184Sek110237 		ret += msync(addr, thismapsize, MS_INVALIDATE);
1495184Sek110237 		(void) munmap(addr, thismapsize);
1505184Sek110237 	}
1515184Sek110237 	return (ret);
1525184Sek110237 }
1535184Sek110237 
1545184Sek110237 /*
1555184Sek110237  * Creates a path string from the filesetentry_t "*entry"
1565184Sek110237  * and all of its parent's path names. The resulting path
1575184Sek110237  * is a concatination of all the individual parent paths.
1585184Sek110237  * Allocates memory for the path string and returns a
1595184Sek110237  * pointer to it.
1605184Sek110237  */
1615184Sek110237 char *
1625184Sek110237 fileset_resolvepath(filesetentry_t *entry)
1635184Sek110237 {
1645184Sek110237 	filesetentry_t *fsep = entry;
1655184Sek110237 	char path[MAXPATHLEN];
1665184Sek110237 	char pathtmp[MAXPATHLEN];
1675184Sek110237 	char *s;
1685184Sek110237 
1695184Sek110237 	*path = 0;
1705184Sek110237 	while (fsep->fse_parent) {
1715184Sek110237 		(void) strcpy(pathtmp, "/");
1725184Sek110237 		(void) strcat(pathtmp, fsep->fse_path);
1735184Sek110237 		(void) strcat(pathtmp, path);
1745184Sek110237 		(void) strcpy(path, pathtmp);
1755184Sek110237 		fsep = fsep->fse_parent;
1765184Sek110237 	}
1775184Sek110237 
1785184Sek110237 	s = malloc(strlen(path) + 1);
1795184Sek110237 	(void) strcpy(s, path);
1805184Sek110237 	return (s);
1815184Sek110237 }
1825184Sek110237 
1835184Sek110237 /*
1845184Sek110237  * Creates multiple nested directories as required by the
1855184Sek110237  * supplied path. Starts at the end of the path, creating
1865184Sek110237  * a list of directories to mkdir, up to the root of the
1875184Sek110237  * path, then mkdirs them one at a time from the root on down.
1885184Sek110237  */
1895184Sek110237 static int
1905184Sek110237 fileset_mkdir(char *path, int mode)
1915184Sek110237 {
1925184Sek110237 	char *p;
1935184Sek110237 	char *dirs[65536];
1945184Sek110237 	int i = 0;
1955184Sek110237 
1965184Sek110237 	if ((p = strdup(path)) == NULL)
1975184Sek110237 		goto null_str;
1985184Sek110237 
1995184Sek110237 	/*
2005184Sek110237 	 * Fill an array of subdirectory path names until either we
2015184Sek110237 	 * reach the root or encounter an already existing subdirectory
2025184Sek110237 	 */
2035184Sek110237 	/* CONSTCOND */
2045184Sek110237 	while (1) {
2055184Sek110237 		struct stat64 sb;
2065184Sek110237 
2075184Sek110237 		if (stat64(p, &sb) == 0)
2085184Sek110237 			break;
2095184Sek110237 		if (strlen(p) < 3)
2105184Sek110237 			break;
2115184Sek110237 		if ((dirs[i] = strdup(p)) == NULL) {
2125184Sek110237 			free(p);
2135184Sek110237 			goto null_str;
2145184Sek110237 		}
2155184Sek110237 
2165184Sek110237 		(void) trunc_dirname(p);
2175184Sek110237 		i++;
2185184Sek110237 	}
2195184Sek110237 
2205184Sek110237 	/* Make the directories, from closest to root downwards. */
2215184Sek110237 	for (--i; i >= 0; i--) {
2225184Sek110237 		(void) mkdir(dirs[i], mode);
2235184Sek110237 		free(dirs[i]);
2245184Sek110237 	}
2255184Sek110237 
2265184Sek110237 	free(p);
2277556SAndrew.W.Wilson@sun.com 	return (FILEBENCH_OK);
2285184Sek110237 
2295184Sek110237 null_str:
2305184Sek110237 	/* clean up */
2315184Sek110237 	for (--i; i >= 0; i--)
2325184Sek110237 		free(dirs[i]);
2335184Sek110237 
2345184Sek110237 	filebench_log(LOG_ERROR,
2355184Sek110237 	    "Failed to create directory path %s: Out of memory", path);
2365184Sek110237 
2377556SAndrew.W.Wilson@sun.com 	return (FILEBENCH_ERROR);
2385184Sek110237 }
2395184Sek110237 
2405673Saw148015 /*
2415673Saw148015  * creates the subdirectory tree for a fileset.
2425673Saw148015  */
2435673Saw148015 static int
2445673Saw148015 fileset_create_subdirs(fileset_t *fileset, char *filesetpath)
2455673Saw148015 {
2465673Saw148015 	filesetentry_t *direntry;
2475673Saw148015 	char full_path[MAXPATHLEN];
2485673Saw148015 	char *part_path;
2495673Saw148015 
2505673Saw148015 	/* walk the subdirectory list, enstanciating subdirs */
2515673Saw148015 	direntry = fileset->fs_dirlist;
2525673Saw148015 	while (direntry) {
2535673Saw148015 		(void) strcpy(full_path, filesetpath);
2545673Saw148015 		part_path = fileset_resolvepath(direntry);
2555673Saw148015 		(void) strcat(full_path, part_path);
2565673Saw148015 		free(part_path);
2575673Saw148015 
2585673Saw148015 		/* now create this portion of the subdirectory tree */
2597556SAndrew.W.Wilson@sun.com 		if (fileset_mkdir(full_path, 0755) == FILEBENCH_ERROR)
2607556SAndrew.W.Wilson@sun.com 			return (FILEBENCH_ERROR);
2615673Saw148015 
2625673Saw148015 		direntry = direntry->fse_dirnext;
2635673Saw148015 	}
2647556SAndrew.W.Wilson@sun.com 	return (FILEBENCH_OK);
2655673Saw148015 }
2665673Saw148015 
2675673Saw148015 /*
2685673Saw148015  * given a fileset entry, determines if the associated file
2695673Saw148015  * needs to be allocated or not, and if so does the allocation.
2705673Saw148015  */
2715673Saw148015 static int
2725673Saw148015 fileset_alloc_file(filesetentry_t *entry)
2735673Saw148015 {
2745673Saw148015 	char path[MAXPATHLEN];
2755673Saw148015 	char *buf;
2765673Saw148015 	struct stat64 sb;
2775673Saw148015 	char *pathtmp;
2785673Saw148015 	off64_t seek;
2795673Saw148015 	int fd;
2805673Saw148015 
2815673Saw148015 	*path = 0;
2826212Saw148015 	(void) strcpy(path, avd_get_str(entry->fse_fileset->fs_path));
2835673Saw148015 	(void) strcat(path, "/");
2846212Saw148015 	(void) strcat(path, avd_get_str(entry->fse_fileset->fs_name));
2855673Saw148015 	pathtmp = fileset_resolvepath(entry);
2865673Saw148015 	(void) strcat(path, pathtmp);
2875673Saw148015 
2885673Saw148015 	filebench_log(LOG_DEBUG_IMPL, "Populated %s", entry->fse_path);
2895673Saw148015 
2905673Saw148015 	/* see if reusing and this file exists */
2915673Saw148015 	if ((entry->fse_flags & FSE_REUSING) && (stat64(path, &sb) == 0)) {
2925673Saw148015 		if ((fd = open64(path, O_RDWR)) < 0) {
2935673Saw148015 			filebench_log(LOG_INFO,
2945673Saw148015 			    "Attempted but failed to Re-use file %s",
2955673Saw148015 			    path);
2967556SAndrew.W.Wilson@sun.com 			return (FILEBENCH_ERROR);
2975673Saw148015 		}
2985673Saw148015 
2995673Saw148015 		if (sb.st_size == (off64_t)entry->fse_size) {
3007556SAndrew.W.Wilson@sun.com 			filebench_log(LOG_DEBUG_IMPL,
3015673Saw148015 			    "Re-using file %s", path);
3025673Saw148015 
3036212Saw148015 			if (!avd_get_bool(entry->fse_fileset->fs_cached))
3045673Saw148015 				(void) fileset_freemem(fd,
3055673Saw148015 				    entry->fse_size);
3065673Saw148015 
3077556SAndrew.W.Wilson@sun.com 			(void) ipc_mutex_lock(
3087556SAndrew.W.Wilson@sun.com 			    &entry->fse_fileset->fs_pick_lock);
3095673Saw148015 			entry->fse_flags |= FSE_EXISTS;
3106701Saw148015 			entry->fse_fileset->fs_num_act_files++;
3116701Saw148015 			(void) ipc_mutex_unlock(
3127556SAndrew.W.Wilson@sun.com 			    &entry->fse_fileset->fs_pick_lock);
3136701Saw148015 
3145673Saw148015 			(void) close(fd);
3157556SAndrew.W.Wilson@sun.com 			return (FILEBENCH_OK);
3165673Saw148015 
3175673Saw148015 		} else if (sb.st_size > (off64_t)entry->fse_size) {
3185673Saw148015 			/* reuse, but too large */
3195673Saw148015 			filebench_log(LOG_INFO,
3205673Saw148015 			    "Truncating & re-using file %s", path);
3215673Saw148015 
3226613Sek110237 #ifdef HAVE_FTRUNCATE64
3236613Sek110237 			(void) ftruncate64(fd, (off64_t)entry->fse_size);
3246613Sek110237 #else
3256613Sek110237 			(void) ftruncate(fd, (off_t)entry->fse_size);
3266613Sek110237 #endif
3275673Saw148015 
3286212Saw148015 			if (!avd_get_bool(entry->fse_fileset->fs_cached))
3295673Saw148015 				(void) fileset_freemem(fd,
3305673Saw148015 				    entry->fse_size);
3315673Saw148015 
3327556SAndrew.W.Wilson@sun.com 			(void) ipc_mutex_lock(
3337556SAndrew.W.Wilson@sun.com 			    &entry->fse_fileset->fs_pick_lock);
3345673Saw148015 			entry->fse_flags |= FSE_EXISTS;
3356701Saw148015 			entry->fse_fileset->fs_num_act_files++;
3366701Saw148015 			(void) ipc_mutex_unlock(
3377556SAndrew.W.Wilson@sun.com 			    &entry->fse_fileset->fs_pick_lock);
3386701Saw148015 
3395673Saw148015 			(void) close(fd);
3407556SAndrew.W.Wilson@sun.com 			return (FILEBENCH_OK);
3415673Saw148015 		}
3425673Saw148015 	} else {
3435673Saw148015 
3445673Saw148015 		/* No file or not reusing, so create */
3455673Saw148015 		if ((fd = open64(path, O_RDWR | O_CREAT, 0644)) < 0) {
3465673Saw148015 			filebench_log(LOG_ERROR,
3475673Saw148015 			    "Failed to pre-allocate file %s: %s",
3485673Saw148015 			    path, strerror(errno));
3495673Saw148015 
3507556SAndrew.W.Wilson@sun.com 			return (FILEBENCH_ERROR);
3515673Saw148015 		}
3525673Saw148015 	}
3535673Saw148015 
3545673Saw148015 	if ((buf = (char *)malloc(FILE_ALLOC_BLOCK)) == NULL)
3557556SAndrew.W.Wilson@sun.com 		return (FILEBENCH_ERROR);
3565673Saw148015 
3577556SAndrew.W.Wilson@sun.com 	(void) ipc_mutex_lock(&entry->fse_fileset->fs_pick_lock);
3587556SAndrew.W.Wilson@sun.com 	entry->fse_flags |= FSE_EXISTS;
3596701Saw148015 	entry->fse_fileset->fs_num_act_files++;
3607556SAndrew.W.Wilson@sun.com 	(void) ipc_mutex_unlock(&entry->fse_fileset->fs_pick_lock);
3616701Saw148015 
3625673Saw148015 	for (seek = 0; seek < entry->fse_size; ) {
3635673Saw148015 		off64_t wsize;
3645673Saw148015 		int ret = 0;
3655673Saw148015 
3665673Saw148015 		/*
3675673Saw148015 		 * Write FILE_ALLOC_BLOCK's worth,
3685673Saw148015 		 * except on last write
3695673Saw148015 		 */
3705673Saw148015 		wsize = MIN(entry->fse_size - seek, FILE_ALLOC_BLOCK);
3715673Saw148015 
3725673Saw148015 		ret = write(fd, buf, wsize);
3735673Saw148015 		if (ret != wsize) {
3745673Saw148015 			filebench_log(LOG_ERROR,
3755673Saw148015 			    "Failed to pre-allocate file %s: %s",
3765673Saw148015 			    path, strerror(errno));
3775673Saw148015 			(void) close(fd);
3785673Saw148015 			free(buf);
3797556SAndrew.W.Wilson@sun.com 			return (FILEBENCH_ERROR);
3805673Saw148015 		}
3815673Saw148015 		seek += wsize;
3825673Saw148015 	}
3835673Saw148015 
3846212Saw148015 	if (!avd_get_bool(entry->fse_fileset->fs_cached))
3855673Saw148015 		(void) fileset_freemem(fd, entry->fse_size);
3865673Saw148015 
3875673Saw148015 	(void) close(fd);
3885673Saw148015 
3895673Saw148015 	free(buf);
3905673Saw148015 
3915673Saw148015 	filebench_log(LOG_DEBUG_IMPL,
3926286Saw148015 	    "Pre-allocated file %s size %llu",
3936286Saw148015 	    path, (u_longlong_t)entry->fse_size);
3945673Saw148015 
3957556SAndrew.W.Wilson@sun.com 	return (FILEBENCH_OK);
3965673Saw148015 }
3975673Saw148015 
3985673Saw148015 /*
3995673Saw148015  * given a fileset entry, determines if the associated file
4005673Saw148015  * needs to be allocated or not, and if so does the allocation.
4017556SAndrew.W.Wilson@sun.com  * Sets shm_fsparalloc_count to -1 on error.
4025673Saw148015  */
4035673Saw148015 static void *
4045673Saw148015 fileset_alloc_thread(filesetentry_t *entry)
4055673Saw148015 {
4067556SAndrew.W.Wilson@sun.com 	if (fileset_alloc_file(entry) == FILEBENCH_ERROR) {
4077556SAndrew.W.Wilson@sun.com 		(void) pthread_mutex_lock(&filebench_shm->shm_fsparalloc_lock);
4087556SAndrew.W.Wilson@sun.com 		filebench_shm->shm_fsparalloc_count = -1;
4095673Saw148015 	} else {
4107556SAndrew.W.Wilson@sun.com 		(void) pthread_mutex_lock(&filebench_shm->shm_fsparalloc_lock);
4117556SAndrew.W.Wilson@sun.com 		filebench_shm->shm_fsparalloc_count--;
4125673Saw148015 	}
4135673Saw148015 
4147556SAndrew.W.Wilson@sun.com 	(void) pthread_cond_signal(&filebench_shm->shm_fsparalloc_cv);
4157556SAndrew.W.Wilson@sun.com 	(void) pthread_mutex_unlock(&filebench_shm->shm_fsparalloc_lock);
4165673Saw148015 
4175673Saw148015 	pthread_exit(NULL);
4185673Saw148015 	return (NULL);
4195673Saw148015 }
4205673Saw148015 
4215184Sek110237 
4225184Sek110237 /*
4235184Sek110237  * First creates the parent directories of the file using
4245184Sek110237  * fileset_mkdir(). Then Optionally sets the O_DSYNC flag
4255184Sek110237  * and opens the file with open64(). It unlocks the fileset
4265184Sek110237  * entry lock, sets the DIRECTIO_ON or DIRECTIO_OFF flags
4275184Sek110237  * as requested, and returns the file descriptor integer
4285184Sek110237  * for the opened file.
4295184Sek110237  */
4305184Sek110237 int
4315184Sek110237 fileset_openfile(fileset_t *fileset,
432*7736SAndrew.W.Wilson@sun.com     filesetentry_t *entry, int flag, int filemode, int attrs)
4335184Sek110237 {
4345184Sek110237 	char path[MAXPATHLEN];
4355184Sek110237 	char dir[MAXPATHLEN];
4365184Sek110237 	char *pathtmp;
4375184Sek110237 	struct stat64 sb;
4385184Sek110237 	int fd;
4395184Sek110237 	int open_attrs = 0;
4405184Sek110237 
4415184Sek110237 	*path = 0;
4426212Saw148015 	(void) strcpy(path, avd_get_str(fileset->fs_path));
4435184Sek110237 	(void) strcat(path, "/");
4446212Saw148015 	(void) strcat(path, avd_get_str(fileset->fs_name));
4455184Sek110237 	pathtmp = fileset_resolvepath(entry);
4465184Sek110237 	(void) strcat(path, pathtmp);
4475184Sek110237 	(void) strcpy(dir, path);
4485184Sek110237 	free(pathtmp);
4495184Sek110237 	(void) trunc_dirname(dir);
4505184Sek110237 
4515184Sek110237 	/* If we are going to create a file, create the parent dirs */
4525184Sek110237 	if ((flag & O_CREAT) && (stat64(dir, &sb) != 0)) {
4537556SAndrew.W.Wilson@sun.com 		if (fileset_mkdir(dir, 0755) == FILEBENCH_ERROR)
4547556SAndrew.W.Wilson@sun.com 			return (FILEBENCH_ERROR);
4556701Saw148015 	}
4566701Saw148015 
4575184Sek110237 	if (attrs & FLOW_ATTR_DSYNC) {
4585184Sek110237 #ifdef sun
4595184Sek110237 		open_attrs |= O_DSYNC;
4605184Sek110237 #else
4615184Sek110237 		open_attrs |= O_FSYNC;
4625184Sek110237 #endif
4635184Sek110237 	}
4645184Sek110237 
465*7736SAndrew.W.Wilson@sun.com 	if ((fd = open64(path, flag | open_attrs, filemode)) < 0) {
4665184Sek110237 		filebench_log(LOG_ERROR,
4675184Sek110237 		    "Failed to open file %s: %s",
4685184Sek110237 		    path, strerror(errno));
4697556SAndrew.W.Wilson@sun.com 
4707556SAndrew.W.Wilson@sun.com 		fileset_unbusy(entry, FALSE, FALSE);
4717556SAndrew.W.Wilson@sun.com 		return (FILEBENCH_ERROR);
4725184Sek110237 	}
4737556SAndrew.W.Wilson@sun.com 
4747556SAndrew.W.Wilson@sun.com 	if (flag & O_CREAT)
4757556SAndrew.W.Wilson@sun.com 		fileset_unbusy(entry, TRUE, TRUE);
4767556SAndrew.W.Wilson@sun.com 	else
4777556SAndrew.W.Wilson@sun.com 		fileset_unbusy(entry, FALSE, FALSE);
4785184Sek110237 
4795184Sek110237 #ifdef sun
4805184Sek110237 	if (attrs & FLOW_ATTR_DIRECTIO)
4815184Sek110237 		(void) directio(fd, DIRECTIO_ON);
4825184Sek110237 	else
4835184Sek110237 		(void) directio(fd, DIRECTIO_OFF);
4845184Sek110237 #endif
4855184Sek110237 
4865184Sek110237 	return (fd);
4875184Sek110237 }
4885184Sek110237 
4895184Sek110237 
4905184Sek110237 /*
4915184Sek110237  * Selects a fileset entry from a fileset. If the
4925184Sek110237  * FILESET_PICKDIR flag is set it will pick a directory
4935184Sek110237  * entry, otherwise a file entry. The FILESET_PICKRESET
4945184Sek110237  * flag will cause it to reset the free list to the
4955184Sek110237  * overall list (file or directory). The FILESET_PICKUNIQUE
4965184Sek110237  * flag will take an entry off of one of the free (unused)
4975184Sek110237  * lists (file or directory), otherwise the entry will be
4985184Sek110237  * picked off of one of the rotor lists (file or directory).
4995184Sek110237  * The FILESET_PICKEXISTS will insure that only extant
5005184Sek110237  * (FSE_EXISTS) state files are selected, while
5015184Sek110237  * FILESET_PICKNOEXIST insures that only non extant
5025184Sek110237  * (not FSE_EXISTS) state files are selected.
5036391Saw148015  * Note that the selected fileset entry (file) is returned
5047556SAndrew.W.Wilson@sun.com  * with its FSE_BUSY flag (in fse_flags) set.
5055184Sek110237  */
5065184Sek110237 filesetentry_t *
5075184Sek110237 fileset_pick(fileset_t *fileset, int flags, int tid)
5085184Sek110237 {
5095184Sek110237 	filesetentry_t *entry = NULL;
5105184Sek110237 	filesetentry_t *first = NULL;
5115184Sek110237 
5127556SAndrew.W.Wilson@sun.com 	(void) ipc_mutex_lock(&fileset->fs_pick_lock);
5137556SAndrew.W.Wilson@sun.com 
5147556SAndrew.W.Wilson@sun.com 	/* see if we have to wait for available files or directories */
5157556SAndrew.W.Wilson@sun.com 	if (flags & FILESET_PICKDIR) {
5167556SAndrew.W.Wilson@sun.com 		while (fileset->fs_idle_dirs == 0) {
5177556SAndrew.W.Wilson@sun.com 			(void) pthread_cond_wait(&fileset->fs_idle_dirs_cv,
5187556SAndrew.W.Wilson@sun.com 			    &fileset->fs_pick_lock);
5197556SAndrew.W.Wilson@sun.com 		}
5207556SAndrew.W.Wilson@sun.com 	} else {
5217556SAndrew.W.Wilson@sun.com 		while (fileset->fs_idle_files == 0) {
5227556SAndrew.W.Wilson@sun.com 			(void) pthread_cond_wait(&fileset->fs_idle_files_cv,
5237556SAndrew.W.Wilson@sun.com 			    &fileset->fs_pick_lock);
5247556SAndrew.W.Wilson@sun.com 		}
5257556SAndrew.W.Wilson@sun.com 	}
5265184Sek110237 
5276701Saw148015 	/* see if asking for impossible */
5286701Saw148015 	if (flags & FILESET_PICKEXISTS) {
5296701Saw148015 		if (fileset->fs_num_act_files == 0) {
5307556SAndrew.W.Wilson@sun.com 			(void) ipc_mutex_unlock(&fileset->fs_pick_lock);
5316701Saw148015 			return (NULL);
5326701Saw148015 		}
5336701Saw148015 	} else if (flags & FILESET_PICKNOEXIST) {
5346701Saw148015 		if (fileset->fs_num_act_files == fileset->fs_realfiles) {
5357556SAndrew.W.Wilson@sun.com 			(void) ipc_mutex_unlock(&fileset->fs_pick_lock);
5366701Saw148015 			return (NULL);
5376701Saw148015 		}
5386701Saw148015 	}
5396701Saw148015 
5405184Sek110237 	while (entry == NULL) {
5415184Sek110237 
5425184Sek110237 		if ((flags & FILESET_PICKDIR) && (flags & FILESET_PICKRESET)) {
5435184Sek110237 			entry = fileset->fs_dirlist;
5445184Sek110237 			while (entry) {
5455184Sek110237 				entry->fse_flags |= FSE_FREE;
5465184Sek110237 				entry = entry->fse_dirnext;
5475184Sek110237 			}
5485184Sek110237 			fileset->fs_dirfree = fileset->fs_dirlist;
5495184Sek110237 		}
5505184Sek110237 
5515184Sek110237 		if (!(flags & FILESET_PICKDIR) && (flags & FILESET_PICKRESET)) {
5525184Sek110237 			entry = fileset->fs_filelist;
5535184Sek110237 			while (entry) {
5545184Sek110237 				entry->fse_flags |= FSE_FREE;
5555184Sek110237 				entry = entry->fse_filenext;
5565184Sek110237 			}
5575184Sek110237 			fileset->fs_filefree = fileset->fs_filelist;
5585184Sek110237 		}
5595184Sek110237 
5605184Sek110237 		if (flags & FILESET_PICKUNIQUE) {
5615184Sek110237 			if (flags & FILESET_PICKDIR) {
5625184Sek110237 				entry = fileset->fs_dirfree;
5635184Sek110237 				if (entry == NULL)
5645184Sek110237 					goto empty;
5655184Sek110237 				fileset->fs_dirfree = entry->fse_dirnext;
5665184Sek110237 			} else {
5675184Sek110237 				entry = fileset->fs_filefree;
5685184Sek110237 				if (entry == NULL)
5695184Sek110237 					goto empty;
5705184Sek110237 				fileset->fs_filefree = entry->fse_filenext;
5715184Sek110237 			}
5725184Sek110237 			entry->fse_flags &= ~FSE_FREE;
5735184Sek110237 		} else {
5745184Sek110237 			if (flags & FILESET_PICKDIR) {
5755184Sek110237 				entry = fileset->fs_dirrotor;
5765184Sek110237 				if (entry == NULL)
5775184Sek110237 				fileset->fs_dirrotor =
5785184Sek110237 				    entry = fileset->fs_dirlist;
5795184Sek110237 				fileset->fs_dirrotor = entry->fse_dirnext;
5805184Sek110237 			} else {
5817556SAndrew.W.Wilson@sun.com 				if (flags & FILESET_PICKNOEXIST) {
5827556SAndrew.W.Wilson@sun.com 					entry = fileset->fs_file_ne_rotor;
5837556SAndrew.W.Wilson@sun.com 					if (entry == NULL)
5847556SAndrew.W.Wilson@sun.com 						fileset->fs_file_ne_rotor =
5857556SAndrew.W.Wilson@sun.com 						    entry =
5867556SAndrew.W.Wilson@sun.com 						    fileset->fs_filelist;
5877556SAndrew.W.Wilson@sun.com 					fileset->fs_file_ne_rotor =
5887556SAndrew.W.Wilson@sun.com 					    entry->fse_filenext;
5897556SAndrew.W.Wilson@sun.com 				} else {
5907556SAndrew.W.Wilson@sun.com 					entry = fileset->fs_filerotor[tid];
5917556SAndrew.W.Wilson@sun.com 					if (entry == NULL)
5927556SAndrew.W.Wilson@sun.com 						fileset->fs_filerotor[tid] =
5937556SAndrew.W.Wilson@sun.com 						    entry =
5947556SAndrew.W.Wilson@sun.com 						    fileset->fs_filelist;
5955184Sek110237 					fileset->fs_filerotor[tid] =
5967556SAndrew.W.Wilson@sun.com 					    entry->fse_filenext;
5977556SAndrew.W.Wilson@sun.com 				}
5985184Sek110237 			}
5995184Sek110237 		}
6005184Sek110237 
6015184Sek110237 		if (first == entry)
6025184Sek110237 			goto empty;
6035184Sek110237 
6045184Sek110237 		if (first == NULL)
6055184Sek110237 			first = entry;
6065184Sek110237 
6077556SAndrew.W.Wilson@sun.com 		/* see if entry in use */
6087556SAndrew.W.Wilson@sun.com 		if (entry->fse_flags & FSE_BUSY) {
6097556SAndrew.W.Wilson@sun.com 
6107556SAndrew.W.Wilson@sun.com 			/* it is, so try next */
6117556SAndrew.W.Wilson@sun.com 			entry = NULL;
6127556SAndrew.W.Wilson@sun.com 			continue;
6137556SAndrew.W.Wilson@sun.com 		}
6145184Sek110237 
6155184Sek110237 		/* If we ask for an existing file, go round again */
6165184Sek110237 		if ((flags & FILESET_PICKEXISTS) &&
6177556SAndrew.W.Wilson@sun.com 		    !(entry->fse_flags & FSE_EXISTS))
6185184Sek110237 			entry = NULL;
6195184Sek110237 
6205184Sek110237 		/* If we ask for not an existing file, go round again */
6215184Sek110237 		if ((flags & FILESET_PICKNOEXIST) &&
6227556SAndrew.W.Wilson@sun.com 		    (entry->fse_flags & FSE_EXISTS))
6235184Sek110237 			entry = NULL;
6245184Sek110237 	}
6255184Sek110237 
6267556SAndrew.W.Wilson@sun.com 	/* update file or directory idle counts */
6277556SAndrew.W.Wilson@sun.com 	if (flags & FILESET_PICKDIR)
6287556SAndrew.W.Wilson@sun.com 		fileset->fs_idle_dirs--;
6297556SAndrew.W.Wilson@sun.com 	else
6307556SAndrew.W.Wilson@sun.com 		fileset->fs_idle_files--;
6317556SAndrew.W.Wilson@sun.com 
6327556SAndrew.W.Wilson@sun.com 	/* Indicate that file or directory is now busy */
6337556SAndrew.W.Wilson@sun.com 	entry->fse_flags |= FSE_BUSY;
6347556SAndrew.W.Wilson@sun.com 
6357556SAndrew.W.Wilson@sun.com 	(void) ipc_mutex_unlock(&fileset->fs_pick_lock);
6365184Sek110237 	filebench_log(LOG_DEBUG_SCRIPT, "Picked file %s", entry->fse_path);
6375184Sek110237 	return (entry);
6385184Sek110237 
6395184Sek110237 empty:
6407556SAndrew.W.Wilson@sun.com 	(void) ipc_mutex_unlock(&fileset->fs_pick_lock);
6415184Sek110237 	return (NULL);
6425184Sek110237 }
6435184Sek110237 
6445184Sek110237 /*
6457556SAndrew.W.Wilson@sun.com  * Removes a filesetentry from the "FSE_BUSY" state, signaling any threads
6467556SAndrew.W.Wilson@sun.com  * that are waiting for a NOT BUSY filesetentry. Also sets whether it is
6477556SAndrew.W.Wilson@sun.com  * existant or not, or leaves that designation alone.
6487556SAndrew.W.Wilson@sun.com  */
6497556SAndrew.W.Wilson@sun.com void
6507556SAndrew.W.Wilson@sun.com fileset_unbusy(filesetentry_t *entry, int update_exist, int new_exist_val)
6517556SAndrew.W.Wilson@sun.com {
6527556SAndrew.W.Wilson@sun.com 	fileset_t *fileset = NULL;
6537556SAndrew.W.Wilson@sun.com 	int fse_is_dir;
6547556SAndrew.W.Wilson@sun.com 
6557556SAndrew.W.Wilson@sun.com 	if (entry)
6567556SAndrew.W.Wilson@sun.com 		fileset = entry->fse_fileset;
6577556SAndrew.W.Wilson@sun.com 
6587556SAndrew.W.Wilson@sun.com 	if (fileset == NULL) {
6597556SAndrew.W.Wilson@sun.com 		filebench_log(LOG_ERROR, "fileset_unbusy: NO FILESET!");
6607556SAndrew.W.Wilson@sun.com 		return;
6617556SAndrew.W.Wilson@sun.com 	}
6627556SAndrew.W.Wilson@sun.com 
6637556SAndrew.W.Wilson@sun.com 	(void) ipc_mutex_lock(&fileset->fs_pick_lock);
6647556SAndrew.W.Wilson@sun.com 	fse_is_dir = entry->fse_flags & FSE_DIR;
6657556SAndrew.W.Wilson@sun.com 
6667556SAndrew.W.Wilson@sun.com 	/* increment idle count, clear FSE_BUSY and signal IF it was busy */
6677556SAndrew.W.Wilson@sun.com 	if (entry->fse_flags & FSE_BUSY) {
6687556SAndrew.W.Wilson@sun.com 
6697556SAndrew.W.Wilson@sun.com 		/* unbusy it */
6707556SAndrew.W.Wilson@sun.com 		entry->fse_flags &= (~FSE_BUSY);
6717556SAndrew.W.Wilson@sun.com 
6727556SAndrew.W.Wilson@sun.com 		/* release any threads waiting for unbusy */
6737556SAndrew.W.Wilson@sun.com 		if (entry->fse_flags & FSE_THRD_WAITNG) {
6747556SAndrew.W.Wilson@sun.com 			entry->fse_flags &= (~FSE_THRD_WAITNG);
6757556SAndrew.W.Wilson@sun.com 			(void) pthread_cond_broadcast(
6767556SAndrew.W.Wilson@sun.com 			    &fileset->fs_thrd_wait_cv);
6777556SAndrew.W.Wilson@sun.com 		}
6787556SAndrew.W.Wilson@sun.com 
6797556SAndrew.W.Wilson@sun.com 		/* increment idle count and signal waiting threads */
6807556SAndrew.W.Wilson@sun.com 		if (fse_is_dir) {
6817556SAndrew.W.Wilson@sun.com 			fileset->fs_idle_dirs++;
6827556SAndrew.W.Wilson@sun.com 			if (fileset->fs_idle_dirs == 1) {
6837556SAndrew.W.Wilson@sun.com 				(void) pthread_cond_signal(
6847556SAndrew.W.Wilson@sun.com 				    &fileset->fs_idle_dirs_cv);
6857556SAndrew.W.Wilson@sun.com 			}
6867556SAndrew.W.Wilson@sun.com 		} else {
6877556SAndrew.W.Wilson@sun.com 			fileset->fs_idle_files++;
6887556SAndrew.W.Wilson@sun.com 			if (fileset->fs_idle_files == 1) {
6897556SAndrew.W.Wilson@sun.com 				(void) pthread_cond_signal(
6907556SAndrew.W.Wilson@sun.com 				    &fileset->fs_idle_files_cv);
6917556SAndrew.W.Wilson@sun.com 			}
6927556SAndrew.W.Wilson@sun.com 		}
6937556SAndrew.W.Wilson@sun.com 	}
6947556SAndrew.W.Wilson@sun.com 
6957556SAndrew.W.Wilson@sun.com 	/* modify FSE_EXIST flag and actual dirs/files count, if requested */
6967556SAndrew.W.Wilson@sun.com 	if (update_exist) {
6977556SAndrew.W.Wilson@sun.com 		if (new_exist_val == TRUE) {
6987556SAndrew.W.Wilson@sun.com 			if (!(entry->fse_flags & FSE_EXISTS)) {
6997556SAndrew.W.Wilson@sun.com 
7007556SAndrew.W.Wilson@sun.com 				/* asked to set, and it was clear */
7017556SAndrew.W.Wilson@sun.com 				entry->fse_flags |= FSE_EXISTS;
7027556SAndrew.W.Wilson@sun.com 				if (fse_is_dir)
7037556SAndrew.W.Wilson@sun.com 					fileset->fs_num_act_dirs++;
7047556SAndrew.W.Wilson@sun.com 				else
7057556SAndrew.W.Wilson@sun.com 					fileset->fs_num_act_files++;
7067556SAndrew.W.Wilson@sun.com 			}
7077556SAndrew.W.Wilson@sun.com 		} else {
7087556SAndrew.W.Wilson@sun.com 			if (entry->fse_flags & FSE_EXISTS) {
7097556SAndrew.W.Wilson@sun.com 
7107556SAndrew.W.Wilson@sun.com 				/* asked to clear, and it was set */
7117556SAndrew.W.Wilson@sun.com 				entry->fse_flags &= (~FSE_EXISTS);
7127556SAndrew.W.Wilson@sun.com 				if (fse_is_dir)
7137556SAndrew.W.Wilson@sun.com 					fileset->fs_num_act_dirs--;
7147556SAndrew.W.Wilson@sun.com 				else
7157556SAndrew.W.Wilson@sun.com 					fileset->fs_num_act_files--;
7167556SAndrew.W.Wilson@sun.com 			}
7177556SAndrew.W.Wilson@sun.com 		}
7187556SAndrew.W.Wilson@sun.com 	}
7197556SAndrew.W.Wilson@sun.com 
7207556SAndrew.W.Wilson@sun.com 	(void) ipc_mutex_unlock(&fileset->fs_pick_lock);
7217556SAndrew.W.Wilson@sun.com }
7227556SAndrew.W.Wilson@sun.com 
7237556SAndrew.W.Wilson@sun.com /*
7245184Sek110237  * Given a fileset "fileset", create the associated files as
7255184Sek110237  * specified in the attributes of the fileset. The fileset is
7266212Saw148015  * rooted in a directory whose pathname is in fileset_path. If the
7275184Sek110237  * directory exists, meaning that there is already a fileset,
7286212Saw148015  * and the fileset_reuse attribute is false, then remove it and all
7295184Sek110237  * its contained files and subdirectories. Next, the routine
7305184Sek110237  * creates a root directory for the fileset. All the file type
7315184Sek110237  * filesetentries are cycled through creating as needed
7325184Sek110237  * their containing subdirectory trees in the filesystem and
7336212Saw148015  * creating actual files for fileset_preallocpercent of them. The
7345184Sek110237  * created files are filled with fse_size bytes of unitialized
7357556SAndrew.W.Wilson@sun.com  * data. The routine returns FILEBENCH_ERROR on errors,
7367556SAndrew.W.Wilson@sun.com  * FILEBENCH_OK on success.
7375184Sek110237  */
7385184Sek110237 static int
7395184Sek110237 fileset_create(fileset_t *fileset)
7405184Sek110237 {
7415184Sek110237 	filesetentry_t *entry;
7425184Sek110237 	char path[MAXPATHLEN];
7435184Sek110237 	struct stat64 sb;
7445184Sek110237 	int pickflags = FILESET_PICKUNIQUE | FILESET_PICKRESET;
7455184Sek110237 	hrtime_t start = gethrtime();
7466212Saw148015 	char *fileset_path;
7476212Saw148015 	char *fileset_name;
7486212Saw148015 	int randno;
7495184Sek110237 	int preallocated = 0;
750*7736SAndrew.W.Wilson@sun.com 	int reusing;
7515184Sek110237 
7526212Saw148015 	if ((fileset_path = avd_get_str(fileset->fs_path)) == NULL) {
7535673Saw148015 		filebench_log(LOG_ERROR, "%s path not set",
7545673Saw148015 		    fileset_entity_name(fileset));
7557556SAndrew.W.Wilson@sun.com 		return (FILEBENCH_ERROR);
7565184Sek110237 	}
7575184Sek110237 
7586212Saw148015 	if ((fileset_name = avd_get_str(fileset->fs_name)) == NULL) {
7596212Saw148015 		filebench_log(LOG_ERROR, "%s name not set",
7606212Saw148015 		    fileset_entity_name(fileset));
7617556SAndrew.W.Wilson@sun.com 		return (FILEBENCH_ERROR);
7626212Saw148015 	}
7636212Saw148015 
7645673Saw148015 #ifdef HAVE_RAW_SUPPORT
7655673Saw148015 	/* treat raw device as special case */
7665673Saw148015 	if (fileset->fs_attrs & FILESET_IS_RAW_DEV)
7677556SAndrew.W.Wilson@sun.com 		return (FILEBENCH_OK);
7685673Saw148015 #endif /* HAVE_RAW_SUPPORT */
7695673Saw148015 
7705184Sek110237 	/* XXX Add check to see if there is enough space */
7715184Sek110237 
772*7736SAndrew.W.Wilson@sun.com 	/* set up path to fileset */
7736212Saw148015 	(void) strcpy(path, fileset_path);
7745184Sek110237 	(void) strcat(path, "/");
7756212Saw148015 	(void) strcat(path, fileset_name);
776*7736SAndrew.W.Wilson@sun.com 
777*7736SAndrew.W.Wilson@sun.com 	/* if exists and resusing, then don't create new */
778*7736SAndrew.W.Wilson@sun.com 	if (((stat64(path, &sb) == 0)&& (strlen(path) > 3) &&
779*7736SAndrew.W.Wilson@sun.com 	    (strlen(avd_get_str(fileset->fs_path)) > 2)) &&
780*7736SAndrew.W.Wilson@sun.com 	    avd_get_bool(fileset->fs_reuse)) {
781*7736SAndrew.W.Wilson@sun.com 		reusing = 1;
782*7736SAndrew.W.Wilson@sun.com 	} else {
783*7736SAndrew.W.Wilson@sun.com 		reusing = 0;
784*7736SAndrew.W.Wilson@sun.com 	}
785*7736SAndrew.W.Wilson@sun.com 
786*7736SAndrew.W.Wilson@sun.com 	if (!reusing) {
787*7736SAndrew.W.Wilson@sun.com 		char cmd[MAXPATHLEN];
7885184Sek110237 
789*7736SAndrew.W.Wilson@sun.com 		/* Remove existing */
790*7736SAndrew.W.Wilson@sun.com 		(void) snprintf(cmd, sizeof (cmd), "rm -rf %s", path);
791*7736SAndrew.W.Wilson@sun.com 		(void) system(cmd);
792*7736SAndrew.W.Wilson@sun.com 		filebench_log(LOG_VERBOSE,
793*7736SAndrew.W.Wilson@sun.com 		    "Removed any existing %s %s in %llu seconds",
794*7736SAndrew.W.Wilson@sun.com 		    fileset_entity_name(fileset), fileset_name,
795*7736SAndrew.W.Wilson@sun.com 		    (u_longlong_t)(((gethrtime() - start) /
796*7736SAndrew.W.Wilson@sun.com 		    1000000000) + 1));
797*7736SAndrew.W.Wilson@sun.com 	} else {
798*7736SAndrew.W.Wilson@sun.com 		/* we are re-using */
799*7736SAndrew.W.Wilson@sun.com 		filebench_log(LOG_VERBOSE, "Re-using %s %s.",
800*7736SAndrew.W.Wilson@sun.com 		    fileset_entity_name(fileset), fileset_name);
8015184Sek110237 	}
802*7736SAndrew.W.Wilson@sun.com 
803*7736SAndrew.W.Wilson@sun.com 	/* make the filesets directory tree unless in reuse mode */
804*7736SAndrew.W.Wilson@sun.com 	if (!reusing && (avd_get_bool(fileset->fs_prealloc))) {
805*7736SAndrew.W.Wilson@sun.com 		filebench_log(LOG_INFO,
806*7736SAndrew.W.Wilson@sun.com 		    "making tree for filset %s", path);
8075184Sek110237 
808*7736SAndrew.W.Wilson@sun.com 		(void) mkdir(path, 0755);
809*7736SAndrew.W.Wilson@sun.com 
810*7736SAndrew.W.Wilson@sun.com 		if (fileset_create_subdirs(fileset, path) == FILEBENCH_ERROR)
811*7736SAndrew.W.Wilson@sun.com 			return (FILEBENCH_ERROR);
812*7736SAndrew.W.Wilson@sun.com 	}
8135673Saw148015 
8145184Sek110237 	start = gethrtime();
8155184Sek110237 
8165673Saw148015 	filebench_log(LOG_VERBOSE, "Creating %s %s...",
8176212Saw148015 	    fileset_entity_name(fileset), fileset_name);
8185673Saw148015 
8196212Saw148015 	if (!avd_get_bool(fileset->fs_prealloc))
8205673Saw148015 		goto exit;
8215184Sek110237 
8226212Saw148015 	randno = ((RAND_MAX * (100
8236212Saw148015 	    - avd_get_int(fileset->fs_preallocpercent))) / 100);
8246212Saw148015 
8255184Sek110237 	while (entry = fileset_pick(fileset, pickflags, 0)) {
8265673Saw148015 		pthread_t tid;
827*7736SAndrew.W.Wilson@sun.com 		int newrand;
8285184Sek110237 
8295184Sek110237 		pickflags = FILESET_PICKUNIQUE;
8305184Sek110237 
8315673Saw148015 		/* entry doesn't need to be locked during initialization */
8327556SAndrew.W.Wilson@sun.com 		fileset_unbusy(entry, FALSE, FALSE);
8335673Saw148015 
834*7736SAndrew.W.Wilson@sun.com 		newrand = rand();
835*7736SAndrew.W.Wilson@sun.com 
836*7736SAndrew.W.Wilson@sun.com 		if (newrand < randno)
8375184Sek110237 			continue;
8385184Sek110237 
8395184Sek110237 		preallocated++;
8405184Sek110237 
8415673Saw148015 		if (reusing)
8425673Saw148015 			entry->fse_flags |= FSE_REUSING;
8435673Saw148015 		else
8445673Saw148015 			entry->fse_flags &= (~FSE_REUSING);
8455673Saw148015 
8467556SAndrew.W.Wilson@sun.com 		/* fire off allocation threads for each file if paralloc set */
8476212Saw148015 		if (avd_get_bool(fileset->fs_paralloc)) {
8485184Sek110237 
8497556SAndrew.W.Wilson@sun.com 			/* limit total number of simultaneous allocations */
8507556SAndrew.W.Wilson@sun.com 			(void) pthread_mutex_lock(
8517556SAndrew.W.Wilson@sun.com 			    &filebench_shm->shm_fsparalloc_lock);
8527556SAndrew.W.Wilson@sun.com 			while (filebench_shm->shm_fsparalloc_count
8537556SAndrew.W.Wilson@sun.com 			    >= MAX_PARALLOC_THREADS) {
8545673Saw148015 				(void) pthread_cond_wait(
8557556SAndrew.W.Wilson@sun.com 				    &filebench_shm->shm_fsparalloc_cv,
8567556SAndrew.W.Wilson@sun.com 				    &filebench_shm->shm_fsparalloc_lock);
8575673Saw148015 			}
8585673Saw148015 
8597556SAndrew.W.Wilson@sun.com 			/* quit if any allocation thread reports and error */
8607556SAndrew.W.Wilson@sun.com 			if (filebench_shm->shm_fsparalloc_count < 0) {
8617556SAndrew.W.Wilson@sun.com 				(void) pthread_mutex_unlock(
8627556SAndrew.W.Wilson@sun.com 				    &filebench_shm->shm_fsparalloc_lock);
8637556SAndrew.W.Wilson@sun.com 				return (FILEBENCH_ERROR);
8645184Sek110237 			}
8655184Sek110237 
8667556SAndrew.W.Wilson@sun.com 			filebench_shm->shm_fsparalloc_count++;
8677556SAndrew.W.Wilson@sun.com 			(void) pthread_mutex_unlock(
8687556SAndrew.W.Wilson@sun.com 			    &filebench_shm->shm_fsparalloc_lock);
8695184Sek110237 
8707556SAndrew.W.Wilson@sun.com 			/*
8717556SAndrew.W.Wilson@sun.com 			 * Fire off a detached allocation thread per file.
8727556SAndrew.W.Wilson@sun.com 			 * The thread will self destruct when it finishes
8737556SAndrew.W.Wilson@sun.com 			 * writing pre-allocation data to the file.
8747556SAndrew.W.Wilson@sun.com 			 */
8755673Saw148015 			if (pthread_create(&tid, NULL,
8765673Saw148015 			    (void *(*)(void*))fileset_alloc_thread,
8777556SAndrew.W.Wilson@sun.com 			    entry) == 0) {
8787556SAndrew.W.Wilson@sun.com 				/*
8797556SAndrew.W.Wilson@sun.com 				 * A thread was created; detach it so it can
8807556SAndrew.W.Wilson@sun.com 				 * fully quit when finished.
8817556SAndrew.W.Wilson@sun.com 				 */
8827556SAndrew.W.Wilson@sun.com 				(void) pthread_detach(tid);
8837556SAndrew.W.Wilson@sun.com 			} else {
8845184Sek110237 				filebench_log(LOG_ERROR,
8855673Saw148015 				    "File prealloc thread create failed");
8865673Saw148015 				filebench_shutdown(1);
8875184Sek110237 			}
8885184Sek110237 
8895673Saw148015 		} else {
8907556SAndrew.W.Wilson@sun.com 			if (fileset_alloc_file(entry) == FILEBENCH_ERROR)
8917556SAndrew.W.Wilson@sun.com 				return (FILEBENCH_ERROR);
8925673Saw148015 		}
8935673Saw148015 	}
8945184Sek110237 
8955673Saw148015 exit:
8965184Sek110237 	filebench_log(LOG_VERBOSE,
8976286Saw148015 	    "Preallocated %d of %llu of %s %s in %llu seconds",
8985184Sek110237 	    preallocated,
8996286Saw148015 	    (u_longlong_t)fileset->fs_constentries,
9006212Saw148015 	    fileset_entity_name(fileset), fileset_name,
9016286Saw148015 	    (u_longlong_t)(((gethrtime() - start) / 1000000000) + 1));
9025184Sek110237 
9037556SAndrew.W.Wilson@sun.com 	return (FILEBENCH_OK);
9045184Sek110237 }
9055184Sek110237 
9065184Sek110237 /*
9075184Sek110237  * Adds an entry to the fileset's file list. Single threaded so
9085184Sek110237  * no locking needed.
9095184Sek110237  */
9105184Sek110237 static void
9115184Sek110237 fileset_insfilelist(fileset_t *fileset, filesetentry_t *entry)
9125184Sek110237 {
9135184Sek110237 	if (fileset->fs_filelist == NULL) {
9145184Sek110237 		fileset->fs_filelist = entry;
9155184Sek110237 		entry->fse_filenext = NULL;
9165184Sek110237 	} else {
9175184Sek110237 		entry->fse_filenext = fileset->fs_filelist;
9185184Sek110237 		fileset->fs_filelist = entry;
9195184Sek110237 	}
9205184Sek110237 }
9215184Sek110237 
9225184Sek110237 /*
9235184Sek110237  * Adds an entry to the fileset's directory list. Single
9245184Sek110237  * threaded so no locking needed.
9255184Sek110237  */
9265184Sek110237 static void
9275184Sek110237 fileset_insdirlist(fileset_t *fileset, filesetentry_t *entry)
9285184Sek110237 {
9295184Sek110237 	if (fileset->fs_dirlist == NULL) {
9305184Sek110237 		fileset->fs_dirlist = entry;
9315184Sek110237 		entry->fse_dirnext = NULL;
9325184Sek110237 	} else {
9335184Sek110237 		entry->fse_dirnext = fileset->fs_dirlist;
9345184Sek110237 		fileset->fs_dirlist = entry;
9355184Sek110237 	}
9365184Sek110237 }
9375184Sek110237 
9385184Sek110237 /*
9395184Sek110237  * Obtaines a filesetentry entity for a file to be placed in a
9405184Sek110237  * (sub)directory of a fileset. The size of the file may be
9416212Saw148015  * specified by fileset_meansize, or calculated from a gamma
9426212Saw148015  * distribution of parameter fileset_sizegamma and of mean size
9436212Saw148015  * fileset_meansize. The filesetentry entity is placed on the file
9445184Sek110237  * list in the specified parent filesetentry entity, which may
9455184Sek110237  * be a directory filesetentry, or the root filesetentry in the
9465184Sek110237  * fileset. It is also placed on the fileset's list of all
9477556SAndrew.W.Wilson@sun.com  * contained files. Returns FILEBENCH_OK if successful or FILEBENCH_ERROR
9487556SAndrew.W.Wilson@sun.com  * if ipc memory for the path string cannot be allocated.
9495184Sek110237  */
9505184Sek110237 static int
9515184Sek110237 fileset_populate_file(fileset_t *fileset, filesetentry_t *parent, int serial)
9525184Sek110237 {
9535184Sek110237 	char tmpname[16];
9545184Sek110237 	filesetentry_t *entry;
9555184Sek110237 	double drand;
9565184Sek110237 
9575184Sek110237 	if ((entry = (filesetentry_t *)ipc_malloc(FILEBENCH_FILESETENTRY))
9585184Sek110237 	    == NULL) {
9595184Sek110237 		filebench_log(LOG_ERROR,
9605184Sek110237 		    "fileset_populate_file: Can't malloc filesetentry");
9617556SAndrew.W.Wilson@sun.com 		return (FILEBENCH_ERROR);
9625184Sek110237 	}
9635184Sek110237 
9647556SAndrew.W.Wilson@sun.com 	/* Another currently idle file */
9657556SAndrew.W.Wilson@sun.com 	(void) ipc_mutex_lock(&fileset->fs_pick_lock);
9667556SAndrew.W.Wilson@sun.com 	fileset->fs_idle_files++;
9677556SAndrew.W.Wilson@sun.com 	(void) ipc_mutex_unlock(&fileset->fs_pick_lock);
9687556SAndrew.W.Wilson@sun.com 
9695184Sek110237 	entry->fse_parent = parent;
9705184Sek110237 	entry->fse_fileset = fileset;
9717556SAndrew.W.Wilson@sun.com 	entry->fse_flags = FSE_FREE;
9725184Sek110237 	fileset_insfilelist(fileset, entry);
9735184Sek110237 
9745184Sek110237 	(void) snprintf(tmpname, sizeof (tmpname), "%08d", serial);
9755184Sek110237 	if ((entry->fse_path = (char *)ipc_pathalloc(tmpname)) == NULL) {
9765184Sek110237 		filebench_log(LOG_ERROR,
9775184Sek110237 		    "fileset_populate_file: Can't alloc path string");
9787556SAndrew.W.Wilson@sun.com 		return (FILEBENCH_ERROR);
9795184Sek110237 	}
9805184Sek110237 
9816212Saw148015 	/* see if random variable was supplied for file size */
9826212Saw148015 	if (fileset->fs_meansize == -1) {
9836212Saw148015 		entry->fse_size = (off64_t)avd_get_int(fileset->fs_size);
9846212Saw148015 	} else {
9856212Saw148015 		double gamma;
9865184Sek110237 
9876212Saw148015 		gamma = avd_get_int(fileset->fs_sizegamma) / 1000.0;
9886212Saw148015 		if (gamma > 0) {
9896212Saw148015 			drand = gamma_dist_knuth(gamma,
9906212Saw148015 			    fileset->fs_meansize / gamma);
9916212Saw148015 			entry->fse_size = (off64_t)drand;
9926212Saw148015 		} else {
9936212Saw148015 			entry->fse_size = (off64_t)fileset->fs_meansize;
9946212Saw148015 		}
9955184Sek110237 	}
9965184Sek110237 
9975184Sek110237 	fileset->fs_bytes += entry->fse_size;
9985184Sek110237 
9995184Sek110237 	fileset->fs_realfiles++;
10007556SAndrew.W.Wilson@sun.com 	return (FILEBENCH_OK);
10015184Sek110237 }
10025184Sek110237 
10035184Sek110237 /*
10045184Sek110237  * Creates a directory node in a fileset, by obtaining a
10055184Sek110237  * filesetentry entity for the node and initializing it
10065184Sek110237  * according to parameters of the fileset. It determines a
10075184Sek110237  * directory tree depth and directory width, optionally using
10085184Sek110237  * a gamma distribution. If its calculated depth is less then
10095184Sek110237  * its actual depth in the directory tree, it becomes a leaf
10105184Sek110237  * node and files itself with "width" number of file type
10115184Sek110237  * filesetentries, otherwise it files itself with "width"
10125184Sek110237  * number of directory type filesetentries, using recursive
10135184Sek110237  * calls to fileset_populate_subdir. The end result of the
10145184Sek110237  * initial call to this routine is a tree of directories of
10155184Sek110237  * random width and varying depth with sufficient leaf
10165184Sek110237  * directories to contain all required files.
10177556SAndrew.W.Wilson@sun.com  * Returns FILEBENCH_OK on success. Returns FILEBENCH_ERROR if ipc path
10187556SAndrew.W.Wilson@sun.com  * string memory cannot be allocated and returns the error code (currently
10197556SAndrew.W.Wilson@sun.com  * also FILEBENCH_ERROR) from calls to fileset_populate_file or recursive
10205184Sek110237  * calls to fileset_populate_subdir.
10215184Sek110237  */
10225184Sek110237 static int
10235184Sek110237 fileset_populate_subdir(fileset_t *fileset, filesetentry_t *parent,
10245184Sek110237     int serial, double depth)
10255184Sek110237 {
10266212Saw148015 	double randepth, drand, ranwidth;
10275184Sek110237 	int isleaf = 0;
10285184Sek110237 	char tmpname[16];
10295184Sek110237 	filesetentry_t *entry;
10305184Sek110237 	int i;
10315184Sek110237 
10325184Sek110237 	depth += 1;
10335184Sek110237 
10345184Sek110237 	/* Create dir node */
10355184Sek110237 	if ((entry = (filesetentry_t *)ipc_malloc(FILEBENCH_FILESETENTRY))
10365184Sek110237 	    == NULL) {
10375184Sek110237 		filebench_log(LOG_ERROR,
10385184Sek110237 		    "fileset_populate_subdir: Can't malloc filesetentry");
10397556SAndrew.W.Wilson@sun.com 		return (FILEBENCH_ERROR);
10405184Sek110237 	}
10415184Sek110237 
10427556SAndrew.W.Wilson@sun.com 	/* another idle directory */
10437556SAndrew.W.Wilson@sun.com 	(void) ipc_mutex_lock(&fileset->fs_pick_lock);
10447556SAndrew.W.Wilson@sun.com 	fileset->fs_idle_dirs++;
10457556SAndrew.W.Wilson@sun.com 	(void) ipc_mutex_unlock(&fileset->fs_pick_lock);
10465184Sek110237 
10475184Sek110237 	(void) snprintf(tmpname, sizeof (tmpname), "%08d", serial);
10485184Sek110237 	if ((entry->fse_path = (char *)ipc_pathalloc(tmpname)) == NULL) {
10495184Sek110237 		filebench_log(LOG_ERROR,
10505184Sek110237 		    "fileset_populate_subdir: Can't alloc path string");
10517556SAndrew.W.Wilson@sun.com 		return (FILEBENCH_ERROR);
10525184Sek110237 	}
10535184Sek110237 
10545184Sek110237 	entry->fse_parent = parent;
10557556SAndrew.W.Wilson@sun.com 	entry->fse_flags = FSE_DIR | FSE_FREE;
10565184Sek110237 	fileset_insdirlist(fileset, entry);
10575184Sek110237 
10586212Saw148015 	if (fileset->fs_dirdepthrv) {
10596212Saw148015 		randepth = (int)avd_get_int(fileset->fs_dirdepthrv);
10605184Sek110237 	} else {
10616212Saw148015 		double gamma;
10626212Saw148015 
10636212Saw148015 		gamma = avd_get_int(fileset->fs_dirgamma) / 1000.0;
10646212Saw148015 		if (gamma > 0) {
10656212Saw148015 			drand = gamma_dist_knuth(gamma,
10666212Saw148015 			    fileset->fs_meandepth / gamma);
10676212Saw148015 			randepth = (int)drand;
10686212Saw148015 		} else {
10696212Saw148015 			randepth = (int)fileset->fs_meandepth;
10706212Saw148015 		}
10715184Sek110237 	}
10725184Sek110237 
10736212Saw148015 	if (fileset->fs_meanwidth == -1) {
10746212Saw148015 		ranwidth = avd_get_dbl(fileset->fs_dirwidth);
10756212Saw148015 	} else {
10766212Saw148015 		double gamma;
10775184Sek110237 
10786212Saw148015 		gamma = avd_get_int(fileset->fs_sizegamma) / 1000.0;
10796212Saw148015 		if (gamma > 0) {
10806212Saw148015 			drand = gamma_dist_knuth(gamma,
10816212Saw148015 			    fileset->fs_meanwidth / gamma);
10826212Saw148015 			ranwidth = drand;
10836212Saw148015 		} else {
10846212Saw148015 			ranwidth = fileset->fs_meanwidth;
10856212Saw148015 		}
10865184Sek110237 	}
10875184Sek110237 
10885184Sek110237 	if (randepth == 0)
10895184Sek110237 		randepth = 1;
10905184Sek110237 	if (ranwidth == 0)
10915184Sek110237 		ranwidth = 1;
10925184Sek110237 	if (depth >= randepth)
10935184Sek110237 		isleaf = 1;
10945184Sek110237 
10955184Sek110237 	/*
10965184Sek110237 	 * Create directory of random width according to distribution, or
10975184Sek110237 	 * if root directory, continue until #files required
10985184Sek110237 	 */
10996212Saw148015 	for (i = 1; ((parent == NULL) || (i < ranwidth + 1)) &&
11006212Saw148015 	    (fileset->fs_realfiles < fileset->fs_constentries);
11016212Saw148015 	    i++) {
11025184Sek110237 		int ret = 0;
11035184Sek110237 
11045184Sek110237 		if (parent && isleaf)
11055184Sek110237 			ret = fileset_populate_file(fileset, entry, i);
11065184Sek110237 		else
11075184Sek110237 			ret = fileset_populate_subdir(fileset, entry, i, depth);
11085184Sek110237 
11095184Sek110237 		if (ret != 0)
11105184Sek110237 			return (ret);
11115184Sek110237 	}
11127556SAndrew.W.Wilson@sun.com 	return (FILEBENCH_OK);
11135184Sek110237 }
11145184Sek110237 
11155184Sek110237 /*
11165184Sek110237  * Populates a fileset with files and subdirectory entries. Uses
11176212Saw148015  * the supplied fileset_dirwidth and fileset_entries (number of files) to
11186212Saw148015  * calculate the required fileset_meandepth (of subdirectories) and
11196212Saw148015  * initialize the fileset_meanwidth and fileset_meansize variables. Then
11205184Sek110237  * calls fileset_populate_subdir() to do the recursive
11215184Sek110237  * subdirectory entry creation and leaf file entry creation. All
11225184Sek110237  * of the above is skipped if the fileset has already been
11235184Sek110237  * populated. Returns 0 on success, or an error code from the
11245184Sek110237  * call to fileset_populate_subdir if that call fails.
11255184Sek110237  */
11265184Sek110237 static int
11275184Sek110237 fileset_populate(fileset_t *fileset)
11285184Sek110237 {
11296212Saw148015 	int entries = (int)avd_get_int(fileset->fs_entries);
11306212Saw148015 	int meandirwidth;
11315184Sek110237 	int ret;
11325184Sek110237 
11335184Sek110237 	/* Skip if already populated */
11345184Sek110237 	if (fileset->fs_bytes > 0)
11355184Sek110237 		goto exists;
11365184Sek110237 
11375673Saw148015 #ifdef HAVE_RAW_SUPPORT
11385673Saw148015 	/* check for raw device */
11395673Saw148015 	if (fileset->fs_attrs & FILESET_IS_RAW_DEV)
11407556SAndrew.W.Wilson@sun.com 		return (FILEBENCH_OK);
11415673Saw148015 #endif /* HAVE_RAW_SUPPORT */
11425673Saw148015 
11436212Saw148015 	/* save value of entries obtained for later, in case it was random */
11446212Saw148015 	fileset->fs_constentries = entries;
11456212Saw148015 
11467556SAndrew.W.Wilson@sun.com 	/* declare all files currently non existant */
11477556SAndrew.W.Wilson@sun.com 	fileset->fs_num_act_files = 0;
11487556SAndrew.W.Wilson@sun.com 
11497556SAndrew.W.Wilson@sun.com 	/* initialize idle files and directories condition variables */
11507556SAndrew.W.Wilson@sun.com 	(void) pthread_cond_init(&fileset->fs_idle_dirs_cv, ipc_condattr());
11517556SAndrew.W.Wilson@sun.com 	(void) pthread_cond_init(&fileset->fs_idle_files_cv, ipc_condattr());
11527556SAndrew.W.Wilson@sun.com 
11537556SAndrew.W.Wilson@sun.com 	/* no files or dirs idle (or busy) yet */
11547556SAndrew.W.Wilson@sun.com 	fileset->fs_idle_files = 0;
11557556SAndrew.W.Wilson@sun.com 	fileset->fs_idle_dirs = 0;
11567556SAndrew.W.Wilson@sun.com 
11577556SAndrew.W.Wilson@sun.com 	/* initialize locks and other condition variables */
11587556SAndrew.W.Wilson@sun.com 	(void) pthread_mutex_init(&fileset->fs_pick_lock,
11597556SAndrew.W.Wilson@sun.com 	    ipc_mutexattr(IPC_MUTEX_NORMAL));
11607556SAndrew.W.Wilson@sun.com 	(void) pthread_cond_init(&fileset->fs_thrd_wait_cv, ipc_condattr());
11617556SAndrew.W.Wilson@sun.com 
11626212Saw148015 	/* is dirwidth a random variable? */
11636212Saw148015 	if (AVD_IS_RANDOM(fileset->fs_dirwidth)) {
11646212Saw148015 		meandirwidth =
11656212Saw148015 		    (int)fileset->fs_dirwidth->avd_val.randptr->rnd_dbl_mean;
11666212Saw148015 		fileset->fs_meanwidth = -1;
11676212Saw148015 	} else {
11686212Saw148015 		meandirwidth = (int)avd_get_int(fileset->fs_dirwidth);
11696212Saw148015 		fileset->fs_meanwidth = (double)meandirwidth;
11706212Saw148015 	}
11716212Saw148015 
11725184Sek110237 	/*
11735184Sek110237 	 * Input params are:
11745184Sek110237 	 *	# of files
11755184Sek110237 	 *	ave # of files per dir
11765184Sek110237 	 *	max size of dir
11775184Sek110237 	 *	# ave size of file
11785184Sek110237 	 *	max size of file
11795184Sek110237 	 */
11806212Saw148015 	fileset->fs_meandepth = log(entries) / log(meandirwidth);
11816212Saw148015 
11826212Saw148015 	/* Has a random variable been supplied for dirdepth? */
11836212Saw148015 	if (fileset->fs_dirdepthrv) {
11846212Saw148015 		/* yes, so set the random variable's mean value to meandepth */
11856212Saw148015 		fileset->fs_dirdepthrv->avd_val.randptr->rnd_dbl_mean =
11866212Saw148015 		    fileset->fs_meandepth;
11876212Saw148015 	}
11886212Saw148015 
11896212Saw148015 	/* test for random size variable */
11906212Saw148015 	if (AVD_IS_RANDOM(fileset->fs_size))
11916212Saw148015 		fileset->fs_meansize = -1;
11926212Saw148015 	else
11936212Saw148015 		fileset->fs_meansize = avd_get_int(fileset->fs_size);
11945184Sek110237 
11955184Sek110237 	if ((ret = fileset_populate_subdir(fileset, NULL, 1, 0)) != 0)
11965184Sek110237 		return (ret);
11975184Sek110237 
11985184Sek110237 
11995184Sek110237 exists:
12005673Saw148015 	if (fileset->fs_attrs & FILESET_IS_FILE) {
12016286Saw148015 		filebench_log(LOG_VERBOSE, "File %s: mbytes=%llu",
12026212Saw148015 		    avd_get_str(fileset->fs_name),
12036286Saw148015 		    (u_longlong_t)(fileset->fs_bytes / 1024UL / 1024UL));
12045673Saw148015 	} else {
12056286Saw148015 		filebench_log(LOG_VERBOSE, "Fileset %s: %d files, "
12066286Saw148015 		    "avg dir = %d, avg depth = %.1lf, mbytes=%llu",
12076212Saw148015 		    avd_get_str(fileset->fs_name), entries,
12086212Saw148015 		    meandirwidth,
12095673Saw148015 		    fileset->fs_meandepth,
12106286Saw148015 		    (u_longlong_t)(fileset->fs_bytes / 1024UL / 1024UL));
12115673Saw148015 	}
12126701Saw148015 
12137556SAndrew.W.Wilson@sun.com 	return (FILEBENCH_OK);
12145184Sek110237 }
12155184Sek110237 
12165184Sek110237 /*
12176212Saw148015  * Allocates a fileset instance, initializes fileset_dirgamma and
12186212Saw148015  * fileset_sizegamma default values, and sets the fileset name to the
12195184Sek110237  * supplied name string. Puts the allocated fileset on the
12205184Sek110237  * master fileset list and returns a pointer to it.
12216701Saw148015  *
12226701Saw148015  * This routine implements the 'define fileset' calls found in a .f
12236701Saw148015  * workload, such as in the following example:
12246701Saw148015  * define fileset name=drew4ever, entries=$nfiles
12255184Sek110237  */
12265184Sek110237 fileset_t *
12276212Saw148015 fileset_define(avd_t name)
12285184Sek110237 {
12295184Sek110237 	fileset_t *fileset;
12305184Sek110237 
12315184Sek110237 	if (name == NULL)
12325184Sek110237 		return (NULL);
12335184Sek110237 
12345184Sek110237 	if ((fileset = (fileset_t *)ipc_malloc(FILEBENCH_FILESET)) == NULL) {
12355184Sek110237 		filebench_log(LOG_ERROR,
12365184Sek110237 		    "fileset_define: Can't malloc fileset");
12375184Sek110237 		return (NULL);
12385184Sek110237 	}
12395184Sek110237 
12406212Saw148015 	filebench_log(LOG_DEBUG_IMPL,
12416212Saw148015 	    "Defining file %s", avd_get_str(name));
12425184Sek110237 
12436391Saw148015 	(void) ipc_mutex_lock(&filebench_shm->shm_fileset_lock);
12445184Sek110237 
12456212Saw148015 	fileset->fs_dirgamma = avd_int_alloc(1500);
12466212Saw148015 	fileset->fs_sizegamma = avd_int_alloc(1500);
12475184Sek110237 
12485184Sek110237 	/* Add fileset to global list */
12496391Saw148015 	if (filebench_shm->shm_filesetlist == NULL) {
12506391Saw148015 		filebench_shm->shm_filesetlist = fileset;
12515184Sek110237 		fileset->fs_next = NULL;
12525184Sek110237 	} else {
12536391Saw148015 		fileset->fs_next = filebench_shm->shm_filesetlist;
12546391Saw148015 		filebench_shm->shm_filesetlist = fileset;
12555184Sek110237 	}
12565184Sek110237 
12576391Saw148015 	(void) ipc_mutex_unlock(&filebench_shm->shm_fileset_lock);
12585184Sek110237 
12596212Saw148015 	fileset->fs_name = name;
12605184Sek110237 
12615184Sek110237 	return (fileset);
12625184Sek110237 }
12635184Sek110237 
12645184Sek110237 /*
12655184Sek110237  * If supplied with a pointer to a fileset and the fileset's
12666212Saw148015  * fileset_prealloc flag is set, calls fileset_populate() to populate
12675184Sek110237  * the fileset with filesetentries, then calls fileset_create()
12685184Sek110237  * to make actual directories and files for the filesetentries.
12695184Sek110237  * Otherwise, it applies fileset_populate() and fileset_create()
12705184Sek110237  * to all the filesets on the master fileset list. It always
12715184Sek110237  * returns zero (0) if one fileset is populated / created,
12725184Sek110237  * otherwise it returns the sum of returned values from
12735184Sek110237  * fileset_create() and fileset_populate(), which
12745184Sek110237  * will be a negative one (-1) times the number of
12755184Sek110237  * fileset_create() calls which failed.
12765184Sek110237  */
12775184Sek110237 int
12785184Sek110237 fileset_createset(fileset_t *fileset)
12795184Sek110237 {
12805184Sek110237 	fileset_t *list;
12815184Sek110237 	int ret = 0;
12825184Sek110237 
12835673Saw148015 	/* set up for possible parallel allocate */
12847556SAndrew.W.Wilson@sun.com 	filebench_shm->shm_fsparalloc_count = 0;
12857556SAndrew.W.Wilson@sun.com 	(void) pthread_cond_init(
12867556SAndrew.W.Wilson@sun.com 	    &filebench_shm->shm_fsparalloc_cv,
12877556SAndrew.W.Wilson@sun.com 	    ipc_condattr());
12885673Saw148015 
12896212Saw148015 	if (fileset && avd_get_bool(fileset->fs_prealloc)) {
12905673Saw148015 
12916305Saw148015 		/* check for raw files */
12926305Saw148015 		if (fileset_checkraw(fileset)) {
12936305Saw148015 			filebench_log(LOG_INFO,
12946305Saw148015 			    "file %s/%s is a RAW device",
12956305Saw148015 			    avd_get_str(fileset->fs_path),
12966305Saw148015 			    avd_get_str(fileset->fs_name));
12977556SAndrew.W.Wilson@sun.com 			return (FILEBENCH_OK);
12986305Saw148015 		}
12996305Saw148015 
13005673Saw148015 		filebench_log(LOG_INFO,
13015673Saw148015 		    "creating/pre-allocating %s %s",
13026212Saw148015 		    fileset_entity_name(fileset),
13036212Saw148015 		    avd_get_str(fileset->fs_name));
13045673Saw148015 
13057556SAndrew.W.Wilson@sun.com 		if ((ret = fileset_populate(fileset)) != FILEBENCH_OK)
13065184Sek110237 			return (ret);
13075673Saw148015 
13087556SAndrew.W.Wilson@sun.com 		if ((ret = fileset_create(fileset)) != FILEBENCH_OK)
13095673Saw148015 			return (ret);
13105673Saw148015 	} else {
13115673Saw148015 
13125673Saw148015 		filebench_log(LOG_INFO,
13135673Saw148015 		    "Creating/pre-allocating files and filesets");
13145673Saw148015 
13156391Saw148015 		list = filebench_shm->shm_filesetlist;
13165673Saw148015 		while (list) {
13176305Saw148015 			/* check for raw files */
13186305Saw148015 			if (fileset_checkraw(list)) {
13196305Saw148015 				filebench_log(LOG_INFO,
13206305Saw148015 				    "file %s/%s is a RAW device",
13216305Saw148015 				    avd_get_str(list->fs_path),
13226305Saw148015 				    avd_get_str(list->fs_name));
13236305Saw148015 				list = list->fs_next;
13246305Saw148015 				continue;
13256305Saw148015 			}
13266305Saw148015 
13277556SAndrew.W.Wilson@sun.com 			if ((ret = fileset_populate(list)) != FILEBENCH_OK)
13285673Saw148015 				return (ret);
13297556SAndrew.W.Wilson@sun.com 
13307556SAndrew.W.Wilson@sun.com 			if ((ret = fileset_create(list)) != FILEBENCH_OK)
13315673Saw148015 				return (ret);
13327556SAndrew.W.Wilson@sun.com 
13335673Saw148015 			list = list->fs_next;
13345673Saw148015 		}
13355184Sek110237 	}
13365184Sek110237 
13375673Saw148015 	/* wait for allocation threads to finish */
13385673Saw148015 	filebench_log(LOG_INFO,
13395673Saw148015 	    "waiting for fileset pre-allocation to finish");
13405184Sek110237 
13417556SAndrew.W.Wilson@sun.com 	(void) pthread_mutex_lock(&filebench_shm->shm_fsparalloc_lock);
13427556SAndrew.W.Wilson@sun.com 	while (filebench_shm->shm_fsparalloc_count > 0)
13437556SAndrew.W.Wilson@sun.com 		(void) pthread_cond_wait(
13447556SAndrew.W.Wilson@sun.com 		    &filebench_shm->shm_fsparalloc_cv,
13457556SAndrew.W.Wilson@sun.com 		    &filebench_shm->shm_fsparalloc_lock);
13467556SAndrew.W.Wilson@sun.com 	(void) pthread_mutex_unlock(&filebench_shm->shm_fsparalloc_lock);
13475673Saw148015 
13487556SAndrew.W.Wilson@sun.com 	if (filebench_shm->shm_fsparalloc_count < 0)
13497556SAndrew.W.Wilson@sun.com 		return (FILEBENCH_ERROR);
13505673Saw148015 
13517556SAndrew.W.Wilson@sun.com 	return (FILEBENCH_OK);
13525184Sek110237 }
13535184Sek110237 
13545184Sek110237 /*
13555184Sek110237  * Searches through the master fileset list for the named fileset.
13565184Sek110237  * If found, returns pointer to same, otherwise returns NULL.
13575184Sek110237  */
13585184Sek110237 fileset_t *
13595184Sek110237 fileset_find(char *name)
13605184Sek110237 {
13616391Saw148015 	fileset_t *fileset = filebench_shm->shm_filesetlist;
13625184Sek110237 
13636391Saw148015 	(void) ipc_mutex_lock(&filebench_shm->shm_fileset_lock);
13645184Sek110237 
13655184Sek110237 	while (fileset) {
13666212Saw148015 		if (strcmp(name, avd_get_str(fileset->fs_name)) == 0) {
13676391Saw148015 			(void) ipc_mutex_unlock(
13686391Saw148015 			    &filebench_shm->shm_fileset_lock);
13695184Sek110237 			return (fileset);
13705184Sek110237 		}
13715184Sek110237 		fileset = fileset->fs_next;
13725184Sek110237 	}
13736391Saw148015 	(void) ipc_mutex_unlock(&filebench_shm->shm_fileset_lock);
13745184Sek110237 
13755184Sek110237 	return (NULL);
13765184Sek110237 }
13775673Saw148015 
13785673Saw148015 /*
13795673Saw148015  * Iterates over all the file sets in the filesetlist,
13805673Saw148015  * executing the supplied command "*cmd()" on them. Also
13815673Saw148015  * indicates to the executed command if it is the first
13825673Saw148015  * time the command has been executed since the current
13835673Saw148015  * call to fileset_iter.
13845673Saw148015  */
13855673Saw148015 void
13865673Saw148015 fileset_iter(int (*cmd)(fileset_t *fileset, int first))
13875673Saw148015 {
13886391Saw148015 	fileset_t *fileset = filebench_shm->shm_filesetlist;
13895673Saw148015 	int count = 0;
13905673Saw148015 
13916391Saw148015 	(void) ipc_mutex_lock(&filebench_shm->shm_fileset_lock);
13925673Saw148015 
13935673Saw148015 	while (fileset) {
13945673Saw148015 		cmd(fileset, count == 0);
13955673Saw148015 		fileset = fileset->fs_next;
13965673Saw148015 		count++;
13975673Saw148015 	}
13985673Saw148015 
13996391Saw148015 	(void) ipc_mutex_unlock(&filebench_shm->shm_fileset_lock);
14005673Saw148015 }
14015673Saw148015 
14025673Saw148015 /*
14035673Saw148015  * Prints information to the filebench log about the file
14045673Saw148015  * object. Also prints a header on the first call.
14055673Saw148015  */
14065673Saw148015 int
14075673Saw148015 fileset_print(fileset_t *fileset, int first)
14085673Saw148015 {
14096212Saw148015 	int pathlength;
14106212Saw148015 	char *fileset_path;
14116212Saw148015 	char *fileset_name;
14126212Saw148015 	static char pad[] = "                              "; /* 30 spaces */
14136212Saw148015 
14146212Saw148015 	if ((fileset_path = avd_get_str(fileset->fs_path)) == NULL) {
14156212Saw148015 		filebench_log(LOG_ERROR, "%s path not set",
14166212Saw148015 		    fileset_entity_name(fileset));
14177556SAndrew.W.Wilson@sun.com 		return (FILEBENCH_ERROR);
14186212Saw148015 	}
14196212Saw148015 
14206212Saw148015 	if ((fileset_name = avd_get_str(fileset->fs_name)) == NULL) {
14216212Saw148015 		filebench_log(LOG_ERROR, "%s name not set",
14226212Saw148015 		    fileset_entity_name(fileset));
14237556SAndrew.W.Wilson@sun.com 		return (FILEBENCH_ERROR);
14246212Saw148015 	}
14256212Saw148015 
14266212Saw148015 	pathlength = strlen(fileset_path) + strlen(fileset_name);
14275673Saw148015 
14285673Saw148015 	if (pathlength > 29)
14295673Saw148015 		pathlength = 29;
14305673Saw148015 
14315673Saw148015 	if (first) {
14325673Saw148015 		filebench_log(LOG_INFO, "File or Fileset name%20s%12s%10s",
14335673Saw148015 		    "file size",
14345673Saw148015 		    "dir width",
14355673Saw148015 		    "entries");
14365673Saw148015 	}
14375673Saw148015 
14385673Saw148015 	if (fileset->fs_attrs & FILESET_IS_FILE) {
14395673Saw148015 		if (fileset->fs_attrs & FILESET_IS_RAW_DEV) {
14405673Saw148015 			filebench_log(LOG_INFO,
14415673Saw148015 			    "%s/%s%s         (Raw Device)",
14426212Saw148015 			    fileset_path, fileset_name, &pad[pathlength]);
14435673Saw148015 		} else {
14445673Saw148015 			filebench_log(LOG_INFO,
14456286Saw148015 			    "%s/%s%s%9llu     (Single File)",
14466212Saw148015 			    fileset_path, fileset_name, &pad[pathlength],
14476286Saw148015 			    (u_longlong_t)avd_get_int(fileset->fs_size));
14485673Saw148015 		}
14495673Saw148015 	} else {
14506286Saw148015 		filebench_log(LOG_INFO, "%s/%s%s%9llu%12llu%10llu",
14516212Saw148015 		    fileset_path, fileset_name,
14525673Saw148015 		    &pad[pathlength],
14536286Saw148015 		    (u_longlong_t)avd_get_int(fileset->fs_size),
14546286Saw148015 		    (u_longlong_t)avd_get_int(fileset->fs_dirwidth),
14556286Saw148015 		    (u_longlong_t)fileset->fs_constentries);
14565673Saw148015 	}
14577556SAndrew.W.Wilson@sun.com 	return (FILEBENCH_OK);
14585673Saw148015 }
14595673Saw148015 /*
14605673Saw148015  * checks to see if the path/name pair points to a raw device. If
14615673Saw148015  * so it sets the raw device flag (FILESET_IS_RAW_DEV) and returns 1.
14625673Saw148015  * If RAW is not defined, or it is not a raw device, it clears the
14635673Saw148015  * raw device flag and returns 0.
14645673Saw148015  */
14655673Saw148015 int
14665673Saw148015 fileset_checkraw(fileset_t *fileset)
14675673Saw148015 {
14685673Saw148015 	char path[MAXPATHLEN];
14695673Saw148015 	struct stat64 sb;
14706305Saw148015 	char *pathname;
14716305Saw148015 	char *setname;
14725673Saw148015 
14735673Saw148015 	fileset->fs_attrs &= (~FILESET_IS_RAW_DEV);
14745673Saw148015 
14755673Saw148015 #ifdef HAVE_RAW_SUPPORT
14765673Saw148015 	/* check for raw device */
14776305Saw148015 	if ((pathname = avd_get_str(fileset->fs_path)) == NULL)
14787556SAndrew.W.Wilson@sun.com 		return (FILEBENCH_OK);
14796305Saw148015 
14806305Saw148015 	if ((setname = avd_get_str(fileset->fs_name)) == NULL)
14817556SAndrew.W.Wilson@sun.com 		return (FILEBENCH_OK);
14826305Saw148015 
14836305Saw148015 	(void) strcpy(path, pathname);
14845673Saw148015 	(void) strcat(path, "/");
14856305Saw148015 	(void) strcat(path, setname);
14865673Saw148015 	if ((stat64(path, &sb) == 0) &&
14875673Saw148015 	    ((sb.st_mode & S_IFMT) == S_IFBLK) && sb.st_rdev) {
14885673Saw148015 		fileset->fs_attrs |= FILESET_IS_RAW_DEV;
14896305Saw148015 		if (!(fileset->fs_attrs & FILESET_IS_FILE)) {
14906305Saw148015 			filebench_log(LOG_ERROR,
14916305Saw148015 			    "WARNING Fileset %s/%s Cannot be RAW device",
14926305Saw148015 			    avd_get_str(fileset->fs_path),
14936305Saw148015 			    avd_get_str(fileset->fs_name));
14946305Saw148015 			filebench_shutdown(1);
14956305Saw148015 		}
14966305Saw148015 
14975673Saw148015 		return (1);
14985673Saw148015 	}
14995673Saw148015 #endif /* HAVE_RAW_SUPPORT */
15005673Saw148015 
15017556SAndrew.W.Wilson@sun.com 	return (FILEBENCH_OK);
15025673Saw148015 }
1503