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