Commit | Line | Data |
---|---|---|
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 | |
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 | |
5c39f310 | 15 | static 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" | |
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)); | |
875ef07c | 47 | int lfs_markv __P((fsid_t, BLOCK_INFO *, int)); |
f0f9e35d KB |
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 | ||
f97378c7 | 109 | int |
f0f9e35d KB |
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 */ | |
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 */ | |
151 | int | |
152 | clean_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 | ||
200 | void | |
201 | clean_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 | */ | |
238 | int | |
239 | cost_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 | */ | |
252 | int | |
253 | choose_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 | ||
294 | int | |
295 | clean_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 | ||
379 | int | |
380 | bi_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 | } |