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