mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2025-01-19 20:34:20 +08:00
f2fs: fix to recover quota data correctly
With -O quota mkfs option, xfstests generic/417 fails due to fsck detects data corruption on quota inodes. [ASSERT] (fsck_chk_quota_files:2051) --> Quota file is missing or invalid quota file content found. The root cause is there is a hole f2fs doesn't hold quota inodes, so all recovered quota data will be dropped due to SBI_POR_DOING flag was set. - f2fs_fill_super - f2fs_recover_orphan_inodes - f2fs_enable_quota_files - f2fs_quota_off_umount <--- quota inodes were dropped ---> - f2fs_recover_fsync_data - f2fs_enable_quota_files - f2fs_quota_off_umount This patch tries to eliminate the hole by holding quota inodes during entire recovery flow as below: - f2fs_fill_super - f2fs_recover_quota_begin - f2fs_recover_orphan_inodes - f2fs_recover_fsync_data - f2fs_recover_quota_end Then, recovered quota data can be persisted after SBI_POR_DOING is cleared. Signed-off-by: Chao Yu <chao@kernel.org> Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
This commit is contained in:
parent
d78dfefcde
commit
e1bb7d3d9c
@ -713,11 +713,7 @@ err_out:
|
|||||||
int f2fs_recover_orphan_inodes(struct f2fs_sb_info *sbi)
|
int f2fs_recover_orphan_inodes(struct f2fs_sb_info *sbi)
|
||||||
{
|
{
|
||||||
block_t start_blk, orphan_blocks, i, j;
|
block_t start_blk, orphan_blocks, i, j;
|
||||||
unsigned int s_flags = sbi->sb->s_flags;
|
|
||||||
int err = 0;
|
int err = 0;
|
||||||
#ifdef CONFIG_QUOTA
|
|
||||||
int quota_enabled;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!is_set_ckpt_flags(sbi, CP_ORPHAN_PRESENT_FLAG))
|
if (!is_set_ckpt_flags(sbi, CP_ORPHAN_PRESENT_FLAG))
|
||||||
return 0;
|
return 0;
|
||||||
@ -727,18 +723,8 @@ int f2fs_recover_orphan_inodes(struct f2fs_sb_info *sbi)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s_flags & SB_RDONLY) {
|
if (is_sbi_flag_set(sbi, SBI_IS_WRITABLE))
|
||||||
f2fs_info(sbi, "orphan cleanup on readonly fs");
|
f2fs_info(sbi, "orphan cleanup on readonly fs");
|
||||||
sbi->sb->s_flags &= ~SB_RDONLY;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CONFIG_QUOTA
|
|
||||||
/*
|
|
||||||
* Turn on quotas which were not enabled for read-only mounts if
|
|
||||||
* filesystem has quota feature, so that they are updated correctly.
|
|
||||||
*/
|
|
||||||
quota_enabled = f2fs_enable_quota_files(sbi, s_flags & SB_RDONLY);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
start_blk = __start_cp_addr(sbi) + 1 + __cp_payload(sbi);
|
start_blk = __start_cp_addr(sbi) + 1 + __cp_payload(sbi);
|
||||||
orphan_blocks = __start_sum_addr(sbi) - 1 - __cp_payload(sbi);
|
orphan_blocks = __start_sum_addr(sbi) - 1 - __cp_payload(sbi);
|
||||||
@ -772,13 +758,6 @@ int f2fs_recover_orphan_inodes(struct f2fs_sb_info *sbi)
|
|||||||
out:
|
out:
|
||||||
set_sbi_flag(sbi, SBI_IS_RECOVERED);
|
set_sbi_flag(sbi, SBI_IS_RECOVERED);
|
||||||
|
|
||||||
#ifdef CONFIG_QUOTA
|
|
||||||
/* Turn quotas off */
|
|
||||||
if (quota_enabled)
|
|
||||||
f2fs_quota_off_umount(sbi->sb);
|
|
||||||
#endif
|
|
||||||
sbi->sb->s_flags = s_flags; /* Restore SB_RDONLY status */
|
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -352,6 +352,7 @@ static const char *s_flag[MAX_SBI_FLAG] = {
|
|||||||
[SBI_QUOTA_NEED_REPAIR] = "quota_need_repair",
|
[SBI_QUOTA_NEED_REPAIR] = "quota_need_repair",
|
||||||
[SBI_IS_RESIZEFS] = "resizefs",
|
[SBI_IS_RESIZEFS] = "resizefs",
|
||||||
[SBI_IS_FREEZING] = "freezefs",
|
[SBI_IS_FREEZING] = "freezefs",
|
||||||
|
[SBI_IS_WRITABLE] = "writable",
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char *ipu_mode_names[F2FS_IPU_MAX] = {
|
static const char *ipu_mode_names[F2FS_IPU_MAX] = {
|
||||||
|
@ -1294,6 +1294,7 @@ enum {
|
|||||||
SBI_QUOTA_NEED_REPAIR, /* quota file may be corrupted */
|
SBI_QUOTA_NEED_REPAIR, /* quota file may be corrupted */
|
||||||
SBI_IS_RESIZEFS, /* resizefs is in process */
|
SBI_IS_RESIZEFS, /* resizefs is in process */
|
||||||
SBI_IS_FREEZING, /* freezefs is in process */
|
SBI_IS_FREEZING, /* freezefs is in process */
|
||||||
|
SBI_IS_WRITABLE, /* remove ro mountoption transiently */
|
||||||
MAX_SBI_FLAG,
|
MAX_SBI_FLAG,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -825,19 +825,9 @@ int f2fs_recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only)
|
|||||||
unsigned long s_flags = sbi->sb->s_flags;
|
unsigned long s_flags = sbi->sb->s_flags;
|
||||||
bool need_writecp = false;
|
bool need_writecp = false;
|
||||||
bool fix_curseg_write_pointer = false;
|
bool fix_curseg_write_pointer = false;
|
||||||
#ifdef CONFIG_QUOTA
|
|
||||||
int quota_enabled;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (s_flags & SB_RDONLY) {
|
if (is_sbi_flag_set(sbi, SBI_IS_WRITABLE))
|
||||||
f2fs_info(sbi, "recover fsync data on readonly fs");
|
f2fs_info(sbi, "recover fsync data on readonly fs");
|
||||||
sbi->sb->s_flags &= ~SB_RDONLY;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CONFIG_QUOTA
|
|
||||||
/* Turn on quotas so that they are updated correctly */
|
|
||||||
quota_enabled = f2fs_enable_quota_files(sbi, s_flags & SB_RDONLY);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
INIT_LIST_HEAD(&inode_list);
|
INIT_LIST_HEAD(&inode_list);
|
||||||
INIT_LIST_HEAD(&tmp_inode_list);
|
INIT_LIST_HEAD(&tmp_inode_list);
|
||||||
@ -909,11 +899,6 @@ skip:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_QUOTA
|
|
||||||
/* Turn quotas off */
|
|
||||||
if (quota_enabled)
|
|
||||||
f2fs_quota_off_umount(sbi->sb);
|
|
||||||
#endif
|
|
||||||
sbi->sb->s_flags = s_flags; /* Restore SB_RDONLY status */
|
sbi->sb->s_flags = s_flags; /* Restore SB_RDONLY status */
|
||||||
|
|
||||||
return ret ? ret : err;
|
return ret ? ret : err;
|
||||||
|
@ -2501,6 +2501,54 @@ restore_opts:
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_QUOTA
|
#ifdef CONFIG_QUOTA
|
||||||
|
static bool f2fs_need_recovery(struct f2fs_sb_info *sbi)
|
||||||
|
{
|
||||||
|
/* need to recovery orphan */
|
||||||
|
if (is_set_ckpt_flags(sbi, CP_ORPHAN_PRESENT_FLAG))
|
||||||
|
return true;
|
||||||
|
/* need to recovery data */
|
||||||
|
if (test_opt(sbi, DISABLE_ROLL_FORWARD))
|
||||||
|
return false;
|
||||||
|
if (test_opt(sbi, NORECOVERY))
|
||||||
|
return false;
|
||||||
|
return !is_set_ckpt_flags(sbi, CP_UMOUNT_FLAG);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool f2fs_recover_quota_begin(struct f2fs_sb_info *sbi)
|
||||||
|
{
|
||||||
|
bool readonly = f2fs_readonly(sbi->sb);
|
||||||
|
|
||||||
|
if (!f2fs_need_recovery(sbi))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* it doesn't need to check f2fs_sb_has_readonly() */
|
||||||
|
if (f2fs_hw_is_readonly(sbi))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (readonly) {
|
||||||
|
sbi->sb->s_flags &= ~SB_RDONLY;
|
||||||
|
set_sbi_flag(sbi, SBI_IS_WRITABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Turn on quotas which were not enabled for read-only mounts if
|
||||||
|
* filesystem has quota feature, so that they are updated correctly.
|
||||||
|
*/
|
||||||
|
return f2fs_enable_quota_files(sbi, readonly);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void f2fs_recover_quota_end(struct f2fs_sb_info *sbi,
|
||||||
|
bool quota_enabled)
|
||||||
|
{
|
||||||
|
if (quota_enabled)
|
||||||
|
f2fs_quota_off_umount(sbi->sb);
|
||||||
|
|
||||||
|
if (is_sbi_flag_set(sbi, SBI_IS_WRITABLE)) {
|
||||||
|
clear_sbi_flag(sbi, SBI_IS_WRITABLE);
|
||||||
|
sbi->sb->s_flags |= SB_RDONLY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Read data from quotafile */
|
/* Read data from quotafile */
|
||||||
static ssize_t f2fs_quota_read(struct super_block *sb, int type, char *data,
|
static ssize_t f2fs_quota_read(struct super_block *sb, int type, char *data,
|
||||||
size_t len, loff_t off)
|
size_t len, loff_t off)
|
||||||
@ -4116,6 +4164,9 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
|
|||||||
int recovery, i, valid_super_block;
|
int recovery, i, valid_super_block;
|
||||||
struct curseg_info *seg_i;
|
struct curseg_info *seg_i;
|
||||||
int retry_cnt = 1;
|
int retry_cnt = 1;
|
||||||
|
#ifdef CONFIG_QUOTA
|
||||||
|
bool quota_enabled = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
try_onemore:
|
try_onemore:
|
||||||
err = -EINVAL;
|
err = -EINVAL;
|
||||||
@ -4409,6 +4460,8 @@ try_onemore:
|
|||||||
if (err)
|
if (err)
|
||||||
f2fs_err(sbi, "Cannot turn on quotas: error %d", err);
|
f2fs_err(sbi, "Cannot turn on quotas: error %d", err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
quota_enabled = f2fs_recover_quota_begin(sbi);
|
||||||
#endif
|
#endif
|
||||||
/* if there are any orphan inodes, free them */
|
/* if there are any orphan inodes, free them */
|
||||||
err = f2fs_recover_orphan_inodes(sbi);
|
err = f2fs_recover_orphan_inodes(sbi);
|
||||||
@ -4466,6 +4519,10 @@ try_onemore:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_QUOTA
|
||||||
|
f2fs_recover_quota_end(sbi, quota_enabled);
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the f2fs is not readonly and fsync data recovery succeeds,
|
* If the f2fs is not readonly and fsync data recovery succeeds,
|
||||||
* check zoned block devices' write pointer consistency.
|
* check zoned block devices' write pointer consistency.
|
||||||
|
Loading…
Reference in New Issue
Block a user