155860Sbostic /*- 255860Sbostic * Copyright (c) 1992 The Regents of the University of California. 355860Sbostic * All rights reserved. 455860Sbostic * 555860Sbostic * %sccs.include.redist.c% 655860Sbostic */ 755860Sbostic 855860Sbostic #ifndef lint 955860Sbostic char copyright[] = 1055860Sbostic "@(#) Copyright (c) 1992 The Regents of the University of California.\n\ 1155860Sbostic All rights reserved.\n"; 1255860Sbostic #endif /* not lint */ 1355860Sbostic 1455860Sbostic #ifndef lint 15*60096Sbostic static char sccsid[] = "@(#)cleanerd.c 5.9 (Berkeley) 05/17/93"; 1655860Sbostic #endif /* not lint */ 1755860Sbostic 1855857Sbostic #include <sys/param.h> 1955857Sbostic #include <sys/mount.h> 2055857Sbostic #include <sys/time.h> 21*60096Sbostic 2255857Sbostic #include <ufs/ufs/dinode.h> 2355857Sbostic #include <ufs/lfs/lfs.h> 24*60096Sbostic 25*60096Sbostic #include <signal.h> 2655857Sbostic #include <stdio.h> 2755857Sbostic #include <stdlib.h> 2855857Sbostic #include <unistd.h> 2955857Sbostic 3055857Sbostic #include "clean.h" 3155857Sbostic char *special = "cleanerd"; 3257200Smargo int do_small = 0; 3357200Smargo int do_mmap = 0; 3457200Smargo struct cleaner_stats { 3557200Smargo int blocks_read; 3657200Smargo int blocks_written; 3757200Smargo int segs_cleaned; 3857200Smargo int segs_empty; 3957200Smargo int segs_error; 4057200Smargo } cleaner_stats; 4155857Sbostic 4255857Sbostic struct seglist { 4355857Sbostic int sl_id; /* segment number */ 4455857Sbostic int sl_cost; /* cleaning cost */ 4555857Sbostic char sl_empty; /* is segment empty */ 4655857Sbostic }; 4755857Sbostic 4855857Sbostic struct tossstruct { 4955857Sbostic struct lfs *lfs; 5055857Sbostic int seg; 5155857Sbostic }; 5255857Sbostic 5355857Sbostic /* function prototypes for system calls; not sure where they should go */ 5455857Sbostic int lfs_segwait __P((fsid_t, struct timeval *)); 5555857Sbostic int lfs_segclean __P((fsid_t, u_long)); 5655857Sbostic int lfs_bmapv __P((fsid_t, BLOCK_INFO *, int)); 5755931Sbostic int lfs_markv __P((fsid_t, BLOCK_INFO *, int)); 5855857Sbostic 5955857Sbostic /* function prototypes */ 6055857Sbostic int bi_tossold __P((const void *, const void *, const void *)); 6155857Sbostic int choose_segments __P((FS_INFO *, struct seglist *, 6255857Sbostic int (*)(FS_INFO *, SEGUSE *))); 6355857Sbostic void clean_fs __P((FS_INFO *, int (*)(FS_INFO *, SEGUSE *))); 6455857Sbostic int clean_loop __P((FS_INFO *)); 6555857Sbostic int clean_segment __P((FS_INFO *, int)); 6655857Sbostic int cost_benefit __P((FS_INFO *, SEGUSE *)); 6755857Sbostic int cost_compare __P((const void *, const void *)); 6857200Smargo void sig_report __P((int)); 6955857Sbostic 7055857Sbostic /* 7155857Sbostic * Cleaning Cost Functions: 7255857Sbostic * 7355857Sbostic * These return the cost of cleaning a segment. The higher the cost value 7455857Sbostic * the better it is to clean the segment, so empty segments have the highest 7555857Sbostic * cost. (It is probably better to think of this as a priority value 7655857Sbostic * instead). 7755857Sbostic * 7855857Sbostic * This is the cost-benefit policy simulated and described in Rosenblum's 7955857Sbostic * 1991 SOSP paper. 8055857Sbostic */ 8155857Sbostic 8255857Sbostic int 8355857Sbostic cost_benefit(fsp, su) 8455857Sbostic FS_INFO *fsp; /* file system information */ 8555857Sbostic SEGUSE *su; 8655857Sbostic { 8755857Sbostic struct lfs *lfsp; 8855857Sbostic struct timeval t; 8955857Sbostic int age; 9055857Sbostic int live; 9155857Sbostic 9255857Sbostic gettimeofday(&t, NULL); 9355857Sbostic 9455857Sbostic live = su->su_nbytes; 95*60096Sbostic age = t.tv_sec < su->su_lastmod ? 0 : t.tv_sec - su->su_lastmod; 9655857Sbostic 9755857Sbostic lfsp = &fsp->fi_lfs; 9855857Sbostic if (live == 0) 9955857Sbostic return (t.tv_sec * lblkno(lfsp, seg_size(lfsp))); 10055857Sbostic else { 10155857Sbostic /* 10255857Sbostic * from lfsSegUsage.c (Mendel's code). 10355857Sbostic * priority calculation is done using INTEGER arithmetic. 10455857Sbostic * sizes are in BLOCKS (that is why we use lblkno below). 10555857Sbostic * age is in seconds. 10655857Sbostic * 10755857Sbostic * priority = ((seg_size - live) * age) / (seg_size + live) 10855857Sbostic */ 10955857Sbostic #ifdef VERBOSE 11055857Sbostic if (live < 0 || live > seg_size(lfsp)) { 11155857Sbostic err(0, "Bad segusage count: %d", live); 11255857Sbostic live = 0; 11355857Sbostic } 11455857Sbostic #endif 11555857Sbostic return (lblkno(lfsp, seg_size(lfsp) - live) * age) 11655857Sbostic / lblkno(lfsp, seg_size(lfsp) + live); 11755857Sbostic } 11855857Sbostic } 11955857Sbostic 12055860Sbostic int 12155857Sbostic main(argc, argv) 12255857Sbostic int argc; 12355857Sbostic char *argv[]; 12455857Sbostic { 12555857Sbostic FS_INFO *lfp, *fsp; 12655857Sbostic struct statfs *lstatfsp; /* file system stats */ 12755857Sbostic struct timeval timeout; /* sleep timeout */ 12855857Sbostic fsid_t fsid; 12955857Sbostic int count; /* number of file systems */ 13056030Sbostic int i, nclean; 13157200Smargo int opt, cmd_err; 13257200Smargo 13357200Smargo cmd_err = 0; 13457200Smargo while ((opt = getopt(argc, argv, "sm")) != EOF) { 13557200Smargo switch (opt) { 13657200Smargo case 's': /* small writes */ 13757200Smargo do_small = 1; 13857200Smargo break; 13957200Smargo case 'm': 14057200Smargo do_mmap = 1; 14157200Smargo break; 14257200Smargo default: 14357200Smargo ++cmd_err; 14457200Smargo } 14557200Smargo } 14657200Smargo if (cmd_err) 14757200Smargo err(1, "usage: lfs_cleanerd [-su]"); 14857200Smargo 14957200Smargo signal (SIGINT, sig_report); 15057200Smargo signal (SIGUSR1, sig_report); 15157200Smargo signal (SIGUSR2, sig_report); 15255857Sbostic count = fs_getmntinfo(&lstatfsp, MOUNT_LFS); 15355857Sbostic 15455857Sbostic timeout.tv_sec = 5*60; /* five minutes */ 15555857Sbostic timeout.tv_usec = 0; 15655857Sbostic fsid.val[0] = 0; 15755857Sbostic fsid.val[1] = 0; 15855857Sbostic 15957200Smargo for (fsp = get_fs_info(lstatfsp, count, do_mmap); ; 16057200Smargo reread_fs_info(fsp, count, do_mmap)) { 16156030Sbostic for (nclean = 0, lfp = fsp, i = 0; i < count; ++lfp, ++i) 16256030Sbostic nclean += clean_loop(lfp); 16356030Sbostic /* 16456030Sbostic * If some file systems were actually cleaned, run again 16556030Sbostic * to make sure that some nasty process hasn't just 16656030Sbostic * filled the disk system up. 16756030Sbostic */ 16856030Sbostic if (nclean) 16956030Sbostic continue; 17055857Sbostic 17155857Sbostic #ifdef VERBOSE 17255857Sbostic (void)printf("Cleaner going to sleep.\n"); 17355857Sbostic #endif 17455857Sbostic if (lfs_segwait(fsid, &timeout) < 0) 17555857Sbostic err(0, "lfs_segwait: returned error\n"); 17655857Sbostic #ifdef VERBOSE 17755857Sbostic (void)printf("Cleaner waking up.\n"); 17855857Sbostic #endif 17955857Sbostic } 18055857Sbostic } 18155857Sbostic 18255857Sbostic /* return the number of segments cleaned */ 18355857Sbostic int 18455857Sbostic clean_loop(fsp) 18555857Sbostic FS_INFO *fsp; /* file system information */ 18655857Sbostic { 18755857Sbostic double loadavg[MAXLOADS]; 18855857Sbostic time_t now; 18955857Sbostic u_long max_free_segs; 19055857Sbostic 19155857Sbostic /* 19255857Sbostic * Compute the maximum possible number of free segments, given the 19355857Sbostic * number of free blocks. 19455857Sbostic */ 19555857Sbostic max_free_segs = fsp->fi_statfsp->f_bfree / fsp->fi_lfs.lfs_ssize; 19655857Sbostic 19755857Sbostic /* 19855857Sbostic * We will clean if there are not enough free blocks or total clean 19955857Sbostic * space is less than BUSY_LIM % of possible clean space. 20055857Sbostic */ 20155857Sbostic now = time((time_t *)NULL); 20256181Smargo if (fsp->fi_cip->clean < max_free_segs && 20356181Smargo (fsp->fi_cip->clean <= MIN_SEGS(&fsp->fi_lfs) || 20456181Smargo fsp->fi_cip->clean < max_free_segs * BUSY_LIM)) { 20556656Sbostic printf("Cleaner Running at %s (%d of %d segments available)\n", 20656656Sbostic ctime(&now), fsp->fi_cip->clean, max_free_segs); 20756030Sbostic clean_fs(fsp, cost_benefit); 20855857Sbostic return (1); 20955857Sbostic } else { 21055857Sbostic /* 21155857Sbostic * We will also clean if the system is reasonably idle and 21255857Sbostic * the total clean space is less then IDLE_LIM % of possible 21355857Sbostic * clean space. 21455857Sbostic */ 21555857Sbostic if (getloadavg(loadavg, MAXLOADS) == -1) { 21655857Sbostic perror("getloadavg: failed\n"); 21755857Sbostic return (-1); 21855857Sbostic } 21955857Sbostic if (loadavg[ONE_MIN] == 0.0 && loadavg[FIVE_MIN] && 22055857Sbostic fsp->fi_cip->clean < max_free_segs * IDLE_LIM) { 22155857Sbostic clean_fs(fsp, cost_benefit); 22255857Sbostic printf("Cleaner Running at %s (system idle)\n", 22355857Sbostic ctime(&now)); 22455857Sbostic return (1); 22555857Sbostic } 22655857Sbostic } 22755857Sbostic printf("Cleaner Not Running at %s\n", ctime(&now)); 22855857Sbostic return (0); 22955857Sbostic } 23055857Sbostic 23155857Sbostic 23255857Sbostic void 23355857Sbostic clean_fs(fsp, cost_func) 23455857Sbostic FS_INFO *fsp; /* file system information */ 23555857Sbostic int (*cost_func) __P((FS_INFO *, SEGUSE *)); 23655857Sbostic { 23755857Sbostic struct seglist *segs, *sp; 23855857Sbostic int i; 23955857Sbostic 24055857Sbostic if ((segs = malloc(fsp->fi_lfs.lfs_nseg * sizeof(struct seglist))) 24155857Sbostic == NULL) { 24255857Sbostic err(0, "malloc failed"); 24355857Sbostic return; 24455857Sbostic } 24555857Sbostic i = choose_segments(fsp, segs, cost_func); 24655857Sbostic #ifdef VERBOSE 24756051Sbostic printf("clean_fs: found %d segments to clean in file system %s\n", 24855857Sbostic i, fsp->fi_statfsp->f_mntonname); 24955857Sbostic fflush(stdout); 25055857Sbostic #endif 25155857Sbostic if (i) 25256051Sbostic for (i = MIN(i, NUM_TO_CLEAN(fsp)), sp = segs; i-- ; ++sp) { 25355857Sbostic if (clean_segment(fsp, sp->sl_id) < 0) 25455857Sbostic perror("clean_segment failed"); 25555857Sbostic else if (lfs_segclean (fsp->fi_statfsp->f_fsid, 25655857Sbostic sp->sl_id) < 0) 25755857Sbostic perror("lfs_segclean failed"); 25856051Sbostic printf("Completed cleaning segment %d\n", sp->sl_id); 25956051Sbostic } 26055857Sbostic free(segs); 26155857Sbostic } 26255857Sbostic 26355857Sbostic /* 26455857Sbostic * Segment with the highest priority get sorted to the beginning of the 26555857Sbostic * list. This sort assumes that empty segments always have a higher 26655857Sbostic * cost/benefit than any utilized segment. 26755857Sbostic */ 26855857Sbostic int 26955857Sbostic cost_compare(a, b) 27055857Sbostic const void *a; 27155857Sbostic const void *b; 27255857Sbostic { 27355857Sbostic return (((struct seglist *)b)->sl_cost - 27455857Sbostic ((struct seglist *)a)->sl_cost); 27555857Sbostic } 27655857Sbostic 27755857Sbostic 27855857Sbostic /* 27955857Sbostic * Returns the number of segments to be cleaned with the elements of seglist 28055857Sbostic * filled in. 28155857Sbostic */ 28255857Sbostic int 28355857Sbostic choose_segments(fsp, seglist, cost_func) 28455857Sbostic FS_INFO *fsp; 28555857Sbostic struct seglist *seglist; 28655857Sbostic int (*cost_func) __P((FS_INFO *, SEGUSE *)); 28755857Sbostic { 28855857Sbostic struct lfs *lfsp; 28955857Sbostic struct seglist *sp; 29055857Sbostic SEGUSE *sup; 29155857Sbostic int i, nsegs; 29255857Sbostic 29355857Sbostic lfsp = &fsp->fi_lfs; 29455857Sbostic 29555857Sbostic #ifdef VERBOSE 29655857Sbostic (void) printf("Entering choose_segments\n"); 29755857Sbostic #endif 29855857Sbostic dump_super(lfsp); 29955857Sbostic dump_cleaner_info(fsp->fi_cip); 30055857Sbostic 30155857Sbostic for (sp = seglist, i = 0; i < lfsp->lfs_nseg; ++i) { 30255857Sbostic sup = SEGUSE_ENTRY(lfsp, fsp->fi_segusep, i); 30355857Sbostic PRINT_SEGUSE(sup, i); 30455857Sbostic if (!(sup->su_flags & SEGUSE_DIRTY) || 30555857Sbostic sup->su_flags & SEGUSE_ACTIVE) 30655857Sbostic continue; 30755857Sbostic #ifdef VERBOSE 30855857Sbostic (void) printf("\tchoosing segment %d\n", i); 30955857Sbostic #endif 31055857Sbostic sp->sl_cost = (*cost_func)(fsp, sup); 31155857Sbostic sp->sl_id = i; 31255857Sbostic sp->sl_empty = sup->su_nbytes ? 0 : 1; 31355857Sbostic ++sp; 31455857Sbostic } 31555857Sbostic nsegs = sp - seglist; 31655857Sbostic qsort(seglist, nsegs, sizeof(struct seglist), cost_compare); 31755857Sbostic #ifdef VERBOSE 31855857Sbostic (void)printf("Returning %d segments\n", nsegs); 31955857Sbostic #endif 32055857Sbostic return (nsegs); 32155857Sbostic } 32255857Sbostic 32355857Sbostic 32455857Sbostic int 32555857Sbostic clean_segment(fsp, id) 32655857Sbostic FS_INFO *fsp; /* file system information */ 32755857Sbostic int id; /* segment number */ 32855857Sbostic { 32957200Smargo BLOCK_INFO *block_array, *bp; 33055857Sbostic SEGUSE *sp; 33155857Sbostic struct lfs *lfsp; 33255857Sbostic struct tossstruct t; 33355857Sbostic caddr_t seg_buf; 33457200Smargo int num_blocks, maxblocks, clean_blocks; 33555857Sbostic 33655857Sbostic lfsp = &fsp->fi_lfs; 33755857Sbostic sp = SEGUSE_ENTRY(lfsp, fsp->fi_segusep, id); 33855857Sbostic 33955857Sbostic #ifdef VERBOSE 34055857Sbostic (void) printf("cleaning segment %d: contains %lu bytes\n", id, 34155857Sbostic sp->su_nbytes); 34255857Sbostic fflush(stdout); 34355857Sbostic #endif 34455857Sbostic /* XXX could add debugging to verify that segment is really empty */ 34557200Smargo if (sp->su_nbytes == sp->su_nsums * LFS_SUMMARY_SIZE) { 34657200Smargo ++cleaner_stats.segs_empty; 34755857Sbostic return (0); 34857200Smargo } 34955857Sbostic 35055857Sbostic /* map the segment into a buffer */ 35157200Smargo if (mmap_segment(fsp, id, &seg_buf, do_mmap) < 0) { 35255857Sbostic err(0, "mmap_segment failed"); 35357200Smargo ++cleaner_stats.segs_error; 35455857Sbostic return (-1); 35555857Sbostic } 35655857Sbostic /* get a list of blocks that are contained by the segment */ 35755931Sbostic if (lfs_segmapv(fsp, id, seg_buf, &block_array, &num_blocks) < 0) { 35855857Sbostic err(0, "clean_segment: lfs_segmapv failed"); 35957200Smargo ++cleaner_stats.segs_error; 36055857Sbostic return (-1); 36155857Sbostic } 36257200Smargo cleaner_stats.blocks_read += fsp->fi_lfs.lfs_ssize; 36355857Sbostic 36455857Sbostic #ifdef VERBOSE 36555931Sbostic (void) printf("lfs_segmapv returned %d blocks\n", num_blocks); 36655857Sbostic fflush (stdout); 36755857Sbostic #endif 36855857Sbostic 36955857Sbostic /* get the current disk address of blocks contained by the segment */ 37055857Sbostic if (lfs_bmapv(fsp->fi_statfsp->f_fsid, block_array, num_blocks) < 0) { 37155857Sbostic perror("clean_segment: lfs_bmapv failed\n"); 37257200Smargo ++cleaner_stats.segs_error; 37355857Sbostic return -1; 37455857Sbostic } 37555857Sbostic 37655857Sbostic /* Now toss any blocks not in the current segment */ 37755857Sbostic t.lfs = lfsp; 37855857Sbostic t.seg = id; 37955857Sbostic toss(block_array, &num_blocks, sizeof(BLOCK_INFO), bi_tossold, &t); 38055857Sbostic 38155857Sbostic /* Check if last element should be tossed */ 38255857Sbostic if (num_blocks && bi_tossold(&t, block_array + num_blocks - 1, NULL)) 38355857Sbostic --num_blocks; 38455857Sbostic 38555857Sbostic #ifdef VERBOSE 38655857Sbostic { 38755857Sbostic BLOCK_INFO *_bip; 38855857Sbostic u_long *lp; 38955857Sbostic int i; 39055857Sbostic 39155857Sbostic (void) printf("after bmapv still have %d blocks\n", num_blocks); 39255857Sbostic fflush (stdout); 39355857Sbostic if (num_blocks) 39455857Sbostic printf("BLOCK INFOS\n"); 39555857Sbostic for (_bip = block_array, i=0; i < num_blocks; ++_bip, ++i) { 39655857Sbostic PRINT_BINFO(_bip); 39755857Sbostic lp = (u_long *)_bip->bi_bp; 39855857Sbostic } 39955857Sbostic } 40055857Sbostic #endif 40157200Smargo cleaner_stats.blocks_written += num_blocks; 40257200Smargo if (do_small) 40357200Smargo maxblocks = MAXPHYS / fsp->fi_lfs.lfs_bsize - 1; 40457200Smargo else 40557200Smargo maxblocks = num_blocks; 40657200Smargo 40757200Smargo for (bp = block_array; num_blocks > 0; bp += clean_blocks) { 40857200Smargo clean_blocks = maxblocks < num_blocks ? maxblocks : num_blocks; 40957200Smargo if (lfs_markv(fsp->fi_statfsp->f_fsid, bp, clean_blocks) < 0 ) { 41056051Sbostic err(0, "clean_segment: lfs_markv failed"); 41157200Smargo ++cleaner_stats.segs_error; 41255857Sbostic return (-1); 41355857Sbostic } 41457200Smargo num_blocks -= clean_blocks; 41557200Smargo } 41657200Smargo 41755857Sbostic free(block_array); 41857200Smargo munmap_segment (fsp, seg_buf, do_mmap); 41957200Smargo ++cleaner_stats.segs_cleaned; 42055857Sbostic return (0); 42155857Sbostic } 42255857Sbostic 42355857Sbostic 42455857Sbostic int 42555857Sbostic bi_tossold(client, a, b) 42655857Sbostic const void *client; 42755857Sbostic const void *a; 42855857Sbostic const void *b; 42955857Sbostic { 43055857Sbostic const struct tossstruct *t; 43155857Sbostic 43255857Sbostic t = (struct tossstruct *)client; 43355857Sbostic 43455857Sbostic return (((BLOCK_INFO *)a)->bi_daddr == LFS_UNUSED_DADDR || 43555857Sbostic datosn(t->lfs, ((BLOCK_INFO *)a)->bi_daddr) != t->seg); 43655857Sbostic } 43757200Smargo 43857200Smargo void 43957200Smargo sig_report(sig) 44057200Smargo int sig; 44157200Smargo { 44257200Smargo 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", 44357200Smargo "blocks_read ", cleaner_stats.blocks_read, 44457200Smargo "blocks_written ", cleaner_stats.blocks_written, 44557200Smargo "segs_cleaned ", cleaner_stats.segs_cleaned, 44657200Smargo "segs_empty ", cleaner_stats.segs_empty, 44757200Smargo "seg_error ", cleaner_stats.segs_error); 44857200Smargo if (sig == SIGUSR2) { 449*60096Sbostic cleaner_stats.blocks_read = 0; 450*60096Sbostic cleaner_stats.blocks_written = 0; 451*60096Sbostic cleaner_stats.segs_cleaned = 0; 452*60096Sbostic cleaner_stats.segs_empty = 0; 453*60096Sbostic cleaner_stats.segs_error = 0; 45457200Smargo } 45557200Smargo if (sig == SIGINT) 45657200Smargo exit(0); 45757200Smargo } 458