mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	f2fs: checkpoint disabling
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 <drosen@google.com> [Jaegeuk Kim: use SB_RDONLY instead of MS_RDONLY] Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
This commit is contained in:
		
							parent
							
								
									fb7d70db30
								
							
						
					
					
						commit
						4354994f09
					
				
					 13 changed files with 324 additions and 14 deletions
				
			
		| 
						 | 
					@ -212,6 +212,11 @@ fsync_mode=%s          Control the policy of fsync. Currently supports "posix",
 | 
				
			||||||
                       non-atomic files likewise "nobarrier" mount option.
 | 
					                       non-atomic files likewise "nobarrier" mount option.
 | 
				
			||||||
test_dummy_encryption  Enable dummy encryption, which provides a fake fscrypt
 | 
					test_dummy_encryption  Enable dummy encryption, which provides a fake fscrypt
 | 
				
			||||||
                       context. The fake fscrypt context is used by xfstests.
 | 
					                       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
 | 
					DEBUGFS ENTRIES
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1210,6 +1210,11 @@ static void update_ckpt_flags(struct f2fs_sb_info *sbi, struct cp_control *cpc)
 | 
				
			||||||
	if (is_sbi_flag_set(sbi, SBI_NEED_FSCK))
 | 
						if (is_sbi_flag_set(sbi, SBI_NEED_FSCK))
 | 
				
			||||||
		__set_ckpt_flags(ckpt, CP_FSCK_FLAG);
 | 
							__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 this flag to activate crc|cp_ver for recovery */
 | 
				
			||||||
	__set_ckpt_flags(ckpt, CP_CRC_RECOVERY_FLAG);
 | 
						__set_ckpt_flags(ckpt, CP_CRC_RECOVERY_FLAG);
 | 
				
			||||||
	__clear_ckpt_flags(ckpt, CP_NOCRC_RECOVERY_FLAG);
 | 
						__clear_ckpt_flags(ckpt, CP_NOCRC_RECOVERY_FLAG);
 | 
				
			||||||
| 
						 | 
					@ -1417,6 +1422,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_IS_DIRTY);
 | 
				
			||||||
	clear_sbi_flag(sbi, SBI_NEED_CP);
 | 
						clear_sbi_flag(sbi, SBI_NEED_CP);
 | 
				
			||||||
 | 
						sbi->unusable_block_count = 0;
 | 
				
			||||||
	__set_cp_next_pack(sbi);
 | 
						__set_cp_next_pack(sbi);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
| 
						 | 
					@ -1441,6 +1447,12 @@ int f2fs_write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
 | 
				
			||||||
	unsigned long long ckpt_ver;
 | 
						unsigned long long ckpt_ver;
 | 
				
			||||||
	int err = 0;
 | 
						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);
 | 
						mutex_lock(&sbi->cp_mutex);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!is_sbi_flag_set(sbi, SBI_IS_DIRTY) &&
 | 
						if (!is_sbi_flag_set(sbi, SBI_IS_DIRTY) &&
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -537,7 +537,8 @@ void f2fs_submit_page_write(struct f2fs_io_info *fio)
 | 
				
			||||||
	if (fio->in_list)
 | 
						if (fio->in_list)
 | 
				
			||||||
		goto next;
 | 
							goto next;
 | 
				
			||||||
out:
 | 
					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);
 | 
							__submit_merged_bio(io);
 | 
				
			||||||
	up_write(&io->io_rwsem);
 | 
						up_write(&io->io_rwsem);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1703,6 +1704,10 @@ static inline bool check_inplace_update_policy(struct inode *inode,
 | 
				
			||||||
			is_inode_flag_set(inode, FI_NEED_IPU))
 | 
								is_inode_flag_set(inode, FI_NEED_IPU))
 | 
				
			||||||
		return true;
 | 
							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;
 | 
						return false;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1733,6 +1738,9 @@ bool f2fs_should_update_outplace(struct inode *inode, struct f2fs_io_info *fio)
 | 
				
			||||||
			return true;
 | 
								return true;
 | 
				
			||||||
		if (IS_ATOMIC_WRITTEN_PAGE(fio->page))
 | 
							if (IS_ATOMIC_WRITTEN_PAGE(fio->page))
 | 
				
			||||||
			return true;
 | 
								return true;
 | 
				
			||||||
 | 
							if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED) &&
 | 
				
			||||||
 | 
								f2fs_is_checkpointed_data(sbi, fio->old_blkaddr)))
 | 
				
			||||||
 | 
								return true;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return false;
 | 
						return false;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -2353,6 +2361,10 @@ static int f2fs_write_begin(struct file *file, struct address_space *mapping,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	trace_f2fs_write_begin(inode, pos, len, flags);
 | 
						trace_f2fs_write_begin(inode, pos, len, flags);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = f2fs_is_checkpoint_ready(sbi);
 | 
				
			||||||
 | 
						if (err)
 | 
				
			||||||
 | 
							goto fail;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if ((f2fs_is_atomic_file(inode) &&
 | 
						if ((f2fs_is_atomic_file(inode) &&
 | 
				
			||||||
			!f2fs_available_free_memory(sbi, INMEM_PAGES)) ||
 | 
								!f2fs_available_free_memory(sbi, INMEM_PAGES)) ||
 | 
				
			||||||
			is_inode_flag_set(inode, FI_ATOMIC_REVOKE_REQUEST)) {
 | 
								is_inode_flag_set(inode, FI_ATOMIC_REVOKE_REQUEST)) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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",
 | 
							seq_printf(s, "\n=====[ partition info(%pg). #%d, %s, CP: %s]=====\n",
 | 
				
			||||||
			si->sbi->sb->s_bdev, i++,
 | 
								si->sbi->sb->s_bdev, i++,
 | 
				
			||||||
			f2fs_readonly(si->sbi->sb) ? "RO": "RW",
 | 
								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] ",
 | 
							seq_printf(s, "[SB: 1] [CP: 2] [SIT: %d] [NAT: %d] ",
 | 
				
			||||||
			   si->sit_area_segs, si->nat_area_segs);
 | 
								   si->sit_area_segs, si->nat_area_segs);
 | 
				
			||||||
		seq_printf(s, "[SSA: %d] [MAIN: %d",
 | 
							seq_printf(s, "[SSA: %d] [MAIN: %d",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -99,6 +99,7 @@ extern char *f2fs_fault_name[FAULT_MAX];
 | 
				
			||||||
#define F2FS_MOUNT_QUOTA		0x00400000
 | 
					#define F2FS_MOUNT_QUOTA		0x00400000
 | 
				
			||||||
#define F2FS_MOUNT_INLINE_XATTR_SIZE	0x00800000
 | 
					#define F2FS_MOUNT_INLINE_XATTR_SIZE	0x00800000
 | 
				
			||||||
#define F2FS_MOUNT_RESERVE_ROOT		0x01000000
 | 
					#define F2FS_MOUNT_RESERVE_ROOT		0x01000000
 | 
				
			||||||
 | 
					#define F2FS_MOUNT_DISABLE_CHECKPOINT	0x02000000
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define F2FS_OPTION(sbi)	((sbi)->mount_opt)
 | 
					#define F2FS_OPTION(sbi)	((sbi)->mount_opt)
 | 
				
			||||||
#define clear_opt(sbi, option)	(F2FS_OPTION(sbi).opt &= ~F2FS_MOUNT_##option)
 | 
					#define clear_opt(sbi, option)	(F2FS_OPTION(sbi).opt &= ~F2FS_MOUNT_##option)
 | 
				
			||||||
| 
						 | 
					@ -178,6 +179,7 @@ enum {
 | 
				
			||||||
#define	CP_RECOVERY	0x00000008
 | 
					#define	CP_RECOVERY	0x00000008
 | 
				
			||||||
#define	CP_DISCARD	0x00000010
 | 
					#define	CP_DISCARD	0x00000010
 | 
				
			||||||
#define CP_TRIMMED	0x00000020
 | 
					#define CP_TRIMMED	0x00000020
 | 
				
			||||||
 | 
					#define CP_PAUSE	0x00000040
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define MAX_DISCARD_BLOCKS(sbi)		BLKS_PER_SEC(sbi)
 | 
					#define MAX_DISCARD_BLOCKS(sbi)		BLKS_PER_SEC(sbi)
 | 
				
			||||||
#define DEF_MAX_DISCARD_REQUEST		8	/* issue 8 discards per round */
 | 
					#define DEF_MAX_DISCARD_REQUEST		8	/* issue 8 discards per round */
 | 
				
			||||||
| 
						 | 
					@ -187,6 +189,7 @@ enum {
 | 
				
			||||||
#define DEF_DISCARD_URGENT_UTIL		80	/* do more discard over 80% */
 | 
					#define DEF_DISCARD_URGENT_UTIL		80	/* do more discard over 80% */
 | 
				
			||||||
#define DEF_CP_INTERVAL			60	/* 60 secs */
 | 
					#define DEF_CP_INTERVAL			60	/* 60 secs */
 | 
				
			||||||
#define DEF_IDLE_INTERVAL		5	/* 5 secs */
 | 
					#define DEF_IDLE_INTERVAL		5	/* 5 secs */
 | 
				
			||||||
 | 
					#define DEF_DISABLE_INTERVAL		5	/* 5 secs */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct cp_control {
 | 
					struct cp_control {
 | 
				
			||||||
	int reason;
 | 
						int reason;
 | 
				
			||||||
| 
						 | 
					@ -1092,6 +1095,7 @@ enum {
 | 
				
			||||||
	SBI_NEED_CP,				/* need to checkpoint */
 | 
						SBI_NEED_CP,				/* need to checkpoint */
 | 
				
			||||||
	SBI_IS_SHUTDOWN,			/* shutdown by ioctl */
 | 
						SBI_IS_SHUTDOWN,			/* shutdown by ioctl */
 | 
				
			||||||
	SBI_IS_RECOVERED,			/* recovered orphan/data */
 | 
						SBI_IS_RECOVERED,			/* recovered orphan/data */
 | 
				
			||||||
 | 
						SBI_CP_DISABLED,			/* CP was disabled last mount */
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
enum {
 | 
					enum {
 | 
				
			||||||
| 
						 | 
					@ -1099,6 +1103,7 @@ enum {
 | 
				
			||||||
	REQ_TIME,
 | 
						REQ_TIME,
 | 
				
			||||||
	DISCARD_TIME,
 | 
						DISCARD_TIME,
 | 
				
			||||||
	GC_TIME,
 | 
						GC_TIME,
 | 
				
			||||||
 | 
						DISABLE_TIME,
 | 
				
			||||||
	MAX_TIME,
 | 
						MAX_TIME,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1225,6 +1230,9 @@ struct f2fs_sb_info {
 | 
				
			||||||
	block_t reserved_blocks;		/* configurable reserved blocks */
 | 
						block_t reserved_blocks;		/* configurable reserved blocks */
 | 
				
			||||||
	block_t current_reserved_blocks;	/* current 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 */
 | 
						unsigned int nquota_files;		/* # of quota sysfile */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	u32 s_next_generation;			/* for NFS support */
 | 
						u32 s_next_generation;			/* for NFS support */
 | 
				
			||||||
| 
						 | 
					@ -1735,7 +1743,8 @@ static inline int inc_valid_block_count(struct f2fs_sb_info *sbi,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!__allow_reserved_blocks(sbi, inode, true))
 | 
						if (!__allow_reserved_blocks(sbi, inode, true))
 | 
				
			||||||
		avail_user_block_count -= F2FS_OPTION(sbi).root_reserved_blocks;
 | 
							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)) {
 | 
						if (unlikely(sbi->total_valid_block_count > avail_user_block_count)) {
 | 
				
			||||||
		diff = sbi->total_valid_block_count - avail_user_block_count;
 | 
							diff = sbi->total_valid_block_count - avail_user_block_count;
 | 
				
			||||||
		if (diff > *count)
 | 
							if (diff > *count)
 | 
				
			||||||
| 
						 | 
					@ -1942,6 +1951,8 @@ static inline int inc_valid_node_count(struct f2fs_sb_info *sbi,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!__allow_reserved_blocks(sbi, inode, false))
 | 
						if (!__allow_reserved_blocks(sbi, inode, false))
 | 
				
			||||||
		valid_block_count += F2FS_OPTION(sbi).root_reserved_blocks;
 | 
							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)) {
 | 
						if (unlikely(valid_block_count > sbi->user_block_count)) {
 | 
				
			||||||
		spin_unlock(&sbi->stat_lock);
 | 
							spin_unlock(&sbi->stat_lock);
 | 
				
			||||||
| 
						 | 
					@ -2945,6 +2956,8 @@ void f2fs_stop_discard_thread(struct f2fs_sb_info *sbi);
 | 
				
			||||||
bool f2fs_wait_discard_bios(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,
 | 
					void f2fs_clear_prefree_segments(struct f2fs_sb_info *sbi,
 | 
				
			||||||
					struct cp_control *cpc);
 | 
										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);
 | 
					void f2fs_release_discard_addrs(struct f2fs_sb_info *sbi);
 | 
				
			||||||
int f2fs_npages_for_summary_flush(struct f2fs_sb_info *sbi, bool for_ra);
 | 
					int f2fs_npages_for_summary_flush(struct f2fs_sb_info *sbi, bool for_ra);
 | 
				
			||||||
void f2fs_allocate_new_segments(struct f2fs_sb_info *sbi);
 | 
					void f2fs_allocate_new_segments(struct f2fs_sb_info *sbi);
 | 
				
			||||||
| 
						 | 
					@ -3532,6 +3545,9 @@ static inline bool f2fs_force_buffered_io(struct inode *inode,
 | 
				
			||||||
	if (test_opt(sbi, LFS) && (rw == WRITE) &&
 | 
						if (test_opt(sbi, LFS) && (rw == WRITE) &&
 | 
				
			||||||
				block_unaligned_IO(inode, iocb, iter))
 | 
									block_unaligned_IO(inode, iocb, iter))
 | 
				
			||||||
		return true;
 | 
							return true;
 | 
				
			||||||
 | 
						if (is_sbi_flag_set(F2FS_I_SB(inode), SBI_CP_DISABLED))
 | 
				
			||||||
 | 
							return true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return false;
 | 
						return false;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -210,7 +210,8 @@ static int f2fs_do_sync_file(struct file *file, loff_t start, loff_t end,
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
	unsigned int seq_id = 0;
 | 
						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;
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	trace_f2fs_sync_file_enter(inode);
 | 
						trace_f2fs_sync_file_enter(inode);
 | 
				
			||||||
| 
						 | 
					@ -2157,6 +2158,12 @@ static int f2fs_ioc_write_checkpoint(struct file *filp, unsigned long arg)
 | 
				
			||||||
	if (f2fs_readonly(sbi->sb))
 | 
						if (f2fs_readonly(sbi->sb))
 | 
				
			||||||
		return -EROFS;
 | 
							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);
 | 
						ret = mnt_want_write_file(filp);
 | 
				
			||||||
	if (ret)
 | 
						if (ret)
 | 
				
			||||||
		return ret;
 | 
							return ret;
 | 
				
			||||||
| 
						 | 
					@ -2528,6 +2535,9 @@ static int f2fs_ioc_flush_device(struct file *filp, unsigned long arg)
 | 
				
			||||||
	if (f2fs_readonly(sbi->sb))
 | 
						if (f2fs_readonly(sbi->sb))
 | 
				
			||||||
		return -EROFS;
 | 
							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,
 | 
						if (copy_from_user(&range, (struct f2fs_flush_device __user *)arg,
 | 
				
			||||||
							sizeof(range)))
 | 
												sizeof(range)))
 | 
				
			||||||
		return -EFAULT;
 | 
							return -EFAULT;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -370,6 +370,10 @@ static int get_victim_by_default(struct f2fs_sb_info *sbi,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (sec_usage_check(sbi, secno))
 | 
							if (sec_usage_check(sbi, secno))
 | 
				
			||||||
			goto next;
 | 
								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))
 | 
							if (gc_type == BG_GC && test_bit(secno, dirty_i->victim_secmap))
 | 
				
			||||||
			goto next;
 | 
								goto next;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1189,7 +1193,8 @@ int f2fs_gc(struct f2fs_sb_info *sbi, bool sync,
 | 
				
			||||||
		 * threshold, we can make them free by checkpoint. Then, we
 | 
							 * threshold, we can make them free by checkpoint. Then, we
 | 
				
			||||||
		 * secure free segments which doesn't need fggc any more.
 | 
							 * 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);
 | 
								ret = f2fs_write_checkpoint(sbi, &cpc);
 | 
				
			||||||
			if (ret)
 | 
								if (ret)
 | 
				
			||||||
				goto stop;
 | 
									goto stop;
 | 
				
			||||||
| 
						 | 
					@ -1241,7 +1246,7 @@ int f2fs_gc(struct f2fs_sb_info *sbi, bool sync,
 | 
				
			||||||
			segno = NULL_SEGNO;
 | 
								segno = NULL_SEGNO;
 | 
				
			||||||
			goto gc_more;
 | 
								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);
 | 
								ret = f2fs_write_checkpoint(sbi, &cpc);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
stop:
 | 
					stop:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -607,6 +607,9 @@ int f2fs_write_inode(struct inode *inode, struct writeback_control *wbc)
 | 
				
			||||||
	if (!is_inode_flag_set(inode, FI_DIRTY_INODE))
 | 
						if (!is_inode_flag_set(inode, FI_DIRTY_INODE))
 | 
				
			||||||
		return 0;
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (f2fs_is_checkpoint_ready(sbi))
 | 
				
			||||||
 | 
							return -ENOSPC;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * We need to balance fs here to prevent from producing dirty node pages
 | 
						 * We need to balance fs here to prevent from producing dirty node pages
 | 
				
			||||||
	 * during the urgent cleaning time when runing out of free sections.
 | 
						 * during the urgent cleaning time when runing out of free sections.
 | 
				
			||||||
| 
						 | 
					@ -688,7 +691,8 @@ void f2fs_evict_inode(struct inode *inode)
 | 
				
			||||||
	stat_dec_inline_dir(inode);
 | 
						stat_dec_inline_dir(inode);
 | 
				
			||||||
	stat_dec_inline_inode(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));
 | 
							f2fs_bug_on(sbi, is_inode_flag_set(inode, FI_DIRTY_INODE));
 | 
				
			||||||
	else
 | 
						else
 | 
				
			||||||
		f2fs_inode_synced(inode);
 | 
							f2fs_inode_synced(inode);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -16,6 +16,7 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "f2fs.h"
 | 
					#include "f2fs.h"
 | 
				
			||||||
#include "node.h"
 | 
					#include "node.h"
 | 
				
			||||||
 | 
					#include "segment.h"
 | 
				
			||||||
#include "xattr.h"
 | 
					#include "xattr.h"
 | 
				
			||||||
#include "acl.h"
 | 
					#include "acl.h"
 | 
				
			||||||
#include <trace/events/f2fs.h>
 | 
					#include <trace/events/f2fs.h>
 | 
				
			||||||
| 
						 | 
					@ -269,6 +270,9 @@ static int f2fs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (unlikely(f2fs_cp_error(sbi)))
 | 
						if (unlikely(f2fs_cp_error(sbi)))
 | 
				
			||||||
		return -EIO;
 | 
							return -EIO;
 | 
				
			||||||
 | 
						err = f2fs_is_checkpoint_ready(sbi);
 | 
				
			||||||
 | 
						if (err)
 | 
				
			||||||
 | 
							return err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err = dquot_initialize(dir);
 | 
						err = dquot_initialize(dir);
 | 
				
			||||||
	if (err)
 | 
						if (err)
 | 
				
			||||||
| 
						 | 
					@ -315,6 +319,9 @@ static int f2fs_link(struct dentry *old_dentry, struct inode *dir,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (unlikely(f2fs_cp_error(sbi)))
 | 
						if (unlikely(f2fs_cp_error(sbi)))
 | 
				
			||||||
		return -EIO;
 | 
							return -EIO;
 | 
				
			||||||
 | 
						err = f2fs_is_checkpoint_ready(sbi);
 | 
				
			||||||
 | 
						if (err)
 | 
				
			||||||
 | 
							return err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err = fscrypt_prepare_link(old_dentry, dir, dentry);
 | 
						err = fscrypt_prepare_link(old_dentry, dir, dentry);
 | 
				
			||||||
	if (err)
 | 
						if (err)
 | 
				
			||||||
| 
						 | 
					@ -561,6 +568,9 @@ static int f2fs_symlink(struct inode *dir, struct dentry *dentry,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (unlikely(f2fs_cp_error(sbi)))
 | 
						if (unlikely(f2fs_cp_error(sbi)))
 | 
				
			||||||
		return -EIO;
 | 
							return -EIO;
 | 
				
			||||||
 | 
						err = f2fs_is_checkpoint_ready(sbi);
 | 
				
			||||||
 | 
						if (err)
 | 
				
			||||||
 | 
							return err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err = fscrypt_prepare_symlink(dir, symname, len, dir->i_sb->s_blocksize,
 | 
						err = fscrypt_prepare_symlink(dir, symname, len, dir->i_sb->s_blocksize,
 | 
				
			||||||
				      &disk_link);
 | 
									      &disk_link);
 | 
				
			||||||
| 
						 | 
					@ -690,6 +700,9 @@ static int f2fs_mknod(struct inode *dir, struct dentry *dentry,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (unlikely(f2fs_cp_error(sbi)))
 | 
						if (unlikely(f2fs_cp_error(sbi)))
 | 
				
			||||||
		return -EIO;
 | 
							return -EIO;
 | 
				
			||||||
 | 
						err = f2fs_is_checkpoint_ready(sbi);
 | 
				
			||||||
 | 
						if (err)
 | 
				
			||||||
 | 
							return err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err = dquot_initialize(dir);
 | 
						err = dquot_initialize(dir);
 | 
				
			||||||
	if (err)
 | 
						if (err)
 | 
				
			||||||
| 
						 | 
					@ -824,6 +837,9 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (unlikely(f2fs_cp_error(sbi)))
 | 
						if (unlikely(f2fs_cp_error(sbi)))
 | 
				
			||||||
		return -EIO;
 | 
							return -EIO;
 | 
				
			||||||
 | 
						err = f2fs_is_checkpoint_ready(sbi);
 | 
				
			||||||
 | 
						if (err)
 | 
				
			||||||
 | 
							return err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (is_inode_flag_set(new_dir, FI_PROJ_INHERIT) &&
 | 
						if (is_inode_flag_set(new_dir, FI_PROJ_INHERIT) &&
 | 
				
			||||||
			(!projid_eq(F2FS_I(new_dir)->i_projid,
 | 
								(!projid_eq(F2FS_I(new_dir)->i_projid,
 | 
				
			||||||
| 
						 | 
					@ -1014,6 +1030,9 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (unlikely(f2fs_cp_error(sbi)))
 | 
						if (unlikely(f2fs_cp_error(sbi)))
 | 
				
			||||||
		return -EIO;
 | 
							return -EIO;
 | 
				
			||||||
 | 
						err = f2fs_is_checkpoint_ready(sbi);
 | 
				
			||||||
 | 
						if (err)
 | 
				
			||||||
 | 
							return err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if ((is_inode_flag_set(new_dir, FI_PROJ_INHERIT) &&
 | 
						if ((is_inode_flag_set(new_dir, FI_PROJ_INHERIT) &&
 | 
				
			||||||
			!projid_eq(F2FS_I(new_dir)->i_projid,
 | 
								!projid_eq(F2FS_I(new_dir)->i_projid,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -176,6 +176,8 @@ bool f2fs_need_SSR(struct f2fs_sb_info *sbi)
 | 
				
			||||||
		return false;
 | 
							return false;
 | 
				
			||||||
	if (sbi->gc_mode == GC_URGENT)
 | 
						if (sbi->gc_mode == GC_URGENT)
 | 
				
			||||||
		return true;
 | 
							return true;
 | 
				
			||||||
 | 
						if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED)))
 | 
				
			||||||
 | 
							return true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return free_sections(sbi) <= (node_secs + 2 * dent_secs + imeta_secs +
 | 
						return free_sections(sbi) <= (node_secs + 2 * dent_secs + imeta_secs +
 | 
				
			||||||
			SM_I(sbi)->min_ssr_sections + reserved_sections(sbi));
 | 
								SM_I(sbi)->min_ssr_sections + reserved_sections(sbi));
 | 
				
			||||||
| 
						 | 
					@ -480,6 +482,9 @@ void f2fs_balance_fs(struct f2fs_sb_info *sbi, bool need)
 | 
				
			||||||
	if (need && excess_cached_nats(sbi))
 | 
						if (need && excess_cached_nats(sbi))
 | 
				
			||||||
		f2fs_balance_fs_bg(sbi);
 | 
							f2fs_balance_fs_bg(sbi);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (f2fs_is_checkpoint_ready(sbi))
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * We should do GC or end up with checkpoint, if there are so many dirty
 | 
						 * We should do GC or end up with checkpoint, if there are so many dirty
 | 
				
			||||||
	 * dir/node pages without enough free segments.
 | 
						 * dir/node pages without enough free segments.
 | 
				
			||||||
| 
						 | 
					@ -796,7 +801,7 @@ static void __remove_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno,
 | 
				
			||||||
static void locate_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno)
 | 
					static void locate_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
 | 
						struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
 | 
				
			||||||
	unsigned short valid_blocks;
 | 
						unsigned short valid_blocks, ckpt_valid_blocks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (segno == NULL_SEGNO || IS_CURSEG(sbi, segno))
 | 
						if (segno == NULL_SEGNO || IS_CURSEG(sbi, segno))
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
| 
						 | 
					@ -804,8 +809,10 @@ static void locate_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno)
 | 
				
			||||||
	mutex_lock(&dirty_i->seglist_lock);
 | 
						mutex_lock(&dirty_i->seglist_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	valid_blocks = get_valid_blocks(sbi, segno, false);
 | 
						valid_blocks = get_valid_blocks(sbi, segno, false);
 | 
				
			||||||
 | 
						ckpt_valid_blocks = get_ckpt_valid_blocks(sbi, segno);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (valid_blocks == 0) {
 | 
						if (valid_blocks == 0 && (!is_sbi_flag_set(sbi, SBI_CP_DISABLED) ||
 | 
				
			||||||
 | 
									ckpt_valid_blocks == sbi->blocks_per_seg)) {
 | 
				
			||||||
		__locate_dirty_segment(sbi, segno, PRE);
 | 
							__locate_dirty_segment(sbi, segno, PRE);
 | 
				
			||||||
		__remove_dirty_segment(sbi, segno, DIRTY);
 | 
							__remove_dirty_segment(sbi, segno, DIRTY);
 | 
				
			||||||
	} else if (valid_blocks < sbi->blocks_per_seg) {
 | 
						} else if (valid_blocks < sbi->blocks_per_seg) {
 | 
				
			||||||
| 
						 | 
					@ -818,6 +825,66 @@ static void locate_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno)
 | 
				
			||||||
	mutex_unlock(&dirty_i->seglist_lock);
 | 
						mutex_unlock(&dirty_i->seglist_lock);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* This moves currently empty dirty blocks to prefree. Must hold seglist_lock */
 | 
				
			||||||
 | 
					void f2fs_dirty_to_prefree(struct f2fs_sb_info *sbi)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
 | 
				
			||||||
 | 
						unsigned int segno;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mutex_lock(&dirty_i->seglist_lock);
 | 
				
			||||||
 | 
						for_each_set_bit(segno, dirty_i->dirty_segmap[DIRTY], MAIN_SEGS(sbi)) {
 | 
				
			||||||
 | 
							if (get_valid_blocks(sbi, segno, false))
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
							if (IS_CURSEG(sbi, segno))
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
							__locate_dirty_segment(sbi, segno, PRE);
 | 
				
			||||||
 | 
							__remove_dirty_segment(sbi, segno, DIRTY);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						mutex_unlock(&dirty_i->seglist_lock);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int f2fs_disable_cp_again(struct f2fs_sb_info *sbi)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
 | 
				
			||||||
 | 
						block_t ovp = overprovision_segments(sbi) << sbi->log_blocks_per_seg;
 | 
				
			||||||
 | 
						block_t holes[2] = {0, 0};	/* DATA and NODE */
 | 
				
			||||||
 | 
						struct seg_entry *se;
 | 
				
			||||||
 | 
						unsigned int segno;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mutex_lock(&dirty_i->seglist_lock);
 | 
				
			||||||
 | 
						for_each_set_bit(segno, dirty_i->dirty_segmap[DIRTY], MAIN_SEGS(sbi)) {
 | 
				
			||||||
 | 
							se = get_seg_entry(sbi, segno);
 | 
				
			||||||
 | 
							if (IS_NODESEG(se->type))
 | 
				
			||||||
 | 
								holes[NODE] += sbi->blocks_per_seg - se->valid_blocks;
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
								holes[DATA] += sbi->blocks_per_seg - se->valid_blocks;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						mutex_unlock(&dirty_i->seglist_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (holes[DATA] > ovp || holes[NODE] > ovp)
 | 
				
			||||||
 | 
							return -EAGAIN;
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* This is only used by SBI_CP_DISABLED */
 | 
				
			||||||
 | 
					static unsigned int get_free_segment(struct f2fs_sb_info *sbi)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
 | 
				
			||||||
 | 
						unsigned int segno = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mutex_lock(&dirty_i->seglist_lock);
 | 
				
			||||||
 | 
						for_each_set_bit(segno, dirty_i->dirty_segmap[DIRTY], MAIN_SEGS(sbi)) {
 | 
				
			||||||
 | 
							if (get_valid_blocks(sbi, segno, false))
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
							if (get_ckpt_valid_blocks(sbi, segno))
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
							mutex_unlock(&dirty_i->seglist_lock);
 | 
				
			||||||
 | 
							return segno;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						mutex_unlock(&dirty_i->seglist_lock);
 | 
				
			||||||
 | 
						return NULL_SEGNO;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static struct discard_cmd *__create_discard_cmd(struct f2fs_sb_info *sbi,
 | 
					static struct discard_cmd *__create_discard_cmd(struct f2fs_sb_info *sbi,
 | 
				
			||||||
		struct block_device *bdev, block_t lstart,
 | 
							struct block_device *bdev, block_t lstart,
 | 
				
			||||||
		block_t start, block_t len)
 | 
							block_t start, block_t len)
 | 
				
			||||||
| 
						 | 
					@ -2028,7 +2095,8 @@ static void update_sit_entry(struct f2fs_sb_info *sbi, block_t blkaddr, int del)
 | 
				
			||||||
			sbi->discard_blks--;
 | 
								sbi->discard_blks--;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/* don't overwrite by SSR to keep node chain */
 | 
							/* don't overwrite by SSR to keep node chain */
 | 
				
			||||||
		if (IS_NODESEG(se->type)) {
 | 
							if (IS_NODESEG(se->type) &&
 | 
				
			||||||
 | 
									!is_sbi_flag_set(sbi, SBI_CP_DISABLED)) {
 | 
				
			||||||
			if (!f2fs_test_and_set_bit(offset, se->ckpt_valid_map))
 | 
								if (!f2fs_test_and_set_bit(offset, se->ckpt_valid_map))
 | 
				
			||||||
				se->ckpt_valid_blocks++;
 | 
									se->ckpt_valid_blocks++;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -2050,6 +2118,15 @@ static void update_sit_entry(struct f2fs_sb_info *sbi, block_t blkaddr, int del)
 | 
				
			||||||
			f2fs_bug_on(sbi, 1);
 | 
								f2fs_bug_on(sbi, 1);
 | 
				
			||||||
			se->valid_blocks++;
 | 
								se->valid_blocks++;
 | 
				
			||||||
			del = 0;
 | 
								del = 0;
 | 
				
			||||||
 | 
							} else if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED))) {
 | 
				
			||||||
 | 
								/*
 | 
				
			||||||
 | 
								 * If checkpoints are off, we must not reuse data that
 | 
				
			||||||
 | 
								 * was used in the previous checkpoint. If it was used
 | 
				
			||||||
 | 
								 * before, we must track that to know how much space we
 | 
				
			||||||
 | 
								 * really have.
 | 
				
			||||||
 | 
								 */
 | 
				
			||||||
 | 
								if (f2fs_test_bit(offset, se->ckpt_valid_map))
 | 
				
			||||||
 | 
									sbi->unusable_block_count++;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (f2fs_test_and_clear_bit(offset, se->discard_map))
 | 
							if (f2fs_test_and_clear_bit(offset, se->discard_map))
 | 
				
			||||||
| 
						 | 
					@ -2332,6 +2409,9 @@ static unsigned int __get_next_segno(struct f2fs_sb_info *sbi, int type)
 | 
				
			||||||
	if (sbi->segs_per_sec != 1)
 | 
						if (sbi->segs_per_sec != 1)
 | 
				
			||||||
		return CURSEG_I(sbi, type)->segno;
 | 
							return CURSEG_I(sbi, type)->segno;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED)))
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (test_opt(sbi, NOHEAP) &&
 | 
						if (test_opt(sbi, NOHEAP) &&
 | 
				
			||||||
		(type == CURSEG_HOT_DATA || IS_NODESEG(type)))
 | 
							(type == CURSEG_HOT_DATA || IS_NODESEG(type)))
 | 
				
			||||||
		return 0;
 | 
							return 0;
 | 
				
			||||||
| 
						 | 
					@ -2476,6 +2556,15 @@ static int get_ssr_segment(struct f2fs_sb_info *sbi, int type)
 | 
				
			||||||
			return 1;
 | 
								return 1;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* find valid_blocks=0 in dirty list */
 | 
				
			||||||
 | 
						if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED))) {
 | 
				
			||||||
 | 
							segno = get_free_segment(sbi);
 | 
				
			||||||
 | 
							if (segno != NULL_SEGNO) {
 | 
				
			||||||
 | 
								curseg->next_segno = segno;
 | 
				
			||||||
 | 
								return 1;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2493,7 +2582,8 @@ static void allocate_segment_by_default(struct f2fs_sb_info *sbi,
 | 
				
			||||||
	else if (!is_set_ckpt_flags(sbi, CP_CRC_RECOVERY_FLAG) &&
 | 
						else if (!is_set_ckpt_flags(sbi, CP_CRC_RECOVERY_FLAG) &&
 | 
				
			||||||
					type == CURSEG_WARM_NODE)
 | 
										type == CURSEG_WARM_NODE)
 | 
				
			||||||
		new_curseg(sbi, type, false);
 | 
							new_curseg(sbi, type, false);
 | 
				
			||||||
	else if (curseg->alloc_type == LFS && is_next_segment_free(sbi, type))
 | 
						else if (curseg->alloc_type == LFS && is_next_segment_free(sbi, type) &&
 | 
				
			||||||
 | 
								likely(!is_sbi_flag_set(sbi, SBI_CP_DISABLED)))
 | 
				
			||||||
		new_curseg(sbi, type, false);
 | 
							new_curseg(sbi, type, false);
 | 
				
			||||||
	else if (f2fs_need_SSR(sbi) && get_ssr_segment(sbi, type))
 | 
						else if (f2fs_need_SSR(sbi) && get_ssr_segment(sbi, type))
 | 
				
			||||||
		change_curseg(sbi, type);
 | 
							change_curseg(sbi, type);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -339,6 +339,12 @@ static inline unsigned int get_valid_blocks(struct f2fs_sb_info *sbi,
 | 
				
			||||||
		return get_seg_entry(sbi, segno)->valid_blocks;
 | 
							return get_seg_entry(sbi, segno)->valid_blocks;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline unsigned int get_ckpt_valid_blocks(struct f2fs_sb_info *sbi,
 | 
				
			||||||
 | 
									unsigned int segno)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return get_seg_entry(sbi, segno)->ckpt_valid_blocks;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline void seg_info_from_raw_sit(struct seg_entry *se,
 | 
					static inline void seg_info_from_raw_sit(struct seg_entry *se,
 | 
				
			||||||
					struct f2fs_sit_entry *rs)
 | 
										struct f2fs_sit_entry *rs)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -576,6 +582,15 @@ static inline bool has_not_enough_free_secs(struct f2fs_sb_info *sbi,
 | 
				
			||||||
		reserved_sections(sbi) + needed);
 | 
							reserved_sections(sbi) + needed);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline int f2fs_is_checkpoint_ready(struct f2fs_sb_info *sbi)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (likely(!is_sbi_flag_set(sbi, SBI_CP_DISABLED)))
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
						if (likely(!has_not_enough_free_secs(sbi, 0, 0)))
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
						return -ENOSPC;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline bool excess_prefree_segs(struct f2fs_sb_info *sbi)
 | 
					static inline bool excess_prefree_segs(struct f2fs_sb_info *sbi)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	return prefree_segments(sbi) > SM_I(sbi)->rec_prefree_segments;
 | 
						return prefree_segments(sbi) > SM_I(sbi)->rec_prefree_segments;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										126
									
								
								fs/f2fs/super.c
									
									
									
									
									
								
							
							
						
						
									
										126
									
								
								fs/f2fs/super.c
									
									
									
									
									
								
							| 
						 | 
					@ -136,6 +136,7 @@ enum {
 | 
				
			||||||
	Opt_alloc,
 | 
						Opt_alloc,
 | 
				
			||||||
	Opt_fsync,
 | 
						Opt_fsync,
 | 
				
			||||||
	Opt_test_dummy_encryption,
 | 
						Opt_test_dummy_encryption,
 | 
				
			||||||
 | 
						Opt_checkpoint,
 | 
				
			||||||
	Opt_err,
 | 
						Opt_err,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -194,6 +195,7 @@ static match_table_t f2fs_tokens = {
 | 
				
			||||||
	{Opt_alloc, "alloc_mode=%s"},
 | 
						{Opt_alloc, "alloc_mode=%s"},
 | 
				
			||||||
	{Opt_fsync, "fsync_mode=%s"},
 | 
						{Opt_fsync, "fsync_mode=%s"},
 | 
				
			||||||
	{Opt_test_dummy_encryption, "test_dummy_encryption"},
 | 
						{Opt_test_dummy_encryption, "test_dummy_encryption"},
 | 
				
			||||||
 | 
						{Opt_checkpoint, "checkpoint=%s"},
 | 
				
			||||||
	{Opt_err, NULL},
 | 
						{Opt_err, NULL},
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -769,6 +771,23 @@ static int parse_options(struct super_block *sb, char *options)
 | 
				
			||||||
					"Test dummy encryption mount option ignored");
 | 
										"Test dummy encryption mount option ignored");
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
 | 
							case Opt_checkpoint:
 | 
				
			||||||
 | 
								name = match_strdup(&args[0]);
 | 
				
			||||||
 | 
								if (!name)
 | 
				
			||||||
 | 
									return -ENOMEM;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (strlen(name) == 6 &&
 | 
				
			||||||
 | 
										!strncmp(name, "enable", 6)) {
 | 
				
			||||||
 | 
									clear_opt(sbi, DISABLE_CHECKPOINT);
 | 
				
			||||||
 | 
								} else if (strlen(name) == 7 &&
 | 
				
			||||||
 | 
										!strncmp(name, "disable", 7)) {
 | 
				
			||||||
 | 
									set_opt(sbi, DISABLE_CHECKPOINT);
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									kfree(name);
 | 
				
			||||||
 | 
									return -EINVAL;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								kfree(name);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
		default:
 | 
							default:
 | 
				
			||||||
			f2fs_msg(sb, KERN_ERR,
 | 
								f2fs_msg(sb, KERN_ERR,
 | 
				
			||||||
				"Unrecognized mount option \"%s\" or missing value",
 | 
									"Unrecognized mount option \"%s\" or missing value",
 | 
				
			||||||
| 
						 | 
					@ -827,6 +846,12 @@ static int parse_options(struct super_block *sb, char *options)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (test_opt(sbi, DISABLE_CHECKPOINT) && test_opt(sbi, LFS)) {
 | 
				
			||||||
 | 
							f2fs_msg(sb, KERN_ERR,
 | 
				
			||||||
 | 
									"LFS not compatible with checkpoint=disable\n");
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Not pass down write hints if the number of active logs is lesser
 | 
						/* Not pass down write hints if the number of active logs is lesser
 | 
				
			||||||
	 * than NR_CURSEG_TYPE.
 | 
						 * than NR_CURSEG_TYPE.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
| 
						 | 
					@ -1014,8 +1039,8 @@ static void f2fs_put_super(struct super_block *sb)
 | 
				
			||||||
	 * But, the previous checkpoint was not done by umount, it needs to do
 | 
						 * But, the previous checkpoint was not done by umount, it needs to do
 | 
				
			||||||
	 * clean checkpoint again.
 | 
						 * clean checkpoint again.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	if (is_sbi_flag_set(sbi, SBI_IS_DIRTY) ||
 | 
						if ((is_sbi_flag_set(sbi, SBI_IS_DIRTY) ||
 | 
				
			||||||
			!is_set_ckpt_flags(sbi, CP_UMOUNT_FLAG)) {
 | 
								!is_set_ckpt_flags(sbi, CP_UMOUNT_FLAG))) {
 | 
				
			||||||
		struct cp_control cpc = {
 | 
							struct cp_control cpc = {
 | 
				
			||||||
			.reason = CP_UMOUNT,
 | 
								.reason = CP_UMOUNT,
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
| 
						 | 
					@ -1087,6 +1112,8 @@ int f2fs_sync_fs(struct super_block *sb, int sync)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (unlikely(f2fs_cp_error(sbi)))
 | 
						if (unlikely(f2fs_cp_error(sbi)))
 | 
				
			||||||
		return 0;
 | 
							return 0;
 | 
				
			||||||
 | 
						if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED)))
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	trace_f2fs_sync_fs(sb, sync);
 | 
						trace_f2fs_sync_fs(sb, sync);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1186,6 +1213,11 @@ static int f2fs_statfs(struct dentry *dentry, struct kstatfs *buf)
 | 
				
			||||||
	buf->f_blocks = total_count - start_count;
 | 
						buf->f_blocks = total_count - start_count;
 | 
				
			||||||
	buf->f_bfree = user_block_count - valid_user_blocks(sbi) -
 | 
						buf->f_bfree = user_block_count - valid_user_blocks(sbi) -
 | 
				
			||||||
						sbi->current_reserved_blocks;
 | 
											sbi->current_reserved_blocks;
 | 
				
			||||||
 | 
						if (unlikely(buf->f_bfree <= sbi->unusable_block_count))
 | 
				
			||||||
 | 
							buf->f_bfree = 0;
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							buf->f_bfree -= sbi->unusable_block_count;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (buf->f_bfree > F2FS_OPTION(sbi).root_reserved_blocks)
 | 
						if (buf->f_bfree > F2FS_OPTION(sbi).root_reserved_blocks)
 | 
				
			||||||
		buf->f_bavail = buf->f_bfree -
 | 
							buf->f_bavail = buf->f_bfree -
 | 
				
			||||||
				F2FS_OPTION(sbi).root_reserved_blocks;
 | 
									F2FS_OPTION(sbi).root_reserved_blocks;
 | 
				
			||||||
| 
						 | 
					@ -1365,6 +1397,9 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root)
 | 
				
			||||||
	else if (F2FS_OPTION(sbi).alloc_mode == ALLOC_MODE_REUSE)
 | 
						else if (F2FS_OPTION(sbi).alloc_mode == ALLOC_MODE_REUSE)
 | 
				
			||||||
		seq_printf(seq, ",alloc_mode=%s", "reuse");
 | 
							seq_printf(seq, ",alloc_mode=%s", "reuse");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (test_opt(sbi, DISABLE_CHECKPOINT))
 | 
				
			||||||
 | 
							seq_puts(seq, ",checkpoint=disable");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (F2FS_OPTION(sbi).fsync_mode == FSYNC_MODE_POSIX)
 | 
						if (F2FS_OPTION(sbi).fsync_mode == FSYNC_MODE_POSIX)
 | 
				
			||||||
		seq_printf(seq, ",fsync_mode=%s", "posix");
 | 
							seq_printf(seq, ",fsync_mode=%s", "posix");
 | 
				
			||||||
	else if (F2FS_OPTION(sbi).fsync_mode == FSYNC_MODE_STRICT)
 | 
						else if (F2FS_OPTION(sbi).fsync_mode == FSYNC_MODE_STRICT)
 | 
				
			||||||
| 
						 | 
					@ -1392,6 +1427,7 @@ static void default_options(struct f2fs_sb_info *sbi)
 | 
				
			||||||
	set_opt(sbi, INLINE_DENTRY);
 | 
						set_opt(sbi, INLINE_DENTRY);
 | 
				
			||||||
	set_opt(sbi, EXTENT_CACHE);
 | 
						set_opt(sbi, EXTENT_CACHE);
 | 
				
			||||||
	set_opt(sbi, NOHEAP);
 | 
						set_opt(sbi, NOHEAP);
 | 
				
			||||||
 | 
						clear_opt(sbi, DISABLE_CHECKPOINT);
 | 
				
			||||||
	sbi->sb->s_flags |= SB_LAZYTIME;
 | 
						sbi->sb->s_flags |= SB_LAZYTIME;
 | 
				
			||||||
	set_opt(sbi, FLUSH_MERGE);
 | 
						set_opt(sbi, FLUSH_MERGE);
 | 
				
			||||||
	set_opt(sbi, DISCARD);
 | 
						set_opt(sbi, DISCARD);
 | 
				
			||||||
| 
						 | 
					@ -1413,6 +1449,57 @@ static void default_options(struct f2fs_sb_info *sbi)
 | 
				
			||||||
#ifdef CONFIG_QUOTA
 | 
					#ifdef CONFIG_QUOTA
 | 
				
			||||||
static int f2fs_enable_quotas(struct super_block *sb);
 | 
					static int f2fs_enable_quotas(struct super_block *sb);
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int f2fs_disable_checkpoint(struct f2fs_sb_info *sbi)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct cp_control cpc;
 | 
				
			||||||
 | 
						int err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sbi->sb->s_flags |= SB_ACTIVE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mutex_lock(&sbi->gc_mutex);
 | 
				
			||||||
 | 
						f2fs_update_time(sbi, DISABLE_TIME);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						while (!f2fs_time_over(sbi, DISABLE_TIME)) {
 | 
				
			||||||
 | 
							err = f2fs_gc(sbi, true, false, NULL_SEGNO);
 | 
				
			||||||
 | 
							if (err == -ENODATA)
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							if (err && err != -EAGAIN) {
 | 
				
			||||||
 | 
								mutex_unlock(&sbi->gc_mutex);
 | 
				
			||||||
 | 
								return err;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						mutex_unlock(&sbi->gc_mutex);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = sync_filesystem(sbi->sb);
 | 
				
			||||||
 | 
						if (err)
 | 
				
			||||||
 | 
							return err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (f2fs_disable_cp_again(sbi))
 | 
				
			||||||
 | 
							return -EAGAIN;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mutex_lock(&sbi->gc_mutex);
 | 
				
			||||||
 | 
						cpc.reason = CP_PAUSE;
 | 
				
			||||||
 | 
						set_sbi_flag(sbi, SBI_CP_DISABLED);
 | 
				
			||||||
 | 
						f2fs_write_checkpoint(sbi, &cpc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sbi->unusable_block_count = 0;
 | 
				
			||||||
 | 
						mutex_unlock(&sbi->gc_mutex);
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void f2fs_enable_checkpoint(struct f2fs_sb_info *sbi)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						mutex_lock(&sbi->gc_mutex);
 | 
				
			||||||
 | 
						f2fs_dirty_to_prefree(sbi);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						clear_sbi_flag(sbi, SBI_CP_DISABLED);
 | 
				
			||||||
 | 
						set_sbi_flag(sbi, SBI_IS_DIRTY);
 | 
				
			||||||
 | 
						mutex_unlock(&sbi->gc_mutex);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						f2fs_sync_fs(sbi->sb, 1);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int f2fs_remount(struct super_block *sb, int *flags, char *data)
 | 
					static int f2fs_remount(struct super_block *sb, int *flags, char *data)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct f2fs_sb_info *sbi = F2FS_SB(sb);
 | 
						struct f2fs_sb_info *sbi = F2FS_SB(sb);
 | 
				
			||||||
| 
						 | 
					@ -1422,6 +1509,8 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
 | 
				
			||||||
	bool need_restart_gc = false;
 | 
						bool need_restart_gc = false;
 | 
				
			||||||
	bool need_stop_gc = false;
 | 
						bool need_stop_gc = false;
 | 
				
			||||||
	bool no_extent_cache = !test_opt(sbi, EXTENT_CACHE);
 | 
						bool no_extent_cache = !test_opt(sbi, EXTENT_CACHE);
 | 
				
			||||||
 | 
						bool disable_checkpoint = test_opt(sbi, DISABLE_CHECKPOINT);
 | 
				
			||||||
 | 
						bool checkpoint_changed;
 | 
				
			||||||
#ifdef CONFIG_QUOTA
 | 
					#ifdef CONFIG_QUOTA
 | 
				
			||||||
	int i, j;
 | 
						int i, j;
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
| 
						 | 
					@ -1466,6 +1555,8 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
 | 
				
			||||||
	err = parse_options(sb, data);
 | 
						err = parse_options(sb, data);
 | 
				
			||||||
	if (err)
 | 
						if (err)
 | 
				
			||||||
		goto restore_opts;
 | 
							goto restore_opts;
 | 
				
			||||||
 | 
						checkpoint_changed =
 | 
				
			||||||
 | 
								disable_checkpoint != test_opt(sbi, DISABLE_CHECKPOINT);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * Previous and new state of filesystem is RO,
 | 
						 * Previous and new state of filesystem is RO,
 | 
				
			||||||
| 
						 | 
					@ -1479,7 +1570,7 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
 | 
				
			||||||
		err = dquot_suspend(sb, -1);
 | 
							err = dquot_suspend(sb, -1);
 | 
				
			||||||
		if (err < 0)
 | 
							if (err < 0)
 | 
				
			||||||
			goto restore_opts;
 | 
								goto restore_opts;
 | 
				
			||||||
	} else if (f2fs_readonly(sb) && !(*flags & MS_RDONLY)) {
 | 
						} else if (f2fs_readonly(sb) && !(*flags & SB_RDONLY)) {
 | 
				
			||||||
		/* dquot_resume needs RW */
 | 
							/* dquot_resume needs RW */
 | 
				
			||||||
		sb->s_flags &= ~SB_RDONLY;
 | 
							sb->s_flags &= ~SB_RDONLY;
 | 
				
			||||||
		if (sb_any_quota_suspended(sb)) {
 | 
							if (sb_any_quota_suspended(sb)) {
 | 
				
			||||||
| 
						 | 
					@ -1499,6 +1590,13 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
 | 
				
			||||||
		goto restore_opts;
 | 
							goto restore_opts;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if ((*flags & SB_RDONLY) && test_opt(sbi, DISABLE_CHECKPOINT)) {
 | 
				
			||||||
 | 
							err = -EINVAL;
 | 
				
			||||||
 | 
							f2fs_msg(sbi->sb, KERN_WARNING,
 | 
				
			||||||
 | 
								"disabling checkpoint not compatible with read-only");
 | 
				
			||||||
 | 
							goto restore_opts;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * We stop the GC thread if FS is mounted as RO
 | 
						 * We stop the GC thread if FS is mounted as RO
 | 
				
			||||||
	 * or if background_gc = off is passed in mount
 | 
						 * or if background_gc = off is passed in mount
 | 
				
			||||||
| 
						 | 
					@ -1527,6 +1625,16 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
 | 
				
			||||||
		clear_sbi_flag(sbi, SBI_IS_CLOSE);
 | 
							clear_sbi_flag(sbi, SBI_IS_CLOSE);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (checkpoint_changed) {
 | 
				
			||||||
 | 
							if (test_opt(sbi, DISABLE_CHECKPOINT)) {
 | 
				
			||||||
 | 
								err = f2fs_disable_checkpoint(sbi);
 | 
				
			||||||
 | 
								if (err)
 | 
				
			||||||
 | 
									goto restore_gc;
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								f2fs_enable_checkpoint(sbi);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * We stop issue flush thread if FS is mounted as RO
 | 
						 * We stop issue flush thread if FS is mounted as RO
 | 
				
			||||||
	 * or if flush_merge is not passed in mount option.
 | 
						 * or if flush_merge is not passed in mount option.
 | 
				
			||||||
| 
						 | 
					@ -2485,6 +2593,7 @@ static void init_sb_info(struct f2fs_sb_info *sbi)
 | 
				
			||||||
	sbi->interval_time[REQ_TIME] = DEF_IDLE_INTERVAL;
 | 
						sbi->interval_time[REQ_TIME] = DEF_IDLE_INTERVAL;
 | 
				
			||||||
	sbi->interval_time[DISCARD_TIME] = DEF_IDLE_INTERVAL;
 | 
						sbi->interval_time[DISCARD_TIME] = DEF_IDLE_INTERVAL;
 | 
				
			||||||
	sbi->interval_time[GC_TIME] = DEF_IDLE_INTERVAL;
 | 
						sbi->interval_time[GC_TIME] = DEF_IDLE_INTERVAL;
 | 
				
			||||||
 | 
						sbi->interval_time[DISABLE_TIME] = DEF_DISABLE_INTERVAL;
 | 
				
			||||||
	clear_sbi_flag(sbi, SBI_NEED_FSCK);
 | 
						clear_sbi_flag(sbi, SBI_NEED_FSCK);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for (i = 0; i < NR_COUNT_TYPE; i++)
 | 
						for (i = 0; i < NR_COUNT_TYPE; i++)
 | 
				
			||||||
| 
						 | 
					@ -3093,6 +3202,9 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
 | 
				
			||||||
	if (err)
 | 
						if (err)
 | 
				
			||||||
		goto free_meta;
 | 
							goto free_meta;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (unlikely(is_set_ckpt_flags(sbi, CP_DISABLED_FLAG)))
 | 
				
			||||||
 | 
							goto skip_recovery;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* recover fsynced data */
 | 
						/* recover fsynced data */
 | 
				
			||||||
	if (!test_opt(sbi, DISABLE_ROLL_FORWARD)) {
 | 
						if (!test_opt(sbi, DISABLE_ROLL_FORWARD)) {
 | 
				
			||||||
		/*
 | 
							/*
 | 
				
			||||||
| 
						 | 
					@ -3132,6 +3244,14 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
 | 
				
			||||||
	/* f2fs_recover_fsync_data() cleared this already */
 | 
						/* f2fs_recover_fsync_data() cleared this already */
 | 
				
			||||||
	clear_sbi_flag(sbi, SBI_POR_DOING);
 | 
						clear_sbi_flag(sbi, SBI_POR_DOING);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (test_opt(sbi, DISABLE_CHECKPOINT)) {
 | 
				
			||||||
 | 
							err = f2fs_disable_checkpoint(sbi);
 | 
				
			||||||
 | 
							if (err)
 | 
				
			||||||
 | 
								goto free_meta;
 | 
				
			||||||
 | 
						} else if (is_set_ckpt_flags(sbi, CP_DISABLED_FLAG)) {
 | 
				
			||||||
 | 
							f2fs_enable_checkpoint(sbi);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * If filesystem is not mounted as read-only then
 | 
						 * If filesystem is not mounted as read-only then
 | 
				
			||||||
	 * do start the gc_thread.
 | 
						 * do start the gc_thread.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -116,6 +116,7 @@ struct f2fs_super_block {
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * For checkpoint
 | 
					 * For checkpoint
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					#define CP_DISABLED_FLAG		0x00001000
 | 
				
			||||||
#define CP_LARGE_NAT_BITMAP_FLAG	0x00000400
 | 
					#define CP_LARGE_NAT_BITMAP_FLAG	0x00000400
 | 
				
			||||||
#define CP_NOCRC_RECOVERY_FLAG	0x00000200
 | 
					#define CP_NOCRC_RECOVERY_FLAG	0x00000200
 | 
				
			||||||
#define CP_TRIMMED_FLAG		0x00000100
 | 
					#define CP_TRIMMED_FLAG		0x00000100
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue