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