Skip to content

Commit

Permalink
f2fs: guarantee journalled quota data by checkpoint
Browse files Browse the repository at this point in the history
For journalled quota mode, let checkpoint to flush dquot dirty data
and quota file data to guarntee persistence of all quota sysfile in
last checkpoint, by this way, we can avoid corrupting quota sysfile
when encountering SPO.

The implementation is as below:

1. add a global state SBI_QUOTA_NEED_FLUSH to indicate that there is
cached dquot metadata changes in quota subsystem, and later checkpoint
should:
 a) flush dquot metadata into quota file.
 b) flush quota file to storage to keep file usage be consistent.

2. add a global state SBI_QUOTA_NEED_REPAIR to indicate that quota
operation failed due to -EIO or -ENOSPC, so later,
 a) checkpoint will skip syncing dquot metadata.
 b) CP_QUOTA_NEED_FSCK_FLAG will be set in last cp pack to give a
    hint for fsck repairing.

3. add a global state SBI_QUOTA_SKIP_FLUSH, in checkpoint, if quota
data updating is very heavy, it may cause hungtask in block_operation().
To avoid this, if our retry time exceed threshold, let's just skip
flushing and retry in next checkpoint().

Signed-off-by: Weichao Guo <[email protected]>
Signed-off-by: Chao Yu <[email protected]>
[Jaegeuk Kim: avoid warnings and set fsck flag]
Signed-off-by: Jaegeuk Kim <[email protected]>
Signed-off-by: Albert I <[email protected]>
  • Loading branch information
chaseyu authored and krasCGQ committed Dec 4, 2018
1 parent a7d50ef commit 3f5ae4e
Show file tree
Hide file tree
Showing 10 changed files with 288 additions and 56 deletions.
62 changes: 58 additions & 4 deletions fs/f2fs/checkpoint.c
Original file line number Diff line number Diff line change
Expand Up @@ -1075,6 +1075,21 @@ static void __prepare_cp_block(struct f2fs_sb_info *sbi)
ckpt->next_free_nid = cpu_to_le32(last_nid);
}

static bool __need_flush_quota(struct f2fs_sb_info *sbi)
{
if (!is_journalled_quota(sbi))
return false;
if (is_sbi_flag_set(sbi, SBI_QUOTA_SKIP_FLUSH))
return false;
if (is_sbi_flag_set(sbi, SBI_QUOTA_NEED_REPAIR))
return false;
if (is_sbi_flag_set(sbi, SBI_QUOTA_NEED_FLUSH))
return true;
if (get_pages(sbi, F2FS_DIRTY_QDATA))
return true;
return false;
}

/*
* Freeze all the FS-operations for checkpoint.
*/
Expand All @@ -1086,20 +1101,44 @@ static int block_operations(struct f2fs_sb_info *sbi)
.for_reclaim = 0,
};
struct blk_plug plug;
int err = 0;
int err = 0, cnt = 0;

blk_start_plug(&plug);

retry_flush_dents:
retry_flush_quotas:
if (__need_flush_quota(sbi)) {
int locked;

if (++cnt > DEFAULT_RETRY_QUOTA_FLUSH_COUNT) {
set_sbi_flag(sbi, SBI_QUOTA_SKIP_FLUSH);
f2fs_lock_all(sbi);
goto retry_flush_dents;
}
clear_sbi_flag(sbi, SBI_QUOTA_NEED_FLUSH);

/* only failed during mount/umount/freeze/quotactl */
locked = down_read_trylock(&sbi->sb->s_umount);
f2fs_quota_sync(sbi->sb, -1);
if (locked)
up_read(&sbi->sb->s_umount);
}

f2fs_lock_all(sbi);
if (__need_flush_quota(sbi)) {
f2fs_unlock_all(sbi);
cond_resched();
goto retry_flush_quotas;
}

retry_flush_dents:
/* write all the dirty dentry pages */
if (get_pages(sbi, F2FS_DIRTY_DENTS)) {
f2fs_unlock_all(sbi);
err = f2fs_sync_dirty_inodes(sbi, DIR_INODE);
if (err)
goto out;
cond_resched();
goto retry_flush_dents;
goto retry_flush_quotas;
}

/*
Expand All @@ -1108,14 +1147,20 @@ static int block_operations(struct f2fs_sb_info *sbi)
*/
down_write(&sbi->node_change);

if (__need_flush_quota(sbi)) {
up_write(&sbi->node_change);
f2fs_unlock_all(sbi);
goto retry_flush_quotas;
}

if (get_pages(sbi, F2FS_DIRTY_IMETA)) {
up_write(&sbi->node_change);
f2fs_unlock_all(sbi);
err = f2fs_sync_inode_meta(sbi);
if (err)
goto out;
cond_resched();
goto retry_flush_dents;
goto retry_flush_quotas;
}

retry_flush_nodes:
Expand Down Expand Up @@ -1211,6 +1256,14 @@ static void update_ckpt_flags(struct f2fs_sb_info *sbi, struct cp_control *cpc)
else
__clear_ckpt_flags(ckpt, CP_DISABLED_FLAG);

if (is_sbi_flag_set(sbi, SBI_QUOTA_SKIP_FLUSH))
__set_ckpt_flags(ckpt, CP_QUOTA_NEED_FSCK_FLAG);
else
__clear_ckpt_flags(ckpt, CP_QUOTA_NEED_FSCK_FLAG);

if (is_sbi_flag_set(sbi, SBI_QUOTA_NEED_REPAIR))
__set_ckpt_flags(ckpt, CP_QUOTA_NEED_FSCK_FLAG);

/* set this flag to activate crc|cp_ver for recovery */
__set_ckpt_flags(ckpt, CP_CRC_RECOVERY_FLAG);
__clear_ckpt_flags(ckpt, CP_NOCRC_RECOVERY_FLAG);
Expand Down Expand Up @@ -1418,6 +1471,7 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)

clear_sbi_flag(sbi, SBI_IS_DIRTY);
clear_sbi_flag(sbi, SBI_NEED_CP);
clear_sbi_flag(sbi, SBI_QUOTA_SKIP_FLUSH);
sbi->unusable_block_count = 0;
__set_cp_next_pack(sbi);

Expand Down
16 changes: 11 additions & 5 deletions fs/f2fs/data.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ static bool __is_cp_guaranteed(struct page *page)
inode->i_ino == F2FS_NODE_INO(sbi) ||
S_ISDIR(inode->i_mode) ||
(S_ISREG(inode->i_mode) &&
is_inode_flag_set(inode, FI_ATOMIC_FILE)) ||
(f2fs_is_atomic_file(inode) || IS_NOQUOTA(inode))) ||
is_cold_data(page))
return true;
return false;
Expand Down Expand Up @@ -1732,6 +1732,8 @@ bool f2fs_should_update_outplace(struct inode *inode, struct f2fs_io_info *fio)
return true;
if (S_ISDIR(inode->i_mode))
return true;
if (IS_NOQUOTA(inode))
return true;
if (f2fs_is_atomic_file(inode))
return true;
if (fio) {
Expand Down Expand Up @@ -1982,7 +1984,7 @@ static int __write_data_page(struct page *page, bool *submitted,
}

unlock_page(page);
if (!S_ISDIR(inode->i_mode))
if (!S_ISDIR(inode->i_mode) && !IS_NOQUOTA(inode))
f2fs_balance_fs(sbi, need_balance_fs);

if (unlikely(f2fs_cp_error(sbi))) {
Expand Down Expand Up @@ -2173,6 +2175,8 @@ static inline bool __should_serialize_io(struct inode *inode,
{
if (!S_ISREG(inode->i_mode))
return false;
if (IS_NOQUOTA(inode))
return false;
if (wbc->sync_mode != WB_SYNC_ALL)
return true;
if (get_dirty_pages(inode) >= SM_I(F2FS_I_SB(inode))->min_seq_blocks)
Expand Down Expand Up @@ -2202,7 +2206,8 @@ static int __f2fs_write_data_pages(struct address_space *mapping,
if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING)))
goto skip_write;

if (S_ISDIR(inode->i_mode) && wbc->sync_mode == WB_SYNC_NONE &&
if ((S_ISDIR(inode->i_mode) || IS_NOQUOTA(inode)) &&
wbc->sync_mode == WB_SYNC_NONE &&
get_dirty_pages(inode) < nr_pages_to_skip(sbi, DATA) &&
f2fs_available_free_memory(sbi, DIRTY_DENTS))
goto skip_write;
Expand Down Expand Up @@ -2267,7 +2272,7 @@ static void f2fs_write_failed(struct address_space *mapping, loff_t to)
down_write(&F2FS_I(inode)->i_mmap_sem);

truncate_pagecache(inode, i_size);
f2fs_truncate_blocks(inode, i_size, true);
f2fs_truncate_blocks(inode, i_size, true, true);

up_write(&F2FS_I(inode)->i_mmap_sem);
up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
Expand Down Expand Up @@ -2416,7 +2421,8 @@ static int f2fs_write_begin(struct file *file, struct address_space *mapping,
if (err)
goto fail;

if (need_balance && has_not_enough_free_secs(sbi, 0, 0)) {
if (need_balance && !IS_NOQUOTA(inode) &&
has_not_enough_free_secs(sbi, 0, 0)) {
unlock_page(page);
f2fs_balance_fs(sbi, true);
lock_page(page);
Expand Down
48 changes: 40 additions & 8 deletions fs/f2fs/f2fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -626,6 +626,9 @@ enum {

#define DEFAULT_RETRY_IO_COUNT 8 /* maximum retry read IO count */

/* maximum retry quota flush count */
#define DEFAULT_RETRY_QUOTA_FLUSH_COUNT 8

#define F2FS_LINK_MAX 0xffffffff /* maximum link count per file */

#define MAX_DIR_RA_PAGES 4 /* maximum ra pages of dir */
Expand Down Expand Up @@ -1192,6 +1195,9 @@ enum {
SBI_IS_SHUTDOWN, /* shutdown by ioctl */
SBI_IS_RECOVERED, /* recovered orphan/data */
SBI_CP_DISABLED, /* CP was disabled last mount */
SBI_QUOTA_NEED_FLUSH, /* need to flush quota info in CP */
SBI_QUOTA_SKIP_FLUSH, /* skip flushing quota in current CP */
SBI_QUOTA_NEED_REPAIR, /* quota file may be corrupted */
};

enum {
Expand Down Expand Up @@ -2018,12 +2024,18 @@ static inline int inc_valid_node_count(struct f2fs_sb_info *sbi,
{
block_t valid_block_count;
unsigned int valid_node_count;
bool quota = inode && !is_inode;
int err;

if (quota) {
int ret = dquot_reserve_block(inode, 1);
if (ret)
return ret;
if (is_inode) {
if (inode) {
err = dquot_alloc_inode(inode);
if (err)
return err;
}
} else {
err = dquot_reserve_block(inode, 1);
if (err)
return err;
}

if (time_to_inject(sbi, FAULT_BLOCK)) {
Expand Down Expand Up @@ -2067,8 +2079,12 @@ static inline int inc_valid_node_count(struct f2fs_sb_info *sbi,
return 0;

enospc:
if (quota)
if (is_inode) {
if (inode)
dquot_free_inode(inode);
} else {
dquot_release_reservation_block(inode, 1);
}
return -ENOSPC;
}

Expand All @@ -2089,7 +2105,9 @@ static inline void dec_valid_node_count(struct f2fs_sb_info *sbi,

spin_unlock(&sbi->stat_lock);

if (!is_inode)
if (is_inode)
dquot_free_inode(inode);
else
f2fs_i_blocks_write(inode, 1, false, true);
}

Expand Down Expand Up @@ -2915,7 +2933,8 @@ static inline bool is_valid_data_blkaddr(struct f2fs_sb_info *sbi,
*/
int f2fs_sync_file(struct file *file, loff_t start, loff_t end, int datasync);
void f2fs_truncate_data_blocks(struct dnode_of_data *dn);
int f2fs_truncate_blocks(struct inode *inode, u64 from, bool lock);
int f2fs_truncate_blocks(struct inode *inode, u64 from, bool lock,
bool buf_write);
int f2fs_truncate(struct inode *inode);
int f2fs_getattr(struct vfsmount *mnt, struct dentry *dentry,
struct kstat *stat);
Expand Down Expand Up @@ -3003,6 +3022,7 @@ static inline int f2fs_add_link(struct dentry *dentry, struct inode *inode)
int f2fs_inode_dirtied(struct inode *inode, bool sync);
void f2fs_inode_synced(struct inode *inode);
int f2fs_enable_quota_files(struct f2fs_sb_info *sbi, bool rdonly);
int f2fs_quota_sync(struct super_block *sb, int type);
void f2fs_quota_off_umount(struct super_block *sb);
int f2fs_commit_super(struct f2fs_sb_info *sbi, bool recover);
int f2fs_sync_fs(struct super_block *sb, int sync);
Expand Down Expand Up @@ -3677,3 +3697,15 @@ extern void f2fs_build_fault_attr(struct f2fs_sb_info *sbi, unsigned int rate,
#endif

#endif

static inline bool is_journalled_quota(struct f2fs_sb_info *sbi)
{
#ifdef CONFIG_QUOTA
if (f2fs_sb_has_quota_ino(sbi->sb))
return true;
if (F2FS_OPTION(sbi).s_qf_names[USRQUOTA] ||
F2FS_OPTION(sbi).s_qf_names[GRPQUOTA])
return true;
#endif
return false;
}
31 changes: 24 additions & 7 deletions fs/f2fs/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -587,14 +587,16 @@ static int truncate_partial_data_page(struct inode *inode, u64 from,
return 0;
}

int f2fs_truncate_blocks(struct inode *inode, u64 from, bool lock)
int f2fs_truncate_blocks(struct inode *inode, u64 from, bool lock,
bool buf_write)
{
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
struct dnode_of_data dn;
pgoff_t free_from;
int count = 0, err = 0;
struct page *ipage;
bool truncate_page = false;
int flag = buf_write ? F2FS_GET_BLOCK_PRE_AIO : F2FS_GET_BLOCK_PRE_DIO;

trace_f2fs_truncate_blocks_enter(inode, from);

Expand All @@ -604,7 +606,7 @@ int f2fs_truncate_blocks(struct inode *inode, u64 from, bool lock)
goto free_partial;

if (lock)
f2fs_lock_op(sbi);
__do_map_lock(sbi, flag, true);

ipage = f2fs_get_node_page(sbi, inode->i_ino);
if (IS_ERR(ipage)) {
Expand Down Expand Up @@ -642,7 +644,7 @@ int f2fs_truncate_blocks(struct inode *inode, u64 from, bool lock)
err = f2fs_truncate_inode_blocks(inode, free_from);
out:
if (lock)
f2fs_unlock_op(sbi);
__do_map_lock(sbi, flag, false);
free_partial:
/* lastly zero out the first data page */
if (!err)
Expand Down Expand Up @@ -677,7 +679,7 @@ int f2fs_truncate(struct inode *inode)
return err;
}

err = f2fs_truncate_blocks(inode, i_size_read(inode), true);
err = f2fs_truncate_blocks(inode, i_size_read(inode), true, false);
if (err)
return err;

Expand Down Expand Up @@ -785,9 +787,24 @@ int f2fs_setattr(struct dentry *dentry, struct iattr *attr)
!uid_eq(attr->ia_uid, inode->i_uid)) ||
(attr->ia_valid & ATTR_GID &&
!gid_eq(attr->ia_gid, inode->i_gid))) {
f2fs_lock_op(F2FS_I_SB(inode));
err = dquot_transfer(inode, attr);
if (err)
if (err) {
set_sbi_flag(F2FS_I_SB(inode),
SBI_QUOTA_NEED_REPAIR);
f2fs_unlock_op(F2FS_I_SB(inode));
return err;
}
/*
* update uid/gid under lock_op(), so that dquot and inode can
* be updated atomically.
*/
if (attr->ia_valid & ATTR_UID)
inode->i_uid = attr->ia_uid;
if (attr->ia_valid & ATTR_GID)
inode->i_gid = attr->ia_gid;
f2fs_mark_inode_dirty_sync(inode, true);
f2fs_unlock_op(F2FS_I_SB(inode));
}

if (attr->ia_valid & ATTR_SIZE) {
Expand Down Expand Up @@ -1245,7 +1262,7 @@ static int f2fs_collapse_range(struct inode *inode, loff_t offset, loff_t len)
new_size = i_size_read(inode) - len;
truncate_pagecache(inode, new_size);

ret = f2fs_truncate_blocks(inode, new_size, true);
ret = f2fs_truncate_blocks(inode, new_size, true, false);
up_write(&F2FS_I(inode)->i_mmap_sem);
if (!ret)
f2fs_i_size_write(inode, new_size);
Expand Down Expand Up @@ -1430,7 +1447,7 @@ static int f2fs_insert_range(struct inode *inode, loff_t offset, loff_t len)
f2fs_balance_fs(sbi, true);

down_write(&F2FS_I(inode)->i_mmap_sem);
ret = f2fs_truncate_blocks(inode, i_size_read(inode), true);
ret = f2fs_truncate_blocks(inode, i_size_read(inode), true, false);
up_write(&F2FS_I(inode)->i_mmap_sem);
if (ret)
return ret;
Expand Down
4 changes: 2 additions & 2 deletions fs/f2fs/inline.c
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ bool f2fs_recover_inline_data(struct inode *inode, struct page *npage)
clear_inode_flag(inode, FI_INLINE_DATA);
f2fs_put_page(ipage, 1);
} else if (ri && (ri->i_inline & F2FS_INLINE_DATA)) {
if (f2fs_truncate_blocks(inode, 0, false))
if (f2fs_truncate_blocks(inode, 0, false, false))
return false;
goto process_inline;
}
Expand Down Expand Up @@ -488,7 +488,7 @@ static int f2fs_add_inline_entries(struct inode *dir, void *inline_dentry)
return 0;
punch_dentry_pages:
truncate_inode_pages(&dir->i_data, 0);
f2fs_truncate_blocks(dir, 0, false);
f2fs_truncate_blocks(dir, 0, false, false);
f2fs_remove_dirty_inode(dir);
return err;
}
Expand Down
Loading

0 comments on commit 3f5ae4e

Please sign in to comment.