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 |
