From: NeilBrown Date: Sun, 15 Aug 2010 05:08:49 +0000 (+1000) Subject: clean.c - assorted tidy-ups X-Git-Url: http://git.neil.brown.name/?a=commitdiff_plain;h=2b0c252a4575d432e7cb46e5d0eb725d50c69381;p=LaFS.git clean.c - assorted tidy-ups Change some magic constants into named constants, and improves some comments. Signed-off-by: NeilBrown --- diff --git a/clean.c b/clean.c index 6cc09f5..6760631 100644 --- a/clean.c +++ b/clean.c @@ -1,13 +1,19 @@ /* * fs/lafs/clean.c - * Copyright (C) 2005-2009 + * Copyright (C) 2005-2010 * Neil Brown * 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; @@ -51,12 +57,16 @@ static int mark_cleaning(struct block *b) 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)) @@ -88,8 +98,9 @@ static struct block *first_in_seg(struct block *b, struct fs *fs, /* 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) { @@ -103,7 +114,7 @@ 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. @@ -133,9 +144,14 @@ static void cleaner_flush(struct fs *fs) 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, @@ -178,6 +194,14 @@ bad_header: 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; @@ -393,6 +417,13 @@ static void cleaner_parse(struct fs *fs, struct toclean *tc) 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; @@ -463,6 +494,11 @@ static int cleaner_process(struct fs *fs, struct toclean *tc) 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 */ @@ -485,6 +521,12 @@ static int try_clean(struct fs *fs, struct toclean *tc) 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)) { @@ -528,6 +570,7 @@ unsigned long lafs_do_clean(struct fs *fs) * 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 @@ -545,17 +588,22 @@ unsigned long lafs_do_clean(struct fs *fs) 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 @@ -572,10 +620,9 @@ unsigned long lafs_do_clean(struct fs *fs) 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: @@ -596,12 +643,12 @@ unsigned long lafs_do_clean(struct fs *fs) + 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; @@ -634,14 +681,14 @@ unsigned long lafs_do_clean(struct fs *fs) 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 */ diff --git a/segments.c b/segments.c index 214cba7..0656bac 100644 --- a/segments.c +++ b/segments.c @@ -619,10 +619,10 @@ int lafs_space_alloc(struct fs *fs, int credits, int why) spin_lock(&fs->alloc_lock); switch(why) { case NewSpace: - watermark += 1 * fs->max_segment; + watermark += RELEASE_RESERVED * fs->max_segment; /* FALL THROUGH */ case ReleaseSpace: - watermark += 3 * fs->max_segment; + watermark += ACCOUNT_RESERVED * fs->max_segment; /* FALL THROUGH */ case CleanSpace: case AccountSpace: diff --git a/state.h b/state.h index f0be1d3..9e741d3 100644 --- a/state.h +++ b/state.h @@ -37,6 +37,15 @@ struct skippoint { }; #define WC_NUM 3 /* 3 active write-clusters: new, clean, and defrag */ +#define CLEANER_SEGS 4 /* Clean at most 4 segments at a time */ + +#define ACCOUNT_RESERVED 3 /* Reserve 3 segments of space for accounting and + * cleaning + */ +#define RELEASE_RESERVED 1 /* Reserve 1 segment of space for overhead required + * to release space (e.g. delete file) + */ +#define TOTAL_RESERVED (ACCOUNT_RESERVED + RELEASE_RESERVED) struct fs { struct lafs_state *state; @@ -138,7 +147,7 @@ struct fs { int have_addr; /* true if dev/seg are valid */ struct list_head cleaning; struct page *chead; - } seg[4]; + } seg[CLEANER_SEGS]; } cleaner; struct task_struct *thread;