1 /* 2 * Copyright (c) International Business Machines Corp., 2001-2004 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation; either version 2 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See 12 * the GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, write to the Free Software 16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 */ 18 #include <stdio.h> 19 #include <stdlib.h> 20 #include <sys/types.h> 21 #include <sys/stat.h> 22 #include <dirent.h> 23 #include <fcntl.h> 24 25 #include "ffsb_fs.h" 26 #include "util.h" 27 #include "fh.h" 28 29 /* First zero out struct, set num_dirs, and strdups basedir */ 30 void init_ffsb_fs(ffsb_fs_t * fs, char *basedir, uint32_t num_data_dirs, 31 uint32_t numstartfiles, unsigned flags) 32 { 33 memset(fs, 0, sizeof(ffsb_fs_t)); 34 fs->basedir = ffsb_strdup(basedir); 35 fs->num_dirs = num_data_dirs; 36 fs->num_start_files = numstartfiles; 37 fs->flags = flags; 38 fs->create_blocksize = FFSB_FS_DEFAULT_CREATE_BLOCKSIZE; 39 fs->age_blocksize = FFSB_FS_DEFAULT_AGE_BLOCKSIZE; 40 fs->age_fs = 0; 41 } 42 43 /* 44 * Does not remove files/dirs on disk, only frees up data 45 * structures 46 */ 47 void destroy_ffsb_fs(ffsb_fs_t * fs) 48 { 49 free(fs->basedir); 50 destroy_filelist(&fs->files); 51 destroy_filelist(&fs->fill); 52 destroy_filelist(&fs->meta); 53 } 54 55 void clone_ffsb_fs(ffsb_fs_t * target, ffsb_fs_t * orig) 56 { 57 target->basedir = orig->basedir; 58 target->flags = orig->flags; 59 60 /* !!!! hackish, write a filelist_clone() function later */ 61 memcpy(&target->files, &orig->files, sizeof(orig->files)); 62 memcpy(&target->fill, &orig->fill, sizeof(orig->fill)); 63 memcpy(&target->meta, &orig->meta, sizeof(orig->meta)); 64 65 target->num_dirs = orig->num_dirs; 66 target->num_start_files = orig->num_start_files; 67 target->minfilesize = orig->minfilesize; 68 target->maxfilesize = orig->maxfilesize; 69 70 target->start_fsutil = orig->start_fsutil; 71 target->desired_fsutil = orig->desired_fsutil; 72 73 target->age_fs = orig->age_fs; 74 target->num_age_dirs = orig->num_age_dirs; 75 target->aging_tg = orig->aging_tg; 76 77 target->create_blocksize = orig->create_blocksize; 78 target->age_blocksize = orig->age_blocksize; 79 80 memcpy(target->op_data, orig->op_data, sizeof(void *) * FFSB_NUMOPS); 81 } 82 83 static void add_files(ffsb_fs_t * fs, struct benchfiles *bf, int num, 84 uint64_t minsize, uint64_t maxsize, unsigned blocksize) 85 { 86 struct ffsb_file *cur; 87 int i, fd, condition = 0, has_directio = 0; 88 randdata_t rd; 89 char *buf = ffsb_malloc(blocksize); 90 uint64_t initial_free = getfsutil_size(fs->basedir); 91 92 if (fs_get_directio(fs)) { 93 has_directio = 1; 94 fs_set_directio(fs, 0); 95 } 96 97 assert(blocksize); 98 99 init_random(&rd, 0); 100 101 if (num) 102 condition = num; 103 else if (fs->init_size) { 104 if (getfsutil(fs->basedir) != initial_free || 105 fs->init_size > (getfsutil_size(fs->basedir) - 106 initial_free)) 107 condition = 1; 108 else 109 condition = 0; 110 } else if (fs->init_fsutil) { 111 if (fs->init_fsutil > getfsutil(fs->basedir)) 112 condition = 1; 113 else 114 condition = 0; 115 } 116 117 while (condition) { 118 uint64_t size; 119 if (fs->num_weights) { 120 int num = 1 + getrandom(&rd, fs->sum_weights); 121 int curop = 0; 122 123 while (fs->size_weights[curop].weight < num) { 124 num -= fs->size_weights[curop].weight; 125 curop++; 126 } 127 size = fs->size_weights[curop].size; 128 } else 129 size = minsize + getllrandom(&rd, maxsize - minsize); 130 131 cur = add_file(bf, size, &rd); 132 fd = fhopencreate(cur->name, NULL, fs); 133 writefile_helper(fd, size, blocksize, buf, NULL, fs); 134 fhclose(fd, NULL, fs); 135 unlock_file_writer(cur); 136 137 if (num) 138 condition--; 139 else if (fs->init_size) { 140 if (fs->init_size > getfsutil_size(fs->basedir) - 141 initial_free) 142 condition = 1; 143 else 144 condition = 0; 145 } else if (fs->init_fsutil) { 146 if (fs->init_fsutil > getfsutil(fs->basedir)) 147 condition = 1; 148 else 149 condition = 0; 150 } 151 152 } 153 free(buf); 154 if (has_directio) 155 fs_set_directio(fs, 1); 156 } 157 158 static void age_fs(ffsb_fs_t * fs, double utilization); 159 static ffsb_fs_t *construct_new_fileset(ffsb_fs_t * fs); 160 static ffsb_fs_t *check_existing_fileset(ffsb_fs_t * fs); 161 162 void *construct_ffsb_fs(void *data) 163 { 164 ffsb_fs_t *fs = (ffsb_fs_t *) data; 165 ffsb_fs_t *ret = NULL; 166 167 if (fs_get_reuse_fs(fs)) { 168 printf("checking existing fs: %s\n", fs->basedir); 169 ret = check_existing_fileset(fs); 170 if (ret == NULL) { 171 printf("recreating new fileset\n"); 172 ret = construct_new_fileset(fs); 173 } 174 } else { 175 printf("creating new fileset %s\n", fs->basedir); 176 ret = construct_new_fileset(fs); 177 } 178 if (ret == NULL) { 179 printf("fs setup on %s failed\n", fs->basedir); 180 exit(1); 181 } 182 return ret; 183 } 184 185 static int verify_file(struct benchfiles *bf, char *fname, void *fs_ptr) 186 { 187 ffsb_fs_t *fs = (ffsb_fs_t *) fs_ptr; 188 uint64_t minsize = fs->minfilesize; 189 uint64_t maxsize = fs->maxfilesize; 190 uint64_t filesize = 0; 191 int fd = 0; 192 DIR *dirptr = NULL; 193 194 /* If it is a directory and it passed the name verification we 195 * don't need to do anything here 196 */ 197 dirptr = opendir(fname); 198 if (dirptr) { 199 closedir(dirptr); 200 return 0; 201 } 202 203 fd = open(fname, O_RDONLY); 204 /* If we can't open it for read we're done */ 205 if (fd < 0) { 206 printf("verify_file: error opening %s for readonly\n", fname); 207 perror(fname); 208 return 1; 209 } 210 close(fd); 211 filesize = ffsb_get_filesize(fname); 212 213 if (filesize < minsize || filesize > maxsize) { 214 printf("size %llu bytes for file %s is invalid\n", 215 filesize, fname); 216 return 1; 217 } 218 219 return 0; 220 } 221 222 /* Record the number of files and directorys there are supposed to be 223 * grab (check and build the structures) the regular data fileset then 224 * check to make sure the number of directories and files in that 225 * filelist matches up. Then grab the meta filelist and verify that 226 * the meta filelist is empty. Set up the filelist for fill (aging) 227 * and setup the ops for the benchmark. 228 */ 229 static ffsb_fs_t *check_existing_fileset(ffsb_fs_t * fs) 230 { 231 char buf[FILENAME_MAX * 3]; 232 int retval = 0; 233 uint32_t num_dirs = fs->num_dirs; 234 uint32_t num_files = fs->num_start_files; 235 236 if (fs->age_fs) { 237 printf("Aging and reusing the fileset are mutually " 238 "exclusive\n"); 239 printf("aborting\n"); 240 return NULL; 241 } 242 243 /* Set up bench/age dir */ 244 if (FILENAME_MAX <= 245 snprintf(buf, FILENAME_MAX, "%s/%s", fs->basedir, FILES_BASE)) { 246 printf("pathname \"%s\" is too long, aborting\n", buf); 247 return NULL; 248 } 249 250 /* Make a "dummy" filelist that has numsubdirs set to 0 and 251 * numstartfiles set to 0 252 */ 253 init_filelist(&fs->files, buf, FILES_BASE, 0, 0); 254 255 retval = grab_old_fileset(&fs->files, buf, verify_file, fs); 256 257 if (retval) 258 return NULL; 259 260 if ((get_listsize(&fs->files) != num_files) || 261 (get_numsubdirs(&fs->files) != num_dirs)) { 262 printf("check_existing_fileset: number of files (%u)" 263 " or directorys (%u) don't match up\n", 264 get_listsize(&fs->files), get_numsubdirs(&fs->files)); 265 destroy_filelist(&fs->files); 266 return NULL; 267 } 268 269 if (FILENAME_MAX <= 270 snprintf(buf, FILENAME_MAX, "%s/%s", fs->basedir, META_BASE)) { 271 printf("pathname \"%s\" is too long, aborting\n", buf); 272 return NULL; 273 } 274 275 init_filelist(&fs->meta, buf, META_BASE, 0, 1); 276 retval = grab_old_fileset(&fs->meta, buf, verify_file, fs); 277 278 if (retval) { 279 destroy_filelist(&fs->files); 280 return NULL; 281 } 282 283 if ((get_listsize(&fs->meta) != 0) || (get_numsubdirs(&fs->meta) != 0)) { 284 printf("check_existing_fileset: meta directory isn't empty\n" 285 "aborting\n"); 286 destroy_filelist(&fs->files); 287 destroy_filelist(&fs->meta); 288 return NULL; 289 } 290 291 /* Even though we won't use it, we still need to be consistent 292 * here. 293 */ 294 init_filelist(&fs->fill, buf, AGE_BASE, 0, 0); 295 296 /* Have to do this or everything else could break. */ 297 ops_setup_bench(fs); 298 299 return fs; 300 } 301 302 /* 303 * clean up fs, "rm -rf data meta" 304 * record utilization 305 * set up the dirs: files, meta 306 * age filesystem 307 * have ffsb_ops setup their data 308 * create starting files in files 309 */ 310 static ffsb_fs_t *construct_new_fileset(ffsb_fs_t * fs) 311 { 312 char buf[FILENAME_MAX * 3]; 313 314 /* TODO: Convert this quick and dirty rm -rf to a "real" 315 * programmatic version, that doesn't rely on the rm command. 316 */ 317 if (FILENAME_MAX * 3 <= snprintf(buf, FILENAME_MAX * 3, 318 "rm -rf %s/data %s/meta", 319 fs->basedir, fs->basedir)) { 320 printf("pathname too long for command \"%s\"\n", buf); 321 return NULL; 322 } 323 324 if (ffsb_system(buf) < 0) { 325 perror(buf); 326 return NULL; 327 } 328 329 fs->start_fsutil = getfsutil(fs->basedir); 330 331 /* Set up bench/age dir */ 332 if (FILENAME_MAX <= 333 snprintf(buf, FILENAME_MAX, "%s/%s", fs->basedir, FILES_BASE)) { 334 printf("pathname \"%s\" is too long, aborting\n", buf); 335 return NULL; 336 } 337 338 ffsb_mkdir(buf); 339 340 /* Regular files and aging share this directory */ 341 init_filelist(&fs->files, buf, FILES_BASE, fs->num_dirs, 1); 342 init_filelist(&fs->fill, buf, AGE_BASE, fs->num_age_dirs, 1); 343 344 /* Set up meta dir */ 345 snprintf(buf, FILENAME_MAX, "%s/%s", fs->basedir, META_BASE); 346 347 ffsb_mkdir(buf); 348 349 init_filelist(&fs->meta, buf, META_BASE, 0, 1); 350 351 /* Do aging */ 352 if (fs->age_fs) 353 age_fs(fs, fs->desired_fsutil); 354 355 /* Call back into ops, set for benchmark */ 356 ops_setup_bench(fs); 357 358 /* Create initial fileset */ 359 add_files(fs, &fs->files, fs->num_start_files, fs->minfilesize, 360 fs->maxfilesize, fs->create_blocksize); 361 return fs; 362 } 363 364 struct poll_data { 365 ffsb_fs_t *fs; 366 double util; 367 }; 368 369 static int fs_get_util(void *data) 370 { 371 struct poll_data *pd = (struct poll_data *)data; 372 double fsutil = getfsutil(pd->fs->basedir); 373 374 if (fsutil >= pd->util) 375 return 1; 376 377 return 0; 378 } 379 380 static void age_fs(ffsb_fs_t * fs, double utilization) 381 { 382 ffsb_barrier_t barrier; 383 pthread_t thread; 384 struct poll_data pdata; 385 ffsb_tg_t *tg = fs_get_aging_tg(fs); 386 tg_run_params_t params; 387 ffsb_config_t fc; 388 389 printf("aging fs %s from %.2lf to %.2lf\n", fs->basedir, 390 fs->start_fsutil, utilization); 391 ffsb_barrier_init(&barrier, tg_get_numthreads(tg)); 392 393 init_ffsb_config_1fs(&fc, fs, tg); 394 395 pdata.fs = fs; 396 pdata.util = utilization; 397 398 params.tg = tg; 399 params.poll_fn = fs_get_util; 400 params.poll_data = &pdata; 401 params.wait_time = 1; 402 params.fc = &fc; 403 404 params.tg_barrier = NULL; 405 params.thread_barrier = &barrier; 406 407 /* Call back into ops, setup for aging */ 408 ops_setup_age(fs); 409 410 /* Throw in some files to start off, so there's something */ 411 add_files(fs, &fs->fill, 10, 0, 0, fs->age_blocksize); 412 413 pthread_create(&thread, NULL, tg_run, ¶ms); 414 pthread_join(thread, NULL); 415 } 416 417 void fs_set_create_blocksize(ffsb_fs_t * fs, uint32_t blocksize) 418 { 419 fs->create_blocksize = blocksize; 420 } 421 422 void fs_set_age_blocksize(ffsb_fs_t * fs, uint32_t blocksize) 423 { 424 fs->age_blocksize = blocksize; 425 } 426 427 uint32_t fs_get_create_blocksize(ffsb_fs_t * fs) 428 { 429 return fs->create_blocksize; 430 } 431 432 uint32_t fs_get_age_blocksize(ffsb_fs_t * fs) 433 { 434 return fs->age_blocksize; 435 } 436 437 char *fs_get_basedir(ffsb_fs_t * fs) 438 { 439 return fs->basedir; 440 } 441 442 uint32_t fs_get_numstartfiles(ffsb_fs_t * fs) 443 { 444 return fs->num_start_files; 445 } 446 447 uint32_t fs_get_numdirs(ffsb_fs_t * fs) 448 { 449 return fs->num_dirs; 450 } 451 452 int fs_get_libcio(ffsb_fs_t * fs) 453 { 454 return fs->flags & FFSB_FS_LIBCIO; 455 } 456 457 void fs_set_libcio(ffsb_fs_t * fs, int lio) 458 { 459 if (lio) 460 fs->flags |= FFSB_FS_LIBCIO; 461 else 462 fs->flags &= ~0 & ~FFSB_FS_LIBCIO; 463 } 464 465 int fs_get_directio(ffsb_fs_t * fs) 466 { 467 return fs->flags & FFSB_FS_DIRECTIO; 468 } 469 470 void fs_set_directio(ffsb_fs_t * fs, int dio) 471 { 472 if (dio) 473 fs->flags |= FFSB_FS_DIRECTIO; 474 else 475 fs->flags &= ~0 & ~FFSB_FS_DIRECTIO; 476 } 477 478 int fs_get_alignio(ffsb_fs_t * fs) 479 { 480 return fs->flags & FFSB_FS_ALIGNIO4K; 481 } 482 483 void fs_set_alignio(ffsb_fs_t * fs, int aio) 484 { 485 if (aio) 486 fs->flags |= FFSB_FS_ALIGNIO4K; 487 else 488 fs->flags &= ~0 & ~FFSB_FS_ALIGNIO4K; 489 } 490 491 int fs_get_reuse_fs(ffsb_fs_t * fs) 492 { 493 return fs->flags & FFSB_FS_REUSE_FS; 494 } 495 496 void fs_set_reuse_fs(ffsb_fs_t * fs, int rfs) 497 { 498 if (rfs) 499 fs->flags |= FFSB_FS_REUSE_FS; 500 else 501 fs->flags &= ~0 & ~FFSB_FS_REUSE_FS; 502 } 503 504 struct benchfiles *fs_get_datafiles(ffsb_fs_t * fs) 505 { 506 return &fs->files; 507 } 508 509 struct benchfiles *fs_get_metafiles(ffsb_fs_t * fs) 510 { 511 return &fs->meta; 512 } 513 514 struct benchfiles *fs_get_agefiles(ffsb_fs_t * fs) 515 { 516 return &fs->fill; 517 } 518 519 void fs_set_aging_tg(ffsb_fs_t * fs, struct ffsb_tg *tg, double util) 520 { 521 fs->aging_tg = tg; 522 fs->age_fs = 1; 523 fs->desired_fsutil = util; 524 } 525 526 struct ffsb_tg *fs_get_aging_tg(ffsb_fs_t * fs) 527 { 528 return fs->aging_tg; 529 } 530 531 int fs_get_agefs(ffsb_fs_t * fs) 532 { 533 return fs->age_fs; 534 } 535 536 /* TODO: Implement this!!!*/ 537 void fs_set_num_age_dirs(ffsb_fs_t * fs, uint32_t numdirs) 538 { 539 fs->num_age_dirs = numdirs; 540 } 541 542 void fs_set_opdata(ffsb_fs_t * fs, void *data, unsigned opnum) 543 { 544 fs->op_data[opnum] = data; 545 } 546 547 void *fs_get_opdata(ffsb_fs_t * fs, unsigned opnum) 548 { 549 return fs->op_data[opnum]; 550 } 551 552 void fs_set_min_filesize(ffsb_fs_t * fs, uint64_t size) 553 { 554 fs->minfilesize = size; 555 } 556 557 void fs_set_max_filesize(ffsb_fs_t * fs, uint64_t size) 558 { 559 fs->maxfilesize = size; 560 } 561 562 uint64_t fs_get_min_filesize(ffsb_fs_t * fs) 563 { 564 return fs->minfilesize; 565 } 566 567 uint64_t fs_get_max_filesize(ffsb_fs_t * fs) 568 { 569 return fs->maxfilesize; 570 } 571 572 double fs_get_desired_fsutil(ffsb_fs_t * fs) 573 { 574 return fs->desired_fsutil; 575 } 576 577 void fs_print_config(ffsb_fs_t * fs) 578 { 579 char buf[256]; 580 581 printf("FileSystem %s\n", fs->basedir); 582 printf("==========\n"); 583 printf("\t num_dirs = %u\n", fs->num_dirs); 584 printf("\t starting files = %u\n", fs->num_start_files); 585 printf("\t\n"); 586 if (fs->num_weights) { 587 int i; 588 printf("\t Fileset weight:\n"); 589 for (i = 0; i < fs->num_weights; i++) 590 printf("\t\t %12llu (%6s) -> %u (%.2f\%)\n", 591 fs->size_weights[i].size, 592 ffsb_printsize(buf, fs->size_weights[i].size, 593 256), fs->size_weights[i].weight, 594 ((float)fs->size_weights[i].weight / 595 (float)fs->sum_weights) * 100); 596 } else { 597 printf("\t min file size = %llu\t(%s)\n", fs->minfilesize, 598 ffsb_printsize(buf, fs->minfilesize, 256)); 599 printf("\t max file size = %llu\t(%s)\n", fs->maxfilesize, 600 ffsb_printsize(buf, fs->maxfilesize, 256)); 601 } 602 printf("\t directio = %s\n", (fs->flags & FFSB_FS_DIRECTIO) ? 603 "on" : "off"); 604 printf("\t alignedio = %s\n", (fs->flags & FFSB_FS_ALIGNIO4K) ? 605 "on" : "off"); 606 printf("\t bufferedio = %s\n", (fs->flags & FFSB_FS_LIBCIO) ? 607 "on" : "off"); 608 printf("\t\n"); 609 printf("\t aging is %s\n", (fs->age_fs) ? "on" : "off"); 610 printf("\t current utilization = %.2f\%\n", 611 getfsutil(fs->basedir) * 100); 612 if (fs->age_fs) { 613 printf("\t desired utilization = %.2lf%\n", 614 fs->desired_fsutil * 100); 615 printf("\t \n"); 616 tg_print_config_aging(fs->aging_tg, fs->basedir); 617 } 618 printf("\t\n"); 619 } 620 621 int fs_needs_stats(ffsb_fs_t * fs, syscall_t sys) 622 { 623 return (fs != NULL) ? (int)fs->fsd.config : 0; 624 } 625 626 void fs_add_stat(ffsb_fs_t * fs, syscall_t sys, uint32_t val) 627 { 628 if (fs) 629 ffsb_add_data(&fs->fsd, sys, val); 630 } 631