| /* |
| * Copyright (c) International Business Machines Corp., 2001-2004 |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See |
| * the GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| */ |
| #include <string.h> |
| #include <stdio.h> |
| #include <assert.h> |
| #include <pthread.h> |
| |
| #include "ffsb_tg.h" |
| #include "util.h" |
| |
| void init_ffsb_tg(ffsb_tg_t * tg, unsigned num_threads, unsigned tg_num) |
| { |
| int i; |
| memset(tg, 0, sizeof(ffsb_tg_t)); |
| |
| tg->threads = ffsb_malloc(sizeof(ffsb_thread_t) * num_threads); |
| tg->tg_num = tg_num; |
| tg->num_threads = num_threads; |
| |
| tg->bindfs = -1; /* default is not bound */ |
| |
| tg->thread_bufsize = 0; |
| for (i = 0; i < num_threads; i++) |
| init_ffsb_thread(tg->threads + i, tg, 0, tg_num, i); |
| } |
| |
| void destroy_ffsb_tg(ffsb_tg_t * tg) |
| { |
| int i; |
| for (i = 0; i < tg->num_threads; i++) |
| destroy_ffsb_thread(tg->threads + i); |
| free(tg->threads); |
| if (tg_needs_stats(tg)) |
| ffsb_statsc_destroy(&tg->fsc); |
| } |
| |
| void *tg_run(void *data) |
| { |
| tg_run_params_t *params = (tg_run_params_t *) data; |
| ffsb_tg_t *tg = params->tg; |
| int i; |
| pthread_attr_t attr; |
| |
| pthread_attr_init(&attr); |
| pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); |
| |
| tg->start_barrier = params->thread_barrier; |
| |
| /* Sum up the weights for use later by tg_get_op() */ |
| tg->sum_weights = 0; |
| for (i = 0; i < FFSB_NUMOPS; i++) |
| tg->sum_weights += tg->op_weights[i]; |
| |
| tg->fc = params->fc; |
| tg->flagval = -1; |
| tg->stopval = 1; |
| |
| /* spawn threads */ |
| for (i = 0; i < tg->num_threads; i++) { |
| ffsb_thread_t *ft = &tg->threads[i]; |
| pthread_create(&ft->ptid, &attr, ft_run, ft); |
| } |
| |
| if (params->tg_barrier) |
| ffsb_barrier_wait(params->tg_barrier); |
| |
| /* wait for termination condition to be true */ |
| do { |
| ffsb_sleep(params->wait_time); |
| } while (params->poll_fn(params->poll_data) == 0); |
| |
| /* set flag value */ |
| tg->flagval = tg->stopval; |
| |
| /* wait on theads to finish */ |
| for (i = 0; i < tg->num_threads; i++) |
| pthread_join(tg->threads[i].ptid, NULL); |
| |
| return NULL; |
| } |
| |
| /* Needs to set params->opnum and params->fs */ |
| void tg_get_op(ffsb_tg_t * tg, randdata_t * rd, tg_op_params_t * params) |
| { |
| unsigned curop; |
| int num; |
| int fsnum; |
| |
| num = 1 + getrandom(rd, tg->sum_weights); |
| curop = 0; |
| |
| while (tg->op_weights[curop] < num) { |
| num -= tg->op_weights[curop]; |
| curop++; |
| } |
| |
| params->opnum = curop; |
| |
| /* If we're bound to a particular filesystem, use that, |
| * otherwise, pick one at random. |
| */ |
| fsnum = tg->bindfs; |
| if (fsnum < 0) |
| fsnum = getrandom(rd, tg->fc->num_filesys); |
| |
| params->fs = fc_get_fs(tg->fc, fsnum); |
| } |
| |
| void tg_set_op_weight(ffsb_tg_t * tg, char *opname, unsigned weight) |
| { |
| int opnum = ops_find_op(opname); |
| assert(opnum >= 0); |
| tg->op_weights[opnum] = weight; |
| } |
| |
| unsigned tg_get_op_weight(ffsb_tg_t * tg, char *opname) |
| { |
| int opnum = ops_find_op(opname); |
| assert(opnum >= 0); |
| return tg->op_weights[opnum]; |
| } |
| |
| void tg_set_bindfs(ffsb_tg_t * tg, int fsnum) |
| { |
| tg->bindfs = fsnum; |
| } |
| |
| int tg_get_bindfs(ffsb_tg_t * tg) |
| { |
| return tg->bindfs; |
| } |
| |
| unsigned tg_get_numthreads(ffsb_tg_t * tg) |
| { |
| return tg->num_threads; |
| } |
| |
| static void update_bufsize(ffsb_tg_t * tg) |
| { |
| int i; |
| uint32_t newmax = max(tg->read_blocksize, tg->write_blocksize); |
| |
| if (newmax == max(newmax, tg->thread_bufsize)) |
| for (i = 0; i < tg->num_threads; i++) |
| ft_alter_bufsize(tg->threads + i, newmax); |
| } |
| |
| void tg_set_read_random(ffsb_tg_t * tg, int rr) |
| { |
| tg->read_random = rr; |
| } |
| |
| void tg_set_write_random(ffsb_tg_t * tg, int wr) |
| { |
| tg->write_random = wr; |
| } |
| |
| void tg_set_fsync_file(ffsb_tg_t * tg, int fsync) |
| { |
| tg->fsync_file = fsync; |
| } |
| |
| void tg_set_read_size(ffsb_tg_t * tg, uint64_t rs) |
| { |
| tg->read_size = rs; |
| } |
| |
| void tg_set_read_blocksize(ffsb_tg_t * tg, uint32_t rs) |
| { |
| tg->read_blocksize = rs; |
| update_bufsize(tg); |
| } |
| |
| void tg_set_read_skip(ffsb_tg_t * tg, int rs) |
| { |
| tg->read_skip = rs; |
| } |
| |
| void tg_set_read_skipsize(ffsb_tg_t * tg, uint32_t rs) |
| { |
| tg->read_skipsize = rs; |
| } |
| |
| void tg_set_write_size(ffsb_tg_t * tg, uint64_t ws) |
| { |
| tg->write_size = ws; |
| } |
| |
| void tg_set_write_blocksize(ffsb_tg_t * tg, uint32_t ws) |
| { |
| tg->write_blocksize = ws; |
| update_bufsize(tg); |
| } |
| |
| int tg_get_read_random(ffsb_tg_t * tg) |
| { |
| return tg->read_random; |
| } |
| |
| int tg_get_write_random(ffsb_tg_t * tg) |
| { |
| return tg->write_random; |
| } |
| |
| int tg_get_fsync_file(ffsb_tg_t * tg) |
| { |
| return tg->fsync_file; |
| } |
| |
| uint64_t tg_get_read_size(ffsb_tg_t * tg) |
| { |
| return tg->read_size; |
| } |
| |
| uint32_t tg_get_read_blocksize(ffsb_tg_t * tg) |
| { |
| return tg->read_blocksize; |
| } |
| |
| int tg_get_read_skip(ffsb_tg_t * tg) |
| { |
| return tg->read_skip; |
| } |
| |
| uint32_t tg_get_read_skipsize(ffsb_tg_t * tg) |
| { |
| return tg->read_skipsize; |
| } |
| |
| uint64_t tg_get_write_size(ffsb_tg_t * tg) |
| { |
| return tg->write_size; |
| } |
| |
| uint32_t tg_get_write_blocksize(ffsb_tg_t * tg) |
| { |
| return tg->write_blocksize; |
| } |
| |
| int tg_get_stopval(ffsb_tg_t * tg) |
| { |
| return tg->stopval; |
| } |
| |
| ffsb_barrier_t *tg_get_start_barrier(ffsb_tg_t * tg) |
| { |
| return tg->start_barrier; |
| } |
| |
| static void tg_print_config_helper(ffsb_tg_t * tg) |
| { |
| int i; |
| int sumweights = 0; |
| char buf[256]; |
| |
| printf("\t num_threads = %d\n", tg->num_threads); |
| printf("\t\n"); |
| printf("\t read_random = %s\n", (tg->read_random) ? "on" : "off"); |
| printf("\t read_size = %llu\t(%s)\n", tg->read_size, |
| ffsb_printsize(buf, tg->read_size, 256)); |
| printf("\t read_blocksize = %u\t(%s)\n", tg->read_blocksize, |
| ffsb_printsize(buf, tg->read_blocksize, 256)); |
| printf("\t read_skip = %s\n", (tg->read_skip) ? "on" : "off"); |
| printf("\t read_skipsize = %u\t(%s)\n", tg->read_skipsize, |
| ffsb_printsize(buf, tg->read_skipsize, 256)); |
| printf("\t\n"); |
| printf("\t write_random = %s\n", (tg->write_random) ? "on" : "off"); |
| printf("\t write_size = %llu\t(%s)\n", tg->write_size, |
| ffsb_printsize(buf, tg->write_size, 256)); |
| printf("\t fsync_file = %d\n", tg->fsync_file); |
| printf("\t write_blocksize = %u\t(%s)\n", tg->write_blocksize, |
| ffsb_printsize(buf, tg->write_blocksize, 256)); |
| printf("\t wait time = %u\n", tg->wait_time); |
| if (tg->bindfs >= 0) { |
| printf("\t\n"); |
| printf("\t bound to fs %d\n", tg->bindfs); |
| } |
| printf("\t\n"); |
| printf("\t op weights\n"); |
| |
| for (i = 0; i < FFSB_NUMOPS; i++) |
| sumweights += tg->op_weights[i]; |
| |
| for (i = 0; i < FFSB_NUMOPS; i++) |
| printf("\t %20s = %d (%.2f%%)\n", op_get_name(i), |
| tg->op_weights[i], 100 * (float)tg->op_weights[i] / |
| (float)sumweights); |
| printf("\t\n"); |
| } |
| |
| void tg_print_config(ffsb_tg_t * tg) |
| { |
| printf("ThreadGroup %d\n", tg->tg_num); |
| printf("================\n"); |
| tg_print_config_helper(tg); |
| } |
| |
| void tg_print_config_aging(ffsb_tg_t * tg, char *fsname) |
| { |
| printf("\t Aging ThreadGroup for fs %s\n", fsname); |
| printf("\t ================\n"); |
| tg_print_config_helper(tg); |
| } |
| |
| void tg_collect_results(ffsb_tg_t * tg, ffsb_op_results_t * r) |
| { |
| int i; |
| for (i = 0; i < tg_get_numthreads(tg); i++) |
| add_results(r, ft_get_results(tg->threads + i)); |
| } |
| |
| void tg_set_waittime(ffsb_tg_t * tg, unsigned time) |
| { |
| tg->wait_time = time; |
| } |
| |
| unsigned tg_get_waittime(ffsb_tg_t * tg) |
| { |
| return tg->wait_time; |
| } |
| |
| int tg_get_flagval(ffsb_tg_t * tg) |
| { |
| return tg->flagval; |
| } |
| |
| void tg_set_statsc(ffsb_tg_t * tg, ffsb_statsc_t * fsc) |
| { |
| if (fsc) { |
| int i; |
| |
| tg->need_stats = 1; |
| ffsb_statsc_copy(&tg->fsc, fsc); |
| |
| for (i = 0; i < tg->num_threads; i++) |
| ft_set_statsc(tg->threads + i, &tg->fsc); |
| } |
| } |
| |
| void tg_collect_stats(ffsb_tg_t * tg, ffsb_statsd_t * fsd) |
| { |
| int i; |
| |
| assert(tg->need_stats); |
| ffsb_statsd_init(fsd, &tg->fsc); |
| |
| for (i = 0; i < tg_get_numthreads(tg); i++) |
| ffsb_statsd_add(fsd, ft_get_stats_data(tg->threads + i)); |
| } |
| |
| int tg_needs_stats(ffsb_tg_t * tg) |
| { |
| return tg->need_stats; |
| } |