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