xref: /onnv-gate/usr/src/cmd/filebench/common/fileset.c (revision 8615:fa52c2eca6c5)
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 2009 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 #include <sys/shm.h>
35 
36 #include "filebench.h"
37 #include "fileset.h"
38 #include "gamma_dist.h"
39 #include "utils.h"
40 #include "fsplug.h"
41 
42 /*
43  * File sets, of type fileset_t, are entities which contain
44  * information about collections of files and subdirectories in Filebench.
45  * The fileset, once populated, consists of a tree of fileset entries of
46  * type filesetentry_t which specify files and directories.  The fileset
47  * is rooted in a directory specified by fileset_path, and once the populated
48  * fileset has been created, has a tree of directories and files
49  * corresponding to the fileset's filesetentry tree.
50  *
51  * Fileset entities are allocated by fileset_define() which is called from
52  * parser_gram.y: parser_fileset_define(). The filesetentry tree corrseponding
53  * to the eventual directory and file tree to be instantiated on the storage
54  * medium is built by fileset_populate(), which is This routine is called
55  * from fileset_createset(), which is in turn called by fileset_createset().
56  * After calling fileset_populate(), fileset_createset() will call
57  * fileset_create() to pre-allocate designated files and directories.
58  *
59  * Fileset_createset() is called from parser_gram.y: parser_create_fileset()
60  * when a "create fileset" or "run" command is encountered. When the
61  * "create fileset" command is used, it is generally paired with
62  * a "create processes" command, and must appear first, in order to
63  * instantiate all the files in the fileset before trying to use them.
64  */
65 
66 static int fileset_checkraw(fileset_t *fileset);
67 
68 /* maximum parallel allocation control */
69 #define	MAX_PARALLOC_THREADS 32
70 
71 /*
72  * returns pointer to file or fileset
73  * string, as appropriate
74  */
75 static char *
76 fileset_entity_name(fileset_t *fileset)
77 {
78 	if (fileset->fs_attrs & FILESET_IS_FILE)
79 		return ("file");
80 	else
81 		return ("fileset");
82 }
83 
84 /*
85  * Removes the last file or directory name from a pathname.
86  * Basically removes characters from the end of the path by
87  * setting them to \0 until a forward slash '/' is
88  * encountered. It also removes the forward slash.
89  */
90 static char *
91 trunc_dirname(char *dir)
92 {
93 	char *s = dir + strlen(dir);
94 
95 	while (s != dir) {
96 		int c = *s;
97 
98 		*s = 0;
99 		if (c == '/')
100 			break;
101 		s--;
102 	}
103 	return (dir);
104 }
105 
106 /*
107  * Prints a list of allowed options and how to specify them.
108  */
109 void
110 fileset_usage(void)
111 {
112 	(void) fprintf(stderr,
113 	    "define [file name=<name> | fileset name=<name>],path=<pathname>,"
114 	    ",entries=<number>\n");
115 	(void) fprintf(stderr,
116 	    "		        [,filesize=[size]]\n");
117 	(void) fprintf(stderr,
118 	    "		        [,dirwidth=[width]]\n");
119 	(void) fprintf(stderr,
120 	    "		        [,dirdepthrv=$random_variable_name]\n");
121 	(void) fprintf(stderr,
122 	    "		        [,dirgamma=[100-10000]] "
123 	    "(Gamma * 1000)\n");
124 	(void) fprintf(stderr,
125 	    "		        [,sizegamma=[100-10000]] (Gamma * 1000)\n");
126 	(void) fprintf(stderr,
127 	    "		        [,prealloc=[percent]]\n");
128 	(void) fprintf(stderr, "		        [,paralloc]\n");
129 	(void) fprintf(stderr, "		        [,reuse]\n");
130 	(void) fprintf(stderr, "\n");
131 }
132 
133 /*
134  * Creates a path string from the filesetentry_t "*entry"
135  * and all of its parent's path names. The resulting path
136  * is a concatination of all the individual parent paths.
137  * Allocates memory for the path string and returns a
138  * pointer to it.
139  */
140 char *
141 fileset_resolvepath(filesetentry_t *entry)
142 {
143 	filesetentry_t *fsep = entry;
144 	char path[MAXPATHLEN];
145 	char pathtmp[MAXPATHLEN];
146 	char *s;
147 
148 	path[0] = '\0';
149 	while (fsep->fse_parent) {
150 		(void) strcpy(pathtmp, "/");
151 		(void) fb_strlcat(pathtmp, fsep->fse_path, MAXPATHLEN);
152 		(void) fb_strlcat(pathtmp, path, MAXPATHLEN);
153 		(void) fb_strlcpy(path, pathtmp, MAXPATHLEN);
154 		fsep = fsep->fse_parent;
155 	}
156 
157 	s = malloc(strlen(path) + 1);
158 	(void) fb_strlcpy(s, path, MAXPATHLEN);
159 	return (s);
160 }
161 
162 /*
163  * Creates multiple nested directories as required by the
164  * supplied path. Starts at the end of the path, creating
165  * a list of directories to mkdir, up to the root of the
166  * path, then mkdirs them one at a time from the root on down.
167  */
168 static int
169 fileset_mkdir(char *path, int mode)
170 {
171 	char *p;
172 	char *dirs[65536];
173 	int i = 0;
174 
175 	if ((p = strdup(path)) == NULL)
176 		goto null_str;
177 
178 	/*
179 	 * Fill an array of subdirectory path names until either we
180 	 * reach the root or encounter an already existing subdirectory
181 	 */
182 	/* CONSTCOND */
183 	while (1) {
184 		struct stat64 sb;
185 
186 		if (stat64(p, &sb) == 0)
187 			break;
188 		if (strlen(p) < 3)
189 			break;
190 		if ((dirs[i] = strdup(p)) == NULL) {
191 			free(p);
192 			goto null_str;
193 		}
194 
195 		(void) trunc_dirname(p);
196 		i++;
197 	}
198 
199 	/* Make the directories, from closest to root downwards. */
200 	for (--i; i >= 0; i--) {
201 		(void) FB_MKDIR(dirs[i], mode);
202 		free(dirs[i]);
203 	}
204 
205 	free(p);
206 	return (FILEBENCH_OK);
207 
208 null_str:
209 	/* clean up */
210 	for (--i; i >= 0; i--)
211 		free(dirs[i]);
212 
213 	filebench_log(LOG_ERROR,
214 	    "Failed to create directory path %s: Out of memory", path);
215 	return (FILEBENCH_ERROR);
216 }
217 
218 /*
219  * creates the subdirectory tree for a fileset.
220  */
221 static int
222 fileset_create_subdirs(fileset_t *fileset, char *filesetpath)
223 {
224 	filesetentry_t *direntry;
225 	char full_path[MAXPATHLEN];
226 	char *part_path;
227 
228 	/* walk the subdirectory list, enstanciating subdirs */
229 	direntry = fileset->fs_dirlist;
230 	while (direntry) {
231 		(void) fb_strlcpy(full_path, filesetpath, MAXPATHLEN);
232 		part_path = fileset_resolvepath(direntry);
233 		(void) fb_strlcat(full_path, part_path, MAXPATHLEN);
234 		free(part_path);
235 
236 		/* now create this portion of the subdirectory tree */
237 		if (fileset_mkdir(full_path, 0755) == FILEBENCH_ERROR)
238 			return (FILEBENCH_ERROR);
239 
240 		direntry = direntry->fse_nextoftype;
241 	}
242 	return (FILEBENCH_OK);
243 }
244 
245 /*
246  * move filesetentry between exist tree and non-exist tree, source_tree
247  * to destination tree.
248  */
249 static void
250 fileset_move_entry(avl_tree_t *src_tree, avl_tree_t *dst_tree,
251     filesetentry_t *entry)
252 {
253 	avl_remove(src_tree, entry);
254 	avl_add(dst_tree, entry);
255 }
256 
257 /*
258  * given a fileset entry, determines if the associated leaf directory
259  * needs to be made or not, and if so does the mkdir.
260  */
261 static int
262 fileset_alloc_leafdir(filesetentry_t *entry)
263 {
264 	fileset_t *fileset;
265 	char path[MAXPATHLEN];
266 	struct stat64 sb;
267 	char *pathtmp;
268 
269 	fileset = entry->fse_fileset;
270 	(void) fb_strlcpy(path, avd_get_str(fileset->fs_path), MAXPATHLEN);
271 	(void) fb_strlcat(path, "/", MAXPATHLEN);
272 	(void) fb_strlcat(path, avd_get_str(fileset->fs_name), MAXPATHLEN);
273 	pathtmp = fileset_resolvepath(entry);
274 	(void) fb_strlcat(path, pathtmp, MAXPATHLEN);
275 	free(pathtmp);
276 
277 	filebench_log(LOG_DEBUG_IMPL, "Populated %s", entry->fse_path);
278 
279 	/* see if not reusing and this directory does not exist */
280 	if (!((entry->fse_flags & FSE_REUSING) && (stat64(path, &sb) == 0))) {
281 
282 		/* No file or not reusing, so create */
283 		if (FB_MKDIR(path, 0755) < 0) {
284 			filebench_log(LOG_ERROR,
285 			    "Failed to pre-allocate leaf directory %s: %s",
286 			    path, strerror(errno));
287 			fileset_unbusy(entry, TRUE, FALSE, 0);
288 			return (FILEBENCH_ERROR);
289 		}
290 	}
291 
292 	/* unbusy the allocated entry */
293 	fileset_unbusy(entry, TRUE, TRUE, 0);
294 	return (FILEBENCH_OK);
295 }
296 
297 /*
298  * given a fileset entry, determines if the associated file
299  * needs to be allocated or not, and if so does the allocation.
300  */
301 static int
302 fileset_alloc_file(filesetentry_t *entry)
303 {
304 	fileset_t *fileset;
305 	char path[MAXPATHLEN];
306 	char *buf;
307 	struct stat64 sb;
308 	char *pathtmp;
309 	off64_t seek;
310 	fb_fdesc_t fdesc;
311 
312 	fileset = entry->fse_fileset;
313 	(void) fb_strlcpy(path, avd_get_str(fileset->fs_path), MAXPATHLEN);
314 	(void) fb_strlcat(path, "/", MAXPATHLEN);
315 	(void) fb_strlcat(path, avd_get_str(fileset->fs_name), MAXPATHLEN);
316 	pathtmp = fileset_resolvepath(entry);
317 	(void) fb_strlcat(path, pathtmp, MAXPATHLEN);
318 	free(pathtmp);
319 
320 	filebench_log(LOG_DEBUG_IMPL, "Populated %s", entry->fse_path);
321 
322 	/* see if reusing and this file exists */
323 	if ((entry->fse_flags & FSE_REUSING) && (FB_STAT(path, &sb) == 0)) {
324 		if (FB_OPEN(&fdesc, path, O_RDWR, 0) == FILEBENCH_ERROR) {
325 			filebench_log(LOG_INFO,
326 			    "Attempted but failed to Re-use file %s",
327 			    path);
328 			fileset_unbusy(entry, TRUE, FALSE, 0);
329 			return (FILEBENCH_ERROR);
330 		}
331 
332 		if (sb.st_size == (off64_t)entry->fse_size) {
333 			filebench_log(LOG_DEBUG_IMPL,
334 			    "Re-using file %s", path);
335 
336 			if (!avd_get_bool(fileset->fs_cached))
337 				(void) FB_FREEMEM(&fdesc, entry->fse_size);
338 
339 			(void) FB_CLOSE(&fdesc);
340 
341 			/* unbusy the allocated entry */
342 			fileset_unbusy(entry, TRUE, TRUE, 0);
343 			return (FILEBENCH_OK);
344 
345 		} else if (sb.st_size > (off64_t)entry->fse_size) {
346 			/* reuse, but too large */
347 			filebench_log(LOG_DEBUG_IMPL,
348 			    "Truncating & re-using file %s", path);
349 
350 			(void) FB_FTRUNC(&fdesc, (off64_t)entry->fse_size);
351 
352 			if (!avd_get_bool(fileset->fs_cached))
353 				(void) FB_FREEMEM(&fdesc, entry->fse_size);
354 
355 			(void) FB_CLOSE(&fdesc);
356 
357 			/* unbusy the allocated entry */
358 			fileset_unbusy(entry, TRUE, TRUE, 0);
359 			return (FILEBENCH_OK);
360 		}
361 	} else {
362 
363 		/* No file or not reusing, so create */
364 		if (FB_OPEN(&fdesc, path, O_RDWR | O_CREAT, 0644) ==
365 		    FILEBENCH_ERROR) {
366 			filebench_log(LOG_ERROR,
367 			    "Failed to pre-allocate file %s: %s",
368 			    path, strerror(errno));
369 
370 			/* unbusy the unallocated entry */
371 			fileset_unbusy(entry, TRUE, FALSE, 0);
372 			return (FILEBENCH_ERROR);
373 		}
374 	}
375 
376 	if ((buf = (char *)malloc(FILE_ALLOC_BLOCK)) == NULL) {
377 		/* unbusy the unallocated entry */
378 		fileset_unbusy(entry, TRUE, FALSE, 0);
379 		return (FILEBENCH_ERROR);
380 	}
381 
382 	for (seek = 0; seek < entry->fse_size; ) {
383 		off64_t wsize;
384 		int ret = 0;
385 
386 		/*
387 		 * Write FILE_ALLOC_BLOCK's worth,
388 		 * except on last write
389 		 */
390 		wsize = MIN(entry->fse_size - seek, FILE_ALLOC_BLOCK);
391 
392 		ret = FB_WRITE(&fdesc, buf, wsize);
393 		if (ret != wsize) {
394 			filebench_log(LOG_ERROR,
395 			    "Failed to pre-allocate file %s: %s",
396 			    path, strerror(errno));
397 			(void) FB_CLOSE(&fdesc);
398 			free(buf);
399 			fileset_unbusy(entry, TRUE, FALSE, 0);
400 			return (FILEBENCH_ERROR);
401 		}
402 		seek += wsize;
403 	}
404 
405 	if (!avd_get_bool(fileset->fs_cached))
406 		(void) FB_FREEMEM(&fdesc, entry->fse_size);
407 
408 	(void) FB_CLOSE(&fdesc);
409 
410 	free(buf);
411 
412 	/* unbusy the allocated entry */
413 	fileset_unbusy(entry, TRUE, TRUE, 0);
414 
415 	filebench_log(LOG_DEBUG_IMPL,
416 	    "Pre-allocated file %s size %llu",
417 	    path, (u_longlong_t)entry->fse_size);
418 
419 	return (FILEBENCH_OK);
420 }
421 
422 /*
423  * given a fileset entry, determines if the associated file
424  * needs to be allocated or not, and if so does the allocation.
425  * Sets shm_fsparalloc_count to -1 on error.
426  */
427 static void *
428 fileset_alloc_thread(filesetentry_t *entry)
429 {
430 	if (fileset_alloc_file(entry) == FILEBENCH_ERROR) {
431 		(void) pthread_mutex_lock(&filebench_shm->shm_fsparalloc_lock);
432 		filebench_shm->shm_fsparalloc_count = -1;
433 	} else {
434 		(void) pthread_mutex_lock(&filebench_shm->shm_fsparalloc_lock);
435 		filebench_shm->shm_fsparalloc_count--;
436 	}
437 
438 	(void) pthread_cond_signal(&filebench_shm->shm_fsparalloc_cv);
439 	(void) pthread_mutex_unlock(&filebench_shm->shm_fsparalloc_lock);
440 
441 	pthread_exit(NULL);
442 	return (NULL);
443 }
444 
445 
446 /*
447  * First creates the parent directories of the file using
448  * fileset_mkdir(). Then Optionally sets the O_DSYNC flag
449  * and opens the file with open64(). It unlocks the fileset
450  * entry lock, sets the DIRECTIO_ON or DIRECTIO_OFF flags
451  * as requested, and returns the file descriptor integer
452  * for the opened file in the supplied filebench file descriptor.
453  * Returns FILEBENCH_ERROR on error, and FILEBENCH_OK on success.
454  */
455 int
456 fileset_openfile(fb_fdesc_t *fdesc, fileset_t *fileset,
457     filesetentry_t *entry, int flag, int filemode, int attrs)
458 {
459 	char path[MAXPATHLEN];
460 	char dir[MAXPATHLEN];
461 	char *pathtmp;
462 	struct stat64 sb;
463 	int open_attrs = 0;
464 
465 	(void) fb_strlcpy(path, avd_get_str(fileset->fs_path), MAXPATHLEN);
466 	(void) fb_strlcat(path, "/", MAXPATHLEN);
467 	(void) fb_strlcat(path, avd_get_str(fileset->fs_name), MAXPATHLEN);
468 	pathtmp = fileset_resolvepath(entry);
469 	(void) fb_strlcat(path, pathtmp, MAXPATHLEN);
470 	(void) fb_strlcpy(dir, path, MAXPATHLEN);
471 	free(pathtmp);
472 	(void) trunc_dirname(dir);
473 
474 	/* If we are going to create a file, create the parent dirs */
475 	if ((flag & O_CREAT) && (stat64(dir, &sb) != 0)) {
476 		if (fileset_mkdir(dir, 0755) == FILEBENCH_ERROR)
477 			return (FILEBENCH_ERROR);
478 	}
479 
480 	if (attrs & FLOW_ATTR_DSYNC) {
481 #ifdef sun
482 		open_attrs |= O_DSYNC;
483 #else
484 		open_attrs |= O_FSYNC;
485 #endif
486 	}
487 
488 	if (FB_OPEN(fdesc, path, flag | open_attrs, filemode)
489 	    == FILEBENCH_ERROR) {
490 		filebench_log(LOG_ERROR,
491 		    "Failed to open file %d, %s, with status %x: %s",
492 		    entry->fse_index, path, entry->fse_flags, strerror(errno));
493 
494 		fileset_unbusy(entry, FALSE, FALSE, 0);
495 		return (FILEBENCH_ERROR);
496 	}
497 
498 	if (flag & O_CREAT)
499 		fileset_unbusy(entry, TRUE, TRUE, 1);
500 	else
501 		fileset_unbusy(entry, FALSE, FALSE, 1);
502 
503 #ifdef sun
504 	if (attrs & FLOW_ATTR_DIRECTIO)
505 		(void) directio(fdesc->fd_num, DIRECTIO_ON);
506 	else
507 		(void) directio(fdesc->fd_num, DIRECTIO_OFF);
508 #endif
509 
510 	return (FILEBENCH_OK);
511 }
512 
513 /*
514  * removes all filesetentries from their respective btrees, and puts them
515  * on the free list. The supplied argument indicates which free list to
516  * use.
517  */
518 static void
519 fileset_pickreset(fileset_t *fileset, int entry_type)
520 {
521 	filesetentry_t	*entry;
522 
523 	switch (entry_type & FILESET_PICKMASK) {
524 	case FILESET_PICKFILE:
525 		entry = (filesetentry_t *)avl_first(&fileset->fs_noex_files);
526 
527 		/* make sure non-existing files are marked free */
528 		while (entry) {
529 			entry->fse_flags |= FSE_FREE;
530 			entry->fse_open_cnt = 0;
531 			fileset_move_entry(&fileset->fs_noex_files,
532 			    &fileset->fs_free_files, entry);
533 			entry =  AVL_NEXT(&fileset->fs_noex_files, entry);
534 		}
535 
536 		/* free up any existing files */
537 		entry = (filesetentry_t *)avl_first(&fileset->fs_exist_files);
538 
539 		while (entry) {
540 			entry->fse_flags |= FSE_FREE;
541 			entry->fse_open_cnt = 0;
542 			fileset_move_entry(&fileset->fs_exist_files,
543 			    &fileset->fs_free_files, entry);
544 
545 			entry =  AVL_NEXT(&fileset->fs_exist_files, entry);
546 		}
547 
548 		break;
549 
550 	case FILESET_PICKDIR:
551 		/* nothing to reset, as all (sub)dirs always exist */
552 		break;
553 
554 	case FILESET_PICKLEAFDIR:
555 		entry = (filesetentry_t *)
556 		    avl_first(&fileset->fs_noex_leaf_dirs);
557 
558 		/* make sure non-existing leaf dirs are marked free */
559 		while (entry) {
560 			entry->fse_flags |= FSE_FREE;
561 			entry->fse_open_cnt = 0;
562 			fileset_move_entry(&fileset->fs_noex_leaf_dirs,
563 			    &fileset->fs_free_leaf_dirs, entry);
564 			entry =  AVL_NEXT(&fileset->fs_noex_leaf_dirs, entry);
565 		}
566 
567 		/* free up any existing leaf dirs */
568 		entry = (filesetentry_t *)
569 		    avl_first(&fileset->fs_exist_leaf_dirs);
570 
571 		while (entry) {
572 			entry->fse_flags |= FSE_FREE;
573 			entry->fse_open_cnt = 0;
574 			fileset_move_entry(&fileset->fs_exist_leaf_dirs,
575 			    &fileset->fs_free_leaf_dirs, entry);
576 
577 			entry =  AVL_NEXT(&fileset->fs_exist_leaf_dirs, entry);
578 		}
579 
580 		break;
581 	}
582 }
583 
584 /*
585  * find a filesetentry from the fileset using the supplied index
586  */
587 static filesetentry_t *
588 fileset_find_entry(avl_tree_t *atp, uint_t index)
589 {
590 	avl_index_t	found_loc;
591 	filesetentry_t	desired_fse, *found_fse;
592 
593 	/* find the file with the desired index, if it is in the tree */
594 	desired_fse.fse_index = index;
595 	found_fse = avl_find(atp, (void *)(&desired_fse), &found_loc);
596 	if (found_fse != NULL)
597 		return (found_fse);
598 
599 	/* if requested node not found, find next higher node */
600 	found_fse = avl_nearest(atp, found_loc, AVL_AFTER);
601 	if (found_fse != NULL)
602 		return (found_fse);
603 
604 	/* might have hit the end, return lowest available index node */
605 	found_fse = avl_first(atp);
606 	return (found_fse);
607 }
608 
609 /*
610  * Selects a fileset entry from a fileset. If the
611  * FILESET_PICKLEAFDIR flag is set it will pick a leaf directory entry,
612  * if the FILESET_PICKDIR flag is set it will pick a non leaf directory
613  * entry, otherwise a file entry. The FILESET_PICKUNIQUE
614  * flag will take an entry off of one of the free (unused)
615  * lists (file or directory), otherwise the entry will be
616  * picked off of one of the rotor lists (file or directory).
617  * The FILESET_PICKEXISTS will insure that only extant
618  * (FSE_EXISTS) state files are selected, while
619  * FILESET_PICKNOEXIST insures that only non extant
620  * (not FSE_EXISTS) state files are selected.
621  * Note that the selected fileset entry (file) is returned
622  * with its FSE_BUSY flag (in fse_flags) set.
623  */
624 filesetentry_t *
625 fileset_pick(fileset_t *fileset, int flags, int tid, int index)
626 {
627 	filesetentry_t *entry = NULL;
628 	filesetentry_t *start_point;
629 	avl_tree_t *atp;
630 	fbint_t max_entries;
631 
632 	(void) ipc_mutex_lock(&fileset->fs_pick_lock);
633 
634 	/* see if we have to wait for available files or directories */
635 	switch (flags & FILESET_PICKMASK) {
636 	case FILESET_PICKFILE:
637 		if (fileset->fs_filelist == NULL)
638 			goto empty;
639 
640 		while (fileset->fs_idle_files == 0) {
641 			(void) pthread_cond_wait(&fileset->fs_idle_files_cv,
642 			    &fileset->fs_pick_lock);
643 		}
644 
645 		max_entries = fileset->fs_constentries;
646 		if (flags & FILESET_PICKUNIQUE) {
647 			atp = &fileset->fs_free_files;
648 		} else if (flags & FILESET_PICKNOEXIST) {
649 			atp = &fileset->fs_noex_files;
650 		} else {
651 			atp = &fileset->fs_exist_files;
652 		}
653 		break;
654 
655 	case FILESET_PICKDIR:
656 		if (fileset->fs_dirlist == NULL)
657 			goto empty;
658 
659 		while (fileset->fs_idle_dirs == 0) {
660 			(void) pthread_cond_wait(&fileset->fs_idle_dirs_cv,
661 			    &fileset->fs_pick_lock);
662 		}
663 
664 		max_entries = 1;
665 		atp = &fileset->fs_dirs;
666 		break;
667 
668 	case FILESET_PICKLEAFDIR:
669 		if (fileset->fs_leafdirlist == NULL)
670 			goto empty;
671 
672 		while (fileset->fs_idle_leafdirs == 0) {
673 			(void) pthread_cond_wait(&fileset->fs_idle_leafdirs_cv,
674 			    &fileset->fs_pick_lock);
675 		}
676 
677 		max_entries = fileset->fs_constleafdirs;
678 		if (flags & FILESET_PICKUNIQUE) {
679 			atp = &fileset->fs_free_leaf_dirs;
680 		} else if (flags & FILESET_PICKNOEXIST) {
681 			atp = &fileset->fs_noex_leaf_dirs;
682 		} else {
683 			atp = &fileset->fs_exist_leaf_dirs;
684 		}
685 		break;
686 	}
687 
688 	/* see if asking for impossible */
689 	if (avl_is_empty(atp))
690 		goto empty;
691 
692 	if (flags & FILESET_PICKUNIQUE) {
693 		uint64_t  index64;
694 
695 		/*
696 		 * pick at random from free list in order to
697 		 * distribute initially allocated files more
698 		 * randomly on storage media. Use uniform
699 		 * random number generator to select index
700 		 * if it is not supplied with pick call.
701 		 */
702 		if (index) {
703 			index64 = index;
704 		} else {
705 			if (filebench_randomno64(&index64, max_entries, 1,
706 			    NULL) == FILEBENCH_ERROR)
707 				return (NULL);
708 		}
709 
710 		entry = fileset_find_entry(atp, (int)index64);
711 
712 		if (entry == NULL)
713 			goto empty;
714 
715 	} else if (flags & FILESET_PICKBYINDEX) {
716 		/* pick by supplied index */
717 		entry = fileset_find_entry(atp, index);
718 
719 	} else {
720 		/* pick in rotation */
721 		switch (flags & FILESET_PICKMASK) {
722 		case FILESET_PICKFILE:
723 			if (flags & FILESET_PICKNOEXIST) {
724 				entry = fileset_find_entry(atp,
725 				    fileset->fs_file_nerotor);
726 				fileset->fs_file_nerotor =
727 				    entry->fse_index + 1;
728 			} else {
729 				entry = fileset_find_entry(atp,
730 				    fileset->fs_file_exrotor[tid]);
731 				fileset->fs_file_exrotor[tid] =
732 				    entry->fse_index + 1;
733 			}
734 			break;
735 
736 		case FILESET_PICKDIR:
737 			entry = fileset_find_entry(atp, fileset->fs_dirrotor);
738 			fileset->fs_dirrotor = entry->fse_index + 1;
739 			break;
740 
741 		case FILESET_PICKLEAFDIR:
742 			if (flags & FILESET_PICKNOEXIST) {
743 				entry = fileset_find_entry(atp,
744 				    fileset->fs_leafdir_nerotor);
745 				fileset->fs_leafdir_nerotor =
746 				    entry->fse_index + 1;
747 			} else {
748 				entry = fileset_find_entry(atp,
749 				    fileset->fs_leafdir_exrotor);
750 				fileset->fs_leafdir_exrotor =
751 				    entry->fse_index + 1;
752 			}
753 			break;
754 		}
755 	}
756 
757 	if (entry == NULL)
758 		goto empty;
759 
760 	/* see if entry in use */
761 	start_point = entry;
762 	while (entry->fse_flags & FSE_BUSY) {
763 
764 		/* it is, so try next */
765 		entry = AVL_NEXT(atp, entry);
766 		if (entry == NULL)
767 			entry = avl_first(atp);
768 
769 		/* see if we have wrapped around */
770 		if ((entry == NULL) || (entry == start_point)) {
771 			filebench_log(LOG_DEBUG_SCRIPT,
772 			    "All %d files are busy", avl_numnodes(atp));
773 			goto empty;
774 		}
775 
776 	}
777 
778 	/* update file or directory idle counts */
779 	switch (flags & FILESET_PICKMASK) {
780 	case FILESET_PICKFILE:
781 		fileset->fs_idle_files--;
782 		break;
783 	case FILESET_PICKDIR:
784 		fileset->fs_idle_dirs--;
785 		break;
786 	case FILESET_PICKLEAFDIR:
787 		fileset->fs_idle_leafdirs--;
788 		break;
789 	}
790 
791 	/* Indicate that file or directory is now busy */
792 	entry->fse_flags |= FSE_BUSY;
793 
794 	(void) ipc_mutex_unlock(&fileset->fs_pick_lock);
795 	filebench_log(LOG_DEBUG_SCRIPT, "Picked file %s", entry->fse_path);
796 	return (entry);
797 
798 empty:
799 	filebench_log(LOG_DEBUG_SCRIPT, "No file found");
800 	(void) ipc_mutex_unlock(&fileset->fs_pick_lock);
801 	return (NULL);
802 }
803 
804 /*
805  * Removes a filesetentry from the "FSE_BUSY" state, signaling any threads
806  * that are waiting for a NOT BUSY filesetentry. Also sets whether it is
807  * existant or not, or leaves that designation alone.
808  */
809 void
810 fileset_unbusy(filesetentry_t *entry, int update_exist,
811     int new_exist_val, int open_cnt_incr)
812 {
813 	fileset_t *fileset = NULL;
814 
815 	if (entry)
816 		fileset = entry->fse_fileset;
817 
818 	if (fileset == NULL) {
819 		filebench_log(LOG_ERROR, "fileset_unbusy: NO FILESET!");
820 		return;
821 	}
822 
823 	(void) ipc_mutex_lock(&fileset->fs_pick_lock);
824 
825 	/* modify FSE_EXIST flag and actual dirs/files count, if requested */
826 	if (update_exist) {
827 		if (new_exist_val == TRUE) {
828 			if (entry->fse_flags & FSE_FREE) {
829 
830 				/* asked to set and it was free */
831 				entry->fse_flags |= FSE_EXISTS;
832 				entry->fse_flags &= (~FSE_FREE);
833 				switch (entry->fse_flags & FSE_TYPE_MASK) {
834 				case FSE_TYPE_FILE:
835 					fileset_move_entry(
836 					    &fileset->fs_free_files,
837 					    &fileset->fs_exist_files, entry);
838 					break;
839 
840 				case FSE_TYPE_DIR:
841 					break;
842 
843 				case FSE_TYPE_LEAFDIR:
844 					fileset_move_entry(
845 					    &fileset->fs_free_leaf_dirs,
846 					    &fileset->fs_exist_leaf_dirs,
847 					    entry);
848 					break;
849 				}
850 
851 			} else if (!(entry->fse_flags & FSE_EXISTS)) {
852 
853 				/* asked to set, and it was clear */
854 				entry->fse_flags |= FSE_EXISTS;
855 				switch (entry->fse_flags & FSE_TYPE_MASK) {
856 				case FSE_TYPE_FILE:
857 					fileset_move_entry(
858 					    &fileset->fs_noex_files,
859 					    &fileset->fs_exist_files, entry);
860 					break;
861 				case FSE_TYPE_DIR:
862 					break;
863 				case FSE_TYPE_LEAFDIR:
864 					fileset_move_entry(
865 					    &fileset->fs_noex_leaf_dirs,
866 					    &fileset->fs_exist_leaf_dirs,
867 					    entry);
868 					break;
869 				}
870 			}
871 		} else {
872 			if (entry->fse_flags & FSE_FREE) {
873 				/* asked to clear, and it was free */
874 				entry->fse_flags &= (~(FSE_FREE | FSE_EXISTS));
875 				switch (entry->fse_flags & FSE_TYPE_MASK) {
876 				case FSE_TYPE_FILE:
877 					fileset_move_entry(
878 					    &fileset->fs_free_files,
879 					    &fileset->fs_noex_files, entry);
880 					break;
881 
882 				case FSE_TYPE_DIR:
883 					break;
884 
885 				case FSE_TYPE_LEAFDIR:
886 					fileset_move_entry(
887 					    &fileset->fs_free_leaf_dirs,
888 					    &fileset->fs_noex_leaf_dirs,
889 					    entry);
890 					break;
891 				}
892 			} else if (entry->fse_flags & FSE_EXISTS) {
893 
894 				/* asked to clear, and it was set */
895 				entry->fse_flags &= (~FSE_EXISTS);
896 				switch (entry->fse_flags & FSE_TYPE_MASK) {
897 				case FSE_TYPE_FILE:
898 					fileset_move_entry(
899 					    &fileset->fs_exist_files,
900 					    &fileset->fs_noex_files, entry);
901 					break;
902 				case FSE_TYPE_DIR:
903 					break;
904 				case FSE_TYPE_LEAFDIR:
905 					fileset_move_entry(
906 					    &fileset->fs_exist_leaf_dirs,
907 					    &fileset->fs_noex_leaf_dirs,
908 					    entry);
909 					break;
910 				}
911 			}
912 		}
913 	}
914 
915 	/* update open count */
916 	entry->fse_open_cnt += open_cnt_incr;
917 
918 	/* increment idle count, clear FSE_BUSY and signal IF it was busy */
919 	if (entry->fse_flags & FSE_BUSY) {
920 
921 		/* unbusy it */
922 		entry->fse_flags &= (~FSE_BUSY);
923 
924 		/* release any threads waiting for unbusy */
925 		if (entry->fse_flags & FSE_THRD_WAITNG) {
926 			entry->fse_flags &= (~FSE_THRD_WAITNG);
927 			(void) pthread_cond_broadcast(
928 			    &fileset->fs_thrd_wait_cv);
929 		}
930 
931 		/* increment idle count and signal waiting threads */
932 		switch (entry->fse_flags & FSE_TYPE_MASK) {
933 		case FSE_TYPE_FILE:
934 			fileset->fs_idle_files++;
935 			if (fileset->fs_idle_files == 1) {
936 				(void) pthread_cond_signal(
937 				    &fileset->fs_idle_files_cv);
938 			}
939 			break;
940 
941 		case FSE_TYPE_DIR:
942 			fileset->fs_idle_dirs++;
943 			if (fileset->fs_idle_dirs == 1) {
944 				(void) pthread_cond_signal(
945 				    &fileset->fs_idle_dirs_cv);
946 			}
947 			break;
948 
949 		case FSE_TYPE_LEAFDIR:
950 			fileset->fs_idle_leafdirs++;
951 			if (fileset->fs_idle_leafdirs == 1) {
952 				(void) pthread_cond_signal(
953 				    &fileset->fs_idle_leafdirs_cv);
954 			}
955 			break;
956 		}
957 	}
958 
959 	(void) ipc_mutex_unlock(&fileset->fs_pick_lock);
960 }
961 
962 /*
963  * Given a fileset "fileset", create the associated files as
964  * specified in the attributes of the fileset. The fileset is
965  * rooted in a directory whose pathname is in fileset_path. If the
966  * directory exists, meaning that there is already a fileset,
967  * and the fileset_reuse attribute is false, then remove it and all
968  * its contained files and subdirectories. Next, the routine
969  * creates a root directory for the fileset. All the file type
970  * filesetentries are cycled through creating as needed
971  * their containing subdirectory trees in the filesystem and
972  * creating actual files for fileset_preallocpercent of them. The
973  * created files are filled with fse_size bytes of unitialized
974  * data. The routine returns FILEBENCH_ERROR on errors,
975  * FILEBENCH_OK on success.
976  */
977 static int
978 fileset_create(fileset_t *fileset)
979 {
980 	filesetentry_t *entry;
981 	char path[MAXPATHLEN];
982 	struct stat64 sb;
983 	hrtime_t start = gethrtime();
984 	char *fileset_path;
985 	char *fileset_name;
986 	int randno;
987 	int preallocated = 0;
988 	int reusing;
989 
990 	if ((fileset_path = avd_get_str(fileset->fs_path)) == NULL) {
991 		filebench_log(LOG_ERROR, "%s path not set",
992 		    fileset_entity_name(fileset));
993 		return (FILEBENCH_ERROR);
994 	}
995 
996 	if ((fileset_name = avd_get_str(fileset->fs_name)) == NULL) {
997 		filebench_log(LOG_ERROR, "%s name not set",
998 		    fileset_entity_name(fileset));
999 		return (FILEBENCH_ERROR);
1000 	}
1001 
1002 #ifdef HAVE_RAW_SUPPORT
1003 	/* treat raw device as special case */
1004 	if (fileset->fs_attrs & FILESET_IS_RAW_DEV)
1005 		return (FILEBENCH_OK);
1006 #endif /* HAVE_RAW_SUPPORT */
1007 
1008 	/* XXX Add check to see if there is enough space */
1009 
1010 	/* set up path to fileset */
1011 	(void) fb_strlcpy(path, fileset_path, MAXPATHLEN);
1012 	(void) fb_strlcat(path, "/", MAXPATHLEN);
1013 	(void) fb_strlcat(path, fileset_name, MAXPATHLEN);
1014 
1015 	/* if exists and resusing, then don't create new */
1016 	if (((stat64(path, &sb) == 0)&& (strlen(path) > 3) &&
1017 	    (strlen(avd_get_str(fileset->fs_path)) > 2)) &&
1018 	    avd_get_bool(fileset->fs_reuse)) {
1019 		reusing = 1;
1020 	} else {
1021 		reusing = 0;
1022 	}
1023 
1024 	if (!reusing) {
1025 		char cmd[MAXPATHLEN];
1026 
1027 		/* Remove existing */
1028 		(void) snprintf(cmd, sizeof (cmd), "rm -rf %s", path);
1029 		(void) system(cmd);
1030 		filebench_log(LOG_VERBOSE,
1031 		    "Removed any existing %s %s in %llu seconds",
1032 		    fileset_entity_name(fileset), fileset_name,
1033 		    (u_longlong_t)(((gethrtime() - start) /
1034 		    1000000000) + 1));
1035 	} else {
1036 		/* we are re-using */
1037 		filebench_log(LOG_VERBOSE, "Re-using %s %s.",
1038 		    fileset_entity_name(fileset), fileset_name);
1039 	}
1040 
1041 	/* make the filesets directory tree unless in reuse mode */
1042 	if (!reusing && (avd_get_bool(fileset->fs_prealloc))) {
1043 		filebench_log(LOG_VERBOSE,
1044 		    "making tree for filset %s", path);
1045 
1046 		(void) FB_MKDIR(path, 0755);
1047 
1048 		if (fileset_create_subdirs(fileset, path) == FILEBENCH_ERROR)
1049 			return (FILEBENCH_ERROR);
1050 	}
1051 
1052 	start = gethrtime();
1053 
1054 	filebench_log(LOG_VERBOSE, "Creating %s %s...",
1055 	    fileset_entity_name(fileset), fileset_name);
1056 
1057 	randno = ((RAND_MAX * (100
1058 	    - avd_get_int(fileset->fs_preallocpercent))) / 100);
1059 
1060 	/* alloc any files, as required */
1061 	fileset_pickreset(fileset, FILESET_PICKFILE);
1062 	while (entry = fileset_pick(fileset,
1063 	    FILESET_PICKFREE | FILESET_PICKFILE, 0, 0)) {
1064 		pthread_t tid;
1065 		int newrand;
1066 
1067 		newrand = rand();
1068 
1069 		if (newrand < randno) {
1070 			/* unbusy the unallocated entry */
1071 			fileset_unbusy(entry, TRUE, FALSE, 0);
1072 			continue;
1073 		}
1074 
1075 		preallocated++;
1076 
1077 		if (reusing)
1078 			entry->fse_flags |= FSE_REUSING;
1079 		else
1080 			entry->fse_flags &= (~FSE_REUSING);
1081 
1082 		/* fire off allocation threads for each file if paralloc set */
1083 		if (avd_get_bool(fileset->fs_paralloc)) {
1084 
1085 			/* limit total number of simultaneous allocations */
1086 			(void) pthread_mutex_lock(
1087 			    &filebench_shm->shm_fsparalloc_lock);
1088 			while (filebench_shm->shm_fsparalloc_count
1089 			    >= MAX_PARALLOC_THREADS) {
1090 				(void) pthread_cond_wait(
1091 				    &filebench_shm->shm_fsparalloc_cv,
1092 				    &filebench_shm->shm_fsparalloc_lock);
1093 			}
1094 
1095 			/* quit if any allocation thread reports an error */
1096 			if (filebench_shm->shm_fsparalloc_count < 0) {
1097 				(void) pthread_mutex_unlock(
1098 				    &filebench_shm->shm_fsparalloc_lock);
1099 				return (FILEBENCH_ERROR);
1100 			}
1101 
1102 			filebench_shm->shm_fsparalloc_count++;
1103 			(void) pthread_mutex_unlock(
1104 			    &filebench_shm->shm_fsparalloc_lock);
1105 
1106 			/*
1107 			 * Fire off a detached allocation thread per file.
1108 			 * The thread will self destruct when it finishes
1109 			 * writing pre-allocation data to the file.
1110 			 */
1111 			if (pthread_create(&tid, NULL,
1112 			    (void *(*)(void*))fileset_alloc_thread,
1113 			    entry) == 0) {
1114 				/*
1115 				 * A thread was created; detach it so it can
1116 				 * fully quit when finished.
1117 				 */
1118 				(void) pthread_detach(tid);
1119 			} else {
1120 				filebench_log(LOG_ERROR,
1121 				    "File prealloc thread create failed");
1122 				filebench_shutdown(1);
1123 			}
1124 
1125 		} else {
1126 			if (fileset_alloc_file(entry) == FILEBENCH_ERROR)
1127 				return (FILEBENCH_ERROR);
1128 		}
1129 	}
1130 
1131 	/* alloc any leaf directories, as required */
1132 	fileset_pickreset(fileset, FILESET_PICKLEAFDIR);
1133 	while (entry = fileset_pick(fileset,
1134 	    FILESET_PICKFREE | FILESET_PICKLEAFDIR, 0, 0)) {
1135 
1136 		if (rand() < randno) {
1137 			/* unbusy the unallocated entry */
1138 			fileset_unbusy(entry, TRUE, FALSE, 0);
1139 			continue;
1140 		}
1141 
1142 		preallocated++;
1143 
1144 		if (reusing)
1145 			entry->fse_flags |= FSE_REUSING;
1146 		else
1147 			entry->fse_flags &= (~FSE_REUSING);
1148 
1149 		if (fileset_alloc_leafdir(entry) == FILEBENCH_ERROR)
1150 			return (FILEBENCH_ERROR);
1151 	}
1152 
1153 exit:
1154 	filebench_log(LOG_VERBOSE,
1155 	    "Preallocated %d of %llu of %s %s in %llu seconds",
1156 	    preallocated,
1157 	    (u_longlong_t)fileset->fs_constentries,
1158 	    fileset_entity_name(fileset), fileset_name,
1159 	    (u_longlong_t)(((gethrtime() - start) / 1000000000) + 1));
1160 
1161 	return (FILEBENCH_OK);
1162 }
1163 
1164 /*
1165  * Adds an entry to the fileset's file list. Single threaded so
1166  * no locking needed.
1167  */
1168 static void
1169 fileset_insfilelist(fileset_t *fileset, filesetentry_t *entry)
1170 {
1171 	entry->fse_flags = FSE_TYPE_FILE | FSE_FREE;
1172 	avl_add(&fileset->fs_free_files, entry);
1173 
1174 	if (fileset->fs_filelist == NULL) {
1175 		fileset->fs_filelist = entry;
1176 		entry->fse_nextoftype = NULL;
1177 	} else {
1178 		entry->fse_nextoftype = fileset->fs_filelist;
1179 		fileset->fs_filelist = entry;
1180 	}
1181 }
1182 
1183 /*
1184  * Adds an entry to the fileset's directory list. Single
1185  * threaded so no locking needed.
1186  */
1187 static void
1188 fileset_insdirlist(fileset_t *fileset, filesetentry_t *entry)
1189 {
1190 	entry->fse_flags = FSE_TYPE_DIR | FSE_EXISTS;
1191 	avl_add(&fileset->fs_dirs, entry);
1192 
1193 	if (fileset->fs_dirlist == NULL) {
1194 		fileset->fs_dirlist = entry;
1195 		entry->fse_nextoftype = NULL;
1196 	} else {
1197 		entry->fse_nextoftype = fileset->fs_dirlist;
1198 		fileset->fs_dirlist = entry;
1199 	}
1200 }
1201 
1202 /*
1203  * Adds an entry to the fileset's leaf directory list. Single
1204  * threaded so no locking needed.
1205  */
1206 static void
1207 fileset_insleafdirlist(fileset_t *fileset, filesetentry_t *entry)
1208 {
1209 	entry->fse_flags = FSE_TYPE_LEAFDIR | FSE_FREE;
1210 	avl_add(&fileset->fs_free_leaf_dirs, entry);
1211 
1212 	if (fileset->fs_leafdirlist == NULL) {
1213 		fileset->fs_leafdirlist = entry;
1214 		entry->fse_nextoftype = NULL;
1215 	} else {
1216 		entry->fse_nextoftype = fileset->fs_leafdirlist;
1217 		fileset->fs_leafdirlist = entry;
1218 	}
1219 }
1220 
1221 /*
1222  * Compares two fileset entries to determine their relative order
1223  */
1224 static int
1225 fileset_entry_compare(const void *node_1, const void *node_2)
1226 {
1227 	if (((filesetentry_t *)node_1)->fse_index <
1228 	    ((filesetentry_t *)node_2)->fse_index)
1229 		return (-1);
1230 
1231 	if (((filesetentry_t *)node_1)->fse_index ==
1232 	    ((filesetentry_t *)node_2)->fse_index)
1233 		return (0);
1234 
1235 	return (1);
1236 }
1237 
1238 /*
1239  * Obtains a filesetentry entity for a file to be placed in a
1240  * (sub)directory of a fileset. The size of the file may be
1241  * specified by fileset_meansize, or calculated from a gamma
1242  * distribution of parameter fileset_sizegamma and of mean size
1243  * fileset_meansize. The filesetentry entity is placed on the file
1244  * list in the specified parent filesetentry entity, which may
1245  * be a directory filesetentry, or the root filesetentry in the
1246  * fileset. It is also placed on the fileset's list of all
1247  * contained files. Returns FILEBENCH_OK if successful or FILEBENCH_ERROR
1248  * if ipc memory for the path string cannot be allocated.
1249  */
1250 static int
1251 fileset_populate_file(fileset_t *fileset, filesetentry_t *parent, int serial)
1252 {
1253 	char tmpname[16];
1254 	filesetentry_t *entry;
1255 	double drand;
1256 	uint_t index;
1257 
1258 	if ((entry = (filesetentry_t *)ipc_malloc(FILEBENCH_FILESETENTRY))
1259 	    == NULL) {
1260 		filebench_log(LOG_ERROR,
1261 		    "fileset_populate_file: Can't malloc filesetentry");
1262 		return (FILEBENCH_ERROR);
1263 	}
1264 
1265 	/* Another currently idle file */
1266 	(void) ipc_mutex_lock(&fileset->fs_pick_lock);
1267 	index = fileset->fs_idle_files++;
1268 	(void) ipc_mutex_unlock(&fileset->fs_pick_lock);
1269 
1270 	entry->fse_index = index;
1271 	entry->fse_parent = parent;
1272 	entry->fse_fileset = fileset;
1273 	fileset_insfilelist(fileset, entry);
1274 
1275 	(void) snprintf(tmpname, sizeof (tmpname), "%08d", serial);
1276 	if ((entry->fse_path = (char *)ipc_pathalloc(tmpname)) == NULL) {
1277 		filebench_log(LOG_ERROR,
1278 		    "fileset_populate_file: Can't alloc path string");
1279 		return (FILEBENCH_ERROR);
1280 	}
1281 
1282 	/* see if random variable was supplied for file size */
1283 	if (fileset->fs_meansize == -1) {
1284 		entry->fse_size = (off64_t)avd_get_int(fileset->fs_size);
1285 	} else {
1286 		double gamma;
1287 
1288 		gamma = avd_get_int(fileset->fs_sizegamma) / 1000.0;
1289 		if (gamma > 0) {
1290 			drand = gamma_dist_knuth(gamma,
1291 			    fileset->fs_meansize / gamma);
1292 			entry->fse_size = (off64_t)drand;
1293 		} else {
1294 			entry->fse_size = (off64_t)fileset->fs_meansize;
1295 		}
1296 	}
1297 
1298 	fileset->fs_bytes += entry->fse_size;
1299 
1300 	fileset->fs_realfiles++;
1301 	return (FILEBENCH_OK);
1302 }
1303 
1304 /*
1305  * Obtaines a filesetentry entity for a leaf directory to be placed in a
1306  * (sub)directory of a fileset. The leaf directory will always be empty so
1307  * it can be created and deleted (mkdir, rmdir) at will. The filesetentry
1308  * entity is placed on the leaf directory list in the specified parent
1309  * filesetentry entity, which may be a (sub) directory filesetentry, or
1310  * the root filesetentry in the fileset. It is also placed on the fileset's
1311  * list of all contained leaf directories. Returns FILEBENCH_OK if successful
1312  * or FILEBENCH_ERROR if ipc memory cannot be allocated.
1313  */
1314 static int
1315 fileset_populate_leafdir(fileset_t *fileset, filesetentry_t *parent, int serial)
1316 {
1317 	char tmpname[16];
1318 	filesetentry_t *entry;
1319 	uint_t index;
1320 
1321 	if ((entry = (filesetentry_t *)ipc_malloc(FILEBENCH_FILESETENTRY))
1322 	    == NULL) {
1323 		filebench_log(LOG_ERROR,
1324 		    "fileset_populate_file: Can't malloc filesetentry");
1325 		return (FILEBENCH_ERROR);
1326 	}
1327 
1328 	/* Another currently idle leaf directory */
1329 	(void) ipc_mutex_lock(&fileset->fs_pick_lock);
1330 	index = fileset->fs_idle_leafdirs++;
1331 	(void) ipc_mutex_unlock(&fileset->fs_pick_lock);
1332 
1333 	entry->fse_index = index;
1334 	entry->fse_parent = parent;
1335 	entry->fse_fileset = fileset;
1336 	fileset_insleafdirlist(fileset, entry);
1337 
1338 	(void) snprintf(tmpname, sizeof (tmpname), "%08d", serial);
1339 	if ((entry->fse_path = (char *)ipc_pathalloc(tmpname)) == NULL) {
1340 		filebench_log(LOG_ERROR,
1341 		    "fileset_populate_file: Can't alloc path string");
1342 		return (FILEBENCH_ERROR);
1343 	}
1344 
1345 	fileset->fs_realleafdirs++;
1346 	return (FILEBENCH_OK);
1347 }
1348 
1349 /*
1350  * Creates a directory node in a fileset, by obtaining a
1351  * filesetentry entity for the node and initializing it
1352  * according to parameters of the fileset. It determines a
1353  * directory tree depth and directory width, optionally using
1354  * a gamma distribution. If its calculated depth is less then
1355  * its actual depth in the directory tree, it becomes a leaf
1356  * node and files itself with "width" number of file type
1357  * filesetentries, otherwise it files itself with "width"
1358  * number of directory type filesetentries, using recursive
1359  * calls to fileset_populate_subdir. The end result of the
1360  * initial call to this routine is a tree of directories of
1361  * random width and varying depth with sufficient leaf
1362  * directories to contain all required files.
1363  * Returns FILEBENCH_OK on success. Returns FILEBENCH_ERROR if ipc path
1364  * string memory cannot be allocated and returns the error code (currently
1365  * also FILEBENCH_ERROR) from calls to fileset_populate_file or recursive
1366  * calls to fileset_populate_subdir.
1367  */
1368 static int
1369 fileset_populate_subdir(fileset_t *fileset, filesetentry_t *parent,
1370     int serial, double depth)
1371 {
1372 	double randepth, drand, ranwidth;
1373 	int isleaf = 0;
1374 	char tmpname[16];
1375 	filesetentry_t *entry;
1376 	int i;
1377 	uint_t index;
1378 
1379 	depth += 1;
1380 
1381 	/* Create dir node */
1382 	if ((entry = (filesetentry_t *)ipc_malloc(FILEBENCH_FILESETENTRY))
1383 	    == NULL) {
1384 		filebench_log(LOG_ERROR,
1385 		    "fileset_populate_subdir: Can't malloc filesetentry");
1386 		return (FILEBENCH_ERROR);
1387 	}
1388 
1389 	/* another idle directory */
1390 	(void) ipc_mutex_lock(&fileset->fs_pick_lock);
1391 	index = fileset->fs_idle_dirs++;
1392 	(void) ipc_mutex_unlock(&fileset->fs_pick_lock);
1393 
1394 	(void) snprintf(tmpname, sizeof (tmpname), "%08d", serial);
1395 	if ((entry->fse_path = (char *)ipc_pathalloc(tmpname)) == NULL) {
1396 		filebench_log(LOG_ERROR,
1397 		    "fileset_populate_subdir: Can't alloc path string");
1398 		return (FILEBENCH_ERROR);
1399 	}
1400 
1401 	entry->fse_index = index;
1402 	entry->fse_parent = parent;
1403 	entry->fse_fileset = fileset;
1404 	fileset_insdirlist(fileset, entry);
1405 
1406 	if (fileset->fs_dirdepthrv) {
1407 		randepth = (int)avd_get_int(fileset->fs_dirdepthrv);
1408 	} else {
1409 		double gamma;
1410 
1411 		gamma = avd_get_int(fileset->fs_dirgamma) / 1000.0;
1412 		if (gamma > 0) {
1413 			drand = gamma_dist_knuth(gamma,
1414 			    fileset->fs_meandepth / gamma);
1415 			randepth = (int)drand;
1416 		} else {
1417 			randepth = (int)fileset->fs_meandepth;
1418 		}
1419 	}
1420 
1421 	if (fileset->fs_meanwidth == -1) {
1422 		ranwidth = avd_get_dbl(fileset->fs_dirwidth);
1423 	} else {
1424 		double gamma;
1425 
1426 		gamma = avd_get_int(fileset->fs_sizegamma) / 1000.0;
1427 		if (gamma > 0) {
1428 			drand = gamma_dist_knuth(gamma,
1429 			    fileset->fs_meanwidth / gamma);
1430 			ranwidth = drand;
1431 		} else {
1432 			ranwidth = fileset->fs_meanwidth;
1433 		}
1434 	}
1435 
1436 	if (randepth == 0)
1437 		randepth = 1;
1438 	if (ranwidth == 0)
1439 		ranwidth = 1;
1440 	if (depth >= randepth)
1441 		isleaf = 1;
1442 
1443 	/*
1444 	 * Create directory of random width filled with files according
1445 	 * to distribution, or if root directory, continue until #files required
1446 	 */
1447 	for (i = 1; ((parent == NULL) || (i < ranwidth + 1)) &&
1448 	    (fileset->fs_realfiles < fileset->fs_constentries);
1449 	    i++) {
1450 		int ret = 0;
1451 
1452 		if (parent && isleaf)
1453 			ret = fileset_populate_file(fileset, entry, i);
1454 		else
1455 			ret = fileset_populate_subdir(fileset, entry, i, depth);
1456 
1457 		if (ret != 0)
1458 			return (ret);
1459 	}
1460 
1461 	/*
1462 	 * Create directory of random width filled with leaf directories
1463 	 * according to distribution, or if root directory, continue until
1464 	 * the number of leaf directories required has been generated.
1465 	 */
1466 	for (i = 1; ((parent == NULL) || (i < ranwidth + 1)) &&
1467 	    (fileset->fs_realleafdirs < fileset->fs_constleafdirs);
1468 	    i++) {
1469 		int ret = 0;
1470 
1471 		if (parent && isleaf)
1472 			ret = fileset_populate_leafdir(fileset, entry, i);
1473 		else
1474 			ret = fileset_populate_subdir(fileset, entry, i, depth);
1475 
1476 		if (ret != 0)
1477 			return (ret);
1478 	}
1479 
1480 	return (FILEBENCH_OK);
1481 }
1482 
1483 /*
1484  * Populates a fileset with files and subdirectory entries. Uses
1485  * the supplied fileset_dirwidth and fileset_entries (number of files) to
1486  * calculate the required fileset_meandepth (of subdirectories) and
1487  * initialize the fileset_meanwidth and fileset_meansize variables. Then
1488  * calls fileset_populate_subdir() to do the recursive
1489  * subdirectory entry creation and leaf file entry creation. All
1490  * of the above is skipped if the fileset has already been
1491  * populated. Returns 0 on success, or an error code from the
1492  * call to fileset_populate_subdir if that call fails.
1493  */
1494 static int
1495 fileset_populate(fileset_t *fileset)
1496 {
1497 	fbint_t entries = avd_get_int(fileset->fs_entries);
1498 	fbint_t leafdirs = avd_get_int(fileset->fs_leafdirs);
1499 	int meandirwidth;
1500 	int ret;
1501 
1502 	/* Skip if already populated */
1503 	if (fileset->fs_bytes > 0)
1504 		goto exists;
1505 
1506 #ifdef HAVE_RAW_SUPPORT
1507 	/* check for raw device */
1508 	if (fileset->fs_attrs & FILESET_IS_RAW_DEV)
1509 		return (FILEBENCH_OK);
1510 #endif /* HAVE_RAW_SUPPORT */
1511 
1512 	/*
1513 	 * save value of entries and leaf dirs obtained for later
1514 	 * in case it was random
1515 	 */
1516 	fileset->fs_constentries = entries;
1517 	fileset->fs_constleafdirs = leafdirs;
1518 
1519 	/* initialize idle files and directories condition variables */
1520 	(void) pthread_cond_init(&fileset->fs_idle_files_cv, ipc_condattr());
1521 	(void) pthread_cond_init(&fileset->fs_idle_dirs_cv, ipc_condattr());
1522 	(void) pthread_cond_init(&fileset->fs_idle_leafdirs_cv, ipc_condattr());
1523 
1524 	/* no files or dirs idle (or busy) yet */
1525 	fileset->fs_idle_files = 0;
1526 	fileset->fs_idle_dirs = 0;
1527 	fileset->fs_idle_leafdirs = 0;
1528 
1529 	/* initialize locks and other condition variables */
1530 	(void) pthread_mutex_init(&fileset->fs_pick_lock,
1531 	    ipc_mutexattr(IPC_MUTEX_NORMAL));
1532 	(void) pthread_mutex_init(&fileset->fs_histo_lock,
1533 	    ipc_mutexattr(IPC_MUTEX_NORMAL));
1534 	(void) pthread_cond_init(&fileset->fs_thrd_wait_cv, ipc_condattr());
1535 
1536 	/* Initialize avl btrees */
1537 	avl_create(&(fileset->fs_free_files), fileset_entry_compare,
1538 	    sizeof (filesetentry_t), FSE_OFFSETOF(fse_link));
1539 	avl_create(&(fileset->fs_noex_files), fileset_entry_compare,
1540 	    sizeof (filesetentry_t), FSE_OFFSETOF(fse_link));
1541 	avl_create(&(fileset->fs_exist_files), fileset_entry_compare,
1542 	    sizeof (filesetentry_t), FSE_OFFSETOF(fse_link));
1543 	avl_create(&(fileset->fs_free_leaf_dirs), fileset_entry_compare,
1544 	    sizeof (filesetentry_t), FSE_OFFSETOF(fse_link));
1545 	avl_create(&(fileset->fs_noex_leaf_dirs), fileset_entry_compare,
1546 	    sizeof (filesetentry_t), FSE_OFFSETOF(fse_link));
1547 	avl_create(&(fileset->fs_exist_leaf_dirs), fileset_entry_compare,
1548 	    sizeof (filesetentry_t), FSE_OFFSETOF(fse_link));
1549 	avl_create(&(fileset->fs_dirs), fileset_entry_compare,
1550 	    sizeof (filesetentry_t), FSE_OFFSETOF(fse_link));
1551 
1552 	/* is dirwidth a random variable? */
1553 	if (AVD_IS_RANDOM(fileset->fs_dirwidth)) {
1554 		meandirwidth =
1555 		    (int)fileset->fs_dirwidth->avd_val.randptr->rnd_dbl_mean;
1556 		fileset->fs_meanwidth = -1;
1557 	} else {
1558 		meandirwidth = (int)avd_get_int(fileset->fs_dirwidth);
1559 		fileset->fs_meanwidth = (double)meandirwidth;
1560 	}
1561 
1562 	/*
1563 	 * Input params are:
1564 	 *	# of files
1565 	 *	ave # of files per dir
1566 	 *	max size of dir
1567 	 *	# ave size of file
1568 	 *	max size of file
1569 	 */
1570 	fileset->fs_meandepth = log(entries+leafdirs) / log(meandirwidth);
1571 
1572 	/* Has a random variable been supplied for dirdepth? */
1573 	if (fileset->fs_dirdepthrv) {
1574 		/* yes, so set the random variable's mean value to meandepth */
1575 		fileset->fs_dirdepthrv->avd_val.randptr->rnd_dbl_mean =
1576 		    fileset->fs_meandepth;
1577 	}
1578 
1579 	/* test for random size variable */
1580 	if (AVD_IS_RANDOM(fileset->fs_size))
1581 		fileset->fs_meansize = -1;
1582 	else
1583 		fileset->fs_meansize = avd_get_int(fileset->fs_size);
1584 
1585 	if ((ret = fileset_populate_subdir(fileset, NULL, 1, 0)) != 0)
1586 		return (ret);
1587 
1588 
1589 exists:
1590 	if (fileset->fs_attrs & FILESET_IS_FILE) {
1591 		filebench_log(LOG_VERBOSE, "File %s: mbytes=%llu",
1592 		    avd_get_str(fileset->fs_name),
1593 		    (u_longlong_t)(fileset->fs_bytes / 1024UL / 1024UL));
1594 	} else {
1595 		filebench_log(LOG_VERBOSE, "Fileset %s: %d files, %d leafdirs "
1596 		    "avg dir = %d, avg depth = %.1lf, mbytes=%llu",
1597 		    avd_get_str(fileset->fs_name), entries, leafdirs,
1598 		    meandirwidth,
1599 		    fileset->fs_meandepth,
1600 		    (u_longlong_t)(fileset->fs_bytes / 1024UL / 1024UL));
1601 	}
1602 
1603 	return (FILEBENCH_OK);
1604 }
1605 
1606 /*
1607  * Allocates a fileset instance, initializes fileset_dirgamma and
1608  * fileset_sizegamma default values, and sets the fileset name to the
1609  * supplied name string. Puts the allocated fileset on the
1610  * master fileset list and returns a pointer to it.
1611  *
1612  * This routine implements the 'define fileset' calls found in a .f
1613  * workload, such as in the following example:
1614  * define fileset name=drew4ever, entries=$nfiles
1615  */
1616 fileset_t *
1617 fileset_define(avd_t name)
1618 {
1619 	fileset_t *fileset;
1620 
1621 	if (name == NULL)
1622 		return (NULL);
1623 
1624 	if ((fileset = (fileset_t *)ipc_malloc(FILEBENCH_FILESET)) == NULL) {
1625 		filebench_log(LOG_ERROR,
1626 		    "fileset_define: Can't malloc fileset");
1627 		return (NULL);
1628 	}
1629 
1630 	filebench_log(LOG_DEBUG_IMPL,
1631 	    "Defining file %s", avd_get_str(name));
1632 
1633 	(void) ipc_mutex_lock(&filebench_shm->shm_fileset_lock);
1634 
1635 	fileset->fs_dirgamma = avd_int_alloc(1500);
1636 	fileset->fs_sizegamma = avd_int_alloc(1500);
1637 	fileset->fs_histo_id = -1;
1638 
1639 	/* Add fileset to global list */
1640 	if (filebench_shm->shm_filesetlist == NULL) {
1641 		filebench_shm->shm_filesetlist = fileset;
1642 		fileset->fs_next = NULL;
1643 	} else {
1644 		fileset->fs_next = filebench_shm->shm_filesetlist;
1645 		filebench_shm->shm_filesetlist = fileset;
1646 	}
1647 
1648 	(void) ipc_mutex_unlock(&filebench_shm->shm_fileset_lock);
1649 
1650 	fileset->fs_name = name;
1651 
1652 	return (fileset);
1653 }
1654 
1655 /*
1656  * If supplied with a pointer to a fileset and the fileset's
1657  * fileset_prealloc flag is set, calls fileset_populate() to populate
1658  * the fileset with filesetentries, then calls fileset_create()
1659  * to make actual directories and files for the filesetentries.
1660  * Otherwise, it applies fileset_populate() and fileset_create()
1661  * to all the filesets on the master fileset list. It always
1662  * returns zero (0) if one fileset is populated / created,
1663  * otherwise it returns the sum of returned values from
1664  * fileset_create() and fileset_populate(), which
1665  * will be a negative one (-1) times the number of
1666  * fileset_create() calls which failed.
1667  */
1668 int
1669 fileset_createset(fileset_t *fileset)
1670 {
1671 	fileset_t *list;
1672 	int ret = 0;
1673 
1674 	/* set up for possible parallel allocate */
1675 	filebench_shm->shm_fsparalloc_count = 0;
1676 	(void) pthread_cond_init(
1677 	    &filebench_shm->shm_fsparalloc_cv,
1678 	    ipc_condattr());
1679 
1680 	if (fileset && avd_get_bool(fileset->fs_prealloc)) {
1681 
1682 		/* check for raw files */
1683 		if (fileset_checkraw(fileset)) {
1684 			filebench_log(LOG_INFO,
1685 			    "file %s/%s is a RAW device",
1686 			    avd_get_str(fileset->fs_path),
1687 			    avd_get_str(fileset->fs_name));
1688 			return (FILEBENCH_OK);
1689 		}
1690 
1691 		filebench_log(LOG_INFO,
1692 		    "creating/pre-allocating %s %s",
1693 		    fileset_entity_name(fileset),
1694 		    avd_get_str(fileset->fs_name));
1695 
1696 		if ((ret = fileset_populate(fileset)) != FILEBENCH_OK)
1697 			return (ret);
1698 
1699 		if ((ret = fileset_create(fileset)) != FILEBENCH_OK)
1700 			return (ret);
1701 	} else {
1702 
1703 		filebench_log(LOG_INFO,
1704 		    "Creating/pre-allocating files and filesets");
1705 
1706 		list = filebench_shm->shm_filesetlist;
1707 		while (list) {
1708 			/* check for raw files */
1709 			if (fileset_checkraw(list)) {
1710 				filebench_log(LOG_INFO,
1711 				    "file %s/%s is a RAW device",
1712 				    avd_get_str(list->fs_path),
1713 				    avd_get_str(list->fs_name));
1714 				list = list->fs_next;
1715 				continue;
1716 			}
1717 
1718 			if ((ret = fileset_populate(list)) != FILEBENCH_OK)
1719 				return (ret);
1720 
1721 			if ((ret = fileset_create(list)) != FILEBENCH_OK)
1722 				return (ret);
1723 
1724 			list = list->fs_next;
1725 		}
1726 	}
1727 
1728 	/* wait for allocation threads to finish */
1729 	filebench_log(LOG_INFO,
1730 	    "waiting for fileset pre-allocation to finish");
1731 
1732 	(void) pthread_mutex_lock(&filebench_shm->shm_fsparalloc_lock);
1733 	while (filebench_shm->shm_fsparalloc_count > 0)
1734 		(void) pthread_cond_wait(
1735 		    &filebench_shm->shm_fsparalloc_cv,
1736 		    &filebench_shm->shm_fsparalloc_lock);
1737 	(void) pthread_mutex_unlock(&filebench_shm->shm_fsparalloc_lock);
1738 
1739 	if (filebench_shm->shm_fsparalloc_count < 0)
1740 		return (FILEBENCH_ERROR);
1741 
1742 	return (FILEBENCH_OK);
1743 }
1744 
1745 /*
1746  * Searches through the master fileset list for the named fileset.
1747  * If found, returns pointer to same, otherwise returns NULL.
1748  */
1749 fileset_t *
1750 fileset_find(char *name)
1751 {
1752 	fileset_t *fileset = filebench_shm->shm_filesetlist;
1753 
1754 	(void) ipc_mutex_lock(&filebench_shm->shm_fileset_lock);
1755 
1756 	while (fileset) {
1757 		if (strcmp(name, avd_get_str(fileset->fs_name)) == 0) {
1758 			(void) ipc_mutex_unlock(
1759 			    &filebench_shm->shm_fileset_lock);
1760 			return (fileset);
1761 		}
1762 		fileset = fileset->fs_next;
1763 	}
1764 	(void) ipc_mutex_unlock(&filebench_shm->shm_fileset_lock);
1765 
1766 	return (NULL);
1767 }
1768 
1769 /*
1770  * Iterates over all the file sets in the filesetlist,
1771  * executing the supplied command "*cmd()" on them. Also
1772  * indicates to the executed command if it is the first
1773  * time the command has been executed since the current
1774  * call to fileset_iter.
1775  */
1776 int
1777 fileset_iter(int (*cmd)(fileset_t *fileset, int first))
1778 {
1779 	fileset_t *fileset = filebench_shm->shm_filesetlist;
1780 	int count = 0;
1781 
1782 	(void) ipc_mutex_lock(&filebench_shm->shm_fileset_lock);
1783 
1784 	while (fileset) {
1785 		if (cmd(fileset, count == 0) == FILEBENCH_ERROR) {
1786 			(void) ipc_mutex_unlock(
1787 			    &filebench_shm->shm_fileset_lock);
1788 			return (FILEBENCH_ERROR);
1789 		}
1790 		fileset = fileset->fs_next;
1791 		count++;
1792 	}
1793 
1794 	(void) ipc_mutex_unlock(&filebench_shm->shm_fileset_lock);
1795 	return (FILEBENCH_OK);
1796 }
1797 
1798 /*
1799  * Prints information to the filebench log about the file
1800  * object. Also prints a header on the first call.
1801  */
1802 int
1803 fileset_print(fileset_t *fileset, int first)
1804 {
1805 	int pathlength;
1806 	char *fileset_path;
1807 	char *fileset_name;
1808 	static char pad[] = "                              "; /* 30 spaces */
1809 
1810 	if ((fileset_path = avd_get_str(fileset->fs_path)) == NULL) {
1811 		filebench_log(LOG_ERROR, "%s path not set",
1812 		    fileset_entity_name(fileset));
1813 		return (FILEBENCH_ERROR);
1814 	}
1815 
1816 	if ((fileset_name = avd_get_str(fileset->fs_name)) == NULL) {
1817 		filebench_log(LOG_ERROR, "%s name not set",
1818 		    fileset_entity_name(fileset));
1819 		return (FILEBENCH_ERROR);
1820 	}
1821 
1822 	pathlength = strlen(fileset_path) + strlen(fileset_name);
1823 
1824 	if (pathlength > 29)
1825 		pathlength = 29;
1826 
1827 	if (first) {
1828 		filebench_log(LOG_INFO, "File or Fileset name%20s%12s%10s",
1829 		    "file size",
1830 		    "dir width",
1831 		    "entries");
1832 	}
1833 
1834 	if (fileset->fs_attrs & FILESET_IS_FILE) {
1835 		if (fileset->fs_attrs & FILESET_IS_RAW_DEV) {
1836 			filebench_log(LOG_INFO,
1837 			    "%s/%s%s         (Raw Device)",
1838 			    fileset_path, fileset_name, &pad[pathlength]);
1839 		} else {
1840 			filebench_log(LOG_INFO,
1841 			    "%s/%s%s%9llu     (Single File)",
1842 			    fileset_path, fileset_name, &pad[pathlength],
1843 			    (u_longlong_t)avd_get_int(fileset->fs_size));
1844 		}
1845 	} else {
1846 		filebench_log(LOG_INFO, "%s/%s%s%9llu%12llu%10llu",
1847 		    fileset_path, fileset_name,
1848 		    &pad[pathlength],
1849 		    (u_longlong_t)avd_get_int(fileset->fs_size),
1850 		    (u_longlong_t)avd_get_int(fileset->fs_dirwidth),
1851 		    (u_longlong_t)fileset->fs_constentries);
1852 	}
1853 	return (FILEBENCH_OK);
1854 }
1855 
1856 /*
1857  * checks to see if the path/name pair points to a raw device. If
1858  * so it sets the raw device flag (FILESET_IS_RAW_DEV) and returns 1.
1859  * If RAW is not defined, or it is not a raw device, it clears the
1860  * raw device flag and returns 0.
1861  */
1862 int
1863 fileset_checkraw(fileset_t *fileset)
1864 {
1865 	char path[MAXPATHLEN];
1866 	struct stat64 sb;
1867 	char *pathname;
1868 	char *setname;
1869 
1870 	fileset->fs_attrs &= (~FILESET_IS_RAW_DEV);
1871 
1872 #ifdef HAVE_RAW_SUPPORT
1873 	/* check for raw device */
1874 	if ((pathname = avd_get_str(fileset->fs_path)) == NULL)
1875 		return (FILEBENCH_OK);
1876 
1877 	if ((setname = avd_get_str(fileset->fs_name)) == NULL)
1878 		return (FILEBENCH_OK);
1879 
1880 	(void) fb_strlcpy(path, pathname, MAXPATHLEN);
1881 	(void) fb_strlcat(path, "/", MAXPATHLEN);
1882 	(void) fb_strlcat(path, setname, MAXPATHLEN);
1883 	if ((stat64(path, &sb) == 0) &&
1884 	    ((sb.st_mode & S_IFMT) == S_IFBLK) && sb.st_rdev) {
1885 		fileset->fs_attrs |= FILESET_IS_RAW_DEV;
1886 		if (!(fileset->fs_attrs & FILESET_IS_FILE)) {
1887 			filebench_log(LOG_ERROR,
1888 			    "WARNING Fileset %s/%s Cannot be RAW device",
1889 			    avd_get_str(fileset->fs_path),
1890 			    avd_get_str(fileset->fs_name));
1891 			filebench_shutdown(1);
1892 		}
1893 
1894 		return (1);
1895 	}
1896 #endif /* HAVE_RAW_SUPPORT */
1897 
1898 	return (FILEBENCH_OK);
1899 }
1900