1 /*- 2 * Copyright (c) 1992, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 */ 7 8 #ifndef lint 9 static char copyright[] = 10 "@(#) Copyright (c) 1992, 1993\n\ 11 The Regents of the University of California. All rights reserved.\n"; 12 #endif /* not lint */ 13 14 #ifndef lint 15 static char sccsid[] = "@(#)cleanerd.c 8.4 (Berkeley) 05/24/95"; 16 #endif /* not lint */ 17 18 #include <sys/param.h> 19 #include <sys/mount.h> 20 #include <sys/time.h> 21 22 #include <ufs/ufs/dinode.h> 23 #include <ufs/lfs/lfs.h> 24 25 #include <signal.h> 26 #include <stdio.h> 27 #include <stdlib.h> 28 #include <unistd.h> 29 30 #include "clean.h" 31 char *special = "cleanerd"; 32 int do_small = 0; 33 int do_mmap = 0; 34 int stat_report = 0; 35 struct cleaner_stats { 36 double util_tot; 37 double util_sos; 38 int blocks_read; 39 int blocks_written; 40 int segs_cleaned; 41 int segs_empty; 42 int segs_error; 43 } cleaner_stats; 44 45 struct seglist { 46 int sl_id; /* segment number */ 47 int sl_cost; /* cleaning cost */ 48 char sl_bytes; /* bytes in segment */ 49 }; 50 51 struct tossstruct { 52 struct lfs *lfs; 53 int seg; 54 }; 55 56 #define CLEAN_BYTES 0x1 57 58 /* function prototypes for system calls; not sure where they should go */ 59 int lfs_segwait __P((fsid_t *, struct timeval *)); 60 int lfs_segclean __P((fsid_t *, u_long)); 61 int lfs_bmapv __P((fsid_t *, BLOCK_INFO *, int)); 62 int lfs_markv __P((fsid_t *, BLOCK_INFO *, int)); 63 64 /* function prototypes */ 65 int bi_tossold __P((const void *, const void *, const void *)); 66 int choose_segments __P((FS_INFO *, struct seglist *, 67 int (*)(FS_INFO *, SEGUSE *))); 68 void clean_fs __P((FS_INFO *, int (*)(FS_INFO *, SEGUSE *), int, long)); 69 int clean_loop __P((FS_INFO *, int, long)); 70 int clean_segment __P((FS_INFO *, int)); 71 int cost_benefit __P((FS_INFO *, SEGUSE *)); 72 int cost_compare __P((const void *, const void *)); 73 void sig_report __P((int)); 74 75 /* 76 * Cleaning Cost Functions: 77 * 78 * These return the cost of cleaning a segment. The higher the cost value 79 * the better it is to clean the segment, so empty segments have the highest 80 * cost. (It is probably better to think of this as a priority value 81 * instead). 82 * 83 * This is the cost-benefit policy simulated and described in Rosenblum's 84 * 1991 SOSP paper. 85 */ 86 87 int 88 cost_benefit(fsp, su) 89 FS_INFO *fsp; /* file system information */ 90 SEGUSE *su; 91 { 92 struct lfs *lfsp; 93 struct timeval t; 94 int age; 95 int live; 96 97 gettimeofday(&t, NULL); 98 99 live = su->su_nbytes; 100 age = t.tv_sec < su->su_lastmod ? 0 : t.tv_sec - su->su_lastmod; 101 102 lfsp = &fsp->fi_lfs; 103 if (live == 0) 104 return (t.tv_sec * lblkno(lfsp, seg_size(lfsp))); 105 else { 106 /* 107 * from lfsSegUsage.c (Mendel's code). 108 * priority calculation is done using INTEGER arithmetic. 109 * sizes are in BLOCKS (that is why we use lblkno below). 110 * age is in seconds. 111 * 112 * priority = ((seg_size - live) * age) / (seg_size + live) 113 */ 114 #ifdef VERBOSE 115 if (live < 0 || live > seg_size(lfsp)) { 116 err(0, "Bad segusage count: %d", live); 117 live = 0; 118 } 119 #endif 120 return (lblkno(lfsp, seg_size(lfsp) - live) * age) 121 / lblkno(lfsp, seg_size(lfsp) + live); 122 } 123 } 124 125 int 126 main(argc, argv) 127 int argc; 128 char *argv[]; 129 { 130 FS_INFO *fsp; 131 struct statfs *lstatfsp; /* file system stats */ 132 struct timeval timeout; /* sleep timeout */ 133 fsid_t fsid; 134 long clean_opts; /* cleaning options */ 135 int i, nodaemon, segs_per_clean; 136 int opt, cmd_err; 137 char *fs_name; /* name of filesystem to clean */ 138 extern int optind; 139 140 cmd_err = nodaemon = 0; 141 clean_opts = 0; 142 segs_per_clean = 1; 143 while ((opt = getopt(argc, argv, "bdmn:r:s")) != EOF) { 144 switch (opt) { 145 case 'b': /* 146 * Use live bytes to determine 147 * how many segs to clean. 148 */ 149 clean_opts |= CLEAN_BYTES; 150 break; 151 case 'd': /* Debug mode. */ 152 nodaemon = 1; 153 break; 154 case 'm': /* Use mmap instead of read/write */ 155 do_mmap = 1; 156 break; 157 case 'n': /* How many segs to clean at once */ 158 segs_per_clean = atoi(optarg); 159 break; 160 case 'r': /* Report every stat_report segments */ 161 stat_report = atoi(optarg); 162 break; 163 case 's': /* small writes */ 164 do_small = 1; 165 break; 166 default: 167 ++cmd_err; 168 } 169 } 170 argc -= optind; 171 argv += optind; 172 if (cmd_err || (argc != 1)) 173 err(1, "usage: lfs_cleanerd [-smd] fs_name"); 174 175 fs_name = argv[0]; 176 177 signal(SIGINT, sig_report); 178 signal(SIGUSR1, sig_report); 179 signal(SIGUSR2, sig_report); 180 if (fs_getmntinfo(&lstatfsp, fs_name, "lfs") == 0) { 181 /* didn't find the filesystem */ 182 err(1, "lfs_cleanerd: filesystem %s isn't an LFS!", fs_name); 183 } 184 185 if (!nodaemon) /* should we become a daemon, chdir to / & close fd's */ 186 if (daemon(0, 0) == -1) 187 err(1, "lfs_cleanerd: couldn't become a daemon!"); 188 189 timeout.tv_sec = 5*60; /* five minutes */ 190 timeout.tv_usec = 0; 191 fsid.val[0] = 0; 192 fsid.val[1] = 0; 193 194 for (fsp = get_fs_info(lstatfsp, do_mmap); ; 195 reread_fs_info(fsp, do_mmap)) { 196 /* 197 * clean the filesystem, and, if it needed cleaning 198 * (i.e. it returned nonzero) try it again 199 * to make sure that some nasty process hasn't just 200 * filled the disk system up. 201 */ 202 if (clean_loop(fsp, segs_per_clean, clean_opts)) 203 continue; 204 205 #ifdef VERBOSE 206 (void)printf("Cleaner going to sleep.\n"); 207 #endif 208 if (lfs_segwait(&fsid, &timeout) < 0) 209 err(0, "lfs_segwait: returned error\n"); 210 #ifdef VERBOSE 211 (void)printf("Cleaner waking up.\n"); 212 #endif 213 } 214 } 215 216 /* return the number of segments cleaned */ 217 int 218 clean_loop(fsp, nsegs, options) 219 FS_INFO *fsp; /* file system information */ 220 int nsegs; 221 long options; 222 { 223 double loadavg[MAXLOADS]; 224 time_t now; 225 u_long max_free_segs; 226 227 /* 228 * Compute the maximum possible number of free segments, given the 229 * number of free blocks. 230 */ 231 max_free_segs = fsp->fi_statfsp->f_bfree / fsp->fi_lfs.lfs_ssize; 232 233 /* 234 * We will clean if there are not enough free blocks or total clean 235 * space is less than BUSY_LIM % of possible clean space. 236 */ 237 now = time((time_t *)NULL); 238 if (fsp->fi_cip->clean < max_free_segs && 239 (fsp->fi_cip->clean <= MIN_SEGS(&fsp->fi_lfs) || 240 fsp->fi_cip->clean < max_free_segs * BUSY_LIM)) { 241 printf("Cleaner Running at %s (%d of %d segments available)\n", 242 ctime(&now), fsp->fi_cip->clean, max_free_segs); 243 clean_fs(fsp, cost_benefit, nsegs, options); 244 return (1); 245 } else { 246 /* 247 * We will also clean if the system is reasonably idle and 248 * the total clean space is less then IDLE_LIM % of possible 249 * clean space. 250 */ 251 if (getloadavg(loadavg, MAXLOADS) == -1) { 252 perror("getloadavg: failed\n"); 253 return (-1); 254 } 255 if (loadavg[ONE_MIN] == 0.0 && loadavg[FIVE_MIN] && 256 fsp->fi_cip->clean < max_free_segs * IDLE_LIM) { 257 clean_fs(fsp, cost_benefit, nsegs, options); 258 printf("Cleaner Running at %s (system idle)\n", 259 ctime(&now)); 260 return (1); 261 } 262 } 263 printf("Cleaner Not Running at %s\n", ctime(&now)); 264 return (0); 265 } 266 267 268 void 269 clean_fs(fsp, cost_func, nsegs, options) 270 FS_INFO *fsp; /* file system information */ 271 int (*cost_func) __P((FS_INFO *, SEGUSE *)); 272 int nsegs; 273 long options; 274 { 275 struct seglist *segs, *sp; 276 int to_clean, cleaned_bytes; 277 int i; 278 279 if ((segs = 280 malloc(fsp->fi_lfs.lfs_nseg * sizeof(struct seglist))) == NULL) { 281 err(0, "malloc failed"); 282 return; 283 } 284 i = choose_segments(fsp, segs, cost_func); 285 #ifdef VERBOSE 286 printf("clean_fs: found %d segments to clean in file system %s\n", 287 i, fsp->fi_statfsp->f_mntonname); 288 fflush(stdout); 289 #endif 290 if (i) { 291 /* Check which cleaning algorithm to use. */ 292 if (options & CLEAN_BYTES) { 293 cleaned_bytes = 0; 294 to_clean = nsegs << 295 (fsp->fi_lfs.lfs_segshift + fsp->fi_lfs.lfs_bshift); 296 for (sp = segs; i && cleaned_bytes < to_clean; 297 i--, ++sp) { 298 if (clean_segment(fsp, sp->sl_id) < 0) 299 perror("clean_segment failed"); 300 else if (lfs_segclean(&fsp->fi_statfsp->f_fsid, 301 sp->sl_id) < 0) 302 perror("lfs_segclean failed"); 303 printf("Cleaned segment %d (%d bytes)\n", 304 sp->sl_id, sp->sl_bytes); 305 cleaned_bytes += sp->sl_bytes; 306 } 307 } else 308 for (i = MIN(i, nsegs), sp = segs; i-- ; ++sp) { 309 if (clean_segment(fsp, sp->sl_id) < 0) 310 perror("clean_segment failed"); 311 else if (lfs_segclean(&fsp->fi_statfsp->f_fsid, 312 sp->sl_id) < 0) 313 perror("lfs_segclean failed"); 314 printf("Completed cleaning segment %d\n", sp->sl_id); 315 } 316 } 317 free(segs); 318 } 319 320 /* 321 * Segment with the highest priority get sorted to the beginning of the 322 * list. This sort assumes that empty segments always have a higher 323 * cost/benefit than any utilized segment. 324 */ 325 int 326 cost_compare(a, b) 327 const void *a; 328 const void *b; 329 { 330 return (((struct seglist *)b)->sl_cost - 331 ((struct seglist *)a)->sl_cost); 332 } 333 334 335 /* 336 * Returns the number of segments to be cleaned with the elements of seglist 337 * filled in. 338 */ 339 int 340 choose_segments(fsp, seglist, cost_func) 341 FS_INFO *fsp; 342 struct seglist *seglist; 343 int (*cost_func) __P((FS_INFO *, SEGUSE *)); 344 { 345 struct lfs *lfsp; 346 struct seglist *sp; 347 SEGUSE *sup; 348 int i, nsegs; 349 350 lfsp = &fsp->fi_lfs; 351 352 #ifdef VERBOSE 353 (void)printf("Entering choose_segments\n"); 354 #endif 355 dump_super(lfsp); 356 dump_cleaner_info(fsp->fi_cip); 357 358 for (sp = seglist, i = 0; i < lfsp->lfs_nseg; ++i) { 359 sup = SEGUSE_ENTRY(lfsp, fsp->fi_segusep, i); 360 PRINT_SEGUSE(sup, i); 361 if (!(sup->su_flags & SEGUSE_DIRTY) || 362 sup->su_flags & SEGUSE_ACTIVE) 363 continue; 364 #ifdef VERBOSE 365 (void)printf("\tchoosing segment %d\n", i); 366 #endif 367 sp->sl_cost = (*cost_func)(fsp, sup); 368 sp->sl_id = i; 369 sp->sl_bytes = sup->su_nbytes; 370 ++sp; 371 } 372 nsegs = sp - seglist; 373 qsort(seglist, nsegs, sizeof(struct seglist), cost_compare); 374 #ifdef VERBOSE 375 (void)printf("Returning %d segments\n", nsegs); 376 #endif 377 return (nsegs); 378 } 379 380 381 int 382 clean_segment(fsp, id) 383 FS_INFO *fsp; /* file system information */ 384 int id; /* segment number */ 385 { 386 BLOCK_INFO *block_array, *bp; 387 SEGUSE *sp; 388 struct lfs *lfsp; 389 struct tossstruct t; 390 caddr_t seg_buf; 391 double util; 392 int num_blocks, maxblocks, clean_blocks; 393 394 lfsp = &fsp->fi_lfs; 395 sp = SEGUSE_ENTRY(lfsp, fsp->fi_segusep, id); 396 397 #ifdef VERBOSE 398 (void)printf("cleaning segment %d: contains %lu bytes\n", id, 399 sp->su_nbytes); 400 fflush(stdout); 401 #endif 402 /* XXX could add debugging to verify that segment is really empty */ 403 if (sp->su_nbytes == sp->su_nsums * LFS_SUMMARY_SIZE) { 404 ++cleaner_stats.segs_empty; 405 return (0); 406 } 407 408 /* map the segment into a buffer */ 409 if (mmap_segment(fsp, id, &seg_buf, do_mmap) < 0) { 410 err(0, "mmap_segment failed"); 411 ++cleaner_stats.segs_error; 412 return (-1); 413 } 414 /* get a list of blocks that are contained by the segment */ 415 if (lfs_segmapv(fsp, id, seg_buf, &block_array, &num_blocks) < 0) { 416 err(0, "clean_segment: lfs_segmapv failed"); 417 ++cleaner_stats.segs_error; 418 return (-1); 419 } 420 cleaner_stats.blocks_read += fsp->fi_lfs.lfs_ssize; 421 422 #ifdef VERBOSE 423 (void)printf("lfs_segmapv returned %d blocks\n", num_blocks); 424 fflush(stdout); 425 #endif 426 427 /* get the current disk address of blocks contained by the segment */ 428 if (lfs_bmapv(&fsp->fi_statfsp->f_fsid, block_array, num_blocks) < 0) { 429 perror("clean_segment: lfs_bmapv failed\n"); 430 ++cleaner_stats.segs_error; 431 return -1; 432 } 433 434 /* Now toss any blocks not in the current segment */ 435 t.lfs = lfsp; 436 t.seg = id; 437 toss(block_array, &num_blocks, sizeof(BLOCK_INFO), bi_tossold, &t); 438 439 /* Check if last element should be tossed */ 440 if (num_blocks && bi_tossold(&t, block_array + num_blocks - 1, NULL)) 441 --num_blocks; 442 443 #ifdef VERBOSE 444 { 445 BLOCK_INFO *_bip; 446 u_long *lp; 447 int i; 448 449 (void)printf("after bmapv still have %d blocks\n", num_blocks); 450 fflush(stdout); 451 if (num_blocks) 452 printf("BLOCK INFOS\n"); 453 for (_bip = block_array, i=0; i < num_blocks; ++_bip, ++i) { 454 PRINT_BINFO(_bip); 455 lp = (u_long *)_bip->bi_bp; 456 } 457 } 458 459 #endif 460 ++cleaner_stats.segs_cleaned; 461 cleaner_stats.blocks_written += num_blocks; 462 util = ((double)num_blocks / fsp->fi_lfs.lfs_ssize); 463 cleaner_stats.util_tot += util; 464 cleaner_stats.util_sos += util * util; 465 466 if (do_small) 467 maxblocks = MAXPHYS / fsp->fi_lfs.lfs_bsize - 1; 468 else 469 maxblocks = num_blocks; 470 471 for (bp = block_array; num_blocks > 0; bp += clean_blocks) { 472 clean_blocks = maxblocks < num_blocks ? maxblocks : num_blocks; 473 if (lfs_markv(&fsp->fi_statfsp->f_fsid, 474 bp, clean_blocks) < 0) { 475 err(0, "clean_segment: lfs_markv failed"); 476 ++cleaner_stats.segs_error; 477 return (-1); 478 } 479 num_blocks -= clean_blocks; 480 } 481 482 free(block_array); 483 munmap_segment(fsp, seg_buf, do_mmap); 484 if (stat_report && cleaner_stats.segs_cleaned % stat_report == 0) 485 sig_report(SIGUSR1); 486 return (0); 487 } 488 489 490 int 491 bi_tossold(client, a, b) 492 const void *client; 493 const void *a; 494 const void *b; 495 { 496 const struct tossstruct *t; 497 498 t = (struct tossstruct *)client; 499 500 return (((BLOCK_INFO *)a)->bi_daddr == LFS_UNUSED_DADDR || 501 datosn(t->lfs, ((BLOCK_INFO *)a)->bi_daddr) != t->seg); 502 } 503 504 void 505 sig_report(sig) 506 int sig; 507 { 508 double avg; 509 510 printf("lfs_cleanerd:\t%s%d\n\t\t%s%d\n\t\t%s%d\n\t\t%s%d\n\t\t%s%d\n", 511 "blocks_read ", cleaner_stats.blocks_read, 512 "blocks_written ", cleaner_stats.blocks_written, 513 "segs_cleaned ", cleaner_stats.segs_cleaned, 514 "segs_empty ", cleaner_stats.segs_empty, 515 "seg_error ", cleaner_stats.segs_error); 516 printf("\t\t%s%5.2f\n\t\t%s%5.2f\n", 517 "util_tot ", cleaner_stats.util_tot, 518 "util_sos ", cleaner_stats.util_sos); 519 printf("\t\tavg util: %4.2f std dev: %9.6f\n", 520 avg = cleaner_stats.util_tot / cleaner_stats.segs_cleaned, 521 cleaner_stats.util_sos / cleaner_stats.segs_cleaned - avg * avg); 522 523 524 if (sig == SIGUSR2) { 525 cleaner_stats.blocks_read = 0; 526 cleaner_stats.blocks_written = 0; 527 cleaner_stats.segs_cleaned = 0; 528 cleaner_stats.segs_empty = 0; 529 cleaner_stats.segs_error = 0; 530 cleaner_stats.util_tot = 0.0; 531 cleaner_stats.util_sos = 0.0; 532 } 533 if (sig == SIGINT) 534 exit(0); 535 } 536