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*56030Sbostic static char sccsid[] = "@(#)cleanerd.c	5.3 (Berkeley) 08/25/92";
1655860Sbostic #endif /* not lint */
1755860Sbostic 
1855857Sbostic #include <sys/param.h>
1955857Sbostic #include <sys/mount.h>
2055857Sbostic #include <sys/time.h>
2155860Sbostic 
2255857Sbostic #include <ufs/ufs/dinode.h>
2355857Sbostic #include <ufs/lfs/lfs.h>
2455857Sbostic 
2555857Sbostic #include <stdio.h>
2655857Sbostic #include <stdlib.h>
2755857Sbostic #include <unistd.h>
2855857Sbostic 
2955857Sbostic #include "clean.h"
3055857Sbostic char *special = "cleanerd";
3155857Sbostic 
3255857Sbostic struct seglist {
3355857Sbostic 	int sl_id;	/* segment number */
3455857Sbostic 	int sl_cost; 	/* cleaning cost */
3555857Sbostic 	char sl_empty;	/* is segment empty */
3655857Sbostic };
3755857Sbostic 
3855857Sbostic struct tossstruct {
3955857Sbostic 	struct lfs *lfs;
4055857Sbostic 	int seg;
4155857Sbostic };
4255857Sbostic 
4355857Sbostic /* function prototypes for system calls; not sure where they should go */
4455857Sbostic int	 lfs_segwait __P((fsid_t, struct timeval *));
4555857Sbostic int	 lfs_segclean __P((fsid_t, u_long));
4655857Sbostic int	 lfs_bmapv __P((fsid_t, BLOCK_INFO *, int));
4755931Sbostic int	 lfs_markv __P((fsid_t, BLOCK_INFO *, int));
4855857Sbostic 
4955857Sbostic /* function prototypes */
5055857Sbostic int	 bi_tossold __P((const void *, const void *, const void *));
5155857Sbostic int	 choose_segments __P((FS_INFO *, struct seglist *,
5255857Sbostic 	     int (*)(FS_INFO *, SEGUSE *)));
5355857Sbostic void	 clean_fs __P((FS_INFO	*, int (*)(FS_INFO *, SEGUSE *)));
5455857Sbostic int	 clean_loop __P((FS_INFO *));
5555857Sbostic int	 clean_segment __P((FS_INFO *, int));
5655857Sbostic int	 cost_benefit __P((FS_INFO *, SEGUSE *));
5755857Sbostic int	 cost_compare __P((const void *, const void *));
5855857Sbostic 
5955857Sbostic /*
6055857Sbostic  * Cleaning Cost Functions:
6155857Sbostic  *
6255857Sbostic  * These return the cost of cleaning a segment.  The higher the cost value
6355857Sbostic  * the better it is to clean the segment, so empty segments have the highest
6455857Sbostic  * cost.  (It is probably better to think of this as a priority value
6555857Sbostic  * instead).
6655857Sbostic  *
6755857Sbostic  * This is the cost-benefit policy simulated and described in Rosenblum's
6855857Sbostic  * 1991 SOSP paper.
6955857Sbostic  */
7055857Sbostic 
7155857Sbostic int
7255857Sbostic cost_benefit(fsp, su)
7355857Sbostic 	FS_INFO *fsp;		/* file system information */
7455857Sbostic 	SEGUSE *su;
7555857Sbostic {
7655857Sbostic 	struct lfs *lfsp;
7755857Sbostic 	struct timeval t;
7855857Sbostic 	int age;
7955857Sbostic 	int live;
8055857Sbostic 
8155857Sbostic 	gettimeofday(&t, NULL);
8255857Sbostic 
8355857Sbostic 	live = su->su_nbytes;
8455857Sbostic 	age = t.tv_sec - su->su_lastmod < 0 ? 0 : t.tv_sec - su->su_lastmod;
8555857Sbostic 
8655857Sbostic 	lfsp = &fsp->fi_lfs;
8755857Sbostic 	if (live == 0)
8855857Sbostic 		return (t.tv_sec * lblkno(lfsp, seg_size(lfsp)));
8955857Sbostic 	else {
9055857Sbostic 		/*
9155857Sbostic 		 * from lfsSegUsage.c (Mendel's code).
9255857Sbostic 		 * priority calculation is done using INTEGER arithmetic.
9355857Sbostic 		 * sizes are in BLOCKS (that is why we use lblkno below).
9455857Sbostic 		 * age is in seconds.
9555857Sbostic 		 *
9655857Sbostic 		 * priority = ((seg_size - live) * age) / (seg_size + live)
9755857Sbostic 		 */
9855857Sbostic #ifdef VERBOSE
9955857Sbostic 		if (live < 0 || live > seg_size(lfsp)) {
10055857Sbostic 			err(0, "Bad segusage count: %d", live);
10155857Sbostic 			live = 0;
10255857Sbostic 		}
10355857Sbostic #endif
10455857Sbostic 		return (lblkno(lfsp, seg_size(lfsp) - live) * age)
10555857Sbostic 			/ lblkno(lfsp, seg_size(lfsp) + live);
10655857Sbostic 	}
10755857Sbostic }
10855857Sbostic 
10955860Sbostic int
11055857Sbostic main(argc, argv)
11155857Sbostic 	int argc;
11255857Sbostic 	char *argv[];
11355857Sbostic {
11455857Sbostic 	FS_INFO	*lfp, *fsp;
11555857Sbostic 	struct statfs *lstatfsp;	/* file system stats */
11655857Sbostic 	struct timeval timeout;		/* sleep timeout */
11755857Sbostic 	fsid_t fsid;
11855857Sbostic 	int count;			/* number of file systems */
119*56030Sbostic 	int i, nclean;
12055857Sbostic 
12155857Sbostic 	count = fs_getmntinfo(&lstatfsp, MOUNT_LFS);
12255857Sbostic 
12355857Sbostic 	timeout.tv_sec = 5*60; /* five minutes */
12455857Sbostic 	timeout.tv_usec = 0;
12555857Sbostic 	fsid.val[0] = 0;
12655857Sbostic 	fsid.val[1] = 0;
12755857Sbostic 
12855857Sbostic 	for (fsp = get_fs_info(lstatfsp, count); ; reread_fs_info(fsp, count)) {
129*56030Sbostic 		for (nclean = 0, lfp = fsp, i = 0; i < count; ++lfp, ++i)
130*56030Sbostic 			nclean += clean_loop(lfp);
131*56030Sbostic 		/*
132*56030Sbostic 		 * If some file systems were actually cleaned, run again
133*56030Sbostic 		 * to make sure that some nasty process hasn't just
134*56030Sbostic 		 * filled the disk system up.
135*56030Sbostic 		 */
136*56030Sbostic 		if (nclean)
137*56030Sbostic 			continue;
13855857Sbostic 
13955857Sbostic #ifdef VERBOSE
14055857Sbostic 		(void)printf("Cleaner going to sleep.\n");
14155857Sbostic #endif
14255857Sbostic 		if (lfs_segwait(fsid, &timeout) < 0)
14355857Sbostic 			err(0, "lfs_segwait: returned error\n");
14455857Sbostic #ifdef VERBOSE
14555857Sbostic 		(void)printf("Cleaner waking up.\n");
14655857Sbostic #endif
14755857Sbostic 	}
14855857Sbostic }
14955857Sbostic 
15055857Sbostic /* return the number of segments cleaned */
15155857Sbostic int
15255857Sbostic clean_loop(fsp)
15355857Sbostic 	FS_INFO	*fsp;	/* file system information */
15455857Sbostic {
15555857Sbostic 	double loadavg[MAXLOADS];
15655857Sbostic 	time_t	now;
15755857Sbostic 	u_long max_free_segs;
15855857Sbostic 
15955857Sbostic         /*
16055857Sbostic 	 * Compute the maximum possible number of free segments, given the
16155857Sbostic 	 * number of free blocks.
16255857Sbostic 	 */
16355857Sbostic 	max_free_segs = fsp->fi_statfsp->f_bfree / fsp->fi_lfs.lfs_ssize;
16455857Sbostic 
16555857Sbostic 	/*
16655857Sbostic 	 * We will clean if there are not enough free blocks or total clean
16755857Sbostic 	 * space is less than BUSY_LIM % of possible clean space.
16855857Sbostic 	 */
16955857Sbostic 	now = time((time_t *)NULL);
17055857Sbostic 	if (fsp->fi_cip->clean <= MIN_SEGS(&fsp->fi_lfs) ||
17155857Sbostic 	    fsp->fi_cip->clean < max_free_segs * BUSY_LIM) {
17255857Sbostic 		printf("Cleaner Running  at %s (need space)\n",
17355857Sbostic 		    ctime(&now));
174*56030Sbostic 		clean_fs(fsp, cost_benefit);
17555857Sbostic 		return (1);
17655857Sbostic 	} else {
17755857Sbostic 	        /*
17855857Sbostic 		 * We will also clean if the system is reasonably idle and
17955857Sbostic 		 * the total clean space is less then IDLE_LIM % of possible
18055857Sbostic 		 * clean space.
18155857Sbostic 		 */
18255857Sbostic 		if (getloadavg(loadavg, MAXLOADS) == -1) {
18355857Sbostic 			perror("getloadavg: failed\n");
18455857Sbostic 			return (-1);
18555857Sbostic 		}
18655857Sbostic 		if (loadavg[ONE_MIN] == 0.0 && loadavg[FIVE_MIN] &&
18755857Sbostic 		    fsp->fi_cip->clean < max_free_segs * IDLE_LIM) {
18855857Sbostic 		        clean_fs(fsp, cost_benefit);
18955857Sbostic 			printf("Cleaner Running  at %s (system idle)\n",
19055857Sbostic 			    ctime(&now));
19155857Sbostic 			return (1);
19255857Sbostic 		}
19355857Sbostic 	}
19455857Sbostic 	printf("Cleaner Not Running at %s\n", ctime(&now));
19555857Sbostic 	return (0);
19655857Sbostic }
19755857Sbostic 
19855857Sbostic 
19955857Sbostic void
20055857Sbostic clean_fs(fsp, cost_func)
20155857Sbostic 	FS_INFO	*fsp;	/* file system information */
20255857Sbostic 	int (*cost_func) __P((FS_INFO *, SEGUSE *));
20355857Sbostic {
20455857Sbostic 	struct seglist *segs, *sp;
20555857Sbostic 	int i;
20655857Sbostic 
20755857Sbostic 	if ((segs = malloc(fsp->fi_lfs.lfs_nseg * sizeof(struct seglist)))
20855857Sbostic 	    == NULL) {
20955857Sbostic 		err(0, "malloc failed");
21055857Sbostic 		return;
21155857Sbostic 	}
21255857Sbostic 	i = choose_segments(fsp, segs, cost_func);
21355857Sbostic #ifdef VERBOSE
21455857Sbostic 	printf("clean_fs: cleaning %d segments in file system %s\n",
21555857Sbostic 		i, fsp->fi_statfsp->f_mntonname);
21655857Sbostic 	fflush(stdout);
21755857Sbostic #endif
21855857Sbostic 	if (i)
21955857Sbostic 		for (i = MIN(i, NUM_TO_CLEAN(fsp)), sp = segs; i-- ; ++sp)
22055857Sbostic 			if (clean_segment(fsp, sp->sl_id) < 0)
22155857Sbostic 				perror("clean_segment failed");
22255857Sbostic 			else if (lfs_segclean (fsp->fi_statfsp->f_fsid,
22355857Sbostic 			    sp->sl_id) < 0)
22455857Sbostic 				perror("lfs_segclean failed");
22555857Sbostic 	free(segs);
22655857Sbostic }
22755857Sbostic 
22855857Sbostic /*
22955857Sbostic  * Segment with the highest priority get sorted to the beginning of the
23055857Sbostic  * list.  This sort assumes that empty segments always have a higher
23155857Sbostic  * cost/benefit than any utilized segment.
23255857Sbostic  */
23355857Sbostic int
23455857Sbostic cost_compare(a, b)
23555857Sbostic 	const void *a;
23655857Sbostic 	const void *b;
23755857Sbostic {
23855857Sbostic 	return (((struct seglist *)b)->sl_cost -
23955857Sbostic 	    ((struct seglist *)a)->sl_cost);
24055857Sbostic }
24155857Sbostic 
24255857Sbostic 
24355857Sbostic /*
24455857Sbostic  * Returns the number of segments to be cleaned with the elements of seglist
24555857Sbostic  * filled in.
24655857Sbostic  */
24755857Sbostic int
24855857Sbostic choose_segments(fsp, seglist, cost_func)
24955857Sbostic 	FS_INFO *fsp;
25055857Sbostic 	struct seglist *seglist;
25155857Sbostic 	int (*cost_func) __P((FS_INFO *, SEGUSE *));
25255857Sbostic {
25355857Sbostic 	struct lfs *lfsp;
25455857Sbostic 	struct seglist *sp;
25555857Sbostic 	SEGUSE *sup;
25655857Sbostic 	int i, nsegs;
25755857Sbostic 
25855857Sbostic 	lfsp = &fsp->fi_lfs;
25955857Sbostic 
26055857Sbostic #ifdef VERBOSE
26155857Sbostic 	(void) printf("Entering choose_segments\n");
26255857Sbostic #endif
26355857Sbostic 	dump_super(lfsp);
26455857Sbostic 	dump_cleaner_info(fsp->fi_cip);
26555857Sbostic 
26655857Sbostic 	for (sp = seglist, i = 0; i < lfsp->lfs_nseg; ++i) {
26755857Sbostic 		sup = SEGUSE_ENTRY(lfsp, fsp->fi_segusep, i);
26855857Sbostic 		 PRINT_SEGUSE(sup, i);
26955857Sbostic 		if (!(sup->su_flags & SEGUSE_DIRTY) ||
27055857Sbostic 		    sup->su_flags & SEGUSE_ACTIVE)
27155857Sbostic 			continue;
27255857Sbostic #ifdef VERBOSE
27355857Sbostic 		(void) printf("\tchoosing segment %d\n", i);
27455857Sbostic #endif
27555857Sbostic 		sp->sl_cost = (*cost_func)(fsp, sup);
27655857Sbostic 		sp->sl_id = i;
27755857Sbostic 		sp->sl_empty = sup->su_nbytes ? 0 : 1;
27855857Sbostic 		++sp;
27955857Sbostic 	}
28055857Sbostic 	nsegs = sp - seglist;
28155857Sbostic 	qsort(seglist, nsegs, sizeof(struct seglist), cost_compare);
28255857Sbostic #ifdef VERBOSE
28355857Sbostic 	(void)printf("Returning %d segments\n", nsegs);
28455857Sbostic #endif
28555857Sbostic 	return (nsegs);
28655857Sbostic }
28755857Sbostic 
28855857Sbostic 
28955857Sbostic int
29055857Sbostic clean_segment(fsp, id)
29155857Sbostic 	FS_INFO *fsp;	/* file system information */
29255857Sbostic 	int id;		/* segment number */
29355857Sbostic {
29455857Sbostic 	BLOCK_INFO *block_array;
29555857Sbostic 	SEGUSE *sp;
29655857Sbostic 	struct lfs *lfsp;
29755857Sbostic 	struct tossstruct t;
29855857Sbostic 	caddr_t seg_buf;
29955931Sbostic 	int num_blocks;
30055857Sbostic 
30155857Sbostic 	lfsp = &fsp->fi_lfs;
30255857Sbostic 	sp = SEGUSE_ENTRY(lfsp, fsp->fi_segusep, id);
30355857Sbostic 
30455857Sbostic #ifdef VERBOSE
30555857Sbostic 	(void) printf("cleaning segment %d: contains %lu bytes\n", id,
30655857Sbostic 	    sp->su_nbytes);
30755857Sbostic 	fflush(stdout);
30855857Sbostic #endif
30955857Sbostic 	/* XXX could add debugging to verify that segment is really empty */
31055857Sbostic 	if (sp->su_nbytes == sp->su_nsums * LFS_SUMMARY_SIZE)
31155857Sbostic 		return (0);
31255857Sbostic 
31355857Sbostic 	/* map the segment into a buffer */
31455857Sbostic 	if (mmap_segment(fsp, id, &seg_buf) < 0) {
31555857Sbostic 		err(0, "mmap_segment failed");
31655857Sbostic 		return (-1);
31755857Sbostic 	}
31855857Sbostic 	/* get a list of blocks that are contained by the segment */
31955931Sbostic 	if (lfs_segmapv(fsp, id, seg_buf, &block_array, &num_blocks) < 0) {
32055857Sbostic 		err(0, "clean_segment: lfs_segmapv failed");
32155857Sbostic 		return (-1);
32255857Sbostic 	}
32355857Sbostic 
32455857Sbostic #ifdef VERBOSE
32555931Sbostic 	(void) printf("lfs_segmapv returned %d blocks\n", num_blocks);
32655857Sbostic 	fflush (stdout);
32755857Sbostic #endif
32855857Sbostic 
32955857Sbostic 	/* get the current disk address of blocks contained by the segment */
33055857Sbostic 	if (lfs_bmapv(fsp->fi_statfsp->f_fsid, block_array, num_blocks) < 0) {
33155857Sbostic 		perror("clean_segment: lfs_bmapv failed\n");
33255857Sbostic 		return -1;
33355857Sbostic 	}
33455857Sbostic 
33555857Sbostic 	/* Now toss any blocks not in the current segment */
33655857Sbostic 	t.lfs = lfsp;
33755857Sbostic 	t.seg = id;
33855857Sbostic 	toss(block_array, &num_blocks, sizeof(BLOCK_INFO), bi_tossold, &t);
33955857Sbostic 
34055857Sbostic 	/* Check if last element should be tossed */
34155857Sbostic 	if (num_blocks && bi_tossold(&t, block_array + num_blocks - 1, NULL))
34255857Sbostic 		--num_blocks;
34355857Sbostic 
34455857Sbostic #ifdef VERBOSE
34555857Sbostic 	{
34655857Sbostic 		BLOCK_INFO *_bip;
34755857Sbostic 		u_long *lp;
34855857Sbostic 		int i;
34955857Sbostic 
35055857Sbostic 		(void) printf("after bmapv still have %d blocks\n", num_blocks);
35155857Sbostic 		fflush (stdout);
35255857Sbostic 		if (num_blocks)
35355857Sbostic 			printf("BLOCK INFOS\n");
35455857Sbostic 		for (_bip = block_array, i=0; i < num_blocks; ++_bip, ++i) {
35555857Sbostic 			PRINT_BINFO(_bip);
35655857Sbostic 			lp = (u_long *)_bip->bi_bp;
35755857Sbostic 		}
35855857Sbostic 	}
35955857Sbostic #endif
36055857Sbostic 	/* rewrite the live data */
36155931Sbostic 	if (num_blocks > 0)
36255931Sbostic 		if (lfs_markv(fsp->fi_statfsp->f_fsid, block_array, num_blocks)
36355931Sbostic 		    < 0 ) {
36455857Sbostic 			err(0, "clean_segment: lfs_bmapv failed");
36555857Sbostic 			return (-1);
36655857Sbostic 		}
36755857Sbostic 	free(block_array);
36855857Sbostic 	munmap_segment(fsp, seg_buf);
36955857Sbostic 
37055857Sbostic 	return (0);
37155857Sbostic }
37255857Sbostic 
37355857Sbostic 
37455857Sbostic int
37555857Sbostic bi_tossold(client, a, b)
37655857Sbostic 	const void *client;
37755857Sbostic 	const void *a;
37855857Sbostic 	const void *b;
37955857Sbostic {
38055857Sbostic 	const struct tossstruct *t;
38155857Sbostic 
38255857Sbostic 	t = (struct tossstruct *)client;
38355857Sbostic 
38455857Sbostic 	return (((BLOCK_INFO *)a)->bi_daddr == LFS_UNUSED_DADDR ||
38555857Sbostic 	    datosn(t->lfs, ((BLOCK_INFO *)a)->bi_daddr) != t->seg);
38655857Sbostic }
387