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