This patch introduces a mount option bad_ftl that disables the periodic overwrites of the super block to make the file system better suitable for bad flash memory with a bad FTL. The super block is only written at umount time. So if there is a power outage the file system needs to be recovered by a linear scan of all segment summary blocks. The linear scan is only necessary if the file system wasn't umounted properly. So the normal mount time is not affected. Signed-off-by: Andreas Rohner <andreas.rohner@xxxxxxx> --- fs/nilfs2/segbuf.c | 3 ++- fs/nilfs2/segment.c | 3 ++- fs/nilfs2/super.c | 10 +++++++-- fs/nilfs2/the_nilfs.c | 54 ++++++++++++++++++++++++++++++++++++++++++++--- include/linux/nilfs2_fs.h | 4 +++- 5 files changed, 66 insertions(+), 8 deletions(-) diff --git a/fs/nilfs2/segbuf.c b/fs/nilfs2/segbuf.c index 2d8be51..4ea9dd6 100644 --- a/fs/nilfs2/segbuf.c +++ b/fs/nilfs2/segbuf.c @@ -158,6 +158,7 @@ void nilfs_segbuf_fill_in_segsum(struct nilfs_segment_buffer *segbuf) { struct nilfs_segment_summary *raw_sum; struct buffer_head *bh_sum; + struct the_nilfs *nilfs = segbuf->sb_super->s_fs_info; bh_sum = list_entry(segbuf->sb_segsum_buffers.next, struct buffer_head, b_assoc_buffers); @@ -172,7 +173,7 @@ void nilfs_segbuf_fill_in_segsum(struct nilfs_segment_buffer *segbuf) raw_sum->ss_nblocks = cpu_to_le32(segbuf->sb_sum.nblocks); raw_sum->ss_nfinfo = cpu_to_le32(segbuf->sb_sum.nfinfo); raw_sum->ss_sumbytes = cpu_to_le32(segbuf->sb_sum.sumbytes); - raw_sum->ss_pad = 0; + raw_sum->ss_crc_seed = cpu_to_le32(nilfs->ns_crc_seed); raw_sum->ss_cno = cpu_to_le64(segbuf->sb_sum.cno); } diff --git a/fs/nilfs2/segment.c b/fs/nilfs2/segment.c index a1a1916..e8e38a9 100644 --- a/fs/nilfs2/segment.c +++ b/fs/nilfs2/segment.c @@ -2288,7 +2288,8 @@ static int nilfs_segctor_construct(struct nilfs_sc_info *sci, int mode) if (mode != SC_FLUSH_DAT) atomic_set(&nilfs->ns_ndirtyblks, 0); if (test_bit(NILFS_SC_SUPER_ROOT, &sci->sc_flags) && - nilfs_discontinued(nilfs)) { + nilfs_discontinued(nilfs) && + !nilfs_test_opt(nilfs, BAD_FTL)) { down_write(&nilfs->ns_sem); err = -EIO; sbp = nilfs_prepare_super(sci->sc_super, diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c index 7ac2a12..c3374ed 100644 --- a/fs/nilfs2/super.c +++ b/fs/nilfs2/super.c @@ -505,7 +505,7 @@ static int nilfs_sync_fs(struct super_block *sb, int wait) err = nilfs_construct_segment(sb); down_write(&nilfs->ns_sem); - if (nilfs_sb_dirty(nilfs)) { + if (nilfs_sb_dirty(nilfs) && !nilfs_test_opt(nilfs, BAD_FTL)) { sbp = nilfs_prepare_super(sb, nilfs_sb_will_flip(nilfs)); if (likely(sbp)) { nilfs_set_log_cursor(sbp[0], nilfs); @@ -691,6 +691,8 @@ static int nilfs_show_options(struct seq_file *seq, struct dentry *dentry) seq_puts(seq, ",norecovery"); if (nilfs_test_opt(nilfs, DISCARD)) seq_puts(seq, ",discard"); + if (nilfs_test_opt(nilfs, BAD_FTL)) + seq_puts(seq, ",bad_ftl"); return 0; } @@ -712,7 +714,7 @@ static const struct super_operations nilfs_sops = { enum { Opt_err_cont, Opt_err_panic, Opt_err_ro, Opt_barrier, Opt_nobarrier, Opt_snapshot, Opt_order, Opt_norecovery, - Opt_discard, Opt_nodiscard, Opt_err, + Opt_discard, Opt_nodiscard, Opt_err, Opt_bad_ftl, }; static match_table_t tokens = { @@ -726,6 +728,7 @@ static match_table_t tokens = { {Opt_norecovery, "norecovery"}, {Opt_discard, "discard"}, {Opt_nodiscard, "nodiscard"}, + {Opt_bad_ftl, "bad_ftl"}, {Opt_err, NULL} }; @@ -787,6 +790,9 @@ static int parse_options(char *options, struct super_block *sb, int is_remount) case Opt_nodiscard: nilfs_clear_opt(nilfs, DISCARD); break; + case Opt_bad_ftl: + nilfs_set_opt(nilfs, BAD_FTL); + break; default: printk(KERN_ERR "NILFS: Unrecognized mount option \"%s\"\n", p); diff --git a/fs/nilfs2/the_nilfs.c b/fs/nilfs2/the_nilfs.c index 94c451c..d29b2f0 100644 --- a/fs/nilfs2/the_nilfs.c +++ b/fs/nilfs2/the_nilfs.c @@ -168,6 +168,50 @@ static void nilfs_clear_recovery_info(struct nilfs_recovery_info *ri) nilfs_dispose_segment_list(&ri->ri_used_segments); } +static int nilfs_search_log_cursor(struct the_nilfs *nilfs) +{ + u64 segnum; + struct buffer_head *bh_sum = NULL; + struct nilfs_segment_summary *sum; + sector_t seg_start, seg_end; /* range of full segment (block number) */ + + for (segnum = 0; segnum < nilfs->ns_nsegments; ++segnum) { + brelse(bh_sum); + + /* Calculate range of segment */ + nilfs_get_segment_range(nilfs, segnum, &seg_start, &seg_end); + + bh_sum = __bread(nilfs->ns_bdev, seg_start, + nilfs->ns_blocksize); + if (!bh_sum) { + printk(KERN_ERR "NILFS error searching for cursor.\n"); + return -EINVAL; + } + + sum = (struct nilfs_segment_summary *)bh_sum->b_data; + + /* + * use ss_crc_seed to distinguish the segments from previous + * nilfs2 file systems on the same volume + */ + if (le32_to_cpu(sum->ss_magic) != NILFS_SEGSUM_MAGIC + || le32_to_cpu(sum->ss_nblocks) == 0 + || le32_to_cpu(sum->ss_nblocks) > + nilfs->ns_blocks_per_segment + || le32_to_cpu(sum->ss_crc_seed) != nilfs->ns_crc_seed) + continue; + + if (le64_to_cpu(sum->ss_seq) > nilfs->ns_last_seq) { + nilfs->ns_last_pseg = seg_start; + nilfs->ns_last_cno = le64_to_cpu(sum->ss_cno); + nilfs->ns_last_seq = le64_to_cpu(sum->ss_seq); + } + } + brelse(bh_sum); + + return 0; +} + /** * nilfs_store_log_cursor - load log cursor from a super block * @nilfs: nilfs object @@ -179,7 +223,7 @@ static void nilfs_clear_recovery_info(struct nilfs_recovery_info *ri) * scanning and recovery. */ static int nilfs_store_log_cursor(struct the_nilfs *nilfs, - struct nilfs_super_block *sbp) + struct nilfs_super_block *sbp, int valid_fs) { int ret = 0; @@ -187,6 +231,9 @@ static int nilfs_store_log_cursor(struct the_nilfs *nilfs, nilfs->ns_last_cno = le64_to_cpu(sbp->s_last_cno); nilfs->ns_last_seq = le64_to_cpu(sbp->s_last_seq); + if (!valid_fs && nilfs_test_opt(nilfs, BAD_FTL)) + ret = nilfs_search_log_cursor(nilfs); + nilfs->ns_prev_seq = nilfs->ns_last_seq; nilfs->ns_seg_seq = nilfs->ns_last_seq; nilfs->ns_segnum = @@ -263,7 +310,7 @@ int load_nilfs(struct the_nilfs *nilfs, struct super_block *sb) goto scan_error; } - err = nilfs_store_log_cursor(nilfs, sbp[0]); + err = nilfs_store_log_cursor(nilfs, sbp[0], 1); if (err) goto scan_error; @@ -626,7 +673,8 @@ int init_nilfs(struct the_nilfs *nilfs, struct super_block *sb, char *data) nilfs->ns_mount_state = le16_to_cpu(sbp->s_state); - err = nilfs_store_log_cursor(nilfs, sbp); + err = nilfs_store_log_cursor(nilfs, sbp, + nilfs->ns_mount_state & NILFS_VALID_FS); if (err) goto failed_sbh; diff --git a/include/linux/nilfs2_fs.h b/include/linux/nilfs2_fs.h index 4140f7f..6a8f5f8 100644 --- a/include/linux/nilfs2_fs.h +++ b/include/linux/nilfs2_fs.h @@ -135,6 +135,8 @@ struct nilfs_super_root { #define NILFS_MOUNT_NORECOVERY 0x4000 /* Disable write access during mount-time recovery */ #define NILFS_MOUNT_DISCARD 0x8000 /* Issue DISCARD requests */ +#define NILFS_MOUNT_BAD_FTL 0x10000 /* Only write super block + at umount time */ /** @@ -422,7 +424,7 @@ struct nilfs_segment_summary { __le32 ss_nblocks; __le32 ss_nfinfo; __le32 ss_sumbytes; - __le32 ss_pad; + __le32 ss_crc_seed; __le64 ss_cno; /* array of finfo structures */ }; -- 1.8.5.3 -- To unsubscribe from this list: send the line "unsubscribe linux-nilfs" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html