xref: /onnv-gate/usr/src/cmd/filebench/common/flowop.c (revision 9356:2ff1c33b24c1)
15184Sek110237 /*
25184Sek110237  * CDDL HEADER START
35184Sek110237  *
45184Sek110237  * The contents of this file are subject to the terms of the
55184Sek110237  * Common Development and Distribution License (the "License").
65184Sek110237  * You may not use this file except in compliance with the License.
75184Sek110237  *
85184Sek110237  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
95184Sek110237  * or http://www.opensolaris.org/os/licensing.
105184Sek110237  * See the License for the specific language governing permissions
115184Sek110237  * and limitations under the License.
125184Sek110237  *
135184Sek110237  * When distributing Covered Code, include this CDDL HEADER in each
145184Sek110237  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
155184Sek110237  * If applicable, add the following below this CDDL HEADER, with the
165184Sek110237  * fields enclosed by brackets "[]" replaced with your own identifying
175184Sek110237  * information: Portions Copyright [yyyy] [name of copyright owner]
185184Sek110237  *
195184Sek110237  * CDDL HEADER END
205184Sek110237  */
215184Sek110237 /*
228615SAndrew.W.Wilson@sun.com  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
235184Sek110237  * Use is subject to license terms.
245184Sek110237  */
255184Sek110237 
265184Sek110237 #include "config.h"
275184Sek110237 
285184Sek110237 #ifdef HAVE_LWPS
295184Sek110237 #include <sys/lwp.h>
305184Sek110237 #endif
315184Sek110237 #include <fcntl.h>
325184Sek110237 #include "filebench.h"
335184Sek110237 #include "flowop.h"
345184Sek110237 #include "stats.h"
355184Sek110237 
365184Sek110237 #ifdef LINUX_PORT
375184Sek110237 #include <sys/types.h>
385184Sek110237 #include <linux/unistd.h>
395184Sek110237 #endif
405184Sek110237 
415184Sek110237 static flowop_t *flowop_define_common(threadflow_t *threadflow, char *name,
426550Saw148015     flowop_t *inherit, flowop_t **flowoplist_hdp, int instance, int type);
436550Saw148015 static int flowop_composite(threadflow_t *threadflow, flowop_t *flowop);
446550Saw148015 static int flowop_composite_init(flowop_t *flowop);
456550Saw148015 static void flowop_composite_destruct(flowop_t *flowop);
465184Sek110237 
475184Sek110237 /*
485184Sek110237  * A collection of flowop support functions. The actual code that
495184Sek110237  * implements the various flowops is in flowop_library.c.
505184Sek110237  *
515184Sek110237  * Routines for defining, creating, initializing and destroying
525184Sek110237  * flowops, cyclically invoking the flowops on each threadflow's flowop
535184Sek110237  * list, collecting statistics about flowop execution, and other
545184Sek110237  * housekeeping duties are included in this file.
556550Saw148015  *
566550Saw148015  * User Defined Composite Flowops
576550Saw148015  *    The ability to define new flowops as lists of built-in or previously
586550Saw148015  * defined flowops has been added to Filebench. In a sense they are like
596550Saw148015  * in-line subroutines, which can have default attributes set at definition
606550Saw148015  * time and passed arguments at invocation time. Like other flowops (and
616550Saw148015  * unlike conventional subroutines) you can invoke them with an iteration
626550Saw148015  * count (the "iter" attribute), and they will loop through their associated
636550Saw148015  * list of flowops for "iter" number of times each time they are encountered
646550Saw148015  * in the thread or outer composite flowop which invokes them.
656550Saw148015  *
666550Saw148015  * Composite flowops are created with a "define" command, are given a name,
676550Saw148015  * optional default attributes, and local variable definitions on the
686550Saw148015  * "define" command line, followed by a brace enclosed list of flowops
696550Saw148015  * to execute. The enclosed flowops may include attributes that reference
706550Saw148015  * the local variables, as well as constants and global variables.
716550Saw148015  *
726550Saw148015  * Composite flowops are used pretty much like regular flowops, but you can
736550Saw148015  * also set local variables to constants or global variables ($local_var =
746550Saw148015  * [$var | $random_var | string | boolean | integer | double]) as part of
756550Saw148015  * the invocation. Thus each invocation can pass customized values to its
766550Saw148015  * inner flowops, greatly increasing their generality.
776550Saw148015  *
786550Saw148015  * All flowops are placed on a global, singly linked list, with fo_next
796550Saw148015  * being the link pointer for this list. The are also placed on a private
806550Saw148015  * list for the thread or composite flowop they are part of. The tf_thrd_fops
816550Saw148015  * pointer in the thread will point to the list of top level flowops in the
826550Saw148015  * thread, which are linked together by fo_exec_next. If any of these flowops
836550Saw148015  * are composite flowops, they will have a list of second level flowops rooted
846550Saw148015  * at the composite flowop's fo_comp_fops pointer. So, there is one big list
856550Saw148015  * of all flowops, and an n-arry tree of threads, composite flowops, and
866550Saw148015  * flowops, with composite flowops being the branch nodes in the tree.
876550Saw148015  *
886550Saw148015  * To illustrate, if we have three first level flowops, the first of which is
896550Saw148015  * a composite flowop consisting of two other flowops, we get:
906550Saw148015  *
916550Saw148015  * Thread->tf_thrd_fops -> flowop->fo_exec_next -> flowop->fo_exec_next
926550Saw148015  *			   flowop->fo_comp_fops		    |
936550Saw148015  *				    |			    V
946550Saw148015  *				    |			flowop->fo_exec_next
956550Saw148015  *				    |
966550Saw148015  *				    V
976550Saw148015  *				flowop->fo_exec_next -> flowop->fo_exec_next
986550Saw148015  *
996550Saw148015  * And all five flowops (plus others from any other threads) are on a global
1006550Saw148015  * list linked with fo_next.
1015184Sek110237  */
1025184Sek110237 
1035184Sek110237 /*
1045184Sek110237  * Prints the name and instance number of each flowop in
1055184Sek110237  * the supplied list to the filebench log.
1065184Sek110237  */
1075184Sek110237 int
flowop_printlist(flowop_t * list)1085184Sek110237 flowop_printlist(flowop_t *list)
1095184Sek110237 {
1105184Sek110237 	flowop_t *flowop = list;
1115184Sek110237 
1125184Sek110237 	while (flowop) {
1135184Sek110237 		filebench_log(LOG_DEBUG_IMPL, "flowop-list %s-%d",
1145184Sek110237 		    flowop->fo_name, flowop->fo_instance);
1156550Saw148015 		flowop = flowop->fo_exec_next;
1165184Sek110237 	}
1175184Sek110237 	return (0);
1185184Sek110237 }
1195184Sek110237 
1206212Saw148015 /*
1216212Saw148015  * Prints the name and instance number of all flowops on
1226212Saw148015  * the master flowop list to the console and the filebench log.
1236212Saw148015  */
1246212Saw148015 void
flowop_printall(void)1256212Saw148015 flowop_printall(void)
1266212Saw148015 {
1276391Saw148015 	flowop_t *flowop = filebench_shm->shm_flowoplist;
1286212Saw148015 
1296212Saw148015 	while (flowop) {
1306212Saw148015 		filebench_log(LOG_VERBOSE, "flowop-list %s-%d",
1316212Saw148015 		    flowop->fo_name, flowop->fo_instance);
1326212Saw148015 		flowop = flowop->fo_next;
1336212Saw148015 	}
1346212Saw148015 }
1356212Saw148015 
1365184Sek110237 #define	TIMESPEC_TO_HRTIME(s, e) (((e.tv_sec - s.tv_sec) * 1000000000LL) + \
1375184Sek110237 					(e.tv_nsec - s.tv_nsec))
1385184Sek110237 /*
1395184Sek110237  * Puts current high resolution time in start time entry
1405184Sek110237  * for threadflow and may also calculate running filebench
1415184Sek110237  * overhead statistics.
1425184Sek110237  */
1435184Sek110237 void
flowop_beginop(threadflow_t * threadflow,flowop_t * flowop)1445184Sek110237 flowop_beginop(threadflow_t *threadflow, flowop_t *flowop)
1455184Sek110237 {
1465184Sek110237 #ifdef HAVE_PROCFS
147*9356SAndrew.W.Wilson@sun.com 	if ((filebench_shm->shm_mmode & FILEBENCH_MODE_NOUSAGE) == 0) {
148*9356SAndrew.W.Wilson@sun.com 		if ((noproc == 0) && (threadflow->tf_lwpusagefd == 0)) {
149*9356SAndrew.W.Wilson@sun.com 			char procname[128];
1505184Sek110237 
151*9356SAndrew.W.Wilson@sun.com 			(void) snprintf(procname, sizeof (procname),
152*9356SAndrew.W.Wilson@sun.com 			    "/proc/%d/lwp/%d/lwpusage", my_pid, _lwp_self());
153*9356SAndrew.W.Wilson@sun.com 			threadflow->tf_lwpusagefd = open(procname, O_RDONLY);
154*9356SAndrew.W.Wilson@sun.com 		}
1555184Sek110237 
156*9356SAndrew.W.Wilson@sun.com 		(void) pread(threadflow->tf_lwpusagefd,
157*9356SAndrew.W.Wilson@sun.com 		    &threadflow->tf_susage,
158*9356SAndrew.W.Wilson@sun.com 		    sizeof (struct prusage), 0);
1595184Sek110237 
160*9356SAndrew.W.Wilson@sun.com 		/* Compute overhead time in this thread around op */
161*9356SAndrew.W.Wilson@sun.com 		if (threadflow->tf_eusage.pr_stime.tv_nsec) {
162*9356SAndrew.W.Wilson@sun.com 			flowop->fo_stats.fs_mstate[FLOW_MSTATE_OHEAD] +=
163*9356SAndrew.W.Wilson@sun.com 			    TIMESPEC_TO_HRTIME(threadflow->tf_eusage.pr_utime,
164*9356SAndrew.W.Wilson@sun.com 			    threadflow->tf_susage.pr_utime) +
165*9356SAndrew.W.Wilson@sun.com 			    TIMESPEC_TO_HRTIME(threadflow->tf_eusage.pr_ttime,
166*9356SAndrew.W.Wilson@sun.com 			    threadflow->tf_susage.pr_ttime) +
167*9356SAndrew.W.Wilson@sun.com 			    TIMESPEC_TO_HRTIME(threadflow->tf_eusage.pr_stime,
168*9356SAndrew.W.Wilson@sun.com 			    threadflow->tf_susage.pr_stime);
169*9356SAndrew.W.Wilson@sun.com 		}
1705184Sek110237 	}
1715184Sek110237 #endif
172*9356SAndrew.W.Wilson@sun.com 
1735184Sek110237 	/* Start of op for this thread */
1745184Sek110237 	threadflow->tf_stime = gethrtime();
1755184Sek110237 }
1765184Sek110237 
1775184Sek110237 flowstat_t controlstats;
1786212Saw148015 pthread_mutex_t controlstats_lock;
1795184Sek110237 static int controlstats_zeroed = 0;
1805184Sek110237 
1815184Sek110237 /*
1825184Sek110237  * Updates flowop's latency statistics, using saved start
1835184Sek110237  * time and current high resolution time. Updates flowop's
1845184Sek110237  * io count and transferred bytes statistics. Also updates
1855184Sek110237  * threadflow's and flowop's cumulative read or write byte
1865184Sek110237  * and io count statistics.
1875184Sek110237  */
1885184Sek110237 void
flowop_endop(threadflow_t * threadflow,flowop_t * flowop,int64_t bytes)1895673Saw148015 flowop_endop(threadflow_t *threadflow, flowop_t *flowop, int64_t bytes)
1905184Sek110237 {
1915184Sek110237 	hrtime_t t;
1925184Sek110237 
1935184Sek110237 	flowop->fo_stats.fs_mstate[FLOW_MSTATE_LAT] +=
1945184Sek110237 	    (gethrtime() - threadflow->tf_stime);
1955184Sek110237 #ifdef HAVE_PROCFS
196*9356SAndrew.W.Wilson@sun.com 	if ((filebench_shm->shm_mmode & FILEBENCH_MODE_NOUSAGE) == 0) {
197*9356SAndrew.W.Wilson@sun.com 		if ((pread(threadflow->tf_lwpusagefd, &threadflow->tf_eusage,
198*9356SAndrew.W.Wilson@sun.com 		    sizeof (struct prusage), 0)) != sizeof (struct prusage))
199*9356SAndrew.W.Wilson@sun.com 			filebench_log(LOG_ERROR, "cannot read /proc");
2005184Sek110237 
201*9356SAndrew.W.Wilson@sun.com 		t =
202*9356SAndrew.W.Wilson@sun.com 		    TIMESPEC_TO_HRTIME(threadflow->tf_susage.pr_utime,
203*9356SAndrew.W.Wilson@sun.com 		    threadflow->tf_eusage.pr_utime) +
204*9356SAndrew.W.Wilson@sun.com 		    TIMESPEC_TO_HRTIME(threadflow->tf_susage.pr_ttime,
205*9356SAndrew.W.Wilson@sun.com 		    threadflow->tf_eusage.pr_ttime) +
206*9356SAndrew.W.Wilson@sun.com 		    TIMESPEC_TO_HRTIME(threadflow->tf_susage.pr_stime,
207*9356SAndrew.W.Wilson@sun.com 		    threadflow->tf_eusage.pr_stime);
208*9356SAndrew.W.Wilson@sun.com 		flowop->fo_stats.fs_mstate[FLOW_MSTATE_CPU] += t;
2095184Sek110237 
210*9356SAndrew.W.Wilson@sun.com 		flowop->fo_stats.fs_mstate[FLOW_MSTATE_WAIT] +=
211*9356SAndrew.W.Wilson@sun.com 		    TIMESPEC_TO_HRTIME(threadflow->tf_susage.pr_tftime,
212*9356SAndrew.W.Wilson@sun.com 		    threadflow->tf_eusage.pr_tftime) +
213*9356SAndrew.W.Wilson@sun.com 		    TIMESPEC_TO_HRTIME(threadflow->tf_susage.pr_dftime,
214*9356SAndrew.W.Wilson@sun.com 		    threadflow->tf_eusage.pr_dftime) +
215*9356SAndrew.W.Wilson@sun.com 		    TIMESPEC_TO_HRTIME(threadflow->tf_susage.pr_kftime,
216*9356SAndrew.W.Wilson@sun.com 		    threadflow->tf_eusage.pr_kftime) +
217*9356SAndrew.W.Wilson@sun.com 		    TIMESPEC_TO_HRTIME(threadflow->tf_susage.pr_kftime,
218*9356SAndrew.W.Wilson@sun.com 		    threadflow->tf_eusage.pr_kftime) +
219*9356SAndrew.W.Wilson@sun.com 		    TIMESPEC_TO_HRTIME(threadflow->tf_susage.pr_slptime,
220*9356SAndrew.W.Wilson@sun.com 		    threadflow->tf_eusage.pr_slptime);
221*9356SAndrew.W.Wilson@sun.com 	}
2225184Sek110237 #endif
2235184Sek110237 
2245184Sek110237 	flowop->fo_stats.fs_count++;
2255673Saw148015 	flowop->fo_stats.fs_bytes += bytes;
2266212Saw148015 	(void) ipc_mutex_lock(&controlstats_lock);
2275184Sek110237 	if ((flowop->fo_type & FLOW_TYPE_IO) ||
2285184Sek110237 	    (flowop->fo_type & FLOW_TYPE_AIO)) {
2295184Sek110237 		controlstats.fs_count++;
2305673Saw148015 		controlstats.fs_bytes += bytes;
2315184Sek110237 	}
2325184Sek110237 	if (flowop->fo_attrs & FLOW_ATTR_READ) {
2335673Saw148015 		threadflow->tf_stats.fs_rbytes += bytes;
2345184Sek110237 		threadflow->tf_stats.fs_rcount++;
2355184Sek110237 		flowop->fo_stats.fs_rcount++;
2365673Saw148015 		controlstats.fs_rbytes += bytes;
2375184Sek110237 		controlstats.fs_rcount++;
2385184Sek110237 	} else if (flowop->fo_attrs & FLOW_ATTR_WRITE) {
2395673Saw148015 		threadflow->tf_stats.fs_wbytes += bytes;
2405184Sek110237 		threadflow->tf_stats.fs_wcount++;
2415184Sek110237 		flowop->fo_stats.fs_wcount++;
2425673Saw148015 		controlstats.fs_wbytes += bytes;
2435184Sek110237 		controlstats.fs_wcount++;
2445184Sek110237 	}
2456212Saw148015 	(void) ipc_mutex_unlock(&controlstats_lock);
2465184Sek110237 }
2475184Sek110237 
2485184Sek110237 /*
2495184Sek110237  * Calls the flowop's initialization function, pointed to by
2505184Sek110237  * flowop->fo_init.
2515184Sek110237  */
2525184Sek110237 static int
flowop_initflow(flowop_t * flowop)2535184Sek110237 flowop_initflow(flowop_t *flowop)
2545184Sek110237 {
2556212Saw148015 	/*
2566212Saw148015 	 * save static copies of two items, in case they are supplied
2576212Saw148015 	 * from random variables
2586212Saw148015 	 */
2597556SAndrew.W.Wilson@sun.com 	if (!AVD_IS_STRING(flowop->fo_value))
2607556SAndrew.W.Wilson@sun.com 		flowop->fo_constvalue = avd_get_int(flowop->fo_value);
2617556SAndrew.W.Wilson@sun.com 
2626212Saw148015 	flowop->fo_constwss = avd_get_int(flowop->fo_wss);
2636212Saw148015 
2645184Sek110237 	if ((*flowop->fo_init)(flowop) < 0) {
2655184Sek110237 		filebench_log(LOG_ERROR, "flowop %s-%d init failed",
2665184Sek110237 		    flowop->fo_name, flowop->fo_instance);
2675184Sek110237 		return (-1);
2685184Sek110237 	}
2695184Sek110237 	return (0);
2705184Sek110237 }
2715184Sek110237 
2726550Saw148015 static int
flowop_create_runtime_flowops(threadflow_t * threadflow,flowop_t ** ops_list_ptr)2736550Saw148015 flowop_create_runtime_flowops(threadflow_t *threadflow, flowop_t **ops_list_ptr)
2746550Saw148015 {
2756550Saw148015 	flowop_t *flowop = *ops_list_ptr;
2766550Saw148015 
2776550Saw148015 	while (flowop) {
2786550Saw148015 		flowop_t *newflowop;
2796550Saw148015 
2806550Saw148015 		if (flowop == *ops_list_ptr)
2816550Saw148015 			*ops_list_ptr = NULL;
2826550Saw148015 
2836550Saw148015 		newflowop = flowop_define_common(threadflow, flowop->fo_name,
2846550Saw148015 		    flowop, ops_list_ptr, 1, 0);
2856550Saw148015 		if (newflowop == NULL)
2866550Saw148015 			return (FILEBENCH_ERROR);
2876550Saw148015 
2886550Saw148015 		/* check for fo_filename attribute, and resolve if present */
2896550Saw148015 		if (flowop->fo_filename) {
2906550Saw148015 			char *name;
2916550Saw148015 
2926550Saw148015 			name = avd_get_str(flowop->fo_filename);
2936550Saw148015 			newflowop->fo_fileset = fileset_find(name);
2946550Saw148015 
2956550Saw148015 			if (newflowop->fo_fileset == NULL) {
2966550Saw148015 				filebench_log(LOG_ERROR,
2976550Saw148015 				    "flowop %s: file %s not found",
2986550Saw148015 				    newflowop->fo_name, name);
2996550Saw148015 				filebench_shutdown(1);
3006550Saw148015 			}
3016550Saw148015 		}
3026550Saw148015 
3036550Saw148015 		if (flowop_initflow(newflowop) < 0) {
3046550Saw148015 			filebench_log(LOG_ERROR, "Flowop init of %s failed",
3056550Saw148015 			    newflowop->fo_name);
3066550Saw148015 		}
3076550Saw148015 
3086550Saw148015 		flowop = flowop->fo_exec_next;
3096550Saw148015 	}
3106550Saw148015 	return (FILEBENCH_OK);
3116550Saw148015 }
3126550Saw148015 
3135184Sek110237 /*
3146084Saw148015  * Calls the flowop's destruct function, pointed to by
3156084Saw148015  * flowop->fo_destruct.
3166084Saw148015  */
3176084Saw148015 static void
flowop_destructflow(flowop_t * flowop)3186084Saw148015 flowop_destructflow(flowop_t *flowop)
3196084Saw148015 {
3206084Saw148015 	(*flowop->fo_destruct)(flowop);
3216084Saw148015 }
3226084Saw148015 
3236084Saw148015 /*
3246084Saw148015  * call the destruct funtions of all the threadflow's flowops,
3256084Saw148015  * if it is still flagged as "running".
3266084Saw148015  */
3276084Saw148015 void
flowop_destruct_all_flows(threadflow_t * threadflow)3286084Saw148015 flowop_destruct_all_flows(threadflow_t *threadflow)
3296084Saw148015 {
3306084Saw148015 	flowop_t *flowop;
3316084Saw148015 
3326701Saw148015 	/* wait a moment to give other threads a chance to stop too */
3336701Saw148015 	(void) sleep(1);
3346701Saw148015 
3356084Saw148015 	(void) ipc_mutex_lock(&threadflow->tf_lock);
3366084Saw148015 
3376084Saw148015 	/* prepare to call destruct flow routines, if necessary */
3386084Saw148015 	if (threadflow->tf_running == 0) {
3396084Saw148015 
3406084Saw148015 		/* allready destroyed */
3416084Saw148015 		(void) ipc_mutex_unlock(&threadflow->tf_lock);
3426084Saw148015 		return;
3436084Saw148015 	}
3446084Saw148015 
3456550Saw148015 	flowop = threadflow->tf_thrd_fops;
3466084Saw148015 	threadflow->tf_running = 0;
3476084Saw148015 	(void) ipc_mutex_unlock(&threadflow->tf_lock);
3486084Saw148015 
3496084Saw148015 	while (flowop) {
3506084Saw148015 		flowop_destructflow(flowop);
3516550Saw148015 		flowop = flowop->fo_exec_next;
3526084Saw148015 	}
3536084Saw148015 }
3546084Saw148015 
3556084Saw148015 /*
3565184Sek110237  * The final initialization and main execution loop for the
3575184Sek110237  * worker threads. Sets threadflow and flowop start times,
3585184Sek110237  * waits for all process to start, then creates the runtime
3595184Sek110237  * flowops from those defined by the F language workload
3605184Sek110237  * script. It does some more initialization, then enters a
3615184Sek110237  * loop to repeatedly execute the flowops on the flowop list
3625184Sek110237  * until an abort condition is detected, at which time it exits.
3635184Sek110237  * This is the starting routine for the new worker thread
3645184Sek110237  * created by threadflow_createthread(), and is not currently
3655184Sek110237  * called from anywhere else.
3665184Sek110237  */
3675184Sek110237 void
flowop_start(threadflow_t * threadflow)3685184Sek110237 flowop_start(threadflow_t *threadflow)
3695184Sek110237 {
3705184Sek110237 	flowop_t *flowop;
3715184Sek110237 	size_t memsize;
3729326SAndrew.W.Wilson@sun.com 	int ret = FILEBENCH_OK;
3735184Sek110237 
3745184Sek110237 #ifdef HAVE_PROCFS
3755184Sek110237 	if (noproc == 0) {
3765184Sek110237 		char procname[128];
3775184Sek110237 		long ctl[2] = {PCSET, PR_MSACCT};
3785184Sek110237 		int pfd;
3795184Sek110237 
3805184Sek110237 		(void) snprintf(procname, sizeof (procname),
3816084Saw148015 		    "/proc/%d/lwp/%d/lwpctl", my_pid, _lwp_self());
3825184Sek110237 		pfd = open(procname, O_WRONLY);
3835184Sek110237 		(void) pwrite(pfd, &ctl, sizeof (ctl), 0);
3845184Sek110237 		(void) close(pfd);
3855184Sek110237 	}
3865184Sek110237 #endif
3875184Sek110237 
3886212Saw148015 	(void) ipc_mutex_lock(&controlstats_lock);
3895184Sek110237 	if (!controlstats_zeroed) {
3905184Sek110237 		(void) memset(&controlstats, 0, sizeof (controlstats));
3915184Sek110237 		controlstats_zeroed = 1;
3925184Sek110237 	}
3936212Saw148015 	(void) ipc_mutex_unlock(&controlstats_lock);
3945184Sek110237 
3956550Saw148015 	flowop = threadflow->tf_thrd_fops;
3965184Sek110237 	threadflow->tf_stats.fs_stime = gethrtime();
3975184Sek110237 	flowop->fo_stats.fs_stime = gethrtime();
3985184Sek110237 
3995184Sek110237 	/* Hold the flowop find lock as reader to prevent lookups */
4006391Saw148015 	(void) pthread_rwlock_rdlock(&filebench_shm->shm_flowop_find_lock);
4015184Sek110237 
4025184Sek110237 	/*
4035184Sek110237 	 * Block until all processes have started, acting like
4045184Sek110237 	 * a barrier. The original filebench process initially
4055184Sek110237 	 * holds the run_lock as a reader, preventing any of the
4065184Sek110237 	 * threads from obtaining the writer lock, and hence
4075184Sek110237 	 * passing this point. Once all processes and threads
4085184Sek110237 	 * have been created, the original process unlocks
4095184Sek110237 	 * run_lock, allowing each waiting thread to lock
4105184Sek110237 	 * and then immediately unlock it, then begin running.
4115184Sek110237 	 */
4126391Saw148015 	(void) pthread_rwlock_wrlock(&filebench_shm->shm_run_lock);
4136391Saw148015 	(void) pthread_rwlock_unlock(&filebench_shm->shm_run_lock);
4145184Sek110237 
4155184Sek110237 	/* Create the runtime flowops from those defined by the script */
4166391Saw148015 	(void) ipc_mutex_lock(&filebench_shm->shm_flowop_lock);
4176550Saw148015 	if (flowop_create_runtime_flowops(threadflow, &threadflow->tf_thrd_fops)
4186550Saw148015 	    != FILEBENCH_OK) {
4196550Saw148015 		(void) ipc_mutex_unlock(&filebench_shm->shm_flowop_lock);
4206550Saw148015 		filebench_shutdown(1);
4216550Saw148015 		return;
4225184Sek110237 	}
4236391Saw148015 	(void) ipc_mutex_unlock(&filebench_shm->shm_flowop_lock);
4245184Sek110237 
4255184Sek110237 	/* Release the find lock as reader to allow lookups */
4266391Saw148015 	(void) pthread_rwlock_unlock(&filebench_shm->shm_flowop_find_lock);
4275184Sek110237 
4285184Sek110237 	/* Set to the start of the new flowop list */
4296550Saw148015 	flowop = threadflow->tf_thrd_fops;
4305184Sek110237 
4315184Sek110237 	threadflow->tf_abort = 0;
4325184Sek110237 	threadflow->tf_running = 1;
4335184Sek110237 
4346212Saw148015 	memsize = (size_t)threadflow->tf_constmemsize;
4356212Saw148015 
4365184Sek110237 	/* If we are going to use ISM, allocate later */
4375184Sek110237 	if (threadflow->tf_attrs & THREADFLOW_USEISM) {
4385184Sek110237 		threadflow->tf_mem =
4396212Saw148015 		    ipc_ismmalloc(memsize);
4405184Sek110237 	} else {
4416212Saw148015 		threadflow->tf_mem =
4426212Saw148015 		    malloc(memsize);
4435184Sek110237 	}
4445184Sek110237 
4455184Sek110237 	(void) memset(threadflow->tf_mem, 0, memsize);
4465184Sek110237 	filebench_log(LOG_DEBUG_SCRIPT, "Thread allocated %d bytes", memsize);
4475184Sek110237 
4485184Sek110237 #ifdef HAVE_LWPS
4495184Sek110237 	filebench_log(LOG_DEBUG_SCRIPT, "Thread %zx (%d) started",
4505184Sek110237 	    threadflow,
4515184Sek110237 	    _lwp_self());
4525184Sek110237 #endif
4535184Sek110237 
4545184Sek110237 	/* Main filebench worker loop */
4559326SAndrew.W.Wilson@sun.com 	while (ret == FILEBENCH_OK) {
4566212Saw148015 		int i, count;
4575184Sek110237 
4585184Sek110237 		/* Abort if asked */
4596391Saw148015 		if (threadflow->tf_abort || filebench_shm->shm_f_abort)
4605184Sek110237 			break;
4615184Sek110237 
4625184Sek110237 		/* Be quiet while stats are gathered */
4636391Saw148015 		if (filebench_shm->shm_bequiet) {
4645184Sek110237 			(void) sleep(1);
4655184Sek110237 			continue;
4665184Sek110237 		}
4675184Sek110237 
4685184Sek110237 		/* Take it easy until everyone is ready to go */
4696701Saw148015 		if (!filebench_shm->shm_procs_running) {
4705184Sek110237 			(void) sleep(1);
4716391Saw148015 			continue;
4726391Saw148015 		}
4735184Sek110237 
4745184Sek110237 		if (flowop == NULL) {
4755184Sek110237 			filebench_log(LOG_ERROR, "flowop_read null flowop");
4765184Sek110237 			return;
4775184Sek110237 		}
4785184Sek110237 
4796212Saw148015 		if (flowop->fo_stats.fs_stime == 0)
4806212Saw148015 			flowop->fo_stats.fs_stime = gethrtime();
4815184Sek110237 
4825184Sek110237 		/* Execute the flowop for fo_iters times */
4836550Saw148015 		count = (int)avd_get_int(flowop->fo_iters);
4846212Saw148015 		for (i = 0; i < count; i++) {
4856212Saw148015 
4865184Sek110237 			filebench_log(LOG_DEBUG_SCRIPT, "%s: executing flowop "
4875184Sek110237 			    "%s-%d", threadflow->tf_name, flowop->fo_name,
4885184Sek110237 			    flowop->fo_instance);
4896212Saw148015 
4905184Sek110237 			ret = (*flowop->fo_func)(threadflow, flowop);
4915184Sek110237 
4926084Saw148015 			/*
4936084Saw148015 			 * Return value FILEBENCH_ERROR means "flowop
4946084Saw148015 			 * failed, stop the filebench run"
4956084Saw148015 			 */
4966084Saw148015 			if (ret == FILEBENCH_ERROR) {
4976084Saw148015 				filebench_log(LOG_ERROR,
4986084Saw148015 				    "%s-%d: flowop %s-%d failed",
4996084Saw148015 				    threadflow->tf_name,
5006084Saw148015 				    threadflow->tf_instance,
5016084Saw148015 				    flowop->fo_name,
5025184Sek110237 				    flowop->fo_instance);
5035184Sek110237 				(void) ipc_mutex_lock(&threadflow->tf_lock);
5045184Sek110237 				threadflow->tf_abort = 1;
5056391Saw148015 				filebench_shm->shm_f_abort =
5066391Saw148015 				    FILEBENCH_ABORT_ERROR;
5075184Sek110237 				(void) ipc_mutex_unlock(&threadflow->tf_lock);
5085184Sek110237 				break;
5095184Sek110237 			}
5106084Saw148015 
5115184Sek110237 			/*
5126084Saw148015 			 * Return value of FILEBENCH_NORSC means "stop
5136084Saw148015 			 * the filebench run" if in "end on no work mode",
5146084Saw148015 			 * otherwise it indicates an error
5155184Sek110237 			 */
5166084Saw148015 			if (ret == FILEBENCH_NORSC) {
5175184Sek110237 				(void) ipc_mutex_lock(&threadflow->tf_lock);
5186084Saw148015 				threadflow->tf_abort = FILEBENCH_DONE;
5196084Saw148015 				if (filebench_shm->shm_rmode ==
5206084Saw148015 				    FILEBENCH_MODE_Q1STDONE) {
5216391Saw148015 					filebench_shm->shm_f_abort =
5226084Saw148015 					    FILEBENCH_ABORT_RSRC;
5236084Saw148015 				} else if (filebench_shm->shm_rmode !=
5246084Saw148015 				    FILEBENCH_MODE_QALLDONE) {
5256084Saw148015 					filebench_log(LOG_ERROR1,
5266084Saw148015 					    "WARNING! Run stopped early:\n   "
5276084Saw148015 					    "             flowop %s-%d could "
5286084Saw148015 					    "not obtain a file. Please\n      "
5296084Saw148015 					    "          reduce runtime, "
5306084Saw148015 					    "increase fileset entries "
5316084Saw148015 					    "($nfiles), or switch modes.",
5326084Saw148015 					    flowop->fo_name,
5336084Saw148015 					    flowop->fo_instance);
5346391Saw148015 					filebench_shm->shm_f_abort =
5356084Saw148015 					    FILEBENCH_ABORT_ERROR;
5366084Saw148015 				}
5376084Saw148015 				(void) ipc_mutex_unlock(&threadflow->tf_lock);
5386084Saw148015 				break;
5396084Saw148015 			}
5406084Saw148015 
5416084Saw148015 			/*
5426084Saw148015 			 * Return value of FILEBENCH_DONE means "stop
5436084Saw148015 			 * the filebench run without error"
5446084Saw148015 			 */
5456084Saw148015 			if (ret == FILEBENCH_DONE) {
5466084Saw148015 				(void) ipc_mutex_lock(&threadflow->tf_lock);
5476084Saw148015 				threadflow->tf_abort = FILEBENCH_DONE;
5486391Saw148015 				filebench_shm->shm_f_abort =
5496391Saw148015 				    FILEBENCH_ABORT_DONE;
5505184Sek110237 				(void) ipc_mutex_unlock(&threadflow->tf_lock);
5515184Sek110237 				break;
5525184Sek110237 			}
5536391Saw148015 
5546391Saw148015 			/*
5556391Saw148015 			 * If we get here and the return is something other
5566391Saw148015 			 * than FILEBENCH_OK, it means a spurious code
5576391Saw148015 			 * was returned, so treat as major error. This
5586391Saw148015 			 * probably indicates a bug in the flowop.
5596391Saw148015 			 */
5606391Saw148015 			if (ret != FILEBENCH_OK) {
5616391Saw148015 				filebench_log(LOG_ERROR,
5626391Saw148015 				    "Flowop %s unexpected return value = %d\n",
5636391Saw148015 				    flowop->fo_name, ret);
5646391Saw148015 				filebench_shm->shm_f_abort =
5656391Saw148015 				    FILEBENCH_ABORT_ERROR;
5666391Saw148015 				break;
5676391Saw148015 			}
5685184Sek110237 		}
5695184Sek110237 
5705184Sek110237 		/* advance to next flowop */
5716550Saw148015 		flowop = flowop->fo_exec_next;
5725184Sek110237 
5735184Sek110237 		/* but if at end of list, start over from the beginning */
5745184Sek110237 		if (flowop == NULL) {
5756550Saw148015 			flowop = threadflow->tf_thrd_fops;
5765184Sek110237 			threadflow->tf_stats.fs_count++;
5775184Sek110237 		}
5785184Sek110237 	}
5795184Sek110237 
5805184Sek110237 #ifdef HAVE_LWPS
5815184Sek110237 	filebench_log(LOG_DEBUG_SCRIPT, "Thread %d exiting",
5825184Sek110237 	    _lwp_self());
5835184Sek110237 #endif
5845184Sek110237 
5856084Saw148015 	/* Tell flowops to destroy locally acquired state */
5866084Saw148015 	flowop_destruct_all_flows(threadflow);
5876084Saw148015 
5886084Saw148015 	pthread_exit(&threadflow->tf_abort);
5895184Sek110237 }
5905184Sek110237 
5918615SAndrew.W.Wilson@sun.com void flowoplib_flowinit(void);
5928615SAndrew.W.Wilson@sun.com void fb_lfs_flowinit(void);
5938615SAndrew.W.Wilson@sun.com 
5945184Sek110237 void
flowop_init(void)5955184Sek110237 flowop_init(void)
5965184Sek110237 {
5977556SAndrew.W.Wilson@sun.com 	(void) pthread_mutex_init(&controlstats_lock,
5987556SAndrew.W.Wilson@sun.com 	    ipc_mutexattr(IPC_MUTEX_NORMAL));
5998615SAndrew.W.Wilson@sun.com 	flowoplib_flowinit();
6005184Sek110237 }
6015184Sek110237 
6028615SAndrew.W.Wilson@sun.com static int plugin_flowinit_done = FALSE;
6038615SAndrew.W.Wilson@sun.com 
6048615SAndrew.W.Wilson@sun.com /*
6058615SAndrew.W.Wilson@sun.com  * Initialize any "plug-in" flowops. Called when the first "create fileset"
6068615SAndrew.W.Wilson@sun.com  * command is encountered.
6078615SAndrew.W.Wilson@sun.com  */
6088615SAndrew.W.Wilson@sun.com void
flowop_plugin_flowinit(void)6098615SAndrew.W.Wilson@sun.com flowop_plugin_flowinit(void)
6108615SAndrew.W.Wilson@sun.com {
6118615SAndrew.W.Wilson@sun.com 	if (plugin_flowinit_done)
6128615SAndrew.W.Wilson@sun.com 		return;
6138615SAndrew.W.Wilson@sun.com 
6148615SAndrew.W.Wilson@sun.com 	plugin_flowinit_done = TRUE;
6158615SAndrew.W.Wilson@sun.com 
6168615SAndrew.W.Wilson@sun.com 	switch (filebench_shm->shm_filesys_type) {
6178615SAndrew.W.Wilson@sun.com 	case LOCAL_FS_PLUG:
6188615SAndrew.W.Wilson@sun.com 		fb_lfs_flowinit();
6198615SAndrew.W.Wilson@sun.com 		break;
6208615SAndrew.W.Wilson@sun.com 
6218615SAndrew.W.Wilson@sun.com 	case NFS3_PLUG:
6228615SAndrew.W.Wilson@sun.com 	case NFS4_PLUG:
6238615SAndrew.W.Wilson@sun.com 	case CIFS_PLUG:
6248615SAndrew.W.Wilson@sun.com 		break;
6258615SAndrew.W.Wilson@sun.com 	}
6268615SAndrew.W.Wilson@sun.com }
6278615SAndrew.W.Wilson@sun.com 
6288615SAndrew.W.Wilson@sun.com 
6295184Sek110237 /*
6305184Sek110237  * Delete the designated flowop from the thread's flowop list.
6315184Sek110237  */
6325184Sek110237 static void
flowop_delete(flowop_t ** flowoplist,flowop_t * flowop)6335184Sek110237 flowop_delete(flowop_t **flowoplist, flowop_t *flowop)
6345184Sek110237 {
6355184Sek110237 	flowop_t *entry = *flowoplist;
6365184Sek110237 	int found = 0;
6375184Sek110237 
6385184Sek110237 	filebench_log(LOG_DEBUG_IMPL, "Deleting flowop (%s-%d)",
6395184Sek110237 	    flowop->fo_name,
6405184Sek110237 	    flowop->fo_instance);
6415184Sek110237 
6425184Sek110237 	/* Delete from thread's flowop list */
6435184Sek110237 	if (flowop == *flowoplist) {
6445184Sek110237 		/* First on list */
6456550Saw148015 		*flowoplist = flowop->fo_exec_next;
6465184Sek110237 		filebench_log(LOG_DEBUG_IMPL,
6475184Sek110237 		    "Delete0 flowop: (%s-%d)",
6485184Sek110237 		    flowop->fo_name,
6495184Sek110237 		    flowop->fo_instance);
6505184Sek110237 	} else {
6516550Saw148015 		while (entry->fo_exec_next) {
6525184Sek110237 			filebench_log(LOG_DEBUG_IMPL,
6535184Sek110237 			    "Delete0 flowop: (%s-%d) == (%s-%d)",
6546550Saw148015 			    entry->fo_exec_next->fo_name,
6556550Saw148015 			    entry->fo_exec_next->fo_instance,
6565184Sek110237 			    flowop->fo_name,
6575184Sek110237 			    flowop->fo_instance);
6585184Sek110237 
6596550Saw148015 			if (flowop == entry->fo_exec_next) {
6605184Sek110237 				/* Delete */
6615184Sek110237 				filebench_log(LOG_DEBUG_IMPL,
6625184Sek110237 				    "Deleted0 flowop: (%s-%d)",
6636550Saw148015 				    entry->fo_exec_next->fo_name,
6646550Saw148015 				    entry->fo_exec_next->fo_instance);
6656550Saw148015 				entry->fo_exec_next =
6666550Saw148015 				    entry->fo_exec_next->fo_exec_next;
6675184Sek110237 				break;
6685184Sek110237 			}
6696550Saw148015 			entry = entry->fo_exec_next;
6705184Sek110237 		}
6715184Sek110237 	}
6725184Sek110237 
6735184Sek110237 #ifdef HAVE_PROCFS
6745184Sek110237 	/* Close /proc stats */
6755184Sek110237 	if (flowop->fo_thread)
6765184Sek110237 		(void) close(flowop->fo_thread->tf_lwpusagefd);
6775184Sek110237 #endif
6785184Sek110237 
6795184Sek110237 	/* Delete from global list */
6806391Saw148015 	entry = filebench_shm->shm_flowoplist;
6815184Sek110237 
6826391Saw148015 	if (flowop == filebench_shm->shm_flowoplist) {
6835184Sek110237 		/* First on list */
6846391Saw148015 		filebench_shm->shm_flowoplist = flowop->fo_next;
6855184Sek110237 		found = 1;
6865184Sek110237 	} else {
6875184Sek110237 		while (entry->fo_next) {
6885184Sek110237 			filebench_log(LOG_DEBUG_IMPL,
6895184Sek110237 			    "Delete flowop: (%s-%d) == (%s-%d)",
6905184Sek110237 			    entry->fo_next->fo_name,
6915184Sek110237 			    entry->fo_next->fo_instance,
6925184Sek110237 			    flowop->fo_name,
6935184Sek110237 			    flowop->fo_instance);
6945184Sek110237 
6955184Sek110237 			if (flowop == entry->fo_next) {
6965184Sek110237 				/* Delete */
6975184Sek110237 				entry->fo_next = entry->fo_next->fo_next;
6985184Sek110237 				found = 1;
6995184Sek110237 				break;
7005184Sek110237 			}
7015184Sek110237 
7025184Sek110237 			entry = entry->fo_next;
7035184Sek110237 		}
7045184Sek110237 	}
7055184Sek110237 	if (found) {
7065184Sek110237 		filebench_log(LOG_DEBUG_IMPL,
7075184Sek110237 		    "Deleted flowop: (%s-%d)",
7085184Sek110237 		    flowop->fo_name,
7095184Sek110237 		    flowop->fo_instance);
7105184Sek110237 		ipc_free(FILEBENCH_FLOWOP, (char *)flowop);
7115184Sek110237 	} else {
7125184Sek110237 		filebench_log(LOG_DEBUG_IMPL, "Flowop %s-%d not found!",
7135184Sek110237 		    flowop->fo_name,
7145184Sek110237 		    flowop->fo_instance);
7155184Sek110237 	}
7165184Sek110237 }
7175184Sek110237 
7185184Sek110237 /*
7195184Sek110237  * Deletes all the flowops from a flowop list.
7205184Sek110237  */
7215184Sek110237 void
flowop_delete_all(flowop_t ** flowoplist)7225184Sek110237 flowop_delete_all(flowop_t **flowoplist)
7235184Sek110237 {
7245184Sek110237 	flowop_t *flowop = *flowoplist;
7258762SAndrew.W.Wilson@sun.com 	flowop_t *next_flowop;
7265184Sek110237 
7276550Saw148015 	(void) ipc_mutex_lock(&filebench_shm->shm_flowop_lock);
7286550Saw148015 
7295184Sek110237 	while (flowop) {
7306212Saw148015 		filebench_log(LOG_DEBUG_IMPL, "Deleting flowop (%s-%d)",
7315184Sek110237 		    flowop->fo_name, flowop->fo_instance);
7325184Sek110237 
7335184Sek110237 		if (flowop->fo_instance &&
7345184Sek110237 		    (flowop->fo_instance == FLOW_MASTER)) {
7356550Saw148015 			flowop = flowop->fo_exec_next;
7365184Sek110237 			continue;
7375184Sek110237 		}
7388762SAndrew.W.Wilson@sun.com 		next_flowop = flowop->fo_exec_next;
7395184Sek110237 		flowop_delete(flowoplist, flowop);
7408762SAndrew.W.Wilson@sun.com 		flowop = next_flowop;
7415184Sek110237 	}
7425184Sek110237 
7436391Saw148015 	(void) ipc_mutex_unlock(&filebench_shm->shm_flowop_lock);
7445184Sek110237 }
7455184Sek110237 
7465184Sek110237 /*
7475184Sek110237  * Allocates a flowop entity and initializes it with inherited
7485184Sek110237  * contents from the "inherit" flowop, if it is supplied, or
7496550Saw148015  * with zeros otherwise. In either case the fo_next and fo_exec_next
7505184Sek110237  * pointers are set to NULL, and fo_thread is set to point to
7515184Sek110237  * the owning threadflow. The initialized flowop is placed at
7525184Sek110237  * the head of the global flowop list, and also placed on the
7536550Saw148015  * tail of the supplied local flowop list, which will either
7546550Saw148015  * be a threadflow's tf_thrd_fops list or a composite flowop's
7556550Saw148015  * fo_comp_fops list. The routine locks the flowop's fo_lock and
7566550Saw148015  * leaves it held on return. If successful, it returns a pointer
7576550Saw148015  * to the allocated and initialized flowop, otherwise it returns NULL.
7585184Sek110237  *
7596391Saw148015  * filebench_shm->shm_flowop_lock must be held by caller.
7605184Sek110237  */
7615184Sek110237 static flowop_t *
flowop_define_common(threadflow_t * threadflow,char * name,flowop_t * inherit,flowop_t ** flowoplist_hdp,int instance,int type)7625184Sek110237 flowop_define_common(threadflow_t *threadflow, char *name, flowop_t *inherit,
7636550Saw148015     flowop_t **flowoplist_hdp, int instance, int type)
7645184Sek110237 {
7655184Sek110237 	flowop_t *flowop;
7665184Sek110237 
7675184Sek110237 	if (name == NULL)
7685184Sek110237 		return (NULL);
7695184Sek110237 
7705184Sek110237 	if ((flowop = (flowop_t *)ipc_malloc(FILEBENCH_FLOWOP)) == NULL) {
7715184Sek110237 		filebench_log(LOG_ERROR,
7725184Sek110237 		    "flowop_define: Can't malloc flowop");
7735184Sek110237 		return (NULL);
7745184Sek110237 	}
7755184Sek110237 
7765184Sek110237 	filebench_log(LOG_DEBUG_IMPL, "defining flowops %s-%d, addr %zx",
7775184Sek110237 	    name, instance, flowop);
7785184Sek110237 
7795184Sek110237 	if (flowop == NULL)
7805184Sek110237 		return (NULL);
7815184Sek110237 
7825184Sek110237 	if (inherit) {
7835184Sek110237 		(void) memcpy(flowop, inherit, sizeof (flowop_t));
7847556SAndrew.W.Wilson@sun.com 		(void) pthread_mutex_init(&flowop->fo_lock,
7857556SAndrew.W.Wilson@sun.com 		    ipc_mutexattr(IPC_MUTEX_PRI_ROB));
7865184Sek110237 		(void) ipc_mutex_lock(&flowop->fo_lock);
7875184Sek110237 		flowop->fo_next = NULL;
7886550Saw148015 		flowop->fo_exec_next = NULL;
7895184Sek110237 		filebench_log(LOG_DEBUG_IMPL,
7905184Sek110237 		    "flowop %s-%d calling init", name, instance);
7915184Sek110237 	} else {
7925184Sek110237 		(void) memset(flowop, 0, sizeof (flowop_t));
7936212Saw148015 		flowop->fo_iters = avd_int_alloc(1);
7945184Sek110237 		flowop->fo_type = type;
7957556SAndrew.W.Wilson@sun.com 		(void) pthread_mutex_init(&flowop->fo_lock,
7967556SAndrew.W.Wilson@sun.com 		    ipc_mutexattr(IPC_MUTEX_PRI_ROB));
7975184Sek110237 		(void) ipc_mutex_lock(&flowop->fo_lock);
7985184Sek110237 	}
7995184Sek110237 
8005184Sek110237 	/* Create backpointer to thread */
8015184Sek110237 	flowop->fo_thread = threadflow;
8025184Sek110237 
8035184Sek110237 	/* Add flowop to global list */
8046391Saw148015 	if (filebench_shm->shm_flowoplist == NULL) {
8056391Saw148015 		filebench_shm->shm_flowoplist = flowop;
8065184Sek110237 		flowop->fo_next = NULL;
8075184Sek110237 	} else {
8086391Saw148015 		flowop->fo_next = filebench_shm->shm_flowoplist;
8096391Saw148015 		filebench_shm->shm_flowoplist = flowop;
8105184Sek110237 	}
8115184Sek110237 
8125184Sek110237 	(void) strcpy(flowop->fo_name, name);
8135184Sek110237 	flowop->fo_instance = instance;
8145184Sek110237 
8156550Saw148015 	if (flowoplist_hdp == NULL)
8165184Sek110237 		return (flowop);
8175184Sek110237 
8185184Sek110237 	/* Add flowop to thread op list */
8196550Saw148015 	if (*flowoplist_hdp == NULL) {
8206550Saw148015 		*flowoplist_hdp = flowop;
8216550Saw148015 		flowop->fo_exec_next = NULL;
8225184Sek110237 	} else {
8235184Sek110237 		flowop_t *flowend;
8245184Sek110237 
8255184Sek110237 		/* Find the end of the thread list */
8266550Saw148015 		flowend = *flowoplist_hdp;
8276550Saw148015 		while (flowend->fo_exec_next != NULL)
8286550Saw148015 			flowend = flowend->fo_exec_next;
8296550Saw148015 		flowend->fo_exec_next = flowop;
8306550Saw148015 		flowop->fo_exec_next = NULL;
8315184Sek110237 	}
8325184Sek110237 
8335184Sek110237 	return (flowop);
8345184Sek110237 }
8355184Sek110237 
8365184Sek110237 /*
8375184Sek110237  * Calls flowop_define_common() to allocate and initialize a
8385184Sek110237  * flowop, and holds the shared flowop_lock during the call.
8395184Sek110237  * It releases the created flowop's fo_lock when done.
8405184Sek110237  */
8415184Sek110237 flowop_t *
flowop_define(threadflow_t * threadflow,char * name,flowop_t * inherit,flowop_t ** flowoplist_hdp,int instance,int type)8425184Sek110237 flowop_define(threadflow_t *threadflow, char *name, flowop_t *inherit,
8436550Saw148015     flowop_t **flowoplist_hdp, int instance, int type)
8445184Sek110237 {
8456212Saw148015 	flowop_t	*flowop;
8465184Sek110237 
8476391Saw148015 	(void) ipc_mutex_lock(&filebench_shm->shm_flowop_lock);
8485184Sek110237 	flowop = flowop_define_common(threadflow, name,
8496550Saw148015 	    inherit, flowoplist_hdp, instance, type);
8506391Saw148015 	(void) ipc_mutex_unlock(&filebench_shm->shm_flowop_lock);
8515184Sek110237 
8525184Sek110237 	if (flowop == NULL)
8535184Sek110237 		return (NULL);
8545184Sek110237 
8555184Sek110237 	(void) ipc_mutex_unlock(&flowop->fo_lock);
8567556SAndrew.W.Wilson@sun.com 
8576550Saw148015 	return (flowop);
8586550Saw148015 }
8596550Saw148015 
8606550Saw148015 /*
8616550Saw148015  * Calls flowop_define_common() to allocate and initialize a
8626550Saw148015  * composite flowop, and holds the shared flowop_lock during the call.
8636550Saw148015  * It releases the created flowop's fo_lock when done.
8646550Saw148015  */
8656550Saw148015 flowop_t *
flowop_new_composite_define(char * name)8666550Saw148015 flowop_new_composite_define(char *name)
8676550Saw148015 {
8686550Saw148015 	flowop_t *flowop;
8696550Saw148015 
8706550Saw148015 	(void) ipc_mutex_lock(&filebench_shm->shm_flowop_lock);
8716550Saw148015 	flowop = flowop_define_common(NULL, name,
8726550Saw148015 	    NULL, NULL, 0, FLOW_TYPE_COMPOSITE);
8736550Saw148015 	(void) ipc_mutex_unlock(&filebench_shm->shm_flowop_lock);
8746550Saw148015 
8756550Saw148015 	if (flowop == NULL)
8766550Saw148015 		return (NULL);
8776550Saw148015 
8786550Saw148015 	flowop->fo_func = flowop_composite;
8796550Saw148015 	flowop->fo_init = flowop_composite_init;
8806550Saw148015 	flowop->fo_destruct = flowop_composite_destruct;
8816550Saw148015 	(void) ipc_mutex_unlock(&flowop->fo_lock);
8825184Sek110237 
8835184Sek110237 	return (flowop);
8845184Sek110237 }
8855184Sek110237 
8865184Sek110237 /*
8875184Sek110237  * Attempts to take a write lock on the flowop_find_lock that is
8885184Sek110237  * defined in interprocess shared memory. Since each call to
8895184Sek110237  * flowop_start() holds a read lock on flowop_find_lock, this
8905184Sek110237  * routine effectively blocks until all instances of
8915184Sek110237  * flowop_start() have finished. The flowop_find() routine calls
8925184Sek110237  * this routine so that flowops won't be searched for until all
8935184Sek110237  * flowops have been created by flowop_start.
8945184Sek110237  */
8955184Sek110237 static void
flowop_find_barrier(void)8965184Sek110237 flowop_find_barrier(void)
8975184Sek110237 {
8985184Sek110237 	/* Block on wrlock to ensure find waits for all creates */
8996391Saw148015 	(void) pthread_rwlock_wrlock(&filebench_shm->shm_flowop_find_lock);
9006391Saw148015 	(void) pthread_rwlock_unlock(&filebench_shm->shm_flowop_find_lock);
9015184Sek110237 }
9025184Sek110237 
9035184Sek110237 /*
9045184Sek110237  * Returns a list of flowops named "name" from the master
9055184Sek110237  * flowop list.
9065184Sek110237  */
9075184Sek110237 flowop_t *
flowop_find(char * name)9085184Sek110237 flowop_find(char *name)
9095184Sek110237 {
9106212Saw148015 	flowop_t *flowop;
9115184Sek110237 	flowop_t *result = NULL;
9125184Sek110237 
9135184Sek110237 	flowop_find_barrier();
9145184Sek110237 
9156391Saw148015 	(void) ipc_mutex_lock(&filebench_shm->shm_flowop_lock);
9165184Sek110237 
9176391Saw148015 	flowop = filebench_shm->shm_flowoplist;
9186212Saw148015 
9195184Sek110237 	while (flowop) {
9205184Sek110237 		if (strcmp(name, flowop->fo_name) == 0) {
9215184Sek110237 
9225184Sek110237 			/* Add flowop to result list */
9235184Sek110237 			if (result == NULL) {
9245184Sek110237 				result = flowop;
9255184Sek110237 				flowop->fo_resultnext = NULL;
9265184Sek110237 			} else {
9275184Sek110237 				flowop->fo_resultnext = result;
9285184Sek110237 				result = flowop;
9295184Sek110237 			}
9305184Sek110237 		}
9315184Sek110237 		flowop = flowop->fo_next;
9325184Sek110237 	}
9335184Sek110237 
9346391Saw148015 	(void) ipc_mutex_unlock(&filebench_shm->shm_flowop_lock);
9355184Sek110237 
9365184Sek110237 
9375184Sek110237 	return (result);
9385184Sek110237 }
9395184Sek110237 
9405184Sek110237 /*
9415184Sek110237  * Returns a pointer to the specified instance of flowop
9426701Saw148015  * "name" from the global list.
9435184Sek110237  */
9445184Sek110237 flowop_t *
flowop_find_one(char * name,int instance)9455184Sek110237 flowop_find_one(char *name, int instance)
9465184Sek110237 {
9476212Saw148015 	flowop_t *test_flowop;
9485184Sek110237 
9496212Saw148015 	flowop_find_barrier();
9506212Saw148015 
9516391Saw148015 	(void) ipc_mutex_lock(&filebench_shm->shm_flowop_lock);
9525184Sek110237 
9536391Saw148015 	test_flowop = filebench_shm->shm_flowoplist;
9546212Saw148015 
9556212Saw148015 	while (test_flowop) {
9566212Saw148015 		if ((strcmp(name, test_flowop->fo_name) == 0) &&
9576212Saw148015 		    (instance == test_flowop->fo_instance))
9585184Sek110237 			break;
9596212Saw148015 
9606212Saw148015 		test_flowop = test_flowop->fo_next;
9615184Sek110237 	}
9625184Sek110237 
9636391Saw148015 	(void) ipc_mutex_unlock(&filebench_shm->shm_flowop_lock);
9646212Saw148015 
9656212Saw148015 	return (test_flowop);
9665184Sek110237 }
9676550Saw148015 
9686550Saw148015 /*
9696701Saw148015  * recursively searches through lists of flowops on a given thread
9706701Saw148015  * and those on any included composite flowops for the named flowop.
9716701Saw148015  * either returns with a pointer to the named flowop or NULL if it
9726701Saw148015  * cannot be found.
9736701Saw148015  */
9746701Saw148015 static flowop_t *
flowop_recurse_search(char * path,char * name,flowop_t * list)9756701Saw148015 flowop_recurse_search(char *path, char *name, flowop_t *list)
9766701Saw148015 {
9776701Saw148015 	flowop_t *test_flowop;
9786701Saw148015 	char fullname[MAXPATHLEN];
9796701Saw148015 
9806701Saw148015 	test_flowop = list;
9816701Saw148015 
9826701Saw148015 	/*
9836701Saw148015 	 * when searching a list of inner flowops, "path" is the fullname
9846701Saw148015 	 * of the containing composite flowop. Use it to form the
9856701Saw148015 	 * full name of the inner flowop to search for.
9866701Saw148015 	 */
9876701Saw148015 	if (path) {
9886701Saw148015 		if ((strlen(path) + strlen(name) + 1) > MAXPATHLEN) {
9896701Saw148015 			filebench_log(LOG_ERROR,
9906701Saw148015 			    "composite flowop path name %s.%s too long",
9916701Saw148015 			    path, name);
9926701Saw148015 			return (NULL);
9936701Saw148015 		}
9946701Saw148015 
9956701Saw148015 		/* create composite_name.name for recursive search */
9966701Saw148015 		(void) strcpy(fullname, path);
9976701Saw148015 		(void) strcat(fullname, ".");
9986701Saw148015 		(void) strcat(fullname, name);
9996701Saw148015 	} else {
10006701Saw148015 		(void) strcpy(fullname, name);
10016701Saw148015 	}
10026701Saw148015 
10036701Saw148015 	/*
10046701Saw148015 	 * loop through all flowops on the supplied tf_thrd_fops (flowop)
10056701Saw148015 	 * list or fo_comp_fops (inner flowop) list.
10066701Saw148015 	 */
10076701Saw148015 	while (test_flowop) {
10086701Saw148015 		if (strcmp(fullname, test_flowop->fo_name) == 0)
10096701Saw148015 			return (test_flowop);
10106701Saw148015 
10116701Saw148015 		if (test_flowop->fo_type == FLOW_TYPE_COMPOSITE) {
10126701Saw148015 			flowop_t *found_flowop;
10136701Saw148015 
10146701Saw148015 			found_flowop = flowop_recurse_search(
10156701Saw148015 			    test_flowop->fo_name, name,
10166701Saw148015 			    test_flowop->fo_comp_fops);
10176701Saw148015 
10186701Saw148015 			if (found_flowop)
10196701Saw148015 				return (found_flowop);
10206701Saw148015 		}
10216701Saw148015 		test_flowop = test_flowop->fo_exec_next;
10226701Saw148015 	}
10236701Saw148015 
10246701Saw148015 	/* not found here or on any child lists */
10256701Saw148015 	return (NULL);
10266701Saw148015 }
10276701Saw148015 
10286701Saw148015 /*
10296701Saw148015  * Returns a pointer to flowop named "name" from the supplied tf_thrd_fops
10306701Saw148015  * list of flowops. Returns the named flowop if found, or NULL.
10316701Saw148015  */
10326701Saw148015 flowop_t *
flowop_find_from_list(char * name,flowop_t * list)10336701Saw148015 flowop_find_from_list(char *name, flowop_t *list)
10346701Saw148015 {
10356701Saw148015 	flowop_t *found_flowop;
10366701Saw148015 
10376701Saw148015 	flowop_find_barrier();
10386701Saw148015 
10396701Saw148015 	(void) ipc_mutex_lock(&filebench_shm->shm_flowop_lock);
10406701Saw148015 
10416701Saw148015 	found_flowop = flowop_recurse_search(NULL, name, list);
10426701Saw148015 
10436701Saw148015 	(void) ipc_mutex_unlock(&filebench_shm->shm_flowop_lock);
10446701Saw148015 
10456701Saw148015 	return (found_flowop);
10466701Saw148015 }
10476701Saw148015 
10486701Saw148015 /*
10496550Saw148015  * Composite flowop method. Does one pass through its list of
10506550Saw148015  * inner flowops per iteration.
10516550Saw148015  */
10526550Saw148015 static int
flowop_composite(threadflow_t * threadflow,flowop_t * flowop)10536550Saw148015 flowop_composite(threadflow_t *threadflow, flowop_t *flowop)
10546550Saw148015 {
10556550Saw148015 	flowop_t	*inner_flowop;
10566550Saw148015 
10576550Saw148015 	/* get the first flowop in the list */
10586550Saw148015 	inner_flowop = flowop->fo_comp_fops;
10596550Saw148015 
10606550Saw148015 	/* make a pass through the list of sub flowops */
10616550Saw148015 	while (inner_flowop) {
10626550Saw148015 		int	i, count;
10636550Saw148015 
10646550Saw148015 		/* Abort if asked */
10656550Saw148015 		if (threadflow->tf_abort || filebench_shm->shm_f_abort)
10666550Saw148015 			return (FILEBENCH_DONE);
10676550Saw148015 
10686550Saw148015 		if (inner_flowop->fo_stats.fs_stime == 0)
10696550Saw148015 			inner_flowop->fo_stats.fs_stime = gethrtime();
10706550Saw148015 
10716550Saw148015 		/* Execute the flowop for fo_iters times */
10726550Saw148015 		count = (int)avd_get_int(inner_flowop->fo_iters);
10736550Saw148015 		for (i = 0; i < count; i++) {
10746550Saw148015 
10756550Saw148015 			filebench_log(LOG_DEBUG_SCRIPT, "%s: executing flowop "
10766550Saw148015 			    "%s-%d", threadflow->tf_name,
10776550Saw148015 			    inner_flowop->fo_name,
10786550Saw148015 			    inner_flowop->fo_instance);
10796550Saw148015 
10806550Saw148015 			switch ((*inner_flowop->fo_func)(threadflow,
10816550Saw148015 			    inner_flowop)) {
10826550Saw148015 
10836550Saw148015 			/* all done */
10846550Saw148015 			case FILEBENCH_DONE:
10856550Saw148015 				return (FILEBENCH_DONE);
10866550Saw148015 
10876550Saw148015 			/* quit if inner flowop limit reached */
10886550Saw148015 			case FILEBENCH_NORSC:
10896550Saw148015 				return (FILEBENCH_NORSC);
10906550Saw148015 
10916550Saw148015 			/* quit on inner flowop error */
10926550Saw148015 			case FILEBENCH_ERROR:
10936550Saw148015 				filebench_log(LOG_ERROR,
10946550Saw148015 				    "inner flowop %s failed",
10956550Saw148015 				    inner_flowop->fo_name);
10966550Saw148015 				return (FILEBENCH_ERROR);
10976550Saw148015 
10986550Saw148015 			/* otherwise keep going */
10996550Saw148015 			default:
11006550Saw148015 				break;
11016550Saw148015 			}
11026550Saw148015 
11036550Saw148015 		}
11046550Saw148015 
11056550Saw148015 		/* advance to next flowop */
11066550Saw148015 		inner_flowop = inner_flowop->fo_exec_next;
11076550Saw148015 	}
11086550Saw148015 
11096550Saw148015 	/* finished with this pass */
11106550Saw148015 	return (FILEBENCH_OK);
11116550Saw148015 }
11126550Saw148015 
11136550Saw148015 /*
11146550Saw148015  * Composite flowop initialization. Creates runtime inner flowops
11156550Saw148015  * from prototype inner flowops.
11166550Saw148015  */
11176550Saw148015 static int
flowop_composite_init(flowop_t * flowop)11186550Saw148015 flowop_composite_init(flowop_t *flowop)
11196550Saw148015 {
11206550Saw148015 	int err;
11216550Saw148015 
11226550Saw148015 	err = flowop_create_runtime_flowops(flowop->fo_thread,
11236550Saw148015 	    &flowop->fo_comp_fops);
11246550Saw148015 	if (err != FILEBENCH_OK)
11256550Saw148015 		return (err);
11266550Saw148015 
11276550Saw148015 	(void) ipc_mutex_unlock(&flowop->fo_lock);
11286550Saw148015 	return (0);
11296550Saw148015 }
11306550Saw148015 
11316550Saw148015 /*
11326550Saw148015  * clean up inner flowops
11336550Saw148015  */
11346550Saw148015 static void
flowop_composite_destruct(flowop_t * flowop)11356550Saw148015 flowop_composite_destruct(flowop_t *flowop)
11366550Saw148015 {
11376550Saw148015 	flowop_t *inner_flowop = flowop->fo_comp_fops;
11386550Saw148015 
11396550Saw148015 	while (inner_flowop) {
11406550Saw148015 		filebench_log(LOG_DEBUG_IMPL, "Deleting inner flowop (%s-%d)",
11416550Saw148015 		    inner_flowop->fo_name, inner_flowop->fo_instance);
11426550Saw148015 
11436550Saw148015 		if (inner_flowop->fo_instance &&
11446550Saw148015 		    (inner_flowop->fo_instance == FLOW_MASTER)) {
11456550Saw148015 			inner_flowop = inner_flowop->fo_exec_next;
11466550Saw148015 			continue;
11476550Saw148015 		}
11486550Saw148015 		flowop_delete(&flowop->fo_comp_fops, inner_flowop);
11496550Saw148015 		inner_flowop = inner_flowop->fo_exec_next;
11506550Saw148015 	}
11516550Saw148015 }
11528615SAndrew.W.Wilson@sun.com 
11538615SAndrew.W.Wilson@sun.com /*
11548615SAndrew.W.Wilson@sun.com  * Support routines for libraries of flowops
11558615SAndrew.W.Wilson@sun.com  */
11568615SAndrew.W.Wilson@sun.com 
11578615SAndrew.W.Wilson@sun.com int
flowop_init_generic(flowop_t * flowop)11588615SAndrew.W.Wilson@sun.com flowop_init_generic(flowop_t *flowop)
11598615SAndrew.W.Wilson@sun.com {
11608615SAndrew.W.Wilson@sun.com 	(void) ipc_mutex_unlock(&flowop->fo_lock);
11618615SAndrew.W.Wilson@sun.com 	return (FILEBENCH_OK);
11628615SAndrew.W.Wilson@sun.com }
11638615SAndrew.W.Wilson@sun.com 
11648615SAndrew.W.Wilson@sun.com void
flowop_destruct_generic(flowop_t * flowop)11658615SAndrew.W.Wilson@sun.com flowop_destruct_generic(flowop_t *flowop)
11668615SAndrew.W.Wilson@sun.com {
11678615SAndrew.W.Wilson@sun.com 	char *buf;
11688615SAndrew.W.Wilson@sun.com 
11698615SAndrew.W.Wilson@sun.com 	/* release any local resources held by the flowop */
11708615SAndrew.W.Wilson@sun.com 	(void) ipc_mutex_lock(&flowop->fo_lock);
11718615SAndrew.W.Wilson@sun.com 	buf = flowop->fo_buf;
11728615SAndrew.W.Wilson@sun.com 	flowop->fo_buf = NULL;
11738615SAndrew.W.Wilson@sun.com 	(void) ipc_mutex_unlock(&flowop->fo_lock);
11748615SAndrew.W.Wilson@sun.com 
11758615SAndrew.W.Wilson@sun.com 	if (buf)
11768615SAndrew.W.Wilson@sun.com 		free(buf);
11778615SAndrew.W.Wilson@sun.com }
11788615SAndrew.W.Wilson@sun.com 
11798615SAndrew.W.Wilson@sun.com 
11808615SAndrew.W.Wilson@sun.com /*
11818615SAndrew.W.Wilson@sun.com  * Loops through the supplied list of flowops and creates and initializes
11828615SAndrew.W.Wilson@sun.com  * a flowop for each one by calling flowop_define. As a side effect of
11838615SAndrew.W.Wilson@sun.com  * calling flowop define, the created flowops are placed on the
11848615SAndrew.W.Wilson@sun.com  * master flowop list. All created flowops are set to instance "0".
11858615SAndrew.W.Wilson@sun.com  */
11868615SAndrew.W.Wilson@sun.com void
flowop_flow_init(flowop_proto_t * list,int nops)11878615SAndrew.W.Wilson@sun.com flowop_flow_init(flowop_proto_t *list, int nops)
11888615SAndrew.W.Wilson@sun.com {
11898615SAndrew.W.Wilson@sun.com 	int i;
11908615SAndrew.W.Wilson@sun.com 
11918615SAndrew.W.Wilson@sun.com 	for (i = 0; i < nops; i++) {
11928615SAndrew.W.Wilson@sun.com 		flowop_t *flowop;
11938615SAndrew.W.Wilson@sun.com 		flowop_proto_t *fl;
11948615SAndrew.W.Wilson@sun.com 
11958615SAndrew.W.Wilson@sun.com 		fl = &(list[i]);
11968615SAndrew.W.Wilson@sun.com 
11978615SAndrew.W.Wilson@sun.com 		if ((flowop = flowop_define(NULL,
11988615SAndrew.W.Wilson@sun.com 		    fl->fl_name, NULL, NULL, 0, fl->fl_type)) == 0) {
11998615SAndrew.W.Wilson@sun.com 			filebench_log(LOG_ERROR,
12008615SAndrew.W.Wilson@sun.com 			    "failed to create flowop %s\n",
12018615SAndrew.W.Wilson@sun.com 			    fl->fl_name);
12028615SAndrew.W.Wilson@sun.com 			filebench_shutdown(1);
12038615SAndrew.W.Wilson@sun.com 		}
12048615SAndrew.W.Wilson@sun.com 
12058615SAndrew.W.Wilson@sun.com 		flowop->fo_func = fl->fl_func;
12068615SAndrew.W.Wilson@sun.com 		flowop->fo_init = fl->fl_init;
12078615SAndrew.W.Wilson@sun.com 		flowop->fo_destruct = fl->fl_destruct;
12088615SAndrew.W.Wilson@sun.com 		flowop->fo_attrs = fl->fl_attrs;
12098615SAndrew.W.Wilson@sun.com 	}
12108615SAndrew.W.Wilson@sun.com }
1211