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*69668Smargo static char sccsid[] = "@(#)cleanerd.c	8.4 (Berkeley) 05/24/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;
34*69668Smargo int stat_report = 0;
3557200Smargo struct cleaner_stats {
36*69668Smargo 	double	util_tot;
37*69668Smargo 	double	util_sos;
3857200Smargo 	int	blocks_read;
3957200Smargo 	int	blocks_written;
4057200Smargo 	int	segs_cleaned;
4157200Smargo 	int	segs_empty;
4257200Smargo 	int	segs_error;
4357200Smargo } cleaner_stats;
4455857Sbostic 
4555857Sbostic struct seglist {
4655857Sbostic 	int sl_id;	/* segment number */
4755857Sbostic 	int sl_cost; 	/* cleaning cost */
48*69668Smargo 	char sl_bytes;	/* bytes in segment */
4955857Sbostic };
5055857Sbostic 
5155857Sbostic struct tossstruct {
5255857Sbostic 	struct lfs *lfs;
5355857Sbostic 	int seg;
5455857Sbostic };
5555857Sbostic 
56*69668Smargo #define	CLEAN_BYTES	0x1
57*69668Smargo 
5855857Sbostic /* function prototypes for system calls; not sure where they should go */
5965737Sbostic int	 lfs_segwait __P((fsid_t *, struct timeval *));
6065737Sbostic int	 lfs_segclean __P((fsid_t *, u_long));
6165737Sbostic int	 lfs_bmapv __P((fsid_t *, BLOCK_INFO *, int));
6265737Sbostic int	 lfs_markv __P((fsid_t *, BLOCK_INFO *, int));
6355857Sbostic 
6455857Sbostic /* function prototypes */
6555857Sbostic int	 bi_tossold __P((const void *, const void *, const void *));
6655857Sbostic int	 choose_segments __P((FS_INFO *, struct seglist *,
6755857Sbostic 	     int (*)(FS_INFO *, SEGUSE *)));
68*69668Smargo void	 clean_fs __P((FS_INFO	*, int (*)(FS_INFO *, SEGUSE *), int, long));
69*69668Smargo int	 clean_loop __P((FS_INFO *, int, long));
7055857Sbostic int	 clean_segment __P((FS_INFO *, int));
7155857Sbostic int	 cost_benefit __P((FS_INFO *, SEGUSE *));
7255857Sbostic int	 cost_compare __P((const void *, const void *));
7357200Smargo void	 sig_report __P((int));
7455857Sbostic 
7555857Sbostic /*
7655857Sbostic  * Cleaning Cost Functions:
7755857Sbostic  *
7855857Sbostic  * These return the cost of cleaning a segment.  The higher the cost value
7955857Sbostic  * the better it is to clean the segment, so empty segments have the highest
8055857Sbostic  * cost.  (It is probably better to think of this as a priority value
8155857Sbostic  * instead).
8255857Sbostic  *
8355857Sbostic  * This is the cost-benefit policy simulated and described in Rosenblum's
8455857Sbostic  * 1991 SOSP paper.
8555857Sbostic  */
8655857Sbostic 
8755857Sbostic int
8855857Sbostic cost_benefit(fsp, su)
8955857Sbostic 	FS_INFO *fsp;		/* file system information */
9055857Sbostic 	SEGUSE *su;
9155857Sbostic {
9255857Sbostic 	struct lfs *lfsp;
9355857Sbostic 	struct timeval t;
9455857Sbostic 	int age;
9555857Sbostic 	int live;
9655857Sbostic 
9755857Sbostic 	gettimeofday(&t, NULL);
9855857Sbostic 
9955857Sbostic 	live = su->su_nbytes;
10060096Sbostic 	age = t.tv_sec < su->su_lastmod ? 0 : t.tv_sec - su->su_lastmod;
10155857Sbostic 
10255857Sbostic 	lfsp = &fsp->fi_lfs;
10365737Sbostic 	if (live == 0)
10455857Sbostic 		return (t.tv_sec * lblkno(lfsp, seg_size(lfsp)));
10555857Sbostic 	else {
10655857Sbostic 		/*
10755857Sbostic 		 * from lfsSegUsage.c (Mendel's code).
10855857Sbostic 		 * priority calculation is done using INTEGER arithmetic.
10955857Sbostic 		 * sizes are in BLOCKS (that is why we use lblkno below).
11055857Sbostic 		 * age is in seconds.
11155857Sbostic 		 *
11265737Sbostic 		 * priority = ((seg_size - live) * age) / (seg_size + live)
11355857Sbostic 		 */
11455857Sbostic #ifdef VERBOSE
11555857Sbostic 		if (live < 0 || live > seg_size(lfsp)) {
11655857Sbostic 			err(0, "Bad segusage count: %d", live);
11755857Sbostic 			live = 0;
11855857Sbostic 		}
11955857Sbostic #endif
12055857Sbostic 		return (lblkno(lfsp, seg_size(lfsp) - live) * age)
12155857Sbostic 			/ lblkno(lfsp, seg_size(lfsp) + live);
12255857Sbostic 	}
12355857Sbostic }
12455857Sbostic 
12555860Sbostic int
12655857Sbostic main(argc, argv)
12755857Sbostic 	int argc;
12855857Sbostic 	char *argv[];
12955857Sbostic {
13061371Scgd 	FS_INFO	*fsp;
13155857Sbostic 	struct statfs *lstatfsp;	/* file system stats */
13255857Sbostic 	struct timeval timeout;		/* sleep timeout */
13355857Sbostic 	fsid_t fsid;
134*69668Smargo 	long clean_opts;		/* cleaning options */
135*69668Smargo 	int i, nodaemon, segs_per_clean;
13657200Smargo 	int opt, cmd_err;
13761371Scgd 	char *fs_name;			/* name of filesystem to clean */
13861371Scgd 	extern int optind;
13957200Smargo 
14061417Scgd 	cmd_err = nodaemon = 0;
141*69668Smargo 	clean_opts = 0;
142*69668Smargo 	segs_per_clean = 1;
143*69668Smargo 	while ((opt = getopt(argc, argv, "bdmn:r:s")) != EOF) {
14457200Smargo 		switch (opt) {
145*69668Smargo 			case 'b':	/*
146*69668Smargo 					 * Use live bytes to determine
147*69668Smargo 					 * how many segs to clean.
148*69668Smargo 					 */
149*69668Smargo 				clean_opts |= CLEAN_BYTES;
15057200Smargo 				break;
151*69668Smargo 			case 'd':	/* Debug mode. */
152*69668Smargo 				nodaemon = 1;
153*69668Smargo 				break;
154*69668Smargo 			case 'm':	/* Use mmap instead of read/write */
15557200Smargo 				do_mmap = 1;
15657200Smargo 				break;
157*69668Smargo 			case 'n':	/* How many segs to clean at once */
158*69668Smargo 				segs_per_clean = atoi(optarg);
15961417Scgd 				break;
160*69668Smargo 			case 'r':	/* Report every stat_report segments */
161*69668Smargo 				stat_report = atoi(optarg);
162*69668Smargo 				break;
163*69668Smargo 			case 's':	/* small writes */
164*69668Smargo 				do_small = 1;
165*69668Smargo 				break;
16657200Smargo 			default:
16757200Smargo 				++cmd_err;
16857200Smargo 		}
16957200Smargo 	}
17061371Scgd 	argc -= optind;
17161371Scgd 	argv += optind;
17261371Scgd 	if (cmd_err || (argc != 1))
17361417Scgd 		err(1, "usage: lfs_cleanerd [-smd] fs_name");
17457200Smargo 
17561371Scgd 	fs_name = argv[0];
17661371Scgd 
17765737Sbostic 	signal(SIGINT, sig_report);
17865737Sbostic 	signal(SIGUSR1, sig_report);
17965737Sbostic 	signal(SIGUSR2, sig_report);
18069256Smckusick 	if (fs_getmntinfo(&lstatfsp, fs_name, "lfs") == 0) {
18161371Scgd 		/* didn't find the filesystem */
18261371Scgd 		err(1, "lfs_cleanerd: filesystem %s isn't an LFS!", fs_name);
18361371Scgd 	}
18455857Sbostic 
18561417Scgd 	if (!nodaemon)	/* should we become a daemon, chdir to / & close fd's */
18661417Scgd 		if (daemon(0, 0) == -1)
18761417Scgd 			err(1, "lfs_cleanerd: couldn't become a daemon!");
18861417Scgd 
18955857Sbostic 	timeout.tv_sec = 5*60; /* five minutes */
19055857Sbostic 	timeout.tv_usec = 0;
19155857Sbostic 	fsid.val[0] = 0;
19255857Sbostic 	fsid.val[1] = 0;
19355857Sbostic 
19461371Scgd 	for (fsp = get_fs_info(lstatfsp, do_mmap); ;
19561371Scgd 	    reread_fs_info(fsp, do_mmap)) {
19656030Sbostic 		/*
19761371Scgd 		 * clean the filesystem, and, if it needed cleaning
19861371Scgd 		 * (i.e. it returned nonzero) try it again
19956030Sbostic 		 * to make sure that some nasty process hasn't just
20056030Sbostic 		 * filled the disk system up.
20156030Sbostic 		 */
202*69668Smargo 		if (clean_loop(fsp, segs_per_clean, clean_opts))
20356030Sbostic 			continue;
20455857Sbostic 
20555857Sbostic #ifdef VERBOSE
20655857Sbostic 		(void)printf("Cleaner going to sleep.\n");
20755857Sbostic #endif
20865737Sbostic 		if (lfs_segwait(&fsid, &timeout) < 0)
20955857Sbostic 			err(0, "lfs_segwait: returned error\n");
21055857Sbostic #ifdef VERBOSE
21155857Sbostic 		(void)printf("Cleaner waking up.\n");
21255857Sbostic #endif
21355857Sbostic 	}
21455857Sbostic }
21555857Sbostic 
21655857Sbostic /* return the number of segments cleaned */
21755857Sbostic int
218*69668Smargo clean_loop(fsp, nsegs, options)
21955857Sbostic 	FS_INFO	*fsp;	/* file system information */
220*69668Smargo 	int nsegs;
221*69668Smargo 	long options;
22255857Sbostic {
22355857Sbostic 	double loadavg[MAXLOADS];
22455857Sbostic 	time_t	now;
22555857Sbostic 	u_long max_free_segs;
22655857Sbostic 
22755857Sbostic         /*
22855857Sbostic 	 * Compute the maximum possible number of free segments, given the
22955857Sbostic 	 * number of free blocks.
23055857Sbostic 	 */
23155857Sbostic 	max_free_segs = fsp->fi_statfsp->f_bfree / fsp->fi_lfs.lfs_ssize;
23255857Sbostic 
23355857Sbostic 	/*
23455857Sbostic 	 * We will clean if there are not enough free blocks or total clean
23555857Sbostic 	 * space is less than BUSY_LIM % of possible clean space.
23655857Sbostic 	 */
23755857Sbostic 	now = time((time_t *)NULL);
23856181Smargo 	if (fsp->fi_cip->clean < max_free_segs &&
23956181Smargo 	    (fsp->fi_cip->clean <= MIN_SEGS(&fsp->fi_lfs) ||
24056181Smargo 	    fsp->fi_cip->clean < max_free_segs * BUSY_LIM)) {
24156656Sbostic 		printf("Cleaner Running  at %s (%d of %d segments available)\n",
24256656Sbostic 		    ctime(&now), fsp->fi_cip->clean, max_free_segs);
243*69668Smargo 		clean_fs(fsp, cost_benefit, nsegs, options);
24455857Sbostic 		return (1);
24555857Sbostic 	} else {
24655857Sbostic 	        /*
24755857Sbostic 		 * We will also clean if the system is reasonably idle and
24855857Sbostic 		 * the total clean space is less then IDLE_LIM % of possible
24955857Sbostic 		 * clean space.
25055857Sbostic 		 */
25155857Sbostic 		if (getloadavg(loadavg, MAXLOADS) == -1) {
25255857Sbostic 			perror("getloadavg: failed\n");
25355857Sbostic 			return (-1);
25455857Sbostic 		}
25555857Sbostic 		if (loadavg[ONE_MIN] == 0.0 && loadavg[FIVE_MIN] &&
25655857Sbostic 		    fsp->fi_cip->clean < max_free_segs * IDLE_LIM) {
257*69668Smargo 		        clean_fs(fsp, cost_benefit, nsegs, options);
25855857Sbostic 			printf("Cleaner Running  at %s (system idle)\n",
25955857Sbostic 			    ctime(&now));
26055857Sbostic 			return (1);
26155857Sbostic 		}
26255857Sbostic 	}
26355857Sbostic 	printf("Cleaner Not Running at %s\n", ctime(&now));
26455857Sbostic 	return (0);
26555857Sbostic }
26655857Sbostic 
26755857Sbostic 
26855857Sbostic void
269*69668Smargo clean_fs(fsp, cost_func, nsegs, options)
27055857Sbostic 	FS_INFO	*fsp;	/* file system information */
27155857Sbostic 	int (*cost_func) __P((FS_INFO *, SEGUSE *));
272*69668Smargo 	int nsegs;
273*69668Smargo 	long options;
27455857Sbostic {
27555857Sbostic 	struct seglist *segs, *sp;
276*69668Smargo 	int to_clean, cleaned_bytes;
27755857Sbostic 	int i;
27855857Sbostic 
27965737Sbostic 	if ((segs =
28065737Sbostic 	    malloc(fsp->fi_lfs.lfs_nseg * sizeof(struct seglist))) == NULL) {
28155857Sbostic 		err(0, "malloc failed");
28255857Sbostic 		return;
28355857Sbostic 	}
28455857Sbostic 	i = choose_segments(fsp, segs, cost_func);
28555857Sbostic #ifdef VERBOSE
28656051Sbostic 	printf("clean_fs: found %d segments to clean in file system %s\n",
28755857Sbostic 		i, fsp->fi_statfsp->f_mntonname);
28855857Sbostic 	fflush(stdout);
28955857Sbostic #endif
290*69668Smargo 	if (i) {
291*69668Smargo 		/* Check which cleaning algorithm to use. */
292*69668Smargo 		if (options & CLEAN_BYTES) {
293*69668Smargo 			cleaned_bytes = 0;
294*69668Smargo 			to_clean = nsegs <<
295*69668Smargo 			    (fsp->fi_lfs.lfs_segshift + fsp->fi_lfs.lfs_bshift);
296*69668Smargo 			for (sp = segs; i && cleaned_bytes < to_clean;
297*69668Smargo 			    i--, ++sp) {
298*69668Smargo 				if (clean_segment(fsp, sp->sl_id) < 0)
299*69668Smargo 					perror("clean_segment failed");
300*69668Smargo 				else if (lfs_segclean(&fsp->fi_statfsp->f_fsid,
301*69668Smargo 				    sp->sl_id) < 0)
302*69668Smargo 					perror("lfs_segclean failed");
303*69668Smargo 				printf("Cleaned segment %d (%d bytes)\n",
304*69668Smargo 				    sp->sl_id, sp->sl_bytes);
305*69668Smargo 				cleaned_bytes += sp->sl_bytes;
306*69668Smargo 			}
307*69668Smargo 		} else
308*69668Smargo 			for (i = MIN(i, nsegs), sp = segs; i-- ; ++sp) {
309*69668Smargo 				if (clean_segment(fsp, sp->sl_id) < 0)
310*69668Smargo 					perror("clean_segment failed");
311*69668Smargo 				else if (lfs_segclean(&fsp->fi_statfsp->f_fsid,
312*69668Smargo 				    sp->sl_id) < 0)
313*69668Smargo 					perror("lfs_segclean failed");
314*69668Smargo 				printf("Completed cleaning segment %d\n", sp->sl_id);
315*69668Smargo 			}
316*69668Smargo 	}
31755857Sbostic 	free(segs);
31855857Sbostic }
31955857Sbostic 
32055857Sbostic /*
32155857Sbostic  * Segment with the highest priority get sorted to the beginning of the
32255857Sbostic  * list.  This sort assumes that empty segments always have a higher
32355857Sbostic  * cost/benefit than any utilized segment.
32455857Sbostic  */
32555857Sbostic int
32655857Sbostic cost_compare(a, b)
32755857Sbostic 	const void *a;
32855857Sbostic 	const void *b;
32955857Sbostic {
33055857Sbostic 	return (((struct seglist *)b)->sl_cost -
33155857Sbostic 	    ((struct seglist *)a)->sl_cost);
33255857Sbostic }
33355857Sbostic 
33455857Sbostic 
33555857Sbostic /*
33655857Sbostic  * Returns the number of segments to be cleaned with the elements of seglist
33755857Sbostic  * filled in.
33855857Sbostic  */
33955857Sbostic int
34055857Sbostic choose_segments(fsp, seglist, cost_func)
34155857Sbostic 	FS_INFO *fsp;
34255857Sbostic 	struct seglist *seglist;
34355857Sbostic 	int (*cost_func) __P((FS_INFO *, SEGUSE *));
34455857Sbostic {
34555857Sbostic 	struct lfs *lfsp;
34655857Sbostic 	struct seglist *sp;
34755857Sbostic 	SEGUSE *sup;
34855857Sbostic 	int i, nsegs;
34955857Sbostic 
35055857Sbostic 	lfsp = &fsp->fi_lfs;
35155857Sbostic 
35255857Sbostic #ifdef VERBOSE
35365737Sbostic 	(void)printf("Entering choose_segments\n");
35455857Sbostic #endif
35555857Sbostic 	dump_super(lfsp);
35655857Sbostic 	dump_cleaner_info(fsp->fi_cip);
35755857Sbostic 
35855857Sbostic 	for (sp = seglist, i = 0; i < lfsp->lfs_nseg; ++i) {
35955857Sbostic 		sup = SEGUSE_ENTRY(lfsp, fsp->fi_segusep, i);
36055857Sbostic 		 PRINT_SEGUSE(sup, i);
36155857Sbostic 		if (!(sup->su_flags & SEGUSE_DIRTY) ||
36255857Sbostic 		    sup->su_flags & SEGUSE_ACTIVE)
36355857Sbostic 			continue;
36455857Sbostic #ifdef VERBOSE
36565737Sbostic 		(void)printf("\tchoosing segment %d\n", i);
36655857Sbostic #endif
36755857Sbostic 		sp->sl_cost = (*cost_func)(fsp, sup);
36855857Sbostic 		sp->sl_id = i;
369*69668Smargo 		sp->sl_bytes = sup->su_nbytes;
37055857Sbostic 		++sp;
37155857Sbostic 	}
37255857Sbostic 	nsegs = sp - seglist;
37355857Sbostic 	qsort(seglist, nsegs, sizeof(struct seglist), cost_compare);
37455857Sbostic #ifdef VERBOSE
37555857Sbostic 	(void)printf("Returning %d segments\n", nsegs);
37655857Sbostic #endif
37755857Sbostic 	return (nsegs);
37855857Sbostic }
37955857Sbostic 
38055857Sbostic 
38155857Sbostic int
38255857Sbostic clean_segment(fsp, id)
38355857Sbostic 	FS_INFO *fsp;	/* file system information */
38455857Sbostic 	int id;		/* segment number */
38555857Sbostic {
38657200Smargo 	BLOCK_INFO *block_array, *bp;
38755857Sbostic 	SEGUSE *sp;
38855857Sbostic 	struct lfs *lfsp;
38955857Sbostic 	struct tossstruct t;
39055857Sbostic 	caddr_t seg_buf;
391*69668Smargo 	double util;
39257200Smargo 	int num_blocks, maxblocks, clean_blocks;
39355857Sbostic 
39455857Sbostic 	lfsp = &fsp->fi_lfs;
39555857Sbostic 	sp = SEGUSE_ENTRY(lfsp, fsp->fi_segusep, id);
39655857Sbostic 
39755857Sbostic #ifdef VERBOSE
39865737Sbostic 	(void)printf("cleaning segment %d: contains %lu bytes\n", id,
39955857Sbostic 	    sp->su_nbytes);
40055857Sbostic 	fflush(stdout);
40155857Sbostic #endif
40255857Sbostic 	/* XXX could add debugging to verify that segment is really empty */
40357200Smargo 	if (sp->su_nbytes == sp->su_nsums * LFS_SUMMARY_SIZE) {
40457200Smargo 		++cleaner_stats.segs_empty;
40555857Sbostic 		return (0);
40657200Smargo 	}
40755857Sbostic 
40855857Sbostic 	/* map the segment into a buffer */
40957200Smargo 	if (mmap_segment(fsp, id, &seg_buf, do_mmap) < 0) {
41055857Sbostic 		err(0, "mmap_segment failed");
41157200Smargo 		++cleaner_stats.segs_error;
41255857Sbostic 		return (-1);
41355857Sbostic 	}
41455857Sbostic 	/* get a list of blocks that are contained by the segment */
41555931Sbostic 	if (lfs_segmapv(fsp, id, seg_buf, &block_array, &num_blocks) < 0) {
41655857Sbostic 		err(0, "clean_segment: lfs_segmapv failed");
41757200Smargo 		++cleaner_stats.segs_error;
41855857Sbostic 		return (-1);
41955857Sbostic 	}
42057200Smargo 	cleaner_stats.blocks_read += fsp->fi_lfs.lfs_ssize;
42155857Sbostic 
42255857Sbostic #ifdef VERBOSE
42365737Sbostic 	(void)printf("lfs_segmapv returned %d blocks\n", num_blocks);
42465737Sbostic 	fflush(stdout);
42555857Sbostic #endif
42655857Sbostic 
42755857Sbostic 	/* get the current disk address of blocks contained by the segment */
42865737Sbostic 	if (lfs_bmapv(&fsp->fi_statfsp->f_fsid, block_array, num_blocks) < 0) {
42955857Sbostic 		perror("clean_segment: lfs_bmapv failed\n");
43057200Smargo 		++cleaner_stats.segs_error;
43155857Sbostic 		return -1;
43255857Sbostic 	}
43355857Sbostic 
43455857Sbostic 	/* Now toss any blocks not in the current segment */
43555857Sbostic 	t.lfs = lfsp;
43655857Sbostic 	t.seg = id;
43755857Sbostic 	toss(block_array, &num_blocks, sizeof(BLOCK_INFO), bi_tossold, &t);
43855857Sbostic 
43955857Sbostic 	/* Check if last element should be tossed */
44055857Sbostic 	if (num_blocks && bi_tossold(&t, block_array + num_blocks - 1, NULL))
44155857Sbostic 		--num_blocks;
44255857Sbostic 
44355857Sbostic #ifdef VERBOSE
44455857Sbostic 	{
44555857Sbostic 		BLOCK_INFO *_bip;
44655857Sbostic 		u_long *lp;
44755857Sbostic 		int i;
44855857Sbostic 
44965737Sbostic 		(void)printf("after bmapv still have %d blocks\n", num_blocks);
45065737Sbostic 		fflush(stdout);
45155857Sbostic 		if (num_blocks)
45255857Sbostic 			printf("BLOCK INFOS\n");
45355857Sbostic 		for (_bip = block_array, i=0; i < num_blocks; ++_bip, ++i) {
45455857Sbostic 			PRINT_BINFO(_bip);
45555857Sbostic 			lp = (u_long *)_bip->bi_bp;
45655857Sbostic 		}
45755857Sbostic 	}
458*69668Smargo 
45955857Sbostic #endif
460*69668Smargo 	++cleaner_stats.segs_cleaned;
46157200Smargo 	cleaner_stats.blocks_written += num_blocks;
462*69668Smargo 	util = ((double)num_blocks / fsp->fi_lfs.lfs_ssize);
463*69668Smargo 	cleaner_stats.util_tot += util;
464*69668Smargo 	cleaner_stats.util_sos += util * util;
465*69668Smargo 
46657200Smargo 	if (do_small)
46757200Smargo 		maxblocks = MAXPHYS / fsp->fi_lfs.lfs_bsize - 1;
46857200Smargo 	else
46957200Smargo 		maxblocks = num_blocks;
47057200Smargo 
47157200Smargo 	for (bp = block_array; num_blocks > 0; bp += clean_blocks) {
47257200Smargo 		clean_blocks = maxblocks < num_blocks ? maxblocks : num_blocks;
47365737Sbostic 		if (lfs_markv(&fsp->fi_statfsp->f_fsid,
47465737Sbostic 		    bp, clean_blocks) < 0) {
47556051Sbostic 			err(0, "clean_segment: lfs_markv failed");
47657200Smargo 			++cleaner_stats.segs_error;
47755857Sbostic 			return (-1);
47855857Sbostic 		}
47957200Smargo 		num_blocks -= clean_blocks;
48057200Smargo 	}
48157200Smargo 
48255857Sbostic 	free(block_array);
48365737Sbostic 	munmap_segment(fsp, seg_buf, do_mmap);
484*69668Smargo 	if (stat_report && cleaner_stats.segs_cleaned % stat_report == 0)
485*69668Smargo 		sig_report(SIGUSR1);
48655857Sbostic 	return (0);
48755857Sbostic }
48855857Sbostic 
48955857Sbostic 
49055857Sbostic int
49155857Sbostic bi_tossold(client, a, b)
49255857Sbostic 	const void *client;
49355857Sbostic 	const void *a;
49455857Sbostic 	const void *b;
49555857Sbostic {
49655857Sbostic 	const struct tossstruct *t;
49755857Sbostic 
49855857Sbostic 	t = (struct tossstruct *)client;
49955857Sbostic 
50055857Sbostic 	return (((BLOCK_INFO *)a)->bi_daddr == LFS_UNUSED_DADDR ||
50155857Sbostic 	    datosn(t->lfs, ((BLOCK_INFO *)a)->bi_daddr) != t->seg);
50255857Sbostic }
50357200Smargo 
50457200Smargo void
50557200Smargo sig_report(sig)
50657200Smargo 	int sig;
50757200Smargo {
508*69668Smargo 	double avg;
509*69668Smargo 
51065737Sbostic 	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",
51157200Smargo 		"blocks_read    ", cleaner_stats.blocks_read,
51257200Smargo 		"blocks_written ", cleaner_stats.blocks_written,
51357200Smargo 		"segs_cleaned   ", cleaner_stats.segs_cleaned,
51457200Smargo 		"segs_empty     ", cleaner_stats.segs_empty,
51557200Smargo 		"seg_error      ", cleaner_stats.segs_error);
516*69668Smargo 	printf("\t\t%s%5.2f\n\t\t%s%5.2f\n",
517*69668Smargo 		"util_tot       ", cleaner_stats.util_tot,
518*69668Smargo 		"util_sos       ", cleaner_stats.util_sos);
519*69668Smargo 	printf("\t\tavg util: %4.2f std dev: %9.6f\n",
520*69668Smargo 		avg = cleaner_stats.util_tot / cleaner_stats.segs_cleaned,
521*69668Smargo 		cleaner_stats.util_sos / cleaner_stats.segs_cleaned - avg * avg);
522*69668Smargo 
523*69668Smargo 
52457200Smargo 	if (sig == SIGUSR2) {
52560096Sbostic 		cleaner_stats.blocks_read = 0;
52660096Sbostic 		cleaner_stats.blocks_written = 0;
52760096Sbostic 		cleaner_stats.segs_cleaned = 0;
52860096Sbostic 		cleaner_stats.segs_empty = 0;
52960096Sbostic 		cleaner_stats.segs_error = 0;
530*69668Smargo 		cleaner_stats.util_tot = 0.0;
531*69668Smargo 		cleaner_stats.util_sos = 0.0;
53257200Smargo 	}
53357200Smargo 	if (sig == SIGINT)
53457200Smargo 		exit(0);
53557200Smargo }
536