]> git.neil.brown.name Git - LaFS.git/commitdiff
Implement youth decay.
authorNeilBrown <neilb@suse.de>
Sat, 14 Aug 2010 03:36:36 +0000 (13:36 +1000)
committerNeilBrown <neilb@suse.de>
Sat, 14 Aug 2010 03:36:36 +0000 (13:36 +1000)
 - After a checkpoint, check if we are close enough to the end
   of youth space to need a decay.
 - when we record a new youth number, un-decay it if the block hasn't
   been decayed yet (and convert endian properly)
 - Change scan_seg to updates free_block/free_dev atomically in just
   one place, and do a block worth of decay at that point.
   As part of this, the youth block is only released at one place now.

Signed-off-by: NeilBrown <neilb@suse.de>
checkpoint.c
layout.h
segments.c
state.h
super.c

index 8eccf47df233370976df341b7814dff673cac50b..d3ba9fe0cae3c7966d61462b21eaea9cb01c7a7b 100644 (file)
@@ -503,8 +503,17 @@ static void finish_checkpoint(struct fs *fs, int youth)
        lafs_write_state(fs);
        dprintk("State written, all done %d\n", fs->seq);
 
-       if (!test_bit(CleanerDisabled, &fs->fsstate))
+       if (!test_bit(CleanerDisabled, &fs->fsstate)) {
                fs->scan.done = 0;
+               if (youth > 65536 - 2 * fs->max_newsegs) {
+                       /* time to decay youth */
+                       spin_lock(&fs->lock);
+                       youth = decay_youth(youth);
+                       fs->youth_next = decay_youth(fs->youth_next);
+                       fs->scan.do_decay = 1;
+                       spin_unlock(&fs->lock);
+               }
+       }
        fs->cleaner.cleaning = 0;
 
        fs->checkpoint_youth = youth;
@@ -526,7 +535,7 @@ unsigned long lafs_do_checkpoint(struct fs *fs)
        if (!test_bit(CheckpointNeeded, &fs->fsstate))
                return MAX_SCHEDULE_TIMEOUT;
 
-       if (fs->cleaner.active)
+       if (fs->cleaner.active || ! fs->scan.done)
                return HZ/10; /* FIXME that is gross ... is it needed? */
 
        /* Make sure all cleaner blocks are flushed as if anything lingers
index efa077d72084cb9935de34985365c74e643d9f98..c1e5ea22caccdc8c716cffa9d00b15053e13bde2 100644 (file)
--- a/layout.h
+++ b/layout.h
@@ -265,3 +265,20 @@ struct orphan {
        u32     inum;
        u32     addr;
 } __attribute__((packed));
+
+/* Youth values are decayed when nextyouth gets too big */
+static int inline decay_youth(int y)
+{
+       if (y < 8)
+               return y;
+       if (y < 32768+8)
+               y = (y-8)/2 + 8;
+       else
+               y -= 16384;
+       return y;
+}
+/* This is only called on large youth values */
+static int inline decay_undo(int y)
+{
+       return y + 16384;
+}
index 66c7c0fea9060f89d1da97db2d8681e503202f50..6091449f64eea1746680c4e22e1a7af8efd683f9 100644 (file)
@@ -1115,10 +1115,19 @@ again:
                }
 
                if (db) {
+                       u16 y = fs->youth_next;
+                       if (fs->scan.do_decay &&
+                           (fs->scan.free_dev < ss->dev
+                            || (fs->scan.free_dev == ss->dev 
+                                && fs->scan.free_block < (ss->segment
+                                                          / (fs->blocksize / 2)))
+                                   ))
+                               /* Haven't decayed this block yet - revert decay */
+                               y = decay_undo(y);
                        youthp = map_dblock(db);
                        youthp[(*seg) & ((1 << (fs->blocksize_bits
                                                - 1)) - 1)]
-                               = fs->youth_next;
+                               = cpu_to_le16(y);
                        unmap_dblock(db, youthp);
                }
                fs->youth_next++;
@@ -1744,51 +1753,91 @@ unsigned long lafs_scan_seg(struct fs *fs)
        dprintk("scan: dev=%d block=%d stage=%d\n",
                fs->scan.free_dev, fs->scan.free_block, fs->scan.free_stage);
        if (fs->scan.free_stage == 0) {
-               /* Need to find the youth block for this dev/offset.
+               /* Need to find the youth block for next dev/offset.
                 * Possibly we are finished with this dev and must go
                 * to next.  Possibly we are finished altogether.
                 */
+               int dev = fs->scan.free_dev;
+               int block = fs->scan.free_block + 1;
+               int err;
 
-               while (fs->scan.free_block >
-                      (fs->devs[fs->scan.free_dev].segment_count
-                       >> (fs->blocksize_bits - 1))) {
-                       fs->scan.free_dev++;
-                       fs->scan.free_block = 0;
-                       if (fs->scan.free_dev >= fs->devices) {
-                               fs->scan.free_dev = 0;
-                               fs->scan.done = 1;
+               while (dev < 0 ||
+                      block > (fs->devs[dev].segment_count
+                               >> (fs->blocksize_bits - 1))) {
+                       dev++;
+                       block = 0;
+                       if (dev >= fs->devices) {
+                               fs->scan.free_dev = -1;
+                               dev = -1;
+                               fs->scan.do_decay = 0;
                                fs->scan.trace = 0;
                                fs->total_free_prev = fs->total_free;
                                fs->total_free = 0;
                                fs->scan.first_free_pass = 0;
-                               wake_up(&fs->phase_wait);
-                               return MAX_SCHEDULE_TIMEOUT;
+                               break;
                        }
                }
+               if (fs->scan.youth_db)
+                       if (fs->scan.youth_db->b.fileaddr != block ||
+                           dev < 0 ||
+                           fs->scan.youth_db->b.inode != fs->devs[dev].segsum) {
+                               putdref(fs->scan.youth_db, MKREF(youth_scan));
+                               fs->scan.youth_db = NULL;
+                       }
+               if (dev == -1) {
+                       fs->scan.done = 1;
+                       wake_up(&fs->phase_wait);
+                       return MAX_SCHEDULE_TIMEOUT;
+               }
+
                if (fs->scan.youth_db == NULL)
                        fs->scan.youth_db =
-                               lafs_get_block(fs->devs[fs->scan.free_dev]
-                                              .segsum,
-                                              fs->scan.free_block,
-                                              NULL, GFP_KERNEL, MKREF(youth));
+                               lafs_get_block(fs->devs[dev].segsum,
+                                              block,
+                                              NULL, GFP_KERNEL, MKREF(youth_scan));
                if (!fs->scan.youth_db) {
                        printk("EEEEEKKKKK get_block failed\n");
-                       fs->scan.free_block++;
-                       return 1;
-               }
-               switch (lafs_read_block_async(fs->scan.youth_db)) {
-               default:
-               case -EIO:
-                       printk("EEEEEKKKKK read of youth block failed\n");
-                       putdref(fs->scan.youth_db, MKREF(youth));
-                       fs->scan.youth_db = NULL;
-                       fs->scan.free_block++;
-                       return 1;
-               case -EAGAIN:
                        return MAX_SCHEDULE_TIMEOUT;
-               case 0:
-                       break;
+               } else
+                       switch (err = lafs_read_block_async(fs->scan.youth_db)) {
+                       default:
+                       case -EIO:
+                               printk("EEEEEKKKKK read of youth block failed\n");
+                               break;
+                       case -EAGAIN:
+                               return MAX_SCHEDULE_TIMEOUT;
+                       case 0:
+                               break;
+                       }
+
+               if (fs->scan.do_decay) {
+                       /* youth_db must be writable */
+                       struct datablock *db = fs->scan.youth_db;
+                       lafs_checkpoint_lock(fs);
+                       set_bit(B_PinPending, &db->b.flags);
+                       lafs_pin_dblock(db, AccountSpace);
                }
+               spin_lock(&fs->lock);
+               fs->scan.free_block = block;
+               fs->scan.free_dev = dev;
+               if (!err && fs->scan.do_decay) {
+                       u16 *yp = map_dblock(fs->scan.youth_db);
+                       int i;
+                       int segperblk = fs->blocksize / 2;
+
+                       for (i = 0 ; i < segperblk ; i++) {
+                               int y = le16_to_cpu(yp[i]);
+                               if (y >= 8)
+                                       y = decay_youth(y);
+                       }
+                       unmap_dblock(fs->scan.youth_db, yp);
+                       lafs_dirty_dblock(fs->scan.youth_db);
+               }
+               spin_unlock(&fs->lock);
+               if (fs->scan.do_decay)
+                       lafs_checkpoint_unlock(fs);
+               if (err)
+                       return 1;
                fs->scan.free_stage = 1;
        }
        WARN_ON(lafs_check_seg_cnt(fs->segtrack));
@@ -1814,10 +1863,7 @@ unsigned long lafs_scan_seg(struct fs *fs)
                if (!db) {
                        printk("EEEEKKK get_block for first usage failed\n");
                abort:
-                       fs->scan.free_block++;
                        fs->scan.free_stage = 0;
-                       putdref(fs->scan.youth_db, MKREF(youth));
-                       fs->scan.youth_db = NULL;
                        return 1;
                }
                switch (lafs_read_block_async(db)) {
@@ -1891,10 +1937,7 @@ unsigned long lafs_scan_seg(struct fs *fs)
                if (!db) {
                        printk("EEEEKKK get_block for subsequent usage failed\n");
                abort2:
-                       fs->scan.free_block++;
                        fs->scan.free_stage = 0;
-                       putdref(fs->scan.youth_db, MKREF(youth));
-                       fs->scan.youth_db = NULL;
                        putdref(fs->scan.usage0_db, MKREF(usage0));
                        fs->scan.usage0_db = NULL;
                        return 1;
@@ -1946,16 +1989,15 @@ unsigned long lafs_scan_seg(struct fs *fs)
                        }
 
                unmap_dblock(fs->scan.youth_db, yp);
-               putdref(fs->scan.youth_db, MKREF(youth));
-               fs->scan.youth_db = NULL;
                putdref(fs->scan.usage0_db, MKREF(usage0));
                fs->scan.usage0_db = NULL;
-               fs->scan.free_block++;
                fs->scan.free_stage = 0;
        }
        WARN_ON(lafs_check_seg_cnt(fs->segtrack));
        if (fs->scan.trace)
                return HZ/10;
+       else if (fs->scan.free_stage == 0)
+               return 1;
        else
                return MAX_SCHEDULE_TIMEOUT;
 }
diff --git a/state.h b/state.h
index ee37bec839c54975f13095ee347a0195ca2199b3..0c10615d8f57331b9e4c118cbdc32f2f0db52ce0 100644 (file)
--- a/state.h
+++ b/state.h
@@ -180,9 +180,7 @@ struct fs {
 
        /* Youth management */
        int     youth_next;     /* number to assign to next segment */
-       unsigned short  youth_dev;      /* device number being decayed, or devs_loaded if none */
-       int     youth_block;    /* block number of next block to decay */
-       int checkpoint_youth; // FIXME make sure this gets decayed
+       int     checkpoint_youth;
 
 /* 10 heights: 0 to 9 */
 #define        SEG_NUM_HEIGHTS (10)
@@ -207,7 +205,7 @@ struct fs {
        struct {
                int     free_dev, free_block, free_stage;
                int     first_free_pass;        /* true the first time */
-               int     done; /* cleared on each checkpoint */
+               int     done, do_decay; /* cleared on each checkpoint */
                struct  datablock *youth_db, *usage0_db;
                u16     *free_usages; /* This is an allocated page */
                int     trace;
diff --git a/super.c b/super.c
index 1f4364e702fe600e261e6837466da9407376093b..9bd0ccd5c054a8a9bdcc89e3484f4d58356bc377 100644 (file)
--- a/super.c
+++ b/super.c
@@ -518,6 +518,7 @@ lafs_load(struct fs *fs, struct options *op, int newest)
        if (fs->youth_next < 8)
                fs->youth_next = 8;
        fs->scan.first_free_pass = 1;
+       fs->scan.free_dev = -1;
 
        fs->maxsnapshot = le32_to_cpu(st->maxsnapshot);