/* * 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 #include #include #include #include #include #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); }