mirror of
https://github.com/torvalds/linux.git
synced 2024-11-22 20:22:09 +00:00
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>
This commit is contained in:
parent
a703018f7b
commit
c85399c2da
@ -413,7 +413,6 @@ static int nilfs_prepare_segment_for_recovery(struct the_nilfs *nilfs,
|
||||
struct nilfs_segment_entry *ent, *n;
|
||||
struct inode *sufile = nilfs->ns_sufile;
|
||||
__u64 segnum[4];
|
||||
time_t mtime;
|
||||
int err;
|
||||
int i;
|
||||
|
||||
@ -442,24 +441,13 @@ static int nilfs_prepare_segment_for_recovery(struct the_nilfs *nilfs,
|
||||
* Collecting segments written after the latest super root.
|
||||
* These are marked dirty to avoid being reallocated in the next write.
|
||||
*/
|
||||
mtime = get_seconds();
|
||||
list_for_each_entry_safe(ent, n, head, list) {
|
||||
if (ent->segnum == segnum[0]) {
|
||||
list_del(&ent->list);
|
||||
nilfs_free_segment_entry(ent);
|
||||
continue;
|
||||
}
|
||||
err = nilfs_open_segment_entry(ent, sufile);
|
||||
if (unlikely(err))
|
||||
goto failed;
|
||||
if (!nilfs_segment_usage_dirty(ent->raw_su)) {
|
||||
/* make the segment garbage */
|
||||
ent->raw_su->su_nblocks = cpu_to_le32(0);
|
||||
ent->raw_su->su_lastmod = cpu_to_le32(mtime);
|
||||
nilfs_segment_usage_set_dirty(ent->raw_su);
|
||||
if (ent->segnum != segnum[0]) {
|
||||
err = nilfs_sufile_scrap(sufile, ent->segnum);
|
||||
if (unlikely(err))
|
||||
goto failed;
|
||||
}
|
||||
list_del(&ent->list);
|
||||
nilfs_close_segment_entry(ent, sufile);
|
||||
nilfs_free_segment_entry(ent);
|
||||
}
|
||||
|
||||
|
@ -258,6 +258,35 @@ void nilfs_sufile_do_cancel_free(struct inode *sufile, __u64 segnum,
|
||||
nilfs_mdt_mark_dirty(sufile);
|
||||
}
|
||||
|
||||
void nilfs_sufile_do_scrap(struct inode *sufile, __u64 segnum,
|
||||
struct buffer_head *header_bh,
|
||||
struct buffer_head *su_bh)
|
||||
{
|
||||
struct nilfs_segment_usage *su;
|
||||
void *kaddr;
|
||||
int clean, dirty;
|
||||
|
||||
kaddr = kmap_atomic(su_bh->b_page, KM_USER0);
|
||||
su = nilfs_sufile_block_get_segment_usage(sufile, segnum, su_bh, kaddr);
|
||||
if (su->su_flags == cpu_to_le32(1UL << NILFS_SEGMENT_USAGE_DIRTY) &&
|
||||
su->su_nblocks == cpu_to_le32(0)) {
|
||||
kunmap_atomic(kaddr, KM_USER0);
|
||||
return;
|
||||
}
|
||||
clean = nilfs_segment_usage_clean(su);
|
||||
dirty = nilfs_segment_usage_dirty(su);
|
||||
|
||||
/* make the segment garbage */
|
||||
su->su_lastmod = cpu_to_le64(0);
|
||||
su->su_nblocks = cpu_to_le32(0);
|
||||
su->su_flags = cpu_to_le32(1UL << NILFS_SEGMENT_USAGE_DIRTY);
|
||||
kunmap_atomic(kaddr, KM_USER0);
|
||||
|
||||
nilfs_sufile_mod_counter(header_bh, clean ? (u64)-1 : 0, dirty ? 0 : 1);
|
||||
nilfs_mdt_mark_buffer_dirty(su_bh);
|
||||
nilfs_mdt_mark_dirty(sufile);
|
||||
}
|
||||
|
||||
void nilfs_sufile_do_free(struct inode *sufile, __u64 segnum,
|
||||
struct buffer_head *header_bh,
|
||||
struct buffer_head *su_bh)
|
||||
|
@ -52,6 +52,8 @@ int nilfs_sufile_update(struct inode *, __u64, int,
|
||||
struct buffer_head *));
|
||||
void nilfs_sufile_do_cancel_free(struct inode *, __u64, struct buffer_head *,
|
||||
struct buffer_head *);
|
||||
void nilfs_sufile_do_scrap(struct inode *, __u64, struct buffer_head *,
|
||||
struct buffer_head *);
|
||||
void nilfs_sufile_do_free(struct inode *, __u64, struct buffer_head *,
|
||||
struct buffer_head *);
|
||||
void nilfs_sufile_do_set_error(struct inode *, __u64, struct buffer_head *,
|
||||
@ -77,6 +79,16 @@ static inline int nilfs_sufile_cancel_free(struct inode *sufile, __u64 segnum)
|
||||
nilfs_sufile_do_cancel_free);
|
||||
}
|
||||
|
||||
/**
|
||||
* nilfs_sufile_scrap - make a segment garbage
|
||||
* @sufile: inode of segment usage file
|
||||
* @segnum: segment number to be freed
|
||||
*/
|
||||
static inline int nilfs_sufile_scrap(struct inode *sufile, __u64 segnum)
|
||||
{
|
||||
return nilfs_sufile_update(sufile, segnum, 1, nilfs_sufile_do_scrap);
|
||||
}
|
||||
|
||||
/**
|
||||
* nilfs_sufile_free - free segment
|
||||
* @sufile: inode of segment usage file
|
||||
|
Loading…
Reference in New Issue
Block a user