From: NeilBrown Date: Sat, 14 Aug 2010 03:36:36 +0000 (+1000) Subject: Implement youth decay. X-Git-Url: http://git.neil.brown.name/?a=commitdiff_plain;h=27dfe0735e2094e09e18b3ea0fc341c4de48d395;p=LaFS.git Implement youth decay. - 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 --- diff --git a/checkpoint.c b/checkpoint.c index 8eccf47..d3ba9fe 100644 --- a/checkpoint.c +++ b/checkpoint.c @@ -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 diff --git a/layout.h b/layout.h index efa077d..c1e5ea2 100644 --- 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; +} diff --git a/segments.c b/segments.c index 66c7c0f..6091449 100644 --- a/segments.c +++ b/segments.c @@ -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 ee37bec..0c10615 100644 --- 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 1f4364e..9bd0ccd 100644 --- 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);