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