Skip to content

Commit

Permalink
f2fs: fix to recover quota data correctly
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Chao Yu authored and Jaegeuk Kim committed Apr 13, 2023
1 parent d78dfef commit e1bb7d3
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 38 deletions.
23 changes: 1 addition & 22 deletions fs/f2fs/checkpoint.c
Original file line number Diff line number Diff line change
Expand Up @@ -713,11 +713,7 @@ static int recover_orphan_inode(struct f2fs_sb_info *sbi, nid_t ino)
int f2fs_recover_orphan_inodes(struct f2fs_sb_info *sbi)
{
block_t start_blk, orphan_blocks, i, j;
unsigned int s_flags = sbi->sb->s_flags;
int err = 0;
#ifdef CONFIG_QUOTA
int quota_enabled;
#endif

if (!is_set_ckpt_flags(sbi, CP_ORPHAN_PRESENT_FLAG))
return 0;
Expand All @@ -727,18 +723,8 @@ int f2fs_recover_orphan_inodes(struct f2fs_sb_info *sbi)
return 0;
}

if (s_flags & SB_RDONLY) {
if (is_sbi_flag_set(sbi, SBI_IS_WRITABLE))
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);
orphan_blocks = __start_sum_addr(sbi) - 1 - __cp_payload(sbi);
Expand Down Expand Up @@ -772,13 +758,6 @@ int f2fs_recover_orphan_inodes(struct f2fs_sb_info *sbi)
out:
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;
}

Expand Down
1 change: 1 addition & 0 deletions fs/f2fs/debug.c
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,7 @@ static const char *s_flag[MAX_SBI_FLAG] = {
[SBI_QUOTA_NEED_REPAIR] = "quota_need_repair",
[SBI_IS_RESIZEFS] = "resizefs",
[SBI_IS_FREEZING] = "freezefs",
[SBI_IS_WRITABLE] = "writable",
};

static const char *ipu_mode_names[F2FS_IPU_MAX] = {
Expand Down
1 change: 1 addition & 0 deletions fs/f2fs/f2fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -1294,6 +1294,7 @@ enum {
SBI_QUOTA_NEED_REPAIR, /* quota file may be corrupted */
SBI_IS_RESIZEFS, /* resizefs is in process */
SBI_IS_FREEZING, /* freezefs is in process */
SBI_IS_WRITABLE, /* remove ro mountoption transiently */
MAX_SBI_FLAG,
};

Expand Down
17 changes: 1 addition & 16 deletions fs/f2fs/recovery.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
bool need_writecp = 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");
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(&tmp_inode_list);
Expand Down Expand Up @@ -909,11 +899,6 @@ int f2fs_recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only)
}
}

#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 ret ? ret : err;
Expand Down
57 changes: 57 additions & 0 deletions fs/f2fs/super.c
Original file line number Diff line number Diff line change
Expand Up @@ -2501,6 +2501,54 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
}

#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 */
static ssize_t f2fs_quota_read(struct super_block *sb, int type, char *data,
size_t len, loff_t off)
Expand Down Expand Up @@ -4116,6 +4164,9 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
int recovery, i, valid_super_block;
struct curseg_info *seg_i;
int retry_cnt = 1;
#ifdef CONFIG_QUOTA
bool quota_enabled = false;
#endif

try_onemore:
err = -EINVAL;
Expand Down Expand Up @@ -4409,6 +4460,8 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
if (err)
f2fs_err(sbi, "Cannot turn on quotas: error %d", err);
}

quota_enabled = f2fs_recover_quota_begin(sbi);
#endif
/* if there are any orphan inodes, free them */
err = f2fs_recover_orphan_inodes(sbi);
Expand Down Expand Up @@ -4466,6 +4519,10 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
}
}

#ifdef CONFIG_QUOTA
f2fs_recover_quota_end(sbi, quota_enabled);
#endif

/*
* If the f2fs is not readonly and fsync data recovery succeeds,
* check zoned block devices' write pointer consistency.
Expand Down

0 comments on commit e1bb7d3

Please sign in to comment.