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