diff options
author | rgrimes <rgrimes@FreeBSD.org> | 1994-05-27 12:39:25 +0000 |
---|---|---|
committer | rgrimes <rgrimes@FreeBSD.org> | 1994-05-27 12:39:25 +0000 |
commit | 7d07d2de2f52d4e2eba169e5563165309a795128 (patch) | |
tree | c3590f60f61233b4a571cfe3bfc08f6ab6591c88 /libexec/lfs_cleanerd | |
parent | f9ab90d9d6d02989a075d0f0074496d5b1045e4b (diff) | |
download | FreeBSD-src-7d07d2de2f52d4e2eba169e5563165309a795128.zip FreeBSD-src-7d07d2de2f52d4e2eba169e5563165309a795128.tar.gz |
BSD 4.4 Lite Libexec Sources
Diffstat (limited to 'libexec/lfs_cleanerd')
-rw-r--r-- | libexec/lfs_cleanerd/Makefile | 10 | ||||
-rw-r--r-- | libexec/lfs_cleanerd/clean.h | 168 | ||||
-rw-r--r-- | libexec/lfs_cleanerd/cleanerd.c | 498 | ||||
-rw-r--r-- | libexec/lfs_cleanerd/lfs_cleanerd.8 | 77 | ||||
-rw-r--r-- | libexec/lfs_cleanerd/library.c | 671 | ||||
-rw-r--r-- | libexec/lfs_cleanerd/misc.c | 94 | ||||
-rw-r--r-- | libexec/lfs_cleanerd/print.c | 218 |
7 files changed, 1736 insertions, 0 deletions
diff --git a/libexec/lfs_cleanerd/Makefile b/libexec/lfs_cleanerd/Makefile new file mode 100644 index 0000000..6993cf7 --- /dev/null +++ b/libexec/lfs_cleanerd/Makefile @@ -0,0 +1,10 @@ +# @(#)Makefile 8.1 (Berkeley) 6/5/93 + +PROG= lfs_cleanerd +CFLAGS+=-I/sys/ufs/lfs -I${.CURDIR} -DDIAGNOSTIC +MAN8= lfs_cleanerd.0 +SRCS= cleanerd.c lfs_cksum.c library.c misc.c print.c + +.PATH: /sys/ufs/lfs + +.include <bsd.prog.mk> diff --git a/libexec/lfs_cleanerd/clean.h b/libexec/lfs_cleanerd/clean.h new file mode 100644 index 0000000..0dd8775 --- /dev/null +++ b/libexec/lfs_cleanerd/clean.h @@ -0,0 +1,168 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)clean.h 8.1 (Berkeley) 6/4/93 + */ + +/* + * The LFS user-level library will be used when writing cleaners and + * checkers for LFS file systems. It will have facilities for finding + * and parsing LFS segments. + */ + +#define DUMP_SUM_HEADER 0x0001 +#define DUMP_INODE_ADDRS 0x0002 +#define DUMP_FINFOS 0x0004 +#define DUMP_ALL 0xFFFF + +#define IFILE_NAME "ifile" + +/* + * Cleaner parameters + * BUSY_LIM: lower bound of the number of segments currently available + * as a percentage of the total number of free segments possibly + * available. + * IDLE_LIM: Same as BUSY_LIM but used when the system is idle. + * MIN_SEGS: Minimum number of segments you should always have. + * I have no idea what this should be, but it should probably + * be a function of lfsp. + * NUM_TO_CLEAN: Number of segments to clean at once. Again, this + * should probably be based on the file system size and how + * full or empty the segments being cleaned are. + */ + +#define BUSY_LIM 0.50 +#define IDLE_LIM 0.90 + +#define MIN_SEGS(lfsp) (3) +#define NUM_TO_CLEAN(fsp) (1) + +#define MAXLOADS 3 +#define ONE_MIN 0 +#define FIVE_MIN 1 +#define FIFTEEN_MIN 2 + +typedef struct fs_info { + struct statfs *fi_statfsp; /* fsstat info from getfsstat */ + struct lfs fi_lfs; /* superblock */ + CLEANERINFO *fi_cip; /* Cleaner info from ifile */ + SEGUSE *fi_segusep; /* segment usage table (from ifile) */ + IFILE *fi_ifilep; /* ifile table (from ifile) */ + u_long fi_daddr_shift; /* shift to get byte offset of daddr */ + u_long fi_ifile_count; /* # entries in the ifile table */ + off_t fi_ifile_length; /* length of the ifile */ +} FS_INFO; + +/* + * XXX: size (in bytes) of a segment + * should lfs_bsize be fsbtodb(fs,1), blksize(fs), or lfs_dsize? + */ +#define seg_size(fs) ((fs)->lfs_ssize << (fs)->lfs_bshift) + +/* daddr -> byte offset */ +#define datobyte(fs, da) ((da) << (fs)->fi_daddr_shift) +#define bytetoda(fs, byte) ((byte) >> (fs)->fi_daddr_shift) + +#define CLEANSIZE(fsp) (fsp->fi_lfs.lfs_cleansz << fsp->fi_lfs.lfs_bshift) +#define SEGTABSIZE(fsp) (fsp->fi_lfs.lfs_segtabsz << fsp->fi_lfs.lfs_bshift) + +#define IFILE_ENTRY(fs, if, i) \ + ((IFILE *)((caddr_t)(if) + ((i) / (fs)->lfs_ifpb << (fs)->lfs_bshift)) \ + + (i) % (fs)->lfs_ifpb) + +#define SEGUSE_ENTRY(fs, su, i) \ + ((SEGUSE *)((caddr_t)(su) + (fs)->lfs_bsize * ((i) / (fs)->lfs_sepb)) +\ + (i) % (fs)->lfs_sepb) + +__BEGIN_DECLS +int dump_summary __P((struct lfs *, SEGSUM *, u_long, daddr_t **)); +void err __P((const int, const char *, ...)); +int fs_getmntinfo __P((struct statfs **, char *, int)); +int get __P((int, off_t, void *, size_t)); +FS_INFO *get_fs_info __P((struct statfs *, int)); +int lfs_segmapv __P((FS_INFO *, int, caddr_t, BLOCK_INFO **, int *)); +int mmap_segment __P((FS_INFO *, int, caddr_t *, int)); +void munmap_segment __P((FS_INFO *, caddr_t, int)); +void reread_fs_info __P((FS_INFO *, int)); +void toss __P((void *, int *, size_t, + int (*)(const void *, const void *, const void *), void *)); + +/* + * USEFUL DEBUGGING FUNCTIONS: + */ +#ifdef VERBOSE +#define PRINT_FINFO(fp, ip) { \ + (void)printf(" %s %s%d version %d nblocks %d\n", \ + (ip)->if_version > (fp)->fi_version ? "TOSSING" : "KEEPING", \ + "FINFO for inode: ", (fp)->fi_ino, \ + (fp)->fi_version, (fp)->fi_nblocks); \ + fflush(stdout); \ +} + +#define PRINT_INODE(b, bip) { \ + (void) printf("\t%s inode: %d daddr: 0x%lx create: %s\n", \ + b ? "KEEPING" : "TOSSING", (bip)->bi_inode, (bip)->bi_daddr, \ + ctime((time_t *)&(bip)->bi_segcreate)); \ + fflush(stdout); \ +} + +#define PRINT_BINFO(bip) { \ + (void)printf("\tinode: %d lbn: %d daddr: 0x%lx create: %s\n", \ + (bip)->bi_inode, (bip)->bi_lbn, (bip)->bi_daddr, \ + ctime((time_t *)&(bip)->bi_segcreate)); \ + fflush(stdout); \ +} + +#define PRINT_SEGUSE(sup, n) { \ + (void)printf("Segment %d nbytes=%lu\tflags=%c%c%c ninos=%d nsums=%d lastmod: %s\n", \ + n, (sup)->su_nbytes, \ + (sup)->su_flags & SEGUSE_DIRTY ? 'D' : 'C', \ + (sup)->su_flags & SEGUSE_ACTIVE ? 'A' : ' ', \ + (sup)->su_flags & SEGUSE_SUPERBLOCK ? 'S' : ' ', \ + (sup)->su_ninos, (sup)->su_nsums, \ + ctime((time_t *)&(sup)->su_lastmod)); \ + fflush(stdout); \ +} + +void dump_super __P((struct lfs *)); +void dump_cleaner_info __P((void *)); +void print_SEGSUM __P(( struct lfs *, SEGSUM *)); +void print_CLEANERINFO __P((CLEANERINFO *)); +#else +#define PRINT_FINFO(fp, ip) +#define PRINT_INODE(b, bip) +#define PRINT_BINFO(bip) +#define PRINT_SEGUSE(sup, n) +#define dump_cleaner_info(cip) +#define dump_super(lfsp) +#endif +__END_DECLS diff --git a/libexec/lfs_cleanerd/cleanerd.c b/libexec/lfs_cleanerd/cleanerd.c new file mode 100644 index 0000000..515d2d5 --- /dev/null +++ b/libexec/lfs_cleanerd/cleanerd.c @@ -0,0 +1,498 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1992, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)cleanerd.c 8.2 (Berkeley) 1/13/94"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/mount.h> +#include <sys/time.h> + +#include <ufs/ufs/dinode.h> +#include <ufs/lfs/lfs.h> + +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include "clean.h" +char *special = "cleanerd"; +int do_small = 0; +int do_mmap = 0; +struct cleaner_stats { + int blocks_read; + int blocks_written; + int segs_cleaned; + int segs_empty; + int segs_error; +} cleaner_stats; + +struct seglist { + int sl_id; /* segment number */ + int sl_cost; /* cleaning cost */ + char sl_empty; /* is segment empty */ +}; + +struct tossstruct { + struct lfs *lfs; + int seg; +}; + +/* function prototypes for system calls; not sure where they should go */ +int lfs_segwait __P((fsid_t *, struct timeval *)); +int lfs_segclean __P((fsid_t *, u_long)); +int lfs_bmapv __P((fsid_t *, BLOCK_INFO *, int)); +int lfs_markv __P((fsid_t *, BLOCK_INFO *, int)); + +/* function prototypes */ +int bi_tossold __P((const void *, const void *, const void *)); +int choose_segments __P((FS_INFO *, struct seglist *, + int (*)(FS_INFO *, SEGUSE *))); +void clean_fs __P((FS_INFO *, int (*)(FS_INFO *, SEGUSE *))); +int clean_loop __P((FS_INFO *)); +int clean_segment __P((FS_INFO *, int)); +int cost_benefit __P((FS_INFO *, SEGUSE *)); +int cost_compare __P((const void *, const void *)); +void sig_report __P((int)); + +/* + * Cleaning Cost Functions: + * + * These return the cost of cleaning a segment. The higher the cost value + * the better it is to clean the segment, so empty segments have the highest + * cost. (It is probably better to think of this as a priority value + * instead). + * + * This is the cost-benefit policy simulated and described in Rosenblum's + * 1991 SOSP paper. + */ + +int +cost_benefit(fsp, su) + FS_INFO *fsp; /* file system information */ + SEGUSE *su; +{ + struct lfs *lfsp; + struct timeval t; + int age; + int live; + + gettimeofday(&t, NULL); + + live = su->su_nbytes; + age = t.tv_sec < su->su_lastmod ? 0 : t.tv_sec - su->su_lastmod; + + lfsp = &fsp->fi_lfs; + if (live == 0) + return (t.tv_sec * lblkno(lfsp, seg_size(lfsp))); + else { + /* + * from lfsSegUsage.c (Mendel's code). + * priority calculation is done using INTEGER arithmetic. + * sizes are in BLOCKS (that is why we use lblkno below). + * age is in seconds. + * + * priority = ((seg_size - live) * age) / (seg_size + live) + */ +#ifdef VERBOSE + if (live < 0 || live > seg_size(lfsp)) { + err(0, "Bad segusage count: %d", live); + live = 0; + } +#endif + return (lblkno(lfsp, seg_size(lfsp) - live) * age) + / lblkno(lfsp, seg_size(lfsp) + live); + } +} + +int +main(argc, argv) + int argc; + char *argv[]; +{ + FS_INFO *fsp; + struct statfs *lstatfsp; /* file system stats */ + struct timeval timeout; /* sleep timeout */ + fsid_t fsid; + int i, nodaemon; + int opt, cmd_err; + char *fs_name; /* name of filesystem to clean */ + extern int optind; + + cmd_err = nodaemon = 0; + while ((opt = getopt(argc, argv, "smd")) != EOF) { + switch (opt) { + case 's': /* small writes */ + do_small = 1; + break; + case 'm': + do_mmap = 1; + break; + case 'd': + nodaemon = 1; + break; + default: + ++cmd_err; + } + } + argc -= optind; + argv += optind; + if (cmd_err || (argc != 1)) + err(1, "usage: lfs_cleanerd [-smd] fs_name"); + + fs_name = argv[0]; + + signal(SIGINT, sig_report); + signal(SIGUSR1, sig_report); + signal(SIGUSR2, sig_report); + if (fs_getmntinfo(&lstatfsp, fs_name, MOUNT_LFS) == 0) { + /* didn't find the filesystem */ + err(1, "lfs_cleanerd: filesystem %s isn't an LFS!", fs_name); + } + + if (!nodaemon) /* should we become a daemon, chdir to / & close fd's */ + if (daemon(0, 0) == -1) + err(1, "lfs_cleanerd: couldn't become a daemon!"); + + timeout.tv_sec = 5*60; /* five minutes */ + timeout.tv_usec = 0; + fsid.val[0] = 0; + fsid.val[1] = 0; + + for (fsp = get_fs_info(lstatfsp, do_mmap); ; + reread_fs_info(fsp, do_mmap)) { + /* + * clean the filesystem, and, if it needed cleaning + * (i.e. it returned nonzero) try it again + * to make sure that some nasty process hasn't just + * filled the disk system up. + */ + if (clean_loop(fsp)) + continue; + +#ifdef VERBOSE + (void)printf("Cleaner going to sleep.\n"); +#endif + if (lfs_segwait(&fsid, &timeout) < 0) + err(0, "lfs_segwait: returned error\n"); +#ifdef VERBOSE + (void)printf("Cleaner waking up.\n"); +#endif + } +} + +/* return the number of segments cleaned */ +int +clean_loop(fsp) + FS_INFO *fsp; /* file system information */ +{ + double loadavg[MAXLOADS]; + time_t now; + u_long max_free_segs; + + /* + * Compute the maximum possible number of free segments, given the + * number of free blocks. + */ + max_free_segs = fsp->fi_statfsp->f_bfree / fsp->fi_lfs.lfs_ssize; + + /* + * We will clean if there are not enough free blocks or total clean + * space is less than BUSY_LIM % of possible clean space. + */ + now = time((time_t *)NULL); + if (fsp->fi_cip->clean < max_free_segs && + (fsp->fi_cip->clean <= MIN_SEGS(&fsp->fi_lfs) || + fsp->fi_cip->clean < max_free_segs * BUSY_LIM)) { + printf("Cleaner Running at %s (%d of %d segments available)\n", + ctime(&now), fsp->fi_cip->clean, max_free_segs); + clean_fs(fsp, cost_benefit); + return (1); + } else { + /* + * We will also clean if the system is reasonably idle and + * the total clean space is less then IDLE_LIM % of possible + * clean space. + */ + if (getloadavg(loadavg, MAXLOADS) == -1) { + perror("getloadavg: failed\n"); + return (-1); + } + if (loadavg[ONE_MIN] == 0.0 && loadavg[FIVE_MIN] && + fsp->fi_cip->clean < max_free_segs * IDLE_LIM) { + clean_fs(fsp, cost_benefit); + printf("Cleaner Running at %s (system idle)\n", + ctime(&now)); + return (1); + } + } + printf("Cleaner Not Running at %s\n", ctime(&now)); + return (0); +} + + +void +clean_fs(fsp, cost_func) + FS_INFO *fsp; /* file system information */ + int (*cost_func) __P((FS_INFO *, SEGUSE *)); +{ + struct seglist *segs, *sp; + int i; + + if ((segs = + malloc(fsp->fi_lfs.lfs_nseg * sizeof(struct seglist))) == NULL) { + err(0, "malloc failed"); + return; + } + i = choose_segments(fsp, segs, cost_func); +#ifdef VERBOSE + printf("clean_fs: found %d segments to clean in file system %s\n", + i, fsp->fi_statfsp->f_mntonname); + fflush(stdout); +#endif + if (i) + for (i = MIN(i, NUM_TO_CLEAN(fsp)), sp = segs; i-- ; ++sp) { + if (clean_segment(fsp, sp->sl_id) < 0) + perror("clean_segment failed"); + else if (lfs_segclean(&fsp->fi_statfsp->f_fsid, + sp->sl_id) < 0) + perror("lfs_segclean failed"); + printf("Completed cleaning segment %d\n", sp->sl_id); + } + free(segs); +} + +/* + * Segment with the highest priority get sorted to the beginning of the + * list. This sort assumes that empty segments always have a higher + * cost/benefit than any utilized segment. + */ +int +cost_compare(a, b) + const void *a; + const void *b; +{ + return (((struct seglist *)b)->sl_cost - + ((struct seglist *)a)->sl_cost); +} + + +/* + * Returns the number of segments to be cleaned with the elements of seglist + * filled in. + */ +int +choose_segments(fsp, seglist, cost_func) + FS_INFO *fsp; + struct seglist *seglist; + int (*cost_func) __P((FS_INFO *, SEGUSE *)); +{ + struct lfs *lfsp; + struct seglist *sp; + SEGUSE *sup; + int i, nsegs; + + lfsp = &fsp->fi_lfs; + +#ifdef VERBOSE + (void)printf("Entering choose_segments\n"); +#endif + dump_super(lfsp); + dump_cleaner_info(fsp->fi_cip); + + for (sp = seglist, i = 0; i < lfsp->lfs_nseg; ++i) { + sup = SEGUSE_ENTRY(lfsp, fsp->fi_segusep, i); + PRINT_SEGUSE(sup, i); + if (!(sup->su_flags & SEGUSE_DIRTY) || + sup->su_flags & SEGUSE_ACTIVE) + continue; +#ifdef VERBOSE + (void)printf("\tchoosing segment %d\n", i); +#endif + sp->sl_cost = (*cost_func)(fsp, sup); + sp->sl_id = i; + sp->sl_empty = sup->su_nbytes ? 0 : 1; + ++sp; + } + nsegs = sp - seglist; + qsort(seglist, nsegs, sizeof(struct seglist), cost_compare); +#ifdef VERBOSE + (void)printf("Returning %d segments\n", nsegs); +#endif + return (nsegs); +} + + +int +clean_segment(fsp, id) + FS_INFO *fsp; /* file system information */ + int id; /* segment number */ +{ + BLOCK_INFO *block_array, *bp; + SEGUSE *sp; + struct lfs *lfsp; + struct tossstruct t; + caddr_t seg_buf; + int num_blocks, maxblocks, clean_blocks; + + lfsp = &fsp->fi_lfs; + sp = SEGUSE_ENTRY(lfsp, fsp->fi_segusep, id); + +#ifdef VERBOSE + (void)printf("cleaning segment %d: contains %lu bytes\n", id, + sp->su_nbytes); + fflush(stdout); +#endif + /* XXX could add debugging to verify that segment is really empty */ + if (sp->su_nbytes == sp->su_nsums * LFS_SUMMARY_SIZE) { + ++cleaner_stats.segs_empty; + return (0); + } + + /* map the segment into a buffer */ + if (mmap_segment(fsp, id, &seg_buf, do_mmap) < 0) { + err(0, "mmap_segment failed"); + ++cleaner_stats.segs_error; + return (-1); + } + /* get a list of blocks that are contained by the segment */ + if (lfs_segmapv(fsp, id, seg_buf, &block_array, &num_blocks) < 0) { + err(0, "clean_segment: lfs_segmapv failed"); + ++cleaner_stats.segs_error; + return (-1); + } + cleaner_stats.blocks_read += fsp->fi_lfs.lfs_ssize; + +#ifdef VERBOSE + (void)printf("lfs_segmapv returned %d blocks\n", num_blocks); + fflush(stdout); +#endif + + /* get the current disk address of blocks contained by the segment */ + if (lfs_bmapv(&fsp->fi_statfsp->f_fsid, block_array, num_blocks) < 0) { + perror("clean_segment: lfs_bmapv failed\n"); + ++cleaner_stats.segs_error; + return -1; + } + + /* Now toss any blocks not in the current segment */ + t.lfs = lfsp; + t.seg = id; + toss(block_array, &num_blocks, sizeof(BLOCK_INFO), bi_tossold, &t); + + /* Check if last element should be tossed */ + if (num_blocks && bi_tossold(&t, block_array + num_blocks - 1, NULL)) + --num_blocks; + +#ifdef VERBOSE + { + BLOCK_INFO *_bip; + u_long *lp; + int i; + + (void)printf("after bmapv still have %d blocks\n", num_blocks); + fflush(stdout); + if (num_blocks) + printf("BLOCK INFOS\n"); + for (_bip = block_array, i=0; i < num_blocks; ++_bip, ++i) { + PRINT_BINFO(_bip); + lp = (u_long *)_bip->bi_bp; + } + } +#endif + cleaner_stats.blocks_written += num_blocks; + if (do_small) + maxblocks = MAXPHYS / fsp->fi_lfs.lfs_bsize - 1; + else + maxblocks = num_blocks; + + for (bp = block_array; num_blocks > 0; bp += clean_blocks) { + clean_blocks = maxblocks < num_blocks ? maxblocks : num_blocks; + if (lfs_markv(&fsp->fi_statfsp->f_fsid, + bp, clean_blocks) < 0) { + err(0, "clean_segment: lfs_markv failed"); + ++cleaner_stats.segs_error; + return (-1); + } + num_blocks -= clean_blocks; + } + + free(block_array); + munmap_segment(fsp, seg_buf, do_mmap); + ++cleaner_stats.segs_cleaned; + return (0); +} + + +int +bi_tossold(client, a, b) + const void *client; + const void *a; + const void *b; +{ + const struct tossstruct *t; + + t = (struct tossstruct *)client; + + return (((BLOCK_INFO *)a)->bi_daddr == LFS_UNUSED_DADDR || + datosn(t->lfs, ((BLOCK_INFO *)a)->bi_daddr) != t->seg); +} + +void +sig_report(sig) + int sig; +{ + 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", + "blocks_read ", cleaner_stats.blocks_read, + "blocks_written ", cleaner_stats.blocks_written, + "segs_cleaned ", cleaner_stats.segs_cleaned, + "segs_empty ", cleaner_stats.segs_empty, + "seg_error ", cleaner_stats.segs_error); + if (sig == SIGUSR2) { + cleaner_stats.blocks_read = 0; + cleaner_stats.blocks_written = 0; + cleaner_stats.segs_cleaned = 0; + cleaner_stats.segs_empty = 0; + cleaner_stats.segs_error = 0; + } + if (sig == SIGINT) + exit(0); +} diff --git a/libexec/lfs_cleanerd/lfs_cleanerd.8 b/libexec/lfs_cleanerd/lfs_cleanerd.8 new file mode 100644 index 0000000..3d134db --- /dev/null +++ b/libexec/lfs_cleanerd/lfs_cleanerd.8 @@ -0,0 +1,77 @@ +.\" Copyright (c) 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)lfs_cleanerd.8 8.2 (Berkeley) 12/11/93 +.\" +.Dd "December 11, 1993" +.Dt LFS_CLEANERD 8 +.Os BSD 4.4 +.Sh NAME +.Nm lfs_cleanerd +.Nd garbage collect a log-structured file system +.Sh SYNOPSIS +.Nm lfs_cleanerd +.Op Fl ds +.Pa node +.Sh DESCRIPTION +The +.Nm lfs_cleanerd +command starts a daemon process which garbage-collects +the log-structured file system residing at the point named by +.Ar node +in the global file system namespace. +This command is normally executed by +.Xr mount_lfs 8 +when the log-structured file system is mounted. +The daemon will exit within a few minutes +of when the file system it was cleaning is unmounted. +.Pp +Garbage collection on a log-structured file system is done by scanning +the file system's segments for active, i.e. referenced, data and copying +it to new segments. +When all of the active data in a given segment has been copied to a new +segment that segment can be marked as empty, thus reclaiming the space +taken by the inactive data which was in it. +.Pp +The following options are available: +.Bl -tag -width indent +.It Fl d +Run in debug mode. +Do not become a daemon process, and print debugging information. +.It Fl s +When cleaning the file system, read data in small chunks. +.El +.Sh SEE ALSO +.Xr mount_lfs 8 +.Sh HISTORY +The +.Nm lfs_cleanerd +utility first appeared in 4.4BSD. diff --git a/libexec/lfs_cleanerd/library.c b/libexec/lfs_cleanerd/library.c new file mode 100644 index 0000000..b825a02 --- /dev/null +++ b/libexec/lfs_cleanerd/library.c @@ -0,0 +1,671 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)library.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <sys/mount.h> +#include <sys/types.h> +#include <sys/mman.h> + +#include <ufs/ufs/dinode.h> +#include <ufs/lfs/lfs.h> + +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "clean.h" + +void add_blocks __P((FS_INFO *, BLOCK_INFO *, int *, SEGSUM *, caddr_t, + daddr_t, daddr_t)); +void add_inodes __P((FS_INFO *, BLOCK_INFO *, int *, SEGSUM *, caddr_t, + daddr_t)); +int bi_compare __P((const void *, const void *)); +int bi_toss __P((const void *, const void *, const void *)); +void get_ifile __P((FS_INFO *, int)); +int get_superblock __P((FS_INFO *, struct lfs *)); +int pseg_valid __P((FS_INFO *, SEGSUM *)); + +/* + * This function will get information on a a filesystem which matches + * the name and type given. If a "name" is in a filesystem of the given + * type, then buf is filled with that filesystem's info, and the + * a non-zero value is returned. + */ +int +fs_getmntinfo(buf, name, type) + struct statfs **buf; + char *name; + int type; +{ + /* allocate space for the filesystem info */ + *buf = (struct statfs *)malloc(sizeof(struct statfs)); + if (*buf == NULL) + return 0; + + /* grab the filesystem info */ + if (statfs(name, *buf) < 0) { + free(*buf); + return 0; + } + + /* check to see if it's the one we want */ + if (((*buf)->f_type != type) || + strncmp(name, (*buf)->f_mntonname, MNAMELEN)) { + /* "this is not the filesystem you're looking for */ + free(*buf); + return 0; + } + + return 1; +} + +/* + * Get all the information available on an LFS file system. + * Returns an pointer to an FS_INFO structure, NULL on error. + */ +FS_INFO * +get_fs_info (lstatfsp, use_mmap) + struct statfs *lstatfsp; /* IN: pointer to statfs struct */ + int use_mmap; /* IN: mmap or read */ +{ + FS_INFO *fsp; + int i; + + fsp = (FS_INFO *)malloc(sizeof(FS_INFO)); + if (fsp == NULL) + return NULL; + bzero(fsp, sizeof(FS_INFO)); + + fsp->fi_statfsp = lstatfsp; + if (get_superblock (fsp, &fsp->fi_lfs)) + err(1, "get_fs_info: get_superblock failed"); + fsp->fi_daddr_shift = + fsp->fi_lfs.lfs_bshift - fsp->fi_lfs.lfs_fsbtodb; + get_ifile (fsp, use_mmap); + return (fsp); +} + +/* + * If we are reading the ifile then we need to refresh it. Even if + * we are mmapping it, it might have grown. Finally, we need to + * refresh the file system information (statfs) info. + */ +void +reread_fs_info(fsp, use_mmap) + FS_INFO *fsp; /* IN: prointer fs_infos to reread */ + int use_mmap; +{ + int i; + + if (statfs(fsp->fi_statfsp->f_mntonname, fsp->fi_statfsp)) + err(1, "reread_fs_info: statfs failed"); + get_ifile (fsp, use_mmap); +} + +/* + * Gets the superblock from disk (possibly in face of errors) + */ +int +get_superblock (fsp, sbp) + FS_INFO *fsp; /* local file system info structure */ + struct lfs *sbp; +{ + char mntfromname[MNAMELEN+1]; + int fid; + + strcpy(mntfromname, "/dev/r"); + strcat(mntfromname, fsp->fi_statfsp->f_mntfromname+5); + + if ((fid = open(mntfromname, O_RDONLY, (mode_t)0)) < 0) { + err(0, "get_superblock: bad open"); + return (-1); + } + + get(fid, LFS_LABELPAD, sbp, sizeof(struct lfs)); + close (fid); + + return (0); +} + +/* + * This function will map the ifile into memory. It causes a + * fatal error on failure. + */ +void +get_ifile (fsp, use_mmap) + FS_INFO *fsp; + int use_mmap; + +{ + struct stat file_stat; + caddr_t ifp; + char *ifile_name; + int count, fid; + + ifp = NULL; + ifile_name = malloc(strlen(fsp->fi_statfsp->f_mntonname) + + strlen(IFILE_NAME)+2); + strcat(strcat(strcpy(ifile_name, fsp->fi_statfsp->f_mntonname), "/"), + IFILE_NAME); + + if ((fid = open(ifile_name, O_RDWR, (mode_t)0)) < 0) + err(1, "get_ifile: bad open"); + + if (fstat (fid, &file_stat)) + err(1, "get_ifile: fstat failed"); + + if (use_mmap && file_stat.st_size == fsp->fi_ifile_length) { + (void) close(fid); + return; + } + + /* get the ifile */ + if (use_mmap) { + if (fsp->fi_cip) + munmap((caddr_t)fsp->fi_cip, fsp->fi_ifile_length); + ifp = mmap ((caddr_t)0, file_stat.st_size, + PROT_READ|PROT_WRITE, 0, fid, (off_t)0); + if (ifp == (caddr_t)(-1)) + err(1, "get_ifile: mmap failed"); + } else { + if (fsp->fi_cip) + free(fsp->fi_cip); + if (!(ifp = malloc (file_stat.st_size))) + err (1, "get_ifile: malloc failed"); +redo_read: + count = read (fid, ifp, (size_t) file_stat.st_size); + + if (count < 0) + err(1, "get_ifile: bad ifile read"); + else if (count < file_stat.st_size) { + err(0, "get_ifile"); + if (lseek(fid, 0, SEEK_SET) < 0) + err(1, "get_ifile: bad ifile lseek"); + goto redo_read; + } + } + fsp->fi_ifile_length = file_stat.st_size; + close (fid); + + fsp->fi_cip = (CLEANERINFO *)ifp; + fsp->fi_segusep = (SEGUSE *)(ifp + CLEANSIZE(fsp)); + fsp->fi_ifilep = (IFILE *)((caddr_t)fsp->fi_segusep + SEGTABSIZE(fsp)); + + /* + * The number of ifile entries is equal to the number of blocks + * blocks in the ifile minus the ones allocated to cleaner info + * and segment usage table multiplied by the number of ifile + * entries per page. + */ + fsp->fi_ifile_count = (fsp->fi_ifile_length >> fsp->fi_lfs.lfs_bshift - + fsp->fi_lfs.lfs_cleansz - fsp->fi_lfs.lfs_segtabsz) * + fsp->fi_lfs.lfs_ifpb; + + free (ifile_name); +} + +/* + * This function will scan a segment and return a list of + * <inode, blocknum> pairs which indicate which blocks were + * contained as live data within the segment when the segment + * summary was read (it may have "died" since then). Any given + * pair will be listed at most once. + */ +int +lfs_segmapv(fsp, seg, seg_buf, blocks, bcount) + FS_INFO *fsp; /* pointer to local file system information */ + int seg; /* the segment number */ + caddr_t seg_buf; /* the buffer containing the segment's data */ + BLOCK_INFO **blocks; /* OUT: array of block_info for live blocks */ + int *bcount; /* OUT: number of active blocks in segment */ +{ + BLOCK_INFO *bip; + SEGSUM *sp; + SEGUSE *sup; + FINFO *fip; + struct lfs *lfsp; + caddr_t s, segend; + daddr_t pseg_addr, seg_addr; + int i, nelem, nblocks, sumsize; + time_t timestamp; + + lfsp = &fsp->fi_lfs; + nelem = 2 * lfsp->lfs_ssize; + if (!(bip = malloc(nelem * sizeof(BLOCK_INFO)))) + goto err0; + + sup = SEGUSE_ENTRY(lfsp, fsp->fi_segusep, seg); + s = seg_buf + (sup->su_flags & SEGUSE_SUPERBLOCK ? LFS_SBPAD : 0); + seg_addr = sntoda(lfsp, seg); + pseg_addr = seg_addr + (sup->su_flags & SEGUSE_SUPERBLOCK ? btodb(LFS_SBPAD) : 0); +#ifdef VERBOSE + printf("\tsegment buffer at: 0x%x\tseg_addr 0x%x\n", s, seg_addr); +#endif /* VERBOSE */ + + *bcount = 0; + for (segend = seg_buf + seg_size(lfsp), timestamp = 0; s < segend; ) { + sp = (SEGSUM *)s; + +#ifdef VERBOSE + printf("\tpartial at: 0x%x\n", pseg_addr); + print_SEGSUM(lfsp, sp); + fflush(stdout); +#endif /* VERBOSE */ + + nblocks = pseg_valid(fsp, sp); + if (nblocks <= 0) + break; + + /* Check if we have hit old data */ + if (timestamp > ((SEGSUM*)s)->ss_create) + break; + timestamp = ((SEGSUM*)s)->ss_create; + +#ifdef DIAGNOSTIC + /* Verfiy size of summary block */ + sumsize = sizeof(SEGSUM) + + (sp->ss_ninos + INOPB(lfsp) - 1) / INOPB(lfsp); + for (fip = (FINFO *)(sp + 1); i < sp->ss_nfinfo; ++i) { + sumsize += sizeof(FINFO) + + (fip->fi_nblocks - 1) * sizeof(daddr_t); + fip = (FINFO *)(&fip->fi_blocks[fip->fi_nblocks]); + } + if (sumsize > LFS_SUMMARY_SIZE) { + fprintf(stderr, + "Segment %d summary block too big: %d\n", + seg, sumsize); + exit(1); + } +#endif + + if (*bcount + nblocks + sp->ss_ninos > nelem) { + nelem = *bcount + nblocks + sp->ss_ninos; + bip = realloc (bip, nelem * sizeof(BLOCK_INFO)); + if (!bip) + goto err0; + } + add_blocks(fsp, bip, bcount, sp, seg_buf, seg_addr, pseg_addr); + add_inodes(fsp, bip, bcount, sp, seg_buf, seg_addr); + pseg_addr += fsbtodb(lfsp, nblocks) + + bytetoda(fsp, LFS_SUMMARY_SIZE); + s += (nblocks << lfsp->lfs_bshift) + LFS_SUMMARY_SIZE; + } + qsort(bip, *bcount, sizeof(BLOCK_INFO), bi_compare); + toss(bip, bcount, sizeof(BLOCK_INFO), bi_toss, NULL); +#ifdef VERBOSE + { + BLOCK_INFO *_bip; + int i; + + printf("BLOCK INFOS\n"); + for (_bip = bip, i=0; i < *bcount; ++_bip, ++i) + PRINT_BINFO(_bip); + } +#endif + *blocks = bip; + return (0); + +err0: *bcount = 0; + return (-1); + +} + +/* + * This will parse a partial segment and fill in BLOCK_INFO structures + * for each block described in the segment summary. It will not include + * blocks or inodes from files with new version numbers. + */ +void +add_blocks (fsp, bip, countp, sp, seg_buf, segaddr, psegaddr) + FS_INFO *fsp; /* pointer to super block */ + BLOCK_INFO *bip; /* Block info array */ + int *countp; /* IN/OUT: number of blocks in array */ + SEGSUM *sp; /* segment summmary pointer */ + caddr_t seg_buf; /* buffer containing segment */ + daddr_t segaddr; /* address of this segment */ + daddr_t psegaddr; /* address of this partial segment */ +{ + IFILE *ifp; + FINFO *fip; + caddr_t bp; + daddr_t *dp, *iaddrp; + int db_per_block, i, j; + u_long page_size; + +#ifdef VERBOSE + printf("FILE INFOS\n"); +#endif + db_per_block = fsbtodb(&fsp->fi_lfs, 1); + page_size = fsp->fi_lfs.lfs_bsize; + bp = seg_buf + datobyte(fsp, psegaddr - segaddr) + LFS_SUMMARY_SIZE; + bip += *countp; + psegaddr += bytetoda(fsp, LFS_SUMMARY_SIZE); + iaddrp = (daddr_t *)((caddr_t)sp + LFS_SUMMARY_SIZE); + --iaddrp; + for (fip = (FINFO *)(sp + 1), i = 0; i < sp->ss_nfinfo; + ++i, fip = (FINFO *)(&fip->fi_blocks[fip->fi_nblocks])) { + + ifp = IFILE_ENTRY(&fsp->fi_lfs, fsp->fi_ifilep, fip->fi_ino); + PRINT_FINFO(fip, ifp); + if (ifp->if_version > fip->fi_version) + continue; + dp = &(fip->fi_blocks[0]); + for (j = 0; j < fip->fi_nblocks; j++, dp++) { + while (psegaddr == *iaddrp) { + psegaddr += db_per_block; + bp += page_size; + --iaddrp; + } + bip->bi_inode = fip->fi_ino; + bip->bi_lbn = *dp; + bip->bi_daddr = psegaddr; + bip->bi_segcreate = (time_t)(sp->ss_create); + bip->bi_bp = bp; + bip->bi_version = ifp->if_version; + psegaddr += db_per_block; + bp += page_size; + ++bip; + ++(*countp); + } + } +} + +/* + * For a particular segment summary, reads the inode blocks and adds + * INODE_INFO structures to the array. Returns the number of inodes + * actually added. + */ +void +add_inodes (fsp, bip, countp, sp, seg_buf, seg_addr) + FS_INFO *fsp; /* pointer to super block */ + BLOCK_INFO *bip; /* block info array */ + int *countp; /* pointer to current number of inodes */ + SEGSUM *sp; /* segsum pointer */ + caddr_t seg_buf; /* the buffer containing the segment's data */ + daddr_t seg_addr; /* disk address of seg_buf */ +{ + struct dinode *di; + struct lfs *lfsp; + IFILE *ifp; + BLOCK_INFO *bp; + daddr_t *daddrp; + ino_t inum; + int i; + + if (sp->ss_ninos <= 0) + return; + + bp = bip + *countp; + lfsp = &fsp->fi_lfs; +#ifdef VERBOSE + (void) printf("INODES:\n"); +#endif + daddrp = (daddr_t *)((caddr_t)sp + LFS_SUMMARY_SIZE); + for (i = 0; i < sp->ss_ninos; ++i) { + if (i % INOPB(lfsp) == 0) { + --daddrp; + di = (struct dinode *)(seg_buf + + ((*daddrp - seg_addr) << fsp->fi_daddr_shift)); + } else + ++di; + + inum = di->di_inumber; + bp->bi_lbn = LFS_UNUSED_LBN; + bp->bi_inode = inum; + bp->bi_daddr = *daddrp; + bp->bi_bp = di; + bp->bi_segcreate = sp->ss_create; + + if (inum == LFS_IFILE_INUM) { + bp->bi_version = 1; /* Ifile version should be 1 */ + bp++; + ++(*countp); + PRINT_INODE(1, bp); + } else { + ifp = IFILE_ENTRY(lfsp, fsp->fi_ifilep, inum); + PRINT_INODE(ifp->if_daddr == *daddrp, bp); + bp->bi_version = ifp->if_version; + if (ifp->if_daddr == *daddrp) { + bp++; + ++(*countp); + } + } + } +} + +/* + * Checks the summary checksum and the data checksum to determine if the + * segment is valid or not. Returns the size of the partial segment if it + * is valid, * and 0 otherwise. Use dump_summary to figure out size of the + * the partial as well as whether or not the checksum is valid. + */ +int +pseg_valid (fsp, ssp) + FS_INFO *fsp; /* pointer to file system info */ + SEGSUM *ssp; /* pointer to segment summary block */ +{ + caddr_t p; + int i, nblocks; + u_long *datap; + + if ((nblocks = dump_summary(&fsp->fi_lfs, ssp, 0, NULL)) <= 0 || + nblocks > fsp->fi_lfs.lfs_ssize - 1) + return(0); + + /* check data/inode block(s) checksum too */ + datap = (u_long *)malloc(nblocks * sizeof(u_long)); + p = (caddr_t)ssp + LFS_SUMMARY_SIZE; + for (i = 0; i < nblocks; ++i) { + datap[i] = *((u_long *)p); + p += fsp->fi_lfs.lfs_bsize; + } + if (cksum ((void *)datap, nblocks * sizeof(u_long)) != ssp->ss_datasum) + return (0); + + return (nblocks); +} + + +/* #define MMAP_SEGMENT */ +/* + * read a segment into a memory buffer + */ +int +mmap_segment (fsp, segment, segbuf, use_mmap) + FS_INFO *fsp; /* file system information */ + int segment; /* segment number */ + caddr_t *segbuf; /* pointer to buffer area */ + int use_mmap; /* mmap instead of read */ +{ + struct lfs *lfsp; + int fid; /* fildes for file system device */ + daddr_t seg_daddr; /* base disk address of segment */ + off_t seg_byte; + size_t ssize; + char mntfromname[MNAMELEN+2]; + + lfsp = &fsp->fi_lfs; + + /* get the disk address of the beginning of the segment */ + seg_daddr = sntoda(lfsp, segment); + seg_byte = datobyte(fsp, seg_daddr); + ssize = seg_size(lfsp); + + strcpy(mntfromname, "/dev/r"); + strcat(mntfromname, fsp->fi_statfsp->f_mntfromname+5); + + if ((fid = open(mntfromname, O_RDONLY, (mode_t)0)) < 0) { + err(0, "mmap_segment: bad open"); + return (-1); + } + + if (use_mmap) { + *segbuf = mmap ((caddr_t)0, seg_size(lfsp), PROT_READ, + 0, fid, seg_byte); + if (*(long *)segbuf < 0) { + err(0, "mmap_segment: mmap failed"); + return (NULL); + } + } else { +#ifdef VERBOSE + printf("mmap_segment\tseg_daddr: %lu\tseg_size: %lu\tseg_offset: %qu\n", + seg_daddr, ssize, seg_byte); +#endif + /* malloc the space for the buffer */ + *segbuf = malloc(ssize); + if (!*segbuf) { + err(0, "mmap_segment: malloc failed"); + return(NULL); + } + + /* read the segment data into the buffer */ + if (lseek (fid, seg_byte, SEEK_SET) != seg_byte) { + err (0, "mmap_segment: bad lseek"); + free(*segbuf); + return (-1); + } + + if (read (fid, *segbuf, ssize) != ssize) { + err (0, "mmap_segment: bad read"); + free(*segbuf); + return (-1); + } + } + close (fid); + + return (0); +} + +void +munmap_segment (fsp, seg_buf, use_mmap) + FS_INFO *fsp; /* file system information */ + caddr_t seg_buf; /* pointer to buffer area */ + int use_mmap; /* mmap instead of read/write */ +{ + if (use_mmap) + munmap (seg_buf, seg_size(&fsp->fi_lfs)); + else + free (seg_buf); +} + + +/* + * USEFUL DEBUGGING TOOLS: + */ +void +print_SEGSUM (lfsp, p) + struct lfs *lfsp; + SEGSUM *p; +{ + if (p) + (void) dump_summary(lfsp, p, DUMP_ALL, NULL); + else printf("0x0"); + fflush(stdout); +} + +int +bi_compare(a, b) + const void *a; + const void *b; +{ + const BLOCK_INFO *ba, *bb; + int diff; + + ba = a; + bb = b; + + if (diff = (int)(ba->bi_inode - bb->bi_inode)) + return (diff); + if (diff = (int)(ba->bi_lbn - bb->bi_lbn)) { + if (ba->bi_lbn == LFS_UNUSED_LBN) + return(-1); + else if (bb->bi_lbn == LFS_UNUSED_LBN) + return(1); + else if (ba->bi_lbn < 0 && bb->bi_lbn >= 0) + return(1); + else if (bb->bi_lbn < 0 && ba->bi_lbn >= 0) + return(-1); + else + return (diff); + } + if (diff = (int)(ba->bi_segcreate - bb->bi_segcreate)) + return (diff); + diff = (int)(ba->bi_daddr - bb->bi_daddr); + return (diff); +} + +int +bi_toss(dummy, a, b) + const void *dummy; + const void *a; + const void *b; +{ + const BLOCK_INFO *ba, *bb; + + ba = a; + bb = b; + + return(ba->bi_inode == bb->bi_inode && ba->bi_lbn == bb->bi_lbn); +} + +void +toss(p, nump, size, dotoss, client) + void *p; + int *nump; + size_t size; + int (*dotoss) __P((const void *, const void *, const void *)); + void *client; +{ + int i; + void *p1; + + if (*nump == 0) + return; + + for (i = *nump; --i > 0;) { + p1 = p + size; + if (dotoss(client, p, p1)) { + memmove(p, p1, i * size); + --(*nump); + } else + p += size; + } +} diff --git a/libexec/lfs_cleanerd/misc.c b/libexec/lfs_cleanerd/misc.c new file mode 100644 index 0000000..ad6e11a --- /dev/null +++ b/libexec/lfs_cleanerd/misc.c @@ -0,0 +1,94 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)misc.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <unistd.h> +#include <errno.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +extern char *special; + +#if __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +void +#if __STDC__ +err(const int fatal, const char *fmt, ...) +#else +err(fmt, va_alist) + char *fmt; + va_dcl +#endif +{ + va_list ap; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + (void)fprintf(stderr, "%s: ", special); + (void)vfprintf(stderr, fmt, ap); + va_end(ap); + if (errno) + (void)fprintf(stderr, " %s", strerror(errno)); + (void)fprintf(stderr, "\n"); + if (fatal) + exit(1); +} + +void +get(fd, off, p, len) + int fd; + off_t off; + void *p; + size_t len; +{ + int rbytes; + + if (lseek(fd, off, SEEK_SET) < 0) + err(1, "%s: %s", special, strerror(errno)); + if ((rbytes = read(fd, p, len)) < 0) + err(1, "%s: %s", special, strerror(errno)); + if (rbytes != len) + err(1, "%s: short read (%d, not %d)", special, rbytes, len); +} diff --git a/libexec/lfs_cleanerd/print.c b/libexec/lfs_cleanerd/print.c new file mode 100644 index 0000000..5c3863a --- /dev/null +++ b/libexec/lfs_cleanerd/print.c @@ -0,0 +1,218 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)print.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/ucred.h> +#include <sys/mount.h> +#include <sys/time.h> + +#include <ufs/ufs/dinode.h> +#include <ufs/lfs/lfs.h> + +#include <stdlib.h> +#include <stdio.h> +#include "clean.h" + +/* + * Print out a summary block; return number of blocks in segment; 0 + * for empty segment or corrupt segment. + * Returns a pointer to the array of inode addresses. + */ +int +dump_summary(lfsp, sp, flags, iaddrp) + struct lfs *lfsp; + SEGSUM *sp; + u_long flags; + daddr_t **iaddrp; +{ + int i, j, numblocks; + daddr_t *dp; + + FINFO *fp; + int ck; + + if (sp->ss_sumsum != (ck = cksum(&sp->ss_datasum, + LFS_SUMMARY_SIZE - sizeof(sp->ss_sumsum)))) + return(-1); + + if (flags & DUMP_SUM_HEADER) { + (void)printf(" %s0x%X\t%s%d\t%s%d\n %s0x%X\t%s0x%X", + "next ", sp->ss_next, + "nfinfo ", sp->ss_nfinfo, + "ninos ", sp->ss_ninos, + "sumsum ", sp->ss_sumsum, + "datasum ", sp->ss_datasum ); + (void)printf("\tcreate %s", ctime((time_t *)&sp->ss_create)); + } + + numblocks = (sp->ss_ninos + INOPB(lfsp) - 1) / INOPB(lfsp); + + /* Dump out inode disk addresses */ + if (flags & DUMP_INODE_ADDRS) + printf(" Inode addresses:"); + + dp = (daddr_t *)((caddr_t)sp + LFS_SUMMARY_SIZE); + for (--dp, i = 0; i < sp->ss_ninos; --dp) + if (flags & DUMP_INODE_ADDRS) { + (void)printf("\t0x%lx", *dp); + if (++i % 7 == 0) + (void)printf("\n"); + } else + ++i; + if (iaddrp) + *iaddrp = ++dp; + if (flags & DUMP_INODE_ADDRS) + printf("\n"); + + for (fp = (FINFO *)(sp + 1), i = 0; i < sp->ss_nfinfo; ++i) { + numblocks += fp->fi_nblocks; + if (flags & DUMP_FINFOS) { + (void)printf(" %s%d version %d nblocks %d\n", + "FINFO for inode: ", fp->fi_ino, + fp->fi_version, fp->fi_nblocks); + dp = &(fp->fi_blocks[0]); + for (j = 0; j < fp->fi_nblocks; j++, dp++) { + (void)printf("\t%d", *dp); + if ((j % 8) == 7) + (void)printf("\n"); + } + if ((j % 8) != 0) + (void)printf("\n"); + fp = (FINFO *)dp; + } else { + fp = (FINFO *)(&fp->fi_blocks[fp->fi_nblocks]); + } + } + return (numblocks); +} + +#ifdef VERBOSE +void +dump_cleaner_info(ipage) + void *ipage; +{ + CLEANERINFO *cip; + + cip = (CLEANERINFO *)ipage; + (void)printf("segments clean\t%d\tsegments dirty\t%d\n\n", + cip->clean, cip->dirty); +} + +void +dump_super(lfsp) + struct lfs *lfsp; +{ + int i; + + (void)printf("%s0x%X\t%s0x%X\t%s%d\t%s%d\n", + "magic ", lfsp->lfs_magic, + "version ", lfsp->lfs_version, + "size ", lfsp->lfs_size, + "ssize ", lfsp->lfs_ssize); + (void)printf("%s%d\t\t%s%d\t%s%d\t%s%d\n", + "dsize ", lfsp->lfs_dsize, + "bsize ", lfsp->lfs_bsize, + "fsize ", lfsp->lfs_fsize, + "frag ", lfsp->lfs_frag); + + (void)printf("%s%d\t\t%s%d\t%s%d\t%s%d\n", + "minfree ", lfsp->lfs_minfree, + "inopb ", lfsp->lfs_inopb, + "ifpb ", lfsp->lfs_ifpb, + "nindir ", lfsp->lfs_nindir); + + (void)printf("%s%d\t\t%s%d\t%s%d\t%s%d\n", + "nseg ", lfsp->lfs_nseg, + "nspf ", lfsp->lfs_nspf, + "cleansz ", lfsp->lfs_cleansz, + "segtabsz ", lfsp->lfs_segtabsz); + + (void)printf("%s0x%X\t%s%d\t%s0x%X\t%s%d\n", + "segmask ", lfsp->lfs_segmask, + "segshift ", lfsp->lfs_segshift, + "bmask ", lfsp->lfs_bmask, + "bshift ", lfsp->lfs_bshift); + + (void)printf("%s0x%X\t\t%s%d\t%s0x%X\t%s%d\n", + "ffmask ", lfsp->lfs_ffmask, + "ffshift ", lfsp->lfs_ffshift, + "fbmask ", lfsp->lfs_fbmask, + "fbshift ", lfsp->lfs_fbshift); + + (void)printf("%s%d\t\t%s0x%X\t%s0x%qx\n", + "fsbtodb ", lfsp->lfs_fsbtodb, + "cksum ", lfsp->lfs_cksum, + "maxfilesize ", lfsp->lfs_maxfilesize); + + (void)printf("Superblock disk addresses:\t"); + for (i = 0; i < LFS_MAXNUMSB; i++) { + (void)printf(" 0x%X", lfsp->lfs_sboffs[i]); + if ( i == (LFS_MAXNUMSB >> 1)) + (void)printf("\n\t\t\t\t"); + } + (void)printf("\n"); + + (void)printf("Checkpoint Info\n"); + (void)printf("%s%d\t%s0x%X\t%s%d\n", + "free ", lfsp->lfs_free, + "idaddr ", lfsp->lfs_idaddr, + "ifile ", lfsp->lfs_ifile); + (void)printf("%s%d\t%s%d\t%s%d\n", + "bfree ", lfsp->lfs_bfree, + "avail ", lfsp->lfs_avail, + "uinodes ", lfsp->lfs_uinodes); + (void)printf("%s%d\t%s0x%X\t%s0x%X\n%s0x%X\t%s0x%X\t", + "nfiles ", lfsp->lfs_nfiles, + "lastseg ", lfsp->lfs_lastseg, + "nextseg ", lfsp->lfs_nextseg, + "curseg ", lfsp->lfs_curseg, + "offset ", lfsp->lfs_offset); + (void)printf("tstamp %s", ctime((time_t *)&lfsp->lfs_tstamp)); + (void)printf("\nIn-Memory Information\n"); + (void)printf("%s%d\t%s0x%X\t%s%d\t%s%d\t%s%d\n", + "seglock ", lfsp->lfs_seglock, + "iocount ", lfsp->lfs_iocount, + "writer ", lfsp->lfs_writer, + "dirops ", lfsp->lfs_dirops, + "doifile ", lfsp->lfs_doifile ); + (void)printf("%s%d\t%s%d\t%s0x%X\t%s%d\n", + "nactive ", lfsp->lfs_nactive, + "fmod ", lfsp->lfs_fmod, + "clean ", lfsp->lfs_clean, + "ronly ", lfsp->lfs_ronly); +} +#endif /* VERBOSE */ |