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