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 | |
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')
-rw-r--r-- | fs/logfs/dev_bdev.c | 6 | ||||
-rw-r--r-- | fs/logfs/dev_mtd.c | 24 | ||||
-rw-r--r-- | fs/logfs/gc.c | 49 | ||||
-rw-r--r-- | fs/logfs/logfs.h | 2 |
4 files changed, 52 insertions, 29 deletions
diff --git a/fs/logfs/dev_bdev.c b/fs/logfs/dev_bdev.c index 243c00071f76..9bd2ce2a3040 100644 --- a/fs/logfs/dev_bdev.c +++ b/fs/logfs/dev_bdev.c | |||
@@ -303,6 +303,11 @@ static void bdev_put_device(struct super_block *sb) | |||
303 | close_bdev_exclusive(logfs_super(sb)->s_bdev, FMODE_READ|FMODE_WRITE); | 303 | close_bdev_exclusive(logfs_super(sb)->s_bdev, FMODE_READ|FMODE_WRITE); |
304 | } | 304 | } |
305 | 305 | ||
306 | static int bdev_can_write_buf(struct super_block *sb, u64 ofs) | ||
307 | { | ||
308 | return 0; | ||
309 | } | ||
310 | |||
306 | static const struct logfs_device_ops bd_devops = { | 311 | static const struct logfs_device_ops bd_devops = { |
307 | .find_first_sb = bdev_find_first_sb, | 312 | .find_first_sb = bdev_find_first_sb, |
308 | .find_last_sb = bdev_find_last_sb, | 313 | .find_last_sb = bdev_find_last_sb, |
@@ -310,6 +315,7 @@ static const struct logfs_device_ops bd_devops = { | |||
310 | .readpage = bdev_readpage, | 315 | .readpage = bdev_readpage, |
311 | .writeseg = bdev_writeseg, | 316 | .writeseg = bdev_writeseg, |
312 | .erase = bdev_erase, | 317 | .erase = bdev_erase, |
318 | .can_write_buf = bdev_can_write_buf, | ||
313 | .sync = bdev_sync, | 319 | .sync = bdev_sync, |
314 | .put_device = bdev_put_device, | 320 | .put_device = bdev_put_device, |
315 | }; | 321 | }; |
diff --git a/fs/logfs/dev_mtd.c b/fs/logfs/dev_mtd.c index b02a4020241a..a85d47d13e4b 100644 --- a/fs/logfs/dev_mtd.c +++ b/fs/logfs/dev_mtd.c | |||
@@ -9,6 +9,7 @@ | |||
9 | #include <linux/completion.h> | 9 | #include <linux/completion.h> |
10 | #include <linux/mount.h> | 10 | #include <linux/mount.h> |
11 | #include <linux/sched.h> | 11 | #include <linux/sched.h> |
12 | #include <linux/slab.h> | ||
12 | 13 | ||
13 | #define PAGE_OFS(ofs) ((ofs) & (PAGE_SIZE-1)) | 14 | #define PAGE_OFS(ofs) ((ofs) & (PAGE_SIZE-1)) |
14 | 15 | ||
@@ -126,7 +127,8 @@ static int mtd_readpage(void *_sb, struct page *page) | |||
126 | 127 | ||
127 | err = mtd_read(sb, page->index << PAGE_SHIFT, PAGE_SIZE, | 128 | err = mtd_read(sb, page->index << PAGE_SHIFT, PAGE_SIZE, |
128 | page_address(page)); | 129 | page_address(page)); |
129 | if (err == -EUCLEAN) { | 130 | if (err == -EUCLEAN || err == -EBADMSG) { |
131 | /* -EBADMSG happens regularly on power failures */ | ||
130 | err = 0; | 132 | err = 0; |
131 | /* FIXME: force GC this segment */ | 133 | /* FIXME: force GC this segment */ |
132 | } | 134 | } |
@@ -233,12 +235,32 @@ static void mtd_put_device(struct super_block *sb) | |||
233 | put_mtd_device(logfs_super(sb)->s_mtd); | 235 | put_mtd_device(logfs_super(sb)->s_mtd); |
234 | } | 236 | } |
235 | 237 | ||
238 | static int mtd_can_write_buf(struct super_block *sb, u64 ofs) | ||
239 | { | ||
240 | struct logfs_super *super = logfs_super(sb); | ||
241 | void *buf; | ||
242 | int err; | ||
243 | |||
244 | buf = kmalloc(super->s_writesize, GFP_KERNEL); | ||
245 | if (!buf) | ||
246 | return -ENOMEM; | ||
247 | err = mtd_read(sb, ofs, super->s_writesize, buf); | ||
248 | if (err) | ||
249 | goto out; | ||
250 | if (memchr_inv(buf, 0xff, super->s_writesize)) | ||
251 | err = -EIO; | ||
252 | kfree(buf); | ||
253 | out: | ||
254 | return err; | ||
255 | } | ||
256 | |||
236 | static const struct logfs_device_ops mtd_devops = { | 257 | static const struct logfs_device_ops mtd_devops = { |
237 | .find_first_sb = mtd_find_first_sb, | 258 | .find_first_sb = mtd_find_first_sb, |
238 | .find_last_sb = mtd_find_last_sb, | 259 | .find_last_sb = mtd_find_last_sb, |
239 | .readpage = mtd_readpage, | 260 | .readpage = mtd_readpage, |
240 | .writeseg = mtd_writeseg, | 261 | .writeseg = mtd_writeseg, |
241 | .erase = mtd_erase, | 262 | .erase = mtd_erase, |
263 | .can_write_buf = mtd_can_write_buf, | ||
242 | .sync = mtd_sync, | 264 | .sync = mtd_sync, |
243 | .put_device = mtd_put_device, | 265 | .put_device = mtd_put_device, |
244 | }; | 266 | }; |
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 | ||
diff --git a/fs/logfs/logfs.h b/fs/logfs/logfs.h index 26a9458e6b13..93b55f337245 100644 --- a/fs/logfs/logfs.h +++ b/fs/logfs/logfs.h | |||
@@ -144,6 +144,7 @@ struct logfs_area_ops { | |||
144 | * @erase: erase one segment | 144 | * @erase: erase one segment |
145 | * @read: read from the device | 145 | * @read: read from the device |
146 | * @erase: erase part of the device | 146 | * @erase: erase part of the device |
147 | * @can_write_buf: decide whether wbuf can be written to ofs | ||
147 | */ | 148 | */ |
148 | struct logfs_device_ops { | 149 | struct logfs_device_ops { |
149 | struct page *(*find_first_sb)(struct super_block *sb, u64 *ofs); | 150 | struct page *(*find_first_sb)(struct super_block *sb, u64 *ofs); |
@@ -153,6 +154,7 @@ struct logfs_device_ops { | |||
153 | void (*writeseg)(struct super_block *sb, u64 ofs, size_t len); | 154 | void (*writeseg)(struct super_block *sb, u64 ofs, size_t len); |
154 | int (*erase)(struct super_block *sb, loff_t ofs, size_t len, | 155 | int (*erase)(struct super_block *sb, loff_t ofs, size_t len, |
155 | int ensure_write); | 156 | int ensure_write); |
157 | int (*can_write_buf)(struct super_block *sb, u64 ofs); | ||
156 | void (*sync)(struct super_block *sb); | 158 | void (*sync)(struct super_block *sb); |
157 | void (*put_device)(struct super_block *sb); | 159 | void (*put_device)(struct super_block *sb); |
158 | }; | 160 | }; |