From: NeilBrown Date: Sun, 15 Aug 2010 08:30:36 +0000 (+1000) Subject: Wait for a checkpoint before returning ENOSPC X-Git-Url: http://git.neil.brown.name/?a=commitdiff_plain;h=8e28990842d1d7d07113ad04861c1ef02106494f;p=LaFS.git Wait for a checkpoint before returning ENOSPC If we seem to run out of space, it is worth waiting for a checkpoint as that might free up some space. So add an extra step to the sequence leading from 'no space' to 'ENOSPC'. Signed-off-by: NeilBrown --- diff --git a/block.c b/block.c index ea64fcc..6d9b04a 100644 --- a/block.c +++ b/block.c @@ -370,6 +370,7 @@ lafs_reserve_block(struct block *b, int alloc_type) { int err = 0; struct fs *fs = fs_from_inode(b->inode); + int in_emergency; if (!test_bit(B_PhysValid, &b->flags)) b->physaddr = 0; @@ -396,6 +397,12 @@ lafs_reserve_block(struct block *b, int alloc_type) */ alloc_type = ReleaseSpace; + /* Important to test EmergencyClean before we + * called in to lafs_space_alloc to avoid races: + * space becomes available and EmergencyClean are + * set at the same time (strange, but true). + */ + in_emergency = test_bit(EmergencyClean, &fs->fsstate); /* Allocate space in the filesystem */ err = err ?: lafs_prealloc(b, alloc_type); @@ -421,7 +428,7 @@ lafs_reserve_block(struct block *b, int alloc_type) if (err == 0) return 0; if (alloc_type == NewSpace) { - if (test_bit(EmergencyClean, &fs->fsstate)) + if (in_emergency) return -ENOSPC; return -EAGAIN; } diff --git a/checkpoint.c b/checkpoint.c index fca7bee..f0b08c2 100644 --- a/checkpoint.c +++ b/checkpoint.c @@ -504,6 +504,9 @@ static void finish_checkpoint(struct fs *fs, int youth) if (!test_bit(FinalCheckpoint, &fs->fsstate)) lafs_seg_apply_all(fs); + lafs_clean_free(fs); + if (test_bit(EmergencyPending, &fs->fsstate)) + set_bit(EmergencyClean, &fs->fsstate); lafs_write_state(fs); dprintk("State written, all done %d\n", fs->seq); diff --git a/clean.c b/clean.c index 6760631..dfc8b1d 100644 --- a/clean.c +++ b/clean.c @@ -655,6 +655,7 @@ unsigned long lafs_do_clean(struct fs *fs) dprintk("C=%llu F=%llu T=%llu\n", C, F, T); if ((F < C || C * F >= T * (F - C)) && !test_bit(EmergencyClean, &fs->fsstate) && + !test_bit(EmergencyPending, &fs->fsstate) && !test_bit(CleanerBlocks, &fs->fsstate)) { dprintk("CLEANER: enough cleaning with %d segments\n", i); @@ -669,6 +670,9 @@ unsigned long lafs_do_clean(struct fs *fs) if (!tc->have_addr) { dprintk("CLEANER: Nothing found to clean at %d :-(\n", i); + if (i == 0 && test_bit(EmergencyPending, &fs->fsstate) + && !test_bit(EmergencyClean, &fs->fsstate)) + lafs_checkpoint_start(fs); break; } printk("CLEANER: clean %d/%d\n", tc->dev, tc->seg); @@ -708,10 +712,13 @@ unsigned long lafs_do_clean(struct fs *fs) } } if (test_bit(CleanerBlocks, &fs->fsstate)) { - int clean = lafs_clean_count(fs); + int any_clean; + int clean = lafs_clean_count(fs, &any_clean); dprintk("clean=%d max_seg=%d need=%d\n", (int)clean, (int)fs->max_segment, (int)fs->cleaner.need); - if (clean * fs->max_segment >= fs->cleaner.need) + if (any_clean && + clean * fs->max_segment >= + fs->allocated_blocks + fs->cleaner.need) lafs_checkpoint_start(fs); } return MAX_SCHEDULE_TIMEOUT; diff --git a/cluster.c b/cluster.c index 0f3e2eb..4f20fff 100644 --- a/cluster.c +++ b/cluster.c @@ -429,7 +429,7 @@ static int new_segment(struct fs *fs, int cnum) if (cnum && fs->clean_reserved < fs->max_segment) { /* we have reached the end of the last cleaner * segment. The remainder of clean_reserved - * is of no value (if there even is any + * is of no value (if there even is any) */ if (fs->clean_reserved) { spin_lock(&fs->alloc_lock); diff --git a/lafs.h b/lafs.h index 17a8e27..a93d95d 100644 --- a/lafs.h +++ b/lafs.h @@ -692,7 +692,8 @@ void lafs_space_return(struct fs *fs, int credits); int lafs_alloc_cleaner_segs(struct fs *fs, int max); int lafs_space_alloc(struct fs *fs, int credits, int why); unsigned long lafs_scan_seg(struct fs *fs); -int lafs_clean_count(struct fs *fs); +int lafs_clean_count(struct fs *fs, int *any_clean); +void lafs_clean_free(struct fs *fs); /* Cleaner */ unsigned long lafs_do_clean(struct fs *fs); diff --git a/segments.c b/segments.c index 0656bac..17c71c2 100644 --- a/segments.c +++ b/segments.c @@ -415,7 +415,6 @@ static void seg_apply(struct fs *fs, struct segsum *ss) * This function applies all the delayed updates to the segment usage * files at the end of a checkpoint. */ -static void clean_free(struct fs *fs); void lafs_seg_apply_all(struct fs *fs) { int i; @@ -437,7 +436,6 @@ void lafs_seg_apply_all(struct fs *fs) spin_unlock(&fs->stable_lock); } /* Now any clean segments found earlier are free. */ - clean_free(fs); } /* @@ -650,10 +648,7 @@ int lafs_space_alloc(struct fs *fs, int credits, int why) if (credits == 0) { if (why == NewSpace) - /* FIXME This is really a bit too early - need to wait - * at least one checkpoint - */ - set_bit(EmergencyClean, &fs->fsstate); + set_bit(EmergencyPending, &fs->fsstate); if (!test_bit(CleanerBlocks, &fs->fsstate) || fs->cleaner.need > watermark + fs->max_segment) { fs->cleaner.need = watermark + fs->max_segment; @@ -661,7 +656,11 @@ int lafs_space_alloc(struct fs *fs, int credits, int why) lafs_wake_thread(fs); } } else if (why == NewSpace) - clear_bit(EmergencyClean, &fs->fsstate); + if (test_bit(EmergencyClean, &fs->fsstate) || + test_bit(EmergencyPending, &fs->fsstate)) { + clear_bit(EmergencyPending, &fs->fsstate); + clear_bit(EmergencyClean, &fs->fsstate); + } fs->allocated_blocks += credits; BUG_ON(fs->free_blocks + fs->clean_reserved < fs->allocated_blocks); @@ -1226,10 +1225,11 @@ static u16 segunused(struct segtracker *st) return rv; } -int lafs_clean_count(struct fs *fs) +int lafs_clean_count(struct fs *fs, int *any_clean) { int rv; spin_lock(&fs->lock); + *any_clean = fs->segtrack->clean.cnt != 0; rv = fs->segtrack->free.cnt + fs->segtrack->clean.cnt; spin_unlock(&fs->lock); return rv; @@ -1381,7 +1381,7 @@ void lafs_add_active(struct fs *fs, u64 addr) spin_unlock(&fs->lock); } -static void clean_free(struct fs *fs) +void lafs_clean_free(struct fs *fs) { /* We are finishing off a checkpoint. Move all from 'clean' * list to 'free' list, and set the youth for each to 0. @@ -1590,7 +1590,7 @@ static int add_cleanable(struct fs *fs, unsigned int dev, u32 seg, u16 *where[SEG_NUM_HEIGHTS]; WARN_ON(lafs_check_seg_cnt(fs->segtrack)); - if (fs->scan.trace || lafs_trace) + if (fs->scan.trace || lafs_trace || 1) printk("CLEANABLE: %u/%lu y=%d u=%d\n", dev, (unsigned long)seg, (int)youth, (int)usage); if (youth < 8) @@ -1610,6 +1610,8 @@ static int add_cleanable(struct fs *fs, unsigned int dev, u32 seg, if (test_bit(EmergencyClean, &fs->fsstate)) score = usage; else + /* 0x10000 is to ensure this score is always + * more than the above score */ score = youth * usage / segsize + 0x10000; spin_lock(&fs->lock); diff --git a/state.h b/state.h index 9e741d3..589a56e 100644 --- a/state.h +++ b/state.h @@ -115,6 +115,10 @@ struct fs { #define SecondFlushNeeded 9 /* Need a second cluster to commit the blocks * in the previous one */ +#define EmergencyPending 10 /* Cleaner isn't quite in emergency mode, but + * should be after the next checkpoint unless that + * releases lots of space + */ struct work_struct done_work; /* used for handling * refile after write completes */ diff --git a/super.c b/super.c index 36e539b..064e960 100644 --- a/super.c +++ b/super.c @@ -515,6 +515,7 @@ lafs_load(struct fs *fs, struct options *op, int newest) fs->nonlog_dev = le16_to_cpu(st->nonlog_dev); fs->nonlog_offset = le16_to_cpu(st->nonlog_offset); fs->youth_next = le16_to_cpu(st->nextyouth); + fs->checkpoint_youth = fs->youth_next; if (fs->youth_next < 8) fs->youth_next = 8; fs->scan.first_free_pass = 1;