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*61371Scgd static char sccsid[] = "@(#)cleanerd.c 5.10 (Berkeley) 06/04/93"; 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 */ 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; 9560096Sbostic 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 { 125*61371Scgd FS_INFO *fsp; 12655857Sbostic struct statfs *lstatfsp; /* file system stats */ 12755857Sbostic struct timeval timeout; /* sleep timeout */ 12855857Sbostic fsid_t fsid; 129*61371Scgd int i; 13057200Smargo int opt, cmd_err; 131*61371Scgd char *fs_name; /* name of filesystem to clean */ 132*61371Scgd extern int optind; 13357200Smargo 13457200Smargo cmd_err = 0; 13557200Smargo while ((opt = getopt(argc, argv, "sm")) != EOF) { 13657200Smargo switch (opt) { 13757200Smargo case 's': /* small writes */ 13857200Smargo do_small = 1; 13957200Smargo break; 14057200Smargo case 'm': 14157200Smargo do_mmap = 1; 14257200Smargo break; 14357200Smargo default: 14457200Smargo ++cmd_err; 14557200Smargo } 14657200Smargo } 147*61371Scgd argc -= optind; 148*61371Scgd argv += optind; 149*61371Scgd if (cmd_err || (argc != 1)) 150*61371Scgd err(1, "usage: lfs_cleanerd [-sm] fs_name"); 15157200Smargo 152*61371Scgd fs_name = argv[0]; 153*61371Scgd 15457200Smargo signal (SIGINT, sig_report); 15557200Smargo signal (SIGUSR1, sig_report); 15657200Smargo signal (SIGUSR2, sig_report); 157*61371Scgd if (fs_getmntinfo(&lstatfsp, fs_name, MOUNT_LFS) == 0) { 158*61371Scgd /* didn't find the filesystem */ 159*61371Scgd err(1, "lfs_cleanerd: filesystem %s isn't an LFS!", fs_name); 160*61371Scgd } 16155857Sbostic 16255857Sbostic timeout.tv_sec = 5*60; /* five minutes */ 16355857Sbostic timeout.tv_usec = 0; 16455857Sbostic fsid.val[0] = 0; 16555857Sbostic fsid.val[1] = 0; 16655857Sbostic 167*61371Scgd for (fsp = get_fs_info(lstatfsp, do_mmap); ; 168*61371Scgd reread_fs_info(fsp, do_mmap)) { 16956030Sbostic /* 170*61371Scgd * clean the filesystem, and, if it needed cleaning 171*61371Scgd * (i.e. it returned nonzero) try it again 17256030Sbostic * to make sure that some nasty process hasn't just 17356030Sbostic * filled the disk system up. 17456030Sbostic */ 175*61371Scgd if (clean_loop(fsp)) 17656030Sbostic continue; 17755857Sbostic 17855857Sbostic #ifdef VERBOSE 17955857Sbostic (void)printf("Cleaner going to sleep.\n"); 18055857Sbostic #endif 18155857Sbostic if (lfs_segwait(fsid, &timeout) < 0) 18255857Sbostic err(0, "lfs_segwait: returned error\n"); 18355857Sbostic #ifdef VERBOSE 18455857Sbostic (void)printf("Cleaner waking up.\n"); 18555857Sbostic #endif 18655857Sbostic } 18755857Sbostic } 18855857Sbostic 18955857Sbostic /* return the number of segments cleaned */ 19055857Sbostic int 19155857Sbostic clean_loop(fsp) 19255857Sbostic FS_INFO *fsp; /* file system information */ 19355857Sbostic { 19455857Sbostic double loadavg[MAXLOADS]; 19555857Sbostic time_t now; 19655857Sbostic u_long max_free_segs; 19755857Sbostic 19855857Sbostic /* 19955857Sbostic * Compute the maximum possible number of free segments, given the 20055857Sbostic * number of free blocks. 20155857Sbostic */ 20255857Sbostic max_free_segs = fsp->fi_statfsp->f_bfree / fsp->fi_lfs.lfs_ssize; 20355857Sbostic 20455857Sbostic /* 20555857Sbostic * We will clean if there are not enough free blocks or total clean 20655857Sbostic * space is less than BUSY_LIM % of possible clean space. 20755857Sbostic */ 20855857Sbostic now = time((time_t *)NULL); 20956181Smargo if (fsp->fi_cip->clean < max_free_segs && 21056181Smargo (fsp->fi_cip->clean <= MIN_SEGS(&fsp->fi_lfs) || 21156181Smargo fsp->fi_cip->clean < max_free_segs * BUSY_LIM)) { 21256656Sbostic printf("Cleaner Running at %s (%d of %d segments available)\n", 21356656Sbostic ctime(&now), fsp->fi_cip->clean, max_free_segs); 21456030Sbostic clean_fs(fsp, cost_benefit); 21555857Sbostic return (1); 21655857Sbostic } else { 21755857Sbostic /* 21855857Sbostic * We will also clean if the system is reasonably idle and 21955857Sbostic * the total clean space is less then IDLE_LIM % of possible 22055857Sbostic * clean space. 22155857Sbostic */ 22255857Sbostic if (getloadavg(loadavg, MAXLOADS) == -1) { 22355857Sbostic perror("getloadavg: failed\n"); 22455857Sbostic return (-1); 22555857Sbostic } 22655857Sbostic if (loadavg[ONE_MIN] == 0.0 && loadavg[FIVE_MIN] && 22755857Sbostic fsp->fi_cip->clean < max_free_segs * IDLE_LIM) { 22855857Sbostic clean_fs(fsp, cost_benefit); 22955857Sbostic printf("Cleaner Running at %s (system idle)\n", 23055857Sbostic ctime(&now)); 23155857Sbostic return (1); 23255857Sbostic } 23355857Sbostic } 23455857Sbostic printf("Cleaner Not Running at %s\n", ctime(&now)); 23555857Sbostic return (0); 23655857Sbostic } 23755857Sbostic 23855857Sbostic 23955857Sbostic void 24055857Sbostic clean_fs(fsp, cost_func) 24155857Sbostic FS_INFO *fsp; /* file system information */ 24255857Sbostic int (*cost_func) __P((FS_INFO *, SEGUSE *)); 24355857Sbostic { 24455857Sbostic struct seglist *segs, *sp; 24555857Sbostic int i; 24655857Sbostic 24755857Sbostic if ((segs = malloc(fsp->fi_lfs.lfs_nseg * sizeof(struct seglist))) 24855857Sbostic == NULL) { 24955857Sbostic err(0, "malloc failed"); 25055857Sbostic return; 25155857Sbostic } 25255857Sbostic i = choose_segments(fsp, segs, cost_func); 25355857Sbostic #ifdef VERBOSE 25456051Sbostic printf("clean_fs: found %d segments to clean in file system %s\n", 25555857Sbostic i, fsp->fi_statfsp->f_mntonname); 25655857Sbostic fflush(stdout); 25755857Sbostic #endif 25855857Sbostic if (i) 25956051Sbostic for (i = MIN(i, NUM_TO_CLEAN(fsp)), sp = segs; i-- ; ++sp) { 26055857Sbostic if (clean_segment(fsp, sp->sl_id) < 0) 26155857Sbostic perror("clean_segment failed"); 26255857Sbostic else if (lfs_segclean (fsp->fi_statfsp->f_fsid, 26355857Sbostic sp->sl_id) < 0) 26455857Sbostic perror("lfs_segclean failed"); 26556051Sbostic printf("Completed cleaning segment %d\n", sp->sl_id); 26656051Sbostic } 26755857Sbostic free(segs); 26855857Sbostic } 26955857Sbostic 27055857Sbostic /* 27155857Sbostic * Segment with the highest priority get sorted to the beginning of the 27255857Sbostic * list. This sort assumes that empty segments always have a higher 27355857Sbostic * cost/benefit than any utilized segment. 27455857Sbostic */ 27555857Sbostic int 27655857Sbostic cost_compare(a, b) 27755857Sbostic const void *a; 27855857Sbostic const void *b; 27955857Sbostic { 28055857Sbostic return (((struct seglist *)b)->sl_cost - 28155857Sbostic ((struct seglist *)a)->sl_cost); 28255857Sbostic } 28355857Sbostic 28455857Sbostic 28555857Sbostic /* 28655857Sbostic * Returns the number of segments to be cleaned with the elements of seglist 28755857Sbostic * filled in. 28855857Sbostic */ 28955857Sbostic int 29055857Sbostic choose_segments(fsp, seglist, cost_func) 29155857Sbostic FS_INFO *fsp; 29255857Sbostic struct seglist *seglist; 29355857Sbostic int (*cost_func) __P((FS_INFO *, SEGUSE *)); 29455857Sbostic { 29555857Sbostic struct lfs *lfsp; 29655857Sbostic struct seglist *sp; 29755857Sbostic SEGUSE *sup; 29855857Sbostic int i, nsegs; 29955857Sbostic 30055857Sbostic lfsp = &fsp->fi_lfs; 30155857Sbostic 30255857Sbostic #ifdef VERBOSE 30355857Sbostic (void) printf("Entering choose_segments\n"); 30455857Sbostic #endif 30555857Sbostic dump_super(lfsp); 30655857Sbostic dump_cleaner_info(fsp->fi_cip); 30755857Sbostic 30855857Sbostic for (sp = seglist, i = 0; i < lfsp->lfs_nseg; ++i) { 30955857Sbostic sup = SEGUSE_ENTRY(lfsp, fsp->fi_segusep, i); 31055857Sbostic PRINT_SEGUSE(sup, i); 31155857Sbostic if (!(sup->su_flags & SEGUSE_DIRTY) || 31255857Sbostic sup->su_flags & SEGUSE_ACTIVE) 31355857Sbostic continue; 31455857Sbostic #ifdef VERBOSE 31555857Sbostic (void) printf("\tchoosing segment %d\n", i); 31655857Sbostic #endif 31755857Sbostic sp->sl_cost = (*cost_func)(fsp, sup); 31855857Sbostic sp->sl_id = i; 31955857Sbostic sp->sl_empty = sup->su_nbytes ? 0 : 1; 32055857Sbostic ++sp; 32155857Sbostic } 32255857Sbostic nsegs = sp - seglist; 32355857Sbostic qsort(seglist, nsegs, sizeof(struct seglist), cost_compare); 32455857Sbostic #ifdef VERBOSE 32555857Sbostic (void)printf("Returning %d segments\n", nsegs); 32655857Sbostic #endif 32755857Sbostic return (nsegs); 32855857Sbostic } 32955857Sbostic 33055857Sbostic 33155857Sbostic int 33255857Sbostic clean_segment(fsp, id) 33355857Sbostic FS_INFO *fsp; /* file system information */ 33455857Sbostic int id; /* segment number */ 33555857Sbostic { 33657200Smargo BLOCK_INFO *block_array, *bp; 33755857Sbostic SEGUSE *sp; 33855857Sbostic struct lfs *lfsp; 33955857Sbostic struct tossstruct t; 34055857Sbostic caddr_t seg_buf; 34157200Smargo int num_blocks, maxblocks, clean_blocks; 34255857Sbostic 34355857Sbostic lfsp = &fsp->fi_lfs; 34455857Sbostic sp = SEGUSE_ENTRY(lfsp, fsp->fi_segusep, id); 34555857Sbostic 34655857Sbostic #ifdef VERBOSE 34755857Sbostic (void) printf("cleaning segment %d: contains %lu bytes\n", id, 34855857Sbostic sp->su_nbytes); 34955857Sbostic fflush(stdout); 35055857Sbostic #endif 35155857Sbostic /* XXX could add debugging to verify that segment is really empty */ 35257200Smargo if (sp->su_nbytes == sp->su_nsums * LFS_SUMMARY_SIZE) { 35357200Smargo ++cleaner_stats.segs_empty; 35455857Sbostic return (0); 35557200Smargo } 35655857Sbostic 35755857Sbostic /* map the segment into a buffer */ 35857200Smargo if (mmap_segment(fsp, id, &seg_buf, do_mmap) < 0) { 35955857Sbostic err(0, "mmap_segment failed"); 36057200Smargo ++cleaner_stats.segs_error; 36155857Sbostic return (-1); 36255857Sbostic } 36355857Sbostic /* get a list of blocks that are contained by the segment */ 36455931Sbostic if (lfs_segmapv(fsp, id, seg_buf, &block_array, &num_blocks) < 0) { 36555857Sbostic err(0, "clean_segment: lfs_segmapv failed"); 36657200Smargo ++cleaner_stats.segs_error; 36755857Sbostic return (-1); 36855857Sbostic } 36957200Smargo cleaner_stats.blocks_read += fsp->fi_lfs.lfs_ssize; 37055857Sbostic 37155857Sbostic #ifdef VERBOSE 37255931Sbostic (void) printf("lfs_segmapv returned %d blocks\n", num_blocks); 37355857Sbostic fflush (stdout); 37455857Sbostic #endif 37555857Sbostic 37655857Sbostic /* get the current disk address of blocks contained by the segment */ 37755857Sbostic if (lfs_bmapv(fsp->fi_statfsp->f_fsid, block_array, num_blocks) < 0) { 37855857Sbostic perror("clean_segment: lfs_bmapv failed\n"); 37957200Smargo ++cleaner_stats.segs_error; 38055857Sbostic return -1; 38155857Sbostic } 38255857Sbostic 38355857Sbostic /* Now toss any blocks not in the current segment */ 38455857Sbostic t.lfs = lfsp; 38555857Sbostic t.seg = id; 38655857Sbostic toss(block_array, &num_blocks, sizeof(BLOCK_INFO), bi_tossold, &t); 38755857Sbostic 38855857Sbostic /* Check if last element should be tossed */ 38955857Sbostic if (num_blocks && bi_tossold(&t, block_array + num_blocks - 1, NULL)) 39055857Sbostic --num_blocks; 39155857Sbostic 39255857Sbostic #ifdef VERBOSE 39355857Sbostic { 39455857Sbostic BLOCK_INFO *_bip; 39555857Sbostic u_long *lp; 39655857Sbostic int i; 39755857Sbostic 39855857Sbostic (void) printf("after bmapv still have %d blocks\n", num_blocks); 39955857Sbostic fflush (stdout); 40055857Sbostic if (num_blocks) 40155857Sbostic printf("BLOCK INFOS\n"); 40255857Sbostic for (_bip = block_array, i=0; i < num_blocks; ++_bip, ++i) { 40355857Sbostic PRINT_BINFO(_bip); 40455857Sbostic lp = (u_long *)_bip->bi_bp; 40555857Sbostic } 40655857Sbostic } 40755857Sbostic #endif 40857200Smargo cleaner_stats.blocks_written += num_blocks; 40957200Smargo if (do_small) 41057200Smargo maxblocks = MAXPHYS / fsp->fi_lfs.lfs_bsize - 1; 41157200Smargo else 41257200Smargo maxblocks = num_blocks; 41357200Smargo 41457200Smargo for (bp = block_array; num_blocks > 0; bp += clean_blocks) { 41557200Smargo clean_blocks = maxblocks < num_blocks ? maxblocks : num_blocks; 41657200Smargo if (lfs_markv(fsp->fi_statfsp->f_fsid, bp, clean_blocks) < 0 ) { 41756051Sbostic err(0, "clean_segment: lfs_markv failed"); 41857200Smargo ++cleaner_stats.segs_error; 41955857Sbostic return (-1); 42055857Sbostic } 42157200Smargo num_blocks -= clean_blocks; 42257200Smargo } 42357200Smargo 42455857Sbostic free(block_array); 42557200Smargo munmap_segment (fsp, seg_buf, do_mmap); 42657200Smargo ++cleaner_stats.segs_cleaned; 42755857Sbostic return (0); 42855857Sbostic } 42955857Sbostic 43055857Sbostic 43155857Sbostic int 43255857Sbostic bi_tossold(client, a, b) 43355857Sbostic const void *client; 43455857Sbostic const void *a; 43555857Sbostic const void *b; 43655857Sbostic { 43755857Sbostic const struct tossstruct *t; 43855857Sbostic 43955857Sbostic t = (struct tossstruct *)client; 44055857Sbostic 44155857Sbostic return (((BLOCK_INFO *)a)->bi_daddr == LFS_UNUSED_DADDR || 44255857Sbostic datosn(t->lfs, ((BLOCK_INFO *)a)->bi_daddr) != t->seg); 44355857Sbostic } 44457200Smargo 44557200Smargo void 44657200Smargo sig_report(sig) 44757200Smargo int sig; 44857200Smargo { 44957200Smargo 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", 45057200Smargo "blocks_read ", cleaner_stats.blocks_read, 45157200Smargo "blocks_written ", cleaner_stats.blocks_written, 45257200Smargo "segs_cleaned ", cleaner_stats.segs_cleaned, 45357200Smargo "segs_empty ", cleaner_stats.segs_empty, 45457200Smargo "seg_error ", cleaner_stats.segs_error); 45557200Smargo if (sig == SIGUSR2) { 45660096Sbostic cleaner_stats.blocks_read = 0; 45760096Sbostic cleaner_stats.blocks_written = 0; 45860096Sbostic cleaner_stats.segs_cleaned = 0; 45960096Sbostic cleaner_stats.segs_empty = 0; 46060096Sbostic cleaner_stats.segs_error = 0; 46157200Smargo } 46257200Smargo if (sig == SIGINT) 46357200Smargo exit(0); 46457200Smargo } 465