* Copyright (c) 1992, 1993
* The Regents of the University of California. All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
static char copyright
[] =
"@(#) Copyright (c) 1992, 1993\n\
The Regents of the University of California. All rights reserved.\n";
static char sccsid
[] = "@(#)cleanerd.c 8.5 (Berkeley) 6/10/95";
#include <ufs/ufs/dinode.h>
char *special
= "cleanerd";
int sl_id
; /* segment number */
int sl_cost
; /* cleaning cost */
char sl_bytes
; /* bytes in segment */
/* function prototypes for system calls; not sure where they should go */
int lfs_segwait
__P((fsid_t
*, struct timeval
*));
int lfs_segclean
__P((fsid_t
*, u_long
));
int lfs_bmapv
__P((fsid_t
*, BLOCK_INFO
*, int));
int lfs_markv
__P((fsid_t
*, BLOCK_INFO
*, int));
/* function prototypes */
int bi_tossold
__P((const void *, const void *, const void *));
int choose_segments
__P((FS_INFO
*, struct seglist
*,
int (*)(FS_INFO
*, SEGUSE
*)));
void clean_fs
__P((FS_INFO
*, int (*)(FS_INFO
*, SEGUSE
*), int, long));
int clean_loop
__P((FS_INFO
*, int, long));
int clean_segment
__P((FS_INFO
*, int));
int cost_benefit
__P((FS_INFO
*, SEGUSE
*));
int cost_compare
__P((const void *, const void *));
void sig_report
__P((int));
* Cleaning Cost Functions:
* These return the cost of cleaning a segment. The higher the cost value
* the better it is to clean the segment, so empty segments have the highest
* cost. (It is probably better to think of this as a priority value
* This is the cost-benefit policy simulated and described in Rosenblum's
FS_INFO
*fsp
; /* file system information */
age
= t
.tv_sec
< su
->su_lastmod
? 0 : t
.tv_sec
- su
->su_lastmod
;
return (t
.tv_sec
* lblkno(lfsp
, seg_size(lfsp
)));
* from lfsSegUsage.c (Mendel's code).
* priority calculation is done using INTEGER arithmetic.
* sizes are in BLOCKS (that is why we use lblkno below).
* priority = ((seg_size - live) * age) / (seg_size + live)
if (live
< 0 || live
> seg_size(lfsp
)) {
err(0, "Bad segusage count: %d", live
);
return (lblkno(lfsp
, seg_size(lfsp
) - live
) * age
)
/ lblkno(lfsp
, seg_size(lfsp
) + live
);
struct statfs
*lstatfsp
; /* file system stats */
struct timeval timeout
; /* sleep timeout */
long clean_opts
; /* cleaning options */
int i
, nodaemon
, segs_per_clean
;
char *fs_name
; /* name of filesystem to clean */
while ((opt
= getopt(argc
, argv
, "bdmn:r:s")) != EOF
) {
* Use live bytes to determine
* how many segs to clean.
clean_opts
|= CLEAN_BYTES
;
case 'd': /* Debug mode. */
case 'm': /* Use mmap instead of read/write */
case 'n': /* How many segs to clean at once */
segs_per_clean
= atoi(optarg
);
case 'r': /* Report every stat_report segments */
stat_report
= atoi(optarg
);
case 's': /* small writes */
if (cmd_err
|| (argc
!= 1))
err(1, "usage: lfs_cleanerd [-smd] fs_name");
signal(SIGINT
, sig_report
);
signal(SIGUSR1
, sig_report
);
signal(SIGUSR2
, sig_report
);
if (fs_getmntinfo(&lstatfsp
, fs_name
, "lfs") == 0) {
/* didn't find the filesystem */
err(1, "lfs_cleanerd: filesystem %s isn't an LFS!", fs_name
);
if (!nodaemon
) /* should we become a daemon, chdir to / & close fd's */
err(1, "lfs_cleanerd: couldn't become a daemon!");
timeout
.tv_sec
= 5*60; /* five minutes */
for (fsp
= get_fs_info(lstatfsp
, do_mmap
); ;
reread_fs_info(fsp
, do_mmap
)) {
* clean the filesystem, and, if it needed cleaning
* (i.e. it returned nonzero) try it again
* to make sure that some nasty process hasn't just
* filled the disk system up.
if (clean_loop(fsp
, segs_per_clean
, clean_opts
))
(void)printf("Cleaner going to sleep.\n");
if (lfs_segwait(&fsid
, &timeout
) < 0)
err(0, "lfs_segwait: returned error\n");
(void)printf("Cleaner waking up.\n");
/* return the number of segments cleaned */
clean_loop(fsp
, nsegs
, options
)
FS_INFO
*fsp
; /* file system information */
double loadavg
[MAXLOADS
];
* Compute the maximum possible number of free segments, given the
db_per_seg
= fsbtodb(&fsp
->fi_lfs
, fsp
->fi_lfs
.lfs_ssize
);
max_free_segs
= fsp
->fi_lfs
.lfs_bfree
/ db_per_seg
;
* We will clean if there are not enough free blocks or total clean
* space is less than BUSY_LIM % of possible clean space.
now
= time((time_t *)NULL
);
printf("db_er_seg = %d max_free_segs = %d, bfree = %d avail = %d ",
db_per_seg
, max_free_segs
, fsp
->fi_lfs
.lfs_bfree
,
printf("clean = %d\n", fsp
->fi_cip
->clean
);
if ((fsp
->fi_lfs
.lfs_bfree
- fsp
->fi_lfs
.lfs_avail
> db_per_seg
&&
fsp
->fi_lfs
.lfs_avail
< db_per_seg
) ||
(fsp
->fi_cip
->clean
< max_free_segs
&&
(fsp
->fi_cip
->clean
<= MIN_SEGS(&fsp
->fi_lfs
) ||
fsp
->fi_cip
->clean
< max_free_segs
* BUSY_LIM
))) {
printf("Cleaner Running at %s (%d of %d segments available)\n",
ctime(&now
), fsp
->fi_cip
->clean
, max_free_segs
);
clean_fs(fsp
, cost_benefit
, nsegs
, options
);
* We will also clean if the system is reasonably idle and
* the total clean space is less then IDLE_LIM % of possible
if (getloadavg(loadavg
, MAXLOADS
) == -1) {
perror("getloadavg: failed\n");
if (loadavg
[ONE_MIN
] == 0.0 && loadavg
[FIVE_MIN
] &&
fsp
->fi_cip
->clean
< max_free_segs
* IDLE_LIM
) {
clean_fs(fsp
, cost_benefit
, nsegs
, options
);
printf("Cleaner Running at %s (system idle)\n",
printf("Cleaner Not Running at %s\n", ctime(&now
));
clean_fs(fsp
, cost_func
, nsegs
, options
)
FS_INFO
*fsp
; /* file system information */
int (*cost_func
) __P((FS_INFO
*, SEGUSE
*));
struct seglist
*segs
, *sp
;
int to_clean
, cleaned_bytes
;
malloc(fsp
->fi_lfs
.lfs_nseg
* sizeof(struct seglist
))) == NULL
) {
i
= choose_segments(fsp
, segs
, cost_func
);
printf("clean_fs: found %d segments to clean in file system %s\n",
i
, fsp
->fi_statfsp
->f_mntonname
);
/* Check which cleaning algorithm to use. */
if (options
& CLEAN_BYTES
) {
(fsp
->fi_lfs
.lfs_segshift
+ fsp
->fi_lfs
.lfs_bshift
);
for (sp
= segs
; i
&& cleaned_bytes
< to_clean
;
if (clean_segment(fsp
, sp
->sl_id
) < 0)
perror("clean_segment failed");
else if (lfs_segclean(&fsp
->fi_statfsp
->f_fsid
,
perror("lfs_segclean failed");
printf("Cleaned segment %d (%d bytes)\n",
sp
->sl_id
, sp
->sl_bytes
);
cleaned_bytes
+= sp
->sl_bytes
;
for (i
= MIN(i
, nsegs
), sp
= segs
; i
-- ; ++sp
) {
if (clean_segment(fsp
, sp
->sl_id
) < 0)
perror("clean_segment failed");
else if (lfs_segclean(&fsp
->fi_statfsp
->f_fsid
,
perror("lfs_segclean failed");
printf("Completed cleaning segment %d\n", sp
->sl_id
);
* Segment with the highest priority get sorted to the beginning of the
* list. This sort assumes that empty segments always have a higher
* cost/benefit than any utilized segment.
return (((struct seglist
*)b
)->sl_cost
-
((struct seglist
*)a
)->sl_cost
);
* Returns the number of segments to be cleaned with the elements of seglist
choose_segments(fsp
, seglist
, cost_func
)
int (*cost_func
) __P((FS_INFO
*, SEGUSE
*));
(void)printf("Entering choose_segments\n");
dump_cleaner_info(fsp
->fi_cip
);
for (sp
= seglist
, i
= 0; i
< lfsp
->lfs_nseg
; ++i
) {
sup
= SEGUSE_ENTRY(lfsp
, fsp
->fi_segusep
, i
);
if (!(sup
->su_flags
& SEGUSE_DIRTY
) ||
sup
->su_flags
& SEGUSE_ACTIVE
)
(void)printf("\tchoosing segment %d\n", i
);
sp
->sl_cost
= (*cost_func
)(fsp
, sup
);
sp
->sl_bytes
= sup
->su_nbytes
;
qsort(seglist
, nsegs
, sizeof(struct seglist
), cost_compare
);
(void)printf("Returning %d segments\n", nsegs
);
FS_INFO
*fsp
; /* file system information */
int id
; /* segment number */
BLOCK_INFO
*block_array
, *bp
;
int num_blocks
, maxblocks
, clean_blocks
;
sp
= SEGUSE_ENTRY(lfsp
, fsp
->fi_segusep
, id
);
(void)printf("cleaning segment %d: contains %lu bytes\n", id
,
/* XXX could add debugging to verify that segment is really empty */
if (sp
->su_nbytes
== sp
->su_nsums
* LFS_SUMMARY_SIZE
) {
++cleaner_stats
.segs_empty
;
/* map the segment into a buffer */
if (mmap_segment(fsp
, id
, &seg_buf
, do_mmap
) < 0) {
err(0, "mmap_segment failed");
++cleaner_stats
.segs_error
;
/* get a list of blocks that are contained by the segment */
if (lfs_segmapv(fsp
, id
, seg_buf
, &block_array
, &num_blocks
) < 0) {
err(0, "clean_segment: lfs_segmapv failed");
++cleaner_stats
.segs_error
;
cleaner_stats
.blocks_read
+= fsp
->fi_lfs
.lfs_ssize
;
(void)printf("lfs_segmapv returned %d blocks\n", num_blocks
);
/* get the current disk address of blocks contained by the segment */
if (lfs_bmapv(&fsp
->fi_statfsp
->f_fsid
, block_array
, num_blocks
) < 0) {
perror("clean_segment: lfs_bmapv failed\n");
++cleaner_stats
.segs_error
;
/* Now toss any blocks not in the current segment */
toss(block_array
, &num_blocks
, sizeof(BLOCK_INFO
), bi_tossold
, &t
);
/* Check if last element should be tossed */
if (num_blocks
&& bi_tossold(&t
, block_array
+ num_blocks
- 1, NULL
))
(void)printf("after bmapv still have %d blocks\n", num_blocks
);
for (_bip
= block_array
, i
=0; i
< num_blocks
; ++_bip
, ++i
) {
lp
= (u_long
*)_bip
->bi_bp
;
++cleaner_stats
.segs_cleaned
;
cleaner_stats
.blocks_written
+= num_blocks
;
util
= ((double)num_blocks
/ fsp
->fi_lfs
.lfs_ssize
);
cleaner_stats
.util_tot
+= util
;
cleaner_stats
.util_sos
+= util
* util
;
maxblocks
= MAXPHYS
/ fsp
->fi_lfs
.lfs_bsize
- 1;
for (bp
= block_array
; num_blocks
> 0; bp
+= clean_blocks
) {
clean_blocks
= maxblocks
< num_blocks
? maxblocks
: num_blocks
;
if (lfs_markv(&fsp
->fi_statfsp
->f_fsid
,
err(0, "clean_segment: lfs_markv failed");
++cleaner_stats
.segs_error
;
num_blocks
-= clean_blocks
;
munmap_segment(fsp
, seg_buf
, do_mmap
);
if (stat_report
&& cleaner_stats
.segs_cleaned
% stat_report
== 0)
const struct tossstruct
*t
;
t
= (struct tossstruct
*)client
;
return (((BLOCK_INFO
*)a
)->bi_daddr
== LFS_UNUSED_DADDR
||
datosn(t
->lfs
, ((BLOCK_INFO
*)a
)->bi_daddr
) != t
->seg
);
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",
"blocks_read ", cleaner_stats
.blocks_read
,
"blocks_written ", cleaner_stats
.blocks_written
,
"segs_cleaned ", cleaner_stats
.segs_cleaned
,
"segs_empty ", cleaner_stats
.segs_empty
,
"seg_error ", cleaner_stats
.segs_error
);
printf("\t\t%s%5.2f\n\t\t%s%5.2f\n",
"util_tot ", cleaner_stats
.util_tot
,
"util_sos ", cleaner_stats
.util_sos
);
printf("\t\tavg util: %4.2f std dev: %9.6f\n",
avg
= cleaner_stats
.util_tot
/ cleaner_stats
.segs_cleaned
,
cleaner_stats
.util_sos
/ cleaner_stats
.segs_cleaned
- avg
* avg
);
cleaner_stats
.blocks_read
= 0;
cleaner_stats
.blocks_written
= 0;
cleaner_stats
.segs_cleaned
= 0;
cleaner_stats
.segs_empty
= 0;
cleaner_stats
.segs_error
= 0;
cleaner_stats
.util_tot
= 0.0;
cleaner_stats
.util_sos
= 0.0;