aboutsummaryrefslogtreecommitdiffstats
path: root/fs/logfs/gc.c
diff options
context:
space:
mode:
authorJoern Engel <joern@logfs.org>2010-05-07 13:38:40 -0400
committerJoern Engel <joern@logfs.org>2010-05-07 13:38:40 -0400
commit6f485b41875dbf5160c1990322469c1f65f77b28 (patch)
tree9912cee9517b57c2cb3c0318861f2a9eeb4139b2 /fs/logfs/gc.c
parentccf31c10f125ab5233c8517f91d4b3bd0bd60936 (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.c49
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
125static u32 logfs_gc_segment(struct super_block *sb, u32 segno, u8 dist) 125static 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