155860Sbostic /*- 261433Sbostic * Copyright (c) 1992, 1993 361433Sbostic * The Regents of the University of California. All rights reserved. 455860Sbostic * 555860Sbostic * %sccs.include.redist.c% 655860Sbostic */ 755860Sbostic 855860Sbostic #ifndef lint 961433Sbostic static char copyright[] = 1061433Sbostic "@(#) Copyright (c) 1992, 1993\n\ 1161433Sbostic The Regents of the University of California. All rights reserved.\n"; 1255860Sbostic #endif /* not lint */ 1355860Sbostic 1455860Sbostic #ifndef lint 15*69256Smckusick static char sccsid[] = "@(#)cleanerd.c 8.3 (Berkeley) 05/04/95"; 1655860Sbostic #endif /* not lint */ 1755860Sbostic 1855857Sbostic #include <sys/param.h> 1955857Sbostic #include <sys/mount.h> 2055857Sbostic #include <sys/time.h> 2160096Sbostic 2255857Sbostic #include <ufs/ufs/dinode.h> 2355857Sbostic #include <ufs/lfs/lfs.h> 2460096Sbostic 2560096Sbostic #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 */ 5465737Sbostic int lfs_segwait __P((fsid_t *, struct timeval *)); 5565737Sbostic int lfs_segclean __P((fsid_t *, u_long)); 5665737Sbostic int lfs_bmapv __P((fsid_t *, BLOCK_INFO *, int)); 5765737Sbostic 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; 9560096Sbostic age = t.tv_sec < su->su_lastmod ? 0 : t.tv_sec - su->su_lastmod; 9655857Sbostic 9755857Sbostic lfsp = &fsp->fi_lfs; 9865737Sbostic 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 * 10765737Sbostic * 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 { 12561371Scgd FS_INFO *fsp; 12655857Sbostic struct statfs *lstatfsp; /* file system stats */ 12755857Sbostic struct timeval timeout; /* sleep timeout */ 12855857Sbostic fsid_t fsid; 12961417Scgd int i, nodaemon; 13057200Smargo int opt, cmd_err; 13161371Scgd char *fs_name; /* name of filesystem to clean */ 13261371Scgd extern int optind; 13357200Smargo 13461417Scgd cmd_err = nodaemon = 0; 13561417Scgd while ((opt = getopt(argc, argv, "smd")) != EOF) { 13657200Smargo switch (opt) { 13757200Smargo case 's': /* small writes */ 13857200Smargo do_small = 1; 13957200Smargo break; 14057200Smargo case 'm': 14157200Smargo do_mmap = 1; 14257200Smargo break; 14361417Scgd case 'd': 14461417Scgd nodaemon = 1; 14561417Scgd break; 14657200Smargo default: 14757200Smargo ++cmd_err; 14857200Smargo } 14957200Smargo } 15061371Scgd argc -= optind; 15161371Scgd argv += optind; 15261371Scgd if (cmd_err || (argc != 1)) 15361417Scgd err(1, "usage: lfs_cleanerd [-smd] fs_name"); 15457200Smargo 15561371Scgd fs_name = argv[0]; 15661371Scgd 15765737Sbostic signal(SIGINT, sig_report); 15865737Sbostic signal(SIGUSR1, sig_report); 15965737Sbostic signal(SIGUSR2, sig_report); 160*69256Smckusick if (fs_getmntinfo(&lstatfsp, fs_name, "lfs") == 0) { 16161371Scgd /* didn't find the filesystem */ 16261371Scgd err(1, "lfs_cleanerd: filesystem %s isn't an LFS!", fs_name); 16361371Scgd } 16455857Sbostic 16561417Scgd if (!nodaemon) /* should we become a daemon, chdir to / & close fd's */ 16661417Scgd if (daemon(0, 0) == -1) 16761417Scgd err(1, "lfs_cleanerd: couldn't become a daemon!"); 16861417Scgd 16955857Sbostic timeout.tv_sec = 5*60; /* five minutes */ 17055857Sbostic timeout.tv_usec = 0; 17155857Sbostic fsid.val[0] = 0; 17255857Sbostic fsid.val[1] = 0; 17355857Sbostic 17461371Scgd for (fsp = get_fs_info(lstatfsp, do_mmap); ; 17561371Scgd reread_fs_info(fsp, do_mmap)) { 17656030Sbostic /* 17761371Scgd * clean the filesystem, and, if it needed cleaning 17861371Scgd * (i.e. it returned nonzero) try it again 17956030Sbostic * to make sure that some nasty process hasn't just 18056030Sbostic * filled the disk system up. 18156030Sbostic */ 18261371Scgd if (clean_loop(fsp)) 18356030Sbostic continue; 18455857Sbostic 18555857Sbostic #ifdef VERBOSE 18655857Sbostic (void)printf("Cleaner going to sleep.\n"); 18755857Sbostic #endif 18865737Sbostic if (lfs_segwait(&fsid, &timeout) < 0) 18955857Sbostic err(0, "lfs_segwait: returned error\n"); 19055857Sbostic #ifdef VERBOSE 19155857Sbostic (void)printf("Cleaner waking up.\n"); 19255857Sbostic #endif 19355857Sbostic } 19455857Sbostic } 19555857Sbostic 19655857Sbostic /* return the number of segments cleaned */ 19755857Sbostic int 19855857Sbostic clean_loop(fsp) 19955857Sbostic FS_INFO *fsp; /* file system information */ 20055857Sbostic { 20155857Sbostic double loadavg[MAXLOADS]; 20255857Sbostic time_t now; 20355857Sbostic u_long max_free_segs; 20455857Sbostic 20555857Sbostic /* 20655857Sbostic * Compute the maximum possible number of free segments, given the 20755857Sbostic * number of free blocks. 20855857Sbostic */ 20955857Sbostic max_free_segs = fsp->fi_statfsp->f_bfree / fsp->fi_lfs.lfs_ssize; 21055857Sbostic 21155857Sbostic /* 21255857Sbostic * We will clean if there are not enough free blocks or total clean 21355857Sbostic * space is less than BUSY_LIM % of possible clean space. 21455857Sbostic */ 21555857Sbostic now = time((time_t *)NULL); 21656181Smargo if (fsp->fi_cip->clean < max_free_segs && 21756181Smargo (fsp->fi_cip->clean <= MIN_SEGS(&fsp->fi_lfs) || 21856181Smargo fsp->fi_cip->clean < max_free_segs * BUSY_LIM)) { 21956656Sbostic printf("Cleaner Running at %s (%d of %d segments available)\n", 22056656Sbostic ctime(&now), fsp->fi_cip->clean, max_free_segs); 22156030Sbostic clean_fs(fsp, cost_benefit); 22255857Sbostic return (1); 22355857Sbostic } else { 22455857Sbostic /* 22555857Sbostic * We will also clean if the system is reasonably idle and 22655857Sbostic * the total clean space is less then IDLE_LIM % of possible 22755857Sbostic * clean space. 22855857Sbostic */ 22955857Sbostic if (getloadavg(loadavg, MAXLOADS) == -1) { 23055857Sbostic perror("getloadavg: failed\n"); 23155857Sbostic return (-1); 23255857Sbostic } 23355857Sbostic if (loadavg[ONE_MIN] == 0.0 && loadavg[FIVE_MIN] && 23455857Sbostic fsp->fi_cip->clean < max_free_segs * IDLE_LIM) { 23555857Sbostic clean_fs(fsp, cost_benefit); 23655857Sbostic printf("Cleaner Running at %s (system idle)\n", 23755857Sbostic ctime(&now)); 23855857Sbostic return (1); 23955857Sbostic } 24055857Sbostic } 24155857Sbostic printf("Cleaner Not Running at %s\n", ctime(&now)); 24255857Sbostic return (0); 24355857Sbostic } 24455857Sbostic 24555857Sbostic 24655857Sbostic void 24755857Sbostic clean_fs(fsp, cost_func) 24855857Sbostic FS_INFO *fsp; /* file system information */ 24955857Sbostic int (*cost_func) __P((FS_INFO *, SEGUSE *)); 25055857Sbostic { 25155857Sbostic struct seglist *segs, *sp; 25255857Sbostic int i; 25355857Sbostic 25465737Sbostic if ((segs = 25565737Sbostic malloc(fsp->fi_lfs.lfs_nseg * sizeof(struct seglist))) == NULL) { 25655857Sbostic err(0, "malloc failed"); 25755857Sbostic return; 25855857Sbostic } 25955857Sbostic i = choose_segments(fsp, segs, cost_func); 26055857Sbostic #ifdef VERBOSE 26156051Sbostic printf("clean_fs: found %d segments to clean in file system %s\n", 26255857Sbostic i, fsp->fi_statfsp->f_mntonname); 26355857Sbostic fflush(stdout); 26455857Sbostic #endif 26555857Sbostic if (i) 26656051Sbostic for (i = MIN(i, NUM_TO_CLEAN(fsp)), sp = segs; i-- ; ++sp) { 26755857Sbostic if (clean_segment(fsp, sp->sl_id) < 0) 26855857Sbostic perror("clean_segment failed"); 26965737Sbostic else if (lfs_segclean(&fsp->fi_statfsp->f_fsid, 27055857Sbostic sp->sl_id) < 0) 27155857Sbostic perror("lfs_segclean failed"); 27256051Sbostic printf("Completed cleaning segment %d\n", sp->sl_id); 27356051Sbostic } 27455857Sbostic free(segs); 27555857Sbostic } 27655857Sbostic 27755857Sbostic /* 27855857Sbostic * Segment with the highest priority get sorted to the beginning of the 27955857Sbostic * list. This sort assumes that empty segments always have a higher 28055857Sbostic * cost/benefit than any utilized segment. 28155857Sbostic */ 28255857Sbostic int 28355857Sbostic cost_compare(a, b) 28455857Sbostic const void *a; 28555857Sbostic const void *b; 28655857Sbostic { 28755857Sbostic return (((struct seglist *)b)->sl_cost - 28855857Sbostic ((struct seglist *)a)->sl_cost); 28955857Sbostic } 29055857Sbostic 29155857Sbostic 29255857Sbostic /* 29355857Sbostic * Returns the number of segments to be cleaned with the elements of seglist 29455857Sbostic * filled in. 29555857Sbostic */ 29655857Sbostic int 29755857Sbostic choose_segments(fsp, seglist, cost_func) 29855857Sbostic FS_INFO *fsp; 29955857Sbostic struct seglist *seglist; 30055857Sbostic int (*cost_func) __P((FS_INFO *, SEGUSE *)); 30155857Sbostic { 30255857Sbostic struct lfs *lfsp; 30355857Sbostic struct seglist *sp; 30455857Sbostic SEGUSE *sup; 30555857Sbostic int i, nsegs; 30655857Sbostic 30755857Sbostic lfsp = &fsp->fi_lfs; 30855857Sbostic 30955857Sbostic #ifdef VERBOSE 31065737Sbostic (void)printf("Entering choose_segments\n"); 31155857Sbostic #endif 31255857Sbostic dump_super(lfsp); 31355857Sbostic dump_cleaner_info(fsp->fi_cip); 31455857Sbostic 31555857Sbostic for (sp = seglist, i = 0; i < lfsp->lfs_nseg; ++i) { 31655857Sbostic sup = SEGUSE_ENTRY(lfsp, fsp->fi_segusep, i); 31755857Sbostic PRINT_SEGUSE(sup, i); 31855857Sbostic if (!(sup->su_flags & SEGUSE_DIRTY) || 31955857Sbostic sup->su_flags & SEGUSE_ACTIVE) 32055857Sbostic continue; 32155857Sbostic #ifdef VERBOSE 32265737Sbostic (void)printf("\tchoosing segment %d\n", i); 32355857Sbostic #endif 32455857Sbostic sp->sl_cost = (*cost_func)(fsp, sup); 32555857Sbostic sp->sl_id = i; 32655857Sbostic sp->sl_empty = sup->su_nbytes ? 0 : 1; 32755857Sbostic ++sp; 32855857Sbostic } 32955857Sbostic nsegs = sp - seglist; 33055857Sbostic qsort(seglist, nsegs, sizeof(struct seglist), cost_compare); 33155857Sbostic #ifdef VERBOSE 33255857Sbostic (void)printf("Returning %d segments\n", nsegs); 33355857Sbostic #endif 33455857Sbostic return (nsegs); 33555857Sbostic } 33655857Sbostic 33755857Sbostic 33855857Sbostic int 33955857Sbostic clean_segment(fsp, id) 34055857Sbostic FS_INFO *fsp; /* file system information */ 34155857Sbostic int id; /* segment number */ 34255857Sbostic { 34357200Smargo BLOCK_INFO *block_array, *bp; 34455857Sbostic SEGUSE *sp; 34555857Sbostic struct lfs *lfsp; 34655857Sbostic struct tossstruct t; 34755857Sbostic caddr_t seg_buf; 34857200Smargo int num_blocks, maxblocks, clean_blocks; 34955857Sbostic 35055857Sbostic lfsp = &fsp->fi_lfs; 35155857Sbostic sp = SEGUSE_ENTRY(lfsp, fsp->fi_segusep, id); 35255857Sbostic 35355857Sbostic #ifdef VERBOSE 35465737Sbostic (void)printf("cleaning segment %d: contains %lu bytes\n", id, 35555857Sbostic sp->su_nbytes); 35655857Sbostic fflush(stdout); 35755857Sbostic #endif 35855857Sbostic /* XXX could add debugging to verify that segment is really empty */ 35957200Smargo if (sp->su_nbytes == sp->su_nsums * LFS_SUMMARY_SIZE) { 36057200Smargo ++cleaner_stats.segs_empty; 36155857Sbostic return (0); 36257200Smargo } 36355857Sbostic 36455857Sbostic /* map the segment into a buffer */ 36557200Smargo if (mmap_segment(fsp, id, &seg_buf, do_mmap) < 0) { 36655857Sbostic err(0, "mmap_segment failed"); 36757200Smargo ++cleaner_stats.segs_error; 36855857Sbostic return (-1); 36955857Sbostic } 37055857Sbostic /* get a list of blocks that are contained by the segment */ 37155931Sbostic if (lfs_segmapv(fsp, id, seg_buf, &block_array, &num_blocks) < 0) { 37255857Sbostic err(0, "clean_segment: lfs_segmapv failed"); 37357200Smargo ++cleaner_stats.segs_error; 37455857Sbostic return (-1); 37555857Sbostic } 37657200Smargo cleaner_stats.blocks_read += fsp->fi_lfs.lfs_ssize; 37755857Sbostic 37855857Sbostic #ifdef VERBOSE 37965737Sbostic (void)printf("lfs_segmapv returned %d blocks\n", num_blocks); 38065737Sbostic fflush(stdout); 38155857Sbostic #endif 38255857Sbostic 38355857Sbostic /* get the current disk address of blocks contained by the segment */ 38465737Sbostic if (lfs_bmapv(&fsp->fi_statfsp->f_fsid, block_array, num_blocks) < 0) { 38555857Sbostic perror("clean_segment: lfs_bmapv failed\n"); 38657200Smargo ++cleaner_stats.segs_error; 38755857Sbostic return -1; 38855857Sbostic } 38955857Sbostic 39055857Sbostic /* Now toss any blocks not in the current segment */ 39155857Sbostic t.lfs = lfsp; 39255857Sbostic t.seg = id; 39355857Sbostic toss(block_array, &num_blocks, sizeof(BLOCK_INFO), bi_tossold, &t); 39455857Sbostic 39555857Sbostic /* Check if last element should be tossed */ 39655857Sbostic if (num_blocks && bi_tossold(&t, block_array + num_blocks - 1, NULL)) 39755857Sbostic --num_blocks; 39855857Sbostic 39955857Sbostic #ifdef VERBOSE 40055857Sbostic { 40155857Sbostic BLOCK_INFO *_bip; 40255857Sbostic u_long *lp; 40355857Sbostic int i; 40455857Sbostic 40565737Sbostic (void)printf("after bmapv still have %d blocks\n", num_blocks); 40665737Sbostic fflush(stdout); 40755857Sbostic if (num_blocks) 40855857Sbostic printf("BLOCK INFOS\n"); 40955857Sbostic for (_bip = block_array, i=0; i < num_blocks; ++_bip, ++i) { 41055857Sbostic PRINT_BINFO(_bip); 41155857Sbostic lp = (u_long *)_bip->bi_bp; 41255857Sbostic } 41355857Sbostic } 41455857Sbostic #endif 41557200Smargo cleaner_stats.blocks_written += num_blocks; 41657200Smargo if (do_small) 41757200Smargo maxblocks = MAXPHYS / fsp->fi_lfs.lfs_bsize - 1; 41857200Smargo else 41957200Smargo maxblocks = num_blocks; 42057200Smargo 42157200Smargo for (bp = block_array; num_blocks > 0; bp += clean_blocks) { 42257200Smargo clean_blocks = maxblocks < num_blocks ? maxblocks : num_blocks; 42365737Sbostic if (lfs_markv(&fsp->fi_statfsp->f_fsid, 42465737Sbostic bp, clean_blocks) < 0) { 42556051Sbostic err(0, "clean_segment: lfs_markv failed"); 42657200Smargo ++cleaner_stats.segs_error; 42755857Sbostic return (-1); 42855857Sbostic } 42957200Smargo num_blocks -= clean_blocks; 43057200Smargo } 43157200Smargo 43255857Sbostic free(block_array); 43365737Sbostic munmap_segment(fsp, seg_buf, do_mmap); 43457200Smargo ++cleaner_stats.segs_cleaned; 43555857Sbostic return (0); 43655857Sbostic } 43755857Sbostic 43855857Sbostic 43955857Sbostic int 44055857Sbostic bi_tossold(client, a, b) 44155857Sbostic const void *client; 44255857Sbostic const void *a; 44355857Sbostic const void *b; 44455857Sbostic { 44555857Sbostic const struct tossstruct *t; 44655857Sbostic 44755857Sbostic t = (struct tossstruct *)client; 44855857Sbostic 44955857Sbostic return (((BLOCK_INFO *)a)->bi_daddr == LFS_UNUSED_DADDR || 45055857Sbostic datosn(t->lfs, ((BLOCK_INFO *)a)->bi_daddr) != t->seg); 45155857Sbostic } 45257200Smargo 45357200Smargo void 45457200Smargo sig_report(sig) 45557200Smargo int sig; 45657200Smargo { 45765737Sbostic 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", 45857200Smargo "blocks_read ", cleaner_stats.blocks_read, 45957200Smargo "blocks_written ", cleaner_stats.blocks_written, 46057200Smargo "segs_cleaned ", cleaner_stats.segs_cleaned, 46157200Smargo "segs_empty ", cleaner_stats.segs_empty, 46257200Smargo "seg_error ", cleaner_stats.segs_error); 46357200Smargo if (sig == SIGUSR2) { 46460096Sbostic cleaner_stats.blocks_read = 0; 46560096Sbostic cleaner_stats.blocks_written = 0; 46660096Sbostic cleaner_stats.segs_cleaned = 0; 46760096Sbostic cleaner_stats.segs_empty = 0; 46860096Sbostic cleaner_stats.segs_error = 0; 46957200Smargo } 47057200Smargo if (sig == SIGINT) 47157200Smargo exit(0); 47257200Smargo } 473