]> git.neil.brown.name Git - LaFS.git/commitdiff
Allow writers to block while the cleaner makes a little progress.
authorNeilBrown <neilb@suse.de>
Wed, 23 Jun 2010 07:00:23 +0000 (17:00 +1000)
committerNeilBrown <neilb@suse.de>
Fri, 25 Jun 2010 06:46:40 +0000 (16:46 +1000)
We record how much progress is required, and allow to wait
for that much progress to happen at which point a checkpoint
happens.

Also add 'free_segs' similar to 'free_blocks' (and counting blocks)
which counts the number of blocks in free segs, not including
any current segs.  This forced allocators to wait sooner and
may be more appropriate.

Signed-off-by: NeilBrown <neilb@suse.de>
block.c
checkpoint.c
clean.c
lafs.h
segments.c
state.h

diff --git a/block.c b/block.c
index 89431fe0844d470457db7b8f8d9a76c1b2beb544..9f8680b21093d9d397d5070f33a0bcf7b454a357 100644 (file)
--- a/block.c
+++ b/block.c
@@ -406,6 +406,10 @@ lafs_reserve_block(struct block *b, int alloc_type)
 
        if (err == 0)
                return 0;
+       /* FIXME maybe CleanSpace should return -EAGAIN if there
+        * is a good chance that the cleaner will help out soon??
+        * I wonder how "soon" can be defined.
+        */
        if (alloc_type == CleanSpace || alloc_type == NewSpace)
                return -ENOSPC;
        if (alloc_type == ReleaseSpace)
index a199932f5328eb575953f559c73e35b410060182..22f4cee01d303c402dce7ba87d5e97d6130cadfe 100644 (file)
@@ -484,6 +484,9 @@ unsigned long long lafs_checkpoint_start(struct fs *fs)
        unsigned long long cp = fs->wc[0].cluster_seq;
        WARN_ON(test_bit(FinalCheckpoint, &fs->fsstate));
        set_bit(CheckpointNeeded, &fs->fsstate);
+       /* Whenever we do a checkpoint, get anyone waiting on
+        * space to check again */
+       clear_bit(CleanerBlocks, &fs->fsstate);
        fs->prime_sb->s_dirt = 0;
        lafs_wake_cleaner(fs);
        return cp;
@@ -521,6 +524,7 @@ void lafs_checkpoint_unlock_wait(struct fs *fs)
         * right??
         */
        wait_event(fs->phase_wait,
+                  !test_bit(CleanerBlocks, &fs->fsstate) &&
                   !test_bit(CheckpointNeeded, &fs->fsstate) &&
                   fs->checkpointing == 0);
 }
diff --git a/clean.c b/clean.c
index 35e5117bade9ad646df291ecd1574d38a8e2547a..069c54f1ab20c77fb02afc11db3a8ccf872da633 100644 (file)
--- a/clean.c
+++ b/clean.c
@@ -111,6 +111,7 @@ static int cleaner(void *data)
                        timeout = to;
 
                lafs_clusters_done(fs);
+               cond_resched();
        }
        return 0;
 }
@@ -574,7 +575,8 @@ static unsigned long do_clean(struct fs *fs)
                                F = 0;
 
                        dprintk("C=%llu F=%llu T=%llu\n", C, F, T);
-                       if (F < C || C * F >= T * (F - C)) {
+                       if ((F < C || C * F >= T * (F - C)) &&
+                           !test_bit(CleanerBlocks, &fs->fsstate)) {
                                dprintk("CLEANER: enough cleaning with %d segments\n",
                                       i);
                                break;
@@ -625,5 +627,12 @@ static unsigned long do_clean(struct fs *fs)
                        wake_up(&fs->async_complete);
                }
        }
+       if (test_bit(CleanerBlocks, &fs->fsstate)) {
+               int clean = lafs_clean_count(fs);
+               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)
+                       lafs_checkpoint_start(fs);
+       }
        return MAX_SCHEDULE_TIMEOUT;
 }
diff --git a/lafs.h b/lafs.h
index 6457c7b3d356bf7540b9421280fd3febb3f53548..ace538176876d0716e37d60af3481ee58ba8ba9b 100644 (file)
--- a/lafs.h
+++ b/lafs.h
@@ -625,6 +625,7 @@ int lafs_get_cleanable(struct fs *fs, u16 *dev, u32 *seg);
 void lafs_space_return(struct fs *fs, int credits);
 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);
 
 /* Cleaner */
 int lafs_start_cleaner(struct fs *fs);
index 18b76ad4a3e274d02d10d2dff91930d6475cf0b3..75b7011961cec832a903fe931df59e1609754679 100644 (file)
@@ -632,21 +632,30 @@ int lafs_space_alloc(struct fs *fs, int credits, int why)
        if (fs->rolled) {
                /* We cannot account properly before roll-forward has
                 * completed. FIXME once it has completed we need to
-                * check and invalidate the FS is there was a problem.
+                * check and invalidate the FS if there was a problem.
                 */
-               if (fs->free_blocks < fs->allocated_blocks
+               if (fs->free_segs < fs->allocated_blocks
                    + credits + watermark)
                        credits = 0; /* Sorry, no room */
        }
 
-       if (credits == 0 && why == AccountSpace)
-               /* FIXME I should switch to READ-ONLY here,
-                * not BUG.
-                */
-               BUG();
+       if (credits == 0) {
+               if (why == AccountSpace)
+                       /* FIXME I should switch to READ-ONLY here,
+                        * not BUG.
+                        */
+                       BUG();
+
+               if (!test_bit(CleanerBlocks, &fs->fsstate) ||
+                   fs->cleaner.need > watermark + fs->max_segment) {
+                       fs->cleaner.need = watermark + fs->max_segment;
+                       set_bit(CleanerBlocks, &fs->fsstate);
+                       lafs_wake_cleaner(fs);
+               }
+       }
 
        fs->allocated_blocks += credits;
-       BUG_ON(fs->free_blocks < fs->allocated_blocks);
+//     BUG_ON(fs->free_blocks < fs->allocated_blocks);
        spin_unlock(&fs->alloc_lock);
        return credits;
 }
@@ -1098,6 +1107,7 @@ void lafs_free_get(struct fs *fs, unsigned int *dev, u32 *seg,
                fs->segtrack->free.cnt--;
                segdelete(fs->segtrack, ss);
 
+               fs->free_segs -= fs->devs[*dev].segment_size;
                spin_unlock(&fs->lock);
 
                /* now need to reserve/dirty/reference the youth and
@@ -1153,6 +1163,16 @@ static u16 segunused(struct segtracker *st)
        return rv;
 }
 
+int lafs_clean_count(struct fs *fs)
+{
+       int rv;
+       spin_lock(&fs->lock);
+       rv = fs->segtrack->free.cnt + fs->segtrack->clean.cnt;
+       spin_unlock(&fs->lock);
+       return rv;
+}
+
+
 static int add_free(struct fs *fs, unsigned int dev, u32 seg, u16 *youthp)
 {
        /* This dev/seg is known to be free. add it to the list */
@@ -1282,6 +1302,7 @@ static void clean_free(struct fs *fs)
                int err;
                ss = segfollow(fs->segtrack, ssn);
                fs->free_blocks += fs->devs[ss->dev].segment_size; // FIXME locking??
+               fs->free_segs += fs->devs[ss->dev].segment_size; // FIXME locking??
                db = lafs_get_block(fs->devs[ss->dev].segsum,
                                    ss->segment >> (fs->prime_sb->s_blocksize_bits-1),
                                    NULL, GFP_KERNEL | __GFP_NOFAIL,
@@ -1731,10 +1752,14 @@ unsigned long lafs_scan_seg(struct fs *fs)
                yp = map_dblock(fs->scan.youth_db);
                for (i = 0; i < segments ; i++)
                        if (yp[i] == cpu_to_le16(0)) {
-                               if (fs->scan.first_free_pass)
+                               if (fs->scan.first_free_pass) {
                                        fs->free_blocks +=
                                                fs->devs[fs->scan.free_dev]
                                                .segment_size;
+                                       fs->free_segs +=
+                                               fs->devs[fs->scan.free_dev]
+                                               .segment_size;
+                               }
                                if (add_free(fs, fs->scan.free_dev, firstseg + i,
                                             &yp[i])) {
                                        /* Everything in the table owns a reference
diff --git a/state.h b/state.h
index 35ee0b50251cde6f50cbf51349c5f9add38435be..06143728e027ba135ec916ae5ef45b54cd78f1c8 100644 (file)
--- a/state.h
+++ b/state.h
@@ -90,6 +90,10 @@ struct fs {
 #define CleanerDisabled 4
 #define OrphansRunning 5
 #define CheckpointFlushing 6  /* We are writing the segusage blocks */
+#define CleanerBlocks 7        /* One or more threads is blocked waiting for the
+                        * cleaner to progress - cleaner.need blocks are
+                        * needed.
+                        */
 
        struct work_struct done_work;   /* used for handling
                                         * refile after write completes */
@@ -99,6 +103,10 @@ struct fs {
                u32 cleaning;           /* amount of space that is being cleaned
                                         * this checkpoint
                                         */
+               u32 need;               /* Amount of space that is needed by
+                                        * Some thread waiting on CleanerBlocks
+                                        * flag.
+                                        */
                struct mutex lock;      /* protects list mani and refcnt of core
                                         * cleaner.
                                         */
@@ -127,6 +135,7 @@ struct fs {
        /* counters for (pre)allocating space. */
        spinlock_t alloc_lock;
        u64     free_blocks; /* initialised from free segment info */
+       u64     free_segs; /* counts blocks in completely free segments */
        u64     allocated_blocks; /* Blocks that have been (pre)allocated */
        u64     clean_reserved; /* Blocks reserved for cleaning */
        u64     max_segment; /* largest segment size */