diff options
author | Ryusuke Konishi <konishi.ryusuke@lab.ntt.co.jp> | 2009-04-05 05:30:58 -0400 |
---|---|---|
committer | Ryusuke Konishi <konishi.ryusuke@lab.ntt.co.jp> | 2009-04-12 20:53:52 -0400 |
commit | c85399c2da8b86de8f6877980294fa1a4a88a5a4 (patch) | |
tree | 0319b8ab4d8b7da4d614e46949755b8a9ba4eab0 | |
parent | a703018f7bbec8109419318f5d51f235fdce5155 (diff) |
nilfs2: fix possible mismatch of sufile counters on recovery
On-disk counters ndirtysegs and ncleansegs of sufile, can go wrong
after roll-forward recovery because
nilfs_prepare_segment_for_recovery() function marks segments dirty
without adjusting value of these counters.
This fixes the problem by adding a function to sufile which does the
operation adjusting the counters, and by letting the recovery function
use it.
Signed-off-by: Ryusuke Konishi <konishi.ryusuke@lab.ntt.co.jp>
-rw-r--r-- | fs/nilfs2/recovery.c | 20 | ||||
-rw-r--r-- | fs/nilfs2/sufile.c | 29 | ||||
-rw-r--r-- | fs/nilfs2/sufile.h | 12 |
3 files changed, 45 insertions, 16 deletions
diff --git a/fs/nilfs2/recovery.c b/fs/nilfs2/recovery.c index 6ade0963fc1d..4fc081e47d70 100644 --- a/fs/nilfs2/recovery.c +++ b/fs/nilfs2/recovery.c | |||
@@ -413,7 +413,6 @@ static int nilfs_prepare_segment_for_recovery(struct the_nilfs *nilfs, | |||
413 | struct nilfs_segment_entry *ent, *n; | 413 | struct nilfs_segment_entry *ent, *n; |
414 | struct inode *sufile = nilfs->ns_sufile; | 414 | struct inode *sufile = nilfs->ns_sufile; |
415 | __u64 segnum[4]; | 415 | __u64 segnum[4]; |
416 | time_t mtime; | ||
417 | int err; | 416 | int err; |
418 | int i; | 417 | int i; |
419 | 418 | ||
@@ -442,24 +441,13 @@ static int nilfs_prepare_segment_for_recovery(struct the_nilfs *nilfs, | |||
442 | * Collecting segments written after the latest super root. | 441 | * Collecting segments written after the latest super root. |
443 | * These are marked dirty to avoid being reallocated in the next write. | 442 | * These are marked dirty to avoid being reallocated in the next write. |
444 | */ | 443 | */ |
445 | mtime = get_seconds(); | ||
446 | list_for_each_entry_safe(ent, n, head, list) { | 444 | list_for_each_entry_safe(ent, n, head, list) { |
447 | if (ent->segnum == segnum[0]) { | 445 | if (ent->segnum != segnum[0]) { |
448 | list_del(&ent->list); | 446 | err = nilfs_sufile_scrap(sufile, ent->segnum); |
449 | nilfs_free_segment_entry(ent); | 447 | if (unlikely(err)) |
450 | continue; | 448 | goto failed; |
451 | } | ||
452 | err = nilfs_open_segment_entry(ent, sufile); | ||
453 | if (unlikely(err)) | ||
454 | goto failed; | ||
455 | if (!nilfs_segment_usage_dirty(ent->raw_su)) { | ||
456 | /* make the segment garbage */ | ||
457 | ent->raw_su->su_nblocks = cpu_to_le32(0); | ||
458 | ent->raw_su->su_lastmod = cpu_to_le32(mtime); | ||
459 | nilfs_segment_usage_set_dirty(ent->raw_su); | ||
460 | } | 449 | } |
461 | list_del(&ent->list); | 450 | list_del(&ent->list); |
462 | nilfs_close_segment_entry(ent, sufile); | ||
463 | nilfs_free_segment_entry(ent); | 451 | nilfs_free_segment_entry(ent); |
464 | } | 452 | } |
465 | 453 | ||
diff --git a/fs/nilfs2/sufile.c b/fs/nilfs2/sufile.c index 07013f58dfe9..98e68677f045 100644 --- a/fs/nilfs2/sufile.c +++ b/fs/nilfs2/sufile.c | |||
@@ -258,6 +258,35 @@ void nilfs_sufile_do_cancel_free(struct inode *sufile, __u64 segnum, | |||
258 | nilfs_mdt_mark_dirty(sufile); | 258 | nilfs_mdt_mark_dirty(sufile); |
259 | } | 259 | } |
260 | 260 | ||
261 | void nilfs_sufile_do_scrap(struct inode *sufile, __u64 segnum, | ||
262 | struct buffer_head *header_bh, | ||
263 | struct buffer_head *su_bh) | ||
264 | { | ||
265 | struct nilfs_segment_usage *su; | ||
266 | void *kaddr; | ||
267 | int clean, dirty; | ||
268 | |||
269 | kaddr = kmap_atomic(su_bh->b_page, KM_USER0); | ||
270 | su = nilfs_sufile_block_get_segment_usage(sufile, segnum, su_bh, kaddr); | ||
271 | if (su->su_flags == cpu_to_le32(1UL << NILFS_SEGMENT_USAGE_DIRTY) && | ||
272 | su->su_nblocks == cpu_to_le32(0)) { | ||
273 | kunmap_atomic(kaddr, KM_USER0); | ||
274 | return; | ||
275 | } | ||
276 | clean = nilfs_segment_usage_clean(su); | ||
277 | dirty = nilfs_segment_usage_dirty(su); | ||
278 | |||
279 | /* make the segment garbage */ | ||
280 | su->su_lastmod = cpu_to_le64(0); | ||
281 | su->su_nblocks = cpu_to_le32(0); | ||
282 | su->su_flags = cpu_to_le32(1UL << NILFS_SEGMENT_USAGE_DIRTY); | ||
283 | kunmap_atomic(kaddr, KM_USER0); | ||
284 | |||
285 | nilfs_sufile_mod_counter(header_bh, clean ? (u64)-1 : 0, dirty ? 0 : 1); | ||
286 | nilfs_mdt_mark_buffer_dirty(su_bh); | ||
287 | nilfs_mdt_mark_dirty(sufile); | ||
288 | } | ||
289 | |||
261 | void nilfs_sufile_do_free(struct inode *sufile, __u64 segnum, | 290 | void nilfs_sufile_do_free(struct inode *sufile, __u64 segnum, |
262 | struct buffer_head *header_bh, | 291 | struct buffer_head *header_bh, |
263 | struct buffer_head *su_bh) | 292 | struct buffer_head *su_bh) |
diff --git a/fs/nilfs2/sufile.h b/fs/nilfs2/sufile.h index 449a6e2671b0..a2e2efd4ade1 100644 --- a/fs/nilfs2/sufile.h +++ b/fs/nilfs2/sufile.h | |||
@@ -52,6 +52,8 @@ int nilfs_sufile_update(struct inode *, __u64, int, | |||
52 | struct buffer_head *)); | 52 | struct buffer_head *)); |
53 | void nilfs_sufile_do_cancel_free(struct inode *, __u64, struct buffer_head *, | 53 | void nilfs_sufile_do_cancel_free(struct inode *, __u64, struct buffer_head *, |
54 | struct buffer_head *); | 54 | struct buffer_head *); |
55 | void nilfs_sufile_do_scrap(struct inode *, __u64, struct buffer_head *, | ||
56 | struct buffer_head *); | ||
55 | void nilfs_sufile_do_free(struct inode *, __u64, struct buffer_head *, | 57 | void nilfs_sufile_do_free(struct inode *, __u64, struct buffer_head *, |
56 | struct buffer_head *); | 58 | struct buffer_head *); |
57 | void nilfs_sufile_do_set_error(struct inode *, __u64, struct buffer_head *, | 59 | void nilfs_sufile_do_set_error(struct inode *, __u64, struct buffer_head *, |
@@ -78,6 +80,16 @@ static inline int nilfs_sufile_cancel_free(struct inode *sufile, __u64 segnum) | |||
78 | } | 80 | } |
79 | 81 | ||
80 | /** | 82 | /** |
83 | * nilfs_sufile_scrap - make a segment garbage | ||
84 | * @sufile: inode of segment usage file | ||
85 | * @segnum: segment number to be freed | ||
86 | */ | ||
87 | static inline int nilfs_sufile_scrap(struct inode *sufile, __u64 segnum) | ||
88 | { | ||
89 | return nilfs_sufile_update(sufile, segnum, 1, nilfs_sufile_do_scrap); | ||
90 | } | ||
91 | |||
92 | /** | ||
81 | * nilfs_sufile_free - free segment | 93 | * nilfs_sufile_free - free segment |
82 | * @sufile: inode of segment usage file | 94 | * @sufile: inode of segment usage file |
83 | * @segnum: segment number to be freed | 95 | * @segnum: segment number to be freed |