1*55860Sbostic /*- 2*55860Sbostic * Copyright (c) 1992 The Regents of the University of California. 3*55860Sbostic * All rights reserved. 4*55860Sbostic * 5*55860Sbostic * %sccs.include.redist.c% 6*55860Sbostic */ 7*55860Sbostic 8*55860Sbostic #ifndef lint 9*55860Sbostic char copyright[] = 10*55860Sbostic "@(#) Copyright (c) 1992 The Regents of the University of California.\n\ 11*55860Sbostic All rights reserved.\n"; 12*55860Sbostic #endif /* not lint */ 13*55860Sbostic 14*55860Sbostic #ifndef lint 15*55860Sbostic static char sccsid[] = "@(#)cleanerd.c 5.1 (Berkeley) 08/06/92"; 16*55860Sbostic #endif /* not lint */ 17*55860Sbostic 1855857Sbostic #include <sys/param.h> 1955857Sbostic #include <sys/mount.h> 2055857Sbostic #include <sys/time.h> 21*55860Sbostic 2255857Sbostic #include <ufs/ufs/dinode.h> 2355857Sbostic #include <ufs/lfs/lfs.h> 2455857Sbostic 2555857Sbostic #include <stdio.h> 2655857Sbostic #include <stdlib.h> 2755857Sbostic #include <unistd.h> 2855857Sbostic 2955857Sbostic #include "clean.h" 3055857Sbostic char *special = "cleanerd"; 3155857Sbostic 3255857Sbostic struct seglist { 3355857Sbostic int sl_id; /* segment number */ 3455857Sbostic int sl_cost; /* cleaning cost */ 3555857Sbostic char sl_empty; /* is segment empty */ 3655857Sbostic }; 3755857Sbostic 3855857Sbostic struct tossstruct { 3955857Sbostic struct lfs *lfs; 4055857Sbostic int seg; 4155857Sbostic }; 4255857Sbostic 4355857Sbostic /* function prototypes for system calls; not sure where they should go */ 4455857Sbostic int lfs_segwait __P((fsid_t, struct timeval *)); 4555857Sbostic int lfs_segclean __P((fsid_t, u_long)); 4655857Sbostic int lfs_bmapv __P((fsid_t, BLOCK_INFO *, int)); 4755857Sbostic int lfs_markv __P((fsid_t, BLOCK_INFO *, int, INODE_INFO *, int)); 4855857Sbostic 4955857Sbostic /* function prototypes */ 5055857Sbostic int bi_tossold __P((const void *, const void *, const void *)); 5155857Sbostic int choose_segments __P((FS_INFO *, struct seglist *, 5255857Sbostic int (*)(FS_INFO *, SEGUSE *))); 5355857Sbostic void clean_fs __P((FS_INFO *, int (*)(FS_INFO *, SEGUSE *))); 5455857Sbostic int clean_loop __P((FS_INFO *)); 5555857Sbostic int clean_segment __P((FS_INFO *, int)); 5655857Sbostic int cost_benefit __P((FS_INFO *, SEGUSE *)); 5755857Sbostic int cost_compare __P((const void *, const void *)); 5855857Sbostic 5955857Sbostic /* 6055857Sbostic * Cleaning Cost Functions: 6155857Sbostic * 6255857Sbostic * These return the cost of cleaning a segment. The higher the cost value 6355857Sbostic * the better it is to clean the segment, so empty segments have the highest 6455857Sbostic * cost. (It is probably better to think of this as a priority value 6555857Sbostic * instead). 6655857Sbostic * 6755857Sbostic * This is the cost-benefit policy simulated and described in Rosenblum's 6855857Sbostic * 1991 SOSP paper. 6955857Sbostic */ 7055857Sbostic 7155857Sbostic int 7255857Sbostic cost_benefit(fsp, su) 7355857Sbostic FS_INFO *fsp; /* file system information */ 7455857Sbostic SEGUSE *su; 7555857Sbostic { 7655857Sbostic struct lfs *lfsp; 7755857Sbostic struct timeval t; 7855857Sbostic int age; 7955857Sbostic int live; 8055857Sbostic 8155857Sbostic gettimeofday(&t, NULL); 8255857Sbostic 8355857Sbostic live = su->su_nbytes; 8455857Sbostic age = t.tv_sec - su->su_lastmod < 0 ? 0 : t.tv_sec - su->su_lastmod; 8555857Sbostic 8655857Sbostic lfsp = &fsp->fi_lfs; 8755857Sbostic if (live == 0) 8855857Sbostic return (t.tv_sec * lblkno(lfsp, seg_size(lfsp))); 8955857Sbostic else { 9055857Sbostic /* 9155857Sbostic * from lfsSegUsage.c (Mendel's code). 9255857Sbostic * priority calculation is done using INTEGER arithmetic. 9355857Sbostic * sizes are in BLOCKS (that is why we use lblkno below). 9455857Sbostic * age is in seconds. 9555857Sbostic * 9655857Sbostic * priority = ((seg_size - live) * age) / (seg_size + live) 9755857Sbostic */ 9855857Sbostic #ifdef VERBOSE 9955857Sbostic if (live < 0 || live > seg_size(lfsp)) { 10055857Sbostic err(0, "Bad segusage count: %d", live); 10155857Sbostic live = 0; 10255857Sbostic } 10355857Sbostic #endif 10455857Sbostic return (lblkno(lfsp, seg_size(lfsp) - live) * age) 10555857Sbostic / lblkno(lfsp, seg_size(lfsp) + live); 10655857Sbostic } 10755857Sbostic } 10855857Sbostic 109*55860Sbostic int 11055857Sbostic main(argc, argv) 11155857Sbostic int argc; 11255857Sbostic char *argv[]; 11355857Sbostic { 11455857Sbostic FS_INFO *lfp, *fsp; 11555857Sbostic struct statfs *lstatfsp; /* file system stats */ 11655857Sbostic struct timeval timeout; /* sleep timeout */ 11755857Sbostic fsid_t fsid; 11855857Sbostic int count; /* number of file systems */ 11955857Sbostic int i; 12055857Sbostic 12155857Sbostic count = fs_getmntinfo(&lstatfsp, MOUNT_LFS); 12255857Sbostic 12355857Sbostic timeout.tv_sec = 5*60; /* five minutes */ 12455857Sbostic timeout.tv_usec = 0; 12555857Sbostic fsid.val[0] = 0; 12655857Sbostic fsid.val[1] = 0; 12755857Sbostic 12855857Sbostic for (fsp = get_fs_info(lstatfsp, count); ; reread_fs_info(fsp, count)) { 12955857Sbostic for (lfp = fsp, i = 0; i < count; ++lfp, ++i) 13055857Sbostic clean_loop(lfp); 13155857Sbostic 13255857Sbostic #ifdef VERBOSE 13355857Sbostic (void)printf("Cleaner going to sleep.\n"); 13455857Sbostic #endif 13555857Sbostic if (lfs_segwait(fsid, &timeout) < 0) 13655857Sbostic err(0, "lfs_segwait: returned error\n"); 13755857Sbostic #ifdef VERBOSE 13855857Sbostic (void)printf("Cleaner waking up.\n"); 13955857Sbostic #endif 14055857Sbostic } 14155857Sbostic } 14255857Sbostic 14355857Sbostic /* return the number of segments cleaned */ 14455857Sbostic int 14555857Sbostic clean_loop(fsp) 14655857Sbostic FS_INFO *fsp; /* file system information */ 14755857Sbostic { 14855857Sbostic double loadavg[MAXLOADS]; 14955857Sbostic time_t now; 15055857Sbostic u_long max_free_segs; 15155857Sbostic 15255857Sbostic /* 15355857Sbostic * Compute the maximum possible number of free segments, given the 15455857Sbostic * number of free blocks. 15555857Sbostic */ 15655857Sbostic max_free_segs = fsp->fi_statfsp->f_bfree / fsp->fi_lfs.lfs_ssize; 15755857Sbostic 15855857Sbostic /* 15955857Sbostic * We will clean if there are not enough free blocks or total clean 16055857Sbostic * space is less than BUSY_LIM % of possible clean space. 16155857Sbostic */ 16255857Sbostic now = time((time_t *)NULL); 16355857Sbostic if (fsp->fi_cip->clean <= MIN_SEGS(&fsp->fi_lfs) || 16455857Sbostic fsp->fi_cip->clean < max_free_segs * BUSY_LIM) { 16555857Sbostic clean_fs(fsp, cost_benefit); 16655857Sbostic printf("Cleaner Running at %s (need space)\n", 16755857Sbostic ctime(&now)); 16855857Sbostic return (1); 16955857Sbostic } else { 17055857Sbostic /* 17155857Sbostic * We will also clean if the system is reasonably idle and 17255857Sbostic * the total clean space is less then IDLE_LIM % of possible 17355857Sbostic * clean space. 17455857Sbostic */ 17555857Sbostic if (getloadavg(loadavg, MAXLOADS) == -1) { 17655857Sbostic perror("getloadavg: failed\n"); 17755857Sbostic return (-1); 17855857Sbostic } 17955857Sbostic if (loadavg[ONE_MIN] == 0.0 && loadavg[FIVE_MIN] && 18055857Sbostic fsp->fi_cip->clean < max_free_segs * IDLE_LIM) { 18155857Sbostic clean_fs(fsp, cost_benefit); 18255857Sbostic printf("Cleaner Running at %s (system idle)\n", 18355857Sbostic ctime(&now)); 18455857Sbostic return (1); 18555857Sbostic } 18655857Sbostic } 18755857Sbostic printf("Cleaner Not Running at %s\n", ctime(&now)); 18855857Sbostic return (0); 18955857Sbostic } 19055857Sbostic 19155857Sbostic 19255857Sbostic void 19355857Sbostic clean_fs(fsp, cost_func) 19455857Sbostic FS_INFO *fsp; /* file system information */ 19555857Sbostic int (*cost_func) __P((FS_INFO *, SEGUSE *)); 19655857Sbostic { 19755857Sbostic struct seglist *segs, *sp; 19855857Sbostic int i; 19955857Sbostic 20055857Sbostic if ((segs = malloc(fsp->fi_lfs.lfs_nseg * sizeof(struct seglist))) 20155857Sbostic == NULL) { 20255857Sbostic err(0, "malloc failed"); 20355857Sbostic return; 20455857Sbostic } 20555857Sbostic i = choose_segments(fsp, segs, cost_func); 20655857Sbostic #ifdef VERBOSE 20755857Sbostic printf("clean_fs: cleaning %d segments in file system %s\n", 20855857Sbostic i, fsp->fi_statfsp->f_mntonname); 20955857Sbostic fflush(stdout); 21055857Sbostic #endif 21155857Sbostic if (i) 21255857Sbostic for (i = MIN(i, NUM_TO_CLEAN(fsp)), sp = segs; i-- ; ++sp) 21355857Sbostic if (clean_segment(fsp, sp->sl_id) < 0) 21455857Sbostic perror("clean_segment failed"); 21555857Sbostic else if (lfs_segclean (fsp->fi_statfsp->f_fsid, 21655857Sbostic sp->sl_id) < 0) 21755857Sbostic perror("lfs_segclean failed"); 21855857Sbostic free(segs); 21955857Sbostic } 22055857Sbostic 22155857Sbostic /* 22255857Sbostic * Segment with the highest priority get sorted to the beginning of the 22355857Sbostic * list. This sort assumes that empty segments always have a higher 22455857Sbostic * cost/benefit than any utilized segment. 22555857Sbostic */ 22655857Sbostic int 22755857Sbostic cost_compare(a, b) 22855857Sbostic const void *a; 22955857Sbostic const void *b; 23055857Sbostic { 23155857Sbostic return (((struct seglist *)b)->sl_cost - 23255857Sbostic ((struct seglist *)a)->sl_cost); 23355857Sbostic } 23455857Sbostic 23555857Sbostic 23655857Sbostic /* 23755857Sbostic * Returns the number of segments to be cleaned with the elements of seglist 23855857Sbostic * filled in. 23955857Sbostic */ 24055857Sbostic int 24155857Sbostic choose_segments(fsp, seglist, cost_func) 24255857Sbostic FS_INFO *fsp; 24355857Sbostic struct seglist *seglist; 24455857Sbostic int (*cost_func) __P((FS_INFO *, SEGUSE *)); 24555857Sbostic { 24655857Sbostic struct lfs *lfsp; 24755857Sbostic struct seglist *sp; 24855857Sbostic SEGUSE *sup; 24955857Sbostic int i, nsegs; 25055857Sbostic 25155857Sbostic lfsp = &fsp->fi_lfs; 25255857Sbostic 25355857Sbostic #ifdef VERBOSE 25455857Sbostic (void) printf("Entering choose_segments\n"); 25555857Sbostic #endif 25655857Sbostic dump_super(lfsp); 25755857Sbostic dump_cleaner_info(fsp->fi_cip); 25855857Sbostic 25955857Sbostic for (sp = seglist, i = 0; i < lfsp->lfs_nseg; ++i) { 26055857Sbostic sup = SEGUSE_ENTRY(lfsp, fsp->fi_segusep, i); 26155857Sbostic PRINT_SEGUSE(sup, i); 26255857Sbostic if (!(sup->su_flags & SEGUSE_DIRTY) || 26355857Sbostic sup->su_flags & SEGUSE_ACTIVE) 26455857Sbostic continue; 26555857Sbostic #ifdef VERBOSE 26655857Sbostic (void) printf("\tchoosing segment %d\n", i); 26755857Sbostic #endif 26855857Sbostic sp->sl_cost = (*cost_func)(fsp, sup); 26955857Sbostic sp->sl_id = i; 27055857Sbostic sp->sl_empty = sup->su_nbytes ? 0 : 1; 27155857Sbostic ++sp; 27255857Sbostic } 27355857Sbostic nsegs = sp - seglist; 27455857Sbostic qsort(seglist, nsegs, sizeof(struct seglist), cost_compare); 27555857Sbostic #ifdef VERBOSE 27655857Sbostic (void)printf("Returning %d segments\n", nsegs); 27755857Sbostic #endif 27855857Sbostic return (nsegs); 27955857Sbostic } 28055857Sbostic 28155857Sbostic 28255857Sbostic int 28355857Sbostic clean_segment(fsp, id) 28455857Sbostic FS_INFO *fsp; /* file system information */ 28555857Sbostic int id; /* segment number */ 28655857Sbostic { 28755857Sbostic BLOCK_INFO *block_array; 28855857Sbostic INODE_INFO *inode_array; 28955857Sbostic SEGUSE *sp; 29055857Sbostic struct lfs *lfsp; 29155857Sbostic struct tossstruct t; 29255857Sbostic caddr_t seg_buf; 29355857Sbostic int num_inodes, num_blocks; 29455857Sbostic 29555857Sbostic lfsp = &fsp->fi_lfs; 29655857Sbostic sp = SEGUSE_ENTRY(lfsp, fsp->fi_segusep, id); 29755857Sbostic 29855857Sbostic #ifdef VERBOSE 29955857Sbostic (void) printf("cleaning segment %d: contains %lu bytes\n", id, 30055857Sbostic sp->su_nbytes); 30155857Sbostic fflush(stdout); 30255857Sbostic #endif 30355857Sbostic /* XXX could add debugging to verify that segment is really empty */ 30455857Sbostic if (sp->su_nbytes == sp->su_nsums * LFS_SUMMARY_SIZE) 30555857Sbostic return (0); 30655857Sbostic 30755857Sbostic /* map the segment into a buffer */ 30855857Sbostic if (mmap_segment(fsp, id, &seg_buf) < 0) { 30955857Sbostic err(0, "mmap_segment failed"); 31055857Sbostic return (-1); 31155857Sbostic } 31255857Sbostic /* get a list of blocks that are contained by the segment */ 31355857Sbostic if (lfs_segmapv(fsp, id, seg_buf, &block_array, &num_blocks, 31455857Sbostic &inode_array, &num_inodes) < 0) { 31555857Sbostic err(0, "clean_segment: lfs_segmapv failed"); 31655857Sbostic return (-1); 31755857Sbostic } 31855857Sbostic 31955857Sbostic #ifdef VERBOSE 32055857Sbostic (void) printf("lfs_segmapv returned %d blocks and %d inodes\n", 32155857Sbostic num_blocks, num_inodes); 32255857Sbostic fflush (stdout); 32355857Sbostic #endif 32455857Sbostic 32555857Sbostic /* get the current disk address of blocks contained by the segment */ 32655857Sbostic if (lfs_bmapv(fsp->fi_statfsp->f_fsid, block_array, num_blocks) < 0) { 32755857Sbostic perror("clean_segment: lfs_bmapv failed\n"); 32855857Sbostic return -1; 32955857Sbostic } 33055857Sbostic 33155857Sbostic /* Now toss any blocks not in the current segment */ 33255857Sbostic t.lfs = lfsp; 33355857Sbostic t.seg = id; 33455857Sbostic toss(block_array, &num_blocks, sizeof(BLOCK_INFO), bi_tossold, &t); 33555857Sbostic 33655857Sbostic /* Check if last element should be tossed */ 33755857Sbostic if (num_blocks && bi_tossold(&t, block_array + num_blocks - 1, NULL)) 33855857Sbostic --num_blocks; 33955857Sbostic 34055857Sbostic #ifdef VERBOSE 34155857Sbostic { 34255857Sbostic BLOCK_INFO *_bip; 34355857Sbostic INODE_INFO *_iip; 34455857Sbostic u_long *lp; 34555857Sbostic int i; 34655857Sbostic 34755857Sbostic (void) printf("after bmapv still have %d blocks\n", num_blocks); 34855857Sbostic fflush (stdout); 34955857Sbostic if (num_blocks) 35055857Sbostic printf("BLOCK INFOS\n"); 35155857Sbostic for (_bip = block_array, i=0; i < num_blocks; ++_bip, ++i) { 35255857Sbostic PRINT_BINFO(_bip); 35355857Sbostic lp = (u_long *)_bip->bi_bp; 35455857Sbostic } 35555857Sbostic if (num_inodes) 35655857Sbostic printf("INODE INFOS\n"); 35755857Sbostic for (_iip = inode_array, i=0; i < num_inodes; ++_iip, ++i) 35855857Sbostic PRINT_IINFO(1, _iip); 35955857Sbostic } 36055857Sbostic #endif 36155857Sbostic /* rewrite the live data */ 36255857Sbostic if (num_blocks > 0 || num_inodes > 0) 36355857Sbostic if (lfs_markv(fsp->fi_statfsp->f_fsid, block_array, num_blocks, 36455857Sbostic inode_array, num_inodes) < 0) { 36555857Sbostic err(0, "clean_segment: lfs_bmapv failed"); 36655857Sbostic return (-1); 36755857Sbostic } 36855857Sbostic free(block_array); 36955857Sbostic free(inode_array); 37055857Sbostic munmap_segment(fsp, seg_buf); 37155857Sbostic 37255857Sbostic return (0); 37355857Sbostic } 37455857Sbostic 37555857Sbostic 37655857Sbostic int 37755857Sbostic bi_tossold(client, a, b) 37855857Sbostic const void *client; 37955857Sbostic const void *a; 38055857Sbostic const void *b; 38155857Sbostic { 38255857Sbostic const struct tossstruct *t; 38355857Sbostic 38455857Sbostic t = (struct tossstruct *)client; 38555857Sbostic 38655857Sbostic return (((BLOCK_INFO *)a)->bi_daddr == LFS_UNUSED_DADDR || 38755857Sbostic datosn(t->lfs, ((BLOCK_INFO *)a)->bi_daddr) != t->seg); 38855857Sbostic } 389