Don't clean if its not possible for us to generate any new segments.
[unix-history] / usr / src / libexec / lfs_cleanerd / cleanerd.c
CommitLineData
f97378c7
KB
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
9char 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
5c39f310 15static char sccsid[] = "@(#)cleanerd.c 5.5 (Berkeley) %G%";
f97378c7
KB
16#endif /* not lint */
17
f0f9e35d
KB
18#include <sys/param.h>
19#include <sys/mount.h>
20#include <sys/time.h>
f97378c7 21
f0f9e35d
KB
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"
30char *special = "cleanerd";
31
32struct seglist {
33 int sl_id; /* segment number */
34 int sl_cost; /* cleaning cost */
35 char sl_empty; /* is segment empty */
36};
37
38struct tossstruct {
39 struct lfs *lfs;
40 int seg;
41};
42
43/* function prototypes for system calls; not sure where they should go */
44int lfs_segwait __P((fsid_t, struct timeval *));
45int lfs_segclean __P((fsid_t, u_long));
46int lfs_bmapv __P((fsid_t, BLOCK_INFO *, int));
875ef07c 47int lfs_markv __P((fsid_t, BLOCK_INFO *, int));
f0f9e35d
KB
48
49/* function prototypes */
50int bi_tossold __P((const void *, const void *, const void *));
51int choose_segments __P((FS_INFO *, struct seglist *,
52 int (*)(FS_INFO *, SEGUSE *)));
53void clean_fs __P((FS_INFO *, int (*)(FS_INFO *, SEGUSE *)));
54int clean_loop __P((FS_INFO *));
55int clean_segment __P((FS_INFO *, int));
56int cost_benefit __P((FS_INFO *, SEGUSE *));
57int 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
71int
72cost_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
f97378c7 109int
f0f9e35d
KB
110main(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 */
28cc73c5 119 int i, nclean;
f0f9e35d
KB
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)) {
28cc73c5
KB
129 for (nclean = 0, lfp = fsp, i = 0; i < count; ++lfp, ++i)
130 nclean += clean_loop(lfp);
131 /*
132 * If some file systems were actually cleaned, run again
133 * to make sure that some nasty process hasn't just
134 * filled the disk system up.
135 */
136 if (nclean)
137 continue;
f0f9e35d
KB
138
139#ifdef VERBOSE
140 (void)printf("Cleaner going to sleep.\n");
141#endif
142 if (lfs_segwait(fsid, &timeout) < 0)
143 err(0, "lfs_segwait: returned error\n");
144#ifdef VERBOSE
145 (void)printf("Cleaner waking up.\n");
146#endif
147 }
148}
149
150/* return the number of segments cleaned */
151int
152clean_loop(fsp)
153 FS_INFO *fsp; /* file system information */
154{
155 double loadavg[MAXLOADS];
156 time_t now;
157 u_long max_free_segs;
158
159 /*
160 * Compute the maximum possible number of free segments, given the
161 * number of free blocks.
162 */
163 max_free_segs = fsp->fi_statfsp->f_bfree / fsp->fi_lfs.lfs_ssize;
164
165 /*
166 * We will clean if there are not enough free blocks or total clean
167 * space is less than BUSY_LIM % of possible clean space.
168 */
169 now = time((time_t *)NULL);
5c39f310
MS
170 if (fsp->fi_cip->clean < max_free_segs &&
171 (fsp->fi_cip->clean <= MIN_SEGS(&fsp->fi_lfs) ||
172 fsp->fi_cip->clean < max_free_segs * BUSY_LIM)) {
f0f9e35d
KB
173 printf("Cleaner Running at %s (need space)\n",
174 ctime(&now));
28cc73c5 175 clean_fs(fsp, cost_benefit);
f0f9e35d
KB
176 return (1);
177 } else {
178 /*
179 * We will also clean if the system is reasonably idle and
180 * the total clean space is less then IDLE_LIM % of possible
181 * clean space.
182 */
183 if (getloadavg(loadavg, MAXLOADS) == -1) {
184 perror("getloadavg: failed\n");
185 return (-1);
186 }
187 if (loadavg[ONE_MIN] == 0.0 && loadavg[FIVE_MIN] &&
188 fsp->fi_cip->clean < max_free_segs * IDLE_LIM) {
189 clean_fs(fsp, cost_benefit);
190 printf("Cleaner Running at %s (system idle)\n",
191 ctime(&now));
192 return (1);
193 }
194 }
195 printf("Cleaner Not Running at %s\n", ctime(&now));
196 return (0);
197}
198
199
200void
201clean_fs(fsp, cost_func)
202 FS_INFO *fsp; /* file system information */
203 int (*cost_func) __P((FS_INFO *, SEGUSE *));
204{
205 struct seglist *segs, *sp;
206 int i;
207
208 if ((segs = malloc(fsp->fi_lfs.lfs_nseg * sizeof(struct seglist)))
209 == NULL) {
210 err(0, "malloc failed");
211 return;
212 }
213 i = choose_segments(fsp, segs, cost_func);
214#ifdef VERBOSE
8e5862f2 215 printf("clean_fs: found %d segments to clean in file system %s\n",
f0f9e35d
KB
216 i, fsp->fi_statfsp->f_mntonname);
217 fflush(stdout);
218#endif
219 if (i)
8e5862f2 220 for (i = MIN(i, NUM_TO_CLEAN(fsp)), sp = segs; i-- ; ++sp) {
f0f9e35d
KB
221 if (clean_segment(fsp, sp->sl_id) < 0)
222 perror("clean_segment failed");
223 else if (lfs_segclean (fsp->fi_statfsp->f_fsid,
224 sp->sl_id) < 0)
225 perror("lfs_segclean failed");
8e5862f2
KB
226#ifdef VERBOSE
227 printf("Completed cleaning segment %d\n", sp->sl_id);
228#endif
229 }
f0f9e35d
KB
230 free(segs);
231}
232
233/*
234 * Segment with the highest priority get sorted to the beginning of the
235 * list. This sort assumes that empty segments always have a higher
236 * cost/benefit than any utilized segment.
237 */
238int
239cost_compare(a, b)
240 const void *a;
241 const void *b;
242{
243 return (((struct seglist *)b)->sl_cost -
244 ((struct seglist *)a)->sl_cost);
245}
246
247
248/*
249 * Returns the number of segments to be cleaned with the elements of seglist
250 * filled in.
251 */
252int
253choose_segments(fsp, seglist, cost_func)
254 FS_INFO *fsp;
255 struct seglist *seglist;
256 int (*cost_func) __P((FS_INFO *, SEGUSE *));
257{
258 struct lfs *lfsp;
259 struct seglist *sp;
260 SEGUSE *sup;
261 int i, nsegs;
262
263 lfsp = &fsp->fi_lfs;
264
265#ifdef VERBOSE
266 (void) printf("Entering choose_segments\n");
267#endif
268 dump_super(lfsp);
269 dump_cleaner_info(fsp->fi_cip);
270
271 for (sp = seglist, i = 0; i < lfsp->lfs_nseg; ++i) {
272 sup = SEGUSE_ENTRY(lfsp, fsp->fi_segusep, i);
273 PRINT_SEGUSE(sup, i);
274 if (!(sup->su_flags & SEGUSE_DIRTY) ||
275 sup->su_flags & SEGUSE_ACTIVE)
276 continue;
277#ifdef VERBOSE
278 (void) printf("\tchoosing segment %d\n", i);
279#endif
280 sp->sl_cost = (*cost_func)(fsp, sup);
281 sp->sl_id = i;
282 sp->sl_empty = sup->su_nbytes ? 0 : 1;
283 ++sp;
284 }
285 nsegs = sp - seglist;
286 qsort(seglist, nsegs, sizeof(struct seglist), cost_compare);
287#ifdef VERBOSE
288 (void)printf("Returning %d segments\n", nsegs);
289#endif
290 return (nsegs);
291}
292
293
294int
295clean_segment(fsp, id)
296 FS_INFO *fsp; /* file system information */
297 int id; /* segment number */
298{
299 BLOCK_INFO *block_array;
f0f9e35d
KB
300 SEGUSE *sp;
301 struct lfs *lfsp;
302 struct tossstruct t;
303 caddr_t seg_buf;
875ef07c 304 int num_blocks;
f0f9e35d
KB
305
306 lfsp = &fsp->fi_lfs;
307 sp = SEGUSE_ENTRY(lfsp, fsp->fi_segusep, id);
308
309#ifdef VERBOSE
310 (void) printf("cleaning segment %d: contains %lu bytes\n", id,
311 sp->su_nbytes);
312 fflush(stdout);
313#endif
314 /* XXX could add debugging to verify that segment is really empty */
315 if (sp->su_nbytes == sp->su_nsums * LFS_SUMMARY_SIZE)
316 return (0);
317
318 /* map the segment into a buffer */
319 if (mmap_segment(fsp, id, &seg_buf) < 0) {
320 err(0, "mmap_segment failed");
321 return (-1);
322 }
323 /* get a list of blocks that are contained by the segment */
875ef07c 324 if (lfs_segmapv(fsp, id, seg_buf, &block_array, &num_blocks) < 0) {
f0f9e35d
KB
325 err(0, "clean_segment: lfs_segmapv failed");
326 return (-1);
327 }
328
329#ifdef VERBOSE
875ef07c 330 (void) printf("lfs_segmapv returned %d blocks\n", num_blocks);
f0f9e35d
KB
331 fflush (stdout);
332#endif
333
334 /* get the current disk address of blocks contained by the segment */
335 if (lfs_bmapv(fsp->fi_statfsp->f_fsid, block_array, num_blocks) < 0) {
336 perror("clean_segment: lfs_bmapv failed\n");
337 return -1;
338 }
339
340 /* Now toss any blocks not in the current segment */
341 t.lfs = lfsp;
342 t.seg = id;
343 toss(block_array, &num_blocks, sizeof(BLOCK_INFO), bi_tossold, &t);
344
345 /* Check if last element should be tossed */
346 if (num_blocks && bi_tossold(&t, block_array + num_blocks - 1, NULL))
347 --num_blocks;
348
349#ifdef VERBOSE
350 {
351 BLOCK_INFO *_bip;
f0f9e35d
KB
352 u_long *lp;
353 int i;
354
355 (void) printf("after bmapv still have %d blocks\n", num_blocks);
356 fflush (stdout);
357 if (num_blocks)
358 printf("BLOCK INFOS\n");
359 for (_bip = block_array, i=0; i < num_blocks; ++_bip, ++i) {
360 PRINT_BINFO(_bip);
361 lp = (u_long *)_bip->bi_bp;
362 }
f0f9e35d
KB
363 }
364#endif
365 /* rewrite the live data */
875ef07c
KB
366 if (num_blocks > 0)
367 if (lfs_markv(fsp->fi_statfsp->f_fsid, block_array, num_blocks)
368 < 0 ) {
8e5862f2 369 err(0, "clean_segment: lfs_markv failed");
f0f9e35d
KB
370 return (-1);
371 }
372 free(block_array);
f0f9e35d
KB
373 munmap_segment(fsp, seg_buf);
374
375 return (0);
376}
377
378
379int
380bi_tossold(client, a, b)
381 const void *client;
382 const void *a;
383 const void *b;
384{
385 const struct tossstruct *t;
386
387 t = (struct tossstruct *)client;
388
389 return (((BLOCK_INFO *)a)->bi_daddr == LFS_UNUSED_DADDR ||
390 datosn(t->lfs, ((BLOCK_INFO *)a)->bi_daddr) != t->seg);
391}