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