BSD 4_4_Lite1 release
[unix-history] / usr / src / libexec / lfs_cleanerd / cleanerd.c
CommitLineData
f97378c7 1/*-
88f72820
KB
2 * Copyright (c) 1992, 1993
3 * The Regents of the University of California. All rights reserved.
f97378c7 4 *
ad787160
C
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
f97378c7
KB
32 */
33
34#ifndef lint
88f72820
KB
35static char copyright[] =
36"@(#) Copyright (c) 1992, 1993\n\
37 The Regents of the University of California. All rights reserved.\n";
f97378c7
KB
38#endif /* not lint */
39
40#ifndef lint
ed554bc5 41static char sccsid[] = "@(#)cleanerd.c 8.2 (Berkeley) 1/13/94";
f97378c7
KB
42#endif /* not lint */
43
f0f9e35d
KB
44#include <sys/param.h>
45#include <sys/mount.h>
46#include <sys/time.h>
a1cd5d4a 47
f0f9e35d
KB
48#include <ufs/ufs/dinode.h>
49#include <ufs/lfs/lfs.h>
a1cd5d4a
KB
50
51#include <signal.h>
f0f9e35d
KB
52#include <stdio.h>
53#include <stdlib.h>
54#include <unistd.h>
55
56#include "clean.h"
57char *special = "cleanerd";
1f5c0335
MS
58int do_small = 0;
59int do_mmap = 0;
60struct cleaner_stats {
61 int blocks_read;
62 int blocks_written;
63 int segs_cleaned;
64 int segs_empty;
65 int segs_error;
66} cleaner_stats;
f0f9e35d
KB
67
68struct seglist {
69 int sl_id; /* segment number */
70 int sl_cost; /* cleaning cost */
71 char sl_empty; /* is segment empty */
72};
73
74struct tossstruct {
75 struct lfs *lfs;
76 int seg;
77};
78
79/* function prototypes for system calls; not sure where they should go */
9bd4a030
KB
80int lfs_segwait __P((fsid_t *, struct timeval *));
81int lfs_segclean __P((fsid_t *, u_long));
82int lfs_bmapv __P((fsid_t *, BLOCK_INFO *, int));
83int lfs_markv __P((fsid_t *, BLOCK_INFO *, int));
f0f9e35d
KB
84
85/* function prototypes */
86int bi_tossold __P((const void *, const void *, const void *));
87int choose_segments __P((FS_INFO *, struct seglist *,
88 int (*)(FS_INFO *, SEGUSE *)));
89void clean_fs __P((FS_INFO *, int (*)(FS_INFO *, SEGUSE *)));
90int clean_loop __P((FS_INFO *));
91int clean_segment __P((FS_INFO *, int));
92int cost_benefit __P((FS_INFO *, SEGUSE *));
93int cost_compare __P((const void *, const void *));
1f5c0335 94void sig_report __P((int));
f0f9e35d
KB
95
96/*
97 * Cleaning Cost Functions:
98 *
99 * These return the cost of cleaning a segment. The higher the cost value
100 * the better it is to clean the segment, so empty segments have the highest
101 * cost. (It is probably better to think of this as a priority value
102 * instead).
103 *
104 * This is the cost-benefit policy simulated and described in Rosenblum's
105 * 1991 SOSP paper.
106 */
107
108int
109cost_benefit(fsp, su)
110 FS_INFO *fsp; /* file system information */
111 SEGUSE *su;
112{
113 struct lfs *lfsp;
114 struct timeval t;
115 int age;
116 int live;
117
118 gettimeofday(&t, NULL);
119
120 live = su->su_nbytes;
a1cd5d4a 121 age = t.tv_sec < su->su_lastmod ? 0 : t.tv_sec - su->su_lastmod;
f0f9e35d
KB
122
123 lfsp = &fsp->fi_lfs;
9bd4a030 124 if (live == 0)
f0f9e35d
KB
125 return (t.tv_sec * lblkno(lfsp, seg_size(lfsp)));
126 else {
127 /*
128 * from lfsSegUsage.c (Mendel's code).
129 * priority calculation is done using INTEGER arithmetic.
130 * sizes are in BLOCKS (that is why we use lblkno below).
131 * age is in seconds.
132 *
9bd4a030 133 * priority = ((seg_size - live) * age) / (seg_size + live)
f0f9e35d
KB
134 */
135#ifdef VERBOSE
136 if (live < 0 || live > seg_size(lfsp)) {
137 err(0, "Bad segusage count: %d", live);
138 live = 0;
139 }
140#endif
141 return (lblkno(lfsp, seg_size(lfsp) - live) * age)
142 / lblkno(lfsp, seg_size(lfsp) + live);
143 }
144}
145
f97378c7 146int
f0f9e35d
KB
147main(argc, argv)
148 int argc;
149 char *argv[];
150{
d59d68b6 151 FS_INFO *fsp;
f0f9e35d
KB
152 struct statfs *lstatfsp; /* file system stats */
153 struct timeval timeout; /* sleep timeout */
154 fsid_t fsid;
3013fbb8 155 int i, nodaemon;
1f5c0335 156 int opt, cmd_err;
d59d68b6
CD
157 char *fs_name; /* name of filesystem to clean */
158 extern int optind;
1f5c0335 159
3013fbb8
CD
160 cmd_err = nodaemon = 0;
161 while ((opt = getopt(argc, argv, "smd")) != EOF) {
1f5c0335
MS
162 switch (opt) {
163 case 's': /* small writes */
164 do_small = 1;
165 break;
166 case 'm':
167 do_mmap = 1;
168 break;
3013fbb8
CD
169 case 'd':
170 nodaemon = 1;
171 break;
1f5c0335
MS
172 default:
173 ++cmd_err;
174 }
175 }
d59d68b6
CD
176 argc -= optind;
177 argv += optind;
178 if (cmd_err || (argc != 1))
3013fbb8 179 err(1, "usage: lfs_cleanerd [-smd] fs_name");
d59d68b6
CD
180
181 fs_name = argv[0];
1f5c0335 182
9bd4a030
KB
183 signal(SIGINT, sig_report);
184 signal(SIGUSR1, sig_report);
185 signal(SIGUSR2, sig_report);
d59d68b6
CD
186 if (fs_getmntinfo(&lstatfsp, fs_name, MOUNT_LFS) == 0) {
187 /* didn't find the filesystem */
188 err(1, "lfs_cleanerd: filesystem %s isn't an LFS!", fs_name);
189 }
f0f9e35d 190
3013fbb8
CD
191 if (!nodaemon) /* should we become a daemon, chdir to / & close fd's */
192 if (daemon(0, 0) == -1)
193 err(1, "lfs_cleanerd: couldn't become a daemon!");
194
f0f9e35d
KB
195 timeout.tv_sec = 5*60; /* five minutes */
196 timeout.tv_usec = 0;
197 fsid.val[0] = 0;
198 fsid.val[1] = 0;
199
d59d68b6
CD
200 for (fsp = get_fs_info(lstatfsp, do_mmap); ;
201 reread_fs_info(fsp, do_mmap)) {
28cc73c5 202 /*
d59d68b6
CD
203 * clean the filesystem, and, if it needed cleaning
204 * (i.e. it returned nonzero) try it again
28cc73c5
KB
205 * to make sure that some nasty process hasn't just
206 * filled the disk system up.
207 */
d59d68b6 208 if (clean_loop(fsp))
28cc73c5 209 continue;
f0f9e35d
KB
210
211#ifdef VERBOSE
212 (void)printf("Cleaner going to sleep.\n");
213#endif
9bd4a030 214 if (lfs_segwait(&fsid, &timeout) < 0)
f0f9e35d
KB
215 err(0, "lfs_segwait: returned error\n");
216#ifdef VERBOSE
217 (void)printf("Cleaner waking up.\n");
218#endif
219 }
220}
221
222/* return the number of segments cleaned */
223int
224clean_loop(fsp)
225 FS_INFO *fsp; /* file system information */
226{
227 double loadavg[MAXLOADS];
228 time_t now;
229 u_long max_free_segs;
230
231 /*
232 * Compute the maximum possible number of free segments, given the
233 * number of free blocks.
234 */
235 max_free_segs = fsp->fi_statfsp->f_bfree / fsp->fi_lfs.lfs_ssize;
236
237 /*
238 * We will clean if there are not enough free blocks or total clean
239 * space is less than BUSY_LIM % of possible clean space.
240 */
241 now = time((time_t *)NULL);
5c39f310
MS
242 if (fsp->fi_cip->clean < max_free_segs &&
243 (fsp->fi_cip->clean <= MIN_SEGS(&fsp->fi_lfs) ||
244 fsp->fi_cip->clean < max_free_segs * BUSY_LIM)) {
8f8caf36
KB
245 printf("Cleaner Running at %s (%d of %d segments available)\n",
246 ctime(&now), fsp->fi_cip->clean, max_free_segs);
28cc73c5 247 clean_fs(fsp, cost_benefit);
f0f9e35d
KB
248 return (1);
249 } else {
250 /*
251 * We will also clean if the system is reasonably idle and
252 * the total clean space is less then IDLE_LIM % of possible
253 * clean space.
254 */
255 if (getloadavg(loadavg, MAXLOADS) == -1) {
256 perror("getloadavg: failed\n");
257 return (-1);
258 }
259 if (loadavg[ONE_MIN] == 0.0 && loadavg[FIVE_MIN] &&
260 fsp->fi_cip->clean < max_free_segs * IDLE_LIM) {
261 clean_fs(fsp, cost_benefit);
262 printf("Cleaner Running at %s (system idle)\n",
263 ctime(&now));
264 return (1);
265 }
266 }
267 printf("Cleaner Not Running at %s\n", ctime(&now));
268 return (0);
269}
270
271
272void
273clean_fs(fsp, cost_func)
274 FS_INFO *fsp; /* file system information */
275 int (*cost_func) __P((FS_INFO *, SEGUSE *));
276{
277 struct seglist *segs, *sp;
278 int i;
279
9bd4a030
KB
280 if ((segs =
281 malloc(fsp->fi_lfs.lfs_nseg * sizeof(struct seglist))) == NULL) {
f0f9e35d
KB
282 err(0, "malloc failed");
283 return;
284 }
285 i = choose_segments(fsp, segs, cost_func);
286#ifdef VERBOSE
8e5862f2 287 printf("clean_fs: found %d segments to clean in file system %s\n",
f0f9e35d
KB
288 i, fsp->fi_statfsp->f_mntonname);
289 fflush(stdout);
290#endif
291 if (i)
8e5862f2 292 for (i = MIN(i, NUM_TO_CLEAN(fsp)), sp = segs; i-- ; ++sp) {
f0f9e35d
KB
293 if (clean_segment(fsp, sp->sl_id) < 0)
294 perror("clean_segment failed");
9bd4a030 295 else if (lfs_segclean(&fsp->fi_statfsp->f_fsid,
f0f9e35d
KB
296 sp->sl_id) < 0)
297 perror("lfs_segclean failed");
8e5862f2 298 printf("Completed cleaning segment %d\n", sp->sl_id);
8e5862f2 299 }
f0f9e35d
KB
300 free(segs);
301}
302
303/*
304 * Segment with the highest priority get sorted to the beginning of the
305 * list. This sort assumes that empty segments always have a higher
306 * cost/benefit than any utilized segment.
307 */
308int
309cost_compare(a, b)
310 const void *a;
311 const void *b;
312{
313 return (((struct seglist *)b)->sl_cost -
314 ((struct seglist *)a)->sl_cost);
315}
316
317
318/*
319 * Returns the number of segments to be cleaned with the elements of seglist
320 * filled in.
321 */
322int
323choose_segments(fsp, seglist, cost_func)
324 FS_INFO *fsp;
325 struct seglist *seglist;
326 int (*cost_func) __P((FS_INFO *, SEGUSE *));
327{
328 struct lfs *lfsp;
329 struct seglist *sp;
330 SEGUSE *sup;
331 int i, nsegs;
332
333 lfsp = &fsp->fi_lfs;
334
335#ifdef VERBOSE
9bd4a030 336 (void)printf("Entering choose_segments\n");
f0f9e35d
KB
337#endif
338 dump_super(lfsp);
339 dump_cleaner_info(fsp->fi_cip);
340
341 for (sp = seglist, i = 0; i < lfsp->lfs_nseg; ++i) {
342 sup = SEGUSE_ENTRY(lfsp, fsp->fi_segusep, i);
343 PRINT_SEGUSE(sup, i);
344 if (!(sup->su_flags & SEGUSE_DIRTY) ||
345 sup->su_flags & SEGUSE_ACTIVE)
346 continue;
347#ifdef VERBOSE
9bd4a030 348 (void)printf("\tchoosing segment %d\n", i);
f0f9e35d
KB
349#endif
350 sp->sl_cost = (*cost_func)(fsp, sup);
351 sp->sl_id = i;
352 sp->sl_empty = sup->su_nbytes ? 0 : 1;
353 ++sp;
354 }
355 nsegs = sp - seglist;
356 qsort(seglist, nsegs, sizeof(struct seglist), cost_compare);
357#ifdef VERBOSE
358 (void)printf("Returning %d segments\n", nsegs);
359#endif
360 return (nsegs);
361}
362
363
364int
365clean_segment(fsp, id)
366 FS_INFO *fsp; /* file system information */
367 int id; /* segment number */
368{
1f5c0335 369 BLOCK_INFO *block_array, *bp;
f0f9e35d
KB
370 SEGUSE *sp;
371 struct lfs *lfsp;
372 struct tossstruct t;
373 caddr_t seg_buf;
1f5c0335 374 int num_blocks, maxblocks, clean_blocks;
f0f9e35d
KB
375
376 lfsp = &fsp->fi_lfs;
377 sp = SEGUSE_ENTRY(lfsp, fsp->fi_segusep, id);
378
379#ifdef VERBOSE
9bd4a030 380 (void)printf("cleaning segment %d: contains %lu bytes\n", id,
f0f9e35d
KB
381 sp->su_nbytes);
382 fflush(stdout);
383#endif
384 /* XXX could add debugging to verify that segment is really empty */
1f5c0335
MS
385 if (sp->su_nbytes == sp->su_nsums * LFS_SUMMARY_SIZE) {
386 ++cleaner_stats.segs_empty;
f0f9e35d 387 return (0);
1f5c0335 388 }
f0f9e35d
KB
389
390 /* map the segment into a buffer */
1f5c0335 391 if (mmap_segment(fsp, id, &seg_buf, do_mmap) < 0) {
f0f9e35d 392 err(0, "mmap_segment failed");
1f5c0335 393 ++cleaner_stats.segs_error;
f0f9e35d
KB
394 return (-1);
395 }
396 /* get a list of blocks that are contained by the segment */
875ef07c 397 if (lfs_segmapv(fsp, id, seg_buf, &block_array, &num_blocks) < 0) {
f0f9e35d 398 err(0, "clean_segment: lfs_segmapv failed");
1f5c0335 399 ++cleaner_stats.segs_error;
f0f9e35d
KB
400 return (-1);
401 }
1f5c0335 402 cleaner_stats.blocks_read += fsp->fi_lfs.lfs_ssize;
f0f9e35d
KB
403
404#ifdef VERBOSE
9bd4a030
KB
405 (void)printf("lfs_segmapv returned %d blocks\n", num_blocks);
406 fflush(stdout);
f0f9e35d
KB
407#endif
408
409 /* get the current disk address of blocks contained by the segment */
9bd4a030 410 if (lfs_bmapv(&fsp->fi_statfsp->f_fsid, block_array, num_blocks) < 0) {
f0f9e35d 411 perror("clean_segment: lfs_bmapv failed\n");
1f5c0335 412 ++cleaner_stats.segs_error;
f0f9e35d
KB
413 return -1;
414 }
415
416 /* Now toss any blocks not in the current segment */
417 t.lfs = lfsp;
418 t.seg = id;
419 toss(block_array, &num_blocks, sizeof(BLOCK_INFO), bi_tossold, &t);
420
421 /* Check if last element should be tossed */
422 if (num_blocks && bi_tossold(&t, block_array + num_blocks - 1, NULL))
423 --num_blocks;
424
425#ifdef VERBOSE
426 {
427 BLOCK_INFO *_bip;
f0f9e35d
KB
428 u_long *lp;
429 int i;
430
9bd4a030
KB
431 (void)printf("after bmapv still have %d blocks\n", num_blocks);
432 fflush(stdout);
f0f9e35d
KB
433 if (num_blocks)
434 printf("BLOCK INFOS\n");
435 for (_bip = block_array, i=0; i < num_blocks; ++_bip, ++i) {
436 PRINT_BINFO(_bip);
437 lp = (u_long *)_bip->bi_bp;
438 }
f0f9e35d
KB
439 }
440#endif
1f5c0335
MS
441 cleaner_stats.blocks_written += num_blocks;
442 if (do_small)
443 maxblocks = MAXPHYS / fsp->fi_lfs.lfs_bsize - 1;
444 else
445 maxblocks = num_blocks;
446
447 for (bp = block_array; num_blocks > 0; bp += clean_blocks) {
448 clean_blocks = maxblocks < num_blocks ? maxblocks : num_blocks;
9bd4a030
KB
449 if (lfs_markv(&fsp->fi_statfsp->f_fsid,
450 bp, clean_blocks) < 0) {
8e5862f2 451 err(0, "clean_segment: lfs_markv failed");
1f5c0335 452 ++cleaner_stats.segs_error;
f0f9e35d
KB
453 return (-1);
454 }
1f5c0335
MS
455 num_blocks -= clean_blocks;
456 }
457
f0f9e35d 458 free(block_array);
9bd4a030 459 munmap_segment(fsp, seg_buf, do_mmap);
1f5c0335 460 ++cleaner_stats.segs_cleaned;
f0f9e35d
KB
461 return (0);
462}
463
464
465int
466bi_tossold(client, a, b)
467 const void *client;
468 const void *a;
469 const void *b;
470{
471 const struct tossstruct *t;
472
473 t = (struct tossstruct *)client;
474
475 return (((BLOCK_INFO *)a)->bi_daddr == LFS_UNUSED_DADDR ||
476 datosn(t->lfs, ((BLOCK_INFO *)a)->bi_daddr) != t->seg);
477}
1f5c0335
MS
478
479void
480sig_report(sig)
481 int sig;
482{
9bd4a030 483 printf("lfs_cleanerd:\t%s%d\n\t\t%s%d\n\t\t%s%d\n\t\t%s%d\n\t\t%s%d\n",
1f5c0335
MS
484 "blocks_read ", cleaner_stats.blocks_read,
485 "blocks_written ", cleaner_stats.blocks_written,
486 "segs_cleaned ", cleaner_stats.segs_cleaned,
487 "segs_empty ", cleaner_stats.segs_empty,
488 "seg_error ", cleaner_stats.segs_error);
489 if (sig == SIGUSR2) {
a1cd5d4a
KB
490 cleaner_stats.blocks_read = 0;
491 cleaner_stats.blocks_written = 0;
492 cleaner_stats.segs_cleaned = 0;
493 cleaner_stats.segs_empty = 0;
494 cleaner_stats.segs_error = 0;
1f5c0335
MS
495 }
496 if (sig == SIGINT)
497 exit(0);
498}