]> git.neil.brown.name Git - LaFS.git/commitdiff
Wait for a checkpoint before returning ENOSPC
authorNeilBrown <neilb@suse.de>
Sun, 15 Aug 2010 08:30:36 +0000 (18:30 +1000)
committerNeilBrown <neilb@suse.de>
Sun, 15 Aug 2010 08:30:36 +0000 (18:30 +1000)
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 <neilb@suse.de>
block.c
checkpoint.c
clean.c
cluster.c
lafs.h
segments.c
state.h
super.c

diff --git a/block.c b/block.c
index ea64fcc2d77d9c0db304931319574aad2ce11f94..6d9b04a1f764c4681a65463bd6b3d8695fbe6a1b 100644 (file)
--- 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;
        }
index fca7bee76ea71ff7bfeb2de455fba7f143fe9238..f0b08c253cd02fa2d3c88abd3197b520788c00d2 100644 (file)
@@ -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 67606314f784c36887a72e59cfa2e9ffec616bfc..dfc8b1d217b7dc81ba606a24a1dcdd9515553995 100644 (file)
--- 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;
index 0f3e2eb2221bebe551fa3bf9c17cf51a01868b45..4f20fff8f8ebdac8d13e74eb8ac6dc35104ba6be 100644 (file)
--- 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 17a8e27057b68c8239e96001a18cde3706e8c9b3..a93d95d623d35be6a1482e6a8fc23e376841aa12 100644 (file)
--- 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);
index 0656bac7117147399b9e103cece815ce654b346c..17c71c2139b22a10947fd1ecdbf3917b2ab9ddba 100644 (file)
@@ -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 9e741d3b65a0a46d4766ba277fef7c98c27b1147..589a56e044da3504b5b1da6643462c49f427d7c1 100644 (file)
--- 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 36e539b0097b02e938cc646353ac0ec8849d9f5e..064e96073207c38210b4c40b21f5a884416cf2fe 100644 (file)
--- 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;