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