diff options
author | Joern Engel <joern@logfs.org> | 2010-05-07 13:38:40 -0400 |
---|---|---|
committer | Joern Engel <joern@logfs.org> | 2010-05-07 13:38:40 -0400 |
commit | 6f485b41875dbf5160c1990322469c1f65f77b28 (patch) | |
tree | 9912cee9517b57c2cb3c0318861f2a9eeb4139b2 /fs/logfs/gc.c | |
parent | ccf31c10f125ab5233c8517f91d4b3bd0bd60936 (diff) |
logfs: handle powerfail on NAND flash
The write buffer may not have been written and may no longer be written
due to an interrupted write in the affected page.
Signed-off-by: Joern Engel <joern@logfs.org>
Diffstat (limited to 'fs/logfs/gc.c')
-rw-r--r-- | fs/logfs/gc.c | 49 |
1 files changed, 21 insertions, 28 deletions
diff --git a/fs/logfs/gc.c b/fs/logfs/gc.c index 76c242fbe1b0..caa4419285dc 100644 --- a/fs/logfs/gc.c +++ b/fs/logfs/gc.c | |||
@@ -122,7 +122,7 @@ static void logfs_cleanse_block(struct super_block *sb, u64 ofs, u64 ino, | |||
122 | logfs_safe_iput(inode, cookie); | 122 | logfs_safe_iput(inode, cookie); |
123 | } | 123 | } |
124 | 124 | ||
125 | static u32 logfs_gc_segment(struct super_block *sb, u32 segno, u8 dist) | 125 | static u32 logfs_gc_segment(struct super_block *sb, u32 segno) |
126 | { | 126 | { |
127 | struct logfs_super *super = logfs_super(sb); | 127 | struct logfs_super *super = logfs_super(sb); |
128 | struct logfs_segment_header sh; | 128 | struct logfs_segment_header sh; |
@@ -401,7 +401,7 @@ static int __logfs_gc_once(struct super_block *sb, struct gc_candidate *cand) | |||
401 | segno, (u64)segno << super->s_segshift, | 401 | segno, (u64)segno << super->s_segshift, |
402 | dist, no_free_segments(sb), valid, | 402 | dist, no_free_segments(sb), valid, |
403 | super->s_free_bytes); | 403 | super->s_free_bytes); |
404 | cleaned = logfs_gc_segment(sb, segno, dist); | 404 | cleaned = logfs_gc_segment(sb, segno); |
405 | log_gc("GC segment #%02x complete - now %x valid\n", segno, | 405 | log_gc("GC segment #%02x complete - now %x valid\n", segno, |
406 | valid - cleaned); | 406 | valid - cleaned); |
407 | BUG_ON(cleaned != valid); | 407 | BUG_ON(cleaned != valid); |
@@ -632,38 +632,31 @@ static int check_area(struct super_block *sb, int i) | |||
632 | { | 632 | { |
633 | struct logfs_super *super = logfs_super(sb); | 633 | struct logfs_super *super = logfs_super(sb); |
634 | struct logfs_area *area = super->s_area[i]; | 634 | struct logfs_area *area = super->s_area[i]; |
635 | struct logfs_object_header oh; | 635 | gc_level_t gc_level; |
636 | u32 cleaned, valid, ec; | ||
636 | u32 segno = area->a_segno; | 637 | u32 segno = area->a_segno; |
637 | u32 ofs = area->a_used_bytes; | 638 | u64 ofs = dev_ofs(sb, area->a_segno, area->a_written_bytes); |
638 | __be32 crc; | ||
639 | int err; | ||
640 | 639 | ||
641 | if (!area->a_is_open) | 640 | if (!area->a_is_open) |
642 | return 0; | 641 | return 0; |
643 | 642 | ||
644 | for (ofs = area->a_used_bytes; | 643 | if (super->s_devops->can_write_buf(sb, ofs) == 0) |
645 | ofs <= super->s_segsize - sizeof(oh); | 644 | return 0; |
646 | ofs += (u32)be16_to_cpu(oh.len) + sizeof(oh)) { | ||
647 | err = wbuf_read(sb, dev_ofs(sb, segno, ofs), sizeof(oh), &oh); | ||
648 | if (err) | ||
649 | return err; | ||
650 | |||
651 | if (!memchr_inv(&oh, 0xff, sizeof(oh))) | ||
652 | break; | ||
653 | 645 | ||
654 | crc = logfs_crc32(&oh, sizeof(oh) - 4, 4); | 646 | printk(KERN_INFO"LogFS: Possibly incomplete write at %llx\n", ofs); |
655 | if (crc != oh.crc) { | 647 | /* |
656 | printk(KERN_INFO "interrupted header at %llx\n", | 648 | * The device cannot write back the write buffer. Most likely the |
657 | dev_ofs(sb, segno, ofs)); | 649 | * wbuf was already written out and the system crashed at some point |
658 | return 0; | 650 | * before the journal commit happened. In that case we wouldn't have |
659 | } | 651 | * to do anything. But if the crash happened before the wbuf was |
660 | } | 652 | * written out correctly, we must GC this segment. So assume the |
661 | if (ofs != area->a_used_bytes) { | 653 | * worst and always do the GC run. |
662 | printk(KERN_INFO "%x bytes unaccounted data found at %llx\n", | 654 | */ |
663 | ofs - area->a_used_bytes, | 655 | area->a_is_open = 0; |
664 | dev_ofs(sb, segno, area->a_used_bytes)); | 656 | valid = logfs_valid_bytes(sb, segno, &ec, &gc_level); |
665 | area->a_used_bytes = ofs; | 657 | cleaned = logfs_gc_segment(sb, segno); |
666 | } | 658 | if (cleaned != valid) |
659 | return -EIO; | ||
667 | return 0; | 660 | return 0; |
668 | } | 661 | } |
669 | 662 | ||