Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 43727527 authored by Jaegeuk Kim's avatar Jaegeuk Kim
Browse files

f2fs: clarify and enhance the f2fs_gc flow



This patch makes clearer the ambiguous f2fs_gc flow as follows.

1. Remove intermediate checkpoint condition during f2fs_gc
 (i.e., should_do_checkpoint() and GC_BLOCKED)

2. Remove unnecessary return values of f2fs_gc because of #1.
 (i.e., GC_NODE, GC_OK, etc)

3. Simplify write_checkpoint() because of #2.

4. Clarify the main f2fs_gc flow.
 o monitor how many freed sections during one iteration of do_garbage_collect().
 o do GC more without checkpoints if we can't get enough free sections.
 o do checkpoint once we've got enough free sections through forground GCs.

5. Adopt thread-logging (Slack-Space-Recycle) scheme more aggressively on data
  log types. See. get_ssr_segement()

Signed-off-by: default avatarJaegeuk Kim <jaegeuk.kim@samsung.com>
parent b1f1daf8
Loading
Loading
Loading
Loading
+4 −6
Original line number Diff line number Diff line
@@ -539,7 +539,7 @@ void sync_dirty_dir_inodes(struct f2fs_sb_info *sbi)
/*
 * Freeze all the FS-operations for checkpoint.
 */
void block_operations(struct f2fs_sb_info *sbi)
static void block_operations(struct f2fs_sb_info *sbi)
{
	int t;
	struct writeback_control wbc = {
@@ -722,15 +722,13 @@ static void do_checkpoint(struct f2fs_sb_info *sbi, bool is_umount)
/*
 * We guarantee that this checkpoint procedure should not fail.
 */
void write_checkpoint(struct f2fs_sb_info *sbi, bool blocked, bool is_umount)
void write_checkpoint(struct f2fs_sb_info *sbi, bool is_umount)
{
	struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
	unsigned long long ckpt_ver;

	if (!blocked) {
	mutex_lock(&sbi->cp_mutex);
	block_operations(sbi);
	}

	f2fs_submit_bio(sbi, DATA, true);
	f2fs_submit_bio(sbi, NODE, true);
+1 −2
Original line number Diff line number Diff line
@@ -969,8 +969,7 @@ int get_valid_checkpoint(struct f2fs_sb_info *);
void set_dirty_dir_page(struct inode *, struct page *);
void remove_dirty_dir_inode(struct inode *);
void sync_dirty_dir_inodes(struct f2fs_sb_info *);
void block_operations(struct f2fs_sb_info *);
void write_checkpoint(struct f2fs_sb_info *, bool, bool);
void write_checkpoint(struct f2fs_sb_info *, bool);
void init_orphan_info(struct f2fs_sb_info *);
int __init create_checkpoint_caches(void);
void destroy_checkpoint_caches(void);
+41 −66
Original line number Diff line number Diff line
@@ -78,7 +78,8 @@ static int gc_thread_func(void *data)

		sbi->bg_gc++;

		if (f2fs_gc(sbi) == GC_NONE)
		/* if return value is not zero, no victim was selected */
		if (f2fs_gc(sbi))
			wait_ms = GC_THREAD_NOGC_SLEEP_TIME;
		else if (wait_ms == GC_THREAD_NOGC_SLEEP_TIME)
			wait_ms = GC_THREAD_MAX_SLEEP_TIME;
@@ -360,7 +361,7 @@ static int check_valid_map(struct f2fs_sb_info *sbi,
	sentry = get_seg_entry(sbi, segno);
	ret = f2fs_test_bit(offset, sentry->cur_valid_map);
	mutex_unlock(&sit_i->sentry_lock);
	return ret ? GC_OK : GC_NEXT;
	return ret;
}

/*
@@ -368,7 +369,7 @@ static int check_valid_map(struct f2fs_sb_info *sbi,
 * On validity, copy that node with cold status, otherwise (invalid node)
 * ignore that.
 */
static int gc_node_segment(struct f2fs_sb_info *sbi,
static void gc_node_segment(struct f2fs_sb_info *sbi,
		struct f2fs_summary *sum, unsigned int segno, int gc_type)
{
	bool initial = true;
@@ -380,21 +381,12 @@ static int gc_node_segment(struct f2fs_sb_info *sbi,
	for (off = 0; off < sbi->blocks_per_seg; off++, entry++) {
		nid_t nid = le32_to_cpu(entry->nid);
		struct page *node_page;
		int err;

		/*
		 * It makes sure that free segments are able to write
		 * all the dirty node pages before CP after this CP.
		 * So let's check the space of dirty node pages.
		 */
		if (should_do_checkpoint(sbi)) {
			mutex_lock(&sbi->cp_mutex);
			block_operations(sbi);
			return GC_BLOCKED;
		}
		/* stop BG_GC if there is not enough free sections. */
		if (gc_type == BG_GC && has_not_enough_free_secs(sbi, 0))
			return;

		err = check_valid_map(sbi, segno, off);
		if (err == GC_NEXT)
		if (check_valid_map(sbi, segno, off) == 0)
			continue;

		if (initial) {
@@ -424,7 +416,6 @@ static int gc_node_segment(struct f2fs_sb_info *sbi,
		};
		sync_node_pages(sbi, 0, &wbc);
	}
	return GC_DONE;
}

/*
@@ -467,13 +458,13 @@ static int check_dnode(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,

	node_page = get_node_page(sbi, nid);
	if (IS_ERR(node_page))
		return GC_NEXT;
		return 0;

	get_node_info(sbi, nid, dni);

	if (sum->version != dni->version) {
		f2fs_put_page(node_page, 1);
		return GC_NEXT;
		return 0;
	}

	*nofs = ofs_of_node(node_page);
@@ -481,8 +472,8 @@ static int check_dnode(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,
	f2fs_put_page(node_page, 1);

	if (source_blkaddr != blkaddr)
		return GC_NEXT;
	return GC_OK;
		return 0;
	return 1;
}

static void move_data_page(struct inode *inode, struct page *page, int gc_type)
@@ -523,13 +514,13 @@ static void move_data_page(struct inode *inode, struct page *page, int gc_type)
 * If the parent node is not valid or the data block address is different,
 * the victim data block is ignored.
 */
static int gc_data_segment(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,
static void gc_data_segment(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,
		struct list_head *ilist, unsigned int segno, int gc_type)
{
	struct super_block *sb = sbi->sb;
	struct f2fs_summary *entry;
	block_t start_addr;
	int err, off;
	int off;
	int phase = 0;

	start_addr = START_BLOCK(sbi, segno);
@@ -543,20 +534,11 @@ static int gc_data_segment(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,
		unsigned int ofs_in_node, nofs;
		block_t start_bidx;

		/*
		 * It makes sure that free segments are able to write
		 * all the dirty node pages before CP after this CP.
		 * So let's check the space of dirty node pages.
		 */
		if (should_do_checkpoint(sbi)) {
			mutex_lock(&sbi->cp_mutex);
			block_operations(sbi);
			err = GC_BLOCKED;
			goto stop;
		}
		/* stop BG_GC if there is not enough free sections. */
		if (gc_type == BG_GC && has_not_enough_free_secs(sbi, 0))
			return;

		err = check_valid_map(sbi, segno, off);
		if (err == GC_NEXT)
		if (check_valid_map(sbi, segno, off) == 0)
			continue;

		if (phase == 0) {
@@ -565,8 +547,7 @@ static int gc_data_segment(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,
		}

		/* Get an inode by ino with checking validity */
		err = check_dnode(sbi, entry, &dni, start_addr + off, &nofs);
		if (err == GC_NEXT)
		if (check_dnode(sbi, entry, &dni, start_addr + off, &nofs) == 0)
			continue;

		if (phase == 1) {
@@ -606,11 +587,9 @@ static int gc_data_segment(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,
	}
	if (++phase < 4)
		goto next_step;
	err = GC_DONE;
stop:

	if (gc_type == FG_GC)
		f2fs_submit_bio(sbi, DATA, true);
	return err;
}

static int __get_victim(struct f2fs_sb_info *sbi, unsigned int *victim,
@@ -624,17 +603,16 @@ static int __get_victim(struct f2fs_sb_info *sbi, unsigned int *victim,
	return ret;
}

static int do_garbage_collect(struct f2fs_sb_info *sbi, unsigned int segno,
static void do_garbage_collect(struct f2fs_sb_info *sbi, unsigned int segno,
				struct list_head *ilist, int gc_type)
{
	struct page *sum_page;
	struct f2fs_summary_block *sum;
	int ret = GC_DONE;

	/* read segment summary of victim */
	sum_page = get_sum_page(sbi, segno);
	if (IS_ERR(sum_page))
		return GC_ERROR;
		return;

	/*
	 * CP needs to lock sum_page. In this time, we don't need
@@ -646,17 +624,16 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi, unsigned int segno,

	switch (GET_SUM_TYPE((&sum->footer))) {
	case SUM_TYPE_NODE:
		ret = gc_node_segment(sbi, sum->entries, segno, gc_type);
		gc_node_segment(sbi, sum->entries, segno, gc_type);
		break;
	case SUM_TYPE_DATA:
		ret = gc_data_segment(sbi, sum->entries, ilist, segno, gc_type);
		gc_data_segment(sbi, sum->entries, ilist, segno, gc_type);
		break;
	}
	stat_inc_seg_count(sbi, GET_SUM_TYPE((&sum->footer)));
	stat_inc_call_count(sbi->stat_info);

	f2fs_put_page(sum_page, 0);
	return ret;
}

int f2fs_gc(struct f2fs_sb_info *sbi)
@@ -664,40 +641,38 @@ int f2fs_gc(struct f2fs_sb_info *sbi)
	struct list_head ilist;
	unsigned int segno, i;
	int gc_type = BG_GC;
	int gc_status = GC_NONE;
	int nfree = 0;
	int ret = -1;

	INIT_LIST_HEAD(&ilist);
gc_more:
	if (!(sbi->sb->s_flags & MS_ACTIVE))
		goto stop;

	if (gc_type == BG_GC && has_not_enough_free_secs(sbi))
	if (gc_type == BG_GC && has_not_enough_free_secs(sbi, nfree))
		gc_type = FG_GC;

	if (!__get_victim(sbi, &segno, gc_type, NO_CHECK_TYPE))
		goto stop;
	ret = 0;

	for (i = 0; i < sbi->segs_per_sec; i++) {
		/*
		 * do_garbage_collect will give us three gc_status:
		 * GC_ERROR, GC_DONE, and GC_BLOCKED.
		 * If GC is finished uncleanly, we have to return
		 * the victim to dirty segment list.
		 */
		gc_status = do_garbage_collect(sbi, segno + i, &ilist, gc_type);
		if (gc_status != GC_DONE)
			break;
	}
	if (has_not_enough_free_secs(sbi)) {
		write_checkpoint(sbi, (gc_status == GC_BLOCKED), false);
		if (has_not_enough_free_secs(sbi))
	for (i = 0; i < sbi->segs_per_sec; i++)
		do_garbage_collect(sbi, segno + i, &ilist, gc_type);

	if (gc_type == FG_GC &&
			get_valid_blocks(sbi, segno, sbi->segs_per_sec) == 0)
		nfree++;

	if (has_not_enough_free_secs(sbi, nfree))
		goto gc_more;
	}

	if (gc_type == FG_GC)
		write_checkpoint(sbi, false);
stop:
	mutex_unlock(&sbi->gc_mutex);

	put_gc_inode(&ilist);
	return gc_status;
	return ret;
}

void build_gc_manager(struct f2fs_sb_info *sbi)
+0 −16
Original line number Diff line number Diff line
@@ -22,15 +22,6 @@
/* Search max. number of dirty segments to select a victim segment */
#define MAX_VICTIM_SEARCH	20

enum {
	GC_NONE = 0,
	GC_ERROR,
	GC_OK,
	GC_NEXT,
	GC_BLOCKED,
	GC_DONE,
};

struct f2fs_gc_kthread {
	struct task_struct *f2fs_gc_task;
	wait_queue_head_t gc_wait_queue_head;
@@ -103,10 +94,3 @@ static inline int is_idle(struct f2fs_sb_info *sbi)
	struct request_list *rl = &q->root_rl;
	return !(rl->count[BLK_RW_SYNC]) && !(rl->count[BLK_RW_ASYNC]);
}

static inline bool should_do_checkpoint(struct f2fs_sb_info *sbi)
{
	int node_secs = get_blocktype_secs(sbi, F2FS_DIRTY_NODES);
	int dent_secs = get_blocktype_secs(sbi, F2FS_DIRTY_DENTS);
	return free_sections(sbi) <= (node_secs + 2 * dent_secs + 2);
}
+1 −1
Original line number Diff line number Diff line
@@ -1135,7 +1135,7 @@ static int f2fs_write_node_pages(struct address_space *mapping,

	/* First check balancing cached NAT entries */
	if (try_to_free_nats(sbi, NAT_ENTRY_PER_BLOCK)) {
		write_checkpoint(sbi, false, false);
		write_checkpoint(sbi, false);
		return 0;
	}

Loading