/*
* fs/lafs/clean.c
- * Copyright (C) 2005-2009
+ * Copyright (C) 2005-2010
* Neil Brown <neilb@suse.de>
* Released under the GPL, version 2
*/
#include "lafs.h"
+/* mark_cleaning
+ * Given a block that is in a segment that is being cleaner, mark
+ * and pin it so that it gets cleaner.
+ * This is written to cope with failure when allocating space, but in
+ * the current design, that should never happen.
+ */
static int mark_cleaning(struct block *b)
{
int err;
return 0;
}
+/* first_in_seg
+ * Find the first block among this and its ancenstor which
+ * is in the nominated segment - if any are.
+ * As the cluster header does not differentiate between
+ * index blocks of different depths, we need to check them
+ * all.
+ */
static struct block *first_in_seg(struct block *b, struct fs *fs,
int dev, u32 seg, REFARG)
{
- /* Find the first block at or above b which is in the given
- * segment and return a reference to it.
- */
struct address_space *as = &b->inode->i_data;
struct block *p;
if (in_seg(fs, dev, seg, b->physaddr))
/* To 'flush' the cleaner, anything on the fs->clean_leafs needs
* to be either allocated to a cleaning-cluster or incorporated then added.
- * If the list gets empty we flush out the cleaning cluster
- * and check again.
+ * If the list gets empty we flush out the cleaning cluster and return.
+ * The next time the cleaner runs it will come back here and do
+ * some more flushing.
*/
static void cleaner_flush(struct fs *fs)
{
dprintk("cleaning %s\n", strblk(b));
if (test_bit(B_PinPending, &b->flags)) {
- /* Cannot safely clean this now. Just make
+ /* Cannot safely clean this now. Just mark
* it Dirty (it probably will be soon anyway)
* so it gets written to the new-data segment
* which will effectively clean it.
lafs_cluster_flush(fs, 1);
}
+/*
+ * Load the next cluster header for this segment and perform
+ * some validity checks. If there is a failure, simply
+ * update the 'tc' state so that it looks like there are
+ * no more clusters to find.
+ */
static void cleaner_load(struct fs *fs, struct toclean *tc)
{
- /* Need to read in the cluster header */
int err;
err = lafs_load_page_async(fs, tc->chead,
tc->haddr,
tc->have_addr = 0;
}
+/* Parse a cluster header and identify blocks that might need cleaning.
+ * Each time through we start parsing from the start.
+ * As we find blocks, or reject inodes we update the header so that
+ * the next time through we don't try those again.
+ * Once we have started IO on 16 different inodes, we take a break
+ * and let some of the IO complete.
+ * As we find blocks, we put them on a list to be processed later.
+ */
static void cleaner_parse(struct fs *fs, struct toclean *tc)
{
u32 bnum;
iput(ino);
}
+/* Process all blocks that have been found to possibly need to be
+ * moved from their current address (i.e. cleaned).
+ * We initiate async index lookup, then if the block really
+ * is in the target segment we initiate async read. Once
+ * that is complete we mark the block for cleaning and
+ * releaes it.
+ */
static int cleaner_process(struct fs *fs, struct toclean *tc)
{
struct datablock *b, *tmp;
return rv;
}
+/*
+ * Try to advance the process of cleaning the given segment.
+ * This may require loading a cluster head, parsing or reparsing
+ * that head, or loading some blocks.
+ */
static int try_clean(struct fs *fs, struct toclean *tc)
{
/* return 1 if everything has been found, -ve if we need to flush */
return rv;
}
+/*
+ * When we truncate a file, and block that is in the
+ * process of being cleaned must have that cleaning
+ * cancelled. That is done by lafs_erase_dblock calling
+ * lafs_unclean.
+ */
void lafs_unclean(struct datablock *db)
{
if (!list_empty_careful(&db->cleaning)) {
* Any activity may trigger an async read in which case we
* leave it and move on.
*
+ * (Most of this happens in try_clean() )
* - If we have chosen a segment/cluster but haven't loaded the
* cluster head, load the cluster head.
* - If we have loaded a cluster head but haven't processed all
if (!fs->cleaner.active &&
!test_bit(CheckpointNeeded, &fs->fsstate) &&
!test_bit(CleanerDisabled, &fs->fsstate)) {
- /* choose to clean when the fraction of all space that is clean
- * is below the faction of free space that is not clean.
+ /* Choose to clean when the fraction of all space that is clean
+ * is below the fraction of free space that is not clean.
* i.e. if T is total space, C is clean space, F is free space,
* then clean when C/T < (F-C)/F
+ * So as the amount of clean space decreases we are less tolerant
+ * of unavailable free space.
* Avoiding division, this is
* C * F < T * (F - C)
+ * As we always reserve 3 clean segments for accounting overhead
+ * and 1 to ensure we can handle deletions, we exclude those
+ * clean segments from the calculations.
+ * i.e. subtract 4 segments from T, C and F
*
* T we know from the size of the devices
* C we know by counting the clean segments
- * F we count each time we scan the segments.
- * We actually count free space in active segments, and add C.
+ * F we count each time we scan the segments. (total_free)
* We used the largest count of last pass and this pass.
*
* We need to avoid cleaning too much in one checkpoint as
for (i = 0; i < fs->devices; i++)
T += fs->devs[i].size;
- /* adjust to unusable space FIXME adjust F too? */
- T -= 4 * fs->max_segment;
+ T -= TOTAL_RESERVED * fs->max_segment;
- max_segs = lafs_alloc_cleaner_segs(fs, 4);
+ max_segs = lafs_alloc_cleaner_segs(fs, CLEANER_SEGS);
if (max_segs < 1) {
/* If we can only clean to main segment, we may
* have to. However:
+ fs->cleaner.cleaning;
u64 F = max(fs->total_free, fs->total_free_prev);
- if (4 * fs->max_segment < C)
- C -= 4 * fs->max_segment; /* adjust to unusable space FIXME adjust F too? */
+ if (TOTAL_RESERVED * fs->max_segment < C)
+ C -= TOTAL_RESERVED * fs->max_segment; /* adjust to unusable space FIXME adjust F too? */
else
C = 0;
- if (4 * fs->max_segment < F)
- F -= 4 * fs->max_segment; /* adjust to unusable space FIXME adjust F too? */
+ if (TOTAL_RESERVED * fs->max_segment < F)
+ F -= TOTAL_RESERVED * fs->max_segment; /* adjust to unusable space FIXME adjust F too? */
else
F = 0;
INIT_LIST_HEAD(&tc->cleaning);
fs->cleaner.active = 1;
}
- if (i == 4)
- printk("CLEANER: found 4 segments to clean\n");
+ if (i == CLEANER_SEGS)
+ dprintk("CLEANER: found %d segments to clean\n", i);
}
if (fs->cleaner.active) {
int cnt = 0;
int i;
int doflush = 1;
- for (i = 0; i < 4 ; i++) {
+ for (i = 0; i < CLEANER_SEGS ; i++) {
struct toclean *tc = &fs->cleaner.seg[i];
if (tc->have_addr || !list_empty(&tc->cleaning)) {
/* Might be something to do here */