1*55857Sbostic #include <sys/param.h>
2*55857Sbostic #include <sys/mount.h>
3*55857Sbostic #include <sys/time.h>
4*55857Sbostic #include <ufs/ufs/dinode.h>
5*55857Sbostic #include <ufs/lfs/lfs.h>
6*55857Sbostic 
7*55857Sbostic #include <stdio.h>
8*55857Sbostic #include <stdlib.h>
9*55857Sbostic #include <unistd.h>
10*55857Sbostic 
11*55857Sbostic #include "clean.h"
12*55857Sbostic char *special = "cleanerd";
13*55857Sbostic 
14*55857Sbostic struct seglist {
15*55857Sbostic 	int sl_id;	/* segment number */
16*55857Sbostic 	int sl_cost; 	/* cleaning cost */
17*55857Sbostic 	char sl_empty;	/* is segment empty */
18*55857Sbostic };
19*55857Sbostic 
20*55857Sbostic struct tossstruct {
21*55857Sbostic 	struct lfs *lfs;
22*55857Sbostic 	int seg;
23*55857Sbostic };
24*55857Sbostic 
25*55857Sbostic /* function prototypes for system calls; not sure where they should go */
26*55857Sbostic int	 lfs_segwait __P((fsid_t, struct timeval *));
27*55857Sbostic int	 lfs_segclean __P((fsid_t, u_long));
28*55857Sbostic int	 lfs_bmapv __P((fsid_t, BLOCK_INFO *, int));
29*55857Sbostic int	 lfs_markv __P((fsid_t, BLOCK_INFO *, int, INODE_INFO *, int));
30*55857Sbostic 
31*55857Sbostic /* function prototypes */
32*55857Sbostic int	 bi_tossold __P((const void *, const void *, const void *));
33*55857Sbostic int	 choose_segments __P((FS_INFO *, struct seglist *,
34*55857Sbostic 	     int (*)(FS_INFO *, SEGUSE *)));
35*55857Sbostic void	 clean_fs __P((FS_INFO	*, int (*)(FS_INFO *, SEGUSE *)));
36*55857Sbostic int	 clean_loop __P((FS_INFO *));
37*55857Sbostic int	 clean_segment __P((FS_INFO *, int));
38*55857Sbostic int	 cost_benefit __P((FS_INFO *, SEGUSE *));
39*55857Sbostic int	 cost_compare __P((const void *, const void *));
40*55857Sbostic 
41*55857Sbostic /*
42*55857Sbostic  * Cleaning Cost Functions:
43*55857Sbostic  *
44*55857Sbostic  * These return the cost of cleaning a segment.  The higher the cost value
45*55857Sbostic  * the better it is to clean the segment, so empty segments have the highest
46*55857Sbostic  * cost.  (It is probably better to think of this as a priority value
47*55857Sbostic  * instead).
48*55857Sbostic  *
49*55857Sbostic  * This is the cost-benefit policy simulated and described in Rosenblum's
50*55857Sbostic  * 1991 SOSP paper.
51*55857Sbostic  */
52*55857Sbostic 
53*55857Sbostic int
54*55857Sbostic cost_benefit(fsp, su)
55*55857Sbostic 	FS_INFO *fsp;		/* file system information */
56*55857Sbostic 	SEGUSE *su;
57*55857Sbostic {
58*55857Sbostic 	struct lfs *lfsp;
59*55857Sbostic 	struct timeval t;
60*55857Sbostic 	int age;
61*55857Sbostic 	int live;
62*55857Sbostic 
63*55857Sbostic 	gettimeofday(&t, NULL);
64*55857Sbostic 
65*55857Sbostic 	live = su->su_nbytes;
66*55857Sbostic 	age = t.tv_sec - su->su_lastmod < 0 ? 0 : t.tv_sec - su->su_lastmod;
67*55857Sbostic 
68*55857Sbostic 	lfsp = &fsp->fi_lfs;
69*55857Sbostic 	if (live == 0)
70*55857Sbostic 		return (t.tv_sec * lblkno(lfsp, seg_size(lfsp)));
71*55857Sbostic 	else {
72*55857Sbostic 		/*
73*55857Sbostic 		 * from lfsSegUsage.c (Mendel's code).
74*55857Sbostic 		 * priority calculation is done using INTEGER arithmetic.
75*55857Sbostic 		 * sizes are in BLOCKS (that is why we use lblkno below).
76*55857Sbostic 		 * age is in seconds.
77*55857Sbostic 		 *
78*55857Sbostic 		 * priority = ((seg_size - live) * age) / (seg_size + live)
79*55857Sbostic 		 */
80*55857Sbostic #ifdef VERBOSE
81*55857Sbostic 		if (live < 0 || live > seg_size(lfsp)) {
82*55857Sbostic 			err(0, "Bad segusage count: %d", live);
83*55857Sbostic 			live = 0;
84*55857Sbostic 		}
85*55857Sbostic #endif
86*55857Sbostic 		return (lblkno(lfsp, seg_size(lfsp) - live) * age)
87*55857Sbostic 			/ lblkno(lfsp, seg_size(lfsp) + live);
88*55857Sbostic 	}
89*55857Sbostic }
90*55857Sbostic 
91*55857Sbostic main(argc, argv)
92*55857Sbostic 	int argc;
93*55857Sbostic 	char *argv[];
94*55857Sbostic {
95*55857Sbostic 	FS_INFO	*lfp, *fsp;
96*55857Sbostic 	struct statfs *lstatfsp;	/* file system stats */
97*55857Sbostic 	struct timeval timeout;		/* sleep timeout */
98*55857Sbostic 	fsid_t fsid;
99*55857Sbostic 	int count;			/* number of file systems */
100*55857Sbostic 	int i;
101*55857Sbostic 
102*55857Sbostic 	count = fs_getmntinfo(&lstatfsp, MOUNT_LFS);
103*55857Sbostic 
104*55857Sbostic 	timeout.tv_sec = 5*60; /* five minutes */
105*55857Sbostic 	timeout.tv_usec = 0;
106*55857Sbostic 	fsid.val[0] = 0;
107*55857Sbostic 	fsid.val[1] = 0;
108*55857Sbostic 
109*55857Sbostic 	for (fsp = get_fs_info(lstatfsp, count); ; reread_fs_info(fsp, count)) {
110*55857Sbostic 		for (lfp = fsp, i = 0; i < count; ++lfp, ++i)
111*55857Sbostic 			clean_loop(lfp);
112*55857Sbostic 
113*55857Sbostic #ifdef VERBOSE
114*55857Sbostic 		(void)printf("Cleaner going to sleep.\n");
115*55857Sbostic #endif
116*55857Sbostic 		if (lfs_segwait(fsid, &timeout) < 0)
117*55857Sbostic 			err(0, "lfs_segwait: returned error\n");
118*55857Sbostic #ifdef VERBOSE
119*55857Sbostic 		(void)printf("Cleaner waking up.\n");
120*55857Sbostic #endif
121*55857Sbostic 	}
122*55857Sbostic }
123*55857Sbostic 
124*55857Sbostic /* return the number of segments cleaned */
125*55857Sbostic int
126*55857Sbostic clean_loop(fsp)
127*55857Sbostic 	FS_INFO	*fsp;	/* file system information */
128*55857Sbostic {
129*55857Sbostic 	double loadavg[MAXLOADS];
130*55857Sbostic 	time_t	now;
131*55857Sbostic 	u_long max_free_segs;
132*55857Sbostic 
133*55857Sbostic         /*
134*55857Sbostic 	 * Compute the maximum possible number of free segments, given the
135*55857Sbostic 	 * number of free blocks.
136*55857Sbostic 	 */
137*55857Sbostic 	max_free_segs = fsp->fi_statfsp->f_bfree / fsp->fi_lfs.lfs_ssize;
138*55857Sbostic 
139*55857Sbostic 	/*
140*55857Sbostic 	 * We will clean if there are not enough free blocks or total clean
141*55857Sbostic 	 * space is less than BUSY_LIM % of possible clean space.
142*55857Sbostic 	 */
143*55857Sbostic 	now = time((time_t *)NULL);
144*55857Sbostic 	if (fsp->fi_cip->clean <= MIN_SEGS(&fsp->fi_lfs) ||
145*55857Sbostic 	    fsp->fi_cip->clean < max_free_segs * BUSY_LIM) {
146*55857Sbostic 		clean_fs(fsp, cost_benefit);
147*55857Sbostic 		printf("Cleaner Running  at %s (need space)\n",
148*55857Sbostic 		    ctime(&now));
149*55857Sbostic 		return (1);
150*55857Sbostic 	} else {
151*55857Sbostic 	        /*
152*55857Sbostic 		 * We will also clean if the system is reasonably idle and
153*55857Sbostic 		 * the total clean space is less then IDLE_LIM % of possible
154*55857Sbostic 		 * clean space.
155*55857Sbostic 		 */
156*55857Sbostic 		if (getloadavg(loadavg, MAXLOADS) == -1) {
157*55857Sbostic 			perror("getloadavg: failed\n");
158*55857Sbostic 			return (-1);
159*55857Sbostic 		}
160*55857Sbostic 		if (loadavg[ONE_MIN] == 0.0 && loadavg[FIVE_MIN] &&
161*55857Sbostic 		    fsp->fi_cip->clean < max_free_segs * IDLE_LIM) {
162*55857Sbostic 		        clean_fs(fsp, cost_benefit);
163*55857Sbostic 			printf("Cleaner Running  at %s (system idle)\n",
164*55857Sbostic 			    ctime(&now));
165*55857Sbostic 			return (1);
166*55857Sbostic 		}
167*55857Sbostic 	}
168*55857Sbostic 	printf("Cleaner Not Running at %s\n", ctime(&now));
169*55857Sbostic 	return (0);
170*55857Sbostic }
171*55857Sbostic 
172*55857Sbostic 
173*55857Sbostic void
174*55857Sbostic clean_fs(fsp, cost_func)
175*55857Sbostic 	FS_INFO	*fsp;	/* file system information */
176*55857Sbostic 	int (*cost_func) __P((FS_INFO *, SEGUSE *));
177*55857Sbostic {
178*55857Sbostic 	struct seglist *segs, *sp;
179*55857Sbostic 	int i;
180*55857Sbostic 
181*55857Sbostic 	if ((segs = malloc(fsp->fi_lfs.lfs_nseg * sizeof(struct seglist)))
182*55857Sbostic 	    == NULL) {
183*55857Sbostic 		err(0, "malloc failed");
184*55857Sbostic 		return;
185*55857Sbostic 	}
186*55857Sbostic 	i = choose_segments(fsp, segs, cost_func);
187*55857Sbostic #ifdef VERBOSE
188*55857Sbostic 	printf("clean_fs: cleaning %d segments in file system %s\n",
189*55857Sbostic 		i, fsp->fi_statfsp->f_mntonname);
190*55857Sbostic 	fflush(stdout);
191*55857Sbostic #endif
192*55857Sbostic 	if (i)
193*55857Sbostic 		for (i = MIN(i, NUM_TO_CLEAN(fsp)), sp = segs; i-- ; ++sp)
194*55857Sbostic 			if (clean_segment(fsp, sp->sl_id) < 0)
195*55857Sbostic 				perror("clean_segment failed");
196*55857Sbostic 			else if (lfs_segclean (fsp->fi_statfsp->f_fsid,
197*55857Sbostic 			    sp->sl_id) < 0)
198*55857Sbostic 				perror("lfs_segclean failed");
199*55857Sbostic 	free(segs);
200*55857Sbostic }
201*55857Sbostic 
202*55857Sbostic /*
203*55857Sbostic  * Segment with the highest priority get sorted to the beginning of the
204*55857Sbostic  * list.  This sort assumes that empty segments always have a higher
205*55857Sbostic  * cost/benefit than any utilized segment.
206*55857Sbostic  */
207*55857Sbostic int
208*55857Sbostic cost_compare(a, b)
209*55857Sbostic 	const void *a;
210*55857Sbostic 	const void *b;
211*55857Sbostic {
212*55857Sbostic 	return (((struct seglist *)b)->sl_cost -
213*55857Sbostic 	    ((struct seglist *)a)->sl_cost);
214*55857Sbostic }
215*55857Sbostic 
216*55857Sbostic 
217*55857Sbostic /*
218*55857Sbostic  * Returns the number of segments to be cleaned with the elements of seglist
219*55857Sbostic  * filled in.
220*55857Sbostic  */
221*55857Sbostic int
222*55857Sbostic choose_segments(fsp, seglist, cost_func)
223*55857Sbostic 	FS_INFO *fsp;
224*55857Sbostic 	struct seglist *seglist;
225*55857Sbostic 	int (*cost_func) __P((FS_INFO *, SEGUSE *));
226*55857Sbostic {
227*55857Sbostic 	struct lfs *lfsp;
228*55857Sbostic 	struct seglist *sp;
229*55857Sbostic 	SEGUSE *sup;
230*55857Sbostic 	int i, nsegs;
231*55857Sbostic 
232*55857Sbostic 	lfsp = &fsp->fi_lfs;
233*55857Sbostic 
234*55857Sbostic #ifdef VERBOSE
235*55857Sbostic 	(void) printf("Entering choose_segments\n");
236*55857Sbostic #endif
237*55857Sbostic 	dump_super(lfsp);
238*55857Sbostic 	dump_cleaner_info(fsp->fi_cip);
239*55857Sbostic 
240*55857Sbostic 	for (sp = seglist, i = 0; i < lfsp->lfs_nseg; ++i) {
241*55857Sbostic 		sup = SEGUSE_ENTRY(lfsp, fsp->fi_segusep, i);
242*55857Sbostic 		 PRINT_SEGUSE(sup, i);
243*55857Sbostic 		if (!(sup->su_flags & SEGUSE_DIRTY) ||
244*55857Sbostic 		    sup->su_flags & SEGUSE_ACTIVE)
245*55857Sbostic 			continue;
246*55857Sbostic #ifdef VERBOSE
247*55857Sbostic 		(void) printf("\tchoosing segment %d\n", i);
248*55857Sbostic #endif
249*55857Sbostic 		sp->sl_cost = (*cost_func)(fsp, sup);
250*55857Sbostic 		sp->sl_id = i;
251*55857Sbostic 		sp->sl_empty = sup->su_nbytes ? 0 : 1;
252*55857Sbostic 		++sp;
253*55857Sbostic 	}
254*55857Sbostic 	nsegs = sp - seglist;
255*55857Sbostic 	qsort(seglist, nsegs, sizeof(struct seglist), cost_compare);
256*55857Sbostic #ifdef VERBOSE
257*55857Sbostic 	(void)printf("Returning %d segments\n", nsegs);
258*55857Sbostic #endif
259*55857Sbostic 	return (nsegs);
260*55857Sbostic }
261*55857Sbostic 
262*55857Sbostic 
263*55857Sbostic int
264*55857Sbostic clean_segment(fsp, id)
265*55857Sbostic 	FS_INFO *fsp;	/* file system information */
266*55857Sbostic 	int id;		/* segment number */
267*55857Sbostic {
268*55857Sbostic 	BLOCK_INFO *block_array;
269*55857Sbostic 	INODE_INFO *inode_array;
270*55857Sbostic 	SEGUSE *sp;
271*55857Sbostic 	struct lfs *lfsp;
272*55857Sbostic 	struct tossstruct t;
273*55857Sbostic 	caddr_t seg_buf;
274*55857Sbostic 	int num_inodes, num_blocks;
275*55857Sbostic 
276*55857Sbostic 	lfsp = &fsp->fi_lfs;
277*55857Sbostic 	sp = SEGUSE_ENTRY(lfsp, fsp->fi_segusep, id);
278*55857Sbostic 
279*55857Sbostic #ifdef VERBOSE
280*55857Sbostic 	(void) printf("cleaning segment %d: contains %lu bytes\n", id,
281*55857Sbostic 	    sp->su_nbytes);
282*55857Sbostic 	fflush(stdout);
283*55857Sbostic #endif
284*55857Sbostic 	/* XXX could add debugging to verify that segment is really empty */
285*55857Sbostic 	if (sp->su_nbytes == sp->su_nsums * LFS_SUMMARY_SIZE)
286*55857Sbostic 		return (0);
287*55857Sbostic 
288*55857Sbostic 	/* map the segment into a buffer */
289*55857Sbostic 	if (mmap_segment(fsp, id, &seg_buf) < 0) {
290*55857Sbostic 		err(0, "mmap_segment failed");
291*55857Sbostic 		return (-1);
292*55857Sbostic 	}
293*55857Sbostic 	/* get a list of blocks that are contained by the segment */
294*55857Sbostic 	if (lfs_segmapv(fsp, id, seg_buf, &block_array, &num_blocks,
295*55857Sbostic 	    &inode_array, &num_inodes) < 0) {
296*55857Sbostic 		err(0, "clean_segment: lfs_segmapv failed");
297*55857Sbostic 		return (-1);
298*55857Sbostic 	}
299*55857Sbostic 
300*55857Sbostic #ifdef VERBOSE
301*55857Sbostic 	(void) printf("lfs_segmapv returned %d blocks and %d inodes\n",
302*55857Sbostic 	    num_blocks, num_inodes);
303*55857Sbostic 	fflush (stdout);
304*55857Sbostic #endif
305*55857Sbostic 
306*55857Sbostic 	/* get the current disk address of blocks contained by the segment */
307*55857Sbostic 	if (lfs_bmapv(fsp->fi_statfsp->f_fsid, block_array, num_blocks) < 0) {
308*55857Sbostic 		perror("clean_segment: lfs_bmapv failed\n");
309*55857Sbostic 		return -1;
310*55857Sbostic 	}
311*55857Sbostic 
312*55857Sbostic 	/* Now toss any blocks not in the current segment */
313*55857Sbostic 	t.lfs = lfsp;
314*55857Sbostic 	t.seg = id;
315*55857Sbostic 	toss(block_array, &num_blocks, sizeof(BLOCK_INFO), bi_tossold, &t);
316*55857Sbostic 
317*55857Sbostic 	/* Check if last element should be tossed */
318*55857Sbostic 	if (num_blocks && bi_tossold(&t, block_array + num_blocks - 1, NULL))
319*55857Sbostic 		--num_blocks;
320*55857Sbostic 
321*55857Sbostic #ifdef VERBOSE
322*55857Sbostic 	{
323*55857Sbostic 		BLOCK_INFO *_bip;
324*55857Sbostic 		INODE_INFO *_iip;
325*55857Sbostic 		u_long *lp;
326*55857Sbostic 		int i;
327*55857Sbostic 
328*55857Sbostic 		(void) printf("after bmapv still have %d blocks\n", num_blocks);
329*55857Sbostic 		fflush (stdout);
330*55857Sbostic 		if (num_blocks)
331*55857Sbostic 			printf("BLOCK INFOS\n");
332*55857Sbostic 		for (_bip = block_array, i=0; i < num_blocks; ++_bip, ++i) {
333*55857Sbostic 			PRINT_BINFO(_bip);
334*55857Sbostic 			lp = (u_long *)_bip->bi_bp;
335*55857Sbostic 		}
336*55857Sbostic 		if (num_inodes)
337*55857Sbostic 			printf("INODE INFOS\n");
338*55857Sbostic 		for (_iip = inode_array, i=0; i < num_inodes; ++_iip, ++i)
339*55857Sbostic 			PRINT_IINFO(1, _iip);
340*55857Sbostic 	}
341*55857Sbostic #endif
342*55857Sbostic 	/* rewrite the live data */
343*55857Sbostic 	if (num_blocks > 0 || num_inodes > 0)
344*55857Sbostic 		if (lfs_markv(fsp->fi_statfsp->f_fsid, block_array, num_blocks,
345*55857Sbostic 		    inode_array, num_inodes) < 0) {
346*55857Sbostic 			err(0, "clean_segment: lfs_bmapv failed");
347*55857Sbostic 			return (-1);
348*55857Sbostic 		}
349*55857Sbostic 	free(block_array);
350*55857Sbostic 	free(inode_array);
351*55857Sbostic 	munmap_segment(fsp, seg_buf);
352*55857Sbostic 
353*55857Sbostic 	return (0);
354*55857Sbostic }
355*55857Sbostic 
356*55857Sbostic 
357*55857Sbostic int
358*55857Sbostic bi_tossold(client, a, b)
359*55857Sbostic 	const void *client;
360*55857Sbostic 	const void *a;
361*55857Sbostic 	const void *b;
362*55857Sbostic {
363*55857Sbostic 	const struct tossstruct *t;
364*55857Sbostic 
365*55857Sbostic 	t = (struct tossstruct *)client;
366*55857Sbostic 
367*55857Sbostic 	return (((BLOCK_INFO *)a)->bi_daddr == LFS_UNUSED_DADDR ||
368*55857Sbostic 	    datosn(t->lfs, ((BLOCK_INFO *)a)->bi_daddr) != t->seg);
369*55857Sbostic }
370