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