| /* |
| * 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 <stdio.h> |
| #include <stdlib.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <dirent.h> |
| #include <fcntl.h> |
| |
| #include "ffsb_fs.h" |
| #include "util.h" |
| #include "fh.h" |
| |
| /* First zero out struct, set num_dirs, and strdups basedir */ |
| void init_ffsb_fs(ffsb_fs_t * fs, char *basedir, uint32_t num_data_dirs, |
| uint32_t numstartfiles, unsigned flags) |
| { |
| memset(fs, 0, sizeof(ffsb_fs_t)); |
| fs->basedir = ffsb_strdup(basedir); |
| fs->num_dirs = num_data_dirs; |
| fs->num_start_files = numstartfiles; |
| fs->flags = flags; |
| fs->create_blocksize = FFSB_FS_DEFAULT_CREATE_BLOCKSIZE; |
| fs->age_blocksize = FFSB_FS_DEFAULT_AGE_BLOCKSIZE; |
| fs->age_fs = 0; |
| } |
| |
| /* |
| * Does not remove files/dirs on disk, only frees up data |
| * structures |
| */ |
| void destroy_ffsb_fs(ffsb_fs_t * fs) |
| { |
| free(fs->basedir); |
| destroy_filelist(&fs->files); |
| destroy_filelist(&fs->fill); |
| destroy_filelist(&fs->meta); |
| } |
| |
| void clone_ffsb_fs(ffsb_fs_t * target, ffsb_fs_t * orig) |
| { |
| target->basedir = orig->basedir; |
| target->flags = orig->flags; |
| |
| /* !!!! hackish, write a filelist_clone() function later */ |
| memcpy(&target->files, &orig->files, sizeof(orig->files)); |
| memcpy(&target->fill, &orig->fill, sizeof(orig->fill)); |
| memcpy(&target->meta, &orig->meta, sizeof(orig->meta)); |
| |
| target->num_dirs = orig->num_dirs; |
| target->num_start_files = orig->num_start_files; |
| target->minfilesize = orig->minfilesize; |
| target->maxfilesize = orig->maxfilesize; |
| |
| target->start_fsutil = orig->start_fsutil; |
| target->desired_fsutil = orig->desired_fsutil; |
| |
| target->age_fs = orig->age_fs; |
| target->num_age_dirs = orig->num_age_dirs; |
| target->aging_tg = orig->aging_tg; |
| |
| target->create_blocksize = orig->create_blocksize; |
| target->age_blocksize = orig->age_blocksize; |
| |
| memcpy(target->op_data, orig->op_data, sizeof(void *) * FFSB_NUMOPS); |
| } |
| |
| static void add_files(ffsb_fs_t * fs, struct benchfiles *bf, int num, |
| uint64_t minsize, uint64_t maxsize, unsigned blocksize) |
| { |
| struct ffsb_file *cur; |
| int i, fd, condition = 0, has_directio = 0; |
| randdata_t rd; |
| char *buf = ffsb_malloc(blocksize); |
| uint64_t initial_free = getfsutil_size(fs->basedir); |
| |
| if (fs_get_directio(fs)) { |
| has_directio = 1; |
| fs_set_directio(fs, 0); |
| } |
| |
| assert(blocksize); |
| |
| init_random(&rd, 0); |
| |
| if (num) |
| condition = num; |
| else if (fs->init_size) { |
| if (getfsutil(fs->basedir) != initial_free || |
| fs->init_size > (getfsutil_size(fs->basedir) - |
| initial_free)) |
| condition = 1; |
| else |
| condition = 0; |
| } else if (fs->init_fsutil) { |
| if (fs->init_fsutil > getfsutil(fs->basedir)) |
| condition = 1; |
| else |
| condition = 0; |
| } |
| |
| while (condition) { |
| uint64_t size; |
| if (fs->num_weights) { |
| int num = 1 + getrandom(&rd, fs->sum_weights); |
| int curop = 0; |
| |
| while (fs->size_weights[curop].weight < num) { |
| num -= fs->size_weights[curop].weight; |
| curop++; |
| } |
| size = fs->size_weights[curop].size; |
| } else |
| size = minsize + getllrandom(&rd, maxsize - minsize); |
| |
| cur = add_file(bf, size, &rd); |
| fd = fhopencreate(cur->name, NULL, fs); |
| writefile_helper(fd, size, blocksize, buf, NULL, fs); |
| fhclose(fd, NULL, fs); |
| unlock_file_writer(cur); |
| |
| if (num) |
| condition--; |
| else if (fs->init_size) { |
| if (fs->init_size > getfsutil_size(fs->basedir) - |
| initial_free) |
| condition = 1; |
| else |
| condition = 0; |
| } else if (fs->init_fsutil) { |
| if (fs->init_fsutil > getfsutil(fs->basedir)) |
| condition = 1; |
| else |
| condition = 0; |
| } |
| |
| } |
| free(buf); |
| if (has_directio) |
| fs_set_directio(fs, 1); |
| } |
| |
| static void age_fs(ffsb_fs_t * fs, double utilization); |
| static ffsb_fs_t *construct_new_fileset(ffsb_fs_t * fs); |
| static ffsb_fs_t *check_existing_fileset(ffsb_fs_t * fs); |
| |
| void *construct_ffsb_fs(void *data) |
| { |
| ffsb_fs_t *fs = (ffsb_fs_t *) data; |
| ffsb_fs_t *ret = NULL; |
| |
| if (fs_get_reuse_fs(fs)) { |
| printf("checking existing fs: %s\n", fs->basedir); |
| ret = check_existing_fileset(fs); |
| if (ret == NULL) { |
| printf("recreating new fileset\n"); |
| ret = construct_new_fileset(fs); |
| } |
| } else { |
| printf("creating new fileset %s\n", fs->basedir); |
| ret = construct_new_fileset(fs); |
| } |
| if (ret == NULL) { |
| printf("fs setup on %s failed\n", fs->basedir); |
| exit(1); |
| } |
| return ret; |
| } |
| |
| static int verify_file(struct benchfiles *bf, char *fname, void *fs_ptr) |
| { |
| ffsb_fs_t *fs = (ffsb_fs_t *) fs_ptr; |
| uint64_t minsize = fs->minfilesize; |
| uint64_t maxsize = fs->maxfilesize; |
| uint64_t filesize = 0; |
| int fd = 0; |
| DIR *dirptr = NULL; |
| |
| /* If it is a directory and it passed the name verification we |
| * don't need to do anything here |
| */ |
| dirptr = opendir(fname); |
| if (dirptr) { |
| closedir(dirptr); |
| return 0; |
| } |
| |
| fd = open(fname, O_RDONLY); |
| /* If we can't open it for read we're done */ |
| if (fd < 0) { |
| printf("verify_file: error opening %s for readonly\n", fname); |
| perror(fname); |
| return 1; |
| } |
| close(fd); |
| filesize = ffsb_get_filesize(fname); |
| |
| if (filesize < minsize || filesize > maxsize) { |
| printf("size %llu bytes for file %s is invalid\n", |
| filesize, fname); |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| /* Record the number of files and directorys there are supposed to be |
| * grab (check and build the structures) the regular data fileset then |
| * check to make sure the number of directories and files in that |
| * filelist matches up. Then grab the meta filelist and verify that |
| * the meta filelist is empty. Set up the filelist for fill (aging) |
| * and setup the ops for the benchmark. |
| */ |
| static ffsb_fs_t *check_existing_fileset(ffsb_fs_t * fs) |
| { |
| char buf[FILENAME_MAX * 3]; |
| int retval = 0; |
| uint32_t num_dirs = fs->num_dirs; |
| uint32_t num_files = fs->num_start_files; |
| |
| if (fs->age_fs) { |
| printf("Aging and reusing the fileset are mutually " |
| "exclusive\n"); |
| printf("aborting\n"); |
| return NULL; |
| } |
| |
| /* Set up bench/age dir */ |
| if (FILENAME_MAX <= |
| snprintf(buf, FILENAME_MAX, "%s/%s", fs->basedir, FILES_BASE)) { |
| printf("pathname \"%s\" is too long, aborting\n", buf); |
| return NULL; |
| } |
| |
| /* Make a "dummy" filelist that has numsubdirs set to 0 and |
| * numstartfiles set to 0 |
| */ |
| init_filelist(&fs->files, buf, FILES_BASE, 0, 0); |
| |
| retval = grab_old_fileset(&fs->files, buf, verify_file, fs); |
| |
| if (retval) |
| return NULL; |
| |
| if ((get_listsize(&fs->files) != num_files) || |
| (get_numsubdirs(&fs->files) != num_dirs)) { |
| printf("check_existing_fileset: number of files (%u)" |
| " or directorys (%u) don't match up\n", |
| get_listsize(&fs->files), get_numsubdirs(&fs->files)); |
| destroy_filelist(&fs->files); |
| return NULL; |
| } |
| |
| if (FILENAME_MAX <= |
| snprintf(buf, FILENAME_MAX, "%s/%s", fs->basedir, META_BASE)) { |
| printf("pathname \"%s\" is too long, aborting\n", buf); |
| return NULL; |
| } |
| |
| init_filelist(&fs->meta, buf, META_BASE, 0, 1); |
| retval = grab_old_fileset(&fs->meta, buf, verify_file, fs); |
| |
| if (retval) { |
| destroy_filelist(&fs->files); |
| return NULL; |
| } |
| |
| if ((get_listsize(&fs->meta) != 0) || (get_numsubdirs(&fs->meta) != 0)) { |
| printf("check_existing_fileset: meta directory isn't empty\n" |
| "aborting\n"); |
| destroy_filelist(&fs->files); |
| destroy_filelist(&fs->meta); |
| return NULL; |
| } |
| |
| /* Even though we won't use it, we still need to be consistent |
| * here. |
| */ |
| init_filelist(&fs->fill, buf, AGE_BASE, 0, 0); |
| |
| /* Have to do this or everything else could break. */ |
| ops_setup_bench(fs); |
| |
| return fs; |
| } |
| |
| /* |
| * clean up fs, "rm -rf data meta" |
| * record utilization |
| * set up the dirs: files, meta |
| * age filesystem |
| * have ffsb_ops setup their data |
| * create starting files in files |
| */ |
| static ffsb_fs_t *construct_new_fileset(ffsb_fs_t * fs) |
| { |
| char buf[FILENAME_MAX * 3]; |
| |
| /* TODO: Convert this quick and dirty rm -rf to a "real" |
| * programmatic version, that doesn't rely on the rm command. |
| */ |
| if (FILENAME_MAX * 3 <= snprintf(buf, FILENAME_MAX * 3, |
| "rm -rf %s/data %s/meta", |
| fs->basedir, fs->basedir)) { |
| printf("pathname too long for command \"%s\"\n", buf); |
| return NULL; |
| } |
| |
| if (ffsb_system(buf) < 0) { |
| perror(buf); |
| return NULL; |
| } |
| |
| fs->start_fsutil = getfsutil(fs->basedir); |
| |
| /* Set up bench/age dir */ |
| if (FILENAME_MAX <= |
| snprintf(buf, FILENAME_MAX, "%s/%s", fs->basedir, FILES_BASE)) { |
| printf("pathname \"%s\" is too long, aborting\n", buf); |
| return NULL; |
| } |
| |
| ffsb_mkdir(buf); |
| |
| /* Regular files and aging share this directory */ |
| init_filelist(&fs->files, buf, FILES_BASE, fs->num_dirs, 1); |
| init_filelist(&fs->fill, buf, AGE_BASE, fs->num_age_dirs, 1); |
| |
| /* Set up meta dir */ |
| snprintf(buf, FILENAME_MAX, "%s/%s", fs->basedir, META_BASE); |
| |
| ffsb_mkdir(buf); |
| |
| init_filelist(&fs->meta, buf, META_BASE, 0, 1); |
| |
| /* Do aging */ |
| if (fs->age_fs) |
| age_fs(fs, fs->desired_fsutil); |
| |
| /* Call back into ops, set for benchmark */ |
| ops_setup_bench(fs); |
| |
| /* Create initial fileset */ |
| add_files(fs, &fs->files, fs->num_start_files, fs->minfilesize, |
| fs->maxfilesize, fs->create_blocksize); |
| return fs; |
| } |
| |
| struct poll_data { |
| ffsb_fs_t *fs; |
| double util; |
| }; |
| |
| static int fs_get_util(void *data) |
| { |
| struct poll_data *pd = (struct poll_data *)data; |
| double fsutil = getfsutil(pd->fs->basedir); |
| |
| if (fsutil >= pd->util) |
| return 1; |
| |
| return 0; |
| } |
| |
| static void age_fs(ffsb_fs_t * fs, double utilization) |
| { |
| ffsb_barrier_t barrier; |
| pthread_t thread; |
| struct poll_data pdata; |
| ffsb_tg_t *tg = fs_get_aging_tg(fs); |
| tg_run_params_t params; |
| ffsb_config_t fc; |
| |
| printf("aging fs %s from %.2lf to %.2lf\n", fs->basedir, |
| fs->start_fsutil, utilization); |
| ffsb_barrier_init(&barrier, tg_get_numthreads(tg)); |
| |
| init_ffsb_config_1fs(&fc, fs, tg); |
| |
| pdata.fs = fs; |
| pdata.util = utilization; |
| |
| params.tg = tg; |
| params.poll_fn = fs_get_util; |
| params.poll_data = &pdata; |
| params.wait_time = 1; |
| params.fc = &fc; |
| |
| params.tg_barrier = NULL; |
| params.thread_barrier = &barrier; |
| |
| /* Call back into ops, setup for aging */ |
| ops_setup_age(fs); |
| |
| /* Throw in some files to start off, so there's something */ |
| add_files(fs, &fs->fill, 10, 0, 0, fs->age_blocksize); |
| |
| pthread_create(&thread, NULL, tg_run, ¶ms); |
| pthread_join(thread, NULL); |
| } |
| |
| void fs_set_create_blocksize(ffsb_fs_t * fs, uint32_t blocksize) |
| { |
| fs->create_blocksize = blocksize; |
| } |
| |
| void fs_set_age_blocksize(ffsb_fs_t * fs, uint32_t blocksize) |
| { |
| fs->age_blocksize = blocksize; |
| } |
| |
| uint32_t fs_get_create_blocksize(ffsb_fs_t * fs) |
| { |
| return fs->create_blocksize; |
| } |
| |
| uint32_t fs_get_age_blocksize(ffsb_fs_t * fs) |
| { |
| return fs->age_blocksize; |
| } |
| |
| char *fs_get_basedir(ffsb_fs_t * fs) |
| { |
| return fs->basedir; |
| } |
| |
| uint32_t fs_get_numstartfiles(ffsb_fs_t * fs) |
| { |
| return fs->num_start_files; |
| } |
| |
| uint32_t fs_get_numdirs(ffsb_fs_t * fs) |
| { |
| return fs->num_dirs; |
| } |
| |
| int fs_get_libcio(ffsb_fs_t * fs) |
| { |
| return fs->flags & FFSB_FS_LIBCIO; |
| } |
| |
| void fs_set_libcio(ffsb_fs_t * fs, int lio) |
| { |
| if (lio) |
| fs->flags |= FFSB_FS_LIBCIO; |
| else |
| fs->flags &= ~0 & ~FFSB_FS_LIBCIO; |
| } |
| |
| int fs_get_directio(ffsb_fs_t * fs) |
| { |
| return fs->flags & FFSB_FS_DIRECTIO; |
| } |
| |
| void fs_set_directio(ffsb_fs_t * fs, int dio) |
| { |
| if (dio) |
| fs->flags |= FFSB_FS_DIRECTIO; |
| else |
| fs->flags &= ~0 & ~FFSB_FS_DIRECTIO; |
| } |
| |
| int fs_get_alignio(ffsb_fs_t * fs) |
| { |
| return fs->flags & FFSB_FS_ALIGNIO4K; |
| } |
| |
| void fs_set_alignio(ffsb_fs_t * fs, int aio) |
| { |
| if (aio) |
| fs->flags |= FFSB_FS_ALIGNIO4K; |
| else |
| fs->flags &= ~0 & ~FFSB_FS_ALIGNIO4K; |
| } |
| |
| int fs_get_reuse_fs(ffsb_fs_t * fs) |
| { |
| return fs->flags & FFSB_FS_REUSE_FS; |
| } |
| |
| void fs_set_reuse_fs(ffsb_fs_t * fs, int rfs) |
| { |
| if (rfs) |
| fs->flags |= FFSB_FS_REUSE_FS; |
| else |
| fs->flags &= ~0 & ~FFSB_FS_REUSE_FS; |
| } |
| |
| struct benchfiles *fs_get_datafiles(ffsb_fs_t * fs) |
| { |
| return &fs->files; |
| } |
| |
| struct benchfiles *fs_get_metafiles(ffsb_fs_t * fs) |
| { |
| return &fs->meta; |
| } |
| |
| struct benchfiles *fs_get_agefiles(ffsb_fs_t * fs) |
| { |
| return &fs->fill; |
| } |
| |
| void fs_set_aging_tg(ffsb_fs_t * fs, struct ffsb_tg *tg, double util) |
| { |
| fs->aging_tg = tg; |
| fs->age_fs = 1; |
| fs->desired_fsutil = util; |
| } |
| |
| struct ffsb_tg *fs_get_aging_tg(ffsb_fs_t * fs) |
| { |
| return fs->aging_tg; |
| } |
| |
| int fs_get_agefs(ffsb_fs_t * fs) |
| { |
| return fs->age_fs; |
| } |
| |
| /* TODO: Implement this!!!*/ |
| void fs_set_num_age_dirs(ffsb_fs_t * fs, uint32_t numdirs) |
| { |
| fs->num_age_dirs = numdirs; |
| } |
| |
| void fs_set_opdata(ffsb_fs_t * fs, void *data, unsigned opnum) |
| { |
| fs->op_data[opnum] = data; |
| } |
| |
| void *fs_get_opdata(ffsb_fs_t * fs, unsigned opnum) |
| { |
| return fs->op_data[opnum]; |
| } |
| |
| void fs_set_min_filesize(ffsb_fs_t * fs, uint64_t size) |
| { |
| fs->minfilesize = size; |
| } |
| |
| void fs_set_max_filesize(ffsb_fs_t * fs, uint64_t size) |
| { |
| fs->maxfilesize = size; |
| } |
| |
| uint64_t fs_get_min_filesize(ffsb_fs_t * fs) |
| { |
| return fs->minfilesize; |
| } |
| |
| uint64_t fs_get_max_filesize(ffsb_fs_t * fs) |
| { |
| return fs->maxfilesize; |
| } |
| |
| double fs_get_desired_fsutil(ffsb_fs_t * fs) |
| { |
| return fs->desired_fsutil; |
| } |
| |
| void fs_print_config(ffsb_fs_t * fs) |
| { |
| char buf[256]; |
| |
| printf("FileSystem %s\n", fs->basedir); |
| printf("==========\n"); |
| printf("\t num_dirs = %u\n", fs->num_dirs); |
| printf("\t starting files = %u\n", fs->num_start_files); |
| printf("\t\n"); |
| if (fs->num_weights) { |
| int i; |
| printf("\t Fileset weight:\n"); |
| for (i = 0; i < fs->num_weights; i++) |
| printf("\t\t %12llu (%6s) -> %u (%.2f\%)\n", |
| fs->size_weights[i].size, |
| ffsb_printsize(buf, fs->size_weights[i].size, |
| 256), fs->size_weights[i].weight, |
| ((float)fs->size_weights[i].weight / |
| (float)fs->sum_weights) * 100); |
| } else { |
| printf("\t min file size = %llu\t(%s)\n", fs->minfilesize, |
| ffsb_printsize(buf, fs->minfilesize, 256)); |
| printf("\t max file size = %llu\t(%s)\n", fs->maxfilesize, |
| ffsb_printsize(buf, fs->maxfilesize, 256)); |
| } |
| printf("\t directio = %s\n", (fs->flags & FFSB_FS_DIRECTIO) ? |
| "on" : "off"); |
| printf("\t alignedio = %s\n", (fs->flags & FFSB_FS_ALIGNIO4K) ? |
| "on" : "off"); |
| printf("\t bufferedio = %s\n", (fs->flags & FFSB_FS_LIBCIO) ? |
| "on" : "off"); |
| printf("\t\n"); |
| printf("\t aging is %s\n", (fs->age_fs) ? "on" : "off"); |
| printf("\t current utilization = %.2f\%\n", |
| getfsutil(fs->basedir) * 100); |
| if (fs->age_fs) { |
| printf("\t desired utilization = %.2lf%\n", |
| fs->desired_fsutil * 100); |
| printf("\t \n"); |
| tg_print_config_aging(fs->aging_tg, fs->basedir); |
| } |
| printf("\t\n"); |
| } |
| |
| int fs_needs_stats(ffsb_fs_t * fs, syscall_t sys) |
| { |
| return (fs != NULL) ? (int)fs->fsd.config : 0; |
| } |
| |
| void fs_add_stat(ffsb_fs_t * fs, syscall_t sys, uint32_t val) |
| { |
| if (fs) |
| ffsb_add_data(&fs->fsd, sys, val); |
| } |