1*5184Sek110237 /*
2*5184Sek110237  * CDDL HEADER START
3*5184Sek110237  *
4*5184Sek110237  * The contents of this file are subject to the terms of the
5*5184Sek110237  * Common Development and Distribution License (the "License").
6*5184Sek110237  * You may not use this file except in compliance with the License.
7*5184Sek110237  *
8*5184Sek110237  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*5184Sek110237  * or http://www.opensolaris.org/os/licensing.
10*5184Sek110237  * See the License for the specific language governing permissions
11*5184Sek110237  * and limitations under the License.
12*5184Sek110237  *
13*5184Sek110237  * When distributing Covered Code, include this CDDL HEADER in each
14*5184Sek110237  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*5184Sek110237  * If applicable, add the following below this CDDL HEADER, with the
16*5184Sek110237  * fields enclosed by brackets "[]" replaced with your own identifying
17*5184Sek110237  * information: Portions Copyright [yyyy] [name of copyright owner]
18*5184Sek110237  *
19*5184Sek110237  * CDDL HEADER END
20*5184Sek110237  */
21*5184Sek110237 /*
22*5184Sek110237  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23*5184Sek110237  * Use is subject to license terms.
24*5184Sek110237  */
25*5184Sek110237 
26*5184Sek110237 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27*5184Sek110237 
28*5184Sek110237 
29*5184Sek110237 #include <fcntl.h>
30*5184Sek110237 #include <pthread.h>
31*5184Sek110237 #include <errno.h>
32*5184Sek110237 #include <math.h>
33*5184Sek110237 #include <libgen.h>
34*5184Sek110237 #include <sys/mman.h>
35*5184Sek110237 #include "fileset.h"
36*5184Sek110237 #include "filebench.h"
37*5184Sek110237 #include "gamma_dist.h"
38*5184Sek110237 
39*5184Sek110237 /*
40*5184Sek110237  * File sets, of type fileset_t, are entities which contain
41*5184Sek110237  * information about collections of files and subdirectories in Filebench.
42*5184Sek110237  * The fileset, once populated, consists of a tree of fileset entries of
43*5184Sek110237  * type filesetentry_t which specify files and directories.  The fileset
44*5184Sek110237  * is rooted in a directory specified by fs_path, and once the populated
45*5184Sek110237  * fileset has been created, has a tree of directories and files
46*5184Sek110237  * corresponding to the fileset's filesetentry tree.
47*5184Sek110237  */
48*5184Sek110237 
49*5184Sek110237 /*
50*5184Sek110237  * Removes the last file or directory name from a pathname.
51*5184Sek110237  * Basically removes characters from the end of the path by
52*5184Sek110237  * setting them to \0 until a forward slash '/' is
53*5184Sek110237  * encountered. It also removes the forward slash.
54*5184Sek110237  */
55*5184Sek110237 static char *
56*5184Sek110237 trunc_dirname(char *dir)
57*5184Sek110237 {
58*5184Sek110237 	char *s = dir + strlen(dir);
59*5184Sek110237 
60*5184Sek110237 	while (s != dir) {
61*5184Sek110237 		int c = *s;
62*5184Sek110237 
63*5184Sek110237 		*s = 0;
64*5184Sek110237 		if (c == '/')
65*5184Sek110237 			break;
66*5184Sek110237 		s--;
67*5184Sek110237 	}
68*5184Sek110237 	return (dir);
69*5184Sek110237 }
70*5184Sek110237 
71*5184Sek110237 /*
72*5184Sek110237  * Prints a list of allowed options and how to specify them.
73*5184Sek110237  */
74*5184Sek110237 void
75*5184Sek110237 fileset_usage(void)
76*5184Sek110237 {
77*5184Sek110237 	(void) fprintf(stderr, "define fileset name=<name>,path=<pathname>,"
78*5184Sek110237 	    "entries=<number>\n");
79*5184Sek110237 	(void) fprintf(stderr, "		        [,dirwidth=[width]\n");
80*5184Sek110237 	(void) fprintf(stderr, "		        [,dirgamma=[100-10000] "
81*5184Sek110237 	    "(Gamma * 1000)\n");
82*5184Sek110237 	(void) fprintf(stderr,
83*5184Sek110237 	    "		        [,sizegamma=[100-10000] (Gamma * 1000)\n");
84*5184Sek110237 	(void) fprintf(stderr,
85*5184Sek110237 	    "		        [,prealloc=[percent]]\n");
86*5184Sek110237 	(void) fprintf(stderr, "		        [,reuse]\n");
87*5184Sek110237 	(void) fprintf(stderr, "\n");
88*5184Sek110237 }
89*5184Sek110237 
90*5184Sek110237 /*
91*5184Sek110237  * Frees up memory mapped file region of supplied size. The
92*5184Sek110237  * file descriptor "fd" indicates which memory mapped file.
93*5184Sek110237  * If successful, returns 0. Otherwise returns -1 if "size"
94*5184Sek110237  * is zero, or -1 times the number of times msync() failed.
95*5184Sek110237  */
96*5184Sek110237 static int
97*5184Sek110237 fileset_freemem(int fd, off64_t size)
98*5184Sek110237 {
99*5184Sek110237 	off64_t left;
100*5184Sek110237 	int ret = 0;
101*5184Sek110237 
102*5184Sek110237 	for (left = size; left > 0; left -= MMAP_SIZE) {
103*5184Sek110237 		off64_t thismapsize;
104*5184Sek110237 		caddr_t addr;
105*5184Sek110237 
106*5184Sek110237 		thismapsize = MIN(MMAP_SIZE, left);
107*5184Sek110237 		addr = mmap64(0, thismapsize, PROT_READ|PROT_WRITE,
108*5184Sek110237 		    MAP_SHARED, fd, size - left);
109*5184Sek110237 		ret += msync(addr, thismapsize, MS_INVALIDATE);
110*5184Sek110237 		(void) munmap(addr, thismapsize);
111*5184Sek110237 	}
112*5184Sek110237 	return (ret);
113*5184Sek110237 }
114*5184Sek110237 
115*5184Sek110237 /*
116*5184Sek110237  * Creates a path string from the filesetentry_t "*entry"
117*5184Sek110237  * and all of its parent's path names. The resulting path
118*5184Sek110237  * is a concatination of all the individual parent paths.
119*5184Sek110237  * Allocates memory for the path string and returns a
120*5184Sek110237  * pointer to it.
121*5184Sek110237  */
122*5184Sek110237 char *
123*5184Sek110237 fileset_resolvepath(filesetentry_t *entry)
124*5184Sek110237 {
125*5184Sek110237 	filesetentry_t *fsep = entry;
126*5184Sek110237 	char path[MAXPATHLEN];
127*5184Sek110237 	char pathtmp[MAXPATHLEN];
128*5184Sek110237 	char *s;
129*5184Sek110237 
130*5184Sek110237 	*path = 0;
131*5184Sek110237 	while (fsep->fse_parent) {
132*5184Sek110237 		(void) strcpy(pathtmp, "/");
133*5184Sek110237 		(void) strcat(pathtmp, fsep->fse_path);
134*5184Sek110237 		(void) strcat(pathtmp, path);
135*5184Sek110237 		(void) strcpy(path, pathtmp);
136*5184Sek110237 		fsep = fsep->fse_parent;
137*5184Sek110237 	}
138*5184Sek110237 
139*5184Sek110237 	s = malloc(strlen(path) + 1);
140*5184Sek110237 	(void) strcpy(s, path);
141*5184Sek110237 	return (s);
142*5184Sek110237 }
143*5184Sek110237 
144*5184Sek110237 /*
145*5184Sek110237  * Creates multiple nested directories as required by the
146*5184Sek110237  * supplied path. Starts at the end of the path, creating
147*5184Sek110237  * a list of directories to mkdir, up to the root of the
148*5184Sek110237  * path, then mkdirs them one at a time from the root on down.
149*5184Sek110237  */
150*5184Sek110237 static int
151*5184Sek110237 fileset_mkdir(char *path, int mode)
152*5184Sek110237 {
153*5184Sek110237 	char *p;
154*5184Sek110237 	char *dirs[65536];
155*5184Sek110237 	int i = 0;
156*5184Sek110237 
157*5184Sek110237 	if ((p = strdup(path)) == NULL)
158*5184Sek110237 		goto null_str;
159*5184Sek110237 
160*5184Sek110237 	/*
161*5184Sek110237 	 * Fill an array of subdirectory path names until either we
162*5184Sek110237 	 * reach the root or encounter an already existing subdirectory
163*5184Sek110237 	 */
164*5184Sek110237 	/* CONSTCOND */
165*5184Sek110237 	while (1) {
166*5184Sek110237 		struct stat64 sb;
167*5184Sek110237 
168*5184Sek110237 		if (stat64(p, &sb) == 0)
169*5184Sek110237 			break;
170*5184Sek110237 		if (strlen(p) < 3)
171*5184Sek110237 			break;
172*5184Sek110237 		if ((dirs[i] = strdup(p)) == NULL) {
173*5184Sek110237 			free(p);
174*5184Sek110237 			goto null_str;
175*5184Sek110237 		}
176*5184Sek110237 
177*5184Sek110237 		(void) trunc_dirname(p);
178*5184Sek110237 		i++;
179*5184Sek110237 	}
180*5184Sek110237 
181*5184Sek110237 	/* Make the directories, from closest to root downwards. */
182*5184Sek110237 	for (--i; i >= 0; i--) {
183*5184Sek110237 		(void) mkdir(dirs[i], mode);
184*5184Sek110237 		free(dirs[i]);
185*5184Sek110237 	}
186*5184Sek110237 
187*5184Sek110237 	free(p);
188*5184Sek110237 	return (0);
189*5184Sek110237 
190*5184Sek110237 null_str:
191*5184Sek110237 	/* clean up */
192*5184Sek110237 	for (--i; i >= 0; i--)
193*5184Sek110237 		free(dirs[i]);
194*5184Sek110237 
195*5184Sek110237 	filebench_log(LOG_ERROR,
196*5184Sek110237 	    "Failed to create directory path %s: Out of memory", path);
197*5184Sek110237 
198*5184Sek110237 	return (-1);
199*5184Sek110237 }
200*5184Sek110237 
201*5184Sek110237 
202*5184Sek110237 /*
203*5184Sek110237  * First creates the parent directories of the file using
204*5184Sek110237  * fileset_mkdir(). Then Optionally sets the O_DSYNC flag
205*5184Sek110237  * and opens the file with open64(). It unlocks the fileset
206*5184Sek110237  * entry lock, sets the DIRECTIO_ON or DIRECTIO_OFF flags
207*5184Sek110237  * as requested, and returns the file descriptor integer
208*5184Sek110237  * for the opened file.
209*5184Sek110237  */
210*5184Sek110237 int
211*5184Sek110237 fileset_openfile(fileset_t *fileset,
212*5184Sek110237     filesetentry_t *entry, int flag, int mode, int attrs)
213*5184Sek110237 {
214*5184Sek110237 	char path[MAXPATHLEN];
215*5184Sek110237 	char dir[MAXPATHLEN];
216*5184Sek110237 	char *pathtmp;
217*5184Sek110237 	struct stat64 sb;
218*5184Sek110237 	int fd;
219*5184Sek110237 	int open_attrs = 0;
220*5184Sek110237 
221*5184Sek110237 	*path = 0;
222*5184Sek110237 	(void) strcpy(path, *fileset->fs_path);
223*5184Sek110237 	(void) strcat(path, "/");
224*5184Sek110237 	(void) strcat(path, fileset->fs_name);
225*5184Sek110237 	pathtmp = fileset_resolvepath(entry);
226*5184Sek110237 	(void) strcat(path, pathtmp);
227*5184Sek110237 	(void) strcpy(dir, path);
228*5184Sek110237 	free(pathtmp);
229*5184Sek110237 	(void) trunc_dirname(dir);
230*5184Sek110237 
231*5184Sek110237 	/* If we are going to create a file, create the parent dirs */
232*5184Sek110237 	if ((flag & O_CREAT) && (stat64(dir, &sb) != 0)) {
233*5184Sek110237 		if (fileset_mkdir(dir, 0755) == -1)
234*5184Sek110237 			return (-1);
235*5184Sek110237 	}
236*5184Sek110237 
237*5184Sek110237 	if (flag & O_CREAT)
238*5184Sek110237 		entry->fse_flags |= FSE_EXISTS;
239*5184Sek110237 
240*5184Sek110237 	if (attrs & FLOW_ATTR_DSYNC) {
241*5184Sek110237 #ifdef sun
242*5184Sek110237 		open_attrs |= O_DSYNC;
243*5184Sek110237 #else
244*5184Sek110237 		open_attrs |= O_FSYNC;
245*5184Sek110237 #endif
246*5184Sek110237 	}
247*5184Sek110237 
248*5184Sek110237 	if ((fd = open64(path, flag | open_attrs, mode)) < 0) {
249*5184Sek110237 		filebench_log(LOG_ERROR,
250*5184Sek110237 		    "Failed to open file %s: %s",
251*5184Sek110237 		    path, strerror(errno));
252*5184Sek110237 		(void) ipc_mutex_unlock(&entry->fse_lock);
253*5184Sek110237 		return (-1);
254*5184Sek110237 	}
255*5184Sek110237 	(void) ipc_mutex_unlock(&entry->fse_lock);
256*5184Sek110237 
257*5184Sek110237 #ifdef sun
258*5184Sek110237 	if (attrs & FLOW_ATTR_DIRECTIO)
259*5184Sek110237 		(void) directio(fd, DIRECTIO_ON);
260*5184Sek110237 	else
261*5184Sek110237 		(void) directio(fd, DIRECTIO_OFF);
262*5184Sek110237 #endif
263*5184Sek110237 
264*5184Sek110237 	return (fd);
265*5184Sek110237 }
266*5184Sek110237 
267*5184Sek110237 
268*5184Sek110237 /*
269*5184Sek110237  * Selects a fileset entry from a fileset. If the
270*5184Sek110237  * FILESET_PICKDIR flag is set it will pick a directory
271*5184Sek110237  * entry, otherwise a file entry. The FILESET_PICKRESET
272*5184Sek110237  * flag will cause it to reset the free list to the
273*5184Sek110237  * overall list (file or directory). The FILESET_PICKUNIQUE
274*5184Sek110237  * flag will take an entry off of one of the free (unused)
275*5184Sek110237  * lists (file or directory), otherwise the entry will be
276*5184Sek110237  * picked off of one of the rotor lists (file or directory).
277*5184Sek110237  * The FILESET_PICKEXISTS will insure that only extant
278*5184Sek110237  * (FSE_EXISTS) state files are selected, while
279*5184Sek110237  * FILESET_PICKNOEXIST insures that only non extant
280*5184Sek110237  * (not FSE_EXISTS) state files are selected.
281*5184Sek110237  */
282*5184Sek110237 filesetentry_t *
283*5184Sek110237 fileset_pick(fileset_t *fileset, int flags, int tid)
284*5184Sek110237 {
285*5184Sek110237 	filesetentry_t *entry = NULL;
286*5184Sek110237 	filesetentry_t *first = NULL;
287*5184Sek110237 
288*5184Sek110237 	(void) ipc_mutex_lock(&filebench_shm->fileset_lock);
289*5184Sek110237 
290*5184Sek110237 	while (entry == NULL) {
291*5184Sek110237 
292*5184Sek110237 		if ((flags & FILESET_PICKDIR) && (flags & FILESET_PICKRESET)) {
293*5184Sek110237 			entry = fileset->fs_dirlist;
294*5184Sek110237 			while (entry) {
295*5184Sek110237 				entry->fse_flags |= FSE_FREE;
296*5184Sek110237 				entry = entry->fse_dirnext;
297*5184Sek110237 			}
298*5184Sek110237 			fileset->fs_dirfree = fileset->fs_dirlist;
299*5184Sek110237 		}
300*5184Sek110237 
301*5184Sek110237 		if (!(flags & FILESET_PICKDIR) && (flags & FILESET_PICKRESET)) {
302*5184Sek110237 			entry = fileset->fs_filelist;
303*5184Sek110237 			while (entry) {
304*5184Sek110237 				entry->fse_flags |= FSE_FREE;
305*5184Sek110237 				entry = entry->fse_filenext;
306*5184Sek110237 			}
307*5184Sek110237 			fileset->fs_filefree = fileset->fs_filelist;
308*5184Sek110237 		}
309*5184Sek110237 
310*5184Sek110237 		if (flags & FILESET_PICKUNIQUE) {
311*5184Sek110237 			if (flags & FILESET_PICKDIR) {
312*5184Sek110237 				entry = fileset->fs_dirfree;
313*5184Sek110237 				if (entry == NULL)
314*5184Sek110237 					goto empty;
315*5184Sek110237 				fileset->fs_dirfree = entry->fse_dirnext;
316*5184Sek110237 			} else {
317*5184Sek110237 				entry = fileset->fs_filefree;
318*5184Sek110237 				if (entry == NULL)
319*5184Sek110237 					goto empty;
320*5184Sek110237 				fileset->fs_filefree = entry->fse_filenext;
321*5184Sek110237 			}
322*5184Sek110237 			entry->fse_flags &= ~FSE_FREE;
323*5184Sek110237 		} else {
324*5184Sek110237 			if (flags & FILESET_PICKDIR) {
325*5184Sek110237 				entry = fileset->fs_dirrotor;
326*5184Sek110237 				if (entry == NULL)
327*5184Sek110237 				fileset->fs_dirrotor =
328*5184Sek110237 				    entry = fileset->fs_dirlist;
329*5184Sek110237 				fileset->fs_dirrotor = entry->fse_dirnext;
330*5184Sek110237 			} else {
331*5184Sek110237 				entry = fileset->fs_filerotor[tid];
332*5184Sek110237 				if (entry == NULL)
333*5184Sek110237 					fileset->fs_filerotor[tid] =
334*5184Sek110237 					    entry = fileset->fs_filelist;
335*5184Sek110237 				fileset->fs_filerotor[tid] =
336*5184Sek110237 				    entry->fse_filenext;
337*5184Sek110237 			}
338*5184Sek110237 		}
339*5184Sek110237 
340*5184Sek110237 		if (first == entry)
341*5184Sek110237 			goto empty;
342*5184Sek110237 
343*5184Sek110237 		if (first == NULL)
344*5184Sek110237 			first = entry;
345*5184Sek110237 
346*5184Sek110237 		/* Return locked entry */
347*5184Sek110237 		(void) ipc_mutex_lock(&entry->fse_lock);
348*5184Sek110237 
349*5184Sek110237 		/* If we ask for an existing file, go round again */
350*5184Sek110237 		if ((flags & FILESET_PICKEXISTS) &&
351*5184Sek110237 		    !(entry->fse_flags & FSE_EXISTS)) {
352*5184Sek110237 			(void) ipc_mutex_unlock(&entry->fse_lock);
353*5184Sek110237 			entry = NULL;
354*5184Sek110237 		}
355*5184Sek110237 
356*5184Sek110237 		/* If we ask for not an existing file, go round again */
357*5184Sek110237 		if ((flags & FILESET_PICKNOEXIST) &&
358*5184Sek110237 		    (entry->fse_flags & FSE_EXISTS)) {
359*5184Sek110237 			(void) ipc_mutex_unlock(&entry->fse_lock);
360*5184Sek110237 			entry = NULL;
361*5184Sek110237 		}
362*5184Sek110237 	}
363*5184Sek110237 
364*5184Sek110237 	(void) ipc_mutex_unlock(&filebench_shm->fileset_lock);
365*5184Sek110237 	filebench_log(LOG_DEBUG_SCRIPT, "Picked file %s", entry->fse_path);
366*5184Sek110237 	return (entry);
367*5184Sek110237 
368*5184Sek110237 empty:
369*5184Sek110237 	(void) ipc_mutex_unlock(&filebench_shm->fileset_lock);
370*5184Sek110237 	return (NULL);
371*5184Sek110237 }
372*5184Sek110237 
373*5184Sek110237 /*
374*5184Sek110237  * Given a fileset "fileset", create the associated files as
375*5184Sek110237  * specified in the attributes of the fileset. The fileset is
376*5184Sek110237  * rooted in a directory whose pathname is in fs_path. If the
377*5184Sek110237  * directory exists, meaning that there is already a fileset,
378*5184Sek110237  * and the fs_reuse attribute is false, then remove it and all
379*5184Sek110237  * its contained files and subdirectories. Next, the routine
380*5184Sek110237  * creates a root directory for the fileset. All the file type
381*5184Sek110237  * filesetentries are cycled through creating as needed
382*5184Sek110237  * their containing subdirectory trees in the filesystem and
383*5184Sek110237  * creating actual files for fs_preallocpercent of them. The
384*5184Sek110237  * created files are filled with fse_size bytes of unitialized
385*5184Sek110237  * data. The routine returns -1 on errors, 0 on success.
386*5184Sek110237  */
387*5184Sek110237 static int
388*5184Sek110237 fileset_create(fileset_t *fileset)
389*5184Sek110237 {
390*5184Sek110237 	filesetentry_t *entry;
391*5184Sek110237 	char path[MAXPATHLEN];
392*5184Sek110237 	char *buf;
393*5184Sek110237 	struct stat64 sb;
394*5184Sek110237 	int pickflags = FILESET_PICKUNIQUE | FILESET_PICKRESET;
395*5184Sek110237 	hrtime_t start = gethrtime();
396*5184Sek110237 	int preallocated = 0;
397*5184Sek110237 	int reusing = 0;
398*5184Sek110237 
399*5184Sek110237 	if ((buf = (char *)malloc(FILE_ALLOC_BLOCK)) == NULL)
400*5184Sek110237 		return (-1);
401*5184Sek110237 
402*5184Sek110237 	if (*fileset->fs_path == NULL) {
403*5184Sek110237 		filebench_log(LOG_ERROR, "Fileset path not set");
404*5184Sek110237 		return (-1);
405*5184Sek110237 	}
406*5184Sek110237 
407*5184Sek110237 	/* XXX Add check to see if there is enough space */
408*5184Sek110237 
409*5184Sek110237 	/* Remove existing */
410*5184Sek110237 	(void) strcpy(path, *fileset->fs_path);
411*5184Sek110237 	(void) strcat(path, "/");
412*5184Sek110237 	(void) strcat(path, fileset->fs_name);
413*5184Sek110237 	if ((stat64(path, &sb) == 0) && (strlen(path) > 3) &&
414*5184Sek110237 	    (strlen(*fileset->fs_path) > 2)) {
415*5184Sek110237 		if (!integer_isset(fileset->fs_reuse)) {
416*5184Sek110237 			char cmd[MAXPATHLEN];
417*5184Sek110237 
418*5184Sek110237 			(void) snprintf(cmd, sizeof (cmd), "rm -rf %s", path);
419*5184Sek110237 			(void) system(cmd);
420*5184Sek110237 			filebench_log(LOG_VERBOSE,
421*5184Sek110237 			    "Removed any existing fileset %s in %d seconds",
422*5184Sek110237 			    fileset->fs_name,
423*5184Sek110237 			    ((gethrtime() - start) / 1000000000) + 1);
424*5184Sek110237 		} else {
425*5184Sek110237 			/* we are re-using */
426*5184Sek110237 			reusing = 1;
427*5184Sek110237 			filebench_log(LOG_VERBOSE,
428*5184Sek110237 			    "Re-using fileset %s on %s file system.",
429*5184Sek110237 			    fileset->fs_name, sb.st_fstype);
430*5184Sek110237 		}
431*5184Sek110237 	}
432*5184Sek110237 	(void) mkdir(path, 0755);
433*5184Sek110237 
434*5184Sek110237 	start = gethrtime();
435*5184Sek110237 
436*5184Sek110237 	filebench_log(LOG_VERBOSE, "Creating fileset %s...",
437*5184Sek110237 	    fileset->fs_name);
438*5184Sek110237 
439*5184Sek110237 	while (entry = fileset_pick(fileset, pickflags, 0)) {
440*5184Sek110237 		char dir[MAXPATHLEN];
441*5184Sek110237 		char *pathtmp;
442*5184Sek110237 		off64_t seek;
443*5184Sek110237 		int fd;
444*5184Sek110237 		int randno;
445*5184Sek110237 
446*5184Sek110237 		pickflags = FILESET_PICKUNIQUE;
447*5184Sek110237 
448*5184Sek110237 		entry->fse_flags &= ~FSE_EXISTS;
449*5184Sek110237 
450*5184Sek110237 		if (!integer_isset(fileset->fs_prealloc)) {
451*5184Sek110237 			(void) ipc_mutex_unlock(&entry->fse_lock);
452*5184Sek110237 			continue;
453*5184Sek110237 		}
454*5184Sek110237 
455*5184Sek110237 		*path = 0;
456*5184Sek110237 		(void) strcpy(path, *fileset->fs_path);
457*5184Sek110237 		(void) strcat(path, "/");
458*5184Sek110237 		(void) strcat(path, fileset->fs_name);
459*5184Sek110237 		pathtmp = fileset_resolvepath(entry);
460*5184Sek110237 		(void) strcat(path, pathtmp);
461*5184Sek110237 		(void) strcpy(dir, path);
462*5184Sek110237 		free(pathtmp);
463*5184Sek110237 
464*5184Sek110237 		(void) trunc_dirname(dir);
465*5184Sek110237 
466*5184Sek110237 		if (stat64(dir, &sb) != 0) {
467*5184Sek110237 			if (fileset_mkdir(dir, 0775) == -1) {
468*5184Sek110237 				(void) ipc_mutex_unlock(&entry->fse_lock);
469*5184Sek110237 				return (-1);
470*5184Sek110237 			}
471*5184Sek110237 		}
472*5184Sek110237 
473*5184Sek110237 		randno = ((RAND_MAX * (100 - *(fileset->fs_preallocpercent)))
474*5184Sek110237 		    / 100);
475*5184Sek110237 
476*5184Sek110237 		if (rand() < randno) {
477*5184Sek110237 			(void) ipc_mutex_unlock(&entry->fse_lock);
478*5184Sek110237 			continue;
479*5184Sek110237 		}
480*5184Sek110237 
481*5184Sek110237 		preallocated++;
482*5184Sek110237 
483*5184Sek110237 		filebench_log(LOG_DEBUG_IMPL, "Populated %s", entry->fse_path);
484*5184Sek110237 
485*5184Sek110237 		/* see if reusing and this file exists */
486*5184Sek110237 		if (reusing && (stat64(path, &sb) == 0)) {
487*5184Sek110237 			if ((fd = open64(path, O_RDWR)) < 0) {
488*5184Sek110237 				filebench_log(LOG_INFO,
489*5184Sek110237 				    "Attempted but failed to Re-use file %s",
490*5184Sek110237 				    path);
491*5184Sek110237 				(void) ipc_mutex_unlock(&entry->fse_lock);
492*5184Sek110237 				return (-1);
493*5184Sek110237 			}
494*5184Sek110237 
495*5184Sek110237 			if (sb.st_size == (off64_t)entry->fse_size) {
496*5184Sek110237 				filebench_log(LOG_INFO,
497*5184Sek110237 				    "Re-using file %s", path);
498*5184Sek110237 
499*5184Sek110237 				if (!integer_isset(fileset->fs_cached))
500*5184Sek110237 					(void) fileset_freemem(fd,
501*5184Sek110237 					    entry->fse_size);
502*5184Sek110237 
503*5184Sek110237 				entry->fse_flags |= FSE_EXISTS;
504*5184Sek110237 				(void) close(fd);
505*5184Sek110237 				(void) ipc_mutex_unlock(&entry->fse_lock);
506*5184Sek110237 				continue;
507*5184Sek110237 
508*5184Sek110237 			} else if (sb.st_size > (off64_t)entry->fse_size) {
509*5184Sek110237 				/* reuse, but too large */
510*5184Sek110237 				filebench_log(LOG_INFO,
511*5184Sek110237 				    "Truncating & re-using file %s", path);
512*5184Sek110237 
513*5184Sek110237 				(void) ftruncate64(fd,
514*5184Sek110237 				    (off64_t)entry->fse_size);
515*5184Sek110237 
516*5184Sek110237 				if (!integer_isset(fileset->fs_cached))
517*5184Sek110237 					(void) fileset_freemem(fd,
518*5184Sek110237 					    entry->fse_size);
519*5184Sek110237 
520*5184Sek110237 				entry->fse_flags |= FSE_EXISTS;
521*5184Sek110237 				(void) close(fd);
522*5184Sek110237 				(void) ipc_mutex_unlock(&entry->fse_lock);
523*5184Sek110237 				continue;
524*5184Sek110237 			}
525*5184Sek110237 		} else {
526*5184Sek110237 
527*5184Sek110237 			/* No file or not reusing, so create */
528*5184Sek110237 			if ((fd = open64(path, O_RDWR | O_CREAT, 0644)) < 0) {
529*5184Sek110237 				filebench_log(LOG_ERROR,
530*5184Sek110237 				    "Failed to pre-allocate file %s: %s",
531*5184Sek110237 				    path, strerror(errno));
532*5184Sek110237 
533*5184Sek110237 				return (-1);
534*5184Sek110237 			}
535*5184Sek110237 		}
536*5184Sek110237 
537*5184Sek110237 		entry->fse_flags |= FSE_EXISTS;
538*5184Sek110237 
539*5184Sek110237 		for (seek = 0; seek < entry->fse_size; ) {
540*5184Sek110237 			off64_t wsize;
541*5184Sek110237 			int ret = 0;
542*5184Sek110237 
543*5184Sek110237 			/*
544*5184Sek110237 			 * Write FILE_ALLOC_BLOCK's worth,
545*5184Sek110237 			 * except on last write
546*5184Sek110237 			 */
547*5184Sek110237 			wsize = MIN(entry->fse_size - seek, FILE_ALLOC_BLOCK);
548*5184Sek110237 
549*5184Sek110237 			ret = write(fd, buf, wsize);
550*5184Sek110237 			if (ret != wsize) {
551*5184Sek110237 				filebench_log(LOG_ERROR,
552*5184Sek110237 				    "Failed to pre-allocate file %s: %s",
553*5184Sek110237 				    path, strerror(errno));
554*5184Sek110237 				(void) close(fd);
555*5184Sek110237 				return (-1);
556*5184Sek110237 			}
557*5184Sek110237 			seek += wsize;
558*5184Sek110237 		}
559*5184Sek110237 
560*5184Sek110237 		if (!integer_isset(fileset->fs_cached))
561*5184Sek110237 			(void) fileset_freemem(fd, entry->fse_size);
562*5184Sek110237 
563*5184Sek110237 		(void) close(fd);
564*5184Sek110237 		(void) ipc_mutex_unlock(&entry->fse_lock);
565*5184Sek110237 
566*5184Sek110237 		filebench_log(LOG_DEBUG_IMPL,
567*5184Sek110237 		    "Pre-allocated file %s size %lld", path, entry->fse_size);
568*5184Sek110237 	}
569*5184Sek110237 	filebench_log(LOG_VERBOSE,
570*5184Sek110237 	    "Preallocated %d of %lld of fileset %s in %d seconds",
571*5184Sek110237 	    preallocated,
572*5184Sek110237 	    *(fileset->fs_entries),
573*5184Sek110237 	    fileset->fs_name,
574*5184Sek110237 	    ((gethrtime() - start) / 1000000000) + 1);
575*5184Sek110237 
576*5184Sek110237 	free(buf);
577*5184Sek110237 	return (0);
578*5184Sek110237 }
579*5184Sek110237 
580*5184Sek110237 /*
581*5184Sek110237  * Adds an entry to the fileset's file list. Single threaded so
582*5184Sek110237  * no locking needed.
583*5184Sek110237  */
584*5184Sek110237 static void
585*5184Sek110237 fileset_insfilelist(fileset_t *fileset, filesetentry_t *entry)
586*5184Sek110237 {
587*5184Sek110237 	if (fileset->fs_filelist == NULL) {
588*5184Sek110237 		fileset->fs_filelist = entry;
589*5184Sek110237 		entry->fse_filenext = NULL;
590*5184Sek110237 	} else {
591*5184Sek110237 		entry->fse_filenext = fileset->fs_filelist;
592*5184Sek110237 		fileset->fs_filelist = entry;
593*5184Sek110237 	}
594*5184Sek110237 }
595*5184Sek110237 
596*5184Sek110237 /*
597*5184Sek110237  * Adds an entry to the fileset's directory list. Single
598*5184Sek110237  * threaded so no locking needed.
599*5184Sek110237  */
600*5184Sek110237 static void
601*5184Sek110237 fileset_insdirlist(fileset_t *fileset, filesetentry_t *entry)
602*5184Sek110237 {
603*5184Sek110237 	if (fileset->fs_dirlist == NULL) {
604*5184Sek110237 		fileset->fs_dirlist = entry;
605*5184Sek110237 		entry->fse_dirnext = NULL;
606*5184Sek110237 	} else {
607*5184Sek110237 		entry->fse_dirnext = fileset->fs_dirlist;
608*5184Sek110237 		fileset->fs_dirlist = entry;
609*5184Sek110237 	}
610*5184Sek110237 }
611*5184Sek110237 
612*5184Sek110237 /*
613*5184Sek110237  * Obtaines a filesetentry entity for a file to be placed in a
614*5184Sek110237  * (sub)directory of a fileset. The size of the file may be
615*5184Sek110237  * specified by fs_meansize, or calculated from a gamma
616*5184Sek110237  * distribution of parameter fs_sizegamma and of mean size
617*5184Sek110237  * fs_meansize. The filesetentry entity is placed on the file
618*5184Sek110237  * list in the specified parent filesetentry entity, which may
619*5184Sek110237  * be a directory filesetentry, or the root filesetentry in the
620*5184Sek110237  * fileset. It is also placed on the fileset's list of all
621*5184Sek110237  * contained files. Returns 0 if successful or -1 if ipc memory
622*5184Sek110237  * for the path string cannot be allocated.
623*5184Sek110237  */
624*5184Sek110237 static int
625*5184Sek110237 fileset_populate_file(fileset_t *fileset, filesetentry_t *parent, int serial)
626*5184Sek110237 {
627*5184Sek110237 	char tmpname[16];
628*5184Sek110237 	filesetentry_t *entry;
629*5184Sek110237 	double drand;
630*5184Sek110237 	double gamma;
631*5184Sek110237 
632*5184Sek110237 	if ((entry = (filesetentry_t *)ipc_malloc(FILEBENCH_FILESETENTRY))
633*5184Sek110237 	    == NULL) {
634*5184Sek110237 		filebench_log(LOG_ERROR,
635*5184Sek110237 		    "fileset_populate_file: Can't malloc filesetentry");
636*5184Sek110237 		return (-1);
637*5184Sek110237 	}
638*5184Sek110237 
639*5184Sek110237 	(void) pthread_mutex_init(&entry->fse_lock, ipc_mutexattr());
640*5184Sek110237 	entry->fse_parent = parent;
641*5184Sek110237 	entry->fse_fileset = fileset;
642*5184Sek110237 	entry->fse_flags |= FSE_FREE;
643*5184Sek110237 	fileset_insfilelist(fileset, entry);
644*5184Sek110237 
645*5184Sek110237 	(void) snprintf(tmpname, sizeof (tmpname), "%08d", serial);
646*5184Sek110237 	if ((entry->fse_path = (char *)ipc_pathalloc(tmpname)) == NULL) {
647*5184Sek110237 		filebench_log(LOG_ERROR,
648*5184Sek110237 		    "fileset_populate_file: Can't alloc path string");
649*5184Sek110237 		return (-1);
650*5184Sek110237 	}
651*5184Sek110237 
652*5184Sek110237 	gamma = *(fileset->fs_sizegamma) / 1000.0;
653*5184Sek110237 
654*5184Sek110237 	if (gamma > 0) {
655*5184Sek110237 		drand = gamma_dist_knuth(gamma, fileset->fs_meansize / gamma);
656*5184Sek110237 		entry->fse_size = (off64_t)drand;
657*5184Sek110237 	} else {
658*5184Sek110237 		entry->fse_size = (off64_t)fileset->fs_meansize;
659*5184Sek110237 	}
660*5184Sek110237 
661*5184Sek110237 	fileset->fs_bytes += entry->fse_size;
662*5184Sek110237 
663*5184Sek110237 	fileset->fs_realfiles++;
664*5184Sek110237 	return (0);
665*5184Sek110237 }
666*5184Sek110237 
667*5184Sek110237 /*
668*5184Sek110237  * Creates a directory node in a fileset, by obtaining a
669*5184Sek110237  * filesetentry entity for the node and initializing it
670*5184Sek110237  * according to parameters of the fileset. It determines a
671*5184Sek110237  * directory tree depth and directory width, optionally using
672*5184Sek110237  * a gamma distribution. If its calculated depth is less then
673*5184Sek110237  * its actual depth in the directory tree, it becomes a leaf
674*5184Sek110237  * node and files itself with "width" number of file type
675*5184Sek110237  * filesetentries, otherwise it files itself with "width"
676*5184Sek110237  * number of directory type filesetentries, using recursive
677*5184Sek110237  * calls to fileset_populate_subdir. The end result of the
678*5184Sek110237  * initial call to this routine is a tree of directories of
679*5184Sek110237  * random width and varying depth with sufficient leaf
680*5184Sek110237  * directories to contain all required files.
681*5184Sek110237  * Returns 0 on success. Returns -1 if ipc path string memory
682*5184Sek110237  * cannot be allocated and returns an error code (currently
683*5184Sek110237  * also -1) from calls to fileset_populate_file or recursive
684*5184Sek110237  * calls to fileset_populate_subdir.
685*5184Sek110237  */
686*5184Sek110237 static int
687*5184Sek110237 fileset_populate_subdir(fileset_t *fileset, filesetentry_t *parent,
688*5184Sek110237     int serial, double depth)
689*5184Sek110237 {
690*5184Sek110237 	double randepth, drand, ranwidth, gamma;
691*5184Sek110237 	int isleaf = 0;
692*5184Sek110237 	char tmpname[16];
693*5184Sek110237 	filesetentry_t *entry;
694*5184Sek110237 	int i;
695*5184Sek110237 
696*5184Sek110237 	depth += 1;
697*5184Sek110237 
698*5184Sek110237 	/* Create dir node */
699*5184Sek110237 	if ((entry = (filesetentry_t *)ipc_malloc(FILEBENCH_FILESETENTRY))
700*5184Sek110237 	    == NULL) {
701*5184Sek110237 		filebench_log(LOG_ERROR,
702*5184Sek110237 		    "fileset_populate_subdir: Can't malloc filesetentry");
703*5184Sek110237 		return (-1);
704*5184Sek110237 	}
705*5184Sek110237 
706*5184Sek110237 	(void) pthread_mutex_init(&entry->fse_lock, ipc_mutexattr());
707*5184Sek110237 
708*5184Sek110237 	(void) snprintf(tmpname, sizeof (tmpname), "%08d", serial);
709*5184Sek110237 	if ((entry->fse_path = (char *)ipc_pathalloc(tmpname)) == NULL) {
710*5184Sek110237 		filebench_log(LOG_ERROR,
711*5184Sek110237 		    "fileset_populate_subdir: Can't alloc path string");
712*5184Sek110237 		return (-1);
713*5184Sek110237 	}
714*5184Sek110237 
715*5184Sek110237 	entry->fse_parent = parent;
716*5184Sek110237 	entry->fse_flags |= FSE_DIR | FSE_FREE;
717*5184Sek110237 	fileset_insdirlist(fileset, entry);
718*5184Sek110237 
719*5184Sek110237 	gamma = *(fileset->fs_dirgamma) / 1000.0;
720*5184Sek110237 	if (gamma > 0) {
721*5184Sek110237 		drand = gamma_dist_knuth(gamma, fileset->fs_meandepth / gamma);
722*5184Sek110237 		randepth = (int)drand;
723*5184Sek110237 	} else {
724*5184Sek110237 		randepth = (int)fileset->fs_meandepth;
725*5184Sek110237 	}
726*5184Sek110237 
727*5184Sek110237 	gamma = *(fileset->fs_sizegamma) / 1000.0;
728*5184Sek110237 
729*5184Sek110237 	if (gamma > 0) {
730*5184Sek110237 		drand = gamma_dist_knuth(gamma, fileset->fs_meanwidth / gamma);
731*5184Sek110237 		ranwidth = drand;
732*5184Sek110237 	} else {
733*5184Sek110237 		ranwidth = fileset->fs_meanwidth;
734*5184Sek110237 	}
735*5184Sek110237 
736*5184Sek110237 	if (randepth == 0)
737*5184Sek110237 		randepth = 1;
738*5184Sek110237 	if (ranwidth == 0)
739*5184Sek110237 		ranwidth = 1;
740*5184Sek110237 	if (depth >= randepth)
741*5184Sek110237 		isleaf = 1;
742*5184Sek110237 
743*5184Sek110237 	/*
744*5184Sek110237 	 * Create directory of random width according to distribution, or
745*5184Sek110237 	 * if root directory, continue until #files required
746*5184Sek110237 	 */
747*5184Sek110237 	for (i = 1;
748*5184Sek110237 	    ((parent == NULL) || (i < ranwidth + 1)) &&
749*5184Sek110237 	    (fileset->fs_realfiles < *(fileset->fs_entries)); i++) {
750*5184Sek110237 		int ret = 0;
751*5184Sek110237 
752*5184Sek110237 		if (parent && isleaf)
753*5184Sek110237 			ret = fileset_populate_file(fileset, entry, i);
754*5184Sek110237 		else
755*5184Sek110237 			ret = fileset_populate_subdir(fileset, entry, i, depth);
756*5184Sek110237 
757*5184Sek110237 		if (ret != 0)
758*5184Sek110237 			return (ret);
759*5184Sek110237 	}
760*5184Sek110237 	return (0);
761*5184Sek110237 }
762*5184Sek110237 
763*5184Sek110237 /*
764*5184Sek110237  * Populates a fileset with files and subdirectory entries. Uses
765*5184Sek110237  * the supplied fs_dirwidth and fs_entries (number of files) to
766*5184Sek110237  * calculate the required fs_meandepth (of subdirectories) and
767*5184Sek110237  * initialize the fs_meanwidth and fs_meansize variables. Then
768*5184Sek110237  * calls fileset_populate_subdir() to do the recursive
769*5184Sek110237  * subdirectory entry creation and leaf file entry creation. All
770*5184Sek110237  * of the above is skipped if the fileset has already been
771*5184Sek110237  * populated. Returns 0 on success, or an error code from the
772*5184Sek110237  * call to fileset_populate_subdir if that call fails.
773*5184Sek110237  */
774*5184Sek110237 static int
775*5184Sek110237 fileset_populate(fileset_t *fileset)
776*5184Sek110237 {
777*5184Sek110237 	int nfiles;
778*5184Sek110237 	int meandirwidth = *(fileset->fs_dirwidth);
779*5184Sek110237 	int ret;
780*5184Sek110237 
781*5184Sek110237 	/* Skip if already populated */
782*5184Sek110237 	if (fileset->fs_bytes > 0)
783*5184Sek110237 		goto exists;
784*5184Sek110237 
785*5184Sek110237 	/*
786*5184Sek110237 	 * Input params are:
787*5184Sek110237 	 *	# of files
788*5184Sek110237 	 *	ave # of files per dir
789*5184Sek110237 	 *	max size of dir
790*5184Sek110237 	 *	# ave size of file
791*5184Sek110237 	 *	max size of file
792*5184Sek110237 	 */
793*5184Sek110237 	nfiles = *(fileset->fs_entries);
794*5184Sek110237 	fileset->fs_meandepth = log(nfiles) / log(meandirwidth);
795*5184Sek110237 	fileset->fs_meanwidth = meandirwidth;
796*5184Sek110237 	fileset->fs_meansize = *(fileset->fs_size);
797*5184Sek110237 
798*5184Sek110237 	if ((ret = fileset_populate_subdir(fileset, NULL, 1, 0)) != 0)
799*5184Sek110237 		return (ret);
800*5184Sek110237 
801*5184Sek110237 
802*5184Sek110237 exists:
803*5184Sek110237 	filebench_log(LOG_VERBOSE, "Fileset %s: %lld files, "
804*5184Sek110237 	    "avg dir = %.1lf, avg depth = %.1lf, mbytes=%lld",
805*5184Sek110237 	    fileset->fs_name,
806*5184Sek110237 	    *(fileset->fs_entries),
807*5184Sek110237 	    fileset->fs_meanwidth,
808*5184Sek110237 	    fileset->fs_meandepth,
809*5184Sek110237 	    fileset->fs_bytes / 1024UL / 1024UL);
810*5184Sek110237 
811*5184Sek110237 	return (0);
812*5184Sek110237 }
813*5184Sek110237 
814*5184Sek110237 /*
815*5184Sek110237  * Allocates a fileset instance, initializes fs_dirgamma and
816*5184Sek110237  * fs_sizegamma default values, and sets the fileset name to the
817*5184Sek110237  * supplied name string. Puts the allocated fileset on the
818*5184Sek110237  * master fileset list and returns a pointer to it.
819*5184Sek110237  */
820*5184Sek110237 fileset_t *
821*5184Sek110237 fileset_define(char *name)
822*5184Sek110237 {
823*5184Sek110237 	fileset_t *fileset;
824*5184Sek110237 
825*5184Sek110237 	if (name == NULL)
826*5184Sek110237 		return (NULL);
827*5184Sek110237 
828*5184Sek110237 	if ((fileset = (fileset_t *)ipc_malloc(FILEBENCH_FILESET)) == NULL) {
829*5184Sek110237 		filebench_log(LOG_ERROR,
830*5184Sek110237 		    "fileset_define: Can't malloc fileset");
831*5184Sek110237 		return (NULL);
832*5184Sek110237 	}
833*5184Sek110237 
834*5184Sek110237 	filebench_log(LOG_DEBUG_IMPL, "Defining file %s", name);
835*5184Sek110237 
836*5184Sek110237 	(void) ipc_mutex_lock(&filebench_shm->fileset_lock);
837*5184Sek110237 
838*5184Sek110237 	fileset->fs_dirgamma = integer_alloc(1500);
839*5184Sek110237 	fileset->fs_sizegamma = integer_alloc(1500);
840*5184Sek110237 
841*5184Sek110237 	/* Add fileset to global list */
842*5184Sek110237 	if (filebench_shm->filesetlist == NULL) {
843*5184Sek110237 		filebench_shm->filesetlist = fileset;
844*5184Sek110237 		fileset->fs_next = NULL;
845*5184Sek110237 	} else {
846*5184Sek110237 		fileset->fs_next = filebench_shm->filesetlist;
847*5184Sek110237 		filebench_shm->filesetlist = fileset;
848*5184Sek110237 	}
849*5184Sek110237 
850*5184Sek110237 	(void) ipc_mutex_unlock(&filebench_shm->fileset_lock);
851*5184Sek110237 
852*5184Sek110237 	(void) strcpy(fileset->fs_name, name);
853*5184Sek110237 
854*5184Sek110237 	return (fileset);
855*5184Sek110237 }
856*5184Sek110237 
857*5184Sek110237 /*
858*5184Sek110237  * If supplied with a pointer to a fileset and the fileset's
859*5184Sek110237  * fs_prealloc flag is set, calls fileset_populate() to populate
860*5184Sek110237  * the fileset with filesetentries, then calls fileset_create()
861*5184Sek110237  * to make actual directories and files for the filesetentries.
862*5184Sek110237  * Otherwise, it applies fileset_populate() and fileset_create()
863*5184Sek110237  * to all the filesets on the master fileset list. It always
864*5184Sek110237  * returns zero (0) if one fileset is populated / created,
865*5184Sek110237  * otherwise it returns the sum of returned values from
866*5184Sek110237  * fileset_create() and fileset_populate(), which
867*5184Sek110237  * will be a negative one (-1) times the number of
868*5184Sek110237  * fileset_create() calls which failed.
869*5184Sek110237  */
870*5184Sek110237 int
871*5184Sek110237 fileset_createset(fileset_t *fileset)
872*5184Sek110237 {
873*5184Sek110237 	fileset_t *list;
874*5184Sek110237 	int ret = 0;
875*5184Sek110237 
876*5184Sek110237 	if (fileset && integer_isset(fileset->fs_prealloc)) {
877*5184Sek110237 		if ((ret = fileset_populate(fileset)) != 0)
878*5184Sek110237 			return (ret);
879*5184Sek110237 		return (fileset_create(fileset));
880*5184Sek110237 	}
881*5184Sek110237 
882*5184Sek110237 	list = filebench_shm->filesetlist;
883*5184Sek110237 	while (list) {
884*5184Sek110237 		ret += fileset_populate(list);
885*5184Sek110237 		ret += fileset_create(list);
886*5184Sek110237 		list = list->fs_next;
887*5184Sek110237 	}
888*5184Sek110237 
889*5184Sek110237 	return (ret);
890*5184Sek110237 }
891*5184Sek110237 
892*5184Sek110237 /*
893*5184Sek110237  * Searches through the master fileset list for the named fileset.
894*5184Sek110237  * If found, returns pointer to same, otherwise returns NULL.
895*5184Sek110237  */
896*5184Sek110237 fileset_t *
897*5184Sek110237 fileset_find(char *name)
898*5184Sek110237 {
899*5184Sek110237 	fileset_t *fileset = filebench_shm->filesetlist;
900*5184Sek110237 
901*5184Sek110237 	(void) ipc_mutex_lock(&filebench_shm->fileset_lock);
902*5184Sek110237 
903*5184Sek110237 	while (fileset) {
904*5184Sek110237 		if (strcmp(name, fileset->fs_name) == 0) {
905*5184Sek110237 			(void) ipc_mutex_unlock(&filebench_shm->fileset_lock);
906*5184Sek110237 			return (fileset);
907*5184Sek110237 		}
908*5184Sek110237 		fileset = fileset->fs_next;
909*5184Sek110237 	}
910*5184Sek110237 	(void) ipc_mutex_unlock(&filebench_shm->fileset_lock);
911*5184Sek110237 
912*5184Sek110237 	return (NULL);
913*5184Sek110237 }
914