Example #1
0
/*
 * The final initialization and main execution loop for the
 * worker threads. Sets threadflow and flowop start times,
 * waits for all process to start, then creates the runtime
 * flowops from those defined by the F language workload
 * script. It does some more initialization, then enters a
 * loop to repeatedly execute the flowops on the flowop list
 * until an abort condition is detected, at which time it exits.
 * This is the starting routine for the new worker thread
 * created by threadflow_createthread(), and is not currently
 * called from anywhere else.
 */
void
flowop_start(threadflow_t *threadflow)
{
	flowop_t *flowop;
	size_t memsize;
	int ret = FILEBENCH_OK;

	set_thread_ioprio(threadflow);

#ifdef HAVE_PROC_PID_LWP
	char procname[128];
	long ctl[2] = {PCSET, PR_MSACCT};
	int pfd;

	(void) snprintf(procname, sizeof (procname),
	    "/proc/%d/lwp/%d/lwpctl", (int)my_pid, _lwp_self());
	pfd = open(procname, O_WRONLY);
	(void) pwrite(pfd, &ctl, sizeof (ctl), 0);
	(void) close(pfd);
#endif

	(void) ipc_mutex_lock(&controlstats_lock);
	if (!controlstats_zeroed) {
		(void) memset(&controlstats, 0, sizeof (controlstats));
		controlstats_zeroed = 1;
	}
	(void) ipc_mutex_unlock(&controlstats_lock);

	flowop = threadflow->tf_thrd_fops;

	/* Hold the flowop find lock as reader to prevent lookups */
	(void) pthread_rwlock_rdlock(&filebench_shm->shm_flowop_find_lock);

	/* Create the runtime flowops from those defined by the script */
	(void) ipc_mutex_lock(&filebench_shm->shm_flowop_lock);
	if (flowop_create_runtime_flowops(threadflow, &threadflow->tf_thrd_fops)
	    != FILEBENCH_OK) {
		(void) ipc_mutex_unlock(&filebench_shm->shm_flowop_lock);
		filebench_shutdown(1);
		return;
	}
	(void) ipc_mutex_unlock(&filebench_shm->shm_flowop_lock);

	/* Release the find lock as reader to allow lookups */
	(void) pthread_rwlock_unlock(&filebench_shm->shm_flowop_find_lock);

	/* Set to the start of the new flowop list */
	flowop = threadflow->tf_thrd_fops;

	memsize = (size_t)threadflow->tf_constmemsize;

	/* If we are going to use ISM, allocate later */
	if (threadflow->tf_attrs & THREADFLOW_USEISM) {
		threadflow->tf_mem =
		    ipc_ismmalloc(memsize);
	} else {
		threadflow->tf_mem =
		    malloc(memsize);
	}

	(void) memset(threadflow->tf_mem, 0, memsize);
	filebench_log(LOG_DEBUG_SCRIPT, "Thread allocated %d bytes", memsize);

#ifdef HAVE_LWPS
	filebench_log(LOG_DEBUG_SCRIPT, "Thread %zx (%d) started",
	    threadflow,
	    _lwp_self());
#endif

	/* 
	 * Now we set tf_running flag to indicate to the main process
	 * that the worker thread is running. However, the thread is
	 * still not executing the workload, as it is blocked by the
	 * shm_run_lock. Main thread will release this lock when all
	 * threads set their tf_running flag to 1.
	 */ 
	threadflow->tf_abort = 0;
	threadflow->tf_running = 1;

	/*
	 * Block until all processes have started, acting like
	 * a barrier. The original filebench process initially
	 * holds the run_lock as a reader, preventing any of the
	 * threads from obtaining the writer lock, and hence
	 * passing this point. Once all processes and threads
	 * have been created, the original process unlocks
	 * run_lock, allowing each waiting thread to lock
	 * and then immediately unlock it, then begin running.
	 */
	(void) pthread_rwlock_wrlock(&filebench_shm->shm_run_lock);
	(void) pthread_rwlock_unlock(&filebench_shm->shm_run_lock);

	/* Main filebench worker loop */
	while (ret == FILEBENCH_OK) {
		int i, count;

		/* Abort if asked */
		if (threadflow->tf_abort || filebench_shm->shm_f_abort)
			break;

		/* Be quiet while stats are gathered */
		if (filebench_shm->shm_bequiet) {
			(void) sleep(1);
			continue;
		}

		/* Take it easy until everyone is ready to go */
		if (!filebench_shm->shm_procs_running) {
			(void) sleep(1);
			continue;
		}

		if (flowop == NULL) {
			filebench_log(LOG_ERROR, "flowop_read null flowop");
			return;
		}

		/* Execute the flowop for fo_iters times */
		count = (int)avd_get_int(flowop->fo_iters);
		for (i = 0; i < count; i++) {

			filebench_log(LOG_DEBUG_SCRIPT, "%s: executing flowop "
			    "%s-%d", threadflow->tf_name, flowop->fo_name,
			    flowop->fo_instance);

			ret = (*flowop->fo_func)(threadflow, flowop);

			/*
			 * Return value FILEBENCH_ERROR means "flowop
			 * failed, stop the filebench run"
			 */
			if (ret == FILEBENCH_ERROR) {
				filebench_log(LOG_ERROR,
				    "%s-%d: flowop %s-%d failed",
				    threadflow->tf_name,
				    threadflow->tf_instance,
				    flowop->fo_name,
				    flowop->fo_instance);
				(void) ipc_mutex_lock(&threadflow->tf_lock);
				threadflow->tf_abort = 1;
				filebench_shm->shm_f_abort =
				    FILEBENCH_ABORT_ERROR;
				(void) ipc_mutex_unlock(&threadflow->tf_lock);
				break;
			}

			/*
			 * Return value of FILEBENCH_NORSC means "stop
			 * the filebench run" if in "end on no work mode",
			 * otherwise it indicates an error
			 */
			if (ret == FILEBENCH_NORSC) {
				(void) ipc_mutex_lock(&threadflow->tf_lock);
				threadflow->tf_abort = FILEBENCH_DONE;
				if (filebench_shm->shm_rmode ==
				    FILEBENCH_MODE_Q1STDONE) {
					filebench_shm->shm_f_abort =
					    FILEBENCH_ABORT_RSRC;
				} else if (filebench_shm->shm_rmode !=
				    FILEBENCH_MODE_QALLDONE) {
					filebench_log(LOG_ERROR1,
					    "WARNING! Run stopped early:\n   "
					    "             flowop %s-%d could "
					    "not obtain a file. Please\n      "
					    "          reduce runtime, "
					    "increase fileset entries "
					    "($nfiles), or switch modes.",
					    flowop->fo_name,
					    flowop->fo_instance);
					filebench_shm->shm_f_abort =
					    FILEBENCH_ABORT_ERROR;
				}
				(void) ipc_mutex_unlock(&threadflow->tf_lock);
				break;
			}

			/*
			 * Return value of FILEBENCH_DONE means "stop
			 * the filebench run without error"
			 */
			if (ret == FILEBENCH_DONE) {
				(void) ipc_mutex_lock(&threadflow->tf_lock);
				threadflow->tf_abort = FILEBENCH_DONE;
				filebench_shm->shm_f_abort =
				    FILEBENCH_ABORT_DONE;
				(void) ipc_mutex_unlock(&threadflow->tf_lock);
				break;
			}

			/*
			 * If we get here and the return is something other
			 * than FILEBENCH_OK, it means a spurious code
			 * was returned, so treat as major error. This
			 * probably indicates a bug in the flowop.
			 */
			if (ret != FILEBENCH_OK) {
				filebench_log(LOG_ERROR,
				    "Flowop %s unexpected return value = %d\n",
				    flowop->fo_name, ret);
				filebench_shm->shm_f_abort =
				    FILEBENCH_ABORT_ERROR;
				break;
			}
		}

		/* advance to next flowop */
		flowop = flowop->fo_exec_next;

		/* but if at end of list, start over from the beginning */
		if (flowop == NULL) {
			flowop = threadflow->tf_thrd_fops;
			threadflow->tf_stats.fs_count++;
		}
	}

#ifdef HAVE_LWPS
	filebench_log(LOG_DEBUG_SCRIPT, "Thread %d exiting",
	    _lwp_self());
#endif

	/* Tell flowops to destroy locally acquired state */
	flowop_destruct_all_flows(threadflow);

	pthread_exit(&threadflow->tf_abort);
}
Example #2
0
/*
 * The final initialization and main execution loop for the
 * worker threads. Sets threadflow and flowop start times,
 * waits for all process to start, then creates the runtime
 * flowops from those defined by the F language workload
 * script. It does some more initialization, then enters a
 * loop to repeatedly execute the flowops on the flowop list
 * until an abort condition is detected, at which time it exits.
 * This is the starting routine for the new worker thread
 * created by threadflow_createthread(), and is not currently
 * called from anywhere else.
 */
void
flowop_start(threadflow_t *threadflow)
{
	flowop_t *flowop;
	size_t memsize;
	int ret = 0;

	pid = getpid();

#ifdef HAVE_PROCFS
	if (noproc == 0) {
		char procname[128];
		long ctl[2] = {PCSET, PR_MSACCT};
		int pfd;

		(void) snprintf(procname, sizeof (procname),
		    "/proc/%d/lwp/%d/lwpctl", pid, _lwp_self());
		pfd = open(procname, O_WRONLY);
		(void) pwrite(pfd, &ctl, sizeof (ctl), 0);
		(void) close(pfd);
	}
#endif

	if (!controlstats_zeroed) {
		(void) memset(&controlstats, 0, sizeof (controlstats));
		controlstats_zeroed = 1;
	}

	flowop = threadflow->tf_ops;
	threadflow->tf_stats.fs_stime = gethrtime();
	flowop->fo_stats.fs_stime = gethrtime();

	/* Hold the flowop find lock as reader to prevent lookups */
	(void) pthread_rwlock_rdlock(&filebench_shm->flowop_find_lock);

	/*
	 * Block until all processes have started, acting like
	 * a barrier. The original filebench process initially
	 * holds the run_lock as a reader, preventing any of the
	 * threads from obtaining the writer lock, and hence
	 * passing this point. Once all processes and threads
	 * have been created, the original process unlocks
	 * run_lock, allowing each waiting thread to lock
	 * and then immediately unlock it, then begin running.
	 */
	(void) pthread_rwlock_wrlock(&filebench_shm->run_lock);
	(void) pthread_rwlock_unlock(&filebench_shm->run_lock);

	/* Create the runtime flowops from those defined by the script */
	(void) ipc_mutex_lock(&filebench_shm->flowop_lock);
	while (flowop) {
		flowop_t *newflowop;

		if (flowop == threadflow->tf_ops)
			threadflow->tf_ops = NULL;
		newflowop = flowop_define_common(threadflow, flowop->fo_name,
		    flowop, 1, 0);
		if (newflowop == NULL)
			return;
		if (flowop_initflow(newflowop) < 0) {
			filebench_log(LOG_ERROR, "Flowop init of %s failed",
			    newflowop->fo_name);
		}
		flowop = flowop->fo_threadnext;
	}
	(void) ipc_mutex_unlock(&filebench_shm->flowop_lock);

	filebench_log(LOG_DEBUG_SCRIPT, "Thread started");

	/* Release the find lock as reader to allow lookups */
	(void) pthread_rwlock_unlock(&filebench_shm->flowop_find_lock);

	/* Set to the start of the new flowop list */
	flowop = threadflow->tf_ops;

	threadflow->tf_abort = 0;
	threadflow->tf_running = 1;


	/* If we are going to use ISM, allocate later */
	if (threadflow->tf_attrs & THREADFLOW_USEISM) {
		threadflow->tf_mem =
		    ipc_ismmalloc((size_t)*threadflow->tf_memsize);
	} else {
		threadflow->tf_mem = malloc((size_t)*threadflow->tf_memsize);
	}

	if (threadflow->tf_mem == NULL) {
		filebench_log(LOG_ERROR,
			"Thread failed to allocate %zd bytes", 
			threadflow->tf_memsize);
		return;
	}

	memsize = *threadflow->tf_memsize;
	filebench_log(LOG_DEBUG_SCRIPT, "Thread allocated %lld bytes at %llx",
		memsize, threadflow->tf_mem);

	(void) memset(threadflow->tf_mem, 0, memsize);
	filebench_log(LOG_DEBUG_SCRIPT, "Thread zeroed %lld bytes", memsize);

#ifdef HAVE_LWPS
	filebench_log(LOG_DEBUG_SCRIPT, "Thread %zx (%d) started",
	    threadflow,
	    _lwp_self());
#else
	filebench_log(LOG_DEBUG_SCRIPT, "Thread %zx started", threadflow);
#endif

	/* Main filebench worker loop */
	/* CONSTCOND */
	while (1) {
		int i;

		/* Abort if asked */
		if (threadflow->tf_abort || filebench_shm->f_abort) {
			(void) ipc_mutex_lock(&threadflow->tf_lock);
			threadflow->tf_running = 0;
			(void) ipc_mutex_unlock(&threadflow->tf_lock);
			filebench_log(LOG_DEBUG_SCRIPT, 
				"%s: aborting thread %s on request of a flowop",
			    	threadflow->tf_name);
			break;
		}

		/* Be quiet while stats are gathered */
		if (filebench_shm->bequiet) {
			(void) sleep(1);
			continue;
		}

		/* Take it easy until everyone is ready to go */
		if (!filebench_shm->allrunning)
			(void) sleep(1);

		if (flowop->fo_stats.fs_stime == 0)
			flowop->fo_stats.fs_stime = gethrtime();

		if (flowop == NULL) {
			filebench_log(LOG_ERROR, "flowop_read null flowop");
			return;
		}

		if (threadflow->tf_memsize == 0) {
			filebench_log(LOG_ERROR,
			    "Zero memory size for thread %s",
			    threadflow->tf_name);
			return;
		}

		/* Execute the flowop for fo_iters times */
		for (i = 0; i < *flowop->fo_iters; i++) {
			filebench_log(LOG_DEBUG_SCRIPT, "%s: executing flowop "
			    "%s-%d", threadflow->tf_name, flowop->fo_name,
			    flowop->fo_instance);
			ret = (*flowop->fo_func)(threadflow, flowop);
			filebench_log(LOG_DEBUG_SCRIPT, "%s: finished flowop "
			    "%s-%d", threadflow->tf_name, flowop->fo_name,
			    flowop->fo_instance);

			/* Return value > 0 means "stop the filebench run" */
			if (ret > 0) {
				filebench_log(LOG_VERBOSE,
				    "%s: exiting flowop %s-%d",
				    threadflow->tf_name, flowop->fo_name,
				    flowop->fo_instance);
				(void) ipc_mutex_lock(&threadflow->tf_lock);
				threadflow->tf_abort = 1;
				filebench_shm->f_abort = 1;
				threadflow->tf_running = 0;
				(void) ipc_mutex_unlock(&threadflow->tf_lock);
				break;
			}
			/*
			 * Return value < 0 means "flowop failed, stop the
			 * filebench run"
			 */
			if (ret < 0) {
				filebench_log(LOG_ERROR, "flowop %s failed",
				    flowop->fo_name);
				(void) ipc_mutex_lock(&threadflow->tf_lock);
				threadflow->tf_abort = 1;
				filebench_shm->f_abort = 1;
				threadflow->tf_running = 0;
				(void) ipc_mutex_unlock(&threadflow->tf_lock);
				break;
			}
		}

		/* advance to next flowop */
		flowop = flowop->fo_threadnext;

		/* but if at end of list, start over from the beginning */
		if (flowop == NULL) {
			flowop = threadflow->tf_ops;
			threadflow->tf_stats.fs_count++;
		}
	}

#ifdef HAVE_LWPS
	filebench_log(LOG_DEBUG_SCRIPT, "Thread %d exiting",
	    _lwp_self());
#else
	filebench_log(LOG_DEBUG_SCRIPT, "Thread exiting");
#endif

	pthread_exit(&ret);
}