Skip to content

Commit

Permalink
f2fs: checkpoint disabling
Browse files Browse the repository at this point in the history
Note that, it requires "f2fs: return correct errno in f2fs_gc".

This adds a lightweight non-persistent snapshotting scheme to f2fs.

To use, mount with the option checkpoint=disable, and to return to
normal operation, remount with checkpoint=enable. If the filesystem
is shut down before remounting with checkpoint=enable, it will revert
back to its apparent state when it was first mounted with
checkpoint=disable. This is useful for situations where you wish to be
able to roll back the state of the disk in case of some critical
failure.

Signed-off-by: Daniel Rosenberg <[email protected]>
[Jaegeuk Kim: use SB_RDONLY instead of MS_RDONLY]
Signed-off-by: Jaegeuk Kim <[email protected]>
  • Loading branch information
drosen-google authored and Jaegeuk Kim committed Oct 30, 2018
1 parent bb423cf commit 99efe50
Show file tree
Hide file tree
Showing 13 changed files with 323 additions and 13 deletions.
5 changes: 5 additions & 0 deletions Documentation/filesystems/f2fs.txt
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,11 @@ fsync_mode=%s Control the policy of fsync. Currently supports "posix",
non-atomic files likewise "nobarrier" mount option.
test_dummy_encryption Enable dummy encryption, which provides a fake fscrypt
context. The fake fscrypt context is used by xfstests.
checkpoint=%s Set to "disable" to turn off checkpointing. Set to "enable"
to reenable checkpointing. Is enabled by default. While
disabled, any unmounting or unexpected shutdowns will cause
the filesystem contents to appear as they did when the
filesystem was mounted with that option.

================================================================================
DEBUGFS ENTRIES
Expand Down
12 changes: 12 additions & 0 deletions fs/f2fs/checkpoint.c
Original file line number Diff line number Diff line change
Expand Up @@ -1211,6 +1211,11 @@ static void update_ckpt_flags(struct f2fs_sb_info *sbi, struct cp_control *cpc)
if (is_sbi_flag_set(sbi, SBI_NEED_FSCK))
__set_ckpt_flags(ckpt, CP_FSCK_FLAG);

if (is_sbi_flag_set(sbi, SBI_CP_DISABLED))
__set_ckpt_flags(ckpt, CP_DISABLED_FLAG);
else
__clear_ckpt_flags(ckpt, CP_DISABLED_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 +1423,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);
sbi->unusable_block_count = 0;
__set_cp_next_pack(sbi);

/*
Expand All @@ -1442,6 +1448,12 @@ int f2fs_write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
unsigned long long ckpt_ver;
int err = 0;

if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED))) {
if (cpc->reason != CP_PAUSE)
return 0;
f2fs_msg(sbi->sb, KERN_WARNING,
"Start checkpoint disabled!");
}
mutex_lock(&sbi->cp_mutex);

if (!is_sbi_flag_set(sbi, SBI_IS_DIRTY) &&
Expand Down
14 changes: 13 additions & 1 deletion fs/f2fs/data.c
Original file line number Diff line number Diff line change
Expand Up @@ -534,7 +534,8 @@ void f2fs_submit_page_write(struct f2fs_io_info *fio)
if (fio->in_list)
goto next;
out:
if (is_sbi_flag_set(sbi, SBI_IS_SHUTDOWN))
if (is_sbi_flag_set(sbi, SBI_IS_SHUTDOWN) ||
f2fs_is_checkpoint_ready(sbi))
__submit_merged_bio(io);
up_write(&io->io_rwsem);
}
Expand Down Expand Up @@ -1699,6 +1700,10 @@ static inline bool check_inplace_update_policy(struct inode *inode,
is_inode_flag_set(inode, FI_NEED_IPU))
return true;

if (unlikely(fio && is_sbi_flag_set(sbi, SBI_CP_DISABLED) &&
!f2fs_is_checkpointed_data(sbi, fio->old_blkaddr)))
return true;

return false;
}

Expand Down Expand Up @@ -1729,6 +1734,9 @@ bool f2fs_should_update_outplace(struct inode *inode, struct f2fs_io_info *fio)
return true;
if (IS_ATOMIC_WRITTEN_PAGE(fio->page))
return true;
if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED) &&
f2fs_is_checkpointed_data(sbi, fio->old_blkaddr)))
return true;
}
return false;
}
Expand Down Expand Up @@ -2349,6 +2357,10 @@ static int f2fs_write_begin(struct file *file, struct address_space *mapping,

trace_f2fs_write_begin(inode, pos, len, flags);

err = f2fs_is_checkpoint_ready(sbi);
if (err)
goto fail;

if ((f2fs_is_atomic_file(inode) &&
!f2fs_available_free_memory(sbi, INMEM_PAGES)) ||
is_inode_flag_set(inode, FI_ATOMIC_REVOKE_REQUEST)) {
Expand Down
3 changes: 2 additions & 1 deletion fs/f2fs/debug.c
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,8 @@ static int stat_show(struct seq_file *s, void *v)
seq_printf(s, "\n=====[ partition info(%pg). #%d, %s, CP: %s]=====\n",
si->sbi->sb->s_bdev, i++,
f2fs_readonly(si->sbi->sb) ? "RO": "RW",
f2fs_cp_error(si->sbi) ? "Error": "Good");
is_set_ckpt_flags(si->sbi, CP_DISABLED_FLAG) ?
"Disabled": (f2fs_cp_error(si->sbi) ? "Error": "Good"));
seq_printf(s, "[SB: 1] [CP: 2] [SIT: %d] [NAT: %d] ",
si->sit_area_segs, si->nat_area_segs);
seq_printf(s, "[SSA: %d] [MAIN: %d",
Expand Down
18 changes: 17 additions & 1 deletion fs/f2fs/f2fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ extern char *f2fs_fault_name[FAULT_MAX];
#define F2FS_MOUNT_QUOTA 0x00400000
#define F2FS_MOUNT_INLINE_XATTR_SIZE 0x00800000
#define F2FS_MOUNT_RESERVE_ROOT 0x01000000
#define F2FS_MOUNT_DISABLE_CHECKPOINT 0x02000000

#define F2FS_OPTION(sbi) ((sbi)->mount_opt)
#define clear_opt(sbi, option) (F2FS_OPTION(sbi).opt &= ~F2FS_MOUNT_##option)
Expand Down Expand Up @@ -246,6 +247,7 @@ enum {
#define CP_RECOVERY 0x00000008
#define CP_DISCARD 0x00000010
#define CP_TRIMMED 0x00000020
#define CP_PAUSE 0x00000040

#define MAX_DISCARD_BLOCKS(sbi) BLKS_PER_SEC(sbi)
#define DEF_MAX_DISCARD_REQUEST 8 /* issue 8 discards per round */
Expand All @@ -255,6 +257,7 @@ enum {
#define DEF_DISCARD_URGENT_UTIL 80 /* do more discard over 80% */
#define DEF_CP_INTERVAL 60 /* 60 secs */
#define DEF_IDLE_INTERVAL 5 /* 5 secs */
#define DEF_DISABLE_INTERVAL 5 /* 5 secs */

struct cp_control {
int reason;
Expand Down Expand Up @@ -1157,13 +1160,15 @@ enum {
SBI_NEED_CP, /* need to checkpoint */
SBI_IS_SHUTDOWN, /* shutdown by ioctl */
SBI_IS_RECOVERED, /* recovered orphan/data */
SBI_CP_DISABLED, /* CP was disabled last mount */
};

enum {
CP_TIME,
REQ_TIME,
DISCARD_TIME,
GC_TIME,
DISABLE_TIME,
MAX_TIME,
};

Expand Down Expand Up @@ -1290,6 +1295,9 @@ struct f2fs_sb_info {
block_t reserved_blocks; /* configurable reserved blocks */
block_t current_reserved_blocks; /* current reserved blocks */

/* Additional tracking for no checkpoint mode */
block_t unusable_block_count; /* # of blocks saved by last cp */

unsigned int nquota_files; /* # of quota sysfile */

u32 s_next_generation; /* for NFS support */
Expand Down Expand Up @@ -1800,7 +1808,8 @@ static inline int inc_valid_block_count(struct f2fs_sb_info *sbi,

if (!__allow_reserved_blocks(sbi, inode, true))
avail_user_block_count -= F2FS_OPTION(sbi).root_reserved_blocks;

if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED)))
avail_user_block_count -= sbi->unusable_block_count;
if (unlikely(sbi->total_valid_block_count > avail_user_block_count)) {
diff = sbi->total_valid_block_count - avail_user_block_count;
if (diff > *count)
Expand Down Expand Up @@ -2007,6 +2016,8 @@ static inline int inc_valid_node_count(struct f2fs_sb_info *sbi,

if (!__allow_reserved_blocks(sbi, inode, false))
valid_block_count += F2FS_OPTION(sbi).root_reserved_blocks;
if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED)))
valid_block_count += sbi->unusable_block_count;

if (unlikely(valid_block_count > sbi->user_block_count)) {
spin_unlock(&sbi->stat_lock);
Expand Down Expand Up @@ -3030,6 +3041,8 @@ void f2fs_stop_discard_thread(struct f2fs_sb_info *sbi);
bool f2fs_wait_discard_bios(struct f2fs_sb_info *sbi);
void f2fs_clear_prefree_segments(struct f2fs_sb_info *sbi,
struct cp_control *cpc);
void f2fs_dirty_to_prefree(struct f2fs_sb_info *sbi);
int f2fs_disable_cp_again(struct f2fs_sb_info *sbi);
void f2fs_release_discard_addrs(struct f2fs_sb_info *sbi);
int f2fs_npages_for_summary_flush(struct f2fs_sb_info *sbi, bool for_ra);
void f2fs_allocate_new_segments(struct f2fs_sb_info *sbi);
Expand Down Expand Up @@ -3617,6 +3630,9 @@ static inline bool f2fs_force_buffered_io(struct inode *inode,
if (test_opt(sbi, LFS) && (rw == WRITE) &&
block_unaligned_IO(inode, iocb, iter))
return true;
if (is_sbi_flag_set(F2FS_I_SB(inode), SBI_CP_DISABLED))
return true;

return false;
}

Expand Down
12 changes: 11 additions & 1 deletion fs/f2fs/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,8 @@ static int f2fs_do_sync_file(struct file *file, loff_t start, loff_t end,
};
unsigned int seq_id = 0;

if (unlikely(f2fs_readonly(inode->i_sb)))
if (unlikely(f2fs_readonly(inode->i_sb) ||
is_sbi_flag_set(sbi, SBI_CP_DISABLED)))
return 0;

trace_f2fs_sync_file_enter(inode);
Expand Down Expand Up @@ -2157,6 +2158,12 @@ static int f2fs_ioc_write_checkpoint(struct file *filp, unsigned long arg)
if (f2fs_readonly(sbi->sb))
return -EROFS;

if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED))) {
f2fs_msg(sbi->sb, KERN_INFO,
"Skipping Checkpoint. Checkpoints currently disabled.");
return -EINVAL;
}

ret = mnt_want_write_file(filp);
if (ret)
return ret;
Expand Down Expand Up @@ -2528,6 +2535,9 @@ static int f2fs_ioc_flush_device(struct file *filp, unsigned long arg)
if (f2fs_readonly(sbi->sb))
return -EROFS;

if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED)))
return -EINVAL;

if (copy_from_user(&range, (struct f2fs_flush_device __user *)arg,
sizeof(range)))
return -EFAULT;
Expand Down
9 changes: 7 additions & 2 deletions fs/f2fs/gc.c
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,10 @@ static int get_victim_by_default(struct f2fs_sb_info *sbi,

if (sec_usage_check(sbi, secno))
goto next;
/* Don't touch checkpointed data */
if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED) &&
get_ckpt_valid_blocks(sbi, segno)))
goto next;
if (gc_type == BG_GC && test_bit(secno, dirty_i->victim_secmap))
goto next;

Expand Down Expand Up @@ -1189,7 +1193,8 @@ int f2fs_gc(struct f2fs_sb_info *sbi, bool sync,
* threshold, we can make them free by checkpoint. Then, we
* secure free segments which doesn't need fggc any more.
*/
if (prefree_segments(sbi)) {
if (prefree_segments(sbi) &&
!is_sbi_flag_set(sbi, SBI_CP_DISABLED)) {
ret = f2fs_write_checkpoint(sbi, &cpc);
if (ret)
goto stop;
Expand Down Expand Up @@ -1241,7 +1246,7 @@ int f2fs_gc(struct f2fs_sb_info *sbi, bool sync,
segno = NULL_SEGNO;
goto gc_more;
}
if (gc_type == FG_GC)
if (gc_type == FG_GC && !is_sbi_flag_set(sbi, SBI_CP_DISABLED))
ret = f2fs_write_checkpoint(sbi, &cpc);
}
stop:
Expand Down
6 changes: 5 additions & 1 deletion fs/f2fs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -607,6 +607,9 @@ int f2fs_write_inode(struct inode *inode, struct writeback_control *wbc)
if (!is_inode_flag_set(inode, FI_DIRTY_INODE))
return 0;

if (f2fs_is_checkpoint_ready(sbi))
return -ENOSPC;

/*
* We need to balance fs here to prevent from producing dirty node pages
* during the urgent cleaning time when runing out of free sections.
Expand Down Expand Up @@ -688,7 +691,8 @@ void f2fs_evict_inode(struct inode *inode)
stat_dec_inline_dir(inode);
stat_dec_inline_inode(inode);

if (likely(!is_set_ckpt_flags(sbi, CP_ERROR_FLAG)))
if (likely(!is_set_ckpt_flags(sbi, CP_ERROR_FLAG) &&
!is_sbi_flag_set(sbi, SBI_CP_DISABLED)))
f2fs_bug_on(sbi, is_inode_flag_set(inode, FI_DIRTY_INODE));
else
f2fs_inode_synced(inode);
Expand Down
19 changes: 19 additions & 0 deletions fs/f2fs/namei.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

#include "f2fs.h"
#include "node.h"
#include "segment.h"
#include "xattr.h"
#include "acl.h"
#include <trace/events/f2fs.h>
Expand Down Expand Up @@ -269,6 +270,9 @@ static int f2fs_create(struct inode *dir, struct dentry *dentry, umode_t mode,

if (unlikely(f2fs_cp_error(sbi)))
return -EIO;
err = f2fs_is_checkpoint_ready(sbi);
if (err)
return err;

err = dquot_initialize(dir);
if (err)
Expand Down Expand Up @@ -316,6 +320,9 @@ static int f2fs_link(struct dentry *old_dentry, struct inode *dir,

if (unlikely(f2fs_cp_error(sbi)))
return -EIO;
err = f2fs_is_checkpoint_ready(sbi);
if (err)
return err;

err = fscrypt_prepare_link(old_dentry, dir, dentry);
if (err)
Expand Down Expand Up @@ -559,6 +566,9 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry,

if (unlikely(f2fs_cp_error(sbi)))
return -EIO;
err = f2fs_is_checkpoint_ready(sbi);
if (err)
return err;

err = fscrypt_prepare_symlink(dir, symname, len, dir->i_sb->s_blocksize,
&disk_link);
Expand Down Expand Up @@ -690,6 +700,9 @@ static int f2fs_mknod(struct inode *dir, struct dentry *dentry,

if (unlikely(f2fs_cp_error(sbi)))
return -EIO;
err = f2fs_is_checkpoint_ready(sbi);
if (err)
return err;

err = dquot_initialize(dir);
if (err)
Expand Down Expand Up @@ -825,6 +838,9 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,

if (unlikely(f2fs_cp_error(sbi)))
return -EIO;
err = f2fs_is_checkpoint_ready(sbi);
if (err)
return err;

if (is_inode_flag_set(new_dir, FI_PROJ_INHERIT) &&
(!projid_eq(F2FS_I(new_dir)->i_projid,
Expand Down Expand Up @@ -1015,6 +1031,9 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry,

if (unlikely(f2fs_cp_error(sbi)))
return -EIO;
err = f2fs_is_checkpoint_ready(sbi);
if (err)
return err;

if ((is_inode_flag_set(new_dir, FI_PROJ_INHERIT) &&
!projid_eq(F2FS_I(new_dir)->i_projid,
Expand Down
Loading

0 comments on commit 99efe50

Please sign in to comment.