xref: /onnv-gate/usr/src/cmd/filebench/common/flowop_library.c (revision 6701:4213fadfdec4)
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 /*
226084Saw148015  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
235184Sek110237  * Use is subject to license terms.
246613Sek110237  *
256613Sek110237  * Portions Copyright 2008 Denis Cheng
265184Sek110237  */
275184Sek110237 
285184Sek110237 #pragma ident	"%Z%%M%	%I%	%E% SMI"
295184Sek110237 
305184Sek110237 #include "config.h"
315184Sek110237 
325184Sek110237 #include <sys/types.h>
335184Sek110237 #ifdef HAVE_SYS_ASYNCH_H
345184Sek110237 #include <sys/asynch.h>
355184Sek110237 #endif
365184Sek110237 #include <sys/ipc.h>
375184Sek110237 #include <sys/sem.h>
385184Sek110237 #include <sys/errno.h>
395184Sek110237 #include <sys/time.h>
405184Sek110237 #include <inttypes.h>
415184Sek110237 #include <fcntl.h>
426212Saw148015 #include <math.h>
435184Sek110237 
445184Sek110237 #ifdef HAVE_UTILITY_H
455184Sek110237 #include <utility.h>
465184Sek110237 #endif /* HAVE_UTILITY_H */
475184Sek110237 
485184Sek110237 #ifdef HAVE_AIO
495184Sek110237 #include <aio.h>
505184Sek110237 #endif /* HAVE_AIO */
515184Sek110237 
525184Sek110237 #ifdef HAVE_LIBAIO_H
535184Sek110237 #include <libaio.h>
545184Sek110237 #endif /* HAVE_LIBAIO_H */
555184Sek110237 
565184Sek110237 #ifdef HAVE_SYS_ASYNC_H
575184Sek110237 #include <sys/asynch.h>
585184Sek110237 #endif /* HAVE_SYS_ASYNC_H */
595184Sek110237 
605184Sek110237 #ifdef HAVE_AIO_H
615184Sek110237 #include <aio.h>
625184Sek110237 #endif /* HAVE_AIO_H */
635184Sek110237 
645184Sek110237 #ifndef HAVE_UINT_T
655184Sek110237 #define	uint_t unsigned int
665184Sek110237 #endif /* HAVE_UINT_T */
675184Sek110237 
685184Sek110237 #ifndef HAVE_AIOCB64_T
695184Sek110237 #define	aiocb64 aiocb
705184Sek110237 #endif /* HAVE_AIOCB64_T */
715184Sek110237 
725184Sek110237 #ifndef HAVE_SYSV_SEM
735184Sek110237 #include <semaphore.h>
745184Sek110237 #endif /* HAVE_SYSV_SEM */
755184Sek110237 
765184Sek110237 #include "filebench.h"
775184Sek110237 #include "flowop.h"
785184Sek110237 #include "fileset.h"
796212Saw148015 #include "fb_random.h"
805184Sek110237 
815184Sek110237 /*
825184Sek110237  * These routines implement the flowops from the f language. Each
835184Sek110237  * flowop has has a name such as "read", and a set of function pointers
845184Sek110237  * to call for initialization, execution and destruction of the flowop.
855184Sek110237  * The table flowoplib_funcs[] contains a flowoplib struct for each
865184Sek110237  * implemented flowop. Most flowops use a generic initialization function
875184Sek110237  * and all currently use a generic destruction function. All flowop
885184Sek110237  * functions referenced from the table are in this file, though, of
895184Sek110237  * course, they often call functions from other files.
905184Sek110237  *
915184Sek110237  * The flowop_init() routine uses the flowoplib_funcs[] table to
925184Sek110237  * create an initial set of "instance 0" flowops, one for each type of
935184Sek110237  * flowop, from which all other flowops are derived. These "instance 0"
945184Sek110237  * flowops are initialized with information from the table including
955184Sek110237  * pointers for their fo_init, fo_func and fo_destroy functions. When
965184Sek110237  * a flowop definition is encountered in an f language script, the
975184Sek110237  * "type" of flowop, such as "read" is used to search for the
985184Sek110237  * "instance 0" flowop named "read", then a new flowop is allocated
995184Sek110237  * which inherits its function pointers and other initial properties
1005184Sek110237  * from the instance 0 flowop, and is given a new name as specified
1015184Sek110237  * by the "name=" attribute.
1025184Sek110237  */
1035184Sek110237 
1045184Sek110237 static int flowoplib_init_generic(flowop_t *flowop);
1055184Sek110237 static void flowoplib_destruct_generic(flowop_t *flowop);
1066084Saw148015 static void flowoplib_destruct_noop(flowop_t *flowop);
1075184Sek110237 static int flowoplib_fdnum(threadflow_t *threadflow, flowop_t *flowop);
1085184Sek110237 static int flowoplib_write(threadflow_t *threadflow, flowop_t *flowop);
1095184Sek110237 #ifdef HAVE_AIO
1105184Sek110237 static int flowoplib_aiowrite(threadflow_t *threadflow, flowop_t *flowop);
1115184Sek110237 static int flowoplib_aiowait(threadflow_t *threadflow, flowop_t *flowop);
1125184Sek110237 #endif
1135184Sek110237 static int flowoplib_read(threadflow_t *threadflow, flowop_t *flowop);
1145184Sek110237 static int flowoplib_block_init(flowop_t *flowop);
1155184Sek110237 static int flowoplib_block(threadflow_t *threadflow, flowop_t *flowop);
1165184Sek110237 static int flowoplib_wakeup(threadflow_t *threadflow, flowop_t *flowop);
1175184Sek110237 static int flowoplib_hog(threadflow_t *threadflow, flowop_t *flowop);
1185184Sek110237 static int flowoplib_delay(threadflow_t *threadflow, flowop_t *flowop);
1195184Sek110237 static int flowoplib_sempost(threadflow_t *threadflow, flowop_t *flowop);
1205184Sek110237 static int flowoplib_sempost_init(flowop_t *flowop);
1215184Sek110237 static int flowoplib_semblock(threadflow_t *threadflow, flowop_t *flowop);
1225184Sek110237 static int flowoplib_semblock_init(flowop_t *flowop);
1235184Sek110237 static void flowoplib_semblock_destruct(flowop_t *flowop);
1245184Sek110237 static int flowoplib_eventlimit(threadflow_t *, flowop_t *flowop);
1255184Sek110237 static int flowoplib_bwlimit(threadflow_t *, flowop_t *flowop);
1265184Sek110237 static int flowoplib_iopslimit(threadflow_t *, flowop_t *flowop);
1275184Sek110237 static int flowoplib_opslimit(threadflow_t *, flowop_t *flowop);
1285184Sek110237 static int flowoplib_openfile(threadflow_t *, flowop_t *flowop);
1295184Sek110237 static int flowoplib_openfile_common(threadflow_t *, flowop_t *flowop, int fd);
1305184Sek110237 static int flowoplib_createfile(threadflow_t *, flowop_t *flowop);
1315184Sek110237 static int flowoplib_closefile(threadflow_t *, flowop_t *flowop);
1325184Sek110237 static int flowoplib_fsync(threadflow_t *, flowop_t *flowop);
1335184Sek110237 static int flowoplib_readwholefile(threadflow_t *, flowop_t *flowop);
1345184Sek110237 static int flowoplib_writewholefile(threadflow_t *, flowop_t *flowop);
1355184Sek110237 static int flowoplib_appendfile(threadflow_t *threadflow, flowop_t *flowop);
1365184Sek110237 static int flowoplib_appendfilerand(threadflow_t *threadflow, flowop_t *flowop);
1375184Sek110237 static int flowoplib_deletefile(threadflow_t *threadflow, flowop_t *flowop);
1385184Sek110237 static int flowoplib_statfile(threadflow_t *threadflow, flowop_t *flowop);
1395184Sek110237 static int flowoplib_finishoncount(threadflow_t *threadflow, flowop_t *flowop);
1405184Sek110237 static int flowoplib_finishonbytes(threadflow_t *threadflow, flowop_t *flowop);
1415184Sek110237 static int flowoplib_fsyncset(threadflow_t *threadflow, flowop_t *flowop);
1426212Saw148015 static int flowoplib_testrandvar(threadflow_t *threadflow, flowop_t *flowop);
1436212Saw148015 static int flowoplib_testrandvar_init(flowop_t *flowop);
1446212Saw148015 static void flowoplib_testrandvar_destruct(flowop_t *flowop);
1455184Sek110237 
1465184Sek110237 typedef struct flowoplib {
1475184Sek110237 	int	fl_type;
1485184Sek110237 	int	fl_attrs;
1495184Sek110237 	char	*fl_name;
1505184Sek110237 	int	(*fl_init)();
1515184Sek110237 	int	(*fl_func)();
1525184Sek110237 	void	(*fl_destruct)();
1535184Sek110237 } flowoplib_t;
1545184Sek110237 
1555184Sek110237 static flowoplib_t flowoplib_funcs[] = {
1565184Sek110237 	FLOW_TYPE_IO, FLOW_ATTR_WRITE, "write", flowoplib_init_generic,
1575184Sek110237 	flowoplib_write, flowoplib_destruct_generic,
1585184Sek110237 	FLOW_TYPE_IO, FLOW_ATTR_READ, "read", flowoplib_init_generic,
1595184Sek110237 	flowoplib_read, flowoplib_destruct_generic,
1605184Sek110237 #ifdef HAVE_AIO
1615184Sek110237 	FLOW_TYPE_AIO, FLOW_ATTR_WRITE, "aiowrite", flowoplib_init_generic,
1625184Sek110237 	flowoplib_aiowrite, flowoplib_destruct_generic,
1635184Sek110237 	FLOW_TYPE_AIO, 0, "aiowait", flowoplib_init_generic,
1645184Sek110237 	flowoplib_aiowait, flowoplib_destruct_generic,
1655184Sek110237 #endif
1665184Sek110237 	FLOW_TYPE_SYNC, 0, "block", flowoplib_block_init,
1675184Sek110237 	flowoplib_block, flowoplib_destruct_generic,
1685184Sek110237 	FLOW_TYPE_SYNC, 0, "wakeup", flowoplib_init_generic,
1695184Sek110237 	flowoplib_wakeup, flowoplib_destruct_generic,
1705184Sek110237 	FLOW_TYPE_SYNC, 0, "semblock", flowoplib_semblock_init,
1715184Sek110237 	flowoplib_semblock, flowoplib_semblock_destruct,
1725184Sek110237 	FLOW_TYPE_SYNC, 0, "sempost", flowoplib_sempost_init,
1736084Saw148015 	flowoplib_sempost, flowoplib_destruct_noop,
1745184Sek110237 	FLOW_TYPE_OTHER, 0, "hog", flowoplib_init_generic,
1755184Sek110237 	flowoplib_hog, flowoplib_destruct_generic,
1765184Sek110237 	FLOW_TYPE_OTHER, 0, "delay", flowoplib_init_generic,
1775184Sek110237 	flowoplib_delay, flowoplib_destruct_generic,
1785184Sek110237 	FLOW_TYPE_OTHER, 0, "eventlimit", flowoplib_init_generic,
1795184Sek110237 	flowoplib_eventlimit, flowoplib_destruct_generic,
1805184Sek110237 	FLOW_TYPE_OTHER, 0, "bwlimit", flowoplib_init_generic,
1815184Sek110237 	flowoplib_bwlimit, flowoplib_destruct_generic,
1825184Sek110237 	FLOW_TYPE_OTHER, 0, "iopslimit", flowoplib_init_generic,
1835184Sek110237 	flowoplib_iopslimit, flowoplib_destruct_generic,
1845184Sek110237 	FLOW_TYPE_OTHER, 0, "opslimit", flowoplib_init_generic,
1855184Sek110237 	flowoplib_opslimit, flowoplib_destruct_generic,
1865184Sek110237 	FLOW_TYPE_OTHER, 0, "finishoncount", flowoplib_init_generic,
1875184Sek110237 	flowoplib_finishoncount, flowoplib_destruct_generic,
1885184Sek110237 	FLOW_TYPE_OTHER, 0, "finishonbytes", flowoplib_init_generic,
1895184Sek110237 	flowoplib_finishonbytes, flowoplib_destruct_generic,
1905184Sek110237 	FLOW_TYPE_IO, 0, "openfile", flowoplib_init_generic,
1915184Sek110237 	flowoplib_openfile, flowoplib_destruct_generic,
1925184Sek110237 	FLOW_TYPE_IO, 0, "createfile", flowoplib_init_generic,
1935184Sek110237 	flowoplib_createfile, flowoplib_destruct_generic,
1945184Sek110237 	FLOW_TYPE_IO, 0, "closefile", flowoplib_init_generic,
1955184Sek110237 	flowoplib_closefile, flowoplib_destruct_generic,
1965184Sek110237 	FLOW_TYPE_IO, 0, "fsync", flowoplib_init_generic,
1975184Sek110237 	flowoplib_fsync, flowoplib_destruct_generic,
1985184Sek110237 	FLOW_TYPE_IO, 0, "fsyncset", flowoplib_init_generic,
1995184Sek110237 	flowoplib_fsyncset, flowoplib_destruct_generic,
2005184Sek110237 	FLOW_TYPE_IO, 0, "statfile", flowoplib_init_generic,
2015184Sek110237 	flowoplib_statfile, flowoplib_destruct_generic,
2025184Sek110237 	FLOW_TYPE_IO, FLOW_ATTR_READ, "readwholefile", flowoplib_init_generic,
2035184Sek110237 	flowoplib_readwholefile, flowoplib_destruct_generic,
2045184Sek110237 	FLOW_TYPE_IO, FLOW_ATTR_WRITE, "appendfile", flowoplib_init_generic,
2055184Sek110237 	flowoplib_appendfile, flowoplib_destruct_generic,
2065184Sek110237 	FLOW_TYPE_IO, FLOW_ATTR_WRITE, "appendfilerand", flowoplib_init_generic,
2075184Sek110237 	flowoplib_appendfilerand, flowoplib_destruct_generic,
2085184Sek110237 	FLOW_TYPE_IO, 0, "deletefile", flowoplib_init_generic,
2095184Sek110237 	flowoplib_deletefile, flowoplib_destruct_generic,
2105184Sek110237 	FLOW_TYPE_IO, FLOW_ATTR_WRITE, "writewholefile", flowoplib_init_generic,
2116212Saw148015 	flowoplib_writewholefile, flowoplib_destruct_generic,
2126212Saw148015 	/* routine to calculate mean and stddev for output from a randvar */
2136212Saw148015 	FLOW_TYPE_OTHER, 0, "testrandvar", flowoplib_testrandvar_init,
2146212Saw148015 	flowoplib_testrandvar, flowoplib_testrandvar_destruct
2155184Sek110237 };
2165184Sek110237 
2175184Sek110237 /*
2185184Sek110237  * Loops through the master list of flowops defined in this
2195184Sek110237  * module, and creates and initializes a flowop for each one
2205184Sek110237  * by calling flowop_define. As a side effect of calling
2215184Sek110237  * flowop define, the created flowops are placed on the
2225184Sek110237  * master flowop list. All created flowops are set to
2235184Sek110237  * instance "0".
2245184Sek110237  */
2255184Sek110237 void
2265184Sek110237 flowoplib_init()
2275184Sek110237 {
2285184Sek110237 	int nops = sizeof (flowoplib_funcs) / sizeof (flowoplib_t);
2295184Sek110237 	int i;
2305184Sek110237 
2315184Sek110237 	for (i = 0; i < nops; i++) {
2325184Sek110237 		flowop_t *flowop;
2335184Sek110237 		flowoplib_t *fl;
2345184Sek110237 
2355184Sek110237 		fl = &flowoplib_funcs[i];
2365184Sek110237 
2375184Sek110237 		if ((flowop = flowop_define(NULL,
2386550Saw148015 		    fl->fl_name, NULL, NULL, 0, fl->fl_type)) == 0) {
2395184Sek110237 			filebench_log(LOG_ERROR,
2405184Sek110237 			    "failed to create flowop %s\n",
2415184Sek110237 			    fl->fl_name);
2425184Sek110237 			filebench_shutdown(1);
2435184Sek110237 		}
2445184Sek110237 
2455184Sek110237 		flowop->fo_func = fl->fl_func;
2465184Sek110237 		flowop->fo_init = fl->fl_init;
2475184Sek110237 		flowop->fo_destruct = fl->fl_destruct;
2485184Sek110237 		flowop->fo_attrs = fl->fl_attrs;
2495184Sek110237 	}
2505184Sek110237 }
2515184Sek110237 
2525184Sek110237 static int
2535184Sek110237 flowoplib_init_generic(flowop_t *flowop)
2545184Sek110237 {
2555184Sek110237 	(void) ipc_mutex_unlock(&flowop->fo_lock);
2566084Saw148015 	return (FILEBENCH_OK);
2575184Sek110237 }
2585184Sek110237 
2595184Sek110237 static void
2605184Sek110237 flowoplib_destruct_generic(flowop_t *flowop)
2615184Sek110237 {
2626084Saw148015 	char *buf;
2636084Saw148015 
2646084Saw148015 	/* release any local resources held by the flowop */
2656084Saw148015 	(void) ipc_mutex_lock(&flowop->fo_lock);
2666084Saw148015 	buf = flowop->fo_buf;
2676084Saw148015 	flowop->fo_buf = NULL;
2686084Saw148015 	(void) ipc_mutex_unlock(&flowop->fo_lock);
2696084Saw148015 
2706084Saw148015 	if (buf)
2716084Saw148015 		free(buf);
2726084Saw148015 }
2736084Saw148015 
2746084Saw148015 /*
2756084Saw148015  * Special total noop destruct
2766084Saw148015  */
2776084Saw148015 /* ARGSUSED */
2786084Saw148015 static void
2796084Saw148015 flowoplib_destruct_noop(flowop_t *flowop)
2806084Saw148015 {
2815184Sek110237 }
2825184Sek110237 
2835184Sek110237 /*
2845184Sek110237  * Generates a file attribute from flags in the supplied flowop.
2855184Sek110237  * Sets FLOW_ATTR_DIRECTIO and/or FLOW_ATTR_DSYNC as needed.
2865184Sek110237  */
2875184Sek110237 static int
2885184Sek110237 flowoplib_fileattrs(flowop_t *flowop)
2895184Sek110237 {
2905184Sek110237 	int attrs = 0;
2915184Sek110237 
2926212Saw148015 	if (avd_get_bool(flowop->fo_directio))
2935184Sek110237 		attrs |= FLOW_ATTR_DIRECTIO;
2945184Sek110237 
2956212Saw148015 	if (avd_get_bool(flowop->fo_dsync))
2965184Sek110237 		attrs |= FLOW_ATTR_DSYNC;
2975184Sek110237 
2985184Sek110237 	return (attrs);
2995184Sek110237 }
3005184Sek110237 
3015184Sek110237 /*
3025184Sek110237  * Searches for a file descriptor. Tries the flowop's
3035184Sek110237  * fo_fdnumber first and returns with it if it has been
3045184Sek110237  * explicitly set (greater than 0). It next checks to
3055184Sek110237  * see if a rotating file descriptor policy is in effect,
3065184Sek110237  * and if not returns the fdnumber regardless of what
3075184Sek110237  * it is. (note that if it is 0, it just selects to the
3085184Sek110237  * default file descriptor in the threadflow's tf_fd
3095184Sek110237  * array). If the rotating fd policy is in effect, it
3105184Sek110237  * cycles from the end of the tf_fd array to one location
3115184Sek110237  * beyond the maximum needed by the number of entries in
3125184Sek110237  * the associated fileset on each invocation, then starts
3135184Sek110237  * over from the end.
3145184Sek110237  *
3155184Sek110237  * The routine returns an index into the threadflow's
3165184Sek110237  * tf_fd table where the actual file descriptor will be
3175184Sek110237  * found. Note: the calling routine must not call this
3185184Sek110237  * routine if the flowop does not have a fileset, and the
3195184Sek110237  * flowop's fo_fdnumber is zero and fo_rotatefd is
3205184Sek110237  * asserted, or an addressing fault may occur.
3215184Sek110237  */
3225673Saw148015 static int
3235184Sek110237 flowoplib_fdnum(threadflow_t *threadflow, flowop_t *flowop)
3245184Sek110237 {
3256212Saw148015 	fbint_t	entries;
3266391Saw148015 	int fdnumber = flowop->fo_fdnumber;
3276212Saw148015 
3285184Sek110237 	/* If the script sets the fd explicitly */
3296391Saw148015 	if (fdnumber > 0)
3306391Saw148015 		return (fdnumber);
3315184Sek110237 
3325184Sek110237 	/* If the flowop defaults to persistent fd */
3336212Saw148015 	if (!avd_get_bool(flowop->fo_rotatefd))
3346391Saw148015 		return (fdnumber);
3356391Saw148015 
3366391Saw148015 	if (flowop->fo_fileset == NULL) {
3376391Saw148015 		filebench_log(LOG_ERROR, "flowop NULL file");
3386391Saw148015 		return (FILEBENCH_ERROR);
3396391Saw148015 	}
3405184Sek110237 
3416212Saw148015 	entries = flowop->fo_fileset->fs_constentries;
3426212Saw148015 
3435184Sek110237 	/* Rotate the fd on each flowop invocation */
3446212Saw148015 	if (entries > (THREADFLOW_MAXFD / 2)) {
3455184Sek110237 		filebench_log(LOG_ERROR, "Out of file descriptors in flowop %s"
3466286Saw148015 		    " (too many files : %llu",
3476286Saw148015 		    flowop->fo_name, (u_longlong_t)entries);
3486084Saw148015 		return (FILEBENCH_ERROR);
3495184Sek110237 	}
3505184Sek110237 
3515184Sek110237 	/* First time around */
3525184Sek110237 	if (threadflow->tf_fdrotor == 0)
3535184Sek110237 		threadflow->tf_fdrotor = THREADFLOW_MAXFD;
3545184Sek110237 
3555184Sek110237 	/* One fd for every file in the set */
3566212Saw148015 	if (entries == (THREADFLOW_MAXFD - threadflow->tf_fdrotor))
3575184Sek110237 		threadflow->tf_fdrotor = THREADFLOW_MAXFD;
3585184Sek110237 
3595184Sek110237 
3605184Sek110237 	threadflow->tf_fdrotor--;
3615184Sek110237 	filebench_log(LOG_DEBUG_IMPL, "selected fd = %d",
3625184Sek110237 	    threadflow->tf_fdrotor);
3635184Sek110237 	return (threadflow->tf_fdrotor);
3645184Sek110237 }
3655184Sek110237 
3665184Sek110237 /*
3675673Saw148015  * Determines the file descriptor to use, and attempts to open
3685673Saw148015  * the file if it is not already open. Also determines the wss
3696084Saw148015  * value. Returns FILEBENCH_ERROR on errors, FILESET_NORSC if
3706084Saw148015  * if flowop_openfile_common couldn't obtain an appropriate file
3716084Saw148015  * from a the fileset, and FILEBENCH_OK otherwise.
3725673Saw148015  */
3735673Saw148015 static int
3745673Saw148015 flowoplib_filesetup(threadflow_t *threadflow, flowop_t *flowop,
3756212Saw148015     fbint_t *wssp, int *filedescp)
3765673Saw148015 {
3775673Saw148015 	int fd = flowoplib_fdnum(threadflow, flowop);
3785673Saw148015 
3795673Saw148015 	if (fd == -1)
3806084Saw148015 		return (FILEBENCH_ERROR);
3815673Saw148015 
3825673Saw148015 	if (threadflow->tf_fd[fd] == 0) {
3836084Saw148015 		int ret;
3846084Saw148015 
3856084Saw148015 		if ((ret = flowoplib_openfile_common(
3866084Saw148015 		    threadflow, flowop, fd)) != FILEBENCH_OK)
3876084Saw148015 			return (ret);
3885673Saw148015 
3895673Saw148015 		if (threadflow->tf_fse[fd]) {
3905673Saw148015 			filebench_log(LOG_DEBUG_IMPL, "opened file %s",
3915673Saw148015 			    threadflow->tf_fse[fd]->fse_path);
3925673Saw148015 		} else {
3935673Saw148015 			filebench_log(LOG_DEBUG_IMPL,
3945673Saw148015 			    "opened device %s/%s",
3956212Saw148015 			    avd_get_str(flowop->fo_fileset->fs_path),
3966212Saw148015 			    avd_get_str(flowop->fo_fileset->fs_name));
3975673Saw148015 		}
3985673Saw148015 	}
3995673Saw148015 
4005673Saw148015 	*filedescp = threadflow->tf_fd[fd];
4015673Saw148015 
4026212Saw148015 	if ((*wssp = flowop->fo_constwss) == 0) {
4035673Saw148015 		if (threadflow->tf_fse[fd])
4045673Saw148015 			*wssp = threadflow->tf_fse[fd]->fse_size;
4055673Saw148015 		else
4066212Saw148015 			*wssp = avd_get_int(flowop->fo_fileset->fs_size);
4075673Saw148015 	}
4085673Saw148015 
4096084Saw148015 	return (FILEBENCH_OK);
4105673Saw148015 }
4115673Saw148015 
4125673Saw148015 /*
4135673Saw148015  * Determines the io buffer or random offset into tf_mem for
4146084Saw148015  * the IO operation. Returns FILEBENCH_ERROR on errors, FILEBENCH_OK otherwise.
4155673Saw148015  */
4165673Saw148015 static int
4175673Saw148015 flowoplib_iobufsetup(threadflow_t *threadflow, flowop_t *flowop,
4186212Saw148015     caddr_t *iobufp, fbint_t iosize)
4195673Saw148015 {
4205673Saw148015 	long memsize;
4215673Saw148015 	size_t memoffset;
4225673Saw148015 
4235673Saw148015 	if (iosize == 0) {
4245673Saw148015 		filebench_log(LOG_ERROR, "zero iosize for thread %s",
4255673Saw148015 		    flowop->fo_name);
4266084Saw148015 		return (FILEBENCH_ERROR);
4275673Saw148015 	}
4285673Saw148015 
4296212Saw148015 	if ((memsize = threadflow->tf_constmemsize) != 0) {
4305673Saw148015 
4315673Saw148015 		/* use tf_mem for I/O with random offset */
4326212Saw148015 		if (filebench_randomno(&memoffset,
4336212Saw148015 		    memsize, iosize, NULL) == -1) {
4345673Saw148015 			filebench_log(LOG_ERROR,
4355673Saw148015 			    "tf_memsize smaller than IO size for thread %s",
4365673Saw148015 			    flowop->fo_name);
4376084Saw148015 			return (FILEBENCH_ERROR);
4385673Saw148015 		}
4395673Saw148015 		*iobufp = threadflow->tf_mem + memoffset;
4405673Saw148015 
4415673Saw148015 	} else {
4425673Saw148015 		/* use private I/O buffer */
4435673Saw148015 		if ((flowop->fo_buf != NULL) &&
4445673Saw148015 		    (flowop->fo_buf_size < iosize)) {
4456212Saw148015 			/* too small, so free up and re-allocate */
4465673Saw148015 			free(flowop->fo_buf);
4475673Saw148015 			flowop->fo_buf = NULL;
4485673Saw148015 		}
4496212Saw148015 
4506212Saw148015 		/*
4516212Saw148015 		 * Allocate memory for the  buffer. The memory is freed
4526212Saw148015 		 * by flowop_destruct_generic() or by this routine if more
4536212Saw148015 		 * memory is needed for the buffer.
4546212Saw148015 		 */
4555673Saw148015 		if ((flowop->fo_buf == NULL) && ((flowop->fo_buf
4565673Saw148015 		    = (char *)malloc(iosize)) == NULL))
4576084Saw148015 			return (FILEBENCH_ERROR);
4585673Saw148015 
4595673Saw148015 		flowop->fo_buf_size = iosize;
4605673Saw148015 		*iobufp = flowop->fo_buf;
4615673Saw148015 	}
4626084Saw148015 	return (FILEBENCH_OK);
4635673Saw148015 }
4645673Saw148015 
4655673Saw148015 /*
4665673Saw148015  * Determines the file descriptor to use, opens it if necessary, the
4675673Saw148015  * io buffer or random offset into tf_mem for IO operation and the wss
4686084Saw148015  * value. Returns FILEBENCH_ERROR on errors, FILEBENCH_OK otherwise.
4695673Saw148015  */
4705673Saw148015 static int
4715673Saw148015 flowoplib_iosetup(threadflow_t *threadflow, flowop_t *flowop,
4726212Saw148015     fbint_t *wssp, caddr_t *iobufp, int *filedescp, fbint_t iosize)
4735673Saw148015 {
4746084Saw148015 	int ret;
4756084Saw148015 
4766084Saw148015 	if ((ret = flowoplib_filesetup(threadflow, flowop, wssp, filedescp)) !=
4776084Saw148015 	    FILEBENCH_OK)
4786084Saw148015 		return (ret);
4795673Saw148015 
4806084Saw148015 	if ((ret = flowoplib_iobufsetup(threadflow, flowop, iobufp, iosize)) !=
4816084Saw148015 	    FILEBENCH_OK)
4826084Saw148015 		return (ret);
4835673Saw148015 
4846084Saw148015 	return (FILEBENCH_OK);
4855673Saw148015 }
4865673Saw148015 
4875673Saw148015 /*
4885184Sek110237  * Emulate posix read / pread. If the flowop has a fileset,
4895184Sek110237  * a file descriptor number index is fetched, otherwise a
4905184Sek110237  * supplied fileobj file is used. In either case the specified
4915184Sek110237  * file will be opened if not already open. If the flowop has
4926084Saw148015  * neither a fileset or fileobj, an error is logged and FILEBENCH_ERROR
4935184Sek110237  * returned.
4945184Sek110237  *
4955184Sek110237  * The actual read is done to a random offset in the
4965184Sek110237  * threadflow's thread memory (tf_mem), with a size set by
4975184Sek110237  * fo_iosize and at either a random disk offset within the
4985184Sek110237  * working set size, or at the next sequential location. If
4996084Saw148015  * any errors are encountered, FILEBENCH_ERROR is returned,
5006084Saw148015  * if no appropriate file can be obtained from the fileset then
5016084Saw148015  * FILEBENCH_NORSC is returned, otherise FILEBENCH_OK is returned.
5025184Sek110237  */
5035184Sek110237 static int
5045184Sek110237 flowoplib_read(threadflow_t *threadflow, flowop_t *flowop)
5055184Sek110237 {
5065673Saw148015 	caddr_t iobuf;
5076212Saw148015 	fbint_t wss;
5086212Saw148015 	fbint_t iosize;
5095184Sek110237 	int filedesc;
5105184Sek110237 	int ret;
5115184Sek110237 
5126212Saw148015 
5136212Saw148015 	iosize = avd_get_int(flowop->fo_iosize);
5146084Saw148015 	if ((ret = flowoplib_iosetup(threadflow, flowop, &wss, &iobuf,
5156212Saw148015 	    &filedesc, iosize)) != FILEBENCH_OK)
5166084Saw148015 		return (ret);
5175184Sek110237 
5186212Saw148015 	if (avd_get_bool(flowop->fo_random)) {
5195184Sek110237 		uint64_t fileoffset;
5205184Sek110237 
5216212Saw148015 		if (filebench_randomno64(&fileoffset,
5226212Saw148015 		    wss, iosize, NULL) == -1) {
5235184Sek110237 			filebench_log(LOG_ERROR,
5245184Sek110237 			    "file size smaller than IO size for thread %s",
5255184Sek110237 			    flowop->fo_name);
5266084Saw148015 			return (FILEBENCH_ERROR);
5275184Sek110237 		}
5285184Sek110237 
5295184Sek110237 		(void) flowop_beginop(threadflow, flowop);
5305673Saw148015 		if ((ret = pread64(filedesc, iobuf,
5316212Saw148015 		    iosize, (off64_t)fileoffset)) == -1) {
5325673Saw148015 			(void) flowop_endop(threadflow, flowop, 0);
5335184Sek110237 			filebench_log(LOG_ERROR,
5346286Saw148015 			    "read file %s failed, offset %llu "
5355673Saw148015 			    "io buffer %zd: %s",
5366212Saw148015 			    avd_get_str(flowop->fo_fileset->fs_name),
5376286Saw148015 			    (u_longlong_t)fileoffset, iobuf, strerror(errno));
5385673Saw148015 			flowop_endop(threadflow, flowop, 0);
5396084Saw148015 			return (FILEBENCH_ERROR);
5405184Sek110237 		}
5415673Saw148015 		(void) flowop_endop(threadflow, flowop, ret);
5425184Sek110237 
5435184Sek110237 		if ((ret == 0))
5445184Sek110237 			(void) lseek64(filedesc, 0, SEEK_SET);
5455184Sek110237 
5465184Sek110237 	} else {
5475184Sek110237 		(void) flowop_beginop(threadflow, flowop);
5486212Saw148015 		if ((ret = read(filedesc, iobuf, iosize)) == -1) {
5496212Saw148015 			(void) flowop_endop(threadflow, flowop, 0);
5505184Sek110237 			filebench_log(LOG_ERROR,
5515673Saw148015 			    "read file %s failed, io buffer %zd: %s",
5526212Saw148015 			    avd_get_str(flowop->fo_fileset->fs_name),
5535673Saw148015 			    iobuf, strerror(errno));
5545673Saw148015 			(void) flowop_endop(threadflow, flowop, 0);
5556084Saw148015 			return (FILEBENCH_ERROR);
5565184Sek110237 		}
5575673Saw148015 		(void) flowop_endop(threadflow, flowop, ret);
5585184Sek110237 
5595184Sek110237 		if ((ret == 0))
5605184Sek110237 			(void) lseek64(filedesc, 0, SEEK_SET);
5615184Sek110237 	}
5625184Sek110237 
5636084Saw148015 	return (FILEBENCH_OK);
5645184Sek110237 }
5655184Sek110237 
5665184Sek110237 #ifdef HAVE_AIO
5675184Sek110237 
5685184Sek110237 /*
5695184Sek110237  * Asynchronous write section. An Asynchronous IO element
5705184Sek110237  * (aiolist_t) is used to associate the asynchronous write request with
5715184Sek110237  * its subsequent completion. This element includes a aiocb64 struct
5725184Sek110237  * that is used by posix aio_xxx calls to track the asynchronous writes.
5735184Sek110237  * The flowops aiowrite and aiowait result in calls to these posix
5745184Sek110237  * aio_xxx system routines to do the actual asynchronous write IO
5755184Sek110237  * operations.
5765184Sek110237  */
5775184Sek110237 
5785184Sek110237 
5795184Sek110237 /*
5805184Sek110237  * Allocates an asynchronous I/O list (aio, of type
5815184Sek110237  * aiolist_t) element. Adds it to the flowop thread's
5825184Sek110237  * threadflow aio list. Returns a pointer to the element.
5835184Sek110237  */
5845184Sek110237 static aiolist_t *
5855184Sek110237 aio_allocate(flowop_t *flowop)
5865184Sek110237 {
5875184Sek110237 	aiolist_t *aiolist;
5885184Sek110237 
5895184Sek110237 	if ((aiolist = malloc(sizeof (aiolist_t))) == NULL) {
5905184Sek110237 		filebench_log(LOG_ERROR, "malloc aiolist failed");
5915184Sek110237 		filebench_shutdown(1);
5925184Sek110237 	}
5935184Sek110237 
5945184Sek110237 	/* Add to list */
5955184Sek110237 	if (flowop->fo_thread->tf_aiolist == NULL) {
5965184Sek110237 		flowop->fo_thread->tf_aiolist = aiolist;
5975184Sek110237 		aiolist->al_next = NULL;
5985184Sek110237 	} else {
5995184Sek110237 		aiolist->al_next = flowop->fo_thread->tf_aiolist;
6005184Sek110237 		flowop->fo_thread->tf_aiolist = aiolist;
6015184Sek110237 	}
6025184Sek110237 	return (aiolist);
6035184Sek110237 }
6045184Sek110237 
6055184Sek110237 /*
6065184Sek110237  * Searches for the aiolist element that has a matching
6076084Saw148015  * completion block, aiocb. If none found returns FILEBENCH_ERROR. If
6085184Sek110237  * found, removes the aiolist element from flowop thread's
6096084Saw148015  * list and returns FILEBENCH_OK.
6105184Sek110237  */
6115184Sek110237 static int
6125184Sek110237 aio_deallocate(flowop_t *flowop, struct aiocb64 *aiocb)
6135184Sek110237 {
6145184Sek110237 	aiolist_t *aiolist = flowop->fo_thread->tf_aiolist;
6155184Sek110237 	aiolist_t *previous = NULL;
6165184Sek110237 	aiolist_t *match = NULL;
6175184Sek110237 
6185184Sek110237 	if (aiocb == NULL) {
6195184Sek110237 		filebench_log(LOG_ERROR, "null aiocb deallocate");
6206084Saw148015 		return (FILEBENCH_OK);
6215184Sek110237 	}
6225184Sek110237 
6235184Sek110237 	while (aiolist) {
6245184Sek110237 		if (aiocb == &(aiolist->al_aiocb)) {
6255184Sek110237 			match = aiolist;
6265184Sek110237 			break;
6275184Sek110237 		}
6285184Sek110237 		previous = aiolist;
6295184Sek110237 		aiolist = aiolist->al_next;
6305184Sek110237 	}
6315184Sek110237 
6325184Sek110237 	if (match == NULL)
6336084Saw148015 		return (FILEBENCH_ERROR);
6345184Sek110237 
6355184Sek110237 	/* Remove from the list */
6365184Sek110237 	if (previous)
6375184Sek110237 		previous->al_next = match->al_next;
6385184Sek110237 	else
6395184Sek110237 		flowop->fo_thread->tf_aiolist = match->al_next;
6405184Sek110237 
6416084Saw148015 	return (FILEBENCH_OK);
6425184Sek110237 }
6435184Sek110237 
6445184Sek110237 /*
6455184Sek110237  * Emulate posix aiowrite(). Determines which file to use,
6465184Sek110237  * either one file of a fileset, or the file associated
6475184Sek110237  * with a fileobj, allocates and fills an aiolist_t element
6485184Sek110237  * for the write, and issues the asynchronous write. This
6495184Sek110237  * operation is only valid for random IO, and returns an
6506084Saw148015  * error if the flowop is set for sequential IO. Returns
6516084Saw148015  * FILEBENCH_OK on success, FILEBENCH_NORSC if iosetup can't
6526084Saw148015  * obtain a file to open, and FILEBENCH_ERROR on any
6536084Saw148015  * encountered error.
6545184Sek110237  */
6555184Sek110237 static int
6565184Sek110237 flowoplib_aiowrite(threadflow_t *threadflow, flowop_t *flowop)
6575184Sek110237 {
6585673Saw148015 	caddr_t iobuf;
6596212Saw148015 	fbint_t wss;
6606212Saw148015 	fbint_t iosize;
6615184Sek110237 	int filedesc;
6626084Saw148015 	int ret;
6635184Sek110237 
6646212Saw148015 	iosize = avd_get_int(flowop->fo_iosize);
6656212Saw148015 
6666084Saw148015 	if ((ret = flowoplib_iosetup(threadflow, flowop, &wss, &iobuf,
6676212Saw148015 	    &filedesc, iosize)) != FILEBENCH_OK)
6686084Saw148015 		return (ret);
6695184Sek110237 
6706212Saw148015 	if (avd_get_bool(flowop->fo_random)) {
6715184Sek110237 		uint64_t fileoffset;
6725184Sek110237 		struct aiocb64 *aiocb;
6735184Sek110237 		aiolist_t *aiolist;
6745184Sek110237 
6755184Sek110237 		if (filebench_randomno64(&fileoffset,
6766212Saw148015 		    wss, iosize, NULL) == -1) {
6775184Sek110237 			filebench_log(LOG_ERROR,
6785184Sek110237 			    "file size smaller than IO size for thread %s",
6795184Sek110237 			    flowop->fo_name);
6806084Saw148015 			return (FILEBENCH_ERROR);
6815184Sek110237 		}
6825184Sek110237 
6835184Sek110237 		aiolist = aio_allocate(flowop);
6845184Sek110237 		aiolist->al_type = AL_WRITE;
6855184Sek110237 		aiocb = &aiolist->al_aiocb;
6865184Sek110237 
6875184Sek110237 		aiocb->aio_fildes = filedesc;
6885673Saw148015 		aiocb->aio_buf = iobuf;
6896212Saw148015 		aiocb->aio_nbytes = (size_t)iosize;
6905184Sek110237 		aiocb->aio_offset = (off64_t)fileoffset;
6915184Sek110237 		aiocb->aio_reqprio = 0;
6925184Sek110237 
6935184Sek110237 		filebench_log(LOG_DEBUG_IMPL,
6946286Saw148015 		    "aio fd=%d, bytes=%llu, offset=%llu",
6956286Saw148015 		    filedesc, (u_longlong_t)iosize, (u_longlong_t)fileoffset);
6965184Sek110237 
6975184Sek110237 		flowop_beginop(threadflow, flowop);
6985184Sek110237 		if (aio_write64(aiocb) < 0) {
6995184Sek110237 			filebench_log(LOG_ERROR, "aiowrite failed: %s",
7005184Sek110237 			    strerror(errno));
7015184Sek110237 			filebench_shutdown(1);
7025184Sek110237 		}
7036212Saw148015 		flowop_endop(threadflow, flowop, iosize);
7045184Sek110237 	} else {
7056084Saw148015 		return (FILEBENCH_ERROR);
7065184Sek110237 	}
7075184Sek110237 
7086084Saw148015 	return (FILEBENCH_OK);
7095184Sek110237 }
7105184Sek110237 
7115184Sek110237 
7125184Sek110237 
7135184Sek110237 #define	MAXREAP 4096
7145184Sek110237 
7155184Sek110237 /*
7165184Sek110237  * Emulate posix aiowait(). Waits for the completion of half the
7175184Sek110237  * outstanding asynchronous IOs, or a single IO, which ever is
7185184Sek110237  * larger. The routine will return after a sufficient number of
7195184Sek110237  * completed calls issued by any thread in the procflow have
7205184Sek110237  * completed, or a 1 second timout elapses. All completed
7215184Sek110237  * IO operations are deleted from the thread's aiolist.
7225184Sek110237  */
7235184Sek110237 static int
7245184Sek110237 flowoplib_aiowait(threadflow_t *threadflow, flowop_t *flowop)
7255184Sek110237 {
7265184Sek110237 	struct aiocb64 **worklist;
7275184Sek110237 	aiolist_t *aio = flowop->fo_thread->tf_aiolist;
7285184Sek110237 	int uncompleted = 0;
7295184Sek110237 
7305184Sek110237 	worklist = calloc(MAXREAP, sizeof (struct aiocb64 *));
7315184Sek110237 
7325184Sek110237 	/* Count the list of pending aios */
7335184Sek110237 	while (aio) {
7345184Sek110237 		uncompleted++;
7355184Sek110237 		aio = aio->al_next;
7365184Sek110237 	}
7375184Sek110237 
7385184Sek110237 	do {
7395184Sek110237 		uint_t ncompleted = 0;
7405184Sek110237 		uint_t todo;
7415184Sek110237 		struct timespec timeout;
7425184Sek110237 		int inprogress;
7435184Sek110237 		int i;
7445184Sek110237 
7455184Sek110237 		/* Wait for half of the outstanding requests */
7465184Sek110237 		timeout.tv_sec = 1;
7475184Sek110237 		timeout.tv_nsec = 0;
7485184Sek110237 
7495184Sek110237 		if (uncompleted > MAXREAP)
7505184Sek110237 			todo = MAXREAP;
7515184Sek110237 		else
7525184Sek110237 			todo = uncompleted / 2;
7535184Sek110237 
7545184Sek110237 		if (todo == 0)
7555184Sek110237 			todo = 1;
7565184Sek110237 
7575184Sek110237 		flowop_beginop(threadflow, flowop);
7585184Sek110237 
7595184Sek110237 #ifdef HAVE_AIOWAITN
7605184Sek110237 		if ((aio_waitn64((struct aiocb64 **)worklist,
7615184Sek110237 		    MAXREAP, &todo, &timeout) == -1) &&
7625184Sek110237 		    errno && (errno != ETIME)) {
7635184Sek110237 			filebench_log(LOG_ERROR,
7645184Sek110237 			    "aiowait failed: %s, outstanding = %d, "
7655184Sek110237 			    "ncompleted = %d ",
7665184Sek110237 			    strerror(errno), uncompleted, todo);
7675184Sek110237 		}
7685184Sek110237 
7695184Sek110237 		ncompleted = todo;
7705184Sek110237 		/* Take the  completed I/Os from the list */
7715184Sek110237 		inprogress = 0;
7725184Sek110237 		for (i = 0; i < ncompleted; i++) {
7735184Sek110237 			if ((aio_return64(worklist[i]) == -1) &&
7745184Sek110237 			    (errno == EINPROGRESS)) {
7755184Sek110237 				inprogress++;
7765184Sek110237 				continue;
7775184Sek110237 			}
7785184Sek110237 			if (aio_deallocate(flowop, worklist[i]) < 0) {
7795184Sek110237 				filebench_log(LOG_ERROR, "Could not remove "
7805184Sek110237 				    "aio from list ");
7815673Saw148015 				flowop_endop(threadflow, flowop, 0);
7826084Saw148015 				return (FILEBENCH_ERROR);
7835184Sek110237 			}
7845184Sek110237 		}
7855184Sek110237 
7865184Sek110237 		uncompleted -= ncompleted;
7875184Sek110237 		uncompleted += inprogress;
7885184Sek110237 
7895184Sek110237 #else
7905184Sek110237 
7915184Sek110237 		for (ncompleted = 0, inprogress = 0,
7925184Sek110237 		    aio = flowop->fo_thread->tf_aiolist;
7935184Sek110237 		    ncompleted < todo, aio != NULL; aio = aio->al_next) {
7946613Sek110237 			int result = aio_error64(&aio->al_aiocb);
7955184Sek110237 
7965184Sek110237 			if (result == EINPROGRESS) {
7975184Sek110237 				inprogress++;
7985184Sek110237 				continue;
7995184Sek110237 			}
8005184Sek110237 
8015184Sek110237 			if ((aio_return64(&aio->al_aiocb) == -1) || result) {
8025184Sek110237 				filebench_log(LOG_ERROR, "aio failed: %s",
8035184Sek110237 				    strerror(result));
8045184Sek110237 				continue;
8055184Sek110237 			}
8065184Sek110237 
8075184Sek110237 			ncompleted++;
8085184Sek110237 
8095184Sek110237 			if (aio_deallocate(flowop, &aio->al_aiocb) < 0) {
8105184Sek110237 				filebench_log(LOG_ERROR, "Could not remove aio "
8115184Sek110237 				    "from list ");
8125673Saw148015 				flowop_endop(threadflow, flowop, 0);
8136084Saw148015 				return (FILEBENCH_ERROR);
8145184Sek110237 			}
8155184Sek110237 		}
8165184Sek110237 
8175184Sek110237 		uncompleted -= ncompleted;
8185184Sek110237 
8195184Sek110237 #endif
8205184Sek110237 		filebench_log(LOG_DEBUG_SCRIPT,
8215184Sek110237 		    "aio2 completed %d ios, uncompleted = %d, inprogress = %d",
8225184Sek110237 		    ncompleted, uncompleted, inprogress);
8235184Sek110237 
8245184Sek110237 	} while (uncompleted > MAXREAP);
8255184Sek110237 
8265673Saw148015 	flowop_endop(threadflow, flowop, 0);
8275184Sek110237 
8285184Sek110237 	free(worklist);
8295184Sek110237 
8306084Saw148015 	return (FILEBENCH_OK);
8315184Sek110237 }
8325184Sek110237 
8335184Sek110237 #endif /* HAVE_AIO */
8345184Sek110237 
8355184Sek110237 /*
8365184Sek110237  * Initializes a "flowop_block" flowop. Specifically, it
8375184Sek110237  * initializes the flowop's fo_cv and unlocks the fo_lock.
8385184Sek110237  */
8395184Sek110237 static int
8405184Sek110237 flowoplib_block_init(flowop_t *flowop)
8415184Sek110237 {
8425184Sek110237 	filebench_log(LOG_DEBUG_IMPL, "flow %s-%d block init address %zx",
8435184Sek110237 	    flowop->fo_name, flowop->fo_instance, &flowop->fo_cv);
8445184Sek110237 	(void) pthread_cond_init(&flowop->fo_cv, ipc_condattr());
8455184Sek110237 	(void) ipc_mutex_unlock(&flowop->fo_lock);
8465184Sek110237 
8476084Saw148015 	return (FILEBENCH_OK);
8485184Sek110237 }
8495184Sek110237 
8505184Sek110237 /*
8515184Sek110237  * Blocks the threadflow until woken up by flowoplib_wakeup.
8525184Sek110237  * The routine blocks on the flowop's fo_cv condition variable.
8535184Sek110237  */
8545184Sek110237 static int
8555184Sek110237 flowoplib_block(threadflow_t *threadflow, flowop_t *flowop)
8565184Sek110237 {
8575184Sek110237 	filebench_log(LOG_DEBUG_IMPL, "flow %s-%d blocking at address %zx",
8585184Sek110237 	    flowop->fo_name, flowop->fo_instance, &flowop->fo_cv);
8595184Sek110237 	(void) ipc_mutex_lock(&flowop->fo_lock);
8605184Sek110237 
8615184Sek110237 	flowop_beginop(threadflow, flowop);
8625184Sek110237 	(void) pthread_cond_wait(&flowop->fo_cv, &flowop->fo_lock);
8635673Saw148015 	flowop_endop(threadflow, flowop, 0);
8645184Sek110237 
8655184Sek110237 	filebench_log(LOG_DEBUG_IMPL, "flow %s-%d unblocking",
8665184Sek110237 	    flowop->fo_name, flowop->fo_instance);
8675184Sek110237 
8685184Sek110237 	(void) ipc_mutex_unlock(&flowop->fo_lock);
8695184Sek110237 
8706084Saw148015 	return (FILEBENCH_OK);
8715184Sek110237 }
8725184Sek110237 
8735184Sek110237 /*
8745184Sek110237  * Wakes up one or more target blocking flowops.
8755184Sek110237  * Sends broadcasts on the fo_cv condition variables of all
8765184Sek110237  * flowops on the target list, except those that are
8775184Sek110237  * FLOW_MASTER flowops. The target list consists of all
8785184Sek110237  * flowops whose name matches this flowop's "fo_targetname"
8795184Sek110237  * attribute. The target list is generated on the first
8805184Sek110237  * invocation, and the run will be shutdown if no targets
8816084Saw148015  * are found. Otherwise the routine always returns FILEBENCH_OK.
8825184Sek110237  */
8835184Sek110237 static int
8845184Sek110237 flowoplib_wakeup(threadflow_t *threadflow, flowop_t *flowop)
8855184Sek110237 {
8865184Sek110237 	flowop_t *target;
8875184Sek110237 
8885184Sek110237 	/* if this is the first wakeup, create the wakeup list */
8895184Sek110237 	if (flowop->fo_targets == NULL) {
8905184Sek110237 		flowop_t *result = flowop_find(flowop->fo_targetname);
8915184Sek110237 
8925184Sek110237 		flowop->fo_targets = result;
8935184Sek110237 		if (result == NULL) {
8945184Sek110237 			filebench_log(LOG_ERROR,
8955184Sek110237 			    "wakeup: could not find op %s for thread %s",
8965184Sek110237 			    flowop->fo_targetname,
8975184Sek110237 			    threadflow->tf_name);
8985184Sek110237 			filebench_shutdown(1);
8995184Sek110237 		}
9005184Sek110237 		while (result) {
9015184Sek110237 			result->fo_targetnext =
9025184Sek110237 			    result->fo_resultnext;
9035184Sek110237 			result = result->fo_resultnext;
9045184Sek110237 		}
9055184Sek110237 	}
9065184Sek110237 
9075184Sek110237 	target = flowop->fo_targets;
9085184Sek110237 
9095184Sek110237 	/* wakeup the targets */
9105184Sek110237 	while (target) {
9115184Sek110237 		if (target->fo_instance == FLOW_MASTER) {
9125184Sek110237 			target = target->fo_targetnext;
9135184Sek110237 			continue;
9145184Sek110237 		}
9155184Sek110237 		filebench_log(LOG_DEBUG_IMPL,
9165184Sek110237 		    "wakeup flow %s-%d at address %zx",
9175184Sek110237 		    target->fo_name,
9185184Sek110237 		    target->fo_instance,
9195184Sek110237 		    &target->fo_cv);
9205184Sek110237 
9215184Sek110237 		flowop_beginop(threadflow, flowop);
9225184Sek110237 		(void) ipc_mutex_lock(&target->fo_lock);
9235184Sek110237 		(void) pthread_cond_broadcast(&target->fo_cv);
9245184Sek110237 		(void) ipc_mutex_unlock(&target->fo_lock);
9255673Saw148015 		flowop_endop(threadflow, flowop, 0);
9265184Sek110237 
9275184Sek110237 		target = target->fo_targetnext;
9285184Sek110237 	}
9295184Sek110237 
9306084Saw148015 	return (FILEBENCH_OK);
9315184Sek110237 }
9325184Sek110237 
9335184Sek110237 /*
9345184Sek110237  * "think time" routines. the "hog" routine consumes cpu cycles as
9355184Sek110237  * it "thinks", while the "delay" flowop simply calls sleep() to delay
9365184Sek110237  * for a given number of seconds without consuming cpu cycles.
9375184Sek110237  */
9385184Sek110237 
9395184Sek110237 
9405184Sek110237 /*
9415184Sek110237  * Consumes CPU cycles and memory bandwidth by looping for
9425184Sek110237  * flowop->fo_value times. With each loop sets memory location
9435184Sek110237  * threadflow->tf_mem to 1.
9445184Sek110237  */
9455184Sek110237 static int
9465184Sek110237 flowoplib_hog(threadflow_t *threadflow, flowop_t *flowop)
9475184Sek110237 {
9486212Saw148015 	uint64_t value = avd_get_int(flowop->fo_value);
9495184Sek110237 	int i;
9505184Sek110237 
9515673Saw148015 	filebench_log(LOG_DEBUG_IMPL, "hog enter");
9525184Sek110237 	flowop_beginop(threadflow, flowop);
9535673Saw148015 	if (threadflow->tf_mem != NULL) {
9545673Saw148015 		for (i = 0; i < value; i++)
9555673Saw148015 			*(threadflow->tf_mem) = 1;
9565673Saw148015 	}
9575673Saw148015 	flowop_endop(threadflow, flowop, 0);
9585184Sek110237 	filebench_log(LOG_DEBUG_IMPL, "hog exit");
9596084Saw148015 	return (FILEBENCH_OK);
9605184Sek110237 }
9615184Sek110237 
9625184Sek110237 
9635184Sek110237 /*
9645184Sek110237  * Delays for fo_value seconds.
9655184Sek110237  */
9665184Sek110237 static int
9675184Sek110237 flowoplib_delay(threadflow_t *threadflow, flowop_t *flowop)
9685184Sek110237 {
9696212Saw148015 	int value = avd_get_int(flowop->fo_value);
9705184Sek110237 
9715184Sek110237 	flowop_beginop(threadflow, flowop);
9725184Sek110237 	(void) sleep(value);
9735673Saw148015 	flowop_endop(threadflow, flowop, 0);
9746084Saw148015 	return (FILEBENCH_OK);
9755184Sek110237 }
9765184Sek110237 
9775184Sek110237 /*
9785184Sek110237  * Rate limiting routines. This is the event consuming half of the
9795184Sek110237  * event system. Each of the four following routines will limit the rate
9805184Sek110237  * to one unit of either calls, issued I/O operations, issued filebench
9815184Sek110237  * operations, or I/O bandwidth. Since there is only one event generator,
9825184Sek110237  * the events will be divided amoung multiple instances of an event
9835184Sek110237  * consumer, and further divided among different consumers if more than
9845184Sek110237  * one has been defined. There is no mechanism to enforce equal sharing
9855184Sek110237  * of events.
9865184Sek110237  */
9875184Sek110237 
9885184Sek110237 /*
9895184Sek110237  * Completes one invocation per posted event. If eventgen_q
9905184Sek110237  * has an event count greater than zero, one will be removed
9915184Sek110237  * (count decremented), otherwise the calling thread will
9925184Sek110237  * block until another event has been posted. Always returns 0
9935184Sek110237  */
9945184Sek110237 static int
9955184Sek110237 flowoplib_eventlimit(threadflow_t *threadflow, flowop_t *flowop)
9965184Sek110237 {
9975184Sek110237 	/* Immediately bail if not set/enabled */
9986391Saw148015 	if (filebench_shm->shm_eventgen_hz == 0)
9996084Saw148015 		return (FILEBENCH_OK);
10005184Sek110237 
10015184Sek110237 	if (flowop->fo_initted == 0) {
10025184Sek110237 		filebench_log(LOG_DEBUG_IMPL, "rate %zx %s-%d locking",
10035184Sek110237 		    flowop, threadflow->tf_name, threadflow->tf_instance);
10045184Sek110237 		flowop->fo_initted = 1;
10055184Sek110237 	}
10065184Sek110237 
10075184Sek110237 	flowop_beginop(threadflow, flowop);
10086391Saw148015 	while (filebench_shm->shm_eventgen_hz) {
10096391Saw148015 		(void) ipc_mutex_lock(&filebench_shm->shm_eventgen_lock);
10106391Saw148015 		if (filebench_shm->shm_eventgen_q > 0) {
10116391Saw148015 			filebench_shm->shm_eventgen_q--;
10126391Saw148015 			(void) ipc_mutex_unlock(
10136391Saw148015 			    &filebench_shm->shm_eventgen_lock);
10145184Sek110237 			break;
10155184Sek110237 		}
10166391Saw148015 		(void) pthread_cond_wait(&filebench_shm->shm_eventgen_cv,
10176391Saw148015 		    &filebench_shm->shm_eventgen_lock);
10186391Saw148015 		(void) ipc_mutex_unlock(&filebench_shm->shm_eventgen_lock);
10195184Sek110237 	}
10205673Saw148015 	flowop_endop(threadflow, flowop, 0);
10216084Saw148015 	return (FILEBENCH_OK);
10225184Sek110237 }
10235184Sek110237 
1024*6701Saw148015 static int
1025*6701Saw148015 flowoplib_event_find_target(threadflow_t *threadflow, flowop_t *flowop)
1026*6701Saw148015 {
1027*6701Saw148015 	if (flowop->fo_targetname[0] != '\0') {
1028*6701Saw148015 
1029*6701Saw148015 		/* Try to use statistics from specific flowop */
1030*6701Saw148015 		flowop->fo_targets =
1031*6701Saw148015 		    flowop_find_from_list(flowop->fo_targetname,
1032*6701Saw148015 		    threadflow->tf_thrd_fops);
1033*6701Saw148015 		if (flowop->fo_targets == NULL) {
1034*6701Saw148015 			filebench_log(LOG_ERROR,
1035*6701Saw148015 			    "limit target: could not find flowop %s",
1036*6701Saw148015 			    flowop->fo_targetname);
1037*6701Saw148015 			filebench_shutdown(1);
1038*6701Saw148015 			return (FILEBENCH_ERROR);
1039*6701Saw148015 		}
1040*6701Saw148015 	} else {
1041*6701Saw148015 		/* use total workload statistics */
1042*6701Saw148015 		flowop->fo_targets = NULL;
1043*6701Saw148015 	}
1044*6701Saw148015 	return (FILEBENCH_OK);
1045*6701Saw148015 }
1046*6701Saw148015 
10475184Sek110237 /*
10485184Sek110237  * Blocks the calling thread if the number of issued I/O
10495184Sek110237  * operations exceeds the number of posted events, thus
10505184Sek110237  * limiting the average I/O operation rate to the rate
10516084Saw148015  * specified by eventgen_hz. Always returns FILEBENCH_OK.
10525184Sek110237  */
10535184Sek110237 static int
10545184Sek110237 flowoplib_iopslimit(threadflow_t *threadflow, flowop_t *flowop)
10555184Sek110237 {
10565184Sek110237 	uint64_t iops;
10575184Sek110237 	uint64_t delta;
10585673Saw148015 	uint64_t events;
10595184Sek110237 
10605184Sek110237 	/* Immediately bail if not set/enabled */
10616391Saw148015 	if (filebench_shm->shm_eventgen_hz == 0)
10626084Saw148015 		return (FILEBENCH_OK);
10635184Sek110237 
10645184Sek110237 	if (flowop->fo_initted == 0) {
10655184Sek110237 		filebench_log(LOG_DEBUG_IMPL, "rate %zx %s-%d locking",
10665184Sek110237 		    flowop, threadflow->tf_name, threadflow->tf_instance);
10675184Sek110237 		flowop->fo_initted = 1;
1068*6701Saw148015 
1069*6701Saw148015 		if (flowoplib_event_find_target(threadflow, flowop)
1070*6701Saw148015 		    == FILEBENCH_ERROR)
1071*6701Saw148015 			return (FILEBENCH_ERROR);
1072*6701Saw148015 
1073*6701Saw148015 		if (flowop->fo_targets && ((flowop->fo_targets->fo_attrs &
1074*6701Saw148015 		    (FLOW_ATTR_READ | FLOW_ATTR_WRITE)) == 0)) {
1075*6701Saw148015 			filebench_log(LOG_ERROR,
1076*6701Saw148015 			    "WARNING: Flowop %s does no IO",
1077*6701Saw148015 			    flowop->fo_targets->fo_name);
1078*6701Saw148015 			filebench_shutdown(1);
1079*6701Saw148015 			return (FILEBENCH_ERROR);
1080*6701Saw148015 		}
10815184Sek110237 	}
10825184Sek110237 
1083*6701Saw148015 	if (flowop->fo_targets) {
1084*6701Saw148015 		/*
1085*6701Saw148015 		 * Note that fs_count is already the sum of fs_rcount
1086*6701Saw148015 		 * and fs_wcount if looking at a single flowop.
1087*6701Saw148015 		 */
1088*6701Saw148015 		iops = flowop->fo_targets->fo_stats.fs_count;
1089*6701Saw148015 	} else {
1090*6701Saw148015 		(void) ipc_mutex_lock(&controlstats_lock);
1091*6701Saw148015 		iops = (controlstats.fs_rcount +
1092*6701Saw148015 		    controlstats.fs_wcount);
1093*6701Saw148015 		(void) ipc_mutex_unlock(&controlstats_lock);
1094*6701Saw148015 	}
10955184Sek110237 
10965184Sek110237 	/* Is this the first time around */
10975184Sek110237 	if (flowop->fo_tputlast == 0) {
10985184Sek110237 		flowop->fo_tputlast = iops;
10996084Saw148015 		return (FILEBENCH_OK);
11005184Sek110237 	}
11015184Sek110237 
11025184Sek110237 	delta = iops - flowop->fo_tputlast;
11035184Sek110237 	flowop->fo_tputbucket -= delta;
11045184Sek110237 	flowop->fo_tputlast = iops;
11055184Sek110237 
11065184Sek110237 	/* No need to block if the q isn't empty */
11075184Sek110237 	if (flowop->fo_tputbucket >= 0LL) {
11085673Saw148015 		flowop_endop(threadflow, flowop, 0);
11096084Saw148015 		return (FILEBENCH_OK);
11105184Sek110237 	}
11115184Sek110237 
11125184Sek110237 	iops = flowop->fo_tputbucket * -1;
11135184Sek110237 	events = iops;
11145184Sek110237 
11155184Sek110237 	flowop_beginop(threadflow, flowop);
11166391Saw148015 	while (filebench_shm->shm_eventgen_hz) {
11175184Sek110237 
11186391Saw148015 		(void) ipc_mutex_lock(&filebench_shm->shm_eventgen_lock);
11196391Saw148015 		if (filebench_shm->shm_eventgen_q >= events) {
11206391Saw148015 			filebench_shm->shm_eventgen_q -= events;
11216391Saw148015 			(void) ipc_mutex_unlock(
11226391Saw148015 			    &filebench_shm->shm_eventgen_lock);
11235184Sek110237 			flowop->fo_tputbucket += events;
11245184Sek110237 			break;
11255184Sek110237 		}
11266391Saw148015 		(void) pthread_cond_wait(&filebench_shm->shm_eventgen_cv,
11276391Saw148015 		    &filebench_shm->shm_eventgen_lock);
11286391Saw148015 		(void) ipc_mutex_unlock(&filebench_shm->shm_eventgen_lock);
11295184Sek110237 	}
11305673Saw148015 	flowop_endop(threadflow, flowop, 0);
11315184Sek110237 
11326084Saw148015 	return (FILEBENCH_OK);
11335184Sek110237 }
11345184Sek110237 
11355184Sek110237 /*
11365184Sek110237  * Blocks the calling thread if the number of issued filebench
11375184Sek110237  * operations exceeds the number of posted events, thus limiting
11385184Sek110237  * the average filebench operation rate to the rate specified by
11396084Saw148015  * eventgen_hz. Always returns FILEBENCH_OK.
11405184Sek110237  */
11415184Sek110237 static int
11425184Sek110237 flowoplib_opslimit(threadflow_t *threadflow, flowop_t *flowop)
11435184Sek110237 {
11445184Sek110237 	uint64_t ops;
11455184Sek110237 	uint64_t delta;
11465673Saw148015 	uint64_t events;
11475184Sek110237 
11485184Sek110237 	/* Immediately bail if not set/enabled */
11496391Saw148015 	if (filebench_shm->shm_eventgen_hz == 0)
11506084Saw148015 		return (FILEBENCH_OK);
11515184Sek110237 
11525184Sek110237 	if (flowop->fo_initted == 0) {
11535184Sek110237 		filebench_log(LOG_DEBUG_IMPL, "rate %zx %s-%d locking",
11545184Sek110237 		    flowop, threadflow->tf_name, threadflow->tf_instance);
11555184Sek110237 		flowop->fo_initted = 1;
1156*6701Saw148015 
1157*6701Saw148015 		if (flowoplib_event_find_target(threadflow, flowop)
1158*6701Saw148015 		    == FILEBENCH_ERROR)
1159*6701Saw148015 			return (FILEBENCH_ERROR);
11605184Sek110237 	}
11615184Sek110237 
1162*6701Saw148015 	if (flowop->fo_targets) {
1163*6701Saw148015 		ops = flowop->fo_targets->fo_stats.fs_count;
1164*6701Saw148015 	} else {
1165*6701Saw148015 		(void) ipc_mutex_lock(&controlstats_lock);
1166*6701Saw148015 		ops = controlstats.fs_count;
1167*6701Saw148015 		(void) ipc_mutex_unlock(&controlstats_lock);
1168*6701Saw148015 	}
11695184Sek110237 
11705184Sek110237 	/* Is this the first time around */
11715184Sek110237 	if (flowop->fo_tputlast == 0) {
11725184Sek110237 		flowop->fo_tputlast = ops;
11736084Saw148015 		return (FILEBENCH_OK);
11745184Sek110237 	}
11755184Sek110237 
11765184Sek110237 	delta = ops - flowop->fo_tputlast;
11775184Sek110237 	flowop->fo_tputbucket -= delta;
11785184Sek110237 	flowop->fo_tputlast = ops;
11795184Sek110237 
11805184Sek110237 	/* No need to block if the q isn't empty */
11815184Sek110237 	if (flowop->fo_tputbucket >= 0LL) {
11825673Saw148015 		flowop_endop(threadflow, flowop, 0);
11836084Saw148015 		return (FILEBENCH_OK);
11845184Sek110237 	}
11855184Sek110237 
11865184Sek110237 	ops = flowop->fo_tputbucket * -1;
11875184Sek110237 	events = ops;
11885184Sek110237 
11895184Sek110237 	flowop_beginop(threadflow, flowop);
11906391Saw148015 	while (filebench_shm->shm_eventgen_hz) {
11916391Saw148015 		(void) ipc_mutex_lock(&filebench_shm->shm_eventgen_lock);
11926391Saw148015 		if (filebench_shm->shm_eventgen_q >= events) {
11936391Saw148015 			filebench_shm->shm_eventgen_q -= events;
11946391Saw148015 			(void) ipc_mutex_unlock(
11956391Saw148015 			    &filebench_shm->shm_eventgen_lock);
11965184Sek110237 			flowop->fo_tputbucket += events;
11975184Sek110237 			break;
11985184Sek110237 		}
11996391Saw148015 		(void) pthread_cond_wait(&filebench_shm->shm_eventgen_cv,
12006391Saw148015 		    &filebench_shm->shm_eventgen_lock);
12016391Saw148015 		(void) ipc_mutex_unlock(&filebench_shm->shm_eventgen_lock);
12025184Sek110237 	}
12035673Saw148015 	flowop_endop(threadflow, flowop, 0);
12045184Sek110237 
12056084Saw148015 	return (FILEBENCH_OK);
12065184Sek110237 }
12075184Sek110237 
12085184Sek110237 
12095184Sek110237 /*
12105184Sek110237  * Blocks the calling thread if the number of bytes of I/O
12115184Sek110237  * issued exceeds one megabyte times the number of posted
12125184Sek110237  * events, thus limiting the average I/O byte rate to one
12135184Sek110237  * megabyte times the event rate as set by eventgen_hz.
12146084Saw148015  * Always retuns FILEBENCH_OK.
12155184Sek110237  */
12165184Sek110237 static int
12175184Sek110237 flowoplib_bwlimit(threadflow_t *threadflow, flowop_t *flowop)
12185184Sek110237 {
12195184Sek110237 	uint64_t bytes;
12205184Sek110237 	uint64_t delta;
12215673Saw148015 	uint64_t events;
12225184Sek110237 
12235184Sek110237 	/* Immediately bail if not set/enabled */
12246391Saw148015 	if (filebench_shm->shm_eventgen_hz == 0)
12256084Saw148015 		return (FILEBENCH_OK);
12265184Sek110237 
12275184Sek110237 	if (flowop->fo_initted == 0) {
12285184Sek110237 		filebench_log(LOG_DEBUG_IMPL, "rate %zx %s-%d locking",
12295184Sek110237 		    flowop, threadflow->tf_name, threadflow->tf_instance);
12305184Sek110237 		flowop->fo_initted = 1;
1231*6701Saw148015 
1232*6701Saw148015 		if (flowoplib_event_find_target(threadflow, flowop)
1233*6701Saw148015 		    == FILEBENCH_ERROR)
1234*6701Saw148015 			return (FILEBENCH_ERROR);
1235*6701Saw148015 
1236*6701Saw148015 		if ((flowop->fo_targets) &&
1237*6701Saw148015 		    ((flowop->fo_targets->fo_attrs &
1238*6701Saw148015 		    (FLOW_ATTR_READ | FLOW_ATTR_WRITE)) == 0)) {
1239*6701Saw148015 			filebench_log(LOG_ERROR,
1240*6701Saw148015 			    "WARNING: Flowop %s does no Reads or Writes",
1241*6701Saw148015 			    flowop->fo_targets->fo_name);
1242*6701Saw148015 			filebench_shutdown(1);
1243*6701Saw148015 			return (FILEBENCH_ERROR);
1244*6701Saw148015 		}
12455184Sek110237 	}
12465184Sek110237 
1247*6701Saw148015 	if (flowop->fo_targets) {
1248*6701Saw148015 		/*
1249*6701Saw148015 		 * Note that fs_bytes is already the sum of fs_rbytes
1250*6701Saw148015 		 * and fs_wbytes if looking at a single flowop.
1251*6701Saw148015 		 */
1252*6701Saw148015 		bytes = flowop->fo_targets->fo_stats.fs_bytes;
1253*6701Saw148015 	} else {
1254*6701Saw148015 		(void) ipc_mutex_lock(&controlstats_lock);
1255*6701Saw148015 		bytes = (controlstats.fs_rbytes +
1256*6701Saw148015 		    controlstats.fs_wbytes);
1257*6701Saw148015 		(void) ipc_mutex_unlock(&controlstats_lock);
1258*6701Saw148015 	}
1259*6701Saw148015 
1260*6701Saw148015 	/* Is this the first time around? */
12615184Sek110237 	if (flowop->fo_tputlast == 0) {
12625184Sek110237 		flowop->fo_tputlast = bytes;
12636084Saw148015 		return (FILEBENCH_OK);
12645184Sek110237 	}
12655184Sek110237 
12665184Sek110237 	delta = bytes - flowop->fo_tputlast;
12675184Sek110237 	flowop->fo_tputbucket -= delta;
12685184Sek110237 	flowop->fo_tputlast = bytes;
12695184Sek110237 
12705184Sek110237 	/* No need to block if the q isn't empty */
12715184Sek110237 	if (flowop->fo_tputbucket >= 0LL) {
12725673Saw148015 		flowop_endop(threadflow, flowop, 0);
12736084Saw148015 		return (FILEBENCH_OK);
12745184Sek110237 	}
12755184Sek110237 
12765184Sek110237 	bytes = flowop->fo_tputbucket * -1;
12775184Sek110237 	events = (bytes / MB) + 1;
12785184Sek110237 
12796286Saw148015 	filebench_log(LOG_DEBUG_IMPL, "%llu bytes, %llu events",
12806286Saw148015 	    (u_longlong_t)bytes, (u_longlong_t)events);
12815184Sek110237 
12825184Sek110237 	flowop_beginop(threadflow, flowop);
12836391Saw148015 	while (filebench_shm->shm_eventgen_hz) {
12846391Saw148015 		(void) ipc_mutex_lock(&filebench_shm->shm_eventgen_lock);
12856391Saw148015 		if (filebench_shm->shm_eventgen_q >= events) {
12866391Saw148015 			filebench_shm->shm_eventgen_q -= events;
12876391Saw148015 			(void) ipc_mutex_unlock(
12886391Saw148015 			    &filebench_shm->shm_eventgen_lock);
12895184Sek110237 			flowop->fo_tputbucket += (events * MB);
12905184Sek110237 			break;
12915184Sek110237 		}
12926391Saw148015 		(void) pthread_cond_wait(&filebench_shm->shm_eventgen_cv,
12936391Saw148015 		    &filebench_shm->shm_eventgen_lock);
12946391Saw148015 		(void) ipc_mutex_unlock(&filebench_shm->shm_eventgen_lock);
12955184Sek110237 	}
12965673Saw148015 	flowop_endop(threadflow, flowop, 0);
12975184Sek110237 
12986084Saw148015 	return (FILEBENCH_OK);
12995184Sek110237 }
13005184Sek110237 
13015184Sek110237 /*
13025184Sek110237  * These flowops terminate a benchmark run when either the specified
13035184Sek110237  * number of bytes of I/O (flowoplib_finishonbytes) or the specified
13045184Sek110237  * number of I/O operations (flowoplib_finishoncount) have been generated.
13055184Sek110237  */
13065184Sek110237 
13075184Sek110237 
13085184Sek110237 /*
13095184Sek110237  * Stop filebench run when specified number of I/O bytes have been
13106212Saw148015  * transferred. Compares controlstats.fs_bytes with flowop->value,
13115184Sek110237  * and if greater returns 1, stopping the run, if not, returns 0
13125184Sek110237  * to continue running.
13135184Sek110237  */
13145184Sek110237 static int
13155184Sek110237 flowoplib_finishonbytes(threadflow_t *threadflow, flowop_t *flowop)
13165184Sek110237 {
1317*6701Saw148015 	uint64_t bytes_io;		/* Bytes of I/O delivered so far */
1318*6701Saw148015 	uint64_t byte_lim = flowop->fo_constvalue;  /* Total Bytes desired */
1319*6701Saw148015 						    /* Uses constant value */
1320*6701Saw148015 
1321*6701Saw148015 	if (flowop->fo_initted == 0) {
1322*6701Saw148015 		filebench_log(LOG_DEBUG_IMPL, "rate %zx %s-%d locking",
1323*6701Saw148015 		    flowop, threadflow->tf_name, threadflow->tf_instance);
1324*6701Saw148015 		flowop->fo_initted = 1;
1325*6701Saw148015 
1326*6701Saw148015 		if (flowoplib_event_find_target(threadflow, flowop)
1327*6701Saw148015 		    == FILEBENCH_ERROR)
1328*6701Saw148015 			return (FILEBENCH_ERROR);
1329*6701Saw148015 
1330*6701Saw148015 		if ((flowop->fo_targets) &&
1331*6701Saw148015 		    ((flowop->fo_targets->fo_attrs &
1332*6701Saw148015 		    (FLOW_ATTR_READ | FLOW_ATTR_WRITE)) == 0)) {
1333*6701Saw148015 			filebench_log(LOG_ERROR,
1334*6701Saw148015 			    "WARNING: Flowop %s does no Reads or Writes",
1335*6701Saw148015 			    flowop->fo_targets->fo_name);
1336*6701Saw148015 			filebench_shutdown(1);
1337*6701Saw148015 			return (FILEBENCH_ERROR);
1338*6701Saw148015 		}
1339*6701Saw148015 	}
1340*6701Saw148015 
1341*6701Saw148015 	if (flowop->fo_targets) {
1342*6701Saw148015 		bytes_io = flowop->fo_targets->fo_stats.fs_bytes;
1343*6701Saw148015 	} else {
1344*6701Saw148015 		(void) ipc_mutex_lock(&controlstats_lock);
1345*6701Saw148015 		bytes_io = controlstats.fs_bytes;
1346*6701Saw148015 		(void) ipc_mutex_unlock(&controlstats_lock);
1347*6701Saw148015 	}
13485184Sek110237 
13495184Sek110237 	flowop_beginop(threadflow, flowop);
1350*6701Saw148015 	if (bytes_io > byte_lim) {
13515673Saw148015 		flowop_endop(threadflow, flowop, 0);
13526084Saw148015 		return (FILEBENCH_DONE);
13535184Sek110237 	}
13545673Saw148015 	flowop_endop(threadflow, flowop, 0);
13555184Sek110237 
13566084Saw148015 	return (FILEBENCH_OK);
13575184Sek110237 }
13585184Sek110237 
13595184Sek110237 /*
13605184Sek110237  * Stop filebench run when specified number of I/O operations have
13615184Sek110237  * been performed. Compares controlstats.fs_count with *flowop->value,
13626084Saw148015  * and if greater returns 1, stopping the run, if not, returns FILEBENCH_OK
13636084Saw148015  * to continue running.
13645184Sek110237  */
13655184Sek110237 static int
13665184Sek110237 flowoplib_finishoncount(threadflow_t *threadflow, flowop_t *flowop)
13675184Sek110237 {
13685184Sek110237 	uint64_t ops;
13696212Saw148015 	uint64_t count = flowop->fo_constvalue; /* use constant value */
13705184Sek110237 
1371*6701Saw148015 	if (flowop->fo_initted == 0) {
1372*6701Saw148015 		filebench_log(LOG_DEBUG_IMPL, "rate %zx %s-%d locking",
1373*6701Saw148015 		    flowop, threadflow->tf_name, threadflow->tf_instance);
1374*6701Saw148015 		flowop->fo_initted = 1;
1375*6701Saw148015 
1376*6701Saw148015 		if (flowoplib_event_find_target(threadflow, flowop)
1377*6701Saw148015 		    == FILEBENCH_ERROR)
1378*6701Saw148015 			return (FILEBENCH_ERROR);
1379*6701Saw148015 	}
1380*6701Saw148015 
1381*6701Saw148015 	if (flowop->fo_targets) {
1382*6701Saw148015 		ops = flowop->fo_targets->fo_stats.fs_count;
1383*6701Saw148015 	} else {
1384*6701Saw148015 		(void) ipc_mutex_lock(&controlstats_lock);
1385*6701Saw148015 		ops = controlstats.fs_count;
1386*6701Saw148015 		(void) ipc_mutex_unlock(&controlstats_lock);
1387*6701Saw148015 	}
13885184Sek110237 
13895184Sek110237 	flowop_beginop(threadflow, flowop);
13906084Saw148015 	if (ops >= count) {
13915673Saw148015 		flowop_endop(threadflow, flowop, 0);
13926084Saw148015 		return (FILEBENCH_DONE);
13935184Sek110237 	}
13945673Saw148015 	flowop_endop(threadflow, flowop, 0);
13955184Sek110237 
13966084Saw148015 	return (FILEBENCH_OK);
13975184Sek110237 }
13985184Sek110237 
13995184Sek110237 /*
14005184Sek110237  * Semaphore synchronization using either System V semaphores or
14015184Sek110237  * posix semaphores. If System V semaphores are available, they will be
14025184Sek110237  * used, otherwise posix semaphores will be used.
14035184Sek110237  */
14045184Sek110237 
14055184Sek110237 
14065184Sek110237 /*
14075184Sek110237  * Initializes the filebench "block on semaphore" flowop.
14085184Sek110237  * If System V semaphores are implemented, the routine
14095184Sek110237  * initializes the System V semaphore subsystem if it hasn't
14105184Sek110237  * already been initialized, also allocates a pair of semids
14115184Sek110237  * and initializes the highwater System V semaphore.
14125184Sek110237  * If no System V semaphores, then does nothing special.
14136084Saw148015  * Returns FILEBENCH_ERROR if it cannot acquire a set of System V semphores
14146084Saw148015  * or if the initial post to the semaphore set fails. Returns FILEBENCH_OK
14155184Sek110237  * on success.
14165184Sek110237  */
14175184Sek110237 static int
14185184Sek110237 flowoplib_semblock_init(flowop_t *flowop)
14195184Sek110237 {
14205184Sek110237 
14215184Sek110237 #ifdef HAVE_SYSV_SEM
14226391Saw148015 	int sys_semid;
14235184Sek110237 	struct sembuf sbuf[2];
14245184Sek110237 	int highwater;
14255184Sek110237 
14265184Sek110237 	ipc_seminit();
14275184Sek110237 
14285184Sek110237 	flowop->fo_semid_lw = ipc_semidalloc();
14295184Sek110237 	flowop->fo_semid_hw = ipc_semidalloc();
14305184Sek110237 
14315184Sek110237 	filebench_log(LOG_DEBUG_IMPL, "flow %s-%d semblock init semid=%x",
14325184Sek110237 	    flowop->fo_name, flowop->fo_instance, flowop->fo_semid_lw);
14335184Sek110237 
14346391Saw148015 	sys_semid = filebench_shm->shm_sys_semid;
14355184Sek110237 
14365184Sek110237 	if ((highwater = flowop->fo_semid_hw) == 0)
14376212Saw148015 		highwater = flowop->fo_constvalue; /* use constant value */
14385184Sek110237 
14395184Sek110237 	filebench_log(LOG_DEBUG_IMPL, "setting highwater to : %d", highwater);
14405184Sek110237 
14415673Saw148015 	sbuf[0].sem_num = (short)highwater;
14426212Saw148015 	sbuf[0].sem_op = avd_get_int(flowop->fo_highwater);
14435184Sek110237 	sbuf[0].sem_flg = 0;
14446391Saw148015 	if ((semop(sys_semid, &sbuf[0], 1) == -1) && errno) {
14455184Sek110237 		filebench_log(LOG_ERROR, "semblock init post failed: %s (%d,"
14465184Sek110237 		    "%d)", strerror(errno), sbuf[0].sem_num, sbuf[0].sem_op);
14476084Saw148015 		return (FILEBENCH_ERROR);
14485184Sek110237 	}
14495184Sek110237 #else
14505184Sek110237 	filebench_log(LOG_DEBUG_IMPL,
14515184Sek110237 	    "flow %s-%d semblock init with posix semaphore",
14525184Sek110237 	    flowop->fo_name, flowop->fo_instance);
14535184Sek110237 
14545184Sek110237 	sem_init(&flowop->fo_sem, 1, 0);
14555184Sek110237 #endif	/* HAVE_SYSV_SEM */
14565184Sek110237 
14576212Saw148015 	if (!(avd_get_bool(flowop->fo_blocking)))
14585184Sek110237 		(void) ipc_mutex_unlock(&flowop->fo_lock);
14595184Sek110237 
14606084Saw148015 	return (FILEBENCH_OK);
14615184Sek110237 }
14625184Sek110237 
14635184Sek110237 /*
14645184Sek110237  * Releases the semids for the System V semaphore allocated
14655184Sek110237  * to this flowop. If not using System V semaphores, then
14666084Saw148015  * it is effectively just a no-op.
14675184Sek110237  */
14685184Sek110237 static void
14695184Sek110237 flowoplib_semblock_destruct(flowop_t *flowop)
14705184Sek110237 {
14715184Sek110237 #ifdef HAVE_SYSV_SEM
14725184Sek110237 	ipc_semidfree(flowop->fo_semid_lw);
14735184Sek110237 	ipc_semidfree(flowop->fo_semid_hw);
14746391Saw148015 	(void) semctl(filebench_shm->shm_sys_semid, 0, IPC_RMID);
14756391Saw148015 	filebench_shm->shm_sys_semid = -1;
14765184Sek110237 #else
14775184Sek110237 	sem_destroy(&flowop->fo_sem);
14785184Sek110237 #endif /* HAVE_SYSV_SEM */
14795184Sek110237 }
14805184Sek110237 
14815184Sek110237 /*
14825184Sek110237  * Attempts to pass a System V or posix semaphore as appropriate,
14836084Saw148015  * and blocks if necessary. Returns FILEBENCH_ERROR if a set of System V
14845184Sek110237  * semphores is not available or cannot be acquired, or if the initial
14856084Saw148015  * post to the semaphore set fails. Returns FILEBENCH_OK on success.
14865184Sek110237  */
14875184Sek110237 static int
14885184Sek110237 flowoplib_semblock(threadflow_t *threadflow, flowop_t *flowop)
14895184Sek110237 {
14905184Sek110237 
14915184Sek110237 #ifdef HAVE_SYSV_SEM
14925184Sek110237 	struct sembuf sbuf[2];
14936212Saw148015 	int value = avd_get_int(flowop->fo_value);
14946391Saw148015 	int sys_semid;
14955184Sek110237 	struct timespec timeout;
14965184Sek110237 
14976391Saw148015 	sys_semid = filebench_shm->shm_sys_semid;
14985184Sek110237 
14995184Sek110237 	filebench_log(LOG_DEBUG_IMPL,
15005184Sek110237 	    "flow %s-%d sem blocking on id %x num %x value %d",
15016391Saw148015 	    flowop->fo_name, flowop->fo_instance, sys_semid,
15025184Sek110237 	    flowop->fo_semid_hw, value);
15035184Sek110237 
15045184Sek110237 	/* Post, decrement the increment the hw queue */
15055184Sek110237 	sbuf[0].sem_num = flowop->fo_semid_hw;
15065673Saw148015 	sbuf[0].sem_op = (short)value;
15075184Sek110237 	sbuf[0].sem_flg = 0;
15085184Sek110237 	sbuf[1].sem_num = flowop->fo_semid_lw;
15095184Sek110237 	sbuf[1].sem_op = value * -1;
15105184Sek110237 	sbuf[1].sem_flg = 0;
15115184Sek110237 	timeout.tv_sec = 600;
15125184Sek110237 	timeout.tv_nsec = 0;
15135184Sek110237 
15146212Saw148015 	if (avd_get_bool(flowop->fo_blocking))
15155184Sek110237 		(void) ipc_mutex_unlock(&flowop->fo_lock);
15165184Sek110237 
15175184Sek110237 	flowop_beginop(threadflow, flowop);
15185184Sek110237 
15195184Sek110237 #ifdef HAVE_SEMTIMEDOP
15206391Saw148015 	(void) semtimedop(sys_semid, &sbuf[0], 1, &timeout);
15216391Saw148015 	(void) semtimedop(sys_semid, &sbuf[1], 1, &timeout);
15225184Sek110237 #else
15236391Saw148015 	(void) semop(sys_semid, &sbuf[0], 1);
15246391Saw148015 	(void) semop(sys_semid, &sbuf[1], 1);
15255184Sek110237 #endif /* HAVE_SEMTIMEDOP */
15265184Sek110237 
15276212Saw148015 	if (avd_get_bool(flowop->fo_blocking))
15285184Sek110237 		(void) ipc_mutex_lock(&flowop->fo_lock);
15295184Sek110237 
15305673Saw148015 	flowop_endop(threadflow, flowop, 0);
15315184Sek110237 
15325184Sek110237 #else
15336212Saw148015 	int value = avd_get_int(flowop->fo_value);
15345184Sek110237 	int i;
15355184Sek110237 
15365184Sek110237 	filebench_log(LOG_DEBUG_IMPL,
15375184Sek110237 	    "flow %s-%d sem blocking on posix semaphore",
15385184Sek110237 	    flowop->fo_name, flowop->fo_instance);
15395184Sek110237 
15405184Sek110237 	/* Decrement sem by value */
15415184Sek110237 	for (i = 0; i < value; i++) {
15425184Sek110237 		if (sem_wait(&flowop->fo_sem) == -1) {
15435184Sek110237 			filebench_log(LOG_ERROR, "semop wait failed");
15446084Saw148015 			return (FILEBENCH_ERROR);
15455184Sek110237 		}
15465184Sek110237 	}
15475184Sek110237 
15485184Sek110237 	filebench_log(LOG_DEBUG_IMPL, "flow %s-%d sem unblocking",
15495184Sek110237 	    flowop->fo_name, flowop->fo_instance);
15505184Sek110237 #endif /* HAVE_SYSV_SEM */
15515184Sek110237 
15526084Saw148015 	return (FILEBENCH_OK);
15535184Sek110237 }
15545184Sek110237 
15555184Sek110237 /*
15566084Saw148015  * Calls ipc_seminit(). Always returns FILEBENCH_OK.
15575184Sek110237  */
15585184Sek110237 /* ARGSUSED */
15595184Sek110237 static int
15605184Sek110237 flowoplib_sempost_init(flowop_t *flowop)
15615184Sek110237 {
15625184Sek110237 #ifdef HAVE_SYSV_SEM
15635184Sek110237 	ipc_seminit();
15645184Sek110237 #endif /* HAVE_SYSV_SEM */
15656084Saw148015 	return (FILEBENCH_OK);
15665184Sek110237 }
15675184Sek110237 
15685184Sek110237 /*
15695184Sek110237  * Post to a System V or posix semaphore as appropriate.
15705184Sek110237  * On the first call for a given flowop instance, this routine
15715184Sek110237  * will use the fo_targetname attribute to locate all semblock
15725184Sek110237  * flowops that are expecting posts from this flowop. All
15735184Sek110237  * target flowops on this list will have a post operation done
15745184Sek110237  * to their semaphores on each call.
15755184Sek110237  */
15765184Sek110237 static int
15775184Sek110237 flowoplib_sempost(threadflow_t *threadflow, flowop_t *flowop)
15785184Sek110237 {
15795184Sek110237 	flowop_t *target;
15805184Sek110237 
15815184Sek110237 	filebench_log(LOG_DEBUG_IMPL,
15825184Sek110237 	    "sempost flow %s-%d",
15835184Sek110237 	    flowop->fo_name,
15845184Sek110237 	    flowop->fo_instance);
15855184Sek110237 
15865184Sek110237 	/* if this is the first post, create the post list */
15875184Sek110237 	if (flowop->fo_targets == NULL) {
15885184Sek110237 		flowop_t *result = flowop_find(flowop->fo_targetname);
15895184Sek110237 
15905184Sek110237 		flowop->fo_targets = result;
15915184Sek110237 
15925184Sek110237 		if (result == NULL) {
15935184Sek110237 			filebench_log(LOG_ERROR,
15945184Sek110237 			    "sempost: could not find op %s for thread %s",
15955184Sek110237 			    flowop->fo_targetname,
15965184Sek110237 			    threadflow->tf_name);
15975184Sek110237 			filebench_shutdown(1);
15985184Sek110237 		}
15995184Sek110237 
16005184Sek110237 		while (result) {
16015184Sek110237 			result->fo_targetnext =
16025184Sek110237 			    result->fo_resultnext;
16035184Sek110237 			result = result->fo_resultnext;
16045184Sek110237 		}
16055184Sek110237 	}
16065184Sek110237 
16075184Sek110237 	target = flowop->fo_targets;
16085184Sek110237 
16095184Sek110237 	flowop_beginop(threadflow, flowop);
16105184Sek110237 	/* post to the targets */
16115184Sek110237 	while (target) {
16125184Sek110237 #ifdef HAVE_SYSV_SEM
16135184Sek110237 		struct sembuf sbuf[2];
16146391Saw148015 		int sys_semid;
16155184Sek110237 		int blocking;
16165184Sek110237 #else
16175184Sek110237 		int i;
16185184Sek110237 #endif /* HAVE_SYSV_SEM */
16195184Sek110237 		struct timespec timeout;
16206550Saw148015 		int value = (int)avd_get_int(flowop->fo_value);
16215184Sek110237 
16225184Sek110237 		if (target->fo_instance == FLOW_MASTER) {
16235184Sek110237 			target = target->fo_targetnext;
16245184Sek110237 			continue;
16255184Sek110237 		}
16265184Sek110237 
16275184Sek110237 #ifdef HAVE_SYSV_SEM
16285184Sek110237 
16295184Sek110237 		filebench_log(LOG_DEBUG_IMPL,
16305184Sek110237 		    "sempost flow %s-%d num %x",
16315184Sek110237 		    target->fo_name,
16325184Sek110237 		    target->fo_instance,
16335184Sek110237 		    target->fo_semid_lw);
16345184Sek110237 
16356391Saw148015 		sys_semid = filebench_shm->shm_sys_semid;
16365184Sek110237 		sbuf[0].sem_num = target->fo_semid_lw;
16375673Saw148015 		sbuf[0].sem_op = (short)value;
16385184Sek110237 		sbuf[0].sem_flg = 0;
16395184Sek110237 		sbuf[1].sem_num = target->fo_semid_hw;
16405184Sek110237 		sbuf[1].sem_op = value * -1;
16415184Sek110237 		sbuf[1].sem_flg = 0;
16425184Sek110237 		timeout.tv_sec = 600;
16435184Sek110237 		timeout.tv_nsec = 0;
16445184Sek110237 
16456212Saw148015 		if (avd_get_bool(flowop->fo_blocking))
16465184Sek110237 			blocking = 1;
16475184Sek110237 		else
16485184Sek110237 			blocking = 0;
16495184Sek110237 
16505184Sek110237 #ifdef HAVE_SEMTIMEDOP
16516391Saw148015 		if ((semtimedop(sys_semid, &sbuf[0], blocking + 1,
16525184Sek110237 		    &timeout) == -1) && (errno && (errno != EAGAIN))) {
16535184Sek110237 #else
16546391Saw148015 		if ((semop(sys_semid, &sbuf[0], blocking + 1) == -1) &&
16555184Sek110237 		    (errno && (errno != EAGAIN))) {
16565184Sek110237 #endif /* HAVE_SEMTIMEDOP */
16575184Sek110237 			filebench_log(LOG_ERROR, "semop post failed: %s",
16585184Sek110237 			    strerror(errno));
16596084Saw148015 			return (FILEBENCH_ERROR);
16605184Sek110237 		}
16615184Sek110237 
16625184Sek110237 		filebench_log(LOG_DEBUG_IMPL,
16635184Sek110237 		    "flow %s-%d finished posting",
16645184Sek110237 		    target->fo_name, target->fo_instance);
16655184Sek110237 #else
16665184Sek110237 		filebench_log(LOG_DEBUG_IMPL,
16675184Sek110237 		    "sempost flow %s-%d to posix semaphore",
16685184Sek110237 		    target->fo_name,
16695184Sek110237 		    target->fo_instance);
16705184Sek110237 
16715184Sek110237 		/* Increment sem by value */
16725184Sek110237 		for (i = 0; i < value; i++) {
16735184Sek110237 			if (sem_post(&target->fo_sem) == -1) {
16745184Sek110237 				filebench_log(LOG_ERROR, "semop post failed");
16756084Saw148015 				return (FILEBENCH_ERROR);
16765184Sek110237 			}
16775184Sek110237 		}
16785184Sek110237 
16795184Sek110237 		filebench_log(LOG_DEBUG_IMPL, "flow %s-%d unblocking",
16805184Sek110237 		    target->fo_name, target->fo_instance);
16815184Sek110237 #endif /* HAVE_SYSV_SEM */
16825184Sek110237 
16835184Sek110237 		target = target->fo_targetnext;
16845184Sek110237 	}
16855673Saw148015 	flowop_endop(threadflow, flowop, 0);
16865184Sek110237 
16876084Saw148015 	return (FILEBENCH_OK);
16885184Sek110237 }
16895184Sek110237 
16905184Sek110237 
16915184Sek110237 /*
16925184Sek110237  * Section for exercising create / open / close / delete operations
16935184Sek110237  * on files within a fileset. For proper operation, the flowop attribute
16945184Sek110237  * "fd", which sets the fo_fdnumber field in the flowop, must be used
16955184Sek110237  * so that the same file is opened and later closed. "fd" is an index
16965184Sek110237  * into a pair of arrays maintained by threadflows, one of which
16975184Sek110237  * contains the operating system assigned file descriptors and the other
16985184Sek110237  * a pointer to the filesetentry whose file the file descriptor
16995184Sek110237  * references. An openfile flowop defined without fd being set will use
17005184Sek110237  * the default (0) fd or, if specified, rotate through fd indices, but
17015184Sek110237  * createfile and closefile must use the default or a specified fd.
17025184Sek110237  * Meanwhile deletefile picks and arbitrary file to delete, regardless
17035184Sek110237  * of fd attribute.
17045184Sek110237  */
17055184Sek110237 
17065184Sek110237 /*
17075184Sek110237  * XXX Making file selection more consistent among the flowops might good
17085184Sek110237  */
17095184Sek110237 
17105184Sek110237 
17115184Sek110237 /*
17125184Sek110237  * Emulates (and actually does) file open. Obtains a file descriptor
17136084Saw148015  * index, then calls flowoplib_openfile_common() to open. Returns
17146084Saw148015  * FILEBENCH_ERROR if no file descriptor is found, and returns the
17156084Saw148015  * status from flowoplib_openfile_common otherwise (FILEBENCH_ERROR,
17166084Saw148015  * FILEBENCH_NORSC, FILEBENCH_OK).
17175184Sek110237  */
17185184Sek110237 static int
17195184Sek110237 flowoplib_openfile(threadflow_t *threadflow, flowop_t *flowop)
17205184Sek110237 {
17215184Sek110237 	int fd = flowoplib_fdnum(threadflow, flowop);
17225184Sek110237 
17235184Sek110237 	if (fd == -1)
17246084Saw148015 		return (FILEBENCH_ERROR);
17255184Sek110237 
17265184Sek110237 	return (flowoplib_openfile_common(threadflow, flowop, fd));
17275184Sek110237 }
17285184Sek110237 
17295184Sek110237 /*
17305184Sek110237  * Common file opening code for filesets. Uses the supplied
17315184Sek110237  * file descriptor index to determine the tf_fd entry to use.
17325184Sek110237  * If the entry is empty (0) and the fileset exists, fileset
17335184Sek110237  * pick is called to select a fileset entry to use. The file
17345184Sek110237  * specified in the filesetentry is opened, and the returned
17355184Sek110237  * operating system file descriptor and a pointer to the
17365184Sek110237  * filesetentry are stored in tf_fd[fd] and tf_fse[fd],
17376084Saw148015  * respectively. Returns FILEBENCH_ERROR on error,
17386084Saw148015  * FILEBENCH_NORSC if no suitable filesetentry can be found,
17396084Saw148015  * and FILEBENCH_OK on success.
17405184Sek110237  */
17415184Sek110237 static int
17425184Sek110237 flowoplib_openfile_common(threadflow_t *threadflow, flowop_t *flowop, int fd)
17435184Sek110237 {
17445184Sek110237 	filesetentry_t *file;
17456212Saw148015 	char *fileset_name;
17465184Sek110237 	int tid = 0;
17475184Sek110237 
17486391Saw148015 	if (flowop->fo_fileset == NULL) {
17496391Saw148015 		filebench_log(LOG_ERROR, "flowop NULL file");
17506391Saw148015 		return (FILEBENCH_ERROR);
17516391Saw148015 	}
17526391Saw148015 
17536212Saw148015 	if ((fileset_name =
17546212Saw148015 	    avd_get_str(flowop->fo_fileset->fs_name)) == NULL) {
17556212Saw148015 		filebench_log(LOG_ERROR,
17566212Saw148015 		    "flowop %s: fileset has no name", flowop->fo_name);
17576212Saw148015 		return (FILEBENCH_ERROR);
17586212Saw148015 	}
17596212Saw148015 
17605184Sek110237 	/*
17615184Sek110237 	 * If the flowop doesn't default to persistent fd
17625184Sek110237 	 * then get unique thread ID for use by fileset_pick
17635184Sek110237 	 */
17646212Saw148015 	if (avd_get_bool(flowop->fo_rotatefd))
17655184Sek110237 		tid = threadflow->tf_utid;
17665184Sek110237 
17675184Sek110237 	if (threadflow->tf_fd[fd] != 0) {
17685184Sek110237 		filebench_log(LOG_ERROR,
17695184Sek110237 		    "flowop %s attempted to open without closing on fd %d",
17705184Sek110237 		    flowop->fo_name, fd);
17716084Saw148015 		return (FILEBENCH_ERROR);
17725184Sek110237 	}
17735184Sek110237 
17745673Saw148015 #ifdef HAVE_RAW_SUPPORT
17755673Saw148015 	if (flowop->fo_fileset->fs_attrs & FILESET_IS_RAW_DEV) {
17765673Saw148015 		int open_attrs = 0;
17775673Saw148015 		char name[MAXPATHLEN];
17785673Saw148015 
17796212Saw148015 		(void) strcpy(name,
17806212Saw148015 		    avd_get_str(flowop->fo_fileset->fs_path));
17815673Saw148015 		(void) strcat(name, "/");
17826212Saw148015 		(void) strcat(name, fileset_name);
17835673Saw148015 
17846212Saw148015 		if (avd_get_bool(flowop->fo_dsync)) {
17855673Saw148015 #ifdef sun
17865673Saw148015 			open_attrs |= O_DSYNC;
17875673Saw148015 #else
17885673Saw148015 			open_attrs |= O_FSYNC;
17895673Saw148015 #endif
17905673Saw148015 		}
17915673Saw148015 
17925673Saw148015 		filebench_log(LOG_DEBUG_SCRIPT,
17935673Saw148015 		    "open raw device %s flags %d = %d", name, open_attrs, fd);
17945673Saw148015 
17955673Saw148015 		threadflow->tf_fd[fd] = open64(name,
17965673Saw148015 		    O_RDWR | open_attrs, 0666);
17975673Saw148015 
17985673Saw148015 		if (threadflow->tf_fd[fd] < 0) {
17995673Saw148015 			filebench_log(LOG_ERROR,
18005673Saw148015 			    "Failed to open raw device %s: %s",
18015673Saw148015 			    name, strerror(errno));
18026084Saw148015 			return (FILEBENCH_ERROR);
18035673Saw148015 		}
18045673Saw148015 
18055673Saw148015 		/* if running on Solaris, use un-buffered io */
18065673Saw148015 #ifdef sun
18075673Saw148015 		(void) directio(threadflow->tf_fd[fd], DIRECTIO_ON);
18085673Saw148015 #endif
18095673Saw148015 
18105673Saw148015 		threadflow->tf_fse[fd] = NULL;
18115673Saw148015 
18126084Saw148015 		return (FILEBENCH_OK);
18135673Saw148015 	}
18145673Saw148015 #endif /* HAVE_RAW_SUPPORT */
18155673Saw148015 
18165184Sek110237 	if ((file = fileset_pick(flowop->fo_fileset,
18175184Sek110237 	    FILESET_PICKEXISTS, tid)) == NULL) {
18186084Saw148015 		filebench_log(LOG_DEBUG_SCRIPT,
18195184Sek110237 		    "flowop %s failed to pick file from %s on fd %d",
18206212Saw148015 		    flowop->fo_name, fileset_name, fd);
18216084Saw148015 		return (FILEBENCH_NORSC);
18225184Sek110237 	}
18235184Sek110237 
18245184Sek110237 	threadflow->tf_fse[fd] = file;
18255184Sek110237 
18265184Sek110237 	flowop_beginop(threadflow, flowop);
18275184Sek110237 	threadflow->tf_fd[fd] = fileset_openfile(flowop->fo_fileset,
18285184Sek110237 	    file, O_RDWR, 0666, flowoplib_fileattrs(flowop));
18295673Saw148015 	flowop_endop(threadflow, flowop, 0);
18305184Sek110237 
18315184Sek110237 	if (threadflow->tf_fd[fd] < 0) {
18326212Saw148015 		filebench_log(LOG_ERROR, "flowop %s failed to open file %s",
18336212Saw148015 		    flowop->fo_name, file->fse_path);
18346084Saw148015 		return (FILEBENCH_ERROR);
18355184Sek110237 	}
18365184Sek110237 
18375184Sek110237 	filebench_log(LOG_DEBUG_SCRIPT,
18385184Sek110237 	    "flowop %s: opened %s fd[%d] = %d",
18395184Sek110237 	    flowop->fo_name, file->fse_path, fd, threadflow->tf_fd[fd]);
18405184Sek110237 
18416084Saw148015 	return (FILEBENCH_OK);
18425184Sek110237 }
18435184Sek110237 
18445184Sek110237 /*
18455184Sek110237  * Emulate create of a file. Uses the flowop's fdnumber to select
18465184Sek110237  * tf_fd and tf_fse array locations to put the created file's file
18475184Sek110237  * descriptor and filesetentry respectively. Uses fileset_pick()
18485184Sek110237  * to select a specific filesetentry whose file does not currently
18495184Sek110237  * exist for the file create operation. Then calls
18505184Sek110237  * fileset_openfile() with the O_CREATE flag set to create the
18516084Saw148015  * file. Returns FILEBENCH_ERROR if the array index specified by fdnumber is
18525184Sek110237  * already in use, the flowop has no associated fileset, or
18535184Sek110237  * the create call fails. Returns 1 if a filesetentry with a
18546084Saw148015  * nonexistent file cannot be found. Returns FILEBENCH_OK on success.
18555184Sek110237  */
18565184Sek110237 static int
18575184Sek110237 flowoplib_createfile(threadflow_t *threadflow, flowop_t *flowop)
18585184Sek110237 {
18595184Sek110237 	filesetentry_t *file;
18605184Sek110237 	int fd = flowop->fo_fdnumber;
18615184Sek110237 
18625184Sek110237 	if (threadflow->tf_fd[fd] != 0) {
18635184Sek110237 		filebench_log(LOG_ERROR,
18645184Sek110237 		    "flowop %s attempted to create without closing on fd %d",
18655184Sek110237 		    flowop->fo_name, fd);
18666084Saw148015 		return (FILEBENCH_ERROR);
18675184Sek110237 	}
18685184Sek110237 
18695184Sek110237 	if (flowop->fo_fileset == NULL) {
18705184Sek110237 		filebench_log(LOG_ERROR, "flowop NULL file");
18716084Saw148015 		return (FILEBENCH_ERROR);
18725184Sek110237 	}
18735184Sek110237 
18745673Saw148015 #ifdef HAVE_RAW_SUPPORT
18755673Saw148015 	/* can't be used with raw devices */
18765673Saw148015 	if (flowop->fo_fileset->fs_attrs & FILESET_IS_RAW_DEV) {
18775673Saw148015 		filebench_log(LOG_ERROR,
18785673Saw148015 		    "flowop %s attempted to a createfile on RAW device",
18795673Saw148015 		    flowop->fo_name);
18806084Saw148015 		return (FILEBENCH_ERROR);
18815673Saw148015 	}
18825673Saw148015 #endif /* HAVE_RAW_SUPPORT */
18835673Saw148015 
18845184Sek110237 	if ((file = fileset_pick(flowop->fo_fileset,
18855184Sek110237 	    FILESET_PICKNOEXIST, 0)) == NULL) {
18866084Saw148015 		filebench_log(LOG_DEBUG_SCRIPT,
18876084Saw148015 		    "flowop %s failed to pick file from fileset %s",
18886212Saw148015 		    flowop->fo_name,
18896212Saw148015 		    avd_get_str(flowop->fo_fileset->fs_name));
18906084Saw148015 		return (FILEBENCH_NORSC);
18915184Sek110237 	}
18925184Sek110237 
18935184Sek110237 	threadflow->tf_fse[fd] = file;
18945184Sek110237 
18955184Sek110237 	flowop_beginop(threadflow, flowop);
18965184Sek110237 	threadflow->tf_fd[fd] = fileset_openfile(flowop->fo_fileset,
18975184Sek110237 	    file, O_RDWR | O_CREAT, 0666, flowoplib_fileattrs(flowop));
18985673Saw148015 	flowop_endop(threadflow, flowop, 0);
18995184Sek110237 
19005184Sek110237 	if (threadflow->tf_fd[fd] < 0) {
19015184Sek110237 		filebench_log(LOG_ERROR, "failed to create file %s",
19025184Sek110237 		    flowop->fo_name);
19036084Saw148015 		return (FILEBENCH_ERROR);
19045184Sek110237 	}
19055184Sek110237 
19065184Sek110237 	filebench_log(LOG_DEBUG_SCRIPT,
19075184Sek110237 	    "flowop %s: created %s fd[%d] = %d",
19085184Sek110237 	    flowop->fo_name, file->fse_path, fd, threadflow->tf_fd[fd]);
19095184Sek110237 
19106084Saw148015 	return (FILEBENCH_OK);
19115184Sek110237 }
19125184Sek110237 
19135184Sek110237 /*
19146391Saw148015  * Emulates delete of a file. If a valid fd is provided, it uses the
19156391Saw148015  * filesetentry stored at that fd location to select the file to be
19166391Saw148015  * deleted, otherwise it picks an arbitrary filesetentry
19176391Saw148015  * whose file exists. It then uses unlink() to delete it and Clears
19186084Saw148015  * the FSE_EXISTS flag for the filesetentry. Returns FILEBENCH_ERROR if the
19196084Saw148015  * flowop has no associated fileset. Returns FILEBENCH_NORSC if an appropriate
19206084Saw148015  * filesetentry cannot be found, and FILEBENCH_OK on success.
19215184Sek110237  */
19225184Sek110237 static int
19235184Sek110237 flowoplib_deletefile(threadflow_t *threadflow, flowop_t *flowop)
19245184Sek110237 {
19255184Sek110237 	filesetentry_t *file;
19265184Sek110237 	fileset_t *fileset;
19275184Sek110237 	char path[MAXPATHLEN];
19285184Sek110237 	char *pathtmp;
19296391Saw148015 	int fd = flowop->fo_fdnumber;
19305184Sek110237 
19316391Saw148015 	/* if fd specified, use it to access file */
19326391Saw148015 	if ((fd > 0) && ((file = threadflow->tf_fse[fd]) != NULL)) {
19336391Saw148015 
19346391Saw148015 		/* check whether file still open */
19356391Saw148015 		if (threadflow->tf_fd[fd] > 0) {
19366391Saw148015 			filebench_log(LOG_DEBUG_SCRIPT,
19376391Saw148015 			    "flowop %s deleting still open file at fd = %d",
19386391Saw148015 			    flowop->fo_name, fd);
19396391Saw148015 		}
19406391Saw148015 
19416391Saw148015 		/* indicate that the file will be deleted */
19426391Saw148015 		threadflow->tf_fse[fd] = NULL;
19436391Saw148015 
19446391Saw148015 		/* if here, we still have a valid file pointer */
19456391Saw148015 		fileset = file->fse_fileset;
19466391Saw148015 	} else {
19476391Saw148015 		/* Otherwise, pick arbitrary file */
19486391Saw148015 		file = NULL;
19496391Saw148015 		fileset = flowop->fo_fileset;
19506391Saw148015 	}
19516391Saw148015 
19526391Saw148015 
19536391Saw148015 	if (fileset == NULL) {
19545184Sek110237 		filebench_log(LOG_ERROR, "flowop NULL file");
19556084Saw148015 		return (FILEBENCH_ERROR);
19565184Sek110237 	}
19575184Sek110237 
19585673Saw148015 #ifdef HAVE_RAW_SUPPORT
19595673Saw148015 	/* can't be used with raw devices */
19606391Saw148015 	if (fileset->fs_attrs & FILESET_IS_RAW_DEV) {
19615673Saw148015 		filebench_log(LOG_ERROR,
19625673Saw148015 		    "flowop %s attempted a deletefile on RAW device",
19635673Saw148015 		    flowop->fo_name);
19646084Saw148015 		return (FILEBENCH_ERROR);
19655673Saw148015 	}
19665673Saw148015 #endif /* HAVE_RAW_SUPPORT */
19675673Saw148015 
19686391Saw148015 	if (file == NULL) {
19696391Saw148015 		if ((file = fileset_pick(fileset, FILESET_PICKEXISTS, 0))
19706391Saw148015 		    == NULL) {
19716391Saw148015 			filebench_log(LOG_DEBUG_SCRIPT,
19726391Saw148015 			    "flowop %s failed to pick file", flowop->fo_name);
19736391Saw148015 			return (FILEBENCH_NORSC);
19746391Saw148015 		}
19756391Saw148015 	} else {
19766391Saw148015 		(void) ipc_mutex_lock(&file->fse_lock);
19775184Sek110237 	}
19785184Sek110237 
19795184Sek110237 	*path = 0;
19806212Saw148015 	(void) strcpy(path, avd_get_str(fileset->fs_path));
19815184Sek110237 	(void) strcat(path, "/");
19826212Saw148015 	(void) strcat(path, avd_get_str(fileset->fs_name));
19835184Sek110237 	pathtmp = fileset_resolvepath(file);
19845184Sek110237 	(void) strcat(path, pathtmp);
19855184Sek110237 	free(pathtmp);
19865184Sek110237 
19875184Sek110237 	flowop_beginop(threadflow, flowop);
19885184Sek110237 	(void) unlink(path);
19895673Saw148015 	flowop_endop(threadflow, flowop, 0);
19905184Sek110237 	file->fse_flags &= ~FSE_EXISTS;
1991*6701Saw148015 	(void) ipc_mutex_lock(&fileset->fs_num_files_lock);
1992*6701Saw148015 	fileset->fs_num_act_files--;
1993*6701Saw148015 	(void) ipc_mutex_unlock(&fileset->fs_num_files_lock);
19945184Sek110237 	(void) ipc_mutex_unlock(&file->fse_lock);
19955184Sek110237 
19965184Sek110237 	filebench_log(LOG_DEBUG_SCRIPT, "deleted file %s", file->fse_path);
19975184Sek110237 
19986084Saw148015 	return (FILEBENCH_OK);
19995184Sek110237 }
20005184Sek110237 
20015184Sek110237 /*
20025184Sek110237  * Emulates fsync of a file. Obtains the file descriptor index
20035184Sek110237  * from the flowop, obtains the actual file descriptor from
20045184Sek110237  * the threadflow's table, checks to be sure it is still an
20056084Saw148015  * open file, then does an fsync operation on it. Returns FILEBENCH_ERROR
20066084Saw148015  * if the file no longer is open, FILEBENCH_OK otherwise.
20075184Sek110237  */
20085184Sek110237 static int
20095184Sek110237 flowoplib_fsync(threadflow_t *threadflow, flowop_t *flowop)
20105184Sek110237 {
20115184Sek110237 	filesetentry_t *file;
20125184Sek110237 	int fd = flowop->fo_fdnumber;
20135184Sek110237 
20145184Sek110237 	if (threadflow->tf_fd[fd] == 0) {
20155184Sek110237 		filebench_log(LOG_ERROR,
20165184Sek110237 		    "flowop %s attempted to fsync a closed fd %d",
20175184Sek110237 		    flowop->fo_name, fd);
20186084Saw148015 		return (FILEBENCH_ERROR);
20195184Sek110237 	}
20205184Sek110237 
20215673Saw148015 	file = threadflow->tf_fse[fd];
20225673Saw148015 
20235673Saw148015 	if ((file == NULL) ||
20245673Saw148015 	    (file->fse_fileset->fs_attrs & FILESET_IS_RAW_DEV)) {
20255673Saw148015 		filebench_log(LOG_ERROR,
20265673Saw148015 		    "flowop %s attempted to a fsync a RAW device",
20275673Saw148015 		    flowop->fo_name);
20286084Saw148015 		return (FILEBENCH_ERROR);
20295673Saw148015 	}
20305673Saw148015 
20315184Sek110237 	/* Measure time to fsync */
20325184Sek110237 	flowop_beginop(threadflow, flowop);
20335184Sek110237 	(void) fsync(threadflow->tf_fd[fd]);
20345673Saw148015 	flowop_endop(threadflow, flowop, 0);
20355184Sek110237 
20365184Sek110237 	filebench_log(LOG_DEBUG_SCRIPT, "fsync file %s", file->fse_path);
20375184Sek110237 
20386084Saw148015 	return (FILEBENCH_OK);
20395184Sek110237 }
20405184Sek110237 
20415184Sek110237 /*
20425184Sek110237  * Emulate fsync of an entire fileset. Search through the
20435184Sek110237  * threadflow's file descriptor array, doing fsync() on each
20445184Sek110237  * open file that belongs to the flowop's fileset. Always
20456084Saw148015  * returns FILEBENCH_OK.
20465184Sek110237  */
20475184Sek110237 static int
20485184Sek110237 flowoplib_fsyncset(threadflow_t *threadflow, flowop_t *flowop)
20495184Sek110237 {
20505184Sek110237 	int fd;
20515184Sek110237 
20525184Sek110237 	for (fd = 0; fd < THREADFLOW_MAXFD; fd++) {
20535184Sek110237 		filesetentry_t *file;
20545184Sek110237 
20555184Sek110237 		/* Match the file set to fsync */
20565184Sek110237 		if ((threadflow->tf_fse[fd] == NULL) ||
20575184Sek110237 		    (flowop->fo_fileset != threadflow->tf_fse[fd]->fse_fileset))
20585184Sek110237 			continue;
20595184Sek110237 
20605184Sek110237 		/* Measure time to fsync */
20615184Sek110237 		flowop_beginop(threadflow, flowop);
20625184Sek110237 		(void) fsync(threadflow->tf_fd[fd]);
20635673Saw148015 		flowop_endop(threadflow, flowop, 0);
20645184Sek110237 
20655184Sek110237 		file = threadflow->tf_fse[fd];
20665184Sek110237 
20675184Sek110237 		filebench_log(LOG_DEBUG_SCRIPT, "fsync file %s",
20685184Sek110237 		    file->fse_path);
20695184Sek110237 	}
20705184Sek110237 
20716084Saw148015 	return (FILEBENCH_OK);
20725184Sek110237 }
20735184Sek110237 
20745184Sek110237 /*
20755184Sek110237  * Emulate close of a file.  Obtains the file descriptor index
20765184Sek110237  * from the flowop, obtains the actual file descriptor from the
20775184Sek110237  * threadflow's table, checks to be sure it is still an open
20785184Sek110237  * file, then does a close operation on it. Then sets the
20795184Sek110237  * threadflow file descriptor table entry to 0, and the file set
20806084Saw148015  * entry pointer to NULL. Returns FILEBENCH_ERROR if the file was not open,
20816084Saw148015  * FILEBENCH_OK otherwise.
20825184Sek110237  */
20835184Sek110237 static int
20845184Sek110237 flowoplib_closefile(threadflow_t *threadflow, flowop_t *flowop)
20855184Sek110237 {
20865184Sek110237 	filesetentry_t *file;
20875184Sek110237 	int fd = flowop->fo_fdnumber;
20885184Sek110237 
20895184Sek110237 	if (threadflow->tf_fd[fd] == 0) {
20905184Sek110237 		filebench_log(LOG_ERROR,
20915184Sek110237 		    "flowop %s attempted to close an already closed fd %d",
20925184Sek110237 		    flowop->fo_name, fd);
20936084Saw148015 		return (FILEBENCH_ERROR);
20945184Sek110237 	}
20955184Sek110237 
20965184Sek110237 	/* Measure time to close */
20975184Sek110237 	flowop_beginop(threadflow, flowop);
20985184Sek110237 	(void) close(threadflow->tf_fd[fd]);
20995673Saw148015 	flowop_endop(threadflow, flowop, 0);
21005184Sek110237 
21015184Sek110237 	file = threadflow->tf_fse[fd];
21025184Sek110237 
21035184Sek110237 	threadflow->tf_fd[fd] = 0;
21045184Sek110237 
21055184Sek110237 	filebench_log(LOG_DEBUG_SCRIPT, "closed file %s", file->fse_path);
21065184Sek110237 
21076084Saw148015 	return (FILEBENCH_OK);
21085184Sek110237 }
21095184Sek110237 
21105184Sek110237 /*
21115184Sek110237  * Emulate stat of a file. Picks an arbitrary filesetentry with
21125184Sek110237  * an existing file from the flowop's fileset, then performs a
21136084Saw148015  * stat() operation on it. Returns FILEBENCH_ERROR if the flowop has no
21146084Saw148015  * associated fileset. Returns FILEBENCH_NORSC if an appropriate filesetentry
21156084Saw148015  * cannot be found, and FILEBENCH_OK on success.
21165184Sek110237  */
21175184Sek110237 static int
21185184Sek110237 flowoplib_statfile(threadflow_t *threadflow, flowop_t *flowop)
21195184Sek110237 {
21205184Sek110237 	filesetentry_t *file;
21215184Sek110237 	fileset_t *fileset;
21225184Sek110237 	char path[MAXPATHLEN];
21235184Sek110237 	char *pathtmp;
21245184Sek110237 
21256391Saw148015 	if ((fileset = flowop->fo_fileset) == NULL) {
21265184Sek110237 		filebench_log(LOG_ERROR, "flowop NULL file");
21276084Saw148015 		return (FILEBENCH_ERROR);
21285184Sek110237 	}
21295184Sek110237 
21306391Saw148015 	if ((file = fileset_pick(fileset, FILESET_PICKEXISTS, 0)) == NULL) {
21316391Saw148015 		filebench_log(LOG_DEBUG_SCRIPT,
21326391Saw148015 		    "flowop %s failed to pick file",
21335184Sek110237 		    flowop->fo_name);
21346084Saw148015 		return (FILEBENCH_NORSC);
21355184Sek110237 	}
21365184Sek110237 
21375184Sek110237 	*path = 0;
21386212Saw148015 	(void) strcpy(path, avd_get_str(fileset->fs_path));
21395184Sek110237 	(void) strcat(path, "/");
21406212Saw148015 	(void) strcat(path, avd_get_str(fileset->fs_name));
21415184Sek110237 	pathtmp = fileset_resolvepath(file);
21425184Sek110237 	(void) strcat(path, pathtmp);
21435184Sek110237 	free(pathtmp);
21445184Sek110237 
21455184Sek110237 	flowop_beginop(threadflow, flowop);
21465673Saw148015 	flowop_endop(threadflow, flowop, 0);
21475184Sek110237 
21485184Sek110237 	(void) ipc_mutex_unlock(&file->fse_lock);
21495184Sek110237 
21506084Saw148015 	return (FILEBENCH_OK);
21515184Sek110237 }
21525184Sek110237 
21535184Sek110237 
21545184Sek110237 /*
21555184Sek110237  * Additional reads and writes. Read and write whole files, write
21565184Sek110237  * and append to files. Some of these work with both fileobjs and
21575184Sek110237  * filesets, others only with filesets. The flowoplib_write routine
21585184Sek110237  * writes from thread memory, while the others read or write using
21595184Sek110237  * fo_buf memory. Note that both flowoplib_read() and
21605184Sek110237  * flowoplib_aiowrite() use thread memory as well.
21615184Sek110237  */
21625184Sek110237 
21635184Sek110237 
21645184Sek110237 /*
21655673Saw148015  * Emulate a read of a whole file. The file must be open with
21665673Saw148015  * file descriptor and filesetentry stored at the locations indexed
21675673Saw148015  * by the flowop's fdnumber. It then seeks to the beginning of the
21685673Saw148015  * associated file, and reads fs_iosize bytes at a time until the end
21696084Saw148015  * of the file. Returns FILEBENCH_ERROR on error, FILEBENCH_NORSC if
21706084Saw148015  * out of files, and FILEBENCH_OK on success.
21715184Sek110237  */
21725184Sek110237 static int
21735184Sek110237 flowoplib_readwholefile(threadflow_t *threadflow, flowop_t *flowop)
21745184Sek110237 {
21755673Saw148015 	caddr_t iobuf;
21765184Sek110237 	off64_t bytes = 0;
21775673Saw148015 	int filedesc;
21786212Saw148015 	uint64_t wss;
21796212Saw148015 	fbint_t iosize;
21805184Sek110237 	int ret;
21816212Saw148015 	char zerordbuf;
21825184Sek110237 
21835673Saw148015 	/* get the file to use */
21846084Saw148015 	if ((ret = flowoplib_filesetup(threadflow, flowop, &wss,
21856084Saw148015 	    &filedesc)) != FILEBENCH_OK)
21866084Saw148015 		return (ret);
21875184Sek110237 
21885673Saw148015 	/* an I/O size of zero means read entire working set with one I/O */
21896212Saw148015 	if ((iosize = avd_get_int(flowop->fo_iosize)) == 0)
21905673Saw148015 		iosize = wss;
21915184Sek110237 
21926212Saw148015 	/*
21936212Saw148015 	 * The file may actually be 0 bytes long, in which case skip
21946212Saw148015 	 * the buffer set up call (which would fail) and substitute
21956212Saw148015 	 * a small buffer, which won't really be used.
21966212Saw148015 	 */
21976212Saw148015 	if (iosize == 0) {
21986212Saw148015 		iobuf = (caddr_t)&zerordbuf;
21996212Saw148015 		filebench_log(LOG_DEBUG_SCRIPT,
22006212Saw148015 		    "flowop %s read zero length file", flowop->fo_name);
22016212Saw148015 	} else {
22026212Saw148015 		if (flowoplib_iobufsetup(threadflow, flowop, &iobuf,
22036212Saw148015 		    iosize) != 0)
22046212Saw148015 			return (FILEBENCH_ERROR);
22056212Saw148015 	}
22065184Sek110237 
22075184Sek110237 	/* Measure time to read bytes */
22085184Sek110237 	flowop_beginop(threadflow, flowop);
22095673Saw148015 	(void) lseek64(filedesc, 0, SEEK_SET);
22105673Saw148015 	while ((ret = read(filedesc, iobuf, iosize)) > 0)
22115184Sek110237 		bytes += ret;
22125184Sek110237 
22135673Saw148015 	flowop_endop(threadflow, flowop, bytes);
22145184Sek110237 
22155184Sek110237 	if (ret < 0) {
22165184Sek110237 		filebench_log(LOG_ERROR,
22176391Saw148015 		    "readwhole fail Failed to read whole file: %s",
22186391Saw148015 		    strerror(errno));
22196084Saw148015 		return (FILEBENCH_ERROR);
22205184Sek110237 	}
22215184Sek110237 
22226084Saw148015 	return (FILEBENCH_OK);
22235184Sek110237 }
22245184Sek110237 
22255184Sek110237 /*
22265184Sek110237  * Emulate a write to a file of size fo_iosize.  Will write
22275184Sek110237  * to a file from a fileset if the flowop's fo_fileset field
22285184Sek110237  * specifies one or its fdnumber is non zero. Otherwise it
22295184Sek110237  * will write to a fileobj file, if one exists. If the file
22305184Sek110237  * is not currently open, the routine will attempt to open
22315184Sek110237  * it. The flowop's fo_wss parameter will be used to set the
22325184Sek110237  * maximum file size if it is non-zero, otherwise the
22335184Sek110237  * filesetentry's  fse_size will be used. A random memory
22345184Sek110237  * buffer offset is calculated, and, if fo_random is TRUE,
22355184Sek110237  * a random file offset is used for the write. Otherwise the
22366084Saw148015  * write is to the next sequential location. Returns
22376084Saw148015  * FILEBENCH_ERROR on errors, FILEBENCH_NORSC if iosetup can't
22386084Saw148015  * obtain a file, or FILEBENCH_OK on success.
22395184Sek110237  */
22405184Sek110237 static int
22415184Sek110237 flowoplib_write(threadflow_t *threadflow, flowop_t *flowop)
22425184Sek110237 {
22435673Saw148015 	caddr_t iobuf;
22446212Saw148015 	fbint_t wss;
22456212Saw148015 	fbint_t iosize;
22465184Sek110237 	int filedesc;
22476084Saw148015 	int ret;
22485184Sek110237 
22496212Saw148015 	iosize = avd_get_int(flowop->fo_iosize);
22506084Saw148015 	if ((ret = flowoplib_iosetup(threadflow, flowop, &wss, &iobuf,
22516212Saw148015 	    &filedesc, iosize)) != FILEBENCH_OK)
22526084Saw148015 		return (ret);
22535184Sek110237 
22546212Saw148015 	if (avd_get_bool(flowop->fo_random)) {
22555184Sek110237 		uint64_t fileoffset;
22565184Sek110237 
22575184Sek110237 		if (filebench_randomno64(&fileoffset,
22586212Saw148015 		    wss, iosize, NULL) == -1) {
22595184Sek110237 			filebench_log(LOG_ERROR,
22605184Sek110237 			    "file size smaller than IO size for thread %s",
22615184Sek110237 			    flowop->fo_name);
22626084Saw148015 			return (FILEBENCH_ERROR);
22635184Sek110237 		}
22645184Sek110237 		flowop_beginop(threadflow, flowop);
22655673Saw148015 		if (pwrite64(filedesc, iobuf,
22666212Saw148015 		    iosize, (off64_t)fileoffset) == -1) {
22675184Sek110237 			filebench_log(LOG_ERROR, "write failed, "
22686286Saw148015 			    "offset %llu io buffer %zd: %s",
22696286Saw148015 			    (u_longlong_t)fileoffset, iobuf, strerror(errno));
22705673Saw148015 			flowop_endop(threadflow, flowop, 0);
22716084Saw148015 			return (FILEBENCH_ERROR);
22725184Sek110237 		}
22736212Saw148015 		flowop_endop(threadflow, flowop, iosize);
22745184Sek110237 	} else {
22755184Sek110237 		flowop_beginop(threadflow, flowop);
22766391Saw148015 		if (write(filedesc, iobuf, iosize) == -1) {
22775184Sek110237 			filebench_log(LOG_ERROR,
22785673Saw148015 			    "write failed, io buffer %zd: %s",
22795673Saw148015 			    iobuf, strerror(errno));
22805673Saw148015 			flowop_endop(threadflow, flowop, 0);
22816084Saw148015 			return (FILEBENCH_ERROR);
22825184Sek110237 		}
22836212Saw148015 		flowop_endop(threadflow, flowop, iosize);
22845184Sek110237 	}
22855184Sek110237 
22866084Saw148015 	return (FILEBENCH_OK);
22875184Sek110237 }
22885184Sek110237 
22895184Sek110237 /*
22905184Sek110237  * Emulate a write of a whole file.  The size of the file
22915673Saw148015  * is taken from a filesetentry identified by fo_srcfdnumber or
22925673Saw148015  * from the working set size, while the file descriptor used is
22935673Saw148015  * identified by fo_fdnumber. Does multiple writes of fo_iosize
22946084Saw148015  * length length until full file has been written. Returns FILEBENCH_ERROR on
22956084Saw148015  * error, FILEBENCH_NORSC if out of files, FILEBENCH_OK on success.
22965184Sek110237  */
22975184Sek110237 static int
22985184Sek110237 flowoplib_writewholefile(threadflow_t *threadflow, flowop_t *flowop)
22995184Sek110237 {
23005673Saw148015 	caddr_t iobuf;
23015184Sek110237 	filesetentry_t *file;
23025184Sek110237 	int wsize;
23035184Sek110237 	off64_t seek;
23045184Sek110237 	off64_t bytes = 0;
23055673Saw148015 	uint64_t wss;
23066212Saw148015 	fbint_t iosize;
23075673Saw148015 	int filedesc;
23085184Sek110237 	int srcfd = flowop->fo_srcfdnumber;
23095184Sek110237 	int ret;
23106212Saw148015 	char zerowrtbuf;
23115184Sek110237 
23125673Saw148015 	/* get the file to use */
23136084Saw148015 	if ((ret = flowoplib_filesetup(threadflow, flowop, &wss,
23146084Saw148015 	    &filedesc)) != FILEBENCH_OK)
23156084Saw148015 		return (ret);
23165184Sek110237 
23176212Saw148015 	/* an I/O size of zero means write entire working set with one I/O */
23186212Saw148015 	if ((iosize = avd_get_int(flowop->fo_iosize)) == 0)
23195673Saw148015 		iosize = wss;
23205184Sek110237 
23216212Saw148015 	/*
23226212Saw148015 	 * The file may actually be 0 bytes long, in which case skip
23236212Saw148015 	 * the buffer set up call (which would fail) and substitute
23246212Saw148015 	 * a small buffer, which won't really be used.
23256212Saw148015 	 */
23266212Saw148015 	if (iosize == 0) {
23276212Saw148015 		iobuf = (caddr_t)&zerowrtbuf;
23286212Saw148015 		filebench_log(LOG_DEBUG_SCRIPT,
23296212Saw148015 		    "flowop %s wrote zero length file", flowop->fo_name);
23306212Saw148015 	} else {
23316212Saw148015 		if (flowoplib_iobufsetup(threadflow, flowop, &iobuf,
23326212Saw148015 		    iosize) != 0)
23336212Saw148015 			return (FILEBENCH_ERROR);
23346212Saw148015 	}
23355184Sek110237 
23365184Sek110237 	file = threadflow->tf_fse[srcfd];
23375673Saw148015 	if ((srcfd != 0) && (file == NULL)) {
23385673Saw148015 		filebench_log(LOG_ERROR, "flowop %s: NULL src file",
23395184Sek110237 		    flowop->fo_name);
23406084Saw148015 		return (FILEBENCH_ERROR);
23415184Sek110237 	}
23425184Sek110237 
23435673Saw148015 	if (file)
23445673Saw148015 		wss = file->fse_size;
23455673Saw148015 
23465673Saw148015 	wsize = (int)MIN(wss, iosize);
23475184Sek110237 
23485184Sek110237 	/* Measure time to write bytes */
23495184Sek110237 	flowop_beginop(threadflow, flowop);
23505673Saw148015 	for (seek = 0; seek < wss; seek += wsize) {
23515673Saw148015 		ret = write(filedesc, iobuf, wsize);
23525184Sek110237 		if (ret != wsize) {
23535184Sek110237 			filebench_log(LOG_ERROR,
23545184Sek110237 			    "Failed to write %d bytes on fd %d: %s",
23555673Saw148015 			    wsize, filedesc, strerror(errno));
23565673Saw148015 			flowop_endop(threadflow, flowop, 0);
23576084Saw148015 			return (FILEBENCH_ERROR);
23585184Sek110237 		}
23595673Saw148015 		wsize = (int)MIN(wss - seek, iosize);
23605184Sek110237 		bytes += ret;
23615184Sek110237 	}
23625673Saw148015 	flowop_endop(threadflow, flowop, bytes);
23635184Sek110237 
23646084Saw148015 	return (FILEBENCH_OK);
23655184Sek110237 }
23665184Sek110237 
23675184Sek110237 
23685184Sek110237 /*
23695184Sek110237  * Emulate a fixed size append to a file. Will append data to
23705184Sek110237  * a file chosen from a fileset if the flowop's fo_fileset
23715184Sek110237  * field specifies one or if its fdnumber is non zero.
23725184Sek110237  * Otherwise it will write to a fileobj file, if one exists.
23735184Sek110237  * The flowop's fo_wss parameter will be used to set the
23745184Sek110237  * maximum file size if it is non-zero, otherwise the
23755184Sek110237  * filesetentry's fse_size will be used. A random memory
23765184Sek110237  * buffer offset is calculated, then a logical seek to the
23775184Sek110237  * end of file is done followed by a write of fo_iosize
23785184Sek110237  * bytes. Writes are actually done from fo_buf, rather than
23795184Sek110237  * tf_mem as is done with flowoplib_write(), and no check
23805184Sek110237  * is made to see if fo_iosize exceeds the size of fo_buf.
23816084Saw148015  * Returns FILEBENCH_ERROR on error, FILEBENCH_NORSC if out of
23826084Saw148015  * files in the fileset, FILEBENCH_OK on success.
23835184Sek110237  */
23845184Sek110237 static int
23855184Sek110237 flowoplib_appendfile(threadflow_t *threadflow, flowop_t *flowop)
23865184Sek110237 {
23875673Saw148015 	caddr_t iobuf;
23885673Saw148015 	int filedesc;
23896212Saw148015 	fbint_t wss;
23906212Saw148015 	fbint_t iosize;
23915184Sek110237 	int ret;
23925184Sek110237 
23936212Saw148015 	iosize = avd_get_int(flowop->fo_iosize);
23946084Saw148015 	if ((ret = flowoplib_iosetup(threadflow, flowop, &wss, &iobuf,
23956084Saw148015 	    &filedesc, iosize)) != FILEBENCH_OK)
23966084Saw148015 		return (ret);
23975184Sek110237 
23985184Sek110237 	/* XXX wss is not being used */
23995184Sek110237 
24005184Sek110237 	/* Measure time to write bytes */
24015184Sek110237 	flowop_beginop(threadflow, flowop);
24025184Sek110237 	(void) lseek64(filedesc, 0, SEEK_END);
24035673Saw148015 	ret = write(filedesc, iobuf, iosize);
24045673Saw148015 	if (ret != iosize) {
24055184Sek110237 		filebench_log(LOG_ERROR,
24066286Saw148015 		    "Failed to write %llu bytes on fd %d: %s",
24076286Saw148015 		    (u_longlong_t)iosize, filedesc, strerror(errno));
24086212Saw148015 		flowop_endop(threadflow, flowop, ret);
24096084Saw148015 		return (FILEBENCH_ERROR);
24105184Sek110237 	}
24116212Saw148015 	flowop_endop(threadflow, flowop, ret);
24125184Sek110237 
24136084Saw148015 	return (FILEBENCH_OK);
24145184Sek110237 }
24155184Sek110237 
24165184Sek110237 /*
24175184Sek110237  * Emulate a random size append to a file. Will append data
24185184Sek110237  * to a file chosen from a fileset if the flowop's fo_fileset
24195184Sek110237  * field specifies one or if its fdnumber is non zero. Otherwise
24205184Sek110237  * it will write to a fileobj file, if one exists. The flowop's
24215184Sek110237  * fo_wss parameter will be used to set the maximum file size
24225184Sek110237  * if it is non-zero, otherwise the filesetentry's fse_size
24235184Sek110237  * will be used.  A random transfer size (but at most fo_iosize
24245184Sek110237  * bytes) and a random memory offset are calculated. A logical
24255184Sek110237  * seek to the end of file is done, then writes of up to
24265184Sek110237  * FILE_ALLOC_BLOCK in size are done until the full transfer
24275184Sek110237  * size has been written. Writes are actually done from fo_buf,
24285184Sek110237  * rather than tf_mem as is done with flowoplib_write().
24296084Saw148015  * Returns FILEBENCH_ERROR on error, FILEBENCH_NORSC if out of
24306084Saw148015  * files in the fileset, FILEBENCH_OK on success.
24315184Sek110237  */
24325184Sek110237 static int
24335184Sek110237 flowoplib_appendfilerand(threadflow_t *threadflow, flowop_t *flowop)
24345184Sek110237 {
24355673Saw148015 	caddr_t iobuf;
24365184Sek110237 	uint64_t appendsize;
24375673Saw148015 	int filedesc;
24386212Saw148015 	fbint_t wss;
24396212Saw148015 	fbint_t iosize;
24406212Saw148015 	int ret = 0;
24415184Sek110237 
24426212Saw148015 	if ((iosize = avd_get_int(flowop->fo_iosize)) == 0) {
24436212Saw148015 		filebench_log(LOG_ERROR, "zero iosize for flowop %s",
24446212Saw148015 		    flowop->fo_name);
24456212Saw148015 		return (FILEBENCH_ERROR);
24466212Saw148015 	}
24476212Saw148015 
24486212Saw148015 	if (filebench_randomno64(&appendsize, iosize, 1LL, NULL) != 0)
24496084Saw148015 		return (FILEBENCH_ERROR);
24505184Sek110237 
24515673Saw148015 	/* skip if attempting zero length append */
24525673Saw148015 	if (appendsize == 0) {
24535673Saw148015 		flowop_beginop(threadflow, flowop);
24545673Saw148015 		flowop_endop(threadflow, flowop, 0LL);
24556084Saw148015 		return (FILEBENCH_OK);
24565673Saw148015 	}
24575184Sek110237 
24586084Saw148015 	if ((ret = flowoplib_iosetup(threadflow, flowop, &wss, &iobuf,
24596084Saw148015 	    &filedesc, appendsize)) != FILEBENCH_OK)
24606084Saw148015 		return (ret);
24615673Saw148015 
24625184Sek110237 	/* XXX wss is not being used */
24635184Sek110237 
24645673Saw148015 	/* Measure time to write bytes */
24655673Saw148015 	flowop_beginop(threadflow, flowop);
24665673Saw148015 
24675673Saw148015 	(void) lseek64(filedesc, 0, SEEK_END);
24685673Saw148015 	ret = write(filedesc, iobuf, appendsize);
24695673Saw148015 	if (ret != appendsize) {
24705673Saw148015 		filebench_log(LOG_ERROR,
24716286Saw148015 		    "Failed to write %llu bytes on fd %d: %s",
24726286Saw148015 		    (u_longlong_t)appendsize, filedesc, strerror(errno));
24735673Saw148015 		flowop_endop(threadflow, flowop, 0);
24746084Saw148015 		return (FILEBENCH_ERROR);
24755184Sek110237 	}
24765184Sek110237 
24775673Saw148015 	flowop_endop(threadflow, flowop, appendsize);
24785184Sek110237 
24796084Saw148015 	return (FILEBENCH_OK);
24805184Sek110237 }
24815184Sek110237 
24826212Saw148015 typedef struct testrandvar_priv {
24836212Saw148015 	uint64_t sample_count;
24846212Saw148015 	double val_sum;
24856212Saw148015 	double sqr_sum;
24866212Saw148015 } testrandvar_priv_t;
24876212Saw148015 
24886212Saw148015 /*
24896212Saw148015  * flowop to calculate various statistics from the number stream
24906212Saw148015  * produced by a random variable. This allows verification that the
24916212Saw148015  * random distribution used to define the random variable is producing
24926212Saw148015  * the expected distribution of random numbers.
24936212Saw148015  */
24946212Saw148015 /* ARGSUSED */
24956212Saw148015 static int
24966212Saw148015 flowoplib_testrandvar(threadflow_t *threadflow, flowop_t *flowop)
24976212Saw148015 {
24986212Saw148015 	testrandvar_priv_t	*mystats;
24996212Saw148015 	double			value;
25006212Saw148015 
25016212Saw148015 	if ((mystats = (testrandvar_priv_t *)flowop->fo_private) == NULL) {
25026212Saw148015 		filebench_log(LOG_ERROR, "testrandvar not initialized\n");
25036212Saw148015 		filebench_shutdown(1);
25046212Saw148015 		return (-1);
25056212Saw148015 	}
25066212Saw148015 
25076212Saw148015 	value = avd_get_dbl(flowop->fo_value);
25086212Saw148015 
25096212Saw148015 	mystats->sample_count++;
25106212Saw148015 	mystats->val_sum += value;
25116212Saw148015 	mystats->sqr_sum += (value * value);
25126212Saw148015 
25136212Saw148015 	return (0);
25146212Saw148015 }
25156212Saw148015 
25166212Saw148015 /*
25176212Saw148015  * Initialize the private data area used to accumulate the statistics
25186212Saw148015  */
25196212Saw148015 static int
25206212Saw148015 flowoplib_testrandvar_init(flowop_t *flowop)
25216212Saw148015 {
25226212Saw148015 	testrandvar_priv_t	*mystats;
25236212Saw148015 
25246212Saw148015 	if ((mystats = (testrandvar_priv_t *)
25256212Saw148015 	    malloc(sizeof (testrandvar_priv_t))) == NULL) {
25266212Saw148015 		filebench_log(LOG_ERROR, "could not initialize testrandvar");
25276212Saw148015 		filebench_shutdown(1);
25286212Saw148015 		return (-1);
25296212Saw148015 	}
25306212Saw148015 
25316212Saw148015 	mystats->sample_count = 0;
25326212Saw148015 	mystats->val_sum = 0;
25336212Saw148015 	mystats->sqr_sum = 0;
25346212Saw148015 	flowop->fo_private = (void *)mystats;
25356212Saw148015 
25366212Saw148015 	(void) ipc_mutex_unlock(&flowop->fo_lock);
25376212Saw148015 	return (0);
25386212Saw148015 }
25396212Saw148015 
25406212Saw148015 /*
25416212Saw148015  * Print out the accumulated statistics, and free the private storage
25426212Saw148015  */
25436212Saw148015 static void
25446212Saw148015 flowoplib_testrandvar_destruct(flowop_t *flowop)
25456212Saw148015 {
25466212Saw148015 	testrandvar_priv_t	*mystats;
25476212Saw148015 	double mean, std_dev, dbl_count;
25486212Saw148015 
25496212Saw148015 	(void) ipc_mutex_lock(&flowop->fo_lock);
25506212Saw148015 	if ((mystats = (testrandvar_priv_t *)
25516212Saw148015 	    flowop->fo_private) == NULL) {
25526212Saw148015 		(void) ipc_mutex_unlock(&flowop->fo_lock);
25536212Saw148015 		return;
25546212Saw148015 	}
25556212Saw148015 
25566212Saw148015 	flowop->fo_private = NULL;
25576212Saw148015 	(void) ipc_mutex_unlock(&flowop->fo_lock);
25586212Saw148015 
25596212Saw148015 	dbl_count = (double)mystats->sample_count;
25606212Saw148015 	mean = mystats->val_sum / dbl_count;
25616212Saw148015 	std_dev = sqrt((mystats->sqr_sum / dbl_count) - (mean * mean)) / mean;
25626212Saw148015 
25636212Saw148015 	filebench_log(LOG_VERBOSE,
25646286Saw148015 	    "testrandvar: ops = %llu, mean = %8.2lf, stddev = %8.2lf",
25656286Saw148015 	    (u_longlong_t)mystats->sample_count, mean, std_dev);
25666212Saw148015 	free(mystats);
25676212Saw148015 }
25685184Sek110237 
25695184Sek110237 /*
25705184Sek110237  * Prints usage information for flowop operations.
25715184Sek110237  */
25725184Sek110237 void
25735184Sek110237 flowoplib_usage()
25745184Sek110237 {
25755184Sek110237 	(void) fprintf(stderr,
25765184Sek110237 	    "flowop [openfile|createfile] name=<name>,fileset=<fname>\n");
25775184Sek110237 	(void) fprintf(stderr,
25785184Sek110237 	    "                       [,fd=<file desc num>]\n");
25795184Sek110237 	(void) fprintf(stderr, "\n");
25805184Sek110237 	(void) fprintf(stderr,
25815184Sek110237 	    "flowop closefile name=<name>,fd=<file desc num>]\n");
25825184Sek110237 	(void) fprintf(stderr, "\n");
25835184Sek110237 	(void) fprintf(stderr, "flowop deletefile name=<name>\n");
25845184Sek110237 	(void) fprintf(stderr, "                       [,fileset=<fname>]\n");
25855184Sek110237 	(void) fprintf(stderr,
25865184Sek110237 	    "                       [,fd=<file desc num>]\n");
25875184Sek110237 	(void) fprintf(stderr, "\n");
25885184Sek110237 	(void) fprintf(stderr, "flowop statfile name=<name>\n");
25895184Sek110237 	(void) fprintf(stderr, "                       [,fileset=<fname>]\n");
25905184Sek110237 	(void) fprintf(stderr,
25915184Sek110237 	    "                       [,fd=<file desc num>]\n");
25925184Sek110237 	(void) fprintf(stderr, "\n");
25935184Sek110237 	(void) fprintf(stderr,
25945184Sek110237 	    "flowop fsync name=<name>,fd=<file desc num>]\n");
25955184Sek110237 	(void) fprintf(stderr, "\n");
25965184Sek110237 	(void) fprintf(stderr,
25975184Sek110237 	    "flowop fsyncset name=<name>,fileset=<fname>]\n");
25985184Sek110237 	(void) fprintf(stderr, "\n");
25995184Sek110237 	(void) fprintf(stderr, "flowop [write|read|aiowrite] name=<name>, \n");
26005184Sek110237 	(void) fprintf(stderr,
26015184Sek110237 	    "                       filename|fileset=<fname>,\n");
26025184Sek110237 	(void) fprintf(stderr, "                       iosize=<size>\n");
26035184Sek110237 	(void) fprintf(stderr, "                       [,directio]\n");
26045184Sek110237 	(void) fprintf(stderr, "                       [,dsync]\n");
26055184Sek110237 	(void) fprintf(stderr, "                       [,iters=<count>]\n");
26065184Sek110237 	(void) fprintf(stderr, "                       [,random]\n");
26075184Sek110237 	(void) fprintf(stderr, "                       [,opennext]\n");
26085184Sek110237 	(void) fprintf(stderr, "                       [,workingset=<size>]\n");
26095184Sek110237 	(void) fprintf(stderr,
26105184Sek110237 	    "flowop [appendfile|appendfilerand] name=<name>, \n");
26115184Sek110237 	(void) fprintf(stderr,
26125184Sek110237 	    "                       filename|fileset=<fname>,\n");
26135184Sek110237 	(void) fprintf(stderr, "                       iosize=<size>\n");
26145184Sek110237 	(void) fprintf(stderr, "                       [,dsync]\n");
26155184Sek110237 	(void) fprintf(stderr, "                       [,iters=<count>]\n");
26165184Sek110237 	(void) fprintf(stderr, "                       [,workingset=<size>]\n");
26175184Sek110237 	(void) fprintf(stderr,
26185184Sek110237 	    "flowop [readwholefile|writewholefile] name=<name>, \n");
26195184Sek110237 	(void) fprintf(stderr,
26205184Sek110237 	    "                       filename|fileset=<fname>,\n");
26215184Sek110237 	(void) fprintf(stderr, "                       iosize=<size>\n");
26225184Sek110237 	(void) fprintf(stderr, "                       [,dsync]\n");
26235184Sek110237 	(void) fprintf(stderr, "                       [,iters=<count>]\n");
26245184Sek110237 	(void) fprintf(stderr, "\n");
26255184Sek110237 	(void) fprintf(stderr, "flowop aiowait name=<name>,target="
26265184Sek110237 	    "<aiowrite-flowop>\n");
26275184Sek110237 	(void) fprintf(stderr, "\n");
26285184Sek110237 	(void) fprintf(stderr, "flowop sempost name=<name>,"
26295184Sek110237 	    "target=<semblock-flowop>,\n");
26305184Sek110237 	(void) fprintf(stderr,
26315184Sek110237 	    "                       value=<increment-to-post>\n");
26325184Sek110237 	(void) fprintf(stderr, "\n");
26335184Sek110237 	(void) fprintf(stderr, "flowop semblock name=<name>,value="
26345184Sek110237 	    "<decrement-to-receive>,\n");
26355184Sek110237 	(void) fprintf(stderr, "                       highwater="
26365184Sek110237 	    "<inbound-queue-max>\n");
26375184Sek110237 	(void) fprintf(stderr, "\n");
26385184Sek110237 	(void) fprintf(stderr, "flowop block name=<name>\n");
26395184Sek110237 	(void) fprintf(stderr, "\n");
26405184Sek110237 	(void) fprintf(stderr,
26415184Sek110237 	    "flowop wakeup name=<name>,target=<block-flowop>,\n");
26425184Sek110237 	(void) fprintf(stderr, "\n");
26435184Sek110237 	(void) fprintf(stderr,
26445184Sek110237 	    "flowop hog name=<name>,value=<number-of-mem-ops>\n");
26455184Sek110237 	(void) fprintf(stderr,
26465184Sek110237 	    "flowop delay name=<name>,value=<number-of-seconds>\n");
26475184Sek110237 	(void) fprintf(stderr, "\n");
26485184Sek110237 	(void) fprintf(stderr, "flowop eventlimit name=<name>\n");
26495184Sek110237 	(void) fprintf(stderr, "flowop bwlimit name=<name>,value=<mb/s>\n");
26505184Sek110237 	(void) fprintf(stderr, "flowop iopslimit name=<name>,value=<iop/s>\n");
26515184Sek110237 	(void) fprintf(stderr,
26525184Sek110237 	    "flowop finishoncount name=<name>,value=<ops/s>\n");
26535184Sek110237 	(void) fprintf(stderr,
26545184Sek110237 	    "flowop finishonbytes name=<name>,value=<bytes>\n");
26555184Sek110237 	(void) fprintf(stderr, "\n");
26565184Sek110237 	(void) fprintf(stderr, "\n");
26575184Sek110237 }
2658