xref: /onnv-gate/usr/src/cmd/filebench/common/flowop_library.c (revision 9326:475779da8c08)
15184Sek110237 /*
25184Sek110237  * CDDL HEADER START
35184Sek110237  *
45184Sek110237  * The contents of this file are subject to the terms of the
55184Sek110237  * Common Development and Distribution License (the "License").
65184Sek110237  * You may not use this file except in compliance with the License.
75184Sek110237  *
85184Sek110237  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
95184Sek110237  * or http://www.opensolaris.org/os/licensing.
105184Sek110237  * See the License for the specific language governing permissions
115184Sek110237  * and limitations under the License.
125184Sek110237  *
135184Sek110237  * When distributing Covered Code, include this CDDL HEADER in each
145184Sek110237  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
155184Sek110237  * If applicable, add the following below this CDDL HEADER, with the
165184Sek110237  * fields enclosed by brackets "[]" replaced with your own identifying
175184Sek110237  * information: Portions Copyright [yyyy] [name of copyright owner]
185184Sek110237  *
195184Sek110237  * CDDL HEADER END
205184Sek110237  */
215184Sek110237 /*
228615SAndrew.W.Wilson@sun.com  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
235184Sek110237  * Use is subject to license terms.
246613Sek110237  *
256613Sek110237  * Portions Copyright 2008 Denis Cheng
265184Sek110237  */
275184Sek110237 
285184Sek110237 #include "config.h"
295184Sek110237 
305184Sek110237 #include <sys/types.h>
315184Sek110237 #ifdef HAVE_SYS_ASYNCH_H
325184Sek110237 #include <sys/asynch.h>
335184Sek110237 #endif
348615SAndrew.W.Wilson@sun.com #include <stddef.h>
355184Sek110237 #include <sys/ipc.h>
365184Sek110237 #include <sys/sem.h>
375184Sek110237 #include <sys/errno.h>
385184Sek110237 #include <sys/time.h>
395184Sek110237 #include <inttypes.h>
405184Sek110237 #include <fcntl.h>
416212Saw148015 #include <math.h>
427946SAndrew.W.Wilson@sun.com #include <dirent.h>
435184Sek110237 
445184Sek110237 #ifdef HAVE_UTILITY_H
455184Sek110237 #include <utility.h>
465184Sek110237 #endif /* HAVE_UTILITY_H */
475184Sek110237 
485184Sek110237 #ifdef HAVE_SYS_ASYNC_H
495184Sek110237 #include <sys/asynch.h>
505184Sek110237 #endif /* HAVE_SYS_ASYNC_H */
515184Sek110237 
525184Sek110237 #ifndef HAVE_UINT_T
535184Sek110237 #define	uint_t unsigned int
545184Sek110237 #endif /* HAVE_UINT_T */
555184Sek110237 
565184Sek110237 #ifndef HAVE_SYSV_SEM
575184Sek110237 #include <semaphore.h>
585184Sek110237 #endif /* HAVE_SYSV_SEM */
595184Sek110237 
605184Sek110237 #include "filebench.h"
615184Sek110237 #include "flowop.h"
625184Sek110237 #include "fileset.h"
636212Saw148015 #include "fb_random.h"
647946SAndrew.W.Wilson@sun.com #include "utils.h"
658615SAndrew.W.Wilson@sun.com #include "fsplug.h"
668615SAndrew.W.Wilson@sun.com 
675184Sek110237 /*
685184Sek110237  * These routines implement the flowops from the f language. Each
695184Sek110237  * flowop has has a name such as "read", and a set of function pointers
705184Sek110237  * to call for initialization, execution and destruction of the flowop.
715184Sek110237  * The table flowoplib_funcs[] contains a flowoplib struct for each
725184Sek110237  * implemented flowop. Most flowops use a generic initialization function
735184Sek110237  * and all currently use a generic destruction function. All flowop
745184Sek110237  * functions referenced from the table are in this file, though, of
755184Sek110237  * course, they often call functions from other files.
765184Sek110237  *
775184Sek110237  * The flowop_init() routine uses the flowoplib_funcs[] table to
785184Sek110237  * create an initial set of "instance 0" flowops, one for each type of
795184Sek110237  * flowop, from which all other flowops are derived. These "instance 0"
805184Sek110237  * flowops are initialized with information from the table including
815184Sek110237  * pointers for their fo_init, fo_func and fo_destroy functions. When
825184Sek110237  * a flowop definition is encountered in an f language script, the
835184Sek110237  * "type" of flowop, such as "read" is used to search for the
845184Sek110237  * "instance 0" flowop named "read", then a new flowop is allocated
855184Sek110237  * which inherits its function pointers and other initial properties
865184Sek110237  * from the instance 0 flowop, and is given a new name as specified
875184Sek110237  * by the "name=" attribute.
885184Sek110237  */
895184Sek110237 
906084Saw148015 static void flowoplib_destruct_noop(flowop_t *flowop);
915184Sek110237 static int flowoplib_fdnum(threadflow_t *threadflow, flowop_t *flowop);
927556SAndrew.W.Wilson@sun.com static int flowoplib_print(threadflow_t *threadflow, flowop_t *flowop);
935184Sek110237 static int flowoplib_write(threadflow_t *threadflow, flowop_t *flowop);
945184Sek110237 static int flowoplib_read(threadflow_t *threadflow, flowop_t *flowop);
955184Sek110237 static int flowoplib_block_init(flowop_t *flowop);
965184Sek110237 static int flowoplib_block(threadflow_t *threadflow, flowop_t *flowop);
975184Sek110237 static int flowoplib_wakeup(threadflow_t *threadflow, flowop_t *flowop);
985184Sek110237 static int flowoplib_hog(threadflow_t *threadflow, flowop_t *flowop);
995184Sek110237 static int flowoplib_delay(threadflow_t *threadflow, flowop_t *flowop);
1005184Sek110237 static int flowoplib_sempost(threadflow_t *threadflow, flowop_t *flowop);
1015184Sek110237 static int flowoplib_sempost_init(flowop_t *flowop);
1025184Sek110237 static int flowoplib_semblock(threadflow_t *threadflow, flowop_t *flowop);
1035184Sek110237 static int flowoplib_semblock_init(flowop_t *flowop);
1045184Sek110237 static void flowoplib_semblock_destruct(flowop_t *flowop);
1055184Sek110237 static int flowoplib_eventlimit(threadflow_t *, flowop_t *flowop);
1065184Sek110237 static int flowoplib_bwlimit(threadflow_t *, flowop_t *flowop);
1075184Sek110237 static int flowoplib_iopslimit(threadflow_t *, flowop_t *flowop);
1085184Sek110237 static int flowoplib_opslimit(threadflow_t *, flowop_t *flowop);
1095184Sek110237 static int flowoplib_openfile(threadflow_t *, flowop_t *flowop);
1105184Sek110237 static int flowoplib_openfile_common(threadflow_t *, flowop_t *flowop, int fd);
1115184Sek110237 static int flowoplib_createfile(threadflow_t *, flowop_t *flowop);
1125184Sek110237 static int flowoplib_closefile(threadflow_t *, flowop_t *flowop);
1137946SAndrew.W.Wilson@sun.com static int flowoplib_makedir(threadflow_t *, flowop_t *flowop);
1147946SAndrew.W.Wilson@sun.com static int flowoplib_removedir(threadflow_t *, flowop_t *flowop);
1157946SAndrew.W.Wilson@sun.com static int flowoplib_listdir(threadflow_t *, flowop_t *flowop);
1165184Sek110237 static int flowoplib_fsync(threadflow_t *, flowop_t *flowop);
1175184Sek110237 static int flowoplib_readwholefile(threadflow_t *, flowop_t *flowop);
1185184Sek110237 static int flowoplib_writewholefile(threadflow_t *, flowop_t *flowop);
1195184Sek110237 static int flowoplib_appendfile(threadflow_t *threadflow, flowop_t *flowop);
1205184Sek110237 static int flowoplib_appendfilerand(threadflow_t *threadflow, flowop_t *flowop);
1215184Sek110237 static int flowoplib_deletefile(threadflow_t *threadflow, flowop_t *flowop);
1225184Sek110237 static int flowoplib_statfile(threadflow_t *threadflow, flowop_t *flowop);
1235184Sek110237 static int flowoplib_finishoncount(threadflow_t *threadflow, flowop_t *flowop);
1245184Sek110237 static int flowoplib_finishonbytes(threadflow_t *threadflow, flowop_t *flowop);
1255184Sek110237 static int flowoplib_fsyncset(threadflow_t *threadflow, flowop_t *flowop);
1266212Saw148015 static int flowoplib_testrandvar(threadflow_t *threadflow, flowop_t *flowop);
1276212Saw148015 static int flowoplib_testrandvar_init(flowop_t *flowop);
1286212Saw148015 static void flowoplib_testrandvar_destruct(flowop_t *flowop);
1295184Sek110237 
1308615SAndrew.W.Wilson@sun.com static flowop_proto_t flowoplib_funcs[] = {
1318615SAndrew.W.Wilson@sun.com 	FLOW_TYPE_IO, FLOW_ATTR_WRITE, "write", flowop_init_generic,
1328615SAndrew.W.Wilson@sun.com 	flowoplib_write, flowop_destruct_generic,
1338615SAndrew.W.Wilson@sun.com 	FLOW_TYPE_IO, FLOW_ATTR_READ, "read", flowop_init_generic,
1348615SAndrew.W.Wilson@sun.com 	flowoplib_read, flowop_destruct_generic,
1355184Sek110237 	FLOW_TYPE_SYNC, 0, "block", flowoplib_block_init,
1368615SAndrew.W.Wilson@sun.com 	flowoplib_block, flowop_destruct_generic,
1378615SAndrew.W.Wilson@sun.com 	FLOW_TYPE_SYNC, 0, "wakeup", flowop_init_generic,
1388615SAndrew.W.Wilson@sun.com 	flowoplib_wakeup, flowop_destruct_generic,
1395184Sek110237 	FLOW_TYPE_SYNC, 0, "semblock", flowoplib_semblock_init,
1405184Sek110237 	flowoplib_semblock, flowoplib_semblock_destruct,
1415184Sek110237 	FLOW_TYPE_SYNC, 0, "sempost", flowoplib_sempost_init,
1426084Saw148015 	flowoplib_sempost, flowoplib_destruct_noop,
1438615SAndrew.W.Wilson@sun.com 	FLOW_TYPE_OTHER, 0, "hog", flowop_init_generic,
1448615SAndrew.W.Wilson@sun.com 	flowoplib_hog, flowop_destruct_generic,
1458615SAndrew.W.Wilson@sun.com 	FLOW_TYPE_OTHER, 0, "delay", flowop_init_generic,
1468615SAndrew.W.Wilson@sun.com 	flowoplib_delay, flowop_destruct_generic,
1478615SAndrew.W.Wilson@sun.com 	FLOW_TYPE_OTHER, 0, "eventlimit", flowop_init_generic,
1488615SAndrew.W.Wilson@sun.com 	flowoplib_eventlimit, flowop_destruct_generic,
1498615SAndrew.W.Wilson@sun.com 	FLOW_TYPE_OTHER, 0, "bwlimit", flowop_init_generic,
1508615SAndrew.W.Wilson@sun.com 	flowoplib_bwlimit, flowop_destruct_generic,
1518615SAndrew.W.Wilson@sun.com 	FLOW_TYPE_OTHER, 0, "iopslimit", flowop_init_generic,
1528615SAndrew.W.Wilson@sun.com 	flowoplib_iopslimit, flowop_destruct_generic,
1538615SAndrew.W.Wilson@sun.com 	FLOW_TYPE_OTHER, 0, "opslimit", flowop_init_generic,
1548615SAndrew.W.Wilson@sun.com 	flowoplib_opslimit, flowop_destruct_generic,
1558615SAndrew.W.Wilson@sun.com 	FLOW_TYPE_OTHER, 0, "finishoncount", flowop_init_generic,
1568615SAndrew.W.Wilson@sun.com 	flowoplib_finishoncount, flowop_destruct_generic,
1578615SAndrew.W.Wilson@sun.com 	FLOW_TYPE_OTHER, 0, "finishonbytes", flowop_init_generic,
1588615SAndrew.W.Wilson@sun.com 	flowoplib_finishonbytes, flowop_destruct_generic,
1598615SAndrew.W.Wilson@sun.com 	FLOW_TYPE_IO, 0, "openfile", flowop_init_generic,
1608615SAndrew.W.Wilson@sun.com 	flowoplib_openfile, flowop_destruct_generic,
1618615SAndrew.W.Wilson@sun.com 	FLOW_TYPE_IO, 0, "createfile", flowop_init_generic,
1628615SAndrew.W.Wilson@sun.com 	flowoplib_createfile, flowop_destruct_generic,
1638615SAndrew.W.Wilson@sun.com 	FLOW_TYPE_IO, 0, "closefile", flowop_init_generic,
1648615SAndrew.W.Wilson@sun.com 	flowoplib_closefile, flowop_destruct_generic,
1658615SAndrew.W.Wilson@sun.com 	FLOW_TYPE_IO, 0, "makedir", flowop_init_generic,
1668615SAndrew.W.Wilson@sun.com 	flowoplib_makedir, flowop_destruct_generic,
1678615SAndrew.W.Wilson@sun.com 	FLOW_TYPE_IO, 0, "removedir", flowop_init_generic,
1688615SAndrew.W.Wilson@sun.com 	flowoplib_removedir, flowop_destruct_generic,
1698615SAndrew.W.Wilson@sun.com 	FLOW_TYPE_IO, 0, "listdir", flowop_init_generic,
1708615SAndrew.W.Wilson@sun.com 	flowoplib_listdir, flowop_destruct_generic,
1718615SAndrew.W.Wilson@sun.com 	FLOW_TYPE_IO, 0, "fsync", flowop_init_generic,
1728615SAndrew.W.Wilson@sun.com 	flowoplib_fsync, flowop_destruct_generic,
1738615SAndrew.W.Wilson@sun.com 	FLOW_TYPE_IO, 0, "fsyncset", flowop_init_generic,
1748615SAndrew.W.Wilson@sun.com 	flowoplib_fsyncset, flowop_destruct_generic,
1758615SAndrew.W.Wilson@sun.com 	FLOW_TYPE_IO, 0, "statfile", flowop_init_generic,
1768615SAndrew.W.Wilson@sun.com 	flowoplib_statfile, flowop_destruct_generic,
1778615SAndrew.W.Wilson@sun.com 	FLOW_TYPE_IO, FLOW_ATTR_READ, "readwholefile", flowop_init_generic,
1788615SAndrew.W.Wilson@sun.com 	flowoplib_readwholefile, flowop_destruct_generic,
1798615SAndrew.W.Wilson@sun.com 	FLOW_TYPE_IO, FLOW_ATTR_WRITE, "appendfile", flowop_init_generic,
1808615SAndrew.W.Wilson@sun.com 	flowoplib_appendfile, flowop_destruct_generic,
1818615SAndrew.W.Wilson@sun.com 	FLOW_TYPE_IO, FLOW_ATTR_WRITE, "appendfilerand", flowop_init_generic,
1828615SAndrew.W.Wilson@sun.com 	flowoplib_appendfilerand, flowop_destruct_generic,
1838615SAndrew.W.Wilson@sun.com 	FLOW_TYPE_IO, 0, "deletefile", flowop_init_generic,
1848615SAndrew.W.Wilson@sun.com 	flowoplib_deletefile, flowop_destruct_generic,
1858615SAndrew.W.Wilson@sun.com 	FLOW_TYPE_IO, FLOW_ATTR_WRITE, "writewholefile", flowop_init_generic,
1868615SAndrew.W.Wilson@sun.com 	flowoplib_writewholefile, flowop_destruct_generic,
1878615SAndrew.W.Wilson@sun.com 	FLOW_TYPE_OTHER, 0, "print", flowop_init_generic,
1888615SAndrew.W.Wilson@sun.com 	flowoplib_print, flowop_destruct_generic,
1896212Saw148015 	/* routine to calculate mean and stddev for output from a randvar */
1906212Saw148015 	FLOW_TYPE_OTHER, 0, "testrandvar", flowoplib_testrandvar_init,
1916212Saw148015 	flowoplib_testrandvar, flowoplib_testrandvar_destruct
1925184Sek110237 };
1935184Sek110237 
1945184Sek110237 /*
1958615SAndrew.W.Wilson@sun.com  * Loops through the list of flowops defined in this
1965184Sek110237  * module, and creates and initializes a flowop for each one
1978615SAndrew.W.Wilson@sun.com  * by calling flowop_flow_init. As a side effect of calling
1988615SAndrew.W.Wilson@sun.com  * flowop_flow_init, the created flowops are placed on the
1995184Sek110237  * master flowop list. All created flowops are set to
2005184Sek110237  * instance "0".
2015184Sek110237  */
2025184Sek110237 void
2038615SAndrew.W.Wilson@sun.com flowoplib_flowinit()
2045184Sek110237 {
2058615SAndrew.W.Wilson@sun.com 	int nops = sizeof (flowoplib_funcs) / sizeof (flowop_proto_t);
2068615SAndrew.W.Wilson@sun.com 
2078615SAndrew.W.Wilson@sun.com 	flowop_flow_init(flowoplib_funcs, nops);
2086084Saw148015 }
2096084Saw148015 
2106084Saw148015 /*
2116084Saw148015  * Special total noop destruct
2126084Saw148015  */
2136084Saw148015 /* ARGSUSED */
2146084Saw148015 static void
2156084Saw148015 flowoplib_destruct_noop(flowop_t *flowop)
2166084Saw148015 {
2175184Sek110237 }
2185184Sek110237 
2195184Sek110237 /*
2205184Sek110237  * Generates a file attribute from flags in the supplied flowop.
2215184Sek110237  * Sets FLOW_ATTR_DIRECTIO and/or FLOW_ATTR_DSYNC as needed.
2225184Sek110237  */
2235184Sek110237 static int
2245184Sek110237 flowoplib_fileattrs(flowop_t *flowop)
2255184Sek110237 {
2265184Sek110237 	int attrs = 0;
2275184Sek110237 
2286212Saw148015 	if (avd_get_bool(flowop->fo_directio))
2295184Sek110237 		attrs |= FLOW_ATTR_DIRECTIO;
2305184Sek110237 
2316212Saw148015 	if (avd_get_bool(flowop->fo_dsync))
2325184Sek110237 		attrs |= FLOW_ATTR_DSYNC;
2335184Sek110237 
2345184Sek110237 	return (attrs);
2355184Sek110237 }
2365184Sek110237 
2375184Sek110237 /*
2388404SAndrew.W.Wilson@sun.com  * Obtain a filesetentry for a file. Result placed where filep points.
2398404SAndrew.W.Wilson@sun.com  * Supply with a flowop and a flag to indicate whether an existent or
2408404SAndrew.W.Wilson@sun.com  * non-existent file is required. Returns FILEBENCH_NORSC if all out
2418404SAndrew.W.Wilson@sun.com  * of the appropriate type of directories, FILEBENCH_ERROR if the
2428404SAndrew.W.Wilson@sun.com  * flowop does not point to a fileset, and FILEBENCH_OK otherwise.
2438404SAndrew.W.Wilson@sun.com  */
2448404SAndrew.W.Wilson@sun.com static int
2458404SAndrew.W.Wilson@sun.com flowoplib_pickfile(filesetentry_t **filep, flowop_t *flowop, int flags, int tid)
2468404SAndrew.W.Wilson@sun.com {
2478404SAndrew.W.Wilson@sun.com 	fileset_t	*fileset;
2488404SAndrew.W.Wilson@sun.com 	int		fileindex;
2498404SAndrew.W.Wilson@sun.com 
2508404SAndrew.W.Wilson@sun.com 	if ((fileset = flowop->fo_fileset) == NULL) {
2518404SAndrew.W.Wilson@sun.com 		filebench_log(LOG_ERROR, "flowop NO fileset");
2528404SAndrew.W.Wilson@sun.com 		return (FILEBENCH_ERROR);
2538404SAndrew.W.Wilson@sun.com 	}
2548404SAndrew.W.Wilson@sun.com 
2558404SAndrew.W.Wilson@sun.com 	if (flowop->fo_fileindex) {
2568404SAndrew.W.Wilson@sun.com 		fileindex = (int)(avd_get_dbl(flowop->fo_fileindex) *
2578404SAndrew.W.Wilson@sun.com 		    ((double)(fileset->fs_constentries / 2)));
2588404SAndrew.W.Wilson@sun.com 		fileindex = fileindex % fileset->fs_constentries;
2598404SAndrew.W.Wilson@sun.com 		flags |= FILESET_PICKBYINDEX;
2608404SAndrew.W.Wilson@sun.com 	} else {
2618404SAndrew.W.Wilson@sun.com 		fileindex = 0;
2628404SAndrew.W.Wilson@sun.com 	}
2638404SAndrew.W.Wilson@sun.com 
2648404SAndrew.W.Wilson@sun.com 	if ((*filep = fileset_pick(fileset, FILESET_PICKFILE | flags,
2658404SAndrew.W.Wilson@sun.com 	    tid, fileindex)) == NULL) {
2668404SAndrew.W.Wilson@sun.com 		filebench_log(LOG_DEBUG_SCRIPT,
2678404SAndrew.W.Wilson@sun.com 		    "flowop %s failed to pick file from fileset %s",
2688404SAndrew.W.Wilson@sun.com 		    flowop->fo_name,
2698404SAndrew.W.Wilson@sun.com 		    avd_get_str(fileset->fs_name));
2708404SAndrew.W.Wilson@sun.com 		return (FILEBENCH_NORSC);
2718404SAndrew.W.Wilson@sun.com 	}
2728404SAndrew.W.Wilson@sun.com 
2738404SAndrew.W.Wilson@sun.com 	return (FILEBENCH_OK);
2748404SAndrew.W.Wilson@sun.com }
2758404SAndrew.W.Wilson@sun.com 
2768404SAndrew.W.Wilson@sun.com /*
2778404SAndrew.W.Wilson@sun.com  * Obtain a filesetentry for a leaf directory. Result placed where dirp
2788404SAndrew.W.Wilson@sun.com  * points. Supply with flowop and a flag to indicate whether an existent
2798404SAndrew.W.Wilson@sun.com  * or non-existent leaf directory is required. Returns FILEBENCH_NORSC
2808404SAndrew.W.Wilson@sun.com  * if all out of the appropriate type of directories, FILEBENCH_ERROR
2818404SAndrew.W.Wilson@sun.com  * if the flowop does not point to a fileset, and FILEBENCH_OK otherwise.
2828404SAndrew.W.Wilson@sun.com  */
2838404SAndrew.W.Wilson@sun.com static int
2848404SAndrew.W.Wilson@sun.com flowoplib_pickleafdir(filesetentry_t **dirp, flowop_t *flowop, int flags)
2858404SAndrew.W.Wilson@sun.com {
2868404SAndrew.W.Wilson@sun.com 	fileset_t	*fileset;
2878404SAndrew.W.Wilson@sun.com 	int		dirindex;
2888404SAndrew.W.Wilson@sun.com 
2898404SAndrew.W.Wilson@sun.com 	if ((fileset = flowop->fo_fileset) == NULL) {
2908404SAndrew.W.Wilson@sun.com 		filebench_log(LOG_ERROR, "flowop NO fileset");
2918404SAndrew.W.Wilson@sun.com 		return (FILEBENCH_ERROR);
2928404SAndrew.W.Wilson@sun.com 	}
2938404SAndrew.W.Wilson@sun.com 
2948404SAndrew.W.Wilson@sun.com 	if (flowop->fo_fileindex) {
2958404SAndrew.W.Wilson@sun.com 		dirindex = (int)(avd_get_dbl(flowop->fo_fileindex) *
2968404SAndrew.W.Wilson@sun.com 		    ((double)(fileset->fs_constleafdirs / 2)));
2978404SAndrew.W.Wilson@sun.com 		dirindex = dirindex % fileset->fs_constleafdirs;
2988404SAndrew.W.Wilson@sun.com 		flags |= FILESET_PICKBYINDEX;
2998404SAndrew.W.Wilson@sun.com 	} else {
3008404SAndrew.W.Wilson@sun.com 		dirindex = 0;
3018404SAndrew.W.Wilson@sun.com 	}
3028404SAndrew.W.Wilson@sun.com 
3038404SAndrew.W.Wilson@sun.com 	if ((*dirp = fileset_pick(fileset,
3048404SAndrew.W.Wilson@sun.com 	    FILESET_PICKLEAFDIR | flags, 0, dirindex)) == NULL) {
3058404SAndrew.W.Wilson@sun.com 		filebench_log(LOG_DEBUG_SCRIPT,
3068404SAndrew.W.Wilson@sun.com 		    "flowop %s failed to pick directory from fileset %s",
3078404SAndrew.W.Wilson@sun.com 		    flowop->fo_name,
3088404SAndrew.W.Wilson@sun.com 		    avd_get_str(fileset->fs_name));
3098404SAndrew.W.Wilson@sun.com 		return (FILEBENCH_NORSC);
3108404SAndrew.W.Wilson@sun.com 	}
3118404SAndrew.W.Wilson@sun.com 
3128404SAndrew.W.Wilson@sun.com 	return (FILEBENCH_OK);
3138404SAndrew.W.Wilson@sun.com }
3148404SAndrew.W.Wilson@sun.com 
3158404SAndrew.W.Wilson@sun.com /*
3165184Sek110237  * Searches for a file descriptor. Tries the flowop's
3175184Sek110237  * fo_fdnumber first and returns with it if it has been
3185184Sek110237  * explicitly set (greater than 0). It next checks to
3195184Sek110237  * see if a rotating file descriptor policy is in effect,
3205184Sek110237  * and if not returns the fdnumber regardless of what
3215184Sek110237  * it is. (note that if it is 0, it just selects to the
3225184Sek110237  * default file descriptor in the threadflow's tf_fd
3235184Sek110237  * array). If the rotating fd policy is in effect, it
3245184Sek110237  * cycles from the end of the tf_fd array to one location
3255184Sek110237  * beyond the maximum needed by the number of entries in
3265184Sek110237  * the associated fileset on each invocation, then starts
3275184Sek110237  * over from the end.
3285184Sek110237  *
3295184Sek110237  * The routine returns an index into the threadflow's
3305184Sek110237  * tf_fd table where the actual file descriptor will be
3315184Sek110237  * found. Note: the calling routine must not call this
3325184Sek110237  * routine if the flowop does not have a fileset, and the
3335184Sek110237  * flowop's fo_fdnumber is zero and fo_rotatefd is
3345184Sek110237  * asserted, or an addressing fault may occur.
3355184Sek110237  */
3365673Saw148015 static int
3375184Sek110237 flowoplib_fdnum(threadflow_t *threadflow, flowop_t *flowop)
3385184Sek110237 {
3396212Saw148015 	fbint_t	entries;
3406391Saw148015 	int fdnumber = flowop->fo_fdnumber;
3416212Saw148015 
3425184Sek110237 	/* If the script sets the fd explicitly */
3436391Saw148015 	if (fdnumber > 0)
3446391Saw148015 		return (fdnumber);
3455184Sek110237 
3465184Sek110237 	/* If the flowop defaults to persistent fd */
3476212Saw148015 	if (!avd_get_bool(flowop->fo_rotatefd))
3486391Saw148015 		return (fdnumber);
3496391Saw148015 
3506391Saw148015 	if (flowop->fo_fileset == NULL) {
3516391Saw148015 		filebench_log(LOG_ERROR, "flowop NULL file");
3526391Saw148015 		return (FILEBENCH_ERROR);
3536391Saw148015 	}
3545184Sek110237 
3556212Saw148015 	entries = flowop->fo_fileset->fs_constentries;
3566212Saw148015 
3575184Sek110237 	/* Rotate the fd on each flowop invocation */
3586212Saw148015 	if (entries > (THREADFLOW_MAXFD / 2)) {
3595184Sek110237 		filebench_log(LOG_ERROR, "Out of file descriptors in flowop %s"
3606286Saw148015 		    " (too many files : %llu",
3616286Saw148015 		    flowop->fo_name, (u_longlong_t)entries);
3626084Saw148015 		return (FILEBENCH_ERROR);
3635184Sek110237 	}
3645184Sek110237 
3655184Sek110237 	/* First time around */
3665184Sek110237 	if (threadflow->tf_fdrotor == 0)
3675184Sek110237 		threadflow->tf_fdrotor = THREADFLOW_MAXFD;
3685184Sek110237 
3695184Sek110237 	/* One fd for every file in the set */
3706212Saw148015 	if (entries == (THREADFLOW_MAXFD - threadflow->tf_fdrotor))
3715184Sek110237 		threadflow->tf_fdrotor = THREADFLOW_MAXFD;
3725184Sek110237 
3735184Sek110237 
3745184Sek110237 	threadflow->tf_fdrotor--;
3755184Sek110237 	filebench_log(LOG_DEBUG_IMPL, "selected fd = %d",
3765184Sek110237 	    threadflow->tf_fdrotor);
3775184Sek110237 	return (threadflow->tf_fdrotor);
3785184Sek110237 }
3795184Sek110237 
3805184Sek110237 /*
3815673Saw148015  * Determines the file descriptor to use, and attempts to open
3825673Saw148015  * the file if it is not already open. Also determines the wss
3836084Saw148015  * value. Returns FILEBENCH_ERROR on errors, FILESET_NORSC if
3846084Saw148015  * if flowop_openfile_common couldn't obtain an appropriate file
3856084Saw148015  * from a the fileset, and FILEBENCH_OK otherwise.
3865673Saw148015  */
3875673Saw148015 static int
3885673Saw148015 flowoplib_filesetup(threadflow_t *threadflow, flowop_t *flowop,
3898615SAndrew.W.Wilson@sun.com     fbint_t *wssp, fb_fdesc_t **fdescp)
3905673Saw148015 {
3915673Saw148015 	int fd = flowoplib_fdnum(threadflow, flowop);
3925673Saw148015 
3935673Saw148015 	if (fd == -1)
3946084Saw148015 		return (FILEBENCH_ERROR);
3955673Saw148015 
396*9326SAndrew.W.Wilson@sun.com 	/* check for conflicting fdnumber and file name */
397*9326SAndrew.W.Wilson@sun.com 	if ((fd > 0) && (threadflow->tf_fse[fd] != NULL)) {
398*9326SAndrew.W.Wilson@sun.com 		char *fd_based_name;
399*9326SAndrew.W.Wilson@sun.com 
400*9326SAndrew.W.Wilson@sun.com 		fd_based_name =
401*9326SAndrew.W.Wilson@sun.com 		    avd_get_str(threadflow->tf_fse[fd]->fse_fileset->fs_name);
402*9326SAndrew.W.Wilson@sun.com 
403*9326SAndrew.W.Wilson@sun.com 		if (flowop->fo_filename != NULL) {
404*9326SAndrew.W.Wilson@sun.com 			char *fo_based_name;
405*9326SAndrew.W.Wilson@sun.com 
406*9326SAndrew.W.Wilson@sun.com 			fo_based_name = avd_get_str(flowop->fo_filename);
407*9326SAndrew.W.Wilson@sun.com 			if (strcmp(fd_based_name, fo_based_name) != 0) {
408*9326SAndrew.W.Wilson@sun.com 				filebench_log(LOG_ERROR, "Name of fd refer"
409*9326SAndrew.W.Wilson@sun.com 				    "enced fileset name (%s) CONFLICTS with"
410*9326SAndrew.W.Wilson@sun.com 				    " flowop supplied fileset name (%s)",
411*9326SAndrew.W.Wilson@sun.com 				    fd_based_name, fo_based_name);
412*9326SAndrew.W.Wilson@sun.com 				filebench_shutdown(1);
413*9326SAndrew.W.Wilson@sun.com 				return (FILEBENCH_ERROR);
414*9326SAndrew.W.Wilson@sun.com 			}
415*9326SAndrew.W.Wilson@sun.com 		}
416*9326SAndrew.W.Wilson@sun.com 	}
417*9326SAndrew.W.Wilson@sun.com 
4188615SAndrew.W.Wilson@sun.com 	if (threadflow->tf_fd[fd].fd_ptr == NULL) {
4196084Saw148015 		int ret;
4206084Saw148015 
4216084Saw148015 		if ((ret = flowoplib_openfile_common(
4226084Saw148015 		    threadflow, flowop, fd)) != FILEBENCH_OK)
4236084Saw148015 			return (ret);
4245673Saw148015 
4255673Saw148015 		if (threadflow->tf_fse[fd]) {
4265673Saw148015 			filebench_log(LOG_DEBUG_IMPL, "opened file %s",
4275673Saw148015 			    threadflow->tf_fse[fd]->fse_path);
4285673Saw148015 		} else {
4295673Saw148015 			filebench_log(LOG_DEBUG_IMPL,
4305673Saw148015 			    "opened device %s/%s",
4316212Saw148015 			    avd_get_str(flowop->fo_fileset->fs_path),
4326212Saw148015 			    avd_get_str(flowop->fo_fileset->fs_name));
4335673Saw148015 		}
4345673Saw148015 	}
4355673Saw148015 
4368615SAndrew.W.Wilson@sun.com 	*fdescp = &(threadflow->tf_fd[fd]);
4375673Saw148015 
4386212Saw148015 	if ((*wssp = flowop->fo_constwss) == 0) {
4395673Saw148015 		if (threadflow->tf_fse[fd])
4405673Saw148015 			*wssp = threadflow->tf_fse[fd]->fse_size;
4415673Saw148015 		else
4426212Saw148015 			*wssp = avd_get_int(flowop->fo_fileset->fs_size);
4435673Saw148015 	}
4445673Saw148015 
4456084Saw148015 	return (FILEBENCH_OK);
4465673Saw148015 }
4475673Saw148015 
4485673Saw148015 /*
4495673Saw148015  * Determines the io buffer or random offset into tf_mem for
4506084Saw148015  * the IO operation. Returns FILEBENCH_ERROR on errors, FILEBENCH_OK otherwise.
4515673Saw148015  */
4525673Saw148015 static int
4535673Saw148015 flowoplib_iobufsetup(threadflow_t *threadflow, flowop_t *flowop,
4546212Saw148015     caddr_t *iobufp, fbint_t iosize)
4555673Saw148015 {
4565673Saw148015 	long memsize;
4575673Saw148015 	size_t memoffset;
4585673Saw148015 
4595673Saw148015 	if (iosize == 0) {
4605673Saw148015 		filebench_log(LOG_ERROR, "zero iosize for thread %s",
4615673Saw148015 		    flowop->fo_name);
4626084Saw148015 		return (FILEBENCH_ERROR);
4635673Saw148015 	}
4645673Saw148015 
4656212Saw148015 	if ((memsize = threadflow->tf_constmemsize) != 0) {
4665673Saw148015 
4675673Saw148015 		/* use tf_mem for I/O with random offset */
4686212Saw148015 		if (filebench_randomno(&memoffset,
4696212Saw148015 		    memsize, iosize, NULL) == -1) {
4705673Saw148015 			filebench_log(LOG_ERROR,
4715673Saw148015 			    "tf_memsize smaller than IO size for thread %s",
4725673Saw148015 			    flowop->fo_name);
4736084Saw148015 			return (FILEBENCH_ERROR);
4745673Saw148015 		}
4755673Saw148015 		*iobufp = threadflow->tf_mem + memoffset;
4765673Saw148015 
4775673Saw148015 	} else {
4785673Saw148015 		/* use private I/O buffer */
4795673Saw148015 		if ((flowop->fo_buf != NULL) &&
4805673Saw148015 		    (flowop->fo_buf_size < iosize)) {
4816212Saw148015 			/* too small, so free up and re-allocate */
4825673Saw148015 			free(flowop->fo_buf);
4835673Saw148015 			flowop->fo_buf = NULL;
4845673Saw148015 		}
4856212Saw148015 
4866212Saw148015 		/*
4876212Saw148015 		 * Allocate memory for the  buffer. The memory is freed
4886212Saw148015 		 * by flowop_destruct_generic() or by this routine if more
4896212Saw148015 		 * memory is needed for the buffer.
4906212Saw148015 		 */
4915673Saw148015 		if ((flowop->fo_buf == NULL) && ((flowop->fo_buf
4925673Saw148015 		    = (char *)malloc(iosize)) == NULL))
4936084Saw148015 			return (FILEBENCH_ERROR);
4945673Saw148015 
4955673Saw148015 		flowop->fo_buf_size = iosize;
4965673Saw148015 		*iobufp = flowop->fo_buf;
4975673Saw148015 	}
4986084Saw148015 	return (FILEBENCH_OK);
4995673Saw148015 }
5005673Saw148015 
5015673Saw148015 /*
5025673Saw148015  * Determines the file descriptor to use, opens it if necessary, the
5035673Saw148015  * io buffer or random offset into tf_mem for IO operation and the wss
5046084Saw148015  * value. Returns FILEBENCH_ERROR on errors, FILEBENCH_OK otherwise.
5055673Saw148015  */
5068615SAndrew.W.Wilson@sun.com int
5075673Saw148015 flowoplib_iosetup(threadflow_t *threadflow, flowop_t *flowop,
5088615SAndrew.W.Wilson@sun.com     fbint_t *wssp, caddr_t *iobufp, fb_fdesc_t **filedescp, fbint_t iosize)
5095673Saw148015 {
5106084Saw148015 	int ret;
5116084Saw148015 
5126084Saw148015 	if ((ret = flowoplib_filesetup(threadflow, flowop, wssp, filedescp)) !=
5136084Saw148015 	    FILEBENCH_OK)
5146084Saw148015 		return (ret);
5155673Saw148015 
5166084Saw148015 	if ((ret = flowoplib_iobufsetup(threadflow, flowop, iobufp, iosize)) !=
5176084Saw148015 	    FILEBENCH_OK)
5186084Saw148015 		return (ret);
5195673Saw148015 
5206084Saw148015 	return (FILEBENCH_OK);
5215673Saw148015 }
5225673Saw148015 
5235673Saw148015 /*
5245184Sek110237  * Emulate posix read / pread. If the flowop has a fileset,
5255184Sek110237  * a file descriptor number index is fetched, otherwise a
5265184Sek110237  * supplied fileobj file is used. In either case the specified
5275184Sek110237  * file will be opened if not already open. If the flowop has
5286084Saw148015  * neither a fileset or fileobj, an error is logged and FILEBENCH_ERROR
5295184Sek110237  * returned.
5305184Sek110237  *
5315184Sek110237  * The actual read is done to a random offset in the
5325184Sek110237  * threadflow's thread memory (tf_mem), with a size set by
5335184Sek110237  * fo_iosize and at either a random disk offset within the
5345184Sek110237  * working set size, or at the next sequential location. If
5356084Saw148015  * any errors are encountered, FILEBENCH_ERROR is returned,
5366084Saw148015  * if no appropriate file can be obtained from the fileset then
5376084Saw148015  * FILEBENCH_NORSC is returned, otherise FILEBENCH_OK is returned.
5385184Sek110237  */
5395184Sek110237 static int
5405184Sek110237 flowoplib_read(threadflow_t *threadflow, flowop_t *flowop)
5415184Sek110237 {
5425673Saw148015 	caddr_t iobuf;
5436212Saw148015 	fbint_t wss;
5446212Saw148015 	fbint_t iosize;
5458615SAndrew.W.Wilson@sun.com 	fb_fdesc_t *fdesc;
5465184Sek110237 	int ret;
5475184Sek110237 
5486212Saw148015 
5496212Saw148015 	iosize = avd_get_int(flowop->fo_iosize);
5506084Saw148015 	if ((ret = flowoplib_iosetup(threadflow, flowop, &wss, &iobuf,
5518615SAndrew.W.Wilson@sun.com 	    &fdesc, iosize)) != FILEBENCH_OK)
5526084Saw148015 		return (ret);
5535184Sek110237 
5546212Saw148015 	if (avd_get_bool(flowop->fo_random)) {
5555184Sek110237 		uint64_t fileoffset;
5565184Sek110237 
5576212Saw148015 		if (filebench_randomno64(&fileoffset,
5586212Saw148015 		    wss, iosize, NULL) == -1) {
5595184Sek110237 			filebench_log(LOG_ERROR,
5605184Sek110237 			    "file size smaller than IO size for thread %s",
5615184Sek110237 			    flowop->fo_name);
5626084Saw148015 			return (FILEBENCH_ERROR);
5635184Sek110237 		}
5645184Sek110237 
5655184Sek110237 		(void) flowop_beginop(threadflow, flowop);
5668615SAndrew.W.Wilson@sun.com 		if ((ret = FB_PREAD(fdesc, iobuf,
5676212Saw148015 		    iosize, (off64_t)fileoffset)) == -1) {
5685673Saw148015 			(void) flowop_endop(threadflow, flowop, 0);
5695184Sek110237 			filebench_log(LOG_ERROR,
5706286Saw148015 			    "read file %s failed, offset %llu "
5715673Saw148015 			    "io buffer %zd: %s",
5726212Saw148015 			    avd_get_str(flowop->fo_fileset->fs_name),
5736286Saw148015 			    (u_longlong_t)fileoffset, iobuf, strerror(errno));
5745673Saw148015 			flowop_endop(threadflow, flowop, 0);
5756084Saw148015 			return (FILEBENCH_ERROR);
5765184Sek110237 		}
5775673Saw148015 		(void) flowop_endop(threadflow, flowop, ret);
5785184Sek110237 
5795184Sek110237 		if ((ret == 0))
5808615SAndrew.W.Wilson@sun.com 			(void) FB_LSEEK(fdesc, 0, SEEK_SET);
5815184Sek110237 
5825184Sek110237 	} else {
5835184Sek110237 		(void) flowop_beginop(threadflow, flowop);
5848615SAndrew.W.Wilson@sun.com 		if ((ret = FB_READ(fdesc, iobuf, iosize)) == -1) {
5856212Saw148015 			(void) flowop_endop(threadflow, flowop, 0);
5865184Sek110237 			filebench_log(LOG_ERROR,
5875673Saw148015 			    "read file %s failed, io buffer %zd: %s",
5886212Saw148015 			    avd_get_str(flowop->fo_fileset->fs_name),
5895673Saw148015 			    iobuf, strerror(errno));
5905673Saw148015 			(void) flowop_endop(threadflow, flowop, 0);
5916084Saw148015 			return (FILEBENCH_ERROR);
5925184Sek110237 		}
5935673Saw148015 		(void) flowop_endop(threadflow, flowop, ret);
5945184Sek110237 
5955184Sek110237 		if ((ret == 0))
5968615SAndrew.W.Wilson@sun.com 			(void) FB_LSEEK(fdesc, 0, SEEK_SET);
5975184Sek110237 	}
5985184Sek110237 
5996084Saw148015 	return (FILEBENCH_OK);
6005184Sek110237 }
6015184Sek110237 
6025184Sek110237 /*
6035184Sek110237  * Initializes a "flowop_block" flowop. Specifically, it
6045184Sek110237  * initializes the flowop's fo_cv and unlocks the fo_lock.
6055184Sek110237  */
6065184Sek110237 static int
6075184Sek110237 flowoplib_block_init(flowop_t *flowop)
6085184Sek110237 {
6095184Sek110237 	filebench_log(LOG_DEBUG_IMPL, "flow %s-%d block init address %zx",
6105184Sek110237 	    flowop->fo_name, flowop->fo_instance, &flowop->fo_cv);
6115184Sek110237 	(void) pthread_cond_init(&flowop->fo_cv, ipc_condattr());
6125184Sek110237 	(void) ipc_mutex_unlock(&flowop->fo_lock);
6135184Sek110237 
6146084Saw148015 	return (FILEBENCH_OK);
6155184Sek110237 }
6165184Sek110237 
6175184Sek110237 /*
6185184Sek110237  * Blocks the threadflow until woken up by flowoplib_wakeup.
6195184Sek110237  * The routine blocks on the flowop's fo_cv condition variable.
6205184Sek110237  */
6215184Sek110237 static int
6225184Sek110237 flowoplib_block(threadflow_t *threadflow, flowop_t *flowop)
6235184Sek110237 {
6245184Sek110237 	filebench_log(LOG_DEBUG_IMPL, "flow %s-%d blocking at address %zx",
6255184Sek110237 	    flowop->fo_name, flowop->fo_instance, &flowop->fo_cv);
6265184Sek110237 	(void) ipc_mutex_lock(&flowop->fo_lock);
6275184Sek110237 
6285184Sek110237 	flowop_beginop(threadflow, flowop);
6295184Sek110237 	(void) pthread_cond_wait(&flowop->fo_cv, &flowop->fo_lock);
6305673Saw148015 	flowop_endop(threadflow, flowop, 0);
6315184Sek110237 
6325184Sek110237 	filebench_log(LOG_DEBUG_IMPL, "flow %s-%d unblocking",
6335184Sek110237 	    flowop->fo_name, flowop->fo_instance);
6345184Sek110237 
6355184Sek110237 	(void) ipc_mutex_unlock(&flowop->fo_lock);
6365184Sek110237 
6376084Saw148015 	return (FILEBENCH_OK);
6385184Sek110237 }
6395184Sek110237 
6405184Sek110237 /*
6415184Sek110237  * Wakes up one or more target blocking flowops.
6425184Sek110237  * Sends broadcasts on the fo_cv condition variables of all
6435184Sek110237  * flowops on the target list, except those that are
6445184Sek110237  * FLOW_MASTER flowops. The target list consists of all
6455184Sek110237  * flowops whose name matches this flowop's "fo_targetname"
6465184Sek110237  * attribute. The target list is generated on the first
6475184Sek110237  * invocation, and the run will be shutdown if no targets
6486084Saw148015  * are found. Otherwise the routine always returns FILEBENCH_OK.
6495184Sek110237  */
6505184Sek110237 static int
6515184Sek110237 flowoplib_wakeup(threadflow_t *threadflow, flowop_t *flowop)
6525184Sek110237 {
6535184Sek110237 	flowop_t *target;
6545184Sek110237 
6555184Sek110237 	/* if this is the first wakeup, create the wakeup list */
6565184Sek110237 	if (flowop->fo_targets == NULL) {
6575184Sek110237 		flowop_t *result = flowop_find(flowop->fo_targetname);
6585184Sek110237 
6595184Sek110237 		flowop->fo_targets = result;
6605184Sek110237 		if (result == NULL) {
6615184Sek110237 			filebench_log(LOG_ERROR,
6625184Sek110237 			    "wakeup: could not find op %s for thread %s",
6635184Sek110237 			    flowop->fo_targetname,
6645184Sek110237 			    threadflow->tf_name);
6655184Sek110237 			filebench_shutdown(1);
6665184Sek110237 		}
6675184Sek110237 		while (result) {
6685184Sek110237 			result->fo_targetnext =
6695184Sek110237 			    result->fo_resultnext;
6705184Sek110237 			result = result->fo_resultnext;
6715184Sek110237 		}
6725184Sek110237 	}
6735184Sek110237 
6745184Sek110237 	target = flowop->fo_targets;
6755184Sek110237 
6765184Sek110237 	/* wakeup the targets */
6775184Sek110237 	while (target) {
6785184Sek110237 		if (target->fo_instance == FLOW_MASTER) {
6795184Sek110237 			target = target->fo_targetnext;
6805184Sek110237 			continue;
6815184Sek110237 		}
6825184Sek110237 		filebench_log(LOG_DEBUG_IMPL,
6835184Sek110237 		    "wakeup flow %s-%d at address %zx",
6845184Sek110237 		    target->fo_name,
6855184Sek110237 		    target->fo_instance,
6865184Sek110237 		    &target->fo_cv);
6875184Sek110237 
6885184Sek110237 		flowop_beginop(threadflow, flowop);
6895184Sek110237 		(void) ipc_mutex_lock(&target->fo_lock);
6905184Sek110237 		(void) pthread_cond_broadcast(&target->fo_cv);
6915184Sek110237 		(void) ipc_mutex_unlock(&target->fo_lock);
6925673Saw148015 		flowop_endop(threadflow, flowop, 0);
6935184Sek110237 
6945184Sek110237 		target = target->fo_targetnext;
6955184Sek110237 	}
6965184Sek110237 
6976084Saw148015 	return (FILEBENCH_OK);
6985184Sek110237 }
6995184Sek110237 
7005184Sek110237 /*
7015184Sek110237  * "think time" routines. the "hog" routine consumes cpu cycles as
7025184Sek110237  * it "thinks", while the "delay" flowop simply calls sleep() to delay
7035184Sek110237  * for a given number of seconds without consuming cpu cycles.
7045184Sek110237  */
7055184Sek110237 
7065184Sek110237 
7075184Sek110237 /*
7085184Sek110237  * Consumes CPU cycles and memory bandwidth by looping for
7095184Sek110237  * flowop->fo_value times. With each loop sets memory location
7105184Sek110237  * threadflow->tf_mem to 1.
7115184Sek110237  */
7125184Sek110237 static int
7135184Sek110237 flowoplib_hog(threadflow_t *threadflow, flowop_t *flowop)
7145184Sek110237 {
7156212Saw148015 	uint64_t value = avd_get_int(flowop->fo_value);
7165184Sek110237 	int i;
7175184Sek110237 
7185673Saw148015 	filebench_log(LOG_DEBUG_IMPL, "hog enter");
7195184Sek110237 	flowop_beginop(threadflow, flowop);
7205673Saw148015 	if (threadflow->tf_mem != NULL) {
7215673Saw148015 		for (i = 0; i < value; i++)
7225673Saw148015 			*(threadflow->tf_mem) = 1;
7235673Saw148015 	}
7245673Saw148015 	flowop_endop(threadflow, flowop, 0);
7255184Sek110237 	filebench_log(LOG_DEBUG_IMPL, "hog exit");
7266084Saw148015 	return (FILEBENCH_OK);
7275184Sek110237 }
7285184Sek110237 
7295184Sek110237 
7305184Sek110237 /*
7315184Sek110237  * Delays for fo_value seconds.
7325184Sek110237  */
7335184Sek110237 static int
7345184Sek110237 flowoplib_delay(threadflow_t *threadflow, flowop_t *flowop)
7355184Sek110237 {
7366212Saw148015 	int value = avd_get_int(flowop->fo_value);
7375184Sek110237 
7385184Sek110237 	flowop_beginop(threadflow, flowop);
7395184Sek110237 	(void) sleep(value);
7405673Saw148015 	flowop_endop(threadflow, flowop, 0);
7416084Saw148015 	return (FILEBENCH_OK);
7425184Sek110237 }
7435184Sek110237 
7445184Sek110237 /*
7455184Sek110237  * Rate limiting routines. This is the event consuming half of the
7465184Sek110237  * event system. Each of the four following routines will limit the rate
7475184Sek110237  * to one unit of either calls, issued I/O operations, issued filebench
7485184Sek110237  * operations, or I/O bandwidth. Since there is only one event generator,
7495184Sek110237  * the events will be divided amoung multiple instances of an event
7505184Sek110237  * consumer, and further divided among different consumers if more than
7515184Sek110237  * one has been defined. There is no mechanism to enforce equal sharing
7525184Sek110237  * of events.
7535184Sek110237  */
7545184Sek110237 
7555184Sek110237 /*
7565184Sek110237  * Completes one invocation per posted event. If eventgen_q
7575184Sek110237  * has an event count greater than zero, one will be removed
7585184Sek110237  * (count decremented), otherwise the calling thread will
7595184Sek110237  * block until another event has been posted. Always returns 0
7605184Sek110237  */
7615184Sek110237 static int
7625184Sek110237 flowoplib_eventlimit(threadflow_t *threadflow, flowop_t *flowop)
7635184Sek110237 {
7645184Sek110237 	/* Immediately bail if not set/enabled */
7657946SAndrew.W.Wilson@sun.com 	if (filebench_shm->shm_eventgen_hz == NULL)
7666084Saw148015 		return (FILEBENCH_OK);
7675184Sek110237 
7685184Sek110237 	if (flowop->fo_initted == 0) {
7695184Sek110237 		filebench_log(LOG_DEBUG_IMPL, "rate %zx %s-%d locking",
7705184Sek110237 		    flowop, threadflow->tf_name, threadflow->tf_instance);
7715184Sek110237 		flowop->fo_initted = 1;
7725184Sek110237 	}
7735184Sek110237 
7745184Sek110237 	flowop_beginop(threadflow, flowop);
7757946SAndrew.W.Wilson@sun.com 	while (filebench_shm->shm_eventgen_hz != NULL) {
7766391Saw148015 		(void) ipc_mutex_lock(&filebench_shm->shm_eventgen_lock);
7776391Saw148015 		if (filebench_shm->shm_eventgen_q > 0) {
7786391Saw148015 			filebench_shm->shm_eventgen_q--;
7796391Saw148015 			(void) ipc_mutex_unlock(
7806391Saw148015 			    &filebench_shm->shm_eventgen_lock);
7815184Sek110237 			break;
7825184Sek110237 		}
7836391Saw148015 		(void) pthread_cond_wait(&filebench_shm->shm_eventgen_cv,
7846391Saw148015 		    &filebench_shm->shm_eventgen_lock);
7856391Saw148015 		(void) ipc_mutex_unlock(&filebench_shm->shm_eventgen_lock);
7865184Sek110237 	}
7875673Saw148015 	flowop_endop(threadflow, flowop, 0);
7886084Saw148015 	return (FILEBENCH_OK);
7895184Sek110237 }
7905184Sek110237 
7916701Saw148015 static int
7926701Saw148015 flowoplib_event_find_target(threadflow_t *threadflow, flowop_t *flowop)
7936701Saw148015 {
7946701Saw148015 	if (flowop->fo_targetname[0] != '\0') {
7956701Saw148015 
7966701Saw148015 		/* Try to use statistics from specific flowop */
7976701Saw148015 		flowop->fo_targets =
7986701Saw148015 		    flowop_find_from_list(flowop->fo_targetname,
7996701Saw148015 		    threadflow->tf_thrd_fops);
8006701Saw148015 		if (flowop->fo_targets == NULL) {
8016701Saw148015 			filebench_log(LOG_ERROR,
8026701Saw148015 			    "limit target: could not find flowop %s",
8036701Saw148015 			    flowop->fo_targetname);
8046701Saw148015 			filebench_shutdown(1);
8056701Saw148015 			return (FILEBENCH_ERROR);
8066701Saw148015 		}
8076701Saw148015 	} else {
8086701Saw148015 		/* use total workload statistics */
8096701Saw148015 		flowop->fo_targets = NULL;
8106701Saw148015 	}
8116701Saw148015 	return (FILEBENCH_OK);
8126701Saw148015 }
8136701Saw148015 
8145184Sek110237 /*
8155184Sek110237  * Blocks the calling thread if the number of issued I/O
8165184Sek110237  * operations exceeds the number of posted events, thus
8175184Sek110237  * limiting the average I/O operation rate to the rate
8186084Saw148015  * specified by eventgen_hz. Always returns FILEBENCH_OK.
8195184Sek110237  */
8205184Sek110237 static int
8215184Sek110237 flowoplib_iopslimit(threadflow_t *threadflow, flowop_t *flowop)
8225184Sek110237 {
8235184Sek110237 	uint64_t iops;
8245184Sek110237 	uint64_t delta;
8255673Saw148015 	uint64_t events;
8265184Sek110237 
8275184Sek110237 	/* Immediately bail if not set/enabled */
8287946SAndrew.W.Wilson@sun.com 	if (filebench_shm->shm_eventgen_hz == NULL)
8296084Saw148015 		return (FILEBENCH_OK);
8305184Sek110237 
8315184Sek110237 	if (flowop->fo_initted == 0) {
8325184Sek110237 		filebench_log(LOG_DEBUG_IMPL, "rate %zx %s-%d locking",
8335184Sek110237 		    flowop, threadflow->tf_name, threadflow->tf_instance);
8345184Sek110237 		flowop->fo_initted = 1;
8356701Saw148015 
8366701Saw148015 		if (flowoplib_event_find_target(threadflow, flowop)
8376701Saw148015 		    == FILEBENCH_ERROR)
8386701Saw148015 			return (FILEBENCH_ERROR);
8396701Saw148015 
8406701Saw148015 		if (flowop->fo_targets && ((flowop->fo_targets->fo_attrs &
8416701Saw148015 		    (FLOW_ATTR_READ | FLOW_ATTR_WRITE)) == 0)) {
8426701Saw148015 			filebench_log(LOG_ERROR,
8436701Saw148015 			    "WARNING: Flowop %s does no IO",
8446701Saw148015 			    flowop->fo_targets->fo_name);
8456701Saw148015 			filebench_shutdown(1);
8466701Saw148015 			return (FILEBENCH_ERROR);
8476701Saw148015 		}
8485184Sek110237 	}
8495184Sek110237 
8506701Saw148015 	if (flowop->fo_targets) {
8516701Saw148015 		/*
8526701Saw148015 		 * Note that fs_count is already the sum of fs_rcount
8536701Saw148015 		 * and fs_wcount if looking at a single flowop.
8546701Saw148015 		 */
8556701Saw148015 		iops = flowop->fo_targets->fo_stats.fs_count;
8566701Saw148015 	} else {
8576701Saw148015 		(void) ipc_mutex_lock(&controlstats_lock);
8586701Saw148015 		iops = (controlstats.fs_rcount +
8596701Saw148015 		    controlstats.fs_wcount);
8606701Saw148015 		(void) ipc_mutex_unlock(&controlstats_lock);
8616701Saw148015 	}
8625184Sek110237 
8635184Sek110237 	/* Is this the first time around */
8645184Sek110237 	if (flowop->fo_tputlast == 0) {
8655184Sek110237 		flowop->fo_tputlast = iops;
8666084Saw148015 		return (FILEBENCH_OK);
8675184Sek110237 	}
8685184Sek110237 
8695184Sek110237 	delta = iops - flowop->fo_tputlast;
8705184Sek110237 	flowop->fo_tputbucket -= delta;
8715184Sek110237 	flowop->fo_tputlast = iops;
8725184Sek110237 
8735184Sek110237 	/* No need to block if the q isn't empty */
8745184Sek110237 	if (flowop->fo_tputbucket >= 0LL) {
8755673Saw148015 		flowop_endop(threadflow, flowop, 0);
8766084Saw148015 		return (FILEBENCH_OK);
8775184Sek110237 	}
8785184Sek110237 
8795184Sek110237 	iops = flowop->fo_tputbucket * -1;
8805184Sek110237 	events = iops;
8815184Sek110237 
8825184Sek110237 	flowop_beginop(threadflow, flowop);
8837946SAndrew.W.Wilson@sun.com 	while (filebench_shm->shm_eventgen_hz != NULL) {
8845184Sek110237 
8856391Saw148015 		(void) ipc_mutex_lock(&filebench_shm->shm_eventgen_lock);
8866391Saw148015 		if (filebench_shm->shm_eventgen_q >= events) {
8876391Saw148015 			filebench_shm->shm_eventgen_q -= events;
8886391Saw148015 			(void) ipc_mutex_unlock(
8896391Saw148015 			    &filebench_shm->shm_eventgen_lock);
8905184Sek110237 			flowop->fo_tputbucket += events;
8915184Sek110237 			break;
8925184Sek110237 		}
8936391Saw148015 		(void) pthread_cond_wait(&filebench_shm->shm_eventgen_cv,
8946391Saw148015 		    &filebench_shm->shm_eventgen_lock);
8956391Saw148015 		(void) ipc_mutex_unlock(&filebench_shm->shm_eventgen_lock);
8965184Sek110237 	}
8975673Saw148015 	flowop_endop(threadflow, flowop, 0);
8985184Sek110237 
8996084Saw148015 	return (FILEBENCH_OK);
9005184Sek110237 }
9015184Sek110237 
9025184Sek110237 /*
9035184Sek110237  * Blocks the calling thread if the number of issued filebench
9045184Sek110237  * operations exceeds the number of posted events, thus limiting
9055184Sek110237  * the average filebench operation rate to the rate specified by
9066084Saw148015  * eventgen_hz. Always returns FILEBENCH_OK.
9075184Sek110237  */
9085184Sek110237 static int
9095184Sek110237 flowoplib_opslimit(threadflow_t *threadflow, flowop_t *flowop)
9105184Sek110237 {
9115184Sek110237 	uint64_t ops;
9125184Sek110237 	uint64_t delta;
9135673Saw148015 	uint64_t events;
9145184Sek110237 
9155184Sek110237 	/* Immediately bail if not set/enabled */
9167946SAndrew.W.Wilson@sun.com 	if (filebench_shm->shm_eventgen_hz == NULL)
9176084Saw148015 		return (FILEBENCH_OK);
9185184Sek110237 
9195184Sek110237 	if (flowop->fo_initted == 0) {
9205184Sek110237 		filebench_log(LOG_DEBUG_IMPL, "rate %zx %s-%d locking",
9215184Sek110237 		    flowop, threadflow->tf_name, threadflow->tf_instance);
9225184Sek110237 		flowop->fo_initted = 1;
9236701Saw148015 
9246701Saw148015 		if (flowoplib_event_find_target(threadflow, flowop)
9256701Saw148015 		    == FILEBENCH_ERROR)
9266701Saw148015 			return (FILEBENCH_ERROR);
9275184Sek110237 	}
9285184Sek110237 
9296701Saw148015 	if (flowop->fo_targets) {
9306701Saw148015 		ops = flowop->fo_targets->fo_stats.fs_count;
9316701Saw148015 	} else {
9326701Saw148015 		(void) ipc_mutex_lock(&controlstats_lock);
9336701Saw148015 		ops = controlstats.fs_count;
9346701Saw148015 		(void) ipc_mutex_unlock(&controlstats_lock);
9356701Saw148015 	}
9365184Sek110237 
9375184Sek110237 	/* Is this the first time around */
9385184Sek110237 	if (flowop->fo_tputlast == 0) {
9395184Sek110237 		flowop->fo_tputlast = ops;
9406084Saw148015 		return (FILEBENCH_OK);
9415184Sek110237 	}
9425184Sek110237 
9435184Sek110237 	delta = ops - flowop->fo_tputlast;
9445184Sek110237 	flowop->fo_tputbucket -= delta;
9455184Sek110237 	flowop->fo_tputlast = ops;
9465184Sek110237 
9475184Sek110237 	/* No need to block if the q isn't empty */
9485184Sek110237 	if (flowop->fo_tputbucket >= 0LL) {
9495673Saw148015 		flowop_endop(threadflow, flowop, 0);
9506084Saw148015 		return (FILEBENCH_OK);
9515184Sek110237 	}
9525184Sek110237 
9535184Sek110237 	ops = flowop->fo_tputbucket * -1;
9545184Sek110237 	events = ops;
9555184Sek110237 
9565184Sek110237 	flowop_beginop(threadflow, flowop);
9577946SAndrew.W.Wilson@sun.com 	while (filebench_shm->shm_eventgen_hz != NULL) {
9586391Saw148015 		(void) ipc_mutex_lock(&filebench_shm->shm_eventgen_lock);
9596391Saw148015 		if (filebench_shm->shm_eventgen_q >= events) {
9606391Saw148015 			filebench_shm->shm_eventgen_q -= events;
9616391Saw148015 			(void) ipc_mutex_unlock(
9626391Saw148015 			    &filebench_shm->shm_eventgen_lock);
9635184Sek110237 			flowop->fo_tputbucket += events;
9645184Sek110237 			break;
9655184Sek110237 		}
9666391Saw148015 		(void) pthread_cond_wait(&filebench_shm->shm_eventgen_cv,
9676391Saw148015 		    &filebench_shm->shm_eventgen_lock);
9686391Saw148015 		(void) ipc_mutex_unlock(&filebench_shm->shm_eventgen_lock);
9695184Sek110237 	}
9705673Saw148015 	flowop_endop(threadflow, flowop, 0);
9715184Sek110237 
9726084Saw148015 	return (FILEBENCH_OK);
9735184Sek110237 }
9745184Sek110237 
9755184Sek110237 
9765184Sek110237 /*
9775184Sek110237  * Blocks the calling thread if the number of bytes of I/O
9785184Sek110237  * issued exceeds one megabyte times the number of posted
9795184Sek110237  * events, thus limiting the average I/O byte rate to one
9805184Sek110237  * megabyte times the event rate as set by eventgen_hz.
9816084Saw148015  * Always retuns FILEBENCH_OK.
9825184Sek110237  */
9835184Sek110237 static int
9845184Sek110237 flowoplib_bwlimit(threadflow_t *threadflow, flowop_t *flowop)
9855184Sek110237 {
9865184Sek110237 	uint64_t bytes;
9875184Sek110237 	uint64_t delta;
9885673Saw148015 	uint64_t events;
9895184Sek110237 
9905184Sek110237 	/* Immediately bail if not set/enabled */
9917946SAndrew.W.Wilson@sun.com 	if (filebench_shm->shm_eventgen_hz == NULL)
9926084Saw148015 		return (FILEBENCH_OK);
9935184Sek110237 
9945184Sek110237 	if (flowop->fo_initted == 0) {
9955184Sek110237 		filebench_log(LOG_DEBUG_IMPL, "rate %zx %s-%d locking",
9965184Sek110237 		    flowop, threadflow->tf_name, threadflow->tf_instance);
9975184Sek110237 		flowop->fo_initted = 1;
9986701Saw148015 
9996701Saw148015 		if (flowoplib_event_find_target(threadflow, flowop)
10006701Saw148015 		    == FILEBENCH_ERROR)
10016701Saw148015 			return (FILEBENCH_ERROR);
10026701Saw148015 
10036701Saw148015 		if ((flowop->fo_targets) &&
10046701Saw148015 		    ((flowop->fo_targets->fo_attrs &
10056701Saw148015 		    (FLOW_ATTR_READ | FLOW_ATTR_WRITE)) == 0)) {
10066701Saw148015 			filebench_log(LOG_ERROR,
10076701Saw148015 			    "WARNING: Flowop %s does no Reads or Writes",
10086701Saw148015 			    flowop->fo_targets->fo_name);
10096701Saw148015 			filebench_shutdown(1);
10106701Saw148015 			return (FILEBENCH_ERROR);
10116701Saw148015 		}
10125184Sek110237 	}
10135184Sek110237 
10146701Saw148015 	if (flowop->fo_targets) {
10156701Saw148015 		/*
10166701Saw148015 		 * Note that fs_bytes is already the sum of fs_rbytes
10176701Saw148015 		 * and fs_wbytes if looking at a single flowop.
10186701Saw148015 		 */
10196701Saw148015 		bytes = flowop->fo_targets->fo_stats.fs_bytes;
10206701Saw148015 	} else {
10216701Saw148015 		(void) ipc_mutex_lock(&controlstats_lock);
10226701Saw148015 		bytes = (controlstats.fs_rbytes +
10236701Saw148015 		    controlstats.fs_wbytes);
10246701Saw148015 		(void) ipc_mutex_unlock(&controlstats_lock);
10256701Saw148015 	}
10266701Saw148015 
10276701Saw148015 	/* Is this the first time around? */
10285184Sek110237 	if (flowop->fo_tputlast == 0) {
10295184Sek110237 		flowop->fo_tputlast = bytes;
10306084Saw148015 		return (FILEBENCH_OK);
10315184Sek110237 	}
10325184Sek110237 
10335184Sek110237 	delta = bytes - flowop->fo_tputlast;
10345184Sek110237 	flowop->fo_tputbucket -= delta;
10355184Sek110237 	flowop->fo_tputlast = bytes;
10365184Sek110237 
10375184Sek110237 	/* No need to block if the q isn't empty */
10385184Sek110237 	if (flowop->fo_tputbucket >= 0LL) {
10395673Saw148015 		flowop_endop(threadflow, flowop, 0);
10406084Saw148015 		return (FILEBENCH_OK);
10415184Sek110237 	}
10425184Sek110237 
10435184Sek110237 	bytes = flowop->fo_tputbucket * -1;
10445184Sek110237 	events = (bytes / MB) + 1;
10455184Sek110237 
10466286Saw148015 	filebench_log(LOG_DEBUG_IMPL, "%llu bytes, %llu events",
10476286Saw148015 	    (u_longlong_t)bytes, (u_longlong_t)events);
10485184Sek110237 
10495184Sek110237 	flowop_beginop(threadflow, flowop);
10507946SAndrew.W.Wilson@sun.com 	while (filebench_shm->shm_eventgen_hz != NULL) {
10516391Saw148015 		(void) ipc_mutex_lock(&filebench_shm->shm_eventgen_lock);
10526391Saw148015 		if (filebench_shm->shm_eventgen_q >= events) {
10536391Saw148015 			filebench_shm->shm_eventgen_q -= events;
10546391Saw148015 			(void) ipc_mutex_unlock(
10556391Saw148015 			    &filebench_shm->shm_eventgen_lock);
10565184Sek110237 			flowop->fo_tputbucket += (events * MB);
10575184Sek110237 			break;
10585184Sek110237 		}
10596391Saw148015 		(void) pthread_cond_wait(&filebench_shm->shm_eventgen_cv,
10606391Saw148015 		    &filebench_shm->shm_eventgen_lock);
10616391Saw148015 		(void) ipc_mutex_unlock(&filebench_shm->shm_eventgen_lock);
10625184Sek110237 	}
10635673Saw148015 	flowop_endop(threadflow, flowop, 0);
10645184Sek110237 
10656084Saw148015 	return (FILEBENCH_OK);
10665184Sek110237 }
10675184Sek110237 
10685184Sek110237 /*
10695184Sek110237  * These flowops terminate a benchmark run when either the specified
10705184Sek110237  * number of bytes of I/O (flowoplib_finishonbytes) or the specified
10715184Sek110237  * number of I/O operations (flowoplib_finishoncount) have been generated.
10725184Sek110237  */
10735184Sek110237 
10745184Sek110237 
10755184Sek110237 /*
10765184Sek110237  * Stop filebench run when specified number of I/O bytes have been
10776212Saw148015  * transferred. Compares controlstats.fs_bytes with flowop->value,
10785184Sek110237  * and if greater returns 1, stopping the run, if not, returns 0
10795184Sek110237  * to continue running.
10805184Sek110237  */
10815184Sek110237 static int
10825184Sek110237 flowoplib_finishonbytes(threadflow_t *threadflow, flowop_t *flowop)
10835184Sek110237 {
10846701Saw148015 	uint64_t bytes_io;		/* Bytes of I/O delivered so far */
10856701Saw148015 	uint64_t byte_lim = flowop->fo_constvalue;  /* Total Bytes desired */
10866701Saw148015 						    /* Uses constant value */
10876701Saw148015 
10886701Saw148015 	if (flowop->fo_initted == 0) {
10896701Saw148015 		filebench_log(LOG_DEBUG_IMPL, "rate %zx %s-%d locking",
10906701Saw148015 		    flowop, threadflow->tf_name, threadflow->tf_instance);
10916701Saw148015 		flowop->fo_initted = 1;
10926701Saw148015 
10936701Saw148015 		if (flowoplib_event_find_target(threadflow, flowop)
10946701Saw148015 		    == FILEBENCH_ERROR)
10956701Saw148015 			return (FILEBENCH_ERROR);
10966701Saw148015 
10976701Saw148015 		if ((flowop->fo_targets) &&
10986701Saw148015 		    ((flowop->fo_targets->fo_attrs &
10996701Saw148015 		    (FLOW_ATTR_READ | FLOW_ATTR_WRITE)) == 0)) {
11006701Saw148015 			filebench_log(LOG_ERROR,
11016701Saw148015 			    "WARNING: Flowop %s does no Reads or Writes",
11026701Saw148015 			    flowop->fo_targets->fo_name);
11036701Saw148015 			filebench_shutdown(1);
11046701Saw148015 			return (FILEBENCH_ERROR);
11056701Saw148015 		}
11066701Saw148015 	}
11076701Saw148015 
11086701Saw148015 	if (flowop->fo_targets) {
11096701Saw148015 		bytes_io = flowop->fo_targets->fo_stats.fs_bytes;
11106701Saw148015 	} else {
11116701Saw148015 		(void) ipc_mutex_lock(&controlstats_lock);
11126701Saw148015 		bytes_io = controlstats.fs_bytes;
11136701Saw148015 		(void) ipc_mutex_unlock(&controlstats_lock);
11146701Saw148015 	}
11155184Sek110237 
11165184Sek110237 	flowop_beginop(threadflow, flowop);
11176701Saw148015 	if (bytes_io > byte_lim) {
11185673Saw148015 		flowop_endop(threadflow, flowop, 0);
11196084Saw148015 		return (FILEBENCH_DONE);
11205184Sek110237 	}
11215673Saw148015 	flowop_endop(threadflow, flowop, 0);
11225184Sek110237 
11236084Saw148015 	return (FILEBENCH_OK);
11245184Sek110237 }
11255184Sek110237 
11265184Sek110237 /*
11275184Sek110237  * Stop filebench run when specified number of I/O operations have
11285184Sek110237  * been performed. Compares controlstats.fs_count with *flowop->value,
11296084Saw148015  * and if greater returns 1, stopping the run, if not, returns FILEBENCH_OK
11306084Saw148015  * to continue running.
11315184Sek110237  */
11325184Sek110237 static int
11335184Sek110237 flowoplib_finishoncount(threadflow_t *threadflow, flowop_t *flowop)
11345184Sek110237 {
11355184Sek110237 	uint64_t ops;
11366212Saw148015 	uint64_t count = flowop->fo_constvalue; /* use constant value */
11375184Sek110237 
11386701Saw148015 	if (flowop->fo_initted == 0) {
11396701Saw148015 		filebench_log(LOG_DEBUG_IMPL, "rate %zx %s-%d locking",
11406701Saw148015 		    flowop, threadflow->tf_name, threadflow->tf_instance);
11416701Saw148015 		flowop->fo_initted = 1;
11426701Saw148015 
11436701Saw148015 		if (flowoplib_event_find_target(threadflow, flowop)
11446701Saw148015 		    == FILEBENCH_ERROR)
11456701Saw148015 			return (FILEBENCH_ERROR);
11466701Saw148015 	}
11476701Saw148015 
11486701Saw148015 	if (flowop->fo_targets) {
11496701Saw148015 		ops = flowop->fo_targets->fo_stats.fs_count;
11506701Saw148015 	} else {
11516701Saw148015 		(void) ipc_mutex_lock(&controlstats_lock);
11526701Saw148015 		ops = controlstats.fs_count;
11536701Saw148015 		(void) ipc_mutex_unlock(&controlstats_lock);
11546701Saw148015 	}
11555184Sek110237 
11565184Sek110237 	flowop_beginop(threadflow, flowop);
11576084Saw148015 	if (ops >= count) {
11585673Saw148015 		flowop_endop(threadflow, flowop, 0);
11596084Saw148015 		return (FILEBENCH_DONE);
11605184Sek110237 	}
11615673Saw148015 	flowop_endop(threadflow, flowop, 0);
11625184Sek110237 
11636084Saw148015 	return (FILEBENCH_OK);
11645184Sek110237 }
11655184Sek110237 
11665184Sek110237 /*
11675184Sek110237  * Semaphore synchronization using either System V semaphores or
11685184Sek110237  * posix semaphores. If System V semaphores are available, they will be
11695184Sek110237  * used, otherwise posix semaphores will be used.
11705184Sek110237  */
11715184Sek110237 
11725184Sek110237 
11735184Sek110237 /*
11745184Sek110237  * Initializes the filebench "block on semaphore" flowop.
11755184Sek110237  * If System V semaphores are implemented, the routine
11765184Sek110237  * initializes the System V semaphore subsystem if it hasn't
11775184Sek110237  * already been initialized, also allocates a pair of semids
11785184Sek110237  * and initializes the highwater System V semaphore.
11795184Sek110237  * If no System V semaphores, then does nothing special.
11806084Saw148015  * Returns FILEBENCH_ERROR if it cannot acquire a set of System V semphores
11816084Saw148015  * or if the initial post to the semaphore set fails. Returns FILEBENCH_OK
11825184Sek110237  * on success.
11835184Sek110237  */
11845184Sek110237 static int
11855184Sek110237 flowoplib_semblock_init(flowop_t *flowop)
11865184Sek110237 {
11875184Sek110237 
11885184Sek110237 #ifdef HAVE_SYSV_SEM
11896391Saw148015 	int sys_semid;
11905184Sek110237 	struct sembuf sbuf[2];
11915184Sek110237 	int highwater;
11925184Sek110237 
11935184Sek110237 	ipc_seminit();
11945184Sek110237 
11955184Sek110237 	flowop->fo_semid_lw = ipc_semidalloc();
11965184Sek110237 	flowop->fo_semid_hw = ipc_semidalloc();
11975184Sek110237 
11985184Sek110237 	filebench_log(LOG_DEBUG_IMPL, "flow %s-%d semblock init semid=%x",
11995184Sek110237 	    flowop->fo_name, flowop->fo_instance, flowop->fo_semid_lw);
12005184Sek110237 
12016391Saw148015 	sys_semid = filebench_shm->shm_sys_semid;
12025184Sek110237 
12035184Sek110237 	if ((highwater = flowop->fo_semid_hw) == 0)
12046212Saw148015 		highwater = flowop->fo_constvalue; /* use constant value */
12055184Sek110237 
12065184Sek110237 	filebench_log(LOG_DEBUG_IMPL, "setting highwater to : %d", highwater);
12075184Sek110237 
12085673Saw148015 	sbuf[0].sem_num = (short)highwater;
12096212Saw148015 	sbuf[0].sem_op = avd_get_int(flowop->fo_highwater);
12105184Sek110237 	sbuf[0].sem_flg = 0;
12116391Saw148015 	if ((semop(sys_semid, &sbuf[0], 1) == -1) && errno) {
12125184Sek110237 		filebench_log(LOG_ERROR, "semblock init post failed: %s (%d,"
12135184Sek110237 		    "%d)", strerror(errno), sbuf[0].sem_num, sbuf[0].sem_op);
12146084Saw148015 		return (FILEBENCH_ERROR);
12155184Sek110237 	}
12165184Sek110237 #else
12175184Sek110237 	filebench_log(LOG_DEBUG_IMPL,
12185184Sek110237 	    "flow %s-%d semblock init with posix semaphore",
12195184Sek110237 	    flowop->fo_name, flowop->fo_instance);
12205184Sek110237 
12215184Sek110237 	sem_init(&flowop->fo_sem, 1, 0);
12225184Sek110237 #endif	/* HAVE_SYSV_SEM */
12235184Sek110237 
12246212Saw148015 	if (!(avd_get_bool(flowop->fo_blocking)))
12255184Sek110237 		(void) ipc_mutex_unlock(&flowop->fo_lock);
12265184Sek110237 
12276084Saw148015 	return (FILEBENCH_OK);
12285184Sek110237 }
12295184Sek110237 
12305184Sek110237 /*
12315184Sek110237  * Releases the semids for the System V semaphore allocated
12325184Sek110237  * to this flowop. If not using System V semaphores, then
12336084Saw148015  * it is effectively just a no-op.
12345184Sek110237  */
12355184Sek110237 static void
12365184Sek110237 flowoplib_semblock_destruct(flowop_t *flowop)
12375184Sek110237 {
12385184Sek110237 #ifdef HAVE_SYSV_SEM
12395184Sek110237 	ipc_semidfree(flowop->fo_semid_lw);
12405184Sek110237 	ipc_semidfree(flowop->fo_semid_hw);
12415184Sek110237 #else
12425184Sek110237 	sem_destroy(&flowop->fo_sem);
12435184Sek110237 #endif /* HAVE_SYSV_SEM */
12445184Sek110237 }
12455184Sek110237 
12465184Sek110237 /*
12475184Sek110237  * Attempts to pass a System V or posix semaphore as appropriate,
12486084Saw148015  * and blocks if necessary. Returns FILEBENCH_ERROR if a set of System V
12495184Sek110237  * semphores is not available or cannot be acquired, or if the initial
12506084Saw148015  * post to the semaphore set fails. Returns FILEBENCH_OK on success.
12515184Sek110237  */
12525184Sek110237 static int
12535184Sek110237 flowoplib_semblock(threadflow_t *threadflow, flowop_t *flowop)
12545184Sek110237 {
12555184Sek110237 
12565184Sek110237 #ifdef HAVE_SYSV_SEM
12575184Sek110237 	struct sembuf sbuf[2];
12586212Saw148015 	int value = avd_get_int(flowop->fo_value);
12596391Saw148015 	int sys_semid;
12605184Sek110237 	struct timespec timeout;
12615184Sek110237 
12626391Saw148015 	sys_semid = filebench_shm->shm_sys_semid;
12635184Sek110237 
12645184Sek110237 	filebench_log(LOG_DEBUG_IMPL,
12655184Sek110237 	    "flow %s-%d sem blocking on id %x num %x value %d",
12666391Saw148015 	    flowop->fo_name, flowop->fo_instance, sys_semid,
12675184Sek110237 	    flowop->fo_semid_hw, value);
12685184Sek110237 
12695184Sek110237 	/* Post, decrement the increment the hw queue */
12705184Sek110237 	sbuf[0].sem_num = flowop->fo_semid_hw;
12715673Saw148015 	sbuf[0].sem_op = (short)value;
12725184Sek110237 	sbuf[0].sem_flg = 0;
12735184Sek110237 	sbuf[1].sem_num = flowop->fo_semid_lw;
12745184Sek110237 	sbuf[1].sem_op = value * -1;
12755184Sek110237 	sbuf[1].sem_flg = 0;
12765184Sek110237 	timeout.tv_sec = 600;
12775184Sek110237 	timeout.tv_nsec = 0;
12785184Sek110237 
12796212Saw148015 	if (avd_get_bool(flowop->fo_blocking))
12805184Sek110237 		(void) ipc_mutex_unlock(&flowop->fo_lock);
12815184Sek110237 
12825184Sek110237 	flowop_beginop(threadflow, flowop);
12835184Sek110237 
12845184Sek110237 #ifdef HAVE_SEMTIMEDOP
12856391Saw148015 	(void) semtimedop(sys_semid, &sbuf[0], 1, &timeout);
12866391Saw148015 	(void) semtimedop(sys_semid, &sbuf[1], 1, &timeout);
12875184Sek110237 #else
12886391Saw148015 	(void) semop(sys_semid, &sbuf[0], 1);
12896391Saw148015 	(void) semop(sys_semid, &sbuf[1], 1);
12905184Sek110237 #endif /* HAVE_SEMTIMEDOP */
12915184Sek110237 
12926212Saw148015 	if (avd_get_bool(flowop->fo_blocking))
12935184Sek110237 		(void) ipc_mutex_lock(&flowop->fo_lock);
12945184Sek110237 
12955673Saw148015 	flowop_endop(threadflow, flowop, 0);
12965184Sek110237 
12975184Sek110237 #else
12986212Saw148015 	int value = avd_get_int(flowop->fo_value);
12995184Sek110237 	int i;
13005184Sek110237 
13015184Sek110237 	filebench_log(LOG_DEBUG_IMPL,
13025184Sek110237 	    "flow %s-%d sem blocking on posix semaphore",
13035184Sek110237 	    flowop->fo_name, flowop->fo_instance);
13045184Sek110237 
13055184Sek110237 	/* Decrement sem by value */
13065184Sek110237 	for (i = 0; i < value; i++) {
13075184Sek110237 		if (sem_wait(&flowop->fo_sem) == -1) {
13085184Sek110237 			filebench_log(LOG_ERROR, "semop wait failed");
13096084Saw148015 			return (FILEBENCH_ERROR);
13105184Sek110237 		}
13115184Sek110237 	}
13125184Sek110237 
13135184Sek110237 	filebench_log(LOG_DEBUG_IMPL, "flow %s-%d sem unblocking",
13145184Sek110237 	    flowop->fo_name, flowop->fo_instance);
13155184Sek110237 #endif /* HAVE_SYSV_SEM */
13165184Sek110237 
13176084Saw148015 	return (FILEBENCH_OK);
13185184Sek110237 }
13195184Sek110237 
13205184Sek110237 /*
13216084Saw148015  * Calls ipc_seminit(). Always returns FILEBENCH_OK.
13225184Sek110237  */
13235184Sek110237 /* ARGSUSED */
13245184Sek110237 static int
13255184Sek110237 flowoplib_sempost_init(flowop_t *flowop)
13265184Sek110237 {
13275184Sek110237 #ifdef HAVE_SYSV_SEM
13285184Sek110237 	ipc_seminit();
13295184Sek110237 #endif /* HAVE_SYSV_SEM */
13306084Saw148015 	return (FILEBENCH_OK);
13315184Sek110237 }
13325184Sek110237 
13335184Sek110237 /*
13345184Sek110237  * Post to a System V or posix semaphore as appropriate.
13355184Sek110237  * On the first call for a given flowop instance, this routine
13365184Sek110237  * will use the fo_targetname attribute to locate all semblock
13375184Sek110237  * flowops that are expecting posts from this flowop. All
13385184Sek110237  * target flowops on this list will have a post operation done
13395184Sek110237  * to their semaphores on each call.
13405184Sek110237  */
13415184Sek110237 static int
13425184Sek110237 flowoplib_sempost(threadflow_t *threadflow, flowop_t *flowop)
13435184Sek110237 {
13445184Sek110237 	flowop_t *target;
13455184Sek110237 
13465184Sek110237 	filebench_log(LOG_DEBUG_IMPL,
13475184Sek110237 	    "sempost flow %s-%d",
13485184Sek110237 	    flowop->fo_name,
13495184Sek110237 	    flowop->fo_instance);
13505184Sek110237 
13515184Sek110237 	/* if this is the first post, create the post list */
13525184Sek110237 	if (flowop->fo_targets == NULL) {
13535184Sek110237 		flowop_t *result = flowop_find(flowop->fo_targetname);
13545184Sek110237 
13555184Sek110237 		flowop->fo_targets = result;
13565184Sek110237 
13575184Sek110237 		if (result == NULL) {
13585184Sek110237 			filebench_log(LOG_ERROR,
13595184Sek110237 			    "sempost: could not find op %s for thread %s",
13605184Sek110237 			    flowop->fo_targetname,
13615184Sek110237 			    threadflow->tf_name);
13625184Sek110237 			filebench_shutdown(1);
13635184Sek110237 		}
13645184Sek110237 
13655184Sek110237 		while (result) {
13665184Sek110237 			result->fo_targetnext =
13675184Sek110237 			    result->fo_resultnext;
13685184Sek110237 			result = result->fo_resultnext;
13695184Sek110237 		}
13705184Sek110237 	}
13715184Sek110237 
13725184Sek110237 	target = flowop->fo_targets;
13735184Sek110237 
13745184Sek110237 	flowop_beginop(threadflow, flowop);
13755184Sek110237 	/* post to the targets */
13765184Sek110237 	while (target) {
13775184Sek110237 #ifdef HAVE_SYSV_SEM
13785184Sek110237 		struct sembuf sbuf[2];
13796391Saw148015 		int sys_semid;
13805184Sek110237 		int blocking;
13815184Sek110237 #else
13825184Sek110237 		int i;
13835184Sek110237 #endif /* HAVE_SYSV_SEM */
13845184Sek110237 		struct timespec timeout;
13856550Saw148015 		int value = (int)avd_get_int(flowop->fo_value);
13865184Sek110237 
13875184Sek110237 		if (target->fo_instance == FLOW_MASTER) {
13885184Sek110237 			target = target->fo_targetnext;
13895184Sek110237 			continue;
13905184Sek110237 		}
13915184Sek110237 
13925184Sek110237 #ifdef HAVE_SYSV_SEM
13935184Sek110237 
13945184Sek110237 		filebench_log(LOG_DEBUG_IMPL,
13955184Sek110237 		    "sempost flow %s-%d num %x",
13965184Sek110237 		    target->fo_name,
13975184Sek110237 		    target->fo_instance,
13985184Sek110237 		    target->fo_semid_lw);
13995184Sek110237 
14006391Saw148015 		sys_semid = filebench_shm->shm_sys_semid;
14015184Sek110237 		sbuf[0].sem_num = target->fo_semid_lw;
14025673Saw148015 		sbuf[0].sem_op = (short)value;
14035184Sek110237 		sbuf[0].sem_flg = 0;
14045184Sek110237 		sbuf[1].sem_num = target->fo_semid_hw;
14055184Sek110237 		sbuf[1].sem_op = value * -1;
14065184Sek110237 		sbuf[1].sem_flg = 0;
14075184Sek110237 		timeout.tv_sec = 600;
14085184Sek110237 		timeout.tv_nsec = 0;
14095184Sek110237 
14106212Saw148015 		if (avd_get_bool(flowop->fo_blocking))
14115184Sek110237 			blocking = 1;
14125184Sek110237 		else
14135184Sek110237 			blocking = 0;
14145184Sek110237 
14155184Sek110237 #ifdef HAVE_SEMTIMEDOP
14166391Saw148015 		if ((semtimedop(sys_semid, &sbuf[0], blocking + 1,
14175184Sek110237 		    &timeout) == -1) && (errno && (errno != EAGAIN))) {
14185184Sek110237 #else
14196391Saw148015 		if ((semop(sys_semid, &sbuf[0], blocking + 1) == -1) &&
14205184Sek110237 		    (errno && (errno != EAGAIN))) {
14215184Sek110237 #endif /* HAVE_SEMTIMEDOP */
14225184Sek110237 			filebench_log(LOG_ERROR, "semop post failed: %s",
14235184Sek110237 			    strerror(errno));
14246084Saw148015 			return (FILEBENCH_ERROR);
14255184Sek110237 		}
14265184Sek110237 
14275184Sek110237 		filebench_log(LOG_DEBUG_IMPL,
14285184Sek110237 		    "flow %s-%d finished posting",
14295184Sek110237 		    target->fo_name, target->fo_instance);
14305184Sek110237 #else
14315184Sek110237 		filebench_log(LOG_DEBUG_IMPL,
14325184Sek110237 		    "sempost flow %s-%d to posix semaphore",
14335184Sek110237 		    target->fo_name,
14345184Sek110237 		    target->fo_instance);
14355184Sek110237 
14365184Sek110237 		/* Increment sem by value */
14375184Sek110237 		for (i = 0; i < value; i++) {
14385184Sek110237 			if (sem_post(&target->fo_sem) == -1) {
14395184Sek110237 				filebench_log(LOG_ERROR, "semop post failed");
14406084Saw148015 				return (FILEBENCH_ERROR);
14415184Sek110237 			}
14425184Sek110237 		}
14435184Sek110237 
14445184Sek110237 		filebench_log(LOG_DEBUG_IMPL, "flow %s-%d unblocking",
14455184Sek110237 		    target->fo_name, target->fo_instance);
14465184Sek110237 #endif /* HAVE_SYSV_SEM */
14475184Sek110237 
14485184Sek110237 		target = target->fo_targetnext;
14495184Sek110237 	}
14505673Saw148015 	flowop_endop(threadflow, flowop, 0);
14515184Sek110237 
14526084Saw148015 	return (FILEBENCH_OK);
14535184Sek110237 }
14545184Sek110237 
14555184Sek110237 
14565184Sek110237 /*
14575184Sek110237  * Section for exercising create / open / close / delete operations
14585184Sek110237  * on files within a fileset. For proper operation, the flowop attribute
14595184Sek110237  * "fd", which sets the fo_fdnumber field in the flowop, must be used
14605184Sek110237  * so that the same file is opened and later closed. "fd" is an index
14615184Sek110237  * into a pair of arrays maintained by threadflows, one of which
14625184Sek110237  * contains the operating system assigned file descriptors and the other
14635184Sek110237  * a pointer to the filesetentry whose file the file descriptor
14645184Sek110237  * references. An openfile flowop defined without fd being set will use
14655184Sek110237  * the default (0) fd or, if specified, rotate through fd indices, but
14665184Sek110237  * createfile and closefile must use the default or a specified fd.
14675184Sek110237  * Meanwhile deletefile picks and arbitrary file to delete, regardless
14685184Sek110237  * of fd attribute.
14695184Sek110237  */
14705184Sek110237 
14715184Sek110237 /*
14725184Sek110237  * Emulates (and actually does) file open. Obtains a file descriptor
14736084Saw148015  * index, then calls flowoplib_openfile_common() to open. Returns
14746084Saw148015  * FILEBENCH_ERROR if no file descriptor is found, and returns the
14756084Saw148015  * status from flowoplib_openfile_common otherwise (FILEBENCH_ERROR,
14766084Saw148015  * FILEBENCH_NORSC, FILEBENCH_OK).
14775184Sek110237  */
14785184Sek110237 static int
14795184Sek110237 flowoplib_openfile(threadflow_t *threadflow, flowop_t *flowop)
14805184Sek110237 {
14815184Sek110237 	int fd = flowoplib_fdnum(threadflow, flowop);
14825184Sek110237 
14835184Sek110237 	if (fd == -1)
14846084Saw148015 		return (FILEBENCH_ERROR);
14855184Sek110237 
14865184Sek110237 	return (flowoplib_openfile_common(threadflow, flowop, fd));
14875184Sek110237 }
14885184Sek110237 
14895184Sek110237 /*
14905184Sek110237  * Common file opening code for filesets. Uses the supplied
14915184Sek110237  * file descriptor index to determine the tf_fd entry to use.
14925184Sek110237  * If the entry is empty (0) and the fileset exists, fileset
14935184Sek110237  * pick is called to select a fileset entry to use. The file
14945184Sek110237  * specified in the filesetentry is opened, and the returned
14955184Sek110237  * operating system file descriptor and a pointer to the
14965184Sek110237  * filesetentry are stored in tf_fd[fd] and tf_fse[fd],
14976084Saw148015  * respectively. Returns FILEBENCH_ERROR on error,
14986084Saw148015  * FILEBENCH_NORSC if no suitable filesetentry can be found,
14996084Saw148015  * and FILEBENCH_OK on success.
15005184Sek110237  */
15015184Sek110237 static int
15025184Sek110237 flowoplib_openfile_common(threadflow_t *threadflow, flowop_t *flowop, int fd)
15035184Sek110237 {
15045184Sek110237 	filesetentry_t *file;
15056212Saw148015 	char *fileset_name;
15065184Sek110237 	int tid = 0;
1507*9326SAndrew.W.Wilson@sun.com 	int openflag = 0;
15088404SAndrew.W.Wilson@sun.com 	int err;
15095184Sek110237 
15106391Saw148015 	if (flowop->fo_fileset == NULL) {
15116391Saw148015 		filebench_log(LOG_ERROR, "flowop NULL file");
15126391Saw148015 		return (FILEBENCH_ERROR);
15136391Saw148015 	}
15146391Saw148015 
15156212Saw148015 	if ((fileset_name =
15166212Saw148015 	    avd_get_str(flowop->fo_fileset->fs_name)) == NULL) {
15176212Saw148015 		filebench_log(LOG_ERROR,
15186212Saw148015 		    "flowop %s: fileset has no name", flowop->fo_name);
15196212Saw148015 		return (FILEBENCH_ERROR);
15206212Saw148015 	}
15216212Saw148015 
15225184Sek110237 	/*
1523*9326SAndrew.W.Wilson@sun.com 	 * set the open flag for read only or read/write, as appropriate.
1524*9326SAndrew.W.Wilson@sun.com 	 */
1525*9326SAndrew.W.Wilson@sun.com 	if (avd_get_bool(flowop->fo_fileset->fs_readonly) == TRUE)
1526*9326SAndrew.W.Wilson@sun.com 		openflag = O_RDONLY;
1527*9326SAndrew.W.Wilson@sun.com 	else
1528*9326SAndrew.W.Wilson@sun.com 		openflag = O_RDWR;
1529*9326SAndrew.W.Wilson@sun.com 
1530*9326SAndrew.W.Wilson@sun.com 	/*
15315184Sek110237 	 * If the flowop doesn't default to persistent fd
15325184Sek110237 	 * then get unique thread ID for use by fileset_pick
15335184Sek110237 	 */
15346212Saw148015 	if (avd_get_bool(flowop->fo_rotatefd))
15355184Sek110237 		tid = threadflow->tf_utid;
15365184Sek110237 
15378615SAndrew.W.Wilson@sun.com 	if (threadflow->tf_fd[fd].fd_ptr != NULL) {
15385184Sek110237 		filebench_log(LOG_ERROR,
15395184Sek110237 		    "flowop %s attempted to open without closing on fd %d",
15405184Sek110237 		    flowop->fo_name, fd);
15416084Saw148015 		return (FILEBENCH_ERROR);
15425184Sek110237 	}
15435184Sek110237 
15445673Saw148015 #ifdef HAVE_RAW_SUPPORT
15455673Saw148015 	if (flowop->fo_fileset->fs_attrs & FILESET_IS_RAW_DEV) {
15465673Saw148015 		int open_attrs = 0;
15475673Saw148015 		char name[MAXPATHLEN];
15485673Saw148015 
15497946SAndrew.W.Wilson@sun.com 		(void) fb_strlcpy(name,
15507946SAndrew.W.Wilson@sun.com 		    avd_get_str(flowop->fo_fileset->fs_path), MAXPATHLEN);
15517946SAndrew.W.Wilson@sun.com 		(void) fb_strlcat(name, "/", MAXPATHLEN);
15527946SAndrew.W.Wilson@sun.com 		(void) fb_strlcat(name, fileset_name, MAXPATHLEN);
15535673Saw148015 
15546212Saw148015 		if (avd_get_bool(flowop->fo_dsync)) {
15555673Saw148015 #ifdef sun
15565673Saw148015 			open_attrs |= O_DSYNC;
15575673Saw148015 #else
15585673Saw148015 			open_attrs |= O_FSYNC;
15595673Saw148015 #endif
15605673Saw148015 		}
15615673Saw148015 
15625673Saw148015 		filebench_log(LOG_DEBUG_SCRIPT,
15635673Saw148015 		    "open raw device %s flags %d = %d", name, open_attrs, fd);
15645673Saw148015 
15658615SAndrew.W.Wilson@sun.com 		if (FB_OPEN(&(threadflow->tf_fd[fd]), name,
1566*9326SAndrew.W.Wilson@sun.com 		    openflag | open_attrs, 0666) == FILEBENCH_ERROR) {
15675673Saw148015 			filebench_log(LOG_ERROR,
15685673Saw148015 			    "Failed to open raw device %s: %s",
15695673Saw148015 			    name, strerror(errno));
15706084Saw148015 			return (FILEBENCH_ERROR);
15715673Saw148015 		}
15725673Saw148015 
15735673Saw148015 		/* if running on Solaris, use un-buffered io */
15745673Saw148015 #ifdef sun
15758615SAndrew.W.Wilson@sun.com 		(void) directio(threadflow->tf_fd[fd].fd_num, DIRECTIO_ON);
15765673Saw148015 #endif
15775673Saw148015 
15785673Saw148015 		threadflow->tf_fse[fd] = NULL;
15795673Saw148015 
15806084Saw148015 		return (FILEBENCH_OK);
15815673Saw148015 	}
15825673Saw148015 #endif /* HAVE_RAW_SUPPORT */
15835673Saw148015 
15848404SAndrew.W.Wilson@sun.com 	if ((err = flowoplib_pickfile(&file, flowop,
15858404SAndrew.W.Wilson@sun.com 	    FILESET_PICKEXISTS, tid)) != FILEBENCH_OK) {
15866084Saw148015 		filebench_log(LOG_DEBUG_SCRIPT,
15875184Sek110237 		    "flowop %s failed to pick file from %s on fd %d",
15886212Saw148015 		    flowop->fo_name, fileset_name, fd);
15898404SAndrew.W.Wilson@sun.com 		return (err);
15905184Sek110237 	}
15915184Sek110237 
15925184Sek110237 	threadflow->tf_fse[fd] = file;
15935184Sek110237 
15945184Sek110237 	flowop_beginop(threadflow, flowop);
15958615SAndrew.W.Wilson@sun.com 	err = fileset_openfile(&threadflow->tf_fd[fd], flowop->fo_fileset,
1596*9326SAndrew.W.Wilson@sun.com 	    file, openflag, 0666, flowoplib_fileattrs(flowop));
15975673Saw148015 	flowop_endop(threadflow, flowop, 0);
15985184Sek110237 
15998615SAndrew.W.Wilson@sun.com 	if (err == FILEBENCH_ERROR) {
16006212Saw148015 		filebench_log(LOG_ERROR, "flowop %s failed to open file %s",
16016212Saw148015 		    flowop->fo_name, file->fse_path);
16026084Saw148015 		return (FILEBENCH_ERROR);
16035184Sek110237 	}
16045184Sek110237 
16055184Sek110237 	filebench_log(LOG_DEBUG_SCRIPT,
16065184Sek110237 	    "flowop %s: opened %s fd[%d] = %d",
16075184Sek110237 	    flowop->fo_name, file->fse_path, fd, threadflow->tf_fd[fd]);
16085184Sek110237 
16096084Saw148015 	return (FILEBENCH_OK);
16105184Sek110237 }
16115184Sek110237 
16125184Sek110237 /*
16135184Sek110237  * Emulate create of a file. Uses the flowop's fdnumber to select
16145184Sek110237  * tf_fd and tf_fse array locations to put the created file's file
16158404SAndrew.W.Wilson@sun.com  * descriptor and filesetentry respectively. Uses flowoplib_pickfile()
16165184Sek110237  * to select a specific filesetentry whose file does not currently
16175184Sek110237  * exist for the file create operation. Then calls
16185184Sek110237  * fileset_openfile() with the O_CREATE flag set to create the
16196084Saw148015  * file. Returns FILEBENCH_ERROR if the array index specified by fdnumber is
16205184Sek110237  * already in use, the flowop has no associated fileset, or
16215184Sek110237  * the create call fails. Returns 1 if a filesetentry with a
16226084Saw148015  * nonexistent file cannot be found. Returns FILEBENCH_OK on success.
16235184Sek110237  */
16245184Sek110237 static int
16255184Sek110237 flowoplib_createfile(threadflow_t *threadflow, flowop_t *flowop)
16265184Sek110237 {
16275184Sek110237 	filesetentry_t *file;
16285184Sek110237 	int fd = flowop->fo_fdnumber;
16298404SAndrew.W.Wilson@sun.com 	int err;
16305184Sek110237 
16318615SAndrew.W.Wilson@sun.com 	if (threadflow->tf_fd[fd].fd_ptr != NULL) {
16325184Sek110237 		filebench_log(LOG_ERROR,
16335184Sek110237 		    "flowop %s attempted to create without closing on fd %d",
16345184Sek110237 		    flowop->fo_name, fd);
16356084Saw148015 		return (FILEBENCH_ERROR);
16365184Sek110237 	}
16375184Sek110237 
16385184Sek110237 	if (flowop->fo_fileset == NULL) {
16395184Sek110237 		filebench_log(LOG_ERROR, "flowop NULL file");
16406084Saw148015 		return (FILEBENCH_ERROR);
16415184Sek110237 	}
16425184Sek110237 
1643*9326SAndrew.W.Wilson@sun.com 	if (avd_get_bool(flowop->fo_fileset->fs_readonly) == TRUE) {
1644*9326SAndrew.W.Wilson@sun.com 		filebench_log(LOG_ERROR, "Can not CREATE the READONLY file %s",
1645*9326SAndrew.W.Wilson@sun.com 		    avd_get_str(flowop->fo_fileset->fs_name));
1646*9326SAndrew.W.Wilson@sun.com 		return (FILEBENCH_ERROR);
1647*9326SAndrew.W.Wilson@sun.com 	}
1648*9326SAndrew.W.Wilson@sun.com 
1649*9326SAndrew.W.Wilson@sun.com 
16505673Saw148015 #ifdef HAVE_RAW_SUPPORT
16515673Saw148015 	/* can't be used with raw devices */
16525673Saw148015 	if (flowop->fo_fileset->fs_attrs & FILESET_IS_RAW_DEV) {
16535673Saw148015 		filebench_log(LOG_ERROR,
16545673Saw148015 		    "flowop %s attempted to a createfile on RAW device",
16555673Saw148015 		    flowop->fo_name);
16566084Saw148015 		return (FILEBENCH_ERROR);
16575673Saw148015 	}
16585673Saw148015 #endif /* HAVE_RAW_SUPPORT */
16595673Saw148015 
16608404SAndrew.W.Wilson@sun.com 	if ((err = flowoplib_pickfile(&file, flowop,
16618404SAndrew.W.Wilson@sun.com 	    FILESET_PICKNOEXIST, 0)) != FILEBENCH_OK) {
16626084Saw148015 		filebench_log(LOG_DEBUG_SCRIPT,
16636084Saw148015 		    "flowop %s failed to pick file from fileset %s",
16646212Saw148015 		    flowop->fo_name,
16656212Saw148015 		    avd_get_str(flowop->fo_fileset->fs_name));
16668404SAndrew.W.Wilson@sun.com 		return (err);
16675184Sek110237 	}
16685184Sek110237 
16695184Sek110237 	threadflow->tf_fse[fd] = file;
16705184Sek110237 
16715184Sek110237 	flowop_beginop(threadflow, flowop);
16728615SAndrew.W.Wilson@sun.com 	err = fileset_openfile(&threadflow->tf_fd[fd], flowop->fo_fileset,
16735184Sek110237 	    file, O_RDWR | O_CREAT, 0666, flowoplib_fileattrs(flowop));
16745673Saw148015 	flowop_endop(threadflow, flowop, 0);
16755184Sek110237 
16768615SAndrew.W.Wilson@sun.com 	if (err == FILEBENCH_ERROR) {
16775184Sek110237 		filebench_log(LOG_ERROR, "failed to create file %s",
16785184Sek110237 		    flowop->fo_name);
16796084Saw148015 		return (FILEBENCH_ERROR);
16805184Sek110237 	}
16815184Sek110237 
16825184Sek110237 	filebench_log(LOG_DEBUG_SCRIPT,
16835184Sek110237 	    "flowop %s: created %s fd[%d] = %d",
16845184Sek110237 	    flowop->fo_name, file->fse_path, fd, threadflow->tf_fd[fd]);
16855184Sek110237 
16866084Saw148015 	return (FILEBENCH_OK);
16875184Sek110237 }
16885184Sek110237 
16895184Sek110237 /*
16906391Saw148015  * Emulates delete of a file. If a valid fd is provided, it uses the
16916391Saw148015  * filesetentry stored at that fd location to select the file to be
16926391Saw148015  * deleted, otherwise it picks an arbitrary filesetentry
16936391Saw148015  * whose file exists. It then uses unlink() to delete it and Clears
16946084Saw148015  * the FSE_EXISTS flag for the filesetentry. Returns FILEBENCH_ERROR if the
16956084Saw148015  * flowop has no associated fileset. Returns FILEBENCH_NORSC if an appropriate
16966084Saw148015  * filesetentry cannot be found, and FILEBENCH_OK on success.
16975184Sek110237  */
16985184Sek110237 static int
16995184Sek110237 flowoplib_deletefile(threadflow_t *threadflow, flowop_t *flowop)
17005184Sek110237 {
17015184Sek110237 	filesetentry_t *file;
17025184Sek110237 	fileset_t *fileset;
17035184Sek110237 	char path[MAXPATHLEN];
17045184Sek110237 	char *pathtmp;
17056391Saw148015 	int fd = flowop->fo_fdnumber;
17065184Sek110237 
17076391Saw148015 	/* if fd specified, use it to access file */
17086391Saw148015 	if ((fd > 0) && ((file = threadflow->tf_fse[fd]) != NULL)) {
17096391Saw148015 
17106391Saw148015 		/* indicate that the file will be deleted */
17116391Saw148015 		threadflow->tf_fse[fd] = NULL;
17126391Saw148015 
17136391Saw148015 		/* if here, we still have a valid file pointer */
17146391Saw148015 		fileset = file->fse_fileset;
17156391Saw148015 	} else {
17168404SAndrew.W.Wilson@sun.com 
17176391Saw148015 		/* Otherwise, pick arbitrary file */
17186391Saw148015 		file = NULL;
17196391Saw148015 		fileset = flowop->fo_fileset;
17206391Saw148015 	}
17216391Saw148015 
17226391Saw148015 
17236391Saw148015 	if (fileset == NULL) {
17245184Sek110237 		filebench_log(LOG_ERROR, "flowop NULL file");
17256084Saw148015 		return (FILEBENCH_ERROR);
17265184Sek110237 	}
17275184Sek110237 
17285673Saw148015 #ifdef HAVE_RAW_SUPPORT
17295673Saw148015 	/* can't be used with raw devices */
17306391Saw148015 	if (fileset->fs_attrs & FILESET_IS_RAW_DEV) {
17315673Saw148015 		filebench_log(LOG_ERROR,
17325673Saw148015 		    "flowop %s attempted a deletefile on RAW device",
17335673Saw148015 		    flowop->fo_name);
17346084Saw148015 		return (FILEBENCH_ERROR);
17355673Saw148015 	}
17365673Saw148015 #endif /* HAVE_RAW_SUPPORT */
17375673Saw148015 
17386391Saw148015 	if (file == NULL) {
17398404SAndrew.W.Wilson@sun.com 		int err;
17408404SAndrew.W.Wilson@sun.com 
17417556SAndrew.W.Wilson@sun.com 		/* pick arbitrary, existing (allocated) file */
17428404SAndrew.W.Wilson@sun.com 		if ((err = flowoplib_pickfile(&file, flowop,
17438404SAndrew.W.Wilson@sun.com 		    FILESET_PICKEXISTS, 0)) != FILEBENCH_OK) {
17446391Saw148015 			filebench_log(LOG_DEBUG_SCRIPT,
17456391Saw148015 			    "flowop %s failed to pick file", flowop->fo_name);
17468404SAndrew.W.Wilson@sun.com 			return (err);
17476391Saw148015 		}
17486391Saw148015 	} else {
17497556SAndrew.W.Wilson@sun.com 		/* delete specific file. wait for it to be non-busy */
17507556SAndrew.W.Wilson@sun.com 		(void) ipc_mutex_lock(&fileset->fs_pick_lock);
17517556SAndrew.W.Wilson@sun.com 		while (file->fse_flags & FSE_BUSY) {
17527556SAndrew.W.Wilson@sun.com 			file->fse_flags |= FSE_THRD_WAITNG;
17537556SAndrew.W.Wilson@sun.com 			(void) pthread_cond_wait(&fileset->fs_thrd_wait_cv,
17547556SAndrew.W.Wilson@sun.com 			    &fileset->fs_pick_lock);
17557556SAndrew.W.Wilson@sun.com 		}
17567556SAndrew.W.Wilson@sun.com 
17577556SAndrew.W.Wilson@sun.com 		/* File now available, grab it for deletion */
17587556SAndrew.W.Wilson@sun.com 		file->fse_flags |= FSE_BUSY;
17597556SAndrew.W.Wilson@sun.com 		fileset->fs_idle_files--;
17607556SAndrew.W.Wilson@sun.com 		(void) ipc_mutex_unlock(&fileset->fs_pick_lock);
17615184Sek110237 	}
17625184Sek110237 
17638404SAndrew.W.Wilson@sun.com 	/* don't delete if anyone (other than me) has file open */
17648615SAndrew.W.Wilson@sun.com 	if ((fd > 0) && (threadflow->tf_fd[fd].fd_num > 0)) {
17658404SAndrew.W.Wilson@sun.com 		if (file->fse_open_cnt > 1) {
17668404SAndrew.W.Wilson@sun.com 			filebench_log(LOG_DEBUG_SCRIPT,
17678404SAndrew.W.Wilson@sun.com 			    "flowop %s can't delete file opened by other"
17688404SAndrew.W.Wilson@sun.com 			    " threads at fd = %d", flowop->fo_name, fd);
17698404SAndrew.W.Wilson@sun.com 			fileset_unbusy(file, FALSE, FALSE, 0);
17708404SAndrew.W.Wilson@sun.com 			return (FILEBENCH_OK);
17718404SAndrew.W.Wilson@sun.com 		} else {
17728404SAndrew.W.Wilson@sun.com 			filebench_log(LOG_DEBUG_SCRIPT,
17738404SAndrew.W.Wilson@sun.com 			    "flowop %s deleting still open file at fd = %d",
17748404SAndrew.W.Wilson@sun.com 			    flowop->fo_name, fd);
17758404SAndrew.W.Wilson@sun.com 		}
17768404SAndrew.W.Wilson@sun.com 	} else if (file->fse_open_cnt > 0) {
17778404SAndrew.W.Wilson@sun.com 		filebench_log(LOG_DEBUG_SCRIPT,
17788404SAndrew.W.Wilson@sun.com 		    "flowop %s can't delete file opened by other"
17798404SAndrew.W.Wilson@sun.com 		    " threads at fd = %d, open count = %d",
17808404SAndrew.W.Wilson@sun.com 		    flowop->fo_name, fd, file->fse_open_cnt);
17818404SAndrew.W.Wilson@sun.com 		fileset_unbusy(file, FALSE, FALSE, 0);
17828404SAndrew.W.Wilson@sun.com 		return (FILEBENCH_OK);
17838404SAndrew.W.Wilson@sun.com 	}
17848404SAndrew.W.Wilson@sun.com 
17857946SAndrew.W.Wilson@sun.com 	(void) fb_strlcpy(path, avd_get_str(fileset->fs_path), MAXPATHLEN);
17867946SAndrew.W.Wilson@sun.com 	(void) fb_strlcat(path, "/", MAXPATHLEN);
17877946SAndrew.W.Wilson@sun.com 	(void) fb_strlcat(path, avd_get_str(fileset->fs_name), MAXPATHLEN);
17885184Sek110237 	pathtmp = fileset_resolvepath(file);
17897946SAndrew.W.Wilson@sun.com 	(void) fb_strlcat(path, pathtmp, MAXPATHLEN);
17905184Sek110237 	free(pathtmp);
17915184Sek110237 
17927556SAndrew.W.Wilson@sun.com 	/* delete the selected file */
17935184Sek110237 	flowop_beginop(threadflow, flowop);
17948615SAndrew.W.Wilson@sun.com 	(void) FB_UNLINK(path);
17955673Saw148015 	flowop_endop(threadflow, flowop, 0);
17967556SAndrew.W.Wilson@sun.com 
17977556SAndrew.W.Wilson@sun.com 	/* indicate that it is no longer busy and no longer exists */
17988404SAndrew.W.Wilson@sun.com 	fileset_unbusy(file, TRUE, FALSE, -file->fse_open_cnt);
17995184Sek110237 
18005184Sek110237 	filebench_log(LOG_DEBUG_SCRIPT, "deleted file %s", file->fse_path);
18015184Sek110237 
18026084Saw148015 	return (FILEBENCH_OK);
18035184Sek110237 }
18045184Sek110237 
18055184Sek110237 /*
18065184Sek110237  * Emulates fsync of a file. Obtains the file descriptor index
18075184Sek110237  * from the flowop, obtains the actual file descriptor from
18085184Sek110237  * the threadflow's table, checks to be sure it is still an
18096084Saw148015  * open file, then does an fsync operation on it. Returns FILEBENCH_ERROR
18106084Saw148015  * if the file no longer is open, FILEBENCH_OK otherwise.
18115184Sek110237  */
18125184Sek110237 static int
18135184Sek110237 flowoplib_fsync(threadflow_t *threadflow, flowop_t *flowop)
18145184Sek110237 {
18155184Sek110237 	filesetentry_t *file;
18165184Sek110237 	int fd = flowop->fo_fdnumber;
18175184Sek110237 
18188615SAndrew.W.Wilson@sun.com 	if (threadflow->tf_fd[fd].fd_ptr == NULL) {
18195184Sek110237 		filebench_log(LOG_ERROR,
18205184Sek110237 		    "flowop %s attempted to fsync a closed fd %d",
18215184Sek110237 		    flowop->fo_name, fd);
18226084Saw148015 		return (FILEBENCH_ERROR);
18235184Sek110237 	}
18245184Sek110237 
18255673Saw148015 	file = threadflow->tf_fse[fd];
18265673Saw148015 
18275673Saw148015 	if ((file == NULL) ||
18285673Saw148015 	    (file->fse_fileset->fs_attrs & FILESET_IS_RAW_DEV)) {
18295673Saw148015 		filebench_log(LOG_ERROR,
18305673Saw148015 		    "flowop %s attempted to a fsync a RAW device",
18315673Saw148015 		    flowop->fo_name);
18326084Saw148015 		return (FILEBENCH_ERROR);
18335673Saw148015 	}
18345673Saw148015 
18355184Sek110237 	/* Measure time to fsync */
18365184Sek110237 	flowop_beginop(threadflow, flowop);
18378615SAndrew.W.Wilson@sun.com 	(void) FB_FSYNC(&threadflow->tf_fd[fd]);
18385673Saw148015 	flowop_endop(threadflow, flowop, 0);
18395184Sek110237 
18405184Sek110237 	filebench_log(LOG_DEBUG_SCRIPT, "fsync file %s", file->fse_path);
18415184Sek110237 
18426084Saw148015 	return (FILEBENCH_OK);
18435184Sek110237 }
18445184Sek110237 
18455184Sek110237 /*
18465184Sek110237  * Emulate fsync of an entire fileset. Search through the
18475184Sek110237  * threadflow's file descriptor array, doing fsync() on each
18485184Sek110237  * open file that belongs to the flowop's fileset. Always
18496084Saw148015  * returns FILEBENCH_OK.
18505184Sek110237  */
18515184Sek110237 static int
18525184Sek110237 flowoplib_fsyncset(threadflow_t *threadflow, flowop_t *flowop)
18535184Sek110237 {
18545184Sek110237 	int fd;
18555184Sek110237 
18565184Sek110237 	for (fd = 0; fd < THREADFLOW_MAXFD; fd++) {
18575184Sek110237 		filesetentry_t *file;
18585184Sek110237 
18595184Sek110237 		/* Match the file set to fsync */
18605184Sek110237 		if ((threadflow->tf_fse[fd] == NULL) ||
18615184Sek110237 		    (flowop->fo_fileset != threadflow->tf_fse[fd]->fse_fileset))
18625184Sek110237 			continue;
18635184Sek110237 
18645184Sek110237 		/* Measure time to fsync */
18655184Sek110237 		flowop_beginop(threadflow, flowop);
18668615SAndrew.W.Wilson@sun.com 		(void) FB_FSYNC(&threadflow->tf_fd[fd]);
18675673Saw148015 		flowop_endop(threadflow, flowop, 0);
18685184Sek110237 
18695184Sek110237 		file = threadflow->tf_fse[fd];
18705184Sek110237 
18715184Sek110237 		filebench_log(LOG_DEBUG_SCRIPT, "fsync file %s",
18725184Sek110237 		    file->fse_path);
18735184Sek110237 	}
18745184Sek110237 
18756084Saw148015 	return (FILEBENCH_OK);
18765184Sek110237 }
18775184Sek110237 
18785184Sek110237 /*
18795184Sek110237  * Emulate close of a file.  Obtains the file descriptor index
18805184Sek110237  * from the flowop, obtains the actual file descriptor from the
18815184Sek110237  * threadflow's table, checks to be sure it is still an open
18825184Sek110237  * file, then does a close operation on it. Then sets the
18835184Sek110237  * threadflow file descriptor table entry to 0, and the file set
18846084Saw148015  * entry pointer to NULL. Returns FILEBENCH_ERROR if the file was not open,
18856084Saw148015  * FILEBENCH_OK otherwise.
18865184Sek110237  */
18875184Sek110237 static int
18885184Sek110237 flowoplib_closefile(threadflow_t *threadflow, flowop_t *flowop)
18895184Sek110237 {
18905184Sek110237 	filesetentry_t *file;
18918404SAndrew.W.Wilson@sun.com 	fileset_t *fileset;
18925184Sek110237 	int fd = flowop->fo_fdnumber;
18935184Sek110237 
18948615SAndrew.W.Wilson@sun.com 	if (threadflow->tf_fd[fd].fd_ptr == NULL) {
18955184Sek110237 		filebench_log(LOG_ERROR,
18965184Sek110237 		    "flowop %s attempted to close an already closed fd %d",
18975184Sek110237 		    flowop->fo_name, fd);
18986084Saw148015 		return (FILEBENCH_ERROR);
18995184Sek110237 	}
19005184Sek110237 
19018404SAndrew.W.Wilson@sun.com 	file = threadflow->tf_fse[fd];
19028404SAndrew.W.Wilson@sun.com 	fileset = file->fse_fileset;
19038404SAndrew.W.Wilson@sun.com 
19048404SAndrew.W.Wilson@sun.com 	/* Wait for it to be non-busy */
19058404SAndrew.W.Wilson@sun.com 	(void) ipc_mutex_lock(&fileset->fs_pick_lock);
19068404SAndrew.W.Wilson@sun.com 	while (file->fse_flags & FSE_BUSY) {
19078404SAndrew.W.Wilson@sun.com 		file->fse_flags |= FSE_THRD_WAITNG;
19088404SAndrew.W.Wilson@sun.com 		(void) pthread_cond_wait(&fileset->fs_thrd_wait_cv,
19098404SAndrew.W.Wilson@sun.com 		    &fileset->fs_pick_lock);
19108404SAndrew.W.Wilson@sun.com 	}
19118404SAndrew.W.Wilson@sun.com 
19128404SAndrew.W.Wilson@sun.com 	/* File now available, grab it for closing */
19138404SAndrew.W.Wilson@sun.com 	file->fse_flags |= FSE_BUSY;
19148404SAndrew.W.Wilson@sun.com 
19158404SAndrew.W.Wilson@sun.com 	/* if last open, set declare idle */
19168404SAndrew.W.Wilson@sun.com 	if (file->fse_open_cnt == 1)
19178404SAndrew.W.Wilson@sun.com 		fileset->fs_idle_files--;
19188404SAndrew.W.Wilson@sun.com 
19198404SAndrew.W.Wilson@sun.com 	(void) ipc_mutex_unlock(&fileset->fs_pick_lock);
19208404SAndrew.W.Wilson@sun.com 
19215184Sek110237 	/* Measure time to close */
19225184Sek110237 	flowop_beginop(threadflow, flowop);
19238615SAndrew.W.Wilson@sun.com 	(void) FB_CLOSE(&threadflow->tf_fd[fd]);
19245673Saw148015 	flowop_endop(threadflow, flowop, 0);
19255184Sek110237 
19268404SAndrew.W.Wilson@sun.com 	fileset_unbusy(file, FALSE, FALSE, -1);
19275184Sek110237 
19288615SAndrew.W.Wilson@sun.com 	threadflow->tf_fd[fd].fd_ptr = NULL;
19295184Sek110237 
19305184Sek110237 	filebench_log(LOG_DEBUG_SCRIPT, "closed file %s", file->fse_path);
19315184Sek110237 
19326084Saw148015 	return (FILEBENCH_OK);
19335184Sek110237 }
19345184Sek110237 
19355184Sek110237 /*
19367946SAndrew.W.Wilson@sun.com  * Obtain the full pathname of the directory described by the filesetentry
19377946SAndrew.W.Wilson@sun.com  * indicated by "dir", and copy it into the character array pointed to by
19387946SAndrew.W.Wilson@sun.com  * path. Returns FILEBENCH_ERROR on errors, FILEBENCH_OK otherwise.
19397946SAndrew.W.Wilson@sun.com  */
19407946SAndrew.W.Wilson@sun.com static int
19417946SAndrew.W.Wilson@sun.com flowoplib_getdirpath(filesetentry_t *dir, char *path)
19427946SAndrew.W.Wilson@sun.com {
19437946SAndrew.W.Wilson@sun.com 	char		*fileset_path;
19447946SAndrew.W.Wilson@sun.com 	char		*fileset_name;
19457946SAndrew.W.Wilson@sun.com 	char		*part_path;
19467946SAndrew.W.Wilson@sun.com 
19477946SAndrew.W.Wilson@sun.com 	if ((fileset_path = avd_get_str(dir->fse_fileset->fs_path)) == NULL) {
19487946SAndrew.W.Wilson@sun.com 		filebench_log(LOG_ERROR, "Fileset path not set");
19497946SAndrew.W.Wilson@sun.com 		return (FILEBENCH_ERROR);
19507946SAndrew.W.Wilson@sun.com 	}
19517946SAndrew.W.Wilson@sun.com 
19527946SAndrew.W.Wilson@sun.com 	if ((fileset_name = avd_get_str(dir->fse_fileset->fs_name)) == NULL) {
19537946SAndrew.W.Wilson@sun.com 		filebench_log(LOG_ERROR, "Fileset name not set");
19547946SAndrew.W.Wilson@sun.com 		return (FILEBENCH_ERROR);
19557946SAndrew.W.Wilson@sun.com 	}
19567946SAndrew.W.Wilson@sun.com 
19577946SAndrew.W.Wilson@sun.com 	(void) fb_strlcpy(path, fileset_path, MAXPATHLEN);
19587946SAndrew.W.Wilson@sun.com 	(void) fb_strlcat(path, "/", MAXPATHLEN);
19597946SAndrew.W.Wilson@sun.com 	(void) fb_strlcat(path, fileset_name, MAXPATHLEN);
19607946SAndrew.W.Wilson@sun.com 
19617946SAndrew.W.Wilson@sun.com 	if ((part_path = fileset_resolvepath(dir)) == NULL)
19627946SAndrew.W.Wilson@sun.com 		return (FILEBENCH_ERROR);
19637946SAndrew.W.Wilson@sun.com 
19647946SAndrew.W.Wilson@sun.com 	(void) fb_strlcat(path, part_path, MAXPATHLEN);
19657946SAndrew.W.Wilson@sun.com 	free(part_path);
19667946SAndrew.W.Wilson@sun.com 
19677946SAndrew.W.Wilson@sun.com 	return (FILEBENCH_OK);
19687946SAndrew.W.Wilson@sun.com }
19697946SAndrew.W.Wilson@sun.com 
19707946SAndrew.W.Wilson@sun.com /*
19717946SAndrew.W.Wilson@sun.com  * Use mkdir to create a directory.  Obtains the fileset name from the
19727946SAndrew.W.Wilson@sun.com  * flowop, selects a non-existent leaf directory and obtains its full
19737946SAndrew.W.Wilson@sun.com  * path, then uses mkdir to create it on the storage subsystem (make it
19747946SAndrew.W.Wilson@sun.com  * existent). Returns FILEBENCH_NORSC is there are no more non-existent
19757946SAndrew.W.Wilson@sun.com  * directories in the fileset, FILEBENCH_ERROR on other errors, and
19767946SAndrew.W.Wilson@sun.com  * FILEBENCH_OK on success.
19777946SAndrew.W.Wilson@sun.com  */
19787946SAndrew.W.Wilson@sun.com static int
19797946SAndrew.W.Wilson@sun.com flowoplib_makedir(threadflow_t *threadflow, flowop_t *flowop)
19807946SAndrew.W.Wilson@sun.com {
19817946SAndrew.W.Wilson@sun.com 	filesetentry_t	*dir;
19827946SAndrew.W.Wilson@sun.com 	int		ret;
19837946SAndrew.W.Wilson@sun.com 	char		full_path[MAXPATHLEN];
19847946SAndrew.W.Wilson@sun.com 
19857946SAndrew.W.Wilson@sun.com 	if ((ret = flowoplib_pickleafdir(&dir, flowop,
19867946SAndrew.W.Wilson@sun.com 	    FILESET_PICKNOEXIST)) != FILEBENCH_OK)
19877946SAndrew.W.Wilson@sun.com 		return (ret);
19887946SAndrew.W.Wilson@sun.com 
19897946SAndrew.W.Wilson@sun.com 	if ((ret = flowoplib_getdirpath(dir, full_path)) != FILEBENCH_OK)
19907946SAndrew.W.Wilson@sun.com 		return (ret);
19917946SAndrew.W.Wilson@sun.com 
19927946SAndrew.W.Wilson@sun.com 	flowop_beginop(threadflow, flowop);
19938615SAndrew.W.Wilson@sun.com 	(void) FB_MKDIR(full_path, 0755);
19947946SAndrew.W.Wilson@sun.com 	flowop_endop(threadflow, flowop, 0);
19957946SAndrew.W.Wilson@sun.com 
19967946SAndrew.W.Wilson@sun.com 	/* indicate that it is no longer busy and now exists */
19978404SAndrew.W.Wilson@sun.com 	fileset_unbusy(dir, TRUE, TRUE, 0);
19987946SAndrew.W.Wilson@sun.com 
19997946SAndrew.W.Wilson@sun.com 	return (FILEBENCH_OK);
20007946SAndrew.W.Wilson@sun.com }
20017946SAndrew.W.Wilson@sun.com 
20027946SAndrew.W.Wilson@sun.com /*
20037946SAndrew.W.Wilson@sun.com  * Use rmdir to delete a directory.  Obtains the fileset name from the
20047946SAndrew.W.Wilson@sun.com  * flowop, selects an existent leaf directory and obtains its full path,
20057946SAndrew.W.Wilson@sun.com  * then uses rmdir to remove it from the storage subsystem (make it
20067946SAndrew.W.Wilson@sun.com  * non-existent). Returns FILEBENCH_NORSC is there are no more existent
20077946SAndrew.W.Wilson@sun.com  * directories in the fileset, FILEBENCH_ERROR on other errors, and
20087946SAndrew.W.Wilson@sun.com  * FILEBENCH_OK on success.
20097946SAndrew.W.Wilson@sun.com  */
20107946SAndrew.W.Wilson@sun.com static int
20117946SAndrew.W.Wilson@sun.com flowoplib_removedir(threadflow_t *threadflow, flowop_t *flowop)
20127946SAndrew.W.Wilson@sun.com {
20137946SAndrew.W.Wilson@sun.com 	filesetentry_t *dir;
20147946SAndrew.W.Wilson@sun.com 	int		ret;
20157946SAndrew.W.Wilson@sun.com 	char		full_path[MAXPATHLEN];
20167946SAndrew.W.Wilson@sun.com 
20177946SAndrew.W.Wilson@sun.com 	if ((ret = flowoplib_pickleafdir(&dir, flowop,
20187946SAndrew.W.Wilson@sun.com 	    FILESET_PICKEXISTS)) != FILEBENCH_OK)
20197946SAndrew.W.Wilson@sun.com 		return (ret);
20207946SAndrew.W.Wilson@sun.com 
20217946SAndrew.W.Wilson@sun.com 	if ((ret = flowoplib_getdirpath(dir, full_path)) != FILEBENCH_OK)
20227946SAndrew.W.Wilson@sun.com 		return (ret);
20237946SAndrew.W.Wilson@sun.com 
20247946SAndrew.W.Wilson@sun.com 	flowop_beginop(threadflow, flowop);
20258615SAndrew.W.Wilson@sun.com 	(void) FB_RMDIR(full_path);
20267946SAndrew.W.Wilson@sun.com 	flowop_endop(threadflow, flowop, 0);
20277946SAndrew.W.Wilson@sun.com 
20287946SAndrew.W.Wilson@sun.com 	/* indicate that it is no longer busy and no longer exists */
20298404SAndrew.W.Wilson@sun.com 	fileset_unbusy(dir, TRUE, FALSE, 0);
20307946SAndrew.W.Wilson@sun.com 
20317946SAndrew.W.Wilson@sun.com 	return (FILEBENCH_OK);
20327946SAndrew.W.Wilson@sun.com }
20337946SAndrew.W.Wilson@sun.com 
20347946SAndrew.W.Wilson@sun.com /*
20357946SAndrew.W.Wilson@sun.com  * Use opendir(), multiple readdir() calls, and closedir() to list the
20367946SAndrew.W.Wilson@sun.com  * contents of a directory.  Obtains the fileset name from the
20377946SAndrew.W.Wilson@sun.com  * flowop, selects a normal subdirectory (which always exist) and obtains
20387946SAndrew.W.Wilson@sun.com  * its full path, then uses opendir() to get a DIR handle to it from the
20397946SAndrew.W.Wilson@sun.com  * file system, a readdir() loop to access each directory entry, and
20407946SAndrew.W.Wilson@sun.com  * finally cleans up with a closedir(). The latency reported is the total
20417946SAndrew.W.Wilson@sun.com  * for all this activity, and it also reports the total number of bytes
20427946SAndrew.W.Wilson@sun.com  * in the entries as the amount "read". Returns FILEBENCH_ERROR on errors,
20437946SAndrew.W.Wilson@sun.com  * and FILEBENCH_OK on success.
20447946SAndrew.W.Wilson@sun.com  */
20457946SAndrew.W.Wilson@sun.com static int
20467946SAndrew.W.Wilson@sun.com flowoplib_listdir(threadflow_t *threadflow, flowop_t *flowop)
20477946SAndrew.W.Wilson@sun.com {
20487946SAndrew.W.Wilson@sun.com 	fileset_t	*fileset;
20497946SAndrew.W.Wilson@sun.com 	filesetentry_t	*dir;
20508615SAndrew.W.Wilson@sun.com 	DIR		*dir_handle;
20517946SAndrew.W.Wilson@sun.com 	struct dirent	*direntp;
20527946SAndrew.W.Wilson@sun.com 	int		dir_bytes = 0;
20537946SAndrew.W.Wilson@sun.com 	int		ret;
20547946SAndrew.W.Wilson@sun.com 	char		full_path[MAXPATHLEN];
20557946SAndrew.W.Wilson@sun.com 
20567946SAndrew.W.Wilson@sun.com 	if ((fileset = flowop->fo_fileset) == NULL) {
20577946SAndrew.W.Wilson@sun.com 		filebench_log(LOG_ERROR, "flowop NO fileset");
20587946SAndrew.W.Wilson@sun.com 		return (FILEBENCH_ERROR);
20597946SAndrew.W.Wilson@sun.com 	}
20607946SAndrew.W.Wilson@sun.com 
20618404SAndrew.W.Wilson@sun.com 	if ((dir = fileset_pick(fileset, FILESET_PICKDIR, 0, 0)) == NULL) {
20627946SAndrew.W.Wilson@sun.com 		filebench_log(LOG_DEBUG_SCRIPT,
20637946SAndrew.W.Wilson@sun.com 		    "flowop %s failed to pick directory from fileset %s",
20647946SAndrew.W.Wilson@sun.com 		    flowop->fo_name,
20657946SAndrew.W.Wilson@sun.com 		    avd_get_str(fileset->fs_name));
20668404SAndrew.W.Wilson@sun.com 		return (FILEBENCH_ERROR);
20677946SAndrew.W.Wilson@sun.com 	}
20687946SAndrew.W.Wilson@sun.com 
20697946SAndrew.W.Wilson@sun.com 	if ((ret = flowoplib_getdirpath(dir, full_path)) != FILEBENCH_OK)
20707946SAndrew.W.Wilson@sun.com 		return (ret);
20717946SAndrew.W.Wilson@sun.com 
20727946SAndrew.W.Wilson@sun.com 	flowop_beginop(threadflow, flowop);
20737946SAndrew.W.Wilson@sun.com 
20747946SAndrew.W.Wilson@sun.com 	/* open the directory */
20758615SAndrew.W.Wilson@sun.com 	if ((dir_handle = FB_OPENDIR(full_path)) == NULL) {
20767946SAndrew.W.Wilson@sun.com 		filebench_log(LOG_ERROR,
20777946SAndrew.W.Wilson@sun.com 		    "flowop %s failed to open directory in fileset %s\n",
20787946SAndrew.W.Wilson@sun.com 		    flowop->fo_name, avd_get_str(fileset->fs_name));
20797946SAndrew.W.Wilson@sun.com 		return (FILEBENCH_ERROR);
20807946SAndrew.W.Wilson@sun.com 	}
20817946SAndrew.W.Wilson@sun.com 
20827946SAndrew.W.Wilson@sun.com 	/* read through the directory entries */
20838615SAndrew.W.Wilson@sun.com 	while ((direntp = FB_READDIR(dir_handle)) != NULL) {
20847946SAndrew.W.Wilson@sun.com 		dir_bytes += (strlen(direntp->d_name) +
20857946SAndrew.W.Wilson@sun.com 		    sizeof (struct dirent) - 1);
20867946SAndrew.W.Wilson@sun.com 	}
20877946SAndrew.W.Wilson@sun.com 
20887946SAndrew.W.Wilson@sun.com 	/* close the directory */
20898615SAndrew.W.Wilson@sun.com 	(void) FB_CLOSEDIR(dir_handle);
20907946SAndrew.W.Wilson@sun.com 
20917946SAndrew.W.Wilson@sun.com 	flowop_endop(threadflow, flowop, dir_bytes);
20927946SAndrew.W.Wilson@sun.com 
20937946SAndrew.W.Wilson@sun.com 	/* indicate that it is no longer busy */
20948404SAndrew.W.Wilson@sun.com 	fileset_unbusy(dir, FALSE, FALSE, 0);
20957946SAndrew.W.Wilson@sun.com 
20967946SAndrew.W.Wilson@sun.com 	return (FILEBENCH_OK);
20977946SAndrew.W.Wilson@sun.com }
20987946SAndrew.W.Wilson@sun.com 
20997946SAndrew.W.Wilson@sun.com /*
21005184Sek110237  * Emulate stat of a file. Picks an arbitrary filesetentry with
21015184Sek110237  * an existing file from the flowop's fileset, then performs a
21026084Saw148015  * stat() operation on it. Returns FILEBENCH_ERROR if the flowop has no
21036084Saw148015  * associated fileset. Returns FILEBENCH_NORSC if an appropriate filesetentry
21046084Saw148015  * cannot be found, and FILEBENCH_OK on success.
21055184Sek110237  */
21065184Sek110237 static int
21075184Sek110237 flowoplib_statfile(threadflow_t *threadflow, flowop_t *flowop)
21085184Sek110237 {
21095184Sek110237 	filesetentry_t *file;
21105184Sek110237 	fileset_t *fileset;
21118615SAndrew.W.Wilson@sun.com 	struct stat64 statbuf;
21127556SAndrew.W.Wilson@sun.com 	int fd = flowop->fo_fdnumber;
21137556SAndrew.W.Wilson@sun.com 
21147556SAndrew.W.Wilson@sun.com 	/* if fd specified and the file is open, use it to access file */
21158615SAndrew.W.Wilson@sun.com 	if ((fd > 0) && (threadflow->tf_fd[fd].fd_num > 0)) {
21167556SAndrew.W.Wilson@sun.com 
21177556SAndrew.W.Wilson@sun.com 		/* check whether file handle still valid */
21187556SAndrew.W.Wilson@sun.com 		if ((file = threadflow->tf_fse[fd]) == NULL) {
21197556SAndrew.W.Wilson@sun.com 			filebench_log(LOG_DEBUG_SCRIPT,
21207556SAndrew.W.Wilson@sun.com 			    "flowop %s trying to stat NULL file at fd = %d",
21217556SAndrew.W.Wilson@sun.com 			    flowop->fo_name, fd);
21227556SAndrew.W.Wilson@sun.com 			return (FILEBENCH_ERROR);
21237556SAndrew.W.Wilson@sun.com 		}
21247556SAndrew.W.Wilson@sun.com 
21257556SAndrew.W.Wilson@sun.com 		/* if here, we still have a valid file pointer */
21267556SAndrew.W.Wilson@sun.com 		fileset = file->fse_fileset;
21277556SAndrew.W.Wilson@sun.com 	} else {
21287556SAndrew.W.Wilson@sun.com 		/* Otherwise, pick arbitrary file */
21297556SAndrew.W.Wilson@sun.com 		file = NULL;
21307556SAndrew.W.Wilson@sun.com 		fileset = flowop->fo_fileset;
21317556SAndrew.W.Wilson@sun.com 	}
21327556SAndrew.W.Wilson@sun.com 
21337556SAndrew.W.Wilson@sun.com 	if (fileset == NULL) {
21347556SAndrew.W.Wilson@sun.com 		filebench_log(LOG_ERROR,
21357556SAndrew.W.Wilson@sun.com 		    "statfile with no fileset specified");
21367556SAndrew.W.Wilson@sun.com 		return (FILEBENCH_ERROR);
21377556SAndrew.W.Wilson@sun.com 	}
21387556SAndrew.W.Wilson@sun.com 
21397556SAndrew.W.Wilson@sun.com #ifdef HAVE_RAW_SUPPORT
21407556SAndrew.W.Wilson@sun.com 	/* can't be used with raw devices */
21417556SAndrew.W.Wilson@sun.com 	if (fileset->fs_attrs & FILESET_IS_RAW_DEV) {
21427556SAndrew.W.Wilson@sun.com 		filebench_log(LOG_ERROR,
21437556SAndrew.W.Wilson@sun.com 		    "flowop %s attempted do a statfile on a RAW device",
21447556SAndrew.W.Wilson@sun.com 		    flowop->fo_name);
21456084Saw148015 		return (FILEBENCH_ERROR);
21465184Sek110237 	}
21477556SAndrew.W.Wilson@sun.com #endif /* HAVE_RAW_SUPPORT */
21487556SAndrew.W.Wilson@sun.com 
21497556SAndrew.W.Wilson@sun.com 	if (file == NULL) {
21507556SAndrew.W.Wilson@sun.com 		char path[MAXPATHLEN];
21517556SAndrew.W.Wilson@sun.com 		char *pathtmp;
21528404SAndrew.W.Wilson@sun.com 		int err;
21537556SAndrew.W.Wilson@sun.com 
21547556SAndrew.W.Wilson@sun.com 		/* pick arbitrary, existing (allocated) file */
21558404SAndrew.W.Wilson@sun.com 		if ((err = flowoplib_pickfile(&file, flowop,
21568404SAndrew.W.Wilson@sun.com 		    FILESET_PICKEXISTS, 0)) != FILEBENCH_OK) {
21577556SAndrew.W.Wilson@sun.com 			filebench_log(LOG_DEBUG_SCRIPT,
21587556SAndrew.W.Wilson@sun.com 			    "Statfile flowop %s failed to pick file",
21597556SAndrew.W.Wilson@sun.com 			    flowop->fo_name);
21608404SAndrew.W.Wilson@sun.com 			return (err);
21617556SAndrew.W.Wilson@sun.com 		}
21627556SAndrew.W.Wilson@sun.com 
21637556SAndrew.W.Wilson@sun.com 		/* resolve path and do a stat on file */
21647946SAndrew.W.Wilson@sun.com 		(void) fb_strlcpy(path, avd_get_str(fileset->fs_path),
21657946SAndrew.W.Wilson@sun.com 		    MAXPATHLEN);
21667946SAndrew.W.Wilson@sun.com 		(void) fb_strlcat(path, "/", MAXPATHLEN);
21677946SAndrew.W.Wilson@sun.com 		(void) fb_strlcat(path, avd_get_str(fileset->fs_name),
21687946SAndrew.W.Wilson@sun.com 		    MAXPATHLEN);
21697556SAndrew.W.Wilson@sun.com 		pathtmp = fileset_resolvepath(file);
21707946SAndrew.W.Wilson@sun.com 		(void) fb_strlcat(path, pathtmp, MAXPATHLEN);
21717556SAndrew.W.Wilson@sun.com 		free(pathtmp);
21727556SAndrew.W.Wilson@sun.com 
21737556SAndrew.W.Wilson@sun.com 		/* stat the file */
21747556SAndrew.W.Wilson@sun.com 		flowop_beginop(threadflow, flowop);
21758615SAndrew.W.Wilson@sun.com 		if (FB_STAT(path, &statbuf) == -1)
21767556SAndrew.W.Wilson@sun.com 			filebench_log(LOG_ERROR,
21777556SAndrew.W.Wilson@sun.com 			    "statfile flowop %s failed", flowop->fo_name);
21787556SAndrew.W.Wilson@sun.com 		flowop_endop(threadflow, flowop, 0);
21797556SAndrew.W.Wilson@sun.com 
21808404SAndrew.W.Wilson@sun.com 		fileset_unbusy(file, FALSE, FALSE, 0);
21817556SAndrew.W.Wilson@sun.com 	} else {
21827556SAndrew.W.Wilson@sun.com 		/* stat specific file */
21837556SAndrew.W.Wilson@sun.com 		flowop_beginop(threadflow, flowop);
21848615SAndrew.W.Wilson@sun.com 		if (FB_FSTAT(&threadflow->tf_fd[fd], &statbuf) == -1)
21857556SAndrew.W.Wilson@sun.com 			filebench_log(LOG_ERROR,
21867556SAndrew.W.Wilson@sun.com 			    "statfile flowop %s failed", flowop->fo_name);
21877556SAndrew.W.Wilson@sun.com 		flowop_endop(threadflow, flowop, 0);
21887556SAndrew.W.Wilson@sun.com 
21895184Sek110237 	}
21905184Sek110237 
21916084Saw148015 	return (FILEBENCH_OK);
21925184Sek110237 }
21935184Sek110237 
21945184Sek110237 
21955184Sek110237 /*
21965184Sek110237  * Additional reads and writes. Read and write whole files, write
21975184Sek110237  * and append to files. Some of these work with both fileobjs and
21985184Sek110237  * filesets, others only with filesets. The flowoplib_write routine
21995184Sek110237  * writes from thread memory, while the others read or write using
22005184Sek110237  * fo_buf memory. Note that both flowoplib_read() and
22015184Sek110237  * flowoplib_aiowrite() use thread memory as well.
22025184Sek110237  */
22035184Sek110237 
22045184Sek110237 
22055184Sek110237 /*
22065673Saw148015  * Emulate a read of a whole file. The file must be open with
22075673Saw148015  * file descriptor and filesetentry stored at the locations indexed
22085673Saw148015  * by the flowop's fdnumber. It then seeks to the beginning of the
22095673Saw148015  * associated file, and reads fs_iosize bytes at a time until the end
22106084Saw148015  * of the file. Returns FILEBENCH_ERROR on error, FILEBENCH_NORSC if
22116084Saw148015  * out of files, and FILEBENCH_OK on success.
22125184Sek110237  */
22135184Sek110237 static int
22145184Sek110237 flowoplib_readwholefile(threadflow_t *threadflow, flowop_t *flowop)
22155184Sek110237 {
22165673Saw148015 	caddr_t iobuf;
22175184Sek110237 	off64_t bytes = 0;
22188615SAndrew.W.Wilson@sun.com 	fb_fdesc_t *fdesc;
22196212Saw148015 	uint64_t wss;
22206212Saw148015 	fbint_t iosize;
22215184Sek110237 	int ret;
22226212Saw148015 	char zerordbuf;
22235184Sek110237 
22245673Saw148015 	/* get the file to use */
22256084Saw148015 	if ((ret = flowoplib_filesetup(threadflow, flowop, &wss,
22268615SAndrew.W.Wilson@sun.com 	    &fdesc)) != FILEBENCH_OK)
22276084Saw148015 		return (ret);
22285184Sek110237 
22295673Saw148015 	/* an I/O size of zero means read entire working set with one I/O */
22306212Saw148015 	if ((iosize = avd_get_int(flowop->fo_iosize)) == 0)
22315673Saw148015 		iosize = wss;
22325184Sek110237 
22336212Saw148015 	/*
22346212Saw148015 	 * The file may actually be 0 bytes long, in which case skip
22356212Saw148015 	 * the buffer set up call (which would fail) and substitute
22366212Saw148015 	 * a small buffer, which won't really be used.
22376212Saw148015 	 */
22386212Saw148015 	if (iosize == 0) {
22396212Saw148015 		iobuf = (caddr_t)&zerordbuf;
22406212Saw148015 		filebench_log(LOG_DEBUG_SCRIPT,
22416212Saw148015 		    "flowop %s read zero length file", flowop->fo_name);
22426212Saw148015 	} else {
22436212Saw148015 		if (flowoplib_iobufsetup(threadflow, flowop, &iobuf,
22446212Saw148015 		    iosize) != 0)
22456212Saw148015 			return (FILEBENCH_ERROR);
22466212Saw148015 	}
22475184Sek110237 
22485184Sek110237 	/* Measure time to read bytes */
22495184Sek110237 	flowop_beginop(threadflow, flowop);
22508615SAndrew.W.Wilson@sun.com 	(void) FB_LSEEK(fdesc, 0, SEEK_SET);
22518615SAndrew.W.Wilson@sun.com 	while ((ret = FB_READ(fdesc, iobuf, iosize)) > 0)
22525184Sek110237 		bytes += ret;
22535184Sek110237 
22545673Saw148015 	flowop_endop(threadflow, flowop, bytes);
22555184Sek110237 
22565184Sek110237 	if (ret < 0) {
22575184Sek110237 		filebench_log(LOG_ERROR,
22586391Saw148015 		    "readwhole fail Failed to read whole file: %s",
22596391Saw148015 		    strerror(errno));
22606084Saw148015 		return (FILEBENCH_ERROR);
22615184Sek110237 	}
22625184Sek110237 
22636084Saw148015 	return (FILEBENCH_OK);
22645184Sek110237 }
22655184Sek110237 
22665184Sek110237 /*
22675184Sek110237  * Emulate a write to a file of size fo_iosize.  Will write
22685184Sek110237  * to a file from a fileset if the flowop's fo_fileset field
22695184Sek110237  * specifies one or its fdnumber is non zero. Otherwise it
22705184Sek110237  * will write to a fileobj file, if one exists. If the file
22715184Sek110237  * is not currently open, the routine will attempt to open
22725184Sek110237  * it. The flowop's fo_wss parameter will be used to set the
22735184Sek110237  * maximum file size if it is non-zero, otherwise the
22745184Sek110237  * filesetentry's  fse_size will be used. A random memory
22755184Sek110237  * buffer offset is calculated, and, if fo_random is TRUE,
22765184Sek110237  * a random file offset is used for the write. Otherwise the
22776084Saw148015  * write is to the next sequential location. Returns
22786084Saw148015  * FILEBENCH_ERROR on errors, FILEBENCH_NORSC if iosetup can't
22796084Saw148015  * obtain a file, or FILEBENCH_OK on success.
22805184Sek110237  */
22815184Sek110237 static int
22825184Sek110237 flowoplib_write(threadflow_t *threadflow, flowop_t *flowop)
22835184Sek110237 {
22845673Saw148015 	caddr_t iobuf;
22856212Saw148015 	fbint_t wss;
22866212Saw148015 	fbint_t iosize;
22878615SAndrew.W.Wilson@sun.com 	fb_fdesc_t *fdesc;
22886084Saw148015 	int ret;
22895184Sek110237 
22906212Saw148015 	iosize = avd_get_int(flowop->fo_iosize);
22916084Saw148015 	if ((ret = flowoplib_iosetup(threadflow, flowop, &wss, &iobuf,
22928615SAndrew.W.Wilson@sun.com 	    &fdesc, iosize)) != FILEBENCH_OK)
22936084Saw148015 		return (ret);
22945184Sek110237 
22956212Saw148015 	if (avd_get_bool(flowop->fo_random)) {
22965184Sek110237 		uint64_t fileoffset;
22975184Sek110237 
22985184Sek110237 		if (filebench_randomno64(&fileoffset,
22996212Saw148015 		    wss, iosize, NULL) == -1) {
23005184Sek110237 			filebench_log(LOG_ERROR,
23015184Sek110237 			    "file size smaller than IO size for thread %s",
23025184Sek110237 			    flowop->fo_name);
23036084Saw148015 			return (FILEBENCH_ERROR);
23045184Sek110237 		}
23055184Sek110237 		flowop_beginop(threadflow, flowop);
23068615SAndrew.W.Wilson@sun.com 		if (FB_PWRITE(fdesc, iobuf,
23076212Saw148015 		    iosize, (off64_t)fileoffset) == -1) {
23085184Sek110237 			filebench_log(LOG_ERROR, "write failed, "
23096286Saw148015 			    "offset %llu io buffer %zd: %s",
23106286Saw148015 			    (u_longlong_t)fileoffset, iobuf, strerror(errno));
23115673Saw148015 			flowop_endop(threadflow, flowop, 0);
23126084Saw148015 			return (FILEBENCH_ERROR);
23135184Sek110237 		}
23146212Saw148015 		flowop_endop(threadflow, flowop, iosize);
23155184Sek110237 	} else {
23165184Sek110237 		flowop_beginop(threadflow, flowop);
23178615SAndrew.W.Wilson@sun.com 		if (FB_WRITE(fdesc, iobuf, iosize) == -1) {
23185184Sek110237 			filebench_log(LOG_ERROR,
23195673Saw148015 			    "write failed, io buffer %zd: %s",
23205673Saw148015 			    iobuf, strerror(errno));
23215673Saw148015 			flowop_endop(threadflow, flowop, 0);
23226084Saw148015 			return (FILEBENCH_ERROR);
23235184Sek110237 		}
23246212Saw148015 		flowop_endop(threadflow, flowop, iosize);
23255184Sek110237 	}
23265184Sek110237 
23276084Saw148015 	return (FILEBENCH_OK);
23285184Sek110237 }
23295184Sek110237 
23305184Sek110237 /*
23315184Sek110237  * Emulate a write of a whole file.  The size of the file
23325673Saw148015  * is taken from a filesetentry identified by fo_srcfdnumber or
23335673Saw148015  * from the working set size, while the file descriptor used is
23345673Saw148015  * identified by fo_fdnumber. Does multiple writes of fo_iosize
23356084Saw148015  * length length until full file has been written. Returns FILEBENCH_ERROR on
23366084Saw148015  * error, FILEBENCH_NORSC if out of files, FILEBENCH_OK on success.
23375184Sek110237  */
23385184Sek110237 static int
23395184Sek110237 flowoplib_writewholefile(threadflow_t *threadflow, flowop_t *flowop)
23405184Sek110237 {
23415673Saw148015 	caddr_t iobuf;
23425184Sek110237 	filesetentry_t *file;
23435184Sek110237 	int wsize;
23445184Sek110237 	off64_t seek;
23455184Sek110237 	off64_t bytes = 0;
23465673Saw148015 	uint64_t wss;
23476212Saw148015 	fbint_t iosize;
23488615SAndrew.W.Wilson@sun.com 	fb_fdesc_t *fdesc;
23495184Sek110237 	int srcfd = flowop->fo_srcfdnumber;
23505184Sek110237 	int ret;
23516212Saw148015 	char zerowrtbuf;
23525184Sek110237 
23535673Saw148015 	/* get the file to use */
23546084Saw148015 	if ((ret = flowoplib_filesetup(threadflow, flowop, &wss,
23558615SAndrew.W.Wilson@sun.com 	    &fdesc)) != FILEBENCH_OK)
23566084Saw148015 		return (ret);
23575184Sek110237 
23586212Saw148015 	/* an I/O size of zero means write entire working set with one I/O */
23596212Saw148015 	if ((iosize = avd_get_int(flowop->fo_iosize)) == 0)
23605673Saw148015 		iosize = wss;
23615184Sek110237 
23626212Saw148015 	/*
23636212Saw148015 	 * The file may actually be 0 bytes long, in which case skip
23646212Saw148015 	 * the buffer set up call (which would fail) and substitute
23656212Saw148015 	 * a small buffer, which won't really be used.
23666212Saw148015 	 */
23676212Saw148015 	if (iosize == 0) {
23686212Saw148015 		iobuf = (caddr_t)&zerowrtbuf;
23696212Saw148015 		filebench_log(LOG_DEBUG_SCRIPT,
23706212Saw148015 		    "flowop %s wrote zero length file", flowop->fo_name);
23716212Saw148015 	} else {
23726212Saw148015 		if (flowoplib_iobufsetup(threadflow, flowop, &iobuf,
23736212Saw148015 		    iosize) != 0)
23746212Saw148015 			return (FILEBENCH_ERROR);
23756212Saw148015 	}
23765184Sek110237 
23775184Sek110237 	file = threadflow->tf_fse[srcfd];
23785673Saw148015 	if ((srcfd != 0) && (file == NULL)) {
23795673Saw148015 		filebench_log(LOG_ERROR, "flowop %s: NULL src file",
23805184Sek110237 		    flowop->fo_name);
23816084Saw148015 		return (FILEBENCH_ERROR);
23825184Sek110237 	}
23835184Sek110237 
23845673Saw148015 	if (file)
23855673Saw148015 		wss = file->fse_size;
23865673Saw148015 
23875673Saw148015 	wsize = (int)MIN(wss, iosize);
23885184Sek110237 
23895184Sek110237 	/* Measure time to write bytes */
23905184Sek110237 	flowop_beginop(threadflow, flowop);
23915673Saw148015 	for (seek = 0; seek < wss; seek += wsize) {
23928615SAndrew.W.Wilson@sun.com 		ret = FB_WRITE(fdesc, iobuf, wsize);
23935184Sek110237 		if (ret != wsize) {
23945184Sek110237 			filebench_log(LOG_ERROR,
23955184Sek110237 			    "Failed to write %d bytes on fd %d: %s",
23968615SAndrew.W.Wilson@sun.com 			    wsize, fdesc->fd_num, strerror(errno));
23975673Saw148015 			flowop_endop(threadflow, flowop, 0);
23986084Saw148015 			return (FILEBENCH_ERROR);
23995184Sek110237 		}
24005673Saw148015 		wsize = (int)MIN(wss - seek, iosize);
24015184Sek110237 		bytes += ret;
24025184Sek110237 	}
24035673Saw148015 	flowop_endop(threadflow, flowop, bytes);
24045184Sek110237 
24056084Saw148015 	return (FILEBENCH_OK);
24065184Sek110237 }
24075184Sek110237 
24085184Sek110237 
24095184Sek110237 /*
24105184Sek110237  * Emulate a fixed size append to a file. Will append data to
24115184Sek110237  * a file chosen from a fileset if the flowop's fo_fileset
24125184Sek110237  * field specifies one or if its fdnumber is non zero.
24135184Sek110237  * Otherwise it will write to a fileobj file, if one exists.
24145184Sek110237  * The flowop's fo_wss parameter will be used to set the
24155184Sek110237  * maximum file size if it is non-zero, otherwise the
24165184Sek110237  * filesetentry's fse_size will be used. A random memory
24175184Sek110237  * buffer offset is calculated, then a logical seek to the
24185184Sek110237  * end of file is done followed by a write of fo_iosize
24195184Sek110237  * bytes. Writes are actually done from fo_buf, rather than
24205184Sek110237  * tf_mem as is done with flowoplib_write(), and no check
24215184Sek110237  * is made to see if fo_iosize exceeds the size of fo_buf.
24226084Saw148015  * Returns FILEBENCH_ERROR on error, FILEBENCH_NORSC if out of
24236084Saw148015  * files in the fileset, FILEBENCH_OK on success.
24245184Sek110237  */
24255184Sek110237 static int
24265184Sek110237 flowoplib_appendfile(threadflow_t *threadflow, flowop_t *flowop)
24275184Sek110237 {
24285673Saw148015 	caddr_t iobuf;
24298615SAndrew.W.Wilson@sun.com 	fb_fdesc_t *fdesc;
24306212Saw148015 	fbint_t wss;
24316212Saw148015 	fbint_t iosize;
24325184Sek110237 	int ret;
24335184Sek110237 
24346212Saw148015 	iosize = avd_get_int(flowop->fo_iosize);
24356084Saw148015 	if ((ret = flowoplib_iosetup(threadflow, flowop, &wss, &iobuf,
24368615SAndrew.W.Wilson@sun.com 	    &fdesc, iosize)) != FILEBENCH_OK)
24376084Saw148015 		return (ret);
24385184Sek110237 
24395184Sek110237 	/* XXX wss is not being used */
24405184Sek110237 
24415184Sek110237 	/* Measure time to write bytes */
24425184Sek110237 	flowop_beginop(threadflow, flowop);
24438615SAndrew.W.Wilson@sun.com 	(void) FB_LSEEK(fdesc, 0, SEEK_END);
24448615SAndrew.W.Wilson@sun.com 	ret = FB_WRITE(fdesc, iobuf, iosize);
24455673Saw148015 	if (ret != iosize) {
24465184Sek110237 		filebench_log(LOG_ERROR,
24476286Saw148015 		    "Failed to write %llu bytes on fd %d: %s",
24488615SAndrew.W.Wilson@sun.com 		    (u_longlong_t)iosize, fdesc->fd_num, strerror(errno));
24496212Saw148015 		flowop_endop(threadflow, flowop, ret);
24506084Saw148015 		return (FILEBENCH_ERROR);
24515184Sek110237 	}
24526212Saw148015 	flowop_endop(threadflow, flowop, ret);
24535184Sek110237 
24546084Saw148015 	return (FILEBENCH_OK);
24555184Sek110237 }
24565184Sek110237 
24575184Sek110237 /*
24585184Sek110237  * Emulate a random size append to a file. Will append data
24595184Sek110237  * to a file chosen from a fileset if the flowop's fo_fileset
24605184Sek110237  * field specifies one or if its fdnumber is non zero. Otherwise
24615184Sek110237  * it will write to a fileobj file, if one exists. The flowop's
24625184Sek110237  * fo_wss parameter will be used to set the maximum file size
24635184Sek110237  * if it is non-zero, otherwise the filesetentry's fse_size
24645184Sek110237  * will be used.  A random transfer size (but at most fo_iosize
24655184Sek110237  * bytes) and a random memory offset are calculated. A logical
24665184Sek110237  * seek to the end of file is done, then writes of up to
24675184Sek110237  * FILE_ALLOC_BLOCK in size are done until the full transfer
24685184Sek110237  * size has been written. Writes are actually done from fo_buf,
24695184Sek110237  * rather than tf_mem as is done with flowoplib_write().
24706084Saw148015  * Returns FILEBENCH_ERROR on error, FILEBENCH_NORSC if out of
24716084Saw148015  * files in the fileset, FILEBENCH_OK on success.
24725184Sek110237  */
24735184Sek110237 static int
24745184Sek110237 flowoplib_appendfilerand(threadflow_t *threadflow, flowop_t *flowop)
24755184Sek110237 {
24765673Saw148015 	caddr_t iobuf;
24775184Sek110237 	uint64_t appendsize;
24788615SAndrew.W.Wilson@sun.com 	fb_fdesc_t *fdesc;
24796212Saw148015 	fbint_t wss;
24806212Saw148015 	fbint_t iosize;
24816212Saw148015 	int ret = 0;
24825184Sek110237 
24836212Saw148015 	if ((iosize = avd_get_int(flowop->fo_iosize)) == 0) {
24846212Saw148015 		filebench_log(LOG_ERROR, "zero iosize for flowop %s",
24856212Saw148015 		    flowop->fo_name);
24866212Saw148015 		return (FILEBENCH_ERROR);
24876212Saw148015 	}
24886212Saw148015 
24896212Saw148015 	if (filebench_randomno64(&appendsize, iosize, 1LL, NULL) != 0)
24906084Saw148015 		return (FILEBENCH_ERROR);
24915184Sek110237 
24925673Saw148015 	/* skip if attempting zero length append */
24935673Saw148015 	if (appendsize == 0) {
24945673Saw148015 		flowop_beginop(threadflow, flowop);
24955673Saw148015 		flowop_endop(threadflow, flowop, 0LL);
24966084Saw148015 		return (FILEBENCH_OK);
24975673Saw148015 	}
24985184Sek110237 
24996084Saw148015 	if ((ret = flowoplib_iosetup(threadflow, flowop, &wss, &iobuf,
25008615SAndrew.W.Wilson@sun.com 	    &fdesc, appendsize)) != FILEBENCH_OK)
25016084Saw148015 		return (ret);
25025673Saw148015 
25035184Sek110237 	/* XXX wss is not being used */
25045184Sek110237 
25055673Saw148015 	/* Measure time to write bytes */
25065673Saw148015 	flowop_beginop(threadflow, flowop);
25075673Saw148015 
25088615SAndrew.W.Wilson@sun.com 	(void) FB_LSEEK(fdesc, 0, SEEK_END);
25098615SAndrew.W.Wilson@sun.com 	ret = FB_WRITE(fdesc, iobuf, appendsize);
25105673Saw148015 	if (ret != appendsize) {
25115673Saw148015 		filebench_log(LOG_ERROR,
25126286Saw148015 		    "Failed to write %llu bytes on fd %d: %s",
25138615SAndrew.W.Wilson@sun.com 		    (u_longlong_t)appendsize, fdesc->fd_num, strerror(errno));
25145673Saw148015 		flowop_endop(threadflow, flowop, 0);
25156084Saw148015 		return (FILEBENCH_ERROR);
25165184Sek110237 	}
25175184Sek110237 
25185673Saw148015 	flowop_endop(threadflow, flowop, appendsize);
25195184Sek110237 
25206084Saw148015 	return (FILEBENCH_OK);
25215184Sek110237 }
25225184Sek110237 
25236212Saw148015 typedef struct testrandvar_priv {
25246212Saw148015 	uint64_t sample_count;
25256212Saw148015 	double val_sum;
25266212Saw148015 	double sqr_sum;
25276212Saw148015 } testrandvar_priv_t;
25286212Saw148015 
25296212Saw148015 /*
25306212Saw148015  * flowop to calculate various statistics from the number stream
25316212Saw148015  * produced by a random variable. This allows verification that the
25326212Saw148015  * random distribution used to define the random variable is producing
25336212Saw148015  * the expected distribution of random numbers.
25346212Saw148015  */
25356212Saw148015 /* ARGSUSED */
25366212Saw148015 static int
25376212Saw148015 flowoplib_testrandvar(threadflow_t *threadflow, flowop_t *flowop)
25386212Saw148015 {
25396212Saw148015 	testrandvar_priv_t	*mystats;
25406212Saw148015 	double			value;
25416212Saw148015 
25426212Saw148015 	if ((mystats = (testrandvar_priv_t *)flowop->fo_private) == NULL) {
25436212Saw148015 		filebench_log(LOG_ERROR, "testrandvar not initialized\n");
25446212Saw148015 		filebench_shutdown(1);
25456212Saw148015 		return (-1);
25466212Saw148015 	}
25476212Saw148015 
25486212Saw148015 	value = avd_get_dbl(flowop->fo_value);
25496212Saw148015 
25506212Saw148015 	mystats->sample_count++;
25516212Saw148015 	mystats->val_sum += value;
25526212Saw148015 	mystats->sqr_sum += (value * value);
25536212Saw148015 
25546212Saw148015 	return (0);
25556212Saw148015 }
25566212Saw148015 
25576212Saw148015 /*
25586212Saw148015  * Initialize the private data area used to accumulate the statistics
25596212Saw148015  */
25606212Saw148015 static int
25616212Saw148015 flowoplib_testrandvar_init(flowop_t *flowop)
25626212Saw148015 {
25636212Saw148015 	testrandvar_priv_t	*mystats;
25646212Saw148015 
25656212Saw148015 	if ((mystats = (testrandvar_priv_t *)
25666212Saw148015 	    malloc(sizeof (testrandvar_priv_t))) == NULL) {
25676212Saw148015 		filebench_log(LOG_ERROR, "could not initialize testrandvar");
25686212Saw148015 		filebench_shutdown(1);
25696212Saw148015 		return (-1);
25706212Saw148015 	}
25716212Saw148015 
25726212Saw148015 	mystats->sample_count = 0;
25736212Saw148015 	mystats->val_sum = 0;
25746212Saw148015 	mystats->sqr_sum = 0;
25756212Saw148015 	flowop->fo_private = (void *)mystats;
25766212Saw148015 
25776212Saw148015 	(void) ipc_mutex_unlock(&flowop->fo_lock);
25786212Saw148015 	return (0);
25796212Saw148015 }
25806212Saw148015 
25816212Saw148015 /*
25826212Saw148015  * Print out the accumulated statistics, and free the private storage
25836212Saw148015  */
25846212Saw148015 static void
25856212Saw148015 flowoplib_testrandvar_destruct(flowop_t *flowop)
25866212Saw148015 {
25876212Saw148015 	testrandvar_priv_t	*mystats;
25886212Saw148015 	double mean, std_dev, dbl_count;
25896212Saw148015 
25906212Saw148015 	(void) ipc_mutex_lock(&flowop->fo_lock);
25916212Saw148015 	if ((mystats = (testrandvar_priv_t *)
25926212Saw148015 	    flowop->fo_private) == NULL) {
25936212Saw148015 		(void) ipc_mutex_unlock(&flowop->fo_lock);
25946212Saw148015 		return;
25956212Saw148015 	}
25966212Saw148015 
25976212Saw148015 	flowop->fo_private = NULL;
25986212Saw148015 	(void) ipc_mutex_unlock(&flowop->fo_lock);
25996212Saw148015 
26006212Saw148015 	dbl_count = (double)mystats->sample_count;
26016212Saw148015 	mean = mystats->val_sum / dbl_count;
26026212Saw148015 	std_dev = sqrt((mystats->sqr_sum / dbl_count) - (mean * mean)) / mean;
26036212Saw148015 
26046212Saw148015 	filebench_log(LOG_VERBOSE,
26056286Saw148015 	    "testrandvar: ops = %llu, mean = %8.2lf, stddev = %8.2lf",
26066286Saw148015 	    (u_longlong_t)mystats->sample_count, mean, std_dev);
26076212Saw148015 	free(mystats);
26086212Saw148015 }
26095184Sek110237 
26105184Sek110237 /*
26117556SAndrew.W.Wilson@sun.com  * prints message to the console from within a thread
26127556SAndrew.W.Wilson@sun.com  */
26137556SAndrew.W.Wilson@sun.com static int
26147556SAndrew.W.Wilson@sun.com flowoplib_print(threadflow_t *threadflow, flowop_t *flowop)
26157556SAndrew.W.Wilson@sun.com {
26167556SAndrew.W.Wilson@sun.com 	procflow_t *procflow;
26177556SAndrew.W.Wilson@sun.com 
26187556SAndrew.W.Wilson@sun.com 	procflow = threadflow->tf_process;
26197556SAndrew.W.Wilson@sun.com 	filebench_log(LOG_INFO,
26207556SAndrew.W.Wilson@sun.com 	    "Message from process (%s,%d), thread (%s,%d): %s",
26217556SAndrew.W.Wilson@sun.com 	    procflow->pf_name, procflow->pf_instance,
26227556SAndrew.W.Wilson@sun.com 	    threadflow->tf_name, threadflow->tf_instance,
26237556SAndrew.W.Wilson@sun.com 	    avd_get_str(flowop->fo_value));
26247556SAndrew.W.Wilson@sun.com 
26257556SAndrew.W.Wilson@sun.com 	return (FILEBENCH_OK);
26267556SAndrew.W.Wilson@sun.com }
26277556SAndrew.W.Wilson@sun.com 
26287556SAndrew.W.Wilson@sun.com /*
26295184Sek110237  * Prints usage information for flowop operations.
26305184Sek110237  */
26315184Sek110237 void
26325184Sek110237 flowoplib_usage()
26335184Sek110237 {
26345184Sek110237 	(void) fprintf(stderr,
26355184Sek110237 	    "flowop [openfile|createfile] name=<name>,fileset=<fname>\n");
26365184Sek110237 	(void) fprintf(stderr,
26375184Sek110237 	    "                       [,fd=<file desc num>]\n");
26385184Sek110237 	(void) fprintf(stderr, "\n");
26395184Sek110237 	(void) fprintf(stderr,
26405184Sek110237 	    "flowop closefile name=<name>,fd=<file desc num>]\n");
26415184Sek110237 	(void) fprintf(stderr, "\n");
26425184Sek110237 	(void) fprintf(stderr, "flowop deletefile name=<name>\n");
26435184Sek110237 	(void) fprintf(stderr, "                       [,fileset=<fname>]\n");
26445184Sek110237 	(void) fprintf(stderr,
26455184Sek110237 	    "                       [,fd=<file desc num>]\n");
26465184Sek110237 	(void) fprintf(stderr, "\n");
26475184Sek110237 	(void) fprintf(stderr, "flowop statfile name=<name>\n");
26485184Sek110237 	(void) fprintf(stderr, "                       [,fileset=<fname>]\n");
26495184Sek110237 	(void) fprintf(stderr,
26505184Sek110237 	    "                       [,fd=<file desc num>]\n");
26515184Sek110237 	(void) fprintf(stderr, "\n");
26525184Sek110237 	(void) fprintf(stderr,
26535184Sek110237 	    "flowop fsync name=<name>,fd=<file desc num>]\n");
26545184Sek110237 	(void) fprintf(stderr, "\n");
26555184Sek110237 	(void) fprintf(stderr,
26565184Sek110237 	    "flowop fsyncset name=<name>,fileset=<fname>]\n");
26575184Sek110237 	(void) fprintf(stderr, "\n");
26585184Sek110237 	(void) fprintf(stderr, "flowop [write|read|aiowrite] name=<name>, \n");
26595184Sek110237 	(void) fprintf(stderr,
26605184Sek110237 	    "                       filename|fileset=<fname>,\n");
26615184Sek110237 	(void) fprintf(stderr, "                       iosize=<size>\n");
26625184Sek110237 	(void) fprintf(stderr, "                       [,directio]\n");
26635184Sek110237 	(void) fprintf(stderr, "                       [,dsync]\n");
26645184Sek110237 	(void) fprintf(stderr, "                       [,iters=<count>]\n");
26655184Sek110237 	(void) fprintf(stderr, "                       [,random]\n");
26665184Sek110237 	(void) fprintf(stderr, "                       [,opennext]\n");
26675184Sek110237 	(void) fprintf(stderr, "                       [,workingset=<size>]\n");
26685184Sek110237 	(void) fprintf(stderr,
26695184Sek110237 	    "flowop [appendfile|appendfilerand] name=<name>, \n");
26705184Sek110237 	(void) fprintf(stderr,
26715184Sek110237 	    "                       filename|fileset=<fname>,\n");
26725184Sek110237 	(void) fprintf(stderr, "                       iosize=<size>\n");
26735184Sek110237 	(void) fprintf(stderr, "                       [,dsync]\n");
26745184Sek110237 	(void) fprintf(stderr, "                       [,iters=<count>]\n");
26755184Sek110237 	(void) fprintf(stderr, "                       [,workingset=<size>]\n");
26765184Sek110237 	(void) fprintf(stderr,
26775184Sek110237 	    "flowop [readwholefile|writewholefile] name=<name>, \n");
26785184Sek110237 	(void) fprintf(stderr,
26795184Sek110237 	    "                       filename|fileset=<fname>,\n");
26805184Sek110237 	(void) fprintf(stderr, "                       iosize=<size>\n");
26815184Sek110237 	(void) fprintf(stderr, "                       [,dsync]\n");
26825184Sek110237 	(void) fprintf(stderr, "                       [,iters=<count>]\n");
26835184Sek110237 	(void) fprintf(stderr, "\n");
26845184Sek110237 	(void) fprintf(stderr, "flowop aiowait name=<name>,target="
26855184Sek110237 	    "<aiowrite-flowop>\n");
26865184Sek110237 	(void) fprintf(stderr, "\n");
26875184Sek110237 	(void) fprintf(stderr, "flowop sempost name=<name>,"
26885184Sek110237 	    "target=<semblock-flowop>,\n");
26895184Sek110237 	(void) fprintf(stderr,
26905184Sek110237 	    "                       value=<increment-to-post>\n");
26915184Sek110237 	(void) fprintf(stderr, "\n");
26925184Sek110237 	(void) fprintf(stderr, "flowop semblock name=<name>,value="
26935184Sek110237 	    "<decrement-to-receive>,\n");
26945184Sek110237 	(void) fprintf(stderr, "                       highwater="
26955184Sek110237 	    "<inbound-queue-max>\n");
26965184Sek110237 	(void) fprintf(stderr, "\n");
26975184Sek110237 	(void) fprintf(stderr, "flowop block name=<name>\n");
26985184Sek110237 	(void) fprintf(stderr, "\n");
26995184Sek110237 	(void) fprintf(stderr,
27005184Sek110237 	    "flowop wakeup name=<name>,target=<block-flowop>,\n");
27015184Sek110237 	(void) fprintf(stderr, "\n");
27025184Sek110237 	(void) fprintf(stderr,
27035184Sek110237 	    "flowop hog name=<name>,value=<number-of-mem-ops>\n");
27045184Sek110237 	(void) fprintf(stderr,
27055184Sek110237 	    "flowop delay name=<name>,value=<number-of-seconds>\n");
27065184Sek110237 	(void) fprintf(stderr, "\n");
27075184Sek110237 	(void) fprintf(stderr, "flowop eventlimit name=<name>\n");
27085184Sek110237 	(void) fprintf(stderr, "flowop bwlimit name=<name>,value=<mb/s>\n");
27095184Sek110237 	(void) fprintf(stderr, "flowop iopslimit name=<name>,value=<iop/s>\n");
27105184Sek110237 	(void) fprintf(stderr,
27115184Sek110237 	    "flowop finishoncount name=<name>,value=<ops/s>\n");
27125184Sek110237 	(void) fprintf(stderr,
27135184Sek110237 	    "flowop finishonbytes name=<name>,value=<bytes>\n");
27145184Sek110237 	(void) fprintf(stderr, "\n");
27155184Sek110237 	(void) fprintf(stderr, "\n");
27165184Sek110237 }
2717